el-input实现金额输入

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

效果图

失焦

聚焦

报错效果

javascript 复制代码
// 组件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>
相关推荐
JieE2123 小时前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2123 小时前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
kyriewen7 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
Larcher7 小时前
AI Loop:让AI像人一样自主完成任务的核心机制
javascript·人工智能·设计模式
默_笙7 小时前
🃏 JS 只有 8 种数据类型,但我花了 2 天才搞懂 null 和 undefined 的区别
javascript
jump_jump8 小时前
流式 HTML:从 htmx 片段装配到浏览器原生增量渲染
javascript·性能优化·前端工程化
swipe10 小时前
正则表达式入门到进阶:从表单校验到手写模板引擎
前端·javascript·面试
kyriewen10 小时前
前端错误监控最全指南:捕获 JS 异常、Promise 拒绝、资源加载失败,附上报代码
前端·javascript·监控
大家的林语冰10 小时前
ESLint 近期动态大全,新版本正式发布,antfu 大佬推荐的插件也更新了!
前端·javascript·前端工程化
胡志辉11 小时前
深入浅出 call、apply、bind
前端·javascript·后端