4
4
This commit is contained in:
125
src/App.tsx
125
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={
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
@ -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 (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<Text style={{ color: '#fa8c16' }}>建议增加缓冲期:+{displayInt}天</Text>
|
||||
<Button
|
||||
size="small"
|
||||
disabled={hasAppliedSuggestedBuffer}
|
||||
onClick={() => {
|
||||
if (!hasAppliedSuggestedBuffer) {
|
||||
setBaseBufferDays(Math.ceil(baseBufferDays) + displayInt);
|
||||
setHasAppliedSuggestedBuffer(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{hasAppliedSuggestedBuffer ? '已应用' : '应用'}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})()}
|
||||
<Button onClick={resetTimelineAdjustments}>
|
||||
重置所有调整
|
||||
</Button>
|
||||
@ -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触发,避免使用旧状态
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user