fix(fulfillment): 修复载体重试并完善核销任务列表
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { ReloadOutlined, SaveOutlined } from '@ant-design/icons'
|
||||
import { ReloadOutlined, SaveOutlined, SearchOutlined } from '@ant-design/icons'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
type VoucherConsumePolicy,
|
||||
type VoucherConsumeProvider,
|
||||
} from '@/services/admin'
|
||||
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||
import { formatDateTime } from '@/utils/date-time'
|
||||
|
||||
const PROVIDER_LABEL: Record<VoucherConsumeProvider, string> = {
|
||||
@@ -90,16 +92,63 @@ const JOB_STATUS: Record<string, { color: string; label: string }> = {
|
||||
cancelled: { color: 'default', label: '已取消' },
|
||||
}
|
||||
|
||||
const PROVIDER_FILTER_OPTIONS: Array<{ value: VoucherConsumeProvider; label: string }> = [
|
||||
{ value: 'kuaishou_industry', label: PROVIDER_LABEL.kuaishou_industry },
|
||||
{ value: 'douyin_mashida', label: PROVIDER_LABEL.douyin_mashida },
|
||||
]
|
||||
|
||||
const CHANNEL_FILTER_OPTIONS: Array<{ value: VoucherConsumeChannel; label: string }> = [
|
||||
{ value: 'affiliate_dash', label: CHANNEL_LABEL.affiliate_dash },
|
||||
{ value: 'work_order', label: CHANNEL_LABEL.work_order },
|
||||
{ value: 'manual', label: CHANNEL_LABEL.manual },
|
||||
{ value: 'third_party_voucher', label: CHANNEL_LABEL.third_party_voucher },
|
||||
{ value: 'on_account', label: CHANNEL_LABEL.on_account },
|
||||
]
|
||||
|
||||
const JOB_STATUS_FILTER_OPTIONS: Array<{
|
||||
value: VoucherConsumeJob['status']
|
||||
label: string
|
||||
}> = [
|
||||
{ value: 'pending', label: JOB_STATUS.pending!.label },
|
||||
{ value: 'processing', label: JOB_STATUS.processing!.label },
|
||||
{ value: 'succeeded', label: JOB_STATUS.succeeded!.label },
|
||||
{ value: 'failed', label: JOB_STATUS.failed!.label },
|
||||
{ value: 'cancelled', label: JOB_STATUS.cancelled!.label },
|
||||
]
|
||||
|
||||
export default function VoucherConsumePolicyPanel() {
|
||||
const queryClient = useQueryClient()
|
||||
const [draft, setDraft] = useState<VoucherConsumePolicy | null>(null)
|
||||
const [keywordDraft, setKeywordDraft] = useState('')
|
||||
const [keyword, setKeyword] = useState('')
|
||||
const [providerFilter, setProviderFilter] = useState<VoucherConsumeProvider | ''>('')
|
||||
const [channelFilter, setChannelFilter] = useState<VoucherConsumeChannel | ''>('')
|
||||
const [statusFilter, setStatusFilter] = useState<VoucherConsumeJob['status'] | ''>('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
|
||||
const policyQuery = useQuery({
|
||||
queryKey: ['voucher-consume-policy'],
|
||||
queryFn: fetchVoucherConsumePolicy,
|
||||
})
|
||||
const jobsQuery = useQuery({
|
||||
queryKey: ['voucher-consume-jobs'],
|
||||
queryFn: () => fetchVoucherConsumeJobs({ page: 1, pageSize: 20 }),
|
||||
queryKey: [
|
||||
'voucher-consume-jobs',
|
||||
keyword,
|
||||
providerFilter,
|
||||
channelFilter,
|
||||
statusFilter,
|
||||
page,
|
||||
pageSize,
|
||||
],
|
||||
queryFn: () =>
|
||||
fetchVoucherConsumeJobs({
|
||||
keyword: keyword || undefined,
|
||||
provider: providerFilter || undefined,
|
||||
channel: channelFilter || undefined,
|
||||
status: statusFilter || undefined,
|
||||
page,
|
||||
pageSize,
|
||||
}),
|
||||
refetchInterval: 10_000,
|
||||
})
|
||||
|
||||
@@ -134,6 +183,16 @@ export default function VoucherConsumePolicyPanel() {
|
||||
})
|
||||
|
||||
const rows = useMemo(() => buildPolicyRows(draft), [draft])
|
||||
const hasJobFilters = Boolean(keyword || providerFilter || channelFilter || statusFilter)
|
||||
|
||||
function resetJobFilters() {
|
||||
setKeywordDraft('')
|
||||
setKeyword('')
|
||||
setProviderFilter('')
|
||||
setChannelFilter('')
|
||||
setStatusFilter('')
|
||||
setPage(1)
|
||||
}
|
||||
|
||||
function updateChannel(
|
||||
provider: VoucherConsumeProvider,
|
||||
@@ -348,25 +407,92 @@ export default function VoucherConsumePolicyPanel() {
|
||||
loading={policyQuery.isLoading}
|
||||
/>
|
||||
|
||||
<Space className="full-width" style={{ justifyContent: 'space-between' }}>
|
||||
<Space className="full-width" style={{ justifyContent: 'space-between' }} wrap>
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>
|
||||
最近核销任务
|
||||
</Typography.Title>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() => void queryClient.invalidateQueries({ queryKey: ['voucher-consume-jobs'] })}
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
<Space wrap>
|
||||
<Input.Search
|
||||
allowClear
|
||||
prefix={<SearchOutlined />}
|
||||
placeholder="任务号 / 订单号 / 平台单号"
|
||||
value={keywordDraft}
|
||||
onChange={(event) => {
|
||||
const value = event.target.value
|
||||
setKeywordDraft(value)
|
||||
if (!value) {
|
||||
setKeyword('')
|
||||
setPage(1)
|
||||
}
|
||||
}}
|
||||
onSearch={(value) => {
|
||||
setKeyword(String(value || '').trim())
|
||||
setPage(1)
|
||||
}}
|
||||
style={{ width: 260 }}
|
||||
/>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="全部平台"
|
||||
value={providerFilter || undefined}
|
||||
options={PROVIDER_FILTER_OPTIONS}
|
||||
onChange={(value: VoucherConsumeProvider | undefined) => {
|
||||
setProviderFilter(value || '')
|
||||
setPage(1)
|
||||
}}
|
||||
style={{ width: 150 }}
|
||||
/>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="全部渠道"
|
||||
value={channelFilter || undefined}
|
||||
options={CHANNEL_FILTER_OPTIONS}
|
||||
onChange={(value: VoucherConsumeChannel | undefined) => {
|
||||
setChannelFilter(value || '')
|
||||
setPage(1)
|
||||
}}
|
||||
style={{ width: 150 }}
|
||||
/>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="全部状态"
|
||||
value={statusFilter || undefined}
|
||||
options={JOB_STATUS_FILTER_OPTIONS}
|
||||
onChange={(value: VoucherConsumeJob['status'] | undefined) => {
|
||||
setStatusFilter(value || '')
|
||||
setPage(1)
|
||||
}}
|
||||
style={{ width: 130 }}
|
||||
/>
|
||||
<Button disabled={!hasJobFilters} onClick={resetJobFilters}>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
onClick={() =>
|
||||
void queryClient.invalidateQueries({ queryKey: ['voucher-consume-jobs'] })
|
||||
}
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
<PercentTable
|
||||
rowKey="id"
|
||||
size="small"
|
||||
columns={jobColumns}
|
||||
dataSource={jobsQuery.data?.data.items || []}
|
||||
loading={jobsQuery.isLoading}
|
||||
pagination={false}
|
||||
scroll={{ x: 1100 }}
|
||||
loading={jobsQuery.isFetching}
|
||||
pagination={buildAdminTablePagination({
|
||||
current: jobsQuery.data?.data.page || page,
|
||||
pageSize: jobsQuery.data?.data.pageSize || pageSize,
|
||||
total: jobsQuery.data?.data.total || 0,
|
||||
onChange: (nextPage, nextPageSize) => {
|
||||
setPage(nextPageSize !== pageSize ? 1 : nextPage)
|
||||
setPageSize(nextPageSize)
|
||||
},
|
||||
})}
|
||||
scroll={{ x: 1200 }}
|
||||
/>
|
||||
</Space>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user