From 1d9e102527af7d67d459998b58dbd0087a2ef431 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 17 Aug 2026 12:43:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=85=E8=A1=A5=E5=9B=BE?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/repositories/worker-platform/types.ts | 2 ++ .../worker-platform/work-order-repo.ts | 18 +++++++++++++++++- .../services/worker-platform/admin-service.ts | 5 ++++- .../services/worker-platform/worker-service.ts | 3 +++ .../src/pages/admin/panels/WorkOrdersPanel.tsx | 11 ++++++++++- .../frontend/src/pages/admin/panels/shared.tsx | 2 ++ .../src/pages/worker/WorkerOrdersPage.tsx | 18 ++++++++++++------ 7 files changed, 50 insertions(+), 9 deletions(-) diff --git a/apps/backend/src/repositories/worker-platform/types.ts b/apps/backend/src/repositories/worker-platform/types.ts index 06761b3d..0a286f30 100644 --- a/apps/backend/src/repositories/worker-platform/types.ts +++ b/apps/backend/src/repositories/worker-platform/types.ts @@ -226,6 +226,8 @@ export type ListInput = { categoryId?: number workerSharingId?: number visibleAfterIso?: string + /** VIP 免审核后仍在补图期限内的订单;与状态筛选取并集 */ + vipEvidencePending?: boolean /** 置顶订单优先展示(抢单大厅使用) */ pinnedFirst?: boolean sort?: 'id_desc' | 'worker_claimed_at_desc' diff --git a/apps/backend/src/repositories/worker-platform/work-order-repo.ts b/apps/backend/src/repositories/worker-platform/work-order-repo.ts index 25ee91ce..bc129abf 100644 --- a/apps/backend/src/repositories/worker-platform/work-order-repo.ts +++ b/apps/backend/src/repositories/worker-platform/work-order-repo.ts @@ -764,6 +764,7 @@ export async function listWorkOrders({ categoryId = 0, workerSharingId = 0, visibleAfterIso = '', + vipEvidencePending = false, pinnedFirst = false, sort = 'id_desc', }: ListInput = {}): Promise<{ items: WorkOrderRow[]; total: number }> { @@ -775,6 +776,7 @@ export async function listWorkOrders({ categoryId, workerSharingId, visibleAfterIso, + vipEvidencePending, }) const totalResult = await query<{ total: number }>( ` @@ -2585,13 +2587,27 @@ function buildWorkOrderWhere({ categoryId = 0, workerSharingId = 0, visibleAfterIso = '', + vipEvidencePending = false, }: ListInput) { const filters: string[] = [] const params: unknown[] = [] const normalizedStatuses = [ ...new Set(statuses.map((item) => String(item || '').trim()).filter(Boolean)), ] - if (normalizedStatuses.length > 0) { + if (vipEvidencePending) { + const vipEvidencePendingWhere = ` + wo.status = 'accepted' + AND wo.acceptance_json->>'reviewMode' = 'vip_auto' + AND wo.acceptance_json->>'evidenceStatus' = 'pending' + AND NULLIF(wo.acceptance_json->>'evidenceDueAt', '')::timestamptz > NOW() + ` + if (normalizedStatuses.length > 0) { + params.push(normalizedStatuses) + filters.push(`(wo.status = ANY($${params.length}::text[]) OR (${vipEvidencePendingWhere}))`) + } else { + filters.push(`(${vipEvidencePendingWhere})`) + } + } else if (normalizedStatuses.length > 0) { params.push(normalizedStatuses) filters.push(`wo.status = ANY($${params.length}::text[])`) } diff --git a/apps/backend/src/services/worker-platform/admin-service.ts b/apps/backend/src/services/worker-platform/admin-service.ts index 067f44e2..0113194c 100644 --- a/apps/backend/src/services/worker-platform/admin-service.ts +++ b/apps/backend/src/services/worker-platform/admin-service.ts @@ -696,12 +696,15 @@ export async function listAdminWorkOrders(query: JsonObject = {}) { const page = normalizePage(query.page) const pageSize = normalizePageSize(query.pageSize) const workerId = normalizeOptionalId(query.workerId ?? query.worker_id) || 0 + const statuses = normalizeStatuses(query.status) + const vipEvidencePending = normalizeBoolean(query.vipEvidencePending, false) const { items, total } = await listWorkOrders({ page, pageSize, - statuses: normalizeStatuses(query.status), + statuses, keyword: String(query.keyword || '').trim(), workerSharingId: workerId, + vipEvidencePending, }) const shares = await listWorkOrderSharesByOrderIds(items.map((item) => Number(item.id))) const sharesByOrderId = new Map() diff --git a/apps/backend/src/services/worker-platform/worker-service.ts b/apps/backend/src/services/worker-platform/worker-service.ts index 31263050..2bd0376b 100644 --- a/apps/backend/src/services/worker-platform/worker-service.ts +++ b/apps/backend/src/services/worker-platform/worker-service.ts @@ -89,6 +89,7 @@ import { mapWorkerUser, normalizeAmountFen, assertWithdrawChannelAllowedForAmount, + normalizeBoolean, normalizeFinanceRequestStatus, normalizeFinanceRequestType, normalizeInteger, @@ -1114,12 +1115,14 @@ export async function listWorkerMyOrders(query: JsonObject = {}, session: Worker await settleOverdueWorkOrders({ workerId: worker.id, limit: 50 }) const page = normalizePage(query.page) const pageSize = normalizePageSize(query.pageSize) + const vipEvidencePending = normalizeBoolean(query.vipEvidencePending, false) const { items, total } = await listWorkOrders({ page, pageSize, statuses: normalizeStatuses(query.status), keyword: String(query.keyword || '').trim(), workerSharingId: worker.id, + vipEvidencePending, sort: 'worker_claimed_at_desc', }) const permissions = resolveWorkerPermissions(worker) diff --git a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx index 84babb3d..3a41e3ae 100644 --- a/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx +++ b/apps/frontend/src/pages/admin/panels/WorkOrdersPanel.tsx @@ -95,10 +95,12 @@ const FILTERABLE_WORK_ORDER_STATUSES = [ 'in_progress', 'pending_acceptance', 'problem', + 'vip_evidence_pending', 'accepted', 'cancelled', ] as const +const VIP_EVIDENCE_PENDING_STATUS = 'vip_evidence_pending' const SKIP_PUBLISH_CONFIRM_KEY = 'admin-work-order-skip-publish-confirm' type MaterialFormValues = { @@ -158,6 +160,10 @@ export default function WorkOrdersPanel() { }>() // 仅单独筛选「待验收」时提供批量验收入口(多选状态下不启用勾选,避免误操作) const isPendingAcceptanceOnly = statuses.length === 1 && statuses[0] === 'pending_acceptance' + const selectedWorkOrderStatuses = statuses.filter( + (status) => status !== VIP_EVIDENCE_PENDING_STATUS, + ) + const vipEvidencePending = statuses.includes(VIP_EVIDENCE_PENDING_STATUS) const ordersQuery = useQuery({ queryKey: [ @@ -170,7 +176,9 @@ export default function WorkOrdersPanel() { ], queryFn: () => fetchAdminWorkOrders({ - status: statuses.length > 0 ? statuses.join(',') : undefined, + status: + selectedWorkOrderStatuses.length > 0 ? selectedWorkOrderStatuses.join(',') : undefined, + vipEvidencePending: vipEvidencePending || undefined, keyword, workerId, page, @@ -875,6 +883,7 @@ export default function WorkOrdersPanel() { placeholder="全部状态(可多选)" style={{ minWidth: 220, maxWidth: 420 }} maxTagCount="responsive" + listHeight={320} options={FILTERABLE_WORK_ORDER_STATUSES.map((item) => ({ value: item, label: {formatStatus(item)}, diff --git a/apps/frontend/src/pages/admin/panels/shared.tsx b/apps/frontend/src/pages/admin/panels/shared.tsx index 1f928a14..0a8c334a 100644 --- a/apps/frontend/src/pages/admin/panels/shared.tsx +++ b/apps/frontend/src/pages/admin/panels/shared.tsx @@ -202,6 +202,7 @@ export function formatStatus(status: string) { in_progress: '代练中', pending_acceptance: '待验收', problem: '问题单', + vip_evidence_pending: '待补图', accepted: '已验收', cancelled: '已取消', } @@ -216,6 +217,7 @@ export function resolveStatusColor(status: string) { in_progress: 'geekblue', pending_acceptance: 'purple', problem: 'red', + vip_evidence_pending: 'gold', accepted: 'green', cancelled: 'default', } diff --git a/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx b/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx index 1430a62e..7fff9770 100644 --- a/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx +++ b/apps/frontend/src/pages/worker/WorkerOrdersPage.tsx @@ -56,6 +56,7 @@ const STATUS_OPTIONS = [ { value: 'pending_acceptance', label: '待验收' }, { value: 'problem', label: '问题单' }, { value: 'open', label: '已超时' }, + { value: 'vip_evidence_pending', label: '待补图' }, { value: 'accepted', label: '已验收' }, { value: 'cancelled', label: '已取消' }, { value: '', label: '全部订单' }, @@ -69,6 +70,15 @@ type WorkerNoteFormValues = { note?: string } +function buildWorkerOrderQuery(status: string, keyword: string, page: number, pageSize: number) { + return { + keyword, + page, + pageSize, + ...(status === 'vip_evidence_pending' ? { vipEvidencePending: true } : { status }), + } +} + type NoteTheme = 'success' | 'processing' | 'danger' | 'warning' | 'purple' | 'default' const PRESET_NOTE_TAGS: Array<{ label: string; theme: NoteTheme }> = [ @@ -187,7 +197,7 @@ export default function WorkerOrdersPage() { const ordersQuery = useQuery({ queryKey: ['worker-my-orders', status, keyword, page, pageSize], - queryFn: () => fetchWorkerMyOrders({ status, keyword, page, pageSize }), + queryFn: () => fetchWorkerMyOrders(buildWorkerOrderQuery(status, keyword, page, pageSize)), retry: false, }) const pagination = ordersQuery.data?.data.pagination @@ -208,11 +218,7 @@ export default function WorkerOrdersPage() { queryFn: async () => { const results = await Promise.all( STATUS_OPTIONS.map(async (item) => { - const response = await fetchWorkerMyOrders({ - status: item.value, - page: 1, - pageSize: 1, - }) + const response = await fetchWorkerMyOrders(buildWorkerOrderQuery(item.value, '', 1, 1)) return [item.value || 'all', response.data.pagination.total] as const }), )