index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="80px">
  4. <el-form-item label="告警类型" prop="alarmType">
  5. <el-select v-model="queryParams.alarmType" placeholder="请选择告警类型" clearable class="search-select">
  6. <el-option
  7. v-for="dict in alarm_type"
  8. :key="dict.value"
  9. :label="dict.label"
  10. :value="dict.value"
  11. />
  12. </el-select>
  13. </el-form-item>
  14. <el-form-item label="告警级别" prop="alarmLevel">
  15. <el-select v-model="queryParams.alarmLevel" placeholder="请选择告警级别" clearable class="search-select">
  16. <el-option
  17. v-for="dict in alarm_level"
  18. :key="dict.value"
  19. :label="dict.label"
  20. :value="dict.value"
  21. />
  22. </el-select>
  23. </el-form-item>
  24. <el-form-item label="处理状态" prop="handleStatus">
  25. <el-select v-model="queryParams.handleStatus" placeholder="请选择处理状态" clearable class="search-select">
  26. <el-option
  27. v-for="dict in alarm_handle_status"
  28. :key="dict.value"
  29. :label="dict.label"
  30. :value="dict.value"
  31. />
  32. </el-select>
  33. </el-form-item>
  34. <el-form-item label="告警时间">
  35. <el-date-picker
  36. v-model="daterangeAlarmTime"
  37. value-format="YYYY-MM-DD"
  38. type="daterange"
  39. range-separator="-"
  40. start-placeholder="开始日期"
  41. end-placeholder="结束日期"
  42. class="search-date"
  43. />
  44. </el-form-item>
  45. <el-form-item>
  46. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  47. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  48. </el-form-item>
  49. </el-form>
  50. <el-row :gutter="10" class="mb8">
  51. <el-col :span="1.5">
  52. <el-button
  53. type="warning"
  54. plain
  55. icon="Download"
  56. @click="handleExport"
  57. v-hasPermi="['base:alarmLog:export']"
  58. >导出</el-button>
  59. </el-col>
  60. <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
  61. </el-row>
  62. <el-table v-loading="loading" :data="alarmLogList">
  63. <el-table-column label="告警时间" align="center" prop="alarmTime" width="170">
  64. <template #default="scope">
  65. <span>{{ parseTime(scope.row.alarmTime) }}</span>
  66. </template>
  67. </el-table-column>
  68. <el-table-column label="告警类型" align="center" prop="alarmType" width="110">
  69. <template #default="scope">
  70. <dict-tag :options="alarm_type" :value="scope.row.alarmType"/>
  71. </template>
  72. </el-table-column>
  73. <el-table-column label="告警级别" align="center" prop="alarmLevel" width="100">
  74. <template #default="scope">
  75. <dict-tag :options="alarm_level" :value="scope.row.alarmLevel"/>
  76. </template>
  77. </el-table-column>
  78. <el-table-column label="告警标题" align="left" prop="alarmTitle" min-width="220" show-overflow-tooltip>
  79. <template #default="scope">
  80. <span>{{ scope.row.alarmTitle || formatSummary(scope.row.description) }}</span>
  81. </template>
  82. </el-table-column>
  83. <el-table-column label="来源位置" align="center" prop="sourcePosition" width="160" show-overflow-tooltip />
  84. <el-table-column label="处理状态" align="center" prop="handleStatus" width="110">
  85. <template #default="scope">
  86. <dict-tag :options="alarm_handle_status" :value="scope.row.handleStatus"/>
  87. </template>
  88. </el-table-column>
  89. <el-table-column label="操作" align="center" fixed="right" width="220">
  90. <template #default="scope">
  91. <el-button link type="primary" icon="View" @click="handleDetail(scope.row)">详情</el-button>
  92. <el-button
  93. v-if="scope.row.handleStatus === '1'"
  94. link
  95. type="success"
  96. icon="CircleCheck"
  97. @click="openHandleDialog(scope.row, 'confirm')"
  98. >确认</el-button>
  99. <el-button
  100. v-if="scope.row.handleStatus === '1'"
  101. link
  102. type="warning"
  103. icon="CircleClose"
  104. @click="openHandleDialog(scope.row, 'ignore')"
  105. >忽略</el-button>
  106. </template>
  107. </el-table-column>
  108. </el-table>
  109. <pagination
  110. v-show="total>0"
  111. :total="total"
  112. v-model:page="queryParams.pageNum"
  113. v-model:limit="queryParams.pageSize"
  114. @pagination="getList"
  115. />
  116. <el-dialog title="安防告警详情" v-model="detailOpen" width="820px" append-to-body>
  117. <el-descriptions :column="2" border>
  118. <el-descriptions-item label="告警时间">{{ parseTime(detail.alarmTime) || '-' }}</el-descriptions-item>
  119. <el-descriptions-item label="告警类型">
  120. <dict-tag :options="alarm_type" :value="detail.alarmType"/>
  121. </el-descriptions-item>
  122. <el-descriptions-item label="告警级别">
  123. <dict-tag :options="alarm_level" :value="detail.alarmLevel"/>
  124. </el-descriptions-item>
  125. <el-descriptions-item label="告警来源">
  126. <dict-tag :options="alarm_source" :value="detail.alarmSource"/>
  127. </el-descriptions-item>
  128. <el-descriptions-item label="来源位置">{{ detail.sourcePosition || '-' }}</el-descriptions-item>
  129. <el-descriptions-item label="处理状态">
  130. <dict-tag :options="alarm_handle_status" :value="detail.handleStatus"/>
  131. </el-descriptions-item>
  132. <el-descriptions-item label="告警标题" :span="2">{{ detail.alarmTitle || '-' }}</el-descriptions-item>
  133. <el-descriptions-item label="告警描述" :span="2">{{ detail.description || '-' }}</el-descriptions-item>
  134. <el-descriptions-item label="处理人">{{ detail.handleBy || '-' }}</el-descriptions-item>
  135. <el-descriptions-item label="处理时间">{{ parseTime(detail.handleTime) || '-' }}</el-descriptions-item>
  136. <el-descriptions-item label="处理备注" :span="2">{{ detail.handleRemark || '-' }}</el-descriptions-item>
  137. <el-descriptions-item label="备注" :span="2">{{ detail.remark || '-' }}</el-descriptions-item>
  138. </el-descriptions>
  139. <div class="snapshot-block">
  140. <div class="snapshot-title">抓拍图</div>
  141. <el-image
  142. v-if="detail.snapshotUrl"
  143. class="snapshot-image"
  144. :src="detail.snapshotUrl"
  145. :preview-src-list="[detail.snapshotUrl]"
  146. fit="cover"
  147. preview-teleported
  148. />
  149. <div v-else class="snapshot-empty">暂无抓拍图</div>
  150. </div>
  151. <template #footer>
  152. <div class="dialog-footer">
  153. <el-button @click="detailOpen = false">关 闭</el-button>
  154. </div>
  155. </template>
  156. </el-dialog>
  157. <el-dialog :title="handleDialogTitle" v-model="handleOpen" width="520px" append-to-body>
  158. <el-form ref="handleRef" :model="handleForm" label-width="90px">
  159. <el-form-item label="告警标题">
  160. <span>{{ handleForm.alarmTitle || '-' }}</span>
  161. </el-form-item>
  162. <el-form-item label="处理备注" prop="handleRemark">
  163. <el-input
  164. v-model="handleForm.handleRemark"
  165. type="textarea"
  166. :rows="4"
  167. maxlength="500"
  168. show-word-limit
  169. placeholder="请输入处理备注"
  170. />
  171. </el-form-item>
  172. </el-form>
  173. <template #footer>
  174. <div class="dialog-footer">
  175. <el-button type="primary" @click="submitHandle">确 定</el-button>
  176. <el-button @click="handleOpen = false">取 消</el-button>
  177. </div>
  178. </template>
  179. </el-dialog>
  180. </div>
  181. </template>
  182. <script setup name="AlarmLog">
  183. import { listAlarmLog, getAlarmLog, confirmAlarmLog, ignoreAlarmLog } from "@/api/base/alarmLog"
  184. const { proxy } = getCurrentInstance()
  185. const { alarm_type, alarm_level, alarm_source, alarm_handle_status } = useDict(
  186. 'alarm_type',
  187. 'alarm_level',
  188. 'alarm_source',
  189. 'alarm_handle_status'
  190. )
  191. const alarmLogList = ref([])
  192. const loading = ref(true)
  193. const showSearch = ref(true)
  194. const total = ref(0)
  195. const daterangeAlarmTime = ref([])
  196. const queryParams = ref({
  197. pageNum: 1,
  198. pageSize: 10,
  199. alarmType: undefined,
  200. alarmLevel: undefined,
  201. handleStatus: undefined
  202. })
  203. const detailOpen = ref(false)
  204. const detail = ref({})
  205. const handleOpen = ref(false)
  206. const handleType = ref("")
  207. const handleDialogTitle = ref("")
  208. const handleForm = reactive({
  209. id: null,
  210. alarmTitle: "",
  211. handleRemark: ""
  212. })
  213. /** 查询安防告警日志列表 */
  214. function getList() {
  215. loading.value = true
  216. const params = proxy.addDateRange(queryParams.value, daterangeAlarmTime.value, 'AlarmTime')
  217. listAlarmLog(params).then(response => {
  218. alarmLogList.value = response.rows || []
  219. total.value = response.total || 0
  220. }).finally(() => {
  221. loading.value = false
  222. })
  223. }
  224. /** 搜索按钮操作 */
  225. function handleQuery() {
  226. queryParams.value.pageNum = 1
  227. getList()
  228. }
  229. /** 重置按钮操作 */
  230. function resetQuery() {
  231. daterangeAlarmTime.value = []
  232. proxy.resetForm("queryRef")
  233. handleQuery()
  234. }
  235. /** 详情按钮操作 */
  236. function handleDetail(row) {
  237. getAlarmLog(row.id).then(response => {
  238. detail.value = response.data || row
  239. detailOpen.value = true
  240. })
  241. }
  242. /** 摘要兜底方法 */
  243. function formatSummary(content) {
  244. if (!content) return '-'
  245. return content.length > 60 ? content.substring(0, 60) + '...' : content
  246. }
  247. /** 打开确认/忽略弹窗 */
  248. function openHandleDialog(row, type) {
  249. handleType.value = type
  250. handleDialogTitle.value = type === 'confirm' ? '确认告警' : '忽略告警'
  251. handleForm.id = row.id
  252. handleForm.alarmTitle = row.alarmTitle || formatSummary(row.description)
  253. handleForm.handleRemark = ''
  254. handleOpen.value = true
  255. }
  256. /** 提交确认/忽略 */
  257. function submitHandle() {
  258. if (!handleForm.id) return
  259. const request = handleType.value === 'confirm' ? confirmAlarmLog : ignoreAlarmLog
  260. const successMsg = handleType.value === 'confirm' ? '告警已确认' : '告警已忽略'
  261. request(handleForm.id, {
  262. handleRemark: handleForm.handleRemark
  263. }).then(() => {
  264. proxy.$modal.msgSuccess(successMsg)
  265. handleOpen.value = false
  266. getList()
  267. })
  268. }
  269. /** 导出按钮操作 */
  270. function handleExport() {
  271. proxy.$modal.confirm('确认导出当前查询条件下的安防告警日志数据吗?').then(() => {
  272. const params = proxy.addDateRange(queryParams.value, daterangeAlarmTime.value, 'AlarmTime')
  273. proxy.download('base/alarmLog/export', {
  274. ...params
  275. }, `安防告警日志_${new Date().getTime()}.xlsx`)
  276. }).catch(() => {})
  277. }
  278. getList()
  279. </script>
  280. <style scoped>
  281. .search-select {
  282. width: 220px;
  283. }
  284. .search-date {
  285. width: 260px;
  286. }
  287. .snapshot-block {
  288. margin-top: 16px;
  289. }
  290. .snapshot-title {
  291. margin-bottom: 8px;
  292. font-weight: 600;
  293. color: #303133;
  294. }
  295. .snapshot-image {
  296. width: 220px;
  297. height: 140px;
  298. border-radius: 6px;
  299. border: 1px solid #ebeef5;
  300. background: #f8fafc;
  301. }
  302. .snapshot-empty {
  303. width: 220px;
  304. height: 140px;
  305. display: flex;
  306. align-items: center;
  307. justify-content: center;
  308. color: #909399;
  309. background: #f8fafc;
  310. border: 1px dashed #dcdfe6;
  311. border-radius: 6px;
  312. font-size: 13px;
  313. }
  314. </style>