feat(ckbox): 实现聊天界面功能并集成WebSocket
添加联系人列表、聊天会话和消息窗口组件 集成WebSocket连接实现实时聊天功能 重构API调用以获取聊天记录和联系人信息 添加样式文件美化聊天界面
This commit is contained in:
@@ -129,9 +129,19 @@ const Login: React.FC = () => {
|
||||
});
|
||||
|
||||
//获取触客宝
|
||||
getToken2().then(() => {
|
||||
getToken2().then((Token: string) => {
|
||||
getChuKeBaoUserInfo().then(res => {
|
||||
setUserInfo(res);
|
||||
// 使用WebSocket store连接
|
||||
const { connect } = useWebSocketStore.getState();
|
||||
connect({
|
||||
accessToken: Token,
|
||||
accountId: getAccountId()?.toString() || "",
|
||||
client: "kefu-client",
|
||||
autoReconnect: true,
|
||||
reconnectInterval: 3000,
|
||||
maxReconnectAttempts: 5,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -19,16 +19,24 @@ export const getContactList = (params: { prevId: number; count: number }) => {
|
||||
return request("/api/wechatFriend/list", params, "GET");
|
||||
};
|
||||
|
||||
// 搜索联系人
|
||||
export const searchContacts = (keyword: string): Promise<ContactData[]> => {
|
||||
return request("/v1/contacts/search", { keyword }, "GET");
|
||||
};
|
||||
|
||||
// 获取聊天会话列表
|
||||
export const getChatSessions = (): Promise<ChatSession[]> => {
|
||||
return request("/v1/chats/sessions", {}, "GET");
|
||||
};
|
||||
|
||||
// 搜索联系人
|
||||
export const getChatMessage = (params: {
|
||||
wechatAccountId: number;
|
||||
wechatFriendId: number;
|
||||
From: number;
|
||||
To: number;
|
||||
Count: number;
|
||||
olderData: boolean;
|
||||
keyword: string;
|
||||
}) => {
|
||||
return request("/api/FriendMessage/SearchMessage", params, "GET");
|
||||
};
|
||||
|
||||
// 获取聊天历史
|
||||
export const getChatHistory = (
|
||||
chatId: string,
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
} from "@ant-design/icons";
|
||||
import dayjs from "dayjs";
|
||||
import { ChatSession, MessageData, MessageType } from "../../data";
|
||||
// import { getChatHistory, sendMessage } from "../api";
|
||||
import { getChatMessage } from "../../api";
|
||||
import styles from "./ChatWindow.module.scss";
|
||||
|
||||
const { Header, Content, Footer, Sider } = Layout;
|
||||
@@ -78,38 +78,65 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
const fetchChatHistory = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// 模拟聊天历史数据
|
||||
const mockMessages: MessageData[] = [
|
||||
{
|
||||
id: "1",
|
||||
senderId: "other",
|
||||
senderName: chat.name,
|
||||
content: "你好,请问有什么可以帮助您的吗?",
|
||||
type: MessageType.TEXT,
|
||||
timestamp: dayjs().subtract(10, "minute").toISOString(),
|
||||
isRead: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
senderId: "me",
|
||||
senderName: "我",
|
||||
content: "我想了解一下你们的产品",
|
||||
type: MessageType.TEXT,
|
||||
timestamp: dayjs().subtract(8, "minute").toISOString(),
|
||||
isRead: true,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
senderId: "other",
|
||||
senderName: chat.name,
|
||||
content: "好的,我来为您详细介绍",
|
||||
type: MessageType.TEXT,
|
||||
timestamp: dayjs().subtract(5, "minute").toISOString(),
|
||||
isRead: true,
|
||||
},
|
||||
];
|
||||
setMessages(mockMessages);
|
||||
|
||||
// 从chat对象中提取wechatFriendId
|
||||
// 假设chat.id存储的是wechatFriendId
|
||||
const wechatFriendId = parseInt(chat.id);
|
||||
|
||||
if (isNaN(wechatFriendId)) {
|
||||
messageApi.error("无效的聊天ID");
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用API获取聊天历史
|
||||
const response = await getChatMessage({
|
||||
wechatAccountId: 32686452, // 使用实际的wechatAccountId
|
||||
wechatFriendId: wechatFriendId,
|
||||
From: 0,
|
||||
To: 0,
|
||||
Count: 50, // 获取最近的50条消息
|
||||
olderData: false,
|
||||
keyword: "",
|
||||
});
|
||||
|
||||
console.log("聊天历史响应:", response);
|
||||
|
||||
if (response && Array.isArray(response)) {
|
||||
// 将API返回的消息记录转换为MessageData格式
|
||||
const chatMessages: MessageData[] = response.map(item => {
|
||||
// 解析content字段,它是一个JSON字符串
|
||||
let msgContent = "";
|
||||
try {
|
||||
const contentObj = JSON.parse(item.content);
|
||||
msgContent = contentObj.content || "";
|
||||
} catch (e) {
|
||||
msgContent = item.content;
|
||||
}
|
||||
|
||||
// 判断消息是发送还是接收
|
||||
const isSend = item.isSend === true;
|
||||
|
||||
return {
|
||||
id: item.id.toString(),
|
||||
senderId: isSend ? "me" : "other",
|
||||
senderName: isSend ? "我" : chat.name,
|
||||
content: msgContent,
|
||||
type: MessageType.TEXT, // 默认为文本类型,实际应根据msgType字段判断
|
||||
timestamp: item.createTime || new Date(item.wechatTime).toISOString(),
|
||||
isRead: true, // 默认已读
|
||||
};
|
||||
});
|
||||
|
||||
// 按时间排序
|
||||
chatMessages.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
||||
|
||||
setMessages(chatMessages);
|
||||
} else {
|
||||
// 如果没有消息,显示空数组
|
||||
setMessages([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取聊天记录失败:", error);
|
||||
messageApi.error("获取聊天记录失败");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
.contactListSimple {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
|
||||
.header {
|
||||
padding: 10px 15px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
|
||||
:global(.ant-list-item) {
|
||||
padding: 10px 15px;
|
||||
border-bottom: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contactItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 15px;
|
||||
|
||||
&.selected {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.avatarContainer {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
background-color: #1890ff;
|
||||
}
|
||||
|
||||
.contactInfo {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.status {
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import { List, Avatar, Badge } from "antd";
|
||||
import { ContactData } from "../../data";
|
||||
import styles from "./ContactListSimple.module.scss";
|
||||
|
||||
interface ContactListSimpleProps {
|
||||
contacts: ContactData[];
|
||||
onContactClick: (contact: ContactData) => void;
|
||||
selectedContactId?: string;
|
||||
}
|
||||
|
||||
const ContactListSimple: React.FC<ContactListSimpleProps> = ({
|
||||
contacts,
|
||||
onContactClick,
|
||||
selectedContactId,
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.contactListSimple}>
|
||||
<div className={styles.header}>全部好友</div>
|
||||
<List
|
||||
className={styles.list}
|
||||
dataSource={contacts}
|
||||
renderItem={contact => (
|
||||
<List.Item
|
||||
key={contact.id}
|
||||
onClick={() => onContactClick(contact)}
|
||||
className={`${styles.contactItem} ${contact.id === selectedContactId ? styles.selected : ""}`}
|
||||
>
|
||||
<div className={styles.avatarContainer}>
|
||||
<Badge dot={contact.online} color="green" offset={[-5, 5]}>
|
||||
<Avatar
|
||||
src={contact.avatar}
|
||||
icon={
|
||||
!contact.avatar && <span>{contact.name.charAt(0)}</span>
|
||||
}
|
||||
className={styles.avatar}
|
||||
/>
|
||||
</Badge>
|
||||
</div>
|
||||
<div className={styles.contactInfo}>
|
||||
<div className={styles.name}>{contact.name}</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContactListSimple;
|
||||
@@ -0,0 +1,63 @@
|
||||
.sidebar {
|
||||
background: #fff;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.searchBar {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background: #fff;
|
||||
|
||||
:global(.ant-input) {
|
||||
border-radius: 20px;
|
||||
background: #f5f5f5;
|
||||
border: none;
|
||||
|
||||
&:focus {
|
||||
background: #fff;
|
||||
border: 1px solid #1890ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
:global(.ant-tabs-content) {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.ant-tabs-tabpane) {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.ant-tabs-nav) {
|
||||
margin: 0;
|
||||
padding: 0 16px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
:global(.ant-tabs-tab) {
|
||||
padding: 12px 0;
|
||||
margin: 0 16px 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emptyState {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #999;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
125
Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx
Normal file
125
Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import React, { useState } from "react";
|
||||
import { Layout, Input, Tabs } from "antd";
|
||||
import {
|
||||
SearchOutlined,
|
||||
UserOutlined,
|
||||
TeamOutlined,
|
||||
MessageOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { ContactData, ChatSession } from "../../data";
|
||||
import ContactListSimple from "./ContactListSimple";
|
||||
import MessageList from "../MessageList/index";
|
||||
import styles from "./SidebarMenu.module.scss";
|
||||
|
||||
const { Sider } = Layout;
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
interface SidebarMenuProps {
|
||||
contacts: ContactData[];
|
||||
chatSessions: ChatSession[];
|
||||
currentChat: ChatSession | null;
|
||||
onContactClick: (contact: ContactData) => void;
|
||||
onChatSelect: (chat: ChatSession) => void;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
||||
contacts,
|
||||
chatSessions,
|
||||
currentChat,
|
||||
onContactClick,
|
||||
onChatSelect,
|
||||
loading = false,
|
||||
}) => {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [activeTab, setActiveTab] = useState("contacts");
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
setSearchText(value);
|
||||
};
|
||||
|
||||
const getFilteredContacts = () => {
|
||||
if (!searchText) return contacts;
|
||||
return contacts.filter(
|
||||
contact =>
|
||||
contact.name.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
contact.phone.includes(searchText),
|
||||
);
|
||||
};
|
||||
|
||||
const getFilteredSessions = () => {
|
||||
if (!searchText) return chatSessions;
|
||||
return chatSessions.filter(session =>
|
||||
session.name.toLowerCase().includes(searchText.toLowerCase()),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Sider width={300} className={styles.sidebar}>
|
||||
{/* 搜索栏 */}
|
||||
<div className={styles.searchBar}>
|
||||
<Input
|
||||
placeholder="搜索联系人、群组"
|
||||
prefix={<SearchOutlined />}
|
||||
value={searchText}
|
||||
onChange={e => handleSearch(e.target.value)}
|
||||
allowClear
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 标签页 */}
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={setActiveTab}
|
||||
className={styles.tabs}
|
||||
>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<MessageOutlined />
|
||||
聊天
|
||||
</span>
|
||||
}
|
||||
key="chats"
|
||||
>
|
||||
<MessageList
|
||||
sessions={getFilteredSessions()}
|
||||
currentChat={currentChat}
|
||||
onChatSelect={onChatSelect}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<UserOutlined />
|
||||
联系人
|
||||
</span>
|
||||
}
|
||||
key="contacts"
|
||||
>
|
||||
<ContactListSimple
|
||||
contacts={getFilteredContacts()}
|
||||
onContactClick={onContactClick}
|
||||
selectedContactId={currentChat?.id.split("_")[1]}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<TeamOutlined />
|
||||
群组
|
||||
</span>
|
||||
}
|
||||
key="groups"
|
||||
>
|
||||
<div className={styles.emptyState}>
|
||||
<TeamOutlined style={{ fontSize: 48, color: "#ccc" }} />
|
||||
<p>暂无群组</p>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Sider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarMenu;
|
||||
@@ -1,47 +1,66 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { Layout, Input, Button, Tabs, Space, message, Tooltip } from "antd";
|
||||
import {
|
||||
SearchOutlined,
|
||||
UserOutlined,
|
||||
TeamOutlined,
|
||||
MessageOutlined,
|
||||
InfoCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { Layout, Button, Space, message, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined, MessageOutlined } from "@ant-design/icons";
|
||||
import dayjs from "dayjs";
|
||||
import { ContactData, MessageData, ChatSession } from "./data";
|
||||
import ChatWindow from "./components/ChatWindow/index";
|
||||
import ContactList from "./components/ContactList/index";
|
||||
import MessageList from "./components/MessageList/index";
|
||||
import SidebarMenu from "./components/SidebarMenu/index";
|
||||
import styles from "./index.module.scss";
|
||||
import { getContactList } from "./api";
|
||||
const { Sider, Content } = Layout;
|
||||
const { TabPane } = Tabs;
|
||||
import { getContactList, getChatMessage } from "./api";
|
||||
const { Content } = Layout;
|
||||
|
||||
import { loginWithToken, getChuKeBaoUserInfo } from "@/pages/login/api";
|
||||
import { useCkChatStore } from "@/store/module/ckchat";
|
||||
import { useUserStore } from "@/store/module/user";
|
||||
const CkboxPage: React.FC = () => {
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const [contacts, setContacts] = useState<ContactData[]>([]);
|
||||
const [chatSessions, setChatSessions] = useState<ChatSession[]>([]);
|
||||
const [currentChat, setCurrentChat] = useState<ChatSession | null>(null);
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("contacts");
|
||||
const [showProfile, setShowProfile] = useState(true);
|
||||
const { setUserInfo, getAccountId } = useCkChatStore();
|
||||
const { login2 } = useUserStore();
|
||||
|
||||
useEffect(() => {
|
||||
fetchContacts();
|
||||
fetchChatSessions();
|
||||
//获取触客宝
|
||||
getToken2().then(() => {
|
||||
getChuKeBaoUserInfo().then(res => {
|
||||
setUserInfo(res);
|
||||
setTimeout(() => {
|
||||
fetchContacts();
|
||||
fetchChatSessions();
|
||||
});
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
const getToken2 = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const params = {
|
||||
grant_type: "password",
|
||||
password: "kr123456",
|
||||
username: "kr_xf3",
|
||||
};
|
||||
loginWithToken(params)
|
||||
.then(res => {
|
||||
login2(res.access_token);
|
||||
resolve(res.access_token);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const fetchContacts = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// 使用API获取联系人数据
|
||||
const response = await getContactList({ prevId: 0, count: 500 });
|
||||
console.log(response);
|
||||
|
||||
if (response && response.data) {
|
||||
if (response) {
|
||||
// 转换API返回的数据结构为组件所需的ContactData结构
|
||||
const contactList: ContactData[] = response.data.map((item: any) => ({
|
||||
const contactList: ContactData[] = response.map((item: any) => ({
|
||||
id: item.id.toString(),
|
||||
name: item.nickname || item.conRemark || item.alias || "",
|
||||
phone: item.phone || "",
|
||||
@@ -63,41 +82,83 @@ const CkboxPage: React.FC = () => {
|
||||
|
||||
const fetchChatSessions = async () => {
|
||||
try {
|
||||
// 模拟聊天会话数据
|
||||
const sessions: ChatSession[] = [
|
||||
{
|
||||
id: "1",
|
||||
type: "private",
|
||||
name: "张三",
|
||||
avatar: "",
|
||||
lastMessage: "你好,请问有什么可以帮助您的吗?",
|
||||
lastTime: dayjs().subtract(5, "minute").toISOString(),
|
||||
unreadCount: 2,
|
||||
online: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "group",
|
||||
name: "技术支持群",
|
||||
avatar: "",
|
||||
lastMessage: "新版本已经发布,请大家及时更新",
|
||||
lastTime: dayjs().subtract(1, "hour").toISOString(),
|
||||
unreadCount: 0,
|
||||
online: false,
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
type: "private",
|
||||
name: "李四",
|
||||
avatar: "",
|
||||
lastMessage: "谢谢您的帮助!",
|
||||
lastTime: dayjs().subtract(2, "hour").toISOString(),
|
||||
unreadCount: 0,
|
||||
online: false,
|
||||
},
|
||||
];
|
||||
setChatSessions(sessions);
|
||||
// 先确保联系人列表已加载
|
||||
if (contacts.length === 0) {
|
||||
await fetchContacts();
|
||||
}
|
||||
|
||||
const response = await getChatMessage({
|
||||
wechatAccountId: 32686452, // 使用实际的wechatAccountId
|
||||
wechatFriendId: 0, // 可以设置为0获取所有好友的消息
|
||||
From: 0,
|
||||
To: 0,
|
||||
Count: 50, // 获取最近的50条消息
|
||||
olderData: false,
|
||||
keyword: "",
|
||||
});
|
||||
|
||||
console.log("聊天消息响应:", response);
|
||||
|
||||
if (response && Array.isArray(response)) {
|
||||
// 创建一个Map来存储每个好友ID对应的最新消息
|
||||
const friendMessageMap = new Map<number, any>();
|
||||
|
||||
// 遍历所有消息,只保留每个好友的最新消息
|
||||
response.forEach(item => {
|
||||
const friendId = item.wechatFriendId;
|
||||
const existingMessage = friendMessageMap.get(friendId);
|
||||
|
||||
// 如果Map中没有这个好友的消息,或者当前消息比已存在的更新,则更新Map
|
||||
if (
|
||||
!existingMessage ||
|
||||
new Date(item.createTime) > new Date(existingMessage.createTime)
|
||||
) {
|
||||
friendMessageMap.set(friendId, item);
|
||||
}
|
||||
});
|
||||
|
||||
// 将Map转换为数组
|
||||
const latestMessages = Array.from(friendMessageMap.values());
|
||||
|
||||
// 将API返回的消息记录转换为ChatSession格式
|
||||
const sessions: ChatSession[] = latestMessages.map(item => {
|
||||
// 解析content字段,它是一个JSON字符串
|
||||
let msgContent = "";
|
||||
try {
|
||||
const contentObj = JSON.parse(item.content);
|
||||
msgContent = contentObj.content || "";
|
||||
} catch (e) {
|
||||
msgContent = item.content;
|
||||
}
|
||||
|
||||
// 尝试从联系人列表中找到对应的联系人信息
|
||||
const contact = contacts.find(
|
||||
c => c.id === item.wechatFriendId.toString(),
|
||||
);
|
||||
|
||||
return {
|
||||
id: item.id.toString(),
|
||||
type: "private", // 假设都是私聊
|
||||
name: contact ? contact.name : `联系人 ${item.wechatFriendId}`, // 使用联系人名称或默认名称
|
||||
avatar: contact?.avatar || "", // 使用联系人头像或默认空字符串
|
||||
lastMessage: msgContent,
|
||||
lastTime:
|
||||
item.createTime || new Date(item.wechatTime).toISOString(),
|
||||
unreadCount: 0, // 未读消息数需要另外获取
|
||||
online: contact?.online || false, // 使用联系人在线状态或默认为false
|
||||
};
|
||||
});
|
||||
|
||||
// 按最后消息时间排序
|
||||
sessions.sort(
|
||||
(a, b) =>
|
||||
new Date(b.lastTime).getTime() - new Date(a.lastTime).getTime(),
|
||||
);
|
||||
|
||||
setChatSessions(sessions);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取聊天记录失败:", error);
|
||||
messageApi.error("获取聊天记录失败");
|
||||
}
|
||||
};
|
||||
@@ -154,93 +215,18 @@ const CkboxPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = (value: string) => {
|
||||
setSearchText(value);
|
||||
};
|
||||
|
||||
const getFilteredContacts = () => {
|
||||
if (!searchText) return contacts;
|
||||
return contacts.filter(
|
||||
contact =>
|
||||
contact.name.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
contact.phone.includes(searchText),
|
||||
);
|
||||
};
|
||||
|
||||
const getFilteredSessions = () => {
|
||||
if (!searchText) return chatSessions;
|
||||
return chatSessions.filter(session =>
|
||||
session.name.toLowerCase().includes(searchText.toLowerCase()),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout className={styles.ckboxLayout}>
|
||||
{contextHolder}
|
||||
{/* 左侧边栏 */}
|
||||
<Sider width={300} className={styles.sidebar}>
|
||||
{/* 搜索栏 */}
|
||||
<div className={styles.searchBar}>
|
||||
<Input
|
||||
placeholder="搜索联系人、群组"
|
||||
prefix={<SearchOutlined />}
|
||||
value={searchText}
|
||||
onChange={e => handleSearch(e.target.value)}
|
||||
allowClear
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 标签页 */}
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onChange={setActiveTab}
|
||||
className={styles.tabs}
|
||||
>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<MessageOutlined />
|
||||
聊天
|
||||
</span>
|
||||
}
|
||||
key="chats"
|
||||
>
|
||||
<MessageList
|
||||
sessions={getFilteredSessions()}
|
||||
currentChat={currentChat}
|
||||
onChatSelect={setCurrentChat}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<UserOutlined />
|
||||
联系人
|
||||
</span>
|
||||
}
|
||||
key="contacts"
|
||||
>
|
||||
<ContactList
|
||||
contacts={getFilteredContacts()}
|
||||
onContactClick={handleContactClick}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab={
|
||||
<span>
|
||||
<TeamOutlined />
|
||||
群组
|
||||
</span>
|
||||
}
|
||||
key="groups"
|
||||
>
|
||||
<div className={styles.emptyState}>
|
||||
<TeamOutlined style={{ fontSize: 48, color: "#ccc" }} />
|
||||
<p>暂无群组</p>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Sider>
|
||||
<SidebarMenu
|
||||
contacts={contacts}
|
||||
chatSessions={chatSessions}
|
||||
currentChat={currentChat}
|
||||
onContactClick={handleContactClick}
|
||||
onChatSelect={setCurrentChat}
|
||||
loading={loading}
|
||||
/>
|
||||
|
||||
{/* 主内容区 */}
|
||||
<Content className={styles.mainContent}>
|
||||
|
||||
7
Cunkebao/src/pages/pc/ckbox/main.ts
Normal file
7
Cunkebao/src/pages/pc/ckbox/main.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { useCkChatStore } from "@/store/module/ckchat";
|
||||
import { useWebSocketStore } from "@/store/module/websocket";
|
||||
const { connect } = useWebSocketStore.getState();
|
||||
//获取微信账户组
|
||||
export const getWechatAccountGroup = () => {
|
||||
console.log(connect);
|
||||
};
|
||||
Reference in New Issue
Block a user