自定义一个保留小时位指令

43 阅读1分钟
import type { App } from 'vue'
import { isExist } from '../utils';

/**
 * 
 * @param app
 * 单个数值表示:小数点保留位数 
 * v-digit="2"
 * deci: 小数点保留位数
 * isFirstCompleteDeci: 首次更新时是否自动补全,小数位。默认值为true
 * v-digit="{ deci: 2, isFirstCompleteDeci: false }" 
 */
export function digit(app: App) {
    // 是否第一次更新时,自动补全小数点
    let isFirstCompleteDeci = false;
    app.directive('digit', {
        // 在绑定元素的父组件
        // 及他自己的所有子节点都更新后调用
        mounted(el, binding, vnode, prevVnode) {
            const input = el.querySelector('input');
            let deci = binding.value || 2;
            if(typeof binding.value == 'object') {
                deci = binding.value.deci;
            }
            bindEvents(input, input.value, deci);
        },
        updated(el, binding, vnode) {
            const input = el.querySelector('input');
            const bindValue = binding.value;
            // 是否自动触发
            if(bindValue?.isFirstCompleteDeci !== false && !isFirstCompleteDeci) {
                isFirstCompleteDeci = true;
                input.dispatchEvent(new Event('blur'))
            }
        }
    })

    const deciHandler = (input: HTMLElement | EventTarget, deci: number) => {
        let value = input.value;
        let selectionStart = input.selectionStart;
        const reg = new RegExp("(?:[^-.0-9]+)?\\-*(0*)([1-9]\\d*)?(\\.(?:\\d{1,"+deci+"})?)?(?:[^-.0-9]+)?", "g");
        const matches = reg.exec(value);
        let $1 = ''
        if (isExist(matches[1], true)) {
            matches[1].length > 1 && selectionStart--;
            $1 = matches[1].replace(/0+/, '0');
        }
        const $2 = matches[2] || '';
        const $3 = matches[3] || '';
        if ($1 == '0') {
            if ($2) {
                value = $2 + $3;
            } else {
                value = $1 + $3;
            }
        } else {
            value = $2 + $3
        }
        return { value, selectionStart }
    }

    const bindEvents = (input: HTMLElement, oldValue: string, deci = 2) => {
        let lock = false;
        input.addEventListener('compositionstart', () => {
            lock = true;
        })
        input.addEventListener('compositionend', () => {
            lock = false;
            input.dispatchEvent(new Event('input'))
        })
        input.addEventListener('input', () => {
            if (lock) { return }
            const curValue = input.value;
            const { value, selectionStart } = deciHandler(input, deci);
            if(oldValue != curValue) {
                oldValue = value;
                input.value = value;
                input.setSelectionRange(selectionStart, selectionStart);
                input.dispatchEvent(new Event('input'))
            }
        })

        input.addEventListener('blur', (e) => {
            input.value = Number(input.value).toFixed(deci);
            oldValue = input.value;
            input.dispatchEvent(new Event('input'))
        })
    }
}