FEAT => 本次更新项目为:

This commit is contained in:
超级老白兔
2025-08-22 14:21:35 +08:00
parent d0bd7d4cd7
commit 32ea075e90
9 changed files with 206 additions and 250 deletions

View File

@@ -78,16 +78,16 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
const fetchChatHistory = async () => {
try {
setLoading(true);
// 从chat对象中提取wechatFriendId
// 假设chat.id存储的是wechatFriendId
const wechatFriendId = parseInt(chat.id);
if (isNaN(wechatFriendId)) {
messageApi.error("无效的聊天ID");
return;
}
// 调用API获取聊天历史
const response = await getChatMessage({
wechatAccountId: 32686452, // 使用实际的wechatAccountId
@@ -98,9 +98,9 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
olderData: false,
keyword: "",
});
console.log("聊天历史响应:", response);
if (response && Array.isArray(response)) {
// 将API返回的消息记录转换为MessageData格式
const chatMessages: MessageData[] = response.map(item => {
@@ -112,24 +112,28 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
} catch (e) {
msgContent = item.content;
}
// 判断消息是发送还是接收
const isSend = item.isSend === true;
return {
id: item.id.toString(),
senderId: isSend ? "me" : "other",
senderName: isSend ? "我" : chat.name,
content: msgContent,
type: MessageType.TEXT, // 默认为文本类型实际应根据msgType字段判断
timestamp: item.createTime || new Date(item.wechatTime).toISOString(),
timestamp:
item.createTime || new Date(item.wechatTime).toISOString(),
isRead: true, // 默认已读
};
});
// 按时间排序
chatMessages.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
chatMessages.sort(
(a, b) =>
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),
);
setMessages(chatMessages);
} else {
// 如果没有消息,显示空数组

View File

@@ -60,4 +60,4 @@
padding: 20px;
text-align: center;
}
}
}

View File

@@ -1,12 +1,12 @@
import React from "react";
import { List, Avatar, Badge } from "antd";
import { ContactData } from "../../data";
import { ContactData } from "./data";
import styles from "./ContactListSimple.module.scss";
interface ContactListSimpleProps {
contacts: ContactData[];
onContactClick: (contact: ContactData) => void;
selectedContactId?: string;
selectedContactId?: number;
}
const ContactListSimple: React.FC<ContactListSimpleProps> = ({
@@ -31,14 +31,14 @@ const ContactListSimple: React.FC<ContactListSimpleProps> = ({
<Avatar
src={contact.avatar}
icon={
!contact.avatar && <span>{contact.name.charAt(0)}</span>
!contact.avatar && <span>{contact.nickname.charAt(0)}</span>
}
className={styles.avatar}
/>
</Badge>
</div>
<div className={styles.contactInfo}>
<div className={styles.name}>{contact.name}</div>
<div className={styles.name}>{contact.nickname}</div>
</div>
</List.Item>
)}

View File

@@ -0,0 +1,47 @@
// 联系人数据接口
export interface ContactData {
id?: 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: null;
lastMessageTime: number;
unreadCount: number;
duplicate: boolean;
}
//聊天会话类型
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;
}

View File

@@ -6,12 +6,13 @@ import {
TeamOutlined,
MessageOutlined,
} from "@ant-design/icons";
import { ContactData, ChatSession } from "../../data";
import ContactListSimple from "./ContactListSimple";
import { ContactData, ChatSession } from "./data";
import WechatFriendsModule from "./WechatFriendsModule";
import MessageList from "../MessageList/index";
import styles from "./SidebarMenu.module.scss";
const { Sider } = Layout;
const { TabPane } = Tabs;
interface SidebarMenuProps {
contacts: ContactData[];
@@ -41,7 +42,7 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
if (!searchText) return contacts;
return contacts.filter(
contact =>
contact.name.toLowerCase().includes(searchText.toLowerCase()) ||
contact.nickname.toLowerCase().includes(searchText.toLowerCase()) ||
contact.phone.includes(searchText),
);
};
@@ -71,56 +72,52 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
activeKey={activeTab}
onChange={setActiveTab}
className={styles.tabs}
items={[
{
key: "chats",
label: (
<span>
<MessageOutlined />
</span>
),
children: (
<MessageList
sessions={getFilteredSessions()}
currentChat={currentChat}
onChatSelect={onChatSelect}
/>
),
},
{
key: "contacts",
label: (
<span>
<UserOutlined />
</span>
),
children: (
<ContactListSimple
contacts={getFilteredContacts()}
onContactClick={onContactClick}
selectedContactId={currentChat?.id.split("_")[1]}
/>
),
},
{
key: "groups",
label: (
<span>
<TeamOutlined />
</span>
),
children: (
<div className={styles.emptyState}>
<TeamOutlined style={{ fontSize: 48, color: "#ccc" }} />
<p></p>
</div>
),
},
]}
/>
>
<TabPane
tab={
<span>
<MessageOutlined />
</span>
}
key="chats"
>
<MessageList
sessions={getFilteredSessions()}
currentChat={currentChat}
onChatSelect={onChatSelect}
/>
</TabPane>
<TabPane
tab={
<span>
<UserOutlined />
</span>
}
key="contacts"
>
<WechatFriendsModule
contacts={getFilteredContacts()}
onContactClick={onContactClick}
selectedContactId={currentChat?.id.split("_")[1]}
/>
</TabPane>
<TabPane
tab={
<span>
<TeamOutlined />
</span>
}
key="groups"
>
<div className={styles.emptyState}>
<TeamOutlined style={{ fontSize: 48, color: "#ccc" }} />
<p></p>
</div>
</TabPane>
</Tabs>
</Sider>
);
};

View File

@@ -1,14 +1,52 @@
// 联系人数据接口
export interface ContactData {
id: string;
name: string;
phone: string;
id?: number;
wechatAccountId: number;
wechatId: string;
alias: string;
conRemark: string;
nickname: string;
quanPin: string;
avatar?: string;
online: boolean;
lastSeen?: string;
status?: string;
department?: string;
position?: 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: null;
lastMessageTime: number;
unreadCount: number;
duplicate: boolean;
}
/**
* 微信好友基本信息接口
* 包含主要字段和兼容性字段
*/
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;
}
// 消息类型枚举
@@ -52,18 +90,6 @@ export interface ChatSession {
muted?: boolean;
}
// 群组信息接口
export interface GroupData {
id: string;
name: string;
avatar?: string;
description?: string;
members: ContactData[];
adminIds: string[];
createdAt: string;
updatedAt: string;
}
// 聊天历史响应接口
export interface ChatHistoryResponse {
messages: MessageData[];
@@ -79,12 +105,6 @@ export interface SendMessageRequest {
replyTo?: string;
}
// 联系人列表响应接口
export interface ContactListResponse {
contacts: ContactData[];
total: number;
}
// 搜索联系人请求接口
export interface SearchContactRequest {
keyword: string;

View File

@@ -1,19 +1,17 @@
import React, { useState, useEffect, useRef } from "react";
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 { ContactData, MessageData, ChatSession } from "./data";
import { ChatSession } from "./data";
import { ContactData } from "./components/SidebarMenu/data";
import ChatWindow from "./components/ChatWindow/index";
import SidebarMenu from "./components/SidebarMenu/index";
import styles from "./index.module.scss";
import { getContactList, getChatMessage } from "./api";
const { Content } = Layout;
import { loginWithToken, getChuKeBaoUserInfo } from "@/pages/login/api";
import { useCkChatStore } from "@/store/module/ckchat";
const { Content } = Layout;
import { loginWithToken } from "@/pages/login/api";
import { useUserStore } from "@/store/module/user";
import { useWebSocketStore } from "@/store/module/websocket";
import { chatInitAPIdata, getChatInfo } from "./main";
import { chatInitAPIdata } from "./main";
const CkboxPage: React.FC = () => {
const [messageApi, contextHolder] = message.useMessage();
@@ -22,12 +20,19 @@ const CkboxPage: React.FC = () => {
const [currentChat, setCurrentChat] = useState<ChatSession | null>(null);
const [loading, setLoading] = useState(false);
const [showProfile, setShowProfile] = useState(true);
const { setUserInfo, getAccountId } = useCkChatStore();
const { login2 } = useUserStore();
useEffect(() => {
const contactList = chatInitAPIdata();
console.log(contactList);
// 方法一:使用 Promise 链式调用处理异步函数
chatInitAPIdata()
.then(contactList => {
console.log(contactList);
// 如果需要可以设置联系人列表
setContacts(contactList);
})
.catch(error => {
console.error("获取联系人列表失败:", error);
});
}, []);
const getToken2 = () => {
@@ -48,116 +53,6 @@ const CkboxPage: React.FC = () => {
});
};
const fetchContacts = async () => {
try {
setLoading(true);
// 使用API获取联系人数据
const response = await getContactList({ prevId: 0, count: 500 });
if (response) {
// 转换API返回的数据结构为组件所需的ContactData结构
const contactList: ContactData[] = response.map((item: any) => ({
id: item.id.toString(),
name: item.nickname || item.conRemark || item.alias || "",
phone: item.phone || "",
avatar: item.avatar || "",
online: true, // 假设所有联系人都在线实际应根据API返回数据调整
status: "在线", // 假设状态实际应根据API返回数据调整
department: "", // API中没有对应字段可以根据需要添加
position: "", // API中没有对应字段可以根据需要添加
}));
setContacts(contactList);
}
} catch (error) {
messageApi.error("获取联系人失败");
console.error("获取联系人失败:", error);
} finally {
setLoading(false);
}
};
const fetchChatSessions = async () => {
try {
// 先确保联系人列表已加载
if (contacts.length === 0) {
await fetchContacts();
}
const response = await getChatMessage({
wechatAccountId: 32686452, // 使用实际的wechatAccountId
wechatFriendId: 0, // 可以设置为0获取所有好友的消息
From: 0,
To: 0,
Count: 50, // 获取最近的50条消息
olderData: false,
keyword: "",
});
console.log("聊天消息响应:", response);
if (response && Array.isArray(response)) {
// 创建一个Map来存储每个好友ID对应的最新消息
const friendMessageMap = new Map<number, any>();
// 遍历所有消息,只保留每个好友的最新消息
response.forEach(item => {
const friendId = item.wechatFriendId;
const existingMessage = friendMessageMap.get(friendId);
// 如果Map中没有这个好友的消息或者当前消息比已存在的更新则更新Map
if (
!existingMessage ||
new Date(item.createTime) > new Date(existingMessage.createTime)
) {
friendMessageMap.set(friendId, item);
}
});
// 将Map转换为数组
const latestMessages = Array.from(friendMessageMap.values());
// 将API返回的消息记录转换为ChatSession格式
const sessions: ChatSession[] = latestMessages.map(item => {
// 解析content字段它是一个JSON字符串
let msgContent = "";
try {
const contentObj = JSON.parse(item.content);
msgContent = contentObj.content || "";
} catch (e) {
msgContent = item.content;
}
// 尝试从联系人列表中找到对应的联系人信息
const contact = contacts.find(
c => c.id === item.wechatFriendId.toString(),
);
return {
id: item.id.toString(),
type: "private", // 假设都是私聊
name: contact ? contact.name : `联系人 ${item.wechatFriendId}`, // 使用联系人名称或默认名称
avatar: contact?.avatar || "", // 使用联系人头像或默认空字符串
lastMessage: msgContent,
lastTime:
item.createTime || new Date(item.wechatTime).toISOString(),
unreadCount: 0, // 未读消息数需要另外获取
online: contact?.online || false, // 使用联系人在线状态或默认为false
};
});
// 按最后消息时间排序
sessions.sort(
(a, b) =>
new Date(b.lastTime).getTime() - new Date(a.lastTime).getTime(),
);
setChatSessions(sessions);
}
} catch (error) {
console.error("获取聊天记录失败:", error);
messageApi.error("获取聊天记录失败");
}
};
const handleContactClick = (contact: ContactData) => {
// 查找或创建聊天会话
let session = chatSessions.find(s => s.id === contact.id);
@@ -165,7 +60,7 @@ const CkboxPage: React.FC = () => {
session = {
id: contact.id,
type: "private",
name: contact.name,
name: contact.nickname,
avatar: contact.avatar,
lastMessage: "",
lastTime: dayjs().toISOString(),
@@ -181,16 +76,6 @@ const CkboxPage: React.FC = () => {
if (!currentChat || !message.trim()) return;
try {
const newMessage: MessageData = {
id: Date.now().toString(),
senderId: "me",
senderName: "我",
content: message,
type: "text" as any,
timestamp: dayjs().toISOString(),
isRead: false,
};
// 更新当前聊天会话
const updatedSession = {
...currentChat,

View File

@@ -9,30 +9,35 @@ const { connect } = useWebSocketStore.getState();
const { setUserInfo, getAccountId } = useCkChatStore.getState();
//获取触客宝基础信息
export const chatInitAPIdata = async () => {
//获取Token
const Token = await getToken();
//获取用户信息
const userInfo = await getChuKeBaoUserInfo();
setUserInfo(userInfo);
try {
//获取Token
const Token = await getToken();
//获取用户信息
const userInfo = await getChuKeBaoUserInfo();
setUserInfo(userInfo);
//获取用户账号Id
const accountId = getAccountId();
//获取用户账号Id
const accountId = getAccountId();
//发起链接
connect({
accessToken: String(Token),
accountId: accountId,
client: "kefu-client",
cmdType: "CmdSignIn",
seq: 1,
});
//获取联系人列表
const contactList = await getContactList({
prevId: userInfo.tenant.tenantType,
count: 100,
});
//发起链接
connect({
accessToken: String(Token),
accountId: accountId,
client: "kefu-client",
cmdType: "CmdSignIn",
seq: 1,
});
//获取联系人列表
const contactList = await getContactList({
prevId: userInfo.tenant.tenantType,
count: 100,
});
return contactList;
return contactList;
} catch (error) {
console.error("获取联系人列表失败:", error);
return [];
}
};
export const getChatInfo = () => {

View File

@@ -114,8 +114,6 @@ export const useWebSocketStore = createPersistStore<WebSocketState>(
return;
}
console.log("获取connect参数", getAccountId());
// 构建WebSocket URL
const params = new URLSearchParams({
client: fullConfig.client.toString(),