diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/Person.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/Person.module.scss index 66cf4b59..add242f8 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/Person.module.scss +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/Person.module.scss @@ -4,3 +4,51 @@ height: 100%; overflow-y: auto; } + +.tabHeader { + display: flex; + align-items: center; + padding: 0 30px; + border-bottom: 1px solid #f0f0f0; + min-height: 48px; +} + +.tabItem { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + margin-right: 24px; + padding: 12px 0; + font-size: 14px; + color: #333; + cursor: pointer; + transition: color 0.2s ease; +} + +.tabItem:last-child { + margin-right: 0; +} + +.tabItem:hover { + color: #1677ff; +} + +.tabItemActive { + color: #1677ff; + font-weight: 500; +} + +.tabUnderline { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 2px; + background: transparent; + transition: background 0.2s ease; +} + +.tabItemActive .tabUnderline { + background: #1677ff; +} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx index 9e06f593..847b3b41 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import { Input, Button, @@ -22,7 +22,7 @@ import { SwapOutlined, } from "@ant-design/icons"; import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; -import { useCkChatStore } from "@/store/module/ckchat/ckchat"; +import { useCustomerStore } from "@/store/module/weChat/customer"; import { useWebSocketStore } from "@/store/module/websocket/websocket"; import { useWeChatStore } from "@/store/module/weChat/weChat"; import { useContactStore } from "@/store/module/weChat/contacts"; @@ -209,9 +209,14 @@ const Person: React.FC = ({ contract }) => { // 构建联系人或群聊详细信息 - const kfSelectedUser = useCkChatStore(state => - state.getKfUserInfo(contract.wechatAccountId || 0), - ); + const customerList = useCustomerStore(state => state.customerList); + const kfSelectedUser = useMemo(() => { + if (!contract.wechatAccountId) return null; + const matchedCustomer = customerList.find( + customer => customer.id === contract.wechatAccountId, + ); + return matchedCustomer || null; + }, [customerList, contract.wechatAccountId]); const { getContactsByCustomer } = useContactStore(); @@ -221,16 +226,12 @@ const Person: React.FC = ({ contract }) => { const hasGroupManagePermission = () => { // 暂时给所有用户完整的群管理权限 return true; - // if (!kfSelectedUser || !contract) return false; - // // 当客服的wechatId与contract的chatroomOwner相同时,才有完整的群管理权限 - // return kfSelectedUser.nickname === (contract as any).chatroomOwnerNickname; }; // 获取所有可用标签 useEffect(() => { const fetchAvailableTags = async () => { try { - // 从kfSelectedUser.labels和contract.labels合并获取所有标签 const kfTags = kfSelectedUser?.labels || []; const contractTags = contract.labels || []; const allTags = [...new Set([...kfTags, ...contractTags])]; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx index 420d942b..0754db88 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/components/GroupModal.tsx @@ -28,7 +28,7 @@ const GroupModal: React.FC = ({ form.resetFields(); }} footer={null} - destroyOnClose + destroyOnHidden >
= ({ form.resetFields(); }} footer={null} - destroyOnClose + destroyOnHidden > = ({ contract }) => { - const [activeKey, setActiveKey] = useState("profile"); + const [activeKey, setActiveKey] = useState("quickwords"); const isGroup = "chatroomId" in contract; const tabItems = useMemo(() => { const baseItems = [ { key: "quickwords", label: "快捷语录", + children: , }, { key: "profile", label: isGroup ? "群资料" : "个人资料", + children: , }, ]; if (!isGroup) { baseItems.push({ key: "moments", label: "朋友圈", + children: , }); } return baseItems; - }, [isGroup]); + }, [contract, isGroup]); - const handleTabChange = useCallback((key: string) => { - setActiveKey(key); - }, []); + useEffect(() => { + setActiveKey("quickwords"); + setRenderedKeys(["quickwords"]); + }, [contract]); - const tabBarStyle = useMemo(() => ({ padding: "0 30px" }), []); + const tabHeaderItems = useMemo( + () => tabItems.map(({ key, label }) => ({ key, label })), + [tabItems], + ); + + const availableKeys = useMemo( + () => tabItems.map(item => item.key), + [tabItems], + ); + + const [renderedKeys, setRenderedKeys] = useState(() => [ + "quickwords", + ]); + + useEffect(() => { + if (!availableKeys.includes(activeKey) && availableKeys.length > 0) { + setActiveKey(availableKeys[0]); + } + }, [activeKey, availableKeys]); + + useEffect(() => { + setRenderedKeys(keys => { + const filtered = keys.filter(key => availableKeys.includes(key)); + if (!filtered.includes(activeKey)) { + filtered.push(activeKey); + } + const isSameLength = filtered.length === keys.length; + const isSameOrder = + isSameLength && filtered.every((key, index) => key === keys[index]); + return isSameOrder ? keys : filtered; + }); + }, [activeKey, availableKeys]); return ( +
+ {tabHeaderItems.map(({ key, label }) => { + const isActive = key === activeKey; + return ( +
{ + setActiveKey(key); + }} + > + {label} +
+
+ ); + })} +
} > - {activeKey === "profile" && } - {activeKey === "quickwords" && } - {activeKey === "moments" && !isGroup && ( - - )} + {renderedKeys.map(key => { + const item = tabItems.find(tab => tab.key === key); + if (!item) return null; + const isActive = key === activeKey; + return ( +
+ {item.children} +
+ ); + })} ); 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 b24c8343..21712a08 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 @@ -343,9 +343,6 @@ const MessageList: React.FC = () => { }; }); - console.log("群聊数据示例:", groups[0]); // 调试:查看第一个群聊数据 - console.log("好友数据示例:", friends[0]); // 调试:查看第一个好友数据 - // 执行增量同步 const syncResult = await MessageManager.syncSessions(currentUserId, { friends, @@ -655,6 +652,8 @@ const MessageList: React.FC = () => { top: 0, }, sortKey: "", + phone: msgData.phone || "", + region: msgData.region || "", }; await MessageManager.addSession(newSession); @@ -681,6 +680,8 @@ const MessageList: React.FC = () => { top: 0, }, sortKey: "", + phone: msgData.phone || "", + region: msgData.region || "", }; await MessageManager.addSession(newSession); @@ -710,7 +711,6 @@ const MessageList: React.FC = () => { // 点击会话 const onContactClick = async (session: ChatSession) => { console.log("onContactClick", session); - console.log("session.aiType:", session.aiType); // 调试:查看 aiType 字段 // 设置当前会话 setCurrentContact(session as any); diff --git a/Touchkebao/src/store/module/websocket/websocket.ts b/Touchkebao/src/store/module/websocket/websocket.ts index 359fcf4a..51134cc2 100644 --- a/Touchkebao/src/store/module/websocket/websocket.ts +++ b/Touchkebao/src/store/module/websocket/websocket.ts @@ -394,7 +394,7 @@ export const useWebSocketStore = createPersistStore( set({ messages: [...currentState.messages, newMessage], - unreadCount: currentState.config.unreadCount + 1, + unreadCount: (currentState.unreadCount ?? 0) + 1, }); //消息处理器 msgManageCore(data); diff --git a/Touchkebao/src/utils/db.ts b/Touchkebao/src/utils/db.ts index 61bffeb8..836d9b46 100644 --- a/Touchkebao/src/utils/db.ts +++ b/Touchkebao/src/utils/db.ts @@ -60,6 +60,8 @@ export interface ChatSession { chatroomOwner?: string; // 群主 selfDisplayName?: string; // 群内昵称 notice?: string; // 群公告 + phone?: string; // 联系人电话 + region?: string; // 联系人地区 } // ==================== 统一联系人表(兼容好友和群聊) ==================== @@ -128,15 +130,14 @@ class CunkebaoDatabase extends Dexie { constructor(dbName: string) { super(dbName); - // 版本1:统一表结构 this.version(1).stores({ // 会话表索引:支持按用户、类型、时间、置顶等查询 chatSessions: - "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+lastUpdateTime], sortKey, nickname, conRemark, avatar, content, lastUpdateTime", + "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+lastUpdateTime], [userId+aiType], sortKey, nickname, conRemark, avatar, content, lastUpdateTime, aiType, phone, region", // 联系人表索引:支持按用户、类型、标签、搜索等查询 contactsUnified: - "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], sortKey, searchKey, nickname, conRemark, avatar, lastUpdateTime, groupId", + "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+aiType], sortKey, searchKey, nickname, conRemark, avatar, lastUpdateTime, groupId, aiType, phone, region", // 联系人标签映射表索引:支持按用户、标签、联系人、类型查询 contactLabelMap: @@ -146,47 +147,6 @@ class CunkebaoDatabase extends Dexie { userLoginRecords: "serverId, userId, lastLoginTime, loginCount, createTime, lastActiveTime", }); - - // 版本2:添加 aiType 字段 - this.version(2) - .stores({ - // 会话表索引:添加 aiType 索引 - chatSessions: - "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+lastUpdateTime], [userId+aiType], sortKey, nickname, conRemark, avatar, content, lastUpdateTime, aiType", - - // 联系人表索引:添加 aiType 索引 - contactsUnified: - "serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+aiType], sortKey, searchKey, nickname, conRemark, avatar, lastUpdateTime, groupId, aiType", - - // 联系人标签映射表索引:保持不变 - contactLabelMap: - "serverId, userId, labelId, contactId, contactType, [userId+labelId], [userId+contactId], [userId+labelId+sortKey], sortKey, searchKey, avatar, nickname, conRemark, unreadCount, lastUpdateTime", - - // 用户登录记录表索引:保持不变 - userLoginRecords: - "serverId, userId, lastLoginTime, loginCount, createTime, lastActiveTime", - }) - .upgrade(tx => { - // 数据迁移:为现有数据添加 aiType 默认值 - return tx - .table("chatSessions") - .toCollection() - .modify(session => { - if (session.aiType === undefined) { - session.aiType = 0; // 默认为普通类型 - } - }) - .then(() => { - return tx - .table("contactsUnified") - .toCollection() - .modify(contact => { - if (contact.aiType === undefined) { - contact.aiType = 0; // 默认为普通类型 - } - }); - }); - }); } } @@ -344,6 +304,8 @@ export class DatabaseService { const dataToInsert = { ...data, serverId: data.id, // 使用接口的id作为serverId主键 + phone: data.phone ?? "", + region: data.region ?? "", }; return await this.table.add(dataToInsert as T); } @@ -407,6 +369,8 @@ export class DatabaseService { const processedData = newData.map(item => ({ ...item, serverId: item.id, // 使用接口的id作为serverId主键 + phone: item.phone ?? "", + region: item.region ?? "", })); return await this.table.bulkAdd(processedData as T[], { allKeys: true }); diff --git a/Touchkebao/src/utils/dbAction/contact.ts b/Touchkebao/src/utils/dbAction/contact.ts index 254f2ec8..5630d4d4 100644 --- a/Touchkebao/src/utils/dbAction/contact.ts +++ b/Touchkebao/src/utils/dbAction/contact.ts @@ -184,7 +184,9 @@ export class ContactManager { local.conRemark !== server.conRemark || local.avatar !== server.avatar || local.wechatAccountId !== server.wechatAccountId || - (local.aiType ?? 0) !== (server.aiType ?? 0) // 添加 aiType 比较 + (local.aiType ?? 0) !== (server.aiType ?? 0) || // 添加 aiType 比较 + (local.phone ?? "") !== (server.phone ?? "") || + (local.region ?? "") !== (server.region ?? "") ); } diff --git a/Touchkebao/src/utils/dbAction/message.ts b/Touchkebao/src/utils/dbAction/message.ts index f6deae3d..9df03116 100644 --- a/Touchkebao/src/utils/dbAction/message.ts +++ b/Touchkebao/src/utils/dbAction/message.ts @@ -93,6 +93,8 @@ export class MessageManager { content: (friend as any).content || "", lastUpdateTime: friend.lastUpdateTime || new Date().toISOString(), aiType: (friend as any).aiType ?? 0, // AI类型,默认为0(普通) + phone: (friend as any).phone ?? "", + region: (friend as any).region ?? "", config: { unreadCount: friend.config?.unreadCount || 0, top: (friend.config as any)?.top || false, @@ -126,6 +128,8 @@ export class MessageManager { content: (group as any).content || "", lastUpdateTime: (group as any).lastUpdateTime || new Date().toISOString(), aiType: (group as any).aiType ?? 0, // AI类型,默认为0(普通) + phone: (group as any).phone ?? "", + region: (group as any).region ?? "", config: { unreadCount: (group.config as any)?.unreadCount || 0, top: (group.config as any)?.top || false, @@ -199,6 +203,8 @@ export class MessageManager { "avatar", "wechatAccountId", // 添加wechatAccountId比较 "aiType", // 添加aiType比较 + "phone", + "region", ]; for (const field of fieldsToCompare) {