Jelajahi Sumber

新增自动创建检查计划功能 修复人员与岗位跳转之后重置问题

pm 8 bulan lalu
induk
melakukan
0c3d2201e1

+ 16 - 0
src/api/mes/material/plan.js

@@ -51,3 +51,19 @@ export function getCheckPlanCabinetList(query) {
     params: query
   })
 }
+//自动创建检查计划接口 /dev-api/iscs/notify/updateAutomaticConfig
+export function updateAutomaticConfig(data) {
+  return request({
+    url: '/iscs/notify/updateAutomaticConfig',
+    method: 'post',
+    data: data
+  })
+}
+// 自动创建检查计划回显 /dev-api/iscs/notify/selectIsMailNotifyConfigByCode
+export function selectIsMailNotifyConfigByCode(query) {
+  return request({
+    url: '/iscs/notify/selectIsMailNotifyConfigByCode',
+    method: 'get',
+    params: query
+  })
+}

+ 61 - 0
src/api/mes/statisticians/index.js

@@ -0,0 +1,61 @@
+import request from '@/utils/request'
+///dev-api/iscs/statistics-api/getCabinetStatistics
+// 物资柜使⽤情况统计
+export function getCabinetStatistics(query) {
+  return request({
+    url: '/iscs/statistics-api/getCabinetStatistics',
+    method: 'get',
+    params:query
+  })
+}
+// 物资当前状态统计 /dev-api/iscs/statistics-api/getMaterialsLoanStatistics
+export function getMaterialsLoanStatistics(query){
+  return request({
+    url: '/iscs/statistics-api/getMaterialsLoanStatistics',
+    method: 'get',
+    params:query
+  })
+}
+// 物资当前状态统计2 /dev-api/iscs/statistics-api/getMaterialsStatusStatistics
+export function getMaterialsStatusStatistics(query){
+  return request({
+    url: '/iscs/statistics-api/getMaterialsStatusStatistics',
+    method: 'get',
+    params:query
+  })
+}
+
+// dev-api/iscs/statistics-api/getMaterialsChangeStatistics
+export function getMaterialsChangeStatistics(query){
+  return request({
+    url: '/iscs/statistics-api/getMaterialsChangeStatistics',
+    method: 'get',
+    params:query
+  })
+}
+///dev-api/iscs/statistics-api/getUserLoanStatistics
+export function getUserLoanStatistics(query){
+  return request({
+    url: '/iscs/statistics-api/getUserLoanStatistics',
+    method: 'get',
+    params:query
+  })
+}
+// /dev-api/iscs/statistics-api/getDayLoanStatistics
+export function getDayLoanStatistics(query){
+  return request({
+    url: '/iscs/statistics-api/getDayLoanStatistics',
+    method: 'get',
+    params:query
+  })
+}
+
+
+// 首页导航的接口 /dev-api/iscs/statistics-api/getHomePage
+export function getHomePage(query){
+  return request({
+    url: '/iscs/statistics-api/getHomePage',
+    method: 'get',
+    params:query
+  })
+}

+ 1 - 0
src/assets/icons/svg/exception.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1739862237580" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="25515" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M704.501333 448c-159.210667 0-288.768 129.216-288.768 288S545.290667 1024 704.501333 1024c159.210667 0 288.768-129.216 288.768-288S863.712 448 704.501333 448z m0 512c-123.84 0-224.597333-100.48-224.597333-224S580.661333 512 704.501333 512c123.84 0 224.597333 100.48 224.597334 224S828.341333 960 704.501333 960z m-32.085333-384h64.170667v192h-64.170667v-192z m0 256h64.170667v64h-64.170667v-64z m-234.602667 128H94.901333v-64h301.013334c-10.453333 0-18.922667-64-25.28-64H94.901333V384H736.586667v6.976c0 1.984 64.170667 6.144 64.170666 12.032V0H30.730667v1024h476.842666c-26.005333 0-49.557333-64-69.76-64zM94.901333 64H736.586667v128H94.901333V64z m0 192H736.586667v64H94.901333v-64z" fill="#E65256" p-id="25516"></path></svg>

File diff ditekan karena terlalu besar
+ 0 - 0
src/assets/icons/svg/lending.svg


+ 1 - 0
src/assets/icons/svg/locker.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1739861920558" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12696" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M469.333333 341.333333l85.333333 0 0-42.666667-85.333333 0L469.333333 341.333333zM256 853.333333l512 0L768 170.666667 256 170.666667 256 853.333333zM298.666667 213.333333l426.666667 0 0 170.666667L298.666667 384 298.666667 213.333333zM298.666667 426.666667l426.666667 0 0 170.666667L298.666667 597.333333 298.666667 426.666667zM298.666667 640l426.666667 0 0 170.666667L298.666667 810.666667 298.666667 640zM469.333333 554.666667l85.333333 0 0-42.666667-85.333333 0L469.333333 554.666667zM469.333333 768l85.333333 0 0-42.666667-85.333333 0L469.333333 768zM810.666667 42.666667 213.333333 42.666667C166.4 42.666667 128 81.066667 128 128l0 768c0 46.933333 38.4 85.333333 85.333333 85.333333l597.333333 0c46.933333 0 85.333333-38.4 85.333333-85.333333L896 128C896 81.066667 857.6 42.666667 810.666667 42.666667zM853.333333 896c0 23.466667-19.2 42.666667-42.666667 42.666667L213.333333 938.666667c-23.466667 0-42.666667-19.2-42.666667-42.666667L170.666667 128c0-23.466667 19.2-42.666667 42.666667-42.666667l597.333333 0c23.466667 0 42.666667 19.2 42.666667 42.666667L853.333333 896z" fill="#0B80E0" p-id="12697"></path></svg>

+ 1 - 0
src/assets/icons/svg/material.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1739862327968" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="26564" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M921.6 813.056h-81.92c-22.528 0-40.96-18.24768-40.96-40.5504V245.3504c0-22.30272 18.432-40.5504 40.96-40.5504h81.92c22.528 0 40.96 18.24768 40.96 40.5504v527.1552c0 22.30272-18.432 40.5504-40.96 40.5504z m-245.76 0h-81.90976c-22.528 0-40.96-18.24768-40.96-40.5504V407.552c0-22.30272 18.432-40.5504 40.96-40.5504h81.92c22.528 0 40.96 18.24768 40.96 40.5504v364.9536c0 22.30272-18.432 40.5504-40.96 40.5504z m-245.76 0h-81.92c-22.528 0-40.96-18.24768-40.96-40.5504V326.4512c0-22.30272 18.432-40.5504 40.96-40.5504h81.92c22.528 0 40.96 18.24768 40.96 40.5504v446.0544c0 22.30272-18.432 40.5504-40.96 40.5504z m-245.76 0h-81.92c-22.528 0-40.96-18.24768-40.96-40.5504v-202.752c0-22.31296 18.432-40.5504 40.96-40.5504h81.92c22.528 0 40.96 18.24768 40.96 40.5504v202.752c0 22.30272-18.432 40.5504-40.96 40.5504z" fill="#35CCB4" p-id="26565"></path></svg>

TEMPAT SAMPAH
src/assets/images/警告.png


TEMPAT SAMPAH
src/assets/images/警告2.png


+ 10 - 4
src/utils/request.js

@@ -24,15 +24,23 @@ const service = axios.create({
 })
 
 // request拦截器
