From fcbf0a48338e0a4e66c961f2ebdc2a2905e35a6b Mon Sep 17 00:00:00 2001 From: zhuotianyuan Date: Mon, 26 Jan 2026 15:46:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AE=A2=E5=8D=95):=20=E6=B7=BB=E5=8A=A0W?= =?UTF-8?q?inToPay=E8=AE=A2=E5=8D=95=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现订单导入功能,支持上传Excel文件并解析数据。添加相关API接口和前端上传组件,处理文件上传和格式转换逻辑。同时更新类型定义以支持新功能。 --- src/pages/Order/List/index.tsx | 115 ++++++++++++++++++++++++++++++++- src/servers/api/order.ts | 39 +++++++++++ src/servers/api/typings.d.ts | 9 ++- 3 files changed, 159 insertions(+), 4 deletions(-) diff --git a/src/pages/Order/List/index.tsx b/src/pages/Order/List/index.tsx index 6da24ef..36fd5cb 100644 --- a/src/pages/Order/List/index.tsx +++ b/src/pages/Order/List/index.tsx @@ -3,6 +3,7 @@ import styles from '../../../style/order-list.css'; import InternationalPhoneInput from '@/components/InternationalPhoneInput'; import SyncForm from '@/components/SyncForm'; import { showSyncResult, SyncResultData } from '@/components/SyncResultMessage'; +import { UploadOutlined } from '@ant-design/icons'; import { ORDER_STATUS_ENUM } from '@/constants'; import { HistoryOrder } from '@/pages/Statistics/Order'; import { @@ -24,6 +25,7 @@ import { ordercontrollerSyncorderbyid, ordercontrollerSyncorders, ordercontrollerUpdateorderitems, + ordercontrollerImportwintopay, } from '@/servers/api/order'; import { productcontrollerSearchproducts } from '@/servers/api/product'; import { sitecontrollerAll } from '@/servers/api/site'; @@ -74,12 +76,16 @@ import { Tabs, TabsProps, Tag, + Upload, } from 'antd'; import React, { useMemo, useRef, useState } from 'react'; import RelatedOrders from '../../Subscription/Orders/RelatedOrders'; import dayjs from 'dayjs'; - +import * as XLSX from 'xlsx'; const ListPage: React.FC = () => { + const [file, setFile] = useState(null); + const [csvData, setCsvData] = useState([]); + const [processedData, setProcessedData] = useState([]); const actionRef = useRef(); const [activeKey, setActiveKey] = useState('all'); const [count, setCount] = useState([]); @@ -469,6 +475,64 @@ const ListPage: React.FC = () => { ]; const [selectedRowKeys, setSelectedRowKeys] = useState([]); + + /** + * @description 处理文件上传 + */ + const handleFileUpload = (uploadedFile: File) => { + // 检查文件类型 + if (!uploadedFile.name.match(/\.(xlsx)$/)) { + message.error('请上传 xlsx 格式的文件!'); + return false; + } + setFile(uploadedFile); + + const reader = new FileReader(); + // 对于Excel文件,继续使用readAsArrayBuffer + reader.onload = (e) => { + try { + const data = e.target?.result; + // 如果是ArrayBuffer,使用type: 'array'来处理 + const workbook = XLSX.read(data, { type: 'array' }); + 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([]); + return; + } + // 将数组转换为对象数组 + 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('Excel文件解析失败,请检查文件格式!'); + console.error('Excel Parse Error:', error); + setCsvData([]); + } + }; + reader.readAsArrayBuffer(uploadedFile); + + + reader.onerror = (error) => { + message.error('文件读取失败!'); + console.error('File Read Error:', error); + }; + + return false; // 阻止antd Upload组件的默认上传行为 + }; + return ( @@ -496,6 +560,55 @@ const ListPage: React.FC = () => { }} toolBarRender={() => [ // , + { + const { file, onSuccess, onError } = options; + console.log(file); + const formData = new FormData(); + formData.append('file', file); + try { + const res = await request('/order/import', { + method: 'POST', + data: formData, + requestType: 'form', + }); + + if (res?.success && res.data) { + // 使用xlsx将JSON数据转换为Excel + const XLSX = require('xlsx'); + const worksheet = XLSX.utils.json_to_sheet(res.data); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, 'Orders'); + const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' }); + // 否则按原逻辑处理二进制数据 + const blob = new Blob([excelBuffer], { + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'orders.xlsx'; + a.click(); + URL.revokeObjectURL(url); + + } else { + message.error(res.message || '导出失败'); + } + actionRef.current?.reload(); + setSelectedRowKeys([]); + } catch (error: any) { + message.error('导入失败: ' + (error.message || '未知错误')); + onError?.(error); + } + }} + > + + , { try { diff --git a/src/servers/api/order.ts b/src/servers/api/order.ts index ffab60f..56e0079 100644 --- a/src/servers/api/order.ts +++ b/src/servers/api/order.ts @@ -149,6 +149,45 @@ export async function ordercontrollerGetordersales( }); } +/** 此处后端没有提供注释 POST /order/import */ +export async function ordercontrollerImportwintopay( + body: {}, + files?: File[], + options?: { [key: string]: any }, +) { + const formData = new FormData(); + + if (files) { + files.forEach((f) => formData.append('files', f || '')); + } + + Object.keys(body).forEach((ele) => { + const item = (body as any)[ele]; + + if (item !== undefined && item !== null) { + if (typeof item === 'object' && !(item instanceof File)) { + if (item instanceof Array) { + item.forEach((f) => formData.append(ele, f || '')); + } else { + formData.append( + ele, + new Blob([JSON.stringify(item)], { type: 'application/json' }), + ); + } + } else { + formData.append(ele, item); + } + } + }); + + return request('/order/import', { + method: 'POST', + data: formData, + requestType: 'form', + ...(options || {}), + }); +} + /** 此处后端没有提供注释 POST /order/order/cancel/${param0} */ export async function ordercontrollerCancelorder( // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) diff --git a/src/servers/api/typings.d.ts b/src/servers/api/typings.d.ts index b044bbc..b034b67 100644 --- a/src/servers/api/typings.d.ts +++ b/src/servers/api/typings.d.ts @@ -460,6 +460,8 @@ declare namespace API { tracking_id?: string; /** 物流单号 */ tracking_number?: string; + /** 物流产品代码 */ + tracking_product_code?: string; /** 物流公司 */ shipping_provider?: string; /** 发货方式 */ @@ -621,7 +623,7 @@ declare namespace API { shipping_total?: number; shipping_tax?: number; cart_tax?: number; - total?: number; + total?: any; total_tax?: number; customer_id?: number; customer_email?: string; @@ -820,7 +822,7 @@ declare namespace API { shipping_total?: number; shipping_tax?: number; cart_tax?: number; - total?: number; + total?: any; total_tax?: number; customer_id?: number; customer_email?: string; @@ -1634,7 +1636,7 @@ declare namespace API { shipmentPlatform?: string; stockPointId?: number; sender?: string; - startPhone?: string; + startPhone?: Record; startPostalCode?: string; pickupAddress?: string; shipperCountryCode?: string; @@ -1663,6 +1665,7 @@ declare namespace API { phone_number?: string; phone_number_extension?: string; phone_number_country?: string; + email?: string; /** 创建时间 */ createdAt: string; /** 更新时间 */