本文章记录,如何在vue3项目开发中,使用ip输入框组件.
之前写过vue2版本的ip组件,为了更好的适应vue3,此次进行vue3代码重写
几乎涵盖电脑上自带的IP输入框效果,如有问题,欢迎留言探讨.
先上效果图:
禁用效果图:
主要是组件的开发,代码如下,可直接拷贝使用.
大概思路就是: 使用四个输入框拼接,然后给输入内容添加校验操作,添加光标移动,
使用v-model语法糖特性
组件: IpAddress.vue
import { ref, watch } from 'vue'// 接收来自上层的数据const props = defineProps(['value', 'disabled'])// 更新数据const $emits = defineEmits(['update:value', 'blur'])// 存储四个refconst ipInputRefs = ref([]);// 存储左右标识位let markFlag = ref([ { left: false, right: false }, { left: false, right: false }, { left: false, right: false }, { left: false, right: false }])// 更新标识let flag = ref(false)// 鼠标点击const handleMouseUp = (index: any) => { let input = ipInputRefs.value[index] // 全为false markFlag.value.forEach(item => { item.left = false item.right = false }) // 证明在开始阶段 if (input.selectionStart == 0) { console.log('鼠标设置了,', index) markFlag.value[index].left = true } else { markFlag.value[index].left = false } // 证明在结束 if (input.selectionStart == (input.value || '').length) { markFlag.value[index].right = true } else { markFlag.value[index].right = false }}// 获取四个input refsconst getInputRef = (el: any, index: number) => { if (el) { ipInputRefs.value[index] = el; }};// 声明IP存储类型interface IpType { value: string}// 定义要显示的四个iplet ipAddress = ref([ { value: "", }, { value: "", }, { value: "", }, { value: "", },])// 初始化显示数据const initShowData = () => { // 判断不合理行为 if (props.value === '') { ipAddress.value.forEach(item => { item.value = '' }) } else { let ipList = props.value.split('.') ipAddress.value.forEach((item: IpType, index: number) => { item.value = ipList[index] }) }}// 检查ip输入const checkIpVal = (item: any) => { let val = item.value; console.log(val, '===========') // 处理非数字 val = val.toString().replace(/[^0-9]/g, ""); val = parseInt(val, 10); if (isNaN(val)) { val = ""; } else { val = val 255) { // 判断val是几位数 let num = (val + '').length if (num > 3) { val = parseInt((val + '').substring(0, 3)) } else { val = 255 } } } item.value = val; console.log(item.value, '=========')}// 判断光标左右移动位置const turnIpPosition = (item: IpType, index: number, event: any) => { let e = event || window.event; if (e.keyCode === 37) { // 左箭头向左跳转,左一不做任何措施 if (index == 0) { return } if (e.currentTarget.selectionStart === 0) { if (markFlag.value[index].left) { handleFocus(index - 1, 'toLeft') markFlag.value[index].left = false markFlag.value[index].right = false } else { console.log('左键设置了,', index) markFlag.value[index].left = true } } else { markFlag.value[index].right = false markFlag.value[index].left = false } } else if (e.keyCode == 39) { // 右箭头向右跳转,右一不做任何措施 markFlag.value[index].left = false let start = e.currentTarget.selectionStart if (index != 3 && start === item.value.toString().length) { if (markFlag.value[index].right) { handleFocus(index + 1, 'toRight') markFlag.value[index].left = false markFlag.value[index].right = false } else { markFlag.value[index].right = true } } else { markFlag.value[index].right = false } } else if (e.keyCode === 8) { console.log('删除键把当前数据删除完毕后会跳转到前一个input') // 删除键把当前数据删除完毕后会跳转到前一个input,左一不做任何处理 if (index !== 0 && e.currentTarget.selectionStart === 0) { if (markFlag.value[index].left) { ipInputRefs.value[index - 1].focus(); markFlag.value[index].left = false } else { console.log('左键设置了删除,', index) markFlag.value[index].left = true } } } else if (e.keyCode === 13 || e.keyCode === 32) { console.log('回车键、空格键、冒号均向右跳转,右一不做任何措施') // 回车键、空格键、冒号均向右跳转,右一不做任何措施 if (index !== 3) { ipInputRefs.value[index + 1].focus(); } } else if (e.keyCode === 110 || e.keyCode === 190) { // 点 . 向右跳转,右一不做任何措施 console.log('点击') if (item.value == '') { return } if (index !== 3) { ipInputRefs.value[index + 1].select(); } } else if (item.value.toString().length === 3) { console.log('满3位,光标自动向下一个文本框') // 满3位,光标自动向下一个文本框. if (index !== 3) { ipInputRefs.value[index + 1].setSelectionRange(0, 0) } }}// 处理聚焦const handleFocus = (index: number, direction: string) => { // 设置当前位置为选中状态 toRight:从左边来的 let input = ipInputRefs.value[index] input.focus() let value = input.value // null 左右全部设置为true,可以直接跳转 if ((value || '').length == 0) { markFlag.value[index].right = true markFlag.value[index].left = true } else { if (direction == 'toRight') { // 可以直接跳回 console.log('右键focus,', index) markFlag.value[index].left = true // 设置光标为左边第一个 ipInputRefs.value[index].setSelectionRange(0, 0) // 设置上一个的右边标识为false markFlag.value[index - 1] && (markFlag.value[index - 1].right = false) } else { // 直接跳回 markFlag.value[index].right = true // 设置后一个侧边为false markFlag.value[index + 1] && (markFlag.value[index + 1].left = false) } }}// 格式化补零方法const formatter = (val: string) => { let value = val.toString(); if (value.length === 2) { value = "0" + value; } else if (value.length === 1) { value = "00" + value; } else if (value.length === 0) { value = "000"; } return value;}// 监听数据变化,并初始化显示四个数据watch(() => props.value, () => { console.log('变化了~', props.value); if(flag.value){ }else{ initShowData() }}, { immediate: true})// 监听ipAddress数据变化watch(ipAddress, () => { let str = ""; for (const i in ipAddress.value) { str += formatter(ipAddress.value[i].value); } if (str === "000000000000") { str = ""; } else { str = ipAddress.value.map(item => { if (item.value !== null) { return item.value + '' } else { return '0' } }).join(".") } $emits('update:value', str) flag.value = true setTimeout(() => { flag.value = false }, 100)}, { deep: true})const handleBlur = () => { $emits('blur')}.disabled { cursor: not-allowed; background-color: #f5f7fa; .ipAdress { li { .ipInputClass { color: #c3c4cc; cursor: not-allowed; } } }}.ipAdress { display: flex; border: 1px solid #dcdfe6; border-radius: 4px; line-height: 40px; width: 100%; height: 40px; padding-inline-start: 0px; padding-left: 10px; padding-right: 10px; box-sizing: border-box; margin: 0;}.ipAdress li { position: relative; margin: 0; list-style-type: none;}.ipInputClass { border: none; width: 50px; height: 23px; text-align: center; color: #606266; background: transparent;}.ipAdress li p { position: absolute; bottom: 12px; right: 0; border-radius: 50%; background: #b6b8bc; width: 2px; height: 2px;}/*只需要3个p*/.ipAdress li:last-child p { display: none;}/*取消掉默认的input focus状态*/.ipAdress input:focus { outline: none;}
注册为组件以后,在页面中使用如下,当然,组件名自己定义,我这里组件名是IpAddress
// ipAddress双向绑定,handleBlur 可以在失去光标时,做校验等操作