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

This commit is contained in:
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[] // 数据定义列表 dataSpecsList: any[] // 数据定义列表
} }
// IoT 设备数据 VO // IoT 设备属性 VO
export interface DeviceHistoryDataVO { export interface IotDevicePropertyRespVO {
time: number // 时间 identifier: string // 属性标识符
data: string // 数据 value: string // 最新值
updateTime: Date // 更新时间
} }
// TODO @芋艿:调整到 constants // TODO @芋艿:调整到 constants

View File

@ -88,7 +88,7 @@
<!-- 数据图标 - 可点击 --> <!-- 数据图标 - 可点击 -->
<div <div
class="cursor-pointer flex items-center justify-center w-8 h-8 rounded-full hover:bg-blue-50 transition-colors" 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]" /> <Icon icon="ep:data-line" class="text-[18px] text-[#0070ff]" />
</div> </div>
@ -99,7 +99,7 @@
<div class="mb-2.5 last:mb-0"> <div class="mb-2.5 last:mb-0">
<span class="text-[#717c8e] mr-2.5">属性值</span> <span class="text-[#717c8e] mr-2.5">属性值</span>
<span class="text-[#0b1d30] font-600"> <span class="text-[#0b1d30] font-600">
{{ item.value || '-' }} {{ formatValueWithUnit(item) }}
</span> </span>
</div> </div>
<div class="mb-2.5 last:mb-0"> <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="identifier" />
<el-table-column label="属性名称" align="center" prop="name" /> <el-table-column label="属性名称" align="center" prop="name" />
<el-table-column label="数据类型" align="center" prop="dataType" /> <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 <el-table-column
label="更新时间" label="更新时间"
align="center" align="center"
@ -130,7 +134,11 @@
/> />
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="scope"> <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> </el-button>
</template> </template>
@ -138,14 +146,14 @@
</el-table> </el-table>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<DeviceDataDetail ref="detailRef" :device="device" :product="product" /> <DeviceDetailsThingModelPropertyHistory ref="historyRef" :device="device" :product="product" />
</ContentWrap> </ContentWrap>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ProductVO } from '@/api/iot/product/product' import { ProductVO } from '@/api/iot/product/product'
import { DeviceApi, IotDevicePropertyDetailRespVO, DeviceVO } from '@/api/iot/device/device' import { DeviceApi, IotDevicePropertyDetailRespVO, DeviceVO } from '@/api/iot/device/device'
import { dateFormatter, formatDate } from '@/utils/formatTime' import { dateFormatter, formatDate } from '@/utils/formatTime'
import DeviceDataDetail from './DeviceDataDetail.vue' import DeviceDetailsThingModelPropertyHistory from './DeviceDetailsThingModelPropertyHistory.vue'
const props = defineProps<{ product: ProductVO; device: DeviceVO }>() const props = defineProps<{ product: ProductVO; device: DeviceVO }>()
@ -196,10 +204,19 @@ const handleQuery = () => {
handleFilter() handleFilter()
} }
/** 添加/修改操作 */ /** 历史操作 */
const detailRef = ref() const historyRef = ref()
const openDetail = (deviceId: number, identifier: string) => { const openHistory = (deviceId: number, identifier: string) => {
detailRef.value.open(deviceId, identifier) 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> <template>
<Dialog title="查看数据" v-model="dialogVisible" width="1024px"> <Dialog title="查看数据" v-model="dialogVisible" width="1024px" :appendToBody="true">
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form <el-form
@ -19,6 +19,7 @@
end-placeholder="结束日期" end-placeholder="结束日期"
class="!w-360px" class="!w-360px"
@change="handleTimeChange" @change="handleTimeChange"
:shortcuts="defaultShortcuts"
/> />
</el-form-item> </el-form-item>
<el-form-item class="float-right !mr-0 !mb-0"> <el-form-item class="float-right !mr-0 !mb-0">
@ -44,18 +45,13 @@
<ContentWrap> <ContentWrap>
<!-- 图表模式 --> <!-- 图表模式 -->
<div v-if="viewMode === 'chart'" class="chart-container"> <div v-if="viewMode === 'chart'" class="chart-container">
<div v-if="sortedList.length === 0" class="text-center text-gray-500 py-20"> 暂无数据 </div> <div v-if="list.length === 0" class="text-center text-gray-500 py-20"> 暂无数据 </div>
<Echart v-else :options="echartsOption" height="400px" /> <Echart v-else :key="'erchart' + Date.now()" :options="echartsOption" height="400px" />
</div> </div>
<!-- 表格模式 --> <!-- 表格模式 -->
<div v-else> <div v-else>
<el-table <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
v-loading="detailLoading"
:data="sortedList"
:stripe="true"
:show-overflow-tooltip="true"
>
<el-table-column label="时间" align="center" prop="time" width="180px"> <el-table-column label="时间" align="center" prop="time" width="180px">
<template #default="scope"> <template #default="scope">
{{ formatDate(new Date(scope.row.updateTime)) }} {{ formatDate(new Date(scope.row.updateTime)) }}
@ -68,41 +64,41 @@
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <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 { 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' import { Echart } from '@/components/Echart'
defineProps<{ product: ProductVO; device: DeviceVO }>() defineProps<{ product: ProductVO; device: DeviceVO }>()
/** IoT 设备数据详情 */ /** IoT 设备属性历史数据详情 */
defineOptions({ name: 'IoTDeviceDataDetail' }) defineOptions({ name: 'DeviceDetailsThingModelPropertyHistory' })
const dialogVisible = ref(false) // const dialogVisible = ref(false) //
const detailLoading = ref(false) const loading = ref(false)
const viewMode = ref<'chart' | 'list'>('chart') // const viewMode = ref<'chart' | 'list'>('chart') //
const list = ref<IotDevicePropertyRespVO[]>([]) //
const list = ref<DeviceHistoryDataVO[]>([]) // const chartKey = ref(0) // key
const queryParams = reactive({
// deviceId: -1,
const sortedList = computed(() => { identifier: '',
if (!list.value || list.value.length === 0) return [] times: [
//
const sortedData = [...list.value] formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
formatDate(endOfDay(new Date()))
if (viewMode.value === 'list') { ]
//
return sortedData.sort((a, b) => b.time - a.time)
} else {
//
return sortedData.sort((a, b) => a.time - b.time)
}
}) })
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 // Echarts
const echartsOption = reactive<any>({ const echartsOption = reactive<any>({
title: { title: {
text: '设备数据趋势', text: '设备属性值',
left: 'center' left: 'center'
}, },
grid: { 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 () => { const getList = async () => {
detailLoading.value = true loading.value = true
try { try {
const data = await DeviceApi.getHistoryDevicePropertyList(queryParams) const data = await DeviceApi.getHistoryDevicePropertyList(queryParams)
list.value = data || [] list.value = data || []
//
nextTick(() => {
updateChartData() updateChartData()
})
} finally { } finally {
detailLoading.value = false loading.value = false
} }
} }
/** 打开弹窗 */ /** 打开弹窗 */
const open = (deviceId: number, identifier: string) => { const open = async (deviceId: number, identifier: string) => {
dialogVisible.value = true dialogVisible.value = true
queryParams.deviceId = deviceId queryParams.deviceId = deviceId
queryParams.identifier = identifier queryParams.identifier = identifier
// key
chartKey.value = 0
//
await nextTick()
getList() getList()
} }
@ -206,16 +182,12 @@ const handleTimeChange = () => {
getList() getList()
} }
// /** 更新图表数据 */
watch( const updateChartData = () => {
() => sortedList.value, if (echartsOption.series && echartsOption.series[0]) {
() => { echartsOption.series[0].data = echartsData.value
if (viewMode.value === 'chart') {
updateChartData()
} }
}, }
{ deep: true }
)
defineExpose({ open }) // open defineExpose({ open }) // open
</script> </script>