优化导航组件:新增消息列表功能,定时获取未读消息数量,支持标记消息为已读和全部已读,提升用户体验和代码可读性。
This commit is contained in:
16
Touchkebao/src/pages/pc/ckbox/components/NavCommon/api.ts
Normal file
16
Touchkebao/src/pages/pc/ckbox/components/NavCommon/api.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import request from "@/api/request";
|
||||
|
||||
// 消息列表
|
||||
export const noticeList = (params: { page: number; limit: number }) => {
|
||||
return request(`/v1/kefu/notice/list`, params, "GET");
|
||||
};
|
||||
|
||||
// 消息列表
|
||||
export const readMessage = (params: { id: number }) => {
|
||||
return request(`/v1/kefu/notice/readMessage`, params, "PUT");
|
||||
};
|
||||
|
||||
// 消息列表
|
||||
export const readAll = () => {
|
||||
return request(`/v1/kefu/notice/readAll`, undefined, "PUT");
|
||||
};
|
||||
@@ -1,16 +1,24 @@
|
||||
import React, { useState } from "react";
|
||||
import { Layout, Drawer, Avatar, Space, Button, Badge, Dropdown } from "antd";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Layout,
|
||||
Drawer,
|
||||
Avatar,
|
||||
Space,
|
||||
Button,
|
||||
Badge,
|
||||
Dropdown,
|
||||
Empty,
|
||||
} from "antd";
|
||||
import {
|
||||
BarChartOutlined,
|
||||
UserOutlined,
|
||||
BellOutlined,
|
||||
LogoutOutlined,
|
||||
UserSwitchOutlined,
|
||||
ThunderboltOutlined,
|
||||
SettingOutlined,
|
||||
WechatOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
import { noticeList, readMessage, readAll } from "./api";
|
||||
import { useUserStore } from "@/store/module/user";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import styles from "./index.module.scss";
|
||||
@@ -22,13 +30,39 @@ interface NavCommonProps {
|
||||
onMenuClick?: () => void;
|
||||
}
|
||||
|
||||
// 消息数据类型
|
||||
interface MessageItem {
|
||||
id: number;
|
||||
type: number;
|
||||
companyId: number;
|
||||
userId: number;
|
||||
bindId: number;
|
||||
title: string;
|
||||
message: string;
|
||||
isRead: number;
|
||||
createTime: string;
|
||||
readTime: string;
|
||||
friendData: {
|
||||
nickname: string;
|
||||
avatar: string;
|
||||
};
|
||||
}
|
||||
|
||||
const NavCommon: React.FC<NavCommonProps> = ({ title = "触客宝" }) => {
|
||||
const [messageDrawerVisible, setMessageDrawerVisible] = useState(false);
|
||||
const [messageCount] = useState(3); // 模拟消息数量
|
||||
const [messageList, setMessageList] = useState<MessageItem[]>([]);
|
||||
const [messageCount, setMessageCount] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { user, logout } = useUserStore();
|
||||
|
||||
// 初始化时获取消息列表
|
||||
useEffect(() => {
|
||||
fetchMessageList();
|
||||
setInterval(IntervalMessageCount, 30 * 1000);
|
||||
}, []);
|
||||
|
||||
// 处理菜单图标点击:在两个路由之间切换
|
||||
const handleMenuClick = () => {
|
||||
const current = location.pathname;
|
||||
@@ -43,9 +77,41 @@ const NavCommon: React.FC<NavCommonProps> = ({ title = "触客宝" }) => {
|
||||
return location.pathname.startsWith("/pc/weChat");
|
||||
};
|
||||
|
||||
// 定时器获取消息条数
|
||||
const IntervalMessageCount = async () => {
|
||||
try {
|
||||
const response = await noticeList({ page: 1, limit: 20 });
|
||||
if (response && response.noRead) {
|
||||
setMessageCount(response.noRead);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取消息列表失败:", error);
|
||||
}
|
||||
};
|
||||
// 获取消息列表
|
||||
const fetchMessageList = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await noticeList({ page: 1, limit: 20 });
|
||||
if (response && response.list) {
|
||||
setMessageList(response.list);
|
||||
// 计算未读消息数量
|
||||
const unreadCount = response.list.filter(
|
||||
(item: MessageItem) => item.isRead === 0,
|
||||
).length;
|
||||
setMessageCount(unreadCount);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取消息列表失败:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理消息中心点击
|
||||
const handleMessageClick = () => {
|
||||
setMessageDrawerVisible(true);
|
||||
fetchMessageList();
|
||||
};
|
||||
|
||||
// 处理消息抽屉关闭
|
||||
@@ -59,6 +125,61 @@ const NavCommon: React.FC<NavCommonProps> = ({ title = "触客宝" }) => {
|
||||
navigate("/login"); // 跳转到登录页面
|
||||
};
|
||||
|
||||
// 处理消息已读
|
||||
const handleReadMessage = async (messageId: number) => {
|
||||
try {
|
||||
await readMessage({ id: messageId }); // 这里需要根据实际API调整参数
|
||||
// 更新本地状态
|
||||
setMessageList(prev =>
|
||||
prev.map(item =>
|
||||
item.id === messageId ? { ...item, isRead: 1 } : item,
|
||||
),
|
||||
);
|
||||
// 重新计算未读数量
|
||||
const unreadCount =
|
||||
messageList.filter(item => item.isRead === 0).length - 1;
|
||||
setMessageCount(Math.max(0, unreadCount));
|
||||
} catch (error) {
|
||||
console.error("标记消息已读失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理全部已读
|
||||
const handleReadAll = async () => {
|
||||
try {
|
||||
await readAll(); // 这里需要根据实际API调整参数
|
||||
// 更新本地状态
|
||||
setMessageList(prev => prev.map(item => ({ ...item, isRead: 1 })));
|
||||
setMessageCount(0);
|
||||
} catch (error) {
|
||||
console.error("全部已读失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (timeStr: string) => {
|
||||
const date = new Date(timeStr);
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - date.getTime();
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (days === 0) {
|
||||
return date.toLocaleTimeString("zh-CN", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
} else if (days === 1) {
|
||||
return "昨天";
|
||||
} else if (days < 7) {
|
||||
return `${days}天前`;
|
||||
} else {
|
||||
return date.toLocaleDateString("zh-CN", {
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 用户菜单项
|
||||
const userMenuItems = [
|
||||
{
|
||||
@@ -120,14 +241,7 @@ const NavCommon: React.FC<NavCommonProps> = ({ title = "触客宝" }) => {
|
||||
<BellOutlined style={{ fontSize: 20 }} />
|
||||
</Badge>
|
||||
</div>
|
||||
{/* <Button
|
||||
onClick={() => {
|
||||
navigate("/pc/commonConfig");
|
||||
}}
|
||||
icon={<SettingOutlined />}
|
||||
>
|
||||
全局配置
|
||||
</Button> */}
|
||||
|
||||
<Dropdown
|
||||
menu={{ items: userMenuItems }}
|
||||
placement="bottomRight"
|
||||
@@ -160,74 +274,57 @@ const NavCommon: React.FC<NavCommonProps> = ({ title = "触客宝" }) => {
|
||||
className={styles.messageDrawer}
|
||||
extra={
|
||||
<Space>
|
||||
<Button type="text" size="small">
|
||||
<Button type="text" size="small" onClick={handleReadAll}>
|
||||
全部已读
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<div className={styles.messageContent}>
|
||||
<div className={styles.messageItem}>
|
||||
<div className={styles.messageAvatar}>
|
||||
<Avatar size={40} style={{ backgroundColor: "#87d068" }}>
|
||||
林
|
||||
</Avatar>
|
||||
{loading ? (
|
||||
<div style={{ textAlign: "center", padding: "20px" }}>
|
||||
加载中...
|
||||
</div>
|
||||
<div className={styles.messageInfo}>
|
||||
<div className={styles.messageTitle}>
|
||||
<span className={styles.messageType}>新消息</span>
|
||||
<div className={styles.messageStatus}></div>
|
||||
) : messageList.length === 0 ? (
|
||||
<Empty description="暂无消息" />
|
||||
) : (
|
||||
messageList.map(item => (
|
||||
<div
|
||||
key={item.id}
|
||||
className={`${styles.messageItem} ${
|
||||
item.isRead === 0 ? styles.unread : ""
|
||||
}`}
|
||||
onClick={() => handleReadMessage(item.id)}
|
||||
>
|
||||
<div className={styles.messageAvatar}>
|
||||
<Avatar
|
||||
size={40}
|
||||
src={item.friendData?.avatar}
|
||||
style={{ backgroundColor: "#87d068" }}
|
||||
>
|
||||
{item.friendData?.nickname?.charAt(0) || "U"}
|
||||
</Avatar>
|
||||
</div>
|
||||
<div className={styles.messageInfo}>
|
||||
<div className={styles.messageTitle}>
|
||||
<span className={styles.messageType}>{item.title}</span>
|
||||
{item.isRead === 0 && (
|
||||
<div className={styles.messageStatus}></div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.messageText}>{item.message}</div>
|
||||
{item.isRead === 0 && (
|
||||
<div className={styles.messageTime}>
|
||||
{formatTime(item.createTime)}
|
||||
<Button type="link" size="small">
|
||||
标记已读
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.messageText}>
|
||||
林秀杰:关于自由主时间特惠,原价1999元,现在只需1699元
|
||||
</div>
|
||||
<div className={styles.messageTime}>
|
||||
03-05
|
||||
<Button type="link" size="small">
|
||||
回复
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.messageItem}>
|
||||
<div className={styles.messageAvatar}>
|
||||
<Avatar size={40} style={{ backgroundColor: "#f56a00" }}>
|
||||
E
|
||||
</Avatar>
|
||||
</div>
|
||||
<div className={styles.messageInfo}>
|
||||
<div className={styles.messageTitle}>
|
||||
<span className={styles.messageType}>群提醒</span>
|
||||
<div className={styles.messageStatus}></div>
|
||||
</div>
|
||||
<div className={styles.messageText}>
|
||||
Eric在「云归营私域银行项目群」中@了您
|
||||
</div>
|
||||
<div className={styles.messageTime}>03-05</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.messageItem}>
|
||||
<div className={styles.messageAvatar}>
|
||||
<Avatar size={40} style={{ backgroundColor: "#1890ff" }}>
|
||||
李
|
||||
</Avatar>
|
||||
</div>
|
||||
<div className={styles.messageInfo}>
|
||||
<div className={styles.messageTitle}>
|
||||
<span className={styles.messageType}>好友申请</span>
|
||||
</div>
|
||||
<div className={styles.messageText}>李翔想请求添加您为好友</div>
|
||||
<div className={styles.messageTime}>03-04</div>
|
||||
<div className={styles.messageActions}>
|
||||
<Button type="primary" size="small">
|
||||
接受
|
||||
</Button>
|
||||
<Button size="small">拒绝</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</Drawer>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user