feat: 冰灾调整

This commit is contained in:
niedongsheng 2026-04-20 16:13:03 +08:00
parent c850d60368
commit bf86b7d073
6 changed files with 159 additions and 754 deletions

View File

@ -7,6 +7,7 @@
class="inner-input" class="inner-input"
v-model="modelValue" v-model="modelValue"
:placeholder="placeholder" :placeholder="placeholder"
@input="search"
/> />
<van-icon <van-icon
class="close-icon" class="close-icon"
@ -35,9 +36,21 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['search'])
let timer = null
//
const search = () => {
clearTimeout(timer)
timer = setTimeout(() => {
emit('search', modelValue.value)
}, 1000)
}
// //
const clearInput = () => { const clearInput = () => {
modelValue.value = '' modelValue.value = ''
emit('search', modelValue.value)
} }
</script> </script>

View File

@ -36,8 +36,8 @@ const route = useRoute()
const isContinue = computed(() => route.query.isContinue) const isContinue = computed(() => route.query.isContinue)
const title = computed(() => { const title = computed(() => {
const label = route.query.eventType === 'ice' ? '冰毁' : '水毁' const label = eventType.value === 'ice' ? '冰毁' : '水毁'
if(!isContinue) return `${label}填报` if(!isContinue.value) return `${label}填报`
return `${label}续报` return `${label}续报`
}) })

View File

