5
5
This commit is contained in:
230
src/App.tsx
230
src/App.tsx
@ -172,6 +172,7 @@ export default function App() {
|
|||||||
},
|
},
|
||||||
showUI: boolean
|
showUI: boolean
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const skipNextGroupConfigPopupRef = useRef(false);
|
||||||
|
|
||||||
// 客户期望日期状态
|
// 客户期望日期状态
|
||||||
const [expectedDate, setExpectedDate] = useState<Date | null>(null);
|
const [expectedDate, setExpectedDate] = useState<Date | null>(null);
|
||||||
@ -203,8 +204,8 @@ export default function App() {
|
|||||||
const [completionDateAdjustment, setCompletionDateAdjustment] = useState<number>(0);
|
const [completionDateAdjustment, setCompletionDateAdjustment] = useState<number>(0);
|
||||||
// 实际完成日期状态:记录每个节点的实际完成日期
|
// 实际完成日期状态:记录每个节点的实际完成日期
|
||||||
const [actualCompletionDates, setActualCompletionDates] = useState<{[key: number]: Date | null}>({});
|
const [actualCompletionDates, setActualCompletionDates] = useState<{[key: number]: Date | null}>({});
|
||||||
// 基础缓冲期天数(可配置),用于计算动态缓冲期,默认14天
|
// 基础缓冲期天数(可配置),用于计算动态缓冲期,默认0天
|
||||||
const [baseBufferDays, setBaseBufferDays] = useState<number>(14);
|
const [baseBufferDays, setBaseBufferDays] = useState<number>(0);
|
||||||
const [lockedExpectedDeliveryDateTs, setLockedExpectedDeliveryDateTs] = useState<number | null>(null);
|
const [lockedExpectedDeliveryDateTs, setLockedExpectedDeliveryDateTs] = useState<number | null>(null);
|
||||||
const [isExpectedDeliveryDateLocked, setIsExpectedDeliveryDateLocked] = useState<boolean>(false);
|
const [isExpectedDeliveryDateLocked, setIsExpectedDeliveryDateLocked] = useState<boolean>(false);
|
||||||
// 快照回填来源(foreign_id、款式、颜色、文本2)
|
// 快照回填来源(foreign_id、款式、颜色、文本2)
|
||||||
@ -276,7 +277,7 @@ export default function App() {
|
|||||||
setTimelineResults([]);
|
setTimelineResults([]);
|
||||||
setTimelineAdjustments({});
|
setTimelineAdjustments({});
|
||||||
// 新增:重置固定缓冲期、实际完成日期以及一次性建议缓冲期应用标志
|
// 新增:重置固定缓冲期、实际完成日期以及一次性建议缓冲期应用标志
|
||||||
setBaseBufferDays(14);
|
setBaseBufferDays(0);
|
||||||
setActualCompletionDates({});
|
setActualCompletionDates({});
|
||||||
setHasAppliedSuggestedBuffer(false);
|
setHasAppliedSuggestedBuffer(false);
|
||||||
setIsRestoringSnapshot(false);
|
setIsRestoringSnapshot(false);
|
||||||
@ -3115,8 +3116,15 @@ export default function App() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (allGroups.length > 1 && groupOrderConfig.length === 0 && showUI) {
|
if (showUI && allGroups.length > 0) {
|
||||||
const initial = deriveGroupOrderDraftByProcessOrder(matchedProcessNodes);
|
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);
|
setGroupOrderDraft(initial);
|
||||||
setGroupConfigVisible(true);
|
setGroupConfigVisible(true);
|
||||||
setTimelineLoading(false);
|
setTimelineLoading(false);
|
||||||
@ -3133,6 +3141,7 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let orderedProcessNodes: any[] = matchedProcessNodes;
|
let orderedProcessNodes: any[] = matchedProcessNodes;
|
||||||
|
|
||||||
@ -3681,43 +3690,6 @@ export default function App() {
|
|||||||
pendingRecalculateAfterCalculateAdjustmentsRef.current = nextAdjustments;
|
pendingRecalculateAfterCalculateAdjustmentsRef.current = nextAdjustments;
|
||||||
pendingRecalculateAfterCalculateRef.current = true;
|
pendingRecalculateAfterCalculateRef.current = true;
|
||||||
setTimelineResults(results);
|
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<void>((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) {
|
if (showUI && !delayShowTimelineModal) {
|
||||||
setTimelineVisible(true);
|
setTimelineVisible(true);
|
||||||
} else if (showUI && delayShowTimelineModal) {
|
} 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<string, number>): number => {
|
const computeBufferDeficitDaysUsingEndDelta = (adjustments: Record<string, number>): number => {
|
||||||
const deltaDays = computeLastNodeEndDeltaDays(adjustments);
|
const deltaDays = computeLastNodeEndDeltaDays(adjustments);
|
||||||
const base = Math.max(0, Math.ceil(baseBufferDays));
|
const autoBufferDays = computeAutoBufferDaysUsingExpectedDate(timelineResults, expectedDate, completionDateAdjustment);
|
||||||
return Math.max(0, deltaDays - base);
|
return Math.max(0, deltaDays - autoBufferDays);
|
||||||
};
|
};
|
||||||
|
|
||||||
const computeExpectedDeliveryDateTsFromResults = (
|
const computeExpectedDeliveryDateTsFromResults = (
|
||||||
results: any[],
|
results: any[],
|
||||||
adjustments: Record<string, number>,
|
adjustments: Record<string, number>,
|
||||||
baseBufferDaysOverride?: number
|
expectedDateOverride?: Date | null,
|
||||||
|
completionAdjustmentOverride?: number
|
||||||
): number | null => {
|
): number | null => {
|
||||||
if (timelineDirection === 'backward') return null;
|
if (timelineDirection === 'backward') return null;
|
||||||
const lastCompletionDate = getLastValidCompletionDateFromResults(results);
|
const adjustedCompletionDate = getAdjustedLastCompletionDate(results, completionAdjustmentOverride);
|
||||||
if (!lastCompletionDate) return null;
|
if (!adjustedCompletionDate) return null;
|
||||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(adjustments, baseBufferDaysOverride);
|
const expected = expectedDateOverride ?? expectedDate;
|
||||||
const deliveryDate = new Date(lastCompletionDate);
|
if (expected && !isNaN(expected.getTime())) {
|
||||||
deliveryDate.setDate(deliveryDate.getDate() + dynamicBufferDays);
|
const ts = expected.getTime();
|
||||||
const ts = deliveryDate.getTime();
|
return Number.isFinite(ts) ? ts : null;
|
||||||
|
}
|
||||||
|
const ts = adjustedCompletionDate.getTime();
|
||||||
return Number.isFinite(ts) ? ts : null;
|
return Number.isFinite(ts) ? ts : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4144,17 +4149,18 @@ export default function App() {
|
|||||||
setLockedExpectedDeliveryDateTs(expectedDate.getTime());
|
setLockedExpectedDeliveryDateTs(expectedDate.getTime());
|
||||||
};
|
};
|
||||||
|
|
||||||
const computeDynamicBufferDaysUsingEndDelta = (adjustments: Record<string, number>, baseBufferDaysOverride?: number): number => {
|
const computeDynamicBufferDaysUsingEndDelta = (
|
||||||
try {
|
adjustments: Record<string, number>,
|
||||||
if (timelineDirection === 'backward') return 0;
|
expectedDateOverride?: Date | null,
|
||||||
const deltaDays = computeLastNodeEndDeltaDays(adjustments);
|
resultsOverride?: any[],
|
||||||
const base = Math.max(0, Math.ceil(baseBufferDaysOverride ?? baseBufferDays));
|
completionAdjustmentOverride?: number
|
||||||
const remaining = base - deltaDays;
|
): number => {
|
||||||
return Math.max(0, Math.min(base, remaining));
|
const results = resultsOverride ?? timelineResults;
|
||||||
} catch {
|
const expected = expectedDateOverride ?? expectedDate;
|
||||||
const base = Math.max(0, Math.ceil(baseBufferDaysOverride ?? baseBufferDays));
|
const completionAdj = Number.isFinite(completionAdjustmentOverride as number)
|
||||||
return base;
|
? (completionAdjustmentOverride as number)
|
||||||
}
|
: completionDateAdjustment;
|
||||||
|
return computeAutoBufferDaysUsingExpectedDate(results, expected, completionAdj);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 重新计算时间线的函数
|
// 重新计算时间线的函数
|
||||||
@ -4441,7 +4447,7 @@ export default function App() {
|
|||||||
setDeliveryMarginDeductions(0); // 同时重置交期余量扣减
|
setDeliveryMarginDeductions(0); // 同时重置交期余量扣减
|
||||||
setCompletionDateAdjustment(0); // 重置最后流程完成日期调整
|
setCompletionDateAdjustment(0); // 重置最后流程完成日期调整
|
||||||
setActualCompletionDates({}); // 重置实际完成日期
|
setActualCompletionDates({}); // 重置实际完成日期
|
||||||
setBaseBufferDays(14); // 重置固定缓冲期为默认值
|
setBaseBufferDays(0); // 重置固定缓冲期为默认值
|
||||||
setIsExpectedDeliveryDateLocked(false);
|
setIsExpectedDeliveryDateLocked(false);
|
||||||
try { lastBufferDeficitRef.current = 0; } catch {}
|
try { lastBufferDeficitRef.current = 0; } catch {}
|
||||||
setHasAppliedSuggestedBuffer(false); // 重置建议缓冲期应用标志
|
setHasAppliedSuggestedBuffer(false); // 重置建议缓冲期应用标志
|
||||||
@ -4470,7 +4476,7 @@ export default function App() {
|
|||||||
setSelectedLabels(s.selectedLabels || {});
|
setSelectedLabels(s.selectedLabels || {});
|
||||||
const normalizedAdjustments = normalizeTimelineAdjustmentsFromSnapshot(s.timelineAdjustments || {}, s.timelineResults || []);
|
const normalizedAdjustments = normalizeTimelineAdjustmentsFromSnapshot(s.timelineAdjustments || {}, s.timelineResults || []);
|
||||||
setTimelineAdjustments(normalizedAdjustments);
|
setTimelineAdjustments(normalizedAdjustments);
|
||||||
setBaseBufferDays(s.baseBufferDays ?? 14);
|
setBaseBufferDays(s.baseBufferDays ?? 0);
|
||||||
const shouldLock = s.isExpectedDeliveryDateLocked === true || (s.isExpectedDeliveryDateLocked === undefined && s.lockedExpectedDeliveryDateTs !== null && s.lockedExpectedDeliveryDateTs !== undefined);
|
const shouldLock = s.isExpectedDeliveryDateLocked === true || (s.isExpectedDeliveryDateLocked === undefined && s.lockedExpectedDeliveryDateTs !== null && s.lockedExpectedDeliveryDateTs !== undefined);
|
||||||
if (shouldLock && typeof s.lockedExpectedDeliveryDateTs === 'number' && Number.isFinite(s.lockedExpectedDeliveryDateTs)) {
|
if (shouldLock && typeof s.lockedExpectedDeliveryDateTs === 'number' && Number.isFinite(s.lockedExpectedDeliveryDateTs)) {
|
||||||
setLockedExpectedDeliveryDateTs(s.lockedExpectedDeliveryDateTs);
|
setLockedExpectedDeliveryDateTs(s.lockedExpectedDeliveryDateTs);
|
||||||
@ -4669,11 +4675,9 @@ export default function App() {
|
|||||||
// 获取标签汇总:批量模式优先使用传入的labels
|
// 获取标签汇总:批量模式优先使用传入的labels
|
||||||
const selectedLabelValues = Object.values(overrides?.selectedLabels ?? selectedLabels).flat().filter(Boolean);
|
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, overrides?.expectedDate ?? expectedDate, completionDateAdjustment);
|
||||||
// 获取预计交付日期(交期余量的日期版本:最后流程完成日期 + 基础缓冲期)
|
if (isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) {
|
||||||
let expectedDeliveryDate = computeExpectedDeliveryDateTsFromResults(timelineResults, timelineAdjustments, baseBufferDaysToUse);
|
|
||||||
if (mode === 'adjust' && isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) {
|
|
||||||
expectedDeliveryDate = lockedExpectedDeliveryDateTs;
|
expectedDeliveryDate = lockedExpectedDeliveryDateTs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4734,26 +4738,21 @@ export default function App() {
|
|||||||
const styleText = style || '';
|
const styleText = style || '';
|
||||||
const colorText = color || '';
|
const colorText = color || '';
|
||||||
|
|
||||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, baseBufferDaysToUse);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDateToUse, timelineResults, completionDateAdjustment);
|
||||||
|
|
||||||
// 检查是否达到最终限制
|
// 检查是否达到最终限制
|
||||||
let hasReachedFinalLimit = false;
|
let hasReachedFinalLimit = false;
|
||||||
const currentExpectedDate = expectedDate;
|
const currentExpectedDate = expectedDateToUse;
|
||||||
if (dynamicBufferDays === 0 && currentExpectedDate && timelineResults.length > 0) {
|
if (currentExpectedDate) {
|
||||||
const lastNode = timelineResults[timelineResults.length - 1];
|
const adjustedLastCompletion = getAdjustedLastCompletionDate(timelineResults, completionDateAdjustment);
|
||||||
if (lastNode && lastNode.estimatedEnd && !lastNode.estimatedEnd.includes('未找到')) {
|
if (adjustedLastCompletion) {
|
||||||
try {
|
const daysToExpected = differenceInCalendarDays(currentExpectedDate, adjustedLastCompletion);
|
||||||
const lastCompletionDate = new Date(lastNode.estimatedEnd);
|
|
||||||
const daysToExpected = Math.ceil((currentExpectedDate.getTime() - lastCompletionDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
||||||
hasReachedFinalLimit = daysToExpected <= 0;
|
hasReachedFinalLimit = daysToExpected <= 0;
|
||||||
} catch (error) {
|
|
||||||
console.warn('计算最终限制状态失败:', error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为快照提供基础缓冲与节点调整总量(用于兼容历史字段),尽管动态缓冲期已改为“自然日差”口径
|
// 为快照提供基础缓冲与节点调整总量(用于兼容历史字段),尽管动态缓冲期已改为“自然日差”口径
|
||||||
const baseBuferDays = baseBufferDaysToUse;
|
const baseBuferDays = dynamicBufferDays;
|
||||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||||
|
|
||||||
const globalSnapshot = {
|
const globalSnapshot = {
|
||||||
@ -5566,16 +5565,6 @@ export default function App() {
|
|||||||
try {
|
try {
|
||||||
const results = await handleCalculateTimeline(true, { selectedLabels: labels, expectedDate: expectedDateObj || null, startTime: startDate || null, excludedDates: batchExcludedDates }, false);
|
const results = await handleCalculateTimeline(true, { selectedLabels: labels, expectedDate: expectedDateObj || null, startTime: startDate || null, excludedDates: batchExcludedDates }, false);
|
||||||
if (results && results.length > 0) {
|
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 processRecordIds = await writeToProcessDataTable(results, { foreignId, style: styleText, color: colorText });
|
||||||
const deliveryRecordId = await writeToDeliveryRecordTable(
|
const deliveryRecordId = await writeToDeliveryRecordTable(
|
||||||
results,
|
results,
|
||||||
@ -5587,8 +5576,7 @@ export default function App() {
|
|||||||
color: colorText,
|
color: colorText,
|
||||||
expectedDate: expectedDateObj || null,
|
expectedDate: expectedDateObj || null,
|
||||||
startTime: startDate || null,
|
startTime: startDate || null,
|
||||||
selectedLabels: labels,
|
selectedLabels: labels
|
||||||
baseBufferDays: suggestedBaseBufferDays
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
@ -6718,6 +6706,7 @@ export default function App() {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const nextConfig = Array.isArray(groupOrderDraft) ? groupOrderDraft : [];
|
const nextConfig = Array.isArray(groupOrderDraft) ? groupOrderDraft : [];
|
||||||
setGroupOrderConfig(nextConfig);
|
setGroupOrderConfig(nextConfig);
|
||||||
|
skipNextGroupConfigPopupRef.current = true;
|
||||||
setGroupConfigVisible(false);
|
setGroupConfigVisible(false);
|
||||||
if (!pendingGroupConfigCalcRef.current && mode === 'adjust') {
|
if (!pendingGroupConfigCalcRef.current && mode === 'adjust') {
|
||||||
const next = applyGroupOrderConfigToTimelineResults(timelineResults, nextConfig);
|
const next = applyGroupOrderConfigToTimelineResults(timelineResults, nextConfig);
|
||||||
@ -6958,7 +6947,6 @@ export default function App() {
|
|||||||
>
|
>
|
||||||
<span style={{ fontSize: 14, lineHeight: 1 }}>✏️</span>
|
<span style={{ fontSize: 14, lineHeight: 1 }}>✏️</span>
|
||||||
</Button>
|
</Button>
|
||||||
{mode === 'adjust' && (
|
|
||||||
<>
|
<>
|
||||||
<Text type="tertiary" style={{ marginLeft: 8 }}>锁交付</Text>
|
<Text type="tertiary" style={{ marginLeft: 8 }}>锁交付</Text>
|
||||||
<Switch
|
<Switch
|
||||||
@ -6967,7 +6955,6 @@ export default function App() {
|
|||||||
style={{ marginLeft: 4 }}
|
style={{ marginLeft: 4 }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@ -6978,31 +6965,38 @@ export default function App() {
|
|||||||
setDeliveryMarginDeductions(0); // 关闭时重置交期余量扣减
|
setDeliveryMarginDeductions(0); // 关闭时重置交期余量扣减
|
||||||
setCompletionDateAdjustment(0); // 关闭时重置最后流程完成日期调整
|
setCompletionDateAdjustment(0); // 关闭时重置最后流程完成日期调整
|
||||||
setStyleColorEditable(false); // 关闭弹窗后恢复为锁定状态
|
setStyleColorEditable(false); // 关闭弹窗后恢复为锁定状态
|
||||||
if (mode !== 'adjust') {
|
|
||||||
setLockedExpectedDeliveryDateTs(null);
|
|
||||||
setIsExpectedDeliveryDateLocked(false);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
footer={
|
footer={
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<Space align="center">
|
<Space align="center">
|
||||||
<Text>{mode === 'adjust' && isExpectedDeliveryDateLocked ? '基础缓冲期(天)(不影响已锁交付):' : '基础缓冲期(天):'}</Text>
|
<Text>{isExpectedDeliveryDateLocked ? '缓冲期(天)(不影响已锁交付):' : '缓冲期(天):'}</Text>
|
||||||
|
{(() => {
|
||||||
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDate, timelineResults, completionDateAdjustment);
|
||||||
|
const canEdit = !isExpectedDeliveryDateLocked && !!getAdjustedLastCompletionDate(timelineResults, completionDateAdjustment);
|
||||||
|
return (
|
||||||
<InputNumber
|
<InputNumber
|
||||||
min={0}
|
value={dynamicBufferDays}
|
||||||
|
disabled={!canEdit}
|
||||||
step={1}
|
step={1}
|
||||||
value={baseBufferDays}
|
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
|
if (!canEdit) return;
|
||||||
const n = Number(val);
|
const n = Number(val);
|
||||||
setBaseBufferDays(Number.isFinite(n) && n >= 0 ? Math.ceil(n) : 0);
|
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
|
||||||
}}
|
}}
|
||||||
style={{ width: 90 }}
|
|
||||||
/>
|
|
||||||
<Text>剩余缓冲期(天):</Text>
|
|
||||||
<InputNumber
|
|
||||||
value={computeDynamicBufferDaysUsingEndDelta(timelineAdjustments)}
|
|
||||||
disabled
|
|
||||||
style={{ width: 90 }}
|
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
<Button onClick={resetToInitialState}>
|
<Button onClick={resetToInitialState}>
|
||||||
重置所有调整
|
重置所有调整
|
||||||
</Button>
|
</Button>
|
||||||
@ -7082,8 +7076,8 @@ export default function App() {
|
|||||||
// 当前节点是否为零值的周转周期节点
|
// 当前节点是否为零值的周转周期节点
|
||||||
const isCurrentTurnoverZero = isTurnoverNode && adjustedValue === 0;
|
const isCurrentTurnoverZero = isTurnoverNode && adjustedValue === 0;
|
||||||
|
|
||||||
// 计算动态缓冲期(按“最后完成时间变动天数”扣减缓冲期)
|
// 计算动态缓冲期
|
||||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDate, timelineResults, completionDateAdjustment);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card key={index} style={{ marginBottom: '8px', padding: '12px', position: 'relative' }}>
|
<Card key={index} style={{ marginBottom: '8px', padding: '12px', position: 'relative' }}>
|
||||||
@ -7522,12 +7516,12 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 缓冲期(动态)与调整后的最后完成日期
|
// 缓冲期(动态)与调整后的最后完成日期
|
||||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, expectedDate, timelineResults, completionDateAdjustment);
|
||||||
const adjustedCompletionDate = new Date(lastCompletionDate!);
|
const adjustedCompletionDate = new Date(lastCompletionDate!);
|
||||||
adjustedCompletionDate.setDate(adjustedCompletionDate.getDate() + completionDateAdjustment);
|
adjustedCompletionDate.setDate(adjustedCompletionDate.getDate() + completionDateAdjustment);
|
||||||
const deliveryDate = new Date(adjustedCompletionDate);
|
const deliveryDate = new Date(adjustedCompletionDate);
|
||||||
deliveryDate.setDate(deliveryDate.getDate() + dynamicBufferDays);
|
deliveryDate.setDate(deliveryDate.getDate() + dynamicBufferDays);
|
||||||
const lockedDeliveryDate = (mode === 'adjust' && isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null)
|
const lockedDeliveryDate = (isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null)
|
||||||
? new Date(lockedExpectedDeliveryDateTs)
|
? new Date(lockedExpectedDeliveryDateTs)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@ -7558,9 +7552,9 @@ export default function App() {
|
|||||||
<span style={{ fontSize: 13, color: '#666', fontWeight: 600 }}>缓冲期</span>
|
<span style={{ fontSize: 13, color: '#666', fontWeight: 600 }}>缓冲期</span>
|
||||||
<span style={{
|
<span style={{
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: '#1890ff',
|
color: dynamicBufferDays < 0 ? '#cf1322' : '#1890ff',
|
||||||
backgroundColor: 'rgba(24, 144, 255, 0.08)',
|
backgroundColor: dynamicBufferDays < 0 ? '#fff1f0' : 'rgba(24, 144, 255, 0.08)',
|
||||||
border: '1px solid rgba(24, 144, 255, 0.2)',
|
border: dynamicBufferDays < 0 ? '1px solid #ff4d4f' : '1px solid rgba(24, 144, 255, 0.2)',
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
padding: '3px 8px',
|
padding: '3px 8px',
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
@ -7610,7 +7604,7 @@ export default function App() {
|
|||||||
style={{ width: '200px' }}
|
style={{ width: '200px' }}
|
||||||
placeholder="请选择客户期望日期"
|
placeholder="请选择客户期望日期"
|
||||||
value={expectedDate ?? undefined}
|
value={expectedDate ?? undefined}
|
||||||
disabled={mode === 'adjust' && isExpectedDeliveryDateLocked}
|
disabled={isExpectedDeliveryDateLocked}
|
||||||
onChange={(date) => {
|
onChange={(date) => {
|
||||||
if (date instanceof Date) {
|
if (date instanceof Date) {
|
||||||
setExpectedDate(date);
|
setExpectedDate(date);
|
||||||
|
|||||||
Reference in New Issue
Block a user