|
|
@@ -1,9 +1,11 @@
|
|
|
<script setup lang="ts" name="OrderExecutionOrderList">
|
|
|
// ORDER-FLOW-OVERVIEW-LIST-EXPAND-1:统一订单列表 + 单行点击展开订单档案详情。
|
|
|
-// 替代客户分组卡片布局;行内展开复用 StageMatrixCard,不重写五阶段矩阵。
|
|
|
+// ORDER-FLOW-OVERVIEW-EXPAND-MATRIX-TABLE-1:行内五阶段改为横向对照表(行=指标、列=阶段)。
|
|
|
import { computed, ref } from 'vue';
|
|
|
-import type { SalesOrderExecution } from '/@/views/aidop/s8/monitoring/data/order-execution/types';
|
|
|
-import StageMatrixCard from './StageMatrixCard.vue';
|
|
|
+import type {
|
|
|
+ SalesOrderExecution,
|
|
|
+ StageSnapshot,
|
|
|
+} from '/@/views/aidop/s8/monitoring/data/order-execution/types';
|
|
|
|
|
|
interface Props {
|
|
|
orders: SalesOrderExecution[];
|
|
|
@@ -39,6 +41,49 @@ const fmtVar = (v: number | null | undefined) => {
|
|
|
const varTone = (v: number | null | undefined): 'over' | 'under' | 'neutral' =>
|
|
|
v == null ? 'neutral' : v > 0 ? 'over' : v < 0 ? 'under' : 'neutral';
|
|
|
|
|
|
+const STAGE_STATUS_LABEL: Record<StageSnapshot['status'], string> = {
|
|
|
+ green: '正常',
|
|
|
+ yellow: '关注',
|
|
|
+ red: '严重',
|
|
|
+ pending: '未开始',
|
|
|
+};
|
|
|
+
|
|
|
+interface StageColumn {
|
|
|
+ key: string;
|
|
|
+ name: string;
|
|
|
+ kpiText: string;
|
|
|
+ targetDate: string;
|
|
|
+ actualReachedAt: string;
|
|
|
+ status: StageSnapshot['status'];
|
|
|
+ statusLabel: string;
|
|
|
+ nodeVarText: string;
|
|
|
+ nodeVarTone: 'over' | 'under' | 'neutral';
|
|
|
+ cumVarText: string;
|
|
|
+ cumVarTone: 'over' | 'under' | 'neutral';
|
|
|
+}
|
|
|
+
|
|
|
+function buildStageColumns(stages: StageSnapshot[]): StageColumn[] {
|
|
|
+ return stages.map((stage) => {
|
|
|
+ const kpi =
|
|
|
+ stage.actualDays != null && stage.nodeVarianceDays != null
|
|
|
+ ? Number((stage.actualDays - stage.nodeVarianceDays).toFixed(1))
|
|
|
+ : null;
|
|
|
+ return {
|
|
|
+ key: stage.key,
|
|
|
+ name: stage.name,
|
|
|
+ kpiText: kpi != null ? kpi.toFixed(1) : '--',
|
|
|
+ targetDate: stage.targetDate || '--',
|
|
|
+ actualReachedAt: stage.actualReachedAt ?? '尚未达成',
|
|
|
+ status: stage.status,
|
|
|
+ statusLabel: STAGE_STATUS_LABEL[stage.status],
|
|
|
+ nodeVarText: fmtVar(stage.nodeVarianceDays),
|
|
|
+ nodeVarTone: varTone(stage.nodeVarianceDays),
|
|
|
+ cumVarText: fmtVar(stage.cumulativeVarianceDays),
|
|
|
+ cumVarTone: varTone(stage.cumulativeVarianceDays),
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
const rows = computed(() =>
|
|
|
props.orders.map((o) => ({
|
|
|
raw: o,
|
|
|
@@ -201,10 +246,84 @@ const rows = computed(() =>
|
|
|
</table>
|
|
|
</div>
|
|
|
|
|
|
- <StageMatrixCard
|
|
|
- :stages="row.raw.lifecycle"
|
|
|
- :focus-key="row.raw.focusNodeKey ?? row.raw.currentNodeKey ?? null"
|
|
|
- />
|
|
|
+ <div class="oe-detail-matrix">
|
|
|
+ <header class="oe-detail-section-head">
|
|
|
+ <span class="oe-detail-section-bar" />
|
|
|
+ <h3 class="oe-detail-section-title">五阶段对照</h3>
|
|
|
+ <span class="oe-detail-matrix__legend">
|
|
|
+ <span class="oe-detail-matrix__legend-item"><span class="oe-detail-matrix__legend-dot oe-detail-matrix__legend-dot--green" />达成规划</span>
|
|
|
+ <span class="oe-detail-matrix__legend-item"><span class="oe-detail-matrix__legend-dot oe-detail-matrix__legend-dot--yellow" />未达成规划,对客户无影响</span>
|
|
|
+ <span class="oe-detail-matrix__legend-item"><span class="oe-detail-matrix__legend-dot oe-detail-matrix__legend-dot--red" />未达成规划,对客户有影响</span>
|
|
|
+ </span>
|
|
|
+ </header>
|
|
|
+ <table class="oe-detail-matrix-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th class="oe-detail-matrix-table__metric-head">时间节点</th>
|
|
|
+ <th
|
|
|
+ v-for="col in buildStageColumns(row.raw.lifecycle)"
|
|
|
+ :key="col.key"
|
|
|
+ class="oe-detail-matrix-table__stage-head"
|
|
|
+ :class="`oe-detail-matrix-table__stage-head--${col.status}`"
|
|
|
+ >
|
|
|
+ {{ col.name }}
|
|
|
+ </th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr>
|
|
|
+ <td class="oe-detail-matrix-table__metric">KPI</td>
|
|
|
+ <td v-for="col in buildStageColumns(row.raw.lifecycle)" :key="col.key">
|
|
|
+ {{ col.kpiText }}
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="oe-detail-matrix-table__metric">目标时间</td>
|
|
|
+ <td v-for="col in buildStageColumns(row.raw.lifecycle)" :key="col.key">
|
|
|
+ {{ col.targetDate }}
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="oe-detail-matrix-table__metric">完成时间</td>
|
|
|
+ <td v-for="col in buildStageColumns(row.raw.lifecycle)" :key="col.key">
|
|
|
+ {{ col.actualReachedAt }}
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="oe-detail-matrix-table__metric">节点状态</td>
|
|
|
+ <td v-for="col in buildStageColumns(row.raw.lifecycle)" :key="col.key">
|
|
|
+ <span
|
|
|
+ class="oe-detail-matrix-table__status"
|
|
|
+ :class="`oe-detail-matrix-table__status--${col.status}`"
|
|
|
+ >
|
|
|
+ <span class="oe-detail-matrix-table__status-dot" />
|
|
|
+ {{ col.statusLabel }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="oe-detail-matrix-table__metric">当前节点偏差(天)</td>
|
|
|
+ <td
|
|
|
+ v-for="col in buildStageColumns(row.raw.lifecycle)"
|
|
|
+ :key="col.key"
|
|
|
+ :class="`oe-var--${col.nodeVarTone}`"
|
|
|
+ >
|
|
|
+ {{ col.nodeVarText }}
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="oe-detail-matrix-table__metric">累计偏差(天)</td>
|
|
|
+ <td
|
|
|
+ v-for="col in buildStageColumns(row.raw.lifecycle)"
|
|
|
+ :key="col.key"
|
|
|
+ :class="`oe-var--${col.cumVarTone}`"
|
|
|
+ >
|
|
|
+ {{ col.cumVarText }}
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
</section>
|
|
|
</transition>
|
|
|
</div>
|
|
|
@@ -505,6 +624,116 @@ const rows = computed(() =>
|
|
|
.oe-detail-status--yellow { color: #ffc107; }
|
|
|
.oe-detail-status--red { color: #ffb4ab; }
|
|
|
|
|
|
+.oe-detail-matrix {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix__legend {
|
|
|
+ display: inline-flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 12px;
|
|
|
+ margin-left: auto;
|
|
|
+ font-size: 11px;
|
|
|
+ color: var(--order-text-muted, #909097);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix__legend-item {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix__legend-dot {
|
|
|
+ width: 8px;
|
|
|
+ height: 8px;
|
|
|
+ border-radius: 999px;
|
|
|
+ display: inline-block;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix__legend-dot--green { background: #88fd54; }
|
|
|
+.oe-detail-matrix__legend-dot--yellow { background: #ffc107; }
|
|
|
+.oe-detail-matrix__legend-dot--red { background: #ffb4ab; }
|
|
|
+
|
|
|
+.oe-detail-matrix-table {
|
|
|
+ width: 100%;
|
|
|
+ border-collapse: collapse;
|
|
|
+ background: rgba(25, 28, 34, 0.55);
|
|
|
+ border: 1px solid rgba(69, 70, 77, 0.28);
|
|
|
+ border-radius: 10px;
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: 12px;
|
|
|
+ table-layout: fixed;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table th,
|
|
|
+.oe-detail-matrix-table td {
|
|
|
+ padding: 8px 10px;
|
|
|
+ text-align: center;
|
|
|
+ border-bottom: 1px solid rgba(69, 70, 77, 0.18);
|
|
|
+ border-right: 1px solid rgba(69, 70, 77, 0.18);
|
|
|
+ color: var(--order-text-primary, #e1e2eb);
|
|
|
+ font-family: 'JetBrains Mono', 'Cascadia Code', Menlo, monospace;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table th:last-child,
|
|
|
+.oe-detail-matrix-table td:last-child {
|
|
|
+ border-right: none;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table tr:last-child td {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table thead th {
|
|
|
+ background: rgba(15, 19, 26, 0.7);
|
|
|
+ color: var(--order-text-muted, #909097);
|
|
|
+ font-weight: 600;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table__metric-head {
|
|
|
+ width: 140px;
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table__metric {
|
|
|
+ text-align: left;
|
|
|
+ color: var(--order-text-muted, #909097);
|
|
|
+ background: rgba(15, 19, 26, 0.45);
|
|
|
+ font-family: 'PingFang SC', sans-serif;
|
|
|
+ font-weight: 600;
|
|
|
+ width: 140px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table__stage-head--green { box-shadow: inset 3px 0 0 #88fd54; }
|
|
|
+.oe-detail-matrix-table__stage-head--yellow { box-shadow: inset 3px 0 0 #ffc107; }
|
|
|
+.oe-detail-matrix-table__stage-head--red { box-shadow: inset 3px 0 0 #ffb4ab; }
|
|
|
+.oe-detail-matrix-table__stage-head--pending { box-shadow: inset 3px 0 0 rgba(144, 144, 151, 0.45); }
|
|
|
+
|
|
|
+.oe-detail-matrix-table__status {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 5px;
|
|
|
+ padding: 1px 8px;
|
|
|
+ border-radius: 999px;
|
|
|
+ font-size: 11px;
|
|
|
+ font-family: 'PingFang SC', sans-serif;
|
|
|
+ background: rgba(50, 53, 60, 0.6);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table__status-dot {
|
|
|
+ width: 6px;
|
|
|
+ height: 6px;
|
|
|
+ border-radius: 999px;
|
|
|
+ background: currentColor;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-detail-matrix-table__status--green { color: #88fd54; }
|
|
|
+.oe-detail-matrix-table__status--yellow { color: #ffc107; }
|
|
|
+.oe-detail-matrix-table__status--red { color: #ffb4ab; }
|
|
|
+.oe-detail-matrix-table__status--pending { color: var(--order-text-muted, #909097); }
|
|
|
+
|
|
|
.oe-expand-enter-active,
|
|
|
.oe-expand-leave-active {
|
|
|
transition: max-height 200ms ease, opacity 180ms ease;
|