Просмотр исходного кода

feat: 添加PCS数据可视化大屏页面

yusheng 3 месяцев назад
Родитель
Сommit
9f6bf7bc89

+ 52 - 0
package-lock.json

@@ -24,6 +24,7 @@
         "element-china-category-data": "^1.0.4",
         "element-ui": "2.15.7",
         "github-markdown-css": "^5.1.0",
+        "import": "^0.0.6",
         "jsbarcode": "^3.11.5",
         "json-bigint": "^1.0.0",
         "nprogress": "^0.2.0",
@@ -8436,6 +8437,20 @@
       "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
       "dev": true
     },
+    "node_modules/import": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmmirror.com/import/-/import-0.0.6.tgz",
+      "integrity": "sha512-QPhTdjy9J4wUzmWSG7APkSgMFuPGPw+iJTYUblcfc2AfpqaatbwgCldK1HoLYx+v/+lWvab63GWZtNkcnj9JcQ==",
+      "dependencies": {
+        "optimist": "0.3.x"
+      },
+      "bin": {
+        "import": "import"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/import-fresh": {
       "version": "3.3.0",
       "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -11014,6 +11029,14 @@
         "opener": "bin/opener-bin.js"
       }
     },
+    "node_modules/optimist": {
+      "version": "0.3.7",
+      "resolved": "https://registry.npmmirror.com/optimist/-/optimist-0.3.7.tgz",
+      "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==",
+      "dependencies": {
+        "wordwrap": "~0.0.2"
+      }
+    },
     "node_modules/optionator": {
       "version": "0.9.3",
       "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz",
@@ -16204,6 +16227,14 @@
         "node": ">=0.8"
       }
     },
+    "node_modules/wordwrap": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmmirror.com/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/wrap-ansi": {
       "version": "7.0.0",
       "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -23172,6 +23203,14 @@
       "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
       "dev": true
     },
+    "import": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmmirror.com/import/-/import-0.0.6.tgz",
+      "integrity": "sha512-QPhTdjy9J4wUzmWSG7APkSgMFuPGPw+iJTYUblcfc2AfpqaatbwgCldK1HoLYx+v/+lWvab63GWZtNkcnj9JcQ==",
+      "requires": {
+        "optimist": "0.3.x"
+      }
+    },
     "import-fresh": {
       "version": "3.3.0",
       "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -25246,6 +25285,14 @@
       "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
       "dev": true
     },
+    "optimist": {
+      "version": "0.3.7",
+      "resolved": "https://registry.npmmirror.com/optimist/-/optimist-0.3.7.tgz",
+      "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==",
+      "requires": {
+        "wordwrap": "~0.0.2"
+      }
+    },
     "optionator": {
       "version": "0.9.3",
       "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz",
@@ -29358,6 +29405,11 @@
       "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
       "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="
     },
