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

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

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