From 5636ee8eb5d1108668d0abf1e425268bde14922d Mon Sep 17 00:00:00 2001
From: hyingbo <1363390067@qq.com>
Date: 星期一, 01 九月 2025 18:01:02 +0800
Subject: [PATCH] mdc首页开发

---
 src/views/dashboard/TodoList.vue                   |    2 
 src/assets/index.png                               |    0 
 src/components/page/GlobalHeader.vue               |    3 
 src/views/dashboard/Analysis.vue                   |   39 +
 src/views/dashboard/mdcIndex/MdcManagerSignage.vue | 1144 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/api/signage.js                                 |   12 
 6 files changed, 1,188 insertions(+), 12 deletions(-)

diff --git a/src/api/signage.js b/src/api/signage.js
index 8c0a115..68f02af 100644
--- a/src/api/signage.js
+++ b/src/api/signage.js
@@ -1,6 +1,18 @@
 import { getAction, deleteAction, putAction, postAction, httpAction } from '@/api/manage'
 
 export default {
+  // 鑾峰彇鎵�鏈夎溅闂翠俊鎭�
+  getAllWorkShop: id => getAction('/mdc/home/getAllWorkShop', {}),
   // 鏍规嵁鐢ㄦ埛ID鑾峰彇鐢ㄦ埛淇℃伅
   getUserByIdApi: id => getAction('sys/api/getUserById', { id }),
+  // 璁惧杩愯鐘舵��
+  getEquipmentStatusStatisticsApi: productionId => getAction('/mdc/home/equipmentStatusStatistics', { productionId }),
+  // 璁惧鍒╃敤鐜�
+  getEquipmentUtilizationStatisticsApi: productionId => getAction('/mdc/home/equipmentUtilizationStatistics', { productionId }),
+  // 鍏ㄥ巶鍓�15澶╁埄鐢ㄧ巼鎶樼嚎鍥�
+  getEquipmentDayUtilizationStatisticsApi: productionId => getAction('/mdc/home/equipmentDayUtilizationStatistics', { productionId }),
+  // 璁惧OEE缁熻
+  getEquipmentOEEStatistics: productionId => getAction('/mdc/home/equipmentOEEStatistics', { productionId }),
+  // 璁惧OEE鍜屽埄鐢ㄧ巼瀵规瘮
+  getEquipmentMonthStatisticsApi: productionId => getAction('/mdc/home/equipmentMonthStatistics', { productionId }),
 }
\ No newline at end of file
diff --git a/src/assets/index.png b/src/assets/index.png
new file mode 100644
index 0000000..b6fb451
--- /dev/null
+++ b/src/assets/index.png
Binary files differ
diff --git a/src/components/page/GlobalHeader.vue b/src/components/page/GlobalHeader.vue
index 4463912..68f95cd 100644
--- a/src/components/page/GlobalHeader.vue
+++ b/src/components/page/GlobalHeader.vue
@@ -17,8 +17,7 @@
         :type="collapsed ? 'menu-unfold' : 'menu-fold'"
         @click="toggle"/>
 
-      <span v-if="device === 'desktop'">娆㈣繋杩涘叆 Jeecg-Boot 浼佷笟绾т綆浠g爜骞冲彴</span>
-      <span v-else>Jeecg-Boot</span>
+      <span v-if="device === 'desktop'">娆㈣繋杩涘叆 MDC鏅烘収杞﹂棿</span>
 
       <user-menu :theme="theme"/>
     </div>
diff --git a/src/views/dashboard/Analysis.vue b/src/views/dashboard/Analysis.vue
index 156fa95..f3a16fb 100644
--- a/src/views/dashboard/Analysis.vue
+++ b/src/views/dashboard/Analysis.vue
@@ -1,17 +1,25 @@
 <template>
-  <Component :is="currentSignage" :userType="userType" :productionCode="productionCode"
+  <Component :is="currentSignage"
+             :userType="userType"
+             :productionCode="productionCode"
              :workshopSectionProductionCode="workshopSectionProductionCode"
+             v-if="[1,2,3,4].includes(userType)"
              >
   </Component>
+  <div v-else>  <!-- 涓庣粍浠舵覆鏌撲簰鏂� -->
+    <img src="@/assets/index.png" style="width: 100%;height: 785px">
+  </div>
 </template>
 
 <script>
   import signageApi from '@/api/signage'
+  import MdcManagerSignage from './mdcIndex/MdcManagerSignage.vue'
   import DncManagerSignage from './dncIndex/DncManagerSignage.vue'
 
   export default {
     name: "Analysis",
     components: {
+      MdcManagerSignage,
       DncManagerSignage
     },
     data() {
@@ -20,7 +28,7 @@
         productionCode: '',
         branchFactoryProductionCode: '',
         workshopSectionProductionCode: '',
-        userType: ''
+        userType: '',
       }
     },
     created() {
@@ -28,20 +36,27 @@
     },
     methods: {
       showModuleByUserInfo() {
-        const id = JSON.parse(localStorage.getItem('pro__Login_Userinfo')).value.id
+        // 瀹夊叏澶勭悊锛氬厛鍒ゆ柇localStorage涓槸鍚﹀瓨鍦ㄧ敤鎴蜂俊鎭紝閬垮厤JSON.parse鎶ラ敊
+        const userInfoStr = localStorage.getItem('pro__Login_Userinfo')
+        if (!userInfoStr) {
+          this.currentSignage = '' // 鏃犵敤鎴蜂俊鎭椂涓嶆覆鏌撶粍浠�
+          return
+        }
+
+        const id = JSON.parse(userInfoStr).value.id
         signageApi.getUserByIdApi(id)
           .then(res => {
-            console.log("res", res.userType)
-            this.userType = res.userType
+            this.userType = res.userType // 璧嬪�煎悗鑷姩瑙﹀彂鏉′欢娓叉煋鍒ゆ柇
+            // 鏍规嵁userType鍖归厤瀵瑰簲缁勪欢锛堟仮澶峜ase1鍜宑ase4鐨勯�昏緫锛�
             switch (this.userType) {
               // case 1:
               //   //鍒�鍏风鐞�
               //   this.currentSignage = 'EquipmentSignage'
               //   break
-              // case 2:
-              //   //mdc
-              //   this.currentSignage = 'WorkshopSectionSignage'
-              //   break
+              case 2:
+                // mdc
+                this.currentSignage = 'MdcManagerSignage'
+                break
               case 3:
                 //dnc
                 this.currentSignage = 'DncManagerSignage'
@@ -55,6 +70,12 @@
                 break
             }
           })
+          .catch(err => {
+            // 鎺ュ彛璇锋眰澶辫触鏃讹紝榛樿鏄剧ず鍥剧墖
+            console.error('鑾峰彇鐢ㄦ埛绫诲瀷澶辫触锛�', err)
+            this.userType = ''
+            this.currentSignage = ''
+          })
       }
     }
   }
