背景:
担心高级的用户,通过F12开发者工具,将type="password"修改为type="text",从而可以看到密码,尽管心里面很嫌弃这个需求,但谁让咱就是个打工仔了,只能选择干
功能解析:
密码框能像普通的文本框一样正常输入文本,且将实际密码逐次替换为*,并且能获取到实际密码,因为调用后端接口时,需要传实际值(这就是槽点,高级用户都能知道改type,难道不知道去看接口传参嘛...)
具体实现:
组件:
<template>
<div class="password-input" :style="{ width: width }">
<input type="text" :value="hideValue" @input="handleInput" ref="password-input"/>
</div>
</template>
<script>
export default {
name: 'testInput',
props: {
modelVal: String,
placeholder: {
type: String,
default: '请输入'
},
width: {
type: String,
default: '300px'
}
},
model: {
prop: 'modelVal', // 指向props的参数名
event: 'input' //事件名称
},
data() {
return {
hideValue: ''
}
},
watch: {
modelVal: function(val) {
console.log('watch model', val)
this.render(val);
}
},
methods: {
render(val) {
this.hideValue = val.replace(/[^*]/g, '*');
// 同等长度替换时,会出现键入字符不被设置为*
this.$forceUpdate()
},
handleInput() {
this.dealPassword()
},
dealPassword(e) {
let new_pwd = ''; // 存储新的真实密码
let old_pwd = this.modelVal || ''; // 获取旧的真实密码
const pwd_input_elem = this.$refs['password-input']; // 获取密码输入框DOM
let val = this.$refs['password-input'].value; // 获取输入框中的值
const cursorIndex = pwd_input_elem.selectionStart; // 获取光标在输入框中的位置
// 旧的真实密码存在,且其字符串长度大于输入框的字符串长度,说明用户进行删除操作
if (old_pwd && old_pwd.length > val.length) {
const stop = old_pwd.length - val.length + cursorIndex; // 用户删除的字符串长度加光标的当前的位置,计算得出删除字符串的最后一个字符的位置
let del_string;
const reg = /[^*]/.exec(val);
if (reg) { // 说明为删除时有新值补充
del_string = old_pwd.substring(cursorIndex - 1, stop)
old_pwd = old_pwd.replace(del_string, '');
let temp = this.findStrIndex(val)
// 根据temp数组进行插入
temp.forEach(item => {
let isReplace = old_pwd && val.length === old_pwd.length
new_pwd = this.insertStr(old_pwd, item.index, item.val, isReplace); // 将用户新输入的字符插入旧的真实密码
this.cursorMove(pwd_input_elem, item.index + 1); // 设置光标的位置
old_pwd = new_pwd
})
} else { // 仅删除
del_string = old_pwd.substring(cursorIndex, stop)
old_pwd = old_pwd.replace(del_string, '');
new_pwd = old_pwd
}
} else {
// 新增或原长度替换
// reg中的非*字符 要么一个 要么几个 一个表示输入或者复制黏贴 几个 肯定是复制黏贴
const reg = /[^*]/.exec(val)
if (reg) {
if (reg.length > 0 && !/[*]/g.test(reg.input) && reg.input.length > 1) { // 说明为全部选中,则需要将旧密码置空
old_pwd = ''
} else {// 鼠标选中 部分元素(不包含所有)进行复制黏贴
// 看val中有多少个*
let num = this.findStrNum('*', val)
let del_string;
// old_pwd.length - num 表示在旧字符串中删除的位数 reg.index为新字符中第一个非*字符的位置
del_string = old_pwd.substring(reg.index, reg.index + (old_pwd.length - num))
old_pwd = old_pwd.replace(del_string, '');
}
}
// 是否为等长替换
let isReplace = old_pwd && val.length === old_pwd.length
let temp = this.findStrIndex(val)
// 根据temp数组进行插入
temp.forEach(item => {
new_pwd = this.insertStr(old_pwd, item.index, item.val, isReplace); // 将用户新输入的字符插入旧的真实密码
this.cursorMove(pwd_input_elem, item.index + 1); // 设置光标的位置
old_pwd = new_pwd
})
}
// 将实际密码同步到input输入框中
this.$emit('input', new_pwd);
},
// 查找字符串中在源串中的位置
findStrIndex(str) {
if (str) {
const reg = /[^*]/g;
const temp = [];
let match;
// 如果一个个输入的话,不会存在while循环,主要用来处理复制插入 替换等情况
while ((match = reg.exec(str)) !== null) {
temp.push({val: match[0], index: match.index});
}
return temp
} else {
return []
}
},
// 查找源字符串中目标字符串的个数
findStrNum(target, source) {
let num = 0;
let temp = source.split('')
temp.forEach(item => {
if (item === target) num++
})
return num
},
// 在字符串中插入字符串
insertStr(soure, start, newStr, isReplace) {
return soure.slice(0, start) + newStr + soure.slice(isReplace ? start + 1 : start);
},
// 控制光标的位置
cursorMove(elem, spos) {
// spos 光标的位置 -1为最后一位
if (spos < 0) spos = elem.value.length;
if (elem.setSelectionRange) {
//兼容火狐,谷歌
setTimeout(function() {
elem.setSelectionRange(spos, spos);
elem.focus();
}, 0);
} else if (elem.createTextRange) {
//兼容IE
var rng = elem.createTextRange();
rng.move('character', spos);
rng.select();
}
}
}
}
</script>
调用:
<template>
<div>
实际密码: {{ realPwd }}
<testInputPwd v-model="inputVal" @input="getRealPwd" />
</div>
</template>
<script>
import inputPwd from './inputPwd.vue'
export default {
components: {
inputPwd
},
data() {
return {
inputVal: '',
realPwd: ''
}
},
methods: {
getRealPwd(val) {
console.log('getRealPwd', val)
// 替换空格
this.realPwd = val.replace(/\s+/g, '')
}
}
}
</script>
以上方案存在的问题如下:
1.如果输入*,分为两种,input中为空,会获取不到真实密码;非空,输入*,会导致input中内容被清空
2.复制黏贴带*的字符串,黏贴时会将*去掉,但是各个元素索引值未变,导致再次复制插入时,位置会错乱
最后竟然发现可以用一个css属性,来实现这个需求,-webkit-text-security: disc即可以实现输入密码显示为小圆点,变量仍为真实值。妥妥的感觉受到了心灵的打击,哈哈哈
欢迎各位针对逻辑存在的问题,看看有没有办法修复,除了禁止复制黏贴啊。。。。。
参考文章:密码输入框组件的实现
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。