1、新增设备综合效率分析以及报警分析页面
2、新增总控车间看板跳转第三方页面及外部应用功能
3、调整分控车间看板每日生产计划表格一直显示3条记录
已添加4个文件
已修改4个文件
1249 ■■■■■ 文件已修改
src/views/mdc/base/MasterControlWorkshopSignage.vue 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/OEEAnalysis.vue 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/SubControlWorkshopSignage.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/alarmAnalysis.vue 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/modules/OEEAnalysis/OEEAnalysisList.vue 507 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/modules/SparePartsManagement/SparePartsModal.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/modules/SubControlWorkshopSignage/SignageModal.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/modules/alarmAnalysis/alarmAnalysisMain.vue 539 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mdc/base/MasterControlWorkshopSignage.vue
@@ -30,7 +30,7 @@
        </div>
        <div class="workshop-bg">
          <div class="navigate-container" style="top:0;left: 5%;">
            <div @click="navigateTo(item)" v-for="item in productionLineList" :key="item.id"
            <div @click="navigateToSubWorkshopSignage(item)" v-for="item in productionLineList" :key="item.id"
                 class="navigate-item">
              <template v-if="item.productionOrder<5">
                <div :style="{backgroundColor:productionLineBackgroundColorList[item.productionOrder%4]}"
@@ -45,7 +45,7 @@
            </div>
          </div>
          <div class="navigate-container" style="bottom:0;right: 5%;">
            <div @click="navigateTo(item)" v-for="item in productionLineList" :key="item.id"
            <div @click="navigateToSubWorkshopSignage(item)" v-for="item in productionLineList" :key="item.id"
                 class="navigate-item">
              <template v-if="item.productionOrder>=5">
                <div :style="{backgroundColor:productionLineBackgroundColorList[item.productionOrder%4]}"
@@ -61,7 +61,7 @@
          </div>
        </div>
        <div class="switch-container">
          <div @click="activeIndex=index" class="switch-item" v-for="(item,index) in switchList" :key="index"
          <div class="switch-item" v-for="(item,index) in switchList" :key="index" @click="navigateToOthers(item,index)"
               :style="{backgroundColor:activeIndex===index?'#848284':'#6B6D6B',color:activeIndex==index?'#27A2DB':'#000'}">
            {{item.label}}
          </div>
@@ -113,23 +113,38 @@
        switchList: [
          {
            label: 'MES',
            index: 0
            index: 0,
            isNavigateToWeb: true,
            webUrl: 'http://172.16.52.71:8081',
            batPath: ''
          },
          {
            label: '刀具管理',
            index: 1
            index: 1,
            isNavigateToWeb: true,
            webUrl: 'http://172.16.52.99/tms',
            batPath: ''
          },
          {
            label: '故障',
            index: 2
            label: '设备诊断',
            index: 2,
            isNavigateToWeb: false,
            webUrl: '',
            batPath: 'yituoSBZD://'
          },
          {
            label: '3D',
            index: 3
            label: '三维监控',
            index: 3,
            isNavigateToWeb: false,
            webUrl: '',
            batPath: 'yituoVR://'
          },
          {
            label: '安防',
            index: 4
            index: 4,
            isNavigateToWeb: false,
            webUrl: '',
            batPath: 'yituoAF://'
          }
        ],
        rightColChart1: '',
