u-code-input.vue 7.31 KB
Newer Older
潘永坪's avatar
潘永坪 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
<template>
	<view class="u-code-input">
		<view
			class="u-code-input__item"
			:style="[itemStyle(index)]"
			v-for="(item, index) in codeLength"
			:key="index"
		>
			<view
				class="u-code-input__item__dot"
				v-if="dot && codeArray.length > index"
			></view>
			<text
				v-else
				:style="{
					fontSize: $u.addUnit(fontSize),
					fontWeight: bold ? 'bold' : 'normal',
					color: color
				}"
			>{{codeArray[index]}}</text>
			<view
				class="u-code-input__item__line"
				v-if="mode === 'line'"
				:style="[lineStyle]"
			></view>
			<!-- #ifndef APP-PLUS -->
			<view v-if="isFocus && codeArray.length === index" :style="{backgroundColor: color}" class="u-code-input__item__cursor"></view>
			<!-- #endif -->
		</view>
		<input
			:disabled="disabledKeyboard"
			type="number"
			:focus="focus"
			:value="inputValue"
			:maxlength="maxlength"
			:adjustPosition="adjustPosition"
			class="u-code-input__input"
			@input="inputHandler"
			:style="{
				height: $u.addUnit(size) 
			}"
			@focus="isFocus = true"
			@blur="isFocus = false"
		/>
	</view>
</template>

<script>
	import props from './props.js';
	/**
	 * CodeInput 验证码输入
	 * @description 该组件一般用于验证用户短信验证码的场景,也可以结合uView的键盘组件使用
	 * @tutorial https://www.uviewui.com/components/codeInput.html
	 * @property {String | Number}	maxlength			最大输入长度 (默认 6 )
	 * @property {Boolean}			dot					是否用圆点填充 (默认 false )
	 * @property {String}			mode				显示模式,box-盒子模式,line-底部横线模式 (默认 'box' )
	 * @property {Boolean}			hairline			是否细边框 (默认 false )
	 * @property {String | Number}	space				字符间的距离 (默认 10 )
	 * @property {String | Number}	value				预置值
	 * @property {Boolean}			focus				是否自动获取焦点 (默认 false )
	 * @property {Boolean}			bold				字体和输入横线是否加粗 (默认 false )
	 * @property {String}			color				字体颜色 (默认 '#606266' )
	 * @property {String | Number}	fontSize			字体大小,单位px (默认 18 )
	 * @property {String | Number}	size				输入框的大小,宽等于高 (默认 35 )
	 * @property {Boolean}			disabledKeyboard	是否隐藏原生键盘,如果想用自定义键盘的话,需设置此参数为true (默认 false )
	 * @property {String}			borderColor			边框和线条颜色 (默认 '#c9cacc' )
	 * @property {Boolean}			disabledDot			是否禁止输入"."符号 (默认 true )
	 * 
	 * @event {Function}	change	输入内容发生改变时触发,具体见上方说明			value:当前输入的值
	 * @event {Function}	finish	输入字符个数达maxlength值时触发,见上方说明	value:当前输入的值
	 * @example	<u-code-input v-model="value4" :focus="true"></u-code-input>
	 */
	export default {
		name: 'u-code-input',
		mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
		data() {
			return {
				inputValue: '',
				isFocus: this.focus
			}
		},
		watch: {
			value: {
				immediate: true,
				handler(val) {
					// 转为字符串,超出部分截掉
					this.inputValue = String(val).substring(0, this.maxlength)
				}
			},
		},
		computed: {
			// 根据长度,循环输入框的个数,因为头条小程序数值不能用于v-for
			codeLength() {
				return new Array(Number(this.maxlength))
			},
			// 循环item的样式
			itemStyle() {
				return index => {
					const addUnit = uni.$u.addUnit
					const style = {
						width: addUnit(this.size),
						height: addUnit(this.size)
					}
					// 盒子模式下,需要额外进行处理
					if (this.mode === 'box') {
						// 设置盒子的边框,如果是细边框,则设置为0.5px宽度
						style.border = `${this.hairline ? 0.5 : 1}px solid ${this.borderColor}`
						// 如果盒子间距为0的话
						if (uni.$u.getPx(this.space) === 0) {
							// 给第一和最后一个盒子设置圆角
							if (index === 0) {
								style.borderTopLeftRadius = '3px'
								style.borderBottomLeftRadius = '3px'
							}
							if (index === this.codeLength.length - 1) {
								style.borderTopRightRadius = '3px'
								style.borderBottomRightRadius = '3px'
							}
							// 最后一个盒子的右边框需要保留
							if (index !== this.codeLength.length - 1) {
								style.borderRight = 'none'
							}
						}
					}
					if (index !== this.codeLength.length - 1) {
						// 设置验证码字符之间的距离,通过margin-right设置,最后一个字符,无需右边框
						style.marginRight = addUnit(this.space)
					} else {
						// 最后一个盒子的有边框需要保留
						style.marginRight = 0
					}

					return style
				}
			},
			// 将输入的值,转为数组,给item历遍时,根据当前的索引显示数组的元素
			codeArray() {
				return String(this.inputValue).split('')
			},
			// 下划线模式下,横线的样式
			lineStyle() {
				const style = {}
				style.height = this.hairline ? '2px' : '4px'
				style.width = uni.$u.addUnit(this.size)
				// 线条模式下,背景色即为边框颜色
				style.backgroundColor = this.borderColor
				return style
			}
		},
		methods: {
			// 监听输入框的值发生变化
			inputHandler(e) {
				const value = e.detail.value
				this.inputValue = value
				// 是否允许输入“.”符号
				if(this.disabledDot) {
					this.$nextTick(() => {
						this.inputValue = value.replace('.', '')
					})
				}
				// 未达到maxlength之前,发送change事件,达到后发送finish事件
				this.$emit('change', value)
				// 修改通过v-model双向绑定的值
				this.$emit('input', value)
				// 达到用户指定输入长度时,发出完成事件
				if (String(value).length >= Number(this.maxlength)) {
					this.$emit('finish', value)
				}
			}
		}
	}
