Files
makemd/dashboard/src/pages/Orders/OrderList.tsx
wurenzhi aa2cf560c6 feat: 添加汇率服务和缓存服务,优化数据源和日志服务
refactor: 重构数据源工厂和类型定义,提升代码可维护性

fix: 修复类型转换和状态机文档中的错误

docs: 更新服务架构文档,添加新的服务闭环流程

test: 添加汇率服务单元测试

chore: 清理无用代码和注释,优化代码结构
2026-03-19 14:19:01 +08:00

1085 lines
35 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState, useEffect, useCallback } from 'react';
import {
Card,
Table,
Tag,
Button,
Space,
Modal,
Form,
Input,
Select,
DatePicker,
message,
Tooltip,
Row,
Col,
Statistic,
Badge,
Dropdown,
Menu,
Tabs,
InputNumber,
Timeline,
Alert,
Drawer,
Divider,
Typography,
Image,
Steps,
} from 'antd';
import {
EyeOutlined,
SyncOutlined,
CheckCircleOutlined,
ClockCircleOutlined,
TruckOutlined,
CloseCircleOutlined,
MoreOutlined,
SearchOutlined,
ExportOutlined,
WarningOutlined,
FileTextOutlined,
EnvironmentOutlined,
CheckOutlined,
UndoOutlined,
CustomerServiceOutlined,
DollarOutlined,
FilterOutlined,
SortAscendingOutlined,
PrinterOutlined,
MessageOutlined,
ExclamationCircleOutlined,
} from '@ant-design/icons';
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
import type { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/es/table/interface';
import moment from 'moment';
const { Title, Text } = Typography;
const { Option } = Select;
const { RangePicker } = DatePicker;
const { TabPane } = Tabs;
const { Step } = Steps;
const { Search } = Input;
interface Order {
id: string;
orderId: string;
platform: 'AMAZON' | 'EBAY' | 'SHOPIFY' | 'SHOPEE' | 'LAZADA';
customerName: string;
customerEmail: string;
customerPhone: string;
customerAddress: string;
totalAmount: number;
currency: string;
status: 'PENDING' | 'CONFIRMED' | 'SHIPPED' | 'DELIVERED' | 'COMPLETED' | 'CANCELLED' | 'REFUNDING' | 'REFUNDED';
paymentStatus: 'PENDING' | 'PAID' | 'REFUNDED';
itemCount: number;
createdAt: string;
updatedAt: string;
shopId: string;
shopName: string;
trackingNumber?: string;
carrier?: string;
estimatedDelivery?: string;
actualDelivery?: string;
exceptionReason?: string;
items: OrderItem[];
}
interface OrderItem {
id: string;
productName: string;
sku: string;
quantity: number;
price: number;
image: string;
}
interface FilterState {
keyword: string;
status: string[];
platform: string[];
paymentStatus: string[];
dateRange: [moment.Moment, moment.Moment] | null;
}
interface SortState {
field: string;
order: 'ascend' | 'descend' | null;
}
const STATUS_CONFIG: Record<string, { color: string; text: string; icon: React.ReactNode; step: number }> = {
PENDING: { color: 'default', text: '待处理', icon: <ClockCircleOutlined />, step: 0 },
CONFIRMED: { color: 'blue', text: '已确认', icon: <CheckCircleOutlined />, step: 1 },
SHIPPED: { color: 'processing', text: '已发货', icon: <TruckOutlined />, step: 2 },
DELIVERED: { color: 'cyan', text: '已送达', icon: <EnvironmentOutlined />, step: 3 },
COMPLETED: { color: 'success', text: '已完成', icon: <CheckCircleOutlined />, step: 4 },
CANCELLED: { color: 'error', text: '已取消', icon: <CloseCircleOutlined />, step: -1 },
REFUNDING: { color: 'warning', text: '退款中', icon: <DollarOutlined />, step: -1 },
REFUNDED: { color: 'default', text: '已退款', icon: <UndoOutlined />, step: -1 },
};
const PLATFORM_CONFIG: Record<string, { color: string; text: string }> = {
AMAZON: { color: 'orange', text: 'Amazon' },
EBAY: { color: 'blue', text: 'eBay' },
SHOPIFY: { color: 'green', text: 'Shopify' },
SHOPEE: { color: 'red', text: 'Shopee' },
LAZADA: { color: 'purple', text: 'Lazada' },
};
const MOCK_ORDERS: Order[] = [
{
id: '1',
orderId: 'ORD-2026-001',
platform: 'AMAZON',
customerName: '张三',
customerEmail: 'zhangsan@email.com',
customerPhone: '+86-138-0000-0001',
customerAddress: '北京市朝阳区xxx街道xxx号',
totalAmount: 159.99,
currency: 'USD',
status: 'SHIPPED',
paymentStatus: 'PAID',
itemCount: 3,
createdAt: '2026-03-18 10:30:00',
updatedAt: '2026-03-18 14:00:00',
shopId: 'SHOP_001',
shopName: '主店铺',
trackingNumber: '1Z999AA10123456784',
carrier: 'UPS',
estimatedDelivery: '2026-03-22',
items: [
{ id: 'i1', productName: '工业温度传感器', sku: 'TP-TEMP-001', quantity: 2, price: 89.99, image: 'https://via.placeholder.com/60x60' },
{ id: 'i2', productName: '连接线 1m', sku: 'TP-CABLE-001', quantity: 1, price: 19.99, image: 'https://via.placeholder.com/60x60' },
],
},
{
id: '2',
orderId: 'ORD-2026-002',
platform: 'EBAY',
customerName: '李四',
customerEmail: 'lisi@email.com',
customerPhone: '+86-138-0000-0002',
customerAddress: '上海市浦东新区xxx路xxx号',
totalAmount: 299.99,
currency: 'USD',
status: 'PENDING',
paymentStatus: 'PAID',
itemCount: 1,
createdAt: '2026-03-18 09:15:00',
updatedAt: '2026-03-18 09:15:00',
shopId: 'SHOP_002',
shopName: 'eBay店铺',
items: [
{ id: 'i3', productName: 'PLC控制器', sku: 'TP-PLC-001', quantity: 1, price: 299.99, image: 'https://via.placeholder.com/60x60' },
],
},
{
id: '3',
orderId: 'ORD-2026-003',
platform: 'SHOPIFY',
customerName: '王五',
customerEmail: 'wangwu@email.com',
customerPhone: '+86-138-0000-0003',
customerAddress: '广州市天河区xxx大道xxx号',
totalAmount: 459.98,
currency: 'USD',
status: 'CONFIRMED',
paymentStatus: 'PAID',
itemCount: 2,
createdAt: '2026-03-17 16:45:00',
updatedAt: '2026-03-18 08:00:00',
shopId: 'SHOP_003',
shopName: '独立站',
items: [
{ id: 'i4', productName: '压力传感器', sku: 'TP-PRES-001', quantity: 2, price: 129.99, image: 'https://via.placeholder.com/60x60' },
{ id: 'i5', productName: '安装支架', sku: 'TP-BRACKET-001', quantity: 2, price: 29.99, image: 'https://via.placeholder.com/60x60' },
],
},
{
id: '4',
orderId: 'ORD-2026-004',
platform: 'SHOPEE',
customerName: '赵六',
customerEmail: 'zhaoliu@email.com',
customerPhone: '+86-138-0000-0004',
customerAddress: '深圳市南山区xxx街xxx号',
totalAmount: 89.99,
currency: 'USD',
status: 'DELIVERED',
paymentStatus: 'PAID',
itemCount: 1,
createdAt: '2026-03-15 11:20:00',
updatedAt: '2026-03-18 09:30:00',
shopId: 'SHOP_004',
shopName: 'Shopee店铺',
trackingNumber: 'SF1234567890',
carrier: '顺丰',
actualDelivery: '2026-03-18',
items: [
{ id: 'i6', productName: '温度传感器标准版', sku: 'TP-TEMP-002', quantity: 1, price: 89.99, image: 'https://via.placeholder.com/60x60' },
],
},
{
id: '5',
orderId: 'ORD-2026-005',
platform: 'AMAZON',
customerName: '钱七',
customerEmail: 'qianqi@email.com',
customerPhone: '+86-138-0000-0005',
customerAddress: '杭州市西湖区xxx路xxx号',
totalAmount: 599.99,
currency: 'USD',
status: 'COMPLETED',
paymentStatus: 'PAID',
itemCount: 3,
createdAt: '2026-03-10 14:00:00',
updatedAt: '2026-03-16 10:00:00',
shopId: 'SHOP_001',
shopName: '主店铺',
trackingNumber: '1Z888BB20234567890',
carrier: 'UPS',
actualDelivery: '2026-03-16',
items: [
{ id: 'i7', productName: '流量计', sku: 'TP-FLOW-001', quantity: 1, price: 299.99, image: 'https://via.placeholder.com/60x60' },
{ id: 'i8', productName: '显示屏', sku: 'TP-DISP-001', quantity: 2, price: 149.99, image: 'https://via.placeholder.com/60x60' },
],
},
{
id: '6',
orderId: 'ORD-2026-006',
platform: 'EBAY',
customerName: '孙八',
customerEmail: 'sunba@email.com',
customerPhone: '+86-138-0000-0006',
customerAddress: '成都市武侯区xxx街xxx号',
totalAmount: 129.99,
currency: 'USD',
status: 'CANCELLED',
paymentStatus: 'REFUNDED',
itemCount: 1,
createdAt: '2026-03-17 09:00:00',
updatedAt: '2026-03-17 15:30:00',
shopId: 'SHOP_002',
shopName: 'eBay店铺',
exceptionReason: '客户取消订单',
items: [
{ id: 'i9', productName: '电磁阀', sku: 'TP-VALVE-001', quantity: 1, price: 129.99, image: 'https://via.placeholder.com/60x60' },
],
},
{
id: '7',
orderId: 'ORD-2026-007',
platform: 'SHOPIFY',
customerName: '周九',
customerEmail: 'zhoujiu@email.com',
customerPhone: '+86-138-0000-0007',
customerAddress: '武汉市江汉区xxx路xxx号',
totalAmount: 349.99,
currency: 'USD',
status: 'REFUNDING',
paymentStatus: 'PAID',
itemCount: 2,
createdAt: '2026-03-14 13:30:00',
updatedAt: '2026-03-18 11:00:00',
shopId: 'SHOP_003',
shopName: '独立站',
exceptionReason: '商品质量问题',
items: [
{ id: 'i10', productName: '步进电机', sku: 'TP-MOTOR-001', quantity: 1, price: 199.99, image: 'https://via.placeholder.com/60x60' },
{ id: 'i11', productName: '驱动器', sku: 'TP-DRIVER-001', quantity: 1, price: 149.99, image: 'https://via.placeholder.com/60x60' },
],
},
];
export const OrderList: React.FC = () => {
const [orders, setOrders] = useState<Order[]>([]);
const [loading, setLoading] = useState(false);
const [selectedRows, setSelectedRows] = useState<Order[]>([]);
const [currentOrder, setCurrentOrder] = useState<Order | null>(null);
const [filterVisible, setFilterVisible] = useState(false);
const [sortDrawerVisible, setSortDrawerVisible] = useState(false);
const [detailDrawerVisible, setDetailDrawerVisible] = useState(false);
const [confirmModalVisible, setConfirmModalVisible] = useState(false);
const [shipModalVisible, setShipModalVisible] = useState(false);
const [cancelModalVisible, setCancelModalVisible] = useState(false);
const [afterSalesModalVisible, setAfterSalesModalVisible] = useState(false);
const [activeTab, setActiveTab] = useState('all');
const [shipForm] = Form.useForm();
const [cancelForm] = Form.useForm();
const [afterSalesForm] = Form.useForm();
const [filters, setFilters] = useState<FilterState>({
keyword: '',
status: [],
platform: [],
paymentStatus: [],
dateRange: null,
});
const [sort, setSort] = useState<SortState>({
field: 'createdAt',
order: 'descend',
});
useEffect(() => {
fetchOrders();
}, []);
const fetchOrders = useCallback(async () => {
setLoading(true);
await new Promise(resolve => setTimeout(resolve, 500));
setOrders(MOCK_ORDERS);
setLoading(false);
}, []);
const handleFilterChange = (key: keyof FilterState, value: any) => {
setFilters(prev => ({ ...prev, [key]: value }));
};
const handleSortChange = (field: string, order: 'ascend' | 'descend') => {
setSort({ field, order });
setSortDrawerVisible(false);
message.success(`已按 ${field} ${order === 'ascend' ? '升序' : '降序'} 排序`);
};
const handleTableChange = (
pagination: TablePaginationConfig,
filters: Record<string, FilterValue | null>,
sorter: SorterResult<Order> | SorterResult<Order>[],
extra: TableCurrentDataSource<Order>
) => {
if (!Array.isArray(sorter) && sorter.field) {
setSort({
field: sorter.field as string,
order: sorter.order || null,
});
}
};
const handleSearch = (value: string) => {
handleFilterChange('keyword', value);
};
const handleResetFilters = () => {
setFilters({
keyword: '',
status: [],
platform: [],
paymentStatus: [],
dateRange: null,
});
message.success('筛选条件已重置');
};
const handleConfirmOrder = (record: Order) => {
setCurrentOrder(record);
setConfirmModalVisible(true);
};
const handleConfirmSubmit = () => {
if (currentOrder) {
updateOrderStatus(currentOrder.id, 'CONFIRMED');
setConfirmModalVisible(false);
message.success('订单确认成功');
}
};
const handleShipOrder = (record: Order) => {
setCurrentOrder(record);
setShipModalVisible(true);
shipForm.resetFields();
};
const handleShipSubmit = async () => {
try {
const values = await shipForm.validateFields();
if (currentOrder) {
updateOrderStatus(currentOrder.id, 'SHIPPED');
setShipModalVisible(false);
message.success('订单发货成功');
}
} catch (error) {
console.error('Form validation failed:', error);
}
};
const handleCompleteOrder = (record: Order) => {
Modal.confirm({
title: '确认完成订单',
content: `确定要完成订单 "${record.orderId}" 吗?`,
onOk: () => {
updateOrderStatus(record.id, 'COMPLETED');
message.success('订单已完成');
},
});
};
const handleCancelOrder = (record: Order) => {
setCurrentOrder(record);
setCancelModalVisible(true);
cancelForm.resetFields();
};
const handleCancelSubmit = async () => {
try {
const values = await cancelForm.validateFields();
if (currentOrder) {
updateOrderStatus(currentOrder.id, 'CANCELLED');
setCancelModalVisible(false);
message.success('订单已取消');
}
} catch (error) {
console.error('Form validation failed:', error);
}
};
const handleAfterSales = (record: Order) => {
setCurrentOrder(record);
setAfterSalesModalVisible(true);
afterSalesForm.resetFields();
};
const handleAfterSalesSubmit = async () => {
try {
const values = await afterSalesForm.validateFields();
if (currentOrder) {
updateOrderStatus(currentOrder.id, 'REFUNDING');
setAfterSalesModalVisible(false);
message.success('售后申请已提交');
}
} catch (error) {
console.error('Form validation failed:', error);
}
};
const handleViewDetail = (record: Order) => {
setCurrentOrder(record);
setDetailDrawerVisible(true);
};
const updateOrderStatus = (orderId: string, status: Order['status']) => {
setOrders(orders.map(o =>
o.id === orderId ? { ...o, status, updatedAt: moment().format('YYYY-MM-DD HH:mm:ss') } : o
));
};
const handleTabChange = (key: string) => {
setActiveTab(key);
if (key === 'all') {
setFilters(prev => ({ ...prev, status: [] }));
} else {
setFilters(prev => ({ ...prev, status: [key.toUpperCase()] }));
}
};
const filteredOrders = orders.filter(order => {
if (filters.keyword && !order.orderId.toLowerCase().includes(filters.keyword.toLowerCase()) &&
!order.customerName.toLowerCase().includes(filters.keyword.toLowerCase())) {
return false;
}
if (filters.status.length > 0 && !filters.status.includes(order.status)) {
return false;
}
if (filters.platform.length > 0 && !filters.platform.includes(order.platform)) {
return false;
}
if (filters.paymentStatus.length > 0 && !filters.paymentStatus.includes(order.paymentStatus)) {
return false;
}
return true;
});
const sortedOrders = [...filteredOrders].sort((a, b) => {
const field = sort.field as keyof Order;
const aValue = a[field];
const bValue = b[field];
if (sort.order === 'ascend') {
return aValue > bValue ? 1 : -1;
} else {
return aValue < bValue ? 1 : -1;
}
});
const stats = {
total: orders.length,
pending: orders.filter(o => o.status === 'PENDING').length,
processing: orders.filter(o => ['CONFIRMED', 'SHIPPED'].includes(o.status)).length,
completed: orders.filter(o => o.status === 'COMPLETED').length,
exception: orders.filter(o => ['CANCELLED', 'REFUNDING', 'REFUNDED'].includes(o.status)).length,
totalAmount: orders.reduce((sum, o) => sum + o.totalAmount, 0),
};
const columns: ColumnsType<Order> = [
{
title: '订单号',
dataIndex: 'orderId',
key: 'orderId',
render: (text, record) => (
<Space direction="vertical" size={0}>
<Text strong>{text}</Text>
<Tag color={PLATFORM_CONFIG[record.platform].color} size="small">
{PLATFORM_CONFIG[record.platform].text}
</Tag>
</Space>
),
},
{
title: '客户信息',
key: 'customer',
render: (_, record) => (
<Space direction="vertical" size={0}>
<Text>{record.customerName}</Text>
<Text type="secondary" style={{ fontSize: 12 }}>{record.customerPhone}</Text>
</Space>
),
},
{
title: '金额',
dataIndex: 'totalAmount',
key: 'totalAmount',
render: (value, record) => (
<Space direction="vertical" size={0}>
<Text strong>${value.toFixed(2)}</Text>
<Text type="secondary" style={{ fontSize: 12 }}>{record.currency}</Text>
</Space>
),
sorter: true,
},
{
title: '商品数',
dataIndex: 'itemCount',
key: 'itemCount',
render: (value) => <Badge count={value} style={{ backgroundColor: '#1890ff' }} />,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status) => {
const config = STATUS_CONFIG[status];
return (
<Tag color={config.color} icon={config.icon}>
{config.text}
</Tag>
);
},
filters: Object.entries(STATUS_CONFIG).map(([key, config]) => ({
text: config.text,
value: key,
})),
},
{
title: '支付状态',
dataIndex: 'paymentStatus',
key: 'paymentStatus',
render: (status) => {
const config = {
PENDING: { color: 'warning', text: '待支付' },
PAID: { color: 'success', text: '已支付' },
REFUNDED: { color: 'default', text: '已退款' },
}[status];
return <Tag color={config.color}>{config.text}</Tag>;
},
},
{
title: '店铺',
dataIndex: 'shopName',
key: 'shopName',
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
sorter: true,
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 200,
render: (_, record) => {
const menu = (
<Menu>
<Menu.Item key="view" icon={<EyeOutlined />} onClick={() => handleViewDetail(record)}>
</Menu.Item>
{record.status === 'PENDING' && (
<Menu.Item key="confirm" icon={<CheckCircleOutlined />} onClick={() => handleConfirmOrder(record)}>
</Menu.Item>
)}
{record.status === 'CONFIRMED' && (
<Menu.Item key="ship" icon={<TruckOutlined />} onClick={() => handleShipOrder(record)}>
</Menu.Item>
)}
{record.status === 'DELIVERED' && (
<Menu.Item key="complete" icon={<CheckOutlined />} onClick={() => handleCompleteOrder(record)}>
</Menu.Item>
)}
{(record.status === 'PENDING' || record.status === 'CONFIRMED') && (
<Menu.Item key="cancel" icon={<CloseCircleOutlined />} onClick={() => handleCancelOrder(record)}>
</Menu.Item>
)}
{(record.status === 'COMPLETED' || record.status === 'DELIVERED') && (
<Menu.Item key="aftersales" icon={<CustomerServiceOutlined />} onClick={() => handleAfterSales(record)}>
</Menu.Item>
)}
<Menu.Divider />
<Menu.Item key="print" icon={<PrinterOutlined />}>
</Menu.Item>
<Menu.Item key="message" icon={<MessageOutlined />}>
</Menu.Item>
</Menu>
);
// 根据订单状态显示主要操作按钮
const getPrimaryActionButton = () => {
switch (record.status) {
case 'PENDING':
return (
<Button type="primary" size="small" onClick={() => handleConfirmOrder(record)}>
</Button>
);
case 'CONFIRMED':
return (
<Button type="primary" size="small" onClick={() => handleShipOrder(record)}>
</Button>
);
case 'DELIVERED':
return (
<Button type="primary" size="small" onClick={() => handleCompleteOrder(record)}>
</Button>
);
case 'COMPLETED':
return (
<Button type="link" size="small" onClick={() => handleAfterSales(record)}>
</Button>
);
default:
return null;
}
};
return (
<Space size="small">
<Button type="link" size="small" onClick={() => handleViewDetail(record)}>
</Button>
{getPrimaryActionButton()}
<Dropdown overlay={menu} placement="bottomRight">
<Button type="link" size="small" icon={<MoreOutlined />}>
</Button>
</Dropdown>
</Space>
);
},
},
];
const rowSelection = {
selectedRowKeys: selectedRows.map(r => r.id),
onChange: (selectedRowKeys: React.Key[], selectedRows: Order[]) => {
setSelectedRows(selectedRows);
},
};
return (
<Card>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={24}>
<Row gutter={16}>
<Col span={4}>
<Card size="small">
<Statistic title="总订单" value={stats.total} />
</Card>
</Col>
<Col span={4}>
<Card size="small">
<Statistic title="待处理" value={stats.pending} valueStyle={{ color: '#faad14' }} />
</Card>
</Col>
<Col span={4}>
<Card size="small">
<Statistic title="处理中" value={stats.processing} valueStyle={{ color: '#1890ff' }} />
</Card>
</Col>
<Col span={4}>
<Card size="small">
<Statistic title="已完成" value={stats.completed} valueStyle={{ color: '#52c41a' }} />
</Card>
</Col>
<Col span={4}>
<Card size="small">
<Statistic title="异常" value={stats.exception} valueStyle={{ color: '#ff4d4f' }} />
</Card>
</Col>
<Col span={4}>
<Card size="small">
<Statistic title="总金额" value={`$${stats.totalAmount.toFixed(2)}`} />
</Card>
</Col>
</Row>
</Col>
</Row>
<Tabs activeKey={activeTab} onChange={handleTabChange} style={{ marginBottom: 16 }}>
<TabPane tab="全部" key="all" />
<TabPane tab={`待处理 (${stats.pending})`} key="pending" />
<TabPane tab={`处理中 (${stats.processing})`} key="processing" />
<TabPane tab={`已完成 (${stats.completed})`} key="completed" />
<TabPane tab={`异常 (${stats.exception})`} key="exception" />
</Tabs>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={24}>
<Card>
<Row gutter={16}>
<Col span={12}>
<Space>
<Search
placeholder="搜索订单号或客户"
allowClear
enterButton
onSearch={handleSearch}
style={{ width: 300 }}
/>
<Button icon={<FilterOutlined />} onClick={() => setFilterVisible(true)}>
</Button>
<Button icon={<SortAscendingOutlined />} onClick={() => setSortDrawerVisible(true)}>
</Button>
</Space>
</Col>
<Col span={12} style={{ textAlign: 'right' }}>
<Space>
{selectedRows.length > 0 && (
<Space>
<Button onClick={() => message.info('批量确认功能开发中')}>
</Button>
<Button onClick={() => message.info('批量发货功能开发中')}>
</Button>
<Button danger onClick={() => message.info('批量取消功能开发中')}>
</Button>
</Space>
)}
<Button icon={<ExportOutlined />}>
</Button>
<Button icon={<SyncOutlined />} onClick={fetchOrders}>
</Button>
</Space>
</Col>
</Row>
</Card>
</Col>
</Row>
<Table
rowSelection={rowSelection}
columns={columns}
dataSource={sortedOrders}
loading={loading}
rowKey="id"
onChange={handleTableChange}
scroll={{ x: 1300 }}
pagination={{
total: sortedOrders.length,
pageSize: 10,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total}`,
pageSizeOptions: ['10', '20', '50', '100'],
}}
size="middle"
bordered
/>
<Drawer
title="筛选条件"
placement="right"
onClose={() => setFilterVisible(false)}
visible={filterVisible}
width={400}
>
<Form layout="vertical">
<Form.Item label="订单状态">
<Select
mode="multiple"
placeholder="选择状态"
value={filters.status}
onChange={(value) => handleFilterChange('status', value)}
style={{ width: '100%' }}
>
{Object.entries(STATUS_CONFIG).map(([key, config]) => (
<Option key={key} value={key}>{config.text}</Option>
))}
</Select>
</Form.Item>
<Form.Item label="平台">
<Select
mode="multiple"
placeholder="选择平台"
value={filters.platform}
onChange={(value) => handleFilterChange('platform', value)}
style={{ width: '100%' }}
>
{Object.entries(PLATFORM_CONFIG).map(([key, config]) => (
<Option key={key} value={key}>{config.text}</Option>
))}
</Select>
</Form.Item>
<Form.Item label="支付状态">
<Select
mode="multiple"
placeholder="选择支付状态"
value={filters.paymentStatus}
onChange={(value) => handleFilterChange('paymentStatus', value)}
style={{ width: '100%' }}
>
<Option value="PENDING"></Option>
<Option value="PAID"></Option>
<Option value="REFUNDED">退</Option>
</Select>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={filters.dateRange}
onChange={(dates) => handleFilterChange('dateRange', dates)}
style={{ width: '100%' }}
/>
</Form.Item>
</Form>
<div style={{ position: 'absolute', bottom: 0, left: 0, right: 0, padding: 16, borderTop: '1px solid #f0f0f0', background: '#fff' }}>
<Space style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button onClick={handleResetFilters}></Button>
<Button type="primary" onClick={() => setFilterVisible(false)}></Button>
</Space>
</div>
</Drawer>
<Drawer
title="排序设置"
placement="right"
onClose={() => setSortDrawerVisible(false)}
visible={sortDrawerVisible}
width={300}
>
<Space direction="vertical" style={{ width: '100%' }}>
{[
{ key: 'orderId', label: '订单号' },
{ key: 'totalAmount', label: '金额' },
{ key: 'itemCount', label: '商品数' },
{ key: 'createdAt', label: '创建时间' },
{ key: 'updatedAt', label: '更新时间' },
].map(item => (
<Card key={item.key} size="small" hoverable>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>{item.label}</span>
<Space>
<Button
size="small"
type={sort.field === item.key && sort.order === 'ascend' ? 'primary' : 'default'}
onClick={() => handleSortChange(item.key, 'ascend')}
>
</Button>
<Button
size="small"
type={sort.field === item.key && sort.order === 'descend' ? 'primary' : 'default'}
onClick={() => handleSortChange(item.key, 'descend')}
>
</Button>
</Space>
</div>
</Card>
))}
</Space>
</Drawer>
<Drawer
title={`订单详情 - ${currentOrder?.orderId}`}
placement="right"
onClose={() => setDetailDrawerVisible(false)}
visible={detailDrawerVisible}
width={600}
>
{currentOrder && (
<Space direction="vertical" style={{ width: '100%' }} size="large">
<Card title="订单状态">
<Steps current={STATUS_CONFIG[currentOrder.status].step} size="small">
<Step title="待处理" icon={<ClockCircleOutlined />} />
<Step title="已确认" icon={<CheckCircleOutlined />} />
<Step title="已发货" icon={<TruckOutlined />} />
<Step title="已送达" icon={<EnvironmentOutlined />} />
<Step title="已完成" icon={<CheckCircleOutlined />} />
</Steps>
</Card>
<Card title="商品信息">
{currentOrder.items.map(item => (
<div key={item.id} style={{ display: 'flex', marginBottom: 16 }}>
<Image src={item.image} width={80} height={80} style={{ marginRight: 16 }} />
<div style={{ flex: 1 }}>
<div style={{ fontWeight: 500 }}>{item.productName}</div>
<div style={{ color: '#999', fontSize: 12 }}>SKU: {item.sku}</div>
<div style={{ marginTop: 8 }}>
<Text type="secondary">: {item.quantity}</Text>
<Text strong style={{ marginLeft: 16 }}>${item.price.toFixed(2)}</Text>
</div>
</div>
</div>
))}
</Card>
<Card title="客户信息">
<p><strong>:</strong> {currentOrder.customerName}</p>
<p><strong>:</strong> {currentOrder.customerPhone}</p>
<p><strong>:</strong> {currentOrder.customerEmail}</p>
<p><strong>:</strong> {currentOrder.customerAddress}</p>
</Card>
<Card title="物流信息">
{currentOrder.trackingNumber ? (
<>
<p><strong>:</strong> {currentOrder.trackingNumber}</p>
<p><strong>:</strong> {currentOrder.carrier}</p>
{currentOrder.estimatedDelivery && (
<p><strong>:</strong> {currentOrder.estimatedDelivery}</p>
)}
{currentOrder.actualDelivery && (
<p><strong>:</strong> {currentOrder.actualDelivery}</p>
)}
</>
) : (
<Text type="secondary"></Text>
)}
</Card>
</Space>
)}
</Drawer>
<Modal
title="确认订单"
visible={confirmModalVisible}
onOk={handleConfirmSubmit}
onCancel={() => setConfirmModalVisible(false)}
>
<p> "{currentOrder?.orderId}" </p>
<p>: {currentOrder?.customerName}</p>
<p>金额: ${currentOrder?.totalAmount.toFixed(2)}</p>
</Modal>
<Modal
title="订单发货"
visible={shipModalVisible}
onOk={handleShipSubmit}
onCancel={() => setShipModalVisible(false)}
>
<Form form={shipForm} layout="vertical">
<Form.Item
name="carrier"
label="承运商"
rules={[{ required: true, message: '请选择承运商' }]}
>
<Select placeholder="选择承运商">
<Option value="UPS">UPS</Option>
<Option value="FedEx">FedEx</Option>
<Option value="DHL">DHL</Option>
<Option value="顺丰"></Option>
<Option value="中通"></Option>
</Select>
</Form.Item>
<Form.Item
name="trackingNumber"
label="物流单号"
rules={[{ required: true, message: '请输入物流单号' }]}
>
<Input placeholder="输入物流单号" />
</Form.Item>
</Form>
</Modal>
<Modal
title="取消订单"
visible={cancelModalVisible}
onOk={handleCancelSubmit}
onCancel={() => setCancelModalVisible(false)}
>
<Form form={cancelForm} layout="vertical">
<Form.Item
name="reason"
label="取消原因"
rules={[{ required: true, message: '请输入取消原因' }]}
>
<Input.TextArea rows={4} placeholder="输入取消原因" />
</Form.Item>
</Form>
</Modal>
<Modal
title="售后申请"
visible={afterSalesModalVisible}
onOk={handleAfterSalesSubmit}
onCancel={() => setAfterSalesModalVisible(false)}
width={600}
>
<Form form={afterSalesForm} layout="vertical">
<Form.Item
name="type"
label="售后类型"
rules={[{ required: true, message: '请选择售后类型' }]}
>
<Select placeholder="选择售后类型">
<Option value="return">退退</Option>
<Option value="exchange"></Option>
<Option value="refund">退</Option>
</Select>
</Form.Item>
<Form.Item
name="reason"
label="售后原因"
rules={[{ required: true, message: '请输入售后原因' }]}
>
<Input.TextArea rows={4} placeholder="输入售后原因" />
</Form.Item>
<Form.Item
name="amount"
label="退款金额"
rules={[{ required: true, message: '请输入退款金额' }]}
>
<InputNumber
min={0}
max={currentOrder?.totalAmount}
precision={2}
style={{ width: '100%' }}
placeholder="输入退款金额"
/>
</Form.Item>
</Form>
</Modal>
</Card>
);
};
export default OrderList;