diff --git a/Cunkebao/src/pages/login/Login.tsx b/Cunkebao/src/pages/login/Login.tsx index 621e6bf2..197c410d 100644 --- a/Cunkebao/src/pages/login/Login.tsx +++ b/Cunkebao/src/pages/login/Login.tsx @@ -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, + }); }); }); }; diff --git a/Cunkebao/src/pages/pc/ckbox/api.ts b/Cunkebao/src/pages/pc/ckbox/api.ts index f71abd9f..ef1da61a 100644 --- a/Cunkebao/src/pages/pc/ckbox/api.ts +++ b/Cunkebao/src/pages/pc/ckbox/api.ts @@ -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 => { - return request("/v1/contacts/search", { keyword }, "GET"); -}; - // 获取聊天会话列表 export const getChatSessions = (): Promise => { 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, diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx index f02bee3c..f3f76a2f 100644 --- a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx @@ -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 = ({ 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); diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/ContactListSimple.module.scss b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/ContactListSimple.module.scss new file mode 100644 index 00000000..c7a67ec9 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/ContactListSimple.module.scss @@ -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; + } +} \ No newline at end of file diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/ContactListSimple.tsx b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/ContactListSimple.tsx new file mode 100644 index 00000000..9a5dfba3 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/ContactListSimple.tsx @@ -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 = ({ + contacts, + onContactClick, + selectedContactId, +}) => { + return ( +
+
全部好友
+ ( + onContactClick(contact)} + className={`${styles.contactItem} ${contact.id === selectedContactId ? styles.selected : ""}`} + > +
+ + {contact.name.charAt(0)} + } + className={styles.avatar} + /> + +
+
+
{contact.name}
+
+
+ )} + /> +
+ ); +}; + +export default ContactListSimple; diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss new file mode 100644 index 00000000..614120fb --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss @@ -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; + } +} \ No newline at end of file diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx new file mode 100644 index 00000000..85b99318 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx @@ -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 = ({ + 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 ( + + {/* 搜索栏 */} +
+ } + value={searchText} + onChange={e => handleSearch(e.target.value)} + allowClear + /> +
+ + {/* 标签页 */} + + + + 聊天 + + } + key="chats" + > + + + + + 联系人 + + } + key="contacts" + > + + + + + 群组 + + } + key="groups" + > +
+ +

暂无群组

+
+
+
+
+ ); +}; + +export default SidebarMenu; diff --git a/Cunkebao/src/pages/pc/ckbox/index.tsx b/Cunkebao/src/pages/pc/ckbox/index.tsx index 0722949c..a70737e2 100644 --- a/Cunkebao/src/pages/pc/ckbox/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/index.tsx @@ -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([]); const [chatSessions, setChatSessions] = useState([]); const [currentChat, setCurrentChat] = useState(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(); + + // 遍历所有消息,只保留每个好友的最新消息 + 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 ( {contextHolder} {/* 左侧边栏 */} - - {/* 搜索栏 */} -
- } - value={searchText} - onChange={e => handleSearch(e.target.value)} - allowClear - /> -
- - {/* 标签页 */} - - - - 聊天 - - } - key="chats" - > - - - - - 联系人 - - } - key="contacts" - > - - - - - 群组 - - } - key="groups" - > -
- -

暂无群组

-
-
-
-
+ {/* 主内容区 */} diff --git a/Cunkebao/src/pages/pc/ckbox/main.ts b/Cunkebao/src/pages/pc/ckbox/main.ts new file mode 100644 index 00000000..da431b2f --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/main.ts @@ -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); +};