feat: h5灾毁管理
This commit is contained in:
parent
3af90b27da
commit
4832facbf9
@ -15,6 +15,7 @@
|
||||
<!-- 弹出层选择器 -->
|
||||
<van-popup v-model:show="showPicker" position="bottom" round>
|
||||
<van-picker
|
||||
:modelValue="getPickerValue()"
|
||||
:columns="columns"
|
||||
:title="pickerTitle"
|
||||
:loading="loading"
|
||||
@ -91,12 +92,32 @@ const emit = defineEmits(['update:modelValue', 'change'])
|
||||
// 控制弹出层显示
|
||||
const showPicker = ref(false)
|
||||
|
||||
const getPickerValue = () => {
|
||||
// 布尔值需要转换为数字交给picker, 在设置的时候,又需要转换回来
|
||||
if(props.modelValue === true) return [1]
|
||||
if(props.modelValue === false) return [0]
|
||||
return [props.modelValue]
|
||||
}
|
||||
|
||||
// 处理 Picker 数据格式:将 options 转为 Picker 需要的文本数组
|
||||
const columns = computed(() => {
|
||||
return props.options.map(item => {
|
||||
let value = item[props.valueKey]
|
||||
let isBoolean = false
|
||||
// vant不支持布尔值,需要做转换
|
||||
if(value === true) {
|
||||
value = 1
|
||||
isBoolean = true
|
||||
}
|
||||
if(value === false) {
|
||||
value = 0
|
||||
isBoolean = true
|
||||
}
|
||||
|
||||
return {
|
||||
text: item[props.labelKey],
|
||||
value: item[props.valueKey]
|
||||
value,
|
||||
isBoolean
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -119,7 +140,11 @@ const openPicker = () => {
|
||||
|
||||
// 确认选择
|
||||
const onConfirm = ({ selectedValues, selectedOptions }) => {
|
||||
const value = selectedOptions[0][props.valueKey]
|
||||
let value = selectedOptions[0][props.valueKey]
|
||||
// 如果是布尔值,需要转换为布尔值
|
||||
if(selectedOptions[0].isBoolean) {
|
||||
value = value === 1 ? true : false
|
||||
}
|
||||
const label = selectedOptions[0][props.labelKey]
|
||||
emit('update:modelValue', value)
|
||||
emit('change', { value, label })
|
||||
|
||||
@ -45,17 +45,7 @@ const props = defineProps({
|
||||
// 标签列表
|
||||
tags: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '边坡坍塌', value: '边坡坍塌' },
|
||||
{ label: '泥石流', value: '泥石流' },
|
||||
{ label: '路基沉陷', value: '路基沉陷' },
|
||||
{ label: '山体滑坡', value: '山体滑坡' },
|
||||
{ label: '行道树倒塌', value: '行道树倒塌' },
|
||||
{ label: '积水', value: '积水' },
|
||||
{ label: '积雪', value: '积雪' },
|
||||
{ label: '其他', value: '其他' }
|
||||
]
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
@ -86,7 +76,8 @@ const selectTag = (value) => {
|
||||
|
||||
// 重置筛选
|
||||
const resetFilter = () => {
|
||||
tempSelectedTag.value = 'all'
|
||||
tempSelectedTag.value = null
|
||||
emit('update:modelValue', tempSelectedTag.value)
|
||||
}
|
||||
|
||||
// 确认筛选
|
||||
|
||||
@ -62,7 +62,7 @@ const routes = [
|
||||
component: () => import('../views/IceEvent/IceEventAdd.vue')
|
||||
},
|
||||
{
|
||||
path: '/iceEventDetail/:data',
|
||||
path: '/iceEventDetail',
|
||||
name: 'IceEventDetail',
|
||||
component: () => import('../views/IceEvent/IceEventDetails.vue')
|
||||
},
|
||||
@ -107,9 +107,8 @@ const routes = [
|
||||
component: () => import('../views/DisasterManagement/DisasterReport.vue')
|
||||
},
|
||||
{
|
||||
path: '/disasterDetail',
|
||||
name: 'DisasterDetail',
|
||||
component: () => import('../views/DisasterManagement/DisasterDetail.vue')
|
||||
path: '/waterDisasterDetail',
|
||||
component: () => import('../views/DisasterManagement/WaterDisasterDetail.vue')
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@ -1,7 +1,16 @@
|
||||
<template>
|
||||
<PageContainer title="灾害管理" @click-back="handleClickBack">
|
||||
<div class="filter-wrapper">
|
||||
<SearchInput style="margin-top: 0" v-model="searchValue" placeholder="请输入地点关键词" @search="handleSearch" />
|
||||
<div class="filter-btn-block">
|
||||
<van-button @click="showFilter = true">
|
||||
<div class="filter-btn">
|
||||
<span style="white-space: nowrap">{{ getShortTypeName(selectedDisasterType) }}</span> <van-icon name="filter-o" />
|
||||
</div>
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
<CurrentSite />
|
||||
|
||||
<div class="list-panel">
|
||||
<CardItem v-for="(item, index) in list" :key="index" :title="getEventTitle(item)" @click="handleClickItem(item)">
|
||||
<template #headerExtra>
|
||||
@ -37,18 +46,13 @@
|
||||
<van-button type="primary" class="footer-btn" @click="handleAdd">灾害填报</van-button>
|
||||
|
||||
<!-- 筛选组件:v-model 绑定选中的值,visible 控制显示隐藏 -->
|
||||
<TagFilter
|
||||
v-model="selectedDisasterType"
|
||||
:visible="showFilter"
|
||||
@update:visible="showFilter = $event"
|
||||
@confirm="handleFilterConfirm"
|
||||
/>
|
||||
<TagFilter v-model="selectedDisasterType" :visible="showFilter" :tags="disasterTypes" @update:visible="showFilter = $event" @confirm="handleFilterConfirm" />
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { RouterLink, useRouter } from 'vue-router'
|
||||
import { showToast, Tag as VanTag, Loading as VanLoading, Icon as VanIcon, Button as VanButton, showImagePreview } from 'vant'
|
||||
import PageContainer from '@/components/PageContainer.vue'
|
||||
import SearchInput from '@/components/SearchInput.vue'
|
||||
@ -56,7 +60,7 @@ import CardItem from '@/components/CardItem.vue'
|
||||
import EmptyBox from '@/components/EmptyBox.vue'
|
||||
import CurrentSite from '@/components/CurrentSite.vue'
|
||||
import TagFilter from '@/components/TagFilter.vue'
|
||||
import { request } from "@shared/utils/request";
|
||||
import { request } from '@shared/utils/request'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@ -76,11 +80,11 @@ const emptyText = ref('暂无相关灾毁信息')
|
||||
const showFilter = ref(false)
|
||||
|
||||
// 当前选中的灾毁类型(通过 v-model 传给 TagFilter)
|
||||
const selectedDisasterType = ref('all')
|
||||
const selectedDisasterType = ref(null)
|
||||
|
||||
// 灾毁类型列表(根据实际数据中的路况类别或其他字段定义)
|
||||
const disasterTypes = [
|
||||
{ label: '全部', value: 'all' },
|
||||
{ label: '全部', value: null },
|
||||
{ label: '边坡坍塌', value: '边坡坍塌' },
|
||||
{ label: '泥石流', value: '泥石流' },
|
||||
{ label: '路基沉陷', value: '路基沉陷' },
|
||||
@ -93,17 +97,14 @@ const disasterTypes = [
|
||||
|
||||
// 获取事件标题
|
||||
const getEventTitle = (item) => {
|
||||
// 优先使用 eventName,其次使用发生地点+线路编号组合
|
||||
if (item.eventName) {
|
||||
return item.eventName
|
||||
}
|
||||
const parts = []
|
||||
if (item.routeNo) parts.push(item.routeNo)
|
||||
if (item.occurLocation) parts.push(item.occurLocation)
|
||||
if (item.startStakeNo) parts.push(item.startStakeNo)
|
||||
|
||||
if (item.routeNo) parts.push(item.routeNo)
|
||||
// if(item.disasterType) parts.push(item.disasterType == 'WATER_DAMAGE' ? '发生水毁灾害' : '发生冰雪灾害')
|
||||
if (item.roadConditionType) parts.push('发生' + item.roadConditionType)
|
||||
|
||||
if (parts.length > 0) {
|
||||
return parts.join(' ')
|
||||
return parts.join('')
|
||||
}
|
||||
return '未命名事件'
|
||||
}
|
||||
@ -121,72 +122,59 @@ const getEventStatusType = (status) => {
|
||||
|
||||
// 获取灾毁类型文本(根据实际情况映射)
|
||||
const getDisasterTypeText = (item) => {
|
||||
// 可以根据 roadConditionType、repairProgress 或其他字段来生成类型标签
|
||||
// 这里提供几种可能的映射方式
|
||||
|
||||
// 方式1:根据抢险进度
|
||||
if (item.repairProgress) {
|
||||
return item.repairProgress
|
||||
}
|
||||
|
||||
// 方式2:根据路况类别
|
||||
if (item.roadConditionType) {
|
||||
return item.roadConditionType
|
||||
}
|
||||
|
||||
// 方式3:默认返回"水毁灾害"
|
||||
return '水毁灾害'
|
||||
}
|
||||
|
||||
// 获取简短类型名称
|
||||
const getShortTypeName = (type) => {
|
||||
const typeMap = {
|
||||
'边坡坍塌': '边坡',
|
||||
'泥石流': '泥石',
|
||||
'路基沉陷': '路基',
|
||||
'山体滑坡': '滑坡',
|
||||
'行道树倒塌': '树倒',
|
||||
'积水': '积水',
|
||||
'积雪': '积雪',
|
||||
'其他': '其他',
|
||||
'未抢险': '待抢险',
|
||||
'抢险中': '抢险中',
|
||||
'已完成': '已完成',
|
||||
'高速公路': '高速',
|
||||
'国道': '国道',
|
||||
'省道': '省道'
|
||||
边坡坍塌: '边坡',
|
||||
泥石流: '泥石',
|
||||
路基沉陷: '路基',
|
||||
山体滑坡: '滑坡',
|
||||
行道树倒塌: '树倒',
|
||||
积水: '积水',
|
||||
积雪: '积雪',
|
||||
其他: '其他',
|
||||
未抢险: '待抢险',
|
||||
抢险中: '抢险中',
|
||||
已完成: '已完成',
|
||||
高速公路: '高速',
|
||||
国道: '国道',
|
||||
省道: '省道'
|
||||
}
|
||||
return typeMap[type] || (type ? type.substring(0, 2) : '灾害')
|
||||
return typeMap[type] || (type ? type.substring(0, 2) : '全部')
|
||||
}
|
||||
|
||||
// 获取灾毁列表数据
|
||||
const getDisasterList = async (keyword = '', disasterType = 'all') => {
|
||||
const getDisasterList = async (keyword = '', disasterType = null) => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const result = await request({
|
||||
url: '/snow-ops-platform/water-damage/list',
|
||||
url: '/snow-ops-platform/unified-disaster/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
pageSize: 999,
|
||||
keyword: keyword.trim(),
|
||||
disasterType: disasterType === 'all' ? '' : disasterType
|
||||
roadConditionType: disasterType || undefined
|
||||
}
|
||||
})
|
||||
if (result?.data?.records) {
|
||||
list.value = result.data.records.map(item => ({
|
||||
list.value = result.data.records.map((item) => ({
|
||||
...item,
|
||||
// 数据转换,确保前端使用的字段存在
|
||||
title: getEventTitle(item),
|
||||
status: getEventStatusText(item.eventStatus),
|
||||
occurTime: item.occurTime,
|
||||
estimateRecoverTime: item.expectRecoverTime,
|
||||
disasterType: getDisasterTypeText(item)
|
||||
estimateRecoverTime: item.expectRecoverTime
|
||||
}))
|
||||
} else {
|
||||
showToast(result.message || '获取数据失败')
|
||||
list.value = []
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取灾毁列表失败:', error)
|
||||
showToast('获取数据失败,请稍后重试')
|
||||
@ -201,14 +189,6 @@ const handleSearch = () => {
|
||||
getDisasterList(searchValue.value, selectedDisasterType.value)
|
||||
}
|
||||
|
||||
// 点击全部按钮
|
||||
const handleAllClick = () => {
|
||||
if (selectedDisasterType.value !== 'all') {
|
||||
selectedDisasterType.value = 'all'
|
||||
getDisasterList(searchValue.value, 'all')
|
||||
}
|
||||
}
|
||||
|
||||
// 筛选确认
|
||||
const handleFilterConfirm = (type) => {
|
||||
// selectedDisasterType 已经通过 v-model 更新,这里只需重新请求数据
|
||||
@ -222,12 +202,22 @@ const handleClickBack = () => {
|
||||
|
||||
// 点击列表项
|
||||
const handleClickItem = (item) => {
|
||||
router.push({
|
||||
path: '/disasterDetail',
|
||||
query: {
|
||||
id: item.id
|
||||
}
|
||||
})
|
||||
if (item.disasterType === 'WATER_DAMAGE') {
|
||||
router.push({
|
||||
path: '/waterDisasterDetail',
|
||||
query: {
|
||||
id: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
if (item.disasterType === 'ICE_SNOW') {
|
||||
router.push({
|
||||
name: 'IceEventDetail',
|
||||
query: {
|
||||
id: item.relationId
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 灾毁填报
|
||||
@ -249,6 +239,23 @@ onMounted(() => {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.filter-wrapper {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
|
||||
.filter-btn-block {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
.filter-btn {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.card-item) {
|
||||
position: relative;
|
||||
}
|
||||
@ -330,4 +337,4 @@ onMounted(() => {
|
||||
:deep(.van-tag) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -5,22 +5,16 @@
|
||||
|
||||
<!-- 事件类型 -->
|
||||
<PanelItem title="事件类型" style="margin-bottom: 10px" v-if="!isContinue">
|
||||
<van-radio-group v-model="eventType" direction="horizontal" class="event-type-group">
|
||||
<van-radio name="water">水毁灾害</van-radio>
|
||||
<van-radio name="ice">冰雪灾害</van-radio>
|
||||
</van-radio-group>
|
||||
<van-row gutter="10">
|
||||
<van-col v-for="(item, index) in eventTypeOptions" :span="24 / eventTypeOptions.length" :key="index">
|
||||
<van-button block plain :type="item.value === eventType ? 'primary' : 'default'" @click="eventType = item.value"> {{ item.label }} </van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</PanelItem>
|
||||
|
||||
<!-- 根据事件类型渲染不同表单 -->
|
||||
<WaterDisaster v-if="eventType === 'water'" ref="waterDisasterRef" />
|
||||
|
||||
<!-- 冰雪灾害表单(待实现) -->
|
||||
<div v-else class="coming-soon">
|
||||
<van-empty description="冰雪灾害表单开发中..." />
|
||||
</div>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<van-button type="primary" class="footer-btn" @click="handleSubmit" :loading="submitting"> 提交 </van-button>
|
||||
<WaterDisaster v-if="eventType === 'water'" />
|
||||
<IceDisaster v-if="eventType === 'ice'" />
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
@ -32,8 +26,8 @@ import PageContainer from '@/components/PageContainer.vue'
|
||||
import CurrentSite from '@/components/CurrentSite.vue'
|
||||
import PanelItem from '@/components/PanelItem.vue'
|
||||
import WaterDisaster from './WaterDisaster/WaterDisaster.vue'
|
||||
import IceDisaster from './IceDisaster.vue'
|
||||
import { request } from '@shared/utils/request'
|
||||
import mockFormData from './waterDisasterFormData.json'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -45,118 +39,27 @@ const title = ref(!isContinue ? '灾毁填报' : '灾毁续报')
|
||||
|
||||
// 事件类型
|
||||
const eventType = ref('water')
|
||||
|
||||
// 表单数据
|
||||
const formData = ref(route.query.mock ? mockFormData : {})
|
||||
const waterDisasterRef = ref(null)
|
||||
const submitting = ref(false)
|
||||
const eventTypeOptions = [
|
||||
{
|
||||
label: '水毁灾害',
|
||||
value: 'water'
|
||||
},
|
||||
{
|
||||
label: '冰雪灾害',
|
||||
value: 'ice'
|
||||
}
|
||||
]
|
||||
|
||||
// 返回上一页
|
||||
const handleClickBack = () => {
|
||||
router.replace('/disasterManagement')
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
// 验证表单
|
||||
if (eventType.value === 'water') {
|
||||
if (!waterDisasterRef.value.validate()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
// 获取表单数据
|
||||
let formData = {}
|
||||
if (eventType.value === 'water') {
|
||||
formData = waterDisasterRef.value.getFormData()
|
||||
}
|
||||
|
||||
// 添加事件类型和站点信息
|
||||
const submitData = {
|
||||
...formData
|
||||
// 可以在这里添加站点信息等其他数据
|
||||
}
|
||||
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/water-damage/addOrUpdate',
|
||||
method: 'post',
|
||||
data: submitData
|
||||
})
|
||||
|
||||
if (res?.code === '00000') {
|
||||
showSuccessToast('提交成功')
|
||||
if (submitData.event.needsRecovery) {
|
||||
router.replace({
|
||||
name: 'RebuildAdd',
|
||||
params: {
|
||||
data: res.data.id
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 提交成功后返回列表页
|
||||
setTimeout(() => {
|
||||
router.replace('/disasterManagement')
|
||||
}, 500)
|
||||
}
|
||||
} else {
|
||||
showFailToast(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
showFailToast('提交失败,请重试')
|
||||
console.error('提交失败:', error)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取灾毁详情
|
||||
const getDisasterDetail = async () => {
|
||||
const id = route.query.id
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await request({
|
||||
url: `/snow-ops-platform/water-damage/getById`,
|
||||
method: 'get',
|
||||
params: { id }
|
||||
})
|
||||
|
||||
if (result?.data) {
|
||||
// 接口返回 Data 结构
|
||||
const data = result.data
|
||||
const newFormData = {
|
||||
...data,
|
||||
lossList: null,
|
||||
report: formData.value.report,
|
||||
fileList: null
|
||||
}
|
||||
waterDisasterRef.value.initFormData(newFormData)
|
||||
} else {
|
||||
showToast(result.message || '获取详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取灾毁详情失败:', error)
|
||||
showToast('获取详情失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query?.id) {
|
||||
getDisasterDetail()
|
||||
} else {
|
||||
waterDisasterRef.value.initFormData(formData.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-container {
|
||||
padding-bottom: 80px;
|
||||
background-color: #f5f7fa;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.event-type-group {
|
||||
@ -170,26 +73,4 @@ onMounted(() => {
|
||||
.coming-soon {
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.footer-btn {
|
||||
position: fixed;
|
||||
bottom: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: calc(100% - 32px);
|
||||
max-width: 340px;
|
||||
border-radius: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
|
||||
&:active {
|
||||
opacity: 0.9;
|
||||
transform: translateX(-50%) scale(0.98);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
524
packages/mobile/src/views/DisasterManagement/IceDisaster.vue
Normal file
524
packages/mobile/src/views/DisasterManagement/IceDisaster.vue
Normal file
@ -0,0 +1,524 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="content">
|
||||
<PanelItem title="基本信息">
|
||||
<van-form class="IceEventAddForm" label-align="left" colon>
|
||||
<van-field v-model="form.event.occurTime" label="发生时间" center>
|
||||
<template #button>
|
||||
<van-button plain round type="primary" size="mini" @click="getCurrentTime">校准时间</van-button>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field v-model="form.event.occurLocation" label="发生地点" center placeholder="请填写" />
|
||||
<van-field v-model="form.event.routeNo" label="线路编号" center placeholder="请填写" />
|
||||
<van-field v-model="form.event.startStakeNo" label="起点桩号" center placeholder="请填写" />
|
||||
<van-field v-model="form.event.endStakeNo" label="止点桩号" center placeholder="请填写" />
|
||||
<van-field v-model="form.event.disasterMileage" label="受灾里程" center type="number" placeholder="请填写" />
|
||||
</van-form>
|
||||
</PanelItem>
|
||||
|
||||
<PanelItem title="处置情况">
|
||||
<van-form class="IceEventAddForm" label-align="left" colon>
|
||||
<van-field label="处置措施" center>
|
||||
<template #input>
|
||||
<div class="disposal-buttons">
|
||||
<van-button plain :type="form.event.disposalMeasures === '限速通行' ? 'primary' : 'default'" size="small" @click="toggleDisposal('限速通行')">
|
||||
限速通行
|
||||
</van-button>
|
||||
<van-button plain :type="form.event.disposalMeasures === '封闭交通' ? 'primary' : 'default'" size="small" @click="toggleDisposal('封闭交通')" class="last-button">
|
||||
封闭交通
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field v-model="form.event.expectRecoverTime" label="预计恢复时间" center placeholder="请选择" readonly clickable @click="showExpectPicker = true" />
|
||||
<van-popup :show="showExpectPicker" round position="bottom" close-on-click-overlay @close="showExpectPicker = false">
|
||||
<van-picker-group title="选择日期时间" :tabs="['选择日期', '选择时间']" @confirm="handleConfirmExpectTime" @cancel="showExpectPicker = false">
|
||||
<van-date-picker v-model="expectDate" :min-date="minDate" :max-date="maxDate" />
|
||||
<van-time-picker v-model="expectTime" />
|
||||
</van-picker-group>
|
||||
</van-popup>
|
||||
</van-form>
|
||||
</PanelItem>
|
||||
|
||||
<PanelItem title="实施情况">
|
||||
<van-form class="IceEventAddForm" label-align="left" colon>
|
||||
<van-field v-model="form.material.inputManpower" type="number" label="投入人力" center placeholder="请填写">
|
||||
<template #extra> 人次 </template>
|
||||
</van-field>
|
||||
<van-field v-model="form.material.inputFunds" type="number" label="投入资金" center placeholder="请填写">
|
||||
<template #extra> 万元 </template>
|
||||
</van-field>
|
||||
<van-field v-model="form.material.inputEquipment" type="number" label="投入设备" center placeholder="请填写">
|
||||
<template #extra> 台班 </template>
|
||||
</van-field>
|
||||
|
||||
<!-- 选择物资列表 -->
|
||||
<van-field
|
||||
v-for="(material, index) in form.yhzMaterialList"
|
||||
:key="material.rid"
|
||||
v-model="material.usageAmount"
|
||||
type="number"
|
||||
@input="checkMaterialAmount(material, index)"
|
||||
:label="material.wzmc"
|
||||
center
|
||||
:placeholder="`余额: ${material.ye} `"
|
||||
>
|
||||
<template #extra>
|
||||
<span style="margin-right: 10px">{{ material.dw }}</span>
|
||||
<van-button size="small" type="danger" @click.stop="form.yhzMaterialList.splice(index, 1)"> 删除 </van-button>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<van-button class="add-wzbtn" type="primary" icon="plus" plain @click="handleOpenAddMaterial">添加物资 </van-button>
|
||||
<van-popup :show="showAddMaterialPopup" position="bottom" close-on-click-overlay @close="showAddMaterialPopup = false">
|
||||
<div style="padding: 16px">
|
||||
<h3 style="text-align: center; margin-bottom: 16px">添加物资</h3>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<van-field v-model="searchText" placeholder="输入物资名称搜索" clearable @update:model-value="handleSearch"> </van-field>
|
||||
|
||||
<van-checkbox-group v-model="checked">
|
||||
<van-cell-group inset style="margin: 16px 0">
|
||||
<div style="display: flex; justify-content: space-between; padding: 8px 16px">
|
||||
<span>共 {{ materialList.length }} 项</span>
|
||||
<van-button size="mini" @click="toggleSelectAll" :type="isAllSelected ? 'primary' : 'default'">
|
||||
{{ isAllSelected ? '取消全选' : '全选' }}
|
||||
</van-button>
|
||||
</div>
|
||||
<van-cell v-for="(item, index) in materialList" clickable :key="item.rid" :title="item.wzmc" @click="toggle(index)">
|
||||
<template #right-icon>
|
||||
<van-checkbox :name="item.rid" :ref="(el) => (checkboxRefs[index] = el)" @click.stop />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
</van-checkbox-group>
|
||||
|
||||
<van-button type="primary" block @click="addSelectedMaterials" style="margin-top: 10px"> 确认添加 </van-button>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<van-field label="当前通行情况" center>
|
||||
<template #input>
|
||||
<div class="disposal-buttons">
|
||||
<van-button plain :type="form.traffic.currentStatus === 1 ? 'primary' : 'default'" size="small" @click="form.traffic.currentStatus = 1"> 正常通行 </van-button>
|
||||
<van-button plain :type="form.traffic.currentStatus === 2 ? 'primary' : 'default'" size="small" @click="form.traffic.currentStatus = 2"> 限速通行 </van-button>
|
||||
<van-button plain :type="form.traffic.currentStatus === 3 ? 'primary' : 'default'" size="small" @click="form.traffic.currentStatus = 3" class="last-button">
|
||||
封闭交通
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field label="有无车辆滞留" center>
|
||||
<template #input>
|
||||
<div class="disposal-buttons">
|
||||
<van-button plain :type="form.traffic.hasStrandedVehicles === 1 ? 'primary' : 'default'" size="small" @click="form.traffic.hasStrandedVehicles = 1">
|
||||
有滞留
|
||||
</van-button>
|
||||
<van-button
|
||||
plain
|
||||
:type="form.traffic.hasStrandedVehicles === 0 ? 'primary' : 'default'"
|
||||
size="small"
|
||||
@click="
|
||||
() => {
|
||||
form.traffic.hasStrandedVehicles = 0
|
||||
form.traffic.strandedVehicleCount = null
|
||||
}
|
||||
"
|
||||
class="last-button"
|
||||
>
|
||||
无滞留
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field v-if="form.traffic.hasStrandedVehicles === 1" v-model="form.traffic.strandedVehicleCount" type="number" label="滞留车辆数" center placeholder="请填写" />
|
||||
|
||||
<van-field v-model="form.traffic.actualRecoverTime" label="实际恢复时间" center placeholder="请选择" readonly clickable @click="showActualPicker = true" />
|
||||
<van-popup :show="showActualPicker" round position="bottom" close-on-click-overlay @close="showActualPicker = false">
|
||||
<van-picker-group title="选择日期时间" :tabs="['选择日期', '选择时间']" @confirm="handleConfirmActualTime" @cancel="showActualPicker = false">
|
||||
<van-date-picker v-model="actualDate" :min-date="minDate" :max-date="maxDate" />
|
||||
<van-time-picker v-model="actualTime" />
|
||||
</van-picker-group>
|
||||
</van-popup>
|
||||
|
||||
<van-field label="附件" center>
|
||||
<template #input>
|
||||
<van-uploader
|
||||
v-model="fileList"
|
||||
@delete="handleDelete"
|
||||
name="photos"
|
||||
:file-list="fileList"
|
||||
:file-type="['image/jpeg', 'image/png']"
|
||||
:after-read="afterRead"
|
||||
multiple
|
||||
:max-count="6"
|
||||
/>
|
||||
</template>
|
||||
</van-field>
|
||||
</van-form>
|
||||
</PanelItem>
|
||||
</div>
|
||||
|
||||
<van-button type="primary" class="add-btn" @click="handleAdd"> 提交 </van-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, reactive, toRaw, watch, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { showToast, showLoadingToast } from 'vant'
|
||||
import PanelItem from '@/components/PanelItem.vue'
|
||||
import { request } from '../../../../shared/utils/request'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
// 组件挂载时获取数据
|
||||
const yhzDetail = ref({}) // 养护站详情数据
|
||||
const INIT_FORM = reactive({
|
||||
event: {
|
||||
occurLocation: '', // 发生地点
|
||||
routeNo: '', // 线路编号
|
||||
occurTime: '', // 发生时间
|
||||
startStakeNo: '', // 起点桩号
|
||||
endStakeNo: '', // 止点桩号
|
||||
disasterMileage: '', // 受灾里程
|
||||
expectRecoverTime: '', // 预计恢复时间
|
||||
actualRecoverTime: '', // 实际恢复时间
|
||||
serviceStationId: '', // 养护站ID
|
||||
district: '', // 所属区县
|
||||
reportTime: '', // 填报时间
|
||||
reporterPhone: '', // 填报人手机号
|
||||
disposalMeasures: '', // 处置措施
|
||||
createTime: '', // 创建时间
|
||||
updateTime: '', // 更新时间
|
||||
isDeleted: '' // 是否删除 0-未删除 1-已删除
|
||||
},
|
||||
material: {
|
||||
inputManpower: null, // 投入人力
|
||||
inputFunds: null, // 投入资金
|
||||
inputEquipment: null, // 投入设备
|
||||
createTime: '', // 创建时间
|
||||
updateTime: '' // 更新时间
|
||||
},
|
||||
traffic: {
|
||||
currentStatus: 0, // 当前通行情况 1-正常通行 2-限速通行 3-封闭交通
|
||||
hasStrandedVehicles: 0, // 是否有车辆滞留 0-无 1-有
|
||||
strandedVehicleCount: null, // 车辆滞留数量
|
||||
actualRecoverTime: '', // 实际恢复时间
|
||||
createTime: '', // 创建时间
|
||||
updateTime: '' // 更新时间
|
||||
},
|
||||
yhzMaterialList: [], // 养护站物资列表
|
||||
photos: []
|
||||
})
|
||||
const form = reactive({ ...INIT_FORM })
|
||||
const fileList = ref([])
|
||||
|
||||
// 日期格式化
|
||||
const formatTime = (date = new Date()) => {
|
||||
const pad = (n) => n.toString().padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` + `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||
}
|
||||
|
||||
const getCurrentTime = () => {
|
||||
form.event.occurTime = formatTime()
|
||||
}
|
||||
|
||||
const toggleDisposal = (type) => {
|
||||
form.event.disposalMeasures = form.event.disposalMeasures === type ? '' : type
|
||||
}
|
||||
|
||||
// 组件挂载时获取数据
|
||||
onMounted(() => {
|
||||
form.event.occurTime = formatTime() // 初始化为当前时间
|
||||
})
|
||||
|
||||
// 添加物资相关
|
||||
const showAddMaterialPopup = ref(false)
|
||||
const materialList = ref([])
|
||||
const checkboxRefs = ref([])
|
||||
const checked = ref([])
|
||||
const toggle = (index) => {
|
||||
checkboxRefs.value[index].toggle()
|
||||
}
|
||||
|
||||
const searchText = ref('')
|
||||
const handleSearch = () => {
|
||||
getMaterialList(searchText.value)
|
||||
}
|
||||
// 全选功能
|
||||
const toggleSelectAll = () => {
|
||||
if (isAllSelected.value) {
|
||||
checked.value = []
|
||||
} else {
|
||||
checked.value = materialList.value.map((item) => item.rid)
|
||||
}
|
||||
}
|
||||
// 计算是否全选
|
||||
const isAllSelected = computed(() => {
|
||||
return materialList.value.length > 0 && materialList.value.every((item) => checked.value.includes(item.rid))
|
||||
})
|
||||
|
||||
// 添加物资到表单
|
||||
const addSelectedMaterials = () => {
|
||||
checked.value.forEach((rid) => {
|
||||
const material = materialList.value.find((m) => m.rid === rid)
|
||||
if (material && !form.yhzMaterialList.some((m) => m.rid === rid)) {
|
||||
form.yhzMaterialList.push({
|
||||
rid: rid,
|
||||
wzmc: material.wzmc,
|
||||
usageAmount: null,
|
||||
dw: material.dw,
|
||||
ye: material.ye
|
||||
})
|
||||
}
|
||||
})
|
||||
showAddMaterialPopup.value = false
|
||||
checked.value = []
|
||||
}
|
||||
|
||||
// 检查余额
|
||||
const checkMaterialAmount = (material, index) => {
|
||||
if (material.usageAmount > material.ye) {
|
||||
showToast({
|
||||
type: 'fail',
|
||||
message: '输入数量不能超过物资余额'
|
||||
})
|
||||
// 设置为最大值
|
||||
form.yhzMaterialList[index].usageAmount = material.ye
|
||||
}
|
||||
}
|
||||
|
||||
// 查询物资列表
|
||||
const getMaterialList = async (wzmc) => {
|
||||
try {
|
||||
const data = {
|
||||
yhzid: yhzDetail.value.id,
|
||||
wzmc,
|
||||
pageNum: 1,
|
||||
pageSize: 9999
|
||||
}
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/yjwz/list',
|
||||
method: 'GET',
|
||||
params: data
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
materialList.value = res.data.records
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'fail',
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 打开添加物资弹窗
|
||||
const handleOpenAddMaterial = async () => {
|
||||
await getMaterialList()
|
||||
showAddMaterialPopup.value = true
|
||||
}
|
||||
|
||||
const handleAdd = async () => {
|
||||
try {
|
||||
const toast = showLoadingToast({
|
||||
message: '上报中...',
|
||||
forbidClick: true,
|
||||
duration: 0 // 设置为0表示不会自动关闭
|
||||
})
|
||||
form.event.serviceStationId = yhzDetail.value.id
|
||||
form.event.district = yhzDetail.value.qxmc
|
||||
console.log('yhzDetail', toRaw(yhzDetail.value))
|
||||
console.log('form', toRaw(form))
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/event/add',
|
||||
method: 'POST',
|
||||
data: form
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
toast.close()
|
||||
showToast({
|
||||
type: 'success',
|
||||
message: '上报成功'
|
||||
})
|
||||
setTimeout(() => {
|
||||
router.replace('/disasterManagement')
|
||||
}, 500)
|
||||
} else {
|
||||
toast.close()
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: 'fail',
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const expectDate = ref([])
|
||||
const expectTime = ref([])
|
||||
const actualDate = ref([])
|
||||
const actualTime = ref([])
|
||||
|
||||
// 预计恢复时间相关
|
||||
const showExpectPicker = ref(false)
|
||||
const minDate = new Date()
|
||||
const maxDate = new Date(2050, 11, 31)
|
||||
const handleConfirmExpectTime = () => {
|
||||
const [year, month, day] = expectDate.value
|
||||
const [hour, minute] = expectTime.value
|
||||
form.event.expectRecoverTime =
|
||||
`${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} ` + `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:00`
|
||||
showExpectPicker.value = false
|
||||
}
|
||||
// 实际恢复时间相关
|
||||
const showActualPicker = ref(false)
|
||||
const handleConfirmActualTime = () => {
|
||||
const [year, month, day] = actualDate.value
|
||||
const [hour, minute] = actualTime.value
|
||||
form.traffic.actualRecoverTime =
|
||||
`${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} ` + `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}:00`
|
||||
showActualPicker.value = false
|
||||
}
|
||||
|
||||
// 在打开选择器时设置初始值
|
||||
watch(showExpectPicker, (val) => {
|
||||
if (val) {
|
||||
const current = form.event.expectRecoverTime ? new Date(form.event.expectRecoverTime) : new Date()
|
||||
expectDate.value = [current.getFullYear(), current.getMonth() + 1, current.getDate()]
|
||||
expectTime.value = [current.getHours(), current.getMinutes()]
|
||||
}
|
||||
})
|
||||
watch(showActualPicker, (val) => {
|
||||
if (val) {
|
||||
const current = form.traffic.actualRecoverTime ? new Date(form.traffic.actualRecoverTime) : new Date()
|
||||
actualDate.value = [current.getFullYear(), current.getMonth() + 1, current.getDate()]
|
||||
actualTime.value = [current.getHours(), current.getMinutes()]
|
||||
}
|
||||
})
|
||||
|
||||
// 文件上传
|
||||
const afterRead = async (file) => {
|
||||
try {
|
||||
const toast = showLoadingToast({
|
||||
message: '上传中...',
|
||||
forbidClick: true,
|
||||
duration: 0 // 设置为0表示不会自动关闭
|
||||
})
|
||||
const formData = new FormData()
|
||||
formData.append('file', file.file)
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/file/upload',
|
||||
method: 'post',
|
||||
data: formData
|
||||
})
|
||||
toast.close()
|
||||
if (res.code === '00000') {
|
||||
form.photos.push({ photoUrl: res.data })
|
||||
const index = fileList.value.findIndex((f) => f.file === file.file)
|
||||
if (index !== -1) {
|
||||
fileList.value[index].serverUrl = res.data
|
||||
}
|
||||
|
||||
console.log('form.photos', toRaw(form.photos))
|
||||
console.log('fileList.value', fileList.value)
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
toast.close()
|
||||
showToast({
|
||||
type: 'fail',
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 文件删除
|
||||
const handleDelete = (file) => {
|
||||
if (file.serverUrl) {
|
||||
const index = form.photos.findIndex((p) => p.photoUrl === file.serverUrl)
|
||||
if (index !== -1) {
|
||||
form.photos.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.home {
|
||||
/* 自动匹配导航栏高度 */
|
||||
}
|
||||
|
||||
.content {
|
||||
}
|
||||
|
||||
.content .van-cell-group .van-cell {
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.add-wzbtn {
|
||||
width: calc(100% - 32px);
|
||||
margin: 10px 16px;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
width: calc(100% - 32px);
|
||||
margin: 0 auto;
|
||||
border-radius: 24px;
|
||||
font-size: 16px;
|
||||
height: 44px;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.grid {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-good {
|
||||
background-color: #07c160;
|
||||
}
|
||||
|
||||
.status-warning {
|
||||
background-color: #ff976a;
|
||||
}
|
||||
|
||||
.status-danger {
|
||||
background-color: #ee0a24;
|
||||
}
|
||||
|
||||
.disposal-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.disposal-buttons .van-button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.last-button {
|
||||
margin-right: 16px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,313 @@
|
||||
<template>
|
||||
<div class="mobile-selector">
|
||||
<!-- 只读输入框,点击打开选择器 -->
|
||||
<!-- <div class="selector-input" @click="openPicker">
|
||||
<span class="selector-value" :class="{ 'placeholder': !displayValue }">
|
||||
{{ displayValue || placeholder }}
|
||||
</span>
|
||||
<van-icon name="arrow-down" class="selector-icon" />
|
||||
</div> -->
|
||||
<van-field :modelValue="modelValue" is-link readonly :label="label" :placeholder="placeholder" @click="showPicker = true" />
|
||||
|
||||
<!-- 移动端弹出层(使用 van-action-sheet + 内部列表) -->
|
||||
<van-action-sheet v-model:show="showPicker" :title="dialogTitle" :close-on-click-action="false" round :style="{ maxHeight: '80vh' }" @close="handlePickerClose">
|
||||
<div class="picker-content">
|
||||
<!-- 已选择提示 -->
|
||||
<div class="selected-tip" v-if="tempSelectedItem">已选择路线:{{ tempSelectedItem.routeCode }}</div>
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<van-search v-model="searchKeyword" :placeholder="searchPlaceholder" clearable @input="handleSearchInput" @clear="handleSearchClear" />
|
||||
|
||||
<!-- 列表区域 -->
|
||||
<van-list v-model:loading="listLoading" :finished="finished" finished-text="没有更多了" @load="onLoadMore" :immediate-check="false">
|
||||
<van-cell
|
||||
v-for="item in dataList"
|
||||
:key="item.routeCode"
|
||||
:title="item.routeCode"
|
||||
:class="{ 'is-selected': tempSelectedItem?.routeCode === item.routeCode }"
|
||||
@click="handleItemClick(item)"
|
||||
/>
|
||||
<van-empty v-if="!listLoading && dataList.length === 0" description="暂无数据" />
|
||||
</van-list>
|
||||
</div>
|
||||
</van-action-sheet>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onUnmounted } from 'vue'
|
||||
import { showToast } from 'vant'
|
||||
import { request } from '@shared/utils/request'
|
||||
|
||||
// ==================== Props ====================
|
||||
const props = defineProps({
|
||||
// v-model 绑定的值(路线编号)
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 占位符文本
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择路线'
|
||||
},
|
||||
// 弹窗标题
|
||||
dialogTitle: {
|
||||
type: String,
|
||||
default: '选择地点路线'
|
||||
},
|
||||
// 搜索框占位符
|
||||
searchPlaceholder: {
|
||||
type: String,
|
||||
default: '请输入路线编号'
|
||||
},
|
||||
// 请求接口地址
|
||||
apiUrl: {
|
||||
type: String,
|
||||
default: '/snow-ops-platform/infrastructure-asset/routes'
|
||||
},
|
||||
// 每页大小(移动端常用固定分页,也可配置)
|
||||
pageSize: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
// 额外请求参数
|
||||
extraParams: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// 搜索防抖延迟时间(ms)
|
||||
searchDelay: {
|
||||
type: Number,
|
||||
default: 500
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
// ==================== 状态 ====================
|
||||
const showPicker = ref(false) // 控制弹出层显示
|
||||
const searchKeyword = ref('') // 搜索关键词
|
||||
const dataList = ref([]) // 当前已加载的数据列表
|
||||
const listLoading = ref(false) // 是否正在加载更多
|
||||
const finished = ref(false) // 是否已加载全部
|
||||
const currentPage = ref(1) // 当前页码
|
||||
const total = ref(0) // 总记录数
|
||||
|
||||
// 临时选中的数据(确认前暂存,移动端点击即选中并关闭)
|
||||
const tempSelectedItem = ref(null)
|
||||
|
||||
// 防抖定时器
|
||||
let searchTimer = null
|
||||
|
||||
// 显示值:优先显示已确认选中的 routeCode
|
||||
const displayValue = computed(() => {
|
||||
return props.modelValue || ''
|
||||
})
|
||||
|
||||
// 监听外部 modelValue 变化,清空临时选中标记(当外部值改变时)
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (tempSelectedItem.value && tempSelectedItem.value.routeCode !== newVal) {
|
||||
tempSelectedItem.value = null
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// ==================== API 请求 ====================
|
||||
// 加载数据(支持分页追加或重置)
|
||||
const fetchData = async (isReset = true) => {
|
||||
if (isReset) {
|
||||
// 重置时清空列表,重置页码
|
||||
currentPage.value = 1
|
||||
dataList.value = []
|
||||
finished.value = false
|
||||
}
|
||||
|
||||
if (finished.value && !isReset) return
|
||||
|
||||
listLoading.value = true
|
||||
|
||||
try {
|
||||
const params = {
|
||||
pageNum: currentPage.value,
|
||||
pageSize: props.pageSize,
|
||||
lxbh: searchKeyword.value || props.modelValue || undefined,
|
||||
xzdj: props.extraParams?.xzdj,
|
||||
qxid: props.extraParams?.qxid
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if (isReset) {
|
||||
dataList.value = records
|
||||
} else {
|
||||
dataList.value = [...dataList.value, ...records]
|
||||
}
|
||||
|
||||
// 判断是否已加载全部
|
||||
finished.value = dataList.value.length >= total.value
|
||||
} else {
|
||||
showToast(response.message || '加载失败')
|
||||
if (isReset) dataList.value = []
|
||||
finished.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('请求失败:', error)
|
||||
showToast('加载失败,请重试')
|
||||
if (isReset) dataList.value = []
|
||||
finished.value = true
|
||||
} finally {
|
||||
listLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载更多(van-list 的 load 事件)
|
||||
const onLoadMore = () => {
|
||||
if (finished.value) return
|
||||
currentPage.value++
|
||||
fetchData(false)
|
||||
}
|
||||
|
||||
// 防抖搜索函数
|
||||
const debouncedSearch = () => {
|
||||
if (searchTimer) clearTimeout(searchTimer)
|
||||
searchTimer = setTimeout(() => {
|
||||
// 重置列表并重新加载第一页
|
||||
currentPage.value = 1
|
||||
fetchData(true)
|
||||
}, props.searchDelay)
|
||||
}
|
||||
|
||||
// ==================== 事件处理 ====================
|
||||
const openPicker = () => {
|
||||
showPicker.value = true
|
||||
// 打开时重置临时选中状态(基于当前已选中的值)
|
||||
if (props.modelValue) {
|
||||
tempSelectedItem.value = { routeCode: props.modelValue }
|
||||
} else {
|
||||
tempSelectedItem.value = null
|
||||
}
|
||||
// 清空搜索关键词(可选,视需求决定是否保留)
|
||||
searchKeyword.value = ''
|
||||
// 重置分页并加载数据
|
||||
currentPage.value = 1
|
||||
fetchData(true)
|
||||
}
|
||||
|
||||
const handlePickerClose = () => {
|
||||
showPicker.value = false
|
||||
// 清除防抖定时器
|
||||
if (searchTimer) clearTimeout(searchTimer)
|
||||
}
|
||||
|
||||
const handleSearchInput = () => {
|
||||
debouncedSearch()
|
||||
}
|
||||
|
||||
const handleSearchClear = () => {
|
||||
searchKeyword.value = ''
|
||||
debouncedSearch()
|
||||
}
|
||||
|
||||
// 点击某一项:立即选中并关闭弹窗
|
||||
const handleItemClick = (item) => {
|
||||
tempSelectedItem.value = item
|
||||
// 更新 v-model 值
|
||||
emit('update:modelValue', item.routeCode)
|
||||
emit('change', item)
|
||||
// 关闭选择器
|
||||
showPicker.value = false
|
||||
}
|
||||
|
||||
// ==================== 清理定时器 ====================
|
||||
onUnmounted(() => {
|
||||
if (searchTimer) clearTimeout(searchTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mobile-selector {
|
||||
width: 100%;
|
||||
|
||||
.selector-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 12px;
|
||||
background-color: #f7f8fa;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
min-height: 44px; // 移动端触摸区域
|
||||
|
||||
&:active {
|
||||
background-color: #ebedf0;
|
||||
}
|
||||
|
||||
.selector-value {
|
||||
flex: 1;
|
||||
color: #323233;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&.placeholder {
|
||||
color: #c8c9cc;
|
||||
}
|
||||
}
|
||||
|
||||
.selector-icon {
|
||||
color: #969799;
|
||||
font-size: 16px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.picker-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
max-height: 80vh;
|
||||
|
||||
.selected-tip {
|
||||
padding: 12px 16px;
|
||||
background-color: #f0f9eb;
|
||||
color: #67c23a;
|
||||
font-size: 13px;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
}
|
||||
|
||||
// 覆盖 van-list 内部样式,让滚动更自然
|
||||
:deep(.van-list) {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.van-cell {
|
||||
font-size: 14px;
|
||||
padding: 12px 16px;
|
||||
|
||||
&.is-selected {
|
||||
color: #1989fa;
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -4,13 +4,13 @@
|
||||
<PanelItem title="基本信息" v-if="!isContinue">
|
||||
<van-form>
|
||||
<!-- 路况类别 -->
|
||||
<BasePicker v-model="formData.roadConditionType" :options="roadConditionOptions" label="路况类别" placeholder="请选择" />
|
||||
<BasePicker v-model="formData.roadConditionType" :options="options['roadConditionType']" label="路况类别" placeholder="请选择" />
|
||||
|
||||
<!-- 是否阻断 (event.isBlocked) -->
|
||||
<BasePicker v-model="formData.event.isBlocked" :options="blockedOptions" label="是否阻断" placeholder="请选择" />
|
||||
<BasePicker v-model="formData.event.isBlocked" :options="options['yesNoBool']" label="是否阻断" placeholder="请选择" />
|
||||
|
||||
<!-- 抢险进度 (event.repairProgress) -->
|
||||
<BasePicker v-model="formData.event.repairProgress" :options="repairProgressOptions" label="抢险进度" placeholder="请选择" />
|
||||
<BasePicker v-model="formData.event.repairProgress" :options="options['repairProgress']" label="抢险进度" placeholder="请选择" />
|
||||
|
||||
<!-- 水毁处数 (event.damageCount) -->
|
||||
<van-field v-model="formData.event.damageCount" label="水毁处数" placeholder="请填写" type="number" />
|
||||
@ -30,7 +30,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 线路编号 (顶层 routeNo) -->
|
||||
<van-field v-model="formData.routeNo" label="线路编号" placeholder="请填写" />
|
||||
<RoadRoutesPicker v-model="formData.routeNo" label="线路编号" placeholder="请线路" @change="handleRouteNoChange" />
|
||||
|
||||
<!-- 起点桩号 (event.startStakeNo) -->
|
||||
<van-field v-model="formData.event.startStakeNo" label="起点桩号(K)" placeholder="请填写" />
|
||||
@ -38,6 +38,10 @@
|
||||
<!-- 止点桩号 (event.endStakeNo) -->
|
||||
<van-field v-model="formData.event.endStakeNo" label="止点桩号(K)" placeholder="请填写" />
|
||||
|
||||
<van-field v-model="formData.event.longitude" label="经度" placeholder="请填写" />
|
||||
|
||||
<van-field v-model="formData.event.latitude" label="纬度" placeholder="请填写" />
|
||||
|
||||
<!-- 路况位置 (occurLocation) -->
|
||||
<van-field v-model="formData.occurLocation" label="路况位置" placeholder="请填写" />
|
||||
|
||||
@ -51,13 +55,13 @@
|
||||
<div class="disposal-measures">
|
||||
<span class="measures-label">处置措施</span>
|
||||
<div class="measures-options">
|
||||
<!-- 改为单选,使用 van-radio-group -->
|
||||
<van-radio-group v-model="formData.report.disposalMeasures" direction="horizontal">
|
||||
<van-radio name="半幅封闭">半幅封闭</van-radio>
|
||||
<van-radio name="全副封闭">全副封闭</van-radio>
|
||||
<van-radio name="便道通行">便道通行</van-radio>
|
||||
<van-radio name="正常通行">正常通行</van-radio>
|
||||
</van-radio-group>
|
||||
<van-row gutter="10">
|
||||
<van-col v-for="(item, index) in options['disposalMeasures']" :span="24 / options['disposalMeasures'].length" :key="index">
|
||||
<van-button block plain :type="item.value === formData.report.disposalMeasures ? 'primary' : 'default'" @click="formData.report.disposalMeasures = item.value">
|
||||
{{ item.label }}
|
||||
</van-button>
|
||||
</van-col>
|
||||
</van-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -130,44 +134,49 @@
|
||||
<van-field v-model="formData.report.siteDescription" label="现场描述" placeholder="请填写" type="textarea" rows="2" autosize />
|
||||
<van-field label="附件" center>
|
||||
<template #input>
|
||||
<van-uploader accept="video/*,image/*" :modelValue="getFileList()" :after-read="afterRead" multiple :max-count="6" @delete="removeFile" />
|
||||
<van-uploader accept="video/*,image/*" :modelValue="getFileList()" :after-read="afterRead" multiple :max-count="7" @delete="removeFile" />
|
||||
</template>
|
||||
</van-field>
|
||||
</PanelItem>
|
||||
<PanelItem>
|
||||
<PanelItem v-if="!isContinue || (isContinue && !detail?.event.needsRecovery)">
|
||||
<!-- 是否需要恢复重建 (event.needsRecovery) -->
|
||||
<BasePicker v-model="formData.event.needsRecovery" :options="needsRecoveryOptions" label="是否需要恢复重建" placeholder="请选择" />
|
||||
<BasePicker v-model="formData.event.needsRecovery" :options="options['yesNoBool']" label="是否需要恢复重建" placeholder="请选择" />
|
||||
<!-- 恢复重建预估费用 (event.estimatedRecoveryCost) -->
|
||||
<van-field v-model="formData.event.estimatedRecoveryCost" v-if="!isContinue" label="恢复重建预估费用" placeholder="请填写" type="digit">
|
||||
<van-field v-model="formData.event.estimatedRecoveryCost" v-if="formData?.event.needsRecovery" label="恢复重建预估费用" placeholder="请填写" type="digit">
|
||||
<template #button>
|
||||
<span class="field-unit">万元</span>
|
||||
</template>
|
||||
</van-field>
|
||||
</PanelItem>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<van-button type="primary" class="footer-btn" @click="handleSubmit" :loading="submitting"> 提交 </van-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { showToast, showFailToast, showLoadingToast } from 'vant'
|
||||
import PanelItem from '@/components/PanelItem.vue'
|
||||
import BasePicker from '@/components/BasePicker.vue'
|
||||
import BaseDatePicker from '@/components/BaseDatePicker.vue'
|
||||
import RoadRoutesPicker from '../RoadRoutesPicker.vue'
|
||||
import LossList from './LossList.vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { request } from '@shared/utils/request'
|
||||
import { useOptions } from '@shared/composables/useOptions'
|
||||
import mockFormData from '../waterDisasterFormData.json'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const { options } = useOptions()
|
||||
// 是否为续报
|
||||
const isContinue = computed(() => route.query.isContinue)
|
||||
|
||||
|
||||
// 表单数据 - 按 Request 接口结构定义,使用 ref 包装
|
||||
const formData = ref({
|
||||
// 顶层字段
|
||||
occurLocation: '', // 发生地点
|
||||
occurTime: '', // 发生时间
|
||||
occurTime: null, // 发生时间
|
||||
roadConditionType: '', // 路况类别
|
||||
routeNo: '', // 线路编号
|
||||
|
||||
@ -225,31 +234,7 @@ const getFileList = () => {
|
||||
return fileList
|
||||
}
|
||||
|
||||
// BasePicker 选项数据
|
||||
const roadConditionOptions = [
|
||||
{ label: '高速公路', value: '高速公路' },
|
||||
{ label: '国道', value: '国道' },
|
||||
{ label: '省道', value: '省道' },
|
||||
{ label: '县道', value: '县道' },
|
||||
{ label: '乡道', value: '乡道' },
|
||||
{ label: '村道', value: '村道' }
|
||||
]
|
||||
|
||||
const blockedOptions = [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false }
|
||||
]
|
||||
|
||||
const repairProgressOptions = [
|
||||
{ label: '未抢险', value: '未抢险' },
|
||||
{ label: '抢险中', value: '抢险中' },
|
||||
{ label: '已完成', value: '已完成' }
|
||||
]
|
||||
|
||||
const needsRecoveryOptions = [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false }
|
||||
]
|
||||
const submitting = ref(false)
|
||||
|
||||
// 时间选择器范围
|
||||
const minDate = new Date(2020, 0, 1)
|
||||
@ -260,7 +245,7 @@ const initFormData = (newVal) => {
|
||||
// 深度合并数据 - 直接替换整个对象
|
||||
formData.value = {
|
||||
occurLocation: newVal.occurLocation || '',
|
||||
occurTime: newVal.occurTime || '',
|
||||
occurTime: newVal.occurTime || formatTime(),
|
||||
roadConditionType: newVal.roadConditionType || '',
|
||||
routeNo: newVal.routeNo || '',
|
||||
event: { ...formData.value.event, ...(newVal.event || {}) },
|
||||
@ -311,6 +296,11 @@ const isVideoFile = (file) => {
|
||||
if (file.fileUrl && videoExtensions.test(file.fileUrl)) return true
|
||||
}
|
||||
|
||||
const handleRouteNoChange = (item) => {
|
||||
formData.value.event.startStakeNo = item.startStakeNo
|
||||
formData.value.event.endStakeNo = item.endStakeNo
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断图片是否可以上传
|
||||
* @param {File} file - 图片文件
|
||||
@ -371,11 +361,23 @@ const isValidVideo = (file) => {
|
||||
const checkFile = (file) => {
|
||||
// 判断是否为图片
|
||||
if (file.type.startsWith('image/')) {
|
||||
const imageCount = formData.value.fileList?.filter((item) => item.fileType === 1).length
|
||||
if (imageCount == 6) {
|
||||
showFailToast('只能上传六张图片!')
|
||||
return false
|
||||
}
|
||||
|
||||
return isValidImage(file)
|
||||
}
|
||||
|
||||
// 判断是否为视频
|
||||
if (file.type.startsWith('video/')) {
|
||||
const videoCount = formData.value.fileList?.filter((item) => item.fileType === 2).length
|
||||
if (videoCount == 1) {
|
||||
showFailToast('只能上传一个视频文件!')
|
||||
return false
|
||||
}
|
||||
|
||||
return isValidVideo(file)
|
||||
}
|
||||
|
||||
@ -402,9 +404,8 @@ const afterRead = async (options) => {
|
||||
toast.close()
|
||||
if (res.code === '00000') {
|
||||
const name = file.name
|
||||
let type = isImageFile(file) ? 1 : isVideoFile(file) ? 2 : 3
|
||||
|
||||
const url = res.data
|
||||
const type = isImageFile({ fileUrl: url}) ? 1 : isVideoFile({ fileUrl: url}) ? 2 : 3
|
||||
const fileData = {
|
||||
fileName: name,
|
||||
fileUrl: url,
|
||||
@ -413,7 +414,6 @@ const afterRead = async (options) => {
|
||||
}
|
||||
if (!formData.value.fileList) formData.value.fileList = []
|
||||
formData.value.fileList.push(fileData)
|
||||
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
@ -431,14 +431,78 @@ const removeFile = (file, index) => {
|
||||
formData.value.fileList.splice(index, 1)
|
||||
}
|
||||
|
||||
// 暴露验证方法
|
||||
// 简单的空值判断
|
||||
const isEmpty = (value) => {
|
||||
return value === null || value === undefined || value === ''
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
if (!formData.value.occurTime) {
|
||||
showToast('请填写发生时间')
|
||||
if (isEmpty(formData.value.roadConditionType)) {
|
||||
showToast('请选择路况类别')
|
||||
return false
|
||||
}
|
||||
if (!formData.value.routeNo) {
|
||||
showToast('请填写线路编号')
|
||||
if (isEmpty(formData.value.event?.isBlocked)) {
|
||||
showToast('请选择是否阻断')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.repairProgress)) {
|
||||
showToast('请选择抢险进度')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.report?.disposalMeasures)) {
|
||||
showToast('请选择处置措施')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.damageCount)) {
|
||||
showToast('请输入水毁处数')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.blockedMileage)) {
|
||||
showToast('请输入阻断里程')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.occurTime)) {
|
||||
showToast('请选择发生时间')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.report?.expectRecoverTime)) {
|
||||
showToast('请输入预计恢复时间')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.routeNo)) {
|
||||
showToast('请输入线路编号')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.startStakeNo)) {
|
||||
showToast('请输入起点桩号')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.endStakeNo)) {
|
||||
showToast('请输入止点桩号')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.occurLocation)) {
|
||||
showToast('请输入路况位置')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.blockedPointName)) {
|
||||
showToast('请输入阻断点小地名')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.longitude)) {
|
||||
showToast('请输入经度')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.latitude)) {
|
||||
showToast('请输入纬度')
|
||||
return false
|
||||
}
|
||||
if (isEmpty(formData.value.event?.needsRecovery)) {
|
||||
showToast('请选择是否需要恢复重建')
|
||||
return false
|
||||
}
|
||||
if (formData.value.event?.needsRecovery && isEmpty(formData.value.event?.estimatedRecoveryCost)) {
|
||||
showToast('请输入恢复重建预估费用')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -449,6 +513,110 @@ const getFormData = () => {
|
||||
return { ...formData.value }
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
// 验证表单
|
||||
if (!validate()) return
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
// 获取表单数据
|
||||
let formData = getFormData()
|
||||
|
||||
// 添加事件类型和站点信息
|
||||
const submitData = {
|
||||
...formData
|
||||
// 可以在这里添加站点信息等其他数据
|
||||
}
|
||||
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/water-damage/addOrUpdate',
|
||||
method: 'post',
|
||||
data: submitData
|
||||
})
|
||||
|
||||
if (res?.code === '00000') {
|
||||
showSuccessToast('提交成功')
|
||||
let isRebuilded = false
|
||||
if (isContinue && detail.value.event.needsRecovery) {
|
||||
// 如果之前已经进行了项目重建的流程,后续不再进行该流程
|
||||
isRebuilded = true
|
||||
}
|
||||
if (!isRebuilded && submitData.event.needsRecovery) {
|
||||
router.replace({
|
||||
name: 'RebuildAdd',
|
||||
params: {
|
||||
data: res.data.id
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 提交成功后返回列表页
|
||||
setTimeout(() => {
|
||||
router.replace('/disasterManagement')
|
||||
}, 500)
|
||||
}
|
||||
} else {
|
||||
showFailToast(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
showFailToast('提交失败,请重试')
|
||||
console.error('提交失败:', error)
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const detail = ref(null)
|
||||
|
||||
// 获取灾毁详情
|
||||
const getDisasterDetail = async () => {
|
||||
const id = route.query.id
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await request({
|
||||
url: `/snow-ops-platform/water-damage/getById`,
|
||||
method: 'get',
|
||||
params: { id }
|
||||
})
|
||||
|
||||
if (result?.data) {
|
||||
// 接口返回 Data 结构
|
||||
const data = result.data
|
||||
detail.value = result.data
|
||||
const newFormData = {
|
||||
...data,
|
||||
lossList: null,
|
||||
report: route.query?.mock ? mockFormData : {},
|
||||
fileList: null
|
||||
}
|
||||
initFormData(newFormData)
|
||||
} else {
|
||||
showToast(result.message || '获取详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取灾毁详情失败:', error)
|
||||
showToast('获取详情失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 日期格式化
|
||||
const formatTime = (date = new Date()) => {
|
||||
const pad = (n) => n.toString().padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` + `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
formData.value.occurTime = formatTime()
|
||||
if (route.query.id) {
|
||||
getDisasterDetail()
|
||||
} else {
|
||||
initFormData(route.query?.mock ? mockFormData : {})
|
||||
}
|
||||
})
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
validate,
|
||||
@ -543,4 +711,25 @@ defineExpose({
|
||||
width: 110px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.footer-btn {
|
||||
position: fixed;
|
||||
bottom: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: calc(100% - 32px);
|
||||
max-width: 340px;
|
||||
border-radius: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
|
||||
&:active {
|
||||
opacity: 0.9;
|
||||
transform: translateX(-50%) scale(0.98);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<CurrentSite />
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<PanelItem title="基本信息">
|
||||
<PanelItem title="基本信息" v-if="!loading">
|
||||
<template #headerExtra>
|
||||
<div class="status-wrapper">
|
||||
<van-tag :type="getEventStatusType()" size="medium" plain>
|
||||
@ -140,7 +140,7 @@
|
||||
</PanelItem>
|
||||
|
||||
<!-- 填报信息 -->
|
||||
<PanelItem title="填报信息">
|
||||
<PanelItem title="填报信息" v-if="!loading">
|
||||
<!-- 遍历所有填报记录(首报 + 续报) -->
|
||||
<div v-for="(report, index) in allReports" :key="index" class="report-section">
|
||||
<div class="report-header">
|
||||
@ -206,10 +206,12 @@
|
||||
</PanelItem>
|
||||
|
||||
<!-- 底部按钮:未解除状态显示续报按钮 -->
|
||||
<div class="footer-buttons">
|
||||
<div class="footer-buttons" v-if="!loading">
|
||||
<van-button type="primary" class="footer-btn" @click="handleContinueReport">续报</van-button>
|
||||
</div>
|
||||
|
||||
<van-loading class="loading-icon" v-if="loading">加载中...</van-loading>
|
||||
|
||||
<van-image-preview :startPosition="startPosition" v-model:show="previewImagesVisible" :images="imagesForPreview">
|
||||
<template v-slot:index="{ index }">第{{ index + 1 }}页</template>
|
||||
</van-image-preview>
|
||||
@ -241,6 +243,8 @@ const detailData = ref({
|
||||
routeNo: ''
|
||||
})
|
||||
|
||||
const loading = ref(true)
|
||||
|
||||
const allReports = computed(() => {
|
||||
const reports =
|
||||
detailData.value.report?.map((item, index) => {
|
||||
@ -314,6 +318,8 @@ const getDisasterDetail = async () => {
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const result = await request({
|
||||
url: `/snow-ops-platform/water-damage/getById`,
|
||||
@ -341,6 +347,7 @@ const getDisasterDetail = async () => {
|
||||
console.error('获取灾毁详情失败:', error)
|
||||
showToast('获取详情失败,请稍后重试')
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// 点击返回
|
||||
@ -524,4 +531,11 @@ onMounted(() => {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
@ -92,14 +92,14 @@ const gridItems = [
|
||||
params: { data: encodeURIComponent(JSON.stringify(yhzinfo.value)) },
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: group106Icon,
|
||||
text: "冰雪灾害",
|
||||
to: {
|
||||
name: "IceEventManage",
|
||||
params: { data: encodeURIComponent(JSON.stringify(yhzinfo.value)) },
|
||||
},
|
||||
},
|
||||
// {
|
||||
// icon: group106Icon,
|
||||
// text: "冰雪灾害",
|
||||
// to: {
|
||||
// name: "IceEventManage",
|
||||
// params: { data: encodeURIComponent(JSON.stringify(yhzinfo.value)) },
|
||||
// },
|
||||
// },
|
||||
{
|
||||
icon: group106Icon,
|
||||
text: "灾害管理",
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<van-nav-bar title="冰雪灾害" fixed left-arrow @click-left="onClickLeft" />
|
||||
|
||||
<van-cell-group>
|
||||
<van-cell title="当前站点" :value="yhzDetail.mc" />
|
||||
</van-cell-group>
|
||||
<PageContainer title="冰雪灾害" @click-back="onClickLeft">
|
||||
<CurrentSite />
|
||||
|
||||
<div class="content" v-if="eventDetailData">
|
||||
<van-cell-group>
|
||||
@ -276,19 +272,22 @@
|
||||
</van-button>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
</PageContainer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, toRaw, reactive, watch, computed } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { showToast, showLoadingToast, showImagePreview } from "vant";
|
||||
import PageContainer from '@/components/PageContainer.vue'
|
||||
import { request } from "../../../../shared/utils/request";
|
||||
import CurrentSite from '@/components/CurrentSite.vue'
|
||||
import { useYHZStore } from '@/stores/yhzStore';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const yhzStore = useYHZStore()
|
||||
|
||||
const yhzDetail = ref({});
|
||||
const event = ref();
|
||||
const eventDetailData = ref(); // 冰雪事件详情数据
|
||||
|
||||
@ -296,7 +295,7 @@ const eventDetailData = ref(); // 冰雪事件详情数据
|
||||
const getEventDetailData = async () => {
|
||||
try {
|
||||
const res = await request({
|
||||
url: `/snow-ops-platform/event/getById?id=${event.value.id}`,
|
||||
url: `/snow-ops-platform/event/getById?id=${route.query.id}`,
|
||||
method: "GET",
|
||||
});
|
||||
if (res.code === "00000") {
|
||||
@ -313,17 +312,11 @@ const getEventDetailData = async () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const data = JSON.parse(decodeURIComponent(route.params.data));
|
||||
yhzDetail.value = data.yhzDetail;
|
||||
event.value = data.event;
|
||||
getEventDetailData();
|
||||
});
|
||||
|
||||
const onClickLeft = () => {
|
||||
router.push({
|
||||
name: "IceEventManage",
|
||||
params: { data: encodeURIComponent(JSON.stringify(yhzDetail.value)) },
|
||||
});
|
||||
router.replace('/disasterManagement')
|
||||
};
|
||||
|
||||
const showImage = (url) => {
|
||||
@ -574,7 +567,7 @@ const checkMaterialAmount = (material, index) => {
|
||||
const getMaterialList = async (wzmc) => {
|
||||
try {
|
||||
const data = {
|
||||
yhzid: yhzDetail.value.id,
|
||||
yhzid: yhzStore.getYHZInfo?.id,
|
||||
wzmc,
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user