feat(订单): 增加订单导出功能并扩展API类型定义

扩展订单列表API的类型定义,增加时间范围查询参数和分组选项
修改订单导出功能,直接返回CSV文件流供下载
为订单详情添加更多字段类型定义,包括支付时间、客户IP等
This commit is contained in:
tikkhun 2025-12-31 14:56:46 +08:00
parent 6f35c2aee5
commit 6d97ecbcc7
2 changed files with 303 additions and 211 deletions

View File

@ -76,6 +76,7 @@ import {
} from 'antd'; } from 'antd';
import React, { useMemo, useRef, useState } from 'react'; import React, { useMemo, useRef, useState } from 'react';
import RelatedOrders from '../../Subscription/Orders/RelatedOrders'; import RelatedOrders from '../../Subscription/Orders/RelatedOrders';
import { request } from '@umijs/max';
const ListPage: React.FC = () => { const ListPage: React.FC = () => {
const actionRef = useRef<ActionType>(); const actionRef = useRef<ActionType>();
@ -504,15 +505,25 @@ const ListPage: React.FC = () => {
title="批量导出" title="批量导出"
description="确认导出选中的订单吗?" description="确认导出选中的订单吗?"
onConfirm={async () => { onConfirm={async () => {
console.log(selectedRowKeys);
try { try {
const { success, message: errMsg } = const res = await request('/order/order/export', {
await ordercontrollerExportorder({ method: 'GET',
params: {
ids: selectedRowKeys, ids: selectedRowKeys,
}); }
if (!success) { });
throw new Error(errMsg); if (res?.success && res?.data?.csv) {
const blob = new Blob([res.data.csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'customers.csv';
a.click();
URL.revokeObjectURL(url);
} else {
message.error(res.message || '导出失败');
} }
message.success('导出成功');
actionRef.current?.reload(); actionRef.current?.reload();
setSelectedRowKeys([]); setSelectedRowKeys([]);
} catch (error: any) { } catch (error: any) {
@ -616,36 +627,36 @@ const Detail: React.FC<{
) )
? [] ? []
: [ : [
<Divider type="vertical" />, <Divider type="vertical" />,
<Button <Button
type="primary" type="primary"
onClick={async () => { onClick={async () => {
try { try {
if (!record.siteId || !record.externalOrderId) { if (!record.siteId || !record.externalOrderId) {
message.error('站点ID或外部订单ID不存在'); message.error('站点ID或外部订单ID不存在');
return; return;
}
const {
success,
message: errMsg,
data,
} = await ordercontrollerSyncorderbyid({
siteId: record.siteId,
orderId: record.externalOrderId,
});
if (!success) {
throw new Error(errMsg);
}
showSyncResult(data as SyncResultData, '订单');
tableRef.current?.reload();
} catch (error: any) {
message.error(error?.message || '同步失败');
} }
}} const {
> success,
message: errMsg,
</Button>, data,
]), } = await ordercontrollerSyncorderbyid({
siteId: record.siteId,
orderId: record.externalOrderId,
});
if (!success) {
throw new Error(errMsg);
}
showSyncResult(data as SyncResultData, '订单');
tableRef.current?.reload();
} catch (error: any) {
message.error(error?.message || '同步失败');
}
}}
>
</Button>,
]),
// ...(['processing', 'pending_reshipment'].includes(record.orderStatus) // ...(['processing', 'pending_reshipment'].includes(record.orderStatus)
// ? [ // ? [
// <Divider type="vertical" />, // <Divider type="vertical" />,
@ -664,152 +675,152 @@ const Detail: React.FC<{
'pending_refund', 'pending_refund',
].includes(record.orderStatus) ].includes(record.orderStatus)
? [ ? [
<Divider type="vertical" />, <Divider type="vertical" />,
<Popconfirm <Popconfirm
title="转至售后" title="转至售后"
description="确认转至售后?" description="确认转至售后?"
onConfirm={async () => { onConfirm={async () => {
try { try {
if (!record.id) { if (!record.id) {
message.error('订单ID不存在'); message.error('订单ID不存在');
return; return;
}
const { success, message: errMsg } =
await ordercontrollerChangestatus(
{
id: record.id,
},
{
status: 'after_sale_pending',
},
);
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
} }
}} const { success, message: errMsg } =
> await ordercontrollerChangestatus(
<Button type="primary" ghost> {
id: record.id,
</Button> },
</Popconfirm>, {
] status: 'after_sale_pending',
},
);
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
}
}}
>
<Button type="primary" ghost>
</Button>
</Popconfirm>,
]
: []), : []),
...(record.orderStatus === 'after_sale_pending' ...(record.orderStatus === 'after_sale_pending'
? [ ? [
<Divider type="vertical" />, <Divider type="vertical" />,
<Popconfirm <Popconfirm
title="转至取消" title="转至取消"
description="确认转至取消?" description="确认转至取消?"
onConfirm={async () => { onConfirm={async () => {
try { try {
if (!record.id) { if (!record.id) {
message.error('订单ID不存在'); message.error('订单ID不存在');
return; return;
} }
const { success, message: errMsg } = const { success, message: errMsg } =
await ordercontrollerCancelorder({ await ordercontrollerCancelorder({
id: record.id,
});
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
}
}}
>
<Button type="primary" ghost>
</Button>
</Popconfirm>,
<Divider type="vertical" />,
<Popconfirm
title="转至退款"
description="确认转至退款?"
onConfirm={async () => {
try {
if (!record.id) {
message.error('订单ID不存在');
return;
}
const { success, message: errMsg } =
await ordercontrollerRefundorder({
id: record.id,
});
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
}
}}
>
<Button type="primary" ghost>
退
</Button>
</Popconfirm>,
<Divider type="vertical" />,
<Popconfirm
title="转至完成"
description="确认转至完成?"
onConfirm={async () => {
try {
if (!record.id) {
message.error('订单ID不存在');
return;
}
const { success, message: errMsg } =
await ordercontrollerCompletedorder({
id: record.id,
});
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
}
}}
>
<Button type="primary" ghost>
</Button>
</Popconfirm>,
<Divider type="vertical" />,
<Popconfirm
title="转至待补发"
description="确认转至待补发?"
onConfirm={async () => {
try {
const { success, message: errMsg } =
await ordercontrollerChangestatus(
{
id: record.id, id: record.id,
}); },
if (!success) { {
throw new Error(errMsg); status: 'pending_reshipment',
} },
tableRef.current?.reload(); );
} catch (error: any) { if (!success) {
message.error(error.message); throw new Error(errMsg);
} }
}} tableRef.current?.reload();
> } catch (error: any) {
<Button type="primary" ghost> message.error(error.message);
}
</Button> }}
</Popconfirm>, >
<Divider type="vertical" />, <Button type="primary" ghost>
<Popconfirm
title="转至退款" </Button>
description="确认转至退款?" </Popconfirm>,
onConfirm={async () => { ]
try {
if (!record.id) {
message.error('订单ID不存在');
return;
}
const { success, message: errMsg } =
await ordercontrollerRefundorder({
id: record.id,
});
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
}
}}
>
<Button type="primary" ghost>
退
</Button>
</Popconfirm>,
<Divider type="vertical" />,
<Popconfirm
title="转至完成"
description="确认转至完成?"
onConfirm={async () => {
try {
if (!record.id) {
message.error('订单ID不存在');
return;
}
const { success, message: errMsg } =
await ordercontrollerCompletedorder({
id: record.id,
});
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
}
}}
>
<Button type="primary" ghost>
</Button>
</Popconfirm>,
<Divider type="vertical" />,
<Popconfirm
title="转至待补发"
description="确认转至待补发?"
onConfirm={async () => {
try {
const { success, message: errMsg } =
await ordercontrollerChangestatus(
{
id: record.id,
},
{
status: 'pending_reshipment',
},
);
if (!success) {
throw new Error(errMsg);
}
tableRef.current?.reload();
} catch (error: any) {
message.error(error.message);
}
}}
>
<Button type="primary" ghost>
</Button>
</Popconfirm>,
]
: []), : []),
]} ]}
> >
@ -1071,31 +1082,31 @@ const Detail: React.FC<{
} }
actions={ actions={
v.state === 'waiting-for-scheduling' || v.state === 'waiting-for-scheduling' ||
v.state === 'waiting-for-transit' v.state === 'waiting-for-transit'
? [ ? [
<Popconfirm <Popconfirm
title="取消运单" title="取消运单"
description="确认取消运单?" description="确认取消运单?"
onConfirm={async () => { onConfirm={async () => {
try { try {
const { success, message: errMsg } = const { success, message: errMsg } =
await logisticscontrollerDelshipment({ await logisticscontrollerDelshipment({
id: v.id, id: v.id,
}); });
if (!success) { if (!success) {
throw new Error(errMsg); throw new Error(errMsg);
}
tableRef.current?.reload();
initRequest();
} catch (error: any) {
message.error(error.message);
} }
}} tableRef.current?.reload();
> initRequest();
<DeleteFilled /> } catch (error: any) {
message.error(error.message);
</Popconfirm>, }
] }}
>
<DeleteFilled />
</Popconfirm>,
]
: [] : []
} }
> >
@ -1483,16 +1494,16 @@ const Shipping: React.FC<{
<ProFormList <ProFormList
label="发货产品" label="发货产品"
name="sales" name="sales"
// rules={[ // rules={[
// { // {
// required: true, // required: true,
// message: '至少需要一个商品', // message: '至少需要一个商品',
// validator: (_, value) => // validator: (_, value) =>
// value && value.length > 0 // value && value.length > 0
// ? Promise.resolve() // ? Promise.resolve()
// : Promise.reject('至少需要一个商品'), // : Promise.reject('至少需要一个商品'),
// }, // },
// ]} // ]}
> >
<ProForm.Group> <ProForm.Group>
<ProFormSelect <ProFormSelect
@ -1917,7 +1928,7 @@ const Shipping: React.FC<{
name="description" name="description"
placeholder="请输入描述" placeholder="请输入描述"
width="lg" width="lg"
// rules={[{ required: true, message: '请输入描述' }]} // rules={[{ required: true, message: '请输入描述' }]}
/> />
</ProForm.Group> </ProForm.Group>
</ProFormList> </ProFormList>

View File

@ -452,7 +452,7 @@ declare namespace API {
/** 站点ID */ /** 站点ID */
site_id?: number; site_id?: number;
/** 原始ID */ /** 原始ID */
origin_id?: number; origin_id?: string;
/** 站点创建时间 */ /** 站点创建时间 */
site_created_at?: string; site_created_at?: string;
/** 站点更新时间 */ /** 站点更新时间 */
@ -988,6 +988,7 @@ declare namespace API {
purchaseType?: 'all' | 'first_purchase' | 'repeat_purchase'; purchaseType?: 'all' | 'first_purchase' | 'repeat_purchase';
orderType?: 'all' | 'cpc' | 'non_cpc'; orderType?: 'all' | 'cpc' | 'non_cpc';
brand?: 'all' | 'zyn' | 'yoone' | 'zolt'; brand?: 'all' | 'zyn' | 'yoone' | 'zolt';
grouping?: 'day' | 'week' | 'month';
}; };
type OrderStatusCountDTO = { type OrderStatusCountDTO = {
@ -1156,6 +1157,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1669,6 +1674,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1683,6 +1692,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1697,6 +1710,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1711,6 +1728,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1725,6 +1746,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1739,6 +1764,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1758,6 +1787,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1778,6 +1811,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1796,6 +1833,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1820,6 +1861,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1844,6 +1889,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1858,6 +1907,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1872,6 +1925,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -1891,6 +1948,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -2398,6 +2459,8 @@ declare namespace API {
email?: string; email?: string;
/** 电话 */ /** 电话 */
phone?: string; phone?: string;
/** 配送方式 */
method_title?: string;
}; };
type UnifiedCategoryDTO = { type UnifiedCategoryDTO = {
@ -2535,6 +2598,8 @@ declare namespace API {
status?: string; status?: string;
/** 货币 */ /** 货币 */
currency?: string; currency?: string;
/** 货币符号 */
currency_symbol?: string;
/** 总金额 */ /** 总金额 */
total?: string; total?: string;
/** 客户ID */ /** 客户ID */
@ -2543,6 +2608,8 @@ declare namespace API {
customer_name?: string; customer_name?: string;
/** 客户邮箱 */ /** 客户邮箱 */
email?: string; email?: string;
/** 客户邮箱 */
customer_email?: string;
/** 订单项(具体的商品) */ /** 订单项(具体的商品) */
line_items?: UnifiedOrderLineItemDTO[]; line_items?: UnifiedOrderLineItemDTO[];
/** 销售项(兼容前端) */ /** 销售项(兼容前端) */
@ -2573,6 +2640,16 @@ declare namespace API {
coupon_lines?: UnifiedCouponLineDTO[]; coupon_lines?: UnifiedCouponLineDTO[];
/** 物流追踪信息 */ /** 物流追踪信息 */
tracking?: UnifiedOrderTrackingDTO[]; tracking?: UnifiedOrderTrackingDTO[];
/** 支付时间 */
date_paid?: string;
/** 客户IP地址 */
customer_ip_address?: string;
/** UTM来源 */
utm_source?: string;
/** 设备类型 */
device_type?: string;
/** 来源类型 */
source_type?: string;
}; };
type UnifiedOrderLineItemDTO = { type UnifiedOrderLineItemDTO = {
@ -2801,6 +2878,10 @@ declare namespace API {
page?: number; page?: number;
/** 每页数量 */ /** 每页数量 */
per_page?: number; per_page?: number;
/** 查询时间范围开始 */
after?: string;
/** 查询时间范围结束 */
before?: string;
/** 搜索关键词 */ /** 搜索关键词 */
search?: string; search?: string;
/** 过滤条件对象 */ /** 过滤条件对象 */
@ -2916,7 +2997,7 @@ declare namespace API {
/** 站点ID */ /** 站点ID */
site_id?: number; site_id?: number;
/** 原始ID */ /** 原始ID */
origin_id?: number; origin_id?: string;
/** 邮箱 */ /** 邮箱 */
email?: string; email?: string;
/** 名字 */ /** 名字 */