@@ -2148,7 +2163,7 @@
        return s
      },
      navigateTo(record) {
      navigateToSubWorkshopSignage(record) {
        const url = this.$router.resolve({
          path: '/SubControlWorkshopSignage',
          query: {
@@ -2160,6 +2175,15 @@
        window.open(url, '_blank')
      },
      navigateToOthers(record, index) {
        this.activeIndex = index
        if (record.isNavigateToWeb && record.webUrl) {
          window.open(record.webUrl, '_blank')
        } else {
          window.location.href = record.batPath
        }
      },
      /**
       * çª—口尺寸变化时触发
       * è°ƒæ•´å›¾è¡¨å°ºå¯¸ä»¥é€‚应分辨率
src/views/mdc/base/OEEAnalysis.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
<template>
  <div style="width: 100%; height: 100%;">
    <a-card :bordered="false">
      <a-row type="flex" :gutter="16">
        <a-col :md="5">
          <a-tabs :activeKey="activeKey" @change="tabChange">
            <a-tab-pane key="1" tab="车间层级" force-render>
              <base-tree @getCurrSelected="changeSelectionNode" :filterAbnormalDeviceKey="'1'"></base-tree>
            </a-tab-pane>
          </a-tabs>
        </a-col>
        <a-col :md="19">
          <OEEAnalysisList ref="deviceList" :nodeTree='selectEquipment' :nodePeople='selectPeople'
                           :Type="selectTypeTree"/>
        </a-col>
      </a-row>
    </a-card>
  </div>
</template>
<script>
  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
  import BaseTree from '../common/BaseTree'
  import OEEAnalysisList from './modules/OEEAnalysis/OEEAnalysisList'
  export default {
    name: 'OEEAnalysis',
    components: {
      OEEAnalysisList,
      BaseTree,
    },
    data() {
      return {
        activeKey: '1',
        description: '设备信息',
        selectEquipment: {},
        selectTypeTree: '',
        selectPeople: {},
        isDepartType: ''
      }
    },
    methods: {
      tabChange(val) {
        this.activeKey = val
        this.selectTypeTree = val
      },
      changeSelectionNode(val) {
        this.selectEquipment = val
        this.selectTypeTree = '1'
      },
      changeSelectionNodedd(val) {
        this.selectPeople = val
        this.selectTypeTree = '2'
      }
    }
  }
</script>
src/views/mdc/base/SubControlWorkshopSignage.vue
@@ -59,6 +59,22 @@
          <td>{{item.clazz}}</td>
        </template>
      </tr>
      <template v-if="todayProductionPlanList.length<=3">
        <tr v-for="(item,index) in 3-todayProductionPlanList.length"
            :key="index">
          <td colspan="2"></td>
          <td colspan="2"></td>
          <td colspan="2"></td>
          <td colspan="2"></td>
          <td></td>
          <td></td>
          <td></td>
          <td></td>
          <td colspan="2"></td>
          <td colspan="2"></td>
          <td></td>
        </tr>
      </template>
      </tbody>
    </table>
@@ -102,7 +118,7 @@
            <template v-for="(listItem,listIndex) in maxBrandToolLifeListLength">
              <template>
                <tr>
                  <td style="width: 7vw">额定寿命</td>
                  <td style="width: 5vw;min-width: 5vw">额定寿命</td>
                  <template v-for="(item,index) in toolLife.brandList">
                    <td rowspan="2" style="width: 2.5vw">
                      {{toolLife.lifeList[index][listIndex]?toolLife.lifeList[index][listIndex]?toolLife.lifeList[index][listIndex].tid:'':''}}
src/views/mdc/base/alarmAnalysis.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
<template>
  <div style="width: 100%; height: 100%;">
    <a-card :bordered="false">
      <a-row type="flex" :gutter="16">
        <a-col :md="5">
          <a-tabs :activeKey="activeKey"  @change="tabChange">
            <a-tab-pane key="1" tab="车间层级" force-render>
              <base-tree @getCurrSelected="changeSelectionNode"></base-tree>
            </a-tab-pane>
          </a-tabs>
        </a-col>
        <a-col :md="19">
          <alarm-analysis-main  ref="alarmAnalysisMain" :nodePeople='selectPeople' :nodeTree = 'selectEquipment' :Type="selectTypeTree"></alarm-analysis-main>
        </a-col>
      </a-row>
    </a-card>
  </div>
</template>
<script>
  import { JeecgListMixin } from '@/mixins/JeecgListMixin'
  import BaseTree from '../common/BaseTree'
  import alarmAnalysisMain from './modules/alarmAnalysis/alarmAnalysisMain'
  export default {
    name: 'alarmAnalysis',
    components: {
      BaseTree,
      alarmAnalysisMain
    },
    data() {
      return {
        activeKey: '1',
        description: '设备信息',
        selectEquipment: {},
        selectPeople:{},
        selectTypeTree:"",
        isDepartType:'',
      }
    },
    methods: {
      tabChange(val) {
        this.activeKey = val
        this.selectTypeTree = val
      },
      changeSelectionNode(val) {
        this.selectEquipment = val
        this.selectTypeTree = "1"
      },
      changeSelectionNodedd(val) {
        this.selectPeople = val
        this.selectTypeTree = "2"
      }
    }
  }
</script>
src/views/mdc/base/modules/OEEAnalysis/OEEAnalysisList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,507 @@
<template>
  <div style="width: 100%;">
    <div :bordered="false">
      <!-- æŸ¥è¯¢åŒºåŸŸ -->
      <div class="seach-content">
        <div class="table-page-search-wrapper">
          <a-form layout="inline" @keyup.enter.native="searchQuery">
            <a-row :gutter="24">
              <a-col :md="6" :sm="6" :xs="6">
                <a-form-item label="时间">
                  <a-range-picker @change="dateParamChange" :disabledDate="disabledDate" format="YYYYMMDD"
                                  v-model="dates" :allowClear="false"/>
                </a-form-item>
              </a-col>
              <a-col :md="4" :sm="4" :xs="4">
                <a-form-item label="班次">
                  <a-select v-model="queryParam.shiftSubId" placeholder="请选择班次" allow-clear>
                    <a-select-option key="01" value="01">早班</a-select-option>
                    <a-select-option key="02" value="02">中班</a-select-option>
                    <a-select-option key="03" value="03">晚班</a-select-option>
                  </a-select>
                </a-form-item>
              </a-col>
              <a-col :md="14" :sm="14" :xs="14"
                     style="display: flex;justify-content: space-between;align-items: flex-start">
                <div>
                  <a-space style="margin-right: 20px">
                    <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
                    <a-button type="primary" @click="searchReset" icon="reload">重置</a-button>
                    <a-button type="primary" @click="exportExcel" icon="download">导出</a-button>
                  </a-space>
                </div>
                <table cellpadding="5" cellspacing="1" style="border: 1px solid darkgray;">
                  <tr>
                    <td v-for="(item, index) in identifying">{{item.title}}</td>
                  </tr>
                  <tr>
                    <td style="text-align:center;" v-for="(item, index) in identifying">
                      <div class="identifyingclass" :style="{background: item.color}"></div>
                    </td>
                  </tr>
                </table>
              </a-col>
            </a-row>
          </a-form>
        </div>
      </div>
      <a-spin :spinning="spinning">
        <div class="container" id="EfficiencyShift" style="margin-top: 20px;">
          <div class="table2">
            <table class="dataContent table" border="1" cellspacing="0" cellpadding="0" v-if="dataList.length>0">
              <thead>
              <tr class="thead fixed equipname">
                <th class="thgu dong1 name" rowspan="2" style="min-width: 100px; max-width: 100px;width: 100px;">设备编号
                </th>
                <th class="thgu dong2 name" rowspan="2" style="min-width: 162px; max-width: 162px;width: 162px;">设备名称
                </th>
                <th class="thgu dong3 name" rowspan="2" style="min-width: 100px; max-width: 100px;width: 100px;">设备类型
                </th>
                <template v-for="(tableHead, index) in tableHeads">
                  <th class="timeth">{{tableHead}}</th>
                </template>
              </tr>
              <tr class="thead notfixed gudingth">
                <template v-for="(tableHead, index) in tableHeads">
                  <th>OEE</th>
                </template>
              </tr>
              </thead>
              <tbody>
              <tr class="mathData" v-for="(item, index) in dataList">
                <td class="tdgu1  kaitou">{{item.equipmentId}}</td>
                <td class="tdgu2  kaitou" style="min-width: 162px; max-width: 162px;width: 162px;">
                  {{item.equipmentName}}
                </td>
                <td class="tdgu3  kaitou">{{item.equipmentType}}</td>
                <template v-for="(tableHead, index) in item.dataList">
                  <td :style="{background:tableHead.color }">
                    {{tableHead.oeeRate | numFilter}}
                  </td>
                </template>
              </tr>
              </tbody>
            </table>
          </div>
        </div>
      </a-spin>
    </div>
  </div>
</template>
<script>
  import moment from 'moment'
  import { putAction, getAction } from '@/api/manage'
  import '@/components/table2excel/table2excel'
  import { ajaxGetDictItems, getDictItemsFromCache, duplicateCheck } from '@/api/api'
  import api from '@/api/mdc'
  export default {
    name: 'EfficiencyShiftList',
    components: {},
    data() {
      return {
        typeTree: '',
        typeParent: 1,
        typeEquipment: 1,
        dates: [],
        identifying: [],
        queryParam: {},
        queryParams: {
          equipmentType: [],
          driveType: [],
          deviceLevel: [],
          deviceCategory: []
        },
        queryParamEquip: {},
        queryParamPeople: {},
        dataList: [],
        url: {
          list: '/mdc/mdcOeeInfo/list',
          listByType: '/mdc/MdcUtilizationRate/getByType'
        },
        tableHeads: [],
        spinning: false
      }
    },
    props: { nodeTree: '', Type: '', nodePeople: '' },
    created() {
      this.showIdentifying()
      this.dates = [moment().subtract('days', 7), moment().subtract('days', 1)]
      this.queryParam.startTime = moment(this.dates[0]).format('YYYYMMDD')
      this.queryParam.endTime = moment(this.dates[1]).format('YYYYMMDD')
      this.queryParam.typeTree = '1'
      this.loadData()
    },
    mounted() {
      window.addEventListener('resize', this.handleWindowResize)
      this.handleWindowResize()
    },
    watch: {
      Type(valmath) {
        this.dataList = []
        this.queryParam.typeTree = valmath
      },
      nodeTree(val) { //监听currSelected å˜åŒ–,将变化后的数值传递给 getCurrSelected äº‹ä»¶
        if (JSON.stringify(val) != '{}') {
          if (val.equipmentId != null) {
            this.queryParamEquip.parentId = ''
            this.queryParamEquip.equipmentId = val.equipmentId
          } else {
            this.queryParamEquip.parentId = val.key
            this.queryParamEquip.equipmentId = ''
          }
          this.searchQuery()
        }
      },
      nodePeople(val) {
        if (JSON.stringify(val) != '{}') {
          if (val.equipmentId != null) {
            this.queryParamPeople.parentId = val.equipmentId
            this.queryParamPeople.equipmentId = ''
          } else {
            this.queryParamPeople.parentId = val.key
            this.queryParamPeople.equipmentId = ''
          }
          this.searchQuery()
        }
      }
    },
    filters: {
      numFilter(value) {
        if (value) {
          return parseFloat((value * 100).toFixed(2))
        } else {
          return '0'
        }
      },
      /**
       * æ ¼å¼åŒ–æ—¶é—´
       * @param seconds ç§’æ•°
       * @returns '' æ ¼å¼åŒ–后时间字符串
       */
      getFormattedTime(seconds) {
        if (seconds) {
          return parseFloat((seconds / 3600).toFixed(2))
        } else {
          return '0'
        }
      }
    },
    methods: {
      checkSameData(dataList) {
        let cache = {}  //存储的是键是kclx çš„值,值是kclx åœ¨indeces中数组的下标
        let indices = []  //数组中每一个值是一个数组,数组中的每一个元素是原数组中相同kclx的下标
        dataList.map((item, index) => {
          let level1 = item.level1
          let _index = cache[level1]
          if (_index !== undefined) {
            indices[_index].push(index)
          } else {
            cache[level1] = indices.length
            indices.push([index])
          }
        })
        let result = []
        indices.map((item) => {
          item.map((index) => {
            result.push(dataList[index])
          })
        })
        this.dataList = result
      },
      checkSameData1(dataList) {
        let cache = {}  //存储的是键是kclx çš„值,值是kclx åœ¨indeces中数组的下标
        let indices = []  //数组中每一个值是一个数组,数组中的每一个元素是原数组中相同kclx的下标
        dataList.map((item, index) => {
          let level2 = item.level2
          let _index = cache[level2]
          if (_index !== undefined) {
            indices[_index].push(index)
          } else {
            cache[level2] = indices.length
            indices.push([index])
          }
        })
        let result = []
        indices.map((item) => {
          item.map((index) => {
            result.push(dataList[index])
          })
        })
        this.dataList = result
      },
      checkSameData2(dataList) {
        let cache = {}  //存储的是键是kclx çš„值,值是kclx åœ¨indeces中数组的下标
        let indices = []  //数组中每一个值是一个数组,数组中的每一个元素是原数组中相同kclx的下标
        dataList.map((item, index) => {
          let level3 = item.level3
          let _index = cache[level3]
          if (_index !== undefined) {
            indices[_index].push(index)
          } else {
            cache[level3] = indices.length
            indices.push([index])
          }
        })
        let result = []
        indices.map((item) => {
          item.map((index) => {
            result.push(dataList[index])
          })
        })
        this.dataList = result
      },
      // åˆå¹¶
      combineCell() {
        let list = this.dataList
        for (let field in list[0]) {
          var k = 0
          while (k < list.length) {
            list[k][field + 'span'] = 1
            list[k][field + 'dis'] = false
            for (var i = k + 1; i <= list.length - 1; i++) {
              if (list[k][field] == list[i][field] && list[k][field] != '') {
                list[k][field + 'span']++
                list[k][field + 'dis'] = false
                list[i][field + 'span'] = 1
                list[i][field + 'dis'] = true
              } else {
                break
              }
            }
            k = i
          }
        }
        return list
      },
      disabledDate(current) {
        //Can not slect days before today and today
        return current && current > moment().subtract('days', 1)
      },
      initDeviceType(deviceList) {
        let dictCode = 'mdc_equipmentType'
        let items = []
        items = getDictItemsFromCache(dictCode)
        if (deviceList && items.length > 0) {
          for (let a = 0; a < deviceList.length; a++) {
            if (items && items.length > 0) {
              for (let i = 0; i < items.length; i++) {
                if (deviceList[a].equipmentType == items[i].value) {
                  deviceList[a].equipmentType = items[i].title
                }
              }
            } else {
              ajaxGetDictItems(dictCode, null).then((res) => {
                if (res.success) {
                  let items = res.result
                  for (let i = 0; i < items.length; i++) {
                    if (deviceList[a].equipmentType == items[i].value) {
                      deviceList[a].equipmentType = items[i].title
                    }
                  }
                }
              })
            }
          }
        }
      },
      exportExcel() {
        $('#EfficiencyShift').table2excel({
          exclude: '.noExl',
          name: 'Excel Document Name',
          filename: '班次利用率',
          exclude_img: true,
          fileext: '.xls',
          exclude_links: true,
          exclude_inputs: true
        })
      },
      showIdentifying() {
        getAction(this.url.listByType, { type: 'oee' }).then(res => {
          if (res.success) {
            this.identifying = res.result
          }
        })
      },
      dateParamChange(v1, v2) {
        this.queryParam.startTime = v2[0]
        this.queryParam.endTime = v2[1]
        // ç‚¹å‡»æ—¶é—´é€‰æ‹©å™¨çš„æ¸…空按钮时会触发此判断(点击重置按钮不会触发),实现重置列表功能,切实改变列表显示效果
        // if(!this.queryParam.startTime&&!this.queryParam.endTime)this.searchReset()
      },
      searchQuery() {
        if (this.queryParam.typeTree == '1') {
          this.queryParam.parentId = this.queryParamEquip.parentId
          this.queryParam.equipmentId = this.queryParamEquip.equipmentId
        } else {
          this.queryParam.parentId = this.queryParamPeople.parentId
          this.queryParam.equipmentId = ''
        }
        Object.keys(this.queryParams).forEach(item => {
          this.queryParam[item] = this.queryParams[item].join()
          // æ­¤å¤„为保证接口参数不多余,可省略
          if (this.queryParams[item].length === 0) delete this.queryParam[item]
        })
        this.loadData()
      },
      searchReset() {
        this.typeTree = this.queryParam.typeTree
        this.typeParent = this.queryParam.parentId
        this.typeEquipment = this.queryParam.equipmentId
        this.queryParam = {}
        this.dates = [moment().subtract('days', 7), moment().subtract('days', 1)]
        this.queryParam.startTime = moment(this.dates[0]).format('YYYYMMDD')
        this.queryParam.endTime = moment(this.dates[1]).format('YYYYMMDD')
        this.queryParam.typeTree = this.typeTree
        this.queryParam.parentId = this.typeParent
        this.queryParam.equipmentId = this.typeEquipment
        this.queryParams = {}
        this.loadData()
      },
      loadData() {
        this.spinning = true
        this.tableHeads = []
        this.dataList = []
        getAction(this.url.list, this.queryParam).then(res => {
          if (res.success) {
            console.log('res=', res)
            this.tableHeads = res.result.dates
            this.dataList = res.result.mdcOeeListDtoList
            if (res.result.mdcOeeListDtoList && !res.result.mdcOeeListDtoList.length) {
              this.$notification.info({
                message: '消息',
                description: '暂无该类型数据'
              })
            }
            this.checkSameData(this.dataList)
            this.checkSameData1(this.dataList)
            this.checkSameData2(this.dataList)
            this.combineCell()
          }
        }).finally(() => {
          this.spinning = false
        })
      },
      /**
       * åˆ†è¾¨çŽ‡æ”¹å˜æ—¶åŒæ—¶æ”¹å˜è¡¨æ ¼é«˜åº¦å·²ä¿è¯é¦–é¡µä¸€è¿›å…¥ä¸æ‹–åŠ¨åž‚ç›´æ»šåŠ¨æ¡æ—¶å³å¯æ‹–åŠ¨è¡¨æ ¼æ°´å¹³æ»šåŠ¨æ¡
       */
      handleWindowResize() {
        const tableContainer = document.getElementById('EfficiencyShift') // è¡¨æ ¼å®¹å™¨
        const clientHeight = document.documentElement.clientHeight || document.body.clientHeight // æµè§ˆå™¨å¯è§†åŒºåŸŸé«˜åº¦
        const containerTopToClientTopHeight = tableContainer.getBoundingClientRect().top // è¡¨æ ¼å®¹å™¨é¡¶éƒ¨åˆ°æµè§ˆå™¨å¯è§†åŒºåŸŸé¡¶éƒ¨çš„间距
        tableContainer.style.height = (clientHeight - containerTopToClientTopHeight - 32) + 'px'
      }
    }
  }
</script>
<style scoped>
  .table2 {
    width: 100%;
    height: 100%;
    overflow: auto;
  }
  .table2 thead tr th:first-child,
  .table tbody tr .tdgu {
    position: sticky;
    left: 0;
    z-index: 1;
  }
  .table tbody tr .wenzi {
    transform: rotate(360deg);
    writing-mode: vertical-lr;
    letter-spacing: 2px;
  }
  .table tbody tr .kaitou {
    z-index: 1;
    background-color: white;
  }
  .table tbody tr .tdgu1 {
    position: sticky;
    left: 0;
    z-index: 2;
  }
  .table tbody tr .tdgu2 {
    position: sticky;
    left: 100px;
    z-index: 2;
  }
  .table tbody tr .tdgu3 {
    position: sticky;
    left: 262px;
    z-index: 2;
  }
  .table2 thead tr .timeth,
  .table2 thead tr .thgu {
    position: sticky;
    top: 0;
    z-index: 3;
  }
  .table2 thead .gudingth th {
    position: sticky;
    top: 32px;
    z-index: 2;
  }
  .table2 thead .equipname .name {
    z-index: 3;
  }
  .table2 thead .equipname .dong1 {
    z-index: 5;
    left:0;
  }
  .table2 thead .equipname .dong2 {
    z-index: 5;
    left:100px;
  }
  .table2 thead .equipname .dong3 {
    z-index: 5;
    left: 262px;
  }
  #EfficiencyShift {
    overflow: hidden;
  }
  .identifyingclass {
    width: 55px;
    height: 15px;
    display: inline-block
  }
  .dataContent {
    white-space: nowrap;
    border-collapse: separate;
    border-spacing: 0;
    border: 1px solid #ccc;
    width: 100%;
    text-align: center;
  }
  .dataContent .thead th {
    background-color: #fafafa;
    text-align: center;
    height: 30px;
    padding: 5px;
  }
  .dataContent .mathData td {
    padding: 10px;
  }
</style>
src/views/mdc/base/modules/SparePartsManagement/SparePartsModal.vue
@@ -59,13 +59,14 @@
          <a-col :span="12">
            <a-form-model-item prop="overallFlag" label="是否为自动线" :labelCol="{xs: { span: 24 },sm: { span: 8 }}"
                               :wrapperCol="{xs: { span: 24 },sm: { span: 16 }}">
              <j-switch v-model="model.overallFlag"></j-switch>
              <j-switch v-model="model.overallFlag" @change="handleSwitchChange"></j-switch>
            </a-form-model-item>
          </a-col>
          <a-col :span="12">
          <a-col :span="12" v-if="model.overallFlag==='N'">
            <a-form-model-item prop="equipmentId" label="生产设备" :labelCol="labelCol" :wrapperCol="wrapperCol">
              <a-select v-model="model.equipmentId" placeholder="请选择生产设备" style="width: 100%">
              <a-select v-model="model.equipmentId" placeholder="请选择生产设备" mode="multiple" :maxTagCount="1"
                        style="width: 100%" allow-clear>
                <a-select-option v-for="(item,index) in equipmentList" :key="index" :value="item.equipmentId">
                  {{item.equipmentId}}
                </a-select-option>
@@ -80,7 +81,7 @@
    <template slot="footer">
      <a-popconfirm title="确定放弃操作?" @confirm="visible=false" okText="确定" cancelText="取消">
      <a-popconfirm title="确定放弃操作?" @confirm="handleModalClose" okText="确定" cancelText="取消">
        <a-button style="margin-right: .8rem">取消</a-button>
      </a-popconfirm>
      <a-button @click="handleSubmit" type="primary" :loading="confirmLoading">提交</a-button>
@@ -185,6 +186,7 @@
      edit(record) {
        this.visible = true
        this.model = Object.assign({}, record)
        this.model.equipmentId = this.model.equipmentId.split(',')
        console.log('model', this.model)
        this.getProductionLineByApi()
        this.$nextTick(() => {
@@ -199,6 +201,8 @@
          if (valid) {
            that.confirmLoading = true
            let obj
            if (!this.model.equipmentId) this.model.equipmentId = ''
            else this.model.equipmentId = this.model.equipmentId.join(',')
            if (this.title == '新增') {
              obj = api.addSparePartApi(this.model)
            } else {
@@ -249,6 +253,11 @@
        this.getEquipmentListByProductionId()
      },
      handleSwitchChange(value) {
        console.log('value', value)
        if (value === 'Y') delete  this.model.equipmentId
      },
      getEquipmentListByProductionId() {
        api.getEquipmentListByProductionIdApi(this.model.productionId)
          .then(res => {
@@ -260,6 +269,7 @@
      handleModalClose() {
        this.visible = false
        this.equipmentList = []
        this.removeValidate()
      },
      /**
src/views/mdc/base/modules/SubControlWorkshopSignage/SignageModal.vue
@@ -1,6 +1,7 @@
<template>
  <a-modal title="当日生产计划" :width="modalWidth" :visible="modalVisible" :footer="null" @cancel="$emit('closeModal')">
    <a-table :columns="modalTableColumns" :dataSource="todayProductionPlanList" :pagination="false" :loading="loading" rowKey="equipmentId"></a-table>
    <a-table :columns="modalTableColumns" :dataSource="todayProductionPlanList" :pagination="false"
             rowKey="equipmentId"></a-table>
  </a-modal>
</template>
@@ -14,7 +15,7 @@
        default: 1448
      },
      todayProductionPlanList: {
        type: Object
        type: Array
      },
      modalVisible: {
        type: Boolean
src/views/mdc/base/modules/alarmAnalysis/alarmAnalysisMain.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,539 @@
<template>
  <div class="efficiency_list" style="width: 100%;height: 100%;">
    <div :bordered="false" style="height: 100%">
      <!-- æŸ¥è¯¢åŒºåŸŸ -->
      <div class="table-page-search-wrapper">
        <a-form layout="inline" @keyup.enter.native="searchQuery">
          <a-row :gutter="24">
            <a-col :md="7" :sm="7">
              <a-form-item label="时间">
                <a-range-picker @change="dateParamChange" :disabledDate="disabledDate" format="YYYYMMDD"
                                v-model="dates" :allowClear="false"/>
              </a-form-item>
            </a-col>
            <a-col :lg="2" :md="2" :sm="2" :xs="2">
              <a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
            </a-col>
          </a-row>
        </a-form>
      </div>
      <div id="DeviceList">
        <div class="openRateTrendDg">
          <a-table :columns="columns" :data-source="dataList" bordered :pagination="false" :scroll="{y:210}"
                   :customRow="customRow"
                   rowKey="alarmCode" @expand="handleExpandChange" :loading="outerDataLoading"
          >
            <span slot="timeCount" slot-scope="text">{{text | getFormattedTime}}</span>
            <a-table
              slot="expandedRowRender"
              slot-scope="row"
              :columns="innerColumns"
              :data-source="row.innerDataList"
              :pagination="false"
              rowKey="rowIndex"
              :loading="innerDataLoading"
            >
              <span slot="duration" slot-scope="text">{{text | getFormattedTime}}</span>
            </a-table>
          </a-table>
        </div>
        <a-spin :spinning="echartLoading">
          <div style="width: 100%;height: 100%;display: flex;">
            <div id="MdcEquipmentWarningPie" style="height: 100%;width: 35%;"></div>
            <div id="MdcEquipmentWarningLine" style="height: 100%;width: 65%;"></div>
          </div>
        </a-spin>
      </div>
    </div>
  </div>
</template>
<script>
  import moment from 'moment'
  import { putAction, getAction } from '@/api/manage'
  import $ from 'jquery'
  import '@/components/table2excel/table2excel'
  import { ajaxGetDictItems, getDictItemsFromCache, duplicateCheck } from '@/api/api'
  import {
    JeecgListMixin
  } from '@/mixins/JeecgListMixin'
  const columns = [
    { title: '报警号', dataIndex: 'alarmCode', key: 'alarmCode', align: 'center' },
    { title: '出现次数', dataIndex: 'count', key: 'count', align: 'center', sorter: (a, b) => b.count - a.count },
    {
      title: '合计持续时间',
      dataIndex: 'timeCount',
      key: 'timeCount',
      scopedSlots: { customRender: 'timeCount' },
      align: 'center',
      sorter: (a, b) => b.timeCount - a.timeCount
    },
    { title: '报警信息', dataIndex: 'alarmContent', key: 'alarmContent', align: 'center', ellipsis: true }
  ]
  const innerColumns = [
    {
      title: '',
      dataIndex: 'rowIndex',
      key: 'rowIndex',
      width: 60,
      align: 'center',
      customRender: function(t, r, index) {
        return parseInt(index) + 1
      }
    },
    { title: '设备编号', dataIndex: 'equipmentId', key: 'equipmentId', align: 'center' },
    { title: '设备名称', dataIndex: 'equipmentName', key: 'equipmentName', align: 'center' },
    { title: '报警时间', dataIndex: 'startTime', key: 'startTime', align: 'center' },
    { title: '结束时间', dataIndex: 'endTime', key: 'endTime', align: 'center' },
    {
      title: '持续时间',
      dataIndex: 'duration',
      key: 'duration',
      scopedSlots: { customRender: 'duration' },
      align: 'center'
    }
  ]
  export default {
    // mixins: [JeecgListMixin],
    name: 'alarmAnalysisMain',
    components: {},
    data() {
      return {
        dataSource: [],
        /* table加载状态 */
        outerDataLoading: false,
        innerDataLoading: false,
        echartLoading: false,
        typeTree: '',
        typeParent: 1,
        typeEquipment: 1,
        TreeIDOne: 1,
        TreeIDTwo: 2,
        deviceTypeDict: '',
        dates: [],
        identifying: [],
        queryParam: {},
        queryParams: {},
        queryParamEquip: {},
        queryParamPeople: {},
        efficiencyOptions: [
          { label: '利用率', value: 'lyl' },
          { label: '开机率', value: 'kjl' },
          { label: '开机时间', value: 'kjsj' },
          { label: '加工时间', value: 'jgsj' },
          { label: '待机时间', value: 'djsj' },
          { label: '关机时间', value: 'gjsj' }
        ],
        checkedList: ['lyl'],
        dataList: [],
        url: {
          efficiencyList: '/mdc/alarmAnalyze/alarmList',
          listByType: '/mdc/MdcUtilizationRate/getByType',
          alarmTrend: '/mdc/alarmAnalyze/alarmTrend',
          equipmentAlarmList: '/mdc/alarmAnalyze/equipmentAlarmList'
        },
        tableHeads: [],
        pieDate: [0],
        XData: [0],
        YData: [0],
        columns,
        innerColumns,
        hasRequsetAlarmCodeList: []
      }
    },
    props: { nodeTree: '', Type: '', nodePeople: '' },
    /**
     * ç”Ÿå‘½å‘¨æœŸ æŒ‚载前
     * */
    created() {
      this.dates = [moment().subtract('days', 8), moment().subtract('days', 1)]
      this.queryParam.startDate = moment(this.dates[0]).format('YYYYMMDD')
      this.queryParam.endDate = moment(this.dates[1]).format('YYYYMMDD')
      this.queryParam.typeTree = '1'
      this.loadData1()
    },
    mounted() {
      this.drawWrin()
      window.addEventListener('resize', this.handleWindowResize)
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.handleWindowResize)
    },
    watch: {
      Type(valmath) {
        this.dataList = []
        this.queryParam.typeTree = valmath
      },
      nodeTree(val) { //监听currSelected å˜åŒ–,将变化后的数值传递给 getCurrSelected äº‹ä»¶
        if (JSON.stringify(val) != '{}') {
          if (val.equipmentId != null) {
            this.queryParamEquip.parentId = ''
            this.queryParamEquip.equipmentId = val.equipmentId
          } else {
            this.queryParamEquip.parentId = val.key
            this.queryParamEquip.equipmentId = ''
          }
          /*这里你是监听到树得变化 æ˜¯ä¸æ˜¯è¯·æ±‚一次*/
          this.searchQuery()
        }
      },
      nodePeople(val) {
        if (JSON.stringify(val) != '{}') {
          if (val.equipmentId != null) {
            this.queryParamPeople.parentId = val.equipmentId
            this.queryParamPeople.equipmentId = ''
          } else {
            this.queryParamPeople.parentId = val.key
            this.queryParamPeople.equipmentId = ''
          }
          this.searchQuery()
        }
      }
    },
    filters: {
      numFilter(value) {
        if (value) {
          return parseFloat((value * 100).toFixed(2))
        } else {
          return '0'
        }
      },
      /**
       * æ ¼å¼åŒ–æ—¶é—´
       * @param seconds ç§’æ•°
       * @returns '' æ ¼å¼åŒ–后时间字符串
       */
      getFormattedTime(seconds) {
        var hours = Math.floor(seconds / 3600)
        var minutes = Math.floor((seconds % 3600) / 60)
        var secs = seconds % 60
        if (hours === 0) {
          if (minutes === 0) {
            return secs === 0 ? 0 : `${secs}秒`
          } else {
            if (secs === 0) {
              return `${minutes}分`
            }
            return `${minutes}分 ${secs}秒`
          }
        } else {
          if (minutes === 0 && secs === 0) {
            return `${hours}小时`
          } else if (minutes !== 0 && secs === 0) {
            return `${hours}小时 ${minutes}分`
          }
        }
        return `${hours}小时 ${minutes}分 ${secs}秒`
      }
    },
    methods: {
      numBerTwo(value) {
        if (value) {
          return parseFloat((value * 100).toFixed(2))
        } else {
          return '0'
        }
      },
      TableDraw(key, val) {
        let that = this
        that.echartLoading = true
        that.queryParam.alarmCode = val.alarmCode
        console.log(this.queryParam)
        getAction(that.url.alarmTrend, that.queryParam)
          .then(res => {
            if (res.success) {
              that.pieDate = res.result.equipmentCountList.map(item => {
                return {
                  name: item.key,
                  value: item.count
                }
              })
              that.XData = res.result.dateCountList.map(item => item.key)
              that.YData = res.result.dateCountList.map(item => item.count)
              // this.tableHeads = res.result.dates
              // this.dataList = res.result
              // this.draw()
              // this.checkSameData(this.dataList)
              // this.checkSameData1(this.dataList)
              // this.checkSameData2(this.dataList)
              // this.combineCell();
              // this.initDeviceType(this.dataList)
              that.drawWrin()
            }
          })
          .finally(() => {
            that.echartLoading = false
          })
        console.log(this.YData)
      },
      disabledDate(current) {
        //Can not slect days before today and today
        return current && current > moment().subtract('days', 1)
      },
      initDeviceType(deviceList) {
        let dictCode = 'mdc_equipmentType'
        let items = []
        items = getDictItemsFromCache(dictCode)
        if (deviceList && items.length > 0) {
          for (let a = 0; a < deviceList.length; a++) {
            if (items && items.length > 0) {
              for (let i = 0; i < items.length; i++) {
                if (deviceList[a].equipmentType == items[i].value) {
                  deviceList[a].equipmentType = items[i].title
                }
              }
            } else {
              ajaxGetDictItems(dictCode, null).then((res) => {
                if (res.success) {
                  let items = res.result
                  for (let i = 0; i < items.length; i++) {
                    if (deviceList[a].equipmentType == items[i].value) {
                      deviceList[a].equipmentType = items[i].title
                    }
                  }
                }
              })
            }
          }
        }
      },
      dateParamChange(v1, v2) {
        this.queryParam.startDate = v2[0]
        this.queryParam.endDate = v2[1]
      },
      searchQuery() {
        if (this.queryParam.typeTree == '1') {
          this.queryParam.parentId = this.queryParamEquip.parentId
          this.queryParam.equipmentId = this.queryParamEquip.equipmentId
        } else {
          this.queryParam.parentId = this.queryParamPeople.parentId
          this.queryParam.equipmentId = ''
        }
        this.loadData1()
      },
      loadData1() {
        this.outerDataLoading = true
        this.tableHeads = []
        this.dataList = []
        getAction(this.url.efficiencyList, this.queryParam).then(res => {
          if (res.success) {
            // this.tableHeads = res.result.dates
            this.dataList = res.result
            this.hasRequsetAlarmCodeList = []
            // this.draw()
            // this.checkSameData(this.dataList)
            // this.checkSameData1(this.dataList)
            // this.checkSameData2(this.dataList)
            // this.combineCell();
            // this.initDeviceType(this.dataList)
          }
        }).finally(() => {
          this.outerDataLoading = false
        })
      },
      drawWrin() {
        this.equipmentWarningPie = this.$echarts.init(document.getElementById('MdcEquipmentWarningPie'), 'macarons')
        let equipmentWarningPieOption = {
          title: {
            text: '各设备出现此报警的比例',
            x: 'center',
            y: 'bottom',
            textStyle: {
              color: '#4FAEDC'
            }
          },
          tooltip: {
            trigger: 'item',
            formatter: '<br/>{b} : {c} ({d}%)'
          },
          calculable: true,
          series: [{
            type: 'pie',
            radius: '60%',
            itemStyle: {
              color: function(params) {
                var colorList = ['#5AB1EF', '#2EC7C9', '#B6A2DE', '#FFB980', '#D87A80', '#8D98B3']
                return colorList[params.dataIndex]
              },
              label: {
                show: true,
                // position: 'top',
                formatter: '{b}\n{c}',
                color: function(params) {
                  var colorList = ['#5AB1EF', '#2EC7C9', '#B6A2DE', '#FFB980', '#D87A80', '#8D98B3']
                  return colorList[params.dataIndex]
                }
              }
            },
            data: this.pieDate
            // data:[{name:'jjjjjjj',value:'2'}]
          }]
        }
        this.equipmentWarningPie.setOption(equipmentWarningPieOption, true)
        this.equipmentWarningLine = this.$echarts.init(document.getElementById('MdcEquipmentWarningLine'), 'macarons')
        let equipmentWarningLineOption = {
          title: {
            text: '每天出现此报警的数量走势',
            x: 'center',
            y: 'bottom',
            textStyle: {
              color: '#4FAEDC'
            }
          },
          tooltip: {
            trigger: 'axis'
          },
          calculable: true,
          xAxis: [
            {
              type: 'category',
              show: true,
              data: this.XData
              /*axisLabel :{
                  interval:0
              }*/,
              axisLine: {
                //x轴线的颜色以及宽度
                show: true,
                lineStyle: {
                  width: 2,
                  color: '#4FAEDC'
                }
              }
            }
          ],
          yAxis: [
            {
              type: 'value',
              name: '次数',
              axisLine: {
                //x轴线的颜色以及宽度
                show: true,
                lineStyle: {
                  width: 2,
                  color: '#4FAEDC'
                }
              }
            }
          ],
          series: [
            {
              name: '报警数量',
              type: 'line',
              data: this.YData,
              markPoint: {
                data: [
                  { type: 'max', name: '最大值' },
                  { type: 'min', name: '最小值' }
                ],
                label: {
                  color: '#fff'
                }
              },
              itemStyle: {
                color: '#2EC7C9'
              }
            }
          ]
        }
        this.equipmentWarningLine.setOption(equipmentWarningLineOption, true)
      },
      /**
       * è‡ªå®šä¹‰è¡¨æ ¼è¡Œè§¦å‘
       * @param record å½“前行信息
       * @param index å½“前行下标
       * @returns {{on: {click: on.click}}} è¿”回对象
       */
      customRow(record, index) {
        return {
          on: {
            click: (event) => {
              // å¦‚果点击的不是展开图标区域则渲染图表,相反则相当于点击展开图标
              if (event.target.className !== 'ant-table-row-expand-icon-cell') {
                this.TableDraw(index, record)
              } else {
                if (event.target.children && event.target.children.length > 0) event.target.children[0].click()
              }
            }
          }
        }
      },
      /**
       * è‡ªå®šä¹‰è¡¨æ ¼è¡Œè§¦å‘
       * @param expanded å½“前行是否为展开状态
       * @param record å½“前行信息
       */
      handleExpandChange(expanded, record) {
        let _this = this
        // å½“展开时若该行未被展开过才会请求后台数据,展开过的数据会被缓存无需重复请求
        this.queryParam.alarmCode = record.alarmCode
        if (expanded && !this.hasRequsetAlarmCodeList.includes(record.alarmCode)) {
          this.innerDataLoading = true
          getAction(this.url.equipmentAlarmList, this.queryParam).then(res => {
            if (res.success) {
              _this.dataList.forEach(item => {
                if (item.alarmCode === record.alarmCode) {
                  item.innerDataList = res.result
                }
              })
              _this.hasRequsetAlarmCodeList.push(record.alarmCode)
            }
          })
            .finally(() => {
              _this.innerDataLoading = false
            })
        }
      },
      /**
       * å½“浏览器可视窗口尺寸发生改变时触发
       */
      handleWindowResize() {
        if (this.equipmentWarningPie) this.equipmentWarningPie.resize()
        if (this.equipmentWarningLine) this.equipmentWarningLine.resize()
      }
    }
  }
</script>
<style scoped>
  .efficiency_list #DeviceList {
    height: 90% !important;
  }
  /deep/ .ant-table-body .ant-table-row td {
    padding-top: 10px;
    padding-bottom: 10px;
  }
  /deep/ .ant-table-scroll > .ant-table-body > table > .ant-table-tbody > .ant-table-row td {
    cursor: pointer;
  }
  /deep/ .ant-spin-nested-loading {
    height: 55%;
  }
  /deep/ .ant-spin-container {
    height: 100%;
  }
  /deep/ .ant-table.ant-table-bordered {
    height: 265px;
  }
  /deep/ .ant-table-scroll > .ant-table-placeholder {
    height: 210px;
  }
</style>