feat;iothome

pull/764/head
alwayssuper 2025-04-14 11:25:01 +08:00
parent 585cc85df5
commit fc3018bfb2
4 changed files with 210 additions and 129 deletions

View File

@ -0,0 +1,87 @@
<template>
<el-card class="chart-card" shadow="never">
<template #header>
<div class="flex items-center">
<span class="text-base font-medium text-gray-600">设备数量统计</span>
</div>
</template>
<div ref="deviceCountChartRef" class="h-[240px]"></div>
</el-card>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts/core'
import { PieChart } from 'echarts/charts'
import { CanvasRenderer } from 'echarts/renderers'
import { TooltipComponent, LegendComponent } from 'echarts/components'
import { LabelLayout } from 'echarts/features'
import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics'
/** 设备数量统计卡片 */
defineOptions({ name: 'DeviceCountCard' })
const props = defineProps({
statsData: {
type: Object as PropType<IotStatisticsSummaryRespVO>,
required: true
}
})
const deviceCountChartRef = ref()
//
const initChart = () => {
echarts.use([TooltipComponent, LegendComponent, PieChart, CanvasRenderer, LabelLayout])
const chart = echarts.init(deviceCountChartRef.value)
chart.setOption({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
right: '10%',
align: 'left',
orient: 'vertical',
icon: 'circle'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['50%', '80%'],
avoidLabelOverlap: false,
center: ['30%', '50%'],
label: {
show: false,
position: 'outside'
},
emphasis: {
label: {
show: true,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: Object.entries(props.statsData.productCategoryDeviceCounts).map(([name, value]) => ({
name,
value
}))
}
]
})
}
//
watch(() => props.statsData, () => {
initChart()
}, { deep: true })
//
onMounted(() => {
initChart()
})
</script>

View File

@ -0,0 +1,119 @@
<template>
<el-card class="chart-card" shadow="never">
<template #header>
<div class="flex items-center">
<span class="text-base font-medium text-gray-600">设备状态统计</span>
</div>
</template>
<el-row class="h-[240px]">
<el-col :span="8" class="flex flex-col items-center">
<div ref="deviceOnlineCountChartRef" class="h-[160px] w-full"></div>
<div class="text-center mt-2">
<span class="text-sm text-gray-600">在线设备</span>
</div>
</el-col>
<el-col :span="8" class="flex flex-col items-center">
<div ref="deviceOfflineChartRef" class="h-[160px] w-full"></div>
<div class="text-center mt-2">
<span class="text-sm text-gray-600">离线设备</span>
</div>
</el-col>
<el-col :span="8" class="flex flex-col items-center">
<div ref="deviceActiveChartRef" class="h-[160px] w-full"></div>
<div class="text-center mt-2">
<span class="text-sm text-gray-600">待激活设备</span>
</div>
</el-col>
</el-row>
</el-card>
</template>
<script lang="ts" setup>
import * as echarts from 'echarts/core'
import { GaugeChart } from 'echarts/charts'
import { CanvasRenderer } from 'echarts/renderers'
import { IotStatisticsSummaryRespVO } from '@/api/iot/statistics'
/** 设备状态统计卡片 */
defineOptions({ name: 'DeviceStateCountCard' })
const props = defineProps({
statsData: {
type: Object as PropType<IotStatisticsSummaryRespVO>,
required: true
}
})
const deviceOnlineCountChartRef = ref()
const deviceOfflineChartRef = ref()
const deviceActiveChartRef = ref()
//
const initGaugeChart = (el: any, value: number, color: string) => {
echarts.use([GaugeChart, CanvasRenderer])
const chart = echarts.init(el)
chart.setOption({
series: [
{
type: 'gauge',
startAngle: 360,
endAngle: 0,
min: 0,
max: props.statsData.deviceCount || 100, // 使
progress: {
show: true,
width: 12,
itemStyle: {
color: color
}
},
axisLine: {
lineStyle: {
width: 12,
color: [[1, '#E5E7EB']]
}
},
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
pointer: { show: false },
anchor: { show: false },
title: { show: false },
detail: {
valueAnimation: true,
fontSize: 24,
fontWeight: 'bold',
fontFamily: 'Inter, sans-serif',
color: color,
offsetCenter: [0, '0'],
formatter: (value: number) => {
return `${value}`
}
},
data: [{ value: value }]
}
]
})
}
//
const initCharts = () => {
// 线
initGaugeChart(deviceOnlineCountChartRef.value, props.statsData.deviceOnlineCount, '#0d9')
// 线
initGaugeChart(deviceOfflineChartRef.value, props.statsData.deviceOfflineCount, '#f50')
//
initGaugeChart(deviceActiveChartRef.value, props.statsData.deviceInactiveCount, '#05b')
}
//
watch(() => props.statsData, () => {
initCharts()
}, { deep: true })
//
onMounted(() => {
initCharts()
})
</script>