@ -46,22 +46,24 @@
</PanelItem> </PanelItem>
<PanelItem title="处置情况"> <PanelItem title="处置情况">
<van-field label="处置措施"> <van-field label="处置措施" label-align="top">
<template #input> <template #input>
<van-col <van-row :gutter="5">
v-for="(item, index) in options['iceDisposalMeasures']" <van-col
:span="24 / options['iceDisposalMeasures'].length" v-for="(item, index) in options['iceDisposalMeasures']"
:key="index" :span="8"
> :key="index"
<van-button
block
plain
:type="item.value === formData.report.disposalMeasures ? 'primary' : 'default'"
@click="formData.report.disposalMeasures = item.value"
> >
{{ item.label }} <van-button
</van-button> block
</van-col> plain
:type="item.value === formData.report.disposalMeasures ? 'primary' : 'default'"
@click="formData.report.disposalMeasures = item.value"
>
{{ item.label }}
</van-button>
</van-col>
</van-row>
</template> </template>
</van-field> </van-field>
@ -155,7 +157,7 @@ import { request } from '@shared/utils/request';
import { useOptions } from '@shared/composables/useOptions'; import { useOptions } from '@shared/composables/useOptions';
import DisasterFileUpload from '../components/DisasterFileUpload.vue'; import DisasterFileUpload from '../components/DisasterFileUpload.vue';
import { showToast, showFailToast, showLoadingToast, showSuccessToast } from 'vant'; import { showToast, showFailToast, showLoadingToast, showSuccessToast } from 'vant';
import { formatDate } from '@shared/utils' import { formatDate } from '@shared/utils';
const route = useRoute(); const route = useRoute();
const { options } = useOptions(); const { options } = useOptions();
@ -222,17 +224,16 @@ const calibrateTime = isShowToast => {
}; };
const validate = () => { const validate = () => {
return true return true;
} };
const submitting = ref(false) const submitting = ref(false);
const handleSubmit = async () => { const handleSubmit = async () => {
// //
if (!validate()) return; if (!validate()) return;
submitting.value = true; submitting.value = true;
try { try {
// //
const submitData = { const submitData = {
...formData.value, ...formData.value,
@ -262,7 +263,7 @@ const handleSubmit = async () => {
} else { } else {
// //
setTimeout(() => { setTimeout(() => {
if(isContinue) router.go(-1) if (isContinue) router.go(-1);
else router.replace('/disasterManagement'); else router.replace('/disasterManagement');
}, 500); }, 500);
} }
@ -276,7 +277,7 @@ const handleSubmit = async () => {
submitting.value = false; submitting.value = false;
} }
}; };
const detailData = ref(null) const detailData = ref(null);
const getDisasterDetail = async () => { const getDisasterDetail = async () => {
const id = route.query.id; const id = route.query.id;
if (!id) { if (!id) {
@ -295,12 +296,12 @@ const getDisasterDetail = async () => {
if (result?.data) { if (result?.data) {
const data = result.data; const data = result.data;
data.reportList = undefined data.reportList = undefined;
data.report = {} data.report = {};
data.yhzMaterialList = [] data.yhzMaterialList = [];
data.fileList = [] data.fileList = [];
detailData.value = data; detailData.value = data;
formData.value = data formData.value = data;
} else { } else {
ElMessage.warning(result.message || '获取详情失败'); ElMessage.warning(result.message || '获取详情失败');
} }

View File

@ -1,535 +0,0 @@
<template>
<PageContainer title="冰毁详情" @click-back="handleClickBack" class="page-container">
<!-- 当前站点信息 -->
<CurrentSite />
<!-- 基本信息 -->
<PanelItem title="基本信息" v-if="!loading">
<template #headerExtra>
<div class="status-wrapper">
<van-tag :type="getEventStatusType()" size="medium" plain>
{{ getEventStatusText() }}
</van-tag>
</div>
</template>
<!-- 事件类型 -->
<div class="info-row">
<span class="info-label">事件类型</span>
<span class="info-value">冰毁事件</span>
</div>
<!-- 路况类别 -->
<div class="info-row">
<span class="info-label">路况类别</span>
<span class="info-value">{{ detailData.roadConditionType || '-' }}</span>
</div>
<!-- 是否阻断 -->
<div class="info-row">
<span class="info-label">是否阻断</span>
<span class="info-value">{{ detailData.event?.isBlocked ? '是' : '否' }}</span>
</div>
<!-- 抢险进度 -->
<div class="info-row">
<span class="info-label">抢险进度</span>
<span class="info-value">{{ detailData.event?.repairProgress || '-' }}</span>
</div>
<!-- <div class="info-row">
<span class="info-label">处理措施</span>
<span class="info-value">{{ formatDisposalMeasures(detailData.event?.disposalMeasures) || '-' }}</span>
</div> -->
<!-- 水毁处数 -->
<div class="info-row">
<span class="info-label">水毁处数</span>
<span class="info-value">{{ detailData.event?.damageCount || 0 }}</span>
</div>
<!-- 阻断里程 -->
<div class="info-row">
<span class="info-label">阻断里程</span>
<span class="info-value">{{ detailData.event?.blockedMileage ? detailData.event.blockedMileage + '公里' : '-' }}</span>
</div>
<!-- 发生时间 -->
<div class="info-row">
<span class="info-label">发生时间</span>
<span class="info-value">{{ detailData.occurTime || '-' }}</span>
</div>
<!-- 线路编号 -->
<div class="info-row">
<span class="info-label">线路编号</span>
<span class="info-value">{{ detailData.routeNo || '-' }}</span>
</div>
<!-- 地点路线 -->
<div class="info-row">
<span class="info-label">地点路线</span>
<span class="info-value">{{ detailData.occurLocation || '-' }}</span>
</div>
<!-- 起点桩号 -->
<div class="info-row">
<span class="info-label">起点桩号</span>
<span class="info-value">{{ detailData.event?.startStakeNo || '-' }}</span>
</div>
<!-- 止点桩号 -->
<div class="info-row">
<span class="info-label">止点桩号</span>
<span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span>
</div>
<!-- 路况位置使用阻断点小地名或发生地点 -->
<div class="info-row">
<span class="info-label">路况位置</span>
<span class="info-value">{{ detailData.event?.blockedPointName || detailData.occurLocation || '-' }}</span>
</div>
<!-- 阻断点小地名 -->
<div class="info-row">
<span class="info-label">阻断点小地名</span>
<span class="info-value">{{ detailData.event?.blockedPointName || '-' }}</span>
</div>
<!-- 上报区县 -->
<div class="info-row">
<span class="info-label">上报区县</span>
<span class="info-value">{{ detailData.event?.district || '-' }}</span>
</div>
<!-- 是否恢复重建 -->
<div class="info-row">
<span class="info-label">是否恢复重建</span>
<span class="info-value">{{ detailData.event?.needsRecovery ? '是' : '否' }}</span>
</div>
<!-- 恢复重建预估费用 -->
<div class="info-row">
<span class="info-label">恢复重建预估费用</span>
<span class="info-value">{{ detailData.event?.estimatedRecoveryCost ? detailData.event.estimatedRecoveryCost + '万元' : '-' }}</span>
</div>
<!-- 联系人 -->
<div class="info-row">
<span class="info-label">联系人</span>
<span class="info-value">{{ detailData.event?.contactPerson || '-' }}</span>
</div>
<!-- 联系电话 -->
<div class="info-row">
<span class="info-label">联系电话</span>
<span class="info-value">{{ detailData.event?.contactPhone || '-' }}</span>
</div>
<!-- 填报单位 -->
<div class="info-row">
<span class="info-label">填报单位</span>
<span class="info-value">{{ detailData.event?.reporterUnit || '-' }}</span>
</div>
</PanelItem>
<!-- 填报信息 -->
<PanelItem title="填报信息" v-if="!loading">
<!-- 遍历所有填报记录首报 + 续报 -->
<div v-for="(report, index) in allReports" :key="index" class="report-section">
<div class="report-header">
<span class="report-title">{{ report?.title }}</span>
<span class="report-meta">{{ report.reporterName || '-' }} {{ report.reportTime || '-' }}</span>
</div>
<div class="report-content">
<div class="info-row">
<span class="info-label">处置措施</span>
<span class="info-value">{{ report.disposalMeasures || '-' }}</span>
</div>
<template v-for="(lossItem, idx) of report.lossList" :key="idx">
<div class="info-row">
<span class="info-label">{{ lossItem.lossCategory }}</span>
<span class="info-value">{{ lossItem.totalAmount }}{{ lossItem.unit }}</span>
</div>
<div class="info-row" v-if="lossItem.lossCategory == '其他损失'">
<span class="info-label">其它损失描述</span>
<span class="info-value">{{ lossItem.remark }}</span>
</div>
</template>
<div class="info-row">
<span class="info-label">有无车辆滞留</span>
<span class="info-value">{{ getVehicleStrandedText(report) }}</span>
</div>
<div class="info-row">
<span class="info-label">滞留车辆</span>
<span class="info-value">{{ report.strandedVehicleCount || 0 }}</span>
</div>
<div class="info-row">
<span class="info-label">预计恢复时间</span>
<span class="info-value">{{ report.expectRecoverTime || '-' }}</span>
</div>
<div class="info-row">
<span class="info-label">实际恢复时间</span>
<span class="info-value">{{ report.actualRecoverTime || '-' }}</span>
</div>
<div class="info-row">
<span class="info-label">现场描述</span>
<span class="info-value">{{ report.siteDescription || '-' }}</span>
</div>
<!-- 附件 -->
<div class="info-row column" v-if="report.fileList && report.fileList.length > 0">
<span class="info-label">附件</span>
<div class="attachment-list">
<div v-for="(file, fileIndex) in report.fileList" :key="fileIndex" class="attachment-item">
<div class="preview-image-block" v-if="file.fileType === 1" @click="previewFile(report, file)">
<img :src="file.fileUrl" alt="" />
</div>
<div class="preview-video-block" v-else>
<van-icon :name="file.fileType === 1 ? 'photo-o' : 'video-o'" />
<span class="file-name">{{ file.fileName }}</span>
<!-- <van-button size="mini" type="primary" plain @click="previewFile(report, file)">预览</van-button> -->
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 无填报信息时显示 -->
<EmptyBox v-if="!hasReportData" placeholder="暂无填报信息" />
</PanelItem>
<!-- 底部按钮未解除状态显示续报按钮 -->
<div class="footer-buttons" v-if="!loading">
<van-button type="primary" class="footer-btn" @click="handleContinueReport">续报</van-button>
</div>
<van-loading class="loading-icon" v-if="loading">加载中...</van-loading>
<van-image-preview :startPosition="startPosition" v-model:show="previewImagesVisible" :images="imagesForPreview">
<template v-slot:index="{ index }">{{ index + 1 }}</template>
</van-image-preview>
</PageContainer>
</template>
<script setup>
import { onMounted, ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showToast, showImagePreview } from 'vant'
import PageContainer from '@/components/PageContainer.vue'
import PanelItem from '@/components/PanelItem.vue'
import CurrentSite from '@/components/CurrentSite.vue'
import EmptyBox from '@/components/EmptyBox.vue'
import { request } from '@shared/utils/request'
const router = useRouter()
const route = useRoute()
// Data
const detailData = ref({
event: null, // Event
report: [], //
fileList: [], //
lossList: [], //
occurLocation: '',
occurTime: '',
roadConditionType: '',
routeNo: ''
})
const loading = ref(true)
const allReports = computed(() => {
const reports =
detailData.value.report?.map((item, index) => {
if (index === detailData.value.report.length - 1) {
item.title = '首报'
} else {
item.title = '续报' + (detailData.value.report.length - 1 - index)
}
return item
}) || []
return reports
// return reports.reverse()
})
//
const hasReportData = computed(() => {
return allReports.value.length > 0
})
//
const getEventStatusText = () => {
return detailData.eventStatus === 1 ? '已解除' : '未解除'
}
//
const getEventStatusType = () => {
return detailData.eventStatus === 1 ? 'success' : 'danger'
}
//
const formatDisposalMeasures = (measures) => {
if (!measures) return ''
const measureMap = {
半幅封闭: '半幅封闭',
全副封闭: '全副封闭',
便道通行: '便道通行',
正常通行: '正常通行'
}
return measures
.split(',')
.map((m) => measureMap[m.trim()] || m.trim())
.join('、')
}
// lossList
const getLossDescription = (report) => {
const lossList = report?.lossList
if (!lossList || lossList.length === 0) return '-'
const totalVolume = lossList.reduce((sum, loss) => {
const volume = (loss.length || 0) * (loss.width || 0) * (loss.height || 0)
return sum + volume
}, 0)
const totalAmount = lossList.reduce((sum, loss) => sum + (loss.totalAmount || 0), 0)
return `${totalVolume}方,共损失${totalAmount}万元`
}
//
const getVehicleStrandedText = (report) => {
const count = report?.strandedVehicleCount || 0
return count > 0 ? `有车滞留` : '无车滞留'
}
//
const getDisasterDetail = async () => {
const id = route.query.id
if (!id) {
showToast('缺少灾毁ID')
return
}
loading.value = true
try {
const result = await request({
url: `/snow-ops-platform/event/getById`,
method: 'get',
params: { id }
})
if (result?.data) {
// Data
const data = result.data
detailData.value = {
event: data.event || null,
report: data.reportList || [], // 使 report
fileList: data.fileList || [],
lossList: data.lossList || [],
occurLocation: data.occurLocation || '',
occurTime: data.occurTime || '',
roadConditionType: data.roadConditionType || '',
routeNo: data.routeNo || ''
}
} else {
showToast(result.message || '获取详情失败')
}
} catch (error) {
console.error('获取灾毁详情失败:', error)
showToast('获取详情失败,请稍后重试')
}
loading.value = false
}
//
const handleClickBack = () => {
router.push('/disasterManagement')
}
//
const handleContinueReport = () => {
router.push({
path: '/disasterReport',
query: {
id: route.query.id,
eventType: 'ice',
isContinue: 'true'
}
})
}
const isImageFile = (file) => {
// url
const imageExtensions = /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i
if (file.fileUrl && imageExtensions.test(file.fileUrl)) return true
//
if (file.type && file.type.startsWith('image/')) return true
return false
}
const imagesForPreview = ref([])
const previewImagesVisible = ref(false)
const startPosition = ref(0)
//
const previewFile = (report, file) => {
const images = report.fileList.filter((file) => isImageFile(file))
imagesForPreview.value = images.map((item) => item.fileUrl)
startPosition.value = imagesForPreview.value.indexOf(file.fileUrl)
previewImagesVisible.value = true
}
onMounted(() => {
getDisasterDetail()
})
</script>
<style scoped lang="scss">
.page-container {
padding-bottom: 80px;
}
.status-wrapper {
border-bottom: 1px solid #ebedf0;
}
.info-row {
display: flex;
align-items: flex-start;
margin-bottom: 12px;
line-height: 1.4;
&.sub-row {
margin-left: 20px;
margin-top: -8px;
}
&.column {
flex-direction: column;
.info-label {
margin-bottom: 10px;
}
}
}
.info-label {
width: 110px;
flex-shrink: 0;
color: #969799;
font-size: 14px;
}
.info-value {
flex: 1;
color: #323233;
font-size: 14px;
word-break: break-all;
}
.report-section {
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #ebedf0;
&:last-child {
border-bottom: none;
margin-bottom: 0;
}
}
.report-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px dashed #ebedf0;
.report-title {
font-size: 16px;
font-weight: 500;
color: #1989fa;
}
.report-meta {
font-size: 12px;
color: #969799;
}
}
.report-content {
.info-row {
margin-bottom: 10px;
}
}
.attachment-list {
display: flex;
flex-wrap: wrap;
flex: 1;
gap: 10px;
overflow: hidden;
.attachment-item {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
background: #f7f8fa;
border-radius: 4px;
.van-icon {
font-size: 20px;
color: #1989fa;
}
.file-name {
flex: 1;
font-size: 13px;
color: #323233;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.preview-image-block {
width: 60px;
height: 60px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.footer-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
gap: 12px;
padding: 12px 16px;
background: #fff;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
z-index: 10;
.footer-btn {
flex: 1;
height: 44px;
border-radius: 22px;
font-size: 16px;
}
}
.loading-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>

View File

@ -19,39 +19,16 @@
<span class="info-value">冰毁事件</span> <span class="info-value">冰毁事件</span>
</div> </div>
<!-- 路况类别 --> <!-- 所属服务站 -->
<div class="info-row"> <div class="info-row">
<span class="info-label">路况类别</span> <span class="info-label">所属服务站</span>
<span class="info-value">{{ detailData.roadConditionType || '-' }}</span> <span class="info-value">{{ detailData.event?.stationName || '-' }}</span>
</div> </div>
<!-- 是否阻断 --> <!-- 线路编号 -->
<div class="info-row"> <div class="info-row">
<span class="info-label">是否阻断</span> <span class="info-label">线路编号</span>
<span class="info-value">{{ detailData.event?.isBlocked ? '是' : '否' }}</span> <span class="info-value">{{ detailData.routeNo || '-' }}</span>
</div>
<!-- 抢险进度 -->
<div class="info-row">
<span class="info-label">抢险进度</span>
<span class="info-value">{{ detailData.event?.repairProgress || '-' }}</span>
</div>
<!-- <div class="info-row">
<span class="info-label">处理措施</span>
<span class="info-value">{{ formatDisposalMeasures(detailData.event?.disposalMeasures) || '-' }}</span>
</div> -->
<!-- 水毁处数 -->
<div class="info-row">
<span class="info-label">水毁处数</span>
<span class="info-value">{{ detailData.event?.damageCount || 0 }}</span>
</div>
<!-- 阻断里程 -->
<div class="info-row">
<span class="info-label">阻断里程</span>
<span class="info-value">{{ detailData.event?.blockedMileage ? detailData.event.blockedMileage + '公里' : '-' }}</span>
</div> </div>
<!-- 发生时间 --> <!-- 发生时间 -->
@ -60,18 +37,18 @@
<span class="info-value">{{ detailData.occurTime || '-' }}</span> <span class="info-value">{{ detailData.occurTime || '-' }}</span>
</div> </div>
<!-- 线路编号 -->
<div class="info-row">
<span class="info-label">线路编号</span>
<span class="info-value">{{ detailData.routeNo || '-' }}</span>
</div>
<!-- 地点路线 --> <!-- 地点路线 -->
<div class="info-row"> <div class="info-row">
<span class="info-label">地点路线</span> <span class="info-label">路况位置</span>
<span class="info-value">{{ detailData.occurLocation || '-' }}</span> <span class="info-value">{{ detailData.occurLocation || '-' }}</span>
</div> </div>
<!-- 发生地点 -->
<div class="info-row">
<span class="info-label">发生地点</span>
<span class="info-value">{{ detailData.event?.occurLocation || '-' }}</span>
</div>
<!-- 起点桩号 --> <!-- 起点桩号 -->
<div class="info-row"> <div class="info-row">
<span class="info-label">起点桩号</span> <span class="info-label">起点桩号</span>
@ -83,54 +60,6 @@
<span class="info-label">止点桩号</span> <span class="info-label">止点桩号</span>
<span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span> <span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span>
</div> </div>
<!-- 路况位置使用阻断点小地名或发生地点 -->
<div class="info-row">
<span class="info-label">路况位置</span>
<span class="info-value">{{ detailData.event?.blockedPointName || detailData.occurLocation || '-' }}</span>
</div>
<!-- 阻断点小地名 -->
<div class="info-row">
<span class="info-label">阻断点小地名</span>
<span class="info-value">{{ detailData.event?.blockedPointName || '-' }}</span>
</div>
<!-- 上报区县 -->
<div class="info-row">
<span class="info-label">上报区县</span>
<span class="info-value">{{ detailData.event?.district || '-' }}</span>
</div>
<!-- 是否恢复重建 -->
<div class="info-row">
<span class="info-label">是否恢复重建</span>
<span class="info-value">{{ detailData.event?.needsRecovery ? '是' : '否' }}</span>
</div>
<!-- 恢复重建预估费用 -->
<div class="info-row">
<span class="info-label">恢复重建预估费用</span>
<span class="info-value">{{ detailData.event?.estimatedRecoveryCost ? detailData.event.estimatedRecoveryCost + '万元' : '-' }}</span>
</div>
<!-- 联系人 -->
<div class="info-row">
<span class="info-label">联系人</span>
<span class="info-value">{{ detailData.event?.contactPerson || '-' }}</span>
</div>
<!-- 联系电话 -->
<div class="info-row">
<span class="info-label">联系电话</span>
<span class="info-value">{{ detailData.event?.contactPhone || '-' }}</span>
</div>
<!-- 填报单位 -->
<div class="info-row">
<span class="info-label">填报单位</span>
<span class="info-value">{{ detailData.event?.reporterUnit || '-' }}</span>
</div>
</PanelItem> </PanelItem>
<!-- 填报信息 --> <!-- 填报信息 -->
@ -139,21 +68,28 @@
<div v-for="(report, index) in allReports" :key="index" class="report-section"> <div v-for="(report, index) in allReports" :key="index" class="report-section">
<div class="report-header"> <div class="report-header">
<span class="report-title">{{ report?.title }}</span> <span class="report-title">{{ report?.title }}</span>
<span class="report-meta">{{ report.reporterName || '-' }} {{ report.reportTime || '-' }}</span> <span class="report-meta">
{{ report.reporterName || '-' }} {{ report.reportTime || '-' }}
</span>
</div> </div>
<div class="report-content"> <div class="report-content">
<div class="info-row"> <div class="info-row">
<span class="info-label">处置措施</span> <span class="info-label">处置措施</span>
<span class="info-value">{{ report.disposalMeasures || '-' }}</span> <span class="info-value">{{ report.disposalMeasures || '-' }}</span>
</div> </div>
<template v-for="(lossItem, idx) of report.lossList" :key="idx"> <div class="info-row">
<span class="info-label">预计恢复时间</span>
<span class="info-value">{{ report.expectRecoverTime || '-' }}</span>
</div>
<div class="info-row">
<span class="info-label">实际恢复时间</span>
<span class="info-value">{{ report.actualRecoverTime || '-' }}</span>
</div>
<template v-for="(item, idx) of report.materialUsageList" :key="idx">
<div class="info-row"> <div class="info-row">
<span class="info-label">{{ lossItem.lossCategory }}</span> <span class="info-label">{{ item.materialName }}</span>
<span class="info-value">{{ lossItem.totalAmount }}{{ lossItem.unit }}</span> <span class="info-value" v-if="item.usageAmount">{{ item.usageAmount + '' + item.materialUnit }}</span>
</div> <span class="info-value" v-else>-</span>
<div class="info-row" v-if="lossItem.lossCategory == '其他损失'">
<span class="info-label">其它损失描述</span>
<span class="info-value">{{ lossItem.remark }}</span>
</div> </div>
</template> </template>
<div class="info-row"> <div class="info-row">
@ -164,14 +100,6 @@
<span class="info-label">滞留车辆</span> <span class="info-label">滞留车辆</span>
<span class="info-value">{{ report.strandedVehicleCount || 0 }}</span> <span class="info-value">{{ report.strandedVehicleCount || 0 }}</span>
</div> </div>
<div class="info-row">
<span class="info-label">预计恢复时间</span>
<span class="info-value">{{ report.expectRecoverTime || '-' }}</span>
</div>
<div class="info-row">
<span class="info-label">实际恢复时间</span>
<span class="info-value">{{ report.actualRecoverTime || '-' }}</span>
</div>
<div class="info-row"> <div class="info-row">
<span class="info-label">现场描述</span> <span class="info-label">现场描述</span>
<span class="info-value">{{ report.siteDescription || '-' }}</span> <span class="info-value">{{ report.siteDescription || '-' }}</span>
@ -180,8 +108,16 @@
<div class="info-row column" v-if="report.fileList && report.fileList.length > 0"> <div class="info-row column" v-if="report.fileList && report.fileList.length > 0">
<span class="info-label">附件</span> <span class="info-label">附件</span>
<div class="attachment-list"> <div class="attachment-list">
<div v-for="(file, fileIndex) in report.fileList" :key="fileIndex" class="attachment-item"> <div
<div class="preview-image-block" v-if="file.fileType === 1" @click="previewFile(report, file)"> v-for="(file, fileIndex) in report.fileList"
:key="fileIndex"
class="attachment-item"
>
<div
class="preview-image-block"
v-if="file.fileType === 1"
@click="previewFile(report, file)"
>
<img :src="file.fileUrl" alt="" /> <img :src="file.fileUrl" alt="" />
</div> </div>
<div class="preview-video-block" v-else> <div class="preview-video-block" v-else>
@ -206,24 +142,28 @@
<van-loading class="loading-icon" v-if="loading">加载中...</van-loading> <van-loading class="loading-icon" v-if="loading">加载中...</van-loading>
<van-image-preview :startPosition="startPosition" v-model:show="previewImagesVisible" :images="imagesForPreview"> <van-image-preview
:startPosition="startPosition"
v-model:show="previewImagesVisible"
:images="imagesForPreview"
>
<template v-slot:index="{ index }">{{ index + 1 }}</template> <template v-slot:index="{ index }">{{ index + 1 }}</template>
</van-image-preview> </van-image-preview>
</PageContainer> </PageContainer>
</template> </template>
<script setup> <script setup>
import { onMounted, ref, computed } from 'vue' import { onMounted, ref, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router';
import { showToast, showImagePreview } from 'vant' import { showToast, showImagePreview } from 'vant';
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue';
import PanelItem from '@/components/PanelItem.vue' import PanelItem from '@/components/PanelItem.vue';
import CurrentSite from '@/components/CurrentSite.vue' import CurrentSite from '@/components/CurrentSite.vue';
import EmptyBox from '@/components/EmptyBox.vue' import EmptyBox from '@/components/EmptyBox.vue';
import { request } from '@shared/utils/request' import { request } from '@shared/utils/request';
const router = useRouter() const router = useRouter();
const route = useRoute() const route = useRoute();
// Data // Data
const detailData = ref({ const detailData = ref({
@ -234,120 +174,106 @@ const detailData = ref({
occurLocation: '', occurLocation: '',
occurTime: '', occurTime: '',
roadConditionType: '', roadConditionType: '',
routeNo: '' routeNo: '',
}) });
const loading = ref(true) const loading = ref(true);
const allReports = computed(() => { const allReports = computed(() => {
const reports = const reports =
detailData.value.report?.map((item, index) => { detailData.value.report?.map((item, index) => {
if (index === detailData.value.report.length - 1) { if (index === detailData.value.report.length - 1) {
item.title = '首报' item.title = '首报';
} else { } else {
item.title = '续报' + (detailData.value.report.length - 1 - index) item.title = '续报' + (detailData.value.report.length - 1 - index);
} }
return item return item;
}) || [] }) || [];
return reports return reports;
// return reports.reverse() // return reports.reverse()
}) });
// //
const hasReportData = computed(() => { const hasReportData = computed(() => {
return allReports.value.length > 0 return allReports.value.length > 0;
}) });
// //
const getEventStatusText = () => { const getEventStatusText = () => {
return detailData.eventStatus === 1 ? '已解除' : '未解除' return detailData.eventStatus === 1 ? '已解除' : '未解除';
} };
// //
const getEventStatusType = () => { const getEventStatusType = () => {
return detailData.eventStatus === 1 ? 'success' : 'danger' return detailData.eventStatus === 1 ? 'success' : 'danger';
} };
// //
const formatDisposalMeasures = (measures) => { const formatDisposalMeasures = measures => {
if (!measures) return '' if (!measures) return '';
const measureMap = { const measureMap = {
半幅封闭: '半幅封闭', 半幅封闭: '半幅封闭',
全副封闭: '全副封闭', 全副封闭: '全副封闭',
便道通行: '便道通行', 便道通行: '便道通行',
正常通行: '正常通行' 正常通行: '正常通行',
} };
return measures return measures
.split(',') .split(',')
.map((m) => measureMap[m.trim()] || m.trim()) .map(m => measureMap[m.trim()] || m.trim())
.join('、') .join('、');
} };
// lossList
const getLossDescription = (report) => {
const lossList = report?.lossList
if (!lossList || lossList.length === 0) return '-'
const totalVolume = lossList.reduce((sum, loss) => {
const volume = (loss.length || 0) * (loss.width || 0) * (loss.height || 0)
return sum + volume
}, 0)
const totalAmount = lossList.reduce((sum, loss) => sum + (loss.totalAmount || 0), 0)
return `${totalVolume}方,共损失${totalAmount}万元`
}
// //
const getVehicleStrandedText = (report) => { const getVehicleStrandedText = report => {
const count = report?.strandedVehicleCount || 0 const count = report?.strandedVehicleCount || 0;
return count > 0 ? `有车滞留` : '无车滞留' return count > 0 ? `有车滞留` : '无车滞留';
} };
// //
const getDisasterDetail = async () => { const getDisasterDetail = async () => {
const id = route.query.id const id = route.query.id;
if (!id) { if (!id) {
showToast('缺少灾毁ID') showToast('缺少灾毁ID');
return return;
} }
loading.value = true loading.value = true;
try { try {
const result = await request({ const result = await request({
url: `/snow-ops-platform/event/getById`, url: `/snow-ops-platform/event/getById`,
method: 'get', method: 'get',
params: { id } params: { id },
}) });
if (result?.data) { if (result?.data) {
// Data // Data
const data = result.data const data = result.data;
detailData.value = { detailData.value = {
event: data.event || null, event: data.event || null,
report: data.reportList || [], // 使 report report: data.reportList?.reverse() || [], // 使 report
fileList: data.fileList || [], fileList: data.fileList || [],
lossList: data.lossList || [], lossList: data.lossList || [],
occurLocation: data.occurLocation || '', occurLocation: data.occurLocation || '',
occurTime: data.occurTime || '', occurTime: data.occurTime || '',
roadConditionType: data.roadConditionType || '', roadConditionType: data.roadConditionType || '',
routeNo: data.routeNo || '' routeNo: data.routeNo || '',
} };
} else { } else {
showToast(result.message || '获取详情失败') showToast(result.message || '获取详情失败');
} }
} catch (error) { } catch (error) {
console.error('获取灾毁详情失败:', error) console.error('获取灾毁详情失败:', error);
showToast('获取详情失败,请稍后重试') showToast('获取详情失败,请稍后重试');
} }
loading.value = false loading.value = false;
} };
// //
const handleClickBack = () => { const handleClickBack = () => {
router.push('/disasterManagement') router.push('/disasterManagement');
} };
// //
const handleContinueReport = () => { const handleContinueReport = () => {
@ -356,35 +282,35 @@ const handleContinueReport = () => {
query: { query: {
id: route.query.id, id: route.query.id,
eventType: 'ice', eventType: 'ice',
isContinue: 'true' isContinue: 'true',
} },
}) });
} };
const isImageFile = (file) => { const isImageFile = file => {
// url // url
const imageExtensions = /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i const imageExtensions = /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i;
if (file.fileUrl && imageExtensions.test(file.fileUrl)) return true if (file.fileUrl && imageExtensions.test(file.fileUrl)) return true;
// //
if (file.type && file.type.startsWith('image/')) return true if (file.type && file.type.startsWith('image/')) return true;
return false return false;
} };
const imagesForPreview = ref([]) const imagesForPreview = ref([]);
const previewImagesVisible = ref(false) const previewImagesVisible = ref(false);
const startPosition = ref(0) const startPosition = ref(0);
// //
const previewFile = (report, file) => { const previewFile = (report, file) => {
const images = report.fileList.filter((file) => isImageFile(file)) const images = report.fileList.filter(file => isImageFile(file));
imagesForPreview.value = images.map((item) => item.fileUrl) imagesForPreview.value = images.map(item => item.fileUrl);
startPosition.value = imagesForPreview.value.indexOf(file.fileUrl) startPosition.value = imagesForPreview.value.indexOf(file.fileUrl);
previewImagesVisible.value = true previewImagesVisible.value = true;
} };
onMounted(() => { onMounted(() => {
getDisasterDetail() getDisasterDetail();
}) });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -26,7 +26,7 @@
<el-col :span="8"> <el-col :span="8">
<div class="info-item"> <div class="info-item">
<span class="info-label">线路编号</span> <span class="info-label">线路编号</span>
<span class="info-value">{{ detailData.routeNo || detailData.event?.routeNo || '-' }}</span> <span class="info-value">{{ detailData.routeNo || '-' }}</span>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>