diff --git a/apps/backend/src/repositories/order-repo.ts b/apps/backend/src/repositories/order-repo.ts index a9d20bf3..87dd6620 100644 --- a/apps/backend/src/repositories/order-repo.ts +++ b/apps/backend/src/repositories/order-repo.ts @@ -402,8 +402,25 @@ function appendProviderFilter(filters: string[], params: unknown[], provider: st return } - if (normalized === 'kuaishou' || normalized === 'kuaishou-industry') { - filters.push(`(o.provider LIKE 'kuaishou%' OR o.platform LIKE 'kuaishou%')`) + if ( + normalized === 'kuaishou' || + normalized === 'kuaishou_all' || + normalized === 'kuaishou_91' || + normalized === 'kuaishou-industry' + ) { + filters.push( + `(o.provider LIKE 'kuaishou%' OR o.platform LIKE 'kuaishou%' OR o.provider IN ('91kaquan', 'open_91', '91') OR o.platform IN ('91kaquan', '91'))`, + ) + return + } + + if (normalized === 'douyin' || normalized === 'douyin_mashida' || normalized === 'doudian') { + filters.push(`(o.provider LIKE 'douyin%' OR o.platform IN ('doudian', 'douyin'))`) + return + } + + if (normalized === 'mock' || normalized === 'local_dev') { + filters.push(`(o.provider IN ('mock', 'local_dev') OR o.platform IN ('mock', 'local_dev'))`) return } diff --git a/apps/frontend/src/pages/admin/AdminOrdersPage.tsx b/apps/frontend/src/pages/admin/AdminOrdersPage.tsx index 6122c3e4..f6475e94 100644 --- a/apps/frontend/src/pages/admin/AdminOrdersPage.tsx +++ b/apps/frontend/src/pages/admin/AdminOrdersPage.tsx @@ -1,5 +1,26 @@ -import { SearchOutlined, ReloadOutlined, CopyOutlined } from '@ant-design/icons' -import { Button, Card, Form, Input, Select, Space, Table, Tag, Typography, message } from 'antd' +import { + CopyOutlined, + EyeOutlined, + LinkOutlined, + ReloadOutlined, + SearchOutlined, + ShopOutlined, + UserOutlined, +} from '@ant-design/icons' +import { + App, + Button, + Card, + Form, + Input, + Segmented, + Select, + Space, + Table, + Tag, + Tooltip, + Typography, +} from 'antd' import type { TableColumnsType } from 'antd' import { useQuery } from '@tanstack/react-query' import { useEffect, useMemo } from 'react' @@ -11,8 +32,13 @@ import StatusTag from '@/components/admin/StatusTag' import dayjs from '@/lib/dayjs' import { fetchAdminOrders } from '@/services/admin' import type { AdminOrderListItem } from '@/types/admin' +import { + resolveOrderFulfillmentMode, + resolveOrderPlatformBadge, +} from '@/utils/admin-display' import { adminOrderExecutorOptions, + adminOrderPlatformSegmentedOptions, adminOrderProviderOptions, adminPayStatusOptions, adminTaskStatusOptions, @@ -44,6 +70,7 @@ function resolveDefaultOrderDateRange() { export default function AdminOrdersPage() { const navigate = useNavigate() + const { message } = App.useApp() const [form] = Form.useForm() const [searchParams, setSearchParams] = useSearchParams() const page = Number(searchParams.get('page') || 1) || 1 @@ -125,38 +152,127 @@ export default function AdminOrdersPage() { form, ]) + const handleCopyText = (text: string, label: string) => { + if (!text) return + navigator.clipboard + .writeText(text) + .then(() => message.success(`已复制 ${label}`)) + .catch(() => message.error('复制失败,请手动复制')) + } + + const handleSegmentedChange = (val: string) => { + form.setFieldValue('provider', val || undefined) + setSearchParams( + cleanParams({ + ...filters, + provider: val, + page: 1, + pageSize, + }), + ) + } + const columns: TableColumnsType = [ { - title: '订单信息', - dataIndex: 'platformOrderId', - minWidth: 220, - render: (_, row) => ( -
- - navigate(`/admin/orders/${row.orderId}`)}> - {row.platformOrderId} - -
- ), + title: '平台 / 订单号', + minWidth: 230, + render: (_, row) => { + const platformBadge = resolveOrderPlatformBadge(row) + const orderNo = row.platformOrderId || String(row.orderId) + + return ( +
+
+ + {platformBadge.label} + + {row.shopName ? ( + + + {row.shopName} + + ) : null} +
+ + + navigate(`/admin/orders/${row.orderId}`)} + title={`查看订单 #${row.orderId}`} + > + {orderNo} + +
+ ) + }, }, { - title: '商品 / 任务', + title: '买家信息', + minWidth: 140, + render: (_, row) => { + const buyerName = row.buyerName || '' + const buyerId = row.buyerId || '' + const contact = row.receiverContact || '' + + if (!buyerName && !buyerId && !contact) { + return - + } + + return ( +
+
+ + + {buyerName || '匿名买家'} + +
+ {buyerId ? ( + + ID: {buyerId} + + ) : null} + {contact && contact !== buyerName ? ( + + {contact} + + ) : null} +
+ ) + }, + }, + { + title: '商品 / 规格', minWidth: 260, render: (_, row) => { const itemTitle = row.itemTitle || row.itemSummary || '-' @@ -172,7 +288,7 @@ export default function AdminOrdersPage() { {item.itemTitle || '-'} @@ -186,6 +302,7 @@ export default function AdminOrdersPage() { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', + fontSize: 12, }} title={`规格:${item.skuNick}`} > @@ -199,7 +316,11 @@ export default function AdminOrdersPage() { ) : ( <> - + {itemTitle} {skuNick ? ( @@ -212,6 +333,7 @@ export default function AdminOrdersPage() { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', + fontSize: 12, }} title={`规格:${skuNick}`} > @@ -221,41 +343,89 @@ export default function AdminOrdersPage() { ) : null} )} - - 商品 {row.itemCount} · 数量 {row.totalQuantity} · 任务 {row.taskCount} + + 数量 {row.totalQuantity || 1} · 关联任务 {row.taskCount || 0} ) }, }, { - title: '状态', - minWidth: 240, - render: (_, row) => ( - - - - - {row.claimStatus === 'paused' ? ( - H5已暂停 - ) : row.claimStatus === 'closed' ? ( - H5已关闭 - ) : null} - - ), + title: '交付 / 状态', + minWidth: 230, + render: (_, row) => { + const fulfillmentMode = resolveOrderFulfillmentMode(row) + + return ( +
+ + + {fulfillmentMode.label} + + + + + + + {row.claimStatus === 'paused' ? ( + + H5已暂停 + + ) : row.claimStatus === 'closed' ? ( + + H5已关闭 + + ) : null} +
+ ) + }, }, { title: '金额 / 支付时间', - minWidth: 180, + minWidth: 160, render: (_, row) => ( -
- - {row.totalAmount || '0.00'} {row.currency} +
+ + ¥ {row.totalAmount || '0.00'} {row.currency} - {formatAdminDateTime(row.paidAt || row.createdAt)} + + {formatAdminDateTime(row.paidAt || row.createdAt)} +
), }, + { + title: '操作', + minWidth: 140, + fixed: 'right', + render: (_, row) => { + const orderNo = row.platformOrderId || String(row.orderId) + const h5Url = `${window.location.origin}/h5/${encodeURIComponent(orderNo)}` + + return ( + + + + + + + ) + }, + }, ] function applyFilters(values: OrderFilterForm) { @@ -298,75 +468,92 @@ export default function AdminOrdersPage() { setSearchParams(cleanParams({ page: 1, pageSize, dateFrom, dateTo })) } + const currentSegmentedProvider = filters.provider || '' + return (
- - - form={form} - layout="inline" - onFinish={applyFilters} - className="filter-form" - > - - +
+
+ handleSegmentedChange(String(val))} /> - - - - - - item.value)} - style={{ minWidth: 150 }} - /> - - - - - - - - - - - +
+ + + form={form} + layout="inline" + onFinish={applyFilters} + className="filter-form" + style={{ rowGap: 12 }} + > + + + + + + + + item.value)} + style={{ minWidth: 140 }} + /> + + + + + + + + + + + +
@@ -375,7 +562,7 @@ export default function AdminOrdersPage() { loading={query.isLoading || query.isFetching || !hasBootstrappedFilters} columns={columns} dataSource={data?.items || []} - scroll={{ x: 900 }} + scroll={{ x: 1100 }} pagination={buildAdminTablePagination({ current: data?.pagination.page || page, pageSize: data?.pagination.pageSize || pageSize, diff --git a/apps/frontend/src/utils/admin-display.ts b/apps/frontend/src/utils/admin-display.ts index 0590245b..0b1fea61 100644 --- a/apps/frontend/src/utils/admin-display.ts +++ b/apps/frontend/src/utils/admin-display.ts @@ -182,3 +182,60 @@ export function formatTaskEventPayload(payload: Record) { return JSON.stringify(payload || {}) } + +export function resolveOrderPlatformBadge(row: { + platform?: string + provider?: string +}): { label: string; color: string } { + const provider = String(row.provider || '').toLowerCase() + const platform = String(row.platform || '').toLowerCase() + + if (provider === 'douyin_mashida' || platform === 'doudian' || platform === 'douyin') { + return { label: '抖音码速达', color: '#6366f1' } + } + + if ( + provider.startsWith('kuaishou') || + platform.startsWith('kuaishou') || + provider === '91kaquan' || + provider === 'open_91' || + platform === '91kaquan' + ) { + if (provider === '91kaquan' || provider === 'open_91' || platform === '91kaquan') { + return { label: '快手 / 91卡券', color: '#ff5000' } + } + return { label: '快手电商', color: '#ff5000' } + } + + if (provider === 'pdd' || platform === 'pdd') { + return { label: '拼多多', color: '#e02e24' } + } + + if (provider === 'mock' || platform === 'mock' || provider === 'local_dev') { + return { label: '本地模拟', color: '#64748b' } + } + + return { label: row.provider || row.platform || '外部订单', color: '#1677ff' } +} + +export function resolveOrderFulfillmentMode(row: { + provider?: string + platform?: string +}): { label: string; color: string } { + const provider = String(row.provider || '').toLowerCase() + + if (provider === 'douyin_mashida') { + return { label: 'H5直绑', color: 'blue' } + } + + if (provider === 'kuaishou_industry' || provider === '91kaquan' || provider === 'open_91') { + return { label: '电子凭证', color: 'gold' } + } + + if (provider === 'pdd') { + return { label: '拼多多发货', color: 'red' } + } + + return { label: '自动交付', color: 'cyan' } +} + diff --git a/apps/frontend/src/utils/admin-options.ts b/apps/frontend/src/utils/admin-options.ts index 0a4f3507..7a4366a4 100644 --- a/apps/frontend/src/utils/admin-options.ts +++ b/apps/frontend/src/utils/admin-options.ts @@ -6,12 +6,20 @@ export const adminPayStatusOptions = [ { label: '已退款', value: 'refunded' }, ] -export const adminOrderProviderOptions = [ - { label: '拼多多', value: 'pdd' }, - { label: '91卡券', value: '91kaquan' }, - { label: '快手', value: 'kuaishou' }, - // 抖音码速达:订单落在 orders.provider='douyin_mashida'(platform='doudian') +export const adminOrderPlatformSegmentedOptions = [ + { label: '全部平台', value: '' }, + { label: '快手 / 91卡券', value: 'kuaishou' }, { label: '抖音码速达', value: 'douyin_mashida' }, + { label: '拼多多', value: 'pdd' }, + { label: '本地模拟', value: 'mock' }, +] + +export const adminOrderProviderOptions = [ + { label: '全部', value: '' }, + { label: '快手 / 91卡券', value: 'kuaishou' }, + { label: '抖音码速达', value: 'douyin_mashida' }, + { label: '拼多多', value: 'pdd' }, + { label: '本地模拟', value: 'mock' }, ] export const adminOrderExecutorOptions = [