Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
提交反馈
为 GitLab 提交贡献
登录
切换导航
iBiz-Vue-R7-Res
项目
项目
详情
动态
版本
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
iBiz-R7前端标准模板
iBiz-Vue-R7-Res
提交
a840c2a4
提交
a840c2a4
编写于
9月 19, 2025
作者:
Cano1997
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
update: 添加v-safe-html指令
上级
515fe723
变更
7
显示空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
179 行增加
和
6 行删除
+179
-6
app-register.ts
src/app-register.ts
+3
-0
app-alert.vue
src/components/app-alert/app-alert.vue
+1
-1
app-file-upload.vue
src/components/app-file-upload/app-file-upload.vue
+1
-1
app-form-item.vue
src/components/app-form-item/app-form-item.vue
+3
-3
app-rawitem.vue
src/components/app-rawitem/app-rawitem.vue
+1
-1
safe-html.tsx
src/directive/safe-html/safe-html.tsx
+151
-0
xss-util.ts
src/utils/xss-util/xss-util.ts
+19
-0
未找到文件。
src/app-register.ts
浏览文件 @
a840c2a4
...
...
@@ -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
src/components/app-alert/app-alert.vue
浏览文件 @
a840c2a4
...
...
@@ -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>
...
...
src/components/app-file-upload/app-file-upload.vue
浏览文件 @
a840c2a4
...
...
@@ -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>
...
...
src/components/app-form-item/app-form-item.vue
浏览文件 @
a840c2a4
...
...
@@ -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>
...
...
src/components/app-rawitem/app-rawitem.vue
浏览文件 @
a840c2a4
...
...
@@ -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>
...
...
src/directive/safe-html/safe-html.tsx
0 → 100644
浏览文件 @
a840c2a4
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
();
src/utils/xss-util/xss-util.ts
0 → 100644
浏览文件 @
a840c2a4
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
人
到此讨论。请谨慎行事。
先完成此消息的编辑!
取消
想要评论请
注册
或
登录