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
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/660099.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI预测福彩3D采取888=3策略+和值012路一缩定乾坤测试5月29日预测第5弹

今天继续基于8883的大底&#xff0c;使用尽可能少的条件进行缩号&#xff0c;同时&#xff0c;同样准备两套方案&#xff0c;一套是我自己的条件进行缩号&#xff0c;另外一套是8883的大底结合2码不定位奖号预测二次缩水来杀号。好了&#xff0c;直接上结果吧~ 首先&…

【数据结构】

根据先序、中序、后序确定二叉树&#xff1a; #背景&#xff1a;树和二叉树基本上都有先序、中序、后序、按层遍历等遍历顺序&#xff0c;根据先序和后序不一定可以确定一棵二叉树&#xff0c;给定中序和其它一种遍历的序列就可以确定一棵二叉树的结构。 抓住中序特点&#x…

开源工具专题-04 Atlassian Crowd部署备份及迁移

开源工具专题-04 Atlassian Crowd部署备份及迁移 注&#xff1a; 本教程由羞涩梦整理同步发布&#xff0c;本人技术分享站点&#xff1a;blog.hukanfa.com转发本文请备注原文链接&#xff0c;本文内容整理日期&#xff1a;2024-05-29csdn 博客名称&#xff1a;五维空间-影子&…

SpringBoot与Spring Framework提供的缓存抽象

目录 缓存 项目总结 新建一个SpringBoot项目 pom.xml application.properties CacheConfig Book BookRepository接口 BookService服务类 BookController控制器 SpringbootCacheApplication启动类 启动项目&#xff0c;使用Postman测试 参考博文&#xff1a; 1、使用…

无人港口/码头兴起,可视化大屏功不可没。

码头/港口可视化大屏可以为管理上带来多方面的价值&#xff0c;包括但不限于&#xff1a; 1. 实时监控&#xff1a; 大屏可以将港口的各种数据、设备状态、船舶位置等信息实时展示&#xff0c;管理人员可以通过大屏随时监控港口的运营情况&#xff0c;及时发现并处理问题。 2…

第13章 常用类

一、包装类 二、String String的常用方法&#xff1a; equals&#xff1a;判断内容是否相等&#xff0c;区分大小写。 String str1 "hello";String str2 "Hello";System.out.println(str1.equals(str2));//false equalsIgnoreCase&#xff1a;判断内容…

清华大学提出IFT对齐算法,打破SFT与RLHF局限性

监督微调&#xff08;Supervised Fine-Tuning, SFT&#xff09;和基于人类反馈的强化学习&#xff08;Reinforcement Learning from Human Feedback, RLHF&#xff09;是预训练后提升语言模型能力的两大基础流程&#xff0c;其目标是使模型更贴近人类的偏好和需求。 考虑到监督…

一文看懂标准版和Pro版的区别

在CRMEB的众多产品中&#xff0c;有这样两款产品经常被拿来比较&#xff0c;它们就是CRMEB的标准版和Pro版商城系统&#xff0c;今天&#xff0c;我们就来盘一下这两款系统之间究竟有哪些不同。 1、Pro版系统性能更卓越 CRMEB Pro版采用Tp6 SwooleRedis高性能框架开发&#…

游戏联运平台如何助力游戏行业飞速发展?

随着科技的进步和互联网的普及&#xff0c;游戏行业正以前所未有的速度飞速发展。在这个过程中&#xff0c;游戏联运平台凭借其独特的优势和功能&#xff0c;成为了推动游戏行业腾飞的关键力量。本文将探讨游戏联运平台如何助力游戏行业实现飞速发展。 一、游戏联运平台的定义与…

四川易点慧电商抖音小店信誉之店

在当下这个电商飞速发展的时代&#xff0c;如何在众多网店中挑选出一家既可靠又值得信赖的店铺&#xff0c;成为了消费者们关注的焦点。四川易点慧电子商务有限公司抖音小店以其卓越的品质和诚信的经营&#xff0c;逐渐在抖音平台上崭露头角&#xff0c;成为了众多消费者心中的…

北京大学第一医院与智源研究院共同发布基于可信执行环境的AI医学影像挑战赛

