Vue3 自定义指令 只能输入数字

708 阅读1分钟

有两种输入方式, 一种是只能输入整数, 另一种是能输入特定位数的小数

先定义两个处理函数

/** 转正整数 */
export function toPositiveInteger(str: string) {
    const value = str.replace(/((?![0-9]).)*/g, '');
    if (!value) return value;
    return Number(value).toString();
}
/** 转数字 */
export function toNumber(str: string, fixedCount: number) {
    let value = str.replace(/[^\d.]/g, '');

    if (!value.includes('.')) return Number(value).toString(); // 没有小数

    const valueAry = value.split('.');
    value = valueAry[0].concat('.').concat(valueAry[1].slice(0, fixedCount));
    if (value.endsWith('.')) return value;
    return Number(value).toString();
}

然后写自定义指令

const directive: Directive = {
    mounted(el: HTMLElement, binding, vnode) {
        const inputEl =
            el.tagName.toUpperCase() === 'INPUT'
                ? (el as HTMLInputElement)
                : el.querySelector('input');
        if (!inputEl) {
            throw new Error('未找到 input 元素');
        }

        let fixedCount = 0;
        if (binding.modifiers.fixed) {
            fixedCount = 2; // 默认保留两位小数

            if (binding.value) {
                if (
                    !(
                        typeof binding.value === 'number' ||
                        typeof binding.value === 'string'
                    ) ||
                    !/^[0-9]+$/.test(binding.value.toString())
                ) {
                    throw new Error('v-number.fixed 绑定的值只能为正整数');
                }
            }
        }
        
        /** 只能输入小数 和 . */
        const keyPressEvent = (e: KeyboardEvent) => {
            if (!/\d|\./.test(e.key) || (!fixedCount && e.code === '.')) {
                return e.preventDefault();
            }
        };

        const keyDownEvent = (e: KeyboardEvent) => {
            const value = inputEl.value;
            if (!fixedCount) {
                inputEl.value = toPositiveInteger(value);
            } else {
                inputEl.value = toNumber(value, fixedCount);
            }
            
            // 这里可以优化下。 判断是不是组件。如果是组件, 触发 emit('input') 
            // 但是我没有找到 vue3 怎么判断是不是组件
            
            /** 同步一下值  */
            inputEl.dispatchEvent(
                new CustomEvent('input', {
                    detail: inputEl.value,
                })
            );
        };

        // 监听按下事件。 只能输入数字和小数点
        inputEl.addEventListener('keypress', keyPressEvent);

        inputEl.addEventListener('keydown', keyDownEvent);
        
        // 组件卸载的时候可以 removeEventListener
    },
};