609 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="web-detail-container">
<!-- 页面头部 -->
<div class="page-header">
<div class="header-left">
<el-button :icon="ArrowLeft" @click="handleClickBack">返回</el-button>
<h2 class="page-title">灾毁详情</h2>
</div>
<div class="header-right">
<el-tag :type="getEventStatusType()" size="large">
{{ getEventStatusText() }}
</el-tag>
</div>
</div>
<div class="content-container">
<div class="left-panel">
<!-- 基本信息卡片 -->
<el-card class="info-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">基本信息</span>
</div>
</template>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">事件类型</span>
<span class="info-value">水毁事件</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">路况类别</span>
<span class="info-value">{{ detailData.roadConditionType || '-' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">是否阻断</span>
<span class="info-value">{{ detailData.event?.isBlocked ? '是' : '否' }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">抢险进度</span>
<span class="info-value">{{ detailData.event?.repairProgress || '-' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">处理措施</span>
<span class="info-value">{{ getBaseDisposalMeasures() }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">水毁处数</span>
<span class="info-value">{{ detailData.event?.damageCount || 0 }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">阻断里程</span>
<span class="info-value">{{ detailData.event?.blockedMileage ? detailData.event.blockedMileage + '公里' : '-' }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">地点路线</span>
<span class="info-value">{{ detailData.occurLocation || '-' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">起点桩号</span>
<span class="info-value">{{ detailData.event?.startStakeNo || '-' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">止点桩号</span>
<span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">路况位置</span>
<span class="info-value">{{ detailData.event?.blockedPointName || detailData.occurLocation || '-' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">阻断点小地名</span>
<span class="info-value">{{ detailData.event?.blockedPointName || '-' }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">所属区县</span>
<span class="info-value">{{ detailData.event?.district || '-' }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">发生时间</span>
<span class="info-value">{{ detailData.occurTime || '-' }}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">是否恢复重建</span>
<span class="info-value">{{ detailData.event?.needsRecovery ? '是' : '否' }}</span>
</div>
</el-col>
<el-col :span="16" v-if="detailData.event?.needsRecovery">
<div class="info-item">
<span class="info-label">恢复重建预估费用:</span>
<span class="info-value">{{ detailData.event?.estimatedRecoveryCost ? detailData.event.estimatedRecoveryCost + '万元' : '-' }}</span>
</div>
</el-col>
</el-row>
</el-card>
<!-- 填报信息卡片 -->
<el-card class="info-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">填报信息</span>
</div>
</template>
<div v-if="hasReportData">
<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.reportTime || '-' }}</span>
</div>
<div class="content-wrapper">
<div class="basic-info-wrapper">
<div class="info-list">
<div class="info-item">
<span class="info-label">现场描述:</span>
<span class="info-value">{{ report.siteDescription || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">处置措施:</span>
<span class="info-value">{{ report.disposalMeasures || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">实际恢复时间:</span>
<span class="info-value">{{ report.actualRecoverTime || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">预计恢复时间:</span>
<span class="info-value">{{ report.expectRecoverTime || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">填报人:</span>
<span class="info-value">{{ report.reporterName ? report.reporterName : '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">联系电话:</span>
<span class="info-value">{{ report.phone ? report.phone : '-' }}</span>
</div>
</div>
<div class="file-list">
<FileUpload v-model="report.fileList" :readonly="!isEdit" />
</div>
</div>
<div class="detal-info-wrapper">
<template v-if="report.showDetail">
<LossListDetail :modelValue="report.lossList" :col-span="8" />
<el-row :gutter="24">
<el-col :span="8">
<div class="info-item margin">
<span class="info-label">投入机械:</span>
<span class="info-value">{{ report.investedMachinery ? report.investedMachinery + '台/班' : '-'}}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item margin">
<span class="info-label">投入人力:</span>
<span class="info-value">{{ report.investedManpower ? report.investedManpower + '人次' : '-'}}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item margin">
<span class="info-label">投入资金</span>
<span class="info-value">{{ report.investedFunds ? report.investedFunds + '万元' : '-'}}</span>
</div>
</el-col>
</el-row>
</template>
<el-button style="margin-top: 30px" type="primary" link @click="report.showDetail = !report.showDetail">
{{ report.showDetail ? '点击关闭详情' : '点击查看详情' }}
</el-button>
</div>
</div>
</div>
</div>
<el-empty v-else description="暂无填报信息" :image-size="100" />
</el-card>
<!-- 底部按钮 -->
<!-- <div class="footer-buttons">
<el-button type="primary" size="large" @click="handleContinueReport" class="footer-btn"> 续报 </el-button>
</div> -->
</div>
<div class="right-panel" v-if="isEdit">
<ContinueReport ref="continueReport" @refresh="getDisasterDetail" />
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { ArrowLeft, Picture, VideoCamera } from '@element-plus/icons-vue'
import ContinueReport from './WaterDisasterContinueReportPC.vue'
import { request } from '@shared/utils/request'
import LossListDetail from './WaterDisasterLossListDetailPC.vue'
import FileUpload from '@/component/FileUpload/FileUpload.vue'
import mockData from '../DisasterReport/waterMockJson.json'
const router = useRouter()
const route = useRoute()
// 详情数据
const detailData = ref({
event: null,
report: [],
fileList: [],
lossList: [],
occurLocation: '',
occurTime: '',
roadConditionType: '',
routeNo: ''
})
// 事件状态
const eventStatus = ref(0)
// 是否为编辑
const isEdit = computed(() => {
return route.query.mode === 'edit'
})
const continueReport = ref(null)
// 所有填报记录(首报 + 续报)
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 eventStatus.value === 1 ? '已解除' : '未解除'
}
// 获取事件状态类型
const getEventStatusType = () => {
return eventStatus.value === 1 ? 'success' : 'danger'
}
const getBaseDisposalMeasures = () => {
const firstItem = allReports.value[0]
if (!firstItem) return '-'
return formatDisposalMeasures(firstItem.disposalMeasures || '') || '-'
}
// 格式化处置措施
const formatDisposalMeasures = (measures) => {
if (!measures) return ''
const measureMap = {
半幅封闭: '半幅封闭',
全副封闭: '全副封闭',
便道通行: '便道通行',
正常通行: '正常通行'
}
return measures
.split(',')
.map((m) => measureMap[m.trim()] || m.trim())
.join('、')
}
// 获取损失描述
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 ? `有车滞留,共${count}` : '无车滞留'
}
// 获取灾毁详情
const getDisasterDetail = async () => {
const id = route.query.id
if (!id) {
ElMessage.warning('缺少灾毁ID')
return
}
try {
const result = await request({
url: `/snow-ops-platform/water-damage/getById`,
method: 'get',
params: { id }
})
if (result?.data) {
const data = result.data
console.log('🚀 ~ getDisasterDetail ~ data:', data)
detailData.value = {
event: data.event || null,
report: data.report || [],
fileList: data.fileList || [],
lossList: data.lossList || [],
occurLocation: data.occurLocation || '',
occurTime: data.occurTime || '',
roadConditionType: data.roadConditionType || '',
routeNo: data.routeNo || ''
}
eventStatus.value = data.eventStatus || 0
if (isEdit.value) {
const newFormData = {
...data,
lossList: null,
report: route.query.mock ? mockData.report : {},
fileList: null
}
continueReport.value?.initFormData(newFormData)
}
} else {
ElMessage.warning(result.message || '获取详情失败')
}
} catch (error) {
console.error('获取灾毁详情失败:', error)
ElMessage.error('获取详情失败,请稍后重试')
}
}
// 点击返回
const handleClickBack = () => {
router.push('/disasterManagement')
}
onMounted(() => {
getDisasterDetail()
})
</script>
<style scoped lang="scss">
.web-detail-container {
padding: 20px;
background: #f5f7fa;
height: 100%;
overflow: auto;
padding-bottom: 100px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 0 20px;
.header-left {
display: flex;
align-items: center;
gap: 16px;
.page-title {
margin: 0;
font-size: 20px;
font-weight: 600;
color: #303133;
}
}
}
.info-card {
margin-bottom: 20px;
border-radius: 8px;
:deep(.el-card__header) {
padding: 16px 20px;
border-bottom: 1px solid #ebeef5;
background: #fafafa;
}
.card-header {
.card-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
}
.info-row {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
.info-item {
display: flex;
align-items: flex-start;
line-height: 1.5;
& + .info-item {
margin-top: 10px;
}
&.margin {
margin-top: 10px;
}
.info-label {
white-space: nowrap;
flex-shrink: 0;
color: #909399;
font-size: 14px;
}
.info-value {
flex: 1;
color: #606266;
font-size: 14px;
word-break: break-all;
}
}
.report-section {
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 1px solid #ebeef5;
&:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
}
.report-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px dashed #dcdfe6;
.report-title {
font-size: 16px;
font-weight: 600;
color: #409eff;
}
.report-meta {
font-size: 13px;
color: #909399;
}
}
.attachment-wrapper {
margin-top: 12px;
padding-top: 8px;
border-top: 1px dashed #ebeef5;
.attachment-item {
.attachment-list {
display: flex;
flex-wrap: wrap;
gap: 16px;
.attachment-link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: #f5f7fa;
border-radius: 4px;
cursor: pointer;
&:hover {
background: #ecf5ff;
color: #409eff;
}
.file-name {
font-size: 13px;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
.footer-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: center;
padding: 16px 24px;
background: #fff;
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
z-index: 100;
.footer-btn {
width: 200px;
height: 44px;
font-size: 16px;
border-radius: 22px;
}
}
.content-container {
display: flex;
.left-panel {
flex: 1;
margin-right: 10px;
}
.right-panel {
width: 300px;
}
}
.content-wrapper {
display: flex;
flex-direction: column;
}
.basic-info-wrapper {
display: flex;
}
.detal-info-wrapper {
margin-top: 10px;
border-top: 1px solid #efefef;
padding-top: 10px;
}
.info-list {
flex: 1;
overflow: hidden;
}
.file-list {
}
</style>