diff --git a/src/api/system/systemLog.ts b/src/api/system/systemLog.ts index 4d0f7a1d..b3a5ca32 100644 --- a/src/api/system/systemLog.ts +++ b/src/api/system/systemLog.ts @@ -16,3 +16,8 @@ export const getHourlyOnlineStatisticsApi = (params: any) => { export const getUserLoginStatisticsApi = (params: any) => { return post(`${PREFIX}systemLog/getUserLoginStatistics`, params); }; + +// 统计某时间段(天)的文件下载数 +export const getDailyOperateStatisticsApi = (params: any) => { + return post(`${PREFIX}systemLog/getDailyOperateStatistics`, params); +}; diff --git a/src/components/common/treeCaseTable/nodeDetailDialog.vue b/src/components/common/treeCaseTable/nodeDetailDialog.vue index 9ae38cf0..a7b69259 100644 --- a/src/components/common/treeCaseTable/nodeDetailDialog.vue +++ b/src/components/common/treeCaseTable/nodeDetailDialog.vue @@ -26,17 +26,20 @@ @change="onFormChangeFun" > + @@ -58,15 +61,18 @@ import { getTagKeyMap, NODE_TYPE } from '@/utils/enum/node'; import { disposeTaskMembers } from '@/utils/task'; import { disposeTagKey } from '@/views/task/projectDetail/components/project'; import { isCategoryType, isCategoryNodeType, validateCategoryLevel } from '@/utils/node'; -import flowTemplateSelect from './flowTemplateSelect.vue'; -import knowledgeSelect from './knowledgeSelect.vue'; -import nodeNameMixed from './nodeNameMixed.vue'; +import FlowTemplateSelect from './flowTemplateSelect.vue'; +import ReportTemplateSelect from './reportTemplateSelect.vue'; +import KnowledgeSelect from './knowledgeSelect.vue'; +import NodeNameMixed from './nodeNameMixed.vue'; import { TABLE_NAME } from '@/utils/enum/tableName'; import { useDict } from '@/utils/useDict'; import { useI18n } from 'vue-i18n'; import { useTaskStore } from '@/stores/taskPool'; +import { useReportStore } from '@/stores/reportTemplate'; const taskStore = useTaskStore(); +const reportStore = useReportStore(); enum OPERATION_TYPE { ADD = 'add', EDIT = 'edit', @@ -138,6 +144,7 @@ watch( } }); taskStore.fetchTemplates(true); + reportStore.fetchTemplates(true); } } ); diff --git a/src/components/common/treeCaseTable/reportTemplateSelect.vue b/src/components/common/treeCaseTable/reportTemplateSelect.vue new file mode 100644 index 00000000..1a258cba --- /dev/null +++ b/src/components/common/treeCaseTable/reportTemplateSelect.vue @@ -0,0 +1,210 @@ + + + + + + + diff --git a/src/components/common/treeCaseTable/treeTable.vue b/src/components/common/treeCaseTable/treeTable.vue index d434e7ac..11f20e01 100644 --- a/src/components/common/treeCaseTable/treeTable.vue +++ b/src/components/common/treeCaseTable/treeTable.vue @@ -322,6 +322,21 @@ + + + - +
@@ -68,6 +69,9 @@ const formAttrs = ref({ step: 0.01, }, }); +const searchParams = ref({ + businessId: props.taskInfo.uuid, +}); const localDetail = ref({}); @@ -186,7 +190,8 @@ onMounted(() => {}); } } .oprate-log { - flex: 1; + // flex: 1; + height: calc(100% - 210px); } } diff --git a/src/stores/reportTemplate.ts b/src/stores/reportTemplate.ts new file mode 100644 index 00000000..3bfeeec2 --- /dev/null +++ b/src/stores/reportTemplate.ts @@ -0,0 +1,57 @@ +import { defineStore } from 'pinia'; +import { ref, computed } from 'vue'; +import { queryReportTemplateApi } from '@/api/capability/report'; +import { REPORT_TEMPLATE_PUBLIC_STATUS, FLOW_USE_STATUS } from '@/utils/enum/report'; + +interface ReportTemplate { + templateName?: string; + templateCode?: string; + [k: string]: any; +} + +export const useReportStore = defineStore('reportTemplate', () => { + const templates = ref([]); + const loaded = ref(false); + let pendingPromise: Promise | null = null; + + const options = computed(() => + templates.value.map((item) => ({ + label: item.templateName || '', + value: item.templateCode || '', + })) + ); + + const fetchTemplates = async (force = false) => { + if (!force && loaded.value && templates.value.length) return templates.value; + if (pendingPromise) return pendingPromise; + + pendingPromise = (async () => { + try { + const req = { + current: 1, + size: 99999, + type: REPORT_TEMPLATE_PUBLIC_STATUS.PUBLIC, + templateStatus: FLOW_USE_STATUS.USED, + }; + const res: any = await queryReportTemplateApi(req); + if (res && res.code === 200) { + const list = res.data?.data || res.data || []; + templates.value = Array.isArray(list) ? list : []; + } else { + templates.value = []; + } + loaded.value = true; + } catch { + templates.value = []; + loaded.value = true; + } finally { + pendingPromise = null; + } + return templates.value; + })(); + + return pendingPromise; + }; + + return { templates, options, fetchTemplates, loaded }; +}); diff --git a/src/utils/enum/node.ts b/src/utils/enum/node.ts index 9ae06daa..627564ad 100644 --- a/src/utils/enum/node.ts +++ b/src/utils/enum/node.ts @@ -108,6 +108,7 @@ export const poolNodePropPickMap = { 'days', 'flowTemplate', 'standard', + 'reportTemplate', 'analyseTarget', 'confidence', 'analyseSoftware', diff --git a/src/utils/i18n/en.ts b/src/utils/i18n/en.ts index f608387d..fb2dc654 100644 --- a/src/utils/i18n/en.ts +++ b/src/utils/i18n/en.ts @@ -87,6 +87,9 @@ const lang = { 项目详情: 'Project Detail', 任务创建: 'Task Creation', 仿真执行: 'Task Execution', + 报工列表: 'Work Report List', + 我确认的: 'I Confirmed', + 我负责的: 'I Responsible', 应用中心: 'Application Center', 数据管理: 'Data Management', 数据总览: 'Data Overview', @@ -201,6 +204,7 @@ const lang = { 没有修改的数据需要提交审批: 'No modified data to submit for approval', 指标库列表: 'Performance Pool List', 关联N个流程模版: 'Associated {count} Flow Templates', + 关联N个报告模版: 'Associated {count} Report Templates', 关联N个执行规范: 'Associated {count} Standards', 关联N个工况: 'Associated {count} Tasks', 请选择工况库: 'Please select task pool', @@ -329,6 +333,7 @@ const lang = { '指标完成统计(学科)': 'Performance Completion Statistics (Discipline)', 工位评审通过统计: 'Workspace Review Statistics', 项目任务完成情况统计: 'Project Task Completion Statistics', + 文件操作统计: 'File Operate Statistics', }, 工位时间维护: { 阶段: 'Phase', diff --git a/src/utils/i18n/zh.ts b/src/utils/i18n/zh.ts index f60b4902..b8fd02f2 100644 --- a/src/utils/i18n/zh.ts +++ b/src/utils/i18n/zh.ts @@ -86,6 +86,9 @@ const lang = { 项目详情: '项目详情', 任务创建: '任务创建', 仿真执行: '仿真执行', + 报工列表: '报工列表', + 我确认的: '我确认的', + 我负责的: '我负责的', 应用中心: '应用中心', 数据管理: '数据管理', 数据总览: '数据总览', @@ -197,6 +200,7 @@ const lang = { 没有修改的数据需要提交审批: '没有修改的数据需要提交审批', 指标库列表: '指标库列表', 关联N个流程模版: '关联{count}个流程模版', + 关联N个报告模版: '关联{count}个报告模版', 关联N个执行规范: '关联{count}个执行规范', 关联N个工况: '关联{count}个工况', 请选择工况库: '请选择工况库', @@ -324,6 +328,7 @@ const lang = { '指标完成统计(学科)': '指标完成统计(学科)', 工位评审通过统计: '工位评审通过统计', 项目任务完成情况统计: '项目任务完成情况统计', + 文件操作统计: '文件操作统计', }, 工位时间维护: { 阶段: '阶段', diff --git a/src/views/simulation/parameter/components/addParamObject.vue b/src/views/simulation/parameter/components/addParamObject.vue index c894ee49..f1d30cfe 100644 --- a/src/views/simulation/parameter/components/addParamObject.vue +++ b/src/views/simulation/parameter/components/addParamObject.vue @@ -7,13 +7,16 @@ v-model:data="formData" showDisabled :itemNum="6" + :hideKeys="hideKeys" @change="changeFun" + @remove="removeFun" :formAttrs="{ templateId: { moduleCode: 'PARAM_APPROVAL', }, files: { accept: '.json', + multiple: false, }, }" /> @@ -55,22 +58,37 @@ const closeFun = () => { }; const onConfirmFun = async () => { + if (!formData.value.files.length) { + formData.value.templateId = ''; + formData.value.templateName = ''; + } const valid = await tableFormRef.value?.validateFun(); if (valid) { emits('createFun', formData.value); } }; +const hideKeys = ref(['templateId']); + const changeFun = (data: any) => { const { key, val } = data; if (key === 'files') { formData.value.file = val.raw; formData.value.fileName = val.name; + hideKeys.value = []; } if (key === 'templateId') { formData.value.templateName = val.label; } }; +const removeFun = (data: any) => { + const { key } = data; + if (key === 'files') { + formData.value.file = ''; + formData.value.fileName = ''; + hideKeys.value = ['templateId']; + } +}; onMounted(() => { if (props.libTypeInfo) { diff --git a/src/views/simulation/parameter/index.vue b/src/views/simulation/parameter/index.vue index 24068ccc..64e9fbda 100644 --- a/src/views/simulation/parameter/index.vue +++ b/src/views/simulation/parameter/index.vue @@ -421,10 +421,12 @@ const creatLibParamObjFun = async (data: any) => { parameterLibraryId: data.parameterLibraryId, parameterLibraryCategoryId: data.parameterLibraryCategoryId, parameterLibraryCategoryObjectName: data.parameterLibraryCategoryObjectName, - fileName: data.fileName, - file: data.file, - templateId: data.templateId, - templateName: data.templateName, + ...(data.file && { + fileName: data.fileName, + file: data.file, + templateId: data.templateId, + templateName: data.templateName, + }), }); if (res && res.code === 200) { @@ -434,6 +436,8 @@ const creatLibParamObjFun = async (data: any) => { return item.name === data.parameterLibraryCategoryObjectName; }); libTreeRef.value.append(node, currentNode.value.nodeKey); + } else { + ElMessage.warning(res.message); } showParamObjVisible.value = false; }; @@ -454,15 +458,25 @@ const deleteConfirmFun = () => { const deleteParamFun = async (flag: any, row?: any) => { currentNode.value = { ...row }; - // 如果删除的是参数,要走审批流程 + // 如果删除的是参数对象, if (flag === 'param') { - delParams.value = { - // type: currentNode.value.type, - // id: currentNode.value.id, - type: row.type, - id: row.id, - }; - approveDelShow.value = true; + // 有参数 要走审批流程,没参数可以直接删 + const res: any = await getSimulationParameterLibraryCategoryObjectApi({ + ObjectId: row.id, + }); + if (res && res.code === 200) { + if (res.data && res.data.parameterJsonValue && res.data.parameterJsonValue.length) { + delParams.value = { + type: row.type, + id: row.id, + }; + approveDelShow.value = true; + } else { + // 直接删除 + deleteAction(flag, row); + } + } + return; } else { // 0228需求 参数库、分类删除逻辑修改:下级有参数data=true,走审核流程;下级没有参数,直接删除 @@ -478,32 +492,36 @@ const deleteParamFun = async (flag: any, row?: any) => { }; approveDelShow.value = true; } else { - // 走删除流程 - ElMessageBox.confirm( - `确认要删除这个${flag === 'lib' ? '参数库' : flag === 'type' ? '参数库分类' : '参数对象'}吗?`, - '警告', - { - confirmButtonText: '确认', - cancelButtonText: '取消', - type: 'warning', - } - ).then(async () => { - const res: any = await deleteSimulationParameterApi({ - type: row.type, - id: row.id, - isApprove: false, - }); - if (res && res.code === 200) { - ElMessage.success('删除成功!'); - libTreeRef.value.remove(currentNode.value.nodeKey); - currentNode.value = {}; - paramObjInfo.value = ''; - } - }); + deleteAction(flag, row); } } }; +const deleteAction = (flag: any, row: any) => { + // 走删除流程 + ElMessageBox.confirm( + `确认要删除这个${flag === 'lib' ? '参数库' : flag === 'type' ? '参数库分类' : '参数对象'}吗?`, + '警告', + { + confirmButtonText: '确认', + cancelButtonText: '取消', + type: 'warning', + } + ).then(async () => { + const res: any = await deleteSimulationParameterApi({ + type: row.type, + id: row.id, + isApprove: false, + }); + if (res && res.code === 200) { + ElMessage.success('删除成功!'); + libTreeRef.value.remove(currentNode.value.nodeKey); + currentNode.value = {}; + paramObjInfo.value = ''; + } + }); +}; + const updateParams = ref({}); const approveUpdateShow = ref(false); const updateParamFun = () => { diff --git a/src/views/simulation/reportLib/components/addDialog.vue b/src/views/simulation/reportLib/components/addDialog.vue index 96e127c6..a9a8124c 100644 --- a/src/views/simulation/reportLib/components/addDialog.vue +++ b/src/views/simulation/reportLib/components/addDialog.vue @@ -45,7 +45,7 @@ 取消 确定 { if (await tableFormRef.value.validateFun()) { loadingInterface.value = true; const flowForm = tableFormRef.value.getFormDataFun(); + flowForm.simulationPoolInfoList = simulationPoolInfoList.value; if ( props.dialogType === REPORT_OPERATION_TYPE.CREATE || props.dialogType === REPORT_OPERATION_TYPE.COPY ) { const formData = new FormData(); formData.append('templateVersion', 'V1.0'); + formData.append('simulationPoolInfoListStr', JSON.stringify(simulationPoolInfoList.value)); for (const key in flowForm) { if (key === 'file') { // 就算复制模板,如果上传了新的文件就不复制文件了 @@ -122,7 +124,8 @@ const confirmFun = async (type: string) => { key !== 'templateStatus' && key !== 'creatorName' && key !== 'createTime' && - key !== 'approveType' + key !== 'approveType' && + key !== 'simulationPoolInfoList' ) { formData.append(key, flowForm[key] ? flowForm[key] : ''); } @@ -130,17 +133,27 @@ const confirmFun = async (type: string) => { } const flowUuid = await createFlow(formData); emits('confirm', { type, uuid: flowUuid }); - } else if (props.dialogType === REPORT_OPERATION_TYPE.UPGRADE) { - emits('confirm', { - type, - uuid: flowForm.uuid, - comment: flowForm.comment, - file: flowForm.file, - fileId: flowForm.file[0].id, - }); } else { - await editReport(flowForm); - emits('confirm', { type, uuid: flowForm.uuid }); + if (type === 'ok') { + await editReport(flowForm); + emits('confirm', { type, uuid: flowForm.uuid, tVersion: props.templateVersion }); + } + if (type === 'next') { + if (oldComment.value !== flowForm.comment) { + // 升版时只更新描述,不更新工况,工况在提交审批时传递给接口 + await editReport({ ...flowForm, simulationPoolInfoList: oldSimulationPoolInfoList.value }); + } + emits('confirm', { + type, + uuid: flowForm.uuid, + comment: flowForm.comment, + file: flowForm.file, + fileId: flowForm.file[0].id, + tVersion: props.templateVersion, + // 不更新工况,工况在提交审批时传递给升版接口 + simulationPoolInfoList: hasChangeLoadcase.value ? simulationPoolInfoList.value : null, + }); + } } loadingInterface.value = false; } @@ -188,6 +201,7 @@ const editReport = async (params: any) => { formData.append('extras', params.extras); formData.append('fileId', oldFormData.value.fileId); formData.append('templateContent', oldFormData.value.templateContent); + formData.append('simulationPoolInfoListStr', JSON.stringify(params.simulationPoolInfoList)); if (params.file && params.file.length > 0) { if (params.file[0].raw) { formData.append('file', params.file[0].raw); @@ -217,12 +231,17 @@ const closeFun = () => { const oldFormData = ref({}); -const editRowInfo = ref({}); +const editRowInfo = ref({}); + +const oldSimulationPoolInfoList = ref([]); +const oldComment = ref(''); const setEditForm = (val: any) => { oldFormData.value = val; // nextTick(() => { simulationPoolInfoList.value = val.simulationPoolInfoList || []; + oldSimulationPoolInfoList.value = val.simulationPoolInfoList || []; + oldComment.value = val.comment; // tableFormRef.value.setFormDataFun({ ...val }); editRowInfo.value = { ...val }; // }); @@ -245,6 +264,29 @@ const setOptionsFun = (key: string, options: any[]) => { tableFormRef.value.setOptionsFun(key, options); }; +const hasChangeLoadcase = ref(false); + +watch( + () => simulationPoolInfoList.value, + () => { + const oldIds: any[] = []; + oldSimulationPoolInfoList.value.forEach((item) => { + oldIds.push(...item.simulationPoolTaskIds); + }); + const newIds: any[] = []; + simulationPoolInfoList.value.forEach((item) => { + newIds.push(...item.simulationPoolTaskIds); + }); + oldIds.sort(); + newIds.sort(); + if (JSON.stringify(oldIds) !== JSON.stringify(newIds)) { + hasChangeLoadcase.value = true; + } else { + hasChangeLoadcase.value = false; + } + } +); + watch( () => props.showDialog, async () => { diff --git a/src/views/task/dashboard/components/dataStatistics.vue b/src/views/task/dashboard/components/dataStatistics.vue index b0eb5e6d..e79a1d37 100644 --- a/src/views/task/dashboard/components/dataStatistics.vue +++ b/src/views/task/dashboard/components/dataStatistics.vue @@ -28,6 +28,7 @@ import { onMounted, ref, provide } from 'vue'; import { getThemeColor } from '@/utils/theme'; // 引入子组件 +import fileOperateChart from './dataStatistics/fileOperateChart.vue'; import userGroupProjectChart from './dataStatistics/userGroupProjectChart.vue'; import userTaskCompleteChart from './dataStatistics/userTaskCompleteChart.vue'; import userDifficultyCoefficientChart from './dataStatistics/userDifficultyCoefficientChart.vue'; @@ -145,6 +146,11 @@ const baseList = ref([ processNodeColorList, }, }, + { + id: 'fileOperate', + component: fileOperateChart, + props: {}, + }, ]); // 当前展示的图表 const currentList = ref(); diff --git a/src/views/task/dashboard/components/dataStatistics/fileOperateChart.vue b/src/views/task/dashboard/components/dataStatistics/fileOperateChart.vue new file mode 100644 index 00000000..3acd97d0 --- /dev/null +++ b/src/views/task/dashboard/components/dataStatistics/fileOperateChart.vue @@ -0,0 +1,115 @@ + + + diff --git a/src/views/task/dashboard/components/dataStatistics/settingDia.vue b/src/views/task/dashboard/components/dataStatistics/settingDia.vue index 067a1e07..ae6e2213 100644 --- a/src/views/task/dashboard/components/dataStatistics/settingDia.vue +++ b/src/views/task/dashboard/components/dataStatistics/settingDia.vue @@ -86,6 +86,7 @@ const baseTable = [ { key: 'performanceCompletionMechine', title: '指标完成统计(机台)', inBoard: true }, { key: 'performanceCompletionDiscipline', title: '指标完成统计(学科)', inBoard: true }, { key: 'reviewPassed', title: '工位评审通过统计', inBoard: true }, + { key: 'fileOperate', title: '文件下载统计', inBoard: true }, ]; const vxeTableRef = ref(); diff --git a/src/views/task/projectDetail/components/taskDetail.vue b/src/views/task/projectDetail/components/taskDetail.vue index 890e2aae..dd3c34bb 100644 --- a/src/views/task/projectDetail/components/taskDetail.vue +++ b/src/views/task/projectDetail/components/taskDetail.vue @@ -9,7 +9,7 @@ show-footer >
- + - + @@ -350,7 +350,7 @@ const reportInpDiaShow = ref(false); const dialogVisible = ref(true); // const activeTab = ref(props.showTaskInfo ? 'info' : 'model-3d'); const activeTab = ref('info'); -const activeRadio = ref('report'); +const activeRadio = ref('detail'); const keyResultType = ref('performance'); const taskPerformanceRef = ref(); const taskInfoRef = ref();