封装背景
项目有时需要对登录/注册/找回密码这类的页面中的密码输入框右侧的显示/隐藏密码的icon进行自定义
比如
- 默认的icon无法满足需要,需要用带有项目特色的icon
- 由于不同操作环境(安卓和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,如下图
可以看出,在某些浏览器中会出现自带的icon
所以思路就是需要解决这个多出的icon
一般有三种方法:
- 使用css隐藏
- 让输入框失焦又聚焦
- 切换输入框的输入类型
这三种方法我使用的是第3种,因为:
-
第一种:需要做不同浏览器之间的兼容,然后同种浏览器又需要考虑不同版本之间的差异,成本太高
-
第二种:让输入框快速的失焦又聚焦有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('');