176 lines
5.6 KiB
TypeScript
176 lines
5.6 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 { fetchAdminWorkerOrderFeedbacks, resolveAdminWorkerOrderFeedback } from '@/services/admin'
|
||
import type { WorkerOrderFeedback } from '@/types/worker-platform'
|
||
import { formatAdminDateTime } from '@/utils/admin-time'
|
||
|
||
type FeedbackReplyFormValues = {
|
||
reply?: string
|
||
}
|
||
|
||
export default function FeedbacksPanel() {
|
||
const { message } = App.useApp()
|
||
const queryClient = useQueryClient()
|
||
const [searchParams, setSearchParams] = useSearchParams()
|
||
const [target, setTarget] = useState<WorkerOrderFeedback | null>(null)
|
||
const [form] = Form.useForm<FeedbackReplyFormValues>()
|
||
const feedbacksQuery = useQuery({
|
||
queryKey: ['admin-worker-order-feedbacks', 'pending'],
|
||
queryFn: () => fetchAdminWorkerOrderFeedbacks('pending'),
|
||
})
|
||
|
||
useEffect(() => {
|
||
const feedbackId = Number(searchParams.get('feedbackId') || 0)
|
||
if (!Number.isInteger(feedbackId) || feedbackId <= 0) return
|
||
const feedback = (feedbacksQuery.data?.data.items || []).find(
|
||
(item) => item.feedbackId === feedbackId,
|
||
)
|
||
if (!feedback) return
|
||
setTarget(feedback)
|
||
form.resetFields()
|
||
setSearchParams(
|
||
(current) => {
|
||
current.delete('feedbackId')
|
||
return current
|
||
},
|
||
{ replace: true },
|
||
)
|
||
}, [feedbacksQuery.data, form, searchParams, setSearchParams])
|
||
|
||
async function submitReply(values: FeedbackReplyFormValues) {
|
||
if (!target) return
|
||
try {
|
||
await resolveAdminWorkerOrderFeedback(target.feedbackId, {
|
||
reply: String(values.reply || '').trim(),
|
||
})
|
||
message.success('问题反馈已处理')
|
||
setTarget(null)
|
||
form.resetFields()
|
||
await Promise.all([
|
||
queryClient.invalidateQueries({ queryKey: ['admin-worker-order-feedbacks'] }),
|
||
queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-orders'] }),
|
||
])
|
||
} catch (error) {
|
||
message.error(error instanceof Error ? error.message : '处理问题反馈失败')
|
||
}
|
||
}
|
||
|
||
const columns: TableColumnsType<WorkerOrderFeedback> = [
|
||
{
|
||
title: '订单',
|
||
width: 260,
|
||
render: (_, feedback) => (
|
||
<div className="cell-stack">
|
||
<Typography.Text strong>{feedback.productName || '-'}</Typography.Text>
|
||
<Typography.Text type="secondary">{feedback.workOrderNo || '-'}</Typography.Text>
|
||
<Typography.Text type="secondary">{feedback.shopName || '-'}</Typography.Text>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
title: '打手',
|
||
width: 180,
|
||
render: (_, feedback) => (
|
||
<Space wrap>
|
||
<Typography.Text>{feedback.workerName || `打手 #${feedback.workerId}`}</Typography.Text>
|
||
{feedback.shareQuantity ? (
|
||
<Tag color="purple">拼单 {feedback.shareQuantity} 份</Tag>
|
||
) : null}
|
||
</Space>
|
||
),
|
||
},
|
||
{
|
||
title: '反馈内容',
|
||
render: (_, feedback) => (
|
||
<Typography.Text ellipsis={{ tooltip: feedback.content }}>
|
||
{feedback.content}
|
||
</Typography.Text>
|
||
),
|
||
},
|
||
{
|
||
title: '反馈时间',
|
||
width: 180,
|
||
render: (_, feedback) => formatAdminDateTime(feedback.createdAt),
|
||
},
|
||
{
|
||
title: '操作',
|
||
width: 120,
|
||
render: (_, feedback) => (
|
||
<Button type="primary" size="small" onClick={() => setTarget(feedback)}>
|
||
处理
|
||
</Button>
|
||
),
|
||
},
|
||
]
|
||
|
||
return (
|
||
<>
|
||
<Card
|
||
title={`待处理问题反馈(${feedbacksQuery.data?.data.items.length || 0})`}
|
||
extra={
|
||
<Button
|
||
icon={<ReloadOutlined />}
|
||
loading={feedbacksQuery.isFetching}
|
||
onClick={() => feedbacksQuery.refetch()}
|
||
>
|
||
刷新
|
||
</Button>
|
||
}
|
||
bordered={false}
|
||
>
|
||
<Table<WorkerOrderFeedback>
|
||
rowKey="feedbackId"
|
||
size="small"
|
||
loading={feedbacksQuery.isLoading}
|
||
dataSource={feedbacksQuery.data?.data.items || []}
|
||
columns={columns}
|
||
pagination={false}
|
||
locale={{ emptyText: '暂无待处理问题反馈' }}
|
||
scroll={{ x: 900 }}
|
||
/>
|
||
</Card>
|
||
<Modal
|
||
title="处理问题反馈"
|
||
open={Boolean(target)}
|
||
onCancel={() => {
|
||
setTarget(null)
|
||
form.resetFields()
|
||
}}
|
||
onOk={() => form.submit()}
|
||
okText="完成处理"
|
||
destroyOnHidden
|
||
>
|
||
{target ? (
|
||
<Space direction="vertical" size={12} style={{ width: '100%' }}>
|
||
<Typography.Text>
|
||
{target.workerName || `打手 #${target.workerId}`}:{target.content}
|
||
</Typography.Text>
|
||
<Typography.Text type="secondary">
|
||
完成处理不会更改订单状态、验收或资金,仅向打手记录处理回复。
|
||
</Typography.Text>
|
||
<Form form={form} layout="vertical" onFinish={submitReply}>
|
||
<Form.Item
|
||
label="处理回复"
|
||
name="reply"
|
||
rules={[{ required: true, whitespace: true, message: '请填写处理回复' }]}
|
||
>
|
||
<Input.TextArea
|
||
rows={4}
|
||
maxLength={500}
|
||
showCount
|
||
placeholder="请说明已如何处理,或请打手后续操作"
|
||
/>
|
||
</Form.Item>
|
||
</Form>
|
||
</Space>
|
||
) : null}
|
||
</Modal>
|
||
</>
|
||
)
|
||
}
|