Ver Fonte

refactor: device information cache

Casper Dai há 3 anos atrás
pai
commit
a12521a5f5

+ 4 - 0
src/components/Pagination/index.vue

@@ -82,6 +82,10 @@ export default {
   padding-top: $spacing;
   text-align: center;
 
+  &:empty {
+    display: none;
+  }
+
   ::v-deep .el-pagination.is-background .el-pager li:not(.disabled).active {
     background-color: $blue;
   }

+ 8 - 3
src/views/device/detail/components/DeviceRuntime.vue

@@ -1,8 +1,13 @@
 <template>
   <div class="c-info-grid">
-    <running class="c-info-grid__item" />
-    <screen-shot class="c-info-grid__item" />
-    <download class="c-info-grid__item" />
+    <running
+      class="c-info-grid__item"
+      v-bind="$attrs"
+    />
+    <template v-if="$attrs.online">
+      <screen-shot class="c-info-grid__item" />
+      <download class="c-info-grid__item" />
+    </template>
   </div>
 </template>
 

+ 8 - 1
src/views/device/detail/components/DeviceStatus.vue

@@ -12,7 +12,10 @@
       </div>
     </div>
     <div class="l-flex__fill u-overflow-y--auto">
-      <component :is="activeComponent" />
+      <component
+        :is="activeComponent"
+        v-bind="$attrs"
+      />
     </div>
   </div>
 </template>
@@ -83,6 +86,10 @@ export default {
   font-size: 14px;
   line-height: 1;
 
+  .medium {
+    font-size: 18px;
+  }
+
   .large {
     font-size: 32px;
   }

+ 6 - 4
src/views/device/detail/components/Download.vue

@@ -10,13 +10,13 @@
       />
     </div>
     <div class="l-flex__fill u-text-center">
-      <div class="has-bottom-padding">当前下载文件</div>
+      <div class="has-bottom-padding">当前下载文件</div>
       <div class="u-color--black">
         <span class="large">{{ downloadCount }}</span> 个
       </div>
     </div>
     <div
-      v-if="downloadCount"
+      v-if="count !== downloadCount"
       class="l-flex__none u-color--error dark u-bold u-text-center"
     >
       正在下载文件...
@@ -48,7 +48,8 @@ export default {
     return {
       showProgress: false,
       count: 0,
-      downloadCount: 0
+      downloadCount: 0,
+      loading: false
     }
   },
   created () {
@@ -60,7 +61,8 @@ export default {
   methods: {
     onUpdate (files) {
       this.count = files.length
-      this.downloadCount = files.filter(({ complete }) => complete).length
+      this.downloadCount = files.filter(({ complete, success }) => complete && success).length
+      this.loading = files.some(({ complete }) => !complete)
     },
     showDownload () {
       this.showProgress = true

+ 47 - 19
src/views/device/detail/components/Running.vue

@@ -3,21 +3,23 @@
     <div class="l-flex__none l-flex--row">
       <i class="l-flex__none c-runtime__icon running" />
       <span class="l-flex__fill c-runtime__title u-ellipsis">状态</span>
-      <i
-        v-if="asking"
-        class="l-flex__none el-icon-loading u-color--black"
-      />
-      <i
-        v-else
-        class="l-flex__none o-invoke u-pointer"
-        @click="invoke"
-      />
+      <template v-if="online">
+        <i
+          v-if="asking"
+          class="l-flex__none el-icon-loading u-color--black"
+        />
+        <i
+          v-else
+          class="l-flex__none o-invoke u-pointer"
+          @click="invoke"
+        />
+      </template>
     </div>
     <div
       v-if="!asking"
-      class="l-flex__fill l-flex--row center has-bottom-padding u-color--black u-bold"
+      class="l-flex__fill l-flex--row center medium has-bottom-padding u-color--black u-bold"
     >
-      {{ status }}
+      {{ online ? status : '离线' }}
     </div>
   </div>
 </template>
@@ -31,6 +33,12 @@ import {
 
 export default {
   name: 'DeviceRunning',
+  props: {
+    online: {
+      type: [Boolean, String],
+      default: false
+    }
+  },
   data () {
     return {
       asking: false,
@@ -38,20 +46,40 @@ export default {
     }
   },
   created () {
-    addListener('running', this.onUpdate)
+    addListener('status', this.onUpdate)
+    if (this.online && !this.asking) {
+      this.ask()
+    }
   },
   beforeDestroy () {
-    removeListener('running', this.onUpdate)
+    removeListener('status', this.onUpdate)
   },
   methods: {
-    onUpdate (status) {
-      this.asking = false
-      this.status = status
+    onUpdate (status, { loading }) {
+      this.asking = loading
+      switch (status) {
+        case 1:
+          this.status = '未播放节目,处于默认状态'
+          break
+        case 2:
+          this.status = '正在播放节目'
+          break
+        case 3:
+          this.status = '解析节目异常,请重新发布'
+          break
+        case -1:
+          this.status = '解析异常,请重试'
+          break
+        default:
+          this.status = '未知'
+          break
+      }
+    },
+    ask () {
+      return send('status/ask')
     },
     invoke () {
-      send('/status/ask').then(() => {
-        this.asking = true
-      }, () => {
+      this.ask().catch(() => {
         this.$message({
           type: 'warning',
           message: '正在连接,请稍后再试'

+ 7 - 6
src/views/device/detail/components/ScreenShot.vue

@@ -42,16 +42,17 @@ export default {
     removeListener('screenshot', this.onUpdate)
   },
   methods: {
-    onUpdate (screenshot) {
-      this.asking = false
-      this.styles = screenshot ? {
+    onUpdate (screenshot, { loading }) {
+      this.asking = loading
+      this.styles = !loading && screenshot ? {
         backgroundImage: `url("${screenshot}"`
       } : null
     },
+    ask () {
+      return send('screenshot/ask')
+    },
     invoke () {
-      send('/screenshot/ask').then(() => {
-        this.asking = true
-      }, () => {
+      this.ask().catch(() => {
         this.$message({
           type: 'warning',
           message: '正在连接,请稍后再试'

+ 1 - 1
src/views/device/detail/components/Sensor.vue

@@ -121,7 +121,7 @@ export default {
     }
   },
   created () {
-    addListener(this.type, this.onUpdate)
+    addListener(this.type, this.onUpdate, true)
   },
   beforeDestroy () {
     removeListener(this.type, this.onUpdate)

+ 165 - 120
src/views/device/detail/monitor.js

@@ -11,19 +11,13 @@ import {
 
 let productId = null
 let deviceId = null
-let cbs = null
 
-let running = '未知'
-let screenshotPic = null
-let download = null
-let sensor = null
+const types = new Map()
 
 export function start (device) {
   if (productId) {
     stop()
   }
-  cbs = {}
-  download = { files: [] }
   productId = device.productId
   deviceId = device.id
   subscribe([
@@ -33,8 +27,12 @@ export function start (device) {
     `${productId}/${deviceId}/screenshot/reply`,
     `${productId}/${deviceId}/resource/progress`
   ], onMessage)
+  createImmediateType('online', { parser: onlineParser })
+  createLoadType('status', { defaults: '未知', parser: statusParser })
+  createLoadType('screenshot', { parser: screenshotParser })
+  createLoopType('download', { parser: downloadParser })
   if (__SENSOR__) {
-    updateSensors(sensor = { running: true })
+    updateSensors(createLoopType('sensor', { parser: sensorParser }))
   }
 }
 
@@ -49,158 +47,201 @@ export function stop () {
     ], onMessage)
     productId = null
     deviceId = null
-    running = '未知'
-    screenshotPic = null
-    download = null
-    if (__SENSOR__) {
-      sensor.running = false
-      sensor = null
-    }
+    types.forEach(inst => {
+      if (inst.type === 1) {
+        inst.running = false
+      }
+    })
+    types.clear()
   }
 }
 
-export function addListener (type, cb) {
-  if (type && cbs) {
-    let cbSet = cbs[type]
-    if (!cbSet) {
-      cbs[type] = (cbSet = new Set())
-    }
-    cbSet.add(cb)
-    invoke(type, cb)
+export function addListener (type, cb, inject = false) {
+  let inst = types.get(type)
+  if (!inst && inject) {
+    inst = createLoopType(type)
+  }
+  if (inst) {
+    inst.cbs.add(cb)
+    invoke(inst, cb)
   }
 }
 
 export function removeListener (type, cb) {
-  if (type && cbs && cbs[type]) {
-    cbs[type].delete(cb)
+  const inst = types.get(type)
+  if (inst) {
+    inst.cbs.delete(cb)
   }
 }
 
 export function send (topic, message) {
-  return publish(`${productId}/${deviceId}${topic}`, JSON.stringify(message || { timestamp: Date.now() }))
-}
-
-function invoke (type, cb) {
-  switch (type) {
-    case 'running':
-      cb(running)
-      break
-    case 'screenshot':
-      cb(screenshotPic)
-      break
-    case 'download':
-      cb(download.files)
-      break
-    case 'temperature':
-    case 'smoke':
-    case 'flooding':
-    case 'light':
-      sensor[type] && cb(sensor[type])
-      break
-    default:
-      break
-  }
-}
-
-function emit (type, val) {
-  if (cbs && cbs[type]) {
-    if (val == null) {
-      cbs[type].forEach(cb => invoke(type, cb))
-    } else {
-      cbs[type].forEach(cb => cb(val))
+  return publish(`${productId}/${deviceId}/${topic}`, JSON.stringify(message || { timestamp: Date.now() })).then(() => {
+    const inst = types.get(getTypeBySend(topic))
+    if (inst && inst.type === 2) {
+      inst.loading = true
+      inst.cbs.forEach(cb => {
+        cb(inst.value, inst)
+      })
     }
-  }
+  })
 }
 
 function onMessage (topic, message) {
   const result = topic.match(/^(\d+)\/(\d+)\/(.+)$/)
   if (result) {
     if (productId === result[1] && deviceId === result[2]) {
-      onUpdate(result[3], message)
+      onUpdate(types.get(getType(result[3])), message, result[3])
     }
   }
 }
 
-function onUpdate (invoke, message) {
-  switch (invoke) {
+function getTypeBySend (topic) {
+  switch (topic) {
+    case 'status/ask':
+      return 'status'
+    case 'screenshot/ask':
+      return 'screenshot'
+    default:
+      return null
+  }
+}
+
+function getType (topic) {
+  switch (topic) {
     case 'online':
-      emit('online', true)
-      break
     case 'offline':
-      emit('online', false)
-      break
+      return 'online'
     case 'status/reply':
-      onUpdateStatus(message)
-      break
+      return 'status'
     case 'screenshot/reply':
-      onUpdateScreenShot(message)
-      break
+      return 'screenshot'
     case 'resource/progress':
-      onUpdateFile(message)
+      return 'download'
+    default:
+      return null
+  }
+}
+
+function onUpdate (inst, message, topic) {
+  if (inst) {
+    const value = inst.parser ? inst.parser(inst, message, topic) : inst.value
+    if (inst.type === 2) {
+      inst.loading = false
+    }
+    inst.cbs.forEach(cb => {
+      cb(value, inst)
+    })
+  }
+}
+
+function invoke (inst, cb) {
+  switch (inst.type) {
+    case 1:
+    case 2:
+      cb(inst.value, inst)
       break
     default:
       break
   }
 }
 
-function onUpdateStatus (message) {
+function createType (type, inst) {
+  types.set(type, inst)
+  return inst
+}
+
+function createImmediateType (type, options = {}) {
+  return createType(type, {
+    type: 0,
+    parser: options.parser,
+    cbs: new Set()
+  })
+}
+
+function createLoopType (type, options = {}) {
+  return createType(type, {
+    type: 1,
+    parser: options.parser,
+    value: [],
+    running: true,
+    cbs: new Set()
+  })
+}
+
+function createLoadType (type, options = {}) {
+  return createType(type, {
+    type: 2,
+    parser: options.parser,
+    value: options.defaults,
+    loading: false,
+    cbs: new Set()
+  })
+}
+
+function onlineParser (inst, message, topic) {
+  if (topic === 'online') {
+    return true
+  }
+  onUpdate(types.get('status'), '{}')
+  onUpdate(types.get('screenshot'))
+  onUpdate(types.get('download'), 'clear')
+  return false
+}
+
+function statusParser (inst, message) {
+  let status
   try {
-    message = JSON.parse(message)
-    switch (message.status) {
-      case 1:
-        running = '未播放节目,处于默认状态'
-        break
-      case 2:
-        running = '正在播放节目'
-        break
-      case 3:
-        running = '解析节目异常,请重新发布'
-        break
-      default:
-        running = '未知'
-        break
-    }
-  } catch {
-    running = '未知'
+    status = JSON.parse(message).status
+  } catch (e) {
+    console.warn(e)
+    status = -1
   }
-  emit('running')
+  return (inst.value = status)
 }
 
-function onUpdateScreenShot (message) {
-  screenshotPic = `data:image/jpeg;base64,${message.replace(/\s/g, '')}`
-  emit('screenshot')
+function screenshotParser (inst, message) {
+  inst.value = message ? `data:image/jpeg;base64,${message.replace(/\s/g, '')}` : null
+  return inst.value
 }
 
-function onUpdateFile (message) {
+function downloadParser (inst, message) {
   try {
-    const { ossUrl, complete, errorReason, progress, rate, success } = JSON.parse(message)
-    const file = {
-      id: ossUrl,
-      name: ossUrl,
-      complete,
-      success,
-      status: complete
-        ? success
-          ? 'is-success'
-          : 'is-exception'
-        : 'is-processing',
-      tip: complete
-        ? success
-          ? '下载成功'
-          : errorReason || '下载失败'
-        : `${parseByte(rate)}/s`,
-      percentage: complete ? 100 : progress
-    }
-    if (download[file.id]) {
-      Object.assign(download.files[download[file.id] - 1], file)
+    if (message === 'clear') {
+      inst.mark = {}
+      inst.value = []
     } else {
-      download.files.push(file)
-      download[file.id] = download.files.length
+      const { ossUrl, complete, errorReason, progress, rate, success } = JSON.parse(message)
+      const file = {
+        id: ossUrl,
+        name: ossUrl,
+        complete,
+        success,
+        status: complete
+          ? success
+            ? 'is-success'
+            : 'is-exception'
+          : 'is-processing',
+        tip: complete
+          ? success
+            ? '下载成功'
+            : errorReason || '下载失败'
+          : `${parseByte(rate)}/s`,
+        percentage: complete ? 100 : progress
+      }
+      if (!inst.mark) {
+        inst.mark = {}
+      }
+      if (inst.mark[file.id]) {
+        Object.assign(inst.value[inst.mark[file.id] - 1], file)
+      } else {
+        inst.value.push(file)
+        inst.mark[file.id] = inst.value.length
+      }
     }
-    emit('download')
   } catch (e) {
     console.warn(e)
   }
+  return inst.value
 }
 
 function transformValue (type, value, unit) {
@@ -227,8 +268,8 @@ function transform (type, arr) {
 }
 
 function updateSensors (inst) {
-  getSensors({ custom: true }).then(({ data }) => {
-    inst.running && onUpdateSensors(inst, data)
+  inst.running && getSensors({ custom: true }).then(({ data }) => {
+    inst.running && onUpdate(inst, data)
   }).finally(() => {
     if (inst.running) {
       setTimeout(updateSensors, 5000, inst)
@@ -236,9 +277,13 @@ function updateSensors (inst) {
   })
 }
 
-function onUpdateSensors (inst, map) {
+function sensorParser (inst, map) {
   Object.keys(map).forEach(type => {
-    inst[type] = transform(type, map[type])
-    emit(type)
+    let inst = types.get(type)
+    if (!inst) {
+      inst = createLoopType(type)
+    }
+    inst.value = transform(type, map[type])
+    onUpdate(inst)
   })
 }

+ 1 - 1
src/views/remote/index.vue

@@ -17,7 +17,7 @@
           <el-link
             class="u-pointer"
             type="warning"
-            @click="getDevices"
+            @click="getGroups"
           >
             出错了,点击重试
           </el-link>