|
|
@@ -64,9 +64,10 @@
|
|
|
<el-table-column prop="createTime" label="创建时间" width="150" />
|
|
|
<el-table-column label="操作" width="260" fixed="right">
|
|
|
<template #default="{ row }">
|
|
|
- <el-button v-if="row.status === 1" type="primary" plain size="small" @click="openOperation(row, 'start')" v-hasPermi="['dispatch:event:dispose']">开始处置</el-button>
|
|
|
- <el-button v-if="row.status === 2" type="warning" plain size="small" @click="openOperation(row, 'track')" v-hasPermi="['dispatch:event:track']">跟踪</el-button>
|
|
|
- <el-button v-if="row.status === 2" type="success" plain size="small" @click="handleRelease(row)" v-hasPermi="['dispatch:event:release']">解除</el-button>
|
|
|
+ <el-button v-if="row.status === 1 && isHandler(row)" type="primary" plain size="small" @click="openOperation(row, 'start')" v-hasPermi="['dispatch:event:handle']">开始处置</el-button>
|
|
|
+ <el-button v-if="row.status === 2 && isHandler(row)" type="warning" plain size="small" @click="openOperation(row, 'track')" v-hasPermi="['dispatch:event:handle']">跟踪</el-button>
|
|
|
+ <el-button v-if="row.status === 2 && isHandler(row)" type="primary" plain size="small" @click="openOperation(row, 'submit')" v-hasPermi="['dispatch:event:handle']">提交核验</el-button>
|
|
|
+ <el-button v-if="row.status === 3 && isVerifier(row)" type="success" plain size="small" @click="handleAudit(row)" v-hasPermi="['dispatch:event:audit']">核验</el-button>
|
|
|
<el-button type="info" plain size="small" @click="handleDetail(row)">详情</el-button>
|
|
|
<el-button v-if="row.editable" type="danger" plain size="small" @click="handleDelete(row)" v-hasPermi="['dispatch:event:remove']">删除</el-button>
|
|
|
</template>
|
|
|
@@ -86,7 +87,7 @@
|
|
|
</div>
|
|
|
|
|
|
<!-- 开始处置和跟踪共用备注+附件表单 -->
|
|
|
- <el-dialog v-model="operationVisible" :title="operationType === 'start' ? '开始处置' : '事件跟踪'" width="600px" :close-on-click-modal="false">
|
|
|
+ <el-dialog v-model="operationVisible" :title="operationType === 'start' ? '开始处置' : operationType === 'submit' ? '提交核验' : '事件跟踪'" width="600px" :close-on-click-modal="false">
|
|
|
<el-form :model="operationForm" :rules="operationRules" ref="operationFormRef" label-width="100px">
|
|
|
<el-form-item label="备注" prop="operDesc">
|
|
|
<el-input v-model="operationForm.operDesc" type="textarea" :rows="4" maxlength="1000" show-word-limit placeholder="请输入备注" />
|
|
|
@@ -112,73 +113,19 @@
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<el-button @click="operationVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" :loading="operationSubmitting" @click="confirmOperation">{{ operationType === 'start' ? '开始处置' : '保存跟踪' }}</el-button>
|
|
|
+ <el-button type="primary" :loading="operationSubmitting" @click="confirmOperation">{{ operationType === 'start' ? '开始处置' : operationType === 'submit' ? '提交核验' : '保存跟踪' }}</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
- <!-- 解除弹窗 -->
|
|
|
- <el-dialog v-model="releaseVisible" title="事件解除" width="600px" :close-on-click-modal="false">
|
|
|
- <el-form :model="releaseForm" :rules="releaseRules" ref="releaseFormRef" label-width="100px">
|
|
|
- <el-form-item label="事件编号">{{ releaseForm.eventNo }}</el-form-item>
|
|
|
- <el-form-item label="事件摘要">
|
|
|
- <div class="desc-text">{{ releaseForm.summary || '-' }}</div>
|
|
|
+ <el-dialog v-model="auditVisible" title="事件核验" width="560px" :close-on-click-modal="false">
|
|
|
+ <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="100px">
|
|
|
+ <el-form-item label="事件编号">{{ auditForm.eventNo }}</el-form-item>
|
|
|
+ <el-form-item label="核验结果" prop="resolved">
|
|
|
+ <el-radio-group v-model="auditForm.resolved"><el-radio :value="true">已解除</el-radio><el-radio :value="false">未解除</el-radio></el-radio-group>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="解除原因" prop="releaseReason">
|
|
|
- <el-select v-model="releaseForm.releaseReason" placeholder="请选择" style="width:100%">
|
|
|
- <el-option v-for="d in dis_event_release_reason" :key="d.value" :label="d.label" :value="d.value" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="解除说明" prop="releaseNote">
|
|
|
- <el-input v-model="releaseForm.releaseNote" type="textarea" :rows="4" placeholder="请输入解除说明" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="损失(万元)">
|
|
|
- <el-input-number v-model="releaseForm.lossAmount" :min="0" :precision="2" style="width:100%" />
|
|
|
- </el-form-item>
|
|
|
- <template v-if="isHandledRelease">
|
|
|
- <el-form-item label="处置前佐证">
|
|
|
- <el-upload
|
|
|
- ref="beforeUploadRef"
|
|
|
- v-model:file-list="releaseForm.beforeFiles"
|
|
|
- :action="uploadUrl"
|
|
|
- :headers="uploadHeaders"
|
|
|
- :limit="5"
|
|
|
- multiple
|
|
|
- accept=".jpg,.jpeg,.png,.gif,.bmp,.webp,.mp4,.mov,.m4v,.webm"
|
|
|
- :on-success="(res) => handleAttachSuccess(res, 'before')"
|
|
|
- :on-error="handleAttachError"
|
|
|
- :on-exceed="() => ElMessage.warning('处置前佐证最多 5 个')"
|
|
|
- :before-upload="handleAttachBefore"
|
|
|
- style="width:100%"
|
|
|
- >
|
|
|
- <el-button type="primary" size="small">上传处置前照片/视频</el-button>
|
|
|
- <template #tip><div class="el-upload__tip">图片或视频,单个不超过 50MB,最多 5 个</div></template>
|
|
|
- </el-upload>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="处置后佐证">
|
|
|
- <el-upload
|
|
|
- ref="afterUploadRef"
|
|
|
- v-model:file-list="releaseForm.afterFiles"
|
|
|
- :action="uploadUrl"
|
|
|
- :headers="uploadHeaders"
|
|
|
- :limit="5"
|
|
|
- multiple
|
|
|
- accept=".jpg,.jpeg,.png,.gif,.bmp,.webp,.mp4,.mov,.m4v,.webm"
|
|
|
- :on-success="(res) => handleAttachSuccess(res, 'after')"
|
|
|
- :on-error="handleAttachError"
|
|
|
- :on-exceed="() => ElMessage.warning('处置后佐证最多 5 个')"
|
|
|
- :before-upload="handleAttachBefore"
|
|
|
- style="width:100%"
|
|
|
- >
|
|
|
- <el-button type="primary" size="small">上传处置后照片/视频</el-button>
|
|
|
- <template #tip><div class="el-upload__tip">图片或视频,单个不超过 50MB,最多 5 个</div></template>
|
|
|
- </el-upload>
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
+ <el-form-item label="核验意见" prop="opinion"><el-input v-model="auditForm.opinion" type="textarea" :rows="4" placeholder="请输入核验意见" /></el-form-item>
|
|
|
</el-form>
|
|
|
- <template #footer>
|
|
|
- <el-button @click="releaseVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" @click="confirmRelease">确认</el-button>
|
|
|
- </template>
|
|
|
+ <template #footer><el-button @click="auditVisible = false">取消</el-button><el-button type="primary" @click="confirmAudit">确认提交</el-button></template>
|
|
|
</el-dialog>
|
|
|
|
|
|
<EventDetailDrawer ref="eventDetailRef" />
|
|
|
@@ -186,22 +133,26 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup name="Gzjc">
|
|
|
-import { ref, nextTick, computed, onMounted } from 'vue'
|
|
|
+import { ref, nextTick, onMounted } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import { useDict } from '@/utils/dict'
|
|
|
import { getToken } from '@/utils/auth'
|
|
|
+import useUserStore from '@/store/modules/user'
|
|
|
import {
|
|
|
- getEventPage, deleteEventInfo, startDisposeEvent, trackEvent,
|
|
|
- releaseEventInfo, getEventStatusOptions
|
|
|
+ getEventPage, deleteEventInfo, startDisposeEvent, trackEvent, submitVerifyEvent, auditEvent,
|
|
|
+ getEventStatusOptions
|
|
|
} from '@/api/dispatch'
|
|
|
import { Search, Refresh } from '@element-plus/icons-vue'
|
|
|
|
|
|
import EventDetailDrawer from './EventDetailDrawer.vue'
|
|
|
|
|
|
// ==================== 字典 ====================
|
|
|
-const { dis_event_category, dis_event_level, dis_event_trend, dis_event_release_reason, dis_yes_no } = useDict(
|
|
|
- 'dis_event_category', 'dis_event_level', 'dis_event_trend', 'dis_event_release_reason', 'dis_yes_no'
|
|
|
+const { dis_event_category, dis_event_level, dis_event_trend, dis_yes_no } = useDict(
|
|
|
+ 'dis_event_category', 'dis_event_level', 'dis_event_trend', 'dis_yes_no'
|
|
|
)
|
|
|
+const userStore = useUserStore()
|
|
|
+const isHandler = row => Number(userStore.id) > 0 && Number(userStore.id) === Number(row.handlerId)
|
|
|
+const isVerifier = row => Number(userStore.id) > 0 && Number(userStore.id) === Number(row.verifierId)
|
|
|
|
|
|
function dictLabel(list, value) {
|
|
|
if (value === null || value === undefined || value === '') return '-'
|
|
|
@@ -209,20 +160,20 @@ function dictLabel(list, value) {
|
|
|
return hit ? hit.label : value
|
|
|
}
|
|
|
|
|
|
-// 本页覆盖「已核实(1) 待开始处置 / 处置中(2) 待跟踪解除 / 已解除(3) 终态查看」
|
|
|
+// 本页覆盖「已核实(1) 待开始处置 / 处置中(2) 待跟踪 / 待验收(3) 待核验 / 已解除(4) 终态查看」
|
|
|
const trackStatusOptions = ref([])
|
|
|
|
|
|
async function loadStatusOptions() {
|
|
|
try {
|
|
|
const res = await getEventStatusOptions()
|
|
|
- trackStatusOptions.value = (res.data || []).filter(item => [1, 2, 3].includes(item.value))
|
|
|
+ trackStatusOptions.value = (res.data || []).filter(item => [1, 2, 3, 4].includes(item.value))
|
|
|
} catch {
|
|
|
trackStatusOptions.value = []
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 状态码 -> 标签样式(0 待核实 1 已核实 2 处置中 3 已解除 4 不实事件)
|
|
|
-const STATUS_TAG = { 0: 'warning', 1: 'success', 2: 'primary', 3: 'warning', 4: 'info' }
|
|
|
+// 状态码 -> 标签样式(0 待核实 1 已核实 2 处置中 3 待验收 4 已解除 5 不实事件)
|
|
|
+const STATUS_TAG = { 0: 'warning', 1: 'success', 2: 'primary', 3: 'warning', 4: 'warning', 5: 'info' }
|
|
|
function statusTagType(status) {
|
|
|
return STATUS_TAG[status] || 'info'
|
|
|
}
|
|
|
@@ -247,7 +198,7 @@ const hasValue = (val) => val !== '' && val !== null && val !== undefined
|
|
|
async function loadData() {
|
|
|
loading.value = true
|
|
|
try {
|
|
|
- const params = { statusList: [1, 2, 3], orderAsc: true }
|
|
|
+ const params = { statusList: [1, 2, 3, 4], orderAsc: true }
|
|
|
if (hasValue(searchForm.value.status)) params.statusList = [searchForm.value.status]
|
|
|
if (searchForm.value.eventCategory) params.eventCategory = searchForm.value.eventCategory
|
|
|
if (searchForm.value.dateRange && searchForm.value.dateRange.length === 2) {
|
|
|
@@ -304,6 +255,18 @@ const operationForm = ref({ operDesc: '', fileList: [], attachUrls: [] })
|
|
|
const operationRules = {
|
|
|
operDesc: [{ required: true, message: '请输入备注', trigger: 'blur' }]
|
|
|
}
|
|
|
+const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload?dir=dispatch'
|
|
|
+const uploadHeaders = { Authorization: 'Bearer ' + getToken() }
|
|
|
+const ALLOWED_ATTACH_EXT = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp4', 'mov', 'm4v', 'webm']
|
|
|
+function handleAttachBefore(file) {
|
|
|
+ const ext = String(file.name).split('.').pop().toLowerCase()
|
|
|
+ if (!ALLOWED_ATTACH_EXT.includes(ext) || file.size / 1024 / 1024 >= 50) {
|
|
|
+ ElMessage.error('仅支持图片/视频格式且单个不超过 50MB')
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+function handleAttachError() { ElMessage.error('文件上传失败') }
|
|
|
|
|
|
function openOperation(row, type) {
|
|
|
currentRow.value = { ...row }
|
|
|
@@ -337,6 +300,9 @@ async function confirmOperation() {
|
|
|
if (operationType.value === 'start') {
|
|
|
await startDisposeEvent(payload)
|
|
|
ElMessage.success('已开始处置')
|
|
|
+ } else if (operationType.value === 'submit') {
|
|
|
+ await submitVerifyEvent(payload)
|
|
|
+ ElMessage.success('已提交核验')
|
|
|
} else {
|
|
|
await trackEvent(payload)
|
|
|
ElMessage.success('跟踪记录保存成功')
|
|
|
@@ -351,100 +317,25 @@ async function confirmOperation() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// ==================== 解除 ====================
|
|
|
-const releaseVisible = ref(false)
|
|
|
-const releaseFormRef = ref(null)
|
|
|
const currentReleaseRow = ref(null)
|
|
|
-const releaseForm = ref({ eventNo: '', summary: '', releaseReason: '', releaseNote: '', lossAmount: undefined, beforeFiles: [], afterFiles: [], beforeUrls: [], afterUrls: [] })
|
|
|
-const releaseRules = {
|
|
|
- releaseReason: [{ required: true, message: '请选择解除原因', trigger: 'change' }],
|
|
|
- releaseNote: [{ required: true, message: '请输入解除说明', trigger: 'blur' }]
|
|
|
-}
|
|
|
-
|
|
|
-// ==================== 解除佐证附件(处置前 / 处置后) ====================
|
|
|
-// 解除原因为「事件已处置完成」(字典值 '1') 时展示上传控件;图片/视频各最多 5 个、单个 ≤50MB
|
|
|
-// 佐证附件统一存 MinIO(图片/视频直链可预览)
|
|
|
-const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload?dir=dispatch_release'
|
|
|
-const uploadHeaders = { Authorization: 'Bearer ' + getToken() }
|
|
|
-const beforeUploadRef = ref(null)
|
|
|
-const afterUploadRef = ref(null)
|
|
|
-const ALLOWED_ATTACH_EXT = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'mp4', 'mov', 'm4v', 'webm']
|
|
|
-
|
|
|
-const isHandledRelease = computed(() => dictLabel(dis_event_release_reason.value, releaseForm.value.releaseReason) === '事件已处置完成')
|
|
|
-
|
|
|
-function handleAttachBefore(file) {
|
|
|
- const ext = String(file.name).split('.').pop().toLowerCase()
|
|
|
- if (!ALLOWED_ATTACH_EXT.includes(ext)) {
|
|
|
- ElMessage.error('仅支持图片(jpg/png/gif 等)或视频(mp4/mov 等)格式')
|
|
|
- return false
|
|
|
- }
|
|
|
- if (file.size / 1024 / 1024 >= 50) {
|
|
|
- ElMessage.error('文件大小不能超过 50MB')
|
|
|
- return false
|
|
|
- }
|
|
|
- return true
|
|
|
-}
|
|
|
-
|
|
|
-function handleAttachSuccess(res, slot) {
|
|
|
- if (res.code === 200 && res.url) {
|
|
|
- // 库内只存相对路径(迁移环境不用改数据),展示/下载由后端拼 MinIO 地址
|
|
|
- releaseForm.value[slot + 'Urls'].push(res.relativePath || res.url)
|
|
|
- } else {
|
|
|
- ElMessage.error(res.msg || '上传失败')
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function handleAttachError() {
|
|
|
- ElMessage.error('文件上传失败')
|
|
|
-}
|
|
|
-
|
|
|
-function handleRelease(row) {
|
|
|
+// ==================== 最终核验 ====================
|
|
|
+const auditVisible = ref(false)
|
|
|
+const auditFormRef = ref(null)
|
|
|
+const auditForm = ref({ eventNo: '', resolved: true, opinion: '' })
|
|
|
+const auditRules = { opinion: [{ required: true, message: '请输入核验意见', trigger: 'blur' }] }
|
|
|
+function handleAudit(row) {
|
|
|
currentReleaseRow.value = row
|
|
|
- releaseForm.value = {
|
|
|
- eventNo: row.eventNo,
|
|
|
- summary: row.description || '',
|
|
|
- releaseReason: '',
|
|
|
- releaseNote: '',
|
|
|
- lossAmount: row.lossAmount !== null && row.lossAmount !== undefined ? Number(row.lossAmount) : undefined,
|
|
|
- beforeFiles: [],
|
|
|
- afterFiles: [],
|
|
|
- beforeUrls: [],
|
|
|
- afterUrls: []
|
|
|
- }
|
|
|
- releaseVisible.value = true
|
|
|
- // 清掉上次打开残留的上传文件列表
|
|
|
- nextTick(() => {
|
|
|
- beforeUploadRef.value?.clearFiles()
|
|
|
- afterUploadRef.value?.clearFiles()
|
|
|
- })
|
|
|
+ auditForm.value = { eventNo: row.eventNo, resolved: true, opinion: '' }
|
|
|
+ auditVisible.value = true
|
|
|
}
|
|
|
-
|
|
|
-async function confirmRelease() {
|
|
|
+async function confirmAudit() {
|
|
|
+ try { await auditFormRef.value.validate() } catch { return }
|
|
|
try {
|
|
|
- await releaseFormRef.value.validate()
|
|
|
- } catch {
|
|
|
- return
|
|
|
- }
|
|
|
- try {
|
|
|
- await releaseEventInfo(currentReleaseRow.value.id, {
|
|
|
- eventId: currentReleaseRow.value.id,
|
|
|
- releaseReason: releaseForm.value.releaseReason,
|
|
|
- releaseNote: releaseForm.value.releaseNote,
|
|
|
- lossAmount: releaseForm.value.lossAmount,
|
|
|
- // 处置前/后佐证(多文件英文逗号分隔),仅「事件已处置完成」时采集
|
|
|
- beforeUrl: releaseForm.value.beforeUrls.join(','),
|
|
|
- afterUrl: releaseForm.value.afterUrls.join(',')
|
|
|
- })
|
|
|
- ElMessage.success('事件已解除')
|
|
|
- releaseVisible.value = false
|
|
|
- nextTick(() => {
|
|
|
- beforeUploadRef.value?.clearFiles()
|
|
|
- afterUploadRef.value?.clearFiles()
|
|
|
- })
|
|
|
+ await auditEvent(currentReleaseRow.value.id, { resolved: auditForm.value.resolved, auditOpinion: auditForm.value.opinion })
|
|
|
+ ElMessage.success('核验完成')
|
|
|
+ auditVisible.value = false
|
|
|
loadData()
|
|
|
- } catch (error) {
|
|
|
- ElMessage.error('操作失败:' + (error.message || '未知错误'))
|
|
|
- }
|
|
|
+ } catch (error) { ElMessage.error('操作失败:' + (error.message || '未知错误')) }
|
|
|
}
|
|
|
|
|
|
// ==================== 删除 ====================
|