|
|
|
|
@ -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
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|