import { useState } from 'react'; import * as XLSX from 'xlsx'; import { message } from 'antd'; import { ExcelRow, SheetConfig } from '../types'; type ExcelJsonData = Array>; export const useExcelParser = () => { const [jsonData, setJsonData] = useState<{ [key: string]: ExcelRow[] }>({}); // 存储所有字段的原始数据,不受exportFields配置影响 const [allFieldsData, setAllFieldsData] = useState<{ [key: string]: ExcelRow[] }>({}); const processDataForExport = ( data: ExcelRow[], sheetConfig: SheetConfig, tableIndex: number = 0 ): ExcelRow[] => { return data .map((row, index) => { const processedRow: ExcelRow = {}; processedRow._id = `table-${tableIndex}-row-${index}`; // 如果exportFields为空,保留所有原始字段 if (sheetConfig.exportFields.length === 0) { Object.keys(row).forEach(field => { processedRow[field] = row[field] ?? null; }); } else { // 按照exportFields配置处理 sheetConfig.exportFields.forEach(field => { const mappedField = sheetConfig.fieldMappings[field] || field; processedRow[mappedField] = row[field] ?? null; }); } return processedRow; }) .filter(row => { // 如果没有设置必需字段或exportFields为空,则不过滤 if (sheetConfig.requiredFields.length === 0 || sheetConfig.exportFields.length === 0) { return true; } // 检查必需字段是否有值 return sheetConfig.requiredFields.every(field => { const value = row[sheetConfig.fieldMappings[field] || field]; return value !== null && value !== undefined && value !== ''; }); }); }; // 处理所有原始字段数据,不过滤 const processAllFieldsData = ( data: ExcelRow[], tableIndex: number = 0 ): ExcelRow[] => { return data.map((row, index) => { const processedRow: ExcelRow = { ...row }; processedRow._id = `table-${tableIndex}-row-${index}`; return processedRow; }); }; // 获取所有可用字段 const getAllAvailableFields = (data: { [key: string]: ExcelRow[] }): { [key: string]: string[] } => { // 按工作表名收集字段 const fieldsMap: { [key: string]: Set } = {}; // 遍历每个工作表 Object.entries(data).forEach(([sheetName, rows]) => { if (!fieldsMap[sheetName]) { fieldsMap[sheetName] = new Set(); } // 从每行中收集字段 rows.forEach(row => { Object.keys(row).forEach(key => { // 过滤掉内部属性(如_id, tableIndex等) if (!key.startsWith('_')) { fieldsMap[sheetName].add(key); } }); }); }); // 将Set转换为数组 const result: { [key: string]: string[] } = {}; Object.entries(fieldsMap).forEach(([sheetName, fieldsSet]) => { result[sheetName] = Array.from(fieldsSet); }); return result; }; // 处理单个表格的数据 const parseSingleTable = ( json: ExcelJsonData, headers: string[], startRow: number, endMarker: string, orderNumberRow: number, orderNumberCol: number, productNameRow?: number, productNameCol?: number ): { data: ExcelRow[]; endIndex: number } => { const tableData: ExcelRow[] = []; let currentOrderNumber: string | null = null; let currentProductName: string | null = null; let endIndex = json.length; // 获取订单号 if (orderNumberRow > 0 && orderNumberCol > 0) { currentOrderNumber = String(json[orderNumberRow - 1]?.[orderNumberCol - 1] ?? ''); } // 获取产品名称(如果配置了产品名称行列号) if (productNameRow && productNameCol && productNameRow > 0 && productNameCol > 0) { currentProductName = String(json[productNameRow - 1]?.[productNameCol - 1] ?? ''); } for (let i = startRow; i < json.length; i++) { const row = json[i] || []; const rowString = row.join(''); if (rowString.includes(endMarker)) { endIndex = i; break; } const obj: ExcelRow = {}; let hasData = false; headers.forEach((header, index) => { if (header) { const value = row[index]; if (value !== undefined) { obj[header] = value; hasData = true; } else { obj[header] = null; } } }); if (currentOrderNumber) { obj['订单号'] = currentOrderNumber; } if (currentProductName) { obj['产品名称'] = currentProductName; } if (hasData) { tableData.push(obj); } } return { data: tableData, endIndex }; }; // 处理多表格模式 const parseMultiTable = ( json: ExcelJsonData, sheetConfig: SheetConfig ): ExcelRow[][] => { const tableDataSets: ExcelRow[][] = []; let currentIndex = sheetConfig.dataStartRow - 1; const headerOffset = sheetConfig.multiTableHeaderOffset || 1; const dataOffset = sheetConfig.multiTableDataOffset || 1; const orderNumberOffset = sheetConfig.multiTableOrderNumberOffset || -1; const productNameOffset = sheetConfig.multiTableProductNameOffset || -1; while (currentIndex < json.length) { // 获取当前表格的表头行号 const headerIndex = currentIndex - (sheetConfig.dataStartRow - sheetConfig.headerRowIndex); // 获取当前表格的表头 const headers = (json[headerIndex] || []) as string[]; if (!headers || headers.length === 0) break; // 计算订单号行号 const orderNumberIndex = headerIndex + (orderNumberOffset || 0); // 计算产品名称行号(如果配置了产品名称行列号) let productNameIndex = undefined; if (sheetConfig.productNameRow && sheetConfig.productNameCol) { productNameIndex = headerIndex + (productNameOffset || 0); } // 解析当前表格 const { data, endIndex } = parseSingleTable( json, headers, currentIndex, sheetConfig.endMarker, orderNumberIndex + 1, // +1 因为 parseSingleTable 内部会 -1 sheetConfig.orderNumberCol, productNameIndex !== undefined ? productNameIndex + 1 : undefined, // +1 因为 parseSingleTable 内部会 -1 sheetConfig.productNameCol ); if (data.length > 0) { tableDataSets.push(data); } // 如果没有找到结束标记,退出循环 if (endIndex === json.length) break; // 更新下一个表格的起始位置 currentIndex = endIndex + headerOffset + dataOffset; } return tableDataSets; }; const parseExcelFile = async (file: File, sheetConfigs: SheetConfig[]) => { try { // 显示开始处理的消息 const loadingMessage = message.loading({ content: `正在解析Excel文件: ${file.name}`, key: 'excelParsing', duration: 0 }); console.log('开始处理文件:', file.name); const data = await file.arrayBuffer(); // 更新进度提示 message.loading({ content: '正在读取Excel数据...', key: 'excelParsing', duration: 0 }); const workbook = XLSX.read(data); const rawData: { [key: string]: ExcelRow[] } = {}; let totalTables = 0; let processedSheets = 0; // 处理每个配置的工作表 for (const sheetConfig of sheetConfigs) { // 更新进度提示,显示当前处理的工作表 message.loading({ content: `正在处理工作表 "${sheetConfig.sheetName}" (${processedSheets + 1}/${sheetConfigs.length})`, key: 'excelParsing', duration: 0 }); const worksheet = workbook.Sheets[sheetConfig.sheetName]; if (!worksheet) { console.error('未找到工作表:', sheetConfig.sheetName); message.error({ content: `未找到工作表"${sheetConfig.sheetName}"!`, key: 'excelParsing' }); processedSheets++; continue; } const json = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) as ExcelJsonData; const headers = json[sheetConfig.headerRowIndex - 1] as string[]; if (!headers || headers.length === 0) { console.error(`工作表 ${sheetConfig.sheetName} 的表头数据无效`); message.warning({ content: `工作表 "${sheetConfig.sheetName}" 的表头数据无效,已跳过`, key: 'excelParsing' }); processedSheets++; continue; } if (sheetConfig.isMultiTable) { // 处理多表格模式 message.loading({ content: `正在处理 "${sheetConfig.sheetName}" 的多表格数据...`, key: 'excelParsing', duration: 0 }); const tableDataSets = parseMultiTable(json, sheetConfig); if (tableDataSets.length > 0) { message.loading({ content: `"${sheetConfig.sheetName}" 中发现 ${tableDataSets.length} 个数据表,正在处理...`, key: 'excelParsing', duration: 0 }); // 保存所有原始字段数据 rawData[sheetConfig.sheetName] = tableDataSets.flatMap((tableData, tableIndex) => { return processAllFieldsData(tableData, tableIndex + 1).map(row => ({ ...row, tableIndex: tableIndex + 1, })); }); totalTables += tableDataSets.length; } else { message.warning({ content: `未在 "${sheetConfig.sheetName}" 中找到有效的数据表`, duration: 2 }); } } else { // 处理单表格模式 message.loading({ content: `正在处理 "${sheetConfig.sheetName}" 的单表格数据...`, key: 'excelParsing', duration: 0 }); const { data } = parseSingleTable( json, headers, sheetConfig.dataStartRow - 1, sheetConfig.endMarker, sheetConfig.orderNumberRow, sheetConfig.orderNumberCol, sheetConfig.productNameRow || undefined, sheetConfig.productNameCol || undefined ); if (data.length > 0) { // 保存所有原始字段数据 rawData[sheetConfig.sheetName] = processAllFieldsData(data, 1); totalTables += 1; } else { message.warning({ content: `未在 "${sheetConfig.sheetName}" 中找到有效数据`, duration: 2 }); } } processedSheets++; // 更新进度提示 if (processedSheets < sheetConfigs.length) { message.loading({ content: `已处理 ${processedSheets}/${sheetConfigs.length} 个工作表...`, key: 'excelParsing', duration: 0 }); } } // 更新进度提示 message.loading({ content: '正在整理数据字段...', key: 'excelParsing', duration: 0 }); // 更新状态 setAllFieldsData(rawData); // 计算可用字段(按工作表分类) const availableFieldsBySheet = getAllAvailableFields(rawData); // 完成解析,显示成功消息 message.success({ content: `Excel文件解析成功!共处理 ${Object.keys(rawData).length} 个工作表,${totalTables} 个数据表`, key: 'excelParsing', duration: 3 }); console.log('所有字段原始数据:', rawData); console.log('按工作表分类的可用字段:', availableFieldsBySheet); // 返回原始数据、分类字段 return { rawData, availableFieldsBySheet, }; } catch (error) { console.error('文件处理错误:', error); message.error({ content: '文件处理失败,请检查文件格式是否正确!', key: 'excelParsing' }); throw error; } }; // 根据当前配置动态生成导出数据 const generateExportData = (sheetConfigs: SheetConfig[]) => { // 如果没有原始数据,直接返回空对象 if (Object.keys(allFieldsData).length === 0) { return {}; } const exportData: { [key: string]: ExcelRow[] } = {}; // 处理每个工作表的数据 for (const sheetConfig of sheetConfigs) { const rawDataForSheet = allFieldsData[sheetConfig.sheetName]; if (!rawDataForSheet || rawDataForSheet.length === 0) { continue; } // 根据当前配置处理数据 exportData[sheetConfig.sheetName] = processDataForExport( rawDataForSheet, sheetConfig, 1 ); } // 只有当数据确实发生变化时才更新状态,避免不必要的渲染 const currentJsonData = JSON.stringify(jsonData); const newJsonData = JSON.stringify(exportData); if (currentJsonData !== newJsonData) { // 更新 jsonData 状态 setJsonData(exportData); console.log('生成的导出数据:', exportData); } return exportData; }; const getFieldsBySheet = getAllAvailableFields; return { jsonData, allFieldsData, parseExcelFile, generateExportData, getFieldsBySheet }; };