2026-04-07 17:01:46 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="water-disaster">
|
|
|
|
|
|
<!-- 基本信息 -->
|
2026-04-08 16:01:04 +08:00
|
|
|
|
<PanelItem title="基本信息" v-if="!isContinue">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<van-form>
|
|
|
|
|
|
<!-- 路况类别 -->
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<BasePicker v-model="formData.roadConditionType" :options="roadConditionOptions" label="路况类别" placeholder="请选择" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 是否阻断 (event.isBlocked) -->
|
|
|
|
|
|
<BasePicker v-model="formData.event.isBlocked" :options="blockedOptions" label="是否阻断" placeholder="请选择" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-10 11:38:24 +08:00
|
|
|
|
<!-- 抢险进度 (event.repairProgress) -->
|
|
|
|
|
|
<BasePicker v-model="formData.event.repairProgress" :options="repairProgressOptions" label="抢险进度" placeholder="请选择" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 水毁处数 (event.damageCount) -->
|
|
|
|
|
|
<van-field v-model="formData.event.damageCount" label="水毁处数" placeholder="请填写" type="number" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 阻断里程 (event.blockedMileage) -->
|
|
|
|
|
|
<van-field v-model="formData.event.blockedMileage" label="阻断里程" placeholder="请填写" type="digit">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">公里</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 发生时间 (顶层 occurTime) -->
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<BaseDatePicker v-model="formData.occurTime" label="发生时间" placeholder="请选择时间" :columnsType="['year', 'month', 'day', 'hour', 'minute']" />
|
|
|
|
|
|
<div class="calibrate-time-btn" @click="calibrateTime">
|
|
|
|
|
|
<van-icon name="replay" />
|
|
|
|
|
|
<span>校准时间</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 线路编号 (顶层 routeNo) -->
|
|
|
|
|
|
<van-field v-model="formData.routeNo" label="线路编号" placeholder="请填写" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 起点桩号 (event.startStakeNo) -->
|
|
|
|
|
|
<van-field v-model="formData.event.startStakeNo" label="起点桩号(K)" placeholder="请填写" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 止点桩号 (event.endStakeNo) -->
|
|
|
|
|
|
<van-field v-model="formData.event.endStakeNo" label="止点桩号(K)" placeholder="请填写" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-09 09:35:05 +08:00
|
|
|
|
<!-- 路况位置 (occurLocation) -->
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.occurLocation" label="路况位置" placeholder="请填写" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 阻断点小地名 (event.blockedPointName) -->
|
|
|
|
|
|
<van-field v-model="formData.event.blockedPointName" label="阻断点小地名" placeholder="请填写" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</van-form>
|
|
|
|
|
|
</PanelItem>
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 处置情况 (report) -->
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<PanelItem title="处置情况">
|
|
|
|
|
|
<div class="disposal-measures">
|
|
|
|
|
|
<span class="measures-label">处置措施</span>
|
|
|
|
|
|
<div class="measures-options">
|
2026-04-09 09:35:05 +08:00
|
|
|
|
<!-- 改为单选,使用 van-radio-group -->
|
2026-04-10 17:41:23 +08:00
|
|
|
|
<van-radio-group v-model="formData.report.disposalMeasures" direction="horizontal">
|
2026-04-10 11:38:24 +08:00
|
|
|
|
<van-radio name="半幅封闭">半幅封闭</van-radio>
|
|
|
|
|
|
<van-radio name="全副封闭">全副封闭</van-radio>
|
|
|
|
|
|
<van-radio name="便道通行">便道通行</van-radio>
|
|
|
|
|
|
<van-radio name="正常通行">正常通行</van-radio>
|
2026-04-09 09:35:05 +08:00
|
|
|
|
</van-radio-group>
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 预计恢复时间 (report.expectRecoverTime) -->
|
|
|
|
|
|
<BaseDatePicker v-model="formData.report.expectRecoverTime" label="预计恢复时间" placeholder="请选择时间" :min-date="minDate" :max-date="maxDate" type="datetime" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 实际恢复时间 (report.actualRecoverTime) -->
|
|
|
|
|
|
<BaseDatePicker v-model="formData.report.actualRecoverTime" label="实际恢复时间" placeholder="请选择时间" :min-date="minDate" :max-date="maxDate" type="datetime" />
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</PanelItem>
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 人员车辆 (report) -->
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<PanelItem title="人员车辆">
|
|
|
|
|
|
<van-form>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.injuredCount" label="受伤人员" placeholder="请填写" type="number">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<span class="field-unit">人</span>
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.deadCount" label="死亡人员" placeholder="请填写" type="number">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<span class="field-unit">人</span>
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.strandedPersonCount" label="滞留人员" placeholder="请填写" type="number">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<span class="field-unit">人</span>
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.damagedVehicleCount" label="损坏车辆" placeholder="请填写" type="number">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">辆</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.strandedVehicleCount" label="滞留车辆" placeholder="请填写" type="number">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">辆</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
|
|
|
|
|
</van-form>
|
|
|
|
|
|
</PanelItem>
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 灾毁损失 (lossList) -->
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<PanelItem title="灾毁损失">
|
2026-04-08 09:21:47 +08:00
|
|
|
|
<LossList v-model="formData.lossList" />
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.remark" label="处理情况" placeholder="请填写(选填)" />
|
|
|
|
|
|
<van-field v-model="formData.report.totalLossAmount" label="损失总金额" placeholder="请填写(选填)" type="digit">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">万元</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
|
|
|
|
|
</PanelItem>
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<!-- 投入资源 (report) -->
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<PanelItem>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.investedMachinery" label="已投机械" placeholder="请填写" type="digit">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">台/班</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.investedManpower" label="已投入力" placeholder="请填写" type="number">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">人次</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.investedFunds" label="已投资金" placeholder="请填写" type="digit">
|
2026-04-07 17:01:46 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">万元</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<van-field v-model="formData.report.siteDescription" label="现场描述" placeholder="请填写" type="textarea" rows="2" autosize />
|
2026-04-10 11:38:24 +08:00
|
|
|
|
<van-field label="附件" center>
|
|
|
|
|
|
<template #input>
|
2026-04-10 14:55:19 +08:00
|
|
|
|
<van-uploader accept="video/*,image/*" :modelValue="getFileList()" :after-read="afterRead" multiple :max-count="6" @delete="removeFile" />
|
2026-04-10 11:38:24 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</PanelItem>
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<PanelItem>
|
|
|
|
|
|
<!-- 是否需要恢复重建 (event.needsRecovery) -->
|
|
|
|
|
|
<BasePicker v-model="formData.event.needsRecovery" :options="needsRecoveryOptions" label="是否需要恢复重建" placeholder="请选择" />
|
|
|
|
|
|
<!-- 恢复重建预估费用 (event.estimatedRecoveryCost) -->
|
2026-04-08 16:01:04 +08:00
|
|
|
|
<van-field v-model="formData.event.estimatedRecoveryCost" v-if="!isContinue" label="恢复重建预估费用" placeholder="请填写" type="digit">
|
2026-04-08 11:12:41 +08:00
|
|
|
|
<template #button>
|
|
|
|
|
|
<span class="field-unit">万元</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</van-field>
|
|
|
|
|
|
</PanelItem>
|
2026-04-07 17:01:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-04-10 14:55:19 +08:00
|
|
|
|
import { ref, computed, watch } from 'vue'
|
2026-04-10 11:38:24 +08:00
|
|
|
|
import { showToast, showFailToast, showLoadingToast } from 'vant'
|
2026-04-07 17:01:46 +08:00
|
|
|
|
import PanelItem from '@/components/PanelItem.vue'
|
|
|
|
|
|
import BasePicker from '@/components/BasePicker.vue'
|
|
|
|
|
|
import BaseDatePicker from '@/components/BaseDatePicker.vue'
|
2026-04-08 09:21:47 +08:00
|
|
|
|
import LossList from './LossList.vue'
|
2026-04-08 16:01:04 +08:00
|
|
|
|
import { useRouter, useRoute } from 'vue-router'
|
2026-04-10 11:38:24 +08:00
|
|
|
|
import { request } from '@shared/utils/request'
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 16:01:04 +08:00
|
|
|
|
const route = useRoute()
|
|
|
|
|
|
|
|
|
|
|
|
// 是否为续报
|
|
|
|
|
|
const isContinue = computed(() => route.query.isContinue)
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
|
2026-04-10 14:55:19 +08:00
|
|
|
|
// 表单数据 - 按 Request 接口结构定义,使用 ref 包装
|
|
|
|
|
|
const formData = ref({
|
2026-04-08 11:12:41 +08:00
|
|
|
|
// 顶层字段
|
|
|
|
|
|
occurLocation: '', // 发生地点
|
|
|
|
|
|
occurTime: '', // 发生时间
|
|
|
|
|
|
roadConditionType: '', // 路况类别
|
|
|
|
|
|
routeNo: '', // 线路编号
|
|
|
|
|
|
|
|
|
|
|
|
// event 对象
|
|
|
|
|
|
event: {
|
|
|
|
|
|
blockedMileage: '', // 阻断里程
|
|
|
|
|
|
blockedPointName: '', // 阻断点小地名
|
|
|
|
|
|
contactPerson: '', // 联系人
|
|
|
|
|
|
contactPhone: '', // 联系电话
|
|
|
|
|
|
damageCount: '', // 水毁处数
|
|
|
|
|
|
district: '', // 上报区县
|
|
|
|
|
|
endStakeNo: '', // 止点桩号
|
|
|
|
|
|
estimatedRecoveryCost: '', // 恢复重建预估费用
|
|
|
|
|
|
inspectionMileage: '', // 巡查里程
|
|
|
|
|
|
isBlocked: '', // 是否阻断
|
|
|
|
|
|
needsRecovery: '', // 是否需要恢复重建
|
2026-04-10 11:38:24 +08:00
|
|
|
|
repairProgress: '', // 抢险进度
|
2026-04-08 11:12:41 +08:00
|
|
|
|
reporterUnit: '', // 填报单位
|
|
|
|
|
|
startStakeNo: '' // 起点桩号
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// report 对象
|
|
|
|
|
|
report: {
|
|
|
|
|
|
actualRecoverTime: '', // 实际恢复时间
|
|
|
|
|
|
damagedVehicleCount: '', // 损坏车辆
|
|
|
|
|
|
deadCount: '', // 死亡人员
|
2026-04-09 09:35:05 +08:00
|
|
|
|
disposalMeasures: '', // 处置措施(单个值,不再用逗号分隔)
|
2026-04-08 11:12:41 +08:00
|
|
|
|
expectRecoverTime: '', // 预计恢复时间
|
|
|
|
|
|
injuredCount: '', // 受伤人员
|
|
|
|
|
|
investedFunds: '', // 已投资金
|
|
|
|
|
|
investedMachinery: '', // 已投机械
|
|
|
|
|
|
investedManpower: '', // 已投人力
|
|
|
|
|
|
remark: '', // 处理情况/备注
|
|
|
|
|
|
siteDescription: '', // 现场描述
|
|
|
|
|
|
strandedPersonCount: '', // 滞留人员
|
|
|
|
|
|
strandedVehicleCount: '', // 滞留车辆
|
|
|
|
|
|
totalLossAmount: '' // 损失总金额
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// lossList 数组
|
|
|
|
|
|
lossList: [],
|
|
|
|
|
|
|
|
|
|
|
|
// fileList 数组
|
|
|
|
|
|
fileList: []
|
2026-04-07 17:01:46 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2026-04-10 14:55:19 +08:00
|
|
|
|
const getFileList = () => {
|
|
|
|
|
|
const fileList = formData.value.fileList?.map((item) => {
|
|
|
|
|
|
return {
|
|
|
|
|
|
url: item.fileUrl,
|
|
|
|
|
|
name: item.fileName
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return fileList
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 17:01:46 +08:00
|
|
|
|
// BasePicker 选项数据
|
|
|
|
|
|
const roadConditionOptions = [
|
|
|
|
|
|
{ label: '高速公路', value: '高速公路' },
|
|
|
|
|
|
{ label: '国道', value: '国道' },
|
|
|
|
|
|
{ label: '省道', value: '省道' },
|
|
|
|
|
|
{ label: '县道', value: '县道' },
|
|
|
|
|
|
{ label: '乡道', value: '乡道' },
|
|
|
|
|
|
{ label: '村道', value: '村道' }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const blockedOptions = [
|
2026-04-08 11:12:41 +08:00
|
|
|
|
{ label: '是', value: true },
|
|
|
|
|
|
{ label: '否', value: false }
|
2026-04-07 17:01:46 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const repairProgressOptions = [
|
2026-04-10 11:38:24 +08:00
|
|
|
|
{ label: '未抢险', value: '未抢险' },
|
|
|
|
|
|
{ label: '抢险中', value: '抢险中' },
|
|
|
|
|
|
{ label: '已完成', value: '已完成' }
|
2026-04-08 11:12:41 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const needsRecoveryOptions = [
|
|
|
|
|
|
{ label: '是', value: true },
|
|
|
|
|
|
{ label: '否', value: false }
|
2026-04-07 17:01:46 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 时间选择器范围
|
|
|
|
|
|
const minDate = new Date(2020, 0, 1)
|
|
|
|
|
|
const maxDate = new Date(2030, 11, 31)
|
|
|
|
|
|
|
2026-04-08 11:12:41 +08:00
|
|
|
|
const initFormData = (newVal) => {
|
|
|
|
|
|
if (newVal && Object.keys(newVal).length > 0) {
|
2026-04-10 14:55:19 +08:00
|
|
|
|
// 深度合并数据 - 直接替换整个对象
|
|
|
|
|
|
formData.value = {
|
2026-04-08 11:12:41 +08:00
|
|
|
|
occurLocation: newVal.occurLocation || '',
|
|
|
|
|
|
occurTime: newVal.occurTime || '',
|
|
|
|
|
|
roadConditionType: newVal.roadConditionType || '',
|
|
|
|
|
|
routeNo: newVal.routeNo || '',
|
2026-04-10 14:55:19 +08:00
|
|
|
|
event: { ...formData.value.event, ...(newVal.event || {}) },
|
|
|
|
|
|
report: { ...formData.value.report, ...(newVal.report || {}) },
|
2026-04-08 11:12:41 +08:00
|
|
|
|
lossList: newVal.lossList || [],
|
|
|
|
|
|
fileList: newVal.fileList || []
|
2026-04-10 14:55:19 +08:00
|
|
|
|
}
|
2026-04-08 11:12:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-07 17:01:46 +08:00
|
|
|
|
|
|
|
|
|
|
// 校准时间
|
|
|
|
|
|
const calibrateTime = () => {
|
|
|
|
|
|
const now = new Date()
|
|
|
|
|
|
const formatted = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
2026-04-10 14:55:19 +08:00
|
|
|
|
formData.value.occurTime = formatted
|
2026-04-07 17:01:46 +08:00
|
|
|
|
showToast('时间已校准为当前时间')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 图片上传处理
|
|
|
|
|
|
const afterImageRead = (file) => {
|
|
|
|
|
|
console.log('图片上传:', file)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onOversize = () => {
|
|
|
|
|
|
showFailToast('图片大小不能超过500KB')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const afterVideoRead = (file) => {
|
|
|
|
|
|
console.log('视频上传:', file)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const onVideoOversize = () => {
|
|
|
|
|
|
showFailToast('视频大小不能超过20MB')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 11:38:24 +08:00
|
|
|
|
const isImageFile = (file) => {
|
|
|
|
|
|
// 根据url后缀判断
|
|
|
|
|
|
const imageExtensions = /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i
|
|
|
|
|
|
if (file.fileUrl && imageExtensions.test(file.fileUrl)) return true
|
|
|
|
|
|
// 根据文件类型判断
|
|
|
|
|
|
if (file.type && file.type.startsWith('image/')) return true
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const isVideoFile = (file) => {
|
|
|
|
|
|
// 根据url后缀判断
|
|
|
|
|
|
const videoExtensions = /\.(mp4|avi|mov|wmv|flv|mkv)$/i
|
|
|
|
|
|
if (file.fileUrl && videoExtensions.test(file.fileUrl)) return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断图片是否可以上传
|
|
|
|
|
|
* @param {File} file - 图片文件
|
|
|
|
|
|
* @returns {boolean} 是否允许上传
|
|
|
|
|
|
*/
|
|
|
|
|
|
const isValidImage = (file) => {
|
|
|
|
|
|
// 校验文件类型
|
|
|
|
|
|
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
|
|
|
|
|
|
|
|
|
|
|
|
if (!isJpgOrPng) {
|
|
|
|
|
|
showFailToast('只能上传 JPG/PNG 格式的图片!')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 校验文件大小(500KB = 500 * 1024 bytes)
|
|
|
|
|
|
const isLt500k = file.size / 1024 < 500
|
|
|
|
|
|
|
|
|
|
|
|
if (!isLt500k) {
|
|
|
|
|
|
showFailToast(`图片大小不能超过 500KB!当前大小:${(file.size / 1024).toFixed(2)}KB`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 判断视频是否可以上传
|
|
|
|
|
|
* @param {File} file - 视频文件
|
|
|
|
|
|
* @returns {boolean} 是否允许上传
|
|
|
|
|
|
*/
|
|
|
|
|
|
const isValidVideo = (file) => {
|
|
|
|
|
|
// 校验文件类型
|
|
|
|
|
|
const allowedTypes = ['video/mp4', 'video/quicktime', 'video/x-msvideo', 'video/webm']
|
|
|
|
|
|
const isValidType = allowedTypes.includes(file.type)
|
|
|
|
|
|
|
|
|
|
|
|
if (!isValidType) {
|
|
|
|
|
|
showFailToast('请上传有效的视频文件(MP4、MOV、AVI、WEBM格式)!')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 校验文件大小(50MB = 50 * 1024 * 1024 bytes)
|
|
|
|
|
|
const maxSize = 50 * 1024 * 1024
|
|
|
|
|
|
const isValidSize = file.size <= maxSize
|
|
|
|
|
|
|
|
|
|
|
|
if (!isValidSize) {
|
|
|
|
|
|
showFailToast(`视频大小不能超过 50MB!当前大小:${(file.size / 1024 / 1024).toFixed(2)}MB`)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-10 14:55:19 +08:00
|
|
|
|
* 统一的上传前校验
|
2026-04-10 11:38:24 +08:00
|
|
|
|
* @param {File} file - 上传的文件
|
|
|
|
|
|
* @returns {boolean} 是否允许上传
|
|
|
|
|
|
*/
|
|
|
|
|
|
const checkFile = (file) => {
|
|
|
|
|
|
// 判断是否为图片
|
|
|
|
|
|
if (file.type.startsWith('image/')) {
|
|
|
|
|
|
return isValidImage(file)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否为视频
|
|
|
|
|
|
if (file.type.startsWith('video/')) {
|
|
|
|
|
|
return isValidVideo(file)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
showFailToast('只支持图片和视频文件!')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const afterRead = async (options) => {
|
|
|
|
|
|
const file = options.file
|
2026-04-10 14:55:19 +08:00
|
|
|
|
if (!checkFile(file)) return
|
2026-04-10 11:38:24 +08:00
|
|
|
|
const toast = showLoadingToast({
|
|
|
|
|
|
message: '上传中...',
|
|
|
|
|
|
forbidClick: true,
|
|
|
|
|
|
duration: 0 // 设置为0表示不会自动关闭
|
|
|
|
|
|
})
|
|
|
|
|
|
try {
|
2026-04-10 14:55:19 +08:00
|
|
|
|
const uploadFormData = new FormData()
|
|
|
|
|
|
uploadFormData.append('file', file)
|
2026-04-10 11:38:24 +08:00
|
|
|
|
const res = await request({
|
|
|
|
|
|
url: '/snow-ops-platform/file/upload',
|
|
|
|
|
|
method: 'post',
|
2026-04-10 14:55:19 +08:00
|
|
|
|
data: uploadFormData
|
2026-04-10 11:38:24 +08:00
|
|
|
|
})
|
|
|
|
|
|
toast.close()
|
|
|
|
|
|
if (res.code === '00000') {
|
|
|
|
|
|
const name = file.name
|
|
|
|
|
|
let type = isImageFile(file) ? 1 : isVideoFile(file) ? 2 : 3
|
|
|
|
|
|
|
|
|
|
|
|
const url = res.data
|
|
|
|
|
|
const fileData = {
|
|
|
|
|
|
fileName: name,
|
|
|
|
|
|
fileUrl: url,
|
|
|
|
|
|
fileType: type,
|
|
|
|
|
|
fileSize: file.size
|
|
|
|
|
|
}
|
2026-04-10 14:55:19 +08:00
|
|
|
|
if (!formData.value.fileList) formData.value.fileList = []
|
|
|
|
|
|
formData.value.fileList.push(fileData)
|
2026-04-10 11:38:24 +08:00
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new Error(res.message)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
toast.close()
|
|
|
|
|
|
showToast({
|
|
|
|
|
|
type: 'fail',
|
|
|
|
|
|
message: error.message
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 14:55:19 +08:00
|
|
|
|
const removeFile = (file, index) => {
|
|
|
|
|
|
// 删除文件
|
|
|
|
|
|
formData.value.fileList.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 17:01:46 +08:00
|
|
|
|
// 暴露验证方法
|
|
|
|
|
|
const validate = () => {
|
2026-04-10 14:55:19 +08:00
|
|
|
|
if (!formData.value.occurTime) {
|
2026-04-07 17:01:46 +08:00
|
|
|
|
showToast('请填写发生时间')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
2026-04-10 14:55:19 +08:00
|
|
|
|
if (!formData.value.routeNo) {
|
2026-04-07 17:01:46 +08:00
|
|
|
|
showToast('请填写线路编号')
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 14:55:19 +08:00
|
|
|
|
// 获取表单数据 - 返回 formData.value 的副本
|
2026-04-07 17:01:46 +08:00
|
|
|
|
const getFormData = () => {
|
2026-04-10 14:55:19 +08:00
|
|
|
|
return { ...formData.value }
|
2026-04-07 17:01:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 暴露方法给父组件
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
|
validate,
|
2026-04-08 11:12:41 +08:00
|
|
|
|
initFormData,
|
2026-04-07 17:01:46 +08:00
|
|
|
|
getFormData
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.water-disaster {
|
|
|
|
|
|
.coordinate-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
.coordinate-field {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.calibrate-time-btn,
|
|
|
|
|
|
.calibrate-coord-btn {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #1989fa;
|
|
|
|
|
|
margin: 8px 0 8px 12px;
|
|
|
|
|
|
padding: 4px 8px;
|
|
|
|
|
|
background: #f0f7ff;
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
width: fit-content;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.field-unit {
|
|
|
|
|
|
color: #969799;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
margin-left: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.disposal-measures {
|
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
|
.measures-label {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #323233;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.measures-options {
|
2026-04-09 09:35:05 +08:00
|
|
|
|
:deep(.van-radio-group) {
|
2026-04-07 17:01:46 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
2026-04-09 09:35:05 +08:00
|
|
|
|
:deep(.van-radio) {
|
2026-04-07 17:01:46 +08:00
|
|
|
|
margin-right: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.attachment-tip {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #969799;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.upload-area {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
.upload-btn {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 80px;
|
|
|
|
|
|
height: 80px;
|
|
|
|
|
|
background: #f8f9fa;
|
|
|
|
|
|
border: 1px dashed #dcdee0;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #969799;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.video-preview {
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.van-field__label) {
|
2026-04-08 11:12:41 +08:00
|
|
|
|
width: 110px;
|
2026-04-07 17:01:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-10 14:55:19 +08:00
|
|
|
|
</style>
|