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触发,避免使用旧状态
}}
/>