vue3 + ts 实现IP地址及Mac地址输入框功能

1、组件完成代码

<template>
  <div class="ip-input">
    <div v-for="(item, index) in ipArr" :key="index" class="ip-input__item-wrap">
      <input 
        ref="ipInput" 
        v-model="ipArr[index]" 
        type="text" 
        class="ip-input__item" 
        :disabled="(index==ipArr.length-1 && props.lastDisabled) || disabled" 
        :class="{'ip-input__item--active': index === activeIndex}" 
        @input="handleInput(index)" 
        @focus="handleFocus(index)" 
        @blur="handleBlur(index)"
        @keydown.left.exact="handleFocus(index - 1)" 
        @keydown.right.exact="handleFocus(index + 1)"
        @keydown.backspace.exact="handleBackspace(index)">
      <span v-if="index !== ipArr.length - 1" class="ip-input__dot">.</span>
    </div>
    <div class="ip-tip" v-show="showTip">{{props.tipText}}</div>
  </div>
</template>
<script setup lang="ts">

const ipInput = ref()
const props = defineProps({
  // 默认值
  value: {
    type: String,
    default: ''
  },
  // 是否禁用输入框
  disabled: {
    type: Boolean,
    default: false
  },
  // 是否禁用最后一位,并默认赋值0 (公司业务要求,可忽略)
  lastDisabled: {
    type: Boolean,
    default: false
  },
  // 是否启用输入校验
  isValidate: {
    type: Boolean,
    default: false
  },
  // 校验提示信息
  tipText: {
    type: String,
    default: '请输入IP'
  }
})
const lastValue =  ref(props.lastDisabled?'0':'')
const ipArr = ref(['', '', '', lastValue.value])
const oldIpInput = ref(['', '', '',  lastValue.value])
const activeIndex = ref(-1)
const clipboardText = ref('')

const emit = defineEmits(['change', 'input'])

const pasteListener = (event: any) => {
  if (activeIndex.value === -1) { return }
  const clipboardData = event.clipboardData || window.Clipboard
  clipboardText.value = clipboardData.getData('text')
  handlePaste(activeIndex.value)
}
const copyListener = (event: any) => {
  if (activeIndex.value === -1) { return }
  const clipboardData = event.clipboardData || window.Clipboard
  clipboardData.setData('text', ipArr.value.join('.'))
  event.preventDefault()
}
window.addEventListener('paste', pasteListener)
window.addEventListener('copy', copyListener)

onBeforeUnmount(() => {
  window.removeEventListener('paste', pasteListener)
  window.removeEventListener('copy', copyListener)
})
const isNumberValid = (value: any) => {
  return /^\d*$/.test(value) && value <= 255
}
const handleInput = (index: any) => {
  const newValue: any = ipArr.value[index]
  // 如果输入的是非数字,或者输入不在0-255之间,则阻止输入
  if (!isNumberValid(newValue)) {
    ipArr.value[index] = oldIpInput.value[index]
    return false
  }
  emit('input', ipArr.value.join('.'))
  oldIpInput.value[index] = newValue
  if (newValue.length === 3 || (newValue.length === 2 && newValue > 25)) {
    if (index === ipArr.value.length - 1) { return true }
    // 将焦点移动到下一个输入框
    handleFocus(index + 1)
  }
  return true
}
const handleFocus = (index: any) => {
  if (index < 0 || index > ipArr.value.length - 1) { return }
  if (activeIndex.value !== index) {
    ipInput.value[index].focus()
  }
  activeIndex.value = index
}
const showTip = ref(false)
const handleBlur = (index: any) => {
  activeIndex.value = -1
  if(props.isValidate && (ipArr.value[0]==='' || ipArr.value[1]==='' || ipArr.value[2]==='' || ipArr.value[3]==='')) {
    showTip.value = true
  } else {
    showTip.value = false
  }
}
const handlePaste = (startIndex: any) => {
  const clipboardText1 = clipboardText.value
  const tempArr = clipboardText1.split('.')
  let i
  for (i = startIndex; i < startIndex + tempArr.length && i < ipArr.value.length; i++) {
    ipArr.value[i] = tempArr[i]
    if (!handleInput(i)) { break }
  }
  handleFocus(i)
}
const handleBackspace = (index: any) => {
  if (!ipArr.value[index]) {
    handleFocus(index - 1)
  }
}

