2026-04-07 13:56:18 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<PageContainer title="灾毁阻断" @click-back="handleClickBack">
|
2026-04-07 14:26:35 +08:00
|
|
|
|
<SearchInput v-model="searchValue" placeholder="请输入地点关键词" @search="handleSearch">
|
|
|
|
|
|
<template #extra>
|
|
|
|
|
|
<!-- 全部按钮(激活状态) -->
|
|
|
|
|
|
<van-button type="default" size="small" :class="{ active: activeFilter === 'all' }" @click="activeFilter = 'all'"> 全部 </van-button>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 筛选按钮(带图标) -->
|
|
|
|
|
|
<van-button type="default" size="small" icon="filter-o" @click="showFilter = true"></van-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</SearchInput>
|
2026-04-07 13:56:18 +08:00
|
|
|
|
|
|
|
|
|
|
<CurrentSite />
|
|
|
|
|
|
|
|
|
|
|
|
<div class="list-panel">
|
2026-04-07 14:26:35 +08:00
|
|
|
|
<CardItem v-for="(item, index) in list" :key="index" :title="item.title" @click="handleClickItem(item)">
|
2026-04-07 13:56:18 +08:00
|
|
|
|
<template #headerExtra>
|
2026-04-07 14:26:35 +08:00
|
|
|
|
<!-- 使用 Vant Tag 组件显示状态 -->
|
|
|
|
|
|
<van-tag :type="item.status === '未解除' ? 'danger' : 'success'" plain size="medium">
|
|
|
|
|
|
{{ item.status }}
|
|
|
|
|
|
</van-tag>
|
2026-04-07 13:56:18 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="info-block">
|
2026-04-07 14:26:35 +08:00
|
|
|
|
<div class="time-box">
|
|
|
|
|
|
<span class="info-label">发生时间:</span>
|
|
|
|
|
|
<span class="info-value">{{ item.occurTime }}</span>
|
2026-04-07 13:56:18 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="time-box">
|
|
|
|
|
|
<span class="info-label">预计恢复时间:</span>
|
|
|
|
|
|
<span class="info-value">{{ item.estimateRecoverTime }}</span>
|
|
|
|
|
|
</div>
|
2026-04-07 14:26:35 +08:00
|
|
|
|
<!-- 使用 Vant Tag 组件显示灾毁类型 -->
|
|
|
|
|
|
<div class="disaster-type-wrapper">
|
|
|
|
|
|
<van-tag type="primary" size="medium" plain>{{ item.disasterType }}</van-tag>
|
|
|
|
|
|
</div>
|
2026-04-07 13:56:18 +08:00
|
|
|
|
</div>
|
2026-04-07 14:26:35 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 使用绝对定位的箭头 -->
|
|
|
|
|
|
<van-icon class="jump-icon-absolute" name="arrow" />
|
2026-04-07 13:56:18 +08:00
|
|
|
|
</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>
|
2026-04-07 14:26:35 +08:00
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
<!-- 灾毁填报按钮 -->
|
2026-04-07 14:26:35 +08:00
|
|
|
|
<van-button type="primary" class="fab-btn" icon="plus" @click="handleAdd"> 冰雪填报 </van-button>
|
2026-04-07 13:56:18 +08:00
|
|
|
|
</PageContainer>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
|
|
import { useRouter } from 'vue-router'
|
2026-04-07 14:26:35 +08:00
|
|
|
|
import { showToast, Tag as VanTag, Loading as VanLoading, Icon as VanIcon, Button as VanButton } from 'vant'
|
2026-04-07 13:56:18 +08:00
|
|
|
|
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'
|
2026-04-07 14:26:35 +08:00
|
|
|
|
import mockDataJSON from './mockData.json'
|
2026-04-07 13:56:18 +08:00
|
|
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
|
|
|
|
|
|
// 搜索关键词
|
|
|
|
|
|
const searchValue = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
// 列表数据
|
|
|
|
|
|
const list = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
// 加载状态
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
// 空状态文本
|
|
|
|
|
|
const emptyText = ref('暂无相关灾毁信息')
|
|
|
|
|
|
|
|
|
|
|
|
// 获取灾毁列表数据(后端接口)
|
|
|
|
|
|
const getDisasterList = async (keyword = '') => {
|
|
|
|
|
|
loading.value = true
|
2026-04-07 14:26:35 +08:00
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
try {
|
|
|
|
|
|
// TODO: 替换为实际的后端接口地址
|
|
|
|
|
|
const response = await fetch('/api/disaster/list', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
keyword: keyword.trim()
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2026-04-07 14:26:35 +08:00
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
// 模拟接口响应格式,实际使用时根据后端返回结构调整
|
|
|
|
|
|
// const result = await response.json()
|
2026-04-07 14:26:35 +08:00
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
// 模拟异步请求
|
|
|
|
|
|
// if (result.code === 200) {
|
|
|
|
|
|
// list.value = result.data
|
|
|
|
|
|
// emptyText.value = keyword ? '未搜索到相关灾毁信息' : '暂无相关灾毁信息'
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// showToast(result.message || '获取数据失败')
|
|
|
|
|
|
// list.value = []
|
|
|
|
|
|
// }
|
2026-04-07 14:26:35 +08:00
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
// ========== 模拟数据(实际开发时删除此部分) ==========
|
2026-04-07 14:26:35 +08:00
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
|
|
|
|
const mockData = mockDataJSON
|
|
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
// 模拟后端搜索过滤(实际由后端完成)
|
|
|
|
|
|
if (keyword) {
|
2026-04-07 14:26:35 +08:00
|
|
|
|
const filtered = mockData.filter((item) => item.title.toLowerCase().includes(keyword.toLowerCase()))
|
2026-04-07 13:56:18 +08:00
|
|
|
|
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,
|
2026-04-07 14:26:35 +08:00
|
|
|
|
estimateRecoverTime: item.estimateRecoverTime,
|
|
|
|
|
|
disasterType: item.disasterType
|
2026-04-07 13:56:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 灾毁填报
|
2026-04-07 14:26:35 +08:00
|
|
|
|
const handleAdd = () => {
|
2026-04-07 13:56:18 +08:00
|
|
|
|
// 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 14:26:35 +08:00
|
|
|
|
/* CardItem 需要设置相对定位 */
|
|
|
|
|
|
:deep(.card-item) {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
.info-block {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
2026-04-07 14:26:35 +08:00
|
|
|
|
padding-right: 24px; /* 为箭头留出空间 */
|
2026-04-07 13:56:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 14:26:35 +08:00
|
|
|
|
/* 使用绝对定位的箭头样式 */
|
|
|
|
|
|
.jump-icon-absolute {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
right: 16px;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
transform: translateY(-50%);
|
2026-04-07 13:56:18 +08:00
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
color: rgba(102, 102, 102, 0.4);
|
2026-04-07 14:26:35 +08:00
|
|
|
|
cursor: pointer;
|
2026-04-07 13:56:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 14:26:35 +08:00
|
|
|
|
/* 灾毁类型标签样式 */
|
|
|
|
|
|
.disaster-type-wrapper {
|
|
|
|
|
|
margin-top: 4px;
|
2026-04-07 13:56:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
z-index: 10;
|
2026-04-07 14:26:35 +08:00
|
|
|
|
|
2026-04-07 13:56:18 +08:00
|
|
|
|
&:active {
|
|
|
|
|
|
opacity: 0.9;
|
|
|
|
|
|
transform: translateX(-50%) scale(0.98);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading-wrapper {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
padding: 40px 0;
|
|
|
|
|
|
}
|
2026-04-07 14:26:35 +08:00
|
|
|
|
</style>
|