yizai 3 years ago
parent
commit
734f12d34f

+ 13 - 0
src/api/grider-erection.js

@@ -17,3 +17,16 @@ export function getWarnings(params) {
     params
   })
 }
+export function getDeviceList(tkySectionId) {
+  return appservice({
+    url: `${baseUrl}/${tkySectionId}`,
+    method: 'get'
+  })
+}
+
+export function lookByDevice(deviceCode) {
+  return appservice({
+    url: `${baseUrl}/code/${deviceCode}`,
+    method: 'get'
+  })
+}

+ 10 - 0
src/api/tky-api.js

@@ -0,0 +1,10 @@
+import { appservice } from '@/utils/apiService'
+
+const baseUrl = '/api/tky-yjl'
+
+export function getAuthSectionInfo() {
+  return appservice({
+    url: `${baseUrl}/auth-section`,
+    method: 'get'
+  })
+}

+ 1 - 1
src/router/index.js

@@ -70,7 +70,7 @@ export const constantRoutes = [
   },
   {
     path: '/device-manage',
-    component: () => import('@/views/bridge/device-manage/index'),
+    component: () => import('@/views/bridge/device-manage/device-work'),
     name: 'diviceMng',
     hidden: true
   },

+ 237 - 0
src/utils/webRtc.js

