AlarmDetail.vue 5.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. <template>
  2. <el-drawer v-model="visible" title="报警详情" size="min(680px, 100vw)" append-to-body destroy-on-close class="alarm-detail">
  3. <div v-loading="loading">
  4. <el-alert v-if="error" :title="error" type="error" :closable="false" show-icon />
  5. <template v-if="alarm">
  6. <AlarmSummary :alarm="alarm" :level-tone="levelTone(alarm.alarmLevel)" />
  7. <dl class="alarm-state-grid">
  8. <div class="alarm-state-item"><dt>监测状态</dt><dd><el-tag size="small" effect="light" :type="monitorStatusTone(alarm.alarmStatus)">{{ alarm.monitorStatusName || '未知' }}</el-tag></dd></div>
  9. <div class="alarm-state-item"><dt>核实结果</dt><dd><el-tag size="small" effect="light" :type="auditStatusTone(alarm.auditStatus)">{{ alarmAuditText(alarm) }}</el-tag></dd></div>
  10. <div class="alarm-state-item"><dt>处置状态</dt><dd><el-tag size="small" effect="light" :type="alarm.workOrderId != null && alarm.orderStatus != null ? orderStatusType(alarm.orderStatus) : 'info'">{{ alarm.orderStatusName || '未转工单' }}</el-tag></dd></div>
  11. </dl>
  12. <el-descriptions :column="2" border><el-descriptions-item label="关联工单">{{ alarm.orderNo || '—' }}</el-descriptions-item><el-descriptions-item label="责任人">{{ alarm.receiveUserName || '未分配' }}</el-descriptions-item><el-descriptions-item label="完成期限" :span="2">{{ alarm.planFinishTime || '—' }}</el-descriptions-item></el-descriptions>
  13. <AlarmHistory :records="timeline" :loading="loading" :error="error" :base-url="baseUrl" />
  14. </template>
  15. </div>
  16. <template #footer><el-button v-if="alarm && hasActualValue(alarm) && menu('drainageAlarmAnalysis')" @click="navigate('drainageAlarmAnalysis', { alarmId: alarm.id })">进入研判</el-button><el-button v-if="alarm?.alarmStatus === 0 && alarm?.auditStatus === 0 && menu('alarmAudit')" type="primary" @click="navigate('alarmAudit', { alarmId: alarm.id })">前往核实</el-button><el-button v-if="waterShortcuts && alarm?.workOrderId" type="primary" @click="goWaterWorkOrder">前往设备维修派单</el-button><el-button @click="visible = false">关闭</el-button></template>
  17. </el-drawer>
  18. </template>
  19. <script setup>
  20. import { ref, watch, onBeforeUnmount } from 'vue'
  21. import { useRouter } from 'vue-router'
  22. import { ElMessage } from 'element-plus'
  23. import { getAlarm, getAlarmTimeline } from '@/api/alarm'
  24. import { alarmAuditText } from '@/utils/alarmLabels'
  25. import AlarmSummary from './AlarmSummary.vue'
  26. import AlarmHistory from './AlarmHistory.vue'
  27. import { auditStatusTone, levelTone, monitorStatusTone } from './common/js/alarmOperationsModel'
  28. import { orderStatusType } from '../pipeNetwork/ops/workOrder/js/workOrderDict'
  29. import { hasActualValue } from './common/js/alarmReviewModel'
  30. import { useAlarmScope } from './common/js/useAlarmScope'
  31. const { scope, scopeKey, menu, navigate } = useAlarmScope()
  32. const { waterShortcuts = false } = defineProps({ waterShortcuts: Boolean })
  33. const router = useRouter()
  34. const emit = defineEmits(['visible-change'])
  35. const visible = ref(false), loading = ref(false), alarm = ref(null), timeline = ref([]), error = ref('')
  36. const baseUrl = import.meta.env.VITE_APP_BASE_API
  37. let generation = 0
  38. function goWaterWorkOrder() {
  39. if (!alarm.value?.orderNo) return ElMessage.warning('关联工单编号未返回')
  40. const target = router.getRoutes().find(route => route.path.endsWith('/device/maintain') && route.path.includes('/waterSupply/'))
  41. if (!target) return ElMessage.warning('当前账号未配置设备维修派单菜单')
  42. return router.push({ path: target.path, query: { keyword: alarm.value.orderNo || '' } })
  43. }
  44. async function open(id) {
  45. const token = ++generation
  46. visible.value = true; loading.value = true; error.value = ''; alarm.value = null; timeline.value = []
  47. try {
  48. const [detail, logs] = await Promise.all([getAlarm(id, { ...scope.value }), getAlarmTimeline(id, { ...scope.value })])
  49. if (token !== generation) return
  50. alarm.value = detail.data; timeline.value = logs.data || []
  51. } catch (e) { if (token === generation) error.value = e.message || '详情加载失败' }
  52. finally { if (token === generation) loading.value = false }
  53. }
  54. watch(scopeKey, () => { ++generation; visible.value = false })
  55. watch(visible, value => { emit('visible-change', value); if (!value) ++generation })
  56. onBeforeUnmount(() => { ++generation })
  57. defineExpose({ open })
  58. </script>
  59. <style scoped>
  60. .alarm-state-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 20px; margin: 0 0 20px; }
  61. .alarm-state-item { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; min-width: 0; }
  62. .alarm-state-item dt { margin: 0; color: #606266; font-size: 13px; font-weight: 400; line-height: 22px; white-space: nowrap; }
  63. .alarm-state-item dd { margin: 0; min-width: 0; font-weight: 400; }
  64. .alarm-state-item :deep(.el-tag) { height: 22px; padding: 0 7px; border: 0; border-radius: 3px; font-size: 12px; font-weight: 400; }
  65. @media (max-width: 560px) { .alarm-state-grid { grid-template-columns: 1fr; } }
  66. </style>