el-input实现金额输入

发布于:2025-02-28 ⋅ 阅读:(16) ⋅ 点赞:(0)

需求:想要实现一个输入金额的el-input,限制只能输入数字和一个小数点。失焦数字转千分位,聚焦转为数字,超过最大值,红字提示

效果图

失焦

效果图

聚焦

聚焦

报错效果

报错

// 组件limitDialog
<template>
    <el-dialog
        :visible.sync="isVisible"
        title="修改限额"
        width="420px"
        :before-close="cancel"
    >
        <el-form :model="formVal" ref="ruleForm" size="small">
            <el-form-item
                label="单次交易限额"
                prop="single_limit"
                :rules="[
                    {
                        required: true,
                        message: '请输入单次交易限额',
                        trigger: 'change',
                    },
                    { validator: singleRule, trigger: 'change' },
                ]"
            >
                <el-input
                    v-model="formVal.single_limit"
                    v-thousand
                    maxlength="10"
                    type="text"
                    @keypress.native="restrictInput('single_limit', $event)"
                    @blur="formatOnBlur('single_limit', $event)"
                >
                    <template slot="suffix">{{ otherInfo.currency }}</template>
                </el-input>
            </el-form-item>
            <el-form-item
                label="每日限额"
                prop="daily_limit"
                :rules="[
                    {
                        required: true,
                        message: '请输入每日限额',
                        trigger: 'change',
                    },
                    { validator: dailyRule, trigger: 'change' },
                ]"
            >
                <el-input
                    v-model="formVal.daily_limit"
                    maxlength="10"
                    v-thousand
                    type="text"
                    @keypress.native="restrictInput('daily_limit', $event)"
                    @blur="formatOnBlur('daily_limit', $event)"
                >
                    <template slot="suffix">{{ otherInfo.currency }}</template>
                </el-input>
                <p class="tip" v-if="type !== 'bath'">
                    当日已用金额 {{ otherInfo.daily_used }}
                    {{ otherInfo.currency }}
                </p>
            </el-form-item>
            <el-form-item
                label="每月限额"
                prop="monthly_limit"
                :rules="[
                    {
                        required: true,
                        message: '请输入每月限额',
                        trigger: 'change',
                    },
                    { validator: monthlyRule, trigger: 'change' },
                ]"
            >
                <el-input
                    v-model="formVal.monthly_limit"
                    maxlength="10"
                    v-thousand
                    type="text"
                    @keypress.native="restrictInput('monthly_limit', $event)"
                    @blur="formatOnBlur('monthly_limit', $event)"
                >
                    <template slot="suffix">{{ otherInfo.currency }}</template>
                </el-input>
                <p class="tip" v-if="type !== 'bath'">
                    当月已用金额 {{ otherInfo.monthly_used }}
                    {{ otherInfo.currency }}
                </p>
            </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
            <el-button @click="cancel" size="small">取 消</el-button>
            <el-button
                type="primary"
                @click="handleSave"
                size="small"
                :loading="isSumbitLoading"
                >确 定</el-button
            >
        </div>
    </el-dialog>
</template>

<script>
import { updateCardLimitApi } from "@/services/api/cardManage.js";
import { updateObject } from "@/utils/common";
export default {
    data() {
        return {
            isVisible: false,
            isSumbitLoading: false,
            formVal: {
                single_limit: "",
                daily_limit: "",
                monthly_limit: "",
                id: "",
            },
            otherInfo: {
                currency: "USD",
                daily_used: "",
                monthly_used: "",
            },
            type: "bath",
        };
    },
    props: {
        selectedList: {
            type: Array,
            default: () => [],
        },
    },
    methods: {
        singleRule(rule, value, callback) {
            const numValue = Number(value);
            if (numValue > 10000) {
                callback(
                    new Error(
                        `输入金额不可超过单次限额(10,000.00 ${this.otherInfo.currency})`
                    )
                );
            }
            this.checkLimit(value, callback);
        },
        dailyRule(rule, value, callback) {
            const numValue = Number(value);
            if (numValue > 100000) {
                callback(
                    new Error(
                        `输入金额不可超过每日限额(100,000.00 ${this.otherInfo.currency})`
                    )
                );
            }
            this.checkLimit(value, callback);
        },
        monthlyRule(rule, value, callback) {
            const numValue = Number(value);
            if (numValue > 500000) {
                callback(
                    new Error(
                        `输入金额不可超过每月限额(500,000.00 ${this.otherInfo.currency})`
                    )
                );
            }
            this.checkLimit(value, callback);
        },
        checkLimit(value, callback) {
            const strValue = String(value || "");

            if (strValue === "")
                return callback(new Error("请输入单次交易限额"));
            if (strValue.endsWith("."))
                return callback(new Error("不能以小数点结尾"));

            const numValue = Number(strValue);
            if (isNaN(numValue)) return callback(new Error("请输入有效的数字"));
            if (strValue.includes(".") && strValue.split(".")[1].length > 2) {
                return callback(new Error("小数点后最多两位"));
            }
            callback();
        },
        restrictInput(formKey, event) {
            const key = event.key;
            const value = String(this.formVal[formKey] || "");

            if (event.ctrlKey || event.altKey || key.length > 1) return;

            // 只允许数字和小数点,限制多个小数点
            const isValidKey = /[0-9.]/.test(key);
            const hasDecimal = value.includes(".");

            if (!isValidKey || (key === "." && hasDecimal)) {
                event.preventDefault();
                return;
            }
        },
        formatOnBlur(formKey) {
            const strValue = String(this.formVal[formKey] || "");
            if (strValue && !isNaN(Number(strValue))) {
                this.formVal[formKey] = Number(strValue).toFixed(2);
            }
        },
        init(info, type) {
            this.isVisible = true;
            this.type = type;
            updateObject(this.formVal, info);
            updateObject(this.otherInfo, info);
        },
        handleSave() {
            this.isSubmitLoading = true;
            this.$refs.ruleForm.validate(async (valid) => {
                if (valid) {
                    const { code } = await updateCardLimitApi({
                        ...this.formVal,
                        id:
                            this.type === "bath"
                                ? this.selectedList.map((item) => item.id)
                                : [this.formVal.id],
                    });
                    if (code == 0) {
                        this.$message.success("修改成功");
                        this.cancel();
                        this.$emit("reload");
                    }
                }
                this.isSumbitLoading = false;
            });
        },
        cancel() {
            this.isVisible = false;
            this.$refs.ruleForm.resetFields();
            updateObject(this.formVal, {
                single_limit: "",
                daily_limit: "",
                monthly_limit: "",
                id: "",
            });
            this.otherInfo.currency = "USD";
        },
    },
};
</script>

<style lang="scss" scoped>
.dialog-footer {
    text-align: right;
}
.tip {
    color: #999999;
    font-size: 12px;
}
</style>