-
 service.interceptors.request.use(config => {
+  // 检查网络状态
+  if (!navigator.onLine) {
+    Message({
+      message: '网络已断开,请连接网络后再试',
+      type: 'error',
+      duration: 5 * 1000
+    })
+    return Promise.reject(new Error('网络已断开,请连接网络后再试'))
+  }
+
   // 是否需要设置 token
   const isToken = (config.headers || {}).isToken === false
   // 是否需要防止数据重复提交
   const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
   if (getToken() && !isToken) {
     config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
-
   }
   // get请求映射params参数
   if (config.method === 'get' && config.params) {
@@ -74,8 +82,6 @@ service.interceptors.request.use(config => {
 service.interceptors.response.use(res => {
     // 未设置状态码则默认成功状态
     const code = res.data.code || 200
-    // var userAgent = navigator.userAgent;
-    // console.log(userAgent,'userAgent');
     // 获取错误信息
     const msg = errorCode[code] || res.data.msg || errorCode['default']
     // 二进制数据则直接返回

+ 96 - 81
src/views/dashboard/LineChart.vue

@@ -1,11 +1,11 @@
 <template>
-  <div :class="className" :style="{height:height,width:width}" />
+  <div :class="className" :style="{ height: height, width: width }" />
 </template>
 
 <script>
-import echarts from 'echarts'
-require('echarts/theme/macarons') // echarts theme
-import resize from './mixins/resize'
+import echarts from 'echarts';
+require('echarts/theme/macarons'); // echarts theme
+import resize from './mixins/resize';
 
 export default {
   mixins: [resize],
@@ -20,116 +20,131 @@ export default {
     },
     height: {
       type: String,
-      default: '350px'
+      default: '400px'
     },
     autoResize: {
       type: Boolean,
       default: true
     },
     chartData: {
-      type: Object,
-      required: true
-    }
+      type: Array,
+      required: false
+    },
+
   },
   data() {
     return {
       chart: null
-    }
+    };
   },
   watch: {
     chartData: {
       deep: true,
       handler(val) {
-        this.setOptions(val)
+        this.setOptions(val);
       }
     }
   },
   mounted() {
     this.$nextTick(() => {
-      this.initChart()
-    })
+      this.initChart();
+    });
   },
   beforeDestroy() {
     if (!this.chart) {
-      return
+      return;
     }
-    this.chart.dispose()
-    this.chart = null
+    this.chart.dispose();
+    this.chart = null;
   },
   methods: {
     initChart() {
-      this.chart = echarts.init(this.$el, 'macarons')
-      this.setOptions(this.chartData)
+      this.chart = echarts.init(this.$el, 'macarons');
+      this.setOptions(this.chartData);
     },
-    setOptions({ expectedData, actualData } = {}) {
-      this.chart.setOption({
-        xAxis: {
-          data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
-          boundaryGap: false,
-          axisTick: {
-            show: false
-          }
-        },
-        grid: {
-          left: 10,
-          right: 10,
-          bottom: 20,
-          top: 30,
-          containLabel: true
-        },
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'cross'
+    setOptions(chartData) {
+      if (chartData) {
+        const xAxisData = chartData.map(item => item.day);
+        const loanCount = chartData.map(item => item.loanCount);
+        const returnCount = chartData.map(item => item.returnCount);
+        this.chart.setOption({
+          xAxis: {
+            data: xAxisData || [],
+            boundaryGap: false,
+            axisTick: {
+              show: false
+            },
+            axisLabel:{
+              interval:0,
+              rotate:-30   //值>0向右倾斜,值<0则向左倾斜
+            }
+          },
+          grid: {
+            left: 10,
+            right: 35,
+            bottom: 20,
+            top: 30,
+            containLabel: true
+          },
+          tooltip: {
+            trigger: 'axis',
+            axisPointer: {
+              type: 'cross'
+            },
+            padding: [5, 10]
           },
-          padding: [5, 10]
-        },
-        yAxis: {
-          axisTick: {
-            show: false
-          }
-        },
-        legend: {
-          data: ['expected', 'actual']
-        },
-        series: [{
-          name: 'expected', itemStyle: {
-            normal: {
-              color: '#FF005A',
-              lineStyle: {
-                color: '#FF005A',
-                width: 2
-              }
+          yAxis: {
+            axisTick: {
+              show: false
             }
           },
-          smooth: true,
-          type: 'line',
-          data: expectedData,
-          animationDuration: 2800,
-          animationEasing: 'cubicInOut'
-        },
-        {
-          name: 'actual',
-          smooth: true,
-          type: 'line',
-          itemStyle: {
-            normal: {
-              color: '#3888fa',
-              lineStyle: {
-                color: '#3888fa',
-                width: 2
+          legend: {
+            data: ['领取', '归还']
+          },
+          series: [
+            {
+              name: '领取',
+              itemStyle: {
+                normal: {
+                  color: '#FF005A',
+                  lineStyle: {
+                    color: '#FF005A',
+                    width: 2
+                  }
+                }
+              },
+              smooth: true,
+              type: 'line',
+              data: loanCount || [],
+              animationDuration: 2800,
+              animationEasing: 'cubicInOut'
+            },
+            {
+              name: '归还',
+              smooth: true,
+              type: 'line',
+              itemStyle: {
+                normal: {
+                  color: '#3888fa',
+                  lineStyle: {
+                    color: '#3888fa',
+                    width: 2
+                  },
+                  areaStyle: {
+                    color: '#f3f8ff'
+                  }
+                }
               },
-              areaStyle: {
-                color: '#f3f8ff'
-              }
+              data: returnCount || [],
+              animationDuration: 2800,
+              animationEasing: 'quadraticOut'
             }
-          },
-          data: actualData,
-          animationDuration: 2800,
-          animationEasing: 'quadraticOut'
-        }]
-      })
+          ]
+        });
+      }
+
+
     }
   }
-}
+};
 </script>

+ 44 - 16
src/views/dashboard/PanelGroup.vue

@@ -3,52 +3,52 @@
     <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
       <div class="card-panel" @click="handleSetLineChartData('newVisitis')">
         <div class="card-panel-icon-wrapper icon-people">
-          <svg-icon icon-class="device" class-name="card-panel-icon" />
+          <svg-icon icon-class="material" class-name="card-panel-icon" />
         </div>
         <div class="card-panel-description">
           <div class="card-panel-text">
-            设备总数
+            物资总数
           </div>
-          <count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
+          <count-to :start-val="0" :end-val="materialsCount" :duration="2600" class="card-panel-num" />
         </div>
       </div>
     </el-col>
     <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
       <div class="card-panel" @click="handleSetLineChartData('messages')">
         <div class="card-panel-icon-wrapper icon-message">
-          <svg-icon icon-class="running" class-name="card-panel-icon" />
+          <svg-icon icon-class="locker" class-name="card-panel-icon" />
         </div>
         <div class="card-panel-description">
           <div class="card-panel-text">
-            运行中
+            柜中物资
           </div>
-          <count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
+          <count-to :start-val="0" :end-val="cabinetMaterialsCount" :duration="3000" class="card-panel-num" />
         </div>
       </div>
     </el-col>
     <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
       <div class="card-panel" @click="handleSetLineChartData('purchases')">
         <div class="card-panel-icon-wrapper icon-money">
-          <svg-icon icon-class="waitting" class-name="card-panel-icon" />
+          <svg-icon icon-class="lending" class-name="card-panel-icon" />
         </div>
         <div class="card-panel-description">
           <div class="card-panel-text">
-            待机
+            借出物资
           </div>
-          <count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
+          <count-to :start-val="0" :end-val="loanMaterialsCount" :duration="3200" class="card-panel-num" />
         </div>
       </div>
     </el-col>
     <el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
       <div class="card-panel" @click="handleSetLineChartData('shoppings')">
         <div class="card-panel-icon-wrapper icon-shopping">
-          <svg-icon icon-class="fault" class-name="card-panel-icon" />
+          <svg-icon icon-class="exception" class-name="card-panel-icon" />
         </div>
         <div class="card-panel-description">
           <div class="card-panel-text">
-            故障
+            异常数量
           </div>
-          <count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
+          <count-to :start-val="0" :end-val="exceptionMaterialsCount" :duration="3600" class="card-panel-num" />
         </div>
       </div>
     </el-col>
@@ -57,12 +57,35 @@
 
 <script>
 import CountTo from 'vue-count-to'
+import { getHomePage } from '@/api/mes/statisticians'
 
 export default {
   components: {
     CountTo
   },
+  data(){
+    return {
+      materialsCount: null,//物资总数
+      loanMaterialsCount: null,//借出物资
+      exceptionMaterialsCount: null,//异常数量
+      cabinetMaterialsCount: null//柜中物资
+    }
+  },
+  mounted() {
+    this.getList()
+  },
   methods: {
+    getList(){
+      getHomePage().then((res) => {
+        console.log(res, '首页接口')
+        this.loading = false
+
+        this.materialsCount = res.data.materialsCount
+        this.exceptionMaterialsCount = res.data.exceptionMaterialsCount
+        this.cabinetMaterialsCount = res.data.cabinetMaterialsCount
+        this.loanMaterialsCount = res.data.loanMaterialsCount
+      })
+    },
     handleSetLineChartData(type) {
       this.$emit('handleSetLineChartData', type)
     }
@@ -96,19 +119,24 @@ export default {
       }
 
       .icon-people {
-        background: #40c9c6;
+        //background: #40c9c6;
+        background: #eeeff2;
       }
 
       .icon-message {
-        background: #36a3f7;
+        //background: #36a3f7;
+        background: #eeeff2;
       }
 
       .icon-money {
-        background: #f4516c;
+        //background: #f4516c;
+        background: #eeeff2;
+        color:#f4516c;
       }
 
       .icon-shopping {
-        background: #34bfa3
+        //background: #34bfa3;
+        background: #eeeff2;
       }
     }
 

+ 103 - 77
src/views/index.vue

@@ -1,84 +1,79 @@
 <template>
   <div class="dashboard-editor-container">
 
-    <panel-group @handleSetLineChartData="handleSetLineChartData" />
+    <panel-group @handleSetLineChartData="handleSetLineChartData"/>
 
-    <el-row :gutter="32" style="padding:16px 16px 0;margin-bottom:32px;">
-        <el-card>
-            <div slot="header" class="clearfix">
-                <span>生产进度</span>
-            </div>
-            <el-table
-                v-loading="loading"
-                :data="workorderList"
-                row-key="workorderId"
-                default-expand-all
-                :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
-                >
-                <el-table-column label="工单编码" width="180" prop="workorderCode" >
-                    <template slot-scope="scope">
-                    <el-button v-no-more-click
-                        size="mini"
-                        type="text"
-                        @click="handleView(scope.row)"
-                        v-hasPermi="['mes:pro:workorder:query']"
-                    >{{scope.row.workorderCode}}</el-button>
-                    </template>
-                </el-table-column>
-                <el-table-column label="订单编号" width="140" align="center" prop="sourceCode" />            
-                <el-table-column label="客户名称" align="center" prop="clientName" :show-overflow-tooltip="true"/>
-                <el-table-column label="产品编号" width="120" align="center" prop="productCode" />
-                <el-table-column label="产品名称" width="250px" align="center" prop="productName" :show-overflow-tooltip="true"/>
-                <el-table-column label="规格型号" align="center" prop="productSpc" :show-overflow-tooltip="true"/>
-                <el-table-column label="单位" align="center" prop="unitOfMeasure" />
-                <el-table-column label="生产进度" align="center" width="200px" >
-                    <template slot-scope="scope">
-                        <el-progress :text-inside="true" :stroke-width="20" :percentage="parseFloat(((scope.row.quantityProduced/scope.row.quantity*100).toFixed(2)))>100?100:parseFloat(((scope.row.quantityProduced/scope.row.quantity*100).toFixed(2)))"></el-progress>
-                    </template>
-                </el-table-column>            
-                <el-table-column label="需求日期" align="center" prop="requestDate" width="180">
-                    <template slot-scope="scope">
-                    <span>{{ parseTime(scope.row.requestDate, '{y}-{m}-{d}') }}</span>
-                    </template>
-                </el-table-column>         
-            </el-table>
-        </el-card>
-    </el-row>
-    
-    <el-row style="background:#fff;padding:16px 16px 0; margin-bottom:32px;">
-      <line-chart :chart-data="lineChartData" />
+    <el-row :gutter="32" style="padding:0px 16px 0;margin-bottom:32px;">
+      <el-card>
+        <div slot="header" class="clearfix">
+          <span>进行中的作业</span>
+        </div>
+        <el-table
+          height="260"
+          v-loading="loading"
+          :data="jobList"
+          row-key="workorderId"
+          default-expand-all
+          :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+        >
+          <!--                <el-table-column label="工单编码" width="180" prop="workorderCode" >-->
+          <!--                    <template slot-scope="scope">-->
+          <!--                    <el-button v-no-more-click-->
+          <!--                        size="mini"-->
+          <!--                        type="text"-->
+          <!--                        @click="handleView(scope.row)"-->
+          <!--                        v-hasPermi="['mes:pro:workorder:query']"-->
+          <!--                    >{{scope.row.workorderCode}}</el-button>-->
+          <!--                    </template>-->
+          <!--                </el-table-column>-->
+          <el-table-column label="作业票名称" width="320" align="center" prop="ticketName"/>
+          <el-table-column label="岗位" align="center" prop="workstationName" :show-overflow-tooltip="true"/>
+          <el-table-column label="设备工艺" align="center" prop="machineryName"/>
+          <el-table-column label="作业类型" align="center" prop="ticketType" :show-overflow-tooltip="true">
+            <template slot-scope="scope">
+              <dict-tag :options="dict.type.ticket_type" :value="scope.row.ticketType"/>
+            </template>
+          </el-table-column>
+          <el-table-column label="启动时间" align="center" prop="ticketStartTime" :show-overflow-tooltip="true"/>
+
+        </el-table>
+      </el-card>
     </el-row>
 
-    <el-row :gutter="32">
-      <el-col :xs="24" :sm="24" :lg="8">
-        <div class="chart-wrapper">
-          <raddar-chart />
-        </div>
-      </el-col>
-      <el-col :xs="24" :sm="24" :lg="8">
-        <div class="chart-wrapper">
-          <pie-chart />
-        </div>
-      </el-col>
-      <el-col :xs="24" :sm="24" :lg="8">
-        <div class="chart-wrapper">
-          <bar-chart />
-        </div>
-      </el-col>
+    <el-row style="width:100%;background:#fff;padding:16px 16px 0; margin-bottom:32px;">
+      <line-chart :chart-data="lineChartData"/>
     </el-row>
 
-    
+<!--    <el-row :gutter="32">-->
+<!--      <el-col :xs="24" :sm="24" :lg="8">-->
+<!--        <div class="chart-wrapper">-->
+<!--          <raddar-chart/>-->
+<!--        </div>-->
+<!--      </el-col>-->
+<!--      <el-col :xs="24" :sm="24" :lg="8">-->
+<!--        <div class="chart-wrapper">-->
+<!--          <pie-chart/>-->
+<!--        </div>-->
+<!--      </el-col>-->
+<!--      <el-col :xs="24" :sm="24" :lg="8">-->
+<!--        <div class="chart-wrapper">-->
+<!--          <bar-chart/>-->
+<!--        </div>-->
+<!--      </el-col>-->
+<!--    </el-row>-->
+
+
   </div>
 </template>
 
 <script>
-import { listWorkorder } from "@/api/mes/pro/workorder";
+import { listWorkorder } from '@/api/mes/pro/workorder'
 import PanelGroup from './dashboard/PanelGroup'
 import LineChart from './dashboard/LineChart'
 import RaddarChart from './dashboard/RaddarChart'
 import PieChart from './dashboard/PieChart'
 import BarChart from './dashboard/BarChart'
-
+import { getHomePage } from '@/api/mes/statisticians'
 const lineChartData = {
   newVisitis: {
     expectedData: [100, 120, 161, 134, 105, 160, 165],
@@ -100,6 +95,7 @@ const lineChartData = {
 
 export default {
   name: 'Index',
+  dicts: ['ticket_type'],
   components: {
     PanelGroup,
     LineChart,
@@ -109,25 +105,55 @@ export default {
   },
   data() {
     return {
-        loading: true,
-        queryParams: {status:'CONFIRMED'},
-        workorderList: [],
-        lineChartData: lineChartData.newVisitis
+      loading: true,
+      queryParams: { status: 'CONFIRMED' },
+      jobList: [],
+      lineChartData: null,
+      startTime: null,
+      endTime: null,
     }
   },
-  created(){
-      this.getList();
+  created() {
+    this.calculateDateRange()
+    this.getList()
+
   },
   methods: {
+    calculateDateRange() {
+      const today = new Date();
+      const endTime = today.toISOString().split('T')[0]; // 今天的日期,格式为 YYYY-MM-DD
+
+      // 计算 startTime,即从今天往前三十天的日期
+      const thirtyDaysAgo = new Date(today);
+      thirtyDaysAgo.setDate(today.getDate() - 30);
+      const startTime = thirtyDaysAgo.toISOString().split('T')[0]; // 三十天前的日期,格式为 YYYY-MM-DD
+      this.startTime=startTime;
+      this.endTime=endTime;
+      console.log(this.startTime, endTime,'传递的时间');
+    },
     getList() {
-      this.loading = true;
-      listWorkorder(this.queryParams).then(response => {
-        this.workorderList = response.rows;
-        this.loading = false;
-      });
+      this.loading = true
+      let data={
+        startTime:this.startTime,
+        endTime:this.endTime,
+      }
+      getHomePage(data).then((res) => {
+        console.log(res, '首页接口')
+        this.loading = false
+        this.jobList = res.data.jobList
+        this.lineChartData= res.data.loanList.sort((a, b) => {
+          const dateA = new Date(a.day);
+          const dateB = new Date(b.day);
+          return dateA-dateB
+        })
+      })
+      // listWorkorder(this.queryParams).then(response => {
+      //   this.workorderList = response.rows;
+      //   this.loading = false;
+      // });
     },
     handleSetLineChartData(type) {
-      this.lineChartData = lineChartData[type]
+      // this.lineChartData = lineChartData[type]
     }
   }
 }
@@ -146,7 +172,7 @@ export default {
   }
 }
 
-@media (max-width:1024px) {
+@media (max-width: 1024px) {
   .chart-wrapper {
     padding: 8px;
   }

+ 9 - 4
src/views/mes/job/jobm/NewMarsJob.vue

@@ -915,6 +915,9 @@ export default {
   watch: {
     tabPosition: function(val, oldVal) {
       if (val == 'first') {
+        this.sopTypeOption = []
+        this.technologyList=[]
+        this.sopId = null
         this.getList()
       }
     },
@@ -928,6 +931,7 @@ export default {
 
   mounted() {
     this.getList()
+
   },
   methods: {
     // 锁定站信息部分表格收缩
@@ -1047,15 +1051,16 @@ export default {
           (item) => item.workstationId == '7'
         )
       })
+      // 这里注释掉是为了 初始化跳转数据为空
       const data1 = {
         pages: 1,
         size: -1,
         machineryType: '工艺'
       }
-      listTechnology(data1).then((res) => {
-        console.log(res, 'technologyList')
-        this.technologyList = res.data.records
-      })
+      // listTechnology(data1).then((res) => {
+      //   console.log(res, 'technologyList')
+      //   this.technologyList = res.data.records
+      // })
       listMarsDept(data).then((res) => {
         console.log(res, 'marsDeptList')
         this.marsDeptList = res.data.records

+ 232 - 53
src/views/mes/material/inspectionplan/index.vue

@@ -124,7 +124,77 @@
         @queryTable="getList"
       ></right-toolbar>
     </el-row>
+    <!--    自动配置检查计划开始-->
+    <el-row :gutter="10" class="mb8">
+      <el-form label-width="70px">
+        <el-col :span="2" style="margin-top: 8px">
+          <el-checkbox style="transform: scale(1.5);" v-model="autoConfig.startStatus" @change="handleClickStatus"
+          ></el-checkbox>
+          <span style="margin-left: 6%;">启动自动创建</span>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="计划频率">
+            <el-select
+              :disabled="!autoConfig.startStatus"
+              v-model="autoConfig.planFrequency"
+              placeholder="请选择计划频率"
+              clearable
+              @change="handleFrequencyChange"
+            >
+              <el-option
+                label="月"
+                value="月"
+              />
+              <el-option
+                label="周"
+                value="周"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="计划日期">
+            <el-select
+              :disabled="!autoConfig.startStatus"
+              v-model="autoConfig.planDate"
+              placeholder="请选择计划日期"
+              clearable
+              @change="handleClickAuto"
+            >
+              <el-option
+                v-for="option in planDateOptions"
+                :key="option.value"
+                :label="option.label"
+                :value="option.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="5.5">
+          <el-form-item label="检察员">
+            <el-select
+              :disabled="!autoConfig.startStatus"
+              v-model="autoConfig.checkUserId"
+              placeholder="请选择检察员"
+              clearable
+              @change="handleClickAuto"
+            >
+              <el-option
+                v-for="dict in planPersonOption"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="5.5 " style="margin-top: 8px">
+          <span>(*自动创建的检查计划,覆盖所有物资柜)</span>
+        </el-col>
+      </el-form>
 
+    </el-row>
+    <!--    自动配置检查计划结束-->
     <el-table
       v-loading="loading"
       :data="PlanList"
@@ -138,7 +208,8 @@
       <el-table-column label="物资柜" align="center" prop="cabinetName">
         <template slot-scope="scope">
           <el-tooltip :content="scope.row.cabinetName" placement="top">
-            <span style="color:#1890ff;cursor: pointer" @click="PlanDetail(scope.row)">{{ getDisplayText(scope.row.cabinetName) }}</span>
+            <span style="color:#1890ff;cursor: pointer" @click="PlanDetail(scope.row)"
+            >{{ getDisplayText(scope.row.cabinetName) }}</span>
           </el-tooltip>
         </template>
       </el-table-column>
@@ -212,8 +283,8 @@
           <el-input v-model="form.planName" placeholder="请输入计划名称"/>
         </el-form-item>
         <el-form-item label="所属区域" prop="workstationId">
-          <treeselect  style="width: 348px" v-model="form.workstationId" :options="workstationOption"
-                       :normalizer="normalizer" placeholder="选择所属区域"
+          <treeselect style="width: 348px" v-model="form.workstationId" :options="workstationOption"
+                      :normalizer="normalizer" placeholder="选择所属区域"
           />
         </el-form-item>
         <el-form-item label="物资柜" prop="cabinetIds">
@@ -270,13 +341,13 @@
         <el-button v-no-more-click @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
-<!--    签名弹窗-->
+    <!--    签名弹窗-->
     <el-dialog :visible.sync="openTable" width="800px" append-to-body>
       <div slot="title" class="dialog-title">
         <i></i>
         <span class="title">巡检物资柜详情</span>
       </div>
-      <el-table  v-loading="loading" :data="PlanDetailTableList">
+      <el-table v-loading="loading" :data="PlanDetailTableList">
         <el-table-column label="计划名称" align="center" prop="planName">
         </el-table-column>
         <el-table-column label="巡检物资柜" align="center" prop="cabinetName">
@@ -311,13 +382,13 @@
         <el-button v-no-more-click @click="cancel">关 闭</el-button>
       </div>
     </el-dialog>
-<!--  签名详情弹窗-->
+    <!--  签名详情弹窗-->
     <el-dialog :visible.sync="openRecord" width="1500px" append-to-body>
       <div slot="title" class="dialog-title">
         <i></i>
         <span class="title">物资柜检查记录</span>
       </div>
-      <el-table  v-loading="loading" :data="RecordList">
+      <el-table v-loading="loading" :data="RecordList">
         <el-table-column label="计划名称" align="center" prop="planName" width="200">
           <template slot-scope="scope">
             {{ scope.row.planName }}
@@ -389,7 +460,7 @@ import {
   addPlan,
   deletePlan,
   updatePlan,
-  selectPlanById, getCheckPlanCabinetList
+  selectPlanById, getCheckPlanCabinetList, updateAutomaticConfig, selectIsMailNotifyConfigByCode
 } from '@/api/mes/material/plan'
 import Crontab from '@/components/Crontab/index.vue'
 import Treeselect from '@riophae/vue-treeselect'
@@ -409,11 +480,19 @@ export default {
       required: false
     }
   },
-  dicts: ['material_status', 'checking_status','checks_status','exceptions_status'],
+  dicts: ['material_status', 'checking_status', 'checks_status', 'exceptions_status'],
   data() {
     return {
       //自动生成编码
       autoGenFlag: false,
+      // 自动配置检查计划数据项
+      autoConfig: {
+        planFrequency: null,
+        planDate: null,
+        checkUserId: null,
+        startStatus: null,
+        templateCode: 'CHECK_PLAN'
+      },
       optType: undefined,
       // 遮罩层
       loading: true,
@@ -428,21 +507,21 @@ export default {
       showSearch: true,
       // 总条数
       total: 0,
-      recordtotal:0,//签名详情渲染
-      recordCurrent:1,//签名列表页面
-      recordSize:10,
+      recordtotal: 0,//签名详情渲染
+      recordCurrent: 1,//签名列表页面
+      recordSize: 10,
       // 班组表格数据
       PlanList: [],
-      PlanDetailTableList:[],
+      PlanDetailTableList: [],
       // 检查记录表格数据
       RecordList: [],
       // 弹出层标题
       title: '',
       // 是否显示弹出层
       open: false,
-      openTable:false,//巡检物资柜详情
-      openRecord:false,//签名弹框详情再打开
-      RecordselectedRow:null,//保证签名详情列表分页切换查询数据还在
+      openTable: false,//巡检物资柜详情
+      openRecord: false,//签名弹框详情再打开
+      RecordselectedRow: null,//保证签名详情列表分页切换查询数据还在
       // 查询参数
       createTime: '',
       queryParams: {
@@ -464,16 +543,20 @@ export default {
       workstationOption: [],//所属岗位
       // 表单校验
       rules: {
-        userIdStr: [
-          { required: true, message: '人员不能为空', trigger: 'blur' }
+        planDate: [
+          { required: true, message: '日期不能为空', trigger: 'blur' }
+        ],
+        planName: [
+          { required: true, message: '计划名称不能为空', trigger: 'blur' }
+        ],
+        checkUserId: [
+          { required: true, message: '检察员不能为空', trigger: 'blur' }
         ],
-        checkName: [
-          { required: true, message: '任务名称不能为空', trigger: 'blur' }
+        cabinetIds: [
+          { required: true, message: '物资柜不能为空', trigger: 'blur' }
         ]
-
       },
       pickerOptions: {
-
         shortcuts: [
           {
             text: '最近一周',
@@ -506,7 +589,7 @@ export default {
       },
       FormpickerOptions: {
         disabledDate(time) {
-          return time.getTime() < Date.now() - 8.64e7;
+          return time.getTime() < Date.now() - 8.64e7
         },
         shortcuts: [
           {
@@ -538,7 +621,8 @@ export default {
           }
         ]
       },
-      visibleSelect: false//控制查询
+      visibleSelect: false,//控制查询
+      planDateOptions: null
     }
   },
   watch: {
@@ -553,8 +637,8 @@ export default {
         getIsMaterialsCabinets(data).then((response) => {
           console.log(response, 'wgahah')
           if (response.data.records) {
-            this.form.cabinetIds = response.data.records.map((item) => item.cabinetId);
-            console.log(this.form.cabinetIds,'this.form.cabinetIds')
+            this.form.cabinetIds = response.data.records.map((item) => item.cabinetId)
+            console.log(this.form.cabinetIds, 'this.form.cabinetIds')
             this.cabinets = response.data.records.map((item) => ({
               value: item.cabinetId,
               label: item.cabinetName
@@ -571,9 +655,101 @@ export default {
     }
     this.getList()
     this.getOtherList()
+    this.getAutoConfigInfo()
+
   },
 
   methods: {
+    handleClickAuto() {
+      let StartValue = null
+      if (this.autoConfig.startStatus == false) {
+        StartValue = 0
+      } else {
+        StartValue = 1
+      }
+      const data = {
+        planFrequency: this.autoConfig.planFrequency,
+        planDate: this.autoConfig.planDate,
+        checkUserId: this.autoConfig.checkUserId,
+        startStatus: StartValue,
+        templateCode: this.autoConfig.templateCode
+      }
+      updateAutomaticConfig(data).then(res => {
+        console.log(res, '自动创建设置成功')
+        if (res.data) {
+          // this.$message.success('更新成功')
+        }
+      })
+    },
+    handleClickStatus(value) {
+      console.log(value, 'checkbox数据是否更换')
+      let StartValue = null
+      if (value == false) {
+        StartValue = 0
+      } else {
+        StartValue = 1
+      }
+      console.log(this.autoConfig, '拿到的自定义数据')
+      const data = {
+        planFrequency: this.autoConfig.planFrequency,
+        planDate: this.autoConfig.planDate,
+        checkUserId: this.autoConfig.checkUserId,
+        startStatus: StartValue,
+        templateCode: this.autoConfig.templateCode
+      }
+      updateAutomaticConfig(data).then(res => {
+        console.log(res, '自动创建设置成功')
+        if (res.data) {
+          this.$message.success('更新成功')
+        }
+      })
+    },
+    handleFrequencyChange(frequency) {
+      this.autoConfig.planDate = ''
+      if (frequency === '月') {
+        // 生成 1 到 31 的日期,并将个位数格式化为两位数
+        this.planDateOptions = Array.from({ length: 31 }, (_, i) => {
+          const day = i + 1
+          const formattedDay = String(day).padStart(2, '0') // 将个位数格式化为两位数
+          return {
+            value: formattedDay,
+            label: formattedDay
+          }
+        })
+      } else if (frequency === '周') {
+        this.planDateOptions = [
+          { value: 1, label: '周一' },
+          { value: 2, label: '周二' },
+          { value: 3, label: '周三' },
+          { value: 4, label: '周四' },
+          { value: 5, label: '周五' },
+          { value: 6, label: '周六' },
+          { value: 7, label: '周日' }
+        ]
+      } else {
+        this.planDateOptions = []
+      }
+      //   每次更新都调用接口
+      this.handleClickAuto()
+    },
+//     自动创建计划检查 回显函数
+    getAutoConfigInfo() {
+      const data = {
+        templateCode: 'CHECK_PLAN'
+      }
+      selectIsMailNotifyConfigByCode(data).then(res => {
+        console.log(res, '拿到了自动创建检查计划回显数据')
+        this.autoConfig.planDate = res.data.planDate
+        this.autoConfig.planFrequency = res.data.planFrequency
+        this.handleFrequencyChange(res.data.planFrequency)
+        this.autoConfig.checkUserId = res.data.checkUserId
+        if (res.data.startStatus == '0') {
+          this.autoConfig.startStatus = false
+        } else {
+          this.autoConfig.startStatus = true
+        }
+      })
+    },
 // 格式化日期查询数据
     formatDate(date) {
       const year = date.getFullYear().toString().padStart(2, '0')
@@ -605,7 +781,7 @@ export default {
         )
         this.loading = false
       })
-     ;
+
     },
 
     getOtherList() {
@@ -624,7 +800,7 @@ export default {
       const data1 = {
         pageSize: 999999999,
         pageNum: 1,
-        roleId:12
+        roleId: 12
       }
       listUser(data1).then((res) => {
         this.planPersonOption = res.rows.map((item) => {
@@ -665,34 +841,34 @@ export default {
       return names[0]
     },
     // 巡检物资柜 点击事件
-    PlanDetail(row){
-      console.log(row,'签名分页')
-      this.openTable=true
-      const data={
-        planId:row.planId,
+    PlanDetail(row) {
+      console.log(row, '签名分页')
+      this.openTable = true
+      const data = {
+        planId: row.planId,
         pageSize: 999999999,
-        pageNum: 1,
+        pageNum: 1
       }
       getCheckPlanCabinetList(data).then((response) => {
-        console.log(response,'物资柜 签名分页')
-        this.PlanDetailTableList=response.data
+        console.log(response, '物资柜 签名分页')
+        this.PlanDetailTableList = response.data
       })
     },
     // 签名弹窗内部详情查看
     handlePagination() {
       // 确保传递当前选中的 row 参数
-      this.PlanDialogDetail(this.RecordselectedRow);
+      this.PlanDialogDetail(this.RecordselectedRow)
     },
-    PlanDialogDetail(row){
-      this.openRecord=true
-      this.RecordselectedRow = row || this.RecordselectedRow;
-      console.log(this.RecordselectedRow,'存储了参数 详情')
+    PlanDialogDetail(row) {
+      this.openRecord = true
+      this.RecordselectedRow = row || this.RecordselectedRow
+      console.log(this.RecordselectedRow, '存储了参数 详情')
       const data = {
         planId: this.RecordselectedRow.planId,
         cabinetId: this.RecordselectedRow.cabinetId,
         current: this.recordCurrent,
-        size: this.recordSize,
-      };
+        size: this.recordSize
+      }
       listCheckRecord(data).then((response) => {
         console.log(response, 'response')
         this.RecordList = response.data.records
@@ -727,7 +903,7 @@ export default {
       if (this.visibleSelect) {
         this.form.cabinetIds = [this.cabinetId]
       }
-      this.form.planName=`物资检查计划${this.formatELTime(new Date())}`
+      this.form.planName = `物资检查计划${this.formatELTime(new Date())}`
     },
 
     formatELTime(date) {
@@ -784,8 +960,8 @@ export default {
 
       this.reset()
     },
-    cancelDetail(){
-      this.openRecord=false
+    cancelDetail() {
+      this.openRecord = false
     },
     // 表单重置
     reset() {
@@ -818,7 +994,6 @@ export default {
       this.multiple = !selection.length
     },
 
-
     /** 删除按钮操作 */
     handleDelete(row) {
       const checkIds = row.planId || this.ids
@@ -834,11 +1009,11 @@ export default {
         .catch(() => {
         })
     },
-  //   计划日期清空
-    handleClearTime(value){
-      if(value==null){
-        this.queryParams.startTime=null
-        this.queryParams.endTime=null
+    //   计划日期清空
+    handleClearTime(value) {
+      if (value == null) {
+        this.queryParams.startTime = null
+        this.queryParams.endTime = null
       }
     }
 
@@ -847,7 +1022,7 @@ export default {
 </script>
 <style lang="scss" src="@/assets/styles/dialog-title.scss" scoped>
 </style>
-<style lang="scss" >
+<style lang="scss">
 //图片放大
 .img-box {
   width: 50px;
@@ -876,6 +1051,7 @@ export default {
     pointer-events: none
   }
 }
+
 .img-box1 {
   width: 90px;
   height: 90px;
@@ -885,10 +1061,12 @@ export default {
     display: none;
   }
 }
+
 .img-box1:hover {
   background: #000;
   position: absolute;
   top: 62%;
+
   .images {
     opacity: 0.6;
   }
@@ -903,6 +1081,7 @@ export default {
     pointer-events: none
   }
 }
+
 .el-input-width {
   width: 380px !important;
 }

+ 340 - 204
src/views/mes/material/lockers/index.vue

@@ -13,6 +13,7 @@
       "
       v-show="tabPosition == 'first'"
     >
+
       <!-- 缩放容器 -->
       <div
         ref="mapContainer"
@@ -24,11 +25,13 @@
           position: 'relative',
         }"
       >
+
         <img
           style="width: 100%; height: 100%"
           src="@/assets/images/marsBg.png"
           alt=""
         />
+
         <img
           v-for="(cabinet, index) in TicketListPage"
           :key="cabinet.cabinetId"
@@ -78,6 +81,25 @@
         <!--        />-->
         <div class="deptXLG">LD</div>
         <!--        <div class="deptCCO">CCO</div>-->
+        <img style="width: 40px;height: 40px;position: absolute;top:2%;right: -4%" src="@/assets/images/警告.png" alt=""
+             @click="showExTable"
+        >
+        <div v-if="exceptionTableVisible"
+             style="width: 430px;height: 300px;position: absolute;top:2%;right:0;background: white "
+        >
+          <div style="display: flex">
+            <img src="@/assets/images/警告2.png" style="width: 20px;height: 20px;margin: 10px" alt="">
+            <span style="margin: 10px">异常信息</span>
+          </div>
+
+          <el-table :data="exceptionTable">
+            <el-table-column label="物资柜" prop="loanFromName"></el-table-column>
+            <el-table-column label="异常类型" prop="exceptionType"></el-table-column>
+            <el-table-column label="异常信息" prop="status"></el-table-column>
+          </el-table>
+        </div>
+
+
       </div>
     </div>
     <!-- 物资列表 -->
@@ -138,16 +160,16 @@
                 @keyup.enter.native="handleQuery"
               />
             </el-form-item>
-<!--            <el-form-item label="物资柜状态" prop="status">-->
-<!--              <el-select v-model="queryParams.status" placeholder="物资柜状态">-->
-<!--                <el-option-->
-<!--                  v-for="dict in dict.type.cabinet_status"-->
-<!--                  :key="dict.value"-->
-<!--                  :label="dict.label"-->
-<!--                  :value="dict.value"-->
-<!--                />-->
-<!--              </el-select>-->
-<!--            </el-form-item>-->
+            <!--            <el-form-item label="物资柜状态" prop="status">-->
+            <!--              <el-select v-model="queryParams.status" placeholder="物资柜状态">-->
+            <!--                <el-option-->
+            <!--                  v-for="dict in dict.type.cabinet_status"-->
+            <!--                  :key="dict.value"-->
+            <!--                  :label="dict.label"-->
+            <!--                  :value="dict.value"-->
+            <!--                />-->
+            <!--              </el-select>-->
+            <!--            </el-form-item>-->
             <el-form-item>
               <el-button
                 v-no-more-click
@@ -155,14 +177,16 @@
                 icon="el-icon-search"
                 size="mini"
                 @click="handleQuery"
-                >搜索</el-button
+              >搜索
+              </el-button
               >
               <el-button
                 v-no-more-click
                 icon="el-icon-refresh"
                 size="mini"
                 @click="resetQuery"
-                >重置</el-button
+              >重置
+              </el-button
               >
             </el-form-item>
           </el-form>
@@ -177,7 +201,7 @@
               size="mini"
               @click="handleAdd"
               v-hasPermi="['iscs:cabinet:add']"
-              >新增
+            >新增
             </el-button>
           </el-col>
           <right-toolbar
@@ -193,7 +217,7 @@
           <!--          <el-table-column type="selection" width="55" align="center"/>-->
           <el-table-column label="物资柜编号" align="center" prop="cabinetId">
           </el-table-column>
-          <el-table-column label="物资柜名称" prop="cabinetName" width="150" />
+          <el-table-column label="物资柜名称" prop="cabinetName" width="150"/>
           <el-table-column label="物资柜图片" prop="cabinetPicture" width="100">
             <template slot-scope="scope">
               <div class="img-box" v-if="scope.row.cabinetPicture">
@@ -222,7 +246,7 @@
               />
             </template>
           </el-table-column>
-          <el-table-column label="异常类型" prop="exReason" width="100" >
+          <el-table-column label="异常类型" prop="exReason" width="100">
             <template slot-scope="scope">
               <dict-tag
                 :options="dict.type.exception_reason"
@@ -237,7 +261,7 @@
                 size="mini"
                 type="text"
                 @click="handleLook(scope.row)"
-                >查看
+              >查看
               </el-button>
             </template>
           </el-table-column>
@@ -254,7 +278,7 @@
                 icon="el-icon-edit"
                 @click="handleUpdate(scope.row)"
                 v-hasPermi="['iscs:cabinet:edit']"
-                >修改
+              >修改
               </el-button>
               <el-button
                 v-no-more-click
@@ -263,7 +287,7 @@
                 icon="el-icon-delete"
                 @click="handleDelete(scope.row)"
                 v-hasPermi="['iscs:cabinet:remove']"
-                >删除
+              >删除
               </el-button>
             </template>
           </el-table-column>
@@ -339,14 +363,15 @@
           type="primary"
           @click="cancel"
           v-if="optType == 'view'"
-          >返回
+        >返回
         </el-button>
         <el-button v-no-more-click type="primary" @click="submitForm" v-else
-          >确 定
+        >确 定
         </el-button>
         <el-button v-no-more-click @click="cancel">取 消</el-button>
       </div>
     </el-dialog>
+
   </div>
 </template>
 
@@ -356,27 +381,29 @@ import {
   addMaterialsCabinet,
   updateMaterialsCabinet,
   deleteMaterialsCabinet,
-  selectMaterialsCabinetById,
-} from "@/api/mes/material/lockers.js";
+  selectMaterialsCabinetById
+} from '@/api/mes/material/lockers.js'
 
-import Treeselect from "@riophae/vue-treeselect";
-import "@riophae/vue-treeselect/dist/vue-treeselect.css";
-import { genCode } from "@/api/system/autocode/rule";
-import { listMarsDept } from "@/api/system/marsdept";
-import { listLoto } from "@/api/mes/lotoStation/lotoStation";
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { genCode } from '@/api/system/autocode/rule'
+import { listMarsDept } from '@/api/system/marsdept'
+import { listLoto } from '@/api/mes/lotoStation/lotoStation'
+import { listMaterials } from '@/api/mes/material/information'
+import { MaterialsLoanExceptionPage } from '@/api/mes/material/exception'
 
 export default {
-  name: "Team",
+  name: 'Team',
   components: {
-    Treeselect,
+    Treeselect
   },
-  dicts: ["material_status", "cabinet_status",'exception_reason'],
+  dicts: ['material_status', 'cabinet_status', 'exception_reason'],
   data() {
     return {
       //自动生成编码
       autoGenFlag: false,
       optType: undefined,
-      tabPosition: "first", //顶部切换
+      tabPosition: 'first', //顶部切换
       // 遮罩层
       loading: true,
       // 选中数组
@@ -393,11 +420,11 @@ export default {
       // 班组表格数据
       CabinetList: [],
       // 弹出层标题
-      title: "",
+      title: '',
       // 是否显示弹出层
       open: false,
       // 查询参数
-      createTime: "",
+      createTime: '',
       queryParams: {
         current: 1,
         size: 10,
@@ -405,7 +432,7 @@ export default {
         workstationId: undefined,
         cabinetId: null,
         cabinetName: null,
-        status: null,
+        status: null
       },
       // queryParamsList: {
       //   current: 1,
@@ -415,51 +442,51 @@ export default {
       //   status: null,
       // },
       imageMap: {
-        0: "table_map2", //正常
-        1: "table_map1", //使用中
-        2: "table_map3", //异常
+        0: 'table_map2', //正常
+        1: 'table_map1', //使用中
+        2: 'table_map3' //异常
       },
       pickerOptions: {
         shortcuts: [
           {
-            text: "最近一周",
+            text: '最近一周',
             onClick(picker) {
-              const end = new Date();
-              const start = new Date();
-              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
-              picker.$emit("pick", [start, end]);
-            },
+              const end = new Date()
+              const start = new Date()
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
+              picker.$emit('pick', [start, end])
+            }
           },
           {
-            text: "最近一个月",
+            text: '最近一个月',
             onClick(picker) {
-              const end = new Date();
-              const start = new Date();
-              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
-              picker.$emit("pick", [start, end]);
-            },
+              const end = new Date()
+              const start = new Date()
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
+              picker.$emit('pick', [start, end])
+            }
           },
           {
-            text: "最近三个月",
+            text: '最近三个月',
             onClick(picker) {
-              const end = new Date();
-              const start = new Date();
-              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
-              picker.$emit("pick", [start, end]);
-            },
-          },
-        ],
+              const end = new Date()
+              const start = new Date()
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
+              picker.$emit('pick', [start, end])
+            }
+          }
+        ]
       },
       // 表单参数
       form: {},
       // 表单校验
       rules: {
         cabinetCode: [
-          { required: true, message: "物资柜编码不能为空", trigger: "blur" },
+          { required: true, message: '物资柜编码不能为空', trigger: 'blur' }
         ],
         cabinetName: [
-          { required: true, message: "物资柜名称不能为空", trigger: "blur" },
-        ],
+          { required: true, message: '物资柜名称不能为空', trigger: 'blur' }
+        ]
       },
       marsOptions: [], //岗位
       TicketListPage: [], //R&R岗位
@@ -469,317 +496,424 @@ export default {
         left: 500,
         top: 260,
         width: 65,
-        height: 25,
+        height: 25
       },
       deptCCOPosition: {
         left: 670,
         top: 480,
         width: 65,
-        height: 25,
+        height: 25
       },
+      exceptionTableVisible: false,//地图显示异常物资柜具体信息
+      exceptionTable: [],//地图异常数据table数据
       workstationOptions: [], //岗位
       defaultProps: {
-        children: "children",
-        label: "label",
-      },
-    };
+        children: 'children',
+        label: 'label'
+      }
+    }
   },
   computed: {
     deptXLGCenter() {
       return {
         left: this.deptXLGPosition.left + this.deptXLGPosition.width / 2,
-        top: this.deptXLGPosition.top + this.deptXLGPosition.height / 2,
-      };
+        top: this.deptXLGPosition.top + this.deptXLGPosition.height / 2
+      }
     },
     deptCCOCenter() {
       return {
         left: this.deptCCOPosition.left + this.deptCCOPosition.width / 2,
-        top: this.deptCCOPosition.top + this.deptCCOPosition.height / 2,
-      };
-    },
+        top: this.deptCCOPosition.top + this.deptCCOPosition.height / 2
+      }
+    }
   },
   watch: {
-    "queryParams.workstationId": function (newVal, oldVal) {
+    'queryParams.workstationId': function(newVal, oldVal) {
       if (newVal) {
         const data = {
           current: 1,
           size: -1,
-          workstationId: this.queryParams.workstationId,
-        };
+          workstationId: this.queryParams.workstationId
+        }
         getMaterialsCabinet(data).then((response) => {
-          this.CabinetList = response.data.records;
-        });
+          this.CabinetList = response.data.records
+        })
       }
     },
-    tabPosition: function (newVal, oldVal) {
+    tabPosition: function(newVal, oldVal) {
       if (newVal) {
-        this.getList();
+        this.getList()
       }
-    },
+    }
   },
   created() {
-    this.getList();
-    this.getOtherList();
+    this.getList()
+    this.getOtherList()
+    this.getExpection()
   },
 
   methods: {
+    // 显示异常的物资柜信息
+    showExTable() {
+      this.exceptionTableVisible = !this.exceptionTableVisible
+    },
     /** 查询物资柜信息列表 */
     getList() {
-      this.loading = true;
+      this.loading = true
       getMaterialsCabinet(this.queryParams).then((response) => {
-        this.CabinetList = response.data.records;
-        console.log(response, "所有物资柜");
-        this.total = response.data.total;
-        this.loading = false;
-      });
+        this.CabinetList = response.data.records
+        console.log(response, '所有物资柜')
+        this.total = response.data.total
+        this.loading = false
+      })
+
+    },
+
+    getExpection() {
+      // 调用 listMaterials 接口获取所有物资的信息
+      listMaterials({ pages: 1, size: -1 }).then(response => {
+        const materialsData = response.data.records || []
+        // 处理损坏、过期的物资
+        const damagedOrExpiredMaterials = materialsData.filter(material => material.status === '1' || material.status === '2')
+
+        const damagedOrExpiredCabinets = Array.from(new Set(damagedOrExpiredMaterials.map(material => material.cabinetName)))
+        console.log(damagedOrExpiredMaterials, 'damagedOrExpiredMaterials')
+        // 处理归还异常的物资
+        const misplacedMaterials = materialsData.filter(material => material.status === '3')
+        const misplacedCabinets = Array.from(new Set(misplacedMaterials.map(material => material.cabinetName)))
+        console.log(misplacedCabinets, 'misplacedCabinets')
+        // 整合归还异常的柜子信息
+        const misplacedCabinetDetails = misplacedCabinets.map(cabinet => ({
+          loanFromName: cabinet,
+          exceptionType: '物资放错柜子',
+          status: `${misplacedMaterials.filter(material => material.cabinetName === cabinet).length}件物资放错`
+        }))
+
+        // 整合损坏、过期的柜子信息
+        const damagedOrExpiredCabinetDetails = damagedOrExpiredCabinets.map(cabinet => ({
+          loanFromName: cabinet,
+          exceptionType: '物资放过期损坏',
+          status: `${damagedOrExpiredMaterials.filter(material => material.cabinetName === cabinet).length}件物资过期或损坏`
+        }))
+
+        // 调用 MaterialsLoanExceptionPage 接口获取柜门未关闭的数据
+        return MaterialsLoanExceptionPage({
+          pages: 1,
+          size: -1,
+          exceptionType: 1, // 1表示柜门未关
+          status: 0 // 0表示未处理
+        }).then(response1 => {
+          const unClosedCabinets = response1.data.records || []
+
+          if (unClosedCabinets) {
+            // 整合柜门未关闭的柜子信息
+            const unClosedCabinetDetails = unClosedCabinets.map(cabinet => ({
+              loanFromName: cabinet.loanFromName,
+              exceptionType: '超时未关柜门',
+              status: `柜门超过${this.calculateTimeDifference(cabinet.occurTime)}未关`
+            }))
+            // 合并所有结果
+            const result = [
+              ...misplacedCabinetDetails,
+              ...damagedOrExpiredCabinetDetails,
+              ...unClosedCabinetDetails
+            ]
+            const resultData = result.filter((item) => {
+              return item.loanFromName !== null
+            })
+            this.exceptionTable = resultData
+
+          } else {
+            // 合并所有结果
+            const result = [
+              ...misplacedCabinetDetails,
+              ...damagedOrExpiredCabinetDetails
+            ]
+            const resultData = result.filter((item) => {
+              return item.loanFromName !== null
+            })
+            this.exceptionTable = resultData
+
+          }
+
+        })
+      }).catch(error => {
+        console.error('Error fetching data:', error)
+      })
+
+    },
+    //辅助函数计算超时时间
+    calculateTimeDifference(occurTime) {
+      if (!occurTime) return '未知';
+
+      // 将时间字符串转换为 Date 对象
+      const occurDate = new Date(occurTime);
+      if (isNaN(occurDate.getTime())) {
+        console.error('Invalid date format:', occurTime);
+        return '未知';
+      }
+
+      const currentTime = new Date().getTime();
+      const timeDifference = (currentTime - occurDate.getTime()) / (1000 * 60); // 转换为分钟
+
+      if (timeDifference < 1) {
+        return `${(timeDifference * 60).toFixed(0)}秒`;
+      } else if (timeDifference < 60) {
+        return `${timeDifference.toFixed(1)}分钟`;
+      } else {
+        const hours = Math.floor(timeDifference / 60);
+        const minutes = (timeDifference % 60).toFixed(0);
+        return `${hours}小时${minutes}分钟`;
+      }
     },
     getOtherList() {
       const data = {
         pasge: 1,
-        size: -1,
-      };
+        size: -1
+      }
       getMaterialsCabinet(data).then((response) => {
-        console.log(response, "所有物资柜");
+        console.log(response, '所有物资柜')
         this.TicketListPage = response.data.records.filter((item) => {
-          return item.workstationId == "6";
-        });
+          return item.workstationId == '6'
+        })
         this.COCOTicketListPage = response.data.records.filter((item) => {
-          return item.workstationId == "7";
-        });
-        this.total = response.data.total;
-        this.loading = false;
-      });
+          return item.workstationId == '7'
+        })
+        this.total = response.data.total
+        this.loading = false
+      })
       listMarsDept(data).then((response) => {
         // 新增岗位单选
         const data = response.data.records.filter((item) => {
-          return item.parentId == "0";
-        });
-        this.marsOptions = this.handleTree(data, "workstationId", "parentId");
+          return item.parentId == '0'
+        })
+        this.marsOptions = this.handleTree(data, 'workstationId', 'parentId')
         // mars岗位树数据
-        this.workstationOptions = this.transformToTree(data);
+        this.workstationOptions = this.transformToTree(data)
         // 使用递归函数查找匹配的节点
         const selectedTreeNode = this.findNodeById(
           this.workstationOptions,
           this.queryParams.workstationId
-        );
+        )
         // 调用 handleNodeClick 方法
         if (selectedTreeNode) {
-          this.handleNodeClick(selectedTreeNode);
+          this.handleNodeClick(selectedTreeNode)
         } else {
-          console.log("未找到匹配的节点");
+          console.log('未找到匹配的节点')
         }
-      });
+      })
     },
     // mars岗位树点击事件
     handleNodeClick(data) {
-      this.queryParams.workstationId = data.id; //这里给查询传递参数
-      this.queryParams.workstationName = data.label; //这里给回显框显示中文
+      this.queryParams.workstationId = data.id //这里给查询传递参数
+      this.queryParams.workstationName = data.label //这里给回显框显示中文
     },
     /** 转换mars岗位树数据为树形结构 */
     transformToTree(records) {
-      const recordMap = {}; // 创建一个 Map 以存储所有记录
-      const tree = []; // 最终返回的树形结构
+      const recordMap = {} // 创建一个 Map 以存储所有记录
+      const tree = [] // 最终返回的树形结构
 
       // 初始化所有记录到 Map
       records.forEach((record) => {
         recordMap[record.workstationId] = {
           id: record.workstationId,
           label: record.workstationName,
-          children: [],
-        };
-      });
+          children: []
+        }
+      })
 
       // 遍历记录并构建树
       records.forEach((record) => {
-        const parentId = record.parentId;
+        const parentId = record.parentId
 
-        if (parentId === "0") {
+        if (parentId === '0') {
           // 如果是顶层节点,直接添加到树中
-          tree.push(recordMap[record.workstationId]);
+          tree.push(recordMap[record.workstationId])
         } else if (recordMap[parentId]) {
           // 如果有父节点,则将当前节点加入父节点的 children 中
-          recordMap[parentId].children.push(recordMap[record.workstationId]);
+          recordMap[parentId].children.push(recordMap[record.workstationId])
         }
-      });
+      })
 
-      return tree;
+      return tree
     },
     // mars岗位数深层次遍历
     findNodeById(nodes, targetId) {
       for (let i = 0; i < nodes.length; i++) {
-        const node = nodes[i];
+        const node = nodes[i]
         if (node.id === targetId) {
-          return node;
+          return node
         }
         if (node.children && node.children.length > 0) {
-          const foundNode = this.findNodeById(node.children, targetId);
+          const foundNode = this.findNodeById(node.children, targetId)
           if (foundNode) {
-            return foundNode;
+            return foundNode
           }
         }
       }
-      return null;
+      return null
     },
     /** 转换mars岗位数据结构 */
     Marsnormalizer(node) {
       if (node.children && !node.children.length) {
-        delete node.children;
+        delete node.children
       }
       return {
         id: node.workstationId,
         label: node.workstationName,
-        children: node.children,
-      };
+        children: node.children
+      }
     },
     // 筛选节点
     filterNode(value, data) {
-      if (!value) return true;
-      return data.label.indexOf(value) !== -1;
+      if (!value) return true
+      return data.label.indexOf(value) !== -1
     },
     // 岗位查询 清除事件
     handleClear() {
-      this.queryParams.workstationId = "";
-      this.queryParams.workstationName = "";
-      this.getList();
+      this.queryParams.workstationId = ''
+      this.queryParams.workstationName = ''
+      this.getList()
     },
 
     // 物资柜跳转详情
     handleCabinetClick(cabinet) {
       this.$router.push({
-        path: "/mes/material/lockers/DetailsIndex",
+        path: '/mes/material/lockers/DetailsIndex',
         query: {
           cabinetId: cabinet.cabinetId,
-          cabinetName: cabinet.cabinetName,
-        },
-      });
-      console.log(cabinet, "地图跳转详情拿到的");
+          cabinetName: cabinet.cabinetName
+        }
+      })
+      console.log(cabinet, '地图跳转详情拿到的')
     },
     // 物资柜列表跳转详情
     handleLook(row) {
       this.$router.push({
-        path: "/mes/material/lockers/DetailsIndex",
-        query: { cabinetId: row.cabinetId, cabinetName: row.cabinetName },
-      });
-      console.log("列表详情");
+        path: '/mes/material/lockers/DetailsIndex',
+        query: { cabinetId: row.cabinetId, cabinetName: row.cabinetName }
+      })
+      console.log('列表详情')
     },
     // 取消按钮
     cancel() {
-      this.open = false;
-      this.reset();
+      this.open = false
+      this.reset()
     },
     // 表单重置
     reset() {
       this.form = {
-        cabinetCode: "",
-        cabinetName: "",
-        workstationId: "",
-        remark: "",
-        cabinetPicture: "",
-      };
-
-      this.resetForm("form");
-      this.autoGenFlag = false;
+        cabinetCode: '',
+        cabinetName: '',
+        workstationId: '',
+        remark: '',
+        cabinetPicture: ''
+      }
+
+      this.resetForm('form')
+      this.autoGenFlag = false
     },
     /** 搜索按钮操作 */
     handleQuery() {
-      this.queryParams.current = 1;
-      this.getList();
+      this.queryParams.current = 1
+      this.getList()
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.createTime = "";
-      this.queryParams.cabinetCode = null;
-      this.queryParams.cabinetName = null;
-      this.queryParams.cabinetId = null;
-      this.queryParams.status = null;
-      this.resetForm("queryForm");
-
-      this.handleQuery();
+      this.createTime = ''
+      this.queryParams.cabinetCode = null
+      this.queryParams.cabinetName = null
+      this.queryParams.cabinetId = null
+      this.queryParams.status = null
+      this.resetForm('queryForm')
+
+      this.handleQuery()
     },
     // 多选框选中数据
     handleSelectionChange(selection) {
-      this.ids = selection.map((item) => item.cabinetId);
-      this.codes = selection.map((item) => item.cabinetCode);
-      this.single = selection.length !== 1;
-      this.multiple = !selection.length;
+      this.ids = selection.map((item) => item.cabinetId)
+      this.codes = selection.map((item) => item.cabinetCode)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
     },
 
     /** 新增按钮操作 */
     handleAdd() {
-      this.reset();
-      this.open = true;
-      this.form.workstationId = null;
-      this.title = "新增物资柜信息";
-      this.optType = "add";
+      this.reset()
+      this.open = true
+      this.form.workstationId = null
+      this.title = '新增物资柜信息'
+      this.optType = 'add'
     },
 
     /** 修改按钮操作 */
     handleUpdate(row) {
-      this.reset();
+      this.reset()
       selectMaterialsCabinetById(row.cabinetId).then((response) => {
-        this.form = response.data;
-        this.open = true;
-        this.title = "修改物资柜信息";
-        this.optType = "edit";
-      });
+        this.form = response.data
+        this.open = true
+        this.title = '修改物资柜信息'
+        this.optType = 'edit'
+      })
     },
     /** 提交按钮 */
     submitForm() {
-      this.$refs["form"].validate((valid) => {
+      this.$refs['form'].validate((valid) => {
         if (valid) {
           if (this.form.cabinetId != null) {
             updateMaterialsCabinet(this.form).then((response) => {
-              console.log(response, "修改返回");
-              this.$modal.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
+              console.log(response, '修改返回')
+              this.$modal.msgSuccess('修改成功')
+              this.open = false
+              this.getList()
+            })
           } else {
             addMaterialsCabinet(this.form).then((response) => {
-              console.log(response, "新增返回");
-              this.$modal.msgSuccess("新增成功");
-              this.open = false;
-              this.getList();
-            });
+              console.log(response, '新增返回')
+              this.$modal.msgSuccess('新增成功')
+              this.open = false
+              this.getList()
+            })
           }
         }
-      });
+      })
     },
     /** 删除按钮操作 */
     handleDelete(row) {
-      const cabinetIds = row.cabinetId || this.ids;
-      const cabinetCodes = row.cabinetCode || this.codes;
+      const cabinetIds = row.cabinetId || this.ids
+      const cabinetCodes = row.cabinetCode || this.codes
       this.$modal
         .confirm('是否确认删除所选数据项?')
-        .then(function () {
-          return deleteMaterialsCabinet(cabinetIds);
+        .then(function() {
+          return deleteMaterialsCabinet(cabinetIds)
         })
         .then(() => {
-          this.getList();
-          this.$modal.msgSuccess("删除成功");
+          this.getList()
+          this.$modal.msgSuccess('删除成功')
+        })
+        .catch(() => {
         })
-        .catch(() => {});
     },
     //图标上传成功
     handleIconUplaoded(imgUrl) {
-      this.form.cabinetPicture = imgUrl[0].url;
+      this.form.cabinetPicture = imgUrl[0].url
     },
     // 图标移除
     handleIconRemoved(imgUrl) {
-      this.form.cabinetPicture = null;
+      this.form.cabinetPicture = null
     },
     //自动生成编码
     handleAutoGenChange(autoGenFlag) {
       if (autoGenFlag) {
-        genCode("MATERIALS_CABINET").then((response) => {
-          this.form.cabinetCode = response;
-        });
+        genCode('MATERIALS_CABINET').then((response) => {
+          this.form.cabinetCode = response
+        })
       } else {
-        this.form.cabinetCode = null;
+        this.form.cabinetCode = null
       }
-    },
-  },
-};
+    }
+  }
+}
 </script>
 <style lang="scss" src="@/assets/styles/dialog-title.scss" scoped>
 </style>
@@ -811,6 +945,7 @@ export default {
   color: #fff;
   cursor: pointer;
 }
+
 //图片放大
 .img-box {
   width: 75px;
@@ -839,6 +974,7 @@ export default {
     pointer-events: none;
   }
 }
+
 .el-input-width {
   width: 380px !important;
 }

+ 1 - 1
src/views/mes/material/materialinformation/index.vue

@@ -548,7 +548,7 @@
         @selection-change="handleSelectionBindChange"
       >
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="物资编号" align="center" prop="materialsId">
+        <el-table-column :sortable="true" label="物资编号" align="center" prop="materialsId" >
         </el-table-column>
         <el-table-column label="物资名称" align="center" prop="materialsName" />
         <el-table-column

+ 1 - 1
src/views/mes/sop/sopm/index.vue

@@ -95,7 +95,7 @@
       ></right-toolbar>
     </el-row>
 
-    <el-table v-if="refreshTable" v-loading="loading" :data="deptList">
+    <el-table v-if="refreshTable" v-loading="loading" :data="deptList" empty-text="暂无数据">
       <!--      <el-table-column prop="sopIndex" label="SOP序号" ></el-table-column>-->
       <el-table-column
         prop="sopName"

+ 220 - 0
src/views/mes/statisticians/LockerChange.vue

@@ -0,0 +1,220 @@
+<template>
+  <div>
+    <span style="margin: 10px">统计区间:</span>
+    <el-date-picker
+      style="margin: 10px"
+      v-model="value2"
+      type="daterange"
+      align="right"
+      unlink-panels
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :picker-options="pickerOptions" >
+    </el-date-picker>
+    <div ref="barChart" style="width: 1000px; height: 700px;margin: 5% 0 0 20%"></div>  <!-- 这里指定了柱状图的宽高 -->
+  </div>
+</template>
+
+<script>
+import { getMaterialsChangeStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstance: null,
+      value2: this.getDefaultDateRange(),
+      queryParams:{
+        startTime: null,
+        endTime: null,
+      },
+      pickerOptions: {
+        shortcuts: [{
+          text: '最近一周',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近一个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近三个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            picker.$emit('pick', [start, end]);
+          }
+        }]
+      },
+    }
+  },
+  watch: {
+    value2(val) {
+      if(val){
+        this.getList()
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getDefaultDateRange() {
+      const today = new Date();
+      const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
+      return [oneMonthAgo, today];
+    },
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    // 请求数据
+    getList() {
+      if (Array.isArray(this.value2) && this.value2.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.value2[0])
+        this.queryParams.endTime = this.formatDate(this.value2[1])
+      }
+      getMaterialsChangeStatistics(this.queryParams).then(response => {
+        console.log(response, '物资柜状态统计--更换次数')
+        this.cabinetData = response.data // 获取数据后保存
+        this.renderChart()  // 数据获取完后渲染图表
+      })
+    },
+
+    // 渲染柱状图
+    renderChart() {
+      if (this.cabinetData.length === 0) return
+
+      // 初始化图表实例
+      this.chartInstance = echarts.init(this.$refs.barChart)
+
+      // 提取 X 轴和 Y 轴的数据
+      const materialTypeName = this.cabinetData.map(item => item.materialsTypeName)
+      const allCount = this.cabinetData.map(item => item.allCount)
+      const expireCount=this.cabinetData.map(item=>item.expireCount)
+      const badCount=this.cabinetData.map(item=>item.badCount)
+      // 配置图表的选项
+      const options = {
+        title: {
+          text: '物资更换统计',
+          left: 'center',
+          top: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross',
+            crossStyle: {
+              color: '#999'
+            }
+          }
+        },
+        toolbox: {
+          feature: {
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        legend: {
+          data: ['累计更换次数','过期更换次数','损坏更换次数'],
+          top: '5%',  // 调整图例距离顶部的距离,确保图例在标题下方
+          left: 'center',  // 图例居中显示
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: materialTypeName,
+            axisPointer: {
+              type: 'shadow'
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            name: '次数',
+            axisLabel: {
+              formatter: '{value} '  // 修改单位为 次
+            },
+            splitLine: {
+              show: false  // 隐藏纵向的网格线
+            }
+          }
+        ],
+        series: [
+          {
+            name: '累计更换次数',  // 修改为适当的名称
+            type: 'bar',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: allCount,
+            itemStyle: {
+              color: '#5470c6',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+          {
+            name: '过期更换次数',
+            type: 'bar',
+            stack: 'Ad',
+            emphasis: {
+              focus: 'series'
+            },
+            data: expireCount,
+            itemStyle: {
+              color: '#91cc75',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+          {
+            name: '损坏更换次数',
+            type: 'bar',
+            stack: 'Ad',
+            emphasis: {
+              focus: 'series'
+            },
+            data: badCount,
+            itemStyle: {
+              color: '#fac858',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          }
+        ]
+      };
+
+      // 设置图表的配置项
+      this.chartInstance.setOption(options)
+    }
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose()  // 销毁图表实例,避免内存泄漏
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+/* 如果需要更改图表的样式,可以在这里添加 */
+</style>

+ 195 - 0
src/views/mes/statisticians/LockerCollection.vue

@@ -0,0 +1,195 @@
+<template>
+  <div>
+    <span style="margin: 10px">统计区间:</span>
+    <el-date-picker
+      style="margin: 10px"
+      v-model="value2"
+      type="daterange"
+      align="right"
+      unlink-panels
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :picker-options="pickerOptions" >
+    </el-date-picker>
+    <div ref="barChart" style="width: 1000px; height: 700px;margin: 5% 0 0 20%"></div>  <!-- 这里指定了柱状图的宽高 -->
+  </div>
+</template>
+
+<script>
+import {  getMaterialsLoanStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstance: null,
+      value2: this.getDefaultDateRange(),
+      queryParams:{
+        startTime: null,
+        endTime: null,
+      },
+      pickerOptions: {
+        shortcuts: [{
+          text: '最近一周',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近一个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近三个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            picker.$emit('pick', [start, end]);
+          }
+        }]
+      },
+    }
+  },
+  watch: {
+    value2(val) {
+      if(val){
+        this.getList()
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getDefaultDateRange() {
+      const today = new Date();
+      const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
+      return [oneMonthAgo, today];
+    },
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    // 请求数据
+    getList() {
+      if (Array.isArray(this.value2) && this.value2.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.value2[0])
+        this.queryParams.endTime = this.formatDate(this.value2[1])
+      }
+      getMaterialsLoanStatistics(this.queryParams).then(response => {
+        console.log(response, '物资柜状态统计--开关次数')
+        this.cabinetData = response.data // 获取数据后保存
+        this.renderChart()  // 数据获取完后渲染图表
+      })
+    },
+
+    // 渲染柱状图
+    renderChart() {
+      if (this.cabinetData.length === 0) return
+
+      // 初始化图表实例
+      this.chartInstance = echarts.init(this.$refs.barChart)
+
+      // 提取 X 轴和 Y 轴的数据
+      const materialsTypeName = this.cabinetData.map(item => item.materialsTypeName)
+      const allCount = this.cabinetData.map(item => item.allCount)
+
+      // 配置图表的选项
+      const options = {
+        title: {
+          text: '物资领取统计',
+          left: 'center',
+          top: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross',
+            crossStyle: {
+              color: '#999'
+            }
+          }
+        },
+        toolbox: {
+          feature: {
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        legend: {
+          data: ['累计领取次数'],
+          top: '5%',  // 调整图例距离顶部的距离,确保图例在标题下方
+          left: 'center',  // 图例居中显示
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: materialsTypeName,
+            axisPointer: {
+              type: 'shadow'
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            name: '次数',
+            axisLabel: {
+              formatter: '{value} 次'  // 修改单位为 次
+            },
+            splitLine: {
+              show: false  // 隐藏纵向的网格线
+            }
+          }
+        ],
+        series: [
+          {
+            name: '开关次数',  // 修改为适当的名称
+            type: 'bar',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: allCount,
+            itemStyle: {
+              color: '#5470c6',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          }
+        ]
+      };
+
+      // 设置图表的配置项
+      this.chartInstance.setOption(options)
+    }
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose()  // 销毁图表实例,避免内存泄漏
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+/* 如果需要更改图表的样式,可以在这里添加 */
+</style>

+ 230 - 0
src/views/mes/statisticians/LockerDaily.vue

@@ -0,0 +1,230 @@
+<template>
+  <div>
+    <span style="margin: 10px">统计区间:</span>
+    <el-date-picker
+      style="margin: 10px"
+      v-model="value2"
+      type="daterange"
+      align="right"
+      unlink-panels
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :picker-options="pickerOptions" >
+    </el-date-picker>
+    <div ref="barChart" style="width: 1200px; height: 700px;margin: 5% 0 0 20%"></div>  <!-- 这里指定了柱状图的宽高 -->
+  </div>
+</template>
+
+<script>
+import { getDayLoanStatistics, getUserLoanStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstance: null,
+      value2: this.getDefaultDateRange(),
+      queryParams:{
+        startTime: null,
+        endTime: null,
+      },
+      pickerOptions: {
+        shortcuts: [{
+          text: '最近一周',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近一个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近三个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            picker.$emit('pick', [start, end]);
+          }
+        }]
+      },
+    }
+  },
+  watch: {
+    value2(val) {
+      if(val){
+        this.getList()
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getDefaultDateRange() {
+      const today = new Date();
+      const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
+      return [oneMonthAgo, today];
+    },
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    // 请求数据
+    getList() {
+      if (Array.isArray(this.value2) && this.value2.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.value2[0])
+        this.queryParams.endTime = this.formatDate(this.value2[1])
+      }
+      getDayLoanStatistics(this.queryParams).then(response => {
+        console.log(response, '物资柜状态统计--更换次数')
+        const sortData = response.data.sort((a, b) => {
+          const dateA = new Date(a.day);
+          const dateB = new Date(b.day);
+          return dateA - dateB; // 升序排序
+        });
+        console.log(sortData,'sortData');
+        this.cabinetData = sortData // 获取数据后保存
+        this.renderChart()  // 数据获取完后渲染图表
+      })
+    },
+
+    // 渲染柱状图
+    renderChart() {
+      if (this.cabinetData.length === 0) return
+
+      // 初始化图表实例
+      this.chartInstance = echarts.init(this.$refs.barChart)
+
+      // 提取 X 轴和 Y 轴的数据
+      const day = this.cabinetData.map(item => item.day)
+      const allCount = this.cabinetData.map(item => item.allCount)
+      const returnCount=this.cabinetData.map(item => item.returnCount)
+      const timeoutCount=this.cabinetData.map(item => item.timeoutCount)
+      // 配置图表的选项
+      const options = {
+        title: {
+          text: '每日领取归还统计',
+          left: 'center',
+          top: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross',
+            crossStyle: {
+              color: '#999'
+            }
+          }
+        },
+        toolbox: {
+          feature: {
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        legend: {
+          data: ['累计领取次数','累计正常归还次数','累计超时归还次数'],
+          top: '5%',  // 调整图例距离顶部的距离,确保图例在标题下方
+          left: 'center',  // 图例居中显示
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: day,
+            axisPointer: {
+              type: 'shadow'
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            name: '次数',
+            axisLabel: {
+              formatter: '{value} 次'  // 修改单位为 次
+            },
+            splitLine: {
+              show: false  // 隐藏纵向的网格线
+            }
+          }
+        ],
+        series: [
+          {
+            name: '累计领取次数',  // 修改为适当的名称
+            type: 'line',
+            barWidth: '25%',
+
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: allCount,
+            itemStyle: {
+              color: '#5470c6',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+          {
+            name: '累计正常归还次数',  // 修改为适当的名称
+            type: 'line',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: returnCount,
+            itemStyle: {
+              color: '#a6d58f',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+          {
+            name: '累计超时归还次数',  // 修改为适当的名称
+            type: 'line',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: timeoutCount,
+            itemStyle: {
+              color: '#fac85a',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          }
+        ]
+      };
+
+      // 设置图表的配置项
+      this.chartInstance.setOption(options)
+    }
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose()  // 销毁图表实例,避免内存泄漏
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+/* 如果需要更改图表的样式,可以在这里添加 */
+</style>

+ 197 - 0
src/views/mes/statisticians/LockerLending.vue

@@ -0,0 +1,197 @@
+<template>
+  <div>
+    <span style="margin: 10px">统计区间:</span>
+    <el-date-picker
+      style="margin: 10px"
+      v-model="value2"
+      type="daterange"
+      align="right"
+      unlink-panels
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :picker-options="pickerOptions" >
+    </el-date-picker>
+    <div ref="barChart" style="width: 1000px; height: 700px;margin: 5% 0 0 20%"></div>  <!-- 这里指定了柱状图的宽高 -->
+  </div>
+</template>
+
+<script>
+import {  getMaterialsLoanStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstance: null,
+      value2: this.getDefaultDateRange(),
+      queryParams:{
+        startTime: null,
+        endTime: null,
+      },
+      pickerOptions: {
+        shortcuts: [{
+          text: '最近一周',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近一个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近三个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            picker.$emit('pick', [start, end]);
+          }
+        }]
+      },
+    }
+  },
+  watch: {
+    value2(val) {
+      if(val){
+        this.getList()
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getDefaultDateRange() {
+      const today = new Date();
+      const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
+      return [oneMonthAgo, today];
+    },
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    // 请求数据
+    getList() {
+      if (Array.isArray(this.value2) && this.value2.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.value2[0])
+        this.queryParams.endTime = this.formatDate(this.value2[1])
+      }
+      getMaterialsLoanStatistics(this.queryParams).then(response => {
+        console.log(response, '物资柜状态统计--开关次数')
+        this.cabinetData = response.data // 获取数据后保存
+        this.renderChart()  // 数据获取完后渲染图表
+      })
+    },
+
+    // 渲染柱状图
+    renderChart() {
+      if (this.cabinetData.length === 0) return
+
+      // 初始化图表实例
+      this.chartInstance = echarts.init(this.$refs.barChart)
+
+      // 提取 X 轴和 Y 轴的数据
+      const materialsTypeName = this.cabinetData.map(item => item.materialsTypeName)
+      const averageTime = this.cabinetData.map(item => {
+        return item.averageTime/3600
+      })
+
+      // 配置图表的选项
+      const options = {
+        title: {
+          text: '物资借出平均时长',
+          left: 'center',
+          top: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross',
+            crossStyle: {
+              color: '#999'
+            }
+          }
+        },
+        toolbox: {
+          feature: {
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        legend: {
+          data: ['平均借出时长(小时)'],
+          top: '5%',  // 调整图例距离顶部的距离,确保图例在标题下方
+          left: 'center',  // 图例居中显示
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: materialsTypeName,
+            axisPointer: {
+              type: 'shadow'
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            name: '小时',
+            axisLabel: {
+              formatter: '{value} 小时'  // 修改单位为 次
+            },
+            splitLine: {
+              show: false  // 隐藏纵向的网格线
+            }
+          }
+        ],
+        series: [
+          {
+            name: '平均借出时长(小时)',  // 修改为适当的名称
+            type: 'bar',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: averageTime,
+            itemStyle: {
+              color: '#5470c6',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          }
+        ]
+      };
+
+      // 设置图表的配置项
+      this.chartInstance.setOption(options)
+    }
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose()  // 销毁图表实例,避免内存泄漏
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+/* 如果需要更改图表的样式,可以在这里添加 */
+</style>

+ 208 - 0
src/views/mes/statisticians/LockerMistake.vue

@@ -0,0 +1,208 @@
+<template>
+
+  <div class="charts-container">
+   <div style="width: 1400px;">
+     <span style="margin: 10px;">统计区间:</span>
+     <el-date-picker
+       style="margin: 10px"
+       v-model="value2"
+       type="daterange"
+       align="right"
+       unlink-panels
+       range-separator="至"
+       start-placeholder="开始日期"
+       end-placeholder="结束日期"
+       :picker-options="pickerOptions" >
+     </el-date-picker>
+   </div>
+    <!-- 使用 v-for 动态渲染饼图 -->
+    <div class="pie-chart" v-for="(cabinet, index) in cabinetData" :key="index">
+      <div :id="'pieChart' + cabinet.cabinetId" style="width: 450px; height: 300px;"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getCabinetStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstances: [],  // 存储所有饼图实例
+      value2: this.getDefaultDateRange(),
+      queryParams:{
+        startTime: null,
+        endTime: null,
+      },
+      pickerOptions: {
+        shortcuts: [{
+          text: '最近一周',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近一个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近三个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            picker.$emit('pick', [start, end]);
+          }
+        }]
+      },
+    }
+  },
+  watch: {
+    value2(val) {
+      if(val){
+        this.getList()
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getDefaultDateRange() {
+      const today = new Date();
+      const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
+      return [oneMonthAgo, today];
+    },
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    // 请求数据
+    getList() {
+      if (Array.isArray(this.value2) && this.value2.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.value2[0])
+        this.queryParams.endTime = this.formatDate(this.value2[1])
+      }
+      getCabinetStatistics(this.queryParams).then(response => {
+        console.log(response, '物资柜状态统计--开关次数')
+        this.cabinetData = response.data // 获取数据后保存
+
+        // 在数据加载完成后渲染图表
+        this.$nextTick(() => {
+          this.renderCharts();  // 在 DOM 更新完后再执行渲染
+        })
+      })
+    },
+
+    // 渲染饼图
+    renderCharts() {
+      this.cabinetData.forEach(cabinet => {
+        // 确保图表的 DOM 元素存在
+        const chartElement = document.getElementById('pieChart' + cabinet.cabinetId)
+        if (chartElement) {
+          // 初始化每个饼图的实例
+          const chart = echarts.init(chartElement)
+
+          // 配置饼图的选项
+          const options = {
+            title: {
+              text: cabinet.cabinetName,  // 添加饼图标题
+              left: 'center',
+              top: 'center',
+              textStyle: {
+                fontSize: 16,
+                fontWeight: 'bold',
+              },
+            },
+            legend: {
+              data: ['物资错放','超时未关' ],
+              bottom: '5%',
+              left: 'center',
+              textStyle: {
+                fontSize: 14,  // 设置图例的字体大小
+              }
+            },
+            tooltip: {
+              trigger: 'item',
+              formatter: '{b}: {c} 次 ({d}%)',
+            },
+            series: [
+              {
+                name: '异常情况',
+                type: 'pie',
+                radius: '60%',
+                center: ['50%', '50%'],
+                data: [
+                  { value: cabinet.openTimeoutCount || 0, name: '超时未关' },
+                  { value: cabinet.misplaceCount || 0, name: '物资错放' },
+                ],
+                itemStyle: {
+                  // 自定义颜色
+                  color: (params) => {
+                    if (params.name === '超时未关') {
+                      return '#91cc75' // 超时未关用绿色
+                    } else {
+                      return '#5470c6' // 物资错放用蓝色
+                    }
+                  },
+                },
+                label: {
+                  normal: {
+                    formatter: '{b}', // 显示每个扇区的名称
+                    position: 'outside',  // 标签位置在外侧
+                    align: 'left',  // 标签对齐
+                    fontSize: 14,
+                  }
+                }
+
+              },
+            ],
+          }
+
+          // 设置图表的配置项
+          chart.setOption(options)
+
+          // 将图表实例存储
+          this.chartInstances.push(chart)
+        }
+      })
+    }
+  },
+  beforeDestroy() {
+    // 销毁每个图表实例,避免内存泄漏
+    this.chartInstances.forEach(chart => {
+      chart.dispose()
+    })
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.charts-container {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-around;
+  margin-top: 20px;
+}
+
+.pie-chart {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+</style>

+ 196 - 0
src/views/mes/statisticians/LockerOpen.vue

@@ -0,0 +1,196 @@
+<template>
+  <div>
+    <span style="margin: 10px">统计区间:</span>
+    <el-date-picker
+      style="margin: 10px"
+      v-model="value2"
+      type="daterange"
+      align="right"
+      unlink-panels
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :picker-options="pickerOptions" >
+    </el-date-picker>
+    <div ref="barChart" style="width: 1000px; height: 700px;margin: 5% 0 0 20%"></div>  <!-- 这里指定了柱状图的宽高 -->
+  </div>
+</template>
+
+<script>
+import { getCabinetStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstance: null,
+      value2: this.getDefaultDateRange(),
+      queryParams:{
+        startTime: null,
+        endTime: null,
+      },
+      pickerOptions: {
+        shortcuts: [{
+          text: '最近一周',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近一个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近三个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            picker.$emit('pick', [start, end]);
+          }
+        }]
+      },
+    }
+  },
+  watch: {
+    value2(val) {
+      if(val){
+        this.getList()
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getDefaultDateRange() {
+      const today = new Date();
+      const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
+      return [oneMonthAgo, today];
+    },
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    // 请求数据
+    getList() {
+      if (Array.isArray(this.value2) && this.value2.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.value2[0])
+        this.queryParams.endTime = this.formatDate(this.value2[1])
+      }
+      console.log(this.queryParams,'canshu');
+      getCabinetStatistics(this.queryParams).then(response => {
+        console.log(response, '物资柜状态统计--开关次数')
+        this.cabinetData = response.data // 获取数据后保存
+        this.renderChart()  // 数据获取完后渲染图表
+      })
+    },
+
+    // 渲染柱状图
+    renderChart() {
+      if (this.cabinetData.length === 0) return
+
+      // 初始化图表实例
+      this.chartInstance = echarts.init(this.$refs.barChart)
+
+      // 提取 X 轴和 Y 轴的数据
+      const cabinetNames = this.cabinetData.map(item => item.cabinetName)
+      const openCounts = this.cabinetData.map(item => item.openCount)
+
+      // 配置图表的选项
+      const options = {
+        title: {
+          text: '物资柜开关次数',
+          left: 'center',
+          top: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross',
+            crossStyle: {
+              color: '#999'
+            }
+          }
+        },
+        toolbox: {
+          feature: {
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        legend: {
+          data: ['开关次数'],
+          top: '5%',  // 调整图例距离顶部的距离,确保图例在标题下方
+          left: 'center',  // 图例居中显示
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: cabinetNames,
+            axisPointer: {
+              type: 'shadow'
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            name: '次数',
+            axisLabel: {
+              formatter: '{value} 次'  // 修改单位为 次
+            },
+            splitLine: {
+              show: false  // 隐藏纵向的网格线
+            }
+          }
+        ],
+        series: [
+          {
+            name: '开关次数',  // 修改为适当的名称
+            type: 'bar',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: openCounts,
+            itemStyle: {
+              color: '#5470c6',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          }
+        ]
+      };
+
+      // 设置图表的配置项
+      this.chartInstance.setOption(options)
+    }
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose()  // 销毁图表实例,避免内存泄漏
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+/* 如果需要更改图表的样式,可以在这里添加 */
+</style>

+ 210 - 0
src/views/mes/statisticians/LockerReturn.vue

@@ -0,0 +1,210 @@
+<template>
+  <div>
+    <span style="margin: 10px">统计区间:</span>
+    <el-date-picker
+      style="margin: 10px"
+      v-model="value2"
+      type="daterange"
+      align="right"
+      unlink-panels
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :picker-options="pickerOptions" >
+    </el-date-picker>
+    <div ref="barChart" style="width: 1000px; height: 700px;margin: 5% 0 0 20%"></div>  <!-- 这里指定了柱状图的宽高 -->
+  </div>
+</template>
+
+<script>
+import { getMaterialsLoanStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstance: null,
+      value2: this.getDefaultDateRange(),
+      queryParams:{
+        startTime: null,
+        endTime: null,
+      },
+      pickerOptions: {
+        shortcuts: [{
+          text: '最近一周',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近一个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+            picker.$emit('pick', [start, end]);
+          }
+        }, {
+          text: '最近三个月',
+          onClick(picker) {
+            const end = new Date();
+            const start = new Date();
+            start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+            picker.$emit('pick', [start, end]);
+          }
+        }]
+      },
+    }
+  },
+  watch: {
+    value2(val) {
+      if(val){
+        this.getList()
+      }
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    getDefaultDateRange() {
+      const today = new Date();
+      const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
+      return [oneMonthAgo, today];
+    },
+    formatDate(date) {
+      const year = date.getFullYear().toString().padStart(2, "0");
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    // 请求数据
+    getList() {
+      if (Array.isArray(this.value2) && this.value2.length === 2) {
+        this.queryParams.startTime = this.formatDate(this.value2[0])
+        this.queryParams.endTime = this.formatDate(this.value2[1])
+      }
+      getMaterialsLoanStatistics(this.queryParams).then(response => {
+        console.log(response, '物资柜状态统计--开关次数')
+        this.cabinetData = response.data // 获取数据后保存
+        this.renderChart()  // 数据获取完后渲染图表
+      })
+    },
+
+    // 渲染柱状图
+    renderChart() {
+      if (this.cabinetData.length === 0) return
+
+      // 初始化图表实例
+      this.chartInstance = echarts.init(this.$refs.barChart)
+
+      // 提取 X 轴和 Y 轴的数据
+      const materialTypeName = this.cabinetData.map(item => item.materialsTypeName)
+      const returnCount = this.cabinetData.map(item => item.returnCount)
+      const timeoutCount=this.cabinetData.map(item=>item.timeoutCount)
+
+      // 配置图表的选项
+      const options = {
+        title: {
+          text: '物资归还统计',
+          left: 'center',
+          top: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross',
+            crossStyle: {
+              color: '#999'
+            }
+          }
+        },
+        toolbox: {
+          feature: {
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        legend: {
+          data: ['正常归还次数','超时归还次数'],
+          top: '5%',  // 调整图例距离顶部的距离,确保图例在标题下方
+          left: 'center',  // 图例居中显示
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: materialTypeName,
+            axisPointer: {
+              type: 'shadow'
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            name: '次数',
+            axisLabel: {
+              formatter: '{value} '  // 修改单位为 次
+            },
+            splitLine: {
+              show: false  // 隐藏纵向的网格线
+            }
+          }
+        ],
+        series: [
+          {
+            name: '正常归还次数',  // 修改为适当的名称
+            type: 'bar',
+            stack: 'Ad',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: returnCount,
+            itemStyle: {
+              color: '#5470c6',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+          ,
+          {
+            name: '超时归还次数',
+            type: 'bar',
+            stack: 'Ad',
+            emphasis: {
+              focus: 'series'
+            },
+            data: timeoutCount,
+            itemStyle: {
+              color: '#91cc75',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          }
+        ]
+      };
+
+      // 设置图表的配置项
+      this.chartInstance.setOption(options)
+    }
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose()  // 销毁图表实例,避免内存泄漏
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+/* 如果需要更改图表的样式,可以在这里添加 */
+</style>

+ 151 - 0
src/views/mes/statisticians/LockerSpeciality.vue

@@ -0,0 +1,151 @@
+<template>
+  <div>
+    <div ref="barChart" style="width: 1000px; height: 700px;margin: 5% 0 0 20%"></div>  <!-- 这里指定了柱状图的宽高 -->
+  </div>
+</template>
+
+<script>
+import { getMaterialsStatusStatistics } from '@/api/mes/statisticians'
+import * as echarts from 'echarts'
+
+export default {
+  name: 'warehouseEquipmentUsageStatus',
+  data() {
+    return {
+      cabinetData: [],  // 存储请求到的柜子数据
+      chartInstance: null,
+    }
+  },
+  mounted() {
+    this.getList()
+  },
+  methods: {
+    // 请求数据
+    getList() {
+      getMaterialsStatusStatistics().then(response => {
+        console.log(response, '物资柜状态统计--开关次数')
+        this.cabinetData = response.data // 获取数据后保存
+        this.renderChart()  // 数据获取完后渲染图表
+      })
+    },
+
+    // 渲染柱状图
+    renderChart() {
+      if (this.cabinetData.length === 0) return
+
+      // 初始化图表实例
+      this.chartInstance = echarts.init(this.$refs.barChart)
+
+      // 提取 X 轴和 Y 轴的数据
+      const materialTypeName = this.cabinetData.map(item => item.materialsTypeName)
+      const willExpireCount = this.cabinetData.map(item => item.willExpireCount)
+      const expiredCount=this.cabinetData.map(item=>item.expiredCount)
+      const badCount=this.cabinetData.map(item=>item.badCount)
+      // 配置图表的选项
+      const options = {
+        title: {
+          text: '特殊状态物资统计',
+          left: 'center',
+          top: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: 'cross',
+            crossStyle: {
+              color: '#999'
+            }
+          }
+        },
+        toolbox: {
+          feature: {
+            dataView: { show: true, readOnly: false },
+            magicType: { show: true, type: ['line', 'bar'] },
+            restore: { show: true },
+            saveAsImage: { show: true }
+          }
+        },
+        legend: {
+          data: ['即将过期','已过期','损坏数'],
+          top: '5%',  // 调整图例距离顶部的距离,确保图例在标题下方
+          left: 'center',  // 图例居中显示
+        },
+        xAxis: [
+          {
+            type: 'category',
+            data: materialTypeName,
+            axisPointer: {
+              type: 'shadow'
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            name: '次数',
+            axisLabel: {
+              formatter: '{value} '  // 修改单位为 次
+            },
+            splitLine: {
+              show: false  // 隐藏纵向的网格线
+            }
+          }
+        ],
+        series: [
+          {
+            name: '即将过期',  // 修改为适当的名称
+            type: 'bar',
+            stack: 'Ad',
+            barWidth: '25%',
+            tooltip: {
+              valueFormatter: function (value) {
+                return value + ' 次';  // 修改单位为 次
+              }
+            },
+            data: willExpireCount,
+            itemStyle: {
+              color: '#5470c6',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+          {
+            name: '已过期',
+            type: 'bar',
+            stack: 'Ad',
+            emphasis: {
+              focus: 'series'
+            },
+            data: expiredCount,
+            itemStyle: {
+              color: '#91cc75',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+          {
+            name: '损坏数',
+            type: 'bar',
+            stack: 'Ad',
+            emphasis: {
+              focus: 'series'
+            },
+            data: badCount,
+            itemStyle: {
+              color: '#fac858',  // 设置柱子的颜色为蓝色(你可以根据需要修改)
+            }
+          },
+        ]
+      };
+
+      // 设置图表的配置项
+      this.chartInstance.setOption(options)
+    }
+  },
+  beforeDestroy() {
+    if (this.chartInstance) {
+      this.chartInstance.dispose()  // 销毁图表实例,避免内存泄漏
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+/* 如果需要更改图表的样式,可以在这里添加 */
+</style>

+ 8 - 3
src/views/system/mars/index.vue

@@ -304,7 +304,6 @@ export default {
       immediate: true,
       handler(val) {
         console.log(val, "用户id");
-
         this.queryParams.userId = val;
         this.getList();
       },
@@ -377,6 +376,7 @@ export default {
         workstationName: undefined,
         userId: undefined,
       };
+
       this.autoGenFlag = false;
       this.resetForm("form");
     },
@@ -397,12 +397,17 @@ export default {
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.queryParams.userId = undefined;
+      if(this.$route.query.userId) {
+        this.queryParams.userId = this.$route.query.userId;
+      }else{
+        this.queryParams.userId = undefined;
+      }
       this.queryParams.unitName = "";
       this.queryParams.status = undefined;
       this.resetForm("queryForm");
       this.handleQuery();
-      this.$router.push("/user/mars");
+
+      // this.$router.push("/user/mars");
     },
     /** 新增按钮操作 */
     handleAdd(row) {

+ 7 - 1
src/views/system/user/index.vue

@@ -957,14 +957,20 @@ export default {
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.pageNum = 1;
+      if(this.$route.query.workstationId){
+        this.queryParams.workstationId = this.$route.query.workstationId;
+      }else {
+        this.queryParams.workstationId=''
+      }
       this.getList();
     },
     /** 重置按钮操作 */
     resetQuery() {
+
       this.dateRange = [];
       this.resetForm("queryForm");
       this.handleQuery();
-      this.$router.push("/user/user");
+      // this.$router.push("/user/user");
     },
     // 多选框选中数据
     handleSelectionChange(selection) {

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini