|
|
@@ -0,0 +1,218 @@
|
|
|
+<script setup lang="ts" name="aidopS8OrderExecutionChain">
|
|
|
+// ORDER-FLOW-CHAIN-SHELL-1:订单链路全景页壳。
|
|
|
+// 入口:总览页订单卡「链路全景」按钮 → store.selectOrderForChain + router.push。
|
|
|
+// 刷新恢复:onMounted 调 store.restoreChainSelection() 从 sessionStorage 重建 selectedOrderNo。
|
|
|
+import { computed, onMounted } from 'vue';
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { useOrderExecutionStore } from '/@/stores/orderExecution';
|
|
|
+import StageMatrixCard from './components/order-execution/StageMatrixCard.vue';
|
|
|
+import NodeDetailCard from './components/order-execution/NodeDetailCard.vue';
|
|
|
+
|
|
|
+const store = useOrderExecutionStore();
|
|
|
+const router = useRouter();
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ store.loadFixture();
|
|
|
+ store.restoreChainSelection();
|
|
|
+});
|
|
|
+
|
|
|
+const order = computed(() => store.selectedOrder);
|
|
|
+
|
|
|
+const focusStage = computed(() => {
|
|
|
+ const o = order.value;
|
|
|
+ if (!o) return null;
|
|
|
+ const lifecycle = o.lifecycle ?? [];
|
|
|
+ if (lifecycle.length === 0) return null;
|
|
|
+ return (
|
|
|
+ lifecycle.find((s) => s.key === o.focusNodeKey) ??
|
|
|
+ lifecycle.find((s) => s.key === o.currentNodeKey) ??
|
|
|
+ lifecycle[0] ??
|
|
|
+ null
|
|
|
+ );
|
|
|
+});
|
|
|
+
|
|
|
+const stages = computed(() => order.value?.lifecycle ?? []);
|
|
|
+
|
|
|
+function goBack() {
|
|
|
+ if (window.history.length > 1) {
|
|
|
+ router.back();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ router.push('/aidop/s8/monitoring/order-execution');
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="oe-chain">
|
|
|
+ <header class="oe-chain__header">
|
|
|
+ <div class="oe-chain__title">
|
|
|
+ <h1 class="oe-chain__title-main">订单链路全景</h1>
|
|
|
+ <p v-if="order" class="oe-chain__title-sub">
|
|
|
+ <span class="oe-chain__so">{{ order.soNo }}</span>
|
|
|
+ <span class="oe-chain__sep">·</span>
|
|
|
+ <span>{{ order.productName }}</span>
|
|
|
+ </p>
|
|
|
+ <p v-else class="oe-chain__title-sub">尚未选择订单</p>
|
|
|
+ </div>
|
|
|
+ <el-button class="oe-chain__back" @click="goBack">返回总览</el-button>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <template v-if="order">
|
|
|
+ <section class="oe-chain__summary">
|
|
|
+ <div class="oe-chain__summary-cell">
|
|
|
+ <span class="oe-chain__summary-label">客户</span>
|
|
|
+ <span class="oe-chain__summary-value">{{ order.customerName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="oe-chain__summary-cell">
|
|
|
+ <span class="oe-chain__summary-label">产品</span>
|
|
|
+ <span class="oe-chain__summary-value">{{ order.productName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="oe-chain__summary-cell">
|
|
|
+ <span class="oe-chain__summary-label">当前节点</span>
|
|
|
+ <span class="oe-chain__summary-value">{{ order.currentNode }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="oe-chain__summary-cell">
|
|
|
+ <span class="oe-chain__summary-label">异常次数</span>
|
|
|
+ <span class="oe-chain__summary-value oe-chain__summary-value--accent">
|
|
|
+ {{ order.exceptionCount }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div class="oe-chain__summary-cell">
|
|
|
+ <span class="oe-chain__summary-label">工作流</span>
|
|
|
+ <span class="oe-chain__summary-value">
|
|
|
+ {{ order.workflowStatus === 'in_progress' ? '执行中' : '已完成' }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <StageMatrixCard :stages="stages" :focus-key="focusStage?.key ?? null" />
|
|
|
+ <NodeDetailCard :stage="focusStage" />
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <section v-else class="oe-chain__empty">
|
|
|
+ <p>未找到订单数据,请从订单执行档案总览进入。</p>
|
|
|
+ <el-button type="primary" @click="goBack">返回总览</el-button>
|
|
|
+ </section>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.oe-chain {
|
|
|
+ --order-bg: #10131a;
|
|
|
+ --order-panel: rgba(25, 28, 34, 0.72);
|
|
|
+ --order-border: rgba(144, 144, 151, 0.18);
|
|
|
+ --order-accent: #7bd0ff;
|
|
|
+ --order-text-primary: #e1e2eb;
|
|
|
+ --order-text-secondary: #c6c6cd;
|
|
|
+ --order-text-muted: #909097;
|
|
|
+
|
|
|
+ min-height: calc(100vh - 108px);
|
|
|
+ padding: 18px 22px 28px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ color: var(--order-text-primary);
|
|
|
+ background:
|
|
|
+ radial-gradient(circle at top left, rgba(123, 208, 255, 0.08), transparent 30%),
|
|
|
+ radial-gradient(circle at bottom right, rgba(109, 224, 57, 0.06), transparent 26%),
|
|
|
+ linear-gradient(180deg, #10131a 0%, #161b25 100%);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 16px;
|
|
|
+ overflow-x: hidden;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__header {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-end;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 16px;
|
|
|
+ padding: 12px 16px;
|
|
|
+ border-radius: 14px;
|
|
|
+ background: var(--order-panel);
|
|
|
+ border: 1px solid var(--order-border);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__title-main {
|
|
|
+ margin: 0 0 4px;
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__title-sub {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 13px;
|
|
|
+ color: var(--order-text-secondary);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__so {
|
|
|
+ font-family: 'JetBrains Mono', 'Cascadia Code', Menlo, monospace;
|
|
|
+ color: var(--order-accent);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__sep {
|
|
|
+ color: var(--order-text-muted);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__summary {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
|
+ gap: 12px;
|
|
|
+ padding: 14px 16px;
|
|
|
+ border-radius: 12px;
|
|
|
+ background: var(--order-panel);
|
|
|
+ border: 1px solid var(--order-border);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__summary-cell {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__summary-label {
|
|
|
+ font-size: 11px;
|
|
|
+ color: var(--order-text-muted);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__summary-value {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: var(--order-text-primary);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__summary-value--accent {
|
|
|
+ color: #ffc107;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-chain__empty {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ gap: 14px;
|
|
|
+ padding: 48px 24px;
|
|
|
+ border-radius: 12px;
|
|
|
+ background: var(--order-panel);
|
|
|
+ border: 1px dashed var(--order-border);
|
|
|
+ color: var(--order-text-secondary);
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 1080px) {
|
|
|
+ .oe-chain__summary {
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 640px) {
|
|
|
+ .oe-chain__summary {
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .oe-chain__header {
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|