|
@@ -1,186 +1,156 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <!-- 第一行:统计卡片行 -->
|
|
|
|
|
- <el-row :gutter="16" class="mb-6">
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card class="stat-card stat-card-gradient-1" shadow="hover">
|
|
|
|
|
- <div class="stat-content">
|
|
|
|
|
- <div class="stat-header">
|
|
|
|
|
- <div class="stat-icon-wrapper">
|
|
|
|
|
- <Icon icon="ep:data-line" class="stat-icon" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-title">MTTR</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-divider class="stat-divider" />
|
|
|
|
|
- <div class="stat-body">
|
|
|
|
|
- <div class="stat-label">平均解决时间</div>
|
|
|
|
|
- <div class="stat-value">{{ statsData.productCategoryCount }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card class="stat-card stat-card-gradient-2" shadow="hover">
|
|
|
|
|
- <div class="stat-content">
|
|
|
|
|
- <div class="stat-header">
|
|
|
|
|
- <div class="stat-icon-wrapper">
|
|
|
|
|
- <Icon icon="ep:trend-charts" class="stat-icon" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-title">近一周工单数量</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-divider class="stat-divider" />
|
|
|
|
|
- <div class="stat-body">
|
|
|
|
|
- <div class="stat-item">
|
|
|
|
|
- <div class="stat-sub-label">故障上报</div>
|
|
|
|
|
- <div class="stat-sub-value text-blue-500">{{ week.failureWeek }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-item">
|
|
|
|
|
- <div class="stat-sub-label">维修工单</div>
|
|
|
|
|
- <div class="stat-sub-value text-white">{{ week.maintainWeek }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card class="stat-card stat-card-gradient-3" shadow="hover">
|
|
|
|
|
- <div class="stat-content">
|
|
|
|
|
- <div class="stat-header">
|
|
|
|
|
- <div class="stat-icon-wrapper">
|
|
|
|
|
- <Icon icon="ep:calendar" class="stat-icon" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-title">近一月工单数量</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-divider class="stat-divider" />
|
|
|
|
|
- <div class="stat-body">
|
|
|
|
|
- <div class="stat-item">
|
|
|
|
|
- <div class="stat-sub-label">故障上报</div>
|
|
|
|
|
- <div class="stat-sub-value text-blue-500">{{ month.failureMonth }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-item">
|
|
|
|
|
- <div class="stat-sub-label">维修工单</div>
|
|
|
|
|
- <div class="stat-sub-value text-white">{{ month.maintainMonth }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card class="stat-card stat-card-gradient-4" shadow="hover">
|
|
|
|
|
- <div class="stat-content">
|
|
|
|
|
- <div class="stat-header">
|
|
|
|
|
- <div class="stat-icon-wrapper">
|
|
|
|
|
- <Icon icon="ep:pie-chart" class="stat-icon" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-title">工单总数量</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-divider class="stat-divider" />
|
|
|
|
|
- <div class="stat-body">
|
|
|
|
|
- <div class="stat-item">
|
|
|
|
|
- <div class="stat-sub-label">故障上报</div>
|
|
|
|
|
- <div class="stat-sub-value text-blue-500">{{ total.failureTotal }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="stat-item">
|
|
|
|
|
- <div class="stat-sub-label">维修工单</div>
|
|
|
|
|
- <div class="stat-sub-value text-white">{{ total.maintainTotal }}</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 第二行:图表行 -->
|
|
|
|
|
- <el-row :gutter="16" class="mb-6">
|
|
|
|
|
- <el-col :span="12">
|
|
|
|
|
- <el-card class="chart-card-enhanced" shadow="hover" style="border: none">
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <div class="chart-header">
|
|
|
|
|
- <div class="chart-title-wrapper">
|
|
|
|
|
- <div class="chart-title-dot"></div>
|
|
|
|
|
- <span class="chart-title">故障上报状态统计</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
- <el-row class="chart-grid">
|
|
|
|
|
- <el-col :span="6" class="chart-item">
|
|
|
|
|
- <div ref="reportingChartRef" class="gauge-chart"></div>
|
|
|
|
|
- <div class="chart-label">
|
|
|
|
|
- <span class="label-dot label-dot-blue"></span>
|
|
|
|
|
- <span class="label-text">上报中</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6" class="chart-item">
|
|
|
|
|
- <div ref="dealFinishedChartRef" class="gauge-chart"></div>
|
|
|
|
|
- <div class="chart-label">
|
|
|
|
|
- <span class="label-dot label-dot-orange"></span>
|
|
|
|
|
- <span class="label-text">处理完成</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6" class="chart-item">
|
|
|
|
|
- <div ref="transOrderChartRef" class="gauge-chart"></div>
|
|
|
|
|
- <div class="chart-label">
|
|
|
|
|
- <span class="label-dot label-dot-purple"></span>
|
|
|
|
|
- <span class="label-text">转工单</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="6" class="chart-item">
|
|
|
|
|
- <div ref="orderFinishChartRef" class="gauge-chart"></div>
|
|
|
|
|
- <div class="chart-label">
|
|
|
|
|
- <span class="label-dot label-dot-cyan"></span>
|
|
|
|
|
- <span class="label-text">工单处理完成</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="12">
|
|
|
|
|
- <el-card class="chart-card-enhanced" shadow="hover" style="border: none">
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <div class="chart-header">
|
|
|
|
|
- <div class="chart-title-wrapper">
|
|
|
|
|
- <div class="chart-title-dot"></div>
|
|
|
|
|
- <span class="chart-title">维修工单状态统计</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
- <el-row class="chart-grid">
|
|
|
|
|
- <el-col :span="12" class="chart-item">
|
|
|
|
|
- <div ref="writeChartRef" class="gauge-chart"></div>
|
|
|
|
|
- <div class="chart-label">
|
|
|
|
|
- <span class="label-dot label-dot-indigo"></span>
|
|
|
|
|
- <span class="label-text">待填写</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="12" class="chart-item">
|
|
|
|
|
- <div ref="finishedChartRef" class="gauge-chart"></div>
|
|
|
|
|
- <div class="chart-label">
|
|
|
|
|
- <span class="label-dot label-dot-emerald"></span>
|
|
|
|
|
- <span class="label-text">已完成</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 第三行:消息统计行 -->
|
|
|
|
|
- <el-row>
|
|
|
|
|
- <el-col :span="24">
|
|
|
|
|
- <el-card class="chart-card-enhanced" shadow="hover" style="border: none">
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <div class="chart-header">
|
|
|
|
|
- <div class="chart-title-wrapper">
|
|
|
|
|
- <div class="chart-title-dot"></div>
|
|
|
|
|
- <span class="chart-title">近一年数量统计</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </template>
|
|
|
|
|
- <div ref="chartContainer" class="bar-chart-container"></div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
-
|
|
|
|
|
- <!-- TODO 第四行:地图 -->
|
|
|
|
|
|
|
+ <div ref="wrapperRef" class="maintain-kb-wrapper bg absolute top-0 left-0 size-full z-10">
|
|
|
|
|
+ <div class="mx-a overflow-hidden" :style="targetWrapperStyle">
|
|
|
|
|
+ <div class="maintain-kb-screen bg" :style="targetAreaStyle">
|
|
|
|
|
+ <header class="header">维修统计</header>
|
|
|
|
|
+
|
|
|
|
|
+ <main class="maintain-kb-content">
|
|
|
|
|
+ <section class="summary-grid">
|
|
|
|
|
+ <button class="summary-card stat-card" type="button">
|
|
|
|
|
+ <span class="stat-icon-wrapper">
|
|
|
|
|
+ <Icon icon="ep:data-line" class="stat-icon" />
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="stat-title">MTTR</span>
|
|
|
|
|
+ <strong class="stat-value">{{ statsData.productCategoryCount }}</strong>
|
|
|
|
|
+ <span class="stat-label">平均解决时间</span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+
|
|
|
|
|
+ <button class="summary-card stat-card" type="button">
|
|
|
|
|
+ <span class="stat-icon-wrapper">
|
|
|
|
|
+ <Icon icon="ep:trend-charts" class="stat-icon" />
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="stat-title">近一周工单数量</span>
|
|
|
|
|
+ <span class="stat-pair">
|
|
|
|
|
+ <span>
|
|
|
|
|
+ <em>故障上报</em>
|
|
|
|
|
+ <strong>{{ week.failureWeek }}</strong>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span>
|
|
|
|
|
+ <em>维修工单</em>
|
|
|
|
|
+ <strong>{{ week.maintainWeek }}</strong>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+
|
|
|
|
|
+ <button class="summary-card stat-card" type="button">
|
|
|
|
|
+ <span class="stat-icon-wrapper">
|
|
|
|
|
+ <Icon icon="ep:calendar" class="stat-icon" />
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="stat-title">近一月工单数量</span>
|
|
|
|
|
+ <span class="stat-pair">
|
|
|
|
|
+ <span>
|
|
|
|
|
+ <em>故障上报</em>
|
|
|
|
|
+ <strong>{{ month.failureMonth }}</strong>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span>
|
|
|
|
|
+ <em>维修工单</em>
|
|
|
|
|
+ <strong>{{ month.maintainMonth }}</strong>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+
|
|
|
|
|
+ <button class="summary-card stat-card" type="button">
|
|
|
|
|
+ <span class="stat-icon-wrapper">
|
|
|
|
|
+ <Icon icon="ep:pie-chart" class="stat-icon" />
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="stat-title">工单总数量</span>
|
|
|
|
|
+ <span class="stat-pair">
|
|
|
|
|
+ <span>
|
|
|
|
|
+ <em>故障上报</em>
|
|
|
|
|
+ <strong>{{ total.failureTotal }}</strong>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span>
|
|
|
|
|
+ <em>维修工单</em>
|
|
|
|
|
+ <strong>{{ total.maintainTotal }}</strong>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
|
|
+ <section class="status-grid">
|
|
|
|
|
+ <el-card class="panel chart-panel" shadow="never">
|
|
|
|
|
+ <div class="panel-title">
|
|
|
|
|
+ <div class="icon-decorator">
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ 故障上报状态统计
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="gauge-grid gauge-grid--failure">
|
|
|
|
|
+ <div class="chart-item">
|
|
|
|
|
+ <div ref="reportingChartRef" class="gauge-chart"></div>
|
|
|
|
|
+ <div class="chart-label">
|
|
|
|
|
+ <span class="label-dot label-dot-blue"></span>
|
|
|
|
|
+ <span class="label-text">上报中</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="chart-item">
|
|
|
|
|
+ <div ref="dealFinishedChartRef" class="gauge-chart"></div>
|
|
|
|
|
+ <div class="chart-label">
|
|
|
|
|
+ <span class="label-dot label-dot-orange"></span>
|
|
|
|
|
+ <span class="label-text">处理完成</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="chart-item">
|
|
|
|
|
+ <div ref="transOrderChartRef" class="gauge-chart"></div>
|
|
|
|
|
+ <div class="chart-label">
|
|
|
|
|
+ <span class="label-dot label-dot-purple"></span>
|
|
|
|
|
+ <span class="label-text">转工单</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="chart-item">
|
|
|
|
|
+ <div ref="orderFinishChartRef" class="gauge-chart"></div>
|
|
|
|
|
+ <div class="chart-label">
|
|
|
|
|
+ <span class="label-dot label-dot-cyan"></span>
|
|
|
|
|
+ <span class="label-text">工单处理完成</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <el-card class="panel chart-panel" shadow="never">
|
|
|
|
|
+ <div class="panel-title">
|
|
|
|
|
+ <div class="icon-decorator">
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ 维修工单状态统计
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="gauge-grid gauge-grid--maintain">
|
|
|
|
|
+ <div class="chart-item">
|
|
|
|
|
+ <div ref="writeChartRef" class="gauge-chart"></div>
|
|
|
|
|
+ <div class="chart-label">
|
|
|
|
|
+ <span class="label-dot label-dot-indigo"></span>
|
|
|
|
|
+ <span class="label-text">待填写</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="chart-item">
|
|
|
|
|
+ <div ref="finishedChartRef" class="gauge-chart"></div>
|
|
|
|
|
+ <div class="chart-label">
|
|
|
|
|
+ <span class="label-dot label-dot-emerald"></span>
|
|
|
|
|
+ <span class="label-text">已完成</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
|
|
+ <section class="trend-section">
|
|
|
|
|
+ <el-card class="panel trend-panel" shadow="never">
|
|
|
|
|
+ <div class="panel-title">
|
|
|
|
|
+ <div class="icon-decorator">
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ 近一年数量统计
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div ref="chartContainer" class="bar-chart-container"></div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </section>
|
|
|
|
|
+ </main>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts" name="Index">
|
|
<script setup lang="ts" name="Index">
|
|
@@ -203,6 +173,7 @@ import {
|
|
|
} from '@/api/iot/statistics'
|
|
} from '@/api/iot/statistics'
|
|
|
import { formatDate } from '@/utils/formatTime'
|
|
import { formatDate } from '@/utils/formatTime'
|
|
|
import { IotStatApi } from '@/api/pms/stat'
|
|
import { IotStatApi } from '@/api/pms/stat'
|
|
|
|
|
+import { DESIGN_HEIGHT, DESIGN_WIDTH } from '@/utils/kb'
|
|
|
|
|
|
|
|
// TODO @super:参考下 /Users/yunai/Java/yudao-ui-admin-vue3/src/views/mall/home/index.vue,拆一拆组件
|
|
// TODO @super:参考下 /Users/yunai/Java/yudao-ui-admin-vue3/src/views/mall/home/index.vue,拆一拆组件
|
|
|
|
|
|
|
@@ -232,6 +203,37 @@ const queryParams = reactive({
|
|
|
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
|
|
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
|
|
|
endTime: Date.now() // 设置默认结束时间为当前时间
|
|
endTime: Date.now() // 设置默认结束时间为当前时间
|
|
|
})
|
|
})
|
|
|
|
|
+const wrapperRef = ref<HTMLDivElement>()
|
|
|
|
|
+const scale = ref(1)
|
|
|
|
|
+
|
|
|
|
|
+let resizeObserver: ResizeObserver | null = null
|
|
|
|
|
+let resizeRaf = 0
|
|
|
|
|
+
|
|
|
|
|
+const targetWrapperStyle = computed(() => ({
|
|
|
|
|
+ width: `${DESIGN_WIDTH * scale.value}px`,
|
|
|
|
|
+ height: `${DESIGN_HEIGHT * scale.value}px`
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+const targetAreaStyle = computed(() => ({
|
|
|
|
|
+ '--kb-scale': scale.value,
|
|
|
|
|
+ width: `${DESIGN_WIDTH * scale.value}px`,
|
|
|
|
|
+ height: `${DESIGN_HEIGHT * scale.value}px`
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+function updateScale() {
|
|
|
|
|
+ cancelAnimationFrame(resizeRaf)
|
|
|
|
|
+
|
|
|
|
|
+ resizeRaf = requestAnimationFrame(() => {
|
|
|
|
|
+ const wrapper = wrapperRef.value
|
|
|
|
|
+ if (!wrapper) return
|
|
|
|
|
+
|
|
|
|
|
+ const { clientWidth, clientHeight } = wrapper
|
|
|
|
|
+ if (!clientWidth || !clientHeight) return
|
|
|
|
|
+
|
|
|
|
|
+ scale.value = Math.min(clientWidth / DESIGN_WIDTH, clientHeight / DESIGN_HEIGHT)
|
|
|
|
|
+ nextTick(handleResize)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// const deviceCountChartRef = ref() // 设备数量统计的图表
|
|
// const deviceCountChartRef = ref() // 设备数量统计的图表
|
|
|
const reportingChartRef = ref() // 在线设备统计的图表
|
|
const reportingChartRef = ref() // 在线设备统计的图表
|
|
@@ -241,6 +243,7 @@ const orderFinishChartRef = ref()
|
|
|
const deviceMessageCountChartRef = ref() // 上下行消息量统计的图表
|
|
const deviceMessageCountChartRef = ref() // 上下行消息量统计的图表
|
|
|
const writeChartRef = ref() // 上下行消息量统计的图表
|
|
const writeChartRef = ref() // 上下行消息量统计的图表
|
|
|
const finishedChartRef = ref() // 上下行消息量统计的图表
|
|
const finishedChartRef = ref() // 上下行消息量统计的图表
|
|
|
|
|
+const chartInstances: echarts.ECharts[] = []
|
|
|
|
|
|
|
|
// 基础统计数据
|
|
// 基础统计数据
|
|
|
// TODO @super:初始为 -1,然后界面展示先是加载中?试试用 cursor 改哈
|
|
// TODO @super:初始为 -1,然后界面展示先是加载中?试试用 cursor 改哈
|
|
@@ -357,7 +360,6 @@ const getStats = async () => {
|
|
|
})
|
|
})
|
|
|
IotStatApi.getMainStatus().then((res) => {
|
|
IotStatApi.getMainStatus().then((res) => {
|
|
|
status.value = res
|
|
status.value = res
|
|
|
- console.log(JSON.stringify(status.value))
|
|
|
|
|
initCharts()
|
|
initCharts()
|
|
|
})
|
|
})
|
|
|
// statsData.value = await ProductCategoryApi.getIotStatisticsSummary()
|
|
// statsData.value = await ProductCategoryApi.getIotStatisticsSummary()
|
|
@@ -371,6 +373,7 @@ const getStats = async () => {
|
|
|
|
|
|
|
|
/** 初始化图表 */
|
|
/** 初始化图表 */
|
|
|
const initCharts = () => {
|
|
const initCharts = () => {
|
|
|
|
|
+ disposeChartInstances()
|
|
|
// 设备数量统计
|
|
// 设备数量统计
|
|
|
// echarts.init(deviceCountChartRef.value).setOption({
|
|
// echarts.init(deviceCountChartRef.value).setOption({
|
|
|
// tooltip: {
|
|
// tooltip: {
|
|
@@ -456,7 +459,11 @@ const initCharts = () => {
|
|
|
|
|
|
|
|
/** 初始化仪表盘图表 */
|
|
/** 初始化仪表盘图表 */
|
|
|
const initGaugeChart = (el: any, value: number, color: string) => {
|
|
const initGaugeChart = (el: any, value: number, color: string) => {
|
|
|
- echarts.init(el).setOption({
|
|
|
|
|
|
|
+ if (!el) return
|
|
|
|
|
+
|
|
|
|
|
+ const chart = echarts.init(el)
|
|
|
|
|
+ chartInstances.push(chart)
|
|
|
|
|
+ chart.setOption({
|
|
|
series: [
|
|
series: [
|
|
|
{
|
|
{
|
|
|
type: 'gauge',
|
|
type: 'gauge',
|
|
@@ -620,7 +627,11 @@ const initMessageChart = () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const chartContainer = ref(null)
|
|
const chartContainer = ref(null)
|
|
|
-let chartInstance = null
|
|
|
|
|
|
|
+let chartInstance: echarts.ECharts | null = null
|
|
|
|
|
+
|
|
|
|
|
+const disposeChartInstances = () => {
|
|
|
|
|
+ chartInstances.splice(0).forEach((instance) => instance.dispose())
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// 生成过去12个月份的标签 (格式: YYYY-MM)
|
|
// 生成过去12个月份的标签 (格式: YYYY-MM)
|
|
|
const generateMonthLabels = () => {
|
|
const generateMonthLabels = () => {
|
|
@@ -731,49 +742,87 @@ const initChart = async () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 初始化图表
|
|
// 初始化图表
|
|
|
|
|
+ chartInstance?.dispose()
|
|
|
chartInstance = echarts.init(chartContainer.value)
|
|
chartInstance = echarts.init(chartContainer.value)
|
|
|
chartInstance.setOption(option)
|
|
chartInstance.setOption(option)
|
|
|
|
|
|
|
|
- // 窗口缩放监听
|
|
|
|
|
- window.addEventListener('resize', handleResize)
|
|
|
|
|
handleResize()
|
|
handleResize()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 自适应调整
|
|
// 自适应调整
|
|
|
const handleResize = () => {
|
|
const handleResize = () => {
|
|
|
chartInstance?.resize()
|
|
chartInstance?.resize()
|
|
|
|
|
+ chartInstances.forEach((instance) => instance.resize())
|
|
|
}
|
|
}
|
|
|
/** 初始化 */
|
|
/** 初始化 */
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
|
|
+ nextTick(updateScale)
|
|
|
|
|
+ resizeObserver = new ResizeObserver(updateScale)
|
|
|
|
|
+ if (wrapperRef.value) {
|
|
|
|
|
+ resizeObserver.observe(wrapperRef.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ window.addEventListener('resize', updateScale)
|
|
|
|
|
+
|
|
|
getStats()
|
|
getStats()
|
|
|
initChart()
|
|
initChart()
|
|
|
})
|
|
})
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
|
|
|
+ resizeObserver?.disconnect()
|
|
|
|
|
+ window.removeEventListener('resize', updateScale)
|
|
|
|
|
+ cancelAnimationFrame(resizeRaf)
|
|
|
|
|
+ disposeChartInstances()
|
|
|
chartInstance?.dispose()
|
|
chartInstance?.dispose()
|
|
|
- window.removeEventListener('resize', handleResize)
|
|
|
|
|
|
|
+ chartInstance = null
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
|
|
|
+@import url('@/styles/kb.scss');
|
|
|
|
|
+
|
|
|
|
|
+// 响应式优化
|
|
|
|
|
+@media (width <= 1200px) {
|
|
|
|
|
+ .stat-value {
|
|
|
|
|
+ font-size: 28px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .stat-sub-value {
|
|
|
|
|
+ font-size: 24px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@media (width <= 768px) {
|
|
|
|
|
+ .stat-card {
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .chart-item {
|
|
|
|
|
+ padding: 8px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .gauge-chart {
|
|
|
|
|
+ height: 140px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 统计卡片基础样式
|
|
// 统计卡片基础样式
|
|
|
.stat-card {
|
|
.stat-card {
|
|
|
- border-radius: 12px;
|
|
|
|
|
- border: 1px solid rgba(207, 220, 237, 0.9);
|
|
|
|
|
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
position: relative;
|
|
position: relative;
|
|
|
- background: linear-gradient(180deg, #ffffff 0%, #f4f8ff 100%);
|
|
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: linear-gradient(180deg, #fff 0%, #f4f8ff 100%);
|
|
|
|
|
+ border: 1px solid rgb(207 220 237 / 90%);
|
|
|
|
|
+ border-radius: 12px;
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.95),
|
|
|
|
|
- inset 0 -10px 24px rgba(210, 225, 244, 0.26),
|
|
|
|
|
- 0 10px 24px rgba(32, 66, 120, 0.08);
|
|
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 95%),
|
|
|
|
|
+ inset 0 -10px 24px rgb(210 225 244 / 26%),
|
|
|
|
|
+ 0 10px 24px rgb(32 66 120 / 8%);
|
|
|
|
|
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
|
|
|
|
&:hover {
|
|
&:hover {
|
|
|
transform: translateY(-4px);
|
|
transform: translateY(-4px);
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.98),
|
|
|
|
|
- inset 0 -12px 26px rgba(204, 220, 243, 0.32),
|
|
|
|
|
- 0 16px 32px rgba(32, 66, 120, 0.12);
|
|
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 98%),
|
|
|
|
|
+ inset 0 -12px 26px rgb(204 220 243 / 32%),
|
|
|
|
|
+ 0 16px 32px rgb(32 66 120 / 12%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
:deep(.el-card__body) {
|
|
@@ -783,23 +832,23 @@ onUnmounted(() => {
|
|
|
|
|
|
|
|
// 渐变色背景
|
|
// 渐变色背景
|
|
|
.stat-card-gradient-1 {
|
|
.stat-card-gradient-1 {
|
|
|
- background: radial-gradient(circle at top left, rgba(121, 164, 255, 0.16), transparent 42%),
|
|
|
|
|
- linear-gradient(180deg, #ffffff 0%, #f4f8ff 100%);
|
|
|
|
|
|
|
+ background: radial-gradient(circle at top left, rgb(121 164 255 / 16%), transparent 42%),
|
|
|
|
|
+ linear-gradient(180deg, #fff 0%, #f4f8ff 100%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-card-gradient-2 {
|
|
.stat-card-gradient-2 {
|
|
|
- background: radial-gradient(circle at top left, rgba(118, 186, 255, 0.14), transparent 42%),
|
|
|
|
|
- linear-gradient(180deg, #ffffff 0%, #f3f8ff 100%);
|
|
|
|
|
|
|
+ background: radial-gradient(circle at top left, rgb(118 186 255 / 14%), transparent 42%),
|
|
|
|
|
+ linear-gradient(180deg, #fff 0%, #f3f8ff 100%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-card-gradient-3 {
|
|
.stat-card-gradient-3 {
|
|
|
- background: radial-gradient(circle at top left, rgba(96, 154, 241, 0.14), transparent 42%),
|
|
|
|
|
- linear-gradient(180deg, #ffffff 0%, #f5f9ff 100%);
|
|
|
|
|
|
|
+ background: radial-gradient(circle at top left, rgb(96 154 241 / 14%), transparent 42%),
|
|
|
|
|
+ linear-gradient(180deg, #fff 0%, #f5f9ff 100%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-card-gradient-4 {
|
|
.stat-card-gradient-4 {
|
|
|
- background: radial-gradient(circle at top left, rgba(137, 176, 242, 0.14), transparent 42%),
|
|
|
|
|
- linear-gradient(180deg, #ffffff 0%, #f4f8fe 100%);
|
|
|
|
|
|
|
+ background: radial-gradient(circle at top left, rgb(137 176 242 / 14%), transparent 42%),
|
|
|
|
|
+ linear-gradient(180deg, #fff 0%, #f4f8fe 100%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 统计内容区域
|
|
// 统计内容区域
|
|
@@ -816,18 +865,18 @@ onUnmounted(() => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-icon-wrapper {
|
|
.stat-icon-wrapper {
|
|
|
|
|
+ display: flex;
|
|
|
width: 48px;
|
|
width: 48px;
|
|
|
height: 48px;
|
|
height: 48px;
|
|
|
- background: linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0%, rgba(232, 240, 252, 0.92) 100%);
|
|
|
|
|
- border: 1px solid rgba(205, 219, 239, 0.9);
|
|
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 96%) 0%, rgb(232 240 252 / 92%) 100%);
|
|
|
|
|
+ border: 1px solid rgb(205 219 239 / 90%);
|
|
|
border-radius: 12px;
|
|
border-radius: 12px;
|
|
|
- display: flex;
|
|
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 95%),
|
|
|
|
|
+ inset 0 -6px 12px rgb(205 220 242 / 34%),
|
|
|
|
|
+ 0 6px 14px rgb(66 104 168 / 8%);
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
justify-content: center;
|
|
|
- box-shadow:
|
|
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.95),
|
|
|
|
|
- inset 0 -6px 12px rgba(205, 220, 242, 0.34),
|
|
|
|
|
- 0 6px 14px rgba(66, 104, 168, 0.08);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-icon {
|
|
.stat-icon {
|
|
@@ -842,8 +891,8 @@ onUnmounted(() => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-divider {
|
|
.stat-divider {
|
|
|
- border-color: rgba(201, 214, 234, 0.85);
|
|
|
|
|
margin: 12px 0;
|
|
margin: 12px 0;
|
|
|
|
|
+ border-color: rgb(201 214 234 / 85%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-body {
|
|
.stat-body {
|
|
@@ -853,16 +902,16 @@ onUnmounted(() => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-label {
|
|
.stat-label {
|
|
|
|
|
+ margin-bottom: 4px;
|
|
|
font-size: 13px;
|
|
font-size: 13px;
|
|
|
color: #7182a1;
|
|
color: #7182a1;
|
|
|
- margin-bottom: 4px;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-value {
|
|
.stat-value {
|
|
|
font-size: 36px;
|
|
font-size: 36px;
|
|
|
font-weight: 700;
|
|
font-weight: 700;
|
|
|
- color: #1f2f54;
|
|
|
|
|
line-height: 1;
|
|
line-height: 1;
|
|
|
|
|
+ color: #1f2f54;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.stat-item {
|
|
.stat-item {
|
|
@@ -885,32 +934,28 @@ onUnmounted(() => {
|
|
|
|
|
|
|
|
// 图表卡片增强样式
|
|
// 图表卡片增强样式
|
|
|
.chart-card-enhanced {
|
|
.chart-card-enhanced {
|
|
|
|
|
+ background: linear-gradient(180deg, #fff 0%, #f5f9ff 100%);
|
|
|
|
|
+ border: 1px solid rgb(207 220 237 / 90%);
|
|
|
border-radius: 12px;
|
|
border-radius: 12px;
|
|
|
- border: 1px solid rgba(207, 220, 237, 0.9);
|
|
|
|
|
- transition: all 0.3s ease;
|
|
|
|
|
- background: linear-gradient(180deg, #ffffff 0%, #f5f9ff 100%);
|
|
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.96),
|
|
|
|
|
- inset 0 -12px 26px rgba(212, 226, 244, 0.2),
|
|
|
|
|
- 0 10px 24px rgba(32, 66, 120, 0.07);
|
|
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 96%),
|
|
|
|
|
+ inset 0 -12px 26px rgb(212 226 244 / 20%),
|
|
|
|
|
+ 0 10px 24px rgb(32 66 120 / 7%);
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
|
|
|
&:hover {
|
|
&:hover {
|
|
|
|
|
+ border-color: rgb(193 209 231 / 95%);
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.98),
|
|
|
|
|
- inset 0 -14px 30px rgba(208, 223, 244, 0.24),
|
|
|
|
|
- 0 16px 30px rgba(32, 66, 120, 0.1);
|
|
|
|
|
- border-color: rgba(193, 209, 231, 0.95);
|
|
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 98%),
|
|
|
|
|
+ inset 0 -14px 30px rgb(208 223 244 / 24%),
|
|
|
|
|
+ 0 16px 30px rgb(32 66 120 / 10%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
:deep(.el-card__header) {
|
|
:deep(.el-card__header) {
|
|
|
padding: 16px 20px;
|
|
padding: 16px 20px;
|
|
|
- border-bottom: 1px solid rgba(223, 232, 245, 0.95);
|
|
|
|
|
- background: linear-gradient(
|
|
|
|
|
- 90deg,
|
|
|
|
|
- rgba(244, 248, 255, 0.96) 0%,
|
|
|
|
|
- rgba(255, 255, 255, 0.98) 100%
|
|
|
|
|
- );
|
|
|
|
|
- box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.78);
|
|
|
|
|
|
|
+ background: linear-gradient(90deg, rgb(244 248 255 / 96%) 0%, rgb(255 255 255 / 98%) 100%);
|
|
|
|
|
+ border-bottom: 1px solid rgb(223 232 245 / 95%);
|
|
|
|
|
+ box-shadow: inset 0 -1px 0 rgb(255 255 255 / 78%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
:deep(.el-card__body) {
|
|
@@ -933,18 +978,18 @@ onUnmounted(() => {
|
|
|
.chart-title-dot {
|
|
.chart-title-dot {
|
|
|
width: 8px;
|
|
width: 8px;
|
|
|
height: 8px;
|
|
height: 8px;
|
|
|
- border-radius: 50%;
|
|
|
|
|
background: linear-gradient(135deg, #84aef7 0%, #5e87d8 100%);
|
|
background: linear-gradient(135deg, #84aef7 0%, #5e87d8 100%);
|
|
|
|
|
+ border-radius: 50%;
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- 0 0 0 4px rgba(122, 162, 235, 0.12),
|
|
|
|
|
- 0 2px 6px rgba(86, 124, 189, 0.24);
|
|
|
|
|
|
|
+ 0 0 0 4px rgb(122 162 235 / 12%),
|
|
|
|
|
+ 0 2px 6px rgb(86 124 189 / 24%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.chart-title {
|
|
.chart-title {
|
|
|
font-size: 16px;
|
|
font-size: 16px;
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
- color: #233554;
|
|
|
|
|
letter-spacing: 0.3px;
|
|
letter-spacing: 0.3px;
|
|
|
|
|
+ color: #233554;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.chart-grid {
|
|
.chart-grid {
|
|
@@ -953,21 +998,17 @@ onUnmounted(() => {
|
|
|
|
|
|
|
|
.chart-item {
|
|
.chart-item {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
padding: 12px;
|
|
padding: 12px;
|
|
|
- transition: all 0.2s ease;
|
|
|
|
|
border-radius: 12px;
|
|
border-radius: 12px;
|
|
|
|
|
+ transition: all 0.2s ease;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
|
|
|
&:hover {
|
|
&:hover {
|
|
|
- background: linear-gradient(
|
|
|
|
|
- 180deg,
|
|
|
|
|
- rgba(244, 248, 255, 0.9) 0%,
|
|
|
|
|
- rgba(237, 244, 255, 0.95) 100%
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(244 248 255 / 90%) 0%, rgb(237 244 255 / 95%) 100%);
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.86),
|
|
|
|
|
- inset 0 -8px 16px rgba(212, 225, 243, 0.22);
|
|
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 86%),
|
|
|
|
|
+ inset 0 -8px 16px rgb(212 225 243 / 22%);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -978,103 +1019,315 @@ onUnmounted(() => {
|
|
|
|
|
|
|
|
.chart-label {
|
|
.chart-label {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 6px;
|
|
|
|
|
- margin-top: 8px;
|
|
|
|
|
padding: 6px 12px;
|
|
padding: 6px 12px;
|
|
|
- background: linear-gradient(180deg, rgba(255, 255, 255, 0.96) 0%, rgba(242, 247, 255, 0.96) 100%);
|
|
|
|
|
- border: 1px solid rgba(216, 228, 244, 0.95);
|
|
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 96%) 0%, rgb(242 247 255 / 96%) 100%);
|
|
|
|
|
+ border: 1px solid rgb(216 228 244 / 95%);
|
|
|
border-radius: 20px;
|
|
border-radius: 20px;
|
|
|
- transition: all 0.2s ease;
|
|
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.9),
|
|
|
|
|
- 0 4px 10px rgba(58, 90, 145, 0.06);
|
|
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 90%),
|
|
|
|
|
+ 0 4px 10px rgb(58 90 145 / 6%);
|
|
|
|
|
+ transition: all 0.2s ease;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
|
|
|
.chart-item:hover & {
|
|
.chart-item:hover & {
|
|
|
- background: linear-gradient(
|
|
|
|
|
- 180deg,
|
|
|
|
|
- rgba(255, 255, 255, 0.98) 0%,
|
|
|
|
|
- rgba(238, 244, 255, 0.98) 100%
|
|
|
|
|
- );
|
|
|
|
|
- border-color: rgba(201, 217, 239, 0.98);
|
|
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 98%) 0%, rgb(238 244 255 / 98%) 100%);
|
|
|
|
|
+ border-color: rgb(201 217 239 / 98%);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-dot {
|
|
.label-dot {
|
|
|
|
|
+ display: inline-block;
|
|
|
width: 8px;
|
|
width: 8px;
|
|
|
height: 8px;
|
|
height: 8px;
|
|
|
border-radius: 50%;
|
|
border-radius: 50%;
|
|
|
- display: inline-block;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-dot-blue {
|
|
.label-dot-blue {
|
|
|
background: #3b82f6;
|
|
background: #3b82f6;
|
|
|
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
|
|
|
|
|
|
|
+ box-shadow: 0 0 0 2px rgb(59 130 246 / 20%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-dot-orange {
|
|
.label-dot-orange {
|
|
|
background: #f97316;
|
|
background: #f97316;
|
|
|
- box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.2);
|
|
|
|
|
|
|
+ box-shadow: 0 0 0 2px rgb(249 115 22 / 20%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-dot-purple {
|
|
.label-dot-purple {
|
|
|
background: #8b5cf6;
|
|
background: #8b5cf6;
|
|
|
- box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.2);
|
|
|
|
|
|
|
+ box-shadow: 0 0 0 2px rgb(139 92 246 / 20%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-dot-cyan {
|
|
.label-dot-cyan {
|
|
|
background: #06b6d4;
|
|
background: #06b6d4;
|
|
|
- box-shadow: 0 0 0 2px rgba(6, 182, 212, 0.2);
|
|
|
|
|
|
|
+ box-shadow: 0 0 0 2px rgb(6 182 212 / 20%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-dot-indigo {
|
|
.label-dot-indigo {
|
|
|
background: #6366f1;
|
|
background: #6366f1;
|
|
|
- box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
|
|
|
|
|
|
|
+ box-shadow: 0 0 0 2px rgb(99 102 241 / 20%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-dot-emerald {
|
|
.label-dot-emerald {
|
|
|
background: #10b981;
|
|
background: #10b981;
|
|
|
- box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
|
|
|
|
|
|
|
+ box-shadow: 0 0 0 2px rgb(16 185 129 / 20%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.label-text {
|
|
.label-text {
|
|
|
font-size: 13px;
|
|
font-size: 13px;
|
|
|
- color: #64748f;
|
|
|
|
|
font-weight: 500;
|
|
font-weight: 500;
|
|
|
|
|
+ color: #64748f;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.bar-chart-container {
|
|
.bar-chart-container {
|
|
|
- height: 350px;
|
|
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
|
|
+ height: 350px;
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(252 254 255 / 90%) 0%, rgb(244 249 255 / 92%) 100%);
|
|
|
border-radius: 12px;
|
|
border-radius: 12px;
|
|
|
- background: linear-gradient(180deg, rgba(252, 254, 255, 0.9) 0%, rgba(244, 249, 255, 0.92) 100%);
|
|
|
|
|
box-shadow:
|
|
box-shadow:
|
|
|
- inset 0 1px 0 rgba(255, 255, 255, 0.92),
|
|
|
|
|
- inset 0 -10px 20px rgba(217, 230, 245, 0.16);
|
|
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 92%),
|
|
|
|
|
+ inset 0 -10px 20px rgb(217 230 245 / 16%);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 响应式优化
|
|
|
|
|
-@media (max-width: 1200px) {
|
|
|
|
|
- .stat-value {
|
|
|
|
|
- font-size: 28px;
|
|
|
|
|
|
|
+.maintain-kb-wrapper {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.maintain-kb-screen {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ color: #24364f;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.maintain-kb-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ height: calc(100% - 52px * var(--kb-scale));
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: calc(44px * var(--kb-scale)) calc(20px * var(--kb-scale)) calc(20px * var(--kb-scale));
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: calc(12px * var(--kb-scale));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.summary-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
|
|
+ gap: calc(12px * var(--kb-scale));
|
|
|
|
|
+ flex: 0 0 calc(154px * var(--kb-scale));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.status-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+ gap: calc(12px * var(--kb-scale));
|
|
|
|
|
+ flex: 0 0 calc(300px * var(--kb-scale));
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.trend-section {
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-panel,
|
|
|
|
|
+.trend-panel {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-card__body) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ flex-direction: column;
|
|
|
}
|
|
}
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- .stat-sub-value {
|
|
|
|
|
- font-size: 24px;
|
|
|
|
|
|
|
+.stat-card {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ padding: calc(14px * var(--kb-scale));
|
|
|
|
|
+ text-align: left;
|
|
|
|
|
+ cursor: default;
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 42%) 0%, rgb(213 227 249 / 28%) 100%);
|
|
|
|
|
+ border: 1px solid rgb(255 255 255 / 74%);
|
|
|
|
|
+ border-radius: calc(16px * var(--kb-scale));
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 78%),
|
|
|
|
|
+ 0 calc(6px * var(--kb-scale)) calc(14px * var(--kb-scale)) rgb(63 103 171 / 7%);
|
|
|
|
|
+ grid-template-rows: auto auto 1fr;
|
|
|
|
|
+ grid-template-columns: auto 1fr;
|
|
|
|
|
+ gap: calc(8px * var(--kb-scale)) calc(12px * var(--kb-scale));
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ border-color: rgb(76 132 214 / 26%);
|
|
|
|
|
+ transform: translateY(calc(-2px * var(--kb-scale)));
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 84%),
|
|
|
|
|
+ 0 calc(10px * var(--kb-scale)) calc(20px * var(--kb-scale)) rgb(63 103 171 / 11%);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-@media (max-width: 768px) {
|
|
|
|
|
- .stat-card {
|
|
|
|
|
- margin-bottom: 16px;
|
|
|
|
|
|
|
+.stat-icon-wrapper {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ width: calc(44px * var(--kb-scale));
|
|
|
|
|
+ height: calc(44px * var(--kb-scale));
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 96%) 0%, rgb(232 240 252 / 92%) 100%);
|
|
|
|
|
+ border: 1px solid rgb(205 219 239 / 90%);
|
|
|
|
|
+ border-radius: calc(12px * var(--kb-scale));
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 95%),
|
|
|
|
|
+ inset 0 calc(-6px * var(--kb-scale)) calc(12px * var(--kb-scale)) rgb(205 220 242 / 34%),
|
|
|
|
|
+ 0 calc(6px * var(--kb-scale)) calc(14px * var(--kb-scale)) rgb(66 104 168 / 8%);
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ grid-row: 1 / span 2;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-icon {
|
|
|
|
|
+ font-size: calc(24px * var(--kb-scale));
|
|
|
|
|
+ color: #1f5bb8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-title {
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ font-family: YouSheBiaoTiHei, sans-serif;
|
|
|
|
|
+ font-size: calc(22px * var(--kb-scale));
|
|
|
|
|
+ font-weight: normal;
|
|
|
|
|
+ line-height: 1.1;
|
|
|
|
|
+ color: #03409b;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-label {
|
|
|
|
|
+ font-size: calc(13px * var(--kb-scale));
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: #536986;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-value {
|
|
|
|
|
+ font-family: YouSheBiaoTiHei, sans-serif;
|
|
|
|
|
+ font-size: calc(42px * var(--kb-scale));
|
|
|
|
|
+ font-weight: normal;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ color: #1f5bb8;
|
|
|
|
|
+ grid-column: 1 / -1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-pair {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ grid-column: 1 / -1;
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+ gap: calc(10px * var(--kb-scale));
|
|
|
|
|
+
|
|
|
|
|
+ span {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ padding: calc(8px * var(--kb-scale)) calc(10px * var(--kb-scale));
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 30%) 0%, rgb(213 227 249 / 18%) 100%);
|
|
|
|
|
+ border: 1px solid rgb(255 255 255 / 48%);
|
|
|
|
|
+ border-radius: calc(10px * var(--kb-scale));
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: calc(6px * var(--kb-scale));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .chart-item {
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
|
|
+ em {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-size: calc(12px * var(--kb-scale));
|
|
|
|
|
+ font-style: normal;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: #536986;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .gauge-chart {
|
|
|
|
|
- height: 140px;
|
|
|
|
|
|
|
+ strong {
|
|
|
|
|
+ font-family: YouSheBiaoTiHei, sans-serif;
|
|
|
|
|
+ font-size: calc(28px * var(--kb-scale));
|
|
|
|
|
+ font-weight: normal;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ color: #1f5bb8;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+.gauge-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: calc(12px * var(--kb-scale)) calc(14px * var(--kb-scale)) calc(16px * var(--kb-scale));
|
|
|
|
|
+ gap: calc(12px * var(--kb-scale));
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.gauge-grid--failure {
|
|
|
|
|
+ grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.gauge-grid--maintain {
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: calc(8px * var(--kb-scale));
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 42%) 0%, rgb(213 227 249 / 28%) 100%);
|
|
|
|
|
+ border: 1px solid rgb(255 255 255 / 54%);
|
|
|
|
|
+ border-radius: calc(14px * var(--kb-scale));
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 64%),
|
|
|
|
|
+ 0 calc(5px * var(--kb-scale)) calc(12px * var(--kb-scale)) rgb(63 103 171 / 6%);
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.gauge-chart {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-label {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ height: calc(28px * var(--kb-scale));
|
|
|
|
|
+ padding: 0 calc(10px * var(--kb-scale));
|
|
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 88%) 0%, rgb(242 247 255 / 76%) 100%);
|
|
|
|
|
+ border: 1px solid rgb(216 228 244 / 78%);
|
|
|
|
|
+ border-radius: calc(999px * var(--kb-scale));
|
|
|
|
|
+ box-shadow:
|
|
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 90%),
|
|
|
|
|
+ 0 calc(4px * var(--kb-scale)) calc(10px * var(--kb-scale)) rgb(58 90 145 / 6%);
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: calc(6px * var(--kb-scale));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.label-dot {
|
|
|
|
|
+ width: calc(8px * var(--kb-scale));
|
|
|
|
|
+ height: calc(8px * var(--kb-scale));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.label-text {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-size: calc(13px * var(--kb-scale));
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: #536986;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.bar-chart-container {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: calc(8px * var(--kb-scale)) calc(12px * var(--kb-scale)) calc(14px * var(--kb-scale));
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ box-shadow: none;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.el-card) {
|
|
|
|
|
+ --el-card-bg-color: transparent;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|