+    "wordwrap": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmmirror.com/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw=="
+    },
     "wrap-ansi": {
       "version": "7.0.0",
       "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
     "element-china-category-data": "^1.0.4",
     "element-ui": "2.15.7",
     "github-markdown-css": "^5.1.0",
+    "import": "^0.0.6",
     "jsbarcode": "^3.11.5",
     "json-bigint": "^1.0.0",
     "nprogress": "^0.2.0",

+ 40 - 0
public/flexible.js

@@ -0,0 +1,40 @@
+// rem 适配方案 - 针对大屏可视化优化
+(function flexible() {
+  const docEl = document.documentElement;
+  const dpr = window.devicePixelRatio || 1;
+
+  // 设置 data-dpr 属性,用于 css 适配
+  docEl.setAttribute('data-dpr', dpr);
+
+  function setRem() {
+    // 设计稿宽度 1920px,基准 font-size: 16px
+    // 1rem = 16px
+    const baseWidth = 1920;
+    const baseFontSize = 16;
+    
+    // 获取当前视口宽度
+    const clientWidth = docEl.clientWidth || window.innerWidth;
+    
+    // 计算 rem 基准值 - 保持比例
+    // 最小宽度 1280px,最大不限制
+    const width = Math.max(clientWidth, 1280);
+    const rem = (width / baseWidth) * baseFontSize;
+    
+    // 设置根字体大小
+    docEl.style.fontSize = rem + 'px';
+  }
+
+  // 初始化
+  setRem();
+
+  // 监听窗口变化
+  window.addEventListener('resize', setRem);
+  window.addEventListener('orientationchange', setRem);
+
+  // 页面显示时重新计算
+  document.addEventListener('visibilitychange', function() {
+    if (!document.hidden) {
+      setRem();
+    }
+  });
+})();

+ 1 - 0
public/index.html

@@ -6,6 +6,7 @@
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
   <link rel="icon" href="<%= BASE_URL %>logo.png">
   <title><%= process.env.VUE_APP_NAME %></title>
+  <script src="<%= BASE_URL %>flexible.js"></script>
   <style>
     .ele-admin-loading {
       width: 36px;

+ 59 - 0
src/api/pcsData/index.js

@@ -0,0 +1,59 @@
+import request from '@/utils/request';
+
+//告警数据概览 - 环形图
+export async function alarmOverview(params) {
+  const res = await request.get(`/pcs/homePage/alarmOverview`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//告警列表
+export async function queryAlarmList(params) {
+  const res = await request.get(`/pcs/homePage/queryAlarmList`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//设备告警趋势图
+export async function deviceAlarmTrend(params) {
+  const res = await request.get(`/pcs/homePage/deviceAlarmTrend`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//综合数据;
+export async function comprehensiveData(params) {
+  const res = await request.get(`/pcs/homePage/comprehensiveData`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//用水用煤趋势图(trendType: 1-用水, 2-用煤);
+export async function waterCoalTrend(params) {
+  const res = await request.get(`/pcs/homePage/waterCoalTrend`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//固废趋势图;
+export async function wasteTrend(params) {
+  const res = await request.get(`/pcs/homePage/wasteTrend`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+// 获取车间下的所有产线
+export async function listFactoryLine(params) {
+  const res = await request.get(`/main/factoryarea/page`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

BIN
src/assets/pcs/back.png


BIN
src/assets/pcs/header.png


BIN
src/assets/pcs/home.png


BIN
src/assets/pcs/icon.png


BIN
src/assets/pcs/leftItem.png


BIN
src/assets/pcs/select.png


BIN
src/assets/pcs/titleIcon.png


BIN
src/assets/pcs/titleb.png


+ 1460 - 0
src/views/bpm/vis-page/pcsData/index copy.vue

@@ -0,0 +1,1460 @@
+<template>
+  <vue-fullscreen
+    class="box-container"
+    v-cloak
+    v-model="isFullscreen"
+    fullscreenClass="box-container"
+    :exit-on-click-wrapper="false"
+  >
+    <div class="box-container" v-cloak>
+      <!-- 中间内容区域 - 可左右滚动,头部跟随 -->
+      <div class="box-content">
+        <div class="scroll-wrapper">
+          <!-- 头部区域 - 跟随滚动 -->
+          <div class="header-section">
+            <div class="box-header-scroll">
+              <div class="logo">
+                <el-select
+                  v-model="stationId"
+                  placeholder="请选择"
+                  class="custom-select"
+                  popper-class="custom-select-dropdown"
+                  @change="init"
+                >
+                  <el-option
+                    v-for="item in selectOptions"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  />
+                </el-select>
+              </div>
+              <div class="header-center">
+                <div class="title">克拉玛依宇信工业科技股份有限公司</div>
+              </div>
+              <div class="time">
+                <div class="time-display">
+                  <span class="date-text">{{ date }}</span>
+                  <span class="time-text">{{ time }}</span>
+                </div>
+                <span class="fullscreen-toggle" @click.passive="onFullscreen">
+                  <i
+                    v-if="isFullscreen"
+                    title="取消全屏"
+                    class="el-icon-_screen-restore"
+                  />
+                  <i v-else title="全屏" class="el-icon-_screen-full" />
+                </span>
+              </div>
+            </div>
+          </div>
+
+          <!-- 三栏布局区域 -->
+          <div class="three-column-layout">
+            <!-- 左侧区域 -->
+            <div class="left-section">
+              <!-- 综合数据 -->
+              <div class="panel data-panel">
+                <div class="panel-title">
+                  <div class="panel-icon"></div>
+                  综合数据
+                </div>
+                <div
+                  style="
+                    display: flex;
+                    flex: 1;
+                    align-items: center;
+                    justify-content: center;
+                  "
+                >
+                  <div class="data-grid">
+                    <div
+                      class="data-item"
+                      v-for="(item, index) in comprehensiveList"
+                      :key="index"
+                    >
+                      <div class="img"></div>
+                      <div class="data-item_value">
+                        <span class="data-label">{{ item.label }}</span>
+                        <span class="data-value">{{ item.value }}</span></div
+                      >
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 区域用能对比 -->
+              <div class="panel chart-panel" style="position: relative">
+                <div class="panel-title energy-title">
+                  <div class="title-left">
+                    <div class="panel-icon"></div>
+                    用水用煤趋势图
+                  </div>
+                </div>
+                <div class="energy-tabs">
+                  <span
+                    v-for="tab in energyTabs"
+                    :key="tab.value"
+                    :class="[
+                      'tab-item',
+                      { active: activeEnergyTab === tab.value }
+                    ]"
+                    @click="energyTabsCahnge(tab.value)"
+                  >
+                    {{ tab.label }}
+                  </span>
+                </div>
+                <div class="chart-container" ref="waterChart"></div>
+              </div>
+
+              <!-- 区域能耗对比 -->
+              <div class="panel chart-panel">
+                <div class="panel-title energy-title">
+                  <div class="title-left">
+                    <div class="panel-icon"></div>
+                    固废量趋势图
+                  </div>
+                </div>
+                <div class="chart-container" ref="returnChart"></div>
+              </div>
+            </div>
+
+            <!-- 中间区域 -->
+            <div class="center-section">
+              <!-- 锅炉运行参数 -->
+              <div
+                :class="'param-card param-card' + (index + 1)"
+                v-for="(boiler, index) in boilerData"
+                :key="index"
+              >
+                <div class="param-title">{{ boiler.title }}</div>
+                <div class="param-list">
+                  <div
+                    class="param-item"
+                    v-for="(param, pIndex) in boiler.params"
+                    :key="pIndex"
+                  >
+                    <span class="param-name">{{ param.name }}</span>
+                    <span class="param-value">{{ param.value }}</span>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 管道流程图 -->
+              <div class="pipeline-container">
+                <!-- 管道流程图内容 -->
+              </div>
+            </div>
+
+            <!-- 右侧区域 -->
+            <div class="right-section">
+              <!-- 告警数据统计 -->
+              <div class="panel alert-panel">
+                <div class="panel-title">
+                  <divi class="panel-icon"></divi>
+                  告警数据概览
+                </div>
+                <div class="alert-stats">
+                  <!-- 左侧数据卡片 -->
+                  <div class="alert-cards">
+                    <div
+                      class="alert-card-item"
+                      v-for="(item, index) in alertStatsData"
+                      :key="index"
+                    >
+                      <div class="img"></div>
+                      <div class="data-item_value">
+                        <span class="data-label">{{ item.label }}</span>
+                        <span class="data-value">{{ item.value }}</span></div
+                      >
+                    </div>
+                  </div>
+                  <!-- 右侧环形图 -->
+                  <div class="alert-pie-chart">
+                    <div class="alert-pie" ref="alertPieChart"></div>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 告警列表 -->
+              <div class="panel alert-list-panel">
+                <div class="panel-title">
+                  <div class="panel-icon"></div>
+                  告警列表
+                </div>
+                <div class="alert-table">
+                  <dv-scroll-board
+                    :config="alertConfig"
+                    style="width: 100%; height: calc(100% - 40px)"
+                  />
+                </div>
+              </div>
+
+              <!-- 设备告警趋势图 -->
+              <div class="panel chart-panel">
+                <div class="panel-title">
+                  <div class="panel-icon"></div>
+                  设备告警趋势图
+                </div>
+                <div class="chart-container" ref="alertTrendChart"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </vue-fullscreen>
+</template>
+
+<script>
+  import { component } from 'vue-fullscreen';
+  import * as echarts from 'echarts';
+  import {
+    alarmOverview,
+    queryAlarmList,
+    deviceAlarmTrend,
+    comprehensiveData,
+    waterCoalTrend,
+    wasteTrend,
+    listFactoryLine
+  } from '@/api/pcsData/index.js';
+
+  export default {
+    name: 'PcsDataDashboard',
+    components: {
+      VueFullscreen: component
+    },
+    data() {
+      return {
+        isFullscreen: false,
+        isFlag: false,
+        date: '',
+        time: '',
+        week: '',
+        selectedValue: '',
+        selectOptions: [],
+        comprehensiveList: [],
+        boilerData: [
+          {
+            title: '锅炉运行参数',
+            params: []
+          },
+          {
+            title: '注气检查记录',
+            params: []
+          },
+          {
+            title: '蒸汽质量检查记录',
+            params: []
+          }
+        ],
+        alertStatsData: [],
+        alertLegend: [
+          { name: '一般告警', percent: 45.2, color: '#3b82f6' },
+          { name: '警告告警', percent: 35.3, color: '#f59e0b' },
+          { name: '严重告警', percent: 4.7, color: '#ef4444', value: 58 },
+          { name: '紧急告警', percent: 15.2, color: '#ef4444' }
+        ],
+        alertConfig: {},
+        unitValue: '',
+        unitOptions: [
+          { label: '1#机组', value: '1' },
+          { label: '2#机组', value: '2' },
+          { label: '3#机组', value: '3' },
+          { label: '4#机组', value: '4' }
+        ],
+        energyTabs: [
+          { label: '水', value: '1' },
+          { label: '煤', value: '2' }
+        ],
+        activeEnergyTab: '1',
+        charts: {},
+        stationId: ''
+      };
+    },
+    watch: {
+      isFullscreen: {
+        handler() {
+          this.handleResize();
+        },
+        immediate: true
+      }
+    },
+    created() {
+      this.init();
+      listFactoryLine({
+        pageNum: 1,
+        size: 999,
+        type: 4
+      }).then((res) => {
+        this.selectOptions = res.list.map((item) => {
+          return {
+            label: item.name,
+            value: item.id
+          };
+        });
+        console.log(res);
+      });
+    },
+    mounted() {
+      this.updateTimer = setInterval(this.updateTime, 1000);
+
+      window.addEventListener('resize', this.handleResize);
+      // 初始化时同步头部宽度
+      this.$nextTick(() => {
+        this.syncHeaderWidth();
+      });
+    },
+    beforeDestroy() {
+      clearInterval(this.updateTimer);
+      window.removeEventListener('resize', this.handleResize);
+      Object.values(this.charts).forEach((chart) => chart && chart.dispose());
+    },
+    methods: {
+      energyTabsCahnge(val) {
+        this.activeEnergyTab = val;
+        this.waterCoalTrend();
+      },
+
+      init() {
+        this.alarmOverview();
+        this.queryAlarmList();
+        this.deviceAlarmTrend();
+        this.waterCoalTrend();
+        this.wasteTrend();
+        this.comprehensiveData();
+      },
+      //告警数据概览
+      alarmOverview() {
+        alarmOverview({
+          stationId: this.stationId
+        }).then((res) => {
+          this.$nextTick(() => {
+            this.initAlertPieChart(res);
+          });
+        });
+      },
+      //设备告警趋势图
+      deviceAlarmTrend() {
+        deviceAlarmTrend({ stationId: this.stationId }).then((res) => {
+          this.$nextTick(() => {
+            this.initAlertTrendChart(res);
+          });
+        });
+      },
+      //综合数据
+      comprehensiveData() {
+        comprehensiveData({ stationId: this.stationId }).then((res) => {
+          this.comprehensiveList = [
+            { label: '年度总注气量(M³)', value: res.yearTotalGasInjection },
+            { label: '年度总水量(T)', value: res.yearTotalWater },
+            { label: '年度用煤总量(T)', value: res.yearTotalCoal },
+            { label: '年度固废量(T)', value: res.yearTotalWaste },
+            { label: '本月注气量(M³)', value: res.monthTotalGasInjection },
+            { label: '本月水量(T)', value: res.monthTotalWater },
+            { label: '本月用煤量(T)', value: res.monthTotalCoal },
+            { label: '本月固废量(T)', value: res.monthTotalWaste }
+          ];
+          this.$set(
+            this.boilerData[0],
+            'params',
+            this.getParams(res['dailyReportOnBoilerOperation'] || {})
+          );
+          this.$set(
+            this.boilerData[0],
+            'params',
+            this.getParams(res['gasInjectionInspectionRecord'] || {})
+          );
+          this.$set(
+            this.boilerData[0],
+            'params',
+            this.getParams(res['steamQualityInspectionRecord'] || {})
+          );
+        });
+      },
+      getParams(data = {}) {
+        let list = [
+          { name: '报工人:', value: data?.executeUsers?.userName },
+          { name: '报工时间:', value: data.checkFinishTime }
+        ];
+        data.detailList?.forEach((item) => {
+          list.push({
+            name: item.paramValue,
+            value: item.num,
+            unit: item.unitName
+          });
+        });
+        return list;
+      },
+      //用煤用水
+      waterCoalTrend() {
+        waterCoalTrend({
+          trendType: this.activeEnergyTab,
+          stationId: this.stationId
+        }).then((res) => {
+          this.$nextTick(() => {
+            this.initWaterChart(res);
+          });
+        });
+      },
+      //固废趋势图
+      wasteTrend() {
+        wasteTrend({ stationId: this.stationId }).then((res) => {
+          this.$nextTick(() => {
+            this.initReturnChart(res);
+          });
+        });
+      },
+      //告警列表
+      queryAlarmList() {
+        let dataKey = [
+          'name',
+          'deviceLocation',
+          'alertLevel',
+          'ruleName',
+          'alarmTime',
+          'handleStatus'
+        ];
+
+        queryAlarmList({ stationId: this.stationId }).then((res) => {
+          let dataArr = [];
+          res.forEach((item, index) => {
+            let data = [index + 1];
+            dataKey.forEach((key, index) => {
+              if (key == 'alertLevel') {
+                item[key] =
+                  item[key] == 1
+                    ? '紧急'
+                    : item[key] == 2
+                    ? '严重'
+                    : item[key] == 3
+                    ? '一般'
+                    : '告警';
+              }
+              if (key == 'handleStatus') {
+                item[key] =
+                  item[key] == 1
+                    ? '处理中'
+                    : item[key] == 2
+                    ? '已处理'
+                    : '未处理';
+              }
+              data.push(item[key]);
+            });
+            dataArr.push(data);
+          });
+          this.alertConfig = {
+            header: [
+              '序号',
+              '设备名称',
+              '设备位置',
+              '告警级别',
+              '告警内容描述',
+              '告警时间',
+              '处理状态'
+            ].map(
+              (item) =>
+                `<div style="color: #38fdf8;font-size: 0.9rem;font-weight: bold">${item}</div>`
+            ),
+
+            data: dataArr.map((data) =>
+              data.map((item, index) => {
+                return `<div style="color: ${
+                  index == 3 ? this.getClolr(item) : '#fff'
+                };font-size: 0.9rem;">${item}</div>`;
+              })
+            ),
+            rowNum: 4,
+            headerBGC: 'transparent',
+            oddRowBGC: 'rgba(13, 41, 96, 0.3)',
+            evenRowBGC: 'rgba(13, 41, 96, 0.1)',
+            headerHeight: 30,
+            waitTime: 3000,
+            align: [
+              'center',
+              'center',
+              'center',
+              'center',
+              'center',
+              'center',
+              'center'
+            ],
+            columnWidth: []
+          };
+        });
+      },
+
+      onFullscreen() {
+        this.isFullscreen = !this.isFullscreen;
+      },
+      getClolr(item) {
+        return item == '严重'
+          ? '#FF0000'
+          : item == '紧急'
+          ? '#FF7070'
+          : item == '警告'
+          ? '#FFD337'
+          : '#fff';
+      },
+      updateTime() {
+        const now = new Date();
+        const hours = now.getHours().toString().padStart(2, '0');
+        const minutes = now.getMinutes().toString().padStart(2, '0');
+        const seconds = now.getSeconds().toString().padStart(2, '0');
+        this.time = `${hours}:${minutes}:${seconds}`;
+
+        const year = now.getFullYear();
+        const month = (now.getMonth() + 1).toString().padStart(2, '0');
+        const day = now.getDate().toString().padStart(2, '0');
+        this.date = `${year}-${month}-${day}`;
+      },
+      syncHeaderWidth() {
+        this.$nextTick(() => {
+          const threeColumnLayout = document.querySelector(
+            '.three-column-layout'
+          );
+          const headerSection = document.querySelector('.header-section');
+          if (threeColumnLayout && headerSection) {
+            const layoutWidth = threeColumnLayout.scrollWidth;
+            headerSection.style.width = layoutWidth + 'px';
+          }
+        });
+      },
+      handleResize() {
+        this.$nextTick(() => {
+          // 同步头部宽度
+          this.syncHeaderWidth();
+
+          const { clientWidth, clientHeight } = document.documentElement;
+          const containers = document.getElementsByClassName('box-container');
+          Array.from(containers).forEach((item) => {
+            if (this.isFullscreen) {
+              item.style.height = clientHeight + 'px';
+              item.style.width = clientWidth + 'px';
+            } else {
+              item.style.height = clientHeight - 100 + 'px';
+              item.style.width = clientWidth - 240 + 'px';
+            }
+          });
+          Object.values(this.charts).forEach(
+            (chart) => chart && chart.resize()
+          );
+        });
+      },
+
+      initWaterChart(res) {
+        if (!this.$refs.waterChart) return;
+        console.log(res);
+
+        const chart = echarts.init(this.$refs.waterChart);
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            backgroundColor: '#fff',
+            borderColor: '#e0e0e0',
+            borderWidth: 1,
+            textStyle: {
+              color: '#333'
+            },
+            axisPointer: {
+              type: 'line',
+              lineStyle: {
+                color: 'rgba(255, 255, 255, 0.5)'
+              }
+            }
+          },
+          grid: { top: 30, right: 20, bottom: 30, left: 50 },
+          xAxis: {
+            type: 'category',
+            data: res.map((item) => item.time),
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' }
+          },
+          yAxis: {
+            type: 'value',
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' },
+            splitLine: { lineStyle: { color: 'rgba(74, 111, 165, 0.2)' } }
+          },
+          series: [
+            {
+              data: res.map((item) => item.value),
+              type: 'line',
+              smooth: true,
+              areaStyle: {
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: 'rgba(59, 130, 246, 0.5)' },
+                  { offset: 1, color: 'rgba(59, 130, 246, 0.05)' }
+                ])
+              },
+              lineStyle: { color: '#3b82f6', width: 2 },
+              itemStyle: { color: '#3b82f6' }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.water = chart;
+      },
+      initReturnChart(res) {
+        if (!this.$refs.returnChart) return;
+        const chart = echarts.init(this.$refs.returnChart);
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            backgroundColor: '#fff',
+            borderColor: '#e0e0e0',
+            borderWidth: 1,
+            textStyle: {
+              color: '#333'
+            },
+            axisPointer: {
+              type: 'line',
+              lineStyle: {
+                color: 'rgba(255, 255, 255, 0.5)'
+              }
+            }
+          },
+          grid: { top: 30, right: 20, bottom: 30, left: 50 },
+          xAxis: {
+            type: 'category',
+            data: res.map((item) => item.time),
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' }
+          },
+          yAxis: {
+            type: 'value',
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' },
+            splitLine: { lineStyle: { color: 'rgba(74, 111, 165, 0.2)' } }
+          },
+          series: [
+            {
+              data: res.map((item) => item.value),
+              type: 'line',
+              smooth: true,
+              areaStyle: {
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: 'rgba(59, 130, 246, 0.5)' },
+                  { offset: 1, color: 'rgba(59, 130, 246, 0.05)' }
+                ])
+              },
+              lineStyle: { color: '#3b82f6', width: 2 },
+              itemStyle: { color: '#3b82f6' }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.return = chart;
+      },
+      initAlertPieChart(res) {
+        this.alertStatsData = [
+          {
+            label: '系统告警总数',
+            value: res.yearTotalCount,
+            icon: 'el-icon-_alert'
+          },
+          {
+            label: '本月告警数',
+            value: res.monthTotalCount,
+            icon: 'el-icon-_month'
+          },
+          {
+            label: '本年已处理数',
+            value: res.yearHandledCount,
+            icon: 'el-icon-_check'
+          },
+          {
+            label: '本月已处理数',
+            value: res.monthHandledCount,
+            icon: 'el-icon-_check'
+          },
+          {
+            label: '年度未处理数',
+            value: res.yearUnhandledCount,
+            icon: 'el-icon-_warning'
+          },
+          {
+            label: '本月未处理数',
+            value: res.monthUnhandledCount,
+            icon: 'el-icon-_warning'
+          }
+        ];
+        if (!this.$refs.alertPieChart) return;
+        const chart = echarts.init(this.$refs.alertPieChart);
+
+        // 计算总数
+        const total =
+          res?.levelStats?.reduce((sum, item) => sum + item.count, 0) || 1247;
+
+        const option = {
+          tooltip: {
+            trigger: 'item',
+            formatter: '{b}: {c} ({d}%)'
+          },
+          series: [
+            {
+              type: 'pie',
+              radius: ['45%', '65%'],
+              center: ['50%', '50%'],
+              avoidLabelOverlap: true,
+              itemStyle: {
+                borderRadius: 4,
+                borderColor: '#0d1f4a',
+                borderWidth: 2
+              },
+              data: res?.levelStats?.map((item) => {
+                return {
+                  value: item.count,
+                  name: item.levelName,
+                  itemStyle: {
+                    color:
+                      item.level == 1
+                        ? '#ef4444'
+                        : item.level == 2
+                        ? '#dc2626'
+                        : item.level == 3
+                        ? '#3b82f6'
+                        : '#f59e0b'
+                  }
+                };
+              }) || [
+                {
+                  value: 564,
+                  name: '一般告警',
+                  itemStyle: { color: '#3b82f6' }
+                },
+                {
+                  value: 440,
+                  name: '警告告警',
+                  itemStyle: { color: '#f59e0b' }
+                },
+                {
+                  value: 58,
+                  name: '严重告警',
+                  itemStyle: { color: '#dc2626' }
+                },
+                {
+                  value: 185,
+                  name: '紧急告警',
+                  itemStyle: { color: '#ef4444' }
+                }
+              ],
+              label: {
+                show: true,
+                position: 'outside',
+                formatter: '{per|{d}%}\n{name|{b}}',
+                rich: {
+                  per: {
+                    fontSize: 14,
+                    fontWeight: 'bold',
+                    color: '#fff',
+                    padding: [0, 0, 4, 0]
+                  },
+                  name: {
+                    fontSize: 12,
+                    color: '#8ba3c7'
+                  }
+                }
+              },
+              labelLine: {
+                show: true,
+                length: 10,
+                length2: 10,
+                lineStyle: {
+                  color: 'rgba(138, 163, 199, 0.5)'
+                }
+              },
+              emphasis: {
+                scale: true,
+                scaleSize: 5,
+                label: {
+                  show: true,
+                  fontWeight: 'bold'
+                }
+              }
+            }
+          ],
+          graphic: [
+            {
+              type: 'text',
+              left: 'center',
+              top: '45%',
+              style: {
+                text: String(total),
+                textAlign: 'center',
+                fill: '#fff',
+                fontSize: 24,
+                fontWeight: 'bold'
+              }
+            },
+            {
+              type: 'text',
+              left: 'center',
+              top: '60%',
+              style: {
+                text: '告警总数',
+                textAlign: 'center',
+                fill: '#8ba3c7',
+                fontSize: 11
+              }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.alertPie = chart;
+      },
+      initAlertTrendChart(res) {
+        if (!this.$refs.alertTrendChart) return;
+        const chart = echarts.init(this.$refs.alertTrendChart);
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            backgroundColor: '#fff',
+            borderColor: '#e0e0e0',
+            borderWidth: 1,
+            textStyle: {
+              color: '#333'
+            },
+            axisPointer: {
+              type: 'line',
+              lineStyle: {
+                color: 'rgba(255, 255, 255, 0.5)'
+              }
+            }
+          },
+          grid: { top: 30, right: 20, bottom: 30, left: 50 },
+          xAxis: {
+            type: 'category',
+            data: res.map((item) => item.time),
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' }
+          },
+          yAxis: {
+            type: 'value',
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' },
+            splitLine: { lineStyle: { color: 'rgba(74, 111, 165, 0.2)' } }
+          },
+          series: [
+            {
+              data: res.map((item) => item.value),
+              type: 'line',
+              smooth: true,
+              areaStyle: {
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: 'rgba(59, 130, 246, 0.5)' },
+                  { offset: 1, color: 'rgba(59, 130, 246, 0.05)' }
+                ])
+              },
+              lineStyle: { color: '#3b82f6', width: 2 },
+              itemStyle: { color: '#3b82f6' }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.alertTrend = chart;
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .box-container {
+    width: 100vw;
+    height: 100vh;
+
+    .box-header-scroll {
+      height: 80px;
+      background: url('~@/assets/pcs/header.png') no-repeat center top;
+      background-size: 100% 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 0 20px;
+      position: relative;
+      z-index: 10;
+      min-width: 2200px;
+
+      .logo {
+        // width: 200px;
+        padding-top: 10px;
+        position: absolute;
+        left: 80px;
+
+        ::v-deep .custom-select {
+          .el-input__inner {
+            background: #0d2960;
+            border: 1px solid #1a4a8a;
+            color: #fff;
+            height: 36px;
+            line-height: 36px;
+
+            &::placeholder {
+              color: #8ba3c7;
+            }
+          }
+
+          .el-input__suffix {
+            .el-input__icon {
+              color: #8ba3c7;
+            }
+          }
+        }
+      }
+
+      .title {
+        flex: 1;
+        text-align: center;
+        font-size: 28px;
+        font-weight: bold;
+        color: #fff;
+        text-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
+        letter-spacing: 4px;
+      }
+
+      .time {
+        position: absolute;
+        right: 30px;
+        width: 280px;
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+        gap: 15px;
+
+        .time-display {
+          display: flex;
+          flex-direction: column;
+          align-items: flex-end;
+
+          .date-text {
+            font-size: 14px;
+            color: #8ba3c7;
+          }
+
+          .time-text {
+            font-size: 20px;
+            font-weight: bold;
+            color: #fff;
+          }
+        }
+
+        .fullscreen-toggle {
+          cursor: pointer;
+          font-size: 20px;
+          color: #8ba3c7;
+          transition: color 0.3s;
+
+          &:hover {
+            color: #3b82f6;
+          }
+        }
+      }
+    }
+
+    .box-content {
+      height: calc(100%);
+      background: radial-gradient(
+          ellipse at top,
+          rgba(13, 45, 110, 0.3) 0%,
+          transparent 50%
+        ),
+        radial-gradient(
+          ellipse at bottom,
+          rgba(8, 25, 65, 0.5) 0%,
+          transparent 50%
+        ),
+        linear-gradient(180deg, #0a1630 0%, #0d1f4a 50%, #08152e 100%);
+      overflow: auto;
+      font-family: 'Microsoft YaHei', sans-serif;
+      flex: 1;
+      // overflow-x: auto;
+      // overflow-y: hidden;
+
+      &::-webkit-scrollbar {
+        height: 6px;
+      }
+
+      &::-webkit-scrollbar-track {
+        background: rgba(12, 32, 70, 0.4);
+      }
+
+      &::-webkit-scrollbar-thumb {
+        background: linear-gradient(180deg, #1a4a8a, #0d2a5a);
+        border-radius: 3px;
+      }
+
+      .scroll-wrapper {
+        // display: flex;
+        display: inline-block;
+        height: 100%;
+        // flex-direction: column;
+        // gap: 20px;
+        // padding: 0 20px 20px 20px;
+        width: 100%;
+        .header-section {
+          width: auto;
+          flex-shrink: 0;
+          padding-top: 10px;
+          min-width: 100%;
+        }
+
+        .three-column-layout {
+          display: inline-block;
+          white-space: nowrap;
+          flex: 1;
+          min-height: 850px;
+          // min-width: 2200px;
+          height: calc(100% - 130px);
+          min-width: 100%;
+          margin-top: 20px;
+          font-size: 0;
+          background: url('~@/assets/pcs/back.png') no-repeat center top;
+          background-size: 100% 100%;
+        }
+      }
+
+      .left-section,
+      .right-section,
+      .center-section {
+        display: inline-block;
+        vertical-align: top;
+        height: 100%;
+        font-size: 14px;
+        white-space: normal;
+      }
+
+      .left-section,
+      .right-section {
+        min-width: 800px;
+        margin-right: 20px;
+        width: 29%;
+      }
+
+      .center-section {
+        width: 40%;
+        // width: calc(100% - 1240px);
+        min-width: 1250px;
+        margin-right: 20px;
+        position: relative;
+      }
+
+      .left-section,
+      .right-section {
+        display: inline-flex;
+        flex-direction: column;
+        gap: 15px;
+        overflow: hidden;
+      }
+
+      .panel {
+        // background: linear-gradient(
+        //   135deg,
+        //   rgba(16, 42, 88, 0.9) 0%,
+        //   rgba(10, 28, 65, 0.85) 100%
+        // );
+        // border: 1px solid rgba(42, 90, 170, 0.4);
+        // border-radius: 8px;
+        padding: 15px;
+        min-height: 0;
+        // box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05),
+        //   0 4px 20px rgba(0, 0, 0, 0.3);
+
+        .panel-title {
+          font-size: 28px;
+          // font-weight: bold;
+          // font-style: italic;
+          font-family: 'YouSheBiaoTiHei', 'Microsoft YaHei', sans-serif;
+          color: #fff;
+          margin-bottom: 15px;
+          height: 42px;
+          padding-left: 40px;
+          position: relative;
+          display: flex;
+          align-items: center;
+          background: url('~@/assets/pcs/titleb.png') no-repeat center top;
+          background-size: 100% 100%;
+
+          &.energy-title {
+            display: flex;
+            justify-content: space-between;
+            padding-right: 15px;
+
+            .title-left {
+              display: flex;
+              align-items: center;
+              gap: 8px;
+            }
+          }
+          .panel-icon {
+            position: absolute;
+            left: 15px;
+            width: 20px;
+            height: 25px;
+            background: url('~@/assets/pcs/titleIcon.png') no-repeat center top;
+            background-size: 100% 100%;
+          }
+        }
+        .energy-tabs {
+          position: absolute;
+          top: 65px;
+          right: 40px;
+          z-index: 999;
+          display: flex;
+          gap: 0;
+          background: rgba(13, 41, 96, 0.5);
+          border-radius: 4px;
+          padding: 2px;
+
+          .tab-item {
+            padding: 4px 12px;
+            font-size: 12px;
+            color: #8ba3c7;
+            cursor: pointer;
+            border-radius: 3px;
+            transition: all 0.3s;
+
+            &:hover {
+              color: #fff;
+            }
+
+            &.active {
+              background: linear-gradient(135deg, #3b82f6, #1a6fd9);
+              color: #fff;
+            }
+          }
+        }
+      }
+
+      .data-panel {
+        min-height: 220px;
+        flex-shrink: 0;
+        display: flex;
+        flex-direction: column;
+        flex: 0.7;
+
+        .panel-title {
+          flex-shrink: 0;
+        }
+
+        .data-grid {
+          display: flex;
+
+          gap: 12px;
+          // padding: 10px 0;
+          align-items: center;
+          justify-content: space-between;
+          flex-wrap: wrap;
+          overflow: hidden;
+          height: 100%;
+
+          .data-item {
+            // background: linear-gradient(
+            //   135deg,
+            //   rgba(20, 50, 105, 0.9) 0%,
+            //   rgba(14, 38, 82, 0.85) 100%
+            // );
+            // border: 1px solid rgba(45, 95, 180, 0.5);
+            .img {
+              width: 35px;
+              height: 35px;
+              background: url('~@/assets/pcs/icon.png') no-repeat center top;
+              background-size: 100% 100%;
+            }
+            border-radius: 6px;
+            display: flex;
+            .data-item_value {
+              display: flex;
+              flex-direction: column;
+              // align-items: center;
+            }
+            // flex-direction: column;
+            align-items: center;
+            // justify-content: center;
+            padding-left: 15px;
+            gap: 8px;
+            position: relative;
+            overflow: hidden;
+            width: 21%;
+            height: 60px;
+            background: url('~@/assets/pcs/leftItem.png') no-repeat center top;
+            background-size: 100% 100%;
+
+            // &::before {
+            //   content: '';
+            //   position: absolute;
+            //   top: 0;
+            //   left: 0;
+            //   right: 0;
+            //   height: 2px;
+            //   background: linear-gradient(
+            //     90deg,
+            //     transparent,
+            //     #00d4ff,
+            //     transparent
+            //   );
+            //   box-shadow: 0 0 8px rgba(0, 212, 255, 0.6);
+            // }
+
+            .data-label {
+              font-size: 12px;
+              color: #8ba3c7;
+              text-align: center;
+              line-height: 1.3;
+            }
+
+            .data-value {
+              font-size: 18px;
+              font-weight: bold;
+              color: #b6ceff;
+              text-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
+            }
+          }
+        }
+      }
+
+      .chart-panel {
+        flex: 1;
+        min-height: 250px;
+        display: flex;
+        flex-direction: column;
+
+        .chart-container {
+          width: 100%;
+          flex: 1;
+          min-height: 0;
+        }
+      }
+      .param-card3 {
+        right: 10px;
+      }
+      .param-card2 {
+        left: 30%;
+      }
+      .param-card {
+        width: 23%;
+        top: 50px;
+
+        position: absolute;
+        background: rgba(39, 88, 233, 0.1);
+        border: 1px solid #3373d4;
+        border-radius: 8px;
+
+        &::before {
+          content: '';
+          position: absolute;
+          top: 0;
+          left: 0;
+          right: 0;
+          height: 2px;
+          background: linear-gradient(90deg, transparent, #00d4ff, transparent);
+          box-shadow: 0 0 8px rgba(0, 212, 255, 0.5);
+        }
+
+        .param-title {
+          font-size: 14px;
+          font-weight: bold;
+          color: #fff;
+          padding-bottom: 12px;
+          padding-top: 12px;
+          text-align: center;
+          text-shadow: 0 0 10px rgba(0, 212, 255, 0.3);
+          border-bottom: 1px solid rgba(0, 212, 255, 0.5);
+          background: rgba(39, 88, 233, 0.1);
+        }
+
+        .param-list {
+          padding: 15px;
+
+          display: flex;
+          flex-direction: column;
+          gap: 10px;
+
+          .param-item {
+            display: flex;
+            justify-content: space-between;
+            font-size: 12px;
+            padding: 4px 0;
+            // border-bottom: 1px dashed rgba(138, 163, 199, 0.2);
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            .param-name {
+              color: #8ba3c7;
+            }
+
+            .param-value {
+              color: #fff;
+              font-weight: 500;
+            }
+          }
+        }
+      }
+
+      .pipeline-container {
+        flex: 1;
+        min-height: 0;
+        position: absolute;
+        bottom: 15px;
+        left: 0;
+        height: 60%;
+        width: 100%;
+        // background: linear-gradient(
+        //   135deg,
+        //   rgba(14, 40, 85, 0.6) 0%,
+        //   rgba(10, 28, 60, 0.5) 100%
+        // );
+        // border: 1px solid rgba(45, 95, 180, 0.3);
+        // border-radius: 8px;
+        background: url('~@/assets/pcs/home.png') no-repeat center top;
+        background-size: 100% 100%;
+        // max-height: 680px;
+      }
+
+      .alert-panel {
+        min-height: 220px;
+        flex-shrink: 0;
+        display: flex;
+        flex-direction: column;
+        flex: 0.7;
+
+        .panel-title {
+          flex-shrink: 0;
+        }
+
+        .alert-stats {
+          flex: 1;
+          min-height: 0;
+          display: flex;
+          align-items: center;
+          height: calc(100% - 35px);
+          gap: 20px;
+
+          // 左侧数据卡片区域
+          .alert-cards {
+            width: 50%;
+            height: 100%;
+            display: grid;
+            grid-template-columns: repeat(2, 1fr);
+            grid-template-rows: repeat(3, 1fr);
+            gap: 12px;
+
+            .alert-card-item {
+              // background: linear-gradient(
+              //   135deg,
+              //   rgba(20, 50, 105, 0.9) 0%,
+              //   rgba(14, 38, 82, 0.85) 100%
+              // );
+              // border: 1px solid rgba(45, 95, 180, 0.5);
+              .img {
+                width: 35px;
+                height: 35px;
+                background: url('~@/assets/pcs/icon.png') no-repeat center top;
+                background-size: 100% 100%;
+              }
+              border-radius: 6px;
+              display: flex;
+              .data-item_value {
+                display: flex;
+                flex-direction: column;
+                // align-items: center;
+              }
+              // flex-direction: column;
+              align-items: center;
+              // justify-content: center;
+              padding-left: 15px;
+              gap: 8px;
+              position: relative;
+              overflow: hidden;
+              height: 60px;
+              background: url('~@/assets/pcs/leftItem.png') no-repeat center top;
+              background-size: 100% 100%;
+
+              // &::before {
+              //   content: '';
+              //   position: absolute;
+              //   top: 0;
+              //   left: 0;
+              //   right: 0;
+              //   height: 2px;
+              //   background: linear-gradient(
+              //     90deg,
+              //     transparent,
+              //     #00d4ff,
+              //     transparent
+              //   );
+              //   box-shadow: 0 0 8px rgba(0, 212, 255, 0.6);
+              // }
+
+              .data-label {
+                font-size: 12px;
+                color: #8ba3c7;
+                text-align: center;
+                line-height: 1.3;
+              }
+
+              .data-value {
+                font-size: 18px;
+                font-weight: bold;
+                color: #b6ceff;
+                text-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
+              }
+            }
+          }
+
+          // 右侧环形图区域
+          .alert-pie-chart {
+            width: 50%;
+            height: 100%;
+            position: relative;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            .alert-pie {
+              width: 100%;
+              height: 100%;
+              min-height: 180px;
+            }
+          }
+        }
+      }
+
+      .alert-list-panel {
+        flex: 1;
+        min-height: 0;
+        display: flex;
+        flex-direction: column;
+        min-height: 250px;
+
+        .alert-table {
+          flex: 1;
+          min-height: 0;
+          overflow: hidden;
+
+          .table-header {
+            display: grid;
+            grid-template-columns: 50px 90px 100px 70px 1fr 130px 70px;
+            gap: 6px;
+            padding: 10px 6px;
+            background: linear-gradient(
+              90deg,
+              rgba(16, 45, 95, 0.9) 0%,
+              rgba(12, 35, 75, 0.85) 100%
+            );
+            font-size: 12px;
+            color: #00d4ff;
+            text-align: center;
+            font-weight: 500;
+            border-bottom: 1px solid rgba(45, 95, 180, 0.4);
+          }
+        }
+      }
+    }
+  }
+
+  [v-cloak] {
+    display: none;
+  }
+</style>

+ 1480 - 0
src/views/bpm/vis-page/pcsData/index.vue

@@ -0,0 +1,1480 @@
+<template>
+  <vue-fullscreen
+    class="box-container"
+    v-cloak
+    v-model="isFullscreen"
+    fullscreenClass="box-container"
+    :exit-on-click-wrapper="false"
+  >
+    <div class="box-container" v-cloak>
+      <!-- 中间内容区域 - 可左右滚动,头部跟随 -->
+      <div class="box-content">
+        <div class="scroll-wrapper">
+          <!-- 头部区域 - 跟随滚动 -->
+          <div class="header-section">
+            <div class="box-header-scroll">
+              <div class="logo">
+                <el-select
+                  v-model="stationId"
+                  placeholder="请选择"
+                  class="custom-select"
+                  popper-class="custom-select-dropdown"
+                  @change="init"
+                >
+                  <el-option
+                    v-for="item in selectOptions"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value"
+                  />
+                </el-select>
+              </div>
+              <div class="header-center">
+                <div class="title">克拉玛依宇信工业科技股份有限公司</div>
+              </div>
+              <div class="time">
+                <div class="time-display">
+                  <span class="date-text">{{ date }}</span>
+                  <span class="time-text">{{ time }}</span>
+                </div>
+                <span class="fullscreen-toggle" @click.passive="onFullscreen">
+                  <i
+                    v-if="isFullscreen"
+                    title="取消全屏"
+                    class="el-icon-_screen-restore"
+                  />
+                  <i v-else title="全屏" class="el-icon-_screen-full" />
+                </span>
+              </div>
+            </div>
+          </div>
+
+          <!-- 三栏布局区域 -->
+          <div class="three-column-layout">
+            <!-- 左侧区域 -->
+            <div class="left-section">
+              <!-- 综合数据 -->
+              <div class="panel data-panel">
+                <div class="panel-title">
+                  <div class="panel-icon"></div>
+                  综合数据
+                </div>
+                <div
+                  style="
+                    display: flex;
+                    flex: 1;
+                    align-items: center;
+                    justify-content: center;
+                  "
+                >
+                  <div class="data-grid">
+                    <div
+                      class="data-item"
+                      v-for="(item, index) in comprehensiveList"
+                      :key="index"
+                    >
+                      <div class="img"></div>
+                      <div class="data-item_value">
+                        <span class="data-label">{{ item.label }}</span>
+                        <span class="data-value">{{ item.value }}</span></div
+                      >
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 区域用能对比 -->
+              <div class="panel chart-panel" style="position: relative">
+                <div class="panel-title energy-title">
+                  <div class="title-left">
+                    <div class="panel-icon"></div>
+                    用水用煤趋势图
+                  </div>
+                </div>
+                <div class="energy-tabs">
+                  <span
+                    v-for="tab in energyTabs"
+                    :key="tab.value"
+                    :class="[
+                      'tab-item',
+                      { active: activeEnergyTab === tab.value }
+                    ]"
+                    @click="energyTabsCahnge(tab.value)"
+                  >
+                    {{ tab.label }}
+                  </span>
+                </div>
+                <div class="chart-container" ref="waterChart"></div>
+              </div>
+
+              <!-- 区域能耗对比 -->
+              <div class="panel chart-panel">
+                <div class="panel-title energy-title">
+                  <div class="title-left">
+                    <div class="panel-icon"></div>
+                    固废量趋势图
+                  </div>
+                </div>
+                <div class="chart-container" ref="returnChart"></div>
+              </div>
+            </div>
+
+            <!-- 中间区域 -->
+            <div class="center-section">
+              <!-- 锅炉运行参数 -->
+              <div
+                :class="'param-card param-card' + (index + 1)"
+                v-for="(boiler, index) in boilerData"
+                :key="index"
+              >
+                <div class="param-title">{{ boiler.title }}</div>
+                <div class="param-list">
+                  <div
+                    class="param-item"
+                    v-for="(param, pIndex) in boiler.params"
+                    :key="pIndex"
+                  >
+                    <span class="param-name">{{ param.name }}</span>
+                    <span class="param-value">{{ param.value }}</span>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 管道流程图 -->
+              <div class="pipeline-container">
+                <!-- 管道流程图内容 -->
+              </div>
+            </div>
+
+            <!-- 右侧区域 -->
+            <div class="right-section">
+              <!-- 告警数据统计 -->
+              <div class="panel alert-panel">
+                <div class="panel-title">
+                  <div class="panel-icon"></div>
+                  告警数据概览
+                </div>
+                <div class="alert-stats">
+                  <!-- 左侧数据卡片 -->
+                  <div class="alert-cards">
+                    <div
+                      class="alert-card-item"
+                      v-for="(item, index) in alertStatsData"
+                      :key="index"
+                    >
+                      <div class="img"></div>
+                      <div class="data-item_value">
+                        <span class="data-label">{{ item.label }}</span>
+                        <span class="data-value">{{ item.value }}</span></div
+                      >
+                    </div>
+                  </div>
+                  <!-- 右侧环形图 -->
+                  <div class="alert-pie-chart">
+                    <div class="alert-pie" ref="alertPieChart"></div>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 告警列表 -->
+              <div class="panel alert-list-panel">
+                <div class="panel-title">
+                  <div class="panel-icon"></div>
+                  告警列表
+                </div>
+                <div class="alert-table">
+                  <dv-scroll-board
+                    :config="alertConfig"
+                    style="width: 100%; height: calc(100% - 2.5rem)"
+                  />
+                </div>
+              </div>
+
+              <!-- 设备告警趋势图 -->
+              <div class="panel chart-panel">
+                <div class="panel-title">
+                  <div class="panel-icon"></div>
+                  设备告警趋势图
+                </div>
+                <div class="chart-container" ref="alertTrendChart"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </vue-fullscreen>
+</template>
+
+<script>
+  import { component } from 'vue-fullscreen';
+  import * as echarts from 'echarts';
+  import {
+    alarmOverview,
+    queryAlarmList,
+    deviceAlarmTrend,
+    comprehensiveData,
+    waterCoalTrend,
+    wasteTrend,
+    listFactoryLine
+  } from '@/api/pcsData/index.js';
+
+  export default {
+    name: 'PcsDataDashboard',
+    components: {
+      VueFullscreen: component
+    },
+    data() {
+      return {
+        isFullscreen: false,
+        isFlag: false,
+        date: '',
+        time: '',
+        week: '',
+        selectedValue: '',
+        selectOptions: [],
+        comprehensiveList: [],
+        boilerData: [
+          {
+            title: '锅炉运行参数',
+            params: []
+          },
+          {
+            title: '注气检查记录',
+            params: []
+          },
+          {
+            title: '蒸汽质量检查记录',
+            params: []
+          }
+        ],
+        alertStatsData: [],
+        alertLegend: [
+          { name: '一般告警', percent: 45.2, color: '#3b82f6' },
+          { name: '警告告警', percent: 35.3, color: '#f59e0b' },
+          { name: '严重告警', percent: 4.7, color: '#ef4444', value: 58 },
+          { name: '紧急告警', percent: 15.2, color: '#ef4444' }
+        ],
+        alertConfig: {},
+        unitValue: '',
+        unitOptions: [
+          { label: '1#机组', value: '1' },
+          { label: '2#机组', value: '2' },
+          { label: '3#机组', value: '3' },
+          { label: '4#机组', value: '4' }
+        ],
+        energyTabs: [
+          { label: '水', value: '1' },
+          { label: '煤', value: '2' }
+        ],
+        activeEnergyTab: '1',
+        charts: {},
+        stationId: ''
+      };
+    },
+    watch: {
+      isFullscreen: {
+        handler() {
+          this.handleResize();
+        },
+        immediate: true
+      }
+    },
+    created() {
+      this.init();
+      listFactoryLine({
+        pageNum: 1,
+        size: 999,
+        type: 4
+      }).then((res) => {
+        this.selectOptions = res.list.map((item) => {
+          return {
+            label: item.name,
+            value: item.id
+          };
+        });
+        console.log(res);
+      });
+    },
+    mounted() {
+      this.updateTimer = setInterval(this.updateTime, 1000);
+
+      window.addEventListener('resize', this.handleResize);
+      // 初始化时同步头部宽度
+      this.$nextTick(() => {
+        this.syncHeaderWidth();
+      });
+    },
+    beforeDestroy() {
+      clearInterval(this.updateTimer);
+      window.removeEventListener('resize', this.handleResize);
+      Object.values(this.charts).forEach((chart) => chart && chart.dispose());
+    },
+    methods: {
+      energyTabsCahnge(val) {
+        this.activeEnergyTab = val;
+        this.waterCoalTrend();
+      },
+
+      init() {
+        this.alarmOverview();
+        this.queryAlarmList();
+        this.deviceAlarmTrend();
+        this.waterCoalTrend();
+        this.wasteTrend();
+        this.comprehensiveData();
+      },
+      //告警数据概览
+      alarmOverview() {
+        alarmOverview({
+          stationId: this.stationId
+        }).then((res) => {
+          this.$nextTick(() => {
+            this.initAlertPieChart(res);
+          });
+        });
+      },
+      //设备告警趋势图
+      deviceAlarmTrend() {
+        deviceAlarmTrend({ stationId: this.stationId }).then((res) => {
+          this.$nextTick(() => {
+            this.initAlertTrendChart(res);
+          });
+        });
+      },
+      //综合数据
+      comprehensiveData() {
+        comprehensiveData({ stationId: this.stationId }).then((res) => {
+          this.comprehensiveList = [
+            { label: '年度总注气量(M³)', value: res.yearTotalGasInjection },
+            { label: '年度总水量(T)', value: res.yearTotalWater },
+            { label: '年度用煤总量(T)', value: res.yearTotalCoal },
+            { label: '年度固废量(T)', value: res.yearTotalWaste },
+            { label: '本月注气量(M³)', value: res.monthTotalGasInjection },
+            { label: '本月水量(T)', value: res.monthTotalWater },
+            { label: '本月用煤量(T)', value: res.monthTotalCoal },
+            { label: '本月固废量(T)', value: res.monthTotalWaste }
+          ];
+          this.$set(
+            this.boilerData[0],
+            'params',
+            this.getParams(res['dailyReportOnBoilerOperation'] || {})
+          );
+          this.$set(
+            this.boilerData[1],
+            'params',
+            this.getParams(res['gasInjectionInspectionRecord'] || {})
+          );
+          this.$set(
+            this.boilerData[2],
+            'params',
+            this.getParams(res['steamQualityInspectionRecord'] || {})
+          );
+        });
+      },
+      getParams(data = {}) {
+        let list = [
+          {
+            name: '报工人:',
+            value: data?.executeUsers?.map((item) => item.userName).toString()
+          },
+          { name: '报工时间:', value: data.checkFinishTime }
+        ];
+        data.detailList?.forEach((item) => {
+          list.push({
+            name: item.paramValue,
+            value: item.num,
+            unit: item.unitName
+          });
+        });
+        return list;
+      },
+      //用煤用水
+      waterCoalTrend() {
+        waterCoalTrend({
+          trendType: this.activeEnergyTab,
+          stationId: this.stationId
+        }).then((res) => {
+          this.$nextTick(() => {
+            this.initWaterChart(res);
+          });
+        });
+      },
+      //固废趋势图
+      wasteTrend() {
+        wasteTrend({ stationId: this.stationId }).then((res) => {
+          this.$nextTick(() => {
+            this.initReturnChart(res);
+          });
+        });
+      },
+      //告警列表
+      queryAlarmList() {
+        let dataKey = [
+          'name',
+          'deviceLocation',
+          'alertLevel',
+          'ruleName',
+          'alarmTime',
+          'handleStatus'
+        ];
+
+        queryAlarmList({ stationId: this.stationId }).then((res) => {
+          let dataArr = [];
+          res.forEach((item, index) => {
+            let data = [index + 1];
+            dataKey.forEach((key, index) => {
+              if (key == 'alertLevel') {
+                item[key] =
+                  item[key] == 1
+                    ? '紧急'
+                    : item[key] == 2
+                    ? '严重'
+                    : item[key] == 3
+                    ? '一般'
+                    : '告警';
+              }
+              if (key == 'handleStatus') {
+                item[key] =
+                  item[key] == 1
+                    ? '处理中'
+                    : item[key] == 2
+                    ? '已处理'
+                    : '未处理';
+              }
+              data.push(item[key]);
+            });
+            dataArr.push(data);
+          });
+          this.alertConfig = {
+            header: [
+              '序号',
+              '设备名称',
+              '设备位置',
+              '告警级别',
+              '告警内容描述',
+              '告警时间',
+              '处理状态'
+            ].map(
+              (item) =>
+                `<div style="color: #38fdf8;font-size: 0.9rem;font-weight: bold">${item}</div>`
+            ),
+
+            data: dataArr.map((data) =>
+              data.map((item, index) => {
+                return `<div style="color: ${
+                  index == 3 ? this.getClolr(item) : '#fff'
+                };font-size: 0.9rem;">${item}</div>`;
+              })
+            ),
+            rowNum: 4,
+            headerBGC: 'transparent',
+            oddRowBGC: 'rgba(13, 41, 96, 0.3)',
+            evenRowBGC: 'rgba(13, 41, 96, 0.1)',
+            headerHeight: 30,
+            waitTime: 3000,
+            align: [
+              'center',
+              'center',
+              'center',
+              'center',
+              'center',
+              'center',
+              'center'
+            ],
+            columnWidth: []
+          };
+        });
+      },
+
+      onFullscreen() {
+        this.isFullscreen = !this.isFullscreen;
+      },
+      getClolr(item) {
+        return item == '严重'
+          ? '#FF0000'
+          : item == '紧急'
+          ? '#FF7070'
+          : item == '警告'
+          ? '#FFD337'
+          : '#fff';
+      },
+      updateTime() {
+        const now = new Date();
+        const hours = now.getHours().toString().padStart(2, '0');
+        const minutes = now.getMinutes().toString().padStart(2, '0');
+        const seconds = now.getSeconds().toString().padStart(2, '0');
+        this.time = `${hours}:${minutes}:${seconds}`;
+
+        const year = now.getFullYear();
+        const month = (now.getMonth() + 1).toString().padStart(2, '0');
+        const day = now.getDate().toString().padStart(2, '0');
+        this.date = `${year}-${month}-${day}`;
+      },
+      syncHeaderWidth() {
+        this.$nextTick(() => {
+          const threeColumnLayout = document.querySelector(
+            '.three-column-layout'
+          );
+          const headerSection = document.querySelector('.header-section');
+          if (threeColumnLayout && headerSection) {
+            const layoutWidth = threeColumnLayout.scrollWidth;
+            headerSection.style.width = layoutWidth + 'px';
+          }
+        });
+      },
+      handleResize() {
+        this.$nextTick(() => {
+          // 同步头部宽度
+          this.syncHeaderWidth();
+
+          const { clientWidth, clientHeight } = document.documentElement;
+          const containers = document.getElementsByClassName('box-container');
+          Array.from(containers).forEach((item) => {
+            if (this.isFullscreen) {
+              item.style.height = clientHeight + 'px';
+              item.style.width = clientWidth + 'px';
+            } else {
+              item.style.height = clientHeight - 100 + 'px';
+              item.style.width = clientWidth - 240 + 'px';
+            }
+          });
+          Object.values(this.charts).forEach(
+            (chart) => chart && chart.resize()
+          );
+        });
+      },
+
+      initWaterChart(res) {
+        if (!this.$refs.waterChart) return;
+        console.log(res);
+
+        const chart = echarts.init(this.$refs.waterChart);
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            backgroundColor: '#fff',
+            borderColor: '#e0e0e0',
+            borderWidth: 1,
+            textStyle: {
+              color: '#333'
+            },
+            axisPointer: {
+              type: 'line',
+              lineStyle: {
+                color: 'rgba(255, 255, 255, 0.5)'
+              }
+            }
+          },
+          grid: {
+            top: '1.875rem',
+            right: '1.25rem',
+            bottom: '1.875rem',
+            left: '3.125rem'
+          },
+          xAxis: {
+            type: 'category',
+            data: res.map((item) => item.time),
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' }
+          },
+          yAxis: {
+            type: 'value',
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' },
+            splitLine: { lineStyle: { color: 'rgba(74, 111, 165, 0.2)' } }
+          },
+          series: [
+            {
+              data: res.map((item) => item.value),
+              type: 'line',
+              smooth: true,
+              areaStyle: {
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: 'rgba(59, 130, 246, 0.5)' },
+                  { offset: 1, color: 'rgba(59, 130, 246, 0.05)' }
+                ])
+              },
+              lineStyle: { color: '#3b82f6', width: 2 },
+              itemStyle: { color: '#3b82f6' }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.water = chart;
+      },
+      initReturnChart(res) {
+        if (!this.$refs.returnChart) return;
+        const chart = echarts.init(this.$refs.returnChart);
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            backgroundColor: '#fff',
+            borderColor: '#e0e0e0',
+            borderWidth: 1,
+            textStyle: {
+              color: '#333'
+            },
+            axisPointer: {
+              type: 'line',
+              lineStyle: {
+                color: 'rgba(255, 255, 255, 0.5)'
+              }
+            }
+          },
+          grid: {
+            top: '1.875rem',
+            right: '1.25rem',
+            bottom: '1.875rem',
+            left: '3.125rem'
+          },
+          xAxis: {
+            type: 'category',
+            data: res.map((item) => item.time),
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' }
+          },
+          yAxis: {
+            type: 'value',
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' },
+            splitLine: { lineStyle: { color: 'rgba(74, 111, 165, 0.2)' } }
+          },
+          series: [
+            {
+              data: res.map((item) => item.value),
+              type: 'line',
+              smooth: true,
+              areaStyle: {
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: 'rgba(59, 130, 246, 0.5)' },
+                  { offset: 1, color: 'rgba(59, 130, 246, 0.05)' }
+                ])
+              },
+              lineStyle: { color: '#3b82f6', width: 2 },
+              itemStyle: { color: '#3b82f6' }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.return = chart;
+      },
+      initAlertPieChart(res) {
+        this.alertStatsData = [
+          {
+            label: '系统告警总数',
+            value: res.yearTotalCount,
+            icon: 'el-icon-_alert'
+          },
+          {
+            label: '本月告警数',
+            value: res.monthTotalCount,
+            icon: 'el-icon-_month'
+          },
+          {
+            label: '本年已处理数',
+            value: res.yearHandledCount,
+            icon: 'el-icon-_check'
+          },
+          {
+            label: '本月已处理数',
+            value: res.monthHandledCount,
+            icon: 'el-icon-_check'
+          },
+          {
+            label: '年度未处理数',
+            value: res.yearUnhandledCount,
+            icon: 'el-icon-_warning'
+          },
+          {
+            label: '本月未处理数',
+            value: res.monthUnhandledCount,
+            icon: 'el-icon-_warning'
+          }
+        ];
+        if (!this.$refs.alertPieChart) return;
+        const chart = echarts.init(this.$refs.alertPieChart);
+
+        // 计算总数
+        const total =
+          res?.levelStats?.reduce((sum, item) => sum + item.count, 0) || 1247;
+
+        const option = {
+          tooltip: {
+            trigger: 'item',
+            formatter: '{b}: {c} ({d}%)'
+          },
+          series: [
+            {
+              type: 'pie',
+              radius: ['45%', '65%'],
+              center: ['50%', '50%'],
+              avoidLabelOverlap: true,
+              itemStyle: {
+                borderRadius: 4,
+                borderColor: '#0d1f4a',
+                borderWidth: 2
+              },
+              data: res?.levelStats?.map((item) => {
+                return {
+                  value: item.count,
+                  name: item.levelName,
+                  itemStyle: {
+                    color:
+                      item.level == 1
+                        ? '#ef4444'
+                        : item.level == 2
+                        ? '#dc2626'
+                        : item.level == 3
+                        ? '#3b82f6'
+                        : '#f59e0b'
+                  }
+                };
+              }) || [
+                {
+                  value: 564,
+                  name: '一般告警',
+                  itemStyle: { color: '#3b82f6' }
+                },
+                {
+                  value: 440,
+                  name: '警告告警',
+                  itemStyle: { color: '#f59e0b' }
+                },
+                {
+                  value: 58,
+                  name: '严重告警',
+                  itemStyle: { color: '#dc2626' }
+                },
+                {
+                  value: 185,
+                  name: '紧急告警',
+                  itemStyle: { color: '#ef4444' }
+                }
+              ],
+              label: {
+                show: true,
+                position: 'outside',
+                formatter: '{per|{d}%}\n{name|{b}}',
+                rich: {
+                  per: {
+                    fontSize: 14,
+                    fontWeight: 'bold',
+                    color: '#fff',
+                    padding: [0, 0, 4, 0]
+                  },
+                  name: {
+                    fontSize: 12,
+                    color: '#8ba3c7'
+                  }
+                }
+              },
+              labelLine: {
+                show: true,
+                length: 10,
+                length2: 10,
+                lineStyle: {
+                  color: 'rgba(138, 163, 199, 0.5)'
+                }
+              },
+              emphasis: {
+                scale: true,
+                scaleSize: 5,
+                label: {
+                  show: true,
+                  fontWeight: 'bold'
+                }
+              }
+            }
+          ],
+          graphic: [
+            {
+              type: 'text',
+              left: 'center',
+              top: '45%',
+              style: {
+                text: String(total),
+                textAlign: 'center',
+                fill: '#fff',
+                fontSize: 24,
+                fontWeight: 'bold'
+              }
+            },
+            {
+              type: 'text',
+              left: 'center',
+              top: '60%',
+              style: {
+                text: '告警总数',
+                textAlign: 'center',
+                fill: '#8ba3c7',
+                fontSize: 11
+              }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.alertPie = chart;
+      },
+      initAlertTrendChart(res) {
+        if (!this.$refs.alertTrendChart) return;
+        const chart = echarts.init(this.$refs.alertTrendChart);
+        const option = {
+          tooltip: {
+            trigger: 'axis',
+            backgroundColor: '#fff',
+            borderColor: '#e0e0e0',
+            borderWidth: 1,
+            textStyle: {
+              color: '#333'
+            },
+            axisPointer: {
+              type: 'line',
+              lineStyle: {
+                color: 'rgba(255, 255, 255, 0.5)'
+              }
+            }
+          },
+          grid: {
+            top: '1.875rem',
+            right: '1.25rem',
+            bottom: '1.875rem',
+            left: '3.125rem'
+          },
+          xAxis: {
+            type: 'category',
+            data: res.map((item) => item.time),
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' }
+          },
+          yAxis: {
+            type: 'value',
+            axisLine: { lineStyle: { color: '#4a6fa5' } },
+            axisLabel: { color: '#8ba3c7' },
+            splitLine: { lineStyle: { color: 'rgba(74, 111, 165, 0.2)' } }
+          },
+          series: [
+            {
+              data: res.map((item) => item.value),
+              type: 'line',
+              smooth: true,
+              areaStyle: {
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: 'rgba(59, 130, 246, 0.5)' },
+                  { offset: 1, color: 'rgba(59, 130, 246, 0.05)' }
+                ])
+              },
+              lineStyle: { color: '#3b82f6', width: 2 },
+              itemStyle: { color: '#3b82f6' }
+            }
+          ]
+        };
+        chart.setOption(option);
+        this.charts.alertTrend = chart;
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .box-container {
+    width: 100vw;
+    height: 100vh;
+
+    .box-header-scroll {
+      height: 5rem;
+      background: url('~@/assets/pcs/header.png') no-repeat center top;
+      background-size: 100% 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 0 1.25rem;
+      position: relative;
+      z-index: 10;
+      min-width: 137.5rem;
+
+      .logo {
+        // width: 12.5rem;
+        padding-top: 0.625rem;
+        position: absolute;
+        left: 5rem;
+
+        ::v-deep .custom-select {
+          .el-input__inner {
+            background: #0d2960;
+            border: 1px solid #1a4a8a;
+            color: #fff;
+            height: 2.25rem;
+            line-height: 2.25rem;
+
+            &::placeholder {
+              color: #8ba3c7;
+            }
+          }
+
+          .el-input__suffix {
+            .el-input__icon {
+              color: #8ba3c7;
+            }
+          }
+        }
+      }
+
+      .title {
+        flex: 1;
+        text-align: center;
+        font-size: 1.75rem;
+        font-weight: bold;
+        color: #fff;
+        text-shadow: 0 0 0.625rem rgba(59, 130, 246, 0.5);
+        letter-spacing: 0.25rem;
+      }
+
+      .time {
+        position: absolute;
+        right: 1.875rem;
+        width: 17.5rem;
+        display: flex;
+        align-items: center;
+        justify-content: flex-end;
+        gap: 0.9375rem;
+
+        .time-display {
+          display: flex;
+          flex-direction: column;
+          align-items: flex-end;
+
+          .date-text {
+            font-size: 0.875rem;
+            color: #8ba3c7;
+          }
+
+          .time-text {
+            font-size: 1.25rem;
+            font-weight: bold;
+            color: #fff;
+          }
+        }
+
+        .fullscreen-toggle {
+          cursor: pointer;
+          font-size: 1.25rem;
+          color: #8ba3c7;
+          transition: color 0.3s;
+
+          &:hover {
+            color: #3b82f6;
+          }
+        }
+      }
+    }
+
+    .box-content {
+      height: calc(100%);
+      background: radial-gradient(
+          ellipse at top,
+          rgba(13, 45, 110, 0.3) 0%,
+          transparent 50%
+        ),
+        radial-gradient(
+          ellipse at bottom,
+          rgba(8, 25, 65, 0.5) 0%,
+          transparent 50%
+        ),
+        linear-gradient(180deg, #0a1630 0%, #0d1f4a 50%, #08152e 100%);
+      overflow: auto;
+      font-family: 'Microsoft YaHei', sans-serif;
+      flex: 1;
+      // overflow-x: auto;
+      // overflow-y: hidden;
+
+      &::-webkit-scrollbar {
+        height: 0.375rem;
+      }
+
+      &::-webkit-scrollbar-track {
+        background: rgba(12, 32, 70, 0.4);
+      }
+
+      &::-webkit-scrollbar-thumb {
+        background: linear-gradient(180deg, #1a4a8a, #0d2a5a);
+        border-radius: 0.1875rem;
+      }
+
+      .scroll-wrapper {
+        // display: flex;
+        display: inline-block;
+        height: 100%;
+        // flex-direction: column;
+        // gap: 1.25rem;
+        // padding: 0 1.25rem 1.25rem 1.25rem;
+        width: 100%;
+        .header-section {
+          width: auto;
+          flex-shrink: 0;
+          padding-top: 0.625rem;
+          min-width: 100%;
+        }
+
+        .three-column-layout {
+          display: inline-block;
+          white-space: nowrap;
+          flex: 1;
+          min-height: 53.125rem;
+          // min-width: 137.5rem;
+          height: calc(100% - 8.125rem);
+          min-width: 100%;
+          margin-top: 1.25rem;
+          font-size: 0;
+          background: url('~@/assets/pcs/back.png') no-repeat center top;
+          background-size: 100% 100%;
+        }
+      }
+
+      .left-section,
+      .right-section,
+      .center-section {
+        display: inline-block;
+        vertical-align: top;
+        height: 100%;
+        font-size: 0.875rem;
+        white-space: normal;
+      }
+
+      .left-section,
+      .right-section {
+        min-width: 50rem;
+        margin-right: 1.25rem;
+        width: 29%;
+      }
+
+      .center-section {
+        width: 40%;
+        // width: calc(100% - 77.5rem);
+        min-width: 78.125rem;
+        margin-right: 1.25rem;
+        position: relative;
+      }
+
+      .left-section,
+      .right-section {
+        display: inline-flex;
+        flex-direction: column;
+        gap: 0.9375rem;
+        overflow: hidden;
+      }
+
+      .panel {
+        // background: linear-gradient(
+        //   135deg,
+        //   rgba(16, 42, 88, 0.9) 0%,
+        //   rgba(10, 28, 65, 0.85) 100%
+        // );
+        // border: 1px solid rgba(42, 90, 170, 0.4);
+        // border-radius: 0.5rem;
+        padding: 0.9375rem;
+        min-height: 0;
+        // box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05),
+        //   0 4px 20px rgba(0, 0, 0, 0.3);
+
+        .panel-title {
+          font-size: 1.75rem;
+          // font-weight: bold;
+          // font-style: italic;
+          font-family: 'YouSheBiaoTiHei', 'Microsoft YaHei', sans-serif;
+          color: #fff;
+          margin-bottom: 0.9375rem;
+          height: 2.625rem;
+          padding-left: 2.5rem;
+          position: relative;
+          display: flex;
+          align-items: center;
+          background: url('~@/assets/pcs/titleb.png') no-repeat center top;
+          background-size: 100% 100%;
+
+          &.energy-title {
+            display: flex;
+            justify-content: space-between;
+            padding-right: 0.9375rem;
+
+            .title-left {
+              display: flex;
+              align-items: center;
+              gap: 0.5rem;
+            }
+          }
+          .panel-icon {
+            position: absolute;
+            left: 0.9375rem;
+            width: 1.25rem;
+            height: 1.5625rem;
+            background: url('~@/assets/pcs/titleIcon.png') no-repeat center top;
+            background-size: 100% 100%;
+          }
+        }
+        .energy-tabs {
+          position: absolute;
+          top: 4.0625rem;
+          right: 2.5rem;
+          z-index: 999;
+          display: flex;
+          gap: 0;
+          background: rgba(13, 41, 96, 0.5);
+          border-radius: 0.25rem;
+          padding: 0.125rem;
+
+          .tab-item {
+            padding: 0.25rem 0.75rem;
+            font-size: 0.75rem;
+            color: #8ba3c7;
+            cursor: pointer;
+            border-radius: 0.1875rem;
+            transition: all 0.3s;
+
+            &:hover {
+              color: #fff;
+            }
+
+            &.active {
+              background: linear-gradient(135deg, #3b82f6, #1a6fd9);
+              color: #fff;
+            }
+          }
+        }
+      }
+
+      .data-panel {
+        min-height: 13.75rem;
+        flex-shrink: 0;
+        display: flex;
+        flex-direction: column;
+        flex: 0.7;
+
+        .panel-title {
+          flex-shrink: 0;
+        }
+
+        .data-grid {
+          display: flex;
+
+          gap: 0.75rem;
+          // padding: 0.625rem 0;
+          align-items: center;
+          justify-content: space-around;
+          flex-wrap: wrap;
+          overflow: hidden;
+          height: 100%;
+
+          .data-item {
+            // background: linear-gradient(
+            //   135deg,
+            //   rgba(20, 50, 105, 0.9) 0%,
+            //   rgba(14, 38, 82, 0.85) 100%
+            // );
+            // border: 1px solid rgba(45, 95, 180, 0.5);
+            .img {
+              width: 2.1875rem;
+              height: 2.1875rem;
+              background: url('~@/assets/pcs/icon.png') no-repeat center top;
+              background-size: 100% 100%;
+            }
+            border-radius: 0.375rem;
+            display: flex;
+            .data-item_value {
+              display: flex;
+              flex-direction: column;
+              // align-items: center;
+            }
+            // flex-direction: column;
+            align-items: center;
+            // justify-content: center;
+            padding-left: 0.9375rem;
+            gap: 0.5rem;
+            position: relative;
+            overflow: hidden;
+            width: 18%;
+            height: 3.75rem;
+            background: url('~@/assets/pcs/leftItem.png') no-repeat center top;
+            background-size: 100% 100%;
+
+            // &::before {
+            //   content: '';
+            //   position: absolute;
+            //   top: 0;
+            //   left: 0;
+            //   right: 0;
+            //   height: 0.125rem;
+            //   background: linear-gradient(
+            //     90deg,
+            //     transparent,
+            //     #00d4ff,
+            //     transparent
+            //   );
+            //   box-shadow: 0 0 0.5rem rgba(0, 212, 255, 0.6);
+            // }
+
+            .data-label {
+              font-size: 0.75rem;
+              color: #8ba3c7;
+              text-align: center;
+              line-height: 1.3;
+            }
+
+            .data-value {
+              font-size: 1.125rem;
+              font-weight: bold;
+              color: #b6ceff;
+              text-shadow: 0 0 0.625rem rgba(0, 212, 255, 0.5);
+            }
+          }
+        }
+      }
+
+      .chart-panel {
+        flex: 1;
+        min-height: 15.625rem;
+        display: flex;
+        flex-direction: column;
+
+        .chart-container {
+          width: 100%;
+          flex: 1;
+          min-height: 0;
+        }
+      }
+      .param-card3 {
+        right: 0.625rem;
+      }
+      .param-card2 {
+        left: 30%;
+      }
+      .param-card {
+        width: 23%;
+        top: 3.125rem;
+
+        position: absolute;
+        background: rgba(39, 88, 233, 0.1);
+        border: 1px solid #3373d4;
+        border-radius: 0.5rem;
+
+        &::before {
+          content: '';
+          position: absolute;
+          top: 0;
+          left: 0;
+          right: 0;
+          height: 0.125rem;
+          background: linear-gradient(90deg, transparent, #00d4ff, transparent);
+          box-shadow: 0 0 0.5rem rgba(0, 212, 255, 0.5);
+        }
+
+        .param-title {
+          font-size: 0.875rem;
+          font-weight: bold;
+          color: #fff;
+          padding-bottom: 0.75rem;
+          padding-top: 0.75rem;
+          text-align: center;
+          text-shadow: 0 0 0.625rem rgba(0, 212, 255, 0.3);
+          border-bottom: 1px solid rgba(0, 212, 255, 0.5);
+          background: rgba(39, 88, 233, 0.1);
+        }
+
+        .param-list {
+          padding: 0.9375rem;
+
+          display: flex;
+          flex-direction: column;
+          gap: 0.625rem;
+
+          .param-item {
+            display: flex;
+            // justify-content: space-between;
+            font-size: 0.75rem;
+            padding: 0.25rem 0;
+            // border-bottom: 1px dashed rgba(138, 163, 199, 0.2);
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            .param-name {
+              color: #8ba3c7;
+              min-width: 4rem;
+            }
+
+            .param-value {
+              color: #fff;
+              font-weight: 500;
+            }
+          }
+        }
+      }
+
+      .pipeline-container {
+        flex: 1;
+        min-height: 0;
+        position: absolute;
+        bottom: 0.9375rem;
+        left: 0;
+        height: 60%;
+        width: 100%;
+        // background: linear-gradient(
+        //   135deg,
+        //   rgba(14, 40, 85, 0.6) 0%,
+        //   rgba(10, 28, 60, 0.5) 100%
+        // );
+        // border: 1px solid rgba(45, 95, 180, 0.3);
+        // border-radius: 0.5rem;
+        background: url('~@/assets/pcs/home.png') no-repeat center top;
+        background-size: 100% 100%;
+        // max-height: 42.5rem;
+      }
+
+      .alert-panel {
+        min-height: 13.75rem;
+        flex-shrink: 0;
+        display: flex;
+        flex-direction: column;
+        flex: 0.7;
+
+        .panel-title {
+          flex-shrink: 0;
+        }
+
+        .alert-stats {
+          flex: 1;
+          min-height: 0;
+          display: flex;
+          align-items: center;
+          height: calc(100% - 2.1875rem);
+          gap: 1.25rem;
+
+          // 左侧数据卡片区域
+          .alert-cards {
+            width: 50%;
+            height: 100%;
+            display: flex;
+            flex-wrap: wrap;
+            justify-content: space-around;
+            gap: 0.75rem;
+
+            .alert-card-item {
+              width: 40%;
+              // background: linear-gradient(
+              //   135deg,
+              //   rgba(20, 50, 105, 0.9) 0%,
+              //   rgba(14, 38, 82, 0.85) 100%
+              // );
+              // border: 1px solid rgba(45, 95, 180, 0.5);
+              .img {
+                width: 2.1875rem;
+                height: 2.1875rem;
+                background: url('~@/assets/pcs/icon.png') no-repeat center top;
+                background-size: 100% 100%;
+              }
+              border-radius: 0.375rem;
+              display: flex;
+              .data-item_value {
+                display: flex;
+                flex-direction: column;
+                // align-items: center;
+              }
+              // flex-direction: column;
+              align-items: center;
+              // justify-content: center;
+              padding-left: 0.9375rem;
+              gap: 0.5rem;
+              position: relative;
+              overflow: hidden;
+              height: 3.75rem;
+              background: url('~@/assets/pcs/leftItem.png') no-repeat center top;
+              background-size: 100% 100%;
+
+              // &::before {
+              //   content: '';
+              //   position: absolute;
+              //   top: 0;
+              //   left: 0;
+              //   right: 0;
+              //   height: 0.125rem;
+              //   background: linear-gradient(
+              //     90deg,
+              //     transparent,
+              //     #00d4ff,
+              //     transparent
+              //   );
+              //   box-shadow: 0 0 0.5rem rgba(0, 212, 255, 0.6);
+              // }
+
+              .data-label {
+                font-size: 0.75rem;
+                color: #8ba3c7;
+                text-align: center;
+                line-height: 1.3;
+              }
+
+              .data-value {
+                font-size: 1.125rem;
+                font-weight: bold;
+                color: #b6ceff;
+                text-shadow: 0 0 0.625rem rgba(0, 212, 255, 0.5);
+              }
+            }
+          }
+
+          // 右侧环形图区域
+          .alert-pie-chart {
+            width: 50%;
+            height: 100%;
+            position: relative;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            .alert-pie {
+              width: 100%;
+              height: 100%;
+              min-height: 11.25rem;
+            }
+          }
+        }
+      }
+
+      .alert-list-panel {
+        flex: 1;
+        min-height: 0;
+        display: flex;
+        flex-direction: column;
+        min-height: 15.625rem;
+
+        .alert-table {
+          flex: 1;
+          min-height: 0;
+          overflow: hidden;
+
+          .table-header {
+            display: grid;
+            grid-template-columns: 3.125rem 5.625rem 6.25rem 4.375rem 1fr 8.125rem 4.375rem;
+            gap: 0.375rem;
+            padding: 0.625rem 0.375rem;
+            background: linear-gradient(
+              90deg,
+              rgba(16, 45, 95, 0.9) 0%,
+              rgba(12, 35, 75, 0.85) 100%
+            );
+            font-size: 0.75rem;
+            color: #00d4ff;
+            text-align: center;
+            font-weight: 500;
+            border-bottom: 1px solid rgba(45, 95, 180, 0.4);
+          }
+        }
+      }
+    }
+  }
+
+  [v-cloak] {
+    display: none;
+  }
+</style>