|
|
@@ -1,206 +1,146 @@
|
|
|
<template>
|
|
|
- <!-- 第一行:统计卡片行 -->
|
|
|
- <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 :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-card>
|
|
|
- </el-col>
|
|
|
- <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>
|
|
|
- <div ref="typeChartRef" class="h-[220px]"></div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <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="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-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <!-- 第三行:消息统计行 -->
|
|
|
- <el-row>
|
|
|
- <el-col :span="24">
|
|
|
- <el-card class="chart-card" shadow="never">
|
|
|
- <template #header>
|
|
|
- <div class="flex items-center justify-between">
|
|
|
- <span class="text-base font-medium text-gray-600">近一年数量统计</span>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <div ref="chartContainer" class="h-[300px]"></div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <!-- TODO 第四行:地图 -->
|
|
|
+ <div ref="wrapperRef" class="maintenance-kb-wrapper bg absolute top-0 left-0 size-full z-10">
|
|
|
+ <div class="mx-a overflow-hidden" :style="targetWrapperStyle">
|
|
|
+ <div class="maintenance-kb-screen bg" :style="targetAreaStyle">
|
|
|
+ <header class="header">保养统计</header>
|
|
|
+
|
|
|
+ <main class="maintenance-kb-content">
|
|
|
+ <section class="summary-grid">
|
|
|
+ <button class="summary-card stat-card" type="button">
|
|
|
+ <span class="stat-icon-wrapper">
|
|
|
+ <Icon icon="ep:clock" class="stat-icon" />
|
|
|
+ </span>
|
|
|
+ <span class="stat-title">昨日工单数量</span>
|
|
|
+ <span class="stat-pair">
|
|
|
+ <span
|
|
|
+ ><em>总数量</em><strong>{{ day.total }}</strong></span
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ ><em>未完成</em><strong>{{ day.todo }}</strong></span
|
|
|
+ >
|
|
|
+ </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.total }}</strong></span
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ ><em>未完成</em><strong>{{ week.todo }}</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.total }}</strong></span
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ ><em>未完成</em><strong>{{ month.todo }}</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.total }}</strong></span
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ ><em>未完成</em><strong>{{ total.todo }}</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">
|
|
|
+ <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>
|
|
|
+
|
|
|
+ <el-card class="panel chart-panel" shadow="never">
|
|
|
+ <div class="panel-title">
|
|
|
+ <div class="icon-decorator"><span></span><span></span></div>
|
|
|
+ 工单类型统计
|
|
|
+ </div>
|
|
|
+ <div ref="typeChartRef" class="type-chart"></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">
|
|
|
+ <div class="chart-item">
|
|
|
+ <div ref="writeTodayChartRef" 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="finishedTodayChartRef" 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>
|
|
|
|
|
|
<script setup lang="ts" name="Index">
|
|
|
import * as echarts from 'echarts/core'
|
|
|
-import { BarChart } from 'echarts/charts'; // 显式导入柱状图模块
|
|
|
+import { BarChart } from 'echarts/charts' // 显式导入柱状图模块
|
|
|
|
|
|
import {
|
|
|
GridComponent,
|
|
|
@@ -218,6 +158,7 @@ import {
|
|
|
} from '@/api/iot/statistics'
|
|
|
import { formatDate } from '@/utils/formatTime'
|
|
|
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,拆一拆组件
|
|
|
|
|
|
@@ -248,16 +189,44 @@ const queryParams = reactive({
|
|
|
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`
|
|
|
+}))
|
|
|
+
|
|
|
+const 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 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() // 上下行消息量统计的图表
|
|
|
+const chartInstances: echarts.ECharts[] = []
|
|
|
// 基础统计数据
|
|
|
// TODO @super:初始为 -1,然后界面展示先是加载中?试试用 cursor 改哈
|
|
|
const statsData = ref<IotStatisticsSummaryRespVO>({
|
|
|
@@ -396,46 +365,52 @@ const getStats = async () => {
|
|
|
|
|
|
/** 初始化图表 */
|
|
|
const initCharts = () => {
|
|
|
+ disposeChartInstances()
|
|
|
+
|
|
|
// 设备数量统计
|
|
|
- echarts.init(typeChartRef.value).setOption({
|
|
|
- tooltip: {
|
|
|
- trigger: 'item'
|
|
|
- },
|
|
|
- legend: {
|
|
|
- top: '5%',
|
|
|
- right: '10%',
|
|
|
- align: 'left',
|
|
|
- orient: 'vertical',
|
|
|
- icon: 'circle'
|
|
|
- },
|
|
|
- series: [
|
|
|
- {
|
|
|
- name: '',
|
|
|
- type: 'pie',
|
|
|
- radius: ['50%', '80%'],
|
|
|
- avoidLabelOverlap: false,
|
|
|
- center: ['30%', '50%'],
|
|
|
- label: {
|
|
|
- show: false,
|
|
|
- position: 'outside'
|
|
|
- },
|
|
|
- emphasis: {
|
|
|
+ if (typeChartRef.value) {
|
|
|
+ const typeChart = echarts.init(typeChartRef.value)
|
|
|
+ chartInstances.push(typeChart)
|
|
|
+ typeChart.setOption({
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ top: '5%',
|
|
|
+ right: '10%',
|
|
|
+ align: 'left',
|
|
|
+ orient: 'vertical',
|
|
|
+ icon: 'circle'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['50%', '80%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ center: ['30%', '50%'],
|
|
|
label: {
|
|
|
- show: true,
|
|
|
- fontSize: 20,
|
|
|
- fontWeight: 'bold'
|
|
|
- }
|
|
|
- },
|
|
|
- labelLine: {
|
|
|
- show: false
|
|
|
- },
|
|
|
- data: Object.entries(typeData.value).map(([name, value]) => ({
|
|
|
- name,
|
|
|
- value
|
|
|
- }))
|
|
|
- }
|
|
|
- ]
|
|
|
- })
|
|
|
+ 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,
|
|
|
@@ -466,7 +441,11 @@ const initCharts = () => {
|
|
|
|
|
|
/** 初始化仪表盘图表 */
|
|
|
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: [
|
|
|
{
|
|
|
type: 'gauge',
|
|
|
@@ -630,7 +609,11 @@ const initMessageChart = () => {
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
const generateMonthLabels = () => {
|
|
|
@@ -652,8 +635,8 @@ const fetchChartData = async () => {
|
|
|
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],
|
|
|
+ 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)
|
|
|
})
|
|
|
@@ -721,28 +704,278 @@ const initChart = async () => {
|
|
|
}
|
|
|
|
|
|
// 初始化图表
|
|
|
+ chartInstance?.dispose()
|
|
|
chartInstance = echarts.init(chartContainer.value)
|
|
|
chartInstance.setOption(option)
|
|
|
|
|
|
- // 窗口缩放监听
|
|
|
- window.addEventListener('resize', handleResize)
|
|
|
handleResize()
|
|
|
}
|
|
|
|
|
|
// 自适应调整
|
|
|
const handleResize = () => {
|
|
|
chartInstance?.resize()
|
|
|
+ chartInstances.forEach((instance) => instance.resize())
|
|
|
}
|
|
|
/** 初始化 */
|
|
|
onMounted(() => {
|
|
|
+ nextTick(updateScale)
|
|
|
+ resizeObserver = new ResizeObserver(updateScale)
|
|
|
+ if (wrapperRef.value) {
|
|
|
+ resizeObserver.observe(wrapperRef.value)
|
|
|
+ }
|
|
|
+ window.addEventListener('resize', updateScale)
|
|
|
+
|
|
|
getStats()
|
|
|
initChart()
|
|
|
})
|
|
|
onUnmounted(() => {
|
|
|
+ resizeObserver?.disconnect()
|
|
|
+ window.removeEventListener('resize', updateScale)
|
|
|
+ cancelAnimationFrame(resizeRaf)
|
|
|
+ disposeChartInstances()
|
|
|
chartInstance?.dispose()
|
|
|
- window.removeEventListener('resize', handleResize)
|
|
|
+ chartInstance = null
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
+@import url('@/styles/kb.scss');
|
|
|
+
|
|
|
+.maintenance-kb-wrapper {
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.maintenance-kb-screen {
|
|
|
+ display: flex;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #24364f;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.maintenance-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;
|
|
|
+ min-height: 0;
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+ gap: calc(12px * var(--kb-scale));
|
|
|
+ flex: 0 0 calc(300px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.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-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%);
|
|
|
+ transition: all 0.28s ease;
|
|
|
+ grid-template-rows: 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%);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.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;
|
|
|
+}
|
|
|
+
|
|
|
+.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-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));
|
|
|
+ }
|
|
|
+
|
|
|
+ em {
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: calc(12px * var(--kb-scale));
|
|
|
+ font-style: normal;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #536986;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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));
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+ gap: calc(12px * var(--kb-scale));
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.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));
|
|
|
+ border-radius: 50%;
|
|
|
+}
|
|
|
+
|
|
|
+.label-dot-indigo {
|
|
|
+ background: #6366f1;
|
|
|
+ box-shadow: 0 0 0 2px rgb(99 102 241 / 20%);
|
|
|
+}
|
|
|
+
|
|
|
+.label-dot-emerald {
|
|
|
+ background: #10b981;
|
|
|
+ box-shadow: 0 0 0 2px rgb(16 185 129 / 20%);
|
|
|
+}
|
|
|
+
|
|
|
+.label-text {
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: calc(13px * var(--kb-scale));
|
|
|
+ font-weight: 700;
|
|
|
+ color: #536986;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.type-chart,
|
|
|
+.bar-chart-container {
|
|
|
+ width: 100%;
|
|
|
+ min-height: 0;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.type-chart {
|
|
|
+ padding: calc(8px * var(--kb-scale)) calc(12px * var(--kb-scale)) calc(14px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.bar-chart-container {
|
|
|
+ padding: calc(8px * var(--kb-scale)) calc(12px * var(--kb-scale)) calc(14px * var(--kb-scale));
|
|
|
+ background: transparent;
|
|
|
+ box-shadow: none;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-card) {
|
|
|
+ --el-card-bg-color: transparent;
|
|
|
+}
|
|
|
</style>
|