Эх сурвалжийг харах

feat(device): support online duration, remark

alarm supports filtering by type
Casper Dai 2 жил өмнө
parent
commit
e7b1906b96

+ 63 - 3
src/api/platform.js

@@ -5,6 +5,7 @@ import {
 } from '@/constant'
 import {
   parseByte,
+  parseTime,
   getAssetThumb,
   getAssetDiff
 } from '@/utils'
@@ -12,6 +13,7 @@ import request, { tenantRequest } from '@/utils/request'
 import {
   add,
   del,
+  send,
   messageSend,
   addUser,
   addTenant,
@@ -44,14 +46,19 @@ export function publish (type, ids, publishTarget, data) {
   }, '发布', tenantRequest)
 }
 
-export function saveLogger (data) {
+export function savePowerLogger (data) {
   const { user } = addUser()
   return tenantRequest({
     url: '/sysLog/webOperation',
     method: 'POST',
     data: addTenant({
-      ...data,
-      userId: user
+      userId: user,
+      business: 5,
+      ip: '',
+      costTime: 0,
+      status: 1,
+      operTime: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
+      ...data
     }),
     custom: true
   })
@@ -94,3 +101,56 @@ export function deleteSpacer ({ id }) {
     method: 'DELETE'
   })
 }
+
+export function sendDeviceAlarm (data) {
+  return request({
+    url: '/device/error/record',
+    method: 'POST',
+    data: {
+      messageId: `web_${Date.now()}_${Math.random().toString(16).slice(2)}`,
+      handleEnumId: 3,
+      statusEnumId: 0,
+      happenTime: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}'),
+      ...data
+    }
+  })
+}
+
+// 批量关闭/开启
+export function toggleDevicePower (data) {
+  return messageSend({
+    url: '/device/screenPower/batchOperate',
+    method: 'POST',
+    data
+  }, '添加批量任务')
+}
+
+// 查询上次操作
+export function getOperationResult (operationId) {
+  return (operationId ? request : send)({
+    url: '/device/screenPower/batchOperate',
+    method: 'GET',
+    params: { operationId }
+  })
+}
+
+// 分页查询历史操作结果
+export function getOperationResults (query) {
+  const { pageNum: pageIndex, pageSize, ...params } = query
+  return request({
+    url: '/device/screenPower/operationResult/pageQuery',
+    method: 'GET',
+    params: {
+      pageIndex, pageSize,
+      ...params
+    }
+  })
+}
+
+export function getOnlineDurationByIds (ids) {
+  return request({
+    url: '/deviceOnlineInfoDetail/calOnlineHoursByDeviceId',
+    method: 'POST',
+    data: ids
+  })
+}

BIN
src/assets/icon_online.png


+ 8 - 1
src/components/table/GridTable/index.vue

@@ -54,6 +54,13 @@
               @change="onChange"
             />
           </template>
+          <template v-if="filter.type === 'datepicker'">
+            <el-date-picker
+              v-model="options.params[filter.key]"
+              v-bind="filter.options"
+              @change="onChange"
+            />
+          </template>
           <template v-if="filter.type === 'search'">
             <search-input
               v-model.trim="options.params[filter.key]"
@@ -67,7 +74,7 @@
               class="c-sibling-item near o-button"
               @click="onChange"
             >
-              搜索
+              {{ filter.label || '搜索' }}
             </button>
           </template>
         </div>

+ 8 - 8
src/components/table/Table/index.vue

@@ -66,6 +66,13 @@
               <span class="has-active">{{ filter.label }}</span>
             </el-checkbox>
           </template>
+          <template v-if="filter.type === 'datepicker'">
+            <el-date-picker
+              v-model="options.params[filter.key]"
+              v-bind="filter.options"
+              @change="onChange"
+            />
+          </template>
           <template v-if="filter.type === 'search'">
             <search-input
               v-model.trim="options.params[filter.key]"
@@ -74,19 +81,12 @@
               @search="onChange"
             />
           </template>
-          <template v-if="filter.type === 'datepicker'">
-            <el-date-picker
-              v-model="options.params[filter.key]"
-              v-bind="filter.options"
-              @change="onChange"
-            />
-          </template>
           <template v-if="filter.type === 'refresh'">
             <button
               class="c-sibling-item near o-button"
               @click="onChange"
             >
