From 65167be7f8ae38822ea63678f7c4a12594e1071d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=98=E9=A3=8E?= Date: Wed, 17 Dec 2025 16:20:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=81=94=E7=B3=BB=E4=BA=BA?= =?UTF-8?q?=E5=88=86=E7=BB=84=E9=80=BB=E8=BE=91=EF=BC=9A=E5=B0=86=E5=88=86?= =?UTF-8?q?=E7=BB=84=E7=B1=BB=E5=9E=8B=E4=BB=8E=E2=80=9Cgroup=E2=80=9D?= =?UTF-8?q?=E6=9B=B4=E6=94=B9=E4=B8=BA=E2=80=9Cchatroom=E2=80=9D=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E5=9C=A8=E8=81=94=E7=B3=BB=E4=BA=BA=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=E8=8F=9C=E5=8D=95=E4=B8=AD=E6=B7=BB=E5=8A=A0=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E5=88=86=E7=BB=84=E7=9A=84=E5=9B=9E=E8=B0=83=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E3=80=82=E8=B0=83=E6=95=B4=E7=9B=B8=E5=85=B3=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=BB=A5=E6=94=AF=E6=8C=81=E6=96=B0=E7=9A=84=E5=88=86?= =?UTF-8?q?=E7=BB=84=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E5=9C=A8=E7=A7=BB=E5=8A=A8=E5=88=86=E7=BB=84=E6=97=B6?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=AD=A3=E7=A1=AE=E7=9A=84=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Touchkebao/src/api/module/group.ts | 2 +- .../components/ContactContextMenu/index.tsx | 25 +- .../VirtualContactList/index.module.scss | 19 +- .../components/VirtualContactList/index.tsx | 7 +- Touchkebao/src/pages/pc/ckbox/weChat/api.ts | 16 +- .../components/toContract/index.tsx | 12 +- .../SidebarMenu/WechatFriends/api.ts | 2 +- .../SidebarMenu/WechatFriends/com.module.scss | 3 +- .../SidebarMenu/WechatFriends/index.tsx | 165 +++++++----- .../weChat/components/SidebarMenu/index.tsx | 20 +- .../src/store/module/weChat/contacts.new.ts | 248 ++++++++++++------ 11 files changed, 316 insertions(+), 203 deletions(-) diff --git a/Touchkebao/src/api/module/group.ts b/Touchkebao/src/api/module/group.ts index 5090fb0e..f5aa1161 100644 --- a/Touchkebao/src/api/module/group.ts +++ b/Touchkebao/src/api/module/group.ts @@ -35,7 +35,7 @@ export function getGroupList() { // 移动分组 interface MoveGroupData { - type: "friend" | "group"; + type: "friend" | "chatroom"; groupId: number; id: number; } diff --git a/Touchkebao/src/components/ContactContextMenu/index.tsx b/Touchkebao/src/components/ContactContextMenu/index.tsx index 0a8850b1..bbb53788 100644 --- a/Touchkebao/src/components/ContactContextMenu/index.tsx +++ b/Touchkebao/src/components/ContactContextMenu/index.tsx @@ -8,7 +8,6 @@ import { Menu, Modal, Input, Form, Select, message } from "antd"; import { EditOutlined, SwapOutlined } from "@ant-design/icons"; import { Contact } from "@/utils/db"; import { ContactGroup } from "@/store/module/weChat/contacts.data"; -import { moveGroup } from "@/api/module/group"; import styles from "./index.module.scss"; /** @@ -29,7 +28,9 @@ export interface ContactContextMenuProps { /** 操作完成回调 */ onComplete?: () => void; /** 修改备注回调 */ - onUpdateRemark?: (contactId: number, remark: string) => Promise; + onUpdateRemark?: (contact: Contact, remark: string) => Promise; + /** 移动分组回调 */ + onMoveGroup?: (contact: Contact, targetGroupId: number) => Promise; } /** @@ -52,6 +53,7 @@ export const ContactContextMenu: React.FC = ({ onClose, onComplete, onUpdateRemark, + onMoveGroup, }) => { const [remarkForm] = Form.useForm<{ remark: string }>(); const [moveForm] = Form.useForm<{ targetGroupId: number }>(); @@ -102,7 +104,7 @@ export const ContactContextMenu: React.FC = ({ setLoading(true); if (onUpdateRemark) { - await onUpdateRemark(contact.id, values.remark); + await onUpdateRemark(contact, values.remark); message.success("修改备注成功"); } else { message.warning("修改备注功能未实现"); @@ -129,13 +131,12 @@ export const ContactContextMenu: React.FC = ({ const values = await moveForm.validateFields(); setLoading(true); - await moveGroup({ - type: contact.type === "group" ? "group" : "friend", - groupId: values.targetGroupId, - id: contact.id, - }); - + if (onMoveGroup) { + await onMoveGroup(contact, values.targetGroupId); message.success("移动分组成功"); + } else { + message.warning("移动分组功能未实现"); + } setMoveModalVisible(false); moveForm.resetFields(); onComplete?.(); @@ -172,9 +173,9 @@ export const ContactContextMenu: React.FC = ({ g => g.groupType === (contact.type === "group" ? 2 : 1), ); - if (!visible) return null; - return ( + <> + {visible && ( <>
= ({ items={menuItems} onClick={onClose} /> + + )} {/* 修改备注Modal */} = ({ } else if (item.type === "loadMore") { return LOAD_MORE_ITEM_HEIGHT; } else { - // 联系人项,使用固定高度或缓存的高度 - const cachedHeight = itemHeightsRef.current.get(index); - return cachedHeight || CONTACT_ITEM_HEIGHT; + // 联系人项使用固定高度,避免动态计算造成的样式折叠 + return CONTACT_ITEM_HEIGHT; } }, [virtualItems], diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts index 3ceefe75..2df43fb9 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/api.ts @@ -93,10 +93,9 @@ function jsonToQueryString(json) { } return params.toString(); } -//转移客户 +//转移好友 export function WechatFriendAllot(params: { wechatFriendId?: number; - wechatChatroomId?: number; toAccountId: number; notifyReceiver: boolean; comment: string; @@ -107,6 +106,19 @@ export function WechatFriendAllot(params: { "PUT", ); } +//转移群 +export function WechatChatroomAllot(params: { + wechatChatroomId?: number; + toAccountId: number; + notifyReceiver: boolean; + comment: string; +}) { + return request2( + "/api/wechatChatroom/allot?" + jsonToQueryString(params), + undefined, + "PUT", + ); +} //获取可转移客服列表 export function getTransferableAgentList() { diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx index 2d0e4c0e..313a58d7 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/components/toContract/index.tsx @@ -5,6 +5,7 @@ import { getTransferableAgentList, WechatFriendAllot, WechatFriendRebackAllot, + WechatChatroomAllot, } from "@/pages/pc/ckbox/weChat/api"; import { dataProcessing } from "@/api/ai"; import { useCurrentContact } from "@/store/module/weChat/weChat"; @@ -75,10 +76,14 @@ const ToContract: React.FC = ({ try { setLoading(true); - // 调用转接接口 + // 调用转接接口:区分好友 / 群聊 if (currentContact) { - if ("chatroomId" in currentContact && currentContact.chatroomId) { - await WechatFriendAllot({ + const isGroup = + "chatroomId" in currentContact && !!currentContact.chatroomId; + + if (isGroup) { + // 群聊转移:使用 WechatChatroomAllot + await WechatChatroomAllot({ wechatChatroomId: currentContact.id, toAccountId: selectedTarget as number, notifyReceiver: true, @@ -91,6 +96,7 @@ const ToContract: React.FC = ({ wechatAccountId: currentContact.wechatAccountId, }); } else { + // 好友转移:使用 WechatFriendAllot await WechatFriendAllot({ wechatFriendId: currentContact.id, toAccountId: selectedTarget as number, diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/api.ts index fbd6c91c..8b4adb27 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/api.ts +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/api.ts @@ -28,7 +28,7 @@ export function deleteGroup(id) { //移动分组 interface MoveGroupParams { - type: "friend" | "group"; + type: "friend" | "chatroom"; groupId: number; id: number; } diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/com.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/com.module.scss index bb5c4e5f..a7a7ac07 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/com.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/com.module.scss @@ -74,7 +74,8 @@ .list { flex: 1; - overflow-y: auto; + // 避免嵌套滚动条,由外层容器控制滚动 + overflow-y: visible; :global(.ant-list-item) { padding: 10px 15px; 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 eb589af3..34d1946a 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,5 +1,14 @@ import React, { useState, useCallback, useEffect, useRef } from "react"; -import { List, Avatar, Skeleton, Modal, Form, Input, message } from "antd"; +import { + List, + Avatar, + Skeleton, + Modal, + Form, + Input, + Select, + message, +} from "antd"; import dayjs from "dayjs"; import styles from "./com.module.scss"; import { Contact, ChatSession } from "@/utils/db"; @@ -107,13 +116,16 @@ const ContactListSimple: React.FC = ({ groupName: string; groupMemo: string; sort: number; + groupType: 1 | 2; }>(); const [addGroupVisible, setAddGroupVisible] = useState(false); const [editGroupVisible, setEditGroupVisible] = useState(false); const [deleteGroupVisible, setDeleteGroupVisible] = useState(false); const [groupModalLoading, setGroupModalLoading] = useState(false); const [editingGroup, setEditingGroup] = useState(); - const [currentGroupTypeForAdd, setCurrentGroupTypeForAdd] = useState<1 | 2>(1); + const [currentGroupTypeForAdd, setCurrentGroupTypeForAdd] = useState<1 | 2>( + 1, + ); // 生成分组Key的函数 const getGroupKey = useCallback( @@ -162,28 +174,35 @@ const ContactListSimple: React.FC = ({ ); // 打开新增分组弹窗 - const handleOpenAddGroupModal = useCallback((groupType: 1 | 2) => { - setCurrentGroupTypeForAdd(groupType); - groupForm.resetFields(); - groupForm.setFieldsValue({ - groupName: "", - groupMemo: "", - sort: 0, - }); - setAddGroupVisible(true); - }, [groupForm]); + const handleOpenAddGroupModal = useCallback( + (groupType: 1 | 2) => { + setCurrentGroupTypeForAdd(groupType); + groupForm.resetFields(); + groupForm.setFieldsValue({ + groupName: "", + groupMemo: "", + sort: 0, + groupType: groupType || 1, // 默认使用右键菜单传过来的类型,如果没有则默认为1(好友分组) + }); + setAddGroupVisible(true); + }, + [groupForm], + ); // 打开编辑分组弹窗 - const handleOpenEditGroupModal = useCallback((group: ContactGroup) => { - setEditingGroup(group); - groupForm.resetFields(); - groupForm.setFieldsValue({ - groupName: group.groupName, - groupMemo: group.groupMemo || "", - sort: group.sort || 0, - }); - setEditGroupVisible(true); - }, [groupForm]); + const handleOpenEditGroupModal = useCallback( + (group: ContactGroup) => { + setEditingGroup(group); + groupForm.resetFields(); + groupForm.setFieldsValue({ + groupName: group.groupName, + groupMemo: group.groupMemo || "", + sort: group.sort || 0, + }); + setEditGroupVisible(true); + }, + [groupForm], + ); // 打开删除分组确认弹窗 const handleOpenDeleteGroupModal = useCallback((group: ContactGroup) => { @@ -200,7 +219,7 @@ const ContactListSimple: React.FC = ({ await useContactStoreNew.getState().addGroup({ groupName: values.groupName, groupMemo: values.groupMemo || "", - groupType: currentGroupTypeForAdd, + groupType: values.groupType || 1, // 必填,默认1(好友分组) sort: values.sort || 0, }); @@ -254,10 +273,9 @@ const ContactListSimple: React.FC = ({ try { setGroupModalLoading(true); - await useContactStoreNew.getState().deleteGroup( - editingGroup.id, - editingGroup.groupType, - ); + await useContactStoreNew + .getState() + .deleteGroup(editingGroup.id, editingGroup.groupType); message.success("删除分组成功"); setDeleteGroupVisible(false); @@ -291,40 +309,21 @@ const ContactListSimple: React.FC = ({ // 这里可以根据需要实现 }, []); - // 处理修改备注 + // 处理修改备注:直接使用联系人自身的分组信息,不再遍历分组数据 const handleUpdateRemark = useCallback( - async (contactId: number, remark: string) => { - // 找到联系人所在的分组 - let foundGroup: ContactGroup | undefined; - let foundGroupKey: string | undefined; + async (contact: Contact, remark: string) => { + // 联系人上已经带有 groupId 与 type 信息 + const groupId = contact.groupId ?? 0; + const groupType: 1 | 2 = contact.type === "group" ? 2 : 1; - for (const [groupKey, groupDataItem] of newGroupData.entries()) { - const contact = groupDataItem.contacts.find(c => c.id === contactId); - if (contact) { - foundGroupKey = groupKey; - // 从groupKey解析groupId和groupType - const [groupId, groupType] = groupKey.split("_"); - // 使用newGroups查找分组(优先使用新架构的数据) - foundGroup = newGroups.find( - g => g.id === Number(groupId) && g.groupType === Number(groupType), - ); - break; - } - } - - if (foundGroup && foundGroupKey) { - const [groupId, groupType] = foundGroupKey.split("_"); - await updateContactRemark( - contactId, - Number(groupId), - Number(groupType) as 1 | 2, - remark, - ); - } else { - message.error("未找到联系人所在的分组"); + try { + await updateContactRemark(contact.id, groupId, groupType, remark); + } catch (error) { + console.error("更新联系人备注失败:", error); + message.error("更新备注失败,请稍后重试"); } }, - [updateContactRemark, newGroupData, newGroups], + [updateContactRemark], ); // 从服务器同步数据(静默同步,不显示提示) @@ -551,10 +550,12 @@ const ContactListSimple: React.FC = ({ // 渲染分组头部(用于虚拟滚动) const renderGroupHeader = useCallback( (group: ContactGroup, isExpanded: boolean) => { + const displayCount = + typeof group.count === "number" && group.count >= 0 ? group.count : "-"; return (
{group.groupName} - {group.count || 0} + {displayCount}
); }, @@ -577,19 +578,6 @@ const ContactListSimple: React.FC = ({
); - // 不限制容器高度,让列表根据内容自动扩展 - // const [containerHeight, setContainerHeight] = useState(600); - // useEffect(() => { - // const updateHeight = () => { - // if (virtualListRef.current) { - // const rect = virtualListRef.current.getBoundingClientRect(); - // setContainerHeight(rect.height || 600); - // } - // }; - // updateHeight(); - // window.addEventListener("resize", updateHeight); - // return () => window.removeEventListener("resize", updateHeight); - // }, []); const containerHeight = undefined; // 不限制高度,使用内容总高度 // 处理分组展开/折叠(使用新架构的方法) @@ -688,9 +676,31 @@ const ContactListSimple: React.FC = ({ x={contactContextMenu.x} y={contactContextMenu.y} visible={contactContextMenu.visible} - onClose={() => setContactContextMenu({ visible: false, x: 0, y: 0 })} + onClose={() => + setContactContextMenu(prev => ({ + ...prev, + visible: false, + })) + } onComplete={handleContactOperationComplete} onUpdateRemark={handleUpdateRemark} + onMoveGroup={async (contact, targetGroupId) => { + const fromGroupId = contact.groupId ?? 0; + const groupType: 1 | 2 = contact.type === "group" ? 2 : 1; + try { + await useContactStoreNew + .getState() + .moveContactToGroup( + contact.id, + fromGroupId, + targetGroupId, + groupType, + ); + } catch (error) { + console.error("移动分组失败:", error); + message.error("移动分组失败,请稍后重试"); + } + }} /> )} @@ -708,6 +718,17 @@ const ContactListSimple: React.FC = ({ cancelText="取消" >
+ + + = ({ loading = false }) => { } = useContactStore(); // 使用新架构的ContactStore进行搜索 - const { - searchKeyword, - searchContacts, - clearSearch, - } = useContactStoreNew(); + const contactStoreNew = useContactStoreNew(); + const { searchKeyword, searchContacts, clearSearch } = contactStoreNew; const currentCustomer = useCustomerStore(state => state.currentCustomer); const { setCurrentContact } = useWeChatStore(); @@ -222,7 +219,18 @@ const SidebarMenu: React.FC = ({ loading = false }) => {
setActiveTab("contracts")} + onClick={async () => { + setActiveTab("contracts"); + try { + const accountId = currentCustomer?.id || 0; + // 每次切到联系人标签时,强制从接口刷新一次分组列表(通过全局 store 调用,避免 hook 实例问题) + await useContactStoreNew + .getState() + .loadGroupsFromAPI(accountId); + } catch (error) { + console.error("刷新联系人分组失败:", error); + } + }} > 联系人
diff --git a/Touchkebao/src/store/module/weChat/contacts.new.ts b/Touchkebao/src/store/module/weChat/contacts.new.ts index e8e2964c..7307edbc 100644 --- a/Touchkebao/src/store/module/weChat/contacts.new.ts +++ b/Touchkebao/src/store/module/weChat/contacts.new.ts @@ -20,7 +20,12 @@ import { getGroupList, getLabelsListByGroup, } from "@/pages/pc/ckbox/weChat/api"; -import { addGroup, updateGroup, deleteGroup, moveGroup } from "@/api/module/group"; +import { + addGroup, + updateGroup, + deleteGroup, + moveGroup, +} from "@/api/module/group"; import { updateFriendInfo } from "@/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/api"; import { convertFriendsToContacts, @@ -32,6 +37,7 @@ import { groupStatsCache, } from "@/utils/cache"; import { ContactManager } from "@/utils/dbAction/contact"; +import { useMessageStore } from "./message"; import { performanceMonitor } from "@/utils/performance"; /** @@ -81,15 +87,21 @@ export const useContactStoreNew = createPersistStore( * 设置分组列表 */ setGroups: async (groups: ContactGroup[]) => { + // 先重置人数为占位值 -1,等真正展开分组、拿到 total 后再更新 + const groupsWithPlaceholder = groups.map(g => ({ + ...g, + count: -1, + })); + // 按自定义规则排序: // 1. 默认群分组(groupType=2 且 id=0)始终在最前 - // 2. 未分组(groupType=1 且 id=99999999)始终在最后 + // 2. 未分组(groupType=1 且 id=0)始终在最后 // 3. 其他分组按 sort 升序 - const sortedGroups = [...groups].sort((a, b) => { + const sortedGroups = [...groupsWithPlaceholder].sort((a, b) => { const isDefaultGroupA = a.groupType === 2 && a.id === 0; const isDefaultGroupB = b.groupType === 2 && b.id === 0; - const isUngroupedA = a.groupType === 1 && a.id === 99999999; - const isUngroupedB = b.groupType === 1 && b.id === 99999999; + const isUngroupedA = a.groupType === 1 && a.id === 0; + const isUngroupedB = b.groupType === 1 && b.id === 0; // 默认群分组始终最前 if (isDefaultGroupA && !isDefaultGroupB) return -1; @@ -144,39 +156,68 @@ export const useContactStoreNew = createPersistStore( const params: any = { page: 1, limit: 1000, // 分组列表通常不会太多 + // 即便为 0 也要把 wechatAccountId 传给接口,由后端决定含义 + wechatAccountId: accountId, }; - if (accountId !== 0) { - params.wechatAccountId = accountId; - } + // 单次请求分组列表,按 groupType 区分好友/群分组 + const result = await getLabelsListByGroup(params); + const list = result?.list || []; - // 并行请求好友分组和群分组 - const [friendsResult, groupsResult] = await Promise.all([ - getLabelsListByGroup(1, params, { debounceGap: 0 }), - getLabelsListByGroup(2, params, { debounceGap: 0 }), - ]); - - const friendGroups: ContactGroup[] = - friendsResult?.list?.map((item: any) => ({ + const friendGroups: ContactGroup[] = list + .filter((item: any) => Number(item.groupType) === 1) + .map((item: any) => ({ id: item.id, groupName: item.groupName, groupType: 1, count: item.count || 0, sort: item.sort || 0, groupMemo: item.groupMemo || "", - })) || []; + })); - const groupGroups: ContactGroup[] = - groupsResult?.list?.map((item: any) => ({ + const groupGroups: ContactGroup[] = list + .filter((item: any) => Number(item.groupType) === 2) + .map((item: any) => ({ id: item.id, groupName: item.groupName, groupType: 2, count: item.count || 0, sort: item.sort || 0, groupMemo: item.groupMemo || "", - })) || []; + })); - const allGroups = [...friendGroups, ...groupGroups]; + // 额外补充:默认群分组 & 未分组(接口不返回,由前端约定) + const extraGroups: ContactGroup[] = []; + + const hasDefaultGroup = groupGroups.some( + g => g.id === 0 && g.groupType === 2, + ); + if (!hasDefaultGroup) { + extraGroups.push({ + id: 0, + groupName: "默认群分组", + groupType: 2, + count: 0, + sort: -9999, + groupMemo: "", + }); + } + + const hasUngrouped = friendGroups.some( + g => g.id === 0 && g.groupType === 1, + ); + if (!hasUngrouped) { + extraGroups.push({ + id: 0, + groupName: "未分组", + groupType: 1, + count: 0, + sort: 999999, + groupMemo: "", + }); + } + + const allGroups = [...extraGroups, ...friendGroups, ...groupGroups]; await get().setGroups(allGroups); return allGroups; @@ -244,7 +285,8 @@ export const useContactStoreNew = createPersistStore( // 第一页时,先尝试从缓存读取 if (page === 1) { const cacheKey = `groupContacts_${groupKey}`; - const cached = await groupContactsCache.get(cacheKey); + const cached = + await groupContactsCache.get(cacheKey); if (cached && cached.contacts && cached.contacts.length > 0) { // 立即显示缓存数据 const groupData = new Map(state.groupData); @@ -341,29 +383,33 @@ export const useContactStoreNew = createPersistStore( }; // 根据groupType调用不同的API + let total = 0; + if (groupType === 1) { // 好友列表API - if (groupId !== 0) { - params.groupId = groupId; // 分组ID - } - if (accountId !== 0) { - params.wechatAccountId = accountId; - } + // 分组ID:即便为 0(未分组)也要传给接口,由后端决定语义 + params.groupId = groupId; + // 账号ID:即便为 0 也要传 + params.wechatAccountId = accountId; const result = await getContactList(params, { debounceGap: 0 }); const friendList = result?.list || []; + total = + typeof result?.total === "number" + ? result.total + : friendList.length; contacts = convertFriendsToContacts(friendList, userId); } else if (groupType === 2) { // 群列表API - if (groupId !== 0) { - params.groupId = groupId; // 分组ID - } - if (accountId !== 0) { - params.wechatAccountId = accountId; - } + // 分组ID:即便为 0(默认群分组)也要传给接口 + params.groupId = groupId; + // 账号ID:即便为 0 也要传 + params.wechatAccountId = accountId; const result = await getGroupList(params, { debounceGap: 0 }); const groupList = result?.list || []; + total = + typeof result?.total === "number" ? result.total : groupList.length; contacts = convertGroupsToContacts(groupList, userId); } @@ -373,10 +419,7 @@ export const useContactStoreNew = createPersistStore( const existingContacts = latestGroupData?.contacts || []; const updatedData: GroupContactData = { - contacts: - page === 1 - ? contacts - : [...existingContacts, ...contacts], // 使用最新的 contacts,而不是 currentData + contacts: page === 1 ? contacts : [...existingContacts, ...contacts], // 使用最新的 contacts,而不是 currentData page, pageSize: limit, hasMore: contacts.length === limit, @@ -387,7 +430,15 @@ export const useContactStoreNew = createPersistStore( const updatedGroupData = new Map(latestState.groupData); updatedGroupData.set(groupKey, updatedData); - set({ groupData: updatedGroupData }); + + // 同步更新分组人数:使用接口返回的 total + const updatedGroups = latestState.groups.map(g => + g.id === groupId && g.groupType === groupType + ? { ...g, count: total } + : g, + ); + + set({ groupData: updatedGroupData, groups: updatedGroups }); // 缓存第一页数据 if (page === 1) { @@ -523,48 +574,48 @@ export const useContactStoreNew = createPersistStore( const state = get(); set({ selectedAccountId: accountId }); - // 重新加载展开的分组 - const expandedGroups = Array.from(state.expandedGroups); - const groupData = new Map(); + // 重新加载展开的分组 + const expandedGroups = Array.from(state.expandedGroups); + const groupData = new Map(); - // 清理旧账号的数据 - for (const groupKey of expandedGroups) { - // 检查是否是当前账号的分组 - const parts = groupKey.split("_"); - if (parts.length === 3) { - const oldAccountId = parseInt(parts[2], 10); - if (oldAccountId === accountId) { - // 保留当前账号的数据 - const oldData = state.groupData.get(groupKey); - if (oldData) { - groupData.set(groupKey, oldData); + // 清理旧账号的数据 + for (const groupKey of expandedGroups) { + // 检查是否是当前账号的分组 + const parts = groupKey.split("_"); + if (parts.length === 3) { + const oldAccountId = parseInt(parts[2], 10); + if (oldAccountId === accountId) { + // 保留当前账号的数据 + const oldData = state.groupData.get(groupKey); + if (oldData) { + groupData.set(groupKey, oldData); + } + } } } - } - } - set({ groupData }); + set({ groupData }); - // 重新加载展开的分组 - for (const groupKey of expandedGroups) { - const parts = groupKey.split("_"); - if (parts.length === 3) { - const groupId = parseInt(parts[0], 10); - const groupType = parseInt(parts[1], 10) as 1 | 2; - const oldAccountId = parseInt(parts[2], 10); + // 重新加载展开的分组 + for (const groupKey of expandedGroups) { + const parts = groupKey.split("_"); + if (parts.length === 3) { + const groupId = parseInt(parts[0], 10); + const groupType = parseInt(parts[1], 10) as 1 | 2; + const oldAccountId = parseInt(parts[2], 10); - if (oldAccountId !== accountId) { - // 不同账号,需要重新加载 - const newGroupKey = getGroupKey(groupId, groupType, accountId); - const expandedGroupsNew = new Set(state.expandedGroups); - expandedGroupsNew.delete(groupKey); - expandedGroupsNew.add(newGroupKey); - set({ expandedGroups: expandedGroupsNew }); + if (oldAccountId !== accountId) { + // 不同账号,需要重新加载 + const newGroupKey = getGroupKey(groupId, groupType, accountId); + const expandedGroupsNew = new Set(state.expandedGroups); + expandedGroupsNew.delete(groupKey); + expandedGroupsNew.add(newGroupKey); + set({ expandedGroups: expandedGroupsNew }); - await get().loadGroupContacts(groupId, groupType, 1, 50); + await get().loadGroupContacts(groupId, groupType, 1, 50); + } + } } - } - } }, { accountId, expandedGroupsCount: state.expandedGroups.size }, ); @@ -585,11 +636,15 @@ export const useContactStoreNew = createPersistStore( const newGroup: ContactGroup = { id: result?.id || result?.data?.id || 0, - groupName: result?.groupName || result?.data?.groupName || group.groupName, - groupType: (result?.groupType || result?.data?.groupType || group.groupType) as 1 | 2, + groupName: + result?.groupName || result?.data?.groupName || group.groupName, + groupType: (result?.groupType || + result?.data?.groupType || + group.groupType) as 1 | 2, count: 0, // 新分组初始数量为0 sort: result?.sort || result?.data?.sort || group.sort || 0, - groupMemo: result?.groupMemo || result?.data?.groupMemo || group.groupMemo, + groupMemo: + result?.groupMemo || result?.data?.groupMemo || group.groupMemo, }; const groups = [...get().groups, newGroup]; @@ -613,9 +668,7 @@ export const useContactStoreNew = createPersistStore( sort: group.sort || 0, }); - const groups = get().groups.map(g => - g.id === group.id ? group : g, - ); + const groups = get().groups.map(g => (g.id === group.id ? group : g)); get().setGroups(groups); } catch (error) { console.error("更新分组失败:", error); @@ -766,9 +819,8 @@ export const useContactStoreNew = createPersistStore( // 更新缓存 const cacheKey = `groupContacts_${groupKey}`; - const cachedData = await groupContactsCache.get( - cacheKey, - ); + const cachedData = + await groupContactsCache.get(cacheKey); if (cachedData && cachedData.contacts) { const updatedContacts = cachedData.contacts.map(c => c.id === contactId ? { ...c, conRemark: remark } : c, @@ -803,6 +855,30 @@ export const useContactStoreNew = createPersistStore( }); } } + + // 同步更新会话列表中的备注名称 + try { + const messageState = useMessageStore.getState(); + const existingSessions = messageState.allSessions; + if (existingSessions && existingSessions.length > 0) { + existingSessions + .filter( + s => + s.id === contactId && + (groupType === 1 + ? s.type === "friend" + : s.type === "group"), + ) + .forEach(s => { + messageState.addSession({ + ...s, + conRemark: remark, + }); + }); + } + } catch (err) { + console.error("同步会话列表备注失败:", err); + } } catch (error) { console.error("更新联系人备注失败:", error); throw error; @@ -850,7 +926,8 @@ export const useContactStoreNew = createPersistStore( try { // 调用API移动分组 await moveGroup({ - type: groupType === 1 ? "friend" : "group", + // 好友用 friend,群聊用 chatroom + type: groupType === 1 ? "friend" : "chatroom", groupId: toGroupId, id: contactId, }); @@ -895,9 +972,8 @@ export const useContactStoreNew = createPersistStore( // 更新缓存 const cacheKey = `groupContacts_${toGroupKey}`; - const cachedData = await groupContactsCache.get( - cacheKey, - ); + const cachedData = + await groupContactsCache.get(cacheKey); if (cachedData) { await groupContactsCache.set(cacheKey, { ...cachedData,