适用于 vue2、vue3 的自定义指定:v-int(正整数)
在项目中,我们经常会遇到输入框只允许输入数字的情况,下面是一段自定义指定 代码,复制到项目中,注册指定即可使用
- 用法如下:
- 创建一个IntInput.js 文件,将下面代码复制到文件中保存
- 在项目中的 main.js 文件中导入创建的 IntInput.js 文件
- 注册全局指令:app.directive(‘int’, IntInput) ,注册后即可使用 v-int 绑定元素(兼容了大多数组件库中输入框组件)
注意:以下代码中的钩子函数是 vue3 写法,如需在 vue2 项目中使用,修改对应的钩子函数即可(代码中有注释说明)
/*** 查找并返回元素内部的原生 <input> 节点。*/
function getInputElement(el) {if (el.tagName.toLowerCase() === 'input') {return el;}const input = el.querySelector('input');if (!input) {throw new Error('v-integer directive requires the element to contain an <input> tag.');}return input;
}/*** 将字符串中的全角数字转换为半角数字。*/
function toHalfWidth(str) {return str.replace(/[\uff10-\uff19]/g, (char) => {return String.fromCharCode(char.charCodeAt(0) - 65248);});
}export default {/*** Vue 2: bind / Vue 3: mounted*/mounted(el) {const input = getInputElement(el);// ⭐ 核心:引入一个状态标志来跟踪输入法组合状态el._v_integer_is_composing = false;const onCompositionStart = () => {el._v_integer_is_composing = true;};const onCompositionEnd = (event) => {// 组合结束后,标志位复原el._v_integer_is_composing = false;// ⭐ 关键:手动触发一次 input 事件(或直接调用处理函数)来执行清理// 使用 dispatchEvent 更符合事件流,并能被其他可能的监听器捕获event.target.dispatchEvent(new Event('input'));};// onKeydown 逻辑保持不变,用于拦截非组合状态下的无效按键const onKeydown = (event) => {if (el._v_integer_is_composing) {return;}};const onInput = () => {// ⭐ 关键:如果在组合状态中,则不执行任何操作if (el._v_integer_is_composing) {return;}const originalValue = input.value;const normalizedValue = toHalfWidth(originalValue);const sanitizedValue = normalizedValue.replace(/[^\d]/g, '');if (originalValue !== sanitizedValue) {const selectionStart = input.selectionStart;const valueChange = originalValue.length - sanitizedValue.length;input.value = sanitizedValue;// 简单恢复光标位置if (selectionStart) {input.selectionStart = input.selectionEnd = selectionStart - valueChange;}input.dispatchEvent(new Event('input', { bubbles: true }));}};// 存储所有处理器以便解绑el._v_integer_handlers = {keydown: onKeydown,input: onInput,compositionstart: onCompositionStart,compositionend: onCompositionEnd,};// 绑定所有事件监听器input.addEventListener('keydown', onKeydown);input.addEventListener('input', onInput);input.addEventListener('compositionstart', onCompositionStart);input.addEventListener('compositionend', onCompositionEnd);},/*** Vue 2: unbind / Vue 3: unmounted*/unmounted(el) {try {const input = getInputElement(el);if (el._v_integer_handlers) {input.removeEventListener('keydown', el._v_integer_handlers.keydown);input.removeEventListener('input', el._v_integer_handlers.input);input.removeEventListener('compositionstart', el._v_integer_handlers.compositionstart);input.removeEventListener('compositionend', el._v_integer_handlers.compositionend);delete el._v_integer_handlers;delete el._v_integer_is_composing;}} catch (e) {console.warn('Could not unbind v-integer listeners.', e);}}
};