Files
makemd/dashboard/src/pages/Orders/OrderList.tsx
wurenzhi 1b14947e7b refactor: 优化代码结构和类型定义
feat(types): 添加express.d.ts类型引用
style: 格式化express.d.ts中的接口定义
refactor: 移除未使用的AntFC类型导入
chore: 删除自动生成的.umi-production文件
feat: 添加店铺管理相关表和初始化脚本
docs: 更新安全规则和交互指南文档
refactor: 统一使用FC类型替代React.FC
perf: 优化图表组件导入方式
style: 添加.prettierrc配置文件
refactor: 调整组件导入顺序和结构
feat: 添加平台库存管理路由
fix: 修复订单同步时的库存检查逻辑
docs: 更新RBAC设计和租户管理文档
refactor: 优化部门控制器代码
2026-03-30 01:20:57 +08:00

1529 lines
53 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 { useState, useEffect, useCallback, useMemo, moment, FC } from '@/imports';
import {
Card,
Table,
Tag,
Button,
Space,
Modal,
Form,
Input,
Select,
DatePicker,
message,
Row,
Col,
Statistic,
Badge,
Dropdown,
Menu,
Tabs,
InputNumber,
Timeline,
Alert,
Drawer,
Divider,
Typography,
Image,
Steps,
Line,
LineChart,
Pie,
Bar,
BarChart,
ResponsiveContainer,
Tooltip,
Legend,
XAxis,
YAxis,
CartesianGrid,
Cell,
EyeOutlined,
SyncOutlined,
CheckCircleOutlined,
ClockCircleOutlined,
TruckOutlined,
CloseCircleOutlined,
MoreOutlined,
SearchOutlined,
ExportOutlined,
WarningOutlined,
FileTextOutlined,
EnvironmentOutlined,
CheckOutlined,
UndoOutlined,
CustomerServiceOutlined,
DollarOutlined,
FilterOutlined,
SortAscendingOutlined,
PrinterOutlined,
MessageOutlined,
ExclamationCircleOutlined,
AmazonOutlined,
GlobalOutlined,
ShopOutlined,
AppstoreOutlined,
Title,
Text,
Option,
RangePicker,
Step,
Search,
ColumnsType,
TablePaginationConfig,
FilterValue,
SorterResult,
TableCurrentDataSource,
} from '@/imports';
interface Order {
id: string;
orderId: string;
platform: string;
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 Shop {
id: string;
name: string;
platform: string;
status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';
region: string;
currency: string;
}
interface OrderItem {
id: string;
productName: string;
sku: string;
quantity: number;
price: number;
image: string;
}
interface FilterState {
keyword: string;
status: string[];
platform: string[];
shop: string[];
paymentStatus: string[];
dateRange: any;
}
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; icon: React.ReactNode }> = {
// TikTok系列
TIKTOK: { color: 'cyan', text: 'TikTok', icon: <ShopOutlined /> },
TIKTOK_FULL: { color: 'cyan', text: 'TikTok全托管', icon: <ShopOutlined /> },
// Shopee系列
SHOPEE: { color: 'red', text: 'Shopee', icon: <ShopOutlined /> },
SHOPEE_FULL: { color: 'red', text: 'Shopee全托管', icon: <ShopOutlined /> },
SHOPEE_LIGHT: { color: 'red', text: 'Shopee轻出海', icon: <ShopOutlined /> },
// Lazada系列
LAZADA: { color: 'purple', text: 'Lazada', icon: <ShopOutlined /> },
LAZADA_FULL: { color: 'purple', text: 'Lazada全托管', icon: <ShopOutlined /> },
// Temu
TEMU_FULL: { color: 'green', text: 'Temu全托管', icon: <ShopOutlined /> },
// SHEIN系列
SHEIN: { color: 'pink', text: 'SHEIN', icon: <ShopOutlined /> },
SHEIN_HALF: { color: 'pink', text: 'SHEIN半托管', icon: <ShopOutlined /> },
// 其他平台
OZON: { color: 'yellow', text: 'Ozon', icon: <ShopOutlined /> },
YANDEX: { color: 'blue', text: 'Yandex', icon: <ShopOutlined /> },
ALIEXPRESS: { color: 'orange', text: 'AliExpress', icon: <ShopOutlined /> },
ALIEXPRESS_HALF: { color: 'orange', text: '速卖通半托管', icon: <ShopOutlined /> },
ALIEXPRESS_POP: { color: 'orange', text: '速卖通本土POP', icon: <ShopOutlined /> },
COUPANG: { color: 'red', text: 'Coupang', icon: <ShopOutlined /> },
WALMART: { color: 'blue', text: 'Walmart', icon: <ShopOutlined /> },
WILDBERRIES: { color: 'purple', text: 'Wildberries', icon: <ShopOutlined /> },
ALLEGRO: { color: 'green', text: 'Allegro', icon: <ShopOutlined /> },
MERCADO_LIBRE: { color: 'yellow', text: 'Mercado Libre', icon: <ShopOutlined /> },
JUMIA: { color: 'blue', text: 'Jumia', icon: <ShopOutlined /> },
JOOM: { color: 'purple', text: 'Joom', icon: <ShopOutlined /> },
AMAZON: { color: 'orange', text: 'Amazon', icon: <AmazonOutlined /> },
WISH: { color: 'blue', text: 'Wish', icon: <ShopOutlined /> },
EMAG: { color: 'green', text: 'eMAG', icon: <ShopOutlined /> },
MIRAVIA: { color: 'pink', text: 'Miravia', icon: <ShopOutlined /> },
DARAZ: { color: 'blue', text: 'Daraz', icon: <ShopOutlined /> },
JOYBUY: { color: 'red', text: 'Joybuy', icon: <ShopOutlined /> },
ALIBABA: { color: 'orange', text: 'Alibaba', icon: <ShopOutlined /> },
QOO10: { color: 'red', text: 'Qoo10', icon: <ShopOutlined /> },
SHOPIFY: { color: 'green', text: 'Shopify', icon: <ShopOutlined /> },
SHOPLAZZA: { color: 'blue', text: 'Shoplazza', icon: <ShopOutlined /> },
SHOPYY_V1: { color: 'purple', text: 'SHOPYY v1.0', icon: <ShopOutlined /> },
SHOPYY_V2: { color: 'purple', text: 'SHOPYY v2.0', icon: <ShopOutlined /> },
SHOPLINE: { color: 'green', text: 'SHOPLINE', icon: <ShopOutlined /> },
GREATBOSS: { color: 'blue', text: 'GreatBoss', icon: <ShopOutlined /> },
OTHER: { color: 'default', text: '其他', icon: <ShopOutlined /> },
// 原有平台
EBAY: { color: 'blue', text: 'eBay', icon: <GlobalOutlined /> },
};
// 店铺数据
const SHOPS: Shop[] = [
// TikTok平台 - 多个店铺
{ id: 'shop-tiktok-1', name: 'TikTok旗舰店A', platform: 'TIKTOK', status: 'ACTIVE', region: '中国', currency: 'CNY' },
{ id: 'shop-tiktok-2', name: 'TikTok旗舰店B', platform: 'TIKTOK', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-tiktok-3', name: 'TikTok精品店C', platform: 'TIKTOK', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-tiktok-4', name: 'TikTok精品店D', platform: 'TIKTOK', status: 'INACTIVE', region: '东南亚', currency: 'SGD' },
// Shopee平台 - 多个店铺
{ id: 'shop-shopee-1', name: 'Shopee官方店A', platform: 'SHOPEE', status: 'ACTIVE', region: '东南亚', currency: 'SGD' },
{ id: 'shop-shopee-2', name: 'Shopee官方店B', platform: 'SHOPEE', status: 'ACTIVE', region: '台湾', currency: 'TWD' },
{ id: 'shop-shopee-3', name: 'Shopee旗舰店C', platform: 'SHOPEE', status: 'ACTIVE', region: '马来西亚', currency: 'MYR' },
{ id: 'shop-shopee-4', name: 'Shopee旗舰店D', platform: 'SHOPEE', status: 'SUSPENDED', region: '菲律宾', currency: 'PHP' },
// Lazada平台 - 多个店铺
{ id: 'shop-lazada-1', name: 'Lazada官方店A', platform: 'LAZADA', status: 'ACTIVE', region: '东南亚', currency: 'SGD' },
{ id: 'shop-lazada-2', name: 'Lazada官方店B', platform: 'LAZADA', status: 'ACTIVE', region: '泰国', currency: 'THB' },
{ id: 'shop-lazada-3', name: 'Lazada旗舰店C', platform: 'LAZADA', status: 'ACTIVE', region: '越南', currency: 'VND' },
// Amazon平台 - 多个店铺
{ id: 'shop-amazon-1', name: 'Amazon美国店A', platform: 'AMAZON', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-amazon-2', name: 'Amazon美国店B', platform: 'AMAZON', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-amazon-3', name: 'Amazon欧洲店C', platform: 'AMAZON', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-amazon-4', name: 'Amazon欧洲店D', platform: 'AMAZON', status: 'ACTIVE', region: '英国', currency: 'GBP' },
{ id: 'shop-amazon-5', name: 'Amazon日本店E', platform: 'AMAZON', status: 'ACTIVE', region: '日本', currency: 'JPY' },
// eBay平台 - 多个店铺
{ id: 'shop-ebay-1', name: 'eBay全球店A', platform: 'EBAY', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-ebay-2', name: 'eBay全球店B', platform: 'EBAY', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-ebay-3', name: 'eBay美国店C', platform: 'EBAY', status: 'ACTIVE', region: '美国', currency: 'USD' },
// Shopify平台 - 多个店铺
{ id: 'shop-shopify-1', name: 'Shopify独立站A', platform: 'SHOPIFY', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-shopify-2', name: 'Shopify独立站B', platform: 'SHOPIFY', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-shopify-3', name: 'Shopify独立站C', platform: 'SHOPIFY', status: 'ACTIVE', region: '亚洲', currency: 'CNY' },
// Temu平台 - 多个店铺
{ id: 'shop-temu-1', name: 'Temu旗舰店A', platform: 'TEMU_FULL', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-temu-2', name: 'Temu旗舰店B', platform: 'TEMU_FULL', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-temu-3', name: 'Temu旗舰店C', platform: 'TEMU_FULL', status: 'ACTIVE', region: '澳大利亚', currency: 'AUD' },
// SHEIN平台 - 多个店铺
{ id: 'shop-shein-1', name: 'SHEIN官方店A', platform: 'SHEIN', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-shein-2', name: 'SHEIN官方店B', platform: 'SHEIN', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-shein-3', name: 'SHEIN官方店C', platform: 'SHEIN', status: 'ACTIVE', region: '中东', currency: 'AED' },
// 其他平台店铺
{ id: 'shop-ozon-1', name: 'Ozon旗舰店A', platform: 'OZON', status: 'ACTIVE', region: '俄罗斯', currency: 'RUB' },
{ id: 'shop-ali-1', name: '速卖通旗舰店A', platform: 'ALIEXPRESS', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-walmart-1', name: 'Walmart旗舰店A', platform: 'WALMART', status: 'ACTIVE', region: '美国', currency: 'USD' },
];
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-amazon-1',
shopName: 'Amazon美国店',
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' },
],
},
];
const OrderList: 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: [],
shop: [],
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: [],
shop: [],
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 handleBatchConfirm = () => {
if (selectedRows.length === 0) {
message.warning('请先选择要确认的订单');
return;
}
const pendingOrders = selectedRows.filter(order => order.status === 'PENDING');
if (pendingOrders.length === 0) {
message.warning('选中的订单中没有待处理的订单');
return;
}
Modal.confirm({
title: '确认批量确认',
content: `确定要确认选中的 ${pendingOrders.length} 个订单吗?`,
onOk: () => {
pendingOrders.forEach(order => updateOrderStatus(order.id, 'CONFIRMED'));
setSelectedRows([]);
message.success(`成功确认 ${pendingOrders.length} 个订单`);
},
});
};
const handleBatchShip = () => {
if (selectedRows.length === 0) {
message.warning('请先选择要发货的订单');
return;
}
const confirmedOrders = selectedRows.filter(order => order.status === 'CONFIRMED');
if (confirmedOrders.length === 0) {
message.warning('选中的订单中没有已确认的订单');
return;
}
Modal.confirm({
title: '确认批量发货',
content: `确定要发货选中的 ${confirmedOrders.length} 个订单吗?`,
onOk: () => {
confirmedOrders.forEach(order => updateOrderStatus(order.id, 'SHIPPED'));
setSelectedRows([]);
message.success(`成功发货 ${confirmedOrders.length} 个订单`);
},
});
};
const handleBatchCancel = () => {
if (selectedRows.length === 0) {
message.warning('请先选择要取消的订单');
return;
}
const cancellableOrders = selectedRows.filter(order =>
order.status === 'PENDING' || order.status === 'CONFIRMED'
);
if (cancellableOrders.length === 0) {
message.warning('选中的订单中没有可取消的订单');
return;
}
Modal.confirm({
title: '确认批量取消',
content: `确定要取消选中的 ${cancellableOrders.length} 个订单吗?`,
onOk: () => {
cancellableOrders.forEach(order => updateOrderStatus(order.id, 'CANCELLED'));
setSelectedRows([]);
message.success(`成功取消 ${cancellableOrders.length} 个订单`);
},
});
};
const handleBatchExport = () => {
if (selectedRows.length === 0) {
message.warning('请先选择要导出的订单');
return;
}
message.success(`成功导出 ${selectedRows.length} 个订单数据`);
};
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.shop && filters.shop.length > 0 && !filters.shop.includes(order.shopId)) {
return false;
}
if (filters.paymentStatus.length > 0 && !filters.paymentStatus.includes(order.paymentStatus)) {
return false;
}
if (activePlatformTab !== 'all' && order.platform !== activePlatformTab) {
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 || 0) > (bValue || 0) ? 1 : -1;
} else {
return (aValue || 0) < (bValue || 0) ? 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 platformStats = useMemo(() => {
const stats: Record<string, { total: number; amount: number; pending: number }> = {
all: { total: orders.length, amount: 0, pending: 0 },
};
orders.forEach(order => {
const platform = order.platform;
if (!stats[platform]) {
stats[platform] = { total: 0, amount: 0, pending: 0 };
}
stats[platform].total++;
stats[platform].amount += order.totalAmount;
if (order.status === 'PENDING') {
stats[platform].pending++;
stats.all.pending++;
}
stats.all.amount += order.totalAmount;
});
return stats;
}, [orders]);
const [activePlatformTab, setActivePlatformTab] = useState<string>('all');
const [activeStatsTab, setActiveStatsTab] = useState<string>('overview');
// 计算订单趋势数据最近7天
const orderTrendData = useMemo(() => {
const last7Days = Array.from({ length: 7 }, (_, i) => {
const date = moment().subtract(i, 'days').format('MM-DD');
return {
date,
orders: orders.filter(o => moment(o.createdAt).format('MM-DD') === date).length,
amount: orders.filter(o => moment(o.createdAt).format('MM-DD') === date)
.reduce((sum, o) => sum + o.totalAmount, 0),
};
}).reverse();
return last7Days;
}, [orders]);
// 计算平台分布数据
const platformDistributionData = useMemo(() => {
const platformCount: Record<string, number> = {};
orders.forEach(order => {
platformCount[order.platform] = (platformCount[order.platform] || 0) + 1;
});
return Object.entries(platformCount).map(([platform, count]) => ({
name: PLATFORM_CONFIG[platform].text,
value: count,
color: PLATFORM_CONFIG[platform].color,
}));
}, [orders]);
// 计算订单状态分布数据
const statusDistributionData = useMemo(() => {
const statusCount: Record<string, number> = {};
orders.forEach(order => {
statusCount[order.status] = (statusCount[order.status] || 0) + 1;
});
return Object.entries(statusCount).map(([status, count]) => ({
name: STATUS_CONFIG[status].text,
value: count,
color: STATUS_CONFIG[status].color,
}));
}, [orders]);
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} icon={PLATFORM_CONFIG[record.platform].icon}>
{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 statusConfig = {
PENDING: { color: 'warning', text: '待支付' },
PAID: { color: 'success', text: '已支付' },
REFUNDED: { color: 'default', text: '已退款' },
};
const config = statusConfig[status as keyof typeof statusConfig];
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>
<Card style={{ marginBottom: 16 }}>
<Tabs
activeKey={activeStatsTab}
onChange={setActiveStatsTab}
items={[
{ key: 'overview', label: '订单概览' },
{ key: 'trend', label: '订单趋势' },
{ key: 'distribution', label: '分布分析' },
]}
>
<Tabs.TabPane key="overview">
<Row gutter={16}>
<Col span={12}>
<Card title="订单趋势最近7天" size="small">
<ResponsiveContainer width="100%" height={300}>
<LineChart
data={orderTrendData}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis yAxisId="left" />
<YAxis yAxisId="right" orientation="right" />
<Tooltip />
<Legend />
<Line yAxisId="left" type="monotone" dataKey="orders" name="订单数" stroke="#1890ff" activeDot={{ r: 8 }} />
<Line yAxisId="right" type="monotone" dataKey="amount" name="金额" stroke="#52c41a" />
</LineChart>
</ResponsiveContainer>
</Card>
</Col>
<Col span={12}>
<Card title="平台分布" size="small">
<ResponsiveContainer width="100%" height={300}>
<Pie
data={platformDistributionData}
cx="50%"
cy="50%"
labelLine={false}
outerRadius={100}
fill="#8884d8"
dataKey="value"
label={({ name, percent }) => `${name}: ${((percent || 0) * 100).toFixed(0)}%`}
>
{platformDistributionData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</ResponsiveContainer>
</Card>
</Col>
</Row>
</Tabs.TabPane>
<Tabs.TabPane key="trend">
<Card title="订单趋势分析" size="small">
<ResponsiveContainer width="100%" height={400}>
<BarChart
data={orderTrendData}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis yAxisId="left" />
<YAxis yAxisId="right" orientation="right" />
<Tooltip />
<Legend />
<Bar yAxisId="left" dataKey="orders" name="订单数" fill="#1890ff" />
<Bar yAxisId="right" dataKey="amount" name="金额" fill="#52c41a" />
</BarChart>
</ResponsiveContainer>
</Card>
</Tabs.TabPane>
<Tabs.TabPane key="distribution">
<Row gutter={16}>
<Col span={12}>
<Card title="平台分布" size="small">
<ResponsiveContainer width="100%" height={300}>
<Pie
data={platformDistributionData}
cx="50%"
cy="50%"
labelLine={false}
outerRadius={100}
fill="#8884d8"
dataKey="value"
label={({ name, percent }) => `${name}: ${((percent || 0) * 100).toFixed(0)}%`}
>
{platformDistributionData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</ResponsiveContainer>
</Card>
</Col>
<Col span={12}>
<Card title="状态分布" size="small">
<ResponsiveContainer width="100%" height={300}>
<Pie
data={statusDistributionData}
cx="50%"
cy="50%"
labelLine={false}
outerRadius={100}
fill="#8884d8"
dataKey="value"
label={({ name, percent }) => `${name}: ${((percent || 0) * 100).toFixed(0)}%`}
>
{statusDistributionData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</ResponsiveContainer>
</Card>
</Col>
</Row>
</Tabs.TabPane>
</Tabs>
</Card>
<Tabs
activeKey={activeTab}
onChange={handleTabChange}
items={[
{ key: 'all', label: '全部' },
{ key: 'pending', label: `待处理 (${stats.pending})` },
{ key: 'processing', label: `处理中 (${stats.processing})` },
{ key: 'completed', label: `已完成 (${stats.completed})` },
{ key: 'exception', label: `异常 (${stats.exception})` },
]}
tabBarExtraContent={
<Tabs
activeKey={activePlatformTab}
onChange={setActivePlatformTab}
size="small"
style={{ marginBottom: 0 }}
items={[
{
key: 'all',
label: (
<span>
<AppstoreOutlined />
</span>
),
},
...Object.entries(PLATFORM_CONFIG).map(([key, config]) => {
const stat = platformStats[key] || { total: 0, amount: 0, pending: 0 };
return {
key,
label: (
<span>
{config.icon}
{config.text} ({stat.total})
{stat.pending > 0 && (
<Badge count={stat.pending} size="small" style={{ marginLeft: 4 }} />
)}
</span>
),
};
}),
]}
/>
}
/>
<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={handleBatchConfirm}>
</Button>
<Button onClick={handleBatchShip}>
</Button>
<Button danger onClick={handleBatchCancel}>
</Button>
<Button onClick={handleBatchExport}>
</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)}
open={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.shop}
onChange={(value) => handleFilterChange('shop', value)}
style={{ width: '100%' }}
>
{SHOPS.map(shop => (
<Option key={shop.id} value={shop.id}>
{shop.name} ({PLATFORM_CONFIG[shop.platform]?.text || shop.platform})
</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)}
open={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)}
open={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="确认订单"
open={confirmModalVisible}
onOk={handleConfirmSubmit}
onCancel={() => setConfirmModalVisible(false)}
>
<p> "{currentOrder?.orderId}" </p>
<p>: {currentOrder?.customerName}</p>
<p>金额: ${currentOrder?.totalAmount.toFixed(2)}</p>
</Modal>
<Modal
title="订单发货"
open={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="取消订单"
open={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="售后申请"
open={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;