增强SidebarMenu和MessageList组件,新增对currentContact变化的监听,自动切换聊天tab,优化联系人点击处理逻辑,调整置顶标识为数字类型,提升代码可读性和用户体验。
This commit is contained in:
@@ -37,9 +37,15 @@ const CustomerList: React.FC = () => {
|
|||||||
const session = chatSessions.filter(
|
const session = chatSessions.filter(
|
||||||
v => v.wechatAccountId === customerId,
|
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 {
|
} else {
|
||||||
return chatSessions.reduce((pre, cur) => pre + cur.config.unreadCount, 0);
|
return chatSessions.reduce(
|
||||||
|
(pre, cur) => pre + (cur.config?.unreadCount || 0),
|
||||||
|
0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface MessageListProps {}
|
|||||||
|
|
||||||
const MessageList: React.FC<MessageListProps> = () => {
|
const MessageList: React.FC<MessageListProps> = () => {
|
||||||
const searchKeyword = useContactStore(state => state.searchKeyword);
|
const searchKeyword = useContactStore(state => state.searchKeyword);
|
||||||
|
const selectedContact = useContactStore(state => state.currentContact);
|
||||||
const { setCurrentContact, currentContract } = useWeChatStore();
|
const { setCurrentContact, currentContract } = useWeChatStore();
|
||||||
const { currentCustomer } = useCustomerStore();
|
const { currentCustomer } = useCustomerStore();
|
||||||
const { sendCommand } = useWebSocketStore();
|
const { sendCommand } = useWebSocketStore();
|
||||||
@@ -94,8 +95,8 @@ const MessageList: React.FC<MessageListProps> = () => {
|
|||||||
|
|
||||||
// 置顶/取消置顶
|
// 置顶/取消置顶
|
||||||
const handleTogglePin = async (session: ChatSession) => {
|
const handleTogglePin = async (session: ChatSession) => {
|
||||||
const currentPinned = session.config?.top || false;
|
const currentPinned = session.config?.top || 0;
|
||||||
const newPinned = !currentPinned;
|
const newPinned = currentPinned === 1 ? 0 : 1;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 立即更新UI并重新排序(乐观更新)
|
// 1. 立即更新UI并重新排序(乐观更新)
|
||||||
@@ -132,7 +133,7 @@ const MessageList: React.FC<MessageListProps> = () => {
|
|||||||
newPinned,
|
newPinned,
|
||||||
);
|
);
|
||||||
|
|
||||||
message.success(`${newPinned ? "置顶" : "取消置顶"}成功`);
|
message.success(`${newPinned === 1 ? "置顶" : "取消置顶"}成功`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 4. 失败时回滚UI
|
// 4. 失败时回滚UI
|
||||||
setSessions(prev =>
|
setSessions(prev =>
|
||||||
@@ -434,6 +435,33 @@ const MessageList: React.FC<MessageListProps> = () => {
|
|||||||
setFilteredSessions(filtered);
|
setFilteredSessions(filtered);
|
||||||
}, [sessions, currentCustomer, searchKeyword]);
|
}, [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消息处理 ====================
|
||||||
|
|
||||||
// 监听WebSocket消息更新
|
// 监听WebSocket消息更新
|
||||||
@@ -482,7 +510,7 @@ const MessageList: React.FC<MessageListProps> = () => {
|
|||||||
lastUpdateTime: new Date().toISOString(),
|
lastUpdateTime: new Date().toISOString(),
|
||||||
config: {
|
config: {
|
||||||
unreadCount: 1,
|
unreadCount: 1,
|
||||||
top: false,
|
top: 0,
|
||||||
},
|
},
|
||||||
sortKey: "",
|
sortKey: "",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useCallback, useEffect } from "react";
|
import React, { useState, useCallback, useEffect } from "react";
|
||||||
import { List, Avatar, Skeleton, Collapse } from "antd";
|
import { List, Avatar, Skeleton, Collapse } from "antd";
|
||||||
import type { CollapseProps } from "antd";
|
import type { CollapseProps } from "antd";
|
||||||
|
import dayjs from "dayjs";
|
||||||
import styles from "./com.module.scss";
|
import styles from "./com.module.scss";
|
||||||
import { Contact } from "@/utils/db";
|
import { Contact, ChatSession } from "@/utils/db";
|
||||||
import { ContactManager } from "@/utils/dbAction";
|
import { ContactManager, MessageManager } from "@/utils/dbAction";
|
||||||
import { ContactGroupByLabel } from "@/pages/pc/ckbox/data";
|
import { ContactGroupByLabel } from "@/pages/pc/ckbox/data";
|
||||||
import { useContactStore } from "@weChatStore/contacts";
|
import { useContactStore } from "@weChatStore/contacts";
|
||||||
import { useWeChatStore } from "@weChatStore/weChat";
|
|
||||||
import { useCustomerStore } from "@weChatStore/customer";
|
import { useCustomerStore } from "@weChatStore/customer";
|
||||||
import { useUserStore } from "@storeModule/user";
|
import { useUserStore } from "@storeModule/user";
|
||||||
import {
|
import {
|
||||||
@@ -47,7 +47,6 @@ const ContactListSimple: React.FC<WechatFriendsProps> = ({
|
|||||||
// 获取用户和客服信息
|
// 获取用户和客服信息
|
||||||
const currentUser = useUserStore(state => state.user);
|
const currentUser = useUserStore(state => state.user);
|
||||||
const currentCustomer = useCustomerStore(state => state.currentCustomer);
|
const currentCustomer = useCustomerStore(state => state.currentCustomer);
|
||||||
const { setCurrentContact: setWeChatCurrentContact } = useWeChatStore();
|
|
||||||
|
|
||||||
// 从服务器同步数据(静默同步,不显示提示)
|
// 从服务器同步数据(静默同步,不显示提示)
|
||||||
const syncWithServer = useCallback(
|
const syncWithServer = useCallback(
|
||||||
@@ -244,13 +243,64 @@ const ContactListSimple: React.FC<WechatFriendsProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 联系人点击处理
|
// 联系人点击处理
|
||||||
const onContactClick = (contact: Contact) => {
|
const onContactClick = async (contact: Contact) => {
|
||||||
setCurrentContact(contact);
|
if (!currentUser?.id) return;
|
||||||
// 这里需要将 Contact 转换为 weChat 需要的格式
|
|
||||||
// 暂时使用 any 类型,后续需要完善转换逻辑
|
|
||||||
setWeChatCurrentContact(contact as any);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
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) => {
|
const renderContactItem = (contact: Contact) => {
|
||||||
// 判断是否为群组
|
// 判断是否为群组
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Input, Skeleton } from "antd";
|
import { Input, Skeleton } from "antd";
|
||||||
import { SearchOutlined } from "@ant-design/icons";
|
import { SearchOutlined } from "@ant-design/icons";
|
||||||
import WechatFriends from "./WechatFriends";
|
import WechatFriends from "./WechatFriends";
|
||||||
@@ -12,12 +12,23 @@ interface SidebarMenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SidebarMenu: React.FC<SidebarMenuProps> = ({ loading = false }) => {
|
const SidebarMenu: React.FC<SidebarMenuProps> = ({ loading = false }) => {
|
||||||
const { searchKeyword, setSearchKeyword, clearSearchKeyword } =
|
const {
|
||||||
useContactStore();
|
searchKeyword,
|
||||||
|
setSearchKeyword,
|
||||||
|
clearSearchKeyword,
|
||||||
|
currentContact,
|
||||||
|
} = useContactStore();
|
||||||
const currentCustomer = useCustomerStore(state => state.currentCustomer);
|
const currentCustomer = useCustomerStore(state => state.currentCustomer);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState("chats");
|
const [activeTab, setActiveTab] = useState("chats");
|
||||||
|
|
||||||
|
// 监听 currentContact 变化,自动切换到聊天tab
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentContact !== null) {
|
||||||
|
setActiveTab("chats");
|
||||||
|
}
|
||||||
|
}, [currentContact]);
|
||||||
|
|
||||||
const handleSearch = (value: string) => {
|
const handleSearch = (value: string) => {
|
||||||
setSearchKeyword(value);
|
setSearchKeyword(value);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export interface ChatSession {
|
|||||||
lastUpdateTime: string; // 最后更新时间
|
lastUpdateTime: string; // 最后更新时间
|
||||||
config: {
|
config: {
|
||||||
unreadCount: number; // 未读数量
|
unreadCount: number; // 未读数量
|
||||||
top: boolean; // 是否置顶
|
top: number; // 是否置顶(1=置顶,0=非置顶)
|
||||||
};
|
};
|
||||||
sortKey: string; // 预计算排序键
|
sortKey: string; // 预计算排序键
|
||||||
|
|
||||||
|
|||||||
@@ -538,7 +538,7 @@ export class MessageManager {
|
|||||||
userId: number,
|
userId: number,
|
||||||
sessionId: number,
|
sessionId: number,
|
||||||
type: "friend" | "group",
|
type: "friend" | "group",
|
||||||
isPinned: boolean,
|
isPinned: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const serverId = `${type}_${sessionId}`;
|
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
|
* @param userId 用户ID
|
||||||
@@ -690,4 +727,49 @@ export class MessageManager {
|
|||||||
console.error("清空用户会话失败:", error);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user