refactor(WpTool): 替换 CSV 解析为 XLSX 库以支持 Excel 文件
移除 papaparse 依赖,改用 xlsx 库处理文件上传和导出 支持 CSV 和 Excel 格式文件,提升用户兼容性
This commit is contained in:
parent
867b7f74d4
commit
4b01742dcd
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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('下载任务已开始!');
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue