5
5
This commit is contained in:
266
src/App.tsx
266
src/App.tsx
@ -1,6 +1,6 @@
|
|||||||
import { bitable, FieldType } from '@lark-base-open/js-sdk';
|
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 { 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 { addDays, format } from 'date-fns';
|
||||||
import { zhCN } from 'date-fns/locale';
|
import { zhCN } from 'date-fns/locale';
|
||||||
import { executePricingQuery, executeSecondaryProcessQuery, executePricingDetailsQuery } from './services/apiService';
|
import { executePricingQuery, executeSecondaryProcessQuery, executePricingDetailsQuery } from './services/apiService';
|
||||||
@ -136,6 +136,12 @@ export default function App() {
|
|||||||
setRestoredRecordIds([]);
|
setRestoredRecordIds([]);
|
||||||
setRestoredRecordIdsText('');
|
setRestoredRecordIdsText('');
|
||||||
|
|
||||||
|
// 重置初始快照捕获状态
|
||||||
|
try {
|
||||||
|
hasCapturedInitialSnapshotRef.current = false;
|
||||||
|
initialSnapshotRef.current = null;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
// 当前记录与批量信息
|
// 当前记录与批量信息
|
||||||
setCurrentBatchRecord(null);
|
setCurrentBatchRecord(null);
|
||||||
|
|
||||||
@ -2371,8 +2377,8 @@ export default function App() {
|
|||||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||||
const baseBuferDays = baseBufferDays;
|
const baseBuferDays = baseBufferDays;
|
||||||
|
|
||||||
// 智能缓冲期计算逻辑
|
// 智能缓冲期计算逻辑(缓冲期扣减=最后完成时间变动的自然天数)
|
||||||
let dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
let dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||||
|
|
||||||
// 只有在缓冲期为0时才检查最终限制:最后节点预计完成时间是否已达到客户期望日期
|
// 只有在缓冲期为0时才检查最终限制:最后节点预计完成时间是否已达到客户期望日期
|
||||||
let hasReachedFinalLimit = false;
|
let hasReachedFinalLimit = false;
|
||||||
@ -2503,8 +2509,8 @@ export default function App() {
|
|||||||
return timelineResults;
|
return timelineResults;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取重新计算后的时间线结果(不更新状态)
|
// 获取重新计算后的时间线结果(不更新状态,逻辑对齐页面的重算口径)
|
||||||
const getRecalculatedTimeline = (adjustments: {[key: number]: number}) => {
|
const getRecalculatedTimeline = (adjustments: { [key: number]: number }) => {
|
||||||
const updatedResults = [...timelineResults];
|
const updatedResults = [...timelineResults];
|
||||||
let cumulativeStartTime = startTime ? new Date(startTime) : new Date(); // 从起始时间开始
|
let cumulativeStartTime = startTime ? new Date(startTime) : new Date(); // 从起始时间开始
|
||||||
|
|
||||||
@ -2521,19 +2527,52 @@ export default function App() {
|
|||||||
// 计算当前节点的开始时间
|
// 计算当前节点的开始时间
|
||||||
let nodeStartTime = new Date(cumulativeStartTime);
|
let nodeStartTime = new Date(cumulativeStartTime);
|
||||||
|
|
||||||
// 获取节点的计算方式、周末天数和排除日期
|
// 应用起始日期调整规则(与页面重算逻辑一致)
|
||||||
const nodeCalculationMethod = result.calculationMethod || 'external';
|
if (result.startDateRule) {
|
||||||
const nodeWeekendDays = result.weekendDays || [];
|
let ruleJson = '';
|
||||||
const nodeExcludedDates = result.excludedDates || [];
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取调整规则描述
|
// 应用 JSON 日期调整规则(与页面重算逻辑一致)
|
||||||
const ruleDescription = result.ruleDescription || '';
|
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) {
|
if (adjustedTimelineValue !== 0) {
|
||||||
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates);
|
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates);
|
||||||
if (nodeCalculationMethod === 'internal') {
|
if (nodeCalculationMethod === '内部') {
|
||||||
nodeEndTime = addInternalBusinessTime(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates);
|
nodeEndTime = addInternalBusinessTime(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates);
|
||||||
} else {
|
} else {
|
||||||
nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates);
|
nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates);
|
||||||
@ -2542,34 +2581,31 @@ export default function App() {
|
|||||||
nodeEndTime = new Date(nodeStartTime);
|
nodeEndTime = new Date(nodeStartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算跳过的天数
|
// 计算跳过的天数及日期范围内的自定义跳过日期
|
||||||
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates);
|
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates);
|
||||||
const skippedWeekends = calculateSkippedWeekends(adjustedStartTime, nodeEndTime, nodeWeekendDays);
|
const skippedWeekends = calculateSkippedWeekends(adjustedStartTime, nodeEndTime, nodeWeekendDays);
|
||||||
const estimatedStartStr = formatDate(adjustedStartTime);
|
const estimatedStartStr = formatDate(adjustedStartTime);
|
||||||
const estimatedEndStr = adjustedTimelineValue !== 0 ? formatDate(nodeEndTime) : '时效值为0';
|
const estimatedEndStr = adjustedTimelineValue !== 0 ? formatDate(nodeEndTime) : '时效值为0';
|
||||||
const actualDays = calculateActualDays(estimatedStartStr, estimatedEndStr);
|
const actualDays = calculateActualDays(estimatedStartStr, estimatedEndStr);
|
||||||
|
|
||||||
// 计算时间范围内实际跳过的自定义日期
|
|
||||||
const excludedDatesInRange = calculateExcludedDatesInRange(adjustedStartTime, nodeEndTime, nodeExcludedDates);
|
const excludedDatesInRange = calculateExcludedDatesInRange(adjustedStartTime, nodeEndTime, nodeExcludedDates);
|
||||||
|
|
||||||
// 更新结果
|
// 更新结果
|
||||||
updatedResults[i] = {
|
updatedResults[i] = {
|
||||||
...result,
|
...result,
|
||||||
adjustedTimelineValue: adjustedTimelineValue,
|
adjustedTimelineValue,
|
||||||
estimatedStart: estimatedStartStr,
|
estimatedStart: estimatedStartStr,
|
||||||
estimatedEnd: estimatedEndStr,
|
estimatedEnd: estimatedEndStr,
|
||||||
adjustment: adjustment,
|
adjustment,
|
||||||
calculationMethod: nodeCalculationMethod, // 保持计算方式
|
calculationMethod: nodeCalculationMethod,
|
||||||
skippedWeekends: skippedWeekends,
|
skippedWeekends,
|
||||||
actualDays: actualDays,
|
actualDays,
|
||||||
// 更新时间范围内实际跳过的日期
|
|
||||||
actualExcludedDates: excludedDatesInRange.dates,
|
actualExcludedDates: excludedDatesInRange.dates,
|
||||||
actualExcludedDatesCount: excludedDatesInRange.count,
|
actualExcludedDatesCount: excludedDatesInRange.count,
|
||||||
adjustmentDescription: result.adjustmentDescription, // 保持调整规则描述
|
adjustmentDescription: result.adjustmentDescription,
|
||||||
ruleDescription: ruleDescription // 添加更新后的规则描述
|
ruleDescription
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新累积时间:当前节点的完成时间成为下一个节点的开始时间
|
// 更新累积开始时间:使用当前节点的预计完成时间
|
||||||
if (adjustedTimelineValue !== 0) {
|
if (adjustedTimelineValue !== 0) {
|
||||||
cumulativeStartTime = new Date(nodeEndTime);
|
cumulativeStartTime = new Date(nodeEndTime);
|
||||||
}
|
}
|
||||||
@ -2578,6 +2614,56 @@ export default function App() {
|
|||||||
return updatedResults;
|
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 recalculateTimeline = (adjustments: {[key: number]: number}, forceRecalculateAll: boolean = false) => {
|
||||||
const updatedResults = [...timelineResults];
|
const updatedResults = [...timelineResults];
|
||||||
@ -2750,6 +2836,10 @@ export default function App() {
|
|||||||
|
|
||||||
const [hasAppliedSuggestedBuffer, setHasAppliedSuggestedBuffer] = useState(false);
|
const [hasAppliedSuggestedBuffer, setHasAppliedSuggestedBuffer] = useState(false);
|
||||||
|
|
||||||
|
// 初始状态快照(仅捕获一次)
|
||||||
|
const initialSnapshotRef = useRef<any>(null);
|
||||||
|
const hasCapturedInitialSnapshotRef = useRef<boolean>(false);
|
||||||
|
|
||||||
// 当起始时间变更时,重新以最新起始时间为基准重算全流程
|
// 当起始时间变更时,重新以最新起始时间为基准重算全流程
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (timelineResults.length > 0 && !isRestoringSnapshot) {
|
if (timelineResults.length > 0 && !isRestoringSnapshot) {
|
||||||
@ -2764,6 +2854,26 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
}, [actualCompletionDates, isRestoringSnapshot]);
|
}, [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 = () => {
|
const resetTimelineAdjustments = () => {
|
||||||
setTimelineAdjustments({});
|
setTimelineAdjustments({});
|
||||||
@ -2775,6 +2885,40 @@ export default function App() {
|
|||||||
recalculateTimeline({}, true); // 强制重算所有节点
|
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 辅助函数
|
// 已移除未使用的 getTimelineLabelFieldId 辅助函数
|
||||||
|
|
||||||
// 写入货期记录表的函数
|
// 写入货期记录表的函数
|
||||||
@ -2972,10 +3116,8 @@ export default function App() {
|
|||||||
try {
|
try {
|
||||||
const lastCompletionDate = new Date(effectiveLastProcess.estimatedEnd);
|
const lastCompletionDate = new Date(effectiveLastProcess.estimatedEnd);
|
||||||
|
|
||||||
// 计算动态缓冲期:基础缓冲期 - 节点总调整量,最小为0天
|
// 计算动态缓冲期:按“最后完成时间自然日差”扣减基础缓冲期
|
||||||
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 deliveryDate = new Date(lastCompletionDate);
|
const deliveryDate = new Date(lastCompletionDate);
|
||||||
@ -3050,9 +3192,7 @@ export default function App() {
|
|||||||
const styleText = style || '';
|
const styleText = style || '';
|
||||||
const colorText = color || '';
|
const colorText = color || '';
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 检查是否达到最终限制
|
// 检查是否达到最终限制
|
||||||
let hasReachedFinalLimit = false;
|
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 = {
|
const globalSnapshot = {
|
||||||
version: versionNumber,
|
version: versionNumber,
|
||||||
foreignId,
|
foreignId,
|
||||||
@ -5468,10 +5612,24 @@ export default function App() {
|
|||||||
{/* 建议缓冲期增量:根据实际完成偏差与交期余量自动反算 */}
|
{/* 建议缓冲期增量:根据实际完成偏差与交期余量自动反算 */}
|
||||||
{(() => {
|
{(() => {
|
||||||
try {
|
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 baseBuferDays = baseBufferDays;
|
||||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||||
|
|
||||||
// 获取有效的最后流程完成日期
|
// 获取有效的最后流程完成日期
|
||||||
let effectiveLastProcess: any = null;
|
let effectiveLastProcess: any = null;
|
||||||
@ -5562,7 +5720,7 @@ export default function App() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
<Button onClick={resetTimelineAdjustments}>
|
<Button onClick={resetToInitialState}>
|
||||||
重置所有调整
|
重置所有调整
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
@ -5649,10 +5807,10 @@ export default function App() {
|
|||||||
// 当前节点是否为零值的周转周期节点
|
// 当前节点是否为零值的周转周期节点
|
||||||
const isCurrentTurnoverZero = isTurnoverNode && adjustedValue === 0;
|
const isCurrentTurnoverZero = isTurnoverNode && adjustedValue === 0;
|
||||||
|
|
||||||
// 计算动态缓冲期,用于限制节点增加操作
|
// 计算动态缓冲期(按“最后完成时间变动天数”扣减缓冲期)
|
||||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
||||||
const baseBuferDays = baseBufferDays;
|
const baseBuferDays = baseBufferDays;
|
||||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||||
|
|
||||||
// 新的复合限制逻辑:
|
// 新的复合限制逻辑:
|
||||||
// 1. 如果缓冲期 > 0,允许操作
|
// 1. 如果缓冲期 > 0,允许操作
|
||||||
@ -5904,8 +6062,6 @@ export default function App() {
|
|||||||
<div>
|
<div>
|
||||||
<Text strong style={{ display: 'block', marginBottom: '2px', fontSize: '15px' }}>时效值调整:</Text>
|
<Text strong style={{ display: 'block', marginBottom: '2px', fontSize: '15px' }}>时效值调整:</Text>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
|
||||||
{/* 注释掉 -0.5 功能按钮 */}
|
|
||||||
{false && (
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => handleComplexAdjustment(index, -0.5)}
|
onClick={() => handleComplexAdjustment(index, -0.5)}
|
||||||
@ -5915,9 +6071,6 @@ export default function App() {
|
|||||||
>
|
>
|
||||||
-0.5
|
-0.5
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
{/* 注释掉 -1 功能按钮 */}
|
|
||||||
{false && (
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => handleComplexAdjustment(index, -1)}
|
onClick={() => handleComplexAdjustment(index, -1)}
|
||||||
@ -5927,7 +6080,6 @@ export default function App() {
|
|||||||
>
|
>
|
||||||
-1
|
-1
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
<div style={{
|
<div style={{
|
||||||
minWidth: '70px',
|
minWidth: '70px',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
@ -5952,8 +6104,6 @@ export default function App() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* 注释掉 +1 功能按钮 */}
|
|
||||||
{false && (
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => handleComplexAdjustment(index, 1)}
|
onClick={() => handleComplexAdjustment(index, 1)}
|
||||||
@ -5968,9 +6118,6 @@ export default function App() {
|
|||||||
>
|
>
|
||||||
+1
|
+1
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
{/* 注释掉 +0.5 功能按钮 */}
|
|
||||||
{false && (
|
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => handleComplexAdjustment(index, 0.5)}
|
onClick={() => handleComplexAdjustment(index, 0.5)}
|
||||||
@ -5985,7 +6132,6 @@ export default function App() {
|
|||||||
>
|
>
|
||||||
+0.5
|
+0.5
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -6121,12 +6267,8 @@ export default function App() {
|
|||||||
lastCompletionDate = new Date(effectiveLastProcess.estimatedEnd);
|
lastCompletionDate = new Date(effectiveLastProcess.estimatedEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算所有节点的总调整量(用于动态缓冲期计算)
|
// 计算动态缓冲期:按“最后完成时间自然日差”扣减基础缓冲期
|
||||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||||
|
|
||||||
// 计算动态缓冲期:基础14天 - 节点总调整量,最小为0天
|
|
||||||
const baseBuferDays = baseBufferDays;
|
|
||||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
|
||||||
|
|
||||||
// 计算最后完成日期 + 动态缓冲期(考虑最后流程完成日期的调整)
|
// 计算最后完成日期 + 动态缓冲期(考虑最后流程完成日期的调整)
|
||||||
const adjustedCompletionDate = new Date(lastCompletionDate);
|
const adjustedCompletionDate = new Date(lastCompletionDate);
|
||||||
@ -6409,10 +6551,10 @@ export default function App() {
|
|||||||
lineHeight: '1.5'
|
lineHeight: '1.5'
|
||||||
}}>
|
}}>
|
||||||
{(() => {
|
{(() => {
|
||||||
// 计算动态缓冲期和总调整量
|
// 计算动态缓冲期(按“最后完成时间自然日差”扣减基础缓冲期)
|
||||||
const totalAdjustments = Object.values(timelineAdjustments).reduce((sum, adj) => sum + adj, 0);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
||||||
const baseBuferDays = baseBufferDays;
|
const baseBuferDays = baseBufferDays;
|
||||||
const dynamicBufferDays = Math.max(0, baseBuferDays - totalAdjustments);
|
const deltaDays = baseBuferDays - dynamicBufferDays; // 自然日差(已按0..baseBuferDays夹取)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text>
|
<Text>
|
||||||
@ -6427,11 +6569,11 @@ export default function App() {
|
|||||||
: '• 显示预计交付日期距离今天的天数'
|
: '• 显示预计交付日期距离今天的天数'
|
||||||
}
|
}
|
||||||
<br />
|
<br />
|
||||||
• 缓冲期 = 基础{baseBuferDays}天 - 节点调整总量(最小0天),包含质检、包装、物流等后续环节
|
• 缓冲期 = 基础{baseBuferDays}天 - 最后流程节点预计完成日期的自然日差(最小0天),包含质检、包装、物流等后续环节
|
||||||
{totalAdjustments > 0 && (
|
{deltaDays > 0 && (
|
||||||
<>
|
<>
|
||||||
<br />
|
<br />
|
||||||
• 当前节点总调整量:+{totalAdjustments}天,缓冲期相应减少
|
• 当前自然日差:+{deltaDays}天,缓冲期相应减少
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user