6
6
This commit is contained in:
149
src/App.tsx
149
src/App.tsx
@ -1976,7 +1976,8 @@ export default function App() {
|
|||||||
recordDetails?: any[],
|
recordDetails?: any[],
|
||||||
selectedLabels?: {[key: string]: string | string[]},
|
selectedLabels?: {[key: string]: string | string[]},
|
||||||
expectedDate?: Date | null,
|
expectedDate?: Date | null,
|
||||||
startTime?: Date | null
|
startTime?: Date | null,
|
||||||
|
excludedDates?: string[]
|
||||||
},
|
},
|
||||||
showUI: boolean = true // 新增参数控制是否显示UI
|
showUI: boolean = true // 新增参数控制是否显示UI
|
||||||
) => {
|
) => {
|
||||||
@ -1986,6 +1987,7 @@ export default function App() {
|
|||||||
const currentSelectedLabels = overrideData?.selectedLabels || selectedLabels;
|
const currentSelectedLabels = overrideData?.selectedLabels || selectedLabels;
|
||||||
const currentExpectedDate = overrideData?.expectedDate || expectedDate;
|
const currentExpectedDate = overrideData?.expectedDate || expectedDate;
|
||||||
const currentStartTime = overrideData?.startTime || startTime;
|
const currentStartTime = overrideData?.startTime || startTime;
|
||||||
|
const currentExcludedDates = Array.isArray(overrideData?.excludedDates) ? overrideData!.excludedDates : [];
|
||||||
const isBackward = timelineDirection === 'backward';
|
const isBackward = timelineDirection === 'backward';
|
||||||
|
|
||||||
console.log('=== handleCalculateTimeline - 使用的数据 ===');
|
console.log('=== handleCalculateTimeline - 使用的数据 ===');
|
||||||
@ -2381,6 +2383,7 @@ export default function App() {
|
|||||||
|
|
||||||
for (let i = 0; i < nodesToProcess.length; i++) {
|
for (let i = 0; i < nodesToProcess.length; i++) {
|
||||||
const processNode = nodesToProcess[i];
|
const processNode = nodesToProcess[i];
|
||||||
|
const nodeExcludedDates = Array.from(new Set([...(processNode.excludedDates || []), ...currentExcludedDates])).filter(Boolean);
|
||||||
let timelineValue = null;
|
let timelineValue = null;
|
||||||
let matchedTimelineRecord = null;
|
let matchedTimelineRecord = null;
|
||||||
|
|
||||||
@ -2587,15 +2590,15 @@ export default function App() {
|
|||||||
// 计算当前节点的开始和完成时间(使用工作日计算)
|
// 计算当前节点的开始和完成时间(使用工作日计算)
|
||||||
const calculateTimeline = (startDate: Date, timelineValue: number, calculationMethod: string = '外部') => {
|
const calculateTimeline = (startDate: Date, timelineValue: number, calculationMethod: string = '外部') => {
|
||||||
// 根据计算方式调整开始时间
|
// 根据计算方式调整开始时间
|
||||||
const adjustedStartDate = adjustToNextWorkingHour(startDate, calculationMethod, processNode.weekendDays, processNode.excludedDates || []);
|
const adjustedStartDate = adjustToNextWorkingHour(startDate, calculationMethod, processNode.weekendDays, nodeExcludedDates);
|
||||||
|
|
||||||
let endDate: Date;
|
let endDate: Date;
|
||||||
if (calculationMethod === '内部') {
|
if (calculationMethod === '内部') {
|
||||||
// 使用内部工作时间计算
|
// 使用内部工作时间计算
|
||||||
endDate = addInternalBusinessTime(adjustedStartDate, timelineValue, processNode.weekendDays, processNode.excludedDates || []);
|
endDate = addInternalBusinessTime(adjustedStartDate, timelineValue, processNode.weekendDays, nodeExcludedDates);
|
||||||
} else {
|
} else {
|
||||||
// 使用原有的24小时制计算
|
// 使用原有的24小时制计算
|
||||||
endDate = addBusinessDaysWithHolidays(adjustedStartDate, timelineValue, processNode.weekendDays, processNode.excludedDates || []);
|
endDate = addBusinessDaysWithHolidays(adjustedStartDate, timelineValue, processNode.weekendDays, nodeExcludedDates);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -2674,11 +2677,11 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (timelineValue) {
|
if (timelineValue) {
|
||||||
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, processNode.weekendDays, processNode.excludedDates || []);
|
const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, processNode.weekendDays, nodeExcludedDates);
|
||||||
if (nodeCalculationMethod === '内部') {
|
if (nodeCalculationMethod === '内部') {
|
||||||
nodeEndTime = addInternalBusinessTime(adjustedStartTime, timelineValue, processNode.weekendDays, processNode.excludedDates || []);
|
nodeEndTime = addInternalBusinessTime(adjustedStartTime, timelineValue, processNode.weekendDays, nodeExcludedDates);
|
||||||
} else {
|
} else {
|
||||||
nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, timelineValue, processNode.weekendDays, processNode.excludedDates || []);
|
nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, timelineValue, processNode.weekendDays, nodeExcludedDates);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nodeEndTime = new Date(nodeStartTime);
|
nodeEndTime = new Date(nodeStartTime);
|
||||||
@ -2687,9 +2690,9 @@ export default function App() {
|
|||||||
nodeEndTime = new Date(cumulativeTime);
|
nodeEndTime = new Date(cumulativeTime);
|
||||||
if (timelineValue) {
|
if (timelineValue) {
|
||||||
if (nodeCalculationMethod === '内部') {
|
if (nodeCalculationMethod === '内部') {
|
||||||
nodeStartTime = addInternalBusinessTime(nodeEndTime, -timelineValue, processNode.weekendDays, processNode.excludedDates || []);
|
nodeStartTime = addInternalBusinessTime(nodeEndTime, -timelineValue, processNode.weekendDays, nodeExcludedDates);
|
||||||
} else {
|
} else {
|
||||||
nodeStartTime = addBusinessDaysWithHolidays(nodeEndTime, -timelineValue, processNode.weekendDays, processNode.excludedDates || []);
|
nodeStartTime = addBusinessDaysWithHolidays(nodeEndTime, -timelineValue, processNode.weekendDays, nodeExcludedDates);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nodeStartTime = new Date(nodeEndTime);
|
nodeStartTime = new Date(nodeEndTime);
|
||||||
@ -2711,7 +2714,7 @@ export default function App() {
|
|||||||
const actualDays = calculateActualDays(timelineResult.startDate, timelineResult.endDate);
|
const actualDays = calculateActualDays(timelineResult.startDate, timelineResult.endDate);
|
||||||
|
|
||||||
// 计算时间范围内实际跳过的自定义日期
|
// 计算时间范围内实际跳过的自定义日期
|
||||||
const excludedDatesInRange = calculateExcludedDatesInRange(nodeStartTime, nodeEndTime, processNode.excludedDates || []);
|
const excludedDatesInRange = calculateExcludedDatesInRange(nodeStartTime, nodeEndTime, nodeExcludedDates);
|
||||||
|
|
||||||
(isBackward ? results.unshift.bind(results) : results.push.bind(results))({
|
(isBackward ? results.unshift.bind(results) : results.push.bind(results))({
|
||||||
processOrder: processNode.processOrder,
|
processOrder: processNode.processOrder,
|
||||||
@ -2733,7 +2736,7 @@ export default function App() {
|
|||||||
// 新增:标识是否为累加处理
|
// 新增:标识是否为累加处理
|
||||||
isAccumulated: processingRule === '累加值' && matchedCandidates.length > 1,
|
isAccumulated: processingRule === '累加值' && matchedCandidates.length > 1,
|
||||||
weekendDaysConfig: processNode.weekendDays, // 新增:保存休息日配置用于显示
|
weekendDaysConfig: processNode.weekendDays, // 新增:保存休息日配置用于显示
|
||||||
excludedDates: processNode.excludedDates || [], // 新增:保存不参与计算日期用于显示与快照
|
excludedDates: nodeExcludedDates, // 新增:保存不参与计算日期用于显示与快照
|
||||||
// 新增:保存时间范围内实际跳过的日期
|
// 新增:保存时间范围内实际跳过的日期
|
||||||
actualExcludedDates: excludedDatesInRange.dates,
|
actualExcludedDates: excludedDatesInRange.dates,
|
||||||
actualExcludedDatesCount: excludedDatesInRange.count,
|
actualExcludedDatesCount: excludedDatesInRange.count,
|
||||||
@ -2792,6 +2795,43 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@ -3160,12 +3200,13 @@ export default function App() {
|
|||||||
|
|
||||||
const computeExpectedDeliveryDateTsFromResults = (
|
const computeExpectedDeliveryDateTsFromResults = (
|
||||||
results: any[],
|
results: any[],
|
||||||
adjustments: { [key: number]: number }
|
adjustments: { [key: number]: number },
|
||||||
|
baseBufferDaysOverride?: number
|
||||||
): number | null => {
|
): number | null => {
|
||||||
if (timelineDirection === 'backward') return null;
|
if (timelineDirection === 'backward') return null;
|
||||||
const lastCompletionDate = getLastValidCompletionDateFromResults(results);
|
const lastCompletionDate = getLastValidCompletionDateFromResults(results);
|
||||||
if (!lastCompletionDate) return null;
|
if (!lastCompletionDate) return null;
|
||||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(adjustments);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(adjustments, baseBufferDaysOverride);
|
||||||
const deliveryDate = new Date(lastCompletionDate);
|
const deliveryDate = new Date(lastCompletionDate);
|
||||||
deliveryDate.setDate(deliveryDate.getDate() + dynamicBufferDays);
|
deliveryDate.setDate(deliveryDate.getDate() + dynamicBufferDays);
|
||||||
const ts = deliveryDate.getTime();
|
const ts = deliveryDate.getTime();
|
||||||
@ -3181,15 +3222,15 @@ export default function App() {
|
|||||||
if (computed !== null) setLockedExpectedDeliveryDateTs(computed);
|
if (computed !== null) setLockedExpectedDeliveryDateTs(computed);
|
||||||
};
|
};
|
||||||
|
|
||||||
const computeDynamicBufferDaysUsingEndDelta = (adjustments: { [key: number]: number }): number => {
|
const computeDynamicBufferDaysUsingEndDelta = (adjustments: { [key: number]: number }, baseBufferDaysOverride?: number): number => {
|
||||||
try {
|
try {
|
||||||
if (timelineDirection === 'backward') return 0;
|
if (timelineDirection === 'backward') return 0;
|
||||||
const deltaDays = computeLastNodeEndDeltaDays(adjustments);
|
const deltaDays = computeLastNodeEndDeltaDays(adjustments);
|
||||||
const base = Math.max(0, Math.ceil(baseBufferDays));
|
const base = Math.max(0, Math.ceil(baseBufferDaysOverride ?? baseBufferDays));
|
||||||
const remaining = base - deltaDays;
|
const remaining = base - deltaDays;
|
||||||
return Math.max(0, Math.min(base, remaining));
|
return Math.max(0, Math.min(base, remaining));
|
||||||
} catch {
|
} catch {
|
||||||
const base = Math.max(0, Math.ceil(baseBufferDays));
|
const base = Math.max(0, Math.ceil(baseBufferDaysOverride ?? baseBufferDays));
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -3502,7 +3543,7 @@ export default function App() {
|
|||||||
timelineResults: any[],
|
timelineResults: any[],
|
||||||
processRecordIds: string[],
|
processRecordIds: string[],
|
||||||
timelineAdjustments: {[key: number]: number} = {},
|
timelineAdjustments: {[key: number]: number} = {},
|
||||||
overrides?: { foreignId?: string; style?: string; color?: string; expectedDate?: Date | null; startTime?: Date | null; selectedLabels?: {[key: string]: string | string[]} }
|
overrides?: { foreignId?: string; style?: string; color?: string; expectedDate?: Date | null; startTime?: Date | null; selectedLabels?: {[key: string]: string | string[]}; baseBufferDays?: number }
|
||||||
) => {
|
) => {
|
||||||
let recordCells: any[] | undefined;
|
let recordCells: any[] | undefined;
|
||||||
try {
|
try {
|
||||||
@ -3664,8 +3705,10 @@ 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);
|
let expectedDeliveryDate = computeExpectedDeliveryDateTsFromResults(timelineResults, timelineAdjustments, baseBufferDaysToUse);
|
||||||
if (mode === 'adjust' && isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) {
|
if (mode === 'adjust' && isExpectedDeliveryDateLocked && lockedExpectedDeliveryDateTs !== null) {
|
||||||
expectedDeliveryDate = lockedExpectedDeliveryDateTs;
|
expectedDeliveryDate = lockedExpectedDeliveryDateTs;
|
||||||
}
|
}
|
||||||
@ -3721,7 +3764,7 @@ export default function App() {
|
|||||||
const styleText = style || '';
|
const styleText = style || '';
|
||||||
const colorText = color || '';
|
const colorText = color || '';
|
||||||
|
|
||||||
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments);
|
const dynamicBufferDays = computeDynamicBufferDaysUsingEndDelta(timelineAdjustments, baseBufferDaysToUse);
|
||||||
|
|
||||||
// 检查是否达到最终限制
|
// 检查是否达到最终限制
|
||||||
let hasReachedFinalLimit = false;
|
let hasReachedFinalLimit = false;
|
||||||
@ -3740,7 +3783,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 为快照提供基础缓冲与节点调整总量(用于兼容历史字段),尽管动态缓冲期已改为“自然日差”口径
|
// 为快照提供基础缓冲与节点调整总量(用于兼容历史字段),尽管动态缓冲期已改为“自然日差”口径
|
||||||
const baseBuferDays = baseBufferDays;
|
const baseBuferDays = baseBufferDaysToUse;
|
||||||
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 = {
|
||||||
@ -4436,6 +4479,45 @@ export default function App() {
|
|||||||
const v = (rawExpected as any).value || (rawExpected as any).text || (rawExpected as any).name || '';
|
const v = (rawExpected as any).value || (rawExpected as any).text || (rawExpected as any).name || '';
|
||||||
if (typeof v === 'number') expectedDateObj = new Date(v); else expectedDateObj = parseDate(v);
|
if (typeof v === 'number') expectedDateObj = new Date(v); else expectedDateObj = parseDate(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const normalizeExcludedDates = (raw: any): string[] => {
|
||||||
|
const out: string[] = [];
|
||||||
|
const pushToken = (token: any) => {
|
||||||
|
if (token == null) return;
|
||||||
|
if (typeof token === 'number' && Number.isFinite(token)) {
|
||||||
|
const d = new Date(token);
|
||||||
|
if (!isNaN(d.getTime())) out.push(format(d, 'yyyy-MM-dd'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const text = (typeof token === 'string') ? token : extractText(token);
|
||||||
|
if (!text) return;
|
||||||
|
const parts = String(text).split(/[,,;;、\n\r\t ]+/).map(s => s.trim()).filter(Boolean);
|
||||||
|
for (const p of parts) {
|
||||||
|
const head = p.slice(0, 10);
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(head)) {
|
||||||
|
out.push(head);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const parsed = parseDate(p);
|
||||||
|
if (parsed && !isNaN(parsed.getTime())) out.push(format(parsed, 'yyyy-MM-dd'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const walk = (v: any) => {
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
v.forEach(walk);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (v && typeof v === 'object' && Array.isArray((v as any).value)) {
|
||||||
|
walk((v as any).value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pushToken(v);
|
||||||
|
};
|
||||||
|
walk(raw);
|
||||||
|
return Array.from(new Set(out)).filter(Boolean);
|
||||||
|
};
|
||||||
|
|
||||||
|
const batchExcludedDates = normalizeExcludedDates(f['fldQNxtHnd']);
|
||||||
const splitVals = (s: string) => (s || '').split(/[,,、]+/).map(v => v.trim()).filter(Boolean);
|
const splitVals = (s: string) => (s || '').split(/[,,、]+/).map(v => v.trim()).filter(Boolean);
|
||||||
const normalizeToStringList = (raw: any): string[] => {
|
const normalizeToStringList = (raw: any): string[] => {
|
||||||
if (!raw) return [];
|
if (!raw) return [];
|
||||||
@ -4479,10 +4561,33 @@ export default function App() {
|
|||||||
setStartTime(startDate || null);
|
setStartTime(startDate || null);
|
||||||
setSelectedLabels(labels);
|
setSelectedLabels(labels);
|
||||||
try {
|
try {
|
||||||
const results = await handleCalculateTimeline(true, { selectedLabels: labels, expectedDate: expectedDateObj || null, startTime: startDate || null }, 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(results, processRecordIds, {}, { foreignId, style: styleText, color: colorText, expectedDate: expectedDateObj || null, startTime: startDate || null, selectedLabels: labels });
|
const deliveryRecordId = await writeToDeliveryRecordTable(
|
||||||
|
results,
|
||||||
|
processRecordIds,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
foreignId,
|
||||||
|
style: styleText,
|
||||||
|
color: colorText,
|
||||||
|
expectedDate: expectedDateObj || null,
|
||||||
|
startTime: startDate || null,
|
||||||
|
selectedLabels: labels,
|
||||||
|
baseBufferDays: suggestedBaseBufferDays
|
||||||
|
}
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
const candidateNames = ['状态','record_id','记录ID','货期记录ID','deliveryRecordId'];
|
const candidateNames = ['状态','record_id','记录ID','货期记录ID','deliveryRecordId'];
|
||||||
let statusFieldId = '';
|
let statusFieldId = '';
|
||||||
|
|||||||
Reference in New Issue
Block a user