子组件
<template>
<div class="jc-ip-model jc-flex-row">
<div class="ip-ground jc-flex-row" :class="{ isDisabled: props.isDisabled, active: active }">
<div v-for="(item, index) in ipAddress" class="ip-item">
<input
class="input"
:disabled="props.isDisabled"
v-model="item.num"
:ref="(el) => getCarou(el, index)"
@input="handleInput(index)"
@keydown="handleKeyDown($event, index, item)"
@blur="setDefaultVal(item, index)"
@focus="focusInput(index)"
maxlength="3"
:key="index"
/>
<span v-if="index !== ipAddress.length - 1">.</span>
</div>
</div>
<div class="port">
<el-input-number
:disabled="props.isDisabled"
@keyup.enter="enterIp(3)"
@input="changePort"
:controls="false"
v-model="portBase"
:min="1"
:max="65535"
></el-input-number>
</div>
</div>
</template>
<script setup lang="ts">
/**
* @author : yy 2023/09/07 13:35:29
* @description:
* @params : {{catchIp}} ip 地址
* @params : {{port}} 端口号
* @example: <jc-ip-model :port="port" :catchIp="ip" @ipModelSend="ipModelSend" @enter="ensureConfig(2)"></jc-ip-model>
*/
import { nextTick, onMounted, ref } from 'vue'
const props = defineProps({
catchIp: {
type: String
},
port: {
type: String,
default: null
},
isDisabled: {
type: Boolean,
default: false
}
})
const ipAddress = ref([
{ id: 1, num: '' },
{ id: 2, num: '' },
{ id: 3, num: '' },
{ id: 4, num: '' }
])
const divDomList = ref(new Map())
const getCarou = (el, id) => {
if (el) {
divDomList.value.set(id, el)
}
}
const realIp = ref()
// 验证ip是否合格
let pattern = /^([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])(.([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])){3}$/
const handleInput = (index) => {
// TODO
ipAddress.value[index].num = ipAddress.value[index].num.replace(/[^0-9]/g, '')
let ip = ipAddress.value[index].num
if (ip.length > 1 && ip[0] === '0') {
ipAddress.value[index].num = ip.slice(1)
}
if (ip > 255) {
ipAddress.value[index].num = '255'
}
if (ip.length === 3) {
let nextIndex = index + 1
if (nextIndex <= 3) {
divDomList.value.get(nextIndex).focus()
}
}
realIp.value = ipAddress.value[0].num + '.' + ipAddress.value[1].num + '.' + ipAddress.value[2].num + '.' + ipAddress.value[3].num
// 验证ip是否符合请求 不符合不添加
if (pattern.test(realIp.value)) {
emits('ipModelSend', { ip: realIp.value, port: String(portBase.value) })
}
}
const changePort = () => {
realIp.value = ipAddress.value[0].num + '.' + ipAddress.value[1].num + '.' + ipAddress.value[2].num + '.' + ipAddress.value[3].num
console.log('realIp.value', realIp.value, portBase.value)
nextTick(() => {
console.log('realIp.value', realIp.value, portBase.value)
if (portBase.value == null) {
portBase.value = ''
}
// 验证ip是否符合请求 不符合不添加
if (pattern.test(realIp.value)) {
emits('ipModelSend', { ip: realIp.value, port: String(portBase.value) })
}
})
}
const focusInput = (index) => {
active.value = true
if (index > 0 && !ipAddress.value[0].num) {
divDomList.value.get(0).focus()
}
}
const setDefaultVal = (item, index) => {
active.value = false
//当input失去焦点,而ip没有赋值时,会自动赋值为0
if (index > 0 && item.num == '') {
item.num = '0'
}
}
// 回车实践
const enterIp = (index) => {
if (index === 3) {
emits('enter')
}
}
const handleKeyDown = (event, index, item) => {
//删除键把当前数据删除完毕后会跳转到前一个input,左一不做任何处理
let val = item.num
if (event.keyCode == 8) {
if (index == 0) {
} else if (val == '') {
let nextIndex = index - 1
divDomList.value.get(nextIndex).focus()
}
}
if (index > 0 && !ipAddress.value[0].num) {
divDomList.value.get(0).focus()
}
//右箭头、回车键、空格键、冒号均向右跳转,右一不做任何措施 切必须有值
if (event.keyCode == 39 || event.keyCode == 13 || event.keyCode == 32 || event.keyCode == 190 || event.keyCode == 110) {
console.log(index)
if (val && index != 3) {
divDomList.value.get(index + 1).focus()
}
if (index === 3) {
emits('enter')
}
}
}
const emits = defineEmits(['ipModelSend', 'enter'])
const portBase = ref()
const IpModelCancel = (newVal) => {
if (props.catchIp) {
let ip = JSON.parse(JSON.stringify(props.catchIp))
ipAddress.value[0].num = ip.split('.')[0]
ipAddress.value[1].num = ip.split('.')[1]
ipAddress.value[2].num = ip.split('.')[2]
ipAddress.value[3].num = ip.split('.')[3]
portBase.value = Number(props.port) || null
}
}
const active = ref(false)
watch(
() => props.catchIp,
(newVal, oldVal) => {
IpModelCancel(newVal)
},
{ immediate: true, deep: true }
)
</script>
<style scoped lang="scss">
.jc-ip-model {
width: 210px;
height: 24px;
background: #ffffff;
line-height: 24px;
.ip-ground {
padding: 0 5px;
border-radius: 4px;
color: $jc-text;
border: 1px solid $jc-border-regular;
&:hover {
border: 1px solid $jc-border;
}
&.active {
border: 1px solid $jc-primary;
}
&.isDisabled {
background: #f5f7fa;
color: $jc-text-disabled;
}
.ip-item {
position: relative;
height: 22px;
.input {
text-align: center;
height: 20px;
line-height: 22px;
width: 50px;
}
span {
position: absolute;
bottom: -2px;
right: 0;
}
}
}
input:focus {
outline: none;
}
.port {
height: 22px;
line-height: 22px;
width: 50px;
margin-left: 8px;
:deep(.el-input-number--small) {
.el-input {
width: 70px;
.el-input__wrapper {
padding-left: 4px;
padding-right: 4px;
}
}
}
}
}
</style>
父组件
<ip-model :catchIp="catchIp" @ipModelSend="ipModelSend">