feat: 气象预警
This commit is contained in:
parent
d769327701
commit
f24ce93621
62
packages/mobile/src/components/CardItem.vue
Normal file
62
packages/mobile/src/components/CardItem.vue
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<div class="card-item">
|
||||||
|
<slot name="header">
|
||||||
|
<div class="header" :style="{marginBottom: titleGap + 'px'}">
|
||||||
|
<div class="header-title">{{ title }}</div>
|
||||||
|
<div class="header-extra" v-if="$slots.headerExtra">
|
||||||
|
<slot name="headerExtra"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
|
||||||
|
<div class="content" v-if="$slots.content" :style="{gap: contentGap + 'px'}">
|
||||||
|
<slot name="content"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
titleGap: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 16
|
||||||
|
},
|
||||||
|
contentGap: {
|
||||||
|
type:[ Number, String],
|
||||||
|
default: 8
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.card-item {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding: 21px 50px 17px 10px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.header-title {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #4a4a4a;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
39
packages/mobile/src/components/CurrentSite.vue
Normal file
39
packages/mobile/src/components/CurrentSite.vue
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<!-- 当前站点 -->
|
||||||
|
<template>
|
||||||
|
<div class="current-site">
|
||||||
|
<van-icon class="position-icon" name="location-o" />
|
||||||
|
<span class="site-name">当前站点:{{ name || yhzStore.getYHZInfo?.mc }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { useYHZStore } from '../stores/yhzStore';
|
||||||
|
|
||||||
|
const yhzStore = useYHZStore()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.current-site {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 50px;
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0 5px;
|
||||||
|
color: #979797;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.site-name {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
24
packages/mobile/src/components/EmptyBox.vue
Normal file
24
packages/mobile/src/components/EmptyBox.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div class="empty">
|
||||||
|
{{ props.placeholder }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '暂无相关信息'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.empty {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
41
packages/mobile/src/components/PageContainer.vue
Normal file
41
packages/mobile/src/components/PageContainer.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div class="page-container">
|
||||||
|
<van-nav-bar title="气象预警" fixed left-arrow @click-left="onClickLeft" />
|
||||||
|
<div class="page-content-wrapper">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<div v-if="$slots.footer" class="page-footer-wrapper"><slot name="footer" /></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const emit = defineEmits(['back'])
|
||||||
|
|
||||||
|
const onClickLeft = () => {
|
||||||
|
emit('click-back')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.page-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: var(--van-nav-bar-height); /* 自动匹配导航栏高度 */
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
width: 100dvw;
|
||||||
|
height: 100dvh;
|
||||||
|
background-color: #f6f6f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.page-footer-wrapper {
|
||||||
|
padding-top: 10px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
37
packages/mobile/src/components/PanelHeader.vue
Normal file
37
packages/mobile/src/components/PanelHeader.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-header">
|
||||||
|
<span class="icon"></span>
|
||||||
|
<span class="title-text">{{ title }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.panel-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 5px;
|
||||||
|
height: 18px;
|
||||||
|
background: linear-gradient(180deg, #91c4f1 0%, #5892e0 100%);
|
||||||
|
border-radius: 2px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.title-text {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #4a4a4a;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
98
packages/mobile/src/components/SearchInput.vue
Normal file
98
packages/mobile/src/components/SearchInput.vue
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<div class="search-input">
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<div class="input-block">
|
||||||
|
<van-icon class="search-icon" name="search" />
|
||||||
|
<input class="inner-input" v-model="modelValue" :placeholder="placeholder" />
|
||||||
|
<van-icon class="close-icon" name="clear" v-if="modelValue !== ''" @click="modelValue = ''" />
|
||||||
|
</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>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const modelValue = defineModel('modelValue')
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请输入关键词'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.search-input {
|
||||||
|
position: relative;
|
||||||
|
height: 40px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-block {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.search-icon, .close-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 20px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
font-size: 20px;
|
||||||
|
color: #9b9b9b;
|
||||||
|
}
|
||||||
|
.close-icon {
|
||||||
|
left: unset;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-input {
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-block {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 3px;
|
||||||
|
color: #9b9b9b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder-text {
|
||||||
|
color: #9b9b9b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -72,10 +72,15 @@ const routes = [
|
|||||||
component: () => import('../views/WarningMessage/WarningMessage.vue')
|
component: () => import('../views/WarningMessage/WarningMessage.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/warningMessage-detail/:data',
|
path: '/warningMessage-detail',
|
||||||
name: 'WarningMessageDetail',
|
name: 'WarningMessageDetail',
|
||||||
component: () => import('../views/WarningMessage/WarningMessageDetail.vue')
|
component: () => import('../views/WarningMessage/WarningMessageDetail.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/warningMessageHandle',
|
||||||
|
name: 'WarningMessageHandle',
|
||||||
|
component: () => import('../views/WarningMessage/WarningMessageHandle.vue')
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
|||||||
29
packages/mobile/src/stores/yhzStore.js
Normal file
29
packages/mobile/src/stores/yhzStore.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useYHZStore = defineStore('config', {
|
||||||
|
state: () => ({
|
||||||
|
yhzInfo: null
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
getYHZInfo: (state) => {
|
||||||
|
if(state.yhzInfo) {
|
||||||
|
return state.yhzInfo
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const yhzInfo = JSON.parse(localStorage.getItem('yhzInfo'))
|
||||||
|
if(yhzInfo) {
|
||||||
|
return yhzInfo
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setYHZInfo(yhzInfo) {
|
||||||
|
this.yhzInfo = yhzInfo
|
||||||
|
localStorage.setItem('yhzInfo', JSON.stringify(yhzInfo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -77,11 +77,13 @@ import "vant/es/toast/style";
|
|||||||
import "vant/es/popup/style";
|
import "vant/es/popup/style";
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
|
import { useYHZStore } from "../stores/yhzStore";
|
||||||
import { showToast } from "vant";
|
import { showToast } from "vant";
|
||||||
import { request } from "../../../shared/utils/request";
|
import { request } from "../../../shared/utils/request";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const yhzinfo = ref({});
|
const yhzinfo = ref({});
|
||||||
|
const yhzStore = useYHZStore();
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const token = route.query.token;
|
const token = route.query.token;
|
||||||
@ -96,6 +98,7 @@ const getYHZinfo = async () => {
|
|||||||
});
|
});
|
||||||
if (res.code === "00000") {
|
if (res.code === "00000") {
|
||||||
yhzinfo.value = res.data[0];
|
yhzinfo.value = res.data[0];
|
||||||
|
yhzStore.setYHZInfo(res.data[0]);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(res.message);
|
throw new Error(res.message);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,360 +1,145 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="warning-message-page">
|
<PageContainer title="气象预警" @click-back="handleClickBack">
|
||||||
<!-- 顶部导航栏 -->
|
<SearchInput v-model="searchValue" />
|
||||||
<div class="nav-bar">
|
|
||||||
<div class="back-btn" @click="goBack">
|
|
||||||
<van-icon name="arrow-left" />
|
|
||||||
</div>
|
|
||||||
<div class="title">气象预警</div>
|
|
||||||
<div class="placeholder"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 搜索栏 -->
|
<CurrentSite />
|
||||||
<div class="search-section">
|
|
||||||
<div class="search-box">
|
|
||||||
<span class="search-text">关键词</span>
|
|
||||||
<van-icon name="search" class="search-icon" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 当前站点 -->
|
<div class="list-panel">
|
||||||
<div class="current-site">
|
<CardItem v-for="(item, index) in list" :key="index" :title="item.area" @click="handleClickItem(item)">
|
||||||
<span class="site-label">当前站点:</span>
|
<template #headerExtra>
|
||||||
<span class="site-name">{{ currentSite }}</span>
|
<span class="red-ball"></span>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 预警列表 -->
|
<!-- 发布时间 block -->
|
||||||
<div class="warning-list">
|
<div class="time-block">
|
||||||
<div
|
<div class="time-box">
|
||||||
v-for="(item, index) in warningList"
|
<span class="time-label-text">发布时间:</span>
|
||||||
:key="index"
|
<span class="time-value-text">{{ item.publishTime }}</span>
|
||||||
class="warning-item"
|
|
||||||
:class="{ unread: !item.isRead }"
|
|
||||||
@click="viewDetail(item)"
|
|
||||||
>
|
|
||||||
<div class="item-content">
|
|
||||||
<div class="warning-title">{{ item.title }}</div>
|
|
||||||
<div class="warning-time">
|
|
||||||
<span class="time-label">发布时间:</span>
|
|
||||||
<span class="time-value">{{ item.publishTime }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item-right">
|
|
||||||
<div v-if="!item.isRead" class="unread-dot"></div>
|
|
||||||
<van-icon name="arrow" class="arrow-icon" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<van-icon class="jump-icon" name="arrow" />
|
||||||
</div>
|
</div>
|
||||||
|
</CardItem>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 空状态提示 -->
|
||||||
<div v-if="warningList.length === 0" class="empty-state">
|
<EmptyBox v-if="list.length === 0" placeholder="暂无相关预警信息" />
|
||||||
<van-icon name="warning-o" class="empty-icon" />
|
|
||||||
<div class="empty-text">暂无预警消息</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref, onMounted, watch } from 'vue'
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import PageContainer from '@/components/PageContainer.vue'
|
||||||
|
import SearchInput from '@/components/SearchInput.vue'
|
||||||
|
import CurrentSite from '@/components/CurrentSite.vue'
|
||||||
|
import CardItem from '@/components/CardItem.vue'
|
||||||
|
import EmptyBox from '@/components/EmptyBox.vue'
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
// 当前站点
|
onMounted(() => {
|
||||||
const currentSite = ref("李家坝仓库");
|
getData()
|
||||||
|
})
|
||||||
|
|
||||||
// 解析传递的站点信息
|
// 搜索关键词
|
||||||
const initSiteInfo = () => {
|
const searchValue = ref('')
|
||||||
const { data } = route.params;
|
|
||||||
if (data) {
|
watch(
|
||||||
try {
|
() => searchValue.value,
|
||||||
const yhzinfo = JSON.parse(decodeURIComponent(data));
|
(newVal, oldVal) => {
|
||||||
currentSite.value = yhzinfo.mc || "李家坝仓库";
|
if (newVal !== oldVal) {
|
||||||
} catch (e) {
|
getData(newVal)
|
||||||
console.error("解析站点信息失败", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
)
|
||||||
|
|
||||||
|
|
||||||
initSiteInfo();
|
|
||||||
|
|
||||||
// 预警列表数据
|
// 预警列表数据
|
||||||
const warningList = ref([
|
const list = ref([
|
||||||
{
|
{
|
||||||
id: 1,
|
area: '合川区',
|
||||||
title: "合川区暴雨红色预警",
|
level: '红色气象预警',
|
||||||
publishTime: "2025/10/10 20:29",
|
publishTime: '2026/01/10 20:29'
|
||||||
isRead: false,
|
|
||||||
level: "red",
|
|
||||||
type: "rain",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
area: '万州区',
|
||||||
title: "合川区红色气象预警",
|
level: '红色气象预警',
|
||||||
publishTime: "2025/10/09 20:29",
|
publishTime: '2025/10/10 20:29'
|
||||||
isRead: true,
|
|
||||||
level: "red",
|
|
||||||
type: "weather",
|
|
||||||
},
|
},
|
||||||
]);
|
{
|
||||||
|
area: '涪陵区',
|
||||||
|
level: '红色气象预警',
|
||||||
|
publishTime: '2025/10/10 20:29'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
// 返回上一页
|
const getData = async () => {
|
||||||
const goBack = () => {
|
|
||||||
router.back();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 查看详情
|
}
|
||||||
const viewDetail = (item) => {
|
|
||||||
// 标记为已读
|
const handleClickItem = (item) => {
|
||||||
item.isRead = true;
|
|
||||||
// 跳转到详情页
|
|
||||||
router.push({
|
router.push({
|
||||||
name: "WarningMessageDetail",
|
path: '/warningMessage-detail',
|
||||||
params: {
|
|
||||||
id: item.id,
|
})
|
||||||
data: route.params.data,
|
}
|
||||||
},
|
|
||||||
});
|
const handleClickBack = () => {
|
||||||
};
|
router.push('/')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
// 移动端适配变量
|
/* ==================== Panel 层级 ==================== */
|
||||||
$primary-color: #409eff;
|
.list-panel {
|
||||||
$text-color: #333;
|
|
||||||
$text-secondary: #666;
|
|
||||||
$text-tertiary: #999;
|
|
||||||
$bg-color: #f5f5f5;
|
|
||||||
$border-color: #e0e0e0;
|
|
||||||
$unread-color: #f56c6c;
|
|
||||||
|
|
||||||
.warning-message-page {
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: $bg-color;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
|
|
||||||
// 顶部导航栏
|
|
||||||
.nav-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 44px;
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 0 15px;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
.back-btn {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-start;
|
|
||||||
color: $text-color;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
flex: 1;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 17px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: $text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索栏
|
|
||||||
.search-section {
|
|
||||||
padding: 12px 15px;
|
|
||||||
background-color: #fff;
|
|
||||||
|
|
||||||
.search-box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 36px;
|
|
||||||
background-color: #e8e8e8;
|
|
||||||
border-radius: 18px;
|
|
||||||
padding: 0 15px;
|
|
||||||
|
|
||||||
.search-text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: $text-tertiary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-icon {
|
|
||||||
font-size: 18px;
|
|
||||||
color: $text-tertiary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前站点
|
|
||||||
.current-site {
|
|
||||||
padding: 10px 15px;
|
|
||||||
background-color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 13px;
|
|
||||||
border-bottom: 1px solid $border-color;
|
|
||||||
|
|
||||||
.site-label {
|
|
||||||
color: $text-secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-name {
|
|
||||||
color: $primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 预警列表
|
|
||||||
.warning-list {
|
|
||||||
padding: 10px 15px;
|
|
||||||
|
|
||||||
.warning-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
.warning-title {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: $text-color;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-time {
|
|
||||||
font-size: 12px;
|
|
||||||
color: $text-tertiary;
|
|
||||||
|
|
||||||
.time-label {
|
|
||||||
color: $text-secondary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-right {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
margin-left: 10px;
|
|
||||||
|
|
||||||
.unread-dot {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: $unread-color;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
font-size: 16px;
|
|
||||||
color: $text-tertiary;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 未读状态样式
|
|
||||||
&.unread {
|
|
||||||
.warning-title {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 空状态
|
|
||||||
.empty-state {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-item {
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== Block 层级 ==================== */
|
||||||
|
.time-block {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: space-between;
|
||||||
padding: 60px 20px;
|
|
||||||
|
|
||||||
.empty-icon {
|
|
||||||
font-size: 48px;
|
|
||||||
color: $text-tertiary;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-text {
|
/* ==================== Box 层级 ==================== */
|
||||||
font-size: 14px;
|
.time-box {
|
||||||
color: $text-secondary;
|
display: flex;
|
||||||
}
|
align-items: baseline;
|
||||||
}
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 适配 iPhone 刘海屏
|
/* ==================== Text / Num 层级 ==================== */
|
||||||
@supports (padding-top: env(safe-area-inset-top)) {
|
.red-ball {
|
||||||
.warning-message-page {
|
display: inline-block;
|
||||||
.nav-bar {
|
width: 13px;
|
||||||
padding-top: env(safe-area-inset-top);
|
height: 13px;
|
||||||
height: calc(44px + env(safe-area-inset-top));
|
background: linear-gradient(180deg, #fd4646 0%, #fb2222 100%);
|
||||||
}
|
box-shadow: 0px 1px 4px 0px rgba(91, 8, 8, 0.34);
|
||||||
}
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 适配不同屏幕尺寸
|
.jump-icon {
|
||||||
@media screen and (max-width: 320px) {
|
|
||||||
.warning-message-page {
|
|
||||||
.warning-list {
|
|
||||||
.warning-item {
|
|
||||||
.item-content {
|
|
||||||
.warning-title {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 414px) {
|
|
||||||
.warning-message-page {
|
|
||||||
.nav-bar {
|
|
||||||
height: 48px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning-list {
|
|
||||||
.warning-item {
|
|
||||||
padding: 18px;
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
.warning-title {
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
color: rgb(102, 102, 102, .4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-time {
|
.time-label-text {
|
||||||
font-size: 13px;
|
font-weight: 400;
|
||||||
}
|
font-size: 14px;
|
||||||
}
|
color: #666666;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time-value-text {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666666;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,395 +1,175 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="warning-detail-page">
|
<PageContainer class="page-container" title="预警详情" @click-back="handleClickBack">
|
||||||
<!-- 顶部导航栏 -->
|
<CurrentSite />
|
||||||
<div class="nav-bar">
|
|
||||||
<div class="back-btn" @click="goBack">
|
<PanelHeader title="气象预警" />
|
||||||
<van-icon name="arrow-left" />
|
<div class="list-panel margin">
|
||||||
|
<CardItem class="card-item" v-for="(item, index) in list" :key="index" :title="item.area" titleGap="10">
|
||||||
|
<template #content>
|
||||||
|
<div class="time-block">
|
||||||
|
<div class="time-box">
|
||||||
|
<span class="time-label-text">发布时间:</span>
|
||||||
|
<span class="time-value-text">{{ item.publishTime }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">气象预警</div>
|
</div>
|
||||||
<div class="placeholder"></div>
|
<div class="desc-block">
|
||||||
|
<div class="desc-text">{{ item.content }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 当前站点 -->
|
<van-icon class="jump-icon" name="arrow" />
|
||||||
<div class="current-site">
|
</template>
|
||||||
<span class="site-label">当前站点:</span>
|
</CardItem>
|
||||||
<span class="site-name">{{ currentSite }}</span>
|
|
||||||
|
<!-- 空状态提示 -->
|
||||||
|
<EmptyBox v-if="list.length === 0" placeholder="暂无相关预警信息" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 预警详情内容 -->
|
<PanelHeader title="防御措施" />
|
||||||
<div class="detail-content" v-if="warningDetail">
|
<div class="list-panel">
|
||||||
<!-- 预警标题区域 -->
|
<CardItem v-for="(item, index) in list" :key="index" :title="item.area">
|
||||||
<div class="warning-header">
|
<div class="method-block">
|
||||||
<div class="header-title">气象预警</div>
|
<div class="method-text">{{ item.method }}</div>
|
||||||
|
</div>
|
||||||
|
<van-icon class="jump-icon" name="arrow" />
|
||||||
|
</CardItem>
|
||||||
|
|
||||||
|
<!-- 空状态提示 -->
|
||||||
|
<EmptyBox v-if="list.length === 0" placeholder="暂无相关防御措施" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 预警信息卡片 -->
|
<div class="footer-panel">
|
||||||
<div class="info-card">
|
<van-button block type="primary" @click="handleClickBack()">我已知晓</van-button>
|
||||||
<div class="warning-name">{{ warningDetail.title }}</div>
|
<van-button block type="primary" @click="goToHandle()">我要响应</van-button>
|
||||||
<div class="publish-time">
|
|
||||||
<span class="time-label">发布时间:</span>
|
|
||||||
<span class="time-value">{{ warningDetail.publishTime }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 预警描述 -->
|
|
||||||
<div class="description-card">
|
|
||||||
<div class="desc-text">{{ warningDetail.description }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 防御措施 -->
|
|
||||||
<div class="measures-section">
|
|
||||||
<div class="section-title">防御措施</div>
|
|
||||||
<div class="measures-card">
|
|
||||||
<div class="measures-text">{{ warningDetail.measures }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 底部按钮 -->
|
|
||||||
<div class="bottom-action">
|
|
||||||
<van-button type="primary" block round @click="handleResponse">
|
|
||||||
立即响应
|
|
||||||
</van-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</PageContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { showToast } from "vant";
|
import PageContainer from '@/components/PageContainer.vue'
|
||||||
|
import CurrentSite from '@/components/CurrentSite.vue'
|
||||||
|
import PanelHeader from '@/components/PanelHeader.vue'
|
||||||
|
import CardItem from '@/components/CardItem.vue'
|
||||||
|
import EmptyBox from '@/components/EmptyBox.vue'
|
||||||
|
|
||||||
const route = useRoute();
|
const router = useRouter()
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
// 当前站点
|
|
||||||
const currentSite = ref("李家坝仓库");
|
|
||||||
|
|
||||||
// 预警详情数据
|
|
||||||
const warningDetail = ref({
|
|
||||||
id: 1,
|
|
||||||
title: "合川区暴雨红色预警",
|
|
||||||
publishTime: "2025/10/10 20:29",
|
|
||||||
description:
|
|
||||||
"区气象台发布暴雨红色预警,按照相关要求,启动I级防御响应,并请及时关注地质、水文等风险提示信息,落实主动封闭管控/“关停撤转”措施",
|
|
||||||
measures:
|
|
||||||
"请立即按照2小时一次频率对你管养的重点路段/重点部位进行巡查,重点巡查较高及以上风险路段、涉灾隐患点、地质条件复杂路段、临河临崖路段/两区三厂、大型设施设备、取弃土(渣)场、砂石料场、涉水桥梁、富水隧道、围堰、支架脚手架、高切坡、滑坡处置等部位,重点关注涉水桥梁基础及墩台、不良地质隧道、隧道洞口边仰坡及侧切结构、高陡边坡支挡防护以及防排水设施,发现异常情况,立即向上报告,采取紧急排危、告警阻拦、吹哨撤转等措施,并及时报送工作开展情况。",
|
|
||||||
level: "red",
|
|
||||||
type: "rain",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 返回上一页
|
|
||||||
const goBack = () => {
|
|
||||||
router.back();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 立即响应
|
|
||||||
const handleResponse = () => {
|
|
||||||
showToast({
|
|
||||||
message: "响应成功",
|
|
||||||
type: "success",
|
|
||||||
});
|
|
||||||
// 实际项目中调用API提交响应
|
|
||||||
console.log("立即响应", warningDetail.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 从路由参数获取预警ID,然后请求详情数据
|
getData()
|
||||||
const { id } = route.params;
|
})
|
||||||
if (id) {
|
|
||||||
console.log("获取预警详情 ID:", id);
|
|
||||||
// 实际项目中调用API获取详情
|
|
||||||
// fetchWarningDetail(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析传递的站点信息
|
// 列表数据
|
||||||
const { data } = route.params;
|
const list = ref([
|
||||||
if (data) {
|
{
|
||||||
try {
|
area: '合川区',
|
||||||
const yhzinfo = JSON.parse(decodeURIComponent(data));
|
level: '红色气象预警',
|
||||||
currentSite.value = yhzinfo.mc || "李家坝仓库";
|
content: '今天,北京将进入大雪,请做好',
|
||||||
} catch (e) {
|
publishTime: '2026/01/10 20:29',
|
||||||
console.error("解析站点信息失败", e);
|
method: '立即启动防汛Ⅰ级应急响应,立即转移危险区'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
area: '万州区',
|
||||||
|
level: '红色气象预警',
|
||||||
|
publishTime: '2025/10/10 20:29',
|
||||||
|
method: '立即启动防汛Ⅰ级应急响应,立即转移危险区'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
area: '涪陵区',
|
||||||
|
level: '红色气象预警',
|
||||||
|
publishTime: '2025/10/10 20:29',
|
||||||
|
method: '立即启动防汛Ⅰ级应急响应,立即转移危险区'
|
||||||
}
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const getData = async () => {}
|
||||||
|
|
||||||
|
const handleClickBack = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
const goToHandle = () => {
|
||||||
|
router.push({path: '/warningMessageHandle'})
|
||||||
}
|
}
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
// 移动端适配变量
|
.page-container {
|
||||||
$primary-color: #1989fa;
|
position: relative;
|
||||||
$text-color: #333;
|
padding-bottom: 74px;
|
||||||
$text-secondary: #666;
|
}
|
||||||
$text-tertiary: #999;
|
|
||||||
$bg-color: #f5f5f5;
|
|
||||||
$border-color: #e0e0e0;
|
|
||||||
$card-bg: #fff;
|
|
||||||
$section-title-color: #666;
|
|
||||||
|
|
||||||
.warning-detail-page {
|
/* ==================== Panel 层级 ==================== */
|
||||||
min-height: 100vh;
|
.list-panel {
|
||||||
background-color: $bg-color;
|
display: flex;
|
||||||
padding-bottom: 80px;
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
// 顶部导航栏
|
&.margin {
|
||||||
.nav-bar {
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-panel {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 64px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px -5px 12px 0px rgba(200, 200, 200, 0.37);
|
||||||
|
opacity: 0.97;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== Block 层级 ==================== */
|
||||||
|
.time-block {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
height: 44px;
|
}
|
||||||
background-color: #fff;
|
|
||||||
padding: 0 15px;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
.back-btn {
|
/* ==================== Box 层级 ==================== */
|
||||||
width: 40px;
|
.time-box {
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: baseline;
|
||||||
justify-content: flex-start;
|
flex-wrap: wrap;
|
||||||
color: $text-color;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
/* ==================== Text 层级 ==================== */
|
||||||
flex: 1;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 17px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: $text-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
.jump-icon {
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前站点
|
|
||||||
.current-site {
|
|
||||||
padding: 10px 15px;
|
|
||||||
background-color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 13px;
|
|
||||||
border-bottom: 1px solid $border-color;
|
|
||||||
|
|
||||||
.site-label {
|
|
||||||
color: $text-secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-name {
|
|
||||||
color: $primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 详情内容
|
|
||||||
.detail-content {
|
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
// 预警标题区域
|
|
||||||
.warning-header {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.header-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: $text-color;
|
|
||||||
position: relative;
|
|
||||||
padding-left: 12px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
width: 4px;
|
right: 15px;
|
||||||
height: 16px;
|
|
||||||
background-color: $primary-color;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 信息卡片
|
|
||||||
.info-card {
|
|
||||||
background-color: $card-bg;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
.warning-name {
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
color: rgb(102, 102, 102, 0.4);
|
||||||
color: $text-color;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.publish-time {
|
.time-label-text {
|
||||||
font-size: 13px;
|
font-weight: 400;
|
||||||
color: $text-tertiary;
|
font-size: 14px;
|
||||||
|
color: #999999;
|
||||||
.time-label {
|
|
||||||
color: $text-secondary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 描述卡片
|
.time-value-text {
|
||||||
.description-card {
|
font-weight: 400;
|
||||||
background-color: $card-bg;
|
font-size: 14px;
|
||||||
border-radius: 8px;
|
color: #999999;
|
||||||
padding: 15px;
|
}
|
||||||
margin-bottom: 12px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
.desc-text {
|
.desc-text {
|
||||||
|
font-weight: 400;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $text-secondary;
|
color: #666666;
|
||||||
line-height: 1.8;
|
line-height: 13px;
|
||||||
text-align: justify;
|
|
||||||
}
|
}
|
||||||
}
|
.method-text {
|
||||||
|
font-weight: 500;
|
||||||
// 防御措施区域
|
|
||||||
.measures-section {
|
|
||||||
.section-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: $text-color;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
position: relative;
|
|
||||||
padding-left: 12px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
width: 4px;
|
|
||||||
height: 16px;
|
|
||||||
background-color: $primary-color;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.measures-card {
|
|
||||||
background-color: $card-bg;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 15px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
.measures-text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: $text-secondary;
|
|
||||||
line-height: 1.8;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 底部按钮
|
|
||||||
.bottom-action {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
padding: 10px 15px 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-top: 1px solid $border-color;
|
|
||||||
|
|
||||||
.van-button {
|
|
||||||
height: 44px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 适配 iPhone 刘海屏
|
|
||||||
@supports (padding-top: env(safe-area-inset-top)) {
|
|
||||||
.warning-detail-page {
|
|
||||||
.nav-bar {
|
|
||||||
padding-top: env(safe-area-inset-top);
|
|
||||||
height: calc(44px + env(safe-area-inset-top));
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-action {
|
|
||||||
padding-bottom: calc(20px + env(safe-area-inset-bottom));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 适配不同屏幕尺寸
|
|
||||||
@media screen and (max-width: 320px) {
|
|
||||||
.warning-detail-page {
|
|
||||||
.detail-content {
|
|
||||||
.info-card {
|
|
||||||
.warning-name {
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
color: #4a4a4a;
|
||||||
}
|
line-height: 20px;
|
||||||
|
|
||||||
.description-card,
|
|
||||||
.measures-section {
|
|
||||||
.desc-text,
|
|
||||||
.measures-text {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 414px) {
|
|
||||||
.warning-detail-page {
|
|
||||||
.nav-bar {
|
|
||||||
height: 48px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-content {
|
|
||||||
.warning-header {
|
|
||||||
.header-title {
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-card {
|
|
||||||
padding: 18px;
|
|
||||||
|
|
||||||
.warning-name {
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.publish-time {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.description-card,
|
|
||||||
.measures-card {
|
|
||||||
padding: 18px;
|
|
||||||
|
|
||||||
.desc-text,
|
|
||||||
.measures-text {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.measures-section {
|
|
||||||
.section-title {
|
|
||||||
font-size: 17px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
<PageContainer class="page-container" title="预警详情" @click-back="handleClickBack">
|
||||||
|
<CurrentSite />
|
||||||
|
|
||||||
|
<div class="form-panel">
|
||||||
|
<van-field v-model="value" label="现场情况描述" placeholder="请输入备注" label-align="top" rows="3" type="textarea" />
|
||||||
|
|
||||||
|
<van-field class="mt-8" v-model="value" label="备注(非必填)" placeholder="请填写" input-align="right" />
|
||||||
|
|
||||||
|
<div class="image-upload-block">
|
||||||
|
<div class="label-box">
|
||||||
|
<span class="main-text">现场照片</span>
|
||||||
|
<span class="sub-text">最多上传6张</span>
|
||||||
|
</div>
|
||||||
|
<div class="com-">
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer-panel">
|
||||||
|
<van-button block type="primary" @click="confirm()">确认</van-button>
|
||||||
|
</div>
|
||||||
|
</PageContainer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import PageContainer from '@/components/PageContainer.vue'
|
||||||
|
import CurrentSite from '@/components/CurrentSite.vue'
|
||||||
|
import { showToast, showLoadingToast, showImagePreview } from 'vant'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 上传附件相关
|
||||||
|
const fileList = ref([])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 列表数据
|
||||||
|
const list = ref([
|
||||||
|
{
|
||||||
|
area: '合川区',
|
||||||
|
level: '红色气象预警',
|
||||||
|
content: '今天,北京将进入大雪,请做好',
|
||||||
|
publishTime: '2026/01/10 20:29',
|
||||||
|
method: '立即启动防汛Ⅰ级应急响应,立即转移危险区'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
area: '万州区',
|
||||||
|
level: '红色气象预警',
|
||||||
|
publishTime: '2025/10/10 20:29',
|
||||||
|
method: '立即启动防汛Ⅰ级应急响应,立即转移危险区'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
area: '涪陵区',
|
||||||
|
level: '红色气象预警',
|
||||||
|
publishTime: '2025/10/10 20:29',
|
||||||
|
method: '立即启动防汛Ⅰ级应急响应,立即转移危险区'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// 文件删除
|
||||||
|
const handleDelete = (file) => {
|
||||||
|
if (file.serverUrl) {
|
||||||
|
const index = editForm.photos.findIndex((p) => p.photoUrl === file.serverUrl)
|
||||||
|
if (index !== -1) {
|
||||||
|
editForm.photos.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件上传
|
||||||
|
const afterRead = async (file) => {
|
||||||
|
const toast = showLoadingToast({
|
||||||
|
message: '上传中...',
|
||||||
|
forbidClick: true,
|
||||||
|
duration: 0 // 设置为0表示不会自动关闭
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
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') {
|
||||||
|
editForm.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('editForm.photos', toRaw(editForm.photos))
|
||||||
|
console.log('fileList.value', fileList.value)
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.close()
|
||||||
|
showToast({
|
||||||
|
type: 'fail',
|
||||||
|
message: error.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getData = async () => {}
|
||||||
|
|
||||||
|
const handleClickBack = () => {
|
||||||
|
router.go(-1)
|
||||||
|
}
|
||||||
|
const confirm = () => {
|
||||||
|
router.push({ path: '/warningMessageHandle' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.page-container {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-bottom: 74px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-8 {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== Panel 层级 ==================== */
|
||||||
|
|
||||||
|
.footer-panel {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 64px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px -5px 12px 0px rgba(200, 200, 200, 0.37);
|
||||||
|
opacity: 0.97;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-upload-block {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.label-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-text {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #4a4a4a;
|
||||||
|
line-height: 16px;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
.sub-text {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4a4a4a;
|
||||||
|
line-height: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user