feat(erp/erpProjectPlan): 优化甘特图任务时间展示

- 添加计划最晚结束时间字段展示任务最晚结束日期
- 任务日期轴将同一天的任务用散点图表示,避免零宽度矩形不可见
- 计划进度和实际进度均拆分为条形和散点两种数据分别渲染
- 重新组织任务渲染数据,确保时间轴显示更准确更清晰
- 保持任务实际进度和计划进度在图表上的一致性和区分度
dev
zangch@mesnac.com 4 weeks ago
parent 3f6885f096
commit 176ae71f8d

@ -30,8 +30,8 @@
<el-descriptions-item label="任务数量"> <el-descriptions-item label="任务数量">
{{ taskList.length }} {{ taskList.length }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="最后同步时间"> <el-descriptions-item label="计划最晚结束时间">
{{ formatDisplayDate(new Date()) }} {{ lastPlanEndDate }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
@ -129,6 +129,25 @@ const planStatusLabel = computed(() => {
return statusMap.value[planDetail.value.projectPlanStatus] || planDetail.value.projectPlanStatus; return statusMap.value[planDetail.value.projectPlanStatus] || planDetail.value.projectPlanStatus;
}); });
const lastPlanEndDate = computed(() => {
if (!taskList.value.length) {
return '-';
}
let maxTs: number | null = null;
taskList.value.forEach(task => {
const ts = toTimestamp(task.planEnd);
if (ts !== null) {
if (maxTs === null || ts > maxTs) {
maxTs = ts;
}
}
});
if (maxTs === null) {
return '-';
}
return formatDisplayDate(new Date(maxTs));
});
const projectPlanId = computed(() => route.params.projectPlanId as string); const projectPlanId = computed(() => route.params.projectPlanId as string);
const renderGanttItem = (params: any, api: any) => { const renderGanttItem = (params: any, api: any) => {
@ -180,41 +199,65 @@ const updateChart = () => {
let min = Number.POSITIVE_INFINITY; let min = Number.POSITIVE_INFINITY;
let max = Number.NEGATIVE_INFINITY; let max = Number.NEGATIVE_INFINITY;
const planData = taskList.value const planBarData: any[] = [];
.map((task, index) => { const planPointData: any[] = [];
const start = toTimestamp(task.planStart);
const end = toTimestamp(task.planEnd); taskList.value.forEach((task, index) => {
if (start === null || end === null || end < start) { const start = toTimestamp(task.planStart);
return null; const end = toTimestamp(task.planEnd);
} if (start === null || end === null) {
return;
}
// 使
if (end <= start) {
min = Math.min(min, start);
max = Math.max(max, start);
planPointData.push({
name: task.taskName,
value: [start, index],
task
});
} else {
min = Math.min(min, start); min = Math.min(min, start);
max = Math.max(max, end); max = Math.max(max, end);
return { planBarData.push({
name: task.taskName, name: task.taskName,
value: [index, start, end], value: [index, start, end],
task task
}; });
}) }
.filter(Boolean) as Array<{ name: string; value: [number, number, number]; task: GanttTask }>; });
const actualData = showActual.value const actualBarData: any[] = [];
? (taskList.value const actualPointData: any[] = [];
.map((task, index) => {
const start = toTimestamp(task.realStart); if (showActual.value) {
const end = toTimestamp(task.realEnd); taskList.value.forEach((task, index) => {
if (start === null || end === null || end < start) { const start = toTimestamp(task.realStart);
return null; const end = toTimestamp(task.realEnd);
} if (start === null || end === null) {
min = Math.min(min, start); return;
max = Math.max(max, end); }
return { // 使
name: `${task.taskName}-实际`, if (end <= start) {
value: [index, start, end], min = Math.min(min, start);
task max = Math.max(max, start);
}; actualPointData.push({
}) name: `${task.taskName}-实际`,
.filter(Boolean) as Array<{ name: string; value: [number, number, number]; task: GanttTask }>) value: [start, index],
: []; task
});
} else {
min = Math.min(min, start);
max = Math.max(max, end);
actualBarData.push({
name: `${task.taskName}-实际`,
value: [index, start, end],
task
});
}
});
}
if (min === Number.POSITIVE_INFINITY || max === Number.NEGATIVE_INFINITY) { if (min === Number.POSITIVE_INFINITY || max === Number.NEGATIVE_INFINITY) {
const now = Date.now(); const now = Date.now();
@ -223,24 +266,42 @@ const updateChart = () => {
} }
const series: any[] = []; const series: any[] = [];
if (planData.length) { if (planBarData.length) {
series.push({ series.push({
name: '计划进度', name: '计划进度',
type: 'custom', type: 'custom',
renderItem: renderGanttItem, renderItem: renderGanttItem,
itemStyle: { color: '#91cc75', opacity: 0.9 }, itemStyle: { color: '#91cc75', opacity: 0.9 },
encode: { x: [1, 2], y: 0 }, encode: { x: [1, 2], y: 0 },
data: planData data: planBarData
}); });
} }
if (actualData.length) { if (planPointData.length) {
series.push({
name: '计划进度(单日)',
type: 'scatter',
symbolSize: 10,
itemStyle: { color: '#91cc75', opacity: 0.9 },
data: planPointData
});
}
if (actualBarData.length) {
series.push({ series.push({
name: '实际进度', name: '实际进度',
type: 'custom', type: 'custom',
renderItem: renderGanttItem, renderItem: renderGanttItem,
itemStyle: { color: '#5470c6', opacity: 0.75 }, itemStyle: { color: '#5470c6', opacity: 0.75 },
encode: { x: [1, 2], y: 0 }, encode: { x: [1, 2], y: 0 },
data: actualData data: actualBarData
});
}
if (actualPointData.length) {
series.push({
name: '实际进度(单日)',
type: 'scatter',
symbolSize: 10,
itemStyle: { color: '#5470c6', opacity: 0.75 },
data: actualPointData
}); });
} }

Loading…
Cancel
Save