From 7e76eb1fa77c75b5667d4d6c553ab3ef46a83ffa Mon Sep 17 00:00:00 2001 From: mairuiming Date: Fri, 14 Nov 2025 11:07:35 +0800 Subject: [PATCH] 4 4 --- src/App.tsx | 125 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 9 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 37d6b6d..339ca39 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -128,6 +128,10 @@ export default function App() { setTimelineVisible(false); setTimelineResults([]); setTimelineAdjustments({}); + // 新增:重置固定缓冲期、实际完成日期以及一次性建议缓冲期应用标志 + setBaseBufferDays(14); + setActualCompletionDates({}); + setHasAppliedSuggestedBuffer(false); setIsRestoringSnapshot(false); setRestoredRecordIds([]); setRestoredRecordIdsText(''); @@ -2744,6 +2748,8 @@ export default function App() { // 添加快照还原状态标志 const [isRestoringSnapshot, setIsRestoringSnapshot] = useState(false); + const [hasAppliedSuggestedBuffer, setHasAppliedSuggestedBuffer] = useState(false); + // 当起始时间变更时,重新以最新起始时间为基准重算全流程 useEffect(() => { if (timelineResults.length > 0 && !isRestoringSnapshot) { @@ -2751,12 +2757,21 @@ export default function App() { } }, [startTime, isRestoringSnapshot]); + // 当实际完成日期变化时,以最新状态进行重算,避免首次选择不触发或使用旧值 + useEffect(() => { + if (timelineResults.length > 0 && !isRestoringSnapshot) { + recalculateTimeline(timelineAdjustments, false); + } + }, [actualCompletionDates, isRestoringSnapshot]); + // 重置调整的函数 const resetTimelineAdjustments = () => { setTimelineAdjustments({}); setDeliveryMarginDeductions(0); // 同时重置交期余量扣减 setCompletionDateAdjustment(0); // 重置最后流程完成日期调整 setActualCompletionDates({}); // 重置实际完成日期 + setBaseBufferDays(14); // 重置固定缓冲期为默认值 + setHasAppliedSuggestedBuffer(false); // 重置建议缓冲期应用标志 recalculateTimeline({}, true); // 强制重算所有节点 }; @@ -5434,6 +5449,7 @@ export default function App() { setTimelineAdjustments({}); // 关闭时重置调整 setDeliveryMarginDeductions(0); // 关闭时重置交期余量扣减 setCompletionDateAdjustment(0); // 关闭时重置最后流程完成日期调整 + setHasAppliedSuggestedBuffer(false); // 关闭时允许下次重新应用建议缓冲期 }} footer={
@@ -5445,10 +5461,107 @@ export default function App() { value={baseBufferDays} onChange={(val) => { const n = Number(val); - setBaseBufferDays(Number.isFinite(n) && n >= 0 ? n : 0); + setBaseBufferDays(Number.isFinite(n) && n >= 0 ? Math.ceil(n) : 0); }} style={{ width: 90 }} /> + {/* 建议缓冲期增量:根据实际完成偏差与交期余量自动反算 */} + {(() => { + try { + // 计算节点总调整量与动态缓冲期 + const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0); + const baseBuferDays = baseBufferDays; + const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments); + + // 获取有效的最后流程完成日期 + let effectiveLastProcess: any = null; + let lastCompletionDate: Date | null = null; + for (let i = timelineResults.length - 1; i >= 0; i--) { + const process = timelineResults[i]; + const processDate = new Date(process.estimatedEnd); + if (!isNaN(processDate.getTime()) && process.estimatedEnd !== '时效值为0') { + effectiveLastProcess = process; + lastCompletionDate = processDate; + break; + } + } + if (!effectiveLastProcess && timelineResults.length > 0) { + effectiveLastProcess = timelineResults[timelineResults.length - 1]; + lastCompletionDate = new Date(effectiveLastProcess.estimatedEnd); + } + + // 基线计划(不考虑实际完成、也不考虑调整):使用原始时效值重算 + const baseline = getRecalculatedTimeline({}); + let baselineLast: Date | null = null; + for (let i = baseline.length - 1; i >= 0; i--) { + const p = baseline[i]; + const d = new Date(p.estimatedEnd); + if (!isNaN(d.getTime()) && p.estimatedEnd !== '时效值为0') { baselineLast = d; break; } + } + if (!baselineLast && baseline.length > 0) { + const d = new Date(baseline[baseline.length - 1].estimatedEnd); + if (!isNaN(d.getTime())) baselineLast = d; + } + + // 当前计划(考虑实际完成):使用现有timelineResults + let currentLast: Date | null = null; + for (let i = timelineResults.length - 1; i >= 0; i--) { + const p = timelineResults[i]; + const d = new Date(p.estimatedEnd); + if (!isNaN(d.getTime()) && p.estimatedEnd !== '时效值为0') { currentLast = d; break; } + } + if (!currentLast && timelineResults.length > 0) { + const d = new Date(timelineResults[timelineResults.length - 1].estimatedEnd); + if (!isNaN(d.getTime())) currentLast = d; + } + + // 建议口径1:自然日偏差(当前计划 vs 原始计划)扣除默认缓冲期 + let suggestedInt = 0; + let slipDays = 0; + if (baselineLast && currentLast) { + const dayMs = 1000 * 60 * 60 * 24; + slipDays = Math.ceil((currentLast.getTime() - baselineLast.getTime()) / dayMs); + const deficitCalendar = Math.max(0, slipDays - baseBuferDays); + suggestedInt = Math.ceil(deficitCalendar); + } + + // 建议口径2:工作日总调整量(仅统计 external 节点的天数调整)扣除默认缓冲期 + const totalWorkingAdjustments = Object.entries(timelineAdjustments).reduce((sum, [k, adj]) => { + const i = parseInt(k, 10); + const method = timelineResults[i]?.calculationMethod || 'external'; + const val = Number(adj) || 0; + return method === 'internal' ? sum : sum + val; + }, 0); + const deficitWorking = Math.max(0, totalWorkingAdjustments - baseBuferDays); + const suggestedByWorking = Math.ceil(deficitWorking); + + // 取两种口径的较大值,避免出现工作日调整很大但自然日差较小的误差 + suggestedInt = Math.max(suggestedInt, suggestedByWorking); + const displayInt = Math.max(0, suggestedInt); + if (displayInt > 0) { + return ( +
+ 建议增加缓冲期:+{displayInt}天 + +
+ ); + } + return null; + } catch { + return null; + } + })()} @@ -5770,11 +5883,8 @@ export default function App() { const deltaToApply = desiredAdjustmentAbs - currentAdj; if (deltaToApply !== 0) { const updated = handleTimelineAdjustment(index, deltaToApply); + // 若该节点不允许调整,交由实际完成日期的useEffect联动重算 if (!updated) { - // 如果该节点不允许调整,仍然重算以联动后续节点 - setTimeout(() => { - recalculateTimeline(timelineAdjustments, false); - }, 0); return; } } @@ -5784,10 +5894,7 @@ export default function App() { console.warn('自动调整量计算失败:', e); } - // 无论如何都重新计算时间线以联动交期余量 - setTimeout(() => { - recalculateTimeline(timelineAdjustments, false); - }, 0); + // 重算由依赖actualCompletionDates的useEffect触发,避免使用旧状态 }} />