5
5
This commit is contained in:
360
src/App.tsx
360
src/App.tsx
@ -1,6 +1,6 @@
|
||||
import { bitable, FieldType } from '@lark-base-open/js-sdk';
|
||||
import { Button, Typography, List, Card, Space, Divider, Spin, Table, Select, Modal, DatePicker, InputNumber } from '@douyinfe/semi-ui';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { addDays, format } from 'date-fns';
|
||||
import { zhCN } from 'date-fns/locale';
|
||||
import { executePricingQuery, executeSecondaryProcessQuery, executePricingDetailsQuery } from './services/apiService';
|
||||
@ -136,6 +136,12 @@ export default function App() {
|
||||
setRestoredRecordIds([]);
|
||||
setRestoredRecordIdsText('');
|
||||
|
||||
// 重置初始快照捕获状态
|
||||
try {
|
||||
hasCapturedInitialSnapshotRef.current = false;
|
||||
initialSnapshotRef.current = null;
|
||||
} catch {}
|
||||
|
||||
// 当前记录与批量信息
|
||||
setCurrentBatchRecord(null);
|
||||
|
||||
@ -2371,8 +2377,8 @@ export default function App() {
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
const baseBuferDays = baseBufferDays;
|
||||
|
||||
// 智能缓冲期计算逻辑
|
||||
let dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
||||
// 智能缓冲期计算逻辑(缓冲期扣减=最后完成时间变动的自然天数)
|
||||
let dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||
|
||||
// 只有在缓冲期为0时才检查最终限制:最后节点预计完成时间是否已达到客户期望日期
|
||||
let hasReachedFinalLimit = false;
|
||||
@ -2503,8 +2509,8 @@ export default function App() {
|
||||
return timelineResults;
|
||||
};
|
||||
|
||||
// 获取重新计算后的时间线结果(不更新状态)
|
||||
const getRecalculatedTimeline = (adjustments: {[key: number]: number}) => {
|
||||
// 获取重新计算后的时间线结果(不更新状态,逻辑对齐页面的重算口径)
|
||||
const getRecalculatedTimeline = (adjustments: { [key: number]: number }) => {
|
||||
const updatedResults = [...timelineResults];
|
||||
let cumulativeStartTime = startTime ? new Date(startTime) : new Date(); // 从起始时间开始
|
||||
|
||||
@ -2521,19 +2527,52 @@ export default function App() {
|
||||
// 计算当前节点的开始时间
|
||||
let nodeStartTime = new Date(cumulativeStartTime);
|
||||
|
||||
// 获取节点的计算方式、周末天数和排除日期
|
||||
const nodeCalculationMethod = result.calculationMethod || 'external';
|
||||
const nodeWeekendDays = result.weekendDays || [];
|
||||
const nodeExcludedDates = result.excludedDates || [];
|
||||
// 应用起始日期调整规则(与页面重算逻辑一致)
|
||||
if (result.startDateRule) {
|
||||
let ruleJson = '';
|
||||
if (typeof result.startDateRule === 'string') {
|
||||
ruleJson = result.startDateRule;
|
||||
} else if (result.startDateRule && result.startDateRule.text) {
|
||||
ruleJson = result.startDateRule.text;
|
||||
}
|
||||
if (ruleJson.trim()) {
|
||||
nodeStartTime = adjustStartDateByRule(nodeStartTime, ruleJson);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取调整规则描述
|
||||
const ruleDescription = result.ruleDescription || '';
|
||||
// 应用 JSON 日期调整规则(与页面重算逻辑一致)
|
||||
let ruleDescription = result.ruleDescription || '';
|
||||
if (result.dateAdjustmentRule) {
|
||||
let ruleText = '';
|
||||
if (typeof result.dateAdjustmentRule === 'string') {
|
||||
ruleText = result.dateAdjustmentRule;
|
||||
} else if (Array.isArray(result.dateAdjustmentRule)) {
|
||||
ruleText = result.dateAdjustmentRule
|
||||
.filter(item => item.type === 'text')
|
||||
.map(item => item.text)
|
||||
.join('');
|
||||
} else if (result.dateAdjustmentRule && result.dateAdjustmentRule.text) {
|
||||
ruleText = result.dateAdjustmentRule.text;
|
||||
}
|
||||
if (ruleText && ruleText.trim() !== '') {
|
||||
const adjustmentResult = adjustStartDateByJsonRule(nodeStartTime, ruleText);
|
||||
nodeStartTime = adjustmentResult.adjustedDate;
|
||||
if (adjustmentResult.description) {
|
||||
ruleDescription = adjustmentResult.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取节点的计算方式、休息日与排除日期(与页面重算逻辑一致)
|
||||
const nodeWeekendDays = result.weekendDaysConfig || [];
|
||||
const nodeCalculationMethod = result.calculationMethod || '外部';
|
||||
const nodeExcludedDates = Array.isArray(result.excludedDates) ? result.excludedDates : [];
|
||||
|
||||
// 计算节点的结束时间(允许负时效值向前回退)
|
||||
let nodeEndTime;
|
||||
let nodeEndTime: Date;
|
||||
if (adjustedTimelineValue !== 0) {
|
||||
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates);
|
||||
if (nodeCalculationMethod === 'internal') {
|
||||
if (nodeCalculationMethod === '内部') {
|
||||
nodeEndTime = addInternalBusinessTime(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates);
|
||||
} else {
|
||||
nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates);
|
||||
@ -2542,34 +2581,31 @@ export default function App() {
|
||||
nodeEndTime = new Date(nodeStartTime);
|
||||
}
|
||||
|
||||
// 计算跳过的天数
|
||||
// 计算跳过的天数及日期范围内的自定义跳过日期
|
||||
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates);
|
||||
const skippedWeekends = calculateSkippedWeekends(adjustedStartTime, nodeEndTime, nodeWeekendDays);
|
||||
const estimatedStartStr = formatDate(adjustedStartTime);
|
||||
const estimatedEndStr = adjustedTimelineValue !== 0 ? formatDate(nodeEndTime) : '时效值为0';
|
||||
const actualDays = calculateActualDays(estimatedStartStr, estimatedEndStr);
|
||||
|
||||
// 计算时间范围内实际跳过的自定义日期
|
||||
const excludedDatesInRange = calculateExcludedDatesInRange(adjustedStartTime, nodeEndTime, nodeExcludedDates);
|
||||
|
||||
// 更新结果
|
||||
updatedResults[i] = {
|
||||
...result,
|
||||
adjustedTimelineValue: adjustedTimelineValue,
|
||||
adjustedTimelineValue,
|
||||
estimatedStart: estimatedStartStr,
|
||||
estimatedEnd: estimatedEndStr,
|
||||
adjustment: adjustment,
|
||||
calculationMethod: nodeCalculationMethod, // 保持计算方式
|
||||
skippedWeekends: skippedWeekends,
|
||||
actualDays: actualDays,
|
||||
// 更新时间范围内实际跳过的日期
|
||||
adjustment,
|
||||
calculationMethod: nodeCalculationMethod,
|
||||
skippedWeekends,
|
||||
actualDays,
|
||||
actualExcludedDates: excludedDatesInRange.dates,
|
||||
actualExcludedDatesCount: excludedDatesInRange.count,
|
||||
adjustmentDescription: result.adjustmentDescription, // 保持调整规则描述
|
||||
ruleDescription: ruleDescription // 添加更新后的规则描述
|
||||
adjustmentDescription: result.adjustmentDescription,
|
||||
ruleDescription
|
||||
};
|
||||
|
||||
// 更新累积时间:当前节点的完成时间成为下一个节点的开始时间
|
||||
// 更新累积开始时间:使用当前节点的预计完成时间
|
||||
if (adjustedTimelineValue !== 0) {
|
||||
cumulativeStartTime = new Date(nodeEndTime);
|
||||
}
|
||||
@ -2578,6 +2614,56 @@ export default function App() {
|
||||
return updatedResults;
|
||||
};
|
||||
|
||||
// 获取有效的最后完成日期(忽略 '时效值为0'),若不存在则返回最后一项的日期
|
||||
const getLastValidCompletionDateFromResults = (results: any[]): Date | null => {
|
||||
if (!Array.isArray(results) || results.length === 0) return null;
|
||||
for (let i = results.length - 1; i >= 0; i--) {
|
||||
const endStr = results[i]?.estimatedEnd;
|
||||
if (endStr && typeof endStr === 'string' && endStr !== '时效值为0') {
|
||||
const d = new Date(endStr);
|
||||
if (!isNaN(d.getTime())) return d;
|
||||
}
|
||||
}
|
||||
const fallbackStr = results[results.length - 1]?.estimatedEnd;
|
||||
const fallback = fallbackStr ? new Date(fallbackStr) : null;
|
||||
return fallback && !isNaN(fallback.getTime()) ? fallback : null;
|
||||
};
|
||||
|
||||
// 依据“最后流程节点的预计完成日期差(自然日)”计算剩余缓冲期
|
||||
const computeDynamicBufferDaysUsingEndDelta = (adjustments: { [key: number]: number }): number => {
|
||||
try {
|
||||
const baseline = getRecalculatedTimeline({}); // 原始计划(不含任何调整)
|
||||
const current = getRecalculatedTimeline(adjustments); // 当前计划(包含时效值调整)
|
||||
|
||||
// 严格取最后流程节点的预计完成日期;若不可用则回退到最后一个有效完成日期
|
||||
const pickLastNodeEnd = (results: any[]): Date | null => {
|
||||
if (!Array.isArray(results) || results.length === 0) return null;
|
||||
const endStr = results[results.length - 1]?.estimatedEnd;
|
||||
if (endStr && typeof endStr === 'string' && endStr !== '时效值为0') {
|
||||
const d = new Date(endStr);
|
||||
if (!isNaN(d.getTime())) return d;
|
||||
}
|
||||
return getLastValidCompletionDateFromResults(results);
|
||||
};
|
||||
|
||||
const baselineLast = pickLastNodeEnd(baseline);
|
||||
const currentLast = pickLastNodeEnd(current);
|
||||
if (!baselineLast || !currentLast) {
|
||||
return Math.max(0, Math.min(baseBufferDays, baseBufferDays));
|
||||
}
|
||||
|
||||
// 仅使用“预计完成日期”差,不叠加 completionDateAdjustment
|
||||
const dayMs = 1000 * 60 * 60 * 24;
|
||||
const deltaDays = Math.ceil((currentLast.getTime() - baselineLast.getTime()) / dayMs);
|
||||
// 缓冲期剩余 = clamp(基础缓冲期 - 差值, 0, 基础缓冲期)
|
||||
const remaining = baseBufferDays - deltaDays;
|
||||
return Math.max(0, Math.min(baseBufferDays, remaining));
|
||||
} catch {
|
||||
// 兜底:如出现异常,保持基础缓冲期
|
||||
return Math.max(0, Math.min(baseBufferDays, baseBufferDays));
|
||||
}
|
||||
};
|
||||
|
||||
// 重新计算时间线的函数
|
||||
const recalculateTimeline = (adjustments: {[key: number]: number}, forceRecalculateAll: boolean = false) => {
|
||||
const updatedResults = [...timelineResults];
|
||||
@ -2750,6 +2836,10 @@ export default function App() {
|
||||
|
||||
const [hasAppliedSuggestedBuffer, setHasAppliedSuggestedBuffer] = useState(false);
|
||||
|
||||
// 初始状态快照(仅捕获一次)
|
||||
const initialSnapshotRef = useRef<any>(null);
|
||||
const hasCapturedInitialSnapshotRef = useRef<boolean>(false);
|
||||
|
||||
// 当起始时间变更时,重新以最新起始时间为基准重算全流程
|
||||
useEffect(() => {
|
||||
if (timelineResults.length > 0 && !isRestoringSnapshot) {
|
||||
@ -2764,6 +2854,26 @@ export default function App() {
|
||||
}
|
||||
}, [actualCompletionDates, isRestoringSnapshot]);
|
||||
|
||||
// 捕获初始状态快照(在首次生成/还原出完整时间线后)
|
||||
useEffect(() => {
|
||||
if (!hasCapturedInitialSnapshotRef.current && timelineResults.length > 0) {
|
||||
initialSnapshotRef.current = {
|
||||
startTime,
|
||||
expectedDate,
|
||||
selectedLabels,
|
||||
timelineResults,
|
||||
timelineAdjustments,
|
||||
baseBufferDays,
|
||||
actualCompletionDates,
|
||||
completionDateAdjustment,
|
||||
hasAppliedSuggestedBuffer,
|
||||
deliveryMarginDeductions,
|
||||
};
|
||||
hasCapturedInitialSnapshotRef.current = true;
|
||||
console.log('已捕获初始状态快照');
|
||||
}
|
||||
}, [timelineResults, startTime, expectedDate]);
|
||||
|
||||
// 重置调整的函数
|
||||
const resetTimelineAdjustments = () => {
|
||||
setTimelineAdjustments({});
|
||||
@ -2775,6 +2885,40 @@ export default function App() {
|
||||
recalculateTimeline({}, true); // 强制重算所有节点
|
||||
};
|
||||
|
||||
// 一键还原到最初状态
|
||||
const resetToInitialState = async () => {
|
||||
try {
|
||||
if (!hasCapturedInitialSnapshotRef.current || !initialSnapshotRef.current) {
|
||||
// 若未捕获到初始快照,则退化为仅重置调整
|
||||
resetTimelineAdjustments();
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({ toastType: 'warning', message: '未检测到初始快照,已重置调整项' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
const s = initialSnapshotRef.current;
|
||||
setIsRestoringSnapshot(true);
|
||||
setStartTime(s.startTime || null);
|
||||
setExpectedDate(s.expectedDate || null);
|
||||
setSelectedLabels(s.selectedLabels || {});
|
||||
setTimelineAdjustments(s.timelineAdjustments || {});
|
||||
setBaseBufferDays(s.baseBufferDays ?? 14);
|
||||
setActualCompletionDates(s.actualCompletionDates || {});
|
||||
setCompletionDateAdjustment(s.completionDateAdjustment || 0);
|
||||
setHasAppliedSuggestedBuffer(!!s.hasAppliedSuggestedBuffer && s.hasAppliedSuggestedBuffer);
|
||||
setDeliveryMarginDeductions(s.deliveryMarginDeductions || 0);
|
||||
setTimelineResults(Array.isArray(s.timelineResults) ? s.timelineResults : []);
|
||||
recalculateTimeline(s.timelineAdjustments || {}, true);
|
||||
setIsRestoringSnapshot(false);
|
||||
if (bitable.ui.showToast) {
|
||||
await bitable.ui.showToast({ toastType: 'success', message: '已恢复至最初状态' });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('恢复初始状态失败:', e);
|
||||
setIsRestoringSnapshot(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 已移除未使用的 getTimelineLabelFieldId 辅助函数
|
||||
|
||||
// 写入货期记录表的函数
|
||||
@ -2972,10 +3116,8 @@ export default function App() {
|
||||
try {
|
||||
const lastCompletionDate = new Date(effectiveLastProcess.estimatedEnd);
|
||||
|
||||
// 计算动态缓冲期:基础缓冲期 - 节点总调整量,最小为0天
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
||||
// 计算动态缓冲期:按“最后完成时间自然日差”扣减基础缓冲期
|
||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||
|
||||
// 加上动态缓冲期
|
||||
const deliveryDate = new Date(lastCompletionDate);
|
||||
@ -3050,9 +3192,7 @@ export default function App() {
|
||||
const styleText = style || '';
|
||||
const colorText = color || '';
|
||||
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||
|
||||
// 检查是否达到最终限制
|
||||
let hasReachedFinalLimit = false;
|
||||
@ -3070,6 +3210,10 @@ export default function App() {
|
||||
}
|
||||
}
|
||||
|
||||
// 为快照提供基础缓冲与节点调整总量(用于兼容历史字段),尽管动态缓冲期已改为“自然日差”口径
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
|
||||
const globalSnapshot = {
|
||||
version: versionNumber,
|
||||
foreignId,
|
||||
@ -5468,10 +5612,24 @@ export default function App() {
|
||||
{/* 建议缓冲期增量:根据实际完成偏差与交期余量自动反算 */}
|
||||
{(() => {
|
||||
try {
|
||||
// 计算节点总调整量与动态缓冲期
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
// 仅当用户本次会话发生改动时显示建议
|
||||
const initialSnap = initialSnapshotRef.current;
|
||||
const hasAdjustmentChanges = Object.values(timelineAdjustments || {}).some(v => (Number(v) || 0) !== 0) || (Number(completionDateAdjustment) || 0) !== 0;
|
||||
const toMillisMap = (obj: any) => {
|
||||
const entries = Object.entries(obj || {});
|
||||
return Object.fromEntries(entries.map(([k, v]) => [k, v ? new Date(v as any).getTime() : null]));
|
||||
};
|
||||
const currentActualMap = toMillisMap(actualCompletionDates);
|
||||
const initialActualMap = toMillisMap(initialSnap?.actualCompletionDates || {});
|
||||
const hasActualChanges = JSON.stringify(currentActualMap) !== JSON.stringify(initialActualMap);
|
||||
const hasUserSessionChanges = hasAdjustmentChanges || hasActualChanges;
|
||||
if (!hasUserSessionChanges) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算动态缓冲期(按“最后完成时间自然日差”扣减基础缓冲期)
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||
|
||||
// 获取有效的最后流程完成日期
|
||||
let effectiveLastProcess: any = null;
|
||||
@ -5562,7 +5720,7 @@ export default function App() {
|
||||
return null;
|
||||
}
|
||||
})()}
|
||||
<Button onClick={resetTimelineAdjustments}>
|
||||
<Button onClick={resetToInitialState}>
|
||||
重置所有调整
|
||||
</Button>
|
||||
</Space>
|
||||
@ -5649,10 +5807,10 @@ export default function App() {
|
||||
// 当前节点是否为零值的周转周期节点
|
||||
const isCurrentTurnoverZero = isTurnoverNode && adjustedValue === 0;
|
||||
|
||||
// 计算动态缓冲期,用于限制节点增加操作
|
||||
// 计算动态缓冲期(按“最后完成时间变动天数”扣减缓冲期)
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||
|
||||
// 新的复合限制逻辑:
|
||||
// 1. 如果缓冲期 > 0,允许操作
|
||||
@ -5904,30 +6062,24 @@ export default function App() {
|
||||
<div>
|
||||
<Text strong style={{ display: 'block', marginBottom: '2px', fontSize: '15px' }}>时效值调整:</Text>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
||||
{/* 注释掉 -0.5 功能按钮 */}
|
||||
{false && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, -0.5)}
|
||||
disabled={adjustedValue <= 0 || isCurrentTurnoverZero}
|
||||
style={{ minWidth: '28px', height: '24px', fontSize: '13px' }}
|
||||
title={isCurrentTurnoverZero ? '周转周期为零,无法调整' : ''}
|
||||
>
|
||||
-0.5
|
||||
</Button>
|
||||
)}
|
||||
{/* 注释掉 -1 功能按钮 */}
|
||||
{false && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, -1)}
|
||||
disabled={adjustedValue <= 0 || isCurrentTurnoverZero}
|
||||
style={{ minWidth: '24px', height: '24px', fontSize: '13px' }}
|
||||
title={isCurrentTurnoverZero ? '周转周期为零,无法调整' : ''}
|
||||
>
|
||||
-1
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, -0.5)}
|
||||
disabled={adjustedValue <= 0 || isCurrentTurnoverZero}
|
||||
style={{ minWidth: '28px', height: '24px', fontSize: '13px' }}
|
||||
title={isCurrentTurnoverZero ? '周转周期为零,无法调整' : ''}
|
||||
>
|
||||
-0.5
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, -1)}
|
||||
disabled={adjustedValue <= 0 || isCurrentTurnoverZero}
|
||||
style={{ minWidth: '24px', height: '24px', fontSize: '13px' }}
|
||||
title={isCurrentTurnoverZero ? '周转周期为零,无法调整' : ''}
|
||||
>
|
||||
-1
|
||||
</Button>
|
||||
<div style={{
|
||||
minWidth: '70px',
|
||||
textAlign: 'center',
|
||||
@ -5952,40 +6104,34 @@ export default function App() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* 注释掉 +1 功能按钮 */}
|
||||
{false && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, 1)}
|
||||
disabled={isCurrentTurnoverZero || (hasTurnoverNodeWithZero && !isTurnoverNode) || (isTurnoverNode && !isCurrentTurnoverZero) || hasNegativeBuffer}
|
||||
style={{ minWidth: '24px', height: '24px', fontSize: '13px' }}
|
||||
title={
|
||||
isCurrentTurnoverZero ? '周转周期为零,无法调整' :
|
||||
(hasTurnoverNodeWithZero && !isTurnoverNode) ? '周转周期为零,其他节点无法增加' :
|
||||
(isTurnoverNode && !isCurrentTurnoverZero) ? '周转周期节点会自动调整,无法手动修改' :
|
||||
hasNegativeBuffer ? `缓冲期已耗尽且交期余量不足,不允许增加时效` : ''
|
||||
}
|
||||
>
|
||||
+1
|
||||
</Button>
|
||||
)}
|
||||
{/* 注释掉 +0.5 功能按钮 */}
|
||||
{false && (
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, 0.5)}
|
||||
disabled={isCurrentTurnoverZero || (hasTurnoverNodeWithZero && !isTurnoverNode) || (isTurnoverNode && !isCurrentTurnoverZero) || hasNegativeBuffer}
|
||||
style={{ minWidth: '28px', height: '24px', fontSize: '13px' }}
|
||||
title={
|
||||
isCurrentTurnoverZero ? '周转周期为零,无法调整' :
|
||||
(hasTurnoverNodeWithZero && !isTurnoverNode) ? '周转周期为零,其他节点无法增加' :
|
||||
(isTurnoverNode && !isCurrentTurnoverZero) ? '周转周期节点会自动调整,无法手动修改' :
|
||||
hasNegativeBuffer ? `缓冲期已耗尽且交期余量不足,不允许增加时效` : ''
|
||||
}
|
||||
>
|
||||
+0.5
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, 1)}
|
||||
disabled={isCurrentTurnoverZero || (hasTurnoverNodeWithZero && !isTurnoverNode) || (isTurnoverNode && !isCurrentTurnoverZero) || hasNegativeBuffer}
|
||||
style={{ minWidth: '24px', height: '24px', fontSize: '13px' }}
|
||||
title={
|
||||
isCurrentTurnoverZero ? '周转周期为零,无法调整' :
|
||||
(hasTurnoverNodeWithZero && !isTurnoverNode) ? '周转周期为零,其他节点无法增加' :
|
||||
(isTurnoverNode && !isCurrentTurnoverZero) ? '周转周期节点会自动调整,无法手动修改' :
|
||||
hasNegativeBuffer ? `缓冲期已耗尽且交期余量不足,不允许增加时效` : ''
|
||||
}
|
||||
>
|
||||
+1
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => handleComplexAdjustment(index, 0.5)}
|
||||
disabled={isCurrentTurnoverZero || (hasTurnoverNodeWithZero && !isTurnoverNode) || (isTurnoverNode && !isCurrentTurnoverZero) || hasNegativeBuffer}
|
||||
style={{ minWidth: '28px', height: '24px', fontSize: '13px' }}
|
||||
title={
|
||||
isCurrentTurnoverZero ? '周转周期为零,无法调整' :
|
||||
(hasTurnoverNodeWithZero && !isTurnoverNode) ? '周转周期为零,其他节点无法增加' :
|
||||
(isTurnoverNode && !isCurrentTurnoverZero) ? '周转周期节点会自动调整,无法手动修改' :
|
||||
hasNegativeBuffer ? `缓冲期已耗尽且交期余量不足,不允许增加时效` : ''
|
||||
}
|
||||
>
|
||||
+0.5
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -6121,12 +6267,8 @@ export default function App() {
|
||||
lastCompletionDate = new Date(effectiveLastProcess.estimatedEnd);
|
||||
}
|
||||
|
||||
// 计算所有节点的总调整量(用于动态缓冲期计算)
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
|
||||
// 计算动态缓冲期:基础14天 - 节点总调整量,最小为0天
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
||||
// 计算动态缓冲期:按“最后完成时间自然日差”扣减基础缓冲期
|
||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||
|
||||
// 计算最后完成日期 + 动态缓冲期(考虑最后流程完成日期的调整)
|
||||
const adjustedCompletionDate = new Date(lastCompletionDate);
|
||||
@ -6409,10 +6551,10 @@ export default function App() {
|
||||
lineHeight: '1.5'
|
||||
}}>
|
||||
{(() => {
|
||||
// 计算动态缓冲期和总调整量
|
||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||
// 计算动态缓冲期(按“最后完成时间自然日差”扣减基础缓冲期)
|
||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||
const baseBuferDays = baseBufferDays;
|
||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
||||
const deltaDays = baseBuferDays - dynamicBufferDays; // 自然日差(已按0..baseBuferDays夹取)
|
||||
|
||||
return (
|
||||
<Text>
|
||||
@ -6427,11 +6569,11 @@ export default function App() {
|
||||
: '• 显示预计交付日期距离今天的天数'
|
||||
}
|
||||
<br />
|
||||
• 缓冲期 = 基础{baseBuferDays}天 - 节点调整总量(最小0天),包含质检、包装、物流等后续环节
|
||||
{totalAdjustments > 0 && (
|
||||
• 缓冲期 = 基础{baseBuferDays}天 - 最后流程节点预计完成日期的自然日差(最小0天),包含质检、包装、物流等后续环节
|
||||
{deltaDays > 0 && (
|
||||
<>
|
||||
<br />
|
||||
• 当前节点总调整量:+{totalAdjustments}天,缓冲期相应减少
|
||||
• 当前自然日差:+{deltaDays}天,缓冲期相应减少
|
||||
</>
|
||||
)}
|
||||
</Text>
|
||||
|
||||
Reference in New Issue
Block a user