160 lines
4.5 KiB
Vue
160 lines
4.5 KiB
Vue
<script setup lang="ts">
|
||
import type { CSSProperties } from 'vue'
|
||
import { computed, defineComponent, nextTick, ref, watch } from 'vue'
|
||
import { CHANGE_EVENT, INPUT_EVENT, PREFIX, UPDATE_MODEL_EVENT } from '../_constants'
|
||
import { getMainClass, getPx } from '../_utils'
|
||
import { codeinputEmits, codeinputProps } from './codeinput'
|
||
|
||
const props = defineProps(codeinputProps)
|
||
|
||
const emit = defineEmits(codeinputEmits)
|
||
const classes = computed(() => {
|
||
return getMainClass(props, componentName)
|
||
})
|
||
const inputValue = ref('')
|
||
const isFocus = ref(props.focus)
|
||
|
||
const codeLength = computed(() => {
|
||
return Array.from({ length: Number(props.maxlength) })
|
||
})
|
||
const itemStyle = computed(() => {
|
||
return (index: number) => {
|
||
const style: CSSProperties = {
|
||
width: `${props.size}px`,
|
||
height: `${props.size}px`,
|
||
}
|
||
// 盒子模式下,需要额外进行处理
|
||
if (props.mode === 'box') {
|
||
// 设置盒子的边框,如果是细边框,则设置为0.5px宽度
|
||
style.border = `${props.hairline ? 0.5 : 1}px solid ${props.borderColor}`
|
||
// 如果盒子间距为0的话
|
||
if (getPx(props.space) === 0) {
|
||
// 给第一和最后一个盒子设置圆角
|
||
if (index === 0) {
|
||
style.borderTopLeftRadius = '3px'
|
||
style.borderBottomLeftRadius = '3px'
|
||
}
|
||
if (index === codeLength.value.length - 1) {
|
||
style.borderTopRightRadius = '3px'
|
||
style.borderBottomRightRadius = '3px'
|
||
}
|
||
// 最后一个盒子的右边框需要保留
|
||
if (index !== codeLength.value.length - 1)
|
||
style.borderRight = 'none'
|
||
}
|
||
}
|
||
if (index !== codeLength.value.length - 1) {
|
||
// 设置验证码字符之间的距离,通过margin-right设置,最后一个字符,无需右边框
|
||
style.marginRight = `${props.space}px`
|
||
}
|
||
else {
|
||
// 最后一个盒子的有边框需要保留
|
||
style.marginRight = 0
|
||
}
|
||
|
||
return style
|
||
}
|
||
})
|
||
const codeArray = computed(() => {
|
||
return String(inputValue.value).split('')
|
||
})
|
||
const lineStyle = computed(() => {
|
||
const style: CSSProperties = {}
|
||
style.height = props.hairline ? '2px' : '4px'
|
||
style.width = `${props.size}px`
|
||
// 线条模式下,背景色即为边框颜色
|
||
style.backgroundColor = props.borderColor
|
||
return style
|
||
})
|
||
|
||
watch(() => props.modelValue, (val) => {
|
||
// 转为字符串,超出部分截掉
|
||
inputValue.value = String(val).substring(0, +props.maxlength)
|
||
}, {
|
||
immediate: true,
|
||
})
|
||
|
||
function inputHandler(e: { detail: { value: string } }) {
|
||
const value = e.detail.value
|
||
emit(UPDATE_MODEL_EVENT, value)
|
||
|
||
inputValue.value = value
|
||
// 是否允许输入“.”符号
|
||
if (props.disabledDot) {
|
||
nextTick(() => {
|
||
inputValue.value = value.replace('.', '')
|
||
})
|
||
}
|
||
// 未达到maxlength之前,发送change事件,达到后发送finish事件
|
||
emit(CHANGE_EVENT, value)
|
||
// 修改通过v-model双向绑定的值
|
||
emit(INPUT_EVENT, value)
|
||
// 达到用户指定输入长度时,发出完成事件
|
||
if (String(value).length >= Number(props.maxlength))
|
||
emit('finish', value)
|
||
}
|
||
</script>
|
||
|
||
<script lang="ts">
|
||
const componentName = `${PREFIX}-code-input`
|
||
|
||
export default defineComponent({
|
||
name: componentName,
|
||
options: {
|
||
virtualHost: true,
|
||
addGlobalClass: true,
|
||
styleIsolation: 'shared',
|
||
},
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<view :class="classes" :style="customStyle">
|
||
<view
|
||
v-for="(item, index) in codeLength"
|
||
:key="index"
|
||
class="nut-code-input__item"
|
||
:style="[itemStyle(index)]"
|
||
>
|
||
<view v-if="dot && codeArray.length > index" class="nut-code-input__item__dot" />
|
||
<text
|
||
v-else
|
||
:style="{
|
||
fontSize: `${props.fontSize}px`,
|
||
fontWeight: bold ? 'bold' : 'normal',
|
||
color: customColor,
|
||
}"
|
||
>
|
||
{{ codeArray[index] }}
|
||
</text>
|
||
<view v-if="mode === 'line'" class="nut-code-input__item__line" :style="[lineStyle]" />
|
||
<!-- #ifndef APP-PLUS -->
|
||
<view
|
||
v-if="isFocus && codeArray.length === index"
|
||
:style="{ backgroundColor: customColor }"
|
||
class="nut-code-input__item__cursor"
|
||
/>
|
||
<!-- #endif -->
|
||
</view>
|
||
<input
|
||
:disabled="disabledKeyboard"
|
||
type="number"
|
||
:focus="focus"
|
||
:value="inputValue"
|
||
:maxlength="+maxlength"
|
||
:adjustPosition="adjustPosition"
|
||
class="nut-code-input__input"
|
||
:style="{
|
||
height: `${props.size}px`,
|
||
}"
|
||
@input="inputHandler"
|
||
@focus="isFocus = true"
|
||
@blur="isFocus = false"
|
||
>
|
||
</view>
|
||
</template>
|
||
|
||
<style lang="scss">
|
||
@import './index';
|
||
</style>
|