c666f2ec by lihua

标签管理和匿名化管理

1 parent 2e8a3988
......@@ -50,6 +50,9 @@ VITE_APP_QUALITY_BASEURL = ms-daop-data-quality-service
# VITE_APP_CHECK_BASEURL = /mock
VITE_APP_CHECK_BASEURL = ms-daop-zcgl-data-inventory
#匿名化管理接口
VITE_APP_ANONYMIZATION_BASEURL = ms-daop-anonymization-service
#数据源接口地址
VITE_APP_DATA_SOURCE_URL = ms-daop-data-source-service
......
......@@ -43,6 +43,9 @@ VITE_APP_QUALITY_BASEURL = ms-daop-data-quality-service
# VITE_APP_CHECK_BASEURL = /mock
VITE_APP_CHECK_BASEURL = ms-daop-zcgl-data-inventory
#匿名化管理接口
VITE_APP_ANONYMIZATION_BASEURL = ms-daop-anonymization-service
# 数据字典接口地址
VITE_APP_CONFIG_URL = 'ms-daop-configure-service'
......
/**
* 匿名化管理的接口api文件
*/
import request from "@/utils/request";
/** 获取标签列表。 */
export const getDataLabelList = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/label/page-list`,
method: 'post',
data: params
})
/** 修改标签启用禁用状态 */
export const updateLabelState = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/label/update-state`,
method: 'put',
params
})
export const saveLabel = (data) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/label/save`,
method: 'post',
data
})
export const deleteLabel = (data) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/label/delete`,
method: 'delete',
data
})
export const updateLabel = (data) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/label/update`,
method: 'put',
data
})
/** 获取标签详情 */
export const getLabelDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/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',
label: '日期'
}, {
value: '2',
label: '字符串'
}, {
value: '3',
label: '数值'
}]
/** 获取泛化文件列表 */
export const getGeneralizeFileList = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/generalize-file/page-list`,
method: 'post',
data: params
})
export const saveGeneralizeFile = (data) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/generalize-file/save`,
method: 'post',
data
})
/** 删除泛化文件 */
export const deleteGeneralizeFile = (data) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/generalize-file/delete`,
method: 'delete',
data
})
/** 获取泛化文件详情 */
export const getGeneralizeFileDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/generalize-file/detail?guid=${guid}`,
method: 'get'
})
export const updateGeneralizeFile = (data) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/generalize-file/update`,
method: 'put',
data
})
/** --------- 敏感数据识别接口 ------------------- */
/** 获取敏感数据识别任务列表 */
export const getSensitiveDataTaskList = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/page-list`,
method: 'post',
data: params
})
/** 新增敏感数据识别任务 */
export const saveSensitiveDataTask = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/save`,
method: 'post',
data: params
})
/** 编辑修改敏感数据识别任务 */
export const updateSensitiveDataTask = (params) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/update`,
method: 'post',
params
})
/** 删除敏感数据识别任务 */
export const deleteSensitiveDataTask = (data) => request({
url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/sensitive-data-task/delete`,
method: 'delete',
data
})
/** 数据来源类型 */
//export const
\ No newline at end of file
......@@ -11,6 +11,8 @@ import {
MoreFilled,
QuestionFilled,
WarningFilled,
CircleCheckFilled,
CircleCloseFilled,
} from "@element-plus/icons-vue";
// import Table from "../Table/index.vue";
import Tabs from "../Tabs/index.vue";
......@@ -1499,7 +1501,8 @@ const panelChange = (scope, row) => {
:autocomplete="item.autocompleteSetting?.autocomplete || item.autocomplete
" :readonly="item.autocompleteSetting?.readonly || item.readonly" @click="handlerIptClick(item)" />
<div class="input_def" v-else>
<el-input :class="[item.col, { is_block: item.block }]" v-model.trim="formInline[item.field]"
<span v-if="item.beforeMsg" style="color: #212121;">{{ item.beforeMsg }}</span>
<el-input :class="[item.col, { is_block: item.block }]" v-model.trim="formInline[item.field]" :style="item.width ? { width: item.width } : null"
:placeholder="item.placeholder" :type="item.inputType ?? 'text'" :clearable="item.clearable"
:disabled="item.disabled || readonly" :min="item.min" :max="item.max" :maxlength="item.maxlength ?? ''"
:autocomplete="item.autocompleteSetting?.autocomplete || item.autocomplete
......@@ -1515,25 +1518,13 @@ const panelChange = (scope, row) => {
<QuestionFilled />
</el-icon>
</el-tooltip>
<!-- 输入框右上角显示验证按钮 -->
<span v-if="item.validateBtn" style="position: absolute;right: 0;top: 0px" class="text_btn" @click="item.validateBtn.click">{{ item.validateBtn.label }}</span>
<div v-if="item.validateMsg?.msg" class="validate-input-after-msg" :style="{ color: item.validateMsg.status == 'success' ? '#4FA55D' : '#E63E33', right: item.validateMsg.status == 'success' ? '-106px' : '-120px' }"><el-icon class="title-icon"><CircleCloseFilled v-if="item.validateMsg.status != 'success'" />
<CircleCheckFilled v-else /></el-icon><span class="title_text">{{ item.validateMsg?.msg }}</span></div>
<span v-if="item.afterMsg" style="color: #212121">{{ item.afterMsg }}</span>
</div>
</el-form-item>
<!-- <el-form-item v-if="item.type === 'formAndSelect'" class="form-and-select" :prop="item.field"
style="width: 100%;display: flex;">
<div v-for="(child, index) in item.children" :key="index" style="margin-right: 8px;"
@mouseenter="handleMouseEnter(index, item.children)" @mouseleave="handleMouseLeave(index)">
<template v-if="child.type === 'select'">
<el-select v-model="formInline[child.field]" :options="child.options" :props="child.props"
:placeholder="child.placeholder" :filterable="child.filterable" :clearable="child.clearable"
class="main-select" style="width: 255px;" />
</template>
<template v-if="child.type === 'input'">
<el-input v-model="formInline[child.field]" :placeholder="child.placeholder" :maxlength="child.maxlength"
:clearable="child.clearable" style="width: 255px;" />
</template>
<el-button v-if="child.showDeleteButton && extraIcon" class="extra-icon" :icon="extraIcon.icon"
@click="extraIcon.click" circle style="margin-left: 8px;" />
</div>
</el-form-item> -->
</template>
<!-- 默认插槽内容 -->
......@@ -2500,4 +2491,21 @@ const panelChange = (scope, row) => {
flex-wrap: nowrap !important;
}
}
.validate-input-after-msg {
display: flex;
align-items: center;
position: absolute;
top: 26px;
right: -120px;
:deep(.el-icon) {
width: 16px;
height: 16px;
svg {
width: 16px;
height: 16px;
}
}
}
</style>
......
......@@ -148,9 +148,38 @@ export const useValidator = () => {
}
}
/** 判断输入的是否是正则表达式的规则 */
const regexpRuleValidate = () => {
return {
validator: (rule: any, value: any, callback: any) => {
function isValidRegex(pattern) {
try {
new RegExp(pattern);
return true;
} catch (e) {
return false;
}
}
if (!value) {
callback(new Error("输入正则表达式"));
return;
}
if (isValidRegex(value)) {
callback();
return;
} else {
callback(new Error("正则表达式语法不正确"));
return;
}
},
trigger: "blur",
};
}
return {
required,
regexpValidate,
regexpRuleValidate,
orderNum,
description,
chOrEnPreffix,
......
......@@ -55,7 +55,7 @@ onMounted(() => {
<div class="v-uerinfo">
<div class="v-top">{{ userStore.userName }}</div>
<div class="v-top"> {{ JSON.parse(loaclStorageInfo).abbreviation }}</div>
<div class="v-top"> {{ JSON.parse(loaclStorageInfo)?.abbreviation }}</div>
</div>
<el-avatar size="small">
<el-icon>
......
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: '/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
},
},
],
},
]
export default routes
......@@ -5,6 +5,7 @@ import DataAssess from './modules/dataAsset';
import DataMeta from './modules/dataMeta';
import DataQuality from './modules/dataQuality';
import DataInventory from './modules/dataInventory';
import DataAnonymization from './modules/dataAnonymization';
import AssetIndex from './modules/assetIndex';
import DataTrustedSpace from './modules/dataTrustedSpace';
import DataAssetRegistry from './modules/dataAssetRegistry';
......@@ -113,6 +114,7 @@ const asyncRoutes: RouteRecordRaw[] = [
...DataMeta,
...DataQuality,
...DataInventory,
...DataAnonymization,
...DataTrustedSpace,
...DataPricing
]
......
const useDataAnonymizationStore = defineStore(
// 资产目录guid
"isRefresh",
() => {
const isRefresh = ref<boolean>(false);
function setIsRefresh(v: boolean) {
isRefresh.value = v;
}
return {
isRefresh,
setIsRefresh,
};
}
);
export default useDataAnonymizationStore;
<route lang="yaml">
name: generalizeFile
</route>
<script lang="ts" setup name="generalizeFile">
import TableTools from "@/components/Tools/table_tools.vue";
import { commonPageConfig } from '@/components/PageNav/index';
import { TableColumnWidth } from "@/utils/enum";
import {
deleteGeneralizeFile,
getGeneralizeFileList,
fieldTypeList,
} from '@/api/modules/dataAnonymization';
import useDataAnonymizationStore from "@/store/modules/dataAnonymization";
const anonymizationStore = useDataAnonymizationStore();
const router = useRouter()
const { proxy } = getCurrentInstance() as any;
const searchItemList = ref([{
type: "input",
label: "",
field: "generalizeFileName",
default: "",
placeholder: "泛化文件名称",
clearable: true,
}, {
type: "select",
label: "",
field: "fieldType",
default: null,
options: fieldTypeList,
placeholder: "字段类型",
clearable: true,
filterable: true,
}])
/** 分页及搜索传参信息配置。 */
const page = ref({
...commonPageConfig,
generalizeFileName: '',
fieldType: ''
});
const tableInfo = ref({
id: 'data-file-table',
fields: [
{ label: "序号", type: "index", width: TableColumnWidth.INDEX, align: "center" },
{ label: "泛化文件名称", field: "generalizeFileName", width: 160 },
{ label: "字段类型", field: "fieldType", width: 120, getName: (scope) => {
return scope.row.fieldType && fieldTypeList.find(f => f.value == scope.row.fieldType)?.label || '--';
} },
{ label: "泛化层级", field: "generalizeLevel", width: 120, align: 'right', type: 'chnum' },
{ 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: 120,
fixed: 'right',
btns: (scope) => {
return [{
label: "编辑", value: "edit", click: (scope) => {
router.push({
name: 'generalizeFileEdit',
query: {
guid: scope.row.guid,
fileName: scope.row.generalizeFileName
}
});
}
}, {
label: "删除", value: "delete", disabled: scope.row.bizState == 'Y', click: (scope) => {
proxy.$openMessageBox("此操作将永久删除, 是否继续?", () => {
let guids = [scope.row.guid];
deleteGeneralizeFile(guids).then((res: any) => {
if (res.code == proxy.$passCode) {
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.generalizeFileName = '';
page.value.fieldType = '';
} else {
page.value.generalizeFileName = val.generalizeFileName;
page.value.fieldType = val.fieldType;
}
getTableData();
};
const getTableData = () => {
tableInfo.value.loading = true
getGeneralizeFileList({
pageIndex: page.value.curr,
pageSize: page.value.limit,
generalizeFileName: page.value.generalizeFileName,
fieldType: page.value.fieldType
}).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 tablePageChange = (info) => {
page.value.curr = Number(info.curr);
page.value.limit = Number(info.limit);
getTableData();
};
onBeforeMount(() => {
toSearch({});
})
onActivated(() => {
if (anonymizationStore.isRefresh) {//如果是首次加载,则不需要调用
page.value.curr = 1;
getTableData();
anonymizationStore.setIsRefresh(false);
}
})
const handleCreate = () => {
router.push({
name: 'generalizeFileEdit'
});
}
</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
<route lang="yaml">
name: resultProcess
</route>
<script lang="ts" setup name="resultProcess">
</script>
<template>
<div>匿名化处理</div>
</template>
\ No newline at end of file
<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,
} from '@/api/modules/dataAnonymization';
const router = useRouter()
const { proxy } = getCurrentInstance() as any;
const searchItemList = ref([{
type: "input",
label: "",
field: "taskName",
default: "",
placeholder: "任务名称",
clearable: true,
}, {
type: "select",
label: "",
field: "fieldType",
default: null,
options: [],
placeholder: "数据来源",
clearable: true,
filterable: true,
}])
/** 分页及搜索传参信息配置。 */
const page = ref({
...commonPageConfig,
generalizeFileName: '',
fieldType: ''
});
</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
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!