diff --git a/apps/backend/src/services/worker-platform/worker-platform-service.ts b/apps/backend/src/services/worker-platform/worker-platform-service.ts index b7a20123..4cbb5197 100644 --- a/apps/backend/src/services/worker-platform/worker-platform-service.ts +++ b/apps/backend/src/services/worker-platform/worker-platform-service.ts @@ -1301,6 +1301,8 @@ function mapWorkerUser(worker: WorkerUserRow) { function mapWorkOrderAdmin(workOrder: WorkOrderRow) { return { workOrderId: Number(workOrder.id), + orderId: workOrder.order_id ? Number(workOrder.order_id) : null, + orderItemId: workOrder.order_item_id ? Number(workOrder.order_item_id) : null, workOrderNo: workOrder.work_order_no, platformOrderId: workOrder.platform_order_id, productName: workOrder.product_name, @@ -1336,7 +1338,14 @@ function mapWorkOrderForWorker( permissions: { depositFreeAmount: number }, ) { const mapped = mapWorkOrderAdmin(workOrder) - const { requiredDepositAmount, depositThresholdAmount, worker: _worker, ...visibleOrder } = mapped + const { + requiredDepositAmount, + depositThresholdAmount, + worker: _worker, + orderId: _orderId, + orderItemId: _orderItemId, + ...visibleOrder + } = mapped return { ...visibleOrder, freezeDepositAmount: resolveFreezeDepositAmount(workOrder, permissions), diff --git a/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx b/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx index 02d45c30..52307a03 100644 --- a/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx +++ b/apps/frontend/src/pages/admin/AdminWorkerPlatformPage.tsx @@ -12,6 +12,8 @@ import { App, Button, Card, + Descriptions, + Drawer, Form, Input, InputNumber, @@ -27,7 +29,9 @@ import { } from 'antd' import type { TableColumnsType } from 'antd' import { useState } from 'react' +import { useNavigate } from 'react-router' +import JsonPreview from '@/components/admin/JsonPreview' import ImagePreviewList from '@/components/files/ImagePreviewList' import PageHeader from '@/components/admin/PageHeader' import { @@ -63,6 +67,7 @@ import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination, } from '@/utils/admin-pagination' +import { formatAdminDateTime } from '@/utils/admin-time' export default function AdminWorkerPlatformPage() { return ( @@ -119,10 +124,12 @@ function SummaryCards() { function WorkOrdersPanel() { const { message } = App.useApp() + const navigate = useNavigate() const queryClient = useQueryClient() const [status, setStatus] = useState('') const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE) + const [detailOrder, setDetailOrder] = useState(null) const [problemOrder, setProblemOrder] = useState(null) const [resolutionOrder, setResolutionOrder] = useState(null) const [materialOrder, setMaterialOrder] = useState(null) @@ -227,13 +234,27 @@ function WorkOrdersPanel() { materialForm.setFieldsValue({ fields: getMaterialFields(row) }) } + function openDetailDrawer(row: WorkOrder) { + setDetailOrder(row) + } + const columns: TableColumnsType = [ { title: '订单', minWidth: 300, render: (_, row) => (
- {row.productName} + openDetailDrawer(row)}> + {row.productName || row.workOrderNo} + + + {row.categoryName ? {row.categoryName} : null} + {row.orderId ? ( + + 源订单 #{row.orderId} + + ) : null} + {row.platformOrderId} · {row.workOrderNo} @@ -283,9 +304,10 @@ function WorkOrdersPanel() { }, { title: '操作', - width: 340, + width: 400, render: (_, row) => ( + {row.status === 'pending_material' ? ( + ) : null} + + ) : null + } + > + {detailOrder ? ( +
+ + + + {detailOrder.workOrderNo} + + + {detailOrder.platformOrderId || '-'} + + + {detailOrder.orderId || '-'} + + + {detailOrder.orderItemId || '-'} + + + {detailOrder.productName || '-'} + + + {detailOrder.categoryName || '-'} + + + + {formatStatus(detailOrder.status)} + + + + {detailOrder.worker?.displayName || + detailOrder.worker?.username || + '-'} + + + {formatMoney(detailOrder.rewardAmount)} + + + {formatMoney(detailOrder.requiredDepositAmount)} + + + {formatAdminDateTime(detailOrder.publishedAt)} + + + {formatAdminDateTime(detailOrder.assignedAt)} + + + {formatAdminDateTime(getAcceptanceSubmittedAt(detailOrder))} + + + {formatAdminDateTime(detailOrder.acceptedAt)} + + + {formatAdminDateTime(detailOrder.createdAt)} + + + {formatAdminDateTime(detailOrder.updatedAt)} + + + + {String(detailOrder.problemNote || '').trim() || '-'} + + + + + + + {renderDetailFieldValues( + getMaterialDetailItems(detailOrder), + '当前还没有填写资料', + )} + + + + {renderRequirementFieldValues(detailOrder)} + + + +
+ + + {formatAdminDateTime(getAcceptanceSubmittedAt(detailOrder))} + + + {getAcceptanceFiles(detailOrder).length + + getAcceptanceImageUrls(detailOrder).length || '-'} + + + + {String(detailOrder.acceptance?.note || '').trim() || '-'} + + + + {getAcceptanceFiles(detailOrder).length || + getAcceptanceImageUrls(detailOrder).length ? ( + + ) : ( + + 暂无验收图 + + )} +
+
+ + + , + }, + { + key: 'requirement', + label: '需求', + children: , + }, + { + key: 'acceptance', + label: '验收', + children: , + }, + ]} + /> + +
+ ) : null} + + Boolean(item)) } +type DetailFieldItem = { + key: string + label: string + required: boolean + value: string +} + function getMaterialFields(order: WorkOrder): Record { const material = asRecord(order.material) const collect = asRecord(material.collect) @@ -1197,7 +1389,88 @@ function getMaterialFields(order: WorkOrder): Record { ) } +function getMaterialDetailItems(order: WorkOrder): DetailFieldItem[] { + const values = getMaterialFields(order) + const requiredFields = getRequirementFields(order) + const usedKeys = new Set() + const items = requiredFields.map((field) => { + usedKeys.add(field.key) + return { + key: field.key, + label: field.label, + required: field.required, + value: String(values[field.key] || ''), + } + }) + + Object.entries(values).forEach(([key, value]) => { + if (usedKeys.has(key)) return + items.push({ + key, + label: key, + required: false, + value: String(value || ''), + }) + }) + + return items +} + +function renderDetailFieldValues( + items: DetailFieldItem[], + emptyText: string, +) { + if (items.length === 0) { + return {emptyText} + } + + return ( + + {items.map((item) => ( + + {item.label} + {item.required ? 必填 : null} + + } + > + + {String(item.value || '').trim() || '-'} + + + ))} + + ) +} + +function renderRequirementFieldValues(order: WorkOrder) { + const fields = getRequirementFields(order) + if (fields.length === 0) { + return 当前工单未配置资料要求 + } + + return ( + + {fields.map((field) => ( + + + {field.key} + + {field.required ? '必填' : '选填'} + + + + ))} + + ) +} + function renderMaterialSummary(order: WorkOrder) { + const fieldLabelMap = new Map( + getRequirementFields(order).map((field) => [field.key, field.label]), + ) const fields = getMaterialFields(order) const entries = Object.entries(fields).filter(([, value]) => String(value || '').trim(), @@ -1210,7 +1483,7 @@ function renderMaterialSummary(order: WorkOrder) {
{entries.slice(0, 2).map(([key, value]) => ( - {key}: {value} + {fieldLabelMap.get(key) || key}: {value} ))} {entries.length > 2 ? ( @@ -1234,6 +1507,14 @@ function getAcceptanceImageUrls(row: WorkOrder): string[] { : [] } +function getAcceptanceSubmittedAt(order: WorkOrder): string | null { + const acceptanceSubmittedAt = + typeof order.acceptance?.submittedAt === 'string' + ? order.acceptance.submittedAt + : null + return order.submittedAt || acceptanceSubmittedAt || null +} + function asRecord(value: unknown): Record { return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record) diff --git a/apps/frontend/src/styles/main.css b/apps/frontend/src/styles/main.css index 3ecfeefe..22a11d62 100644 --- a/apps/frontend/src/styles/main.css +++ b/apps/frontend/src/styles/main.css @@ -1162,6 +1162,21 @@ select { min-width: 0; } +.worker-order-detail-stack { + display: grid; + gap: 16px; +} + +.worker-order-detail-value { + white-space: pre-wrap; + overflow-wrap: anywhere; + word-break: break-word; +} + +.worker-order-detail-json-tabs > .ant-tabs-nav { + margin-bottom: 12px; +} + .metric-grid { display: grid; gap: 12px; diff --git a/apps/frontend/src/types/worker-platform.ts b/apps/frontend/src/types/worker-platform.ts index e0c924e4..27a5dd3d 100644 --- a/apps/frontend/src/types/worker-platform.ts +++ b/apps/frontend/src/types/worker-platform.ts @@ -92,6 +92,8 @@ export type WorkOrderStatus = export type WorkOrder = { workOrderId: number + orderId?: number | null + orderItemId?: number | null workOrderNo: string platformOrderId: string productName: string