接单工单支持任务时限,超时自动判定失败并处置

This commit is contained in:
yml2213
2026-08-01 19:02:57 +08:00
parent 66193c5d59
commit 23ad60a1ef
18 changed files with 681 additions and 61 deletions
@@ -113,6 +113,8 @@ export default function ProductRulesPanel() {
sharingTotalQuantity?: number
sharingUnitReward?: number
sharingTotalAmount?: number
timeoutMinutes?: number
timeoutPolicy?: string
fieldsText?: string
enabled?: boolean
autoCreate?: boolean
@@ -389,6 +391,27 @@ export default function ProductRulesPanel() {
>
<Input.TextArea rows={4} />
</Form.Item>
<Form.Item
label="默认任务时限"
name="timeoutMinutes"
tooltip="自动创建的工单继承该时限,打手抢单后开始计时;0 表示不限时"
>
<InputNumber min={0} step={5} addonAfter="分钟" />
</Form.Item>
<Form.Item
label="默认超时策略"
name="timeoutPolicy"
tooltip="reopen:释放押金退回大厅;cancel_release:取消退押金;cancel_deduct:取消扣押金"
>
<Select
style={{ width: 260 }}
options={[
{ value: 'reopen', label: '释放押金退回大厅' },
{ value: 'cancel_release', label: '取消订单并退还押金' },
{ value: 'cancel_deduct', label: '取消订单并扣除押金' },
]}
/>
</Form.Item>
<Button type="primary" htmlType="submit">
</Button>
@@ -87,6 +87,8 @@ import {
} from '@/utils/admin-pagination'
import { formatAdminDateTime } from '@/utils/admin-time'
import {
DeadlineCountdown,
formatTimeoutPolicyLabel,
getAcceptanceFiles,
getAcceptanceImageUrls,
getAcceptanceSubmittedAt,
@@ -129,6 +131,8 @@ export default function WorkOrdersPanel() {
rewardAmount?: number
requiredDepositAmount?: number
fieldsText?: string
timeoutMinutes?: number
timeoutPolicy?: string
}>()
const [sharingForm] = Form.useForm<{
enabled?: boolean
@@ -320,6 +324,8 @@ export default function WorkOrdersPanel() {
requiredDepositAmount:
Math.round(Number(row.requiredDepositAmount || 0)) / 100,
fieldsText: formatRequirementFieldsText(getRequirementFields(row)),
timeoutMinutes: Number(row.timeoutMinutes || 0),
timeoutPolicy: row.timeoutPolicy || 'reopen',
})
}
@@ -330,6 +336,8 @@ export default function WorkOrdersPanel() {
rewardAmount?: number
requiredDepositAmount?: number
fieldsText?: string
timeoutMinutes?: number
timeoutPolicy?: string
}) {
if (!editOrder) return
const succeeded = await runAction(
@@ -341,6 +349,8 @@ export default function WorkOrdersPanel() {
rewardAmount: Number(values.rewardAmount || 0),
requiredDepositAmount: Number(values.requiredDepositAmount || 0),
fieldsText: String(values.fieldsText || ''),
timeoutMinutes: Math.max(0, Number(values.timeoutMinutes || 0)),
timeoutPolicy: values.timeoutPolicy || 'reopen',
}),
'工单信息已保存',
)
@@ -398,6 +408,34 @@ export default function WorkOrdersPanel() {
width: 110,
render: (_, row) => formatMoney(row.requiredDepositAmount),
},
{
title: '时限',
width: 190,
render: (_, row) => {
const minutes = Number(row.timeoutMinutes || 0)
if (minutes <= 0) {
return <Typography.Text type="secondary"></Typography.Text>
}
return (
<div className="cell-stack">
<Space wrap size={6}>
<Tag color="blue">{minutes} </Tag>
<Typography.Text type="secondary">
{formatTimeoutPolicyLabel(row.timeoutPolicy)}
</Typography.Text>
</Space>
{row.status === 'in_progress' && row.deadlineAt ? (
<DeadlineCountdown deadlineAt={row.deadlineAt} showExpired />
) : null}
{row.deadlineAt ? (
<Typography.Text type="secondary">
{formatAdminDateTime(row.deadlineAt)}
</Typography.Text>
) : null}
</div>
)
},
},
{
title: '拼单',
width: 190,
@@ -699,6 +737,29 @@ export default function WorkOrdersPanel() {
<Descriptions.Item label="押金">
{formatMoney(detailOrder.requiredDepositAmount)}
</Descriptions.Item>
<Descriptions.Item label="任务时限">
{Number(detailOrder.timeoutMinutes || 0) > 0
? `${detailOrder.timeoutMinutes} 分钟`
: '不限时'}
</Descriptions.Item>
<Descriptions.Item label="超时策略">
{formatTimeoutPolicyLabel(detailOrder.timeoutPolicy)}
</Descriptions.Item>
<Descriptions.Item label="超时截止">
{detailOrder.deadlineAt ? (
<Space wrap size={8}>
<DeadlineCountdown
deadlineAt={detailOrder.deadlineAt}
showExpired
/>
<Typography.Text type="secondary">
{formatAdminDateTime(detailOrder.deadlineAt)}
</Typography.Text>
</Space>
) : (
'-'
)}
</Descriptions.Item>
<Descriptions.Item label="发布时间">
{formatAdminDateTime(detailOrder.publishedAt)}
</Descriptions.Item>
@@ -857,6 +918,32 @@ export default function WorkOrdersPanel() {
>
<Input.TextArea rows={5} />
</Form.Item>
<Form.Item
label="任务时限"
name="timeoutMinutes"
tooltip="打手抢单后开始计时,超时由系统自动按策略处置;0 表示不限时"
>
<InputNumber
min={0}
step={5}
addonAfter="分钟"
style={{ width: 160 }}
/>
</Form.Item>
<Form.Item
label="超时策略"
name="timeoutPolicy"
tooltip="reopen:释放押金退回大厅继续可抢;cancel_release:取消订单并退还押金;cancel_deduct:取消订单并扣除押金"
>
<Select
style={{ width: 320 }}
options={[
{ value: 'reopen', label: '释放押金退回大厅' },
{ value: 'cancel_release', label: '取消订单并退还押金' },
{ value: 'cancel_deduct', label: '取消订单并扣除押金' },
]}
/>
</Form.Item>
</Form>
) : null}
</Modal>
@@ -1,8 +1,14 @@
import { Descriptions, Space, Tag, Typography } from 'antd'
import ImagePreviewList from '@/components/files/ImagePreviewList'
import {
DeadlineCountdown,
formatTimeoutPolicyLabel,
} from '@/components/DeadlineCountdown'
import type { CollectField, UploadedFile, WorkOrder, WorkOrderShare } from '@/types/worker-platform'
import { formatAdminDateTime } from '@/utils/admin-time'
export { DeadlineCountdown, formatTimeoutPolicyLabel }
export function getRequirementFields(order: WorkOrder | null): CollectField[] {
if (!order) return []
const rawFields = Array.isArray(order.requirement?.fields)