import type { Directive } from 'vue'; import { watch } from 'vue'; const xssTokenReplace = { // xss repalce rules '<': '<', '>': '>' // '"': '"', // "'": ''' }; function generateReg(replaceRules:Record) { // 替换字符查找 const temArr = []; temArr.push('('); for (const key in replaceRules) { temArr.push(key); temArr.push('|'); } temArr.length > 1 && temArr.pop(); temArr.push(')'); return new RegExp(temArr.join(''), 'g'); } const replaceReg = generateReg(xssTokenReplace); function replaceAll(str:string, replaceRules:Record) { return str.replace(replaceReg, function(match, position, original) { return replaceRules[match]; }); } const repairMaxLength = function(str:string, maxLength:number) { // 设置最大长度不生效的问题 const len = str.length; return len > maxLength ? str.slice(0, maxLength) : str; }; const handlePaste = function(vnode:any, event:any) { // 复制粘贴过滤 const value = event.target.value; const pos = event.target.selectionStart; const pasetText = (event.clipboardData || (window as any).clipboardData).getData('text'); const insertStr = replaceAll(pasetText, xssTokenReplace); let composeStr = value.slice(0, pos) + insertStr + value.slice(pos); if (vnode.ctx.attrs?.maxlength) composeStr = repairMaxLength(composeStr, vnode.ctx.attrs?.maxlength); event.target.value = composeStr; event.target.setSelectionRange(pos + insertStr.length, pos + insertStr.length);// 计算鼠标位置 vnode.ctx.emit('update:modelValue', event.target.value);// 双向绑定更新 event.preventDefault(); }; const handleInput = function(vnode:any, event:any) { // 输入替换 if (replaceReg.test(event.key)) { const pos = event.target.selectionStart; const value = event.target.value; const insertStr = replaceAll(event.key, xssTokenReplace); let composeStr = value.slice(0, pos) + insertStr + value.slice(pos); if (vnode.ctx.attrs?.maxlength) composeStr = repairMaxLength(composeStr, vnode.ctx.attrs?.maxlength); event.target.value = composeStr; event.target.setSelectionRange(pos + insertStr.length, pos + insertStr.length);// 计算鼠标位置 vnode.ctx.emit('update:modelValue', event.target.value);// 双向绑定更新 event.preventDefault(); } }; export const xssEscape = (str:string) => replaceAll(str, xssTokenReplace);// 替换渲染中的非法字符(如果需要保留v-html的元素渲染,请勿使用) let handleMoveInput:any = null; let handleMovePaste:any = null; export default { mounted(el, binding, vnode) { el.addEventListener('paste', handleMovePaste = handlePaste.bind(null, vnode)); el.addEventListener('keypress', handleMoveInput = handleInput.bind(null, vnode)); }, beforeUnmount(el) { el.removeEventListener('paste', handleMovePaste); el.removeEventListener('keypress', handleMoveInput); } } as Directive;