重构联系人管理逻辑,新增联系人状态管理和数据同步功能,优化联系人列表组件以提升用户体验和代码可读性。
This commit is contained in:
@@ -33,29 +33,20 @@ export interface CkUserInfo {
|
||||
export interface CkChatState {
|
||||
userInfo: CkUserInfo | null;
|
||||
isLoggedIn: boolean;
|
||||
searchKeyword: string;
|
||||
isLoadWeChat: boolean;
|
||||
getIsLoadWeChat: () => boolean;
|
||||
updateIsLoadWeChat: (isLoadWeChat: boolean) => void;
|
||||
contractList: ContractData[];
|
||||
chatSessions: any[];
|
||||
kfUserList: KfUserListData[];
|
||||
kfSelected: number;
|
||||
getKfSelectedUser: () => KfUserListData | undefined;
|
||||
countLables: ContactGroupByLabel[];
|
||||
newContractList: ContactGroupByLabel[];
|
||||
getContractList: () => ContractData[];
|
||||
getSomeContractList: (kfSelected: number) => ContractData[];
|
||||
getNewContractList: () => Promise<ContactGroupByLabel[]>;
|
||||
setSearchKeyword: (keyword: string) => void;
|
||||
clearSearchKeyword: () => void;
|
||||
asyncKfSelected: (data: number) => void;
|
||||
asyncWeChatGroup: (data: weChatGroup[]) => void;
|
||||
asyncCountLables: (data: ContactGroupByLabel[]) => void;
|
||||
getkfUserList: () => KfUserListData[];
|
||||
asyncKfUserList: (data: KfUserListData[]) => void;
|
||||
getKfUserInfo: (wechatAccountId: number) => KfUserListData | undefined;
|
||||
asyncContractList: (data: ContractData[]) => void;
|
||||
getChatSessions: () => any[];
|
||||
asyncChatSessions: (data: any[]) => void;
|
||||
updateChatSession: (session: ContractData | weChatGroup) => void;
|
||||
|
||||
@@ -17,13 +17,10 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
set => ({
|
||||
userInfo: null,
|
||||
isLoggedIn: false,
|
||||
contractList: [], //联系人列表
|
||||
chatSessions: [], //聊天会话
|
||||
kfUserList: [], //客服列表
|
||||
countLables: [], //标签列表
|
||||
newContractList: [], //联系人分组
|
||||
kfSelected: 0, //选中的客服
|
||||
searchKeyword: "", //搜索关键词
|
||||
isLoadWeChat: false, //是否加载微信
|
||||
getIsLoadWeChat: () => {
|
||||
return useCkChatStore.getState().isLoadWeChat;
|
||||
@@ -44,27 +41,10 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
// 异步设置标签列表
|
||||
asyncCountLables: async (data: ContactGroupByLabel[]) => {
|
||||
set({ countLables: data });
|
||||
// 清除getNewContractList缓存
|
||||
const state = useCkChatStore.getState();
|
||||
if (
|
||||
state.getNewContractList &&
|
||||
typeof state.getNewContractList === "function"
|
||||
) {
|
||||
// 触发缓存重新计算
|
||||
await state.getNewContractList();
|
||||
}
|
||||
},
|
||||
// 设置搜索关键词
|
||||
setSearchKeyword: (keyword: string) => {
|
||||
set({ searchKeyword: keyword });
|
||||
},
|
||||
// 清除搜索关键词
|
||||
clearSearchKeyword: () => {
|
||||
set({ searchKeyword: "" });
|
||||
},
|
||||
asyncKfSelected: async (data: number) => {
|
||||
set({ kfSelected: data });
|
||||
// 清除getChatSessions、getContractList和getNewContractList缓存
|
||||
// 清除getChatSessions缓存
|
||||
const state = useCkChatStore.getState();
|
||||
if (
|
||||
state.getChatSessions &&
|
||||
@@ -73,151 +53,8 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
// 触发缓存重新计算
|
||||
state.getChatSessions();
|
||||
}
|
||||
if (
|
||||
state.getContractList &&
|
||||
typeof state.getContractList === "function"
|
||||
) {
|
||||
// 触发缓存重新计算
|
||||
state.getContractList();
|
||||
}
|
||||
if (
|
||||
state.getNewContractList &&
|
||||
typeof state.getNewContractList === "function"
|
||||
) {
|
||||
// 触发缓存重新计算
|
||||
await state.getNewContractList();
|
||||
}
|
||||
},
|
||||
|
||||
// 获取联系人分组列表 - 使用缓存避免无限循环
|
||||
getNewContractList: (() => {
|
||||
let cachedResult: any = null;
|
||||
let lastKfSelected: number | null = null;
|
||||
let lastCountLablesLength: number = 0;
|
||||
let lastSearchKeyword: string = "";
|
||||
|
||||
return async () => {
|
||||
const state = useCkChatStore.getState();
|
||||
|
||||
// 检查是否需要重新计算缓存
|
||||
const shouldRecalculate =
|
||||
cachedResult === null ||
|
||||
lastKfSelected !== state.kfSelected ||
|
||||
lastCountLablesLength !== (state.countLables?.length || 0) ||
|
||||
lastSearchKeyword !== state.searchKeyword;
|
||||
|
||||
if (shouldRecalculate) {
|
||||
// 使用createContractList构建联系人分组数据
|
||||
let contractList = await createContractList(
|
||||
state.kfSelected,
|
||||
state.countLables,
|
||||
);
|
||||
|
||||
// 根据搜索关键词筛选联系人分组
|
||||
if (state.searchKeyword.trim()) {
|
||||
const keyword = state.searchKeyword.toLowerCase();
|
||||
contractList = contractList
|
||||
.map(group => ({
|
||||
...group,
|
||||
contracts:
|
||||
group.contracts?.filter(item => {
|
||||
const nickname = (item.nickname || "").toLowerCase();
|
||||
const conRemark = (item.conRemark || "").toLowerCase();
|
||||
return (
|
||||
nickname.includes(keyword) || conRemark.includes(keyword)
|
||||
);
|
||||
}) || [],
|
||||
}))
|
||||
.filter(group => group.contracts.length > 0);
|
||||
}
|
||||
|
||||
cachedResult = contractList;
|
||||
lastKfSelected = state.kfSelected;
|
||||
lastCountLablesLength = state.countLables?.length || 0;
|
||||
lastSearchKeyword = state.searchKeyword;
|
||||
}
|
||||
|
||||
return cachedResult;
|
||||
};
|
||||
})(),
|
||||
// 搜索好友和群组的新方法 - 从本地数据库查询并返回扁平化的搜索结果
|
||||
searchContactsAndGroups: (() => {
|
||||
let cachedResult: (ContractData | weChatGroup)[] = [];
|
||||
let lastKfSelected: number | null = null;
|
||||
let lastSearchKeyword: string = "";
|
||||
|
||||
return async () => {
|
||||
const state = useCkChatStore.getState();
|
||||
|
||||
// 检查是否需要重新计算缓存
|
||||
const shouldRecalculate =
|
||||
lastKfSelected !== state.kfSelected ||
|
||||
lastSearchKeyword !== state.searchKeyword;
|
||||
|
||||
if (shouldRecalculate) {
|
||||
if (state.searchKeyword.trim()) {
|
||||
const keyword = state.searchKeyword.toLowerCase();
|
||||
|
||||
// 从本地数据库查询联系人数据
|
||||
let allContacts: any[] = await contractService.findAll();
|
||||
|
||||
// 从本地数据库查询群组数据
|
||||
let allGroups: any[] = await weChatGroupService.findAll();
|
||||
|
||||
// 根据选中的客服筛选联系人
|
||||
if (state.kfSelected !== 0) {
|
||||
allContacts = allContacts.filter(
|
||||
item => item.wechatAccountId === state.kfSelected,
|
||||
);
|
||||
}
|
||||
|
||||
// 根据选中的客服筛选群组
|
||||
if (state.kfSelected !== 0) {
|
||||
allGroups = allGroups.filter(
|
||||
item => item.wechatAccountId === state.kfSelected,
|
||||
);
|
||||
}
|
||||
|
||||
// 搜索匹配的联系人
|
||||
const matchedContacts = allContacts.filter(item => {
|
||||
const nickname = (item.nickname || "").toLowerCase();
|
||||
const conRemark = (item.conRemark || "").toLowerCase();
|
||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
||||
});
|
||||
|
||||
// 搜索匹配的群组
|
||||
const matchedGroups = allGroups.filter(item => {
|
||||
const nickname = (item.nickname || "").toLowerCase();
|
||||
const conRemark = (item.conRemark || "").toLowerCase();
|
||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
||||
});
|
||||
|
||||
// 合并搜索结果
|
||||
cachedResult = [...matchedContacts, ...matchedGroups];
|
||||
} else {
|
||||
cachedResult = [];
|
||||
}
|
||||
|
||||
lastKfSelected = state.kfSelected;
|
||||
lastSearchKeyword = state.searchKeyword;
|
||||
}
|
||||
|
||||
return cachedResult;
|
||||
};
|
||||
})(),
|
||||
// 异步设置联系人分组列表
|
||||
asyncNewContractList: async (data: any[]) => {
|
||||
set({ newContractList: data });
|
||||
// 清除getNewContractList缓存
|
||||
const state = useCkChatStore.getState();
|
||||
if (
|
||||
state.getNewContractList &&
|
||||
typeof state.getNewContractList === "function"
|
||||
) {
|
||||
// 触发缓存重新计算
|
||||
await state.getNewContractList();
|
||||
}
|
||||
},
|
||||
// 异步设置会话列表
|
||||
asyncChatSessions: data => {
|
||||
set({ chatSessions: data });
|
||||
@@ -231,73 +68,6 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
state.getChatSessions();
|
||||
}
|
||||
},
|
||||
// 异步设置联系人列表
|
||||
asyncContractList: async (data: ContractData[]) => {
|
||||
set({ contractList: data });
|
||||
await contractService.createManyWithServerId(data);
|
||||
// 清除getContractList缓存
|
||||
const state = useCkChatStore.getState();
|
||||
if (
|
||||
state.getContractList &&
|
||||
typeof state.getContractList === "function"
|
||||
) {
|
||||
// 触发缓存重新计算
|
||||
state.getContractList();
|
||||
}
|
||||
},
|
||||
//获取特定联系人
|
||||
getSomeContractList: (kfSelected: number) => {
|
||||
const state = useCkChatStore.getState();
|
||||
return state.contractList.filter(
|
||||
item => item.wechatAccountId === kfSelected,
|
||||
);
|
||||
},
|
||||
// 获取联系人列表 - 使用缓存避免无限循环
|
||||
getContractList: (() => {
|
||||
let cachedResult: any = null;
|
||||
let lastKfSelected: number | null = null;
|
||||
let lastContractListLength: number = 0;
|
||||
let lastSearchKeyword: string = "";
|
||||
|
||||
return () => {
|
||||
const state = useCkChatStore.getState();
|
||||
|
||||
// 检查是否需要重新计算缓存
|
||||
const shouldRecalculate =
|
||||
cachedResult === null ||
|
||||
lastKfSelected !== state.kfSelected ||
|
||||
lastContractListLength !== state.contractList.length ||
|
||||
lastSearchKeyword !== state.searchKeyword;
|
||||
|
||||
if (shouldRecalculate) {
|
||||
let filteredContracts = state.contractList;
|
||||
|
||||
// 根据客服筛选
|
||||
if (state.kfSelected !== 0) {
|
||||
filteredContracts = filteredContracts.filter(
|
||||
item => item.wechatAccountId === state.kfSelected,
|
||||
);
|
||||
}
|
||||
|
||||
// 根据搜索关键词筛选
|
||||
if (state.searchKeyword.trim()) {
|
||||
const keyword = state.searchKeyword.toLowerCase();
|
||||
filteredContracts = filteredContracts.filter(item => {
|
||||
const nickname = (item.nickname || "").toLowerCase();
|
||||
const conRemark = (item.conRemark || "").toLowerCase();
|
||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
||||
});
|
||||
}
|
||||
|
||||
cachedResult = filteredContracts;
|
||||
lastKfSelected = state.kfSelected;
|
||||
lastContractListLength = state.contractList.length;
|
||||
lastSearchKeyword = state.searchKeyword;
|
||||
}
|
||||
|
||||
return cachedResult;
|
||||
};
|
||||
})(),
|
||||
//异步设置联系人分组
|
||||
asyncWeChatGroup: async (data: weChatGroup[]) => {
|
||||
await weChatGroupService.createManyWithServerId(data);
|
||||
@@ -341,7 +111,6 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
let cachedResult: any = null;
|
||||
let lastKfSelected: number | null = null;
|
||||
let lastChatSessionsLength: number = 0;
|
||||
let lastSearchKeyword: string = "";
|
||||
|
||||
return () => {
|
||||
const state = useCkChatStore.getState();
|
||||
@@ -350,8 +119,7 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
const shouldRecalculate =
|
||||
cachedResult === null ||
|
||||
lastKfSelected !== state.kfSelected ||
|
||||
lastChatSessionsLength !== state.chatSessions.length ||
|
||||
lastSearchKeyword !== state.searchKeyword;
|
||||
lastChatSessionsLength !== state.chatSessions.length;
|
||||
|
||||
if (shouldRecalculate) {
|
||||
let filteredSessions = state.chatSessions;
|
||||
@@ -363,20 +131,9 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
||||
);
|
||||
}
|
||||
|
||||
// 根据搜索关键词筛选
|
||||
if (state.searchKeyword.trim()) {
|
||||
const keyword = state.searchKeyword.toLowerCase();
|
||||
filteredSessions = filteredSessions.filter(item => {
|
||||
const nickname = (item.nickname || "").toLowerCase();
|
||||
const conRemark = (item.conRemark || "").toLowerCase();
|
||||
return nickname.includes(keyword) || conRemark.includes(keyword);
|
||||
});
|
||||
}
|
||||
|
||||
cachedResult = filteredSessions;
|
||||
lastKfSelected = state.kfSelected;
|
||||
lastChatSessionsLength = state.chatSessions.length;
|
||||
lastSearchKeyword = state.searchKeyword;
|
||||
}
|
||||
|
||||
return cachedResult;
|
||||
@@ -621,21 +378,9 @@ export const getKfSelectedUser = () =>
|
||||
useCkChatStore.getState().getKfSelectedUser();
|
||||
export const getKfUserInfo = (wechatAccountId: number) =>
|
||||
useCkChatStore.getState().getKfUserInfo(wechatAccountId);
|
||||
export const getContractList = () =>
|
||||
useCkChatStore.getState().getContractList();
|
||||
export const getNewContractList = () =>
|
||||
useCkChatStore.getState().getNewContractList();
|
||||
export const asyncCountLables = (data: ContactGroupByLabel[]) =>
|
||||
useCkChatStore.getState().asyncCountLables(data);
|
||||
export const asyncNewContractList = (data: any[]) =>
|
||||
useCkChatStore.getState().asyncNewContractList(data);
|
||||
export const getCountLables = () => useCkChatStore.getState().countLables;
|
||||
export const setSearchKeyword = (keyword: string) =>
|
||||
useCkChatStore.getState().setSearchKeyword(keyword);
|
||||
export const clearSearchKeyword = () =>
|
||||
useCkChatStore.getState().clearSearchKeyword();
|
||||
export const searchContactsAndGroups = () =>
|
||||
useCkChatStore.getState().searchContactsAndGroups();
|
||||
export const pinChatSessionToTop = (sessionId: number) =>
|
||||
useCkChatStore.getState().pinChatSessionToTop(sessionId);
|
||||
useCkChatStore.getState().getKfSelectedUser();
|
||||
|
||||
70
Touchkebao/src/store/module/weChat/contacts.data.ts
Normal file
70
Touchkebao/src/store/module/weChat/contacts.data.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
//联系人标签分组
|
||||
export interface ContactGroupByLabel {
|
||||
id: number;
|
||||
accountId?: number;
|
||||
groupName: string;
|
||||
tenantId?: number;
|
||||
count: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
//群聊数据接口
|
||||
export interface weChatGroup {
|
||||
id?: number;
|
||||
wechatAccountId: number;
|
||||
tenantId: number;
|
||||
accountId: number;
|
||||
chatroomId: string;
|
||||
chatroomOwner: string;
|
||||
conRemark: string;
|
||||
nickname: string;
|
||||
chatroomAvatar: string;
|
||||
groupId: number;
|
||||
config?: {
|
||||
top?: false;
|
||||
chat?: boolean;
|
||||
unreadCount?: number;
|
||||
};
|
||||
labels?: string[];
|
||||
notice: string;
|
||||
selfDisplyName: string;
|
||||
wechatChatroomId: number;
|
||||
serverId?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 联系人数据接口
|
||||
export interface ContractData {
|
||||
id?: number;
|
||||
serverId?: number;
|
||||
wechatAccountId: number;
|
||||
wechatId: string;
|
||||
alias: string;
|
||||
conRemark: string;
|
||||
nickname: string;
|
||||
quanPin: string;
|
||||
avatar?: string;
|
||||
gender: number;
|
||||
region: string;
|
||||
addFrom: number;
|
||||
phone: string;
|
||||
labels: string[];
|
||||
signature: string;
|
||||
accountId: number;
|
||||
extendFields: null;
|
||||
city?: string;
|
||||
lastUpdateTime: string;
|
||||
isPassed: boolean;
|
||||
tenantId: number;
|
||||
groupId: number;
|
||||
thirdParty: null;
|
||||
additionalPicture: string;
|
||||
desc: string;
|
||||
config?: {
|
||||
chat?: boolean;
|
||||
unreadCount: number;
|
||||
};
|
||||
lastMessageTime: number;
|
||||
|
||||
duplicate: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -0,0 +1,539 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { ContactGroupByLabel } from "@/pages/pc/ckbox/data";
|
||||
import { Contact } from "@/utils/db";
|
||||
import { ContactManager } from "@/utils/dbAction";
|
||||
|
||||
/**
|
||||
* 联系人状态管理接口
|
||||
*/
|
||||
export interface ContactState {
|
||||
// ==================== 基础状态 ====================
|
||||
/** 联系人列表 */
|
||||
contactList: Contact[];
|
||||
/** 联系人分组列表 */
|
||||
contactGroups: ContactGroupByLabel[];
|
||||
/** 当前选中的联系人 */
|
||||
currentContact: Contact | null;
|
||||
/** 搜索关键词 */
|
||||
searchKeyword: string;
|
||||
/** 加载状态 */
|
||||
loading: boolean;
|
||||
/** 刷新状态 */
|
||||
refreshing: boolean;
|
||||
|
||||
// ==================== 搜索和筛选 ====================
|
||||
/** 搜索结果 */
|
||||
searchResults: Contact[];
|
||||
/** 是否在搜索模式 */
|
||||
isSearchMode: boolean;
|
||||
|
||||
// ==================== 分页和性能 ====================
|
||||
/** 可见联系人(用于分页) */
|
||||
visibleContacts: { [key: string]: Contact[] };
|
||||
/** 加载状态(按分组) */
|
||||
loadingStates: { [key: string]: boolean };
|
||||
/** 是否有更多数据(按分组) */
|
||||
hasMore: { [key: string]: boolean };
|
||||
/** 当前页码(按分组) */
|
||||
currentPage: { [key: string]: number };
|
||||
|
||||
// ==================== 转发相关 ====================
|
||||
/** 选中的转发联系人 */
|
||||
selectedTransmitContacts: Contact[];
|
||||
/** 转发弹窗状态 */
|
||||
openTransmitModal: boolean;
|
||||
|
||||
// ==================== 基础操作方法 ====================
|
||||
/** 设置联系人列表 */
|
||||
setContactList: (contacts: Contact[]) => void;
|
||||
/** 设置联系人分组 */
|
||||
setContactGroups: (groups: ContactGroupByLabel[]) => void;
|
||||
/** 设置当前联系人 */
|
||||
setCurrentContact: (contact: Contact | null) => void;
|
||||
/** 清空当前联系人 */
|
||||
clearCurrentContact: () => void;
|
||||
/** 设置搜索关键词 */
|
||||
setSearchKeyword: (keyword: string) => void;
|
||||
/** 清空搜索关键词 */
|
||||
clearSearchKeyword: () => void;
|
||||
/** 设置加载状态 */
|
||||
setLoading: (loading: boolean) => void;
|
||||
/** 设置刷新状态 */
|
||||
setRefreshing: (refreshing: boolean) => void;
|
||||
|
||||
// ==================== 搜索和筛选方法 ====================
|
||||
/** 搜索联系人 */
|
||||
searchContacts: (keyword: string) => Promise<void>;
|
||||
/** 根据客服筛选联系人 */
|
||||
filterByCustomer: (customerId: number) => Promise<void>;
|
||||
/** 清空筛选 */
|
||||
clearFilter: () => void;
|
||||
|
||||
// ==================== 数据同步方法 ====================
|
||||
/** 从服务器同步联系人数据 */
|
||||
syncFromServer: (userId: number) => Promise<void>;
|
||||
/** 从本地数据库加载联系人数据 */
|
||||
loadFromLocal: (userId: number) => Promise<void>;
|
||||
/** 刷新联系人数据 */
|
||||
refreshContacts: (userId: number) => Promise<void>;
|
||||
|
||||
// ==================== 联系人操作方法 ====================
|
||||
/** 添加联系人 */
|
||||
addContact: (contact: Contact) => Promise<void>;
|
||||
/** 更新联系人 */
|
||||
updateContact: (contact: Contact) => Promise<void>;
|
||||
/** 删除联系人 */
|
||||
deleteContact: (contactId: number) => Promise<void>;
|
||||
/** 批量添加联系人 */
|
||||
addContacts: (contacts: Contact[]) => Promise<void>;
|
||||
|
||||
// ==================== 分组管理方法 ====================
|
||||
/** 获取联系人分组列表 */
|
||||
getContactGroups: (
|
||||
userId: number,
|
||||
customerId?: number,
|
||||
) => Promise<ContactGroupByLabel[]>;
|
||||
/** 创建联系人分组 */
|
||||
createContactGroup: (group: Omit<ContactGroupByLabel, "id">) => Promise<void>;
|
||||
/** 更新联系人分组 */
|
||||
updateContactGroup: (group: ContactGroupByLabel) => Promise<void>;
|
||||
/** 删除联系人分组 */
|
||||
deleteContactGroup: (groupId: number) => Promise<void>;
|
||||
|
||||
// ==================== 分页方法 ====================
|
||||
/** 加载更多联系人 */
|
||||
loadMoreContacts: (groupId: string) => Promise<void>;
|
||||
/** 重置分页状态 */
|
||||
resetPagination: () => void;
|
||||
|
||||
// ==================== 转发相关方法 ====================
|
||||
/** 设置选中的转发联系人 */
|
||||
setSelectedTransmitContacts: (contacts: Contact[]) => void;
|
||||
/** 添加转发联系人 */
|
||||
addTransmitContact: (contact: Contact) => void;
|
||||
/** 移除转发联系人 */
|
||||
removeTransmitContact: (contactId: number) => void;
|
||||
/** 清空转发联系人 */
|
||||
clearTransmitContacts: () => void;
|
||||
/** 设置转发弹窗状态 */
|
||||
setTransmitModal: (open: boolean) => void;
|
||||
|
||||
// ==================== 便捷获取方法 ====================
|
||||
/** 根据ID获取联系人 */
|
||||
getContactById: (id: number) => Contact | undefined;
|
||||
/** 根据客服ID获取联系人 */
|
||||
getContactsByCustomer: (customerId: number) => Contact[];
|
||||
/** 获取联系人总数 */
|
||||
getContactCount: () => number;
|
||||
/** 获取搜索结果总数 */
|
||||
getSearchResultCount: () => number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 联系人状态管理 Store
|
||||
*/
|
||||
export const useContactStore = create<ContactState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
// ==================== 初始状态 ====================
|
||||
contactList: [],
|
||||
contactGroups: [],
|
||||
currentContact: null,
|
||||
searchKeyword: "",
|
||||
loading: false,
|
||||
refreshing: false,
|
||||
searchResults: [],
|
||||
isSearchMode: false,
|
||||
visibleContacts: {},
|
||||
loadingStates: {},
|
||||
hasMore: {},
|
||||
currentPage: {},
|
||||
selectedTransmitContacts: [],
|
||||
openTransmitModal: false,
|
||||
|
||||
// ==================== 基础操作方法 ====================
|
||||
setContactList: (contacts: Contact[]) => {
|
||||
set({ contactList: contacts });
|
||||
},
|
||||
|
||||
setContactGroups: (groups: ContactGroupByLabel[]) => {
|
||||
set({ contactGroups: groups });
|
||||
},
|
||||
|
||||
setCurrentContact: (contact: Contact | null) => {
|
||||
set({ currentContact: contact });
|
||||
},
|
||||
|
||||
clearCurrentContact: () => {
|
||||
set({ currentContact: null });
|
||||
},
|
||||
|
||||
setSearchKeyword: (keyword: string) => {
|
||||
set({ searchKeyword: keyword });
|
||||
if (keyword.trim()) {
|
||||
get().searchContacts(keyword);
|
||||
} else {
|
||||
set({ isSearchMode: false, searchResults: [] });
|
||||
}
|
||||
},
|
||||
|
||||
clearSearchKeyword: () => {
|
||||
set({
|
||||
searchKeyword: "",
|
||||
isSearchMode: false,
|
||||
searchResults: [],
|
||||
});
|
||||
},
|
||||
|
||||
setLoading: (loading: boolean) => {
|
||||
set({ loading });
|
||||
},
|
||||
|
||||
setRefreshing: (refreshing: boolean) => {
|
||||
set({ refreshing });
|
||||
},
|
||||
|
||||
// ==================== 搜索和筛选方法 ====================
|
||||
searchContacts: async (keyword: string) => {
|
||||
if (!keyword.trim()) {
|
||||
set({ isSearchMode: false, searchResults: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
set({ loading: true, isSearchMode: true });
|
||||
|
||||
try {
|
||||
const results = await ContactManager.searchContacts(
|
||||
get().currentContact?.userId || 0,
|
||||
keyword,
|
||||
);
|
||||
set({ searchResults: results });
|
||||
} catch (error) {
|
||||
console.error("搜索联系人失败:", error);
|
||||
set({ searchResults: [] });
|
||||
} finally {
|
||||
set({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
filterByCustomer: async (customerId: number) => {
|
||||
set({ loading: true });
|
||||
|
||||
try {
|
||||
const contacts = await ContactManager.getContactsByCustomer(
|
||||
get().currentContact?.userId || 0,
|
||||
customerId,
|
||||
);
|
||||
set({ contactList: contacts });
|
||||
} catch (error) {
|
||||
console.error("按客服筛选联系人失败:", error);
|
||||
} finally {
|
||||
set({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
clearFilter: () => {
|
||||
set({
|
||||
contactList: [],
|
||||
searchResults: [],
|
||||
isSearchMode: false,
|
||||
searchKeyword: "",
|
||||
});
|
||||
},
|
||||
|
||||
// ==================== 数据同步方法 ====================
|
||||
syncFromServer: async (userId: number) => {
|
||||
set({ refreshing: true });
|
||||
|
||||
try {
|
||||
// 这里应该调用实际的API
|
||||
// const serverContacts = await getContactListFromServer(userId);
|
||||
// await ContactManager.syncContacts(userId, serverContacts);
|
||||
|
||||
// 临时使用本地数据
|
||||
const localContacts = await ContactManager.getUserContacts(userId);
|
||||
set({ contactList: localContacts });
|
||||
} catch (error) {
|
||||
console.error("从服务器同步联系人失败:", error);
|
||||
} finally {
|
||||
set({ refreshing: false });
|
||||
}
|
||||
},
|
||||
|
||||
loadFromLocal: async (userId: number) => {
|
||||
set({ loading: true });
|
||||
|
||||
try {
|
||||
const contacts = await ContactManager.getUserContacts(userId);
|
||||
set({ contactList: contacts });
|
||||
} catch (error) {
|
||||
console.error("从本地加载联系人失败:", error);
|
||||
} finally {
|
||||
set({ loading: false });
|
||||
}
|
||||
},
|
||||
|
||||
refreshContacts: async (userId: number) => {
|
||||
await get().loadFromLocal(userId);
|
||||
await get().syncFromServer(userId);
|
||||
},
|
||||
|
||||
// ==================== 联系人操作方法 ====================
|
||||
addContact: async (contact: Contact) => {
|
||||
try {
|
||||
await ContactManager.addContact(contact);
|
||||
set(state => ({
|
||||
contactList: [...state.contactList, contact],
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("添加联系人失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
updateContact: async (contact: Contact) => {
|
||||
try {
|
||||
await ContactManager.updateContact(contact);
|
||||
set(state => ({
|
||||
contactList: state.contactList.map(c =>
|
||||
c.id === contact.id ? contact : c,
|
||||
),
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("更新联系人失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
deleteContact: async (contactId: number) => {
|
||||
try {
|
||||
await ContactManager.deleteContact(contactId);
|
||||
set(state => ({
|
||||
contactList: state.contactList.filter(c => c.id !== contactId),
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("删除联系人失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
addContacts: async (contacts: Contact[]) => {
|
||||
try {
|
||||
await ContactManager.addContacts(contacts);
|
||||
set(state => ({
|
||||
contactList: [...state.contactList, ...contacts],
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("批量添加联系人失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 分组管理方法 ====================
|
||||
getContactGroups: async (userId: number, customerId?: number) => {
|
||||
try {
|
||||
const groups = await ContactManager.getContactGroups(
|
||||
userId,
|
||||
customerId,
|
||||
);
|
||||
set({ contactGroups: groups });
|
||||
return groups;
|
||||
} catch (error) {
|
||||
console.error("获取联系人分组失败:", error);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
createContactGroup: async (group: Omit<ContactGroupByLabel, "id">) => {
|
||||
try {
|
||||
await ContactManager.createContactGroup(group);
|
||||
// 重新获取分组列表
|
||||
await get().getContactGroups(get().currentContact?.userId || 0);
|
||||
} catch (error) {
|
||||
console.error("创建联系人分组失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
updateContactGroup: async (group: ContactGroupByLabel) => {
|
||||
try {
|
||||
await ContactManager.updateContactGroup(group);
|
||||
set(state => ({
|
||||
contactGroups: state.contactGroups.map(g =>
|
||||
g.id === group.id ? group : g,
|
||||
),
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("更新联系人分组失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
deleteContactGroup: async (groupId: number) => {
|
||||
try {
|
||||
await ContactManager.deleteContactGroup(groupId);
|
||||
set(state => ({
|
||||
contactGroups: state.contactGroups.filter(g => g.id !== groupId),
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("删除联系人分组失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 分页方法 ====================
|
||||
loadMoreContacts: async (groupId: string) => {
|
||||
const state = get();
|
||||
if (state.loadingStates[groupId] || !state.hasMore[groupId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(state => ({
|
||||
loadingStates: { ...state.loadingStates, [groupId]: true },
|
||||
}));
|
||||
|
||||
try {
|
||||
const currentPage = state.currentPage[groupId] || 1;
|
||||
const nextPage = currentPage + 1;
|
||||
const pageSize = 20;
|
||||
|
||||
// 这里应该调用实际的分页API
|
||||
// const newContacts = await getContactsByGroup(groupId, nextPage, pageSize);
|
||||
|
||||
// 临时使用本地数据模拟分页
|
||||
const allContacts = state.contactList;
|
||||
const startIndex = currentPage * pageSize;
|
||||
const endIndex = nextPage * pageSize;
|
||||
const newContacts = allContacts.slice(startIndex, endIndex);
|
||||
|
||||
set(state => ({
|
||||
visibleContacts: {
|
||||
...state.visibleContacts,
|
||||
[groupId]: [
|
||||
...(state.visibleContacts[groupId] || []),
|
||||
...newContacts,
|
||||
],
|
||||
},
|
||||
currentPage: { ...state.currentPage, [groupId]: nextPage },
|
||||
hasMore: {
|
||||
...state.hasMore,
|
||||
[groupId]: endIndex < allContacts.length,
|
||||
},
|
||||
loadingStates: { ...state.loadingStates, [groupId]: false },
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("加载更多联系人失败:", error);
|
||||
set(state => ({
|
||||
loadingStates: { ...state.loadingStates, [groupId]: false },
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
resetPagination: () => {
|
||||
set({
|
||||
visibleContacts: {},
|
||||
loadingStates: {},
|
||||
hasMore: {},
|
||||
currentPage: {},
|
||||
});
|
||||
},
|
||||
|
||||
// ==================== 转发相关方法 ====================
|
||||
setSelectedTransmitContacts: (contacts: Contact[]) => {
|
||||
set({ selectedTransmitContacts: contacts });
|
||||
},
|
||||
|
||||
addTransmitContact: (contact: Contact) => {
|
||||
set(state => {
|
||||
const exists = state.selectedTransmitContacts.some(
|
||||
c => c.id === contact.id,
|
||||
);
|
||||
if (exists) return state;
|
||||
return {
|
||||
selectedTransmitContacts: [
|
||||
...state.selectedTransmitContacts,
|
||||
contact,
|
||||
],
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
removeTransmitContact: (contactId: number) => {
|
||||
set(state => ({
|
||||
selectedTransmitContacts: state.selectedTransmitContacts.filter(
|
||||
c => c.id !== contactId,
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
clearTransmitContacts: () => {
|
||||
set({ selectedTransmitContacts: [] });
|
||||
},
|
||||
|
||||
setTransmitModal: (open: boolean) => {
|
||||
set({ openTransmitModal: open });
|
||||
},
|
||||
|
||||
// ==================== 便捷获取方法 ====================
|
||||
getContactById: (id: number) => {
|
||||
const state = get();
|
||||
return state.contactList.find(contact => contact.id === id);
|
||||
},
|
||||
|
||||
getContactsByCustomer: (customerId: number) => {
|
||||
const state = get();
|
||||
return state.contactList.filter(
|
||||
contact => contact.wechatAccountId === customerId,
|
||||
);
|
||||
},
|
||||
|
||||
getContactCount: () => {
|
||||
return get().contactList.length;
|
||||
},
|
||||
|
||||
getSearchResultCount: () => {
|
||||
return get().searchResults.length;
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "contacts-storage",
|
||||
partialize: () => ({
|
||||
// 只持久化必要的状态,不持久化临时状态
|
||||
contactList: [],
|
||||
contactGroups: [],
|
||||
currentContact: null,
|
||||
searchKeyword: "",
|
||||
selectedTransmitContacts: [],
|
||||
openTransmitModal: false,
|
||||
}),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// ==================== 便捷选择器导出 ====================
|
||||
/** 获取联系人列表的 Hook */
|
||||
export const useContactList = () => useContactStore(state => state.contactList);
|
||||
/** 获取当前联系人的 Hook */
|
||||
export const useCurrentContact = () =>
|
||||
useContactStore(state => state.currentContact);
|
||||
/** 获取搜索关键词的 Hook */
|
||||
export const useSearchKeyword = () =>
|
||||
useContactStore(state => state.searchKeyword);
|
||||
/** 获取加载状态的 Hook */
|
||||
export const useContactLoading = () => useContactStore(state => state.loading);
|
||||
/** 获取刷新状态的 Hook */
|
||||
export const useContactRefreshing = () =>
|
||||
useContactStore(state => state.refreshing);
|
||||
/** 获取搜索结果的 Hook */
|
||||
export const useSearchResults = () =>
|
||||
useContactStore(state => state.searchResults);
|
||||
/** 获取是否在搜索模式的 Hook */
|
||||
export const useIsSearchMode = () =>
|
||||
useContactStore(state => state.isSearchMode);
|
||||
/** 获取转发联系人的 Hook */
|
||||
export const useSelectedTransmitContacts = () =>
|
||||
useContactStore(state => state.selectedTransmitContacts);
|
||||
/** 获取转发弹窗状态的 Hook */
|
||||
export const useTransmitModal = () =>
|
||||
useContactStore(state => state.openTransmitModal);
|
||||
|
||||
@@ -24,17 +24,6 @@ export interface WeChatState {
|
||||
/** 更新选中的聊天记录 */
|
||||
updateSelectedChatRecords: (message: ChatRecord[]) => void;
|
||||
|
||||
/** 选中的联系人或群组列表 */
|
||||
selectedTransmitContact: ContractData[] | weChatGroup[];
|
||||
/** 更新选中的联系人或群组 */
|
||||
updateSelectedTransmitContact: (
|
||||
contact: ContractData[] | weChatGroup[],
|
||||
) => void;
|
||||
|
||||
/** 转发弹窗开启状态 */
|
||||
openTransmitModal: boolean;
|
||||
/** 更新转发弹窗状态 */
|
||||
updateTransmitModal: (open: boolean) => void;
|
||||
// ==================== Transmit Module =========END===========
|
||||
|
||||
// ==================== 当前联系人管理 ====================
|
||||
|
||||
@@ -59,21 +59,6 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
set({ selectedChatRecords: message });
|
||||
},
|
||||
|
||||
/** 选中的联系人或群组列表 */
|
||||
selectedTransmitContact: [],
|
||||
/** 更新选中的联系人或群组 */
|
||||
updateSelectedTransmitContact: (
|
||||
contact: ContractData[] | weChatGroup[],
|
||||
) => {
|
||||
set({ selectedTransmitContact: contact });
|
||||
},
|
||||
|
||||
/** 转发弹窗开启状态 */
|
||||
openTransmitModal: false,
|
||||
/** 更新转发弹窗状态 */
|
||||
updateTransmitModal: (open: boolean) => {
|
||||
set({ openTransmitModal: open });
|
||||
},
|
||||
// ==================== Transmit Module =========END===========
|
||||
|
||||
// ==================== 当前联系人管理状态 ====================
|
||||
@@ -159,7 +144,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
) => {
|
||||
const state = useWeChatStore.getState();
|
||||
// 切换联系人时清空当前消息,等待重新加载
|
||||
set({ currentMessages: [], openTransmitModal: false });
|
||||
set({ currentMessages: [] });
|
||||
|
||||
const params: any = {};
|
||||
|
||||
@@ -495,7 +480,7 @@ export const useWeChatStore = create<WeChatState>()(
|
||||
}),
|
||||
{
|
||||
name: "wechat-storage",
|
||||
partialize: state => ({
|
||||
partialize: () => ({
|
||||
// currentContract 不做持久化,登录和页面刷新时直接清空
|
||||
}),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user