diff --git a/Cunkebao/src/pages/pc/ckbox/index.tsx b/Cunkebao/src/pages/pc/ckbox/index.tsx index 2129769c..5eebff47 100644 --- a/Cunkebao/src/pages/pc/ckbox/index.tsx +++ b/Cunkebao/src/pages/pc/ckbox/index.tsx @@ -1,105 +1,14 @@ -import React, { useState, useEffect } from "react"; -import { Layout, Button, Space, message, Tooltip } from "antd"; -import { InfoCircleOutlined, MessageOutlined } from "@ant-design/icons"; -import dayjs from "dayjs"; -import ChatWindow from "./components/ChatWindow/index"; -import SidebarMenu from "./components/SidebarMenu/index"; -import VerticalUserList from "./components/VerticalUserList"; -import PageSkeleton from "./components/Skeleton"; +import React from "react"; +import { Layout } from "antd"; +import { Outlet } from "react-router-dom"; import NavCommon from "./components/NavCommon"; import styles from "./index.module.scss"; -import { addChatSession } from "@/store/module/ckchat/ckchat"; -const { Content, Sider } = Layout; -import { chatInitAPIdata, initSocket } from "./main"; -import { useWeChatStore } from "@/store/module/weChat/weChat"; - -import { KfUserListData } from "@/pages/pc/ckbox/data"; - const CkboxPage: React.FC = () => { - // 不要在组件初始化时获取sendCommand,而是在需要时动态获取 - const [loading, setLoading] = useState(false); - const currentContract = useWeChatStore(state => state.currentContract); - useEffect(() => { - // 方法一:使用 Promise 链式调用处理异步函数 - setLoading(true); - chatInitAPIdata() - .then(response => { - const data = response as { - contractList: any[]; - groupList: any[]; - kfUserList: KfUserListData[]; - newContractList: { groupName: string; contacts: any[] }[]; - }; - const { contractList } = data; - - //找出已经在聊天的 - const isChatList = contractList.filter( - v => (v?.config && v.config?.chat) || false, - ); - isChatList.forEach(v => { - addChatSession(v); - }); - - // 数据加载完成后初始化WebSocket连接 - initSocket(); - }) - .catch(error => { - console.error("获取联系人列表失败:", error); - }) - .finally(() => { - setLoading(false); - }); - }, []); - return ( - - - - - {/* 垂直侧边栏 */} - - - - - - {/* 左侧联系人边栏 */} - - - - - {/* 主内容区 */} - - {currentContract ? ( - - {/* - - - } - onClick={() => setShowProfile(!showProfile)} - size="small" - > - {showProfile ? "隐藏资料" : "显示资料"} - - - - */} - - - ) : ( - - - - 欢迎使用触客宝 - 选择一个联系人开始聊天 - - - )} - - - - + + + + ); }; diff --git a/Cunkebao/src/pages/pc/ckbox/monitoring/index.module.scss b/Cunkebao/src/pages/pc/ckbox/monitoring/index.module.scss new file mode 100644 index 00000000..07a906b5 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/monitoring/index.module.scss @@ -0,0 +1,153 @@ +.monitoring { + padding: 24px; + background-color: #f5f5f5; + min-height: 100vh; + + .header { + margin-bottom: 24px; + + h2 { + margin: 0 0 8px 0; + color: #262626; + font-size: 24px; + font-weight: 600; + } + + p { + margin: 0; + color: #8c8c8c; + font-size: 14px; + } + } + + .statsRow { + margin-bottom: 24px; + + .statCard { + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: all 0.3s ease; + + &:hover { + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); + transform: translateY(-2px); + } + } + } + + .progressRow { + margin-bottom: 24px; + + .progressCard, + .metricsCard { + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + height: 280px; + + .ant-card-body { + height: calc(100% - 57px); + display: flex; + flex-direction: column; + justify-content: space-between; + } + } + + .progressItem { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + + span { + display: block; + margin-bottom: 8px; + color: #595959; + font-size: 14px; + } + } + + .metricItem { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 0; + border-bottom: 1px solid #f0f0f0; + + &:last-child { + border-bottom: none; + } + + span { + color: #595959; + font-size: 14px; + } + + .metricValue { + display: flex; + align-items: center; + gap: 8px; + + span { + font-weight: 600; + font-size: 16px; + color: #262626; + } + } + } + } + + .tableRow { + .tableCard { + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + + .ant-table { + .ant-table-thead > tr > th { + background-color: #fafafa; + border-bottom: 1px solid #f0f0f0; + font-weight: 600; + } + + .ant-table-tbody > tr { + &:hover { + background-color: #f5f5f5; + } + } + } + } + } +} + +// 响应式设计 +@media (max-width: 768px) { + .monitoring { + padding: 16px; + + .header { + h2 { + font-size: 20px; + } + } + + .progressRow { + .progressCard, + .metricsCard { + height: auto; + margin-bottom: 16px; + } + } + } +} + +@media (max-width: 576px) { + .monitoring { + padding: 12px; + + .statsRow { + .statCard { + margin-bottom: 12px; + } + } + } +} \ No newline at end of file diff --git a/Cunkebao/src/pages/pc/ckbox/monitoring/index.tsx b/Cunkebao/src/pages/pc/ckbox/monitoring/index.tsx new file mode 100644 index 00000000..21efed53 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/monitoring/index.tsx @@ -0,0 +1,186 @@ +import React from 'react'; +import { Card, Row, Col, Statistic, Progress, Table, Tag } from 'antd'; +import { + UserOutlined, + MessageOutlined, + TeamOutlined, + TrophyOutlined, + ArrowUpOutlined, + ArrowDownOutlined +} from '@ant-design/icons'; +import styles from './index.module.scss'; + +interface MonitoringProps { + // 预留接口属性 +} + +const Monitoring: React.FC = () => { + // 模拟数据 + const statsData = [ + { + title: '在线设备数', + value: 128, + prefix: , + suffix: '台', + valueStyle: { color: '#3f8600' }, + }, + { + title: '今日消息量', + value: 2456, + prefix: , + suffix: '条', + valueStyle: { color: '#1890ff' }, + }, + { + title: '活跃群组', + value: 89, + prefix: , + suffix: '个', + valueStyle: { color: '#722ed1' }, + }, + { + title: '成功率', + value: 98.5, + prefix: , + suffix: '%', + valueStyle: { color: '#f5222d' }, + }, + ]; + + const tableColumns = [ + { + title: '设备名称', + dataIndex: 'deviceName', + key: 'deviceName', + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + render: (status: string) => ( + {status} + ), + }, + { + title: '消息数', + dataIndex: 'messageCount', + key: 'messageCount', + }, + { + title: '最后活跃时间', + dataIndex: 'lastActive', + key: 'lastActive', + }, + ]; + + const tableData = [ + { + key: '1', + deviceName: '设备001', + status: '在线', + messageCount: 245, + lastActive: '2024-01-15 14:30:25', + }, + { + key: '2', + deviceName: '设备002', + status: '离线', + messageCount: 156, + lastActive: '2024-01-15 12:15:10', + }, + { + key: '3', + deviceName: '设备003', + status: '在线', + messageCount: 389, + lastActive: '2024-01-15 14:28:45', + }, + ]; + + return ( + + + 数据监控看板 + 实时监控系统运行状态和数据指标 + + + {/* 统计卡片 */} + + {statsData.map((stat, index) => ( + + + + + + ))} + + + {/* 进度指标 */} + + + + + CPU使用率 + + + + 内存使用率 + + + + 磁盘使用率 + + + + + + + + 消息处理速度 + + 1,245 + + + + + 错误率 + + 0.2% + + + + + 响应时间 + + 125ms + + + + + + + + {/* 设备状态表格 */} + + + + + + + + + ); +}; + +export default Monitoring; \ No newline at end of file diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/api.ts b/Cunkebao/src/pages/pc/ckbox/weChat/api.ts new file mode 100644 index 00000000..98bc336a --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/weChat/api.ts @@ -0,0 +1,246 @@ +import request from "@/api/request2"; +import { + MessageData, + ChatHistoryResponse, + MessageType, + OnlineStatus, + MessageStatus, + FileUploadResponse, + EmojiData, + QuickReply, + ChatSettings, +} from "./data"; + +//读取聊天信息 +//kf.quwanzhi.com:9991/api/WechatFriend/clearUnreadCount + +export function WechatGroup(params) { + return request("/api/WechatGroup/list", params, "GET"); +} + +//获取聊天记录-1 清除未读 +export function clearUnreadCount(params) { + return request("/api/WechatFriend/clearUnreadCount", params, "PUT"); +} + +//更新配置 +export function updateConfig(params) { + return request("/api/WechatFriend/updateConfig", params, "PUT"); +} +//获取聊天记录-2 获取列表 +export function getChatMessages(params: { + wechatAccountId: number; + wechatFriendId?: number; + wechatChatroomId?: number; + From: number; + To: number; + Count: number; + olderData: boolean; +}) { + return request("/api/FriendMessage/SearchMessage", params, "GET"); +} +export function getChatroomMessages(params: { + wechatAccountId: number; + wechatFriendId?: number; + wechatChatroomId?: number; + From: number; + To: number; + Count: number; + olderData: boolean; +}) { + return request("/api/ChatroomMessage/SearchMessage", params, "GET"); +} + +//获取群列表 +export function getGroupList(params: { prevId: number; count: number }) { + return request( + "/api/wechatChatroom/listExcludeMembersByPage?", + params, + "GET", + ); +} + +//获取群成员 +export function getGroupMembers(params: { id: number }) { + return request( + "/api/WechatChatroom/listMembersByWechatChatroomId", + params, + "GET", + ); +} + +//触客宝登陆 +export function loginWithToken(params: any) { + return request( + "/token", + params, + "POST", + { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }, + 1000, + ); +} + +// 获取触客宝用户信息 +export function getChuKeBaoUserInfo() { + return request("/api/account/self", {}, "GET"); +} + +// 获取联系人列表 +export const getContactList = (params: { prevId: number; count: number }) => { + return request("/api/wechatFriend/list", params, "GET"); +}; + +//获取控制终端列表 +export const getControlTerminalList = params => { + return request("/api/wechataccount", params, "GET"); +}; + +// 获取聊天历史 +export const getChatHistory = ( + chatId: string, + page: number = 1, + pageSize: number = 50, +): Promise => { + return request(`/v1/chats/${chatId}/messages`, { page, pageSize }, "GET"); +}; + +// 发送消息 +export const sendMessage = ( + chatId: string, + content: string, + type: MessageType = MessageType.TEXT, +): Promise => { + return request(`/v1/chats/${chatId}/messages`, { content, type }, "POST"); +}; + +// 发送文件消息 +export const sendFileMessage = ( + chatId: string, + file: File, + type: MessageType, +): Promise => { + const formData = new FormData(); + formData.append("file", file); + formData.append("type", type); + return request(`/v1/chats/${chatId}/messages/file`, formData, "POST"); +}; + +// 标记消息为已读 +export const markMessageAsRead = (messageId: string): Promise => { + return request(`/v1/messages/${messageId}/read`, {}, "PUT"); +}; + +// 标记聊天为已读 +export const markChatAsRead = (chatId: string): Promise => { + return request(`/v1/chats/${chatId}/read`, {}, "PUT"); +}; + +// 添加群组成员 +export const addGroupMembers = ( + groupId: string, + memberIds: string[], +): Promise => { + return request(`/v1/groups/${groupId}/members`, { memberIds }, "POST"); +}; + +// 移除群组成员 +export const removeGroupMembers = ( + groupId: string, + memberIds: string[], +): Promise => { + return request(`/v1/groups/${groupId}/members`, { memberIds }, "DELETE"); +}; + +// 获取在线状态 +export const getOnlineStatus = (userId: string): Promise => { + return request(`/v1/users/${userId}/status`, {}, "GET"); +}; + +// 获取消息状态 +export const getMessageStatus = (messageId: string): Promise => { + return request(`/v1/messages/${messageId}/status`, {}, "GET"); +}; + +// 上传文件 +export const uploadFile = (file: File): Promise => { + const formData = new FormData(); + formData.append("file", file); + return request("/v1/upload", formData, "POST"); +}; + +// 获取表情包列表 +export const getEmojiList = (): Promise => { + return request("/v1/emojis", {}, "GET"); +}; + +// 获取快捷回复列表 +export const getQuickReplies = (): Promise => { + return request("/v1/quick-replies", {}, "GET"); +}; + +// 添加快捷回复 +export const addQuickReply = (data: { + content: string; + category: string; +}): Promise => { + return request("/v1/quick-replies", data, "POST"); +}; + +// 删除快捷回复 +export const deleteQuickReply = (id: string): Promise => { + return request(`/v1/quick-replies/${id}`, {}, "DELETE"); +}; + +// 获取聊天设置 +export const getChatSettings = (): Promise => { + return request("/v1/chat/settings", {}, "GET"); +}; + +// 更新聊天设置 +export const updateChatSettings = ( + settings: Partial, +): Promise => { + return request("/v1/chat/settings", settings, "PUT"); +}; + +// 删除聊天会话 +export const deleteChatSession = (chatId: string): Promise => { + return request(`/v1/chats/${chatId}`, {}, "DELETE"); +}; + +// 置顶聊天会话 +export const pinChatSession = (chatId: string): Promise => { + return request(`/v1/chats/${chatId}/pin`, {}, "PUT"); +}; + +// 取消置顶聊天会话 +export const unpinChatSession = (chatId: string): Promise => { + return request(`/v1/chats/${chatId}/unpin`, {}, "PUT"); +}; + +// 静音聊天会话 +export const muteChatSession = (chatId: string): Promise => { + return request(`/v1/chats/${chatId}/mute`, {}, "PUT"); +}; + +// 取消静音聊天会话 +export const unmuteChatSession = (chatId: string): Promise => { + return request(`/v1/chats/${chatId}/unmute`, {}, "PUT"); +}; + +// 转发消息 +export const forwardMessage = ( + messageId: string, + targetChatIds: string[], +): Promise => { + return request("/v1/messages/forward", { messageId, targetChatIds }, "POST"); +}; + +// 撤回消息 +export const recallMessage = (messageId: string): Promise => { + return request(`/v1/messages/${messageId}/recall`, {}, "PUT"); +}; diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/ChatWindow.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/ChatWindow.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/ChatWindow.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/ChatWindow.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/MessageEnter.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageEnter/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageEnter/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageEnter/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageRecord/MessageRecord.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/MessageRecord.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageRecord/MessageRecord.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/MessageRecord.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageRecord/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/MessageRecord/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/MessageRecord/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/ProfileCard/Person.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/Person.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/ProfileCard/Person.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/Person.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/ProfileCard/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/components/ProfileCard/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/demo.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/demo.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/demo.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/demo.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/ChatWindow/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.module.scss new file mode 100644 index 00000000..75333cfb --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.module.scss @@ -0,0 +1,258 @@ +.header { + background: #fff; + padding: 0 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + justify-content: space-between; + height: 64px; + border-bottom: 1px solid #f0f0f0; +} + +.headerLeft { + display: flex; + align-items: center; + gap: 12px; +} + +.menuButton { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 6px; + transition: all 0.3s; + + &:hover { + background-color: #f5f5f5; + } + + .anticon { + font-size: 18px; + color: #666; + } +} + +.title { + font-size: 18px; + color: #333; + margin: 0; +} + +.headerRight { + display: flex; + align-items: center; +} + +.userInfo { + cursor: pointer; + padding: 8px 12px; + border-radius: 6px; + transition: all 0.3s; + + &:hover { + background-color: #f5f5f5; + } + .suanli { + font-size: 16px; + color: #666; + .suanliIcon { + font-size: 24px; + } + } +} + +.avatar { + border: 2px solid #f0f0f0; + transition: all 0.3s; + + &:hover { + border-color: #1890ff; + } +} + +.username { + font-size: 14px; + color: #333; + font-weight: 500; + margin-left: 8px; +} + +// 抽屉样式 +.drawer { + :global(.ant-drawer-header) { + background: #fafafa; + border-bottom: 1px solid #f0f0f0; + } + + :global(.ant-drawer-body) { + padding: 0; + } +} + +.drawerContent { + height: 100%; + display: flex; + flex-direction: column; + background: #f8f9fa; +} + +.drawerHeader { + padding: 20px; + background: #fff; + border-bottom: none; +} + +.logoSection { + display: flex; + align-items: center; + gap: 12px; +} + +.logoIcon { + width: 48px; + height: 48px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + color: white; +} + +.logoText { + display: flex; + flex-direction: column; +} + +.appName { + font-size: 18px; + font-weight: 600; + color: #333; + margin: 0; +} + +.appDesc { + font-size: 12px; + color: #999; + margin: 2px 0 0 0; +} + +.drawerBody { + flex: 1; + padding: 20px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.primaryButton { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + border-radius: 12px; + padding: 16px 20px; + display: flex; + align-items: center; + gap: 12px; + cursor: pointer; + transition: all 0.3s; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); + } + + .buttonIcon { + font-size: 20px; + } + + span { + font-size: 16px; + font-weight: 600; + color: white; + } +} + +.menuSection { + margin-top: 8px; +} + +.menuItem { + display: flex; + align-items: center; + padding: 16px 20px; + cursor: pointer; + transition: all 0.3s; + border-radius: 8px; + + &:hover { + background-color: #f0f0f0; + } + + span { + font-size: 16px; + color: #333; + font-weight: 500; + } +} + +.menuIcon { + font-size: 20px; + margin-right: 12px; + width: 20px; + text-align: center; +} + +.drawerFooter { + padding: 20px; + background: #fff; + border-top: 1px solid #f0f0f0; + .balanceSection { + display: flex; + justify-content: space-between; + align-items: center; + .balanceIcon { + color: #666; + .suanliIcon { + font-size: 20px; + } + } + + .balanceText { + color: #3d9c0d; + } + } +} + +.balanceLabel { + font-size: 12px; + color: #999; + margin: 0; +} + +.balanceAmount { + font-size: 18px; + font-weight: 600; + color: #52c41a; + margin: 2px 0 0 0; +} + +// 响应式设计 +@media (max-width: 768px) { + .header { + padding: 0 12px; + } + + .title { + font-size: 16px; + } + + .username { + display: none; + } + + .drawer { + width: 280px !important; + } +} diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.tsx new file mode 100644 index 00000000..c8892960 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/weChat/components/NavCommon/index.tsx @@ -0,0 +1,120 @@ +import React, { useState } from "react"; +import { Layout, Drawer, Avatar, Dropdown, Space, Button } from "antd"; +import { + MenuOutlined, + UserOutlined, + LogoutOutlined, + SettingOutlined, +} from "@ant-design/icons"; +import type { MenuProps } from "antd"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; +import { useNavigate } from "react-router-dom"; +import styles from "./index.module.scss"; + +const { Header } = Layout; + +interface NavCommonProps { + title?: string; + onMenuClick?: () => void; + drawerContent?: React.ReactNode; +} + +const NavCommon: React.FC = ({ + title = "触客宝", + onMenuClick, + drawerContent, +}) => { + const [drawerVisible, setDrawerVisible] = useState(false); + + const { userInfo } = useCkChatStore(); + + // 处理菜单图标点击 + const handleMenuClick = () => { + setDrawerVisible(true); + onMenuClick?.(); + }; + + // 处理抽屉关闭 + const handleDrawerClose = () => { + setDrawerVisible(false); + }; + + // 默认抽屉内容 + const defaultDrawerContent = ( + + + + ✨ + + 触客宝 + AI智能营销系统 + + + + + + 🔒 + AI智能客服 + + + + 📊 + 功能中心 + + + + + + + ⚡算力余额 + + 9307.423 + + + + ); + + return ( + <> + + + } + onClick={handleMenuClick} + className={styles.menuButton} + /> + {title} + + + + + + ⚡ + 9307.423 + + } + src={userInfo?.account?.avatar} + className={styles.avatar} + /> + + + + + + {drawerContent || defaultDrawerContent} + + > + ); +}; + +export default NavCommon; diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/MessageList.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/MessageList.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/MessageList.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/MessageList.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/api.ts b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/api.ts similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/api.ts rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/api.ts diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/data.ts b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/data.ts similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/data.ts rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/data.ts diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/MessageList/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/MessageList/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/SidebarMenu.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/SidebarMenu.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/SidebarMenu.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/WechatFriends.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/WechatFriends.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/WechatFriends.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/WechatFriends.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/WechatFriends/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/WechatFriends/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/SidebarMenu/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/SidebarMenu/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/Skeleton/index.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/Skeleton/index.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/Skeleton/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/Skeleton/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/Skeleton/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/VerticalUserList.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/VerticalUserList.module.scss similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/VerticalUserList.module.scss rename to Cunkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/VerticalUserList.module.scss diff --git a/Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/index.tsx similarity index 100% rename from Cunkebao/src/pages/pc/ckbox/components/VerticalUserList/index.tsx rename to Cunkebao/src/pages/pc/ckbox/weChat/components/VerticalUserList/index.tsx diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/data.ts b/Cunkebao/src/pages/pc/ckbox/weChat/data.ts new file mode 100644 index 00000000..0444a312 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/weChat/data.ts @@ -0,0 +1,323 @@ +// 消息列表数据接口 - 支持weChatGroup和contracts两种数据类型 +export interface MessageListData { + serverId: number | string; // 服务器ID作为主键 + id?: number; // 接口数据的原始ID字段 + + // 数据类型标识 + dataType: "weChatGroup" | "contracts"; // 数据类型:微信群组或联系人 + + // 通用字段(两种类型都有的字段) + wechatAccountId: number; // 微信账号ID + tenantId: number; // 租户ID + accountId: number; // 账号ID + nickname: string; // 昵称 + avatar?: string; // 头像 + groupId: number; // 分组ID + config?: { + chat: boolean; + }; // 配置信息 + labels?: string[]; // 标签列表 + unreadCount: number; // 未读消息数 + + // 联系人特有字段(当dataType为'contracts'时使用) + wechatId?: string; // 微信ID + alias?: string; // 别名 + conRemark?: string; // 备注 + quanPin?: string; // 全拼 + gender?: number; // 性别 + region?: string; // 地区 + addFrom?: number; // 添加来源 + phone?: string; // 电话 + signature?: string; // 签名 + extendFields?: any; // 扩展字段 + city?: string; // 城市 + lastUpdateTime?: string; // 最后更新时间 + isPassed?: boolean; // 是否通过 + thirdParty?: any; // 第三方 + additionalPicture?: string; // 附加图片 + desc?: string; // 描述 + lastMessageTime?: number; // 最后消息时间 + duplicate?: boolean; // 是否重复 + + // 微信群组特有字段(当dataType为'weChatGroup'时使用) + chatroomId?: string; // 群聊ID + chatroomOwner?: string; // 群主 + chatroomAvatar?: string; // 群头像 + notice?: string; // 群公告 + selfDisplyName?: string; // 自己在群里的显示名称 + + [key: string]: any; // 兼容其他字段 +} + +//联系人标签分组 +export interface ContactGroupByLabel { + id: number; + accountId?: number; + groupName: string; + tenantId?: number; + count: number; + [key: string]: any; +} +//终端用户数据接口 +export interface KfUserListData { + id: number; + tenantId: number; + wechatId: string; + nickname: string; + alias: string; + avatar: string; + gender: number; + region: string; + signature: string; + bindQQ: string; + bindEmail: string; + bindMobile: string; + createTime: string; + currentDeviceId: number; + isDeleted: boolean; + deleteTime: string; + groupId: number; + memo: string; + wechatVersion: string; + labels: string[]; + lastUpdateTime: string; + isOnline?: boolean; + [key: string]: any; +} + +// 账户信息接口 +export interface CkAccount { + id: number; + realName: string; + nickname: string | null; + memo: string | null; + avatar: string; + userName: string; + secret: string; + accountType: number; + departmentId: number; + useGoogleSecretKey: boolean; + hasVerifyGoogleSecret: boolean; +} + +//群聊数据接口 +export interface weChatGroup { + id?: number; + wechatAccountId: number; + tenantId: number; + accountId: number; + chatroomId: string; + chatroomOwner: string; + conRemark: string; + nickname: string; + chatroomAvatar: string; + groupId: number; + config?: { + chat: boolean; + }; + labels?: string[]; + unreadCount: number; + 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; + }; + lastMessageTime: number; + unreadCount: number; + duplicate: boolean; + [key: string]: any; +} + +//聊天记录接口 +export interface ChatRecord { + id: number; + wechatFriendId: number; + wechatAccountId: number; + tenantId: number; + accountId: number; + synergyAccountId: number; + content: string; + msgType: number; + msgSubType: number; + msgSvrId: string; + isSend: boolean; + createTime: string; + isDeleted: boolean; + deleteTime: string; + sendStatus: number; + wechatTime: number; + origin: number; + msgId: number; + recalled: boolean; + sender?: { + chatroomNickname: string; + isAdmin: boolean; + isDeleted: boolean; + nickname: string; + ownerWechatId: string; + wechatId: string; + [key: string]: any; + }; + [key: string]: any; +} + +/** + * 微信好友基本信息接口 + * 包含主要字段和兼容性字段 + */ +export interface WechatFriend { + // 主要字段 + id: number; // 好友ID + wechatAccountId: number; // 微信账号ID + wechatId: string; // 微信ID + nickname: string; // 昵称 + conRemark: string; // 备注名 + avatar: string; // 头像URL + gender: number; // 性别:1-男,2-女,0-未知 + region: string; // 地区 + phone: string; // 电话 + labels: string[]; // 标签列表 + [key: string]: any; +} + +// 消息类型枚举 +export enum MessageType { + TEXT = "text", + IMAGE = "image", + VOICE = "voice", + VIDEO = "video", + FILE = "file", + LOCATION = "location", +} + +// 消息数据接口 +export interface MessageData { + id: string; + senderId: string; + senderName: string; + content: string; + type: MessageType; + timestamp: string; + isRead: boolean; + replyTo?: string; + forwardFrom?: string; +} + +// 聊天会话类型 +export type ChatType = "private" | "group"; + +// 聊天会话接口 +export interface ChatSession { + id: string; + type: ChatType; + name: string; + avatar?: string; + lastMessage: string; + lastTime: string; + unreadCount: number; + online: boolean; + members?: string[]; + pinned?: boolean; + muted?: boolean; +} + +// 聊天历史响应接口 +export interface ChatHistoryResponse { + messages: MessageData[]; + hasMore: boolean; + total: number; +} + +// 发送消息请求接口 +export interface SendMessageRequest { + chatId: string; + content: string; + type: MessageType; + replyTo?: string; +} + +// 搜索联系人请求接口 +export interface SearchContactRequest { + keyword: string; + limit?: number; +} + +// 在线状态接口 +export interface OnlineStatus { + userId: string; + online: boolean; + lastSeen: string; +} + +// 消息状态接口 +export interface MessageStatus { + messageId: string; + status: "sending" | "sent" | "delivered" | "read" | "failed"; + timestamp: string; +} + +// 文件上传响应接口 +export interface FileUploadResponse { + url: string; + filename: string; + size: number; + type: string; +} + +// 表情包接口 +export interface EmojiData { + id: string; + name: string; + url: string; + category: string; +} + +// 快捷回复接口 +export interface QuickReply { + id: string; + content: string; + category: string; + useCount: number; +} + +// 聊天设置接口 +export interface ChatSettings { + autoReply: boolean; + autoReplyMessage: string; + notification: boolean; + sound: boolean; + theme: "light" | "dark"; + fontSize: "small" | "medium" | "large"; +} diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/index.module.scss b/Cunkebao/src/pages/pc/ckbox/weChat/index.module.scss new file mode 100644 index 00000000..9b37437e --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/weChat/index.module.scss @@ -0,0 +1,198 @@ +.ckboxLayout { + height: 100vh; + background: #fff; + display: flex; + flex-direction: column; + + .header { + background: #1890ff; + color: #fff; + height: 64px; + line-height: 64px; + padding: 0 24px; + font-size: 18px; + font-weight: bold; + } + + .verticalSider { + background: #2e2e2e; + border-right: 1px solid #f0f0f0; + overflow: hidden; + } + + .sider { + background: #fff; + border-right: 1px solid #f0f0f0; + overflow: auto; + } + + .sidebar { + height: 100%; + display: flex; + flex-direction: column; + + .searchBar { + padding: 16px; + border-bottom: 1px solid #f0f0f0; + background: #fff; + + :global(.ant-input) { + border-radius: 20px; + background: #f5f5f5; + border: none; + + &:focus { + background: #fff; + border: 1px solid #1890ff; + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); + } + } + } + + .tabs { + flex: 1; + display: flex; + flex-direction: column; + + :global(.ant-tabs-content) { + flex: 1; + overflow: hidden; + } + + :global(.ant-tabs-tabpane) { + height: 100%; + overflow: hidden; + } + + :global(.ant-tabs-nav) { + margin: 0; + padding: 0 16px; + background: #fff; + border-bottom: 1px solid #f0f0f0; + + :global(.ant-tabs-tab) { + padding: 12px 16px; + margin: 0; + + &:hover { + color: #1890ff; + } + + &.ant-tabs-tab-active { + .ant-tabs-tab-btn { + color: #1890ff; + } + } + } + + :global(.ant-tabs-ink-bar) { + background: #1890ff; + } + } + } + + .emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 200px; + color: #8c8c8c; + + p { + margin-top: 16px; + font-size: 14px; + } + } + } + + .mainContent { + background: #f5f5f5; + display: flex; + flex-direction: column; + overflow: auto; + + .chatContainer { + height: 100%; + display: flex; + flex-direction: column; + + .chatToolbar { + background: #fff; + border-bottom: 1px solid #f0f0f0; + padding: 8px 16px; + display: flex; + justify-content: flex-end; + align-items: center; + min-height: 48px; + } + } + + .welcomeScreen { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + background: #fff; + + .welcomeContent { + text-align: center; + color: #8c8c8c; + + h2 { + margin: 24px 0 12px 0; + color: #262626; + font-size: 24px; + font-weight: 600; + } + + p { + font-size: 16px; + margin: 0; + } + } + } + } +} + +// 响应式设计 +@media (max-width: 768px) { + .ckboxLayout { + .sidebar { + .searchBar { + padding: 12px; + } + + .tabs { + :global(.ant-tabs-nav) { + padding: 0 12px; + + :global(.ant-tabs-tab) { + padding: 10px 12px; + } + } + } + } + + .mainContent { + .chatContainer { + .chatToolbar { + padding: 6px 12px; + min-height: 40px; + } + } + + .welcomeScreen { + .welcomeContent { + h2 { + font-size: 20px; + } + + p { + font-size: 14px; + } + } + } + } + } +} diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/index.tsx b/Cunkebao/src/pages/pc/ckbox/weChat/index.tsx new file mode 100644 index 00000000..2129769c --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/weChat/index.tsx @@ -0,0 +1,106 @@ +import React, { useState, useEffect } from "react"; +import { Layout, Button, Space, message, Tooltip } from "antd"; +import { InfoCircleOutlined, MessageOutlined } from "@ant-design/icons"; +import dayjs from "dayjs"; +import ChatWindow from "./components/ChatWindow/index"; +import SidebarMenu from "./components/SidebarMenu/index"; +import VerticalUserList from "./components/VerticalUserList"; +import PageSkeleton from "./components/Skeleton"; +import NavCommon from "./components/NavCommon"; +import styles from "./index.module.scss"; +import { addChatSession } from "@/store/module/ckchat/ckchat"; +const { Content, Sider } = Layout; +import { chatInitAPIdata, initSocket } from "./main"; +import { useWeChatStore } from "@/store/module/weChat/weChat"; + +import { KfUserListData } from "@/pages/pc/ckbox/data"; + +const CkboxPage: React.FC = () => { + // 不要在组件初始化时获取sendCommand,而是在需要时动态获取 + const [loading, setLoading] = useState(false); + const currentContract = useWeChatStore(state => state.currentContract); + useEffect(() => { + // 方法一:使用 Promise 链式调用处理异步函数 + setLoading(true); + chatInitAPIdata() + .then(response => { + const data = response as { + contractList: any[]; + groupList: any[]; + kfUserList: KfUserListData[]; + newContractList: { groupName: string; contacts: any[] }[]; + }; + const { contractList } = data; + + //找出已经在聊天的 + const isChatList = contractList.filter( + v => (v?.config && v.config?.chat) || false, + ); + isChatList.forEach(v => { + addChatSession(v); + }); + + // 数据加载完成后初始化WebSocket连接 + initSocket(); + }) + .catch(error => { + console.error("获取联系人列表失败:", error); + }) + .finally(() => { + setLoading(false); + }); + }, []); + + return ( + + + + + {/* 垂直侧边栏 */} + + + + + + {/* 左侧联系人边栏 */} + + + + + {/* 主内容区 */} + + {currentContract ? ( + + {/* + + + } + onClick={() => setShowProfile(!showProfile)} + size="small" + > + {showProfile ? "隐藏资料" : "显示资料"} + + + + */} + + + ) : ( + + + + 欢迎使用触客宝 + 选择一个联系人开始聊天 + + + )} + + + + + ); +}; + +export default CkboxPage; diff --git a/Cunkebao/src/pages/pc/ckbox/weChat/main.ts b/Cunkebao/src/pages/pc/ckbox/weChat/main.ts new file mode 100644 index 00000000..19f6cf62 --- /dev/null +++ b/Cunkebao/src/pages/pc/ckbox/weChat/main.ts @@ -0,0 +1,307 @@ +import { + asyncKfUserList, + asyncContractList, + asyncChatSessions, + asyncWeChatGroup, + asyncCountLables, + useCkChatStore, +} from "@/store/module/ckchat/ckchat"; +import { useWebSocketStore } from "@/store/module/websocket/websocket"; + +import { + loginWithToken, + getControlTerminalList, + getContactList, + getGroupList, +} from "./api"; + +import { useUserStore } from "@/store/module/user"; + +import { + KfUserListData, + ContractData, + weChatGroup, +} from "@/pages/pc/ckbox/data"; + +import { WechatGroup } from "./api"; +const { login2 } = useUserStore.getState(); +//获取触客宝基础信息 +export const chatInitAPIdata = async () => { + try { + //获取联系人列表 + const contractList = await getAllContactList(); + + //获取联系人列表 + asyncContractList(contractList); + + //获取群列表 + const groupList = await getAllGroupList(); + + await asyncWeChatGroup(groupList); + + // 提取不重复的wechatAccountId组 + const uniqueWechatAccountIds: number[] = getUniqueWechatAccountIds( + contractList, + groupList, + ); + + //获取控制终端列表 + const kfUserList: KfUserListData[] = + await getControlTerminalListByWechatAccountIds(uniqueWechatAccountIds); + + //获取用户列表 + await asyncKfUserList(kfUserList); + + //获取标签列表 + const countLables = await getCountLables(); + await asyncCountLables(countLables); + + //获取消息会话列表并按lastUpdateTime排序 + const filterUserSessions = contractList?.filter( + v => v?.config && v.config?.chat, + ); + const filterGroupSessions = groupList?.filter( + v => v?.config && v.config?.chat, + ); + //排序功能 + const sortedSessions = [...filterUserSessions, ...filterGroupSessions].sort( + (a, b) => { + // 如果lastUpdateTime不存在,则将其排在最后 + if (!a.lastUpdateTime) return 1; + if (!b.lastUpdateTime) return -1; + + // 首先按时间降序排列(最新的在前面) + const timeCompare = + new Date(b.lastUpdateTime).getTime() - + new Date(a.lastUpdateTime).getTime(); + + // 如果时间相同,则按未读消息数量降序排列 + if (timeCompare === 0) { + // 如果unreadCount不存在,则将其排在后面 + const aUnread = a.unreadCount || 0; + const bUnread = b.unreadCount || 0; + return bUnread - aUnread; // 未读消息多的排在前面 + } + + return timeCompare; + }, + ); + //会话数据同步 + asyncChatSessions(sortedSessions); + + return { + contractList, + groupList, + kfUserList, + }; + } catch (error) { + console.error("获取联系人列表失败:", error); + return []; + } +}; +//发起soket连接 +export const initSocket = () => { + // 检查WebSocket是否已经连接 + const { status } = useWebSocketStore.getState(); + + // 如果已经连接或正在连接,则不重复连接 + if (["connected", "connecting"].includes(status)) { + console.log("WebSocket已连接或正在连接,跳过重复连接", { status }); + return; + } + + // 从store获取token和accountId + const { token2 } = useUserStore.getState(); + const { getAccountId } = useCkChatStore.getState(); + const Token = token2; + const accountId = getAccountId(); + // 使用WebSocket store初始化连接 + const { connect } = useWebSocketStore.getState(); + + // 连接WebSocket + connect({ + accessToken: Token, + accountId: Number(accountId), + client: "kefu-client", + cmdType: "CmdSignIn", + seq: +new Date(), + }); +}; + +export const getCountLables = async () => { + const LablesRes = await Promise.all( + [1, 2].map(item => + WechatGroup({ + groupType: item, + }), + ), + ); + const [friend, group] = LablesRes; + const countLables = [ + ...[ + { + id: 0, + groupName: "默认群分组", + groupType: 2, + }, + ], + ...group, + ...friend, + ...[ + { + id: 0, + groupName: "未分组", + groupType: 1, + }, + ], + ]; + + return countLables; +}; +/** + * 根据标签组织联系人 + * @param contractList 联系人列表 + * @param countLables 标签列表 + * @returns 按标签分组的联系人 + */ + +//获取控制终端列表 +export const getControlTerminalListByWechatAccountIds = ( + WechatAccountIds: number[], +) => { + return Promise.all( + WechatAccountIds.map(id => getControlTerminalList({ id: id })), + ); +}; +// 递归获取所有联系人列表 +export const getAllContactList = async () => { + try { + let allContacts = []; + let prevId = 0; + const count = 1000; + let hasMore = true; + + while (hasMore) { + const contractList = await getContactList({ + prevId, + count, + }); + + if ( + !contractList || + !Array.isArray(contractList) || + contractList.length === 0 + ) { + hasMore = false; + break; + } + + allContacts = [...allContacts, ...contractList]; + + // 如果返回的数据少于请求的数量,说明已经没有更多数据了 + if (contractList.length < count) { + hasMore = false; + } else { + // 获取最后一条数据的id作为下一次请求的prevId + const lastContact = contractList[contractList.length - 1]; + prevId = lastContact.id; + } + } + return allContacts; + } catch (error) { + console.error("获取所有联系人列表失败:", error); + return []; + } +}; + +// 提取不重复的wechatAccountId组 +export const getUniqueWechatAccountIds = ( + contacts: ContractData[], + groupList: weChatGroup[], +) => { + if (!contacts || !Array.isArray(contacts) || contacts.length === 0) { + return []; + } + + // 使用Set来存储不重复的wechatAccountId + const uniqueAccountIdsSet = new Set(); + + // 遍历联系人列表,将每个wechatAccountId添加到Set中 + contacts.forEach(contact => { + if (contact && contact.wechatAccountId) { + uniqueAccountIdsSet.add(contact.wechatAccountId); + } + }); + + // 遍历联系人列表,将每个wechatAccountId添加到Set中 + groupList.forEach(group => { + if (group && group.wechatAccountId) { + uniqueAccountIdsSet.add(group.wechatAccountId); + } + }); + + // 将Set转换为数组并返回 + return Array.from(uniqueAccountIdsSet); +}; +// 递归获取所有群列表 +export const getAllGroupList = async () => { + try { + let allContacts = []; + let prevId = 0; + const count = 1000; + let hasMore = true; + + while (hasMore) { + const contractList = await getGroupList({ + prevId, + count, + }); + + if ( + !contractList || + !Array.isArray(contractList) || + contractList.length === 0 + ) { + hasMore = false; + break; + } + + allContacts = [...allContacts, ...contractList]; + + // 如果返回的数据少于请求的数量,说明已经没有更多数据了 + if (contractList.length < count) { + hasMore = false; + } else { + // 获取最后一条数据的id作为下一次请求的prevId + const lastContact = contractList[contractList.length - 1]; + prevId = lastContact.id; + } + } + + return allContacts; + } catch (error) { + console.error("获取所有群列表失败:", error); + return []; + } +}; + +//获取token +const getToken = () => { + return new Promise((resolve, reject) => { + const params = { + grant_type: "password", + password: "kr123456", + username: "kr_xf3", + // username: "karuo", + // password: "zhiqun1984", + }; + loginWithToken(params) + .then(res => { + login2(res.access_token); + resolve(res.access_token); + }) + .catch(err => { + reject(err); + }); + }); +}; diff --git a/Cunkebao/src/router/module/ckbox.tsx b/Cunkebao/src/router/module/ckbox.tsx index 6d64437f..7a5ae294 100644 --- a/Cunkebao/src/router/module/ckbox.tsx +++ b/Cunkebao/src/router/module/ckbox.tsx @@ -1,8 +1,6 @@ -import React from "react"; -import { lazy } from "react"; import type { RouteObject } from "react-router-dom"; - -const CkboxPage = lazy(() => import("@/pages/pc/ckbox")); +import CkboxPage from "@/pages/pc/ckbox"; +import WeChatPage from "@/pages/pc/ckbox/weChat"; const ckboxRoutes: (RouteObject & { auth?: boolean; requiredRole?: string })[] = [ @@ -11,6 +9,12 @@ const ckboxRoutes: (RouteObject & { auth?: boolean; requiredRole?: string })[] = element: , auth: true, requiredRole: "user", + children: [ + { + path: "weChat", + element: , + }, + ], }, ];
选择一个联系人开始聊天
实时监控系统运行状态和数据指标