This commit is contained in:
huangchenhao 2026-04-17 14:37:55 +08:00
commit b01a5f663e
22 changed files with 2346 additions and 1216 deletions

View File

@ -190,7 +190,6 @@ const formData = ref({
district: '', // district: '', //
endStakeNo: '', // endStakeNo: '', //
estimatedRecoveryCost: '', // estimatedRecoveryCost: '', //
inspectionMileage: '', //
isBlocked: '', // isBlocked: '', //
needsRecovery: '', // needsRecovery: '', //
repairProgress: '', // repairProgress: '', //

View File

@ -102,12 +102,6 @@
<span class="info-value">{{ detailData.event?.district || '-' }}</span> <span class="info-value">{{ detailData.event?.district || '-' }}</span>
</div> </div>
<!-- 巡查里程 -->
<div class="info-row">
<span class="info-label">巡查里程</span>
<span class="info-value">{{ detailData.event?.inspectionMileage ? detailData.event.inspectionMileage + '公里' : '-' }}</span>
</div>
<!-- 是否恢复重建 --> <!-- 是否恢复重建 -->
<div class="info-row"> <div class="info-row">
<span class="info-label">是否恢复重建</span> <span class="info-label">是否恢复重建</span>

View File

@ -12,7 +12,6 @@
"district": "武侯区", "district": "武侯区",
"endStakeNo": "K2251+200", "endStakeNo": "K2251+200",
"estimatedRecoveryCost": 120.5, "estimatedRecoveryCost": 120.5,
"inspectionMileage": 25.6,
"isBlocked": true, "isBlocked": true,
"needsRecovery": true, "needsRecovery": true,
"repairProgress": "抢险中", "repairProgress": "抢险中",

View File

@ -8,7 +8,7 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, watch, ref, provide } from 'vue' import { onMounted, watch, ref, provide, computed } from 'vue'
import DynamicFormItem from './DynamicFormItem.vue' import DynamicFormItem from './DynamicFormItem.vue'
const props = defineProps({ const props = defineProps({
configList: { configList: {

View File

@ -0,0 +1,5 @@
import hasPermi from './permission/hasPermi'
export default function directive(app){
app.directive('hasPermi', hasPermi)
}

View File

@ -0,0 +1,27 @@
/**
* v-hasPermi 操作权限处理
* Copyright (c) 2019 ruoyi
*/
import useUserStore from '@flightweb/store/modules/user'
export default {
mounted(el, binding, vnode) {
const { value } = binding
const all_permission = "*:*:*"
const permissions = useUserStore().permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
}

View File

@ -0,0 +1,13 @@
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: {
},
getters: {
},
actions: {
}
})

View File

@ -166,7 +166,6 @@ const formData = reactive({
district: '', district: '',
endStakeNo: '', endStakeNo: '',
estimatedRecoveryCost: '', estimatedRecoveryCost: '',
inspectionMileage: '',
isBlocked: '', isBlocked: '',
needsRecovery: '', needsRecovery: '',
repairProgress: '', repairProgress: '',
@ -325,7 +324,6 @@ const resetForm = () => {
district: '', district: '',
endStakeNo: '', endStakeNo: '',
estimatedRecoveryCost: '', estimatedRecoveryCost: '',
inspectionMileage: '',
isBlocked: '', isBlocked: '',
needsRecovery: '', needsRecovery: '',
repairProgress: '', repairProgress: '',

View File

@ -1,9 +1,64 @@
<template> <template>
<div class="web-detail-container"> <div class="web-detail-container">
<div class="content-container"> <div class="content-container">
<div class="left-panel"> <div class="left-panel">
<DynamicDetail v-model="detailData" :config-list="detailConfig" /> <!-- 基本信息卡片 -->
<el-card class="info-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">基本信息</span>
</div>
</template>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">事件类型</span>
<span class="info-value">冰雪事件</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">所属服务站</span>
<span class="info-value">{{}}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">线路编号</span>
<span class="info-value">{{}}</span>
</div>
</el-col>
</el-row>
<el-row :gutter="20" class="info-row">
<el-col :span="8">
<div class="info-item">
<span class="info-label">发现时间</span>
<span class="info-value">冰雪事件</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">路况位置</span>
<span class="info-value">{{}}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">发生地点</span>
<span class="info-value">{{}}</span>
</div>
</el-col>
</el-row>
</el-card>
<!-- 填报情况 -->
<el-card class="info-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">填报情况</span>
</div>
</template>
</el-card>
</div> </div>
<!-- <div class="right-panel" v-if="isEdit"> <!-- <div class="right-panel" v-if="isEdit">
<ContinueReport ref="continueReport" @refresh="getDisasterDetail" /> <ContinueReport ref="continueReport" @refresh="getDisasterDetail" />
@ -20,8 +75,6 @@ import { ArrowLeft, Picture, VideoCamera } from '@element-plus/icons-vue'
import ContinueReport from './IceDisasterContinueReportPC.vue' import ContinueReport from './IceDisasterContinueReportPC.vue'
import { request } from '@shared/utils/request' import { request } from '@shared/utils/request'
import FileUpload from '@/component/FileUpload/FileUpload.vue' import FileUpload from '@/component/FileUpload/FileUpload.vue'
import detailConfig from './detailConfig'
import DynamicDetail from '@/component/DynamicDetail/DynamicDetail.vue'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
@ -68,7 +121,6 @@ const hasReportData = computed(() => {
return allReports.value.length > 0 return allReports.value.length > 0
}) })
// //
const getDisasterDetail = async () => { const getDisasterDetail = async () => {
const id = route.query.id const id = route.query.id
@ -80,7 +132,7 @@ const getDisasterDetail = async () => {
try { try {
const result = await request({ const result = await request({
url: `/snow-ops-platform/event/getById?id=${route.query.id}`, url: `/snow-ops-platform/event/getById?id=${route.query.id}`,
method: 'get', method: 'get'
}) })
if (result?.data) { if (result?.data) {

View File

@ -1,111 +0,0 @@
export default [
{
type: 'card',
label: '基础信息',
children: [
{
type: 'row',
children: [
{
span: 8,
label: '事件类型',
value: '冰雪事件',
},
{
span: 8,
label: '路况类别',
prop: 'event.routeNo'
},
{
span: 8,
label: '处理措施',
prop: 'event.disposalMeasures'
}
]
},
{
type: 'row',
children: [
{
span: 8,
label: '地点路线',
prop: 'event.occurLocation'
},
{
span: 8,
label: '起点桩号',
prop: 'event.startStakeNo'
},
{
span: 8,
label: '止点桩号',
prop: 'event.endStakeNo'
}
]
},
{
type: 'row',
children: [
{
span: 8,
label: '路况位置',
prop: 'event.occurLocation'
},
{
span: 8,
label: '阻断点小地名',
prop: 'event.occurLocation'
},
{
span: 8,
label: '地点路线',
prop: 'event.occurLocation'
}
]
},
{
type: 'row',
children: [
{
span: 24,
label: '受灾里程',
prop: 'event.disasterMileage'
}
]
},
{
type: 'row',
children: [
{
span: 8,
label: '所属区县',
prop: 'event.district'
},
{
span: 8,
label: '发现时间',
prop: 'event.occurTime'
}
]
},
{
type: 'row',
children: [
{
span: 8,
label: '是否需要恢复重建',
prop: 'event.actualRecoverTime'
},
{
show: (formData) => {
return formData.event?.actualRecoverTime
},
span: 8,
label: '恢复重建预估费用(万元)',
prop: 'event.estimatedRecoveryCost'
}
]
}
]
}
]

View File

@ -1,83 +1,50 @@
<template> <template>
<div class="disaster-form-page"> <div class="disaster-form-page">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="140px" class="disaster-form" @submit.prevent> <el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" class="disaster-form" @submit.prevent>
<!-- 基本信息区块 -->
<el-card class="form-section" shadow="never"> <el-card class="form-section" shadow="never">
<template #header> <template #header>
<div class="section-header"> <div class="section-header">
<span class="section-title">信息</span> <span class="section-title">信息</span>
</div> </div>
</template> </template>
<BlockItem title="填报人员信息"> <BlockItem title="填报人员信息">
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 填报单位 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="填报单位" prop="event.reporterUnit"> <el-form-item label="填报单位" prop="event.reporterUnit">
<el-input v-model="formData.event.reporterUnit" placeholder="请填写" /> <el-input v-model="formData.event.reporterUnit" disabled />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 联系人 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="联系人员" prop="event.contactPerson"> <el-form-item label="联系人员" prop="event.contactPerson">
<el-input v-model="formData.event.contactPerson" placeholder="请填写" /> <el-input v-model="formData.event.contactPerson" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 联系电话 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="联系电话" prop="event.contactPhone"> <el-form-item label="联系电话" prop="event.contactPhone">
<el-input v-model="formData.event.contactPhone" placeholder="请填写" /> <el-input v-model="formData.event.contactPhone" maxlength="11" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</BlockItem> </BlockItem>
<BlockItem title="路况事件信息"> <BlockItem title="路况事件信息">
<el-row :gutter="24">
<!-- 发生时间 -->
<el-col :span="8">
<el-form-item label="发生时间" prop="occurTime">
<el-date-picker v-model="formData.occurTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
<!-- 路况类别 -->
<el-col :span="8">
<el-form-item label="路况类别" prop="roadConditionType">
<el-select v-model="formData.roadConditionType" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['iceRoadConditionType']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
<!-- 处理措施-->
<el-col :span="8">
<el-form-item label="处理措施" prop="report.disposalMeasures">
<el-select v-model="formData.report.disposalMeasures" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['disposalMeasures']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="事件类型"> <el-form-item label="事件类型">
<el-select v-model="eventType" placeholder="请选择" style="width: 100%" @change="handleEventTypeChange"> <el-select v-model="eventType" style="width: 100%" @change="handleEventTypeChange">
<el-option v-for="(option, idx) in options['eventType']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['eventType']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 预计恢复时间 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="预计恢复时间" prop="report.expectRecoverTime"> <el-form-item label="填报站点" prop="event.serviceStationId">
<el-date-picker v-model="formData.report.expectRecoverTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" /> <YHZSelect v-model="formData.event.serviceStationId" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> <el-col :span="8">
<el-row :gutter="24"> </el-row> <el-form-item label="发生时间" prop="occurTime">
<el-row :gutter="24"> <el-date-picker v-model="formData.occurTime" type="datetime" placeholder="请选择日期时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
<!-- 现场描述 -->
<el-col :span="16">
<el-form-item label="现场描述(绕行方案)" prop="report.siteDescription">
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="2" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -85,90 +52,85 @@
<BlockItem title="位置信息"> <BlockItem title="位置信息">
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 路线类型 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="路线类型"> <el-form-item label="路线类型">
<el-select v-model="filterForm.routeType" placeholder="请选择" style="width: 100%"> <el-input :model-value="routeTypeLabel" readonly />
<el-option v-for="(option, idx) in options['roadType']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 所属区县 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="所属区县" prop="event.district"> <el-form-item label="所属区县" prop="event.district">
<el-select v-model="formData.event.district" placeholder="请选择" style="width: 100%" @change="handleDistrictChange"> <el-select v-model="formData.event.district" placeholder="请选择" style="width: 100%" @change="handleDistrictChange">
<el-option v-for="(option, idx) in options['area']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['area']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 受灾里程 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="受灾里程" prop="event.disasterMileage"> <el-form-item label="线路编号" prop="routeNo">
<el-input v-model="formData.event.disasterMileage" placeholder="请填写" /> <RoadRoutesSelect v-model="formData.routeNo" :extra-params="{ xzdj: filterForm.routeType, qxid: formData.event.district }" @change="handleRouteNoChange" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="地点路线" prop="routeNo"> <el-form-item label="发生地点" prop="event.occurLocation">
<RoadRoutesSelect v-model="formData.routeNo" @change="handleRouteNoChange" :extra-params="{ xzdj: filterForm.routeType, qxid: formData.event.district }" /> <el-input v-model="formData.event.occurLocation" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 起点桩号 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="起点桩号(K)" prop="event.startStakeNo"> <el-form-item label="路况位置" prop="occurLocation">
<el-input v-model="formData.occurLocation" placeholder="请选择">
<template #suffix>
<el-icon class="location-icon"><LocationFilled /></el-icon>
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="起点桩号" prop="event.startStakeNo">
<el-input v-model="formData.event.startStakeNo" placeholder="请填写"> <el-input v-model="formData.event.startStakeNo" placeholder="请填写">
<template #prepend>K</template> <template #prepend>K</template>
</el-input> </el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 止点桩号 --> </el-row>
<el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="止点桩号(K)" prop="event.endStakeNo"> <el-form-item label="起点桩经度" prop="event.startStakeLongitude">
<el-input v-model="formData.event.startStakeLongitude" placeholder="请填写" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="起点桩纬度" prop="event.startStakeLatitude">
<el-input v-model="formData.event.startStakeLatitude" placeholder="请填写" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="止点桩号" prop="event.endStakeNo">
<el-input v-model="formData.event.endStakeNo" placeholder="请填写"> <el-input v-model="formData.event.endStakeNo" placeholder="请填写">
<template #prepend>K</template> <template #prepend>K</template>
</el-input> </el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="24">
<!-- 路况位置 -->
<el-col :span="8">
<el-form-item label="路况位置" prop="occurLocation">
<el-input v-model="formData.occurLocation" placeholder="请填写" />
</el-form-item>
</el-col>
<!-- 阻断点小地名 -->
<el-col :span="8">
<el-form-item label="阻断点小地名" prop="event.blockedPointName">
<el-input v-model="formData.event.blockedPointName" placeholder="请填写" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 经度 -->
<el-col :span="8">
<el-form-item label="经度" prop="event.longitude">
<el-input v-model="formData.event.longitude" placeholder="经度"> </el-input>
</el-form-item>
</el-col>
<!-- 纬度 -->
<el-col :span="8">
<el-form-item label="纬度" prop="event.latitude">
<el-input v-model="formData.event.latitude" placeholder="纬度"> </el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="图片上传" prop="fileList"> <el-form-item label="止点桩经度" prop="event.endStakeLongitude">
<FileUpload type="image" :limit="9" v-model="formData.fileList" /> <el-input v-model="formData.event.endStakeLongitude" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="视频上传" prop="fileList"> <el-form-item label="止点桩纬度" prop="event.endStakeLatitude">
<FileUpload type="video" :limit="9" v-model="formData.fileList" /> <el-input v-model="formData.event.endStakeLatitude" placeholder="请填写" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="受灾里程" prop="event.disasterMileage">
<el-input v-model="formData.event.disasterMileage" placeholder="请填写">
<template #append>公里</template>
</el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -178,440 +140,151 @@
<el-card class="form-section" shadow="never"> <el-card class="form-section" shadow="never">
<template #header> <template #header>
<div class="section-header"> <div class="section-header">
<span class="section-title">灾毁损失</span> <span class="section-title">处置情况</span>
</div> </div>
</template> </template>
<BlockItem title="路况事件信息">
<BlockItem>
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 受伤人员 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="受伤人员" prop="report.injuredCount"> <el-form-item label="处理措施" prop="report.disposalMeasures">
<el-input-number v-model="formData.report.injuredCount" :min="0" :step="1" style="width: 100%"> <el-select v-model="formData.report.disposalMeasures" placeholder="请选择" style="width: 100%">
<template #suffix> <el-option v-for="(option, idx) in options['iceDisposalMeasures']" :key="idx" :label="option.label" :value="option.value" />
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 死亡人员 -->
<el-col :span="8">
<el-form-item label="死亡人员" prop="report.deadCount">
<el-input-number v-model="formData.report.deadCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 滞留人员 -->
<el-col :span="8">
<el-form-item label="滞留人员" prop="report.strandedPersonCount">
<el-input-number v-model="formData.report.strandedPersonCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 损坏车辆 -->
<el-col :span="8">
<el-form-item label="损坏车辆" prop="report.damagedVehicleCount">
<el-input-number v-model="formData.report.damagedVehicleCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 滞留车辆 -->
<el-col :span="8">
<el-form-item label="滞留车辆" prop="report.strandedVehicleCount">
<el-input-number v-model="formData.report.strandedVehicleCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
</BlockItem>
<BlockItem title="道路损失及其他">
<LossList v-model:model-value="formData.report.lossList" />
<el-row :gutter="24">
<!-- 投入机械 -->
<el-col :span="8">
<el-form-item label="投入机械" prop="report.investedMachinery">
<el-input-number v-model="formData.report.investedMachinery" :min="0" :precision="1" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">/</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 投入人力 -->
<el-col :span="8">
<el-form-item label="投入人力" prop="report.investedManpower">
<el-input-number v-model="formData.report.investedManpower" :min="0" :step="1" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">人次</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 投入资金 -->
<el-col :span="8">
<el-form-item label="投入资金" prop="report.investedFunds">
<el-input-number v-model="formData.report.investedFunds" :min="0" :precision="2" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">万元</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
</BlockItem>
<BlockItem title="恢复重建预估费用">
<el-row :gutter="24">
<!-- 是否需要恢复重建 -->
<el-col :span="8">
<el-form-item label="是否需要恢复重建" prop="event.needsRecovery">
<el-select v-model="formData.event.needsRecovery" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['yesNoBool']" :label="option.label" :value="option.value" :key="idx" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8">
<!-- 恢复重建预估费用 --> <el-form-item label="预计恢复时间" prop="report.expectRecoverTime">
<el-col :span="8" v-if="formData?.event.needsRecovery"> <el-date-picker v-model="formData.report.expectRecoverTime" type="datetime" placeholder="请选择日期时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
<el-form-item label="恢复重建预估费用" prop="event.estimatedRecoveryCost"> </el-form-item>
<el-input-number v-model="formData.event.estimatedRecoveryCost" :min="0" :precision="2" style="width: 100%" placeholder="请填写"> </el-col>
<template #suffix> <el-col :span="8">
<span class="unit-text">万元</span> <el-form-item label="实际预计恢复时间" prop="report.actualRecoverTime">
</template> <el-date-picker v-model="formData.report.actualRecoverTime" type="datetime" placeholder="请选择日期时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-input-number> </el-form-item>
</el-col>
</el-row>
</BlockItem>
</el-card>
<el-card class="form-section" shadow="never">
<template #header>
<div class="section-header">
<span class="section-title">实施情况</span>
</div>
</template>
<BlockItem>
<MaterialList v-model="formData.yhzMaterialList" :yhzId="formData.event.serviceStationId" />
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="投入人力" prop="material.inputManpower">
<el-input-number v-model="formData.material.inputManpower" :min="0" :step="1" :controls="false" placeholder="请填写" style="width: 100%">
<template #suffix>
<span class="unit-text">人次</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="投入资金" prop="material.inputFunds">
<el-input-number v-model="formData.material.inputFunds" :min="0" :precision="2" :controls="false" placeholder="请填写" style="width: 100%">
<template #suffix>
<span class="unit-text">万元</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="投入设备" prop="material.inputEquipment">
<el-input-number v-model="formData.material.inputEquipment" :min="0" :precision="1" :controls="false" placeholder="请填写" style="width: 100%">
<template #suffix>
<span class="unit-text">台班</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="有无滞留车辆" prop="traffic.hasStrandedVehicles">
<el-select v-model="formData.traffic.hasStrandedVehicles" placeholder="请选择" style="width: 100%" @change="handleHasStrandedVehiclesChange">
<el-option v-for="option in strandedVehicleOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="滞留车辆" prop="traffic.strandedVehicleCount">
<el-input-number v-model="formData.traffic.strandedVehicleCount" :min="0" :step="1" :controls="false" placeholder="请填写" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="18">
<el-form-item label="现场情况描述" prop="report.siteDescription">
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="3" placeholder="请填写" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="16">
<el-form-item label="附件上传" prop="fileList">
<div class="upload-wrapper">
<div class="upload-title">图片上传</div>
<FileUpload v-model="formData.fileList" type="image" :limit="9" />
</div>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</BlockItem> </BlockItem>
</el-card> </el-card>
<!-- 提交按钮 -->
<div class="form-actions"> <div class="form-actions">
<el-button @click="handleBack">取消</el-button> <el-button @click="handleBack">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">提交</el-button> <el-button type="primary" :loading="submitting" @click="handleSubmit">提交</el-button>
</div> </div>
</el-form> </el-form>
<!-- 图片预览对话框 -->
<el-dialog v-model="previewDialogVisible" title="图片预览" width="600px">
<img :src="previewImageUrl" style="width: 100%" alt="预览图片" />
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue' import { LocationFilled } from '@element-plus/icons-vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { Plus, Upload } from '@element-plus/icons-vue'
import { request } from '@/utils/request'
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'
import { useOptions } from '@shared/composables/useOptions'
import RoadRoutesSelect from '../components/RoadRoutesSelect.vue' import RoadRoutesSelect from '../components/RoadRoutesSelect.vue'
import YHZSelect from '../components/YHZSelect.vue'
import MaterialList from '../components/MaterialList.vue'
import { useIceDisasterReport } from './useIceDisasterReport'
const router = useRouter() const {
const route = useRoute() eventType,
const { options, getAreaOptions } = useOptions() filterForm,
const formRef = ref(null) formData,
const submitting = ref(false) formRef,
formRules,
handleBack,
handleDistrictChange,
handleEventTypeChange,
handleRouteNoChange,
handleSubmit,
initFormData,
getFormData,
options,
routeTypeLabel,
strandedVehicleOptions,
submitting,
handleHasStrandedVehiclesChange,
validate
} = useIceDisasterReport()
//
const isContinue = computed(() => route.query.isContinue === 'true')
//
const disposalMeasuresArray = ref([])
//
const imageFileList = ref([])
const videoFileList = ref([])
const eventType = ref('冰雪事件')
const filterForm = reactive({
routeType: ''
})
const formData = reactive({
//
occurLocation: null, // /
occurTime: null, //
roadConditionType: null, //
routeNo: null, // 线
// event
event: {
blockedMileage: null, //
blockedPointName: null, //
contactPerson: null, //
contactPhone: null, //
district: null, //
endStakeNo: null, //
estimatedRecoveryCost: null, //
inspectionMileage: null, //
needsRecovery: null, //
reporterUnit: null, //
startStakeNo: null, //
disasterMileage: null //
},
// report
report: {
actualRecoverTime: null, //
damagedVehicleCount: null, //
deadCount: null, //
disposalMeasures: null, //
expectRecoverTime: null, //
injuredCount: null, //
investedFunds: null, //
investedMachinery: null, //
investedManpower: null, //
remark: null, // /
siteDescription: null, //
strandedPersonCount: null, //
strandedVehicleCount: null, //
totalLossAmount: null //
},
// lossList
lossList: [],
// fileList
fileList: []
})
const handleEventTypeChange = () => {
router.replace({ path: '/waterDisasterReport' })
}
// report.disposalMeasures
watch(
disposalMeasuresArray,
(newVal) => {
formData.report.disposalMeasures = newVal.length ? newVal.join(',') : null
},
{ deep: true }
)
// fileList
watch(
imageFileList,
() => {
syncFileList()
},
{ deep: true }
)
// fileList
watch(
videoFileList,
() => {
syncFileList()
},
{ deep: true }
)
// fileList
const syncFileList = () => {
formData.fileList = [
...imageFileList.value.map((f) => ({
fileName: f.name || '',
fileSize: f.size || 0,
fileType: 1, // 1-
fileUrl: f.url || f.content || ''
})),
...videoFileList.value.map((f) => ({
fileName: f.name || '',
fileSize: f.size || 0,
fileType: 2, // 2-
fileUrl: f.url || f.content || ''
}))
]
}
// report.disposalMeasures
watch(
() => formData.report.disposalMeasures,
(newVal) => {
if (newVal && typeof newVal === 'string') {
disposalMeasuresArray.value = newVal.split(',').filter(Boolean)
} else {
disposalMeasuresArray.value = []
}
},
{ immediate: true }
)
//
const formRules = {
roadConditionType: [{ required: true, message: '请选择路况类别', trigger: 'change' }],
'report.disposalMeasures': [{ required: true, message: '请选择处置措施', trigger: 'change' }],
'event.blockedMileage': [{ required: true, message: '请输入阻断里程', trigger: 'blur' }],
occurTime: [{ required: true, message: '请选择发生时间', trigger: 'change' }],
'report.expectRecoverTime': [{ required: true, message: '请输入预计恢复时间', trigger: 'blur' }],
routeNo: [{ required: true, message: '请输入线路编号', trigger: 'blur' }],
'event.startStakeNo': [{ required: true, message: '请输入起点桩号', trigger: 'blur' }],
'event.endStakeNo': [{ required: true, message: '请输入止点桩号', trigger: 'blur' }],
occurLocation: [{ required: true, message: '请输入路况位置', trigger: 'blur' }],
'event.blockedPointName': [{ required: true, message: '请输入阻断点小地名', trigger: 'blur' }],
'event.longitude': [{ required: true, message: '请输入经度', trigger: 'blur' }],
'event.latitude': [{ required: true, message: '请输入纬度', trigger: 'blur' }],
'event.needsRecovery': [{ required: true, message: '请选择是否需要恢复重建', trigger: 'change' }],
'event.estimatedRecoveryCost': [{ required: true, message: '请输入恢复重建预估费用', trigger: 'blur' }]
// 'event.reporterUnit': [{ required: true, message: '', trigger: 'blur' }],
// 'event.contactPerson': [{ required: true, message: '', trigger: 'blur' }],
// 'event.contactPhone': [
// { required: true, message: '', trigger: 'blur' },
// { pattern: /^1[3-9]\d{9}$/, message: '', trigger: 'blur' }
// ]
}
//
const beforeImageUpload = (file) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt500k = file.size / 1024 < 500
if (!isJpgOrPng) {
ElMessage.error('只能上传 JPG/PNG 格式的图片!')
return false
}
if (!isLt500k) {
ElMessage.error('图片大小不能超过 500KB!')
return false
}
return false // false
}
//
const beforeVideoUpload = (file) => {
const isLt20M = file.size / 1024 / 1024 < 20
if (!isLt20M) {
ElMessage.error('视频大小不能超过 20MB!')
return false
}
return false
}
//
const previewDialogVisible = ref(false)
const previewImageUrl = ref('')
const handlePicturePreview = (file) => {
previewImageUrl.value = file.url
previewDialogVisible.value = true
}
const handlePictureRemove = (file, fileList) => {
imageFileList.value = fileList
}
//
const handleBack = () => {
router.back()
}
// /
const initFormData = (data) => {
Object.assign(formData, data)
}
const handleDistrictChange = () => {
formData.routeNo = null
}
const handleRouteNoChange = (item) => {
formData.event.startStakeNo = item.startStakeNo
formData.event.endStakeNo = item.endStakeNo
}
//
const getFormData = () => {
return { ...formData }
}
//
const validate = async () => {
if (!formRef.value) return false
try {
await formRef.value.validate()
return true
} catch (error) {
ElMessage.warning('请完善表单信息')
return false
}
}
//
const handleSubmit = async () => {
//
if (!(await validate())) {
return
}
submitting.value = true
try {
//
//
const submitData = {
...formData
//
}
const res = await request({
url: '/snow-ops-platform/water-damage/addOrUpdate',
method: 'post',
data: submitData
})
if (res?.code === '00000') {
ElMessage.success('提交成功')
} else {
ElMessage.error(res.message)
}
//
setTimeout(() => {
router.replace('/disasterManagement')
}, 1000)
} catch (error) {
ElMessage.error('提交失败,请重试')
console.error('提交失败:', error)
} finally {
submitting.value = false
}
}
//
const loadEditData = async () => {
initFormData({})
}
onMounted(() => {
//
getAreaOptions()
loadEditData()
})
//
defineExpose({ defineExpose({
validate, validate,
initFormData, initFormData,
@ -625,9 +298,9 @@ defineExpose({
background-color: #f5f7fa; background-color: #f5f7fa;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
}
.disaster-form { .form-section {
.form-section {
margin-bottom: 20px; margin-bottom: 20px;
:deep(.el-card__header) { :deep(.el-card__header) {
@ -639,10 +312,9 @@ defineExpose({
:deep(.el-card__body) { :deep(.el-card__body) {
padding: 20px; padding: 20px;
} }
} }
.section-header { .section-title {
.section-title {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: #303133; color: #303133;
@ -657,42 +329,34 @@ defineExpose({
transform: translateY(-50%); transform: translateY(-50%);
width: 3px; width: 3px;
height: 16px; height: 16px;
background-color: #409eff;
border-radius: 2px; border-radius: 2px;
background-color: #409eff;
} }
} }
}
.sub-section-title { .location-icon {
font-size: 14px; color: #909399;
font-weight: 500; }
.unit-text {
color: #606266; color: #606266;
margin: 8px 0 16px 0; font-size: 14px;
padding-left: 8px; }
border-left: 3px solid #409eff;
}
.unit-text { .upload-wrapper {
color: #909399; width: 100%;
font-size: 12px; }
}
.upload-tip { .upload-title {
font-size: 12px; margin-bottom: 12px;
color: #909399; color: #303133;
margin-top: 8px; font-size: 14px;
} }
.video-preview { .form-actions {
margin-top: 12px;
}
.form-actions {
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: 16px; gap: 16px;
padding: 20px 0 40px; padding: 8px 0 32px;
}
}
} }
</style> </style>

View File

@ -0,0 +1,336 @@
import { computed, onMounted, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { request } from '@/utils/request'
import { useOptions } from '@shared/composables/useOptions'
const DEFAULT_REPORTER_UNIT = '万州区公路中心'
const createDefaultFormData = () => ({
occurLocation: '',
occurTime: '',
routeNo: '',
event: {
contactPerson: '',
contactPhone: '',
disasterMileage: null,
district: '',
endStakeLatitude: null,
endStakeLongitude: null,
endStakeNo: '',
occurLocation: '',
reporterUnit: DEFAULT_REPORTER_UNIT,
serviceStationId: '',
startStakeLatitude: null,
startStakeLongitude: null,
startStakeNo: ''
},
material: {
inputEquipment: null,
inputFunds: null,
inputManpower: null
},
report: {
actualRecoverTime: '',
disposalMeasures: '',
expectRecoverTime: '',
siteDescription: ''
},
traffic: {
hasStrandedVehicles: null,
strandedVehicleCount: null
},
fileList: []
})
const mergeFormData = (source = {}) => {
const defaults = createDefaultFormData()
const merged = {
...defaults,
...source,
event: {
...defaults.event,
...(source.event || {})
},
material: {
...defaults.material,
...(source.material || {})
},
report: {
...defaults.report,
...(source.report || {})
},
traffic: {
...defaults.traffic,
...(source.traffic || {})
}
}
merged.fileList = Array.isArray(source.fileList) ? source.fileList : defaults.fileList
if (!merged.event.reporterUnit) {
merged.event.reporterUnit = DEFAULT_REPORTER_UNIT
}
return merged
}
const parsePointValue = (point) => {
if (!point) {
return { longitude: null, latitude: null }
}
if (Array.isArray(point) && point.length >= 2) {
return {
longitude: point[0] ?? null,
latitude: point[1] ?? null
}
}
if (typeof point === 'string') {
try {
const parsed = JSON.parse(point)
if (Array.isArray(parsed) && parsed.length >= 2) {
return {
longitude: parsed[0] ?? null,
latitude: parsed[1] ?? null
}
}
} catch (_error) {
return { longitude: null, latitude: null }
}
}
return { longitude: null, latitude: null }
}
export const useIceDisasterReport = () => {
const router = useRouter()
const route = useRoute()
const { options, getAreaOptions } = useOptions()
const formRef = ref(null)
const submitting = ref(false)
const eventType = ref('冰雪事件')
const filterForm = reactive({
routeType: ''
})
const formData = reactive(createDefaultFormData())
const strandedVehicleOptions = [
{ label: '有', value: 1 },
{ label: '无', value: 0 }
]
const routeTypeLabel = computed(() => {
const matched = options.value.roadType?.find((item) => item.value === filterForm.routeType)
return matched?.label || '国省道'
})
const formRules = {
'event.contactPerson': [{ required: true, message: '请输入联系人员', trigger: 'blur' }],
'event.contactPhone': [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的联系电话', trigger: 'blur' }
],
'event.serviceStationId': [{ required: true, message: '请选择填报站点', trigger: 'change' }],
occurTime: [{ required: true, message: '请选择发生时间', trigger: 'change' }],
'event.district': [{ required: true, message: '请选择所属区县', trigger: 'change' }],
routeNo: [{ required: true, message: '请选择线路编号', trigger: 'change' }],
'event.occurLocation': [{ required: true, message: '请输入发生地点', trigger: 'blur' }],
occurLocation: [{ required: true, message: '请选择路况位置', trigger: 'blur' }],
'event.startStakeNo': [{ required: true, message: '请输入起点桩号', trigger: 'blur' }],
'event.startStakeLongitude': [{ required: true, message: '请输入起点桩经度', trigger: 'blur' }],
'event.startStakeLatitude': [{ required: true, message: '请输入起点桩纬度', trigger: 'blur' }],
'event.endStakeNo': [{ required: true, message: '请输入止点桩号', trigger: 'blur' }],
'event.endStakeLongitude': [{ required: true, message: '请输入止点桩经度', trigger: 'blur' }],
'event.endStakeLatitude': [{ required: true, message: '请输入止点桩纬度', trigger: 'blur' }],
'event.disasterMileage': [{ required: true, message: '请输入受灾里程', trigger: 'blur' }],
'report.disposalMeasures': [{ required: true, message: '请选择处理措施', trigger: 'change' }],
'report.siteDescription': [{ required: true, message: '请输入现场情况描述', trigger: 'blur' }],
fileList: [
{
validator: (_rule, value, callback) => {
if (!Array.isArray(value) || value.length === 0) {
callback(new Error('请上传附件'))
return
}
callback()
},
trigger: 'change'
}
]
}
const initFormData = (data = {}) => {
Object.assign(formData, mergeFormData(data))
}
const handleEventTypeChange = (value) => {
if (value === '水毁事件') {
router.replace({ path: '/waterDisasterReport' })
}
}
const handleDistrictChange = () => {
formData.routeNo = ''
formData.event.startStakeNo = ''
formData.event.endStakeNo = ''
formData.event.startStakeLongitude = null
formData.event.startStakeLatitude = null
formData.event.endStakeLongitude = null
formData.event.endStakeLatitude = null
}
const handleRouteNoChange = (item = {}) => {
formData.routeNo = item.routeCode || formData.routeNo
formData.event.startStakeNo = item.startStakeNo
formData.event.endStakeNo = item.endStakeNo
const startPoint = parsePointValue(item.startPoint ?? item.startpoint)
const endPoint = parsePointValue(item.endPoint ?? item.endpoint)
formData.event.startStakeLongitude = startPoint.longitude
formData.event.startStakeLatitude = startPoint.latitude
formData.event.endStakeLongitude = endPoint.longitude
formData.event.endStakeLatitude = endPoint.latitude
}
const handleHasStrandedVehiclesChange = (value) => {
if (value !== 1) {
formData.traffic.strandedVehicleCount = null
}
}
const buildSubmitData = () => {
const payload = mergeFormData(formData)
payload.event.routeNo = payload.routeNo
payload.event.occurTime = payload.occurTime
payload.event.eventType = eventType.value
payload.event.roadType = filterForm.routeType
payload.event.roadConditionLocation = payload.occurLocation
if (payload.traffic.hasStrandedVehicles !== 1) {
payload.traffic.strandedVehicleCount = null
}
return payload
}
const getFormData = () => buildSubmitData()
const validate = async () => {
if (!formRef.value) {
return false
}
try {
await formRef.value.validate()
return true
} catch (_error) {
ElMessage.warning('请完善表单信息')
return false
}
}
const handleSubmit = async () => {
if (!(await validate())) {
return
}
submitting.value = true
try {
const res = await request({
url: '/snow-ops-platform/event/addOrUpdate',
method: 'post',
data: buildSubmitData()
})
if (res?.code === '00000') {
ElMessage.success('提交成功')
setTimeout(() => {
router.replace('/disasterManagement')
}, 500)
} else {
ElMessage.error(res?.message || '提交失败')
}
} catch (error) {
console.error('提交失败:', error)
ElMessage.error('提交失败,请重试')
} finally {
submitting.value = false
}
}
const getDisasterDetail = async () => {
if (!route.query.id) {
return
}
try {
const result = await request({
url: `/snow-ops-platform/event/getById?id=${route.query.id}`,
method: 'get'
})
if (result?.data) {
const data = result.data
initFormData({
occurLocation: data.occurLocation || data.event?.roadConditionLocation || '',
occurTime: data.occurTime || data.event?.occurTime || '',
routeNo: data.routeNo || data.event?.routeNo || '',
event: {
...(data.event || {})
},
material: {
...(data.material || {})
},
report: {
...(data.report || {})
},
traffic: {
...(data.traffic || {})
}
})
} else {
ElMessage.warning(result?.message || '获取详情失败')
}
} catch (error) {
console.error('获取冰雪详情失败:', error)
ElMessage.error('获取详情失败,请稍后重试')
}
}
const handleBack = () => {
router.back()
}
onMounted(async () => {
await getAreaOptions()
if (route.query.id) {
await getDisasterDetail()
return
}
initFormData({})
})
return {
eventType,
filterForm,
formData,
formRef,
formRules,
handleBack,
handleDistrictChange,
handleEventTypeChange,
handleHasStrandedVehiclesChange,
handleRouteNoChange,
handleSubmit,
initFormData,
getFormData,
options,
routeTypeLabel,
strandedVehicleOptions,
submitting,
validate
}
}

View File

@ -166,7 +166,6 @@ const formData = reactive({
district: '', district: '',
endStakeNo: '', endStakeNo: '',
estimatedRecoveryCost: '', estimatedRecoveryCost: '',
inspectionMileage: '',
isBlocked: '', isBlocked: '',
needsRecovery: '', needsRecovery: '',
repairProgress: '', repairProgress: '',
@ -317,7 +316,6 @@ const resetForm = () => {
district: '', district: '',
endStakeNo: '', endStakeNo: '',
estimatedRecoveryCost: '', estimatedRecoveryCost: '',
inspectionMileage: '',
isBlocked: '', isBlocked: '',
needsRecovery: '', needsRecovery: '',
repairProgress: '', repairProgress: '',

View File

@ -68,6 +68,11 @@ const getLossDict = async () => {
} }
} }
const changeValue = (config, event) => {
const item = getValueItem(config)
item.totalAmount = event
}
onMounted(async () => { onMounted(async () => {
await getLossDict() await getLossDict()
}) })

View File

@ -1,32 +1,28 @@
<template> <template>
<div class="disaster-form-page"> <div class="disaster-form-page">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="140px" class="disaster-form" @submit.prevent> <el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px" class="disaster-form" @submit.prevent>
<!-- 基本信息区块 -->
<el-card class="form-section" shadow="never"> <el-card class="form-section" shadow="never">
<template #header> <template #header>
<div class="section-header"> <div class="section-header">
<span class="section-title">信息</span> <span class="section-title">信息</span>
</div> </div>
</template> </template>
<BlockItem title="填报人员信息"> <BlockItem title="填报人员信息">
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 填报单位 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="填报单位" prop="event.reporterUnit"> <el-form-item label="填报单位" prop="event.reporterUnit">
<el-input v-model="formData.event.reporterUnit" placeholder="请填写" /> <el-input v-model="formData.event.reporterUnit" disabled />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 联系人 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="联系人员" prop="event.contactPerson"> <el-form-item label="联系人员" prop="event.contactPerson">
<el-input v-model="formData.event.contactPerson" placeholder="请填写" /> <el-input v-model="formData.event.contactPerson" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 联系电话 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="联系电话" prop="event.contactPhone"> <el-form-item label="联系电话" prop="event.contactPhone">
<el-input v-model="formData.event.contactPhone" placeholder="请填写" /> <el-input v-model="formData.event.contactPhone" maxlength="11" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -36,87 +32,60 @@
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="事件类型"> <el-form-item label="事件类型">
<el-select v-model="eventType" placeholder="请选择" style="width: 100%" @change="handleEventTypeChange"> <el-select v-model="eventType" style="width: 100%" @change="handleEventTypeChange">
<el-option v-for="(option, idx) in options['eventType']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['eventType']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 路况类别 --> <el-col :span="8">
<el-form-item label="填报站点" prop="event.serviceStationId">
<YHZSelect v-model="formData.event.serviceStationId" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="发生时间" prop="occurTime">
<el-date-picker v-model="formData.occurTime" type="datetime" placeholder="请选择日期时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="路况类别" prop="roadConditionType"> <el-form-item label="路况类别" prop="roadConditionType">
<el-select v-model="formData.roadConditionType" placeholder="请选择" style="width: 100%"> <el-select v-model="formData.roadConditionType" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['waterRoadConditionType']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['waterRoadConditionType']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 是否阻断 -->
<el-col :span="8">
<el-form-item label="是否阻断" prop="event.isBlocked">
<el-select v-model="formData.event.isBlocked" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['yesNoBool']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 抢险进度 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="抢险进度" prop="event.repairProgress"> <el-form-item label="抢险进度" prop="event.repairProgress">
<el-select v-model="formData.event.repairProgress" placeholder="请选择" style="width: 100%"> <el-select v-model="formData.event.repairProgress" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['repairProgress']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['repairProgress']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 处理措施-->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="处理措施" prop="report.disposalMeasures"> <el-form-item label="是否阻断" prop="event.isBlocked">
<el-select v-model="formData.report.disposalMeasures" placeholder="请选择" style="width: 100%"> <el-select v-model="formData.event.isBlocked" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['disposalMeasures']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['yesNoBool']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 水毁处数 --> </el-row>
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="受灾里程" prop="event.blockedMileage">
<el-input v-model="formData.event.blockedMileage" placeholder="请填写">
<template #append>公里</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="水毁处数" prop="event.damageCount"> <el-form-item label="水毁处数" prop="event.damageCount">
<el-input-number v-model="formData.event.damageCount" :min="0" :step="1" style="width: 100%" placeholder="请填写"> <el-input v-model="formData.event.damageCount" placeholder="请填写">
<template #suffix> <template #append></template>
<span class="unit-text"></span> </el-input>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 阻断里程 -->
<el-col :span="8">
<el-form-item label="阻断里程" prop="event.blockedMileage">
<el-input-number v-model="formData.event.blockedMileage" :min="0" :precision="3" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">公里</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 发生时间 -->
<el-col :span="8">
<el-form-item label="发生时间" prop="occurTime">
<el-date-picker v-model="formData.occurTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
<!-- 预计恢复时间 -->
<el-col :span="8">
<el-form-item label="预计恢复时间" prop="report.expectRecoverTime">
<el-date-picker v-model="formData.report.expectRecoverTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 现场描述 -->
<el-col :span="16">
<el-form-item label="现场描述(绕行方案)" prop="report.siteDescription">
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="2" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -124,84 +93,78 @@
<BlockItem title="位置信息"> <BlockItem title="位置信息">
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 路线类型 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="路线类型"> <el-form-item label="路线类型">
<el-select v-model="filterForm.routeType" placeholder="请选择" style="width: 100%"> <el-input :model-value="routeTypeLabel" readonly />
<el-option v-for="(option, idx) in options['roadType']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 所属区县 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="所属区县" prop="event.district"> <el-form-item label="所属区县" prop="event.district">
<el-select v-model="formData.event.district" placeholder="请选择" style="width: 100%" @change="handleDistrictChange"> <el-select v-model="formData.event.district" placeholder="请选择" style="width: 100%" @change="handleDistrictChange">
<el-option v-for="(option, idx) in options['area']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['area']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="地点路线" prop="routeNo"> <el-form-item label="线路编号" prop="routeNo">
<RoadRoutesSelect v-model="formData.routeNo" @change="handleRouteNoChange" :extra-params="{ xzdj: filterForm.routeType, qxid: formData.event.district }" /> <RoadRoutesSelect v-model="formData.routeNo" :extra-params="{ xzdj: filterForm.routeType, qxid: formData.event.district }" @change="handleRouteNoChange" />
</el-form-item>
</el-col>
<!-- 起点桩号 -->
<el-col :span="8">
<el-form-item label="起点桩号(K)" prop="event.startStakeNo">
<el-input v-model="formData.event.startStakeNo" placeholder="请填写">
<template #append>K</template>
</el-input>
</el-form-item>
</el-col>
<!-- 止点桩号 -->
<el-col :span="8">
<el-form-item label="止点桩号(K)" prop="event.endStakeNo">
<el-input v-model="formData.event.endStakeNo" placeholder="请填写">
<template #append>K</template>
</el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 路况位置 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="路况位置" prop="occurLocation"> <el-form-item label="路况位置" prop="occurLocation">
<el-input v-model="formData.occurLocation" placeholder="请填写" /> <el-input v-model="formData.occurLocation" placeholder="请选择">
<template #suffix>
<el-icon class="location-icon"><LocationFilled /></el-icon>
</template>
</el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 阻断点小地名 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="阻断点小地名" prop="event.blockedPointName"> <el-form-item label="阻断点小地名" prop="event.blockedPointName">
<el-input v-model="formData.event.blockedPointName" placeholder="请填写" /> <el-input v-model="formData.event.blockedPointName" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<el-row :gutter="24">
<!-- 经度 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="经度" prop="event.longitude"> <el-form-item label="起点桩号" prop="event.startStakeNo">
<el-input v-model="formData.event.longitude" placeholder="经度"> </el-input> <el-input v-model="formData.event.startStakeNo" placeholder="请填写">
<template #prepend>K</template>
</el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<!-- 纬度 --> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="纬度" prop="event.latitude"> <el-form-item label="起点桩经度" prop="event.startStakeLongitude">
<el-input v-model="formData.event.latitude" placeholder="纬度"> </el-input> <el-input v-model="formData.event.startStakeLongitude" placeholder="请填写" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="起点桩纬度" prop="event.startStakeLatitude">
<el-input v-model="formData.event.startStakeLatitude" placeholder="请填写" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="止点桩号" prop="event.endStakeNo">
<el-input v-model="formData.event.endStakeNo" placeholder="请填写">
<template #prepend>K</template>
</el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="24"> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="图片上传" prop="fileList"> <el-form-item label="止点桩经度" prop="event.endStakeLongitude">
<FileUpload type="image" :limit="9" v-model="formData.fileList" /> <el-input v-model="formData.event.endStakeLongitude" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="视频上传" prop="fileList"> <el-form-item label="止点桩纬度" prop="event.endStakeLatitude">
<FileUpload type="video" :limit="9" v-model="formData.fileList" /> <el-input v-model="formData.event.endStakeLatitude" placeholder="请填写" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -211,12 +174,42 @@
<el-card class="form-section" shadow="never"> <el-card class="form-section" shadow="never">
<template #header> <template #header>
<div class="section-header"> <div class="section-header">
<span class="section-title">灾毁损失</span> <span class="section-title">处置情况</span>
</div> </div>
</template> </template>
<BlockItem title="路况事件信息">
<BlockItem>
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="处置措施" prop="report.disposalMeasures">
<el-select v-model="formData.report.disposalMeasures" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['disposalMeasures']" :key="idx" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="预计恢复时间" prop="report.expectRecoverTime">
<el-date-picker v-model="formData.report.expectRecoverTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="实际恢复时间" prop="report.actualRecoverTime">
<el-date-picker v-model="formData.report.actualRecoverTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
</el-row>
</BlockItem>
</el-card>
<el-card class="form-section" shadow="never">
<template #header>
<div class="section-header">
<span class="section-title">实施情况</span>
</div>
</template>
<BlockItem title="人员车辆">
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 受伤人员 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="受伤人员" prop="report.injuredCount"> <el-form-item label="受伤人员" prop="report.injuredCount">
<el-input-number v-model="formData.report.injuredCount" :min="0" :step="1" style="width: 100%"> <el-input-number v-model="formData.report.injuredCount" :min="0" :step="1" style="width: 100%">
@ -226,8 +219,6 @@
</el-input-number> </el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 死亡人员 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="死亡人员" prop="report.deadCount"> <el-form-item label="死亡人员" prop="report.deadCount">
<el-input-number v-model="formData.report.deadCount" :min="0" :step="1" style="width: 100%"> <el-input-number v-model="formData.report.deadCount" :min="0" :step="1" style="width: 100%">
@ -237,8 +228,6 @@
</el-input-number> </el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 滞留人员 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="滞留人员" prop="report.strandedPersonCount"> <el-form-item label="滞留人员" prop="report.strandedPersonCount">
<el-input-number v-model="formData.report.strandedPersonCount" :min="0" :step="1" style="width: 100%"> <el-input-number v-model="formData.report.strandedPersonCount" :min="0" :step="1" style="width: 100%">
@ -248,8 +237,9 @@
</el-input-number> </el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<!-- 损坏车辆 --> <el-row :gutter="24">
<el-col :span="8"> <el-col :span="8">
<el-form-item label="损坏车辆" prop="report.damagedVehicleCount"> <el-form-item label="损坏车辆" prop="report.damagedVehicleCount">
<el-input-number v-model="formData.report.damagedVehicleCount" :min="0" :step="1" style="width: 100%"> <el-input-number v-model="formData.report.damagedVehicleCount" :min="0" :step="1" style="width: 100%">
@ -259,8 +249,6 @@
</el-input-number> </el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 滞留车辆 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="滞留车辆" prop="report.strandedVehicleCount"> <el-form-item label="滞留车辆" prop="report.strandedVehicleCount">
<el-input-number v-model="formData.report.strandedVehicleCount" :min="0" :step="1" style="width: 100%"> <el-input-number v-model="formData.report.strandedVehicleCount" :min="0" :step="1" style="width: 100%">
@ -272,390 +260,110 @@
</el-col> </el-col>
</el-row> </el-row>
</BlockItem> </BlockItem>
<BlockItem title="道路损失及其他">
<LossList v-model:model-value="formData.report.lossList" /> <BlockItem title="灾毁损失">
<LossList v-model:model-value="formData.lossList" />
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 投入机械 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="投入机械" prop="report.investedMachinery"> <el-form-item label="投入机械" prop="report.investedMachinery">
<el-input-number v-model="formData.report.investedMachinery" :min="0" :precision="1" style="width: 100%" placeholder="请填写"> <el-input v-model="formData.report.investedMachinery" placeholder="请填写">
<template #suffix> <template #append>/</template>
<span class="unit-text">/</span> </el-input>
</template>
</el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 投入人力 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="投入人力" prop="report.investedManpower"> <el-form-item label="投入人力" prop="report.investedManpower">
<el-input-number v-model="formData.report.investedManpower" :min="0" :step="1" style="width: 100%" placeholder="请填写"> <el-input-number v-model="formData.report.investedManpower" :min="0" :step="1" style="width: 100%">
<template #suffix> <template #suffix>
<span class="unit-text">人次</span> <span class="unit-text">人次</span>
</template> </template>
</el-input-number> </el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 投入资金 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="投入资金" prop="report.investedFunds"> <el-form-item label="投入资金" prop="report.investedFunds">
<el-input-number v-model="formData.report.investedFunds" :min="0" :precision="2" style="width: 100%" placeholder="请填写"> <el-input v-model="formData.report.investedFunds" placeholder="请填写">
<template #suffix> <template #append>万元</template>
<span class="unit-text">万元</span> </el-input>
</template> </el-form-item>
</el-input-number> </el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12">
<el-form-item label="现场情况描述" prop="report.siteDescription">
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="3" placeholder="请填写" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="16">
<el-form-item label="附件">
<FileUpload v-model="formData.fileList" type="image" :limit="9" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</BlockItem> </BlockItem>
<BlockItem title="恢复重建预估费用"> <BlockItem title="恢复重建预估费用">
<el-row :gutter="24"> <el-row :gutter="24">
<!-- 是否需要恢复重建 -->
<el-col :span="8"> <el-col :span="8">
<el-form-item label="是否需要恢复重建" prop="event.needsRecovery"> <el-form-item label="是否需要恢复重建" prop="event.needsRecovery">
<el-select v-model="formData.event.needsRecovery" placeholder="请选择" style="width: 100%"> <el-select v-model="formData.event.needsRecovery" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['yesNoBool']" :label="option.label" :value="option.value" :key="idx" /> <el-option v-for="(option, idx) in options['yesNoBool']" :key="idx" :label="option.label" :value="option.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col v-if="showEstimatedRecoveryCost" :span="8">
<!-- 恢复重建预估费用 -->
<el-col :span="8" v-if="formData?.event.needsRecovery">
<el-form-item label="恢复重建预估费用" prop="event.estimatedRecoveryCost"> <el-form-item label="恢复重建预估费用" prop="event.estimatedRecoveryCost">
<el-input-number v-model="formData.event.estimatedRecoveryCost" :min="0" :precision="2" style="width: 100%" placeholder="请填写"> <el-input v-model="formData.event.estimatedRecoveryCost" placeholder="请填写">
<template #suffix> <template #append>万元</template>
<span class="unit-text">万元</span> </el-input>
</template>
</el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</BlockItem> </BlockItem>
</el-card> </el-card>
<!-- 提交按钮 -->
<div class="form-actions"> <div class="form-actions">
<el-button @click="handleBack">取消</el-button> <el-button @click="handleBack">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">提交</el-button> <el-button type="primary" :loading="submitting" @click="handleSubmit">提交</el-button>
</div> </div>
</el-form> </el-form>
<!-- 图片预览对话框 -->
<el-dialog v-model="previewDialogVisible" title="图片预览" width="600px">
<img :src="previewImageUrl" style="width: 100%" alt="预览图片" />
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue' import { LocationFilled } from '@element-plus/icons-vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { Plus, Upload } from '@element-plus/icons-vue'
import mockData from './waterMockJson.json'
import { request } from '@/utils/request'
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'
import { useOptions } from '@shared/composables/useOptions'
import RoadRoutesSelect from '../components/RoadRoutesSelect.vue' import RoadRoutesSelect from '../components/RoadRoutesSelect.vue'
import YHZSelect from '../components/YHZSelect.vue'
import LossList from './WaterDisasterLossListPC.vue'
import { useWaterDisasterReport } from './useWaterDisasterReport'
const router = useRouter() const {
const route = useRoute() eventType,
const { options, getAreaOptions } = useOptions() filterForm,
const formRef = ref(null) formData,
const submitting = ref(false) formRef,
formRules,
handleBack,
handleDistrictChange,
handleEventTypeChange,
handleRouteNoChange,
handleSubmit,
initFormData,
getFormData,
options,
routeTypeLabel,
showEstimatedRecoveryCost,
submitting,
validate
} = useWaterDisasterReport()
//
const isContinue = computed(() => route.query.isContinue === 'true')
//
const disposalMeasuresArray = ref([])
//
const imageFileList = ref([])
const videoFileList = ref([])
const eventType = ref('水毁事件')
const filterForm = reactive({
routeType: ''
})
const formData = reactive({
//
occurLocation: null, // /
occurTime: null, //
roadConditionType: null, //
routeNo: null, // 线
// event
event: {
blockedMileage: null, //
blockedPointName: null, //
contactPerson: null, //
contactPhone: null, //
damageCount: null, //
district: null, //
endStakeNo: null, //
estimatedRecoveryCost: null, //
inspectionMileage: null, //
isBlocked: null, //
needsRecovery: null, //
repairProgress: null, //
reporterUnit: null, //
startStakeNo: null //
},
// report
report: {
actualRecoverTime: null, //
damagedVehicleCount: null, //
deadCount: null, //
disposalMeasures: null, //
expectRecoverTime: null, //
injuredCount: null, //
investedFunds: null, //
investedMachinery: null, //
investedManpower: null, //
remark: null, // /
siteDescription: null, //
strandedPersonCount: null, //
strandedVehicleCount: null, //
totalLossAmount: null //
},
// lossList
lossList: [],
// fileList
fileList: []
})
const handleEventTypeChange = () => {
router.replace({ path: '/iceDisasterReport' })
}
// report.disposalMeasures
watch(
disposalMeasuresArray,
(newVal) => {
formData.report.disposalMeasures = newVal.length ? newVal.join(',') : null
},
{ deep: true }
)
// fileList
watch(
imageFileList,
() => {
syncFileList()
},
{ deep: true }
)
// fileList
watch(
videoFileList,
() => {
syncFileList()
},
{ deep: true }
)
// fileList
const syncFileList = () => {
formData.fileList = [
...imageFileList.value.map((f) => ({
fileName: f.name || '',
fileSize: f.size || 0,
fileType: 1, // 1-
fileUrl: f.url || f.content || ''
})),
...videoFileList.value.map((f) => ({
fileName: f.name || '',
fileSize: f.size || 0,
fileType: 2, // 2-
fileUrl: f.url || f.content || ''
}))
]
}
// report.disposalMeasures
watch(
() => formData.report.disposalMeasures,
(newVal) => {
if (newVal && typeof newVal === 'string') {
disposalMeasuresArray.value = newVal.split(',').filter(Boolean)
} else {
disposalMeasuresArray.value = []
}
},
{ immediate: true }
)
//
const formRules = {
roadConditionType: [{ required: true, message: '请选择路况类别', trigger: 'change' }],
'event.isBlocked': [{ required: true, message: '请选择是否阻断', trigger: 'change' }],
'event.repairProgress': [{ required: true, message: '请选择抢险进度', trigger: 'change' }],
'report.disposalMeasures': [{ required: true, message: '请选择处置措施', trigger: 'change' }],
'event.damageCount': [{ required: true, message: '请输入水毁处数', trigger: 'blur' }],
'event.blockedMileage': [{ required: true, message: '请输入阻断里程', trigger: 'blur' }],
occurTime: [{ required: true, message: '请选择发生时间', trigger: 'change' }],
'report.expectRecoverTime': [{ required: true, message: '请输入预计恢复时间', trigger: 'blur' }],
routeNo: [{ required: true, message: '请输入线路编号', trigger: 'blur' }],
'event.startStakeNo': [{ required: true, message: '请输入起点桩号', trigger: 'blur' }],
'event.endStakeNo': [{ required: true, message: '请输入止点桩号', trigger: 'blur' }],
occurLocation: [{ required: true, message: '请输入路况位置', trigger: 'blur' }],
'event.blockedPointName': [{ required: true, message: '请输入阻断点小地名', trigger: 'blur' }],
'event.longitude': [{ required: true, message: '请输入经度', trigger: 'blur' }],
'event.latitude': [{ required: true, message: '请输入纬度', trigger: 'blur' }],
'event.needsRecovery': [{ required: true, message: '请选择是否需要恢复重建', trigger: 'change' }],
'event.estimatedRecoveryCost': [{ required: true, message: '请输入恢复重建预估费用', trigger: 'blur' }]
// 'event.reporterUnit': [{ required: true, message: '', trigger: 'blur' }],
// 'event.contactPerson': [{ required: true, message: '', trigger: 'blur' }],
// 'event.contactPhone': [
// { required: true, message: '', trigger: 'blur' },
// { pattern: /^1[3-9]\d{9}$/, message: '', trigger: 'blur' }
// ]
}
//
const beforeImageUpload = (file) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt500k = file.size / 1024 < 500
if (!isJpgOrPng) {
ElMessage.error('只能上传 JPG/PNG 格式的图片!')
return false
}
if (!isLt500k) {
ElMessage.error('图片大小不能超过 500KB!')
return false
}
return false // false
}
//
const beforeVideoUpload = (file) => {
const isLt20M = file.size / 1024 / 1024 < 20
if (!isLt20M) {
ElMessage.error('视频大小不能超过 20MB!')
return false
}
return false
}
//
const previewDialogVisible = ref(false)
const previewImageUrl = ref('')
const handlePicturePreview = (file) => {
previewImageUrl.value = file.url
previewDialogVisible.value = true
}
const handlePictureRemove = (file, fileList) => {
imageFileList.value = fileList
}
//
const handleBack = () => {
router.back()
}
// /
const initFormData = (data) => {
Object.assign(formData, data)
}
const handleDistrictChange = () => {
formData.routeNo = null
}
const handleRouteNoChange = (item) => {
formData.event.startStakeNo = item.startStakeNo
formData.event.endStakeNo = item.endStakeNo
}
//
const getFormData = () => {
return { ...formData }
}
//
const validate = async () => {
if (!formRef.value) return false
try {
await formRef.value.validate()
return true
} catch (error) {
ElMessage.warning('请完善表单信息')
return false
}
}
//
const handleSubmit = async () => {
//
if (!(await validate())) {
return
}
submitting.value = true
try {
//
//
const submitData = {
...formData
//
}
const res = await request({
url: '/snow-ops-platform/water-damage/addOrUpdate',
method: 'post',
data: submitData
})
if (res?.code === '00000') {
ElMessage.success('提交成功')
} else {
ElMessage.error(res.message)
}
//
setTimeout(() => {
router.replace('/disasterManagement')
}, 1000)
} catch (error) {
ElMessage.error('提交失败,请重试')
console.error('提交失败:', error)
} finally {
submitting.value = false
}
}
//
const loadEditData = async () => {
if (route.query.mock) {
initFormData(mockData)
} else {
initFormData({})
}
}
onMounted(() => {
//
getAreaOptions()
loadEditData()
})
//
defineExpose({ defineExpose({
validate, validate,
initFormData, initFormData,
@ -669,9 +377,9 @@ defineExpose({
background-color: #f5f7fa; background-color: #f5f7fa;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
}
.disaster-form { .form-section {
.form-section {
margin-bottom: 20px; margin-bottom: 20px;
:deep(.el-card__header) { :deep(.el-card__header) {
@ -683,10 +391,9 @@ defineExpose({
:deep(.el-card__body) { :deep(.el-card__body) {
padding: 20px; padding: 20px;
} }
} }
.section-header { .section-title {
.section-title {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: #303133; color: #303133;
@ -701,42 +408,24 @@ defineExpose({
transform: translateY(-50%); transform: translateY(-50%);
width: 3px; width: 3px;
height: 16px; height: 16px;
background-color: #409eff;
border-radius: 2px; border-radius: 2px;
background-color: #409eff;
} }
} }
}
.sub-section-title { .location-icon {
font-size: 14px; color: #909399;
font-weight: 500; }
color: #606266;
margin: 8px 0 16px 0;
padding-left: 8px;
border-left: 3px solid #409eff;
}
.unit-text { .unit-text {
color: #909399; color: #909399;
font-size: 12px; font-size: 12px;
} }
.upload-tip { .form-actions {
font-size: 12px;
color: #909399;
margin-top: 8px;
}
.video-preview {
margin-top: 12px;
}
.form-actions {
display: flex; display: flex;
justify-content: center; justify-content: center;
gap: 16px; gap: 16px;
padding: 20px 0 40px; padding: 8px 0 32px;
}
}
} }
</style> </style>

View File

@ -0,0 +1,742 @@
<template>
<div class="disaster-form-page">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="140px" class="disaster-form" @submit.prevent>
<!-- 基本信息区块 -->
<el-card class="form-section" shadow="never">
<template #header>
<div class="section-header">
<span class="section-title">基本信息</span>
</div>
</template>
<BlockItem title="填报人员信息">
<el-row :gutter="24">
<!-- 填报单位 -->
<el-col :span="8">
<el-form-item label="填报单位" prop="event.reporterUnit">
<el-input v-model="formData.event.reporterUnit" placeholder="请填写" />
</el-form-item>
</el-col>
<!-- 联系人 -->
<el-col :span="8">
<el-form-item label="联系人员" prop="event.contactPerson">
<el-input v-model="formData.event.contactPerson" placeholder="请填写" />
</el-form-item>
</el-col>
<!-- 联系电话 -->
<el-col :span="8">
<el-form-item label="联系电话" prop="event.contactPhone">
<el-input v-model="formData.event.contactPhone" placeholder="请填写" />
</el-form-item>
</el-col>
</el-row>
</BlockItem>
<BlockItem title="路况事件信息">
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="事件类型">
<el-select v-model="eventType" placeholder="请选择" style="width: 100%" @change="handleEventTypeChange">
<el-option v-for="(option, idx) in options['eventType']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
<!-- 路况类别 -->
<el-col :span="8">
<el-form-item label="路况类别" prop="roadConditionType">
<el-select v-model="formData.roadConditionType" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['waterRoadConditionType']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
<!-- 是否阻断 -->
<el-col :span="8">
<el-form-item label="是否阻断" prop="event.isBlocked">
<el-select v-model="formData.event.isBlocked" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['yesNoBool']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 抢险进度 -->
<el-col :span="8">
<el-form-item label="抢险进度" prop="event.repairProgress">
<el-select v-model="formData.event.repairProgress" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['repairProgress']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
<!-- 处理措施-->
<el-col :span="8">
<el-form-item label="处理措施" prop="report.disposalMeasures">
<el-select v-model="formData.report.disposalMeasures" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['disposalMeasures']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
<!-- 水毁处数 -->
<el-col :span="8">
<el-form-item label="水毁处数" prop="event.damageCount">
<el-input-number v-model="formData.event.damageCount" :min="0" :step="1" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 阻断里程 -->
<el-col :span="8">
<el-form-item label="阻断里程" prop="event.blockedMileage">
<el-input-number v-model="formData.event.blockedMileage" :min="0" :precision="3" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">公里</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 发生时间 -->
<el-col :span="8">
<el-form-item label="发生时间" prop="occurTime">
<el-date-picker v-model="formData.occurTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
<!-- 预计恢复时间 -->
<el-col :span="8">
<el-form-item label="预计恢复时间" prop="report.expectRecoverTime">
<el-date-picker v-model="formData.report.expectRecoverTime" type="datetime" placeholder="请选择时间" style="width: 100%" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 现场描述 -->
<el-col :span="16">
<el-form-item label="现场描述(绕行方案)" prop="report.siteDescription">
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="2" placeholder="请填写" />
</el-form-item>
</el-col>
</el-row>
</BlockItem>
<BlockItem title="位置信息">
<el-row :gutter="24">
<!-- 路线类型 -->
<el-col :span="8">
<el-form-item label="路线类型">
<el-select v-model="filterForm.routeType" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['roadType']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
<!-- 所属区县 -->
<el-col :span="8">
<el-form-item label="所属区县" prop="event.district">
<el-select v-model="formData.event.district" placeholder="请选择" style="width: 100%" @change="handleDistrictChange">
<el-option v-for="(option, idx) in options['area']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="地点路线" prop="routeNo">
<RoadRoutesSelect v-model="formData.routeNo" @change="handleRouteNoChange" :extra-params="{ xzdj: filterForm.routeType, qxid: formData.event.district }" />
</el-form-item>
</el-col>
<!-- 起点桩号 -->
<el-col :span="8">
<el-form-item label="起点桩号(K)" prop="event.startStakeNo">
<el-input v-model="formData.event.startStakeNo" placeholder="请填写">
<template #append>K</template>
</el-input>
</el-form-item>
</el-col>
<!-- 止点桩号 -->
<el-col :span="8">
<el-form-item label="止点桩号(K)" prop="event.endStakeNo">
<el-input v-model="formData.event.endStakeNo" placeholder="请填写">
<template #append>K</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 路况位置 -->
<el-col :span="8">
<el-form-item label="路况位置" prop="occurLocation">
<el-input v-model="formData.occurLocation" placeholder="请填写" />
</el-form-item>
</el-col>
<!-- 阻断点小地名 -->
<el-col :span="8">
<el-form-item label="阻断点小地名" prop="event.blockedPointName">
<el-input v-model="formData.event.blockedPointName" placeholder="请填写" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<!-- 经度 -->
<el-col :span="8">
<el-form-item label="经度" prop="event.longitude">
<el-input v-model="formData.event.longitude" placeholder="经度"> </el-input>
</el-form-item>
</el-col>
<!-- 纬度 -->
<el-col :span="8">
<el-form-item label="纬度" prop="event.latitude">
<el-input v-model="formData.event.latitude" placeholder="纬度"> </el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="8">
<el-form-item label="图片上传" prop="fileList">
<FileUpload type="image" :limit="9" v-model="formData.fileList" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="视频上传" prop="fileList">
<FileUpload type="video" :limit="9" v-model="formData.fileList" />
</el-form-item>
</el-col>
</el-row>
</BlockItem>
</el-card>
<el-card class="form-section" shadow="never">
<template #header>
<div class="section-header">
<span class="section-title">灾毁损失</span>
</div>
</template>
<BlockItem title="路况事件信息">
<el-row :gutter="24">
<!-- 受伤人员 -->
<el-col :span="8">
<el-form-item label="受伤人员" prop="report.injuredCount">
<el-input-number v-model="formData.report.injuredCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 死亡人员 -->
<el-col :span="8">
<el-form-item label="死亡人员" prop="report.deadCount">
<el-input-number v-model="formData.report.deadCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 滞留人员 -->
<el-col :span="8">
<el-form-item label="滞留人员" prop="report.strandedPersonCount">
<el-input-number v-model="formData.report.strandedPersonCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 损坏车辆 -->
<el-col :span="8">
<el-form-item label="损坏车辆" prop="report.damagedVehicleCount">
<el-input-number v-model="formData.report.damagedVehicleCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 滞留车辆 -->
<el-col :span="8">
<el-form-item label="滞留车辆" prop="report.strandedVehicleCount">
<el-input-number v-model="formData.report.strandedVehicleCount" :min="0" :step="1" style="width: 100%">
<template #suffix>
<span class="unit-text"></span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
</BlockItem>
<BlockItem title="道路损失及其他">
<LossList v-model:model-value="formData.report.lossList" />
<el-row :gutter="24">
<!-- 投入机械 -->
<el-col :span="8">
<el-form-item label="投入机械" prop="report.investedMachinery">
<el-input-number v-model="formData.report.investedMachinery" :min="0" :precision="1" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">/</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 投入人力 -->
<el-col :span="8">
<el-form-item label="投入人力" prop="report.investedManpower">
<el-input-number v-model="formData.report.investedManpower" :min="0" :step="1" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">人次</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
<!-- 投入资金 -->
<el-col :span="8">
<el-form-item label="投入资金" prop="report.investedFunds">
<el-input-number v-model="formData.report.investedFunds" :min="0" :precision="2" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">万元</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
</BlockItem>
<BlockItem title="恢复重建预估费用">
<el-row :gutter="24">
<!-- 是否需要恢复重建 -->
<el-col :span="8">
<el-form-item label="是否需要恢复重建" prop="event.needsRecovery">
<el-select v-model="formData.event.needsRecovery" placeholder="请选择" style="width: 100%">
<el-option v-for="(option, idx) in options['yesNoBool']" :label="option.label" :value="option.value" :key="idx" />
</el-select>
</el-form-item>
</el-col>
<!-- 恢复重建预估费用 -->
<el-col :span="8" v-if="formData?.event.needsRecovery">
<el-form-item label="恢复重建预估费用" prop="event.estimatedRecoveryCost">
<el-input-number v-model="formData.event.estimatedRecoveryCost" :min="0" :precision="2" style="width: 100%" placeholder="请填写">
<template #suffix>
<span class="unit-text">万元</span>
</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
</BlockItem>
</el-card>
<!-- 提交按钮 -->
<div class="form-actions">
<el-button @click="handleBack">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">提交</el-button>
</div>
</el-form>
<!-- 图片预览对话框 -->
<el-dialog v-model="previewDialogVisible" title="图片预览" width="600px">
<img :src="previewImageUrl" style="width: 100%" alt="预览图片" />
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { Plus, Upload } from '@element-plus/icons-vue'
import mockData from './waterMockJson.json'
import { request } from '@/utils/request'
import LossList from './WaterDisasterLossListPC.vue'
import BlockItem from '@/component/BlockItem.vue'
import FileUpload from '@/component/FileUpload/FileUpload.vue'
import { useOptions } from '@shared/composables/useOptions'
import RoadRoutesSelect from '../components/RoadRoutesSelect.vue'
const router = useRouter()
const route = useRoute()
const { options, getAreaOptions } = useOptions()
const formRef = ref(null)
const submitting = ref(false)
//
const isContinue = computed(() => route.query.isContinue === 'true')
//
const disposalMeasuresArray = ref([])
//
const imageFileList = ref([])
const videoFileList = ref([])
const eventType = ref('水毁事件')
const filterForm = reactive({
routeType: ''
})
const formData = reactive({
//
occurLocation: null, // /
occurTime: null, //
roadConditionType: null, //
routeNo: null, // 线
// event
event: {
blockedMileage: null, //
blockedPointName: null, //
contactPerson: null, //
contactPhone: null, //
damageCount: null, //
district: null, //
endStakeNo: null, //
estimatedRecoveryCost: null, //
inspectionMileage: null, //
isBlocked: null, //
needsRecovery: null, //
repairProgress: null, //
reporterUnit: null, //
startStakeNo: null //
},
// report
report: {
actualRecoverTime: null, //
damagedVehicleCount: null, //
deadCount: null, //
disposalMeasures: null, //
expectRecoverTime: null, //
injuredCount: null, //
investedFunds: null, //
investedMachinery: null, //
investedManpower: null, //
remark: null, // /
siteDescription: null, //
strandedPersonCount: null, //
strandedVehicleCount: null, //
totalLossAmount: null //
},
// lossList
lossList: [],
// fileList
fileList: []
})
const handleEventTypeChange = () => {
router.replace({ path: '/iceDisasterReport' })
}
// report.disposalMeasures
watch(
disposalMeasuresArray,
(newVal) => {
formData.report.disposalMeasures = newVal.length ? newVal.join(',') : null
},
{ deep: true }
)
// fileList
watch(
imageFileList,
() => {
syncFileList()
},
{ deep: true }
)
// fileList
watch(
videoFileList,
() => {
syncFileList()
},
{ deep: true }
)
// fileList
const syncFileList = () => {
formData.fileList = [
...imageFileList.value.map((f) => ({
fileName: f.name || '',
fileSize: f.size || 0,
fileType: 1, // 1-
fileUrl: f.url || f.content || ''
})),
...videoFileList.value.map((f) => ({
fileName: f.name || '',
fileSize: f.size || 0,
fileType: 2, // 2-
fileUrl: f.url || f.content || ''
}))
]
}
// report.disposalMeasures
watch(
() => formData.report.disposalMeasures,
(newVal) => {
if (newVal && typeof newVal === 'string') {
disposalMeasuresArray.value = newVal.split(',').filter(Boolean)
} else {
disposalMeasuresArray.value = []
}
},
{ immediate: true }
)
//
const formRules = {
roadConditionType: [{ required: true, message: '请选择路况类别', trigger: 'change' }],
'event.isBlocked': [{ required: true, message: '请选择是否阻断', trigger: 'change' }],
'event.repairProgress': [{ required: true, message: '请选择抢险进度', trigger: 'change' }],
'report.disposalMeasures': [{ required: true, message: '请选择处置措施', trigger: 'change' }],
'event.damageCount': [{ required: true, message: '请输入水毁处数', trigger: 'blur' }],
'event.blockedMileage': [{ required: true, message: '请输入阻断里程', trigger: 'blur' }],
occurTime: [{ required: true, message: '请选择发生时间', trigger: 'change' }],
'report.expectRecoverTime': [{ required: true, message: '请输入预计恢复时间', trigger: 'blur' }],
routeNo: [{ required: true, message: '请输入线路编号', trigger: 'blur' }],
'event.startStakeNo': [{ required: true, message: '请输入起点桩号', trigger: 'blur' }],
'event.endStakeNo': [{ required: true, message: '请输入止点桩号', trigger: 'blur' }],
occurLocation: [{ required: true, message: '请输入路况位置', trigger: 'blur' }],
'event.blockedPointName': [{ required: true, message: '请输入阻断点小地名', trigger: 'blur' }],
'event.longitude': [{ required: true, message: '请输入经度', trigger: 'blur' }],
'event.latitude': [{ required: true, message: '请输入纬度', trigger: 'blur' }],
'event.needsRecovery': [{ required: true, message: '请选择是否需要恢复重建', trigger: 'change' }],
'event.estimatedRecoveryCost': [{ required: true, message: '请输入恢复重建预估费用', trigger: 'blur' }]
// 'event.reporterUnit': [{ required: true, message: '', trigger: 'blur' }],
// 'event.contactPerson': [{ required: true, message: '', trigger: 'blur' }],
// 'event.contactPhone': [
// { required: true, message: '', trigger: 'blur' },
// { pattern: /^1[3-9]\d{9}$/, message: '', trigger: 'blur' }
// ]
}
//
const beforeImageUpload = (file) => {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt500k = file.size / 1024 < 500
if (!isJpgOrPng) {
ElMessage.error('只能上传 JPG/PNG 格式的图片!')
return false
}
if (!isLt500k) {
ElMessage.error('图片大小不能超过 500KB!')
return false
}
return false // false
}
//
const beforeVideoUpload = (file) => {
const isLt20M = file.size / 1024 / 1024 < 20
if (!isLt20M) {
ElMessage.error('视频大小不能超过 20MB!')
return false
}
return false
}
//
const previewDialogVisible = ref(false)
const previewImageUrl = ref('')
const handlePicturePreview = (file) => {
previewImageUrl.value = file.url
previewDialogVisible.value = true
}
const handlePictureRemove = (file, fileList) => {
imageFileList.value = fileList
}
//
const handleBack = () => {
router.back()
}
// /
const initFormData = (data) => {
Object.assign(formData, data)
}
const handleDistrictChange = () => {
formData.routeNo = null
}
const handleRouteNoChange = (item) => {
formData.event.startStakeNo = item.startStakeNo
formData.event.endStakeNo = item.endStakeNo
}
//
const getFormData = () => {
return { ...formData }
}
//
const validate = async () => {
if (!formRef.value) return false
try {
await formRef.value.validate()
return true
} catch (error) {
ElMessage.warning('请完善表单信息')
return false
}
}
//
const handleSubmit = async () => {
//
if (!(await validate())) {
return
}
submitting.value = true
try {
//
//
const submitData = {
...formData
//
}
const res = await request({
url: '/snow-ops-platform/water-damage/addOrUpdate',
method: 'post',
data: submitData
})
if (res?.code === '00000') {
ElMessage.success('提交成功')
} else {
ElMessage.error(res.message)
}
//
setTimeout(() => {
router.replace('/disasterManagement')
}, 1000)
} catch (error) {
ElMessage.error('提交失败,请重试')
console.error('提交失败:', error)
} finally {
submitting.value = false
}
}
//
const loadEditData = async () => {
if (route.query.mock) {
initFormData(mockData)
} else {
initFormData({})
}
}
onMounted(() => {
//
getAreaOptions()
loadEditData()
})
//
defineExpose({
validate,
initFormData,
getFormData
})
</script>
<style scoped lang="scss">
.disaster-form-page {
padding: 20px;
background-color: #f5f7fa;
height: 100%;
overflow: auto;
.disaster-form {
.form-section {
margin-bottom: 20px;
:deep(.el-card__header) {
padding: 12px 20px;
background-color: #fafafa;
border-bottom: 1px solid #ebeef5;
}
:deep(.el-card__body) {
padding: 20px;
}
}
.section-header {
.section-title {
font-size: 16px;
font-weight: 600;
color: #303133;
position: relative;
padding-left: 10px;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 16px;
background-color: #409eff;
border-radius: 2px;
}
}
}
.sub-section-title {
font-size: 14px;
font-weight: 500;
color: #606266;
margin: 8px 0 16px 0;
padding-left: 8px;
border-left: 3px solid #409eff;
}
.unit-text {
color: #909399;
font-size: 12px;
}
.upload-tip {
font-size: 12px;
color: #909399;
margin-top: 8px;
}
.video-preview {
margin-top: 12px;
}
.form-actions {
display: flex;
justify-content: center;
gap: 16px;
padding: 20px 0 40px;
}
}
}
</style>

View File

@ -0,0 +1,292 @@
import { computed, onMounted, reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { request } from '@/utils/request'
import { useOptions } from '@shared/composables/useOptions'
import mockData from './waterMockJson.json'
const DEFAULT_REPORTER_UNIT = '万州区公路中心'
const createDefaultFormData = () => ({
occurLocation: '',
occurTime: '',
roadConditionType: '',
routeNo: '',
event: {
blockedMileage: null,
blockedPointName: '',
contactPerson: '',
contactPhone: '',
damageCount: null,
district: '',
endStakeLatitude: null,
endStakeLongitude: null,
endStakeNo: '',
estimatedRecoveryCost: null,
isBlocked: null,
latitude: null,
longitude: null,
needsRecovery: null,
repairProgress: '抢险中',
reportSite: '',
reporterUnit: DEFAULT_REPORTER_UNIT,
serviceStationId: '',
startStakeLatitude: null,
startStakeLongitude: null,
startStakeNo: ''
},
report: {
actualRecoverTime: '',
damagedVehicleCount: null,
deadCount: null,
disposalMeasures: '',
expectRecoverTime: '',
injuredCount: null,
investedFunds: null,
investedMachinery: null,
investedManpower: null,
remark: '',
siteDescription: '',
strandedPersonCount: null,
strandedVehicleCount: null,
totalLossAmount: null
},
lossList: [],
fileList: []
})
const mergeFormData = (source = {}) => {
const defaults = createDefaultFormData()
const merged = {
...defaults,
...source,
event: {
...defaults.event,
...(source.event || {})
},
report: {
...defaults.report,
...(source.report || {})
},
lossList: Array.isArray(source.lossList) ? source.lossList : defaults.lossList,
fileList: Array.isArray(source.fileList) ? source.fileList : defaults.fileList
}
if (!merged.event.reporterUnit) {
merged.event.reporterUnit = DEFAULT_REPORTER_UNIT
}
if (!merged.event.repairProgress) {
merged.event.repairProgress = '抢险中'
}
if (!merged.event.serviceStationId && merged.event.reportSite) {
merged.event.serviceStationId = merged.event.reportSite
}
return merged
}
export const useWaterDisasterReport = () => {
const router = useRouter()
const route = useRoute()
const { options, getAreaOptions } = useOptions()
const formRef = ref(null)
const submitting = ref(false)
const eventType = ref('水毁事件')
const filterForm = reactive({
routeType: ''
})
const formData = reactive(createDefaultFormData())
const routeTypeLabel = computed(() => {
const matched = options.value.roadType?.find((item) => item.value === filterForm.routeType)
return matched?.label || '国省道'
})
const showEstimatedRecoveryCost = computed(() => formData.event.needsRecovery === true)
const formRules = {
'event.contactPerson': [{ required: true, message: '请输入联系人员', trigger: 'blur' }],
'event.contactPhone': [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的联系电话', trigger: 'blur' }
],
'event.serviceStationId': [{ required: true, message: '请选择填报站点', trigger: 'change' }],
occurTime: [{ required: true, message: '请选择发生时间', trigger: 'change' }],
roadConditionType: [{ required: true, message: '请选择路况类别', trigger: 'change' }],
'event.repairProgress': [{ required: true, message: '请选择抢险进度', trigger: 'change' }],
'event.isBlocked': [{ required: true, message: '请选择是否阻断', trigger: 'change' }],
'event.blockedMileage': [{ required: true, message: '请输入受灾里程', trigger: 'blur' }],
'event.damageCount': [{ required: true, message: '请输入水毁处数', trigger: 'blur' }],
'event.district': [{ required: true, message: '请选择所属区县', trigger: 'change' }],
routeNo: [{ required: true, message: '请选择线路编号', trigger: 'change' }],
occurLocation: [{ required: true, message: '请输入路况位置', trigger: 'blur' }],
'event.blockedPointName': [{ required: true, message: '请输入阻断点小地名', trigger: 'blur' }],
'event.startStakeNo': [{ required: true, message: '请输入起点桩号', trigger: 'blur' }],
'event.startStakeLongitude': [{ required: true, message: '请输入起点桩经度', trigger: 'blur' }],
'event.startStakeLatitude': [{ required: true, message: '请输入起点桩纬度', trigger: 'blur' }],
'event.endStakeNo': [{ required: true, message: '请输入止点桩号', trigger: 'blur' }],
'event.endStakeLongitude': [{ required: true, message: '请输入止点桩经度', trigger: 'blur' }],
'event.endStakeLatitude': [{ required: true, message: '请输入止点桩纬度', trigger: 'blur' }],
'report.disposalMeasures': [{ required: true, message: '请选择处置措施', trigger: 'change' }],
'report.expectRecoverTime': [{ required: true, message: '请选择预计恢复时间', trigger: 'change' }],
'event.needsRecovery': [{ required: true, message: '请选择是否需要恢复重建', trigger: 'change' }],
'event.estimatedRecoveryCost': [
{
validator: (_rule, value, callback) => {
if (showEstimatedRecoveryCost.value && (value === '' || value === null || value === undefined)) {
callback(new Error('请输入恢复重建预估费用'))
return
}
callback()
},
trigger: 'blur'
}
]
}
const initFormData = (data = {}) => {
Object.assign(formData, mergeFormData(data))
}
const handleEventTypeChange = (value) => {
if (value === '冰雪事件') {
router.replace({ path: '/iceDisasterReport' })
}
}
const handleDistrictChange = () => {
// formData.routeNo = ''
// formData.occurLocation = ''
// formData.event.startStakeNo = ''
// formData.event.endStakeNo = ''
}
const parsePointValue = (point) => {
if (!point) {
return { longitude: null, latitude: null }
}
if (Array.isArray(point) && point.length >= 2) {
return {
longitude: point[0] ?? null,
latitude: point[1] ?? null
}
}
if (typeof point === 'string') {
try {
const parsed = JSON.parse(point)
if (Array.isArray(parsed) && parsed.length >= 2) {
return {
longitude: parsed[0] ?? null,
latitude: parsed[1] ?? null
}
}
} catch (_error) {
return { longitude: null, latitude: null }
}
}
return { longitude: null, latitude: null }
}
const handleRouteNoChange = (item = {}) => {
formData.routeNo = item.routeCode
formData.event.startStakeNo = item.startStakeNo
formData.event.endStakeNo = item.endStakeNo
const startPoint = parsePointValue(item.startPoint)
const endPoint = parsePointValue(item.endPoint)
formData.event.startStakeLongitude = startPoint.longitude
formData.event.startStakeLatitude = startPoint.latitude
formData.event.endStakeLongitude = endPoint.longitude
formData.event.endStakeLatitude = endPoint.latitude
}
const buildSubmitData = () => {
const payload = mergeFormData(formData)
if (!payload.event.reportSite && payload.event.serviceStationId) {
payload.event.reportSite = payload.event.serviceStationId
}
return payload
}
const getFormData = () => buildSubmitData()
const validate = async () => {
if (!formRef.value) {
return false
}
try {
await formRef.value.validate()
return true
} catch (_error) {
ElMessage.warning('请完善表单信息')
return false
}
}
const handleSubmit = async () => {
if (!(await validate())) {
return
}
submitting.value = true
try {
const res = await request({
url: '/snow-ops-platform/water-damage/addOrUpdate',
method: 'post',
data: buildSubmitData()
})
if (res?.code === '00000') {
ElMessage.success('提交成功')
setTimeout(() => {
router.replace('/disasterManagement')
}, 500)
} else {
ElMessage.error(res?.message || '提交失败')
}
} catch (error) {
console.error('提交失败:', error)
ElMessage.error('提交失败,请重试')
} finally {
submitting.value = false
}
}
const handleBack = () => {
router.back()
}
onMounted(async () => {
await getAreaOptions()
initFormData(route.query.mock ? mockData : {})
})
return {
eventType,
filterForm,
formData,
formRef,
formRules,
handleBack,
handleDistrictChange,
handleEventTypeChange,
handleRouteNoChange,
handleSubmit,
initFormData,
getFormData,
options,
routeTypeLabel,
showEstimatedRecoveryCost,
submitting,
validate
}
}

View File

@ -12,7 +12,6 @@
"district": "武侯区", "district": "武侯区",
"endStakeNo": "K2251+200", "endStakeNo": "K2251+200",
"estimatedRecoveryCost": 120.5, "estimatedRecoveryCost": 120.5,
"inspectionMileage": 25.6,
"isBlocked": true, "isBlocked": true,
"needsRecovery": true, "needsRecovery": true,
"repairProgress": "抢险中", "repairProgress": "抢险中",

View File

@ -0,0 +1,170 @@
<template>
<el-row class="material-list-pc" :gutter="24">
<template v-for="(item, index) in configs" :key="index">
<el-col :span="colSpan">
<el-form-item :label="item.wzmc">
<el-input :modelValue="getValue(item)" :placeholder="`余额: ${item.ye} `" @update:modelValue="(event) => changeValue(item, event)">
<template #suffix>
<span>{{ item.dw }}</span>
</template>
</el-input>
</el-form-item>
</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({
yhzId: {
type: [Number, null, String],
default: null
},
modelValue: {
type: Array,
default: () => []
},
colSpan: {
type: Number,
default: 8
}
})
watch(()=> props.yhzId, ()=> {
emit('update:modelValue', [])
getDict()
})
const getValue = (config) => {
const item = getValueItem(config)
return item?.usageAmount || 0
}
const getValueItem = (config) => {
let item = props.modelValue.find((v) => v.yhzid === config.yhzid)
if (item == null) {
item = { ...config }
props.modelValue.push(item)
}
return item
}
const changeValue = (item, value) => {
const index = props.modelValue.findIndex((v) => v.yhzid === item.yhzid)
if (index > -1) {
props.modelValue[index].usageAmount = value
}
}
const configs = ref([])
//
const getDict = async () => {
if(!props.yhzId) return []
try {
const res = await request({
url: `/snow-ops-platform/yjwz/list?yhzid=${props.yhzId}&pageNum=1&pageSize=9999`,
method: 'get'
})
configs.value = res.data?.records
} catch (error) {
console.error('获取物质列表失败:', error)
}
}
</script>
<style scoped lang="scss">
.material-list-pc {
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;
}
}
}
}
}
</style>

View File

@ -6,7 +6,7 @@
</template> </template>
</el-input> </el-input>
<el-dialog v-model="dialogVisible" :title="dialogTitle" :width="dialogWidth" :append-to-body="true" :modal="true" @close="handleDialogClose"> <el-dialog v-model="dialogVisible" :header="dialogTitle" :width="dialogWidth" :append-to-body="true" :modal="true" @close="handleDialogClose">
<div class="dialog-content"> <div class="dialog-content">
<div class="title">已选择地点路线{{ tempSelectedItem?.routeCode }}</div> <div class="title">已选择地点路线{{ tempSelectedItem?.routeCode }}</div>

View File

@ -0,0 +1,253 @@
<template>
<div class="selector-component">
<el-input :modelValue="tempSelectedItem?.mc" :placeholder="placeholder" readonly @click="openDialog">
<template #suffix>
<el-icon><ArrowDown /></el-icon>
</template>
</el-input>
<el-dialog v-model="dialogVisible" :header="dialogTitle" :width="dialogWidth" :append-to-body="true" :modal="true" @close="handleDialogClose">
<div class="dialog-content">
<div class="title">已选择养护站{{ tempSelectedItem?.mc }}</div>
<el-input v-model="searchKeyword" :placeholder="searchPlaceholder" clearable @input="handleSearch" />
<el-table :data="dataList" v-loading="loading" height="350" @row-click="handleRowClick" highlight-current-row>
<el-table-column prop="mc" label="服务站名称" />
<el-table-column prop="qxmc" label="区县名称" />
</el-table>
<div class="pagination-wrapper" v-if="total > 0">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
:page-sizes="pageSizes"
layout="prev, pager, next, sizes"
@current-change="handlePageChange"
@size-change="handleSizeChange"
/>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed, onUnmounted, watch } from 'vue'
import { ArrowDown } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { request } from '@shared/utils/request'
// ==================== Props ====================
const props = defineProps({
// v-model
modelValue: {
type: [String, Number],
default: ''
},
//
placeholder: {
type: String,
default: '请选择'
},
//
dialogTitle: {
type: String,
default: '选择地点路线'
},
//
dialogWidth: {
type: String,
default: '500px'
},
//
searchPlaceholder: {
type: String,
default: '请输入关键词搜索'
},
//
apiUrl: {
type: String,
default: '/snow-ops-platform/yhz/list'
},
//
pageSize: {
type: Number,
default: 10
},
//
pageSizes: {
type: Array,
default: () => [10, 20, 50]
},
//
extraParams: {
type: Object,
default: () => ({})
},
// (ms)
searchDelay: {
type: Number,
default: 1000
}
})
const emit = defineEmits(['update:modelValue', 'change'])
// ==================== ====================
const dialogVisible = ref(false)
const searchKeyword = ref('')
const dataList = ref([])
const loading = ref(false)
const currentPage = ref(1)
const pageSize = ref(props.pageSize)
const total = ref(0)
//
const tempSelectedItem = ref(null)
//
let searchTimer = null
//
const selectedValue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
watch(() => props.modelValue, () => {
if(props.modelValue != tempSelectedItem.value?.id) {
tempSelectedItem.value = null
}
})
// ==================== API ====================
const fetchData = async () => {
loading.value = true
try {
const params = {
pageNum: currentPage.value,
pageSize: pageSize.value,
}
//
if (searchKeyword.value) {
params.mc = searchKeyword.value
}
const response = await request({
url: props.apiUrl,
method: 'get',
params
})
if (response?.code === '00000') {
const records = response.data.records || []
total.value = response.data.total || 0
// 便使
dataList.value = records
} else {
ElMessage.error(response.message || '加载失败')
dataList.value = []
total.value = 0
}
} catch (error) {
console.error('请求失败:', error)
ElMessage.error('加载失败,请重试')
dataList.value = []
total.value = 0
} finally {
loading.value = false
}
}
//
const debouncedSearch = () => {
//
if (searchTimer) {
clearTimeout(searchTimer)
}
//
searchTimer = setTimeout(() => {
currentPage.value = 1
fetchData()
}, props.searchDelay)
}
// ==================== ====================
const openDialog = () => {
dialogVisible.value = true
//
searchKeyword.value = tempSelectedItem.value?.mc || ''
currentPage.value = 1
fetchData()
}
const handleDialogClose = () => {
dialogVisible.value = false
//
if (searchTimer) {
clearTimeout(searchTimer)
}
}
const handleSearch = () => {
// 使
debouncedSearch()
}
const handlePageChange = (page) => {
currentPage.value = page
fetchData()
}
const handleSizeChange = (size) => {
pageSize.value = size
currentPage.value = 1
fetchData()
}
const handleRowClick = (row) => {
tempSelectedItem.value = row
selectedValue.value = tempSelectedItem.value.id
dialogVisible.value = false
emit('change', tempSelectedItem.value)
}
// ==================== ====================
onUnmounted(() => {
if (searchTimer) {
clearTimeout(searchTimer)
}
})
</script>
<style scoped lang="scss">
.selector-component {
width: 100%;
:deep(.el-input__wrapper) {
cursor: pointer;
.el-input__inner {
cursor: pointer;
}
}
}
.dialog-content {
display: flex;
flex-direction: column;
gap: 16px;
.pagination-wrapper {
display: flex;
justify-content: flex-end;
margin-top: 8px;
}
}
</style>

View File

@ -48,6 +48,12 @@ export function useOptions() {
{ label: '限制通行', value: '限制通行' } { label: '限制通行', value: '限制通行' }
] ]
// 冰灾 处理措施
options.value['iceDisposalMeasures'] = [
{ label: '封闭交通', value: '封闭交通' },
{ label: '限速通行', value: '限速通行' },
]
// 路线类型 // 路线类型
options.value['roadType'] = [ options.value['roadType'] = [
{ label: '国道', value: 'G' }, { label: '国道', value: 'G' },