3
3
This commit is contained in:
474
src/App.tsx
474
src/App.tsx
@ -224,6 +224,7 @@ export default function App() {
|
|||||||
const [batchEndRow, setBatchEndRow] = useState<number>(1);
|
const [batchEndRow, setBatchEndRow] = useState<number>(1);
|
||||||
const [batchTotalRows, setBatchTotalRows] = useState<number>(0);
|
const [batchTotalRows, setBatchTotalRows] = useState<number>(0);
|
||||||
const [batchLoading, setBatchLoading] = useState(false);
|
const [batchLoading, setBatchLoading] = useState(false);
|
||||||
|
const [batchMode, setBatchMode] = useState<'generate' | 'adjust'>('generate');
|
||||||
const [batchProcessedCount, setBatchProcessedCount] = useState<number>(0);
|
const [batchProcessedCount, setBatchProcessedCount] = useState<number>(0);
|
||||||
const [batchProcessingTotal, setBatchProcessingTotal] = useState<number>(0);
|
const [batchProcessingTotal, setBatchProcessingTotal] = useState<number>(0);
|
||||||
const [batchSuccessCount, setBatchSuccessCount] = useState<number>(0);
|
const [batchSuccessCount, setBatchSuccessCount] = useState<number>(0);
|
||||||
@ -404,6 +405,9 @@ export default function App() {
|
|||||||
const BATCH_LABEL11_FIELD_ID = 'fld4BZHtBV';
|
const BATCH_LABEL11_FIELD_ID = 'fld4BZHtBV';
|
||||||
const BATCH_LABEL12_FIELD_ID = 'fldnRlMeaD';
|
const BATCH_LABEL12_FIELD_ID = 'fldnRlMeaD';
|
||||||
const BATCH_BUFFER_FIELD_ID = 'fldLBXEAo0';
|
const BATCH_BUFFER_FIELD_ID = 'fldLBXEAo0';
|
||||||
|
const BATCH_SOURCE_DELIVERY_RECORD_ID_FIELD_ID = 'fld5pcGWeh';
|
||||||
|
const BATCH_RESULT_DELIVERY_RECORD_ID_FIELD_ID = 'fld9as1Y1c';
|
||||||
|
const BATCH_CUSTOMER_EXPECTED_DATE_FIELD_ID = 'fldqi7nUix';
|
||||||
|
|
||||||
const activateTableForPaging = async (table: any) => {
|
const activateTableForPaging = async (table: any) => {
|
||||||
try {
|
try {
|
||||||
@ -468,6 +472,115 @@ export default function App() {
|
|||||||
return Number.isFinite(n) ? n : null;
|
return Number.isFinite(n) ? n : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseBatchDateValue = (raw: any): Date | null => {
|
||||||
|
if (raw == null) return null;
|
||||||
|
if (raw instanceof Date && !isNaN(raw.getTime())) return raw;
|
||||||
|
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
||||||
|
const d = new Date(raw);
|
||||||
|
return isNaN(d.getTime()) ? null : d;
|
||||||
|
}
|
||||||
|
if (Array.isArray(raw) && raw.length > 0) {
|
||||||
|
for (const item of raw) {
|
||||||
|
const parsed = parseBatchDateValue(item);
|
||||||
|
if (parsed) return parsed;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeof raw === 'string') {
|
||||||
|
const t = raw.trim();
|
||||||
|
if (!t) return null;
|
||||||
|
const asNum = Number(t);
|
||||||
|
if (Number.isFinite(asNum) && /^\d{10,13}$/.test(t)) {
|
||||||
|
const d = new Date(asNum);
|
||||||
|
if (!isNaN(d.getTime())) return d;
|
||||||
|
}
|
||||||
|
const parsed = parseDate(t);
|
||||||
|
if (parsed && !isNaN(parsed.getTime())) return parsed;
|
||||||
|
const d = new Date(t);
|
||||||
|
return isNaN(d.getTime()) ? null : d;
|
||||||
|
}
|
||||||
|
if (typeof raw === 'object') {
|
||||||
|
const v = (raw as any).value ?? (raw as any).text ?? (raw as any).name;
|
||||||
|
return parseBatchDateValue(v);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeActualCompletionDatesMap = (raw: any): { [key: number]: Date | null } => {
|
||||||
|
const out: { [key: number]: Date | null } = {};
|
||||||
|
if (!raw || typeof raw !== 'object') return out;
|
||||||
|
for (const [k, v] of Object.entries(raw)) {
|
||||||
|
const idx = Number(k);
|
||||||
|
if (!Number.isFinite(idx)) continue;
|
||||||
|
const parsed = parseBatchDateValue(v);
|
||||||
|
out[idx] = parsed;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBatchSimulationStateFromSnapshot = async (sourceDeliveryRecordId: string) => {
|
||||||
|
const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID);
|
||||||
|
const deliveryRecord: any = await deliveryTable.getRecordById(sourceDeliveryRecordId);
|
||||||
|
const fields = deliveryRecord?.fields || {};
|
||||||
|
const extractAllText = (val: any): string => {
|
||||||
|
if (val === null || val === undefined) return '';
|
||||||
|
if (typeof val === 'string') return val;
|
||||||
|
if (typeof val === 'number') return String(val);
|
||||||
|
if (Array.isArray(val)) return val.map((el: any) => extractAllText(el)).join('');
|
||||||
|
if (typeof val === 'object') return (val as any).text || (val as any).name || (val as any).value?.toString?.() || '';
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
const snapshotPart1 = extractAllText(fields?.[DELIVERY_SNAPSHOT_JSON_FIELD_ID]);
|
||||||
|
const snapshotPart2 = extractAllText(fields?.[DELIVERY_SNAPSHOT_JSON_2_FIELD_ID]);
|
||||||
|
const snapshotStr = `${snapshotPart1 || ''}${snapshotPart2 || ''}`.trim();
|
||||||
|
if (!snapshotStr) {
|
||||||
|
throw new Error('源货期记录缺少快照');
|
||||||
|
}
|
||||||
|
const snapshot = JSON.parse(snapshotStr);
|
||||||
|
if (!Array.isArray(snapshot?.timelineResults) || snapshot.timelineResults.length === 0) {
|
||||||
|
throw new Error('源快照没有可用的timelineResults');
|
||||||
|
}
|
||||||
|
const timelineResultsFromSnapshot = snapshot.timelineResults as any[];
|
||||||
|
const labelsFromSnapshot = normalizeSelectedLabelsForRestore(snapshot?.selectedLabels || {});
|
||||||
|
const expectedFromSnapshot =
|
||||||
|
parseBatchDateValue(snapshot?.expectedDateTimestamp) ||
|
||||||
|
parseBatchDateValue(snapshot?.expectedDateString) ||
|
||||||
|
parseBatchDateValue(fields?.[DELIVERY_CUSTOMER_EXPECTED_DATE_FIELD_ID]);
|
||||||
|
const startFromSnapshot =
|
||||||
|
parseBatchDateValue(snapshot?.startTimestamp) ||
|
||||||
|
parseBatchDateValue(snapshot?.startString) ||
|
||||||
|
parseBatchDateValue(fields?.[DELIVERY_START_TIME_FIELD_ID]);
|
||||||
|
const snapshotAdjustments = snapshot?.timelineAdjustments
|
||||||
|
? normalizeTimelineAdjustmentsFromSnapshot(snapshot.timelineAdjustments, timelineResultsFromSnapshot)
|
||||||
|
: deriveTimelineAdjustmentsFromResults(timelineResultsFromSnapshot);
|
||||||
|
const excludedDatesByNodeFromResults = buildExcludedDatesByNodeFromTimeline(timelineResultsFromSnapshot);
|
||||||
|
const excludedDatesByNodeFromSnapshotMap = normalizeExcludedDatesByNodeMap(
|
||||||
|
snapshot?.excludedDatesByNodeOverride ||
|
||||||
|
snapshot?.timelineCalculationState?.excludedDatesByNodeOverride ||
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const sourceRecordIdsText = extractText(fields?.[DELIVERY_RECORD_IDS_FIELD_ID]).trim();
|
||||||
|
return {
|
||||||
|
snapshot,
|
||||||
|
timelineResults: timelineResultsFromSnapshot,
|
||||||
|
selectedLabels: labelsFromSnapshot,
|
||||||
|
expectedDate: expectedFromSnapshot,
|
||||||
|
startTime: startFromSnapshot,
|
||||||
|
timelineAdjustments: snapshotAdjustments,
|
||||||
|
excludedDatesOverride: normalizeExcludedDatesOverride(snapshot?.excludedDatesOverride || []),
|
||||||
|
excludedDatesByNodeOverride: Object.keys(excludedDatesByNodeFromResults).length > 0
|
||||||
|
? excludedDatesByNodeFromResults
|
||||||
|
: excludedDatesByNodeFromSnapshotMap,
|
||||||
|
foreignId: snapshot?.foreignId || '',
|
||||||
|
styleText: snapshot?.styleText || '',
|
||||||
|
colorText: snapshot?.colorText || '',
|
||||||
|
text2: snapshot?.text2 || '',
|
||||||
|
versionNumber: Number.isFinite(Number(snapshot?.version)) ? Number(snapshot.version) : null,
|
||||||
|
actualCompletionDates: normalizeActualCompletionDatesMap(snapshot?.actualCompletionDates),
|
||||||
|
sourceRecordIdsText
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// 已移除:调整模式不再加载货期记录列表
|
// 已移除:调整模式不再加载货期记录列表
|
||||||
|
|
||||||
// 入口选择处理
|
// 入口选择处理
|
||||||
@ -648,6 +761,21 @@ export default function App() {
|
|||||||
return '#unknown';
|
return '#unknown';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getExcludedDatesNodeKeyCandidates = (node: any, indexFallback?: number): string[] => {
|
||||||
|
const nodeName = node?.nodeName;
|
||||||
|
const processOrder = node?.processOrder;
|
||||||
|
const instanceKey = node?.processGroupInstanceId || node?.processGroupInstanceName;
|
||||||
|
const candidates = [
|
||||||
|
buildExcludedDatesNodeKey(nodeName, processOrder, indexFallback, instanceKey),
|
||||||
|
buildExcludedDatesNodeKey(nodeName, processOrder, indexFallback),
|
||||||
|
buildExcludedDatesNodeKey(nodeName, undefined, indexFallback, instanceKey),
|
||||||
|
buildExcludedDatesNodeKey(nodeName, undefined, indexFallback),
|
||||||
|
]
|
||||||
|
.map(k => (k || '').trim())
|
||||||
|
.filter(k => k && k !== '#unknown');
|
||||||
|
return Array.from(new Set(candidates));
|
||||||
|
};
|
||||||
|
|
||||||
const buildTimelineAdjustmentKey = (node: any, indexFallback?: number) => {
|
const buildTimelineAdjustmentKey = (node: any, indexFallback?: number) => {
|
||||||
return buildExcludedDatesNodeKey(
|
return buildExcludedDatesNodeKey(
|
||||||
node?.nodeName,
|
node?.nodeName,
|
||||||
@ -832,16 +960,35 @@ export default function App() {
|
|||||||
|
|
||||||
const buildExcludedDatesByNodeFromTimeline = (results: any[]): Record<string, string[]> => {
|
const buildExcludedDatesByNodeFromTimeline = (results: any[]): Record<string, string[]> => {
|
||||||
const map: Record<string, string[]> = {};
|
const map: Record<string, string[]> = {};
|
||||||
|
const assignExcludedDates = (key: string, val: string[]) => {
|
||||||
|
if (!key) return;
|
||||||
|
const existing = Array.isArray(map[key]) ? map[key] : [];
|
||||||
|
if (existing.length > 0 && val.length === 0) return;
|
||||||
|
map[key] = val;
|
||||||
|
};
|
||||||
for (let i = 0; i < results.length; i++) {
|
for (let i = 0; i < results.length; i++) {
|
||||||
const r = results[i];
|
const r = results[i];
|
||||||
const key = buildExcludedDatesNodeKey(
|
const keyWithInstance = buildExcludedDatesNodeKey(
|
||||||
r?.nodeName,
|
r?.nodeName,
|
||||||
r?.processOrder,
|
r?.processOrder,
|
||||||
i,
|
i,
|
||||||
r?.processGroupInstanceId || r?.processGroupInstanceName
|
r?.processGroupInstanceId || r?.processGroupInstanceName
|
||||||
);
|
);
|
||||||
|
const keyWithoutInstance = buildExcludedDatesNodeKey(
|
||||||
|
r?.nodeName,
|
||||||
|
r?.processOrder,
|
||||||
|
i
|
||||||
|
);
|
||||||
|
const keyByName = buildExcludedDatesNodeKey(
|
||||||
|
r?.nodeName,
|
||||||
|
undefined,
|
||||||
|
i
|
||||||
|
);
|
||||||
const arr = Array.isArray(r?.excludedDates) ? r.excludedDates : [];
|
const arr = Array.isArray(r?.excludedDates) ? r.excludedDates : [];
|
||||||
map[key] = normalizeExcludedDatesOverride(arr);
|
const normalized = normalizeExcludedDatesOverride(arr);
|
||||||
|
assignExcludedDates(keyWithInstance, normalized);
|
||||||
|
assignExcludedDates(keyWithoutInstance, normalized);
|
||||||
|
assignExcludedDates(keyByName, normalized);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
};
|
};
|
||||||
@ -861,6 +1008,18 @@ export default function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const restoreExcludedDatesByNodeOverrideFromSnapshot = (snapshot: any, timelineResultsCandidate?: any[]) => {
|
const restoreExcludedDatesByNodeOverrideFromSnapshot = (snapshot: any, timelineResultsCandidate?: any[]) => {
|
||||||
|
const results = Array.isArray(timelineResultsCandidate)
|
||||||
|
? timelineResultsCandidate
|
||||||
|
: (Array.isArray(snapshot?.timelineResults) ? snapshot.timelineResults : []);
|
||||||
|
if (results.length > 0) {
|
||||||
|
const derived = buildExcludedDatesByNodeFromTimeline(results);
|
||||||
|
if (Object.keys(derived).length > 0) {
|
||||||
|
excludedDatesByNodeOverrideRef.current = derived;
|
||||||
|
setExcludedDatesByNodeOverride(derived);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const candidates: any[] = [
|
const candidates: any[] = [
|
||||||
snapshot?.excludedDatesByNodeOverride,
|
snapshot?.excludedDatesByNodeOverride,
|
||||||
snapshot?.excludedDatesByNode,
|
snapshot?.excludedDatesByNode,
|
||||||
@ -877,16 +1036,6 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = Array.isArray(timelineResultsCandidate)
|
|
||||||
? timelineResultsCandidate
|
|
||||||
: (Array.isArray(snapshot?.timelineResults) ? snapshot.timelineResults : []);
|
|
||||||
if (results.length > 0) {
|
|
||||||
const derived = buildExcludedDatesByNodeFromTimeline(results);
|
|
||||||
excludedDatesByNodeOverrideRef.current = derived;
|
|
||||||
setExcludedDatesByNodeOverride(derived);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
excludedDatesByNodeOverrideRef.current = {};
|
excludedDatesByNodeOverrideRef.current = {};
|
||||||
setExcludedDatesByNodeOverride({});
|
setExcludedDatesByNodeOverride({});
|
||||||
};
|
};
|
||||||
@ -2775,7 +2924,8 @@ export default function App() {
|
|||||||
selectedLabels?: {[key: string]: string | string[]},
|
selectedLabels?: {[key: string]: string | string[]},
|
||||||
expectedDate?: Date | null,
|
expectedDate?: Date | null,
|
||||||
startTime?: Date | null,
|
startTime?: Date | null,
|
||||||
excludedDates?: string[]
|
excludedDates?: string[],
|
||||||
|
prevResultsForAdjustmentRemap?: any[]
|
||||||
},
|
},
|
||||||
showUI: boolean = true // 新增参数控制是否显示UI
|
showUI: boolean = true // 新增参数控制是否显示UI
|
||||||
) => {
|
) => {
|
||||||
@ -2785,8 +2935,6 @@ 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 globalExcludedDates = normalizeExcludedDatesOverride(currentExcludedDates);
|
|
||||||
const excludedByNode = excludedDatesByNodeOverrideRef.current || {};
|
const excludedByNode = excludedDatesByNodeOverrideRef.current || {};
|
||||||
const isBackward = timelineDirection === 'backward';
|
const isBackward = timelineDirection === 'backward';
|
||||||
|
|
||||||
@ -3159,7 +3307,6 @@ export default function App() {
|
|||||||
selectedLabels: currentSelectedLabels,
|
selectedLabels: currentSelectedLabels,
|
||||||
expectedDate: currentExpectedDate,
|
expectedDate: currentExpectedDate,
|
||||||
startTime: currentStartTime,
|
startTime: currentStartTime,
|
||||||
excludedDates: currentExcludedDates,
|
|
||||||
},
|
},
|
||||||
showUI,
|
showUI,
|
||||||
};
|
};
|
||||||
@ -3269,20 +3416,20 @@ 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 nodeKey = buildExcludedDatesNodeKey(
|
const keyCandidates = getExcludedDatesNodeKeyCandidates(processNode, i);
|
||||||
processNode?.nodeName,
|
let overrideList: string[] | undefined = undefined;
|
||||||
processNode?.processOrder,
|
for (const candidateKey of keyCandidates) {
|
||||||
i,
|
if (Object.prototype.hasOwnProperty.call(excludedByNode, candidateKey)) {
|
||||||
processNode?.processGroupInstanceId || processNode?.processGroupInstanceName
|
overrideList = excludedByNode[candidateKey];
|
||||||
);
|
break;
|
||||||
const overrideList = Object.prototype.hasOwnProperty.call(excludedByNode, nodeKey)
|
}
|
||||||
? excludedByNode[nodeKey]
|
}
|
||||||
: undefined;
|
|
||||||
const baseList = Array.isArray(processNode?.excludedDates) ? processNode.excludedDates : [];
|
const baseList = Array.isArray(processNode?.excludedDates) ? processNode.excludedDates : [];
|
||||||
const selectedList = Array.isArray(overrideList) ? overrideList : baseList;
|
const oldExcludedDates = normalizeExcludedDatesOverride(Array.isArray(overrideList) ? overrideList : []);
|
||||||
|
const newRuleExcludedDates = normalizeExcludedDatesOverride(baseList);
|
||||||
const nodeExcludedDates = Array.from(new Set([
|
const nodeExcludedDates = Array.from(new Set([
|
||||||
...normalizeExcludedDatesOverride(selectedList),
|
...oldExcludedDates,
|
||||||
...globalExcludedDates,
|
...newRuleExcludedDates,
|
||||||
])).filter(Boolean);
|
])).filter(Boolean);
|
||||||
let timelineValue = null;
|
let timelineValue = null;
|
||||||
let matchedTimelineRecord = null;
|
let matchedTimelineRecord = null;
|
||||||
@ -3718,7 +3865,10 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextAdjustments = remapTimelineAdjustmentsToNewResults(timelineResults, results);
|
const prevResultsForRemap = Array.isArray(overrideData?.prevResultsForAdjustmentRemap)
|
||||||
|
? overrideData!.prevResultsForAdjustmentRemap!
|
||||||
|
: timelineResults;
|
||||||
|
const nextAdjustments = remapTimelineAdjustmentsToNewResults(prevResultsForRemap, results);
|
||||||
setTimelineAdjustments(nextAdjustments);
|
setTimelineAdjustments(nextAdjustments);
|
||||||
pendingRecalculateAfterCalculateAdjustmentsRef.current = nextAdjustments;
|
pendingRecalculateAfterCalculateAdjustmentsRef.current = nextAdjustments;
|
||||||
pendingRecalculateAfterCalculateRef.current = true;
|
pendingRecalculateAfterCalculateRef.current = true;
|
||||||
@ -3797,10 +3947,17 @@ export default function App() {
|
|||||||
// 获取重新计算后的时间线结果(不更新状态,逻辑对齐页面的重算口径)
|
// 获取重新计算后的时间线结果(不更新状态,逻辑对齐页面的重算口径)
|
||||||
const getRecalculatedTimeline = (
|
const getRecalculatedTimeline = (
|
||||||
adjustments: Record<string, number>,
|
adjustments: Record<string, number>,
|
||||||
opts?: { ignoreActualCompletionDates?: boolean; actualCompletionDatesOverride?: { [key: number]: Date | null } }
|
opts?: {
|
||||||
|
ignoreActualCompletionDates?: boolean;
|
||||||
|
actualCompletionDatesOverride?: { [key: number]: Date | null };
|
||||||
|
baseResultsOverride?: any[];
|
||||||
|
startTimeOverride?: Date | null;
|
||||||
|
}
|
||||||
) => {
|
) => {
|
||||||
const updatedResults = [...timelineResults];
|
const baseResults = Array.isArray(opts?.baseResultsOverride) ? opts!.baseResultsOverride! : timelineResults;
|
||||||
let cumulativeStartTime = startTime ? new Date(startTime) : new Date(); // 从起始时间开始
|
const updatedResults = [...baseResults];
|
||||||
|
const baseStartTime = opts?.startTimeOverride ?? startTime;
|
||||||
|
let cumulativeStartTime = baseStartTime ? new Date(baseStartTime) : new Date(); // 从起始时间开始
|
||||||
|
|
||||||
for (let i = 0; i < updatedResults.length; i++) {
|
for (let i = 0; i < updatedResults.length; i++) {
|
||||||
const result = updatedResults[i];
|
const result = updatedResults[i];
|
||||||
@ -4530,7 +4687,7 @@ export default function App() {
|
|||||||
timelineResults: any[],
|
timelineResults: any[],
|
||||||
processRecordIds: string[],
|
processRecordIds: string[],
|
||||||
timelineAdjustments: Record<string, number> = {},
|
timelineAdjustments: Record<string, number> = {},
|
||||||
overrides?: { foreignId?: string; style?: string; color?: string; expectedDate?: Date | null; startTime?: Date | null; selectedLabels?: {[key: string]: string | string[]}; baseBufferDays?: number }
|
overrides?: { foreignId?: string; style?: string; color?: string; expectedDate?: Date | null; startTime?: Date | null; selectedLabels?: {[key: string]: string | string[]}; baseBufferDays?: number; recordIdsTextOverride?: string; baseVersionNumber?: number | null; forceMode?: 'generate' | 'adjust' | null }
|
||||||
) => {
|
) => {
|
||||||
let recordCells: any[] | undefined;
|
let recordCells: any[] | undefined;
|
||||||
try {
|
try {
|
||||||
@ -4722,10 +4879,15 @@ export default function App() {
|
|||||||
|
|
||||||
// 创建当前时间戳
|
// 创建当前时间戳
|
||||||
const currentTime = new Date().getTime();
|
const currentTime = new Date().getTime();
|
||||||
|
const effectiveMode = overrides?.forceMode ?? mode;
|
||||||
|
|
||||||
// 计算版本号(数字)并格式化货期调整信息
|
// 计算版本号(数字)并格式化货期调整信息
|
||||||
let versionNumber = 1;
|
let versionNumber = 1;
|
||||||
if (mode === 'adjust' && currentVersionNumber !== null) {
|
const overrideBaseVersion = Number(overrides?.baseVersionNumber);
|
||||||
|
const hasOverrideBaseVersion = Number.isFinite(overrideBaseVersion);
|
||||||
|
if (hasOverrideBaseVersion) {
|
||||||
|
versionNumber = overrideBaseVersion + 1;
|
||||||
|
} else if (effectiveMode === 'adjust' && currentVersionNumber !== null) {
|
||||||
versionNumber = currentVersionNumber + 1;
|
versionNumber = currentVersionNumber + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4779,7 +4941,7 @@ export default function App() {
|
|||||||
styleText,
|
styleText,
|
||||||
colorText,
|
colorText,
|
||||||
text2,
|
text2,
|
||||||
mode,
|
mode: effectiveMode,
|
||||||
timelineDirection,
|
timelineDirection,
|
||||||
excludedDatesOverride,
|
excludedDatesOverride,
|
||||||
excludedDatesByNodeOverride: excludedDatesByNodeOverrideRef.current || excludedDatesByNodeOverride,
|
excludedDatesByNodeOverride: excludedDatesByNodeOverrideRef.current || excludedDatesByNodeOverride,
|
||||||
@ -4912,9 +5074,11 @@ export default function App() {
|
|||||||
// 使用createCell方法创建各个字段的Cell
|
// 使用createCell方法创建各个字段的Cell
|
||||||
const startTimestamp = currentStartTime ? currentStartTime.getTime() : currentTime;
|
const startTimestamp = currentStartTime ? currentStartTime.getTime() : currentTime;
|
||||||
|
|
||||||
const recordIdsText = (restoredRecordIdsText && restoredRecordIdsText.trim() !== '')
|
const recordIdsText = (overrides?.recordIdsTextOverride && overrides.recordIdsTextOverride.trim() !== '')
|
||||||
|
? overrides.recordIdsTextOverride.trim()
|
||||||
|
: ((restoredRecordIdsText && restoredRecordIdsText.trim() !== '')
|
||||||
? restoredRecordIdsText.trim()
|
? restoredRecordIdsText.trim()
|
||||||
: '';
|
: '');
|
||||||
|
|
||||||
const [
|
const [
|
||||||
foreignIdCell,
|
foreignIdCell,
|
||||||
@ -5470,30 +5634,13 @@ export default function App() {
|
|||||||
const styleText = getText('styleText');
|
const styleText = getText('styleText');
|
||||||
const colorText = getText('colorText');
|
const colorText = getText('colorText');
|
||||||
const rawStart = f[nameToId.get('startTimestamp') || ''];
|
const rawStart = f[nameToId.get('startTimestamp') || ''];
|
||||||
const rawExpected = f[nameToId.get('expectedDateTimestamp') || ''];
|
const rawExpected =
|
||||||
|
f[BATCH_CUSTOMER_EXPECTED_DATE_FIELD_ID] ??
|
||||||
|
f[nameToId.get('customerExpectedDate') || ''] ??
|
||||||
|
f[nameToId.get('expectedDateTimestamp') || ''];
|
||||||
const rawBufferDays = f[nameToId.get('缓冲期') || BATCH_BUFFER_FIELD_ID || ''];
|
const rawBufferDays = f[nameToId.get('缓冲期') || BATCH_BUFFER_FIELD_ID || ''];
|
||||||
let startDate: Date | null = null;
|
let startDate: Date | null = parseBatchDateValue(rawStart);
|
||||||
let expectedDateObj: Date | null = null;
|
let expectedDateObj: Date | null = parseBatchDateValue(rawExpected);
|
||||||
if (typeof rawStart === 'number') startDate = new Date(rawStart);
|
|
||||||
else if (Array.isArray(rawStart) && rawStart.length > 0) {
|
|
||||||
const item = rawStart[0];
|
|
||||||
if (typeof item === 'number') startDate = new Date(item);
|
|
||||||
else startDate = parseDate(extractText(rawStart));
|
|
||||||
} else if (typeof rawStart === 'string') startDate = parseDate(rawStart);
|
|
||||||
else if (rawStart && typeof rawStart === 'object') {
|
|
||||||
const v = (rawStart as any).value || (rawStart as any).text || (rawStart as any).name || '';
|
|
||||||
if (typeof v === 'number') startDate = new Date(v); else startDate = parseDate(v);
|
|
||||||
}
|
|
||||||
if (typeof rawExpected === 'number') expectedDateObj = new Date(rawExpected);
|
|
||||||
else if (Array.isArray(rawExpected) && rawExpected.length > 0) {
|
|
||||||
const item = rawExpected[0];
|
|
||||||
if (typeof item === 'number') expectedDateObj = new Date(item);
|
|
||||||
else expectedDateObj = parseDate(extractText(rawExpected));
|
|
||||||
} else if (typeof rawExpected === 'string') expectedDateObj = parseDate(rawExpected);
|
|
||||||
else if (rawExpected && typeof rawExpected === 'object') {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseBufferDays = (raw: any): number | null => {
|
const parseBufferDays = (raw: any): number | null => {
|
||||||
if (raw == null) return null;
|
if (raw == null) return null;
|
||||||
@ -5516,45 +5663,6 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
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 [];
|
||||||
@ -5580,32 +5688,96 @@ export default function App() {
|
|||||||
else labels[key] = list.join(',');
|
else labels[key] = list.join(',');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
const sourceDeliveryRecordId = extractText(
|
||||||
|
f[BATCH_SOURCE_DELIVERY_RECORD_ID_FIELD_ID] ??
|
||||||
|
f[nameToId.get('sourceDeliveryRecordId') || '']
|
||||||
|
).trim();
|
||||||
|
setBatchCurrentRowInfo({ index: displayIndex, foreignId: foreignId || '', style: styleText || '', color: colorText || '' });
|
||||||
|
try {
|
||||||
|
let labelsToUse: { [key: string]: string | string[] } = labels;
|
||||||
|
let expectedDateToUse: Date | null = expectedDateObj || null;
|
||||||
|
let startTimeToUse: Date | null = startDate || null;
|
||||||
|
let foreignIdToUse = foreignId || '';
|
||||||
|
let styleToUse = styleText || '';
|
||||||
|
let colorToUse = colorText || '';
|
||||||
|
let adjustmentsToUse: Record<string, number> = {};
|
||||||
|
let prevResultsForRemap: any[] | undefined = undefined;
|
||||||
|
let sourceRecordIdsText = '';
|
||||||
|
let actualCompletionDatesForWrite: { [key: number]: Date | null } = {};
|
||||||
|
let baseVersionNumberToUse: number | null = null;
|
||||||
|
|
||||||
|
if (batchMode === 'adjust') {
|
||||||
|
if (!sourceDeliveryRecordId) {
|
||||||
|
throw new Error('缺少sourceDeliveryRecordId');
|
||||||
|
}
|
||||||
|
const simulation = await getBatchSimulationStateFromSnapshot(sourceDeliveryRecordId);
|
||||||
|
setMode('adjust');
|
||||||
|
setTimelineResults(simulation.timelineResults);
|
||||||
|
setTimelineAdjustments(simulation.timelineAdjustments || {});
|
||||||
|
setSelectedLabels(simulation.selectedLabels || {});
|
||||||
|
setExpectedDate(simulation.expectedDate || null);
|
||||||
|
setStartTime(simulation.startTime || null);
|
||||||
|
setCurrentForeignId(simulation.foreignId || '');
|
||||||
|
setCurrentStyleText(simulation.styleText || '');
|
||||||
|
setCurrentColorText(simulation.colorText || '');
|
||||||
|
setCurrentText2(simulation.text2 || '');
|
||||||
|
setCurrentVersionNumber(simulation.versionNumber);
|
||||||
|
setActualCompletionDates(simulation.actualCompletionDates || {});
|
||||||
|
setExcludedDatesOverride(simulation.excludedDatesOverride || []);
|
||||||
|
excludedDatesByNodeOverrideRef.current = simulation.excludedDatesByNodeOverride || {};
|
||||||
|
setExcludedDatesByNodeOverride(simulation.excludedDatesByNodeOverride || {});
|
||||||
|
pendingRecalculateAfterCalculateRef.current = false;
|
||||||
|
pendingRecalculateAfterCalculateAdjustmentsRef.current = null;
|
||||||
|
|
||||||
|
labelsToUse = { ...(simulation.selectedLabels || {}) };
|
||||||
|
for (const [k, v] of Object.entries(labels || {})) {
|
||||||
|
if (Array.isArray(v) && v.length > 0) labelsToUse[k] = v;
|
||||||
|
if (typeof v === 'string' && v.trim() !== '') labelsToUse[k] = v.trim();
|
||||||
|
}
|
||||||
|
expectedDateToUse = expectedDateObj || simulation.expectedDate || null;
|
||||||
|
startTimeToUse = simulation.startTime || null;
|
||||||
|
foreignIdToUse = simulation.foreignId || foreignIdToUse;
|
||||||
|
styleToUse = simulation.styleText || styleToUse;
|
||||||
|
colorToUse = simulation.colorText || colorToUse;
|
||||||
|
adjustmentsToUse = simulation.timelineAdjustments || {};
|
||||||
|
prevResultsForRemap = simulation.timelineResults || [];
|
||||||
|
sourceRecordIdsText = simulation.sourceRecordIdsText || '';
|
||||||
|
actualCompletionDatesForWrite = simulation.actualCompletionDates || {};
|
||||||
|
baseVersionNumberToUse = Number.isFinite(Number(simulation.versionNumber))
|
||||||
|
? Number(simulation.versionNumber)
|
||||||
|
: null;
|
||||||
|
} else {
|
||||||
const requiredLabelKeys = Array.from({ length: 12 }, (_, k) => `标签${k + 1}`);
|
const requiredLabelKeys = Array.from({ length: 12 }, (_, k) => `标签${k + 1}`);
|
||||||
const missing = requiredLabelKeys.filter(k => {
|
const missing = requiredLabelKeys.filter(k => {
|
||||||
const val = (labels as any)[k];
|
const val = (labelsToUse as any)[k];
|
||||||
if (Array.isArray(val)) return val.length === 0;
|
if (Array.isArray(val)) return val.length === 0;
|
||||||
return !(typeof val === 'string' && val.trim().length > 0);
|
return !(typeof val === 'string' && val.trim().length > 0);
|
||||||
});
|
});
|
||||||
if (missing.length > 0) {
|
if (missing.length > 0) {
|
||||||
setBatchProcessedCount(p => p + 1);
|
throw new Error(`标签不完整:${missing.join('、')}`);
|
||||||
setBatchFailureCount(fCount => fCount + 1);
|
|
||||||
setBatchProgressList(list => [...list, { index: displayIndex, foreignId: foreignId || '', status: 'failed', message: `标签不完整:${missing.join('、')}` }]);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
setCurrentForeignId(foreignIdToUse);
|
||||||
|
setCurrentStyleText(styleToUse);
|
||||||
|
setCurrentColorText(colorToUse);
|
||||||
|
setExpectedDate(expectedDateToUse);
|
||||||
|
setStartTime(startTimeToUse);
|
||||||
|
setSelectedLabels(labelsToUse);
|
||||||
}
|
}
|
||||||
setBatchCurrentRowInfo({ index: displayIndex, foreignId: foreignId || '', style: styleText || '', color: colorText || '' });
|
|
||||||
setCurrentForeignId(foreignId || '');
|
const results = await handleCalculateTimeline(
|
||||||
setCurrentStyleText(styleText || '');
|
true,
|
||||||
setCurrentColorText(colorText || '');
|
{
|
||||||
setExpectedDate(expectedDateObj || null);
|
selectedLabels: labelsToUse,
|
||||||
setStartTime(startDate || null);
|
expectedDate: expectedDateToUse,
|
||||||
setSelectedLabels(labels);
|
startTime: startTimeToUse,
|
||||||
try {
|
prevResultsForAdjustmentRemap: prevResultsForRemap
|
||||||
const results = await handleCalculateTimeline(true, { selectedLabels: labels, expectedDate: expectedDateObj || null, startTime: startDate || null, excludedDates: batchExcludedDates }, false);
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
if (results && results.length > 0) {
|
if (results && results.length > 0) {
|
||||||
let effectiveExpectedDate = expectedDateObj || null;
|
let resultsToWrite = results;
|
||||||
if (!effectiveExpectedDate) {
|
let effectiveExpectedDate = expectedDateToUse;
|
||||||
|
if (!effectiveExpectedDate && batchMode === 'generate') {
|
||||||
const bufferDays = parseBufferDays(rawBufferDays);
|
const bufferDays = parseBufferDays(rawBufferDays);
|
||||||
const fallbackDays = Number.isFinite(bufferDays as number) ? (bufferDays as number) : 14;
|
const fallbackDays = Number.isFinite(bufferDays as number) ? (bufferDays as number) : 14;
|
||||||
const autoExpected = computeExpectedDateByBufferDays(results, fallbackDays, completionDateAdjustment);
|
const autoExpected = computeExpectedDateByBufferDays(results, fallbackDays, completionDateAdjustment);
|
||||||
@ -5614,25 +5786,43 @@ export default function App() {
|
|||||||
setExpectedDate(autoExpected);
|
setExpectedDate(autoExpected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const processRecordIds = await writeToProcessDataTable(results, { foreignId, style: styleText, color: colorText });
|
if (batchMode === 'adjust' && Array.isArray(prevResultsForRemap) && prevResultsForRemap.length > 0) {
|
||||||
|
adjustmentsToUse = remapTimelineAdjustmentsToNewResults(prevResultsForRemap, results);
|
||||||
|
resultsToWrite = getRecalculatedTimeline(adjustmentsToUse, {
|
||||||
|
actualCompletionDatesOverride: actualCompletionDatesForWrite,
|
||||||
|
baseResultsOverride: results,
|
||||||
|
startTimeOverride: startTimeToUse
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
adjustmentsToUse = {};
|
||||||
|
}
|
||||||
|
const processRecordIds = await writeToProcessDataTable(resultsToWrite, { foreignId: foreignIdToUse, style: styleToUse, color: colorToUse });
|
||||||
const deliveryRecordId = await writeToDeliveryRecordTable(
|
const deliveryRecordId = await writeToDeliveryRecordTable(
|
||||||
results,
|
resultsToWrite,
|
||||||
processRecordIds,
|
processRecordIds,
|
||||||
{},
|
adjustmentsToUse,
|
||||||
{
|
{
|
||||||
foreignId,
|
foreignId: foreignIdToUse,
|
||||||
style: styleText,
|
style: styleToUse,
|
||||||
color: colorText,
|
color: colorToUse,
|
||||||
expectedDate: effectiveExpectedDate,
|
expectedDate: effectiveExpectedDate,
|
||||||
startTime: startDate || null,
|
startTime: startTimeToUse,
|
||||||
selectedLabels: labels
|
selectedLabels: labelsToUse,
|
||||||
|
recordIdsTextOverride: sourceRecordIdsText,
|
||||||
|
baseVersionNumber: baseVersionNumberToUse,
|
||||||
|
forceMode: batchMode === 'adjust' ? 'adjust' : 'generate'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
const candidateNames = ['状态','record_id','记录ID','货期记录ID','deliveryRecordId'];
|
const candidateNames = ['resultDeliveryRecordId', '状态', 'record_id', '记录ID', '货期记录ID', 'deliveryRecordId'];
|
||||||
let statusFieldId = '';
|
let statusFieldId = BATCH_RESULT_DELIVERY_RECORD_ID_FIELD_ID;
|
||||||
for (const nm of candidateNames) { const id = nameToId.get(nm) || ''; if (id) { statusFieldId = id; break; } }
|
for (const nm of candidateNames) {
|
||||||
if (!statusFieldId) statusFieldId = 'fldKTpPL9s';
|
const id = nameToId.get(nm) || '';
|
||||||
|
if (id) {
|
||||||
|
statusFieldId = id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
const rowRecordId = (row.id || (row as any).recordId || (row as any)._id || (row as any).record_id);
|
const rowRecordId = (row.id || (row as any).recordId || (row as any)._id || (row as any).record_id);
|
||||||
const deliveryRecordIdStr = typeof deliveryRecordId === 'string'
|
const deliveryRecordIdStr = typeof deliveryRecordId === 'string'
|
||||||
? deliveryRecordId
|
? deliveryRecordId
|
||||||
@ -5658,13 +5848,13 @@ export default function App() {
|
|||||||
throw e2;
|
throw e2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setBatchProgressList(list => [...list, { index: displayIndex, foreignId: foreignId || '', status: 'success', message: `记录ID: ${deliveryRecordIdStr}` }]);
|
setBatchProgressList(list => [...list, { index: displayIndex, foreignId: foreignIdToUse || '', status: 'success', message: `记录ID: ${deliveryRecordIdStr}` }]);
|
||||||
} else {
|
} else {
|
||||||
setBatchProgressList(list => [...list, { index: displayIndex, foreignId: foreignId || '', status: 'failed', message: '未找到状态字段或记录ID为空' }]);
|
setBatchProgressList(list => [...list, { index: displayIndex, foreignId: foreignIdToUse || '', status: 'failed', message: '未找到结果字段或记录ID为空' }]);
|
||||||
}
|
}
|
||||||
} catch (statusErr: any) {
|
} catch (statusErr: any) {
|
||||||
console.warn('回写批量状态字段失败', statusErr);
|
console.warn('回写批量状态字段失败', statusErr);
|
||||||
setBatchProgressList(list => [...list, { index: displayIndex, foreignId: foreignId || '', status: 'failed', message: `状态写入失败: ${statusErr?.message || '未知错误'}` }]);
|
setBatchProgressList(list => [...list, { index: displayIndex, foreignId: foreignIdToUse || '', status: 'failed', message: `结果回写失败: ${statusErr?.message || '未知错误'}` }]);
|
||||||
}
|
}
|
||||||
processed++;
|
processed++;
|
||||||
setBatchProcessedCount(p => p + 1);
|
setBatchProcessedCount(p => p + 1);
|
||||||
@ -5682,7 +5872,8 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
if (bitable.ui.showToast) {
|
if (bitable.ui.showToast) {
|
||||||
const aborted = batchAbortRef.current;
|
const aborted = batchAbortRef.current;
|
||||||
await bitable.ui.showToast({ toastType: ToastType.success, message: aborted ? `批量已中止,已处理 ${processed} 条记录` : `批量生成完成,共处理 ${processed} 条记录` });
|
const finishedText = batchMode === 'adjust' ? '批量调整完成' : '批量生成完成';
|
||||||
|
await bitable.ui.showToast({ toastType: ToastType.success, message: aborted ? `批量已中止,已处理 ${processed} 条记录` : `${finishedText},共处理 ${processed} 条记录` });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('批量处理失败:', error);
|
console.error('批量处理失败:', error);
|
||||||
@ -6329,8 +6520,8 @@ const omsVersionColumns = [
|
|||||||
background: 'linear-gradient(180deg, #fff, #f9fbff)'
|
background: 'linear-gradient(180deg, #fff, #f9fbff)'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Title heading={3} style={{ marginBottom: 8 }}>批量生成</Title>
|
<Title heading={3} style={{ marginBottom: 8 }}>批量生成/调整</Title>
|
||||||
<Text type='tertiary'>从批量生成数据表读取并写入记录</Text>
|
<Text type='tertiary'>按批量表执行新建记录或基于快照批量调整</Text>
|
||||||
<div style={{ marginTop: 16 }}>
|
<div style={{ marginTop: 16 }}>
|
||||||
<Button type='primary' theme='solid' size='large' style={{ width: '100%' }} onClick={openBatchModal}>进入</Button>
|
<Button type='primary' theme='solid' size='large' style={{ width: '100%' }} onClick={openBatchModal}>进入</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -6339,7 +6530,7 @@ const omsVersionColumns = [
|
|||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title="批量生成"
|
title={batchMode === 'adjust' ? '批量调整' : '批量生成'}
|
||||||
visible={batchModalVisible}
|
visible={batchModalVisible}
|
||||||
onCancel={async () => { batchAbortRef.current = true; setBatchModalVisible(false); }}
|
onCancel={async () => { batchAbortRef.current = true; setBatchModalVisible(false); }}
|
||||||
footer={null}
|
footer={null}
|
||||||
@ -6348,13 +6539,24 @@ const omsVersionColumns = [
|
|||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
<Card style={{ padding: 12 }}>
|
<Card style={{ padding: 12 }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||||||
|
<Text>模式</Text>
|
||||||
|
<Select
|
||||||
|
value={batchMode}
|
||||||
|
onChange={(v) => setBatchMode(v as 'generate' | 'adjust')}
|
||||||
|
disabled={batchLoading}
|
||||||
|
style={{ width: 140 }}
|
||||||
|
optionList={[
|
||||||
|
{ label: '批量生成', value: 'generate' },
|
||||||
|
{ label: '批量调整', value: 'adjust' }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
<Text>起始行</Text>
|
<Text>起始行</Text>
|
||||||
<InputNumber min={1} value={batchStartRow} onChange={(v) => setBatchStartRow(typeof v === 'number' ? v : 1)} style={{ width: 120 }} disabled={batchLoading} />
|
<InputNumber min={1} value={batchStartRow} onChange={(v) => setBatchStartRow(typeof v === 'number' ? v : 1)} style={{ width: 120 }} disabled={batchLoading} />
|
||||||
<Text>结束行</Text>
|
<Text>结束行</Text>
|
||||||
<InputNumber min={1} value={batchEndRow} onChange={(v) => setBatchEndRow(typeof v === 'number' ? v : 1)} style={{ width: 120 }} disabled={batchLoading} />
|
<InputNumber min={1} value={batchEndRow} onChange={(v) => setBatchEndRow(typeof v === 'number' ? v : 1)} style={{ width: 120 }} disabled={batchLoading} />
|
||||||
<Text type='tertiary'>总行数:{batchTotalRows}</Text>
|
<Text type='tertiary'>总行数:{batchTotalRows}</Text>
|
||||||
<Space>
|
<Space>
|
||||||
<Button loading={batchLoading} type='primary' theme='solid' onClick={() => handleBatchProcess({ start: batchStartRow, end: batchEndRow })}>开始</Button>
|
<Button loading={batchLoading} type='primary' theme='solid' onClick={() => handleBatchProcess({ start: batchStartRow, end: batchEndRow })}>{batchMode === 'adjust' ? '开始调整' : '开始生成'}</Button>
|
||||||
<Button type='danger' onClick={() => { batchAbortRef.current = true; }} disabled={!batchLoading}>中止</Button>
|
<Button type='danger' onClick={() => { batchAbortRef.current = true; }} disabled={!batchLoading}>中止</Button>
|
||||||
<Button onClick={() => setBatchModalVisible(false)} disabled={batchLoading}>关闭</Button>
|
<Button onClick={() => setBatchModalVisible(false)} disabled={batchLoading}>关闭</Button>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
Reference in New Issue
Block a user