View File

@ -42,43 +42,10 @@
<!-- 第二行图表行 -->
<el-row :gutter="16" class="mb-4">
<el-col :span="12">
<el-card class="chart-card" shadow="never">
<template #header>
<div class="flex items-center">
<span class="text-base font-medium text-gray-600">设备数量统计</span>
</div>
</template>
<div ref="deviceCountChartRef" class="h-[240px]"></div>
</el-card>
<DeviceCountCard :statsData="statsData" />
</el-col>
<el-col :span="12">
<el-card class="chart-card" shadow="never">
<template #header>
<div class="flex items-center">
<span class="text-base font-medium text-gray-600">设备状态统计</span>
</div>
</template>
<el-row class="h-[240px]">
<el-col :span="8" class="flex flex-col items-center">
<div ref="deviceOnlineCountChartRef" class="h-[160px] w-full"></div>
<div class="text-center mt-2">
<span class="text-sm text-gray-600">在线设备</span>
</div>
</el-col>
<el-col :span="8" class="flex flex-col items-center">
<div ref="deviceOfflineChartRef" class="h-[160px] w-full"></div>
<div class="text-center mt-2">
<span class="text-sm text-gray-600">离线设备</span>
</div>
</el-col>
<el-col :span="8" class="flex flex-col items-center">
<div ref="deviceActiveChartRef" class="h-[160px] w-full"></div>
<div class="text-center mt-2">
<span class="text-sm text-gray-600">待激活设备</span>
</div>
</el-col>
</el-row>
</el-card>
<DeviceStateCountCard :statsData="statsData" />
</el-col>
</el-row>
@ -134,6 +101,8 @@ import {
} from '@/api/iot/statistics'
import { formatDate } from '@/utils/formatTime'
import ComparisonCard from './components/ComparisonCard.vue'
import DeviceCountCard from './components/DeviceCountCard.vue'
import DeviceStateCountCard from './components/DeviceStateCountCard.vue'
// TODO @super /Users/yunai/Java/yudao-ui-admin-vue3/src/views/mall/home/index.vue
@ -252,104 +221,10 @@ const getStats = async () => {
/** 初始化图表 */
const initCharts = () => {
//
echarts.init(deviceCountChartRef.value).setOption({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
right: '10%',
align: 'left',
orient: 'vertical',
icon: 'circle'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['50%', '80%'],
avoidLabelOverlap: false,
center: ['30%', '50%'],
label: {
show: false,
position: 'outside'
},
emphasis: {
label: {
show: true,
fontSize: 20,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: Object.entries(statsData.value.productCategoryDeviceCounts).map(([name, value]) => ({
name,
value
}))
}
]
})
// 线
initGaugeChart(deviceOnlineCountChartRef.value, statsData.value.deviceOnlineCount, '#0d9')
// 线
initGaugeChart(deviceOfflineChartRef.value, statsData.value.deviceOfflineCount, '#f50')
//
initGaugeChart(deviceActiveChartRef.value, statsData.value.deviceInactiveCount, '#05b')
//
initMessageChart()
}
/** 初始化仪表盘图表 */
const initGaugeChart = (el: any, value: number, color: string) => {
echarts.init(el).setOption({
series: [
{
type: 'gauge',
startAngle: 360,
endAngle: 0,
min: 0,
max: statsData.value.deviceCount || 100, // 使
progress: {
show: true,
width: 12,
itemStyle: {
color: color
}
},
axisLine: {
lineStyle: {
width: 12,
color: [[1, '#E5E7EB']]
}
},
axisTick: { show: false },
splitLine: { show: false },
axisLabel: { show: false },
pointer: { show: false },
anchor: { show: false },
title: { show: false },
detail: {
valueAnimation: true,
fontSize: 24,
fontWeight: 'bold',
fontFamily: 'Inter, sans-serif',
color: color,
offsetCenter: [0, '0'],
formatter: (value: number) => {
return `${value}`
}
},
data: [{ value: value }]
}
]
})
}
/** 初始化消息统计图表 */
const initMessageChart = () => {
//