bb427c9b by lihua

添加最新代码修改

1 parent d4e71aa1
/**
* API响应处理工具函数
* 统一处理API请求的成功/失败逻辑
*/
interface ApiResponse {
code: number;
data?: any;
msg?: string;
}
export interface ApiHandlerOptions {
/* 当前实例的proxy对象(用于获取$passCode和$ElMessage)*/
proxy: any
/** 加载状态ref */
loadingRef?: Ref<boolean>;
/** 成功回调函数 */
onSuccess?: (res: any) => void;
/** 失败回调函数 */
onError?: (res: any) => void;
/** 是否显示错误消息,默认true */
showError?: boolean;
}
/**
* 统一处理API响应的工具函数
* @param apiPromise API请求Promise
* @param options 配置选项
*/
export const handleApiResponse = async (
apiPromise: Promise<ApiResponse>,
options: ApiHandlerOptions,
) => {
const {
loadingRef,
onSuccess,
onError,
showError = true,
proxy
} = options;
try {
// 设置加载状态
if (loadingRef) {
loadingRef.value = true;
}
// 执行API请求
const res = await apiPromise;
// 设置加载状态
if (loadingRef) {
loadingRef.value = false;
}
// 判断请求是否成功
if (res?.code == proxy.$passCode) {
// 成功回调
onSuccess && onSuccess(res);
} else {
// 失败处理
const errorMsg = res?.msg || '';
if (showError) {
proxy.$ElMessage.error(errorMsg);
}
onError && onError(res);
}
} catch (error: any) {
// 异常处理
if (loadingRef) {
loadingRef.value = false;
}
}
};
......@@ -2,6 +2,7 @@
* 匿名化管理的接口api文件
*/
import request from "@/utils/request";
import { handleApiResponse, ApiHandlerOptions } from '../apiHander'; // 导入公共处理方法
/** 获取标签列表。 */
export const getDataLabelList = (params) => request({
......@@ -246,6 +247,12 @@ export const updateAnonTask = (params) => request({
})
/** 获取匿名化任务详情 */
// export const getAnonTaskDetail = (guid, defaultOptions: ApiHandlerOptions) => handleApiResponse(request({
// url: `${import.meta.env.VITE_API_COMMON_URL}/anon-task/detail?guid=${guid}`,
// method: 'get'
// }), defaultOptions)
/** 获取匿名化任务详情 */
export const getAnonTaskDetail = (guid) => request({
url: `${import.meta.env.VITE_API_COMMON_URL}/anon-task/detail?guid=${guid}`,
method: 'get'
......@@ -265,12 +272,18 @@ export const anonTaskCheck = (params) => request({
})
/** 获取匿名化任务分析结果数据 */
export const getAnonAnalyzeResult = (execGuid) => request({
export const getAnonAnalyzeResult1 = (execGuid) => request({
url: `${import.meta.env.VITE_API_COMMON_URL}/anon-task/get-anon-analyze?taskExecGuid=${execGuid}`,
method: 'get'
})
/** 获取匿名化任务分析结果数据 */
export const getAnonAnalyzeResult = (execGuid, defaultOptions: ApiHandlerOptions) => handleApiResponse(request({
url: `${import.meta.env.VITE_API_COMMON_URL}/anon-task/get-anon-analyze?taskExecGuid=${execGuid}`,
method: 'get'
}), defaultOptions)
/** 获取匿名化任务分析结果数据 */
export const getLastAnonAnalyzeResult = (execGuid) => request({
url: `${import.meta.env.VITE_API_COMMON_URL}/anon-task/get-anon-analyze?isResult=true&taskExecGuid=${execGuid}`,
method: 'get'
......
import CommonTable from './src/CommonTable.vue'
export { CommonTable }
export default CommonTable
\ No newline at end of file
<script lang="ts" setup name="CommonTable">
import { ref, watch, computed } from "vue";
import { TableColumnWidth } from "@/utils/enum";
import { calcColumnWidth } from "@/utils/index";
import Moment from 'moment';
const props = defineProps({
data: {
type: Array,
default: () => []
},
fields: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
},
height: {
type: String,
default: '100%'
},
showIndex: {
type: Boolean,
default: false
},
rowKey: {
type: String,
default: 'guid'
},
style: {
type: Object,
default: () => ({})
}
});
const originTableFieldColumn = ref({});
const getTextAlign = (field) => {
if (field.dataType === 'decimal' || field.dataType === 'int' || field.dataType == 'bit' || field.dataType == 'tinyint') {
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 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(
() => props.data,
(val: any[], oldVal) => {
if (!props.fields?.length) {
originTableFieldColumn.value = {};
return;
}
originTableFieldColumn.value = {};
props.fields.forEach((field, index) => {
originTableFieldColumn.value[field.enName] = calcTableColumnWidth(
val?.slice(0, 20) || [],
field.enName,
field.chName,
24
);
});
},
{
deep: true,
}
);
</script>
<template>
<div class="common-table" v-loading="loading">
<el-table
:data="data"
:highlight-current-row="true"
stripe
border
tooltip-effect="light"
:height="height"
:row-key="rowKey"
:style="style"
>
<el-table-column
v-if="showIndex"
label="序号"
type="index"
width="56px"
align="center"
show-overflow-tooltip
></el-table-column>
<template v-for="(item, index) in (fields || [])" :key="index">
<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="!fields.length" class="empty-content">
<img src="@/assets/images/empty-data.png" :style="{ width: '168px', height: '96px' }" />
<div class="empty-text">暂无数据</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.common-table {
width: 100%;
height: 100%;
.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
......@@ -133,8 +133,8 @@ const routes: RouteRecordRaw[] = [
children: [
{
path: '',
name: 'resultProcess',
component: () => import('@/views/data_anonymization/resultProcess.vue'),
name: 'anonResultProcessManage',
component: () => import('@/views/data_anonymization/anonResultProcessManage.vue'),
meta: {
title: '匿名化处理',
sidebar: false,
......
<route lang="yaml">
name: anonResultProcessManage
</route>
<script lang="ts" setup name="anonResultProcessManage">
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 useDataAnonymizationStore from "@/store/modules/dataAnonymization";
const anonymizationStore = useDataAnonymizationStore();
const router = useRouter()
const { proxy } = getCurrentInstance() as any;
/** -------------- 搜索栏输入框配置 ------------------------ */
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 == 4 ? '外部数据' : (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", disabled: scope.row.isConfirm == 'Y' || scope.row.dataSource == 4, click: tableBtnHandles['edit'] }, {
label: '查看报告', value: 'report', disabled: scope.row.status != 'Y', click: tableBtnHandles['report']
}, {
label: '查看数据', value: 'view', disabled: scope.row.status != 'Y' || scope.row.handleType == '02', click: tableBtnHandles['view']
}, {
label: "删除", value: "delete", click: tableBtnHandles['delete']
}]
}
}
})
/** 搜索栏触发搜索 */
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 tableBtnHandles = {
edit: (scope) => {
router.push({
name: 'anonTaskCreate',
query: {
guid: scope.row.guid,
taskName: scope.row.taskName
}
});
},
report: (scope) => {
router.push({
name: 'anonResultReportView',
query: {
guid: scope.row.guid,
execGuid: scope.row.lastExecGuid,
taskName: scope.row.taskName
}
});
},
view: (scope) => {
router.push({
name: 'anonResultView',
query: {
guid: scope.row.guid,
execGuid: scope.row.lastExecGuid,
taskName: scope.row.taskName
}
});
},
delete: (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 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
......@@ -228,17 +228,16 @@ onMounted(() => {
onBeforeMount(() => {
resultDataLoading.value = true;
getAnonAnalyzeResult(taskExecGuid.value).then((res: any) => {
resultDataLoading.value = false;
if (res?.code == proxy.$passCode) {
analysisResultInfo.value = res.data || {};
getAnonAnalyzeResult(taskExecGuid.value, {
proxy: proxy,
loadingRef: resultDataLoading,
onSuccess: (res: any) => {
analysisResultInfo.value = res?.data || {};
analysisResultTableFields.value = res.data?.column || [];
pageInfo.value.curr = 1;
getAnalysisResultPageData(true);
} else {
res?.msg && proxy.$ElMessage.error(res.msg);
}
});
})
getAnonTaskDetail(taskGuid.value).then((res: any) => {
if (res?.code == proxy.$passCode) {
oldAnonTaskValueInfo.value = res.data || {};
......
......@@ -9,9 +9,6 @@ import {
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";
......@@ -69,56 +66,7 @@ const getData = () => {
});
}
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) {
......@@ -157,27 +105,7 @@ onBeforeMount(() => {
});
});
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);
......@@ -212,19 +140,14 @@ const exportData = () => {
<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>
<CommonTable
v-show="tableFields.length"
:data="tableData"
:fields="tableFields"
:loading="tableDataLoading"
:height="'100%'"
:style="{ width: '100%', height: !props.isPage ? 'calc(100% - 34px)' : 'calc(100% - 64px)' }"
/>
<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>
......
......@@ -98,7 +98,7 @@
</div>
</div>
<div class="result-title">重标识风险表</div>
<el-table ref="tableRef" v-show="analysisResultTableFields.length" :data="resultData"
<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="68px" align="center" show-overflow-tooltip>
......@@ -127,14 +127,14 @@
<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">
<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">门限阈值的取值:完全公开共享数据发布,取值
<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"
<PageNav v-show="analysisResultTableFields?.length" :class="[pageInfo.type]" :pageInfo="pageInfo"
@pageChange="pageChange" />
<div class="row-two-main" style="margin-top: 12px;">
<div class="table-one">
......@@ -199,7 +199,6 @@
</div>
</div>
<div v-show="isWordStyle" class="analysis-result-main report-main" ref="report">
<!-- TODO,报告里需要添加数据对象,对什么做了什么。 -->
<div
style="font-family: simsun;height: 40px;display: block;line-height: 32px;font-size: 18px;color: #212121;font-weight: 700;text-align: center;margin-top: 10px;margin-bottom: 10px;">匿名化效果评估指标</div>
<p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;white-space: pre-wrap;color: #212121;">{{ ' 依据GB/T 42460-2023《信息安全技术个人信息去标识化效果评估指南》附录D,对去标识化以后的数据集基于K匿名模型进行去标识化效果的评估。' }}</p>
......@@ -395,7 +394,7 @@ const props = defineProps({
},
analysisResultTableFields: {
type: Array,
default: [],
default: <any>[],
},
originResultTableFieldColumn: {
type: Object,
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!