diff --git a/src/App.tsx b/src/App.tsx index 088dd49..c705984 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -74,6 +74,7 @@ export default function App() { const WEEKEND_DAYS_FIELD_ID = 'fld2BvjbIN'; // 休息日字段ID(多选项:0-6代表周一到周日) const START_DATE_RULE_FIELD_ID = 'fld0KsQ2j3'; // 起始日期调整规则字段ID const DATE_ADJUSTMENT_RULE_FIELD_ID = 'fld0KsQ2j3'; // 日期调整规则字段ID + const EXCLUDED_DATES_FIELD_ID = 'fldGxzC5uG'; // 不参与计算日期(多选,格式:yyyy-MM-dd) // 时效数据表相关常量 const TIMELINE_TABLE_ID = 'tblPnQscqwqopJ8V'; // 时效数据表ID @@ -217,7 +218,6 @@ export default function App() { weekendDaysConfig: [], matchedLabels: [], skippedWeekends: 0, - skippedHolidays: 0, actualDays: undefined, startDateRule: undefined, dateAdjustmentRule: undefined, @@ -269,19 +269,7 @@ export default function App() { // 起始时间字段(货期记录表新增) const DELIVERY_START_TIME_FIELD_ID = 'fld727qCAv'; - // 中国法定节假日配置(需要手动维护或使用API) - const CHINESE_HOLIDAYS = [ - '2024-01-01', // 元旦 - '2024-02-10', '2024-02-11', '2024-02-12', // 春节 - '2024-04-04', '2024-04-05', '2024-04-06', // 清明节 - '2024-05-01', '2024-05-02', '2024-05-03', // 劳动节 - '2024-06-10', // 端午节 - '2024-09-15', '2024-09-16', '2024-09-17', // 中秋节 - '2024-10-01', '2024-10-02', '2024-10-03', // 国庆节 - // ... 其他节假日 - ]; - - // 已移除未使用的 fetchHolidays 函数 + // 已移除中国法定节假日相关常量和配置 // 这个变量声明也不需要了 // const nodeNameToOptionId = new Map(); @@ -420,52 +408,39 @@ export default function App() { } }; - // 计算跳过的法定节假日天数 - const calculateSkippedHolidays = (startDateStr: string | Date, endDateStr: string | Date): number => { + // 计算时间范围内实际跳过的自定义日期 + const calculateExcludedDatesInRange = (startDate: Date | string, endDate: Date | string, excludedDates: string[] = []): { count: number, dates: string[] } => { + if (!excludedDates || excludedDates.length === 0) { + return { count: 0, dates: [] }; + } + try { - const startDate = typeof startDateStr === 'string' ? new Date(startDateStr) : startDateStr; - const endDate = typeof endDateStr === 'string' ? new Date(endDateStr) : endDateStr; + const start = typeof startDate === 'string' ? parseDate(startDate) : startDate; + const end = typeof endDate === 'string' ? parseDate(endDate) : endDate; - if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { - return 0; + if (!start || !end || isNaN(start.getTime()) || isNaN(end.getTime())) { + return { count: 0, dates: [] }; } - if (typeof startDateStr === 'string' && (startDateStr.includes('未找到') || startDateStr.includes('时效值为0'))) { - return 0; - } + const actualExcludedDates: string[] = []; - if (typeof endDateStr === 'string' && (endDateStr.includes('未找到') || endDateStr.includes('时效值为0'))) { - return 0; - } - - let count = 0; - let currentDate = new Date(startDate); - - while (currentDate <= endDate) { - if (isChineseHoliday(currentDate)) { - count++; + for (const excludedDateStr of excludedDates) { + const excludedDate = parseDate(excludedDateStr); + if (excludedDate && excludedDate >= start && excludedDate <= end) { + actualExcludedDates.push(excludedDateStr); } - currentDate = addDays(currentDate, 1); } - return count; + return { count: actualExcludedDates.length, dates: actualExcludedDates }; } catch (error) { - return 0; + console.error('计算跳过日期失败:', error); + return { count: 0, dates: [] }; } }; - // 判断是否为中国节假日 - const isChineseHoliday = (date: Date, holidays: string[] = CHINESE_HOLIDAYS): boolean => { - try { - if (isNaN(date.getTime())) { - return false; - } - const dateStr = format(date, 'yyyy-MM-dd'); - return holidays.includes(dateStr); - } catch (error) { - return false; - } - }; + // 已移除法定节假日跳过统计函数 + + // 已移除中国节假日判断函数 // 判断是否为自定义周末 - 支持空的休息日配置 const isCustomWeekend = (date: Date, weekendDays: number[] = []): boolean => { @@ -473,9 +448,15 @@ export default function App() { return weekendDays.includes(date.getDay()); }; - // 判断是否为工作日 - 只排除法定节假日和表格配置的休息日 - const isBusinessDay = (date: Date, weekendDays: number[] = []): boolean => { - return !isCustomWeekend(date, weekendDays) && !isChineseHoliday(date); + // 判断是否为工作日 - 排除表格休息日、以及节点自定义不参与计算日期 + const isBusinessDay = (date: Date, weekendDays: number[] = [], excludedDates: string[] = []): boolean => { + try { + const dateStr = format(date, 'yyyy-MM-dd'); + const isExcluded = Array.isArray(excludedDates) && excludedDates.includes(dateStr); + return !isCustomWeekend(date, weekendDays) && !isExcluded; + } catch { + return !isCustomWeekend(date, weekendDays); + } }; // 日期调整函数 @@ -577,7 +558,7 @@ export default function App() { }; // 调整到下一个工作时间开始点 - const adjustToNextWorkingHour = (date: Date, calculationMethod: string): Date => { + const adjustToNextWorkingHour = (date: Date, calculationMethod: string, weekendDays: number[] = [], excludedDates: string[] = []): Date => { const result = new Date(date); if (calculationMethod === '内部') { @@ -591,8 +572,8 @@ export default function App() { result.setDate(result.getDate() + 1); } - // 找到下一个工作日 - while (!isBusinessDay(result, [])) { + // 找到下一个工作日(考虑休息日和自定义跳过日期) + while (!isBusinessDay(result, weekendDays, excludedDates)) { result.setDate(result.getDate() + 1); } @@ -605,7 +586,7 @@ export default function App() { }; // 内部工作时间计算函数 - const addInternalBusinessTime = (startDate: Date, businessDays: number, weekendDays: number[] = []): Date => { + const addInternalBusinessTime = (startDate: Date, businessDays: number, weekendDays: number[] = [], excludedDates: string[] = []): Date => { const result = new Date(startDate); let remainingDays = businessDays; @@ -615,7 +596,7 @@ export default function App() { while (addedDays < wholeDays) { result.setDate(result.getDate() + 1); - if (isBusinessDay(result, weekendDays)) { + if (isBusinessDay(result, weekendDays, excludedDates)) { addedDays++; } } @@ -634,7 +615,7 @@ export default function App() { } else if (currentHour >= 18) { // 跳到下一个工作日的9:00 result.setDate(result.getDate() + 1); - while (!isBusinessDay(result, weekendDays)) { + while (!isBusinessDay(result, weekendDays, excludedDates)) { result.setDate(result.getDate() + 1); } currentHour = 9; @@ -653,7 +634,7 @@ export default function App() { // 跳到下一个工作日 result.setDate(result.getDate() + 1); - while (!isBusinessDay(result, weekendDays)) { + while (!isBusinessDay(result, weekendDays, excludedDates)) { result.setDate(result.getDate() + 1); } @@ -666,8 +647,8 @@ export default function App() { return result; }; - // 添加工作日 - 使用表格配置的休息日 - const addBusinessDaysWithHolidays = (startDate: Date, businessDays: number, weekendDays: number[] = []): Date => { + // 添加工作日 - 使用表格配置的休息日与节点自定义跳过日期 + const addBusinessDaysWithHolidays = (startDate: Date, businessDays: number, weekendDays: number[] = [], excludedDates: string[] = []): Date => { const result = new Date(startDate); let addedDays = 0; @@ -678,7 +659,7 @@ export default function App() { // 添加整数工作日 while (addedDays < wholeDays) { result.setDate(result.getDate() + 1); - if (isBusinessDay(result, weekendDays)) { + if (isBusinessDay(result, weekendDays, excludedDates)) { addedDays++; } } @@ -1028,6 +1009,27 @@ export default function App() { // 如果表格中没有配置休息日或配置为空,则该节点没有固定休息日 // 这样就完全依赖表格数据,不会有任何硬编码的默认值 + // 处理不参与计算日期(自定义跳过日期) + let excludedDates: string[] = []; + const excludedDatesField = fields[EXCLUDED_DATES_FIELD_ID]; + if (excludedDatesField) { + console.log('原始不参与计算日期字段数据:', excludedDatesField); + if (Array.isArray(excludedDatesField)) { + excludedDates = excludedDatesField.map(item => { + if (item && typeof item === 'object') { + const val = item.text || item.name || item.value || item.id || ''; + return String(val).trim(); + } + return String(item).trim(); + }).filter(d => !!d); + } else if (typeof excludedDatesField === 'string') { + excludedDates = excludedDatesField.split(/[\,\s]+/).map(s => s.trim()).filter(Boolean); + } else if (excludedDatesField && typeof excludedDatesField === 'object' && excludedDatesField.text) { + excludedDates = [String(excludedDatesField.text).trim()].filter(Boolean); + } + console.log('解析后的不参与计算日期:', excludedDates); + } + if (isMatched) { // 获取起始日期调整规则 const startDateRule = fields[START_DATE_RULE_FIELD_ID]; @@ -1041,6 +1043,7 @@ export default function App() { matchedLabels: matchedLabels, processOrder: orderValue, // 添加流程顺序 weekendDays: weekendDays, // 添加休息日配置 + excludedDates: excludedDates, // 添加自定义跳过日期 startDateRule: startDateRule, // 添加起始日期调整规则 dateAdjustmentRule: dateAdjustmentRule // 添加JSON格式日期调整规则 }); @@ -1324,15 +1327,15 @@ export default function App() { // 计算当前节点的开始和完成时间(使用工作日计算) const calculateTimeline = (startDate: Date, timelineValue: number, calculationMethod: string = '外部') => { // 根据计算方式调整开始时间 - const adjustedStartDate = adjustToNextWorkingHour(startDate, calculationMethod); + const adjustedStartDate = adjustToNextWorkingHour(startDate, calculationMethod, processNode.weekendDays, processNode.excludedDates || []); let endDate: Date; if (calculationMethod === '内部') { // 使用内部工作时间计算 - endDate = addInternalBusinessTime(adjustedStartDate, timelineValue, processNode.weekendDays); + endDate = addInternalBusinessTime(adjustedStartDate, timelineValue, processNode.weekendDays, processNode.excludedDates || []); } else { // 使用原有的24小时制计算 - endDate = addBusinessDaysWithHolidays(adjustedStartDate, timelineValue, processNode.weekendDays); + endDate = addBusinessDaysWithHolidays(adjustedStartDate, timelineValue, processNode.weekendDays, processNode.excludedDates || []); } return { @@ -1410,11 +1413,11 @@ export default function App() { let nodeEndTime: Date; if (timelineValue) { - const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod); + const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, processNode.weekendDays, processNode.excludedDates || []); if (nodeCalculationMethod === '内部') { - nodeEndTime = addInternalBusinessTime(adjustedStartTime, timelineValue, processNode.weekendDays); + nodeEndTime = addInternalBusinessTime(adjustedStartTime, timelineValue, processNode.weekendDays, processNode.excludedDates || []); } else { - nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, timelineValue, processNode.weekendDays); + nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, timelineValue, processNode.weekendDays, processNode.excludedDates || []); } } else { nodeEndTime = new Date(nodeStartTime); @@ -1422,9 +1425,11 @@ export default function App() { // 计算跳过的天数 const skippedWeekends = calculateSkippedWeekends(nodeStartTime, nodeEndTime, processNode.weekendDays); - const skippedHolidays = calculateSkippedHolidays(timelineResult.startDate, timelineResult.endDate); const actualDays = calculateActualDays(timelineResult.startDate, timelineResult.endDate); + // 计算时间范围内实际跳过的自定义日期 + const excludedDatesInRange = calculateExcludedDatesInRange(nodeStartTime, nodeEndTime, processNode.excludedDates || []); + results.push({ processOrder: processNode.processOrder, nodeName: processNode.nodeName, @@ -1445,10 +1450,13 @@ export default function App() { // 新增:标识是否为累加处理 isAccumulated: processingRule === '累加值' && matchedCandidates.length > 1, weekendDaysConfig: processNode.weekendDays, // 新增:保存休息日配置用于显示 + excludedDates: processNode.excludedDates || [], // 新增:保存不参与计算日期用于显示与快照 + // 新增:保存时间范围内实际跳过的日期 + actualExcludedDates: excludedDatesInRange.dates, + actualExcludedDatesCount: excludedDatesInRange.count, calculationMethod: nodeCalculationMethod, // 新增:保存计算方式 ruleDescription: ruleDescription, // 新增:保存规则描述 skippedWeekends: skippedWeekends, - skippedHolidays: skippedHolidays, actualDays: actualDays, // 新增:保存调整规则用于重新计算 startDateRule: processNode.startDateRule, @@ -1571,25 +1579,28 @@ export default function App() { const nodeCalculationMethod = result.calculationMethod || '外部'; // 获取节点的计算方式 let nodeEndTime: Date; + const nodeExcludedDates = Array.isArray(result.excludedDates) ? result.excludedDates : []; if (adjustedTimelineValue > 0) { - const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod); + const adjustedStartTime = adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates); if (nodeCalculationMethod === '内部') { - nodeEndTime = addInternalBusinessTime(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays); + nodeEndTime = addInternalBusinessTime(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates); } else { - nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays); + nodeEndTime = addBusinessDaysWithHolidays(adjustedStartTime, adjustedTimelineValue, nodeWeekendDays, nodeExcludedDates); } } else { nodeEndTime = new Date(nodeStartTime); } // 计算跳过的天数 - const adjustedStartTime = adjustedTimelineValue > 0 ? adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod) : nodeStartTime; + const adjustedStartTime = adjustedTimelineValue > 0 ? adjustToNextWorkingHour(nodeStartTime, nodeCalculationMethod, nodeWeekendDays, nodeExcludedDates) : nodeStartTime; const skippedWeekends = calculateSkippedWeekends(adjustedStartTime, nodeEndTime, nodeWeekendDays); const estimatedStartStr = formatDate(adjustedStartTime); const estimatedEndStr = adjustedTimelineValue > 0 ? formatDate(nodeEndTime) : '时效值为0'; - const skippedHolidays = calculateSkippedHolidays(estimatedStartStr, estimatedEndStr); const actualDays = calculateActualDays(estimatedStartStr, estimatedEndStr); + // 计算时间范围内实际跳过的自定义日期 + const excludedDatesInRange = calculateExcludedDatesInRange(adjustedStartTime, nodeEndTime, nodeExcludedDates); + // 更新结果 updatedResults[i] = { ...result, @@ -1599,8 +1610,10 @@ export default function App() { adjustment: adjustment, calculationMethod: nodeCalculationMethod, // 保持计算方式 skippedWeekends: skippedWeekends, - skippedHolidays: skippedHolidays, actualDays: actualDays, + // 更新时间范围内实际跳过的日期 + actualExcludedDates: excludedDatesInRange.dates, + actualExcludedDatesCount: excludedDatesInRange.count, adjustmentDescription: result.adjustmentDescription, // 保持调整规则描述 ruleDescription: ruleDescription // 添加更新后的规则描述 }; @@ -2981,11 +2994,14 @@ export default function App() { backgroundColor: '#f0f0f0', border: '1px solid #d9d9d9' }} - title={`计算方式详情:\n${result.calculationMethod === '内部' ? '内部计算 (9小时工作制)' : '外部计算 (24小时制)'}${result.ruleDescription ? `\n应用规则:${result.ruleDescription}` : ''}\n已跳过周末:${result.skippedWeekends || 0} 天\n已跳过法定节假日:${result.skippedHolidays || 0} 天\n${result.weekendDaysConfig && result.weekendDaysConfig.length > 0 ? `休息日配置:${result.weekendDaysConfig.map(day => ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][day]).join(', ')}` : '休息日配置:无固定休息日'}${result.calculationMethod === '内部' ? '\n工作时间:9:00-18:00 (9小时制)' : ''}`} + title={`计算方式详情:\n${result.calculationMethod === '内部' ? '内部计算 (9小时工作制)' : '外部计算 (24小时制)'}${result.ruleDescription ? `\n应用规则:${result.ruleDescription}` : ''}\n已跳过周末:${result.skippedWeekends || 0} 天\n${result.weekendDaysConfig && result.weekendDaysConfig.length > 0 ? `休息日配置:${result.weekendDaysConfig.map(day => ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][day]).join(', ')}` : '休息日配置:无固定休息日'}${result.calculationMethod === '内部' ? '\n工作时间:9:00-18:00 (9小时制)' : ''}${(Array.isArray(result.actualExcludedDates) && result.actualExcludedDates.length > 0) ? `\n\n跳过的具体日期:\n${result.actualExcludedDates.join('\n')}` : ''}`} > ? +
+ 跳过日期:{(result.actualExcludedDatesCount && result.actualExcludedDatesCount > 0) ? `${result.actualExcludedDatesCount} 天` : '无'} +
@@ -3006,7 +3022,7 @@ export default function App() {
• 根据每个节点的休息日配置自动跳过相应的休息日
- • 自动跳过中国法定节假日(元旦、春节、清明、劳动节、端午、中秋、国庆等) + • 可为每个节点配置“跳过日期”,这些日期将不参与工作日计算
• 时效值以"工作日"为单位计算,确保预期时间的准确性