返回
@@ -35,13 +31,7 @@
-
+
项目甘特图
@@ -57,12 +47,7 @@
-
+
@@ -76,6 +61,7 @@ import * as echarts from 'echarts';
import { useResizeObserver } from '@vueuse/core';
import { getErpProjectPlan } from '@/api/oa/erp/erpProjectPlan';
import type { ErpProjectPlanVO } from '@/api/oa/erp/erpProjectPlan/types';
+import { queryProjectChangeByProjectPlanId } from '@/api/oa/erp/erpProjectChange';
interface GanttTask {
taskId: string;
@@ -88,14 +74,15 @@ interface GanttTask {
realEnd?: string;
dependencyLabels?: string;
remark?: string;
+ originalPlanStart?: string; // 原计划开始时间
+ originalPlanEnd?: string; // 原计划结束时间
+ hasChange?: boolean; // 是否有变更
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute();
const router = useRouter();
-const { project_plan_status, project_phases } = toRefs
(
- proxy?.useDict('project_plan_status', 'project_phases')
-);
+const { project_plan_status, project_phases } = toRefs(proxy?.useDict('project_plan_status', 'project_phases'));
const loading = ref(false);
const showActual = ref(true);
@@ -134,7 +121,7 @@ const lastPlanEndDate = computed(() => {
return '-';
}
let maxTs: number | null = null;
- taskList.value.forEach(task => {
+ taskList.value.forEach((task) => {
const ts = toTimestamp(task.planEnd);
if (ts !== null) {
if (maxTs === null || ts > maxTs) {
@@ -195,7 +182,7 @@ const updateChart = () => {
return;
}
- const categories = taskList.value.map(task => task.taskName || '未命名任务');
+ const categories = taskList.value.map((task) => task.taskName || '未命名任务');
let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY;
@@ -314,13 +301,16 @@ const updateChart = () => {
if (!task) {
return params.name;
}
- const planText = `计划:${task.planStart || '-'} 至 ${task.planEnd || '-'}`;
+ let planText = `计划:${task.planStart || '-'} 至 ${task.planEnd || '-'}`;
+ if (task.hasChange) {
+ planText += `
(最新变更) 原计划:${task.originalPlanStart || '-'} 至 ${task.originalPlanEnd || '-'}`;
+ }
const actualText = `实际:${task.realStart || '-'} 至 ${task.realEnd || '-'}`;
return [params.marker + (task.taskName || '未命名任务'), planText, actualText].join('
');
}
},
legend: {
- data: series.map(s => s.name),
+ data: series.map((s) => s.name),
top: 0
},
grid: {
@@ -363,25 +353,35 @@ watch(showActual, () => {
updateChart();
});
-const buildTaskListFromStages = (stages: any[] = []): GanttTask[] =>
- stages.map(stage => ({
- taskId: String(stage.planStageId ?? generateTaskId()),
- taskName:
- phaseMap.value[stage.projectPhases as string] ||
- stage.projectPhases ||
- stage.scheduleRemark ||
- '未命名阶段',
- phaseName: phaseMap.value[stage.projectPhases as string] || '-',
- ownerName: planDetail.value?.managerName,
- planStart: normalizeDate(stage.planStartTime),
- planEnd: normalizeDate(stage.planEndTime),
- realStart: normalizeDate(stage.realStartTime),
- realEnd: normalizeDate(stage.realEndTime),
- dependencyLabels: Array.isArray(stage.dependencyNames)
- ? stage.dependencyNames.join('、')
- : stage.dependencyNames || '-',
- remark: stage.scheduleRemark || '-'
- }));
+const buildTaskListFromStages = (stages: any[] = [], changeMap: Map = new Map()): GanttTask[] =>
+ stages.map((stage) => {
+ const originalStart = normalizeDate(stage.planStartTime);
+ const originalEnd = normalizeDate(stage.planEndTime);
+
+ // 检查是否有变更
+ const changeInfo = changeMap.get(stage.planStageId);
+ const hasChange = !!changeInfo;
+
+ // 如果有变更,优先使用变更后的时间
+ const planStart = hasChange && changeInfo.changedStart ? normalizeDate(changeInfo.changedStart) : originalStart;
+ const planEnd = hasChange && changeInfo.changedEnd ? normalizeDate(changeInfo.changedEnd) : originalEnd;
+
+ return {
+ taskId: String(stage.planStageId ?? generateTaskId()),
+ taskName: phaseMap.value[stage.projectPhases as string] || stage.projectPhases || stage.scheduleRemark || '未命名阶段',
+ phaseName: phaseMap.value[stage.projectPhases as string] || '-',
+ ownerName: planDetail.value?.managerName,
+ planStart,
+ planEnd,
+ realStart: normalizeDate(stage.realStartTime),
+ realEnd: normalizeDate(stage.realEndTime),
+ dependencyLabels: Array.isArray(stage.dependencyNames) ? stage.dependencyNames.join('、') : stage.dependencyNames || '-',
+ remark: stage.scheduleRemark || '-',
+ originalPlanStart: originalStart,
+ originalPlanEnd: originalEnd,
+ hasChange
+ };
+ });
const loadPlanDetail = async () => {
if (!projectPlanId.value || projectPlanId.value === '0') {
@@ -390,13 +390,37 @@ const loadPlanDetail = async () => {
}
loading.value = true;
try {
- const res = await getErpProjectPlan(projectPlanId.value);
+ // 并行加载项目计划和变更记录
+ const [res, changeRes] = await Promise.all([
+ getErpProjectPlan(projectPlanId.value),
+ queryProjectChangeByProjectPlanId(projectPlanId.value).catch(() => ({ data: [] }))
+ ]);
+
planDetail.value = res.data;
+
+ // 处理变更记录,找到最后一次变更
+ const changeList = changeRes.data || [];
+ const changeMap = new Map();
+
+ if (changeList.length > 0) {
+ // 假设最后一条是最新变更(后端按ID或时间排序通常是这样,如果不确定可以先sort)
+ // 这里简单取列表最后一条
+ const lastChange = changeList[changeList.length - 1];
+ if (lastChange && lastChange.progressList) {
+ lastChange.progressList.forEach((p: any) => {
+ if (p.planStageId) {
+ changeMap.set(p.planStageId, p);
+ }
+ });
+ }
+ }
+
await nextTick();
- taskList.value = buildTaskListFromStages(res.data.planStageList || []);
+ taskList.value = buildTaskListFromStages(res.data.planStageList || [], changeMap);
updateChart();
} catch (error) {
- proxy?.$modal.msgError('获取项目计划失败');
+ proxy?.$modal.msgError('获取项目计划数据失败');
+ console.error(error);
} finally {
loading.value = false;
}