fix(admin): 订单列表默认近7天并按支付时间筛选
避免无日期过滤时展示全库,关键词搜索不受日期限制;SSE 插入裁剪并尊重筛选,新单入库优先使用平台支付时间。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import { fetchAdminOrders } from '@/services/admin'
|
||||
import type { AdminOrderListItem } from '@/types/admin'
|
||||
import {
|
||||
adminOrderExecutorOptions,
|
||||
adminOrderProviderOptions,
|
||||
adminPayStatusOptions,
|
||||
adminTaskStatusOptions,
|
||||
} from '@/utils/admin-options'
|
||||
@@ -25,20 +26,46 @@ import { formatAdminDateTime } from '@/utils/admin-time'
|
||||
|
||||
type OrderFilterForm = {
|
||||
keyword?: string
|
||||
provider?: string
|
||||
executorKey?: string
|
||||
payStatus?: string
|
||||
taskStatus?: string
|
||||
dateRange?: unknown
|
||||
}
|
||||
|
||||
/** 默认展示近 7 天(含今天)。 */
|
||||
function resolveDefaultOrderDateRange() {
|
||||
const today = dayjs()
|
||||
return {
|
||||
dateFrom: today.subtract(6, 'day').format('YYYY-MM-DD'),
|
||||
dateTo: today.format('YYYY-MM-DD'),
|
||||
}
|
||||
}
|
||||
|
||||
export default function AdminOrdersPage() {
|
||||
const navigate = useNavigate()
|
||||
const [form] = Form.useForm<OrderFilterForm>()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const page = Number(searchParams.get('page') || 1) || 1
|
||||
const pageSize = resolveAdminPageSize(searchParams.get('pageSize'), ADMIN_DEFAULT_PAGE_SIZE)
|
||||
|
||||
// 首次进入(仅有分页参数)时写入默认近 7 天,避免一把拉出全库。
|
||||
useEffect(() => {
|
||||
const meaningfulKeys = [...searchParams.keys()].filter(
|
||||
(key) => key !== 'page' && key !== 'pageSize',
|
||||
)
|
||||
if (meaningfulKeys.length > 0) {
|
||||
return
|
||||
}
|
||||
const { dateFrom, dateTo } = resolveDefaultOrderDateRange()
|
||||
setSearchParams(cleanParams({ page: 1, pageSize, dateFrom, dateTo }), { replace: true })
|
||||
// 仅首屏引导默认日期,避免与后续筛选互相打架。
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const filters = {
|
||||
keyword: searchParams.get('keyword') || '',
|
||||
provider: searchParams.get('provider') || '',
|
||||
executorKey: searchParams.get('executorKey') || '',
|
||||
payStatus: searchParams.get('payStatus') || '',
|
||||
taskStatus: searchParams.get('taskStatus') || '',
|
||||
@@ -54,20 +81,31 @@ export default function AdminOrdersPage() {
|
||||
filters.executorKey,
|
||||
filters.keyword,
|
||||
filters.payStatus,
|
||||
filters.provider,
|
||||
filters.taskStatus,
|
||||
page,
|
||||
pageSize,
|
||||
],
|
||||
)
|
||||
|
||||
// 等待默认日期写入 URL 后再请求,避免首屏无日期打出全库。
|
||||
const hasBootstrappedFilters =
|
||||
Boolean(filters.dateFrom && filters.dateTo) ||
|
||||
Boolean(filters.keyword) ||
|
||||
searchParams.get('allTime') === '1' ||
|
||||
Boolean(filters.provider || filters.executorKey || filters.payStatus || filters.taskStatus)
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ['admin-orders', queryParams],
|
||||
queryFn: () => fetchAdminOrders(queryParams),
|
||||
enabled: hasBootstrappedFilters,
|
||||
})
|
||||
const data = query.data?.data
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
keyword: filters.keyword || undefined,
|
||||
provider: filters.provider || undefined,
|
||||
executorKey: filters.executorKey || undefined,
|
||||
payStatus: filters.payStatus || undefined,
|
||||
taskStatus: filters.taskStatus || undefined,
|
||||
@@ -82,6 +120,7 @@ export default function AdminOrdersPage() {
|
||||
filters.executorKey,
|
||||
filters.keyword,
|
||||
filters.payStatus,
|
||||
filters.provider,
|
||||
filters.taskStatus,
|
||||
form,
|
||||
])
|
||||
@@ -185,14 +224,14 @@ export default function AdminOrdersPage() {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '金额 / 时间',
|
||||
title: '金额 / 支付时间',
|
||||
minWidth: 180,
|
||||
render: (_, row) => (
|
||||
<div className="cell-stack">
|
||||
<strong>
|
||||
{row.totalAmount || '0.00'} {row.currency}
|
||||
</strong>
|
||||
<span className="muted">{formatAdminDateTime(row.createdAt)}</span>
|
||||
<span className="muted">{formatAdminDateTime(row.paidAt || row.createdAt)}</span>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -200,29 +239,50 @@ export default function AdminOrdersPage() {
|
||||
|
||||
function applyFilters(values: OrderFilterForm) {
|
||||
const { dateRange, ...restValues } = values
|
||||
const [dateFrom, dateTo] = Array.isArray(dateRange)
|
||||
const keyword = String(restValues.keyword || '').trim()
|
||||
const [rawFrom, rawTo] = Array.isArray(dateRange)
|
||||
? dateRange.map((item: { format?: (pattern: string) => string }) =>
|
||||
item?.format ? item.format('YYYY-MM-DD') : '',
|
||||
)
|
||||
: ['', '']
|
||||
|
||||
setSearchParams(cleanParams({ ...restValues, dateFrom, dateTo, page: 1, pageSize }))
|
||||
// 有关键词时不强制日期(后端也会忽略日期);清空日期且无关键词则查全部时间。
|
||||
const dateFrom = rawFrom || ''
|
||||
const dateTo = rawTo || ''
|
||||
const allTime = !dateFrom && !dateTo && !keyword ? '1' : ''
|
||||
|
||||
setSearchParams(
|
||||
cleanParams({
|
||||
...restValues,
|
||||
keyword,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
allTime,
|
||||
page: 1,
|
||||
pageSize,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
const { dateFrom, dateTo } = resolveDefaultOrderDateRange()
|
||||
form.setFieldsValue({
|
||||
keyword: undefined,
|
||||
provider: undefined,
|
||||
executorKey: undefined,
|
||||
payStatus: undefined,
|
||||
taskStatus: undefined,
|
||||
dateRange: undefined,
|
||||
dateRange: [dayjs(dateFrom), dayjs(dateTo)],
|
||||
})
|
||||
setSearchParams(cleanParams({ page: 1, pageSize }))
|
||||
setSearchParams(cleanParams({ page: 1, pageSize, dateFrom, dateTo }))
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="page-stack">
|
||||
<PageHeader title="订单" description="查看订单入库、商品匹配和任务拆分情况。" />
|
||||
<PageHeader
|
||||
title="订单"
|
||||
description="默认近 7 天(按支付/成团时间)。搜单号或商品时不限日期。"
|
||||
/>
|
||||
|
||||
<Card>
|
||||
<Form<OrderFilterForm>
|
||||
@@ -234,8 +294,16 @@ export default function AdminOrdersPage() {
|
||||
<Form.Item name="keyword">
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="订单号 / 商品标题 / SKU 标题 / 买家 / 店铺"
|
||||
style={{ width: 320 }}
|
||||
placeholder="订单号 / 商品标题 / SKU / 买家 / 店铺"
|
||||
style={{ width: 300 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="provider">
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="来源渠道"
|
||||
options={adminOrderProviderOptions}
|
||||
style={{ minWidth: 130 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="executorKey">
|
||||
@@ -243,15 +311,15 @@ export default function AdminOrdersPage() {
|
||||
allowClear
|
||||
placeholder="履约渠道"
|
||||
options={adminOrderExecutorOptions}
|
||||
style={{ minWidth: 150 }}
|
||||
style={{ minWidth: 140 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="payStatus">
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="支付状态"
|
||||
options={adminPayStatusOptions}
|
||||
style={{ minWidth: 140 }}
|
||||
options={adminPayStatusOptions.filter((item) => item.value)}
|
||||
style={{ minWidth: 130 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="taskStatus">
|
||||
@@ -260,12 +328,12 @@ export default function AdminOrdersPage() {
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="任务状态"
|
||||
options={adminTaskStatusOptions}
|
||||
style={{ minWidth: 170 }}
|
||||
options={adminTaskStatusOptions.filter((item) => item.value)}
|
||||
style={{ minWidth: 150 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="dateRange">
|
||||
<AdminRangePicker />
|
||||
<AdminRangePicker allowClear placeholder={['支付开始', '支付结束']} />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
@@ -283,7 +351,7 @@ export default function AdminOrdersPage() {
|
||||
<Card>
|
||||
<Table<AdminOrderListItem>
|
||||
rowKey="orderId"
|
||||
loading={query.isLoading || query.isFetching}
|
||||
loading={query.isLoading || query.isFetching || !hasBootstrappedFilters}
|
||||
columns={columns}
|
||||
dataSource={data?.items || []}
|
||||
scroll={{ x: 900 }}
|
||||
@@ -292,7 +360,14 @@ export default function AdminOrdersPage() {
|
||||
pageSize: data?.pagination.pageSize || pageSize,
|
||||
total: data?.pagination.total || 0,
|
||||
onChange: (nextPage, nextPageSize) => {
|
||||
setSearchParams(cleanParams({ ...filters, page: nextPage, pageSize: nextPageSize }))
|
||||
setSearchParams(
|
||||
cleanParams({
|
||||
...filters,
|
||||
allTime: searchParams.get('allTime') || '',
|
||||
page: nextPage,
|
||||
pageSize: nextPageSize,
|
||||
}),
|
||||
)
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user