fenghao 2 rokov pred
rodič
commit
0bd053e383

+ 5 - 0
src/router/index.js

@@ -414,6 +414,11 @@ export const asyncRoutes = [
           }
         ]
       },
+      {
+        path: 'device/log',
+        component: () => import('@/views/platform/device-log/index'),
+        meta: { title: '抓取异常日志' }
+      },
       {
         path: 'template',
         component: () => import('@/views/broadcast/template/index'),

+ 70 - 0
src/views/platform/device-log/api.js

@@ -0,0 +1,70 @@
+import request from '@/utils/request'
+import { addTenant } from '@/api/base'
+
+// 新增日志抓取设置
+export function addRemoteLogConfig (data, options) {
+  return request({
+    url: `/device/remoteLog`,
+    method: 'POST',
+    data: addTenant(data), ...options
+  })
+}
+
+// 设备日志抓取设置
+export function updateRemoteLogConfig (data, options) {
+  return request({
+    url: `/device/remoteLog`,
+    method: 'PUT',
+    data, ...options
+  })
+}
+
+// 日志配置查询
+export function getRemoteLogConfig (params) {
+  return request({
+    url: `/device/remoteLog`,
+    method: 'GET',
+    params
+  })
+}
+
+// 开启日志抓取
+export function switchRemoteLog (data, options) {
+  return request({
+    url: `/device/remoteLog/switch`,
+    method: 'PUT',
+    data,
+    ...options
+  })
+}
+
+// 设备上报记录查询
+export function getRemoteLogs (query) {
+  const { pageNum: pageIndex, pageSize, ...params } = query
+  return request({
+    url: `/device/remoteLog/record/pageQuery`,
+    method: 'GET',
+    params: addTenant({
+      pageIndex, pageSize,
+      ...params
+    })
+  })
+}
+
+// 设备心跳列表接口
+export function getHeartbeats (params) {
+  return request({
+    url: `/device/remoteLog/heartbeat`,
+    method: 'GET',
+    params
+  })
+}
+
+// 设备单个心跳查询
+export function getHeartbeat (params) {
+  return request({
+    url: `/device/remoteLog/singleHeartbeat`,
+    method: 'GET',
+    params
+  })
+}

+ 333 - 0
src/views/platform/device-log/index.vue

@@ -0,0 +1,333 @@
+<template>
+  <wrapper
+    fill
+    margin
+    padding
+    background
+  >
+    <schema-table
+      ref="table"
+      :schema="schema"
+    />
+
+    <confirm-dialog
+      ref="editDialog"
+      title="抓取配置"
+      @confirm="onConfirm('save')"
+    >
+      <div class="c-grid-form u-align-self--center">
+        <span class="c-grid-form__label u-required">日志时长(s)</span>
+        <el-input-number
+          v-model="logSetting.duration"
+          :min="60"
+          :max="999999"
+          step-strictly
+        />
+        <span class="c-grid-form__label u-required">抓取指令</span>
+        <el-input
+          v-model.trim="logSetting.commands"
+          type="textarea"
+          :rows="3"
+        />
+        <span class="c-grid-form__label " />
+        <div
+          class="
+          c-sibling-item--v
+          nearer
+          o-info"
+        >
+          多条指令以分号分隔
+        </div>
+        <span
+          class="c-grid-form__label u-required"
+        >是否重启</span>
+        <el-switch
+          v-model="logSetting.reboot"
+          active-color="#13ce66"
+          inactive-color="#ff4949"
+          class="c-grid-form__option"
+        />
+      </div>
+      <!-- <template #footer="{ confirm, cancel }"> -->
+      <template #footer="{ cancel }">
+        <button
+          class="c-sibling-item o-button"
+          @click="onSwitch"
+        >
+          开启抓取
+        </button>
+        <!-- <button
+          class="c-sibling-item o-button"
+          @click="confirm"
+        >保存</button> -->
+        <button
+          class="c-sibling-item o-button o-button--cancel"
+          @click="cancel"
+        >
+          取消
+        </button>
+      </template>
+    </confirm-dialog>
+    <table-dialog
+      ref="resultDialog"
+      title="抓取结果"
+      :schema="resultSchema"
+      size="lg"
+    />
+    <table-dialog
+      ref="heartbeatDialog"
+      size="medium"
+      title="上报记录"
+      :schema="heartbeatSchema"
+    />
+  </wrapper>
+</template>
+<script>
+import { getDevices } from '@/api/device'
+import { parseTime } from '@/utils'
+import {
+  getRemoteLogConfig,
+  switchRemoteLog,
+  addRemoteLogConfig,
+  updateRemoteLogConfig,
+  getRemoteLogs,
+  getHeartbeats,
+  getHeartbeat
+} from './api'
+const defaultLogSettingForm = {
+  duration: 60,
+  commands: 'logcat -vtime | grep -i -E "MqttClient|PushCallback|MqttService"',
+  reboot: true
+}
+export default {
+  name: 'DeviceLog',
+  data () {
+    return {
+      logSetting: {},
+      schema: {
+        buttons: [
+          { label: '上报记录', on: this.onHeartbeats }
+        ],
+        condition: { name: '' },
+        list: getDevices,
+        filters: [{ key: 'name', type: 'search', placeholder: '设备名称' }],
+        cols: [
+          { prop: 'name', label: '设备名称', 'min-width': 120 },
+          { prop: 'productName', label: '配置' },
+          //   { prop: 'serialNumber', label: '序列号' },
+          //   { prop: 'mac', label: 'MAC' },
+          { prop: 'address', label: '地址', 'min-width': 100 },
+          {
+            type: 'tag',
+            render: ({ empty, activate, onlineStatus }) => empty
+              ? null
+              : activate
+                ? onlineStatus === 0
+                  ? { type: 'primary', label: '已启用' }
+                  : onlineStatus === 1
+                    ? { type: 'success', label: '在线' }
+                    : { type: 'danger', label: '离线' }
+                : { type: 'warning', label: '未激活' }
+          },
+          {
+            type: 'invoke',
+            render: [
+              { label: '停止抓取', on: this.onStop },
+              { label: '开始抓取', on: this.onEdit },
+              { label: '抓取结果', on: this.onResult },
+              { label: '上报记录', on: this.onHeartbeat }
+
+            ],
+            width: 300
+          }
+        ]
+      },
+      heartbeatSchema: {
+        nonPagination: true,
+        list: this.getheartbeatData,
+        cols: [{ type: 'refresh' },
+               { prop: 'sn', label: 'sn' },
+               { prop: 'mac', label: 'mac' },
+               { prop: 'ip', label: 'ip' },
+               { prop: 'ip', label: '时间', render ({ timestamp }) {
+                 return parseTime(new Date(timestamp))
+               } }
+
+        ]
+      },
+      curSetting: null,
+      curDeviceId: null
+    }
+  },
+  computed: {
+    resultSchema () {
+      return {
+        condition: { deviceId: this.curDeviceId },
+        list: getRemoteLogs,
+        cols: [
+          { type: 'refresh' },
+          { prop: 'message', label: '消息', render ({ message }) {
+            return message || '-'
+          } },
+          { prop: 'createTime', label: '创建时间' },
+          { label: '执行状态', type: 'tag', render ({ status }) {
+            return {
+              type: status ? 'success' : 'danger',
+              label: status ? '成功' : '失败'
+            }
+          } },
+          {
+            type: 'invoke',
+            render: [
+              { label: '下载日志', on: this.onDownload }
+            ]
+          }
+        ]
+      }
+    }
+  },
+  methods: {
+    transformCommand (params) {
+      if (Array.isArray(params)) {
+        return params.join(';')
+      } else if (params.indexOf(';') !== -1) {
+        return params.split(';')
+      } else if (params.indexOf(';') !== -1) {
+        return params.split(';')
+      }
+      return [params]
+    },
+    onEdit (device) {
+      const loading = this.$showLoading()
+      getRemoteLogConfig({ deviceId: device.id })
+        .then(({ data: logSetting }) => {
+          if (logSetting) {
+            const { duration, commands, reboot } = logSetting
+            this.logSetting = { duration, commands: this.transformCommand(commands), reboot }
+            this.curSetting = logSetting
+          } else {
+            this.logSetting = defaultLogSettingForm
+            this.curSetting = null
+          }
+          this.$refs.editDialog.show()
+          this.curDeviceId = device.id
+        })
+        .finally(() => {
+          this.$closeLoading(loading)
+        })
+    },
+    onConfirm (type) {
+      const method = this.curSetting
+        ? updateRemoteLogConfig
+        : addRemoteLogConfig
+      return new Promise((resolve, reject) => {
+        method({ ...{ ...this.logSetting, commands: this.transformCommand(this.logSetting.commands) }, deviceId: this.curDeviceId }).then(
+          data => {
+            if (type === 'save') {
+              this.$message({
+                type: 'success',
+                message: `${this.curSetting ? '更新' : '新增'}成功`
+              })
+              this.curSetting = data
+            }
+            resolve(true)
+          },
+          error => {
+            console.warn(error)
+            this.$message({
+              type: 'warning',
+              message: `抓取配置生成失败`
+            })
+            reject(error)
+          }
+        )
+      })
+    },
+    async onSwitch () {
+      const loading = this.$showLoading()
+      const res = await this.onConfirm()
+      if (!res) {
+        this.$closeLoading(loading)
+        return
+      }
+      switchRemoteLog({
+        activate: true,
+        deviceId: this.curDeviceId
+      }, { custom: true }).then(
+        () => {
+          this.$refs.editDialog.hide()
+          this.$message({
+            type: 'success',
+            message: `开启抓取成功`
+          })
+        },
+        error => {
+          console.warn(error)
+          this.$message({
+            type: 'warning',
+            message: `开启抓取失败`
+          })
+        }
+      ).finally(() => {
+        this.$closeLoading(loading)
+      })
+    },
+    onStop (device) {
+      switchRemoteLog({
+        activate: false,
+        deviceId: device.id
+      }).then(
+        () => {
+          this.$message({
+            type: 'success',
+            message: `停止抓取成功`
+          })
+        },
+        error => {
+          console.warn(error)
+          this.$message({
+            type: 'warning',
+            message: `停止抓取失败`
+          })
+        }
+      )
+    },
+    onResult (device) {
+      this.curDeviceId = device.id
+      this.$refs.resultDialog.show()
+    },
+    getheartbeatData () {
+      if (this.$sn) {
+        return getHeartbeat({ sn: this.$sn }).then(({ data }) => {
+          if (!data) { return [] }
+          if (!Array.isArray(data)) { return { data: [JSON.parse(data)] } }
+          return { data: data.filter(i => i).map(i => JSON.parse(i)) }
+        })
+      }
+      return getHeartbeats().then(({ data }) => {
+        if (!data) { return [] }
+        if (!Array.isArray(data)) { return { data: [JSON.parse(data)] } }
+        return { data: data.filter(i => i).map(i => JSON.parse(i)) }
+      })
+    },
+    onHeartbeats () {
+      this.$refs.heartbeatDialog.show()
+      this.$sn = null
+    },
+    onHeartbeat (device) {
+      this.$refs.heartbeatDialog.show()
+      this.$sn = device.serialNumber
+    },
+    onDownload (file) {
+      const a = document.createElement('a')
+      a.style.display = 'none'
+      a.setAttribute('target', '_blank')
+      a.setAttribute('download', '日志')
+      a.href = file.fileUrl
+      document.body.appendChild(a)
+      a.click()
+      document.body.removeChild(a)
+    }
+  }
+}
+</script>