diff --git a/src/views/dashboard/TodoList.vue b/src/views/dashboard/TodoList.vue
index 724d8b5..b1bec77 100644
--- a/src/views/dashboard/TodoList.vue
+++ b/src/views/dashboard/TodoList.vue
@@ -587,7 +587,7 @@
   margin: 0;
   box-sizing: border-box;
   /* 鏂板锛氳缃鍣ㄦ渶澶ч珮搴︼紙鍙牴鎹〉闈㈠竷灞�璋冩暣锛屽500px/80vh锛� */
-  max-height: 80vh;
+  max-height: 100vh;
   /* 鏂板锛氬瀭鐩存柟鍚戞孩鍑烘椂鏄剧ず婊氬姩鏉★紝姘村钩鏂瑰悜婧㈠嚭闅愯棌锛堥伩鍏嶅竷灞�閿欎贡锛� */
   overflow-y: auto;
   overflow-x: hidden;
diff --git a/src/views/dashboard/mdcIndex/MdcManagerSignage.vue b/src/views/dashboard/mdcIndex/MdcManagerSignage.vue
new file mode 100644
index 0000000..276a717
--- /dev/null
+++ b/src/views/dashboard/mdcIndex/MdcManagerSignage.vue
@@ -0,0 +1,1144 @@
+<template>
+  <div class="home-container">
+    <div class="tab-nav">
+      <div
+        v-for="(tab, index) in tabList"
+        :key="index"
+        :class="['tab-item', activeTab === index ? 'tab-active' : '']"
+        @click="handleTabChange(index)"
+      >
+        {{ tab.label }}
+      </div>
+    </div>
+
+
+    <div class="chart-container">
+      <div class="left-cards">
+        <div class="card left-cards-card">
+          <div id="running_state_chart" style="width:100%;height: 45%;max-height: 45vh"></div>
+          <div id="efficiency_chart" style="width: 100%;height: 55%;max-height: 55vh"></div>
+        </div>
+      </div>
+
+      <div class="right-cards">
+        <div class="card right-top-card">
+          <div id="first15DaysEfficiency_chart" style="width:100%;height: 100%;max-height: 50vh"></div>
+        </div>
+
+        <div class="right-bottom-card">
+          <div class="card right-bottom-left-card">
+            <div id="bar_chart" style="width:100%;height: 100%;max-height: 50vh"></div>
+          </div>
+          <div class="card right-bottom-right-card">
+            <div id="double_bar_chart" style="width:100%;height: 100%;max-height: 50vh"></div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+
+import signageApi from '@/api/signage'
+import moment from 'moment'
+
+export default {
+  name: 'DncManagerSignage',
+  components: {},
+  data() {
+    return {
+      tabList: [],
+      activeTab: -1, // 鍒濆鏃犻�変腑椤电锛�-1琛ㄧず鏈�夋嫨鐘舵�侊級
+      firstEnterClientWidth: null,
+      pieChartRadius: ['45%', '55%'],
+      normalPieChartRadius: ['45%', '55%'],
+      currentPageProductionId: null,
+      runningStateChart: '',
+      efficiencyChart: '',
+      efficiencyData: [],
+      barChart: '',
+      barChartData: [],
+      doubleBarChart: '',
+      doubleBarChartData: {},
+      runningStateChartDataRequireFinished: false,
+      first15DaysEfficiencyChart: '',
+      first15DaysEfficiencyData: {
+        dataList: [],
+        dateList: []
+      },
+      runningStateData: [
+        { value: '0', name: '鍏虫満' },
+        { value: '0', name: '鎶ヨ' },
+        { value: '0', name: '寰呮満' },
+        { value: '0', name: '杩愯' }
+      ]
+    }
+  },
+  mounted() {
+    window.addEventListener('resize', this.handleWindowResize)
+    // this.re_drawPieChart()
+    this.getAllWorkShop()
+    this.getChartDataByApi()
+  },
+  beforeDestroy() {
+    // 缁勪欢閿�姣佸墠绉婚櫎鐩戝惉
+    window.removeEventListener('resize', this.handleWindowResize)
+    this.destroyEchartsInstances()
+  },
+  methods: {
+
+    getAllWorkShop() {
+      this.tabList = []
+      signageApi.getAllWorkShop()
+        .then(res => {
+          if (res.success) {
+            this.tabList = res.result.map(workshop => ({
+              label: workshop.productionName,
+              factoryId: workshop.id
+            }))
+
+            if (this.tabList.length > 0) {
+              this.activeTab = -1
+              this.destroyEchartsInstances()
+              this.getChartDataByApi()
+            }
+          } else {
+            this.tabList = []
+            this.activeTab = -1
+          }
+        })
+        .catch(err => {
+          this.tabList = []
+          this.activeTab = -1
+          this.destroyEchartsInstances()
+          this.getChartDataByApi()
+        })
+    },
+
+    destroyEchartsInstances() {
+      const charts = [
+        this.runningStateChart,
+        this.efficiencyChart,
+        this.first15DaysEfficiencyChart,
+        this.barChart,
+        this.doubleBarChart
+      ]
+      charts.forEach(chart => {
+        if (chart && chart.dispose) chart.dispose()
+      })
+    },
+
+    handleTabChange(index) {
+      // 1. 濡傛灉鐐瑰嚮鐨勬槸鈥滃凡閫変腑鐨勯〉绛锯�濓紝鎵ц鈥滃彇娑堥�変腑鈥濋�昏緫
+      if (this.activeTab === index) {
+        this.activeTab = -1; // 閲嶇疆涓烘湭閫変腑鐘舵��
+        this.destroyEchartsInstances(); // 閿�姣佹墍鏈夊浘琛ㄥ疄渚�
+        this.getChartDataByApi();
+        return;
+      }
+
+      // 2. 甯歌鍒囨崲椤电閫昏緫锛堝師鏈夐�昏緫淇濈暀锛�
+      this.activeTab = index;
+      this.destroyEchartsInstances();
+      this.getChartDataByApi();
+    },
+
+    /* 璋冪敤鎺ュ彛鑾峰彇鍥捐〃鏁版嵁姹囨�绘柟娉� */
+    getChartDataByApi() {
+      let currentFactoryId = ''
+      if (this.tabList[this.activeTab] != null && this.tabList[this.activeTab] !== undefined) {
+        currentFactoryId = this.tabList[this.activeTab].factoryId
+      }
+      console.log('currentFactoryId', currentFactoryId)
+      this.getRunningStateDataByApi(currentFactoryId)
+      this.getEfficiencyDataByApi(currentFactoryId)
+      this.getFirst15DaysEfficiencyDataByApi(currentFactoryId)
+      this.getBarChartDataByApi(currentFactoryId)
+      this.getDoubleBarChartDataByApi(currentFactoryId)
+    },
+
+    /* 璋冪敤鎺ュ彛鑾峰彇璁惧杩愯鐘舵�� */
+    getRunningStateDataByApi(productionCode) {
+      this.runningStateChart = this.$echarts.init(document.getElementById('running_state_chart'))
+      this.runningStateChart.showLoading({
+        text: '鏁版嵁鍔犺浇涓� ...',
+        color: '#0696e1', // 鍔犺浇鍔ㄧ敾棰滆壊
+        textColor: '#fff',
+        maskColor: 'transparent' // 閬僵灞�
+      })
+      signageApi.getEquipmentStatusStatisticsApi(productionCode)
+        .then(res => {
+          if (res.success) {
+            this.runningStateData = res.result.list
+            this.currentPageProductionId = res.result.productionId
+            this.runningStateChartDataRequireFinished = true
+            this.drawRunningStateChart(res.result.productionId)
+          }
+        })
+    },
+
+    /* 璋冪敤鎺ュ彛鑾峰彇璁惧鍒╃敤鐜� */
+    getEfficiencyDataByApi(productionCode) {
+      this.efficiencyChart = this.$echarts.init(document.getElementById('efficiency_chart'))
+      this.efficiencyChart.showLoading({
+        text: '鏁版嵁鍔犺浇涓� ...',
+        color: '#0696e1', // 鍔犺浇鍔ㄧ敾棰滆壊
+        textColor: '#fff'
+      })
+      signageApi.getEquipmentUtilizationStatisticsApi(productionCode)
+        .then(res => {
+          if (res.success) {
+            this.efficiencyData = res.result
+            this.drawEfficiencyChart()
+          }
+        })
+    },
+
+    /* 璋冪敤鎺ュ彛鑾峰彇鍓�15澶╁埄鐢ㄧ巼 */
+    getFirst15DaysEfficiencyDataByApi(productionCode) {
+      this.first15DaysEfficiencyChart = this.$echarts.init(document.getElementById('first15DaysEfficiency_chart'))
+      this.first15DaysEfficiencyChart.showLoading({
+        text: '鏁版嵁鍔犺浇涓� ...',
+        color: '#0696e1', // 鍔犺浇鍔ㄧ敾棰滆壊
+        textColor: '#000000',
+        maskColor: 'transparent' // 閬僵灞�
+      })
+      signageApi.getEquipmentDayUtilizationStatisticsApi(productionCode)
+        .then(res => {
+          if (res.success) {
+            this.first15DaysEfficiencyData = res.result
+            this.drawFirst15DaysEfficiencyDataChart()
+          }
+        })
+    },
+    /* 缁樺埗鍓�7澶╁埄鐢ㄧ巼鏌卞浘 */
+    drawFirst15DaysEfficiencyDataChart() {
+      this.first15DaysEfficiencyData.dateList.forEach(item => {
+        if (!this.first15DaysEfficiencyData.dataList.map(item => item.date).includes(item)) {
+          const dateObj = {
+            date: item.date,
+            openRate: 0,
+            startRate: 0,
+            utilizationRate: 0
+          }
+          this.first15DaysEfficiencyData.dataList.push(dateObj)
+        }
+      })
+      const dateList = this.first15DaysEfficiencyData.dataList.map(item => item.date)
+      const newData = {
+        xAxis: dateList,
+        yAxis: [
+          {
+            name: '鍒╃敤鐜�',
+            value: this.first15DaysEfficiencyData.dataList.map(item => item.utilizationRate)
+          },
+          {
+            name: '寮�鍔ㄧ巼',
+            value: this.first15DaysEfficiencyData.dataList.map(item => item.startRate)
+          },
+          {
+            name: '寮�鏈虹巼',
+            value: this.first15DaysEfficiencyData.dataList.map(item => item.openRate)
+          }
+        ],
+        yAxisName: '鍓�15澶╁埄鐢ㄧ巼(%)'
+      }
+      let legendData = []
+      let seriesData = []
+      let colorArr = ['#A7F0C1', '#FAE893', '#66DFE2']
+      legendData = newData.yAxis.map((item) => item.name)
+      seriesData = newData.yAxis.map((item1, index1) => {
+        return {
+          name: item1.name,
+          type: 'bar',
+          symbol: 'circle',
+          symbolSize: 8,
+          itemStyle: {
+            color: colorArr[index1],
+            barBorderRadius: 100
+          },
+          lineStyle: {
+            width: 2
+          },
+          markPoint: {
+            show: true
+          },
+          yAxisIndex: 1,
+          data: item1.value // 鎶樼嚎鍥剧殑鏁版嵁
+        }
+      })
+      const option = {
+        grid: {
+          containLabel: true,
+          bottom: '1%',
+          top: '20%',
+          left: '2%',
+          right: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow'
+          },
+          formatter: function(params) {
+            let result = ''
+            params.forEach((item, index) => {
+              let dom = `<span style="display:inline-block;width:10px;height:10px;border-radius:100px;margin-right:5px;background:${item.color}"></span>${item.seriesName}锛�${item.value}%`
+              if (index === 0) {
+                result = `<span style="font-weight:bolder;">${item.name}</span>`
+              }
+              result += '<br />' + dom
+            })
+            return result
+          }
+        },
+        legend: {
+          top: 20,
+          right: 'center',
+          data: legendData,
+          itemGap: 10,
+          textStyle: {
+            fontSize: 14,
+            color: '#000000'
+          }
+        },
+        xAxis: {
+          data: newData.xAxis || [],
+          axisLabel: {
+            interval: 0,
+            show: true,
+            fontSize: 14,
+            color: '#000000'
+            // rotate: -30,
+          },
+          axisLine: {
+            show: true,
+            lineStyle: {
+              color: '#000000'
+            }
+          },
+          axisTick: {
+            show: true,
+            alignWithLabel: true
+          }
+        },
+        yAxis: [
+          {
+            name: newData.yAxisName,
+            nameTextStyle: {
+              color: '#1AD8DE',
+              fontSize: 18,
+              padding: [0, 0, 0, 110]
+            },
+            nameGap: 30,
+            type: 'value',
+            position: 'left',
+            axisLine: {
+              show: true,
+              lineStyle: {
+                color: '#000000'
+              }
+            },
+            axisTick: {
+              show: false
+            },
+            splitLine: {
+              show: false,
+              lineStyle: {
+                color: '#000000'
+              }
+            }
+          },
+          {
+            type: 'value',
+            position: 'right',
+            splitNumber: 5,
+            max: 100,
+            axisLabel: {
+              show: true,
+              color: '#000000',
+              fontSize: 14
+            },
+            axisLine: {
+              show: true,
+              lineStyle: {
+                color: '#000000'
+              }
+            },
+            axisTick: {
+              show: true
+            },
+            splitLine: {
+              show: false,
+              lineStyle: {
+                color: '#000000'
+              }
+            }
+          }
+        ],
+        series: seriesData,
+        dataZoom: {
+          show: false,
+          startValue: 0, // 浠庡ご寮�濮嬨��
+          endValue: 14 // 涓�娆℃�у睍绀哄嚑涓�
+        }
+        // toolbox: {
+        //   show: true,
+        //   feature: {
+        //     mark: { show: true },
+        //     magicType: { show: true, type: ['line', 'bar'] },
+        //     restore: { show: true },
+        //     saveAsImage: { show: true, name: '鍓�7澶╁埄鐢ㄧ巼缁熻鍥�', pixelRatio: 1 }
+        //   }
+        // }
+      }
+      this.first15DaysEfficiencyChart.setOption(option, true)
+      this.first15DaysEfficiencyChart.hideLoading()
+      this.first15DaysEfficiencyChart.on('click', params => {
+        this.$router.push({
+          name: 'mdc-base-StatisticsChart',
+          params: {
+            isEquipment: true,
+            productionId: params.name,
+            tierName: this.first15DaysEfficiencyData.dataList.find(item => item.date === params.name).date
+          }
+        })
+      })
+    },
+
+
+    /* 璋冪敤鎺ュ彛鑾峰彇璁惧OEE缁熻 */
+    getBarChartDataByApi(productionCode) {
+      this.barChart = this.$echarts.init(document.getElementById('bar_chart'))
+      this.barChart.showLoading({
+        text: '鏁版嵁鍔犺浇涓� ...',
+        color: '#0696e1', // 鍔犺浇鍔ㄧ敾棰滆壊
+        textColor: '#fff'
+      })
+      signageApi.getEquipmentOEEStatistics(productionCode)
+        .then(res => {
+          if (res.success && res.result) {
+            this.barChartData = res.result
+            this.drawBarChart()
+          }
+        })
+        .finally(() => {
+          this.barChart.hideLoading()
+        })
+    },
+
+    /* 缁樺埗鍗曟煴鍥� */
+    drawBarChart() {
+      const defaultData = []
+      const colorArray = ['#79CEAA', '#F589A2', '#6FBF9D', '#66DFE2', '#A7F0C1', '#FAE893', '#F7B7A0']
+      const dataMax = this.barChartData.length > 0 ? +this.barChartData.sort((x, y) => +y.value - +x.value)[0].value : 0
+      let yAxisMax
+      if (dataMax === 0) yAxisMax = 1 // 鑻ユ暟鎹腑鏈�澶у�间负0锛屽垯灏嗚儗鏅粯璁ゅ�艰缃负1
+      else yAxisMax = Math.ceil(dataMax / 5) * 5 // 璁剧疆鏌卞浘鑳屾櫙闃村奖榛樿鍊硷紝鎬濊矾涓烘暟鎹渶澶у�兼渶鎺ヨ繎鐨勮兘琚�5鏁撮櫎鐨勬暟瀛�
+      const yAxisInterval = yAxisMax / 5 // 鍚屾椂灏嗗埢搴﹀�煎垎鎴�5浠�
+      this.barChartData.forEach(item => defaultData.push(yAxisMax))
+      const option = {
+        title: {
+          show: true, // 鏄惁鏄剧ず鏍囬锛岄粯璁や负true
+          text: '', // 涓绘爣棰樻枃鏈�
+          x: 'left', // 鏍囬姘村钩瀹夋斁浣嶇疆锛屽彲閫夊�间负'left'銆�'center'銆�'right'鎴栧叿浣撶殑姘村钩鍧愭爣鍊�
+          y: 'top', // 鏍囬鍨傜洿瀹夋斁浣嶇疆锛屽彲閫夊�间负'top'銆�'bottom'銆�'center'鎴栧叿浣撶殑鍨傜洿鍧愭爣鍊�
+          textStyle: {
+            // 涓绘爣棰樻枃鏈牱寮�
+            fontSize: 18,
+            fontWeight: 'normal',
+            color: '#1AD8DE'
+          }
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'shadow'
+          },
+          formatter: function(params) {
+            return '<span style="font-weight:bolder;">' + params[0].name + '</span><br/>' +
+              '<span style="display:inline-block; width:10px; height:10px; border-radius:100px; margin-right:5px; background:' + params[0].color + '"></span>' + ' OEE: ' + params[0].value + '%'
+          },
+          // backgroundColor: 'rgba(9, 24, 48, 0.5)',
+          borderColor: 'rgba(75, 253, 238, 0.4)',
+          textStyle: {
+            // color: '#CFE3FC'
+          },
+          borderWidth: 1
+        },
+        grid: {
+          top: '20%',
+          left: '10%'
+        },
+        xAxis: [{
+          name: '',
+          nameLocation: 'middle',
+          nameGap: 40, // x杞磏ame涓庢í鍧愭爣杞寸嚎鐨勯棿璺�
+          type: 'category',
+          data: this.barChartData.map(item => item.productionId),
+          axisLine: {
+            lineStyle: {
+              color: '#000000'
+            }
+          },
+          axisLabel: {
+            show: true, // 鏄惁鏄剧ず鍒诲害鏍囩锛岄粯璁ゆ樉绀�
+            interval: 0, // 鍧愭爣杞村埢搴︽爣绛剧殑鏄剧ず闂撮殧锛屽湪绫荤洰杞翠腑鏈夋晥锛涢粯璁や細閲囩敤鏍囩涓嶉噸鍙犵殑绛栫暐闂撮殧鏄剧ず鏍囩锛涘彲浠ヨ缃垚0寮哄埗鏄剧ず鎵�鏈夋爣绛撅紱濡傛灉璁剧疆涓�1锛岃〃绀恒�庨殧涓�涓爣绛炬樉绀轰竴涓爣绛俱�忥紝濡傛灉鍊间负2锛岃〃绀洪殧涓や釜鏍囩鏄剧ず涓�涓爣绛撅紝浠ユ绫绘帹銆�
+            rotate: this.barChartData.length >= 6 ? -30 : 0, // 鍒诲害鏍囩鏃嬭浆鐨勮搴︼紝鍦ㄧ被鐩酱鐨勭被鐩爣绛炬樉绀轰笉涓嬬殑鏃跺�欏彲浠ラ�氳繃鏃嬭浆闃叉鏍囩涔嬮棿閲嶅彔锛涙棆杞殑瑙掑害浠�-90搴﹀埌90搴�
+            inside: false, // 鍒诲害鏍囩鏄惁鏈濆唴锛岄粯璁ゆ湞澶�
+            margin: 10, // 鍒诲害鏍囩涓庤酱绾夸箣闂寸殑璺濈
+            formatter: value => {
+              return `${this.barChartData.find(item => item.productionId === value).name}`
+            },
+            fontSize: 14
+          },
+          axisTick: {
+            show: true,
+            alignWithLabel: true
+          }
+        }],
+        yAxis: [{
+          name: '%',
+          min: 0,
+          max: yAxisMax,
+          interval: yAxisInterval,
+          axisLabel: {
+            formatter: '{value}',
+            color: '#000000',
+            fontSize: 14
+          },
+          axisTick: {
+            show: false
+          },
+          axisLine: {
+            show: false,
+            lineStyle: {
+              color: '#000000'
+            }
+          },
+          splitLine: {
+            show: false,
+            lineStyle: {
+              color: 'rgba(255,255,255,0.12)'
+            }
+          }
+        }],
+        series: [{
+          type: 'bar',
+          data: this.barChartData,
+          barWidth: this.barChartData.length > 5 ? '40%' : 30,
+          itemStyle: {
+            color: function(params) {
+              let num = colorArray.length
+              return colorArray[params.dataIndex % num]
+            },
+            barBorderRadius: 100
+          },
+          zlevel: 1,
+          label: {
+            show: false,
+            lineHeight: 10,
+            formatter: params => {
+              if (+params.value === 0) return ''
+              else return params.value
+            },
+            position: 'top',
+            textStyle: {
+              color: '#000000',
+              fontSize: 16
+            }
+          }
+        }]
+      }
+      option.title.text = moment().subtract(1, 'month').format('M鏈�') + `OEE`
+      this.barChart.setOption(option, true)
+    },
+
+    /* 缁樺埗璁惧杩愯鐘舵�佺帿鐟伴ゼ鍥� */
+    drawRunningStateChart() {
+      const option = {
+        height: 300,
+        title: {
+          show: true, // 鏄惁鏄剧ず鏍囬锛岄粯璁や负true
+          text: '璁惧鐘舵��', // 涓绘爣棰樻枃鏈�
+          x: 'left', // 鏍囬姘村钩瀹夋斁浣嶇疆锛屽彲閫夊�间负'left'銆�'center'銆�'right'鎴栧叿浣撶殑姘村钩鍧愭爣鍊�
+          y: 'top', // 鏍囬鍨傜洿瀹夋斁浣嶇疆锛屽彲閫夊�间负'top'銆�'bottom'銆�'center'鎴栧叿浣撶殑鍨傜洿鍧愭爣鍊�
+          textStyle: {
+            // 涓绘爣棰樻枃鏈牱寮�
+            fontSize: 18,
+            fontWeight: 'normal',
+            color: '#1AD8DE'
+          }
+        },
+        tooltip: {
+          trigger: 'item',
+          formatter: function(params) {
+            return '<span style="font-weight:bolder;">' + params.name + '</span><br/>' +
+              '<span style="display:inline-block; width:10%; height:10%; border-radius:100px; margin-right:5px; background:' + params.color + '"></span>' + `${params.value}锛�${params.percent}%锛塦
+          }
+        },
+        legend: {
+          top: 'auto',
+          left: 'center',
+          bottom: '10%', // 搴曢儴璺濈
+          orient: 'horizontal', // 姘村钩鎺掑垪
+          right: '10%',
+          // bottom: "0",
+          itemWidth: 14,
+          itemHeight: 14,
+          icon: 'roundRect',
+          itemGap: 15,
+          textStyle: {
+            color: '#000',
+            fontSize: 14,
+            padding: [0, 0, 0, 0]
+          },
+          data: ['鍏虫満', '鎶ヨ', '寰呮満', '杩愯']
+        },
+        grid: {
+          containLabel: true
+        },
+        series: [
+          {
+            type: 'pie',
+            roseType: 'angle', // 鐜懓鍥�
+            // selectedMode: "single",
+            radius: this.pieChartRadius,
+            center: ['45%', '55%'],
+            color: [
+              '#8B8B8B',
+              '#F56436',
+              '#FFFF40',
+              '#0FC61A'
+            ],
+            label: {
+              position: 'outside',
+              show: true,
+              color: '#000',
+              // textBorderColor: 'inherit',
+              // textBorderWidth: 1,
+              fontSize: 16,
+              formatter: function(params) {
+                if (params.name !== '') {
+                  return `${params.name}:${params.value}`
+                }
+              }
+            },
+            labelLine: {
+              show: true,
+              length2: 10,
+              length: 10
+            },
+            data: this.runningStateData
+          }
+        ]
+      }
+      this.runningStateChart.setOption(option, true)
+      this.runningStateChart.hideLoading()
+    },
+
+    /* 缁樺埗璁惧鍒╃敤鐜囪兌鍥婂浘 */
+    drawEfficiencyChart() {
+      const data = this.efficiencyData || []
+
+      // 鏃犺鏁版嵁鏄惁涓虹┖锛屽厛鍙栨秷loading鐘舵��
+      this.efficiencyChart.hideLoading()
+
+      // 鏍囬鏂囨湰缁熶竴澶勭悊
+      const titleText = `${moment().subtract(1, 'days').format('M鏈圖鏃�')}鍚勮溅闂村埄鐢ㄧ巼鎺掕`
+
+      if (data.length === 0) {
+        // 鏁版嵁涓虹┖鏃跺彧灞曠ず鏍囬
+        const option = {
+          title: {
+            show: true,
+            text: titleText,
+            x: 'left',
+            y: 'top',
+            textStyle: {
+              fontSize: 18,
+              fontWeight: 'normal',
+              color: '#1AD8DE'
+            }
+          },
+          // 闅愯棌鎵�鏈夎酱鍜岀綉鏍�
+          xAxis: { show: false },
+          yAxis: [{ show: false }, { show: false }],
+          grid: { show: false },
+          series: []
+        }
+        this.efficiencyChart.setOption(option, true)
+        return
+      }
+
+      // 鏁版嵁涓嶄负绌烘椂鐨勬甯稿鐞嗛�昏緫锛堜繚鎸佷笉鍙橈級
+      const colorArray = [
+        { top: '#79CEAA', bottom: '#79CEAA' },
+        { top: '#F589A2', bottom: '#F589A2' },
+        { top: '#6FBF9D', bottom: '#6FBF9D' },
+        { top: '#66DFE2', bottom: '#66DFE2' },
+        { top: '#A7F0C1', bottom: '#A7F0C1' },
+        { top: '#FAE893', bottom: '#FAE893' },
+        { top: '#F7B7A0', bottom: '#F7B7A0' }
+      ]
+
+      const dataMax = +data.sort((x, y) => +y.value - +x.value)[0].value
+      let yAxisMax = dataMax === 0 ? 1 : Math.ceil(dataMax / 5) * 5
+      const yAxisInterval = yAxisMax / 5
+
+      const option = {
+        title: {
+          show: true,
+          text: titleText,
+          x: 'left',
+          y: 'top',
+          textStyle: {
+            fontSize: 18,
+            fontWeight: 'normal',
+            color: '#1AD8DE'
+          }
+        },
+        grid: {
+          left: '3%',
+          right: '5%',
+          bottom: '0%',
+          top: '6%',
+          containLabel: true
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { type: 'none' },
+          formatter: function(params) {
+            return `<span style="font-weight:bolder;">${params[0].name}</span><br/>
+                <span style="display:inline-block; width:10px; height:10px; border-radius:100px; margin-right:5px; background:${params[0].color.colorStops[params[0].dataIndex].color}"></span>
+                ${params[0].seriesName} : ${params[0].value}%`
+          }
+        },
+        xAxis: {
+          name: '',
+          nameTextStyle: { color: '#000000' },
+          axisLabel: {
+            margin: 20,
+            textStyle: { color: '#000000' }
+          },
+          show: true,
+          min: 0,
+          max: 'dataMax',
+          interval: yAxisInterval,
+          type: 'value',
+          axisTick: { show: false },
+          splitLine: { show: false }
+        },
+        yAxis: [{
+          type: 'category',
+          inverse: true,
+          triggerEvent: true,
+          axisLabel: {
+            show: true,
+            textStyle: { color: '#000000', fontSize: '14', fontWeight: 'bolder' },
+            formatter: function(value) {
+              return `${data.find(item => item.productionCode === value).name}`
+            }
+          },
+          splitLine: { show: false },
+          axisTick: { show: false },
+          axisLine: { show: false },
+          data: data.map(item => item.productionCode)
+        }, {
+          type: 'category',
+          inverse: true,
+          axisTick: 'none',
+          axisLine: 'none',
+          show: true,
+          axisLabel: {
+            textStyle: { color: '#000000', fontSize: '14' },
+            formatter: '{value}%'
+          },
+          data: data
+        }],
+        series: [{
+          name: '鍒╃敤鐜�',
+          type: 'bar',
+          zlevel: 1,
+          itemStyle: {
+            barBorderRadius: 100,
+            color: function(params) {
+              const num = colorArray.length
+              return {
+                type: 'linear',
+                colorStops: [{
+                  offset: 0,
+                  color: colorArray[params.dataIndex % num].bottom
+                }, {
+                  offset: 1,
+                  color: colorArray[params.dataIndex % num].top
+                }]
+              }
+            }
+          },
+          barWidth: 12,
+          data: data
+        }]
+      }
+
+      this.efficiencyChart.setOption(option, true)
+    },
+
+    re_drawPieChart() {
+      const clientWidth = document.body.clientWidth || document.documentElement.clientWidth
+      if (this.firstEnterClientWidth != 1920) {
+        this.pieChartRadius = this.normalPieChartRadius.map(item => item = (+item.slice(0, -1) * (clientWidth / 1920)) + '%')
+      } else {
+        this.pieChartRadius = this.normalPieChartRadius.map(item => item = (+item.slice(0, -1) * (clientWidth / this.firstEnterClientWidth)) + '%')
+      }
+      console.log('pieChartRadius', this.pieChartRadius)
+    },
+
+
+    /* 璋冪敤鎺ュ彛鑾峰彇璁惧OEE鍜屽埄鐢ㄧ巼瀵规瘮 */
+    getDoubleBarChartDataByApi(productionCode) {
+      this.doubleBarChart = this.$echarts.init(document.getElementById('double_bar_chart'))
+      this.doubleBarChart.showLoading({
+        text: '鏁版嵁鍔犺浇涓� ...',
+        color: '#0696e1', // 鍔犺浇鍔ㄧ敾棰滆壊
+        textColor: '#000000',
+        maskColor: 'transparent' // 閬僵灞�
+      })
+      signageApi.getEquipmentMonthStatisticsApi(productionCode)
+        .then(res => {
+          if (res.success) {
+            this.doubleBarChartData = res.result
+            this.drawDoubleBarChart()
+          }
+        })
+    },
+    /* 缁樺埗鍙屾煴鍥� */
+    drawDoubleBarChart() {
+      const option = {
+        title: {
+          text: '鏈堝埄鐢ㄧ巼OEE缁熻',
+          left: 'left',
+          top: 'top',
+          textStyle: {
+            fontSize: 18,
+            fontWeight: 'normal',
+            color: '#1AD8DE'
+          }
+        },
+        color: ['#66DFE2', '#79CEAA'],
+        tooltip: {
+          confine: true,
+          formatter: function(params) {
+            return '<span style="font-weight:bolder;">' + params.name + '</span><br/>' +
+              '<span style="display:inline-block; width:10px; height:10px; border-radius:100px; margin-right:5px; background:' + params.color + '"></span>' + params.seriesName + ' : ' + params.value + '%'
+          }
+        },
+        grid: {
+          left: '5%',
+          right: '4%',
+          bottom: '10%',
+          top: '20%',
+          containLabel: true
+        },
+        legend: {
+          icon: 'roundRect',
+          orient: 'horizontal',
+          left: 'center',
+          itemWidth: 14,
+          itemHeight: 14,
+          formatter: ['{a|{name}}'].join('\n'),
+          textStyle: {
+            fontSize: 14,
+            color: '#000000',
+            height: 8,
+            rich: {
+              a: {
+                verticalAlign: 'bottom'
+              }
+            }
+          },
+          data: ['OEE', 'TEEP']
+        },
+        xAxis: {
+          type: 'category',
+          data: this.doubleBarChartData.dateList,
+          axisLine: {
+            lineStyle: {
+              color: 'rgba(0,0,0)'
+            }
+          },
+          axisLabel: {
+            fontSize: 14,
+            color: '#000000'
+          },
+          axisTick: {
+            show: true
+          }
+        },
+        yAxis: [
+          {
+            name: '%',
+            nameTextStyle: {
+              color: '#000000'
+            },
+            type: 'value',
+            min: 0,
+            minInterval: 1,
+            axisLine: {
+              show: true
+            },
+            axisTick: {
+              show: true
+            },
+            splitLine: {
+              show: false,
+              lineStyle: {
+                color: 'rgba(255, 255, 255, 0.15)'
+                // type: 'dashed', // dotted 铏氱嚎
+              }
+            },
+            axisLabel: {
+              fontSize: 14,
+              color: '#000000',
+              fontFamily: 'Bebas'
+            }
+          },
+          {
+            type: 'value',
+            axisLine: {
+              show: true
+            },
+            axisTick: {
+              show: false
+            },
+            splitLine: {
+              show: false
+            },
+            axisLabel: {
+              fontSize: 14,
+              formatter: '{value}%', // 鍙充晶Y杞存枃瀛楁樉绀�
+              fontFamily: 'Bebas',
+              color: '#6A93B9'
+            },
+            splitArea: {
+              show: false
+            }
+          }],
+        series: [{
+          type: 'bar',
+          barWidth: 15,
+          itemStyle: { barBorderRadius: 100 },
+          name: 'OEE',
+          data: this.doubleBarChartData.oeeList,
+          label: {
+            show: false,
+            lineHeight: 10,
+            formatter: params => {
+              if (+params.value === 0) return ''
+              else return params.value
+            },
+            position: 'inside',
+            textStyle: {
+              color: '#000000',
+              fontSize: 12
+            }
+          }
+        }, {
+          type: 'bar',
+          barWidth: 15,
+          itemStyle: { barBorderRadius: 100 },
+          name: 'TEEP',
+          data: this.doubleBarChartData.utilizationList,
+          label: {
+            show: false,
+            lineHeight: 10,
+            formatter: params => {
+              if (+params.value === 0) return ''
+              else return params.value
+            },
+            position: 'inside',
+            textStyle: {
+              color: '#000000',
+              fontSize: 12
+            }
+          }
+        }
+        ]
+      }
+      this.doubleBarChart.setOption(option, true)
+      this.doubleBarChart.hideLoading()
+    },
+
+    /**
+     * 绐楀彛灏哄鍙樺寲鏃惰Е鍙�
+     * 璋冩暣鍥捐〃灏哄浠ラ�傚簲鍒嗚鲸鐜�
+     */
+    handleWindowResize() {
+      // this.re_drawPieChart()
+      if (this.runningStateChart) this.runningStateChart.resize()
+      if (this.efficiencyChart) this.efficiencyChart.resize()
+      if (this.first15DaysEfficiencyChart) this.first15DaysEfficiencyChart.resize()
+      if (this.barChart) this.barChart.resize()
+      if (this.doubleBarChart) this.doubleBarChart.resize()
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 椤电瀵艰埅鏍峰紡 */
+.tab-nav {
+  display: flex;
+  gap: 8px;
+  padding: 10px 16px;
+  background-color: #ffffff;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  overflow-x: auto;
+  scrollbar-width: none;
+}
+
+.tab-nav::-webkit-scrollbar {
+  display: none;
+}
+
+.tab-item {
+  padding: 8px 16px;
+  font-size: 14px;
+  color: #666666;
+  border-radius: 20px;
+  cursor: pointer;
+  white-space: nowrap;
+  transition: all 0.3s ease;
+}
+
+.tab-active {
+  background-color: #0696e1;
+  color: #ffffff;
+  font-weight: 500;
+}
+
+.tab-item:hover {
+  background-color: #f0f5ff;
+  color: #0696e1;
+}
+
+/* 涓诲鍣ㄦ牱寮� */
+.home-container {
+  display: flex;
+  flex-direction: column;
+  min-height: 100vh;
+  padding: 1px;
+  box-sizing: border-box;
+  gap: 16px;
+  background-color: #f0f2f7;
+}
+
+/* 鍥捐〃瀹瑰櫒鏍峰紡 */
+.chart-container {
+  display: flex;
+  gap: 16px;
+  flex: 1;
+  min-width: 0;
+}
+
+/* 宸︿晶鍗$墖鍖哄煙 */
+.left-cards {
+  flex: 1;
+  min-width: 0;
+}
+
+/* 鍙充晶鍗$墖鍖哄煙 */
+.right-cards {
+  flex: 2;
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+}
+
+/* 閫氱敤鍗$墖鏍峰紡 */
+.card {
+  background: #ffffff;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  justify-content: flex-start;
+}
+
+/* 宸︿晶鍗$墖 */
+.left-cards-card {
+  height: 100%;
+  min-height: 100vh;
+}
+
+/* 鍙充晶椤堕儴鍗$墖 */
+.right-top-card {
+  flex: 1;
+  min-height: 50vh;
+}
+
+/* 鍙充晶搴曢儴瀹瑰櫒 */
+.right-bottom-card {
+  flex: 1;
+  min-width: 0;
+  min-height: 50vh;
+  display: flex;
+  gap: 16px;
+}
+
+/* 鍙充晶搴曢儴宸﹀彸鍗$墖 */
+.right-bottom-left-card,
+.right-bottom-right-card {
+  flex: 1;
+}
+
+/* 鍝嶅簲寮忚皟鏁� */
+@media (max-width: 1200px) {
+  .chart-container {
+    gap: 12px;
+  }
+
+  .card {
+    padding: 12px;
+  }
+}
+
+@media (max-width: 992px) {
+  .chart-container {
+    flex-direction: column;
+  }
+
+  .left-cards, .right-cards {
+    width: 100%;
+  }
+
+  .left-cards {
+    margin-bottom: 16px;
+  }
+
+  .right-bottom-card {
+    flex-direction: column;
+  }
+
+  .tab-item {
+    padding: 6px 12px;
+    font-size: 13px;
+  }
+}
+
+@media (max-width: 768px) {
+  .home-container {
+    padding: 8px;
+  }
+
+  .tab-nav {
+    padding: 8px;
+    margin-bottom: 8px;
+  }
+}
+</style>
\ No newline at end of file

--
Gitblit v1.9.3