Bladeren bron

feat:【IoT 物联网】完善“设备属性(运行状态)”的查看数据

YunaiV 10 maanden geleden
bovenliggende
commit
14fcb00530

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

@@ -41,10 +41,11 @@ export interface IotDevicePropertyDetailRespVO {
   dataSpecsList: any[] // 数据定义列表
 }
 
-// IoT 设备数据 VO
-export interface DeviceHistoryDataVO {
-  time: number // 时间
-  data: string // 数据
+// IoT 设备属性 VO
+export interface IotDevicePropertyRespVO {
+  identifier: string // 属性标识符
+  value: string // 最新值
+  updateTime: Date // 更新时间
 }
 
 // TODO @芋艿:调整到 constants

+ 27 - 10
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="openDetail(props.device.id, item.identifier)"
+                  @click="openHistory(props.device.id, item.identifier)"
                 >
                   <Icon icon="ep:data-line" class="text-[18px] text-[#0070ff]" />
                 </div>
@@ -99,7 +99,7 @@
                 <div class="mb-2.5 last:mb-0">
                   <span class="text-[#717c8e] mr-2.5">属性值</span>
                   <span class="text-[#0b1d30] font-600">
-                    {{ item.value || '-' }}
+                    {{ formatValueWithUnit(item) }}
                   </span>
                 </div>
                 <div class="mb-2.5 last:mb-0">
@@ -120,7 +120,11 @@
       <el-table-column label="属性标识符" align="center" prop="identifier" />
       <el-table-column label="属性名称" align="center" prop="name" />
       <el-table-column label="数据类型" align="center" prop="dataType" />
-      <el-table-column label="属性值" align="center" prop="value" />
+      <el-table-column label="属性值" align="center" prop="value">
+        <template #default="scope">
+          {{ formatValueWithUnit(scope.row) }}
+        </template>
+      </el-table-column>
       <el-table-column
         label="更新时间"
         align="center"
@@ -130,7 +134,11 @@
       />
       <el-table-column label="操作" align="center">
         <template #default="scope">
-          <el-button link type="primary" @click="openDetail(props.device.id, scope.row.identifier)">
+          <el-button
+            link
+            type="primary"
+            @click="openHistory(props.device.id, scope.row.identifier)"
+          >
             查看数据
           </el-button>
         </template>
@@ -138,14 +146,14 @@
     </el-table>
 
     <!-- 表单弹窗:添加/修改 -->
-    <DeviceDataDetail ref="detailRef" :device="device" :product="product" />
+    <DeviceDetailsThingModelPropertyHistory ref="historyRef" :device="device" :product="product" />
   </ContentWrap>
 </template>
 <script setup lang="ts">
 import { ProductVO } from '@/api/iot/product/product'
 import { DeviceApi, IotDevicePropertyDetailRespVO, DeviceVO } from '@/api/iot/device/device'
 import { dateFormatter, formatDate } from '@/utils/formatTime'
-import DeviceDataDetail from './DeviceDataDetail.vue'
+import DeviceDetailsThingModelPropertyHistory from './DeviceDetailsThingModelPropertyHistory.vue'
 
 const props = defineProps<{ product: ProductVO; device: DeviceVO }>()
 
@@ -196,10 +204,19 @@ const handleQuery = () => {
   handleFilter()
 }
 
-/** 添加/修改操作 */
-const detailRef = ref()
-const openDetail = (deviceId: number, identifier: string) => {
-  detailRef.value.open(deviceId, identifier)
+/** 历史操作 */
+const historyRef = ref()
+const openHistory = (deviceId: number, identifier: string) => {
+  historyRef.value.open(deviceId, identifier)
+}
+
+/** 格式化属性值和单位 */
+const formatValueWithUnit = (item: IotDevicePropertyDetailRespVO) => {
+  if (item.value === null || item.value === undefined || item.value === '') {
+    return '-'
+  }
+  const unitName = item.dataSpecs?.unitName
+  return unitName ? `${item.value} ${unitName}` : item.value
 }
 
 /** 监听自动刷新 */

+ 40 - 68
src/views/iot/device/device/detail/DeviceDataDetail.vue → src/views/iot/device/device/detail/DeviceDetailsThingModelPropertyHistory.vue

@@ -1,6 +1,6 @@
 <!-- 设备物模型 -> 运行状态 -> 查看数据(设备的属性值历史)-->
 <template>
-  <Dialog title="查看数据" v-model="dialogVisible" width="1024px">
+  <Dialog title="查看数据" v-model="dialogVisible" width="1024px" :appendToBody="true">
     <ContentWrap>
       <!-- 搜索工作栏 -->
       <el-form
@@ -19,6 +19,7 @@
             end-placeholder="结束日期"
             class="!w-360px"
             @change="handleTimeChange"
+            :shortcuts="defaultShortcuts"
           />
         </el-form-item>
         <el-form-item class="float-right !mr-0 !mb-0">
@@ -44,18 +45,13 @@
     <ContentWrap>
       <!-- 图表模式 -->
       <div v-if="viewMode === 'chart'" class="chart-container">
-        <div v-if="sortedList.length === 0" class="text-center text-gray-500 py-20"> 暂无数据 </div>
-        <Echart v-else :options="echartsOption" height="400px" />
+        <div v-if="list.length === 0" class="text-center text-gray-500 py-20"> 暂无数据 </div>
+        <Echart v-else :key="'erchart' + Date.now()" :options="echartsOption" height="400px" />
       </div>
 
       <!-- 表格模式 -->
       <div v-else>
-        <el-table
-          v-loading="detailLoading"
-          :data="sortedList"
-          :stripe="true"
-          :show-overflow-tooltip="true"
-        >
+        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
           <el-table-column label="时间" align="center" prop="time" width="180px">
             <template #default="scope">
               {{ formatDate(new Date(scope.row.updateTime)) }}
@@ -68,41 +64,41 @@
   </Dialog>
 </template>
 <script setup lang="ts">
-import { DeviceApi, DeviceHistoryDataVO, DeviceVO } from '@/api/iot/device/device'
+import { DeviceApi, IotDevicePropertyRespVO, DeviceVO } from '@/api/iot/device/device'
 import { ProductVO } from '@/api/iot/product/product'
-import { beginOfDay, endOfDay, formatDate } from '@/utils/formatTime'
+import { beginOfDay, defaultShortcuts, endOfDay, formatDate } from '@/utils/formatTime'
 import { Echart } from '@/components/Echart'
 
 defineProps<{ product: ProductVO; device: DeviceVO }>()
 
-/** IoT 设备数据详情 */
-defineOptions({ name: 'IoTDeviceDataDetail' })
+/** IoT 设备属性历史数据详情 */
+defineOptions({ name: 'DeviceDetailsThingModelPropertyHistory' })
 
 const dialogVisible = ref(false) // 弹窗的是否展示
-const detailLoading = ref(false)
+const loading = ref(false)
 const viewMode = ref<'chart' | 'list'>('chart') // 视图模式状态
+const list = ref<IotDevicePropertyRespVO[]>([]) // 列表的数据
+const chartKey = ref(0) // 图表重新渲染的key
+const queryParams = reactive({
+  deviceId: -1,
+  identifier: '',
+  times: [
+    // 默认显示最近一周的数据
+    formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
+    formatDate(endOfDay(new Date()))
+  ]
+})
+const queryFormRef = ref() // 搜索的表单
 
-const list = ref<DeviceHistoryDataVO[]>([]) // 列表的数据
-
-// 根据视图模式排序的数据
-const sortedList = computed(() => {
+// Echarts 数据
+const echartsData = computed(() => {
   if (!list.value || list.value.length === 0) return []
-
-  const sortedData = [...list.value]
-
-  if (viewMode.value === 'list') {
-    // 列表模式:按时间倒序(最新的在前)
-    return sortedData.sort((a, b) => b.time - a.time)
-  } else {
-    // 图表模式:按时间升序(最早的在前)
-    return sortedData.sort((a, b) => a.time - b.time)
-  }
+  return list.value.map((item) => [item.updateTime, item.value])
 })
-
 // Echarts 配置
 const echartsOption = reactive<any>({
   title: {
-    text: '设备数据趋势',
+    text: '设备属性值',
     left: 'center'
   },
   grid: {
@@ -157,47 +153,27 @@ const echartsOption = reactive<any>({
   ]
 })
 
-// 更新图表数据
-const updateChartData = () => {
-  if (echartsOption.series && echartsOption.series[0]) {
-    echartsOption.series[0].data = sortedList.value.map((item) => [
-      item.updateTime,
-      parseFloat(item.value) || 0
-    ])
-    debugger
-  }
-}
-const queryParams = reactive({
-  deviceId: -1,
-  identifier: '',
-  times: [
-    // 默认显示最近一周的数据
-    formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
-    formatDate(endOfDay(new Date()))
-  ]
-})
-const queryFormRef = ref() // 搜索的表单
-
 /** 获得设备历史数据 */
 const getList = async () => {
-  detailLoading.value = true
+  loading.value = true
   try {
     const data = await DeviceApi.getHistoryDevicePropertyList(queryParams)
     list.value = data || []
-    // 数据获取后更新图表
-    nextTick(() => {
-      updateChartData()
-    })
+    updateChartData()
   } finally {
-    detailLoading.value = false
+    loading.value = false
   }
 }
 
 /** 打开弹窗 */
-const open = (deviceId: number, identifier: string) => {
+const open = async (deviceId: number, identifier: string) => {
   dialogVisible.value = true
   queryParams.deviceId = deviceId
   queryParams.identifier = identifier
+  // 重置图表key,确保每次打开都能正常渲染
+  chartKey.value = 0
+  // 等待弹窗完全渲染后再获取数据
+  await nextTick()
   getList()
 }
 
@@ -206,16 +182,12 @@ const handleTimeChange = () => {
   getList()
 }
 
-// 监听数据变化,更新图表
-watch(
-  () => sortedList.value,
-  () => {
-    if (viewMode.value === 'chart') {
-      updateChartData()
-    }
-  },
-  { deep: true }
-)
+/** 更新图表数据 */
+const updateChartData = () => {
+  if (echartsOption.series && echartsOption.series[0]) {
+    echartsOption.series[0].data = echartsData.value
+  }
+}
 
 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 </script>