ソースを参照

feat: 新增首页

liujt 1 日 前
コミット
23b286d7c6
3 ファイル変更879 行追加206 行削除
  1. 218 0
      src/views/home/common.vue
  2. 12 206
      src/views/home/index.vue
  3. 649 0
      src/views/home/statisticsScreen.vue

+ 218 - 0
src/views/home/common.vue

@@ -0,0 +1,218 @@
+<template>
+  <div class="ele-body">
+    <el-row :gutter="5">
+      <el-col :span="13" style="height: 50%">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>采销统计</span>
+          </div>
+          <v-chart ref="barRef" style="height: 100%" :option="barOption" />
+        </el-card>
+      </el-col>
+
+      <el-col :span="11" style="height: 50%">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span style="cursor: pointer;" @click="handelRouterTo('/page-eos/saleManage/saleOrder')">销售订单进度情况</span>
+          </div>
+          <ele-pro-table
+            ref="table"
+            :pageSize="20"
+            :columns="columns"
+            :datasource="datasource"
+            :toolbar="false"
+            height="calc(100% - 45px)"
+          >
+          </ele-pro-table>
+        </el-card>
+      </el-col>
+
+      <el-col :span="11" style="height: 50%; margin-top: 5px">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span style="cursor: pointer;" @click="handelRouterTo('/page-eos/purchaseOrder')">采购订单进度情况</span>
+          </div>
+          <ele-pro-table
+            ref="table"
+            :columns="columns1"
+            :pageSize="20"
+            :datasource="datasource1"
+            :toolbar="false"
+            height="calc(100% - 45px)"
+          >
+          </ele-pro-table>
+        </el-card>
+      </el-col>
+      <el-col :span="13" style="height: 50%; margin-top: 5px">
+        <el-card class="box-card">
+          <div slot="header" class="clearfix">
+            <span>应收应付统计</span>
+          </div>
+          <v-chart ref="barRef1" style="height: 100%" :option="barOption1" />
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script>
+  import { pieOption, barOption, columns, columns1 } from './data';
+  import { use } from 'echarts/core';
+  import { CanvasRenderer } from 'echarts/renderers';
+  import { BarChart } from 'echarts/charts';
+  import {
+    GridComponent,
+    TooltipComponent,
+    LegendComponent
+  } from 'echarts/components';
+
+  import VChart from 'vue-echarts';
+  import { echartsMixin } from '@/utils/echarts-mixin';
+  import { indexGroup } from '@/api/main/index.js';
+  import { init } from 'echarts';
+  import { getTableList } from '@/api/purchasingManage/purchaseOrder';
+  import { getTableListHome } from '@/api/saleManage/saleorder';
+  // 按需加载 echarts
+  use([
+    CanvasRenderer,
+    BarChart,
+    GridComponent,
+    TooltipComponent,
+    LegendComponent
+  ]);
+  export default {
+    mixins: [echartsMixin(['barRef', 'barRef1'])],
+
+    components: { VChart },
+    data() {
+      return {
+        columns,
+        columns1,
+        barOption: {},
+        barOption1: {},
+        timer: null
+      };
+    },
+
+    created() {},
+    mounted() {
+      this.init();
+      this.timer = setInterval(() => {
+        this.init();
+      }, 3600000);
+    },
+    beforeDestroy() {
+      clearInterval(this.timer);
+    },
+    methods: {
+      handelRouterTo(path) {
+        window.history.pushState(null, '', path);
+      },
+      init() {
+        indexGroup().then((res) => {
+          console.log(res, 'res');
+          this.barOption = barOption(
+            res.saleAndPurchaseGroup?.monthName,
+            [
+              {
+                name: '采购',
+                barWidth: '25%',
+                data: res.saleAndPurchaseGroup.purchaseCounts,
+                type: 'bar',
+                yAxisIndex: 0, // 使用第一个Y轴
+                itemStyle: {
+                  color: '#f9cd5d'
+                }
+              },
+              {
+                name: '销售',
+                barWidth: '25%',
+                data: res.saleAndPurchaseGroup.saleCounts,
+                type: 'bar',
+                yAxisIndex: 0, // 使用第一个Y轴
+                itemStyle: {
+                  color: '#5893eb'
+                }
+              }
+            ],
+          );
+          this.barOption1 = barOption(
+            res.payableAndReceivableGroup?.monthName,
+            [
+              {
+                name: '应收',
+                barWidth: '25%',
+                data: res.payableAndReceivableGroup?.receivableCounts,
+                type: 'bar',
+                yAxisIndex: 0 // 使用第一个Y轴
+              },
+              {
+                name: '应付',
+                barWidth: '25%',
+                data: res.payableAndReceivableGroup?.payableCounts,
+                type: 'bar',
+                yAxisIndex: 0 // 使用第一个Y轴
+              }
+            ],
+            '(万元)'
+          );
+          setTimeout(() => {
+            this.$refs.barRef.resize();
+            this.$refs.barRef1.resize();
+          }, 300);
+        });
+      },
+      datasource({ page, where, limit }) {
+        return getTableListHome({
+          ...where,
+          pageNum: page,
+          size: limit,
+          // progress: 1000
+        });
+      },
+      datasource1({ page, where, limit }) {
+        return getTableList({
+          ...where,
+          pageNum: page,
+          size: limit,
+          // progress: 1000
+        });
+      }
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .clearfix {
+    font-size: 0.7vw;
+
+    > span {
+      font-weight: bold;
+    }
+    :deep(.el-radio-button__inner) {
+      font-size: 0.7vw;
+    }
+  }
+  .ele-body {
+    height: calc(100vh - 100px);
+    > .el-row {
+      height: 100%;
+    }
+    .el-card {
+      height: 100%;
+      :deep(.el-card__body) {
+        padding: 0.3vw;
+      }
+      :deep(.el-card__header) {
+        padding: 0.6vw;
+      }
+      :deep(.el-card__body) {
+        height: calc(100% - 2.3vw);
+      }
+      :deep(.ele-pro-table) {
+        height: 99%;
+      }
+      :deep(.el-table) {
+        font-size: 0.62vw;
+      }
+    }
+  }
+</style>

+ 12 - 206
src/views/home/index.vue

@@ -1,218 +1,24 @@
 <template>
-  <div class="ele-body">
-    <el-row :gutter="5">
-      <el-col :span="13" style="height: 50%">
-        <el-card class="box-card">
-          <div slot="header" class="clearfix">
-            <span>采销统计</span>
-          </div>
-          <v-chart ref="barRef" style="height: 100%" :option="barOption" />
-        </el-card>
-      </el-col>
-
-      <el-col :span="11" style="height: 50%">
-        <el-card class="box-card">
-          <div slot="header" class="clearfix">
-            <span style="cursor: pointer;" @click="handelRouterTo('/page-eos/saleManage/saleOrder')">销售订单进度情况</span>
-          </div>
-          <ele-pro-table
-            ref="table"
-            :pageSize="20"
-            :columns="columns"
-            :datasource="datasource"
-            :toolbar="false"
-            height="calc(100% - 45px)"
-          >
-          </ele-pro-table>
-        </el-card>
-      </el-col>
-
-      <el-col :span="11" style="height: 50%; margin-top: 5px">
-        <el-card class="box-card">
-          <div slot="header" class="clearfix">
-            <span style="cursor: pointer;" @click="handelRouterTo('/page-eos/purchaseOrder')">采购订单进度情况</span>
-          </div>
-          <ele-pro-table
-            ref="table"
-            :columns="columns1"
-            :pageSize="20"
-            :datasource="datasource1"
-            :toolbar="false"
-            height="calc(100% - 45px)"
-          >
-          </ele-pro-table>
-        </el-card>
-      </el-col>
-      <el-col :span="13" style="height: 50%; margin-top: 5px">
-        <el-card class="box-card">
-          <div slot="header" class="clearfix">
-            <span>应收应付统计</span>
-          </div>
-          <v-chart ref="barRef1" style="height: 100%" :option="barOption1" />
-        </el-card>
-      </el-col>
-    </el-row>
+  <div>
+    <statisticsScreen v-if="$hasPermission('eom:home:statisticsScreen')"></statisticsScreen>
+     
+    <common v-else></common>
   </div>
 </template>
+
 <script>
-  import { pieOption, barOption, columns, columns1 } from './data';
-  import { use } from 'echarts/core';
-  import { CanvasRenderer } from 'echarts/renderers';
-  import { BarChart } from 'echarts/charts';
-  import {
-    GridComponent,
-    TooltipComponent,
-    LegendComponent
-  } from 'echarts/components';
+  import common from './common.vue';
+  import statisticsScreen from './statisticsScreen.vue';
 
-  import VChart from 'vue-echarts';
-  import { echartsMixin } from '@/utils/echarts-mixin';
-  import { indexGroup } from '@/api/main/index.js';
-  import { init } from 'echarts';
-  import { getTableList } from '@/api/purchasingManage/purchaseOrder';
-  import { getTableListHome } from '@/api/saleManage/saleorder';
-  // 按需加载 echarts
-  use([
-    CanvasRenderer,
-    BarChart,
-    GridComponent,
-    TooltipComponent,
-    LegendComponent
-  ]);
   export default {
-    mixins: [echartsMixin(['barRef', 'barRef1'])],
-
-    components: { VChart },
+    components: { common, statisticsScreen },
     data() {
-      return {
-        columns,
-        columns1,
-        barOption: {},
-        barOption1: {},
-        timer: null
-      };
+      return {};
     },
+    computed: {},
 
-    created() {},
-    mounted() {
-      this.init();
-      this.timer = setInterval(() => {
-        this.init();
-      }, 3600000);
-    },
-    beforeDestroy() {
-      clearInterval(this.timer);
-    },
-    methods: {
-      handelRouterTo(path) {
-        window.history.pushState(null, '', path);
-      },
-      init() {
-        indexGroup().then((res) => {
-          console.log(res, 'res');
-          this.barOption = barOption(
-            res.saleAndPurchaseGroup?.monthName,
-            [
-              {
-                name: '采购',
-                barWidth: '25%',
-                data: res.saleAndPurchaseGroup.purchaseCounts,
-                type: 'bar',
-                yAxisIndex: 0, // 使用第一个Y轴
-                itemStyle: {
-                  color: '#f9cd5d'
-                }
-              },
-              {
-                name: '销售',
-                barWidth: '25%',
-                data: res.saleAndPurchaseGroup.saleCounts,
-                type: 'bar',
-                yAxisIndex: 0, // 使用第一个Y轴
-                itemStyle: {
-                  color: '#5893eb'
-                }
-              }
-            ],
-          );
-          this.barOption1 = barOption(
-            res.payableAndReceivableGroup?.monthName,
-            [
-              {
-                name: '应收',
-                barWidth: '25%',
-                data: res.payableAndReceivableGroup?.receivableCounts,
-                type: 'bar',
-                yAxisIndex: 0 // 使用第一个Y轴
-              },
-              {
-                name: '应付',
-                barWidth: '25%',
-                data: res.payableAndReceivableGroup?.payableCounts,
-                type: 'bar',
-                yAxisIndex: 0 // 使用第一个Y轴
-              }
-            ],
-            '(万元)'
-          );
-          setTimeout(() => {
-            this.$refs.barRef.resize();
-            this.$refs.barRef1.resize();
-          }, 300);
-        });
-      },
-      datasource({ page, where, limit }) {
-        return getTableListHome({
-          ...where,
-          pageNum: page,
-          size: limit,
-          // progress: 1000
-        });
-      },
-      datasource1({ page, where, limit }) {
-        return getTableList({
-          ...where,
-          pageNum: page,
-          size: limit,
-          // progress: 1000
-        });
-      }
-    }
+    mounted() {}
   };
 </script>
-<style lang="scss" scoped>
-  .clearfix {
-    font-size: 0.7vw;
 
-    > span {
-      font-weight: bold;
-    }
-    :deep(.el-radio-button__inner) {
-      font-size: 0.7vw;
-    }
-  }
-  .ele-body {
-    height: calc(100vh - 100px);
-    > .el-row {
-      height: 100%;
-    }
-    .el-card {
-      height: 100%;
-      :deep(.el-card__body) {
-        padding: 0.3vw;
-      }
-      :deep(.el-card__header) {
-        padding: 0.6vw;
-      }
-      :deep(.el-card__body) {
-        height: calc(100% - 2.3vw);
-      }
-      :deep(.ele-pro-table) {
-        height: 99%;
-      }
-      :deep(.el-table) {
-        font-size: 0.62vw;
-      }
-    }
-  }
-</style>
+<style lang="scss" scoped></style>

+ 649 - 0
src/views/home/statisticsScreen.vue

@@ -0,0 +1,649 @@
+<template>
+  <div class="ops-dashboard">
+    <!-- 顶部指标卡 -->
+    <div class="metric-row">
+      <div
+        v-for="(item, index) in metrics"
+        :key="index"
+        class="metric-card"
+        :style="{ '--top-color': item.color }"
+      >
+        <div class="metric-label">{{ item.label }}</div>
+        <div class="metric-value">
+          <span class="metric-num" :style="{ color: item.color }">{{ item.value }}</span>
+          <span class="metric-unit">{{ item.unit }}</span>
+        </div>
+        <div class="metric-sub" :style="{ color: item.trendColor }">
+          <i :class="item.trendIcon"></i>
+          <span>{{ item.trend }}</span>
+        </div>
+      </div>
+    </div>
+
+    <!-- 中部图表 -->
+    <div class="middle-row">
+      <div class="chart-card chart-large">
+        <div class="chart-header">
+          <span class="chart-title">产销采月度综合趋势</span>
+          <div class="chart-tools">
+            <span
+              v-for="t in trendTabs"
+              :key="t"
+              :class="['tool-tab', { active: trendTab === t }]"
+              @click="trendTab = t"
+            >{{ t }}</span>
+          </div>
+        </div>
+        <v-chart ref="barRef" :option="barOption" />
+      </div>
+      <div class="chart-card">
+        <div class="chart-header">
+          <span class="chart-title">应收应付资金趋势(近6月)</span>
+        </div>
+        <v-chart ref="lineRef" :option="lineOption" />
+      </div>
+    </div>
+
+    <!-- 信息条 + 环形图 + 风险预警 -->
+    <div class="lower-row">
+      <div class="lower-left">
+        <div class="notice-bar">
+          <span class="notice-tag">提示</span>
+          <span class="notice-text">2026年5月营收达本年峰值3,580万,原材料采购上涨5.2%,产销达成率突破98%</span>
+        </div>
+        <div class="chart-card pie-card">
+          <div class="chart-header">
+            <span class="chart-title">产品营收结构占比</span>
+          </div>
+          <div class="pie-wrapper">
+            <v-chart ref="pieRef" :option="pieOption" />
+          </div>
+          <div class="pie-tip">滞销低毛利产品库存占比10%</div>
+        </div>
+      </div>
+      <div class="lower-right">
+        <div class="risk-card">
+          <div class="chart-header">
+            <span class="chart-title">经营风险预警</span>
+          </div>
+          <div class="risk-list">
+            <div
+              v-for="(risk, index) in risks"
+              :key="index"
+              class="risk-item"
+              :class="risk.level"
+            >
+              <span class="risk-tag">{{ risk.tag }}</span>
+              <span class="risk-text">{{ risk.text }}</span>
+              <span class="risk-link">→跳转</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 底部汇总 -->
+    <div class="summary-row">
+      <div
+        v-for="(section, index) in summaries"
+        :key="index"
+        class="summary-card"
+        :style="{ '--bottom-color': section.color }"
+      >
+        <div class="summary-title">{{ section.title }}</div>
+        <div class="summary-items">
+          <div
+            v-for="(it, i) in section.items"
+            :key="i"
+            class="summary-item"
+          >
+            <div class="summary-num">
+              <span :style="{ color: section.color }">{{ it.value }}</span>
+              <span class="summary-unit">{{ it.unit }}</span>
+            </div>
+            <div class="summary-label">{{ it.label }}</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import { use } from 'echarts/core';
+  import { CanvasRenderer } from 'echarts/renderers';
+  import { BarChart, PieChart, LineChart } from 'echarts/charts';
+  import {
+    GridComponent,
+    TooltipComponent,
+    LegendComponent,
+    TitleComponent,
+    MarkLineComponent
+  } from 'echarts/components';
+  import VChart from 'vue-echarts';
+  import { echartsMixin } from '@/utils/echarts-mixin';
+
+  use([
+    CanvasRenderer,
+    BarChart,
+    PieChart,
+    LineChart,
+    GridComponent,
+    TooltipComponent,
+    LegendComponent,
+    TitleComponent,
+    MarkLineComponent
+  ]);
+
+  export default {
+    components: { VChart },
+    mixins: [echartsMixin(['barRef', 'lineRef', 'pieRef'])],
+    data() {
+      return {
+        trendTab: '近6月',
+        trendTabs: ['近6月', '本年度', '近12月', '自定义'],
+        metrics: [
+          { label: '本期营业收入', value: '3,268.5', unit: '万元', color: '#00d9a6', trend: '12.5% 环比', trendColor: '#00d9a6', trendIcon: 'el-icon-top' },
+          { label: '在手订单总产值', value: '5,842.0', unit: '万元', color: '#f5a623', trend: '待交付', trendColor: '#f5a623', trendIcon: 'el-icon-warning-outline' },
+          { label: '累计采购总成本', value: '1,856.3', unit: '万元', color: '#f5a623', trend: '3.2% 同比', trendColor: '#00d9a6', trendIcon: 'el-icon-bottom' },
+          { label: '应收账款余额', value: '1,245.8', unit: '万元', color: '#ff4d4f', trend: '逾期占比 10%', trendColor: '#ff4d4f', trendIcon: 'el-icon-warning-outline' },
+          { label: '应付账款余额', value: '892.6', unit: '万元', color: '#f5a623', trend: '下月到期 420万', trendColor: '#f5a623', trendIcon: 'el-icon-time' },
+          { label: '月度产销达成率', value: '92.6', unit: '%', color: '#00d9a6', trend: '差 7.4% 达标', trendColor: '#ff4d4f', trendIcon: 'el-icon-minus' }
+        ],
+        risks: [
+          { level: 'high', tag: '高风险', text: '90天以上逾期应收共8笔 合计 326万 | 建议加急回款' },
+          { level: 'warn', tag: '预警', text: '下月到期应付账款缺口 218万 | 建议调整付款周期' },
+          { level: 'warn', tag: '预警', text: '低毛利滞销库存积压 15% | 建议制定清库促销' },
+          { level: 'info', tag: '提示', text: '头部大客户在手订单 产值980万,交付周期60天' }
+        ],
+        summaries: [
+          {
+            title: '销售端全局汇总',
+            color: '#00d9a6',
+            items: [
+              { value: '27', unit: '家', label: '本年新增合作客户' },
+              { value: '412', unit: '笔', label: '全年度订单总数' },
+              { value: '38', unit: '天', label: '平均交付周期' },
+              { value: '3', unit: '家', label: '流失预警客户' }
+            ]
+          },
+          {
+            title: '供应链采购汇总',
+            color: '#f5a623',
+            items: [
+              { value: '39', unit: '家', label: '核心稳定供应商' },
+              { value: '682', unit: '万', label: '纳入采购金额' },
+              { value: '47', unit: '天', label: '平均采购周期' }
+            ]
+          },
+          {
+            title: '财务经营汇总',
+            color: '#00d9a6',
+            items: [
+              { value: '23.7', unit: '%', label: '本年综合毛利率' },
+              { value: '+156', unit: '万', label: '月改善净利润' },
+              { value: '11.2', unit: '%', label: '运营费用占比' }
+            ]
+          }
+        ]
+      };
+    },
+    computed: {
+      barOption() {
+        return {
+          backgroundColor: 'transparent',
+          tooltip: {
+            trigger: 'axis',
+            axisPointer: { type: 'shadow' }
+          },
+          legend: {
+            data: ['营业收入', '采购支出', '产销达成率'],
+            textStyle: { color: '#8fa8c5' },
+            bottom: 0
+          },
+          grid: { left: '2%', right: '2%', bottom: '12%', top: '18%', containLabel: true },
+          xAxis: {
+            type: 'category',
+            data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+            axisLine: { lineStyle: { color: '#2c4a6b' } },
+            axisLabel: { color: '#8fa8c5' }
+          },
+          yAxis: [
+            {
+              type: 'value',
+              name: '金额(万)',
+              nameTextStyle: { color: '#8fa8c5' },
+              axisLine: { show: false },
+              splitLine: { lineStyle: { color: '#1c3a5f' } },
+              axisLabel: { color: '#8fa8c5' }
+            },
+            {
+              type: 'value',
+              name: '达成率',
+              nameTextStyle: { color: '#8fa8c5' },
+              min: 0,
+              max: 100,
+              axisLine: { show: false },
+              splitLine: { show: false },
+              axisLabel: { color: '#8fa8c5', formatter: '{value}%' }
+            }
+          ],
+          series: [
+            {
+              name: '营业收入',
+              type: 'bar',
+              barWidth: 22,
+              itemStyle: { color: '#00d9a6' },
+              data: [3200, 2800, 3500, 3600, 3900, 4200]
+            },
+            {
+              name: '采购支出',
+              type: 'bar',
+              barWidth: 22,
+              itemStyle: { color: '#f5a623' },
+              data: [1800, 1600, 1900, 2000, 2100, 2300]
+            },
+            {
+              name: '产销达成率',
+              type: 'line',
+              yAxisIndex: 1,
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              itemStyle: { color: '#ff4d4f' },
+              lineStyle: { width: 2 },
+              data: [82, 85, 88, 86, 90, 92]
+            }
+          ]
+        };
+      },
+      lineOption() {
+        return {
+          backgroundColor: 'transparent',
+          tooltip: { trigger: 'axis' },
+          legend: {
+            data: ['应收账款', '应付账款', '安全阈值'],
+            textStyle: { color: '#8fa8c5' },
+            bottom: 0
+          },
+          grid: { left: '2%', right: '2%', bottom: '12%', top: '18%', containLabel: true },
+          xAxis: {
+            type: 'category',
+            data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+            axisLine: { lineStyle: { color: '#2c4a6b' } },
+            axisLabel: { color: '#8fa8c5' }
+          },
+          yAxis: [
+            {
+              type: 'value',
+              name: '金额(万)',
+              nameTextStyle: { color: '#8fa8c5' },
+              min: 0,
+              max: 1500,
+              axisLine: { show: false },
+              splitLine: { lineStyle: { color: '#1c3a5f' } },
+              axisLabel: { color: '#8fa8c5' }
+            },
+            {
+              type: 'value',
+              name: '占比',
+              min: 0,
+              max: 100,
+              nameTextStyle: { color: '#8fa8c5' },
+              axisLine: { show: false },
+              splitLine: { show: false },
+              axisLabel: { color: '#8fa8c5', formatter: '{value}%' }
+            }
+          ],
+          series: [
+            {
+              name: '应收账款',
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              itemStyle: { color: '#00d9a6' },
+              lineStyle: { width: 2 },
+              data: [980, 1050, 1120, 1180, 1250, 1245]
+            },
+            {
+              name: '应付账款',
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              itemStyle: { color: '#f5a623' },
+              lineStyle: { width: 2 },
+              data: [760, 780, 800, 850, 880, 892]
+            },
+            {
+              name: '安全阈值',
+              type: 'line',
+              smooth: true,
+              symbol: 'circle',
+              symbolSize: 6,
+              itemStyle: { color: '#ff4d4f' },
+              lineStyle: { width: 2, type: 'dashed' },
+              data: [820, 850, 880, 900, 930, 950]
+            }
+          ]
+        };
+      },
+      pieOption() {
+        return {
+          backgroundColor: 'transparent',
+          tooltip: { trigger: 'item' },
+          legend: {
+            orient: 'vertical',
+            right: '5%',
+            top: 'center',
+            textStyle: { color: '#8fa8c5' }
+          },
+          series: [
+            {
+              name: '营收结构',
+              type: 'pie',
+              radius: ['48%', '72%'],
+              center: ['32%', '50%'],
+              avoidLabelOverlap: false,
+              label: { show: false },
+              labelLine: { show: false },
+              data: [
+                { value: 35, name: '核心产品A', itemStyle: { color: '#00d9a6' } },
+                { value: 18, name: '核心产品B', itemStyle: { color: '#409eff' } },
+                { value: 8, name: '核心产品C', itemStyle: { color: '#9254de' } },
+                { value: 23, name: '普通产品', itemStyle: { color: '#f5a623' } },
+                { value: 16, name: '滞销产品', itemStyle: { color: '#ff4d4f' } }
+              ]
+            },
+            {
+              type: 'pie',
+              radius: ['48%', '72%'],
+              center: ['32%', '50%'],
+              label: {
+                show: true,
+                position: 'center',
+                formatter: '{title|核心产品}\n{val|61%}',
+                rich: {
+                  title: { fontSize: 12, color: '#8fa8c5', lineHeight: 24 },
+                  val: { fontSize: 28, fontWeight: 'bold', color: '#00d9a6' }
+                }
+              },
+              data: [{ value: 1, itemStyle: { color: 'transparent' } }],
+              silent: true,
+              z: 10
+            }
+          ]
+        };
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .ops-dashboard {
+    width: 100%;
+    min-height: 100vh;
+    background: #070d1a;
+    color: #fff;
+    padding: 16px;
+    box-sizing: border-box;
+    font-family: 'Microsoft YaHei', sans-serif;
+  }
+
+  .metric-row {
+    display: grid;
+    grid-template-columns: repeat(6, 1fr);
+    gap: 14px;
+    margin-bottom: 16px;
+  }
+
+  .metric-card {
+    background: linear-gradient(180deg, #0f1a30 0%, #0a1224 100%);
+    border: 1px solid #162a45;
+    border-top: 3px solid var(--top-color);
+    border-radius: 6px;
+    padding: 16px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
+    .metric-label {
+      font-size: 13px;
+      color: #8fa8c5;
+      margin-bottom: 10px;
+    }
+    .metric-value {
+      font-size: 28px;
+      font-weight: bold;
+      margin-bottom: 10px;
+      .metric-num {
+        font-weight: bold;
+      }
+      .metric-unit {
+        font-size: 13px;
+        color: #8fa8c5;
+        font-weight: normal;
+        margin-left: 4px;
+      }
+    }
+    .metric-sub {
+      font-size: 12px;
+      i {
+        margin-right: 4px;
+      }
+    }
+  }
+
+  .middle-row {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 14px;
+    margin-bottom: 14px;
+    height: 340px;
+  }
+
+  .lower-row {
+    display: grid;
+    // grid-template-columns: 1.3fr 2fr;
+    grid-template-columns: 1fr 1fr;
+    gap: 14px;
+    height: 350px;
+  }
+
+  .lower-left {
+    display: flex;
+    flex-direction: column;
+    gap: 14px;
+  }
+
+  .lower-right {
+    height: 100%;
+  }
+
+  .notice-bar {
+    background: linear-gradient(180deg, #0f1a30 0%, #0a1224 100%);
+    border: 1px solid #162a45;
+    border-radius: 6px;
+    padding: 14px 18px;
+    display: flex;
+    align-items: center;
+    color: #c5d4e8;
+    font-size: 13px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
+    flex-shrink: 0;
+    .notice-tag {
+      display: inline-block;
+      padding: 3px 8px;
+      border-radius: 3px;
+      font-size: 12px;
+      color: #fff;
+      background: #00d9a6;
+      margin-right: 10px;
+      flex-shrink: 0;
+    }
+    .notice-text {
+      flex: 1;
+    }
+  }
+
+  .risk-card {
+    background: linear-gradient(180deg, #0f1a30 0%, #0a1224 100%);
+    border: 1px solid #162a45;
+    border-radius: 6px;
+    padding: 14px;
+    display: flex;
+    flex-direction: column;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
+    height: 100%;
+    .risk-list {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      gap: 8px;
+      .risk-item {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        padding: 9px 12px;
+        background: rgba(255, 255, 255, 0.03);
+        border-radius: 4px;
+        border-left: 3px solid transparent;
+        &.high {
+          border-left-color: #ff4d4f;
+          .risk-tag { background: #ff4d4f; }
+        }
+        &.warn {
+          border-left-color: #f5a623;
+          .risk-tag { background: #f5a623; }
+        }
+        &.info {
+          border-left-color: #00d9a6;
+          .risk-tag { background: #00d9a6; }
+        }
+        .risk-tag {
+          padding: 2px 7px;
+          border-radius: 3px;
+          font-size: 11px;
+          color: #fff;
+          flex-shrink: 0;
+        }
+        .risk-text {
+          flex: 1;
+          font-size: 12px;
+          color: #c5d4e8;
+        }
+        .risk-link {
+          font-size: 12px;
+          color: #00d9a6;
+          cursor: pointer;
+          flex-shrink: 0;
+        }
+      }
+    }
+  }
+
+  .pie-card {
+    background: linear-gradient(180deg, #0f1a30 0%, #0a1224 100%);
+    border: 1px solid #162a45;
+    border-radius: 6px;
+    padding: 14px;
+    display: flex;
+    flex-direction: column;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
+    flex: 1;
+    position: relative;
+    .pie-wrapper {
+      flex: 1;
+    }
+    .pie-tip {
+      font-size: 12px;
+      color: #f5a623;
+      text-align: left;
+      margin-top: 4px;
+    }
+  }
+
+  .summary-row {
+    display: grid;
+    grid-template-columns: repeat(3, 1fr);
+    gap: 14px;
+    margin-top: 14px;
+  }
+
+  .chart-card {
+    background: linear-gradient(180deg, #0f1a30 0%, #0a1224 100%);
+    border: 1px solid #162a45;
+    border-radius: 6px;
+    padding: 14px;
+    display: flex;
+    flex-direction: column;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
+    .chart-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 10px;
+      .chart-title {
+        font-size: 15px;
+        font-weight: bold;
+        color: #fff;
+      }
+      .chart-tools {
+        display: flex;
+        gap: 8px;
+        .tool-tab {
+          padding: 4px 10px;
+          border-radius: 4px;
+          font-size: 12px;
+          color: #8fa8c5;
+          background: rgba(255, 255, 255, 0.05);
+          cursor: pointer;
+          &.active {
+            background: rgba(0, 217, 166, 0.2);
+            color: #00d9a6;
+          }
+        }
+      }
+    }
+    .echarts {
+      flex: 1;
+    }
+  }
+
+  .summary-card {
+    background: linear-gradient(180deg, #0f1a30 0%, #0a1224 100%);
+    border: 1px solid #162a45;
+    border-bottom: 3px solid var(--bottom-color);
+    border-radius: 6px;
+    padding: 14px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
+    .summary-title {
+      font-size: 14px;
+      font-weight: bold;
+      color: #fff;
+      margin-bottom: 14px;
+    }
+    .summary-items {
+      display: flex;
+      justify-content: space-between;
+      .summary-item {
+        text-align: center;
+        .summary-num {
+          font-size: 22px;
+          font-weight: bold;
+          .summary-unit {
+            font-size: 12px;
+            color: #8fa8c5;
+            margin-left: 2px;
+          }
+        }
+        .summary-label {
+          font-size: 12px;
+          color: #8fa8c5;
+          margin-top: 6px;
+        }
+      }
+    }
+  }
+</style>