提交 a840c2a4 编写于 作者: Cano1997's avatar Cano1997

update: 添加v-safe-html指令

上级 515fe723
......@@ -109,6 +109,7 @@ import AppInputIp from './components/app-input-ip/app-input-ip.vue';
import Loadding from './directive/loadding/loadding';
import AppColorSpan from './components/app-color-span/app-color-span.vue';
import AppColorPicker from './components/app-color-picker/app-color-picker.vue';
import { SafeHtml } from './directive/safe-html/safe-html';
// 全局挂载UI实体服务注册中心
......@@ -234,5 +235,7 @@ export const AppComponents = {
v.directive('loading',Loadding);
v.component('app-color-span', AppColorSpan);
v.component('app-color-picker', AppColorPicker);
// 注册指令
v.directive('safe-html', SafeHtml);
},
};
\ No newline at end of file
......@@ -11,7 +11,7 @@
:closable="item.closable"
@close="alertClose(item)">
<template slot>
<span v-html="item.content"></span>
<span v-safe-html="item.content"></span>
</template>
</el-alert>
</template>
......
......@@ -20,7 +20,7 @@
>
<el-button v-if="!isdrag" size='small' icon='el-icon-upload2' :disabled="disabled">{{this.$t('app.fileUpload.caption')}}</el-button>
<i v-if="isdrag" class="el-icon-upload2"></i>
<div v-if="isdrag" class="el-upload__text" v-html="$t('components.appFileUpload.uploadText')"></div>
<div v-if="isdrag" class="el-upload__text" v-safe-html="$t('components.appFileUpload.uploadText')"></div>
</el-upload>
</el-col>
</el-row>
......
......@@ -33,13 +33,13 @@
<span v-if="required" style="color:red;">* </span>
<span v-if="!isEmptyCaption">
<el-tooltip v-if="isShowTip" placement="top" effect="light">
<span v-html="caption"></span>
<span v-safe-html="caption"></span>
<template >
<span slot="content" v-html="caption" ></span>
<span slot="content" v-safe-html="caption" ></span>
</template>
</el-tooltip>
<template v-if="!isShowTip">
<span v-html="caption" ></span>
<span v-safe-html="caption" ></span>
</template>
</span>
</span>
......
......@@ -4,7 +4,7 @@
<div v-if="Object.is(contentType,'RAW')" :class="contentStyle">
<slot></slot>
</div>
<div v-else-if="Object.is(contentType,'HTML')" :class="contentStyle" v-html="htmlContent" />
<div v-else-if="Object.is(contentType,'HTML')" :class="contentStyle" v-safe-html="htmlContent" />
<div v-else-if="Object.is(contentType,'IMAGE')" :class="contentStyle">
<i :class="imageClass ? imageClass : ''"></i>
</div>
......
import { safeXss } from '@/utils/xss-util/xss-util';
import { VNode } from 'vue';
/**
* 安全html
*
* @export
* @class SafeHtml
*/
export const SafeHtml: any = {
/**
* 指令初始化
*
* @param {HTMLDivElement} el
* @param {*} binding
* @param {VNode} vNode
* @param {VNode} oldVNode
*/
bind(el: HTMLDivElement, binding: any, vNode: VNode, oldVNode: VNode) {
shc.init(el, binding);
},
/**
* 指令更新
*
* @param {HTMLDivElement} el
* @param {*} binding
* @param {VNode} vNode
* @param {VNode} oldVNode
*/
componentUpdated(el: HTMLDivElement, binding: any, vNode: VNode, oldVNode: VNode) {
shc.update(el, binding);
},
};
/**
* 微标控制器
*
* @export
* @class SafeHtmlController
*/
export class SafeHtmlController {
/**
* 唯一实例
*
* @private
* @static
* @memberof SafeHtmlControllerController
*/
private static readonly instance = new SafeHtmlController();
/**
* 容器
*
* @protected
* @type {HTMLDivElement}
* @memberof NotificationSignalController
*/
protected el!: HTMLElement;
/**
* Creates an instance of SafeHtmlControllerController.
* @memberof SafeHtmlControllerController
*/
private constructor() {
if (SafeHtmlController.instance) {
return SafeHtmlController.instance;
}
}
/**
* 初始化
*
* @param {HTMLDivElement}
* @param {any}
* @memberof SafeHtmlController
*/
public init(el: HTMLDivElement, binding: any): void {
const { value } = binding;
// 先清空容器
el.innerHTML = '';
if (value) {
const dom = this.parseHtml(value);
if (dom) {
el.appendChild(dom);
} else {
// 解析失败时使用xss过滤
el.innerHTML = String(safeXss(value));
}
}
}
/**
* 更新
*
* @param {HTMLDivElement}
* @param {any}
* @memberof SafeHtmlController
*/
public update(el: HTMLDivElement, binding: any): void {
const { value } = binding;
// 先清空容器
el.innerHTML = '';
if (value) {
const dom = this.parseHtml(value);
if (dom) {
el.appendChild(dom);
} else {
// 解析失败时使用xss过滤
el.innerHTML = String(safeXss(value));
}
}
}
/**
* @description 解析html
* @param {string} html
* @returns {*} {(HTMLElement | null)}
* @memberof SafeHtmlController
*/
public parseHtml(html: string): HTMLElement | null {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'application/xml');
const parseError = doc.querySelector('parsererror');
if (parseError) {
if (parseError.textContent) {
const msg = parseError.textContent
.replace(/\s+/g, ' ')
.trim();
console.error(msg);
}
return null;
}
const dom = doc.documentElement;
return dom;
}
/**
* 获取唯一实例
*
* @static
* @returns {SafeHtmlController}
* @memberof SafeHtmlController
*/
public static getInstance(): SafeHtmlController {
return SafeHtmlController.instance;
}
}
// 导出服务
export const shc: SafeHtmlController = SafeHtmlController.getInstance();
import xss from 'xss';
/**
* @description 使用xss过滤
* @export
* @param {(string | number)} value
* @returns {*} {string}
*/
export function safeXss(value: string | number): string | number {
// 数值不做处理
if (typeof value === 'number') {
return value;
}
const result = xss(value as string);
if (result !== value) {
console.warn(`xss输入值${value}存在不规范格式,已自动调整!`);
}
return result;
}
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册