feat: iothome

pull/764/head
alwayssuper 2025-04-16 10:47:29 +08:00
parent 89a8b25f17
commit d246c780e4
6 changed files with 384 additions and 210 deletions

View File

@ -23,6 +23,7 @@ interface TimeValueItem {
/** IoT 消息统计数据类型 */ /** IoT 消息统计数据类型 */
export interface IotStatisticsDeviceMessageSummaryRespVO { export interface IotStatisticsDeviceMessageSummaryRespVO {
statType: number
upstreamCounts: TimeValueItem[] upstreamCounts: TimeValueItem[]
downstreamCounts: TimeValueItem[] downstreamCounts: TimeValueItem[]
} }

View File

@ -1,17 +1,19 @@
<template> <template>
<el-card class="stat-card" shadow="never"> <el-card class="stat-card" shadow="never" :loading="loading">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="flex justify-between items-center mb-1"> <div class="flex justify-between items-center mb-1">
<span class="text-gray-500 text-base font-medium">{{ title }}</span> <span class="text-gray-500 text-base font-medium">{{ title }}</span>
<Icon :icon="icon" class="text-[32px]" :class="iconColor" /> <Icon :icon="icon" :class="`text-[32px] ${iconColor}`" />
</div> </div>
<span class="text-3xl font-bold text-gray-700"> <span class="text-3xl font-bold text-gray-700">
{{ value }} <span v-if="value === -1">--</span>
<span v-else>{{ value }}</span>
</span> </span>
<el-divider class="my-2" /> <el-divider class="my-2" />
<div class="flex justify-between items-center text-gray-400 text-sm"> <div class="flex justify-between items-center text-gray-400 text-sm">
<span>今日新增</span> <span>今日新增</span>
<span class="text-green-500">+{{ todayCount }}</span> <span class="text-green-500" v-if="todayCount !== -1">+{{ todayCount }}</span>
<span v-else>--</span>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -28,6 +30,21 @@ const props = defineProps({
value: propTypes.number.def(0).isRequired, value: propTypes.number.def(0).isRequired,
todayCount: propTypes.number.def(0).isRequired, todayCount: propTypes.number.def(0).isRequired,
icon: propTypes.string.def('').isRequired, icon: propTypes.string.def('').isRequired,
iconColor: propTypes.string.def('') iconColor: propTypes.string.def(''),
loading: {
type: Boolean,
default: false
}
}) })
</script> </script>
<style lang="scss" scoped>
.stat-card {
transition: all 0.3s;
&:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgb(0 0 0 / 8%);
}
}
</style>

View File

@ -1,11 +1,17 @@
<template> <template>
<el-card class="chart-card" shadow="never"> <el-card class="chart-card" shadow="never" :loading="loading">
<template #header> <template #header>
<div class="flex items-center"> <div class="flex items-center">
<span class="text-base font-medium text-gray-600">设备数量统计</span> <span class="text-base font-medium text-gray-600">设备数量统计</span>
</div> </div>
</template> </template>
<div ref="deviceCountChartRef" class="h-[240px]"></div> <div v-if="loading && !hasData" class="h-[240px] flex justify-center items-center">
<el-empty description="加载中..." />
</div>
<div v-else-if="!hasData" class="h-[240px] flex justify-center items-center">
<el-empty description="暂无数据" />
</div>
<div v-else ref="deviceCountChartRef" class="h-[240px]"></div>
</el-card> </el-card>
</template> </template>
@ -16,6 +22,7 @@ import { CanvasRenderer } from 'echarts/renderers'
import { TooltipComponent, LegendComponent } from 'echarts/components' import { TooltipComponent, LegendComponent } from 'echarts/components'
import { LabelLayout } from 'echarts/features' import { LabelLayout } from 'echarts/features'
import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics' import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics'
import type { PropType } from 'vue'
/** 设备数量统计卡片 */ /** 设备数量统计卡片 */
defineOptions({ name: 'DeviceCountCard' }) defineOptions({ name: 'DeviceCountCard' })
@ -24,64 +31,97 @@ const props = defineProps({
statsData: { statsData: {
type: Object as PropType<IotStatisticsSummaryRespVO>, type: Object as PropType<IotStatisticsSummaryRespVO>,
required: true required: true
},
loading: {
type: Boolean,
default: false
} }
}) })
const deviceCountChartRef = ref() const deviceCountChartRef = ref()
//
const hasData = computed(() => {
if (!props.statsData) return false
const categories = Object.entries(props.statsData.productCategoryDeviceCounts || {})
return categories.length > 0 && props.statsData.deviceCount !== -1
})
// //
const initChart = () => { const initChart = () => {
//
if (!hasData.value) return
// DOM
if (!deviceCountChartRef.value) {
console.warn('图表DOM元素不存在')
return
}
echarts.use([TooltipComponent, LegendComponent, PieChart, CanvasRenderer, LabelLayout]) echarts.use([TooltipComponent, LegendComponent, PieChart, CanvasRenderer, LabelLayout])
const chart = echarts.init(deviceCountChartRef.value) try {
chart.setOption({ const chart = echarts.init(deviceCountChartRef.value)
tooltip: { chart.setOption({
trigger: 'item' tooltip: {
}, trigger: 'item'
legend: { },
top: '5%', legend: {
right: '10%', top: '5%',
align: 'left', right: '10%',
orient: 'vertical', align: 'left',
icon: 'circle' orient: 'vertical',
}, icon: 'circle'
series: [ },
{ series: [
name: 'Access From', {
type: 'pie', name: 'Access From',
radius: ['50%', '80%'], type: 'pie',
avoidLabelOverlap: false, radius: ['50%', '80%'],
center: ['30%', '50%'], avoidLabelOverlap: false,
label: { center: ['30%', '50%'],
show: false,
position: 'outside'
},
emphasis: {
label: { label: {
show: true, show: false,
fontSize: 20, position: 'outside'
fontWeight: 'bold' },
} emphasis: {
}, label: {
labelLine: { show: true,
show: false fontSize: 20,
}, fontWeight: 'bold'
data: Object.entries(props.statsData.productCategoryDeviceCounts).map(([name, value]) => ({ }
name, },
value labelLine: {
})) show: false
} },
] data: Object.entries(props.statsData.productCategoryDeviceCounts).map(([name, value]) => ({
}) name,
value
}))
}
]
})
return chart
} catch (error) {
console.error('初始化图表失败:', error)
return null
}
} }
// //
watch(() => props.statsData, () => { watch(() => props.statsData, () => {
initChart() // 使 nextTick DOM
nextTick(() => {
initChart()
})
}, { deep: true }) }, { deep: true })
// //
onMounted(() => { onMounted(() => {
initChart() // 使 nextTick DOM
nextTick(() => {
initChart()
})
}) })
</script> </script>

View File

@ -1,11 +1,17 @@
<template> <template>
<el-card class="chart-card" shadow="never"> <el-card class="chart-card" shadow="never" :loading="loading">
<template #header> <template #header>
<div class="flex items-center"> <div class="flex items-center">
<span class="text-base font-medium text-gray-600">设备状态统计</span> <span class="text-base font-medium text-gray-600">设备状态统计</span>
</div> </div>
</template> </template>
<el-row class="h-[240px]"> <div v-if="loading && !hasData" class="h-[240px] flex justify-center items-center">
<el-empty description="加载中..." />
</div>
<div v-else-if="!hasData" class="h-[240px] flex justify-center items-center">
<el-empty description="暂无数据" />
</div>
<el-row v-else class="h-[240px]">
<el-col :span="8" class="flex flex-col items-center"> <el-col :span="8" class="flex flex-col items-center">
<div ref="deviceOnlineCountChartRef" class="h-[160px] w-full"></div> <div ref="deviceOnlineCountChartRef" class="h-[160px] w-full"></div>
<div class="text-center mt-2"> <div class="text-center mt-2">
@ -33,6 +39,7 @@ import * as echarts from 'echarts/core'
import { GaugeChart } from 'echarts/charts' import { GaugeChart } from 'echarts/charts'
import { CanvasRenderer } from 'echarts/renderers' import { CanvasRenderer } from 'echarts/renderers'
import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics' import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics'
import type { PropType } from 'vue'
/** 设备状态统计卡片 */ /** 设备状态统计卡片 */
defineOptions({ name: 'DeviceStateCountCard' }) defineOptions({ name: 'DeviceStateCountCard' })
@ -41,6 +48,10 @@ const props = defineProps({
statsData: { statsData: {
type: Object as PropType<IotStatisticsSummaryRespVO>, type: Object as PropType<IotStatisticsSummaryRespVO>,
required: true required: true
},
loading: {
type: Boolean,
default: false
} }
}) })
@ -48,63 +59,95 @@ const deviceOnlineCountChartRef = ref()
const deviceOfflineChartRef = ref() const deviceOfflineChartRef = ref()
const deviceActiveChartRef = ref() const deviceActiveChartRef = ref()
//
const hasData = computed(() => {
if (!props.statsData) return false
return props.statsData.deviceCount !== -1
})
// //
const initGaugeChart = (el: any, value: number, color: string) => { const initGaugeChart = (el: any, value: number, color: string) => {
// DOM
if (!el) {
console.warn('图表DOM元素不存在')
return
}
echarts.use([GaugeChart, CanvasRenderer]) echarts.use([GaugeChart, CanvasRenderer])
const chart = echarts.init(el) try {
chart.setOption({ const chart = echarts.init(el)
series: [ chart.setOption({
{ series: [
type: 'gauge', {
startAngle: 360, type: 'gauge',
endAngle: 0, startAngle: 360,
min: 0, endAngle: 0,
max: props.statsData.deviceCount || 100, // 使 min: 0,
progress: { max: props.statsData.deviceCount || 100, // 使
show: true, progress: {
width: 12, show: true,
itemStyle: {
color: color
}
},
axisLine: {
lineStyle: {
width: 12, width: 12,
color: [[1, '#E5E7EB']] itemStyle: {
} color: color
}, }
axisTick: { show: false }, },
splitLine: { show: false }, axisLine: {
axisLabel: { show: false }, lineStyle: {
pointer: { show: false }, width: 12,
anchor: { show: false }, color: [[1, '#E5E7EB']]
title: { show: false }, }
detail: { },
valueAnimation: true, axisTick: { show: false },
fontSize: 24, splitLine: { show: false },
fontWeight: 'bold', axisLabel: { show: false },
fontFamily: 'Inter, sans-serif', pointer: { show: false },
color: color, anchor: { show: false },
offsetCenter: [0, '0'], title: { show: false },
formatter: (value: number) => { detail: {
return `${value}` valueAnimation: true,
} fontSize: 24,
}, fontWeight: 'bold',
data: [{ value: value }] fontFamily: 'Inter, sans-serif',
} color: color,
] offsetCenter: [0, '0'],
}) formatter: (value: number) => {
return `${value}`
}
},
data: [{ value: value }]
}
]
})
return chart
} catch (error) {
console.error('初始化图表失败:', error)
return null
}
} }
// //
const initCharts = () => { const initCharts = () => {
// 线 //
initGaugeChart(deviceOnlineCountChartRef.value, props.statsData.deviceOnlineCount, '#0d9') if (!hasData.value) return
// 线
initGaugeChart(deviceOfflineChartRef.value, props.statsData.deviceOfflineCount, '#f50') // 使 nextTick DOM
// nextTick(() => {
initGaugeChart(deviceActiveChartRef.value, props.statsData.deviceInactiveCount, '#05b') // 线
if (deviceOnlineCountChartRef.value) {
initGaugeChart(deviceOnlineCountChartRef.value, props.statsData.deviceOnlineCount, '#0d9')
}
// 线
if (deviceOfflineChartRef.value) {
initGaugeChart(deviceOfflineChartRef.value, props.statsData.deviceOfflineCount, '#f50')
}
//
if (deviceActiveChartRef.value) {
initGaugeChart(deviceActiveChartRef.value, props.statsData.deviceInactiveCount, '#05b')
}
})
} }
// //

View File

@ -1,8 +1,13 @@
<template> <template>
<el-card class="chart-card" shadow="never"> <el-card class="chart-card" shadow="never" :loading="loading">
<template #header> <template #header>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-base font-medium text-gray-600">上下行消息量统计</span> <span class="text-base font-medium text-gray-600">
上下行消息量统计
<span class="text-sm text-gray-400 ml-2">
{{ props.messageStats.statType === 1 ? '(按天)' : '(按小时)' }}
</span>
</span>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<el-radio-group v-model="timeRange" @change="handleTimeRangeChange"> <el-radio-group v-model="timeRange" @change="handleTimeRangeChange">
<el-radio-button label="8h">最近8小时</el-radio-button> <el-radio-button label="8h">最近8小时</el-radio-button>
@ -21,7 +26,13 @@
</div> </div>
</div> </div>
</template> </template>
<div ref="messageChartRef" class="h-[300px]"></div> <div v-if="loading && !hasData" class="h-[300px] flex justify-center items-center">
<el-empty description="加载中..." />
</div>
<div v-else-if="!hasData" class="h-[300px] flex justify-center items-center">
<el-empty description="暂无数据" />
</div>
<div v-else ref="messageChartRef" class="h-[300px]"></div>
</el-card> </el-card>
</template> </template>
@ -43,6 +54,10 @@ const props = defineProps({
messageStats: { messageStats: {
type: Object as PropType<IotStatisticsDeviceMessageSummaryRespVO>, type: Object as PropType<IotStatisticsDeviceMessageSummaryRespVO>,
required: true required: true
},
loading: {
type: Boolean,
default: false
} }
}) })
@ -52,6 +67,20 @@ const timeRange = ref('7d')
const dateRange = ref<any>(null) const dateRange = ref<any>(null)
const messageChartRef = ref() const messageChartRef = ref()
//
const hasData = computed(() => {
if (!props.messageStats) return false
const upstreamCounts = Array.isArray(props.messageStats.upstreamCounts)
? props.messageStats.upstreamCounts
: []
const downstreamCounts = Array.isArray(props.messageStats.downstreamCounts)
? props.messageStats.downstreamCounts
: []
return upstreamCounts.length > 0 || downstreamCounts.length > 0
})
// TODO @super dayjs 1h24h7d utils/formatTime.ts // TODO @super dayjs 1h24h7d utils/formatTime.ts
// //
const handleTimeRangeChange = (range: string) => { const handleTimeRangeChange = (range: string) => {
@ -84,6 +113,15 @@ const initChart = () => {
UniversalTransition UniversalTransition
]) ])
//
if (!hasData.value) return
// DOM
if (!messageChartRef.value) {
console.warn('图表DOM元素不存在')
return
}
// //
const upstreamCounts = Array.isArray(props.messageStats.upstreamCounts) const upstreamCounts = Array.isArray(props.messageStats.upstreamCounts)
@ -117,9 +155,19 @@ const initChart = () => {
timestamps = [] timestamps = []
} }
console.log('时间戳:', timestamps)
// // - statType
const xdata = timestamps.map((ts) => formatDate(dayjs(ts).toDate(), 'YYYY-MM-DD HH:mm')) const xdata = timestamps.map((ts) => {
// statType
if (props.messageStats.statType === 1) {
// - 使 YYYY-MM-DD
return formatDate(dayjs(ts).toDate(), 'YYYY-MM-DD')
} else {
// - 使 YYYY-MM-DD HH:mm
return formatDate(dayjs(ts).toDate(), 'YYYY-MM-DD HH:mm')
}
})
let upData: number[] = [] let upData: number[] = []
let downData: number[] = [] let downData: number[] = []
@ -155,110 +203,123 @@ const initChart = () => {
// //
const chart = echarts.init(messageChartRef.value) try {
chart.setOption({ const chart = echarts.init(messageChartRef.value)
tooltip: {
trigger: 'axis', chart.setOption({
backgroundColor: 'rgba(255, 255, 255, 0.9)', tooltip: {
borderColor: '#E5E7EB', trigger: 'axis',
textStyle: { backgroundColor: 'rgba(255, 255, 255, 0.9)',
color: '#374151' borderColor: '#E5E7EB',
} textStyle: {
}, color: '#374151'
legend: {
data: ['上行消息量', '下行消息量'],
textStyle: {
color: '#374151',
fontWeight: 500
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: xdata,
axisLine: {
lineStyle: {
color: '#E5E7EB'
} }
}, },
axisLabel: { legend: {
color: '#6B7280' data: ['上行消息量', '下行消息量'],
} textStyle: {
}, color: '#374151',
yAxis: { fontWeight: 500
type: 'value',
axisLine: {
lineStyle: {
color: '#E5E7EB'
} }
}, },
axisLabel: { grid: {
color: '#6B7280' left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
}, },
splitLine: { xAxis: {
lineStyle: { type: 'category',
color: '#F3F4F6' boundaryGap: false,
} data: xdata,
} axisLine: {
}, lineStyle: {
series: [ color: '#E5E7EB'
{ }
name: '上行消息量',
type: 'line',
smooth: true,
data: upData,
itemStyle: {
color: '#3B82F6'
}, },
lineStyle: { axisLabel: {
width: 2 color: '#6B7280'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(59, 130, 246, 0.2)' },
{ offset: 1, color: 'rgba(59, 130, 246, 0)' }
])
} }
}, },
{ yAxis: {
name: '下行消息量', type: 'value',
type: 'line', axisLine: {
smooth: true, lineStyle: {
data: downData, color: '#E5E7EB'
itemStyle: { }
color: '#10B981'
}, },
lineStyle: { axisLabel: {
width: 2 color: '#6B7280'
}, },
areaStyle: { splitLine: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ lineStyle: {
{ offset: 0, color: 'rgba(16, 185, 129, 0.2)' }, color: '#F3F4F6'
{ offset: 1, color: 'rgba(16, 185, 129, 0)' } }
])
} }
} },
] series: [
}) {
name: '上行消息量',
type: 'line',
smooth: true,
data: upData,
itemStyle: {
color: '#3B82F6'
},
lineStyle: {
width: 2
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(59, 130, 246, 0.2)' },
{ offset: 1, color: 'rgba(59, 130, 246, 0)' }
])
}
},
{
name: '下行消息量',
type: 'line',
smooth: true,
data: downData,
itemStyle: {
color: '#10B981'
},
lineStyle: {
width: 2
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(16, 185, 129, 0.2)' },
{ offset: 1, color: 'rgba(16, 185, 129, 0)' }
])
}
}
]
})
return chart
} catch (error) {
console.error('初始化图表失败:', error)
return null
}
} }
// //
watch( watch(
() => props.messageStats, () => props.messageStats,
() => { () => {
initChart() // 使 nextTick DOM
nextTick(() => {
initChart()
})
}, },
{ deep: true } { deep: true }
) )
// //
onMounted(() => { onMounted(() => {
initChart() // 使 nextTick DOM
nextTick(() => {
initChart()
})
}) })
</script> </script>

View File

@ -8,6 +8,7 @@
:todayCount="statsData.productCategoryTodayCount" :todayCount="statsData.productCategoryTodayCount"
icon="ep:menu" icon="ep:menu"
iconColor="text-blue-400" iconColor="text-blue-400"
:loading="loading"
/> />
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
@ -17,6 +18,7 @@
:todayCount="statsData.productTodayCount" :todayCount="statsData.productTodayCount"
icon="ep:box" icon="ep:box"
iconColor="text-orange-400" iconColor="text-orange-400"
:loading="loading"
/> />
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
@ -26,6 +28,7 @@
:todayCount="statsData.deviceTodayCount" :todayCount="statsData.deviceTodayCount"
icon="ep:cpu" icon="ep:cpu"
iconColor="text-purple-400" iconColor="text-purple-400"
:loading="loading"
/> />
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
@ -35,6 +38,7 @@
:todayCount="statsData.deviceMessageTodayCount" :todayCount="statsData.deviceMessageTodayCount"
icon="ep:message" icon="ep:message"
iconColor="text-teal-400" iconColor="text-teal-400"
:loading="loading"
/> />
</el-col> </el-col>
</el-row> </el-row>
@ -42,10 +46,10 @@
<!-- 第二行图表行 --> <!-- 第二行图表行 -->
<el-row :gutter="16" class="mb-4"> <el-row :gutter="16" class="mb-4">
<el-col :span="12"> <el-col :span="12">
<DeviceCountCard :statsData="statsData" /> <DeviceCountCard :statsData="statsData" :loading="loading" />
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<DeviceStateCountCard :statsData="statsData" /> <DeviceStateCountCard :statsData="statsData" :loading="loading" />
</el-col> </el-col>
</el-row> </el-row>
@ -55,6 +59,7 @@
<MessageTrendCard <MessageTrendCard
:messageStats="messageStats" :messageStats="messageStats"
@time-range-change="handleTimeRangeChange" @time-range-change="handleTimeRangeChange"
:loading="loading"
/> />
</el-col> </el-col>
</el-row> </el-row>
@ -68,7 +73,7 @@ import {
IotStatisticsSummaryRespVO, IotStatisticsSummaryRespVO,
ProductCategoryApi ProductCategoryApi
} from '@/api/iot/statistics' } from '@/api/iot/statistics'
import { formatDate } from '@/utils/formatTime' import { getHoursAgo } from '@/utils/formatTime'
import ComparisonCard from './components/ComparisonCard.vue' import ComparisonCard from './components/ComparisonCard.vue'
import DeviceCountCard from './components/DeviceCountCard.vue' import DeviceCountCard from './components/DeviceCountCard.vue'
import DeviceStateCountCard from './components/DeviceStateCountCard.vue' import DeviceStateCountCard from './components/DeviceStateCountCard.vue'
@ -79,11 +84,9 @@ defineOptions({ name: 'IoTHome' })
// TODO @super使 Echart yudao-ui-admin-vue3/src/views/mall/home/components/TradeTrendCard.vue // TODO @super使 Echart yudao-ui-admin-vue3/src/views/mall/home/components/TradeTrendCard.vue
const timeRange = ref('7d') //
const dateRange = ref<[Date, Date] | null>(null)
const queryParams = reactive({ const queryParams = reactive({
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 startTime: getHoursAgo( 7 * 24 ), // 7
endTime: Date.now() // endTime: Date.now() //
}) })
@ -91,26 +94,30 @@ const queryParams = reactive({
// //
// TODO @super -1 cursor // TODO @super -1 cursor
const statsData = ref<IotStatisticsSummaryRespVO>({ const statsData = ref<IotStatisticsSummaryRespVO>({
productCategoryCount: 0, productCategoryCount: -1,
productCount: 0, productCount: -1,
deviceCount: 0, deviceCount: -1,
deviceMessageCount: 0, deviceMessageCount: -1,
productCategoryTodayCount: 0, productCategoryTodayCount: -1,
productTodayCount: 0, productTodayCount: -1,
deviceTodayCount: 0, deviceTodayCount: -1,
deviceMessageTodayCount: 0, deviceMessageTodayCount: -1,
deviceOnlineCount: 0, deviceOnlineCount: -1,
deviceOfflineCount: 0, deviceOfflineCount: -1,
deviceInactiveCount: 0, deviceInactiveCount: -1,
productCategoryDeviceCounts: {} productCategoryDeviceCounts: {}
}) })
// //
const messageStats = ref<IotStatisticsDeviceMessageSummaryRespVO>({ const messageStats = ref<IotStatisticsDeviceMessageSummaryRespVO>({
upstreamCounts: {}, statType: 0,
downstreamCounts: {} upstreamCounts: [],
downstreamCounts: []
}) })
//
const loading = ref(true)
/** 处理时间范围变化 */ /** 处理时间范围变化 */
const handleTimeRangeChange = (params: { startTime: number; endTime: number }) => { const handleTimeRangeChange = (params: { startTime: number; endTime: number }) => {
queryParams.startTime = params.startTime queryParams.startTime = params.startTime
@ -120,12 +127,17 @@ const handleTimeRangeChange = (params: { startTime: number; endTime: number }) =
/** 获取统计数据 */ /** 获取统计数据 */
const getStats = async () => { const getStats = async () => {
// loading.value = true
statsData.value = await ProductCategoryApi.getIotStatisticsSummary() try {
// //
messageStats.value = await ProductCategoryApi.getIotStatisticsDeviceMessageSummary(queryParams) statsData.value = await ProductCategoryApi.getIotStatisticsSummary()
console.log('statsData', statsData.value) //
console.log('messageStats', messageStats.value) messageStats.value = await ProductCategoryApi.getIotStatisticsDeviceMessageSummary(queryParams)
} catch (error) {
console.error('获取统计数据出错:', error)
} finally {
loading.value = false
}
} }
/** 初始化 */ /** 初始化 */