Ver código fonte

feat: 缺少在线调试 消息跟踪

lvkun 3 anos atrás
pai
commit
8dbf04ffb8

BIN
dist.zip


BIN
public/logo.ico


+ 23 - 0
src/api/iot/device.ts

@@ -69,6 +69,13 @@ export const getSubDeviceList = (id: string, params: IOT.API.DEVICE.QueryPamars)
   })
 }
 
+export const delSubDevice = (id: String) => {
+  return request<string>({
+    url: `/device/subDevice/${id}`,
+    method: 'DELETE'
+  })
+}
+
 export const addSubDevice = (data: IOT.API.DEVICE.SubBodyParams) => {
   return request<string>({
     url: '/device/subDevice',
@@ -228,3 +235,19 @@ export const getDeviceByGroup = (params: IOT.API.DEVICE.GroupQueryParams) => {
     params
   })
 }
+
+/** 实时数据 */
+export const getDeviceAttribute = (deviceId: string) => {
+  return request<IOT.API.DEVICE.Device[]>({
+    url: `/deviceAttribute/latest?deviceId=${deviceId}`,
+    method: 'GET'
+  })
+}
+
+/** 实时数据 */
+export const getDevicShadow = (deviceId: string) => {
+  return request<IOT.API.DEVICE.Device[]>({
+    url: `/deviceAttribute/shadow?deviceId=${deviceId}`,
+    method: 'GET'
+  })
+}

BIN
src/assets/logo.ico


BIN
src/assets/logo.png


+ 14 - 1
src/controller/iot/device.ts

@@ -2,7 +2,7 @@ import {
   addDevice, addSubDevice, delDevice, delDeviceMul, delDeviceTag,
   getDeviceById, getDeviceCount, getDeviceList, getDeviceMsgList, addDeviceMsg, getDeviceTag,
   getSubDeviceList, updateDeviceLabel, addDeviceCmd, getDeviceCmdList, addDeviceTag, addDeviceGroup,
-  listDeviceGroup, postGroupBindDevice, delGroupBindDevice, getDeviceByGroup, getDevicePage
+  listDeviceGroup, postGroupBindDevice, delGroupBindDevice, getDeviceByGroup, getDevicePage, delSubDevice, getDeviceAttribute, getDevicShadow
 } from '@/api/iot/device'
 import { DeviceMsgEnum } from '@/enum/common'
 import { message } from 'ant-design-vue'
@@ -97,6 +97,11 @@ export class DeviceContriller {
     message.success('新增成功')
   }
 
+  static async delSub (id: string) {
+    await delSubDevice(id)
+    message.success('删除成功')
+  }
+
   static async ListTag (params: {deviceId: number | string}) {
     return await getDeviceTag(params)
   }
@@ -190,4 +195,12 @@ export class DeviceContriller {
   static async getDeviceByGroup (params: IOT.API.DEVICE.GroupQueryParams) {
     return await getDeviceByGroup(params)
   }
+
+  static async getLiveData (deviceId: string) {
+    return await getDeviceAttribute(deviceId)
+  }
+
+  static async getDevicShadow (deviceId: string) {
+    return await getDevicShadow(deviceId)
+  }
 }

+ 14 - 11
src/layout/navbar.vue

@@ -3,7 +3,7 @@
       <a-row style="width: 100%;"  >
         <a-col :span="3" >
           <div class="logo" >
-            logo
+            <img :src="logoPng" alt="">
           </div>
         </a-col>
         <a-col :span="19" >
@@ -34,6 +34,10 @@
 import { ref } from 'vue'
 import { useAppRouter } from '@/store/router'
 import user from './user.vue'
+// import logo from '@/assets/logo.png'
+
+const logoPng = require('@/assets/logo.png')
+
 const appRouter = useAppRouter()
 
 const selectedKeys1 = ref<string[]>([appRouter.$state.router.navbar.selectPath])
@@ -46,15 +50,14 @@ const changeRouter = (path: string) => appRouter.changeNavbar(path)
 .header {
   position: relative;
   z-index: 2;
-  // width: 100vw;
-  // display: flex;
-  // height: 50px;
-  // .logo {
-  //   width: 170px;
-  //   color: #fff;
-  // }
-  // .ant-menu {
-  //   height: 50px;
-  // }
+}
+
+.logo {
+  width: 48px;
+  height: 48px;
+  img {
+    width: 100%;
+    height: 100%;
+  }
 }
 </style>

+ 1 - 1
src/layout/user.vue

@@ -2,7 +2,7 @@
   <div class="user" >
 
     <a-dropdown>
-      <a-badge :count="1">
+      <a-badge :count="0">
       <a-avatar shape="round" :src="ava"  style="cursor: pointer;" />
     </a-badge>
 

+ 7 - 0
src/pages/Iot/dataServer/history.vue

@@ -31,6 +31,8 @@
     :columns="columns"
     :loading="state.loading"
     :data-source="state.dataSource"
+    :pagenation="queryParams"
+    @change="changePage"
   >
 
   </a-table>
@@ -128,6 +130,11 @@ watch(
   }
 )
 
