优化订单打手信息展示
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import { ClockCircleOutlined } from '@ant-design/icons'
|
||||
import { ClockCircleOutlined, LinkOutlined } from '@ant-design/icons'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Empty, Skeleton, Tag, Timeline, Tooltip, Typography } from 'antd'
|
||||
import { Empty, Skeleton, Space, Tag, Timeline, Tooltip, Typography } from 'antd'
|
||||
import { useNavigate } from 'react-router'
|
||||
import dayjs from '@/lib/dayjs'
|
||||
|
||||
import { fetchAdminWorkOrderEvents } from '@/services/admin'
|
||||
import { fetchWorkerMyOrderEvents } from '@/services/worker'
|
||||
import type { WorkOrderEvent } from '@/types/worker-platform'
|
||||
import { getAdminRole } from '@/utils/admin-auth'
|
||||
|
||||
type EventNodeStyle = {
|
||||
/** 节点标题(主文案) */
|
||||
@@ -279,6 +281,8 @@ export default function WorkOrderEventTimeline({
|
||||
orderId: number
|
||||
viewer: 'admin' | 'worker'
|
||||
}) {
|
||||
const navigate = useNavigate()
|
||||
const canNavigateToWorker = viewer === 'admin' && getAdminRole() !== 'support'
|
||||
const eventsQuery = useQuery({
|
||||
queryKey: ['work-order-events', viewer, orderId],
|
||||
queryFn: () =>
|
||||
@@ -324,19 +328,42 @@ export default function WorkOrderEventTimeline({
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
<div className="work-order-event-actor">
|
||||
<Tag
|
||||
color={
|
||||
event.actorType === 'system'
|
||||
? 'default'
|
||||
: event.actorType === 'worker'
|
||||
? 'blue'
|
||||
: event.actorType === 'admin'
|
||||
? 'green'
|
||||
: 'default'
|
||||
}
|
||||
>
|
||||
{event.actorName || event.actorType}
|
||||
</Tag>
|
||||
{event.worker ? (
|
||||
<Space size={[4, 4]} wrap>
|
||||
<Tag
|
||||
color="blue"
|
||||
className={canNavigateToWorker ? 'work-order-event-worker-link' : undefined}
|
||||
onClick={() => {
|
||||
if (!canNavigateToWorker) return
|
||||
void navigate(
|
||||
`/admin/worker-platform?tab=workers&workerId=${event.worker?.workerId}`,
|
||||
)
|
||||
}}
|
||||
>
|
||||
{event.worker.displayName} #{event.worker.workerId}
|
||||
{canNavigateToWorker ? <LinkOutlined /> : null}
|
||||
</Tag>
|
||||
<Typography.Text type="secondary" className="work-order-event-worker-meta">
|
||||
{event.worker.username} ·{' '}
|
||||
{event.worker.workerType === 'internal' ? '内部' : '外部'} ·{' '}
|
||||
{event.worker.levelName} · {formatWorkerStatus(event.worker.status)}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
) : (
|
||||
<Tag
|
||||
color={
|
||||
event.actorType === 'system'
|
||||
? 'default'
|
||||
: event.actorType === 'worker'
|
||||
? 'blue'
|
||||
: event.actorType === 'admin'
|
||||
? 'green'
|
||||
: 'default'
|
||||
}
|
||||
>
|
||||
{event.actorName || event.actorType}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
@@ -345,3 +372,13 @@ export default function WorkOrderEventTimeline({
|
||||
|
||||
return <Timeline items={items} className="work-order-event-timeline" />
|
||||
}
|
||||
|
||||
function formatWorkerStatus(status: string) {
|
||||
const labels: Record<string, string> = {
|
||||
active: '已启用',
|
||||
pending_review: '待审核',
|
||||
rejected: '已冻结',
|
||||
disabled: '已停用',
|
||||
}
|
||||
return labels[status] || status || '未知状态'
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
Typography,
|
||||
} from 'antd'
|
||||
import type { TableColumnsType } from 'antd'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router'
|
||||
|
||||
import {
|
||||
creditAdminWorkerWallet,
|
||||
@@ -50,10 +51,12 @@ type FreezeWorkerFormValues = {
|
||||
export default function WorkersPanel() {
|
||||
const { message, modal } = App.useApp()
|
||||
const queryClient = useQueryClient()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [status, setStatus] = useState('')
|
||||
const [keywordInput, setKeywordInput] = useState('')
|
||||
const [keyword, setKeyword] = useState('')
|
||||
const [workerType, setWorkerType] = useState('')
|
||||
const [targetWorkerId, setTargetWorkerId] = useState(0)
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
|
||||
const [creditWorker, setCreditWorker] = useState<WorkerUser | null>(null)
|
||||
@@ -74,9 +77,39 @@ export default function WorkersPanel() {
|
||||
(account) => account.accountChannel === 'alipay',
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const workerId = Number(searchParams.get('workerId') || 0)
|
||||
if (!Number.isInteger(workerId) || workerId <= 0) {
|
||||
setTargetWorkerId(0)
|
||||
return
|
||||
}
|
||||
setTargetWorkerId(workerId)
|
||||
setKeywordInput('')
|
||||
setKeyword('')
|
||||
setStatus('')
|
||||
setWorkerType('')
|
||||
setPage(1)
|
||||
}, [searchParams])
|
||||
|
||||
const workersQuery = useQuery({
|
||||
queryKey: ['admin-worker-platform-workers', status, keyword, workerType, page, pageSize],
|
||||
queryFn: () => fetchAdminWorkerUsers({ status, keyword, workerType, page, pageSize }),
|
||||
queryKey: [
|
||||
'admin-worker-platform-workers',
|
||||
status,
|
||||
keyword,
|
||||
workerType,
|
||||
targetWorkerId,
|
||||
page,
|
||||
pageSize,
|
||||
],
|
||||
queryFn: () =>
|
||||
fetchAdminWorkerUsers({
|
||||
status,
|
||||
keyword,
|
||||
workerType,
|
||||
workerId: targetWorkerId || undefined,
|
||||
page,
|
||||
pageSize,
|
||||
}),
|
||||
})
|
||||
const levelsQuery = useQuery({
|
||||
queryKey: ['admin-worker-platform-levels'],
|
||||
@@ -105,7 +138,15 @@ export default function WorkersPanel() {
|
||||
setKeyword('')
|
||||
setStatus('')
|
||||
setWorkerType('')
|
||||
setTargetWorkerId(0)
|
||||
setPage(1)
|
||||
setSearchParams(
|
||||
(current) => {
|
||||
current.delete('workerId')
|
||||
return current
|
||||
},
|
||||
{ replace: true },
|
||||
)
|
||||
}
|
||||
|
||||
async function review(worker: WorkerUser, nextStatus: string) {
|
||||
@@ -431,6 +472,9 @@ export default function WorkersPanel() {
|
||||
}
|
||||
bordered={false}
|
||||
>
|
||||
{targetWorkerId ? (
|
||||
<Typography.Text type="secondary">已定位打手 #{targetWorkerId}</Typography.Text>
|
||||
) : null}
|
||||
<Table<WorkerUser>
|
||||
rowKey="workerId"
|
||||
loading={workersQuery.isLoading}
|
||||
|
||||
@@ -292,6 +292,18 @@
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.work-order-event-worker-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.work-order-event-worker-link .anticon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.work-order-event-worker-meta {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.work-order-event-desc {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -495,6 +495,14 @@ export type WorkOrderEvent = {
|
||||
fromStatus: string
|
||||
toStatus: string
|
||||
payload: Record<string, unknown>
|
||||
worker?: {
|
||||
workerId: number
|
||||
displayName: string
|
||||
username: string
|
||||
workerType: string
|
||||
levelName: string
|
||||
status: string
|
||||
}
|
||||
}
|
||||
|
||||
export type Pagination = {
|
||||
|
||||
Reference in New Issue
Block a user