From 96426a64b19f15cdcdfcd8cec1588f46b1839a69 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Fri, 21 Nov 2025 14:59:49 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E8=AE=A2=E5=8D=95):=20=E6=8A=BD?= =?UTF-8?q?=E7=A6=BB=E8=AE=A2=E5=8D=95=E8=AF=A6=E6=83=85=E6=8A=BD=E5=B1=89?= =?UTF-8?q?=E4=B8=BA=E7=8B=AC=E7=AB=8B=E7=BB=84=E4=BB=B6=E5=B9=B6=E5=A4=8D?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将订单列表页的详情抽屉抽离为独立组件 OrderDetailDrawer 在订阅订单页面复用该组件,替换原有实现 --- src/pages/Order/List/OrderDetailDrawer.tsx | 381 +++++++++++++++++++++ src/pages/Order/List/index.tsx | 34 +- src/pages/Subscription/Orders/index.tsx | 106 ++---- 3 files changed, 432 insertions(+), 89 deletions(-) create mode 100644 src/pages/Order/List/OrderDetailDrawer.tsx diff --git a/src/pages/Order/List/OrderDetailDrawer.tsx b/src/pages/Order/List/OrderDetailDrawer.tsx new file mode 100644 index 0000000..b58a880 --- /dev/null +++ b/src/pages/Order/List/OrderDetailDrawer.tsx @@ -0,0 +1,381 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { + App, + Button, + Card, + Divider, + Drawer, + Empty, + Popconfirm, + Space, + Tag, +} from 'antd'; +import { ActionType } from '@ant-design/pro-components'; +import { CopyOutlined, DownOutlined, FileDoneOutlined } from '@ant-design/icons'; + +// 服务器 API 引用(保持与原 index.tsx 一致) +import { + ordercontrollerChangestatus, + ordercontrollerGetorderdetail, + ordercontrollerSyncorderbyid, +} from '@/servers/api/order'; +import { logisticscontrollerDelshipment } from '@/servers/api/logistics'; + +// 工具与子组件 +import { formatShipmentState, formatSource } from '@/utils/format'; +import RelatedOrders from './RelatedOrders'; + +// 中文注释:为保持原文件结构简单,此处从 index.tsx 引入的子组件仍由原文件导出或保持原状 +// 若后续需要彻底解耦,可将 OrderNote / Shipping / SalesChange 也独立到文件 +// 当前按你的要求仅抽离详情 Drawer + +type OrderRecord = API.Order; + +interface OrderDetailDrawerProps { + tableRef: React.MutableRefObject; // 中文注释:列表刷新引用 + orderId: number; // 中文注释:订单主键 ID + record: OrderRecord; // 中文注释:订单行记录 + open: boolean; // 中文注释:是否打开抽屉 + onClose: () => void; // 中文注释:关闭抽屉回调 + setActiveLine: (id: number) => void; // 中文注释:高亮当前行 + OrderNoteComponent: React.ComponentType; // 中文注释:备注组件(从外部注入) + SalesChangeComponent: React.ComponentType; // 中文注释:换货组件(从外部注入) +} + +const OrderDetailDrawer: React.FC = ({ + tableRef, + orderId, + record, + open, + onClose, + setActiveLine, + OrderNoteComponent, + SalesChangeComponent, +}) => { + const { message } = App.useApp(); + const ref = useRef(); + const [detail, setDetail] = useState(null); + + // 中文注释:加载详情数据(与 index.tsx 中完全保持一致) + const initRequest = async () => { + const { data, success }: API.OrderDetailRes = + await ordercontrollerGetorderdetail({ orderId }); + if (!success || !data) return null; + // 中文注释:销售项合并 SKU,展示数量汇总 + data.sales = data.sales?.reduce( + (acc: API.OrderSale[], cur: API.OrderSale) => { + const idx = acc.findIndex((v: any) => v.productId === cur.productId); + if (idx === -1) acc.push(cur); + else acc[idx].quantity += cur.quantity; + return acc; + }, + [], + ); + return data; + }; + + useEffect(() => { + (async () => { + if (open && orderId) { + try { + const data = await initRequest(); + setDetail(data); + } catch (e) { + setDetail(null); + } + } else { + setDetail(null); + } + })(); + }, [open, orderId]); + + return ( + , + ...(['after_sale_pending', 'pending_reshipment'].includes( + record.orderStatus, + ) + ? [] + : [ + , + , + ]), + ...([ + 'processing', + 'pending_reshipment', + 'completed', + 'pending_refund', + ].includes(record.orderStatus) + ? [ + , + { + try { + const { success, message: errMsg } = + await ordercontrollerChangestatus( + { id: record.id }, + { status: 'after_sale_pending' }, + ); + if (!success) throw new Error(errMsg); + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + + , + ] + : []), + ...(record.orderStatus === 'after_sale_pending' + ? [ + , + { + try { + const { success, message: errMsg } = + await ordercontrollerChangestatus( + { id: record.id }, + { status: 'cancelled' }, + ); + if (!success) throw new Error(errMsg); + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + + , + , + { + try { + const { success, message: errMsg } = + await ordercontrollerChangestatus( + { id: record.id }, + { status: 'refund_requested' }, + ); + if (!success) throw new Error(errMsg); + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + + , + , + { + try { + const { success, message: errMsg } = + await ordercontrollerChangestatus( + { id: record.id }, + { status: 'completed' }, + ); + if (!success) throw new Error(errMsg); + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + + , + ] + : []), + ]} + > + {(() => { setActiveLine(record.id as number); return null; })()} + {/** 中文注释:优先使用详情数据,其次使用列表行数据 */} + {(() => { + const drec: any = detail || record; + {/* 中文注释:基本信息展示(保持原有格式) */} + return (<> +
+ + {drec?.orderStatus} + {formatSource(drec.source_type, drec.utm_source)} + +
+ + {/* 中文注释:原始订单行项目 */} +
+
原始订单
+ {Array.isArray((drec as any)?.items) && drec.items.length > 0 ? ( +
    + {drec.items.map((item: any) => ( +
  • + {item.name}:{item.quantity} +
  • + ))} +
+ ) : ( + + )} +
+ + {/* 中文注释:关联(订阅/订单),使用已存在的 RelatedOrders 组件 */} +
+
关联
+ +
+ + {/* 中文注释:订单内容(销售项) */} +
+
订单内容
+ {Array.isArray((drec as any)?.sales) && drec.sales.length > 0 ? ( +
    + {drec.sales.map((item: any) => ( +
  • + {item.name}:{item.quantity} +
  • + ))} +
+ ) : ( + + )} +
+ + {/* 中文注释:备注 */} +
+
备注
+ {Array.isArray((drec as any)?.notes) && drec.notes.length > 0 ? ( +
+ {drec.notes.map((note: any) => ( +
+
+ {note.username} + {note.createdAt} +
+
{note.content}
+
+ ))} +
+ ) : ( + + )} +
+ + {/* 中文注释:物流信息 */} +
+
物流信息
+ {Array.isArray((drec as any)?.shipment) && drec.shipment.length > 0 ? ( +
+ {(drec as any).shipment.map((v: any) => ( + + {v.tracking_provider} + {v.primary_tracking_number} + { + try { + await navigator.clipboard.writeText(v.tracking_url); + message.success('复制成功!'); + } catch (err) { + message.error('复制失败!'); + } + }} + /> + + } + actions={ + v.state === 'waiting-for-scheduling' || v.state === 'waiting-for-transit' + ? [ + { + try { + const { success, message: errMsg } = await logisticscontrollerDelshipment({ id: v.id }); + if (!success) throw new Error(errMsg); + tableRef.current?.reload(); + } catch (error: any) { + message.error(error.message); + } + }} + > + 取消运单 + , + ] + : [] + } + > +
订单号: {Array.isArray(v?.orderIds) ? v.orderIds.join(',') : '-'}
+ {Array.isArray(v?.items) && + v.items.map((item: any) => ( +
+ {item.name}: {item.quantity} +
+ ))} +
+ ))} +
+ ) : ( + + )} +
+ + {/* 中文注释:换货(外部传入组件) */} +
+ +
+ ); + })()} +
+ ); +}; + +export default OrderDetailDrawer; \ No newline at end of file diff --git a/src/pages/Order/List/index.tsx b/src/pages/Order/List/index.tsx index b4c513c..d6f5569 100644 --- a/src/pages/Order/List/index.tsx +++ b/src/pages/Order/List/index.tsx @@ -81,6 +81,7 @@ import { } from 'antd'; import Item from 'antd/es/list/Item'; import RelatedOrders from './RelatedOrders'; +import OrderDetailDrawer from './OrderDetailDrawer'; import React, { useMemo, useRef, useState } from 'react'; import { printPDF } from '@/utils/util'; @@ -89,6 +90,9 @@ const ListPage: React.FC = () => { const [activeKey, setActiveKey] = useState('all'); const [count, setCount] = useState([]); const [activeLine, setActiveLine] = useState(-1); + const [detailOpen, setDetailOpen] = useState(false); + const [detailRecord, setDetailRecord] = useState(null); + const [detailOrderId, setDetailOrderId] = useState(null); const tabs: TabsProps['items'] = useMemo(() => { const total = count.reduce((acc, cur) => acc + Number(cur.count), 0); const tabs = [ @@ -312,13 +316,31 @@ const ListPage: React.FC = () => { ) : ( <> )} - + type="primary" + onClick={() => { + setDetailRecord(record); + setDetailOrderId(record.id as number); + setDetailOpen(true); + setActiveLine(record.id); + }} + > + + 详情 + + {detailRecord && detailOrderId !== null && ( + setDetailOpen(false)} + tableRef={actionRef} + orderId={detailOrderId as number} + record={detailRecord as any} + setActiveLine={setActiveLine} + OrderNoteComponent={OrderNote} + SalesChangeComponent={SalesChange} + /> + )} { const actionRef = useRef(); const { message } = App.useApp(); - // 抽屉状态:用于展示与订阅相关的订单详情(含行项目meta) - const [drawerOpen, setDrawerOpen] = useState(false); - const [drawerTitle, setDrawerTitle] = useState('订阅关联'); - const [drawerItems, setDrawerItems] = useState([]); - const [drawerMeta, setDrawerMeta] = useState([]); - const [isSubscription, setIsSubscription] = useState(false); + // 抽屉状态:改为复用订单详情抽屉组件 + const [detailOpen, setDetailOpen] = useState(false); + const [detailRecord, setDetailRecord] = useState(null); + const [detailOrderId, setDetailOrderId] = useState(null); + const Noop: React.FC = () => null; const columns: ProColumns[] = [ { @@ -84,41 +84,10 @@ const OrdersPage: React.FC = () => { render: (_, row) => (