Merge branch 'dev' of http://222.212.85.86:8222/bdzl2/bxztApp into dev
This commit is contained in:
commit
09693b4a80
@ -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>
|
||||
|
||||
526
packages/mobile/src/views/DisasterManagement/IceDisaster.vue
Normal file
526
packages/mobile/src/views/DisasterManagement/IceDisaster.vue
Normal file
@ -0,0 +1,526 @@
|
||||
<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'
|
||||
import { useYHZStore } from '@/stores/yhzStore';
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const yhzStore = useYHZStore()
|
||||
|
||||
// 组件挂载时获取数据
|
||||
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: yhzStore.getYHZInfo.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 = yhzStore.getYHZInfo.id
|
||||
form.event.district = yhzStore.getYHZInfo.qxmc
|
||||
console.log('yhzDetail', toRaw(yhzStore.getYHZInfo))
|
||||
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,323 @@
|
||||
<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" :rules="rules" :required="required" @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
|
||||
},
|
||||
// 表单验证规则
|
||||
rules: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 左上角是否必填标志
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
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['waterRoadConditionType']" 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>
|
||||
|
||||
@ -112,17 +116,17 @@
|
||||
|
||||
<!-- 投入资源 (report) -->
|
||||
<PanelItem>
|
||||
<van-field v-model="formData.report.investedMachinery" label="已投机械" placeholder="请填写" type="digit">
|
||||
<van-field v-model="formData.report.investedMachinery" label="投入机械" placeholder="请填写" type="digit">
|
||||
<template #button>
|
||||
<span class="field-unit">台/班</span>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field v-model="formData.report.investedManpower" label="已投入力" placeholder="请填写" type="number">
|
||||
<van-field v-model="formData.report.investedManpower" label="投入人力" placeholder="请填写" type="number">
|
||||
<template #button>
|
||||
<span class="field-unit">人次</span>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field v-model="formData.report.investedFunds" label="已投资金" placeholder="请填写" type="digit">
|
||||
<van-field v-model="formData.report.investedFunds" label="投入资金" placeholder="请填写" type="digit">
|
||||
<template #button>
|
||||
<span class="field-unit">万元</span>
|
||||
</template>
|
||||
@ -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,
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
<van-form ref="formRef" label-align="left" colon>
|
||||
<van-field v-model="form.project.districtName" label="区县名称" center placeholder="请填写" required
|
||||
:rules="[{ required: true, message: '请填写区县名称' }]" />
|
||||
<van-field v-model="form.project.routeNo" label="线路编号" center placeholder="请填写" required
|
||||
:rules="[{ required: true, message: '请填写线路编号' }]" />
|
||||
<van-field v-model="form.project.startStakeNo" label="起点桩号" center placeholder="请填写" required
|
||||
:rules="[{ required: true, message: '请填写起点桩号' }]" />
|
||||
<van-field v-model="form.project.endStakeNo" label="止点桩号" center placeholder="请填写" required
|
||||
:rules="[{ required: true, message: '请填写止点桩号' }]" />
|
||||
<RoadRoutesPicker v-model="form.project.routeNo" label="线路编号" center placeholder="请选择" required
|
||||
:rules="[{ required: true, message: '请选择线路编号' }]" @change="handleRouteNoChange" />
|
||||
<van-field v-model="form.project.startStakeNo" label="起点桩号" disabled center placeholder="请选择线路编号"
|
||||
required :rules="[{ required: true, message: '请填写起点桩号' }]" />
|
||||
<van-field v-model="form.project.endStakeNo" label="止点桩号" disabled center placeholder="请选择线路编号"
|
||||
required :rules="[{ required: true, message: '请填写止点桩号' }]" />
|
||||
<van-field v-model="form.project.implementMileage" label="实施里程" center placeholder="单位:公里" required
|
||||
type="number" :rules="[{ required: true, message: '请填写实施里程' }]">
|
||||
<template #extra>
|
||||
@ -59,6 +59,7 @@ import PageContainer from '@/components/PageContainer.vue'
|
||||
import { showToast, showLoadingToast } from "vant";
|
||||
import PanelItem from '@/components/PanelItem.vue'
|
||||
import { request } from "../../../../shared/utils/request";
|
||||
import RoadRoutesPicker from '../DisasterManagement/RoadRoutesPicker.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -82,11 +83,47 @@ const form = reactive({
|
||||
const fileList = ref([]);
|
||||
const formRef = ref();
|
||||
|
||||
// 获取灾害事件详情
|
||||
const getDetail = async (id) => {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/water-damage/getById',
|
||||
method: 'GET',
|
||||
params: {
|
||||
id
|
||||
}
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
form.project.districtName = res.data.event.district
|
||||
form.project.routeNo = res.data.routeNo
|
||||
form.project.startStakeNo = res.data.event.startStakeNo
|
||||
form.project.endStakeNo = res.data.event.endStakeNo
|
||||
form.project.implementMileage = res.data.event.blockedMileage
|
||||
form.project.earthworkLoss = Array.isArray(res.data.lossList) ? res.data.lossList.reduce((sum, item) => sum + (item.totalAmount || 0), 0) : 0
|
||||
form.project.disasterType = res.data.roadConditionType
|
||||
form.project.locationRoute = res.data.occurLocation
|
||||
form.project.roadLocation = res.data.event.blockedPointName
|
||||
form.project.blockedPointName = res.data.event.blockedPointName
|
||||
form.project.estimatedCost = res.data.event.estimatedRecoveryCost
|
||||
form.fileList = res.data.fileList || []
|
||||
fileList.value = res.data.fileList || []
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
showToast({
|
||||
type: "fail",
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.params.data) {
|
||||
const data = JSON.parse(decodeURIComponent(route.params.data));
|
||||
console.log('@@@@data', data);
|
||||
// todo 在有传参的时候 调用接口去获取数据 并且初始化表单
|
||||
// console.log('@@@@data', data);
|
||||
// 在有传参的时候 调用接口去获取数据 并且初始化表单
|
||||
getDetail(data);
|
||||
} else {
|
||||
// console.log('无传入数据');
|
||||
}
|
||||
@ -94,7 +131,7 @@ onMounted(() => {
|
||||
|
||||
const handleClickBack = () => {
|
||||
if (route.params.data) {
|
||||
|
||||
router.push('/')
|
||||
} else {
|
||||
router.push('/rebuild')
|
||||
}
|
||||
@ -180,10 +217,10 @@ const handleAdd = async () => {
|
||||
data: form
|
||||
})
|
||||
toast.close();
|
||||
if(res.code === '00000'){
|
||||
if (res.code === '00000') {
|
||||
showToast('提交成功');
|
||||
handleClickBack();
|
||||
}else{
|
||||
} else {
|
||||
showToast('提交失败, 请稍后重试或联系管理员');
|
||||
}
|
||||
} catch (error) {
|
||||
@ -193,6 +230,12 @@ const handleAdd = async () => {
|
||||
}
|
||||
|
||||
|
||||
const handleRouteNoChange = (item) => {
|
||||
form.project.startStakeNo = item.startStakeNo
|
||||
form.project.endStakeNo = item.endStakeNo
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@ -170,17 +170,26 @@ const routes = [
|
||||
}
|
||||
},
|
||||
// 线下帮扶台账 - 法规处
|
||||
// {
|
||||
// path: '/ledgerManagement3',
|
||||
// name: 'ledgerManagement3',
|
||||
// component: () => import('../views/LedgerManagement/index.vue'),
|
||||
// meta: {
|
||||
// title: '驻地台账',
|
||||
// breadcrumb: true,
|
||||
// parentRoute: 'warningManagement3'
|
||||
// }
|
||||
// },
|
||||
|
||||
{
|
||||
path: '/ledgerManagement3',
|
||||
name: 'ledgerManagement3',
|
||||
component: () => import('../views/LedgerManagement/helpLedger/index.vue'),
|
||||
meta: {
|
||||
title: '线下帮扶台账',
|
||||
breadcrumb: true,
|
||||
parentRoute: 'warningManagement3'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/dutyManagement',
|
||||
name: 'dutyManagement',
|
||||
component: () => import('../views/WarningManagement/law/dutyManagement/index.vue'),
|
||||
meta: {
|
||||
title: '值班管理',
|
||||
breadcrumb: true,
|
||||
parentRoute: 'warningManagement3'
|
||||
}
|
||||
},
|
||||
|
||||
// 项目管理 - 区县
|
||||
{
|
||||
@ -245,18 +254,33 @@ const routes = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/disasterReport',
|
||||
name: 'DisasterReport',
|
||||
component: () => import('../views/DisasterManagement/DisasterReport/DisasterReportPC.vue'),
|
||||
path: '/iceDisasterReport',
|
||||
component: () => import('../views/DisasterManagement/IceDisasterReport/IceDisasterReportPC.vue'),
|
||||
meta: {
|
||||
title: '灾毁事件填报',
|
||||
title: '冰雪灾害上报',
|
||||
breadcrumb: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/iceDisasterDetail',
|
||||
component: () => import('../views/DisasterManagement/IceDisasterDetail/IceDisasterDetailPC.vue'),
|
||||
meta: {
|
||||
title: '冰雪灾害详情',
|
||||
breadcrumb: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/waterDisasterReport',
|
||||
component: () => import('../views/DisasterManagement/WaterDisasterReport/WaterDisasterReportPC.vue'),
|
||||
meta: {
|
||||
title: '水毁灾害上报',
|
||||
breadcrumb: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/waterDisasterDetail',
|
||||
name: 'WaterDisasterDetail',
|
||||
component: () => import('../views/DisasterManagement/DisasterDetail/WaterDisasterDetailPC.vue'),
|
||||
component: () => import('../views/DisasterManagement/WaterDisasterDetail/WaterDisasterDetailPC.vue'),
|
||||
meta: {
|
||||
title: '水毁事件详情',
|
||||
breadcrumb: true
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6">
|
||||
<el-form-item label="事件类别">
|
||||
<el-select v-model="filterForm.disasterType" placeholder="请选择" clearable>
|
||||
<el-select v-model="filterForm.type" placeholder="请选择" clearable>
|
||||
<el-option label="水毁事件" value="WATER_DAMAGE" />
|
||||
<el-option label="冰雪灾害" value="ICE_SNOW" />
|
||||
</el-select>
|
||||
@ -37,18 +37,15 @@
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="6">
|
||||
<el-form-item label="是否阻断">
|
||||
<el-select v-model="filterForm.isBlocked" placeholder="请选择" clearable>
|
||||
<el-option label="是" value="是" />
|
||||
<el-option label="否" value="否" />
|
||||
<el-select v-model="filterForm.blocked" placeholder="请选择" clearable>
|
||||
<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 :xs="24" :sm="12" :md="8" :lg="6">
|
||||
<el-form-item label="路线类型">
|
||||
<el-select v-model="filterForm.roadConditionType" placeholder="请选择" clearable>
|
||||
<el-option label="国省道" value="国省道" />
|
||||
<el-option label="县乡道" value="县乡道" />
|
||||
<el-option label="高速" value="高速" />
|
||||
<el-select v-model="filterForm.routeType" placeholder="请选择" clearable>
|
||||
<el-option v-for="(option, idx) in options['roadType']" :label="option.label" :value="option.value" :key="idx" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -69,7 +66,7 @@
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery">查询</el-button>
|
||||
<el-button @click="toReport">新增</el-button>
|
||||
<el-button type="success" @click="handleExport">导出Excel</el-button>
|
||||
<!-- <el-button type="success" @click="handleExport">导出Excel</el-button> -->
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -86,7 +83,11 @@
|
||||
{{ scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="district" label="所属区县" min-width="120" />
|
||||
<el-table-column prop="district" label="所属区县" min-width="120">
|
||||
<template #default="{ row }">
|
||||
<span>{{ getDistrictName(row.district) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="routeNo" label="路线编号" min-width="120" />
|
||||
<el-table-column prop="routeName" label="路线名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="startStakeNo" label="起点桩号" min-width="110" show-overflow-tooltip />
|
||||
@ -120,13 +121,13 @@
|
||||
<el-table-column prop="contactPhone" label="联系电话" width="120" />
|
||||
<el-table-column label="图片" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-icon style="font-size: 18px; cursor: pointer;" v-if="row.images && row.images.length > 0" @click="viewImages(row)"><Picture /></el-icon>
|
||||
<el-icon style="font-size: 18px; cursor: pointer" v-if="row.images && row.images.length > 0" @click="viewImages(row)"><Picture /></el-icon>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="视频" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-icon v-if="row.videos && row.videos.length > 0" style="font-size: 18px; cursor: pointer;" @click="viewVideos(row)"><VideoPlay /></el-icon>
|
||||
<el-icon v-if="row.videos && row.videos.length > 0" style="font-size: 18px; cursor: pointer" @click="viewVideos(row)"><VideoPlay /></el-icon>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -153,7 +154,7 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-image-viewer v-if="showPreviewImage && selectedRow" :url-list="selectedRow.images" show-progress @close="showPreviewImage = false" />
|
||||
<el-image-viewer v-if="showPreviewImage && selectedRow" :url-list="selectedRow.images" show-progress @close="showPreviewImage = false" />
|
||||
<VideoPreviewDialog ref="videoPreviewDialog" />
|
||||
</div>
|
||||
</template>
|
||||
@ -165,18 +166,19 @@ import { useRouter } from 'vue-router'
|
||||
import { request } from '@/utils/request'
|
||||
import { Picture, VideoPlay } from '@element-plus/icons-vue'
|
||||
import VideoPreviewDialog from '@/component/VideoPreviewDialog.vue'
|
||||
import { useOptions } from '@shared/composables/useOptions'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const { options, getAreaOptions } = useOptions()
|
||||
// 筛选表单
|
||||
const filterForm = reactive({
|
||||
unit: '', // 所属单位
|
||||
occurLocation: '', // 发生地点
|
||||
routeNo: '', // 路线编号
|
||||
disasterType: '', // 灾害类型/事件类别
|
||||
type: '', // 灾害类型/事件类别
|
||||
eventStatus: '', // 事件状态 (0-未解除, 1-已解除)
|
||||
isBlocked: '', // 是否阻断
|
||||
roadConditionType: '', // 路线类型
|
||||
blocked: '', // 是否阻断
|
||||
routeType: '', // 路线类型
|
||||
pushStatus: '' // 推送状态
|
||||
})
|
||||
|
||||
@ -199,6 +201,11 @@ const pagination = reactive({
|
||||
total: 0
|
||||
})
|
||||
|
||||
const getDistrictName = (districtId) => {
|
||||
if (!options.value.area?.length) return '-'
|
||||
return options.value.area.find((item) => item.value === districtId)?.label || '-'
|
||||
}
|
||||
|
||||
// 构建查询参数
|
||||
const buildQueryParams = () => {
|
||||
const params = {
|
||||
@ -207,15 +214,15 @@ const buildQueryParams = () => {
|
||||
unit: filterForm.unit,
|
||||
occurLocation: filterForm.occurLocation,
|
||||
routeNo: filterForm.routeNo,
|
||||
disasterType: filterForm.disasterType,
|
||||
type: filterForm.type,
|
||||
eventStatus: filterForm.eventStatus,
|
||||
isBlocked: filterForm.isBlocked,
|
||||
roadConditionType: filterForm.roadConditionType
|
||||
blocked: filterForm.blocked,
|
||||
routeType: filterForm.routeType
|
||||
}
|
||||
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.startDate = dateRange.value[0]
|
||||
params.endDate = dateRange.value[1]
|
||||
params.startTime = dateRange.value[0] + ' 00:00:00'
|
||||
params.endTime = dateRange.value[1] + ' 23:59:59'
|
||||
}
|
||||
|
||||
// 过滤空字符串或null的参数
|
||||
@ -301,10 +308,10 @@ const resetFilters = () => {
|
||||
filterForm.unit = ''
|
||||
filterForm.occurLocation = ''
|
||||
filterForm.routeNo = ''
|
||||
filterForm.disasterType = ''
|
||||
filterForm.type = ''
|
||||
filterForm.eventStatus = ''
|
||||
filterForm.isBlocked = ''
|
||||
filterForm.roadConditionType = ''
|
||||
filterForm.blocked = ''
|
||||
filterForm.routeType = ''
|
||||
filterForm.pushStatus = ''
|
||||
dateRange.value = null
|
||||
handleQuery()
|
||||
@ -312,17 +319,27 @@ const resetFilters = () => {
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = (row) => {
|
||||
router.push({ path: '/waterDisasterDetail', query: { id: row.id } })
|
||||
if (row.disasterType == 'WATER_DAMAGE') {
|
||||
router.push({ path: '/waterDisasterDetail', query: { id: row.id } })
|
||||
}
|
||||
if (row.disasterType == 'ICE_SNOW') {
|
||||
router.push({ path: '/iceDisasterDetail', query: { id: row.id } })
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row) => {
|
||||
router.push({ path: '/waterDisasterDetail', query: { id: row.id, mode: 'edit' } })
|
||||
if (row.disasterType == 'WATER_DAMAGE') {
|
||||
router.push({ path: '/waterDisasterDetail', query: { id: row.id, mode: 'edit' } })
|
||||
}
|
||||
if (row.disasterType == 'ICE_SNOW') {
|
||||
router.push({ path: '/iceDisasterDetail', query: { id: row.id, mode: 'edit' } })
|
||||
}
|
||||
}
|
||||
|
||||
// 新增跳转
|
||||
const toReport = () => {
|
||||
router.push('/disasterReport')
|
||||
router.push('/waterDisasterReport')
|
||||
}
|
||||
|
||||
// 查看图片
|
||||
@ -353,6 +370,8 @@ const handleCurrentChange = (val) => {
|
||||
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
getAreaOptions()
|
||||
|
||||
fetchData()
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -1,115 +0,0 @@
|
||||
<template>
|
||||
<div class="disaster-report">
|
||||
<el-page-header @back="handleBack" class="page-header">
|
||||
<template #content>
|
||||
<span class="title">{{ isEdit ? '灾毁填报' : '灾毁填报' }}</span>
|
||||
</template>
|
||||
</el-page-header>
|
||||
|
||||
<WaterDisasterReport />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus, Upload } from '@element-plus/icons-vue'
|
||||
import { request } from '@/utils/request'
|
||||
import WaterDisasterReport from './WaterDisasterReportPC.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const isEdit = computed(() => !!route.query.id)
|
||||
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.disaster-report {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
padding: 0 0 16px 0;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.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-suffix {
|
||||
margin-left: 8px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.upload-tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
padding: 20px 0 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,449 @@
|
||||
<template>
|
||||
<div class="water-disaster-pc">
|
||||
<!-- 合并后的单个卡片 -->
|
||||
<el-card class="form-card" shadow="never">
|
||||
<div slot="header" class="card-header">
|
||||
<span>续报信息</span>
|
||||
</div>
|
||||
|
||||
<!-- 所有表单项合并到一个区域,每行一个 -->
|
||||
<el-form :model="formData" label-width="120px" size="small">
|
||||
<!-- 处置措施 -->
|
||||
<el-form-item label="处置措施">
|
||||
<el-select v-model="formData.report.disposalMeasures">
|
||||
<el-option label="半幅封闭" value="半幅封闭" />
|
||||
<el-option label="全副封闭" value="全副封闭" />
|
||||
<el-option label="便道通行" value="便道通行" />
|
||||
<el-option label="正常通行" value="正常通行" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 预计恢复时间 -->
|
||||
<el-form-item label="预计恢复时间">
|
||||
<el-date-picker
|
||||
v-model="formData.report.expectRecoverTime"
|
||||
type="datetime"
|
||||
placeholder="请选择时间"
|
||||
value-format="yyyy-MM-dd HH:mm"
|
||||
:picker-options="pickerOptions"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 实际恢复时间 -->
|
||||
<el-form-item label="实际恢复时间">
|
||||
<el-date-picker
|
||||
v-model="formData.report.actualRecoverTime"
|
||||
type="datetime"
|
||||
placeholder="请选择时间"
|
||||
value-format="yyyy-MM-dd HH:mm"
|
||||
:picker-options="pickerOptions"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 受伤人员 -->
|
||||
<el-form-item label="受伤人员">
|
||||
<el-input-number v-model="formData.report.injuredCount" :min="0" :controls="false" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">人</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 死亡人员 -->
|
||||
<el-form-item label="死亡人员">
|
||||
<el-input-number v-model="formData.report.deadCount" :min="0" :controls="false" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">人</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 滞留人员 -->
|
||||
<el-form-item label="滞留人员">
|
||||
<el-input-number v-model="formData.report.strandedPersonCount" :min="0" :controls="false" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">人</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 损坏车辆 -->
|
||||
<el-form-item label="损坏车辆">
|
||||
<el-input-number v-model="formData.report.damagedVehicleCount" :min="0" :controls="false" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">辆</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 滞留车辆 -->
|
||||
<el-form-item label="滞留车辆">
|
||||
<el-input-number v-model="formData.report.strandedVehicleCount" :min="0" :controls="false" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">辆</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 损失列表组件 -->
|
||||
<loss-list :col-span="24" v-model="formData.lossList" />
|
||||
|
||||
<!-- 处理情况 -->
|
||||
<el-form-item label="处理情况">
|
||||
<el-input v-model="formData.report.remark" type="textarea" :rows="2" placeholder="请填写(选填)" style="width: 60%" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 损失总金额 -->
|
||||
<el-form-item label="损失总金额">
|
||||
<el-input v-model="formData.report.totalLossAmount" placeholder="请填写(选填)" style="width: 300px">
|
||||
<template slot="append">万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 已投机械 -->
|
||||
<el-form-item label="已投机械">
|
||||
<el-input v-model="formData.report.investedMachinery" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">台/班</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 投入人力 -->
|
||||
<el-form-item label="投入人力">
|
||||
<el-input-number v-model="formData.report.investedManpower" :min="0" :controls="false" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">人次</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 投入资金 -->
|
||||
<el-form-item label="投入资金">
|
||||
<el-input v-model="formData.report.investedFunds" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">万元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 现场描述 -->
|
||||
<el-form-item label="现场描述">
|
||||
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="2" placeholder="请填写" style="width: 60%" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 是否需要恢复重建 -->
|
||||
<el-form-item label="是否需要恢复重建">
|
||||
<el-radio-group v-model="formData.event.needsRecovery">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSubmit">追加记录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { request } from '@shared/utils/request'
|
||||
import LossList from '../WaterDisasterReport/WaterDisasterLossListPC.vue'
|
||||
|
||||
// Props 定义
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
// Emits 定义
|
||||
const emit = defineEmits(['input', 'change', 'submit'])
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
occurLocation: '',
|
||||
occurTime: '',
|
||||
roadConditionType: '',
|
||||
routeNo: '',
|
||||
event: {
|
||||
blockedMileage: '',
|
||||
blockedPointName: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
damageCount: '',
|
||||
district: '',
|
||||
endStakeNo: '',
|
||||
estimatedRecoveryCost: '',
|
||||
inspectionMileage: '',
|
||||
isBlocked: '',
|
||||
needsRecovery: '',
|
||||
repairProgress: '',
|
||||
reporterUnit: '',
|
||||
startStakeNo: ''
|
||||
},
|
||||
report: {
|
||||
actualRecoverTime: '',
|
||||
damagedVehicleCount: '',
|
||||
deadCount: '',
|
||||
disposalMeasures: '',
|
||||
expectRecoverTime: '',
|
||||
injuredCount: '',
|
||||
investedFunds: '',
|
||||
investedMachinery: '',
|
||||
investedManpower: '',
|
||||
remark: '',
|
||||
siteDescription: '',
|
||||
strandedPersonCount: '',
|
||||
strandedVehicleCount: '',
|
||||
totalLossAmount: ''
|
||||
},
|
||||
lossList: [],
|
||||
fileList: []
|
||||
})
|
||||
|
||||
// 处置措施单选值
|
||||
const disposalMeasureValue = ref('')
|
||||
|
||||
// 下拉选项
|
||||
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 pickerOptions = {
|
||||
disabledDate(time) {
|
||||
return time.getTime() < new Date(2020, 0, 1) || time.getTime() > new Date(2030, 11, 31)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听处置措施单选值变化
|
||||
watch(disposalMeasureValue, (newVal) => {
|
||||
formData.report.disposalMeasures = newVal
|
||||
})
|
||||
|
||||
// 监听外部传入的值
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal) => {
|
||||
if (newVal && Object.keys(newVal).length > 0) {
|
||||
initFormData(newVal)
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
// 监听表单数据变化,向外传递
|
||||
watch(
|
||||
() => formData,
|
||||
(newVal) => {
|
||||
emit('input', newVal)
|
||||
emit('change', newVal)
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 从 report.disposalMeasures 初始化处置措施单选值
|
||||
watch(
|
||||
() => formData.report.disposalMeasures,
|
||||
(newVal) => {
|
||||
if (newVal && typeof newVal === 'string') {
|
||||
disposalMeasureValue.value = newVal
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 初始化表单数据
|
||||
const initFormData = (data) => {
|
||||
Object.assign(formData, {
|
||||
occurLocation: data.occurLocation || '',
|
||||
occurTime: data.occurTime || '',
|
||||
roadConditionType: data.roadConditionType || '',
|
||||
routeNo: data.routeNo || '',
|
||||
event: { ...formData.event, ...(data.event || {}) },
|
||||
report: { ...formData.report, ...(data.report || {}) },
|
||||
lossList: data.lossList || [],
|
||||
fileList: data.fileList || []
|
||||
})
|
||||
|
||||
if (data.report?.disposalMeasures) {
|
||||
disposalMeasureValue.value = data.report.disposalMeasures
|
||||
}
|
||||
}
|
||||
|
||||
// 校准时间
|
||||
const calibrateTime = () => {
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(now.getDate()).padStart(2, '0')
|
||||
const hours = String(now.getHours()).padStart(2, '0')
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0')
|
||||
formData.occurTime = `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
ElMessage.success('时间已校准为当前时间')
|
||||
}
|
||||
|
||||
// 表单验证
|
||||
const validate = () => {
|
||||
if (!formData.occurTime) {
|
||||
ElMessage.warning('请填写发生时间')
|
||||
return false
|
||||
}
|
||||
if (!formData.routeNo) {
|
||||
ElMessage.warning('请填写线路编号')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 获取表单数据
|
||||
const getFormData = () => {
|
||||
return { ...formData }
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
Object.assign(formData, {
|
||||
occurLocation: '',
|
||||
occurTime: '',
|
||||
roadConditionType: '',
|
||||
routeNo: '',
|
||||
event: {
|
||||
blockedMileage: '',
|
||||
blockedPointName: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
damageCount: '',
|
||||
district: '',
|
||||
endStakeNo: '',
|
||||
estimatedRecoveryCost: '',
|
||||
inspectionMileage: '',
|
||||
isBlocked: '',
|
||||
needsRecovery: '',
|
||||
repairProgress: '',
|
||||
reporterUnit: '',
|
||||
startStakeNo: ''
|
||||
},
|
||||
report: {
|
||||
actualRecoverTime: '',
|
||||
damagedVehicleCount: '',
|
||||
deadCount: '',
|
||||
disposalMeasures: '',
|
||||
expectRecoverTime: '',
|
||||
injuredCount: '',
|
||||
investedFunds: '',
|
||||
investedMachinery: '',
|
||||
investedManpower: '',
|
||||
remark: '',
|
||||
siteDescription: '',
|
||||
strandedPersonCount: '',
|
||||
strandedVehicleCount: '',
|
||||
totalLossAmount: ''
|
||||
},
|
||||
lossList: [],
|
||||
fileList: []
|
||||
})
|
||||
disposalMeasureValue.value = ''
|
||||
}
|
||||
|
||||
// 提交方法
|
||||
const submit = () => {
|
||||
if (validate()) {
|
||||
emit('submit', getFormData())
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
// 验证表单
|
||||
if (!validate()) {
|
||||
return
|
||||
}
|
||||
|
||||
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('提交成功')
|
||||
|
||||
emit("refresh")
|
||||
} else {
|
||||
ElMessage.error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('提交失败,请重试')
|
||||
console.error('提交失败:', error)
|
||||
} finally {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
validate,
|
||||
initFormData,
|
||||
getFormData,
|
||||
resetForm,
|
||||
calibrateTime,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.water-disaster-pc {
|
||||
background-color: #f5f7fa;
|
||||
height: 100%;
|
||||
|
||||
.form-card {
|
||||
border-radius: 8px;
|
||||
|
||||
.card-header {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
border-left: 4px solid #409eff;
|
||||
padding-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-form-item {
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
::v-deep .el-input-group__append {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
::v-deep .el-input-number {
|
||||
width: 300px;
|
||||
|
||||
.el-input-group__append {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-radio-group {
|
||||
.el-radio {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,606 @@
|
||||
<template>
|
||||
<div class="web-detail-container">
|
||||
<!-- 页面头部 -->
|
||||
<div class="page-header">
|
||||
<div class="header-left">
|
||||
<el-button :icon="ArrowLeft" @click="handleClickBack">返回</el-button>
|
||||
<h2 class="page-title">冰雪事件详情</h2>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-tag :type="getEventStatusType()" size="large">
|
||||
{{ getEventStatusText() }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content-container">
|
||||
<div class="left-panel">
|
||||
<!-- 基本信息卡片 -->
|
||||
<el-card class="info-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">基本信息</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">事件类型:</span>
|
||||
<span class="info-value">冰雪事件</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">路况类别:</span>
|
||||
<span class="info-value">{{ detailData.roadConditionType || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">是否阻断:</span>
|
||||
<span class="info-value">{{ detailData.event?.isBlocked ? '是' : '否' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">抢险进度:</span>
|
||||
<span class="info-value">{{ detailData.event?.repairProgress || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">处理措施:</span>
|
||||
<span class="info-value">{{ getBaseDisposalMeasures() }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">水毁处数:</span>
|
||||
<span class="info-value">{{ detailData.event?.damageCount || 0 }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">阻断里程:</span>
|
||||
<span class="info-value">{{ detailData.event?.blockedMileage ? detailData.event.blockedMileage + '公里' : '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">地点路线:</span>
|
||||
<span class="info-value">{{ detailData.occurLocation || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">起点桩号:</span>
|
||||
<span class="info-value">{{ detailData.event?.startStakeNo || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">止点桩号:</span>
|
||||
<span class="info-value">{{ detailData.event?.endStakeNo || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">路况位置:</span>
|
||||
<span class="info-value">{{ detailData.event?.blockedPointName || detailData.occurLocation || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">阻断点小地名:</span>
|
||||
<span class="info-value">{{ detailData.event?.blockedPointName || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">所属区县:</span>
|
||||
<span class="info-value">{{ detailData.event?.district || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">发生时间:</span>
|
||||
<span class="info-value">{{ detailData.occurTime || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">是否恢复重建:</span>
|
||||
<span class="info-value">{{ detailData.event?.needsRecovery ? '是' : '否' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="16" v-if="detailData.event?.needsRecovery">
|
||||
<div class="info-item">
|
||||
<span class="info-label">恢复重建预估费用:</span>
|
||||
<span class="info-value">{{ detailData.event?.estimatedRecoveryCost ? detailData.event.estimatedRecoveryCost + '万元' : '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<!-- 填报信息卡片 -->
|
||||
<el-card class="info-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">填报信息</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="hasReportData">
|
||||
<div v-for="(report, index) in allReports" :key="index" class="report-section">
|
||||
<div class="report-header">
|
||||
<span class="report-title">{{ report?.title }}</span>
|
||||
<span class="report-meta">时间:{{ report.reportTime || '-' }}</span>
|
||||
</div>
|
||||
<div class="content-wrapper">
|
||||
<div class="basic-info-wrapper">
|
||||
<div class="info-list">
|
||||
<div class="info-item">
|
||||
<span class="info-label">现场描述:</span>
|
||||
<span class="info-value">{{ report.siteDescription || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">处置措施:</span>
|
||||
<span class="info-value">{{ report.disposalMeasures || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">实际恢复时间:</span>
|
||||
<span class="info-value">{{ report.actualRecoverTime || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">预计恢复时间:</span>
|
||||
<span class="info-value">{{ report.expectRecoverTime || '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">填报人:</span>
|
||||
<span class="info-value">{{ report.reporterName ? report.reporterName : '-' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">联系电话:</span>
|
||||
<span class="info-value">{{ report.phone ? report.phone : '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-list">
|
||||
<FileUpload v-model="report.fileList" :readonly="!isEdit" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detal-info-wrapper">
|
||||
<template v-if="report.showDetail">
|
||||
<LossListDetail :modelValue="report.lossList" :col-span="8" />
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="8">
|
||||
<div class="info-item margin">
|
||||
<span class="info-label">投入机械:</span>
|
||||
<span class="info-value">{{ report.investedMachinery ? report.investedMachinery + '台/班' : '-'}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item margin">
|
||||
<span class="info-label">投入人力:</span>
|
||||
<span class="info-value">{{ report.investedManpower ? report.investedManpower + '人次' : '-'}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item margin">
|
||||
<span class="info-label">投入资金:</span>
|
||||
<span class="info-value">{{ report.investedFunds ? report.investedFunds + '万元' : '-'}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-button style="margin-top: 30px" type="primary" link @click="report.showDetail = !report.showDetail">
|
||||
{{ report.showDetail ? '点击关闭详情' : '点击查看详情' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无填报信息" :image-size="100" />
|
||||
</el-card>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<!-- <div class="footer-buttons">
|
||||
<el-button type="primary" size="large" @click="handleContinueReport" class="footer-btn"> 续报 </el-button>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="right-panel" v-if="isEdit">
|
||||
<ContinueReport ref="continueReport" @refresh="getDisasterDetail" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, computed } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ArrowLeft, Picture, VideoCamera } from '@element-plus/icons-vue'
|
||||
import ContinueReport from './IceDisasterContinueReportPC.vue'
|
||||
import { request } from '@shared/utils/request'
|
||||
import FileUpload from '@/component/FileUpload/FileUpload.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
// 详情数据
|
||||
const detailData = ref({
|
||||
event: null,
|
||||
report: [],
|
||||
fileList: [],
|
||||
lossList: [],
|
||||
occurLocation: '',
|
||||
occurTime: '',
|
||||
roadConditionType: '',
|
||||
routeNo: ''
|
||||
})
|
||||
|
||||
// 事件状态
|
||||
const eventStatus = ref(0)
|
||||
|
||||
// 是否为编辑
|
||||
const isEdit = computed(() => {
|
||||
return route.query.mode === 'edit'
|
||||
})
|
||||
|
||||
const continueReport = ref(null)
|
||||
|
||||
// 所有填报记录(首报 + 续报)
|
||||
const allReports = computed(() => {
|
||||
const reports =
|
||||
detailData.value.report?.map((item, index) => {
|
||||
if (index === detailData.value.report.length - 1) {
|
||||
item.title = '首报'
|
||||
} else {
|
||||
item.title = '续报' + (detailData.value.report.length - 1 - index)
|
||||
}
|
||||
return item
|
||||
}) || []
|
||||
return reports
|
||||
// return reports.reverse()
|
||||
})
|
||||
|
||||
// 是否有填报数据
|
||||
const hasReportData = computed(() => {
|
||||
return allReports.value.length > 0
|
||||
})
|
||||
|
||||
// 获取事件状态文本
|
||||
const getEventStatusText = () => {
|
||||
return eventStatus.value === 1 ? '已解除' : '未解除'
|
||||
}
|
||||
|
||||
// 获取事件状态类型
|
||||
const getEventStatusType = () => {
|
||||
return eventStatus.value === 1 ? 'success' : 'danger'
|
||||
}
|
||||
|
||||
const getBaseDisposalMeasures = () => {
|
||||
const firstItem = allReports.value[0]
|
||||
if (!firstItem) return '-'
|
||||
return formatDisposalMeasures(firstItem.disposalMeasures || '') || '-'
|
||||
}
|
||||
|
||||
// 格式化处置措施
|
||||
const formatDisposalMeasures = (measures) => {
|
||||
if (!measures) return ''
|
||||
const measureMap = {
|
||||
半幅封闭: '半幅封闭',
|
||||
全副封闭: '全副封闭',
|
||||
便道通行: '便道通行',
|
||||
正常通行: '正常通行'
|
||||
}
|
||||
return measures
|
||||
.split(',')
|
||||
.map((m) => measureMap[m.trim()] || m.trim())
|
||||
.join('、')
|
||||
}
|
||||
|
||||
// 获取损失描述
|
||||
const getLossDescription = (report) => {
|
||||
const lossList = report?.lossList
|
||||
if (!lossList || lossList.length === 0) return '-'
|
||||
|
||||
const totalVolume = lossList.reduce((sum, loss) => {
|
||||
const volume = (loss.length || 0) * (loss.width || 0) * (loss.height || 0)
|
||||
return sum + volume
|
||||
}, 0)
|
||||
|
||||
const totalAmount = lossList.reduce((sum, loss) => sum + (loss.totalAmount || 0), 0)
|
||||
|
||||
return `${totalVolume}方,共损失${totalAmount}万元`
|
||||
}
|
||||
|
||||
// 获取车辆滞留文本
|
||||
const getVehicleStrandedText = (report) => {
|
||||
const count = report?.strandedVehicleCount || 0
|
||||
return count > 0 ? `有车滞留,共${count}辆` : '无车滞留'
|
||||
}
|
||||
|
||||
// 获取灾毁详情
|
||||
const getDisasterDetail = async () => {
|
||||
const id = route.query.id
|
||||
if (!id) {
|
||||
ElMessage.warning('缺少灾毁ID')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await request({
|
||||
url: `/snow-ops-platform/water-damage/getById`,
|
||||
method: 'get',
|
||||
params: { id }
|
||||
})
|
||||
|
||||
if (result?.data) {
|
||||
const data = result.data
|
||||
console.log('🚀 ~ getDisasterDetail ~ data:', data)
|
||||
detailData.value = {
|
||||
event: data.event || null,
|
||||
report: data.report || [],
|
||||
fileList: data.fileList || [],
|
||||
lossList: data.lossList || [],
|
||||
occurLocation: data.occurLocation || '',
|
||||
occurTime: data.occurTime || '',
|
||||
roadConditionType: data.roadConditionType || '',
|
||||
routeNo: data.routeNo || ''
|
||||
}
|
||||
eventStatus.value = data.eventStatus || 0
|
||||
|
||||
if (isEdit.value) {
|
||||
const newFormData = {
|
||||
...data,
|
||||
lossList: null,
|
||||
report: {},
|
||||
fileList: null
|
||||
}
|
||||
continueReport.value?.initFormData(newFormData)
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning(result.message || '获取详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取灾毁详情失败:', error)
|
||||
ElMessage.error('获取详情失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 点击返回
|
||||
const handleClickBack = () => {
|
||||
router.push('/disasterManagement')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDisasterDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.web-detail-container {
|
||||
padding: 20px;
|
||||
background: #f5f7fa;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 0 20px;
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-card {
|
||||
margin-bottom: 20px;
|
||||
border-radius: 8px;
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-row {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
line-height: 1.5;
|
||||
|
||||
& + .info-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&.margin {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.report-section {
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.report-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px dashed #dcdfe6;
|
||||
|
||||
.report-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.report-meta {
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-wrapper {
|
||||
margin-top: 12px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #ebeef5;
|
||||
|
||||
.attachment-item {
|
||||
.attachment-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
|
||||
.attachment-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
background: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #ecf5ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-size: 13px;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-buttons {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 16px 24px;
|
||||
background: #fff;
|
||||
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
|
||||
z-index: 100;
|
||||
|
||||
.footer-btn {
|
||||
width: 200px;
|
||||
height: 44px;
|
||||
font-size: 16px;
|
||||
border-radius: 22px;
|
||||
}
|
||||
}
|
||||
.content-container {
|
||||
display: flex;
|
||||
.left-panel {
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.right-panel {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.basic-info-wrapper {
|
||||
display: flex;
|
||||
}
|
||||
.detal-info-wrapper {
|
||||
margin-top: 10px;
|
||||
border-top: 1px solid #efefef;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.info-list {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.file-list {
|
||||
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,704 @@
|
||||
<template>
|
||||
<div class="disaster-form-page">
|
||||
<el-page-header style="margin-bottom: 10px;" @back="router.go(-1)" class="page-header">
|
||||
<template #content>
|
||||
<span class="title">{{ '冰雪灾害填报' }}</span>
|
||||
</template>
|
||||
</el-page-header>
|
||||
|
||||
<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="发生时间" 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-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="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-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-col :span="8">
|
||||
<el-form-item label="受灾里程" prop="event.disasterMileage">
|
||||
<el-input v-model="formData.event.disasterMileage" placeholder="请填写" />
|
||||
</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 #prepend>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 #prepend>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 { request } from '@/utils/request'
|
||||
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, // 联系电话
|
||||
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({
|
||||
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>
|
||||
@ -99,15 +99,15 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 已投入力 -->
|
||||
<el-form-item label="已投入力">
|
||||
<!-- 投入人力 -->
|
||||
<el-form-item label="投入人力">
|
||||
<el-input-number v-model="formData.report.investedManpower" :min="0" :controls="false" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">人次</template>
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 已投资金 -->
|
||||
<el-form-item label="已投资金">
|
||||
<!-- 投入资金 -->
|
||||
<el-form-item label="投入资金">
|
||||
<el-input v-model="formData.report.investedFunds" placeholder="请填写" style="width: 300px">
|
||||
<template slot="append">万元</template>
|
||||
</el-input>
|
||||
@ -138,7 +138,7 @@
|
||||
import { ref, reactive, watch, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { request } from '@shared/utils/request'
|
||||
import LossList from '../DisasterReport/WaterDisasterLossListPC.vue'
|
||||
import LossList from '../WaterDisasterReport/WaterDisasterLossListPC.vue'
|
||||
|
||||
// Props 定义
|
||||
const props = defineProps({
|
||||
@ -4,7 +4,7 @@
|
||||
<div class="page-header">
|
||||
<div class="header-left">
|
||||
<el-button :icon="ArrowLeft" @click="handleClickBack">返回</el-button>
|
||||
<h2 class="page-title">灾毁详情</h2>
|
||||
<h2 class="page-title">水毁事件详情</h2>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-tag :type="getEventStatusType()" size="large">
|
||||
@ -132,7 +132,7 @@
|
||||
<span class="info-value">{{ detailData.event?.needsRecovery ? '是' : '否' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-col :span="16" v-if="detailData.event?.needsRecovery">
|
||||
<div class="info-item">
|
||||
<span class="info-label">恢复重建预估费用:</span>
|
||||
<span class="info-value">{{ detailData.event?.estimatedRecoveryCost ? detailData.event.estimatedRecoveryCost + '万元' : '-' }}</span>
|
||||
@ -195,21 +195,21 @@
|
||||
<LossListDetail :modelValue="report.lossList" :col-span="8" />
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">已投入机械:</span>
|
||||
<span class="info-value">{{ report.investedMachinery }}</span>
|
||||
<div class="info-item margin">
|
||||
<span class="info-label">投入机械:</span>
|
||||
<span class="info-value">{{ report.investedMachinery ? report.investedMachinery + '台/班' : '-'}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">已投入人力:</span>
|
||||
<span class="info-value">{{ report.investedManpower }}</span>
|
||||
<div class="info-item margin">
|
||||
<span class="info-label">投入人力:</span>
|
||||
<span class="info-value">{{ report.investedManpower ? report.investedManpower + '人次' : '-'}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
<span class="info-label">已投入资金:</span>
|
||||
<span class="info-value">{{ report.investedFunds }}</span>
|
||||
<div class="info-item margin">
|
||||
<span class="info-label">投入资金:</span>
|
||||
<span class="info-value">{{ report.investedFunds ? report.investedFunds + '万元' : '-'}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -246,7 +246,7 @@ import ContinueReport from './WaterDisasterContinueReportPC.vue'
|
||||
import { request } from '@shared/utils/request'
|
||||
import LossListDetail from './WaterDisasterLossListDetailPC.vue'
|
||||
import FileUpload from '@/component/FileUpload/FileUpload.vue'
|
||||
import mockData from '../DisasterReport/waterMockJson.json'
|
||||
import mockData from '../WaterDisasterReport/waterMockJson.json'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
@ -469,6 +469,10 @@ onMounted(() => {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&.margin {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
@ -71,7 +71,6 @@ onMounted(async () => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loss-list-detail {
|
||||
width: 100%;
|
||||
|
||||
.loss-table {
|
||||
margin-bottom: 16px;
|
||||
@ -1,14 +1,21 @@
|
||||
<template>
|
||||
<el-row class="loss-list-pc" :gutter="24">
|
||||
<el-col :span="colSpan" v-for="(item, index) in configs" :key="index">
|
||||
<el-form-item :label="item.lossTypeName">
|
||||
<el-input :modelValue="getValue(item)" @update:modelValue="(event) => changeValue(item, event)">
|
||||
<template #suffix>
|
||||
<span>{{ item.unit }}</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<template v-for="(item, index) in configs" :key="index">
|
||||
<el-col :span="colSpan">
|
||||
<el-form-item :label="item.lossTypeName">
|
||||
<el-input :modelValue="getValue(item)" @update:modelValue="(event) => changeValue(item, event)">
|
||||
<template #suffix>
|
||||
<span>{{ item.unit }}</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" v-if="item.lossTypeCode == 'OTHER_LOSS'">
|
||||
<el-form-item label="其它损失描述">
|
||||
<el-input v-model="getValueItem(item).remark" placeholder="请填写"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
@ -32,9 +39,17 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const getValue = (config) => {
|
||||
const value = props.modelValue.find((v) => v.lossTypeId === config.lossTypeId)
|
||||
if (value == null) props.modelValue.push({ ...config })
|
||||
return value?.totalAmount || 0
|
||||
const item = getValueItem(config)
|
||||
return item?.totalAmount || 0
|
||||
}
|
||||
|
||||
const getValueItem = (config) => {
|
||||
let item = props.modelValue.find((v) => v.lossTypeId === config.lossTypeId)
|
||||
if (item == null) {
|
||||
item = { ...config }
|
||||
props.modelValue.push(item)
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
const configs = ref([])
|
||||
@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<div class="disaster-form-page">
|
||||
<el-page-header style="margin-bottom: 10px;" @back="router.go(-1)" class="page-header">
|
||||
<template #content>
|
||||
<span class="title">{{ '水毁灾害填报' }}</span>
|
||||
</template>
|
||||
</el-page-header>
|
||||
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="140px" class="disaster-form" @submit.prevent>
|
||||
<!-- 基本信息区块 -->
|
||||
<el-card class="form-section" shadow="never">
|
||||
@ -36,9 +42,8 @@
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="事件类型">
|
||||
<el-select v-model="eventType" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="水毁事件" value="水毁事件" />
|
||||
<el-option label="冰雪事件" value="冰雪事件" />
|
||||
<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>
|
||||
@ -46,12 +51,7 @@
|
||||
<el-col :span="8">
|
||||
<el-form-item label="路况类别" prop="roadConditionType">
|
||||
<el-select v-model="formData.roadConditionType" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="高速公路" value="高速公路" />
|
||||
<el-option label="国道" value="国道" />
|
||||
<el-option label="省道" value="省道" />
|
||||
<el-option label="县道" value="县道" />
|
||||
<el-option label="乡道" value="乡道" />
|
||||
<el-option label="村道" value="村道" />
|
||||
<el-option v-for="(option, idx) in options['waterRoadConditionType']" :label="option.label" :value="option.value" :key="idx" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -59,8 +59,7 @@
|
||||
<el-col :span="8">
|
||||
<el-form-item label="是否阻断" prop="event.isBlocked">
|
||||
<el-select v-model="formData.event.isBlocked" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
<el-option v-for="(option, idx) in options['yesNoBool']" :label="option.label" :value="option.value" :key="idx" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -70,19 +69,15 @@
|
||||
<el-col :span="8">
|
||||
<el-form-item label="抢险进度" prop="event.repairProgress">
|
||||
<el-select v-model="formData.event.repairProgress" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="未抢险" value="未抢险" />
|
||||
<el-option label="抢险中" value="抢险中" />
|
||||
<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="event.disposalMeasures">
|
||||
<el-select v-model="formData.event.disposalMeasures" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="全幅封闭" value="全幅封闭" />
|
||||
<el-option label="半幅封闭" value="半幅封闭" />
|
||||
<el-option label="正常通行" value="正常通行" />
|
||||
<el-option label="限制通行" value="限制通行" />
|
||||
<el-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>
|
||||
@ -126,7 +121,7 @@
|
||||
<el-row :gutter="24">
|
||||
<!-- 现场描述 -->
|
||||
<el-col :span="16">
|
||||
<el-form-item label="现场描述" prop="report.siteDescription">
|
||||
<el-form-item label="现场描述(绕行方案)" prop="report.siteDescription">
|
||||
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="2" placeholder="请填写" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -135,20 +130,19 @@
|
||||
|
||||
<BlockItem title="位置信息">
|
||||
<el-row :gutter="24">
|
||||
<!-- 线路编号 -->
|
||||
<!-- 路线类型 -->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="路线类型" prop="routeNo">
|
||||
<el-input v-model="formData.routeNo" placeholder="请填写" />
|
||||
<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%">
|
||||
<el-option label="万州区" value="万州区" />
|
||||
<el-option label="开州区" value="开州区" />
|
||||
<el-option label="黔江区" value="黔江区" />
|
||||
<el-option label="涪陵区" value="涪陵区" />
|
||||
<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>
|
||||
@ -156,7 +150,7 @@
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="地点路线" prop="routeNo">
|
||||
<el-input v-model="formData.routeNo" placeholder="请填写" />
|
||||
<RoadRoutesSelect v-model="formData.routeNo" @change="handleRouteNoChange" :extra-params="{ xzdj: filterForm.routeType, qxid: formData.event.district }" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 起点桩号 -->
|
||||
@ -193,15 +187,15 @@
|
||||
<el-row :gutter="24">
|
||||
<!-- 经度 -->
|
||||
<el-col :span="8">
|
||||
<el-form-item label="经度" prop="event.startStakeLng">
|
||||
<el-input v-model="formData.event.startStakeLng" placeholder="经度"> </el-input>
|
||||
<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.startStakeLat">
|
||||
<el-input v-model="formData.event.startStakeLat" placeholder="纬度"> </el-input>
|
||||
<el-form-item label="纬度" prop="event.latitude">
|
||||
<el-input v-model="formData.event.latitude" placeholder="纬度"> </el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@ -213,14 +207,19 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="视频上传" prop="fileList">
|
||||
<FileUpload type="video" :limit="9" v-model="formData.fileList" />
|
||||
<FileUpload type="video" :limit="9" v-model="formData.fileList" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</BlockItem>
|
||||
</el-card>
|
||||
|
||||
<el-card title="灾毁损失">
|
||||
<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">
|
||||
<!-- 受伤人员 -->
|
||||
@ -283,9 +282,9 @@
|
||||
<LossList v-model:model-value="formData.report.lossList" />
|
||||
|
||||
<el-row :gutter="24">
|
||||
<!-- 已投入机械 -->
|
||||
<!-- 投入机械 -->
|
||||
<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="请填写">
|
||||
<template #suffix>
|
||||
<span class="unit-text">台/班</span>
|
||||
@ -294,7 +293,7 @@
|
||||
</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="请填写">
|
||||
@ -305,9 +304,9 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 已投入资金 -->
|
||||
<!-- 投入资金 -->
|
||||
<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="请填写">
|
||||
<template #suffix>
|
||||
<span class="unit-text">万元</span>
|
||||
@ -315,13 +314,6 @@
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 其他损失描述 -->
|
||||
<el-col :span="24">
|
||||
<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="恢复重建预估费用">
|
||||
@ -330,14 +322,13 @@
|
||||
<el-col :span="8">
|
||||
<el-form-item label="是否需要恢复重建" prop="event.needsRecovery">
|
||||
<el-select v-model="formData.event.needsRecovery" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
<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="!isContinue">
|
||||
<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>
|
||||
@ -374,9 +365,12 @@ 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)
|
||||
|
||||
@ -392,6 +386,10 @@ const videoFileList = ref([])
|
||||
|
||||
const eventType = ref('水毁事件')
|
||||
|
||||
const filterForm = reactive({
|
||||
routeType: ''
|
||||
})
|
||||
|
||||
const formData = reactive({
|
||||
// 顶层字段
|
||||
occurLocation: null, // 发生地点/路况位置
|
||||
@ -442,6 +440,10 @@ const formData = reactive({
|
||||
fileList: []
|
||||
})
|
||||
|
||||
const handleEventTypeChange = () => {
|
||||
router.replace({ path: '/iceDisasterReport' })
|
||||
}
|
||||
|
||||
// 监听处置措施数组变化,转换为逗号分隔的字符串存到 report.disposalMeasures
|
||||
watch(
|
||||
disposalMeasuresArray,
|
||||
@ -502,14 +504,29 @@ watch(
|
||||
|
||||
// 表单校验规则
|
||||
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.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' }
|
||||
]
|
||||
'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' }
|
||||
// ]
|
||||
}
|
||||
|
||||
// 图片上传前校验
|
||||
@ -561,6 +578,15 @@ 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 }
|
||||
@ -621,8 +647,7 @@ const handleSubmit = async () => {
|
||||
|
||||
// 加载编辑数据
|
||||
const loadEditData = async () => {
|
||||
|
||||
if(route.query.mock) {
|
||||
if (route.query.mock) {
|
||||
initFormData(mockData)
|
||||
} else {
|
||||
initFormData({})
|
||||
@ -630,6 +655,9 @@ const loadEditData = async () => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 获取区县下拉列表
|
||||
getAreaOptions()
|
||||
|
||||
loadEditData()
|
||||
})
|
||||
|
||||
@ -643,7 +671,10 @@ defineExpose({
|
||||
|
||||
<style scoped lang="scss">
|
||||
.disaster-form-page {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.disaster-form {
|
||||
.form-section {
|
||||
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div class="selector-component">
|
||||
<el-input :modelValue="modelValue" :placeholder="placeholder" readonly @click="openDialog">
|
||||
<template #suffix>
|
||||
<el-icon><ArrowDown /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" :width="dialogWidth" :append-to-body="true" :modal="true" @close="handleDialogClose">
|
||||
<div class="dialog-content">
|
||||
<div class="title">已选择地点路线:{{ tempSelectedItem?.routeCode }}</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="routeCode" 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/infrastructure-asset/routes'
|
||||
},
|
||||
// 每页大小
|
||||
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?.routeCode) {
|
||||
tempSelectedItem.value = null
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== API 请求 ====================
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const params = {
|
||||
pageNum: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
lxbh: props.modelValue,
|
||||
xzdj: props.extraParams?.xzdj, // 行政等级
|
||||
qxid: props.extraParams?.qxid // 区县编码
|
||||
}
|
||||
|
||||
// 如果有搜索关键词,添加到参数中
|
||||
if (searchKeyword.value) {
|
||||
params.lxbh = 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?.routeCode || ''
|
||||
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.routeCode
|
||||
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>
|
||||
254
packages/screen/src/views/LedgerManagement/helpLedger/index.js
Normal file
254
packages/screen/src/views/LedgerManagement/helpLedger/index.js
Normal file
@ -0,0 +1,254 @@
|
||||
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
|
||||
import { request } from "@/utils/request";
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const tableData = ref([]); // 表格数据
|
||||
|
||||
const modelVisible = ref(false); // 弹窗状态
|
||||
const drawerVisible = ref(false); // 抽屉状态
|
||||
// 弹窗内容
|
||||
const model = reactive({
|
||||
title: '',
|
||||
content: null,
|
||||
props: {},
|
||||
onCancel: null,
|
||||
onConfirm: null,
|
||||
width: '',
|
||||
});
|
||||
const form = reactive({
|
||||
});
|
||||
const INIT_FORM = {
|
||||
|
||||
};
|
||||
// 抽屉内容
|
||||
const drawer = reactive({
|
||||
title: '',
|
||||
content: null,
|
||||
props: {},
|
||||
onCancel: null,
|
||||
onConfirm: null,
|
||||
direction: 'rtl',
|
||||
size: '50%'
|
||||
});
|
||||
const dialogRef = ref(null); // 弹窗实例
|
||||
const drawerRef = ref(null); // 抽屉实例
|
||||
|
||||
const columns = [
|
||||
{
|
||||
prop: "helpTime",
|
||||
label: "帮扶时间",
|
||||
// formatter: (row) => `${row.helpTime || ''} - ${row.helpTimeEnd || ''}`,
|
||||
},
|
||||
{
|
||||
prop: "helpCounty",
|
||||
label: "帮扶区县",
|
||||
},
|
||||
{
|
||||
prop: "helpPerson",
|
||||
label: "帮扶人员",
|
||||
},
|
||||
{
|
||||
prop: "helpUnit",
|
||||
label: "帮扶单位",
|
||||
},
|
||||
{
|
||||
prop: "helpStatus",
|
||||
label: "帮扶情况",
|
||||
},
|
||||
{
|
||||
prop: "attachmentUrl",
|
||||
label: "附件",
|
||||
render: (row) => {
|
||||
if (row.attachmentUrl) {
|
||||
return h(ElButton, {
|
||||
text: 'true',
|
||||
type: 'primary',
|
||||
onClick: () => downloadAttachment(row.attachmentUrl)
|
||||
}, '附件下载');
|
||||
} else {
|
||||
return h('span', {}, '---');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: "uploadTime",
|
||||
label: "上传时间",
|
||||
},
|
||||
{
|
||||
prop: "remark",
|
||||
label: "备注",
|
||||
},
|
||||
]
|
||||
|
||||
// 过滤条件
|
||||
const filterData = reactive({})
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
pageSizes: [10, 20, 50],
|
||||
layout: "prev, pager, next, jumper",
|
||||
onChange: (page, pageSize) => {
|
||||
pagination.current = page;
|
||||
pagination.pageSize = pageSize;
|
||||
getTableData(filterData);
|
||||
},
|
||||
});
|
||||
|
||||
// 获取列表
|
||||
const getTableData = async (filterData = {}) => {
|
||||
try {
|
||||
// 过滤空字符串属性
|
||||
const filteredParams = {};
|
||||
Object.keys(filterData).forEach(key => {
|
||||
if (filterData[key] !== '' && filterData[key] != null) {
|
||||
filteredParams[key] = filterData[key];
|
||||
}
|
||||
});
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/fgc-xxbf/list',
|
||||
method: "GET",
|
||||
params: {
|
||||
...filteredParams,
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
}
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
tableData.value = res.data.records
|
||||
pagination.total = res.data.total
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message)
|
||||
console.error('获取列表失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 下载附件
|
||||
const downloadAttachment = (url) => {
|
||||
if (!url) {
|
||||
ElMessage.warning('无附件地址');
|
||||
return;
|
||||
}
|
||||
// 直接使用 window.open 触发下载
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
// 预警类型选项
|
||||
const eventTypeOptions = [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "台风预警", value: "台风预警" },
|
||||
{ label: "暴雨预警", value: "暴雨预警" },
|
||||
{ label: "寒潮预警", value: "寒潮预警" },
|
||||
{ label: "大雾预警", value: "大雾预警" },
|
||||
{ label: "暴雪预警", value: "暴雪预警" },
|
||||
{ label: "道路结冰预警", value: "道路结冰预警" },
|
||||
{ label: "雷电预警", value: "雷电预警" },
|
||||
{ label: "强对流预警", value: "强对流预警" },
|
||||
];
|
||||
|
||||
// 预警级别选项
|
||||
const severityOptions = [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "红色预警", value: "红色预警" },
|
||||
{ label: "橙色预警", value: "橙色预警" },
|
||||
{ label: "黄色预警", value: "黄色预警" },
|
||||
{ label: "蓝色预警", value: "蓝色预警" },
|
||||
]
|
||||
|
||||
// 响应情况选项
|
||||
const responseOptions = [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "未响应", value: "未响应" },
|
||||
{ label: "部分响应", value: "部分响应" },
|
||||
{ label: "全部响应", value: "全部响应" },
|
||||
]
|
||||
|
||||
|
||||
const downloadFile = async () => {
|
||||
window.open('/snow-ops-platform/fgc-xxbf/template', '_blank');
|
||||
}
|
||||
|
||||
const uploadFile = async (file) => {
|
||||
if (!file) {
|
||||
ElMessage.warning('请选择要上传的文件');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建FormData对象
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '操作中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
|
||||
// 发送POST请求
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/fgc-xxbf/upload',
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
|
||||
// 关闭加载提示
|
||||
loading.close();
|
||||
|
||||
if (res.code === '00000') {
|
||||
ElMessage.success('文件上传成功');
|
||||
// 刷新表格数据
|
||||
getTableData(filterData);
|
||||
} else {
|
||||
throw new Error(res.message || '上传失败');
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message || '文件上传失败');
|
||||
console.error('文件上传失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default () => {
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
})
|
||||
|
||||
|
||||
watch(filterData, (val) => {
|
||||
getTableData(filterData);
|
||||
}, { deep: true })
|
||||
|
||||
|
||||
return {
|
||||
modelVisible,
|
||||
model,
|
||||
drawerVisible,
|
||||
drawer,
|
||||
dialogRef,
|
||||
drawerRef,
|
||||
|
||||
eventTypeOptions,
|
||||
severityOptions,
|
||||
responseOptions,
|
||||
|
||||
|
||||
tableData,
|
||||
filterData,
|
||||
pagination,
|
||||
columns,
|
||||
|
||||
downloadFile,
|
||||
uploadFile,
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="root">
|
||||
<div class="search-box">
|
||||
<el-input v-model="script.filterData.headline" style="width: 240px; margin-right: 10px" size="large"
|
||||
placeholder="预警标题" :suffix-icon="Search" />
|
||||
<el-select v-model="script.filterData.eventType" style="width: 240px; margin-right: 10px" size="large"
|
||||
placeholder="预警类型" :suffix-icon="ArrowDown" :options="script.eventTypeOptions" clearable />
|
||||
<el-select v-model="script.filterData.severity" style="width: 240px; margin-right: 10px" size="large"
|
||||
placeholder="预警级别" :suffix-icon="ArrowDown" :options="script.severityOptions" clearable />
|
||||
<el-select v-model="script.filterData.responseStatus" style="width: 240px; margin-right: 10px" size="large"
|
||||
placeholder="响应情况" :suffix-icon="ArrowDown" :options="script.responseOptions" clearable />
|
||||
</div>
|
||||
<div class="event-box">
|
||||
<el-button type="primary" @click="triggerFileSelect">上传线下帮扶</el-button>
|
||||
<el-button type="primary" @click="script.downloadFile">线下帮扶模板下载</el-button>
|
||||
<input type="file" ref="fileInput" style="display: none" @change="handleFileSelect" accept=".*"></input>
|
||||
</div>
|
||||
<DynamicTable :dataSource="script.tableData.value" :columns="script.columns" :autoHeight="true"
|
||||
:pagination="script.pagination">
|
||||
|
||||
</DynamicTable>
|
||||
<div class="model-box">
|
||||
<MyDialog v-model="script.modelVisible.value" :title="script.model?.title"
|
||||
:dynamicComponent="script.model?.content" :component-props="script.model?.props"
|
||||
:onConfirm="script.model?.onConfirm" :onCancel="script.model?.onCancel" ref="dialogRef"
|
||||
:width="script.model?.width">
|
||||
</MyDialog>
|
||||
<MyDrawer v-model="script.drawerVisible.value" :title="script.drawer?.title"
|
||||
:dynamicComponent="script.drawer?.content" :component-props="script.drawer?.props"
|
||||
:onConfirm="script.drawer?.onConfirm" :onCancel="script.drawer?.onCancel" ref="drawerRef"
|
||||
:direction="script.drawer?.direction" :size="script.drawer?.size"></MyDrawer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import DynamicTable from "../../../component/DynamicTable";
|
||||
import { Search, ArrowDown, CaretBottom } from "@element-plus/icons-vue";
|
||||
import MyDialog from "../../../component/MyDialog";
|
||||
import MyDrawer from "../../../component/MyDrawer";
|
||||
|
||||
import scriptFn from "./index.js";
|
||||
const script = scriptFn();
|
||||
const { dialogRef, drawerRef } = script;
|
||||
const fileInput = ref(null);
|
||||
|
||||
// 触发文件选择
|
||||
const triggerFileSelect = () => {
|
||||
fileInput.value.click();
|
||||
};
|
||||
|
||||
// 处理文件选择
|
||||
const handleFileSelect = (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
script.uploadFile(file);
|
||||
}
|
||||
// 清空input值,确保相同文件可以重复选择
|
||||
event.target.value = '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.root {
|
||||
height: 100%;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.event-box {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
</style>
|
||||
@ -372,15 +372,15 @@ export default () => {
|
||||
getTableData();
|
||||
})
|
||||
|
||||
// 新增填报
|
||||
const newAdd = () => {
|
||||
router.push({
|
||||
name: 'projectAdd',
|
||||
params: {
|
||||
mode: 'add'
|
||||
// 新增填报
|
||||
const newAdd = () => {
|
||||
router.push({
|
||||
name: 'projectAdd',
|
||||
params: {
|
||||
mode: 'add'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
watch(filterData, (val) => {
|
||||
|
||||
@ -128,6 +128,7 @@ const getTableData = async (filterData = {}) => {
|
||||
...filteredParams,
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
ledgerType: 1, // 1-建设处,2-区县
|
||||
}
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
|
||||
@ -43,6 +43,8 @@ import MyDialog from "../../../component/MyDialog/index.js";
|
||||
import MyDrawer from "../../../component/MyDrawer/index.js";
|
||||
import scriptFn from "./index.js";
|
||||
const script = scriptFn();
|
||||
const { dialogRef, drawerRef } = script;
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@ -128,6 +128,7 @@ const getTableData = async (filterData = {}) => {
|
||||
...filteredParams,
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
ledgerType: 2, // 1-建设处,2-区县
|
||||
}
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
|
||||
@ -40,6 +40,7 @@ import MyDialog from "../../../component/MyDialog/index.js";
|
||||
import MyDrawer from "../../../component/MyDrawer/index.js";
|
||||
import scriptFn from "./index.js";
|
||||
const script = scriptFn();
|
||||
const { dialogRef, drawerRef } = script;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@ -4,206 +4,78 @@
|
||||
style="max-height: 60vh; overflow-y: auto; padding-right: 50px" :rules="rules">
|
||||
<div class="form-part">
|
||||
<el-row>
|
||||
<h4 style="margin: 20px 0;">项目信息</h4>
|
||||
<h4 style="margin: 0 0 20px 0;">气象信息</h4>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预警标题" prop="预警标题">
|
||||
<el-input v-model="form.headline" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="工程状态" prop="工程状态">
|
||||
<el-radio-group v-model="form.zt">
|
||||
<el-radio value="1">在建</el-radio>
|
||||
<el-radio value="2">停工</el-radio>
|
||||
</el-radio-group>
|
||||
<el-form-item label="预警时间" prop="预警时间">
|
||||
<el-col :span="11">
|
||||
<el-date-picker v-model="form.onset" type="date" placeholder="开始时间" style="width: 100%" />
|
||||
</el-col>
|
||||
<el-col :span="2" class="text-center">
|
||||
<span class="text-gray-500">-</span>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<el-date-picker v-model="form.expires" type="date" placeholder="过期时间" style="width: 100%" />
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="所属区县" prop="所属区县">
|
||||
<el-select v-model="qx" filterable remote reserve-keyword clearable placeholder="输入区县名称查询"
|
||||
:remote-method="remoteMethod_qx" :loading="loading" @change="handleSelect_qx" value-key="index">
|
||||
<el-option v-for="(item, index) in qxList" :key="index" :label="item.qxmc" :value="item.qxmc" />
|
||||
<el-col :span="12">
|
||||
<el-form-item label="气象类型" prop="气象类型">
|
||||
<el-select v-model="form.eventType" clearable placeholder="请选择">
|
||||
<el-option v-for="(item, index) in [
|
||||
{ label: '道路结冰', value: '道路结冰' },
|
||||
{ label: '暴雪', value: '暴雪' },
|
||||
{ label: '暴雨', value: '暴雨' },
|
||||
]" :key="index" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="项目名称" prop="项目名称">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="驻地名称" prop="驻地名称">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="驻地类型" prop="驻地类型">
|
||||
<el-select>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="气象来源" prop="气象来源">
|
||||
<el-select v-model="form.weatherSource" clearable placeholder="请选择">
|
||||
<el-option v-for="(item, index) in [
|
||||
{ label: '市级', value: '1' },
|
||||
{ label: '部级', value: '2' },
|
||||
{ label: '区县', value: '3' },
|
||||
]" :key="index" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="坐标点位" prop="坐标点位">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-input aria-label="经度" placeholder="经度" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-input aria-label="纬度" placeholder="纬度" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="所属项目名称:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="项目类型:">
|
||||
<el-select>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预警等级" prop="预警等级">
|
||||
<el-select v-model="form.urgency" clearable placeholder="请选择">
|
||||
<el-option v-for="(item, index) in [
|
||||
{ label: '1级(红色预警)', value: '1' },
|
||||
{ label: '2级(橙色预警)', value: '2' },
|
||||
{ label: '3级(黄色预警)', value: '3' },
|
||||
{ label: '4级(蓝色预警)', value: '4' },
|
||||
]" :key="index" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="建设单位:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="施工单位:">
|
||||
<el-input />
|
||||
<el-col :span="12">
|
||||
<el-form-item label="风险区县" prop="风险区县">
|
||||
<el-select v-model="form.areaCodes" clearable placeholder="请选择" multiple>
|
||||
<el-option v-for="(item, index) in areaList" :key="index" :label="item.qxmc" :value="item.xzdm" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="行政区域:">
|
||||
<el-select></el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="驻地人数:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="驻地风险等级:">
|
||||
<el-select></el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房建类型:">
|
||||
<el-select></el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="搬迁状态:">
|
||||
<el-select></el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="form-part">
|
||||
<el-row>
|
||||
<h4 style="margin: 20px 0;">项目联系人信息</h4>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="吹哨人姓名:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="吹哨人电话:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="建设单位包保责任人姓名:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="建设单位包保责任人电话:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="施工单位包保责任人姓名:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="施工单位包保责任人电话:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="驻地包保责任人姓名:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="驻地包保责任人电话:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="区县级包保责任人姓名:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="区县级包保责任人电话:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="市级包保责任人姓名:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="市级包保责任人电话:">
|
||||
<el-input />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="form-part">
|
||||
<el-row>
|
||||
<h4 style="margin: 20px 0;">驻地情况</h4>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<FileUpload type="image" :limit="9" :fileType=6 />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="form-part">
|
||||
<el-row>
|
||||
<h4 style="margin: 20px 0;">现场情况</h4>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-input></el-input>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
@ -224,169 +96,56 @@ const props = defineProps({
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const sfjwd = ref("是");
|
||||
const qx = ref("");
|
||||
const loading = ref(false);
|
||||
const selectOptions = ref([]);
|
||||
const qxList = ref([]);
|
||||
|
||||
// 根据用户信息 查询角色列表
|
||||
const getUserList = async (key) => {
|
||||
const areaList = ref([])
|
||||
|
||||
// 获取区县列表
|
||||
const getAreaList = async () => {
|
||||
try {
|
||||
const keyword = key;
|
||||
let url = "";
|
||||
if (keyword) {
|
||||
url = `/snow-ops-platform/yhzry/getUserByKey?key=${keyword}`;
|
||||
} else {
|
||||
url = `/snow-ops-platform/yhzry/getUserByKey?key=`;
|
||||
}
|
||||
const res = await request({
|
||||
url: url,
|
||||
method: "GET",
|
||||
});
|
||||
if (res.code === "00000") {
|
||||
return res.data;
|
||||
url: '/snow-ops-platform/infrastructure-asset/counties',
|
||||
method: 'get',
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
areaList.value = res.data
|
||||
} else {
|
||||
throw new Error(res.message);
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message);
|
||||
console.log(error);
|
||||
ElMessage.error(error.message)
|
||||
console.log(error)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 选择人员筛选
|
||||
const remoteMethod = async (query) => {
|
||||
if (query === "") {
|
||||
selectOptions.value = [];
|
||||
return [];
|
||||
}
|
||||
loading.value = true;
|
||||
const res = await getUserList(query);
|
||||
if (res) {
|
||||
selectOptions.value = res;
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
// 根据区县名称 查询区县列表
|
||||
const getQxList = async (key) => {
|
||||
try {
|
||||
const keyword = key;
|
||||
let url = "";
|
||||
if (keyword) {
|
||||
url = `/snow-ops-platform/district/listDistricts?qxmc=${keyword}`;
|
||||
} else {
|
||||
url = `/snow-ops-platform/district/listDistricts?qxmc=`;
|
||||
}
|
||||
const res = await request({
|
||||
url: url,
|
||||
method: "GET",
|
||||
});
|
||||
if (res.code === "00000") {
|
||||
return res.data;
|
||||
} else {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
} catch (error) { }
|
||||
};
|
||||
|
||||
// 选择区县筛选
|
||||
const remoteMethod_qx = async (query) => {
|
||||
loading.value = true;
|
||||
const res = await getQxList(query);
|
||||
if (res) {
|
||||
qxList.value = res;
|
||||
}
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
// 选择区县
|
||||
const handleSelect_qx = (value) => {
|
||||
props.form.qxmc = value;
|
||||
};
|
||||
|
||||
// 选择人员
|
||||
const handleSelect = (value) => {
|
||||
console.log("value", value);
|
||||
props.form.fzrXm = value.realName;
|
||||
props.form.fzrSjhm = value.phone;
|
||||
props.form.fzrUserId = value.userId;
|
||||
};
|
||||
onMounted(() => {
|
||||
getAreaList()
|
||||
})
|
||||
|
||||
const rules = computed(() => {
|
||||
return {
|
||||
mc: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (props.form.mc) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("请输入服务站名称"));
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
qxmc: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (props.form.qxmc) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("请选择所属区县"));
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
fzr: [
|
||||
{
|
||||
required: true,
|
||||
validator: (rule, value, callback) => {
|
||||
if (props.form.fzrUserId && props.form.fzrXm && props.form.fzrSjhm) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("请选择负责人"));
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
jd: [
|
||||
{
|
||||
required: sfjwd.value === "否",
|
||||
validator: (rule, value, callback) => {
|
||||
if (props.form.jd) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("请输入站点经度"));
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
wd: [
|
||||
{
|
||||
required: sfjwd.value === "否",
|
||||
validator: (rule, value, callback) => {
|
||||
if (props.form.wd) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error("请输入站点纬度"));
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
// mc: [
|
||||
// {
|
||||
// required: true,
|
||||
// validator: (rule, value, callback) => {
|
||||
// if (props.form.mc) {
|
||||
// callback();
|
||||
// } else {
|
||||
// callback(new Error("请输入服务站名称"));
|
||||
// }
|
||||
// },
|
||||
// trigger: "blur",
|
||||
// },
|
||||
// ],
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-part{
|
||||
.form-part {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div class="detail-container">
|
||||
<el-form ref="formRef" :model="form" label-position="right" label-width="auto"
|
||||
style="max-height: 60vh; overflow-y: auto; padding-right: 50px" :rules="rules">
|
||||
<div class="form-part">
|
||||
<el-row>
|
||||
<h4 style="margin: 0 0 20px 0;">气象信息</h4>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预警标题" prop="预警标题">
|
||||
<el-input v-model="form.headline" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="预警时间" prop="预警时间">
|
||||
<el-col :span="11">
|
||||
<el-date-picker v-model="form.onset" type="date" placeholder="开始时间" style="width: 100%" />
|
||||
</el-col>
|
||||
<el-col :span="2" class="text-center">
|
||||
<span class="text-gray-500">-</span>
|
||||
</el-col>
|
||||
<el-col :span="11">
|
||||
<el-date-picker v-model="form.expires" type="date" placeholder="过期时间" style="width: 100%" />
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="气象类型" prop="气象类型">
|
||||
<el-select v-model="form.eventType" clearable placeholder="请选择">
|
||||
<el-option v-for="(item, index) in [
|
||||
{ label: '道路结冰', value: '道路结冰' },
|
||||
{ label: '暴雪', value: '暴雪' },
|
||||
{ label: '暴雨', value: '暴雨' },
|
||||
]" :key="index" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="气象来源" prop="气象来源">
|
||||
<el-select v-model="form.weatherSource" clearable placeholder="请选择">
|
||||
<el-option v-for="(item, index) in [
|
||||
{ label: '市级', value: '1' },
|
||||
{ label: '部级', value: '2' },
|
||||
{ label: '区县', value: '3' },
|
||||
]" :key="index" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预警等级" prop="预警等级">
|
||||
<el-select v-model="form.urgency" clearable placeholder="请选择">
|
||||
<el-option v-for="(item, index) in [
|
||||
{ label: '1级(红色预警)', value: '1' },
|
||||
{ label: '2级(橙色预警)', value: '2' },
|
||||
{ label: '3级(黄色预警)', value: '3' },
|
||||
{ label: '4级(蓝色预警)', value: '4' },
|
||||
]" :key="index" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="风险区县" prop="风险区县">
|
||||
<el-select v-model="form.areaCodes" clearable placeholder="请选择" multiple>
|
||||
<el-option v-for="(item, index) in areaList" :key="index" :label="item.qxmc" :value="item.xzdm" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, computed } from "vue";
|
||||
import { request } from "@/utils/request";
|
||||
import FileUpload from '@/component/FileUpload/FileUpload.vue'
|
||||
const formRef = ref(null);
|
||||
defineExpose({ formRef });
|
||||
|
||||
const props = defineProps({
|
||||
form: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const areaList = ref([])
|
||||
|
||||
// 获取区县列表
|
||||
const getAreaList = async () => {
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/infrastructure-asset/counties',
|
||||
method: 'get',
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
areaList.value = res.data
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message)
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getAreaList()
|
||||
})
|
||||
|
||||
const rules = computed(() => {
|
||||
return {
|
||||
// mc: [
|
||||
// {
|
||||
// required: true,
|
||||
// validator: (rule, value, callback) => {
|
||||
// if (props.form.mc) {
|
||||
// callback();
|
||||
// } else {
|
||||
// callback(new Error("请输入服务站名称"));
|
||||
// }
|
||||
// },
|
||||
// trigger: "blur",
|
||||
// },
|
||||
// ],
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-part {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,157 @@
|
||||
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
|
||||
import { request } from "@/utils/request";
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import AddDialog from "./addDialog.vue";
|
||||
|
||||
const tableData = ref([]); // 表格数据
|
||||
const modelVisible = ref(false); // 弹窗状态
|
||||
const drawerVisible = ref(false); // 抽屉状态
|
||||
// 弹窗内容
|
||||
const model = reactive({
|
||||
|
||||
});
|
||||
const form = reactive({
|
||||
|
||||
});
|
||||
const INIT_FORM = {
|
||||
|
||||
};
|
||||
// 抽屉内容
|
||||
const drawer = reactive({
|
||||
title: '',
|
||||
content: null,
|
||||
props: {},
|
||||
onCancel: null,
|
||||
onConfirm: null,
|
||||
direction: 'rtl',
|
||||
size: '50%'
|
||||
});
|
||||
const dialogRef = ref(null); // 弹窗实例
|
||||
const drawerRef = ref(null); // 抽屉实例
|
||||
|
||||
const columns = [
|
||||
{
|
||||
prop: "userCount",
|
||||
label: "值班人数",
|
||||
},
|
||||
{
|
||||
prop: "startTime",
|
||||
label: "开始时间",
|
||||
},
|
||||
{
|
||||
prop: "endTime",
|
||||
label: "结束时间",
|
||||
},
|
||||
{
|
||||
prop: "createTime",
|
||||
label: "排班时间",
|
||||
},
|
||||
{
|
||||
prop: "createBy",
|
||||
label: "排班人",
|
||||
},
|
||||
]
|
||||
|
||||
// 过滤条件
|
||||
const filterData = reactive({})
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
pageSizes: [10, 20, 50],
|
||||
layout: "prev, pager, next, jumper",
|
||||
onChange: (page, pageSize) => {
|
||||
pagination.current = page;
|
||||
pagination.pageSize = pageSize;
|
||||
getTableData(filterData);
|
||||
},
|
||||
});
|
||||
|
||||
// 获取预警列表
|
||||
const getTableData = async (filterData = {}) => {
|
||||
try {
|
||||
// 过滤空字符串属性
|
||||
const filteredParams = {};
|
||||
Object.keys(filterData).forEach(key => {
|
||||
if (filterData[key] !== '' && filterData[key] != null) {
|
||||
filteredParams[key] = filterData[key];
|
||||
}
|
||||
});
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/law-duty/page',
|
||||
method: "GET",
|
||||
params: {
|
||||
dutyStartTime: filteredParams.timeRange?.[0],
|
||||
dutyEndTime: filteredParams.timeRange?.[1],
|
||||
pageNum: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
}
|
||||
})
|
||||
if (res.code === '00000') {
|
||||
tableData.value = res.data.records
|
||||
pagination.total = res.data.total
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取排班列表失败');
|
||||
console.error('获取排班列表失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开发布预警弹窗
|
||||
const openAddDialog = () => {
|
||||
model.title = '发布预警';
|
||||
Object.assign(form, INIT_FORM);
|
||||
model.props = {
|
||||
form: form,
|
||||
};
|
||||
model.content = AddDialog;
|
||||
model.onCancel = () => {
|
||||
modelVisible.value = false;
|
||||
};
|
||||
model.onConfirm = async () => {
|
||||
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(async () => {
|
||||
console.log('@@@@@发布预警', form);
|
||||
// await publishWarning(form)
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error('请处理表单中的错误项');
|
||||
});
|
||||
};
|
||||
model.width = "50%"
|
||||
modelVisible.value = true;
|
||||
}
|
||||
|
||||
export default () => {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(() => {
|
||||
getTableData();
|
||||
})
|
||||
|
||||
|
||||
watch(filterData, (val) => {
|
||||
getTableData(filterData);
|
||||
}, { deep: true })
|
||||
|
||||
|
||||
|
||||
return {
|
||||
tableData,
|
||||
filterData,
|
||||
|
||||
pagination,
|
||||
columns,
|
||||
|
||||
modelVisible,
|
||||
model,
|
||||
drawerVisible,
|
||||
drawer,
|
||||
dialogRef,
|
||||
drawerRef,
|
||||
openAddDialog,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div class="root">
|
||||
<div class="search-box">
|
||||
<el-date-picker v-model="script.filterData.timeRange" type="datetimerange" start-placeholder="值班开始时间"
|
||||
end-placeholder="值班结束时间" style="width: 400px; margin-right: 10px" size="large"
|
||||
format="YYYY-MM-DD HH:mm:ss" date-format="YYYY/MM/DD" time-format="hh:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss" />
|
||||
</div>
|
||||
<div class="event-box">
|
||||
<el-button type="primary" @click="">立即排班</el-button>
|
||||
<!-- <el-button type="primary" @click="script.gotoLedgerPage">驻地台账</el-button> -->
|
||||
<!-- <el-button type="primary" @click="script.openAddDialog">上报项目</el-button> -->
|
||||
</div>
|
||||
<DynamicTable :dataSource="script.tableData.value" :columns="script.columns" :autoHeight="true"
|
||||
:pagination="script.pagination">
|
||||
|
||||
</DynamicTable>
|
||||
<div class="model-box">
|
||||
<MyDialog v-model="script.modelVisible.value" :title="script.model?.title"
|
||||
:dynamicComponent="script.model?.content" :component-props="script.model?.props"
|
||||
:onConfirm="script.model?.onConfirm" :onCancel="script.model?.onCancel" ref="dialogRef"
|
||||
:width="script.model?.width">
|
||||
</MyDialog>
|
||||
<MyDrawer v-model="script.drawerVisible.value" :title="script.drawer?.title"
|
||||
:dynamicComponent="script.drawer?.content" :component-props="script.drawer?.props"
|
||||
:onConfirm="script.drawer?.onConfirm" :onCancel="script.drawer?.onCancel" ref="drawerRef"
|
||||
:direction="script.drawer?.direction" :size="script.drawer?.size">
|
||||
</MyDrawer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import DynamicTable from "@/component/DynamicTable/index.js";
|
||||
import { Search, ArrowDown } from "@element-plus/icons-vue";
|
||||
import MyDialog from "@/component/MyDialog/index.js";
|
||||
import MyDrawer from "@/component/MyDrawer/index.js";
|
||||
import scriptFn from "./index.js";
|
||||
const script = scriptFn();
|
||||
const { dialogRef, drawerRef } = script;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.root {
|
||||
height: 100%;
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.event-box {
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@ -17,9 +17,22 @@ const model = reactive({
|
||||
width: '',
|
||||
});
|
||||
const form = reactive({
|
||||
headline: '',
|
||||
onset: '',
|
||||
expires: '',
|
||||
eventType: '',
|
||||
weatherSource: '',
|
||||
urgency: '',
|
||||
areaCodes: [],
|
||||
});
|
||||
const INIT_FORM = {
|
||||
|
||||
headline: '',
|
||||
onset: '',
|
||||
expires: '',
|
||||
eventType: '',
|
||||
weatherSource: '',
|
||||
urgency: '',
|
||||
areaCodes: [],
|
||||
};
|
||||
// 抽屉内容
|
||||
const drawer = reactive({
|
||||
@ -67,8 +80,24 @@ const columns = [
|
||||
label: "预警结束时间",
|
||||
},
|
||||
{
|
||||
prop: "affectedSiteCount",
|
||||
label: "影响数量",
|
||||
prop: "affectedRoadSectionCount",
|
||||
label: "影响路段",
|
||||
},
|
||||
{
|
||||
prop: "affectedBridgeCount",
|
||||
label: "影响桥梁",
|
||||
},
|
||||
{
|
||||
prop: "affectedTunnelCount",
|
||||
label: "影响隧道",
|
||||
},
|
||||
{
|
||||
prop: "affectedSlopeCount",
|
||||
label: "影响边坡",
|
||||
},
|
||||
{
|
||||
prop: "affectedProjectCount",
|
||||
label: "影响项目",
|
||||
},
|
||||
{
|
||||
prop: "responseStatus",
|
||||
@ -122,7 +151,7 @@ const getTableData = async (filterData = {}) => {
|
||||
}
|
||||
});
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/weatherWarning/response-list',
|
||||
url: '/snow-ops-platform/lawWeatherWarning/response-list',
|
||||
method: "GET",
|
||||
params: {
|
||||
...filteredParams,
|
||||
@ -172,9 +201,9 @@ const responseOptions = [
|
||||
{ label: "全部响应", value: "全部响应" },
|
||||
]
|
||||
|
||||
// 打开填报项目弹窗
|
||||
// 打开发布预警弹窗
|
||||
const openAddDialog = () => {
|
||||
model.title = '填报项目';
|
||||
model.title = '发布预警';
|
||||
Object.assign(form, INIT_FORM);
|
||||
model.props = {
|
||||
form: form,
|
||||
@ -184,18 +213,46 @@ const openAddDialog = () => {
|
||||
modelVisible.value = false;
|
||||
};
|
||||
model.onConfirm = async () => {
|
||||
dialogType.value = '';
|
||||
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(() => {
|
||||
console.log('@@@@@填报项目', form);
|
||||
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(async () => {
|
||||
// console.log('@@@@@发布预警', form);
|
||||
await publishWarning(form)
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error('请处理表单中的错误项');
|
||||
});
|
||||
};
|
||||
model.width = "70%"
|
||||
model.width = "50%"
|
||||
modelVisible.value = true;
|
||||
}
|
||||
|
||||
// 发布预警
|
||||
const publishWarning = async (data) => {
|
||||
try {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '操作中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/lawWeatherWarning/publish',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
loading.close();
|
||||
if (res.code === '00000') {
|
||||
ElMessage.success('操作成功');
|
||||
modelVisible.value = false;
|
||||
getTableData(filterData);
|
||||
|
||||
} else {
|
||||
throw new Error(res.message)
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('操作失败');
|
||||
console.error('操作失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开详情弹窗
|
||||
const openDetailDrawer = (row) => {
|
||||
drawer.title = '预警详情';
|
||||
@ -206,14 +263,65 @@ const openDetailDrawer = (row) => {
|
||||
drawerVisible.value = true;
|
||||
}
|
||||
|
||||
// 上传线下帮扶
|
||||
const uploadFile = async (file) => {
|
||||
if (!file) {
|
||||
ElMessage.warning('请选择要上传的文件');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建FormData对象
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '操作中',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
|
||||
// 发送POST请求
|
||||
const res = await request({
|
||||
url: '/snow-ops-platform/fgc-xxbf/upload',
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
|
||||
// 关闭加载提示
|
||||
loading.close();
|
||||
|
||||
if (res.code === '00000') {
|
||||
ElMessage.success('文件上传成功');
|
||||
} else {
|
||||
throw new Error(res.message || '上传失败');
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error(error.message || '文件上传失败');
|
||||
console.error('文件上传失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export default () => {
|
||||
|
||||
const router = useRouter();
|
||||
// 跳转到台账页面
|
||||
const gotoLedgerPage = () => {
|
||||
router.push({
|
||||
path: '/ledgerManagement2'
|
||||
path: '/ledgerManagement3'
|
||||
});
|
||||
};
|
||||
// 跳转到值班管理
|
||||
const gotoDutyPage = () => {
|
||||
router.push({
|
||||
path: '/dutyManagement'
|
||||
});
|
||||
};
|
||||
|
||||
@ -239,6 +347,7 @@ export default () => {
|
||||
pagination,
|
||||
columns,
|
||||
gotoLedgerPage,
|
||||
gotoDutyPage,
|
||||
|
||||
modelVisible,
|
||||
model,
|
||||
@ -247,5 +356,7 @@ export default () => {
|
||||
dialogRef,
|
||||
drawerRef,
|
||||
openAddDialog,
|
||||
|
||||
uploadFile,
|
||||
}
|
||||
}
|
||||
@ -12,10 +12,13 @@
|
||||
</div>
|
||||
<div class="event-box">
|
||||
<el-button type="primary" @click="">生成报告</el-button>
|
||||
<el-button type="primary" @click="">发布预警</el-button>
|
||||
<el-button type="primary" @click="">上传线下帮扶</el-button>
|
||||
<el-button type="primary" @click="">线下帮扶台账</el-button>
|
||||
<el-button type="primary" @click="script.openAddDialog">发布预警</el-button>
|
||||
<el-button type="primary" @click="triggerFileSelect">上传线下帮扶</el-button>
|
||||
<el-button type="primary" @click="script.gotoLedgerPage">线下帮扶台账</el-button>
|
||||
<el-button type="primary" @click="">立即排班</el-button>
|
||||
<el-button type="primary" @click="script.gotoDutyPage">值班管理</el-button>
|
||||
<el-button type="primary" color="#952DE6" @click="">导出</el-button>
|
||||
<input type="file" ref="fileInput" style="display: none" @change="handleFileSelect" accept=".*"></input>
|
||||
<!-- <el-button type="primary" @click="script.gotoLedgerPage">驻地台账</el-button> -->
|
||||
<!-- <el-button type="primary" @click="script.openAddDialog">上报项目</el-button> -->
|
||||
</div>
|
||||
@ -39,12 +42,30 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import DynamicTable from "../../../component/DynamicTable/index.js";
|
||||
import { Search, ArrowDown } from "@element-plus/icons-vue";
|
||||
import MyDialog from "../../../component/MyDialog/index.js";
|
||||
import MyDrawer from "../../../component/MyDrawer/index.js";
|
||||
import scriptFn from "./index.js";
|
||||
const script = scriptFn();
|
||||
const { dialogRef, drawerRef } = script;
|
||||
const fileInput = ref(null);
|
||||
|
||||
// 触发文件选择
|
||||
const triggerFileSelect = () => {
|
||||
fileInput.value.click();
|
||||
};
|
||||
|
||||
// 处理文件选择
|
||||
const handleFileSelect = (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
script.uploadFile(file);
|
||||
}
|
||||
// 清空input值,确保相同文件可以重复选择
|
||||
event.target.value = '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
100
packages/shared/composables/useOptions.js
Normal file
100
packages/shared/composables/useOptions.js
Normal file
@ -0,0 +1,100 @@
|
||||
import { ref } from 'vue'
|
||||
import { request } from '@shared/utils/request'
|
||||
|
||||
// 通用调用接口方法
|
||||
const getOptionsByApi = async ({ url, params }) => {
|
||||
const res = await request({
|
||||
url,
|
||||
method: 'get',
|
||||
params,
|
||||
timeout: 60000
|
||||
})
|
||||
|
||||
if (res?.code === '00000') return res.data
|
||||
else {
|
||||
console.error('接口调用失败', res)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 公用下拉选项
|
||||
|
||||
export function useOptions() {
|
||||
const options = ref({})
|
||||
|
||||
// 是否 布尔类型
|
||||
options.value['yesNoBool'] = [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false }
|
||||
]
|
||||
|
||||
// 事件类型
|
||||
options.value['eventType'] = [
|
||||
{ label: '水毁事件', value: '水毁事件' },
|
||||
{ label: '冰雪事件', value: '冰雪事件' }
|
||||
]
|
||||
|
||||
// 抢险进度
|
||||
options.value['repairProgress'] = [
|
||||
{ label: '未抢险', value: '未抢险' },
|
||||
{ label: '抢险中', value: '抢险中' }
|
||||
]
|
||||
|
||||
// 处理措施
|
||||
options.value['disposalMeasures'] = [
|
||||
{ label: '全幅封闭', value: '全幅封闭' },
|
||||
{ label: '半幅封闭', value: '半幅封闭' },
|
||||
{ label: '正常通行', value: '正常通行' },
|
||||
{ label: '限制通行', value: '限制通行' }
|
||||
]
|
||||
|
||||
// 路线类型
|
||||
options.value['roadType'] = [
|
||||
{ label: '国道', value: 'G' },
|
||||
{ label: '省道', value: 'S' },
|
||||
{ label: '县道', value: 'X' },
|
||||
{ label: '乡道', value: 'Y' },
|
||||
{ label: '村道', value: 'C' }
|
||||
]
|
||||
|
||||
// 路况类型 水灾 冰灾
|
||||
// 雪灾
|
||||
options.value['iceRoadConditionType'] = [
|
||||
{ label: '积雪', value: '积雪' },
|
||||
{ label: '积冰', value: '积冰' },
|
||||
]
|
||||
// 水灾
|
||||
options.value['waterRoadConditionType'] = [
|
||||
{ label: '山体滑坡', value: '山体滑坡' },
|
||||
{ label: '泥石流', value: '泥石流' },
|
||||
{ label: '边坡坍塌(上、下)', value: '边坡坍塌(上、下)' },
|
||||
{ label: '路基沉降(垮塌)', value: '路基沉降(垮塌)' },
|
||||
{ label: '行道树倒塌', value: '行道树倒塌' },
|
||||
{ label: '积水', value: '积水' },
|
||||
{ label: '其他', value: '其他' }
|
||||
]
|
||||
|
||||
// 获取区县
|
||||
const getAreaOptions = async (params) => {
|
||||
let list = await getOptionsByApi({
|
||||
url: '/snow-ops-platform/infrastructure-asset/counties',
|
||||
params: {
|
||||
xzdm: params?.xzdm // 区县代码 可选
|
||||
}
|
||||
})
|
||||
list = list.filter((item) => {
|
||||
return !!item.qxmc
|
||||
})
|
||||
options.value['area'] = list.map((item) => {
|
||||
return {
|
||||
label: item.qxmc,
|
||||
value: item.xzdm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
options,
|
||||
getAreaOptions,
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user