Merge branch 'dev' of http://222.212.85.86:8222/bdzl2/bxztApp into dev
This commit is contained in:
commit
ea4de9b750
@ -55,6 +55,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { Field, Popup, DatePicker, TimePicker } from 'vant'
|
import { Field, Popup, DatePicker, TimePicker } from 'vant'
|
||||||
|
import { formatDate } from '@shared/utils'
|
||||||
|
|
||||||
// Props 定义
|
// Props 定义
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -141,6 +142,11 @@ const props = defineProps({
|
|||||||
return option
|
return option
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 返回结果格式化
|
||||||
|
resultFormat: {
|
||||||
|
type: String,
|
||||||
|
default: 'YYYY-MM-DD HH:mm:ss'
|
||||||
|
},
|
||||||
// 选项过滤函数
|
// 选项过滤函数
|
||||||
filter: {
|
filter: {
|
||||||
type: Function,
|
type: Function,
|
||||||
@ -350,7 +356,6 @@ const formatTimePart = () => {
|
|||||||
// 确认选择
|
// 确认选择
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
let finalValue = formatDatePart()
|
let finalValue = formatDatePart()
|
||||||
|
|
||||||
if (hasTime.value) {
|
if (hasTime.value) {
|
||||||
const timePart = formatTimePart()
|
const timePart = formatTimePart()
|
||||||
if (timePart) {
|
if (timePart) {
|
||||||
@ -358,6 +363,8 @@ const onConfirm = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalValue = formatDate(finalValue)
|
||||||
|
|
||||||
emit('update:modelValue', finalValue)
|
emit('update:modelValue', finalValue)
|
||||||
emit('change', finalValue)
|
emit('change', finalValue)
|
||||||
showPicker.value = false
|
showPicker.value = false
|
||||||
|
|||||||
@ -31,9 +31,9 @@
|
|||||||
<span class="info-value">{{ detailData.event?.isBlocked ? '是' : '否' }}</span>
|
<span class="info-value">{{ detailData.event?.isBlocked ? '是' : '否' }}</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?.repairProgress || '-' }}</span>
|
<span class="info-value">{{ detailData.event?.repairProgress || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -72,33 +72,17 @@
|
|||||||
<span class="info-value">{{ detailData.occurLocation || '-' }}</span>
|
<span class="info-value">{{ detailData.occurLocation || '-' }}</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?.startStakeNo || '-' }}</span>
|
<span class="info-value">{{ detailData.event?.startStakeNo || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row sub-row">
|
|
||||||
<span class="info-label">起点桩经度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.startStakeLng || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-row sub-row">
|
|
||||||
<span class="info-label">起点桩纬度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.startStakeLat || '-' }}</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.event?.endStakeNo || '-' }}</span>
|
<span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-row sub-row">
|
|
||||||
<span class="info-label">止点桩经度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.endStakeLng || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-row sub-row">
|
|
||||||
<span class="info-label">止点桩纬度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.endStakeLat || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 路况位置(使用阻断点小地名或发生地点) -->
|
<!-- 路况位置(使用阻断点小地名或发生地点) -->
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
@ -165,17 +149,19 @@
|
|||||||
</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">{{ formatDisposalMeasures(report.disposalMeasures) || '-' }}</span>
|
<span class="info-value">{{ report.disposalMeasures || '-' }}</span>
|
||||||
</div>
|
|
||||||
<div class="info-row">
|
|
||||||
<span class="info-label">塌方及损失:</span>
|
|
||||||
<span class="info-value">{{ getLossDescription(report) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-row">
|
|
||||||
<span class="info-label">路产损失:</span>
|
|
||||||
<span class="info-value">{{ report.totalLossAmount ? report.totalLossAmount + '万元' : '-' }}</span>
|
|
||||||
</div>
|
</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">
|
<div class="info-row">
|
||||||
<span class="info-label">有无车辆滞留:</span>
|
<span class="info-label">有无车辆滞留:</span>
|
||||||
<span class="info-value">{{ getVehicleStrandedText(report) }}</span>
|
<span class="info-value">{{ getVehicleStrandedText(report) }}</span>
|
||||||
@ -197,13 +183,18 @@
|
|||||||
<span class="info-value">{{ report.siteDescription || '-' }}</span>
|
<span class="info-value">{{ report.siteDescription || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- 附件 -->
|
<!-- 附件 -->
|
||||||
<div class="info-row" 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 v-for="(file, fileIndex) in report.fileList" :key="fileIndex" class="attachment-item">
|
||||||
<van-icon :name="file.fileType === 1 ? 'photo-o' : 'video-o'" />
|
<div class="preview-image-block" v-if="file.fileType === 1" @click="previewFile(report, file)">
|
||||||
<span class="file-name">{{ file.fileName }}</span>
|
<img :src="file.fileUrl" alt="" />
|
||||||
<van-button size="mini" type="primary" plain @click="previewFile(file)">预览</van-button>
|
</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>
|
</div>
|
||||||
@ -216,15 +207,19 @@
|
|||||||
|
|
||||||
<!-- 底部按钮:未解除状态显示续报按钮 -->
|
<!-- 底部按钮:未解除状态显示续报按钮 -->
|
||||||
<div class="footer-buttons">
|
<div class="footer-buttons">
|
||||||
<van-button type="primary" class="footer-btn" @click="handleContinueReport">续报</van-button>
|
<van-button type="primary" class="footer-btn" @click="handleContinueReport">续报</van-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<van-image-preview :startPosition="startPosition" v-model:show="previewImagesVisible" :images="imagesForPreview">
|
||||||
|
<template v-slot:index="{ index }">第{{ index + 1 }}页</template>
|
||||||
|
</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 } 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'
|
||||||
@ -236,10 +231,10 @@ const route = useRoute()
|
|||||||
|
|
||||||
// 详情数据(对应 Data 接口)
|
// 详情数据(对应 Data 接口)
|
||||||
const detailData = ref({
|
const detailData = ref({
|
||||||
event: null, // Event 对象
|
event: null, // Event 对象
|
||||||
reportList: [], // 填报列表(包含首报和续报)
|
report: [], // 填报列表(包含首报和续报)
|
||||||
fileList: [], // 附件列表
|
fileList: [], // 附件列表
|
||||||
lossList: [], // 损失列表
|
lossList: [], // 损失列表
|
||||||
occurLocation: '',
|
occurLocation: '',
|
||||||
occurTime: '',
|
occurTime: '',
|
||||||
roadConditionType: '',
|
roadConditionType: '',
|
||||||
@ -247,11 +242,17 @@ const detailData = ref({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const allReports = computed(() => {
|
const allReports = computed(() => {
|
||||||
const reports = detailData.value.reportList?.map((item, index)=>{
|
const reports =
|
||||||
item.title = index == 0 ? '首报' : ('续报' + index)
|
detailData.value.report?.map((item, index) => {
|
||||||
return item
|
if (index === detailData.value.report.length - 1) {
|
||||||
}) || []
|
item.title = '首报'
|
||||||
return reports.reverse()
|
} else {
|
||||||
|
item.title = '续报' + (detailData.value.report.length - 1 - index)
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}) || []
|
||||||
|
return reports
|
||||||
|
// return reports.reverse()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否有填报数据
|
// 是否有填报数据
|
||||||
@ -259,7 +260,6 @@ 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 ? '已解除' : '未解除'
|
||||||
@ -274,10 +274,10 @@ const getEventStatusType = () => {
|
|||||||
const formatDisposalMeasures = (measures) => {
|
const formatDisposalMeasures = (measures) => {
|
||||||
if (!measures) return ''
|
if (!measures) return ''
|
||||||
const measureMap = {
|
const measureMap = {
|
||||||
halfClose: '半幅封闭',
|
半幅封闭: '半幅封闭',
|
||||||
fullClose: '全副封闭',
|
全副封闭: '全副封闭',
|
||||||
bypass: '便道通行',
|
便道通行: '便道通行',
|
||||||
normal: '正常通行'
|
正常通行: '正常通行'
|
||||||
}
|
}
|
||||||
return measures
|
return measures
|
||||||
.split(',')
|
.split(',')
|
||||||
@ -303,7 +303,7 @@ const getLossDescription = (report) => {
|
|||||||
// 获取车辆滞留文本
|
// 获取车辆滞留文本
|
||||||
const getVehicleStrandedText = (report) => {
|
const getVehicleStrandedText = (report) => {
|
||||||
const count = report?.strandedVehicleCount || 0
|
const count = report?.strandedVehicleCount || 0
|
||||||
return count > 0 ? `有车滞留,共${count}辆` : '无车滞留'
|
return count > 0 ? `有车滞留` : '无车滞留'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取灾毁详情
|
// 获取灾毁详情
|
||||||
@ -326,7 +326,7 @@ const getDisasterDetail = async () => {
|
|||||||
const data = result.data
|
const data = result.data
|
||||||
detailData.value = {
|
detailData.value = {
|
||||||
event: data.event || null,
|
event: data.event || null,
|
||||||
reportList: data.report || [], // 直接使用 reportList,包含首报和续报
|
report: data.report || [], // 直接使用 report,包含首报和续报
|
||||||
fileList: data.fileList || [],
|
fileList: data.fileList || [],
|
||||||
lossList: data.lossList || [],
|
lossList: data.lossList || [],
|
||||||
occurLocation: data.occurLocation || '',
|
occurLocation: data.occurLocation || '',
|
||||||
@ -360,11 +360,25 @@ const handleContinueReport = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = (file) => {
|
const previewFile = (report, file) => {
|
||||||
if (file.fileUrl) {
|
const images = report.fileList.filter((file) => isImageFile(file))
|
||||||
window.open(file.fileUrl, '_blank')
|
imagesForPreview.value = images.map((item) => item.fileUrl)
|
||||||
}
|
startPosition.value = imagesForPreview.value.indexOf(file.fileUrl)
|
||||||
|
previewImagesVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -391,6 +405,13 @@ onMounted(() => {
|
|||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
margin-top: -8px;
|
margin-top: -8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.column {
|
||||||
|
flex-direction: column;
|
||||||
|
.info-label {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-label {
|
.info-label {
|
||||||
@ -445,14 +466,17 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.attachment-list {
|
.attachment-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
gap: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.attachment-item {
|
.attachment-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
padding: 8px;
|
|
||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
@ -467,11 +491,20 @@ onMounted(() => {
|
|||||||
color: #323233;
|
color: #323233;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-image-block {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.footer-buttons {
|
.footer-buttons {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { showToast, Tag as VanTag, Loading as VanLoading, Icon as VanIcon, Button as VanButton } from 'vant'
|
import { showToast, Tag as VanTag, Loading as VanLoading, Icon as VanIcon, Button as VanButton, showImagePreview } from 'vant'
|
||||||
import PageContainer from '@/components/PageContainer.vue'
|
import PageContainer from '@/components/PageContainer.vue'
|
||||||
import SearchInput from '@/components/SearchInput.vue'
|
import SearchInput from '@/components/SearchInput.vue'
|
||||||
import CardItem from '@/components/CardItem.vue'
|
import CardItem from '@/components/CardItem.vue'
|
||||||
@ -124,7 +124,7 @@ const getDisasterTypeText = (item) => {
|
|||||||
// 可以根据 roadConditionType、repairProgress 或其他字段来生成类型标签
|
// 可以根据 roadConditionType、repairProgress 或其他字段来生成类型标签
|
||||||
// 这里提供几种可能的映射方式
|
// 这里提供几种可能的映射方式
|
||||||
|
|
||||||
// 方式1:根据抢修进度
|
// 方式1:根据抢险进度
|
||||||
if (item.repairProgress) {
|
if (item.repairProgress) {
|
||||||
return item.repairProgress
|
return item.repairProgress
|
||||||
}
|
}
|
||||||
@ -149,8 +149,8 @@ const getShortTypeName = (type) => {
|
|||||||
'积水': '积水',
|
'积水': '积水',
|
||||||
'积雪': '积雪',
|
'积雪': '积雪',
|
||||||
'其他': '其他',
|
'其他': '其他',
|
||||||
'未抢修': '待抢修',
|
'未抢险': '待抢险',
|
||||||
'抢修中': '抢修中',
|
'抢险中': '抢险中',
|
||||||
'已完成': '已完成',
|
'已完成': '已完成',
|
||||||
'高速公路': '高速',
|
'高速公路': '高速',
|
||||||
'国道': '国道',
|
'国道': '国道',
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<CurrentSite />
|
<CurrentSite />
|
||||||
|
|
||||||
<!-- 事件类型 -->
|
<!-- 事件类型 -->
|
||||||
<PanelItem title="事件类型" style="margin-bottom: 10px;" v-if="!isContinue">
|
<PanelItem title="事件类型" style="margin-bottom: 10px" v-if="!isContinue">
|
||||||
<van-radio-group v-model="eventType" direction="horizontal" class="event-type-group">
|
<van-radio-group v-model="eventType" direction="horizontal" class="event-type-group">
|
||||||
<van-radio name="water">水毁灾害</van-radio>
|
<van-radio name="water">水毁灾害</van-radio>
|
||||||
<van-radio name="ice">冰雪灾害</van-radio>
|
<van-radio name="ice">冰雪灾害</van-radio>
|
||||||
@ -12,10 +12,7 @@
|
|||||||
</PanelItem>
|
</PanelItem>
|
||||||
|
|
||||||
<!-- 根据事件类型渲染不同表单 -->
|
<!-- 根据事件类型渲染不同表单 -->
|
||||||
<WaterDisaster
|
<WaterDisaster v-if="eventType === 'water'" ref="waterDisasterRef" />
|
||||||
v-if="eventType === 'water'"
|
|
||||||
ref="waterDisasterRef"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 冰雪灾害表单(待实现) -->
|
<!-- 冰雪灾害表单(待实现) -->
|
||||||
<div v-else class="coming-soon">
|
<div v-else class="coming-soon">
|
||||||
@ -23,9 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
<!-- 提交按钮 -->
|
||||||
<van-button type="primary" class="footer-btn" @click="handleSubmit" :loading="submitting">
|
<van-button type="primary" class="footer-btn" @click="handleSubmit" :loading="submitting"> 提交 </van-button>
|
||||||
提交
|
|
||||||
</van-button>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -37,7 +32,7 @@ import PageContainer from '@/components/PageContainer.vue'
|
|||||||
import CurrentSite from '@/components/CurrentSite.vue'
|
import CurrentSite from '@/components/CurrentSite.vue'
|
||||||
import PanelItem from '@/components/PanelItem.vue'
|
import PanelItem from '@/components/PanelItem.vue'
|
||||||
import WaterDisaster from './WaterDisaster/WaterDisaster.vue'
|
import WaterDisaster from './WaterDisaster/WaterDisaster.vue'
|
||||||
import { request } from "@shared/utils/request";
|
import { request } from '@shared/utils/request'
|
||||||
import mockFormData from './waterDisasterFormData.json'
|
import mockFormData from './waterDisasterFormData.json'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -52,7 +47,7 @@ const title = ref(!isContinue ? '灾毁填报' : '灾毁续报')
|
|||||||
const eventType = ref('water')
|
const eventType = ref('water')
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formData = ref(mockFormData)
|
const formData = ref(route.query.mock ? mockFormData : {})
|
||||||
const waterDisasterRef = ref(null)
|
const waterDisasterRef = ref(null)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
|
|
||||||
@ -80,7 +75,7 @@ const handleSubmit = async () => {
|
|||||||
|
|
||||||
// 添加事件类型和站点信息
|
// 添加事件类型和站点信息
|
||||||
const submitData = {
|
const submitData = {
|
||||||
...formData,
|
...formData
|
||||||
// 可以在这里添加站点信息等其他数据
|
// 可以在这里添加站点信息等其他数据
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,16 +85,24 @@ const handleSubmit = async () => {
|
|||||||
data: submitData
|
data: submitData
|
||||||
})
|
})
|
||||||
|
|
||||||
if(res?.code === '00000') {
|
if (res?.code === '00000') {
|
||||||
showSuccessToast('提交成功')
|
showSuccessToast('提交成功')
|
||||||
|
if (submitData.event.needsRecovery) {
|
||||||
|
router.replace({
|
||||||
|
name: 'RebuildAdd',
|
||||||
|
params: {
|
||||||
|
data: res.data.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 提交成功后返回列表页
|
||||||
|
setTimeout(() => {
|
||||||
|
router.replace('/disasterManagement')
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showFailToast(res.message)
|
showFailToast(res.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交成功后返回列表页
|
|
||||||
setTimeout(() => {
|
|
||||||
router.replace('/disasterManagement')
|
|
||||||
}, 1000)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showFailToast('提交失败,请重试')
|
showFailToast('提交失败,请重试')
|
||||||
console.error('提交失败:', error)
|
console.error('提交失败:', error)
|
||||||
@ -126,10 +129,10 @@ const getDisasterDetail = async () => {
|
|||||||
// 接口返回 Data 结构
|
// 接口返回 Data 结构
|
||||||
const data = result.data
|
const data = result.data
|
||||||
const newFormData = {
|
const newFormData = {
|
||||||
...data,
|
...data,
|
||||||
lossList: null,
|
lossList: null,
|
||||||
report: formData.value.report,
|
report: formData.value.report,
|
||||||
fileList: null
|
fileList: null
|
||||||
}
|
}
|
||||||
waterDisasterRef.value.initFormData(newFormData)
|
waterDisasterRef.value.initFormData(newFormData)
|
||||||
} else {
|
} else {
|
||||||
@ -142,7 +145,7 @@ const getDisasterDetail = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if(route.query?.id) {
|
if (route.query?.id) {
|
||||||
getDisasterDetail()
|
getDisasterDetail()
|
||||||
} else {
|
} else {
|
||||||
waterDisasterRef.value.initFormData(formData.value)
|
waterDisasterRef.value.initFormData(formData.value)
|
||||||
|
|||||||
@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 损失计算弹窗 -->
|
||||||
|
<van-dialog v-model:show="visible" :title="lossItem?.title" show-cancel-button @confirm="confirm" @cancel="cancelLoss" confirm-button-text="确定" cancel-button-text="取消">
|
||||||
|
<div class="loss-dialog-content">
|
||||||
|
<!-- 立方计算 -->
|
||||||
|
<template v-if="lossItem.calc == 'cube'">
|
||||||
|
<van-field v-model="formData.length" :label="lossItem?.itemName + '长'" placeholder="请填写长度" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">米</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
|
||||||
|
<van-field v-model="formData.width" :label="lossItem?.itemName + '宽'" placeholder="请填写宽度" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">米</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
|
||||||
|
<van-field v-model="formData.height" :label="lossItem?.itemName + '高'" placeholder="请填写高度" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">米</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
|
||||||
|
<van-field v-model="formData.unitPrice" label="单价" placeholder="请填写单价" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">元</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
|
||||||
|
<van-field v-model="formData.totalAmount" :label="lossItem?.itemName + '损失金额'" placeholder="请填写金额" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">元</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 长度计算-->
|
||||||
|
<template v-else-if="lossItem.calc == 'length'">
|
||||||
|
<van-field v-model="formData.length" :label="lossItem?.itemName + '长度'" placeholder="请填写长度" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">米</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
<van-field v-model="formData.unitPrice" label="单价" placeholder="请填写单价" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">元</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
<van-field v-model="formData.totalAmount" :label="lossItem?.itemName + '损失金额'" placeholder="请填写金额" type="digit" clearable>
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">元</span>
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</van-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, reactive, watch, computed } from 'vue'
|
||||||
|
import { showToast } from 'vant'
|
||||||
|
import { getLossItem } from './LossMap'
|
||||||
|
|
||||||
|
// 弹窗显示状态
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
// 损失计算表单
|
||||||
|
const formData = ref({
|
||||||
|
length: '',
|
||||||
|
width: '',
|
||||||
|
height: '',
|
||||||
|
unitPrice: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const lossItem = ref(null)
|
||||||
|
|
||||||
|
// 立方计算
|
||||||
|
const cubeCalc = () => {
|
||||||
|
const l = parseFloat(formData.value.length)
|
||||||
|
const w = parseFloat(formData.value.width)
|
||||||
|
const h = parseFloat(formData.value.height)
|
||||||
|
const price = parseFloat(formData.value.unitPrice)
|
||||||
|
if (isNaN(l) || l <= 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (isNaN(w) || w <= 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (isNaN(h) || h <= 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (isNaN(price) || price < 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (price > 2000) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return l * w * h * price
|
||||||
|
}
|
||||||
|
|
||||||
|
// 长度计算
|
||||||
|
const lengthCalc = () => {
|
||||||
|
const l = parseFloat(formData.value.length)
|
||||||
|
const price = parseFloat(formData.value.unitPrice)
|
||||||
|
if (isNaN(l) || l <= 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (isNaN(price) || price < 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (price > 2000) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return l * price
|
||||||
|
}
|
||||||
|
const totalAmount = computed(() => {
|
||||||
|
if(lossItem.value?.calc == 'cube') return cubeCalc()
|
||||||
|
if(lossItem.value?.calc == 'length') return lengthCalc()
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(totalAmount, () => {
|
||||||
|
formData.value.totalAmount = totalAmount.value
|
||||||
|
})
|
||||||
|
// 显示弹窗
|
||||||
|
const show = (item) => {
|
||||||
|
lossItem.value = getLossItem(item.lossTypeCode)
|
||||||
|
|
||||||
|
formData.value.length = item.length
|
||||||
|
formData.value.width = item.width
|
||||||
|
formData.value.height = item.height
|
||||||
|
formData.value.unitPrice = item.unitPrice
|
||||||
|
formData.value.totalAmount = item.totalAmount
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证表单数据
|
||||||
|
const validateForm = () => {
|
||||||
|
const l = parseFloat(formData.length)
|
||||||
|
const w = parseFloat(formData.width)
|
||||||
|
const h = parseFloat(formData.height)
|
||||||
|
const price = parseFloat(formData.unitPrice)
|
||||||
|
|
||||||
|
if (isNaN(l) || l <= 0) {
|
||||||
|
showToast('请填写有效的塌方长度')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (isNaN(w) || w <= 0) {
|
||||||
|
showToast('请填写有效的塌方宽度')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (isNaN(h) || h <= 0) {
|
||||||
|
showToast('请填写有效的塌方高度')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (isNaN(price) || price < 0) {
|
||||||
|
showToast('请填写有效的单价')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (price > 2000) {
|
||||||
|
showToast('单价不能超过2000元')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认损失计算
|
||||||
|
const confirm = () => {
|
||||||
|
if (!formData.value.totalAmount) {
|
||||||
|
showToast('请填写损失金额')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
emit('confirm', formData.value)
|
||||||
|
// 重置表单
|
||||||
|
resetForm()
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消操作
|
||||||
|
const cancelLoss = () => {
|
||||||
|
resetForm()
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.value.length = ''
|
||||||
|
formData.value.width = ''
|
||||||
|
formData.value.height = ''
|
||||||
|
formData.value.unitPrice = ''
|
||||||
|
formData.value.totalAmount
|
||||||
|
}
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits(['confirm'])
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@ -1,172 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- 损失计算弹窗 -->
|
|
||||||
<van-dialog
|
|
||||||
v-model:show="visible"
|
|
||||||
:title="itemName + '损失信息'"
|
|
||||||
show-cancel-button
|
|
||||||
@confirm="confirm"
|
|
||||||
@cancel="cancelLoss"
|
|
||||||
confirm-button-text="确定"
|
|
||||||
cancel-button-text="取消"
|
|
||||||
>
|
|
||||||
<div class="loss-dialog-content">
|
|
||||||
<!-- 塌方长 -->
|
|
||||||
<van-field v-model="formData.length" :label="itemName + '长'" placeholder="请填写长度" type="digit" clearable>
|
|
||||||
<template #button>
|
|
||||||
<span class="field-unit">米</span>
|
|
||||||
</template>
|
|
||||||
</van-field>
|
|
||||||
|
|
||||||
<!-- 塌方宽 -->
|
|
||||||
<van-field v-model="formData.width" :label="itemName + '宽'" placeholder="请填写宽度" type="digit" clearable>
|
|
||||||
<template #button>
|
|
||||||
<span class="field-unit">米</span>
|
|
||||||
</template>
|
|
||||||
</van-field>
|
|
||||||
|
|
||||||
<!-- 塌方高 -->
|
|
||||||
<van-field v-model="formData.height" :label="itemName + '高'" placeholder="请填写高度" type="digit" clearable>
|
|
||||||
<template #button>
|
|
||||||
<span class="field-unit">米</span>
|
|
||||||
</template>
|
|
||||||
</van-field>
|
|
||||||
|
|
||||||
<!-- 单价 (0-2000元) -->
|
|
||||||
<van-field v-model="formData.unitPrice" label="单价(0-2000元)" placeholder="请填写单价" type="digit" clearable>
|
|
||||||
<template #button>
|
|
||||||
<span class="field-unit">元</span>
|
|
||||||
</template>
|
|
||||||
</van-field>
|
|
||||||
|
|
||||||
<van-field v-model="formData.totalAmount" :label="itemName + '损失金额'" placeholder="请填写金额" type="digit" clearable>
|
|
||||||
<template #button>
|
|
||||||
<span class="field-unit">元</span>
|
|
||||||
</template>
|
|
||||||
</van-field>
|
|
||||||
</div>
|
|
||||||
</van-dialog>
|
|
||||||
</template>
|
|
||||||
<script setup>
|
|
||||||
import { onMounted, ref, reactive, watch, computed } from 'vue'
|
|
||||||
import { showToast } from 'vant'
|
|
||||||
|
|
||||||
// 弹窗显示状态
|
|
||||||
const visible = ref(false)
|
|
||||||
|
|
||||||
// 损失计算表单
|
|
||||||
const formData = ref({
|
|
||||||
length: '',
|
|
||||||
width: '',
|
|
||||||
height: '',
|
|
||||||
unitPrice: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
const itemName = ref('')
|
|
||||||
|
|
||||||
const totalAmount = computed(() => {
|
|
||||||
const l = parseFloat(formData.value.length)
|
|
||||||
const w = parseFloat(formData.value.width)
|
|
||||||
const h = parseFloat(formData.value.height)
|
|
||||||
const price = parseFloat(formData.value.unitPrice)
|
|
||||||
if (isNaN(l) || l <= 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if (isNaN(w) || w <= 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if (isNaN(h) || h <= 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if (isNaN(price) || price < 0) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if (price > 2000) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return (l * w * h * price)
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(totalAmount, ()=>{
|
|
||||||
formData.value.totalAmount = totalAmount.value
|
|
||||||
})
|
|
||||||
// 显示弹窗
|
|
||||||
const show = (item) => {
|
|
||||||
console.log("🚀 ~ show ~ item:", item)
|
|
||||||
formData.value.length = item.length
|
|
||||||
formData.value.width = item.width
|
|
||||||
formData.value.height = item.height
|
|
||||||
formData.value.unitPrice = item.unitPrice
|
|
||||||
formData.value.totalAmount = item.totalAmount
|
|
||||||
itemName.value = getItemName(item)
|
|
||||||
visible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const getItemName = (item) => {
|
|
||||||
return item.lossTypeName
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证表单数据
|
|
||||||
const validateForm = () => {
|
|
||||||
const l = parseFloat(formData.length)
|
|
||||||
const w = parseFloat(formData.width)
|
|
||||||
const h = parseFloat(formData.height)
|
|
||||||
const price = parseFloat(formData.unitPrice)
|
|
||||||
|
|
||||||
if (isNaN(l) || l <= 0) {
|
|
||||||
showToast('请填写有效的塌方长度')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (isNaN(w) || w <= 0) {
|
|
||||||
showToast('请填写有效的塌方宽度')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (isNaN(h) || h <= 0) {
|
|
||||||
showToast('请填写有效的塌方高度')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (isNaN(price) || price < 0) {
|
|
||||||
showToast('请填写有效的单价')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (price > 2000) {
|
|
||||||
showToast('单价不能超过2000元')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确认损失计算
|
|
||||||
const confirm = () => {
|
|
||||||
if(!formData.value.totalAmount) {
|
|
||||||
showToast('请填写损失金额')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
emit('confirm', formData.value)
|
|
||||||
// 重置表单
|
|
||||||
resetForm()
|
|
||||||
visible.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消操作
|
|
||||||
const cancelLoss = () => {
|
|
||||||
resetForm()
|
|
||||||
visible.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置表单
|
|
||||||
const resetForm = () => {
|
|
||||||
formData.value.length = ''
|
|
||||||
formData.value.width = ''
|
|
||||||
formData.value.height = ''
|
|
||||||
formData.value.unitPrice = ''
|
|
||||||
formData.value.totalAmount
|
|
||||||
}
|
|
||||||
// 定义事件
|
|
||||||
const emit = defineEmits(['confirm'])
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
show
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
@ -1,24 +1,34 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="loss-list">
|
<div class="loss-list">
|
||||||
<template v-for="(item, index) in modelValue">
|
<template v-for="(item, index) in modelValue">
|
||||||
<van-field v-model="item.totalAmount" :label="getItemLabel(item)" placeholder="请填写" type="digit" @click="showCalculateDialog(item, index)">
|
<van-field v-if="getLossItem(item.lossTypeCode)" v-model="item.totalAmount" :label="getItemLabel(item)" placeholder="请填写" type="digit" @click="showCalculateDialog(item, index)">
|
||||||
<template #button>
|
<template #button>
|
||||||
<span class="field-unit">{{ item.unit }}</span>
|
<span class="field-unit">{{ item.unit }}</span>
|
||||||
<van-icon @click.stop="removeItem(index)" class="remove-icon" name="delete-o" />
|
<van-icon @click.stop="removeItem(index)" class="remove-icon" name="delete-o" />
|
||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
|
<template v-else>
|
||||||
|
<van-field v-model="item.totalAmount" :label="getItemLabel(item)" placeholder="请填写" type="digit">
|
||||||
|
<template #button>
|
||||||
|
<span class="field-unit">{{ item.unit }}</span>
|
||||||
|
<van-icon @click.stop="removeItem(index)" class="remove-icon" name="delete-o" />
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
|
<van-field v-if="item.lossTypeCode == 'OTHER_LOSS'" v-model="item.remark" label="其它损失描述" placeholder="请填写"></van-field>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<van-button size="small" block type="primary" plain @click="addLoss">添加损失</van-button>
|
<van-button size="small" block type="primary" plain @click="addLoss">添加损失</van-button>
|
||||||
<CubeCalculateDialog ref="cubeCalculateDialog" @confirm="confirmCalculate" />
|
<CalculateDialog ref="cubeCalculateDialog" @confirm="confirmCalculate" />
|
||||||
<LossPicker ref="lossPicker" :options="options" @confirm="confirmAddLoss" />
|
<LossPicker ref="lossPicker" :options="getOptions()" @confirm="confirmAddLoss" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import CubeCalculateDialog from './CubeCalculateDialog.vue'
|
import CalculateDialog from './CalculateDialog.vue'
|
||||||
import { request } from '@shared/utils/request'
|
import { request } from '@shared/utils/request'
|
||||||
import LossPicker from './LossPicker.vue'
|
import LossPicker from './LossPicker.vue'
|
||||||
|
import { getLossItem } from './LossMap'
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
@ -33,6 +43,19 @@ const lossPicker = ref(null)
|
|||||||
|
|
||||||
const options = ref({})
|
const options = ref({})
|
||||||
|
|
||||||
|
const getOptions = () => {
|
||||||
|
if(!options.value.loss) return []
|
||||||
|
const filteredOptions = options.value.loss.filter((item) => {
|
||||||
|
for(const valueItem of props.modelValue) {
|
||||||
|
if(item.value === valueItem.lossTypeId) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return filteredOptions
|
||||||
|
}
|
||||||
|
|
||||||
const dialogItemIndex = ref(null)
|
const dialogItemIndex = ref(null)
|
||||||
|
|
||||||
// 添加损失项
|
// 添加损失项
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
const lossMap = {
|
||||||
|
COLLAPSE_LOSS: {
|
||||||
|
title: '塌方损失信息',
|
||||||
|
itemName: '塌方',
|
||||||
|
calc: 'cube',
|
||||||
|
},
|
||||||
|
RETAINING_WALL_LOSS: {
|
||||||
|
title: '挡墙损失信息',
|
||||||
|
itemName: '挡墙',
|
||||||
|
calc: 'cube',
|
||||||
|
},
|
||||||
|
ROADBED_LOSS: {
|
||||||
|
title: '路基损失信息',
|
||||||
|
itemName: '路基',
|
||||||
|
calc: 'cube',
|
||||||
|
},
|
||||||
|
PAVEMENT_LOSS: {
|
||||||
|
title: '路面损失信息',
|
||||||
|
itemName: '路面',
|
||||||
|
calc: 'cube',
|
||||||
|
},
|
||||||
|
SLOPE_PROTECTION_LOSS: {
|
||||||
|
title: '护坡损失信息',
|
||||||
|
itemName: '护坡',
|
||||||
|
calc: 'cube',
|
||||||
|
},
|
||||||
|
BRIDGE_LOSS: {
|
||||||
|
title: '桥梁损失信息',
|
||||||
|
itemName: '桥梁',
|
||||||
|
calc: 'cube',
|
||||||
|
},
|
||||||
|
TUNNEL_LOSS: {
|
||||||
|
title: '隧道损失信息',
|
||||||
|
itemName: '隧道',
|
||||||
|
calc: 'length'
|
||||||
|
},
|
||||||
|
CULVERT_LOSS: {
|
||||||
|
title: '涵洞损失信息',
|
||||||
|
itemName: '涵洞',
|
||||||
|
calc: 'length'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLossItem = (code) => {
|
||||||
|
return lossMap[code]
|
||||||
|
}
|
||||||
@ -19,7 +19,7 @@ const pickerTitle = ref("请选择损失类型")
|
|||||||
|
|
||||||
|
|
||||||
const columns = computed(() => {
|
const columns = computed(() => {
|
||||||
return props.options.loss || []
|
return props.options || []
|
||||||
})
|
})
|
||||||
|
|
||||||
const showPicker = ref(false)
|
const showPicker = ref(false)
|
||||||
|
|||||||
@ -9,8 +9,8 @@
|
|||||||
<!-- 是否阻断 (event.isBlocked) -->
|
<!-- 是否阻断 (event.isBlocked) -->
|
||||||
<BasePicker v-model="formData.event.isBlocked" :options="blockedOptions" label="是否阻断" placeholder="请选择" />
|
<BasePicker v-model="formData.event.isBlocked" :options="blockedOptions" label="是否阻断" placeholder="请选择" />
|
||||||
|
|
||||||
<!-- 抢修进度 (event.repairProgress) -->
|
<!-- 抢险进度 (event.repairProgress) -->
|
||||||
<BasePicker v-model="formData.event.repairProgress" :options="repairProgressOptions" label="抢修进度" placeholder="请选择" />
|
<BasePicker v-model="formData.event.repairProgress" :options="repairProgressOptions" label="抢险进度" placeholder="请选择" />
|
||||||
|
|
||||||
<!-- 水毁处数 (event.damageCount) -->
|
<!-- 水毁处数 (event.damageCount) -->
|
||||||
<van-field v-model="formData.event.damageCount" label="水毁处数" placeholder="请填写" type="number" />
|
<van-field v-model="formData.event.damageCount" label="水毁处数" placeholder="请填写" type="number" />
|
||||||
@ -35,29 +35,9 @@
|
|||||||
<!-- 起点桩号 (event.startStakeNo) -->
|
<!-- 起点桩号 (event.startStakeNo) -->
|
||||||
<van-field v-model="formData.event.startStakeNo" label="起点桩号(K)" placeholder="请填写" />
|
<van-field v-model="formData.event.startStakeNo" label="起点桩号(K)" placeholder="请填写" />
|
||||||
|
|
||||||
<!-- 起点桩经纬度 (event.startStakeLng / startStakeLat) -->
|
|
||||||
<div class="coordinate-row">
|
|
||||||
<van-field v-model="formData.event.startStakeLng" label="起点桩经度" placeholder="经度" class="coordinate-field" />
|
|
||||||
<van-field v-model="formData.event.startStakeLat" label="起点桩纬度" placeholder="纬度" class="coordinate-field" />
|
|
||||||
</div>
|
|
||||||
<div class="calibrate-coord-btn" @click="calibrateStartCoord">
|
|
||||||
<van-icon name="location-o" />
|
|
||||||
<span>校准经纬度</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 止点桩号 (event.endStakeNo) -->
|
<!-- 止点桩号 (event.endStakeNo) -->
|
||||||
<van-field v-model="formData.event.endStakeNo" label="止点桩号(K)" placeholder="请填写" />
|
<van-field v-model="formData.event.endStakeNo" label="止点桩号(K)" placeholder="请填写" />
|
||||||
|
|
||||||
<!-- 止点桩经纬度 (event.endStakeLng / endStakeLat) -->
|
|
||||||
<div class="coordinate-row">
|
|
||||||
<van-field v-model="formData.event.endStakeLng" label="止点桩经度" placeholder="经度" class="coordinate-field" />
|
|
||||||
<van-field v-model="formData.event.endStakeLat" label="止点桩纬度" placeholder="纬度" class="coordinate-field" />
|
|
||||||
</div>
|
|
||||||
<div class="calibrate-coord-btn" @click="calibrateEndCoord">
|
|
||||||
<van-icon name="location-o" />
|
|
||||||
<span>校准经纬度</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 路况位置 (occurLocation) -->
|
<!-- 路况位置 (occurLocation) -->
|
||||||
<van-field v-model="formData.occurLocation" label="路况位置" placeholder="请填写" />
|
<van-field v-model="formData.occurLocation" label="路况位置" placeholder="请填写" />
|
||||||
|
|
||||||
@ -72,11 +52,11 @@
|
|||||||
<span class="measures-label">处置措施</span>
|
<span class="measures-label">处置措施</span>
|
||||||
<div class="measures-options">
|
<div class="measures-options">
|
||||||
<!-- 改为单选,使用 van-radio-group -->
|
<!-- 改为单选,使用 van-radio-group -->
|
||||||
<van-radio-group v-model="disposalMeasureValue" direction="horizontal">
|
<van-radio-group v-model="formData.report.disposalMeasures" direction="horizontal">
|
||||||
<van-radio name="halfClose">半幅封闭</van-radio>
|
<van-radio name="半幅封闭">半幅封闭</van-radio>
|
||||||
<van-radio name="fullClose">全副封闭</van-radio>
|
<van-radio name="全副封闭">全副封闭</van-radio>
|
||||||
<van-radio name="bypass">便道通行</van-radio>
|
<van-radio name="便道通行">便道通行</van-radio>
|
||||||
<van-radio name="normal">正常通行</van-radio>
|
<van-radio name="正常通行">正常通行</van-radio>
|
||||||
</van-radio-group>
|
</van-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -148,30 +128,12 @@
|
|||||||
</template>
|
</template>
|
||||||
</van-field>
|
</van-field>
|
||||||
<van-field v-model="formData.report.siteDescription" label="现场描述" placeholder="请填写" type="textarea" rows="2" autosize />
|
<van-field v-model="formData.report.siteDescription" label="现场描述" placeholder="请填写" type="textarea" rows="2" autosize />
|
||||||
|
<van-field label="附件" center>
|
||||||
|
<template #input>
|
||||||
|
<van-uploader accept="video/*,image/*" :modelValue="getFileList()" :after-read="afterRead" multiple :max-count="6" @delete="removeFile" />
|
||||||
|
</template>
|
||||||
|
</van-field>
|
||||||
</PanelItem>
|
</PanelItem>
|
||||||
|
|
||||||
<!-- 附件 (fileList) -->
|
|
||||||
<!-- <PanelItem title="附件">
|
|
||||||
<div class="attachment-tip">图片只能上传jpg/png文件,且不超过500kb;视频仅支持20s内的视频</div>
|
|
||||||
<div class="upload-area">
|
|
||||||
<van-uploader v-model="imageFileList" :after-read="afterImageRead" accept="image/jpeg,image/png" :max-size="500 * 1024" @oversize="onOversize" multiple :max-count="9">
|
|
||||||
<div class="upload-btn">
|
|
||||||
<van-icon name="photo-o" size="24" />
|
|
||||||
<span>上传图片</span>
|
|
||||||
</div>
|
|
||||||
</van-uploader>
|
|
||||||
<van-uploader v-model="videoFileList" :after-read="afterVideoRead" accept="video/*" :max-size="20 * 1024 * 1024" @oversize="onVideoOversize">
|
|
||||||
<div class="upload-btn">
|
|
||||||
<van-icon name="video-o" size="24" />
|
|
||||||
<span>上传视频</span>
|
|
||||||
</div>
|
|
||||||
</van-uploader>
|
|
||||||
</div>
|
|
||||||
<div v-if="videoFileList.length > 0 && videoFileList[0].content" class="video-preview">
|
|
||||||
<video :src="videoFileList[0].content" controls style="width: 100%; max-height: 200px"></video>
|
|
||||||
</div>
|
|
||||||
</PanelItem> -->
|
|
||||||
|
|
||||||
<PanelItem>
|
<PanelItem>
|
||||||
<!-- 是否需要恢复重建 (event.needsRecovery) -->
|
<!-- 是否需要恢复重建 (event.needsRecovery) -->
|
||||||
<BasePicker v-model="formData.event.needsRecovery" :options="needsRecoveryOptions" label="是否需要恢复重建" placeholder="请选择" />
|
<BasePicker v-model="formData.event.needsRecovery" :options="needsRecoveryOptions" label="是否需要恢复重建" placeholder="请选择" />
|
||||||
@ -186,28 +148,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { showToast, showFailToast } from 'vant'
|
import { showToast, showFailToast, showLoadingToast } from 'vant'
|
||||||
import PanelItem from '@/components/PanelItem.vue'
|
import PanelItem from '@/components/PanelItem.vue'
|
||||||
import BasePicker from '@/components/BasePicker.vue'
|
import BasePicker from '@/components/BasePicker.vue'
|
||||||
import BaseDatePicker from '@/components/BaseDatePicker.vue'
|
import BaseDatePicker from '@/components/BaseDatePicker.vue'
|
||||||
import LossList from './LossList.vue'
|
import LossList from './LossList.vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { request } from '@shared/utils/request'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
// 是否为续报
|
// 是否为续报
|
||||||
const isContinue = computed(() => route.query.isContinue)
|
const isContinue = computed(() => route.query.isContinue)
|
||||||
|
|
||||||
// 处置措施单选值(用于单选组件)
|
|
||||||
const disposalMeasureValue = ref('')
|
|
||||||
|
|
||||||
// 附件列表
|
// 表单数据 - 按 Request 接口结构定义,使用 ref 包装
|
||||||
const imageFileList = ref([])
|
const formData = ref({
|
||||||
const videoFileList = ref([])
|
|
||||||
|
|
||||||
// 表单数据 - 按 Request 接口结构定义
|
|
||||||
const formData = reactive({
|
|
||||||
// 顶层字段
|
// 顶层字段
|
||||||
occurLocation: '', // 发生地点
|
occurLocation: '', // 发生地点
|
||||||
occurTime: '', // 发生时间
|
occurTime: '', // 发生时间
|
||||||
@ -222,17 +179,13 @@ const formData = reactive({
|
|||||||
contactPhone: '', // 联系电话
|
contactPhone: '', // 联系电话
|
||||||
damageCount: '', // 水毁处数
|
damageCount: '', // 水毁处数
|
||||||
district: '', // 上报区县
|
district: '', // 上报区县
|
||||||
endStakeLat: '', // 止点纬度
|
|
||||||
endStakeLng: '', // 止点经度
|
|
||||||
endStakeNo: '', // 止点桩号
|
endStakeNo: '', // 止点桩号
|
||||||
estimatedRecoveryCost: '', // 恢复重建预估费用
|
estimatedRecoveryCost: '', // 恢复重建预估费用
|
||||||
inspectionMileage: '', // 巡查里程
|
inspectionMileage: '', // 巡查里程
|
||||||
isBlocked: '', // 是否阻断
|
isBlocked: '', // 是否阻断
|
||||||
needsRecovery: '', // 是否需要恢复重建
|
needsRecovery: '', // 是否需要恢复重建
|
||||||
repairProgress: '', // 抢修进度
|
repairProgress: '', // 抢险进度
|
||||||
reporterUnit: '', // 填报单位
|
reporterUnit: '', // 填报单位
|
||||||
startStakeLat: '', // 起点纬度
|
|
||||||
startStakeLng: '', // 起点经度
|
|
||||||
startStakeNo: '' // 起点桩号
|
startStakeNo: '' // 起点桩号
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -261,65 +214,16 @@ const formData = reactive({
|
|||||||
fileList: []
|
fileList: []
|
||||||
})
|
})
|
||||||
|
|
||||||
// 监听处置措施单选值变化,直接赋值给 report.disposalMeasures
|
const getFileList = () => {
|
||||||
watch(disposalMeasureValue, (newVal) => {
|
const fileList = formData.value.fileList?.map((item) => {
|
||||||
formData.report.disposalMeasures = newVal
|
return {
|
||||||
})
|
url: item.fileUrl,
|
||||||
|
name: item.fileName
|
||||||
// 监听附件变化,同步到 fileList
|
|
||||||
watch(
|
|
||||||
imageFileList,
|
|
||||||
(newVal) => {
|
|
||||||
// 转换为接口需要的格式
|
|
||||||
formData.fileList = [
|
|
||||||
...imageFileList.value.map((f) => ({
|
|
||||||
fileName: f.file?.name || '',
|
|
||||||
fileSize: f.file?.size || 0,
|
|
||||||
fileType: 1, // 1-图片
|
|
||||||
fileUrl: f.content || ''
|
|
||||||
})),
|
|
||||||
...videoFileList.value.map((f) => ({
|
|
||||||
fileName: f.file?.name || '',
|
|
||||||
fileSize: f.file?.size || 0,
|
|
||||||
fileType: 2, // 2-视频
|
|
||||||
fileUrl: f.content || ''
|
|
||||||
}))
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
|
||||||
videoFileList,
|
|
||||||
(newVal) => {
|
|
||||||
formData.fileList = [
|
|
||||||
...imageFileList.value.map((f) => ({
|
|
||||||
fileName: f.file?.name || '',
|
|
||||||
fileSize: f.file?.size || 0,
|
|
||||||
fileType: 1,
|
|
||||||
fileUrl: f.content || ''
|
|
||||||
})),
|
|
||||||
...newVal.map((f) => ({
|
|
||||||
fileName: f.file?.name || '',
|
|
||||||
fileSize: f.file?.size || 0,
|
|
||||||
fileType: 2,
|
|
||||||
fileUrl: f.content || ''
|
|
||||||
}))
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// 从 report.disposalMeasures 初始化处置措施单选值
|
|
||||||
watch(
|
|
||||||
() => formData.report.disposalMeasures,
|
|
||||||
(newVal) => {
|
|
||||||
if (newVal && typeof newVal === 'string') {
|
|
||||||
disposalMeasureValue.value = newVal
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
{ immediate: true }
|
|
||||||
)
|
return fileList
|
||||||
|
}
|
||||||
|
|
||||||
// BasePicker 选项数据
|
// BasePicker 选项数据
|
||||||
const roadConditionOptions = [
|
const roadConditionOptions = [
|
||||||
@ -337,9 +241,9 @@ const blockedOptions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const repairProgressOptions = [
|
const repairProgressOptions = [
|
||||||
{ label: '未抢修', value: '未抢修' },
|
{ label: '未抢险', value: '未抢险' },
|
||||||
{ label: '抢修中', value: '抢修中' },
|
{ label: '抢险中', value: '抢险中' },
|
||||||
{ label: '已完成', value: '已完成' },
|
{ label: '已完成', value: '已完成' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const needsRecoveryOptions = [
|
const needsRecoveryOptions = [
|
||||||
@ -353,21 +257,16 @@ const maxDate = new Date(2030, 11, 31)
|
|||||||
|
|
||||||
const initFormData = (newVal) => {
|
const initFormData = (newVal) => {
|
||||||
if (newVal && Object.keys(newVal).length > 0) {
|
if (newVal && Object.keys(newVal).length > 0) {
|
||||||
// 深度合并数据
|
// 深度合并数据 - 直接替换整个对象
|
||||||
Object.assign(formData, {
|
formData.value = {
|
||||||
occurLocation: newVal.occurLocation || '',
|
occurLocation: newVal.occurLocation || '',
|
||||||
occurTime: newVal.occurTime || '',
|
occurTime: newVal.occurTime || '',
|
||||||
roadConditionType: newVal.roadConditionType || '',
|
roadConditionType: newVal.roadConditionType || '',
|
||||||
routeNo: newVal.routeNo || '',
|
routeNo: newVal.routeNo || '',
|
||||||
event: { ...formData.event, ...(newVal.event || {}) },
|
event: { ...formData.value.event, ...(newVal.event || {}) },
|
||||||
report: { ...formData.report, ...(newVal.report || {}) },
|
report: { ...formData.value.report, ...(newVal.report || {}) },
|
||||||
lossList: newVal.lossList || [],
|
lossList: newVal.lossList || [],
|
||||||
fileList: newVal.fileList || []
|
fileList: newVal.fileList || []
|
||||||
})
|
|
||||||
|
|
||||||
// 初始化处置措施单选值(直接赋值,不再需要 split)
|
|
||||||
if (newVal.report?.disposalMeasures) {
|
|
||||||
disposalMeasureValue.value = newVal.report.disposalMeasures
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,24 +275,10 @@ const initFormData = (newVal) => {
|
|||||||
const calibrateTime = () => {
|
const calibrateTime = () => {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const formatted = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
const formatted = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||||
formData.occurTime = formatted
|
formData.value.occurTime = formatted
|
||||||
showToast('时间已校准为当前时间')
|
showToast('时间已校准为当前时间')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校准起点经纬度
|
|
||||||
const calibrateStartCoord = () => {
|
|
||||||
formData.event.startStakeLng = '108.41763025'
|
|
||||||
formData.event.startStakeLat = '108.41763025'
|
|
||||||
showToast('起点经纬度已校准')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 校准止点经纬度
|
|
||||||
const calibrateEndCoord = () => {
|
|
||||||
formData.event.endStakeLng = '108.41763025'
|
|
||||||
formData.event.endStakeLat = '108.41763025'
|
|
||||||
showToast('止点经纬度已校准')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 图片上传处理
|
// 图片上传处理
|
||||||
const afterImageRead = (file) => {
|
const afterImageRead = (file) => {
|
||||||
console.log('图片上传:', file)
|
console.log('图片上传:', file)
|
||||||
@ -411,22 +296,157 @@ const onVideoOversize = () => {
|
|||||||
showFailToast('视频大小不能超过20MB')
|
showFailToast('视频大小不能超过20MB')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 isVideoFile = (file) => {
|
||||||
|
// 根据url后缀判断
|
||||||
|
const videoExtensions = /\.(mp4|avi|mov|wmv|flv|mkv)$/i
|
||||||
|
if (file.fileUrl && videoExtensions.test(file.fileUrl)) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断图片是否可以上传
|
||||||
|
* @param {File} file - 图片文件
|
||||||
|
* @returns {boolean} 是否允许上传
|
||||||
|
*/
|
||||||
|
const isValidImage = (file) => {
|
||||||
|
// 校验文件类型
|
||||||
|
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
|
||||||
|
|
||||||
|
if (!isJpgOrPng) {
|
||||||
|
showFailToast('只能上传 JPG/PNG 格式的图片!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验文件大小(500KB = 500 * 1024 bytes)
|
||||||
|
const isLt500k = file.size / 1024 < 500
|
||||||
|
|
||||||
|
if (!isLt500k) {
|
||||||
|
showFailToast(`图片大小不能超过 500KB!当前大小:${(file.size / 1024).toFixed(2)}KB`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断视频是否可以上传
|
||||||
|
* @param {File} file - 视频文件
|
||||||
|
* @returns {boolean} 是否允许上传
|
||||||
|
*/
|
||||||
|
const isValidVideo = (file) => {
|
||||||
|
// 校验文件类型
|
||||||
|
const allowedTypes = ['video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/webm']
|
||||||
|
const isValidType = allowedTypes.includes(file.type)
|
||||||
|
|
||||||
|
if (!isValidType) {
|
||||||
|
showFailToast('请上传有效的视频文件(MP4、MOV、AVI、WEBM格式)!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验文件大小(50MB = 50 * 1024 * 1024 bytes)
|
||||||
|
const maxSize = 50 * 1024 * 1024
|
||||||
|
const isValidSize = file.size <= maxSize
|
||||||
|
|
||||||
|
if (!isValidSize) {
|
||||||
|
showFailToast(`视频大小不能超过 50MB!当前大小:${(file.size / 1024 / 1024).toFixed(2)}MB`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一的上传前校验
|
||||||
|
* @param {File} file - 上传的文件
|
||||||
|
* @returns {boolean} 是否允许上传
|
||||||
|
*/
|
||||||
|
const checkFile = (file) => {
|
||||||
|
// 判断是否为图片
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
return isValidImage(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否为视频
|
||||||
|
if (file.type.startsWith('video/')) {
|
||||||
|
return isValidVideo(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
showFailToast('只支持图片和视频文件!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const afterRead = async (options) => {
|
||||||
|
const file = options.file
|
||||||
|
if (!checkFile(file)) return
|
||||||
|
const toast = showLoadingToast({
|
||||||
|
message: '上传中...',
|
||||||
|
forbidClick: true,
|
||||||
|
duration: 0 // 设置为0表示不会自动关闭
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const uploadFormData = new FormData()
|
||||||
|
uploadFormData.append('file', file)
|
||||||
|
const res = await request({
|
||||||
|
url: '/snow-ops-platform/file/upload',
|
||||||
|
method: 'post',
|
||||||
|
data: uploadFormData
|
||||||
|
})
|
||||||
|
toast.close()
|
||||||
|
if (res.code === '00000') {
|
||||||
|
const name = file.name
|
||||||
|
let type = isImageFile(file) ? 1 : isVideoFile(file) ? 2 : 3
|
||||||
|
|
||||||
|
const url = res.data
|
||||||
|
const fileData = {
|
||||||
|
fileName: name,
|
||||||
|
fileUrl: url,
|
||||||
|
fileType: type,
|
||||||
|
fileSize: file.size
|
||||||
|
}
|
||||||
|
if (!formData.value.fileList) formData.value.fileList = []
|
||||||
|
formData.value.fileList.push(fileData)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.close()
|
||||||
|
showToast({
|
||||||
|
type: 'fail',
|
||||||
|
message: error.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeFile = (file, index) => {
|
||||||
|
// 删除文件
|
||||||
|
formData.value.fileList.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
// 暴露验证方法
|
// 暴露验证方法
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
if (!formData.occurTime) {
|
if (!formData.value.occurTime) {
|
||||||
showToast('请填写发生时间')
|
showToast('请填写发生时间')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!formData.routeNo) {
|
if (!formData.value.routeNo) {
|
||||||
showToast('请填写线路编号')
|
showToast('请填写线路编号')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取表单数据
|
// 获取表单数据 - 返回 formData.value 的副本
|
||||||
const getFormData = () => {
|
const getFormData = () => {
|
||||||
return { ...formData }
|
return { ...formData.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 暴露方法给父组件
|
// 暴露方法给父组件
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"title": "G242金铃乡老窖坪发生积雪",
|
|
||||||
"status": "未解除",
|
|
||||||
"occurTime": "2025/10/10 20:29",
|
|
||||||
"estimateRecoverTime": "2025/10/10 20:29",
|
|
||||||
"disasterType": "积雪"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"title": "S521白鹿镇X发生边坡坍塌",
|
|
||||||
"status": "已解除",
|
|
||||||
"occurTime": "2025/10/10 20:29",
|
|
||||||
"estimateRecoverTime": "2025/10/10 20:29",
|
|
||||||
"disasterType": "边坡坍塌"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"title": "彭水S523发生边坡坍塌",
|
|
||||||
"status": "未解除",
|
|
||||||
"occurTime": "2025/10/10 20:29",
|
|
||||||
"estimateRecoverTime": "2025/10/10 20:29",
|
|
||||||
"disasterType": "路基沉陷"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 4,
|
|
||||||
"title": "梁平蟠龙镇G318发生山体滑坡",
|
|
||||||
"status": "已解除",
|
|
||||||
"occurTime": "2025/10/10 20:29",
|
|
||||||
"estimateRecoverTime": "2025/10/10 20:29",
|
|
||||||
"disasterType": "山体滑坡"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5,
|
|
||||||
"title": "重庆市大足区XX县G201行道树倒塌",
|
|
||||||
"status": "已解除",
|
|
||||||
"occurTime": "2025/10/10 20:29",
|
|
||||||
"estimateRecoverTime": "2025/10/10 20:29",
|
|
||||||
"disasterType": "行道树倒塌"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"occurLocation": "G108国道 K2250+300处",
|
"occurLocation": "G108国道 K2250+300处",
|
||||||
"occurTime": "2024-07-15 14:30:00",
|
"occurTime": null,
|
||||||
"roadConditionType": "国道",
|
"roadConditionType": "国道",
|
||||||
"routeNo": "G108",
|
"routeNo": "G108",
|
||||||
"event": {
|
"event": {
|
||||||
@ -10,17 +10,13 @@
|
|||||||
"contactPhone": "13812345678",
|
"contactPhone": "13812345678",
|
||||||
"damageCount": 3,
|
"damageCount": 3,
|
||||||
"district": "武侯区",
|
"district": "武侯区",
|
||||||
"endStakeLat": "30.658712",
|
|
||||||
"endStakeLng": "104.082356",
|
|
||||||
"endStakeNo": "K2251+200",
|
"endStakeNo": "K2251+200",
|
||||||
"estimatedRecoveryCost": 120.5,
|
"estimatedRecoveryCost": 120.5,
|
||||||
"inspectionMileage": 25.6,
|
"inspectionMileage": 25.6,
|
||||||
"isBlocked": true,
|
"isBlocked": true,
|
||||||
"needsRecovery": true,
|
"needsRecovery": true,
|
||||||
"repairProgress": "抢修中",
|
"repairProgress": "抢险中",
|
||||||
"reporterUnit": "武侯区交通运输局",
|
"reporterUnit": "武侯区交通运输局",
|
||||||
"startStakeLat": "30.652145",
|
|
||||||
"startStakeLng": "104.075632",
|
|
||||||
"startStakeNo": "K2250+300"
|
"startStakeNo": "K2250+300"
|
||||||
},
|
},
|
||||||
"report": {
|
"report": {
|
||||||
@ -28,9 +24,9 @@
|
|||||||
"strandedPersonCount": 12,
|
"strandedPersonCount": 12,
|
||||||
"deadCount": 0,
|
"deadCount": 0,
|
||||||
"strandedVehicleCount": 12,
|
"strandedVehicleCount": 12,
|
||||||
"disposalMeasures": "halfClose,bypass",
|
"disposalMeasures": null,
|
||||||
"actualRecoverTime": "2024-07-17 12:00:00",
|
"actualRecoverTime": null,
|
||||||
"expectRecoverTime": "2024-07-18 18:00:00",
|
"expectRecoverTime": null,
|
||||||
"injuredCount": 1,
|
"injuredCount": 1,
|
||||||
"investedFunds": 35.8,
|
"investedFunds": 35.8,
|
||||||
"investedMachinery": 6,
|
"investedMachinery": 6,
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<!-- FileUpload.vue -->
|
<!-- FileUpload.vue -->
|
||||||
<template>
|
<template>
|
||||||
<div class="file-upload">
|
<div class="file-upload">
|
||||||
<UploadBlock v-model="modelValue" :type="type" :multiple="multiple" :limit="limit" :placeholder="placeholder" :customFileType="fileType" />
|
<UploadBlock v-if="!readonly" v-model="modelValue" :type="type" :multiple="multiple" :limit="limit" :placeholder="placeholder" :customFileType="fileType" />
|
||||||
<PreviewBlock v-model:file-list="modelValue" :type="type" />
|
<PreviewBlock :readonly="readonly" v-model:file-list="modelValue" :type="type" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -29,10 +29,10 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="preview-info">
|
<!-- <div class="preview-info">
|
||||||
<span class="file-name" :title="file.fileName">{{ file.fileName }}</span>
|
<span class="file-name" :title="file.fileName">{{ file.fileName }}</span>
|
||||||
<span class="file-size">{{ formatFileSize(file.fileSize) }}</span>
|
<span class="file-size">{{ formatFileSize(file.fileSize) }}</span>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="preview-actions" v-if="!readonly">
|
<div class="preview-actions" v-if="!readonly">
|
||||||
<el-button
|
<el-button
|
||||||
|
|||||||
@ -99,7 +99,6 @@ const getAccept = () => {
|
|||||||
|
|
||||||
const uploadFiles = async (event) => {
|
const uploadFiles = async (event) => {
|
||||||
const file = event.target.files[0]
|
const file = event.target.files[0]
|
||||||
const name = file.name
|
|
||||||
try {
|
try {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
@ -110,8 +109,9 @@ const uploadFiles = async (event) => {
|
|||||||
})
|
})
|
||||||
if (res.code === '00000') {
|
if (res.code === '00000') {
|
||||||
let fileType = 3
|
let fileType = 3
|
||||||
if (props.type == 'image') fileType = 1
|
const name = file.name
|
||||||
if (props.type == 'video') fileType = 2
|
if(props.type == 'image') fileType = 1
|
||||||
|
if(props.type == 'video') fileType = 2
|
||||||
|
|
||||||
const url = res.data
|
const url = res.data
|
||||||
const fileData = {
|
const fileData = {
|
||||||
|
|||||||
142
packages/screen/src/component/VideoPreviewDialog.vue
Normal file
142
packages/screen/src/component/VideoPreviewDialog.vue
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="dialogTitle"
|
||||||
|
width="80%"
|
||||||
|
:show-close="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="true"
|
||||||
|
class="video-preview-dialog"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div class="video-container">
|
||||||
|
<video
|
||||||
|
ref="videoRef"
|
||||||
|
:src="currentVideoUrl"
|
||||||
|
class="video-player"
|
||||||
|
controls
|
||||||
|
autoplay
|
||||||
|
controlslist="nodownload"
|
||||||
|
></video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="handleClose" type="danger" plain>关闭预览</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch, onUnmounted } from 'vue'
|
||||||
|
|
||||||
|
// 当前视频URL
|
||||||
|
const currentVideoUrl = ref('')
|
||||||
|
|
||||||
|
// 弹窗标题
|
||||||
|
const dialogTitle = ref('视频预览')
|
||||||
|
|
||||||
|
// 弹窗显示状态
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
|
||||||
|
// DOM 引用
|
||||||
|
const videoRef = ref(null)
|
||||||
|
|
||||||
|
// 显示弹窗
|
||||||
|
const show = (options) => {
|
||||||
|
if (!options || !options.url) {
|
||||||
|
console.error('视频URL不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
currentVideoUrl.value = options.url
|
||||||
|
dialogTitle.value = options.title || '视频预览'
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
const close = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 延迟清空视频URL,避免视频元素销毁时的问题
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!dialogVisible.value) {
|
||||||
|
currentVideoUrl.value = ''
|
||||||
|
dialogTitle.value = '视频预览'
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 键盘事件处理
|
||||||
|
const handleKeydown = (event) => {
|
||||||
|
if (event.key === 'Escape' && dialogVisible.value) {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听弹窗关闭
|
||||||
|
const handleClose = () => {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听弹窗显示状态,添加/移除键盘监听
|
||||||
|
watch(dialogVisible, (visible) => {
|
||||||
|
if (visible) {
|
||||||
|
document.addEventListener('keydown', handleKeydown)
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('keydown', handleKeydown)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件卸载时清理
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.removeEventListener('keydown', handleKeydown)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 对外暴露方法
|
||||||
|
defineExpose({
|
||||||
|
show,
|
||||||
|
close
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.video-preview-dialog :deep(.el-dialog) {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-preview-dialog :deep(.el-dialog__body) {
|
||||||
|
padding: 0;
|
||||||
|
max-height: 70vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 70vh;
|
||||||
|
object-fit: contain;
|
||||||
|
outline: none;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-preview-dialog :deep(.el-dialog__footer) {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -216,7 +216,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/disasterManagement',
|
path: '/disasterManagement',
|
||||||
name: 'disasterManagement',
|
name: 'disasterManagement',
|
||||||
component: () => import('../views/DisasterManagement/DisasterManagement.vue'),
|
component: () => import('../views/DisasterManagement/DisasterManagementPC.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '灾害巡检事件',
|
title: '灾害巡检事件',
|
||||||
breadcrumb: true
|
breadcrumb: true
|
||||||
@ -225,7 +225,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/disasterReport',
|
path: '/disasterReport',
|
||||||
name: 'DisasterReport',
|
name: 'DisasterReport',
|
||||||
component: () => import('../views/DisasterManagement/DisasterReport/DisasterReport.vue'),
|
component: () => import('../views/DisasterManagement/DisasterReport/DisasterReportPC.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '灾毁事件填报',
|
title: '灾毁事件填报',
|
||||||
breadcrumb: true
|
breadcrumb: true
|
||||||
@ -234,7 +234,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/waterDisasterDetail',
|
path: '/waterDisasterDetail',
|
||||||
name: 'WaterDisasterDetail',
|
name: 'WaterDisasterDetail',
|
||||||
component: () => import('../views/DisasterManagement/DisasterDetail/WaterDisasterDetail.vue'),
|
component: () => import('../views/DisasterManagement/DisasterDetail/WaterDisasterDetailPC.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '水毁事件详情',
|
title: '水毁事件详情',
|
||||||
breadcrumb: true
|
breadcrumb: true
|
||||||
|
|||||||
@ -3,19 +3,19 @@
|
|||||||
<!-- 合并后的单个卡片 -->
|
<!-- 合并后的单个卡片 -->
|
||||||
<el-card class="form-card" shadow="never">
|
<el-card class="form-card" shadow="never">
|
||||||
<div slot="header" class="card-header">
|
<div slot="header" class="card-header">
|
||||||
<span>续保信息</span>
|
<span>续报信息</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 所有表单项合并到一个区域,每行一个 -->
|
<!-- 所有表单项合并到一个区域,每行一个 -->
|
||||||
<el-form :model="formData" label-width="120px" size="small">
|
<el-form :model="formData" label-width="120px" size="small">
|
||||||
<!-- 处置措施 -->
|
<!-- 处置措施 -->
|
||||||
<el-form-item label="处置措施">
|
<el-form-item label="处置措施">
|
||||||
<el-radio-group v-model="disposalMeasureValue">
|
<el-select v-model="formData.report.disposalMeasures">
|
||||||
<el-radio label="halfClose">半幅封闭</el-radio>
|
<el-option label="半幅封闭" value="半幅封闭" />
|
||||||
<el-radio label="fullClose">全副封闭</el-radio>
|
<el-option label="全副封闭" value="全副封闭" />
|
||||||
<el-radio label="bypass">便道通行</el-radio>
|
<el-option label="便道通行" value="便道通行" />
|
||||||
<el-radio label="normal">正常通行</el-radio>
|
<el-option label="正常通行" value="正常通行" />
|
||||||
</el-radio-group>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- 预计恢复时间 -->
|
<!-- 预计恢复时间 -->
|
||||||
@ -138,7 +138,7 @@
|
|||||||
import { ref, reactive, watch, computed } from 'vue'
|
import { ref, reactive, watch, computed } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { request } from '@shared/utils/request'
|
import { request } from '@shared/utils/request'
|
||||||
import LossList from '../DisasterReport/LossList.vue'
|
import LossList from '../DisasterReport/WaterDisasterLossListPC.vue'
|
||||||
|
|
||||||
// Props 定义
|
// Props 定义
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -164,8 +164,6 @@ const formData = reactive({
|
|||||||
contactPhone: '',
|
contactPhone: '',
|
||||||
damageCount: '',
|
damageCount: '',
|
||||||
district: '',
|
district: '',
|
||||||
endStakeLat: '',
|
|
||||||
endStakeLng: '',
|
|
||||||
endStakeNo: '',
|
endStakeNo: '',
|
||||||
estimatedRecoveryCost: '',
|
estimatedRecoveryCost: '',
|
||||||
inspectionMileage: '',
|
inspectionMileage: '',
|
||||||
@ -173,8 +171,6 @@ const formData = reactive({
|
|||||||
needsRecovery: '',
|
needsRecovery: '',
|
||||||
repairProgress: '',
|
repairProgress: '',
|
||||||
reporterUnit: '',
|
reporterUnit: '',
|
||||||
startStakeLat: '',
|
|
||||||
startStakeLng: '',
|
|
||||||
startStakeNo: ''
|
startStakeNo: ''
|
||||||
},
|
},
|
||||||
report: {
|
report: {
|
||||||
@ -216,8 +212,8 @@ const blockedOptions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const repairProgressOptions = [
|
const repairProgressOptions = [
|
||||||
{ label: '未抢修', value: '未抢修' },
|
{ label: '未抢险', value: '未抢险' },
|
||||||
{ label: '抢修中', value: '抢修中' },
|
{ label: '抢险中', value: '抢险中' },
|
||||||
{ label: '已完成', value: '已完成' }
|
{ label: '已完成', value: '已完成' }
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -295,20 +291,6 @@ const calibrateTime = () => {
|
|||||||
ElMessage.success('时间已校准为当前时间')
|
ElMessage.success('时间已校准为当前时间')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校准起点经纬度
|
|
||||||
const calibrateStartCoord = () => {
|
|
||||||
formData.event.startStakeLng = '108.41763025'
|
|
||||||
formData.event.startStakeLat = '108.41763025'
|
|
||||||
ElMessage.success('起点经纬度已校准')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 校准止点经纬度
|
|
||||||
const calibrateEndCoord = () => {
|
|
||||||
formData.event.endStakeLng = '108.41763025'
|
|
||||||
formData.event.endStakeLat = '108.41763025'
|
|
||||||
ElMessage.success('止点经纬度已校准')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单验证
|
// 表单验证
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
if (!formData.occurTime) {
|
if (!formData.occurTime) {
|
||||||
@ -341,8 +323,6 @@ const resetForm = () => {
|
|||||||
contactPhone: '',
|
contactPhone: '',
|
||||||
damageCount: '',
|
damageCount: '',
|
||||||
district: '',
|
district: '',
|
||||||
endStakeLat: '',
|
|
||||||
endStakeLng: '',
|
|
||||||
endStakeNo: '',
|
endStakeNo: '',
|
||||||
estimatedRecoveryCost: '',
|
estimatedRecoveryCost: '',
|
||||||
inspectionMileage: '',
|
inspectionMileage: '',
|
||||||
@ -350,8 +330,6 @@ const resetForm = () => {
|
|||||||
needsRecovery: '',
|
needsRecovery: '',
|
||||||
repairProgress: '',
|
repairProgress: '',
|
||||||
reporterUnit: '',
|
reporterUnit: '',
|
||||||
startStakeLat: '',
|
|
||||||
startStakeLng: '',
|
|
||||||
startStakeNo: ''
|
startStakeNo: ''
|
||||||
},
|
},
|
||||||
report: {
|
report: {
|
||||||
@ -426,8 +404,6 @@ defineExpose({
|
|||||||
getFormData,
|
getFormData,
|
||||||
resetForm,
|
resetForm,
|
||||||
calibrateTime,
|
calibrateTime,
|
||||||
calibrateStartCoord,
|
|
||||||
calibrateEndCoord
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -47,16 +47,25 @@
|
|||||||
<el-row :gutter="20" class="info-row">
|
<el-row :gutter="20" class="info-row">
|
||||||
<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.event?.repairProgress || '-' }}</span>
|
<span class="info-value">{{ detailData.event?.repairProgress || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</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">
|
<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.event?.damageCount || 0 }}</span>
|
<span class="info-value">{{ detailData.event?.damageCount || 0 }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20" class="info-row">
|
||||||
<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>
|
||||||
@ -66,66 +75,24 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20" class="info-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.occurTime || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">线路编号:</span>
|
|
||||||
<span class="info-value">{{ detailData.routeNo || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<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.occurLocation || '-' }}</span>
|
<span class="info-value">{{ detailData.occurLocation || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="20" class="info-row">
|
|
||||||
<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.event?.startStakeNo || '-' }}</span>
|
<span class="info-value">{{ detailData.event?.startStakeNo || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">起点桩经度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.startStakeLng || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">起点桩纬度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.startStakeLat || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-row :gutter="20" class="info-row">
|
|
||||||
<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.event?.endStakeNo || '-' }}</span>
|
<span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">止点桩经度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.endStakeLng || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">止点桩纬度:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.endStakeLat || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20" class="info-row">
|
<el-row :gutter="20" class="info-row">
|
||||||
@ -141,55 +108,37 @@
|
|||||||
<span class="info-value">{{ detailData.event?.blockedPointName || '-' }}</span>
|
<span class="info-value">{{ detailData.event?.blockedPointName || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<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-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20" class="info-row">
|
<el-row :gutter="20" class="info-row">
|
||||||
<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.event?.inspectionMileage ? detailData.event.inspectionMileage + '公里' : '-' }}</span>
|
<span class="info-value">{{ detailData.event?.district || '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</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">
|
<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.event?.needsRecovery ? '是' : '否' }}</span>
|
<span class="info-value">{{ detailData.event?.needsRecovery ? '是' : '否' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="16">
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<span class="info-label">恢复重建预估费用:</span>
|
<span class="info-label">恢复重建预估费用:</span>
|
||||||
<span class="info-value">{{ detailData.event?.estimatedRecoveryCost ? detailData.event.estimatedRecoveryCost + '万元' : '-' }}</span>
|
<span class="info-value">{{ detailData.event?.estimatedRecoveryCost ? detailData.event.estimatedRecoveryCost + '万元' : '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</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?.contactPerson || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">联系电话:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.contactPhone || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">填报单位:</span>
|
|
||||||
<span class="info-value">{{ detailData.event?.reporterUnit || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<!-- 填报信息卡片 -->
|
<!-- 填报信息卡片 -->
|
||||||
@ -204,52 +153,71 @@
|
|||||||
<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.reportTime || '-' }}</span>
|
||||||
</div>
|
</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-list">
|
<div class="info-item">
|
||||||
<div class="info-item">
|
<span class="info-label">填报人:</span>
|
||||||
<span class="info-label">处置情况:</span>
|
<span class="info-value">{{ report.reporterName ? report.reporterName : '-' }}</span>
|
||||||
<span class="info-value">{{ formatDisposalMeasures(report.disposalMeasures) || '-' }}</span>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">塌方及损失:</span>
|
|
||||||
<span class="info-value">{{ getLossDescription(report) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">路产损失:</span>
|
|
||||||
<span class="info-value">{{ report.totalLossAmount ? report.totalLossAmount + '万元' : '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">有无车辆滞留:</span>
|
|
||||||
<span class="info-value">{{ getVehicleStrandedText(report) }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">滞留车辆:</span>
|
|
||||||
<span class="info-value">{{ report.strandedVehicleCount || 0 }}辆</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.actualRecoverTime || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<span class="info-label">现场描述:</span>
|
|
||||||
<span class="info-value">{{ report.siteDescription || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 附件 -->
|
<div class="info-item">
|
||||||
<div v-if="report.fileList && report.fileList.length > 0" class="info-item attachment-item">
|
<span class="info-label">联系电话:</span>
|
||||||
<span class="info-label">附件:</span>
|
<span class="info-value">{{ report.phone ? report.phone : '-' }}</span>
|
||||||
<div class="attachment-list">
|
</div>
|
||||||
<el-link v-for="(file, fileIndex) in report.fileList" :key="fileIndex" :underline="false" @click="previewFile(file)" class="attachment-link">
|
|
||||||
<el-icon><Picture v-if="file.fileType === 1" /><VideoCamera v-else /></el-icon>
|
|
||||||
<span class="file-name">{{ file.fileName }}</span>
|
|
||||||
</el-link>
|
|
||||||
</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">
|
||||||
|
<span class="info-label">已投入机械:</span>
|
||||||
|
<span class="info-value">{{ report.investedMachinery }}</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">已投入人力:</span>
|
||||||
|
<span class="info-value">{{ report.investedManpower }}</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">已投入资金:</span>
|
||||||
|
<span class="info-value">{{ 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>
|
||||||
</div>
|
</div>
|
||||||
@ -274,8 +242,10 @@ import { onMounted, ref, computed } from 'vue'
|
|||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { ArrowLeft, Picture, VideoCamera } from '@element-plus/icons-vue'
|
import { ArrowLeft, Picture, VideoCamera } from '@element-plus/icons-vue'
|
||||||
import ContinueReport from './ContinueReport.vue'
|
import ContinueReport from './WaterDisasterContinueReportPC.vue'
|
||||||
import { request } from '@shared/utils/request'
|
import { request } from '@shared/utils/request'
|
||||||
|
import LossListDetail from './WaterDisasterLossListDetailPC.vue'
|
||||||
|
import FileUpload from '@/component/FileUpload/FileUpload.vue'
|
||||||
import mockData from '../DisasterReport/waterMockJson.json'
|
import mockData from '../DisasterReport/waterMockJson.json'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -307,10 +277,15 @@ const continueReport = ref(null)
|
|||||||
const allReports = computed(() => {
|
const allReports = computed(() => {
|
||||||
const reports =
|
const reports =
|
||||||
detailData.value.report?.map((item, index) => {
|
detailData.value.report?.map((item, index) => {
|
||||||
item.title = index === 0 ? '首报' : '续报' + index
|
if (index === detailData.value.report.length - 1) {
|
||||||
|
item.title = '首报'
|
||||||
|
} else {
|
||||||
|
item.title = '续报' + (detailData.value.report.length - 1 - index)
|
||||||
|
}
|
||||||
return item
|
return item
|
||||||
}) || []
|
}) || []
|
||||||
return reports.reverse()
|
return reports
|
||||||
|
// return reports.reverse()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否有填报数据
|
// 是否有填报数据
|
||||||
@ -328,14 +303,20 @@ const getEventStatusType = () => {
|
|||||||
return eventStatus.value === 1 ? 'success' : 'danger'
|
return eventStatus.value === 1 ? 'success' : 'danger'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getBaseDisposalMeasures = () => {
|
||||||
|
const firstItem = allReports.value[0]
|
||||||
|
if (!firstItem) return '-'
|
||||||
|
return formatDisposalMeasures(firstItem.disposalMeasures || '') || '-'
|
||||||
|
}
|
||||||
|
|
||||||
// 格式化处置措施
|
// 格式化处置措施
|
||||||
const formatDisposalMeasures = (measures) => {
|
const formatDisposalMeasures = (measures) => {
|
||||||
if (!measures) return ''
|
if (!measures) return ''
|
||||||
const measureMap = {
|
const measureMap = {
|
||||||
halfClose: '半幅封闭',
|
半幅封闭: '半幅封闭',
|
||||||
fullClose: '全副封闭',
|
全副封闭: '全副封闭',
|
||||||
bypass: '便道通行',
|
便道通行: '便道通行',
|
||||||
normal: '正常通行'
|
正常通行: '正常通行'
|
||||||
}
|
}
|
||||||
return measures
|
return measures
|
||||||
.split(',')
|
.split(',')
|
||||||
@ -381,6 +362,7 @@ const getDisasterDetail = async () => {
|
|||||||
|
|
||||||
if (result?.data) {
|
if (result?.data) {
|
||||||
const data = result.data
|
const data = result.data
|
||||||
|
console.log('🚀 ~ getDisasterDetail ~ data:', data)
|
||||||
detailData.value = {
|
detailData.value = {
|
||||||
event: data.event || null,
|
event: data.event || null,
|
||||||
report: data.report || [],
|
report: data.report || [],
|
||||||
@ -397,7 +379,7 @@ const getDisasterDetail = async () => {
|
|||||||
const newFormData = {
|
const newFormData = {
|
||||||
...data,
|
...data,
|
||||||
lossList: null,
|
lossList: null,
|
||||||
report: mockData.report,
|
report: route.query.mock ? mockData.report : {},
|
||||||
fileList: null
|
fileList: null
|
||||||
}
|
}
|
||||||
continueReport.value?.initFormData(newFormData)
|
continueReport.value?.initFormData(newFormData)
|
||||||
@ -416,25 +398,6 @@ const handleClickBack = () => {
|
|||||||
router.push('/disasterManagement')
|
router.push('/disasterManagement')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 续报
|
|
||||||
const handleContinueReport = () => {
|
|
||||||
router.push({
|
|
||||||
path: '/disasterReport',
|
|
||||||
query: {
|
|
||||||
id: route.query.id,
|
|
||||||
eventId: detailData.value.event?.id,
|
|
||||||
isContinue: 'true'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 预览附件
|
|
||||||
const previewFile = (file) => {
|
|
||||||
if (file.fileUrl) {
|
|
||||||
window.open(file.fileUrl, '_blank')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDisasterDetail()
|
getDisasterDetail()
|
||||||
})
|
})
|
||||||
@ -502,8 +465,12 @@ onMounted(() => {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
|
||||||
|
& + .info-item {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.info-label {
|
.info-label {
|
||||||
width: 120px;
|
white-space: nowrap;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -612,7 +579,26 @@ onMounted(() => {
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
.right-panel {
|
.right-panel {
|
||||||
width: 400px;
|
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>
|
</style>
|
||||||
@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<el-row class="loss-list-detail" :gutter="24">
|
||||||
|
<template v-for="(item, index) in configs" :key="index">
|
||||||
|
<el-col :span="colSpan">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">{{ item.lossTypeName }}:</span>
|
||||||
|
<span class="info-value">{{ getValue(item) }}{{ item.unit }}</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="colSpan" v-if="item.lossTypeCode == 'OTHER_LOSS'">
|
||||||
|
<div class="info-item">
|
||||||
|
<span class="info-label">其它损失描述:</span>
|
||||||
|
<span class="info-value">{{ getRemark(item) }}</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</template>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Delete, Plus } from '@element-plus/icons-vue'
|
||||||
|
import { request } from '@shared/utils/request'
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
colSpan: {
|
||||||
|
type: Number,
|
||||||
|
default: 8
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getValue = (config) => {
|
||||||
|
const value = props.modelValue?.find((v) => v.lossTypeId === config.lossTypeId)
|
||||||
|
if (value == null) props.modelValue?.push({ ...config })
|
||||||
|
return value?.totalAmount || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRemark = (config) => {
|
||||||
|
const value = props.modelValue?.find((v) => v.lossTypeId === config.lossTypeId)
|
||||||
|
if (value == null) props.modelValue?.push({ ...config })
|
||||||
|
return value?.remark || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const configs = ref([])
|
||||||
|
|
||||||
|
// 获取损失类型字典
|
||||||
|
const getLossDict = async () => {
|
||||||
|
try {
|
||||||
|
const res = await request({
|
||||||
|
url: '/snow-ops-platform/water-damage/loss/typeAndInfo',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
configs.value = res.data?.records
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取损失类型失败:', error)
|
||||||
|
ElMessage.error('获取损失类型失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getLossDict()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.loss-list-detail {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.loss-table {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
.amount-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.unit-text {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-button-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculate-form {
|
||||||
|
padding: 8px 0;
|
||||||
|
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.calculation-preview {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.preview-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.preview-label {
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-value {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loss-picker-content {
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px 0;
|
||||||
|
|
||||||
|
.loss-radio-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.loss-radio-item {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
:deep(.el-radio__label) {
|
||||||
|
width: calc(100% - 22px);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
line-height: 1.5;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -22,9 +22,8 @@
|
|||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6">
|
||||||
<el-form-item label="事件类别">
|
<el-form-item label="事件类别">
|
||||||
<el-select v-model="filterForm.disasterType" placeholder="请选择" clearable>
|
<el-select v-model="filterForm.disasterType" placeholder="请选择" clearable>
|
||||||
<el-option label="水毁事件" value="水毁事件" />
|
<el-option label="水毁事件" value="WATER_DAMAGE" />
|
||||||
<el-option label="塌方事件" value="塌方事件" />
|
<el-option label="冰雪灾害" value="ICE_SNOW" />
|
||||||
<el-option label="泥石流" value="泥石流" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -87,20 +86,25 @@
|
|||||||
{{ scope.$index + 1 }}
|
{{ scope.$index + 1 }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="routeNo" label="所属区县" min-width="120" />
|
<el-table-column prop="district" label="所属区县" min-width="120" />
|
||||||
<el-table-column prop="routeNo" label="路线编号" min-width="120" />
|
<el-table-column prop="routeNo" label="路线编号" min-width="120" />
|
||||||
<el-table-column prop="routeName" label="路线名称" min-width="150" show-overflow-tooltip />
|
<el-table-column prop="routeName" label="路线名称" min-width="150" show-overflow-tooltip />
|
||||||
<el-table-column prop="startPile" label="起点桩号" min-width="110" show-overflow-tooltip />
|
<el-table-column prop="startStakeNo" label="起点桩号" min-width="110" show-overflow-tooltip />
|
||||||
<el-table-column prop="endPile" label="终点桩号" min-width="110" show-overflow-tooltip />
|
<el-table-column prop="endStakeNo" label="终点桩号" min-width="110" show-overflow-tooltip />
|
||||||
<el-table-column prop="roadConditionLocation" label="路况位置" min-width="150" show-overflow-tooltip />
|
<el-table-column prop="roadConditionLocation" label="路况位置" min-width="150" show-overflow-tooltip />
|
||||||
<el-table-column prop="isBlocked" label="是否阻断" width="90" align="center">
|
<el-table-column prop="isBlocked" label="是否阻断" width="90" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="row.isBlocked === '是' ? 'danger' : 'success'" size="small">
|
<el-tag :type="row.blocked === true ? 'danger' : 'success'" size="small">
|
||||||
{{ row.isBlocked || '—' }}
|
{{ row.blocked === true ? '是' : row.blocked === false ? '否' : '—' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="disasterType" label="事件类型" min-width="120" show-overflow-tooltip />
|
<el-table-column prop="disasterType" label="事件类型" min-width="120" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.disasterType == 'ICE_SNOW'">冰雪灾害</span>
|
||||||
|
<span v-if="row.disasterType == 'WATER_DAMAGE'">水毁灾害</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="roadConditionType" label="路况类别" min-width="110" show-overflow-tooltip />
|
<el-table-column prop="roadConditionType" label="路况类别" min-width="110" show-overflow-tooltip />
|
||||||
<el-table-column prop="eventStatus" label="事件状态" width="100" align="center">
|
<el-table-column prop="eventStatus" label="事件状态" width="100" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@ -109,20 +113,20 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="measure" label="处理措施" min-width="150" show-overflow-tooltip />
|
<el-table-column prop="disposalMeasures" label="处理措施" min-width="150" show-overflow-tooltip />
|
||||||
<el-table-column prop="occurTime" label="发现时间" width="170" />
|
<el-table-column prop="occurTime" label="发现时间" width="170" />
|
||||||
<el-table-column prop="expectRecoverTime" label="预计恢复时间" width="170" />
|
<el-table-column prop="expectRecoverTime" label="预计恢复时间" width="170" />
|
||||||
<el-table-column prop="contactPerson" label="联系人" width="110" />
|
<el-table-column prop="contactPerson" label="联系人" width="110" />
|
||||||
<el-table-column prop="contactPhone" label="联系电话" width="120" />
|
<el-table-column prop="contactPhone" label="联系电话" width="120" />
|
||||||
<el-table-column label="图片" width="80" align="center">
|
<el-table-column label="图片" width="80" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button v-if="row.hasImage" link type="primary" size="small" @click="viewImages(row)">查看</el-button>
|
<el-icon style="font-size: 18px; cursor: pointer;" v-if="row.images && row.images.length > 0" @click="viewImages(row)"><Picture /></el-icon>
|
||||||
<span v-else>—</span>
|
<span v-else>—</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="视频" width="80" align="center">
|
<el-table-column label="视频" width="80" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button v-if="row.hasVideo" link type="primary" size="small" @click="viewVideos(row)">播放</el-button>
|
<el-icon v-if="row.videos && row.videos.length > 0" style="font-size: 18px; cursor: pointer;" @click="viewVideos(row)"><VideoPlay /></el-icon>
|
||||||
<span v-else>—</span>
|
<span v-else>—</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@ -148,6 +152,9 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
<el-image-viewer v-if="showPreviewImage && selectedRow" :url-list="selectedRow.images" show-progress @close="showPreviewImage = false" />
|
||||||
|
<VideoPreviewDialog ref="videoPreviewDialog" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -156,6 +163,8 @@ import { ref, reactive, onMounted } from 'vue'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { request } from '@/utils/request'
|
import { request } from '@/utils/request'
|
||||||
|
import { Picture, VideoPlay } from '@element-plus/icons-vue'
|
||||||
|
import VideoPreviewDialog from '@/component/VideoPreviewDialog.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@ -171,6 +180,11 @@ const filterForm = reactive({
|
|||||||
pushStatus: '' // 推送状态
|
pushStatus: '' // 推送状态
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const showPreviewImage = ref(false)
|
||||||
|
const selectedRow = ref(null)
|
||||||
|
|
||||||
|
const videoPreviewDialog = ref(null)
|
||||||
|
|
||||||
// 日期范围
|
// 日期范围
|
||||||
const dateRange = ref(null)
|
const dateRange = ref(null)
|
||||||
|
|
||||||
@ -221,7 +235,7 @@ const fetchData = async () => {
|
|||||||
|
|
||||||
// 实际接口调用
|
// 实际接口调用
|
||||||
const response = await request({
|
const response = await request({
|
||||||
url: '/snow-ops-platform/unified-disaster/list',
|
url: '/snow-ops-platform/unified-disaster/pc/list',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
@ -313,21 +327,16 @@ const toReport = () => {
|
|||||||
|
|
||||||
// 查看图片
|
// 查看图片
|
||||||
const viewImages = (row) => {
|
const viewImages = (row) => {
|
||||||
if (row.images && row.images.length) {
|
selectedRow.value = row
|
||||||
// 打开图片预览
|
showPreviewImage.value = true
|
||||||
ElMessage.info('图片预览功能开发中')
|
|
||||||
} else {
|
|
||||||
ElMessage.info('暂无图片')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看视频
|
// 查看视频
|
||||||
const viewVideos = (row) => {
|
const viewVideos = (row) => {
|
||||||
if (row.videos && row.videos.length) {
|
videoPreviewDialog.value.show({
|
||||||
ElMessage.info('视频播放功能开发中')
|
url: row.videos[0],
|
||||||
} else {
|
title: row.eventName
|
||||||
ElMessage.info('暂无视频')
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页大小改变
|
// 分页大小改变
|
||||||
@ -16,7 +16,7 @@ import { useRouter, useRoute } from 'vue-router'
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { Plus, Upload } from '@element-plus/icons-vue'
|
import { Plus, Upload } from '@element-plus/icons-vue'
|
||||||
import { request } from '@/utils/request'
|
import { request } from '@/utils/request'
|
||||||
import WaterDisasterReport from './WaterDisasterReport.vue'
|
import WaterDisasterReport from './WaterDisasterReportPC.vue'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -77,8 +77,8 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<!-- 处理措施-->
|
<!-- 处理措施-->
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="处理措施" prop="event.repairProgress">
|
<el-form-item label="处理措施" prop="event.disposalMeasures">
|
||||||
<el-select v-model="formData.event.repairProgress" placeholder="请选择" style="width: 100%">
|
<el-select v-model="formData.event.disposalMeasures" placeholder="请选择" style="width: 100%">
|
||||||
<el-option label="全幅封闭" value="全幅封闭" />
|
<el-option label="全幅封闭" value="全幅封闭" />
|
||||||
<el-option label="半幅封闭" value="半幅封闭" />
|
<el-option label="半幅封闭" value="半幅封闭" />
|
||||||
<el-option label="正常通行" value="正常通行" />
|
<el-option label="正常通行" value="正常通行" />
|
||||||
@ -371,7 +371,7 @@ import { ElMessage } from 'element-plus'
|
|||||||
import { Plus, Upload } from '@element-plus/icons-vue'
|
import { Plus, Upload } from '@element-plus/icons-vue'
|
||||||
import mockData from './waterMockJson.json'
|
import mockData from './waterMockJson.json'
|
||||||
import { request } from '@/utils/request'
|
import { request } from '@/utils/request'
|
||||||
import LossList from './LossList.vue'
|
import LossList from './WaterDisasterLossListPC.vue'
|
||||||
import BlockItem from '@/component/BlockItem.vue'
|
import BlockItem from '@/component/BlockItem.vue'
|
||||||
import FileUpload from '@/component/FileUpload/FileUpload.vue'
|
import FileUpload from '@/component/FileUpload/FileUpload.vue'
|
||||||
|
|
||||||
@ -407,17 +407,13 @@ const formData = reactive({
|
|||||||
contactPhone: null, // 联系电话
|
contactPhone: null, // 联系电话
|
||||||
damageCount: null, // 水毁处数
|
damageCount: null, // 水毁处数
|
||||||
district: null, // 上报区县
|
district: null, // 上报区县
|
||||||
endStakeLat: null, // 止点纬度
|
|
||||||
endStakeLng: null, // 止点经度
|
|
||||||
endStakeNo: null, // 止点桩号
|
endStakeNo: null, // 止点桩号
|
||||||
estimatedRecoveryCost: null, // 恢复重建预估费用
|
estimatedRecoveryCost: null, // 恢复重建预估费用
|
||||||
inspectionMileage: null, // 巡查里程
|
inspectionMileage: null, // 巡查里程
|
||||||
isBlocked: null, // 是否阻断
|
isBlocked: null, // 是否阻断
|
||||||
needsRecovery: null, // 是否需要恢复重建
|
needsRecovery: null, // 是否需要恢复重建
|
||||||
repairProgress: null, // 抢修进度
|
repairProgress: null, // 抢险进度
|
||||||
reporterUnit: null, // 填报单位
|
reporterUnit: null, // 填报单位
|
||||||
startStakeLat: null, // 起点纬度
|
|
||||||
startStakeLng: null, // 起点经度
|
|
||||||
startStakeNo: null // 起点桩号
|
startStakeNo: null // 起点桩号
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -10,17 +10,13 @@
|
|||||||
"contactPhone": "13812345678",
|
"contactPhone": "13812345678",
|
||||||
"damageCount": 3,
|
"damageCount": 3,
|
||||||
"district": "武侯区",
|
"district": "武侯区",
|
||||||
"endStakeLat": "30.658712",
|
|
||||||
"endStakeLng": "104.082356",
|
|
||||||
"endStakeNo": "K2251+200",
|
"endStakeNo": "K2251+200",
|
||||||
"estimatedRecoveryCost": 120.5,
|
"estimatedRecoveryCost": 120.5,
|
||||||
"inspectionMileage": 25.6,
|
"inspectionMileage": 25.6,
|
||||||
"isBlocked": true,
|
"isBlocked": true,
|
||||||
"needsRecovery": true,
|
"needsRecovery": true,
|
||||||
"repairProgress": "抢修中",
|
"repairProgress": "抢险中",
|
||||||
"reporterUnit": "武侯区交通运输局",
|
"reporterUnit": "武侯区交通运输局",
|
||||||
"startStakeLat": "30.652145",
|
|
||||||
"startStakeLng": "104.075632",
|
|
||||||
"startStakeNo": "K2250+300"
|
"startStakeNo": "K2250+300"
|
||||||
},
|
},
|
||||||
"report": {
|
"report": {
|
||||||
@ -28,7 +24,7 @@
|
|||||||
"strandedPersonCount": 12,
|
"strandedPersonCount": 12,
|
||||||
"deadCount": 0,
|
"deadCount": 0,
|
||||||
"strandedVehicleCount": 12,
|
"strandedVehicleCount": 12,
|
||||||
"disposalMeasures": "halfClose,bypass",
|
"disposalMeasures": "全幅封闭",
|
||||||
"actualRecoverTime": "2024-07-17 12:00:00",
|
"actualRecoverTime": "2024-07-17 12:00:00",
|
||||||
"expectRecoverTime": "2024-07-18 18:00:00",
|
"expectRecoverTime": "2024-07-18 18:00:00",
|
||||||
"injuredCount": 1,
|
"injuredCount": 1,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user