Browse Source

feat: 增加下拉加载更多功能

banxia 1 tháng trước cách đây
mục cha
commit
00a6eaca06

+ 5 - 1
electron/main/tray.ts

@@ -73,7 +73,11 @@ export const initTray = () => {
     const userObj = JSON.parse(fixedData);
     const msgCount = userObj.count;
     if (msgCount <= 9) {
-      appIcon.setImage(ICONS[`icon${msgCount}`]);
+      if (msgCount === 0) {
+        appIcon.setImage(ICONS.default);
+      } else {
+        appIcon.setImage(ICONS[`icon${msgCount}`]);
+      }
     } else {
       appIcon.setImage(ICONS[`icon9plus`]);
     }

+ 144 - 66
src/components/MessageModal/MessageModal.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React, { useState, useRef, useEffect } from "react";
 import { Modal, Button, Tabs, Empty, message } from "antd";
 import type { TabsProps } from "antd";
 import axios from "axios";
@@ -11,18 +11,90 @@ import { renderNoticeLength } from "@/utils/utils";
 interface MessageModalProps {
   visible: boolean;
   onClose: () => void;
+  fetchNoticeList: (id?: number, isRead?: number) => Promise<any[]>;
 }
 
 const MessageModal: React.FC<MessageModalProps> = ({
   visible,
   onClose,
+  fetchNoticeList,
 }) => {
   const [activeKey, setActiveKey] = useState(MESSAGE_TAB.UNREAD);
   const { notifications, changeNotification } = useNotificationStore();
+  const [loading, setLoading] = useState(false);
+  const [hasMore, setHasMore] = useState(true); // 新增状态,用于标记是否还有更多数据
+  const observerRef = useRef<HTMLDivElement>(null);
+
   const unreadNotifications = notifications.filter(
     (notification) => notification.is_read === MESSAGE_STATUS.UNREAD
   );
 
+  // 根据activeKey获取对应的isRead值
+  const getIsReadStatus = (tabKey: string) => {
+    switch (tabKey) {
+      case MESSAGE_TAB.UNREAD:
+        return MESSAGE_STATUS.UNREAD;
+      case MESSAGE_TAB.HAS_READ:
+        return MESSAGE_STATUS.HAS_READ;
+      default:
+        return undefined; // 全部消息时不传isRead参数
+    }
+  };
+
+  useEffect(() => {
+    const observer = new IntersectionObserver(
+      async (entries) => {
+        const target = entries[0];
+        if (target.isIntersecting && !loading && hasMore) {
+          const filteredNotifications = getFilteredNotifications();
+          if (filteredNotifications.length > 0) {
+            setLoading(true);
+            const lastId =
+              filteredNotifications[filteredNotifications.length - 1].id;
+            try {
+              const isRead = getIsReadStatus(activeKey);
+              const newMessages = await fetchNoticeList(lastId, isRead);
+              if (newMessages.length === 0) {
+                setHasMore(false);
+                setLoading(false);
+                return;
+              }
+            } catch (error) {
+              console.error("Failed to fetch more messages:", error);
+            }
+            setLoading(false);
+          }
+        }
+      },
+      {
+        threshold: 0.1,
+      }
+    );
+
+    if (observerRef.current) {
+      observer.observe(observerRef.current);
+    }
+
+    return () => observer.disconnect();
+  }, [loading, fetchNoticeList, activeKey, hasMore]); // 添加hasMore作为依赖
+
+  // 获取过滤后的通知列表
+  const getFilteredNotifications = () => {
+    let filteredNotifications = notifications;
+
+    if (activeKey === MESSAGE_TAB.UNREAD) {
+      filteredNotifications = notifications.filter(
+        (notification) => notification.is_read === MESSAGE_STATUS.UNREAD
+      );
+    } else if (activeKey === MESSAGE_TAB.HAS_READ) {
+      filteredNotifications = notifications.filter(
+        (notification) => notification.is_read === MESSAGE_STATUS.HAS_READ
+      );
+    }
+
+    return filteredNotifications;
+  };
+
   const tabItems: TabsProps["items"] = [
     {
       key: MESSAGE_TAB.ALL,
@@ -47,6 +119,12 @@ const MessageModal: React.FC<MessageModalProps> = ({
     },
   ];
 
+  // 简化 Tab 切换处理函数
+  const handleTabChange = (key: string) => {
+    setActiveKey(key);
+    setHasMore(true); // 切换tab时重置hasMore状态
+  };
+
   return (
     <Modal
       title={
@@ -84,83 +162,83 @@ const MessageModal: React.FC<MessageModalProps> = ({
           activeKey={activeKey}
           items={tabItems}
           className="mb-4"
-          onChange={(key) => {
-            setActiveKey(key);
-          }}
+          onChange={handleTabChange}
         />
         <div className="space-y-4 max-h-[100%] overflow-auto">
           {(() => {
-            let filteredNotifications = notifications;
-
-            if (activeKey === MESSAGE_TAB.UNREAD) {
-              filteredNotifications = notifications.filter(
-                (notification) => notification.is_read === MESSAGE_STATUS.UNREAD
-              );
-            } else if (activeKey === MESSAGE_TAB.HAS_READ) {
-              filteredNotifications = notifications.filter(
-                (notification) =>
-                  notification.is_read === MESSAGE_STATUS.HAS_READ
-              );
-            }
+            const filteredNotifications = getFilteredNotifications();
 
             return (
               <>
-                {filteredNotifications.length === 0 && (
+                {filteredNotifications.length === 0 ? (
                   <div className="flex justify-center items-center h-full">
                     <Empty description="暂无消息" />
                   </div>
-                )}
-                {filteredNotifications.map((message, index) => (
-                  <div
-                    key={index}
-                    className="p-4 bg-gray-50 rounded-lg shadow-sm flex flex-col gap-4"
-                  >
-                    <div className="flex justify-between items-center">
-                      <div className="flex gap-1 items-center">
-                        <div className="p-1 bg-blue-500 rounded-full w-6 h-6 text-white flex justify-center items-center">
-                          <FileText size={14} />
-                        </div>
-                        <div className="font-bold">病例时效提醒</div>
-                      </div>
-                      <div className="text-gray-500 text-xs">
-                        {dayjs(message.timestamp).format("YYYY-MM-DD HH:mm")}
-                      </div>
-                    </div>
-                    <div className="flex items-start space-x-4">
-                      <div className="flex-1">
-                        <div className="flex">
-                          <h4 className="text-sm font-semibold">
-                            {message.title}
-                          </h4>
+                ) : (
+                  <>
+                    {filteredNotifications.map((message, index) => (
+                      <div
+                        key={index}
+                        className="p-4 bg-gray-50 rounded-lg shadow-sm flex flex-col gap-4"
+                      >
+                        <div className="flex justify-between items-center">
+                          <div className="flex gap-1 items-center">
+                            <div className="p-1 bg-blue-500 rounded-full w-6 h-6 text-white flex justify-center items-center">
+                              <FileText size={14} />
+                            </div>
+                            <div className="font-bold">病例时效提醒</div>
+                          </div>
+                          <div className="text-gray-500 text-xs">
+                            {dayjs(message.push_time).format(
+                              "YYYY-MM-DD HH:mm:ss"
+                            )}
+                          </div>
                         </div>
-                        <p className="text-gray-700 mt-1 indent-[1.75rem]">
-                          {message.content}
-                        </p>
-                        <p className="text-gray-700 mt-1 indent-[1.75rem]">
-                          {message.footer || "祝您工作顺利!"}
-                        </p>
-                        <div className="mt-2 flex space-x-2 justify-end">
-                          <Button
-                            type="primary"
-                            size="middle"
-                            onClick={() => {
-                              axios.post("/notification/bazb/clearMsg", {
-                                msgId: message.id,
-                              });
-                              changeNotification({
-                                type: "clear",
-                                notification: [message.id],
-                              });
-                            }}
-                          >
-                            知道了
-                          </Button>
-                          <Button size="middle">去查看</Button>
+                        <div className="flex items-start space-x-4">
+                          <div className="flex-1">
+                            <div className="flex">
+                              <h4 className="text-sm font-semibold">
+                                {message.title}
+                              </h4>
+                            </div>
+                            <p className="text-gray-700 mt-1 indent-[1.75rem]">
+                              {message.content}
+                            </p>
+                            <p className="text-gray-700 mt-1 indent-[1.75rem]">
+                              {message.footer || "祝您工作顺利!"}
+                            </p>
+                            <div className="mt-2 flex space-x-2 justify-end">
+                              <Button
+                                type="primary"
+                                size="middle"
+                                onClick={() => {
+                                  axios.post("/notification/bazb/clearMsg", {
+                                    msgId: message.id,
+                                  });
+                                  changeNotification({
+                                    type: "clear",
+                                    notification: [message.id],
+                                  });
+                                }}
+                              >
+                                知道了
+                              </Button>
+                              <Button size="middle">去查看</Button>
+                            </div>
+                          </div>
                         </div>
                       </div>
-                    </div>
-                  </div>
-                ))}
+                    ))}
+                    {loading && (
+                      <div className="flex justify-center items-center py-4">
+                        <div className="text-gray-500">加载中...</div>
+                      </div>
+                    )}
+                    {hasMore && (
+                      <div ref={observerRef} style={{ height: "20px" }} />
+                    )}
+                  </>
+                )}
               </>
             );
           })()}

+ 14 - 6
src/layouts/Header/index.tsx

@@ -30,12 +30,19 @@ const LayoutHeader = (props) => {
     setScaleFactor(scaleFactor);
   };
 
-  const fetchNoticeList = async () => {
-    const res = await axios.get(`/notification/bazb/getMsgList`);
-    changeNotification({
-      type: "new_msg",
-      notification: res?.data?.data?.list?.data?.data,
-    });
+  const fetchNoticeList = async (id?: number, isRead?: number) => {
+    const res = await axios.get(
+      `/notification/bazb/getMsgList${id ? `?msgId=${id}` : ""}${
+        isRead !== undefined ? `${id ? "&" : "?"}isRead=${isRead}` : ""
+      }`
+    );
+    if (res?.data?.data?.list?.data) {
+      changeNotification({
+        type: "update_msg",
+        notification: res?.data?.data?.list?.data,
+      });
+    }
+    return res?.data?.data?.list?.data || [];
   };
 
   useEffect(() => {
@@ -131,6 +138,7 @@ const LayoutHeader = (props) => {
       <MessageModal
         visible={messageModalVisible}
         onClose={() => setMessageModalVisible(false)}
+        fetchNoticeList={fetchNoticeList}
       />
     </header>
   );

+ 37 - 20
src/store/NotificationStore.ts

@@ -23,27 +23,44 @@ export const useNotificationStore = create<
   ...initialState,
   changeNotification: ({ type, notification }) => {
     let new_notification: any = [];
-    if (["new_msg"].includes(type)) {
-      new_notification = [...get().notifications, ...notification];
-    } else if (type === "clear") {
-      if (!notification) {
-        const notifications = get().notifications;
-        new_notification = notifications.map((item) => ({
-          ...item,
-          is_read: MESSAGE_STATUS.HAS_READ,
-        }));
-      } else {
-        new_notification = get().notifications.map((item) => {
-          if (notification.includes(item.id)) {
-            return {
-              ...item,
-              is_read: MESSAGE_STATUS.HAS_READ,
-            };
-          }
-          return item;
-        });
-      }
+    
+    switch (type) {
+      case "new_msg":
+        // 新消息添加到数组头部
+        new_notification = [...notification, ...get().notifications];
+        break;
+      
+      case "update_msg":
+        // 加载更多的消息添加到数组尾部
+        new_notification = [...get().notifications, ...notification];
+        break;
+      
+      case "clear":
+        if (!notification) {
+          // 清空所有消息
+          const notifications = get().notifications;
+          new_notification = notifications.map((item) => ({
+            ...item,
+            is_read: MESSAGE_STATUS.HAS_READ,
+          }));
+        } else {
+          // 清空指定消息
+          new_notification = get().notifications.map((item) => {
+            if (notification.includes(item.id)) {
+              return {
+                ...item,
+                is_read: MESSAGE_STATUS.HAS_READ,
+              };
+            }
+            return item;
+          });
+        }
+        break;
+        
+      default:
+        new_notification = get().notifications;
     }
+    
     set({ notifications: new_notification });
   },
 }));