指令封装
directives/input/index.js
/*
限制输入框只能输入数字、字母、中文等规则
使用指令:v-input
修饰符参数说明:
v-input.num 只能输入数字,默认不传修饰符,会自动限制只能输入数字
v-input.intp 只能输入正整数
v-input.num_alp 只能输入数字和字母
v-input.num_alp_blank 只能输入数字、字母、空格
v-input.num_alp_sym 只能输入数字和字母、英文符号、空格
v-input.float 只能输入数字和小数点 v-input.float="2" 表示小数位数为2,默认小数位数为2,v-input.float="2"可以简写为v-input.float
v-input.no_emoji 不能输入表情符号
*/
// 只能输入数字
function num(el) {
el.value = el.value.replace(/\D+/g, '')
}
// 只能输入正整数
function intp(el) {
const value = el.value.replace(/\D+/g, '') // 去掉非数字字符
el.value = /^[1-9][0-9]*$/.test(value) ? value : value.replace(/^0+/, '') // 确保为正整数,去掉前导零
}
// 只能输入数字和字母
function num_alp(el) {
el.value = el.value.replace(/[^A-Za-z0-9]/g, '')
}
// 只能输入数字、字母、空格
function num_alp_blank(el) {
const regex = /[^a-zA-Z0-9 ]/g
el.value = el.value.replace(regex, '')
}
// 只能输入数字、字母、英文符号、空格
function num_alp_sym(el) {
const regex = /[^a-zA-Z0-9`~!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/? ]/g
el.value = el.value.replace(regex, '')
}
// 只能输入数字和小数点,n表示小数位数
function float(el, n) {
let value = el.value
value = value.replace(/[^\d.]/g, '') // 能数字和小数点
value = value.replace(/^\./g, '') // 去掉开头的点
value = value.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.') // 处理多个点的情况
if (n && Number(n) > 0) {
const d = new Array(Number(n)).fill('\\d').join('') // 构建正则表达式
const reg = new RegExp(`^(\\-)*(\\d+)\\.(${d}).*$`, 'ig')
value = value.replace(reg, '$1$2.$3') // 限制小数位数
}
// if (value && !value.includes('.')) {
// value = Number(value).toString() // 去掉开头多个0
// }
el.value = value
}
// 限制表情:😀😂❤️🌟🎉🌍🐶☺
function no_emoji(el) {
const regex =
/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E6}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F900}-\u{1F9FF}\u{263A}]+/gu
el.value = el.value.replace(regex, '')
}
// 这里扩展限制的类型
const map = { num, intp, num_alp, num_alp_blank, num_alp_sym, float, no_emoji }
export default {
bind(el, binding, vnode) {
el = el.querySelector('.el-input__inner') || el.querySelector('.el-textarea__inner') || el
let lock = false // 标记是否需要锁定输入框
let isHandling = false // 标记是否正在处理
let lastValue = null
// input事件处理函数
const handler = () => {
if (lock) return // 如果当前为锁定状态,则不进行处理
if (isHandling) return // 如果已经在处理中,则不进行处理
if (el.value === lastValue) return // 输入内容没有变化,则不进行处理
isHandling = true // 设置标记为处理中
const modifiers = Object.keys(binding.modifiers)
const newModifier = modifiers[0] || 'num'
map[newModifier](el, binding.value || 2)
lastValue = el.value // 记录当前输入框的值
Promise.resolve().then(() => {
// 异步处理,场景:火狐浏览器中,需要在最后派发input事件
el.dispatchEvent(new Event('input'))
})
isHandling = false // 处理完毕后设置标记为非处理状态
}
el.addEventListener('input', handler)
// compositionstart和compositionend事件解决的bug场景:限制只能输入数字的输入框,先输入数字,再切换为中文输入法,输入字母时,会将原来的数字删掉
el.addEventListener('compositionstart', () => {
lock = true
})
el.addEventListener('compositionend', () => {
lock = false
el.dispatchEvent(new Event('input'))
})
// 当指令与元素解绑时,移除事件监听器
vnode.context.$once('hook:destroyed', () => {
el.removeEventListener('input', handler)
})
}
}
directives/index.js
import input from './input'
const directives = { input }
Object.keys(directives).forEach(key => {
Vue.directive(key, directives[key])
})
main.js
import './directives'
注意事项:
- 指令的封装不要用keyup事件,当限制只能输入数字时,中文输入法下,输入字母,鼠标点击输入法提示的中文,会将中文输入至文本框中,原因是鼠标点击事件不会触发keyup,用input事件会规避掉这个问题
- 使用input事件时,控制台会提示堆栈溢出,需要借助isHandling,执行handler时,先判断是否需要处理
- 当限制只能输入数字时,先输入数字,在切换为中文输入法,输入字母时,会将原来的数字删掉,这个问题可以借助compositionstart和compositionend事件解决
使用
组件封装
CustomInput/index.vue
<!--
使用时,直接用 <CustomInput /> 替代 <el-input />
limit:限制输入类型,可选值:num、intp、numAlp、numAlpBlank、numAlpSym、float、noEmoji,默认为num
decimals:当limit为float类型时,限制小数位数
支持插槽:
<CustomInput v-model="subItem.regionHigh" limit="float" maxlength="12" placeholder="请输入" clearable>
<template slot="prepend">H</template>
<template slot="append">cm</template>
</CustomInput>
支持拓展:
1. 定义限制类型的函数
2. 在map对象中添加此类型
3. 完善新类型的注释
-->
<template>
<el-input v-model="inputValue" v-bind="$attrs" v-on="$listeners" @input="handleInput">
<span v-for="{ name, text } of slotList" :slot="name" :key="text">{{ text }}</span>
</el-input>
</template>
<script>
const map = {
num,
intp,
numAlp,
numAlpBlank,
numAlpSym,
float,
noEmoji
}
export default {
name: 'CustomInput',
model: { prop: 'value', event: 'changeValue' },
props: {
value: { type: [String, Number], default: '' },
limit: { type: String, default: 'num' }, // 限制输入类型,可选值:num、intp、numAlp、numAlpBlank、numAlpSym、float、noEmoji,默认为num
decimals: { type: Number, default: 2 } // float类型限制小数位数
},
data() {
const { value } = this
return { inputValue: value || value === 0 ? String(value) : '', slotList: [] }
},
watch: {
// 手动设置value时,触发handleInput,将不符合限制的字符过滤掉
value: {
handler(val) {
this.handleInput(String(val))
},
immediate: true
}
},
created() {
this.initSlot()
},
methods: {
handleInput(val) {
if (!this.limit) return
const filteredInput = map[this.limit](val, this.decimals)
this.$emit('changeValue', filteredInput)
this.inputValue = filteredInput
},
initSlot() {
this.$nextTick(() => {
const { prepend = [], append = [] } = this.$slots
const prependList = prepend.map((item) => ({ name: 'prepend', text: item.text }))
const appendList = append.map((item) => ({ name: 'append', text: item.text }))
const slotList = prependList.concat(appendList)
this.slotList = slotList
})
}
}
}
// 只能输入数字
function num(val) {
const newVal = val.replace(/[^0-9]/g, '')
return newVal
}
// 只能输入正整数
function intp(val) {
let newVal = val.replace(/[^0-9]/g, '') // 去掉非数字字符
newVal = /^[1-9][0-9]*$/.test(newVal) ? newVal : newVal.replace(/^0+/, '') // 确保为正整数,去掉前导零
return newVal
}
// 只能输入数字和字母
function numAlp(val) {
const newVal = val.replace(/[^A-Za-z0-9]/g, '')
return newVal
}
// 只能输入数字、字母、空格
function numAlpBlank(val) {
const newVal = val.replace(/[^a-zA-Z0-9 ]/g, '')
return newVal
}
// 只能输入数字、字母、英文符号、空格
function numAlpSym(val) {
const regex = /[^a-zA-Z0-9`~!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/? ]/g
const newVal = val.replace(regex, '')
return newVal
}
// 只能输入数字和小数点,n表示小数位数
function float(val, n) {
let newVal = val.replace(/[^\d.]/g, '') // 能数字和小数点
newVal = newVal.replace(/^\./g, '') // 去掉开头的点
newVal = newVal.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.') // 处理多个点的情况
if (n && Number(n) > 0) {
const d = new Array(Number(n)).fill('\\d').join('') // 构建正则表达式
const reg = new RegExp(`^(\\-)*(\\d+)\\.(${d}).*$`, 'ig')
newVal = newVal.replace(reg, '$1$2.$3') // 限制小数位数
}
if (newVal && !newVal.includes('.')) {
// value = value.replace(/^0+/, '')
// value = Number(value).toString() // 去掉开头多个0
}
return newVal
}
// 限制不可输入表情
function noEmoji(val) {
// 限制表情:😀😂❤️🌟🎉🌍🐶☺
const regex = /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E6}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F900}-\u{1F9FF}\u{263A}]+/gu
const newVal = val.replace(regex, '')
return newVal
}
</script>
注意事项
- 使用时需要传入limit,限制类型;当limit为float时,需要传入decimals,限制小数位数
- 支持插槽
使用
源码
/xtt-tools/directives-v2/input/index.js