refactor(WpTool): 替换 CSV 解析为 XLSX 库以支持 Excel 文件

移除 papaparse 依赖,改用 xlsx 库处理文件上传和导出
支持 CSV 和 Excel 格式文件,提升用户兼容性
This commit is contained in:
tikkhun 2025-11-26 15:23:43 +08:00
parent 867b7f74d4
commit 4b01742dcd
2 changed files with 52 additions and 48 deletions

View File

@ -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",

View File

@ -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) => {
// 将数组转换为对象数组
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('下载任务已开始!');
};