feat: 灾害管理

This commit is contained in:
niedongsheng 2026-04-07 13:56:18 +08:00
parent ea99d329e8
commit f0fd858f2c

View File

@ -0,0 +1,290 @@
<template>
<PageContainer title="灾毁阻断" @click-back="handleClickBack">
<SearchInput v-model="searchValue" placeholder="请输入地点关键词" @search="handleSearch" />
<CurrentSite />
<div class="list-panel">
<CardItem
v-for="(item, index) in list"
:key="index"
:title="item.title"
@click="handleClickItem(item)"
>
<template #headerExtra>
<span :class="['status-ball', item.status === '未解除' ? 'active' : 'resolved']"></span>
</template>
<div class="info-block">
<div class="time-row">
<div class="time-box">
<span class="info-label">发生时间</span>
<span class="info-value">{{ item.occurTime }}</span>
</div>
<van-icon class="jump-icon" name="arrow" />
</div>
<div class="time-box">
<span class="info-label">预计恢复时间</span>
<span class="info-value">{{ item.estimateRecoverTime }}</span>
</div>
</div>
</CardItem>
<!-- 加载状态 -->
<div v-if="loading" class="loading-wrapper">
<van-loading size="24px" vertical>加载中...</van-loading>
</div>
<!-- 空状态提示 -->
<EmptyBox v-if="!loading && list.length === 0" :placeholder="emptyText" />
</div>
<!-- 灾毁填报按钮 -->
<van-button type="primary" class="fab-btn" icon="plus" @click="handleAdd">
冰雪填报
</van-button>
</PageContainer>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { showToast } from 'vant'
import PageContainer from '@/components/PageContainer.vue'
import SearchInput from '@/components/SearchInput.vue'
import CardItem from '@/components/CardItem.vue'
import EmptyBox from '@/components/EmptyBox.vue'
import CurrentSite from '@/components/CurrentSite.vue'
const router = useRouter()
//
const searchValue = ref('')
//
const list = ref([])
//
const loading = ref(false)
//
const emptyText = ref('暂无相关灾毁信息')
//
const getDisasterList = async (keyword = '') => {
loading.value = true
try {
// TODO:
const response = await fetch('/api/disaster/list', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
keyword: keyword.trim()
})
})
// 使
// const result = await response.json()
//
// if (result.code === 200) {
// list.value = result.data
// emptyText.value = keyword ? '' : ''
// } else {
// showToast(result.message || '')
// list.value = []
// }
// ========== ==========
await new Promise(resolve => setTimeout(resolve, 500))
const mockData = [
{
id: 1,
title: 'G242金铃乡老窖坪发生积雪',
status: '未解除',
occurTime: '2025/10/10 20:29',
estimateRecoverTime: '2025/10/10 20:29'
},
{
id: 2,
title: 'S521白鹿镇X发生边坡坍塌',
status: '已解除',
occurTime: '2025/10/10 20:29',
estimateRecoverTime: '2025/10/10 20:29'
},
{
id: 3,
title: '彭水S523发生边坡坍塌',
status: '未解除',
occurTime: '2025/10/10 20:29',
estimateRecoverTime: '2025/10/10 20:29'
},
{
id: 4,
title: '梁平蟠龙镇G318发生山体滑坡',
status: '已解除',
occurTime: '2025/10/10 20:29',
estimateRecoverTime: '2025/10/10 20:29'
},
{
id: 5,
title: '重庆市大足区XX县G201行道树倒塌',
status: '已解除',
occurTime: '2025/10/10 20:29',
estimateRecoverTime: '2025/10/10 20:29'
}
]
//
if (keyword) {
const filtered = mockData.filter(item =>
item.title.toLowerCase().includes(keyword.toLowerCase())
)
list.value = filtered
emptyText.value = filtered.length === 0 ? '未搜索到相关灾毁信息' : '暂无相关灾毁信息'
} else {
list.value = mockData
emptyText.value = '暂无相关灾毁信息'
}
// ========== ==========
} catch (error) {
console.error('获取灾毁列表失败:', error)
showToast('获取数据失败,请稍后重试')
list.value = []
} finally {
loading.value = false
}
}
//
const handleSearch = () => {
getDisasterList(searchValue.value)
}
//
const handleClickBack = () => {
router.push('/')
}
//
const handleClickItem = (item) => {
router.push({
path: '/disaster-detail',
query: {
id: item.id,
title: item.title,
status: item.status,
occurTime: item.occurTime,
estimateRecoverTime: item.estimateRecoverTime
}
})
}
//
const handleFillReport = () => {
// TODO:
console.log('点击灾毁填报')
// router.push('/disaster-report')
}
//
onMounted(() => {
getDisasterList()
})
</script>
<style lang="scss" scoped>
.list-panel {
display: flex;
flex-direction: column;
gap: 8px;
padding-bottom: 80px;
}
.info-block {
display: flex;
flex-direction: column;
gap: 8px;
}
.time-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.time-box {
display: flex;
align-items: baseline;
flex-wrap: wrap;
}
.info-label {
font-weight: 400;
font-size: 13px;
color: #999999;
}
.info-value {
font-weight: 400;
font-size: 14px;
color: #333333;
}
.jump-icon {
font-size: 16px;
color: rgba(102, 102, 102, 0.4);
}
.status-ball {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 100%;
margin-right: 6px;
&.active {
background: linear-gradient(180deg, #fd4646 0%, #fb2222 100%);
box-shadow: 0px 1px 4px 0px rgba(91, 8, 8, 0.34);
}
&.resolved {
background: #52C41A;
box-shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.1);
}
}
.fab-btn {
position: fixed;
bottom: 30px;
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;
color: white;
font-size: 16px;
cursor: pointer;
z-index: 10;
&:active {
opacity: 0.9;
transform: translateX(-50%) scale(0.98);
}
}
.loading-wrapper {
display: flex;
justify-content: center;
padding: 40px 0;
}
</style>