背景
测试:怎么忘记密码和确认密码被自动填充记住的密码了?麻烦不要填充。
我: 反手给密码框加上了 autocomplete="off"
测试:怎么忘记密码和确认密码还有记住密码的提示框?这不符合产品定义。麻烦去掉。
我: 我得研究下~
解决方案
网上找到了很多简单的方案,比如加 readonly 属性再去掉等,都没有效果。只剩下使用 input 模拟密码框交互的方案。
最后根据文章 vue 自定义密码输入框解决浏览器自动填充密码的问题的思路做了 vue3 的版本。
组件代码
代码思路:
- 创建 input 表单
- 监听 selectionchange 事件,过滤当前输入框输入时再处理
- 更新输入值为密码格式
- 添加预览切换
备注:代码逻辑上,依赖于光标位置的赋值取值时机先后;selectionchange 会比 change 事件后触发。
<script lang="ts" setup>
// src/components/password-input/password-input.vue
// 组件参考来源:https://github.com/lxmghct/my-vue-components/blob/main/src/components/PasswordInput/PasswordInput2.vue
import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons-vue';
import { useVModel } from '@vueuse/core';
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
export interface Props {
value: string;
}
const emit = defineEmits<{
'update:value': [Props['value']];
}>();
const props = defineProps<Props>();
const vmodelValue = useVModel(props, 'value', emit);
// 文字转化为原点
const toDot = (value: string) => '\u2022'.repeat(value.length);
const displayValue = ref(toDot(props.value));
const isPreview = ref(false);
// 更新输入值为密码格式
const updateDisplay = (value: string) => {
if (isPreview.value) {
displayValue.value = value;
} else {
displayValue.value = toDot(value); // 圆点
}
const end = inputRef.value.input.selectionEnd;
const start = inputRef.value.input.selectionStart;
// 更新值后,光标自动跑到最后,手动更新回原本的位置
nextTick(() => {
inputRef.value.input.setSelectionRange(start, end);
selection.start = start;
selection.end = end;
});
};
// 处理输入变化
const handleChange = (e: any) => {
/*
修改前的数据:vmodelValue.value, selection.start, selection.end
修改后的数据:e.target.value, inputRef.value.input.selectionEnd, inputRef.value.input.selectionEnd
*/
const currentStart = inputRef.value.input.selectionStart;
const currentValue = e.target.value;
const originValue = vmodelValue.value;
// 获取新输入的字符
const tempEnd = currentValue.length - (originValue.length - selection.end);
const newStr = currentValue.slice(selection.start, tempEnd);
// 更新输入框的值
vmodelValue.value =
originValue.slice(0, Math.min(selection.start, currentStart)) +
newStr +
originValue.slice(selection.end);
};
const inputRef = ref();
// 是否开始监听光标
const isSelect = ref(false);
// 缓存光标的位置
const selection = reactive({
start: 0,
end: 0,
});
onMounted(() => {
document.addEventListener('selectionchange', (e: any) => {
if (!isSelect.value) return;
selection.start = inputRef.value.input.selectionStart;
selection.end = inputRef.value.input.selectionEnd;
});
});
// 用于实现当前输入框的selectionchange
const handleFocus = () => {
isSelect.value = true;
};
const handleBlur = () => {
isSelect.value = false;
};
const visibilityToggle = () => {
isPreview.value = !isPreview.value;
};
watch(
() => vmodelValue.value,
(value: string) => updateDisplay(value),
);
watch(
() => isPreview.value,
() => updateDisplay(vmodelValue.value),
);
</script>
<template>
<div class="password-main">
<a-input
ref="inputRef"
v-model:value="displayValue"
placeholder="请输入"
autocomplete="off"
@change="handleChange"
@focus="handleFocus"
@blur="handleBlur"
>
<template #suffix>
<eye-outlined v-if="isPreview" @click="visibilityToggle" />
<eye-invisible-outlined v-else @click="visibilityToggle" />
</template>
</a-input>
</div>
</template>
<style lang="scss" scoped>
.password-main {
}
</style>
调用方式
<PasswordInput v-model:value="formState.password"></PasswordInput>
完整 a-form 使用
<template>
<div>
<a-form :model="formState" ref="formRef" :rules="rules" labelAlign="left">
<a-form-item label="确认密码" name="password" :validateFirst="true">
<PasswordInput v-model:value="formState.password"></PasswordInput>
</a-form-item>
</a-form>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import PasswordInput from '@/components/password-input/password-input.vue';
const formState = ref({
password: '',
});
const formRef = ref();
const rules = {
password: [{ required: true, message: '请输入密码' }],
};
</script>
如果有不足或有问题的地方,欢迎留言评论
文章转自:https://juejin.cn/post/7384618394061733898