diff --git a/apps/web-antd/src/views/ops/work-order/dashboard/index.vue b/apps/web-antd/src/views/ops/work-order/dashboard/index.vue index ad5ad9614..83db2ffa7 100644 --- a/apps/web-antd/src/views/ops/work-order/dashboard/index.vue +++ b/apps/web-antd/src/views/ops/work-order/dashboard/index.vue @@ -17,6 +17,8 @@ import { Card, Col, Row, Spin, Statistic, Tooltip } from 'ant-design-vue'; import { getDashboardStats } from '#/api/ops/order-center'; +import { STATUS_COLOR_MAP, STATUS_TEXT_MAP } from '../data'; + defineOptions({ name: 'CleaningWorkOrderDashboard' }); // ========== 保洁类型映射 ========== @@ -468,14 +470,22 @@ function getTimeTrendChartOptions(): ECOption { */ function getStatusDistributionChartOptions(): ECOption { const { statusDistribution } = statsData.value; - const STATUS_COLORS: Record = { - 待处理: '#C2540A', // PENDING - 排队中: '#6D28D9', // QUEUED - 已派单: '#0284C7', // DISPATCHED - 已到岗: '#0D9488', // ARRIVED - 已完成: '#0A7A55', // COMPLETED - 已取消: '#6B5E52', // CANCELLED - 已暂停: '#92400E', // PAUSED + // 中文名 → 状态码映射(兼容后端返回的中文名与前端 TEXT_MAP 的差异) + const NAME_TO_CODE: Record = { + // 从 STATUS_TEXT_MAP 自动生成 + ...Object.fromEntries( + Object.entries(STATUS_TEXT_MAP).map(([code, label]) => [label, code]), + ), + // 后端可能返回的别名补充 + 待处理: 'PENDING', + 已派单: 'DISPATCHED', + 已到岗: 'ARRIVED', + 进行中: 'IN_PROGRESS', + 已恢复: 'RESUMED', + }; + const getStatusColor = (name: string) => { + const code = NAME_TO_CODE[name]; + return code ? STATUS_COLOR_MAP[code]?.text : '#d9d9d9'; }; const total = statusDistribution.reduce((sum, item) => sum + item.value, 0); return { @@ -547,7 +557,7 @@ function getStatusDistributionChartOptions(): ECOption { data: statusDistribution.map((item) => ({ ...item, itemStyle: { - color: STATUS_COLORS[item.name] || '#d9d9d9', + color: getStatusColor(item.name), }, })), }, diff --git a/apps/web-antd/src/views/ops/work-order/detail/index.vue b/apps/web-antd/src/views/ops/work-order/detail/index.vue index b18d9dd63..f3a95fc8c 100644 --- a/apps/web-antd/src/views/ops/work-order/detail/index.vue +++ b/apps/web-antd/src/views/ops/work-order/detail/index.vue @@ -363,10 +363,26 @@ function getStepTime(key: string): string { return ''; } +/** 将 timeline 节点转为步骤展示项 */ +function toStepItem(t: OpsCleaningApi.TimelineItem) { + const step = STATUS_STEPS.find((s) => s.key === t.status); + return { + key: t.status, + title: + t.status === 'CANCELLED' + ? '已取消' + : step?.title || t.statusName || t.status, + icon: step?.icon || 'solar:circle-bold-duotone', + desc: t.description || step?.desc || '', + }; +} + /** 动态生成应该显示的状态步骤(根据 timeline 过滤) */ const visibleSteps = computed(() => { - // 取消状态:只展示 timeline 中已发生的节点(按时间顺序,去重) - if (isCancelled.value) { + const currentStatus = order.value.status; + + // 终态(已完成/已取消):直接按 timeline 返回顺序展示,去重 + if (['CANCELLED', 'COMPLETED'].includes(currentStatus)) { const seen = new Set(); return timeline.value .filter((t) => { @@ -374,59 +390,44 @@ const visibleSteps = computed(() => { seen.add(t.status); return true; }) - .map((t) => { - const step = STATUS_STEPS.find((s) => s.key === t.status); - return { - key: t.status, - title: - t.status === 'CANCELLED' - ? '已取消' - : step?.title || t.statusName || t.status, - icon: step?.icon || 'solar:close-circle-bold-duotone', - desc: t.description || step?.desc || '', - }; - }); + .map((t) => toStepItem(t)); } - // 必须显示的节点(主流程) - const requiredSteps = new Set([ - 'ARRIVED', - 'COMPLETED', - 'CONFIRMED', - 'DISPATCHED', - 'PENDING', - ]); + // 进行中状态:timeline 中已有的节点 + 当前状态后续的预期节点 + // 从 timeline 中已发生的节点(去重、保序) + const seen = new Set(); + const steps = timeline.value + .filter((t) => { + if (seen.has(t.status)) return false; + seen.add(t.status); + return true; + }) + .map((t) => toStepItem(t)); - // timeline 中存在的状态 - const timelineStatuses = new Set(timeline.value.map((t) => t.status)); - - // 当前状态 - const currentStatus = order.value.status; - - // 过滤出要显示的节点 - return STATUS_STEPS.filter((step) => { - // 必须显示的节点 - if (requiredSteps.has(step.key)) return true; - - // QUEUED 节点:只有在 timeline 中明确存在时才显示 - if (step.key === 'QUEUED') { - return timelineStatuses.has('QUEUED'); + // 追加尚未到达的后续预期节点(从 STATUS_STEPS 中取当前状态之后的) + const currentIdx = STATUS_STEPS.findIndex((s) => s.key === currentStatus); + for (const step of STATUS_STEPS.slice(currentIdx + 1)) { + if (!seen.has(step.key)) { + // QUEUED 只在 timeline 中明确出现时才展示 + if (step.key === 'QUEUED') continue; + steps.push({ ...step }); + seen.add(step.key); } + } - // 其他节点:timeline 中存在,或者是当前状态 - return timelineStatuses.has(step.key) || currentStatus === step.key; - }); + return steps; }); /** 计算当前状态步骤索引 */ const currentStepIndex = computed(() => { - // 取消状态:所有节点都视为"已走过",索引指向最后一个 - if (isCancelled.value) { + // 终态(取消/完成):索引指向最后一个节点 + if (isCancelled.value || order.value.status === 'COMPLETED') { return visibleSteps.value.length - 1; } if (order.value.status === 'PAUSED') { // 暂停状态显示在到岗后 - return visibleSteps.value.findIndex((s) => s.key === 'ARRIVED'); + const arrivedIdx = visibleSteps.value.findIndex((s) => s.key === 'ARRIVED'); + return arrivedIdx === -1 ? visibleSteps.value.length - 1 : arrivedIdx; } const index = visibleSteps.value.findIndex( (s) => s.key === order.value.status,