uniapp 密码输入框

619 阅读3分钟

封装背景

项目有时需要对登录/注册/找回密码这类的页面中的密码输入框右侧的显示/隐藏密码的icon进行自定义

比如

  1. 默认的icon无法满足需要,需要用带有项目特色的icon
  2. 由于不同操作环境(安卓和iOS)导致的密码输入框右侧icon不一致,导致客户不满意这样的问题(要求统一😭)

参数

参数名描述示例
inputStyle输入框的样式Object对象
placeholder输入框为空时占位符String字符串

组件有两个插槽

  • controls-show:自定义 右侧显示密码icon 的内容
  • controls-hide:自定义 右侧隐藏密码icon 的内容

代码

<template>
	<view class="password-box">
		<!-- #ifndef MP -->
		<input
			:style="[props.inputStyle, { paddingRight: controlsWidth + 10 + 'px' }]"
			:type="showPassword ? 'text' : 'password'"
			v-model="password"
			:placeholder="props.placeholder"
		/>
		<!-- #endif -->
		<!-- #ifdef MP -->
		<input
			:style="[props.inputStyle, { paddingRight: controlsWidth + 10 + 'px' }]"
			type="text"
			:password="!showPassword"
			v-model="password"
			:placeholder="props.placeholder"
		/>
		<!-- #endif -->

		<div class="controls-box" @click="togglePassword">
			<slot name="controls-show" v-if="showPassword">
				<text>显示</text>
			</slot>
			<slot name="controls-hide" v-else>
				<text>隐藏</text>
			</slot>
		</div>
	</view>
</template>

<script setup>
	import { ref, onMounted, computed, watch, getCurrentInstance, nextTick } from 'vue';
	import { onLoad } from '@dcloudio/uni-app';
	const { proxy } = getCurrentInstance();

	onMounted(() => {
		getControlsWidth();
	});
	onLoad((res) => {});

	const props = defineProps({
		inputStyle: {
			type: Object,
			default: {
				height: '80rpx',
				border: '1px solid #ccc',
				borderRadius: '10rpx',
				padding: '0 20rpx',
			},
		},
		placeholder: {
			type: String,
			default: '请输入密码',
		},
	});
	const password = defineModel('password', { type: String, required: true });
	// #ifdef H5
	watch(
		() => password.value.length,
		async (newVal, oldVal) => {
			newVal = newVal * 1;
			oldVal = oldVal * 1;
			if (oldVal === 0 && newVal === 1) {
				togglePassword();
				await nextTick();
				togglePassword();
			}
		},
		{ immediate: true }
	);
	// #endif
	const showPassword = ref(false);
	async function togglePassword() {
		showPassword.value = !showPassword.value;
		getControlsWidth();
	}
	// 获取controls-box的宽度
	const controlsWidth = ref(0);
	async function getControlsWidth() {
		await nextTick();
		const query = uni.createSelectorQuery().in(proxy);
		query
			.select('.controls-box')
			.boundingClientRect((res) => {
				controlsWidth.value = res?.width || 20;
			})
			.exec();
	}
</script>

<style lang="scss" scoped>
	.password-box {
		position: relative;
		width: 100%;
	}

	.controls-box {
		position: absolute;
		top: 50%;
		right: 20rpx;
		transform: translateY(-50%);
		display: flex;
		justify-content: center;
		align-items: center;
	}
</style>

代码很简单,主要只是为了自定义右侧icon,可根据项目需要自行修改代码

代码解释

为什么有个h5的条件编译代码?

因为uniapp的input密码输入框组件在h5中会出现密码输入框自带的icon,如下图

image.png

可以看出,在某些浏览器中会出现自带的icon

所以思路就是需要解决这个多出的icon

一般有三种方法:

  1. 使用css隐藏
  2. 让输入框失焦又聚焦
  3. 切换输入框的输入类型

这三种方法我使用的是第3种,因为:

  1. 第一种:需要做不同浏览器之间的兼容,然后同种浏览器又需要考虑不同版本之间的差异,成本太高

  2. 第二种:让输入框快速的失焦又聚焦有2个问题:

    1. 输入框会有一瞬间的闪烁,用户体验有影响
    2. 用户在输入的时候由于值的数量在快速增加,如果快速的让输入框失焦又聚焦,可能会导致光标位置错乱(很难复现,但是确实有遇到过),功能会收到影响

第三种方式其实也会有第二种方式中的第一个问题:输入框会有一瞬间的闪烁,用户体验有影响

但是他的问题目前我也就发现了这一个,和另外两个相比好很多了,属于是矮子里拔高个了🥲

如果有更好的方法欢迎评论区里讨论一下

页面使用

<passwordInput v-model:password="password">
		<template #controls-show>
			<text>显示控件</text>
		</template>
		<template #controls-hide>
			<text>隐藏控件</text>
		</template>
	</passwordInput>
        
import passwordInput from './passwordInput.vue';
	const password = ref('');