forked from yoone/WEB
feat(订单): 添加WinToPay订单导入功能
实现订单导入功能,支持上传Excel文件并解析数据。添加相关API接口和前端上传组件,处理文件上传和格式转换逻辑。同时更新类型定义以支持新功能。
This commit is contained in:
parent
236d0a85bf
commit
fcbf0a4833
|
|
@ -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<File | null>(null);
|
||||
const [csvData, setCsvData] = useState<any[]>([]);
|
||||
const [processedData, setProcessedData] = useState<any[]>([]);
|
||||
const actionRef = useRef<ActionType>();
|
||||
const [activeKey, setActiveKey] = useState<string>('all');
|
||||
const [count, setCount] = useState<any[]>([]);
|
||||
|
|
@ -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 (
|
||||
<PageContainer ghost>
|
||||
<Tabs items={tabs} activeKey={activeKey} onChange={setActiveKey} />
|
||||
|
|
@ -496,6 +560,55 @@ const ListPage: React.FC = () => {
|
|||
}}
|
||||
toolBarRender={() => [
|
||||
// <CreateOrder tableRef={actionRef} />,
|
||||
<Upload
|
||||
// beforeUpload={handleFileUpload}
|
||||
name="file"
|
||||
accept=".xlsx"
|
||||
showUploadList={false}
|
||||
maxCount={1}
|
||||
customRequest={async (options) => {
|
||||
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);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>选择文件</Button>
|
||||
</Upload>,
|
||||
<SyncForm
|
||||
onFinish={async (values: any) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -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<any>('/order/import', {
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
requestType: 'form',
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /order/order/cancel/${param0} */
|
||||
export async function ordercontrollerCancelorder(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
|
|
|
|||
|
|
@ -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<string, any>;
|
||||
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;
|
||||
/** 更新时间 */
|
||||
|
|
|
|||
Loading…
Reference in New Issue