gwfxxq.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <!--管网风险详情-->
  2. <template>
  3. <div class="pipeline-risk-analysis">
  4. <!-- 页面头部 -->
  5. <div class="page-header">
  6. <div class="header-left">
  7. <el-icon><DataAnalysis /></el-icon>
  8. <span class="title">燃气管网风险构成要素分析</span>
  9. <div class="stats-badge">
  10. <span class="stat-item"><i class="dot"></i>管网总长 {{ totalLength }}km</span>
  11. <span class="stat-item"><i class="dot"></i>管段数量 {{ totalPipelines }}</span>
  12. <span class="stat-item critical"><i class="dot"></i>高风险 {{ highRiskCount }}</span>
  13. </div>
  14. </div>
  15. <div class="header-right">
  16. <el-input
  17. v-model="searchKeyword"
  18. placeholder="搜索管段/地址"
  19. clearable
  20. :prefix-icon="Search"
  21. style="width: 220px"
  22. />
  23. <el-button type="primary" :icon="Refresh" @click="refreshData">刷新</el-button>
  24. <el-button :icon="Download" @click="exportReport">导出报告</el-button>
  25. </div>
  26. </div>
  27. <!-- 主内容区:左侧列表 + 右侧详情 -->
  28. <div class="main-layout">
  29. <!-- 左侧管网列表 -->
  30. <div class="pipeline-list-panel">
  31. <div class="panel-header">
  32. <span>工艺单元(管网)列表</span>
  33. <el-select v-model="riskFilter" placeholder="风险等级" clearable size="small" style="width: 110px">
  34. <el-option label="高风险" value="high" />
  35. <el-option label="中风险" value="medium" />
  36. <el-option label="低风险" value="low" />
  37. </el-select>
  38. </div>
  39. <div class="pipeline-list">
  40. <div
  41. v-for="pipeline in paginatedPipelines"
  42. :key="pipeline.id"
  43. class="pipeline-item"
  44. :class="{
  45. selected: selectedPipeline?.id === pipeline.id,
  46. 'risk-high': pipeline.riskLevel === 'high',
  47. 'risk-medium': pipeline.riskLevel === 'medium'
  48. }"
  49. @click="selectPipeline(pipeline)"
  50. >
  51. <div class="pipeline-header">
  52. <span class="pipeline-name">{{ pipeline.name }}</span>
  53. <el-tag :type="pipeline.riskLevel === 'high' ? 'danger' : pipeline.riskLevel === 'medium' ? 'warning' : 'success'" size="small">
  54. {{ pipeline.riskLevel === 'high' ? '高风险' : pipeline.riskLevel === 'medium' ? '中风险' : '低风险' }}
  55. </el-tag>
  56. </div>
  57. <div class="pipeline-code">编号: {{ pipeline.code }}</div>
  58. <div class="pipeline-location">
  59. <el-icon><Location /></el-icon> {{ pipeline.area }}
  60. </div>
  61. <div class="risk-score">风险评分: {{ pipeline.riskScore }}分</div>
  62. </div>
  63. </div>
  64. <div class="pagination-area">
  65. <el-pagination small layout="prev, pager, next" :total="filteredPipelines.length" :page-size="pageSize" v-model:current-page="currentPage" />
  66. </div>
  67. </div>
  68. <!-- 右侧风险分析详情 -->
  69. <div class="detail-panel" v-if="selectedPipeline">
  70. <div class="detail-header">
  71. <h3>{{ selectedPipeline.name }}</h3>
  72. <el-tag :type="selectedPipeline.riskLevel === 'high' ? 'danger' : selectedPipeline.riskLevel === 'medium' ? 'warning' : 'success'" size="large">
  73. {{ selectedPipeline.riskLevel === 'high' ? '高风险' : selectedPipeline.riskLevel === 'medium' ? '中风险' : '低风险' }}
  74. </el-tag>
  75. </div>
  76. <!-- 风险成因雷达图 -->
  77. <div class="radar-section">
  78. <div class="section-title">风险构成要素雷达图</div>
  79. <div ref="radarRef" class="radar-chart"></div>
  80. <div class="risk-summary">
  81. 综合风险评分: <strong :class="getScoreClass(selectedPipeline.riskScore)">{{ selectedPipeline.riskScore }}分</strong>
  82. <span class="risk-level">属于{{ selectedPipeline.riskLevel === 'high' ? '高风险' : selectedPipeline.riskLevel === 'medium' ? '中风险' : '低风险' }}管段</span>
  83. </div>
  84. </div>
  85. <!-- 四个信息卡片区 -->
  86. <div class="info-cards">
  87. <!-- 燃气管网基础信息 -->
  88. <div class="info-card">
  89. <div class="card-header">
  90. <el-icon><Monitor /></el-icon>
  91. <span>管网基础信息</span>
  92. </div>
  93. <el-descriptions :column="1" border size="small">
  94. <el-descriptions-item label="管材材质">{{ selectedPipeline.material }}</el-descriptions-item>
  95. <el-descriptions-item label="压力级别">{{ selectedPipeline.pressureLevel }}</el-descriptions-item>
  96. <el-descriptions-item label="管径">{{ selectedPipeline.diameter }}mm</el-descriptions-item>
  97. <el-descriptions-item label="长度">{{ selectedPipeline.length }}km</el-descriptions-item>
  98. <el-descriptions-item label="埋设年代">{{ selectedPipeline.buryYear }}</el-descriptions-item>
  99. <el-descriptions-item label="设计使用年限">{{ selectedPipeline.designLife }}年</el-descriptions-item>
  100. <el-descriptions-item label="权属单位">{{ selectedPipeline.owner }}</el-descriptions-item>
  101. </el-descriptions>
  102. </div>
  103. <!-- 历史维修记录 -->
  104. <div class="info-card">
  105. <div class="card-header">
  106. <el-icon><Document /></el-icon>
  107. <span>历史维修记录</span>
  108. </div>
  109. <div class="maintenance-list">
  110. <div v-for="record in selectedPipeline.maintenanceRecords" :key="record.id" class="maintenance-item">
  111. <div class="maintenance-time">{{ record.time }}</div>
  112. <div class="maintenance-desc">{{ record.description }}</div>
  113. <div class="maintenance-type">
  114. <el-tag :type="record.type === '紧急' ? 'danger' : 'info'" size="small">{{ record.type }}</el-tag>
  115. </div>
  116. </div>
  117. <div v-if="selectedPipeline.maintenanceRecords.length === 0" class="empty-text">暂无维修记录</div>
  118. </div>
  119. </div>
  120. <!-- 周边危险源 -->
  121. <div class="info-card">
  122. <div class="card-header">
  123. <el-icon><Warning /></el-icon>
  124. <span>周边危险源</span>
  125. </div>
  126. <div class="hazard-list">
  127. <div v-for="hazard in selectedPipeline.hazards" :key="hazard.id" class="hazard-item">
  128. <div class="hazard-name">{{ hazard.name }}</div>
  129. <div class="hazard-distance">距离: {{ hazard.distance }}m</div>
  130. <div class="hazard-level">
  131. <el-tag :type="hazard.level === '高' ? 'danger' : hazard.level === '中' ? 'warning' : 'info'" size="small">
  132. 风险等级: {{ hazard.level }}
  133. </el-tag>
  134. </div>
  135. </div>
  136. <div v-if="selectedPipeline.hazards.length === 0" class="empty-text">周边无危险源</div>
  137. </div>
  138. </div>
  139. <!-- 周边防护目标 -->
  140. <div class="info-card">
  141. <div class="card-header">
  142. <el-icon><OfficeBuilding /></el-icon>
  143. <span>周边防护目标</span>
  144. </div>
  145. <div class="target-list">
  146. <div v-for="target in selectedPipeline.protectionTargets" :key="target.id" class="target-item">
  147. <div class="target-name">{{ target.name }}</div>
  148. <div class="target-distance">距离: {{ target.distance }}m</div>
  149. <div class="target-type">{{ target.type }}</div>
  150. </div>
  151. <div v-if="selectedPipeline.protectionTargets.length === 0" class="empty-text">周边无敏感目标</div>
  152. </div>
  153. </div>
  154. </div>
  155. <!-- 风险成因分析 -->
  156. <div class="risk-cause-section">
  157. <div class="section-title">风险成因分析</div>
  158. <div class="cause-list">
  159. <div v-for="(cause, idx) in selectedPipeline.riskCauses" :key="idx" class="cause-item">
  160. <el-icon><CircleClose /></el-icon>
  161. <span>{{ cause }}</span>
  162. </div>
  163. </div>
  164. </div>
  165. </div>
  166. <!-- 未选中状态 -->
  167. <div v-else class="empty-panel">
  168. <el-empty description="请从左侧选择管段查看风险构成要素" :image-size="140" />
  169. </div>
  170. </div>
  171. </div>
  172. </template>
  173. <script setup>
  174. import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue'
  175. import { DataAnalysis, Search, Refresh, Download, Location, Monitor, Document, Warning, OfficeBuilding, CircleClose } from '@element-plus/icons-vue'
  176. import * as echarts from 'echarts'
  177. import { ElMessage } from 'element-plus'
  178. // 沅陵县管网风险构成分析数据(8段)
  179. const pipelines = ref([
  180. {
  181. id: 1, code: 'PL-YL-001', name: '辰州路高压管线', area: '沅陵县太常片区辰州路',
  182. riskLevel: 'high', riskScore: 86.5,
  183. material: '钢管', pressureLevel: '高压 (1.6MPa)', diameter: 500, length: 3.8,
  184. buryYear: '2015年', designLife: '30年', owner: '沅陵燃气有限公司',
  185. maintenanceRecords: [
  186. { id: 1, time: '2025-09-15', description: '防腐层检测发现3处破损,补口修复', type: '紧急' },
  187. { id: 2, time: '2025-03-20', description: '辰州路雨污分流施工段增设管线保护盖板', type: '常规' },
  188. { id: 3, time: '2024-08-10', description: '定期巡检,发现1处标识桩损坏,已更换', type: '常规' }
  189. ],
  190. hazards: [
  191. { id: 1, name: '城南加油站', distance: 85, level: '高' },
  192. { id: 2, name: '辰州路在建施工工地', distance: 50, level: '高' },
  193. { id: 3, name: '太常LNG储配站', distance: 180, level: '中' }
  194. ],
  195. protectionTargets: [
  196. { id: 1, name: '县人民医院太常分院', distance: 120, type: '医院' },
  197. { id: 2, name: '太常中学', distance: 280, type: '学校' },
  198. { id: 3, name: '辰州路商业街', distance: 60, type: '商业区' }
  199. ],
  200. riskCauses: [
  201. '管道服役超过10年,防腐层出现老化剥落现象',
  202. '辰州路主干道交通密集,重型车辆震动荷载大',
  203. '雨污分流施工距管线仅50m,第三方破坏风险高',
  204. '周边有加油站及LNG储配站等重大危险源',
  205. '历史维修记录显示防腐层已多次修复'
  206. ],
  207. radarData: [82, 88, 78, 85, 92]
  208. },
  209. {
  210. id: 2, code: 'PL-YL-002', name: '建设东路高压管', area: '沅陵县太常片区建设东路',
  211. riskLevel: 'high', riskScore: 79.2,
  212. material: '钢管', pressureLevel: '高压 (1.6MPa)', diameter: 400, length: 3.6,
  213. buryYear: '2016年', designLife: '30年', owner: '沅陵燃气有限公司',
  214. maintenanceRecords: [
  215. { id: 1, time: '2025-11-08', description: '壁厚检测发现局部减薄超限,紧急停气修复', type: '紧急' },
  216. { id: 2, time: '2025-06-20', description: '工业园段增设阴极保护装置', type: '常规' },
  217. { id: 3, time: '2025-01-10', description: '检测发现多处腐蚀点,标记重点监控', type: '常规' }
  218. ],
  219. hazards: [
  220. { id: 1, name: '太常化工仓库', distance: 130, level: '高' },
  221. { id: 2, name: '工业园计量站', distance: 90, level: '中' },
  222. { id: 3, name: '建设路加油站', distance: 200, level: '中' }
  223. ],
  224. protectionTargets: [
  225. { id: 1, name: '太常工业园', distance: 150, type: '工业区' },
  226. { id: 2, name: '建设东路居民区', distance: 100, type: '住宅区' }
  227. ],
  228. riskCauses: [
  229. '高压运行,泄漏后果严重性高',
  230. '管道穿越工业园区域,周边化工企业密集',
  231. '检测发现多处腐蚀点,局部壁厚减薄',
  232. '建设路重型车辆通行频繁,地面荷载大',
  233. '防腐层老化加速,需加密监测'
  234. ],
  235. radarData: [88, 72, 85, 76, 65]
  236. },
  237. {
  238. id: 3, code: 'PL-YL-003', name: '古城路中压管线', area: '沅陵县太常片区古城路',
  239. riskLevel: 'medium', riskScore: 64.3,
  240. material: '钢管', pressureLevel: '中压 (0.4MPa)', diameter: 300, length: 3.2,
  241. buryYear: '2018年', designLife: '30年', owner: '沅陵燃气有限公司',
  242. maintenanceRecords: [
  243. { id: 1, time: '2026-01-12', description: '古城路管沟交叉段防腐层修复', type: '常规' },
  244. { id: 2, time: '2025-08-05', description: '房地产开发区域增设警示标识及隔离围栏', type: '常规' }
  245. ],
  246. hazards: [
  247. { id: 1, name: '古城路在建房地产项目', distance: 65, level: '中' },
  248. { id: 2, name: '古城中路电力管沟', distance: 30, level: '中' }
  249. ],
  250. protectionTargets: [
  251. { id: 1, name: '古城路商业步行街', distance: 80, type: '商业区' },
  252. { id: 2, name: '太常安置小区', distance: 150, type: '住宅区' },
  253. { id: 3, name: '沅陵古城游客中心', distance: 200, type: '公共场所' }
  254. ],
  255. riskCauses: [
  256. '沿线房地产开发施工,第三方破坏风险中等',
  257. '与电力管沟交叉段存在杂散电流干扰',
  258. '古城路商业区人流量大,后果严重性较高',
  259. '部分区域防腐层出现老化脱落',
  260. '管沟交叉区域检测难度大'
  261. ],
  262. radarData: [42, 68, 55, 58, 72]
  263. },
  264. {
  265. id: 4, code: 'PL-YL-004', name: '迎宾路中压管线', area: '沅陵县太常片区迎宾路',
  266. riskLevel: 'medium', riskScore: 55.8,
  267. material: '钢管', pressureLevel: '中压 (0.4MPa)', diameter: 300, length: 3.6,
  268. buryYear: '2019年', designLife: '30年', owner: '沅陵燃气有限公司',
  269. maintenanceRecords: [
  270. { id: 1, time: '2025-11-10', description: '电力管沟交叉段杂散电流排流治理', type: '常规' },
  271. { id: 2, time: '2025-05-18', description: '定期巡检,防腐层状况整体良好', type: '常规' }
  272. ],
  273. hazards: [
  274. { id: 1, name: '迎宾东路地下通道', distance: 120, type: '中' },
  275. { id: 2, name: '迎宾路电力管沟', distance: 25, level: '中' }
  276. ],
  277. protectionTargets: [
  278. { id: 1, name: '迎宾路商业广场', distance: 100, type: '商业区' },
  279. { id: 2, name: '太常街道办事处', distance: 200, type: '办公区' }
  280. ],
  281. riskCauses: [
  282. '电力管沟交叉段存在杂散电流干扰风险',
  283. '迎宾路商业区人流较大,防护目标敏感',
  284. '钢管轻微腐蚀,需定期检测维护',
  285. '管线运行正常,整体风险可控'
  286. ],
  287. radarData: [38, 58, 52, 48, 62]
  288. },
  289. {
  290. id: 5, code: 'PL-YL-005', name: '天宁路中压管线', area: '沅陵县太常片区天宁路',
  291. riskLevel: 'medium', riskScore: 48.2,
  292. material: 'PE管', pressureLevel: '中压 (0.4MPa)', diameter: 250, length: 3.8,
  293. buryYear: '2020年', designLife: '50年', owner: '沅陵燃气有限公司',
  294. maintenanceRecords: [
  295. { id: 1, time: '2025-12-05', description: '天宁西路地下车库施工段PE管接头超声波检测', type: '常规' }
  296. ],
  297. hazards: [
  298. { id: 1, name: '天宁西路地下车库施工区', distance: 40, level: '中' }
  299. ],
  300. protectionTargets: [
  301. { id: 1, name: '太常安置小区', distance: 60, type: '住宅区' },
  302. { id: 2, name: '天宁路小学', distance: 180, type: '学校' },
  303. { id: 3, name: '县政府办公区', distance: 250, type: '办公区' }
  304. ],
  305. riskCauses: [
  306. '地下车库施工距管线仅40m,需重点监控',
  307. 'PE管接头处需定期检测确保密封',
  308. '管道穿越安置区,人口密集防护要求高',
  309. 'PE管材耐腐蚀但抗外力破坏能力较弱'
  310. ],
  311. radarData: [25, 55, 28, 42, 48]
  312. },
  313. {
  314. id: 6, code: 'PL-YL-006', name: '辰州北路中压管线', area: '沅陵县太常片区辰州北路',
  315. riskLevel: 'medium', riskScore: 42.5,
  316. material: 'PE管', pressureLevel: '中压 (0.4MPa)', diameter: 200, length: 1.2,
  317. buryYear: '2021年', designLife: '50年', owner: '沅陵燃气有限公司',
  318. maintenanceRecords: [
  319. { id: 1, time: '2025-10-15', description: '管廊交叉段巡检,隔离设施完好', type: '常规' }
  320. ],
  321. hazards: [
  322. { id: 1, name: '辰州北路综合管廊', distance: 15, level: '高' }
  323. ],
  324. protectionTargets: [
  325. { id: 1, name: '辰州北路商业区', distance: 80, type: '商业区' },
  326. { id: 2, name: '古城北路居民区', distance: 120, type: '住宅区' }
  327. ],
  328. riskCauses: [
  329. '与综合管廊共通道,电力通信管线交叉密集',
  330. '管廊内可燃气体可能积聚,交叉风险高',
  331. '管段较短但周边地下环境复杂',
  332. 'PE管与管廊结构隔离需持续维护'
  333. ],
  334. radarData: [18, 55, 20, 65, 48]
  335. },
  336. {
  337. id: 7, code: 'PL-YL-007', name: '滨江路低压管线', area: '沅陵县太常片区滨江路',
  338. riskLevel: 'low', riskScore: 28.6,
  339. material: 'PE管', pressureLevel: '低压 (0.1MPa)', diameter: 200, length: 3.8,
  340. buryYear: '2022年', designLife: '50年', owner: '沅陵燃气有限公司',
  341. maintenanceRecords: [
  342. { id: 1, time: '2025-08-20', description: '汛期后河堤段土体稳定性检查,正常', type: '常规' }
  343. ],
  344. hazards: [
  345. { id: 1, name: '滨江东路河堤管廊', distance: 100, level: '低' }
  346. ],
  347. protectionTargets: [
  348. { id: 1, name: '滨江公园', distance: 80, type: '公园' },
  349. { id: 2, name: '滨江路居民区', distance: 150, type: '住宅区' }
  350. ],
  351. riskCauses: [
  352. '新建管道,整体状况良好',
  353. '低压运行,泄漏后果可控',
  354. '河堤段需关注汛期水土流失影响',
  355. 'PE管耐腐蚀,维护需求低'
  356. ],
  357. radarData: [15, 25, 18, 22, 35]
  358. },
  359. {
  360. id: 8, code: 'PL-YL-008', name: '太常南路低压管线', area: '沅陵县太常片区太常南路',
  361. riskLevel: 'low', riskScore: 18.3,
  362. material: 'PE管', pressureLevel: '低压 (0.1MPa)', diameter: 160, length: 1.5,
  363. buryYear: '2023年', designLife: '50年', owner: '沅陵燃气有限公司',
  364. maintenanceRecords: [],
  365. hazards: [],
  366. protectionTargets: [
  367. { id: 1, name: '城南新开发小区', distance: 100, type: '住宅区' }
  368. ],
  369. riskCauses: [
  370. '新建管道,风险极低',
  371. '管径小、压力低,泄漏影响范围有限',
  372. '位于城南新开发区域,周边环境稳定',
  373. 'PE管材耐腐蚀性能好,寿命长'
  374. ],
  375. radarData: [8, 18, 10, 12, 22]
  376. }
  377. ])
  378. // 统计数据
  379. const totalLength = computed(() => pipelines.value.reduce((sum, p) => sum + p.length, 0).toFixed(1))
  380. const totalPipelines = computed(() => pipelines.value.length)
  381. const highRiskCount = computed(() => pipelines.value.filter(p => p.riskLevel === 'high').length)
  382. // 筛选
  383. const searchKeyword = ref('')
  384. const riskFilter = ref('')
  385. const currentPage = ref(1)
  386. const pageSize = 5
  387. const selectedPipeline = ref(pipelines.value[0])
  388. const filteredPipelines = computed(() => {
  389. let list = pipelines.value
  390. if (searchKeyword.value) {
  391. const kw = searchKeyword.value.toLowerCase()
  392. list = list.filter(p => p.name.toLowerCase().includes(kw) || p.code.toLowerCase().includes(kw))
  393. }
  394. if (riskFilter.value) {
  395. list = list.filter(p => p.riskLevel === riskFilter.value)
  396. }
  397. return list
  398. })
  399. const paginatedPipelines = computed(() => {
  400. const start = (currentPage.value - 1) * pageSize
  401. return filteredPipelines.value.slice(start, start + pageSize)
  402. })
  403. // 雷达图
  404. const radarRef = ref(null)
  405. let radarInstance = null
  406. function renderRadarChart() {
  407. if (!radarRef.value || !selectedPipeline.value) return
  408. if (!radarInstance) {
  409. radarInstance = echarts.init(radarRef.value)
  410. }
  411. const data = selectedPipeline.value.radarData
  412. radarInstance.setOption({
  413. tooltip: {},
  414. legend: { data: ['风险评分'], bottom: 0 },
  415. radar: {
  416. indicator: [
  417. { name: '管道老化风险', max: 100 },
  418. { name: '第三方破坏', max: 100 },
  419. { name: '腐蚀泄漏风险', max: 100 },
  420. { name: '危险源影响', max: 100 },
  421. { name: '防护目标敏感性', max: 100 }
  422. ],
  423. shape: 'circle',
  424. center: ['50%', '50%'],
  425. radius: '62%',
  426. name: { textStyle: { fontSize: 10, color: '#606266' } }
  427. },
  428. series: [{
  429. type: 'radar',
  430. data: [{ value: data, name: '风险评分' }],
  431. areaStyle: { color: 'rgba(245, 108, 108, 0.2)' },
  432. lineStyle: { color: '#f56c6c', width: 2 },
  433. itemStyle: { color: '#f56c6c' }
  434. }]
  435. })
  436. }
  437. const getScoreClass = (score) => {
  438. if (score >= 70) return 'high-score'
  439. if (score >= 40) return 'medium-score'
  440. return 'low-score'
  441. }
  442. const selectPipeline = (pipeline) => {
  443. selectedPipeline.value = pipeline
  444. nextTick(() => renderRadarChart())
  445. }
  446. const refreshData = () => { ElMessage.success('数据已刷新') }
  447. const exportReport = () => { ElMessage.success('导出风险分析报告(演示)') }
  448. onMounted(() => {
  449. nextTick(() => renderRadarChart())
  450. })
  451. onBeforeUnmount(() => {
  452. if (radarInstance) {
  453. radarInstance.dispose()
  454. radarInstance = null
  455. }
  456. })
  457. </script>
  458. <style scoped>
  459. .pipeline-risk-analysis {
  460. background: #f0f2f6;
  461. min-height: 100vh;
  462. padding: 20px;
  463. }
  464. .page-header {
  465. background: white;
  466. border-radius: 16px;
  467. padding: 16px 24px;
  468. margin-bottom: 20px;
  469. display: flex;
  470. justify-content: space-between;
  471. align-items: center;
  472. flex-wrap: wrap;
  473. gap: 12px;
  474. }
  475. .header-left { display: flex; align-items: center; gap: 20px; }
  476. .header-left .title { font-size: 1.3rem; font-weight: 600; }
  477. .stats-badge { display: flex; gap: 20px; }
  478. .stat-item { font-size: 13px; display: flex; align-items: center; gap: 6px; }
  479. .stat-item.critical { color: #f56c6c; }
  480. .stat-item .dot { width: 8px; height: 8px; border-radius: 50%; background: #67c23a; }
  481. .header-right { display: flex; gap: 12px; }
  482. .main-layout {
  483. display: flex;
  484. gap: 20px;
  485. }
  486. .pipeline-list-panel {
  487. width: 320px;
  488. background: white;
  489. border-radius: 16px;
  490. display: flex;
  491. flex-direction: column;
  492. overflow: hidden;
  493. }
  494. .panel-header {
  495. padding: 16px;
  496. border-bottom: 1px solid #e9ecef;
  497. display: flex;
  498. justify-content: space-between;
  499. align-items: center;
  500. font-weight: 600;
  501. }
  502. .pipeline-list {
  503. flex: 1;
  504. overflow-y: auto;
  505. padding: 12px;
  506. }
  507. .pipeline-item {
  508. padding: 12px;
  509. margin-bottom: 10px;
  510. border-radius: 12px;
  511. background: #fafbfc;
  512. border: 1px solid #e9ecef;
  513. cursor: pointer;
  514. transition: all 0.2s;
  515. }
  516. .pipeline-item.selected { background: #ecf5ff; border-color: #409eff; }
  517. .pipeline-item.risk-high { border-left: 3px solid #f56c6c; }
  518. .pipeline-item.risk-medium { border-left: 3px solid #e6a23c; }
  519. .pipeline-header { display: flex; justify-content: space-between; margin-bottom: 6px; }
  520. .pipeline-name { font-weight: 600; }
  521. .pipeline-code { font-size: 11px; color: #909399; margin-bottom: 4px; }
  522. .pipeline-location { font-size: 11px; color: #6c757d; display: flex; align-items: center; gap: 4px; margin-bottom: 4px; }
  523. .risk-score { font-size: 12px; font-weight: 500; }
  524. .detail-panel {
  525. flex: 1;
  526. background: white;
  527. border-radius: 16px;
  528. padding: 20px;
  529. overflow-y: auto;
  530. max-height: calc(100vh - 160px);
  531. }
  532. .empty-panel {
  533. flex: 1;
  534. background: white;
  535. border-radius: 16px;
  536. display: flex;
  537. align-items: center;
  538. justify-content: center;
  539. }
  540. .detail-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
  541. .detail-header h3 { margin: 0; font-size: 20px; }
  542. .section-title { font-weight: 600; margin: 16px 0 12px 0; font-size: 15px; }
  543. .radar-section { background: #fafbfc; border-radius: 12px; padding: 16px; margin-bottom: 20px; }
  544. .radar-chart { height: 280px; }
  545. .risk-summary { text-align: center; margin-top: 12px; font-size: 14px; }
  546. .risk-summary .high-score { color: #f56c6c; font-size: 24px; }
  547. .risk-summary .medium-score { color: #e6a23c; font-size: 24px; }
  548. .risk-summary .low-score { color: #67c23a; font-size: 24px; }
  549. .risk-level { margin-left: 12px; color: #606266; }
  550. .info-cards {
  551. display: grid;
  552. grid-template-columns: repeat(2, 1fr);
  553. gap: 16px;
  554. margin-bottom: 20px;
  555. }
  556. .info-card {
  557. background: #fafbfc;
  558. border-radius: 12px;
  559. padding: 12px;
  560. }
  561. .card-header { display: flex; align-items: center; gap: 8px; font-weight: 600; margin-bottom: 12px; }
  562. .maintenance-list, .hazard-list, .target-list { max-height: 200px; overflow-y: auto; }
  563. .maintenance-item, .hazard-item, .target-item {
  564. padding: 8px 0;
  565. border-bottom: 1px solid #e9ecef;
  566. }
  567. .maintenance-time { font-size: 11px; color: #909399; }
  568. .maintenance-desc { font-size: 12px; margin: 4px 0; }
  569. .hazard-name, .target-name { font-weight: 500; font-size: 13px; }
  570. .hazard-distance, .target-distance { font-size: 11px; color: #909399; }
  571. .empty-text { color: #c0c4cc; text-align: center; padding: 16px; }
  572. .cause-list { display: flex; flex-direction: column; gap: 10px; }
  573. .cause-item { display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 6px 0; border-bottom: 1px solid #f0f0f0; }
  574. .cause-item .el-icon { color: #f56c6c; }
  575. .pagination-area { padding: 12px; text-align: center; border-top: 1px solid #eee; }
  576. </style>