Selaa lähdekoodia

style: 统一维修统计页面看板风格与缩放策略

Zimo 1 viikko sitten
vanhempi
commit
0be731bb1f
1 muutettua tiedostoa jossa 540 lisäystä ja 287 poistoa
  1. 540 287
      src/views/pms/stat/maintain.vue

+ 540 - 287
src/views/pms/stat/maintain.vue

@@ -1,186 +1,156 @@
 <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>
 
 <script setup lang="ts" name="Index">
@@ -203,6 +173,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,拆一拆组件
 
@@ -232,6 +203,37 @@ const queryParams = reactive({
   startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 设置默认开始时间为 7 天前
   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 reportingChartRef = ref() // 在线设备统计的图表
@@ -241,6 +243,7 @@ const orderFinishChartRef = ref()
 const deviceMessageCountChartRef = ref() // 上下行消息量统计的图表
 const writeChartRef = ref() // 上下行消息量统计的图表
 const finishedChartRef = ref() // 上下行消息量统计的图表
+const chartInstances: echarts.ECharts[] = []
 
 // 基础统计数据
 // TODO @super:初始为 -1,然后界面展示先是加载中?试试用 cursor 改哈
@@ -357,7 +360,6 @@ const getStats = async () => {
   })
   IotStatApi.getMainStatus().then((res) => {
     status.value = res
-    console.log(JSON.stringify(status.value))
     initCharts()
   })
   // statsData.value = await ProductCategoryApi.getIotStatisticsSummary()
@@ -371,6 +373,7 @@ const getStats = async () => {
 
 /** 初始化图表 */
 const initCharts = () => {
+  disposeChartInstances()
   // 设备数量统计
   // echarts.init(deviceCountChartRef.value).setOption({
   //   tooltip: {
@@ -456,7 +459,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',
@@ -620,7 +627,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 = () => {
@@ -731,49 +742,87 @@ 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');
+
+// 响应式优化
+@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 {
-  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;
-  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:
-    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 {
     transform: translateY(-4px);
     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) {
@@ -783,23 +832,23 @@ onUnmounted(() => {
 
 // 渐变色背景
 .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 {
-  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 {
-  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 {
-  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 {
+  display: flex;
   width: 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;
-  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;
   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 {
@@ -842,8 +891,8 @@ onUnmounted(() => {
 }
 
 .stat-divider {
-  border-color: rgba(201, 214, 234, 0.85);
   margin: 12px 0;
+  border-color: rgb(201 214 234 / 85%);
 }
 
 .stat-body {
@@ -853,16 +902,16 @@ onUnmounted(() => {
 }
 
 .stat-label {
+  margin-bottom: 4px;
   font-size: 13px;
   color: #7182a1;
-  margin-bottom: 4px;
 }
 
 .stat-value {
   font-size: 36px;
   font-weight: 700;
-  color: #1f2f54;
   line-height: 1;
+  color: #1f2f54;
 }
 
 .stat-item {
@@ -885,32 +934,28 @@ onUnmounted(() => {
 
 // 图表卡片增强样式
 .chart-card-enhanced {
+  background: linear-gradient(180deg, #fff 0%, #f5f9ff 100%);
+  border: 1px solid rgb(207 220 237 / 90%);
   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:
-    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 {
+    border-color: rgb(193 209 231 / 95%);
     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) {
     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) {
@@ -933,18 +978,18 @@ onUnmounted(() => {
 .chart-title-dot {
   width: 8px;
   height: 8px;
-  border-radius: 50%;
   background: linear-gradient(135deg, #84aef7 0%, #5e87d8 100%);
+  border-radius: 50%;
   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 {
   font-size: 16px;
   font-weight: 600;
-  color: #233554;
   letter-spacing: 0.3px;
+  color: #233554;
 }
 
 .chart-grid {
@@ -953,21 +998,17 @@ onUnmounted(() => {
 
 .chart-item {
   display: flex;
-  flex-direction: column;
-  align-items: center;
   padding: 12px;
-  transition: all 0.2s ease;
   border-radius: 12px;
+  transition: all 0.2s ease;
+  flex-direction: column;
+  align-items: center;
 
   &: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:
-      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 {
   display: flex;
-  align-items: center;
-  gap: 6px;
-  margin-top: 8px;
   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;
-  transition: all 0.2s ease;
   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 & {
-    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 {
+  display: inline-block;
   width: 8px;
   height: 8px;
   border-radius: 50%;
-  display: inline-block;
 }
 
 .label-dot-blue {
   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 {
   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 {
   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 {
   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 {
   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 {
   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 {
   font-size: 13px;
-  color: #64748f;
   font-weight: 500;
+  color: #64748f;
 }
 
 .bar-chart-container {
-  height: 350px;
   width: 100%;
+  height: 350px;
+  background: linear-gradient(180deg, rgb(252 254 255 / 90%) 0%, rgb(244 249 255 / 92%) 100%);
   border-radius: 12px;
-  background: linear-gradient(180deg, rgba(252, 254, 255, 0.9) 0%, rgba(244, 249, 255, 0.92) 100%);
   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>