这个输入框分为8个框,前面的框满了4个字符后自动跳到后面的框,并且支持复制如'aaaa-aaaa-aaaa-aaaa-aaaa-aaaa-aaaa-aaaa'这样的激活码。
代码如下:(这个功能是由ip输入框修改而来,个别注释和变量名还是ip请勿见怪)
<!--
* @Author:
* @Date: 2021-12-16 16:43:32
* @Description: 激活码输入框
-->
<template>
<el-popover
ref="popoverInput"
:content="tips"
:max-width="tipsMaxWidth"
:placement="tipsPlacement"
:show-html="true"
:offset-placement="tipsOffset"
:value="tipsVisible"
effect="effect"
trigger="manual"
popper-class="
el-popover__for-input el-popover__for-input-form
"
>
<div
slot="reference"
:style="styleObj"
:class="{
'is-focus': focused,
'is-disabled': disabled
}"
class="code-input"
>
<!-- 给ip-input添加提示功能 -->
<el-input
v-for="i in 8"
:key="i"
:ref="'input_' + i"
v-model="ipArr[i - 1]"
:maxlength="4"
:disabled="disabled"
:style="{ width: itemWidth }"
class="code-input__verse"
@change="updateValue($event, i)"
@focus="handleFocus"
@blur="handleBlur"
@keyup.native="onInputKeyup($event, i)"
@paste.native.prevent="onPaste($event, i)"
/>
</div>
</el-popover>
</template>
<script>
// 获取当前输入框的焦点位置 by liumeng6
function getRange (el) {
const ret = {}
if (el.setSelectionRange) {
ret.begin = el.selectionStart
ret.end = el.selectionEnd
ret.result = el.value.substring(ret.begin, ret.end)
}
el.focus()
return ret
}
export default {
name: 'classInInput',
props: {
value: {
type: String,
default: ''
},
width: {
type: String,
default: '100%'
},
itemWidth: {
type: String,
default: null
},
disabled: {
type: Boolean,
default: false
},
// 提示信息,设置了tips才能显示popover
tips: {
type: String,
default: null
},
// popover的最大宽度
tipsMaxWidth: {
type: [String, Number],
default: null
},
// popover出现的位置
tipsPlacement: {
type: String,
default: 'top'
},
// 出现位置的偏移量
tipsOffset: {
type: Number,
default: 0
},
// 粘贴格式有误时提示
pasteFail: {
type: String,
default: null
}
},
data () {
return {
ipArr: (this.value || '').split('-'),
tipsVisible: false,
focused: false
}
},
computed: {
styleObj () {
return {
width: this.width
}
}
},
watch: {
value (val) {
this.ipArr = val.split('-')
}
},
methods: {
updateValue (val, index) {
this.$nextTick(() => {
let str = val.toLowerCase()
const reg = new RegExp(/[^a-f0-9]+/, 'gi')
if (/[^a-f0-9]+/.test(str)) {
// 输入的内容不是0-9或a-f时,输入框置空
str = str.replace(reg, '')
} else {
// 当不处于第八个输入框,且当前输入框满四位时,自动获取下一个焦点
if (index < 8 && `${str}`.length >= 4) {
const inputEl = this.$el.getElementsByTagName('input')[index]
this._setCursorPosition(inputEl, 0)
}
}
this.ipArr.splice(index - 1, 1, str)
// 如果ip都为空,则返回空字符串,如果ip不为空,则返回ip字符串
const emitStr = this.ipArr.every(ip => ip === '') ? '' : this.ipArr.join('-')
// 如果格式符合才会被emit
if (/^[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}$/.test(emitStr)) {
this.$emit(
'input', emitStr
)
}
})
},
/**
* @author
* @date
* @desc input按键监听
*/
onInputKeyup (event, index) {
const keyCode = event.keyCode || event.which
const value = event.target.value
const arrIndex = index - 1 // 值在数组中的顺序 即从0开始
const ip = this.ipArr[arrIndex]
// 处理键盘监听,8 为退格键,37 为left箭头,39 为right箭头,110 为数字键盘上的小数点,190为控制键上的小数点,229为中文数字键盘上小数点
if (keyCode === 8 || keyCode === 37) {
if (
(value.length === 0 || getRange(event.target).end === 0) &&
arrIndex > 0
) {
const inputEl = this.$el.getElementsByTagName('input')[arrIndex - 1]
this._setCursorPosition(inputEl, inputEl.value.length, event)
}
} else if (keyCode === 39) {
// 按下右箭头,要光标在最后的时候才聚焦下一个
if (getRange(event.target).end !== value.length) return
if (arrIndex < 3 && ip !== undefined) {
const inputEl = this.$el.getElementsByTagName('input')[arrIndex + 1]
this._setCursorPosition(inputEl, 0, event)
}
} else if (
keyCode === 110 ||
keyCode === 190 ||
(keyCode === 229 &&
(event.code === 'NumpadDecimal' || event.code === 'Period'))
) {
if (!value.length) return
// 输入小数点或数字键盘上的句号能跳转
if (arrIndex < 3 && ip !== undefined) {
const inputEl = this.$el.getElementsByTagName('input')[arrIndex + 1]
this._setCursorPosition(inputEl, 0, event)
}
}
},
/**
* @desc
* @author chenguanbin
* @param {Document} inputEl 设置光标的IP框
* @param {Number} index 光标位置
*/
_setCursorPosition (inputEl, index, event) {
if (inputEl.setSelectionRange) inputEl.setSelectionRange(index, index)
inputEl.select()
// 解决缺陷:IE浏览器下一定概率出现,IP输入框按退格键,当前一位是3位数时,无法退回到上一个输入框的问题
// 原因:IE下先执行了键盘的退格事件,再执行前一个输入框的focus,这样会触发前一个输入框的change事件,导致焦点重新回到原输入框。(正确事件顺序:focus - 退格 - change)
// 解决:focus事件后阻止后续事件的执行,这样就不会执行键盘的退格事件和输入框的change事件
event && event.preventDefault()
},
/**
* @author
* @date
* @desc ip粘贴事件
*/
onPaste (event, index) {
const arrayIndex = index - 1
// `ie`和`chrome`兼容
const pasteText = event.clipboardData
? event.clipboardData.getData('text/plain').trim()
: window.clipboardData.getData('text').trim()
if (/^[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}-[a-f0-9]{1,4}$/.test(pasteText)) {
const segments = pasteText.split('-')
segments.forEach((segment, i) => {
const value = segment
if (
arrayIndex + i < 8
) {
this.ipArr.splice(arrayIndex + i, 1, value)
this.$emit('input', this.ipArr.join('-'))
}
})
this.$emit(
'input',
this.ipArr.every(ip => ip === '') ? '' : this.ipArr.join('-')
)
} else {
this.$message.error(this.pasteFail)
}
},
/**
* @author
* @date
* @desc 处理焦点事件
*/
handleFocus () {
this.focused = true
if (this.tips) {
this.tipsVisible = true
}
},
/**
* @author
* @date
* @desc 处理失焦事件
*/
handleBlur () {
this.focused = false
if (this.tips) {
this.tipsVisible = false
}
}
}
}
</script>
<style lang="less" scoped>
.code-input{
display: inline-block;
min-width: 162px;
background-color: #fff;
border-radius: 2px;
line-height: 29px;
}
.code-input__verse.el-input{
position:relative;
width:12%;
border-radius:0;
padding-right: 10px;
padding-left: 5px;
}
.code-input__verse:not(:last-child):after{
position:absolute;z-index:10;top:0;right:0;color:#999;content:"-"}
.code-input__verse input{height:30px;border:none;text-align:center}
</style>