This commit is contained in:
fanjia 2026-04-29 10:33:29 +08:00
commit 82e10762a4
14 changed files with 751 additions and 69 deletions

View File

@ -15,6 +15,7 @@
"vant": "^4.9.21",
"@vueuse/core": "^14.0.0",
"dayjs": "^1.11.19",
"leaflet": "^1.9.4",
"axios": "^1.13.2",
"@h5/shared": "workspace:*"
},

View File

@ -14,7 +14,7 @@
/>
<!-- 弹出层同时包含日期和时间选择器 -->
<van-popup v-model:show="showPicker" position="bottom" round>
<van-popup v-model:show="showPicker" position="bottom">
<div class="picker-container">
<div class="picker-header">
<span class="picker-cancel" @click="showPicker = false">取消</span>

View File

@ -14,7 +14,7 @@
/>
<!-- 弹出层选择器 -->
<van-popup v-model:show="showPicker" position="bottom" round>
<van-popup v-model:show="showPicker" position="bottom">
<van-picker
:modelValue="getPickerValue()"
:columns="columns"

View File

@ -6,6 +6,7 @@ import './styles/index.css'
import 'vant/lib/index.css'
import "vant/es/toast/style";
import "vant/es/popup/style";
import "./styles/vant-ui-style.scss"
const app = createApp(App)

View File

@ -0,0 +1 @@
// vant-ui 的样式覆盖均写在这里

View File

