diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/com.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/com.module.scss index 802ee819..5d0fb1f1 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/com.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/com.module.scss @@ -242,3 +242,41 @@ align-items: center; margin-bottom: 8px; } + +// 同步状态提示栏样式 +.syncStatusBar { + height: 50px; + display: flex; + align-items: center; + justify-content: center; + border-bottom: 1px solid #f0f0f0; + background-color: #fafafa; + padding: 0 16px; + flex-shrink: 0; +} + +.syncStatusContent { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + justify-content: space-between; + padding: 0px 20px; +} + +.syncStatusText { + font-size: 14px; + color: #666; +} + +.syncButton { + color: green; + cursor: pointer; + font-size: 14px; + &:hover { + color: green; + } + &:active { + transform: scale(0.98); + } +} 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 2d187623..1e57ed74 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 @@ -1,20 +1,13 @@ import React, { useEffect, useState, useRef } from "react"; -import { - List, - Avatar, - Badge, - Modal, - Input, - message, - Skeleton, - Spin, -} from "antd"; +import { List, Avatar, Badge, Modal, Input, message } from "antd"; import { UserOutlined, TeamOutlined, PushpinOutlined, DeleteOutlined, EditOutlined, + LoadingOutlined, + CheckCircleOutlined, } from "@ant-design/icons"; import styles from "./com.module.scss"; import { @@ -47,14 +40,13 @@ const MessageList: React.FC = () => { // Store状态 const { - loading, hasLoadedOnce, - setLoading, setHasLoadedOnce, sessions, setSessions: setSessionState, } = useMessageStore(); const [filteredSessions, setFilteredSessions] = useState([]); + const [syncing, setSyncing] = useState(false); // 同步状态 // 右键菜单相关状态 const [contextMenu, setContextMenu] = useState<{ @@ -306,70 +298,104 @@ const MessageList: React.FC = () => { // ==================== 数据加载 ==================== - // 与服务器同步数据 + // 与服务器同步数据(优化版:逐页同步,立即更新UI) const syncWithServer = async () => { if (!currentUserId) return; + setSyncing(true); // 开始同步,显示同步状态栏 + try { - // 获取会话列表数据(分页获取所有数据) - let allMessages: any[] = []; let page = 1; const limit = 500; let hasMore = true; + let totalProcessed = 0; + let successCount = 0; + let failCount = 0; - // 分页获取会话列表 + // 分页获取会话列表,每页成功后立即同步 while (hasMore) { - const result: any = await getMessageList({ - page, - limit, - }); + try { + const result: any = await getMessageList({ + page, + limit, + }); - if (!result || !Array.isArray(result) || result.length === 0) { - hasMore = false; - break; - } + if (!result || !Array.isArray(result) || result.length === 0) { + hasMore = false; + break; + } - allMessages = [...allMessages, ...result]; + // 立即处理这一页的数据 + const friends = result.filter( + (msg: any) => msg.dataType === "friend" || !msg.chatroomId, + ); + const groups = result + .filter((msg: any) => msg.dataType === "group" || msg.chatroomId) + .map((msg: any) => ({ + ...msg, + chatroomAvatar: msg.chatroomAvatar || msg.avatar || "", + })); - if (result.length < limit) { - hasMore = false; - } else { + // 立即同步这一页到数据库(会触发UI更新) + // 分页同步时跳过删除检查,避免误删其他页的会话 + await MessageManager.syncSessions( + currentUserId, + { + friends, + groups, + }, + { skipDelete: true }, + ); + + totalProcessed += result.length; + successCount++; + + // 判断是否还有下一页 + if (result.length < limit) { + hasMore = false; + } else { + page++; + } + } catch (error) { + // 忽略单页失败,继续处理下一页 + console.error(`第${page}页同步失败:`, error); + failCount++; + + // 如果连续失败太多,停止同步 + if (failCount >= 3) { + console.warn("连续失败次数过多,停止同步"); + break; + } + + // 继续下一页 page++; + if (page > 100) { + // 防止无限循环 + hasMore = false; + } } } - // 分离好友和群聊数据 - const friends = allMessages.filter( - (msg: any) => msg.dataType === "friend" || !msg.chatroomId, - ); - const groups = allMessages - .filter((msg: any) => msg.dataType === "group" || msg.chatroomId) - .map((msg: any) => { - // 确保群聊数据包含正确的头像字段 - // 如果接口返回的是 avatar 字段,需要映射到 chatroomAvatar - return { - ...msg, - chatroomAvatar: msg.chatroomAvatar || msg.avatar || "", - }; - }); - - // 执行增量同步 - const syncResult = await MessageManager.syncSessions(currentUserId, { - friends, - groups, - }); - - // 同步后验证数据 - const verifySession = await MessageManager.getUserSessions(currentUserId); - console.log("同步后的会话数据示例:", verifySession[0]); - console.log( - `会话同步完成: 新增${syncResult.added}, 更新${syncResult.updated}, 删除${syncResult.deleted}`, + `会话同步完成: 成功${successCount}页, 失败${failCount}页, 共处理${totalProcessed}条数据`, ); - - // 会话管理器会在有变更时触发订阅回调 } catch (error) { console.error("同步服务器数据失败:", error); + } finally { + setSyncing(false); // 同步完成,更新状态栏 + } + }; + + // 手动触发同步的函数 + const handleManualSync = async () => { + if (syncing) return; // 如果正在同步,不重复触发 + setSyncing(true); + try { + await syncWithServer(); + } catch (error) { + console.error("手动同步失败:", error); + } finally { + setSyncing(false); } }; @@ -394,8 +420,6 @@ const MessageList: React.FC = () => { const requestId = ++loadRequestRef.current; const initializeSessions = async () => { - setLoading(true); - try { const cachedSessions = await MessageManager.getUserSessions(currentUserId); @@ -404,6 +428,7 @@ const MessageList: React.FC = () => { return; } + // 有缓存数据立即显示 if (cachedSessions.length > 0) { setSessionState(cachedSessions); } @@ -411,12 +436,18 @@ const MessageList: React.FC = () => { const needsFullSync = cachedSessions.length === 0 || !hasLoadedOnce; if (needsFullSync) { - await syncWithServer(); - if (isCancelled || loadRequestRef.current !== requestId) { - return; - } - setHasLoadedOnce(true); + // 不等待同步完成,让它在后台进行,第一页数据同步后会立即更新UI + syncWithServer() + .then(() => { + if (!isCancelled && loadRequestRef.current === requestId) { + setHasLoadedOnce(true); + } + }) + .catch(error => { + console.error("同步失败:", error); + }); } else { + // 后台同步 syncWithServer().catch(error => { console.error("后台同步失败:", error); }); @@ -425,10 +456,6 @@ const MessageList: React.FC = () => { if (!isCancelled) { console.error("初始化会话列表失败:", error); } - } finally { - if (!isCancelled && loadRequestRef.current === requestId) { - setLoading(false); - } } }; @@ -526,13 +553,11 @@ const MessageList: React.FC = () => { // 渲染完毕后自动点击第一个聊天记录 useEffect(() => { // 只在以下条件满足时自动点击: - // 1. 不在加载状态 - // 2. 有过滤后的会话列表 - // 3. 当前没有选中的联系人 - // 4. 还没有自动点击过 - // 5. 不在搜索状态(避免搜索时自动切换) + // 1. 有过滤后的会话列表 + // 2. 当前没有选中的联系人 + // 3. 还没有自动点击过 + // 4. 不在搜索状态(避免搜索时自动切换) if ( - !loading && filteredSessions.length > 0 && !currentContract && !autoClickRef.current && @@ -550,7 +575,7 @@ const MessageList: React.FC = () => { return () => clearTimeout(timer); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loading, filteredSessions, currentContract, searchKeyword]); + }, [filteredSessions, currentContract, searchKeyword]); // ==================== WebSocket消息处理 ==================== @@ -796,156 +821,146 @@ const MessageList: React.FC = () => { } }; - // 渲染骨架屏 - const renderSkeleton = () => ( -
- {Array(8) - .fill(null) - .map((_, index) => ( -
- -
-
- - -
- -
-
- ))} -
- ); - - // 渲染加载中状态(带旋转动画) - const renderLoading = () => ( -
- -
{renderSkeleton()}
-
+ // 渲染同步状态提示栏 + const renderSyncStatusBar = () => ( +
+ {syncing ? ( +
+ + 同步中... + +
+ ) : ( +
+ + + 同步完成 + + + 同步 + +
+ )}
); return (
- {loading ? ( - // 加载状态:显示加载动画和骨架屏 - renderLoading() - ) : ( - <> - ( - onContactClick(session)} - onContextMenu={e => handleContextMenu(e, session)} - > -
- - - ) : ( - - ) - } - /> - -
-
-
- {session.conRemark || - session.nickname || - session.wechatId} -
-
- {formatWechatTime(session?.lastUpdateTime)} -
-
-
- {messageFilter(session.content)} -
+ {/* 同步状态提示栏 */} + {renderSyncStatusBar()} + + ( + onContactClick(session)} + onContextMenu={e => handleContextMenu(e, session)} + > +
+ + + ) : ( + + ) + } + /> + +
+
+
+ {session.conRemark || session.nickname || session.wechatId} +
+
+ {formatWechatTime(session?.lastUpdateTime)}
- - )} - /> - - {/* 右键菜单 */} - {contextMenu.visible && contextMenu.session && ( -
-
handleTogglePin(contextMenu.session!)} - > - - {(contextMenu.session.config as any)?.top ? "取消置顶" : "置顶"} -
-
handleEditRemark(contextMenu.session!)} - > - - 修改备注 -
-
handleDelete(contextMenu.session!)} - > - - 删除 +
+ {messageFilter(session.content)} +
- )} + + )} + locale={{ + emptyText: + filteredSessions.length === 0 && !syncing ? "暂无会话" : null, + }} + /> - {/* 修改备注Modal */} - - setEditRemarkModal({ - visible: false, - session: null, - remark: "", - }) - } - okText="保存" - cancelText="取消" + {/* 右键菜单 */} + {contextMenu.visible && contextMenu.session && ( +
+
handleTogglePin(contextMenu.session!)} > - - setEditRemarkModal(prev => ({ - ...prev, - remark: e.target.value, - })) - } - placeholder="请输入备注" - maxLength={20} - /> - - + + {(contextMenu.session.config as any)?.top ? "取消置顶" : "置顶"} +
+
handleEditRemark(contextMenu.session!)} + > + + 修改备注 +
+
handleDelete(contextMenu.session!)} + > + + 删除 +
+
)} + + {/* 修改备注Modal */} + + setEditRemarkModal({ + visible: false, + session: null, + remark: "", + }) + } + okText="保存" + cancelText="取消" + > + + setEditRemarkModal(prev => ({ + ...prev, + remark: e.target.value, + })) + } + placeholder="请输入备注" + maxLength={20} + /> +
); }; diff --git a/Touchkebao/src/utils/dbAction/message.ts b/Touchkebao/src/utils/dbAction/message.ts index 6c534670..0d8f71d3 100644 --- a/Touchkebao/src/utils/dbAction/message.ts +++ b/Touchkebao/src/utils/dbAction/message.ts @@ -259,6 +259,8 @@ export class MessageManager { * 增量同步会话数据 * @param userId 用户ID * @param serverData 服务器数据 + * @param options 同步选项 + * @param options.skipDelete 是否跳过删除检查(用于分页增量同步) * @returns 同步结果统计 */ static async syncSessions( @@ -267,6 +269,9 @@ export class MessageManager { friends?: ContractData[]; groups?: weChatGroup[]; }, + options?: { + skipDelete?: boolean; // 是否跳过删除检查(用于分页增量同步) + }, ): Promise<{ added: number; updated: number; @@ -323,10 +328,12 @@ export class MessageManager { } } - // 检查删除 - for (const localSession of localSessions) { - if (!serverSessionMap.has(localSession.serverId)) { - toDelete.push(localSession.serverId); + // 检查删除(仅在非增量同步模式下执行) + if (!options?.skipDelete) { + for (const localSession of localSessions) { + if (!serverSessionMap.has(localSession.serverId)) { + toDelete.push(localSession.serverId); + } } }