增强SidebarMenu和MessageList组件,新增对currentContact变化的监听,自动切换聊天tab,优化联系人点击处理逻辑,调整置顶标识为数字类型,提升代码可读性和用户体验。

This commit is contained in:
超级老白兔
2025-10-24 11:28:01 +08:00
parent 3b82908e8a
commit 1b02666531
6 changed files with 198 additions and 21 deletions

View File

@@ -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,
);
}
};

View File

@@ -24,6 +24,7 @@ interface MessageListProps {}
const MessageList: React.FC<MessageListProps> = () => {
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<MessageListProps> = () => {
// 置顶/取消置顶
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<MessageListProps> = () => {
newPinned,
);
message.success(`${newPinned ? "置顶" : "取消置顶"}成功`);
message.success(`${newPinned === 1 ? "置顶" : "取消置顶"}成功`);
} catch (error) {
// 4. 失败时回滚UI
setSessions(prev =>
@@ -434,6 +435,33 @@ const MessageList: React.FC<MessageListProps> = () => {
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<MessageListProps> = () => {
lastUpdateTime: new Date().toISOString(),
config: {
unreadCount: 1,
top: false,
top: 0,
},
sortKey: "",
};

View File

@@ -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<WechatFriendsProps> = ({
// 获取用户和客服信息
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<WechatFriendsProps> = ({
);
// 联系人点击处理
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) => {
// 判断是否为群组

View File

@@ -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<SidebarMenuProps> = ({ 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);
};

View File

@@ -49,7 +49,7 @@ export interface ChatSession {
lastUpdateTime: string; // 最后更新时间
config: {
unreadCount: number; // 未读数量
top: boolean; // 是否置顶
top: number; // 是否置顶1=置顶0=非置顶)
};
sortKey: string; // 预计算排序键

View File

@@ -538,7 +538,7 @@ export class MessageManager {
userId: number,
sessionId: number,
type: "friend" | "group",
isPinned: boolean,
isPinned: number,
): Promise<void> {
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<void> {
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<ChatSession | null> {
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<void> {
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;
}
}
}