@ -24,7 +24,13 @@
/>
<!-- 发生地点 (occurLocation) -->
<van-field v-model="formData.event.occurLocation" label="发生地点" placeholder="请填写" />
<PositionPickerMobile
v-model="formData.event.occurLocation"
label="发生地点"
placeholder="请选择"
:initial-longitude="formData.event.startStakeLng"
:initial-latitude="formData.event.startStakeLat"
/>
<!-- 起点桩号 (event.startStakeNo) -->
<van-field v-model="formData.event.startStakeNo" label="起点桩号(K)" placeholder="请填写" />
@ -58,26 +64,11 @@
</PanelItem>
<PanelItem title="处置情况">
<van-field label="处置措施" label-align="top">
<template #input>
<van-row :gutter="5">
<van-col
v-for="(item, index) in options['iceDisposalMeasures']"
:span="8"
: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>
</template>
</van-field>
<DisposalMeasuresSelector
v-model="formData.report.disposalMeasures"
:options="options['iceDisposalMeasures']"
label="处置措施"
/>
<BaseDatePicker
v-model="formData.report.expectRecoverTime"
@ -164,6 +155,8 @@ import { ref, computed, onMounted } from 'vue';
import PanelItem from '@/components/PanelItem.vue';
import BasePicker from '@/components/BasePicker.vue';
import BaseDatePicker from '@/components/BaseDatePicker.vue';
import DisposalMeasuresSelector from '../components/DisposalMeasuresSelector.vue';
import PositionPickerMobile from '../components/PositionPickerMobile.vue';
import RoadRoutesPicker from '../components/RoadRoutesPicker.vue';
import MaterialPicker from '../components/MaterialPicker.vue';
import { request } from '@shared/utils/request';
@ -293,7 +286,7 @@ const validate = () => {
return false;
}
if (isEmpty(formData.value.event?.occurLocation)) {
showToast('请输入发生地点');
showToast('请选择发生地点');
return false;
}
if (isEmpty(formData.value.event?.disasterMileage)) {

View File

@ -51,7 +51,13 @@
<van-field v-model="formData.event.endStakeLat" label="止点桩纬度" placeholder="请填写" type="number" />
<!-- 路况位置 (occurLocation) -->
<van-field v-model="formData.event.occurLocation" label="路况位置" placeholder="请填写" />
<PositionPickerMobile
v-model="formData.event.occurLocation"
label="路况位置"
placeholder="请选择"
:initial-longitude="formData.event.startStakeLng"
:initial-latitude="formData.event.startStakeLat"
/>
<!-- 阻断点小地名 (event.blockedPointName) -->
<van-field v-model="formData.event.blockedPointName" label="阻断点小地名" placeholder="请填写" />
@ -60,19 +66,11 @@
<!-- 处置情况 (report) -->
<PanelItem title="处置情况">
<van-field label="处置措施" label-align="top">
<template #input>
<div class="measures-options">
<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>
</template>
</van-field>
<DisposalMeasuresSelector
v-model="formData.report.disposalMeasures"
:options="options['disposalMeasures']"
label="处置措施"
/>
<!-- 预计恢复时间 (report.expectRecoverTime) -->
<BaseDatePicker v-model="formData.report.expectRecoverTime" label="预计恢复时间" placeholder="请选择时间" :min-date="minDate" :max-date="maxDate" type="datetime" />
@ -167,6 +165,8 @@ import PanelItem from '@/components/PanelItem.vue'
import BasePicker from '@/components/BasePicker.vue'
import BaseDatePicker from '@/components/BaseDatePicker.vue'
import DisasterFileUpload from '../components/DisasterFileUpload.vue'
import DisposalMeasuresSelector from '../components/DisposalMeasuresSelector.vue'
import PositionPickerMobile from '../components/PositionPickerMobile.vue'
import RoadRoutesPicker from '../components/RoadRoutesPicker.vue'
import LossList from '../components/LossList.vue'
import { useRouter, useRoute } from 'vue-router'
@ -332,7 +332,7 @@ const validate = () => {
return false
}
if (isEmpty(formData.value.event?.occurLocation)) {
showToast('请输入路况位置')
showToast('请选择路况位置')
return false
}
if (isEmpty(formData.value.event?.blockedPointName)) {

View File

@ -0,0 +1,87 @@
<template>
<van-field :label="label" label-align="top">
<template #input>
<div class="measures-selector">
<button
v-for="item in options"
:key="item[valueKey]"
type="button"
class="measure-chip"
:class="{ 'is-active': item[valueKey] === modelValue }"
@click="handleSelect(item)"
>
{{ item[labelKey] }}
</button>
</div>
</template>
</van-field>
</template>
<script setup>
const props = defineProps({
modelValue: {
type: [String, Number, Boolean],
default: ''
},
options: {
type: Array,
default: () => []
},
label: {
type: String,
default: '处置措施'
},
labelKey: {
type: String,
default: 'label'
},
valueKey: {
type: String,
default: 'value'
}
})
const emit = defineEmits(['update:modelValue', 'change'])
const handleSelect = (item) => {
const value = item?.[props.valueKey]
emit('update:modelValue', value)
emit('change', item)
}
</script>
<style scoped lang="scss">
.measures-selector {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
gap: 10px;
width: 100%;
}
.measure-chip {
min-height: 44px;
padding: 10px 12px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: #fff;
color: #323233;
font-size: 14px;
line-height: 1.35;
text-align: center;
word-break: break-word;
transition:
border-color 0.2s ease,
background-color 0.2s ease,
color 0.2s ease,
box-shadow 0.2s ease;
}
.measure-chip.is-active {
border-color: var(--van-primary-color);
color: var(--van-primary-color);
}
.measure-chip:active {
transform: scale(0.98);
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<van-popup v-model:show="showPicker" position="bottom" round>
<van-popup v-model:show="showPicker" position="bottom">
<van-picker :columns="columns" :title="pickerTitle" show-toolbar @confirm="onConfirm" @cancel="showPicker = false" />
</van-popup>
</template>

View File

@ -0,0 +1,568 @@
<template>
<div class="position-picker-mobile">
<van-field
:model-value="modelValue"
is-link
readonly
:label="label"
:placeholder="placeholder"
@click="openPopup"
/>
<van-popup
v-model:show="showPopup"
position="right"
class="position-picker-popup"
@opened="handlePopupOpened"
>
<div class="picker-screen">
<van-nav-bar
title="坐标点选取"
left-text="返回"
left-arrow
fixed
placeholder
@click-left="showPopup = false"
/>
<div class="picker-toolbar">
<div class="coord-grid">
<div class="coord-card">
<span class="coord-label">经度</span>
<span class="coord-value">{{ displayLongitude || '--' }}</span>
</div>
<div class="coord-card">
<span class="coord-label">纬度</span>
<span class="coord-value">{{ displayLatitude || '--' }}</span>
</div>
</div>
<div class="search-row">
<van-search
v-model="searchKeyword"
shape="round"
class="search-box"
:placeholder="searchPlaceholder"
@search="handleSearch"
/>
<van-button
type="primary"
block
class="search-btn"
:loading="searching"
@click="handleSearch"
>
搜索
</van-button>
</div>
<div v-if="draftAddress" class="address-tip">
<span class="address-label">地点</span>
<span class="address-text">{{ draftAddress }}</span>
</div>
</div>
<div class="map-wrapper">
<div ref="mapContainerRef" class="map-container"></div>
<div v-if="mapError" class="map-overlay map-overlay--error">
<p>{{ mapError }}</p>
<van-button type="primary" size="small" @click="retryMapLoad">重新加载</van-button>
</div>
<div v-else-if="!mapReady" class="map-overlay">地图加载中...</div>
</div>
<div class="bottom-bar">
<div class="bottom-tip">点击地图或拖动标记点后再确认位置</div>
<van-button type="primary" block round @click="handleConfirm">确认位置</van-button>
</div>
</div>
</van-popup>
</div>
</template>
<script setup>
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import axios from 'axios'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'
import markerIcon from 'leaflet/dist/images/marker-icon.png'
import markerShadow from 'leaflet/dist/images/marker-shadow.png'
import { showToast } from 'vant'
const AMAP_CONFIG = {
webServiceKey: 'c30e9ebd414fd6a4dfcc1ba8c2060dbb',
baseUrl: 'https://restapi.amap.com',
timeout: 10000
}
const DEFAULT_CENTER = {
longitude: 106.551643,
latitude: 29.563761
}
delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions({
iconRetinaUrl: markerIcon2x,
iconUrl: markerIcon,
shadowUrl: markerShadow
})
const props = defineProps({
modelValue: {
type: String,
default: ''
},
label: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请选择位置'
},
searchPlaceholder: {
type: String,
default: '请输入地点名称'
},
initialLongitude: {
type: [String, Number],
default: null
},
initialLatitude: {
type: [String, Number],
default: null
}
})
const emit = defineEmits(['update:modelValue', 'change'])
const showPopup = ref(false)
const mapReady = ref(false)
const mapError = ref('')
const searching = ref(false)
const searchKeyword = ref('')
const mapContainerRef = ref(null)
const selectedLongitude = ref(null)
const selectedLatitude = ref(null)
const selectedAddress = ref('')
const draftLongitude = ref(null)
const draftLatitude = ref(null)
const draftAddress = ref('')
let mapInstance = null
let markerInstance = null
let reverseGeocodeToken = 0
const normalizeCoordinate = (value) => {
if (value === '' || value === null || value === undefined) {
return null
}
const normalized = Number(value)
return Number.isFinite(normalized) ? normalized : null
}
const displayLongitude = computed(() => {
return draftLongitude.value === null ? '' : draftLongitude.value.toFixed(6)
})
const displayLatitude = computed(() => {
return draftLatitude.value === null ? '' : draftLatitude.value.toFixed(6)
})
const currentCenter = () => {
const longitude = selectedLongitude.value ?? normalizeCoordinate(props.initialLongitude) ?? DEFAULT_CENTER.longitude
const latitude = selectedLatitude.value ?? normalizeCoordinate(props.initialLatitude) ?? DEFAULT_CENTER.latitude
return { longitude, latitude }
}
const resolveInitialSelection = () => {
if (selectedLongitude.value !== null && selectedLatitude.value !== null) {
return {
longitude: selectedLongitude.value,
latitude: selectedLatitude.value
}
}
const longitude = normalizeCoordinate(props.initialLongitude)
const latitude = normalizeCoordinate(props.initialLatitude)
if (longitude !== null && latitude !== null) {
return { longitude, latitude }
}
return null
}
const syncDraftFromSelected = () => {
const initialSelection = resolveInitialSelection()
draftLongitude.value = initialSelection?.longitude ?? null
draftLatitude.value = initialSelection?.latitude ?? null
draftAddress.value = selectedAddress.value || props.modelValue || ''
searchKeyword.value = draftAddress.value
}
const setMarkerPosition = (longitude, latitude, shouldPan = true) => {
if (!mapInstance || longitude === null || latitude === null) {
return
}
const latLng = [latitude, longitude]
if (!markerInstance) {
markerInstance = L.marker(latLng, {
draggable: true
}).addTo(mapInstance)
markerInstance.on('dragend', () => {
const markerLatLng = markerInstance.getLatLng()
updateDraftPosition(markerLatLng.lng, markerLatLng.lat, true)
})
} else {
markerInstance.setLatLng(latLng)
}
if (shouldPan) {
mapInstance.setView(latLng, Math.max(mapInstance.getZoom(), 15))
}
}
const reverseGeocode = async (longitude, latitude) => {
const token = ++reverseGeocodeToken
try {
const response = await axios.get(`${AMAP_CONFIG.baseUrl}/v3/geocode/regeo`, {
params: {
key: AMAP_CONFIG.webServiceKey,
location: `${longitude},${latitude}`,
extensions: 'base',
output: 'json'
},
timeout: AMAP_CONFIG.timeout
})
if (token !== reverseGeocodeToken) {
return
}
if (response.data.status !== '1') {
throw new Error(response.data.info || '逆地理编码失败')
}
const formattedAddress = response.data.regeocode?.formatted_address || ''
draftAddress.value = formattedAddress
searchKeyword.value = formattedAddress
} catch (_error) {
if (token !== reverseGeocodeToken) {
return
}
draftAddress.value = draftAddress.value || `${latitude.toFixed(6)}, ${longitude.toFixed(6)}`
}
}
const updateDraftPosition = (longitude, latitude, shouldResolveAddress = false) => {
draftLongitude.value = Number(longitude.toFixed(6))
draftLatitude.value = Number(latitude.toFixed(6))
setMarkerPosition(draftLongitude.value, draftLatitude.value, false)
if (shouldResolveAddress) {
reverseGeocode(draftLongitude.value, draftLatitude.value)
}
}
const initMap = async () => {
mapError.value = ''
mapReady.value = false
try {
await nextTick()
if (!mapContainerRef.value) {
return
}
const center = currentCenter()
if (!mapInstance) {
mapInstance = L.map(mapContainerRef.value, {
zoomControl: true,
attributionControl: false
}).setView([center.latitude, center.longitude], 13)
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: ['1', '2', '3', '4'],
maxZoom: 18,
minZoom: 3
}).addTo(mapInstance)
mapInstance.on('click', (event) => {
updateDraftPosition(event.latlng.lng, event.latlng.lat, true)
})
} else {
mapInstance.setView([center.latitude, center.longitude], mapInstance.getZoom() || 13)
}
if (draftLongitude.value !== null && draftLatitude.value !== null) {
setMarkerPosition(draftLongitude.value, draftLatitude.value)
} else if (markerInstance) {
markerInstance.remove()
markerInstance = null
}
mapReady.value = true
requestAnimationFrame(() => {
mapInstance?.invalidateSize()
})
if (!draftAddress.value && draftLongitude.value !== null && draftLatitude.value !== null) {
reverseGeocode(draftLongitude.value, draftLatitude.value)
}
} catch (error) {
console.error('地图初始化失败:', error)
mapError.value = '地图初始化失败,请稍后重试'
}
}
const openPopup = () => {
syncDraftFromSelected()
showPopup.value = true
}
const handlePopupOpened = async () => {
await initMap()
requestAnimationFrame(() => {
mapInstance?.invalidateSize()
})
}
const handleSearch = async () => {
const keyword = searchKeyword.value.trim()
if (!keyword) {
showToast('请输入地点名称')
return
}
searching.value = true
try {
const response = await axios.get(`${AMAP_CONFIG.baseUrl}/v3/geocode/geo`, {
params: {
key: AMAP_CONFIG.webServiceKey,
address: keyword,
output: 'json'
},
timeout: AMAP_CONFIG.timeout
})
if (response.data.status !== '1') {
throw new Error(response.data.info || '地点搜索失败')
}
const geocode = response.data.geocodes?.[0]
if (!geocode?.location) {
throw new Error('未找到对应地点')
}
const [longitude, latitude] = geocode.location.split(',').map(Number)
draftAddress.value = geocode.formatted_address || keyword
searchKeyword.value = draftAddress.value
updateDraftPosition(longitude, latitude)
setMarkerPosition(longitude, latitude)
} catch (error) {
console.error('地点搜索失败:', error)
showToast(error.message || '地点搜索失败,请重试')
} finally {
searching.value = false
}
}
const retryMapLoad = () => {
initMap()
}
const handleConfirm = () => {
if (draftLongitude.value === null || draftLatitude.value === null) {
showToast('请先在地图上选择位置')
return
}
selectedLongitude.value = draftLongitude.value
selectedLatitude.value = draftLatitude.value
selectedAddress.value = draftAddress.value || searchKeyword.value.trim() || `${draftLatitude.value}, ${draftLongitude.value}`
emit('update:modelValue', selectedAddress.value)
emit('change', {
longitude: selectedLongitude.value,
latitude: selectedLatitude.value,
address: selectedAddress.value
})
showPopup.value = false
}
watch(
() => props.modelValue,
(value) => {
if (!value) {
return
}
selectedAddress.value = value
if (!showPopup.value) {
draftAddress.value = value
}
},
{ immediate: true }
)
onBeforeUnmount(() => {
if (mapInstance) {
mapInstance.remove()
mapInstance = null
}
})
</script>
<style scoped lang="scss">
.position-picker-mobile {
width: 100%;
}
.position-picker-popup {
width: 100%;
height: 100%;
background: #f7f8fa;
}
.picker-screen {
display: flex;
flex-direction: column;
height: 100%;
}
.picker-toolbar {
padding: 12px 12px 10px;
background: #fff;
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.06);
z-index: 2;
}
.coord-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
margin-bottom: 12px;
}
.coord-card {
display: flex;
flex-direction: column;
gap: 6px;
padding: 12px;
border-radius: 12px;
background: linear-gradient(180deg, #f8fbff 0%, #eef4ff 100%);
}
.coord-label {
color: #6b7280;
font-size: 12px;
}
.coord-value {
color: #1f2937;
font-size: 16px;
font-weight: 600;
word-break: break-all;
}
.search-row {
display: flex;
align-items: center;
gap: 10px;
}
.search-box {
flex: 1;
padding: 0;
background: transparent;
}
.search-btn {
width: 88px;
flex-shrink: 0;
}
.address-tip {
display: flex;
gap: 8px;
margin-top: 10px;
color: #4b5563;
font-size: 12px;
line-height: 1.5;
}
.address-label {
flex-shrink: 0;
color: #2563eb;
font-weight: 600;
}
.address-text {
word-break: break-all;
}
.map-wrapper {
position: relative;
flex: 1;
min-height: 280px;
background: #eef2f7;
}
.map-container {
width: 100%;
height: 100%;
}
.map-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
color: #606266;
font-size: 14px;
background: rgba(247, 248, 250, 0.9);
z-index: 2;
}
.map-overlay--error {
flex-direction: column;
gap: 12px;
color: #ee0a24;
}
.bottom-bar {
padding: 10px 12px calc(12px + env(safe-area-inset-bottom));
background: #fff;
box-shadow: 0 -6px 18px rgba(15, 23, 42, 0.08);
}
.bottom-tip {
margin-bottom: 10px;
color: #86909c;
font-size: 12px;
text-align: center;
}
:deep(.van-nav-bar) {
flex-shrink: 0;
}
:deep(.van-nav-bar__content) {
background: #fff;
}
:deep(.leaflet-container) {
font-family: inherit;
}
</style>

