From 1b026665315d72bc7041c8a9e852ff373b4d1e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Fri, 24 Oct 2025 11:28:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BASidebarMenu=E5=92=8CMessageLi?= =?UTF-8?q?st=E7=BB=84=E4=BB=B6=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=AF=B9current?= =?UTF-8?q?Contact=E5=8F=98=E5=8C=96=E7=9A=84=E7=9B=91=E5=90=AC=EF=BC=8C?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=88=87=E6=8D=A2=E8=81=8A=E5=A4=A9tab?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E8=81=94=E7=B3=BB=E4=BA=BA=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E7=BD=AE=E9=A1=B6=E6=A0=87=E8=AF=86=E4=B8=BA=E6=95=B0?= =?UTF-8?q?=E5=AD=97=E7=B1=BB=E5=9E=8B=EF=BC=8C=E6=8F=90=E5=8D=87=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7=E5=92=8C=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weChat/components/CustomerList/index.tsx | 10 ++- .../SidebarMenu/MessageList/index.tsx | 36 +++++++- .../SidebarMenu/WechatFriends/index.tsx | 70 +++++++++++++--- .../weChat/components/SidebarMenu/index.tsx | 17 +++- Touchkebao/src/utils/db.ts | 2 +- Touchkebao/src/utils/dbAction/message.ts | 84 ++++++++++++++++++- 6 files changed, 198 insertions(+), 21 deletions(-) diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx index 37c38d6e..9ce88a1f 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/CustomerList/index.tsx @@ -37,9 +37,15 @@ const CustomerList: React.FC = () => { const session = chatSessions.filter( v => v.wechatAccountId === customerId, ); - return session.reduce((pre, cur) => pre + cur.config.unreadCount, 0); + return session.reduce( + (pre, cur) => pre + (cur.config.unreadCount || 0), + 0, + ); } else { - return chatSessions.reduce((pre, cur) => pre + cur.config.unreadCount, 0); + return chatSessions.reduce( + (pre, cur) => pre + (cur.config?.unreadCount || 0), + 0, + ); } }; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx index 039b7154..11190671 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx @@ -24,6 +24,7 @@ interface MessageListProps {} const MessageList: React.FC = () => { const searchKeyword = useContactStore(state => state.searchKeyword); + const selectedContact = useContactStore(state => state.currentContact); const { setCurrentContact, currentContract } = useWeChatStore(); const { currentCustomer } = useCustomerStore(); const { sendCommand } = useWebSocketStore(); @@ -94,8 +95,8 @@ const MessageList: React.FC = () => { // 置顶/取消置顶 const handleTogglePin = async (session: ChatSession) => { - const currentPinned = session.config?.top || false; - const newPinned = !currentPinned; + const currentPinned = session.config?.top || 0; + const newPinned = currentPinned === 1 ? 0 : 1; try { // 1. 立即更新UI并重新排序(乐观更新) @@ -132,7 +133,7 @@ const MessageList: React.FC = () => { newPinned, ); - message.success(`${newPinned ? "置顶" : "取消置顶"}成功`); + message.success(`${newPinned === 1 ? "置顶" : "取消置顶"}成功`); } catch (error) { // 4. 失败时回滚UI setSessions(prev => @@ -434,6 +435,33 @@ const MessageList: React.FC = () => { setFilteredSessions(filtered); }, [sessions, currentCustomer, searchKeyword]); + // ==================== 监听联系人选中 ==================== + + // 监听 contacts store 中的 currentContact 变化 + useEffect(() => { + if (!selectedContact || !currentUserId) return; + + const handleContactSelection = async () => { + try { + // 从数据库中查找该联系人对应的会话 + const session = await MessageManager.getSessionByContactId( + currentUserId, + selectedContact.id, + selectedContact.type, + ); + + if (session) { + // 直接选中该会话 + setCurrentContact(session as any, true); + } + } catch (error) { + console.error("处理联系人选中失败:", error); + } + }; + + handleContactSelection(); + }, [selectedContact, currentUserId, setCurrentContact]); + // ==================== WebSocket消息处理 ==================== // 监听WebSocket消息更新 @@ -482,7 +510,7 @@ const MessageList: React.FC = () => { lastUpdateTime: new Date().toISOString(), config: { unreadCount: 1, - top: false, + top: 0, }, sortKey: "", }; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/index.tsx index 961f5e80..3987775e 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/index.tsx @@ -1,12 +1,12 @@ import React, { useState, useCallback, useEffect } from "react"; import { List, Avatar, Skeleton, Collapse } from "antd"; import type { CollapseProps } from "antd"; +import dayjs from "dayjs"; import styles from "./com.module.scss"; -import { Contact } from "@/utils/db"; -import { ContactManager } from "@/utils/dbAction"; +import { Contact, ChatSession } from "@/utils/db"; +import { ContactManager, MessageManager } from "@/utils/dbAction"; import { ContactGroupByLabel } from "@/pages/pc/ckbox/data"; import { useContactStore } from "@weChatStore/contacts"; -import { useWeChatStore } from "@weChatStore/weChat"; import { useCustomerStore } from "@weChatStore/customer"; import { useUserStore } from "@storeModule/user"; import { @@ -47,7 +47,6 @@ const ContactListSimple: React.FC = ({ // 获取用户和客服信息 const currentUser = useUserStore(state => state.user); const currentCustomer = useCustomerStore(state => state.currentCustomer); - const { setCurrentContact: setWeChatCurrentContact } = useWeChatStore(); // 从服务器同步数据(静默同步,不显示提示) const syncWithServer = useCallback( @@ -244,13 +243,64 @@ const ContactListSimple: React.FC = ({ ); // 联系人点击处理 - const onContactClick = (contact: Contact) => { - setCurrentContact(contact); - // 这里需要将 Contact 转换为 weChat 需要的格式 - // 暂时使用 any 类型,后续需要完善转换逻辑 - setWeChatCurrentContact(contact as any); - }; + const onContactClick = async (contact: Contact) => { + if (!currentUser?.id) return; + try { + // 1. 查询数据库是否存在该联系人的会话 + const existingSession = await MessageManager.getSessionByContactId( + currentUser.id, + contact.id, + contact.type, + ); + + const currentTime = dayjs().format(); // 当前时间 + + if (!existingSession) { + // === 场景1:会话不存在,创建新会话并插入数据库 === + + const newSession: ChatSession = { + serverId: `${contact.type}_${contact.id}`, + userId: currentUser.id, + id: contact.id, + type: contact.type, + wechatAccountId: contact.wechatAccountId, + nickname: contact.nickname, + conRemark: contact.conRemark || "", + avatar: contact.avatar, + content: "", + lastUpdateTime: currentTime, // 使用当前时间 + config: { + unreadCount: 0, + top: contact.config?.top === true ? 1 : 0, // boolean → number + }, + sortKey: "", + wechatId: contact.wechatId, + }; + + // 插入数据库(等待完成) + await MessageManager.createSession(currentUser.id, newSession); + console.log(`创建新会话: ${contact.nickname || contact.wechatId}`); + } else { + // === 场景2:会话已存在,更新 lastUpdateTime === + + await MessageManager.updateSessionTime( + currentUser.id, + contact.id, + contact.type, + currentTime, // 更新为当前时间 + ); + console.log( + `更新会话时间: ${contact.nickname || contact.wechatId} -> ${currentTime}`, + ); + } + + // 3. 数据库操作完成后,触发 UI 更新 + setCurrentContact(contact); + } catch (error) { + console.error("处理联系人点击失败:", error); + } + }; // 渲染联系人项 const renderContactItem = (contact: Contact) => { // 判断是否为群组 diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx index 871e49cb..4d032784 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { Input, Skeleton } from "antd"; import { SearchOutlined } from "@ant-design/icons"; import WechatFriends from "./WechatFriends"; @@ -12,12 +12,23 @@ interface SidebarMenuProps { } const SidebarMenu: React.FC = ({ loading = false }) => { - const { searchKeyword, setSearchKeyword, clearSearchKeyword } = - useContactStore(); + const { + searchKeyword, + setSearchKeyword, + clearSearchKeyword, + currentContact, + } = useContactStore(); const currentCustomer = useCustomerStore(state => state.currentCustomer); const [activeTab, setActiveTab] = useState("chats"); + // 监听 currentContact 变化,自动切换到聊天tab + useEffect(() => { + if (currentContact !== null) { + setActiveTab("chats"); + } + }, [currentContact]); + const handleSearch = (value: string) => { setSearchKeyword(value); }; diff --git a/Touchkebao/src/utils/db.ts b/Touchkebao/src/utils/db.ts index 407acf9b..ce0450e7 100644 --- a/Touchkebao/src/utils/db.ts +++ b/Touchkebao/src/utils/db.ts @@ -49,7 +49,7 @@ export interface ChatSession { lastUpdateTime: string; // 最后更新时间 config: { unreadCount: number; // 未读数量 - top: boolean; // 是否置顶 + top: number; // 是否置顶(1=置顶,0=非置顶) }; sortKey: string; // 预计算排序键 diff --git a/Touchkebao/src/utils/dbAction/message.ts b/Touchkebao/src/utils/dbAction/message.ts index 34b883fb..5f452f2a 100644 --- a/Touchkebao/src/utils/dbAction/message.ts +++ b/Touchkebao/src/utils/dbAction/message.ts @@ -538,7 +538,7 @@ export class MessageManager { userId: number, sessionId: number, type: "friend" | "group", - isPinned: boolean, + isPinned: number, ): Promise { try { const serverId = `${type}_${sessionId}`; @@ -566,6 +566,43 @@ export class MessageManager { } } + /** + * 更新会话时间(用于联系人点击时更新) + * @param userId 用户ID + * @param sessionId 会话ID + * @param type 会话类型 + * @param newTime 新的时间 + */ + static async updateSessionTime( + userId: number, + sessionId: number, + type: "friend" | "group", + newTime: string, + ): Promise { + try { + const serverId = `${type}_${sessionId}`; + const session = (await chatSessionService.findByPrimaryKey( + serverId, + )) as ChatSession; + + if (session) { + const updatedSession = { + ...session, + lastUpdateTime: newTime, + }; + + // 重新生成 sortKey(因为时间变了,排序会改变) + updatedSession.sortKey = this.generateSortKey(updatedSession); + + await chatSessionService.update(serverId, updatedSession); + console.log(`会话时间已更新: ${serverId} -> ${newTime}`); + } + } catch (error) { + console.error("更新会话时间失败:", error); + throw error; + } + } + /** * 更新会话备注 * @param userId 用户ID @@ -690,4 +727,49 @@ export class MessageManager { console.error("清空用户会话失败:", error); } } + + /** + * 根据联系人ID获取会话 + * @param userId 用户ID + * @param contactId 联系人ID + * @param type 类型(friend/group) + */ + static async getSessionByContactId( + userId: number, + contactId: number, + type: "friend" | "group", + ): Promise { + try { + const serverId = `${type}_${contactId}`; + const session = await chatSessionService.findByPrimaryKey(serverId); + return session as ChatSession | null; + } catch (error) { + console.error("根据联系人ID获取会话失败:", error); + return null; + } + } + + /** + * 创建新会话 + * @param userId 用户ID + * @param session 会话数据 + */ + static async createSession( + userId: number, + session: ChatSession, + ): Promise { + try { + // 生成 sortKey + const sessionWithSortKey = { + ...session, + sortKey: this.generateSortKey(session), + }; + + await chatSessionService.create(sessionWithSortKey); + console.log(`创建新会话: ${session.nickname || session.wechatId}`); + } catch (error) { + console.error("创建会话失败:", error); + throw error; + } + } }