|
|
|
@ -10,7 +10,11 @@ |
|
|
|
<div class="content-area"> |
|
|
|
<!-- Tab 切换 --> |
|
|
|
<van-tabs v-model:active="activeTab" class="message-tabs" sticky swipeable> |
|
|
|
<van-tab title="消息"> |
|
|
|
<van-tab> |
|
|
|
<template #title> |
|
|
|
<span>消息</span> |
|
|
|
<span v-if="messageTotalCount > 0" class="tab-badge">{{ messageTotalCount > 99 ? '99+' : messageTotalCount }}</span> |
|
|
|
</template> |
|
|
|
<!-- 消息列表 --> |
|
|
|
<van-pull-refresh v-model="messageRefreshing" @refresh="onMessageRefresh"> |
|
|
|
<van-list v-model:loading="messageLoading" :finished="messageFinished" finished-text="没有更多了" |
|
|
|
@ -19,8 +23,8 @@ |
|
|
|
|
|
|
|
<div class="message-item" v-for="item in messageList" :key="item.id" |
|
|
|
@click="onMessageClick(item)"> |
|
|
|
<div class="item-icon" :class="item.iconBg"> |
|
|
|
<!-- 红点角标 --> |
|
|
|
<div class="item-icon" :class="item.iconClass"> |
|
|
|
<component :is="item.iconComp" /> |
|
|
|
<span v-if="item.unreadCount > 0" class="icon-badge">{{ item.unreadCount > 99 ? |
|
|
|
'99+' : item.unreadCount }}</span> |
|
|
|
</div> |
|
|
|
@ -37,7 +41,11 @@ |
|
|
|
</van-pull-refresh> |
|
|
|
</van-tab> |
|
|
|
|
|
|
|
<van-tab title="待办"> |
|
|
|
<van-tab> |
|
|
|
<template #title> |
|
|
|
<span>待办</span> |
|
|
|
<span v-if="todoTotalCount > 0" class="tab-badge">{{ todoTotalCount > 99 ? '99+' : todoTotalCount }}</span> |
|
|
|
</template> |
|
|
|
<!-- 待办列表 --> |
|
|
|
<van-pull-refresh v-model="todoRefreshing" @refresh="onTodoRefresh"> |
|
|
|
<van-list v-model:loading="todoLoading" :finished="todoFinished" finished-text="没有更多了" |
|
|
|
@ -46,7 +54,8 @@ |
|
|
|
|
|
|
|
<div class="message-item" v-for="item in todoList" :key="item.id" |
|
|
|
@click="onTodoClick(item)"> |
|
|
|
<div class="item-icon" :class="item.iconBg"> |
|
|
|
<div class="item-icon" :class="item.iconClass"> |
|
|
|
<component :is="item.iconComp" /> |
|
|
|
<span v-if="item.unreadCount > 0" class="icon-badge">{{ item.unreadCount > 99 ? |
|
|
|
'99+' : item.unreadCount }}</span> |
|
|
|
</div> |
|
|
|
@ -71,7 +80,7 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script setup name="messageCenter"> |
|
|
|
import { ref, computed, onMounted, watch } from 'vue'; |
|
|
|
import { ref, computed, onMounted, watch, h } from 'vue'; |
|
|
|
import { useRouter, useRoute } from 'vue-router'; |
|
|
|
import { showToast } from 'vant'; |
|
|
|
import 'vant/es/toast/style'; |
|
|
|
@ -79,12 +88,46 @@ import FooterBar from '@/components/FooterBar.vue'; |
|
|
|
import { getMessageUnreadCount } from '@/api/enforcement/index'; |
|
|
|
import { pffwCount, zfpjcount, zfygCount, jhtzcount, zfrwCount, zfyqCount, setMessageStats } from '@/store/messageState'; |
|
|
|
|
|
|
|
// ========== SVG 图标组件 ========== |
|
|
|
const IconLaw = () => h('svg', { viewBox: '0 0 24 24', fill: 'currentColor' }, [ |
|
|
|
h('path', { d: 'M12 3v10.55c-.59.34-1.27.55-2 .55-2.21 0-4-1.79-4-4s1.79-4 4-4c.28 0 .54.04.79.1C10.81 4.19 11.38 3.5 12 3zm1 2.26V9h1.74C14.38 7.88 13.34 6.64 12 6.26zM11 21c3.87 0 7-3.13 7-7h-2c0 2.76-2.24 5-5 5s-5-2.24-5-5H4c0 3.87 3.13 7 7 7z' }), |
|
|
|
]); |
|
|
|
|
|
|
|
const IconPlan = () => h('svg', { viewBox: '0 0 24 24', fill: 'currentColor' }, [ |
|
|
|
h('path', { d: 'M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z' }), |
|
|
|
]); |
|
|
|
|
|
|
|
const IconFeedback = () => h('svg', { viewBox: '0 0 24 24', fill: 'currentColor' }, [ |
|
|
|
h('path', { d: 'M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 12h-2v-2h2v2zm0-4h-2V6h2v4z' }), |
|
|
|
]); |
|
|
|
|
|
|
|
const IconNotice = () => h('svg', { viewBox: '0 0 24 24', fill: 'currentColor' }, [ |
|
|
|
h('path', { d: 'M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM7 9h10v2H7V9zm6 5H7v-2h6v2zm4-6H7V6h10v2z' }), |
|
|
|
]); |
|
|
|
|
|
|
|
const IconTask = () => h('svg', { viewBox: '0 0 24 24', fill: 'currentColor' }, [ |
|
|
|
h('path', { d: 'M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm-2 14l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z' }), |
|
|
|
]); |
|
|
|
|
|
|
|
const IconAppointment = () => h('svg', { viewBox: '0 0 24 24', fill: 'currentColor' }, [ |
|
|
|
h('path', { d: 'M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z' }), |
|
|
|
]); |
|
|
|
|
|
|
|
const router = useRouter(); |
|
|
|
const route = useRoute(); |
|
|
|
|
|
|
|
// ========== Tab 状态 ========== |
|
|
|
const activeTab = ref(0); |
|
|
|
|
|
|
|
// ========== 计算消息和待办未读总数 ========== |
|
|
|
const messageTotalCount = computed(() => { |
|
|
|
return pffwCount.value + zfpjcount.value + jhtzcount.value; |
|
|
|
}); |
|
|
|
|
|
|
|
const todoTotalCount = computed(() => { |
|
|
|
return zfygCount.value + zfrwCount.value + zfyqCount.value; |
|
|
|
}); |
|
|
|
|
|
|
|
// ========== 消息列表状态 ========== |
|
|
|
const messageList = ref([]); |
|
|
|
const messageLoading = ref(false); |
|
|
|
@ -122,8 +165,8 @@ const initMessageList = () => { |
|
|
|
title: '涉企普法', |
|
|
|
desc: '', |
|
|
|
time: '', |
|
|
|
icon: 'law', |
|
|
|
iconBg: 'icon-blue', |
|
|
|
iconComp: IconLaw, |
|
|
|
iconClass: 'icon-blue', |
|
|
|
unreadCount: pffwCount.value, |
|
|
|
}, |
|
|
|
// { |
|
|
|
@ -131,8 +174,8 @@ const initMessageList = () => { |
|
|
|
// title: '计划通知', |
|
|
|
// desc: '', |
|
|
|
// time: '', |
|
|
|
// icon: 'law', |
|
|
|
// iconBg: 'icon-blue', |
|
|
|
// iconComp: IconPlan, |
|
|
|
// iconClass: 'icon-cyan', |
|
|
|
// unreadCount: jhtzcount.value, |
|
|
|
// }, |
|
|
|
{ |
|
|
|
@ -140,16 +183,17 @@ const initMessageList = () => { |
|
|
|
title: '执法反馈', |
|
|
|
desc: '', |
|
|
|
time: '', |
|
|
|
icon: 'law', |
|
|
|
iconBg: 'icon-blue', |
|
|
|
iconComp: IconFeedback, |
|
|
|
iconClass: 'icon-orange', |
|
|
|
unreadCount: zfpjcount.value, |
|
|
|
},{ |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 4, |
|
|
|
title: '非现场执法反馈', |
|
|
|
desc: '', |
|
|
|
time: '', |
|
|
|
icon: 'law', |
|
|
|
iconBg: 'icon-blue', |
|
|
|
iconComp: IconFeedback, |
|
|
|
iconClass: 'icon-red', |
|
|
|
unreadCount: zfpjcount.value, |
|
|
|
}, |
|
|
|
]; |
|
|
|
@ -163,8 +207,8 @@ const initTodoList = () => { |
|
|
|
title: '执法预告', |
|
|
|
desc: '', |
|
|
|
time: '', |
|
|
|
icon: 'check', |
|
|
|
iconBg: 'icon-red', |
|
|
|
iconComp: IconNotice, |
|
|
|
iconClass: 'icon-cyan', |
|
|
|
unreadCount: zfygCount.value, |
|
|
|
}, |
|
|
|
{ |
|
|
|
@ -172,8 +216,8 @@ const initTodoList = () => { |
|
|
|
title: '执法任务', |
|
|
|
desc: '', |
|
|
|
time: '', |
|
|
|
icon: 'check', |
|
|
|
iconBg: 'icon-red', |
|
|
|
iconComp: IconTask, |
|
|
|
iconClass: 'icon-red', |
|
|
|
unreadCount: zfrwCount.value, |
|
|
|
}, |
|
|
|
{ |
|
|
|
@ -181,8 +225,8 @@ const initTodoList = () => { |
|
|
|
title: '联合执法预约', |
|
|
|
desc: '', |
|
|
|
time: '', |
|
|
|
icon: 'check', |
|
|
|
iconBg: 'icon-red', |
|
|
|
iconComp: IconAppointment, |
|
|
|
iconClass: 'icon-teal', |
|
|
|
unreadCount: zfyqCount.value, |
|
|
|
}, |
|
|
|
]; |
|
|
|
@ -198,16 +242,16 @@ const fetchMessageList = () => { |
|
|
|
title: '涉企普法', |
|
|
|
desc: '最新一条普法信息标题', |
|
|
|
time: '2026-04-15 10:07', |
|
|
|
icon: 'law', |
|
|
|
iconBg: 'icon-blue', |
|
|
|
iconComp: IconLaw, |
|
|
|
iconClass: 'icon-blue', |
|
|
|
unreadCount: pffwCount.value, |
|
|
|
}, { |
|
|
|
id: 2, |
|
|
|
title: '计划通知', |
|
|
|
desc: jhtzcount.value > 0 ? `您有${jhtzcount.value}条计划通知,请及时查看` : '暂无计划通知', |
|
|
|
time: '2026-04-15 10:07', |
|
|
|
icon: 'law', |
|
|
|
iconBg: 'icon-blue', |
|
|
|
iconComp: IconPlan, |
|
|
|
iconClass: 'icon-cyan', |
|
|
|
unreadCount: jhtzcount.value, |
|
|
|
}, |
|
|
|
{ |
|
|
|
@ -215,8 +259,8 @@ const fetchMessageList = () => { |
|
|
|
title: '执法反馈', |
|
|
|
desc: zfpjcount.value > 0 ? `您有${zfpjcount.value}条执法反馈,请及时查看` : '暂无执法反馈', |
|
|
|
time: '2026-04-15 10:07', |
|
|
|
icon: 'law', |
|
|
|
iconBg: 'icon-blue', |
|
|
|
iconComp: IconFeedback, |
|
|
|
iconClass: 'icon-orange', |
|
|
|
unreadCount: zfpjcount.value, |
|
|
|
}, |
|
|
|
{ |
|
|
|
@ -224,11 +268,10 @@ const fetchMessageList = () => { |
|
|
|
title: '非现场执法反馈', |
|
|
|
desc: zfpjcount.value > 0 ? `您有${zfpjcount.value}条非现场执法反馈,请及时查看` : '暂无非现场执法反馈', |
|
|
|
time: '2026-04-15 10:07', |
|
|
|
icon: 'law', |
|
|
|
iconBg: 'icon-blue', |
|
|
|
iconComp: IconFeedback, |
|
|
|
iconClass: 'icon-red', |
|
|
|
unreadCount: zfpjcount.value, |
|
|
|
}, |
|
|
|
|
|
|
|
]; |
|
|
|
messageRefreshing.value = false; |
|
|
|
} |
|
|
|
@ -247,8 +290,8 @@ const fetchTodoList = () => { |
|
|
|
title: '执法预告', |
|
|
|
desc: zfygCount.value > 0 ? `您有${zfygCount.value}条执法预告,请及时查看` : '暂无执法预告', |
|
|
|
time: '2026-04-15 09:30', |
|
|
|
icon: 'check', |
|
|
|
iconBg: 'icon-red', |
|
|
|
iconComp: IconNotice, |
|
|
|
iconClass: 'icon-cyan', |
|
|
|
unreadCount: zfygCount.value, |
|
|
|
}, |
|
|
|
{ |
|
|
|
@ -256,8 +299,8 @@ const fetchTodoList = () => { |
|
|
|
title: '执法任务', |
|
|
|
desc: zfrwCount.value > 0 ? `您有${zfrwCount.value}条执法任务,请及时查看` : '暂无执法任务', |
|
|
|
time: '2026-04-15 09:30', |
|
|
|
icon: 'check', |
|
|
|
iconBg: 'icon-red', |
|
|
|
iconComp: IconTask, |
|
|
|
iconClass: 'icon-red', |
|
|
|
unreadCount: zfrwCount.value, |
|
|
|
}, |
|
|
|
{ |
|
|
|
@ -265,8 +308,8 @@ const fetchTodoList = () => { |
|
|
|
title: '联合执法预约', |
|
|
|
desc: zfyqCount.value > 0 ? `您有${zfyqCount.value}条联合执法预约,请及时查看` : '暂无联合执法预约', |
|
|
|
time: '2026-04-15 09:30', |
|
|
|
icon: 'check', |
|
|
|
iconBg: 'icon-red', |
|
|
|
iconComp: IconAppointment, |
|
|
|
iconClass: 'icon-teal', |
|
|
|
unreadCount: zfyqCount.value, |
|
|
|
}, |
|
|
|
]; |
|
|
|
@ -280,7 +323,7 @@ const fetchTodoList = () => { |
|
|
|
// ========== 消息触底加载 ========== |
|
|
|
const onMessageLoad = () => { |
|
|
|
if (messageRefreshing.value) return; |
|
|
|
fetchMessageList(); |
|
|
|
// fetchMessageList(); |
|
|
|
messagePageNum.value++; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -289,14 +332,14 @@ const onMessageRefresh = () => { |
|
|
|
messagePageNum.value = 1; |
|
|
|
messageFinished.value = false; |
|
|
|
messageLoading.value = true; |
|
|
|
fetchMessageList(); |
|
|
|
// fetchMessageList(); |
|
|
|
messagePageNum.value++; |
|
|
|
}; |
|
|
|
|
|
|
|
// ========== 待办触底加载 ========== |
|
|
|
const onTodoLoad = () => { |
|
|
|
if (todoRefreshing.value) return; |
|
|
|
fetchTodoList(); |
|
|
|
// fetchTodoList(); |
|
|
|
todoPageNum.value++; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -305,7 +348,7 @@ const onTodoRefresh = () => { |
|
|
|
todoPageNum.value = 1; |
|
|
|
todoFinished.value = false; |
|
|
|
todoLoading.value = true; |
|
|
|
fetchTodoList(); |
|
|
|
// fetchTodoList(); |
|
|
|
todoPageNum.value++; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -452,6 +495,20 @@ onMounted(() => { |
|
|
|
border-radius: 2px; |
|
|
|
} |
|
|
|
|
|
|
|
.tab-badge { |
|
|
|
display: inline-block; |
|
|
|
min-width: 16px; |
|
|
|
height: 16px; |
|
|
|
padding: 0 4px; |
|
|
|
margin-left: 4px; |
|
|
|
background: #f44336; |
|
|
|
border-radius: 8px; |
|
|
|
color: #fff; |
|
|
|
font-size: 10px; |
|
|
|
line-height: 16px; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
:deep(.message-tabs .van-tabs__content) { |
|
|
|
background: #fbfdff; |
|
|
|
padding: 12px 16px; |
|
|
|
@ -484,26 +541,39 @@ onMounted(() => { |
|
|
|
height: 24px; |
|
|
|
color: #fff; |
|
|
|
} |
|
|
|
|
|
|
|
&.icon-blue { |
|
|
|
background: linear-gradient(135deg, #60a5fa, #3b82f6); |
|
|
|
} |
|
|
|
|
|
|
|
&.icon-cyan { |
|
|
|
background: linear-gradient(135deg, #22d3ee, #06b6d4); |
|
|
|
} |
|
|
|
|
|
|
|
&.icon-teal { |
|
|
|
background: linear-gradient(135deg, #2dd4bf, #14b8a6); |
|
|
|
} |
|
|
|
|
|
|
|
&.icon-orange { |
|
|
|
background: linear-gradient(135deg, #fb923c, #f97316); |
|
|
|
} |
|
|
|
|
|
|
|
&.icon-red { |
|
|
|
background: linear-gradient(135deg, #f87171, #ef4444); |
|
|
|
} |
|
|
|
|
|
|
|
&.icon-green { |
|
|
|
background: linear-gradient(135deg, #34d399, #10b981); |
|
|
|
} |
|
|
|
|
|
|
|
&.icon-purple { |
|
|
|
background: linear-gradient(135deg, #a78bfa, #8b5cf6); |
|
|
|
} |
|
|
|
} |
|
|
|
.item-right{ |
|
|
|
font-size: 22px; |
|
|
|
color: #a6a6a6; |
|
|
|
} |
|
|
|
/* 图标背景色 */ |
|
|
|
.icon-blue { |
|
|
|
background: url('@/assets/images/icon_pffw.jpg') center center no-repeat; |
|
|
|
background-size: 100% 100%; |
|
|
|
} |
|
|
|
|
|
|
|
.icon-orange { |
|
|
|
// background: url('@/assets/images/icon_zfyg.jpg') center center no-repeat; |
|
|
|
// background-size: 100% 100%; |
|
|
|
} |
|
|
|
|
|
|
|
.icon-red { |
|
|
|
background: url('@/assets/images/icon_zfpj.jpg') center center no-repeat; |
|
|
|
background-size: 100% 100%; |
|
|
|
} |
|
|
|
|
|
|
|
/* 红点角标 */ |
|
|
|
.icon-badge { |
|
|
|
|