+const changePage = ({ current }) => {
+  queryParams.page = current
+  getHistoryPage()
+}
+
 const getDeviceList = async () => {
   const { data } = await DeviceContriller.list()
   state.deviceList = data

+ 7 - 0
src/pages/Iot/dataServer/openApi.vue

@@ -13,6 +13,8 @@
     :loading="state.loading"
     :data-source="state.dataSource"
     style="margin-top: 20px;"
+    :pagenation="queryParams"
+    @change="changePage"
   >
   <template #bodyCell="{column, record}" >
     <template v-if="column.key === 'apiDescription'" >
@@ -107,6 +109,11 @@ const state = reactive({
   dataSource: []
 })
 
+const changePage = ({ current }) => {
+  queryParams.page = current
+  getOpenApiPage()
+}
+
 const getOpenApiPage = async () => {
   state.loading = true
   const { data, sum } = await DataController.pageOpenAPiList(queryParams)

+ 1 - 1
src/pages/Iot/devOps/msgTracking.vue

@@ -1,6 +1,6 @@
 <template>
   <a-card>
-    message
+    <a-empty />
   </a-card>
   </template>
 <script lang='ts' setup >

+ 1 - 1
src/pages/Iot/devOps/statistReport.vue

@@ -1,6 +1,6 @@
 <template>
 <a-card>
-  Statistical report
+  <a-empty />
 </a-card>
 </template>
 <script lang='ts' setup >

+ 1 - 0
src/pages/Iot/device/components/cloudview.vue

@@ -28,6 +28,7 @@
         :columns="state.tasActive === 'msg' ? columns :cmdColumns"
         :dataSource="state.msgDataSource"
         :loading="state.loading"
+        :pagination="false"
       >
         <template #bodyCell="{ column, record }">
           <template v-if="column.key === 'status'">

+ 81 - 6
src/pages/Iot/device/components/deviceShadow.vue

@@ -7,28 +7,103 @@
     </div>
 
     <a-row justify="space-between" style="margin-top: 20px;" >
-      <a-col><a-input-search></a-input-search></a-col>
+      <a-col>
+        <!-- <a-input-search></a-input-search> -->
+      </a-col>
       <a-col>
         <a-space>
-          <a-button type="primary" >属性配置</a-button>
+          <!-- <a-button type="primary" >属性配置</a-button> -->
           <reload-icon-tsx :loading="state.loading" :reload="getDeviceShadow" />
         </a-space>
       </a-col>
     </a-row>
+
+      <a-table
+        style="margin-top: 10px;"
+        :loading="state.loading"
+        :columns="columns"
+        :data-source="state.shadowList"
+      >
+
+      </a-table>
   </a-card>
 </template>
 
 <script lang="ts" setup >
 import { ReloadIconTsx } from '@/components/MicroComponents/index'
+import { DeviceContriller } from '@/controller'
 import { onMounted, reactive } from 'vue'
+import { useRoute } from 'vue-router'
+
+const columns = [
+  {
+    title: '数据类型',
+    dataIndex: 'dataType'
+  },
+  {
+    title: '单位',
+    dataIndex: 'dataUnit'
+  },
+  {
+    title: 'keyLabel',
+    dataIndex: 'keyLabel'
+  },
+  {
+    title: '字符值',
+    dataIndex: 'stringValue'
+  }
+]
+
+const route = useRoute()
+const deviceId = route.query.id as string
 
 const state = reactive({
-  loading: false
+  loading: false,
+  shadowList: []
 })
 
-const getDeviceShadow = () => {
-  // state.loading = true
-  console.log('getDeviceShadow')
+// booleanValue
+// :
+// null
+// dataType
+// :
+// "STRING"
+// dataUnit
+// :
+// null
+// deviceId
+// :
+// "1"
+// doubleValue
+// :
+// null
+// jsonValue
+// :
+// null
+// key
+// :
+// "1"
+// keyLabel
+// :
+// "1"
+// longValue
+// :
+// null
+// scope
+// :
+// null
+// stringValue
+// :
+// "1"
+// ts
+// :
+// 1681181164001
+
+const getDeviceShadow = async () => {
+  state.loading = true
+  const { data } = await DeviceContriller.getDevicShadow(deviceId)
+  state.loading = false
+  state.shadowList = data
 }
 
 onMounted(() => {

+ 2 - 0
src/pages/Iot/device/components/deviceTag.vue

@@ -4,9 +4,11 @@
         <a-col><a-button @click="state.visible = true" >绑定标签</a-button></a-col>
       </a-row>
       <a-table
+        style="margin-top: 20px;"
         :columns="columns"
         :data-source="state.dataSource"
         :loading="state.loading"
+        :pagination="false"
       >
       <template #bodyCell="{column, record}">
         <template v-if="column.key === 'action'" >

+ 6 - 3
src/pages/Iot/device/components/msgTrack.vue

@@ -6,8 +6,11 @@
 
     <AlertTsx style="margin-top: 20px;"  >
       <template #valueSlot>
-        执行情况[ 中止 ]
-        结束时间: {{state.formatStartTime}}
+        <div v-if="state.formatStartTime" >
+          <!-- 执行情况[ 中止 ] -->
+          结束时间: {{state.formatStartTime}}
+        </div>
+
       </template>
       <template #operaSlot>
         <a-space>
@@ -103,7 +106,7 @@ const getEventList = async () => {
 
 const getEventTraceList = async () => {
   const data = await EventController.listTrace({ deviceId: deviceId }) as unknown as Number
-  state.formatStartTime = dayjs(data).format('YYYY-MM-DD hh:mm:ss')
+  state.formatStartTime = data ? dayjs(data).format('YYYY-MM-DD hh:mm:ss') : ''
   getEventList()
 }
 

+ 20 - 8
src/pages/Iot/device/components/overview.vue

@@ -28,15 +28,15 @@
 
     <a-row  justify="space-between" align="middle" style="width: 100%;height: 68px; margin-bottom: 10px;" class="title">
       <a-col>最新上报数据</a-col>
-      <a-col><a-button type="primary" >查看全部属性</a-button></a-col>
+      <!-- <a-col><a-button type="primary" >查看全部属性</a-button></a-col> -->
     </a-row>
 
     <a-row :gutter="[8, 8]" >
-      <a-col :lg="8" :md="8" :sm="24"  v-for="item in 4" :key="item" >
+      <a-col :lg="8" :md="8" :sm="24"  v-for="(item, index) in state.liveDataSource" :key="item.ts" >
         <div class="data">
-          <div>NH3</div>
-          <div> {{`<test>`}}</div>
-          <div>2023/04/26 22:05:03</div>
+          <div>{{item.keyLabel}}</div>
+          <div> {{`<${item.key}>`}}</div>
+          <div>{{dayjs(item.ts).format('YYYY/MM/DD HH:MM:ss')}}</div>
         </div>
       </a-col>
     </a-row>
@@ -53,9 +53,10 @@
 
 <script lang="ts" setup >
 import { DeviceContriller } from '@/controller'
-import { StateEffect } from '@codemirror/state'
+import { useScheduler } from '@/hooks'
 import { onMounted, reactive } from 'vue'
 import { useRoute } from 'vue-router'
+import dayjs from 'dayjs'
 
 const route = useRoute()
 const deviceId = route.query.id as string
@@ -69,11 +70,13 @@ const props = defineProps<IProps>()
 const state = reactive < {
   deviceDetail: IOT.API.DEVICE.Device | null,
   visible: boolean,
-  deviceLabel: string
+  deviceLabel: string,
+  liveDataSource: any[]
 } > ({
   deviceDetail: null,
   visible: false,
-  deviceLabel: ''
+  deviceLabel: '',
+  liveDataSource: []
 })
 
 const ok = async () => {
@@ -83,9 +86,18 @@ const ok = async () => {
   state.deviceLabel = state.deviceDetail.deviceLabel
 }
 
+const getLiveData = async () => {
+  const { data } = await DeviceContriller.getLiveData(deviceId)
+  console.log(data)
+  state.liveDataSource = data
+}
+
+const { start } = useScheduler(getLiveData, 1000)
+
 onMounted(async () => {
   state.deviceDetail = await DeviceContriller.byId(props.deviceId)
   state.deviceLabel = state.deviceDetail.deviceLabel
+  start()
 })
 
 </script>

+ 11 - 3
src/pages/Iot/device/components/subDevice.vue

@@ -36,8 +36,10 @@
 
       <a-table
         :columns="columns"
+        :loading="state.loading"
         :dataSource="state.dataSource"
         :pagination="state.queryParams"
+        @change="changePage"
       >
       <template #bodyCell="{ column, record }">
         <template v-if="column.key === 'deviceStatus'">
@@ -177,9 +179,14 @@ const subDeviceState = reactive({
 
 const ok = async () => {
   const submitState = await formProo.value.onSubmit()
-  console.log('submitState:', submitState)
-  DeviceContriller.postSub({ ...submitState, gatewayId: deviceId })
+  await DeviceContriller.postSub({ ...submitState, gatewayId: deviceId })
   state.visible = false
+  getSubDevicePage()
+}
+
+const changePage = ({ current }) => {
+  state.queryParams.page = current
+  getSubDevicePage()
 }
 
 const goDeviceDetailPage = (id: string) => {
@@ -188,7 +195,8 @@ const goDeviceDetailPage = (id: string) => {
 }
 
 const delSubDevice = async (id: string) => {
-  // const {} = await DeviceContriller
+  await DeviceContriller.delSub(id)
+  getSubDevicePage()
 }
 const getSubDevicePage = async () => {
   state.loading = true

+ 3 - 14
src/pages/Iot/device/detail.vue

@@ -17,7 +17,7 @@
 </template>
 
 <script lang="ts" setup >
-import { computed, reactive } from 'vue'
+import { computed, onMounted, reactive } from 'vue'
 import OverView from './components/overview.vue'
 import CloudView from './components/cloudview.vue'
 import DeviceShadow from './components/deviceShadow.vue'
@@ -25,6 +25,8 @@ import MsgTrack from './components/msgTrack.vue'
 import SubDevice from './components/subDevice.vue'
 import DeviceTag from './components/deviceTag.vue'
 import { useRoute } from 'vue-router'
+import { DeviceContriller } from '@/controller'
+import { StateEffect } from '@codemirror/state'
 
 const tabs = [
   {
@@ -66,19 +68,6 @@ const goDetail = ({ id }) => {
   state.deviceId = id
 }
 
-const domainCom = () => {
-  switch (state.tabsActive) {
-    case '1':
-      return OverView
-
-    case '2':
-
-      return CloudView
-    default:
-      return CloudView
-  }
-}
-
 </script>
 
 <style lang="less" scoped >

+ 11 - 4
src/pages/Iot/device/group.vue

@@ -69,10 +69,12 @@
           <a-col><ReloadIconTsx  :loading="state.loading" :reload="getDevicePage"/></a-col>
         </a-row>
           <a-table
-          style="margin-top: 20px;"
-          :loading="state.loading"
-          :columns="columns"
-          :data-source="state.groupDateSource"
+            style="margin-top: 20px;"
+            :loading="state.loading"
+            :columns="columns"
+            :data-source="state.groupDateSource"
+            :pagination="state.queryParams"
+            @change="changePage"
         >
         <template #bodyCell="{column, record}">
           <template v-if="column.key === 'deviceStatus'" >
@@ -414,6 +416,11 @@ const { resetFields, validate, validateInfos } = useForm(groupState, reactive({
   groupLabel: [{ required: true, message: '请填写分组名称' }]
 }))
 
+const changePage = ({ current }) => {
+  state.queryParams.page = current
+  getDevicePage()
+}
+
 const delBind = async (ids: []) => {
   const params = (ids.length ? ids : state.selectedRowKeys).map(item => {
     return {

+ 7 - 0
src/pages/Iot/device/index.vue

@@ -46,6 +46,8 @@
       :columns="columns"
       :data-source="state.dataSource"
       :loading="state.loading"
+      :pagination="searchState"
+      @change="changePage"
     >
       <template #bodyCell="{column, record}">
         <template v-if="column.key === 'deviceStatus'" >
@@ -213,6 +215,11 @@ const { resetFields: resetFieldsDevice, validate: validateDevice, validateInfos:
   deviceCode: [{ required: true, message: '请填写设备码' }]
 }))
 
+const changePage = ({ current }) => {
+  searchState.page = current
+  getDevicePage()
+}
+
 const goDebugPage = (id: string) => {
   router.push({ path: '/devOps/onlineTest', query: { id } })
 }

+ 77 - 0
src/pages/Iot/model/components/topic.vue

@@ -0,0 +1,77 @@
+<template>
+<a-card>
+  <a-table :columns="columns" :data-source="state.dataSource">
+    <!-- <template #bodyCell="{ column, text }">
+      <template v-if="column.dataIndex === 'name'">
+        <a href="javascript:;">{{ text }}</a>
+      </template>
+    </template> -->
+  </a-table>
+</a-card>
+</template>
+<script lang='ts' setup >
+import { ModelController } from '@/controller'
+import { reactive, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+
+const queryParams = useRoute().query as {id: string}
+
+const columns = [
+
+  {
+    title: 'topic分类',
+    dataIndex: 'cate',
+    customCell: (_, index, column) => {
+      if (index === 1) {
+        return { rowSpan: 2 }
+      } else if (index === 2) {
+        return { rowSpan: 0 }
+      }
+    }
+  },
+  {
+    title: 'topic',
+    dataIndex: 'topic'
+  },
+  {
+    title: '用途',
+    dataIndex: 'use'
+  }
+]
+
+const data = {
+  MQTT: [
+    { cate: '属性', topic: 'api/{deviceId}/attributes', use: '设备发送属性数据到服务端' },
+    { cate: '命令', topic: '/sub/command ', use: '设备订阅命令' },
+    { cate: '命令', topic: '/sub/command/ ', use: '服务端发送命令消息到设备' },
+    { cate: '消息', topic: 'api/{deviceId}/msg', use: '设备发布消息到服务端' },
+    { cate: '消息', topic: '/sub/msg', use: '服务端发送消息到设备' }
+  ],
+  HTTP: [
+    { cate: '属性', topic: 'api/device/{deviceId}/attributes', use: '设备发送属性数据到服务端' }
+  ],
+  COAP: [
+    { cate: '属性', topic: 'api/{deviceId}/attributes', use: '设备发送属性数据到服务端' }
+  ]
+}
+
+const state = reactive({
+  transportType: '',
+  dataSource: []
+})
+
+const getModelById = async (id: string) => {
+  const { transportType } = await ModelController.detail(id)
+  state.transportType = transportType
+
+  state.dataSource = data[transportType]
+  console.log(state.dataSource)
+}
+
+onMounted(() => {
+  getModelById(queryParams.id)
+})
+
+</script>
+<style lang='less' scoped >
+</style>

+ 6 - 3
src/pages/Iot/model/detail.vue

@@ -22,11 +22,13 @@
   <a-card style="margin-top: 20px;" >
     <a-tabs v-model:activeKey="state.activeKey" @change="changeTabs">
       <a-tab-pane  :key="item.key" :tab="item.label" v-for="item in tabsdata">
+
+      </a-tab-pane>
+    </a-tabs>
         <ModelDefine v-if="state.activeKey === 0"/>
         <OnlineTest v-else-if="state.activeKey === 2" />
         <Plugins v-else-if="state.activeKey === 1" />
-      </a-tab-pane>
-    </a-tabs>
+        <Topic v-else-if="state.activeKey === 3" />
   </a-card>
 </template>
 
@@ -37,6 +39,7 @@ import { useRoute } from 'vue-router'
 import ModelDefine from './components/modelDefine.vue'
 import OnlineTest from './components/onlineTest.vue'
 import Plugins from './components/plugins.vue'
+import Topic from './components/topic.vue'
 
 const queryParams = useRoute().query as {id: string}
 
@@ -45,7 +48,7 @@ const state = reactive<{
   activeKey: number
 }>({
   model: null,
-  activeKey: 1
+  activeKey: 0
 })
 
 const tabsdata = [

+ 7 - 0
src/pages/Iot/model/index.vue

@@ -27,6 +27,7 @@
       :dataSource="state.dataSource"
       :pagination="state.queryParams"
       :loading="state.loading"
+      @change="change"
     >
       <template #bodyCell="{ column, record }">
           <template v-if="column.key === 'action'">
@@ -161,6 +162,12 @@ const rulesRef = reactive({
 
 const { resetFields, validate, validateInfos } = useForm(modelRef, rulesRef)
 
+const change = ({ current }) => {
+  state.queryParams.page = current
+  getModel()
+  console.log('page:', current)
+}
+
 const ok = () => {
   validate().then(async () => {
     await opraModel()

+ 7 - 4
src/pages/Iot/rule/forwardRule.vue

@@ -21,7 +21,8 @@
     :columns="columns"
     :data-source="state.dataSource"
     :loading="state.loading"
-    :pagenation="{}"
+    :pagenation="queryParams"
+    @change="changePage"
   >
     <template #bodyCell="{column, record}" >
       <template v-if="column.key === 'subjectResource'" >
@@ -288,9 +289,6 @@ import { RuleController } from '@/controller/index'
 import type { FormItemProps } from '@/components/FormPro/index.vue'
 import { Form, Empty } from 'ant-design-vue'
 import { computed } from '@vue/reactivity'
-import { StateEffect } from '@codemirror/state'
-import { object } from 'vue-types'
-
 const columns = [
   {
     title: '规则名称',
@@ -539,6 +537,11 @@ const forwardRuleStateComputed = computed(() => forwardState.targetType === '' ?
 
 const { resetFields, validate, validateInfos } = useForm(forwardState, forwardRuleStateComputed)
 
+const changePage = ({ current }) => {
+  queryParams.page = current
+  getForwardList()
+}
+
 const openModal = async (opraState: 'add' | 'update', record) => {
   state.opraState = opraState
 

+ 7 - 0
src/pages/Iot/rule/linkRule.vue

@@ -15,6 +15,8 @@
       :columns="columns"
       :data-source="state.dataSource"
       :loading="state.loading"
+      :pagenation="queryParamsState"
+      @change="changePage"
     >
     <template #bodyCell="{column, record}" >
       <template v-if="column.key === 'status'" >
@@ -457,6 +459,11 @@ const state = reactive({
   opraKey: ''
 })
 
+const changePage = ({ current }) => {
+  queryParamsState.page = current
+  getLinkPage()
+}
+
 const changeConditionType = () => {
   console.log('changeConditionType')
 }