更新联系人分组逻辑:将分组类型从“group”更改为“chatroom”,并在联系人上下文菜单中添加移动分组的回调函数。调整相关组件以支持新的分组管理功能,确保在移动分组时使用正确的类型。
This commit is contained in:
@@ -35,7 +35,7 @@ export function getGroupList() {
|
||||
|
||||
// 移动分组
|
||||
interface MoveGroupData {
|
||||
type: "friend" | "group";
|
||||
type: "friend" | "chatroom";
|
||||
groupId: number;
|
||||
id: number;
|
||||
}
|
||||
|
||||
@@ -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<void>;
|
||||
onUpdateRemark?: (contact: Contact, remark: string) => Promise<void>;
|
||||
/** 移动分组回调 */
|
||||
onMoveGroup?: (contact: Contact, targetGroupId: number) => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,6 +53,7 @@ export const ContactContextMenu: React.FC<ContactContextMenuProps> = ({
|
||||
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<ContactContextMenuProps> = ({
|
||||
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<ContactContextMenuProps> = ({
|
||||
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<ContactContextMenuProps> = ({
|
||||
g => g.groupType === (contact.type === "group" ? 2 : 1),
|
||||
);
|
||||
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible && (
|
||||
<>
|
||||
<div
|
||||
className={styles.contextMenuOverlay}
|
||||
@@ -192,6 +193,8 @@ export const ContactContextMenu: React.FC<ContactContextMenuProps> = ({
|
||||
items={menuItems}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 修改备注Modal */}
|
||||
<Modal
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
.virtualList {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: inherit !important;
|
||||
}
|
||||
|
||||
.groupHeader {
|
||||
@@ -13,12 +14,7 @@
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
/* 样式由具体业务组件(如 WechatFriends/com.module.scss)控制,这里只负责布局和点击区域 */
|
||||
}
|
||||
|
||||
.contactItem {
|
||||
@@ -26,16 +22,7 @@
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: rgba(24, 144, 255, 0.1);
|
||||
}
|
||||
/* 避免样式穿透:不在这里设置边框/背景,全部交由内部渲染的联系人项样式控制 */
|
||||
}
|
||||
|
||||
.empty {
|
||||
|
||||
@@ -22,7 +22,7 @@ import styles from "./index.module.scss";
|
||||
const GROUP_HEADER_HEIGHT = 40;
|
||||
|
||||
/**
|
||||
* 联系人项高度(固定)
|
||||
* 联系人项高度(固定),由设计统一控制,避免高度动态变化造成折叠/抖动
|
||||
*/
|
||||
const CONTACT_ITEM_HEIGHT = 60;
|
||||
|
||||
@@ -188,9 +188,8 @@ export const VirtualContactList: React.FC<VirtualContactListProps> = ({
|
||||
} 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],
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<ToContractProps> = ({
|
||||
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<ToContractProps> = ({
|
||||
wechatAccountId: currentContact.wechatAccountId,
|
||||
});
|
||||
} else {
|
||||
// 好友转移:使用 WechatFriendAllot
|
||||
await WechatFriendAllot({
|
||||
wechatFriendId: currentContact.id,
|
||||
toAccountId: selectedTarget as number,
|
||||
|
||||
@@ -28,7 +28,7 @@ export function deleteGroup(id) {
|
||||
|
||||
//移动分组
|
||||
interface MoveGroupParams {
|
||||
type: "friend" | "group";
|
||||
type: "friend" | "chatroom";
|
||||
groupId: number;
|
||||
id: number;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@
|
||||
|
||||
.list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
// 避免嵌套滚动条,由外层容器控制滚动
|
||||
overflow-y: visible;
|
||||
|
||||
:global(.ant-list-item) {
|
||||
padding: 10px 15px;
|
||||
|
||||
@@ -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<WechatFriendsProps> = ({
|
||||
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<ContactGroup | undefined>();
|
||||
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<WechatFriendsProps> = ({
|
||||
);
|
||||
|
||||
// 打开新增分组弹窗
|
||||
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<WechatFriendsProps> = ({
|
||||
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<WechatFriendsProps> = ({
|
||||
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<WechatFriendsProps> = ({
|
||||
// 这里可以根据需要实现
|
||||
}, []);
|
||||
|
||||
// 处理修改备注
|
||||
// 处理修改备注:直接使用联系人自身的分组信息,不再遍历分组数据
|
||||
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<WechatFriendsProps> = ({
|
||||
// 渲染分组头部(用于虚拟滚动)
|
||||
const renderGroupHeader = useCallback(
|
||||
(group: ContactGroup, isExpanded: boolean) => {
|
||||
const displayCount =
|
||||
typeof group.count === "number" && group.count >= 0 ? group.count : "-";
|
||||
return (
|
||||
<div className={styles.groupHeader}>
|
||||
<span>{group.groupName}</span>
|
||||
<span className={styles.contactCount}>{group.count || 0}</span>
|
||||
<span className={styles.contactCount}>{displayCount}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -577,19 +578,6 @@ const ContactListSimple: React.FC<WechatFriendsProps> = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
// 不限制容器高度,让列表根据内容自动扩展
|
||||
// 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<WechatFriendsProps> = ({
|
||||
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<WechatFriendsProps> = ({
|
||||
cancelText="取消"
|
||||
>
|
||||
<Form form={groupForm} layout="vertical">
|
||||
<Form.Item
|
||||
name="groupType"
|
||||
label="分组类型"
|
||||
rules={[{ required: true, message: "请选择分组类型" }]}
|
||||
initialValue={currentGroupTypeForAdd || 1}
|
||||
>
|
||||
<Select placeholder="请选择分组类型">
|
||||
<Select.Option value={1}>好友分组</Select.Option>
|
||||
<Select.Option value={2}>群分组</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="groupName"
|
||||
label="分组名称"
|
||||
|
||||
@@ -31,11 +31,8 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({ 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<SidebarMenuProps> = ({ loading = false }) => {
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.tabItem} ${activeTab === "contracts" ? styles.active : ""}`}
|
||||
onClick={() => setActiveTab("contracts")}
|
||||
onClick={async () => {
|
||||
setActiveTab("contracts");
|
||||
try {
|
||||
const accountId = currentCustomer?.id || 0;
|
||||
// 每次切到联系人标签时,强制从接口刷新一次分组列表(通过全局 store 调用,避免 hook 实例问题)
|
||||
await useContactStoreNew
|
||||
.getState()
|
||||
.loadGroupsFromAPI(accountId);
|
||||
} catch (error) {
|
||||
console.error("刷新联系人分组失败:", error);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span>联系人</span>
|
||||
</div>
|
||||
|
||||
@@ -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<ContactStoreState>(
|
||||
* 设置分组列表
|
||||
*/
|
||||
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<ContactStoreState>(
|
||||
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<ContactStoreState>(
|
||||
// 第一页时,先尝试从缓存读取
|
||||
if (page === 1) {
|
||||
const cacheKey = `groupContacts_${groupKey}`;
|
||||
const cached = await groupContactsCache.get<GroupContactData>(cacheKey);
|
||||
const cached =
|
||||
await groupContactsCache.get<GroupContactData>(cacheKey);
|
||||
if (cached && cached.contacts && cached.contacts.length > 0) {
|
||||
// 立即显示缓存数据
|
||||
const groupData = new Map(state.groupData);
|
||||
@@ -341,29 +383,33 @@ export const useContactStoreNew = createPersistStore<ContactStoreState>(
|
||||
};
|
||||
|
||||
// 根据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<ContactStoreState>(
|
||||
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<ContactStoreState>(
|
||||
|
||||
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<ContactStoreState>(
|
||||
const state = get();
|
||||
set({ selectedAccountId: accountId });
|
||||
|
||||
// 重新加载展开的分组
|
||||
const expandedGroups = Array.from(state.expandedGroups);
|
||||
const groupData = new Map<string, GroupContactData>();
|
||||
// 重新加载展开的分组
|
||||
const expandedGroups = Array.from(state.expandedGroups);
|
||||
const groupData = new Map<string, GroupContactData>();
|
||||
|
||||
// 清理旧账号的数据
|
||||
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<ContactStoreState>(
|
||||
|
||||
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<ContactStoreState>(
|
||||
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<ContactStoreState>(
|
||||
|
||||
// 更新缓存
|
||||
const cacheKey = `groupContacts_${groupKey}`;
|
||||
const cachedData = await groupContactsCache.get<GroupContactData>(
|
||||
cacheKey,
|
||||
);
|
||||
const cachedData =
|
||||
await groupContactsCache.get<GroupContactData>(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<ContactStoreState>(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 同步更新会话列表中的备注名称
|
||||
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<ContactStoreState>(
|
||||
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<ContactStoreState>(
|
||||
|
||||
// 更新缓存
|
||||
const cacheKey = `groupContacts_${toGroupKey}`;
|
||||
const cachedData = await groupContactsCache.get<GroupContactData>(
|
||||
cacheKey,
|
||||
);
|
||||
const cachedData =
|
||||
await groupContactsCache.get<GroupContactData>(cacheKey);
|
||||
if (cachedData) {
|
||||
await groupContactsCache.set(cacheKey, {
|
||||
...cachedData,
|
||||
|
||||
Reference in New Issue
Block a user