462 lines
12 KiB
Vue
Raw Normal View History

2026-04-08 16:01:04 +08:00
<template>
2026-04-20 11:51:52 +08:00
<PageContainer title="冰毁详情" @click-back="handleClickBack" class="page-container">
2026-04-08 16:01:04 +08:00
<!-- 当前站点信息 -->
<CurrentSite />
<!-- 基本信息 -->
2026-04-15 09:08:41 +08:00
<PanelItem title="基本信息" v-if="!loading">
2026-04-08 16:01:04 +08:00
<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>
2026-04-20 11:51:52 +08:00
<span class="info-value">冰毁事件</span>
2026-04-08 16:01:04 +08:00
</div>
2026-04-20 16:13:03 +08:00
<!-- 所属服务站 -->
2026-04-08 16:01:04 +08:00
<div class="info-row">
2026-04-20 16:13:03 +08:00
<span class="info-label">所属服务站</span>
<span class="info-value">{{ detailData.event?.stationName || '-' }}</span>
2026-04-08 16:01:04 +08:00
</div>
2026-04-20 16:13:03 +08:00
<!-- 线路编号 -->
2026-04-08 16:01:04 +08:00
<div class="info-row">
2026-04-20 16:13:03 +08:00
<span class="info-label">线路编号</span>
<span class="info-value">{{ detailData.routeNo || '-' }}</span>
2026-04-08 16:01:04 +08:00
</div>
<!-- 发生时间 -->
<div class="info-row">
<span class="info-label">发生时间</span>
<span class="info-value">{{ detailData.occurTime || '-' }}</span>
</div>
2026-04-20 16:13:03 +08:00
<!-- 地点路线 -->
2026-04-08 16:01:04 +08:00
<div class="info-row">
2026-04-20 16:13:03 +08:00
<span class="info-label">路况位置</span>
<span class="info-value">{{ detailData.occurLocation || '-' }}</span>
2026-04-08 16:01:04 +08:00
</div>
2026-04-20 16:13:03 +08:00
<!-- 发生地点 -->
2026-04-08 16:01:04 +08:00
<div class="info-row">
2026-04-20 16:13:03 +08:00
<span class="info-label">发生地点</span>
<span class="info-value">{{ detailData.event?.occurLocation || '-' }}</span>
2026-04-08 16:01:04 +08:00
</div>
2026-04-10 13:39:36 +08:00
<!-- 起点桩号 -->
2026-04-08 16:01:04 +08:00
<div class="info-row">
<span class="info-label">起点桩号</span>
<span class="info-value">{{ detailData.event?.startStakeNo || '-' }}</span>
</div>
2026-04-10 13:39:36 +08:00
<!-- 止点桩号 -->
2026-04-08 16:01:04 +08:00
<div class="info-row">
<span class="info-label">止点桩号</span>
<span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span>
</div>
</PanelItem>
<!-- 填报信息 -->
2026-04-15 09:08:41 +08:00
<PanelItem title="填报信息" v-if="!loading">
2026-04-09 09:35:05 +08:00
<!-- 遍历所有填报记录首报 + 续报 -->
<div v-for="(report, index) in allReports" :key="index" class="report-section">
2026-04-08 16:01:04 +08:00
<div class="report-header">
2026-04-09 09:35:05 +08:00
<span class="report-title">{{ report?.title }}</span>
2026-04-20 16:13:03 +08:00
<span class="report-meta">
{{ report.reporterName || '-' }} {{ report.reportTime || '-' }}
</span>
2026-04-08 16:01:04 +08:00
</div>
<div class="report-content">
<div class="info-row">
2026-04-10 11:38:24 +08:00
<span class="info-label">处置措施</span>
2026-04-10 17:41:23 +08:00
<span class="info-value">{{ report.disposalMeasures || '-' }}</span>
2026-04-08 16:01:04 +08:00
</div>
2026-04-20 16:13:03 +08:00
<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">
2026-04-10 17:41:23 +08:00
<div class="info-row">
2026-04-20 16:13:03 +08:00
<span class="info-label">{{ item.materialName }}</span>
<span class="info-value" v-if="item.usageAmount">{{ item.usageAmount + '' + item.materialUnit }}</span>
<span class="info-value" v-else>-</span>
2026-04-10 17:41:23 +08:00
</div>
</template>
2026-04-08 16:01:04 +08:00
<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.siteDescription || '-' }}</span>
</div>
<!-- 附件 -->
2026-04-10 17:41:23 +08:00
<div class="info-row column" v-if="report.fileList && report.fileList.length > 0">
2026-04-08 16:01:04 +08:00
<span class="info-label">附件</span>
<div class="attachment-list">
2026-04-20 16:13:03 +08:00
<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)"
>
2026-04-10 17:41:23 +08:00
<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>
2026-04-08 16:01:04 +08:00
</div>
</div>
</div>
</div>
</div>
<!-- 无填报信息时显示 -->
<EmptyBox v-if="!hasReportData" placeholder="暂无填报信息" />
</PanelItem>
<!-- 底部按钮未解除状态显示续报按钮 -->
2026-04-15 09:08:41 +08:00
<div class="footer-buttons" v-if="!loading">
2026-04-10 17:41:23 +08:00
<van-button type="primary" class="footer-btn" @click="handleContinueReport">续报</van-button>
2026-04-08 16:01:04 +08:00
</div>
2026-04-10 17:41:23 +08:00
2026-04-15 09:08:41 +08:00
<van-loading class="loading-icon" v-if="loading">加载中...</van-loading>
2026-04-20 16:13:03 +08:00
<van-image-preview
:startPosition="startPosition"
v-model:show="previewImagesVisible"
:images="imagesForPreview"
>
2026-04-10 17:41:23 +08:00
<template v-slot:index="{ index }">{{ index + 1 }}</template>
</van-image-preview>
2026-04-08 16:01:04 +08:00
</PageContainer>
</template>
<script setup>
2026-04-20 16:13:03 +08:00
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();
2026-04-08 16:01:04 +08:00
// 详情数据(对应 Data 接口)
const detailData = ref({
2026-04-10 17:41:23 +08:00
event: null, // Event 对象
report: [], // 填报列表(包含首报和续报)
fileList: [], // 附件列表
lossList: [], // 损失列表
2026-04-08 16:01:04 +08:00
occurLocation: '',
occurTime: '',
roadConditionType: '',
2026-04-20 16:13:03 +08:00
routeNo: '',
});
2026-04-08 16:01:04 +08:00
2026-04-20 16:13:03 +08:00
const loading = ref(true);
2026-04-15 09:08:41 +08:00
2026-04-09 09:35:05 +08:00
const allReports = computed(() => {
2026-04-10 17:41:23 +08:00
const reports =
detailData.value.report?.map((item, index) => {
if (index === detailData.value.report.length - 1) {
2026-04-20 16:13:03 +08:00
item.title = '首报';
2026-04-10 17:41:23 +08:00
} else {
2026-04-20 16:13:03 +08:00
item.title = '续报' + (detailData.value.report.length - 1 - index);
2026-04-10 17:41:23 +08:00
}
2026-04-20 16:13:03 +08:00
return item;
}) || [];
return reports;
2026-04-10 17:41:23 +08:00
// return reports.reverse()
2026-04-20 16:13:03 +08:00
});
2026-04-08 16:01:04 +08:00
// 是否有填报数据
const hasReportData = computed(() => {
2026-04-20 16:13:03 +08:00
return allReports.value.length > 0;
});
2026-04-08 16:01:04 +08:00
// 获取事件状态文本
const getEventStatusText = () => {
2026-04-20 16:13:03 +08:00
return detailData.eventStatus === 1 ? '已解除' : '未解除';
};
2026-04-08 16:01:04 +08:00
// 获取事件状态类型
const getEventStatusType = () => {
2026-04-20 16:13:03 +08:00
return detailData.eventStatus === 1 ? 'success' : 'danger';
};
2026-04-08 16:01:04 +08:00
// 格式化处置措施(逗号分隔转中文)
2026-04-20 16:13:03 +08:00
const formatDisposalMeasures = measures => {
if (!measures) return '';
2026-04-08 16:01:04 +08:00
const measureMap = {
2026-04-10 17:41:23 +08:00
半幅封闭: '半幅封闭',
全副封闭: '全副封闭',
便道通行: '便道通行',
2026-04-20 16:13:03 +08:00
正常通行: '正常通行',
};
2026-04-08 16:01:04 +08:00
return measures
.split(',')
2026-04-20 16:13:03 +08:00
.map(m => measureMap[m.trim()] || m.trim())
.join('、');
};
2026-04-08 16:01:04 +08:00
// 获取车辆滞留文本
2026-04-20 16:13:03 +08:00
const getVehicleStrandedText = report => {
const count = report?.strandedVehicleCount || 0;
return count > 0 ? `有车滞留` : '无车滞留';
};
2026-04-08 16:01:04 +08:00
// 获取灾毁详情
const getDisasterDetail = async () => {
2026-04-20 16:13:03 +08:00
const id = route.query.id;
2026-04-08 16:01:04 +08:00
if (!id) {
2026-04-20 16:13:03 +08:00
showToast('缺少灾毁ID');
return;
2026-04-08 16:01:04 +08:00
}
2026-04-20 16:13:03 +08:00
loading.value = true;
2026-04-15 09:08:41 +08:00
2026-04-08 16:01:04 +08:00
try {
const result = await request({
2026-04-20 11:51:52 +08:00
url: `/snow-ops-platform/event/getById`,
2026-04-08 16:01:04 +08:00
method: 'get',
2026-04-20 16:13:03 +08:00
params: { id },
});
2026-04-08 16:01:04 +08:00
if (result?.data) {
// 接口返回 Data 结构
2026-04-20 16:13:03 +08:00
const data = result.data;
2026-04-08 16:01:04 +08:00
detailData.value = {
event: data.event || null,
2026-04-20 16:13:03 +08:00
report: data.reportList?.reverse() || [], // 直接使用 report包含首报和续报
2026-04-08 16:01:04 +08:00
fileList: data.fileList || [],
lossList: data.lossList || [],
occurLocation: data.occurLocation || '',
occurTime: data.occurTime || '',
roadConditionType: data.roadConditionType || '',
2026-04-20 16:13:03 +08:00
routeNo: data.routeNo || '',
};
2026-04-08 16:01:04 +08:00
} else {
2026-04-20 16:13:03 +08:00
showToast(result.message || '获取详情失败');
2026-04-08 16:01:04 +08:00
}
} catch (error) {
2026-04-20 16:13:03 +08:00
console.error('获取灾毁详情失败:', error);
showToast('获取详情失败,请稍后重试');
2026-04-08 16:01:04 +08:00
}
2026-04-20 16:13:03 +08:00
loading.value = false;
};
2026-04-08 16:01:04 +08:00
// 点击返回
const handleClickBack = () => {
2026-04-20 16:13:03 +08:00
router.push('/disasterManagement');
};
2026-04-08 16:01:04 +08:00
// 续报
const handleContinueReport = () => {
router.push({
path: '/disasterReport',
query: {
id: route.query.id,
2026-04-20 11:51:52 +08:00
eventType: 'ice',
2026-04-20 16:13:03 +08:00
isContinue: 'true',
},
});
};
2026-04-08 16:01:04 +08:00
2026-04-20 16:13:03 +08:00
const isImageFile = file => {
2026-04-10 17:41:23 +08:00
// 根据url后缀判断
2026-04-20 16:13:03 +08:00
const imageExtensions = /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i;
if (file.fileUrl && imageExtensions.test(file.fileUrl)) return true;
2026-04-10 17:41:23 +08:00
// 根据文件类型判断
2026-04-20 16:13:03 +08:00
if (file.type && file.type.startsWith('image/')) return true;
return false;
};
2026-04-10 17:41:23 +08:00
2026-04-20 16:13:03 +08:00
const imagesForPreview = ref([]);
const previewImagesVisible = ref(false);
const startPosition = ref(0);
2026-04-10 17:41:23 +08:00
2026-04-08 16:01:04 +08:00
// 预览附件
2026-04-10 17:41:23 +08:00
const previewFile = (report, file) => {
2026-04-20 16:13:03 +08:00
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;
};
2026-04-08 16:01:04 +08:00
onMounted(() => {
2026-04-20 16:13:03 +08:00
getDisasterDetail();
});
2026-04-08 16:01:04 +08:00
</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;
}
2026-04-10 17:41:23 +08:00
&.column {
flex-direction: column;
.info-label {
margin-bottom: 10px;
}
}
2026-04-08 16:01:04 +08:00
}
.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 {
2026-04-10 17:41:23 +08:00
display: flex;
flex-wrap: wrap;
2026-04-08 16:01:04 +08:00
flex: 1;
2026-04-10 17:41:23 +08:00
gap: 10px;
overflow: hidden;
2026-04-08 16:01:04 +08:00
.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;
}
}
}
2026-04-10 17:41:23 +08:00
.preview-image-block {
width: 60px;
height: 60px;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
2026-04-08 16:01:04 +08:00
.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;
}
}
2026-04-15 09:08:41 +08:00
.loading-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
2026-04-10 17:41:23 +08:00
</style>