@@ -0,0 +1,237 @@
+const JSWebrtc = {
+  Player: null,
+  CreateVideoElements: function() {
+    var elements = document.querySelectorAll('.jswebrtc')
+    for (var i = 0; i < elements.length; i++) {
+      new JSWebrtc.VideoElement(elements[i])
+    }
+  },
+  FillQuery: function(query_string, obj) {
+    obj.user_query = {}
+    if (query_string.length === 0) return
+    if (query_string.indexOf('?') >= 0) query_string = query_string.split('?')[1]
+    var queries = query_string.split('&')
+    for (var i = 0; i < queries.length; i++) {
+      var query = queries[i].split('=')
+      obj[query[0]] = query[1]
+      obj.user_query[query[0]] = query[1]
+    }
+    if (obj.domain) obj.vhost = obj.domain
+  },
+  ParseUrl: function(rtmp_url) {
+    var a = document.createElement('a')
+    a.href = rtmp_url
+      .replace('rtmp://', 'http://')
+      .replace('webrtc://', 'http://')
+      .replace('rtc://', 'http://')
+    var vhost = a.hostname
+    var app = a.pathname.substr(1, a.pathname.lastIndexOf('/') - 1)
+    var stream = a.pathname.substr(a.pathname.lastIndexOf('/') + 1)
+    app = app.replace('...vhost...', '?vhost=')
+    if (app.indexOf('?') >= 0) {
+      var params = app.substr(app.indexOf('?'))
+      app = app.substr(0, app.indexOf('?'))
+      if (params.indexOf('vhost=') > 0) {
+        vhost = params.substr(params.indexOf('vhost=') + 'vhost='.length)
+        if (vhost.indexOf('&') > 0) {
+          vhost = vhost.substr(0, vhost.indexOf('&'))
+        }
+      }
+    }
+    if (a.hostname === vhost) {
+      var re = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
+      if (re.test(a.hostname)) vhost = '__defaultVhost__'
+    }
+    var schema = 'rtmp'
+    if (rtmp_url.indexOf('://') > 0) schema = rtmp_url.substr(0, rtmp_url.indexOf('://'))
+    var port = a.port
+    if (!port) {
+      if (schema === 'http') {
+        port = 80
+      } else if (schema === 'https') {
+        port = 443
+      } else if (schema === 'rtmp') {
+        port = 1935
+      } else if (schema === 'webrtc' || schema === 'rtc') {
+        port = 1985
+      }
+    }
+    var ret = {
+      url: rtmp_url,
+      schema: schema,
+      server: a.hostname,
+      port: port,
+      vhost: vhost,
+      app: app,
+      stream: stream
+    }
+    JSWebrtc.FillQuery(a.search, ret)
+    return ret
+  },
+  HttpPost: function(url, data) {
+    return new Promise(function(resolve, reject) {
+      var xhr = new XMLHttpRequest()
+      xhr.onreadystatechange = function() {
+        if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {
+          var respone = JSON.parse(xhr.responseText)
+          xhr.onreadystatechange = new Function()
+          xhr = null
+          resolve(respone)
+        }
+      }
+      xhr.open('POST', url, true)
+      xhr.timeout = 5e3
+      xhr.responseType = 'text'
+      xhr.setRequestHeader('Content-Type', 'application/json')
+      xhr.send(data)
+    })
+  },
+  httpGet: function(url) {
+    return new Promise(function(resolve, reject) {
+      var xhr = new XMLHttpRequest()
+      xhr.onreadystatechange = function() {
+        if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) {
+          var respone = JSON.parse(xhr.responseText)
+          xhr.onreadystatechange = new Function()
+          xhr = null
+          resolve(respone)
+        }
+      }
+      xhr.open('GET', url, true)
+      xhr.timeout = 1000
+      xhr.setRequestHeader('Content-Type', 'application/json')
+      xhr.send(null)
+    })
+  }
+}
+JSWebrtc.Player = (function() {
+  'use strict'
+  class Player {
+    constructor(url, options) {
+      this.options = options || {}
+      if (!url.match(/^webrtc?:\/\//)) {
+        // eslint-disable-next-line no-throw-literal
+        throw 'JSWebrtc just work with webrtc'
+      }
+      if (!this.options.video) {
+        // eslint-disable-next-line no-throw-literal
+        throw 'VideoElement is null'
+      }
+      this.urlParams = JSWebrtc.ParseUrl(url)
+      this.pc = null
+      this.autoplay = !!options.autoplay || false
+      this.paused = true
+      if (this.autoplay) this.options.video.muted = true
+      this.startLoading()
+    }
+    startLoading() {
+      var _self = this
+      if (_self.pc) {
+        _self.pc.close()
+      }
+      _self.pc = new RTCPeerConnection(null)
+      _self.pc.ontrack = function(event) {
+        _self.options.video['srcObject'] = event.streams[0]
+      }
+      _self.pc.addTransceiver('audio', {
+        direction: 'recvonly'
+      })
+      _self.pc.addTransceiver('video', {
+        direction: 'recvonly'
+      })
+      _self.pc
+        .createOffer()
+        .then(async function(offer) {
+          await _self.pc.setLocalDescription(offer)
+          return offer
+        })
+        .then(function(offer) {
+          return new Promise(function(resolve, reject) {
+            var port = _self.urlParams.port || 1985
+            var api = _self.urlParams.user_query.play || '/rtc/v1/play/'
+            if (api.lastIndexOf('/') !== api.length - 1) {
+              api += '/'
+            }
+            var url = 'http://' + _self.urlParams.server + ':' + port + api
+            for (var key in _self.urlParams.user_query) {
+              if (key !== 'api' && key !== 'play') {
+                url += '&' + key + '=' + _self.urlParams.user_query[key]
+              }
+            }
+            var data = {
+              api: url,
+              streamurl: _self.urlParams.url,
+              clientip: null,
+              sdp: offer.sdp
+            }
+            JSWebrtc.HttpPost(url, JSON.stringify(data)).then(
+              function(res) {
+                resolve(res.sdp)
+              },
+              function(rej) {
+                reject(rej)
+              }
+            )
+          })
+        })
+        .then(function(answer) {
+          return _self.pc.setRemoteDescription(
+            new RTCSessionDescription({
+              type: 'answer',
+              sdp: answer
+            })
+          )
+        })
+        .catch(function(reason) {
+          throw reason
+        })
+      if (this.autoplay) {
+        this.play()
+      }
+    }
+    play(ev) {
+      if (this.animationId) {
+        return
+      }
+      this.animationId = requestAnimationFrame(this.update.bind(this))
+      this.paused = false
+    }
+    pause(ev) {
+      if (this.paused) {
+        return
+      }
+      cancelAnimationFrame(this.animationId)
+      this.animationId = null
+      this.isPlaying = false
+      this.paused = true
+      this.options.video.pause()
+      if (this.options.onPause) {
+        this.options.onPause(this)
+      }
+    }
+    stop(ev) {
+      this.pause()
+    }
+    destroy() {
+      this.pause()
+      this.pc && this.pc.close() && this.pc.destroy()
+      this.audioOut && this.audioOut.destroy()
+    }
+    update() {
+      this.animationId = requestAnimationFrame(this.update.bind(this))
+      if (this.options.video.readyState < 4) {
+        return
+      }
+      if (!this.isPlaying) {
+        this.isPlaying = true
+        this.options.video.play()
+        if (this.options.onPlay) {
+          this.options.onPlay(this)
+        }
+      }
+    }
+  }
+  return Player
+})()
+export default JSWebrtc
+

+ 131 - 0
src/views/bridge/device-manage/components/THAInfo.vue

@@ -0,0 +1,131 @@
+<template>
+  <table class="info_table">
+    <tr>
+      <td class="cell_title">风速</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.windSpeed }}</span>米/时</td>
+      <td class="cell_title">风力</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.windPower }}</span> 级</td>
+    </tr>
+    <tr>
+      <td class="cell_title">位置</td>
+      <td class="cell_data"><span class="fontSpan">0</span>米</td>
+      <td class="cell_title" title="单次工作时间">单次工作时间</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.onceWorkTime }}</span>小时</td>
+    </tr>
+    <tr>
+      <td class="cell_title">循环次数</td>
+      <td class="cell_data"><span class="fontSpan">1</span>次</td>
+      <td class="cell_title">累计工作时间</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.onceWorkTime }}</span>小时</td>
+    </tr>
+    <tr>
+      <td class="cell_title" colspan="2">起吊重量</td>
+      <td class="cell_data" colspan="2">{{ grider.slingWeight }}</td>
+    </tr>
+    <!-- <tr>
+      <td class="cell_title" title="主梁水平度纵向">主梁水平度纵向</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.verticalLeveLness }}</span>°</td>
+      <td class="cell_title" title="主梁水平度横向">主梁水平度横向</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.horizontaLeveLness }}</span>°</td>
+    </tr> -->
+    <!-- <tr>
+      <td class="cell_title" title="前支腿位置">前支腿位置</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.frontPosition }}</span>米</td>
+      <td class="cell_title" title="前支腿垂直度纵向">前支腿垂直度纵向</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.frontVertical }}</span>°</td>
+    </tr>
+    <tr>
+      <td class="cell_title">速度</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.frontSpeed }}</span>米/s</td>
+      <td class="cell_title" title="中支腿状态">中支腿状态</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.middleStatus }}</span></td>
+    </tr>
+    <tr>
+      <td class="cell_title">系统状态</td>
+      <td class="cell_data"><span class="fontSpan">正常</span></td>
+      <td class="cell_title" title="起吊总重量">起吊总重量</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.slingWeight }}</span>t</td>
+    </tr> -->
+    <!-- <tr>
+      <td class="cell_title" colspan="2">前小车</td>
+      <td class="cell_title" colspan="2">后小车</td>
+    </tr>
+    <tr>
+      <td class="cell_title">行走速度</td>
+      <td class="cell_title">行走位置</td>
+      <td class="cell_title">行走速度</td>
+      <td class="cell_title">行走位置</td>
+    </tr>
+    <tr>
+      <td class="cell_data"><span class="fontSpan">{{ grider.beforeCarSpeed }}</span>m/s</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.beforeCarPosition }}</span></td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.afterCarSpeed }}</span>m/s</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.afterCarPosition }}</span></td>
+    </tr> -->
+  </table>
+</template>
+
+<script>
+export default {
+  name: 'THAInfoMng',
+  data() {
+    return {
+      grider: {}
+    }
+  },
+  methods: {
+    show(data) {
+      if (data) {
+        this.grider = data
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.info_table {
+    position: relative;
+    overflow: hidden;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    width: 100%;
+    max-width: 100%;
+    //background-color: #FFFFFF;
+    font-size: 14px;
+    //color: white;
+    table-layout:fixed;
+    .cell_title{
+      //background-color: #f7f7f7 !important;
+      text-align: center;
+      width: 25%;
+    }
+    .cell_data{
+      text-align: center;
+      width: 25%;
+    }
+    td {
+    border: 1px solid #dfe6ec;
+    padding: 9px 0;
+    min-width: 0;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    text-overflow: ellipsis;
+    vertical-align: middle;
+    position: relative;
+    text-align: left;
+    // white-space:nowrap;
+    // text-overflow:
+    // ellipsis;
+    // overflow: hidden;
+  }
+  .fontSpan{
+    font-weight: 900;
+    color: green;
+  }
+}
+
+</style>

+ 91 - 0
src/views/bridge/device-manage/components/beam-support.vue

@@ -0,0 +1,91 @@
+<template>
+  <table class="info_table">
+    <tr>
+      <td class="cell_title" title="整机纵向水平度">整机纵向水平度</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.verticalLeveLness }}</span>°</td>
+      <td class="cell_title" title="整机纵向水平度">整机纵向垂直度</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.horizontaLeveLness }}</span>°</td>
+    </tr>
+    <tr>
+      <td class="cell_title">前支腿位置</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.frontPosition }}</span>米</td>
+      <td class="cell_title">前支腿速度</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.frontSpeed }}</span>m/小时</td>
+    </tr>
+    <tr>
+      <td class="cell_title" title="整机纵向水平度">前支腿纵向垂直度</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.frontVertical }}</span>°</td>
+      <td class="cell_title">中支腿状态</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.middleStatus }}</span></td>
+    </tr>
+    <tr>
+      <td class="cell_title" colspan="2">后支腿速度</td>
+      <td class="cell_data" colspan="2"><span class="fontSpan">{{ grider.laterSpeed }}</span>m/小时</td>
+    </tr>
+  </table>
+</template>
+
+<script>
+export default {
+  name: 'BeamSupportMng',
+  data() {
+    return {
+      grider: {}
+    }
+  },
+  methods: {
+    show(data) {
+      if (data) {
+        this.grider = data
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.info_table {
+    position: relative;
+    overflow: hidden;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    width: 100%;
+    max-width: 100%;
+    //background-color: #FFFFFF;
+    font-size: 14px;
+    //color: white;
+    table-layout:fixed;
+    .cell_title{
+      //background-color: #f7f7f7 !important;
+      text-align: center;
+      width: 25%;
+    }
+    .cell_data{
+      text-align: center;
+      width: 25%;
+    }
+    td {
+    border: 1px solid #dfe6ec;
+    padding: 9px 0;
+    min-width: 0;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    text-overflow: ellipsis;
+    vertical-align: middle;
+    position: relative;
+    text-align: left;
+    white-space:nowrap;
+    text-overflow:
+    ellipsis;
+    overflow: hidden;
+  }
+  .fontSpan{
+    font-weight: 900;
+    color: green;
+  }
+}
+
+</style>

+ 80 - 0
src/views/bridge/device-manage/components/limit-state.vue

@@ -0,0 +1,80 @@
+<template>
+  <table class="info_table">
+    <tr>
+      <td class="cell_title"><span style="margin-right:15px">行走工作模式</span><i v-show="grider.walk" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.walk" class="el-icon-s-opportunity" style="color:red" /></td>
+      <td class="cell_data"><span style="margin-right:15px">卷扬机1起升工作状态</span><i v-show="grider.firstWinch" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.firstWinch" class="el-icon-s-opportunity" style="color:red" /></td>
+      <td class="cell_title"><span style="margin-right:15px">卷扬机2起升工作状态</span><i v-show="grider.secondWinch" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.secondWinch" class="el-icon-s-opportunity" style="color:red" /></td>
+    </tr>
+    <tr>
+      <td class="cell_data"><span style="margin-right:15px">前车控制权</span><i v-show="grider.frontControl" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.frontControl" class="el-icon-s-opportunity" style="color:red" /></td>
+      <td class="cell_title"><span style="margin-right:15px">中车控制权</span><i v-show="grider.centerControl" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.centerControl" class="el-icon-s-opportunity" style="color:red" /></td>
+      <td class="cell_data"><span style="margin-right:15px">后车控制权</span><i v-show="grider.rearControl" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.rearControl" class="el-icon-s-opportunity" style="color:red" /></td>
+    </tr>
+    <tr>
+      <td class="cell_title"><span style="margin-right:15px">主支腿泵站启动</span><i v-show="grider.ledStart" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.ledStart" class="el-icon-s-opportunity" style="color:red" /></td>
+      <td class="cell_data"><span style="margin-right:15px">蠕动C</span><i v-show="grider.wriggle" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.wriggle" class="el-icon-s-opportunity" style="color:red" /></td>
+      <td class="cell_title"><span style="margin-right:15px">喂梁</span><i v-show="grider.feed" class="el-icon-s-opportunity" style="color:green" /><i v-show="!grider.feed" class="el-icon-s-opportunity" style="color:red" /></td>
+    </tr>
+  </table>
+</template>
+
+<script>
+export default {
+  name: 'LimitStateMng',
+  data() {
+    return {
+      grider: {}
+    }
+  },
+  methods: {
+    show(data) {
+      if (data) {
+        this.grider = data
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.info_table {
+    position: relative;
+    overflow: hidden;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    width: 100%;
+    max-width: 100%;
+    //background-color: #FFFFFF;
+    font-size: 14px;
+    //color: white;
+    table-layout:fixed;
+    .cell_title{
+      //background-color: #f7f7f7 !important;
+      text-align: center;
+      width: 25%;
+    }
+    .cell_data{
+      text-align: center;
+      width: 25%;
+    }
+    td {
+    border: 1px solid #dfe6ec;
+    padding: 9px 0;
+    min-width: 0;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    text-overflow: ellipsis;
+    vertical-align: middle;
+    position: relative;
+    text-align: left;
+  }
+  .fontSpan{
+    font-weight: 900;
+    color: green;
+  }
+}
+
+</style>

+ 100 - 0
src/views/bridge/device-manage/components/tackling-sys.vue

@@ -0,0 +1,100 @@
+<template>
+  <table class="info_table">
+    <tr>
+      <td class="cell_title" colspan="2">前小车</td>
+      <td class="cell_title" colspan="2">后小车</td>
+    </tr>
+    <tr>
+      <td class="cell_title">行走速度</td>
+      <td class="cell_title">行走位置</td>
+      <td class="cell_title">行走速度</td>
+      <td class="cell_title">行走位置</td>
+    </tr>
+    <tr>
+      <td class="cell_data"><span class="fontSpan">{{ grider.beforeCarSpeed }}</span>m/s</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.beforeCarPosition }}</span>m</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.afterCarSpeed }}</span>m/s</td>
+      <td class="cell_data"><span class="fontSpan">{{ grider.afterCarPosition }}</span>m</td>
+    </tr>
+    <tr>
+      <td class="cell_title" colspan="4">吊钩信息</td>
+    </tr>
+    <tr>
+      <td class="cell_title">吊钩序号</td>
+      <td class="cell_title">吊钩重量</td>
+      <td class="cell_title">吊钩高度</td>
+      <td class="cell_title">吊钩横移量</td>
+    </tr>
+    <tr v-for="item in grider.slingSys" :key="item.id">
+      <td class="cell_title"><span class="fontSpan">{{ item.no }}</span>#</td>
+      <td class="cell_title"><span class="fontSpan">{{ item.weight }}</span></td>
+      <td class="cell_title"><span class="fontSpan">{{ item.height }}</span></td>
+      <td class="cell_title"><span class="fontSpan">{{ item.quantity }}</span></td>
+    </tr>
+  </table>
+</template>
+
+<script>
+export default {
+  name: 'TacklingSysMng',
+  data() {
+    return {
+      grider: {}
+    }
+  },
+  methods: {
+    show(data) {
+      if (data) {
+        this.grider = data
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.info_table {
+    position: relative;
+    overflow: hidden;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    width: 100%;
+    max-width: 100%;
+    //background-color: #FFFFFF;
+    font-size: 14px;
+    //color: white;
+    table-layout:fixed;
+    .cell_title{
+      //background-color: #f7f7f7 !important;
+      text-align: center;
+      width: 25%;
+    }
+    .cell_data{
+      text-align: center;
+      width: 25%;
+    }
+    td {
+    border: 1px solid #dfe6ec;
+    padding: 9px 0;
+    min-width: 0;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+    text-overflow: ellipsis;
+    vertical-align: middle;
+    position: relative;
+    text-align: left;
+    // white-space:nowrap;
+    // text-overflow:
+    // ellipsis;
+    // overflow: hidden;
+  }
+  .fontSpan{
+    font-weight: 900;
+    color: green;
+  }
+}
+
+</style>

+ 356 - 0
src/views/bridge/device-manage/device-work.vue

@@ -0,0 +1,356 @@
+<template>
+  <div>
+    <div class="navbar" style="padding:0px;display:block">
+      <div class="nav-title" style="margin-left:10px">{{ title }}</div>
+    </div>
+    <el-container class="content-height-fit">
+      <inner-sidebar>
+        <DeviceTree @node-click="handleTreeClick" />
+      </inner-sidebar>
+      <el-container class="resize-fit-container">
+        <div>
+          <div class="content-container">
+            <tkyIndex ref="tkyIndex" @node-click="nodeClick" />
+          </div>
+        </div>
+
+      </el-container>
+    </el-container>
+  </div>
+</template>
+
+<script>
+import * as interfaceApi from '@/api/device-manage.js'
+import * as griderApi from '@/api/grider-erection'
+import * as areaApi from '@/api/backbone/section-area'
+import * as dictEntryApi from '@/api/dict-entry.js'
+const defaultSettings = require('@/settings.js')
+// import ProjectSectionTree from '@/views/projects/components/ProjectSectionTree'
+import DeviceTree from '@/views/projects/components/DeviceTree.vue'
+
+import waves from '@/directive/waves' // waves directive
+import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+import InnerSidebar from '@/components/InnerSidebar'
+import tkyIndex from '@/views/bridge/device-manage/tky-manage.vue'
+
+export default {
+  name: 'DeviceMng',
+  directives: { waves },
+  // eslint-disable-next-line vue/no-unused-components
+  components: { Pagination, DeviceTree, InnerSidebar, tkyIndex },
+  data() {
+    return {
+      list: {},
+      title: defaultSettings.title,
+      total: 0,
+      listLoading: false,
+      listQuery: {
+        deviceCode: 'UX21YYSI',
+        beamCode: ''
+      },
+      dialogFormVisible: false,
+      dialogStatus: '',
+      node: {},
+      griders: [],
+      formLoading: false
+    }
+  },
+  watch: {
+  },
+  mounted() {
+    try {
+      this.$store.dispatch('user/clientCredentials')
+      this.loading = false
+    } catch (ex) {
+      console.log(ex)
+    }
+  },
+  methods: {
+    getJQJTypes() {
+      dictEntryApi.getJQJTypes().then(res => {
+        res.forEach(t => {
+          t.code = t.code - 0
+        })
+        this.jqjTypes = res
+      })
+    },
+    getYLCTypes() {
+      dictEntryApi.getYLCTypes().then(res => {
+        res.forEach(t => {
+          t.code = t.code - 0
+        })
+        this.ylcTypes = res
+      })
+    },
+    // 树形事件
+    handleTreeClick(data) {
+      this.node = data
+      if (data.isDevice) {
+        this.listQuery.deviceCode = data.deviceNo
+        griderApi.lookByDevice(data.deviceNo)
+          .then(res => {
+            this.griders = res
+            if (this.griders.length > 0) {
+              this.listQuery.beamCode = res[0].name
+              this.getList()
+            }
+          })
+      }
+    },
+    getArea() {
+      this.areas = []
+      areaApi.lookupBySection(this.listQuery.sectionId).then(res => {
+        res.forEach(t => {
+          this.areas.push({
+            id: t.id,
+            name: t.name
+          })
+        })
+      })
+    },
+    getAreaName(areaId) {
+      const area = this.areas.find(t => t.id === areaId)
+      if (area) {
+        return area.name
+      } else {
+        return ''
+      }
+    },
+    // 清空下拉
+    delValue(parameter) {
+      this.listQuery[parameter] = undefined
+    },
+    // 获取数据列表
+    getList() {
+      this.listLoading = true
+      griderApi
+        .getList(this.listQuery)
+        .then(res => {
+          this.list = res
+          var data = {
+            beams: this.griders,
+            data: this.list,
+            deviceCode: this.listQuery.deviceCode
+          }
+          this.$refs.tkyIndex.show(data)
+        })
+        .finally(() => {
+          this.listLoading = false
+        })
+    },
+    nodeClick(beamCode) {
+      this.listQuery.beamCode = beamCode
+      this.getList()
+    },
+    // 查询
+    handleFilter() {
+      this.listQuery.skipCount = 0
+      this.getList()
+    },
+    // 创建清空
+    resetStation() {
+      this.entity = {
+        id: undefined,
+        projectId: this.listQuery.projectId,
+        sectionId: this.listQuery.sectionId,
+        sectionAreaId: this.listQuery.sectionAreaId,
+        deviceNo: '',
+        deviceName: '',
+        category: undefined,
+        categoryName: '',
+        spec: '',
+        madeFactory: '',
+        driverName: '',
+        telephone: '',
+        deviceType: undefined
+      }
+    },
+    // 创建点击事件
+    handleCreate() {
+      if (!this.listQuery.sectionId) {
+        this.verificationTips('请选择标段')
+        return
+      }
+      this.resetStation()
+      this.dialogStatus = 'create'
+      this.dialogFormVisible = true
+      this.formLoading = false
+      this.$nextTick(() => {
+        this.$refs['dataForm'].clearValidate()
+      })
+    },
+    // 提示
+    verificationTips(message) {
+      this.$notify({
+        title: '提示',
+        message: message,
+        type: 'error',
+        duration: 2000
+      })
+    },
+    // 创建
+    createData() {
+      this.$refs['dataForm'].validate(valid => {
+        if (valid) {
+          if (this.entity.deviceType === 1) {
+            this.entity.categoryName = this.jqjTypes.find(t => t.code === this.entity.category).name
+          }
+          if (this.entity.deviceType === 2) {
+            this.entity.categoryName = this.ylcTypes.find(t => t.code === this.entity.category).name
+          }
+          this.formLoading = true
+          interfaceApi
+            .create(this.entity)
+            .then(() => {
+              this.getList()
+              this.dialogFormVisible = false
+              this.$notify({
+                title: '成功',
+                message: '创建成功',
+                type: 'success',
+                duration: 2000
+              })
+            })
+            .finally(() => {
+              this.formLoading = false
+            })
+        }
+      })
+    },
+    // 修改点击事件
+    handleUpdate(row) {
+      const { href } = this.$router.resolve({ path: '/gride-work-info', query: { workSiteId: 27018 }})
+      window.open(href, '_blank')
+      // this.entity = Object.assign({}, row) // copy obj
+      // this.dialogStatus = 'update'
+      // this.dialogFormVisible = true
+      // this.formLoading = false
+      // this.$nextTick(() => {
+      //   this.$refs['dataForm'].clearValidate()
+      // })
+    },
+    // 修改
+    updateData() {
+      this.$refs['dataForm'].validate(valid => {
+        if (valid) {
+          if (this.entity.deviceType === 1) {
+            this.entity.categoryName = this.jqjTypes.find(t => t.code === this.entity.category).name
+          }
+          if (this.entity.deviceType === 2) {
+            this.entity.categoryName = this.ylcTypes.find(t => t.code === this.entity.category).name
+          }
+          const submitData = Object.assign({}, this.entity)
+          this.formLoading = true
+          interfaceApi
+            .update(submitData.id, submitData)
+            .then(() => {
+              this.getList()
+              this.dialogFormVisible = false
+              this.$notify({
+                title: '成功',
+                message: '修改成功',
+                type: 'success',
+                duration: 2000
+              })
+            })
+            .finally(() => {
+              this.formLoading = false
+            })
+        }
+      })
+    },
+    // 删除
+    handleDelete(row) {
+      const { href } = this.$router.resolve({ path: '/beam-carrier-work-info', query: { workSiteId: 27018 }})
+      window.open(href, '_blank')
+      // this.$confirm('此操作将删除该项, 是否继续?', '提示', {
+      //   confirmButtonText: '确定',
+      //   cancelButtonText: '取消',
+      //   type: 'warning'
+      // }).then(() => {
+      //   this.listLoading = true
+      //   interfaceApi
+      //     .remove(row.id)
+      //     .then(() => {
+      //       this.getList()
+      //       this.$notify({
+      //         title: '成功',
+      //         message: '删除成功',
+      //         type: 'success',
+      //         duration: 2000
+      //       })
+      //     })
+      //     .finally(() => {
+      //       this.listLoading = false
+      //     })
+      // })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '~@/styles/variables.scss';
+.navbar {
+  height: $headerHeight;
+  overflow: hidden;
+  position: relative;
+  // background: #1b437e;
+  background: url('../../../assets/top_bg.png') no-repeat;
+  background-size: 100% 100%;
+
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+  margin: 0px;
+  border-radius: 0px;
+  border: none;
+
+  .hamburger-container {
+    line-height: $headerHeight;
+    height: 100%;
+    float: left;
+    cursor: pointer;
+    transition: background 0.3s;
+    -webkit-tap-highlight-color: transparent;
+
+    &:hover {
+      background: rgba(0, 0, 0, 0.1);
+    }
+  }
+
+  .nav-logo {
+    float: left;
+    height: 100%;
+    background-size: 100% 100% !important;
+    width: 60px;
+    margin-left: 5px;
+  }
+  // .nav-logo {
+  //   float: left;
+  //   height: 100%;
+  //   width: 310px;
+  // }
+
+  .nav-title {
+    height: 100%;
+    line-height: $headerHeight;
+    font-size: 24px;
+    font-weight: bold;
+    color: #fff;
+    // margin-left: 20px;
+    margin-left: 5px;
+    display: inline-block;
+  }
+}
+::v-deep .resize-fit-container {
+    width: 100%;
+    padding: 8px;
+    overflow-y: auto;
+    //background: white;
+}
+.number >>> input{
+  text-align: left !important;
+}
+.el-selects >>> .el-input--suffix{
+  width: 150px !important;
+}
+</style>

+ 66 - 125
src/views/bridge/device-manage/index.vue

@@ -1,5 +1,8 @@
 <template>
   <el-container class="content-height-fit">
+    <!-- <inner-sidebar>
+      <ProjectSectionTree @node-click="handleTreeClick" />
+    </inner-sidebar> -->
     <el-container class="resize-fit-container">
       <div class="app-container">
         <div class="filter-container blue-top-border">
@@ -41,6 +44,7 @@
             @click="handleFilter"
           >查询</el-button>
           <el-button
+            v-if="$requirePermission('BSET.DVMG.C')"
             v-waves
             class="filter-item"
             type="primary"
@@ -60,12 +64,23 @@
             style="width: 100%"
             height="calc(100vh - 276px)"
           >
+            <div slot="empty">
+              <span v-if="listQuery.sectionId">没有数据</span>
+              <span v-else>请选择标段</span>
+            </div>
             <el-table-column label="序号" type="index" width="50" align="center" />
+            <!-- <el-table-column label="工区名称" align="center">
+              <template slot-scope="{ row }">
+                {{ getAreaName(row.sectionAreaId) }}
+              </template>
+            </el-table-column> -->
             <el-table-column label="设备编号" prop="deviceNo" :show-overflow-tooltip="true" align="center" />
             <el-table-column label="设备名称" prop="deviceName" :show-overflow-tooltip="true" align="center" />
             <el-table-column label="类型名称" prop="categoryName" :show-overflow-tooltip="true" align="center" />
             <el-table-column label="规格型号" prop="spec" :show-overflow-tooltip="true" align="center" />
             <el-table-column label="制造厂家" prop="madeFactory" :show-overflow-tooltip="true" align="center" />
+            <el-table-column label="司机姓名" prop="driverName" width="80" align="center" />
+            <el-table-column label="联系电话" prop="telephone" width="120" align="center" />
             <el-table-column label="累计过孔数" prop="viaCount" width="100" align="center" />
             <el-table-column label="累计工作时间" prop="allTime" width="110" align="center" />
             <el-table-column label="是否在线" width="80" align="center">
@@ -83,11 +98,13 @@
             >
               <template slot-scope="{ row }">
                 <el-button
+                  v-if="$requirePermission('BSET.DVMG.U')"
                   type="primary"
                   size="mini"
                   @click="handleUpdate(row)"
                 >修改</el-button>
                 <el-button
+                  v-if="$requirePermission('BSET.DVMG.D')"
                   size="mini"
                   type="danger"
                   @click="handleDelete(row)"
@@ -117,29 +134,17 @@
           >
             <el-row :gutter="16">
               <el-col :span="12">
-                <el-form-item label="工区" prop="sectionAreaId">
-                  <el-select v-model="entity.sectionAreaId" class="w100p">
-                    <el-option
-                      v-for="item in areas"
-                      :key="item.id"
-                      :value="item.id"
-                      :label="item.name"
-                    />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="12">
                 <el-form-item label="设备编号" prop="deviceNo">
                   <el-input v-model="entity.deviceNo" :show-overflow-tooltip="true" />
                 </el-form-item>
               </el-col>
-            </el-row>
-            <el-row :gutter="16">
               <el-col :span="12">
                 <el-form-item label="设备名称" prop="deviceName">
                   <el-input v-model="entity.deviceName" :show-overflow-tooltip="true" />
                 </el-form-item>
               </el-col>
+            </el-row>
+            <el-row :gutter="16">
               <el-col :span="12">
                 <el-form-item label="设备类型" prop="deviceType">
                   <el-select v-model="entity.deviceType" class="w100p">
@@ -152,55 +157,38 @@
                   </el-select>
                 </el-form-item>
               </el-col>
-            </el-row>
-
-            <el-row :gutter="16">
               <el-col :span="12">
                 <el-form-item label="规格型号">
                   <el-input v-model="entity.spec" />
                 </el-form-item>
               </el-col>
+            </el-row>
+            <el-row :gutter="16">
               <el-col :span="12">
                 <el-form-item label="制造厂家" align="left">
                   <el-input v-model="entity.madeFactory" align="left" controls-position="right" style="width: 100%;" />
                 </el-form-item>
               </el-col>
-            </el-row>
-            <el-row :gutter="16">
               <el-col :span="12">
                 <el-form-item label="司机姓名" align="left">
                   <el-input v-model="entity.driverName" align="left" controls-position="right" style="width: 100%;" />
                 </el-form-item>
               </el-col>
+            </el-row>
+            <el-row :gutter="16">
               <el-col :span="12">
                 <el-form-item label="联系电话" align="left">
                   <el-input v-model="entity.telephone" align="left" controls-position="right" style="width: 100%;" />
                 </el-form-item>
               </el-col>
-            </el-row>
-            <el-row v-if="entity.deviceType === 1" :gutter="16">
-              <el-col :span="12">
-                <el-form-item label="架桥机类型" prop="category">
-                  <el-select v-model="entity.category" class="w100p">
-                    <el-option
-                      v-for="item in jqjTypes"
-                      :key="item.code"
-                      :value="item.code"
-                      :label="item.name"
-                    />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-            </el-row>
-            <el-row v-if="entity.deviceType === 2" :gutter="16">
               <el-col :span="12">
-                <el-form-item label="运梁车类型" prop="category">
-                  <el-select v-model="entity.category" class="w100p">
+                <el-form-item label="铁科院信息" prop="deviceType">
+                  <el-select v-model="entity.tkySectionId" class="w100p">
                     <el-option
-                      v-for="item in ylcTypes"
-                      :key="item.code"
-                      :value="item.code"
-                      :label="item.name"
+                      v-for="item in authSections"
+                      :key="item.key"
+                      :value="item.sectionId"
+                      :label="item.sectionName"
                     />
                   </el-select>
                 </el-form-item>
@@ -226,9 +214,14 @@
 
 <script>
 import * as interfaceApi from '@/api/device-manage.js'
+import * as tkyApi from '@/api/tky-api'
 import * as areaApi from '@/api/backbone/section-area'
+// import ProjectSectionTree from '@/views/projects/components/ProjectSectionTree'
+
 import waves from '@/directive/waves' // waves directive
 import Pagination from '@/components/Pagination' // secondary package based on el-pagination
+import _ from 'lodash'
+// import InnerSidebar from '@/components/InnerSidebar'
 
 export default {
   name: 'DeviceMng',
@@ -238,13 +231,17 @@ export default {
     return {
       list: [],
       deviceTypes: [
-        { key: 1, name: '架桥机' },
-        { key: 2, name: '运梁车' }
+        { key: 0, name: '架桥机' },
+        { key: 1, name: '运梁车' },
+        { key: 2, name: '运架一体车' }
       ],
       entity: {},
       total: 0,
       listLoading: false,
       listQuery: {
+        projectId: undefined,
+        sectionId: undefined,
+        sectionAreaId: undefined,
         deviceNo: '',
         deviceName: '',
         deviceType: undefined,
@@ -258,7 +255,8 @@ export default {
         update: '修改',
         create: '创建'
       },
-      sections: [],
+      areas: [],
+      authSections: [],
       rules: {
         deviceNo: [
           { required: true, message: '请输入设备编号', trigger: 'blur' },
@@ -270,21 +268,10 @@ export default {
         ],
         deviceType: [
           { required: true, message: '请选择设备类型', trigger: 'blur' }
-        ],
-        sectionAreaId: [
-          { required: true, message: '请选择工区', trigger: 'blur' }
-        ],
-        category: [
-          { required: true, message: '请选择架桥机类型', trigger: 'blur' }
         ]
-      },
-      areas: [],
-      jqjTypes: [],
-      ylcTypes: []
+      }
     }
   },
-  watch: {
-  },
   mounted() {
     try {
       this.$store.dispatch('user/clientCredentials')
@@ -292,27 +279,13 @@ export default {
     } catch (ex) {
       console.log(ex)
     }
+    tkyApi.getAuthSectionInfo()
+      .then(res => {
+        this.authSections = res.data
+      })
     this.getList()
-    // this.getJQJTypes()
-    // this.getYLCTypes()
   },
   methods: {
-    // getJQJTypes() {
-    //   dictEntryApi.getJQJTypes().then(res => {
-    //     res.forEach(t => {
-    //       t.code = t.code - 0
-    //     })
-    //     this.jqjTypes = res
-    //   })
-    // },
-    // getYLCTypes() {
-    //   dictEntryApi.getYLCTypes().then(res => {
-    //     res.forEach(t => {
-    //       t.code = t.code - 0
-    //     })
-    //     this.ylcTypes = res
-    //   })
-    // },
     // 树形事件
     handleTreeClick(data, node) {
       this.listQuery.projectId = undefined
@@ -339,14 +312,14 @@ export default {
         })
       })
     },
-    getAreaName(areaId) {
-      const area = this.areas.find(t => t.id === areaId)
-      if (area) {
-        return area.name
-      } else {
-        return ''
-      }
-    },
+    // getAreaName(areaId) {
+    //   const area = this.areas.find(t => t.id === areaId)
+    //   if (area) {
+    //     return area.name
+    //   } else {
+    //     return ''
+    //   }
+    // },
     // 清空下拉
     delValue(parameter) {
       this.listQuery[parameter] = undefined
@@ -369,31 +342,9 @@ export default {
       this.listQuery.skipCount = 0
       this.getList()
     },
-    // 创建清空
-    resetStation() {
-      this.entity = {
-        id: undefined,
-        projectId: this.listQuery.projectId,
-        sectionId: this.listQuery.sectionId,
-        sectionAreaId: this.listQuery.sectionAreaId,
-        deviceNo: '',
-        deviceName: '',
-        category: undefined,
-        categoryName: '',
-        spec: '',
-        madeFactory: '',
-        driverName: '',
-        telephone: '',
-        deviceType: undefined
-      }
-    },
     // 创建点击事件
     handleCreate() {
-      if (!this.listQuery.sectionId) {
-        this.verificationTips('请选择标段')
-        return
-      }
-      this.resetStation()
+      this.entity = {}
       this.dialogStatus = 'create'
       this.dialogFormVisible = true
       this.formLoading = false
@@ -401,25 +352,20 @@ export default {
         this.$refs['dataForm'].clearValidate()
       })
     },
-    // 提示
-    verificationTips(message) {
-      this.$notify({
-        title: '提示',
-        message: message,
-        type: 'error',
-        duration: 2000
-      })
+    loadAuthSection(sectionId) {
+      var authSeciton = _.find(this.authSections, { 'sectionId': sectionId })
+      if (authSeciton !== undefined) {
+        this.entity.tkyProjectId = authSeciton.projectId
+        this.entity.tkyProjectName = authSeciton.projectName
+        this.entity.tkySectionName = authSeciton.sectionName
+        this.entity.authCode = authSeciton.authCode
+      }
     },
     // 创建
     createData() {
       this.$refs['dataForm'].validate(valid => {
         if (valid) {
-          if (this.entity.deviceType === 1) {
-            this.entity.categoryName = this.jqjTypes.find(t => t.code === this.entity.category).name
-          }
-          if (this.entity.deviceType === 2) {
-            this.entity.categoryName = this.ylcTypes.find(t => t.code === this.entity.category).name
-          }
+          this.loadAuthSection(this.entity.tkySectionId)
           this.formLoading = true
           interfaceApi
             .create(this.entity)
@@ -453,12 +399,7 @@ export default {
     updateData() {
       this.$refs['dataForm'].validate(valid => {
         if (valid) {
-          if (this.entity.deviceType === 1) {
-            this.entity.categoryName = this.jqjTypes.find(t => t.code === this.entity.category).name
-          }
-          if (this.entity.deviceType === 2) {
-            this.entity.categoryName = this.ylcTypes.find(t => t.code === this.entity.category).name
-          }
+          this.loadAuthSection(this.entity.tkySectionId)
           const submitData = Object.assign({}, this.entity)
           this.formLoading = true
           interfaceApi

+ 462 - 0
src/views/bridge/device-manage/tky-manage.vue

@@ -0,0 +1,462 @@
+<template>
+  <div>
+    <div class="app-container">
+      <el-row :gutter="16">
+        <el-col :sm="18" :md="18">
+          <!-- <el-select v-model="query.deviceManageId" size="mini" placeholder="请选择设备" @change="deviceChange">
+                <el-option
+                  v-for="item in griderErections"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select> -->
+          <el-input v-model="query.deviceCode" disabled style="width:30%">
+            <template slot="prepend">设备编号</template>
+          </el-input>
+          <!-- <el-tag type="success" style="margin-left: 6px;" size="mini">在线</el-tag> -->
+          <!-- <el-input v-model="query.beamCode" disabled style="float:right;margin-right: 6px;width:30%">
+            <template slot="prepend">梁片编号</template>
+          </el-input> -->
+          <el-select v-model="query.beamCode" size="mini" style="float:right;margin-right: 6px;" placeholder="请选择梁片编码" @change="change">
+            <el-option
+              v-for="item in beams"
+              :key="item.name"
+              :label="item.name"
+              :value="item.value"
+            />
+          </el-select></el-col>
+        <el-col :sm="6" :md="6">
+          <span style="font-size: 18px;">视频监控</span>
+          <a style="font-size: 18px;float:right" @click="clickAllVideos">更多视频</a>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :lg="18" :sm="18" :xs="18" :xl="18" :md="18" style="padding-right: 8px">
+          <div class="boxall">
+            <el-row>
+              <el-col :lg="4" :sm="4" :xs="4" :xl="4" :md="4">
+                <div class="boxCenter" style="cursor:pointer " @click="warningClick">
+                  <div class="box-warning"><i class="el-icon-warning" /><span style="margin-left:5px">报警信息</span></div>
+                  <div class="left-div"><span style="font-size: 25px;color: #0086ff;">{{ data.warningCount }}</span>次</div>
+                  <div class="left-foot">已解除报警{{ data.doneWarningCount }}次</div>
+                </div>
+                <div class="boxCenter">
+                  <div class="box-warning"><i class="el-icon-timer" /><span style="margin-left:5px">工作时长</span></div>
+                  <div class="left-div"><span style="font-size: 25px;color: #0086ff;">{{ data.timeCount }}</span>小时</div>
+                  <div class="left-foot">累计工作{{ data.allTimeCount }}小时</div>
+                </div>
+                <div class="boxCenter">
+                  <div class="box-warning"><i class="el-icon-refresh" /><span style="margin-left:5px">循环次数</span></div>
+                  <div class="left-div"><span style="font-size: 25px;color: #0086ff;">{{ data.allCycleCount }}</span>次</div>
+                  <div class="left-foot">累计循环{{ data.allCycleCount }}次</div>
+                </div>
+              </el-col>
+              <el-col :lg="20" :sm="20" :xs="20" :xl="20" :md="20">
+                <div class="titleup">
+                  <span>最近更新时间:</span><span style="color: #0086ff;font-weight: bold;">{{ noGuide.lastTime | datetimeseconds }}</span>
+                  <div style="float:right">
+                    <el-button v-if="!playback.isAnimRunning && !playback.anim" type="primary" size="mini" @click="hitoryClick">开始回放</el-button>
+                    <el-button v-if="playback.anim" type="primary" size="mini" @click="stopPlayback">停止回放</el-button>
+                    <el-button v-if="!playback.isAnimRunning && playback.anim" type="primary" size="mini" @click="goon">继续</el-button>
+                    <el-button v-if="playback.isAnimRunning && playback.anim" type="danger" size="mini" @click="pause">暂停</el-button>
+                    <el-button v-if="playback.anim" type="primary" size="mini" @click="speedUp">加速</el-button>
+                    <el-button v-if="playback.anim" type="primary" size="mini" @click="speedDown">减速</el-button>
+                    <el-button type="primary" size="mini" @click="zoomIn">放大</el-button>
+                    <el-button type="primary" size="mini" @click="zoomOut">缩小</el-button>
+                  </div>
+                </div>
+                <div ref="canvasdiv" style="background: #0AA4BC">
+                  <New2d v-if="modelVisible" ref="towD" @playbacking="playbacking" @returndata="returndata" />
+                  <Kiosk2dModel v-if="!modelVisible" />
+                </div>
+              </el-col>
+            </el-row>
+            <div class="boxfoot" />
+          </div>
+        </el-col>
+        <el-col :xs="6" :sm="6" :md="6">
+          <div class="boxall">
+            <el-row :gutter="8" style="margin-top:6px">
+              <el-col v-for="(item,index) in videos" :key="item.name" :sm="12" :md="12">
+                <video
+                  :id="'video' + index"
+                  controls
+                  autoplay
+                  muted
+                  style="height:210px;width:100%;background:#000"
+                />
+              </el-col>
+            </el-row>
+            <div class="boxfoot" />
+          </div>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :lg="24" :sm="24" :xs="24" :xl="24" :md="24">
+          <div class="boxall">
+            <el-row :gutter="16">
+              <el-col :lg="16" :sm="16" :xs="16" :xl="16" :md="16">
+                <el-row>
+                  <el-col :lg="24" :sm="24" :xs="24" :xl="24" :md="24">
+                    <box icon="el-icon-help" title="限位状态" class="boxColor">
+                      <div>
+                        <LimitState ref="limitState" />
+                      </div>
+                    </box>
+                  </el-col>
+                </el-row>
+                <el-row :gutter="16">
+                  <el-col :lg="12" :sm="12" :xs="12" :xl="12" :md="12">
+                    <box icon="el-icon-help" title="基本信息" class="boxInfo">
+                      <div>
+                        <THAInfo ref="thaInfo" />
+                      </div>
+                    </box>
+                  </el-col>
+                  <el-col :lg="12" :sm="12" :xs="12" :xl="12" :md="12">
+                    <box icon="el-icon-help" title="主梁和支座" class="boxInfo">
+                      <div>
+                        <BeamSupport ref="beamSupport" />
+                      </div>
+                    </box>
+                  </el-col>
+                </el-row>
+              </el-col>
+              <el-col :lg="8" :sm="8" :xs="8" :xl="8" :md="8">
+                <box icon="el-icon-help" title="起吊系统" class="boxColor">
+                  <div>
+                    <TacklingSys ref="tacklingSys" />
+                  </div>
+                </box>
+              </el-col>
+            </el-row>
+            <div class="boxfoot" />
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+    <el-dialog title="预警详情" :close-on-click-modal="false" :visible.sync="proDucitonVisible" class="roll-dialog" top="20vh" width="50%">
+      <el-table v-loading="listLoading" row-key="id" :data="warnings" border stripe fit highlight-current-row height="400px">
+        <el-table-column label="序号" type="index" align="center" />
+        <el-table-column label="报警时间" prop="warningDate" align="center">
+          <template slot-scope="{row}">
+            {{ row.warningDate |datetimeseconds }}
+          </template>
+        </el-table-column>
+        <el-table-column label="报警记录" prop="warningRecord" align="center" />
+        <el-table-column label="解除报警时间" align="center">
+          <template slot-scope="{row}">
+            {{ row.relieveDate |datetimeseconds }}
+          </template>
+        </el-table-column>
+        <el-table-column label="是否解除" align="center">
+          <template slot-scope="{row}">
+            <el-tag size="mini" :type="row.isAlarm?'success':'danger'">{{ row.isAlarm?"已解除":"未解除" }}</el-tag>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="proDucitonVisible = false">取消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import Box from '@/components/Box/Box'
+import waves from '@/directive/waves' // waves directive
+import THAInfo from '@/views/bridge/device-manage/components//THAInfo'
+import New2d from '@/views/grider-erections/gride-work-info/components//new-2d.vue'
+import BeamSupport from '@/views/bridge/device-manage/components/beam-support.vue'
+import TacklingSys from '@/views/bridge/device-manage/components/tackling-sys.vue'
+import LimitState from '@/views/bridge/device-manage/components//limit-state.vue'
+import Kiosk2dModel from '@/views/grider-erections/gride-work-info/components/kiosk-2d-model.vue'
+import * as griderErectionApi from '@/api/grider-erection'
+import JSWebrtc from '@/utils/webRtc'
+import _ from 'lodash'
+
+export default {
+  directives: { waves },
+  components: {
+    Box, //, InnerSidebar, ProjectSectionDeviceTree
+    New2d,
+    THAInfo,
+    BeamSupport,
+    TacklingSys,
+    LimitState,
+    Kiosk2dModel
+  },
+  data() {
+    return {
+      query: {
+        deviceCode: '',
+        beamCode: ''
+      },
+      beams: [],
+      listLoading: false,
+      proDucitonVisible: false,
+      modelVisible: true,
+      data: {},
+      noGuide: {},
+      videos: [
+        {
+          name: '沪苏湖2标1#摄像头',
+          webRtc: 'webrtc://120.78.220.33:1985/live/0002@HSH_Crane_V1'
+        },
+        {
+          name: '沪苏湖2标2#摄像头',
+          webRtc: 'webrtc://120.78.220.33:1985/live/0002@CJH_Crane_V2'
+        },
+        {
+          name: '沪苏湖2标3#摄像头',
+          webRtc: 'webrtc://120.78.220.33:1985/live/0002@CJH_Crane_V3'
+        },
+        {
+          name: '沪苏湖2标4#摄像头',
+          webRtc: 'webrtc://120.78.220.33:1985/live/0003@HSH_Crane_V5'
+        }
+      ],
+      warnings: [],
+      playback: { anim: null, isAnimRunning: false }
+    }
+  },
+  created() {
+  },
+  async mounted() {
+    this.loading = true
+
+    // try {
+    //   await this.$store.dispatch('user/login', { username: 'test-grider', password: crypto.decryptAES('B0B10217A69BFEE4F3043E5769106053') })
+    //   this.loading = false
+    // } catch (ex) {
+    //   console.log(ex)
+    // }
+    // try {
+    //   await this.$store.dispatch('user/login', { username: 'yjlview419', password: cryptosss.decryptAES('B00BB09C02A845CE99A9EE6DAF052DC7') })
+    //   this.loading = false
+    // } catch (ex) {
+    //   console.log(ex)
+    // }
+    this.$nextTick(() => {
+      _.forEach(this.videos, (i, index) => {
+        var id = 'video' + index
+        var video = document.getElementById(id)
+        new JSWebrtc.Player(i.webRtc, {
+          video: video,
+          autoplay: true,
+          onPlay: obj => {}
+        })
+      })
+    })
+  },
+  methods: {
+    show(data) {
+      this.query.deviceCode = data.deviceCode
+      this.beams = data.beams
+      if (this.beams.length > 0 && !this.query.beamCode) {
+        this.query.beamCode = this.beams[0].value
+      }
+      this.data = data.data
+      if (this.data.splitTypeNoGuideBeams.length > 0) {
+        // this.noGuide = this.data.splitTypeNoGuideBeams[this.data.splitTypeNoGuideBeams.length - 1]
+        this.returndata(this.data.splitTypeNoGuideBeams[this.data.splitTypeNoGuideBeams.length - 1])
+      }
+    },
+    hitoryClick() {
+      this.$refs.towD.playBack(this.data.splitTypeNoGuideBeams)
+    },
+    playbacking(data) {
+      this.playback = data
+    },
+    stopPlayback() {
+      this.$refs.towD.stopPlayback()
+    },
+    pause() {
+      this.$refs.towD.suspend()
+    },
+    goon() {
+      this.$refs.towD.goon()
+    },
+    zoomIn() {
+      this.$refs.towD.zoomIn()
+    },
+    zoomOut() {
+      this.$refs.towD.zoomOut()
+    },
+    speedUp() {
+      this.$refs.towD.speedUp()
+    },
+    speedDown() {
+      this.$refs.towD.speedDown()
+    },
+    returndata(data) {
+      this.$refs.limitState.show(data)
+      this.$refs.thaInfo.show(data)
+      this.$refs.beamSupport.show(data)
+      this.$refs.tacklingSys.show(data)
+    },
+    warningClick() {
+      this.proDucitonVisible = true
+      this.listLoading = true
+      griderErectionApi.getWarnings(this.query)
+        .then(res => {
+          this.warnings = res
+          this.listLoading = false
+        })
+    },
+    deviceChange(id) {
+      var grider = _.find(this.griderErections, ['value', id])
+      if (grider.type === 1) {
+        this.modelVisible = true
+      } else {
+        this.modelVisible = false
+      }
+    },
+    change() {
+      this.$emit('node-click', this.query.beamCode)
+    },
+    clickAllVideos() {
+      // var href = 'http://14.18.41.63:5000/portal/camera/dashboard'
+      // window.open(href, '_blank')
+    }
+  }
+}
+</script>
+<style  lang="scss" scoped>
+
+.content-height-fit {
+ height: calc(100vh - 0px) !important;
+}
+// ::v-deep .el-table__body-wrapper {
+//   &::-webkit-scrollbar { // 整个滚动条
+//     width: 0; // 纵向滚动条的宽度
+//     background: rgba(213,215,220,0.3);
+//     border: none;
+//   }
+//   &::-webkit-scrollbar-track { // 滚动条轨道
+//     border: none;
+//   }
+// }
+::v-deep .el-table th.gutter{
+  display: none;
+  width:0
+}
+::v-deep .el-table colgroup col[name='gutter']{
+  display: none;
+  width: 0;
+}
+
+::v-deep .el-table__body{
+  width: 100% !important;
+}
+.boxall {
+  margin-top: 8px;
+    border: 2px solid #1b5677;
+    padding: 0rem .3rem .3rem;
+    position: relative;
+    //margin-bottom: 1rem;
+    height: 445px;
+}
+.boxCenter{
+    border: 2px solid #0A3054;
+    padding: 0rem .3rem .3rem;
+    position: relative;
+    width: 80%;
+    margin: auto;
+    //margin-bottom: 1rem;
+    height: 120px;
+    margin-top: 20px;
+}
+.box-warning{
+  //color: #fff;
+  margin-top: 10px;
+  margin-left: 5px;
+}
+.left-div{
+  //border-bottom: 1px solid;
+    margin: 5px 25px;
+    //color: #fff;
+}
+.left-foot{
+  //border-bottom: 1px solid;
+    margin-top: 0px;
+    margin-left: 25px;
+    //color: #fff;
+}
+
+.boxall:before,
+.boxall:after {
+    position: absolute;
+    width: 1rem;
+    height: 1rem;
+    content: "";
+    //border-top: 3px solid #02a6b5;
+    top: 0;
+}
+
+.boxall:before,
+.boxfoot:before {
+    //border-left: 3px solid #02a6b5;
+    left: 0;
+}
+
+.boxall:after,
+.boxfoot:after {
+    //border-right: 3px solid #02a6b5;
+    right: 0;
+}
+
+.boxfoot {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    left: 0;
+}
+
+.boxfoot:before,
+.boxfoot:after {
+    position: absolute;
+    width: 1rem;
+    height: 1rem;
+    content: "";
+    //border-bottom: 3px solid #02a6b5;
+    bottom: 0;
+}
+.titleup{
+      padding: 6px 0px;
+    //color: white;
+}
+.boxColor{
+      //background: #162B56;
+      margin-bottom:0px;
+    margin-top: 20px;
+}
+.boxInfo {
+    //background: #162B56;
+    margin-bottom: 0px;
+    margin-top: 35px;
+}
+::v-deep .card-header {
+    background-color: transparent;
+    border-bottom: 1px solid rgba(0,0,0,.125);
+    padding: 0.25rem 1.25rem;
+    position: relative;
+    border-top-left-radius: .25rem;
+    border-top-right-radius: .25rem;
+}
+::v-deep .card-title {
+    float: left;
+    font-size: 1.1rem;
+    font-weight: bold;
+    //color: white;
+    margin: 0;
+}
+::v-deep .card-body {
+    -ms-flex: 1 1 auto;
+    -webkit-box-flex: 1;
+    flex: 1 1 auto;
+    min-height: 1px;
+    padding: 0.25rem;
+}
+</style>

+ 99 - 0
src/views/projects/components/DeviceTree.vue

@@ -0,0 +1,99 @@
+<template>
+  <div id="project-section-tree">
+    <el-input v-model="treeFilterText" placeholder="输入关键字进行过滤" clearable />
+    <!-- <el-scrollbar wrap-class="scrollbar-wrapper" :style="{ height: height }"> -->
+    <el-tree
+      ref="tree"
+      style="margin-top:5px;"
+      class="filter-tree"
+      :props="treeProps"
+      :filter-node-method="filterTree"
+      :load="loadTreeNode"
+      :highlight-current="true"
+      lazy
+      @node-click="nodeClick"
+    >
+      <span slot-scope="{ node, data }" class="custom-tree-node">
+        <el-tag v-if="node.level === 1 && data.isDevice && data.deviceType===0" size="mini" type="danger">架桥机</el-tag>
+        <el-tag v-if="node.level === 1 && data.isDevice && data.deviceType===1" size="mini" type="danger">运梁车</el-tag>
+        <span>{{ data.name }}</span>
+        <!-- <el-tooltip :content="data.name" placement="right" effect="light" transition>
+        </el-tooltip> -->
+      </span>
+    </el-tree>
+    <!-- </el-scrollbar> -->
+  </div>
+</template>
+
+<script>
+import * as manageApi from '@/api/grider-erection'
+export default {
+  props: {
+    height: {
+      type: String,
+      default: 'calc(100vh - 220px)'
+    },
+    deviceType: {
+      type: String,
+      default: undefined
+    }
+  },
+  data() {
+    return {
+      treeFilterText: '',
+      treeData: [],
+      treeProps: {
+        children: 'children',
+        label: 'name',
+        isLeaf: 'isSite'
+      }
+    }
+  },
+  watch: {
+    treeFilterText(val) {
+      this.$refs.tree.filter(val)
+    }
+  },
+  mounted() {
+    try {
+      this.$store.dispatch('user/clientCredentials')
+      this.loading = false
+    } catch (ex) {
+      console.log(ex)
+    }
+  },
+  methods: {
+    filterTree(value, data) {
+      if (!value) return true
+      return data.name.indexOf(value) !== -1
+    },
+    loadTreeNode(node, resolve) {
+      if (node.level === 0) {
+        manageApi.getDeviceList('10648')
+          .then(res => {
+            res.forEach(t => {
+              t.isDevice = true
+              t.code = t.deviceNo
+              t.name = t.tkySectionName + t.deviceName
+            })
+            console.log(res)
+            return resolve(res)
+          })
+        // projectApi.lookupAll().then(projects => {
+        //   projects.forEach(t => {
+        //     t.isProject = true
+        //     t.name = t.shortName || t.name
+        //   })
+        //   this.$emit('project-loaded', projects)
+        //   return resolve(projects)
+        // })
+      } else {
+        return resolve([])
+      }
+    },
+    nodeClick(data, node) {
+      this.$emit('node-click', data, node)
+    }
+  }
+}
+</script>