204 lines
6.8 KiB
TypeScript
204 lines
6.8 KiB
TypeScript
import { ReloadOutlined } from '@ant-design/icons'
|
||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||
import { App, Button, Card, Form, Input, Modal, Space, Table, Tag, Typography } from 'antd'
|
||
import type { TableColumnsType } from 'antd'
|
||
import { useEffect, useState } from 'react'
|
||
import { useSearchParams } from 'react-router'
|
||
|
||
import { fetchAdminWorkerCancelRequests, reviewAdminWorkerCancelRequest } from '@/services/admin'
|
||
import type { WorkerCancelRequest } from '@/types/worker-platform'
|
||
import { formatAdminDateTime } from '@/utils/admin-time'
|
||
|
||
type ReviewFormValues = {
|
||
reviewNote?: string
|
||
}
|
||
|
||
export default function CancelRequestsPanel() {
|
||
const { message } = App.useApp()
|
||
const queryClient = useQueryClient()
|
||
const [searchParams, setSearchParams] = useSearchParams()
|
||
const [reviewTarget, setReviewTarget] = useState<{
|
||
request: WorkerCancelRequest
|
||
approved: boolean
|
||
} | null>(null)
|
||
const [form] = Form.useForm<ReviewFormValues>()
|
||
const requestsQuery = useQuery({
|
||
queryKey: ['admin-worker-cancel-requests', 'pending'],
|
||
queryFn: () => fetchAdminWorkerCancelRequests('pending'),
|
||
})
|
||
|
||
useEffect(() => {
|
||
const requestId = Number(searchParams.get('cancelRequestId') || 0)
|
||
if (!Number.isInteger(requestId) || requestId <= 0) return
|
||
const request = (requestsQuery.data?.data.items || []).find(
|
||
(item) => item.requestId === requestId,
|
||
)
|
||
if (!request) return
|
||
setReviewTarget({ request, approved: true })
|
||
form.resetFields()
|
||
setSearchParams(
|
||
(current) => {
|
||
current.delete('cancelRequestId')
|
||
return current
|
||
},
|
||
{ replace: true },
|
||
)
|
||
}, [form, requestsQuery.data, searchParams, setSearchParams])
|
||
|
||
async function refreshAll() {
|
||
await Promise.all([
|
||
queryClient.invalidateQueries({ queryKey: ['admin-worker-cancel-requests'] }),
|
||
queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-orders'] }),
|
||
queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-summary'] }),
|
||
])
|
||
}
|
||
|
||
function openReview(request: WorkerCancelRequest, approved: boolean) {
|
||
setReviewTarget({ request, approved })
|
||
form.resetFields()
|
||
}
|
||
|
||
async function submitReview(values: ReviewFormValues) {
|
||
if (!reviewTarget) return
|
||
try {
|
||
const response = await reviewAdminWorkerCancelRequest(reviewTarget.request.requestId, {
|
||
approved: reviewTarget.approved,
|
||
reviewNote: String(values.reviewNote || '').trim(),
|
||
})
|
||
const result = response.data
|
||
const resultText = result.accountDisabled
|
||
? ',该打手已封禁'
|
||
: result.levelChanged
|
||
? ',该打手已降一级 VIP'
|
||
: ''
|
||
message.success(`${result.approved ? '已同意' : '已驳回'}撤单申请${resultText}`)
|
||
setReviewTarget(null)
|
||
form.resetFields()
|
||
await refreshAll()
|
||
} catch (error) {
|
||
message.error(error instanceof Error ? error.message : '审核撤单申请失败')
|
||
}
|
||
}
|
||
|
||
const columns: TableColumnsType<WorkerCancelRequest> = [
|
||
{
|
||
title: '订单号 / 店铺',
|
||
width: 260,
|
||
render: (_, request) => (
|
||
<div className="cell-stack">
|
||
<Typography.Text strong>{request.platformOrderId || '-'}</Typography.Text>
|
||
<Typography.Text type="secondary">{request.shopName || '-'}</Typography.Text>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
title: '打手 / 份数',
|
||
width: 180,
|
||
render: (_, request) => (
|
||
<div className="cell-stack">
|
||
<Typography.Text>{request.workerName || `打手 #${request.workerId}`}</Typography.Text>
|
||
{request.shareQuantity ? (
|
||
<Tag color="purple">拼单 {request.shareQuantity} 份</Tag>
|
||
) : (
|
||
<Tag>整单</Tag>
|
||
)}
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
title: '申请原因',
|
||
render: (_, request) => (
|
||
<Typography.Text ellipsis={{ tooltip: request.reason }}>{request.reason}</Typography.Text>
|
||
),
|
||
},
|
||
{
|
||
title: '申请时间',
|
||
width: 180,
|
||
render: (_, request) => formatAdminDateTime(request.createdAt),
|
||
},
|
||
{
|
||
title: '操作',
|
||
width: 180,
|
||
render: (_, request) => (
|
||
<Space>
|
||
<Button type="primary" size="small" onClick={() => openReview(request, true)}>
|
||
同意
|
||
</Button>
|
||
<Button danger size="small" onClick={() => openReview(request, false)}>
|
||
驳回
|
||
</Button>
|
||
</Space>
|
||
),
|
||
},
|
||
]
|
||
|
||
return (
|
||
<>
|
||
<Card
|
||
title={`待审核撤单申请(${requestsQuery.data?.data.items.length || 0})`}
|
||
extra={
|
||
<Button
|
||
icon={<ReloadOutlined />}
|
||
loading={requestsQuery.isFetching}
|
||
onClick={() => requestsQuery.refetch()}
|
||
>
|
||
刷新
|
||
</Button>
|
||
}
|
||
bordered={false}
|
||
>
|
||
<Table<WorkerCancelRequest>
|
||
rowKey="requestId"
|
||
size="small"
|
||
loading={requestsQuery.isLoading}
|
||
dataSource={requestsQuery.data?.data.items || []}
|
||
columns={columns}
|
||
pagination={false}
|
||
locale={{ emptyText: '暂无待审核撤单申请' }}
|
||
scroll={{ x: 900 }}
|
||
/>
|
||
</Card>
|
||
<Modal
|
||
title={reviewTarget?.approved ? '同意撤单申请' : '驳回撤单申请'}
|
||
open={Boolean(reviewTarget)}
|
||
onCancel={() => {
|
||
setReviewTarget(null)
|
||
form.resetFields()
|
||
}}
|
||
onOk={() => form.submit()}
|
||
okText={reviewTarget?.approved ? '确认同意' : '确认驳回'}
|
||
okButtonProps={reviewTarget?.approved ? undefined : { danger: true }}
|
||
destroyOnHidden
|
||
>
|
||
{reviewTarget ? (
|
||
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
||
<Typography.Text>
|
||
{reviewTarget.request.workerName || `打手 #${reviewTarget.request.workerId}`}:
|
||
{reviewTarget.request.reason}
|
||
</Typography.Text>
|
||
<Typography.Text type={reviewTarget.approved ? 'warning' : 'secondary'}>
|
||
{reviewTarget.approved
|
||
? '同意后将退还冻结押金,并将整单或对应拼单份额回到大厅;该申请会计入撤单处罚次数。'
|
||
: '驳回后不会计入累计次数,且会释放本次申请的当天额度。'}
|
||
</Typography.Text>
|
||
<Form form={form} layout="vertical" onFinish={submitReview}>
|
||
<Form.Item
|
||
label="审核备注"
|
||
name="reviewNote"
|
||
rules={[{ required: true, whitespace: true, message: '请填写审核备注' }]}
|
||
>
|
||
<Input.TextArea
|
||
rows={3}
|
||
maxLength={300}
|
||
showCount
|
||
placeholder="请填写审核结论或说明"
|
||
/>
|
||
</Form.Item>
|
||
</Form>
|
||
</Space>
|
||
) : null}
|
||
</Modal>
|
||
</>
|
||
)
|
||
}
|