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

pull/790/head
YunaiV 2025-06-18 23:39:24 +08:00
parent c255963b31
commit 14fcb00530
3 changed files with 73 additions and 83 deletions

View File

@ -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

View File

@ -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
}
/** 监听自动刷新 */

View File

@ -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<DeviceHistoryDataVO[]>([]) //
//
const sortedList = 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)
}
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() //
// Echarts
const echartsData = computed(() => {
if (!list.value || list.value.length === 0) return []
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>