feat: 灾害管理与部分组件

This commit is contained in:
niedongsheng 2026-04-07 14:26:35 +08:00
parent a5b7b80404
commit 4d23626371
5 changed files with 154 additions and 123 deletions

View File

@ -3,18 +3,26 @@
<div class="input-wrapper"> <div class="input-wrapper">
<div class="input-block"> <div class="input-block">
<van-icon class="search-icon" name="search" /> <van-icon class="search-icon" name="search" />
<input class="inner-input" v-model="modelValue" :placeholder="placeholder" /> <input
<van-icon class="close-icon" name="clear" v-if="modelValue !== ''" @click="modelValue = ''" /> class="inner-input"
v-model="modelValue"
:placeholder="placeholder"
/>
<van-icon
class="close-icon"
name="clear"
v-if="modelValue !== ''"
@click="clearInput"
/>
</div>
<!-- 右侧插槽用于放置全部和筛选按钮 -->
<div class="slot-wrapper" v-if="$slots.extra">
<slot name="extra"></slot>
</div> </div>
<!-- 占位符 -->
<!-- <div class="placeholder-block" v-if="modelValue === '111'">
<van-icon class="search-icon" name="search" />
<span class="placeholder-text">{{ placeholder }}</span>
</div> -->
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
@ -26,7 +34,13 @@ const props = defineProps({
default: '请输入关键词' default: '请输入关键词'
} }
}) })
//
const clearInput = () => {
modelValue.value = ''
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.search-input { .search-input {
position: relative; position: relative;
@ -37,19 +51,23 @@ const props = defineProps({
} }
.input-wrapper { .input-wrapper {
display: flex;
align-items: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #fff; background-color: #fff;
border-radius: 4px; border-radius: 4px;
gap: 12px; //
padding-right: 12px; //
box-sizing: border-box;
} }
.input-block { .input-block {
position: relative; position: relative;
flex: 1;
height: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
width: 100%;
height: 100%;
padding: 0 20px;
box-sizing: border-box; box-sizing: border-box;
.search-icon, .close-icon { .search-icon, .close-icon {
@ -59,10 +77,18 @@ const props = defineProps({
transform: translateY(-50%); transform: translateY(-50%);
font-size: 20px; font-size: 20px;
color: #9b9b9b; color: #9b9b9b;
pointer-events: none; //
} }
.close-icon { .close-icon {
left: unset; left: unset;
right: 20px; right: 20px;
pointer-events: auto; //
cursor: pointer;
&:active {
opacity: 0.6;
}
} }
} }
@ -71,28 +97,17 @@ const props = defineProps({
border: none; border: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
width: 100%;
height: 100%; height: 100%;
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
background: transparent;
} }
.placeholder-block { .slot-wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex; display: flex;
align-items: center; align-items: center;
pointer-events: none; gap: 8px; //
flex-shrink: 0; //
.search-icon {
font-size: 20px;
margin-right: 3px;
color: #9b9b9b;
}
.placeholder-text {
color: #9b9b9b;
}
} }
</style> </style>

View File

@ -80,6 +80,11 @@ const routes = [
path: '/warningMessageHandle', path: '/warningMessageHandle',
name: 'WarningMessageHandle', name: 'WarningMessageHandle',
component: () => import('../views/WarningMessage/WarningMessageHandle.vue') component: () => import('../views/WarningMessage/WarningMessageHandle.vue')
},
{
path: '/disasterManagement',
name: 'DisasterManagement',
component: () => import('../views/DisasterManagement/DisasterManagement.vue')
} }
] ]

View File

@ -1,33 +1,43 @@
<template> <template>
<PageContainer title="灾毁阻断" @click-back="handleClickBack"> <PageContainer title="灾毁阻断" @click-back="handleClickBack">
<SearchInput v-model="searchValue" placeholder="请输入地点关键词" @search="handleSearch" /> <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>
<CurrentSite /> <CurrentSite />
<div class="list-panel"> <div class="list-panel">
<CardItem <CardItem v-for="(item, index) in list" :key="index" :title="item.title" @click="handleClickItem(item)">
v-for="(item, index) in list"
:key="index"
:title="item.title"
@click="handleClickItem(item)"
>
<template #headerExtra> <template #headerExtra>
<span :class="['status-ball', item.status === '未解除' ? 'active' : 'resolved']"></span> <!-- 使用 Vant Tag 组件显示状态 -->
<van-tag :type="item.status === '未解除' ? 'danger' : 'success'" plain size="medium">
{{ item.status }}
</van-tag>
</template> </template>
<div class="info-block"> <div class="info-block">
<div class="time-row"> <div class="time-box">
<div class="time-box"> <span class="info-label">发生时间</span>
<span class="info-label">发生时间</span> <span class="info-value">{{ item.occurTime }}</span>
<span class="info-value">{{ item.occurTime }}</span>
</div>
<van-icon class="jump-icon" name="arrow" />
</div> </div>
<div class="time-box"> <div class="time-box">
<span class="info-label">预计恢复时间</span> <span class="info-label">预计恢复时间</span>
<span class="info-value">{{ item.estimateRecoverTime }}</span> <span class="info-value">{{ item.estimateRecoverTime }}</span>
</div> </div>
<!-- 使用 Vant Tag 组件显示灾毁类型 -->
<div class="disaster-type-wrapper">
<van-tag type="primary" size="medium" plain>{{ item.disasterType }}</van-tag>
</div>
</div> </div>
<!-- 使用绝对定位的箭头 -->
<van-icon class="jump-icon-absolute" name="arrow" />
</CardItem> </CardItem>
<!-- 加载状态 --> <!-- 加载状态 -->
@ -38,23 +48,22 @@
<!-- 空状态提示 --> <!-- 空状态提示 -->
<EmptyBox v-if="!loading && list.length === 0" :placeholder="emptyText" /> <EmptyBox v-if="!loading && list.length === 0" :placeholder="emptyText" />
</div> </div>
<!-- 灾毁填报按钮 --> <!-- 灾毁填报按钮 -->
<van-button type="primary" class="fab-btn" icon="plus" @click="handleAdd"> <van-button type="primary" class="fab-btn" icon="plus" @click="handleAdd"> 冰雪填报 </van-button>
冰雪填报
</van-button>
</PageContainer> </PageContainer>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { showToast } from 'vant' import { showToast, Tag as VanTag, Loading as VanLoading, Icon as VanIcon, Button as VanButton } from 'vant'
import PageContainer from '@/components/PageContainer.vue' import PageContainer from '@/components/PageContainer.vue'
import SearchInput from '@/components/SearchInput.vue' import SearchInput from '@/components/SearchInput.vue'
import CardItem from '@/components/CardItem.vue' import CardItem from '@/components/CardItem.vue'
import EmptyBox from '@/components/EmptyBox.vue' import EmptyBox from '@/components/EmptyBox.vue'
import CurrentSite from '@/components/CurrentSite.vue' import CurrentSite from '@/components/CurrentSite.vue'
import mockDataJSON from './mockData.json'
const router = useRouter() const router = useRouter()
@ -73,7 +82,7 @@ const emptyText = ref('暂无相关灾毁信息')
// //
const getDisasterList = async (keyword = '') => { const getDisasterList = async (keyword = '') => {
loading.value = true loading.value = true
try { try {
// TODO: // TODO:
const response = await fetch('/api/disaster/list', { const response = await fetch('/api/disaster/list', {
@ -85,10 +94,10 @@ const getDisasterList = async (keyword = '') => {
keyword: keyword.trim() keyword: keyword.trim()
}) })
}) })
// 使 // 使
// const result = await response.json() // const result = await response.json()
// //
// if (result.code === 200) { // if (result.code === 200) {
// list.value = result.data // list.value = result.data
@ -97,52 +106,14 @@ const getDisasterList = async (keyword = '') => {
// showToast(result.message || '') // showToast(result.message || '')
// list.value = [] // list.value = []
// } // }
// ========== ========== // ========== ==========
await new Promise(resolve => setTimeout(resolve, 500)) await new Promise((resolve) => setTimeout(resolve, 500))
const mockData = [ const mockData = mockDataJSON
{
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) { if (keyword) {
const filtered = mockData.filter(item => const filtered = mockData.filter((item) => item.title.toLowerCase().includes(keyword.toLowerCase()))
item.title.toLowerCase().includes(keyword.toLowerCase())
)
list.value = filtered list.value = filtered
emptyText.value = filtered.length === 0 ? '未搜索到相关灾毁信息' : '暂无相关灾毁信息' emptyText.value = filtered.length === 0 ? '未搜索到相关灾毁信息' : '暂无相关灾毁信息'
} else { } else {
@ -150,7 +121,6 @@ const getDisasterList = async (keyword = '') => {
emptyText.value = '暂无相关灾毁信息' emptyText.value = '暂无相关灾毁信息'
} }
// ========== ========== // ========== ==========
} catch (error) { } catch (error) {
console.error('获取灾毁列表失败:', error) console.error('获取灾毁列表失败:', error)
showToast('获取数据失败,请稍后重试') showToast('获取数据失败,请稍后重试')
@ -179,13 +149,14 @@ const handleClickItem = (item) => {
title: item.title, title: item.title,
status: item.status, status: item.status,
occurTime: item.occurTime, occurTime: item.occurTime,
estimateRecoverTime: item.estimateRecoverTime estimateRecoverTime: item.estimateRecoverTime,
disasterType: item.disasterType
} }
}) })
} }
// //
const handleFillReport = () => { const handleAdd = () => {
// TODO: // TODO:
console.log('点击灾毁填报') console.log('点击灾毁填报')
// router.push('/disaster-report') // router.push('/disaster-report')
@ -198,7 +169,6 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.list-panel { .list-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -206,16 +176,16 @@ onMounted(() => {
padding-bottom: 80px; padding-bottom: 80px;
} }
/* CardItem 需要设置相对定位 */
:deep(.card-item) {
position: relative;
}
.info-block { .info-block {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
} padding-right: 24px; /* 为箭头留出空间 */
.time-row {
display: flex;
align-items: center;
justify-content: space-between;
} }
.time-box { .time-box {
@ -236,27 +206,20 @@ onMounted(() => {
color: #333333; color: #333333;
} }
.jump-icon { /* 使用绝对定位的箭头样式 */
.jump-icon-absolute {
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
font-size: 16px; font-size: 16px;
color: rgba(102, 102, 102, 0.4); color: rgba(102, 102, 102, 0.4);
cursor: pointer;
} }
.status-ball { /* 灾毁类型标签样式 */
display: inline-block; .disaster-type-wrapper {
width: 10px; margin-top: 4px;
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 { .fab-btn {
@ -271,11 +234,10 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: white;
font-size: 16px; font-size: 16px;
cursor: pointer; cursor: pointer;
z-index: 10; z-index: 10;
&:active { &:active {
opacity: 0.9; opacity: 0.9;
transform: translateX(-50%) scale(0.98); transform: translateX(-50%) scale(0.98);
@ -287,4 +249,4 @@ onMounted(() => {
justify-content: center; justify-content: center;
padding: 40px 0; padding: 40px 0;
} }
</style> </style>

View File

@ -0,0 +1,42 @@
[
{
"id": 1,
"title": "G242金铃乡老窖坪发生积雪",
"status": "未解除",
"occurTime": "2025/10/10 20:29",
"estimateRecoverTime": "2025/10/10 20:29",
"disasterType": "积雪"
},
{
"id": 2,
"title": "S521白鹿镇X发生边坡坍塌",
"status": "已解除",
"occurTime": "2025/10/10 20:29",
"estimateRecoverTime": "2025/10/10 20:29",
"disasterType": "边坡坍塌"
},
{
"id": 3,
"title": "彭水S523发生边坡坍塌",
"status": "未解除",
"occurTime": "2025/10/10 20:29",
"estimateRecoverTime": "2025/10/10 20:29",
"disasterType": "路基沉陷"
},
{
"id": 4,
"title": "梁平蟠龙镇G318发生山体滑坡",
"status": "已解除",
"occurTime": "2025/10/10 20:29",
"estimateRecoverTime": "2025/10/10 20:29",
"disasterType": "山体滑坡"
},
{
"id": 5,
"title": "重庆市大足区XX县G201行道树倒塌",
"status": "已解除",
"occurTime": "2025/10/10 20:29",
"estimateRecoverTime": "2025/10/10 20:29",
"disasterType": "行道树倒塌"
}
]

View File

@ -100,6 +100,13 @@ const gridItems = [
params: { data: encodeURIComponent(JSON.stringify(yhzinfo.value)) }, params: { data: encodeURIComponent(JSON.stringify(yhzinfo.value)) },
}, },
}, },
{
icon: group106Icon,
text: "灾害管理",
to: {
name: "DisasterManagement",
},
},
{ {
icon: group105Icon, icon: group105Icon,
text: "预警信息", text: "预警信息",