From 24fa103a01e5abb6da32378e7ee4c2f4587133a2 Mon Sep 17 00:00:00 2001 From: mairuiming Date: Fri, 13 Mar 2026 14:18:19 +0800 Subject: [PATCH] 5 5 --- src/App.tsx | 260 +++++++++++++++++++++++++--------------------------- 1 file changed, 127 insertions(+), 133 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 48418f7..ac9730c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -172,6 +172,7 @@ export default function App() { }, showUI: boolean } | null>(null); + const skipNextGroupConfigPopupRef = useRef(false); // 客户期望日期状态 const [expectedDate, setExpectedDate] = useState(null); @@ -203,8 +204,8 @@ export default function App() { const [completionDateAdjustment, setCompletionDateAdjustment] = useState(0); // 实际完成日期状态:记录每个节点的实际完成日期 const [actualCompletionDates, setActualCompletionDates] = useState<{[key: number]: Date | null}>({}); - // 基础缓冲期天数(可配置),用于计算动态缓冲期,默认14天 - const [baseBufferDays, setBaseBufferDays] = useState(14); + // 基础缓冲期天数(可配置),用于计算动态缓冲期,默认0天 + const [baseBufferDays, setBaseBufferDays] = useState(0); const [lockedExpectedDeliveryDateTs, setLockedExpectedDeliveryDateTs] = useState(null); const [isExpectedDeliveryDateLocked, setIsExpectedDeliveryDateLocked] = useState(false); // 快照回填来源(foreign_id、款式、颜色、文本2) @@ -276,7 +277,7 @@ export default function App() { setTimelineResults([]); setTimelineAdjustments({}); // 新增:重置固定缓冲期、实际完成日期以及一次性建议缓冲期应用标志 - setBaseBufferDays(14); + setBaseBufferDays(0); setActualCompletionDates({}); setHasAppliedSuggestedBuffer(false); setIsRestoringSnapshot(false); @@ -3115,8 +3116,15 @@ export default function App() { ) ); - if (allGroups.length > 1 && groupOrderConfig.length === 0 && showUI) { - const initial = deriveGroupOrderDraftByProcessOrder(matchedProcessNodes); + if (showUI && allGroups.length > 0) { + if (skipNextGroupConfigPopupRef.current) { + skipNextGroupConfigPopupRef.current = false; + } else { + const groupSet = new Set(allGroups); + const existing = Array.isArray(groupOrderConfig) + ? groupOrderConfig.filter(inst => groupSet.has((inst?.groupName || '').trim())) + : []; + const initial = existing.length > 0 ? existing : deriveGroupOrderDraftByProcessOrder(matchedProcessNodes); setGroupOrderDraft(initial); setGroupConfigVisible(true); setTimelineLoading(false); @@ -3132,6 +3140,7 @@ export default function App() { showUI, }; return []; + } } let orderedProcessNodes: any[] = matchedProcessNodes; @@ -3681,43 +3690,6 @@ export default function App() { pendingRecalculateAfterCalculateAdjustmentsRef.current = nextAdjustments; pendingRecalculateAfterCalculateRef.current = true; setTimelineResults(results); - if (!isBackward && mode === 'generate' && showUI && currentExpectedDate && results.length > 0) { - let lastCompletionDate: Date | null = null; - for (let i = results.length - 1; i >= 0; i--) { - const end = results[i]?.estimatedEnd; - if (!end || end === '时效值为0') continue; - const parsed = typeof end === 'string' ? parseDate(end) : (end as any as Date); - if (parsed && !isNaN(parsed.getTime())) { - lastCompletionDate = parsed; - break; - } - } - - if (lastCompletionDate) { - const bufferDays = Math.max(0, Math.ceil(baseBufferDays)); - const computedDeliveryDate = new Date(lastCompletionDate); - computedDeliveryDate.setDate(computedDeliveryDate.getDate() + bufferDays); - const slackDays = differenceInCalendarDays(currentExpectedDate, computedDeliveryDate); - if (slackDays > 0) { - const suggested = bufferDays + slackDays; - await new Promise((resolve) => { - Modal.confirm({ - title: '建议增加缓冲期', - content: `客户交期 ${formatDate(currentExpectedDate)}(${getDayOfWeek(currentExpectedDate)})晚于当前预计交付日期 ${formatDate(computedDeliveryDate)}(${getDayOfWeek(computedDeliveryDate)}),建议将缓冲期从 ${bufferDays} 天调整为 ${suggested} 天以对齐。`, - okText: '增加缓冲期', - cancelText: '不调整', - onOk: () => { - setBaseBufferDays(suggested); - setHasAppliedSuggestedBuffer(true); - setLastSuggestedApplied(suggested); - resolve(); - }, - onCancel: () => resolve() - }); - }); - } - } - } if (showUI && !delayShowTimelineModal) { setTimelineVisible(true); } else if (showUI && delayShowTimelineModal) { @@ -4105,24 +4077,57 @@ export default function App() { } }; + const getAdjustedLastCompletionDate = (results: any[], completionAdjustmentOverride?: number): Date | null => { + const lastCompletionDate = getLastValidCompletionDateFromResults(results); + if (!lastCompletionDate) return null; + const adjusted = new Date(lastCompletionDate); + const adj = Number.isFinite(completionAdjustmentOverride as number) + ? (completionAdjustmentOverride as number) + : completionDateAdjustment; + if (Number.isFinite(adj) && adj !== 0) { + adjusted.setDate(adjusted.getDate() + adj); + } + return adjusted; + }; + + const computeAutoBufferDaysUsingExpectedDate = ( + results: any[], + expectedDateOverride?: Date | null, + completionAdjustmentOverride?: number + ): number => { + try { + if (timelineDirection === 'backward') return 0; + const expected = expectedDateOverride ?? expectedDate; + if (!expected || isNaN(expected.getTime())) return 0; + const adjustedCompletionDate = getAdjustedLastCompletionDate(results, completionAdjustmentOverride); + if (!adjustedCompletionDate) return 0; + return differenceInCalendarDays(expected, adjustedCompletionDate); + } catch { + return 0; + } + }; + const computeBufferDeficitDaysUsingEndDelta = (adjustments: Record): number => { const deltaDays = computeLastNodeEndDeltaDays(adjustments); - const base = Math.max(0, Math.ceil(baseBufferDays)); - return Math.max(0, deltaDays - base); + const autoBufferDays = computeAutoBufferDaysUsingExpectedDate(timelineResults, expectedDate, completionDateAdjustment); + return Math.max(0, deltaDays - autoBufferDays); }; const computeExpectedDeliveryDateTsFromResults = ( results: any[], adjustments: Record, - baseBufferDaysOverride?: number + expectedDateOverride?: Date | null, + completionAdjustmentOverride?: number ): number | null => { if (timelineDirection === 'backward') return null; - const lastCompletionDate = getLastValidCompletionDateFromResults(results); - if (!lastCompletionDate) return null; - const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(adjustments, baseBufferDaysOverride); - const deliveryDate = new Date(lastCompletionDate); - deliveryDate.setDate(deliveryDate.getDate() + dynamicBufferDays); - const ts = deliveryDate.getTime(); + const adjustedCompletionDate = getAdjustedLastCompletionDate(results, completionAdjustmentOverride); + if (!adjustedCompletionDate) return null; + const expected = expectedDateOverride ?? expectedDate; + if (expected && !isNaN(expected.getTime())) { + const ts = expected.getTime(); + return Number.isFinite(ts) ? ts : null; + } + const ts = adjustedCompletionDate.getTime(); return Number.isFinite(ts) ? ts : null; }; @@ -4144,17 +4149,18 @@ export default function App() { setLockedExpectedDeliveryDateTs(expectedDate.getTime()); }; - const computeDynamicBufferDaysUsingEndDelta = (adjustments: Record, baseBufferDaysOverride?: number): number => { - try { - if (timelineDirection === 'backward') return 0; - const deltaDays = computeLastNodeEndDeltaDays(adjustments); - const base = Math.max(0, Math.ceil(baseBufferDaysOverride ?? baseBufferDays)); - const remaining = base - deltaDays; - return Math.max(0, Math.min(base, remaining)); - } catch { - const base = Math.max(0, Math.ceil(baseBufferDaysOverride ?? baseBufferDays)); - return base; - } + const computeDynamicBufferDaysUsingEndDelta = ( + adjustments: Record, + expectedDateOverride?: Date | null, + resultsOverride?: any[], + completionAdjustmentOverride?: number + ): number => { + const results = resultsOverride ?? timelineResults; + const expected = expectedDateOverride ?? expectedDate; + const completionAdj = Number.isFinite(completionAdjustmentOverride as number) + ? (completionAdjustmentOverride as number) + : completionDateAdjustment; + return computeAutoBufferDaysUsingExpectedDate(results, expected, completionAdj); }; // 重新计算时间线的函数 @@ -4441,7 +4447,7 @@ export default function App() { setDeliveryMarginDeductions(0); // 同时重置交期余量扣减 setCompletionDateAdjustment(0); // 重置最后流程完成日期调整 setActualCompletionDates({}); // 重置实际完成日期 - setBaseBufferDays(14); // 重置固定缓冲期为默认值 + setBaseBufferDays(0); // 重置固定缓冲期为默认值 setIsExpectedDeliveryDateLocked(false); try { lastBufferDeficitRef.current = 0; } catch {} setHasAppliedSuggestedBuffer(false); // 重置建议缓冲期应用标志 @@ -4470,7 +4476,7 @@ export default function App() { setSelectedLabels(s.selectedLabels || {}); const normalizedAdjustments = normalizeTimelineAdjustmentsFromSnapshot(s.timelineAdjustments || {}, s.timelineResults || []); setTimelineAdjustments(normalizedAdjustments); - setBaseBufferDays(s.baseBufferDays ?? 14); + setBaseBufferDays(s.baseBufferDays ?? 0); const shouldLock = s.isExpectedDeliveryDateLocked === true || (s.isExpectedDeliveryDateLocked === undefined && s.lockedExpectedDeliveryDateTs !== null && s.lockedExpectedDeliveryDateTs !== undefined); if (shouldLock && typeof s.lockedExpectedDeliveryDateTs === 'number' && Number.isFinite(s.lockedExpectedDeliveryDateTs)) { setLockedExpectedDeliveryDateTs(s.lockedExpectedDeliveryDateTs); @@ -4669,11 +4675,9 @@ export default function App() { // 获取标签汇总:批量模式优先使用传入的labels const selectedLabelValues = Object.values(overrides?.selectedLabels ?? selectedLabels).flat().filter(Boolean); - const baseBufferDaysToUse = Math.max(0, Math.ceil(overrides?.baseBufferDays ?? baseBufferDays)); - - // 获取预计交付日期(交期余量的日期版本:最后流程完成日期 + 基础缓冲期) - let expectedDeliveryDate = computeExpectedDeliveryDateTsFromResults(timelineResults, timelineAdjustments, baseBufferDaysToUse); - if (mode === 'adjust' && isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) { + // 获取预计交付日期(交期余量的日期版本:根据客户期望日期自动计算缓冲期) + let expectedDeliveryDate = computeExpectedDeliveryDateTsFromResults(timelineResults, timelineAdjustments, overrides?.expectedDate ?? expectedDate, completionDateAdjustment); + if (isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) { expectedDeliveryDate = lockedExpectedDeliveryDateTs; } @@ -4734,26 +4738,21 @@ export default function App() { const styleText = style || ''; const colorText = color || ''; - const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, baseBufferDaysToUse); + const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDateToUse, timelineResults, completionDateAdjustment); // 检查是否达到最终限制 let hasReachedFinalLimit = false; - const currentExpectedDate = expectedDate; - if (dynamicBufferDays === 0 && currentExpectedDate && timelineResults.length > 0) { - const lastNode = timelineResults[timelineResults.length - 1]; - if (lastNode && lastNode.estimatedEnd && !lastNode.estimatedEnd.includes('未找到')) { - try { - const lastCompletionDate = new Date(lastNode.estimatedEnd); - const daysToExpected = Math.ceil((currentExpectedDate.getTime() - lastCompletionDate.getTime()) / (1000 * 60 * 60 * 24)); - hasReachedFinalLimit = daysToExpected <= 0; - } catch (error) { - console.warn('计算最终限制状态失败:', error); - } + const currentExpectedDate = expectedDateToUse; + if (currentExpectedDate) { + const adjustedLastCompletion = getAdjustedLastCompletionDate(timelineResults, completionDateAdjustment); + if (adjustedLastCompletion) { + const daysToExpected = differenceInCalendarDays(currentExpectedDate, adjustedLastCompletion); + hasReachedFinalLimit = daysToExpected <= 0; } } // 为快照提供基础缓冲与节点调整总量(用于兼容历史字段),尽管动态缓冲期已改为“自然日差”口径 - const baseBuferDays = baseBufferDaysToUse; + const baseBuferDays = dynamicBufferDays; const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0); const globalSnapshot = { @@ -5566,16 +5565,6 @@ export default function App() { try { const results = await handleCalculateTimeline(true, { selectedLabels: labels, expectedDate: expectedDateObj || null, startTime: startDate || null, excludedDates: batchExcludedDates }, false); if (results && results.length > 0) { - const baseForSuggestion = 14; - const suggestedBaseBufferDays = (() => { - if (!expectedDateObj || isNaN(expectedDateObj.getTime())) return baseForSuggestion; - const lastCompletionDate = getLastValidCompletionDateFromResults(results); - if (!lastCompletionDate) return baseForSuggestion; - const computedDeliveryDate = new Date(lastCompletionDate); - computedDeliveryDate.setDate(computedDeliveryDate.getDate() + baseForSuggestion); - const slackDays = differenceInCalendarDays(expectedDateObj, computedDeliveryDate); - return slackDays > 0 ? (baseForSuggestion + slackDays) : baseForSuggestion; - })(); const processRecordIds = await writeToProcessDataTable(results, { foreignId, style: styleText, color: colorText }); const deliveryRecordId = await writeToDeliveryRecordTable( results, @@ -5587,8 +5576,7 @@ export default function App() { color: colorText, expectedDate: expectedDateObj || null, startTime: startDate || null, - selectedLabels: labels, - baseBufferDays: suggestedBaseBufferDays + selectedLabels: labels } ); try { @@ -6718,6 +6706,7 @@ export default function App() { onClick={() => { const nextConfig = Array.isArray(groupOrderDraft) ? groupOrderDraft : []; setGroupOrderConfig(nextConfig); + skipNextGroupConfigPopupRef.current = true; setGroupConfigVisible(false); if (!pendingGroupConfigCalcRef.current && mode === 'adjust') { const next = applyGroupOrderConfigToTimelineResults(timelineResults, nextConfig); @@ -6958,16 +6947,14 @@ export default function App() { > ✏️ - {mode === 'adjust' && ( - <> - 锁交付 - - - )} + <> + 锁交付 + + } @@ -6978,31 +6965,38 @@ export default function App() { setDeliveryMarginDeductions(0); // 关闭时重置交期余量扣减 setCompletionDateAdjustment(0); // 关闭时重置最后流程完成日期调整 setStyleColorEditable(false); // 关闭弹窗后恢复为锁定状态 - if (mode !== 'adjust') { - setLockedExpectedDeliveryDateTs(null); - setIsExpectedDeliveryDateLocked(false); - } }} footer={
- {mode === 'adjust' && isExpectedDeliveryDateLocked ? '基础缓冲期(天)(不影响已锁交付):' : '基础缓冲期(天):'} - { - const n = Number(val); - setBaseBufferDays(Number.isFinite(n) && n >= 0 ? Math.ceil(n) : 0); - }} - style={{ width: 90 }} - /> - 剩余缓冲期(天): - + {isExpectedDeliveryDateLocked ? '缓冲期(天)(不影响已锁交付):' : '缓冲期(天):'} + {(() => { + const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDate, timelineResults, completionDateAdjustment); + const canEdit = !isExpectedDeliveryDateLocked && !!getAdjustedLastCompletionDate(timelineResults, completionDateAdjustment); + return ( + { + if (!canEdit) return; + const n = Number(val); + if (!Number.isFinite(n)) return; + const adjustedCompletion = getAdjustedLastCompletionDate(timelineResults, completionDateAdjustment); + if (!adjustedCompletion) return; + const nextExpected = new Date(adjustedCompletion); + nextExpected.setDate(nextExpected.getDate() + n); + setExpectedDate(nextExpected); + }} + style={{ + width: 90, + backgroundColor: dynamicBufferDays < 0 ? '#fff1f0' : undefined, + borderColor: dynamicBufferDays < 0 ? '#ff4d4f' : undefined, + color: dynamicBufferDays < 0 ? '#cf1322' : undefined + }} + /> + ); + })()} @@ -7082,8 +7076,8 @@ export default function App() { // 当前节点是否为零值的周转周期节点 const isCurrentTurnoverZero = isTurnoverNode && adjustedValue === 0; - // 计算动态缓冲期(按“最后完成时间变动天数”扣减缓冲期) - const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments); + // 计算动态缓冲期 + const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDate, timelineResults, completionDateAdjustment); return ( @@ -7522,12 +7516,12 @@ export default function App() { } // 缓冲期(动态)与调整后的最后完成日期 - const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments); + const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDate, timelineResults, completionDateAdjustment); const adjustedCompletionDate = new Date(lastCompletionDate!); adjustedCompletionDate.setDate(adjustedCompletionDate.getDate() + completionDateAdjustment); const deliveryDate = new Date(adjustedCompletionDate); deliveryDate.setDate(deliveryDate.getDate() + dynamicBufferDays); - const lockedDeliveryDate = (mode === 'adjust' && isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) + const lockedDeliveryDate = (isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) ? new Date(lockedExpectedDeliveryDateTs) : null; @@ -7558,9 +7552,9 @@ export default function App() { 缓冲期 { if (date instanceof Date) { setExpectedDate(date);