From 76f946c248ae1fcf4c2ecefb2b6220979b9d73b9 Mon Sep 17 00:00:00 2001 From: mairuiming Date: Wed, 17 Dec 2025 16:14:36 +0800 Subject: [PATCH] 5 5 --- src/App.tsx | 750 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 565 insertions(+), 185 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1261105..c09acd9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { Button, Typography, List, Card, Space, Divider, Spin, Table, Select, Mo import { useState, useEffect, useRef } from 'react'; import { addDays, format } from 'date-fns'; import { zhCN } from 'date-fns/locale'; +import { executePricingQuery, executeSecondaryProcessQuery, executePricingDetailsQuery } from './services/apiService'; const { Title, Text } = Typography; @@ -36,7 +37,12 @@ export default function App() { const [selectedRecords, setSelectedRecords] = useState([]); const [recordDetails, setRecordDetails] = useState([]); const [loading, setLoading] = useState(false); - + const [queryResults, setQueryResults] = useState([]); + const [queryLoading, setQueryLoading] = useState(false); + const [secondaryProcessResults, setSecondaryProcessResults] = useState([]); + const [secondaryProcessLoading, setSecondaryProcessLoading] = useState(false); + const [pricingDetailsResults, setPricingDetailsResults] = useState([]); + const [pricingDetailsLoading, setPricingDetailsLoading] = useState(false); // 标签相关状态 const [labelOptions, setLabelOptions] = useState<{[key: string]: any[]}>({}); @@ -88,8 +94,6 @@ export default function App() { const [batchProgressList, setBatchProgressList] = useState<{ index: number; foreignId: string; status: 'success' | 'failed'; message?: string }[]>([]); const [batchCurrentRowInfo, setBatchCurrentRowInfo] = useState<{ index: number; foreignId: string; style: string; color: string } | null>(null); const batchAbortRef = useRef(false); - const [lastSavedDeliveryRecordId, setLastSavedDeliveryRecordId] = useState(null); - const [lastSavedDeliveryVersion, setLastSavedDeliveryVersion] = useState(null); // 删除未使用的 deliveryRecords 状态 const [selectedDeliveryRecordId, setSelectedDeliveryRecordId] = useState(''); // 从货期记录读取到的record_ids(用于保存回写) @@ -103,6 +107,9 @@ export default function App() { const resetGlobalState = (opts?: { resetMode?: boolean }) => { // 运行时加载状态 setLoading(false); + setQueryLoading(false); + setSecondaryProcessLoading(false); + setPricingDetailsLoading(false); setLabelLoading(false); setAdjustLoading(false); setTimelineLoading(false); @@ -216,6 +223,26 @@ export default function App() { // 新表ID(批量生成表) const BATCH_TABLE_ID = 'tblXO7iSxBYxrqtY'; + const fetchAllRecordsByPage = async (table: any, params?: any) => { + let token: any = undefined; + let all: any[] = []; + for (let i = 0; i < 10000; i++) { + const res: any = await table.getRecordsByPage({ pageSize: Math.min(200, (params && params.pageSize) ? params.pageSize : 200), pageToken: token, ...(params || {}) }); + const recs: any[] = Array.isArray(res?.records) ? res.records : []; + all = all.concat(recs); + const nextToken = res?.pageToken; + const hm = !!res?.hasMore; + token = nextToken; + if (!hm && !nextToken) break; + } + return all; + }; + + const getRecordTotalByPage = async (table: any, params?: any) => { + const res: any = await table.getRecordIdListByPage({ pageSize: 1, ...(params || {}) }); + return res?.total || 0; + }; + // 已移除:调整模式不再加载货期记录列表 // 入口选择处理 @@ -229,8 +256,7 @@ export default function App() { const openBatchModal = async () => { try { const batchTable = await bitable.base.getTable(BATCH_TABLE_ID); - const res = await batchTable.getRecords({ pageSize: 5000 }); - const total = res.records?.length || 0; + const total = await getRecordTotalByPage(batchTable); setBatchTotalRows(total); setBatchStartRow(1); setBatchEndRow(total > 0 ? total : 1); @@ -258,11 +284,15 @@ export default function App() { setTimelineLoading(true); try { const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID); - const deliveryRecord = await deliveryTable.getRecordById(deliveryRecordId); - const nodeDetailsVal = deliveryRecord?.fields?.[DELIVERY_NODE_DETAILS_FIELD_ID]; + const nodeDetailsField: any = await deliveryTable.getField(DELIVERY_NODE_DETAILS_FIELD_ID); + const recordIdsField: any = await deliveryTable.getField(DELIVERY_RECORD_IDS_FIELD_ID); + const snapshotField: any = await deliveryTable.getField(DELIVERY_SNAPSHOT_JSON_FIELD_ID); + const startTimeField: any = await deliveryTable.getField(DELIVERY_START_TIME_FIELD_ID); + + const nodeDetailsVal = await nodeDetailsField.getValue(deliveryRecordId); // 读取record_ids文本字段并保留原始文本(用于原样写回) try { - const recordIdsTextVal = deliveryRecord?.fields?.[DELIVERY_RECORD_IDS_FIELD_ID]; + const recordIdsTextVal = await recordIdsField.getValue(deliveryRecordId); const raw = extractText(recordIdsTextVal); if (raw && raw.trim() !== '') { setRestoredRecordIdsText(raw.trim()); @@ -284,7 +314,7 @@ export default function App() { // 优先使用货期记录表中的快照字段进行一键还原(新方案) try { - const deliverySnapVal = deliveryRecord?.fields?.[DELIVERY_SNAPSHOT_JSON_FIELD_ID]; + const deliverySnapVal = await snapshotField.getValue(deliveryRecordId); let deliverySnapStr: string | null = null; if (typeof deliverySnapVal === 'string') { deliverySnapStr = deliverySnapVal; @@ -357,7 +387,7 @@ export default function App() { } if (!startTimeRestored) { - const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID]; + const startTimeValue = await startTimeField.getValue(deliveryRecordId); if (startTimeValue) { let extractedStartTime: Date | null = null; if (typeof startTimeValue === 'number') { @@ -427,6 +457,120 @@ export default function App() { } const processTable = await bitable.base.getTable(PROCESS_DATA_TABLE_ID); + try { + const processSnapshotField: any = await processTable.getField(PROCESS_SNAPSHOT_JSON_FIELD_ID); + let snapStr: string | null = null; + for (const id of recordIds) { + const snapVal = await processSnapshotField.getValue(id); + const candidate = extractText(snapVal); + if (candidate && candidate.trim() !== '') { + snapStr = candidate; + break; + } + } + if (snapStr && snapStr.trim() !== '') { + const snapshot = JSON.parse(snapStr); + if (Array.isArray(snapshot.timelineResults)) { + setIsRestoringSnapshot(true); + + if (snapshot.selectedLabels) setSelectedLabels(snapshot.selectedLabels); + if (!mode && snapshot.mode) setMode(snapshot.mode); + if (snapshot.foreignId) setCurrentForeignId(snapshot.foreignId); + if (snapshot.styleText) setCurrentStyleText(snapshot.styleText); + if (snapshot.colorText) setCurrentColorText(snapshot.colorText); + if (snapshot.text2) setCurrentText2(snapshot.text2); + + if (snapshot.generationModeState) { + const genState = snapshot.generationModeState; + if (genState.currentForeignId) setCurrentForeignId(genState.currentForeignId); + if (genState.currentStyleText) setCurrentStyleText(genState.currentStyleText); + if (genState.currentColorText) setCurrentColorText(genState.currentColorText); + if (genState.currentText2) setCurrentText2(genState.currentText2); + if (genState.currentVersionNumber !== undefined) setCurrentVersionNumber(genState.currentVersionNumber); + if (genState.recordDetails && Array.isArray(genState.recordDetails)) { + setRecordDetails(genState.recordDetails); + } + } + + if (snapshot.version !== undefined) { + let vNum: number | null = null; + if (typeof snapshot.version === 'number') { + vNum = snapshot.version; + } else if (typeof snapshot.version === 'string') { + const match = snapshot.version.match(/\d+/); + if (match) { + vNum = parseInt(match[0], 10); + } + } + if (vNum !== null && !isNaN(vNum)) setCurrentVersionNumber(vNum); + } + + if (snapshot.timelineAdjustments) setTimelineAdjustments(snapshot.timelineAdjustments); + if (snapshot.expectedDateTimestamp) { + setExpectedDate(new Date(snapshot.expectedDateTimestamp)); + } else if (snapshot.expectedDateString) { + setExpectedDate(new Date(snapshot.expectedDateString)); + } + + let startTimeRestored = false; + if (snapshot.startTimestamp) { + setStartTime(new Date(snapshot.startTimestamp)); + startTimeRestored = true; + } else if (snapshot.startString) { + const parsed = new Date(snapshot.startString); + if (!isNaN(parsed.getTime())) { + setStartTime(parsed); + startTimeRestored = true; + } + } + if (!startTimeRestored) { + const startTimeValue = await startTimeField.getValue(deliveryRecordId); + if (startTimeValue) { + let extractedStartTime: Date | null = null; + if (typeof startTimeValue === 'number') { + extractedStartTime = new Date(startTimeValue); + } else if (Array.isArray(startTimeValue) && startTimeValue.length > 0) { + const timestamp = startTimeValue[0]; + if (typeof timestamp === 'number') { + extractedStartTime = new Date(timestamp); + } + } + if (extractedStartTime && !isNaN(extractedStartTime.getTime())) { + setStartTime(extractedStartTime); + } + } + } + + setTimelineResults(snapshot.timelineResults); + Modal.confirm({ + title: '是否调整标签?', + content: '选择“是”将允许修改标签并重新生成计划(版本按V2/V3/V4递增)', + okText: '是,调整标签', + cancelText: '否,直接还原', + onOk: async () => { + setLabelAdjustmentFlow(true); + setTimelineVisible(false); + if (bitable.ui.showToast) { + await bitable.ui.showToast({ toastType: 'info', message: '请在下方修改标签后点击重新生成计划' }); + } + }, + onCancel: async () => { + setLabelAdjustmentFlow(false); + setTimelineVisible(true); + if (bitable.ui.showToast) { + await bitable.ui.showToast({ toastType: 'success', message: '已按快照一模一样还原流程数据' }); + } + } + }); + setTimelineLoading(false); + setIsRestoringSnapshot(false); + return; + } + } + } catch (e) { + console.warn('从节点快照快速还原失败,继续旧流程:', e); + } + const records = await Promise.all(recordIds.map(id => processTable.getRecordById(id))); // 优先使用文本2快照一模一样还原 @@ -523,7 +667,7 @@ export default function App() { // 如果快照中没有起始时间信息,则从当前选中的货期记录中获取 if (!startTimeRestored) { - const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID]; + const startTimeValue = await startTimeField.getValue(deliveryRecordId); if (startTimeValue) { let extractedStartTime: Date | null = null; if (typeof startTimeValue === 'number') { @@ -897,7 +1041,7 @@ export default function App() { }); // 如果没有快照恢复起始时间,则从当前货期记录中获取起始时间 - const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID]; + const startTimeValue = await startTimeField.getValue(deliveryRecordId); if (startTimeValue) { let extractedStartTime: Date | null = null; if (typeof startTimeValue === 'number') { @@ -963,6 +1107,7 @@ export default function App() { const DELIVERY_TEXT2_FIELD_ID = 'fldG6LZnmU'; // 记录ID文本字段(货期记录表新增) const DELIVERY_RECORD_IDS_FIELD_ID = 'fldq3u7h7H'; + const DELIVERY_FACTORY_DEPARTURE_DATE_FIELD_ID = 'fldZFdZDKj'; // OMS看板表相关常量(新增) const OMS_BOARD_TABLE_ID = 'tbl7j8bCpUbFmGuk'; // OMS看板表ID @@ -1516,8 +1661,8 @@ export default function App() { if (recordId && tableId === DELIVERY_RECORD_TABLE_ID) { // 如果选中的是货期记录表的记录,从中获取起始时间 const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID); - const deliveryRecord = await deliveryTable.getRecordById(recordId); - const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID]; + const startTimeField: any = await deliveryTable.getField(DELIVERY_START_TIME_FIELD_ID); + const startTimeValue = await startTimeField.getValue(recordId); if (startTimeValue) { let extractedStartTime: Date | null = null; @@ -1539,22 +1684,23 @@ export default function App() { // 从OMS看板匹配对应的货期记录后,尝试获取其起始时间 try { const omsTable = await bitable.base.getTable(OMS_BOARD_TABLE_ID); - const omsRecord = await omsTable.getRecordById(recordId); - const planTextRaw = omsRecord?.fields?.[OMS_PLAN_TEXT_FIELD_ID]; - const planVersionRaw = omsRecord?.fields?.[OMS_PLAN_VERSION_FIELD_ID]; + const planTextField: any = await omsTable.getField(OMS_PLAN_TEXT_FIELD_ID); + const planVersionField: any = await omsTable.getField(OMS_PLAN_VERSION_FIELD_ID); + const planTextRaw = await planTextField.getValue(recordId); + const planVersionRaw = await planVersionField.getValue(recordId); const planText = extractText(planTextRaw)?.trim(); let planVersion: number | null = null; if (typeof planVersionRaw === 'number') { planVersion = planVersionRaw; } else if (typeof planVersionRaw === 'string') { - const m = planVersionRaw.match(/\d+/); - if (m) planVersion = parseInt(m[0], 10); + const m = planVersionRaw.match(/\d+(?:\.\d+)?/); + if (m) planVersion = parseFloat(m[0]); } else if (planVersionRaw && typeof planVersionRaw === 'object') { const v = (planVersionRaw as any).value ?? (planVersionRaw as any).text; if (typeof v === 'number') planVersion = v; else if (typeof v === 'string') { - const m = v.match(/\d+/); - if (m) planVersion = parseInt(m[0], 10); + const m = v.match(/\d+(?:\.\d+)?/); + if (m) planVersion = parseFloat(m[0]); } } @@ -1562,8 +1708,8 @@ export default function App() { const deliveryRecordId = await findDeliveryRecordIdByPlan(planText, planVersion); if (deliveryRecordId) { const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID); - const deliveryRecord = await deliveryTable.getRecordById(deliveryRecordId); - const startTimeValue = deliveryRecord?.fields?.[DELIVERY_START_TIME_FIELD_ID]; + const startTimeField: any = await deliveryTable.getField(DELIVERY_START_TIME_FIELD_ID); + const startTimeValue = await startTimeField.getValue(deliveryRecordId); if (startTimeValue) { let extractedStartTime: Date | null = null; if (typeof startTimeValue === 'number') { @@ -1597,30 +1743,47 @@ export default function App() { const findDeliveryRecordIdByPlan = async (planText: string, planVersion: number): Promise => { try { const deliveryTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID); - // 拉取一定数量的记录进行匹配(如需可优化为分页/索引) - const recordsResult = await deliveryTable.getRecords({ pageSize: 5000 }); - const records = recordsResult.records || []; - for (const rec of records) { - const fields = rec?.fields || {}; - const recordIdsTextVal = fields[DELIVERY_RECORD_IDS_FIELD_ID]; - const versionVal = fields[DELIVERY_VERSION_FIELD_ID]; - const recordIdsText = extractText(recordIdsTextVal)?.trim(); - let versionNum: number | null = null; - if (typeof versionVal === 'number') versionNum = versionVal; - else if (typeof versionVal === 'string') { - const m = versionVal.match(/\d+/); - if (m) versionNum = parseInt(m[0], 10); - } else if (versionVal && typeof versionVal === 'object') { - const v = (versionVal as any).value ?? (versionVal as any).text; - if (typeof v === 'number') versionNum = v; - else if (typeof v === 'string') { - const m = v.match(/\d+/); - if (m) versionNum = parseInt(m[0], 10); + const versionField: any = await deliveryTable.getField(DELIVERY_VERSION_FIELD_ID); + const planFilter = { + conjunction: 'and', + conditions: [{ fieldId: DELIVERY_RECORD_IDS_FIELD_ID, operator: 'is', value: planText }] + }; + const sort = DELIVERY_CREATE_TIME_FIELD_ID + ? [{ fieldId: DELIVERY_CREATE_TIME_FIELD_ID, desc: true }] + : undefined; + + let token: any = undefined; + const eps = 1e-9; + for (let i = 0; i < 10000; i++) { + const res: any = await versionField.getFieldValueListByPage({ + pageSize: 200, + pageToken: token, + filter: planFilter, + sort, + stringValue: true + }); + const fieldValues: any[] = Array.isArray(res?.fieldValues) ? res.fieldValues : []; + + for (const fv of fieldValues) { + const recordId = fv?.recordId; + const raw = fv?.value; + let v: number | null = null; + if (typeof raw === 'number') { + v = raw; + } else { + const s = typeof raw === 'string' ? raw : extractText(raw); + const m = (s || '').match(/\d+(?:\.\d+)?/); + if (m) v = parseFloat(m[0]); + } + if (v !== null && Math.abs(v - planVersion) < eps) { + return recordId || null; } } - if (recordIdsText && versionNum !== null && recordIdsText === planText && versionNum === planVersion) { - return rec.id || rec.recordId || null; - } + + const nextToken = res?.pageToken; + const hasMore = !!res?.hasMore; + token = nextToken; + if (!hasMore && !nextToken) break; } return null; } catch (error) { @@ -1779,11 +1942,7 @@ export default function App() { // 1. 先获取匹配的流程节点(复用预览功能的逻辑) const processTable = await bitable.base.getTable(PROCESS_CONFIG_TABLE_ID); - const processRecordsResult = await processTable.getRecords({ - pageSize: 5000 - }); - - const processRecords = processRecordsResult.records || []; + const processRecords = await fetchAllRecordsByPage(processTable); const matchedProcessNodes: any[] = []; // 匹配流程配置节点 @@ -1965,11 +2124,7 @@ export default function App() { console.log('按顺序排列的流程节点:', matchedProcessNodes); // 2. 优化:预先获取所有时效数据并建立索引 - const timelineRecordsResult = await timelineTable.getRecords({ - pageSize: 5000 - }); - - const timelineRecords = timelineRecordsResult.records || []; + const timelineRecords = await fetchAllRecordsByPage(timelineTable); // 优化2:预处理时效数据,建立节点名称到记录的映射 const timelineIndexByNode = new Map(); @@ -3008,7 +3163,8 @@ export default function App() { DELIVERY_CUSTOMER_EXPECTED_DATE_FIELD_ID, DELIVERY_NODE_DETAILS_FIELD_ID, DELIVERY_ADJUSTMENT_INFO_FIELD_ID, // 添加货期调整信息字段 - DELIVERY_START_TIME_FIELD_ID // 新增:起始时间字段 + DELIVERY_START_TIME_FIELD_ID, // 新增:起始时间字段 + DELIVERY_FACTORY_DEPARTURE_DATE_FIELD_ID ]; console.log('需要检查的字段ID列表:', fieldsToCheck); @@ -3028,6 +3184,7 @@ export default function App() { const startTimeField = await deliveryRecordTable.getField(DELIVERY_START_TIME_FIELD_ID); const snapshotField = await deliveryRecordTable.getField(DELIVERY_SNAPSHOT_JSON_FIELD_ID); const recordIdsTextField = await deliveryRecordTable.getField(DELIVERY_RECORD_IDS_FIELD_ID); + const factoryDepartureDateField = await deliveryRecordTable.getField(DELIVERY_FACTORY_DEPARTURE_DATE_FIELD_ID); console.log('成功获取所有字段对象'); // 检查标签汇总字段的类型 @@ -3159,33 +3316,28 @@ export default function App() { if (expectedDateToUse) { customerExpectedDate = expectedDateToUse.getTime(); } + + let factoryDepartureDate = null as number | null; + const reservationInbound = timelineResults.find(r => r?.nodeName === '预约入库'); + const reservationEnd = reservationInbound?.estimatedEnd; + if (reservationEnd instanceof Date && !isNaN(reservationEnd.getTime())) { + factoryDepartureDate = reservationEnd.getTime(); + } else if (typeof reservationEnd === 'number' && Number.isFinite(reservationEnd)) { + factoryDepartureDate = reservationEnd; + } else if (typeof reservationEnd === 'string' && reservationEnd.trim() !== '') { + const parsed = parseDate(reservationEnd); + if (parsed) { + factoryDepartureDate = parsed.getTime(); + } + } // 创建当前时间戳 const currentTime = new Date().getTime(); // 计算版本号(数字)并格式化货期调整信息 let versionNumber = 1; - try { - if (mode === 'adjust' && currentVersionNumber !== null) { - // 调整模式:优先使用快照version +1 - versionNumber = currentVersionNumber + 1; - } else if (foreignId) { - const existing = await deliveryRecordTable.getRecords({ - pageSize: 5000, - filter: { - conjunction: 'and', - conditions: [{ - fieldId: DELIVERY_FOREIGN_ID_FIELD_ID, - operator: 'is', - value: [foreignId] - }] - } - }); - const count = existing.records?.length || 0; - versionNumber = count + 1; - } - } catch (e) { - console.warn('计算版本号失败:', e); + if (mode === 'adjust' && currentVersionNumber !== null) { + versionNumber = currentVersionNumber + 1; } let adjustmentInfo = `版本:V${versionNumber}`; @@ -3374,6 +3526,7 @@ export default function App() { const snapshotCell = await snapshotField.createCell(snapshotJson); const expectedDateCell = expectedDeliveryDate ? await expectedDateField.createCell(expectedDeliveryDate) : null; const customerExpectedDateCell = customerExpectedDate ? await customerExpectedDateField.createCell(customerExpectedDate) : null; + const factoryDepartureDateCell = factoryDepartureDate ? await factoryDepartureDateField.createCell(factoryDepartureDate) : null; // 对于关联记录字段,确保传入的是记录ID数组 const nodeDetailsCell = processRecordIds.length > 0 ? await nodeDetailsField.createCell({ recordIds: processRecordIds }) : null; @@ -3394,22 +3547,13 @@ export default function App() { if (labelsCell) recordCells.push(labelsCell); if (expectedDateCell) recordCells.push(expectedDateCell); if (customerExpectedDateCell) recordCells.push(customerExpectedDateCell); + if (factoryDepartureDateCell) recordCells.push(factoryDepartureDateCell); if (nodeDetailsCell) recordCells.push(nodeDetailsCell); if (adjustmentInfoCell) recordCells.push(adjustmentInfoCell); // 添加记录到货期记录表 const addedRecord = await deliveryRecordTable.addRecord(recordCells); - // 保存最近一次写入的记录ID与版本号 - try { - const savedId = typeof addedRecord === 'string' - ? addedRecord - : (((addedRecord as any)?.id || (addedRecord as any)?.recordId) as string); - if (savedId) { - setLastSavedDeliveryRecordId(savedId); - setLastSavedDeliveryVersion(versionNumber); - } - } catch {} - + return addedRecord; } catch (error) { console.error('写入货期记录表详细错误:', { @@ -3533,70 +3677,13 @@ export default function App() { } } - // 先删除该foreign_id的所有现有记录 - console.log('=== 开始删除现有记录 ==='); - console.log('使用的foreign_id:', foreignId); - try { - console.log('正在查询现有记录...'); - const existingRecords = await processDataTable.getRecords({ - pageSize: 5000, - filter: { - conjunction: 'And', - conditions: [{ - fieldId: FOREIGN_ID_FIELD_ID, - operator: 'is', - value: [foreignId] - }] - } - }); - - console.log('查询到现有记录数量:', existingRecords.records?.length || 0); - - if (existingRecords.records && existingRecords.records.length > 0) { - const recordIdsToDelete = existingRecords.records.map(record => record.id); - console.log('准备删除的记录ID:', recordIdsToDelete); - await processDataTable.deleteRecords(recordIdsToDelete); - console.log(`成功删除 ${recordIdsToDelete.length} 条现有记录`); - - if (bitable.ui.showToast) { - await bitable.ui.showToast({ - toastType: 'info', - message: `已删除 ${recordIdsToDelete.length} 条现有流程数据` - }); - } - } else { - console.log('没有找到需要删除的现有记录'); - } - } catch (deleteError) { - console.error('删除现有记录时出错:', deleteError); - // 继续执行,不中断流程 - } + // 构建页面快照JSON(确保可一模一样还原) - // 计算版本号:数字。默认按 foreign_id 在货期记录表的记录数量 + 1 + // 计算版本号:仅在调整模式下递增 let versionNumber = 1; - try { - const deliveryRecordTable = await bitable.base.getTable(DELIVERY_RECORD_TABLE_ID); - if (mode === 'adjust' && currentVersionNumber !== null) { - // 调整模式:优先使用快照version +1,避免公式字段读取错误 - versionNumber = currentVersionNumber + 1; - } else if (foreignId) { - const existing = await deliveryRecordTable.getRecords({ - pageSize: 5000, - filter: { - conjunction: 'and', - conditions: [{ - fieldId: DELIVERY_FOREIGN_ID_FIELD_ID, - operator: 'is', - value: [foreignId] - }] - } - }); - const count = existing.records?.length || 0; - versionNumber = count + 1; - } - } catch (e) { - console.warn('计算版本号失败:', e); + if (mode === 'adjust' && currentVersionNumber !== null) { + versionNumber = currentVersionNumber + 1; } @@ -3606,6 +3693,12 @@ export default function App() { for (let index = 0; index < timelineResults.length; index++) { const result = timelineResults[index]; console.log('处理节点数据:', result); + + const hasValidTimelineValue = typeof result.timelineValue === 'number' && Number.isFinite(result.timelineValue) && result.timelineValue > 0; + if (!hasValidTimelineValue) { + console.log(`跳过节点 "${result.nodeName}" - 未匹配到时效值`); + continue; + } // 检查是否有有效的预计完成时间(只检查结束时间) const hasValidEndTime = ( @@ -3653,22 +3746,20 @@ export default function App() { // 组合所有Cell到一个记录中 const versionCell = await versionField.createCell(versionNumber); - const timelinessCell = (typeof result.timelineValue === 'number') - ? await timelinessField.createCell(result.timelineValue) - : null; + const timelinessCell = await timelinessField.createCell(result.timelineValue); const recordCells = [ foreignIdCell, processNameCell, processOrderCell, styleCell, colorCell, - versionCell + versionCell, + timelinessCell ]; // 只有当时间戳存在时才添加日期Cell if (startDateCell) recordCells.push(startDateCell); if (endDateCell) recordCells.push(endDateCell); - if (timelinessCell) recordCells.push(timelinessCell); console.log(`准备写入的Cell记录 - ${result.nodeName}:`, recordCells); recordCellsToAdd.push(recordCells); @@ -3712,7 +3803,7 @@ export default function App() { if (bitable.ui.showToast) { await bitable.ui.showToast({ toastType: 'warning', - message: '没有有效的流程数据可以写入 - 所有节点都缺少时效数据' + message: '没有有效的流程数据可以写入 - 未匹配到时效值' }); } return []; @@ -3782,9 +3873,15 @@ export default function App() { const batchTable = await bitable.base.getTable(BATCH_TABLE_ID); const fieldMetaList = await batchTable.getFieldMetaList(); const nameToId = new Map(); - for (const f of fieldMetaList) nameToId.set(f.name, f.id); - const res = await batchTable.getRecords({ pageSize: 5000 }); - const rows = res.records || []; + for (const meta of (fieldMetaList || [])) { + if (!meta) continue; + const nm = (meta as any).name; + const id = (meta as any).id; + if (typeof nm === 'string' && typeof id === 'string' && nm.trim() && id.trim()) { + nameToId.set(nm, id); + } + } + const rows = await fetchAllRecordsByPage(batchTable); const total = rows.length; const startIndex1 = range?.start && range.start > 0 ? range.start : 1; const endIndex1 = range?.end && range.end > 0 ? range.end : total; @@ -3933,7 +4030,169 @@ export default function App() { - + // 执行定价数据查询 + const executeQuery = async (packId: string, packType: string) => { + if (queryLoading) return; + setQueryLoading(true); + try { + // 使用 apiService 中的函数 + const data = await executePricingQuery(packId, packType, selectedLabels); + setQueryResults(data); + + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'success', + message: '查询成功' + }); + } + + } catch (error: any) { + console.error('数据库查询出错:', error); + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'error', + message: `数据库查询失败: ${error.message}` + }); + } + } finally { + setQueryLoading(false); + } + }; + + // 执行二次工艺查询 + const executeSecondaryProcessQueryLocal = async (packId: string, packType: string) => { + if (secondaryProcessLoading) return; + setSecondaryProcessLoading(true); + try { + const data = await executeSecondaryProcessQuery(packId, packType); + setSecondaryProcessResults(data); + + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'success', + message: '二次工艺查询成功' + }); + } + } catch (error: any) { + console.error('二次工艺查询出错:', error); + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'error', + message: `二次工艺查询失败: ${error.message}` + }); + } + } finally { + setSecondaryProcessLoading(false); + } + }; + + // 执行定价详情查询 + const executePricingDetailsQueryLocal = async (packId: string) => { + if (pricingDetailsLoading) return; + setPricingDetailsLoading(true); + try { + const data = await executePricingDetailsQuery(packId); + setPricingDetailsResults(data); + + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'success', + message: '定价详情查询成功' + }); + } + } catch (error: any) { + console.error('定价详情查询出错:', error); + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'error', + message: `定价详情查询失败: ${error.message}` + }); + } + } finally { + setPricingDetailsLoading(false); + } + }; + + // 处理数据库查询 + const handleQueryDatabase = async (record: any) => { + // 从记录字段中提取 packId 和 packType + let packId = ''; + let packType = ''; + + // 提取 pack_id (fldpvBfeC0) + if (record.fields.fldpvBfeC0 && Array.isArray(record.fields.fldpvBfeC0) && record.fields.fldpvBfeC0.length > 0) { + packId = record.fields.fldpvBfeC0[0].text; + } + + // 提取 pack_type (fldSAF9qXe) + if (record.fields.fldSAF9qXe && record.fields.fldSAF9qXe.text) { + packType = record.fields.fldSAF9qXe.text; + } + + if (!packId || !packType) { + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'error', + message: '缺少必要的查询参数 (pack_id 或 pack_type)' + }); + } + return; + } + + await executeQuery(packId, packType); + }; + + // 处理二次工艺查询 + const handleSecondaryProcessQuery = async (record: any) => { + // 从记录字段中提取 packId 和 packType + let packId = ''; + let packType = ''; + + // 提取 pack_id (fldpvBfeC0) + if (record.fields.fldpvBfeC0 && Array.isArray(record.fields.fldpvBfeC0) && record.fields.fldpvBfeC0.length > 0) { + packId = record.fields.fldpvBfeC0[0].text; + } + + // 提取 pack_type (fldSAF9qXe) + if (record.fields.fldSAF9qXe && record.fields.fldSAF9qXe.text) { + packType = record.fields.fldSAF9qXe.text; + } + + if (!packId || !packType) { + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'error', + message: '缺少必要的查询参数 (pack_id 或 pack_type)' + }); + } + return; + } + + await executeSecondaryProcessQueryLocal(packId, packType); + }; + + // 处理定价详情查询 + const handlePricingDetailsQuery = async (record: any) => { + // 从记录字段中提取 packId + let packId = ''; + + // 提取 pack_id (fldpvBfeC0) + if (record.fields.fldpvBfeC0 && Array.isArray(record.fields.fldpvBfeC0) && record.fields.fldpvBfeC0.length > 0) { + packId = record.fields.fldpvBfeC0[0].text; + } + + if (!packId) { + if (bitable.ui.showToast) { + await bitable.ui.showToast({ + toastType: 'error', + message: '缺少必要的查询参数 (pack_id)' + }); + } + return; + } + + await executePricingDetailsQueryLocal(packId); + }; @@ -4173,12 +4432,115 @@ export default function App() { const handleClearRecords = () => { setSelectedRecords([]); setRecordDetails([]); + setQueryResults([]); + setSecondaryProcessResults([]); + setPricingDetailsResults([]); // 同时清空标签选择 setSelectedLabels({}); setExpectedDate(null); }; - + // 定价数据表格列定义 + const columns = [ + { + title: 'ID', + dataIndex: 'id', + key: 'id', + }, + { + title: 'Pack ID', + dataIndex: 'pack_id', + key: 'pack_id', + }, + { + title: 'Pack Type', + dataIndex: 'pack_type', + key: 'pack_type', + }, + { + title: '物料编码', + dataIndex: 'material_code', + key: 'material_code', + }, + { + title: '一级分类', + dataIndex: 'category1_name', + key: 'category1_name', + }, + { + title: '二级分类', + dataIndex: 'category2_name', + key: 'category2_name', + }, + { + title: '三级分类', + dataIndex: 'category3_name', + key: 'category3_name', + }, + { + title: '品类属性', + dataIndex: '品类属性', + key: '品类属性', + }, + ]; + + // 二次工艺表格列定义 + const secondaryProcessColumns = [ + { + title: 'ID', + dataIndex: 'id', + key: 'id', + }, + { + title: 'Foreign ID', + dataIndex: 'foreign_id', + key: 'foreign_id', + }, + { + title: 'Pack Type', + dataIndex: 'pack_type', + key: 'pack_type', + }, + { + title: '项目', + dataIndex: 'costs_item', + key: 'costs_item', + }, + { + title: '二次工艺', + dataIndex: 'costs_type', + key: 'costs_type', + }, + { + title: '备注', + dataIndex: 'remarks', + key: 'remarks', + }, + ]; + + // 定价详情表格列定义 + const pricingDetailsColumns = [ + { + title: 'Pack ID', + dataIndex: 'pack_id', + key: 'pack_id', + }, + { + title: '倍率', + dataIndex: '倍率', + key: '倍率', + }, + { + title: '加工费', + dataIndex: '加工费', + key: '加工费', + }, + { + title: '总价', + dataIndex: '总价', + key: '总价', + }, + ]; return (
@@ -4330,30 +4692,6 @@ export default function App() { 调整流程日期 读取货期记录,精确还原时间线 - {lastSavedDeliveryRecordId && lastSavedDeliveryVersion !== null && ( - - 最近保存的货期记录 -
- 版本:V{lastSavedDeliveryVersion} - record_id:{lastSavedDeliveryRecordId} -
-
- )}
)} {/* 功能入口切换与调整入口 */} @@ -5534,7 +5872,49 @@ export default function App() { {/* 批量处理配置已移除 */} )} - + {mode === 'generate' && ( + <> + {/* 面料数据查询结果 */} + {queryResults.length > 0 && ( + <> + + 面料数据查询结果 ({queryResults.length} 条) + ({ ...item, key: index }))} + pagination={{ pageSize: 10 }} + style={{ marginTop: '10px' }} + /> + + )} + {/* 二次工艺查询结果 */} + {secondaryProcessResults.length > 0 && ( + <> + + 二次工艺查询结果 ({secondaryProcessResults.length} 条) +
({ ...item, key: index }))} + pagination={{ pageSize: 10 }} + style={{ marginTop: '10px' }} + /> + + )} + {/* 工艺价格查询结果 */} + {pricingDetailsResults.length > 0 && ( + <> + + 工艺价格查询结果 ({pricingDetailsResults.length} 条) +
({ ...item, key: index }))} + pagination={{ pageSize: 10 }} + style={{ marginTop: '10px' }} + /> + + )} + + )} ); }