5f0a75ec by lihua

提交泛化文件迁移

1 parent bb0d0c63
/**
* 匿名化管理的接口api文件
*/
import request from "@/utils/request";
/** 字段类型 */
export const fieldTypeList = [{
value: '1',
label: '日期'
}, {
value: '2',
label: '字符串'
}, {
value: '3',
label: '数值'
}]
/** 获取泛化文件列表 */
export const getGeneralizeFileList = (params) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/generalize-file/page-list`,
method: 'post',
data: params
})
/** 获取泛化文件列表,包括名称和guid */
export const getGeneralizeFileNameList = () => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/generalize-file/name-list`,
method: 'post'
})
export const saveGeneralizeFile = (data) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/generalize-file/save`,
method: 'post',
data
})
/** 删除泛化文件 */
export const deleteGeneralizeFile = (data) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/generalize-file/delete`,
method: 'delete',
data
})
/** 获取泛化文件详情 */
export const getGeneralizeFileDetail = (guid) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/generalize-file/detail?guid=${guid}`,
method: 'get'
})
export const updateGeneralizeFile = (data) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/generalize-file/update`,
method: 'put',
data
})
/** 获取泛化文件解析结果 */
export const parseGeneralizeFileData = (data) => request({
url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/generalize-file/parse-file`,
method: 'post',
data
})
\ No newline at end of file
......@@ -217,6 +217,46 @@ const routes: RouteRecordRaw[] = [
},
}]
},
{
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
......
const useDataAnonymizationStore = defineStore(
// 资产目录guid
'anonymization',
() => {
const isRefresh = ref<boolean>(false);
function setIsRefresh(v: boolean) {
isRefresh.value = v;
}
const isAnonPageRefresh = ref<boolean>(false);
function setIsAnonPageRefresh(v: boolean) {
isAnonPageRefresh.value = v;
}
return {
isRefresh,
isAnonPageRefresh,
setIsRefresh,
setIsAnonPageRefresh,
};
}
);
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,
});
}
});
}, () => {
proxy.$ElMessage.info("已取消");
})
}
}]
}
}
})
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
<script lang="ts" setup name="generalizeFileEdit">
import {
fieldTypeList,
saveGeneralizeFile,
getGeneralizeFileDetail,
updateGeneralizeFile,
deleteGeneralizeFile,
parseGeneralizeFileData,
} from '@/api/modules/dataAnonymization';
import {
parseAndDecodeUrl,
getDownFileSignByUrl,
obsDownloadRequest
} from "@/api/modules/obsService";
import useUserStore from "@/store/modules/user";
import { useValidator } from '@/hooks/useValidator';
import * as XLSX from 'xlsx';
import { TableColumnWidth } from '@/utils/enum';
import { calcColumnWidth } from "@/utils/index";
import Moment from 'moment';
import useDataAnonymizationStore from "@/store/modules/dataAnonymization";
import { ElMessage } from 'element-plus';
const anonymizationStore = useDataAnonymizationStore();
const { required } = useValidator();
const userStore = useUserStore();
const router = useRouter();
const route = useRoute();
const { proxy } = getCurrentInstance() as any;
const fullPath = route.fullPath;
const guid = ref(route.query.guid);
const baseInfoItems = ref([{
label: '泛化文件名称',
type: 'input',
maxlength: 15,
placeholder: '请输入',
field: 'generalizeFileName',
default: '',
block: false,
clearable: true,
required: true
}, {
type: "select",
label: "字段类型",
field: "fieldType",
default: '2',
options: fieldTypeList,
placeholder: "请选择",
clearable: true,
filterable: true,
required: true,
block: false,
}, {
label: '最小泛化层级',
field: 'generalizeLevel',
placeholder: "请输入",
type: 'input',
inputType: 'integerNumber',
maxlength: 6,
default: null,
clearable: true,
required: true,
}]);
const baseInfoFormRules = ref({
generalizeFileName: [required('请输入泛化文件名称')],
fieldType: [required('请选择字段类型')],
generalizeLevel: [required('请输入最小泛化层级')]
});
/** 文件上传表单配置。 */
const fileFormItems: any = ref([
{
label: '选择文件上传',
tip: '支持扩展名:xlsx、xls、csv,文件大小不超过10MB',
type: 'upload-file',
accept: '.xlsx, .xls, .csv',
limit: 1,
limitSize: 10,
isExcel: true,
required: true,
default: [],
block: false,
field: 'file',
}
]);
const fileFormRules = ref({
file: [{
validator: (rule: any, value: any, callback: any) => {
if (!value?.length) {
callback(new Error('请上传文件'))
} else {
callback();
}
}, trigger: 'change'
}]
});
/** 上传文件之后解析出字段和数据 */
const fileFieldLoading = ref(false);
const fileTableFields: any = ref([]);
const fileTableData: any = ref([]);
const parseFileData = (fileRaw) => {
fileFieldLoading.value = true;
let file = fileFormItems.value[0].default;
parseGeneralizeFileData({
name: file[0]?.name,
url: file[0]?.url
}).then((res: any) => {
fileFieldLoading.value = false;
if (res?.code == proxy.$passCode) {
let result = res.data?.fileDataRQVOS || [];
let resultFields = res.data?.sheetHeader || [];
fileRaw.arrayBuffer().then((f) => {
const wb = XLSX.read(f, {
raw: false, cellDates: true
});
const sheet = wb.Sheets[wb.SheetNames[0]];
const json: any[] = XLSX.utils.sheet_to_json(sheet, { header: 1 });
if (json.length == 0) {
fileTableFields.value = [];
fileTableData.value = [];
} else {
fileTableFields.value = resultFields?.map((j, index) => {
return {
index: index,
enName: j.enName + '',
chName: j.chName + '',
dataType: 'varchar'
}
}) || [];
if (json.length > 1) {
json.slice(1, 20).map((info, row) => {
let object = {};
json[0].forEach((name, col) => {
if (info[col] === "" || info[col] == null) {
object[name] = info[col];
} else {
var cellRef = XLSX.utils.encode_cell({ r: row + 1, c: col });
var cell = sheet[cellRef];
object[name] = cell.w || info[col];
let isNum = cell.t == 'n';
if (isNum && fileTableFields.value[col].dataType != 'int') {
fileTableFields.value[col].dataType = 'int';
}
}
});
return object;
});
fileTableData.value = result.slice(0, 50)?.map(d => {
return d.fileData || {};
})
} else {
fileTableData.value = [];
}
}
fileFieldLoading.value = false;
});
} else {
proxy.$ElMessage.error(res.msg);
}
});
}
const uploadFileChange = (file) => {
fileTableFields.value = [];
fileTableData.value = [];
if (!file.length) {
fileTableFields.value = [];
fileTableData.value = [];
fileFormItems.value[0].default = file;
return;
}
let fileRaw = file[0].file;
fileFormItems.value[0].default = file;
parseFileData(fileRaw);
}
/** 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(
fileTableData,
(val: any[], oldVal) => {
if (!fileTableFields.value?.length) {
originTableFieldColumn.value = {};
return;
}
originTableFieldColumn.value = {};
fileTableFields.value.forEach((field, index) => {
originTableFieldColumn.value[field.enName] = calcTableColumnWidth(
val?.slice(0, 20) || [],
field.enName,
field.chName,
24
);
});
},
{
deep: true,
}
);
const formatterPreviewDate = (row, info) => {
let enName = info.enName;
let v = row[enName];
if (v === 0) {
return v;
}
if (!v) {
return v || '--';
}
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 cancel = () => {
proxy.$openMessageBox("当前页面尚未保存,确定放弃修改吗?", () => {
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
router.push({
name: 'generalizeFile'
});
}, () => {
proxy.$ElMessage.info("已取消");
});
}
const formRef = ref();
/** 文件表单 */
const fileFormRef = ref();
const saveLoading = ref(false);
const saveUpdate = async () => {
formRef.value?.ruleFormRef?.validate((valid, errorItem) => {
fileFormRef.value?.ruleFormRef?.validate((fileValid, errorItem) => {
if (valid && fileValid) {
let fileJson = fileFormRef.value?.formInline.file || [];
let params = Object.assign({}, formRef.value.formInline, {
filePath: {
name: fileJson[0]?.name,
url: fileJson[0]?.url
}
})
if (!guid.value) {
saveLoading.value = true;
saveGeneralizeFile(params).then((res: any) => {
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('泛化文件新建成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
anonymizationStore.setIsRefresh(true);
router.push({
name: 'generalizeFile'
});
saveLoading.value = false;
} else {
saveLoading.value = false;
proxy.$ElMessage.error(res.msg);
}
})
} else {
params.guid = guid.value;
saveLoading.value = true;
updateGeneralizeFile(params).then((res: any) => {
if (res?.code == proxy.$passCode) {
proxy.$ElMessage.success('泛化文件编辑成功');
userStore.setTabbar(userStore.tabbar.filter((tab: any) => tab.fullPath !== fullPath));
anonymizationStore.setIsRefresh(true);
router.push({
name: 'generalizeFile'
});
saveLoading.value = false;
} else {
saveLoading.value = false;
proxy.$ElMessage.error(res.msg);
}
})
}
}
});
})
}
const fullscreenLoading = ref(false);
onBeforeMount(() => {
if (guid.value) {
fullscreenLoading.value = true;
getGeneralizeFileDetail(guid.value).then(async (res: any) => {
if (res?.code == proxy.$passCode) {
let detail = res.data || {};
baseInfoItems.value.forEach(item => {
item.default = detail[item.field];
});
fileFormItems.value.forEach(item => {
item.default = [{
name: detail.filePath.name,
url: detail.filePath.url
}]
});
let url = detail.filePath.url;
const refSignInfo: any = await getDownFileSignByUrl(parseAndDecodeUrl(url).fileName);
if (!refSignInfo?.data) {
fullscreenLoading.value = false;
refSignInfo?.msg && ElMessage.error(refSignInfo?.msg);
return;
}
obsDownloadRequest(refSignInfo?.data).then((res: any) => {
if (res && !res.msg) {
parseFileData(res);
fullscreenLoading.value = false;
} else {
fullscreenLoading.value = false;
res?.msg && ElMessage.error(res?.msg);
}
})
} else {
fullscreenLoading.value = false;
proxy.$ElMessage.error(res.msg);
}
});
}
})
onActivated(() => {
const fileName = route.query.fileName;
let tab: any = userStore.tabbar.find((tab: any) => tab.fullPath === fullPath);
if (tab) {
if (fileName) {
tab.meta.title = `编辑-${fileName}`;
}
if (fullPath === route.fullPath) {
document.title = tab.meta.title;
}
}
});
</script>
<template>
<div class="container_wrap" v-loading="fullscreenLoading">
<div class="content_main">
<ContentWrap id="id-baseInfo" title="基本信息" description="" style="margin-top: 8px;">
<Form ref="formRef" :itemList="baseInfoItems" :rules="baseInfoFormRules" formId="main-model-edit" col="col3" />
</ContentWrap>
<ContentWrap id="id-grade-info" title="上传文件" class="detail-content" description="" style="margin-top: 8px;">
<Form ref="fileFormRef" :itemList="fileFormItems" :noUpload="false" formId="file-form" :rules="fileFormRules"
@uploadFileChange="uploadFileChange" />
<div v-show="fileTableFields.length" class="preview-data-totals">
<span>该表仅显示</span>
<span class="fontC-4fa1a4"> 50 </span>
<span>条数据</span>
</div>
<el-table ref="tableRef" v-loading="fileFieldLoading" v-show="fileTableFields.length" :data="fileTableData"
:highlight-current-row="true" stripe border tooltip-effect="light" height="100%" row-key="guid"
:style="{ width: '100%', height: '280px' }">
<template v-for="(item, index) in (fileTableFields || [])">
<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>
</ContentWrap>
</div>
<div class="bottom_tool_wrap">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="saveUpdate" :loading="saveLoading">提交</el-button>
</div>
</div>
</template>
<style lang="scss" scoped>
.container_wrap {
padding: 0px;
}
.content_main {
height: calc(100% - 44px);
padding: 10px 16px;
overflow: auto;
.table-top-btns {
margin-bottom: 12px;
}
}
.bottom_tool_wrap {
height: 44px;
padding: 0 16px;
border-top: 1px solid #d9d9d9;
display: flex;
justify-content: center;
align-items: center;
}
.tools_btns {
position: relative;
margin-bottom: 16px;
.show-change-btn {
position: absolute;
right: 0px;
}
}
.detail-content {
position: relative;
}
.preview-data-totals {
position: absolute;
top: 150px;
right: 16px;
font-size: 14px;
color: #666666;
font-weight: 400;
.fontC-4fa1a4 {
color: var(--el-color-primary);
}
}
</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!