修复接单平台并发、分页与补资料
This commit is contained in:
@@ -25,7 +25,7 @@ export default function WorkerLayout() {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const { message } = App.useApp()
|
||||
const username = getWorkerUsername() || '接单员'
|
||||
const username = getWorkerUsername() || '打手'
|
||||
const status = getWorkerStatus()
|
||||
|
||||
async function submitLogout() {
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
fetchAdminWorkOrders,
|
||||
markAdminWorkOrderProblem,
|
||||
publishAdminWorkOrder,
|
||||
resolveAdminProblemWorkOrder,
|
||||
reviewAdminWorkerUser,
|
||||
saveAdminWorkCategory,
|
||||
saveAdminWorkProductRule,
|
||||
@@ -58,13 +59,17 @@ import type {
|
||||
WorkerLevel,
|
||||
WorkerUser,
|
||||
} from '@/types/worker-platform'
|
||||
import {
|
||||
ADMIN_DEFAULT_PAGE_SIZE,
|
||||
buildAdminTablePagination,
|
||||
} from '@/utils/admin-pagination'
|
||||
|
||||
export default function AdminWorkerPlatformPage() {
|
||||
return (
|
||||
<section className="page-stack">
|
||||
<PageHeader
|
||||
title="接单平台"
|
||||
description="管理接单员、接单工单、等级权限和本地 Mock 工单。"
|
||||
description="管理打手、接单工单、等级权限和本地 Mock 工单。"
|
||||
/>
|
||||
<SummaryCards />
|
||||
<Tabs
|
||||
@@ -73,7 +78,7 @@ export default function AdminWorkerPlatformPage() {
|
||||
{ key: 'orders', label: '接单工单', children: <WorkOrdersPanel /> },
|
||||
{ key: 'rules', label: '物品规则', children: <ProductRulesPanel /> },
|
||||
{ key: 'categories', label: '分类', children: <CategoriesPanel /> },
|
||||
{ key: 'workers', label: '接单员', children: <WorkersPanel /> },
|
||||
{ key: 'workers', label: '打手', children: <WorkersPanel /> },
|
||||
{ key: 'levels', label: '等级权限', children: <LevelsPanel /> },
|
||||
]}
|
||||
/>
|
||||
@@ -92,7 +97,7 @@ function SummaryCards() {
|
||||
<div className="metric-grid four">
|
||||
<Card>
|
||||
<Statistic
|
||||
title="待审核接单员"
|
||||
title="待审核打手"
|
||||
value={summary?.pendingWorkerCount || 0}
|
||||
/>
|
||||
</Card>
|
||||
@@ -116,15 +121,20 @@ function WorkOrdersPanel() {
|
||||
const { message } = App.useApp()
|
||||
const queryClient = useQueryClient()
|
||||
const [status, setStatus] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
|
||||
const [problemOrder, setProblemOrder] = useState<WorkOrder | null>(null)
|
||||
const [resolutionOrder, setResolutionOrder] = useState<WorkOrder | null>(null)
|
||||
const [materialOrder, setMaterialOrder] = useState<WorkOrder | null>(null)
|
||||
const [problemForm] = Form.useForm()
|
||||
const [resolutionForm] = Form.useForm()
|
||||
const [materialForm] = Form.useForm()
|
||||
|
||||
const ordersQuery = useQuery({
|
||||
queryKey: ['admin-worker-platform-orders', status],
|
||||
queryFn: () => fetchAdminWorkOrders({ status }),
|
||||
queryKey: ['admin-worker-platform-orders', status, page, pageSize],
|
||||
queryFn: () => fetchAdminWorkOrders({ status, page, pageSize }),
|
||||
})
|
||||
const ordersPagination = ordersQuery.data?.data.pagination
|
||||
|
||||
async function refreshAll() {
|
||||
await Promise.all([
|
||||
@@ -145,25 +155,47 @@ function WorkOrdersPanel() {
|
||||
await action()
|
||||
message.success(successMessage)
|
||||
await refreshAll()
|
||||
return true
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '操作失败')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function submitProblem(values: { note?: string }) {
|
||||
if (!problemOrder) return
|
||||
await runAction(
|
||||
const succeeded = await runAction(
|
||||
() =>
|
||||
markAdminWorkOrderProblem(problemOrder.workOrderId, values.note || ''),
|
||||
'已标记问题单',
|
||||
)
|
||||
if (!succeeded) return
|
||||
setProblemOrder(null)
|
||||
problemForm.resetFields()
|
||||
}
|
||||
|
||||
async function submitResolution(values: {
|
||||
action?: 'return_to_worker' | 'reopen' | 'cancel_release' | 'cancel_deduct'
|
||||
note?: string
|
||||
}) {
|
||||
if (!resolutionOrder || !values.action) return
|
||||
const action = values.action
|
||||
const succeeded = await runAction(
|
||||
() =>
|
||||
resolveAdminProblemWorkOrder(resolutionOrder.workOrderId, {
|
||||
action,
|
||||
note: values.note || '',
|
||||
}),
|
||||
'问题单已处置',
|
||||
)
|
||||
if (!succeeded) return
|
||||
setResolutionOrder(null)
|
||||
resolutionForm.resetFields()
|
||||
}
|
||||
|
||||
async function submitMaterial(values: { fields?: Record<string, string> }) {
|
||||
if (!materialOrder) return
|
||||
await runAction(
|
||||
const succeeded = await runAction(
|
||||
() =>
|
||||
submitAdminWorkOrderMaterial(
|
||||
materialOrder.workOrderId,
|
||||
@@ -171,6 +203,7 @@ function WorkOrdersPanel() {
|
||||
),
|
||||
'订单资料已保存',
|
||||
)
|
||||
if (!succeeded) return
|
||||
setMaterialOrder(null)
|
||||
materialForm.resetFields()
|
||||
}
|
||||
@@ -227,7 +260,7 @@ function WorkOrdersPanel() {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '接单员',
|
||||
title: '打手',
|
||||
width: 140,
|
||||
render: (_, row) =>
|
||||
row.worker?.displayName || row.worker?.username || '-',
|
||||
@@ -297,6 +330,11 @@ function WorkOrdersPanel() {
|
||||
问题单
|
||||
</Button>
|
||||
) : null}
|
||||
{row.status === 'problem' ? (
|
||||
<Button icon={<FormOutlined />} onClick={() => setResolutionOrder(row)}>
|
||||
处置
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -342,8 +380,12 @@ function WorkOrdersPanel() {
|
||||
{ value: 'pending_acceptance', label: '待验收' },
|
||||
{ value: 'problem', label: '问题单' },
|
||||
{ value: 'accepted', label: '已验收' },
|
||||
{ value: 'cancelled', label: '已取消' },
|
||||
]}
|
||||
onChange={setStatus}
|
||||
onChange={(nextStatus) => {
|
||||
setStatus(nextStatus)
|
||||
setPage(1)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
@@ -361,7 +403,15 @@ function WorkOrdersPanel() {
|
||||
loading={ordersQuery.isLoading}
|
||||
dataSource={ordersQuery.data?.data.items || []}
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
pagination={buildAdminTablePagination({
|
||||
current: ordersPagination?.page || page,
|
||||
pageSize: ordersPagination?.pageSize || pageSize,
|
||||
total: ordersPagination?.total || 0,
|
||||
onChange: (nextPage, nextPageSize) => {
|
||||
setPage(nextPage)
|
||||
setPageSize(nextPageSize)
|
||||
},
|
||||
})}
|
||||
scroll={{ x: 1100 }}
|
||||
/>
|
||||
</Card>
|
||||
@@ -369,7 +419,10 @@ function WorkOrdersPanel() {
|
||||
<Modal
|
||||
title="标记问题单"
|
||||
open={Boolean(problemOrder)}
|
||||
onCancel={() => setProblemOrder(null)}
|
||||
onCancel={() => {
|
||||
setProblemOrder(null)
|
||||
problemForm.resetFields()
|
||||
}}
|
||||
onOk={() => problemForm.submit()}
|
||||
destroyOnHidden
|
||||
>
|
||||
@@ -384,10 +437,53 @@ function WorkOrdersPanel() {
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="处置问题单"
|
||||
open={Boolean(resolutionOrder)}
|
||||
onCancel={() => {
|
||||
setResolutionOrder(null)
|
||||
resolutionForm.resetFields()
|
||||
}}
|
||||
onOk={() => resolutionForm.submit()}
|
||||
destroyOnHidden
|
||||
>
|
||||
<Form
|
||||
form={resolutionForm}
|
||||
layout="vertical"
|
||||
initialValues={{ action: 'return_to_worker' }}
|
||||
onFinish={submitResolution}
|
||||
>
|
||||
<Form.Item
|
||||
label="处置方式"
|
||||
name="action"
|
||||
rules={[{ required: true, message: '请选择处置方式' }]}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'return_to_worker', label: '退回打手继续代练' },
|
||||
{ value: 'reopen', label: '释放押金并重新放回大厅' },
|
||||
{ value: 'cancel_release', label: '取消订单并退还押金' },
|
||||
{ value: 'cancel_deduct', label: '取消订单并扣除押金' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="处置备注"
|
||||
name="note"
|
||||
rules={[{ required: true, message: '请填写处置备注' }]}
|
||||
>
|
||||
<Input.TextArea rows={4} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="人工补充资料"
|
||||
open={Boolean(materialOrder)}
|
||||
onCancel={() => setMaterialOrder(null)}
|
||||
onCancel={() => {
|
||||
setMaterialOrder(null)
|
||||
materialForm.resetFields()
|
||||
}}
|
||||
onOk={() => materialForm.submit()}
|
||||
destroyOnHidden
|
||||
>
|
||||
@@ -804,13 +900,16 @@ function WorkersPanel() {
|
||||
const { message } = App.useApp()
|
||||
const queryClient = useQueryClient()
|
||||
const [status, setStatus] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
|
||||
const [creditWorker, setCreditWorker] = useState<WorkerUser | null>(null)
|
||||
const [creditForm] = Form.useForm()
|
||||
|
||||
const workersQuery = useQuery({
|
||||
queryKey: ['admin-worker-platform-workers', status],
|
||||
queryFn: () => fetchAdminWorkerUsers({ status }),
|
||||
queryKey: ['admin-worker-platform-workers', status, page, pageSize],
|
||||
queryFn: () => fetchAdminWorkerUsers({ status, page, pageSize }),
|
||||
})
|
||||
const workersPagination = workersQuery.data?.data.pagination
|
||||
|
||||
async function refreshWorkers() {
|
||||
await Promise.all([
|
||||
@@ -826,7 +925,7 @@ function WorkersPanel() {
|
||||
async function review(worker: WorkerUser, nextStatus: string) {
|
||||
try {
|
||||
await reviewAdminWorkerUser(worker.workerId, { status: nextStatus })
|
||||
message.success('接单员状态已更新')
|
||||
message.success('打手状态已更新')
|
||||
await refreshWorkers()
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '操作失败')
|
||||
@@ -851,7 +950,7 @@ function WorkersPanel() {
|
||||
|
||||
const columns: TableColumnsType<WorkerUser> = [
|
||||
{
|
||||
title: '接单员',
|
||||
title: '打手',
|
||||
minWidth: 220,
|
||||
render: (_, row) => (
|
||||
<div className="cell-stack">
|
||||
@@ -901,7 +1000,7 @@ function WorkersPanel() {
|
||||
|
||||
return (
|
||||
<Card
|
||||
title="接单员"
|
||||
title="打手"
|
||||
extra={
|
||||
<Space>
|
||||
<Select
|
||||
@@ -914,7 +1013,10 @@ function WorkersPanel() {
|
||||
{ value: 'rejected', label: '已拒绝' },
|
||||
{ value: 'disabled', label: '已停用' },
|
||||
]}
|
||||
onChange={setStatus}
|
||||
onChange={(nextStatus) => {
|
||||
setStatus(nextStatus)
|
||||
setPage(1)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
@@ -932,14 +1034,25 @@ function WorkersPanel() {
|
||||
loading={workersQuery.isLoading}
|
||||
dataSource={workersQuery.data?.data.items || []}
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
pagination={buildAdminTablePagination({
|
||||
current: workersPagination?.page || page,
|
||||
pageSize: workersPagination?.pageSize || pageSize,
|
||||
total: workersPagination?.total || 0,
|
||||
onChange: (nextPage, nextPageSize) => {
|
||||
setPage(nextPage)
|
||||
setPageSize(nextPageSize)
|
||||
},
|
||||
})}
|
||||
scroll={{ x: 1000 }}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="人工增加余额"
|
||||
open={Boolean(creditWorker)}
|
||||
onCancel={() => setCreditWorker(null)}
|
||||
onCancel={() => {
|
||||
setCreditWorker(null)
|
||||
creditForm.resetFields()
|
||||
}}
|
||||
onOk={() => creditForm.submit()}
|
||||
destroyOnHidden
|
||||
>
|
||||
@@ -1010,7 +1123,7 @@ function LevelsPanel() {
|
||||
layout="inline"
|
||||
initialValues={{
|
||||
levelKey: 'regular',
|
||||
name: '普通接单员',
|
||||
name: '普通打手',
|
||||
depositFreeAmount: 0,
|
||||
maxActiveOrders: 3,
|
||||
}}
|
||||
|
||||
@@ -3,51 +3,45 @@ import { App, Button, Card, Form, Input, Result, Space, Typography } from 'antd'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { lookupCollectOrder, submitCollectOrder } from '@/services/worker'
|
||||
import type { CollectField } from '@/types/worker-platform'
|
||||
|
||||
type LookupOrder = {
|
||||
workOrderNo: string
|
||||
platformOrderId: string
|
||||
productName: string
|
||||
status: string
|
||||
}
|
||||
import type { CollectLookupOrder } from '@/types/worker-platform'
|
||||
|
||||
export default function CollectPage() {
|
||||
const { message } = App.useApp()
|
||||
const [orderNo, setOrderNo] = useState('')
|
||||
const [order, setOrder] = useState<LookupOrder | null>(null)
|
||||
const [fields, setFields] = useState<CollectField[]>([])
|
||||
const [lookupOrders, setLookupOrders] = useState<CollectLookupOrder[]>([])
|
||||
const [lookupLoading, setLookupLoading] = useState(false)
|
||||
const [submitLoading, setSubmitLoading] = useState(false)
|
||||
const [submittingWorkOrderId, setSubmittingWorkOrderId] = useState(0)
|
||||
const [complete, setComplete] = useState(false)
|
||||
|
||||
async function submitLookup(values: { orderNo: string }) {
|
||||
setLookupLoading(true)
|
||||
setComplete(false)
|
||||
try {
|
||||
const response = await lookupCollectOrder(values.orderNo)
|
||||
setOrderNo(values.orderNo)
|
||||
setOrder(response.data.order)
|
||||
setFields(response.data.fields)
|
||||
const nextOrderNo = String(values.orderNo || '').trim()
|
||||
setOrderNo(nextOrderNo)
|
||||
await refreshLookupOrders(nextOrderNo)
|
||||
} catch (error) {
|
||||
setOrder(null)
|
||||
setFields([])
|
||||
setLookupOrders([])
|
||||
message.error(error instanceof Error ? error.message : '查询失败')
|
||||
} finally {
|
||||
setLookupLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function submitMaterial(values: Record<string, string>) {
|
||||
setSubmitLoading(true)
|
||||
async function submitMaterial(order: CollectLookupOrder, values: Record<string, string>) {
|
||||
setSubmittingWorkOrderId(order.workOrderId)
|
||||
try {
|
||||
const response = await submitCollectOrder({ orderNo, fields: values })
|
||||
setComplete(response.data.complete)
|
||||
message.success(response.data.complete ? '资料已完善' : '资料已提交')
|
||||
const response = await submitCollectOrder({
|
||||
orderNo,
|
||||
workOrderId: order.workOrderId,
|
||||
fields: values,
|
||||
})
|
||||
message.success(response.data.complete ? '当前商品资料已完善' : '当前商品资料已提交')
|
||||
await refreshLookupOrders(orderNo, { allowCompleteWhenNotFound: true })
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '提交失败')
|
||||
} finally {
|
||||
setSubmitLoading(false)
|
||||
setSubmittingWorkOrderId(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,13 +72,30 @@ export default function CollectPage() {
|
||||
<Result status="success" title="资料已提交完成" subTitle="订单已进入待分配状态,请等待处理。" />
|
||||
) : null}
|
||||
|
||||
{order && !complete ? (
|
||||
<Card className="platform-section-gap" title={order.productName} size="small">
|
||||
{!complete && lookupOrders.length > 1 ? (
|
||||
<Typography.Paragraph type="secondary" className="platform-section-gap">
|
||||
当前订单有 {lookupOrders.length} 个待完善商品,请逐个提交资料。
|
||||
</Typography.Paragraph>
|
||||
) : null}
|
||||
|
||||
{lookupOrders.map((currentOrder) => (
|
||||
<Card
|
||||
key={currentOrder.workOrderId}
|
||||
className="platform-section-gap"
|
||||
title={currentOrder.productName}
|
||||
extra={<Typography.Text type="secondary">{currentOrder.workOrderNo}</Typography.Text>}
|
||||
size="small"
|
||||
>
|
||||
<Typography.Paragraph type="secondary">
|
||||
订单号:{order.platformOrderId}
|
||||
订单号:{currentOrder.platformOrderId}
|
||||
</Typography.Paragraph>
|
||||
<Form layout="vertical" onFinish={submitMaterial}>
|
||||
{fields.map((field) => (
|
||||
<Form
|
||||
key={currentOrder.workOrderId}
|
||||
layout="vertical"
|
||||
initialValues={currentOrder.values || {}}
|
||||
onFinish={(values) => submitMaterial(currentOrder, values)}
|
||||
>
|
||||
{currentOrder.fields.map((field) => (
|
||||
<Form.Item
|
||||
key={field.key}
|
||||
label={field.label}
|
||||
@@ -94,13 +105,39 @@ export default function CollectPage() {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
))}
|
||||
<Button type="primary" htmlType="submit" loading={submitLoading}>
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
loading={submittingWorkOrderId === currentOrder.workOrderId}
|
||||
>
|
||||
提交资料
|
||||
</Button>
|
||||
</Form>
|
||||
</Card>
|
||||
) : null}
|
||||
))}
|
||||
</Card>
|
||||
</main>
|
||||
)
|
||||
|
||||
async function refreshLookupOrders(
|
||||
currentOrderNo: string,
|
||||
options: { allowCompleteWhenNotFound?: boolean } = {},
|
||||
) {
|
||||
try {
|
||||
const response = await lookupCollectOrder(currentOrderNo)
|
||||
setLookupOrders(response.data.orders || [])
|
||||
setComplete(false)
|
||||
} catch (error) {
|
||||
if (options.allowCompleteWhenNotFound && isCollectOrderNotFoundError(error)) {
|
||||
setLookupOrders([])
|
||||
setComplete(true)
|
||||
return
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isCollectOrderNotFoundError(error: unknown) {
|
||||
return String((error as { errorCode?: string })?.errorCode || '').trim() === 'collect_order_not_found'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ReloadOutlined, SearchOutlined } from '@ant-design/icons'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { App, Button, Card, Empty, Input, Space, Spin, Tag, Typography } from 'antd'
|
||||
import { App, Button, Card, Empty, Input, Pagination, Space, Spin, Tag, Typography } from 'antd'
|
||||
import { useState } from 'react'
|
||||
|
||||
import { fetchWorkerHallOrders, grabWorkerOrder } from '@/services/worker'
|
||||
@@ -9,16 +9,20 @@ import type { WorkOrder } from '@/types/worker-platform'
|
||||
export default function WorkerHallPage() {
|
||||
const { message } = App.useApp()
|
||||
const queryClient = useQueryClient()
|
||||
const [keywordInput, setKeywordInput] = useState('')
|
||||
const [keyword, setKeyword] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(20)
|
||||
const [grabbingId, setGrabbingId] = useState(0)
|
||||
|
||||
const ordersQuery = useQuery({
|
||||
queryKey: ['worker-hall-orders', keyword],
|
||||
queryFn: () => fetchWorkerHallOrders({ keyword }),
|
||||
queryKey: ['worker-hall-orders', keyword, page, pageSize],
|
||||
queryFn: () => fetchWorkerHallOrders({ keyword, page, pageSize }),
|
||||
retry: false,
|
||||
})
|
||||
|
||||
const orders = ordersQuery.data?.data.items || []
|
||||
const pagination = ordersQuery.data?.data.pagination
|
||||
|
||||
async function submitGrab(order: WorkOrder) {
|
||||
setGrabbingId(order.workOrderId)
|
||||
@@ -33,6 +37,11 @@ export default function WorkerHallPage() {
|
||||
}
|
||||
}
|
||||
|
||||
function applyKeywordSearch() {
|
||||
setKeyword(keywordInput.trim())
|
||||
setPage(1)
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="page-stack">
|
||||
<div className="worker-page-head">
|
||||
@@ -40,13 +49,31 @@ export default function WorkerHallPage() {
|
||||
<Space.Compact>
|
||||
<Input
|
||||
allowClear
|
||||
value={keyword}
|
||||
value={keywordInput}
|
||||
placeholder="搜索商品或订单号"
|
||||
prefix={<SearchOutlined />}
|
||||
onChange={(event) => setKeyword(event.target.value)}
|
||||
onPressEnter={() => void ordersQuery.refetch()}
|
||||
onChange={(event) => {
|
||||
const nextKeyword = event.target.value
|
||||
setKeywordInput(nextKeyword)
|
||||
if (!nextKeyword.trim()) {
|
||||
setKeyword('')
|
||||
setPage(1)
|
||||
}
|
||||
}}
|
||||
onPressEnter={applyKeywordSearch}
|
||||
/>
|
||||
<Button icon={<ReloadOutlined />} loading={ordersQuery.isFetching} onClick={() => ordersQuery.refetch()}>
|
||||
<Button
|
||||
icon={<SearchOutlined />}
|
||||
loading={ordersQuery.isFetching}
|
||||
onClick={applyKeywordSearch}
|
||||
>
|
||||
查询
|
||||
</Button>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
loading={ordersQuery.isFetching}
|
||||
onClick={() => ordersQuery.refetch()}
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
@@ -79,7 +106,7 @@ export default function WorkerHallPage() {
|
||||
{formatMoney(order.rewardAmount)}
|
||||
</Typography.Title>
|
||||
<Typography.Text type="secondary">
|
||||
所需冻结押金:{formatMoney(order.freezeDepositAmount ?? order.requiredDepositAmount)}
|
||||
所需冻结押金:{formatMoney(order.freezeDepositAmount)}
|
||||
</Typography.Text>
|
||||
<Button
|
||||
type="primary"
|
||||
@@ -93,6 +120,21 @@ export default function WorkerHallPage() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(pagination?.total || 0) > 0 ? (
|
||||
<Pagination
|
||||
current={pagination?.page || page}
|
||||
pageSize={pagination?.pageSize || pageSize}
|
||||
total={pagination?.total || 0}
|
||||
showSizeChanger
|
||||
pageSizeOptions={[10, 20, 50, 100]}
|
||||
showTotal={(total) => `共 ${total} 条`}
|
||||
onChange={(nextPage, nextPageSize) => {
|
||||
setPage(nextPage)
|
||||
setPageSize(nextPageSize)
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,21 +26,25 @@ const STATUS_OPTIONS = [
|
||||
{ value: 'pending_acceptance', label: '待验收' },
|
||||
{ value: 'problem', label: '问题单' },
|
||||
{ value: 'accepted', label: '已验收' },
|
||||
{ value: 'cancelled', label: '已取消' },
|
||||
]
|
||||
|
||||
export default function WorkerOrdersPage() {
|
||||
const { message } = App.useApp()
|
||||
const queryClient = useQueryClient()
|
||||
const [status, setStatus] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(20)
|
||||
const [submittingOrder, setSubmittingOrder] = useState<WorkOrder | null>(null)
|
||||
const [acceptanceFiles, setAcceptanceFiles] = useState<UploadedFile[]>([])
|
||||
const [form] = Form.useForm()
|
||||
|
||||
const ordersQuery = useQuery({
|
||||
queryKey: ['worker-my-orders', status],
|
||||
queryFn: () => fetchWorkerMyOrders({ status }),
|
||||
queryKey: ['worker-my-orders', status, page, pageSize],
|
||||
queryFn: () => fetchWorkerMyOrders({ status, page, pageSize }),
|
||||
retry: false,
|
||||
})
|
||||
const pagination = ordersQuery.data?.data.pagination
|
||||
|
||||
async function submitAcceptance(values: { note?: string }) {
|
||||
if (!submittingOrder) return
|
||||
@@ -121,6 +125,7 @@ export default function WorkerOrdersPage() {
|
||||
onClick={() => {
|
||||
setSubmittingOrder(row)
|
||||
setAcceptanceFiles([])
|
||||
form.resetFields()
|
||||
}}
|
||||
>
|
||||
提交验收
|
||||
@@ -138,7 +143,10 @@ export default function WorkerOrdersPage() {
|
||||
value={status}
|
||||
options={STATUS_OPTIONS}
|
||||
style={{ width: 140 }}
|
||||
onChange={setStatus}
|
||||
onChange={(nextStatus) => {
|
||||
setStatus(nextStatus)
|
||||
setPage(1)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
@@ -155,7 +163,18 @@ export default function WorkerOrdersPage() {
|
||||
loading={ordersQuery.isLoading}
|
||||
dataSource={ordersQuery.data?.data.items || []}
|
||||
columns={columns}
|
||||
pagination={false}
|
||||
pagination={{
|
||||
current: pagination?.page || page,
|
||||
pageSize: pagination?.pageSize || pageSize,
|
||||
total: pagination?.total || 0,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
onChange: (nextPage, nextPageSize) => {
|
||||
setPage(nextPage)
|
||||
setPageSize(nextPageSize)
|
||||
},
|
||||
}}
|
||||
scroll={{ x: 900 }}
|
||||
/>
|
||||
|
||||
@@ -165,6 +184,7 @@ export default function WorkerOrdersPage() {
|
||||
onCancel={() => {
|
||||
setSubmittingOrder(null)
|
||||
setAcceptanceFiles([])
|
||||
form.resetFields()
|
||||
}}
|
||||
onOk={() => form.submit()}
|
||||
destroyOnHidden
|
||||
@@ -210,12 +230,14 @@ function formatStatus(status: string) {
|
||||
pending_acceptance: '待验收',
|
||||
problem: '问题单',
|
||||
accepted: '已验收',
|
||||
cancelled: '已取消',
|
||||
}
|
||||
return labels[status] || status
|
||||
}
|
||||
|
||||
function resolveStatusColor(status: string) {
|
||||
if (status === 'accepted') return 'green'
|
||||
if (status === 'cancelled') return 'default'
|
||||
if (status === 'problem') return 'red'
|
||||
if (status === 'pending_acceptance') return 'gold'
|
||||
return 'blue'
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function WorkerProfilePage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{!worker && !profileQuery.isLoading ? <Empty description="暂无接单员信息" /> : null}
|
||||
{!worker && !profileQuery.isLoading ? <Empty description="暂无打手信息" /> : null}
|
||||
|
||||
{worker ? (
|
||||
<>
|
||||
|
||||
@@ -164,6 +164,19 @@ export function markAdminWorkOrderProblem(workOrderId: number, note: string) {
|
||||
)
|
||||
}
|
||||
|
||||
export function resolveAdminProblemWorkOrder(
|
||||
workOrderId: number,
|
||||
payload: {
|
||||
action: 'return_to_worker' | 'reopen' | 'cancel_release' | 'cancel_deduct'
|
||||
note: string
|
||||
},
|
||||
) {
|
||||
return apiPost<{ order: WorkOrder }>(
|
||||
`/api/v1/admin/worker-platform/orders/${workOrderId}/problem/resolve`,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
export function acceptAdminWorkOrder(workOrderId: number) {
|
||||
return apiPost<{ order: WorkOrder }>(
|
||||
`/api/v1/admin/worker-platform/orders/${workOrderId}/accept`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { apiGet, apiPost } from '@/lib/http'
|
||||
import type {
|
||||
CollectField,
|
||||
CollectLookupResponse,
|
||||
UploadedFile,
|
||||
WorkOrder,
|
||||
WorkerListResponse,
|
||||
@@ -70,17 +70,12 @@ export function submitWorkerAcceptance(
|
||||
}
|
||||
|
||||
export function lookupCollectOrder(orderNo: string) {
|
||||
return apiPost<{
|
||||
order: Pick<
|
||||
WorkOrder,
|
||||
'workOrderNo' | 'platformOrderId' | 'productName' | 'status'
|
||||
>
|
||||
fields: CollectField[]
|
||||
}>('/api/v1/collect/lookup', { orderNo })
|
||||
return apiPost<CollectLookupResponse>('/api/v1/collect/lookup', { orderNo })
|
||||
}
|
||||
|
||||
export function submitCollectOrder(payload: {
|
||||
orderNo: string
|
||||
workOrderId?: number
|
||||
fields: Record<string, string>
|
||||
}) {
|
||||
return apiPost<{ complete: boolean; order: Record<string, unknown> }>(
|
||||
|
||||
@@ -98,8 +98,8 @@ export type WorkOrder = {
|
||||
categoryName: string
|
||||
status: WorkOrderStatus
|
||||
rewardAmount: number
|
||||
requiredDepositAmount: number
|
||||
depositThresholdAmount: number
|
||||
requiredDepositAmount?: number
|
||||
depositThresholdAmount?: number
|
||||
freezeDepositAmount?: number
|
||||
material: Record<string, unknown>
|
||||
requirement: Record<string, unknown>
|
||||
@@ -146,3 +146,23 @@ export type CollectField = {
|
||||
label: string
|
||||
required: boolean
|
||||
}
|
||||
|
||||
export type CollectLookupOrder = {
|
||||
workOrderId: number
|
||||
workOrderNo: string
|
||||
platformOrderId: string
|
||||
productName: string
|
||||
status: string
|
||||
fields: CollectField[]
|
||||
values?: Record<string, string>
|
||||
}
|
||||
|
||||
export type CollectLookupResponse = {
|
||||
order: Pick<
|
||||
WorkOrder,
|
||||
'workOrderId' | 'workOrderNo' | 'platformOrderId' | 'productName' | 'status'
|
||||
>
|
||||
fields: CollectField[]
|
||||
orders: CollectLookupOrder[]
|
||||
pendingCount: number
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user