2025-10-11 10:35:25 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="mac-address-input" :class="{ disabled: disabled }">
|
2025-10-20 09:42:01 +08:00
|
|
|
|
<el-input
|
|
|
|
|
|
ref="inputRef"
|
|
|
|
|
|
v-model="macValue"
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
maxlength="17"
|
|
|
|
|
|
:disabled="disabled"
|
|
|
|
|
|
@input="handleInput"
|
|
|
|
|
|
@keydown="handleKeydown"
|
|
|
|
|
|
@focus="handleFocus"
|
|
|
|
|
|
@blur="handleBlur"
|
|
|
|
|
|
@paste="handlePaste"
|
|
|
|
|
|
/>
|
2025-10-11 10:35:25 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-10-20 09:42:01 +08:00
|
|
|
|
import { ref, watch } from 'vue'
|
2025-10-11 10:35:25 +08:00
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
modelValue?: string
|
|
|
|
|
|
disabled?: boolean
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface Emits {
|
|
|
|
|
|
(e: 'update:modelValue', value: string): void
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
|
|
|
|
modelValue: '',
|
|
|
|
|
|
disabled: false
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<Emits>()
|
|
|
|
|
|
|
2025-10-20 09:42:01 +08:00
|
|
|
|
// 创建单个输入框的引用
|
|
|
|
|
|
const inputRef = ref<InstanceType<typeof import('element-plus').ElInput> | null>(null)
|
2025-10-11 10:35:25 +08:00
|
|
|
|
|
2025-10-20 09:42:01 +08:00
|
|
|
|
// MAC地址值
|
|
|
|
|
|
const macValue = ref<string>('')
|
2025-10-11 10:35:25 +08:00
|
|
|
|
|
|
|
|
|
|
// 解析传入的MAC地址
|
2025-10-20 09:42:01 +08:00
|
|
|
|
const parseMacAddress = (mac: string): string => {
|
|
|
|
|
|
if (!mac) return ''
|
2025-10-11 10:35:25 +08:00
|
|
|
|
|
|
|
|
|
|
// 移除非十六进制字符并转为大写
|
|
|
|
|
|
const cleanMac = mac.replace(/[^0-9a-fA-F]/g, '').toUpperCase()
|
|
|
|
|
|
|
2025-10-20 09:42:01 +08:00
|
|
|
|
// 按每2个字符分割并用冒号连接
|
|
|
|
|
|
let result = ''
|
|
|
|
|
|
for (let i = 0; i < cleanMac.length; i += 2) {
|
|
|
|
|
|
if (i > 0) result += ':'
|
|
|
|
|
|
result += cleanMac.substr(i, 2)
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
2025-10-20 09:42:01 +08:00
|
|
|
|
return result.substring(0, 17) // 最多17个字符 (12个数字+5个冒号)
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-20 09:42:01 +08:00
|
|
|
|
// 格式化MAC地址 - 改进版
|
|
|
|
|
|
const formatMac = (value: string): string => {
|
|
|
|
|
|
// 移除所有冒号
|
|
|
|
|
|
const cleanValue = value.replace(/:/g, '')
|
|
|
|
|
|
// 只保留十六进制字符并转为大写
|
|
|
|
|
|
const hexOnly = cleanValue.replace(/[^0-9a-fA-F]/g, '').toUpperCase()
|
|
|
|
|
|
|
|
|
|
|
|
// 按每两个字符添加冒号,最多6段
|
|
|
|
|
|
let formatted = ''
|
|
|
|
|
|
for (let i = 0; i < Math.min(hexOnly.length, 12); i += 2) {
|
|
|
|
|
|
if (i > 0) formatted += ':'
|
|
|
|
|
|
formatted += hexOnly.substr(i, 2)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return formatted
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 当前聚焦的输入框索引
|
|
|
|
|
|
const focusedIndex = ref<number | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
// 处理输入事件
|
2025-10-20 09:42:01 +08:00
|
|
|
|
const handleInput = (value: string) => {
|
|
|
|
|
|
const formatted = formatMac(value)
|
|
|
|
|
|
macValue.value = formatted
|
|
|
|
|
|
// 发出不带冒号的纯净值
|
|
|
|
|
|
emit('update:modelValue', formatted.replace(/:/g, ''))
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理键盘事件
|
2025-10-20 09:42:01 +08:00
|
|
|
|
const handleKeydown = (event: KeyboardEvent) => {
|
2025-10-11 10:35:25 +08:00
|
|
|
|
const target = event.target as HTMLInputElement
|
|
|
|
|
|
|
|
|
|
|
|
// 处理退格键
|
2025-10-20 09:42:01 +08:00
|
|
|
|
if (event.key === 'Backspace') {
|
|
|
|
|
|
// 处理在冒号前删除的情况
|
|
|
|
|
|
const cursorPos = target.selectionStart || 0
|
|
|
|
|
|
if (cursorPos > 0 && macValue.value[cursorPos - 1] === ':' &&
|
|
|
|
|
|
target.selectionStart === target.selectionEnd) {
|
2025-10-11 10:35:25 +08:00
|
|
|
|
event.preventDefault()
|
2025-10-20 09:42:01 +08:00
|
|
|
|
// 删除冒号前的两个字符
|
|
|
|
|
|
const newValue = macValue.value.substring(0, cursorPos - 3) +
|
|
|
|
|
|
macValue.value.substring(cursorPos)
|
|
|
|
|
|
macValue.value = newValue
|
|
|
|
|
|
// 设置光标位置
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (target.setSelectionRange) {
|
|
|
|
|
|
target.setSelectionRange(cursorPos - 3, cursorPos - 3)
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
2025-10-20 09:42:01 +08:00
|
|
|
|
}, 0)
|
|
|
|
|
|
emit('update:modelValue', newValue.replace(/:/g, ''))
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理焦点事件
|
2025-10-20 09:42:01 +08:00
|
|
|
|
const handleFocus = () => {
|
|
|
|
|
|
focusedIndex.value = 0
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理失焦事件
|
|
|
|
|
|
const handleBlur = () => {
|
|
|
|
|
|
focusedIndex.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理粘贴事件
|
2025-10-20 09:42:01 +08:00
|
|
|
|
const handlePaste = (event: ClipboardEvent) => {
|
2025-10-11 10:35:25 +08:00
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
const pastedText = event.clipboardData?.getData('text') || ''
|
|
|
|
|
|
|
|
|
|
|
|
// 清理粘贴的文本
|
2025-10-20 09:42:01 +08:00
|
|
|
|
const cleanPastedText = pastedText.replace(/[^0-9a-fA-F]/g, '').toUpperCase()
|
|
|
|
|
|
const formatted = formatMac(cleanPastedText)
|
|
|
|
|
|
macValue.value = formatted
|
|
|
|
|
|
emit('update:modelValue', formatted.replace(/:/g, ''))
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 监听modelValue变化
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => props.modelValue,
|
|
|
|
|
|
(newVal) => {
|
2025-10-20 09:42:01 +08:00
|
|
|
|
const cleanNewVal = (newVal || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase()
|
|
|
|
|
|
const currentCleanValue = macValue.value.replace(/:/g, '')
|
|
|
|
|
|
|
|
|
|
|
|
if (cleanNewVal !== currentCleanValue) {
|
|
|
|
|
|
macValue.value = parseMacAddress(cleanNewVal)
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
)
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.mac-address-input {
|
2025-10-20 09:42:01 +08:00
|
|
|
|
width: 100%;
|
2025-10-11 10:35:25 +08:00
|
|
|
|
|
|
|
|
|
|
&.disabled {
|
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-20 09:42:01 +08:00
|
|
|
|
:deep(.el-input__wrapper) {
|
|
|
|
|
|
input {
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
font-family: inherit; // 使用继承的字体而不是等宽字体
|
2025-10-11 10:35:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|