Kaynağa Gözat

提交班组生产日综合看板

hezhanp 10 ay önce
ebeveyn
işleme
04ba0d408c

Dosya farkı çok büyük olduğundan ihmal edildi
+ 153 - 117
package-lock.json


BIN
src/assets/png/Group.png


BIN
src/assets/png/bj.png


BIN
src/assets/png/icon.png


BIN
src/assets/png/item_img.png


BIN
src/assets/png/num_bj.png


+ 1446 - 0
src/views/bpm/vis-page/productionDailyDashboard.vue

@@ -0,0 +1,1446 @@
+<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-header">
+        <div class="logo"></div>
+        <div class="title">班组生产日综合看板</div>
+        <div class="time">
+          <div class="time-display">
+            <span class="date-text">{{ date }}</span>
+            <span class="time-text">{{ time }}</span>
+            <span class="week-text">{{ week }}</span>
+          </div>
+          <span class="fullscreen-toggle" @click.passive="onFullscreen">
+            <i
+              v-if="isFullscreen"
+              title="取消全屏"
+              class="el-icon-_screen-restore"
+            ></i>
+            <i v-else title="全屏" class="el-icon-_screen-full"></i>
+          </span>
+        </div>
+      </div>
+
+      <div class="box-middle">
+        <div class="box-middle-sidebar left-sidebar">
+          <!-- 当日生产数量卡片 -->
+          <div class="sidebar-card">
+            <div class="card-header">
+              <div class="card-logo">
+                <img src="@/assets/png/icon.png" alt="图标" />
+              </div>
+              <div class="card-title">当日生产数量</div>
+            </div>
+            <div class="card-content">
+              <div class="number-display">
+                <!-- 十万位 -->
+                <div class="unit-box">
+                  <span class="unit">十万</span>
+                  <div class="digit-box">
+                    <span class="digit">{{ countDigits[0] }}</span>
+                  </div>
+                </div>
+                <!-- 万位 -->
+                <div class="unit-box">
+                  <span class="unit">万</span>
+                  <div class="digit-box">
+                    <span class="digit">{{ countDigits[1] }}</span>
+                  </div>
+                </div>
+                <!-- 千位 -->
+                <div class="unit-box">
+                  <span class="unit">千</span>
+                  <div class="digit-box">
+                    <span class="digit">{{ countDigits[2] }}</span>
+                  </div>
+                </div>
+                <!-- 千位分隔符 -->
+                <div class="separator">,</div>
+                <!-- 百位 -->
+                <div class="unit-box">
+                  <span class="unit"></span>
+                  <div class="digit-box">
+                    <span class="digit">{{ countDigits[3] }}</span>
+                  </div>
+                </div>
+                <!-- 十位 -->
+                <div class="unit-box">
+                  <span class="unit"></span>
+                  <div class="digit-box">
+                    <span class="digit">{{ countDigits[4] }}</span>
+                  </div>
+                </div>
+                <!-- 个位 -->
+                <div class="unit-box">
+                  <span class="unit"></span>
+                  <div class="digit-box">
+                    <span class="digit">{{ countDigits[5] }}</span>
+                  </div>
+                </div>
+              </div>
+
+              <!-- 生产统计项 -->
+              <div class="card-stats">
+                <div class="stat-item-container">
+                  <div class="stat-icon">
+                    <img src="@/assets/png/Group.png" alt="要求生产" />
+                  </div>
+                  <div class="stat-info">
+                    <div class="stat-label">要求生产总数量:</div>
+                    <div
+                      ><span class="stat-value">44</span
+                      ><span class="stat-dw"
+                        >({{ productionUnit }})</span
+                      ></div
+                    >
+                  </div>
+                </div>
+                <div class="stat-item-container">
+                  <div class="stat-icon">
+                    <img src="@/assets/png/Group.png" alt="达成率" />
+                  </div>
+                  <div class="stat-info">
+                    <div class="stat-label">生产达成率:</div>
+                    <div><span class="stat-value">58.8%</span></div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <!-- 各工序在岗人数卡片 -->
+          <div class="sidebar-card chart-card-small">
+            <div class="card-header">
+              <div class="card-logo">
+                <img src="@/assets/png/icon.png" alt="图标" />
+              </div>
+              <div class="card-title">各工序在岗人数</div>
+            </div>
+            <div class="card-content">
+              <div
+                ref="processPersonnelChart"
+                class="process-chart-container"
+              ></div>
+            </div>
+          </div>
+
+          <!-- 产品生产数量TOP10 -->
+          <div class="sidebar-card chart-card-large">
+            <div class="card-header">
+              <div class="card-logo">
+                <img src="@/assets/png/icon.png" alt="图标" />
+              </div>
+              <div class="card-title">产品生产数量TOP10</div>
+            </div>
+            <div class="card-content">
+              <div
+                ref="productTop10Chart"
+                class="product-top10-chart-container"
+              ></div>
+            </div>
+          </div>
+        </div>
+
+        <div class="box-middle-content">
+          <div class="content-card-container">
+            <!-- 当日生产情况 -->
+            <div class="sidebar-card">
+              <div class="card-header">
+                <div class="card-logo">
+                  <img src="@/assets/png/icon.png" alt="图标" />
+                </div>
+                <div class="card-title">当日生产情况</div>
+              </div>
+              <div class="content-empty">
+                <div class="Remark"
+                  >注:生产达成率=已完成生产数量/要求生产总数量</div
+                >
+                <div class="content-box">
+                  <div class="box-item">
+                    <div class="item-img">23</div>
+                    <div class="item-text">已延期生产单数</div>
+                  </div>
+                  <div class="box-item">
+                    <div class="item-img">58%</div>
+                    <div class="item-text">生产达成率</div>
+                  </div>
+                  <div class="box-item">
+                    <div class="item-img">11</div>
+                    <div class="item-text">待生产单数</div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <!-- 生产工单进度:移除滚动功能 -->
+          <div class="content-top-container">
+            <div class="sidebar-card">
+              <div class="card-header">
+                <div class="card-logo">
+                  <img src="@/assets/png/icon.png" alt="图标" />
+                </div>
+                <div class="card-title">生产工单进度</div>
+              </div>
+              <!-- 表格容器:移除动态高度和滚动 -->
+              <div class="content-table" v-if="isFlag">
+                <table class="main-table">
+                  <thead>
+                    <tr>
+                      <th style="width: 12%">生产工单号</th>
+                      <th style="width: 15%">产品名称</th>
+                      <th style="width: 13%">要求完成数量</th>
+                      <th style="width: 13%">已完成数量</th>
+                      <th style="width: 12%">生产达成率</th>
+                      <th style="width: 15%">计划开始时间</th>
+                      <th style="width: 15%">计划结束时间</th>
+                      <th style="width: 15%">当前工序</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    <!-- 固定红色行(State=Y) -->
+                    <tr
+                      v-for="item in fixedRows"
+                      :key="`fixed-${item.id}`"
+                      class="table-row state-red"
+                    >
+                      <td class="table-cell">{{ item.id }}</td>
+                      <td class="table-cell">{{ item.name }}</td>
+                      <td class="table-cell">{{ item.yqsl }}</td>
+                      <td class="table-cell">{{ item.ywcsl }}</td>
+                      <td class="table-cell">{{ item.scdcl }}</td>
+                      <td class="table-cell">{{ item.kstime }}</td>
+                      <td class="table-cell">{{ item.jstime }}</td>
+                      <td class="table-cell">{{ item.dom }}</td>
+                    </tr>
+
+                    <!-- 非红色行(State=N) -->
+                    <tr
+                      v-for="item in scrollRows"
+                      :key="`scroll-${item.id}`"
+                      class="table-row"
+                    >
+                      <td class="table-cell">{{ item.id }}</td>
+                      <td class="table-cell">{{ item.name }}</td>
+                      <td class="table-cell">{{ item.yqsl }}</td>
+                      <td class="table-cell">{{ item.ywcsl }}</td>
+                      <td class="table-cell">{{ item.scdcl }}</td>
+                      <td class="table-cell">{{ item.kstime }}</td>
+                      <td class="table-cell">{{ item.jstime }}</td>
+                      <td class="table-cell">{{ item.dom }}</td>
+                    </tr>
+
+                    <!-- 无数据时显示占位 -->
+                    <tr v-if="fixedRows.length === 0 && scrollRows.length === 0">
+                      <td colspan="8" class="empty-cell"> 暂无生产工单数据 </td>
+                    </tr>
+                  </tbody>
+                </table>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="box-middle-sidebar right-sidebar">
+          <!-- 当日生产重量卡片 -->
+          <div class="sidebar-card">
+            <div class="card-header">
+              <div class="card-logo">
+                <img src="@/assets/png/icon.png" alt="图标" />
+              </div>
+              <div class="card-title">当日生产重量</div>
+            </div>
+            <div class="card-content">
+              <div class="number-display">
+                <!-- 十万位 -->
+                <div class="unit-box">
+                  <span class="unit">十万</span>
+                  <div class="digit-box">
+                    <span class="digit">{{ weightDigits[0] }}</span>
+                  </div>
+                </div>
+                <!-- 万位 -->
+                <div class="unit-box">
+                  <span class="unit">万</span>
+                  <div class="digit-box">
+                    <span class="digit">{{ weightDigits[1] }}</span>
+                  </div>
+                </div>
+                <!-- 千位 -->
+                <div class="unit-box">
+                  <span class="unit">千</span>
+                  <div class="digit-box">
+                    <span class="digit">{{ weightDigits[2] }}</span>
+                  </div>
+                </div>
+                <!-- 千位分隔符 -->
+                <div class="separator">,</div>
+                <!-- 百位 -->
+                <div class="unit-box">
+                  <span class="unit"></span>
+                  <div class="digit-box">
+                    <span class="digit">{{ weightDigits[3] }}</span>
+                  </div>
+                </div>
+                <!-- 十位 -->
+                <div class="unit-box">
+                  <span class="unit"></span>
+                  <div class="digit-box">
+                    <span class="digit">{{ weightDigits[4] }}</span>
+                  </div>
+                </div>
+                <!-- 个位 -->
+                <div class="unit-box">
+                  <span class="unit"></span>
+                  <div class="digit-box">
+                    <span class="digit">{{ weightDigits[5] }}</span>
+                  </div>
+                </div>
+              </div>
+              <!-- 重量统计项 -->
+              <div class="card-stats">
+                <div class="stat-item-container">
+                  <div class="stat-icon">
+                    <img src="@/assets/png/Group.png" alt="目标重量" />
+                  </div>
+                  <div class="stat-info">
+                    <div class="stat-label">目标生产重量:</div>
+                    <div
+                      ><span class="stat-value">{{ targetWeight }}</span
+                      ><span class="stat-dw">({{ weightUnit }})</span></div
+                    >
+                  </div>
+                </div>
+                <div class="stat-item-container">
+                  <div class="stat-icon">
+                    <img src="@/assets/png/Group.png" alt="重量达成率" />
+                  </div>
+                  <div class="stat-info">
+                    <div class="stat-label">重量达成率:</div>
+                    <div
+                      ><span class="stat-value">{{
+                        weightCompletionRate
+                      }}</span></div
+                    >
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <!-- 设备实时状态 -->
+          <div class="sidebar-card chart-card-small">
+            <div class="card-header">
+              <div class="card-logo">
+                <img src="@/assets/png/icon.png" alt="图标" />
+              </div>
+              <div class="card-title">设备实时状态-按工序</div>
+            </div>
+            <div class="card-content">
+              <div
+                ref="deviceStatusChart"
+                class="process-chart-container"
+              ></div>
+            </div>
+          </div>
+
+          <!-- 各产品合格率 -->
+          <div class="sidebar-card chart-card-large">
+            <div class="card-header">
+              <div class="card-logo">
+                <img src="@/assets/png/icon.png" alt="图标" />
+              </div>
+              <div class="card-title">各产品合格率</div>
+            </div>
+            <div class="card-content">
+              <div
+                ref="qualificationRateChart"
+                class="qualification-rate-chart-container"
+              ></div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </vue-fullscreen>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+import { component as VueFullscreen } from 'vue-fullscreen';
+import { toggleFullscreen } from 'ele-admin';
+
+export default {
+  name: 'DashboardFramework',
+  components: {
+    VueFullscreen
+  },
+  computed: {
+    contentWidth() {
+      return this.$store.state.theme.contentWidth || '100%';
+    },
+    // 固定行:红色行(State=Y)
+    fixedRows() {
+      return this.tableData.filter((item) => item.State === 'Y');
+    },
+    // 非红色行:State=N
+    scrollRows() {
+      return this.tableData.filter((item) => item.State === 'N');
+    },
+    // 重量达成率计算
+    weightCompletionRate() {
+      return ((this.productionWeight / this.targetWeight) * 100).toFixed(1) + '%';
+    }
+  },
+  watch: {
+    isFullscreen: {
+      handler() {
+        this.isFlag = false;
+        this.$nextTick(() => {
+          this.isFlag = true;
+          this.resizeCharts();
+        });
+      },
+      immediate: true
+    },
+    contentWidth: {
+      handler() {
+        clearTimeout(this.resizeTimer);
+        this.isFlag = false;
+        this.resizeTimer = setTimeout(() => {
+          this.$nextTick(() => {
+            this.isFlag = true;
+            this.resizeCharts();
+          });
+        }, 300);
+      },
+      immediate: true
+    }
+  },
+  data() {
+    return {
+      isFullscreen: false,
+      isFlag: false,
+      resizeTimer: null,
+      updateTimer: null,
+      date: '',
+      time: '',
+      week: '',
+
+      // 生产工单表格数据
+      tableData: [
+        { id: 1, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'Y' },
+        { id: 2, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'N' },
+        { id: 3, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'N' },
+        { id: 4, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'N' },
+        { id: 5, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'N' },
+        { id: 6, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'Y' },
+        { id: 7, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'N' },
+        { id: 8, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'Y' },
+        { id: 9, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'Y' },
+        { id: 10, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'Y' },
+        { id: 11, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'Y' },
+        { id: 12, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'Y' },
+        { id: 13, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'N' },
+        { id: 14, name: '项目A', yqsl: '23', ywcsl: '12', scdcl: '10%', kstime: '2023-09-01', jstime: '2023-09-01', dom: '3', State: 'N' },
+      ],
+
+      // 动态数据源
+      productionCount: 588, // 当日生产数量(核心数据)
+      requiredProductionCount: 1000, // 要求生产总数量
+      productionUnit: '件', // 生产数量单位
+      delayedOrderCount: 25, // 已延期生产单数
+      pendingOrderCount: 53, // 待生产单数
+
+      productionWeight: 123456, // 当日生产重量(核心数据)
+      targetWeight: 2000, // 目标生产重量
+      weightUnit: 'kg', // 重量单位
+
+      // 图表相关
+      countDigits: [], // 拆分后的数量数字数组
+      weightDigits: [], // 拆分后的重量数字数组
+      processChartInstance: null, // 工序在岗人数ECharts实例
+      deviceChartInstance: null, // 设备状态ECharts实例
+      productChartInstance: null, // 产品TOP10图表实例
+      qualificationChartInstance: null, // 合格率图表实例
+      // 工序在岗人数数据
+      processPersonnelData: [
+        { name: '冲压工序', value: 28 },
+        { name: '焊接工序', value: 15 },
+        { name: '组装工序', value: 32 },
+        { name: '检测工序', value: 10 }
+      ],
+      // 设备实时状态数据
+      deviceStatusData: [
+        { name: '运行中', value: 25 },
+        { name: '待机', value: 10 },
+        { name: '故障', value: 5 },
+        { name: '维护中', value: 3 }
+      ],
+      // 产品生产数量TOP10数据
+      productTop10Data: [
+        { name: '产品A-01', value: 985 },
+        { name: '产品B-03', value: 860 },
+        { name: '产品C-02', value: 752 },
+        { name: '产品D-05', value: 628 },
+        { name: '产品E-01', value: 590 },
+        { name: '产品F-04', value: 485 },
+        { name: '产品G-02', value: 376 },
+        { name: '产品H-01', value: 290 },
+        { name: '产品I-03', value: 215 },
+        { name: '产品J-02', value: 180 }
+      ],
+      // 各产品合格率数据
+      qualificationRateData: {
+        productNames: ['产品一', '产品二', '产品三', '产品四', '产品五', '产品六', '产品七'],
+        rates: [90, 98, 85, 95, 96, 99, 97] // 合格率(%)
+      }
+    };
+  },
+  created() {
+    this.updateTime();
+    this.updateTimer = setInterval(this.updateTime, 1000);
+  },
+  mounted() {
+    this.formatNumber(this.productionCount, 'count');
+    this.formatNumber(this.productionWeight, 'weight');
+    this.sortTableData(); // 排序Y状态数据置顶
+
+    this.$nextTick(() => {
+      this.initProcessPersonnelChart();
+      this.initDeviceStatusChart();
+      this.initProductTop10Chart();
+      this.initQualificationRateChart();
+      this.isFlag = true;
+
+      // 窗口 resize 时仅处理图表缩放
+      window.addEventListener('resize', () => {
+        clearTimeout(this.resizeTimer);
+        this.resizeTimer = setTimeout(() => {
+          this.resizeCharts();
+        }, 200);
+      });
+    });
+  },
+  beforeDestroy() {
+    clearInterval(this.updateTimer);
+    clearTimeout(this.resizeTimer);
+    window.removeEventListener('resize', () => {
+      clearTimeout(this.resizeTimer);
+      this.resizeTimer = setTimeout(() => {
+        this.resizeCharts();
+      }, 200);
+    });
+
+    // 销毁图表实例(避免内存泄漏)
+    [
+      this.processChartInstance,
+      this.deviceChartInstance,
+      this.productChartInstance,
+      this.qualificationChartInstance
+    ].forEach((chart) => {
+      if (chart) {
+        chart.dispose();
+      }
+    });
+  },
+  methods: {
+    // 表格数据排序 - State为"Y"的置顶
+    sortTableData() {
+      this.tableData.sort((a, b) => {
+        if (a.State === 'Y' && b.State !== 'Y') return -1;
+        if (a.State !== 'Y' && b.State === 'Y') return 1;
+        return 0;
+      });
+    },
+
+    onFullscreen() {
+      this.isFullscreen = !this.isFullscreen;
+    },
+
+    // 更新时间
+    updateTime() {
+      const now = new Date();
+      const format = (num) => num.toString().padStart(2, '0');
+      this.time = `${format(now.getHours())}:${format(now.getMinutes())}:${format(now.getSeconds())}`;
+      this.date = `${now.getFullYear()}年${now.getMonth() + 1}月${now.getDate()}日`;
+      const weekMap = { 0: '日', 1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六' };
+      this.week = `星期${weekMap[now.getDay()]}`;
+    },
+
+    // 格式化数字为6位数组(补零)
+    formatNumber(num, target) {
+      const paddedStr = num.toString().padStart(6, '0');
+      const digits = paddedStr.split('').map(Number);
+      this[target === 'count' ? 'countDigits' : 'weightDigits'] = digits;
+    },
+
+    // 工序在岗人数饼图
+    initProcessPersonnelChart() {
+      const chartDom = this.$refs.processPersonnelChart;
+      if (!chartDom) return;
+
+      this.processChartInstance = echarts.init(chartDom);
+      const totalPersonnel = this.processPersonnelData.reduce((sum, item) => sum + item.value, 0);
+      const option = {
+        backgroundColor: 'transparent',
+        tooltip: {
+          trigger: 'item',
+          formatter: '{b}:{c}人',
+          textStyle: { color: '#000', whiteSpace: 'normal', fontSize: 14 },
+          backgroundColor: '#fff',
+          borderColor: '#2068b8',
+          borderWidth: 1,
+          padding: 10
+        },
+        legend: {
+          orient: 'vertical',
+          right: 20,
+          top: 'center',
+          textStyle: { color: '#e9ecef', fontSize: 12, padding: [0, 8] },
+          itemWidth: 12,
+          itemHeight: 10,
+          itemGap: 20,
+          formatter: '{name}',
+          align: 'left'
+        },
+        series: [{
+          name: '在岗人数',
+          type: 'pie',
+          radius: ['45%', '80%'],
+          center: ['30%', '50%'],
+          avoidLabelOverlap: false,
+          label: {
+            show: true,
+            position: 'center',
+            formatter: `{total|${totalPersonnel}}\n{unit|在岗人员数(位)}`,
+            textStyle: {
+              textAlign: 'center',
+              lineHeight: 22,
+              rich: {
+                total: { fontSize: 18, fontWeight: 'bold', color: '#fff' },
+                unit: { fontSize: 8, color: '#ccc', marginTop: 5 }
+              }
+            }
+          },
+          data: this.processPersonnelData,
+          color: ['#1163fb', '#14cdc9', '#f9bb19', '#712ed2', '#ec4899', '#06b6d4', '#6b7280']
+        }]
+      };
+      this.processChartInstance.setOption(option);
+    },
+
+    // 设备实时状态饼图
+    initDeviceStatusChart() {
+      const chartDom = this.$refs.deviceStatusChart;
+      if (!chartDom) return;
+
+      this.deviceChartInstance = echarts.init(chartDom);
+      const totalDevice = this.deviceStatusData.reduce((sum, item) => sum + item.value, 0);
+      const option = {
+        backgroundColor: 'transparent',
+        tooltip: {
+          trigger: 'item',
+          formatter: '{b}:{c}台',
+          textStyle: { color: '#000', whiteSpace: 'normal', fontSize: 14 },
+          backgroundColor: '#fff',
+          borderColor: '#2068b8',
+          borderWidth: 1,
+          padding: 10
+        },
+        legend: {
+          orient: 'vertical',
+          right: 20,
+          top: 'center',
+          textStyle: { color: '#e9ecef', fontSize: 12, padding: [0, 8] },
+          itemWidth: 12,
+          itemHeight: 10,
+          itemGap: 20,
+          formatter: '{name}',
+          align: 'left'
+        },
+        series: [{
+          name: '设备状态',
+          type: 'pie',
+          radius: ['45%', '80%'],
+          center: ['30%', '50%'],
+          avoidLabelOverlap: false,
+          label: {
+            show: true,
+            position: 'center',
+            formatter: `{total|${totalDevice}}\n{unit|设备总数(台)}`,
+            textStyle: {
+              textAlign: 'center',
+              lineHeight: 22,
+              rich: {
+                total: { fontSize: 18, fontWeight: 'bold', color: '#fff' },
+                unit: { fontSize: 8, color: '#ccc', marginTop: 5 }
+              }
+            }
+          },
+          data: this.deviceStatusData,
+          color: ['#1163fb', '#14cdc9', '#ff4d4f', '#f9bb19']
+        }]
+      };
+      this.deviceChartInstance.setOption(option);
+    },
+
+    // 产品TOP10图表
+    initProductTop10Chart() {
+      const chartDom = this.$refs.productTop10Chart;
+      if (!chartDom) return;
+
+      this.productChartInstance = echarts.init(chartDom);
+      const sortedProductData = [...this.productTop10Data].sort((a, b) => b.value - a.value);
+      const salvProName = sortedProductData.map((item) => item.name);
+      const salvProValue = sortedProductData.map((item) => item.value);
+      const maxValue = Math.max(...salvProValue);
+      const salvProMax = salvProValue.map(() => maxValue);
+
+      const option = {
+        backgroundColor: 'transparent',
+        grid: { left: '2%', right: '2%', bottom: '8%', top: '5%', containLabel: true },
+        tooltip: { trigger: 'axis', axisPointer: { type: 'none' }, borderColor: '#2068b8', formatter: '{b} 生产数量:{c}' },
+        xAxis: {
+          show: true,
+          type: 'value',
+          axisTick: { show: true, lineStyle: { color: 'rgba(255, 255, 255, 0.3)' } },
+          axisLabel: { color: '#ccc', fontSize: 10, formatter: '{value}' },
+          splitLine: { show: false }
+        },
+        yAxis: [
+          {
+            type: 'category',
+            inverse: true,
+            axisLabel: { show: true, textStyle: { color: '#ccc', fontSize: '12' } },
+            splitLine: { show: false },
+            axisTick: { show: false },
+            axisLine: { show: false },
+            data: salvProName
+          },
+          {
+            type: 'category',
+            inverse: true,
+            axisTick: 'none',
+            axisLine: 'none',
+            show: true,
+            axisLabel: { textStyle: { color: '#fff', fontSize: '12' } },
+            data: salvProValue
+          }
+        ],
+        series: [
+          {
+            name: '生产数量',
+            type: 'bar',
+            zlevel: 1,
+            itemStyle: {
+              normal: {
+                barBorderRadius: 30,
+                color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+                  { offset: 0, color: 'rgb(31, 56, 109,1)' },
+                  { offset: 1, color: 'rgb(1, 83, 255,1)' }
+                ])
+              }
+            },
+            barWidth: 5,
+            data: salvProValue
+          },
+          {
+            name: '背景',
+            type: 'bar',
+            barWidth: 5,
+            barGap: '-100%',
+            data: salvProMax,
+            itemStyle: { normal: { color: 'rgba(33, 59, 107,1)', barBorderRadius: 30 } }
+          }
+        ]
+      };
+      this.productChartInstance.setOption(option);
+    },
+
+    // 产品合格率折线图
+    initQualificationRateChart() {
+      const chartDom = this.$refs.qualificationRateChart;
+      if (!chartDom) return;
+
+      this.qualificationChartInstance = echarts.init(chartDom);
+      const { productNames, rates } = this.qualificationRateData;
+
+      const option = {
+        backgroundColor: 'transparent',
+        grid: { left: '2%', right: '2%', bottom: '12%', top: '10%', containLabel: true },
+        tooltip: {
+          trigger: 'axis',
+          formatter: '{b} 合格率: {c}%',
+          backgroundColor: '#fff',
+          borderColor: '#2068b8',
+          borderWidth: 1,
+          padding: 10,
+          textStyle: { color: '#000', fontSize: 14 },
+          axisPointer: {
+            type: 'line',
+            axis: 'x',
+            lineStyle: {
+              color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+                { offset: 0, color: 'rgb(31, 56, 109,1)' },
+                { offset: 1, color: 'rgb(1, 83, 255,1)' }
+              ]),
+              width: 1.5,
+              type: 'solid'
+            }
+          }
+        },
+        xAxis: {
+          type: 'category',
+          data: productNames,
+          axisTick: { show: true, lineStyle: { color: '#203454' }, alignWithLabel: true },
+          axisLabel: { color: '#ccc', fontSize: 10, align: 'center', offset: 10 },
+          axisLine: { lineStyle: { color: '#203454' } }
+        },
+        yAxis: {
+          type: 'value',
+          min: 80,
+          max: 100,
+          axisLabel: { color: '#ccc', fontSize: 12, formatter: '{value}%' },
+          axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.3)' } },
+          splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)' } }
+        },
+        series: [{
+          name: '合格率',
+          type: 'line',
+          data: rates,
+          symbol: 'circle',
+          symbolSize: 8,
+          smooth: true,
+          itemStyle: { color: '#FFF' },
+          lineStyle: { color: 'rgba(39, 102, 203)', width: 2 },
+          emphasis: {
+            itemStyle: { color: '#fff', borderColor: '#14cdc9', borderWidth: 2 }
+          }
+        }]
+      };
+      this.qualificationChartInstance.setOption(option);
+    },
+
+    // 图表自适应缩放
+    resizeCharts() {
+      [
+        this.processChartInstance,
+        this.deviceChartInstance,
+        this.productChartInstance,
+        this.qualificationChartInstance
+      ].forEach((chart) => {
+        if (chart) {
+          chart.resize();
+        }
+      });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+[v-cloak] {
+  display: none;
+}
+
+.box-container {
+  font-size: 16px;
+  font-family: 'AlibabaPuHuiTi';
+  background-image: url('@/assets/png/bj.png');
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  background-color: rgb(8, 29, 65);
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  height: 100%;
+  .box-header {
+    background-image: url('@/assets/border2.png');
+    background-repeat: no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    width: 100% !important;
+    height: 10%;
+    justify-content: space-between;
+    align-items: center;
+
+    .logo {
+      width: 15%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .title {
+      font-family: '优设标题黑';
+      font-size: 2.85rem;
+      flex: 1;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      color: #fff;
+      transform: translate(2%, -10%);
+      letter-spacing: 0.4rem;
+    }
+
+    .time {
+      width: 15%;
+      height: 100%;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: flex-end;
+      font-size: 0.8rem;
+      color: #7fa7ce;
+    }
+
+    .time-display {
+      margin: 0 15px 0 0;
+      color: rgb(210 215 221);
+      font-size: 0.8rem;
+      font-weight: 600;
+    }
+
+    .time-text {
+      padding: 0 5px;
+    }
+
+    .fullscreen-toggle {
+      margin: 0 15px 0 0;
+      cursor: pointer;
+    }
+  }
+
+  .box-middle {
+    width: 100% !important;
+    flex: 1;
+    display: flex;
+    gap: 0.5rem;
+    padding: 0.5rem 1rem;
+    box-sizing: border-box;
+
+    .box-middle-sidebar {
+      flex-basis: 38%;
+      max-width: 420px;
+      min-width: 280px;
+      flex-shrink: 1;
+      flex-grow: 0;
+      display: flex;
+      flex-direction: column;
+      gap: 1rem;
+
+      .sidebar-card {
+        background-color: rgba(8, 29, 65, 0.8);
+        background-size: 100% 100%;
+        background-repeat: no-repeat;
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        color: #fff;
+        font-size: 1rem;
+        border: 1px solid #124c77;
+        overflow: hidden;
+
+        &.chart-card-small {
+          flex: 0.8 !important;
+        }
+
+        &.chart-card-large {
+          flex: 1.2 !important;
+        }
+
+        .card-header {
+          display: flex;
+          align-items: center;
+          padding: 0.2rem 0.5rem;
+          gap: 0.3rem;
+          background-color: rgb(0, 64, 133);
+          border-bottom: 1px solid #124c77;
+          font-family: '优设标题黑';
+          font-size: 1.2rem;
+          color: #fff;
+
+          .card-logo {
+            width: 1.3rem;
+            height: 1.3rem;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            img {
+              width: 100%;
+              height: 100%;
+              object-fit: contain;
+            }
+          }
+
+          .card-title {
+            font-size: clamp(0.85rem, 1.5vw, 0.95rem);
+            letter-spacing: 0.05rem;
+          }
+        }
+
+        .card-content {
+          flex: 1;
+          display: flex;
+          flex-direction: column;
+          padding: 0 1rem 0.5rem 1rem;
+          box-sizing: border-box;
+          overflow: hidden;
+        }
+
+        .number-display {
+  flex: 3;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 0.5rem;
+  box-sizing: border-box;
+  overflow: hidden;
+  // 关键新增:增加顶部外边距,与顶部拉开距离
+  margin-top: 0.8rem; 
+        }
+
+        .card-stats {
+          display: flex;
+          justify-content: center;
+          gap: 0.8rem;
+          padding: 0.3rem 0;
+          flex-wrap: wrap;
+          flex-shrink: 0;
+          width: 100%;
+          box-sizing: border-box;
+
+          .stat-item-container {
+            background-color: rgba(0, 123, 255, 0.1);
+            border: 1px solid rgba(88, 137, 196);
+            border-radius: 4px;
+            display: flex;
+            align-items: flex-start;
+            gap: 0.4rem;
+            flex: 1 0 calc(50% - 1rem);
+            max-width: calc(50% - 1rem);
+            box-sizing: border-box;
+            padding: 0.3rem;
+
+            .stat-icon {
+              width: 1.6rem;
+              height: 1.6rem;
+              padding: 0.4rem;
+              background-color: rgba(0, 64, 133, 0.3);
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              flex-shrink: 0;
+
+              img {
+                width: 100%;
+                height: 100%;
+                object-fit: contain;
+              }
+            }
+
+            .stat-info {
+              display: flex;
+              flex-direction: column;
+              justify-content: center;
+              align-items: center;
+              height: 100%;
+
+              .stat-label {
+                color: #fff;
+                font-size: clamp(0.7rem, 1.2vw, 0.8rem);
+                white-space: nowrap;
+                line-height: 1.1;
+                letter-spacing: 1px;
+              }
+
+              .stat-value {
+                font-weight: bold;
+                white-space: nowrap;
+                color: #3bfff2;
+                font-size: clamp(0.7rem, 1.2vw, 0.8rem);
+                line-height: 1.1;
+              }
+              .stat-dw {
+                font-weight: bold;
+                white-space: nowrap;
+                color: #fff;
+                font-size: clamp(0.7rem, 1.2vw, 0.8rem);
+                line-height: 1.1;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .box-middle-content {
+      flex: 1;
+      padding: 0 0.5rem;
+      display: flex;
+      flex-direction: column;
+      gap: 1rem;
+
+      .content-card-container {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+
+        .sidebar-card {
+          flex: 1;
+          background-size: 100% 100%;
+          background-repeat: no-repeat;
+          display: flex;
+          flex-direction: column;
+          color: #fff;
+          font-size: 1rem;
+          overflow: hidden;
+
+          .card-header {
+            display: flex;
+            align-items: center;
+            padding: 0.2rem 0.5rem;
+            gap: 0.3rem;
+            font-family: '优设标题黑';
+            font-size: 1.2rem;
+            color: #fff;
+
+            .card-logo {
+              width: 1.3rem;
+              height: 1.3rem;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+
+              img {
+                width: 100%;
+                height: 100%;
+                object-fit: contain;
+              }
+            }
+
+            .card-title {
+              font-size: clamp(0.85rem, 1.5vw, 0.95rem);
+              letter-spacing: 0.05rem;
+            }
+          }
+
+          .content-empty {
+            flex: 1;
+            color: #7fa7ce;
+            display: flex;
+            flex-direction: column;
+
+            .Remark {
+              font-size: 0.7rem;
+              letter-spacing: 0.1rem;
+              color: #fff;
+              text-align: right;
+            }
+
+            .content-box {
+              display: flex;
+              flex: 1;
+              gap: 2vw;
+              padding: 0.5rem;
+
+              .box-item {
+                flex: 1;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                flex-direction: column;
+                gap: 0.5vw;
+
+                .item-img {
+                  font-family: 'LCD2B';
+                  aspect-ratio: 100 / 64;
+                  width: 80%;
+                  background-image: url('@/assets/png/item_img.png');
+                  background-repeat: no-repeat;
+                  background-size: 100% 100%;
+                  color: #fff;
+                  font-size: clamp(1.5rem, 4vw, 3rem);
+                  font-weight: 600;
+                  display: flex;
+                  align-items: center;
+                  justify-content: center;
+                }
+
+                .item-text {
+                  font-size: clamp(0.8rem, 1.5vw, 1rem);
+                  font-weight: 600;
+                  color: #fff;
+                  text-align: center;
+                  padding: 0 0.2rem;
+                }
+              }
+            }
+          }
+        }
+      }
+
+      .content-top-container {
+        flex: 2;
+        display: flex;
+        flex-direction: column;
+
+        .sidebar-card {
+          flex: 1;
+          background-size: 100% 100%;
+          background-repeat: no-repeat;
+          display: flex;
+          flex-direction: column;
+          color: #fff;
+          font-size: 1rem;
+          overflow: hidden;
+          border: 1px solid #124c77;
+
+          .card-header {
+            display: flex;
+            align-items: center;
+            padding: 0.2rem 0.5rem;
+            gap: 0.3rem;
+            background-color: rgb(0, 64, 133);
+            border-bottom: 1px solid #124c77;
+            font-family: '优设标题黑';
+            font-size: 1.2rem;
+            color: #fff;
+            flex-shrink: 0;
+
+            .card-logo {
+              width: 1.3rem;
+              height: 1.3rem;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+
+              img {
+                width: 100%;
+                height: 100%;
+                object-fit: contain;
+              }
+            }
+
+            .card-title {
+              font-size: clamp(0.85rem, 1.5vw, 0.95rem);
+              letter-spacing: 0.05rem;
+            }
+          }
+
+          /* 表格容器:移除滚动相关样式 */
+          .content-table {
+            flex: 1;
+            padding: 8px;
+            box-sizing: border-box;
+
+            /* 主表格样式 */
+            .main-table {
+              width: 100%;
+              border-collapse: collapse;
+              table-layout: fixed;
+            }
+
+            /* 表头样式(保留固定定位但移除滚动关联) */
+            .main-table thead th {
+              background-color: rgba(3, 29, 66, 0.95);
+              padding: 0.5rem 0.2rem;
+              text-align: center;
+              font-weight: bold;
+              color: #0b6fd5;
+              border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+              font-size: 0.8rem;
+              white-space: nowrap;
+              overflow: hidden;
+              text-overflow: ellipsis;
+            }
+
+            /* 表格行样式 */
+            .table-row {
+              width: 100%;
+            }
+
+            /* 表格单元格样式 */
+            .table-cell {
+              padding: 0.2rem;
+              text-align: center;
+              color: #e9ecef;
+              font-size: 0.8rem;
+              border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+              white-space: nowrap;
+              overflow: hidden;
+              text-overflow: ellipsis;
+            }
+
+            /* 红色行样式(State=Y) */
+            .state-red {
+              .table-cell {
+                background-color: #ff1514 !important;
+                color: #fff !important;
+              }
+            }
+
+            /* 行hover效果 */
+            .table-row:hover .table-cell {
+              background-color: rgba(3, 29, 66, 0.8) !important;
+            }
+
+            /* 无数据占位样式 */
+            .empty-cell {
+              text-align: center;
+              padding: 1rem;
+              color: #7fa7ce;
+              border: none !important;
+              height: 100%;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/* 数字显示样式 */
+.unit-box {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 3rem;
+  height: 5rem;
+  position: relative;
+}
+
+.unit {
+  font-size: 14px;
+  color: #a4b9d7;
+  text-align: center;
+  position: absolute;
+  top: -22px;
+  left: 50%;
+  transform: translateX(-50%);
+  margin-bottom: 0;
+  width: 35px;
+  white-space: nowrap;
+}
+
+.digit-box {
+  background-image: url('@/assets/png/num_bj.png');
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  color: #fff;
+  padding: 0 15px;
+  height: 5rem;
+  font-size: clamp(1.5rem, 4vw, 2.2rem);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-weight: bold;
+  border-radius: 2px;
+}
+
+.separator {
+  color: #fff;
+  font-size: 2.5rem;
+  font-weight: 700;
+  margin: 0 5px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  line-height: 1;
+}
+
+/* 图表容器样式 */
+.process-chart-container {
+  width: 100%;
+  height: 100%;
+  min-height: 160px;
+}
+
+.product-top10-chart-container {
+  width: 100%;
+  height: 100%;
+  min-height: 260px;
+}
+
+.qualification-rate-chart-container {
+  width: 100%;
+  height: 100%;
+  min-height: 260px;
+}
+
+/* 响应式适配 */
+@media (max-width: 1200px) {
+  .box-container {
+    .box-header {
+      .time {
+        width: 40%;
+        font-size: 0.75rem;
+      }
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .box-container {
+    .box-header {
+      .title {
+        font-size: 2rem;
+      }
+      .time {
+        width: 45%;
+        font-size: 0.7rem;
+      }
+    }
+    .box-middle .box-middle-sidebar {
+      flex-basis: 35%;
+      min-width: 220px;
+
+      .sidebar-card .card-header .card-title {
+        font-size: 0.85rem;
+      }
+    }
+    .box-middle .box-middle-content .content-top-container .sidebar-card {
+      .card-header .card-title {
+        font-size: 0.85rem;
+      }
+      .content-table .main-table thead th {
+        font-size: 0.7rem;
+        padding: 0.3rem 0.1rem;
+      }
+      .content-table .table-cell {
+        font-size: 0.7rem;
+        padding: 0.3rem 0.1rem;
+      }
+    }
+  }
+}
+</style>
+
+<style>
+/* 全屏图标样式 */
+.el-icon-_screen-full,
+.el-icon-_screen-restore {
+  font-size: 1.2rem;
+  cursor: pointer;
+  transition: all 0.3s;
+  color: #7fa7ce;
+}
+
+.el-icon-_screen-full:hover,
+.el-icon-_screen-restore:hover {
+  color: #fff;
+  transform: scale(1.1);
+}
+</style>

+ 1 - 1
vue.config.js

@@ -36,7 +36,7 @@ module.exports = {
         // target: 'http://192.168.1.139:18086', // 粟
         // target: 'http://192.168.1.132:18086', // 徐1
         // target: 'http://192.168.1.134:18086', //徐2
-        target: 'http://192.168.1.116:18086',
+        target: 'http://192.168.1.125:18086',
         changeOrigin: true, // 只有这个值为true的情况下 才表示开启跨域
         pathRewrite: {
           '^/api': ''

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor