123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- import React, { useState, useRef, useEffect } from "react";
- import { Modal, Button, Tabs, Empty, message } from "antd";
- import type { TabsProps } from "antd";
- import axios from "axios";
- import { MESSAGE_STATUS, MESSAGE_TAB } from "@/constants";
- import { useNotificationStore } from "@/store/NotificationStore";
- import { Eraser, FileText } from "lucide-react";
- import dayjs from "dayjs";
- 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,
- label: <span className="flex">全部</span>,
- },
- {
- key: MESSAGE_TAB.UNREAD,
- label: (
- <span className="flex">
- 未读
- {!!unreadNotifications.length && (
- <span className="ml-1 text-xs text-white bg-red-500 rounded-full w-5 h-5 flex justify-center items-center">
- {renderNoticeLength(unreadNotifications)}
- </span>
- )}
- </span>
- ),
- },
- {
- key: MESSAGE_TAB.HAS_READ,
- label: <span className="flex">已读</span>,
- },
- ];
- // 简化 Tab 切换处理函数
- const handleTabChange = (key: string) => {
- setActiveKey(key);
- setHasMore(true); // 切换tab时重置hasMore状态
- };
- return (
- <Modal
- title={
- <div className="flex items-center">
- <span>
- 消息中心
- {!!renderNoticeLength(unreadNotifications) &&
- `(${renderNoticeLength(unreadNotifications)})`}
- </span>
- <div
- className="cursor-pointer hover:bg-gray-100 rounded-md px-2 py-1 ml-2 text-xs flex justify-center items-center gap-1"
- onClick={async () => {
- const baseUrl =
- import.meta.env.MODE === "development"
- ? "/notification"
- : import.meta.env.VITE_NOTIFICATION_URL;
- const res = await axios.post(`${baseUrl}/bazb/clearMsg`);
- if (res.data.code === 200) {
- changeNotification({ type: "clear" });
- }
- }}
- >
- <Eraser size={12} />
- 清除消息
- </div>
- </div>
- }
- open={visible}
- onCancel={onClose}
- footer={null}
- style={{
- top: 30,
- maxWidth: "90%",
- maxHeight: "80%",
- }}
- >
- <div className="flex flex-col h-[80vh]">
- <Tabs
- activeKey={activeKey}
- items={tabItems}
- className="mb-4"
- onChange={handleTabChange}
- />
- <div className="space-y-4 max-h-[100%] overflow-auto">
- {(() => {
- const filteredNotifications = getFilteredNotifications();
- return (
- <>
- {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.push_time).format(
- "YYYY-MM-DD HH:mm:ss"
- )}
- </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>
- </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>
- ))}
- {loading && (
- <div className="flex justify-center items-center py-4">
- <div className="text-gray-500">加载中...</div>
- </div>
- )}
- {hasMore && (
- <div ref={observerRef} style={{ height: "20px" }} />
- )}
- </>
- )}
- </>
- );
- })()}
- </div>
- </div>
- </Modal>
- );
- };
- export default MessageModal;
|