肾动脉狭窄是导致继发性高血压及肾功能不全的常见原因&#xff0c;而目前针对肾动脉狭窄功能学的评估尚处于探索阶段。数据保护和可信计算环境是目前人工智能技术应用于临床研究的一大瓶颈。北京大学第一医院与北京智源人工智能研究院心脏AI 联合研究中心特发布基于可信执行环境…

FreeSwitch视频会议同时支持内网和外网接入

我们在使用freeswitch进行视频会议时&#xff0c;之前所有的用户都是通过外网的方式接入&#xff0c;因为fs给其返回的sdp协议内容里&#xff0c;只需要fs配置的外网IP就可以了&#xff1b;最近由于引入新的业务需要有其他内网的服务器也可以直接接入fs的视频会议房间&#xff…

VirtualBox虚拟机与bhyve虚拟机冲突问题解决@FreeBSD

问题 在安装完bhyve虚拟系统的主机上启动VirtualBox虚拟机的时候&#xff0c;报错&#xff1a;不能为虚拟电脑 debian 打开一个新任务. VirtualBox cant operate in VMX root mode. Please close all other virtualization programs. (VERR_VMX_IN_VMX_ROOT_MODE). 返回 代码…

5292A 物联网信号分析仪

5292A 物联网信号分析仪 —— 10MHz&#xff5e;6GHz —— 简述 5292A物联网信号分析仪是一款通用的矢量信号分析仪&#xff0c;频率范围覆盖 10MHz&#xff5e;6GHz&#xff0c;具有良好的频率、功率测量精度和稳定度&#xff1b;支持模拟与数字调制信号、全制式的通信标准…

Linux DHCP server 配置

参考&#xff1a;linux dhcp配置多vlan ip_linux 接口vlan-CSDN博客 配置静态IP地址&#xff1a; 给固定的MAC地址分配指定的IP地址&#xff0c;固定的IP地址不必包含在指定的IP池中&#xff0c;如果包含在IP地址池中&#xff0c;固定的IP地址会从IP地址池中移除 配置方法&…

数组-检查数组内是否存在和为7的倍数的子序列

一、题目描述 二、解题思路 这里首先要分辨清楚是子序列还是子数组 原数组&#xff1a;[1,2,3,4,5] 子序列&#xff1a;元素和元素之间相对位置保持不变&#xff0c;但是在原数组中不一定连续&#xff0c;如&#xff1a;[1,3,4]&#xff1b; 子数组&#xff1a;元素元素之间保…

小型水库水雨情和大坝安全监测解决方案

小型水库水雨情和大坝安全监测解决方案 小型水库作为重要的水资源管理和防洪调蓄设施&#xff0c;在保障农业灌溉、居民饮水及防洪安全方面发挥着不可或缺的作用。然而&#xff0c;由于其规模限制&#xff0c;小型水库往往在水雨情监测和大坝安全评估方面面临资源和技术的双重…

全球市值最高的能源公司沙特阿美股份拟出售,筹集百亿美元

KlipC报道&#xff1a;据5月28日市场消息&#xff0c;沙特政府可能最快会在本周宣布拟出售国营石油公司沙特阿美股份&#xff0c;筹集100亿-200亿美元。 沙特阿美是世界最大的石油生产商&#xff0c;2019年在沙特证交所上市。沙特的经济高度依赖石油出口。此前&#xff0c;受石…

DxO PhotoLab 6 for Mac/Win:专业RAW图片编辑的利器

DxO PhotoLab 6 for Mac/Win是一款专为摄影师和摄影爱好者打造的专业RAW图片编辑软件&#xff0c;它将先进的技术、丰富的功能与直观的操作完美结合&#xff0c;为用户提供了一款全面而强大的图片处理工具。 一、技术领先&#xff0c;处理RAW图片更高效 DxO PhotoLab 6采用了…

Swift 构造过程

构造过程 一、存储属性的初始赋值1、构造器2、默认属性值 二、自定义构造过程1、形参的构造过程2、形参命名和实参标签3、不带实参标签的构造器形参4、可选属性类型5、构造过程中常量属性的赋值 三、默认构造器结构体的逐一成员构造器 四、值类型的构造器代理五、类的继承和构造…