From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/views/iot/home/components/DeviceCountCard.vue |  131 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 131 insertions(+), 0 deletions(-)

diff --git a/src/views/iot/home/components/DeviceCountCard.vue b/src/views/iot/home/components/DeviceCountCard.vue
new file mode 100644
index 0000000..5514eff
--- /dev/null
+++ b/src/views/iot/home/components/DeviceCountCard.vue
@@ -0,0 +1,131 @@
+<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>
+    <div v-else 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'
+import type { PropType } from 'vue'
+
+/** 銆愯澶囨暟閲忋�戠粺璁″崱鐗� */
+defineOptions({ name: 'DeviceCountCard' })
+
+const props = defineProps({
+  statsData: {
+    type: Object as PropType<IotStatisticsSummaryRespVO>,
+    required: true
+  },
+  loading: {
+    type: Boolean,
+    default: false
+  }
+})
+
+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({
+      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
+            })
+          )
+        }
+      ]
+    })
+    return chart
+  } catch (error) {
+    console.error('鍒濆鍖栧浘琛ㄥけ璐�:', error)
+    return null
+  }
+}
+
+/** 鐩戝惉鏁版嵁鍙樺寲 */
+watch(
+  () => props.statsData,
+  () => {
+    // 浣跨敤 nextTick 纭繚 DOM 宸叉洿鏂�
+    nextTick(() => {
+      initChart()
+    })
+  },
+  { deep: true }
+)
+
+/** 缁勪欢鎸傝浇鏃跺垵濮嬪寲鍥捐〃 */
+onMounted(async () => {
+  // 浣跨敤 nextTick 纭繚 DOM 宸叉洿鏂�
+  await nextTick(() => {
+    initChart()
+  })
+})
+</script>

--
Gitblit v1.8.0