lipenghui 3 months ago
parent
commit
f608d46d65
1 changed files with 702 additions and 353 deletions
  1. 702 353
      src/views/Home/Index.vue

+ 702 - 353
src/views/Home/Index.vue

@@ -1,401 +1,750 @@
 <template>
-  <div>
-    <el-card shadow="never">
-      <el-skeleton :loading="loading" animated>
-        <el-row :gutter="16" justify="space-between">
-          <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
-            <div class="flex items-center">
-              <el-avatar :src="avatar" :size="70" class="mr-16px">
-                <img src="@/assets/imgs/avatar.gif" alt="" />
-              </el-avatar>
-              <div>
-                <div class="text-20px">
-                  {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
-                </div>
-                <div class="mt-10px text-14px text-gray-500">
-                  {{ t('workplace.toady') }},20℃ - 32℃!
-                </div>
-              </div>
+  <!-- 第一行:统计卡片行 -->
+  <el-row :gutter="16" class="mb-4">
+    <el-col :span="6">
+      <el-card class="stat-card" shadow="never">
+        <div class="flex flex-col">
+          <div class="flex justify-between items-center text-gray-400">
+            <span>昨日工单数量</span>
+            <Icon icon="ep:menu" class="text-[32px] text-blue-400" />
+          </div>
+          <el-divider />
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >总数量</span
+            >
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >未完成</span
+            >
+          </div>
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-3xl font-bold text-gray-700">
+              {{ day.total }}
+            </span>
+            <span class="text-3xl font-bold text-gray-700">
+              {{ day.todo }}
+            </span>
+          </div>
+          <!--          <el-divider class="my-2" />-->
+          <!--          <div class="flex justify-between items-center text-gray-400 text-sm">-->
+          <!--            <span>今日新增</span>-->
+          <!--            <span class="text-green-500">+{{ statsData.productCategoryTodayCount }}</span>-->
+          <!--          </div>-->
+        </div>
+      </el-card>
+    </el-col>
+    <el-col :span="6">
+      <el-card class="stat-card" shadow="never">
+        <div class="flex flex-col">
+          <div class="flex justify-between items-center text-gray-400">
+            <span>近一周工单数量</span>
+            <Icon icon="ep:menu" class="text-[32px] text-blue-400" />
+          </div>
+          <el-divider />
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >总数量</span
+            >
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >未完成</span
+            >
+          </div>
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-3xl font-bold text-gray-700">
+              {{ week.total }}
+            </span>
+            <span class="text-3xl font-bold text-gray-700">
+              {{ week.todo }}
+            </span>
+          </div>
+        </div>
+      </el-card>
+    </el-col>
+    <el-col :span="6">
+      <el-card class="stat-card" shadow="never">
+        <div class="flex flex-col">
+          <div class="flex justify-between items-center text-gray-400">
+            <span>近一月工单数量</span>
+            <Icon icon="ep:menu" class="text-[32px] text-blue-400" />
+          </div>
+          <el-divider />
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >总数量</span
+            >
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >未完成</span
+            >
+          </div>
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-3xl font-bold text-gray-700">
+              {{ month.total }}
+            </span>
+            <span class="text-3xl font-bold text-gray-700">
+              {{ month.todo }}
+            </span>
+          </div>
+        </div>
+      </el-card>
+    </el-col>
+    <el-col :span="6">
+      <el-card class="stat-card" shadow="never">
+        <div class="flex flex-col">
+          <div class="flex justify-between items-center text-gray-400">
+            <span>工单数量</span>
+            <Icon icon="ep:menu" class="text-[32px] text-blue-400" />
+          </div>
+          <el-divider />
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >总数量</span
+            >
+            <span class="text-gray-500 text-base font-medium" style="font-size: 14px"
+            >未完成</span
+            >
+          </div>
+          <div class="flex justify-between items-center mb-1">
+            <span class="text-3xl font-bold text-gray-700">
+              {{ total.total }}
+            </span>
+            <span class="text-3xl font-bold text-gray-700">
+              {{ total.todo }}
+            </span>
+          </div>
+          <!--          <el-divider class="my-2" />-->
+          <!--          <div class="flex justify-between items-center text-gray-400 text-sm">-->
+          <!--            <span>今日新增</span>-->
+          <!--            <span class="text-green-500">+{{ statsData.productCategoryTodayCount }}</span>-->
+          <!--          </div>-->
+        </div>
+      </el-card>
+    </el-col>
+  </el-row>
+
+  <!-- 第二行:图表行 -->
+  <el-row :gutter="16" class="mb-4">
+    <el-col :span="8">
+      <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-[220px]">
+          <el-col :span="12" class="flex flex-col items-center">
+            <div ref="writeChartRef" 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 :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
-            <div class="h-70px flex items-center justify-end lt-sm:mt-10px">
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.project"
-                  :duration="2600"
-                />
-              </div>
-              <el-divider direction="vertical" />
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.todo"
-                  :duration="2600"
-                />
-              </div>
-              <el-divider direction="vertical" border-style="dashed" />
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.access') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.access"
-                  :duration="2600"
-                />
-              </div>
+          <el-col :span="12" class="flex flex-col items-center">
+            <div ref="finishedChartRef" 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-skeleton>
-    </el-card>
-  </div>
-
-  <el-row class="mt-8px" :gutter="8" justify="space-between">
-    <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px">
-      <!--
-      <el-card shadow="never">
+      </el-card>
+    </el-col>
+    <el-col :span="8">
+      <el-card class="chart-card" shadow="never">
         <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.project') }}</span>
-            <el-link
-              type="primary"
-              :underline="false"
-              href="https://github.com/yudaocode"
-              target="_blank"
-            >
-              {{ t('action.more') }}
-            </el-link>
+          <div class="flex items-center">
+            <span class="text-base font-medium text-gray-600">工单类型统计</span>
           </div>
         </template>
-        <el-skeleton :loading="loading" animated>
-          <el-row>
-            <el-col
-              v-for="(item, index) in projects"
-              :key="`card-${index}`"
-              :xl="8"
-              :lg="8"
-              :md="8"
-              :sm="24"
-              :xs="24"
-            >
-              <el-card shadow="hover" class="mr-5px mt-5px">
-                <div class="flex items-center">
-                  <Icon :icon="item.icon" :size="25" class="mr-8px" />
-                  <span class="text-16px">{{ item.name }}</span>
-                </div>
-                <div class="mt-12px text-9px text-gray-400">{{ t(item.message) }}</div>
-                <div class="mt-12px flex justify-between text-12px text-gray-400">
-                  <span>{{ item.personal }}</span>
-                  <span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>
-                </div>
-              </el-card>
-            </el-col>
-          </el-row>
-        </el-skeleton>
+        <div ref="typeChartRef" class="h-[220px]"></div>
       </el-card>
-      -->
-      <!--
-      <el-card shadow="never" class="mt-8px">
-        <el-skeleton :loading="loading" animated>
-          <el-row :gutter="20" justify="space-between">
-            <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
-              <el-card shadow="hover" class="mb-8px">
-                <el-skeleton :loading="loading" animated>
-                  <Echart :options="pieOptionsData" :height="280" />
-                </el-skeleton>
-              </el-card>
-            </el-col>
-            <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
-              <el-card shadow="hover" class="mb-8px">
-                <el-skeleton :loading="loading" animated>
-                  <Echart :options="barOptionsData" :height="280" />
-                </el-skeleton>
-              </el-card>
-            </el-col>
-          </el-row>
-        </el-skeleton>
-      </el-card>
-      -->
     </el-col>
-    <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px">
-      <!--
-      <el-card shadow="never">
+    <el-col :span="8">
+      <el-card class="chart-card" shadow="never">
         <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.shortcutOperation') }}</span>
+          <div class="flex items-center">
+            <span class="text-base font-medium text-gray-600">今日工单状态统计</span>
           </div>
         </template>
-        <el-skeleton :loading="loading" animated>
-          <el-row>
-            <el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-8px">
-              <div class="flex items-center">
-                <Icon :icon="item.icon" class="mr-8px" />
-                <el-link type="default" :underline="false" @click="setWatermark(item.name)">
-                  {{ item.name }}
-                </el-link>
-              </div>
-            </el-col>
-          </el-row>
-        </el-skeleton>
+        <el-row class="h-[220px]">
+          <el-col :span="12" class="flex flex-col items-center">
+            <div ref="writeTodayChartRef" 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="12" class="flex flex-col items-center">
+            <div ref="finishedTodayChartRef" 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>
-      -->
-      <!--
-      <el-card shadow="never" class="mt-8px">
+    </el-col>
+  </el-row>
+
+  <!-- 第三行:消息统计行 -->
+  <el-row>
+    <el-col :span="24">
+      <el-card class="chart-card" shadow="never">
         <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.notice') }}</span>
-            <el-link type="primary" :underline="false">{{ t('action.more') }}</el-link>
+          <div class="flex items-center justify-between">
+            <span class="text-base font-medium text-gray-600">近一年数量统计</span>
           </div>
         </template>
-        <el-skeleton :loading="loading" animated>
-          <div v-for="(item, index) in notice" :key="`dynamics-${index}`">
-            <div class="flex items-center">
-              <el-avatar :src="avatar" :size="35" class="mr-16px">
-                <img src="@/assets/imgs/avatar.gif" alt="" />
-              </el-avatar>
-              <div>
-                <div class="text-14px">
-                  <Highlight :keys="item.keys.map((v) => t(v))">
-                    {{ item.type }} : {{ item.title }}
-                  </Highlight>
-                </div>
-                <div class="mt-16px text-12px text-gray-400">
-                  {{ formatTime(item.date, 'yyyy-MM-dd') }}
-                </div>
-              </div>
-            </div>
-            <el-divider />
-          </div>
-        </el-skeleton>
+        <div ref="chartContainer" class="h-[300px]"></div>
       </el-card>
-      -->
     </el-col>
   </el-row>
+
+  <!-- TODO 第四行:地图 -->
 </template>
-<script lang="ts" setup>
-import { set } from 'lodash-es'
-import { EChartsOption } from 'echarts'
-import { formatTime } from '@/utils'
-
-import { useUserStore } from '@/store/modules/user'
-import { useWatermark } from '@/hooks/web/useWatermark'
-import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
-import { pieOptions, barOptions } from './echarts-data'
-
-defineOptions({ name: 'Home' })
-
-const { t } = useI18n()
-const userStore = useUserStore()
-const { setWatermark } = useWatermark()
-const loading = ref(true)
-const avatar = userStore.getUser.avatar
-const username = userStore.getUser.nickname
-const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
-// 获取统计数
-let totalSate = reactive<WorkplaceTotal>({
-  project: 0,
-  access: 0,
+
+<script setup lang="ts" name="Index">
+import * as echarts from 'echarts/core'
+import { BarChart } from 'echarts/charts'; // 显式导入柱状图模块
+
+import {
+  GridComponent,
+  LegendComponent,
+  TitleComponent,
+  ToolboxComponent,
+  TooltipComponent
+} from 'echarts/components'
+import { GaugeChart, LineChart, PieChart } from 'echarts/charts'
+import { LabelLayout, UniversalTransition } from 'echarts/features'
+import { CanvasRenderer } from 'echarts/renderers'
+import {
+  IotStatisticsDeviceMessageSummaryRespVO,
+  IotStatisticsSummaryRespVO
+} from '@/api/iot/statistics'
+import { formatDate } from '@/utils/formatTime'
+import { IotStatApi } from '@/api/pms/stat'
+
+// TODO @super:参考下 /Users/yunai/Java/yudao-ui-admin-vue3/src/views/mall/home/index.vue,拆一拆组件
+
+/** IoT 首页 */
+defineOptions({ name: 'IoTHome' })
+
+// TODO @super:使用下 Echart 组件,参考 yudao-ui-admin-vue3/src/views/mall/home/components/TradeTrendCard.vue 等
+echarts.use([
+  TooltipComponent,
+  LegendComponent,
+  PieChart,
+  CanvasRenderer,
+  LabelLayout,
+  TitleComponent,
+  ToolboxComponent,
+  GridComponent,
+  LineChart,
+  UniversalTransition,
+  GaugeChart,
+  BarChart
+])
+
+const timeRange = ref('7d') // 修改默认选择为近一周
+const dateRange = ref<[Date, Date] | null>(null)
+
+const queryParams = reactive({
+  startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
+  endTime: Date.now() // 设置默认结束时间为当前时间
+})
+
+const typeChartRef = ref() // 设备数量统计的图表
+const reportingChartRef = ref() // 在线设备统计的图表
+const dealFinishedChartRef = ref() // 离线设备统计的图表
+const transOrderChartRef = ref() // 待激活设备统计的图表
+const orderFinishChartRef = ref()
+const deviceMessageCountChartRef = ref() // 上下行消息量统计的图表
+const writeChartRef = ref() // 上下行消息量统计的图表
+const finishedChartRef = ref() // 上下行消息量统计的图表
+const writeTodayChartRef = ref() // 上下行消息量统计的图表
+const finishedTodayChartRef = ref() // 上下行消息量统计的图表
+// 基础统计数据
+// TODO @super:初始为 -1,然后界面展示先是加载中?试试用 cursor 改哈
+const statsData = ref<IotStatisticsSummaryRespVO>({
+  productCategoryCount: 0,
+  productCount: 0,
+  deviceCount: 0,
+  deviceMessageCount: 0,
+  productCategoryTodayCount: 0,
+  productTodayCount: 0,
+  deviceTodayCount: 0,
+  deviceMessageTodayCount: 0,
+  deviceOnlineCount: 0,
+  deviceOfflineCount: 0,
+  deviceInactiveCount: 0,
+  productCategoryDeviceCounts: {}
+})
+
+const day = ref({
+  total: undefined,
+  todo: undefined
+})
+const week = ref({
+  total: undefined,
+  todo: undefined
+})
+const month = ref({
+  total: undefined,
+  todo: undefined
+})
+const total = ref({
+  total: undefined,
+  todo: undefined
+})
+
+const status = ref({
+  finished: 0,
+  todo: 0
+})
+const todayStatus = ref({
+  finished: 0,
   todo: 0
 })
+const typeData = ref({})
+// 消息统计数据
+const messageStats = ref<IotStatisticsDeviceMessageSummaryRespVO>({
+  upstreamCounts: {},
+  downstreamCounts: {}
+})
 
-const getCount = async () => {
-  const data = {
-    project: 40,
-    access: 2340,
-    todo: 10
+/** 处理快捷时间范围选择 */
+const handleTimeRangeChange = (timeRange: string) => {
+  const now = Date.now()
+  let startTime: number
+
+  // TODO @super:这个的计算,看看能不能结合 dayjs 简化。因为 1h、24h、7d 感觉是比较标准的。如果没有,抽到 utils/formatTime.ts 作为一个工具方法
+  switch (timeRange) {
+    case '1h':
+      startTime = now - 60 * 60 * 1000
+      break
+    case '24h':
+      startTime = now - 24 * 60 * 60 * 1000
+      break
+    case '7d':
+      startTime = now - 7 * 24 * 60 * 60 * 1000
+      break
+    default:
+      return
   }
-  totalSate = Object.assign(totalSate, data)
+
+  // 清空日期选择器
+  dateRange.value = null
+
+  // 更新查询参数
+  queryParams.startTime = startTime
+  queryParams.endTime = now
+
+  // 重新获取数据
+  getStats()
 }
 
-// 获取项目数
-let projects = reactive<Project[]>([])
-const getProject = async () => {
-  const data = [
-    {
-      name: 'ruoyi-vue-pro',
-      icon: 'akar-icons:github-fill',
-      message: 'https://github.com/YunaiV/ruoyi-vue-pro',
-      personal: 'Spring Boot 单体架构',
-      time: new Date()
-    },
-    {
-      name: 'yudao-ui-admin-vue3',
-      icon: 'logos:vue',
-      message: 'https://github.com/yudaocode/yudao-ui-admin-vue3',
-      personal: 'Vue3 + element-plus',
-      time: new Date()
-    },
-    {
-      name: 'yudao-ui-admin-vben',
-      icon: 'logos:vue',
-      message: 'https://github.com/yudaocode/yudao-ui-admin-vben',
-      personal: 'Vue3 + vben(antd)',
-      time: new Date()
-    },
-    {
-      name: 'yudao-cloud',
-      icon: 'akar-icons:github',
-      message: 'https://github.com/YunaiV/yudao-cloud',
-      personal: 'Spring Cloud 微服务架构',
-      time: new Date()
-    },
-    {
-      name: 'yudao-ui-mall-uniapp',
-      icon: 'logos:vue',
-      message: 'https://github.com/yudaocode/yudao-ui-admin-uniapp',
-      personal: 'Vue3 + uniapp',
-      time: new Date()
-    },
-    {
-      name: 'yudao-ui-admin-vue2',
-      icon: 'logos:vue',
-      message: 'https://github.com/yudaocode/yudao-ui-admin-vue2',
-      personal: 'Vue2 + element-ui',
-      time: new Date()
-    }
-  ]
-  projects = Object.assign(projects, data)
+/** 处理自定义日期范围选择 */
+const handleDateRangeChange = (value: [Date, Date] | null) => {
+  if (value) {
+    // 清空快捷选项
+    timeRange.value = ''
+
+    // 更新查询参数
+    queryParams.startTime = value[0].getTime()
+    queryParams.endTime = value[1].getTime()
+
+    // 重新获取数据
+    getStats()
+  }
 }
 
-// 获取通知公告
-let notice = reactive<Notice[]>([])
-const getNotice = async () => {
-  const data = [
-    {
-      title: '系统支持 JDK 8/17/21,Vue 2/3',
-      type: '通知',
-      keys: ['通知', '8', '17', '21', '2', '3'],
-      date: new Date()
-    },
-    {
-      title: '后端提供 Spring Boot 2.7/3.2 + Cloud 双架构',
-      type: '公告',
-      keys: ['公告', 'Boot', 'Cloud'],
-      date: new Date()
+/** 获取统计数据 */
+const getStats = async () => {
+  // 获取基础统计数据
+  IotStatApi.getMaintenanceDay().then((res) => {
+    day.value = res
+  })
+  IotStatApi.getMaintenanceWeek().then((res) => {
+    week.value = res
+  })
+  IotStatApi.getMaintenanceMonth().then((res) => {
+    month.value = res
+  })
+  IotStatApi.getMaintenanceTotal().then((res) => {
+    total.value = res
+  })
+  IotStatApi.getMaintenanceStatus().then((res) => {
+    debugger
+    status.value = res
+    initCharts()
+  })
+  IotStatApi.getMaintenanceTodayStatus().then((res) => {
+    todayStatus.value = res
+    debugger
+    initCharts()
+  })
+  IotStatApi.getMaintenanceType().then((res) => {
+    typeData.value = res
+    initCharts()
+  })
+  // IotStatApi.getInspectTodayStatus().then((res) => {
+  //   todayStatus.value = res
+  //   debugger
+  //   initCharts()
+  // })
+  // statsData.value = await ProductCategoryApi.getIotStatisticsSummary()
+
+  //
+  // // 获取消息统计数据
+  // messageStats.value = await ProductCategoryApi.getIotStatisticsDeviceMessageSummary(queryParams)
+
+  // 初始化图表
+}
+
+/** 初始化图表 */
+const initCharts = () => {
+  // 设备数量统计
+  echarts.init(typeChartRef.value).setOption({
+    tooltip: {
+      trigger: 'item'
     },
-    {
-      title: '全部开源,个人与企业可 100% 直接使用,无需授权',
-      type: '通知',
-      keys: ['通知', '无需授权'],
-      date: new Date()
+    legend: {
+      top: '5%',
+      right: '10%',
+      align: 'left',
+      orient: 'vertical',
+      icon: 'circle'
     },
-    {
-      title: '国内使用最广泛的快速开发平台,超 300+ 人贡献',
-      type: '公告',
-      keys: ['公告', '最广泛'],
-      date: new Date()
-    }
-  ]
-  notice = Object.assign(notice, data)
+    series: [
+      {
+        name: '',
+        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(typeData.value).map(([name, value]) => ({
+          name,
+          value
+        }))
+      }
+    ]
+  })
+  //待执行
+  initGaugeChart(
+    writeTodayChartRef.value,
+    todayStatus.value.todo === undefined ? 0 : todayStatus.value.todo,
+    '#05b'
+  )
+  //已执行
+  initGaugeChart(
+    finishedTodayChartRef.value,
+    todayStatus.value.finished === undefined ? 0 : todayStatus.value.finished,
+    '#f50'
+  )
+  // 待执行
+  initGaugeChart(
+    writeChartRef.value,
+    status.value.todo === undefined ? 0 : status.value.todo,
+    '#05b'
+  )
+  //已执行
+  initGaugeChart(
+    finishedChartRef.value,
+    status.value.finished === undefined ? 0 : status.value.finished,
+    '#f50'
+  )
+  // 消息量统计
+  //initMessageChart()
 }
 
-// 获取快捷入口
-let shortcut = reactive<Shortcut[]>([])
+/** 初始化仪表盘图表 */
+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 = () => {
+  // 获取所有时间戳并排序
+  // TODO @super:一些 idea 里的红色报错,要去处理掉噢。
+  const timestamps = Array.from(
+    new Set([
+      ...messageStats.value.upstreamCounts.map((item) => Number(Object.keys(item)[0])),
+      ...messageStats.value.downstreamCounts.map((item) => Number(Object.keys(item)[0]))
+    ])
+  ).sort((a, b) => a - b) // 确保时间戳从小到大排序
+
+  // 准备数据
+  const xdata = timestamps.map((ts) => formatDate(ts, 'YYYY-MM-DD HH:mm'))
+  const upData = timestamps.map((ts) => {
+    const item = messageStats.value.upstreamCounts.find(
+      (count) => Number(Object.keys(count)[0]) === ts
+    )
+    return item ? Object.values(item)[0] : 0
+  })
+  const downData = timestamps.map((ts) => {
+    const item = messageStats.value.downstreamCounts.find(
+      (count) => Number(Object.keys(count)[0]) === ts
+    )
+    return item ? Object.values(item)[0] : 0
+  })
 
-const getShortcut = async () => {
-  const data = [
-    {
-      name: 'Github',
-      icon: 'akar-icons:github-fill',
-      url: 'github.io'
+  // 配置图表
+  echarts.init(deviceMessageCountChartRef.value).setOption({
+    tooltip: {
+      trigger: 'axis',
+      backgroundColor: 'rgba(255, 255, 255, 0.9)',
+      borderColor: '#E5E7EB',
+      textStyle: {
+        color: '#374151'
+      }
     },
-    {
-      name: 'Vue',
-      icon: 'logos:vue',
-      url: 'vuejs.org'
+    legend: {
+      data: ['上行消息量', '下行消息量'],
+      textStyle: {
+        color: '#374151',
+        fontWeight: 500
+      }
     },
-    {
-      name: 'Vite',
-      icon: 'vscode-icons:file-type-vite',
-      url: 'https://vitejs.dev/'
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
     },
-    {
-      name: 'Angular',
-      icon: 'logos:angular-icon',
-      url: 'github.io'
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: xdata,
+      axisLine: {
+        lineStyle: {
+          color: '#E5E7EB'
+        }
+      },
+      axisLabel: {
+        color: '#6B7280'
+      }
     },
-    {
-      name: 'React',
-      icon: 'logos:react',
-      url: 'github.io'
+    yAxis: {
+      type: 'value',
+      axisLine: {
+        lineStyle: {
+          color: '#E5E7EB'
+        }
+      },
+      axisLabel: {
+        color: '#6B7280'
+      },
+      splitLine: {
+        lineStyle: {
+          color: '#F3F4F6'
+        }
+      }
     },
-    {
-      name: 'Webpack',
-      icon: 'logos:webpack',
-      url: 'github.io'
-    }
-  ]
-  shortcut = Object.assign(shortcut, data)
+    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)' }
+          ])
+        }
+      }
+    ]
+  })
 }
 
-// 用户来源
-/*
-const getUserAccessSource = async () => {
-  const data = [
-    { value: 335, name: 'analysis.directAccess' },
-    { value: 310, name: 'analysis.mailMarketing' },
-    { value: 234, name: 'analysis.allianceAdvertising' },
-    { value: 135, name: 'analysis.videoAdvertising' },
-    { value: 1548, name: 'analysis.searchEngines' }
-  ]
-  set(
-    pieOptionsData,
-    'legend.data',
-    data.map((v) => t(v.name))
-  )
-  pieOptionsData!.series![0].data = data.map((v) => {
-    return {
-      name: t(v.name),
-      value: v.value
-    }
+const chartContainer = ref(null)
+let chartInstance = null
+
+// 生成过去12个月份的标签 (格式: YYYY-MM)
+const generateMonthLabels = () => {
+  const months = []
+  const date = new Date()
+  for (let i = 11; i >= 0; i--) {
+    const tempDate = new Date(date.getFullYear(), date.getMonth() - i, 1)
+    const year = tempDate.getFullYear()
+    const month = String(tempDate.getMonth() + 1).padStart(2, '0')
+    months.push(`${year}-${month}`)
+  }
+  return months
+}
+
+// 模拟数据获取
+const fetchChartData = async () => {
+  // 模拟异步请求
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      resolve({
+        months: generateMonthLabels(),
+        faults: [20,30,100,40,20,50,70,80,60,90,100,100],
+        repairs: [10,30,90,30,10,20,60,50,22,34,70,85],
+      })
+    }, 300)
   })
-} */
-const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
-
-// 周活跃量
-const getWeeklyUserActivity = async () => {
-  const data = [
-    { value: 13253, name: 'analysis.monday' },
-    { value: 34235, name: 'analysis.tuesday' },
-    { value: 26321, name: 'analysis.wednesday' },
-    { value: 12340, name: 'analysis.thursday' },
-    { value: 24643, name: 'analysis.friday' },
-    { value: 1322, name: 'analysis.saturday' },
-    { value: 1324, name: 'analysis.sunday' }
-  ]
-  set(
-    barOptionsData,
-    'xAxis.data',
-    data.map((v) => t(v.name))
-  )
-  set(barOptionsData, 'series', [
-    {
-      name: t('analysis.activeQuantity'),
-      data: data.map((v) => v.value),
-      type: 'bar'
-    }
-  ])
 }
 
-const getAllApi = async () => {
-  await Promise.all([
-    getCount(),
-    getProject(),
-    getNotice(),
-    getShortcut(),
-    // getUserAccessSource(),
-    getWeeklyUserActivity()
-  ])
-  loading.value = false
+// 初始化图表配置
+const initChart = async () => {
+  if (!chartContainer.value) return
+
+  // 获取数据
+  const { months, faults, repairs } = await fetchChartData()
+
+  // ECharts配置
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'shadow'
+      },
+      formatter: (params) => {
+        return `${params[0].axisValue}<br/>
+                ${params[0].marker} ${params[0].seriesName}: ${params[0].value}`
+      }
+    },
+    legend: {
+      data: ['保养工单数量'],
+      top: 25
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      data: months,
+      axisLabel: {
+        rotate: 45,
+        margin: 15
+      }
+    },
+    yAxis: {
+      type: 'value',
+      axisLabel: {
+        formatter: (value) => Math.floor(value).toString()
+      }
+    },
+    series: [
+      {
+        name: '保养工单数量',
+        type: 'bar',
+        itemStyle: {
+          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            { offset: 0, color: '#2196df' },
+            { offset: 1, color: '#2196df' }
+          ])
+        },
+        emphasis: {
+          focus: 'series'
+        },
+        data: repairs
+      }
+    ]
+  }
+
+  // 初始化图表
+  chartInstance = echarts.init(chartContainer.value)
+  chartInstance.setOption(option)
+
+  // 窗口缩放监听
+  window.addEventListener('resize', handleResize)
+  handleResize()
 }
 
-getAllApi()
+// 自适应调整
+const handleResize = () => {
+  chartInstance?.resize()
+}
+/** 初始化 */
+onMounted(() => {
+  getStats()
+  initChart()
+})
+onUnmounted(() => {
+  chartInstance?.dispose()
+  window.removeEventListener('resize', handleResize)
+})
 </script>
+
+<style lang="scss" scoped>
+</style>