| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774 |
- <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>
- </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>
- </div>
- </div>
- </template>
- <script>
- import { getSysOrganizationAuthTreeByRoleId } from "@/api/ledger.js";
- import selecttree from "../../components/selecttree.vue";
- import axios from "axios";
- export default {
- components: {
- selecttree,
- },
- data() {
- return {
- parentOpt: [],
- companyCode: "",
- ListData: [],
- loading: false, // ✅ 加载状态.
- // demo演示
- timevalue: new Date(2024, 6), // 7 月是 6,因为月份从 0 开始
- };
- },
- 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();
- },
- 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
- });
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .searchbox {
- display: flex;
- margin: 0 0 20px 0;
- p {
- margin-right: 10px;
- }
- .el-select {
- width: 180px;
- }
- }
- .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(--header-bg);
- .title {
- padding: 5px 0;
- width: 100%;
- border-bottom: 1px solid var(--header-bg);
- text-align: left;
- display: flex;
- align-items: center; // 垂直居中
- // justify-content: center; // 水平居中
- span {
- display: inline-block;
- margin-right: 6px;
- svg {
- display: block;
- }
- }
- h4 {
- width: 175px;
- color: var(--header-bg);
- font-size: 1.1em;
- font-weight: 600;
- margin: 0;
- }
- .mill {
- font-size: 12px;
- color: rgb(43, 42, 42);
- }
- }
- .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;
- }
- }
- }
- li span {
- &:last-child {
- color: var(--header-bg);
- }
- }
- }
- }
- .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;
- }
- }
- }
- }
- .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;
- .legend-head {
- padding: 12px 14px 0;
- }
- .legend-title {
- font-size: 14px;
- font-weight: 800;
- color: #303133;
- }
- .legend-body {
- 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;
- 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 (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;
- }
- }
- }
- // 动画
- /* ✅ 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);
- }
- }
- </style>
|