Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
csbr-daop
/
fe-data-trusted-space
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
c89a85a1
authored
2026-02-03 11:42:52 +0800
by
lihua
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
迁移数据匿名化代码
1 parent
f1dd2e03
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
6898 additions
and
42 deletions
src/api/modules/dataAnonymization.ts
src/router/modules/dataAnonymization.ts
src/router/modules/dataSmartContract.ts
src/router/routes.ts
src/views/data_anonymization/anonResultReportView.vue
src/views/data_anonymization/anonResultView.vue
src/views/data_anonymization/anonTaskCreate.vue
src/views/data_anonymization/anonTaskStepTwo.vue
src/views/data_anonymization/components/anonResultAnalysis.vue
src/views/data_smart_contract/generalizeFile.vue → src/views/data_anonymization/generalizeFile.vue
src/views/data_smart_contract/generalizeFileEdit.vue → src/views/data_anonymization/generalizeFileEdit.vue
src/views/data_anonymization/labelManagement.vue
src/views/data_anonymization/resultProcess.vue
src/views/data_anonymization/sensitiveIdentify.vue
src/views/data_anonymization/sensitiveIdentifyConfig.vue
src/views/data_anonymization/sensitiveIdentifyTaskExecLog.vue
src/views/data_smart_contract/smartContractDetail.vue
src/api/modules/dataAnonymization.ts
View file @
c89a85a
...
...
@@ -3,6 +3,51 @@
*/
import
request
from
"@/utils/request"
;
/** 获取标签列表。 */
export
const
getDataLabelList
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/label/page-list`
,
method
:
'post'
,
data
:
params
})
/** 修改标签启用禁用状态 */
export
const
updateLabelState
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/label/update-state`
,
method
:
'put'
,
params
})
export
const
saveLabel
=
(
data
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/label/save`
,
method
:
'post'
,
data
})
export
const
deleteLabel
=
(
data
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/label/delete`
,
method
:
'delete'
,
data
})
export
const
updateLabel
=
(
data
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/label/update`
,
method
:
'put'
,
data
})
/** 获取标签详情 */
export
const
getLabelDetail
=
(
guid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/label/detail?guid=
${
guid
}
`
,
method
:
'get'
})
/** 获取数据字典配置 */
export
const
getParamsList
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_CONFIG_URL
}
/dict/data/get-by-dictType`
,
method
:
'get'
,
params
})
/** 字段类型 */
export
const
fieldTypeList
=
[{
value
:
'1'
,
...
...
@@ -58,4 +103,241 @@ export const parseGeneralizeFileData = (data) => request({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/generalize-file/parse-file`
,
method
:
'post'
,
data
})
/** --------- 敏感数据识别接口 ------------------- */
/** 获取敏感数据识别任务列表 */
export
const
getSensitiveDataTaskList
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/page-list`
,
method
:
'post'
,
data
:
params
})
/** 新增敏感数据识别任务 */
export
const
saveSensitiveDataTask
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/save`
,
method
:
'post'
,
data
:
params
})
/** 编辑修改敏感数据识别任务 */
export
const
updateSensitiveDataTask
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/update`
,
method
:
'put'
,
data
:
params
})
/** 删除敏感数据识别任务 */
export
const
deleteSensitiveDataTask
=
(
data
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/delete`
,
method
:
'delete'
,
data
})
/** 手动执行敏感任务 */
export
const
execSensitiveDataTask
=
(
guid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/exec-task?taskGuid=
${
guid
}
`
,
method
:
'get'
})
/** 数据来源类型 */
export
const
dataSourceTypeList
=
[{
value
:
1
,
label
:
'数据库'
},
{
value
:
2
,
label
:
'文件导入'
}];
/** 获取数据库选择列表 */
export
const
getDatabase
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DATA_SOURCE_URL
}
/data-source/get-source-list`
,
method
:
'post'
,
data
:
params
})
/** 获取敏感数据任务执行的数据表列表 */
export
const
getExecSensitiveTable
=
(
execGuid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/get-exec-sensitive-table?execGuid=
${
execGuid
}
`
,
method
:
'get'
})
/** 根据数据源或表获取敏感数据任务执行的字段列表 */
export
const
getExecSensitiveFieldTable
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/get-exec-sensitive-field`
,
method
:
'post'
,
data
:
params
})
/** 获取当前数据表下的执行字段 */
export
const
getExecSensitiveFieldColumnListByCondition
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/column-list-by-condition`
,
method
:
'post'
,
data
:
params
})
/** 获取敏感数据识别任务执行后的统计结果 */
export
const
getStatisticsNum
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/statistics-num`
,
method
:
'get'
,
params
})
/** 修改敏感数据识别字段标签 */
export
const
updateSensitiveDataTaskFieldLabel
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/batch-update-label`
,
method
:
'put'
,
data
:
params
})
/** 批量修改确认状态 */
export
const
batchUpdateSensitiveDataTaskFieldStatus
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/batch-change-status`
,
method
:
'post'
,
data
:
params
})
/** 修改任务状态 */
export
const
confirmTaskStatus
=
(
guid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/confirm-task?execGuid=
${
guid
}
`
,
method
:
'get'
})
/** 获取敏感数据识别任务执行日志 */
export
const
getSensitiveDataTaskExecLog
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/get-exec-sensitive-exec-log`
,
method
:
'post'
,
data
:
params
})
/** ---------- 匿名化处理 ------------------ */
/** 获取匿名化任务列表 */
export
const
getAnonTaskList
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/page-list`
,
method
:
'post'
,
data
:
params
})
/** 删除匿名化任务 */
export
const
deleteAnonTask
=
(
data
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/delete`
,
method
:
'delete'
,
data
})
/** 保存匿名化任务 */
export
const
saveAnonTask
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/save`
,
method
:
'post'
,
data
:
params
})
/** 更新匿名化任务 */
export
const
updateAnonTask
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/update`
,
method
:
'put'
,
data
:
params
})
/** 获取匿名化任务详情 */
export
const
getAnonTaskDetail
=
(
guid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/detail?guid=
${
guid
}
`
,
method
:
'get'
})
/** 执行匿名化任务 */
export
const
execAnonTask
=
(
taskGuid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/exec-task?taskGuid=
${
taskGuid
}
`
,
method
:
'post'
})
/** 匿名化任务检验接口 */
export
const
anonTaskCheck
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/check`
,
method
:
'post'
,
data
:
params
})
/** 获取匿名化任务分析结果数据 */
export
const
getAnonAnalyzeResult
=
(
execGuid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/get-anon-analyze?taskExecGuid=
${
execGuid
}
`
,
method
:
'get'
})
/** 获取匿名化任务分析结果数据 */
export
const
getLastAnonAnalyzeResult
=
(
execGuid
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/get-anon-analyze?isResult=true&taskExecGuid=
${
execGuid
}
`
,
method
:
'get'
})
/** 获取匿名化任务分析结果统计分页数据 */
export
const
getAnonAnalyzePageData
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/page-anon-analyze-data`
,
method
:
'post'
,
data
:
params
})
/** 获取匿名化任务结果数据 */
export
const
getAnonPageData
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/page-anon-data`
,
method
:
'post'
,
data
:
params
})
/** 字段中文转英文 */
export
const
chTransformEn
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_COMMON_URL
}
/common/convert-field-ch-name`
,
method
:
"post"
,
data
:
params
,
});
/** 根据选择的连接池获取表列表 */
export
const
getDsTableByDs
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DATA_SOURCE_URL
}
/data-source/schema-table-page-list`
,
method
:
'post'
,
data
:
params
})
/** 根据数据表获取表字段结构 */
export
const
getDsTableFieldColumn
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DATA_SOURCE_URL
}
/data-source/table-column-list`
,
method
:
'post'
,
data
:
params
});
/** 根据数据表获取表数据 */
export
const
getDsTableSampleData
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DATA_SOURCE_URL
}
/data-source/table-data-preview-page`
,
method
:
'post'
,
data
:
params
});
/** 根据字段名称获取敏感数据识别标签 */
export
const
getLableByFieldName
=
(
fieldName
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/sensitive-data-task/get-label-by-field-name?fieldName=
${
fieldName
}
`
,
method
:
'get'
});
/** 验证样本数据 */
export
const
validateAnonRule
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/check`
,
method
:
'post'
,
data
:
params
})
/** 导出匿名化结果数据 */
export
const
exportAnonExecData
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/export-anon-data?taskGuid=
${
params
.
taskGuid
}
&taskExecGuid=
${
params
.
execGuid
}
`
,
method
:
'get'
,
responseType
:
'blob'
})
/** 下载匿名化评估报告 */
export
const
exportAnonReport
=
(
params
)
=>
request
({
url
:
`
${
import
.
meta
.
env
.
VITE_APP_DIGITAL_CONTRACT_URL
}
/anon-task/download-report?taskGuid=
${
params
.
taskGuid
}
&taskExecGuid=
${
params
.
execGuid
}
`
,
method
:
'post'
,
responseType
:
'blob'
})
\ No newline at end of file
...
...
src/router/modules/dataAnonymization.ts
0 → 100644
View file @
c89a85a
import
type
{
RouteRecordRaw
}
from
'vue-router'
function
Layout
()
{
return
import
(
'@/layouts/index.vue'
)
}
const
routes
:
RouteRecordRaw
[]
=
[
{
path
:
'/data-anonymization/label-management'
,
component
:
Layout
,
meta
:
{
title
:
'标签管理'
,
icon
:
'sidebar-videos'
,
},
children
:
[
{
path
:
''
,
name
:
'labelManagement'
,
component
:
()
=>
import
(
'@/views/data_anonymization/labelManagement.vue'
),
meta
:
{
title
:
'标签管理'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
},
}
],
},
{
path
:
'/data-anonymization/generalize-file'
,
component
:
Layout
,
meta
:
{
title
:
'泛化文件管理'
,
icon
:
'sidebar-videos'
,
},
children
:
[
{
path
:
''
,
name
:
'generalizeFile'
,
component
:
()
=>
import
(
'@/views/data_anonymization/generalizeFile.vue'
),
meta
:
{
title
:
'泛化文件管理'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
},
},
{
path
:
'generalize-file-edit'
,
name
:
'generalizeFileEdit'
,
component
:
()
=>
import
(
'@/views/data_anonymization/generalizeFileEdit.vue'
),
meta
:
{
title
:
'新建泛化文件'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
,
reuse
:
true
,
editPage
:
true
,
activeMenu
:
'/data-anonymization/generalize-file'
},
beforeEnter
:
(
to
,
from
)
=>
{
if
(
to
.
query
.
fileName
)
{
to
.
meta
.
title
=
`编辑-
${
to
.
query
.
fileName
}
`
;
}
}
},
],
},
{
path
:
'/data-anonymization/sensitive-identify'
,
component
:
Layout
,
meta
:
{
title
:
'敏感数据识别'
,
icon
:
'sidebar-videos'
,
},
children
:
[
{
path
:
''
,
name
:
'sensitiveIdentify'
,
component
:
()
=>
import
(
'@/views/data_anonymization/sensitiveIdentify.vue'
),
meta
:
{
title
:
'敏感数据识别'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
},
},
{
path
:
'sensitive-identify-config'
,
name
:
'sensitiveIdentifyConfig'
,
component
:
()
=>
import
(
'@/views/data_anonymization/sensitiveIdentifyConfig.vue'
),
meta
:
{
title
:
'敏感数据识别查看'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
,
reuse
:
true
,
editPage
:
false
,
activeMenu
:
'/data-anonymization/sensitive-identify'
},
beforeEnter
:
(
to
,
from
)
=>
{
if
(
to
.
query
.
taskName
)
{
to
.
meta
.
title
=
`敏感数据
${
to
.
query
.
isLook
==
'1'
?
'日志查看'
:
'查看'
}
-
${
to
.
query
.
taskName
}
`
;
}
}
},
{
path
:
'sensitive-identify-task-exec-log'
,
name
:
'sensitiveIdentifyTaskExecLog'
,
component
:
()
=>
import
(
'@/views/data_anonymization/sensitiveIdentifyTaskExecLog.vue'
),
meta
:
{
title
:
'执行日志'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
,
reuse
:
true
},
beforeEnter
:
(
to
,
from
)
=>
{
if
(
to
.
query
.
guid
)
{
to
.
meta
.
title
=
`日志-
${
to
.
query
.
name
}
`
;
}
}
}
],
},
{
path
:
'/data-anonymization/result-process'
,
component
:
Layout
,
meta
:
{
title
:
'匿名化处理'
,
icon
:
'sidebar-videos'
,
},
children
:
[
{
path
:
''
,
name
:
'resultProcess'
,
component
:
()
=>
import
(
'@/views/data_anonymization/resultProcess.vue'
),
meta
:
{
title
:
'匿名化处理'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
},
},
{
path
:
'anon-task-create'
,
name
:
'anonTaskCreate'
,
component
:
()
=>
import
(
'@/views/data_anonymization/anonTaskCreate.vue'
),
meta
:
{
title
:
'匿名化处理任务'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
,
reuse
:
true
,
editPage
:
true
,
activeMenu
:
'/data-anonymization/result-process'
},
beforeEnter
:
(
to
,
from
)
=>
{
if
(
to
.
query
.
taskName
)
{
to
.
meta
.
title
=
`编辑-
${
to
.
query
.
taskName
}
`
;
}
}
},
{
path
:
'anonResultView'
,
name
:
'anonResultView'
,
component
:
()
=>
import
(
'@/views/data_anonymization/anonResultView.vue'
),
meta
:
{
title
:
'查看数据'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
,
reuse
:
true
},
beforeEnter
:
(
to
,
from
)
=>
{
if
(
to
.
query
.
guid
)
{
to
.
meta
.
title
=
`查看数据-
${
to
.
query
.
taskName
}
`
;
}
}
},
{
path
:
'anonResultReportView'
,
name
:
'anonResultReportView'
,
component
:
()
=>
import
(
'@/views/data_anonymization/anonResultReportView.vue'
),
meta
:
{
title
:
'查看报告'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
,
reuse
:
true
},
beforeEnter
:
(
to
,
from
)
=>
{
if
(
to
.
query
.
guid
)
{
to
.
meta
.
title
=
`查看报告-
${
to
.
query
.
taskName
}
`
;
}
}
},
],
},
]
export
default
routes
src/router/modules/dataSmartContract.ts
View file @
c89a85a
...
...
@@ -216,47 +216,7 @@ const routes: RouteRecordRaw[] = [
editPage
:
true
},
}]
},
{
path
:
'/data-smart-contract-common/generalize-file'
,
component
:
Layout
,
meta
:
{
title
:
'泛化文件管理'
,
icon
:
'sidebar-videos'
,
},
children
:
[
{
path
:
''
,
name
:
'generalizeFile'
,
component
:
()
=>
import
(
'@/views/data_smart_contract/generalizeFile.vue'
),
meta
:
{
title
:
''
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
},
},
{
path
:
'generalize-file-edit'
,
name
:
'generalizeFileEdit'
,
component
:
()
=>
import
(
'@/views/data_smart_contract/generalizeFileEdit.vue'
),
meta
:
{
title
:
'新建泛化文件'
,
sidebar
:
false
,
breadcrumb
:
false
,
cache
:
true
,
reuse
:
true
,
editPage
:
true
,
activeMenu
:
'/data-smart-contract-common/generalize-file'
},
beforeEnter
:
(
to
,
from
)
=>
{
if
(
to
.
query
.
fileName
)
{
to
.
meta
.
title
=
`编辑-
${
to
.
query
.
fileName
}
`
;
}
}
},
],
},
}
]
export
default
routes
\ No newline at end of file
...
...
src/router/routes.ts
View file @
c89a85a
...
...
@@ -7,6 +7,7 @@ import DataSmartContract from './modules/dataSmartContract';
import
DataFacilitator
from
'./modules/dataFacilitator'
;
import
HomeIndex
from
'./modules/homeIndex'
;
import
DataDelivery
from
'./modules/dataDelivery'
;
import
DataAnonymization
from
'./modules/dataAnonymization'
;
import
useSettingsStore
from
'@/store/modules/settings'
...
...
@@ -99,6 +100,7 @@ const asyncRoutes: RouteRecordRaw[] = [
...
DataFacilitator
,
...
HomeIndex
,
...
DataDelivery
,
...
DataAnonymization
,
// ...DataAssetRegistry,
]
...
...
src/views/data_anonymization/anonResultReportView.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: anonResultReportView
</route>
<
script
lang=
"ts"
setup
name=
"anonResultReportView"
>
import
{
exportAnonReport
,
getAnonAnalyzePageData
,
getAnonAnalyzeResult
,
getAnonTaskDetail
,
}
from
'@/api/modules/dataAnonymization'
;
import
{
changeNum
,
download
}
from
'@/utils/common'
;
import
{
ElMessage
}
from
'element-plus'
;
import
anonResultAnalysis
from
'./components/anonResultAnalysis.vue'
;
import
{
commonPageConfig
}
from
'@/utils/enum'
;
import
{
calcColumnWidth
}
from
'@/utils'
;
const
route
=
useRoute
();
const
router
=
useRouter
();
const
fullPath
=
route
.
fullPath
;
const
taskGuid
=
ref
(
route
.
query
.
guid
);
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
resultDataLoading
=
ref
(
false
);
const
downPromise
:
any
=
ref
()
/** 提交保存和编辑后的执行guid */
const
taskExecGuid
=
ref
(
route
.
query
.
execGuid
);
/** 记录原始的值信息,防止上一步之后未修改数据时不调用接口 */
const
oldAnonTaskValueInfo
:
any
=
ref
({});
/** 执行结果信息 */
const
analysisResultInfo
:
any
=
ref
({});
const
containerRef
=
ref
();
const
containerWidth
=
ref
(
containerRef
.
value
?.
offsetWidth
||
0
)
/** ------------------------- 匿名化分析结果页面数据展示 ---------------- */
const
pageInfo
:
any
=
ref
({
...
commonPageConfig
,
})
const
pageChange
=
(
info
)
=>
{
pageInfo
.
value
.
curr
=
Number
(
info
.
curr
);
pageInfo
.
value
.
limit
=
Number
(
info
.
limit
);
getAnalysisResultPageData
();
}
/** 每列字段对应的列宽计算结果。 */
const
originResultTableFieldColumn
=
ref
({});
/** 结果分析中的字段表格数据 */
const
resultData
:
any
=
ref
([]);
/** 结果分析中的字段信息 */
const
analysisResultTableFields
:
any
=
ref
([]);
const
analysisResultLoading
=
ref
(
false
);
/** otherWidth表示使用标题宽度时添加标题排序图标等宽度 */
const
calcTableColumnWidth
=
(
data
:
any
[],
prop
,
title
,
otherWidth
=
0
)
=>
{
let
d
:
any
[]
=
[];
data
.
forEach
((
dt
)
=>
d
.
push
(
dt
[
prop
]));
//样式使用默认值。
return
calcColumnWidth
(
d
,
title
,
{
fontSize
:
14
,
fontFamily
:
"SimSun"
,
},
{
fontSize
:
14
,
fontFamily
:
"SimSun"
,
},
otherWidth
);
};
watch
(
resultData
,
(
val
:
any
[],
oldVal
)
=>
{
if
(
!
analysisResultTableFields
.
value
?.
length
)
{
originResultTableFieldColumn
.
value
=
{};
return
;
}
originResultTableFieldColumn
.
value
=
{};
analysisResultTableFields
.
value
.
forEach
((
field
,
index
)
=>
{
originResultTableFieldColumn
.
value
[
field
.
enName
]
=
calcTableColumnWidth
(
val
?.
slice
(
0
,
20
)
||
[],
field
.
enName
,
field
.
chName
,
24
);
});
},
{
deep
:
true
,
}
);
const
getAnalysisResultPageData
=
()
=>
{
analysisResultLoading
.
value
=
true
;
getAnonAnalyzePageData
({
pageIndex
:
pageInfo
.
value
.
curr
,
pageSize
:
pageInfo
.
value
.
limit
,
taskExecGuid
:
taskExecGuid
.
value
,
}).
then
((
res
:
any
)
=>
{
analysisResultLoading
.
value
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
pageInfo
.
value
.
rows
=
resultData
.
value
=
[];
res
.
data
?.
records
?.
forEach
(
d
=>
{
let
obj
=
{};
analysisResultTableFields
.
value
.
forEach
(
t
=>
{
obj
[
t
.
enName
]
=
d
.
fieldValue
?.[
t
.
enName
];
});
obj
[
'equivalenceClassNum'
]
=
changeNum
(
d
.
equivalenceClassNum
||
0
,
0
);
obj
[
'reIdentifyRisk'
]
=
changeNum
(
d
.
reIdentifyRisk
||
0
,
2
);
obj
[
'isGtThreshold'
]
=
d
.
isGtThreshold
;
resultData
.
value
.
push
(
obj
);
});
pageInfo
.
value
.
rows
=
res
.
data
?.
totalRows
??
0
;
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
/** 下载评估报告 */
const
transfer
=
()
=>
{
if
(
downPromise
.
value
)
{
return
;
}
downPromise
.
value
=
exportAnonReport
({
taskGuid
:
route
.
query
.
guid
,
execGuid
:
taskExecGuid
.
value
}).
then
((
res
:
any
)
=>
{
downPromise
.
value
=
null
;
if
(
res
&&
!
res
.
msg
)
{
download
(
res
,
(
route
.
query
.
taskName
||
oldAnonTaskValueInfo
.
value
.
taskName
)
+
'_匿名化评估报告.docx'
,
'word'
)
}
else
{
res
?.
msg
&&
ElMessage
.
error
(
res
?.
msg
);
}
}).
catch
(()
=>
{
downPromise
.
value
=
null
;
})
}
onMounted
(()
=>
{
nextTick
(()
=>
{
containerWidth
.
value
=
containerRef
.
value
?.
offsetWidth
||
0
;
})
window
.
onresize
=
()
=>
{
containerWidth
.
value
=
containerRef
.
value
?.
offsetWidth
||
0
;
}
})
onBeforeMount
(()
=>
{
resultDataLoading
.
value
=
true
;
getAnonAnalyzeResult
(
taskExecGuid
.
value
).
then
((
res
:
any
)
=>
{
resultDataLoading
.
value
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
analysisResultInfo
.
value
=
res
.
data
||
{};
analysisResultTableFields
.
value
=
res
.
data
?.
column
||
[];
pageInfo
.
value
.
curr
=
1
;
getAnalysisResultPageData
();
}
else
{
res
?.
msg
&&
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getAnonTaskDetail
(
taskGuid
.
value
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
oldAnonTaskValueInfo
.
value
=
res
.
data
||
{};
}
else
{
res
?.
msg
&&
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
})
</
script
>
<
template
>
<div
class=
"table_tool_wrap"
v-loading=
"resultDataLoading"
ref=
"containerRef"
>
<el-button
style=
"margin-bottom: 8px;"
type=
"primary"
@
click=
"transfer"
v-preReClick
>
下载评估报告
</el-button>
<anonResultAnalysis
:show-title=
"true"
:analysis-result-info=
"analysisResultInfo"
:analysis-result-loading=
"analysisResultLoading"
:analysis-result-table-fields=
"analysisResultTableFields"
:old-anon-task-value-info=
"oldAnonTaskValueInfo"
:container-width=
"containerWidth"
:origin-result-table-field-column=
"originResultTableFieldColumn"
:page-info=
"pageInfo"
:result-data=
"resultData"
@
page-change=
"pageChange"
></anonResultAnalysis>
</div>
</
template
>
<
style
lang=
"scss"
scoped
>
.table_tool_wrap
{
width
:
100%
;
height
:
100%
;
padding
:
8px
16px
16px
;
overflow-y
:
auto
;
}
</
style
>
src/views/data_anonymization/anonResultView.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: anonResultView
</route>
<
script
lang=
"ts"
setup
name=
"anonResultView"
>
import
{
ref
}
from
"vue"
;
import
{
getAnonPageData
,
getLastAnonAnalyzeResult
,
exportAnonExecData
,
}
from
"@/api/modules/dataAnonymization"
;
import
{
calcColumnWidth
}
from
"@/utils/index"
;
import
Moment
from
'moment'
;
import
{
TableColumnWidth
}
from
"@/utils/enum"
;
import
{
ElMessage
}
from
"element-plus"
;
import
{
commonPageConfig
}
from
'@/components/PageNav/index'
;
import
{
download
}
from
"@/utils/common"
;
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
route
=
useRoute
();
const
props
=
defineProps
({
isPage
:
{
default
:
true
,
type
:
Boolean
},
execGuid
:
{
default
:
''
,
type
:
String
}
});
const
tableData
:
any
=
ref
([]);
const
tableDataLoading
=
ref
(
false
);
const
tableFields
:
any
=
ref
([]);
const
pageInfo
:
any
=
ref
({
...
commonPageConfig
,
rows
:
0
,
})
const
getData
=
()
=>
{
tableData
.
value
=
[];
if
(
!
tableFields
.
value
?.
length
)
{
return
;
}
tableDataLoading
.
value
=
true
;
getAnonPageData
({
pageIndex
:
pageInfo
.
value
.
curr
,
pageSize
:
pageInfo
.
value
.
limit
,
taskExecGuid
:
props
.
isPage
?
route
.
query
.
execGuid
:
props
.
execGuid
,
}).
then
((
res
:
any
)
=>
{
tableDataLoading
.
value
=
false
;
if
(
res
.
code
==
proxy
.
$passCode
)
{
tableData
.
value
=
[];
res
.
data
?.
records
?.
forEach
(
d
=>
{
let
obj
=
{};
tableFields
.
value
.
forEach
(
t
=>
{
obj
[
t
.
enName
]
=
d
.
fieldValue
?.[
t
.
enName
];
});
tableData
.
value
.
push
(
obj
);
});
pageInfo
.
value
.
rows
=
res
.
data
?.
totalRows
??
0
;
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
});
}
const
getTextAlign
=
(
field
)
=>
{
if
(
field
.
dataType
===
'decimal'
||
field
.
dataType
===
'int'
||
field
.
dataType
==
'bit'
||
field
.
dataType
==
'tinyint'
)
{
return
'right'
;
}
return
'left'
}
/** otherWidth表示使用标题宽度时添加标题排序图标等宽度 */
const
calcTableColumnWidth
=
(
data
:
any
[],
prop
,
title
,
otherWidth
=
0
)
=>
{
let
d
:
any
[]
=
[];
data
.
forEach
((
dt
)
=>
d
.
push
(
dt
[
prop
]));
return
calcColumnWidth
(
d
,
title
,
{
fontSize
:
14
,
fontFamily
:
"SimSun"
,
},
{
fontSize
:
14
,
fontFamily
:
"SimSun"
,
},
otherWidth
);
};
/** 每列字段对应的列宽计算结果。 */
const
originTableFieldColumn
=
ref
({});
watch
(
tableData
,
(
val
:
any
[],
oldVal
)
=>
{
if
(
!
tableFields
.
value
?.
length
)
{
originTableFieldColumn
.
value
=
{};
return
;
}
originTableFieldColumn
.
value
=
{};
tableFields
.
value
.
forEach
((
field
,
index
)
=>
{
originTableFieldColumn
.
value
[
field
.
enName
]
=
calcTableColumnWidth
(
val
?.
slice
(
0
,
20
)
||
[],
field
.
enName
,
field
.
chName
,
24
);
});
},
{
deep
:
true
,
}
);
watch
(()
=>
props
.
execGuid
,
(
val
)
=>
{
if
(
!
val
)
{
return
;
}
tableDataLoading
.
value
=
true
;
getLastAnonAnalyzeResult
(
val
).
then
((
res
:
any
)
=>
{
tableDataLoading
.
value
=
false
;
if
(
res
.
code
==
proxy
.
$passCode
)
{
let
column
=
res
.
data
?.
column
||
{};
tableFields
.
value
=
column
;
pageInfo
.
value
.
curr
=
1
;
getData
();
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
});
},
{
immediate
:
true
})
onBeforeMount
(()
=>
{
if
(
!
props
.
isPage
)
{
return
;
}
tableDataLoading
.
value
=
true
;
getLastAnonAnalyzeResult
(
route
.
query
.
execGuid
).
then
((
res
:
any
)
=>
{
tableDataLoading
.
value
=
false
;
if
(
res
.
code
==
proxy
.
$passCode
)
{
let
column
=
res
.
data
?.
column
||
{};
tableFields
.
value
=
column
;
getData
();
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
});
});
const
formatterPreviewDate
=
(
row
,
info
)
=>
{
let
enName
=
info
.
enName
;
let
v
=
row
[
enName
];
if
(
v
===
0
)
{
return
v
;
}
if
(
!
v
||
v
==
'null'
)
{
return
'--'
;
}
if
(
info
.
dataType
===
'datetime'
)
{
return
Moment
(
v
).
format
(
'YYYY-MM-DD HH:mm:ss'
);
}
if
(
info
.
dataType
===
'date'
)
{
if
(
isNaN
(
<
any
>
(
new
Date
(
v
))))
{
return
Moment
(
parseInt
(
v
)).
format
(
'YYYY-MM-DD'
);
}
else
{
return
Moment
(
v
).
format
(
'YYYY-MM-DD'
);
}
}
return
v
;
};
const
pageChange
=
(
info
)
=>
{
pageInfo
.
value
.
curr
=
Number
(
info
.
curr
);
pageInfo
.
value
.
limit
=
Number
(
info
.
limit
);
getData
();
}
const
promise
:
any
=
ref
(
null
);
const
exportData
=
()
=>
{
if
(
promise
.
value
)
{
return
;
}
promise
.
value
=
exportAnonExecData
({
taskGuid
:
route
.
query
.
guid
,
execGuid
:
route
.
query
.
execGuid
}).
then
((
res
:
any
)
=>
{
promise
.
value
=
null
;
if
(
res
&&
!
res
.
msg
)
{
download
(
res
,
route
.
query
.
taskName
+
'_匿名化数据.xlsx'
,
'excel'
)
}
else
{
res
?.
msg
&&
ElMessage
.
error
(
res
?.
msg
);
}
}).
catch
(()
=>
{
promise
.
value
=
null
;
})
}
</
script
>
<
template
>
<div
class=
"table_tool_wrap"
v-loading=
"tableDataLoading"
>
<el-button
v-show=
"props.isPage"
style=
"margin-bottom: 8px;"
type=
"primary"
@
click=
"exportData"
v-preReClick
>
导出数据
</el-button>
<el-table
ref=
"tableRef"
v-show=
"tableFields.length"
:data=
"tableData"
:highlight-current-row=
"true"
stripe
border
tooltip-effect=
"light"
height=
"100%"
row-key=
"guid"
:style=
"
{ width: '100%', height: !props.isPage ? 'calc(100% - 34px)' : 'calc(100% - 64px)' }">
<template
v-for=
"(item, index) in (tableFields || [])"
>
<el-table-column
:label=
"item.chName"
:width=
"item.dataType === 'datetime'
? TableColumnWidth.DATETIME
: item.dataType === 'date'
? TableColumnWidth.DATE
: originTableFieldColumn[item.enName]
"
:align=
"getTextAlign(item)"
:header-align=
"getTextAlign(item)"
:formatter=
"(row) => formatterPreviewDate(row, item)"
:show-overflow-tooltip=
"true"
>
</el-table-column>
</
template
>
</el-table>
<div
v-show=
"!tableFields.length"
class=
"empty-content"
>
<img
src=
"../../assets/images/empty-data.png"
:style=
"{ width: '168px', height: '96px' }"
/>
<div
class=
"empty-text"
>
暂无数据
</div>
</div>
<PageNav
:class=
"[pageInfo.type]"
:pageInfo=
"pageInfo"
@
pageChange=
"pageChange"
/>
</div>
</template>
<
style
lang=
"scss"
scoped
>
.table_tool_wrap
{
width
:
100%
;
height
:
100%
;
padding
:
8px
16px
16px
;
.tips_text
{
font-size
:
14px
;
color
:
var
(
--el-text-color-tip
);
display
:
block
;
font-weight
:
normal
;
margin-bottom
:
8px
;
line-height
:
21px
;
}
.el-table
{
display
:
inline-block
;
}
.empty-content
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
height
:
100%
;
width
:
100%
;
flex-direction
:
column
;
.empty-text
{
font-size
:
14px
;
color
:
#b2b2b2
;
}
}
}
</
style
>
\ No newline at end of file
src/views/data_anonymization/anonTaskCreate.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: anonTaskCreate
</route>
<
template
>
<div
class=
"container_wrap full"
v-loading=
"fullscreenLoading"
ref=
"containerRef"
>
<div
class=
"content_main"
>
<!-- 顶部步骤条 -->
<div
class=
"top_tool_wrap"
>
<StepBar
:steps-info=
"stepsInfo"
:style=
"
{ width: stepsInfo.list.length == 2 ? '30%' : '60%' }" />
</div>
<!-- 第一步 数据输入 -->
<div
class=
"operator_panel_wrap"
v-show=
"step == 0"
>
<ContentWrap
id=
"id-baseInfo"
title=
"数据选择"
description=
""
style=
"margin-top: 8px;"
>
<!-- 数据选择相关属性表单设置 -->
<Form
ref=
"formRef"
:itemList=
"dataSelectInfoItems"
:rules=
"dataSelectInfoFormRules"
formId=
"model-select-edit"
col=
"col3 custom-form"
@
select-change=
"handleDataSelectFormSelectChange"
@
uploadFileChange=
"uploadFileChange"
@
checkboxChange=
"handleDataSelectFormCheckboxChange"
/>
</ContentWrap>
<ContentWrap
id=
"id-previewData"
title=
"数据抽样预览"
description=
""
style=
"margin-top: 16px;"
>
<!-- 选择抽样预览的表单设置 -->
<Form
ref=
"dataSimpleFormRef"
:itemList=
"dataSimpleFormItems"
:rules=
"dataSimpleFormRules"
formId=
"data-simple-edit"
col=
"col3 fixwidth-form"
@
switch-change=
"handleDataSimpleFormSwitchChange"
@
input-change=
"handleDataSimpleFormChange"
/>
<!-- 抽样预览的数据表格设置 -->
<div
class=
"table-v2-main"
v-show=
"dataSimpleFormRef?.formInline?.enableSamplingRate == 'Y'"
v-loading=
"sampleTableDataLoading"
>
<el-table
ref=
"tableRef"
v-show=
"sampleTableFields.length"
:data=
"sampleTableData"
:highlight-current-row=
"true"
stripe
border
tooltip-effect=
"light"
height=
"100%"
row-key=
"guid"
:style=
"
{ width: '100%', height: '240px' }">
<el-table-column
label=
"序号"
type=
"index"
width=
"56px"
align=
"center"
show-overflow-tooltip
></el-table-column>
<template
v-for=
"(item, index) in (sampleTableFields || [])"
>
<el-table-column
:label=
"item.chName"
:width=
"item.dataType === 'datetime'
? TableColumnWidth.DATETIME
: item.dataType === 'date'
? TableColumnWidth.DATE
: originTableFieldColumn[item.enName]
"
:align=
"getTextAlign(item)"
:header-align=
"getTextAlign(item)"
:formatter=
"(row) => formatterPreviewDate(row, item)"
:show-overflow-tooltip=
"true"
>
</el-table-column>
</
template
>
</el-table>
<div
v-show=
"!sampleTableFields.length"
class=
"main-placeholder"
>
<img
src=
"../../assets/images/no-data.png"
:style=
"{ width: '96px', height: '96px' }"
/>
<div
class=
"empty-text"
>
暂无抽样数据
</div>
</div>
</div>
</ContentWrap>
</div>
<!-- 第二步 配置匿名化方案,单独抽取vue组件页面 -->
<anonTaskStepTwo
ref=
"anonTaskStepTwoRef"
v-show=
"step == 1"
:anonTaskRules=
"detailInfo.anonTaskRules"
:isFile=
"formRef?.formInline?.file?.length > 0"
:anonPrivacyMode=
"detailInfo.anonPrivacyMode"
:fieldTypeList=
"fieldTypeList"
:fieldNameList=
"sampleTableFields"
>
</anonTaskStepTwo>
<!-- 第三步 结果分析 -->
<div
class=
"operator_panel_wrap"
v-show=
"step == 2"
>
<ContentWrap
class=
"anlysis-content-wrap"
id=
"analysis-result"
title=
"匿名结果分析"
description=
""
style=
"margin-top: 8px;"
>
<div
class=
"wait-result-div"
v-show=
"!isExecEnd"
>
<img
class=
"loading-img"
src=
"../../assets/images/loading.gif"
/>
<div
class=
"desc"
>
正在进行匿名化处理,请稍候...
</div>
<el-button
:icon=
"RefreshRight"
link
@
click=
"refreshQueryData"
v-preReClick
>
刷新查看结果
</el-button>
</div>
<div
class=
"wait-result-div"
v-show=
"isExecEnd && analysisResultInfo.status == 'E'"
>
<el-icon
class=
"failed"
>
<CircleCloseFilled
/>
</el-icon>
<div
class=
"error-desc"
>
{{ '执行失败,请返回上一步修改配置或联系管理员' }}
</div>
<div
v-show=
"analysisResultInfo.errorMsg"
class=
"error-desc"
>
{{ '【' + analysisResultInfo.errorMsg + '】' }}
</div>
</div>
<anonResultAnalysis
v-show=
"isExecEnd && analysisResultInfo.status == 'Y'"
:analysis-result-info=
"analysisResultInfo"
:analysis-result-loading=
"analysisResultLoading"
:analysis-result-table-fields=
"analysisResultTableFields"
:old-anon-task-value-info=
"oldAnonTaskValueInfo"
:container-width=
"containerWidth"
:origin-result-table-field-column=
"originResultTableFieldColumn"
:page-info=
"pageInfo"
:result-data=
"resultData"
@
page-change=
"pageChange"
></anonResultAnalysis>
<
template
#
header
>
<el-button
v-show=
"isExecEnd && analysisResultInfo.status == 'Y'"
type=
"primary"
v-loading=
"!!downPromise"
@
click=
"transfer"
>
下载评估报告
</el-button>
</
template
>
</ContentWrap>
</div>
<!-- 匿名化结果展示 -->
<div
class=
"operator_panel_wrap step-result"
v-show=
"step == 3"
style=
"height: calc(100% - 88px);"
>
<ContentWrap
id=
"analysis-result"
title=
"匿名化数据结果"
description=
""
style=
"margin-top: 8px;height: 100%;"
>
<!-- 匿名化结果数据查看页面,单独抽取业务组件,新开页面要使用 -->
<anonResultView
:is-page=
"false"
:execGuid=
"analysisResultInfo.status == 'Y' && step == 3 ? taskExecGuid : ''"
>
</anonResultView>
</ContentWrap>
</div>
</div>
<!-- 底部按钮,需要根据当前步骤条来展示对应的按钮 -->
<div
class=
"bottom_tool_wrap"
>
<
template
v-if=
"step == 0"
>
<el-button
@
click=
"cancelTask"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"changeStep(formRef?.formInline?.handleType == '02' ? 3 : 2)"
>
下一步
</el-button>
</
template
>
<
template
v-else-if=
"step == 1"
>
<el-button
@
click=
"changeStep(1)"
>
上一步
</el-button>
<el-button
type=
"primary"
@
click=
"changeStep(3)"
>
下一步
</el-button>
</
template
>
<
template
v-else-if=
"step == 2"
>
<el-button
@
click=
"changeStep(formRef?.formInline?.handleType == '02' ? 1 : 2)"
>
上一步
</el-button>
<el-button
v-show=
"formRef?.formInline?.handleType != '02'"
type=
"primary"
:disabled=
"analysisResultInfo.status == 'R' || (isExecEnd && analysisResultInfo.status == 'E')"
@
click=
"changeStep(4)"
>
下一步
</el-button>
<el-button
type=
"primary"
v-show=
"formRef?.formInline?.handleType == '02'"
:disabled=
"analysisResultInfo.status == 'R' || (isExecEnd && analysisResultInfo.status == 'E')"
v-preReClick
@
click=
"closeTask"
>
关闭
</el-button>
</
template
>
<
template
v-else
>
<el-button
@
click=
"changeStep(3)"
>
上一步
</el-button>
<el-button
type=
"primary"
v-preReClick
@
click=
"exportResult"
>
导出
</el-button>
</
template
>
</div>
</div>
</template>
<
script
lang=
"ts"
setup
name=
"anonTaskCreate"
>
import
{
dataSourceTypeList
,
getAnonTaskDetail
,
getParamsList
,
chTransformEn
,
getAnonAnalyzeResult
,
getAnonAnalyzePageData
,
getDatabase
,
getDsTableByDs
,
getDsTableFieldColumn
,
getDsTableSampleData
,
saveAnonTask
,
updateAnonTask
,
exportAnonExecData
,
exportAnonReport
,
}
from
'@/api/modules/dataAnonymization'
;
import
{
parseAndDecodeUrl
,
getDownFileSignByUrl
,
obsDownloadRequest
}
from
"@/api/modules/obsService"
;
import
{
getAreaData
}
from
"@/api/modules/queryService"
;
import
useUserStore
from
"@/store/modules/user"
;
import
{
useValidator
}
from
'@/hooks/useValidator'
;
import
{
TableColumnWidth
}
from
'@/utils/enum'
;
import
{
calcColumnWidth
}
from
"@/utils/index"
;
import
Moment
from
'moment'
;
import
anonTaskStepTwo
from
'./anonTaskStepTwo.vue'
;
import
*
as
XLSX
from
'xlsx'
;
import
{
ElMessage
}
from
'element-plus'
;
import
{
isEqual
,
cloneDeep
}
from
"lodash-es"
;
import
{
changeNum
,
download
}
from
"@/utils/common"
;
import
anonResultView
from
'./anonResultView.vue'
;
import
useDataAnonymizationStore
from
"@/store/modules/dataAnonymization"
;
import
{
RefreshRight
,
CircleCloseFilled
,
Right
}
from
"@element-plus/icons-vue"
;
import
{
commonPageConfig
}
from
'@/components/PageNav'
;
import
{
QuestionFilled
}
from
"@element-plus/icons-vue"
;
import
anonResultAnalysis
from
'./components/anonResultAnalysis.vue'
;
const
anonymizationStore
=
useDataAnonymizationStore
();
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
userStore
=
useUserStore
();
const
route
=
useRoute
();
const
router
=
useRouter
();
const
fullPath
=
route
.
fullPath
;
const
taskGuid
=
ref
(
route
.
query
.
guid
);
/** 提交保存和编辑后的执行guid */
const
taskExecGuid
=
ref
(
''
);
/** 是否执行结束,用于第四步获取执行结果 */
const
isExecEnd
=
ref
(
false
);
const
{
required
}
=
useValidator
();
const
fullscreenLoading
=
ref
(
false
);
const
containerRef
=
ref
();
const
containerWidth
=
ref
(
containerRef
.
value
?.
offsetWidth
||
0
)
const
step
=
ref
(
0
);
const
originStepsInfo
=
ref
({
step
:
step
.
value
,
list
:
[
{
title
:
'数据输入'
,
value
:
1
},
{
title
:
'配置匿名化方案'
,
value
:
2
},
{
title
:
'匿名结果分析'
,
value
:
3
},
{
title
:
'结果输出'
,
value
:
4
}
]
})
const
stepsInfo
=
ref
(
originStepsInfo
.
value
);
/** 已匿名化出具报告的 */
const
reportStepsInfo
=
ref
({
step
:
step
.
value
,
list
:
[
{
title
:
'数据输入'
,
value
:
1
},
// { title: '配置匿名化方案', value: 2 },
{
title
:
'匿名结果分析'
,
value
:
2
},
// { title: '结果输出', value: 4 }
]
})
/** 数据源列表 */
const
dataSourceList
:
any
=
ref
([]);
/** 数据源对应的数据表 */
const
dsTableList
:
any
=
ref
([]);
/** 数据共享类型字段列表 */
const
dataSharingTypeList
=
ref
([]);
/** 匿名化处理类型 */
const
handleTypeList
=
ref
([]);
const
getParentAreaPromise
:
any
=
ref
(
null
);
const
getAreaDataPromise
:
any
=
ref
({});
const
getAreaDatas
:
any
=
ref
({});
const
parentAreaData
:
any
=
ref
([]);
const
getArea
=
(
node
,
resolve
)
=>
{
const
{
level
}
=
node
let
params
=
{
parentGuid
:
node
.
value
}
if
(
!
node
.
value
)
{
if
(
getParentAreaPromise
.
value
)
{
getParentAreaPromise
.
value
.
then
((
res
:
any
)
=>
{
resolve
(
res
);
})
}
else
{
resolve
(
parentAreaData
.
value
);
}
return
;
}
if
(
node
.
loaded
)
{
resolve
([]);
return
;
}
if
(
getAreaDatas
.
value
[
node
.
value
]?.
length
)
{
resolve
(
getAreaDatas
.
value
[
node
.
value
]);
return
;
}
if
(
!
getAreaDataPromise
.
value
[
node
.
value
])
{
getAreaDataPromise
.
value
[
node
.
value
]
=
getAreaData
(
params
).
then
((
res
:
any
)
=>
{
node
.
loaded
=
true
;
getAreaDataPromise
.
value
[
node
.
value
]
=
null
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
const
data
=
res
.
data
??
[]
data
.
map
(
item
=>
{
item
.
leaf
=
level
>=
1
})
resolve
(
data
)
getAreaDatas
.
value
[
node
.
value
]
=
data
;
return
data
;
}
})
}
else
{
getAreaDataPromise
.
value
[
node
.
value
].
then
((
data
)
=>
{
getAreaDataPromise
.
value
[
node
.
value
]
=
null
;
node
.
loaded
=
true
;
data
.
map
(
item
=>
{
item
.
leaf
=
level
>=
1
})
resolve
(
data
)
})
}
}
const
formRef
=
ref
();
/** 数据选择的表单配置信息 */
const
dataSelectInfoItems
=
ref
([{
label
:
'数据集名称'
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'taskName'
,
maxlength
:
15
,
default
:
''
,
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
true
,
},
{
label
:
'数据共享类型'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'dataSharingTypeCode'
,
default
:
'01'
,
options
:
dataSharingTypeList
.
value
,
props
:
{
label
:
"label"
,
value
:
"value"
,
},
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
true
,
},
{
label
:
'所属地域'
,
type
:
"cascader"
,
placeholder
:
"请选择"
,
field
:
"coverageArea"
,
default
:
[],
showAllLevels
:
true
,
props
:
{
label
:
'name'
,
value
:
'guid'
,
lazy
:
true
,
checkStrictly
:
true
,
lazyLoad
:
getArea
,
multiple
:
false
,
},
collapse
:
true
,
tagsTooltip
:
true
,
// filterable: true,
clearable
:
true
,
required
:
false
,
//不选默认表示全国。
//col: 'checkbox-right',
visible
:
true
},
// { 去掉,直接用数据集总行数/全国人口总数计算
// label: '患者占总人口比',
// type: 'input',
// placeholder: '数值,支持小数点9位',
// field: 'patientPopulationRate',
// maxlength: 11,
// min: 0,
// max: 1,
// inputType: 'scoreNumber',
// decimalCnt: 9,
// default: '',
// required: false,
// filterable: true,
// clearable: true,
// visible: true,
// },
{
label
:
'处理类型'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'handleType'
,
default
:
'01'
,
options
:
handleTypeList
.
value
,
props
:
{
label
:
"label"
,
value
:
"value"
,
},
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
true
,
},
{
label
:
'数据来源'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'dataSource'
,
default
:
1
,
options
:
dataSourceTypeList
,
props
:
{
label
:
"label"
,
value
:
"value"
,
},
required
:
true
,
filterable
:
true
,
visible
:
true
,
},
{
label
:
'数据源'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'dataSourceGuid'
,
default
:
''
,
options
:
dataSourceList
.
value
,
props
:
{
label
:
'databaseNameZh'
,
value
:
'guid'
},
filterable
:
true
,
visible
:
true
,
required
:
true
},
{
label
:
"数据表"
,
type
:
"select"
,
placeholder
:
"请选择"
,
field
:
"tableName"
,
options
:
dsTableList
.
value
,
props
:
{
label
:
'tableComment'
,
value
:
'tableName'
},
default
:
''
,
filterable
:
true
,
clearable
:
true
,
required
:
true
,
},
{
label
:
"准标识符"
,
type
:
"select"
,
placeholder
:
"请选择"
,
field
:
"qualifiedIdentifier"
,
options
:
dsTableList
.
value
,
props
:
{
label
:
'chName'
,
value
:
'enName'
},
default
:
[],
multiple
:
true
,
collapse
:
true
,
tagsTooltip
:
true
,
filterable
:
true
,
clearable
:
true
,
required
:
true
,
visible
:
false
,
},
{
label
:
'文件上传'
,
tip
:
'支持扩展名:xlsx、xls、csv,文件大小不超过10MB'
,
type
:
'upload-file'
,
accept
:
'.xlsx, .xls, .csv'
,
limitSize
:
10
,
limit
:
1
,
isExcel
:
true
,
required
:
true
,
default
:
<
any
>
[],
block
:
false
,
col
:
'wid60'
,
visible
:
false
,
field
:
'file'
,
},]);
const
dataSelectInfoFormRules
=
ref
({
taskName
:
[
required
(
'请输入数据集名称'
)],
dataSharingTypeCode
:
[
required
(
'请选择数据共享类型'
)],
// patientPopulationRate: [required('请输入患者占总人口比')],
dataSourceGuid
:
[
required
(
'请选择数据源'
)],
handleType
:
[
required
(
'请选择处理类型'
)],
tableName
:
[
required
(
'请选择数据表'
)],
qualifiedIdentifier
:
[{
type
:
'array'
,
required
:
true
,
trigger
:
'change'
,
message
:
"请选择准标识符"
}],
file
:
[{
validator
:
(
rule
:
any
,
value
:
any
,
callback
:
any
)
=>
{
if
(
!
value
?.
length
)
{
callback
(
new
Error
(
'请上传文件'
))
}
else
{
callback
();
}
},
trigger
:
'change'
}]
});
/** 最新选中的 */
const
currDatasourceSelect
:
any
=
ref
({});
const
handleDataSelectFormSelectChange
=
async
(
val
,
row
,
formInfo
)
=>
{
if
(
row
.
field
==
'dataSource'
)
{
dataSelectInfoItems
.
value
[
5
].
visible
=
val
==
1
;
dataSelectInfoItems
.
value
[
6
].
visible
=
val
==
1
;
dataSelectInfoItems
.
value
[
8
].
visible
=
val
==
2
;
setDataSelectFormItems
(
Object
.
assign
({},
formInfo
,
{
file
:
!
formInfo
[
'file'
]
?
[]
:
formInfo
[
'file'
]
}))
sampleTableFields
.
value
=
[];
parseFileDataSum
.
value
=
[];
sampleTableData
.
value
=
[];
}
else
if
(
row
.
field
==
'dataSourceGuid'
)
{
if
(
!
val
)
{
currDatasourceSelect
.
value
=
[];
sampleTableFields
.
value
=
[];
parseFileDataSum
.
value
=
[];
sampleTableData
.
value
=
[];
setDataSelectFormItems
(
Object
.
assign
({},
formInfo
,
{
file
:
!
formInfo
[
'file'
]
?
[]
:
formInfo
[
'file'
],
tableName
:
''
,
qualifiedIdentifier
:
[]
}))
let
item
=
dataSelectInfoItems
.
value
.
find
(
d
=>
d
.
field
==
'tableName'
);
item
&&
(
item
.
options
=
dsTableList
.
value
);
return
;
}
let
dsInfo
=
currDatasourceSelect
.
value
=
dataSourceList
.
value
.
find
(
d
=>
d
.
guid
==
val
);
//清除数据表得值,重新获取下拉列表
const
res
:
any
=
await
getDsTableByDs
({
pageSize
:
-
1
,
pageIndex
:
1
,
dataSourceGuid
:
val
,
database
:
dsInfo
.
databaseNameEn
,
databaseType
:
dsInfo
.
databaseType
,
tableName
:
''
,
hadFlag
:
false
});
if
(
res
.
code
==
proxy
.
$passCode
)
{
dsTableList
.
value
=
res
.
data
?.
records
||
[];
setDataSelectFormItems
(
Object
.
assign
({},
formInfo
,
{
file
:
!
formInfo
[
'file'
]
?
[]
:
formInfo
[
'file'
],
tableName
:
''
,
qualifiedIdentifier
:
[]
}))
let
item
=
dataSelectInfoItems
.
value
.
find
(
d
=>
d
.
field
==
'tableName'
);
item
&&
(
item
.
options
=
dsTableList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
sampleTableFields
.
value
=
[];
parseFileDataSum
.
value
=
[];
sampleTableData
.
value
=
[];
}
else
if
(
row
.
field
==
'tableName'
)
{
if
(
!
val
)
{
sampleTableFields
.
value
=
[];
sampleTableData
.
value
=
[];
return
;
}
getDsTableFieldColumn
({
pageSize
:
50
,
pageIndex
:
1
,
dataSourceGuid
:
currDatasourceSelect
.
value
.
guid
,
database
:
currDatasourceSelect
.
value
.
databaseNameEn
,
databaseType
:
currDatasourceSelect
.
value
.
databaseType
,
tableName
:
val
,
}).
then
((
res
:
any
)
=>
{
if
(
res
.
code
==
proxy
.
$passCode
)
{
sampleTableFields
.
value
=
res
.
data
?.
map
(
d
=>
{
d
.
fieldDataType
=
d
.
dataType
;
d
.
enName
=
d
.
columnName
;
d
.
chName
=
d
.
columnZhName
;
return
d
;
})
||
[];
/** 判断有抽样数据,需要查询接口 */
getSampleDataByDsTable
();
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
});
}
else
if
(
row
.
field
==
'handleType'
)
{
setDataSelectFormItems
(
formInfo
);
}
}
const
setDataSelectFormItems
=
(
info
,
isDetail
=
false
)
=>
{
dataSelectInfoItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
info
[
item
.
field
];
if
(
item
.
field
==
'coverageArea'
)
{
// item && item.children?.length && (item.children[0].visible = info['coverageArea'] != 'all');
if
(
!
isDetail
)
{
return
;
}
let
coverageArea
=
info
.
coverageArea
;
if
(
coverageArea
&&
Array
.
isArray
(
coverageArea
)
&&
coverageArea
.
length
>
0
)
{
item
.
default
=
coverageArea
[
0
]
as
any
;
let
p
:
any
=
[];
coverageArea
?.
forEach
(
area
=>
{
if
(
p
.
includes
(
area
[
0
]))
{
return
;
}
p
.
push
(
area
[
0
]);
getArea
({
value
:
area
[
0
],
level
:
1
},
()
=>
{
})
});
let
ps
:
any
=
[]
for
(
const
key
in
getAreaDataPromise
.
value
)
{
ps
.
push
(
getAreaDataPromise
.
value
[
key
])
}
Promise
.
all
(
ps
).
then
(()
=>
{
item
.
default
=
coverageArea
[
0
];
});
}
else
{
item
.
default
=
''
;
}
}
else
if
(
item
.
field
==
'qualifiedIdentifier'
)
{
item
.
visible
=
info
[
'handleType'
]
==
'02'
;
}
});
stepsInfo
.
value
=
info
.
handleType
==
'02'
?
reportStepsInfo
.
value
:
originStepsInfo
.
value
;
}
const
handleDataSelectFormCheckboxChange
=
(
val
,
info
,
row
)
=>
{
row
.
field
==
'coverageArea'
&&
setDataSelectFormItems
(
info
);
}
const
dataSimpleFormRef
=
ref
();
/** 抽样数据预览 */
const
dataSimpleFormItems
=
ref
([{
label
:
'抽样开关'
,
type
:
'switch'
,
field
:
'enableSamplingRate'
,
default
:
'N'
,
col
:
'autoWidth'
,
activeValue
:
'Y'
,
inactiveValue
:
'N'
},
{
label
:
'抽样比例(%)'
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'samplingRate'
,
maxlength
:
3
,
min
:
0
,
//可以是0条。万一只是想看下字段呢
max
:
100
,
inputType
:
'integerNumber'
,
default
:
10
,
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
false
,
}]);
const
dataSimpleFormRules
=
ref
({
samplingRate
:
[
required
(
'请填写抽样比例'
)],
});
const
oldSamplingRate
=
ref
(
'10'
);
const
handleDataSimpleFormSwitchChange
=
(
val
,
info
)
=>
{
if
(
val
==
'N'
)
{
oldSamplingRate
.
value
=
info
.
samplingRate
;
}
else
{
dataSimpleFormItems
.
value
[
1
].
default
=
oldSamplingRate
.
value
||
10
;
}
dataSimpleFormItems
.
value
[
1
].
visible
=
val
==
'Y'
;
dataSimpleFormItems
.
value
[
0
].
default
=
info
.
enableSamplingRate
||
'N'
;
if
(
formRef
.
value
?.
formInline
?.
file
?.
length
)
{
transferSampleData
();
}
else
{
getSampleDataByDsTable
();
}
}
/** 输入抽样比例值改变 */
const
handleDataSimpleFormChange
=
(
val
)
=>
{
if
(
formRef
.
value
?.
formInline
?.
file
?.
length
)
{
transferSampleData
();
}
else
{
getSampleDataByDsTable
();
}
}
/** 样本表格加载中 */
const
sampleTableDataLoading
=
ref
(
false
);
/** 样本表格的数据 */
const
sampleTableData
:
any
=
ref
([]);
/** 样本表格的字段 */
const
sampleTableFields
:
any
=
ref
([]);
/** otherWidth表示使用标题宽度时添加标题排序图标等宽度 */
const
calcTableColumnWidth
=
(
data
:
any
[],
prop
,
title
,
otherWidth
=
0
)
=>
{
let
d
:
any
[]
=
[];
data
.
forEach
((
dt
)
=>
d
.
push
(
dt
[
prop
]));
return
calcColumnWidth
(
d
,
title
,
{
fontSize
:
14
,
fontFamily
:
"SimSun"
,
},
{
fontSize
:
14
,
fontFamily
:
"SimSun"
,
},
otherWidth
);
};
/** 每列字段对应的列宽计算结果。 */
const
originTableFieldColumn
=
ref
({});
const
getTextAlign
=
(
field
)
=>
{
if
(
field
.
dataType
===
'decimal'
||
field
.
dataType
===
'int'
)
{
return
'right'
;
}
return
'left'
}
watch
(
sampleTableData
,
(
val
:
any
[],
oldVal
)
=>
{
if
(
!
sampleTableFields
.
value
?.
length
)
{
originTableFieldColumn
.
value
=
{};
return
;
}
originTableFieldColumn
.
value
=
{};
sampleTableFields
.
value
.
forEach
((
field
,
index
)
=>
{
originTableFieldColumn
.
value
[
field
.
enName
]
=
calcTableColumnWidth
(
val
?.
slice
(
0
,
20
)
||
[],
field
.
enName
,
field
.
chName
,
24
);
});
},
{
deep
:
true
,
}
);
watch
(()
=>
sampleTableFields
.
value
,
(
val
)
=>
{
let
item
=
dataSelectInfoItems
.
value
.
find
(
selectItem
=>
selectItem
.
field
==
'qualifiedIdentifier'
);
item
&&
(
item
.
options
=
val
);
let
formInfo
=
formRef
.
value
.
formInline
;
if
(
formInfo
.
handleType
==
'02'
&&
!
(
taskGuid
.
value
&&
(
formInfo
.
file
?.[
0
]
&&
formInfo
.
file
?.[
0
]?.
url
==
detailInfo
.
value
.
filePath
?.
url
||
(
formInfo
.
tableName
&&
formInfo
.
tableName
==
detailInfo
.
value
.
tableName
))))
{
//需要同步清除准标识符的字段选择
setDataSelectFormItems
(
Object
.
assign
({},
formInfo
,
{
file
:
!
formInfo
[
'file'
]
?
[]
:
formInfo
[
'file'
],
qualifiedIdentifier
:
[]
}))
}
},
{
deep
:
true
})
const
formatterPreviewDate
=
(
row
,
info
)
=>
{
let
enName
=
info
.
enName
;
let
v
=
row
[
enName
];
if
(
v
===
0
)
{
return
v
;
}
if
(
!
v
||
v
==
'null'
)
{
return
'--'
;
}
if
(
info
.
dataType
===
'datetime'
)
{
return
Moment
(
v
).
format
(
'YYYY-MM-DD HH:mm:ss'
);
}
if
(
info
.
dataType
===
'date'
)
{
if
(
isNaN
(
<
any
>
(
new
Date
(
v
))))
{
return
Moment
(
parseInt
(
v
)).
format
(
'YYYY-MM-DD'
);
}
else
{
return
Moment
(
v
).
format
(
'YYYY-MM-DD'
);
}
}
return
v
;
};
/** 解析的总的表格数据,方便后面修改抽样比例时使用 */
const
parseFileDataSum
:
any
=
ref
([]);
const
currentSheet
:
any
=
ref
();
const
parseFileData
=
(
fileRaw
)
=>
{
sampleTableDataLoading
.
value
=
true
;
fileRaw
.
arrayBuffer
().
then
(
async
(
f
)
=>
{
const
wb
=
XLSX
.
read
(
f
,
{
raw
:
false
,
cellDates
:
true
});
const
sheet
=
wb
.
Sheets
[
wb
.
SheetNames
[
0
]];
currentSheet
.
value
=
sheet
;
const
json
:
any
[]
=
XLSX
.
utils
.
sheet_to_json
(
sheet
,
{
header
:
1
});
if
(
json
.
length
==
0
)
{
sampleTableFields
.
value
=
[];
sampleTableData
.
value
=
[];
}
else
{
try
{
const
res
:
any
=
await
chTransformEn
(
json
[
0
]);
if
(
res
?.
code
!=
proxy
.
$passCode
)
{
sampleTableDataLoading
.
value
=
false
;
proxy
.
$ElMessage
.
error
(
res
.
msg
);
return
;
}
let
fields
=
res
.
data
||
[];
sampleTableFields
.
value
=
fields
?.
map
((
j
,
index
)
=>
{
return
{
index
:
index
,
enName
:
j
.
enName
+
''
,
chName
:
j
.
chName
+
''
,
dataType
:
'varchar'
}
})
||
[];
parseFileDataSum
.
value
=
json
;
/** 粗略算出字段类型 */
json
.
slice
(
1
,
10
).
forEach
((
info
,
row
)
=>
{
json
[
0
].
forEach
((
name
,
col
)
=>
{
if
(
info
[
col
]
===
""
||
info
[
col
]
==
null
||
sampleTableFields
.
value
[
col
].
dataType
!=
'varchar'
)
{
return
;
}
else
{
var
cellRef
=
XLSX
.
utils
.
encode_cell
({
r
:
row
+
1
,
c
:
col
});
var
cell
=
sheet
[
cellRef
];
let
v
=
cell
.
w
||
info
[
col
];
let
isNum
=
cell
.
t
==
'n'
;
if
(
isNum
)
{
if
(
v
.
includes
(
'.'
)
&&
sampleTableFields
.
value
[
col
].
dataType
!=
'decimal'
)
{
sampleTableFields
.
value
[
col
].
dataType
=
'decimal'
;
}
else
{
sampleTableFields
.
value
[
col
].
dataType
=
'int'
;
}
}
}
});
})
transferSampleData
();
sampleTableDataLoading
.
value
=
false
;
}
catch
(
error
)
{
sampleTableDataLoading
.
value
=
false
;
}
}
});
}
/** 获取文件解析后根据抽样比例得出的表格数据,默认查看前500条数据 */
const
transferSampleData
=
()
=>
{
let
samplingRate
=
dataSimpleFormRef
.
value
?.
formInline
?.
samplingRate
;
if
(
parseFileDataSum
.
value
.
length
>
1
&&
samplingRate
)
{
let
totalCnt
=
parseFileDataSum
.
value
.
length
-
1
;
let
cnt
=
Math
.
ceil
(
samplingRate
*
0.01
*
totalCnt
)
+
1
;
sampleTableData
.
value
=
parseFileDataSum
.
value
.
slice
(
1
,
cnt
>
500
?
501
:
cnt
).
map
((
info
,
row
)
=>
{
let
object
=
{};
parseFileDataSum
.
value
[
0
].
forEach
((
chName
,
col
)
=>
{
let
name
=
sampleTableFields
.
value
[
col
].
enName
;
var
cellRef
=
XLSX
.
utils
.
encode_cell
({
r
:
row
+
1
,
c
:
col
});
var
cell
=
currentSheet
.
value
[
cellRef
];
let
v
=
cell
.
w
||
info
[
col
];
object
[
name
]
=
v
;
});
return
object
;
});
}
else
{
sampleTableData
.
value
=
[];
}
}
/** 获取选择的数据库表根据抽样比例得出的表格数据 */
const
getSampleDataByDsTable
=
()
=>
{
const
tableName
=
formRef
.
value
?.
formInline
?.
tableName
;
if
(
!
currDatasourceSelect
.
value
.
guid
||
!
tableName
)
{
sampleTableFields
.
value
=
[];
sampleTableData
.
value
=
[];
return
;
}
let
samplingRate
=
dataSimpleFormRef
.
value
?.
formInline
?.
samplingRate
;
if
(
!
samplingRate
)
{
sampleTableData
.
value
=
[];
return
;
}
let
totalCnt
=
dsTableList
.
value
.
find
(
t
=>
t
.
tableName
==
tableName
)?.
tableRows
||
0
;
let
cnt
=
Math
.
ceil
(
samplingRate
*
0.01
*
totalCnt
);
if
(
!
cnt
)
{
sampleTableData
.
value
=
[];
return
;
}
sampleTableDataLoading
.
value
=
true
;
getDsTableSampleData
({
limitNum
:
cnt
>
500
?
500
:
cnt
,
pageSize
:
cnt
>
500
?
500
:
cnt
,
pageIndex
:
1
,
dataSourceGuid
:
currDatasourceSelect
.
value
.
guid
,
database
:
currDatasourceSelect
.
value
.
databaseNameEn
,
databaseType
:
currDatasourceSelect
.
value
.
databaseType
,
tableName
:
tableName
,
hadFlag
:
false
,
}).
then
((
res
:
any
)
=>
{
sampleTableDataLoading
.
value
=
false
;
if
(
res
.
code
==
proxy
.
$passCode
)
{
sampleTableData
.
value
=
res
.
data
?.
datas
||
[];
}
else
{
sampleTableData
.
value
=
[];
ElMessage
.
error
(
res
.
msg
);
}
});
}
const
uploadFileChange
=
(
file
)
=>
{
sampleTableData
.
value
=
[];
if
(
!
file
.
length
)
{
sampleTableFields
.
value
=
[];
sampleTableData
.
value
=
[];
return
;
}
let
fileRaw
=
file
[
0
].
file
;
parseFileData
(
fileRaw
);
}
/** 第二步的配置组件引用。 */
const
anonTaskStepTwoRef
=
ref
();
const
changeStep
=
async
(
val
)
=>
{
if
(
val
<=
step
.
value
)
{
step
.
value
=
val
-
1
;
stepsInfo
.
value
.
step
=
val
-
1
;
}
else
if
(
val
==
2
)
{
formRef
.
value
?.
ruleFormRef
?.
validate
((
valid
)
=>
{
if
(
valid
)
{
if
(
formRef
.
value
?.
formInline
?.
dataSource
==
2
&&
!
sampleTableFields
.
value
?.
length
)
{
proxy
.
$ElMessage
.
error
(
'上传文件的字段不能为空'
);
return
;
}
dataSimpleFormRef
.
value
?.
ruleFormRef
?.
validate
((
valid
)
=>
{
if
(
valid
)
{
// 第一步到第二步时,如果字段列表中与字段脱敏规则中的字段不匹配,应清空。
step
.
value
=
val
-
1
;
stepsInfo
.
value
.
step
=
val
-
1
;
anonTaskStepTwoRef
.
value
?.
updateNextStepRules
();
}
});
}
});
}
else
if
(
val
==
3
)
{
let
exec
=
(
saveParams
)
=>
{
if
(
saveParams
.
coverageArea
?.
length
)
{
saveParams
.
coverageArea
=
[
saveParams
.
coverageArea
];
}
else
{
saveParams
.
coverageArea
=
[];
}
if
(
saveParams
.
file
?.
length
)
{
saveParams
.
filePath
=
{
name
:
saveParams
.
file
[
0
].
name
,
url
:
saveParams
.
file
[
0
].
url
}
delete
saveParams
.
file
;
saveParams
.
dataSourceGuid
=
null
;
saveParams
.
tableName
=
null
;
}
else
{
saveParams
.
filePath
=
null
;
}
let
simpleFormInline
=
dataSimpleFormRef
.
value
.
formInline
;
if
(
simpleFormInline
.
enableSamplingRate
==
'Y'
)
{
saveParams
.
samplingRate
=
simpleFormInline
.
samplingRate
&&
parseInt
(
simpleFormInline
.
samplingRate
);
}
else
{
saveParams
.
samplingRate
=
null
;
}
if
(
taskGuid
.
value
)
{
saveParams
.
guid
=
taskGuid
.
value
;
}
if
(
isEqual
(
saveParams
,
oldAnonTaskValueInfo
.
value
))
{
isExecEnd
.
value
=
false
;
step
.
value
=
val
-
1
;
stepsInfo
.
value
.
step
=
val
-
1
;
if
(
!
analysisResultInfo
.
value
?.
status
)
{
processStepThreeResultView
();
}
else
{
isExecEnd
.
value
=
analysisResultInfo
.
value
?.
status
==
'E'
||
analysisResultInfo
.
value
?.
status
==
'Y'
;
}
return
;
}
if
(
!
taskGuid
.
value
)
{
//保存
fullscreenLoading
.
value
=
true
;
saveAnonTask
(
saveParams
).
then
((
res
:
any
)
=>
{
fullscreenLoading
.
value
=
false
;
if
(
res
.
code
==
proxy
.
$passCode
)
{
taskGuid
.
value
=
res
.
data
?.
taskGuid
;
isExecEnd
.
value
=
false
;
taskExecGuid
.
value
=
res
.
data
?.
lastExecGuid
;
step
.
value
=
val
-
1
;
stepsInfo
.
value
.
step
=
val
-
1
;
analysisResultInfo
.
value
=
{};
if
(
refreshTimer
.
value
)
{
clearInterval
(
refreshTimer
.
value
);
refreshTimer
.
value
=
null
;
}
processStepThreeResultView
();
oldAnonTaskValueInfo
.
value
=
saveParams
;
anonymizationStore
.
setIsAnonPageRefresh
(
true
);
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
});
}
else
{
//更新
fullscreenLoading
.
value
=
true
;
updateAnonTask
(
saveParams
).
then
((
res
:
any
)
=>
{
fullscreenLoading
.
value
=
false
;
if
(
res
.
code
==
proxy
.
$passCode
)
{
isExecEnd
.
value
=
false
;
taskExecGuid
.
value
=
res
.
data
;
step
.
value
=
val
-
1
;
stepsInfo
.
value
.
step
=
val
-
1
;
analysisResultInfo
.
value
=
{};
if
(
refreshTimer
.
value
)
{
clearInterval
(
refreshTimer
.
value
);
refreshTimer
.
value
=
null
;
}
processStepThreeResultView
();
oldAnonTaskValueInfo
.
value
=
saveParams
;
anonymizationStore
.
setIsAnonPageRefresh
(
true
);
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
})
}
}
let
saveParams
:
any
=
{
...
formRef
.
value
.
formInline
};
if
(
saveParams
.
handleType
==
'01'
)
{
// 保存并提交 TODO。需要加个 记录旧值的,用来判断新值和旧值,是否发生变化,若变化则需要调用保存接口之后,再进行下一步。
let
configInfo
=
await
anonTaskStepTwoRef
.
value
?.
getStepTwoConfigInfo
();
if
(
!
configInfo
)
{
return
;
}
let
privacy
=
configInfo
.
anonPrivacyMode
;
//值是克隆过的,可以删除
delete
privacy
.
isKaNumber
;
delete
privacy
.
isRiskThreshold
;
delete
privacy
.
isTcField
;
delete
privacy
.
isLdField
;
// 为空时为了跟原始值保持一致
privacy
.
kaNumber
=
(
privacy
.
kaNumber
&&
parseInt
(
privacy
.
kaNumber
))
??
null
;
privacy
.
riskThreshold
=
(
privacy
.
riskThreshold
&&
parseFloat
(
privacy
.
riskThreshold
))
??
null
;
privacy
.
tcFieldName
=
privacy
.
tcFieldName
??
null
;
privacy
.
tcThreshold
=
(
privacy
.
tcThreshold
&&
parseFloat
(
privacy
.
tcThreshold
))
??
null
;
privacy
.
ldFieldName
=
privacy
.
ldFieldName
??
null
;
privacy
.
ldNumber
=
(
privacy
.
ldNumber
&&
parseInt
(
privacy
.
ldNumber
))
??
null
;
Object
.
assign
(
saveParams
,
configInfo
);
exec
(
saveParams
);
}
else
{
formRef
.
value
?.
ruleFormRef
?.
validate
((
valid
,
errorItem
)
=>
{
if
(
valid
)
{
if
(
formRef
.
value
?.
formInline
?.
dataSource
==
2
&&
!
sampleTableFields
.
value
?.
length
)
{
proxy
.
$ElMessage
.
error
(
'上传文件的字段不能为空'
);
return
;
}
dataSimpleFormRef
.
value
?.
ruleFormRef
?.
validate
((
valid
)
=>
{
if
(
valid
)
{
Object
.
assign
(
saveParams
,
{
riskThreshold
:
'0.05'
});
exec
(
saveParams
);
}
});
}
else
{
var
obj
=
Object
.
keys
(
errorItem
);
formRef
.
value
.
ruleFormRef
?.
scrollToField
(
obj
[
0
])
}
})
}
}
else
if
(
val
==
4
)
{
//下一步之后,设置执行结束, 查看结果。
step
.
value
=
val
-
1
;
stepsInfo
.
value
.
step
=
val
-
1
;
}
}
const
promise
:
any
=
ref
(
null
);
const
exportResult
=
()
=>
{
promise
.
value
=
exportAnonExecData
({
taskGuid
:
route
.
query
.
guid
,
execGuid
:
route
.
query
.
execGuid
}).
then
((
res
:
any
)
=>
{
promise
.
value
=
null
;
if
(
res
&&
!
res
.
msg
)
{
download
(
res
,
(
route
.
query
.
taskName
||
oldAnonTaskValueInfo
.
value
.
taskName
)
+
'_匿名化数据.xlsx'
,
'excel'
)
}
else
{
res
?.
msg
&&
ElMessage
.
error
(
res
?.
msg
);
}
}).
catch
(()
=>
{
promise
.
value
=
null
;
})
}
/** 获取字段类型的数据字典 */
const
fieldTypeList
:
any
=
ref
([]);
/** 编辑时获取的匿名化任务的详情信息 */
const
detailInfo
:
any
=
ref
({});
/** 记录原始的值信息,防止上一步之后未修改数据时不调用接口 */
const
oldAnonTaskValueInfo
:
any
=
ref
({});
onBeforeMount
(()
=>
{
if
(
taskGuid
.
value
)
{
fullscreenLoading
.
value
=
true
;
getAnonTaskDetail
(
taskGuid
.
value
).
then
(
async
(
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
detailInfo
.
value
=
res
.
data
||
{};
taskExecGuid
.
value
=
detailInfo
.
value
.
lastExecGuid
;
oldAnonTaskValueInfo
.
value
=
{
guid
:
detailInfo
.
value
.
guid
,
taskName
:
detailInfo
.
value
.
taskName
,
dataSource
:
detailInfo
.
value
.
dataSource
,
filePath
:
detailInfo
.
value
.
filePath
&&
cloneDeep
(
detailInfo
.
value
.
filePath
),
dataSourceGuid
:
detailInfo
.
value
.
dataSourceGuid
,
tableName
:
detailInfo
.
value
.
tableName
,
samplingRate
:
detailInfo
.
value
.
samplingRate
,
// patientPopulationRate: detailInfo.value.patientPopulationRate && typeof detailInfo.value.patientPopulationRate == 'number' ? detailInfo.value.patientPopulationRate?.toFixed(9) : detailInfo.value.patientPopulationRate,
dataSharingTypeCode
:
detailInfo
.
value
.
dataSharingTypeCode
,
anonTaskRules
:
cloneDeep
(
detailInfo
.
value
.
anonTaskRules
),
anonPrivacyMode
:
{
kaNumber
:
detailInfo
.
value
.
anonPrivacyMode
?.
kaNumber
,
ldFieldName
:
detailInfo
.
value
.
anonPrivacyMode
?.
ldFieldName
,
ldNumber
:
detailInfo
.
value
.
anonPrivacyMode
?.
ldNumber
,
riskThreshold
:
detailInfo
.
value
.
anonPrivacyMode
?.
riskThreshold
,
tcFieldName
:
detailInfo
.
value
.
anonPrivacyMode
?.
tcFieldName
,
tcThreshold
:
detailInfo
.
value
.
anonPrivacyMode
?.
tcThreshold
,
}
}
setDataSelectFormItems
(
Object
.
assign
(
detailInfo
.
value
,
{
file
:
detailInfo
.
value
.
filePath
?
[
detailInfo
.
value
.
filePath
]
:
[]
}),
true
);
if
(
detailInfo
.
value
.
samplingRate
!=
null
)
{
dataSimpleFormItems
.
value
[
0
].
default
=
'Y'
;
dataSimpleFormItems
.
value
[
1
].
visible
=
true
;
dataSimpleFormItems
.
value
[
1
].
default
=
detailInfo
.
value
.
samplingRate
;
}
else
{
dataSimpleFormItems
.
value
[
0
].
default
=
'N'
;
dataSimpleFormItems
.
value
[
1
].
visible
=
false
;
}
let
dataSource
=
detailInfo
.
value
.
dataSource
;
dataSelectInfoItems
.
value
[
5
].
visible
=
dataSource
==
1
;
dataSelectInfoItems
.
value
[
6
].
visible
=
dataSource
==
1
;
dataSelectInfoItems
.
value
[
8
].
visible
=
dataSource
==
2
;
try
{
//文件解析
if
(
dataSource
==
2
)
{
let
url
=
detailInfo
.
value
.
filePath
?.
url
;
sampleTableDataLoading
.
value
=
true
;
const
refSignInfo
:
any
=
await
getDownFileSignByUrl
(
parseAndDecodeUrl
(
url
).
fileName
);
if
(
!
refSignInfo
?.
data
)
{
fullscreenLoading
.
value
=
false
;
refSignInfo
?.
msg
&&
ElMessage
.
error
(
refSignInfo
?.
msg
);
return
;
}
const
fileRes
:
any
=
await
obsDownloadRequest
(
refSignInfo
?.
data
);
sampleTableDataLoading
.
value
=
false
;
if
(
fileRes
&&
!
fileRes
.
msg
)
{
parseFileData
(
fileRes
);
}
else
{
fileRes
?.
msg
&&
ElMessage
.
error
(
fileRes
?.
msg
);
}
// 会出现从文件切换到数据库时没有数据库列表的问题。
const
res
:
any
=
await
getDatabase
({
connectStatus
:
1
});
if
(
res
?.
code
==
proxy
.
$passCode
)
{
dataSourceList
.
value
=
res
.
data
||
[];
let
item
=
dataSelectInfoItems
.
value
.
find
(
item
=>
item
.
field
==
'dataSourceGuid'
);
item
&&
(
item
.
options
=
dataSourceList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
}
else
{
const
res
:
any
=
await
getDatabase
({
connectStatus
:
1
});
if
(
res
?.
code
==
proxy
.
$passCode
)
{
dataSourceList
.
value
=
res
.
data
||
[];
let
item
=
dataSelectInfoItems
.
value
.
find
(
item
=>
item
.
field
==
'dataSourceGuid'
);
item
&&
(
item
.
options
=
dataSourceList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
currDatasourceSelect
.
value
=
dataSourceList
.
value
.
find
(
d
=>
d
.
guid
==
detailInfo
.
value
.
dataSourceGuid
);
const
tableRes
:
any
=
await
getDsTableByDs
({
pageSize
:
-
1
,
pageIndex
:
1
,
dataSourceGuid
:
detailInfo
.
value
.
dataSourceGuid
,
database
:
currDatasourceSelect
.
value
.
databaseNameEn
,
databaseType
:
currDatasourceSelect
.
value
.
databaseType
,
tableName
:
''
,
hadFlag
:
false
});
if
(
tableRes
?.
code
==
proxy
.
$passCode
)
{
dsTableList
.
value
=
tableRes
.
data
?.
records
||
[];
let
item
=
dataSelectInfoItems
.
value
.
find
(
item
=>
item
.
field
==
'tableName'
);
item
&&
(
item
.
options
=
dsTableList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
tableRes
.
msg
);
}
getDsTableFieldColumn
({
pageSize
:
50
,
pageIndex
:
1
,
dataSourceGuid
:
currDatasourceSelect
.
value
.
guid
,
database
:
currDatasourceSelect
.
value
.
databaseNameEn
,
databaseType
:
currDatasourceSelect
.
value
.
databaseType
,
tableName
:
detailInfo
.
value
.
tableName
,
}).
then
((
res
:
any
)
=>
{
if
(
res
.
code
==
proxy
.
$passCode
)
{
sampleTableFields
.
value
=
res
.
data
?.
map
(
d
=>
{
d
.
fieldDataType
=
d
.
dataType
;
d
.
enName
=
d
.
columnName
;
d
.
chName
=
d
.
columnZhName
;
return
d
;
})
||
[];
/** 判断有抽样数据,需要查询接口 */
getSampleDataByDsTable
();
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
});
}
fullscreenLoading
.
value
=
false
;
}
catch
(
error
)
{
fullscreenLoading
.
value
=
false
;
}
}
else
{
fullscreenLoading
.
value
=
false
;
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
}
else
{
getDatabase
({
connectStatus
:
1
}).
then
((
res
:
any
)
=>
{
if
(
res
.
code
==
proxy
.
$passCode
)
{
dataSourceList
.
value
=
res
.
data
||
[];
let
item
=
dataSelectInfoItems
.
value
.
find
(
item
=>
item
.
field
==
'dataSourceGuid'
);
item
&&
(
item
.
options
=
dataSourceList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
getParamsList
({
dictType
:
"数据共享类型"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
dataSharingTypeList
.
value
=
res
.
data
||
[];
let
item
=
dataSelectInfoItems
.
value
.
find
(
item
=>
item
.
field
==
'dataSharingTypeCode'
);
item
&&
(
item
.
options
=
dataSharingTypeList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getParamsList
({
dictType
:
"数据匿名化处理类型"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
handleTypeList
.
value
=
res
.
data
||
[];
let
item
=
dataSelectInfoItems
.
value
.
find
(
item
=>
item
.
field
==
'handleType'
);
item
&&
(
item
.
options
=
handleTypeList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getParamsList
({
dictType
:
"字段类型"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
fieldTypeList
.
value
=
res
.
data
||
[];
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getParentAreaPromise
.
value
=
getAreaData
({
parentId
:
null
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
parentAreaData
.
value
=
res
.
data
??
[];
return
parentAreaData
.
value
;
}
})
})
onMounted
(()
=>
{
nextTick
(()
=>
{
containerWidth
.
value
=
containerRef
.
value
?.
offsetWidth
||
0
;
})
window
.
onresize
=
()
=>
{
containerWidth
.
value
=
containerRef
.
value
?.
offsetWidth
||
0
;
}
})
const
cancelTask
=
()
=>
{
proxy
.
$openMessageBox
(
"当前页面尚未保存,确定放弃修改吗?"
,
()
=>
{
userStore
.
setTabbar
(
userStore
.
tabbar
.
filter
((
tab
:
any
)
=>
tab
.
fullPath
!==
fullPath
));
router
.
push
({
name
:
'resultProcess'
});
},
()
=>
{
proxy
.
$ElMessage
.
info
(
"已取消"
);
});
}
/** 完成任务关闭 */
const
closeTask
=
()
=>
{
userStore
.
setTabbar
(
userStore
.
tabbar
.
filter
((
tab
:
any
)
=>
tab
.
fullPath
!==
fullPath
));
router
.
push
({
name
:
'resultProcess'
});
}
const
refreshTimer
=
ref
()
/** 执行结果信息 */
const
analysisResultInfo
:
any
=
ref
({});
const
getResultPromise
:
any
=
ref
(
null
);
/** 第三步处理,定时刷新查看结果 */
const
processStepThreeResultView
=
(
isRefresh
=
false
)
=>
{
let
process
=
(
isRefresh
)
=>
{
getResultPromise
.
value
=
getAnonAnalyzeResult
(
taskExecGuid
.
value
).
then
((
res
:
any
)
=>
{
getResultPromise
.
value
=
null
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
analysisResultInfo
.
value
=
res
.
data
||
{};
if
(
analysisResultInfo
.
value
.
status
==
'R'
)
{
//正在运行中
if
(
isRefresh
)
{
proxy
.
$ElMessage
.
success
(
'刷新成功,正在执行中...'
);
}
//添加定时器。
if
(
refreshTimer
.
value
)
{
return
;
}
refreshTimer
.
value
=
setInterval
(
async
()
=>
{
process
(
false
);
},
20000
);
}
else
if
(
analysisResultInfo
.
value
.
status
==
'Y'
)
{
//去获取结果。
isExecEnd
.
value
=
true
;
refreshTimer
.
value
&&
clearInterval
(
refreshTimer
.
value
);
refreshTimer
.
value
=
null
;
analysisResultTableFields
.
value
=
res
.
data
?.
column
||
[];
pageInfo
.
value
.
curr
=
1
;
getAnalysisResultPageData
();
}
else
if
(
analysisResultInfo
.
value
.
status
==
'E'
)
{
isExecEnd
.
value
=
true
refreshTimer
.
value
&&
clearInterval
(
refreshTimer
.
value
);
refreshTimer
.
value
=
null
;
}
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
}
process
(
isRefresh
);
}
/** 随时点击刷新查看结果。 */
const
refreshQueryData
=
()
=>
{
if
(
getResultPromise
.
value
)
{
return
;
}
if
(
refreshTimer
.
value
)
{
clearInterval
(
refreshTimer
.
value
);
refreshTimer
.
value
=
null
;
}
processStepThreeResultView
(
true
);
}
/** ------------------------- 匿名化分析结果页面数据展示 ---------------- */
const
pageInfo
:
any
=
ref
({
...
commonPageConfig
,
})
const
pageChange
=
(
info
)
=>
{
pageInfo
.
value
.
curr
=
Number
(
info
.
curr
);
pageInfo
.
value
.
limit
=
Number
(
info
.
limit
);
getAnalysisResultPageData
();
}
/** 每列字段对应的列宽计算结果。 */
const
originResultTableFieldColumn
=
ref
({});
/** 结果分析中的字段表格数据 */
const
resultData
:
any
=
ref
([]);
/** 结果分析中的字段信息 */
const
analysisResultTableFields
:
any
=
ref
([]);
const
analysisResultLoading
=
ref
(
false
);
watch
(
resultData
,
(
val
:
any
[],
oldVal
)
=>
{
if
(
!
analysisResultTableFields
.
value
?.
length
)
{
originResultTableFieldColumn
.
value
=
{};
return
;
}
originResultTableFieldColumn
.
value
=
{};
analysisResultTableFields
.
value
.
forEach
((
field
,
index
)
=>
{
originResultTableFieldColumn
.
value
[
field
.
enName
]
=
calcTableColumnWidth
(
val
?.
slice
(
0
,
20
)
||
[],
field
.
enName
,
field
.
chName
,
24
);
});
},
{
deep
:
true
,
}
);
const
getAnalysisResultPageData
=
()
=>
{
analysisResultLoading
.
value
=
true
;
getAnonAnalyzePageData
({
pageIndex
:
pageInfo
.
value
.
curr
,
pageSize
:
pageInfo
.
value
.
limit
,
taskExecGuid
:
taskExecGuid
.
value
,
}).
then
((
res
:
any
)
=>
{
analysisResultLoading
.
value
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
pageInfo
.
value
.
rows
=
resultData
.
value
=
[];
res
.
data
?.
records
?.
forEach
(
d
=>
{
let
obj
=
{};
analysisResultTableFields
.
value
.
forEach
(
t
=>
{
obj
[
t
.
enName
]
=
d
.
fieldValue
?.[
t
.
enName
];
});
obj
[
'equivalenceClassNum'
]
=
changeNum
(
d
.
equivalenceClassNum
||
0
,
0
);
obj
[
'reIdentifyRisk'
]
=
changeNum
(
d
.
reIdentifyRisk
||
0
,
2
);
obj
[
'isGtThreshold'
]
=
d
.
isGtThreshold
;
resultData
.
value
.
push
(
obj
);
});
pageInfo
.
value
.
rows
=
res
.
data
?.
totalRows
??
0
;
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
const
downPromise
:
any
=
ref
()
/** 下载评估报告 */
const
transfer
=
()
=>
{
if
(
downPromise
.
value
)
{
return
;
}
downPromise
.
value
=
exportAnonReport
({
taskGuid
:
route
.
query
.
guid
,
execGuid
:
taskExecGuid
.
value
}).
then
((
res
:
any
)
=>
{
downPromise
.
value
=
null
;
if
(
res
&&
!
res
.
msg
)
{
download
(
res
,
(
route
.
query
.
taskName
||
oldAnonTaskValueInfo
.
value
.
taskName
)
+
'_匿名化评估报告.docx'
,
'word'
)
}
else
{
res
?.
msg
&&
ElMessage
.
error
(
res
?.
msg
);
}
}).
catch
(()
=>
{
downPromise
.
value
=
null
;
})
}
onUnmounted
(()
=>
{
refreshTimer
.
value
&&
clearInterval
(
refreshTimer
.
value
);
refreshTimer
.
value
=
null
;
})
</
script
>
<
style
lang=
"scss"
scoped
>
.top_tool_wrap
{
width
:
100%
;
height
:
72px
;
margin
:
8px
0
0px
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
:deep(.el-steps)
{
width
:
60%
;
}
}
.bottom_tool_wrap
{
height
:
40px
;
padding
:
0
16px
;
border-top
:
1px
solid
#d9d9d9
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
}
.content_main
{
height
:
calc
(
100%
-
40px
);
padding
:
0
16px
;
overflow
:
hidden
auto
;
}
.operator_panel_wrap
{
padding-bottom
:
12px
;
}
.wait-result-div
{
height
:
250px
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
center
;
align-items
:
center
;
.loading-img
{
width
:
40px
;
height
:
40px
;
margin-bottom
:
18px
;
}
.desc
{
color
:
#999
;
margin-bottom
:
18px
;
margin-left
:
26px
;
}
:deep
(
.el-icon.failed
)
{
color
:
#E63E33
;
width
:
32px
;
height
:
32px
;
margin-bottom
:
8px
;
svg
{
width
:
32px
;
height
:
32px
;
}
}
.error-desc
{
color
:
#E63E33
;
font-size
:
14px
;
line-height
:
21px
;
margin-bottom
:
8px
;
font-weight
:
600
;
}
}
.analysis-result-main
{
min-height
:
250px
;
.value-desc
{
font-size
:
14px
;
color
:
#212121
;
line-height
:
21px
;
}
.result-title
{
font-size
:
16px
;
color
:
#212121
;
line-height
:
24px
;
font-weight
:
600
;
margin-bottom
:
6px
;
}
.result-title-h1
{
color
:
#212121
;
font-weight
:
600
;
font-size
:
24px
;
text-align
:
center
;
line-height
:
36px
;
margin-top
:
12px
;
}
.result-title-desc
{
color
:
#666
;
font-size
:
14px
;
line-height
:
21px
;
margin-top
:
12px
;
}
.kpi-content
{
display
:
flex
;
flex-direction
:
row
;
column-gap
:
12px
;
row-gap
:
12px
;
flex-wrap
:
wrap
;
margin-bottom
:
20px
;
}
.border-content
{
height
:
76px
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
left
;
padding-left
:
16px
;
justify-content
:
center
;
border
:
1px
solid
#d9d9d9
;
width
:
calc
(
20%
-
8px
);
min-width
:
228px
;
border-radius
:
2px
;
padding-left
:
16px
;
.number
{
font-weight
:
700
;
font-size
:
20px
;
color
:
#212121
;
line-height
:
30px
;
margin-top
:
2px
;
&.score-color
{
color
:
#FF5F1F
;
}
}
.text
{
font-size
:
14px
;
line-height
:
21px
;
color
:
#666666
;
display
:
flex
;
.el-icon
{
color
:
#b2b2b2
;
}
}
}
.result-table-desc
{
font-size
:
14px
;
color
:
#999999
;
line-height
:
21px
;
}
.row-two-main
{
margin-top
:
18px
;
display
:
flex
;
.table-one
{
width
:
586px
;
&.border
{
border
:
1px
solid
#d9d9d9
;
padding
:
14px
18px
18px
;
}
}
.table-two
{
margin-left
:
20px
;
width
:
calc
(
100%
-
606px
);
&.border
{
border
:
1px
solid
#d9d9d9
;
padding
:
14px
18px
18px
;
}
}
}
}
.step-result
{
:deep(.v-content-wrap)
{
height
:
100%
;
.el-card__body
{
height
:
calc
(
100%
-
50px
)
!important
;
.card-body-content
{
height
:
100%
;
}
}
.table_tool_wrap
{
padding
:
0px
;
}
}
}
:deep
(
.custom-form
)
{
align-items
:
flex-start
;
.wid60.el-form-item
{
width
:
calc
(
66.66%
-
12px
);
}
}
:deep
(
.fixwidth-form
)
{
width
:
500px
;
.autoWidth.el-form-item
{
width
:
80px
;
}
}
.table-v2-main
{
width
:
100%
;
height
:
240px
;
margin-top
:
2px
;
.main-placeholder
{
height
:
100%
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
flex-direction
:
column
;
.empty-text
{
font-size
:
14px
;
color
:
#b2b2b2
;
}
}
}
:deep
(
.el-table-v2
)
{
.el-table-v2__main
{
border
:
1px
solid
#d9d9d9
;
background
:
#fff
;
}
.el-table-v2__body
tr
.hover-row.el-table-v2__row--striped.current-row
>
td
.el-table-v2__cell
{
background-color
:
var
(
--el-table-row-hover-bg-color
);
}
.el-table-v2__body
tr
.current-row
>
td
.el-table-v2__cell
{
background-color
:
var
(
--el-table-current-row-bg-color
);
}
.el-table-v2__header
{
width
:
100%
!important
;
}
.el-table-v2__header-cell
,
.el-table-v2__row-cell
{
border-right
:
1px
#d9d9d9
solid
;
}
.el-table-v2__header-cell-text
{
color
:
#000
;
font-weight
:
normal
;
}
.el-table-v2__empty
{
display
:
none
!important
;
}
.el-table-v2__header-row
{
border-bottom
:
1px
solid
#d9d9d9
;
}
}
.empty-content
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
height
:
316px
;
width
:
100%
;
flex-direction
:
column
;
.empty-text
{
font-size
:
14px
;
color
:
#b2b2b2
;
}
}
:deep
(
.el-form
)
{
.checkbox-cascader
{
display
:
flex
;
.el-cascader
{
margin-left
:
8px
;
}
}
.checkbox-right
{
width
:
calc
(
100%
-
50px
);
&.el-form-item
{
margin-bottom
:
0px
;
margin-right
:
0px
;
width
:
100%
;
}
}
}
:deep
(
.cell-tooltip-bg
)
{
background-color
:
#fff1d4
!important
;
}
:deep
(
.anlysis-content-wrap
)
{
.card-title
{
justify-content
:
space-between
;
}
}
</
style
>
\ No newline at end of file
src/views/data_anonymization/anonTaskStepTwo.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: anonTaskStepTwo
</route>
<
script
lang=
"ts"
setup
name=
"anonTaskStepTwo"
>
import
{
TableColumnWidth
}
from
'@/utils/enum'
;
import
{
CirclePlus
,
Delete
}
from
'@element-plus/icons-vue'
;
import
{
getParamsList
,
getGeneralizeFileNameList
,
getLableByFieldName
,
validateAnonRule
,
}
from
'@/api/modules/dataAnonymization'
;
import
{
useValidator
}
from
'@/hooks/useValidator'
;
import
{
cloneDeep
}
from
'lodash-es'
;
const
props
=
defineProps
({
fieldTypeList
:
{
default
:
[],
type
:
Array
<
any
>
},
isFile
:
{
default
:
false
,
type
:
Boolean
},
fieldNameList
:
{
default
:
[],
type
:
Array
<
any
>
},
anonTaskRules
:
{
default
:
[],
type
:
Array
<
any
>
},
anonPrivacyMode
:
{
default
:
{},
type
:
Object
}
})
const
{
required
}
=
useValidator
();
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
drawerRef
=
ref
();
/** 泛化文件列表 */
const
generalizeFileNameList
:
any
=
ref
([]);
/** 脱敏规则字典列表 */
const
desensitiveRuleTypeList
:
any
=
ref
([]);
/** 标签类型字典列表 */
const
labelTypeList
:
any
=
ref
([]);
/** 加密算法字典列表 */
const
hashMethodList
=
ref
([]);
/** 当前正在编辑的表格索引 */
const
currTableRowIndex
:
any
=
ref
(
null
);
const
ruleModelTableInfo
=
ref
({
id
:
'rule-model-table'
,
loading
:
false
,
minHeight
:
'150px'
,
nodeKey
:
'guid'
,
height
:
'200px'
,
fields
:
[
{
label
:
"序号"
,
type
:
"index"
,
width
:
TableColumnWidth
.
INDEX
,
align
:
"center"
},
{
label
:
"字段中文名称"
,
field
:
"fieldChName"
,
width
:
150
},
{
label
:
"字段英文名称"
,
field
:
"fieldName"
,
width
:
150
},
{
label
:
"字段类型"
,
field
:
"fieldTypeName"
,
width
:
120
},
{
label
:
"数据类型"
,
field
:
"dataTypeName"
,
width
:
120
},
{
label
:
"脱敏方式"
,
field
:
"desensitiveRule"
,
width
:
120
,
getName
:
(
scope
)
=>
{
let
rule
=
scope
.
row
.
desensitiveRule
;
return
rule
?
rule
:
(
scope
.
row
.
generalizeFileGuid
?
'泛化'
:
'--'
);
}
},
],
data
:
<
any
>
[],
showPage
:
false
,
actionInfo
:
{
label
:
"操作"
,
type
:
"btn"
,
width
:
100
,
fixed
:
'right'
,
btns
:
[
{
label
:
"编辑"
,
value
:
"ruleEdit"
,
click
:
(
scope
)
=>
{
currTableRowIndex
.
value
=
scope
.
$index
;
drawerInfo
.
value
.
visible
=
true
;
drawerInfo
.
value
.
type
=
'edit'
;
drawerInfo
.
value
.
header
.
title
=
'编辑字段脱敏规则'
;
let
row
=
scope
.
row
;
fieldRulesFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
row
[
item
.
field
];
if
(
item
.
field
==
'encryptionAlgorithmCode'
||
item
.
field
==
'salted'
)
{
item
.
visible
=
row
.
desensitiveRuleCode
==
'HASH'
;
}
else
if
(
item
.
field
==
'decimalPlaces'
)
{
item
.
visible
=
row
.
desensitiveRuleCode
==
'ROUNDING'
;
}
});
let
fieldNameList
=
props
.
fieldNameList
.
map
(
f
=>
{
if
(
f
.
enName
!=
row
.
fieldName
&&
ruleModelTableInfo
.
value
.
data
.
some
(
d
=>
d
.
fieldName
==
f
.
enName
))
{
f
.
disabled
=
true
;
}
else
{
f
.
disabled
=
false
;
}
return
f
;
});
fieldRulesFormItems
.
value
[
0
].
options
=
fieldNameList
;
fieldRulesFormInfo
.
value
.
formInfo
.
items
=
fieldRulesFormItems
.
value
;
drawerInfo
.
value
.
container
.
contents
[
0
]
=
fieldRulesFormInfo
.
value
;
desensitiveRuleDetail
.
value
=
{
dissembleType
:
1
,
/** 1从左往右,2.从右往左 */
ruleDetails
:
<
any
>
[{
digitType
:
1
,
}]
};
charReplaceRuleDetail
.
value
=
{
replaceType
:
1
,
/** 1从左往右,2.从右往左 */
ruleDetails
:
<
any
>
[{
digitType
:
1
,
ruleType
:
1
}]
};
rangeReplaceRuleDetails
.
value
=
[{
lowOperator
:
'≤'
,
fieldChName
:
row
.
fieldChName
,
upperOperator
:
'≤'
}];
if
(
row
.
desensitiveRuleCode
==
'DISSEMBLE'
)
{
desensitiveRuleDetail
.
value
=
row
.
desensitiveRuleDetail
||
{};
}
else
if
(
row
.
desensitiveRuleCode
==
'CHARREPLACE'
)
{
charReplaceRuleDetail
.
value
=
row
.
desensitiveRuleDetail
||
{};
}
else
if
(
row
.
desensitiveRuleCode
==
'RANGEREPLACE'
)
{
rangeReplaceRuleDetails
.
value
=
row
.
desensitiveRuleDetail
?.
ruleDetails
?.
map
(
rd
=>
{
rd
.
fieldChName
=
row
.
fieldChName
;
return
rd
;
})
||
[];
}
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
0
].
default
=
''
;
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
1
].
default
=
''
;
if
(
!
row
.
desensitiveRuleCode
)
{
drawerInfo
.
value
.
container
.
contents
=
[
fieldRulesFormInfo
.
value
];
}
else
{
drawerInfo
.
value
.
container
.
contents
=
[
fieldRulesFormInfo
.
value
,
fieldRulesEndFormInfo
.
value
];
}
}
},
{
label
:
"删除"
,
value
:
"delete"
,
click
:
(
scope
)
=>
{
proxy
.
$openMessageBox
(
"此操作将永久删除, 是否继续?"
,
()
=>
{
let
fieldName
=
scope
.
row
.
fieldName
;
ruleModelTableInfo
.
value
.
data
.
splice
(
scope
.
$index
,
1
);
// updatePrivacyFormFieldsOptions(fieldName);
// 同步去掉隐私模型设置中的字段。
proxy
.
$ElMessage
.
success
(
'删除成功'
);
},
()
=>
{
proxy
.
$ElMessage
.
info
(
"已取消"
);
})
}
},
],
}
})
/** 字段脱敏规则表单配置 */
const
fieldRulesFormItems
=
ref
([{
label
:
'选择字段'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'fieldName'
,
default
:
''
,
options
:
props
.
fieldNameList
,
props
:
{
label
:
'chName'
,
value
:
'enName'
,
disabled
:
'disabled'
},
filterable
:
true
,
clearable
:
false
,
required
:
true
},
{
label
:
'字段类型'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'fieldTypeCode'
,
default
:
'varchar'
,
options
:
props
.
fieldTypeList
,
props
:
{
label
:
'label'
,
value
:
'value'
},
disabled
:
true
,
clearable
:
true
,
required
:
true
},
{
label
:
'数据类型'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'dataTypeCode'
,
default
:
''
,
options
:
labelTypeList
.
value
,
props
:
{
label
:
'label'
,
value
:
'value'
},
filterable
:
true
,
clearable
:
true
,
required
:
false
},
{
label
:
'K匿名泛化'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'generalizeFileGuid'
,
default
:
''
,
options
:
generalizeFileNameList
.
value
,
props
:
{
label
:
'generalizeFileName'
,
value
:
'guid'
},
filterable
:
true
,
clearable
:
true
,
required
:
false
},
{
label
:
'脱敏规则'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'desensitiveRuleCode'
,
default
:
'DISSEMBLE'
,
options
:
desensitiveRuleTypeList
.
value
,
props
:
{
label
:
'label'
,
value
:
'value'
},
filterable
:
true
,
clearable
:
true
,
required
:
false
},
{
label
:
'加密算法'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'encryptionAlgorithmCode'
,
default
:
''
,
options
:
hashMethodList
.
value
,
props
:
{
label
:
'label'
,
value
:
'value'
},
filterable
:
true
,
clearable
:
true
,
visible
:
false
,
required
:
true
},
{
label
:
'加盐值'
,
type
:
'input'
,
placeholder
:
'请输入0~9'
,
field
:
'salted'
,
maxlength
:
1
,
min
:
0
,
max
:
9
,
inputType
:
'integerNumber'
,
default
:
5
,
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
false
,
},
{
label
:
'保留小数'
,
type
:
'input'
,
placeholder
:
'请输入0~5'
,
field
:
'decimalPlaces'
,
maxlength
:
1
,
min
:
0
,
max
:
5
,
inputType
:
'integerNumber'
,
default
:
2
,
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
false
,
}]);
const
fieldRulesFormRules
=
ref
({
fieldName
:
[
required
(
'请选择字段'
)],
dataTypeCode
:
[
required
(
'请选择数据类型'
)],
//不填标识非敏感标识。
// desensitiveRuleCode: [required('请选择脱敏规则')], 脱敏规则和泛化文件选择一个即可。二者必选其一,可以两者共存。
encryptionAlgorithmCode
:
[
required
(
'请选择加密算法'
)],
salted
:
[
required
(
'请输入加盐值'
)],
decimalPlaces
:
[
required
(
'请输入保留小数'
)]
});
const
fieldRulesFormInfo
=
ref
({
type
:
"form"
,
title
:
""
,
col
:
"span"
,
formInfo
:
{
id
:
"add-class-form"
,
readonly
:
false
,
items
:
fieldRulesFormItems
.
value
,
rules
:
fieldRulesFormRules
.
value
,
},
});
const
fieldRulesEndFormInfo
=
ref
({
type
:
"form"
,
title
:
""
,
col
:
"mt8"
,
showSlot
:
false
,
formInfo
:
{
id
:
"add-rules-end-form"
,
readonly
:
false
,
items
:
[{
label
:
'样本数据'
,
type
:
'input'
,
placeholder
:
'请输入样本'
,
field
:
'testData'
,
default
:
''
,
required
:
false
,
clearable
:
true
,
block
:
true
,
col
:
'mb8'
,
validateBtn
:
{
value
:
'validate'
,
label
:
'验证'
,
click
:
()
=>
{
let
formInline
=
drawerRef
.
value
?.
getDrawerConRef
(
'drawerFormRef'
)?.
formInline
;
let
formInline1
=
drawerRef
.
value
?.
getDrawerConRef
(
'drawerFormRef'
,
1
)?.
formInline
;
if
(
!
formInline1
.
testData
)
{
proxy
.
$ElMessage
.
error
(
'样本数据不能为空'
);
return
;
}
validateAnonRule
({
desensitiveRuleCode
:
formInline
.
desensitiveRuleCode
,
value
:
formInline1
.
testData
,
desensitiveRuleDetail
:
getDesensitiveRuleDetailInfo
(
formInline
)
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
let
result
=
res
.
data
;
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
0
].
default
=
formInline1
.
testData
;
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
1
].
default
=
formInline
.
desensitiveRuleCode
==
'BLANK'
?
' '
:
result
;
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
}
},
{
label
:
'脱敏效果'
,
type
:
'input'
,
placeholder
:
'点击验证后展示脱敏效果'
,
field
:
'validateResult'
,
default
:
''
,
block
:
true
,
required
:
false
,
disabled
:
true
}],
rules
:
{},
},
});
/** 获取规则配置详情。 */
const
getDesensitiveRuleDetailInfo
=
(
formInline
)
=>
{
let
desensitiveRuleDetailInfo
:
any
=
{};
if
(
formInline
.
desensitiveRuleCode
==
'DISSEMBLE'
)
{
desensitiveRuleDetailInfo
=
desensitiveRuleDetail
.
value
;
}
else
if
(
formInline
.
desensitiveRuleCode
==
'CHARREPLACE'
)
{
desensitiveRuleDetailInfo
=
charReplaceRuleDetail
.
value
;
}
else
if
(
formInline
.
desensitiveRuleCode
==
'RANGEREPLACE'
)
{
desensitiveRuleDetailInfo
=
{
ruleDetails
:
rangeReplaceRuleDetails
.
value
};
}
else
if
(
formInline
.
desensitiveRuleCode
==
'ROUNDING'
)
{
desensitiveRuleDetailInfo
=
{
decimalPlaces
:
formInline
.
decimalPlaces
}
}
else
if
(
formInline
.
desensitiveRuleCode
==
'HASH'
)
{
desensitiveRuleDetailInfo
=
{
encryptionAlgorithmCode
:
formInline
.
encryptionAlgorithmCode
,
salted
:
formInline
.
salted
}
}
return
desensitiveRuleDetailInfo
;
}
const
drawerInfo
=
ref
({
visible
:
false
,
direction
:
'rtl'
,
size
:
540
,
header
:
{
title
:
'添加字段脱敏规则'
,
},
type
:
''
,
container
:
{
contents
:
[
fieldRulesFormInfo
.
value
,
fieldRulesEndFormInfo
.
value
],
},
footer
:
{
visible
:
true
,
btns
:
[
{
type
:
'default'
,
label
:
'取消'
,
value
:
'cancel'
},
{
type
:
'primary'
,
label
:
'确定'
,
value
:
'save'
,
loading
:
false
},
]
}
});
const
addRowRules
=
()
=>
{
drawerInfo
.
value
.
visible
=
true
;
drawerInfo
.
value
.
type
=
'add'
;
drawerInfo
.
value
.
header
.
title
=
'添加字段脱敏规则'
;
fieldRulesFormItems
.
value
.
forEach
(
item
=>
{
if
(
item
.
field
==
'fieldTypeCode'
)
{
item
.
default
=
'varchar'
;
}
else
if
(
item
.
field
==
'desensitiveRuleCode'
)
{
item
.
default
=
'DISSEMBLE'
;
}
else
if
(
item
.
field
==
'encryptionAlgorithmCode'
||
item
.
field
==
'salted'
)
{
item
.
visible
=
false
;
item
.
default
=
''
;
item
.
field
==
'salted'
&&
(
item
.
default
=
5
);
}
else
if
(
item
.
field
==
'decimalPlaces'
)
{
item
.
visible
=
false
;
item
.
default
=
2
;
}
else
{
item
.
default
=
''
;
}
});
let
fieldNameList
=
props
.
fieldNameList
.
map
(
f
=>
{
if
(
ruleModelTableInfo
.
value
.
data
.
some
(
d
=>
d
.
fieldName
==
f
.
enName
))
{
f
.
disabled
=
true
;
}
else
{
f
.
disabled
=
false
;
}
return
f
});
fieldRulesFormItems
.
value
[
0
].
options
=
fieldNameList
;
fieldRulesFormInfo
.
value
.
formInfo
.
items
=
fieldRulesFormItems
.
value
;
desensitiveRuleDetail
.
value
=
{
dissembleType
:
1
,
/** 1从左往右,2.从右往左 */
ruleDetails
:
<
any
>
[{
digitType
:
1
,
}]
};
charReplaceRuleDetail
.
value
=
{
replaceType
:
1
,
/** 1从左往右,2.从右往左 */
ruleDetails
:
<
any
>
[{
digitType
:
1
,
ruleType
:
1
}]
};
rangeReplaceRuleDetails
.
value
=
[{
lowOperator
:
'≤'
,
fieldChName
:
''
,
upperOperator
:
'≤'
}];
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
0
].
default
=
''
;
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
1
].
default
=
''
;
drawerInfo
.
value
.
container
.
contents
=
[
fieldRulesFormInfo
.
value
,
fieldRulesEndFormInfo
.
value
];
}
const
drawerBtnClick
=
async
(
btn
,
info
)
=>
{
if
(
btn
.
value
==
'cancel'
)
{
drawerInfo
.
value
.
visible
=
false
}
else
{
if
(
!
info
.
generalizeFileGuid
&&
!
info
.
desensitiveRuleCode
)
{
proxy
.
$ElMessage
.
error
(
'K匿名泛化与脱敏规则不能同时为空'
);
return
;
}
if
(
!
(
info
.
fieldTypeCode
==
'int'
||
info
.
fieldTypeCode
==
'decimal'
||
info
.
fieldTypeCode
==
'tinyint'
))
{
if
(
info
.
desensitiveRuleCode
==
'ROUNDING'
)
{
proxy
.
$ElMessage
.
error
(
'非数值类型字段的脱敏规则不能设置取整'
);
return
;
}
if
(
info
.
desensitiveRuleCode
==
'RANGEREPLACE'
)
{
proxy
.
$ElMessage
.
error
(
'非数值类型字段的脱敏规则不能设置区间替换'
);
return
;
}
}
drawerInfo
.
value
.
footer
.
btns
[
1
].
loading
=
true
;
let
desensitiveRuleDetailInfo
=
getDesensitiveRuleDetailInfo
(
info
);
// 脱敏规则为掩盖,字符,区间替换存在时需要调用接口检验
if
(
info
.
desensitiveRuleCode
==
'DISSEMBLE'
||
info
.
desensitiveRuleCode
==
'CHARREPLACE'
||
info
.
desensitiveRuleCode
==
'RANGEREPLACE'
)
{
try
{
let
res
:
any
=
await
validateAnonRule
({
desensitiveRuleCode
:
info
.
desensitiveRuleCode
,
value
:
''
,
desensitiveRuleDetail
:
desensitiveRuleDetailInfo
})
if
(
res
?.
code
!=
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
drawerInfo
.
value
.
footer
.
btns
[
1
].
loading
=
false
;
return
;
}
}
catch
(
error
)
{
drawerInfo
.
value
.
footer
.
btns
[
1
].
loading
=
false
;
}
}
drawerInfo
.
value
.
footer
.
btns
[
1
].
loading
=
false
;
let
saveData
:
any
=
{
...
info
};
saveData
.
desensitiveRuleDetail
=
desensitiveRuleDetailInfo
;
saveData
.
fieldChName
=
props
.
fieldNameList
?.
find
(
f
=>
f
.
enName
==
saveData
.
fieldName
)?.
chName
;
saveData
.
dataTypeName
=
saveData
.
dataTypeCode
&&
labelTypeList
.
value
.
find
(
l
=>
l
.
value
==
saveData
.
dataTypeCode
)?.
label
;
saveData
.
fieldTypeName
=
saveData
.
fieldTypeCode
&&
props
.
fieldTypeList
?.
find
(
f
=>
f
.
value
==
saveData
.
fieldTypeCode
)?.
label
;
saveData
.
desensitiveRule
=
info
.
desensitiveRuleCode
&&
desensitiveRuleTypeList
.
value
.
find
(
d
=>
d
.
value
==
info
.
desensitiveRuleCode
)?.
label
;
let
changeFields
=
''
;
if
(
drawerInfo
.
value
.
type
==
'add'
)
{
ruleModelTableInfo
.
value
.
data
.
push
(
saveData
);
}
else
if
(
drawerInfo
.
value
.
type
==
'edit'
)
{
if
(
!
currTableRowIndex
.
value
!=
null
)
{
let
originFieldName
=
ruleModelTableInfo
.
value
.
data
[
currTableRowIndex
.
value
]?.
fieldName
;
changeFields
=
originFieldName
==
saveData
.
fieldName
?
''
:
originFieldName
;
ruleModelTableInfo
.
value
.
data
[
currTableRowIndex
.
value
]
=
saveData
;
}
}
drawerInfo
.
value
.
visible
=
false
;
// updatePrivacyFormFieldsOptions(changeFields);
}
}
/** 更新下拉选择字段的下拉列表,若是字段被删除,需要同步去掉下拉选择值。 */
const
updatePrivacyFormFieldsOptions
=
(
changeFields
)
=>
{
privacyFormItems
.
value
[
2
].
children
[
0
].
options
=
props
.
fieldNameList
;
privacyFormItems
.
value
[
3
].
children
[
0
].
options
=
props
.
fieldNameList
;
let
formInline
=
privacyFormRef
.
value
?.
formInline
;
privacyFormItems
.
value
.
forEach
((
item
,
index
)
=>
{
item
.
default
=
formInline
[
item
.
field
];
if
(
item
.
default
==
'Y'
)
{
item
.
children
?.
forEach
(
child
=>
{
child
.
default
=
formInline
[
child
.
field
];
if
(
changeFields
&&
(
child
.
field
==
'tcFieldName'
||
child
.
field
==
'ldFieldName'
)
&&
child
.
default
==
changeFields
)
{
child
.
default
=
''
}
});
}
})
}
/** 当前选择的字段对应的标签信息。 */
const
currLabelInfo
:
any
=
ref
({});
const
drawerSelectChange
=
(
val
,
row
,
info
)
=>
{
if
(
row
.
field
===
'desensitiveRuleCode'
)
{
let
methodItem
=
fieldRulesFormItems
.
value
.
find
(
item
=>
item
.
field
==
'encryptionAlgorithmCode'
);
let
saltedItem
=
fieldRulesFormItems
.
value
.
find
(
item
=>
item
.
field
==
'salted'
);
let
decimalPlaceItem
=
fieldRulesFormItems
.
value
.
find
(
item
=>
item
.
field
==
'decimalPlaces'
);
methodItem
&&
(
methodItem
.
visible
=
val
==
'HASH'
);
saltedItem
&&
(
saltedItem
.
visible
=
val
==
'HASH'
);
decimalPlaceItem
&&
(
decimalPlaceItem
.
visible
=
val
==
'ROUNDING'
);
fieldRulesFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
info
[
item
.
field
];
if
(
item
.
field
==
'decimalPlaces'
)
{
if
(
item
.
default
==
null
)
{
item
.
default
=
2
;
}
}
else
if
(
item
.
field
==
'salted'
)
{
if
(
item
.
default
==
null
)
{
item
.
default
=
5
;
}
}
});
if
(
val
==
'RANGEREPLACE'
)
{
let
fieldName
=
info
.
fieldName
;
let
fieldChName
=
fieldName
&&
props
.
fieldNameList
.
find
(
f
=>
f
.
enName
==
fieldName
)?.
chName
;
rangeReplaceRuleDetails
.
value
.
forEach
(
r
=>
{
if
(
fieldChName
&&
r
.
fieldChName
!=
fieldChName
)
{
r
.
fieldChName
=
fieldChName
;
}
});
}
if
(
!
val
)
{
drawerInfo
.
value
.
container
.
contents
=
[
fieldRulesFormInfo
.
value
];
}
else
{
drawerInfo
.
value
.
container
.
contents
=
[
fieldRulesFormInfo
.
value
,
fieldRulesEndFormInfo
.
value
];
}
}
// else if (row.field == 'fieldTypeCode') {
// fieldRulesFormItems.value.forEach(item => {
// item.default = info[item.field];
// if (item.field == 'desensitiveRuleCode') {
// if (!(val == 'int' || val == 'decimal' || val == 'tinyint')) {
// item.options = desensitiveRuleTypeList.value.filter(d => d.value != 'ROUNDING' && d.value != 'RANGEREPLACE');
// if (item.default == 'ROUNDING' || item.default == 'RANGEREPLACE') {
// item.default = 'DISSEMBLE';
// }
// } else {
// item.options = desensitiveRuleTypeList.value;
// }
// }
// });
// }
else
if
(
row
.
field
==
'fieldName'
)
{
//选择字段改变之后,调用接口。
let
tableField
=
props
.
fieldNameList
.
find
(
f
=>
f
.
enName
==
val
);
let
dataType
=
tableField
?.
dataType
||
'varchar'
;
fieldRulesFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
info
[
item
.
field
];
if
(
item
.
field
==
'fieldTypeCode'
)
{
item
.
default
=
dataType
;
}
});
getLableByFieldName
(
val
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
let
labelInfo
=
currLabelInfo
.
value
=
res
.
data
||
{};
fieldRulesFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
info
[
item
.
field
];
if
(
item
.
field
==
'dataTypeCode'
)
{
item
.
default
=
labelInfo
.
labelTypeCode
;
}
else
if
(
item
.
field
==
'fieldTypeCode'
)
{
item
.
default
=
dataType
;
}
});
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
if
(
info
.
desensitiveRuleCode
==
'RANGEREPLACE'
)
{
rangeReplaceRuleDetails
.
value
.
forEach
(
r
=>
{
r
.
fieldChName
=
tableField
?.
chName
||
val
;
});
}
}
}
const
drawerInputChange
=
(
val
,
row
,
info
)
=>
{
if
(
row
.
field
==
'testData'
)
{
//样本数据修改后,清空脱敏结果
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
0
].
default
=
val
;
fieldRulesEndFormInfo
.
value
.
formInfo
.
items
[
1
].
default
=
''
;
}
}
/** 掩盖规则中的位数和剩余位数 */
const
digitTypeList
=
ref
([{
value
:
1
,
label
:
'位数'
},
{
value
:
2
,
label
:
'剩余位数'
}]);
const
dissembleRuleTypeList
=
ref
([{
value
:
1
,
label
:
'脱敏'
},
{
value
:
2
,
label
:
'不脱敏'
}]);
/** 掩盖类型的脱敏规则 分段规则配置 */
const
desensitiveRuleDetail
=
ref
({
dissembleType
:
1
,
/** 1从左往右,2.从右往左 */
ruleDetails
:
<
any
>
[{
digitType
:
1
,
}]
});
const
addSegmentRule
=
()
=>
{
desensitiveRuleDetail
.
value
.
ruleDetails
.
push
({
digitType
:
1
,
});
};
const
deleteSegmentRule
=
(
item
,
index
)
=>
{
desensitiveRuleDetail
.
value
.
ruleDetails
.
splice
(
index
,
1
);
}
const
inputEventDigitChange
=
(
val
,
item
,
prop
=
'digit'
)
=>
{
item
[
prop
]
=
item
[
prop
].
toString
().
replace
(
/
\.
/g
,
""
)
item
[
prop
]
=
item
[
prop
].
toString
().
replace
(
/^
\D
*
(\d{0,7}(?:\.\d{0})?)
.*$/g
,
"$1"
)
if
(
prop
==
'digit'
&&
val
!=
""
&&
item
.
digit
<
1
)
{
item
.
digit
=
1
;
}
}
/** 字符规则中的替换方式 */
const
charReplaceRuleTypeList
=
ref
([{
value
:
1
,
label
:
'随机替换'
},
{
value
:
2
,
label
:
'固定值替换'
}]);
/** 字符替换的脱敏规则 分段规则配置 */
const
charReplaceRuleDetail
=
ref
({
replaceType
:
1
,
/** 1从左往右,2.从右往左 */
ruleDetails
:
<
any
>
[{
digitType
:
1
,
ruleType
:
1
}]
});
const
addCharReplaceSegmentRule
=
()
=>
{
charReplaceRuleDetail
.
value
.
ruleDetails
.
push
({
digitType
:
1
,
ruleType
:
1
});
};
const
deleteCharReplaceSegmentRule
=
(
item
,
index
)
=>
{
charReplaceRuleDetail
.
value
.
ruleDetails
.
splice
(
index
,
1
);
}
/** ----- 区间替换规则 --- */
/** 区间替换的小于或小于等于字符列表 */
const
lowerOperatorList
:
any
=
ref
([{
value
:
'≤'
,
label
:
'≤'
},
{
value
:
'<'
,
label
:
'<'
}]);
/**
* 字符替换的脱敏规则 分段规则配置。
* { lowValue: , lowOperator: , upperValue: , upperOperator: , replaceValue: '' }
*/
const
rangeReplaceRuleDetails
:
any
=
ref
([{
lowOperator
:
'≤'
,
fieldChName
:
''
,
upperOperator
:
'≤'
}]);
const
addRangeReplaceSegmentRule
=
()
=>
{
let
fieldChName
=
''
;
if
(
rangeReplaceRuleDetails
.
value
?.
length
)
{
fieldChName
=
rangeReplaceRuleDetails
.
value
[
0
].
fieldChName
;
}
else
{
let
fieldName
=
drawerRef
.
value
?.
getDrawerConRef
(
'drawerFormRef'
)?.
formInline
?.
fieldName
;
let
tableField
=
fieldName
&&
props
.
fieldNameList
?.
find
(
f
=>
f
.
enName
==
fieldName
);
fieldChName
=
tableField
?.
chName
;
}
rangeReplaceRuleDetails
.
value
.
push
({
lowOperator
:
'≤'
,
fieldChName
:
fieldChName
,
upperOperator
:
'≤'
});
};
const
deleteRangeReplaceSegmentRule
=
(
item
,
index
)
=>
{
rangeReplaceRuleDetails
.
value
.
splice
(
index
,
1
);
}
/** 隐私模型设置 */
const
privacyFormItems
:
any
=
ref
([{
label
:
'选择准标识符等价类隐私模型'
,
type
:
'checkbox-input-item'
,
field
:
'isKaNumber'
,
default
:
'N'
,
placeholder
:
'K匿名'
,
trueValue
:
'Y'
,
falseValue
:
'N'
,
children
:
[
{
label
:
''
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'kaNumber'
,
inputType
:
'integerNumber'
,
default
:
''
,
min
:
1
,
maxlength
:
6
,
disabled
:
false
,
clearable
:
true
,
visible
:
false
,
col
:
'ka-checkbox-input'
,
style
:
{
width
:
'100px'
,
margin
:
0
}
}
],
required
:
true
,
col
:
'checkbox-input'
},
{
label
:
'设置重标识可接受风险阈值'
,
type
:
'checkbox-input-item'
,
field
:
'isRiskThreshold'
,
default
:
'N'
,
placeholder
:
'阈值'
,
trueValue
:
'Y'
,
falseValue
:
'N'
,
children
:
[
{
label
:
''
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'riskThreshold'
,
default
:
''
,
maxlength
:
6
,
min
:
0
,
max
:
1
,
inputType
:
'scoreNumber'
,
disabled
:
false
,
clearable
:
true
,
visible
:
false
,
col
:
'ka-checkbox-input'
,
style
:
{
width
:
'100px'
,
margin
:
0
}
}
],
required
:
true
,
col
:
'checkbox-input'
},
{
label
:
'设置L多样性及T接近'
,
type
:
'checkbox-input-item'
,
field
:
'isLdField'
,
default
:
'N'
,
placeholder
:
'L多样性'
,
trueValue
:
'Y'
,
falseValue
:
'N'
,
children
:
[
{
label
:
''
,
type
:
'select'
,
placeholder
:
'请选择字段'
,
field
:
'ldFieldName'
,
options
:
props
.
fieldNameList
||
[],
props
:
{
label
:
'chName'
,
value
:
'enName'
,
disabled
:
false
},
default
:
''
,
filterable
:
true
,
clearable
:
true
,
visible
:
false
,
col
:
'ka-checkbox-input'
,
style
:
{
width
:
'120px'
,
margin
:
0
}
},
{
label
:
''
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'ldNumber'
,
inputType
:
'integerNumber'
,
default
:
''
,
maxlength
:
6
,
min
:
1
,
disabled
:
false
,
clearable
:
true
,
visible
:
false
,
col
:
'ka-checkbox-input'
,
style
:
{
width
:
'100px'
,
margin
:
0
}
}
],
block
:
false
,
required
:
true
,
col
:
'checkbox-input'
},
{
label
:
''
,
type
:
'checkbox-input-item'
,
field
:
'isTcField'
,
default
:
'N'
,
placeholder
:
'T接近'
,
trueValue
:
'Y'
,
falseValue
:
'N'
,
style
:
{
'margin-top'
:
'2px'
},
children
:
[
{
label
:
''
,
type
:
'select'
,
placeholder
:
'请选择字段'
,
field
:
'tcFieldName'
,
default
:
''
,
options
:
props
.
fieldNameList
||
[],
props
:
{
label
:
'chName'
,
value
:
'enName'
,
disabled
:
false
},
filterable
:
true
,
clearable
:
true
,
visible
:
false
,
col
:
'ka-checkbox-input'
,
style
:
{
width
:
'120px'
,
margin
:
0
}
},
{
label
:
''
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'tcThreshold'
,
default
:
''
,
maxlength
:
6
,
min
:
0
,
max
:
1
,
inputType
:
'scoreNumber'
,
disabled
:
false
,
clearable
:
true
,
visible
:
false
,
col
:
'ka-checkbox-input'
,
style
:
{
width
:
'100px'
,
margin
:
0
}
}
],
required
:
false
,
block
:
true
,
col
:
'checkbox-input lmt12'
}]);
const
privacyFormRules
=
ref
({
kaNumber
:
[
required
(
'请输入K匿名值'
)],
riskThreshold
:
[
required
(
'请输入阈值'
)],
ldFieldName
:
[
required
(
'请选择L多样性字段'
)],
ldNumber
:
[
required
(
'请输入L多样性值'
)],
tcFieldName
:
[
required
(
'请选择T接近字段'
)],
tcThreshold
:
[
required
(
'请输入T接近阈值'
)]
});
/** 记录下旧的隐私模型设置 */
const
oldPrivacyModelValue
:
any
=
ref
({
isKaNumber
:
'N'
,
isRiskThreshold
:
'N'
,
isLdField
:
'N'
,
isTcField
:
'N'
});
const
handleCheckboxChange
=
(
val
,
value
,
row
)
=>
{
oldPrivacyModelValue
.
value
=
Object
.
assign
({},
oldPrivacyModelValue
.
value
,
value
);
privacyFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
oldPrivacyModelValue
.
value
[
item
.
field
];
item
.
children
?.
forEach
(
child
=>
{
child
.
default
=
oldPrivacyModelValue
.
value
[
child
.
field
];
});
})
if
(
row
.
field
==
'isKaNumber'
)
{
let
kaItem
=
privacyFormItems
.
value
[
0
]?.
children
?.[
0
];
kaItem
&&
(
kaItem
.
visible
=
val
==
'Y'
);
}
else
if
(
row
.
field
==
'isRiskThreshold'
)
{
let
riskItem
=
privacyFormItems
.
value
[
1
]?.
children
?.[
0
];
riskItem
&&
(
riskItem
.
visible
=
val
==
'Y'
);
}
else
if
(
row
.
field
==
'isLdField'
)
{
let
childrenItem
=
privacyFormItems
.
value
[
2
]?.
children
;
childrenItem
?.[
0
]
&&
(
childrenItem
[
0
].
visible
=
val
==
"Y"
);
childrenItem
?.[
1
]
&&
(
childrenItem
[
1
].
visible
=
val
==
"Y"
);
}
else
if
(
row
.
field
==
'isTcField'
)
{
let
childrenItem
=
privacyFormItems
.
value
[
3
]?.
children
;
childrenItem
?.[
0
]
&&
(
childrenItem
[
0
].
visible
=
val
==
"Y"
);
childrenItem
?.[
1
]
&&
(
childrenItem
[
1
].
visible
=
val
==
"Y"
);
}
}
watch
(()
=>
props
.
fieldNameList
,
(
val
)
=>
{
fieldRulesFormItems
.
value
[
0
].
options
=
val
||
[];
if
(
props
.
isFile
)
{
fieldRulesFormItems
.
value
[
1
].
disabled
=
false
}
else
{
fieldRulesFormItems
.
value
[
1
].
disabled
=
true
;
}
},
{
immediate
:
true
})
watch
(()
=>
props
.
fieldTypeList
,
(
val
)
=>
{
fieldRulesFormItems
.
value
[
1
].
options
=
val
||
[];
},
{
immediate
:
true
})
watch
(()
=>
props
.
anonPrivacyMode
,
(
val
)
=>
{
if
(
!
val
)
{
return
;
}
let
hasKaNumber
=
val
.
kaNumber
!=
null
;
privacyFormItems
.
value
[
0
].
default
=
hasKaNumber
?
'Y'
:
'N'
;
privacyFormItems
.
value
[
0
].
children
[
0
].
visible
=
hasKaNumber
;
privacyFormItems
.
value
[
0
].
children
[
0
].
default
=
val
.
kaNumber
;
oldPrivacyModelValue
.
value
.
isKaNumber
=
hasKaNumber
?
'Y'
:
'N'
;
oldPrivacyModelValue
.
value
.
kaNumber
=
val
.
kaNumber
;
let
hasRiskThreshold
=
val
.
riskThreshold
!=
null
;
privacyFormItems
.
value
[
1
].
default
=
hasRiskThreshold
?
'Y'
:
'N'
;
privacyFormItems
.
value
[
1
].
children
[
0
].
visible
=
hasRiskThreshold
;
privacyFormItems
.
value
[
1
].
children
[
0
].
default
=
val
.
riskThreshold
;
oldPrivacyModelValue
.
value
.
isRiskThreshold
=
hasRiskThreshold
?
'Y'
:
'N'
;
oldPrivacyModelValue
.
value
.
riskThreshold
=
val
.
riskThreshold
;
let
hasldFieldName
=
!!
val
.
ldFieldName
;
privacyFormItems
.
value
[
2
].
default
=
hasldFieldName
?
'Y'
:
'N'
;
privacyFormItems
.
value
[
2
].
children
[
0
].
visible
=
hasldFieldName
;
privacyFormItems
.
value
[
2
].
children
[
1
].
visible
=
hasldFieldName
;
privacyFormItems
.
value
[
2
].
children
[
0
].
default
=
val
.
ldFieldName
;
privacyFormItems
.
value
[
2
].
children
[
1
].
default
=
val
.
ldNumber
;
oldPrivacyModelValue
.
value
.
isLdField
=
hasldFieldName
?
'Y'
:
'N'
;
oldPrivacyModelValue
.
value
.
ldFieldName
=
val
.
ldFieldName
;
oldPrivacyModelValue
.
value
.
ldNumber
=
val
.
ldNumber
;
let
hasTcField
=
!!
val
.
tcFieldName
;
privacyFormItems
.
value
[
3
].
default
=
hasTcField
?
'Y'
:
'N'
;
privacyFormItems
.
value
[
3
].
children
[
0
].
visible
=
hasTcField
;
privacyFormItems
.
value
[
3
].
children
[
1
].
visible
=
hasTcField
;
privacyFormItems
.
value
[
3
].
children
[
0
].
default
=
val
.
tcFieldName
;
privacyFormItems
.
value
[
3
].
children
[
1
].
default
=
val
.
tcThreshold
;
oldPrivacyModelValue
.
value
.
isTcField
=
hasTcField
?
'Y'
:
'N'
;
oldPrivacyModelValue
.
value
.
tcFieldName
=
val
.
tcFieldName
;
oldPrivacyModelValue
.
value
.
tcThreshold
=
val
.
tcThreshold
;
},
{
deep
:
true
})
watch
(()
=>
props
.
anonTaskRules
,
(
val
)
=>
{
ruleModelTableInfo
.
value
.
data
=
val
||
[];
let
optionsList
=
val
?.
map
(
v
=>
{
return
{
fieldName
:
v
.
fieldName
,
fieldChName
:
v
.
fieldChName
}
})
||
[];
privacyFormItems
.
value
[
2
].
children
[
0
].
options
=
optionsList
;
privacyFormItems
.
value
[
3
].
children
[
0
].
options
=
optionsList
;
})
onBeforeMount
(()
=>
{
getParamsList
({
dictType
:
"标签类型"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
labelTypeList
.
value
=
res
.
data
||
[];
let
item
=
fieldRulesFormItems
.
value
.
find
(
item
=>
item
.
field
==
'dataTypeCode'
);
item
&&
(
item
.
options
=
labelTypeList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getParamsList
({
dictType
:
"脱敏规则"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
desensitiveRuleTypeList
.
value
=
res
.
data
||
[];
let
item
=
fieldRulesFormItems
.
value
.
find
(
item
=>
item
.
field
==
'desensitiveRuleCode'
);
item
&&
(
item
.
options
=
desensitiveRuleTypeList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getParamsList
({
dictType
:
"加密算法"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
hashMethodList
.
value
=
res
.
data
||
[];
let
item
=
fieldRulesFormItems
.
value
.
find
(
item
=>
item
.
field
==
'encryptionAlgorithmCode'
);
item
&&
(
item
.
options
=
hashMethodList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getGeneralizeFileNameList
().
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
generalizeFileNameList
.
value
=
res
.
data
||
[];
let
item
=
fieldRulesFormItems
.
value
.
find
(
item
=>
item
.
field
==
'generalizeFileGuid'
);
item
&&
(
item
.
options
=
generalizeFileNameList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
})
/** 隐私模型设置表单 */
const
privacyFormRef
=
ref
();
const
getStepTwoConfigInfo
=
async
()
=>
{
if
(
!
ruleModelTableInfo
.
value
.
data
?.
length
)
{
proxy
.
$ElMessage
.
error
(
'字段脱敏规则不能为空'
);
return
false
;
}
try
{
await
privacyFormRef
.
value
?.
ruleFormRef
?.
validate
();
// 验证通过
return
{
anonPrivacyMode
:
cloneDeep
(
privacyFormRef
.
value
?.
formInline
),
anonTaskRules
:
cloneDeep
(
ruleModelTableInfo
.
value
.
data
)
};
}
catch
(
error
)
{
// 验证失败
return
false
;
}
}
/** 字段改变,可能是切换了表,需要清空规则配置。判断 第一步到第二步时,如果字段列表中与字段脱敏规则中的字段不匹配,应清空,并同时清空T接近字段。 */
const
updateNextStepRules
=
()
=>
{
ruleModelTableInfo
.
value
.
data
=
ruleModelTableInfo
.
value
.
data
.
filter
(
rule
=>
{
if
(
!
props
.
fieldNameList
?.
some
(
v
=>
v
.
enName
==
rule
.
fieldName
))
{
return
false
;
}
return
true
;
});
privacyFormItems
.
value
[
2
].
children
[
0
].
options
=
props
.
fieldNameList
;
privacyFormItems
.
value
[
3
].
children
[
0
].
options
=
props
.
fieldNameList
;
let
formInline
=
privacyFormRef
.
value
?.
formInline
;
privacyFormItems
.
value
.
forEach
((
item
,
index
)
=>
{
item
.
default
=
formInline
[
item
.
field
];
if
(
item
.
default
==
'Y'
)
{
item
.
children
?.
forEach
(
child
=>
{
child
.
default
=
formInline
[
child
.
field
];
if
((
child
.
field
==
'tcFieldName'
||
child
.
field
==
'ldFieldName'
)
&&
!
props
.
fieldNameList
.
some
(
f
=>
f
.
enName
==
child
.
default
))
{
child
.
default
=
''
}
});
}
})
}
defineExpose
({
getStepTwoConfigInfo
,
updateNextStepRules
})
</
script
>
<
template
>
<div
class=
"operator_panel_wrap"
>
<ContentWrap
id=
"id-rules"
title=
"设置匿名化规则"
description=
""
style=
"margin-top: 8px;"
>
<Table
:tableInfo=
"ruleModelTableInfo"
/>
<div
class=
"row-add-btn"
>
<el-button
link
@
click=
"addRowRules"
:icon=
"CirclePlus"
v-preReClick
>
添加字段脱敏规则
</el-button>
</div>
</ContentWrap>
<ContentWrap
id=
"id-screctModel"
title=
"隐私模型设置"
description=
""
style=
"margin-top: 16px;"
>
<Form
style=
"width: 80%;max-width: 500px;"
ref=
"privacyFormRef"
:itemList=
"privacyFormItems"
:rules=
"privacyFormRules"
formId=
"model-select-edit"
@
checkboxChange=
"handleCheckboxChange"
/>
</ContentWrap>
<Drawer
ref=
"drawerRef"
:drawerInfo=
"drawerInfo"
@
drawerBtnClick=
"drawerBtnClick"
@
drawerInputChange=
"drawerInputChange"
@
drawerSelectChange=
"drawerSelectChange"
>
<template
v-slot:default
>
<div
v-show=
"drawerRef?.getDrawerConRef('drawerFormRef')?.formInline?.desensitiveRuleCode == 'DISSEMBLE'"
>
<div>
{{
'请配置分段是否脱敏'
+
`(${desensitiveRuleDetail.ruleDetails?.length
}
/10)`
}}
<
/div
>
<
el
-
radio
-
group
v
-
model
=
"desensitiveRuleDetail.dissembleType"
>
<
el
-
radio
:
value
=
"1"
>
从左往右
<
/el-radio
>
<
el
-
radio
:
value
=
"2"
>
从右往左
<
/el-radio
>
<
/el-radio-group
>
<
div
class
=
"seg-main"
>
<
div
class
=
"row-per"
v
-
for
=
"(item, index) in desensitiveRuleDetail.ruleDetails"
>
<
el
-
select
v
-
model
=
"item.digitType"
:
style
=
"
{
width
:
item
.
digitType
==
2
?
'322px'
:
'170px'
}
">
<el-option v-for="
item
in
digitTypeList
" :label="
item
.
label
" :value="
item
.
value
"
:key="
item
.
value
"></el-option>
</el-select>
<el-input v-show="
item
.
digitType
!=
2
" style="
width
:
137
px
;
margin
-
left
:
4
px
;
" v-model="
item
.
digit
"
:maxlength="
6
" :min="
1
" @input="
(
val
)
=>
inputEventDigitChange
(
val
,
item
)
" placeholder="
请输入
"></el-input>
<el-select v-model="
item
.
ruleType
" style="
width
:
170
px
;
margin
-
left
:
4
px
;
">
<el-option v-for="
item
in
dissembleRuleTypeList
" :label="
item
.
label
" :value="
item
.
value
"
:key="
item
.
value
"></el-option>
</el-select>
<div class="
title_tool
" @click="
deleteSegmentRule
(
item
,
index
)
">
<el-icon :size="
20
" color="
#
b2b2b2
">
<Delete />
</el-icon>
</div>
</div>
<div class="
row
-
add
-
btn
">
<el-button :disabled="
desensitiveRuleDetail
.
ruleDetails
?.
length
>
9
" link @click="
addSegmentRule
"
:icon="
CirclePlus
" v-preReClick>添加分段规则</el-button>
</div>
</div>
</div>
<div v-show="
drawerRef
?.
getDrawerConRef
(
'drawerFormRef'
)?.
formInline
?.
desensitiveRuleCode
==
'CHARREPLACE'
"
>
<
div
>
{{
'请配置分段的替换方式'
+
`(${charReplaceRuleDetail.ruleDetails?.length
}
/10)`
}}
<
/div
>
<
el
-
radio
-
group
v
-
model
=
"charReplaceRuleDetail.replaceType"
>
<
el
-
radio
:
value
=
"1"
>
从左往右
<
/el-radio
>
<
el
-
radio
:
value
=
"2"
>
从右往左
<
/el-radio
>
<
/el-radio-group
>
<
div
class
=
"seg-main"
>
<
div
class
=
"row-per"
v
-
for
=
"(item, index) in charReplaceRuleDetail.ruleDetails"
>
<
el
-
select
v
-
model
=
"item.digitType"
:
style
=
"
{
width
:
item
.
digitType
==
2
?
'220px'
:
'130px'
}
">
<el-option v-for="
item
in
digitTypeList
" :label="
item
.
label
" :value="
item
.
value
"
:key="
item
.
value
"></el-option>
</el-select>
<el-input v-show="
item
.
digitType
!=
2
" style="
width
:
86
px
;
margin
-
left
:
4
px
;
" v-model="
item
.
digit
"
:maxlength="
6
" :min="
1
" @input="
(
val
)
=>
inputEventDigitChange
(
val
,
item
)
" placeholder="
请输入
"></el-input>
<el-select v-model="
item
.
ruleType
"
:style="
{
width
:
item
.
ruleType
==
1
?
'244px'
:
'130px'
,
'margin-left'
:
'4px'
}
">
<el-option v-for="
item
in
charReplaceRuleTypeList
" :label="
item
.
label
" :value="
item
.
value
"
:key="
item
.
value
"></el-option>
</el-select>
<el-input v-model="
item
.
fixedValue
" v-show="
item
.
ruleType
==
2
" style="
width
:
110
px
;
margin
-
left
:
4
px
;
"
:maxlength="
50
" placeholder="
请输入
"></el-input>
<div class="
title_tool
" @click="
deleteCharReplaceSegmentRule
(
item
,
index
)
">
<el-icon :size="
20
" color="
#
b2b2b2
">
<Delete />
</el-icon>
</div>
</div>
<div class="
row
-
add
-
btn
">
<el-button :disabled="
charReplaceRuleDetail
.
ruleDetails
?.
length
>
9
" link
@click="
addCharReplaceSegmentRule
" :icon="
CirclePlus
" v-preReClick>添加分段规则</el-button>
</div>
</div>
</div>
<div v-show="
drawerRef
?.
getDrawerConRef
(
'drawerFormRef'
)?.
formInline
?.
desensitiveRuleCode
==
'RANGEREPLACE'
">
<div class="
mb8
"
>
{{
'请配置区间替换规则'
+
`(${rangeReplaceRuleDetails?.length
}
/10)`
}}
<
/div
>
<
div
class
=
"seg-main"
>
<
div
class
=
"row-per"
v
-
for
=
"(item, index) in rangeReplaceRuleDetails"
>
<
el
-
input
style
=
"width:16.5%"
v
-
model
=
"item.lowValue"
:
maxlength
=
"6"
@
input
=
"(val) => inputEventDigitChange(val, item, 'lowValue')"
placeholder
=
"请输入"
><
/el-input
>
<
el
-
select
v
-
model
=
"item.lowOperator"
style
=
"width: 16.5%;margin-left: 4px;"
>
<
el
-
option
v
-
for
=
"item in lowerOperatorList"
:
label
=
"item.label"
:
value
=
"item.value"
:
key
=
"item.value"
><
/el-option
>
<
/el-select
>
<
el
-
input
style
=
"width: 16.5%;margin-left: 4px;"
:
disabled
=
"true"
v
-
model
=
"item.fieldChName"
><
/el-input
>
<
el
-
select
v
-
model
=
"item.upperOperator"
style
=
"width: 16.5%;margin-left: 4px;"
>
<
el
-
option
v
-
for
=
"item in lowerOperatorList"
:
label
=
"item.label"
:
value
=
"item.value"
:
key
=
"item.value"
><
/el-option
>
<
/el-select
>
<
el
-
input
style
=
"width:16.5%;margin-left: 4px;"
v
-
model
=
"item.upperValue"
:
maxlength
=
"6"
@
input
=
"(val) => inputEventDigitChange(val, item, 'upperValue')"
placeholder
=
"请输入"
><
/el-input
>
<
el
-
input
v
-
model
=
"item.replaceValue"
style
=
"width:16.5%;margin-left: 4px;"
:
maxlength
=
"50"
placeholder
=
"替换值"
><
/el-input
>
<
div
class
=
"title_tool"
@
click
=
"deleteRangeReplaceSegmentRule(item, index)"
>
<
el
-
icon
:
size
=
"20"
color
=
"#b2b2b2"
>
<
Delete
/>
<
/el-icon
>
<
/div
>
<
/div
>
<
div
class
=
"row-add-btn"
>
<
el
-
button
:
disabled
=
"charReplaceRuleDetail.ruleDetails?.length > 9"
link
@
click
=
"addRangeReplaceSegmentRule"
:
icon
=
"CirclePlus"
v
-
preReClick
>
添加分段规则
<
/el-button
>
<
/div
>
<
/div
>
<
/div
>
<
/template
>
<
/Drawer
>
<
/div
>
<
/template
>
<
style
lang
=
"scss"
scoped
>
.
row
-
add
-
btn
{
.
el
-
button
--
default
{
padding
:
4
px
0
px
;
}
:
deep
(.
el
-
icon
)
{
width
:
16
px
;
height
:
16
px
;
svg
{
width
:
16
px
;
height
:
16
px
;
}
}
}
.
seg
-
main
{
.
row
-
add
-
btn
{
margin
-
top
:
-
6
px
;
}
}
.
row
-
per
{
display
:
flex
;
align
-
items
:
center
;
margin
-
bottom
:
8
px
;
position
:
relative
;
.
title_tool
{
margin
-
left
:
4
px
;
// position: absolute;
// right: 4px;
cursor
:
pointer
;
:
deep
(.
el
-
icon
)
{
--
color
:
#
FB2323
!
important
;
svg
{
width
:
16
px
;
height
:
16
px
;
}
}
}
}
:
deep
(.
mt8
.
drawer_panel
)
{
margin
-
top
:
8
px
;
}
.
mb8
{
margin
-
bottom
:
8
px
;
}
:
deep
(.
el
-
form
-
item
.
ka
-
checkbox
-
input
)
{
.
el
-
input
{
min
-
width
:
50
px
!
important
;
}
}
:
deep
(.
el
-
form
)
{
.
lmt12
{
// margin-top: -4px; 验证信息会被遮挡
.
input_panel
{
flex
:
0
!
important
;
}
}
}
<
/style>
\ No newline at end of file
src/views/data_anonymization/components/anonResultAnalysis.vue
0 → 100644
View file @
c89a85a
<
template
>
<div
class=
"analysis-result-main"
>
<div
v-if=
"showTitle"
class=
"result-title"
>
匿名结果分析
</div>
<div
class=
"kpi-content"
v-show=
"Object.keys(analysisResultInfo).length > 0"
>
<div
class=
"border-content"
>
<div
class=
"text"
>
去标识化效果评估结果
</div>
<span
class=
"number score-color"
>
{{
analysisResultInfo
.
rating
+
'级'
}}
</span>
</div>
<div
class=
"border-content"
>
<span
class=
"text"
>
重标识风险最大值
<el-tooltip
placement=
"top"
effect=
"light"
popper-class=
"table_tooltip"
>
<template
#
content
>
<div
style=
"max-width: 236px;"
>
所有等价类的重标识风险最大值
</div>
</
template
>
<el-icon
style=
"margin-left: 2px;margin-top: 2px;"
>
<QuestionFilled
/>
</el-icon>
</el-tooltip></span>
<span
class=
"number"
>
{{ analysisResultInfo.reIdentifyRiskRb != null ?
(analysisResultInfo.reIdentifyRiskRb || 0) : '--'
}}
</span>
</div>
<div
class=
"border-content"
>
<span
class=
"text"
>
重标识风险平均值
<el-tooltip
placement=
"top"
effect=
"light"
popper-class=
"table_tooltip"
>
<
template
#
content
>
<div
style=
"max-width: 236px;"
>
所有等价类的重标识风险平均值
</div>
</
template
>
<el-icon
style=
"margin-left: 2px;margin-top: 2px;"
>
<QuestionFilled
/>
</el-icon>
</el-tooltip></span>
<span
class=
"number"
>
{{ analysisResultInfo.reIdentifyRiskRc != null ?
(analysisResultInfo.reIdentifyRiskRc || 0) : '--' }}
</span>
</div>
<div
class=
"border-content"
>
<span
class=
"text"
>
环境重标识攻击概率
<el-tooltip
placement=
"top"
effect=
"light"
popper-class=
"table_tooltip"
>
<
template
#
content
>
<div
style=
"max-width: 236px;"
>
完全公开共享数据发布,攻击者对数据集进行环境重标识攻击的概率为1。领地公开共享与受控公开共享数据发布,环境重标识攻击概率为内部故意攻击概率、数据集包含熟人概率和数据泄露概率三者的最大值。
</div>
</
template
>
<el-icon
style=
"margin-left: 2px;margin-top: 2px;"
>
<QuestionFilled
/>
</el-icon>
</el-tooltip></span>
<span
class=
"number"
>
{{ analysisResultInfo.envReAttackPr != null ?
(analysisResultInfo.envReAttackPr || 0) : '--' }}
</span>
</div>
<div
class=
"border-content"
>
<span
class=
"text"
>
等价类门限风险
<el-tooltip
placement=
"top"
effect=
"light"
popper-class=
"table_tooltip"
>
<
template
#
content
>
<div
style=
"max-width: 236px;"
>
完全公开共享数据发布,门限阈值取值 1/20;受控公开共享数据发布,门限阈值取值 1/5;领地公开共享数据发布,门限阈值取值
1/3;等价类门限风险为:等价类的重标识风险大于门限阈值为1,小于等于为0,求和后除以等价类个数。
</div>
</
template
>
<el-icon
style=
"margin-left: 2px;margin-top: 2px;"
>
<QuestionFilled
/>
</el-icon>
</el-tooltip></span>
<span
class=
"number"
>
{{ analysisResultInfo.reIdentifyRiskRa != null ?
(analysisResultInfo.reIdentifyRiskRa || 0) : '--' }}
</span>
</div>
<div
class=
"border-content"
>
<span
class=
"text"
>
重标识风险总体风险
<el-tooltip
placement=
"top"
effect=
"light"
popper-class=
"table_tooltip"
>
<
template
#
content
>
<div
style=
"max-width: 236px;"
>
完全公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险最大值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。
受控公开共享和领地公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险平均值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。
<!--
{{
oldAnonTaskValueInfo
.
dataSharingTypeCode
==
'01'
?
'完全公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险最大值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。'
:
`${oldAnonTaskValueInfo.dataSharingTypeCode == '02' ? '受控公开共享' : '领地公开共享'
}
,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险平均值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。`
}}
-->
<
/div
>
<
/template
>
<
el
-
icon
style
=
"margin-left: 2px;margin-top: 2px;"
>
<
QuestionFilled
/>
<
/el-icon
>
<
/el-tooltip></
span
>
<
span
class
=
"number"
>
{{
analysisResultInfo
.
reIdentifyOverallRisk
!=
null
?
(
analysisResultInfo
.
reIdentifyOverallRisk
||
0
)
:
'--'
}}
<
/span
>
<
/div
>
<
div
class
=
"border-content"
>
<
span
class
=
"text"
>
重标识可接受风险阈值
<
/span
>
<
span
class
=
"number"
>
{{
oldAnonTaskValueInfo
.
anonPrivacyMode
?.
riskThreshold
==
null
?
0.05
:
(
oldAnonTaskValueInfo
.
anonPrivacyMode
?.
riskThreshold
||
0
)
}}
<
/span
>
<
/div
>
<
/div
>
<
div
class
=
"result-title"
>
重标识风险表
<
/div
>
<
el
-
table
ref
=
"tableRef"
v
-
show
=
"analysisResultTableFields.length"
:
data
=
"resultData"
v
-
loading
=
"analysisResultLoading"
:
highlight
-
current
-
row
=
"true"
stripe
border
tooltip
-
effect
=
"light"
height
=
"100%"
row
-
key
=
"guid"
:
style
=
"
{
width
:
'100%'
,
height
:
'280px'
}
">
<el-table-column label="
等价类
" type="
index
" width="
68
px
" align="
center
" show-overflow-tooltip>
<template #default="
scope
"
>
<
span
>
{{
pageInfo
.
curr
!==
undefined
?
(
pageInfo
.
curr
-
1
)
*
pageInfo
.
limit
+
scope
.
$index
+
1
:
scope
.
$index
+
1
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
template
v
-
for
=
"(item, index) in (analysisResultTableFields || [])"
>
<
el
-
table
-
column
:
label
=
"item.chName"
:
width
=
"item.dataType === 'datetime'
? TableColumnWidth.DATETIME
: item.dataType === 'date'
? TableColumnWidth.DATE
: originResultTableFieldColumn[item.enName]
"
:
align
=
"getTextAlign(item)"
:
header
-
align
=
"getTextAlign(item)"
:
formatter
=
"(row) => formatterPreviewDate(row, item)"
:
show
-
overflow
-
tooltip
=
"true"
>
<
/el-table-column
>
<
/template
>
<
el
-
table
-
column
label
=
"等价类大小"
prop
=
"equivalenceClassNum"
width
=
"98"
align
=
"right"
fixed
=
"right"
show
-
overflow
-
tooltip
><
/el-table-column
>
<
el
-
table
-
column
label
=
"重标识风险"
prop
=
"reIdentifyRisk"
width
=
"96"
align
=
"right"
fixed
=
"right"
show
-
overflow
-
tooltip
><
/el-table-column
>
<
el
-
table
-
column
label
=
"判断重风险是否大于门限阈值"
prop
=
"isGtThreshold"
width
=
"130"
align
=
"left"
fixed
=
"right"
show
-
overflow
-
tooltip
><
/el-table-column
>
<
/el-table
>
<
div
v
-
show
=
"!analysisResultTableFields.length"
class
=
"empty-content"
>
<
img
src
=
"../../../assets/images/empty-data.png"
:
style
=
"
{
width
:
'168px'
,
height
:
'96px'
}
" />
<div class="
empty
-
text
">暂无数据</div>
</div>
<div v-show="
analysisResultTableFields
.
length
" class="
result
-
table
-
desc
">门限阈值的取值:完全公开共享数据发布,取值
1/20;受控公开共享数据发布,取值
1/5;领地公开共享数据发布,取值 1/3</div>
<PageNav v-show="
analysisResultTableFields
.
length
" :class="
[
pageInfo
.
type
]
" :pageInfo="
pageInfo
"
@pageChange="
pageChange
" />
<div class="
row
-
two
-
main
" style="
margin
-
top
:
12
px
;
">
<div class="
table
-
one
">
<div class="
result
-
title
">对抗性测试关键变量</div>
<el-table ref="
varTableRef
" :data="
analysisResultInfo
?.
adversarialTest
||
[]
" :highlight-current-row="
true
"
stripe border tooltip-effect="
light
" height="
100
%
" row-key="
guid
"
:style="
{
height
:
oldAnonTaskValueInfo
.
dataSharingTypeCode
!=
'01'
?
(
containerWidth
>
1397
?
'378px'
:
(
containerWidth
<
1048
?
'414px'
:
'396px'
))
:
'auto'
}
">
<el-table-column label="
序号
" type="
index
" width="
56
px
" align="
center
" show-overflow-tooltip>
</el-table-column>
<el-table-column label="
数据项
" prop="
chName
" width="
150
px
" align="
left
" show-overflow-tooltip></el-table-column>
<el-table-column label="
唯一性分值
" prop="
uniqueScore
" width="
110
px
" align="
right
"
show-overflow-tooltip></el-table-column>
<el-table-column label="
影响力分值
" prop="
influenceScore
" width="
110
" align="
right
"
show-overflow-tooltip></el-table-column>
<el-table-column label="
数据属性标识度分值
" prop="
dataAttrIdentScore
" width="
160
" align="
right
"
show-overflow-tooltip></el-table-column>
</el-table>
</div>
<div class="
table
-
two
" v-show="
oldAnonTaskValueInfo
.
dataSharingTypeCode
!=
'01'
">
<div class="
result
-
title
">内部故意攻击概率</div>
<div class="
desc
" style="
margin
-
bottom
:
4
px
;
color
:
#
999
;
line
-
height
:
18
px
;
">
重标识数据的动机和能力为低,从重标识攻击可能性分析表可得出在内部攻击方面,重标识攻击概率的取值为0.05。</div>
<el-table ref="
innerTableRef
" :data="
innerResultData
" :highlight-current-row="
true
" stripe border
tooltip-effect="
light
" height="
100
%
" row-key="
guid
" :style="
{
height
:
'356px'
}
"
:span-method="
arrayInnerSpanMethod
" :cell-class-name="
handleInnerCellClass
">
<!-- <el-table-column label="
序号
" type="
index
" width="
56
px
" align="
center
" show-overflow-tooltip>
</el-table-column> -->
<el-table-column label="
风险减缓控制水平
" prop="
level
" width="
150
px
" align="
left
"
show-overflow-tooltip></el-table-column>
<el-table-column label="
动机和能力
" prop="
competencyLevel
" width="
140
px
" align="
left
"
show-overflow-tooltip></el-table-column>
<el-table-column label="
重标识攻击概率
" prop="
value
" width="
140
" align="
right
" show-overflow-tooltip>
<template #default="
scope
"
>
<
span
>
{{
scope
.
row
.
value
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
/el-table
>
<
/div
>
<
/div
>
<
div
class
=
"row-two-main"
v
-
show
=
"oldAnonTaskValueInfo.dataSharingTypeCode != '01'"
>
<
div
class
=
"table-one border"
>
<
div
class
=
"result-title"
>
数据集包含熟人概率
<
/div
>
<
div
class
=
"result-title-h1"
>
pr
=
1
-
(
1
-
p
)
^
m
<
/div
>
<
div
class
=
"result-title-desc"
>
{{
` 数据集包含熟人概率可通过以上公式计算,${new
Date().getFullYear()
}
年我国最新的人口统计为${analysisResultInfo?.allPerson ||
0
}
亿人,其中该数据集的容量为${analysisResultInfo?.dataSetNum || 0
}
万,
占总人口的比例为${analysisResultInfo.patientPopulationRate ||
0
}
%,m值取推荐值150,数据集包含熟人的概率为${analysisResultInfo.randomAcquaintancePr || 0
}
。`
}}
<
/div
>
<
/div
>
<
div
class
=
"table-two border"
>
<
div
class
=
"result-title"
>
数据泄露概率
<
/div
>
<
div
class
=
"result-title-desc"
>
<
div
>
对于安全和隐私控制能力评估为低的情况
,
推荐将数据泄露概率设定为
0.55
。
<
/div
>
<
div
>
对于安全和隐私控制能力评估为中的情况
,
推荐将数据泄露概率设定为
0.27
。
<
/div
>
<
div
>
对于安全和隐私控制能力评估为高的情况
,
推荐将数据泄露概率设定为
0.14
。
<
/div
>
<
div
>
{{
`数据接收方的安全和隐私控制能力为高,按照推荐值将数据泄露概率设定为${analysisResultInfo.dataBreachPr || 0
}
。`
}}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/template
>
<
script
lang
=
"ts"
setup
name
=
"anonResultAnalysis"
>
import
{
TableColumnWidth
}
from
'@/utils/enum'
;
import
Moment
from
'moment'
;
defineProps
({
showTitle
:
{
type
:
Boolean
,
default
:
false
,
}
,
analysisResultInfo
:
{
type
:
Object
,
default
:
{
}
,
}
,
pageInfo
:
{
type
:
Object
,
default
:
{
}
,
}
,
analysisResultLoading
:
{
type
:
Boolean
,
default
:
false
,
}
,
oldAnonTaskValueInfo
:
{
type
:
Object
,
default
:
{
}
,
}
,
resultData
:
{
type
:
Array
,
default
:
[]
}
,
analysisResultTableFields
:
{
type
:
Array
,
default
:
[],
}
,
originResultTableFieldColumn
:
{
type
:
Object
,
default
:
{
}
,
}
,
containerWidth
:
{
type
:
Number
,
default
:
1200
,
}
,
}
)
const
emits
=
defineEmits
([
"pageChange"
]);
const
getTextAlign
=
(
field
)
=>
{
if
(
field
.
dataType
===
'decimal'
||
field
.
dataType
===
'int'
)
{
return
'right'
;
}
return
'left'
}
const
formatterPreviewDate
=
(
row
,
info
)
=>
{
let
enName
=
info
.
enName
;
let
v
=
row
[
enName
];
if
(
v
===
0
)
{
return
v
;
}
if
(
!
v
||
v
==
'null'
)
{
return
'--'
;
}
if
(
info
.
dataType
===
'datetime'
)
{
return
Moment
(
v
).
format
(
'YYYY-MM-DD HH:mm:ss'
);
}
if
(
info
.
dataType
===
'date'
)
{
if
(
isNaN
(
<
any
>
(
new
Date
(
v
))))
{
return
Moment
(
parseInt
(
v
)).
format
(
'YYYY-MM-DD'
);
}
else
{
return
Moment
(
v
).
format
(
'YYYY-MM-DD'
);
}
}
return
v
;
}
;
/** 内部故意攻击概率的表格 */
const
innerResultData
=
ref
([{
guid
:
'1'
,
level
:
'高'
,
competencyLevel
:
'低'
,
value
:
'0.05'
}
,
{
guid
:
'2'
,
level
:
'高'
,
competencyLevel
:
'中'
,
tooltip
:
true
,
value
:
'0.1'
}
,
{
guid
:
'3'
,
level
:
'高'
,
competencyLevel
:
'高'
,
tooltip
:
true
,
value
:
'0.2'
}
,
{
guid
:
'4'
,
level
:
'中'
,
competencyLevel
:
'低'
,
value
:
'0.2'
}
,
{
guid
:
'5'
,
level
:
'中'
,
competencyLevel
:
'中'
,
value
:
'0.3'
}
,
{
guid
:
'6'
,
level
:
'中'
,
competencyLevel
:
'高'
,
value
:
'0.4'
}
,
{
guid
:
'7'
,
level
:
'低'
,
competencyLevel
:
'低'
,
value
:
'0.4'
}
,
{
guid
:
'8'
,
level
:
'低'
,
competencyLevel
:
'中'
,
value
:
'0.5'
}
,
{
guid
:
'9'
,
level
:
'低'
,
competencyLevel
:
'高'
,
value
:
'0.6'
}
]);
const
arrayInnerSpanMethod
=
({
row
,
column
,
rowIndex
,
columnIndex
}
)
=>
{
if
(
columnIndex
>
0
)
{
return
[
1
,
1
];
}
// 查找当前字段值相同的连续行
let
startRow
=
rowIndex
;
let
endRow
=
rowIndex
;
let
field
=
'level'
;
// 向前查找
while
(
startRow
>
0
&&
innerResultData
.
value
[
startRow
-
1
][
field
]
===
innerResultData
.
value
[
rowIndex
][
field
])
{
startRow
--
;
}
// 向后查找
while
(
endRow
<
innerResultData
.
value
.
length
-
1
&&
innerResultData
.
value
[
endRow
+
1
][
field
]
===
innerResultData
.
value
[
rowIndex
][
field
])
{
endRow
++
;
}
// 如果当前行不是相同值组的起始行,则不显示
if
(
startRow
!==
rowIndex
)
{
return
[
0
,
0
];
}
// 返回合并的行数
const
rowspan
=
endRow
-
startRow
+
1
;
return
[
rowspan
,
1
];
}
const
handleInnerCellClass
=
({
row
,
column
,
rowIndex
,
columnIndex
}
)
=>
{
if
(
rowIndex
==
0
&&
columnIndex
>
0
)
{
return
'cell-tooltip-bg'
;
}
return
''
;
}
const
pageChange
=
(
info
)
=>
{
emits
(
'pageChange'
,
info
);
}
<
/script
>
<
style
lang
=
"scss"
scoped
>
.
analysis
-
result
-
main
{
min
-
height
:
250
px
;
.
value
-
desc
{
font
-
size
:
14
px
;
color
:
#
212121
;
line
-
height
:
21
px
;
}
.
result
-
title
{
font
-
size
:
16
px
;
color
:
#
212121
;
line
-
height
:
24
px
;
font
-
weight
:
600
;
margin
-
bottom
:
6
px
;
}
.
result
-
title
-
h1
{
color
:
#
212121
;
font
-
weight
:
600
;
font
-
size
:
24
px
;
text
-
align
:
center
;
line
-
height
:
36
px
;
margin
-
top
:
12
px
;
}
.
result
-
title
-
desc
{
color
:
#
666
;
font
-
size
:
14
px
;
line
-
height
:
21
px
;
margin
-
top
:
12
px
;
}
.
kpi
-
content
{
display
:
flex
;
flex
-
direction
:
row
;
column
-
gap
:
12
px
;
row
-
gap
:
12
px
;
flex
-
wrap
:
wrap
;
margin
-
bottom
:
20
px
;
}
.
border
-
content
{
height
:
76
px
;
display
:
flex
;
flex
-
direction
:
column
;
align
-
items
:
left
;
padding
-
left
:
16
px
;
justify
-
content
:
center
;
border
:
1
px
solid
#
d9d9d9
;
width
:
calc
(
20
%
-
8
px
);
min
-
width
:
228
px
;
border
-
radius
:
2
px
;
padding
-
left
:
16
px
;
.
number
{
font
-
weight
:
700
;
font
-
size
:
20
px
;
color
:
#
212121
;
line
-
height
:
30
px
;
margin
-
top
:
2
px
;
&
.
score
-
color
{
color
:
#
FF5F1F
;
}
}
.
text
{
font
-
size
:
14
px
;
line
-
height
:
21
px
;
color
:
#
666666
;
display
:
flex
;
.
el
-
icon
{
color
:
#
b2b2b2
;
}
}
}
.
result
-
table
-
desc
{
font
-
size
:
14
px
;
color
:
#
999999
;
line
-
height
:
21
px
;
}
.
row
-
two
-
main
{
margin
-
top
:
18
px
;
display
:
flex
;
.
table
-
one
{
width
:
586
px
;
&
.
border
{
border
:
1
px
solid
#
d9d9d9
;
padding
:
14
px
18
px
18
px
;
}
}
.
table
-
two
{
margin
-
left
:
20
px
;
width
:
calc
(
100
%
-
606
px
);
&
.
border
{
border
:
1
px
solid
#
d9d9d9
;
padding
:
14
px
18
px
18
px
;
}
}
}
}
.
empty
-
content
{
display
:
flex
;
align
-
items
:
center
;
justify
-
content
:
center
;
height
:
316
px
;
width
:
100
%
;
flex
-
direction
:
column
;
.
empty
-
text
{
font
-
size
:
14
px
;
color
:
#
b2b2b2
;
}
}
<
/style>
\ No newline at end of file
src/views/data_
smart_contract
/generalizeFile.vue
→
src/views/data_
anonymization
/generalizeFile.vue
View file @
c89a85a
File moved
src/views/data_
smart_contract
/generalizeFileEdit.vue
→
src/views/data_
anonymization
/generalizeFileEdit.vue
View file @
c89a85a
File moved
src/views/data_anonymization/labelManagement.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: labelManagement
</route>
<
script
lang=
"ts"
setup
name=
"labelManagement"
>
import
{
commonPageConfig
}
from
'@/components/PageNav/index'
;
import
{
TableColumnWidth
}
from
'@/utils/enum'
;
import
TableTools
from
"@/components/Tools/table_tools.vue"
;
import
{
getDataLabelList
,
updateLabelState
,
saveLabel
,
updateLabel
,
deleteLabel
,
getLabelDetail
,
getParamsList
,
}
from
'@/api/modules/dataAnonymization'
;
import
{
useValidator
}
from
'@/hooks/useValidator'
;
import
{
Delete
,
CirclePlus
,
Warning
}
from
"@element-plus/icons-vue"
;
const
{
required
,
regexpRuleValidate
}
=
useValidator
();
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
/** 标签类型字典列表 */
const
labelTypeList
:
any
=
ref
([]);
/** 内置规则字典列表 */
const
builtInRuleList
:
any
=
ref
([]);
const
searchItemList
=
ref
([{
type
:
"input"
,
label
:
""
,
field
:
"labelName"
,
default
:
""
,
placeholder
:
"标签名称"
,
clearable
:
true
,
}])
/** 分页及搜索传参信息配置。 */
const
page
=
ref
({
...
commonPageConfig
,
labelName
:
''
,
});
const
currTableData
:
any
=
ref
();
const
tableInfo
=
ref
({
id
:
'data-label-table'
,
// multiple:true,
fields
:
[
{
label
:
"序号"
,
type
:
"index"
,
width
:
TableColumnWidth
.
INDEX
,
align
:
"center"
},
{
label
:
"标签名称"
,
field
:
"labelName"
,
width
:
140
},
{
label
:
"标签类型"
,
field
:
"labelTypeName"
,
width
:
140
},
{
label
:
'状态'
,
field
:
'bizState'
,
type
:
'switch'
,
activeText
:
'启用'
,
inactiveText
:
'禁用'
,
activeValue
:
'Y'
,
inactiveValue
:
'N'
,
switchWidth
:
56
,
width
:
100
,
align
:
'center'
},
{
label
:
"界面排序"
,
field
:
"orderNum"
,
width
:
90
,
align
:
"center"
},
{
label
:
"修改人"
,
field
:
"updateUserName"
,
width
:
TableColumnWidth
.
USERNAME
},
{
label
:
"修改时间"
,
field
:
"updateTime"
,
width
:
TableColumnWidth
.
DATETIME
},
],
data
:
[],
page
:
{
type
:
"normal"
,
rows
:
0
,
...
page
.
value
,
},
actionInfo
:
{
label
:
"操作"
,
type
:
"btn"
,
width
:
120
,
fixed
:
'right'
,
btns
:
(
scope
)
=>
{
let
btnsArr
:
any
=
[];
btnsArr
.
push
({
label
:
"编辑"
,
value
:
"edit"
,
click
:
(
scope
)
=>
{
currTableData
.
value
=
scope
.
row
;
getLabelDetail
(
scope
.
row
.
guid
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
const
detail
=
res
.
data
||
{};
currTableData
.
value
=
Object
.
assign
({},
currTableData
.
value
,
detail
);
newCreateLabelFormItems
.
value
.
forEach
(
item
=>
{
if
(
item
.
children
?.
length
)
{
item
.
children
.
forEach
(
child
=>
child
.
default
=
detail
[
child
.
field
]);
}
else
{
item
.
default
=
detail
[
item
.
field
];
}
item
.
default
=
detail
[
item
.
field
];
});
let
labelRuleField
=
detail
.
labelRuleField
||
{};
matchChValue
.
value
.
value
=
labelRuleField
.
matchChValue
;
matchChValue
.
value
.
disabled
=
false
;
matchEnValue
.
value
.
value
=
labelRuleField
.
matchEnValue
;
matchEnValue
.
value
.
disabled
=
false
;
formRows
.
value
=
labelRuleField
.
vagueMatchRule
||
[{
matchValue
:
''
,
position
:
''
,
name
:
''
,
disabled
:
false
}];
tabsInfo
.
value
.
activeName
=
'labelRuleField'
;
let
labelRuleContent
=
detail
.
labelRuleContent
||
{};
ruleContentFormItems
.
value
[
1
].
visible
=
labelRuleContent
.
ruleType
==
1
;
ruleContentFormItems
.
value
[
2
].
visible
=
labelRuleContent
.
ruleType
==
2
;
ruleContentFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
labelRuleContent
[
item
.
field
];
if
(
item
.
field
==
'regularTestData'
)
{
item
.
validateMsg
=
null
;
}
});
newCreateLabelDialogInfo
.
value
.
title
=
'编辑标签'
;
newCreateLabelDialogInfo
.
value
.
visible
=
true
;
newCreateLabelDialogInfo
.
value
.
type
=
'update'
;
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
}
else
{
proxy
.
$ElMessage
({
type
:
'error'
,
message
:
res
.
msg
,
})
}
});
}
})
btnsArr
.
push
({
label
:
"删除"
,
value
:
"delete"
,
disabled
:
scope
.
row
.
bizState
==
'Y'
,
click
:
(
scope
)
=>
{
proxy
.
$openMessageBox
(
"此操作将永久删除, 是否继续?"
,
()
=>
{
let
guids
=
[
scope
.
row
.
guid
];
deleteLabel
(
guids
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
getTableData
();
proxy
.
$ElMessage
({
type
:
"success"
,
message
:
"删除成功"
,
});
}
else
{
proxy
.
$ElMessage
({
type
:
"error"
,
message
:
res
.
msg
,
});
}
});
})
}
});
return
btnsArr
},
},
loading
:
false
})
const
toSearch
=
(
val
:
any
,
clear
:
boolean
=
false
)
=>
{
if
(
clear
)
{
searchItemList
.
value
.
map
((
item
)
=>
(
item
.
default
=
""
));
page
.
value
.
labelName
=
''
;
}
else
{
page
.
value
.
labelName
=
val
.
labelName
;
}
getTableData
();
};
const
getTableData
=
()
=>
{
tableInfo
.
value
.
loading
=
true
getDataLabelList
({
pageIndex
:
page
.
value
.
curr
,
pageSize
:
page
.
value
.
limit
,
labelName
:
page
.
value
.
labelName
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
const
data
=
res
.
data
||
{}
tableInfo
.
value
.
data
=
data
.
records
||
[]
tableInfo
.
value
.
page
.
limit
=
data
.
pageSize
tableInfo
.
value
.
page
.
curr
=
data
.
pageIndex
tableInfo
.
value
.
page
.
rows
=
data
.
totalRows
}
else
{
proxy
.
$ElMessage
({
type
:
'error'
,
message
:
res
.
msg
,
})
}
tableInfo
.
value
.
loading
=
false
})
};
const
tableSwitchBeforeChange
=
(
scope
,
field
,
callback
)
=>
{
const
msg
=
`确定【
${
scope
.
row
[
field
]
==
'Y'
?
'禁用'
:
'启用'
}
】
${
scope
.
row
.
labelName
}
?`
proxy
.
$openMessageBox
(
msg
,
()
=>
{
const
state
=
scope
.
row
[
field
]
==
'Y'
?
'N'
:
'Y'
const
result
=
tableSwitchChange
(
state
,
scope
,
field
)
callback
(
result
)
},
()
=>
{
callback
(
false
)
});
}
const
tableSwitchChange
=
(
val
,
scope
,
field
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
let
params
=
{
guid
:
scope
.
row
.
guid
,
bizState
:
val
}
updateLabelState
(
params
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
&&
res
.
data
)
{
getTableData
();
proxy
.
$ElMessage
({
type
:
"success"
,
message
:
`
${
scope
.
row
.
labelName
}
【
${
val
==
'Y'
?
'启用'
:
'禁用'
}
】成功`
,
});
resolve
(
true
)
}
else
{
proxy
.
$ElMessage
({
type
:
"error"
,
message
:
res
.
msg
,
});
reject
(
false
)
}
}).
catch
(()
=>
{
reject
(
false
)
})
})
}
const
tablePageChange
=
(
info
)
=>
{
page
.
value
.
curr
=
Number
(
info
.
curr
);
page
.
value
.
limit
=
Number
(
info
.
limit
);
getTableData
();
};
const
newCreateLabelFormItems
=
ref
<
any
>
([{
label
:
'标签名称'
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'labelName'
,
default
:
''
,
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
true
,
},
{
label
:
' '
,
type
:
'select-group'
,
placeholder
:
'请选择'
,
field
:
'labelgroup'
,
children
:
[{
label
:
'标签类型'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'labelTypeCode'
,
default
:
''
,
options
:
labelTypeList
.
value
,
props
:
{
label
:
"label"
,
value
:
"value"
,
},
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
true
,
},
{
label
:
'界面排序'
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'orderNum'
,
maxlength
:
6
,
regexp
:
/
\D
/g
,
required
:
false
,
clearable
:
true
,
}],
},
{
type
:
'radio-group'
,
label
:
'规则配置'
,
field
:
'matchType'
,
default
:
1
,
required
:
false
,
block
:
true
,
options
:
[
{
label
:
'字段识别和内容识别满足其一即可'
,
value
:
1
},
{
label
:
'字段识别和内容识别需满足全部'
,
value
:
2
},
],
}]);
const
newCreateLabelFormRules
=
ref
({
labelName
:
[
required
(
'请输入标签名称'
)],
labelTypeCode
:
[
required
(
'请选择标签类型'
)]
});
const
ruleContentFormRef
=
ref
();
const
newCreateLabelDialogInfo
=
ref
({
visible
:
false
,
size
:
600
,
title
:
"添加标签"
,
type
:
""
,
formInfo
:
{
id
:
"label-form"
,
items
:
newCreateLabelFormItems
.
value
,
rules
:
newCreateLabelFormRules
.
value
,
},
submitBtnLoading
:
false
,
btns
:
{
cancel
:
()
=>
{
newCreateLabelDialogInfo
.
value
.
visible
=
false
;
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
},
submit
:
(
btn
,
info
)
=>
{
// 需要验证两个
let
validateRuleField
=
(
showMsg
=
true
)
=>
{
if
(
!
matchChValue
.
value
.
value
&&
!
matchEnValue
.
value
.
value
&&
(
!
formRows
.
value
.
length
||
(
formRows
.
value
.
length
==
1
&&
!
formRows
.
value
[
0
].
matchValue
&&
!
formRows
.
value
[
0
].
position
&&
!
formRows
.
value
[
0
].
name
)))
{
showMsg
&&
proxy
.
$ElMessage
.
error
(
'字段识别匹配规则不能为空'
);
return
false
;
}
for
(
let
i
=
0
;
i
<
formRows
.
value
.
length
;
i
++
)
{
const
row
=
formRows
.
value
[
i
];
// 如果某一条数据的 matchValue, position, name 都为空,则跳过,不校验
if
(
!
row
.
matchValue
&&
!
row
.
position
&&
!
row
.
name
)
{
continue
;
// 如果全为空,跳过这一行的校验
}
if
(
!
row
.
matchValue
||
!
row
.
position
||
!
row
.
name
)
{
showMsg
&&
proxy
.
$ElMessage
.
error
(
'请填写完整的模糊匹配规则'
);
return
false
;
}
}
return
true
;
}
let
submitLabel
=
()
=>
{
let
params
=
Object
.
assign
({},
info
,
{
bizState
:
'Y'
,
orderNum
:
info
.
orderNum
!=
null
?
parseInt
(
info
.
orderNum
)
:
null
,
labelRuleField
:
{
matchChValue
:
matchChValue
.
value
.
value
,
matchEnValue
:
matchEnValue
.
value
.
value
,
vagueMatchRule
:
formRows
.
value
?.
filter
(
f
=>
f
.
matchValue
!=
''
||
f
.
position
!=
''
||
f
.
name
!=
''
)
},
labelRuleContent
:
ruleContentFormRef
.
value
.
formInline
});
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
true
;
if
(
newCreateLabelDialogInfo
.
value
.
type
==
'add'
)
{
saveLabel
(
params
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
success
(
'标签新建成功'
);
newCreateLabelDialogInfo
.
value
.
visible
=
false
;
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
page
.
value
.
curr
=
1
;
getTableData
();
}
else
{
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
}).
catch
(()
=>
{
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
});
}
else
{
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
true
;
params
.
guid
=
currTableData
.
value
.
guid
;
params
.
labelRuleContent
.
guid
=
currTableData
.
value
.
labelRuleContent
?.
guid
;
params
.
labelRuleField
.
guid
=
currTableData
.
value
.
labelRuleField
?.
guid
;
updateLabel
(
params
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
success
(
'标签编辑成功'
);
newCreateLabelDialogInfo
.
value
.
visible
=
false
;
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
getTableData
();
}
else
{
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
}).
catch
(()
=>
{
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
});
}
}
if
(
info
.
matchType
==
2
)
{
if
(
tabsInfo
.
value
.
activeName
==
'labelRuleField'
)
{
if
(
!
validateRuleField
())
{
return
;
}
ruleContentFormRef
.
value
?.
ruleFormRef
?.
validate
((
valid
,
errorItem
)
=>
{
if
(
valid
)
{
submitLabel
();
}
else
{
tabsInfo
.
value
.
activeName
=
'labelRuleContent'
;
}
})
}
else
{
ruleContentFormRef
.
value
?.
ruleFormRef
?.
validate
((
valid
,
errorItem
)
=>
{
if
(
valid
)
{
if
(
!
validateRuleField
())
{
tabsInfo
.
value
.
activeName
=
'labelRuleField'
;
return
;
}
submitLabel
();
}
})
}
}
else
{
//只需匹配一项即可。
if
(
tabsInfo
.
value
.
activeName
==
'labelRuleField'
)
{
//先验证字段识别
if
(
!
matchChValue
.
value
.
value
&&
!
matchEnValue
.
value
.
value
&&
(
!
formRows
.
value
.
length
||
(
formRows
.
value
.
length
==
1
&&
(
!
formRows
.
value
[
0
].
matchValue
&&
!
formRows
.
value
[
0
].
position
&&
!
formRows
.
value
[
0
].
name
))))
{
// 没有配置字段识别内容
ruleContentFormRef
.
value
?.
ruleFormRef
?.
validate
((
valid
,
errorItem
)
=>
{
if
(
valid
)
{
submitLabel
();
}
else
{
proxy
.
$ElMessage
.
error
(
'字段识别和内容识别不能同时为空'
);
return
;
}
})
}
else
{
ruleContentFormRef
.
value
?.
ruleFormRef
?.
validate
((
valid
,
errorItem
)
=>
{
if
(
valid
)
{
submitLabel
();
//只需要满足一个即可。
}
else
{
ruleContentFormRef
.
value
?.
ruleFormRef
?.
clearValidate
([
'builtInRuleCode'
,
'regularExpression'
,
'hitRate'
]);
// 先清除验证
// 去 验证 字段识别
if
(
!
validateRuleField
(
true
))
{
return
;
}
submitLabel
();
}
})
}
}
else
{
ruleContentFormRef
.
value
?.
ruleFormRef
?.
validate
((
valid
,
errorItem
)
=>
{
if
(
valid
)
{
submitLabel
();
//只需要满足一个即可。
}
else
{
if
(
!
validateRuleField
(
false
))
{
return
;
}
submitLabel
();
}
})
}
}
}
}
});
const
formRows
=
ref
([
{
matchValue
:
''
,
position
:
''
,
name
:
''
,
disabled
:
false
},
// 初始行
]);
// 位置选项
const
positionOptions
=
[
{
label
:
'前面'
,
value
:
'B'
},
{
label
:
'后面'
,
value
:
'A'
},
{
label
:
'任意位置'
,
value
:
'C'
},
];
// 语言options
const
languageOptions
=
[
{
label
:
'中文名'
,
value
:
'chName'
},
{
label
:
'英文名'
,
value
:
'enName'
},
];
// 新增行
const
addRow
=
()
=>
{
formRows
.
value
.
push
({
matchValue
:
''
,
position
:
''
,
name
:
''
,
disabled
:
false
});
};
// 删除行
const
deleteRow
=
(
index
:
number
)
=>
{
formRows
.
value
.
splice
(
index
,
1
);
};
const
matchChValue
=
ref
({
value
:
''
,
disabled
:
false
})
const
matchEnValue
=
ref
({
value
:
''
,
disabled
:
false
})
const
tabsInfo
=
ref
({
activeName
:
'labelRuleField'
,
tabs
:
[
{
label
:
'字段识别'
,
name
:
'labelRuleField'
},
{
label
:
'内容识别'
,
name
:
'labelRuleContent'
},
]
});
const
tabChange
=
(
val
)
=>
{
tabsInfo
.
value
.
activeName
=
val
;
}
const
handleCreate
=
()
=>
{
newCreateLabelFormItems
.
value
.
forEach
(
item
=>
{
if
(
item
.
children
?.
length
)
{
item
.
children
[
0
].
default
=
''
item
.
children
[
1
].
default
=
''
}
else
{
if
(
item
.
field
==
'matchType'
)
{
item
.
default
=
1
;
}
else
{
item
.
default
=
''
;
}
}
});
matchChValue
.
value
.
value
=
''
;
matchChValue
.
value
.
disabled
=
false
;
matchEnValue
.
value
.
value
=
''
;
matchEnValue
.
value
.
disabled
=
false
;
formRows
.
value
=
[{
matchValue
:
''
,
position
:
''
,
name
:
''
,
disabled
:
false
}];
tabsInfo
.
value
.
activeName
=
'labelRuleField'
;
ruleContentFormItems
.
value
[
1
].
visible
=
true
;
ruleContentFormItems
.
value
[
2
].
visible
=
false
;
ruleContentFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
item
.
field
==
'ruleType'
?
1
:
''
;
if
(
item
.
field
==
'regularTestData'
)
{
item
.
validateMsg
=
null
;
}
});
newCreateLabelDialogInfo
.
value
.
title
=
'添加标签'
;
newCreateLabelDialogInfo
.
value
.
visible
=
true
;
newCreateLabelDialogInfo
.
value
.
type
=
'add'
;
newCreateLabelDialogInfo
.
value
.
submitBtnLoading
=
false
;
}
/** 规则配置的内容识别内容表单配置 */
const
ruleContentFormItems
=
ref
([{
label
:
'规则类型'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'ruleType'
,
default
:
1
,
options
:
[{
value
:
1
,
label
:
'内置规则'
},
{
value
:
2
,
label
:
'自定义规则'
}],
props
:
{
label
:
"label"
,
value
:
"value"
,
},
required
:
true
,
filterable
:
true
,
clearable
:
false
,
block
:
true
,
visible
:
true
,
},
{
label
:
'内置规则'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'builtInRuleCode'
,
default
:
''
,
options
:
builtInRuleList
.
value
,
props
:
{
label
:
"label"
,
value
:
"value"
,
},
required
:
true
,
filterable
:
true
,
clearable
:
true
,
block
:
true
,
visible
:
true
,
},
{
label
:
'规则表达式'
,
type
:
'input'
,
placeholder
:
'请输入正则表达式'
,
field
:
'regularExpression'
,
default
:
''
,
required
:
true
,
filterable
:
true
,
clearable
:
true
,
block
:
true
,
visible
:
false
,
},
{
label
:
'测试验证'
,
type
:
'input'
,
placeholder
:
'请输入测试数据'
,
field
:
'regularTestData'
,
default
:
''
,
required
:
false
,
clearable
:
true
,
col
:
'mb8'
,
block
:
true
,
visible
:
true
,
validateBtn
:
{
value
:
'validate'
,
label
:
'验证'
,
click
:
()
=>
{
let
info
=
ruleContentFormRef
.
value
.
formInline
;
if
(
info
.
ruleType
==
1
&&
!
info
.
builtInRuleCode
)
{
proxy
.
$ElMessage
.
error
(
'请先选择内置规则'
);
return
;
}
if
(
info
.
ruleType
==
2
&&
!
info
.
regularExpression
)
{
proxy
.
$ElMessage
.
error
(
'请先输入规则表达式'
);
return
;
}
if
(
!
info
.
regularTestData
)
{
proxy
.
$ElMessage
.
error
(
'请先输入测试数据'
);
return
;
}
let
exp
=
info
.
ruleType
==
1
?
builtInRuleList
.
value
.
find
(
b
=>
b
.
value
==
info
.
builtInRuleCode
)?.
remarks
:
info
.
regularExpression
;
let
result
=
exp
&&
new
RegExp
(
exp
).
test
(
info
.
regularTestData
);
ruleContentFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
info
[
item
.
field
];
if
(
item
.
field
==
'regularTestData'
)
{
item
.
validateMsg
=
result
?
{
status
:
'success'
,
msg
:
'符合识别规则'
}
:
{
status
:
'error'
,
msg
:
'不符合识别规则'
}
}
});
}
},
validateMsg
:
<
any
>
null
},
{
label
:
'命中率设置(%)'
,
type
:
'input'
,
placeholder
:
'请输入1~100'
,
field
:
'hitRate'
,
min
:
1
,
max
:
100
,
inputType
:
'integerNumber'
,
default
:
null
,
required
:
true
,
block
:
true
,
clearable
:
true
,
visible
:
true
,
width
:
'100px'
,
beforeMsg
:
'一列数据中的非空数据,大于等于'
,
afterMsg
:
' %的数据符合以上识别条件,则认为命中该识别规则。'
}]);
/** 规则配置的内容识别内容表单规则配置 */
const
ruleContentFormRules
=
ref
({
builtInRuleCode
:
[
required
(
'请选择内置规则'
)],
regularExpression
:
[
required
(
'请输入正则表达式'
),
regexpRuleValidate
()],
hitRate
:
[
required
(
' '
)]
});
const
handleRuleContentSelectChange
=
(
val
,
row
,
info
)
=>
{
if
(
row
.
field
==
'ruleType'
)
{
ruleContentFormItems
.
value
[
1
].
visible
=
val
==
1
;
ruleContentFormItems
.
value
[
2
].
visible
=
val
==
2
;
ruleContentFormItems
.
value
.
forEach
(
item
=>
{
item
.
default
=
info
[
item
.
field
];
})
}
}
const
handleRuleContentInputChange
=
(
val
,
row
)
=>
{
if
(
row
.
field
==
'regularTestData'
)
{
let
item
=
ruleContentFormItems
.
value
.
at
(
-
2
);
item
&&
(
item
.
validateMsg
=
null
);
}
}
onBeforeMount
(()
=>
{
toSearch
({});
getParamsList
({
dictType
:
"标签类型"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
labelTypeList
.
value
=
res
.
data
||
[];
let
item
=
newCreateLabelFormItems
.
value
.
find
(
item
=>
item
.
field
==
'labelgroup'
);
item
&&
(
item
.
children
[
0
].
options
=
labelTypeList
.
value
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
getParamsList
({
dictType
:
"内置规则"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
builtInRuleList
.
value
=
res
.
data
||
[];
ruleContentFormItems
.
value
[
1
].
options
=
builtInRuleList
.
value
;
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
})
</
script
>
<
template
>
<div
class=
"container_wrap"
>
<div
class=
"table_tool_wrap"
>
<!-- 头部搜索 -->
<TableTools
:searchItems=
"searchItemList"
:searchId=
"'data-label-search'"
@
search=
"toSearch"
:init=
"false"
/>
<div
class=
"tools_btns"
>
<el-button
type=
"primary"
@
click=
"handleCreate"
>
新建
</el-button>
</div>
</div>
<div
class=
"table_panel_wrap"
>
<!-- 右侧标签管理表格 -->
<Table
:tableInfo=
"tableInfo"
@
tablePageChange=
"tablePageChange"
@
tableSwitchBeforeChange=
"tableSwitchBeforeChange"
/>
</div>
<!-- 新建编辑标签对话框 -->
<Dialog
_form
ref=
"dialogLabelFormRef"
:dialogConfigInfo=
"newCreateLabelDialogInfo"
class=
"v-dialog-form"
>
<template
v-slot:default
>
<Tabs
:tabs-info=
"tabsInfo"
@
tab-change=
"tabChange"
style=
"margin-top: -20px;"
/>
<div
v-show=
"tabsInfo.activeName == 'labelRuleField'"
>
<div>
<div
class=
"dim-label"
>
<span
class=
"front"
>
精确匹配
</span>
<el-icon>
<Warning
/>
</el-icon>
<span
class=
"tip"
>
精确匹配使用英文","分隔每个规则
</span>
</div>
<div
class=
"v-match"
>
<el-input
v-model=
"matchChValue.value"
maxlength=
"200"
style=
"width: 272px;height:94px;"
show-word-limit
type=
"textarea"
class=
"no-resize"
placeholder=
"请输入字段中文"
/>
<el-input
v-model=
"matchEnValue.value"
maxlength=
"200"
style=
"width: 272px;height:94px;"
show-word-limit
type=
"textarea"
class=
"no-resize"
placeholder=
"请输入字段英文"
/>
</div>
</div>
<div
class=
"dim-label"
style=
"margin-top: 16px;"
>
<span
class=
"front"
>
模糊匹配
</span>
<el-icon>
<Warning
/>
</el-icon>
<span
class=
"tip"
>
模糊匹配是或的关系,可配置多个模糊匹配规则
</span>
</div>
<!-- 渲染行 -->
<div
v-for=
"(row, index) in formRows"
:key=
"index"
class=
"match-content-wrapper"
>
<div
class=
"match-content"
>
<!-- 位置映射下拉框 -->
<el-select
v-model=
"row.name"
placeholder=
"请选择"
class=
"v-select"
>
<el-option
v-for=
"option in languageOptions"
:key=
"option.value"
:label=
"option.label"
:value=
"option.value"
/>
</el-select>
<el-select
v-model=
"row.position"
placeholder=
"请选择位置"
class=
"v-select"
>
<el-option
v-for=
"option in positionOptions"
:key=
"option.value"
:label=
"option.label"
:value=
"option.value"
/>
</el-select>
<el-input
v-model=
"row.matchValue"
class=
"v-input"
placeholder=
"请输入匹配值"
/>
<!-- 删除按钮 -->
<el-button
class=
"extra-icon"
:icon=
"Delete"
@
click=
"deleteRow(index)"
circle
style=
"margin-left: 8px;"
/>
</div>
</div>
<!-- 新增按钮 -->
<div
class=
"add-Icon"
@
click=
"addRow"
>
<el-icon
class=
"icon-add"
color=
"#4fa1a4"
:size=
"30"
>
<CirclePlus
/>
</el-icon>
<span
class=
"word-des"
>
模糊匹配规则
</span>
</div>
</div>
<div
v-show=
"tabsInfo.activeName == 'labelRuleContent'"
>
<Form
ref=
"ruleContentFormRef"
:itemList=
"ruleContentFormItems"
formId=
"rule-content-form"
:rules=
"ruleContentFormRules"
@
select-change=
"handleRuleContentSelectChange"
@
inputChange=
"handleRuleContentInputChange"
/>
</div>
</
template
>
<
/Dialog_form>
</div>
</template>
<
style
scoped
lang=
"scss"
>
.table_tool_wrap
{
width
:
100%
;
height
:
84px
!important
;
padding
:
0
8px
;
.tools_btns
{
padding
:
0px
0
0
;
}
}
.table_panel_wrap
{
width
:
100%
;
height
:
calc
(
100%
-
84px
);
padding
:
0px
8px
0
;
}
:deep
(
.v-dialog-form
)
{
.title-label
{
font-size
:
16px
;
color
:
#212121
;
line-height
:
24px
;
font-weight
:
600
;
}
.el-dialog__body
{
height
:
480px
;
overflow
:
auto
;
}
.dim-label
{
height
:
10px
;
display
:
flex
;
align-items
:
center
;
margin-top
:
6px
;
.el-icon
svg
{
height
:
16px
;
width
:
16px
;
color
:
#999999
;
}
.front
{
margin-right
:
16px
;
}
.tip
{
margin-left
:
4px
;
font-size
:
12px
;
color
:
#999999
;
}
}
.match-content-wrapper
{
width
:
100%
;
.match-content
{
display
:
flex
;
align-items
:
center
;
margin-top
:
8px
;
.v-select
{
margin-right
:
8px
;
width
:
33%
;
}
.v-input
{
width
:
calc
(
33%
-
50px
);
}
.extra-icon
{
transition
:
opacity
1s
;
display
:
none
;
}
&
:hover
{
.extra-icon
{
display
:
flex
;
}
}
}
}
.add-Icon
{
display
:
flex
;
align-items
:
center
;
margin-top
:
4px
;
width
:
50%
;
margin-left
:
-4px
;
.el-icon
svg
{
height
:
19px
;
width
:
19px
;
}
.word-des
{
color
:
#4fa1a4
}
}
.v-match
{
display
:
flex
;
justify-content
:
space-between
;
margin-top
:
10px
;
margin-bottom
:
10px
;
}
.no-resize
{
height
:
94px
;
.el-textarea__inner
{
min-height
:
94px
!important
;
resize
:
none
;
}
}
}
</
style
>
\ No newline at end of file
src/views/data_anonymization/resultProcess.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: resultProcess
</route>
<
script
lang=
"ts"
setup
name=
"resultProcess"
>
import
TableTools
from
"@/components/Tools/table_tools.vue"
;
import
{
commonPageConfig
}
from
'@/components/PageNav/index'
;
import
{
TableColumnWidth
}
from
"@/utils/enum"
;
import
{
dataSourceTypeList
,
getAnonTaskList
,
deleteAnonTask
,
}
from
'@/api/modules/dataAnonymization'
;
import
{
useValidator
}
from
'@/hooks/useValidator'
;
import
useDataAnonymizationStore
from
"@/store/modules/dataAnonymization"
;
const
anonymizationStore
=
useDataAnonymizationStore
();
const
router
=
useRouter
()
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
{
required
}
=
useValidator
();
const
searchItemList
=
ref
([{
type
:
"input"
,
label
:
""
,
field
:
"taskName"
,
default
:
""
,
placeholder
:
"数据集名称"
,
clearable
:
true
,
},
{
type
:
"select"
,
label
:
""
,
field
:
"dataSource"
,
default
:
null
,
options
:
dataSourceTypeList
,
placeholder
:
"数据来源"
,
clearable
:
true
,
filterable
:
true
,
}])
/** 分页及搜索传参信息配置。 */
const
page
=
ref
({
...
commonPageConfig
,
taskName
:
''
,
dataSource
:
null
});
const
tableInfo
=
ref
({
id
:
'data-file-table'
,
fields
:
[
{
label
:
"序号"
,
type
:
"index"
,
width
:
TableColumnWidth
.
INDEX
,
align
:
"center"
},
{
label
:
"数据集名称"
,
field
:
"taskName"
,
width
:
160
},
{
label
:
"数据来源"
,
field
:
"dataSource"
,
width
:
100
,
getName
:
(
scope
)
=>
{
return
scope
.
row
.
dataSource
&&
dataSourceTypeList
.
find
(
f
=>
f
.
value
==
scope
.
row
.
dataSource
)?.
label
||
'--'
;
}
},
{
label
:
"任务状态"
,
field
:
"sensitiveIdentifyTaskStatus"
,
width
:
TableColumnWidth
.
STATE
,
align
:
'center'
,
type
:
"tag"
},
{
label
:
"导出时间"
,
field
:
"exportTime"
,
width
:
TableColumnWidth
.
DATETIME
},
{
label
:
"修改人"
,
field
:
"updateUserName"
,
width
:
TableColumnWidth
.
USERNAME
},
{
label
:
"修改时间"
,
field
:
"updateTime"
,
width
:
TableColumnWidth
.
DATETIME
},
],
data
:
[],
page
:
{
type
:
"normal"
,
rows
:
0
,
...
page
.
value
,
},
loading
:
false
,
actionInfo
:
{
label
:
"操作"
,
type
:
"btn"
,
width
:
230
,
fixed
:
'right'
,
btns
:
(
scope
)
=>
{
return
[{
label
:
"编辑"
,
value
:
"edit"
,
click
:
(
scope
)
=>
{
router
.
push
({
name
:
'anonTaskCreate'
,
query
:
{
guid
:
scope
.
row
.
guid
,
taskName
:
scope
.
row
.
taskName
}
});
}
},
{
label
:
'查看报告'
,
value
:
'report'
,
disabled
:
scope
.
row
.
status
!=
'Y'
,
click
:
(
scope
)
=>
{
router
.
push
({
name
:
'anonResultReportView'
,
query
:
{
guid
:
scope
.
row
.
guid
,
execGuid
:
scope
.
row
.
lastExecGuid
,
taskName
:
scope
.
row
.
taskName
}
});
}
},
{
label
:
'查看数据'
,
value
:
'view'
,
disabled
:
scope
.
row
.
status
!=
'Y'
||
scope
.
row
.
handleType
==
'02'
,
click
:
(
scope
)
=>
{
router
.
push
({
name
:
'anonResultView'
,
query
:
{
guid
:
scope
.
row
.
guid
,
execGuid
:
scope
.
row
.
lastExecGuid
,
taskName
:
scope
.
row
.
taskName
}
});
}
},
{
label
:
"删除"
,
value
:
"delete"
,
click
:
(
scope
)
=>
{
proxy
.
$openMessageBox
(
"此操作将永久删除, 是否继续?"
,
()
=>
{
let
guids
=
[
scope
.
row
.
guid
];
deleteAnonTask
(
guids
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
page
.
value
.
curr
=
1
;
getTableData
();
proxy
.
$ElMessage
({
type
:
"success"
,
message
:
"删除成功"
,
});
}
else
{
proxy
.
$ElMessage
({
type
:
"error"
,
message
:
res
.
msg
,
});
}
});
})
}
}]
}
}
})
const
toSearch
=
(
val
:
any
,
clear
:
boolean
=
false
)
=>
{
if
(
clear
)
{
searchItemList
.
value
.
map
((
item
)
=>
(
item
.
default
=
""
));
page
.
value
.
taskName
=
''
;
page
.
value
.
dataSource
=
null
;
}
else
{
page
.
value
.
taskName
=
val
.
taskName
;
page
.
value
.
dataSource
=
val
.
dataSource
;
}
getTableData
();
};
const
getTableData
=
()
=>
{
tableInfo
.
value
.
loading
=
true
getAnonTaskList
({
pageIndex
:
page
.
value
.
curr
,
pageSize
:
page
.
value
.
limit
,
taskName
:
page
.
value
.
taskName
,
dataSource
:
page
.
value
.
dataSource
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
const
data
=
res
.
data
||
{};
tableInfo
.
value
.
data
=
data
.
records
?.
map
(
d
=>
{
d
.
sensitiveIdentifyTaskStatus
=
d
.
status
;
return
d
;
})
||
[]
tableInfo
.
value
.
page
.
limit
=
data
.
pageSize
tableInfo
.
value
.
page
.
curr
=
data
.
pageIndex
tableInfo
.
value
.
page
.
rows
=
data
.
totalRows
}
else
{
proxy
.
$ElMessage
({
type
:
'error'
,
message
:
res
.
msg
,
})
}
tableInfo
.
value
.
loading
=
false
})
};
const
tablePageChange
=
(
info
)
=>
{
page
.
value
.
curr
=
Number
(
info
.
curr
);
page
.
value
.
limit
=
Number
(
info
.
limit
);
getTableData
();
};
const
handleCreate
=
()
=>
{
router
.
push
({
name
:
'anonTaskCreate'
});
}
onBeforeMount
(()
=>
{
toSearch
({});
anonymizationStore
?.
setIsAnonPageRefresh
?.(
false
);
})
onActivated
(()
=>
{
if
(
anonymizationStore
.
isAnonPageRefresh
)
{
//如果是首次加载,则不需要调用
page
.
value
.
curr
=
1
;
getTableData
();
anonymizationStore
.
setIsAnonPageRefresh
(
false
);
}
});
</
script
>
<
template
>
<div
class=
"container_wrap"
>
<div
class=
"table_tool_wrap"
>
<!-- 头部搜索 -->
<TableTools
:searchItems=
"searchItemList"
:searchId=
"'data-label-search'"
@
search=
"toSearch"
:init=
"false"
/>
<div
class=
"tools_btns"
>
<el-button
type=
"primary"
@
click=
"handleCreate"
>
新建
</el-button>
</div>
</div>
<div
class=
"table_panel_wrap"
>
<!-- 右侧标签管理表格 -->
<Table
:tableInfo=
"tableInfo"
@
tablePageChange=
"tablePageChange"
/>
</div>
</div>
</
template
>
<
style
lang=
"scss"
scoped
>
.table_tool_wrap
{
width
:
100%
;
height
:
84px
!important
;
padding
:
0
8px
;
.tools_btns
{
padding
:
0px
0
0
;
}
}
.table_panel_wrap
{
width
:
100%
;
height
:
calc
(
100%
-
84px
);
padding
:
0px
8px
0
;
}
</
style
>
\ No newline at end of file
src/views/data_anonymization/sensitiveIdentify.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: sensitiveIdentify
</route>
<
script
lang=
"ts"
setup
name=
"sensitiveIdentify"
>
import
TableTools
from
"@/components/Tools/table_tools.vue"
;
import
{
commonPageConfig
}
from
'@/components/PageNav/index'
;
import
{
TableColumnWidth
}
from
"@/utils/enum"
;
import
{
getSensitiveDataTaskList
,
dataSourceTypeList
,
deleteSensitiveDataTask
,
saveSensitiveDataTask
,
updateSensitiveDataTask
,
getDatabase
,
execSensitiveDataTask
,
}
from
'@/api/modules/dataAnonymization'
;
import
{
useValidator
}
from
'@/hooks/useValidator'
;
const
router
=
useRouter
()
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
{
required
}
=
useValidator
();
const
searchItemList
=
ref
([{
type
:
"input"
,
label
:
""
,
field
:
"taskName"
,
default
:
""
,
placeholder
:
"任务名称"
,
clearable
:
true
,
},
{
type
:
"select"
,
label
:
""
,
field
:
"dataSource"
,
default
:
null
,
options
:
dataSourceTypeList
,
placeholder
:
"数据来源"
,
clearable
:
true
,
filterable
:
true
,
}])
/** 分页及搜索传参信息配置。 */
const
page
=
ref
({
...
commonPageConfig
,
taskName
:
''
,
dataSource
:
null
});
const
currTableData
=
ref
();
const
tableInfo
=
ref
({
id
:
'data-file-table'
,
fields
:
[
{
label
:
"序号"
,
type
:
"index"
,
width
:
TableColumnWidth
.
INDEX
,
align
:
"center"
},
{
label
:
"任务名称"
,
field
:
"taskName"
,
width
:
170
},
{
label
:
"数据来源"
,
field
:
"dataSource"
,
width
:
100
,
getName
:
(
scope
)
=>
{
return
scope
.
row
.
dataSource
&&
dataSourceTypeList
.
find
(
f
=>
f
.
value
==
scope
.
row
.
dataSource
)?.
label
||
'--'
;
}
},
{
label
:
"任务状态"
,
field
:
"sensitiveIdentifyTaskStatus"
,
width
:
TableColumnWidth
.
STATE
,
align
:
'center'
,
type
:
"tag"
},
{
label
:
"执行人"
,
field
:
"execUserName"
,
width
:
100
},
{
label
:
"执行时间"
,
field
:
"execTime"
,
width
:
170
},
{
label
:
"修改人"
,
field
:
"updateUserName"
,
width
:
100
},
{
label
:
"修改时间"
,
field
:
"updateTime"
,
width
:
170
},
],
data
:
[],
page
:
{
type
:
"normal"
,
rows
:
0
,
...
page
.
value
,
},
loading
:
false
,
actionInfo
:
{
label
:
"操作"
,
type
:
"btn"
,
width
:
304
,
fixed
:
'right'
,
btns
:
(
scope
)
=>
{
return
[{
label
:
'敏感数据查看'
,
value
:
'view'
,
disabled
:
scope
.
row
.
status
!=
'Y'
,
click
:
(
scope
)
=>
{
router
.
push
({
name
:
'sensitiveIdentifyConfig'
,
query
:
{
guid
:
scope
.
row
.
guid
,
execGuid
:
scope
.
row
.
execGuid
,
taskName
:
scope
.
row
.
taskName
}
});
}
},
{
label
:
'手动执行'
,
value
:
'execute'
,
disabled
:
scope
.
row
.
status
==
'R'
,
click
:
(
scope
)
=>
{
execSensitiveDataTask
(
scope
.
row
.
guid
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
getTableData
();
proxy
.
$ElMessage
.
success
(
'该任务手动执行提交成功'
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
},
{
label
:
"编辑"
,
value
:
"edit"
,
disabled
:
scope
.
row
.
status
==
'R'
,
click
:
(
scope
)
=>
{
let
row
=
scope
.
row
;
currTableData
.
value
=
row
;
newCreateTaskDialogInfo
.
value
.
visible
=
true
;
newCreateTaskDialogInfo
.
value
.
title
=
'编辑数据敏感识别任务'
;
newCreateTaskDialogInfo
.
value
.
type
=
'edit'
;
newCreateTaskFormItems
.
value
[
0
].
default
=
row
.
taskName
;
newCreateTaskFormItems
.
value
[
1
].
default
=
row
.
dataSource
;
newCreateTaskFormItems
.
value
[
2
].
default
=
row
.
dataSourceGuid
||
''
;
newCreateTaskFormItems
.
value
[
2
].
visible
=
row
.
dataSource
==
1
;
newCreateTaskFormItems
.
value
[
3
].
default
=
row
.
filePath
||
[];
newCreateTaskFormItems
.
value
[
3
].
visible
=
row
.
dataSource
==
2
;
}
},
{
label
:
"删除"
,
value
:
"delete"
,
disabled
:
scope
.
row
.
status
==
'R'
,
click
:
(
scope
)
=>
{
proxy
.
$openMessageBox
(
"此操作将永久删除, 是否继续?"
,
()
=>
{
let
guids
=
[
scope
.
row
.
guid
];
deleteSensitiveDataTask
(
guids
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
getTableData
();
proxy
.
$ElMessage
({
type
:
"success"
,
message
:
"删除成功"
,
});
}
else
{
proxy
.
$ElMessage
({
type
:
"error"
,
message
:
res
.
msg
,
});
}
});
})
}
},
{
label
:
'日志'
,
value
:
'log'
,
click
:
(
scope
)
=>
{
router
.
push
({
name
:
'sensitiveIdentifyTaskExecLog'
,
query
:
{
guid
:
scope
.
row
.
guid
,
name
:
scope
.
row
.
taskName
,
}
});
}
}]
}
}
})
const
toSearch
=
(
val
:
any
,
clear
:
boolean
=
false
)
=>
{
if
(
clear
)
{
searchItemList
.
value
.
map
((
item
)
=>
(
item
.
default
=
""
));
page
.
value
.
taskName
=
''
;
page
.
value
.
dataSource
=
null
;
}
else
{
page
.
value
.
taskName
=
val
.
taskName
;
page
.
value
.
dataSource
=
val
.
dataSource
;
}
getTableData
();
};
const
getTableData
=
()
=>
{
tableInfo
.
value
.
loading
=
true
getSensitiveDataTaskList
({
pageIndex
:
page
.
value
.
curr
,
pageSize
:
page
.
value
.
limit
,
taskName
:
page
.
value
.
taskName
,
dataSource
:
page
.
value
.
dataSource
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
const
data
=
res
.
data
||
{};
tableInfo
.
value
.
data
=
data
.
records
?.
map
(
d
=>
{
d
.
sensitiveIdentifyTaskStatus
=
d
.
status
;
return
d
;
})
||
[]
tableInfo
.
value
.
page
.
limit
=
data
.
pageSize
tableInfo
.
value
.
page
.
curr
=
data
.
pageIndex
tableInfo
.
value
.
page
.
rows
=
data
.
totalRows
}
else
{
proxy
.
$ElMessage
({
type
:
'error'
,
message
:
res
.
msg
,
})
}
tableInfo
.
value
.
loading
=
false
})
};
const
tablePageChange
=
(
info
)
=>
{
page
.
value
.
curr
=
Number
(
info
.
curr
);
page
.
value
.
limit
=
Number
(
info
.
limit
);
getTableData
();
};
const
dataSourceList
=
ref
([]);
const
newCreateTaskFormItems
=
ref
([{
label
:
'任务名称'
,
type
:
'input'
,
placeholder
:
'请输入'
,
field
:
'taskName'
,
maxlength
:
15
,
default
:
''
,
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
true
,
},
{
label
:
'数据来源'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'dataSource'
,
default
:
1
,
options
:
dataSourceTypeList
,
props
:
{
label
:
"label"
,
value
:
"value"
,
},
required
:
true
,
filterable
:
true
,
clearable
:
true
,
visible
:
true
,
},
{
label
:
'数据源'
,
type
:
'select'
,
placeholder
:
'请选择'
,
field
:
'dataSourceGuid'
,
default
:
''
,
options
:
dataSourceList
.
value
,
props
:
{
label
:
'databaseNameZh'
,
value
:
'guid'
},
filterable
:
true
,
visible
:
true
,
required
:
true
},
{
label
:
'文件上传'
,
tip
:
'支持扩展名:xlsx、xls、csv,文件大小不超过10MB'
,
type
:
'upload-file'
,
accept
:
'.xlsx, .xls, .csv'
,
limitSize
:
10
,
isExcel
:
true
,
required
:
true
,
default
:
<
any
>
[],
block
:
true
,
visible
:
false
,
field
:
'file'
,
}]);
const
newCreateTaskFormRules
=
ref
({
taskName
:
[
required
(
'请输入任务名称'
)],
dataSource
:
[
required
(
'请选择数据来源'
)],
dataSourceGuid
:
[
required
(
'请选择数据源'
)],
file
:
[{
validator
:
(
rule
:
any
,
value
:
any
,
callback
:
any
)
=>
{
if
(
!
value
?.
length
)
{
callback
(
new
Error
(
'请上传文件'
))
}
else
{
callback
();
}
},
trigger
:
'change'
}]
});
const
newCreateTaskDialogInfo
=
ref
({
visible
:
false
,
size
:
550
,
title
:
"添加数据敏感识别任务"
,
type
:
""
,
formInfo
:
{
id
:
"label-form"
,
items
:
newCreateTaskFormItems
.
value
,
rules
:
newCreateTaskFormRules
.
value
,
},
submitBtnLoading
:
false
,
btns
:
{
cancel
:
()
=>
{
newCreateTaskDialogInfo
.
value
.
visible
=
false
;
newCreateTaskDialogInfo
.
value
.
submitBtnLoading
=
false
;
},
submit
:
(
btn
,
info
)
=>
{
let
params
=
Object
.
assign
({},
info
,
{
filePath
:
info
.
file
?.
map
(
f
=>
{
return
{
name
:
f
.
name
,
url
:
f
.
url
}
})
||
[]
});
delete
params
.
file
;
newCreateTaskDialogInfo
.
value
.
submitBtnLoading
=
true
;
if
(
newCreateTaskDialogInfo
.
value
.
type
==
'add'
)
{
saveSensitiveDataTask
(
params
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
success
(
'标签新建成功'
);
newCreateTaskDialogInfo
.
value
.
visible
=
false
;
newCreateTaskDialogInfo
.
value
.
submitBtnLoading
=
false
;
page
.
value
.
curr
=
1
;
getTableData
();
}
else
{
newCreateTaskDialogInfo
.
value
.
submitBtnLoading
=
false
;
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
}
else
{
newCreateTaskDialogInfo
.
value
.
submitBtnLoading
=
true
;
params
.
guid
=
currTableData
.
value
.
guid
;
updateSensitiveDataTask
(
params
).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
success
(
'标签编辑成功'
);
newCreateTaskDialogInfo
.
value
.
visible
=
false
;
newCreateTaskDialogInfo
.
value
.
submitBtnLoading
=
false
;
getTableData
();
}
else
{
newCreateTaskDialogInfo
.
value
.
submitBtnLoading
=
false
;
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
}
}
}
});
const
handleTaskSelectChange
=
(
val
,
row
,
item
)
=>
{
if
(
item
.
field
==
'dataSource'
)
{
newCreateTaskFormItems
.
value
[
0
].
default
=
row
.
taskName
;
newCreateTaskFormItems
.
value
[
1
].
default
=
val
;
newCreateTaskFormItems
.
value
[
2
].
default
=
row
.
dataSourceGuid
||
''
;
newCreateTaskFormItems
.
value
[
2
].
visible
=
val
==
1
;
newCreateTaskFormItems
.
value
[
3
].
default
=
row
.
file
||
[];
newCreateTaskFormItems
.
value
[
3
].
visible
=
val
==
2
;
}
}
const
handleCreate
=
()
=>
{
newCreateTaskDialogInfo
.
value
.
visible
=
true
;
newCreateTaskDialogInfo
.
value
.
title
=
'添加数据敏感识别任务'
;
newCreateTaskDialogInfo
.
value
.
type
=
'add'
;
newCreateTaskFormItems
.
value
[
0
].
default
=
''
;
newCreateTaskFormItems
.
value
[
1
].
default
=
1
;
newCreateTaskFormItems
.
value
[
2
].
default
=
''
;
newCreateTaskFormItems
.
value
[
2
].
visible
=
true
;
newCreateTaskFormItems
.
value
[
3
].
default
=
[];
newCreateTaskFormItems
.
value
[
3
].
visible
=
false
;
}
onBeforeMount
(()
=>
{
toSearch
({});
getDatabase
({
connectStatus
:
1
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
dataSourceList
.
value
=
res
.
data
||
[];
newCreateTaskFormItems
.
value
[
2
].
options
=
dataSourceList
.
value
;
}
else
{
proxy
.
$ElMessage
({
type
:
"error"
,
message
:
res
.
msg
,
});
}
})
})
</
script
>
<
template
>
<div
class=
"container_wrap"
>
<div
class=
"table_tool_wrap"
>
<!-- 头部搜索 -->
<TableTools
:searchItems=
"searchItemList"
:searchId=
"'data-label-search'"
@
search=
"toSearch"
:init=
"false"
/>
<div
class=
"tools_btns"
>
<el-button
type=
"primary"
@
click=
"handleCreate"
>
新建
</el-button>
</div>
</div>
<div
class=
"table_panel_wrap"
>
<!-- 右侧标签管理表格 -->
<Table
:tableInfo=
"tableInfo"
@
tablePageChange=
"tablePageChange"
/>
</div>
<Dialog
_form
ref=
"dialogTaskFormRef"
:dialogConfigInfo=
"newCreateTaskDialogInfo"
@
formDialogSelectChange=
"handleTaskSelectChange"
>
<
/Dialog_form>
</div>
</
template
>
<
style
lang=
"scss"
scoped
>
.table_tool_wrap
{
width
:
100%
;
height
:
84px
!important
;
padding
:
0
8px
;
.tools_btns
{
padding
:
0px
0
0
;
}
}
.table_panel_wrap
{
width
:
100%
;
height
:
calc
(
100%
-
84px
);
padding
:
0px
8px
0
;
}
</
style
>
\ No newline at end of file
src/views/data_anonymization/sensitiveIdentifyConfig.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: sensitiveIdentifyConfig
</route>
<
script
lang=
"ts"
setup
name=
"sensitiveIdentifyConfig"
>
import
{
getExecSensitiveTable
,
getExecSensitiveFieldTable
,
getExecSensitiveFieldColumnListByCondition
,
getDataLabelList
,
getStatisticsNum
,
getParamsList
,
updateSensitiveDataTaskFieldLabel
,
batchUpdateSensitiveDataTaskFieldStatus
,
confirmTaskStatus
,
}
from
'@/api/modules/dataAnonymization'
;
import
PageNav
from
"@/components/PageNav/index.vue"
;
import
{
commonPageConfig
}
from
'@/components/PageNav/index'
;
import
{
ElMessageBox
}
from
'element-plus'
;
import
{
changeNum
}
from
"@/utils/common"
;
import
BtnPopover
from
"@/components/Popover/index.vue"
;
import
useUserStore
from
"@/store/modules/user"
;
const
route
=
useRoute
();
const
router
=
useRouter
();
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
userStore
=
useUserStore
();
const
fullPath
=
route
.
fullPath
;
const
isLook
=
ref
(
route
.
query
.
isLook
==
'1'
);
const
fullScreenLoading
=
ref
(
false
);
const
treeInfo
=
ref
({
id
:
"data-list-tree"
,
filter
:
true
,
queryValue
:
""
,
queryPlaceholder
:
"请输入关键字搜索"
,
props
:
{
label
:
"label"
,
value
:
"value"
,
isLeaf
:
"isLeaf"
,
children
:
'tableList'
},
nodeKey
:
'value'
,
expandedKey
:
<
any
>
[],
currentNodeKey
:
''
,
data
:
[],
expandOnNodeClick
:
false
,
loading
:
false
,
currentObj
:
<
any
>
{}
});
const
treeRef
=
ref
();
const
nodeClick
=
(
data
,
node
)
=>
{
let
exec
=
()
=>
{
pageInfo
.
value
.
labelGuids
=
[];
pageInfo
.
value
.
labelTypeCodes
=
[];
pageInfo
.
value
.
confirmStatus
=
[];
treeInfo
.
value
.
currentNodeKey
=
data
.
value
;
treeInfo
.
value
.
currentObj
=
data
;
if
(
data
.
parent
)
{
pageInfo
.
value
.
databaseName
=
data
.
parent
;
pageInfo
.
value
.
tableName
=
data
.
tableName
;
}
else
{
pageInfo
.
value
.
databaseName
=
data
.
databaseName
;
pageInfo
.
value
.
tableName
=
''
;
}
pageInfo
.
value
.
curr
=
1
;
getSensitiveTableFieldData
();
getCntSumInfo
();
getSensitiveFieldLabelData
();
}
if
(
checkTableSave
())
{
ElMessageBox
.
confirm
(
'存在未保存的数据,确定放弃修改吗?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
).
then
(()
=>
{
exec
();
}).
catch
(()
=>
{
treeRef
.
value
.
setCurrentKey
(
treeInfo
.
value
.
currentObj
.
value
);
})
}
else
{
exec
();
}
}
const
batchConfirm
=
()
=>
{
if
(
!
selectTableFieldRows
.
value
.
length
)
{
proxy
.
$ElMessage
.
error
(
'请先勾选待确认的字段'
);
return
;
}
if
(
checkTableSave
())
{
ElMessageBox
.
confirm
(
'存在未保存的数据,确定放弃修改吗?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
).
then
(()
=>
{
batchUpdateSensitiveDataTaskFieldStatus
(
selectTableFieldRows
.
value
.
map
(
s
=>
s
.
guid
)).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
success
(
'批量确认字段成功'
);
getSensitiveTableFieldData
();
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
})
}
else
{
batchUpdateSensitiveDataTaskFieldStatus
(
selectTableFieldRows
.
value
.
map
(
s
=>
s
.
guid
)).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
success
(
'批量确认字段成功'
);
getSensitiveTableFieldData
();
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
}
/** 数量统计信息 */
const
cntSumInfo
:
any
=
ref
({});
const
getCntSumInfo
=
()
=>
{
getStatisticsNum
({
execGuid
:
route
.
query
.
execGuid
,
databaseName
:
pageInfo
.
value
.
databaseName
,
tableName
:
pageInfo
.
value
.
tableName
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
cntSumInfo
.
value
=
res
.
data
||
{};
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
const
dataSource
=
ref
(
null
);
const
getExecSensitiveTableData
=
()
=>
{
treeInfo
.
value
.
loading
=
true
;
getExecSensitiveTable
(
route
.
query
.
execGuid
).
then
((
res
:
any
)
=>
{
treeInfo
.
value
.
loading
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
treeInfo
.
value
.
data
=
res
.
data
?.
map
((
d
,
index
)
=>
{
d
.
value
=
`
${
d
.
databaseName
}
-ds`
;
//解决文件名称和文件的sheet名称一样的树形选中问题
d
.
label
=
d
.
databaseChName
;
if
(
index
==
0
)
{
dataSource
.
value
=
d
.
dataSource
;
}
d
.
tableList
=
d
.
tableList
?.
map
(
t
=>
{
t
.
value
=
`
${
d
.
databaseName
}
-
${
t
.
tableName
}
`
;
t
.
label
=
dataSource
.
value
==
1
?
(
t
.
tableChName
||
t
.
tableName
)
:
t
.
tableChName
;
t
.
parent
=
d
.
databaseName
;
return
t
;
})
||
[]
return
d
;
})
||
[];
let
treeData
:
any
=
treeInfo
.
value
.
data
||
[];
if
(
treeData
.
length
)
{
treeInfo
.
value
.
expandedKey
=
[
treeData
[
0
].
value
];
treeInfo
.
value
.
currentNodeKey
=
treeData
[
0
]?.
value
;
treeInfo
.
value
.
currentObj
=
treeData
[
0
];
treeData
[
0
].
tableList
?.[
0
]
&&
nodeClick
(
treeData
[
0
],
{});
}
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
/** 所有的标签列表供编辑下拉显示 */
const
allDataLabelList
:
any
=
ref
([]);
onBeforeMount
(()
=>
{
getExecSensitiveTableData
();
getDataLabelList
({
pageSize
:
-
1
,
bizState
:
'Y'
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
allDataLabelList
.
value
=
res
.
data
?.
records
||
[];
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
getParamsList
({
dictType
:
"标签类型"
,
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
labelTypeList
.
value
=
res
.
data
?.
map
(
d
=>
{
d
.
guid
=
d
.
value
;
return
d
;
})
||
[];
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
})
/** ------------------------- 敏感数据识别字段列表操作 --------------------------------- */
const
labelList
=
ref
([]);
/** 标签类型的字典列表 */
const
labelTypeList
:
any
=
ref
([]);
/** 查询数据的标签类型列表 */
const
labelTypeDataList
:
any
=
ref
([]);
/** 分页设置 */
const
pageInfo
=
ref
({
...
commonPageConfig
,
taskExecGuid
:
route
.
query
.
execGuid
,
databaseName
:
''
,
tableName
:
''
,
rows
:
0
,
labelGuids
:
[],
//列头筛选
labelTypeCodes
:
[],
confirmStatus
:
[]
});
/** 敏感数据 */
const
sensitiveTableData
:
any
=
ref
([]);
const
sensitiveTableDataLoading
=
ref
(
false
);
const
getSensitiveTableFieldData
=
()
=>
{
sensitiveTableDataLoading
.
value
=
true
;
let
confirmStatus
=
pageInfo
.
value
.
confirmStatus
||
[];
getExecSensitiveFieldTable
({
pageIndex
:
pageInfo
.
value
.
curr
,
pageSize
:
pageInfo
.
value
.
limit
,
taskExecGuid
:
pageInfo
.
value
.
taskExecGuid
,
databaseName
:
pageInfo
.
value
.
databaseName
,
tableName
:
pageInfo
.
value
.
tableName
,
labelGuids
:
pageInfo
.
value
.
labelGuids
,
labelTypeCodes
:
pageInfo
.
value
.
labelTypeCodes
,
confirmStatus
:
confirmStatus
?.
length
>
1
?
null
:
confirmStatus
[
0
]
}).
then
((
res
:
any
)
=>
{
sensitiveTableDataLoading
.
value
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
const
data
=
res
.
data
||
{};
sensitiveTableData
.
value
=
data
.
records
||
[];
pageInfo
.
value
.
limit
=
data
.
pageSize
pageInfo
.
value
.
curr
=
data
.
pageIndex
pageInfo
.
value
.
rows
=
data
.
totalRows
}
else
{
proxy
.
$ElMessage
({
type
:
'error'
,
message
:
res
.
msg
,
})
}
})
}
/** 获取当前选中的左侧库表下对应的数据标签数组,做列头筛选时使用。 */
const
getSensitiveFieldLabelData
=
(
info
?:
{
filterLabel
?:
Boolean
,
filterType
?:
Boolean
,
filterStatus
?:
Boolean
,
setLable
?:
Boolean
,
setType
?:
Boolean
,
setStatus
?:
Boolean
})
=>
{
getExecSensitiveFieldColumnListByCondition
({
pageSize
:
-
1
,
taskExecGuid
:
pageInfo
.
value
.
taskExecGuid
,
databaseName
:
pageInfo
.
value
.
databaseName
,
tableName
:
pageInfo
.
value
.
tableName
,
labelGuids
:
!
info
?.
filterLabel
?
[]
:
pageInfo
.
value
.
labelGuids
,
labelTypeCodes
:
!
info
?.
filterType
?
[]
:
pageInfo
.
value
.
labelTypeCodes
,
confirmStatus
:
!
info
?.
filterStatus
?
[]
:
pageInfo
.
value
.
confirmStatus
}).
then
((
res
:
any
)
=>
{
if
(
res
?.
code
==
proxy
.
$passCode
)
{
let
data
=
res
.
data
||
{};
(
info
?.
setLable
!==
false
||
!
pageInfo
.
value
.
labelGuids
?.
length
)
&&
(
labelList
.
value
=
data
[
'标签'
]?.
map
(
d
=>
{
d
.
guid
=
d
.
labelGuid
;
if
(
!
d
.
labelGuid
&&
!
d
.
labelName
)
{
d
.
guid
=
`
${
d
.
labelGuid
}
`
;
d
.
labelName
=
'--'
;
}
return
d
;
})
||
[]);
let
list
=
data
[
'确认状态'
];
if
(
info
?.
setStatus
!==
false
||
!
pageInfo
.
value
.
confirmStatus
?.
length
)
{
confirmStatusList
.
value
=
list
?.
map
(
d
=>
{
d
.
guid
=
d
.
confirmStatus
;
d
.
label
=
d
.
confirmStatus
==
'Y'
?
'已确认'
:
'未确认'
;
return
d
;
})
||
[]
};
(
info
?.
setType
!==
false
||
!
pageInfo
.
value
.
labelTypeCodes
?.
length
)
&&
(
labelTypeDataList
.
value
=
data
[
'标签类型'
]?.
map
(
d
=>
{
d
.
guid
=
d
.
labelTypeCode
;
if
(
!
d
.
labelTypeCode
)
{
d
.
guid
=
`
${
d
.
labelTypeCode
}
`
;
d
.
labelTypeName
=
'--'
;
}
return
d
;
})
||
[])
}
else
{
proxy
.
$ElMessage
({
type
:
'error'
,
message
:
res
.
msg
,
})
}
})
}
const
popoverLabelListInfo
=
computed
(()
=>
{
return
{
type
:
'checkbox-list-btns'
,
data
:
labelList
.
value
,
placeholder
:
'请输入关键字搜索'
,
scope
:
{
row
:
{}
},
placement
:
'right-start'
,
props
:
{
value
:
'guid'
,
label
:
'labelName'
},
checked
:
pageInfo
.
value
.
labelGuids
,
btn
:
{
value
:
'filter'
}
}
});
const
handleLabelPopoverClick
=
(
scope
)
=>
{
if
(
checkTableSave
())
{
ElMessageBox
.
confirm
(
'存在未保存的数据,确定放弃修改吗?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
).
then
(()
=>
{
pageInfo
.
value
.
labelGuids
=
scope
.
row
.
selectedData
||
[];
pageInfo
.
value
.
curr
=
1
;
getSensitiveTableFieldData
();
getSensitiveFieldLabelData
({
filterLabel
:
true
,
filterStatus
:
true
,
filterType
:
true
,
setLable
:
false
,
setStatus
:
true
,
setType
:
true
});
})
}
else
{
pageInfo
.
value
.
labelGuids
=
scope
.
row
.
selectedData
||
[];
pageInfo
.
value
.
curr
=
1
;
getSensitiveTableFieldData
();
getSensitiveFieldLabelData
({
filterLabel
:
true
,
filterStatus
:
true
,
filterType
:
true
,
setLable
:
false
,
setStatus
:
true
,
setType
:
true
});
}
}
const
popoverLabelTypeListInfo
=
computed
(()
=>
{
return
{
type
:
'checkbox-list-btns'
,
data
:
labelTypeDataList
.
value
,
placeholder
:
'请输入关键字搜索'
,
scope
:
{
row
:
{}
},
placement
:
'right-start'
,
props
:
{
value
:
'guid'
,
label
:
'labelTypeName'
},
checked
:
pageInfo
.
value
.
labelTypeCodes
,
btn
:
{
value
:
'filter'
}
}
});
const
handleLabelTypePopoverClick
=
(
scope
)
=>
{
if
(
checkTableSave
())
{
ElMessageBox
.
confirm
(
'存在未保存的数据,确定放弃修改吗?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
).
then
(()
=>
{
pageInfo
.
value
.
labelTypeCodes
=
scope
.
row
.
selectedData
||
[];
pageInfo
.
value
.
curr
=
1
;
getSensitiveTableFieldData
();
getSensitiveFieldLabelData
({
filterLabel
:
true
,
filterStatus
:
true
,
filterType
:
true
,
setLable
:
true
,
setStatus
:
true
,
setType
:
false
});
})
}
else
{
pageInfo
.
value
.
labelTypeCodes
=
scope
.
row
.
selectedData
||
[];
pageInfo
.
value
.
curr
=
1
;
getSensitiveTableFieldData
();
getSensitiveFieldLabelData
({
filterLabel
:
true
,
filterStatus
:
true
,
filterType
:
true
,
setLable
:
true
,
setStatus
:
true
,
setType
:
false
});
}
}
const
confirmStatusList
:
any
=
ref
([]);
const
popoverStatusListInfo
=
computed
(()
=>
{
return
{
type
:
'checkbox-list-btns'
,
data
:
confirmStatusList
.
value
,
placeholder
:
'请输入关键字搜索'
,
scope
:
{
row
:
{}
},
placement
:
'right-start'
,
props
:
{
value
:
'guid'
,
label
:
'label'
},
checked
:
pageInfo
.
value
.
confirmStatus
,
btn
:
{
value
:
'filter'
}
}
});
const
handleStatusPopoverClick
=
(
scope
)
=>
{
if
(
checkTableSave
())
{
ElMessageBox
.
confirm
(
'存在未保存的数据,确定放弃修改吗?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
).
then
(()
=>
{
pageInfo
.
value
.
confirmStatus
=
scope
.
row
.
selectedData
||
[];
pageInfo
.
value
.
curr
=
1
;
getSensitiveTableFieldData
();
getSensitiveFieldLabelData
({
filterLabel
:
true
,
filterStatus
:
true
,
filterType
:
true
,
setLable
:
true
,
setStatus
:
false
,
setType
:
true
});
})
}
else
{
pageInfo
.
value
.
confirmStatus
=
scope
.
row
.
selectedData
||
[];
pageInfo
.
value
.
curr
=
1
;
getSensitiveTableFieldData
();
getSensitiveFieldLabelData
({
filterLabel
:
true
,
filterStatus
:
true
,
filterType
:
true
,
setLable
:
true
,
setStatus
:
false
,
setType
:
true
});
}
}
/** 标签选择的值改变,标签类型跟着变化。 */
const
handleSelectChange
=
(
val
,
scope
)
=>
{
//暂时不处理,因为未保存会出现清空的场景,需要先存下之前的值。
let
label
=
allDataLabelList
.
value
.
find
(
label
=>
label
.
guid
==
val
);
if
(
label
)
{
scope
.
row
.
labelTypeCode
=
label
.
labelTypeCode
;
scope
.
row
.
labelTypeName
=
label
.
labelTypeName
;
scope
.
row
.
labelName
=
label
.
labelName
;
}
else
{
scope
.
row
.
labelTypeCode
=
null
;
scope
.
row
.
labelTypeName
=
null
;
scope
.
row
.
labelName
=
null
;
}
}
const
sensitiveTableSelectable
=
(
row
,
index
)
=>
{
// return row.confirmStatus == 'N';
return
true
;
}
const
selectTableFieldRows
:
any
=
ref
([]);
/** 勾选字段标准选中变化。 */
const
selectionDataChange
=
(
val
)
=>
{
selectTableFieldRows
.
value
=
val
;
};
const
handleFieldClickEdit
=
(
scope
)
=>
{
scope
.
row
.
isEdit
=
true
;
}
const
handleFieldClickSave
=
(
scope
)
=>
{
let
labelName
=
''
;
let
labelTypeCode
=
''
;
if
(
scope
.
row
.
labelGuid
)
{
let
label
=
allDataLabelList
.
value
.
find
(
label
=>
label
.
guid
==
scope
.
row
.
labelGuid
);
if
(
label
)
{
labelName
=
label
.
labelName
;
labelTypeCode
=
label
.
labelTypeCode
;
}
}
sensitiveTableDataLoading
.
value
=
true
;
updateSensitiveDataTaskFieldLabel
([{
guid
:
scope
.
row
.
guid
,
labelGuid
:
scope
.
row
.
labelGuid
,
labelName
:
labelName
,
labelTypeCode
:
labelTypeCode
}]).
then
((
res
:
any
)
=>
{
sensitiveTableDataLoading
.
value
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
getSensitiveTableFieldData
();
getSensitiveFieldLabelData
({
filterLabel
:
true
,
filterStatus
:
true
,
filterType
:
true
});
getCntSumInfo
();
proxy
.
$ElMessage
.
success
(
'字段的标签修改成功'
);
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
});
}
const
checkTableSave
=
()
=>
{
return
sensitiveTableData
.
value
.
some
(
s
=>
s
.
isEdit
==
true
);
}
const
pageChange
=
(
info
)
=>
{
const
toChange
=
checkTableSave
()
const
changeCont
=
()
=>
{
pageInfo
.
value
.
curr
=
Number
(
info
.
curr
)
pageInfo
.
value
.
limit
=
Number
(
info
.
limit
)
getSensitiveTableFieldData
();
}
if
(
toChange
)
{
ElMessageBox
.
confirm
(
'存在未保存的数据,确定放弃修改吗?'
,
'提示'
,
{
confirmButtonText
:
'确定'
,
cancelButtonText
:
'取消'
,
type
:
'warning'
,
}
).
then
(()
=>
{
changeCont
();
})
}
else
{
changeCont
();
}
}
const
cancel
=
()
=>
{
if
(
checkTableSave
())
{
proxy
.
$openMessageBox
(
"当前页面存在未保存的数据,确定放弃修改吗?"
,
()
=>
{
userStore
.
setTabbar
(
userStore
.
tabbar
.
filter
((
tab
:
any
)
=>
tab
.
fullPath
!==
fullPath
));
router
.
push
({
name
:
'sensitiveIdentify'
});
},
()
=>
{
proxy
.
$ElMessage
.
info
(
"已取消"
);
});
}
else
{
userStore
.
setTabbar
(
userStore
.
tabbar
.
filter
((
tab
:
any
)
=>
tab
.
fullPath
!==
fullPath
));
router
.
push
({
name
:
'sensitiveIdentify'
});
}
}
const
pageConfirm
=
()
=>
{
fullScreenLoading
.
value
=
true
;
confirmTaskStatus
(
route
.
query
.
execGuid
).
then
((
res
:
any
)
=>
{
fullScreenLoading
.
value
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
proxy
.
$ElMessage
.
success
(
'确认成功'
);
userStore
.
setTabbar
(
userStore
.
tabbar
.
filter
((
tab
:
any
)
=>
tab
.
fullPath
!==
fullPath
));
router
.
push
({
name
:
'sensitiveIdentify'
});
}
else
{
proxy
.
$ElMessage
.
error
(
res
.
msg
);
}
})
}
const
cntLabelMap
=
ref
({
'01'
:
'directIdentifierNum'
,
'02'
:
'standardIdentifierNum'
,
'03'
:
'sensitiveNum'
,
'04'
:
'nonSensitiveNum'
})
</
script
>
<
template
>
<div
class=
"container_wrap full flex"
v-loading=
"fullScreenLoading"
>
<div
class=
"aside_wrap"
:style=
"
{ height: isLook ? '100%' : 'calc(100% - 40px)' }">
<div
class=
"aside_title"
>
数据表列表
</div>
<Tree
ref=
"treeRef"
:treeInfo=
"treeInfo"
@
nodeClick=
"nodeClick"
/>
</div>
<div
class=
"main_wrap"
:style=
"
{ height: isLook ? '100%' : 'calc(100% - 40px)' }">
<div
class=
"table_tool_wrap"
>
<div
class=
"cnt-desc"
>
{{
'表总数:'
+
changeNum
(
cntSumInfo
.
tableNum
||
0
,
0
)
+
'张 字段总数:'
+
changeNum
(
cntSumInfo
.
fieldNum
||
0
,
0
)
+
'个 敏感表总数:'
+
changeNum
(
cntSumInfo
.
sensitiveTableNum
||
0
,
0
)
+
'张 敏感字段总数:'
+
changeNum
(
cntSumInfo
.
sensitiveFieldNum
||
0
,
0
)
+
'个,其中'
+
labelTypeList
?.
map
(
l
=>
`${l.label
}
${changeNum(cntSumInfo[cntLabelMap[l.value]] || 0,
0)
}
个`
).
join
(
','
)
+
',非敏感'
+
changeNum
(
cntSumInfo
[
'nonSensitiveNum'
]
||
0
,
0
)
+
'个'
}}
<
/div
>
<
div
class
=
"tools_btns"
v
-
if
=
"!isLook"
>
<
el
-
button
type
=
"primary"
@
click
=
"batchConfirm"
>
批量确认
<
/el-button
>
<
/div
>
<
/div
>
<
div
class
=
"table_panel_wrap"
:
style
=
"
{
height
:
isLook
?
'calc(100% - 54px)'
:
'calc(100% - 91px)'
}
">
<!-- 右侧表字段标签匹配管理表格 -->
<el-table ref="
sensitiveTableRef
" :data="
sensitiveTableData
" v-loading="
sensitiveTableDataLoading
"
:highlight-current-row="
true
" stripe border height="
100
%
" row-key="
guid
"
@selection-change="
selectionDataChange
" tooltip-effect="
light
" :style="
{
width
:
'100%'
,
'max-height'
:
'calc(100% - 16px)'
,
display
:
'inline-block'
,
}
">
<el-table-column type="
selection
" v-if="
!
isLook
" :selectable="
sensitiveTableSelectable
" :width="
32
"
align="
center
" />
<el-table-column label="
序号
" type="
index
" width="
56
px
" align="
center
" show-overflow-tooltip>
<template #default="
scope
"
>
<
span
>
{{
pageInfo
.
curr
!==
undefined
?
(
pageInfo
.
curr
-
1
)
*
pageInfo
.
limit
+
scope
.
$index
+
1
:
scope
.
$index
+
1
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"字段中文名"
prop
=
"fieldChName"
width
=
"160"
align
=
"left"
show
-
overflow
-
tooltip
>
<
template
#
default
=
"scope"
>
<
span
>
{{
scope
.
row
.
fieldChName
||
'--'
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"字段英文名"
prop
=
"fieldName"
width
=
"150"
align
=
"left"
show
-
overflow
-
tooltip
>
<
template
#
default
=
"scope"
>
<
span
>
{{
scope
.
row
.
fieldName
||
'--'
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"标签"
prop
=
"labelGuid"
class
-
name
=
"filter-cell"
width
=
"140"
align
=
"left"
show
-
overflow
-
tooltip
>
<
template
#
header
>
<
span
>
标签
<
/span
>
<
BtnPopover
v
-
show
=
"labelList.length"
:
popoverInfo
=
"popoverLabelListInfo"
@
popverBtnClick
=
"handleLabelPopoverClick"
/>
<
/template
>
<
template
#
default
=
"scope"
>
<
el
-
select
-
v2
v
-
if
=
"scope.row['isEdit']"
v
-
model
=
"scope.row['labelGuid']"
filterable
popper
-
class
=
"el-select-v2-popper"
:
options
=
"allDataLabelList"
placeholder
=
"请选择"
clearable
:
props
=
"
{
value
:
'guid'
,
label
:
'labelName'
}
" @change="
(
val
)
=>
handleSelectChange
(
val
,
scope
)
">
<template #default="
{
item
}
">
<ellipsis-tooltip :content="
item
.
labelName
??
''
" class-name="
w100f
"
:refName="
'tooltipOver'
+
item
.
guid
"
><
/ellipsis-tooltip
>
<
/template
>
<
/el-select-v2
>
<
span
v
-
else
>
{{
scope
.
row
[
"labelName"
]
||
'--'
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"标签类型"
prop
=
"labelTypeName"
class
-
name
=
"filter-cell"
width
=
"130"
align
=
"left"
show
-
overflow
-
tooltip
>
<
template
#
header
>
<
span
>
标签类型
<
/span
>
<
BtnPopover
v
-
show
=
"labelTypeDataList.length"
:
popoverInfo
=
"popoverLabelTypeListInfo"
@
popverBtnClick
=
"handleLabelTypePopoverClick"
/>
<
/template
>
<
template
#
default
=
"scope"
>
<
span
>
{{
scope
.
row
.
labelTypeName
||
'--'
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"所属表中文名"
prop
=
"tableChName"
width
=
"140"
align
=
"left"
show
-
overflow
-
tooltip
>
<
template
#
default
=
"scope"
>
<
span
>
{{
scope
.
row
.
tableChName
||
'--'
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"所属表英文名"
prop
=
"tableName"
:
width
=
"dataSource == 1 ? 160 : 130"
align
=
"left"
show
-
overflow
-
tooltip
>
<
/el-table-column
>
<
el
-
table
-
column
label
=
"确认状态"
prop
=
"confirmStatus"
class
-
name
=
"filter-cell"
width
=
"120"
align
=
"center"
show
-
overflow
-
tooltip
>
<
template
#
header
>
<
span
>
确认状态
<
/span
>
<
BtnPopover
v
-
show
=
"confirmStatusList.length"
:
popoverInfo
=
"popoverStatusListInfo"
@
popverBtnClick
=
"handleStatusPopoverClick"
/>
<
/template
>
<
template
#
default
=
"scope"
>
<
el
-
tag
v
-
if
=
"scope.row.confirmStatus != null"
:
type
=
"scope.row.confirmStatus == 'Y' ? 'success' : 'warning'"
>
{{
scope
.
row
.
confirmStatus
==
'Y'
?
'已确认'
:
'未确认'
}}
<
/el-tag
>
<
span
v
-
else
>
{{
'--'
}}
<
/span
>
<
/template
>
<
/el-table-column
>
<
el
-
table
-
column
v
-
if
=
"!isLook"
label
=
"操作"
width
=
"90px"
align
=
"left"
fixed
=
"right"
show
-
overflow
-
tooltip
>
<
template
#
default
=
"scope"
>
<
span
class
=
"text_btn"
v
-
if
=
"!scope.row['isEdit']"
@
click
=
"handleFieldClickEdit(scope)"
v
-
preReClick
>
编辑
<
/span
>
<
span
class
=
"text_btn"
v
-
else
@
click
=
"handleFieldClickSave(scope)"
v
-
preReClick
>
保存
<
/span
>
<
/template
>
<
/el-table-column
>
<
/el-table
>
<
PageNav
:
pageInfo
=
"pageInfo"
@
pageChange
=
"pageChange"
/>
<
/div
>
<
/div
>
<
div
class
=
"bottom_tool_wrap"
>
<
el
-
button
@
click
=
"cancel"
v
-
preReClick
>
取消
<
/el-button
>
<
el
-
button
type
=
"primary"
@
click
=
"pageConfirm"
v
-
preReClick
>
确认
<
/el-button
>
<
/div
>
<
/div
>
<
/template
>
<
style
lang
=
"scss"
scoped
>
.
container_wrap
{
flex
-
wrap
:
wrap
;
.
aside_wrap
{
width
:
200
px
;
height
:
calc
(
100
%
-
40
px
);
}
.
table_tool_wrap
{
display
:
flex
;
flex
-
direction
:
column
;
min
-
height
:
25
px
;
.
cnt
-
desc
{
line
-
height
:
24
px
;
white
-
space
:
break
-
spaces
;
margin
-
top
:
4
px
;
}
}
.
tools_btns
{
padding
-
top
:
0
px
;
}
.
table_panel_wrap
{
height
:
calc
(
100
%
-
91
px
);
}
.
bottom_tool_wrap
{
height
:
40
px
;
width
:
100
%
;
padding
:
0
16
px
;
border
-
top
:
1
px
solid
#
d9d9d9
;
display
:
flex
;
justify
-
content
:
center
;
align
-
items
:
center
;
}
}
.
tree_panel
{
height
:
100
%
;
padding
-
top
:
0
;
:
deep
(.
el
-
tree
)
{
margin
:
0
;
height
:
calc
(
100
%
-
68
px
);
overflow
:
hidden
auto
;
}
}
:
deep
(.
el
-
table
)
{
&
td
.
el
-
table__cell
{
.
cell
{
padding
:
0
px
10
px
;
}
padding
:
2
px
0
px
;
height
:
36
px
;
}
}
:
deep
(.
filter
-
cell
)
{
.
cell
{
position
:
relative
;
.
el
-
icon
.
filter
-
icon
{
position
:
absolute
;
right
:
6
px
;
top
:
2
px
;
width
:
18
px
;
height
:
18
px
;
svg
{
width
:
18
px
;
height
:
18
px
;
}
}
}
}
<
/style>
\ No newline at end of file
src/views/data_anonymization/sensitiveIdentifyTaskExecLog.vue
0 → 100644
View file @
c89a85a
<route
lang=
"yaml"
>
name: sensitiveIdentifyTaskExecLog
</route>
<
script
lang=
"ts"
setup
name=
"sensitiveIdentifyTaskExecLog"
>
import
{
ref
}
from
"vue"
;
import
{
useRouter
,
useRoute
}
from
"vue-router"
;
import
Table
from
"@/components/Table/index.vue"
;
import
{
ElMessage
}
from
"element-plus"
;
import
{
commonPageConfig
}
from
'@/components/PageNav/index'
;
import
{
getSensitiveDataTaskExecLog
,
}
from
'@/api/modules/dataAnonymization'
;
import
{
TableColumnWidth
}
from
"@/utils/enum"
;
const
{
proxy
}
=
getCurrentInstance
()
as
any
;
const
router
=
useRouter
();
const
route
=
useRoute
();
const
guid
=
route
.
query
.
guid
;
const
wordName
=
route
.
query
.
name
const
page
=
ref
({
...
commonPageConfig
});
const
tableInfo
=
ref
({
id
:
"word-log-table"
,
loading
:
false
,
fields
:
[
{
label
:
"序号"
,
type
:
"index"
,
width
:
TableColumnWidth
.
INDEX
,
align
:
"center"
},
{
label
:
"执行人"
,
field
:
"createUserName"
,
width
:
TableColumnWidth
.
USERNAME
},
{
label
:
"执行时间"
,
field
:
"execTime"
,
width
:
TableColumnWidth
.
DATETIME
},
{
label
:
"执行状态"
,
field
:
"sensitiveIdentifyTaskStatus"
,
width
:
TableColumnWidth
.
STATE
,
align
:
'center'
,
type
:
"tag"
},
{
label
:
"确认人"
,
field
:
"confirmUserName"
,
width
:
TableColumnWidth
.
USERNAME
},
{
label
:
"确认时间"
,
field
:
"confirmTime"
,
width
:
TableColumnWidth
.
DATETIME
},
{
label
:
"确认状态"
,
field
:
"sensitiveIdentifyConfirmStatus"
,
width
:
TableColumnWidth
.
STATE
,
align
:
'center'
,
type
:
"tag"
},
],
data
:
[],
page
:
{
type
:
"normal"
,
rows
:
0
,
...
page
.
value
,
},
actionInfo
:
{
label
:
"操作"
,
type
:
"btn"
,
width
:
100
,
fixed
:
'right'
,
btns
:
(
scope
)
=>
{
return
[{
label
:
"查看"
,
value
:
"report"
,
disabled
:
scope
.
row
[
'status'
]
!=
'Y'
,
click
:
(
scope
)
=>
{
router
.
push
({
name
:
'sensitiveIdentifyConfig'
,
query
:
{
guid
:
route
.
query
.
guid
,
execGuid
:
scope
.
row
.
guid
,
taskName
:
route
.
query
.
name
,
isLook
:
'1'
,
}
});
}
}];
}
}
});
const
getTableData
=
()
=>
{
tableInfo
.
value
.
loading
=
true
;
getSensitiveDataTaskExecLog
({
pageIndex
:
page
.
value
.
curr
,
pageSize
:
page
.
value
.
limit
,
taskGuid
:
guid
}).
then
((
res
:
any
)
=>
{
tableInfo
.
value
.
loading
=
false
;
if
(
res
?.
code
==
proxy
.
$passCode
)
{
const
data
=
res
.
data
||
{}
tableInfo
.
value
.
data
=
data
.
records
?.
map
(
d
=>
{
d
.
sensitiveIdentifyTaskStatus
=
d
.
status
;
d
.
sensitiveIdentifyConfirmStatus
=
d
.
confirmStatus
;
return
d
;
})
||
[]
tableInfo
.
value
.
page
.
limit
=
data
.
pageSize
tableInfo
.
value
.
page
.
curr
=
data
.
pageIndex
tableInfo
.
value
.
page
.
rows
=
data
.
totalRows
}
else
{
ElMessage
.
error
(
res
.
msg
);
}
})
};
const
tableBtnClick
=
(
scope
,
btn
)
=>
{
const
type
=
btn
.
value
;
const
row
=
scope
.
row
;
if
(
type
==
'reportView'
)
{
router
.
push
({
name
:
'analysisReport'
,
query
:
{
planGuid
:
row
.
planGuid
,
reportExecGuid
:
row
.
guid
,
name
:
wordName
}
});
}
};
onActivated
(()
=>
{
getTableData
();
});
</
script
>
<
template
>
<div
class=
"container_wrap"
>
<div
class=
"table_panel_wrap"
>
<Table
:tableInfo=
"tableInfo"
@
tableBtnClick=
"tableBtnClick"
/>
</div>
</div>
</
template
>
<
style
lang=
"scss"
scoped
>
.container_wrap
{
padding
:
0
;
.table_panel_wrap
{
height
:
100%
;
padding
:
16px
16px
0
;
}
}
</
style
>
\ No newline at end of file
src/views/data_smart_contract/smartContractDetail.vue
View file @
c89a85a
...
...
@@ -1387,7 +1387,7 @@ onActivated(() => {
<ContentWrap
v-show=
"!currentStep && detailType == 'sign' || currentStep == 3"
id=
"sign-info"
title=
"合约签署"
expandSwicth
style=
"margin-top: 15px"
:isExpand=
"expandSign"
@
expand=
"(v) => (expandSign = v)"
description=
""
>
<template
v-for=
"(item, index) in signDetailInfo"
>
<div
:class=
"
{ 'h-title': true, 'mt6': index > 0 }">
{{
item
.
executionerTypeName
}}
</div>
<div
:class=
"
{ 'h-title': true, 'mt6':
<number>
index > 0 }">
{{
item
.
executionerTypeName
}}
</div>
<div
class=
"list_panel"
>
<div
class=
"list_item wrap"
>
<span
class=
"item_label"
>
签署主体标识
</span>
...
...
Write
Preview
Styling with
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment