feat:【IoT 物联网】设备消息统计的代码优化
This commit is contained in:
@ -28,19 +28,32 @@ export interface IotStatisticsDeviceMessageSummaryRespVO {
|
||||
downstreamCounts: TimeValueItem[]
|
||||
}
|
||||
|
||||
/** 新的消息统计数据项 */
|
||||
export interface IotStatisticsDeviceMessageSummaryByDateRespVO {
|
||||
time: string
|
||||
upstreamCount: number
|
||||
downstreamCount: number
|
||||
}
|
||||
|
||||
/** 新的消息统计接口参数 */
|
||||
export interface IotStatisticsDeviceMessageReqVO {
|
||||
interval: number
|
||||
times?: string[]
|
||||
}
|
||||
|
||||
// IoT 数据统计 API
|
||||
export const ProductCategoryApi = {
|
||||
// 查询基础的数据统计
|
||||
getIotStatisticsSummary: async () => {
|
||||
export const StatisticsApi = {
|
||||
// 查询全局的数据统计
|
||||
getStatisticsSummary: async () => {
|
||||
return await request.get<IotStatisticsSummaryRespVO>({
|
||||
url: `/iot/statistics/get-summary`
|
||||
})
|
||||
},
|
||||
|
||||
// 查询设备上下行消息的数据统计
|
||||
getIotStatisticsDeviceMessageSummary: async (params: { startTime: number; endTime: number }) => {
|
||||
return await request.get<IotStatisticsDeviceMessageSummaryRespVO>({
|
||||
url: `/iot/statistics/get-log-summary`,
|
||||
// 获取设备消息的数据统计
|
||||
getDeviceMessageSummaryByDate: async (params: IotStatisticsDeviceMessageReqVO) => {
|
||||
return await request.get<IotStatisticsDeviceMessageSummaryByDateRespVO[]>({
|
||||
url: `/iot/statistics/get-device-message-summary-by-date`,
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
@ -330,30 +330,3 @@ export function getDateRange(
|
||||
dayjs(endDate).endOf('d').format('YYYY-MM-DD HH:mm:ss')
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定小时前的时间戳
|
||||
* @param hours 小时数
|
||||
* @returns 返回指定小时前的时间戳(毫秒)
|
||||
*/
|
||||
export function getHoursAgo(hours: number): number {
|
||||
return dayjs().subtract(hours, 'hour').valueOf()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标准时间范围的时间戳
|
||||
* @param range 时间范围,支持 '8h' | '24h' | '7d'
|
||||
* @returns 返回开始时间戳(毫秒)
|
||||
*/
|
||||
export function getTimeRangeStart(range: '8h' | '24h' | '7d'): number {
|
||||
switch (range) {
|
||||
case '8h':
|
||||
return getHoursAgo(8)
|
||||
case '24h':
|
||||
return getHoursAgo(24)
|
||||
case '7d':
|
||||
return dayjs().subtract(7, 'day').valueOf()
|
||||
default:
|
||||
return dayjs().valueOf()
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +80,6 @@ import { DeviceApi, DeviceVO } from '@/api/iot/device/device'
|
||||
import { DeviceGroupApi } from '@/api/iot/device/group'
|
||||
import { DeviceTypeEnum, ProductApi, ProductVO } from '@/api/iot/product/product'
|
||||
import { UploadImg } from '@/components/UploadFile'
|
||||
import { generateRandomStr } from '@/utils'
|
||||
|
||||
/** IoT 设备表单 */
|
||||
defineOptions({ name: 'IoTDeviceForm' })
|
||||
|
||||
@ -22,8 +22,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
|
||||
/** 统计卡片组件 */
|
||||
defineOptions({ name: 'ComparisonCard' })
|
||||
/** 【总数 + 新增数】统计卡片组件 */
|
||||
defineOptions({ name: 'IoTComparisonCard' })
|
||||
|
||||
const props = defineProps({
|
||||
title: propTypes.string.def('').isRequired,
|
||||
|
||||
@ -24,7 +24,7 @@ import { LabelLayout } from 'echarts/features'
|
||||
import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
/** 设备数量统计卡片 */
|
||||
/** 【设备数量】统计卡片 */
|
||||
defineOptions({ name: 'DeviceCountCard' })
|
||||
|
||||
const props = defineProps({
|
||||
@ -40,27 +40,25 @@ const props = defineProps({
|
||||
|
||||
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 = () => {
|
||||
// 如果没有数据,则不初始化图表
|
||||
if (!hasData.value) return
|
||||
|
||||
// 确保 DOM 元素存在且已渲染
|
||||
if (!deviceCountChartRef.value) {
|
||||
console.warn('图表DOM元素不存在')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
echarts.use([TooltipComponent, LegendComponent, PieChart, CanvasRenderer, LabelLayout])
|
||||
|
||||
try {
|
||||
const chart = echarts.init(deviceCountChartRef.value)
|
||||
chart.setOption({
|
||||
@ -95,10 +93,12 @@ const initChart = () => {
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: Object.entries(props.statsData.productCategoryDeviceCounts).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}))
|
||||
data: Object.entries(props.statsData.productCategoryDeviceCounts).map(
|
||||
([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
})
|
||||
)
|
||||
}
|
||||
]
|
||||
})
|
||||
@ -109,18 +109,22 @@ const initChart = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.statsData, () => {
|
||||
// 使用 nextTick 确保 DOM 已更新
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
}, { deep: true })
|
||||
/** 监听数据变化 */
|
||||
watch(
|
||||
() => props.statsData,
|
||||
() => {
|
||||
// 使用 nextTick 确保 DOM 已更新
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
onMounted(() => {
|
||||
/** 组件挂载时初始化图表 */
|
||||
onMounted(async () => {
|
||||
// 使用 nextTick 确保 DOM 已更新
|
||||
nextTick(() => {
|
||||
await nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
})
|
||||
|
||||
@ -41,7 +41,7 @@ import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics'
|
||||
import type { PropType } from 'vue'
|
||||
|
||||
/** 设备状态统计卡片 */
|
||||
/** 【设备状态】统计卡片 */
|
||||
defineOptions({ name: 'DeviceStateCountCard' })
|
||||
|
||||
const props = defineProps({
|
||||
@ -59,22 +59,21 @@ const deviceOnlineCountChartRef = ref()
|
||||
const deviceOfflineChartRef = 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) => {
|
||||
// 确保 DOM 元素存在且已渲染
|
||||
if (!el) {
|
||||
console.warn('图表DOM元素不存在')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
echarts.use([GaugeChart, CanvasRenderer])
|
||||
|
||||
try {
|
||||
const chart = echarts.init(el)
|
||||
chart.setOption({
|
||||
@ -126,23 +125,21 @@ const initGaugeChart = (el: any, value: number, color: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化所有图表
|
||||
/** 初始化所有图表 */
|
||||
const initCharts = () => {
|
||||
// 如果没有数据,则不初始化图表
|
||||
if (!hasData.value) return
|
||||
|
||||
|
||||
// 使用 nextTick 确保 DOM 已更新
|
||||
nextTick(() => {
|
||||
// 在线设备统计
|
||||
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')
|
||||
@ -150,12 +147,16 @@ const initCharts = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 监听数据变化
|
||||
watch(() => props.statsData, () => {
|
||||
initCharts()
|
||||
}, { deep: true })
|
||||
/** 监听数据变化 */
|
||||
watch(
|
||||
() => props.statsData,
|
||||
() => {
|
||||
initCharts()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
/** 组件挂载时初始化图表 */
|
||||
onMounted(() => {
|
||||
initCharts()
|
||||
})
|
||||
|
||||
@ -2,27 +2,36 @@
|
||||
<el-card class="chart-card" shadow="never" :loading="loading">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<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">
|
||||
<el-radio-group v-model="timeRange" @change="handleTimeRangeChange">
|
||||
<el-radio-button label="8h">最近8小时</el-radio-button>
|
||||
<el-radio-button label="24h">最近24小时</el-radio-button>
|
||||
<el-radio-button label="7d">近一周</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
@change="handleDateRangeChange"
|
||||
/>
|
||||
<span class="text-base font-medium text-gray-600">消息量统计</span>
|
||||
<div class="flex flex-wrap items-center gap-4">
|
||||
<el-form-item label="时间范围" class="!mb-0">
|
||||
<el-date-picker
|
||||
v-model="queryParams.times"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
:shortcuts="defaultShortcuts"
|
||||
class="!w-240px"
|
||||
end-placeholder="结束日期"
|
||||
start-placeholder="开始日期"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
@change="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间间隔" class="!mb-0">
|
||||
<el-select
|
||||
v-model="queryParams.interval"
|
||||
class="!w-120px"
|
||||
placeholder="间隔类型"
|
||||
@change="handleQuery"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.DATE_INTERVAL)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -42,68 +51,68 @@ import { LineChart } from 'echarts/charts'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { GridComponent, LegendComponent, TooltipComponent } from 'echarts/components'
|
||||
import { UniversalTransition } from 'echarts/features'
|
||||
import { IotStatisticsDeviceMessageSummaryRespVO } from '@/api/iot/statistics'
|
||||
import { formatDate, getTimeRangeStart } from '@/utils/formatTime'
|
||||
import type { PropType } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
StatisticsApi,
|
||||
IotStatisticsDeviceMessageSummaryByDateRespVO,
|
||||
IotStatisticsDeviceMessageReqVO
|
||||
} from '@/api/iot/statistics'
|
||||
import { formatDate, beginOfDay, endOfDay, defaultShortcuts } from '@/utils/formatTime'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
/** 消息趋势统计卡片 */
|
||||
defineOptions({ name: 'MessageTrendCard' })
|
||||
|
||||
const props = defineProps({
|
||||
messageStats: {
|
||||
type: Object as PropType<IotStatisticsDeviceMessageSummaryRespVO>,
|
||||
required: true
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['timeRangeChange'])
|
||||
|
||||
const timeRange = ref('7d')
|
||||
const dateRange = ref<any>(null)
|
||||
const messageChartRef = ref()
|
||||
const loading = ref(false)
|
||||
const messageData = ref<IotStatisticsDeviceMessageSummaryByDateRespVO[]>([])
|
||||
|
||||
const queryParams = reactive<IotStatisticsDeviceMessageReqVO>({
|
||||
interval: 1, // DAY, 日
|
||||
times: [
|
||||
// 默认显示最近一周的数据
|
||||
formatDate(beginOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24 * 7))),
|
||||
formatDate(endOfDay(new Date(new Date().getTime() - 3600 * 1000 * 24)))
|
||||
]
|
||||
}) // 查询参数
|
||||
|
||||
// 是否有数据
|
||||
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
|
||||
return messageData.value && messageData.value.length > 0
|
||||
})
|
||||
// TODO @super:这个的计算,看看能不能结合 dayjs 简化。因为 1h、24h、7d 感觉是比较标准的。如果没有,抽到 utils/formatTime.ts 作为一个工具方法
|
||||
// 处理快捷时间范围选择
|
||||
const handleTimeRangeChange = (range: string) => {
|
||||
const now = dayjs().valueOf()
|
||||
const startTime = getTimeRangeStart(range as '8h' | '24h' | '7d')
|
||||
|
||||
dateRange.value = null
|
||||
emit('timeRangeChange', { startTime, endTime: now })
|
||||
// 处理查询操作
|
||||
const handleQuery = () => {
|
||||
fetchMessageData()
|
||||
}
|
||||
|
||||
// 处理自定义日期范围选择
|
||||
const handleDateRangeChange = (value: [Date, Date] | null) => {
|
||||
if (value) {
|
||||
timeRange.value = ''
|
||||
emit('timeRangeChange', {
|
||||
startTime: value[0].getTime(),
|
||||
endTime: value[1].getTime()
|
||||
})
|
||||
// 获取消息统计数据
|
||||
const fetchMessageData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
messageData.value = await StatisticsApi.getDeviceMessageSummaryByDate(queryParams)
|
||||
|
||||
// 使用 nextTick 确保数据更新后重新渲染图表
|
||||
await nextTick()
|
||||
initChart()
|
||||
} catch (error) {
|
||||
console.error('获取消息统计数据失败:', error)
|
||||
messageData.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
// 检查是否有数据可以绘制
|
||||
if (!hasData.value) return
|
||||
// 确保 DOM 元素存在且已渲染
|
||||
if (!messageChartRef.value) {
|
||||
console.warn('图表 DOM 元素不存在')
|
||||
return
|
||||
}
|
||||
|
||||
// 配置图表
|
||||
echarts.use([
|
||||
LineChart,
|
||||
CanvasRenderer,
|
||||
@ -112,100 +121,8 @@ const initChart = () => {
|
||||
TooltipComponent,
|
||||
UniversalTransition
|
||||
])
|
||||
|
||||
// 检查是否有数据可以绘制
|
||||
if (!hasData.value) return
|
||||
|
||||
// 确保 DOM 元素存在且已渲染
|
||||
if (!messageChartRef.value) {
|
||||
console.warn('图表DOM元素不存在')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 检查数据格式并转换
|
||||
const upstreamCounts = Array.isArray(props.messageStats.upstreamCounts)
|
||||
? props.messageStats.upstreamCounts
|
||||
: Object.entries(props.messageStats.upstreamCounts || {}).map(([key, value]) => ({ [key]: value }))
|
||||
|
||||
const downstreamCounts = Array.isArray(props.messageStats.downstreamCounts)
|
||||
? props.messageStats.downstreamCounts
|
||||
: Object.entries(props.messageStats.downstreamCounts || {}).map(([key, value]) => ({ [key]: value }))
|
||||
|
||||
// 获取所有时间戳并排序
|
||||
let timestamps: number[] = []
|
||||
|
||||
try {
|
||||
// 尝试从数组中提取时间戳
|
||||
if (Array.isArray(upstreamCounts) && upstreamCounts.length > 0) {
|
||||
timestamps = Array.from(
|
||||
new Set([
|
||||
...upstreamCounts.map(item => Number(Object.keys(item)[0])),
|
||||
...downstreamCounts.map(item => Number(Object.keys(item)[0]))
|
||||
])
|
||||
).sort((a, b) => a - b)
|
||||
} else {
|
||||
// 如果数组为空或不是数组,尝试从对象中提取时间戳
|
||||
const upKeys = Object.keys(props.messageStats.upstreamCounts || {}).map(Number)
|
||||
const downKeys = Object.keys(props.messageStats.downstreamCounts || {}).map(Number)
|
||||
timestamps = Array.from(new Set([...upKeys, ...downKeys])).sort((a, b) => a - b)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提取时间戳出错:', error)
|
||||
timestamps = []
|
||||
}
|
||||
|
||||
console.log('时间戳:', timestamps)
|
||||
|
||||
// 准备数据 - 根据 statType 确定时间格式
|
||||
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 downData: number[] = []
|
||||
|
||||
try {
|
||||
// 尝试从数组中提取数据
|
||||
if (Array.isArray(upstreamCounts) && upstreamCounts.length > 0) {
|
||||
upData = timestamps.map((ts) => {
|
||||
const item = upstreamCounts.find(count =>
|
||||
Number(Object.keys(count)[0]) === ts
|
||||
)
|
||||
return item ? Number(Object.values(item)[0]) : 0
|
||||
})
|
||||
|
||||
downData = timestamps.map((ts) => {
|
||||
const item = downstreamCounts.find(count =>
|
||||
Number(Object.keys(count)[0]) === ts
|
||||
)
|
||||
return item ? Number(Object.values(item)[0]) : 0
|
||||
})
|
||||
} else {
|
||||
// 如果数组为空或不是数组,尝试从对象中提取数据
|
||||
const upstreamObj = props.messageStats.upstreamCounts || {}
|
||||
const downstreamObj = props.messageStats.downstreamCounts || {}
|
||||
upData = timestamps.map((ts) => Number(upstreamObj[ts as keyof typeof upstreamObj] || 0))
|
||||
downData = timestamps.map((ts) => Number(downstreamObj[ts as keyof typeof downstreamObj] || 0))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提取数据出错:', error)
|
||||
upData = []
|
||||
downData = []
|
||||
}
|
||||
|
||||
|
||||
// 配置图表
|
||||
try {
|
||||
const chart = echarts.init(messageChartRef.value)
|
||||
|
||||
chart.setOption({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
@ -231,7 +148,7 @@ const initChart = () => {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: xdata,
|
||||
data: messageData.value.map((item) => item.time),
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#E5E7EB'
|
||||
@ -262,7 +179,7 @@ const initChart = () => {
|
||||
name: '上行消息量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: upData,
|
||||
data: messageData.value.map((item) => item.upstreamCount),
|
||||
itemStyle: {
|
||||
color: '#3B82F6'
|
||||
},
|
||||
@ -280,7 +197,7 @@ const initChart = () => {
|
||||
name: '下行消息量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: downData,
|
||||
data: messageData.value.map((item) => item.downstreamCount),
|
||||
itemStyle: {
|
||||
color: '#10B981'
|
||||
},
|
||||
@ -303,23 +220,8 @@ const initChart = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => props.messageStats,
|
||||
() => {
|
||||
// 使用 nextTick 确保 DOM 已更新
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 组件挂载时初始化图表
|
||||
/** 组件挂载时初始化 */
|
||||
onMounted(() => {
|
||||
// 使用 nextTick 确保 DOM 已更新
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
fetchMessageData()
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -56,11 +56,7 @@
|
||||
<!-- 第三行:消息统计行 -->
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<MessageTrendCard
|
||||
:messageStats="messageStats"
|
||||
@time-range-change="handleTimeRangeChange"
|
||||
:loading="loading"
|
||||
/>
|
||||
<MessageTrendCard />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
@ -68,12 +64,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Index">
|
||||
import {
|
||||
IotStatisticsDeviceMessageSummaryRespVO,
|
||||
IotStatisticsSummaryRespVO,
|
||||
ProductCategoryApi
|
||||
} from '@/api/iot/statistics'
|
||||
import { getHoursAgo } from '@/utils/formatTime'
|
||||
import { IotStatisticsSummaryRespVO, StatisticsApi } from '@/api/iot/statistics'
|
||||
import ComparisonCard from './components/ComparisonCard.vue'
|
||||
import DeviceCountCard from './components/DeviceCountCard.vue'
|
||||
import DeviceStateCountCard from './components/DeviceStateCountCard.vue'
|
||||
@ -82,17 +73,6 @@ import MessageTrendCard from './components/MessageTrendCard.vue'
|
||||
/** IoT 首页 */
|
||||
defineOptions({ name: 'IoTHome' })
|
||||
|
||||
// TODO @super:使用下 Echart 组件,参考 yudao-ui-admin-vue3/src/views/mall/home/components/TradeTrendCard.vue 等
|
||||
|
||||
|
||||
const queryParams = reactive({
|
||||
startTime: getHoursAgo( 7 * 24 ), // 设置默认开始时间为 7 天前
|
||||
endTime: Date.now() // 设置默认结束时间为当前时间
|
||||
})
|
||||
|
||||
|
||||
// 基础统计数据
|
||||
// TODO @super:初始为 -1,然后界面展示先是加载中?试试用 cursor 改哈
|
||||
const statsData = ref<IotStatisticsSummaryRespVO>({
|
||||
productCategoryCount: -1,
|
||||
productCount: -1,
|
||||
@ -106,33 +86,16 @@ const statsData = ref<IotStatisticsSummaryRespVO>({
|
||||
deviceOfflineCount: -1,
|
||||
deviceInactiveCount: -1,
|
||||
productCategoryDeviceCounts: {}
|
||||
})
|
||||
}) // 基础统计数据
|
||||
|
||||
// 消息统计数据
|
||||
const messageStats = ref<IotStatisticsDeviceMessageSummaryRespVO>({
|
||||
statType: 0,
|
||||
upstreamCounts: [],
|
||||
downstreamCounts: []
|
||||
})
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(true)
|
||||
|
||||
/** 处理时间范围变化 */
|
||||
const handleTimeRangeChange = (params: { startTime: number; endTime: number }) => {
|
||||
queryParams.startTime = params.startTime
|
||||
queryParams.endTime = params.endTime
|
||||
getStats()
|
||||
}
|
||||
const loading = ref(true) // 加载状态
|
||||
|
||||
/** 获取统计数据 */
|
||||
const getStats = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 获取基础统计数据
|
||||
statsData.value = await ProductCategoryApi.getIotStatisticsSummary()
|
||||
// 获取消息统计数据
|
||||
messageStats.value = await ProductCategoryApi.getIotStatisticsDeviceMessageSummary(queryParams)
|
||||
statsData.value = await StatisticsApi.getStatisticsSummary()
|
||||
} catch (error) {
|
||||
console.error('获取统计数据出错:', error)
|
||||
} finally {
|
||||
@ -145,5 +108,3 @@ onMounted(() => {
|
||||
getStats()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
Reference in New Issue
Block a user