已取消工单在列表与详情展示取消原因来源操作人与时间
This commit is contained in:
@@ -382,6 +382,12 @@ export type WorkOrderRow = {
|
|||||||
category_name?: string
|
category_name?: string
|
||||||
worker_username?: string
|
worker_username?: string
|
||||||
worker_display_name?: string
|
worker_display_name?: string
|
||||||
|
/** 取消信息,由工单查询从取消类事件推导,仅已取消工单有值。 */
|
||||||
|
cancel_reason?: string | null
|
||||||
|
cancel_event_type?: string | null
|
||||||
|
cancel_actor_type?: string | null
|
||||||
|
cancel_actor_id?: string | null
|
||||||
|
cancelled_at?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProblemWorkOrderResolutionAction =
|
export type ProblemWorkOrderResolutionAction =
|
||||||
|
|||||||
@@ -12,10 +12,43 @@ export const WORK_ORDER_SELECT = `
|
|||||||
COALESCE(share_summary.pending_share_count, 0)::int AS pending_share_count,
|
COALESCE(share_summary.pending_share_count, 0)::int AS pending_share_count,
|
||||||
wc.name AS category_name,
|
wc.name AS category_name,
|
||||||
wu.username AS worker_username,
|
wu.username AS worker_username,
|
||||||
wu.display_name AS worker_display_name
|
wu.display_name AS worker_display_name,
|
||||||
|
cancel_info.cancel_reason,
|
||||||
|
cancel_info.cancel_event_type,
|
||||||
|
cancel_info.cancel_actor_type,
|
||||||
|
cancel_info.cancel_actor_id,
|
||||||
|
cancel_info.cancelled_at
|
||||||
FROM work_orders wo
|
FROM work_orders wo
|
||||||
LEFT JOIN work_categories wc ON wc.id = wo.category_id
|
LEFT JOIN work_categories wc ON wc.id = wo.category_id
|
||||||
LEFT JOIN worker_users wu ON wu.id = wo.assigned_worker_id
|
LEFT JOIN worker_users wu ON wu.id = wo.assigned_worker_id
|
||||||
|
LEFT JOIN LATERAL (
|
||||||
|
SELECT
|
||||||
|
ev.event_type AS cancel_event_type,
|
||||||
|
ev.actor_type AS cancel_actor_type,
|
||||||
|
ev.actor_id AS cancel_actor_id,
|
||||||
|
ev.created_at AS cancelled_at,
|
||||||
|
BTRIM(COALESCE(
|
||||||
|
NULLIF(BTRIM(COALESCE(ev.payload_json->>'reason', '')), ''),
|
||||||
|
NULLIF(BTRIM(COALESCE(ev.payload_json->>'note', '')), ''),
|
||||||
|
CASE ev.event_type
|
||||||
|
WHEN 'timeout_cancel_release' THEN '任务超时未完成,系统自动取消并退还押金'
|
||||||
|
WHEN 'timeout_cancel_deduct' THEN '任务超时未完成,系统自动取消并扣除押金'
|
||||||
|
ELSE ''
|
||||||
|
END
|
||||||
|
)) AS cancel_reason
|
||||||
|
FROM work_order_events ev
|
||||||
|
WHERE ev.work_order_id = wo.id
|
||||||
|
AND wo.status = 'cancelled'
|
||||||
|
AND ev.event_type IN (
|
||||||
|
'cancelled_by_admin',
|
||||||
|
'problem_cancel_release',
|
||||||
|
'problem_cancel_deduct',
|
||||||
|
'timeout_cancel_release',
|
||||||
|
'timeout_cancel_deduct'
|
||||||
|
)
|
||||||
|
ORDER BY ev.id DESC
|
||||||
|
LIMIT 1
|
||||||
|
) cancel_info ON TRUE
|
||||||
LEFT JOIN LATERAL (
|
LEFT JOIN LATERAL (
|
||||||
SELECT
|
SELECT
|
||||||
SUM(wos.quantity) FILTER (WHERE wos.status != 'cancelled') AS active_share_quantity,
|
SUM(wos.quantity) FILTER (WHERE wos.status != 'cancelled') AS active_share_quantity,
|
||||||
|
|||||||
@@ -114,6 +114,15 @@ export function mapWorkOrderSharingWorkers(shares: WorkOrderShareRow[]) {
|
|||||||
return [...workers.values()]
|
return [...workers.values()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 取消来源:由触发取消的事件类型推导,用于列表与详情展示。 */
|
||||||
|
function resolveWorkOrderCancelSource(cancelEventType: string | null | undefined) {
|
||||||
|
const eventType = String(cancelEventType || '').trim()
|
||||||
|
if (eventType === 'cancelled_by_admin') return '后台撤单'
|
||||||
|
if (eventType.startsWith('problem_')) return '问题单处置'
|
||||||
|
if (eventType.startsWith('timeout_')) return '超时自动取消'
|
||||||
|
return eventType || '未知来源'
|
||||||
|
}
|
||||||
|
|
||||||
export function mapWorkOrderAdmin(workOrder: WorkOrderRow, shares?: WorkOrderShareRow[]) {
|
export function mapWorkOrderAdmin(workOrder: WorkOrderRow, shares?: WorkOrderShareRow[]) {
|
||||||
const activeShareQuantity = Math.max(0, Number(workOrder.active_share_quantity || 0))
|
const activeShareQuantity = Math.max(0, Number(workOrder.active_share_quantity || 0))
|
||||||
const pendingShareCount = Math.max(0, Number(workOrder.pending_share_count || 0))
|
const pendingShareCount = Math.max(0, Number(workOrder.pending_share_count || 0))
|
||||||
@@ -232,6 +241,16 @@ export function mapWorkOrderAdmin(workOrder: WorkOrderRow, shares?: WorkOrderSha
|
|||||||
acceptance: mapAcceptanceForResponse(workOrder.acceptance_json),
|
acceptance: mapAcceptanceForResponse(workOrder.acceptance_json),
|
||||||
draftAcceptance: mapAcceptanceForResponse(workOrder.draft_acceptance_json),
|
draftAcceptance: mapAcceptanceForResponse(workOrder.draft_acceptance_json),
|
||||||
problemNote: workOrder.problem_note || '',
|
problemNote: workOrder.problem_note || '',
|
||||||
|
cancel:
|
||||||
|
workOrder.status === WORK_ORDER_STATUS.CANCELLED
|
||||||
|
? {
|
||||||
|
reason: String(workOrder.cancel_reason || ''),
|
||||||
|
source: resolveWorkOrderCancelSource(workOrder.cancel_event_type),
|
||||||
|
actorType: String(workOrder.cancel_actor_type || ''),
|
||||||
|
actorName: String(workOrder.cancel_actor_id || ''),
|
||||||
|
cancelledAt: workOrder.cancelled_at || null,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
worker: workOrder.assigned_worker_id
|
worker: workOrder.assigned_worker_id
|
||||||
? {
|
? {
|
||||||
workerId: Number(workOrder.assigned_worker_id),
|
workerId: Number(workOrder.assigned_worker_id),
|
||||||
|
|||||||
@@ -896,21 +896,34 @@ export default function WorkOrdersPanel() {
|
|||||||
title: '状态',
|
title: '状态',
|
||||||
width: 150,
|
width: 150,
|
||||||
render: (_, row) => {
|
render: (_, row) => {
|
||||||
|
const cancelText =
|
||||||
|
row.cancel?.reason || (row.cancel ? `${row.cancel.source}(未记录原因)` : '')
|
||||||
return (
|
return (
|
||||||
<Space wrap size={4}>
|
<div className="cell-stack">
|
||||||
<Tag color={resolveStatusColor(row.status)}>{formatStatus(row.status)}</Tag>
|
<Space wrap size={4}>
|
||||||
{row.rawStatus === 'open' && row.pinnedAt ? (
|
<Tag color={resolveStatusColor(row.status)}>{formatStatus(row.status)}</Tag>
|
||||||
<Tag color="gold" icon={<PushpinOutlined />}>
|
{row.rawStatus === 'open' && row.pinnedAt ? (
|
||||||
置顶
|
<Tag color="gold" icon={<PushpinOutlined />}>
|
||||||
</Tag>
|
置顶
|
||||||
|
</Tag>
|
||||||
|
) : null}
|
||||||
|
{row.acceptanceMode === 'friend_gift' ? <Tag color="purple">好友赠送</Tag> : null}
|
||||||
|
{row.acceptanceMode === 'friend_gift' && row.gift ? (
|
||||||
|
<Tag color={resolveGiftPhaseColor(row.gift.phase)}>
|
||||||
|
{formatGiftPhase(row.gift.phase)}
|
||||||
|
</Tag>
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
|
{row.cancel ? (
|
||||||
|
<Typography.Text
|
||||||
|
type="secondary"
|
||||||
|
style={{ fontSize: 12, maxWidth: 140 }}
|
||||||
|
ellipsis={{ tooltip: `${row.cancel.source}:${cancelText}` }}
|
||||||
|
>
|
||||||
|
{cancelText}
|
||||||
|
</Typography.Text>
|
||||||
) : null}
|
) : null}
|
||||||
{row.acceptanceMode === 'friend_gift' ? <Tag color="purple">好友赠送</Tag> : null}
|
</div>
|
||||||
{row.acceptanceMode === 'friend_gift' && row.gift ? (
|
|
||||||
<Tag color={resolveGiftPhaseColor(row.gift.phase)}>
|
|
||||||
{formatGiftPhase(row.gift.phase)}
|
|
||||||
</Tag>
|
|
||||||
) : null}
|
|
||||||
</Space>
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1418,6 +1431,22 @@ export default function WorkOrdersPanel() {
|
|||||||
{String(detailOrder.problemNote || '').trim() || '-'}
|
{String(detailOrder.problemNote || '').trim() || '-'}
|
||||||
</span>
|
</span>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
|
{detailOrder.cancel ? (
|
||||||
|
<Descriptions.Item label="取消信息" span={2}>
|
||||||
|
<span className="worker-order-detail-value">
|
||||||
|
{detailOrder.cancel.source}
|
||||||
|
{detailOrder.cancel.cancelledAt
|
||||||
|
? ` · ${formatAdminDateTime(detailOrder.cancel.cancelledAt)}`
|
||||||
|
: ''}
|
||||||
|
{detailOrder.cancel.actorType === 'admin' && detailOrder.cancel.actorName
|
||||||
|
? ` · 操作人 ${detailOrder.cancel.actorName}`
|
||||||
|
: ''}
|
||||||
|
{detailOrder.cancel.reason
|
||||||
|
? ` · 原因:${detailOrder.cancel.reason}`
|
||||||
|
: ' · 原因未记录'}
|
||||||
|
</span>
|
||||||
|
</Descriptions.Item>
|
||||||
|
) : null}
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -548,6 +548,14 @@ export type WorkOrder = {
|
|||||||
/** 打手本人尚未点击查看或操作过该工单。 */
|
/** 打手本人尚未点击查看或操作过该工单。 */
|
||||||
isUnread?: boolean
|
isUnread?: boolean
|
||||||
problemNote: string
|
problemNote: string
|
||||||
|
/** 取消信息,仅已取消工单返回。 */
|
||||||
|
cancel?: {
|
||||||
|
reason: string
|
||||||
|
source: string
|
||||||
|
actorType: string
|
||||||
|
actorName: string
|
||||||
|
cancelledAt: string | null
|
||||||
|
} | null
|
||||||
worker: null | {
|
worker: null | {
|
||||||
workerId: number
|
workerId: number
|
||||||
username: string
|
username: string
|
||||||
|
|||||||
Reference in New Issue
Block a user