15f829df by lihua

敏感数据识别

1 parent d99b2298
......@@ -112,8 +112,8 @@ export const saveSensitiveDataTask = (params) => request({
/** 编辑修改敏感数据识别任务 */
export const updateSensitiveDataTask = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/update`,
method: 'post',
params
method: 'put',
data: params
})
/** 删除敏感数据识别任务 */
......@@ -123,5 +123,72 @@ export const deleteSensitiveDataTask = (data) => request({
data
})
/** 手动执行敏感任务 */
export const execSensitiveDataTask = (guid) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/exec-task?taskGuid=${guid}`,
method: 'get'
})
/** 数据来源类型 */
//export const
\ No newline at end of file
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_ANONYMIZATION_BASEURL}/sensitive-data-task/get-exec-sensitive-table?execGuid=${execGuid}`,
method: 'get'
})
/** 根据数据源或表获取敏感数据任务执行的字段列表 */
export const getExecSensitiveFieldTable = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/get-exec-sensitive-field`,
method: 'post',
data: params
})
/** 获取当前数据表下的执行字段 */
export const getExecSensitiveFieldLabel = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/get-exec-sensitive-label`,
method: 'post',
data: params
})
/** 获取敏感数据识别任务执行后的统计结果 */
export const getStatisticsNum = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/statistics-num`,
method: 'get',
params
})
/** 修改敏感数据识别字段标签 */
export const updateSensitiveDataTaskFieldLabel = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/batch-update-label`,
method: 'put',
data: params
})
/** 批量修改确认状态 */
export const batchUpdateSensitiveDataTaskFieldStatus = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/batch-change-status`,
method: 'post',
data: params
})
/** 获取敏感数据识别任务执行日志 */
export const getSensitiveDataTaskExecLog = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/get-exec-sensitive-exec-log`,
method: 'post',
data: params
})
\ No newline at end of file
......
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1755825941248" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1233" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M102.784 179.648A32 32 0 0 1 128.064 128H768a32 32 0 0 1 25.28 51.648L576 458.944v468.992a32 32 0 0 1-49.728 26.624l-192-128a32 32 0 0 1-14.24-26.592V458.944L102.784 179.648z m274.528 248.672A32 32 0 0 1 384 448v334.848l128 85.344V448a32 32 0 0 1 6.72-19.68L702.624 192H193.472l183.84 236.32zM735.968 512h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z m0 128h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z m0 128h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z" fill="#4fa1a4" p-id="1234"></path></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" class="icon" viewBox="0 0 1024 1024"><path d="M102.784 179.648A32 32 0 0 1 128.064 128H768a32 32 0 0 1 25.28 51.648L576 458.944v468.992a32 32 0 0 1-49.728 26.624l-192-128a32 32 0 0 1-14.24-26.592V458.944L102.784 179.648z m274.528 248.672A32 32 0 0 1 384 448v334.848l128 85.344V448a32 32 0 0 1 6.72-19.68L702.624 192H193.472l183.84 236.32zM735.968 512h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z m0 128h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z m0 128h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z" fill="#999999" p-id="1519"></path></svg>
\ No newline at end of file
<svg t="1755766425695" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1233" width="200" height="200"><path d="M102.784 179.648A32 32 0 0 1 128.064 128H768a32 32 0 0 1 25.28 51.648L576 458.944v468.992a32 32 0 0 1-49.728 26.624l-192-128a32 32 0 0 1-14.24-26.592V458.944L102.784 179.648z m274.528 248.672A32 32 0 0 1 384 448v334.848l128 85.344V448a32 32 0 0 1 6.72-19.68L702.624 192H193.472l183.84 236.32zM735.968 512h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z m0 128h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z m0 128h128a32 32 0 1 1 0 64h-128a32 32 0 1 1 0-64z" fill="#999999" p-id="1234"></path></svg>
\ No newline at end of file
......
......@@ -43,18 +43,23 @@ const btn = computed(() => {
return props.popoverInfo.btn ?? {};
});
const dataProps = computed(() => {
return <any>(props.popoverInfo.props || {});
});
const initAttr = () => {
const data = props.popoverInfo.data ?? []
const check = props.popoverInfo.checked ?? []
checkedVal.value = check
isIndeterminate.value = data.length > 0 && data.length == check.length
checkAll.value = data.length > 0 && data.length == check.length;
isIndeterminate.value = check.length > 0 && data.length > check.length
authorities.value = data
}
const filterPopover = (val) => {
let data = props.popoverInfo.data ?? []
if (val) {
authorities.value = data.filter(item => item.dataPermissionName.indexOf(val) > -1)
authorities.value = data.filter(item => item[dataProps.value.label || 'dataPermissionName'].indexOf(val) > -1)
} else {
authorities.value = data
}
......@@ -70,7 +75,8 @@ const handleCheckedCitiesChange = (val) => {
isIndeterminate.value = checkedCount > 0 && checkedCount < authorities.value.length;
};
const showPopver = () => {
inputValue.value = ''
inputValue.value = '';
initAttr();
emits('showPopver', props.popoverInfo.scope, btn.value)
}
const btnClick = () => {
......@@ -98,10 +104,10 @@ watch(() => props.popoverInfo, async (val) => {
</template>
<template v-else-if="type == 'checkbox-list-btns'">
<div class="content_body">
<el-input v-model.trim="inputValue" placeholder="请输入权限名称" :prefix-icon="Search" clearable
@change="filterPopover" />
<el-input v-model.trim="inputValue" v-show="popoverInfo.data?.length > 5" :placeholder="props.popoverInfo.placeholder ?? '请输入权限名称'" :prefix-icon="Search" clearable
@input="filterPopover" />
<el-checkbox-group v-model="checkedVal" @change="handleCheckedCitiesChange">
<el-checkbox v-for="auth in authorities" :key="auth.guid" :label="auth.guid">{{ auth.dataPermissionName
<el-checkbox v-for="auth in authorities" :key="auth.guid" :value="auth.guid">{{ auth[dataProps.label || 'dataPermissionName']
}}</el-checkbox>
</el-checkbox-group>
</div>
......@@ -118,6 +124,9 @@ watch(() => props.popoverInfo, async (val) => {
</div>
<template #reference>
<span v-if="btn.value == 'authority'" @click="showPopver" v-preReClick>{{ btn.label }}</span>
<el-icon class="filter-icon" v-else-if="btn.value == 'filter'" @click="showPopver">
<svg-icon :name="checkedVal.length ? 'filter-mobile-select' : 'filter-mobile'" />
</el-icon>
<el-icon v-else-if="btn.value == 'QuestionFilled'">
<QuestionFilled />
</el-icon>
......@@ -138,8 +147,8 @@ watch(() => props.popoverInfo, async (val) => {
}
.el-checkbox-group {
margin: 8px -8px 0;
max-height: 276px;
margin: 0px -8px 0;
max-height: 256px;
overflow: hidden auto;
.el-checkbox {
......
......@@ -339,7 +339,7 @@ onMounted(() => {
<span v-if="item.unit">{{ item.unit }}</span>
</template>
<template #default="scope" v-else-if="item.type == 'switch'">
<el-switch v-model="scope.row[item.field]" inline-prompt :key="scope.row.guid"
<el-switch v-model="scope.row[item.field]" inline-prompt :key="scope.row[rowKey || 'guid']"
:disabled="!item.isDisabled ? false : item.isDisabled(scope)" :active-value="item.activeValue"
:inactive-value="item.inactiveValue" :width="item.switchWidth" :active-text="item.activeText"
:inactive-text="item.inactiveText" :before-change="() => beforeChange(scope, item)"
......
......@@ -85,6 +85,42 @@ const routes: RouteRecordRaw[] = [
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.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}`;
}
}
}
],
},
{
......
......@@ -496,6 +496,16 @@ export const tagType = (row, type): any => {
} else {
state = 'info';
}
} else if (type == 'sensitiveIdentifyTaskStatus') {
if (row[type] == 'Y') {
state = 'success'
} else if (row[type] == 'N') {
state = 'info';
} else if (row[type] == 'E') {
state = 'danger';
} else if (row[type] == 'R') {
state = 'warning';
}
} else if (type == 'state' || type == 'documentState') {
switch (row[type]) {
case 'N':
......@@ -809,6 +819,16 @@ export const tagMethod = (row, type) => {
}else if (row[type] == 'R') { //部分通过
tag = '执行中';
}
} else if (type == 'sensitiveIdentifyTaskStatus') {
if (row[type] == 'Y') {
tag = '成功'
} else if (row[type] == 'N') {
tag = '未执行';
} else if (row[type] == 'E') { //部分通过
tag = '失败';
}else if (row[type] == 'R') { //部分通过
tag = '执行中';
}
} else if (type == 'execState') {
if (row[type] == 0 || row[type] == null) {
tag = '未执行'
......
......@@ -82,7 +82,7 @@ const tableInfo = ref({
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
let guids = [scope.row.guid];
deleteGeneralizeFile(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
getTableData();
proxy.$ElMessage({
type: "success",
......@@ -122,7 +122,7 @@ const getTableData = () => {
generalizeFileName: page.value.generalizeFileName,
fieldType: page.value.fieldType
}).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
const data = res.data || {}
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
......
......@@ -268,7 +268,7 @@ const saveUpdate = async () => {
if (!guid.value) {
saveLoading.value = true;
saveGeneralizeFile(params).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('泛化文件新建成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
anonymizationStore.setIsRefresh(true);
......@@ -285,7 +285,7 @@ const saveUpdate = async () => {
params.guid = guid.value;
saveLoading.value = true;
updateGeneralizeFile(params).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('泛化文件编辑成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
anonymizationStore.setIsRefresh(true);
......@@ -310,7 +310,7 @@ onBeforeMount(() => {
if (guid.value) {
fullscreenLoading.value = true;
getGeneralizeFileDetail(guid.value).then(async (res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
let detail = res.data || {};
baseInfoItems.value.forEach(item => {
item.default = detail[item.field];
......
......@@ -73,7 +73,7 @@ const tableInfo = ref({
label: "编辑", value: "edit", click: (scope) => {
currTableData.value = scope.row;
getLabelDetail(scope.row.guid).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
const detail = res.data || {};
currTableData.value = Object.assign({}, currTableData.value, detail);
newCreateLabelFormItems.value.forEach(item => {
......@@ -120,7 +120,7 @@ const tableInfo = ref({
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
let guids = [scope.row.guid];
deleteLabel(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
getTableData();
proxy.$ElMessage({
type: "success",
......@@ -159,7 +159,7 @@ const getTableData = () => {
pageSize: page.value.limit,
labelName: page.value.labelName
}).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
const data = res.data || {}
tableInfo.value.data = data.records || []
tableInfo.value.page.limit = data.pageSize
......@@ -193,7 +193,7 @@ const tableSwitchChange = (val, scope, field) => {
bizState: val
}
updateLabelState(params).then((res: any) => {
if (res.code == proxy.$passCode && res.data) {
if (res?.code == proxy.$passCode && res.data) {
getTableData();
proxy.$ElMessage({
type: "success",
......@@ -331,7 +331,7 @@ const newCreateLabelDialogInfo = ref({
newCreateLabelDialogInfo.value.submitBtnLoading = true;
if (newCreateLabelDialogInfo.value.type == 'add') {
saveLabel(params).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('标签新建成功');
newCreateLabelDialogInfo.value.visible = false;
newCreateLabelDialogInfo.value.submitBtnLoading = false;
......@@ -350,7 +350,7 @@ const newCreateLabelDialogInfo = ref({
params.labelRuleContent.guid = currTableData.value.labelRuleContent?.guid;
params.labelRuleField.guid = currTableData.value.labelRuleField?.guid;
updateLabel(params).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('标签编辑成功');
newCreateLabelDialogInfo.value.visible = false;
newCreateLabelDialogInfo.value.submitBtnLoading = false;
......@@ -654,7 +654,7 @@ onBeforeMount(() => {
getParamsList({
dictType: "标签类型",
}).then((res: any) => {
if (res.code == proxy.$passCode) {
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);
......@@ -665,7 +665,7 @@ onBeforeMount(() => {
getParamsList({
dictType: "内置规则",
}).then((res: any) => {
if (res.code == proxy.$passCode) {
if (res?.code == proxy.$passCode) {
builtInRuleList.value = res.data || [];
ruleContentFormItems.value[1].options = builtInRuleList.value;
} else {
......
......@@ -8,10 +8,18 @@ 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",
......@@ -23,9 +31,9 @@ const searchItemList = ref([{
}, {
type: "select",
label: "",
field: "fieldType",
field: "dataSource",
default: null,
options: [],
options: dataSourceTypeList,
placeholder: "数据来源",
clearable: true,
filterable: true,
......@@ -34,25 +42,342 @@ const searchItemList = ref([{
/** 分页及搜索传参信息配置。 */
const page = ref({
...commonPageConfig,
generalizeFileName: '',
fieldType: ''
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: 160 },
{
label: "数据来源", field: "dataSource", width: 120, 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: TableColumnWidth.USERNAME },
{ label: "执行时间", field: "execTime", 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: 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',
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" />
<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>
<div class="table_panel_wrap">
<!-- 右侧标签管理表格 -->
<!-- <Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" /> -->
<Table :tableInfo="tableInfo" @tablePageChange="tablePageChange" />
</div>
<Dialog_form ref="dialogTaskFormRef" :dialogConfigInfo="newCreateTaskDialogInfo"
@formDialogSelectChange="handleTaskSelectChange"></Dialog_form>
</div>
</template>
......
<route lang="yaml">
name: sensitiveIdentifyConfig
</route>
<script lang="ts" setup name="sensitiveIdentifyConfig">
import {
getExecSensitiveTable,
getExecSensitiveFieldTable,
getExecSensitiveFieldLabel,
getDataLabelList,
getStatisticsNum,
getParamsList,
updateSensitiveDataTaskFieldLabel,
batchUpdateSensitiveDataTaskFieldStatus,
} 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";
const route = useRoute();
const { proxy } = getCurrentInstance() as any;
const isLook = ref(!!route.query.isLook);
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 = '';
}
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;
}
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 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 => {
d.value = d.databaseName;
d.label = d.databaseChName;
d.tableList = d.tableList?.map(t => {
t.value = t.tableName;
t.label = 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 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: any = [];
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 = () => {
getExecSensitiveFieldLabel({
pageSize: -1,
taskExecGuid: pageInfo.value.taskExecGuid,
databaseName: pageInfo.value.databaseName,
tableName: pageInfo.value.tableName
}).then((res: any) => {
if (res?.code == proxy.$passCode) {
labelList.value = res.data?.map(d => {
d.guid = d.labelGuid;
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();
})
} else {
pageInfo.value.labelGuids = scope.row.selectedData || [];
pageInfo.value.curr = 1;
getSensitiveTableFieldData();
}
}
const popoverLabelTypeListInfo = computed(() => {
return {
type: 'checkbox-list-btns',
data: labelTypeList.value,
placeholder: '请输入关键字搜索',
scope: {
row: {}
},
placement: 'right-start',
props: {
value: 'guid',
label: 'label'
},
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();
})
} else {
pageInfo.value.labelTypeCodes = scope.row.selectedData || [];
pageInfo.value.curr = 1;
getSensitiveTableFieldData();
}
}
const popoverStatusListInfo = computed(() => {
return {
type: 'checkbox-list-btns',
data: [{
guid: 'N',
label: '待确认'
}, {
guid: 'Y',
label: '已确认'
}],
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();
})
} else {
pageInfo.value.confirmStatus = scope.row.selectedData || [];
pageInfo.value.curr = 1;
getSensitiveTableFieldData();
}
}
/** 标签选择的值改变,标签类型跟着变化。 */
const handleSelectChange = (val) => {
//暂时不处理,因为未保存会出现清空的场景,需要先存下之前的值。
}
const sensitiveTableSelectable = (row, index) => {
return row.confirmStatus == 'N';
}
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();
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();
}
}
</script>
<template>
<div class="container_wrap full flex">
<div class="aside_wrap">
<div class="aside_title">数据表列表</div>
<Tree ref="treeRef" :treeInfo="treeInfo" @nodeClick="nodeClick" />
</div>
<div class="main_wrap">
<div class="table_tool_wrap">
<div class="tools_btns">
<el-button v-if="!isLook" type="primary" @click="batchConfirm">批量确认</el-button>
<div class="cnt-desc">{{ '表总数:' + changeNum(cntSumInfo.tableNum || 0, 0) + '张 敏感表总数:' +
changeNum(cntSumInfo.sensitiveTableNum || 0, 0) + '张 字段总数:'
+ changeNum(cntSumInfo.fieldNum || 0, 0) + '个 敏感字段总数:' + changeNum(cntSumInfo.sensitiveFieldNum || 0, 0) +
'个,其中直接标识:' +
changeNum(cntSumInfo.directIdentifierNum || 0, 0) + '个 准标识:' + changeNum(cntSumInfo.standardIdentifierNum ||
0, 0) + '个 敏感:' +
changeNum(cntSumInfo.sensitiveNum || 0, 0) + '个 非敏感:' + changeNum(cntSumInfo.nonSensitiveNum || 0, 0) + '个'
}}</div>
</div>
</div>
<div class="table_panel_wrap">
<!-- 右侧表字段标签匹配管理表格 -->
<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="56px" align="center" show-overflow-tooltip>
</el-table-column>
<el-table-column label="字段中文名" prop="fieldChName" width="140" align="left" show-overflow-tooltip>
<template #default="scope">
<span>{{ scope.row.fieldChName || '--' }}</span>
</template>
</el-table-column>
<el-table-column label="字段英文名" prop="fieldName" width="140" 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-if="labelList.length" :popoverInfo="popoverLabelListInfo"
@popverBtnClick="handleLabelPopoverClick" />
</template>
<template #default="scope">
<el-select-v2 v-if="scope.row['isEdit']" v-model="scope.row['labelGuid']" filterable
:options="allDataLabelList" placeholder="请选择" clearable :props="{ value: 'guid', label: 'labelName' }"
@change="handleSelectChange" />
<span v-else>{{ scope.row["labelName"] || '--' }}</span>
</template>
</el-table-column>
<el-table-column label="标签类型" prop="labelTypeName" class-name="filter-cell" width="120" align="left"
show-overflow-tooltip>
<template #header>
<span>标签类型</span>
<BtnPopover v-if="labelTypeList.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>
</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 :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="100px" 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>
</template>
<style lang="scss" scoped>
.container_wrap {
.aside_wrap {
width: 200px;
}
.table_tool_wrap {
display: flex;
}
.table_panel_wrap {
height: calc(100% - 70px);
}
}
.tree_panel {
height: 100%;
padding-top: 0;
:deep(.el-tree) {
margin: 0;
height: calc(100% - 68px);
overflow: hidden auto;
}
}
:deep(.el-table) {
& td.el-table__cell {
.cell {
padding: 0px 10px;
}
padding: 2px 0px;
height: 36px;
}
}
.tools_btns {
display: flex;
align-items: center;
.el-button {
margin-right: 8px;
}
}
:deep(.filter-cell) {
.cell {
position: relative;
.el-icon.filter-icon {
position: absolute;
right: 6px;
top: 2px;
width: 18px;
height: 18px;
svg {
width: 18px;
height: 18px;
}
}
}
}
</style>
\ No newline at end of file
<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';
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: "报告名称", field: "analysisReportName", width: 230 },
{
label: "方案类型", field: "analysisReportType", width: 100, getName: (scope) => {
let planType = scope.row.analysisReportType;
return planType == 1 ? '表' : (planType == 2 ? '数据库' : (planType == 4 ? '数据同步' : '分组'));
}
},
{ label: "报告对象", field: "qualityModelName", width: 180, },
{ label: "质量评分", field: "qualityScore", width: 100, align: "right" },
{ label: "最后执行时间", field: "execTime", width: 180, },
{ label: "执行状态", field: "execResult", type: "tag", width: 120, align: "center" },
],
data: [],
page: {
type: "normal",
rows: 0,
...page.value,
},
actionInfo: {
label: "操作",
type: "btn",
width: 100,
fixed: 'right',
btns: (scope) => {
return [{ label: "查看报告", value: "reportView", disabled: scope.row['execResult'] != 'Y' }];
}
}
});
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 || []
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
}
});
}
};
onBeforeMount(() => {
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
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!