forked from yoone/WEB
feat(客户管理): 添加历史订单组件和地址展示优化
- 新增HistoryOrders组件用于展示客户历史订单及统计信息 - 创建Address组件统一处理地址展示逻辑 - 优化客户列表页的地址展示和操作列 - 更新API类型定义和批量操作接口 - 调整代码格式和样式
This commit is contained in:
parent
8524cc1ec0
commit
96cc6d3dda
|
|
@ -16,6 +16,7 @@ export default defineConfig({
|
|||
layout: {
|
||||
title: 'YOONE',
|
||||
},
|
||||
esbuildMinifyIIFE: true,
|
||||
define: {
|
||||
UMI_APP_API_URL,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
|
||||
interface AddressProps {
|
||||
address: {
|
||||
address_1?: string;
|
||||
address_2?: string;
|
||||
city?: string;
|
||||
state?: string;
|
||||
postcode?: string;
|
||||
country?: string;
|
||||
phone?: string;
|
||||
};
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Address: React.FC<AddressProps> = ({ address, style }) => {
|
||||
if (!address) {
|
||||
return <span>-</span>;
|
||||
}
|
||||
|
||||
const { address_1, address_2, city, state, postcode, country, phone } =
|
||||
address;
|
||||
|
||||
return (
|
||||
<div style={{ fontSize: 12, ...style }}>
|
||||
<div>
|
||||
{address_1} {address_2}
|
||||
</div>
|
||||
<div>
|
||||
{city}, {state}, {postcode}
|
||||
</div>
|
||||
<div>{country}</div>
|
||||
<div>{phone}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Address;
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
import { ordercontrollerGetorders } from '@/servers/api/order';
|
||||
import { siteapicontrollerGetorders } from '@/servers/api/siteApi';
|
||||
import {
|
||||
App,
|
||||
Col,
|
||||
Modal,
|
||||
Row,
|
||||
Spin,
|
||||
Statistic,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useState } from 'react';
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
|
||||
interface HistoryOrdersProps {
|
||||
customer: API.UnifiedCustomerDTO;
|
||||
siteId?: number;
|
||||
}
|
||||
|
||||
interface OrderStats {
|
||||
totalOrders: number;
|
||||
totalAmount: number;
|
||||
yooneOrders: number;
|
||||
yooneAmount: number;
|
||||
}
|
||||
|
||||
const HistoryOrders: React.FC<HistoryOrdersProps> = ({ customer, siteId }) => {
|
||||
const { message } = App.useApp();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [orders, setOrders] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [stats, setStats] = useState<OrderStats>({
|
||||
totalOrders: 0,
|
||||
totalAmount: 0,
|
||||
yooneOrders: 0,
|
||||
yooneAmount: 0,
|
||||
});
|
||||
|
||||
// 计算订单统计信息
|
||||
const calculateStats = (orders: any[]) => {
|
||||
let totalOrders = 0;
|
||||
let totalAmount = 0;
|
||||
let yooneOrders = 0;
|
||||
let yooneAmount = 0;
|
||||
|
||||
orders.forEach((order) => {
|
||||
totalOrders++;
|
||||
// total是字符串,需要转换为数字
|
||||
const orderTotal = parseFloat(order.total || '0');
|
||||
totalAmount += orderTotal;
|
||||
|
||||
// 检查订单中是否包含yoone商品
|
||||
let hasYoone = false;
|
||||
let orderYooneAmount = 0;
|
||||
|
||||
// 优先使用line_items,如果没有则使用items
|
||||
const items = order.line_items || order.items || [];
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach((item: any) => {
|
||||
// 检查商品名称或SKU是否包含yoone(不区分大小写)
|
||||
const itemName = (item.name || '').toLowerCase();
|
||||
const sku = (item.sku || '').toLowerCase();
|
||||
|
||||
if (itemName.includes('yoone') || sku.includes('yoone')) {
|
||||
hasYoone = true;
|
||||
const itemTotal = parseFloat(item.total || item.price || '0');
|
||||
orderYooneAmount += itemTotal;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (hasYoone) {
|
||||
yooneOrders++;
|
||||
yooneAmount += orderYooneAmount;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
totalOrders,
|
||||
totalAmount,
|
||||
yooneOrders,
|
||||
yooneAmount,
|
||||
};
|
||||
};
|
||||
|
||||
// 获取客户订单数据
|
||||
const fetchOrders = async () => {
|
||||
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await ordercontrollerGetorders({
|
||||
customer_email: customer.email,
|
||||
});
|
||||
|
||||
if (response) {
|
||||
const orderList = response.items || [];
|
||||
setOrders(orderList);
|
||||
const calculatedStats = calculateStats(orderList);
|
||||
setStats(calculatedStats);
|
||||
} else {
|
||||
message.error('获取订单数据失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取订单失败:', error);
|
||||
message.error('获取订单失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 打开弹框时获取数据
|
||||
const handleOpenModal = () => {
|
||||
setModalVisible(true);
|
||||
fetchOrders();
|
||||
};
|
||||
|
||||
// 订单表格列配置
|
||||
const orderColumns = [
|
||||
{
|
||||
title: '订单号',
|
||||
dataIndex: 'externalOrderId',
|
||||
key: 'externalOrderId',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '订单状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
render: (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
pending: '待处理',
|
||||
processing: '处理中',
|
||||
'on-hold': '等待中',
|
||||
completed: '已完成',
|
||||
cancelled: '已取消',
|
||||
refunded: '已退款',
|
||||
failed: '失败',
|
||||
};
|
||||
return <Tag color="blue">{statusMap[status] || status}</Tag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '订单金额',
|
||||
dataIndex: 'total',
|
||||
key: 'total',
|
||||
width: 100,
|
||||
render: (total: string, record: any) => (
|
||||
<Text>
|
||||
{record.currency_symbol || '$'}
|
||||
{parseFloat(total || '0').toFixed(2)}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'date_created',
|
||||
key: 'date_created',
|
||||
width: 140,
|
||||
render: (date: string) => (
|
||||
<Text>{date ? dayjs(date).format('YYYY-MM-DD HH:mm') : '-'}</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '包含Yoone',
|
||||
key: 'hasYoone',
|
||||
width: 80,
|
||||
render: (_: any, record: any) => {
|
||||
let hasYoone = false;
|
||||
const items = record.line_items || record.items || [];
|
||||
if (Array.isArray(items)) {
|
||||
hasYoone = items.some((item: any) => {
|
||||
const itemName = (item.name || '').toLowerCase();
|
||||
const sku = (item.sku || '').toLowerCase();
|
||||
return itemName.includes('yoone') || sku.includes('yoone');
|
||||
});
|
||||
}
|
||||
return hasYoone ? <Tag color="green">是</Tag> : <Tag>否</Tag>;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<a onClick={handleOpenModal}>历史订单</a>
|
||||
|
||||
<Modal
|
||||
title={`${customer.fullname || customer.email} 的历史订单`}
|
||||
open={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
footer={null}
|
||||
width={1000}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
{/* 统计信息 */}
|
||||
<Row gutter={16} style={{ marginBottom: 24 }}>
|
||||
<Col span={6}>
|
||||
<Statistic
|
||||
title="总订单数"
|
||||
value={stats.totalOrders}
|
||||
prefix="#"
|
||||
/>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Statistic
|
||||
title="总金额"
|
||||
value={stats.totalAmount}
|
||||
precision={2}
|
||||
prefix="$"
|
||||
/>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Statistic
|
||||
title="Yoone订单数"
|
||||
value={stats.yooneOrders}
|
||||
prefix="#"
|
||||
/>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Statistic
|
||||
title="Yoone金额"
|
||||
value={stats.yooneAmount}
|
||||
precision={2}
|
||||
prefix="$"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* 订单列表 */}
|
||||
<Title level={4} style={{ marginTop: 24 }}>
|
||||
订单详情
|
||||
</Title>
|
||||
<Table
|
||||
columns={orderColumns}
|
||||
dataSource={orders}
|
||||
rowKey="id"
|
||||
pagination={{
|
||||
pageSize: 10,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
}}
|
||||
scroll={{ x: 800 }}
|
||||
/>
|
||||
</Spin>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HistoryOrders;
|
||||
|
|
@ -16,8 +16,8 @@ import {
|
|||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { App, Avatar, Button, Rate, Space, Tag, Tooltip } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import HistoryOrders from './HistoryOrders';
|
||||
|
||||
// 地址格式化函数
|
||||
const formatAddress = (address: any) => {
|
||||
|
|
@ -42,7 +42,7 @@ const formatAddress = (address: any) => {
|
|||
postcode,
|
||||
country,
|
||||
phone: addressPhone,
|
||||
email: addressEmail
|
||||
email: addressEmail,
|
||||
} = address;
|
||||
|
||||
const parts = [];
|
||||
|
|
@ -73,7 +73,10 @@ const formatAddress = (address: any) => {
|
|||
};
|
||||
|
||||
// 地址卡片组件
|
||||
const AddressCell: React.FC<{ address: any; title: string }> = ({ address, title }) => {
|
||||
const AddressCell: React.FC<{ address: any; title: string }> = ({
|
||||
address,
|
||||
title,
|
||||
}) => {
|
||||
const formattedAddress = formatAddress(address);
|
||||
|
||||
if (formattedAddress === '-') {
|
||||
|
|
@ -91,13 +94,15 @@ const AddressCell: React.FC<{ address: any; title: string }> = ({ address, title
|
|||
}
|
||||
placement="topLeft"
|
||||
>
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 200,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
cursor: 'pointer'
|
||||
}}>
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
{formattedAddress}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
|
@ -126,8 +131,11 @@ const CustomerList: React.FC = () => {
|
|||
// 根据站点ID获取站点名称
|
||||
const getSiteName = (siteId: number | undefined | null) => {
|
||||
if (!siteId) return '-';
|
||||
const site = sites.find(s => s.id === siteId);
|
||||
console.log(`site`,site)
|
||||
if (typeof siteId === 'string') {
|
||||
return siteId;
|
||||
}
|
||||
const site = sites.find((s) => s.id === siteId);
|
||||
console.log(`site`, site);
|
||||
return site ? site.name : String(siteId);
|
||||
};
|
||||
|
||||
|
|
@ -161,9 +169,8 @@ const CustomerList: React.FC = () => {
|
|||
return [];
|
||||
}
|
||||
},
|
||||
render: (siteId, record) => {
|
||||
return siteId
|
||||
return getSiteName(record.site_id) || '-';
|
||||
render: (siteId: any) => {
|
||||
return <span>{getSiteName(siteId) || '-'}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -187,8 +194,8 @@ const CustomerList: React.FC = () => {
|
|||
sorter: true,
|
||||
render: (_, record) => {
|
||||
return (
|
||||
record.fullName ||
|
||||
`${record.firstName || ''} ${record.lastName || ''}`.trim() ||
|
||||
record.fullname ||
|
||||
`${record.first_name || ''} ${record.last_name || ''}`.trim() ||
|
||||
record.username ||
|
||||
'-'
|
||||
);
|
||||
|
|
@ -228,6 +235,7 @@ const CustomerList: React.FC = () => {
|
|||
title: '评分',
|
||||
dataIndex: 'rate',
|
||||
width: 120,
|
||||
hideInSearch: true,
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Rate
|
||||
|
|
@ -246,7 +254,7 @@ const CustomerList: React.FC = () => {
|
|||
message.error(e?.message || '设置评分失败');
|
||||
}
|
||||
}}
|
||||
value={record.rate}
|
||||
value={record.raw?.rate || 0}
|
||||
allowHalf
|
||||
/>
|
||||
);
|
||||
|
|
@ -257,9 +265,10 @@ const CustomerList: React.FC = () => {
|
|||
dataIndex: 'tags',
|
||||
hideInSearch: true,
|
||||
render: (_, record) => {
|
||||
const tags = record.raw?.tags || [];
|
||||
return (
|
||||
<Space size={[0, 8]} wrap>
|
||||
{(record.tags || []).map((tag: string) => {
|
||||
{tags.map((tag: string) => {
|
||||
return (
|
||||
<Tag
|
||||
key={tag}
|
||||
|
|
@ -302,7 +311,6 @@ const CustomerList: React.FC = () => {
|
|||
hideInSearch: true,
|
||||
sorter: true,
|
||||
width: 140,
|
||||
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
|
|
@ -314,20 +322,12 @@ const CustomerList: React.FC = () => {
|
|||
return (
|
||||
<Space direction="vertical" size="small">
|
||||
<AddTag
|
||||
email={record.email}
|
||||
tags={record.tags}
|
||||
email={record.email || ''}
|
||||
tags={record.raw?.tags || []}
|
||||
tableRef={actionRef}
|
||||
/>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
// 这里可以添加查看客户详情的逻辑
|
||||
message.info('客户详情功能开发中...');
|
||||
}}
|
||||
>
|
||||
详情
|
||||
</Button>
|
||||
{/* 订单 */}
|
||||
<HistoryOrders customer={record} siteId={record.raw?.site_id} />
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
|
|
@ -347,7 +347,9 @@ const CustomerList: React.FC = () => {
|
|||
...params,
|
||||
current: params.current?.toString(),
|
||||
pageSize: params.pageSize?.toString(),
|
||||
...(key ? { sorterKey: key, sorterValue: sorter[key] as string } : {}),
|
||||
...(key
|
||||
? { sorterKey: key, sorterValue: sorter[key] as string }
|
||||
: {}),
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -488,7 +490,11 @@ const SyncCustomersModal: React.FC<{
|
|||
const handleSync = async (values: { siteId: number }) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { success, message: msg, data } = await customercontrollerSynccustomers({
|
||||
const {
|
||||
success,
|
||||
message: msg,
|
||||
data,
|
||||
} = await customercontrollerSynccustomers({
|
||||
siteId: values.siteId,
|
||||
});
|
||||
|
||||
|
|
@ -500,7 +506,7 @@ const SyncCustomersModal: React.FC<{
|
|||
synced = 0,
|
||||
created = 0,
|
||||
updated = 0,
|
||||
errors = []
|
||||
errors = [],
|
||||
} = result;
|
||||
|
||||
let resultMessage = `同步完成!共处理 ${total} 个客户:`;
|
||||
|
|
@ -516,20 +522,24 @@ const SyncCustomersModal: React.FC<{
|
|||
<div>
|
||||
<div>{resultMessage}</div>
|
||||
<div style={{ marginTop: 8, fontSize: 12, color: '#faad14' }}>
|
||||
失败详情:{errors.slice(0, 3).map((err: any) => err.email || err.error).join(', ')}
|
||||
失败详情:
|
||||
{errors
|
||||
.slice(0, 3)
|
||||
.map((err: any) => err.email || err.error)
|
||||
.join(', ')}
|
||||
{errors.length > 3 && ` 等 ${errors.length - 3} 个错误...`}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
duration: 8,
|
||||
key: 'sync-result'
|
||||
key: 'sync-result',
|
||||
});
|
||||
} else {
|
||||
// 完全成功
|
||||
message.success({
|
||||
content: resultMessage,
|
||||
duration: 4,
|
||||
key: 'sync-result'
|
||||
key: 'sync-result',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import Address from '@/components/Address';
|
||||
import {
|
||||
DeleteFilled,
|
||||
EditOutlined,
|
||||
|
|
@ -187,19 +188,15 @@ const CustomerPage: React.FC = () => {
|
|||
hideInSearch: true,
|
||||
render: (_, record) => {
|
||||
const { billing } = record;
|
||||
if (!billing) return '-';
|
||||
return (
|
||||
<div style={{ fontSize: 12 }}>
|
||||
<div>
|
||||
{billing.address_1} {billing.address_2}
|
||||
</div>
|
||||
<div>
|
||||
{billing.city}, {billing.state}, {billing.postcode}
|
||||
</div>
|
||||
<div>{billing.country}</div>
|
||||
<div>{billing.phone}</div>
|
||||
</div>
|
||||
);
|
||||
return <Address address={billing} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '物流地址',
|
||||
dataIndex: 'shipping',
|
||||
hideInSearch: true,
|
||||
render: (shipping) => {
|
||||
return <Address address={shipping} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -239,7 +239,10 @@ const WpToolPage: React.FC = () => {
|
|||
useEffect(() => {
|
||||
const fetchAllConfigs = async () => {
|
||||
try {
|
||||
message.loading({ content: '正在加载字典配置...', key: 'loading-config' });
|
||||
message.loading({
|
||||
content: '正在加载字典配置...',
|
||||
key: 'loading-config',
|
||||
});
|
||||
|
||||
// 1. 获取所有字典列表以找到对应的 ID
|
||||
const dictListResponse = await request('/dict/list');
|
||||
|
|
@ -303,13 +306,22 @@ const WpToolPage: React.FC = () => {
|
|||
message.success({ content: '字典配置加载成功', key: 'loading-config' });
|
||||
|
||||
// 显示加载结果统计
|
||||
const totalItems = brands.length + fruitKeys.length + mintKeys.length + flavorKeys.length +
|
||||
strengthKeys.length + sizeKeys.length + humidityKeys.length + categoryKeys.length;
|
||||
const totalItems =
|
||||
brands.length +
|
||||
fruitKeys.length +
|
||||
mintKeys.length +
|
||||
flavorKeys.length +
|
||||
strengthKeys.length +
|
||||
sizeKeys.length +
|
||||
humidityKeys.length +
|
||||
categoryKeys.length;
|
||||
console.log(`字典配置加载完成: 共 ${totalItems} 个配置项`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch configs:', error);
|
||||
message.error({ content: '获取字典配置失败,请刷新页面重试', key: 'loading-config' });
|
||||
message.error({
|
||||
content: '获取字典配置失败,请刷新页面重试',
|
||||
key: 'loading-config',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -262,19 +262,22 @@ export async function siteapicontrollerCreateorder(
|
|||
export async function siteapicontrollerBatchorders(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerBatchordersParams,
|
||||
body: Record<string, any>,
|
||||
body: API.BatchOperationDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { siteId: param0, ...queryParams } = params;
|
||||
return request<Record<string, any>>(`/site-api/${param0}/orders/batch`, {
|
||||
return request<API.BatchOperationResultDTO>(
|
||||
`/site-api/${param0}/orders/batch`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 POST /site-api/${param0}/orders/batch-ship */
|
||||
|
|
@ -381,19 +384,22 @@ export async function siteapicontrollerCreateproduct(
|
|||
export async function siteapicontrollerBatchproducts(
|
||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||
params: API.siteapicontrollerBatchproductsParams,
|
||||
body: Record<string, any>,
|
||||
body: API.BatchOperationDTO,
|
||||
options?: { [key: string]: any },
|
||||
) {
|
||||
const { siteId: param0, ...queryParams } = params;
|
||||
return request<Record<string, any>>(`/site-api/${param0}/products/batch`, {
|
||||
return request<API.BatchOperationResultDTO>(
|
||||
`/site-api/${param0}/products/batch`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
params: { ...queryParams },
|
||||
data: body,
|
||||
...(options || {}),
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** 此处后端没有提供注释 GET /site-api/${param0}/products/export */
|
||||
|
|
|
|||
|
|
@ -40,6 +40,39 @@ declare namespace API {
|
|||
ids: any[];
|
||||
};
|
||||
|
||||
type BatchErrorItemDTO = {
|
||||
/** 错误项标识(如ID、邮箱等) */
|
||||
identifier?: string;
|
||||
/** 错误信息 */
|
||||
error?: string;
|
||||
};
|
||||
|
||||
type BatchOperationDTO = {
|
||||
/** 要创建的数据列表 */
|
||||
create?: any[];
|
||||
/** 要更新的数据列表 */
|
||||
update?: any[];
|
||||
/** 要删除的ID列表 */
|
||||
delete?: string[];
|
||||
};
|
||||
|
||||
type BatchOperationResultDTO = {
|
||||
/** 总处理数量 */
|
||||
total?: number;
|
||||
/** 成功处理数量 */
|
||||
processed?: number;
|
||||
/** 创建数量 */
|
||||
created?: number;
|
||||
/** 更新数量 */
|
||||
updated?: number;
|
||||
/** 删除数量 */
|
||||
deleted?: number;
|
||||
/** 跳过的数量 */
|
||||
skipped?: number;
|
||||
/** 错误列表 */
|
||||
errors?: BatchErrorItemDTO[];
|
||||
};
|
||||
|
||||
type BatchShipOrderItemDTO = {
|
||||
/** 订单ID */
|
||||
order_id?: string;
|
||||
|
|
|
|||
Loading…
Reference in New Issue