|
|
@@ -1,792 +1,141 @@
|
|
|
<template>
|
|
|
- <div class="global-variable">
|
|
|
- <div class="searchbox">
|
|
|
- <p>
|
|
|
- 单位:
|
|
|
- <selecttree
|
|
|
- size="small"
|
|
|
- style="width: 180px"
|
|
|
- placeholder="请选择所属公司"
|
|
|
- :list="parentOpt"
|
|
|
- type="1"
|
|
|
- v-model="companyCode"
|
|
|
- :defaultParentProps="{
|
|
|
- children: 'children',
|
|
|
- label: 'companyName',
|
|
|
- value: 'codeNumber',
|
|
|
- }"
|
|
|
- />
|
|
|
- </p>
|
|
|
-
|
|
|
- <p>
|
|
|
- 时间:
|
|
|
- <el-date-picker
|
|
|
- v-model="timevalue"
|
|
|
- size="small"
|
|
|
- type="month"
|
|
|
- placeholder="选择月"
|
|
|
- :picker-options="monthPickerOptions"
|
|
|
- />
|
|
|
- </p>
|
|
|
-
|
|
|
- <el-button type="primary" size="small" @click="conditions">
|
|
|
- 查询
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="kuang">
|
|
|
- <!-- 加载中动画 -->
|
|
|
- <div v-if="loading" class="loader-wrapper">
|
|
|
- <div class="loader"></div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 空状态 -->
|
|
|
- <el-empty
|
|
|
- class="Anempty"
|
|
|
- v-if="!loading && ListData.length === 0"
|
|
|
- :image-size="200"
|
|
|
- />
|
|
|
-
|
|
|
- <!-- 主体内容 -->
|
|
|
- <div class="main-body" v-if="!loading && ListData.length > 0">
|
|
|
- <div
|
|
|
- class="basics"
|
|
|
- v-for="(item, index) in ListData"
|
|
|
- :key="item + index"
|
|
|
- >
|
|
|
- <div class="title">
|
|
|
- <span>
|
|
|
- <SvgIcons
|
|
|
- name="WindPower3"
|
|
|
- class="WindPower3"
|
|
|
- width="26"
|
|
|
- height="26"
|
|
|
- />
|
|
|
- </span>
|
|
|
- <h4>风机:{{ item.wind_turbine_name }}</h4>
|
|
|
- <span class="mill">{{ millTypeMap[item.mill_type] || "-" }}</span>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="content">
|
|
|
- <div class="summary" style="color: #28a745">
|
|
|
- <span :style="{ color: getHealthColor(item.total_health_score) }">
|
|
|
- {{
|
|
|
- item.total_health_score < 0
|
|
|
- ? "-"
|
|
|
- : Math.floor(item.total_health_score)
|
|
|
- }}
|
|
|
- </span>
|
|
|
- <span :style="{ color: getHealthColor(item.total_health_score) }">
|
|
|
- {{ getHealthLabel(item.total_health_score) }}
|
|
|
- </span>
|
|
|
- </div>
|
|
|
-
|
|
|
- <ul class="health">
|
|
|
- <li>
|
|
|
- <span>发电机组</span>
|
|
|
- <span :class="'health-' + item.val">
|
|
|
- {{
|
|
|
- item.subsystems.generator.health_score >= 0
|
|
|
- ? String(
|
|
|
- Math.floor(item.subsystems.generator.health_score),
|
|
|
- ).slice(0, 3)
|
|
|
- : "-"
|
|
|
- }}
|
|
|
- </span>
|
|
|
- </li>
|
|
|
-
|
|
|
- <li v-if="item.subsystems.drive_train">
|
|
|
- <span>传动系统</span>
|
|
|
- <span :class="'health-' + item.val">
|
|
|
- {{
|
|
|
- item.subsystems.drive_train.health_score >= 0
|
|
|
- ? String(
|
|
|
- Math.floor(item.subsystems.drive_train.health_score),
|
|
|
- ).slice(0, 3)
|
|
|
- : "-"
|
|
|
- }}
|
|
|
- </span>
|
|
|
- </li>
|
|
|
-
|
|
|
- <li>
|
|
|
- <span>机舱系统</span>
|
|
|
- <span :class="'health-' + item.val">
|
|
|
- {{
|
|
|
- item.subsystems.nacelle.health_score >= 0
|
|
|
- ? String(
|
|
|
- Math.floor(item.subsystems.nacelle.health_score),
|
|
|
- ).slice(0, 3)
|
|
|
- : "-"
|
|
|
- }}
|
|
|
- </span>
|
|
|
- </li>
|
|
|
-
|
|
|
- <li>
|
|
|
- <span>电网环境</span>
|
|
|
- <span :class="'health-' + item.val">
|
|
|
- {{
|
|
|
- item.subsystems.grid.health_score >= 0
|
|
|
- ? String(
|
|
|
- Math.floor(item.subsystems.grid.health_score),
|
|
|
- ).slice(0, 3)
|
|
|
- : "-"
|
|
|
- }}
|
|
|
- </span>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 健康评估建议(通用展示) -->
|
|
|
- <!-- <div class="advice-box">
|
|
|
- <div class="advice-head">
|
|
|
- <span class="advice-title">健康评估建议</span>
|
|
|
- <span
|
|
|
- class="advice-badge"
|
|
|
- :class="`is-${getHealthLevelKey(item.total_health_score)}`"
|
|
|
- >
|
|
|
- {{ getHealthLabel(item.total_health_score) }}
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- <p class="advice-text">
|
|
|
- {{ getHealthAdvice(item.total_health_score) }}
|
|
|
- </p>
|
|
|
- </div> -->
|
|
|
+ <div class="health-dashboard-page">
|
|
|
+ <HealthHeader v-model="companyCode" @change="handleFarmChange" />
|
|
|
+
|
|
|
+ <div class="health-page__body">
|
|
|
+ <main class="dashboard-main">
|
|
|
+ <div class="dashboard-top-row">
|
|
|
+ <HealthScorePanel
|
|
|
+ :distribution="scoreDistribution"
|
|
|
+ :summary="scoreSummary"
|
|
|
+ :total-score="totalScore"
|
|
|
+ />
|
|
|
+
|
|
|
+ <HealthSubsystemPanel :chart-data="subsystemData" />
|
|
|
+
|
|
|
+ <HealthRankingPanel
|
|
|
+ :healthy-list="healthyRankList"
|
|
|
+ :risk-list="riskRankList"
|
|
|
+ />
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 健康度说明(卡片版) -->
|
|
|
- <div class="health-legend">
|
|
|
- <div class="legend-head">
|
|
|
- <span class="legend-title">健康度说明</span>
|
|
|
- </div>
|
|
|
- <div class="legend-body">
|
|
|
- <div class="legend-item is-good">
|
|
|
- <div class="legend-icon" aria-hidden="true">
|
|
|
- <svg viewBox="0 0 24 24" class="legend-icon-svg">
|
|
|
- <path
|
|
|
- class="shield"
|
|
|
- d="M12 2.5 19 6.2v6.4c0 5.2-3.6 9.8-7 10.9-3.4-1.1-7-5.7-7-10.9V6.2L12 2.5Z"
|
|
|
- />
|
|
|
- <path class="mark" d="M8.2 12.2l2.3 2.3 5.3-5.3" />
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <div class="legend-content">
|
|
|
- <p class="legend-range">100–85分(健康)</p>
|
|
|
- <p class="legend-advice">
|
|
|
- 建议:按计划进行常规巡检与维护,保持当前运行状态,无需额外检修。
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="legend-item is-subhealthy">
|
|
|
- <div class="legend-icon" aria-hidden="true">
|
|
|
- <svg viewBox="0 0 24 24" class="legend-icon-svg">
|
|
|
- <path
|
|
|
- class="shield"
|
|
|
- d="M12 2.5 19 6.2v6.4c0 5.2-3.6 9.8-7 10.9-3.4-1.1-7-5.7-7-10.9V6.2L12 2.5Z"
|
|
|
- />
|
|
|
- <path class="mark" d="M12 7.6v6.2" />
|
|
|
- <path class="mark" d="M12 16.9h.01" />
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <div class="legend-content">
|
|
|
- <p class="legend-range">84–70分(亚健康)</p>
|
|
|
- <p class="legend-advice">
|
|
|
- 建议:加强状态监测,缩短巡检周期,排查潜在隐患,可安排预防性维护。
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="legend-item is-normal">
|
|
|
- <div class="legend-icon" aria-hidden="true">
|
|
|
- <svg viewBox="0 0 24 24" class="legend-icon-svg">
|
|
|
- <path
|
|
|
- class="shield"
|
|
|
- d="M12 2.5 19 6.2v6.4c0 5.2-3.6 9.8-7 10.9-3.4-1.1-7-5.7-7-10.9V6.2L12 2.5Z"
|
|
|
- />
|
|
|
- <path class="mark" d="M12 7.6v6.2" />
|
|
|
- <path class="mark" d="M12 16.9h.01" />
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <div class="legend-content">
|
|
|
- <p class="legend-range">69–55分(一般)</p>
|
|
|
- <p class="legend-advice">
|
|
|
- 建议:开展专项检测与故障诊断,制定针对性检修计划,及时处理发现的问题,避免状态进一步恶化。
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="legend-item is-fault">
|
|
|
- <div class="legend-icon" aria-hidden="true">
|
|
|
- <svg viewBox="0 0 24 24" class="legend-icon-svg">
|
|
|
- <path
|
|
|
- class="shield"
|
|
|
- d="M12 2.5 19 6.2v6.4c0 5.2-3.6 9.8-7 10.9-3.4-1.1-7-5.7-7-10.9V6.2L12 2.5Z"
|
|
|
- />
|
|
|
- <path class="mark" d="M9 9l6 6" />
|
|
|
- <path class="mark" d="M15 9l-6 6" />
|
|
|
- </svg>
|
|
|
- </div>
|
|
|
- <div class="legend-content">
|
|
|
- <p class="legend-range">55分以下(故障)</p>
|
|
|
- <p class="legend-advice">
|
|
|
- 建议:立即停机或安排紧急检修,进行全面故障排查与修复,必要时更换关键部件,确保设备恢复正常运行能力。
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <section class="health-unit-grid">
|
|
|
+ <HealthUnitCard
|
|
|
+ v-for="unit in unitCards"
|
|
|
+ :key="unit.name"
|
|
|
+ :unit="unit"
|
|
|
+ />
|
|
|
+ </section>
|
|
|
+ </main>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { getSysOrganizationAuthTreeByRoleId } from "@/api/ledger.js";
|
|
|
-import selecttree from "../../components/selecttree.vue";
|
|
|
-import axios from "axios";
|
|
|
+import HealthHeader from "./components/health/HealthHeader.vue";
|
|
|
+import HealthScorePanel from "./components/health/HealthScorePanel.vue";
|
|
|
+import HealthSubsystemPanel from "./components/health/HealthSubsystemPanel.vue";
|
|
|
+import HealthRankingPanel from "./components/health/HealthRankingPanel.vue";
|
|
|
+import HealthUnitCard from "./components/health/HealthUnitCard.vue";
|
|
|
+import {
|
|
|
+ healthyRankList,
|
|
|
+ riskRankList,
|
|
|
+ scoreDistribution,
|
|
|
+ scoreSummary,
|
|
|
+ subsystemData,
|
|
|
+ unitCards,
|
|
|
+} from "./components/health/dashboardData";
|
|
|
|
|
|
export default {
|
|
|
+ name: "HealthDashboard",
|
|
|
components: {
|
|
|
- selecttree,
|
|
|
+ HealthHeader,
|
|
|
+ HealthScorePanel,
|
|
|
+ HealthSubsystemPanel,
|
|
|
+ HealthRankingPanel,
|
|
|
+ HealthUnitCard,
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
|
- parentOpt: [],
|
|
|
companyCode: "",
|
|
|
- ListData: [],
|
|
|
- loading: false, // ✅ 加载状态.
|
|
|
- // demo演示
|
|
|
- timevalue: new Date(2024, 6), // 7 月是 6,因为月份从 0 开始
|
|
|
+ totalScore: 92,
|
|
|
+ scoreDistribution,
|
|
|
+ scoreSummary,
|
|
|
+ subsystemData,
|
|
|
+ healthyRankList,
|
|
|
+ riskRankList,
|
|
|
+ unitCards,
|
|
|
};
|
|
|
},
|
|
|
- computed: {
|
|
|
- millTypeMap() {
|
|
|
- return {
|
|
|
- dfig: "双馈",
|
|
|
- direct: "直驱",
|
|
|
- semi: "半直驱",
|
|
|
- };
|
|
|
- },
|
|
|
-
|
|
|
- monthPickerOptions() {
|
|
|
- return {
|
|
|
- disabledDate: (time) => {
|
|
|
- const code = this.companyCode;
|
|
|
- const date = time;
|
|
|
-
|
|
|
- if (code === "WOF046400029") {
|
|
|
- const start = new Date("2023-10-01");
|
|
|
- const end = new Date("2024-10-30");
|
|
|
- return date < start || date > end;
|
|
|
- } else if (code === "WOF091200030") {
|
|
|
- const start = new Date("2023-01-01");
|
|
|
- const end = new Date("2024-11-30");
|
|
|
- return date < start || date > end;
|
|
|
- } else if (code === "WOF093400005") {
|
|
|
- const start = new Date("2023-12-01");
|
|
|
- const end = new Date("2024-05-30");
|
|
|
- return date < start || date > end;
|
|
|
- } else {
|
|
|
- // 其他公司不限制时间选择
|
|
|
- return false;
|
|
|
- }
|
|
|
- },
|
|
|
- };
|
|
|
- },
|
|
|
- },
|
|
|
- created() {
|
|
|
- this.GETtree();
|
|
|
+ mounted() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ window.dispatchEvent(new Event("resize"));
|
|
|
+ });
|
|
|
},
|
|
|
methods: {
|
|
|
- async GETtree() {
|
|
|
- const res = await getSysOrganizationAuthTreeByRoleId();
|
|
|
- const treedata = res.data;
|
|
|
- const processedData = this.processTreeData(treedata);
|
|
|
- this.parentOpt = processedData;
|
|
|
- this.defaultdata = res.data[0];
|
|
|
- // demo演示
|
|
|
- this.$nextTick(() => {
|
|
|
- this.companyCode = "WOF046400029";
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- processTreeData(treeData) {
|
|
|
- const processedData = [];
|
|
|
- function processNode(node) {
|
|
|
- if (node.codeType === "field") {
|
|
|
- node.companyName = node.fieldName;
|
|
|
- }
|
|
|
- if (node.children && node.children.length > 0) {
|
|
|
- node.children.forEach((child) => {
|
|
|
- processNode(child);
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- treeData.forEach((root) => {
|
|
|
- processNode(root);
|
|
|
- processedData.push(root);
|
|
|
- });
|
|
|
- return processedData;
|
|
|
- },
|
|
|
-
|
|
|
- getHealthLabel(val) {
|
|
|
- if (!Number.isFinite(val) || val < 0) return "无数据";
|
|
|
- if (val >= 85) return "健康";
|
|
|
- if (val >= 70) return "亚健康";
|
|
|
- if (val >= 55) return "一般";
|
|
|
- return "故障";
|
|
|
- },
|
|
|
-
|
|
|
- getHealthLevelKey(val) {
|
|
|
- if (!Number.isFinite(val) || val < 0) return "nodata";
|
|
|
- if (val >= 85) return "good";
|
|
|
- if (val >= 70) return "subhealthy";
|
|
|
- if (val >= 55) return "normal";
|
|
|
- return "fault";
|
|
|
- },
|
|
|
-
|
|
|
- getHealthAdvice(val) {
|
|
|
- if (!Number.isFinite(val) || val < 0) return "暂无健康评估数据。";
|
|
|
- if (val >= 85) {
|
|
|
- return "按计划进行常规巡检与维护,保持当前运行状态,无需额外检修。";
|
|
|
- }
|
|
|
- if (val >= 70) {
|
|
|
- return "加强状态监测,缩短巡检周期,排查潜在隐患,可安排预防性维护。";
|
|
|
- }
|
|
|
- if (val >= 55) {
|
|
|
- return "开展专项检测与故障诊断,制定针对性检修计划,及时处理发现的问题,避免状态进一步恶化。";
|
|
|
- }
|
|
|
- return "立即停机或安排紧急检修,进行全面故障排查与修复,必要时更换关键部件,确保设备恢复正常运行能力。";
|
|
|
- },
|
|
|
-
|
|
|
- getHealthColor(val) {
|
|
|
- if (!Number.isFinite(val) || val < 0) return "#6c757d"; // 灰色
|
|
|
- if (val >= 85) return "#28a745"; // 绿色
|
|
|
- if (val >= 70) return "#ffc107"; // 黄色
|
|
|
- if (val >= 55) return "#fd7e14"; // 橙色
|
|
|
- return "#dc3545"; // 红色
|
|
|
- },
|
|
|
-
|
|
|
- conditions() {
|
|
|
- const fullStr = this.$formatDateTWO(this.timevalue);
|
|
|
- const datePart = typeof fullStr === "string" ? fullStr.slice(0, 7) : "";
|
|
|
- const parms = {
|
|
|
- windcode: this.companyCode,
|
|
|
- month: datePart,
|
|
|
- };
|
|
|
-
|
|
|
- this.loading = true; // ✅ 显示 loading
|
|
|
- const start = performance.now(); // ⏱️ 请求开始时间
|
|
|
-
|
|
|
- axios
|
|
|
- .post(`/AnalysisMulti/health_assess`, parms)
|
|
|
- .then((res) => {
|
|
|
- this.ListData = res.data;
|
|
|
- const end = performance.now(); // ⏱️ 请求结束时间
|
|
|
- const duration = ((end - start) / 1000).toFixed(2);
|
|
|
- console.log(`接口请求耗时:${duration} 秒`);
|
|
|
- })
|
|
|
- .catch((error) => {
|
|
|
- console.error("Error fetching data:", error);
|
|
|
- const end = performance.now(); // ⏱️ 请求结束时间
|
|
|
- const duration = ((end - start) / 1000).toFixed(2);
|
|
|
- console.log(`接口请求耗时:${duration} 秒`);
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- this.loading = false; // ✅ 隐藏 loading
|
|
|
- });
|
|
|
+ handleFarmChange(data) {
|
|
|
+ if (!data || !data.codeNumber) return;
|
|
|
+ // TODO: 切换风场/组织后刷新仪表盘数据
|
|
|
+ console.log("当前选中:", data.companyName || data.fieldName, data);
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
-<style lang="scss" scoped>
|
|
|
-.searchbox {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- margin: 0 0 20px 0;
|
|
|
- color: #ffffff;
|
|
|
+<style lang="scss">
|
|
|
+@import "./components/health/health-dashboard.scss";
|
|
|
|
|
|
- p {
|
|
|
- margin-right: 10px;
|
|
|
- color: #b7dff5;
|
|
|
- }
|
|
|
- .el-select {
|
|
|
- width: 180px;
|
|
|
+.health-dashboard-page {
|
|
|
+ .dashboard-top-row {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ gap: 24px;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-.el-input__inner {
|
|
|
- width: 340px;
|
|
|
-}
|
|
|
-
|
|
|
-.main-body {
|
|
|
- margin: 0 auto;
|
|
|
- display: grid;
|
|
|
- grid-template-columns: repeat(auto-fill, 260px); /* 严格保持300px宽度 */
|
|
|
- justify-content: space-evenly; /* 均匀分布 */
|
|
|
- gap: 10px;
|
|
|
-
|
|
|
- // height: min-content;
|
|
|
- // max-height: 700px;
|
|
|
- overflow: hidden;
|
|
|
- overflow-y: auto;
|
|
|
- .basics {
|
|
|
- width: 260px; /* 固定宽度 */
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- margin-bottom: 20px;
|
|
|
- border: 1px solid var(--panel-border, rgba(0, 170, 255, 0.35));
|
|
|
- background: rgba(0, 16, 32, 0.45);
|
|
|
-
|
|
|
- .title {
|
|
|
- padding: 8px 10px;
|
|
|
- width: 100%;
|
|
|
- border-bottom: 1px solid var(--panel-border, rgba(0, 170, 255, 0.25));
|
|
|
- text-align: left;
|
|
|
-
|
|
|
- display: flex;
|
|
|
- align-items: center; // 垂直居中
|
|
|
- // justify-content: center; // 水平居中
|
|
|
|
|
|
- span {
|
|
|
- display: inline-block;
|
|
|
- margin-right: 6px;
|
|
|
-
|
|
|
- svg {
|
|
|
- display: block;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- h4 {
|
|
|
- flex: 1;
|
|
|
- min-width: 0;
|
|
|
- color: #ffffff;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 600;
|
|
|
- margin: 0;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- }
|
|
|
-
|
|
|
- .mill {
|
|
|
- flex-shrink: 0;
|
|
|
- font-size: 12px;
|
|
|
- color: #b7dff5;
|
|
|
- background: rgba(0, 170, 255, 0.12);
|
|
|
- border: 1px solid rgba(0, 170, 255, 0.35);
|
|
|
- padding: 2px 8px;
|
|
|
- border-radius: 4px;
|
|
|
- line-height: 1.4;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .content {
|
|
|
- display: flex;
|
|
|
- width: 100%;
|
|
|
- gap: 28px;
|
|
|
- .summary {
|
|
|
- width: 70px;
|
|
|
- display: flex;
|
|
|
- flex-direction: column; // 垂直排列
|
|
|
- align-items: center; // 居中对齐
|
|
|
- justify-content: center;
|
|
|
- font-weight: bold;
|
|
|
- padding-left: 26px;
|
|
|
- span {
|
|
|
- display: block;
|
|
|
- text-align: center;
|
|
|
- line-height: 1.3;
|
|
|
- &:first-child {
|
|
|
- font-size: 1.6em; // 更大的字体
|
|
|
- margin-bottom: 4px;
|
|
|
- }
|
|
|
- &:last-child {
|
|
|
- font-size: 0.8em; // 稍小一点
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .health {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- flex-wrap: wrap;
|
|
|
- gap: 6px;
|
|
|
- list-style: none;
|
|
|
- padding: 0;
|
|
|
- margin: 0;
|
|
|
-
|
|
|
- li {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- padding: 6px 6px;
|
|
|
- border-radius: 4px;
|
|
|
- min-width: 60px;
|
|
|
- font-size: 0.8em;
|
|
|
-
|
|
|
- span {
|
|
|
- display: block;
|
|
|
- text-align: center;
|
|
|
- line-height: 1.3;
|
|
|
-
|
|
|
- &:first-child {
|
|
|
- font-size: 0.9em;
|
|
|
- font-weight: 600;
|
|
|
- margin-bottom: 2px;
|
|
|
- color: #b7dff5;
|
|
|
- }
|
|
|
-
|
|
|
- &:last-child {
|
|
|
- color: #ffffff;
|
|
|
- font-weight: 600;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ @media (min-width: 900px) {
|
|
|
+ .dashboard-top-row {
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
}
|
|
|
|
|
|
- .advice-box {
|
|
|
- margin-top: 10px;
|
|
|
- padding: 10px 10px;
|
|
|
- border-top: 1px solid var(--header-bg);
|
|
|
- background: #fff;
|
|
|
-
|
|
|
- .advice-head {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- gap: 8px;
|
|
|
- margin-bottom: 6px;
|
|
|
- }
|
|
|
- .advice-title {
|
|
|
- font-size: 12px;
|
|
|
- font-weight: 700;
|
|
|
- color: #303133;
|
|
|
- }
|
|
|
- .advice-badge {
|
|
|
- font-size: 12px;
|
|
|
- font-weight: 700;
|
|
|
- padding: 2px 8px;
|
|
|
- border-radius: 999px;
|
|
|
- &.is-good {
|
|
|
- color: #1f7a3a;
|
|
|
- background: #e7f6ee;
|
|
|
- }
|
|
|
- &.is-subhealthy {
|
|
|
- color: #8a6d00;
|
|
|
- background: #fff6e5;
|
|
|
- }
|
|
|
- &.is-normal {
|
|
|
- color: #9a4d00;
|
|
|
- background: #fff0e6;
|
|
|
- }
|
|
|
- &.is-fault {
|
|
|
- color: #b42318;
|
|
|
- background: #ffebee;
|
|
|
- }
|
|
|
- &.is-nodata {
|
|
|
- color: #606266;
|
|
|
- background: #f4f4f5;
|
|
|
- }
|
|
|
- }
|
|
|
- .advice-text {
|
|
|
- margin: 0;
|
|
|
- font-size: 12px;
|
|
|
- line-height: 1.45;
|
|
|
- color: #606266;
|
|
|
- word-break: break-all;
|
|
|
- }
|
|
|
+ .dashboard-top-row > :last-child {
|
|
|
+ grid-column: span 2;
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
-.jia {
|
|
|
- color: var(--header-bg);
|
|
|
-}
|
|
|
-.kuang {
|
|
|
- min-height: 79vh;
|
|
|
- height: 680px;
|
|
|
- position: relative;
|
|
|
- overflow: hidden;
|
|
|
- overflow-y: auto;
|
|
|
- .Anempty {
|
|
|
- position: absolute;
|
|
|
- top: 50%;
|
|
|
- left: 50%;
|
|
|
- transform: translate(-50%, -50%);
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-.health-legend {
|
|
|
- margin-top: 14px;
|
|
|
- border-radius: 14px;
|
|
|
- background: #fff;
|
|
|
- box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.08);
|
|
|
- border: 1px solid rgba(228, 231, 237, 0.8);
|
|
|
- overflow: hidden;
|
|
|
+ @media (min-width: 1280px) {
|
|
|
+ .dashboard-top-row {
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
+ }
|
|
|
|
|
|
- .legend-head {
|
|
|
- padding: 12px 14px 0;
|
|
|
- }
|
|
|
- .legend-title {
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 800;
|
|
|
- color: #303133;
|
|
|
+ .dashboard-top-row > :last-child {
|
|
|
+ grid-column: auto;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- .legend-body {
|
|
|
+ .health-unit-grid {
|
|
|
display: grid;
|
|
|
- grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
|
- gap: 0;
|
|
|
- padding: 10px 10px 12px;
|
|
|
- }
|
|
|
-
|
|
|
- .legend-item {
|
|
|
- display: flex;
|
|
|
- align-items: flex-start;
|
|
|
- gap: 10px;
|
|
|
- padding: 10px 12px;
|
|
|
- position: relative;
|
|
|
- min-height: 88px;
|
|
|
- }
|
|
|
- .legend-item + .legend-item::before {
|
|
|
- content: "";
|
|
|
- position: absolute;
|
|
|
- left: 0;
|
|
|
- top: 16px;
|
|
|
- bottom: 16px;
|
|
|
- width: 1px;
|
|
|
- background: rgba(220, 223, 230, 0.7);
|
|
|
- }
|
|
|
-
|
|
|
- .legend-icon {
|
|
|
- width: 34px;
|
|
|
- height: 34px;
|
|
|
- flex: 0 0 auto;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- color: currentColor;
|
|
|
- }
|
|
|
- .legend-icon-svg {
|
|
|
- width: 22px;
|
|
|
- height: 22px;
|
|
|
- }
|
|
|
- .legend-icon-svg .shield {
|
|
|
- fill: none;
|
|
|
- stroke: currentColor;
|
|
|
- stroke-width: 1.8;
|
|
|
- stroke-linejoin: round;
|
|
|
- }
|
|
|
- .legend-icon-svg .mark {
|
|
|
- fill: none;
|
|
|
- stroke: currentColor;
|
|
|
- stroke-width: 2.2;
|
|
|
- stroke-linecap: round;
|
|
|
- stroke-linejoin: round;
|
|
|
- }
|
|
|
- .legend-content {
|
|
|
- flex: 1;
|
|
|
+ grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
|
+ gap: 16px;
|
|
|
min-width: 0;
|
|
|
}
|
|
|
- .legend-range {
|
|
|
- margin: 0 0 6px;
|
|
|
- font-size: 13px;
|
|
|
- font-weight: 800;
|
|
|
- line-height: 1.2;
|
|
|
- }
|
|
|
- .legend-advice {
|
|
|
- margin: 0;
|
|
|
- font-size: 12px;
|
|
|
- line-height: 1.45;
|
|
|
- color: #606266;
|
|
|
- word-break: break-all;
|
|
|
- }
|
|
|
|
|
|
- .legend-item.is-good {
|
|
|
- color: #28a745;
|
|
|
- .legend-range {
|
|
|
- color: inherit;
|
|
|
- }
|
|
|
- }
|
|
|
- .legend-item.is-subhealthy {
|
|
|
- color: #ffc107;
|
|
|
- .legend-range {
|
|
|
- color: inherit;
|
|
|
- }
|
|
|
- }
|
|
|
- .legend-item.is-normal {
|
|
|
- color: #fd7e14;
|
|
|
- .legend-range {
|
|
|
- color: inherit;
|
|
|
- }
|
|
|
- }
|
|
|
- .legend-item.is-fault {
|
|
|
- color: #dc3545;
|
|
|
- .legend-range {
|
|
|
- color: inherit;
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .health-unit-grid {
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-@media (max-width: 768px) {
|
|
|
- .health-legend {
|
|
|
- .legend-body {
|
|
|
- grid-template-columns: 1fr;
|
|
|
- }
|
|
|
- .legend-item + .legend-item::before {
|
|
|
- display: none;
|
|
|
- }
|
|
|
- .legend-item {
|
|
|
- border-top: 1px solid rgba(220, 223, 230, 0.7);
|
|
|
- }
|
|
|
- .legend-item:first-child {
|
|
|
- border-top: 0;
|
|
|
+ @media (min-width: 1024px) {
|
|
|
+ .health-unit-grid {
|
|
|
+ grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-// 动画
|
|
|
-/* ✅ loading 包裹容器 */
|
|
|
-.loader-wrapper {
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- height: 100%; /* 可根据页面需要调整高度 */
|
|
|
-}
|
|
|
-
|
|
|
-/* ✅ loading 样式动画 */
|
|
|
-
|
|
|
-.loader {
|
|
|
- width: 50px;
|
|
|
- --b: 8px;
|
|
|
- aspect-ratio: 1;
|
|
|
- border-radius: 50%;
|
|
|
- padding: 1px;
|
|
|
- background: conic-gradient(#0000 10%, var(--header-bg)) content-box;
|
|
|
-
|
|
|
- -webkit-mask: repeating-conic-gradient(
|
|
|
- #0000 0deg,
|
|
|
- #000 1deg 20deg,
|
|
|
- #0000 21deg 36deg
|
|
|
- ),
|
|
|
- radial-gradient(
|
|
|
- farthest-side,
|
|
|
- #0000 calc(100% - var(--b) - 1px),
|
|
|
- #000 calc(100% - var(--b))
|
|
|
- );
|
|
|
- mask: repeating-conic-gradient(#0000 0deg, #000 1deg 20deg, #0000 21deg 36deg),
|
|
|
- radial-gradient(
|
|
|
- farthest-side,
|
|
|
- #0000 calc(100% - var(--b) - 1px),
|
|
|
- #000 calc(100% - var(--b))
|
|
|
- );
|
|
|
- -webkit-mask-composite: destination-in;
|
|
|
- mask-composite: intersect;
|
|
|
- animation: l4 1s infinite steps(10);
|
|
|
-}
|
|
|
-@keyframes l4 {
|
|
|
- to {
|
|
|
- transform: rotate(1turn);
|
|
|
+ @media (min-width: 1280px) {
|
|
|
+ .health-unit-grid {
|
|
|
+ grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
</style>
|