2025-10-14 14:17:44 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="main-dashboard">
|
|
|
|
|
|
<div class="header">
|
|
|
|
|
|
<h1>海洋科考数据统计分析</h1>
|
|
|
|
|
|
<p>全面展示各类科考设备与平台的运行数据统计</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-row :gutter="20" class="stats-grid">
|
|
|
|
|
|
<!-- 科考船汇总统计 -->
|
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card
|
2026-04-03 02:05:29 +08:00
|
|
|
|
class="stat-card stat-card--nav stat-card--ship"
|
|
|
|
|
|
shadow="never"
|
2025-10-14 14:17:44 +08:00
|
|
|
|
@click.native="goToDetail('ships')"
|
|
|
|
|
|
>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<path d="M3 18h18v2H3v-2zm1-2h16l-2-9H6l-2 9z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
|
|
|
|
|
|
<path d="M8 7V5a2 2 0 012-2h4a2 2 0 012 2v2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">科考船汇总统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge">{{ ship.length }} 艘在册</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-metrics" :class="{ 'stat-metrics--loading': summaryLoading }">
|
|
|
|
|
|
<div v-for="(m, idx) in shipMetrics" :key="'s'+idx" class="stat-metric">
|
|
|
|
|
|
<span class="stat-metric__value">{{ m.display }}</span>
|
|
|
|
|
|
<span class="stat-metric__label">{{ m.label }}</span>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<p class="stat-card__fleet" :title="shipNamesJoined">{{ shipNamesJoined }}</p>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 载人潜水器汇总统计 -->
|
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card
|
2026-04-03 02:05:29 +08:00
|
|
|
|
class="stat-card stat-card--nav stat-card--hov"
|
|
|
|
|
|
shadow="never"
|
2025-10-14 14:17:44 +08:00
|
|
|
|
@click.native="goToDetail('hov')"
|
|
|
|
|
|
>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<ellipse cx="12" cy="12" rx="7" ry="4" stroke="currentColor" stroke-width="1.5" />
|
|
|
|
|
|
<path d="M5 12h14M12 8v8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
|
|
|
|
<circle cx="12" cy="12" r="2" stroke="currentColor" stroke-width="1.5" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">载人潜水器汇总统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge">{{ hov.length }} 艘在册</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-metrics" :class="{ 'stat-metrics--loading': summaryLoading }">
|
|
|
|
|
|
<div v-for="(m, idx) in platformMetrics(hovSummary)" :key="'h'+idx" class="stat-metric">
|
|
|
|
|
|
<span class="stat-metric__value">{{ m.display }}</span>
|
|
|
|
|
|
<span class="stat-metric__label">{{ m.label }}</span>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<p class="stat-card__fleet" :title="hovNamesJoined">{{ hovNamesJoined }}</p>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 遥控潜水器汇总统计 -->
|
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card
|
2026-04-03 02:05:29 +08:00
|
|
|
|
class="stat-card stat-card--nav stat-card--rov"
|
|
|
|
|
|
shadow="never"
|
2025-10-14 14:17:44 +08:00
|
|
|
|
@click.native="goToDetail('rov')"
|
|
|
|
|
|
>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<rect x="5" y="9" width="14" height="10" rx="2" stroke="currentColor" stroke-width="1.5" />
|
|
|
|
|
|
<path d="M9 14h6M12 11v6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
|
|
|
|
<path d="M19 13v4M3 13v4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">遥控潜水器汇总统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge">{{ rov.length }} 艘在册</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-metrics" :class="{ 'stat-metrics--loading': summaryLoading }">
|
|
|
|
|
|
<div v-for="(m, idx) in platformMetrics(rovSummary)" :key="'r'+idx" class="stat-metric">
|
|
|
|
|
|
<span class="stat-metric__value">{{ m.display }}</span>
|
|
|
|
|
|
<span class="stat-metric__label">{{ m.label }}</span>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<p class="stat-card__fleet" :title="rovNamesJoined">{{ rovNamesJoined }}</p>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 无人航行潜水器汇总统计 -->
|
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card
|
2026-04-03 02:05:29 +08:00
|
|
|
|
class="stat-card stat-card--nav stat-card--uuv"
|
|
|
|
|
|
shadow="never"
|
|
|
|
|
|
@click.native="goToDetail('auv')"
|
2025-10-14 14:17:44 +08:00
|
|
|
|
>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<path d="M4 12c2-4 6-6 8-6s6 2 8 6c-2 4-6 6-8 6s-6-2-8-6z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
|
|
|
|
|
|
<circle cx="12" cy="12" r="2" stroke="currentColor" stroke-width="1.5" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">无人航行潜水器汇总统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge">{{ uuv.length }} 艘在册</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-metrics" :class="{ 'stat-metrics--loading': summaryLoading }">
|
|
|
|
|
|
<div v-for="(m, idx) in platformMetrics(uuvSummary)" :key="'u'+idx" class="stat-metric">
|
|
|
|
|
|
<span class="stat-metric__value">{{ m.display }}</span>
|
|
|
|
|
|
<span class="stat-metric__label">{{ m.label }}</span>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<p class="stat-card__fleet" :title="uuvNamesJoined">{{ uuvNamesJoined }}</p>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<!-- 参航单位统计(无人航行潜水器) -->
|
2025-10-14 14:17:44 +08:00
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card
|
2026-04-03 02:05:29 +08:00
|
|
|
|
class="stat-card stat-card--nav stat-card--uuv-unit"
|
|
|
|
|
|
shadow="never"
|
|
|
|
|
|
@click.native="goToDetail('uuvUnit')"
|
2025-10-14 14:17:44 +08:00
|
|
|
|
>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<path d="M3 8l9-5 9 5-9 5-9-5z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
|
|
|
|
|
|
<path d="M3 8v8l9 5 9-5V8" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">参航单位统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge">{{ uuvUnitBadge }} 家</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-metrics" :class="{ 'stat-metrics--loading': summaryLoading }">
|
|
|
|
|
|
<div v-for="(m, idx) in uuvUnitCardMetrics" :key="'uu'+idx" class="stat-metric">
|
|
|
|
|
|
<span class="stat-metric__value">{{ m.display }}</span>
|
|
|
|
|
|
<span class="stat-metric__label">{{ m.label }}</span>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<p class="stat-card__fleet" :title="uuvNamesJoined">单位汇总与参航单位详情页一致 · 点击查看</p>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<!-- 参行科学家统计(无人航行潜水器) -->
|
2025-10-14 14:17:44 +08:00
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card
|
2026-04-03 02:05:29 +08:00
|
|
|
|
class="stat-card stat-card--nav stat-card--uuv-scientist"
|
|
|
|
|
|
shadow="never"
|
|
|
|
|
|
@click.native="goToDetail('uuvMember')"
|
2025-10-14 14:17:44 +08:00
|
|
|
|
>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<path d="M9 21h6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
|
|
|
|
<path d="M10 17l-2-2 2-6h4l2 6-2 2" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
|
|
|
|
|
|
<path d="M9 9V7a3 3 0 016 0v2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">参航科学家统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge">{{ uuvScientistBadge }} 人</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-metrics" :class="{ 'stat-metrics--loading': summaryLoading }">
|
|
|
|
|
|
<div v-for="(m, idx) in uuvScientistMetrics" :key="'ms'+idx" class="stat-metric">
|
|
|
|
|
|
<span class="stat-metric__value">{{ m.display }}</span>
|
|
|
|
|
|
<span class="stat-metric__label">{{ m.label }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stat-card__fleet" :title="uuvNamesJoined">默认单位:中国科学院深海科学与工程研究所 · 点击查看详情</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 基站汇总统计 -->
|
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card class="stat-card stat-card--placeholder stat-card--station" shadow="never">
|
|
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<path d="M12 3l8 4v6c0 5-3.5 9-8 10-4.5-1-8-5-8-10V7l8-4z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" />
|
|
|
|
|
|
<path d="M12 8v5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">基站汇总统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge stat-card__badge--muted">敬请期待</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-placeholder">
|
|
|
|
|
|
<span class="stat-placeholder__text">暂无数据</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 着陆器汇总统计 -->
|
|
|
|
|
|
<el-col :xs="24" :sm="12" :md="8">
|
|
|
|
|
|
<el-card class="stat-card stat-card--placeholder stat-card--lander" shadow="never">
|
|
|
|
|
|
<div class="stat-card__inner">
|
|
|
|
|
|
<div class="stat-card__top">
|
|
|
|
|
|
<div class="stat-card__icon-wrap" aria-hidden="true">
|
|
|
|
|
|
<svg class="stat-card__icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
<path d="M12 21s7-4.5 7-11a7 7 0 10-14 0c0 6.5 7 11 7 11z" stroke="currentColor" stroke-width="1.5" />
|
|
|
|
|
|
<circle cx="12" cy="10" r="2.5" stroke="currentColor" stroke-width="1.5" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card__head-text">
|
|
|
|
|
|
<h3 class="stat-card__title">着陆器汇总统计</h3>
|
|
|
|
|
|
<p class="stat-card__meta">
|
|
|
|
|
|
<span class="stat-card__badge stat-card__badge--muted">敬请期待</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<div class="stat-placeholder">
|
|
|
|
|
|
<span class="stat-placeholder__text">暂无数据</span>
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
2026-04-03 02:05:29 +08:00
|
|
|
|
import {
|
|
|
|
|
|
reportList,
|
|
|
|
|
|
shipTotal,
|
|
|
|
|
|
platformHov,
|
|
|
|
|
|
platformRov,
|
|
|
|
|
|
platformAuv,
|
|
|
|
|
|
hovUnit,
|
|
|
|
|
|
unitMember,
|
|
|
|
|
|
unitTotal
|
|
|
|
|
|
} from '../../api/statistics'
|
2025-10-31 16:15:28 +08:00
|
|
|
|
import { filterDictItems } from '../../utils/index'
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 与 UuvMemberStatistics 默认统计单位一致
|
|
|
|
|
|
* @type {string}
|
|
|
|
|
|
*/
|
|
|
|
|
|
const DEFAULT_MEMBER_STAT_UNIT_NAME = '中国科学院深海科学与工程研究所'
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param {*} v 原始值
|
|
|
|
|
|
* @returns {number}
|
|
|
|
|
|
*/
|
|
|
|
|
|
function num(v) {
|
|
|
|
|
|
if (v == null || v === '') return 0
|
|
|
|
|
|
const n = Number(v)
|
|
|
|
|
|
return Number.isFinite(n) ? n : 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 将 unitTotal 按单位行聚合为卡片用汇总(与 UuvUnitStatistics 全量口径一致)。
|
|
|
|
|
|
* @param {Object[]} list - report/unit/total.htm 的 array
|
|
|
|
|
|
* @returns {{ unitCount: number, voyageSum: number, daysSum: number, memberSum: number, drvingSum: number }|null}
|
|
|
|
|
|
*/
|
|
|
|
|
|
function aggregateUnitTotalForCard(list) {
|
|
|
|
|
|
if (!Array.isArray(list) || !list.length) return null
|
|
|
|
|
|
return list.reduce(
|
|
|
|
|
|
(acc, row) => ({
|
|
|
|
|
|
unitCount: acc.unitCount + 1,
|
|
|
|
|
|
voyageSum: acc.voyageSum + num(row.voyage_total),
|
|
|
|
|
|
daysSum: acc.daysSum + num(row.voyage_days),
|
|
|
|
|
|
memberSum: acc.memberSum + num(row.member_total),
|
|
|
|
|
|
drvingSum: acc.drvingSum + num(row.drving_total)
|
|
|
|
|
|
}),
|
|
|
|
|
|
{ unitCount: 0, voyageSum: 0, daysSum: 0, memberSum: 0, drvingSum: 0 }
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 统计分析入口:展示各平台类型入口卡片及与子页一致的汇总指标
|
|
|
|
|
|
*/
|
2025-10-14 14:17:44 +08:00
|
|
|
|
export default {
|
|
|
|
|
|
name: 'MainDashboard',
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
2025-10-31 16:15:28 +08:00
|
|
|
|
ship: [],
|
|
|
|
|
|
hov: [],
|
|
|
|
|
|
rov: [],
|
2026-04-03 02:05:29 +08:00
|
|
|
|
uuv: [],
|
|
|
|
|
|
/** 科考船汇总(与 ShipStatistics 顶部 banner 同源) */
|
|
|
|
|
|
shipSummary: null,
|
|
|
|
|
|
/** 载人潜水器汇总(与 HovStatistics 同源接口) */
|
|
|
|
|
|
hovSummary: null,
|
|
|
|
|
|
/** 遥控潜水器汇总 */
|
|
|
|
|
|
rovSummary: null,
|
|
|
|
|
|
/** 无人航行潜水器汇总 */
|
|
|
|
|
|
uuvSummary: null,
|
|
|
|
|
|
/** 参航科学家卡片:unitMember 聚合(默认单位) */
|
|
|
|
|
|
memberScientistCard: null,
|
|
|
|
|
|
/** 参航单位统计卡片:unitTotal 全表聚合(与 UuvUnitStatistics 同源) */
|
|
|
|
|
|
uuvUnitCardAgg: null,
|
|
|
|
|
|
summaryLoading: false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
shipNamesJoined() {
|
|
|
|
|
|
return this.joinReportNames(this.ship)
|
|
|
|
|
|
},
|
|
|
|
|
|
hovNamesJoined() {
|
|
|
|
|
|
return this.joinReportNames(this.hov)
|
|
|
|
|
|
},
|
|
|
|
|
|
rovNamesJoined() {
|
|
|
|
|
|
return this.joinReportNames(this.rov)
|
|
|
|
|
|
},
|
|
|
|
|
|
uuvNamesJoined() {
|
|
|
|
|
|
return this.joinReportNames(this.uuv)
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 科考船四项核心指标展示行 */
|
|
|
|
|
|
shipMetrics() {
|
|
|
|
|
|
const s = this.shipSummary
|
|
|
|
|
|
if (!s) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '航次', display: '—' },
|
|
|
|
|
|
{ label: '航次天数', display: '—' },
|
|
|
|
|
|
{ label: '参航单位', display: '—' },
|
|
|
|
|
|
{ label: '数据样品', display: '—' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '航次', display: this.formatMetric(s.member_total) + ' 次' },
|
|
|
|
|
|
{ label: '航次天数', display: this.formatMetric(s.days_total) + ' 天' },
|
|
|
|
|
|
{ label: '参航单位', display: this.formatMetric(s.unit_total) + ' 家' },
|
|
|
|
|
|
{ label: '数据样品', display: this.formatMetric(s.dataset_total) + ' 个' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 无人航行潜水器:参航单位数量(汇总接口兜底) */
|
|
|
|
|
|
uuvUnitValue() {
|
|
|
|
|
|
if (!this.uuvSummary) return '—'
|
|
|
|
|
|
return this.formatMetric(this.uuvSummary.unit_total)
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 参航单位卡片角标:优先 unitTotal 家数,否则 platformAuv 汇总 */
|
|
|
|
|
|
uuvUnitBadge() {
|
|
|
|
|
|
if (this.uuvUnitCardAgg != null) {
|
|
|
|
|
|
return this.formatMetric(this.uuvUnitCardAgg.unitCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.uuvUnitValue
|
|
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 参航单位统计卡片四项指标(与参航单位详情页 Banner/汇总一致)
|
|
|
|
|
|
* @returns {Array<{ label: string, display: string }>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
uuvUnitCardMetrics() {
|
|
|
|
|
|
const a = this.uuvUnitCardAgg
|
|
|
|
|
|
if (!a) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '参航单位', display: '—' },
|
|
|
|
|
|
{ label: '参航人数', display: '—' },
|
|
|
|
|
|
{ label: '深潜次数', display: '—' },
|
|
|
|
|
|
{ label: '航行次数', display: '—' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '参航单位', display: this.formatMetric(a.unitCount) + ' 家' },
|
|
|
|
|
|
{ label: '参航人数', display: this.formatMetric(a.memberSum) + ' 人' },
|
|
|
|
|
|
{ label: '深潜次数', display: this.formatMetric(a.drvingSum) + ' 次' },
|
|
|
|
|
|
{ label: '航行次数', display: this.formatMetric(a.voyageSum) + ' 次' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 无人航行潜水器:参航科学家数量(汇总接口兜底) */
|
|
|
|
|
|
uuvScientistValue() {
|
|
|
|
|
|
if (!this.uuvSummary) return '—'
|
|
|
|
|
|
return this.formatMetric(this.uuvSummary.driver_member_total)
|
|
|
|
|
|
},
|
|
|
|
|
|
/** 卡片角标:优先 unitMember 科学家人数,否则汇总接口人数 */
|
|
|
|
|
|
uuvScientistBadge() {
|
|
|
|
|
|
if (this.memberScientistCard && this.memberScientistCard.scientistCount != null) {
|
|
|
|
|
|
return this.formatMetric(this.memberScientistCard.scientistCount)
|
|
|
|
|
|
}
|
|
|
|
|
|
return this.uuvScientistValue
|
|
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 参航科学家卡片三项指标(与 Uuv 详情页 banner 一致)
|
|
|
|
|
|
* @returns {Array<{ label: string, display: string }>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
uuvScientistMetrics() {
|
|
|
|
|
|
const c = this.memberScientistCard
|
|
|
|
|
|
if (!c) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '科学家人数', display: '—' },
|
|
|
|
|
|
{ label: '累计参加航次', display: '—' },
|
|
|
|
|
|
{ label: '累计参航天数', display: '—' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '科学家人数', display: this.formatMetric(c.scientistCount) + ' 人' },
|
|
|
|
|
|
{ label: '累计参加航次', display: this.formatMetric(c.sumVoyage) + ' 次' },
|
|
|
|
|
|
{ label: '累计参航天数', display: this.formatMetric(c.sumDays) + ' 天' }
|
|
|
|
|
|
]
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-10-31 16:15:28 +08:00
|
|
|
|
created() {
|
|
|
|
|
|
this.getTypeList()
|
2026-04-03 02:05:29 +08:00
|
|
|
|
this.loadSummaries()
|
2025-10-31 16:15:28 +08:00
|
|
|
|
},
|
2025-10-14 14:17:44 +08:00
|
|
|
|
methods: {
|
|
|
|
|
|
goToDetail(type) {
|
2026-04-03 02:05:29 +08:00
|
|
|
|
this.$router.push(`/container/${type}`)
|
2025-10-31 16:15:28 +08:00
|
|
|
|
},
|
|
|
|
|
|
getTypeList() {
|
|
|
|
|
|
reportList().then(res => {
|
|
|
|
|
|
this.ship = filterDictItems(res.array, '科考船')
|
|
|
|
|
|
this.hov = filterDictItems(res.array, '载人潜水器')
|
|
|
|
|
|
this.rov = filterDictItems(res.array, '遥控潜水器')
|
2026-04-02 14:25:50 +08:00
|
|
|
|
this.auv = filterDictItems(res.array, '无人航行潜水器')
|
2025-10-31 16:15:28 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
2026-04-03 02:05:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 并行拉取各子页汇总接口,用于卡片内核心指标展示
|
|
|
|
|
|
*/
|
|
|
|
|
|
loadSummaries() {
|
|
|
|
|
|
this.summaryLoading = true
|
|
|
|
|
|
const pMemberCard = hovUnit()
|
|
|
|
|
|
.then(res => {
|
|
|
|
|
|
const list = res.array || []
|
|
|
|
|
|
const unit =
|
|
|
|
|
|
list.find(u => u.unit_name === DEFAULT_MEMBER_STAT_UNIT_NAME) || list[0]
|
|
|
|
|
|
if (!unit) return null
|
|
|
|
|
|
const key = unit.unit_id != null && unit.unit_id !== '' ? unit.unit_id : unit.unit_name
|
|
|
|
|
|
return unitMember({ unit: key })
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(memberRes => {
|
|
|
|
|
|
if (!memberRes || !Array.isArray(memberRes.array)) return null
|
|
|
|
|
|
const arr = memberRes.array
|
|
|
|
|
|
return {
|
|
|
|
|
|
scientistCount: arr.length,
|
|
|
|
|
|
sumVoyage: arr.reduce((s, m) => s + num(m.voyage_total), 0),
|
|
|
|
|
|
sumDays: arr.reduce((s, m) => s + num(m.days_total), 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(() => null)
|
|
|
|
|
|
|
|
|
|
|
|
const pUnitTotalCard = unitTotal({})
|
|
|
|
|
|
.then(res => aggregateUnitTotalForCard(res && res.array ? res.array : []))
|
|
|
|
|
|
.catch(() => null)
|
|
|
|
|
|
|
|
|
|
|
|
Promise.all([
|
|
|
|
|
|
shipTotal({}),
|
|
|
|
|
|
platformHov({}),
|
|
|
|
|
|
platformRov({}),
|
|
|
|
|
|
platformAuv({}),
|
|
|
|
|
|
pMemberCard,
|
|
|
|
|
|
pUnitTotalCard
|
|
|
|
|
|
])
|
|
|
|
|
|
.then(([shipRes, hovRes, rovRes, uuvRes, memberAgg, unitAgg]) => {
|
|
|
|
|
|
this.shipSummary = (shipRes.array && shipRes.array[0]) || null
|
|
|
|
|
|
this.hovSummary = (hovRes.array && hovRes.array[0]) || null
|
|
|
|
|
|
this.rovSummary = (rovRes.array && rovRes.array[0]) || null
|
|
|
|
|
|
this.uuvSummary = (uuvRes.array && uuvRes.array[0]) || null
|
|
|
|
|
|
this.memberScientistCard = memberAgg
|
|
|
|
|
|
this.uuvUnitCardAgg = unitAgg
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(() => {
|
|
|
|
|
|
this.shipSummary = null
|
|
|
|
|
|
this.hovSummary = null
|
|
|
|
|
|
this.rovSummary = null
|
|
|
|
|
|
this.uuvSummary = null
|
|
|
|
|
|
this.memberScientistCard = null
|
|
|
|
|
|
this.uuvUnitCardAgg = null
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
this.summaryLoading = false
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
joinReportNames(list) {
|
|
|
|
|
|
if (!list || !list.length) return '—'
|
|
|
|
|
|
return list.map(i => i.report_name).join('、')
|
|
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 载人/遥控/无人平台卡片指标(与子页 banner 字段一致)
|
|
|
|
|
|
* @param {object|null} row - 接口 array[0]
|
|
|
|
|
|
* @returns {Array<{ label: string, display: string }>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
platformMetrics(row) {
|
|
|
|
|
|
if (!row) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '潜次数', display: '—' },
|
|
|
|
|
|
{ label: '下潜人数', display: '—' },
|
|
|
|
|
|
{ label: '下潜单位', display: '—' },
|
|
|
|
|
|
{ label: '数据样品', display: '—' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ label: '潜次数', display: this.formatMetric(row.diving_total) + ' 次' },
|
|
|
|
|
|
{ label: '下潜人数', display: this.formatMetric(row.driver_member_total) + ' 人' },
|
|
|
|
|
|
{ label: '下潜单位', display: this.formatMetric(row.unit_total) + ' 家' },
|
|
|
|
|
|
{ label: '数据样品', display: this.formatMetric(row.dataset_total) + ' 个' }
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param {string|number|null|undefined} v
|
|
|
|
|
|
* @returns {string}
|
|
|
|
|
|
*/
|
|
|
|
|
|
formatMetric(v) {
|
|
|
|
|
|
if (v === null || v === undefined || v === '') return '—'
|
|
|
|
|
|
return String(v)
|
|
|
|
|
|
}
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
<style scoped lang="scss">
|
2025-10-14 14:17:44 +08:00
|
|
|
|
.main-dashboard {
|
2026-04-03 02:05:29 +08:00
|
|
|
|
padding: 24px 20px 32px;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
min-height: calc(100vh - 130px);
|
2026-04-03 02:05:29 +08:00
|
|
|
|
background: #e9eef4;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
|
text-align: center;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
margin-bottom: 28px;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header h1 {
|
2026-04-03 02:05:29 +08:00
|
|
|
|
color: #0f172a;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
font-size: 28px;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
margin: 0 0 10px;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
font-weight: 600;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
letter-spacing: 0.02em;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header p {
|
2026-04-03 02:05:29 +08:00
|
|
|
|
color: #475569;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
font-size: 16px;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
margin: 0;
|
|
|
|
|
|
line-height: 1.5;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stats-grid {
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
|
margin-bottom: 20px;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
border: 1px solid #e2e8f0;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
min-height: 280px;
|
|
|
|
|
|
transition:
|
|
|
|
|
|
box-shadow 0.22s ease,
|
|
|
|
|
|
border-color 0.22s ease,
|
|
|
|
|
|
transform 0.22s ease;
|
|
|
|
|
|
|
|
|
|
|
|
::v-deep .el-card__body {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card--nav {
|
2025-10-14 14:17:44 +08:00
|
|
|
|
cursor: pointer;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
transform: translateY(-3px);
|
|
|
|
|
|
border-color: #cbd5e1;
|
|
|
|
|
|
box-shadow: 0 14px 32px -12px rgba(15, 23, 42, 0.18);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card--placeholder {
|
|
|
|
|
|
cursor: default;
|
|
|
|
|
|
opacity: 0.92;
|
|
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
|
border-color: #e2e8f0;
|
|
|
|
|
|
}
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card__inner {
|
|
|
|
|
|
padding: 18px 18px 16px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
min-height: 280px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
/* 移除顶部描边(border-top) */
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__top {
|
2025-10-14 14:17:44 +08:00
|
|
|
|
display: flex;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 14px;
|
|
|
|
|
|
margin-bottom: 4px;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card__icon-wrap {
|
|
|
|
|
|
width: 60px;
|
|
|
|
|
|
height: 60px;
|
|
|
|
|
|
border-radius: 14px;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card--ship .stat-card__icon-wrap {
|
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
|
color: #1d4ed8;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card--hov .stat-card__icon-wrap {
|
|
|
|
|
|
background: #f0fdfa;
|
|
|
|
|
|
color: #0d9488;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card--rov .stat-card__icon-wrap {
|
|
|
|
|
|
background: #f5f3ff;
|
|
|
|
|
|
color: #6d28d9;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card--uuv .stat-card__icon-wrap {
|
|
|
|
|
|
background: #fffbeb;
|
|
|
|
|
|
color: #b45309;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card--uuv-unit .stat-card__icon-wrap {
|
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
|
color: #1d4ed8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card--uuv-scientist .stat-card__icon-wrap {
|
|
|
|
|
|
background: #f5f3ff;
|
|
|
|
|
|
color: #6d28d9;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card--station .stat-card__icon-wrap,
|
|
|
|
|
|
.stat-card--lander .stat-card__icon-wrap {
|
|
|
|
|
|
background: #f1f5f9;
|
|
|
|
|
|
color: #64748b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__icon {
|
|
|
|
|
|
width: 32px;
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__head-text {
|
2025-10-14 14:17:44 +08:00
|
|
|
|
flex: 1;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
min-width: 0;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card__title {
|
2025-10-14 14:17:44 +08:00
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
color: #0f172a;
|
|
|
|
|
|
margin: 0 0 6px;
|
|
|
|
|
|
line-height: 1.35;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card__meta {
|
|
|
|
|
|
margin: 0;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card__badge {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #475569;
|
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
background: #f1f5f9;
|
|
|
|
|
|
letter-spacing: 0.02em;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-card__badge--muted {
|
|
|
|
|
|
color: #94a3b8;
|
|
|
|
|
|
background: #f8fafc;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-metrics {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
margin-top: 14px;
|
|
|
|
|
|
flex: 1;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-metrics--loading .stat-metric {
|
|
|
|
|
|
opacity: 0.55;
|
|
|
|
|
|
}
|
2025-10-14 14:17:44 +08:00
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-metric {
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
|
border: 1px solid #f1f5f9;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
2025-10-14 14:17:44 +08:00
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.stat-metric__value {
|
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #0f172a;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-metric__label {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #64748b;
|
|
|
|
|
|
letter-spacing: 0.02em;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__fleet {
|
|
|
|
|
|
margin: 12px 0 0;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #64748b;
|
|
|
|
|
|
line-height: 1.45;
|
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-placeholder {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
min-height: 160px;
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
border: 1px dashed #cbd5e1;
|
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-placeholder__text {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #94a3b8;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@keyframes fadeInUp {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
2026-04-03 02:05:29 +08:00
|
|
|
|
transform: translateY(12px);
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
2026-04-03 02:05:29 +08:00
|
|
|
|
animation: fadeInUp 0.45s ease forwards;
|
2025-10-14 14:17:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-03 02:05:29 +08:00
|
|
|
|
.el-col:nth-child(1) .stat-card {
|
|
|
|
|
|
animation-delay: 0.05s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.el-col:nth-child(2) .stat-card {
|
|
|
|
|
|
animation-delay: 0.1s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.el-col:nth-child(3) .stat-card {
|
|
|
|
|
|
animation-delay: 0.15s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.el-col:nth-child(4) .stat-card {
|
|
|
|
|
|
animation-delay: 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.el-col:nth-child(5) .stat-card {
|
|
|
|
|
|
animation-delay: 0.25s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.el-col:nth-child(6) .stat-card {
|
|
|
|
|
|
animation-delay: 0.3s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.el-col:nth-child(7) .stat-card {
|
|
|
|
|
|
animation-delay: 0.35s;
|
|
|
|
|
|
}
|
|
|
|
|
|
.el-col:nth-child(8) .stat-card {
|
|
|
|
|
|
animation-delay: 0.4s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
|
min-height: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__inner {
|
|
|
|
|
|
min-height: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-14 14:17:44 +08:00
|
|
|
|
</style>
|