Просмотр исходного кода

feat:【IoT 物联网】增加“设备事件上报”

YunaiV 9 месяцев назад
Родитель
Сommit
1975e46cb2

+ 5 - 0
src/api/iot/device/device/index.ts

@@ -159,6 +159,11 @@ export const DeviceApi = {
     return await request.get({ url: `/iot/device/message/page`, params })
   },
 
+  // 查询设备消息配对分页
+  getDeviceMessagePairPage: async (params: any) => {
+    return await request.get({ url: `/iot/device/message/pair-page`, params })
+  },
+
   // 发送设备消息
   sendDeviceMessage: async (params: IotDeviceMessageSendReqVO) => {
     return await request.post({ url: `/iot/device/message/send`, data: params })

+ 1 - 0
src/views/iot/device/device/detail/DeviceDetailsSimulator.vue

@@ -171,6 +171,7 @@ const dataTypeOptionsLabel = computed(() => (value: string) => getDataTypeOption
 
 /** 查询物模型列表 */
 // TODO @super:getThingModelList 更精准
+// TODO @haohao:目前 index.vue 已经有了 thingModels,可以考虑服用下;
 const getList = async () => {
   loading.value = true
   try {

+ 13 - 8
src/views/iot/device/device/detail/DeviceDetailsThingModel.vue

@@ -3,25 +3,30 @@
   <ContentWrap>
     <el-tabs v-model="activeTab">
       <el-tab-pane label="设备属性(运行状态)" name="property">
-        <DeviceDetailsThingModelProperty :product="product" :device="device" />
+        <DeviceDetailsThingModelProperty :device-id="deviceId" />
       </el-tab-pane>
-      <el-tab-pane label="设备事件" name="event">
-        <DeviceDetailsThingModelEvent :product="product" :device="device" />
+      <el-tab-pane label="设备事件上报" name="event">
+        <DeviceDetailsThingModelEvent
+          :device-id="props.deviceId"
+          :thing-model-list="props.thingModelList"
+        />
       </el-tab-pane>
-      <el-tab-pane label="服务调用" name="service">
-        <DeviceDetailsThingModelService :product="product" :device="device" />
+      <el-tab-pane label="设备服务调用" name="service">
+        <DeviceDetailsThingModelService :device-id="deviceId" />
       </el-tab-pane>
     </el-tabs>
   </ContentWrap>
 </template>
 <script setup lang="ts">
-import { ProductVO } from '@/api/iot/product/product'
-import { DeviceVO } from '@/api/iot/device/device'
+import { ThingModelData } from '@/api/iot/thingmodel'
 import DeviceDetailsThingModelProperty from './DeviceDetailsThingModelProperty.vue'
 import DeviceDetailsThingModelEvent from './DeviceDetailsThingModelEvent.vue'
 import DeviceDetailsThingModelService from './DeviceDetailsThingModelService.vue'
 
-const props = defineProps<{ product: ProductVO; device: DeviceVO }>()
+const props = defineProps<{
+  deviceId: number
+  thingModelList: ThingModelData[]
+}>()
 
 const activeTab = ref('property') // 默认选中设备属性
 </script>

+ 180 - 7
src/views/iot/device/device/detail/DeviceDetailsThingModelEvent.vue

@@ -1,16 +1,189 @@
 <!-- 设备事件管理 -->
 <template>
   <ContentWrap>
-    <div class="text-center p-4">
-      <el-empty description="设备事件管理功能开发中..." />
-    </div>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="80px"
+      @submit.prevent
+    >
+      <el-form-item label="标识符" prop="identifier">
+        <el-select
+          v-model="queryParams.identifier"
+          placeholder="请选择事件标识符"
+          clearable
+          class="!w-240px"
+        >
+          <el-option
+            v-for="event in eventThingModels"
+            :key="event.identifier"
+            :label="`${event.name}(${event.identifier})`"
+            :value="event.identifier!"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="时间范围" prop="times">
+        <el-date-picker
+          v-model="queryParams.times"
+          type="datetimerange"
+          range-separator="至"
+          start-placeholder="开始时间"
+          end-placeholder="结束时间"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
+          class="!w-360px"
+          :shortcuts="defaultShortcuts"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px" />
+          重置
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <ContentWrap>
+    <!-- 事件列表 -->
+    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+      <el-table-column label="时间" align="center" prop="reportTime" width="180px">
+        <template #default="scope">
+          {{
+            scope.row.request?.reportTime ? formatDate(new Date(scope.row.request.reportTime)) : '-'
+          }}
+        </template>
+      </el-table-column>
+      <el-table-column label="标识符" align="center" prop="identifier" width="120px">
+        <template #default="scope">
+          <el-tag type="primary" size="small">
+            {{ scope.row.request?.identifier }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="事件名称" align="center" prop="eventName" width="150px">
+        <template #default="scope">
+          {{ getEventName(scope.row.request?.identifier) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="事件类型" align="center" prop="eventType" width="100px">
+        <template #default="scope">
+          {{ getEventType(scope.row.request?.identifier) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="输入参数" align="center" prop="params">
+        <template #default="scope"> {{ parseParams(scope.row.request.params) }} </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
   </ContentWrap>
 </template>
+
 <script setup lang="ts">
-import { ProductVO } from '@/api/iot/product/product'
-import { DeviceVO } from '@/api/iot/device/device'
+import { DeviceApi } from '@/api/iot/device/device'
+import { ThingModelData } from '@/api/iot/thingmodel'
+import { formatDate, defaultShortcuts } from '@/utils/formatTime'
+import { IotDeviceMessageMethodEnum } from '@/views/iot/utils/constants'
+import { ThingModelType, getEventTypeByValue } from '@/views/iot/thingmodel/config'
+
+const props = defineProps<{
+  deviceId: number
+  thingModelList: ThingModelData[]
+}>()
+
+const loading = ref(false) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([] as any[]) // 列表的数据
+const queryParams = reactive({
+  deviceId: props.deviceId,
+  method: IotDeviceMessageMethodEnum.EVENT_POST.method, // 固定筛选事件消息
+  identifier: '',
+  times: [] as any[],
+  pageNo: 1,
+  pageSize: 10
+})
+const queryFormRef = ref() // 搜索的表单
+
+/** 事件类型的物模型数据 */
+const eventThingModels = computed(() => {
+  return props.thingModelList.filter((item: ThingModelData) => item.type === ThingModelType.EVENT)
+})
+
+/** 查询列表 */
+const getList = async () => {
+  if (!props.deviceId) return
+  loading.value = true
+  try {
+    const data = await DeviceApi.getDeviceMessagePairPage(queryParams)
+    list.value = data.list
+    total.value = data.length
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields()
+  queryParams.identifier = ''
+  queryParams.times = []
+  handleQuery()
+}
+
+/** 获取事件名称 */
+const getEventName = (identifier: string | undefined) => {
+  if (!identifier) return '-'
+  const event = eventThingModels.value.find(
+    (item: ThingModelData) => item.identifier === identifier
+  )
+  return event?.name || identifier
+}
+
+/** 获取事件类型 */
+const getEventType = (identifier: string | undefined) => {
+  if (!identifier) return '-'
+  const event = eventThingModels.value.find(
+    (item: ThingModelData) => item.identifier === identifier
+  )
+  if (!event?.event?.type) return '-'
+  return getEventTypeByValue(event.event.type) || '-'
+}
 
-const props = defineProps<{ product: ProductVO; device: DeviceVO }>()
+/** 解析参数 */
+const parseParams = (params: string) => {
+  try {
+    const parsed = JSON.parse(params)
+    if (parsed.params) {
+      return parsed.params
+    }
+    return parsed
+  } catch (error) {
+    return {}
+  }
+}
 
-// TODO: 实现设备事件管理功能
+/** 初始化 */
+onMounted(() => {
+  getList()
+})
 </script>

+ 6 - 11
src/views/iot/device/device/detail/DeviceDetailsThingModelProperty.vue

@@ -88,7 +88,7 @@
                 <!-- 数据图标 - 可点击 -->
                 <div
                   class="cursor-pointer flex items-center justify-center w-8 h-8 rounded-full hover:bg-blue-50 transition-colors"
-                  @click="openHistory(props.device.id, item.identifier)"
+                  @click="openHistory(props.deviceId, item.identifier)"
                 >
                   <Icon icon="ep:data-line" class="text-[18px] text-[#0070ff]" />
                 </div>
@@ -134,11 +134,7 @@
       />
       <el-table-column label="操作" align="center">
         <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openHistory(props.device.id, scope.row.identifier)"
-          >
+          <el-button link type="primary" @click="openHistory(props.deviceId, scope.row.identifier)">
             查看数据
           </el-button>
         </template>
@@ -146,16 +142,15 @@
     </el-table>
 
     <!-- 表单弹窗:添加/修改 -->
-    <DeviceDetailsThingModelPropertyHistory ref="historyRef" :device="device" :product="product" />
+    <DeviceDetailsThingModelPropertyHistory ref="historyRef" :deviceId="props.deviceId" />
   </ContentWrap>
 </template>
 <script setup lang="ts">
-import { ProductVO } from '@/api/iot/product/product'
-import { DeviceApi, IotDevicePropertyDetailRespVO, DeviceVO } from '@/api/iot/device/device'
+import { DeviceApi, IotDevicePropertyDetailRespVO } from '@/api/iot/device/device'
 import { dateFormatter, formatDate } from '@/utils/formatTime'
 import DeviceDetailsThingModelPropertyHistory from './DeviceDetailsThingModelPropertyHistory.vue'
 
-const props = defineProps<{ product: ProductVO; device: DeviceVO }>()
+const props = defineProps<{ deviceId: number }>()
 
 const loading = ref(true) // 列表的加载中
 const list = ref<IotDevicePropertyDetailRespVO[]>([]) // 显示的列表数据
@@ -174,7 +169,7 @@ const getList = async () => {
   loading.value = true
   try {
     const params = {
-      deviceId: props.device.id,
+      deviceId: props.deviceId,
       identifier: undefined as string | undefined,
       name: undefined as string | undefined
     }

+ 2 - 3
src/views/iot/device/device/detail/DeviceDetailsThingModelPropertyHistory.vue

@@ -64,12 +64,11 @@
   </Dialog>
 </template>
 <script setup lang="ts">
-import { DeviceApi, IotDevicePropertyRespVO, DeviceVO } from '@/api/iot/device/device'
-import { ProductVO } from '@/api/iot/product/product'
+import { DeviceApi, IotDevicePropertyRespVO } from '@/api/iot/device/device'
 import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
 import { Echart } from '@/components/Echart'
 
-defineProps<{ product: ProductVO; device: DeviceVO }>()
+defineProps<{ deviceId: number }>()
 
 /** IoT 设备属性历史数据详情 */
 defineOptions({ name: 'DeviceDetailsThingModelPropertyHistory' })

+ 1 - 4
src/views/iot/device/device/detail/DeviceDetailsThingModelService.vue

@@ -7,10 +7,7 @@
   </ContentWrap>
 </template>
 <script setup lang="ts">
-import { ProductVO } from '@/api/iot/product/product'
-import { DeviceVO } from '@/api/iot/device/device'
-
-const props = defineProps<{ product: ProductVO; device: DeviceVO }>()
+const props = defineProps<{ deviceId: number }>()
 
 // TODO: 实现设备服务调用功能
 </script>

+ 23 - 3
src/views/iot/device/device/detail/index.vue

@@ -3,7 +3,7 @@
     :loading="loading"
     :product="product"
     :device="device"
-    @refresh="getDeviceData(id)"
+    @refresh="getDeviceData"
   />
   <el-col>
     <el-tabs v-model="activeTab">
@@ -11,7 +11,11 @@
         <DeviceDetailsInfo v-if="activeTab === 'info'" :product="product" :device="device" />
       </el-tab-pane>
       <el-tab-pane label="物模型数据" name="model">
-        <DeviceDetailsThingModel v-if="activeTab === 'model'" :product="product" :device="device" />
+        <DeviceDetailsThingModel
+          v-if="activeTab === 'model'"
+          :device-id="device.id"
+          :thing-model-list="thingModelList"
+        />
       </el-tab-pane>
       <el-tab-pane label="子设备管理" v-if="product.deviceType === DeviceTypeEnum.GATEWAY" />
       <el-tab-pane label="设备消息" name="log">
@@ -38,6 +42,7 @@
 import { useTagsViewStore } from '@/store/modules/tagsView'
 import { DeviceApi, DeviceVO } from '@/api/iot/device/device'
 import { DeviceTypeEnum, ProductApi, ProductVO } from '@/api/iot/product/product'
+import { ThingModelApi, ThingModelData } from '@/api/iot/thingmodel'
 import DeviceDetailsHeader from './DeviceDetailsHeader.vue'
 import DeviceDetailsInfo from './DeviceDetailsInfo.vue'
 import DeviceDetailsThingModel from './DeviceDetailsThingModel.vue'
@@ -49,11 +54,12 @@ defineOptions({ name: 'IoTDeviceDetail' })
 
 const route = useRoute()
 const message = useMessage()
-const id = route.params.id // 将字符串转换为数字
+const id = Number(route.params.id) // 将字符串转换为数字
 const loading = ref(true) // 加载中
 const product = ref<ProductVO>({} as ProductVO) // 产品详情
 const device = ref<DeviceVO>({} as DeviceVO) // 设备详情
 const activeTab = ref('info') // 默认激活的标签页
+const thingModelList = ref<ThingModelData[]>([]) // 物模型列表数据
 
 /** 获取设备详情 */
 const getDeviceData = async () => {
@@ -61,6 +67,7 @@ const getDeviceData = async () => {
   try {
     device.value = await DeviceApi.getDevice(id)
     await getProductData(device.value.productId)
+    await getThingModelList(device.value.productId)
   } finally {
     loading.value = false
   }
@@ -71,6 +78,19 @@ const getProductData = async (id: number) => {
   product.value = await ProductApi.getProduct(id)
 }
 
+/** 获取物模型列表 */
+const getThingModelList = async (productId: number) => {
+  try {
+    const data = await ThingModelApi.getThingModelList({
+      productId: productId
+    })
+    thingModelList.value = data || []
+  } catch (error) {
+    console.error('获取物模型列表失败:', error)
+    thingModelList.value = []
+  }
+}
+
 /** 初始化 */
 const { delView } = useTagsViewStore() // 视图操作
 const router = useRouter() // 路由