|
|
@@ -1,254 +1,260 @@
|
|
|
<template>
|
|
|
- <!-- 第一行:筛选条件 -->
|
|
|
- <el-row :gutter="16" class="mb-6">
|
|
|
- <el-col :span="24">
|
|
|
- <el-card class="filter-card" shadow="hover" style="border: none">
|
|
|
- <el-form
|
|
|
- class="-mb-15px"
|
|
|
- :model="queryParams"
|
|
|
- ref="queryFormRef"
|
|
|
- :inline="true"
|
|
|
- label-width="80px"
|
|
|
- >
|
|
|
- <el-form-item label="所属部门" prop="project_name">
|
|
|
- <el-tree-select
|
|
|
- v-model="queryParams.deptId"
|
|
|
- :data="deptList"
|
|
|
- :props="defaultProps"
|
|
|
- check-strictly
|
|
|
- node-key="id"
|
|
|
- filterable
|
|
|
- placeholder="请选择所在部门"
|
|
|
- clearable
|
|
|
- style="width: 200px"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="创建时间" prop="createTime">
|
|
|
- <el-date-picker
|
|
|
- v-model="queryParams.createTime"
|
|
|
- value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
- type="daterange"
|
|
|
- start-placeholder="开始日期"
|
|
|
- end-placeholder="结束日期"
|
|
|
- :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
- class="!w-240px"
|
|
|
- @change="handleDateChange"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <el-button type="primary" @click="handleQuery">
|
|
|
- <Icon icon="ep:search" class="mr-5px" /> 搜索
|
|
|
- </el-button>
|
|
|
- <el-button @click="resetQuery" class="btn-default">
|
|
|
- <Icon icon="ep:refresh" class="mr-5px" /> 重置
|
|
|
- </el-button>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <!-- 第二行:统计卡片行 -->
|
|
|
- <el-row :gutter="16" class="mb-6">
|
|
|
- <el-col :span="6">
|
|
|
- <el-card class="stat-card stat-card-neutral-1" shadow="hover" style="border: none">
|
|
|
- <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-slate-700">{{ day.total }}</div>
|
|
|
- </div>
|
|
|
- <div class="stat-item">
|
|
|
- <div class="stat-sub-label">未完成</div>
|
|
|
- <div class="stat-sub-value text-amber-600">{{ day.todo }}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-card class="stat-card stat-card-neutral-2" shadow="hover" style="border: none">
|
|
|
- <div class="stat-content">
|
|
|
- <div class="stat-header">
|
|
|
- <div class="stat-icon-wrapper">
|
|
|
- <Icon icon="ep:star" 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-slate-700">{{ week.total }}</div>
|
|
|
- </div>
|
|
|
- <div class="stat-item">
|
|
|
- <div class="stat-sub-label">未完成</div>
|
|
|
- <div class="stat-sub-value text-amber-600">{{ week.todo }}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-card class="stat-card stat-card-neutral-3" shadow="hover" style="border: none">
|
|
|
- <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-slate-700">{{ month.total }}</div>
|
|
|
- </div>
|
|
|
- <div class="stat-item">
|
|
|
- <div class="stat-sub-label">未完成</div>
|
|
|
- <div class="stat-sub-value text-amber-600">{{ month.todo }}</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="6">
|
|
|
- <el-card class="stat-card stat-card-neutral-4" shadow="hover" style="border: none">
|
|
|
- <div class="stat-content">
|
|
|
- <div class="stat-header">
|
|
|
- <div class="stat-icon-wrapper">
|
|
|
- <Icon icon="ep:data-analysis" 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-slate-700">{{ total.total }}</div>
|
|
|
- </div>
|
|
|
- <div class="stat-item">
|
|
|
- <div class="stat-sub-label">未完成</div>
|
|
|
- <div class="stat-sub-value text-amber-600">{{ total.todo }}</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 chart-title-dot-blue"></div>
|
|
|
- <span class="chart-title">工单状态统计</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <el-row class="chart-grid">
|
|
|
- <el-col :span="8" class="chart-item">
|
|
|
- <div ref="finishedChartRef" class="gauge-chart"></div>
|
|
|
- <div class="chart-label clickable" @click="clickStatus('工单已执行')">
|
|
|
- <span class="label-dot label-dot-blue"></span>
|
|
|
- <span class="label-text">已执行</span>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8" class="chart-item">
|
|
|
- <div ref="writeChartRef" class="gauge-chart"></div>
|
|
|
- <div class="chart-label clickable" @click="clickStatus('工单待执行')">
|
|
|
- <span class="label-dot label-dot-amber"></span>
|
|
|
- <span class="label-text">待执行</span>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8" class="chart-item">
|
|
|
- <div ref="ignoreChartRef" class="gauge-chart"></div>
|
|
|
- <div class="chart-label clickable" @click="clickStatus('工单忽略')">
|
|
|
- <span class="label-dot label-dot-gray"></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 chart-title-dot-green"></div>
|
|
|
- <span class="chart-title">设备状态统计</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <el-row class="chart-grid">
|
|
|
- <el-col :span="8" class="chart-item">
|
|
|
- <div ref="finishedTodayChartRef" class="gauge-chart"></div>
|
|
|
- <div class="chart-label clickable" @click="clickStatus('设备已执行')">
|
|
|
- <span class="label-dot label-dot-blue"></span>
|
|
|
- <span class="label-text">已填写</span>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8" class="chart-item">
|
|
|
- <div ref="writeTodayChartRef" class="gauge-chart"></div>
|
|
|
- <div class="chart-label clickable" @click="clickStatus('设备待执行')">
|
|
|
- <span class="label-dot label-dot-amber"></span>
|
|
|
- <span class="label-text">未填写</span>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- <el-col :span="8" class="chart-item">
|
|
|
- <div ref="ignoreTodayChartRef" class="gauge-chart"></div>
|
|
|
- <div class="chart-label clickable" @click="clickStatus('设备忽略')">
|
|
|
- <span class="label-dot label-dot-gray"></span>
|
|
|
- <span class="label-text">忽略</span>
|
|
|
- </div>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
-
|
|
|
- <!-- 第四行:消息统计行 -->
|
|
|
- <el-row :gutter="16" class="mb-6">
|
|
|
- <el-col :span="8">
|
|
|
- <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 chart-title-dot-purple"></div>
|
|
|
- <span class="chart-title">巡检项状态统计</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <div ref="statusChartRef" class="pie-chart-container"></div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- <el-col :span="16">
|
|
|
- <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 chart-title-dot-indigo"></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="bg absolute top-0 left-0 size-full z-10">
|
|
|
+ <div class="mx-a overflow-hidden" :style="targetWrapperStyle">
|
|
|
+ <div class="bg kb-screen" id="inspect-kb" :style="targetAreaStyle">
|
|
|
+ <header class="header">巡检统计</header>
|
|
|
+ <main class="inspect-kb-content">
|
|
|
+ <!-- 第一行:筛选条件 -->
|
|
|
+ <el-row :gutter="16" class="filter-row">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-card class="filter-card" shadow="hover" style="border: none">
|
|
|
+ <el-form
|
|
|
+ class="-mb-15px"
|
|
|
+ :model="queryParams"
|
|
|
+ ref="queryFormRef"
|
|
|
+ :inline="true"
|
|
|
+ label-width="80px">
|
|
|
+ <el-form-item label="所属部门" prop="project_name">
|
|
|
+ <el-tree-select
|
|
|
+ v-model="queryParams.deptId"
|
|
|
+ :data="deptList"
|
|
|
+ :props="defaultProps"
|
|
|
+ check-strictly
|
|
|
+ node-key="id"
|
|
|
+ filterable
|
|
|
+ placeholder="请选择所在部门"
|
|
|
+ clearable
|
|
|
+ style="width: 200px" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="创建时间" prop="createTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryParams.createTime"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ type="daterange"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
+ class="!w-240px"
|
|
|
+ @change="handleDateChange" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="handleQuery">
|
|
|
+ <Icon icon="ep:search" class="mr-5px" /> 搜索
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="resetQuery" class="btn-default">
|
|
|
+ <Icon icon="ep:refresh" class="mr-5px" /> 重置
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第二行:统计卡片行 -->
|
|
|
+ <el-row :gutter="16" class="summary-row">
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card stat-card-neutral-1" shadow="hover" style="border: none">
|
|
|
+ <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-slate-700">{{ day.total }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <div class="stat-sub-label">未完成</div>
|
|
|
+ <div class="stat-sub-value text-amber-600">{{ day.todo }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card stat-card-neutral-2" shadow="hover" style="border: none">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-header">
|
|
|
+ <div class="stat-icon-wrapper">
|
|
|
+ <Icon icon="ep:star" 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-slate-700">{{ week.total }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <div class="stat-sub-label">未完成</div>
|
|
|
+ <div class="stat-sub-value text-amber-600">{{ week.todo }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card stat-card-neutral-3" shadow="hover" style="border: none">
|
|
|
+ <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-slate-700">{{ month.total }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <div class="stat-sub-label">未完成</div>
|
|
|
+ <div class="stat-sub-value text-amber-600">{{ month.todo }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-card class="stat-card stat-card-neutral-4" shadow="hover" style="border: none">
|
|
|
+ <div class="stat-content">
|
|
|
+ <div class="stat-header">
|
|
|
+ <div class="stat-icon-wrapper">
|
|
|
+ <Icon icon="ep:data-analysis" 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-slate-700">{{ total.total }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="stat-item">
|
|
|
+ <div class="stat-sub-label">未完成</div>
|
|
|
+ <div class="stat-sub-value text-amber-600">{{ total.todo }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第三行:图表行 -->
|
|
|
+ <el-row :gutter="16" class="status-row">
|
|
|
+ <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 chart-title-dot-blue"></div>
|
|
|
+ <span class="chart-title">工单状态统计</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-row class="chart-grid">
|
|
|
+ <el-col :span="8" class="chart-item">
|
|
|
+ <div ref="finishedChartRef" class="gauge-chart"></div>
|
|
|
+ <div class="chart-label clickable" @click="clickStatus('工单已执行')">
|
|
|
+ <span class="label-dot label-dot-blue"></span>
|
|
|
+ <span class="label-text">已执行</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" class="chart-item">
|
|
|
+ <div ref="writeChartRef" class="gauge-chart"></div>
|
|
|
+ <div class="chart-label clickable" @click="clickStatus('工单待执行')">
|
|
|
+ <span class="label-dot label-dot-amber"></span>
|
|
|
+ <span class="label-text">待执行</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" class="chart-item">
|
|
|
+ <div ref="ignoreChartRef" class="gauge-chart"></div>
|
|
|
+ <div class="chart-label clickable" @click="clickStatus('工单忽略')">
|
|
|
+ <span class="label-dot label-dot-gray"></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 chart-title-dot-green"></div>
|
|
|
+ <span class="chart-title">设备状态统计</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-row class="chart-grid">
|
|
|
+ <el-col :span="8" class="chart-item">
|
|
|
+ <div ref="finishedTodayChartRef" class="gauge-chart"></div>
|
|
|
+ <div class="chart-label clickable" @click="clickStatus('设备已执行')">
|
|
|
+ <span class="label-dot label-dot-blue"></span>
|
|
|
+ <span class="label-text">已填写</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" class="chart-item">
|
|
|
+ <div ref="writeTodayChartRef" class="gauge-chart"></div>
|
|
|
+ <div class="chart-label clickable" @click="clickStatus('设备待执行')">
|
|
|
+ <span class="label-dot label-dot-amber"></span>
|
|
|
+ <span class="label-text">未填写</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="8" class="chart-item">
|
|
|
+ <div ref="ignoreTodayChartRef" class="gauge-chart"></div>
|
|
|
+ <div class="chart-label clickable" @click="clickStatus('设备忽略')">
|
|
|
+ <span class="label-dot label-dot-gray"></span>
|
|
|
+ <span class="label-text">忽略</span>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第四行:消息统计行 -->
|
|
|
+ <el-row :gutter="16" class="trend-row">
|
|
|
+ <el-col :span="8">
|
|
|
+ <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 chart-title-dot-purple"></div>
|
|
|
+ <span class="chart-title">巡检项状态统计</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div ref="statusChartRef" class="pie-chart-container"></div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="16">
|
|
|
+ <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 chart-title-dot-indigo"></div>
|
|
|
+ <span class="chart-title">近一年数量统计</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div ref="chartContainer" class="bar-chart-container"></div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- TODO 第五行:地图 -->
|
|
|
+ </main>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts" name="Index">
|
|
|
@@ -277,12 +283,56 @@ import { ref } from 'vue'
|
|
|
import { DeptTreeItem } from '@/api/system/dept'
|
|
|
import * as DeptApi from '@/api/system/dept'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
+import { DESIGN_HEIGHT, DESIGN_WIDTH } from '@/utils/kb'
|
|
|
|
|
|
// TODO @super:参考下 /Users/yunai/Java/yudao-ui-admin-vue3/src/views/mall/home/index.vue,拆一拆组件
|
|
|
|
|
|
/** IoT 首页 */
|
|
|
defineOptions({ name: 'IotInspectStat' })
|
|
|
|
|
|
+const wrapperRef = ref<HTMLDivElement>()
|
|
|
+const scale = ref(1)
|
|
|
+const chartInstances = new Set<ReturnType<typeof echarts.init>>()
|
|
|
+
|
|
|
+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 getChartInstance = (el: HTMLElement | null) => {
|
|
|
+ if (!el) return null
|
|
|
+ const chart = echarts.getInstanceByDom(el) || echarts.init(el)
|
|
|
+ chartInstances.add(chart)
|
|
|
+ return chart
|
|
|
+}
|
|
|
+
|
|
|
+const resizeCharts = () => {
|
|
|
+ chartInstances.forEach((chart) => chart.resize())
|
|
|
+}
|
|
|
+
|
|
|
+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(resizeCharts)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
// TODO @super:使用下 Echart 组件,参考 yudao-ui-admin-vue3/src/views/mall/home/components/TradeTrendCard.vue 等
|
|
|
echarts.use([
|
|
|
TooltipComponent,
|
|
|
@@ -505,7 +555,8 @@ const getStats = async () => {
|
|
|
|
|
|
/** 初始化图表 */
|
|
|
const initCharts = () => {
|
|
|
- const chart = echarts.init(statusChartRef.value)
|
|
|
+ const chart = getChartInstance(statusChartRef.value)
|
|
|
+ if (!chart) return
|
|
|
chart.setOption({
|
|
|
tooltip: {
|
|
|
trigger: 'item'
|
|
|
@@ -543,6 +594,7 @@ const initCharts = () => {
|
|
|
}
|
|
|
]
|
|
|
})
|
|
|
+ chart.off('click')
|
|
|
chart.on('click', (params) => {
|
|
|
console.log('点击的数据值为:', params.value)
|
|
|
console.log('点击的数据类型为:', params.data.type)
|
|
|
@@ -601,7 +653,8 @@ const initCharts = () => {
|
|
|
|
|
|
/** 初始化仪表盘图表 */
|
|
|
const initGaugeChart = (el: any, value: number, color: string, type: string) => {
|
|
|
- const chart = echarts.init(el)
|
|
|
+ const chart = getChartInstance(el)
|
|
|
+ if (!chart) return
|
|
|
chart.setOption({
|
|
|
series: [
|
|
|
{
|
|
|
@@ -646,6 +699,7 @@ const initGaugeChart = (el: any, value: number, color: string, type: string) =>
|
|
|
})
|
|
|
|
|
|
// 添加点击事件监听器
|
|
|
+ chart.off('click')
|
|
|
chart.on('click', (params) => {
|
|
|
console.log('点击的数据值为:', params.value)
|
|
|
console.log('点击的数据类型为:', params.data.type)
|
|
|
@@ -701,7 +755,7 @@ const initMessageChart = () => {
|
|
|
})
|
|
|
|
|
|
// 配置图表
|
|
|
- echarts.init(deviceMessageCountChartRef.value).setOption({
|
|
|
+ getChartInstance(deviceMessageCountChartRef.value)?.setOption({
|
|
|
tooltip: {
|
|
|
trigger: 'axis',
|
|
|
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
|
|
@@ -794,7 +848,7 @@ const initMessageChart = () => {
|
|
|
}
|
|
|
|
|
|
const chartContainer = ref(null)
|
|
|
-let chartInstance = null
|
|
|
+let chartInstance: ReturnType<typeof echarts.init> | null = null
|
|
|
|
|
|
// 生成过去12个月份的标签 (格式: YYYY-MM)
|
|
|
const generateMonthLabels = () => {
|
|
|
@@ -885,11 +939,9 @@ const initChart = async () => {
|
|
|
}
|
|
|
|
|
|
// 初始化图表
|
|
|
- chartInstance = echarts.init(chartContainer.value)
|
|
|
+ chartInstance = getChartInstance(chartContainer.value)
|
|
|
+ if (!chartInstance) return
|
|
|
chartInstance.setOption(option)
|
|
|
-
|
|
|
- // 窗口缩放监听
|
|
|
- window.addEventListener('resize', handleResize)
|
|
|
handleResize()
|
|
|
}
|
|
|
|
|
|
@@ -899,28 +951,62 @@ const handleResize = () => {
|
|
|
}
|
|
|
/** 初始化 */
|
|
|
onMounted(async () => {
|
|
|
+ nextTick(updateScale)
|
|
|
+ resizeObserver = new ResizeObserver(updateScale)
|
|
|
+ if (wrapperRef.value) {
|
|
|
+ resizeObserver.observe(wrapperRef.value)
|
|
|
+ }
|
|
|
+ window.addEventListener('resize', updateScale)
|
|
|
+
|
|
|
deptList.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
|
queryParams.deptId = useUserStore().getUser.deptId
|
|
|
await getStats()
|
|
|
+ nextTick(resizeCharts)
|
|
|
// await initChart()
|
|
|
// await initCharts()
|
|
|
})
|
|
|
onUnmounted(() => {
|
|
|
- chartInstance?.dispose()
|
|
|
- window.removeEventListener('resize', handleResize)
|
|
|
+ resizeObserver?.disconnect()
|
|
|
+ window.removeEventListener('resize', updateScale)
|
|
|
+ cancelAnimationFrame(resizeRaf)
|
|
|
+ chartInstances.forEach((chart) => chart.dispose())
|
|
|
+ chartInstances.clear()
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
+@import url('@/styles/kb.scss');
|
|
|
+
|
|
|
+// 响应式优化
|
|
|
+@media (width <= 1200px) {
|
|
|
+ .stat-sub-value {
|
|
|
+ font-size: 24px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@media (width <= 768px) {
|
|
|
+ .stat-card {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-item {
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .gauge-chart {
|
|
|
+ height: 140px;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 筛选卡片样式
|
|
|
.filter-card {
|
|
|
- border-radius: 12px;
|
|
|
+ background: linear-gradient(to bottom, #fafafa, #fff);
|
|
|
border: 1px solid #e5e7eb;
|
|
|
- background: linear-gradient(to bottom, #fafafa, #ffffff);
|
|
|
+ border-radius: 12px;
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
&:hover {
|
|
|
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
|
|
+ box-shadow: 0 4px 12px rgb(0 0 0 / 5%);
|
|
|
}
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
@@ -928,8 +1014,8 @@ onUnmounted(() => {
|
|
|
}
|
|
|
|
|
|
:deep(.el-form-item__label) {
|
|
|
- color: #6b7280;
|
|
|
font-weight: 500;
|
|
|
+ color: #6b7280;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -941,35 +1027,35 @@ onUnmounted(() => {
|
|
|
&:hover {
|
|
|
background: linear-gradient(135deg, #475569 0%, #334155 100%);
|
|
|
transform: translateY(-1px);
|
|
|
- box-shadow: 0 4px 8px rgba(100, 116, 139, 0.3);
|
|
|
+ box-shadow: 0 4px 8px rgb(100 116 139 / 30%);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.btn-default {
|
|
|
- border-color: #d1d5db;
|
|
|
color: #6b7280;
|
|
|
+ border-color: #d1d5db;
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
|
&:hover {
|
|
|
- border-color: #9ca3af;
|
|
|
color: #4b5563;
|
|
|
background: #f9fafb;
|
|
|
+ border-color: #9ca3af;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 统计卡片基础样式
|
|
|
.stat-card {
|
|
|
- border-radius: 12px;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #fff;
|
|
|
border: 1px solid #e5e7eb;
|
|
|
+ border-radius: 12px;
|
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
- overflow: hidden;
|
|
|
- position: relative;
|
|
|
- background: #ffffff;
|
|
|
|
|
|
&:hover {
|
|
|
- transform: translateY(-4px);
|
|
|
- box-shadow: 0 12px 24px -8px rgba(0, 0, 0, 0.1);
|
|
|
border-color: #d1d5db;
|
|
|
+ transform: translateY(-4px);
|
|
|
+ box-shadow: 0 12px 24px -8px rgb(0 0 0 / 10%);
|
|
|
}
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
@@ -1007,14 +1093,14 @@ onUnmounted(() => {
|
|
|
}
|
|
|
|
|
|
.stat-icon-wrapper {
|
|
|
+ display: flex;
|
|
|
width: 48px;
|
|
|
height: 48px;
|
|
|
background: #0090ff;
|
|
|
border-radius: 12px;
|
|
|
- display: flex;
|
|
|
+ box-shadow: 0 4px 8px rgb(100 116 139 / 20%);
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- box-shadow: 0 4px 8px rgba(100, 116, 139, 0.2);
|
|
|
}
|
|
|
|
|
|
.stat-icon {
|
|
|
@@ -1029,8 +1115,8 @@ onUnmounted(() => {
|
|
|
}
|
|
|
|
|
|
.stat-divider {
|
|
|
- border-color: #e2e8f0;
|
|
|
margin: 12px 0;
|
|
|
+ border-color: #e2e8f0;
|
|
|
}
|
|
|
|
|
|
.stat-body {
|
|
|
@@ -1044,19 +1130,19 @@ onUnmounted(() => {
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 8px 12px;
|
|
|
- background: rgba(255, 255, 255, 0.6);
|
|
|
+ background: rgb(255 255 255 / 60%);
|
|
|
border-radius: 8px;
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
|
&:hover {
|
|
|
- background: rgba(255, 255, 255, 0.9);
|
|
|
+ background: rgb(255 255 255 / 90%);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.stat-sub-label {
|
|
|
font-size: 13px;
|
|
|
- color: #64748b;
|
|
|
font-weight: 500;
|
|
|
+ color: #64748b;
|
|
|
}
|
|
|
|
|
|
.stat-sub-value {
|
|
|
@@ -1067,20 +1153,20 @@ onUnmounted(() => {
|
|
|
|
|
|
// 图表卡片增强样式
|
|
|
.chart-card-enhanced {
|
|
|
- border-radius: 12px;
|
|
|
+ background: #fff;
|
|
|
border: 1px solid #e5e7eb;
|
|
|
+ border-radius: 12px;
|
|
|
transition: all 0.3s ease;
|
|
|
- background: #ffffff;
|
|
|
|
|
|
&:hover {
|
|
|
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
|
|
border-color: #d1d5db;
|
|
|
+ box-shadow: 0 8px 24px rgb(0 0 0 / 8%);
|
|
|
}
|
|
|
|
|
|
:deep(.el-card__header) {
|
|
|
padding: 16px 20px;
|
|
|
+ background: linear-gradient(to right, #fafafa, #fff);
|
|
|
border-bottom: 1px solid #f1f5f9;
|
|
|
- background: linear-gradient(to right, #fafafa, #ffffff);
|
|
|
}
|
|
|
|
|
|
:deep(.el-card__body) {
|
|
|
@@ -1104,30 +1190,30 @@ onUnmounted(() => {
|
|
|
width: 8px;
|
|
|
height: 8px;
|
|
|
border-radius: 50%;
|
|
|
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
+ box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
|
|
|
}
|
|
|
|
|
|
.chart-title-dot-blue {
|
|
|
- background: #0055bb;
|
|
|
+ background: #05b;
|
|
|
}
|
|
|
|
|
|
.chart-title-dot-green {
|
|
|
- background: #0055bb;
|
|
|
+ background: #05b;
|
|
|
}
|
|
|
|
|
|
.chart-title-dot-purple {
|
|
|
- background: #0055bb;
|
|
|
+ background: #05b;
|
|
|
}
|
|
|
|
|
|
.chart-title-dot-indigo {
|
|
|
- background: #0055bb;
|
|
|
+ background: #05b;
|
|
|
}
|
|
|
|
|
|
.chart-title {
|
|
|
font-size: 16px;
|
|
|
font-weight: 600;
|
|
|
- color: #334155;
|
|
|
letter-spacing: 0.3px;
|
|
|
+ color: #334155;
|
|
|
}
|
|
|
|
|
|
.chart-grid {
|
|
|
@@ -1154,13 +1240,13 @@ onUnmounted(() => {
|
|
|
|
|
|
.chart-label {
|
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 6px;
|
|
|
- margin-top: 8px;
|
|
|
padding: 6px 12px;
|
|
|
+ margin-top: 8px;
|
|
|
background: #f1f5f9;
|
|
|
border-radius: 20px;
|
|
|
transition: all 0.2s ease;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
|
|
|
.chart-item:hover & {
|
|
|
background: #e2e8f0;
|
|
|
@@ -1177,61 +1263,323 @@ onUnmounted(() => {
|
|
|
}
|
|
|
|
|
|
.label-dot {
|
|
|
+ display: inline-block;
|
|
|
width: 8px;
|
|
|
height: 8px;
|
|
|
border-radius: 50%;
|
|
|
- display: inline-block;
|
|
|
}
|
|
|
|
|
|
.label-dot-blue {
|
|
|
background: #64748b;
|
|
|
- box-shadow: 0 0 0 2px rgba(100, 116, 139, 0.2);
|
|
|
+ box-shadow: 0 0 0 2px rgb(100 116 139 / 20%);
|
|
|
}
|
|
|
|
|
|
.label-dot-amber {
|
|
|
background: #d97706;
|
|
|
- box-shadow: 0 0 0 2px rgba(217, 119, 6, 0.2);
|
|
|
+ box-shadow: 0 0 0 2px rgb(217 119 6 / 20%);
|
|
|
}
|
|
|
|
|
|
.label-dot-gray {
|
|
|
background: #78716c;
|
|
|
- box-shadow: 0 0 0 2px rgba(120, 113, 108, 0.2);
|
|
|
+ box-shadow: 0 0 0 2px rgb(120 113 108 / 20%);
|
|
|
}
|
|
|
|
|
|
.label-text {
|
|
|
font-size: 13px;
|
|
|
- color: #64748b;
|
|
|
font-weight: 500;
|
|
|
+ color: #64748b;
|
|
|
}
|
|
|
|
|
|
.pie-chart-container {
|
|
|
- height: 300px;
|
|
|
width: 100%;
|
|
|
+ height: 300px;
|
|
|
}
|
|
|
|
|
|
.bar-chart-container {
|
|
|
+ width: 100%;
|
|
|
height: 300px;
|
|
|
+}
|
|
|
+
|
|
|
+.kb-screen {
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.inspect-kb-content {
|
|
|
+ display: grid;
|
|
|
+ height: calc(100% - 52px * var(--kb-scale));
|
|
|
+ padding: calc(16px * var(--kb-scale)) calc(20px * var(--kb-scale)) calc(20px * var(--kb-scale));
|
|
|
+ gap: calc(12px * var(--kb-scale));
|
|
|
+ grid-template-rows:
|
|
|
+ calc(74px * var(--kb-scale)) calc(174px * var(--kb-scale))
|
|
|
+ calc(300px * var(--kb-scale)) minmax(0, 1fr);
|
|
|
+}
|
|
|
+
|
|
|
+.inspect-kb-content > .el-row {
|
|
|
+ height: 100%;
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+}
|
|
|
+
|
|
|
+.inspect-kb-content > .el-row > .el-col {
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card,
|
|
|
+.stat-card,
|
|
|
+.chart-card-enhanced {
|
|
|
+ height: 100%;
|
|
|
+ background: linear-gradient(180deg, rgb(235 243 255 / 76%) 0%, rgb(221 233 251 / 58%) 100%);
|
|
|
+ border: 1px solid rgb(255 255 255 / 68%) !important;
|
|
|
+ border-radius: calc(8px * var(--kb-scale));
|
|
|
+ box-shadow:
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 78%),
|
|
|
+ 0 calc(10px * var(--kb-scale)) calc(24px * var(--kb-scale)) rgb(46 90 164 / 10%);
|
|
|
+ backdrop-filter: blur(12px);
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card:hover,
|
|
|
+.stat-card:hover,
|
|
|
+.chart-card-enhanced:hover {
|
|
|
+ border-color: rgb(255 255 255 / 84%) !important;
|
|
|
+ transform: none;
|
|
|
+ box-shadow:
|
|
|
+ inset 0 1px 0 rgb(255 255 255 / 86%),
|
|
|
+ 0 calc(12px * var(--kb-scale)) calc(26px * var(--kb-scale)) rgb(46 90 164 / 14%);
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card :deep(.el-card__body) {
|
|
|
+ display: flex;
|
|
|
+ height: 100%;
|
|
|
+ padding: calc(12px * var(--kb-scale)) calc(18px * var(--kb-scale));
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card :deep(.el-form) {
|
|
|
+ display: flex;
|
|
|
width: 100%;
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+ align-items: center;
|
|
|
+ gap: calc(16px * var(--kb-scale));
|
|
|
}
|
|
|
|
|
|
-// 响应式优化
|
|
|
-@media (max-width: 1200px) {
|
|
|
- .stat-sub-value {
|
|
|
- font-size: 24px;
|
|
|
- }
|
|
|
+.filter-card :deep(.el-form-item) {
|
|
|
+ margin-right: 0;
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
|
|
|
|
-@media (max-width: 768px) {
|
|
|
- .stat-card {
|
|
|
- margin-bottom: 16px;
|
|
|
- }
|
|
|
+.filter-card :deep(.el-form-item__label) {
|
|
|
+ height: calc(32px * var(--kb-scale));
|
|
|
+ padding-right: calc(10px * var(--kb-scale));
|
|
|
+ font-size: calc(14px * var(--kb-scale));
|
|
|
+ line-height: calc(32px * var(--kb-scale));
|
|
|
+ color: #31527e;
|
|
|
+}
|
|
|
|
|
|
- .chart-item {
|
|
|
- padding: 8px;
|
|
|
- }
|
|
|
+.filter-card :deep(.el-tree-select) {
|
|
|
+ width: calc(220px * var(--kb-scale)) !important;
|
|
|
+}
|
|
|
|
|
|
- .gauge-chart {
|
|
|
- height: 140px;
|
|
|
- }
|
|
|
+.filter-card :deep(.el-date-editor) {
|
|
|
+ width: calc(320px * var(--kb-scale)) !important;
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card :deep(.el-input__wrapper),
|
|
|
+.filter-card :deep(.el-select__wrapper) {
|
|
|
+ min-height: calc(32px * var(--kb-scale));
|
|
|
+ padding: 0 calc(10px * var(--kb-scale));
|
|
|
+ background: rgb(255 255 255 / 72%);
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card :deep(.el-range-input),
|
|
|
+.filter-card :deep(.el-range-separator),
|
|
|
+.filter-card :deep(.el-input__inner) {
|
|
|
+ font-size: calc(13px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.filter-card :deep(.el-button) {
|
|
|
+ height: calc(32px * var(--kb-scale));
|
|
|
+ padding: 0 calc(14px * var(--kb-scale));
|
|
|
+ margin-left: calc(8px * var(--kb-scale));
|
|
|
+ font-size: calc(13px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card {
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card :deep(.el-card__body) {
|
|
|
+ height: 100%;
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-card-neutral-1,
|
|
|
+.stat-card-neutral-2,
|
|
|
+.stat-card-neutral-3,
|
|
|
+.stat-card-neutral-4 {
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 58%) 0%, rgb(213 227 249 / 42%) 100%);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-content {
|
|
|
+ display: flex;
|
|
|
+ height: 100%;
|
|
|
+ padding: calc(16px * var(--kb-scale));
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-header {
|
|
|
+ margin-bottom: calc(8px * var(--kb-scale));
|
|
|
+ gap: calc(10px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.stat-icon-wrapper {
|
|
|
+ width: calc(38px * var(--kb-scale));
|
|
|
+ height: calc(38px * var(--kb-scale));
|
|
|
+ background: linear-gradient(180deg, rgb(255 255 255 / 96%) 0%, rgb(235 244 255 / 92%) 100%);
|
|
|
+ border: 1px solid rgb(255 255 255 / 88%);
|
|
|
+ border-radius: calc(8px * var(--kb-scale));
|
|
|
+ box-shadow: 0 calc(5px * var(--kb-scale)) calc(10px * var(--kb-scale)) rgb(57 111 194 / 12%);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-icon {
|
|
|
+ font-size: calc(19px * var(--kb-scale));
|
|
|
+ color: #2474dd;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-title {
|
|
|
+ font-family: YouSheBiaoTiHei, sans-serif;
|
|
|
+ font-size: calc(20px * var(--kb-scale));
|
|
|
+ font-weight: normal;
|
|
|
+ letter-spacing: 0;
|
|
|
+ color: #03409b;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-divider {
|
|
|
+ margin: 0 0 calc(8px * var(--kb-scale));
|
|
|
+ border-color: rgb(3 64 155 / 12%);
|
|
|
+}
|
|
|
+
|
|
|
+.stat-body {
|
|
|
+ display: grid;
|
|
|
+ min-height: 0;
|
|
|
+ flex: 1;
|
|
|
+ gap: calc(10px * var(--kb-scale));
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
+}
|
|
|
+
|
|
|
+.stat-item {
|
|
|
+ padding: calc(8px * var(--kb-scale)) calc(12px * var(--kb-scale));
|
|
|
+ background: rgb(255 255 255 / 38%);
|
|
|
+ border: 1px solid rgb(255 255 255 / 46%);
|
|
|
+ border-radius: calc(6px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.stat-sub-label {
|
|
|
+ font-size: calc(13px * var(--kb-scale));
|
|
|
+ color: #55708f;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-sub-value {
|
|
|
+ font-family: YouSheBiaoTiHei, sans-serif;
|
|
|
+ font-size: calc(28px * var(--kb-scale));
|
|
|
+ font-weight: normal;
|
|
|
+ color: #1762c8;
|
|
|
+}
|
|
|
+
|
|
|
+.stat-sub-value.text-amber-600 {
|
|
|
+ color: #e87b1e;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-card-enhanced {
|
|
|
+ display: flex;
|
|
|
+ overflow: hidden;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-card-enhanced :deep(.el-card__header) {
|
|
|
+ height: calc(42px * var(--kb-scale));
|
|
|
+ padding: 0 calc(16px * var(--kb-scale)) 0 calc(36px * var(--kb-scale));
|
|
|
+ background: transparent;
|
|
|
+ border-bottom: 1px solid rgb(3 64 155 / 12%);
|
|
|
+}
|
|
|
+
|
|
|
+.chart-card-enhanced :deep(.el-card__body) {
|
|
|
+ min-height: 0;
|
|
|
+ padding: calc(8px * var(--kb-scale)) calc(12px * var(--kb-scale));
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-header,
|
|
|
+.chart-title-wrapper {
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-title-wrapper {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-title-dot {
|
|
|
+ position: absolute;
|
|
|
+ left: calc(-22px * var(--kb-scale));
|
|
|
+ width: calc(4px * var(--kb-scale));
|
|
|
+ height: calc(18px * var(--kb-scale));
|
|
|
+ background: #03409b;
|
|
|
+ border-radius: calc(2px * var(--kb-scale));
|
|
|
+ box-shadow: none;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-title {
|
|
|
+ font-family: YouSheBiaoTiHei, sans-serif;
|
|
|
+ font-size: calc(22px * var(--kb-scale));
|
|
|
+ font-weight: normal;
|
|
|
+ letter-spacing: 0;
|
|
|
+ color: #03409b;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-grid {
|
|
|
+ height: 100%;
|
|
|
+ min-height: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-item {
|
|
|
+ height: 100%;
|
|
|
+ padding: calc(4px * var(--kb-scale));
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-item:hover {
|
|
|
+ background: transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.gauge-chart {
|
|
|
+ height: calc(190px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.chart-label {
|
|
|
+ min-height: calc(28px * var(--kb-scale));
|
|
|
+ padding: calc(4px * var(--kb-scale)) calc(10px * var(--kb-scale));
|
|
|
+ margin-top: 0;
|
|
|
+ background: rgb(255 255 255 / 52%);
|
|
|
+ border-radius: calc(5px * var(--kb-scale));
|
|
|
+ gap: calc(6px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.chart-label.clickable:hover {
|
|
|
+ background: rgb(255 255 255 / 78%);
|
|
|
+ transform: none;
|
|
|
+}
|
|
|
+
|
|
|
+.label-dot {
|
|
|
+ width: calc(7px * var(--kb-scale));
|
|
|
+ height: calc(7px * var(--kb-scale));
|
|
|
+}
|
|
|
+
|
|
|
+.label-text {
|
|
|
+ font-size: calc(13px * var(--kb-scale));
|
|
|
+ color: #466484;
|
|
|
+}
|
|
|
+
|
|
|
+.pie-chart-container,
|
|
|
+.bar-chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
}
|
|
|
</style>
|