| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- <template>
- <el-card class="chart-card" shadow="never" :loading="loading">
- <template #header>
- <div class="flex items-center">
- <span class="text-base font-medium text-gray-600">设备状态统计</span>
- </div>
- </template>
- <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">
- <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'
- import type { PropType } from 'vue'
- /** 【设备状态】统计卡片 */
- defineOptions({ name: 'DeviceStateCountCard' })
- const props = defineProps({
- statsData: {
- type: Object as PropType<IotStatisticsSummaryRespVO>,
- required: true
- },
- loading: {
- type: Boolean,
- default: false
- }
- })
- 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({
- 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 }]
- }
- ]
- })
- return chart
- } catch (error) {
- console.error('初始化图表失败:', error)
- return null
- }
- }
- /** 初始化所有图表 */
- 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')
- }
- })
- }
- /** 监听数据变化 */
- watch(
- () => props.statsData,
- () => {
- initCharts()
- },
- { deep: true }
- )
- /** 组件挂载时初始化图表 */
- onMounted(() => {
- initCharts()
- })
- </script>
|