EngineeringSafetyView.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <template>
  2. <div class="safety-container">
  3. <div class="tab-bar">
  4. <div class="tab-item" :class="{ active: currentTab === 'dam' }" @click="currentTab = 'dam'">大坝整体</div>
  5. <div class="tab-item" :class="{ active: currentTab === 'spillway' }" @click="currentTab = 'spillway'">泄洪设施</div>
  6. <div class="tab-item" :class="{ active: currentTab === 'conveyance' }" @click="currentTab = 'conveyance'">输水设施</div>
  7. <div class="tab-item" :class="{ active: currentTab === 'bank' }" @click="currentTab = 'bank'">库岸稳定</div>
  8. </div>
  9. <div class="safety-scroll-area">
  10. <div v-if="currentTab === 'dam'" class="tab-content">
  11. <div class="left-sidebar">
  12. <div class="data-card">
  13. <div class="card-header">
  14. <h3 class="card-title">大坝变形监测</h3>
  15. <div class="header-actions"><span class="update-time">单位:mm</span></div>
  16. </div>
  17. <div class="card-body chart-body">
  18. <div class="monitor-chart-box">
  19. <div class="monitor-chart-label">变形趋势</div>
  20. <canvas id="deformationChart" width="320" height="170"></canvas>
  21. <div class="chart-legend">
  22. <span class="legend-dot dot-horizontal"></span>水平位移
  23. <span class="legend-dot dot-vertical" style="margin-left:12px;"></span>垂直位移
  24. </div>
  25. </div>
  26. <div class="water-stats-row" style="margin-top:4px;">
  27. <div class="water-stat-card">
  28. <div class="water-stat-label">最大水平位移</div>
  29. <div class="water-stat-value">12.5</div>
  30. </div>
  31. <div class="water-stat-card">
  32. <div class="water-stat-label">最大垂直位移</div>
  33. <div class="water-stat-value">8.3</div>
  34. </div>
  35. </div>
  36. </div>
  37. </div>
  38. <div class="data-card mt-10">
  39. <div class="card-header">
  40. <h3 class="card-title">渗流监测</h3>
  41. <div class="header-actions"><span class="update-time">单位:L/s</span></div>
  42. </div>
  43. <div class="card-body chart-body">
  44. <div class="water-stats-row">
  45. <div class="water-stat-card">
  46. <div class="water-stat-label">渗流量</div>
  47. <div class="water-stat-value">3.5</div>
  48. </div>
  49. <div class="water-stat-card">
  50. <div class="water-stat-label">坝基扬压力</div>
  51. <div class="water-stat-value">18.2</div>
  52. </div>
  53. </div>
  54. <div class="monitor-chart-box" style="margin-top:4px;">
  55. <div class="monitor-chart-label">渗流趋势</div>
  56. <canvas id="seepageChart" width="320" height="170"></canvas>
  57. <div class="chart-legend">
  58. <span class="legend-dot dot-seepage"></span>渗流量
  59. <span class="legend-dot dot-pressure" style="margin-left:12px;"></span>渗流压力(kPa)
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. <div class="right-sidebar">
  66. <div class="data-card">
  67. <div class="card-header">
  68. <h3 class="card-title">安全状态评估</h3>
  69. </div>
  70. <div class="card-body chart-body">
  71. <div class="safety-assessment-row">
  72. <div class="safety-pie-section">
  73. <canvas id="statusPieChart" width="140" height="140"></canvas>
  74. <div class="pie-legend">
  75. <div class="pie-legend-item" v-for="item in pieData" :key="item.name">
  76. <span class="pie-dot" :style="{ background: item.color }"></span>
  77. <span class="pie-name">{{ item.name }}</span>
  78. <span class="pie-count">{{ item.count }}个</span>
  79. </div>
  80. </div>
  81. </div>
  82. <div class="safety-assessment-section">
  83. <div class="safety-assessment-bar">
  84. <div class="safety-assessment-grade">一类坝</div>
  85. <div class="safety-assessment-texts">
  86. <div class="safety-assessment-text">评估时间:2026-05-15</div>
  87. <div class="safety-assessment-text">运行正常,安全可靠</div>
  88. </div>
  89. </div>
  90. <div class="safety-assessment-tags">
  91. <div class="assess-tag good" v-for="item in assessmentIndicators" :key="item">{{ item }}</div>
  92. </div>
  93. </div>
  94. </div>
  95. </div>
  96. </div>
  97. <div class="data-card mt-10">
  98. <div class="card-header">
  99. <h3 class="card-title">监测点状态</h3>
  100. <div class="header-actions"><span class="update-time">共 {{ monitorList.length }} 个测点</span></div>
  101. </div>
  102. <div class="card-body card-body-tight">
  103. <div class="monitor-table">
  104. <div class="monitor-table-header">
  105. <span class="col-name">测点名称</span>
  106. <span class="col-value">当前值</span>
  107. <span class="col-status">状态</span>
  108. <span class="col-trend">趋势</span>
  109. </div>
  110. <div class="monitor-table-body">
  111. <div class="monitor-row" v-for="item in monitorList" :key="item.name">
  112. <span class="col-name">{{ item.name }}</span>
  113. <span class="col-value">{{ item.value }}{{ item.unit }}</span>
  114. <span class="col-status"><span class="status-tag" :class="item.status">{{ item.statusText }}</span></span>
  115. <span class="col-trend"><span class="trend-icon" :class="item.trend">{{ item.trend === 'up' ? '↑' : item.trend === 'down' ? '↓' : '→' }}</span></span>
  116. </div>
  117. </div>
  118. </div>
  119. </div>
  120. </div>
  121. </div>
  122. </div>
  123. <div v-if="currentTab === 'spillway'" class="tab-content tab-placeholder"><div class="placeholder-text">泄洪设施监测数据</div></div>
  124. <div v-if="currentTab === 'conveyance'" class="tab-content tab-placeholder"><div class="placeholder-text">输水设施监测数据</div></div>
  125. <div v-if="currentTab === 'bank'" class="tab-content tab-placeholder"><div class="placeholder-text">库岸稳定监测数据</div></div>
  126. </div>
  127. </div>
  128. </template>
  129. <script>
  130. import * as echarts from "echarts";
  131. export default {
  132. name: "EngineeringSafetyView",
  133. data() {
  134. return {
  135. currentTab: "dam",
  136. monitorList: [
  137. { name: "坝顶水平位移-01", value: "12.5", unit: "mm", status: "normal", statusText: "正常", trend: "up" },
  138. { name: "坝顶水平位移-02", value: "11.8", unit: "mm", status: "normal", statusText: "正常", trend: "steady" },
  139. { name: "坝基垂直位移-01", value: "8.3", unit: "mm", status: "normal", statusText: "正常", trend: "down" },
  140. { name: "坝基垂直位移-02", value: "7.9", unit: "mm", status: "normal", statusText: "正常", trend: "steady" },
  141. { name: "渗流压力-P01", value: "25.6", unit: "kPa", status: "normal", statusText: "正常", trend: "up" },
  142. { name: "渗流压力-P02", value: "22.1", unit: "kPa", status: "normal", statusText: "正常", trend: "steady" },
  143. { name: "绕坝渗流-S01", value: "1.2", unit: "L/s", status: "normal", statusText: "正常", trend: "steady" },
  144. { name: "裂缝开合度-C01", value: "1.2", unit: "mm", status: "warning", statusText: "注意", trend: "up" },
  145. { name: "钢筋应力-R01", value: "45.2", unit: "MPa", status: "normal", statusText: "正常", trend: "steady" },
  146. { name: "库岸变形-B01", value: "3.2", unit: "mm", status: "normal", statusText: "正常", trend: "down" },
  147. ],
  148. pieData: [
  149. { name: "正常", count: 8, color: "#22c55e" },
  150. { name: "注意", count: 2, color: "#ffd93d" },
  151. { name: "警戒", count: 0, color: "#f97316" },
  152. { name: "危险", count: 0, color: "#ef4444" },
  153. ],
  154. assessmentIndicators: ["变形指标 合格", "渗流指标 合格", "结构指标 合格", "环境指标 合格"],
  155. deformationChart: null,
  156. seepageChart: null,
  157. statusPieChart: null,
  158. };
  159. },
  160. mounted() { this.$nextTick(() => this.initCharts()); },
  161. beforeUnmount() {
  162. if (this.deformationChart) this.deformationChart.dispose();
  163. if (this.seepageChart) this.seepageChart.dispose();
  164. if (this.statusPieChart) this.statusPieChart.dispose();
  165. },
  166. methods: {
  167. initCharts() {
  168. if (!echarts) return;
  169. const dc = document.getElementById("deformationChart");
  170. if (dc) {
  171. if (this.deformationChart) this.deformationChart.dispose();
  172. this.deformationChart = echarts.init(dc);
  173. this.deformationChart.setOption({ animation: false, tooltip: { trigger: "axis" }, grid: { left: "8%", right: "5%", top: "8%", bottom: "12%" }, xAxis: { type: "category", data: ["1月","2月","3月","4月","5月","6月"], axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 } }, yAxis: { type: "value", name: "mm", nameTextStyle: { color: "#7bbef6", fontSize: 9 }, axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 }, splitLine: { lineStyle: { color: "rgba(123,190,246,0.15)" } } }, series: [{ name: "水平位移", type: "line", data: [8.2,9.5,10.8,11.2,12.0,12.5], smooth: true, symbol: "circle", symbolSize: 4, lineStyle: { color: "#62f6fb", width: 2 }, itemStyle: { color: "#62f6fb" }, areaStyle: { color: "rgba(98,246,251,0.1)" } }, { name: "垂直位移", type: "line", data: [5.1,6.2,7.0,7.5,8.0,8.3], smooth: true, symbol: "diamond", symbolSize: 4, lineStyle: { color: "#fbbf24", width: 2 }, itemStyle: { color: "#fbbf24" }, areaStyle: { color: "rgba(251,191,36,0.08)" } }] });
  174. }
  175. const sc = document.getElementById("seepageChart");
  176. if (sc) {
  177. if (this.seepageChart) this.seepageChart.dispose();
  178. this.seepageChart = echarts.init(sc);
  179. this.seepageChart.setOption({ animation: false, tooltip: { trigger: "axis" }, legend: { data: ["渗流量","渗流压力"], textStyle: { color: "#7bbef6", fontSize: 9 }, bottom: 0, itemWidth: 10, itemHeight: 8 }, grid: { left: "8%", right: "5%", top: "8%", bottom: "22%" }, xAxis: { type: "category", data: ["1月","2月","3月","4月","5月","6月"], axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 } }, yAxis: { type: "value", nameTextStyle: { color: "#7bbef6", fontSize: 9 }, axisLine: { lineStyle: { color: "#7bbef6" } }, axisLabel: { color: "#7bbef6", fontSize: 9 }, splitLine: { lineStyle: { color: "rgba(123,190,246,0.15)" } } }, series: [{ name: "渗流量", type: "line", data: [4.2,3.8,3.6,3.5,3.5,3.5], smooth: true, symbol: "circle", symbolSize: 4, lineStyle: { color: "#62f6fb", width: 2 }, itemStyle: { color: "#62f6fb" }, areaStyle: { color: "rgba(98,246,251,0.12)" } }, { name: "渗流压力", type: "line", data: [28.5,26.8,25.2,24.8,25.0,25.6], smooth: true, symbol: "diamond", symbolSize: 4, lineStyle: { color: "#a78bfa", width: 2 }, itemStyle: { color: "#a78bfa" }, areaStyle: { color: "rgba(167,139,250,0.08)" } }] });
  180. }
  181. const pc = document.getElementById("statusPieChart");
  182. if (pc) {
  183. if (this.statusPieChart) this.statusPieChart.dispose();
  184. this.statusPieChart = echarts.init(pc);
  185. this.statusPieChart.setOption({ animation: false, tooltip: { trigger: "item", formatter: "{b}: {c}个 ({d}%)" }, series: [{ type: "pie", radius: ["45%","75%"], center: ["50%","50%"], avoidLabelOverlap: false, itemStyle: { borderColor: "rgba(0,30,60,0.6)", borderWidth: 2 }, label: { show: false }, labelLine: { show: false }, data: [{ value: 8, name: "正常", itemStyle: { color: "#22c55e" } }, { value: 2, name: "注意", itemStyle: { color: "#ffd93d" } }, { value: 0, name: "警戒", itemStyle: { color: "#f97316" } }, { value: 0, name: "危险", itemStyle: { color: "#ef4444" } }] }] });
  186. }
  187. },
  188. },
  189. };
  190. </script>
  191. <style scoped>
  192. .safety-container { width: 100%; height: 100%; display: flex; flex-direction: column; position: relative; overflow: hidden; }
  193. .tab-bar { position: absolute; left: 20px; top: 80px; z-index: 10; display: flex; gap: 4px; }
  194. .tab-item { padding: 6px 24px; font-size: 14px; font-weight: bold; color: #7bbef6; background: rgba(0,20,40,0.6); border: 1px solid rgba(0,212,255,0.25); border-radius: 3px 3px 0 0; cursor: pointer; transition: all 0.2s; user-select: none; }
  195. .tab-item:hover { background: rgba(0,40,80,0.7); color: #62f6fb; }
  196. .tab-item.active { background: rgba(0,20,40,0.85); color: #e0fcff; border-color: rgba(0,212,255,0.5); border-bottom-color: transparent; text-shadow: 0 0 5px rgba(0,212,255,0.3); }
  197. .safety-scroll-area { position: absolute; left: 0; right: 0; top: 110px; bottom: 0; overflow-y: auto; overflow-x: hidden; z-index: 5; padding-bottom: 40px; }
  198. .safety-scroll-area::-webkit-scrollbar { width: 4px; }
  199. .safety-scroll-area::-webkit-scrollbar-track { background: rgba(0,20,40,0.5); }
  200. .safety-scroll-area::-webkit-scrollbar-thumb { background: rgba(0,212,255,0.3); border-radius: 2px; }
  201. .tab-content { width: 100%; display: flex; justify-content: space-between; padding: 0 20px; box-sizing: border-box; }
  202. .left-sidebar { width: 350px; flex-shrink: 0; }
  203. .right-sidebar { width: 350px; flex-shrink: 0; }
  204. .tab-placeholder { justify-content: center; align-items: center; min-height: 300px; }
  205. .placeholder-text { color: #7bbef6; font-size: 18px; opacity: 0.6; padding: 80px 0; }
  206. .mt-10 { margin-top: 10px; }
  207. .data-card { width: 100%; background: rgba(0,20,40,0.7); border-radius: 4px; overflow: hidden; box-shadow: 0 0 10px rgba(0,212,255,0.2); }
  208. .card-header { height: 42px; background-image: url("/src/assets/images/数据小标题.png"); background-size: 100% 100%; background-position: center; background-repeat: no-repeat; display: flex; align-items: flex-start; justify-content: space-between; padding: 4px 16px 0; }
  209. .card-title { font-size: var(--fs-card-title); font-weight: bold; color: #e0fcff; margin: 0; text-shadow: 0 0 5px rgba(0,212,255,0.5); padding-left: 20px; }
  210. .header-actions { display: flex; align-items: flex-start; gap: 10px; }
  211. .update-time { color: #7bbef6; font-size: 11px; font-family: monospace; }
  212. .card-body { padding: 8px; min-height: auto; font-size: 13px; line-height: 1.5; margin-top: -6px; }
  213. .card-body.chart-body { padding: 6px 8px 8px; }
  214. .card-body.card-body-tight { padding: 6px 8px; }
  215. .monitor-chart-box { background: rgba(0,20,40,0.4); border-radius: 4px; border: 1px solid rgba(0,212,255,0.15); padding: 6px; }
  216. .monitor-chart-label { color: #62f6fb; font-size: var(--fs-label); font-weight: bold; margin-bottom: 2px; }
  217. .chart-legend { text-align: center; margin-top: 3px; color: #7bbef6; font-size: 10px; }
  218. .legend-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 3px; vertical-align: middle; }
  219. .dot-horizontal { background: #62f6fb; } .dot-vertical { background: #fbbf24; } .dot-seepage { background: #62f6fb; } .dot-pressure { background: #a78bfa; }
  220. .water-stats-row { display: flex; gap: 6px; }
  221. .water-stat-card { flex: 1; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 3px 10px; background-image: url("/src/assets/images/卡片背景2.png"); background-size: 100% 100%; background-position: center; background-repeat: no-repeat; height: 42px; }
  222. .water-stat-label { color: #62f6fb; font-size: var(--fs-label); margin-bottom: 0; text-align: left; line-height: 1.2; white-space: nowrap; }
  223. .water-stat-value { color: #ffffff; font-size: var(--fs-value-xl); font-weight: bold; text-shadow: 0 0 6px rgba(0,212,255,0.5); line-height: 1.2; }
  224. .safety-assessment-row { display: flex; gap: 8px; align-items: stretch; }
  225. .safety-pie-section { flex-shrink: 0; width: 45%; text-align: center; }
  226. .pie-legend { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 3px; }
  227. .pie-legend-item { display: flex; align-items: center; gap: 3px; flex: 1 1 45%; min-width: 70px; }
  228. .pie-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
  229. .pie-name { color: #7bbef6; font-size: 10px; }
  230. .pie-count { color: #e0fcff; font-size: 11px; font-weight: bold; margin-left: auto; }
  231. .safety-assessment-section { flex: 1; display: flex; flex-direction: column; gap: 4px; }
  232. .safety-assessment-bar { background: rgba(0,20,40,0.5); border-radius: 4px; border: 1px solid rgba(0,212,255,0.15); padding: 8px 10px; display: flex; flex-direction: column; align-items: center; gap: 4px; }
  233. .safety-assessment-grade { font-size: 20px; font-weight: bold; padding: 2px 16px; border-radius: 4px; background: rgba(34,197,94,0.25); color: #22c55e; border: 1px solid rgba(34,197,94,0.5); text-align: center; }
  234. .safety-assessment-texts { display: flex; flex-direction: column; align-items: center; gap: 1px; }
  235. .safety-assessment-text { color: #7bbef6; font-size: 11px; }
  236. .safety-assessment-tags { display: flex; flex-wrap: wrap; gap: 3px; justify-content: center; }
  237. .assess-tag { font-size: 10px; font-weight: bold; padding: 2px 8px; border-radius: 2px; }
  238. .assess-tag.good { background: rgba(34,197,94,0.15); color: #22c55e; border: 1px solid rgba(34,197,94,0.3); }
  239. .monitor-table { width: 100%; font-size: 11px; }
  240. .monitor-table-header { display: flex; padding: 4px 6px; background: rgba(0,212,255,0.1); border-bottom: 1px solid rgba(0,212,255,0.2); color: #7bbef6; font-weight: bold; }
  241. .monitor-table-body { max-height: 260px; overflow-y: auto; }
  242. .monitor-table-body::-webkit-scrollbar { width: 3px; }
  243. .monitor-table-body::-webkit-scrollbar-track { background: rgba(0,20,40,0.5); }
  244. .monitor-table-body::-webkit-scrollbar-thumb { background: rgba(0,212,255,0.3); border-radius: 2px; }
  245. .monitor-row { display: flex; padding: 4px 6px; border-bottom: 1px solid rgba(0,212,255,0.08); align-items: flex-start; color: #e0fcff; }
  246. .monitor-row:last-child { border-bottom: none; }
  247. .monitor-row:hover { background: rgba(0,212,255,0.05); }
  248. .col-name { flex: 2.5; } .col-value { flex: 1.5; text-align: center; } .col-status { flex: 1.2; text-align: center; } .col-trend { flex: 0.8; text-align: center; }
  249. .status-tag { font-size: var(--fs-status); font-weight: bold; padding: 1px 6px; border-radius: 2px; display: inline-block; }
  250. .status-tag.normal { background: rgba(34,197,94,0.2); color: #22c55e; }
  251. .status-tag.warning { background: rgba(255,217,61,0.2); color: #ffd93d; }
  252. .status-tag.danger { background: rgba(239,68,68,0.2); color: #ff6b6b; }
  253. .trend-icon { font-size: 12px; font-weight: bold; }
  254. .trend-icon.up { color: #f97316; } .trend-icon.down { color: #22c55e; } .trend-icon.steady { color: #7bbef6; }
  255. canvas { display: block; margin: 0 auto; }
  256. </style>