gear.vue 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  1. <template>
  2. <div class="gear-container">
  3. <div class="gear-header">
  4. <div class="header-title">
  5. <p class="title">齿轮诊断状态总览</p>
  6. <p class="sub-title">共 {{ statusSummary.total }} 个监测点</p>
  7. </div>
  8. <div class="header-stat">
  9. <div
  10. v-for="item in statusCards"
  11. :key="item.key"
  12. class="stat-item"
  13. :class="`is-${item.key}`"
  14. >
  15. <el-divider direction="vertical" class="header-divider"></el-divider>
  16. <div class="stat-item-content">
  17. <div class="icon-wrap">
  18. <i :class="item.icon"></i>
  19. </div>
  20. <div class="stat-content">
  21. <p class="label">{{ item.label }}</p>
  22. <p class="count">{{ item.count }}</p>
  23. <p class="ratio">占比 {{ item.ratio }}</p>
  24. </div>
  25. </div>
  26. <div class="stat-nobox"></div>
  27. </div>
  28. </div>
  29. </div>
  30. <div class="gear">
  31. <div class="TopBox">
  32. <div class="rightdiv">
  33. <div class="monitor-panel">
  34. <h4>齿轮监测点列表</h4>
  35. <div v-if="!tableData.length" class="monitor-empty">
  36. <el-empty description="暂无监测点数据"></el-empty>
  37. </div>
  38. <div v-else class="monitor-list">
  39. <div
  40. v-for="(row, index) in tableData"
  41. :key="row.id || `${row.functionTypeName}-${index}`"
  42. class="monitor-item"
  43. :class="{
  44. 'is-active':
  45. selectedFunctionTypeName &&
  46. selectedFunctionTypeName === rowFunctionTypeName(row),
  47. }"
  48. @click="selectMonitorPoint(row)"
  49. >
  50. <div
  51. class="index-badge"
  52. :class="`is-${statusTagType(row.status)}`"
  53. >
  54. {{ bearingTableIndex(index) }}
  55. </div>
  56. <div class="item-main">
  57. <p class="item-name">
  58. {{ monitorDisplayName(row) }}
  59. </p>
  60. <p class="item-line">监测类型:{{ monitorTypeText(row) }}</p>
  61. <!-- <p class="item-line">型号:{{ row.brandType }}</p>
  62. <p class="item-line">品牌:{{ row.brand }}</p> -->
  63. </div>
  64. <div class="item-right">
  65. <span
  66. class="status-pill"
  67. :class="`is-${statusTagType(row.status)}`"
  68. >
  69. <i class="status-dot"></i>
  70. {{ formatStatusText(row.status) }}
  71. </span>
  72. <!-- <el-button
  73. type="text"
  74. class="detail-btn"
  75. @click.stop="handleDetail(row)"
  76. >
  77. <i class="el-icon-arrow-right"></i>
  78. </el-button> -->
  79. </div>
  80. </div>
  81. </div>
  82. </div>
  83. <div class="fenye">
  84. <p></p>
  85. <el-pagination
  86. @current-change="handleCurrentChange"
  87. :current-page="currentPage"
  88. layout="total, prev, pager, next"
  89. :total="totalCount"
  90. :page-size="pageSize"
  91. small
  92. ></el-pagination>
  93. </div>
  94. </div>
  95. </div>
  96. <div class="bottomBox">
  97. <div class="BtLeft">
  98. <h4>齿轮状态趋势图:</h4>
  99. <p class="status-legend">
  100. <span class="legend-label">状态码说明:</span>
  101. <span class="legend-item is-normal"
  102. ><i class="legend-dot"></i>0 正常</span
  103. >
  104. <span class="legend-item is-warning"
  105. ><i class="legend-dot"></i>1 报警</span
  106. >
  107. <span class="legend-item is-danger"
  108. ><i class="legend-dot"></i>2 危险</span
  109. >
  110. </p>
  111. <el-empty
  112. v-if="!filteredTrendChartList.length"
  113. description="暂无数据"
  114. style="padding: 46px 0"
  115. ></el-empty>
  116. <div v-else class="mpoint-charts">
  117. <el-carousel :key="trendCarouselKey">
  118. <el-carousel-item
  119. v-for="(chart, index) in filteredTrendChartList"
  120. :key="`${chart.title}-${index}`"
  121. >
  122. <div class="mpoint-chart-item">
  123. <!-- <p class="mpoint-chart-title">{{ chart.title }}</p> -->
  124. <Eecharts
  125. style="height: 260px"
  126. :xData="chart.xData"
  127. :yData="chart.yData"
  128. :yNames="chart.yNames"
  129. yAxisName="故障状态"
  130. :jumpContext="chart.jumpContext"
  131. ></Eecharts>
  132. </div>
  133. </el-carousel-item>
  134. </el-carousel>
  135. </div>
  136. </div>
  137. </div>
  138. </div>
  139. </div>
  140. </template>
  141. <script>
  142. import axios from "axios";
  143. import Eecharts from "./mpointEecharts.vue";
  144. import bingTwo from "./bingTwo.vue";
  145. export default {
  146. components: { Eecharts, bingTwo },
  147. props: {
  148. codedata: {
  149. type: Array,
  150. default: () => [],
  151. },
  152. totalCount: {
  153. type: Number,
  154. default: 0,
  155. },
  156. totalPage: {
  157. type: Number,
  158. default: 0,
  159. },
  160. fieldCode: {
  161. type: String,
  162. default: "",
  163. },
  164. windTurbineNumber: {
  165. type: String,
  166. default: "",
  167. },
  168. echartsdata: {
  169. type: Array,
  170. default: () => [],
  171. },
  172. },
  173. data() {
  174. return {
  175. tableData: [],
  176. // 添加新的数据属性用于绑定输入框的值
  177. transmissionRatio: "",
  178. bearingPitchDiameter: "",
  179. rollingElementDiameter: "",
  180. rollingElementCount: "",
  181. contactAngle: "",
  182. vibrationSpeedAlarmThreshold: "",
  183. vibrationSpeedDangerThreshold: "",
  184. envelopeTotalAlarmThreshold: "",
  185. envelopeTotalDangerThreshold: "",
  186. // 分页
  187. currentPage: 1,
  188. total: 1,
  189. pageSize: 10,
  190. xData: [],
  191. yData: [],
  192. // 颜色判断
  193. bearingStateColors: {
  194. innerRing: "#80808057",
  195. outerRing: "#80808057",
  196. rollingElement: "#80808057",
  197. cage: "#80808057",
  198. },
  199. hasError: false, // 标志是否已经显示过错误提示
  200. result: {},
  201. dialogVisible: false,
  202. loading: false, // 控制加载状态
  203. statistics: {},
  204. trendChartList: [],
  205. selectedFunctionTypeName: "",
  206. selectedMonitorId: "",
  207. };
  208. },
  209. created() {},
  210. watch: {
  211. codedata: {
  212. handler(newVal) {
  213. this.tableData = newVal;
  214. const exists = this.tableData.some(
  215. (row) =>
  216. this.resolveFunctionTypeNameByRow(row) ===
  217. this.selectedFunctionTypeName,
  218. );
  219. if (!exists) {
  220. if (this.tableData.length) {
  221. this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(
  222. this.tableData[0],
  223. );
  224. this.selectedMonitorId = this.tableData[0]?.id
  225. ? String(this.tableData[0].id)
  226. : "";
  227. } else {
  228. this.selectedFunctionTypeName = "";
  229. this.selectedMonitorId = "";
  230. }
  231. }
  232. },
  233. immediate: true, // 组件创建时立刻执行一次
  234. deep: true, // 如果 codedata 是复杂对象,建议加上
  235. },
  236. echartsdata: {
  237. handler(newVal) {
  238. const list = Array.isArray(newVal) ? newVal : [];
  239. this.trendChartList = list
  240. .map((item) => {
  241. const title = item?.pointNameCn || "未命名测点";
  242. const inner = Array.isArray(item?.innerDatas)
  243. ? item.innerDatas
  244. : [];
  245. const xData = inner.map((d) => d?.timeStamp || "");
  246. const seriesY = inner.map((d) => {
  247. const v = d?.status;
  248. return v === null || v === undefined || v === ""
  249. ? null
  250. : Number(v);
  251. });
  252. const pointIds = inner.map((d) => d?.id);
  253. const pointRows = inner.map((d) => {
  254. const pid = d?.id;
  255. return (
  256. this.tableData.find((r) => String(r?.id) === String(pid)) ||
  257. null
  258. );
  259. });
  260. const normalizedFunctionTypeName = this.normalizeFunctionTypeName(
  261. item?.functionTypeName ||
  262. inner.find((d) => d?.functionTypeName)?.functionTypeName ||
  263. pointRows.find((r) => r?.functionTypeName)?.functionTypeName ||
  264. item?.functionTypeNameCn ||
  265. item?.pointNameCn ||
  266. item?.mesurePointName ||
  267. "",
  268. );
  269. return {
  270. title,
  271. xData,
  272. yData: [seriesY],
  273. yNames: [title],
  274. functionTypeName: normalizedFunctionTypeName,
  275. jumpContext: {
  276. companyCode: this.fieldCode,
  277. windCode: this.fieldCode,
  278. windTurbineNumber: this.windTurbineNumber,
  279. itemKey: item?.itemKey ?? item?.item_key,
  280. functionTypeName: normalizedFunctionTypeName,
  281. id: item?.id,
  282. mesurePointName: item?.mesurePointName || title,
  283. detectionPointCn: item?.pointNameCn || title,
  284. rotationalSpeed: item?.rotationalSpeed,
  285. samplingFrequency: item?.samplingFrequency,
  286. otherData:
  287. item?.otherData && typeof item.otherData === "object"
  288. ? { ...item.otherData }
  289. : undefined,
  290. pointIds,
  291. pointRows,
  292. },
  293. };
  294. })
  295. .filter((it) => it.xData.length);
  296. if (!this.selectedFunctionTypeName && this.tableData.length) {
  297. this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(
  298. this.tableData[0],
  299. );
  300. this.selectedMonitorId = this.tableData[0]?.id
  301. ? String(this.tableData[0].id)
  302. : "";
  303. }
  304. },
  305. immediate: true,
  306. deep: true,
  307. },
  308. },
  309. computed: {
  310. statusSummary() {
  311. const rows = Array.isArray(this.tableData) ? this.tableData : [];
  312. const total = rows.length;
  313. const summary = { total, normal: 0, warning: 0, danger: 0 };
  314. rows.forEach((row) => {
  315. const code = Number(row?.status);
  316. if (code === 0) summary.normal += 1;
  317. else if (code === 1) summary.warning += 1;
  318. else if (code === 2) summary.danger += 1;
  319. });
  320. return summary;
  321. },
  322. statusCards() {
  323. const { total, normal, warning, danger } = this.statusSummary;
  324. const toRatio = (count) =>
  325. total > 0 ? `${((count / total) * 100).toFixed(0)}%` : "0%";
  326. return [
  327. {
  328. key: "normal",
  329. label: "正常",
  330. count: normal,
  331. ratio: toRatio(normal),
  332. icon: "el-icon-check",
  333. },
  334. {
  335. key: "warning",
  336. label: "报警",
  337. count: warning,
  338. ratio: toRatio(warning),
  339. icon: "el-icon-warning-outline",
  340. },
  341. {
  342. key: "danger",
  343. label: "危险",
  344. count: danger,
  345. ratio: toRatio(danger),
  346. icon: "el-icon-warning",
  347. },
  348. ];
  349. },
  350. filteredTrendChartList() {
  351. const selected = this.selectedFunctionTypeName;
  352. if (!selected) return this.trendChartList;
  353. return this.trendChartList.filter(
  354. (chart) => this.chartFunctionTypeName(chart) === selected,
  355. );
  356. },
  357. trendCarouselKey() {
  358. return `trend-${this.selectedFunctionTypeName || "all"}`;
  359. },
  360. },
  361. methods: {
  362. normalizeFunctionTypeName(name) {
  363. return typeof name === "string" ? name.trim() : "";
  364. },
  365. rowFunctionTypeName(row) {
  366. return this.normalizeFunctionTypeName(
  367. row?.functionTypeName ? String(row.functionTypeName) : "",
  368. );
  369. },
  370. resolveFunctionTypeNameByRow(row) {
  371. const direct = this.rowFunctionTypeName(row);
  372. if (direct) return direct;
  373. const rowId =
  374. row?.id !== undefined && row?.id !== null ? String(row.id) : "";
  375. if (!rowId) return "";
  376. const chart = this.trendChartList.find((item) => {
  377. const rows = item?.jumpContext?.pointRows;
  378. if (!Array.isArray(rows)) return false;
  379. return rows.some(
  380. (r) =>
  381. r?.id !== undefined && r?.id !== null && String(r.id) === rowId,
  382. );
  383. });
  384. return chart ? this.chartFunctionTypeName(chart) : "";
  385. },
  386. monitorDisplayName(row) {
  387. return row?.functionTypeName || row?.detectionPointCn || "-";
  388. },
  389. chartFunctionTypeName(chart) {
  390. return this.normalizeFunctionTypeName(
  391. chart?.jumpContext?.functionTypeName
  392. ? String(chart.jumpContext.functionTypeName)
  393. : "",
  394. );
  395. },
  396. statusTagType(status) {
  397. const code = Number(status);
  398. if (code === 0) return "success";
  399. if (code === 1) return "warning";
  400. if (code === 2) return "danger";
  401. return "info";
  402. },
  403. formatStatusText(status) {
  404. if (status === null || status === undefined || status === "") return "/";
  405. const code = Number(status);
  406. if (code === 0) return "正常";
  407. if (code === 1) return "报警";
  408. if (code === 2) return "危险";
  409. if (code === -1) return "未定义";
  410. return String(status);
  411. },
  412. monitorTypeText(row) {
  413. return "振动-加速度";
  414. },
  415. selectMonitorPoint(row) {
  416. this.selectedMonitorId =
  417. row?.id !== undefined && row?.id !== null ? String(row.id) : "";
  418. this.selectedFunctionTypeName = this.resolveFunctionTypeNameByRow(row);
  419. },
  420. handleDetail(row) {
  421. const itemKey = row.itemKey ?? row.item_key;
  422. const payload = {
  423. /** 与故障诊断页「单位」一致(selecttree 的 companyCode / codeNumber) */
  424. companyCode: this.fieldCode,
  425. windCode: this.fieldCode,
  426. windTurbineNumber: this.windTurbineNumber,
  427. itemKey,
  428. id: row.id,
  429. mesurePointName: row.mesurePointName,
  430. detectionPointCn: row.detectionPointCn,
  431. timeStamp: row.timeStamp,
  432. /** 频谱特征线需要 RPM → Hz;接口字段可能为 rotationalSpeed / otherData.RPM */
  433. rotationalSpeed: row.rotationalSpeed,
  434. samplingFrequency: row.samplingFrequency,
  435. otherData:
  436. row.otherData && typeof row.otherData === "object"
  437. ? { ...row.otherData }
  438. : undefined,
  439. };
  440. try {
  441. sessionStorage.setItem(
  442. "healthVibrationDeepLink",
  443. JSON.stringify(payload),
  444. );
  445. } catch (e) {
  446. console.error(e);
  447. }
  448. this.$router.push({ path: "/home/health/vibration" });
  449. },
  450. bearingTableIndex(index) {
  451. return (this.currentPage - 1) * this.pageSize + index + 1;
  452. },
  453. toggleSelection(rows) {
  454. if (rows) {
  455. rows.forEach((row) => {
  456. this.$refs.multipleTable.toggleRowSelection(row);
  457. });
  458. } else {
  459. this.$refs.multipleTable.clearSelection();
  460. }
  461. },
  462. handleSelectionChange(val) {
  463. this.multipleSelection = val;
  464. },
  465. handleCurrentChange(val) {
  466. console.log(`当前页: ${val}`);
  467. // setTimeout(() => {
  468. // this.automaticDiagnosis();
  469. // }, 500);
  470. this.currentPage = val; // 子组件内部更新当前页
  471. this.$emit("updatePage", this.currentPage); // 通知父组件,把当前页传出去
  472. },
  473. // automaticDiagnosis() {
  474. // if (this.tableData.length === 0) {
  475. // this.$message.warning("当前没有数据,无法进行诊断");
  476. // this.loading = false; // 确保关闭 loading 状态
  477. // return; // 直接返回,不发请求
  478. // }
  479. // this.loading = true;
  480. // },
  481. reset() {
  482. this.tableData = [];
  483. this.xData = [];
  484. this.yData = [];
  485. this.statistics = {};
  486. this.result = {};
  487. this.trendChartList = [];
  488. this.selectedFunctionTypeName = "";
  489. this.selectedMonitorId = "";
  490. this.bearingStateColors = {
  491. innerRing: "#80808057",
  492. outerRing: "#80808057",
  493. rollingElement: "#80808057",
  494. cage: "#80808057",
  495. };
  496. },
  497. },
  498. };
  499. </script>
  500. <style lang="scss" scoped>
  501. .gear-container {
  502. height: 100%;
  503. display: flex;
  504. flex-direction: column;
  505. gap: 12px;
  506. margin-top: -10px;
  507. }
  508. .gear-header {
  509. display: flex;
  510. align-items: stretch;
  511. // border: 1px solid #ebeef5;
  512. border-radius: 12px;
  513. background: #f5f7fa5e;
  514. overflow: hidden;
  515. box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
  516. .header-title {
  517. width: 230px;
  518. padding: 16px 18px;
  519. // border-right: 1px solid #ebeef5;
  520. display: flex;
  521. flex-direction: column;
  522. justify-content: center;
  523. .title {
  524. margin: 0;
  525. font-size: 18px;
  526. font-weight: 700;
  527. color: #303133;
  528. }
  529. .sub-title {
  530. margin: 6px 0 0;
  531. font-size: 13px;
  532. color: #909399;
  533. }
  534. }
  535. .header-stat {
  536. flex: 1;
  537. display: grid;
  538. grid-template-columns: repeat(3, minmax(0, 1fr));
  539. ::v-deep .header-divider.el-divider--vertical {
  540. height: 48px; /* 分割线长度 */
  541. margin: 0 8px 0 0; /* 与图标间距 */
  542. width: 1px; /* 粗细 */
  543. background-color: #dcdfe65e; /* 颜色 */
  544. }
  545. .stat-item {
  546. padding: 10px 18px;
  547. display: flex;
  548. align-items: center;
  549. justify-content: space-between;
  550. gap: 10px;
  551. // border-right: 1px solid #ebeef5;
  552. &:last-child {
  553. border-right: 0;
  554. }
  555. .icon-wrap {
  556. width: 42px;
  557. height: 42px;
  558. border-radius: 50%;
  559. color: #fff;
  560. display: flex;
  561. align-items: center;
  562. justify-content: center;
  563. font-size: 22px;
  564. }
  565. .stat-item-content {
  566. display: flex;
  567. align-items: center;
  568. gap: 10px;
  569. }
  570. .stat-content {
  571. .label,
  572. .count,
  573. .ratio {
  574. margin: 0;
  575. line-height: 1.2;
  576. }
  577. .label {
  578. font-size: 14px;
  579. color: #303133;
  580. font-weight: 500;
  581. }
  582. .count {
  583. margin-top: 2px;
  584. font-size: 30px;
  585. font-weight: 700;
  586. color: #303133;
  587. }
  588. .ratio {
  589. margin-top: 2px;
  590. font-size: 12px;
  591. color: #909399;
  592. }
  593. }
  594. &.is-normal .icon-wrap {
  595. background: #008080;
  596. }
  597. &.is-warning .icon-wrap {
  598. background: #e6a23c;
  599. }
  600. &.is-danger .icon-wrap {
  601. background: #f56c6c;
  602. }
  603. }
  604. }
  605. }
  606. .gear {
  607. height: 100%;
  608. display: flex;
  609. justify-content: space-between;
  610. // flex-direction: column;
  611. gap: 10px;
  612. .TopBox {
  613. width: 40%;
  614. }
  615. .bottomBox {
  616. width: 59%;
  617. height: 360px;
  618. }
  619. }
  620. h4 {
  621. margin-bottom: 5px;
  622. font-weight: 600;
  623. }
  624. .TopBox {
  625. // height: 280px;
  626. display: flex;
  627. justify-content: space-around;
  628. .leftdiv {
  629. width: 50%;
  630. display: flex;
  631. justify-content: space-around;
  632. .stateBox {
  633. .state {
  634. display: flex;
  635. flex-direction: column;
  636. justify-content: center;
  637. /* 整体居中 */
  638. align-items: center;
  639. height: 200px;
  640. gap: 20px;
  641. /* 控制间距 */
  642. p {
  643. width: 150px;
  644. height: 40px;
  645. background: rgb(227, 227, 227);
  646. color: rgb(50, 50, 50);
  647. text-align: center;
  648. align-content: center;
  649. font-weight: 600;
  650. }
  651. }
  652. .btn-auto {
  653. margin-top: 10px;
  654. width: 100%;
  655. }
  656. }
  657. .Btn {
  658. width: 50%;
  659. display: flex;
  660. flex-direction: column;
  661. /* 垂直排列 */
  662. justify-content: space-between;
  663. /* 顶部和底部对齐 */
  664. min-height: 100px;
  665. /* 确保容器有足够高度 */
  666. margin: 10px 0;
  667. .PText {
  668. display: flex;
  669. justify-content: space-around;
  670. span {
  671. display: flex;
  672. align-items: center;
  673. i {
  674. width: 30px;
  675. height: 15px;
  676. margin-right: 5px;
  677. }
  678. .color1 {
  679. background-color: #8ae359;
  680. }
  681. .color2 {
  682. background-color: #eecb5f;
  683. }
  684. .color3 {
  685. background-color: #f7715f;
  686. }
  687. }
  688. }
  689. margin: 10px 0;
  690. .minp {
  691. font-size: 12px;
  692. margin-top: auto;
  693. /* 自动推到最底部 */
  694. }
  695. }
  696. }
  697. .rightdiv {
  698. width: 100%;
  699. .monitor-panel {
  700. // border: 1px solid #e4e7ed;
  701. border-radius: 14px;
  702. // background: #f5f7fa;
  703. padding: 14px;
  704. // min-height: 360px;
  705. height: 360px;
  706. overflow-y: auto;
  707. box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
  708. }
  709. .panel-title {
  710. margin: 0 0 12px;
  711. font-size: 18px;
  712. font-weight: 700;
  713. color: #1f2d3d;
  714. }
  715. .monitor-empty {
  716. display: flex;
  717. align-items: center;
  718. justify-content: center;
  719. min-height: 260px;
  720. background: #fff;
  721. border-radius: 10px;
  722. }
  723. .monitor-list {
  724. display: flex;
  725. flex-direction: column;
  726. gap: 10px;
  727. }
  728. .monitor-item {
  729. position: relative;
  730. display: flex;
  731. align-items: center;
  732. gap: 14px;
  733. border: 1px solid #e4e7ed;
  734. border-radius: 10px;
  735. background: #fff;
  736. padding: 14px 16px;
  737. cursor: pointer;
  738. transition: all 0.2s ease;
  739. &.is-active {
  740. background: #f0f5f7;
  741. }
  742. &.is-active::before {
  743. content: "";
  744. position: absolute;
  745. left: 0;
  746. top: 10px;
  747. bottom: 10px;
  748. width: 4px;
  749. background: #74727224;
  750. box-shadow: 0 0 0 2px #24a16600 inset;
  751. border-radius: 0 4px 4px 0;
  752. }
  753. }
  754. .index-badge {
  755. width: 30px;
  756. height: 30px;
  757. border-radius: 50%;
  758. color: #fff;
  759. font-size: 16px;
  760. font-weight: 700;
  761. display: flex;
  762. align-items: center;
  763. justify-content: center;
  764. flex-shrink: 0;
  765. &.is-success {
  766. background: #008080;
  767. }
  768. &.is-warning {
  769. background: #e6a23c;
  770. }
  771. &.is-danger {
  772. background: #f56c6c;
  773. }
  774. &.is-info {
  775. background: #909399;
  776. }
  777. }
  778. .item-main {
  779. flex: 1;
  780. min-width: 0;
  781. .item-name {
  782. margin: 0 0 8px;
  783. font-size: 16px;
  784. font-weight: 700;
  785. color: #1f2d3d;
  786. }
  787. .item-line {
  788. margin: 0;
  789. font-size: 14px;
  790. line-height: 1.5;
  791. color: #6b7280;
  792. }
  793. }
  794. .item-right {
  795. display: flex;
  796. align-items: center;
  797. gap: 12px;
  798. .detail-btn {
  799. margin-left: 2px;
  800. font-size: 22px;
  801. color: #909399;
  802. }
  803. }
  804. .status-pill {
  805. display: inline-flex;
  806. align-items: center;
  807. gap: 6px;
  808. border-radius: 12px;
  809. padding: 10px 16px;
  810. font-size: 16px;
  811. font-weight: 700;
  812. line-height: 1;
  813. .status-dot {
  814. width: 10px;
  815. height: 10px;
  816. border-radius: 50%;
  817. background: currentColor;
  818. }
  819. &.is-success {
  820. color: #21a366;
  821. background: #e7f6ee;
  822. }
  823. &.is-warning {
  824. color: #d48806;
  825. background: #fff6e5;
  826. }
  827. &.is-danger {
  828. color: #f04438;
  829. background: #ffebee;
  830. }
  831. &.is-info {
  832. color: #606266;
  833. background: #f4f4f5;
  834. }
  835. }
  836. .fenye {
  837. display: flex;
  838. justify-content: flex-end;
  839. margin: 10px 0 0;
  840. font-size: 12px;
  841. line-height: 30px;
  842. span {
  843. font-weight: 600;
  844. }
  845. }
  846. }
  847. }
  848. .bottomBox {
  849. display: flex;
  850. justify-content: space-around;
  851. box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
  852. border-radius: 12px;
  853. .BtLeft {
  854. // border: 1px solid rgb(231, 231, 231);
  855. width: 100%;
  856. padding: 10px 20px;
  857. }
  858. .BtRight {
  859. border: 1px solid rgb(231, 231, 231);
  860. padding: 10px;
  861. .BtRightDiv {
  862. padding: 0 10px;
  863. h4 {
  864. font-size: 14px;
  865. }
  866. .BtRightP {
  867. line-height: 30px;
  868. font-size: 12px;
  869. display: flex;
  870. margin-bottom: 5px;
  871. width: 50%;
  872. span {
  873. margin-left: 10px;
  874. .el-input {
  875. width: 100px;
  876. }
  877. }
  878. .label-text {
  879. width: 140px;
  880. }
  881. }
  882. .canshu {
  883. display: flex;
  884. flex-wrap: wrap;
  885. justify-content: space-between;
  886. }
  887. }
  888. }
  889. .dialog-footer {
  890. margin-top: 20px;
  891. text-align: right;
  892. }
  893. }
  894. .mpoint-charts {
  895. // display: grid;
  896. // grid-template-columns: repeat(2, minmax(0, 1fr));
  897. // gap: 16px;
  898. display: block;
  899. }
  900. .mpoint-chart-item {
  901. border: 1px solid #f0f0f0;
  902. border-radius: 4px;
  903. padding: 8px 10px;
  904. }
  905. .mpoint-chart-title {
  906. margin: 0 0 6px;
  907. font-size: 13px;
  908. color: #303133;
  909. }
  910. .status-legend {
  911. margin: 4px 0 8px;
  912. font-size: 12px;
  913. color: #909399;
  914. display: flex;
  915. align-items: center;
  916. gap: 12px;
  917. .legend-label {
  918. color: #606266;
  919. }
  920. .legend-item {
  921. display: inline-flex;
  922. align-items: center;
  923. gap: 4px;
  924. .legend-dot {
  925. width: 8px;
  926. height: 8px;
  927. border-radius: 50%;
  928. background: currentColor;
  929. }
  930. &.is-normal {
  931. color: #008080;
  932. }
  933. &.is-warning {
  934. color: #e6a23c;
  935. }
  936. &.is-danger {
  937. color: #f56c6c;
  938. }
  939. }
  940. }
  941. /* el-carousel 指示器颜色(Element UI 2.x) */
  942. ::v-deep .el-carousel__indicator .el-carousel__button {
  943. background-color: rgba(144, 147, 153, 0.45); /* 默认灰 */
  944. opacity: 1; /* Element UI 默认会用 opacity 控制,这里统一用背景色 */
  945. }
  946. ::v-deep .el-carousel__indicator.is-active .el-carousel__button {
  947. background-color: #24a166; /* 激活:主色蓝(可改) */
  948. }
  949. ::v-deep .el-carousel__indicator:hover .el-carousel__button {
  950. background-color: #24a166aa; /* hover */
  951. }
  952. ::v-deep .el-carousel__indicators--horizontal {
  953. bottom: -10px !important;
  954. }
  955. @media (max-width: 1200px) {
  956. .mpoint-charts {
  957. grid-template-columns: 1fr;
  958. }
  959. }
  960. ::v-deep .el-table__cell {
  961. padding: 2px 0;
  962. font-size: 12px;
  963. }
  964. </style>