feat:【IoT 物联网】完善“设备属性(运行状态)”的查看数据
This commit is contained in:
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 监听自动刷新 */
|
/** 监听自动刷新 */
|
||||||
|
|||||||
@ -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>
|
||||||
Reference in New Issue
Block a user