View File

@ -17,6 +17,7 @@
"cesium": "^1.135.0",
"echarts": "^6.0.0",
"element-plus": "^2.11.5",
"leaflet": "^1.9.4",
"pinia": "^3.0.3",
"vue": "^3.5.18",
"vue-echarts": "^8.0.1",

View File

@ -463,7 +463,15 @@ onMounted(() => {
}
.file-list {
width: 260px;
max-width: 260px;
flex-shrink: 0;
}
:deep(.upload-block) {
display: flex;
justify-content: flex-end;
}
:deep(.preview-block .preview-list) {
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -9,7 +9,7 @@
</template>
<!-- 所有表单项合并到一个区域每行一个 -->
<el-form :model="formData" >
<el-form :model="formData">
<BlockItem title="处置情况">
<!-- 处置措施 -->
<el-form-item label="处置措施" required>
@ -121,7 +121,7 @@
<!-- 现场描述 -->
<el-form-item label="现场情况描述">
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="2" placeholder="请填写" />
<el-input v-model="formData.report.siteDescription" type="textarea" :rows="2" placeholder="请填写" />
</el-form-item>
<!-- 是否需要恢复重建 -->
@ -132,6 +132,13 @@
</el-radio-group>
</el-form-item> -->
<el-form-item label="附件">
<div class="upload-wrapper">
<FileUpload v-model="formData.fileList" type="image" :limit="6" />
<div class="upload-tip">只能上传jpg/png文件且不超过500kb 最多上传6张照片</div>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleAddSubmit">追加记录</el-button>
</el-form-item>
@ -147,14 +154,15 @@ import { request } from '@shared/utils/request'
import BlockItem from '@/component/BlockItem.vue'
import NumberInput from '@/component/NumberInput/NumberInput.vue'
import LossList from '../WaterDisasterReport/WaterDisasterLossListPC.vue'
import FileUpload from '@/component/FileUpload/FileUpload.vue'
import { useRouter } from 'vue-router'
// Props
const props = defineProps({
value: {
type: Object,
default: () => ({})
}
default: () => ({}),
},
})
// Emits
@ -163,12 +171,10 @@ const emit = defineEmits(['input', 'change'])
const router = useRouter()
const createDefaultFormData = () => ({
event: {
},
report: {
},
event: {},
report: {},
lossList: [],
fileList: []
fileList: [],
})
//
@ -181,25 +187,25 @@ const roadConditionOptions = [
{ label: '省道', value: '省道' },
{ label: '县道', value: '县道' },
{ label: '乡道', value: '乡道' },
{ label: '村道', value: '村道' }
{ label: '村道', value: '村道' },
]
const blockedOptions = [
{ label: '是', value: true },
{ label: '否', value: false }
{ label: '否', value: false },
]
const repairProgressOptions = [
{ label: '未抢险', value: '未抢险' },
{ 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)
}
},
}
//
@ -210,7 +216,7 @@ watch(
initFormData(newVal)
}
},
{ immediate: true, deep: true }
{ immediate: true, deep: true },
)
//
@ -220,7 +226,7 @@ watch(
emit('input', newVal)
emit('change', newVal)
},
{ deep: true }
{ deep: true },
)
//
@ -276,7 +282,7 @@ const handleAddSubmit = async () => {
//
const submitData = {
...formData.value
...formData.value,
//
}
@ -288,7 +294,7 @@ const handleAddSubmit = async () => {
const res = await request({
url: apiUrl,
method: 'post',
data: submitData
data: submitData,
})
if (res?.code === '00000') {
@ -316,7 +322,7 @@ defineExpose({
initFormData,
getFormData,
resetForm,
calibrateTime
calibrateTime,
})
</script>
@ -358,4 +364,10 @@ defineExpose({
}
}
}
.upload-tip {
margin-top: 8px;
color: #909399;
font-size: 12px;
line-height: 1.6;
}
</style>

View File

@ -38,14 +38,16 @@
</div>
<div class="content-wrapper">
<div class="basic-info-wrapper">
<el-row :gutter="24" class="report-info-row">
<el-col v-for="item in getReportInfoItems(report)" :key="item.label" :span="24" class="info-column">
<div class="info-item">
<span class="info-label">{{ item.label }}</span>
<span class="info-value">{{ item.value }}</span>
</div>
</el-col>
<div>
<el-row :gutter="24" class="report-info-row">
<el-col v-for="item in getReportInfoItems(report)" :key="item.label" :span="24" class="info-column">
<div class="info-item">
<span class="info-label">{{ item.label }}</span>
<span class="info-value">{{ item.value }}</span>
</div>
</el-col>
</el-row>
</div>
<div class="file-list">
<FileUpload v-model="report.fileList" :readonly="!isEdit" />
</div>
@ -252,9 +254,9 @@ const getDisasterDetail = async () => {
if (isEdit.value) {
const newFormData = {
...data,
lossList: null,
lossList: [],
report: {},
fileList: null
fileList: []
}
nextTick(() => {
continueReport.value?.initFormData(newFormData)
@ -402,7 +404,15 @@ onMounted(() => {
margin-bottom: -10px;
}
.file-list {
width: 320px;
max-width: 320px;
flex-shrink: 0;
}
:deep(.upload-block) {
display: flex;
justify-content: flex-end;
}
:deep(.preview-block .preview-list) {
display: flex;
justify-content: flex-end;
}
</style>