需求背景:
用户通过 OCR 识别出身份证结果后回填值页面内,但是只允许修改 3 个字符,新增删除或修改超过3个字符后还原为原始字符
版本
vue.2.6.14
vant.2.x latest
代码:
<van-field
v-model="enterInfo.legalName"
v-limit-input="{ original: BACK_INFO.legalName, limit: 3 }"
name="legalName"
label="姓名"
placeholder="请输入姓名"
required
:rules="[{ required: true, message: '请输入姓名' }]"
></van-field>
// BACK_INFO 是识别结果的备份数据
// enterInfo 是页面中所有表单控件双向绑定的变量
import { feedback } from "@utils/toast";
import Vue from "vue";
/**
* Vue 指令,用于限制输入字段中可以修改的字符数量。
* 它还会跟踪对输入字段所做的更改。
* @example
* <input v-limit-input="{ original: '123456', limit: 3, tips: '最多只能修改3个字符' }" />
*/
Vue.directive("limit-input", {
/**
* 在包含组件的 VNode 及其子 VNode 更新后调用。
* @param {HTMLInputElement | any} el - 绑定指令的元素。
* @param {Object} binding - 包含指令的名称、值、旧值、表达式、参数和修饰符的对象。
* @param {Object} vnode - 绑定指令的 Vue 节点。
*/
update(el: HTMLInputElement | any, binding, vnode) {
const { original = "", limit = 3, tips } = binding.value; // 从指令的值中获取原始内容、限制长度和提示信息
// 获取el下的input
const inputEl = el.querySelector("input");
if (!inputEl || original == inputEl.value) return;
// 计算用户修改的字符数量
const diffCount = getDiffCount(original, inputEl.value);
const actionType = getActionType(original, inputEl.value);
// 如果用户添加字符且不超过限制,则允许
if (actionType === "add" && diffCount <= limit) {
return;
}
// 如果用户删除字符且不超过限制,则允许
if (actionType === "delete" && diffCount <= limit) {
return;
}
// 根据不同的操作类型判断是否超出限制
if (actionType === "modify" && diffCount > limit) {
feedback.msg(tips || `最多只能修改${limit}个字符`, { closeOnClick: true });
inputEl.value = original.substr(0, limit); // 截取前limit个字符
el.__vue__.$emit("input", inputEl.value);
} else if (actionType === "add" && inputEl.value.length > limit) {
feedback.msg(tips || `最多只能添加${limit}个字符`, { closeOnClick: true });
inputEl.value = original; // 恢复原始值
el.__vue__.$emit("input", inputEl.value);
} else if (actionType === "delete" && original.length - inputEl.value.length > limit) {
feedback.msg(tips || `最多只能删除${limit}个字符`, { closeOnClick: true });
inputEl.value = original; // 恢复原始值
el.__vue__.$emit("input", inputEl.value);
}
},
});
/**
* 计算两个字符串之间不同字符的数量
*/
function getDiffCount(oldStr: string, newStr: string) {
if (!oldStr || !newStr) return 0;
const maxLength = Math.max(oldStr.length, newStr.length);
let count = 0;
for (let i = 0; i < maxLength; i++) {
if (oldStr[i] !== newStr[i]) {
count++;
}
}
return count;
}
/**
* 判断用户的操作类型:添加、删除、修改
*/
function getActionType(oldStr: string, newStr: string) {
if (oldStr.length > newStr.length) {
return "delete";
} else if (oldStr.length < newStr.length) {
return "add";
} else {
return "modify";
}
}
// 导出指令
export default Vue.directive("limit-input");
// *****************
// feedback 是一个自定义封装的消息提示
使用:
// 引入
import limitInput from "@/composables/directive/limit-input";
export default {
directives: {
limitInput,
},
}