68 lines
2.9 KiB
TypeScript
68 lines
2.9 KiB
TypeScript
|
|
import type { Directive } from 'vue';
|
|||
|
|
import { watch } from 'vue';
|
|||
|
|
|
|||
|
|
const xssTokenReplace = { // xss repalce rules
|
|||
|
|
'<': '<',
|
|||
|
|
'>': '>'
|
|||
|
|
// '"': '"',
|
|||
|
|
// "'": '''
|
|||
|
|
};
|
|||
|
|
function generateReg(replaceRules:Record<string, string>) { // 替换字符查找
|
|||
|
|
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<string, string>) {
|
|||
|
|
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;
|
|||
|
|
|