Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
N
nse-ui
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Members
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
吴超
nse-ui
Commits
5897447f
Commit
5897447f
authored
Aug 26, 2025
by
ningjihai
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
脱敏策略添加
parent
c0126dce
隐藏空白字符变更
内嵌
并排
正在显示
9 个修改的文件
包含
2108 行增加
和
891 行删除
+2108
-891
src/api/desensitizationStrategy/index.js
+45
-81
src/views/classification/Classification/index.vue
+1
-1
src/views/desensitizationStrategy/DesensitizationStrategy/QueryForm.vue
+12
-12
src/views/desensitizationStrategy/DesensitizationStrategy/list.vue
+139
-80
src/views/desensitizationStrategy/DesensitizationStrategy/modules/AddStrategyDialog.vue
+0
-399
src/views/desensitizationStrategy/DesensitizationStrategy/modules/DesensitizationRuleDialog.vue
+269
-318
src/views/desensitizationStrategy/DesensitizationStrategy/modules/StrategyAddDialog.vue
+717
-0
src/views/desensitizationStrategy/DesensitizationStrategy/modules/StrategyAddDialogccc.vue
+739
-0
src/views/desensitizationStrategy/DesensitizationStrategy/modules/TreeFilter.vue
+186
-0
没有找到文件。
src/api/desensitizationStrategy/index.js
View file @
5897447f
import
request
from
'@/utils/request'
import
request
from
'@/utils/request'
/**
/**
*
资产库 - 查询用户列表
*
脱敏策略 - 列表查询
* @param {*} query
* @param {*} query
* @returns
* @returns
*/
*/
export
function
queryUser
(
query
)
{
export
function
getTableList
(
data
)
{
return
request
({
return
request
({
url
:
'/co
nsole/user/queryAll
'
,
url
:
'/co
re/desensitizationStrategy/loadDesensitizationStrategy
'
,
method
:
'
ge
t'
,
method
:
'
pos
t'
,
params
:
query
data
:
data
})
})
}
}
/**
* 资产库 - 查询列表
* @param {*} query
* @returns
*/
export
function
queryAll
(
query
)
{
export
function
queryAll
(
query
)
{
return
request
({
return
request
({
url
:
'/co
re/datasystem
/queryAll'
,
url
:
'/co
nsole/role
/queryAll'
,
method
:
'get'
,
method
:
'get'
,
params
:
query
params
:
query
})
})
}
}
/**
* 资产库 - 数据源明细
export
function
desensitizationruleQuery
(
query
)
{
* @param {*} query
* @returns
*/
export
function
initEdit
(
query
)
{
return
request
({
return
request
({
url
:
'/core/d
atasystem/initEdit
'
,
url
:
'/core/d
esensitizationrule/query?dataarea=
'
,
method
:
'get'
,
method
:
'get'
,
params
:
query
params
:
query
})
})
}
}
export
function
queryversion
(
data
)
{
return
request
({
url
:
'/core/fieldscopedata/queryversion'
,
method
:
'post'
,
data
:
data
})
}
export
function
queryTask
(
data
)
{
return
request
({
url
:
'/core/fieldscopedata/queryTask'
,
method
:
'post'
,
data
:
data
})
}
/**
* 资产库 - 查询数据库版本
export
function
queryProVersion
(
data
)
{
* @param {*} query
return
request
({
* @returns
url
:
'/core/fieldscopedata/queryProVersion'
,
*/
method
:
'post'
,
export
function
queryDatadis
(
query
)
{
data
:
data
})
}
export
function
queryTaskVersion
(
query
)
{
return
request
({
return
request
({
url
:
'/core/
datadis/queryAll
'
,
url
:
'/core/
fieldscopedata/queryTaskVersion
'
,
method
:
'get'
,
method
:
'get'
,
params
:
query
params
:
query
})
})
}
}
/**
export
function
tdatasourceQuery
(
data
)
{
* 资产库 - 数据库版本检测
* @param {*} data
* @returns
*/
export
function
checkVersion
(
data
)
{
return
request
({
return
request
({
url
:
'/core/
datasystem/checkVersion
'
,
url
:
'/core/
tdatasource/query
'
,
method
:
'post'
,
method
:
'post'
,
data
:
data
data
:
data
})
})
}
}
export
function
queryShemas
(
data
)
{
/**
* 资产库 - 测试数据源名称是否重复
* @param {*} data
* @returns
*/
export
function
checkRepeatName
(
data
)
{
return
request
({
return
request
({
url
:
'/core/
datasystem/checkRepeatName
'
,
url
:
'/core/
modifysubsettask/queryShemas
'
,
method
:
'post'
,
method
:
'post'
,
data
:
data
data
:
data
})
})
}
}
/**
export
function
rowsensitivelevel
(
tableName
,
data
)
{
* 资产库 - 测试连接
* @param {*} data
* @returns
*/
export
function
testConnect
(
data
)
{
return
request
({
return
request
({
url
:
'/core/
datasystem/testConnect'
,
url
:
'/core/
rowsensitivelevel/'
+
tableName
,
method
:
'post'
,
method
:
'post'
,
data
:
data
data
:
data
})
})
}
}
/**
export
function
queryDesensitizationTables
(
data
)
{
* 资产库 - 新增修改接口
* @param {*} data
* @returns
*/
export
function
add
(
data
)
{
return
request
({
return
request
({
url
:
'/core/
datasystem/add
'
,
url
:
'/core/
rowsensitivelevel/queryDesensitizationTables
'
,
method
:
'post'
,
method
:
'post'
,
data
:
data
data
:
data
})
})
}
}
/**
export
function
queryOriginalList
(
data
)
{
* 资产库 - 关联检测
* @param {*} data
* @returns
*/
export
function
checkDatasystemUsed
(
data
)
{
return
request
({
return
request
({
url
:
'/core/
datasystem/checkDatasystemUsed
'
,
url
:
'/core/
encryptionconfig/queryOriginalList
'
,
method
:
'post'
,
method
:
'post'
,
data
:
data
data
:
data
})
})
}
}
/**
export
function
desensitizationStrategyDetails
(
data
)
{
* 资产库 - 删除数据源
* @param {*} data
* @returns
*/
export
function
del
(
data
)
{
return
request
({
return
request
({
url
:
'/core/d
atasystem/del
'
,
url
:
'/core/d
esensitizationStrategy/desensitizationStrategyDetails
'
,
method
:
'post'
,
method
:
'post'
,
data
:
data
data
:
data
})
})
}
}
/**
* 资产库 - 查询字符编码
* @param {*} query
* @returns
*/
export
function
queryCharset
(
query
)
{
return
request
({
url
:
'/core/datasystem/queryCharset'
,
method
:
'get'
,
params
:
query
})
}
\ No newline at end of file
src/views/classification/Classification/index.vue
View file @
5897447f
...
@@ -87,7 +87,7 @@ watch([currentNodeLevel, currentNodeData, activeTab], async ([level, node, tab])
...
@@ -87,7 +87,7 @@ watch([currentNodeLevel, currentNodeData, activeTab], async ([level, node, tab])
})
})
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
projectId
.
value
=
route
.
query
.
projectId
||
'df345570-d044-47b3-a2c6-0ff265f89b80'
projectId
.
value
=
route
.
query
.
projectId
||
sessionStorage
.
getItem
(
'projectId'
)
||
'df345570-d044-47b3-a2c6-0ff265f89b80'
if
(
projectId
.
value
)
{
if
(
projectId
.
value
)
{
// 1. 查询数据源列表
// 1. 查询数据源列表
const
res
=
await
query
({
project_id
:
projectId
.
value
})
const
res
=
await
query
({
project_id
:
projectId
.
value
})
...
...
src/views/desensitizationStrategy/DesensitizationStrategy/QueryForm.vue
View file @
5897447f
...
@@ -34,9 +34,9 @@ function onSearch() {
...
@@ -34,9 +34,9 @@ function onSearch() {
// 重置
// 重置
function
onReset
(
formRef
:
FormInstance
)
{
function
onReset
(
formRef
:
FormInstance
)
{
queryForm
.
value
.
titl
e
=
''
queryForm
.
value
.
strategyNam
e
=
''
queryForm
.
value
.
remark
=
''
queryForm
.
value
.
remark
s
=
''
queryForm
.
value
.
stat
us
=
''
queryForm
.
value
.
stat
e
=
''
emit
(
'reset'
,
formRef
)
emit
(
'reset'
,
formRef
)
}
}
...
@@ -49,9 +49,9 @@ function onReset(formRef: FormInstance) {
...
@@ -49,9 +49,9 @@ function onReset(formRef: FormInstance) {
:model=
"queryForm"
:model=
"queryForm"
@
search=
"onSearch"
@
search=
"onSearch"
@
reset=
"onReset"
>
@
reset=
"onReset"
>
<el-form-item
label=
"策略名"
prop=
"
titl
e"
>
<el-form-item
label=
"策略名"
prop=
"
strategyNam
e"
>
<el-input
<el-input
v-model=
"queryForm.
titl
e"
v-model=
"queryForm.
strategyNam
e"
placeholder=
"请输入策略名"
placeholder=
"请输入策略名"
clearable
clearable
/>
/>
...
@@ -59,23 +59,23 @@ function onReset(formRef: FormInstance) {
...
@@ -59,23 +59,23 @@ function onReset(formRef: FormInstance) {
<el-form-item
label=
"备注"
prop=
"remark"
>
<el-form-item
label=
"备注"
prop=
"remark
s
"
>
<el-input
<el-input
v-model=
"queryForm.remark"
v-model=
"queryForm.remark
s
"
placeholder=
"请输入备注"
placeholder=
"请输入备注"
clearable
clearable
/>
/>
</el-form-item>
</el-form-item>
<el-form-item
label=
"状态"
prop=
"stat
us
"
>
<el-form-item
label=
"状态"
prop=
"stat
e
"
>
<el-select
<el-select
v-model=
"queryForm.stat
us
"
v-model=
"queryForm.stat
e
"
placeholder=
"请选择状态"
placeholder=
"请选择状态"
clearable
clearable
>
>
<el-option
label=
"全部"
value=
""
/>
<el-option
label=
"已启用"
value=
"
active
"
/>
<el-option
label=
"已启用"
value=
"
1
"
/>
<el-option
label=
"未启用"
value=
"
inactive
"
/>
<el-option
label=
"未启用"
value=
"
0
"
/>
</el-select>
</el-select>
</el-form-item>
</el-form-item>
</page-wrapper-search>
</page-wrapper-search>
...
...
src/views/desensitizationStrategy/DesensitizationStrategy/list.vue
View file @
5897447f
<
script
setup
name=
"ProjectManageList"
>
<
script
setup
name=
"ProjectManageList"
>
import
{
getCurrentInstance
,
reactive
,
ref
,
toRefs
}
from
'vue'
import
{
getCurrentInstance
,
reactive
,
ref
,
toRefs
,
onMounted
}
from
'vue'
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
{
useRouter
}
from
'vue-router'
import
{
useRouter
}
from
'vue-router'
import
useAppStore
from
'@/store/modules/app'
import
useAppStore
from
'@/store/modules/app'
import
usePermissionStore
from
'@/store/modules/permission'
import
{
changeRoute
}
from
'@/utils/switchRoute'
import
{
changeRoute
}
from
'@/utils/switchRoute'
import
QueryForm
from
'./QueryForm.vue'
import
QueryForm
from
'./QueryForm.vue'
import
AddStrategyDialog
from
'./modules/AddStrategyDialog.vue'
import
StrategyAddDialog
from
'./modules/StrategyAddDialog.vue'
// 统一对话框组件
import
StrategyDetailDialog
from
'./modules/StrategyDetailDialog.vue'
// 引入详情组件
import
EditStrategyDialog
from
'./modules/EditStrategyDialog.vue'
// 引入编辑策略组件
import
{
getTableList
,
queryAll
}
from
'@/api/desensitizationStrategy'
const
appStore
=
useAppStore
()
const
appStore
=
useAppStore
()
const
permissionStore
=
usePermissionStore
()
const
router
=
useRouter
()
const
router
=
useRouter
()
const
emit
=
defineEmits
([
'page'
])
const
emit
=
defineEmits
([
'page'
])
const
{
proxy
}
=
getCurrentInstance
()
const
{
proxy
}
=
getCurrentInstance
()
// 新增策略弹窗引用
const
strategyDialogRef
=
ref
()
const
addStrategyDialogRef
=
ref
()
onMounted
(()
=>
{
handleQuery
()
queryAll
().
then
(
res
=>
{
if
(
res
.
flag
)
{
roleList
.
value
=
res
.
data
}
else
{
ElMessage
.
error
(
res
.
msg
)
}
}).
catch
(
err
=>
{
ElMessage
.
error
(
err
.
msg
)
})
})
function
onReset
(
formQuery
)
{
function
onReset
(
formQuery
)
{
console
.
log
(
'onReset'
)
formQuery
.
resetFields
()
formQuery
.
resetFields
()
handleQuery
()
handleQuery
()
}
}
const
roleList
=
ref
([])
function
onQuery
()
{
function
onQuery
()
{
handleQuery
()
handleQuery
()
}
}
// 搜索按钮操作
// 搜索按钮操作
function
handleQuery
()
{
function
handleQuery
()
{
console
.
log
(
'queryParams'
,
queryParams
.
value
)
queryParams
.
value
.
pageno
=
1
queryParams
.
value
.
pageNum
=
1
getList
()
getList
()
}
}
const
data
=
reactive
({
const
data
=
reactive
({
queryParams
:
{
queryParams
:
{
pageNum
:
1
,
pageno
:
1
,
pageSize
:
8
pagesize
:
8
,
remarks
:
''
,
state
:
''
,
strategyName
:
''
}
}
})
})
// 表格数据
// 表格数据
const
{
queryParams
}
=
toRefs
(
data
)
const
{
queryParams
}
=
toRefs
(
data
)
const
total
=
ref
(
3
)
const
total
=
ref
(
0
)
const
loading
=
ref
(
false
)
const
loading
=
ref
(
false
)
const
projectId
=
ref
(
sessionStorage
.
getItem
(
'projectId'
))
// 查询列表
// 查询列表
function
getList
()
{
function
getList
()
{
loading
.
value
=
true
loading
.
value
=
true
setTimeout
(()
=>
{
getTableList
({
loading
.
value
=
false
pageno
:
queryParams
.
value
.
pageno
,
},
3000
)
pagesize
:
queryParams
.
value
.
pagesize
,
projectid
:
projectId
.
value
,
remarks
:
queryParams
.
value
.
remarks
,
roleGroup
:
''
,
state
:
queryParams
.
value
.
state
,
strategyName
:
queryParams
.
value
.
strategyName
}).
then
(
res
=>
{
if
(
res
.
flag
)
{
tableData
.
value
=
res
.
data
.
list
total
.
value
=
res
.
data
.
total
}
loading
.
value
=
false
})
}
}
function
pageProjectManage
()
{
function
pageProjectManage
()
{
changeRoute
()
changeRoute
()
router
.
push
({
router
.
push
({
path
:
'/project/Project'
path
:
'/project/Project'
})
})
}
}
const
strategyAddDialogRef
=
ref
()
/**
/**
* 新增策略
* 新增策略
*/
*/
function
handleAdd
(){
function
handleAdd
()
{
addStrategyDialogRef
.
value
.
openDialog
()
}
dialog
.
value
.
title
=
'添加策略'
const
tableData
=
ref
([
dialog
.
value
.
type
=
'addtactics'
{
index
:
1
,
strategyAddDialogRef
.
value
.
openDialog
(
'addtactics'
)
id
:
'1'
,
}
name
:
'测试'
,
remark
:
'123'
,
const
tableData
=
ref
([])
createTime
:
'2025-08-21 16:55:30'
,
const
dialog
=
ref
({
creator
:
'admin'
,
title
:
''
,
status
:
'1'
type
:
''
}
})
])
/**
const
strategyDetailDialogRef
=
ref
()
* 查看策略详情
*/
const
showDetail
=
(
row
)
=>
{
const
showDetail
=
(
row
)
=>
{
strategyDetailDialogRef
.
value
.
openDialog
(
row
)
dialog
.
value
.
title
=
'详情'
dialog
.
value
.
type
=
'info'
strategyDialogRef
.
value
.
openDialog
(
'detail'
,
row
)
}
}
// 编辑策略弹窗引用
const
editStrategyDialogRef
=
ref
()
/**
* 编辑策略
*/
const
editStrategy
=
(
row
)
=>
{
const
editStrategy
=
(
row
)
=>
{
console
.
log
(
'编辑策略:'
,
row
)
editStrategyDialogRef
.
value
.
openDialog
(
row
)
dialog
.
value
.
title
=
'编辑策略'
dialog
.
value
.
type
=
'edit'
strategyAddDialogRef
.
value
.
openDialog
(
'edit'
,
row
)
}
}
/**
* 删除策略
*/
const
deleteStrategy
=
(
row
)
=>
{
const
deleteStrategy
=
(
row
)
=>
{
ElMessageBox
.
confirm
(
ElMessageBox
.
confirm
(
`确定删除【
${
row
.
n
ame
}
】策略吗?`
,
`确定删除【
${
row
.
strategyN
ame
}
】策略吗?`
,
'删除确认'
,
'删除确认'
,
{
{
confirmButtonText
:
'确定'
,
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
type
:
'warning'
beforeClose
:
(
action
,
instance
,
done
)
=>
{
if
(
action
===
'confirm'
)
{
instance
.
confirmButtonLoading
=
true
// 这里调用实际删除API
setTimeout
(()
=>
{
done
()
// 删除成功后刷新数据或从tableData中移除
tableData
.
value
=
tableData
.
value
.
filter
(
item
=>
item
.
index
!==
row
.
index
)
},
1000
)
}
else
{
done
()
}
}
}
}
).
then
(()
=>
{
).
then
(()
=>
{
ElMessage
({
deleteStrategyApi
(
row
.
id
).
then
(
res
=>
{
type
:
'success'
,
if
(
res
.
flag
)
{
message
:
'删除成功'
,
ElMessage
.
success
(
'删除成功'
)
getList
()
// 刷新列表
}
else
{
ElMessage
.
error
(
res
.
msg
)
}
}).
catch
(
err
=>
{
ElMessage
.
error
(
'删除失败'
)
})
})
}).
catch
(()
=>
{
}).
catch
(()
=>
{
// 用户取消删除
// 用户取消删除
})
})
}
}
/**
* 启用/禁用策略
*/
const
toggleStatus
=
(
row
)
=>
{
const
toggleStatus
=
(
row
)
=>
{
console
.
log
(
'启用/禁用:'
,
row
)
const
newStatus
=
row
.
status
===
'1'
?
'0'
:
'1'
// changeRoleStatus(row.roleId, row.status).then(() => {
const
action
=
newStatus
===
'1'
?
'启用'
:
'禁用'
// ElMessage({
// type: 'success',
ElMessageBox
.
confirm
(
// message: '操作成功',
`确定要
${
action
}
【
${
row
.
strategyName
}
】策略吗?`
,
// })
`
${
action
}
确认`
,
// })
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}
).
then
(()
=>
{
// 调用启用/禁用API
// changeStrategyStatus(row.id, newStatus).then(res => {
// if (res.flag) {
// ElMessage.success(`${action}成功`)
// getList() // 刷新列表
// } else {
// ElMessage.error(res.msg)
// }
// }).catch(err => {
// ElMessage.error(`${action}失败`)
// })
ElMessage
.
info
(
`
${
action
}
功能待实现`
)
}).
catch
(()
=>
{
// 用户取消操作
})
}
}
</
script
>
</
script
>
...
@@ -162,18 +224,20 @@ const toggleStatus = (row) => {
...
@@ -162,18 +224,20 @@ const toggleStatus = (row) => {
v-model=
"queryParams"
v-model=
"queryParams"
@
query=
"onQuery"
@
query=
"onQuery"
@
reset=
"onReset"
/>
@
reset=
"onReset"
/>
<el-table
<el-table
:data=
"tableData"
:data=
"tableData"
border
border
style=
"width: 100%"
style=
"width: 100%"
:header-cell-style=
"{ background: '#f5f7fa', color: '#606266' }"
:header-cell-style=
"{ background: '#f5f7fa', color: '#606266' }"
v-loading=
"loading"
>
>
<el-table-column
prop=
"index"
label=
"序号"
width=
"80"
align=
"center"
/>
<el-table-column
prop=
"index"
label=
"序号"
width=
"80"
align=
"center"
/>
<el-table-column
prop=
"
n
ame"
label=
"策略名称"
min-width=
"120"
/>
<el-table-column
prop=
"
strategyN
ame"
label=
"策略名称"
min-width=
"120"
/>
<el-table-column
prop=
"remark"
label=
"备注"
min-width=
"120"
show-overflow-tooltip
/>
<el-table-column
prop=
"remark
s
"
label=
"备注"
min-width=
"120"
show-overflow-tooltip
/>
<el-table-column
prop=
"create
T
ime"
label=
"创建时间"
min-width=
"180"
/>
<el-table-column
prop=
"create
t
ime"
label=
"创建时间"
min-width=
"180"
/>
<el-table-column
prop=
"creat
o
r"
label=
"创建人"
width=
"120"
/>
<el-table-column
prop=
"creat
euse
r"
label=
"创建人"
width=
"120"
/>
<el-table-column
prop=
"stat
us
"
label=
"状态"
width=
"100"
align=
"center"
>
<el-table-column
prop=
"stat
ezh
"
label=
"状态"
width=
"100"
align=
"center"
>
<
template
#
default=
"{ row }"
>
<
template
#
default=
"{ row }"
>
<el-tag
:type=
"row.status === '1' ? 'success' : 'info'"
>
<el-tag
:type=
"row.status === '1' ? 'success' : 'info'"
>
{{
row
.
status
===
'1'
?
'已启用'
:
'未启用'
}}
{{
row
.
status
===
'1'
?
'已启用'
:
'未启用'
}}
...
@@ -198,20 +262,15 @@ const toggleStatus = (row) => {
...
@@ -198,20 +262,15 @@ const toggleStatus = (row) => {
<pagination
<pagination
v-show=
"total > 0"
v-show=
"total > 0"
:total=
"total"
:total=
"total"
v-model:page=
"queryParams.page
Num
"
v-model:page=
"queryParams.page
no
"
v-model:limit=
"queryParams.page
S
ize"
v-model:limit=
"queryParams.page
s
ize"
@
pagination=
"getList"
@
pagination=
"getList"
/>
/>
</div>
</div>
</div>
</div>
<!-- 新增策略弹窗 -->
<!-- 统一策略对话框组件 -->
<AddStrategyDialog
ref=
"addStrategyDialogRef"
/>
<StrategyAddDialog
:title=
"dialog.title"
ref=
"strategyAddDialogRef"
@
refresh=
"getList"
/>
<!-- 详情弹窗 -->
<StrategyDetailDialog
ref=
"strategyDetailDialogRef"
/>
<!-- 编辑策略弹窗 -->
<EditStrategyDialog
ref=
"editStrategyDialogRef"
/>
</div>
</div>
</template>
</template>
...
...
src/views/desensitizationStrategy/DesensitizationStrategy/modules/AddStrategyDialog.vue
deleted
100644 → 0
View file @
c0126dce
<
template
>
<el-dialog
v-model=
"dialogVisible"
:title=
"currentStep === 1 ? '添加策略 - 步骤1/2' : '添加策略 - 步骤2/2'"
width=
"80%"
:before-close=
"handleClose"
>
<!-- 步骤1:基本信息 -->
<div
v-if=
"currentStep === 1"
class=
"step-content"
>
<el-form
:model=
"formData"
:rules=
"rules"
ref=
"step1Form"
label-width=
"120px"
>
<el-form-item
label=
"策略名称"
prop=
"name"
>
<el-input
v-model=
"formData.name"
placeholder=
"请输入策略名称"
clearable
/>
</el-form-item>
<el-form-item
label=
"备注"
>
<el-input
v-model=
"formData.remark"
type=
"textarea"
:rows=
"4"
placeholder=
"请输入策略备注信息"
maxlength=
"200"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<!-- 步骤2:字段设置 -->
<div
v-else
class=
"step-content"
>
<div
class=
"version-select"
>
<span>
根据发现版本设置脱敏:
</span>
<div
class=
"flex-container align-center"
>
<div>
选择发现版本
</div>
<el-select
class=
"version-select-content flex1"
v-model=
"selectedVersion"
placeholder=
"选择发现版本"
>
<el-option
label=
"无版本"
value=
""
/>
<el-option
v-for=
"version in versions"
:key=
"version"
:label=
"version"
:value=
"version"
/>
</el-select>
</div>
</div>
<div
class=
"field-selection"
>
<div
class=
"tree-container"
>
<el-tree
:data=
"treeData"
:props=
"treeProps"
@
node-click=
"handleNodeClick"
highlight-current
/>
</div>
<el-divider
direction=
"vertical"
/>
<div
class=
"table-container"
>
<el-table
:data=
"tableFields"
border
style=
"width: 100%"
height=
"400px"
>
<el-table-column
prop=
"isPk"
label=
"主键"
width=
"80"
align=
"center"
fixed
>
<template
#
default=
"
{ row }">
<span
v-if=
"row.isPk"
>
PK
</span>
</
template
>
</el-table-column>
<el-table-column
prop=
"name"
label=
"字段名"
min-widthwidth=
"150"
/>
<el-table-column
prop=
"comment"
label=
"注释"
min-width=
"150"
/>
<el-table-column
prop=
"dataType"
label=
"数据域"
min-width=
"120"
/>
<el-table-column
prop=
"algorithm"
label=
"脱敏算法"
min-width=
"180"
/>
<el-table-column
label=
"操作"
width=
"80"
align=
"center"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
type=
"text"
size=
"small"
@
click=
"handleSet(row)"
>
设置
</el-button
>
</
template
>
</el-table-column>
</el-table>
</div>
</div>
</div>
<
template
#
footer
>
<div
class=
"dialog-footer"
>
<el-button
@
click=
"handleCancel"
>
取消
</el-button>
<el-button
v-if=
"currentStep === 1"
type=
"primary"
@
click=
"handleNextStep"
>
下一步
</el-button
>
<el-button
v-else
type=
"primary"
@
click=
"handlePrevStep"
>
上一步
</el-button
>
<el-button
v-if=
"currentStep === 2"
type=
"primary"
@
click=
"handleConfirm"
>
确定
</el-button
>
</div>
</
template
>
</el-dialog>
<!-- 脱敏规则设置弹窗 -->
<DesensitizationRuleDialog
v-model=
"desensitizationDialogVisible"
:current-field=
"currentField"
@
confirm=
"handleRuleConfirm"
/>
</template>
<
script
setup
>
import
DesensitizationRuleDialog
from
'./DesensitizationRuleDialog.vue'
;
import
{
ref
,
reactive
}
from
'vue'
;
import
{
ElMessage
}
from
'element-plus'
;
const
dialogVisible
=
ref
(
false
);
const
currentStep
=
ref
(
1
);
const
desensitizationDialogVisible
=
ref
(
false
);
const
currentField
=
ref
({});
// 表单数据
const
formData
=
reactive
({
name
:
''
,
remark
:
''
,
});
// 表单验证规则
const
rules
=
reactive
({
name
:
[
{
required
:
true
,
message
:
'请填写策略名称'
,
trigger
:
'blur'
},
],
});
// 步骤2数据
const
selectedVersion
=
ref
(
''
);
const
versions
=
ref
([
'v1.0'
,
'v2.0'
]);
const
treeData
=
ref
([
{
id
:
'system1'
,
label
:
'若依测试系统1'
,
children
:
[
{
id
:
'ry'
,
label
:
'ry'
,
children
:
[
{
id
:
'tables'
,
label
:
'表'
,
children
:
[
{
id
:
'gen_table'
,
label
:
'gen_table'
,
type
:
'table'
},
{
id
:
'gen_table_column'
,
label
:
'gen_table_column'
,
type
:
'table'
},
{
id
:
'sys_config'
,
label
:
'sys_config'
,
type
:
'table'
},
{
id
:
'sys_dept'
,
label
:
'sys_dept'
,
type
:
'table'
},
{
id
:
'sys_dict_data'
,
label
:
'sys_dict_data'
,
type
:
'table'
},
{
id
:
'sys_dict_type'
,
label
:
'sys_dict_type'
,
type
:
'table'
},
{
id
:
'sys_job'
,
label
:
'sys_job'
,
type
:
'table'
},
{
id
:
'sys_job_log'
,
label
:
'sys_job_log'
,
type
:
'table'
},
{
id
:
'sys_logininfor'
,
label
:
'sys_logininfor'
,
type
:
'table'
},
{
id
:
'sys_menu'
,
label
:
'sys_menu'
,
type
:
'table'
},
{
id
:
'sys_notice'
,
label
:
'sys_notice'
,
type
:
'table'
},
{
id
:
'sys_oper_log'
,
label
:
'sys_oper_log'
,
type
:
'table'
}
]
}
]
}
]
}
]);
const
treeProps
=
{
children
:
'children'
,
label
:
'label'
,
};
const
tableFields
=
ref
([]);
const
algorithms
=
ref
([
{
value
:
'mask'
,
label
:
'掩码处理'
},
{
value
:
'encrypt'
,
label
:
'加密处理'
},
{
value
:
'hash'
,
label
:
'哈希处理'
},
]);
// 打开弹窗
const
openDialog
=
()
=>
{
dialogVisible
.
value
=
true
;
currentStep
.
value
=
1
;
resetForm
();
};
// 重置表单
const
resetForm
=
()
=>
{
formData
.
name
=
''
;
formData
.
remark
=
''
;
selectedVersion
.
value
=
''
;
tableFields
.
value
=
[];
};
// 关闭弹窗
const
handleClose
=
()
=>
{
dialogVisible
.
value
=
false
;
};
// 取消
const
handleCancel
=
()
=>
{
dialogVisible
.
value
=
false
;
};
// 下一步
const
handleNextStep
=
()
=>
{
if
(
!
formData
.
name
)
{
ElMessage
.
error
(
'请填写策略名称'
);
return
;
}
currentStep
.
value
=
2
;
};
// 上一步
const
handlePrevStep
=
()
=>
{
currentStep
.
value
=
1
;
};
// 确定
const
handleConfirm
=
()
=>
{
// 这里调用API提交数据
console
.
log
(
'提交数据:'
,
{
...
formData
,
version
:
selectedVersion
.
value
,
fields
:
tableFields
.
value
,
});
dialogVisible
.
value
=
false
;
ElMessage
.
success
(
'策略添加成功'
);
};
// 树节点点击
const
handleNodeClick
=
(
node
)
=>
{
if
(
node
.
type
===
'table'
)
{
// 模拟根据表名获取字段
fetchTableFields
(
node
.
label
);
}
};
// 获取表字段
const
fetchTableFields
=
(
tableName
)
=>
{
// 这里应该是API调用,根据表名获取字段
// 模拟数据
if
(
tableName
===
'gen_table_column'
)
{
tableFields
.
value
=
[
{
isPk
:
true
,
name
:
'column_id'
,
comment
:
'编号'
,
dataType
:
'bigint'
,
algorithm
:
''
,
},
{
isPk
:
false
,
name
:
'table_id'
,
comment
:
'归属表编号'
,
dataType
:
'bigint'
,
algorithm
:
''
,
},
// 其他字段...
];
}
else
{
tableFields
.
value
=
[];
}
};
// 设置字段
const
handleSet
=
(
row
)
=>
{
currentField
.
value
=
row
;
desensitizationDialogVisible
.
value
=
true
;
};
// 处理规则确认
const
handleRuleConfirm
=
(
ruleData
)
=>
{
// 更新表格中的算法字段
const
fieldIndex
=
tableFields
.
value
.
findIndex
(
field
=>
field
.
name
===
ruleData
.
fieldName
);
if
(
fieldIndex
!==
-
1
)
{
tableFields
.
value
[
fieldIndex
].
algorithm
=
ruleData
.
algorithm
;
ElMessage
.
success
(
`字段
${
ruleData
.
fieldName
}
的脱敏规则已更新`
);
}
};
defineExpose
({
openDialog
,
});
</
script
>
<
style
scoped
lang=
"scss"
>
.step-content
{
min-height
:
400px
;
}
.version-select
{
margin-bottom
:
20px
;
}
.field-selection
{
display
:
flex
;
height
:
400px
;
}
.tree-container
{
width
:
250px
;
height
:
400px
;
overflow-y
:
auto
;
padding-right
:
10px
;
}
.table-container
{
flex
:
1
;
height
:
100%
;
overflow-y
:
auto
;
}
.dialog-footer
{
display
:
flex
;
justify-content
:
flex-end
;
}
.el-divider
{
height
:
100%
;
margin
:
0
10px
;
}
.flex-container
{
display
:
flex
;
}
.align-center
{
align-items
:
center
;
}
.justify-between
{
justify-content
:
space-between
;
}
.flex1
{
flex
:
1
;
}
.version-select-content
{
margin-left
:
15px
;
}
</
style
>
\ No newline at end of file
src/views/desensitizationStrategy/DesensitizationStrategy/modules/DesensitizationRuleDialog.vue
View file @
5897447f
...
@@ -3,391 +3,341 @@
...
@@ -3,391 +3,341 @@
v-model=
"dialogVisible"
v-model=
"dialogVisible"
title=
"设置脱敏规则"
title=
"设置脱敏规则"
width=
"800px"
width=
"800px"
destroy-on-close
:before-close=
"handleClose"
:before-close=
"handleClose"
class=
"desensitization-rule-dialog"
>
>
<div
class=
"desensitization-dialog"
>
<div
class=
"rule-dialog-content"
>
<div
class=
"dialog-content"
>
<div
class=
"selection-container"
>
<!-- 左侧:选择数据域(按照图片样式重写) -->
<!-- 左侧:选择数据域 -->
<div
class=
"data-domain-section"
>
<div
class=
"selection-section left-section"
>
<!--
<el-card></el-card>
-->
<div
class=
"section-header"
>
<div
class=
"selector-header"
>
<div
class=
"section-title"
>
选择数据域
</div>
<span
class=
"selector-title"
>
选择数据域
</span>
</div>
</div>
<div
class=
"section-content"
>
<div
class=
"options-list"
>
<div
<div
v-for=
"(item,index) in dataAreaList"
v-for=
"domain in dataDomains"
:key=
"item.id"
:key=
"domain.value"
class=
"selection-item"
class=
"option-item"
:class=
"
{ selected: selectedDataArea
&&
(selectedDataArea.id === item.id) }"
:class=
"
{ 'option-selected': selectedDomain === domain.value }"
@click="handleSelectDataArea(item)"
@click="selectDomain(domain.value)"
>
>
<!-- 自定义单选按钮 -->
<div
class=
"selection-indicator"
>
<div
class=
"custom-radio"
>
<div
class=
"indicator-dot"
v-if=
"selectedDataArea && (selectedDataArea.id === item.id)"
></div>
<div
class=
"radio-outer"
>
<div
class=
"radio-inner"
:class=
"
{ selected: selectedDomain === domain.value }">
</div>
</div>
</div>
</div>
<div
class=
"selection-text"
>
{{
item
.
dataarea
}}
</div>
<!-- 选项标签 -->
<span
class=
"option-label"
>
{{
domain
.
label
}}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!--
<el-divider
direction=
"vertical"
/>
-->
<!-- 右侧:选择字段脱敏规则 -->
<!-- 右侧:选择字段脱敏规则 -->
<div
class=
"algorithm-section"
>
<div
class=
"selection-section right-section"
>
<h4>
选择字段脱敏规则
</h4>
<div
class=
"section-header"
>
<div
class=
"algorithm-list"
>
<div
class=
"section-title"
>
选择字段脱敏规则
</div>
<el-radio-group
v-model=
"selectedAlgorithm"
>
</div>
<el-radio
<div
class=
"section-content"
v-if=
"selectedDataArea && selectedDataArea.ruleList.length > 0"
>
v-for=
"algorithm in currentAlgorithms"
<div
:key=
"algorithm.value"
v-for=
"(item,index) in selectedDataArea.ruleList"
:label=
"algorithm.value"
:key=
"item.id"
class=
"algorithm-item"
class=
"selection-item"
>
:class=
"
{ selected:selectedRule
&&
(selectedRule.id === item.id) }"
{{
algorithm
.
label
}}
@click="selectRule(item)"
</el-radio>
>
</el-radio-group>
<div
class=
"selection-indicator"
>
<div
class=
"indicator-dot"
v-if=
"selectedRule && (selectedRule.id === item.id)"
></div>
</div>
<div
class=
"selection-text"
>
{{
item
.
rulename
}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<el-divider
style=
"height: 1px;"
/>
<!-- 底部按钮 -->
<div
class=
"dialog-footer"
>
<el-button
@
click=
"resetRules"
>
重置规则
</el-button>
<el-button
type=
"primary"
@
click=
"confirmRules"
>
确定规则
</el-button>
</div>
</div>
</div>
<template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"handleReset"
:disabled=
"!selectedRule"
>
重置规则
</el-button>
<el-button
type=
"primary"
@
click=
"handleConfirm"
>
确定规则
</el-button>
</span>
</
template
>
</el-dialog>
</el-dialog>
</template>
</template>
<
script
setup
>
<
script
setup
>
import
{
ref
,
computed
,
watch
,
onMounted
}
from
'vue'
import
{
ref
,
reactive
,
watch
,
onMounted
}
from
'vue'
import
{
ElMessage
}
from
'element-plus'
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
{
const
props
=
defineProps
({
desensitizationruleQuery
,
modelValue
:
{
// queryDesensitizationRules
type
:
Boolean
,
}
from
'@/api/desensitizationStrategy'
default
:
false
},
currentField
:
{
type
:
Object
,
default
:
()
=>
({})
}
})
const
emit
=
defineEmits
([
'
update:modelValue'
,
'
confirm'
])
const
emit
=
defineEmits
([
'confirm'
])
// 弹窗显示控制
// 控制弹窗显示
const
dialogVisible
=
computed
({
const
dialogVisible
=
ref
(
false
)
get
:
()
=>
props
.
modelValue
,
set
:
(
value
)
=>
emit
(
'update:modelValue'
,
value
)
// 当前字段数据
})
const
currentFieldData
=
ref
({})
// 数据域列表(根据图片中的选项)
const
dataAreaList
=
ref
([
// 数据域选项(完全按照图片顺序)
const
dataDomains
=
ref
([
{
value
:
'wang'
,
label
:
'小王'
},
{
value
:
'test'
,
label
:
'测试数据域'
},
{
value
:
'general'
,
label
:
'通用规则'
},
{
value
:
'decrypt'
,
label
:
'解密数据'
},
{
value
:
'mixed_id'
,
label
:
'混合证件号'
},
{
value
:
'black_white'
,
label
:
'黑白名单'
},
{
value
:
'english_address'
,
label
:
'英文地址'
},
{
value
:
'name'
,
label
:
'姓名'
},
{
value
:
'phone'
,
label
:
'手机号码'
},
{
value
:
'age'
,
label
:
'年龄'
},
{
value
:
'business_license'
,
label
:
'营业执照'
},
{
value
:
'mixed_phone'
,
label
:
'混合电话号码'
}
])
])
// 脱敏算法选项
// // 脱敏规则列表(根据图片中的选项)
const
algorithmOptions
=
{
// const desensitizationRules = ref([
// 通用规则
// { id: '1', name: '测试算法' },
general
:
[
// { id: '2', name: '测试算法1' }
{
value
:
'general_mask'
,
label
:
'通用掩码'
},
// ])
{
value
:
'general_encrypt'
,
label
:
'通用加密'
},
{
value
:
'general_hash'
,
label
:
'通用哈希'
}
// 选中的数据域
],
const
selectedDataArea
=
ref
(
null
)
// 默认选中第一个
// 姓名
name
:
[
{
value
:
'name_mask'
,
label
:
'姓名掩码'
},
{
value
:
'name_random'
,
label
:
'姓名随机替换'
},
{
value
:
'name_encrypt'
,
label
:
'姓名加密'
}
],
// 手机号码
phone
:
[
{
value
:
'phone_mask'
,
label
:
'手机号掩码'
},
{
value
:
'phone_encrypt'
,
label
:
'手机号加密'
},
{
value
:
'phone_hash'
,
label
:
'手机号哈希'
}
],
// 其他数据域的算法...
wang
:
[
{
value
:
'wang_mask'
,
label
:
'小王掩码'
},
{
value
:
'wang_encrypt'
,
label
:
'小王加密'
}
],
test
:
[
{
value
:
'test_mask'
,
label
:
'测试掩码'
},
{
value
:
'test_encrypt'
,
label
:
'测试加密'
}
],
// 默认算法
default
:
[
{
value
:
'mask'
,
label
:
'掩码处理'
},
{
value
:
'encrypt'
,
label
:
'加密处理'
},
{
value
:
'hash'
,
label
:
'哈希处理'
}
]
}
// 选中的数据域和算法
// 选中的脱敏规则
const
selectedDomain
=
ref
(
'general'
)
const
selectedRule
=
ref
(
null
)
// 默认选中第一个
const
selectedAlgorithm
=
ref
(
''
)
// 组件挂载时加载数据
onMounted
(()
=>
{
// 计算当前可用的脱敏算法
const
currentAlgorithms
=
computed
(()
=>
{
return
algorithmOptions
[
selectedDomain
.
value
]
||
algorithmOptions
.
default
})
})
// 选择数据域
const
loadDataAreaList
=
()
=>
{
const
selectDomain
=
(
value
)
=>
{
desensitizationruleQuery
().
then
(
res
=>
{
selectedDomain
.
value
=
value
dataAreaList
.
value
=
res
.
data
const
algorithms
=
currentAlgorithms
.
value
})
if
(
algorithms
.
length
>
0
)
{
selectedAlgorithm
.
value
=
algorithms
[
0
].
value
}
}
}
// 重置选择函数
const
resetSelection
=
()
=>
{
selectedDomain
.
value
=
'general'
const
algorithms
=
currentAlgorithms
.
value
if
(
algorithms
.
length
>
0
)
{
selectedAlgorithm
.
value
=
algorithms
[
0
].
value
}
}
// 根据算法推断数据域
// 选择数据域
const
inferDomainFromAlgorithm
=
(
algorithm
)
=>
{
const
handleSelectDataArea
=
(
val
)
=>
{
for
(
const
[
domain
,
algorithms
]
of
Object
.
entries
(
algorithmOptions
))
{
selectedDataArea
.
value
=
val
if
(
algorithms
.
some
(
algo
=>
algo
.
value
===
algorithm
))
{
selectedDomain
.
value
=
domain
return
}
}
selectedDomain
.
value
=
'general'
}
}
// 监听当前字段变化,恢复已保存的规则
// 选择脱敏规则
watch
(()
=>
props
.
currentField
,
(
newField
)
=>
{
const
selectRule
=
(
val
)
=>
{
if
(
newField
&&
newField
.
algorithm
)
{
console
.
log
(
val
)
selectedAlgorithm
.
value
=
newField
.
algorithm
selectedRule
.
value
=
val
inferDomainFromAlgorithm
(
newField
.
algorithm
)
console
.
log
(
'selectedRule.value'
,
selectedRule
.
value
)
}
else
{
}
resetSelection
()
}
},
{
immediate
:
true
})
// 组件挂载时初始化
// 打开弹窗
onMounted
(()
=>
{
const
open
=
(
fieldData
=
{})
=>
{
resetSelection
()
console
.
log
(
'open'
,
fieldData
)
})
loadDataAreaList
()
currentFieldData
.
value
=
fieldData
console
.
log
(
'currentFieldData.value'
,
currentFieldData
.
value
)
initData
(
fieldData
)
dialogVisible
.
value
=
true
}
// 关闭弹窗
// 关闭弹窗
const
handleC
lose
=
()
=>
{
const
c
lose
=
()
=>
{
dialogVisible
.
value
=
false
dialogVisible
.
value
=
false
resetForm
()
}
// 初始化数据
const
initData
=
(
fieldData
)
=>
{
// 如果有传入的字段数据,可以预设选择
if
(
fieldData
.
dataarea_id
)
{
// selectedDataArea.value = fieldData.dataarea_id
}
if
(
fieldData
.
rule_id
)
{
// selectedRule.value = fieldData.rule_id
}
// 如果没有传入数据,重置表单
// if (!fieldData || Object.keys(fieldData).length === 0) {
// resetForm()
// }
}
}
// 重置规则
// 重置规则
const
resetRules
=
()
=>
{
const
handleReset
=
()
=>
{
resetSelection
()
selectedRule
.
value
=
''
ElMessage
.
info
(
'规则已重置'
)
ElMessage
.
info
(
'规则已重置'
)
}
}
// 确定规则
// 确认规则
const
confirmRules
=
()
=>
{
const
handleConfirm
=
()
=>
{
if
(
!
selectedAlgorithm
.
value
)
{
if
(
!
selectedDataArea
.
value
)
{
ElMessage
.
warning
(
'请选择数据域'
)
return
}
if
(
!
selectedRule
.
value
)
{
ElMessage
.
warning
(
'请选择脱敏规则'
)
ElMessage
.
warning
(
'请选择脱敏规则'
)
return
return
}
}
console
.
log
(
'selectedRule'
,
selectedRule
.
vaue
)
emit
(
'confirm'
,
{
emit
(
'confirm'
,
{
algorithm
:
selectedAlgorithm
.
value
,
selectedDataArea
:
selectedDataArea
.
value
,
domain
:
selectedDomain
.
value
,
selectedRule
:
selectedRule
.
value
,
field
Name
:
props
.
currentField
.
nam
e
field
Data
:
currentFieldData
.
valu
e
})
})
close
()
ElMessage
.
success
(
'脱敏规则设置成功'
)
ElMessage
.
success
(
'脱敏规则设置成功'
)
dialogVisible
.
value
=
false
}
}
</
script
>
<
style
scoped
lang=
"scss"
>
// 关闭弹窗
.desensitization-dialog
{
const
handleClose
=
(
done
)
=>
{
.dialog-content
{
// 如果有未保存的更改,可以提示用户
display
:
flex
;
if
(
selectedDataArea
.
value
||
selectedRule
.
value
)
{
height
:
400px
;
ElMessageBox
.
confirm
(
'确定要关闭吗?未保存的更改将会丢失'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
resetForm
()
done
()
}).
catch
(()
=>
{
// 取消关闭
})
}
else
{
resetForm
()
done
()
}
}
}
.data-domain-section
{
// 重置表单
flex
:
1
;
const
resetForm
=
()
=>
{
display
:
flex
;
dataAreaList
.
value
=
[]
flex-direction
:
column
;
selectedDataArea
.
value
=
null
padding-right
:
20px
;
selectedRule
.
value
=
null
}
currentFieldData
.
value
=
{}
}
.algorithm-section
{
// 暴露方法给父组件
flex
:
1
;
defineExpose
({
display
:
flex
;
open
,
flex-direction
:
column
;
close
padding-left
:
20px
;
})
}
</
script
>
.selector-header
{
<
style
scoped
>
padding
:
0
0
16px
0
;
.desensitization-rule-dialog
{
margin-bottom
:
8px
;
:deep(.el-dialog__body)
{
//
border-bottom
:
1px
dashed
#dcdfe6
;
padding
:
20px
;
.selector-title
{
font-size
:
16px
;
font-weight
:
900
;
color
:
#000000
;
line-height
:
1.5
;
display
:
block
;
}
}
}
}
.options-list
{
.rule-dialog-content
{
border
:
thin
solid
#dcdfe6
;
padding
:
10px
;
border-radius
:
5px
;
}
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0.04
);
padding
:
10px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
0
;
flex
:
1
;
overflow-y
:
auto
;
}
.option-item
{
.selection-container
{
display
:
flex
;
display
:
flex
;
align-items
:
center
;
gap
:
20px
;
padding
:
12px
0
;
height
:
400px
;
cursor
:
pointer
;
}
transition
:
background-color
0.2s
ease
;
border-bottom
:
1px
dashed
#e0e0e0
;
&:last-child
{
border-bottom
:
none
;
}
&
:hover
{
background-color
:
#f8f9fa
;
}
&
.option-selected
{
background-color
:
#f0f7ff
;
.option-label
{
color
:
#1890ff
;
font-weight
:
500
;
}
.radio-outer
{
background-color
:
#1890ff
;
}
}
}
.custom-radio
{
.selection-section
{
display
:
flex
;
flex
:
1
;
align-items
:
center
;
border
:
1px
solid
#e4e7ed
;
justify-content
:
center
;
border-radius
:
4px
;
margin-right
:
12px
;
background-color
:
#f8f9fa
;
width
:
20px
;
display
:
flex
;
height
:
20px
;
flex-direction
:
column
;
flex-shrink
:
0
;
}
}
.radio-outer
{
.section-header
{
width
:
16px
;
padding
:
15px
;
height
:
16px
;
border-bottom
:
1px
solid
#e4e7ed
;
border
:
2px
solid
#c0c4cc
;
background-color
:
#f5f5f5
;
border-radius
:
50%
;
}
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
transition
:
all
0.2s
ease
;
.option-selected
&
{
border-color
:
#1890ff
;
background-color
:
#1890ff
;
}
}
.radio-inner
{
.section-title
{
width
:
6px
;
font-weight
:
bold
;
height
:
6px
;
color
:
#303133
;
border-radius
:
50%
;
font-size
:
14px
;
background-color
:
transparent
;
text-align
:
center
;
transition
:
background-color
0.2s
ease
;
}
&.selected
{
background-color
:
#ffffff
;
}
}
.option-label
{
.section-content
{
font-size
:
14px
;
flex
:
1
;
color
:
#606266
;
padding
:
10px
;
line-height
:
1.5
;
overflow-y
:
auto
;
flex
:
1
;
}
user-select
:
none
;
.option-selected
&
{
color
:
#1890ff
;
}
}
h4
{
.selection-item
{
margin
:
0
0
16px
0
;
display
:
flex
;
font-size
:
14px
;
align-items
:
center
;
font-weight
:
600
;
padding
:
12px
15px
;
color
:
#606266
;
border-bottom
:
1px
solid
#f0f0f0
;
}
cursor
:
pointer
;
transition
:
all
0.2s
ease
;
border-radius
:
4px
;
margin-bottom
:
4px
;
}
.algorithm-list
{
.selection-item
:hover
{
flex
:
1
;
background-color
:
#f5f7fa
;
overflow-y
:
auto
;
}
}
.algorithm-item
{
.selection-item.selected
{
display
:
block
;
background-color
:
#ecf5ff
;
margin
:
8px
0
;
border-color
:
#409EFF
;
padding
:
8px
12px
;
}
border-radius
:
4px
;
transition
:
background-color
0.3s
;
&:hover
{
.selection-item
:last-child
{
background-color
:
#f5f7fa
;
border-bottom
:
none
;
}
margin-bottom
:
0
;
}
:deep
(
.el-radio__label
)
{
.selection-indicator
{
font-size
:
13px
;
width
:
16px
;
}
height
:
16px
;
}
border
:
2px
solid
#dcdfe6
;
border-radius
:
50%
;
margin-right
:
12px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
transition
:
all
0.2s
ease
;
}
.el-divider
{
.selection-item.selected
.selection-indicator
{
//
height
:
360px
;
border-color
:
#409EFF
;
//
margin
:
0
20px
;
}
}
.dialog-footer
{
.indicator-dot
{
display
:
flex
;
width
:
8px
;
justify-content
:
flex-end
;
height
:
8px
;
margin-top
:
20px
;
background-color
:
#409EFF
;
gap
:
12px
;
border-radius
:
50%
;
}
}
.selection-text
{
font-size
:
13px
;
color
:
#606266
;
flex
:
1
;
}
.selection-item.selected
.selection-text
{
color
:
#409EFF
;
font-weight
:
500
;
}
.dialog-footer
{
display
:
flex
;
justify-content
:
flex-end
;
padding
:
10px
0
;
}
.left-section
{
border-right
:
2px
solid
#e4e7ed
;
}
.right-section
{
border-left
:
2px
solid
#e4e7ed
;
}
}
</
style
>
</
style
>
\ No newline at end of file
src/views/desensitizationStrategy/DesensitizationStrategy/modules/StrategyAddDialog.vue
0 → 100644
View file @
5897447f
<
template
>
<el-dialog
v-model=
"dialogVisible"
:title=
"title"
width=
"80%"
:before-close=
"handleClose"
class=
"strategy-add-dialog"
>
<!-- 步骤指示器 -->
<div
class=
"steps-container"
>
<el-steps
:active=
"activeStep"
align-center
>
<el-step
title=
"基本信息"
/>
<el-step
title=
"规则配置"
/>
</el-steps>
</div>
<!-- 第一步:基本信息 -->
<div
v-if=
"activeStep === 1"
class=
"step-content"
>
<el-form
ref=
"step1FormRef"
:model=
"formData"
:rules=
"formRules"
label-width=
"100px"
label-position=
"top"
>
<el-form-item
label=
"策略名称"
prop=
"strategyName"
required
>
<el-input
v-model=
"formData.strategyName"
placeholder=
"请输入策略名称"
maxlength=
"50"
show-word-limit
/>
</el-form-item>
<el-form-item
label=
"备注"
prop=
"remarks"
>
<el-input
v-model=
"formData.remarks"
type=
"textarea"
:rows=
"4"
placeholder=
"请输入策略备注信息"
maxlength=
"200"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<!-- 第二步:规则配置 -->
<div
v-if=
"activeStep === 2"
class=
"step-content"
>
<div
class=
"version-select"
>
<span
class=
"label"
>
根据发现版本设置脱敏:
</span>
<span
class=
"label"
style=
"margin-left: 60px;"
>
选择发现版本:
</span>
<el-tree-select
v-model=
"selectedVersion"
:data=
"editionList"
:props=
"treeSelectProps"
:load=
"loadCascader"
lazy
placeholder=
"选择发现版本"
clearable
style=
"width: 200px; margin-left: 10px;"
/>
</div>
<div
class=
"config-container"
>
<!-- 左侧树形结构 -->
<div
class=
"tree-panel"
>
<TreeFilter
:tree-data=
"treeData"
@
node-click=
"handleNodeClick"
/>
</div>
<!-- 右侧表格 -->
<div
class=
"table-panel"
>
<el-table
:data=
"tableData"
border
style=
"width: 100%"
height=
"100%"
v-loading=
"tableLoading"
>
<el-table-column
prop=
"id"
label=
"主键"
width=
"80"
align=
"center"
/>
<el-table-column
prop=
"fieldName"
label=
"字段名"
min-width=
"120"
/>
<el-table-column
prop=
"comment"
label=
"注释"
min-width=
"150"
show-overflow-tooltip
/>
<el-table-column
prop=
"dataareaname"
label=
"数据域"
min-width=
"120"
/>
<el-table-column
prop=
"rulename"
label=
"脱敏算法"
min-width=
"150"
/>
<el-table-column
label=
"操作"
width=
"120"
align=
"center"
fixed=
"right"
>
<template
#
default=
"
{ row }">
<el-button
link
type=
"primary"
size=
"small"
@
click=
"editField(row)"
>
设置
</el-button>
</
template
>
</el-table-column>
</el-table>
<div
v-if=
"tableData.length === 0 && !tableLoading"
class=
"empty-table"
>
<el-empty
description=
"暂无数据"
/>
</div>
</div>
</div>
</div>
<
template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"handleCancel"
>
取消
</el-button>
<el-button
v-if=
"activeStep === 2"
@
click=
"activeStep = 1"
>
上一步
</el-button>
<el-button
type=
"primary"
@
click=
"handleNextStep"
>
{{
activeStep
===
1
?
'下一步'
:
'确定'
}}
</el-button>
</span>
</
template
>
</el-dialog>
<DesensitizationRuleDialog
ref=
"desensitizationRuleDialogRef"
@
confirm=
"handleRuleConfirm"
/>
</template>
<
script
setup
>
import
{
ref
,
reactive
,
watch
,
computed
,
nextTick
}
from
'vue'
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
import
DesensitizationRuleDialog
from
'./DesensitizationRuleDialog.vue'
import
{
desensitizationruleQuery
,
queryversion
,
queryShemas
,
queryOriginalList
,
queryTask
,
queryProVersion
,
queryTaskVersion
,
desensitizationStrategyDetails
,
rowsensitivelevel
,
queryDesensitizationTables
}
from
'@/api/desensitizationStrategy'
import
{
queryTables
,
query
}
from
'@/api/classification/classification.js'
import
TreeFilter
from
'./TreeFilter.vue'
const
props
=
defineProps
({
title
:
{
type
:
String
,
default
:
'添加策略'
},
strategyId
:
{
type
:
String
,
default
:
''
}
})
const
emit
=
defineEmits
([
'refresh'
,
'update:visible'
])
const
dialogVisible
=
ref
(
false
)
const
activeStep
=
ref
(
1
)
const
selectedVersion
=
ref
(
''
)
const
tableLoading
=
ref
(
false
)
// 表单数据
const
formData
=
reactive
({
strategyName
:
''
,
remarks
:
''
})
const
step1FormRef
=
ref
()
// 表单验证规则
const
formRules
=
{
strategyName
:
[
{
required
:
true
,
message
:
'请填写策略名称'
,
trigger
:
'blur'
}
]
}
const
treeSelectProps
=
{
// value: 'value',
// label: 'text',
// children: 'children',
// isLeaf: 'leaf'
}
// 版本选项
const
editionList
=
ref
([
{
value
:
'none'
,
label
:
'无版本'
},
{
value
:
'v1'
,
label
:
'版本1.0'
},
{
value
:
'v2'
,
label
:
'版本2.0'
}
])
/**
* 发现版本懒加载
* @param node
* @param resolve
*/
const
loadCascader
=
async
(
node
,
resolve
)
=>
{
try
{
const
{
value
,
level
}
=
node
if
(
level
===
0
)
{
// 第一级节点已经加载完成,直接返回
resolve
(
editionList
.
value
)
return
}
let
children
=
[]
if
(
value
===
'TaskVersion'
)
{
// 加载任务列表
const
res
=
await
queryTask
({
projectid
:
sessionStorage
.
getItem
(
'projectId'
)
})
children
=
res
.
data
.
map
(
item
=>
({
label
:
item
.
text
,
value
:
item
.
value
,
loading
:
false
,
children
:
[],
leaf
:
false
}))
}
else
if
(
value
===
'ProjectVersion'
)
{
// 加载项目版本
const
res
=
await
queryProVersion
({
projectid
:
sessionStorage
.
getItem
(
'projectId'
),
versiontype
:
'project'
})
if
(
res
.
data
.
versionname
&&
res
.
data
.
filename
)
{
children
=
[{
label
:
res
.
data
.
versionname
,
value
:
res
.
data
.
filename
,
leaf
:
true
}]
}
}
else
{
// 加载任务版本
const
res
=
await
queryTaskVersion
({
taskid
:
value
})
children
=
res
.
data
.
map
(
item
=>
({
label
:
item
.
versionname
,
value
:
item
.
filename
,
leaf
:
true
}))
}
resolve
(
children
)
}
catch
(
error
)
{
console
.
error
(
'加载版本数据失败:'
,
error
)
ElMessage
.
error
(
'加载版本数据失败'
)
resolve
([])
}
}
// 树形数据
const
treeData
=
ref
([
// {
// id: '1',
// label: '若依配测2',
// type: 'project',
// children: [
// {
// id: '1-1',
// label: 'ry',
// children: [
// { id: '1-1-1', label: 'gen_table', type: 'table' },
// { id: '1-1-2', label: 'gen_table_column', type: 'table' },
// { id: '1-1-3', label: 'sys_config', type: 'table' }
// ]
// }
// ]
// },
// {
// id: '2',
// label: '若依测试库2',
// type: 'project',
// children: [
// {
// id: '2-1',
// label: 'ry_test',
// type: 'database',
// tableCount: 15,
// children: [
// { id: '2-1-1', label: 'sys_dept', type: 'table' },
// { id: '2-1-2', label: 'sys_dict_data', type: 'table' },
// { id: '2-1-3', label: 'sys_dict_type', type: 'table' }
// ]
// }
// ]
// }
])
// 当前选中的节点数据
const
currentNodeData
=
ref
(
null
)
const
currentNodeLevel
=
computed
(()
=>
{
if
(
!
currentNodeData
.
value
)
return
1
if
(
currentNodeData
.
value
.
type
===
'system'
)
return
1
if
(
currentNodeData
.
value
.
type
===
'database'
)
return
2
if
(
currentNodeData
.
value
.
type
===
'category'
)
return
3
if
(
currentNodeData
.
value
.
type
===
'table'
)
return
4
return
0
})
const
datasystem_id
=
ref
(
''
)
const
schema
=
ref
(
''
)
// 监听 currentNodeLevel 和 currentNodeData,一级节点时自动查详情
watch
([
currentNodeLevel
,
currentNodeData
],
async
([
level
,
node
])
=>
{
// if (level === 1 && node && tab === 'basic') {
// const res = await queryDatasystemInfo({ dataSystemId: node.tid, projectId: projectId.value })
// if (res && res.data) {
// // 你可以将结果赋值到 basicInfoData 或其他变量
// basicInfoData.value = { ...basicInfoData.value, ...res.data }
// }
// }
if
(
level
===
1
&&
node
)
{
datasystem_id
.
value
=
node
.
tid
}
if
(
level
===
2
&&
node
)
{
console
.
log
(
'第一层'
,
node
)
schema
.
value
=
node
.
value
}
// 查询表基本信息
if
(
level
===
4
&&
node
)
{
console
.
log
(
node
)
queryOriginalList
({
table_name
:
node
.
showName
,
projectId
:
sessionStorage
.
getItem
(
'projectId'
),
datasystem_id
:
datasystem_id
.
value
,
schema
:
schema
.
value
}).
then
(
res
=>
{
if
(
res
.
flag
){
tableData
.
value
=
res
.
data
.
map
(
item
=>
(({
...
item
,
id
:
item
.
primarykey
,
fieldName
:
item
.
columnname
,
comment
:
item
.
remarks
,
dataareaname
:
''
,
rulename
:
''
// dataDomain: item.
})))
}
else
{
ElMessage
.
error
(
res
.
msg
)
}
}).
catch
(
err
=>
{
ElMessage
.
error
(
err
.
msg
)
})
}
})
const
selectedDataArea
=
ref
(
null
)
const
selectedRule
=
ref
(
null
)
const
handleRuleConfirm
=
(
val
)
=>
{
console
.
log
(
'fieldData'
,
val
.
fieldData
)
console
.
log
(
'selectedDataArea'
,
val
.
selectedDataArea
)
console
.
log
(
'selectedRule'
,
val
.
selectedRule
)
selectedDataArea
.
value
=
val
.
selectedDataArea
selectedRule
.
value
=
val
.
selectedRule
let
index
=
tableData
.
value
.
findIndex
(
item
=>
item
.
id
===
val
.
fieldData
.
id
)
if
(
index
!==
-
1
){
console
.
log
(
'index'
,
index
)
console
.
log
(
'给table重新赋值'
)
tableData
.
value
[
index
].
dataareaname
=
val
.
selectedDataArea
.
dataarea
tableData
.
value
[
index
].
rulename
=
val
.
selectedRule
.
rulename
}
}
// 生成唯一标识符
function
uuid
()
{
return
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
.
replace
(
/
[
xy
]
/g
,
function
(
c
)
{
var
r
=
Math
.
random
()
*
16
|
0
,
v
=
c
===
'x'
?
r
:
(
r
&
0x3
|
0x8
);
return
v
.
toString
(
16
);
});
}
// 树节点点击处理
const
handleNodeClick
=
async
(
data
)
=>
{
currentNodeData
.
value
=
data
// 2. 点击一层系统数据,查 schemas
if
(
data
.
type
===
'system'
)
{
const
res
=
await
queryShemas
({
dataSystemId
:
data
.
tid
,
dataType
:
data
.
dbtype
,
projectid
:
sessionStorage
.
getItem
(
'projectId'
)
})
if
(
res
&&
res
.
data
)
{
data
.
children
=
res
.
data
.
map
(
schema
=>
({
id
:
uuid
(),
...
schema
,
type
:
'database'
,
label
:
schema
.
text
,
parent
:
data
,
children
:
[
{
id
:
uuid
(),
label
:
'表'
,
type
:
'category'
,
parent
:
{
...
schema
,
type
:
'database'
,
label
:
schema
.
text
,
parent
:
data
,
tid
:
data
.
tid
,
dbservername
:
schema
.
text
},
children
:
[]
}
]
}))
treeData
.
value
=
[...
treeData
.
value
]
// 触发视图更新
}
}
// 3. 点击三层 category,查表
if
(
data
.
type
===
'category'
&&
data
.
parent
)
{
const
parentDb
=
data
.
parent
const
res
=
await
queryTables
({
dataSystemId
:
parentDb
.
tid
,
schema
:
parentDb
.
dbservername
})
if
(
res
&&
res
.
data
)
{
data
.
children
=
res
.
data
.
map
(
table
=>
({
...
table
,
id
:
uuid
(),
label
:
table
.
showName
,
type
:
'table'
,
parent
:
data
}))
treeData
.
value
=
[...
treeData
.
value
]
}
}
}
// 表格数据
const
tableData
=
ref
([])
const
infoTable
=
ref
([])
// 打开对话框
const
openDialog
=
async
(
mode
,
strategyData
=
null
)
=>
{
dialogVisible
.
value
=
true
activeStep
.
value
=
1
selectedVersion
.
value
=
''
desensitizationStrategyDetails
({
strategyId
:
props
.
strategyId
,
projectid
:
sessionStorage
.
getItem
(
'projectId'
)
}).
then
(
res
=>
{
if
(
res
.
flag
){
infoTable
.
value
=
res
.
data
}
})
queryversion
({
projectid
:
sessionStorage
.
getItem
(
'projectId'
)
}).
then
(
res
=>
{
// editionList.value = res.data
editionList
.
value
=
res
.
data
.
map
(
item
=>
{
let
res
=
{
label
:
item
.
text
,
value
:
item
.
value
,
children
:
[],
leaf
:
item
.
value
===
'null'
||
item
.
value
===
'AllVersion'
}
!
res
.
leaf
&&
(
res
.
loading
=
false
)
return
res
})
editionList
.
value
.
splice
(
1
,
2
)
console
.
log
(
editionList
.
value
)
})
// 重置表单数据
if
(
mode
===
'addtactics'
)
{
formData
.
strategyName
=
''
formData
.
remarks
=
''
}
else
if
(
strategyData
)
{
// 填充编辑数据
formData
.
strategyName
=
strategyData
.
strategyName
||
''
formData
.
remarks
=
strategyData
.
remarks
||
''
}
// 清空表格数据
tableData
.
value
=
[]
const
res
=
await
query
({
project_id
:
sessionStorage
.
getItem
(
'projectId'
)
})
if
(
res
&&
res
.
data
)
{
treeData
.
value
=
res
.
data
.
map
(
item
=>
({
...
item
,
label
:
item
.
sysname
,
type
:
'system'
,
children
:
[
]
}))
// 赋值触发监听,默认查询第一个数据源的基本信息
currentNodeData
.
value
=
treeData
.
value
[
0
]
}
}
// 关闭对话框
const
handleClose
=
(
done
)
=>
{
// 如果有未保存的数据,提示用户
if
(
formData
.
strategyName
||
formData
.
remarks
)
{
ElMessageBox
.
confirm
(
'确定要关闭吗?未保存的数据将会丢失'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
resetForm
()
done
()
}).
catch
(()
=>
{
// 取消关闭
})
}
else
{
resetForm
()
done
()
}
}
// 取消操作
const
handleCancel
=
()
=>
{
dialogVisible
.
value
=
false
resetForm
()
}
// 重置表单
const
resetForm
=
()
=>
{
formData
.
strategyName
=
''
formData
.
remarks
=
''
activeStep
.
value
=
1
selectedVersion
.
value
=
''
tableData
.
value
=
[]
}
// 下一步操作
const
handleNextStep
=
async
()
=>
{
if
(
activeStep
.
value
===
1
)
{
// 验证第一步表单
if
(
step1FormRef
.
value
)
{
const
formInstance
=
step1FormRef
.
value
if
(
formInstance
)
{
try
{
await
formInstance
.
validate
()
activeStep
.
value
=
2
}
catch
(
error
)
{
ElMessage
.
error
(
'请填写策略名称'
)
}
}
}
}
else
{
// 第二步:保存策略
await
saveStrategy
()
}
}
// 保存策略
const
saveStrategy
=
async
()
=>
{
try
{
// 调用保存API
const
params
=
{
strategyName
:
formData
.
strategyName
,
remarks
:
formData
.
remarks
,
state
:
"0"
,
strategyId
:
props
.
strategyId
||
''
,
projectId
:
sessionStorage
.
getItem
(
'projectId'
),
roleGroup
:
''
,
map
:{
}
// 其他需要保存的数据
}
// if (props.strategyId) {
// params.id = props.strategyId
// }
// const res = await (props.strategyId ? updateStrategy(params) : addStrategy(params))
// if (res.flag) {
ElMessage
.
success
(
props
.
strategyId
?
'编辑成功'
:
'新增成功'
)
dialogVisible
.
value
=
false
emit
(
'refresh'
)
resetForm
()
// } else {
// ElMessage.error(res.msg)
// }
// 模拟API调用
setTimeout
(()
=>
{
ElMessage
.
success
(
props
.
strategyId
?
'编辑成功'
:
'新增成功'
)
dialogVisible
.
value
=
false
emit
(
'refresh'
)
resetForm
()
},
500
)
}
catch
(
error
)
{
ElMessage
.
error
(
'保存失败'
)
}
}
const
desensitizationRuleDialogRef
=
ref
(
null
)
// 编辑字段
const
currentFieldData
=
ref
(
null
)
const
editField
=
(
row
)
=>
{
// ElMessage.info('编辑字段功能待实现')
currentFieldData
.
value
=
row
desensitizationRuleDialogRef
.
value
.
open
(
row
)
}
// 暴露方法给父组件
defineExpose
({
openDialog
})
</
script
>
<
style
scoped
>
.strategy-add-dialog
{
:deep(.el-dialog__body)
{
padding
:
20px
;
}
}
.steps-container
{
margin-bottom
:
30px
;
padding
:
0
50px
;
}
.step-content
{
min-height
:
400px
;
}
.version-select
{
margin-bottom
:
20px
;
display
:
flex
;
align-items
:
center
;
.label
{
font-weight
:
bold
;
color
:
#606266
;
}
}
.config-container
{
display
:
flex
;
height
:
400px
;
border
:
1px
solid
#e4e7ed
;
border-radius
:
4px
;
}
.tree-panel
{
width
:
300px
;
border-right
:
1px
solid
#e4e7ed
;
padding
:
10px
;
overflow-y
:
auto
;
}
.table-panel
{
flex
:
1
;
position
:
relative
;
padding
:
10px
;
}
.empty-table
{
position
:
absolute
;
top
:
50%
;
left
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
}
.tree-node
{
display
:
flex
;
align-items
:
center
;
.node-label
{
flex
:
1
;
}
.node-count
{
color
:
#909399
;
font-size
:
12px
;
margin-left
:
8px
;
}
}
.dialog-footer
{
display
:
flex
;
justify-content
:
flex-end
;
gap
:
10px
;
}
:deep
(
.el-form-item__label
)
{
font-weight
:
bold
;
}
</
style
>
\ No newline at end of file
src/views/desensitizationStrategy/DesensitizationStrategy/modules/StrategyAddDialogccc.vue
0 → 100644
View file @
5897447f
<
template
>
<el-dialog
v-model=
"dialogVisible"
:title=
"title"
width=
"80%"
:before-close=
"handleClose"
class=
"strategy-add-dialog"
>
<!-- 步骤指示器 -->
<div
class=
"steps-container"
>
<el-steps
:active=
"activeStep"
align-center
>
<el-step
title=
"基本信息"
/>
<el-step
title=
"规则配置"
/>
</el-steps>
</div>
<!-- 第一步:基本信息 -->
<div
v-if=
"activeStep === 1"
class=
"step-content"
>
<el-form
ref=
"step1FormRef"
:model=
"formData"
:rules=
"formRules"
label-width=
"100px"
label-position=
"top"
>
<el-form-item
label=
"策略名称"
prop=
"strategyName"
required
>
<el-input
v-model=
"formData.strategyName"
placeholder=
"请输入策略名称"
maxlength=
"50"
show-word-limit
/>
</el-form-item>
<el-form-item
label=
"备注"
prop=
"remarks"
>
<el-input
v-model=
"formData.remarks"
type=
"textarea"
:rows=
"4"
placeholder=
"请输入策略备注信息"
maxlength=
"200"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<!-- 第二步:规则配置 -->
<div
v-if=
"activeStep === 2"
class=
"step-content"
>
<div
class=
"version-select"
>
<span
class=
"label"
>
根据发现版本设置脱敏:
</span>
<span
class=
"label"
style=
"margin-left: 60px;"
>
选择发现版本:
</span>
<el-tree-select
v-model=
"selectedVersion"
:data=
"editionList"
:props=
"treeSelectProps"
:load=
"loadCascader"
lazy
placeholder=
"选择发现版本"
clearable
style=
"width: 200px; margin-left: 10px;"
/>
</div>
<div
class=
"config-container"
>
<!-- 左侧树形结构 -->
<div
class=
"tree-panel"
>
<el-tree
ref=
"treeRef"
:data=
"treeData"
node-key=
"id"
:props=
"treeProps"
:expand-on-click-node=
"false"
:highlight-current=
"true"
@
node-click=
"handleNodeClick"
>
<template
#
default=
"
{ node, data }">
<span
class=
"tree-node"
>
<span
class=
"node-label"
>
{{
node
.
label
}}
</span>
<span
v-if=
"data.type === 'database'"
class=
"node-count"
>
(
{{
data
.
tableCount
}}
)
</span>
</span>
</
template
>
</el-tree>
</div>
<!-- 右侧表格 -->
<div
class=
"table-panel"
>
<el-table
:data=
"tableData"
border
style=
"width: 100%"
height=
"100%"
v-loading=
"tableLoading"
>
<el-table-column
prop=
"id"
label=
"主键"
width=
"80"
align=
"center"
/>
<el-table-column
prop=
"fieldName"
label=
"字段名"
min-width=
"120"
/>
<el-table-column
prop=
"comment"
label=
"注释"
min-width=
"150"
show-overflow-tooltip
/>
<el-table-column
prop=
"dataDomain"
label=
"数据域"
min-width=
"120"
/>
<el-table-column
prop=
"algorithm"
label=
"脱敏算法"
min-width=
"150"
/>
<el-table-column
label=
"操作"
width=
"120"
align=
"center"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<el-button
link
type=
"primary"
size=
"small"
@
click=
"editField(row)"
>
编辑
</el-button>
</
template
>
</el-table-column>
</el-table>
<div
v-if=
"tableData.length === 0 && !tableLoading"
class=
"empty-table"
>
<el-empty
description=
"暂无数据"
/>
</div>
</div>
</div>
</div>
<
template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"handleCancel"
>
取消
</el-button>
<el-button
v-if=
"activeStep === 2"
@
click=
"activeStep = 1"
>
上一步
</el-button>
<el-button
type=
"primary"
@
click=
"handleNextStep"
>
{{
activeStep
===
1
?
'下一步'
:
'确定'
}}
</el-button>
</span>
</
template
>
</el-dialog>
</template>
<
script
setup
>
import
{
ref
,
reactive
,
watch
,
computed
,
nextTick
}
from
'vue'
import
{
ElMessage
}
from
'element-plus'
import
{
desensitizationruleQuery
,
queryversion
,
queryTask
,
queryProVersion
,
queryTaskVersion
,
tdatasourceQuery
,
queryShemas
,
rowsensitivelevel
,
queryDesensitizationTables
}
from
'@/api/desensitizationStrategy'
const
props
=
defineProps
({
title
:
{
type
:
String
,
default
:
'添加策略'
},
strategyId
:
{
type
:
String
,
default
:
''
}
})
const
emit
=
defineEmits
([
'refresh'
,
'update:visible'
])
const
dialogVisible
=
ref
(
false
)
const
activeStep
=
ref
(
1
)
const
selectedVersion
=
ref
(
''
)
const
tableLoading
=
ref
(
false
)
// 表单数据
const
formData
=
reactive
({
strategyName
:
''
,
remarks
:
''
})
const
step1FormRef
=
ref
()
// 表单验证规则
const
formRules
=
{
strategyName
:
[
{
required
:
true
,
message
:
'请填写策略名称'
,
trigger
:
'blur'
}
]
}
const
treeSelectProps
=
{
// value: 'value',
// label: 'text',
// children: 'children',
// isLeaf: 'leaf'
}
// 版本选项
const
editionList
=
ref
([
{
value
:
'none'
,
label
:
'无版本'
},
{
value
:
'v1'
,
label
:
'版本1.0'
},
{
value
:
'v2'
,
label
:
'版本2.0'
}
])
// 树形数据
const
treeData
=
ref
([
{
id
:
'1'
,
label
:
'若依配测2'
,
type
:
'project'
,
children
:
[
{
id
:
'1-1'
,
label
:
'ry'
,
children
:
[
{
id
:
'1-1-1'
,
label
:
'gen_table'
,
type
:
'table'
},
{
id
:
'1-1-2'
,
label
:
'gen_table_column'
,
type
:
'table'
},
{
id
:
'1-1-3'
,
label
:
'sys_config'
,
type
:
'table'
}
]
}
]
},
{
id
:
'2'
,
label
:
'若依测试库2'
,
type
:
'project'
,
children
:
[
{
id
:
'2-1'
,
label
:
'ry_test'
,
type
:
'database'
,
tableCount
:
15
,
children
:
[
{
id
:
'2-1-1'
,
label
:
'sys_dept'
,
type
:
'table'
},
{
id
:
'2-1-2'
,
label
:
'sys_dict_data'
,
type
:
'table'
},
{
id
:
'2-1-3'
,
label
:
'sys_dict_type'
,
type
:
'table'
}
]
}
]
}
])
// 表格数据
const
tableData
=
ref
([])
// 树形配置
const
treeProps
=
{
// children: 'children',
// label: 'text'
}
const
dataAreaInfo
=
ref
({
dataAreaList
:[]
})
// 打开对话框
const
openDialog
=
(
mode
,
strategyData
=
null
)
=>
{
dialogVisible
.
value
=
true
activeStep
.
value
=
1
selectedVersion
.
value
=
''
desensitizationruleQuery
().
then
(
res
=>
{
dataAreaInfo
.
value
.
dataAreaList
=
res
.
data
})
queryversion
({
projectid
:
sessionStorage
.
getItem
(
'projectId'
)
}).
then
(
res
=>
{
// editionList.value = res.data
editionList
.
value
=
res
.
data
.
map
(
item
=>
{
let
res
=
{
label
:
item
.
text
,
value
:
item
.
value
,
children
:
[],
leaf
:
item
.
value
===
'null'
||
item
.
value
===
'AllVersion'
}
!
res
.
leaf
&&
(
res
.
loading
=
false
)
return
res
})
editionList
.
value
.
splice
(
1
,
2
)
console
.
log
(
editionList
.
value
)
})
// 重置表单数据
if
(
mode
===
'addtactics'
)
{
formData
.
strategyName
=
''
formData
.
remarks
=
''
}
else
if
(
strategyData
)
{
// 填充编辑数据
formData
.
strategyName
=
strategyData
.
strategyName
||
''
formData
.
remarks
=
strategyData
.
remarks
||
''
}
// 清空表格数据
tableData
.
value
=
[]
}
const
dblinkname
=
ref
(
''
)
const
dataSystem
=
ref
()
const
loadNode
=
(
node
,
resolve
,
searchData
)
=>
{
if
(
node
.
level
===
0
)
{
// 第一级显示数据源
tdatasourceQuery
({
project_id
:
sessionStorage
.
getItem
(
'projectId'
)}).
then
(
res
=>
{
let
data
=
res
.
data
dblinkname
.
vlaue
=
res
.
data
[
0
].
dblinkname
data
.
map
(
item
=>
{
item
.
level
=
1
item
.
type
=
'datasource'
item
.
label
=
item
.
sysname
item
.
icon
=
this
.
DATABASE_ICONS
[
item
.
dbtype
]
item
.
expand
=
true
item
.
isLeaf
=
false
item
.
isshow
=
false
item
.
parent
=
null
})
if
(
treeData
.
value
.
length
===
0
)
{
getTreeData
(
JSON
.
parse
(
JSON
.
stringify
(
data
)))
}
this
.
treeNode
=
node
resolve
(
data
)
})
}
else
if
(
node
.
level
===
1
)
{
queryShemas
({
dataType
:
node
.
data
.
dbtype
,
dataSystemId
:
node
.
data
.
tid
,
projectid
:
sessionStorage
.
getItem
(
'projectId'
)
}).
then
(
res
=>
{
if
(
res
.
data
.
data
&&
res
.
data
.
data
.
length
>=
1
)
{
let
data
=
res
.
data
.
data
dataSystem
.
value
=
node
.
data
let
schemaValus
=
res
.
data
.
data
.
map
(
item
=>
item
.
value
).
join
(
','
)
data
.
map
(
item
=>
{
item
.
type
=
'schema'
item
.
label
=
item
.
text
item
.
dataSystemId
=
node
.
data
.
tid
item
.
icon
=
this
.
DATABASE_ICONS
.
DATABASE
item
.
expand
=
true
item
.
isLeaf
=
false
item
.
isshow
=
false
item
.
parent
=
node
.
data
})
node
.
data
.
resolve
=
resolve
resolve
(
data
)
let
searchData
=
[]
searchWay
(
res
.
data
.
data
).
then
(
tabs
=>
{
filterschema
.
value
.
forEach
(
item
=>
{
let
index
=
schemaValus
.
indexOf
(
item
.
value
)
if
(
index
!==
-
1
)
{
searchData
.
push
(
item
)
}
})
if
(
searchData
.
length
>=
1
)
{
if
(
node
.
childNodes
&&
node
.
childNodes
.
length
>=
1
)
{
for
(
let
i
=
node
.
childNodes
.
length
-
1
;
i
>=
0
;
i
--
)
{
if
(
searchData
.
findIndex
(
item
=>
item
.
value
===
node
.
childNodes
[
i
].
data
.
value
)
===
-
1
)
{
this
.
deleteNodeFn
(
node
.
data
.
tid
,
node
.
childNodes
[
i
].
data
.
value
,
node
.
childNodes
[
i
])
node
.
childNodes
.
splice
(
i
,
1
)
}
}
}
}
else
{
if
(
node
.
childNodes
&&
node
.
childNodes
.
length
>=
1
)
{
for
(
let
i
=
node
.
childNodes
.
length
-
1
;
i
>=
0
;
i
--
)
{
this
.
deleteNodeFn
(
node
.
data
.
tid
,
node
.
childNodes
[
i
].
data
.
value
,
node
.
childNodes
[
i
])
node
.
childNodes
.
splice
(
i
,
1
)
}
}
}
})
}
})
}
else
if
(
node
.
level
===
2
)
{
// 显示 表
dataSystem
.
value
=
node
.
data
.
parent
node
.
data
.
resolve
=
resolve
resolve
([
{
type
:
'queryTables'
,
label
:
'表'
,
dataSystemId
:
node
.
data
.
dataSystemId
,
icon
:
this
.
DATABASE_ICONS
.
queryTables
,
isLeaf
:
false
,
isshow
:
false
,
parent
:
node
.
data
}
])
}
else
if
(
node
.
level
===
3
)
{
if
(
searchData
)
{
if
(
resolve
)
{
resolve
(
searchData
.
map
(
item
=>
{
return
{
datasystem_id
:
node
.
data
.
dataSystemId
,
label
:
item
,
schema
:
this
.
schemaValue
,
type
:
'table'
,
icon
:
this
.
DATABASE_ICONS
.
TABLE
,
isLeaf
:
true
,
isshow
:
true
,
children
:
[],
parent
:
node
.
data
}
}))
}
}
else
{
dataSystem
.
value
=
node
.
data
.
parent
.
parent
rowsensitivelevel
(
`
${
node
.
data
.
type
}
`
,
{
dataSystemId
:
node
.
data
.
parent
.
dataSystemId
,
schema
:
node
.
data
.
parent
.
value
}).
then
(
res
=>
{
console
.
log
(
res
.
data
.
data
)
let
data
=
[]
this
.
searchWay
([{
value
:
node
.
data
.
parent
.
value
}]).
then
(
tabs
=>
{
tabs
.
forEach
(
item
=>
{
if
(
res
.
data
.
data
.
findIndex
(
i
=>
i
.
realName
===
item
)
!==
-
1
)
{
data
.
push
(
item
)
}
})
console
.
log
(
tabs
,
data
)
node
.
data
.
resolve
=
resolve
resolve
(
data
.
map
(
item
=>
{
console
.
log
(
item
)
return
{
datasystem_id
:
node
.
data
.
dataStrueystemId
,
label
:
item
,
schema
:
this
.
schemaValue
,
type
:
'table'
,
// icon: this.DATABASE_ICONS.TABLE,
isLeaf
:
true
,
isshow
:
false
,
children
:
[],
parent
:
node
.
data
}
}))
})
})
}
}
else
{
resolve
([])
}
}
const
deleteNodeFn
=
(
tid
,
schemaValue
,
schemas
)
=>
{
let
isrepetition
=
false
this
.
deleteNode
.
forEach
(
item
=>
{
if
(
item
.
tid
===
tid
&&
item
.
schema
===
schemaValue
)
{
isrepetition
=
true
item
.
node
=
schemas
}
})
if
(
!
isrepetition
)
{
this
.
deleteNode
.
push
({
tid
,
schema
:
schemaValue
,
schemas
})
}
}
const
getTreeData
=
(
data
,
resolve
)
=>
{
treeData
.
value
=
data
treeData
.
value
.
forEach
(
item
=>
{
item
.
data
=
JSON
.
parse
(
JSON
.
stringify
(
item
))
queryShemas
({
dataType
:
item
.
dbtype
,
dataSystemId
:
item
.
tid
,
projectid
:
sessionStorage
.
getItem
(
'projectId'
)
}).
then
(
res
=>
{
item
.
children
=
res
.
data
.
data
.
filter
(
re
=>
{
re
.
isunfold
=
false
re
.
isshow
=
false
// 判断schema是否显示
return
re
})
item
.
children
.
forEach
(
schema
=>
{
schema
.
children
=
[{
type
:
'queryTables'
,
label
:
'表'
,
dataSystemId
:
item
.
tid
,
// icon: this.DATABASE_ICONS.queryTables,
isLeaf
:
false
,
parent
:
schema
,
isunfold
:
false
}]
})
})
})
}
const
isDesensitization
=
ref
(
'-1'
)
const
tableName
=
ref
(
''
)
const
dataArea
=
ref
(
'-1'
)
const
filterschema
=
ref
([])
const
searchWay
=
async
(
schema
,
node
)
=>
{
let
data
=
[]
filterschema
.
value
=
[]
for
(
let
item
of
schema
)
{
await
queryDesensitizationTables
({
dataSystemId
:
dataSystem
.
value
.
tid
,
schema
:
item
.
value
,
state
:
isDesensitization
.
value
===
'-1'
?
''
:
isDesensitization
.
value
,
tableName
:
tableName
.
value
,
dataareaid
:
dataArea
.
value
===
'-1'
?
''
:
dataArea
.
value
,
strategyId
:
props
.
strategyId
}).
then
(
res
=>
{
if
(
res
.
data
&&
res
.
data
.
length
>=
1
)
{
filterschema
.
value
.
push
(
item
)
data
=
res
.
data
}
})
}
return
data
}
// 关闭对话框
const
handleClose
=
(
done
)
=>
{
// 如果有未保存的数据,提示用户
if
(
formData
.
strategyName
||
formData
.
remarks
)
{
ElMessageBox
.
confirm
(
'确定要关闭吗?未保存的数据将会丢失'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
}).
then
(()
=>
{
resetForm
()
done
()
}).
catch
(()
=>
{
// 取消关闭
})
}
else
{
resetForm
()
done
()
}
}
// 取消操作
const
handleCancel
=
()
=>
{
dialogVisible
.
value
=
false
resetForm
()
}
// 重置表单
const
resetForm
=
()
=>
{
formData
.
strategyName
=
''
formData
.
remarks
=
''
activeStep
.
value
=
1
selectedVersion
.
value
=
''
tableData
.
value
=
[]
}
// 下一步操作
const
handleNextStep
=
async
()
=>
{
if
(
activeStep
.
value
===
1
)
{
// 验证第一步表单
if
(
step1FormRef
.
value
)
{
const
formInstance
=
step1FormRef
.
value
if
(
formInstance
)
{
try
{
await
formInstance
.
validate
()
activeStep
.
value
=
2
}
catch
(
error
)
{
ElMessage
.
error
(
'请填写策略名称'
)
}
}
}
}
else
{
// 第二步:保存策略
await
saveStrategy
()
}
}
// 保存策略
const
saveStrategy
=
async
()
=>
{
try
{
// 调用保存API
// const params = {
// strategyName: formData.strategyName,
// remarks: formData.remarks,
// version: selectedVersion.value,
// // 其他需要保存的数据
// }
// if (props.strategyId) {
// params.id = props.strategyId
// }
// const res = await (props.strategyId ? updateStrategy(params) : addStrategy(params))
// if (res.flag) {
ElMessage
.
success
(
props
.
strategyId
?
'编辑成功'
:
'新增成功'
)
dialogVisible
.
value
=
false
emit
(
'refresh'
)
resetForm
()
// } else {
// ElMessage.error(res.msg)
// }
// 模拟API调用
setTimeout
(()
=>
{
ElMessage
.
success
(
props
.
strategyId
?
'编辑成功'
:
'新增成功'
)
dialogVisible
.
value
=
false
emit
(
'refresh'
)
resetForm
()
},
500
)
}
catch
(
error
)
{
ElMessage
.
error
(
'保存失败'
)
}
}
// 树节点点击事件
const
handleNodeClick
=
(
data
,
node
)
=>
{
if
(
data
.
type
===
'table'
)
{
showTable
(
data
)
}
}
// 显示表格数据
const
showTable
=
async
(
tableData
)
=>
{
tableLoading
.
value
=
true
try
{
// 调用接口查询表格字段数据
// const res = await getTableFields({
// tableName: tableData.label,
// database: tableData.parent.label,
// project: tableData.parent.parent.label
// })
// if (res.flag) {
// tableData.value = res.data
// } else {
// ElMessage.error(res.msg)
// tableData.value = []
// }
// 模拟数据
setTimeout
(()
=>
{
tableData
.
value
=
[
{
id
:
1
,
fieldName
:
'id'
,
comment
:
'主键ID'
,
dataDomain
:
'数字'
,
algorithm
:
'保留原值'
},
{
id
:
2
,
fieldName
:
'name'
,
comment
:
'名称'
,
dataDomain
:
'文本'
,
algorithm
:
'部分隐藏'
},
{
id
:
3
,
fieldName
:
'email'
,
comment
:
'邮箱地址'
,
dataDomain
:
'邮箱'
,
algorithm
:
'完全隐藏'
},
{
id
:
4
,
fieldName
:
'phone'
,
comment
:
'手机号码'
,
dataDomain
:
'手机号'
,
algorithm
:
'部分隐藏'
},
{
id
:
5
,
fieldName
:
'create_time'
,
comment
:
'创建时间'
,
dataDomain
:
'日期'
,
algorithm
:
'保留原值'
}
]
tableLoading
.
value
=
false
},
500
)
}
catch
(
error
)
{
ElMessage
.
error
(
'获取表格数据失败'
)
tableLoading
.
value
=
false
}
}
// 编辑字段
const
editField
=
(
row
)
=>
{
ElMessage
.
info
(
'编辑字段功能待实现'
)
}
// 暴露方法给父组件
defineExpose
({
openDialog
})
</
script
>
<
style
scoped
>
.strategy-add-dialog
{
:deep(.el-dialog__body)
{
padding
:
20px
;
}
}
.steps-container
{
margin-bottom
:
30px
;
padding
:
0
50px
;
}
.step-content
{
min-height
:
400px
;
}
.version-select
{
margin-bottom
:
20px
;
display
:
flex
;
align-items
:
center
;
.label
{
font-weight
:
bold
;
color
:
#606266
;
}
}
.config-container
{
display
:
flex
;
height
:
400px
;
border
:
1px
solid
#e4e7ed
;
border-radius
:
4px
;
}
.tree-panel
{
width
:
300px
;
border-right
:
1px
solid
#e4e7ed
;
padding
:
10px
;
overflow-y
:
auto
;
}
.table-panel
{
flex
:
1
;
position
:
relative
;
padding
:
10px
;
}
.empty-table
{
position
:
absolute
;
top
:
50%
;
left
:
50%
;
transform
:
translate
(
-50%
,
-50%
);
}
.tree-node
{
display
:
flex
;
align-items
:
center
;
.node-label
{
flex
:
1
;
}
.node-count
{
color
:
#909399
;
font-size
:
12px
;
margin-left
:
8px
;
}
}
.dialog-footer
{
display
:
flex
;
justify-content
:
flex-end
;
gap
:
10px
;
}
:deep
(
.el-form-item__label
)
{
font-weight
:
bold
;
}
</
style
>
\ No newline at end of file
src/views/desensitizationStrategy/DesensitizationStrategy/modules/TreeFilter.vue
0 → 100644
View file @
5897447f
<
template
>
<div
class=
"tree-filter-container"
>
<!-- 搜索框 -->
<div
class=
"search-box"
>
<el-input
v-model=
"filterText"
placeholder=
"输入关键字过滤"
clearable
prefix-icon=
"el-icon-search"
/>
</div>
<!-- 树形结构 -->
<el-tree
ref=
"treeRef"
class=
"filter-tree"
:data=
"treeData"
:props=
"defaultProps"
:filter-node-method=
"filterNode"
:expand-on-click-node=
"false"
node-key=
"id"
highlight-current
default-expand-all
@
node-click=
"handleNodeClick"
>
<template
#
default=
"
{ node, data }">
<span
class=
"custom-tree-node"
>
<i
:class=
"getNodeIcon(data.type)"
class=
"node-icon"
></i>
<span>
{{
node
.
label
}}
</span>
<template
v-if=
"data.type === 'category'"
>
<i
class=
"el-icon-collection icon-category"
style=
"margin-left:6px;color:#F7BA2A;font-size:16px;"
></i>
</
template
>
<
template
v-if=
"data.type === 'system'"
>
<i
class=
"el-icon-s-platform icon-system"
style=
"margin-left:6px;color:#409EFF;font-size:16px;"
></i>
</
template
>
<
template
v-if=
"data.type === 'database'"
>
<i
class=
"el-icon-s-data icon-database"
style=
"margin-left:6px;color:#67C23A;font-size:16px;"
></i>
</
template
>
<
template
v-if=
"data.type === 'table'"
>
<i
class=
"el-icon-s-grid icon-table"
style=
"margin-left:6px;color:#E6A23C;font-size:16px;"
></i>
</
template
>
</span>
</template>
</el-tree>
</div>
</template>
<
script
setup
>
import
{
ref
,
watch
}
from
'vue'
const
emit
=
defineEmits
([
'node-click'
])
const
props
=
defineProps
({
treeData
:
{
type
:
Array
,
default
:
()
=>
[
{
id
:
'system'
,
label
:
'若依配测系统'
,
type
:
'system'
,
children
:
[
{
id
:
'database'
,
label
:
'ry'
,
type
:
'database'
,
children
:
[
{
id
:
'tables'
,
label
:
'表'
,
type
:
'category'
,
children
:
[
{
id
:
'gen_table'
,
label
:
'gen_table'
,
type
:
'table'
},
{
id
:
'gen_table_column'
,
label
:
'gen_table_column'
,
type
:
'table'
},
{
id
:
'sys_config'
,
label
:
'sys_config'
,
type
:
'table'
},
{
id
:
'sys_dept'
,
label
:
'sys_dept'
,
type
:
'table'
},
{
id
:
'sys_dict_data'
,
label
:
'sys_dict_data'
,
type
:
'table'
},
{
id
:
'sys_dict_type'
,
label
:
'sys_dict_type'
,
type
:
'table'
},
{
id
:
'sys_job'
,
label
:
'sys_job'
,
type
:
'table'
},
{
id
:
'sys_job_log'
,
label
:
'sys_job_log'
,
type
:
'table'
},
{
id
:
'sys_logininfor'
,
label
:
'sys_logininfor'
,
type
:
'table'
},
{
id
:
'sys_menu'
,
label
:
'sys_menu'
,
type
:
'table'
},
{
id
:
'sys_notice'
,
label
:
'sys_notice'
,
type
:
'table'
},
{
id
:
'sys_oper_log'
,
label
:
'sys_oper_log'
,
type
:
'table'
},
{
id
:
'sys_post'
,
label
:
'sys_post'
,
type
:
'table'
},
{
id
:
'sys_role'
,
label
:
'sys_role'
,
type
:
'table'
},
{
id
:
'sys_role_dept'
,
label
:
'sys_role_dept'
,
type
:
'table'
},
{
id
:
'sys_role_menu'
,
label
:
'sys_role_menu'
,
type
:
'table'
},
{
id
:
'sys_user'
,
label
:
'sys_user'
,
type
:
'table'
},
{
id
:
'sys_user_online'
,
label
:
'sys_user_online'
,
type
:
'table'
},
{
id
:
'sys_user_post'
,
label
:
'sys_user_post'
,
type
:
'table'
},
{
id
:
'sys_user_role'
,
label
:
'sys_user_role'
,
type
:
'table'
}
]
}
]
}
]
}
]
}
})
const
filterText
=
ref
(
''
)
const
treeRef
=
ref
(
null
)
const
defaultProps
=
{
children
:
'children'
,
label
:
'label'
}
// 根据节点类型获取图标
const
getNodeIcon
=
(
type
)
=>
{
const
iconMap
=
{
system
:
'el-icon-s-platform'
,
database
:
'el-icon-s-data'
,
category
:
'el-icon-folder-opened'
,
table
:
'el-icon-s-grid'
}
return
iconMap
[
type
]
||
'el-icon-document'
}
// 过滤树节点
const
filterNode
=
(
value
,
data
)
=>
{
if
(
!
value
)
return
true
return
data
.
label
.
toLowerCase
().
includes
(
value
.
toLowerCase
())
}
// 监听过滤文本变化
watch
(
filterText
,
(
val
)
=>
{
treeRef
.
value
.
filter
(
val
)
})
// 节点点击事件
const
handleNodeClick
=
(
data
)
=>
{
emit
(
'node-click'
,
data
)
}
</
script
>
<
style
scoped
>
.tree-filter-container
{
max-height
:
700px
;
overflow-y
:
auto
;
width
:
100%
;
height
:
100%
;
/* background-color: #f5f5f5; */
padding
:
10px
;
border-right
:
1px
solid
#e6e6e6
;
}
.search-box
{
margin-bottom
:
10px
;
}
.filter-tree
{
background-color
:
transparent
;
}
.custom-tree-node
{
flex
:
1
;
display
:
flex
;
align-items
:
center
;
font-size
:
14px
;
}
.node-icon
{
margin-right
:
6px
;
color
:
#606266
;
}
.icon-category
{
vertical-align
:
middle
;
}
.icon-system
{
vertical-align
:
middle
;
}
.icon-database
{
vertical-align
:
middle
;
}
.icon-table
{
vertical-align
:
middle
;
}
:deep
(
.el-tree-node__content
)
{
height
:
36px
;
}
</
style
>
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论