瀏覽代碼

新增消息页面组件,整合消息API,支持获取消息列表、未读消息数量、标记已读和删除消息功能,提升用户消息管理体验。

zyh 8 月之前
父節點
當前提交
4a63d22955
共有 3 個文件被更改,包括 204 次插入21 次删除
  1. 96 1
      client/mobile/api.ts
  2. 1 20
      client/mobile/mobile_app.tsx
  3. 107 0
      client/mobile/pages_messages.tsx

+ 96 - 1
client/mobile/api.ts

@@ -4,7 +4,8 @@ import 'dayjs/locale/zh-cn';
 import type { 
   User, FileLibrary, FileCategory, ThemeSettings,
   SystemSetting, SystemSettingGroupData, 
-  LoginLocation, LoginLocationDetail
+  LoginLocation, LoginLocationDetail,
+  Message, MessageType, MessageStatus
 } from '../share/types.ts';
 
 
@@ -575,6 +576,7 @@ export const SystemAPI = {
     } catch (error) {
       throw error;
     }
+    
   },
 
   // 更新系统设置
@@ -598,3 +600,96 @@ export const SystemAPI = {
   }
 };
 
+
+// 消息API响应类型
+export interface MessageResponse {
+  message: string;
+  data?: any;
+}
+
+export interface MessagesResponse {
+  data: Message[];
+  pagination: {
+    total: number;
+    current: number;
+    pageSize: number;
+    totalPages: number;
+  };
+}
+
+export interface UnreadCountResponse {
+  count: number;
+}
+
+// 消息API
+export const MessageAPI = {
+  // 获取消息列表
+  getMessages: async (params?: {
+    page?: number,
+    pageSize?: number,
+    type?: MessageType,
+    status?: MessageStatus
+  }): Promise<MessagesResponse> => {
+    try {
+      const response = await axios.get(`${API_BASE_URL}/messages`, { params });
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 获取消息详情
+  getMessage: async (id: number): Promise<MessageResponse> => {
+    try {
+      const response = await axios.get(`${API_BASE_URL}/messages/${id}`);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 发送消息
+  sendMessage: async (data: {
+    title: string,
+    content: string,
+    type: MessageType,
+    receiver_ids: number[]
+  }): Promise<MessageResponse> => {
+    try {
+      const response = await axios.post(`${API_BASE_URL}/messages`, data);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 删除消息(软删除)
+  deleteMessage: async (id: number): Promise<MessageResponse> => {
+    try {
+      const response = await axios.delete(`${API_BASE_URL}/messages/${id}`);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 获取未读消息数量
+  getUnreadCount: async (): Promise<UnreadCountResponse> => {
+    try {
+      const response = await axios.get(`${API_BASE_URL}/messages/unread-count`);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  },
+
+  // 标记消息为已读
+  markAsRead: async (id: number): Promise<MessageResponse> => {
+    try {
+      const response = await axios.post(`${API_BASE_URL}/messages/${id}/read`);
+      return response.data;
+    } catch (error) {
+      throw error;
+    }
+  }
+};

+ 1 - 20
client/mobile/mobile_app.tsx

@@ -18,7 +18,7 @@ import HomePage from './pages_index.tsx';
 import LoginPage from './pages_login.tsx';
 import { GlobalConfig } from "../share/types.ts";
 import { ExclamationTriangleIcon, HomeIcon, BellIcon, UserIcon } from '@heroicons/react/24/outline';
-
+import { NotificationsPage } from './pages_messages.tsx';
 // 设置中文语言
 dayjs.locale('zh-cn');
 
@@ -258,25 +258,6 @@ const ProfilePage = () => {
   );
 };
 
-// 添加通知页面组件
-const NotificationsPage = () => (
-  <div className="p-4">
-    <h1 className="text-2xl font-bold mb-4">通知</h1>
-    <div className="bg-white rounded-lg shadow divide-y">
-      <div className="p-4">
-        <h3 className="font-medium">系统通知</h3>
-        <p className="text-gray-500 text-sm mt-1">欢迎使用移动应用!</p>
-        <p className="text-xs text-gray-400 mt-2">今天 10:00</p>
-      </div>
-      <div className="p-4">
-        <h3 className="font-medium">活动提醒</h3>
-        <p className="text-gray-500 text-sm mt-1">您有一个新的活动邀请</p>
-        <p className="text-xs text-gray-400 mt-2">昨天 14:30</p>
-      </div>
-    </div>
-  </div>
-);
-
 // 移动端布局组件 - 包含底部导航
 const MobileLayout = () => {
   const location = useLocation();

+ 107 - 0
client/mobile/pages_messages.tsx

@@ -0,0 +1,107 @@
+import React from 'react';
+import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
+import dayjs from 'dayjs';
+import 'dayjs/locale/zh-cn';
+import { BellIcon } from '@heroicons/react/24/outline';
+
+
+// 添加通知页面组件
+import { MessageAPI } from './api.ts';
+
+export const NotificationsPage = () => {
+  const queryClient = useQueryClient();
+
+  // 获取消息列表
+  const { data: messages, isLoading } = useQuery({
+    queryKey: ['messages'],
+    queryFn: () => MessageAPI.getMessages(),
+  });
+
+  // 获取未读消息数量
+  const { data: unreadCount } = useQuery({
+    queryKey: ['unreadCount'],
+    queryFn: () => MessageAPI.getUnreadCount(),
+  });
+
+  // 标记消息为已读
+  const markAsReadMutation = useMutation({
+    mutationFn: (id: number) => MessageAPI.markAsRead(id),
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['messages'] });
+      queryClient.invalidateQueries({ queryKey: ['unreadCount'] });
+    },
+  });
+
+  // 删除消息
+  const deleteMutation = useMutation({
+    mutationFn: (id: number) => MessageAPI.deleteMessage(id),
+    onSuccess: () => {
+      queryClient.invalidateQueries({ queryKey: ['messages'] });
+    },
+  });
+
+  const handleMarkAsRead = (id: number) => {
+    markAsReadMutation.mutate(id);
+  };
+
+  const handleDelete = (id: number) => {
+    if (confirm('确定要删除这条消息吗?')) {
+      deleteMutation.mutate(id);
+    }
+  };
+
+  if (isLoading) {
+    return (
+      <div className="p-4">
+        <h1 className="text-2xl font-bold mb-4">通知</h1>
+        <div className="flex justify-center items-center h-40">
+          <div className="w-8 h-8 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
+        </div>
+      </div>
+    );
+  }
+
+  return (
+    <div className="p-4">
+      <div className="flex items-center justify-between mb-4">
+        <h1 className="text-2xl font-bold">通知</h1>
+        {unreadCount && unreadCount.count > 0 ? (
+          <div className="flex items-center">
+            <BellIcon className="h-5 w-5 text-red-500 mr-1" />
+            <span className="text-sm text-red-500">{unreadCount.count}条未读</span>
+          </div>
+        ) : null}
+      </div>
+
+      <div className="bg-white rounded-lg shadow divide-y">
+        {messages?.data.map((message) => (
+          <div key={message.id} className="p-4">
+            <div className="flex justify-between items-start">
+              <h3 className="font-medium">{message.title}</h3>
+              <div className="flex space-x-2">
+                <button
+                  type="button"
+                  onClick={() => handleMarkAsRead(message.id)}
+                  className="text-xs text-blue-600 hover:text-blue-800"
+                >
+                  标记已读
+                </button>
+                <button
+                  type="button"
+                  onClick={() => handleDelete(message.id)}
+                  className="text-xs text-red-600 hover:text-red-800"
+                >
+                  删除
+                </button>
+              </div>
+            </div>
+            <p className="text-gray-500 text-sm mt-1">{message.content}</p>
+            <p className="text-xs text-gray-400 mt-2">
+              {dayjs(message.created_at).format('YYYY-MM-DD HH:mm')}
+            </p>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+};