Files
makemd/dashboard/src/pages/Orders/OrderList.tsx

1529 lines
53 KiB
TypeScript
Raw Normal View History

import { useState, useEffect, useCallback, useMemo, moment } 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: 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: [],
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;