hookehuyr

feat(消息中心): 实现消息列表接口集成和未读消息显示

添加消息相关API接口文件并集成到消息中心页面
在个人中心页面显示未读消息数量
优化消息列表页面样式和空状态提示
1 +/*
2 + * @Date: 2025-06-12 14:11:09
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2025-06-12 14:12:22
5 + * @FilePath: /mlaj/src/api/news.js
6 + * @Description: 消息相关接口
7 + */
8 +import { fn, fetch } from './fn'
9 +
10 +const Api = {
11 + NEWS_LIST: '/srv/?a=website_msg&t=my_list',
12 +}
13 +
14 +/**
15 + * @description: 我的消息列表
16 + */
17 +export const getNewsListAPI = (params) => fn(fetch.get(Api.NEWS_LIST, params))
1 <!-- 1 <!--
2 * @Date: 2025-03-24 12:56:07 2 * @Date: 2025-03-24 12:56:07
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-06-12 10:53:01 4 + * @LastEditTime: 2025-06-12 15:34:41
5 * @FilePath: /mlaj/src/views/profile/MessagesPage.vue 5 * @FilePath: /mlaj/src/views/profile/MessagesPage.vue
6 * @Description: 消息中心页面 6 * @Description: 消息中心页面
7 --> 7 -->
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
11 <van-list 11 <van-list
12 v-model:loading="loading" 12 v-model:loading="loading"
13 :finished="finished" 13 :finished="finished"
14 - finished-text="没有更多了" 14 + :finished-text="finishText"
15 @load="onLoad" 15 @load="onLoad"
16 class="px-4 py-4" 16 class="px-4 py-4"
17 > 17 >
...@@ -24,18 +24,38 @@ ...@@ -24,18 +24,38 @@
24 <div class="p-4"> 24 <div class="p-4">
25 <div class="flex justify-between items-start mb-2"> 25 <div class="flex justify-between items-start mb-2">
26 <h3 class="text-base font-medium flex items-center"> 26 <h3 class="text-base font-medium flex items-center">
27 - <span 27 + <span v-if="!message.isRead" class="w-2 h-2 bg-red-500 rounded-full mr-2"></span>
28 - v-if="!message.isRead" 28 + <!-- {{ message.title }} -->
29 - class="w-2 h-2 bg-red-500 rounded-full mr-2" 29 + 消息
30 - ></span>
31 - {{ message.title }}
32 </h3> 30 </h3>
33 - <span class="text-xs text-gray-500">{{ message.time }}</span> 31 + <span class="text-xs text-gray-500">{{ message.created_time }}</span>
34 </div> 32 </div>
35 - <p class="text-sm text-gray-600 line-clamp-2">{{ message.content }}</p> 33 + <p class="text-sm text-gray-600 line-clamp-2">{{ message.note }}</p>
36 </div> 34 </div>
37 </FrostedGlass> 35 </FrostedGlass>
38 </van-list> 36 </van-list>
37 +
38 + <!-- 无数据提示 -->
39 + <div
40 + v-if="!loading && messages.length === 0"
41 + class="flex flex-col items-center justify-center py-12"
42 + >
43 + <svg
44 + xmlns="http://www.w3.org/2000/svg"
45 + class="h-16 w-16 text-gray-300"
46 + fill="none"
47 + viewBox="0 0 24 24"
48 + stroke="currentColor"
49 + >
50 + <path
51 + stroke-linecap="round"
52 + stroke-linejoin="round"
53 + stroke-width="2"
54 + d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
55 + />
56 + </svg>
57 + <p class="mt-4 text-gray-500">暂无消息</p>
58 + </div>
39 </div> 59 </div>
40 </AppLayout> 60 </AppLayout>
41 </template> 61 </template>
...@@ -47,6 +67,9 @@ import AppLayout from '@/components/layout/AppLayout.vue'; ...@@ -47,6 +67,9 @@ import AppLayout from '@/components/layout/AppLayout.vue';
47 import FrostedGlass from '@/components/ui/FrostedGlass.vue'; 67 import FrostedGlass from '@/components/ui/FrostedGlass.vue';
48 import { useTitle } from '@vueuse/core'; 68 import { useTitle } from '@vueuse/core';
49 69
70 +// 导入接口
71 +import { getNewsListAPI } from '@/api/news';
72 +
50 const $route = useRoute(); 73 const $route = useRoute();
51 const $router = useRouter(); 74 const $router = useRouter();
52 useTitle($route.meta.title); 75 useTitle($route.meta.title);
...@@ -55,46 +78,23 @@ const router = useRouter(); ...@@ -55,46 +78,23 @@ const router = useRouter();
55 const loading = ref(false); 78 const loading = ref(false);
56 const finished = ref(false); 79 const finished = ref(false);
57 const messages = ref([]); 80 const messages = ref([]);
81 +const limit = ref(10);
58 const page = ref(0); 82 const page = ref(0);
59 - 83 +const finishText = ref('没有更多了');
60 -// 模拟消息数据
61 -const mockMessages = [
62 - {
63 - id: 1,
64 - title: '课程更新通知',
65 - content: '您关注的课程《亲子教育入门》已更新最新一期内容,快来查看吧!',
66 - time: '2024-01-10 10:30',
67 - isRead: false
68 - },
69 - {
70 - id: 2,
71 - title: '活动提醒',
72 - content: '您报名的「亲子互动工作坊」将于本周六下午2点开始,请准时参加。',
73 - time: '2024-01-09 15:20',
74 - isRead: true
75 - },
76 - {
77 - id: 3,
78 - title: '系统通知',
79 - content: '为了给您提供更好的服务,我们的App将于今晚凌晨2点进行系统升级维护。',
80 - time: '2024-01-08 18:00',
81 - isRead: false
82 - }
83 -];
84 84
85 // 加载更多 85 // 加载更多
86 -const onLoad = () => { 86 +const onLoad = async() => {
87 - // 模拟异步加载数据 87 + const res = await getNewsListAPI({ page: page.value, limit: limit.value });
88 - setTimeout(() => { 88 + if (res.code) {
89 - if (page.value === 0) { 89 + messages.value = [...messages.value, ...res.data];
90 - messages.value = mockMessages; 90 + finished.value = res.data.length < limit.value;
91 - } else {
92 - // 模拟没有更多数据
93 - finished.value = true;
94 - }
95 - loading.value = false;
96 page.value++; 91 page.value++;
97 - }, 1000); 92 + loading.value = false;
93 + } else {
94 + loading.value = false;
95 + finished.value = true;
96 + finishText.value = '';
97 + }
98 }; 98 };
99 99
100 // 更新消息已读状态 100 // 更新消息已读状态
...@@ -107,6 +107,7 @@ const updateMessageReadStatus = (messageId) => { ...@@ -107,6 +107,7 @@ const updateMessageReadStatus = (messageId) => {
107 107
108 // 处理消息点击 108 // 处理消息点击
109 const handleMessageClick = (message) => { 109 const handleMessageClick = (message) => {
110 + return;
110 updateMessageReadStatus(message.id); 111 updateMessageReadStatus(message.id);
111 router.push(`/profile/messages/${message.id}`); 112 router.push(`/profile/messages/${message.id}`);
112 }; 113 };
......
...@@ -143,6 +143,12 @@ onMounted(async () => { ...@@ -143,6 +143,12 @@ onMounted(async () => {
143 if (code) { 143 if (code) {
144 profile.value = data.user; 144 profile.value = data.user;
145 checkIns.value = data.checkin; 145 checkIns.value = data.checkin;
146 + // 处理消息中心的未读消息数量
147 + menuItems3.value.forEach(item => {
148 + if (item.path === '/profile/messages') {
149 + item.badge = data.unread_msg_count || 0;
150 + }
151 + });
146 } 152 }
147 }) 153 })
148 154
...@@ -221,7 +227,7 @@ const menuItems1 = [ ...@@ -221,7 +227,7 @@ const menuItems1 = [
221 icon: "clock", 227 icon: "clock",
222 title: "学习记录", 228 title: "学习记录",
223 path: "/profile/learning-records", 229 path: "/profile/learning-records",
224 - badge: "NEW", 230 + badge: "",
225 }, 231 },
226 { 232 {
227 icon: "user", 233 icon: "user",
...@@ -258,12 +264,12 @@ const menuItems2 = [ ...@@ -258,12 +264,12 @@ const menuItems2 = [
258 }, 264 },
259 ]; 265 ];
260 266
261 -const menuItems3 = [ 267 +const menuItems3 = ref([
262 { 268 {
263 icon: "email", 269 icon: "email",
264 title: "消息中心", 270 title: "消息中心",
265 path: "/profile/messages", 271 path: "/profile/messages",
266 - badge: "3", 272 + badge: "",
267 }, 273 },
268 { 274 {
269 icon: "question", 275 icon: "question",
...@@ -275,7 +281,7 @@ const menuItems3 = [ ...@@ -275,7 +281,7 @@ const menuItems3 = [
275 title: "设置", 281 title: "设置",
276 path: "/profile/settings", 282 path: "/profile/settings",
277 }, 283 },
278 -]; 284 +]);
279 285
280 const handleCheckin = () => { 286 const handleCheckin = () => {
281 if (checkinData.value.length) { 287 if (checkinData.value.length) {
......