本文章记录,如何在vue3项目开发中,使用ip输入框组件.
之前写过vue2版本的ip组件,为了更好的适应vue3,此次进行vue3代码重写
先上效果图:
禁用效果图:
主要是组件的开发,代码如下,可直接拷贝使用.
大概思路就是: 使用四个输入框拼接,然后给输入内容添加校验操作,添加光标移动,
使用v-model语法糖特性
组件: IpAddress.vue
<template> <div :class="{ 'disabled': disabled }"> <ul class="ipAdress"> <li v-for="(item, index) in ipAddress" :key="index"> <input :ref="el => getInputRef(el, index)" v-model="item.value" type="text" class="ipInputClass" :disabled="disabled" @input="checkIpVal(item)" @keyup="turnIpPosition(item, index, $event)" @blur="handleBlur" /> <div></div> </li> </ul> </div> </template> <script lang="ts" setup name="routePage"> import { ref, watch } from 'vue' // 接收来自上层的数据 const props = defineProps(['value', 'disabled']) // 更新数据 const $emits = defineEmits(['update:value', 'blur']) // 存储四个ref const ipInputRefs = ref<HTMLElement[]>([]); // 获取refs const getInputRef = (el: any, index: number) => { if (el) { ipInputRefs.value[index] = el; } }; // 声明类型 interface IpType { value: string } // 要显示的四个ip let ipAddress = ref<IpType[]>([ { 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) => { //确保每个值都处于0-255 let val = item.value; // 处理非数字 val = val.toString().replace(/[^0-9]/g, ""); val = parseInt(val, 10); if (isNaN(val)) { val = ""; } else { val = val < 0 ? 0 : val; val = val > 255 ? 255 : val; } item.value = val; } // 判断光标位置 const turnIpPosition = (item: IpType, index: number, event: any) => { let e = event || window.event; if (e.keyCode === 37) { // 左箭头向左跳转,左一不做任何措施 if (index !== 0 && e.currentTarget.selectionStart === 0) { ipInputRefs.value[index - 1].focus(); } } else if (e.keyCode == 39) { // 右箭头向右跳转,右一不做任何措施 if ( index !== 3 && e.currentTarget.selectionStart === item.value.toString().length ) { ipInputRefs.value[index + 1].focus(); } } else if (e.keyCode === 8) { // 删除键把当前数据删除完毕后会跳转到前一个input,左一不做任何处理 if (index !== 0 && item.value === "") { ipInputRefs.value[index - 1].focus(); } } else if (e.keyCode === 13 || e.keyCode === 32) { // 回车键、空格键、冒号均向右跳转,右一不做任何措施 if (index !== 3) { ipInputRefs.value[index + 1].focus(); } } // else if (item.value.toString().length === 3) { // // 满3位,光标自动向下一个文本框. // if (index !== 3) { // ipInputRefs.value[index + 1].focus(); // } // } else if (e.keyCode === 110 || e.keyCode === 190) { // 点 . 向右跳转,右一不做任何措施 if ( index !== 3 && e.currentTarget.selectionStart !== 0 ) { ipInputRefs.value[index + 1].focus(); } } } // 格式化补零方法 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, () => { 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) }, { deep: true }) const handleBlur = () => { $emits('blur') } </script> <style lang="scss" scoped> .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 div { position: absolute; bottom: 12px; right: 0; border-radius: 50%; background: #b6b8bc; width: 2px; height: 2px; } /*只需要3个div*/ .ipAdress li:last-child div { display: none; } /*取消掉默认的input focus状态*/ .ipAdress input:focus { outline: none; } </style>
注册为组件以后,在页面中使用如下,当然,组件名自己定义,我这里组件名是
// ipAddress双向绑定,handleBlur 可以在失去光标时,做校验等操作 <IpAddress v-model:value="ipAddress" @blur="handleBlur" :disabled="true"/>