</script>

<style lang="scss" scoped>
	@import "../../libs/css/components.scss";
	$u-code-input-cursor-width: 1px;
	$u-code-input-cursor-height: 40%;
	$u-code-input-cursor-animation-duration: 1s;
	$u-code-input-cursor-animation-name: u-cursor-flicker;

	.u-code-input {
		@include flex;
		position: relative;
		overflow: hidden;

		&__item {
			@include flex;
			justify-content: center;
			align-items: center;
			position: relative;

			&__text {
				font-size: 15px;
				color: $u-content-color;
			}

			&__dot {
				width: 7px;
				height: 7px;
				border-radius: 100px;
				background-color: $u-content-color;
			}

			&__line {
				position: absolute;
				bottom: 0;
				height: 4px;
				border-radius: 100px;
				width: 40px;
				background-color: $u-content-color;
			}
			/* #ifndef APP-PLUS */
			&__cursor {
				position: absolute;
				top: 50%;
				left: 50%;
				transform: translate(-50%,-50%);
				width: $u-code-input-cursor-width;
				height: $u-code-input-cursor-height;
				animation: $u-code-input-cursor-animation-duration u-cursor-flicker infinite;
			}
			/* #endif */
			
		}

		&__input {
			// 之所以需要input输入框,是因为有它才能唤起键盘
			// 这里将它设置为两倍的屏幕宽度,再将左边的一半移出屏幕,为了不让用户看到输入的内容
			position: absolute;
			left: -750rpx;
			width: 1500rpx;
			top: 0;
			background-color: transparent;
			text-align: left;
		}
	}
	
	/* #ifndef APP-PLUS */
	@keyframes u-cursor-flicker {
		0% {
		    opacity: 0;
		}
		50% {
		    opacity: 1;
		}
		100% {
		    opacity: 0;
		}
	}
	/* #endif */

</style>