-              搜索
+              {{ filter.label || '搜索' }}
             </button>
           </template>
         </div>

+ 18 - 0
src/scss/bem/_object.scss

@@ -309,3 +309,21 @@
     transform: translate(-50%, -50%);
   }
 }
+
+.o-status {
+  display: inline-block;
+  width: 16px;
+  height: 16px;
+  border-radius: 8px;
+  background-color: currentColor;
+  vertical-align: middle;
+}
+
+.o-icon-placeholder {
+  display: inline-flex;
+  justify-content: center;
+  align-items: center;
+  width: 24px;
+  height: 24px;
+  vertical-align: middle;
+}

+ 39 - 1
src/views/device/detail/components/DeviceAlarm.vue

@@ -22,6 +22,22 @@ import {
 import { createListOptions } from '@/utils'
 import { getDeviceAlarms } from '@/api/device'
 
+const DeviceAlarmTypes = [
+  { value: 1, label: '播控器上下线' },
+  { value: 2, label: '屏幕监测' },
+  { value: 3, label: '摄像头状态' },
+  { value: 4, label: '传感器预警' },
+  { value: 5, label: '电源管理' }
+]
+
+const DeviceAlarmType = {
+  1: [1, 9, 15, 16, 17, 30, 31, 32],
+  2: [0, 2, 3, 4, 5, 10, 11, 13, 35],
+  3: [7, 8, 33, 34],
+  4: [18, 19, 20, 21, 22, 23],
+  5: [36, 37, 38, 39]
+}
+
 export default {
   name: 'DeviceAlarm',
   props: {
@@ -34,7 +50,23 @@ export default {
     return {
       options: createListOptions({ deviceId: this.device.id }),
       schema: {
-        list: getDeviceAlarms,
+        list: this.getDeviceAlarms,
+        filters: [
+          { key: 'type', type: 'select', placeholder: '预警类型', options: [
+            ...DeviceAlarmTypes
+          ], className: 'u-width--sm' },
+          { key: 'happenDate', type: 'datepicker', options: {
+            mode: 'date',
+            placeholder: '预警日期',
+            'value-format': 'yyyy-MM-dd',
+            'class': 'u-width',
+            'picker-options': {
+              disabledDate (date) {
+                return date > new Date()
+              }
+            }
+          } }
+        ],
         condition: { deviceId: this.device.id },
         cols: [
           { prop: 'file', label: '截图', type: 'asset', refresh: true, on: this.onView },
@@ -60,6 +92,12 @@ export default {
     }
   },
   methods: {
+    getDeviceAlarms ({ type, ...params }) {
+      return getDeviceAlarms({
+        types: DeviceAlarmType[type]?.join(','),
+        ...params
+      })
+    },
     onView ({ file }) {
       this.$refs.previewDialog.show(file)
     },

+ 239 - 0
src/views/device/detail/components/DeviceRuntime/OnlineDuration.vue

@@ -0,0 +1,239 @@
+<template>
+  <div class="l-flex--col c-device-grid-item has-border radius">
+    <div class="l-flex__none l-flex--row c-sibling-item--v">
+      <i class="l-flex__none c-sibling-item o-icon" />
+      <span class="l-flex__fill c-sibling-item near u-color--info u-ellipsis">在线总时长</span>
+      <div class="u-color--info u-font-size--xs">{{ timestamp }}</div>
+      <!-- <i
+        class="el-icon-date"
+        @click="open()"
+      /> -->
+      <!-- <el-dialog
+        title="历史在线时长"
+        custom-class="c-dialog xl"
+        :visible.sync="dialogVisible"
+        width="30%"
+        close-on-press-escape="true"
+      >
+        <div class="picker_top">
+          <el-date-picker
+            v-model="date"
+            format="yyyy-MM-dd"
+            value-format="yyyy-MM-dd"
+            type="daterange"
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            @change="getTime()"
+          />
+        </div>
+        <div
+          id="chart"
+          class="content"
+          style="width:1250px;height:1000px "
+        />
+      </el-dialog> -->
+    </div>
+    <div class="l-flex__fill l-flex--row center u-color--black u-text--center">
+      <div
+        v-if="duration"
+        class="u-font-size--lg"
+      >
+        {{ duration }}
+      </div>
+      <i
+        v-else
+        class="el-icon-loading"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts'
+import { getOnlineDurationByIds } from '@/api/platform'
+
+export default {
+  name: 'OnlineDuration',
+  props: {
+    device: {
+      type: Object,
+      required: true
+    }
+  },
+  data () {
+    return {
+      duration: '',
+      timestamp: '',
+      datearr: [],
+      publication: [],
+      date: [],
+      dialogVisible: false
+    }
+  },
+  created () {
+    this.$running = true
+    this.$timer = -1
+    this.startRun()
+  },
+  mounted () {
+    this.datearr = this.getDayAll(this.getDay(-1), this.getDay(0))
+  },
+  beforeDestroy () {
+    this.$running = false
+    clearTimeout(this.$timer)
+  },
+  methods: {
+    startRun () {
+      if (!this.$running) {
+        return
+      }
+      getOnlineDurationByIds([this.device.id], { custom: true }).then(({ data }) => {
+        if (data?.[0]) {
+          if (data[0].powerSeconds === '0') {
+            this.duration = this.transformDuration(data[0].onlineSeconds)
+            this.timestamp = data[0].onlineSecondsUpdateTime
+          } else {
+            this.duration = this.transformDuration(data[0].powerSeconds)
+            this.timestamp = data[0].powerSecondsUpdateTime
+          }
+        } else {
+          this.duration = '暂无统计数据'
+        }
+      }).finally(() => {
+        if (this.$running) {
+          this.$timer = setTimeout(this.startRun, 60000)
+        }
+      })
+    },
+    transformDuration (duration) {
+      duration = Number(duration)
+      return [
+        { value: duration / (24 * 3600) | 0, unit: '天' },
+        { value: (duration % (24 * 3600)) / 3600 | 0, unit: '小时' },
+        { value: (duration / 3600) / 60 | 0, unit: '分' },
+        { value: duration % 60, unit: '秒' }
+      ].reduce((curr, { value, unit }) => value ? `${curr}${value}${unit}` : curr, '')
+    },
+    getDayAll (starDay, endDay) {
+      const arr = []
+      const dates = []
+      const db = new Date(starDay)
+      const de = new Date(endDay)
+      const s = db.getTime() - 24 * 60 * 60 * 1000
+      const d = de.getTime() - 24 * 60 * 60 * 1000
+      for (let i = s; i <= d;) {
+        i += 24 * 60 * 60 * 1000
+        arr.push(parseInt(i))
+      }
+      for (const j in arr) {
+        if (arr[j] !== null) {
+          const time = new Date(arr[j])
+          const year = time.getFullYear(time)
+          const mouth = (time.getMonth() + 1) >= 10 ? (time.getMonth() + 1) : (`0${time.getMonth() + 1}`)
+          const day = time.getDate() >= 10 ? time.getDate() : (`0${time.getDate()}`)
+          const YYMMDD = `${year}-${mouth}-${day}`
+          dates.push(YYMMDD)
+        }
+      }
+
+      return dates
+    },
+    getDay (setmonth) {
+      const date = new Date()
+      const seperator1 = '-'
+      const year = date.getFullYear()
+      let month = date.getMonth() + 1 + setmonth
+      let strDate = date.getDate()
+      if (month >= 1 && month <= 9) {
+        month = `0${month}`
+      }
+      if (strDate >= 0 && strDate <= 9) {
+        strDate = `0${strDate}`
+      }
+      const currentdate = year + seperator1 + month + seperator1 + strDate
+      return currentdate
+    },
+    getTime () {
+      this.datearr = this.getDayAll(this.date[0], this.date[1])
+      this.setChart()
+    },
+    open () {
+      this.dialogVisible = true
+      this.setChart()
+    },
+    setChart () {
+      setTimeout(() => {
+        const option = {
+          tooltip: {
+            trigger: 'axis'
+          },
+          legend: {
+            data: ['设备1', '设备2', '设备3', '设备4', '设备5']
+          },
+          grid: {
+            left: '3%',
+            right: '4%',
+            bottom: '3%',
+            containLabel: true
+          },
+          xAxis: {
+            type: 'category',
+            boundaryGap: false,
+            data: this.datearr
+          },
+          yAxis: {
+            type: 'value'
+          },
+          series: [
+            {
+              name: '设备1',
+              type: 'line',
+              stack: 'Total',
+              data: [120, 132, 101, 134, 90, 230, 210]
+            },
+            {
+              name: '设备2',
+              type: 'line',
+              stack: 'Total',
+              data: [220, 182, 191, 234, 290, 330, 310]
+            },
+            {
+              name: '设备3',
+              type: 'line',
+              stack: 'Total',
+              data: [150, 232, 201, 154, 190, 330, 410]
+            },
+            {
+              name: '设备4',
+              type: 'line',
+              stack: 'Total',
+              data: [320, 332, 301, 334, 390, 330, 320]
+            },
+            {
+              name: '设备5',
+              type: 'line',
+              stack: 'Total',
+              data: [820, 932, 901, 934, 1290, 1330, 1320]
+            }
+          ]
+        }
+        const chartDom = document.getElementById('chart')
+        console.log('chart', document.getElementById('chart'))
+        const myChart = echarts.init(chartDom)
+        myChart.setOption(option)
+      }, 100)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.picker_top {
+  margin-left: 30px;
+}
+
+.o-icon {
+  background-image: url("~@/assets/icon_online.png");
+}
+</style>

+ 16 - 13
src/views/device/detail/components/DeviceRuntime/index.vue

@@ -1,4 +1,5 @@
 <script>
+import OnlineDuration from './OnlineDuration'
 import Running from './Running'
 import ScreenShot from './ScreenShot'
 import Download from './Download'
@@ -6,6 +7,7 @@ import Download from './Download'
 export default {
   name: 'DeviceRuntime',
   components: {
+    OnlineDuration,
     Running,
     ScreenShot,
     Download
@@ -17,25 +19,26 @@ export default {
     }
   },
   render (h) {
-    if (!this.online) {
-      return h(
-        'div',
-        {
-          staticClass: 'u-font-size--lg u-color--info u-text--center'
-        },
-        '设备当前未上线'
-      )
-    }
+    // if (!this.online) {
+    //   return h(
+    //     'div',
+    //     {
+    //       staticClass: 'u-font-size--lg u-color--info u-text--center'
+    //     },
+    //     '设备当前未上线'
+    //   )
+    // }
     return h(
       'div',
       {
         staticClass: 'l-grid--info'
       },
       [
-        h('Running'),
-        h('ScreenShot', { props: this.$attrs }),
-        h('Download')
-      ]
+        h('OnlineDuration', { props: this.$attrs }),
+        this.online && h('Running'),
+        this.online && h('ScreenShot', { props: this.$attrs }),
+        this.online && h('Download')
+      ].filter(Boolean)
     )
   }
 }

+ 5 - 84
src/views/device/index.vue

@@ -15,16 +15,12 @@
       class="c-sibling-item far"
       row-key="id"
       :schema="schema"
-      @row-click="onRowClick"
     />
   </wrapper>
 </template>
 
 <script>
-import {
-  getDevicesByQuery,
-  getSubDevices
-} from '@/api/device'
+import { getDevicesByQuery } from '@/api/device'
 
 export default {
   name: 'DeviceList',
@@ -33,23 +29,12 @@ export default {
       schema: {
         keepalive: true,
         list: this.getDevicesByQuery,
-        transform: this.transform,
-        transformData: __SUB_DEVICE__ && this.transformTableData,
         filters: [
-          { key: 'name', type: 'search', placeholder: '设备名称' }
+          { key: 'name', type: 'search', placeholder: '设备名称' },
+          { type: 'refresh' }
         ],
         cols: [
-          { type: 'refresh', render: __SUB_DEVICE__
-            ? (data, h) => data.isMaster
-              ? h('i', {
-                staticClass: `o-expand-icon u-pointer ${data.loading ? 'el-icon-loading' : 'el-icon-arrow-right'}`,
-                class: { expand: data.expand }
-              })
-              : null
-            : null },
-          __SUB_DEVICE__
-            ? { label: '设备名称', render: (data, h) => data.empty ? h('span', { staticClass: 'u-color--info' }, '暂无备份设备') : data.name, 'min-width': 120 }
-            : { prop: 'name', label: '设备名称', 'min-width': 120 },
+          { prop: 'name', label: '设备名称', 'min-width': 120 },
           { type: 'tag', render: ({ empty, activate, onlineStatus }) => empty
             ? null
             : activate
@@ -66,7 +51,7 @@ export default {
               : '-', 'min-width': 100 },
           { prop: 'address', label: '地址', 'min-width': 160 },
           { type: 'invoke', render: [
-            { label: '详情', render: ({ isMaster }) => isMaster, on: this.onViewDevice }
+            { label: '详情', on: this.onViewDevice }
           ] }
         ]
       }
@@ -87,26 +72,6 @@ export default {
         ...params
       })
     },
-    transform (data) {
-      return {
-        ...data,
-        loaded: false,
-        loading: false,
-        expand: false,
-        subs: [],
-        isMaster: true
-      }
-    },
-    transformTableData (data) {
-      const arr = []
-      data.forEach(item => {
-        arr.push(item)
-        if (item.loaded && item.expand) {
-          arr.push(...item.subs)
-        }
-      })
-      return arr
-    },
     onViewDevice (item) {
       this.$router.push({
         name: 'device-detail',
@@ -114,50 +79,6 @@ export default {
           id: item.id
         }
       })
-    },
-    reloadSubDevices (item) {
-      item.loaded = false
-      item.children = []
-      this.getSubDevices(item)
-    },
-    getSubDevices (item, pageSize = 999) {
-      item.loading = true
-      getSubDevices({
-        id: item.id,
-        pageNum: 1,
-        pageSize
-      }).then(({ data, totalCount }) => {
-        if (totalCount > pageSize) {
-          this.getSubDevices(item, totalCount)
-        } else {
-          item.loading = false
-          item.loaded = true
-          item.expand = true
-          if (data.length === 0) {
-            item.subs = [{ id: `${Math.random()}`, empty: true }]
-          } else {
-            item.subs = data.map(device => {
-              return {
-                ...device,
-                isMaster: false,
-                empty: false,
-                parent: item
-              }
-            })
-          }
-        }
-      }, () => {
-        item.loading = false
-      })
-    },
-    onRowClick (data) {
-      if (__SUB_DEVICE__ && data.isMaster) {
-        if (data.loaded) {
-          data.expand = !data.expand
-        } else if (!data.loading) {
-          this.getSubDevices(data)
-        }
-      }
     }
   }
 }

+ 28 - 1
src/views/external/box/components/Device.vue

@@ -25,6 +25,11 @@
           :schema="productSelectSchema"
           placeholder="请选择配置"
         />
+        <span class="c-grid-form__label u-required">型号</span>
+        <el-input
+          v-model.trim="currObj.remark"
+          placeholder="播控器的系列号"
+        />
         <span class="c-grid-form__label u-required">序列号</span>
         <el-input
           v-model.trim="currObj.serialNumber"
@@ -89,7 +94,8 @@ import {
   addSubDevice,
   activateDevice,
   deactivateDevice,
-  getProducts
+  getProducts,
+  updateDevice
 } from '@/api/device'
 import MeshDialog from '../../components/MeshDialog.vue'
 
@@ -147,6 +153,13 @@ export default {
           __SUB_DEVICE__
             ? { label: '设备名称', render: (data, h) => data.empty ? h('span', { staticClass: 'u-color--info' }, '暂无备份设备') : data.name, 'min-width': 120 }
             : { prop: 'name', label: '设备名称', 'min-width': 120 },
+          { label: '型号', render: (data, h) => h('edit-input', {
+            props: {
+              value: data.remark,
+              placeholder: '-'
+            },
+            on: { edit: val => this.onEditRemark(data, val) }
+          }), 'class-name': 'c-edit-column' },
           { prop: 'productName', label: '配置' },
           { prop: 'serialNumber', label: '序列号', 'min-width': 140 },
           { prop: 'mac', label: 'MAC', 'min-width': 140 },
@@ -225,6 +238,7 @@ export default {
         const { productId, openTime, closeTime, address, longitude, latitude } = this.$master
         this.currObj = {
           name: '',
+          remark: '',
           productId,
           serialNumber: '',
           mac: '',
@@ -237,6 +251,7 @@ export default {
       } else {
         this.currObj = {
           name: '',
+          remark: '',
           productId: '',
           serialNumber: '',
           mac: '',
@@ -445,6 +460,18 @@ export default {
     },
     onViewMesh ({ id }) {
       this.$refs.meshDialog.show(id)
+    },
+    onEditRemark (device, { newVal, oldVal }) {
+      if (newVal === oldVal) {
+        return
+      }
+      device.remark = newVal
+      updateDevice({
+        id: device.id,
+        remark: newVal
+      }).catch(() => {
+        device.remark = oldVal
+      })
     }
   }
 }

+ 31 - 23
src/views/realm/device/index.vue

@@ -10,17 +10,17 @@
       class="c-sibling-item c-sidebar u-width--md"
       @change="onGroupChanged"
     />
-    <table-dialog
-      ref="attentionList"
-      title="关注列表"
-      :schema="attentionSchema"
-    />
     <schema-table
       ref="table"
       class="c-sibling-item far"
       row-key="id"
       :schema="schema"
     />
+    <table-dialog
+      ref="attentionList"
+      title="关注列表"
+      :schema="attentionSchema"
+    />
     <confirm-dialog
       ref="editDialog"
       title="编辑"
@@ -92,18 +92,16 @@ export default {
       attentionSchema: {
         nonPagination: true,
         list: getDeviceAttentionList,
+        listeners: {
+          'row-click': this.onAttentionRowClick
+        },
         cols: [
-          { type: 'refresh', render: (data, h) => h(
+          { label: '', render: (data, h) => h(
             'i',
             {
-              staticClass: `${data.ifAttention === 1 ? 'el-icon-star-on u-font-size--xl' : 'el-icon-star-off u-font-size--lg'} u-color--warning`,
-              on: {
-                click: () => {
-                  data.ifAttention === 1 ? this.cancelDeviceAttention(data) : this.addDeviceAttention(data)
-                }
-              }
+              staticClass: `o-icon-placeholder ${data.ifAttention === 1 ? 'el-icon-star-on u-font-size--xl' : 'el-icon-star-off u-font-size--lg'} u-color--warning`
             }
-          ) },
+          ), width: 60, align: 'center' },
           { prop: 'name', label: '设备名称' }
         ]
       },
@@ -111,7 +109,7 @@ export default {
         keepalive: true,
         list: this.getDevicesByQuery,
         transform: this.transform,
-        transformData: __SUB_DEVICE__ && this.transformTableData,
+        // transformData: __SUB_DEVICE__ && this.transformTableData,
         // listeners: {
         //   'row-click': this.onRowClick
         // },
@@ -136,7 +134,7 @@ export default {
           { type: 'refresh', render: (data, h) => h(
             'i',
             {
-              staticClass: `${data.ifAttention === 1 ? 'el-icon-star-on u-font-size--xl' : 'el-icon-star-off u-font-size--lg'} u-color--warning`,
+              staticClass: `o-icon-placeholder ${data.ifAttention === 1 ? 'el-icon-star-on u-font-size--xl' : 'el-icon-star-off u-font-size--lg'} u-color--warning`,
               on: {
                 click: () => {
                   data.ifAttention === 1 ? this.cancelDeviceAttention(data) : this.addDeviceAttention(data)
@@ -360,23 +358,33 @@ export default {
         params: { id }
       })
     },
-    addDeviceAttention (row) {
-      addDeviceAttention(row.id).then(() => {
-        row.ifAttention = 1
+    addDeviceAttention (data) {
+      addDeviceAttention(data.id).then(() => {
+        data.ifAttention = 1
       })
     },
-    cancelDeviceAttention (row) {
-      cancelDeviceAttention(row.id).then(() => {
-        row.ifAttention = 0
+    cancelDeviceAttention (data) {
+      cancelDeviceAttention(data.id).then(() => {
+        data.ifAttention = 0
       })
     },
-    cancelDeviceAttentionByDialog (row) {
-      cancelDeviceAttention(row.id).then(() => {
+    addDeviceAttentionByDialog (data) {
+      cancelDeviceAttention(data.id).then(() => {
+        data.ifAttention = 1
+        this.$refs.table.pageTo()
+      })
+    },
+    cancelDeviceAttentionByDialog (data) {
+      cancelDeviceAttention(data.id).then(() => {
+        data.ifAttention = 0
         this.$refs.table.pageTo()
       })
     },
     onShowAttentionList () {
       this.$refs.attentionList.show()
+    },
+    onAttentionRowClick (data) {
+      data.ifAttention === 1 ? this.cancelDeviceAttentionByDialog(data) : this.addDeviceAttentionByDialog(data)
     }
   }
 }

+ 18 - 0
src/views/realm/settings/api.js

@@ -72,3 +72,21 @@ export function updateTenantAttribute (key, val) {
     })
   })
 }
+
+const REFRESH_KEY = 'refresh_online_seconds'
+export function getOnlineRefreshInterval () {
+  return send({
+    url: '/deviceOnlineInfoDetail/getExceptionOfflineConfig',
+    method: 'GET'
+  }).then(({ data }) => {
+    return { data: data.find(({ paramKey }) => paramKey === REFRESH_KEY) }
+  })
+}
+
+export function updateOnlineRefreshInterval (data) {
+  return update({
+    url: '/deviceOnlineInfoDetail/updateExceptionOfflineConfig',
+    method: 'POST',
+    data
+  })
+}

+ 62 - 0
src/views/realm/settings/components/OnlineRefreshConfigDialog.vue

@@ -0,0 +1,62 @@
+<template>
+  <confirm-dialog
+    ref="configDialog"
+    title="在线时长刷新间隔"
+    @confirm="onSave"
+  >
+    <div class="c-grid-form auto u-align-self--center">
+      <div class="c-grid-form__label">刷新间隔(秒)</div>
+      <div class="l-flex--row">
+        <el-input-number
+          v-model="config.paramValue"
+          class=" c-sibling-item"
+          controls-position="right"
+          :min="600"
+          step-strictly
+        />
+      </div>
+    </div>
+  </confirm-dialog>
+</template>
+
+<script>
+import {
+  getOnlineRefreshInterval,
+  updateOnlineRefreshInterval
+} from '../api'
+
+export default {
+  name: 'OnlineRefreshConfigDialog',
+  data () {
+    return {
+      config: {}
+    }
+  },
+  methods: {
+    show () {
+      getOnlineRefreshInterval().then(({ data }) => {
+        if (data) {
+          this.config = data
+          this.$temp = data.paramValue
+          this.$refs.configDialog.show()
+        } else {
+          this.$message({
+            type: 'warning',
+            message: '属性未配置,请联系管理员'
+          })
+        }
+      })
+    },
+    onSave (done) {
+      if (this.$temp === this.config.paramValue) {
+        done()
+        return
+      }
+      updateOnlineRefreshInterval({
+        id: this.config.id,
+        paramValue: this.config.paramValue
+      }).then(done)
+    }
+  }
+}
+</script>

+ 12 - 0
src/views/realm/settings/index.vue

@@ -19,6 +19,12 @@
       >
         定时重启策略
       </button>
+      <button
+        class="o-button"
+        @click="onOnlineRefreshConfig"
+      >
+        在线时长统计频率
+      </button>
     </div>
     <span class="c-sibling-item--v u-font-size--sm u-bold">安全配置</span>
     <div class="c-sibling-item--v l-grid--info mini">
@@ -58,6 +64,7 @@
     </div>
     <alarm-strat-config-dialog ref="alarmStratConfigDialog" />
     <auto-restart-config-dialog ref="autoRestartConfigDialog" />
+    <online-refresh-config-dialog ref="onlineRefreshConfigDialog" />
     <power-config-dialog ref="powerConfigDialog" />
     <unattend-config-dialog ref="unattendConfigDialog" />
     <temperature-Threshold-config-dialog ref="temperatureThresholdConfigDialog" />
@@ -69,6 +76,7 @@
 <script>
 import AlarmStratConfigDialog from './components/AlarmStratConfigDialog.vue'
 import AutoRestartConfigDialog from './components/AutoRestartConfigDialog.vue'
+import OnlineRefreshConfigDialog from './components/OnlineRefreshConfigDialog.vue'
 import PowerConfigDialog from './components/PowerConfigDialog.vue'
 import UnattendConfigDialog from './components/UnattendConfigDialog.vue'
 import TemperatureThresholdConfigDialog from './components/TemperatureThresholdConfigDialog.vue'
@@ -80,6 +88,7 @@ export default {
   components: {
     AlarmStratConfigDialog,
     AutoRestartConfigDialog,
+    OnlineRefreshConfigDialog,
     PowerConfigDialog,
     UnattendConfigDialog,
     TemperatureThresholdConfigDialog,
@@ -93,6 +102,9 @@ export default {
     onAutoRestartConfig () {
       this.$refs.autoRestartConfigDialog.show()
     },
+    onOnlineRefreshConfig () {
+      this.$refs.onlineRefreshConfigDialog.show()
+    },
     onPowerConfig () {
       this.$refs.powerConfigDialog.show()
     },