1
This commit is contained in:
		
							
								
								
									
										2
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -15,11 +15,13 @@ declare module 'vue' { | |||||||
|     VanCheckbox: typeof import('vant/es')['Checkbox'] |     VanCheckbox: typeof import('vant/es')['Checkbox'] | ||||||
|     VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup'] |     VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup'] | ||||||
|     VanIcon: typeof import('vant/es')['Icon'] |     VanIcon: typeof import('vant/es')['Icon'] | ||||||
|  |     VanLoading: typeof import('vant/es')['Loading'] | ||||||
|     VanNavBar: typeof import('vant/es')['NavBar'] |     VanNavBar: typeof import('vant/es')['NavBar'] | ||||||
|     VanPopup: typeof import('vant/es')['Popup'] |     VanPopup: typeof import('vant/es')['Popup'] | ||||||
|     VanTab: typeof import('vant/es')['Tab'] |     VanTab: typeof import('vant/es')['Tab'] | ||||||
|     VanTabbar: typeof import('vant/es')['Tabbar'] |     VanTabbar: typeof import('vant/es')['Tabbar'] | ||||||
|     VanTabbarItem: typeof import('vant/es')['TabbarItem'] |     VanTabbarItem: typeof import('vant/es')['TabbarItem'] | ||||||
|     VanTabs: typeof import('vant/es')['Tabs'] |     VanTabs: typeof import('vant/es')['Tabs'] | ||||||
|  |     VanTag: typeof import('vant/es')['Tag'] | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								public/assets/img.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/assets/img.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 56 KiB | 
| @ -1,5 +1,8 @@ | |||||||
| import { createRouter, createWebHistory } from "vue-router"; | import { createRouter, createWebHistory } from "vue-router"; | ||||||
|  | import AlarmLevel from "@/views/alarm/alarmLevel.vue"; | ||||||
|  | import AlarmList from "@/views/alarm/alarmList.vue"; | ||||||
|  | import AlarmDetail from "@/views/alarm/alarmDetail.vue"; | ||||||
|  | ("@/views/alarm/alarmLevel.vue"); | ||||||
| const router = createRouter({ | const router = createRouter({ | ||||||
|   history: createWebHistory(import.meta.env.BASE_URL), |   history: createWebHistory(import.meta.env.BASE_URL), | ||||||
|   routes: [ |   routes: [ | ||||||
| @ -20,6 +23,16 @@ const router = createRouter({ | |||||||
|           path: "/management", |           path: "/management", | ||||||
|           component: () => import("@/views/management.vue"), |           component: () => import("@/views/management.vue"), | ||||||
|         }, |         }, | ||||||
|  |         { | ||||||
|  |           path: "/alarmLevel", | ||||||
|  |           name: "AlarmLevel", | ||||||
|  |           component: AlarmLevel, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           path: "/alarmList", | ||||||
|  |           name: "AlarmList", | ||||||
|  |           component: AlarmList, | ||||||
|  |         }, | ||||||
|       ], |       ], | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @ -49,6 +62,11 @@ const router = createRouter({ | |||||||
|       path: "/ktxx/:title", |       path: "/ktxx/:title", | ||||||
|       component: () => import("@/views/ktxx.vue"), |       component: () => import("@/views/ktxx.vue"), | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |       name: "AlarmDetail", | ||||||
|  |       path: "/alarmDetail", | ||||||
|  |       component: AlarmDetail, | ||||||
|  |     }, | ||||||
|   ], |   ], | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,2 +1,286 @@ | |||||||
| <template>实时警报</template> | <template> | ||||||
| <script setup></script> |   <div class="real-time-report"> | ||||||
|  |     <!-- 顶部标题栏 --> | ||||||
|  |     <header class="header"> | ||||||
|  |       <h1>实时警报</h1> | ||||||
|  |     </header> | ||||||
|  |  | ||||||
|  |     <div class="vant-buttons-top"> | ||||||
|  |       <van-button type="primary" @click="handleSourceDistribution" round | ||||||
|  |         >来源分布</van-button | ||||||
|  |       > | ||||||
|  |       <van-button type="success" @click="handleAlarmLevel" round | ||||||
|  |         >告警级别统计</van-button | ||||||
|  |       > | ||||||
|  |       <van-button type="warning" @click="handleAlarmList" round | ||||||
|  |         >告警列表</van-button | ||||||
|  |       > | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- 数据概览卡片 --> | ||||||
|  |     <div class="summary-card"> | ||||||
|  |       <div class="summary-item"> | ||||||
|  |         <div class="summary-label">告警总数</div> | ||||||
|  |         <div class="summary-value">{{ alarmTotal }}</div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- 图表容器 --> | ||||||
|  |     <div class="chart-wrapper"> | ||||||
|  |       <!-- 环形图区域(已修改中间内容) --> | ||||||
|  |       <div class="chart-card"> | ||||||
|  |         <div class="chart-title">分布图</div> | ||||||
|  |         <div class="chart-container" ref="pieChartRef"></div> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <!-- 柱状图 --> | ||||||
|  |       <div class="chart-card"> | ||||||
|  |         <div class="chart-title">分类对比</div> | ||||||
|  |         <div class="chart-container" ref="barChartRef"></div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { onMounted, ref } from "vue"; | ||||||
|  | import * as echarts from "echarts"; | ||||||
|  | import { useRouter } from "vue-router"; | ||||||
|  |  | ||||||
|  | const router = useRouter(); | ||||||
|  |  | ||||||
|  | // 图表引用 | ||||||
|  | const pieChartRef = ref(null); | ||||||
|  | const barChartRef = ref(null); | ||||||
|  |  | ||||||
|  | // 状态数据 | ||||||
|  | const alarmTotal = ref(100); | ||||||
|  | const pieChart = ref(null); | ||||||
|  | const barChart = ref(null); | ||||||
|  |  | ||||||
|  | // 模拟真实数据 | ||||||
|  | const ringChartData = [ | ||||||
|  |   { name: "华三网", value: 20 }, | ||||||
|  |   { name: "阿里云", value: 30 }, | ||||||
|  |   { name: "S600", value: 25 }, | ||||||
|  |   { name: "动环", value: 25 }, | ||||||
|  | ]; | ||||||
|  | const barChartData = { | ||||||
|  |   xAxis: ["S6000", "动环", "阿里云", "华三网"], | ||||||
|  |   series: [25, 30, 25, 20], | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 初始化图表 | ||||||
|  | const initCharts = () => { | ||||||
|  |   // 环形图配置 | ||||||
|  |   if (pieChartRef.value) { | ||||||
|  |     pieChart.value = echarts.init(pieChartRef.value); | ||||||
|  |     pieChart.value.setOption({ | ||||||
|  |       tooltip: { | ||||||
|  |         trigger: "item", | ||||||
|  |       }, | ||||||
|  |       legend: { | ||||||
|  |         bottom: 0, | ||||||
|  |         textStyle: { | ||||||
|  |           fontSize: 12, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       series: [ | ||||||
|  |         { | ||||||
|  |           name: "数据分布", | ||||||
|  |           type: "pie", | ||||||
|  |           radius: ["40%", "70%"], | ||||||
|  |           avoidLabelOverlap: false, | ||||||
|  |           itemStyle: { | ||||||
|  |             borderRadius: 8, | ||||||
|  |             borderColor: "#fff", | ||||||
|  |             borderWidth: 2, | ||||||
|  |           }, | ||||||
|  |           label: { | ||||||
|  |             show: false, | ||||||
|  |             position: "center", | ||||||
|  |           }, | ||||||
|  |           emphasis: { | ||||||
|  |             label: { | ||||||
|  |               show: false, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           labelLine: { | ||||||
|  |             show: false, | ||||||
|  |           }, | ||||||
|  |           data: ringChartData, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // 柱状图配置 | ||||||
|  |   if (barChartRef.value) { | ||||||
|  |     barChart.value = echarts.init(barChartRef.value); | ||||||
|  |     barChart.value.setOption({ | ||||||
|  |       tooltip: { | ||||||
|  |         trigger: "axis", | ||||||
|  |         axisPointer: { | ||||||
|  |           type: "shadow", | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       grid: { | ||||||
|  |         left: "3%", | ||||||
|  |         right: "4%", | ||||||
|  |         bottom: "3%", | ||||||
|  |         containLabel: true, | ||||||
|  |       }, | ||||||
|  |       xAxis: { | ||||||
|  |         type: "category", | ||||||
|  |         data: barChartData.xAxis, | ||||||
|  |         axisLabel: { | ||||||
|  |           fontSize: 10, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       yAxis: { | ||||||
|  |         type: "value", | ||||||
|  |         axisLabel: { | ||||||
|  |           fontSize: 10, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       series: [ | ||||||
|  |         { | ||||||
|  |           name: "数量", | ||||||
|  |           type: "bar", | ||||||
|  |           data: barChartData.series, | ||||||
|  |           itemStyle: { | ||||||
|  |             borderRadius: [4, 4, 0, 0], | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 按钮点击事件(无修改) | ||||||
|  | const handleSourceDistribution = () => { | ||||||
|  |   router.push("/alarm"); | ||||||
|  | }; | ||||||
|  | const handleAlarmLevel = () => { | ||||||
|  |   router.push({ name: "AlarmLevel" }); | ||||||
|  | }; | ||||||
|  | const handleAlarmList = () => { | ||||||
|  |   router.push({ name: "AlarmList" }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | onMounted(() => { | ||||||
|  |   initCharts(); | ||||||
|  |  | ||||||
|  |   // 窗口大小监听(保留原功能) | ||||||
|  |   const handleResize = () => { | ||||||
|  |     if (pieChart.value) pieChart.value.resize(); | ||||||
|  |     if (barChart.value) barChart.value.resize(); | ||||||
|  |   }; | ||||||
|  |   window.addEventListener("resize", handleResize); | ||||||
|  |   return () => { | ||||||
|  |     window.removeEventListener("resize", handleResize); | ||||||
|  |     if (pieChart.value) pieChart.value.dispose(); | ||||||
|  |     if (barChart.value) barChart.value.dispose(); | ||||||
|  |   }; | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | .real-time-report { | ||||||
|  |   min-height: 100vh; | ||||||
|  |   background-color: #f5f7fa; | ||||||
|  |   padding-bottom: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .header { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   align-items: center; | ||||||
|  |   padding: 16px 20px; | ||||||
|  |   background-color: #35495e; | ||||||
|  |   color: white; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .header h1 { | ||||||
|  |   font-size: 18px; | ||||||
|  |   font-weight: 600; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .vant-buttons-top { | ||||||
|  |   display: flex; | ||||||
|  |   gap: 12px; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  |   justify-content: center; | ||||||
|  |   padding: 12px 15px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .van-button--round { | ||||||
|  |   --van-button-border-radius: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .summary-card { | ||||||
|  |   padding: 15px 10px; | ||||||
|  |   background-color: white; | ||||||
|  |   margin: 0 15px 15px; | ||||||
|  |   border-radius: 12px; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .summary-item { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .summary-label { | ||||||
|  |   font-size: 14px; | ||||||
|  |   color: #666; | ||||||
|  |   margin-bottom: 5px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .summary-value { | ||||||
|  |   font-size: 18px; | ||||||
|  |   font-weight: 600; | ||||||
|  |   color: #333; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .chart-wrapper { | ||||||
|  |   padding: 0 15px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .chart-card { | ||||||
|  |   background-color: white; | ||||||
|  |   border-radius: 12px; | ||||||
|  |   padding: 15px; | ||||||
|  |   margin-bottom: 15px; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .chart-title { | ||||||
|  |   font-size: 16px; | ||||||
|  |   font-weight: 500; | ||||||
|  |   color: #333; | ||||||
|  |   margin-bottom: 10px; | ||||||
|  |   padding-left: 5px; | ||||||
|  |   border-left: 3px solid #35495e; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .chart-container { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 220px; | ||||||
|  | } | ||||||
|  | @media (max-width: 320px) { | ||||||
|  |   .chart-container { | ||||||
|  |     height: 180px; | ||||||
|  |   } | ||||||
|  |   .summary-value { | ||||||
|  |     font-size: 16px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media (min-width: 414px) { | ||||||
|  |   .chart-container { | ||||||
|  |     height: 250px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										430
									
								
								src/views/alarm/alarmDetail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										430
									
								
								src/views/alarm/alarmDetail.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,430 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="alarm-detail-page"> | ||||||
|  |     <!-- 顶部导航栏 --> | ||||||
|  |     <header class="detail-header"> | ||||||
|  |       <div class="back-btn" @click="goBack"> | ||||||
|  |         <van-icon name="arrow-left" size="22" color="#fff" /> | ||||||
|  |       </div> | ||||||
|  |       <h2 class="header-title">告警详情</h2> | ||||||
|  |       <div class="header-placeholder"></div> | ||||||
|  |     </header> | ||||||
|  |  | ||||||
|  |     <!-- 加载状态 --> | ||||||
|  |     <van-loading | ||||||
|  |       v-if="isLoading" | ||||||
|  |       type="spinner" | ||||||
|  |       color="#1890ff" | ||||||
|  |       class="loading" | ||||||
|  |     /> | ||||||
|  |  | ||||||
|  |     <!-- 核心内容区 --> | ||||||
|  |     <div class="detail-container" v-else> | ||||||
|  |       <!-- 告警标题卡片 --> | ||||||
|  |       <div class="alarm-title-card"> | ||||||
|  |         <h3 class="alarm-name">{{ detailData.name }}</h3> | ||||||
|  |         <div class="alarm-level"> | ||||||
|  |           <van-tag :type="getTagType(detailData.levels[0])" size="small"> | ||||||
|  |             {{ detailData.levels[0] }} | ||||||
|  |           </van-tag> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <!-- 详情信息区 --> | ||||||
|  |       <div class="info-section"> | ||||||
|  |         <!-- 基础信息 --> | ||||||
|  |         <div class="info-card"> | ||||||
|  |           <h4 class="card-title">基础信息</h4> | ||||||
|  |           <div class="info-grid"> | ||||||
|  |             <div class="info-item"> | ||||||
|  |               <span class="item-label">告警时间</span> | ||||||
|  |               <span class="item-value">{{ detailData.alarmTime }}</span> | ||||||
|  |             </div> | ||||||
|  |             <div class="info-item"> | ||||||
|  |               <span class="item-label">告警来源</span> | ||||||
|  |               <span class="item-value">{{ detailData.source }}</span> | ||||||
|  |             </div> | ||||||
|  |             <div class="info-item"> | ||||||
|  |               <span class="item-label">告警类型</span> | ||||||
|  |               <span class="item-value">{{ detailData.type }}</span> | ||||||
|  |             </div> | ||||||
|  |             <div class="info-item"> | ||||||
|  |               <span class="item-label">告警分类</span> | ||||||
|  |               <span class="item-value">{{ detailData.category }}</span> | ||||||
|  |             </div> | ||||||
|  |             <div class="info-item"> | ||||||
|  |               <span class="item-label">OID</span> | ||||||
|  |               <span class="item-value">{{ detailData.oid }}</span> | ||||||
|  |             </div> | ||||||
|  |             <div class="info-item"> | ||||||
|  |               <span class="item-label">告警状态</span> | ||||||
|  |               <span | ||||||
|  |                 class="item-value status-{{ detailData.status ? 'resolved' : 'pending' }}" | ||||||
|  |               > | ||||||
|  |                 {{ detailData.status || "未处理" }} | ||||||
|  |               </span> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <!-- 告警原因 --> | ||||||
|  |         <div class="info-card"> | ||||||
|  |           <h4 class="card-title">告警原因</h4> | ||||||
|  |           <div class="reason-content"> | ||||||
|  |             {{ detailData.reason }} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <!-- 可能原因 --> | ||||||
|  |         <div class="info-card"> | ||||||
|  |           <h4 class="card-title">可能原因</h4> | ||||||
|  |           <ul class="reason-list"> | ||||||
|  |             <li | ||||||
|  |               v-for="(reason, index) in detailData.possibleReasons" | ||||||
|  |               :key="index" | ||||||
|  |             > | ||||||
|  |               <span class="reason-index">{{ index + 1 }}</span> | ||||||
|  |               <span class="reason-text">{{ reason }}</span> | ||||||
|  |             </li> | ||||||
|  |           </ul> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <!-- 修复建议 --> | ||||||
|  |         <div class="info-card"> | ||||||
|  |           <h4 class="card-title">修复建议</h4> | ||||||
|  |           <ul class="suggestion-list"> | ||||||
|  |             <li | ||||||
|  |               v-for="(suggestion, index) in detailData.repairSuggestions" | ||||||
|  |               :key="index" | ||||||
|  |             > | ||||||
|  |               <span class="suggestion-icon"> | ||||||
|  |                 <van-icon name="check-circle" color="#1890ff" size="16" /> | ||||||
|  |               </span> | ||||||
|  |               <span class="suggestion-text">{{ suggestion }}</span> | ||||||
|  |             </li> | ||||||
|  |           </ul> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <!-- 考核部门 --> | ||||||
|  |         <div class="info-card"> | ||||||
|  |           <h4 class="card-title">考核部门</h4> | ||||||
|  |           <div class="dept-content"> | ||||||
|  |             {{ detailData.department || "暂无指定部门" }} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <!-- 操作按钮区 --> | ||||||
|  |       <div class="action-buttons"> | ||||||
|  |         <van-button type="primary" round @click="handleResolve"> | ||||||
|  |           标记已处理 | ||||||
|  |         </van-button> | ||||||
|  |         <van-button type="warning" round @click="handleIgnore"> | ||||||
|  |           忽略告警 | ||||||
|  |         </van-button> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { ref, onMounted } from "vue"; | ||||||
|  | import { useRoute, useRouter } from "vue-router"; | ||||||
|  |  | ||||||
|  | const route = useRoute(); | ||||||
|  | const router = useRouter(); | ||||||
|  | const detailData = ref({ | ||||||
|  |   name: "ASW-201-DE.AM798", | ||||||
|  |   oid: "1.3.6.1.4.1.2550.6.1.2.6.6.9", | ||||||
|  |   alarmTime: "2022-08-31 19:01:03", | ||||||
|  |   type: "阿里云", | ||||||
|  |   category: "网站性能告警", | ||||||
|  |   reason: "性能数据处于任务告警阈值区域,超过预设阈值 15%", | ||||||
|  |   suggestion: "调整告警阈值参数;网络流量过大,请检查网络配置", | ||||||
|  |   source: "本系统", | ||||||
|  |   levels: ["重要"], | ||||||
|  |   possibleReasons: [ | ||||||
|  |     "阈值设置不合理(当前阈值低于业务峰值)", | ||||||
|  |     "网络流量过大(峰值达 1.2Gbps)", | ||||||
|  |     "服务器资源不足(CPU 使用率 85%)", | ||||||
|  |   ], | ||||||
|  |   repairSuggestions: [ | ||||||
|  |     "调整告警阈值至业务峰值 120%", | ||||||
|  |     "扩容网络带宽至 2Gbps", | ||||||
|  |     "增加服务器节点分担负载", | ||||||
|  |   ], | ||||||
|  |   department: "运维部", | ||||||
|  |   status: "未处理", | ||||||
|  | }); | ||||||
|  | const isLoading = ref(true); | ||||||
|  |  | ||||||
|  | // 加载数据 | ||||||
|  | onMounted(() => { | ||||||
|  |   const itemStr = route.params.item; | ||||||
|  |   if (itemStr) { | ||||||
|  |     detailData.value = JSON.parse(itemStr); | ||||||
|  |   } | ||||||
|  |   // 模拟接口加载延迟 | ||||||
|  |   setTimeout(() => { | ||||||
|  |     isLoading.value = false; | ||||||
|  |   }, 600); | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | // 返回上一页 | ||||||
|  | const goBack = () => { | ||||||
|  |   router.back(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 标记已处理 | ||||||
|  | const handleResolve = () => { | ||||||
|  |   detailData.value.status = "已处理"; | ||||||
|  |   // 实际项目中可调用接口更新状态 | ||||||
|  |   vanToast.success("已标记为已处理"); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 忽略告警 | ||||||
|  | const handleIgnore = () => { | ||||||
|  |   // 实际项目中可调用接口忽略告警 | ||||||
|  |   vanToast.success("已忽略此告警"); | ||||||
|  |   router.back(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 根据级别获取 Tag 类型 | ||||||
|  | const getTagType = (level) => { | ||||||
|  |   switch (level) { | ||||||
|  |     case "紧急": | ||||||
|  |       return "danger"; | ||||||
|  |     case "重要": | ||||||
|  |       return "warning"; | ||||||
|  |     case "次要": | ||||||
|  |       return "info"; | ||||||
|  |     case "通知": | ||||||
|  |       return "success"; | ||||||
|  |     default: | ||||||
|  |       return "default"; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | /* 全局样式 */ | ||||||
|  | .alarm-detail-page { | ||||||
|  |   min-height: 100vh; | ||||||
|  |   background-color: #f5f7fa; | ||||||
|  |   font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | ||||||
|  |   padding-bottom: 30px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 顶部导航 */ | ||||||
|  | .detail-header { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   background-color: #2f4050; | ||||||
|  |   padding: 14px 20px; | ||||||
|  |   color: #fff; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||||||
|  |   position: sticky; | ||||||
|  |   top: 0; | ||||||
|  |   z-index: 10; | ||||||
|  | } | ||||||
|  | .back-btn { | ||||||
|  |   cursor: pointer; | ||||||
|  |   padding: 4px; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   transition: background-color 0.2s; | ||||||
|  | } | ||||||
|  | .back-btn:hover { | ||||||
|  |   background-color: rgba(255, 255, 255, 0.1); | ||||||
|  | } | ||||||
|  | .header-title { | ||||||
|  |   font-size: 18px; | ||||||
|  |   font-weight: 600; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | .header-placeholder { | ||||||
|  |   width: 22px; /* 与返回键宽度一致,保持标题居中 */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 加载状态 */ | ||||||
|  | .loading { | ||||||
|  |   position: fixed; | ||||||
|  |   top: 50%; | ||||||
|  |   left: 50%; | ||||||
|  |   transform: translate(-50%, -50%); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 核心内容容器 */ | ||||||
|  | .detail-container { | ||||||
|  |   max-width: 1200px; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   padding: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 告警标题卡片 */ | ||||||
|  | .alarm-title-card { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border-radius: 10px; | ||||||
|  |   padding: 20px; | ||||||
|  |   margin-bottom: 20px; | ||||||
|  |   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   align-items: center; | ||||||
|  | } | ||||||
|  | .alarm-name { | ||||||
|  |   font-size: 20px; | ||||||
|  |   font-weight: 600; | ||||||
|  |   color: #333; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | .alarm-level { | ||||||
|  |   margin-left: 10px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 信息区通用样式 */ | ||||||
|  | .info-section { | ||||||
|  |   margin-bottom: 20px; | ||||||
|  | } | ||||||
|  | .info-card { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border-radius: 10px; | ||||||
|  |   padding: 20px; | ||||||
|  |   margin-bottom: 16px; | ||||||
|  |   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05); | ||||||
|  |   transition: box-shadow 0.2s; | ||||||
|  | } | ||||||
|  | .info-card:hover { | ||||||
|  |   box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); | ||||||
|  | } | ||||||
|  | .card-title { | ||||||
|  |   font-size: 16px; | ||||||
|  |   font-weight: 600; | ||||||
|  |   color: #333; | ||||||
|  |   margin: 0 0 16px; | ||||||
|  |   padding-left: 8px; | ||||||
|  |   border-left: 3px solid #1890ff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 基础信息网格 */ | ||||||
|  | .info-grid { | ||||||
|  |   display: grid; | ||||||
|  |   grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); | ||||||
|  |   gap: 16px; | ||||||
|  | } | ||||||
|  | .info-item { | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  | } | ||||||
|  | .item-label { | ||||||
|  |   font-size: 14px; | ||||||
|  |   color: #666; | ||||||
|  |   margin-bottom: 4px; | ||||||
|  | } | ||||||
|  | .item-value { | ||||||
|  |   font-size: 15px; | ||||||
|  |   color: #333; | ||||||
|  | } | ||||||
|  | .status-resolved { | ||||||
|  |   color: #52c41a; | ||||||
|  | } | ||||||
|  | .status-pending { | ||||||
|  |   color: #faad14; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 告警原因 */ | ||||||
|  | .reason-content { | ||||||
|  |   font-size: 15px; | ||||||
|  |   color: #333; | ||||||
|  |   line-height: 1.6; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 可能原因列表 */ | ||||||
|  | .reason-list { | ||||||
|  |   list-style: none; | ||||||
|  |   padding: 0; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | .reason-list li { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: flex-start; | ||||||
|  |   margin-bottom: 12px; | ||||||
|  |   line-height: 1.6; | ||||||
|  | } | ||||||
|  | .reason-index { | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 22px; | ||||||
|  |   height: 22px; | ||||||
|  |   background-color: #1890ff; | ||||||
|  |   color: #fff; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   text-align: center; | ||||||
|  |   line-height: 22px; | ||||||
|  |   font-size: 12px; | ||||||
|  |   margin-right: 10px; | ||||||
|  |   flex-shrink: 0; | ||||||
|  | } | ||||||
|  | .reason-text { | ||||||
|  |   font-size: 15px; | ||||||
|  |   color: #333; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 修复建议列表 */ | ||||||
|  | .suggestion-list { | ||||||
|  |   list-style: none; | ||||||
|  |   padding: 0; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | .suggestion-list li { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: flex-start; | ||||||
|  |   margin-bottom: 12px; | ||||||
|  |   line-height: 1.6; | ||||||
|  | } | ||||||
|  | .suggestion-icon { | ||||||
|  |   margin-right: 10px; | ||||||
|  |   margin-top: 2px; | ||||||
|  |   flex-shrink: 0; | ||||||
|  | } | ||||||
|  | .suggestion-text { | ||||||
|  |   font-size: 15px; | ||||||
|  |   color: #333; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 考核部门 */ | ||||||
|  | .dept-content { | ||||||
|  |   font-size: 15px; | ||||||
|  |   color: #333; | ||||||
|  |   line-height: 1.6; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 操作按钮区 */ | ||||||
|  | .action-buttons { | ||||||
|  |   display: flex; | ||||||
|  |   gap: 16px; | ||||||
|  |   justify-content: center; | ||||||
|  |   margin-top: 30px; | ||||||
|  | } | ||||||
|  | .action-buttons .van-button { | ||||||
|  |   min-width: 140px; | ||||||
|  |   height: 44px; | ||||||
|  |   font-size: 15px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 响应式适配 */ | ||||||
|  | @media (max-width: 768px) { | ||||||
|  |   .info-grid { | ||||||
|  |     grid-template-columns: 1fr; | ||||||
|  |   } | ||||||
|  |   .action-buttons { | ||||||
|  |     flex-direction: column; | ||||||
|  |   } | ||||||
|  |   .alarm-title-card { | ||||||
|  |     flex-direction: column; | ||||||
|  |     align-items: flex-start; | ||||||
|  |   } | ||||||
|  |   .alarm-level { | ||||||
|  |     margin-left: 0; | ||||||
|  |     margin-top: 12px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										366
									
								
								src/views/alarm/alarmLevel.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								src/views/alarm/alarmLevel.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,366 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="alarm-level-stat-page"> | ||||||
|  |     <!-- 顶部导航栏 --> | ||||||
|  |     <header class="stat-header"> | ||||||
|  |       <div class="header-inner"> | ||||||
|  |         <h1 class="page-title">实时警报</h1> | ||||||
|  |       </div> | ||||||
|  |     </header> | ||||||
|  |  | ||||||
|  |     <!-- 功能按钮区 --> | ||||||
|  |     <div class="vant-buttons-top"> | ||||||
|  |       <van-button type="primary" @click="handleSourceDistribution" round | ||||||
|  |         >来源分布</van-button | ||||||
|  |       > | ||||||
|  |       <van-button type="success" @click="handleAlarmLevel" round | ||||||
|  |         >告警级别统计</van-button | ||||||
|  |       > | ||||||
|  |       <van-button type="warning" @click="handleAlarmList" round | ||||||
|  |         >告警列表</van-button | ||||||
|  |       > | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <!-- 核心内容区 --> | ||||||
|  |     <div class="content-container"> | ||||||
|  |       <!-- 标签栏 --> | ||||||
|  |       <van-tabs | ||||||
|  |         v-model:active="activeTab" | ||||||
|  |         @change="handleTabChange" | ||||||
|  |         class="tabs-wrapper" | ||||||
|  |         color="#1890ff" | ||||||
|  |         title-active-color="#1890ff" | ||||||
|  |         title-inactive-color="#666" | ||||||
|  |       > | ||||||
|  |         <!-- 告警级别统计标签 --> | ||||||
|  |         <van-tab title="告警级别统计"> | ||||||
|  |           <div class="tab-panel"> | ||||||
|  |             <!-- 统计图表卡片 --> | ||||||
|  |             <div class="chart-card"> | ||||||
|  |               <h3 class="card-title">告警级别分布</h3> | ||||||
|  |               <div class="chart-container" ref="chartRef"></div> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <!-- 统计表格卡片 --> | ||||||
|  |             <div class="table-card"> | ||||||
|  |               <h3 class="card-title">告警级别明细</h3> | ||||||
|  |               <div class="table-container"> | ||||||
|  |                 <table class="alarm-table"> | ||||||
|  |                   <thead> | ||||||
|  |                     <tr> | ||||||
|  |                       <th>告警级别</th> | ||||||
|  |                       <th>已处理</th> | ||||||
|  |                       <th>未处理</th> | ||||||
|  |                       <th>总数</th> | ||||||
|  |                       <th>处理率</th> | ||||||
|  |                     </tr> | ||||||
|  |                   </thead> | ||||||
|  |                   <tbody> | ||||||
|  |                     <tr | ||||||
|  |                       v-for="(item, index) in alarmStats" | ||||||
|  |                       :key="index" | ||||||
|  |                       :class="`level-${item.levelEn}`" | ||||||
|  |                     > | ||||||
|  |                       <td class="level-name"> | ||||||
|  |                         <span class="level-dot"></span> | ||||||
|  |                         {{ item.level }} | ||||||
|  |                       </td> | ||||||
|  |                       <td>{{ item.processed }}</td> | ||||||
|  |                       <td>{{ item.unprocessed }}</td> | ||||||
|  |                       <td>{{ item.total }}</td> | ||||||
|  |                       <td> | ||||||
|  |                         <div class="progress-bar"> | ||||||
|  |                           <div | ||||||
|  |                             class="progress-fill" | ||||||
|  |                             :style="{ width: item.rate + '%' }" | ||||||
|  |                           ></div> | ||||||
|  |                         </div> | ||||||
|  |                         <span class="rate-text">{{ item.rate }}%</span> | ||||||
|  |                       </td> | ||||||
|  |                     </tr> | ||||||
|  |                   </tbody> | ||||||
|  |                 </table> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </van-tab> | ||||||
|  |       </van-tabs> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { ref, onMounted, nextTick, onUnmounted } from "vue"; | ||||||
|  | import { useRouter } from "vue-router"; | ||||||
|  | import * as echarts from "echarts"; | ||||||
|  |  | ||||||
|  | const router = useRouter(); | ||||||
|  | const chartRef = ref(null); | ||||||
|  | let chartInstance = null; | ||||||
|  |  | ||||||
|  | // 告警统计数据 | ||||||
|  | const alarmStats = ref([ | ||||||
|  |   { | ||||||
|  |     level: "紧急", | ||||||
|  |     levelEn: "emergency", | ||||||
|  |     processed: 4, | ||||||
|  |     unprocessed: 3, | ||||||
|  |     total: 7, | ||||||
|  |     rate: 57, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     level: "重要", | ||||||
|  |     levelEn: "important", | ||||||
|  |     processed: 3, | ||||||
|  |     unprocessed: 2, | ||||||
|  |     total: 5, | ||||||
|  |     rate: 60, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     level: "次要", | ||||||
|  |     levelEn: "secondary", | ||||||
|  |     processed: 2, | ||||||
|  |     unprocessed: 3, | ||||||
|  |     total: 5, | ||||||
|  |     rate: 40, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     level: "通知", | ||||||
|  |     levelEn: "notice", | ||||||
|  |     processed: 0, | ||||||
|  |     unprocessed: 7, | ||||||
|  |     total: 7, | ||||||
|  |     rate: 0, | ||||||
|  |   }, | ||||||
|  | ]); | ||||||
|  |  | ||||||
|  | // 初始化图表 | ||||||
|  | const initChart = () => { | ||||||
|  |   nextTick(() => { | ||||||
|  |     if (!chartRef.value) return; | ||||||
|  |  | ||||||
|  |     chartInstance = echarts.init(chartRef.value); | ||||||
|  |     const option = { | ||||||
|  |       tooltip: { trigger: "axis", axisPointer: { type: "shadow" } }, | ||||||
|  |       legend: { | ||||||
|  |         data: ["已处理", "未处理"], | ||||||
|  |         bottom: 0, | ||||||
|  |         textStyle: { fontSize: 12 }, | ||||||
|  |       }, | ||||||
|  |       grid: { | ||||||
|  |         left: "3%", | ||||||
|  |         right: "4%", | ||||||
|  |         top: "10%", | ||||||
|  |         bottom: "15%", | ||||||
|  |         containLabel: true, | ||||||
|  |       }, | ||||||
|  |       xAxis: { | ||||||
|  |         type: "category", | ||||||
|  |         data: alarmStats.value.map((item) => item.level), | ||||||
|  |         axisLabel: { fontSize: 12 }, | ||||||
|  |       }, | ||||||
|  |       yAxis: { type: "value", axisLabel: { fontSize: 12 } }, | ||||||
|  |       series: [ | ||||||
|  |         { | ||||||
|  |           name: "已处理", | ||||||
|  |           type: "bar", | ||||||
|  |           data: alarmStats.value.map((item) => item.processed), | ||||||
|  |           itemStyle: { color: "green" }, | ||||||
|  |           barWidth: "30%", | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           name: "未处理", | ||||||
|  |           type: "bar", | ||||||
|  |           data: alarmStats.value.map((item) => item.unprocessed), | ||||||
|  |           itemStyle: { color: "red" }, | ||||||
|  |           barWidth: "30%", | ||||||
|  |         }, | ||||||
|  |       ], | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     chartInstance.setOption(option); | ||||||
|  |     window.addEventListener("resize", () => chartInstance.resize()); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | onMounted(() => initChart()); | ||||||
|  | onUnmounted(() => { | ||||||
|  |   if (chartInstance) { | ||||||
|  |     chartInstance.dispose(); | ||||||
|  |     window.removeEventListener("resize", () => chartInstance.resize()); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | const handleSourceDistribution = () => router.push("/alarm"); | ||||||
|  | const handleAlarmLevel = () => router.push({ name: "AlarmLevel" }); | ||||||
|  | const handleAlarmList = () => router.push({ name: "AlarmList" }); | ||||||
|  |  | ||||||
|  | // 标签切换逻辑 | ||||||
|  | const handleTabChange = (index) => { | ||||||
|  |   activeTab.value = index; | ||||||
|  |   console.log("切换到标签:", index); | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | .alarm-level-stat-page { | ||||||
|  |   min-height: 100vh; | ||||||
|  |   background-color: #f5f7fa; | ||||||
|  |   font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .stat-header { | ||||||
|  |   background-color: #2f4050; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | ||||||
|  | } | ||||||
|  | .header-inner { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   padding: 12px 20px; | ||||||
|  |   max-width: 1200px; | ||||||
|  |   margin: 0 auto; | ||||||
|  | } | ||||||
|  | .page-title { | ||||||
|  |   font-size: 18px; | ||||||
|  |   color: #fff; | ||||||
|  |   font-weight: 600; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .vant-buttons-top { | ||||||
|  |   display: flex; | ||||||
|  |   gap: 12px; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  |   justify-content: center; | ||||||
|  |   padding: 12px 15px; | ||||||
|  | } | ||||||
|  | .van-button--round { | ||||||
|  |   --van-button-border-radius: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .content-container { | ||||||
|  |   max-width: 1200px; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   padding: 0 20px 30px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tabs-wrapper { | ||||||
|  |   --van-tabs-nav-background: transparent; | ||||||
|  |   --van-tabs-line-height: 36px; | ||||||
|  |   --van-tabs-font-size: 16px; | ||||||
|  |   margin-bottom: 20px; | ||||||
|  | } | ||||||
|  | .van-tabs-nav { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border-radius: 8px; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | ||||||
|  |   overflow: hidden; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .tab-panel { | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   gap: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .chart-card { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border-radius: 10px; | ||||||
|  |   padding: 20px; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | ||||||
|  | } | ||||||
|  | .chart-container { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 300px; | ||||||
|  | } | ||||||
|  | .table-card { | ||||||
|  |   background-color: #fff; | ||||||
|  |   border-radius: 10px; | ||||||
|  |   padding: 20px; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | ||||||
|  | } | ||||||
|  | .table-container { | ||||||
|  |   overflow-x: auto; | ||||||
|  | } | ||||||
|  | .alarm-table { | ||||||
|  |   width: 100%; | ||||||
|  |   border-collapse: separate; | ||||||
|  |   border-spacing: 0; | ||||||
|  |   text-align: center; | ||||||
|  |   min-width: 500px; | ||||||
|  | } | ||||||
|  | .alarm-table th { | ||||||
|  |   background-color: #f8f9fa; | ||||||
|  |   color: #333; | ||||||
|  |   font-weight: 600; | ||||||
|  |   padding: 12px 8px; | ||||||
|  |   font-size: 14px; | ||||||
|  |   border-bottom: 1px solid #eee; | ||||||
|  | } | ||||||
|  | .alarm-table td { | ||||||
|  |   padding: 16px 8px; | ||||||
|  |   font-size: 14px; | ||||||
|  |   color: #fff; | ||||||
|  | } | ||||||
|  | .alarm-table tr:not(:last-child) td { | ||||||
|  |   border-bottom: 1px solid rgba(255, 255, 255, 0.2); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .level-emergency { | ||||||
|  |   background-color: #ff4d4f; | ||||||
|  | } | ||||||
|  | .level-important { | ||||||
|  |   background-color: #ff9933; | ||||||
|  | } | ||||||
|  | .level-secondary { | ||||||
|  |   background-color: #ffd700; | ||||||
|  |   color: #333; | ||||||
|  | } | ||||||
|  | .level-notice { | ||||||
|  |   background-color: #66ccff; | ||||||
|  | } | ||||||
|  | .level-name { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: center; | ||||||
|  |   gap: 8px; | ||||||
|  | } | ||||||
|  | .level-dot { | ||||||
|  |   width: 8px; | ||||||
|  |   height: 8px; | ||||||
|  |   border-radius: 50%; | ||||||
|  |   background-color: #fff; | ||||||
|  | } | ||||||
|  | .level-secondary .level-dot { | ||||||
|  |   background-color: #333; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .progress-bar { | ||||||
|  |   width: 80px; | ||||||
|  |   height: 8px; | ||||||
|  |   background-color: rgba(255, 255, 255, 0.2); | ||||||
|  |   border-radius: 4px; | ||||||
|  |   overflow: hidden; | ||||||
|  |   margin: 0 auto; | ||||||
|  | } | ||||||
|  | .progress-fill { | ||||||
|  |   height: 100%; | ||||||
|  |   background-color: #fff; | ||||||
|  |   transition: width 0.3s; | ||||||
|  | } | ||||||
|  | .rate-text { | ||||||
|  |   font-size: 12px; | ||||||
|  |   margin-top: 4px; | ||||||
|  |   display: block; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media (max-width: 768px) { | ||||||
|  |   .header-inner { | ||||||
|  |     flex-direction: column; | ||||||
|  |     align-items: flex-start; | ||||||
|  |     gap: 10px; | ||||||
|  |   } | ||||||
|  |   .chart-container { | ||||||
|  |     height: 220px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										279
									
								
								src/views/alarm/alarmList.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								src/views/alarm/alarmList.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,279 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="alarm-list-page"> | ||||||
|  |     <!-- 顶部导航栏 --> | ||||||
|  |     <header class="header"> | ||||||
|  |       <div class="header-inner"> | ||||||
|  |         <h1 class="page-title">实时警报</h1> | ||||||
|  |       </div> | ||||||
|  |     </header> | ||||||
|  |     <div class="vant-buttons-top"> | ||||||
|  |       <van-button type="primary" @click="handleSourceDistribution" round | ||||||
|  |         >来源分布</van-button | ||||||
|  |       > | ||||||
|  |       <van-button type="success" @click="handleAlarmLevel" round | ||||||
|  |         >告警级别统计</van-button | ||||||
|  |       > | ||||||
|  |       <van-button type="warning" @click="handleAlarmList" round | ||||||
|  |         >告警列表</van-button | ||||||
|  |       > | ||||||
|  |     </div> | ||||||
|  |     <!-- 告警列表 --> | ||||||
|  |     <div | ||||||
|  |       class="alarm-item" | ||||||
|  |       v-for="(item, idx) in alarmList" | ||||||
|  |       :key="idx" | ||||||
|  |       @click="goToDetail(item)" | ||||||
|  |     > | ||||||
|  |       <div class="alarm-header"> | ||||||
|  |         <h3 class="alarm-name">{{ item.name }}</h3> | ||||||
|  |         <div class="level-tags"> | ||||||
|  |           <van-tag | ||||||
|  |             size="small" | ||||||
|  |             v-for="(tag, tIndex) in item.levels" | ||||||
|  |             :key="tIndex" | ||||||
|  |             :type="getTagType(tag)" | ||||||
|  |           > | ||||||
|  |             {{ tag }} | ||||||
|  |           </van-tag> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |       <div class="alarm-meta"> | ||||||
|  |         <p><span>OID:</span> {{ item.oid }}</p> | ||||||
|  |         <p><span>告警时间:</span> {{ item.alarmTime }}</p> | ||||||
|  |         <p><span>类型:</span> {{ item.type }}</p> | ||||||
|  |         <p><span>分类:</span> {{ item.category }}</p> | ||||||
|  |       </div> | ||||||
|  |       <div class="alarm-desc"> | ||||||
|  |         <p><span>告警原因:</span> {{ item.reason }}</p> | ||||||
|  |         <p><span>修复建议:</span> {{ item.suggestion }}</p> | ||||||
|  |         <p class="source"><span>来源:</span> {{ item.source }}</p> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { ref, computed } from "vue"; | ||||||
|  | import { useRouter } from "vue-router"; | ||||||
|  |  | ||||||
|  | const router = useRouter(); | ||||||
|  | const activeTab = ref(0); | ||||||
|  | const alarmList = ref([ | ||||||
|  |   { | ||||||
|  |     name: "ASW-201-DE.AM798", | ||||||
|  |     oid: "1.3.6.1.4.1.2550.6.1.2.6.6.9", | ||||||
|  |     alarmTime: "2022-08-31 19:01:03", | ||||||
|  |     type: "阿里云", | ||||||
|  |     category: "网站性能告警", | ||||||
|  |     reason: "性能数据恢复正常", | ||||||
|  |     suggestion: "无需修复", | ||||||
|  |     source: "本系统", | ||||||
|  |     levels: ["重要", "紧急"], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "ASW-201-DE.AM797", | ||||||
|  |     oid: "1.3.6.1.4.1.2550.6.1.2.6.6.9", | ||||||
|  |     alarmTime: "2022-08-31 19:01:03", | ||||||
|  |     type: "阿里云", | ||||||
|  |     category: "网站性能告警", | ||||||
|  |     reason: "性能数据恢复正常", | ||||||
|  |     suggestion: "无需修复", | ||||||
|  |     source: "本系统", | ||||||
|  |     levels: ["次要", "警告"], | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: "ASW-201-DE.AM796", | ||||||
|  |     oid: "1.3.6.1.4.1.2550.6.1.2.6.6.9", | ||||||
|  |     alarmTime: "2022-08-31 19:01:03", | ||||||
|  |     type: "阿里云", | ||||||
|  |     category: "网站性能告警", | ||||||
|  |     reason: "性能数据恢复正常", | ||||||
|  |     suggestion: "无需修复", | ||||||
|  |     source: "本系统", | ||||||
|  |     levels: ["通知"], | ||||||
|  |   }, | ||||||
|  | ]); | ||||||
|  |  | ||||||
|  | // 模拟多标签 | ||||||
|  | const tabs = computed(() => [ | ||||||
|  |   { title: "来源分布", content: "来源分布内容" }, | ||||||
|  |   { title: "告警级别统计", content: "告警级别统计内容" }, | ||||||
|  |   { title: "告警列表", content: "告警列表内容" }, | ||||||
|  | ]); | ||||||
|  |  | ||||||
|  | // 按钮跳转逻辑 | ||||||
|  | const handleSourceDistribution = () => router.push("/alarm"); | ||||||
|  | const handleAlarmLevel = () => router.push({ name: "AlarmLevel" }); | ||||||
|  | const handleAlarmList = () => router.push({ name: "AlarmList" }); | ||||||
|  |  | ||||||
|  | // 标签切换 | ||||||
|  | const handleTabChange = (index) => console.log("切换到标签:", index); | ||||||
|  |  | ||||||
|  | // 跳转详情 | ||||||
|  | const goToDetail = (item) => { | ||||||
|  |   router.push({ | ||||||
|  |     name: "AlarmDetail", | ||||||
|  |     params: { item: JSON.stringify(item) }, | ||||||
|  |   }); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Tag 类型映射 | ||||||
|  | const getTagType = (tag) => { | ||||||
|  |   switch (tag) { | ||||||
|  |     case "紧急": | ||||||
|  |       return "danger"; | ||||||
|  |     case "重要": | ||||||
|  |       return "warning"; | ||||||
|  |     case "次要": | ||||||
|  |       return "info"; | ||||||
|  |     case "警告": | ||||||
|  |       return "primary"; | ||||||
|  |     case "通知": | ||||||
|  |       return "success"; | ||||||
|  |     default: | ||||||
|  |       return "default"; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | /* 全局布局 */ | ||||||
|  | .alarm-list-page { | ||||||
|  |   min-height: 100vh; | ||||||
|  |   background: #f5f7fa; | ||||||
|  |   font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, | ||||||
|  |     Arial, sans-serif; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 顶部导航 */ | ||||||
|  | .header { | ||||||
|  |   background: #2f4050; | ||||||
|  |   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); | ||||||
|  | } | ||||||
|  | .header-inner { | ||||||
|  |   display: flex; | ||||||
|  |   align-items: center; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   padding: 12px 20px; | ||||||
|  |   max-width: 1200px; | ||||||
|  |   margin: 0 auto; | ||||||
|  | } | ||||||
|  | .page-title { | ||||||
|  |   font-size: 18px; | ||||||
|  |   color: #fff; | ||||||
|  |   font-weight: 600; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | .header-actions { | ||||||
|  |   display: flex; | ||||||
|  |   gap: 8px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .vant-buttons-top { | ||||||
|  |   display: flex; | ||||||
|  |   gap: 12px; | ||||||
|  |   flex-wrap: wrap; | ||||||
|  |   justify-content: center; | ||||||
|  |   padding: 12px 15px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .van-button--round { | ||||||
|  |   --van-button-border-radius: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Tabs 容器 */ | ||||||
|  | .tabs-wrapper { | ||||||
|  |   --van-tabs-line-height: 36px; | ||||||
|  |   --van-tabs-font-size: 16px; | ||||||
|  |   max-width: 1200px; | ||||||
|  |   margin: 20px auto; | ||||||
|  |   padding: 0 20px; | ||||||
|  | } | ||||||
|  | .van-tabs-nav { | ||||||
|  |   background: #fff; | ||||||
|  |   border-radius: 8px; | ||||||
|  |   overflow: hidden; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | ||||||
|  | } | ||||||
|  | .van-tab { | ||||||
|  |   color: #333; | ||||||
|  | } | ||||||
|  | .van-tab--active { | ||||||
|  |   color: #1890ff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 内容区 */ | ||||||
|  | .tab-content { | ||||||
|  |   background: #fff; | ||||||
|  |   border-radius: 8px; | ||||||
|  |   padding: 20px; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); | ||||||
|  |   margin-top: 16px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 告警项卡片 */ | ||||||
|  | .alarm-item { | ||||||
|  |   background: #fff; | ||||||
|  |   border-radius: 8px; | ||||||
|  |   padding: 16px; | ||||||
|  |   margin-bottom: 16px; | ||||||
|  |   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03); | ||||||
|  |   transition: all 0.3s ease; | ||||||
|  |   cursor: pointer; | ||||||
|  | } | ||||||
|  | .alarm-item:hover { | ||||||
|  |   transform: translateY(-3px); | ||||||
|  |   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 告警项头部 */ | ||||||
|  | .alarm-header { | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   align-items: center; | ||||||
|  |   margin-bottom: 12px; | ||||||
|  | } | ||||||
|  | .alarm-name { | ||||||
|  |   font-size: 16px; | ||||||
|  |   font-weight: 600; | ||||||
|  |   color: #333; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
|  | .level-tags { | ||||||
|  |   display: flex; | ||||||
|  |   gap: 6px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 元信息区 */ | ||||||
|  | .alarm-meta { | ||||||
|  |   color: #666; | ||||||
|  |   margin-bottom: 12px; | ||||||
|  |   line-height: 1.6; | ||||||
|  | } | ||||||
|  | .alarm-meta span { | ||||||
|  |   color: #333; | ||||||
|  |   font-weight: 500; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 描述区 */ | ||||||
|  | .alarm-desc { | ||||||
|  |   color: #666; | ||||||
|  |   line-height: 1.6; | ||||||
|  | } | ||||||
|  | .alarm-desc span { | ||||||
|  |   color: #333; | ||||||
|  |   font-weight: 500; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 响应式适配 */ | ||||||
|  | @media (max-width: 768px) { | ||||||
|  |   .header-inner { | ||||||
|  |     flex-direction: column; | ||||||
|  |     align-items: flex-start; | ||||||
|  |     gap: 8px; | ||||||
|  |   } | ||||||
|  |   .action-buttons { | ||||||
|  |     flex-direction: column; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @ -1,2 +1,205 @@ | |||||||
| <template>home</template> | <template> | ||||||
| <script setup></script> |   <div class="image-selector-container"> | ||||||
|  |     <!-- 图片展示区域 - 占满整个页面 --> | ||||||
|  |     <div class="image-display"> | ||||||
|  |       <transition name="fade"> | ||||||
|  |         <img | ||||||
|  |           src="/assets/img.jpg" | ||||||
|  |           class="selected-image" | ||||||
|  |           key="selected-image" | ||||||
|  |           alt="选中的图片" | ||||||
|  |         /> | ||||||
|  |       </transition> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup> | ||||||
|  | import { ref, onUnmounted } from "vue"; | ||||||
|  |  | ||||||
|  | // 图片列表和状态管理 | ||||||
|  | const images = ref([1, 2, 3, 4, 5]); // 模拟图片列表 | ||||||
|  | const selectedIndex = ref(0); | ||||||
|  | const isOpen = ref(false); | ||||||
|  |  | ||||||
|  | // 选择图片 | ||||||
|  | const selectImage = (index) => { | ||||||
|  |   selectedIndex.value = index; | ||||||
|  |   isOpen.value = false; | ||||||
|  |   // 实际项目中这里会更新图片源 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 点击外部关闭下拉框 | ||||||
|  | const handleClickOutside = (event) => { | ||||||
|  |   const selectorContainer = document.querySelector(".selector-container"); | ||||||
|  |   if (selectorContainer && !selectorContainer.contains(event.target)) { | ||||||
|  |     isOpen.value = false; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 监听点击事件,实现点击外部关闭下拉框 | ||||||
|  | document.addEventListener("click", handleClickOutside); | ||||||
|  |  | ||||||
|  | // 组件卸载时移除事件监听 | ||||||
|  | onUnmounted(() => { | ||||||
|  |   document.removeEventListener("click", handleClickOutside); | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped> | ||||||
|  | /* 基础样式重置 */ | ||||||
|  | * { | ||||||
|  |   margin: 0; | ||||||
|  |   padding: 0; | ||||||
|  |   box-sizing: border-box; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | html, | ||||||
|  | body, | ||||||
|  | .image-selector-container { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   overflow: hidden; /* 防止页面滚动 */ | ||||||
|  |   font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 图片展示区域 - 占满整个页面 */ | ||||||
|  | .image-display { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   position: relative; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .selected-image { | ||||||
|  |   height: calc(100vh - 50px - 10px); | ||||||
|  |   display: block; | ||||||
|  |   object-fit: cover; /* 保持比例并覆盖整个区域 */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 选择器容器 - 固定在底部 */ | ||||||
|  | .selector-container { | ||||||
|  |   position: fixed; | ||||||
|  |   bottom: 0; | ||||||
|  |   left: 0; | ||||||
|  |   right: 0; | ||||||
|  |   padding: 20px; | ||||||
|  |   background: linear-gradient(transparent, rgba(0, 0, 0, 0.5)); | ||||||
|  |   z-index: 10; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .select-button { | ||||||
|  |   width: 100%; | ||||||
|  |   padding: 14px 16px; | ||||||
|  |   background-color: rgba(255, 255, 255, 0.9); | ||||||
|  |   border: none; | ||||||
|  |   border-radius: 8px; | ||||||
|  |   font-size: 16px; | ||||||
|  |   font-weight: 500; | ||||||
|  |   color: #333333; | ||||||
|  |   display: flex; | ||||||
|  |   justify-content: space-between; | ||||||
|  |   align-items: center; | ||||||
|  |   cursor: pointer; | ||||||
|  |   box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); | ||||||
|  |   transition: all 0.2s ease; | ||||||
|  |   backdrop-filter: blur(4px); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .select-button:hover, | ||||||
|  | .select-button:focus { | ||||||
|  |   background-color: #ffffff; | ||||||
|  |   outline: none; | ||||||
|  |   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .icon { | ||||||
|  |   transition: transform 0.2s ease; | ||||||
|  |   font-size: 14px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .icon.rotate { | ||||||
|  |   transform: rotate(180deg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .dropdown-list { | ||||||
|  |   position: absolute; | ||||||
|  |   bottom: 100%; | ||||||
|  |   left: 20px; | ||||||
|  |   right: 20px; | ||||||
|  |   margin: 0 0 8px; | ||||||
|  |   padding: 8px 0; | ||||||
|  |   list-style: none; | ||||||
|  |   background-color: rgba(255, 255, 255, 0.95); | ||||||
|  |   border-radius: 8px; | ||||||
|  |   box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15); | ||||||
|  |   z-index: 10; | ||||||
|  |   max-height: 240px; | ||||||
|  |   overflow-y: auto; | ||||||
|  |   -webkit-overflow-scrolling: touch; | ||||||
|  |   backdrop-filter: blur(4px); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .dropdown-item { | ||||||
|  |   padding: 12px 16px; | ||||||
|  |   font-size: 16px; | ||||||
|  |   color: #333333; | ||||||
|  |   cursor: pointer; | ||||||
|  |   transition: background-color 0.15s ease; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .dropdown-item:hover, | ||||||
|  | .dropdown-item:focus { | ||||||
|  |   background-color: #f5f9ff; | ||||||
|  |   color: #007bff; | ||||||
|  |   outline: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .dropdown-item.selected { | ||||||
|  |   background-color: #e6f0ff; | ||||||
|  |   color: #007bff; | ||||||
|  |   font-weight: 500; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 动画效果 */ | ||||||
|  | .fade-enter-active, | ||||||
|  | .fade-leave-active { | ||||||
|  |   transition: opacity 0.3s ease; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .fade-enter-from, | ||||||
|  | .fade-leave-to { | ||||||
|  |   opacity: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .slide-up-enter-active, | ||||||
|  | .slide-up-leave-active { | ||||||
|  |   transition: transform 0.2s ease, opacity 0.2s ease; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .slide-up-enter-from { | ||||||
|  |   transform: translateY(8px); | ||||||
|  |   opacity: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .slide-up-leave-to { | ||||||
|  |   transform: translateY(-8px); | ||||||
|  |   opacity: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* 移动端适配 */ | ||||||
|  | @media (max-width: 768px) { | ||||||
|  |   .selector-container { | ||||||
|  |     padding: 16px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .select-button { | ||||||
|  |     padding: 12px 14px; | ||||||
|  |     font-size: 15px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .dropdown-item { | ||||||
|  |     padding: 11px 14px; | ||||||
|  |     font-size: 15px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 xiaozhou
					xiaozhou