|
|
@@ -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>
|