From b5de3ab2f7f807d1ea641cb7120e7d8033e371fc Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 1 Nov 2026 15:45:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(orders):=20=E4=BC=98=E5=8C=96=E6=88=91?= =?UTF-8?q?=E7=9A=84=E8=AE=A2=E5=8D=95=E5=88=97=E8=A1=A8=E4=B8=8E=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E8=AF=A6=E6=83=85=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/features/orders/OrderDetailPage.tsx | 93 ++++++- .../src/features/orders/OrderListPage.tsx | 235 ++++++++++-------- frontend/src/styles/storefront-listings.css | 227 ++++++++++++++++- 3 files changed, 436 insertions(+), 119 deletions(-) diff --git a/frontend/src/features/orders/OrderDetailPage.tsx b/frontend/src/features/orders/OrderDetailPage.tsx index ef6328c..aad9fbc 100644 --- a/frontend/src/features/orders/OrderDetailPage.tsx +++ b/frontend/src/features/orders/OrderDetailPage.tsx @@ -13,7 +13,7 @@ import { Tag, Typography, } from 'antd' -import { ArrowLeftOutlined, MessageOutlined } from '@ant-design/icons' +import { ArrowLeftOutlined, CopyOutlined, MessageOutlined } from '@ant-design/icons' import { useQueryClient } from '@tanstack/react-query' import { Link, useNavigate, useParams } from 'react-router-dom' @@ -37,6 +37,18 @@ const handoffRecordTypeMap: Record = { renter_return: '归还记录', } +const TAG_COLORS: Record = { + default: 'default', + primary: 'blue', + success: 'green', + warning: 'orange', + danger: 'red', +} + +function toneColor(tone: string) { + return TAG_COLORS[tone] ?? 'default' +} + export function OrderDetailPage() { const { id } = useParams() const navigate = useNavigate() @@ -50,6 +62,16 @@ export function OrderDetailPage() { const records = handoffQuery.data ?? [] const [openingChat, setOpeningChat] = useState(false) + const copyOrderNo = async () => { + if (!order) return + try { + await navigator.clipboard.writeText(order.order_no) + message.success('订单号已复制') + } catch { + message.error('复制失败,请手动选择订单号') + } + } + const openOrderChat = async () => { if (!order || openingChat) return setOpeningChat(true) @@ -135,28 +157,78 @@ export function OrderDetailPage() {
-
- {order.order_no} +
+
+ {order.order_no} + +
{order.title} - {status.label} - + {status.label} + 交接:{handoff.label} - + 结算:{settlement.label} + {order.active_dispute ? 申诉处理中 : null} + {order.status === 'pending_payment' ? ( +

+ 请在 {formatDateTime(order.payment_deadline_at)} 前完成支付,超时订单会自动关闭。 +

+ ) : null}
订单金额 {formatMoney(order.display_amount_cent)} - 押金 {formatMoney(order.deposit_amount_cent)} + 含押金 {formatMoney(order.deposit_amount_cent)} + {order.deposit_waived_amount_cent > 0 ? ( + 已免押 {formatMoney(order.deposit_waived_amount_cent)} + ) : null}
+
+
+ 租金 + + {formatMoney( + order.rent_amount_cent ?? + Math.max(0, order.display_amount_cent - order.deposit_amount_cent) + )} + +
+
+ 实收押金 + {formatMoney(order.deposit_amount_cent)} +
+
+ 押金减免 + 0 ? 'is-waived' : ''}> + {order.deposit_waived_amount_cent > 0 + ? `-${formatMoney(order.deposit_waived_amount_cent)}` + : '—'} + +
+
+ 预计租期 + + {order.estimated_duration_hours ? `${order.estimated_duration_hours} 小时` : '—'} + +
+
+ @@ -222,7 +294,7 @@ export function OrderDetailPage() { - + {checkoutStatusMeta(checkout.status).label} @@ -285,7 +357,10 @@ export function OrderDetailPage() { {records.length === 0 ? ( - + ) : (
{records.map(record => ( diff --git a/frontend/src/features/orders/OrderListPage.tsx b/frontend/src/features/orders/OrderListPage.tsx index 7a50924..b6d7fc3 100644 --- a/frontend/src/features/orders/OrderListPage.tsx +++ b/frontend/src/features/orders/OrderListPage.tsx @@ -4,12 +4,13 @@ import { Alert, Button, Card, + Empty, + Pagination, Select, + Skeleton, Space, - Table, Tag, Typography, - type TableColumnsType, } from 'antd' import { ReloadOutlined, RightOutlined } from '@ant-design/icons' import { Link, useNavigate } from 'react-router-dom' @@ -23,6 +24,14 @@ import { formatDateTime, formatMoney, formatText } from '@/shared/utils/format' const PAGE_SIZE = 20 +const TAG_COLORS: Record = { + default: 'default', + primary: 'blue', + success: 'green', + warning: 'orange', + danger: 'red', +} + const statusOptions = [ 'pending_payment', 'pending_handoff', @@ -38,91 +47,79 @@ const statusOptions = [ 'abnormal', ].map(value => ({ value, label: orderStatusMeta(value).label })) -function buildColumns(navigate: (id: number) => void): TableColumnsType { - return [ - { - title: '订单', - dataIndex: 'order_no', - width: 300, - fixed: 'left', - render: (_, row) => ( -
- - {row.title} - - {roleLabel(row.role)} - {row.listing_no} - +function toneColor(tone: string) { + return TAG_COLORS[tone] ?? 'default' +} + +function OrderCard({ + order, + onOpen, +}: { + order: UserOrderListItem + onOpen: () => void +}) { + const status = orderStatusMeta(order.status) + const handoff = handoffStatusMeta(order.handoff_status) + + return ( +
{ + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault() + onOpen() + } + }} + > +
+
+ {order.order_no} + {roleLabel(order.role)}
- ), - }, - { - title: '商品', - key: 'listing', - width: 220, - render: (_, row) => ( -
- {formatText(row.server_region)} - {formatText(row.login_platform)} - - {row.rental_days ? `${row.rental_days} 天` : '按账号规则'} - -
- ), - }, - { - title: '金额', - key: 'amount', - width: 180, - align: 'right', - render: (_, row) => ( -
- {formatMoney(row.display_amount_cent)} - 押金 {formatMoney(row.deposit_amount_cent)} - {row.deposit_waived_amount_cent > 0 ? ( - 减免 {formatMoney(row.deposit_waived_amount_cent)} - ) : null} -
- ), - }, - { - title: '状态', - key: 'status', - width: 190, - render: (_, row) => ( - - {orderStatusMeta(row.status).label} - - {handoffStatusMeta(row.handoff_status).label} + + {status.label} + + {handoff.label} - ), - }, - { - title: '时间', - key: 'time', - width: 190, - render: (_, row) => ( -
- 下单 {formatDateTime(row.created_at)} - 支付截止 {formatDateTime(row.payment_deadline_at)} +
+ +
+
+

{order.title || '未命名订单'}

+
+ 商品编号 {formatText(order.listing_no)} + {formatText(order.server_region)} + {formatText(order.login_platform)} + {order.rental_days ? `${order.rental_days} 天` : '按账号规则'} + {order.daily_loss_m ? 日耗 {order.daily_loss_m}M : null} +
+
+ 下单 {formatDateTime(order.created_at)} + 支付截止 {formatDateTime(order.payment_deadline_at)} +
- ), - }, - { - title: '', - key: 'actions', - width: 90, - fixed: 'right', - render: (_, row) => ( - - ), - }, - ] + +
+
+ {formatMoney(order.display_amount_cent)} + 押金 {formatMoney(order.deposit_amount_cent)} + {order.deposit_waived_amount_cent > 0 ? ( + + 押金减免 -{formatMoney(order.deposit_waived_amount_cent)} + + ) : null} +
+ + 查看详情 + +
+
+
+ ) } export function OrderListPage() { @@ -139,8 +136,9 @@ export function OrderListPage() { enabled: Boolean(token), }) - const columns = buildColumns(id => navigate(`/orders/${id}`)) const errorText = error instanceof Error ? error.message : '请稍后重试或重新登录。' + const items = data?.items ?? [] + const total = data?.total ?? 0 if (!hydrated) { return ( @@ -183,7 +181,7 @@ export function OrderListPage() { 查看租用进度、支付、交接、结账与申诉记录。
- +