|
|
@@ -8,7 +8,7 @@
|
|
|
<template #header>
|
|
|
<div class="panel-title">应急事件列表</div>
|
|
|
</template>
|
|
|
- <div class="event-list">
|
|
|
+ <div class="event-list" v-if="events.length">
|
|
|
<div
|
|
|
v-for="event in events"
|
|
|
:key="event.id"
|
|
|
@@ -22,12 +22,13 @@
|
|
|
</div>
|
|
|
<div class="event-meta">{{ event.location }}</div>
|
|
|
<div class="event-status">
|
|
|
- <el-tag v-if="event.evaluated" type="success" size="small">已评估</el-tag>
|
|
|
+ <el-tag v-if="evaluatedIds.has(event.id)" type="success" size="small">已评估</el-tag>
|
|
|
<el-tag v-else type="info" size="small">待评估</el-tag>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <el-button type="primary" plain style="width: 100%; margin-top: 12px" @click="handleRefresh">刷新事件列表</el-button>
|
|
|
+ <el-empty v-else description="暂无已解除/已关闭事件" :image-size="80" />
|
|
|
+ <el-button type="primary" plain :loading="loading" style="width: 100%; margin-top: 12px" @click="handleRefresh">刷新事件列表</el-button>
|
|
|
</el-card>
|
|
|
|
|
|
<el-card shadow="hover" class="event-detail-card" v-if="currentEvent">
|
|
|
@@ -59,11 +60,11 @@
|
|
|
|
|
|
<!-- 右侧评估面板 -->
|
|
|
<div class="right-panel">
|
|
|
- <el-card shadow="hover">
|
|
|
+ <el-card shadow="hover" v-if="currentEvent">
|
|
|
<template #header>
|
|
|
<div class="panel-title-row">
|
|
|
<span class="panel-title">应急事件事后评估</span>
|
|
|
- <el-tag v-if="currentEvent?.evaluated" type="success" effect="dark" size="large">已评估</el-tag>
|
|
|
+ <el-tag v-if="evaluatedIds.has(currentEvent.id)" type="success" effect="dark" size="large">已评估</el-tag>
|
|
|
<el-tag v-else type="warning" effect="dark" size="large">未评估</el-tag>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -111,50 +112,25 @@
|
|
|
<el-button @click="resetEvaluation">重新评估</el-button>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
+ <el-empty v-else description="暂无已解除/已关闭事件,请先完成事件处置后进行评估" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, reactive, computed, watch } from 'vue'
|
|
|
+import { ref, reactive, computed, watch, onMounted } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
-
|
|
|
-// Mock 事件数据
|
|
|
-const events = ref([
|
|
|
- {
|
|
|
- id: 1, name: '化工园区氯气泄漏事故', time: '2025-05-15 08:30', location: '华东化工园区',
|
|
|
- level: '重大', type: '事故灾难', desc: '储罐区氯气阀门失效导致泄漏,影响周边2km范围,已启动二级应急响应。',
|
|
|
- evaluated: false, caseAdded: false,
|
|
|
- assessments: { org: { score: 0, comment: '' }, cap: { score: 0, comment: '' }, effect: { score: 0, comment: '' }, loss: { score: 0, comment: '' } }
|
|
|
- },
|
|
|
- {
|
|
|
- id: 2, name: '特大暴雨洪涝灾害', time: '2025-07-20 14:00', location: '华南沿海城市',
|
|
|
- level: '特别重大', type: '自然灾害', desc: '连续强降雨引发城市内涝,受灾人口超10万,多处基础设施受损。',
|
|
|
- evaluated: false, caseAdded: false,
|
|
|
- assessments: { org: { score: 0, comment: '' }, cap: { score: 0, comment: '' }, effect: { score: 0, comment: '' }, loss: { score: 0, comment: '' } }
|
|
|
- },
|
|
|
- {
|
|
|
- id: 3, name: '地铁施工坍塌事故', time: '2025-03-10 10:15', location: '城西地铁5号线',
|
|
|
- level: '较大', type: '事故灾难', desc: '隧道施工段发生坍塌,3人被困,已组织现场救援。',
|
|
|
- evaluated: false, caseAdded: false,
|
|
|
- assessments: { org: { score: 0, comment: '' }, cap: { score: 0, comment: '' }, effect: { score: 0, comment: '' }, loss: { score: 0, comment: '' } }
|
|
|
- },
|
|
|
- {
|
|
|
- id: 4, name: '太常片区供水管道爆裂', time: '2025-01-25 06:45', location: '太常片区主干道',
|
|
|
- level: '重大', type: '管网泄漏', desc: 'DN800供水主管爆裂,影响周边3万居民用水,已紧急调度供水车。',
|
|
|
- evaluated: true, caseAdded: true,
|
|
|
- assessments: { org: { score: 4, comment: '组织架构较为清晰,指挥层级明确' }, cap: { score: 3.5, comment: '响应速度较快,但资源调配稍显滞后' }, effect: { score: 4, comment: '现场处置得当,各部门协同配合良好' }, loss: { score: 3, comment: '经济损失较大,但人员伤亡控制较好' } }
|
|
|
- },
|
|
|
- {
|
|
|
- id: 5, name: '城南片区排水管网堵塞', time: '2025-04-08 16:30', location: '城南片区低洼路段',
|
|
|
- level: '一般', type: '管道堵塞', desc: '排水管网严重堵塞导致路面积水,影响交通通行。',
|
|
|
- evaluated: false, caseAdded: false,
|
|
|
- assessments: { org: { score: 0, comment: '' }, cap: { score: 0, comment: '' }, effect: { score: 0, comment: '' }, loss: { score: 0, comment: '' } }
|
|
|
- }
|
|
|
-])
|
|
|
-
|
|
|
-const currentEventId = ref(1)
|
|
|
+import { getEventPage, getEventAssess, listEventAssess, saveEventAssess, deleteEventAssess } from '@/api/dispatch'
|
|
|
+
|
|
|
+// 已解除/已关闭事件列表(可评估)
|
|
|
+const events = ref([])
|
|
|
+const loading = ref(false)
|
|
|
+const currentEventId = ref(null)
|
|
|
+// 已评估事件 id 集合
|
|
|
+const evaluatedIds = ref(new Set())
|
|
|
+// 当前事件评估记录(含案例库标记)
|
|
|
+const currentAssess = ref(null)
|
|
|
const assessments = reactive({
|
|
|
org: { score: 0, comment: '' },
|
|
|
cap: { score: 0, comment: '' },
|
|
|
@@ -164,26 +140,62 @@ const assessments = reactive({
|
|
|
|
|
|
// 当前选中事件
|
|
|
const currentEvent = computed(() => {
|
|
|
- return events.value.find(e => e.id === currentEventId.value) || events.value[0]
|
|
|
+ return events.value.find(e => e.id === currentEventId.value) || null
|
|
|
})
|
|
|
|
|
|
-// 加载评估数据到表单
|
|
|
-const loadAssessmentsToForm = () => {
|
|
|
+// 清空评估表单
|
|
|
+const resetAssessments = () => {
|
|
|
+ assessments.org.score = 0; assessments.org.comment = ''
|
|
|
+ assessments.cap.score = 0; assessments.cap.comment = ''
|
|
|
+ assessments.effect.score = 0; assessments.effect.comment = ''
|
|
|
+ assessments.loss.score = 0; assessments.loss.comment = ''
|
|
|
+}
|
|
|
+
|
|
|
+// 加载评估数据到表单(查询后端评估记录回显)
|
|
|
+const loadAssessmentsToForm = async () => {
|
|
|
+ resetAssessments()
|
|
|
const event = currentEvent.value
|
|
|
- if (event && event.evaluated && event.assessments) {
|
|
|
- assessments.org.score = event.assessments.org.score
|
|
|
- assessments.org.comment = event.assessments.org.comment
|
|
|
- assessments.cap.score = event.assessments.cap.score
|
|
|
- assessments.cap.comment = event.assessments.cap.comment
|
|
|
- assessments.effect.score = event.assessments.effect.score
|
|
|
- assessments.effect.comment = event.assessments.effect.comment
|
|
|
- assessments.loss.score = event.assessments.loss.score
|
|
|
- assessments.loss.comment = event.assessments.loss.comment
|
|
|
- } else {
|
|
|
- assessments.org.score = 0; assessments.org.comment = ''
|
|
|
- assessments.cap.score = 0; assessments.cap.comment = ''
|
|
|
- assessments.effect.score = 0; assessments.effect.comment = ''
|
|
|
- assessments.loss.score = 0; assessments.loss.comment = ''
|
|
|
+ currentAssess.value = null
|
|
|
+ if (!event) return
|
|
|
+ try {
|
|
|
+ const res = await getEventAssess(event.id)
|
|
|
+ currentAssess.value = res.data || null
|
|
|
+ if (currentAssess.value) {
|
|
|
+ assessments.org.score = Number(currentAssess.value.orgScore) || 0
|
|
|
+ assessments.org.comment = currentAssess.value.orgComment || ''
|
|
|
+ assessments.cap.score = Number(currentAssess.value.capScore) || 0
|
|
|
+ assessments.cap.comment = currentAssess.value.capComment || ''
|
|
|
+ assessments.effect.score = Number(currentAssess.value.effectScore) || 0
|
|
|
+ assessments.effect.comment = currentAssess.value.effectComment || ''
|
|
|
+ assessments.loss.score = Number(currentAssess.value.lossScore) || 0
|
|
|
+ assessments.loss.comment = currentAssess.value.lossComment || ''
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ currentAssess.value = null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 加载事件列表(已解除/已关闭)并批量标记已评估
|
|
|
+const loadEvents = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const res = await getEventPage(1, 200, { statusList: ['已解除', '已关闭'] })
|
|
|
+ const rows = res.data?.records || res.data?.rows || res.records || res.rows || []
|
|
|
+ events.value = (rows || []).map(e => ({
|
|
|
+ id: e.id, name: e.eventName, time: e.happenTime, location: e.location,
|
|
|
+ level: e.level, type: e.eventType, desc: e.description
|
|
|
+ }))
|
|
|
+ evaluatedIds.value = new Set()
|
|
|
+ if (events.value.length) {
|
|
|
+ const ass = await listEventAssess(events.value.map(e => e.id))
|
|
|
+ evaluatedIds.value = new Set((ass.data || []).map(a => a.relEventId))
|
|
|
+ }
|
|
|
+ if (!currentEventId.value || !events.value.some(e => e.id === currentEventId.value)) {
|
|
|
+ currentEventId.value = events.value.length ? events.value[0].id : null
|
|
|
+ }
|
|
|
+ await loadAssessmentsToForm()
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -199,9 +211,10 @@ const levelTagType = (level) => {
|
|
|
return map[level] || 'info'
|
|
|
}
|
|
|
|
|
|
-// 生成评估报告
|
|
|
-const generateReport = () => {
|
|
|
+// 生成评估报告(保存评估到后端 + 下载报告)
|
|
|
+const generateReport = async () => {
|
|
|
const event = currentEvent.value
|
|
|
+ if (!event) return
|
|
|
const scores = {
|
|
|
org: assessments.org.score,
|
|
|
cap: assessments.cap.score,
|
|
|
@@ -246,13 +259,21 @@ const generateReport = () => {
|
|
|
报告生成时间: ${new Date().toLocaleString()}
|
|
|
`.trim()
|
|
|
|
|
|
- // 保存到事件对象
|
|
|
- event.evaluated = true
|
|
|
- event.assessments = {
|
|
|
- org: { score: scores.org, comment: assessments.org.comment },
|
|
|
- cap: { score: scores.cap, comment: assessments.cap.comment },
|
|
|
- effect: { score: scores.effect, comment: assessments.effect.comment },
|
|
|
- loss: { score: scores.loss, comment: assessments.loss.comment }
|
|
|
+ // 保存评估到后端(自动计算综合分与等级)
|
|
|
+ try {
|
|
|
+ await saveEventAssess({
|
|
|
+ eventId: event.id,
|
|
|
+ orgScore: scores.org, orgComment: assessments.org.comment,
|
|
|
+ capScore: scores.cap, capComment: assessments.cap.comment,
|
|
|
+ effectScore: scores.effect, effectComment: assessments.effect.comment,
|
|
|
+ lossScore: scores.loss, lossComment: assessments.loss.comment,
|
|
|
+ caseFlag: currentAssess.value?.caseFlag || '0'
|
|
|
+ })
|
|
|
+ evaluatedIds.value.add(event.id)
|
|
|
+ currentAssess.value = { ...(currentAssess.value || {}), caseFlag: currentAssess.value?.caseFlag || '0' }
|
|
|
+ } catch (e) {
|
|
|
+ ElMessage.error('评估保存失败,请重试')
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
// 生成下载
|
|
|
@@ -264,51 +285,60 @@ const generateReport = () => {
|
|
|
link.click()
|
|
|
URL.revokeObjectURL(url)
|
|
|
|
|
|
- ElMessage.success('评估报告已生成并下载')
|
|
|
+ ElMessage.success('评估已保存,报告已生成并下载')
|
|
|
}
|
|
|
|
|
|
// 录入案例库
|
|
|
-const saveToCaseLibrary = () => {
|
|
|
+const saveToCaseLibrary = async () => {
|
|
|
const event = currentEvent.value
|
|
|
- if (!event.evaluated) {
|
|
|
+ if (!event) return
|
|
|
+ if (!evaluatedIds.value.has(event.id)) {
|
|
|
ElMessage.warning('请先生成评估报告后再录入案例库')
|
|
|
return
|
|
|
}
|
|
|
- if (event.caseAdded) {
|
|
|
+ if (currentAssess.value?.caseFlag === '1') {
|
|
|
ElMessage.warning('该事件已录入案例库,不可重复录入')
|
|
|
return
|
|
|
}
|
|
|
- event.caseAdded = true
|
|
|
- ElMessage.success('已成功录入案例库')
|
|
|
+ try {
|
|
|
+ await saveEventAssess({ eventId: event.id, caseFlag: '1' })
|
|
|
+ if (currentAssess.value) currentAssess.value.caseFlag = '1'
|
|
|
+ ElMessage.success('已成功录入案例库')
|
|
|
+ } catch (e) {
|
|
|
+ ElMessage.error('录入失败,请重试')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// 重新评估
|
|
|
+// 重新评估(删除后端评估记录并清空表单)
|
|
|
const resetEvaluation = () => {
|
|
|
- ElMessageBox.confirm('确认重新评估?所有评分和评语将被清空。', '提示', {
|
|
|
+ const event = currentEvent.value
|
|
|
+ if (!event) return
|
|
|
+ ElMessageBox.confirm('确认重新评估?当前评估记录将被删除,所有评分和评语将被清空。', '提示', {
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning'
|
|
|
- }).then(() => {
|
|
|
- assessments.org.score = 0; assessments.org.comment = ''
|
|
|
- assessments.cap.score = 0; assessments.cap.comment = ''
|
|
|
- assessments.effect.score = 0; assessments.effect.comment = ''
|
|
|
- assessments.loss.score = 0; assessments.loss.comment = ''
|
|
|
- const event = currentEvent.value
|
|
|
- if (event.evaluated) {
|
|
|
- event.evaluated = false
|
|
|
- event.caseAdded = false
|
|
|
+ }).then(async () => {
|
|
|
+ try {
|
|
|
+ await deleteEventAssess(event.id)
|
|
|
+ evaluatedIds.value.delete(event.id)
|
|
|
+ currentAssess.value = null
|
|
|
+ resetAssessments()
|
|
|
+ ElMessage.info('评估已重置')
|
|
|
+ } catch (e) {
|
|
|
+ ElMessage.error('重置失败,请重试')
|
|
|
}
|
|
|
- ElMessage.info('评估已重置')
|
|
|
}).catch(() => {})
|
|
|
}
|
|
|
|
|
|
// 刷新事件列表
|
|
|
const handleRefresh = () => {
|
|
|
- ElMessage.success('事件列表已刷新')
|
|
|
+ loadEvents()
|
|
|
}
|
|
|
|
|
|
// 初始化加载
|
|
|
-loadAssessmentsToForm()
|
|
|
+onMounted(() => {
|
|
|
+ loadEvents()
|
|
|
+})
|
|
|
|
|
|
// 监听事件切换
|
|
|
watch(currentEventId, () => {
|