From 4b01742dcda807e4ba0c9eabaca915665f9930c8 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Wed, 26 Nov 2025 15:23:43 +0800 Subject: [PATCH] =?UTF-8?q?refactor(WpTool):=20=E6=9B=BF=E6=8D=A2=20CSV=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E4=B8=BA=20XLSX=20=E5=BA=93=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20Excel=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除 papaparse 依赖,改用 xlsx 库处理文件上传和导出 支持 CSV 和 Excel 格式文件,提升用户兼容性 --- package.json | 3 - src/pages/Product/WpTool/index.tsx | 97 ++++++++++++++++-------------- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 46ba166..d77a71f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "@ant-design/icons": "^5.0.1", "@ant-design/pro-components": "^2.4.4", "@fingerprintjs/fingerprintjs": "^4.6.2", - "@umijs/max": "^4.4.4", "@umijs/max-plugin-openapi": "^2.0.3", "@umijs/plugin-openapi": "^1.3.3", @@ -25,7 +24,6 @@ "echarts": "^5.6.0", "echarts-for-react": "^3.0.2", "file-saver": "^2.0.5", - "papaparse": "^5.5.3", "print-js": "^1.6.0", "react-phone-input-2": "^2.15.1", "react-toastify": "^11.0.5", @@ -34,7 +32,6 @@ "devDependencies": { "@types/react": "^18.0.33", "@types/react-dom": "^18.0.11", - "@types/papaparse": "^5.5.0", "code-inspector-plugin": "^1.2.10", "husky": "^9", "lint-staged": "^13.2.0", diff --git a/src/pages/Product/WpTool/index.tsx b/src/pages/Product/WpTool/index.tsx index 38f48cd..a31f180 100644 --- a/src/pages/Product/WpTool/index.tsx +++ b/src/pages/Product/WpTool/index.tsx @@ -7,7 +7,7 @@ import { } from '@ant-design/pro-components'; import { Button, Card, Col, Input, message, Row, Upload } from 'antd'; import { UploadOutlined } from '@ant-design/icons'; -import Papa from 'papaparse'; +import * as XLSX from 'xlsx'; // 定义配置接口 interface TagConfig { @@ -176,41 +176,53 @@ const WpToolPage: React.FC = () => { * @param {File} uploadedFile - 用户上传的文件 */ const handleFileUpload = (uploadedFile: File) => { - // 检查文件类型是否为 CSV - if (uploadedFile.type !== 'text/csv') { - message.error('请上传 CSV 格式的文件!'); + // 检查文件类型,虽然 xlsx 库更宽容,但最好还是保留基本验证 + if (!uploadedFile.name.match(/\.(csv|xlsx|xls)$/)) { + message.error('请上传 CSV 或 Excel 格式的文件!'); return false; } setFile(uploadedFile); - // 使用 Papaparse 解析 CSV 文件 - Papa.parse(uploadedFile, { - header: true, // 将第一行作为表头 - skipEmptyLines: true, - // 简化配置,依赖解析器的自动检测能力,同时保持必要的兼容性 - quoteChar: '"', - dynamicTyping: false, // 禁用动态类型转换 - relaxColumnCount: true, // 允许列数不匹配 - complete: (results) => { - // 如果解析过程中出现错误 - if (results.errors.length > 0) { - // 提取第一条错误信息用于展示 - const firstError = results.errors[0]; - // 构造更详细的错误提示 - const errorMsg = `CSV 解析失败 (行号: ${firstError.row}): ${firstError.message}`; - message.error(errorMsg); - console.error('CSV Parsing Errors:', results.errors); + + const reader = new FileReader(); + reader.onload = (e) => { + try { + const data = e.target?.result; + const workbook = XLSX.read(data, { type: 'binary' }); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 }); + + if (jsonData.length < 2) { + message.error('文件为空或缺少表头!'); setCsvData([]); - } else { - message.success(`成功解析 ${results.data.length} 条数据。`); - setCsvData(results.data); - setProcessedData([]); // 清空旧的处理结果 + return; } - }, - error: (error) => { - message.error('文件读取失败!'); - console.error('File Read Error:', error); - }, - }); + + // 将数组转换为对象数组 + const headers = jsonData[0] as string[]; + const rows = jsonData.slice(1).map((rowArray: any) => { + const rowData: { [key: string]: any } = {}; + headers.forEach((header, index) => { + rowData[header] = rowArray[index]; + }); + return rowData; + }); + + message.success(`成功解析 ${rows.length} 条数据。`); + setCsvData(rows); + setProcessedData([]); // 清空旧的处理结果 + } catch (error) { + message.error('文件解析失败,请检查文件格式!'); + console.error('File Parse Error:', error); + setCsvData([]); + } + }; + reader.onerror = (error) => { + message.error('文件读取失败!'); + console.error('File Read Error:', error); + }; + reader.readAsBinaryString(uploadedFile); + return false; // 阻止 antd Upload 组件的默认上传行为 }; @@ -274,21 +286,16 @@ const WpToolPage: React.FC = () => { return; } - // 使用 Papaparse 将 JSON 对象数组转换回 CSV 字符串 - const csvString = Papa.unparse(processedData); + // 创建一个新的工作簿 + const workbook = XLSX.utils.book_new(); + // 将 JSON 数据转换为工作表 + const worksheet = XLSX.utils.json_to_sheet(processedData); + // 将工作表添加到工作簿 + XLSX.utils.book_append_sheet(workbook, worksheet, 'Products with Tags'); - // 创建 Blob 对象 - const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' }); - - // 创建一个临时的 a 标签用于下载 - const link = document.createElement('a'); - const url = URL.createObjectURL(blob); - link.setAttribute('href', url); - link.setAttribute('download', `products_with_tags_${Date.now()}.csv`); - link.style.visibility = 'hidden'; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + // 生成文件名并触发下载 + const fileName = `products_with_tags_${Date.now()}.xlsx`; + XLSX.writeFile(workbook, fileName); message.success('下载任务已开始!'); };