watch(
  () => props.value,
  (newVal: any, oldVal: any) => {
    if (newVal !== oldVal) {
      emit('change', newVal, oldVal)
      ipArr.value = ['', '', '', lastValue.value]
      const tempArr = newVal.split('.')
      for (let i = 0; i < tempArr.length; i++) {
        if (!isNumberValid(tempArr[i])) {
          break
        }
        ipArr.value[i] = tempArr[i]
      }
    }
  },
  { deep: true, immediate: true }
)

defineExpose({ handleBlur, showTip })
</script>
<style lang="scss" scoped>
.ip-input {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-direction: row;
  .ip-tip {
    position: absolute;
    top: 100%;
    left: 0;
    color: #f56c6c;
    font-size: 12px;
    padding-top: 2px;
    line-height: 1;
    animation: topshow 0.1s ease-in-out;
    @keyframes topshow {
      from {
        top: 80%;
      }
      to {
        top: 100%;
      }
    }
  }
}

.ip-input__item-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
}

.ip-input__item {
  height: 30px;
  line-height: 30px;
  width: 50px;
  color: #606266;
  border: none;
  box-shadow: 0 0 0 1px #dcdfe6 inset;
  border-radius: 4px;
  text-align: center;
  font-size: 13px;
  outline: none;
}

.ip-input__item--active {
  border-color: #409eff;
}

.ip-input__dot {
  margin: 0 3px;
  font-size: 20px;
  color: #606266;
}
input:disabled  {
  background-color: #ddd;
}
</style>

2、组件使用

<template>
<div>
  <el-form ref="ruleFormRef" :model="formData" :rules="rules" label-width="180px">
    <el-form-item label="IP地址" prop="ip">
      <IpInput ref="refIpInput" :value="formData.ip" @input="ipChange" :isValidate="true" />
    </el-form-item>
    <el-form-item label="">
      <el-button type="primary"  @click="submitForm">保 存</el-button>
    </el-form-item>
  </el-form>
</div>
</template>
<script setup lang='ts'>
import IpInput from '@/components/IpInput/index.vue'
const formData = ref<any>({
  ip: ''
})

const rules = reactive({
  ip: [{ required: true, message: '', trigger: 'blur' }]
})

// ip输入框
const refIpInput = ref<any>(null)
const ipChange = (val: string) => {
	formData.value.ip = val
}

// 保存
const ruleFormRef = ref<any>()
const submitForm = () => {
  ruleFormRef.value!.validate(async (valid: boolean) => {
    // 校验IP输入
    refIpInput.value.handleBlur()
    if(refIpInput.value.showTip) return

    if (valid) {
      // const res = await dataApi()
    }
  })
}
</script>
<style lang='scss' scoped>

</style>

3、实现Mac地址输入的修改

        - 将组件中所有的  ['', '', '', lastValue.value] 数组添加两个空值,即改成如下数组

['', '', '', '', '', lastValue.value]

        - 将组件中的 handleBlur 方法修改成

const handleBlur = (index: any) => {
  activeIndex.value = -1
  if(props.isValidate && (ipArr.value[0]==='' || ipArr.value[1]==='' || ipArr.value[2]==='' || ipArr.value[3]==='' || ipArr.value[4]==='' || ipArr.value[5]==='')) {
    showTip.value = true
  } else {
    showTip.value = false
  }
}

        - 将组件中的 isNumberValid 方法修改成

const isNumberValid = (value: any) => {
  return /^[0-9A-Fa-f]*$/.test(value) && value.length <= 2
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值