|
|
@@ -0,0 +1,239 @@
|
|
|
+<script setup lang="ts" name="aidopS8OrderExecution">
|
|
|
+// ORDER-FLOW-OVERVIEW-SHELL-1:SO 订单执行档案总览页壳。
|
|
|
+// 仅本轮:4 Tab + KPI 三卡 + 客户分组列表;不接后端、不做链路全景、不做筛选条。
|
|
|
+import { computed, onMounted, ref } from 'vue';
|
|
|
+import { useOrderExecutionStore } from '/@/stores/orderExecution';
|
|
|
+import OrderExecutionKpiCards from './components/order-execution/OrderExecutionKpiCards.vue';
|
|
|
+import OrderExecutionCustomerGroups from './components/order-execution/OrderExecutionCustomerGroups.vue';
|
|
|
+
|
|
|
+const store = useOrderExecutionStore();
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ store.loadFixture();
|
|
|
+});
|
|
|
+
|
|
|
+const customers = computed(() => store.customerGroups);
|
|
|
+const orders = computed(() => store.allOrders);
|
|
|
+const kpi = computed(() => store.kpiAverages);
|
|
|
+const observedAt = computed(() => store.currentObservationTime);
|
|
|
+const ready = computed(() => store.initialized && orders.value.length > 0);
|
|
|
+const orderCount = computed(() => orders.value.length);
|
|
|
+const customerCount = computed(() => customers.value.length);
|
|
|
+
|
|
|
+const activeTab = ref<'overview' | 'execution' | 'source-dept' | 'handle-dept'>('overview');
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <div class="oe-dashboard">
|
|
|
+ <header class="oe-dashboard__header">
|
|
|
+ <div class="oe-dashboard__title">
|
|
|
+ <h1 class="oe-dashboard__title-main">SO 订单执行档案总览</h1>
|
|
|
+ <p class="oe-dashboard__title-sub">基于订单全流程维度的客户订单执行监控</p>
|
|
|
+ </div>
|
|
|
+ <div class="oe-dashboard__observation">
|
|
|
+ <span class="oe-dashboard__observation-label">当前观测时间</span>
|
|
|
+ <span class="oe-dashboard__observation-value">{{ observedAt }}</span>
|
|
|
+ </div>
|
|
|
+ </header>
|
|
|
+
|
|
|
+ <el-tabs v-model="activeTab" class="oe-dashboard__tabs">
|
|
|
+ <el-tab-pane label="订单/客户总览" name="overview">
|
|
|
+ <section class="oe-dashboard__section">
|
|
|
+ <OrderExecutionKpiCards :kpi="kpi" :ready="ready" />
|
|
|
+
|
|
|
+ <div class="oe-dashboard__counts">
|
|
|
+ <div class="oe-dashboard__count">
|
|
|
+ <span class="oe-dashboard__count-label">当前命中订单</span>
|
|
|
+ <span class="oe-dashboard__count-value">{{ ready ? orderCount : '--' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="oe-dashboard__count">
|
|
|
+ <span class="oe-dashboard__count-label">客户分组</span>
|
|
|
+ <span class="oe-dashboard__count-value">{{ ready ? customerCount : '--' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="oe-dashboard__group-title">
|
|
|
+ <span class="oe-dashboard__group-bar" />
|
|
|
+ <h2 class="oe-dashboard__group-text">客户分组</h2>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <OrderExecutionCustomerGroups :customers="customers" :preview-limit="3" />
|
|
|
+ </section>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <el-tab-pane label="执行流程环节" name="execution">
|
|
|
+ <div class="oe-dashboard__placeholder">后续迭代接入</div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="异常发生部门" name="source-dept">
|
|
|
+ <div class="oe-dashboard__placeholder">后续迭代接入</div>
|
|
|
+ </el-tab-pane>
|
|
|
+ <el-tab-pane label="处理部门" name="handle-dept">
|
|
|
+ <div class="oe-dashboard__placeholder">后续迭代接入</div>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.oe-dashboard {
|
|
|
+ --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%);
|
|
|
+ overflow-x: hidden;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__header {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-end;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 14px;
|
|
|
+ padding: 12px 16px;
|
|
|
+ border-radius: 14px;
|
|
|
+ background: var(--order-panel);
|
|
|
+ border: 1px solid var(--order-border);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__title-main {
|
|
|
+ margin: 0 0 4px;
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+ letter-spacing: 0.02em;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__title-sub {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--order-text-secondary);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__observation {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-end;
|
|
|
+ gap: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__observation-label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--order-text-muted);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__observation-value {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: var(--order-accent);
|
|
|
+ font-family: 'JetBrains Mono', 'Cascadia Code', Menlo, monospace;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__tabs {
|
|
|
+ --el-tabs-header-height: 44px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.oe-dashboard__tabs .el-tabs__item) {
|
|
|
+ color: var(--order-text-secondary);
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.oe-dashboard__tabs .el-tabs__item.is-active) {
|
|
|
+ color: var(--order-accent);
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.oe-dashboard__tabs .el-tabs__active-bar) {
|
|
|
+ background-color: var(--order-accent);
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.oe-dashboard__tabs .el-tabs__nav-wrap::after) {
|
|
|
+ background-color: var(--order-border);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__section {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 16px;
|
|
|
+ padding: 4px 0 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__counts {
|
|
|
+ display: flex;
|
|
|
+ gap: 24px;
|
|
|
+ padding: 10px 16px;
|
|
|
+ border-radius: 12px;
|
|
|
+ background: var(--order-panel);
|
|
|
+ border: 1px solid var(--order-border);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__count {
|
|
|
+ display: flex;
|
|
|
+ align-items: baseline;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__count-label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--order-text-secondary);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__count-value {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: 700;
|
|
|
+ color: var(--order-accent);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__group-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ margin-top: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__group-bar {
|
|
|
+ width: 5px;
|
|
|
+ height: 18px;
|
|
|
+ border-radius: 999px;
|
|
|
+ background: var(--order-accent);
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__group-text {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 700;
|
|
|
+}
|
|
|
+
|
|
|
+.oe-dashboard__placeholder {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ min-height: 240px;
|
|
|
+ border-radius: 14px;
|
|
|
+ background: var(--order-panel);
|
|
|
+ border: 1px dashed var(--order-border);
|
|
|
+ color: var(--order-text-secondary);
|
|
|
+ font-size: 14px;
|
|
|
+ letter-spacing: 0.04em;
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .oe-dashboard__header {
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+ }
|
|
|
+
|
|
|
+ .oe-dashboard__observation {
|
|
|
+ align-items: flex-start;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|