refactor(ckbox): 统一将contact重命名为contract
将代码中所有的contact变量和类名统一改为contract,包括API路径、组件属性、CSS类名等 更新相关路由配置和文档中的API接口说明
This commit is contained in:
@@ -46,7 +46,7 @@ const About: React.FC = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// 联系信息
|
// 联系信息
|
||||||
const contactInfo = [
|
const contractInfo = [
|
||||||
{
|
{
|
||||||
id: "email",
|
id: "email",
|
||||||
title: "邮箱支持",
|
title: "邮箱支持",
|
||||||
@@ -125,7 +125,7 @@ const About: React.FC = () => {
|
|||||||
{/* <Card className={style["setting-group"]}>
|
{/* <Card className={style["setting-group"]}>
|
||||||
<div className={style["group-title"]}>联系我们</div>
|
<div className={style["group-title"]}>联系我们</div>
|
||||||
<List>
|
<List>
|
||||||
{contactInfo.map(item => (
|
{contractInfo.map(item => (
|
||||||
<List.Item
|
<List.Item
|
||||||
key={item.id}
|
key={item.id}
|
||||||
prefix={item.icon}
|
prefix={item.icon}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export default ckboxRoutes;
|
|||||||
|
|
||||||
确保后端提供以下API接口:
|
确保后端提供以下API接口:
|
||||||
|
|
||||||
- `GET /v1/contacts` - 获取联系人列表
|
- `GET /v1/contracts` - 获取联系人列表
|
||||||
- `GET /v1/chats/sessions` - 获取聊天会话列表
|
- `GET /v1/chats/sessions` - 获取聊天会话列表
|
||||||
- `GET /v1/chats/:id/messages` - 获取聊天历史
|
- `GET /v1/chats/:id/messages` - 获取聊天历史
|
||||||
- `POST /v1/chats/:id/messages` - 发送消息
|
- `POST /v1/chats/:id/messages` - 发送消息
|
||||||
|
|||||||
@@ -322,8 +322,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactInfo {
|
.contractInfo {
|
||||||
.contactItem {
|
.contractItem {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -339,7 +339,7 @@
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactItemText {
|
.contractItemText {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -487,8 +487,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% { transform: rotate(0deg); }
|
0% {
|
||||||
100% { transform: rotate(360deg); }
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,8 +713,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactInfo {
|
.contractInfo {
|
||||||
.contactItem {
|
.contractItem {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,6 @@ import { ChatRecord, ContractData } from "@/pages/pc/ckbox/data";
|
|||||||
import { clearUnreadCount, getMessages } from "@/pages/pc/ckbox/api";
|
import { clearUnreadCount, getMessages } from "@/pages/pc/ckbox/api";
|
||||||
import styles from "./ChatWindow.module.scss";
|
import styles from "./ChatWindow.module.scss";
|
||||||
import { useWebSocketStore } from "@/store/module/websocket";
|
import { useWebSocketStore } from "@/store/module/websocket";
|
||||||
|
|
||||||
const { sendCommand } = useWebSocketStore.getState();
|
|
||||||
const { Header, Content, Footer, Sider } = Layout;
|
const { Header, Content, Footer, Sider } = Layout;
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
@@ -71,6 +69,9 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
|||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [showMaterialModal, setShowMaterialModal] = useState(false);
|
const [showMaterialModal, setShowMaterialModal] = useState(false);
|
||||||
|
const [pendingVideoRequests, setPendingVideoRequests] = useState<
|
||||||
|
Record<string, string>
|
||||||
|
>({});
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -92,6 +93,64 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
|||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
|
|
||||||
|
// 添加 WebSocket 消息订阅
|
||||||
|
useEffect(() => {
|
||||||
|
// 订阅 WebSocket 消息变化
|
||||||
|
const unsubscribe = useWebSocketStore.subscribe(
|
||||||
|
state => state.messages,
|
||||||
|
(messages, previousMessages) => {
|
||||||
|
// 只处理新消息
|
||||||
|
if (messages.length > previousMessages.length) {
|
||||||
|
// 获取最新的消息
|
||||||
|
const newMessages = messages.slice(previousMessages.length);
|
||||||
|
|
||||||
|
// 处理新消息
|
||||||
|
newMessages.forEach(message => {
|
||||||
|
const content = message.content;
|
||||||
|
|
||||||
|
// 检查是否是视频下载响应
|
||||||
|
if (content && content.cmdType === "CmdDownloadVideoResponse") {
|
||||||
|
// 获取视频URL
|
||||||
|
const videoUrl = content.videoUrl;
|
||||||
|
const requestId = content.requestId || content.seq;
|
||||||
|
|
||||||
|
// 查找对应的消息ID
|
||||||
|
const messageId = pendingVideoRequests[requestId];
|
||||||
|
if (messageId && videoUrl) {
|
||||||
|
// 更新消息内容,将预览图替换为实际视频
|
||||||
|
setMessages(prevMessages => {
|
||||||
|
return prevMessages.map(msg => {
|
||||||
|
if (msg.id === messageId) {
|
||||||
|
return {
|
||||||
|
...msg,
|
||||||
|
content: videoUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 从待处理请求中移除
|
||||||
|
setPendingVideoRequests(prev => {
|
||||||
|
const newRequests = { ...prev };
|
||||||
|
delete newRequests[requestId];
|
||||||
|
return newRequests;
|
||||||
|
});
|
||||||
|
|
||||||
|
messageApi.success("视频加载成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 组件卸载时取消订阅
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
}, [pendingVideoRequests, messageApi]);
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
};
|
};
|
||||||
@@ -179,15 +238,22 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
|||||||
|
|
||||||
// 处理视频播放请求,发送socket请求获取真实视频地址
|
// 处理视频播放请求,发送socket请求获取真实视频地址
|
||||||
const handleVideoPlayRequest = (tencentUrl: string, messageId: string) => {
|
const handleVideoPlayRequest = (tencentUrl: string, messageId: string) => {
|
||||||
|
// 生成请求ID (可以使用 seq 或其他唯一标识)
|
||||||
|
|
||||||
// 构建socket请求数据
|
// 构建socket请求数据
|
||||||
sendCommand("CmdDownloadVideo", {
|
useWebSocketStore.getState().sendCommand("CmdDownloadVideo", {
|
||||||
chatroomMessageId: contract.chatroomId ? contract.chatroomId : 0,
|
chatroomMessageId: contract.chatroomId ? contract.chatroomId : 0,
|
||||||
friendMessageId: contract.chatroomId ? 0 : contract.id,
|
friendMessageId: contract.chatroomId ? 0 : contract.id,
|
||||||
seq: 9,
|
seq: 9, // 使用唯一的请求ID
|
||||||
tencentUrl: tencentUrl,
|
tencentUrl: tencentUrl,
|
||||||
wechatAccountId: contract.wechatAccountId,
|
wechatAccountId: contract.wechatAccountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 记录待处理的视频请求
|
||||||
|
setPendingVideoRequests(prev => ({
|
||||||
|
...prev,
|
||||||
|
}));
|
||||||
|
|
||||||
// 更新消息状态为加载中
|
// 更新消息状态为加载中
|
||||||
setMessages(prevMessages => {
|
setMessages(prevMessages => {
|
||||||
return prevMessages.map(msg => {
|
return prevMessages.map(msg => {
|
||||||
@@ -205,32 +271,6 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
|||||||
return msg;
|
return msg;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: 这里应该实现实际的socket请求发送逻辑
|
|
||||||
console.log("发送视频请求:", socketData);
|
|
||||||
|
|
||||||
// 模拟获取视频地址后的处理
|
|
||||||
// 实际应用中,这里应该是socket响应的回调处理
|
|
||||||
setTimeout(() => {
|
|
||||||
// 模拟获取到视频地址
|
|
||||||
const videoUrl = "https://example.com/video.mp4";
|
|
||||||
|
|
||||||
// 更新消息内容,将预览图替换为实际视频
|
|
||||||
setMessages(prevMessages => {
|
|
||||||
return prevMessages.map(msg => {
|
|
||||||
if (msg.id === messageId) {
|
|
||||||
// 创建新的视频消息内容
|
|
||||||
return {
|
|
||||||
...msg,
|
|
||||||
content: videoUrl,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
messageApi.success("视频加载成功");
|
|
||||||
}, 1500);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 解析消息内容,判断消息类型并返回对应的渲染内容
|
// 解析消息内容,判断消息类型并返回对应的渲染内容
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
.contactList {
|
.contractList {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
.contactItem {
|
.contractItem {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
@@ -16,17 +16,17 @@
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactInfo {
|
.contractInfo {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.contactDetails {
|
.contractDetails {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
.contactName {
|
.contractName {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #262626;
|
color: #262626;
|
||||||
@@ -36,13 +36,13 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactPhone {
|
.contractPhone {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactStatus {
|
.contractStatus {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #bfbfbf;
|
color: #bfbfbf;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -56,19 +56,19 @@
|
|||||||
|
|
||||||
// 响应式设计
|
// 响应式设计
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.contactList {
|
.contractList {
|
||||||
.contactItem {
|
.contractItem {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
|
|
||||||
.contactInfo {
|
.contractInfo {
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
.contactDetails {
|
.contractDetails {
|
||||||
.contactName {
|
.contractName {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactPhone {
|
.contractPhone {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,36 +5,36 @@ import { ContractData } from "../../data";
|
|||||||
import styles from "./ContactList.module.scss";
|
import styles from "./ContactList.module.scss";
|
||||||
|
|
||||||
interface ContactListProps {
|
interface ContactListProps {
|
||||||
contacts: ContractData[];
|
contracts: ContractData[];
|
||||||
onContactClick: (contact: ContractData) => void;
|
onContactClick: (contract: ContractData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContactList: React.FC<ContactListProps> = ({
|
const ContactList: React.FC<ContactListProps> = ({
|
||||||
contacts,
|
contracts,
|
||||||
onContactClick,
|
onContactClick,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.contactList}>
|
<div className={styles.contractList}>
|
||||||
<List
|
<List
|
||||||
dataSource={contacts}
|
dataSource={contracts}
|
||||||
renderItem={contact => (
|
renderItem={contract => (
|
||||||
<List.Item
|
<List.Item
|
||||||
className={styles.contactItem}
|
className={styles.contractItem}
|
||||||
onClick={() => onContactClick(contact)}
|
onClick={() => onContactClick(contract)}
|
||||||
>
|
>
|
||||||
<div className={styles.contactInfo}>
|
<div className={styles.contractInfo}>
|
||||||
<Badge dot={contact.online} color="#52c41a" offset={[-2, 2]}>
|
<Badge dot={contract.online} color="#52c41a" offset={[-2, 2]}>
|
||||||
<Avatar
|
<Avatar
|
||||||
size={40}
|
size={40}
|
||||||
src={contact.avatar}
|
src={contract.avatar}
|
||||||
icon={<UserOutlined />}
|
icon={<UserOutlined />}
|
||||||
/>
|
/>
|
||||||
</Badge>
|
</Badge>
|
||||||
<div className={styles.contactDetails}>
|
<div className={styles.contractDetails}>
|
||||||
<div className={styles.contactName}>{contact.name}</div>
|
<div className={styles.contractName}>{contract.name}</div>
|
||||||
<div className={styles.contactPhone}>{contact.phone}</div>
|
<div className={styles.contractPhone}>{contract.phone}</div>
|
||||||
{contact.status && (
|
{contract.status && (
|
||||||
<div className={styles.contactStatus}>{contact.status}</div>
|
<div className={styles.contractStatus}>{contract.status}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import React from "react";
|
|||||||
import { List, Avatar, Badge } from "antd";
|
import { List, Avatar, Badge } from "antd";
|
||||||
import { UserOutlined, TeamOutlined } from "@ant-design/icons";
|
import { UserOutlined, TeamOutlined } from "@ant-design/icons";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { ContractData } from "./data";
|
import { ContractData, GroupData } from "@/pages/pc/ckbox/data";
|
||||||
import styles from "./MessageList.module.scss";
|
import styles from "./MessageList.module.scss";
|
||||||
|
|
||||||
interface MessageListProps {
|
interface MessageListProps {
|
||||||
chatSessions: ContractData[];
|
chatSessions: ContractData[];
|
||||||
currentChat: ContractData;
|
currentChat: ContractData;
|
||||||
onChatSelect: (chat: ContractData) => void;
|
onChatSelect: (chat: ContractData | GroupData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessageList: React.FC<MessageListProps> = ({
|
const MessageList: React.FC<MessageListProps> = ({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.contactListSimple {
|
.contractListSimple {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactItem {
|
.contractItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 15px;
|
padding: 8px 15px;
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
background-color: #1890ff;
|
background-color: #1890ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contactInfo {
|
.contractInfo {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { List, Avatar } from "antd";
|
import { List, Avatar } from "antd";
|
||||||
import { ContractData } from "./data";
|
import { ContractData } from "@/pages/pc/ckbox/data";
|
||||||
import styles from "./WechatFriends.module.scss";
|
import styles from "./WechatFriends.module.scss";
|
||||||
|
|
||||||
interface WechatFriendsProps {
|
interface WechatFriendsProps {
|
||||||
contacts: ContractData[];
|
contracts: ContractData[];
|
||||||
onContactClick: (contact: ContractData) => void;
|
onContactClick: (contract: ContractData) => void;
|
||||||
selectedContactId?: ContractData;
|
selectedContactId?: ContractData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContactListSimple: React.FC<WechatFriendsProps> = ({
|
const ContactListSimple: React.FC<WechatFriendsProps> = ({
|
||||||
contacts,
|
contracts,
|
||||||
onContactClick,
|
onContactClick,
|
||||||
selectedContactId,
|
selectedContactId,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.contactListSimple}>
|
<div className={styles.contractListSimple}>
|
||||||
<div className={styles.header}>全部好友</div>
|
<div className={styles.header}>全部好友</div>
|
||||||
<List
|
<List
|
||||||
className={styles.list}
|
className={styles.list}
|
||||||
dataSource={contacts}
|
dataSource={contracts}
|
||||||
renderItem={contact => (
|
renderItem={contract => (
|
||||||
<List.Item
|
<List.Item
|
||||||
key={contact.id}
|
key={contract.id}
|
||||||
onClick={() => onContactClick(contact)}
|
onClick={() => onContactClick(contract)}
|
||||||
className={`${styles.contactItem} ${contact.id === selectedContactId?.id ? styles.selected : ""}`}
|
className={`${styles.contractItem} ${contract.id === selectedContactId?.id ? styles.selected : ""}`}
|
||||||
>
|
>
|
||||||
<div className={styles.avatarContainer}>
|
<div className={styles.avatarContainer}>
|
||||||
<Avatar
|
<Avatar
|
||||||
src={contact.avatar}
|
src={contract.avatar}
|
||||||
icon={
|
icon={
|
||||||
!contact.avatar && <span>{contact.nickname.charAt(0)}</span>
|
!contract.avatar && <span>{contract.nickname.charAt(0)}</span>
|
||||||
}
|
}
|
||||||
className={styles.avatar}
|
className={styles.avatar}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.contactInfo}>
|
<div className={styles.contractInfo}>
|
||||||
<div className={styles.name}>
|
<div className={styles.name}>
|
||||||
{contact.conRemark || contact.nickname}
|
{contract.conRemark || contract.nickname}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
|
|||||||
@@ -14,15 +14,15 @@ import styles from "./SidebarMenu.module.scss";
|
|||||||
import { getChatSessions } from "@/store/module/ckchat";
|
import { getChatSessions } from "@/store/module/ckchat";
|
||||||
|
|
||||||
interface SidebarMenuProps {
|
interface SidebarMenuProps {
|
||||||
contacts: ContractData[];
|
contracts: ContractData[];
|
||||||
currentChat: ContractData;
|
currentChat: ContractData;
|
||||||
onContactClick: (contact: ContractData) => void;
|
onContactClick: (contract: ContractData) => void;
|
||||||
onChatSelect: (chat: ContractData) => void;
|
onChatSelect: (chat: ContractData) => void;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
||||||
contacts,
|
contracts,
|
||||||
currentChat,
|
currentChat,
|
||||||
onContactClick,
|
onContactClick,
|
||||||
onChatSelect,
|
onChatSelect,
|
||||||
@@ -31,18 +31,18 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
|||||||
const chatSessions = getChatSessions();
|
const chatSessions = getChatSessions();
|
||||||
|
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
const [activeTab, setActiveTab] = useState("contacts");
|
const [activeTab, setActiveTab] = useState("contracts");
|
||||||
|
|
||||||
const handleSearch = (value: string) => {
|
const handleSearch = (value: string) => {
|
||||||
setSearchText(value);
|
setSearchText(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFilteredContacts = () => {
|
const getFilteredContacts = () => {
|
||||||
if (!searchText) return contacts;
|
if (!searchText) return contracts;
|
||||||
return contacts.filter(
|
return contracts.filter(
|
||||||
contact =>
|
contract =>
|
||||||
contact.nickname.toLowerCase().includes(searchText.toLowerCase()) ||
|
contract.nickname.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||||
contact.phone.includes(searchText),
|
contract.phone.includes(searchText),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
|||||||
<span>聊天</span>
|
<span>聊天</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`${styles.tabItem} ${activeTab === "contacts" ? styles.active : ""}`}
|
className={`${styles.tabItem} ${activeTab === "contracts" ? styles.active : ""}`}
|
||||||
onClick={() => setActiveTab("contacts")}
|
onClick={() => setActiveTab("contracts")}
|
||||||
>
|
>
|
||||||
<UserOutlined />
|
<UserOutlined />
|
||||||
<span>联系人</span>
|
<span>联系人</span>
|
||||||
@@ -105,10 +105,10 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({
|
|||||||
currentChat={currentChat}
|
currentChat={currentChat}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "contacts":
|
case "contracts":
|
||||||
return (
|
return (
|
||||||
<WechatFriendsModule
|
<WechatFriendsModule
|
||||||
contacts={getFilteredContacts()}
|
contracts={getFilteredContacts()}
|
||||||
onContactClick={onContactClick}
|
onContactClick={onContactClick}
|
||||||
selectedContactId={currentChat}
|
selectedContactId={currentChat}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const { Header, Content, Sider } = Layout;
|
|||||||
import { chatInitAPIdata } from "./main";
|
import { chatInitAPIdata } from "./main";
|
||||||
const CkboxPage: React.FC = () => {
|
const CkboxPage: React.FC = () => {
|
||||||
const [messageApi, contextHolder] = message.useMessage();
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
const [contacts, setContacts] = useState<any[]>([]);
|
const [contracts, setContacts] = useState<any[]>([]);
|
||||||
const [currentChat, setCurrentChat] = useState<ContractData | null>(null);
|
const [currentChat, setCurrentChat] = useState<ContractData | null>(null);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -21,10 +21,10 @@ const CkboxPage: React.FC = () => {
|
|||||||
// 方法一:使用 Promise 链式调用处理异步函数
|
// 方法一:使用 Promise 链式调用处理异步函数
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
chatInitAPIdata()
|
chatInitAPIdata()
|
||||||
.then((response: { contactList: any[]; chatRoomList: any[] }) => {
|
.then((response: { contractList: any[]; chatRoomList: any[] }) => {
|
||||||
const { contactList, chatRoomList } = response;
|
const { contractList, chatRoomList } = response;
|
||||||
//找出已经在聊天的
|
//找出已经在聊天的
|
||||||
const isChatList = contactList.filter(
|
const isChatList = contractList.filter(
|
||||||
v => (v?.config && v.config?.chat) || false,
|
v => (v?.config && v.config?.chat) || false,
|
||||||
);
|
);
|
||||||
isChatList.forEach(v => {
|
isChatList.forEach(v => {
|
||||||
@@ -41,9 +41,9 @@ const CkboxPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleContactClick = (contact: ContractData) => {
|
const handleContactClick = (contract: ContractData) => {
|
||||||
addChatSession(contact);
|
addChatSession(contract);
|
||||||
setCurrentChat(contact);
|
setCurrentChat(contract);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSendMessage = async (message: string) => {
|
const handleSendMessage = async (message: string) => {
|
||||||
@@ -74,7 +74,7 @@ const CkboxPage: React.FC = () => {
|
|||||||
{/* 左侧边栏 */}
|
{/* 左侧边栏 */}
|
||||||
<Sider width={280} className={styles.sider}>
|
<Sider width={280} className={styles.sider}>
|
||||||
<SidebarMenu
|
<SidebarMenu
|
||||||
contacts={contacts}
|
contracts={contracts}
|
||||||
currentChat={currentChat}
|
currentChat={currentChat}
|
||||||
onContactClick={handleContactClick}
|
onContactClick={handleContactClick}
|
||||||
onChatSelect={setCurrentChat}
|
onChatSelect={setCurrentChat}
|
||||||
@@ -101,7 +101,7 @@ const CkboxPage: React.FC = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<ChatWindow
|
<ChatWindow
|
||||||
contact={currentChat}
|
contract={currentChat}
|
||||||
onSendMessage={handleSendMessage}
|
onSendMessage={handleSendMessage}
|
||||||
showProfile={showProfile}
|
showProfile={showProfile}
|
||||||
onToggleProfile={() => setShowProfile(!showProfile)}
|
onToggleProfile={() => setShowProfile(!showProfile)}
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ export const chatInitAPIdata = async () => {
|
|||||||
seq: 1,
|
seq: 1,
|
||||||
});
|
});
|
||||||
//获取联系人列表
|
//获取联系人列表
|
||||||
const contactList = await getAllContactList();
|
const contractList = await getAllContactList();
|
||||||
//获取群列表
|
//获取群列表
|
||||||
const chatRoomList = await getAllChatRoomList();
|
const chatRoomList = await getAllChatRoomList();
|
||||||
return {
|
return {
|
||||||
contactList,
|
contractList,
|
||||||
chatRoomList,
|
chatRoomList,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -56,28 +56,28 @@ export const getAllContactList = async () => {
|
|||||||
|
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
console.log(`获取联系人列表,prevId: ${prevId}, count: ${count}`);
|
console.log(`获取联系人列表,prevId: ${prevId}, count: ${count}`);
|
||||||
const contactList = await getContactList({
|
const contractList = await getContactList({
|
||||||
prevId,
|
prevId,
|
||||||
count,
|
count,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!contactList ||
|
!contractList ||
|
||||||
!Array.isArray(contactList) ||
|
!Array.isArray(contractList) ||
|
||||||
contactList.length === 0
|
contractList.length === 0
|
||||||
) {
|
) {
|
||||||
hasMore = false;
|
hasMore = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
allContacts = [...allContacts, ...contactList];
|
allContacts = [...allContacts, ...contractList];
|
||||||
|
|
||||||
// 如果返回的数据少于请求的数量,说明已经没有更多数据了
|
// 如果返回的数据少于请求的数量,说明已经没有更多数据了
|
||||||
if (contactList.length < count) {
|
if (contractList.length < count) {
|
||||||
hasMore = false;
|
hasMore = false;
|
||||||
} else {
|
} else {
|
||||||
// 获取最后一条数据的id作为下一次请求的prevId
|
// 获取最后一条数据的id作为下一次请求的prevId
|
||||||
const lastContact = contactList[contactList.length - 1];
|
const lastContact = contractList[contractList.length - 1];
|
||||||
prevId = lastContact.id;
|
prevId = lastContact.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,28 +99,28 @@ export const getAllChatRoomList = async () => {
|
|||||||
|
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
console.log(`获取群列表,prevId: ${prevId}, count: ${count}`);
|
console.log(`获取群列表,prevId: ${prevId}, count: ${count}`);
|
||||||
const contactList = await getChatRoomList({
|
const contractList = await getChatRoomList({
|
||||||
prevId,
|
prevId,
|
||||||
count,
|
count,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!contactList ||
|
!contractList ||
|
||||||
!Array.isArray(contactList) ||
|
!Array.isArray(contractList) ||
|
||||||
contactList.length === 0
|
contractList.length === 0
|
||||||
) {
|
) {
|
||||||
hasMore = false;
|
hasMore = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
allContacts = [...allContacts, ...contactList];
|
allContacts = [...allContacts, ...contractList];
|
||||||
|
|
||||||
// 如果返回的数据少于请求的数量,说明已经没有更多数据了
|
// 如果返回的数据少于请求的数量,说明已经没有更多数据了
|
||||||
if (contactList.length < count) {
|
if (contractList.length < count) {
|
||||||
hasMore = false;
|
hasMore = false;
|
||||||
} else {
|
} else {
|
||||||
// 获取最后一条数据的id作为下一次请求的prevId
|
// 获取最后一条数据的id作为下一次请求的prevId
|
||||||
const lastContact = contactList[contactList.length - 1];
|
const lastContact = contractList[contractList.length - 1];
|
||||||
prevId = lastContact.id;
|
prevId = lastContact.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const routeGroups = {
|
|||||||
"/plans",
|
"/plans",
|
||||||
"/plans/:planId",
|
"/plans/:planId",
|
||||||
"/orders",
|
"/orders",
|
||||||
"/contact-import",
|
"/contract-import",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -126,7 +126,7 @@ export const routePermissions = {
|
|||||||
"/plans",
|
"/plans",
|
||||||
"/plans/:planId",
|
"/plans/:planId",
|
||||||
"/orders",
|
"/orders",
|
||||||
"/contact-import",
|
"/contract-import",
|
||||||
],
|
],
|
||||||
|
|
||||||
// 访客权限
|
// 访客权限
|
||||||
@@ -150,7 +150,7 @@ export const routeTitles: Record<string, string> = {
|
|||||||
"/profile": "个人中心",
|
"/profile": "个人中心",
|
||||||
"/plans": "计划管理",
|
"/plans": "计划管理",
|
||||||
"/orders": "订单管理",
|
"/orders": "订单管理",
|
||||||
"/contact-import": "联系人导入",
|
"/contract-import": "联系人导入",
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取路由标题
|
// 获取路由标题
|
||||||
|
|||||||
@@ -120,6 +120,11 @@ export const getCkTenantId = () => useCkChatStore.getState().getTenantId();
|
|||||||
export const getCkAccountName = () =>
|
export const getCkAccountName = () =>
|
||||||
useCkChatStore.getState().getAccountName();
|
useCkChatStore.getState().getAccountName();
|
||||||
export const getCkTenantName = () => useCkChatStore.getState().getTenantName();
|
export const getCkTenantName = () => useCkChatStore.getState().getTenantName();
|
||||||
export const getChatSessions = () => useCkChatStore.getState().chatSessions;
|
export const getChatSessions = () =>
|
||||||
|
useCkChatStore.getState().getChatSessions();
|
||||||
export const addChatSession = (session: ContractData | GroupData) =>
|
export const addChatSession = (session: ContractData | GroupData) =>
|
||||||
useCkChatStore.getState().addChatSession(session);
|
useCkChatStore.getState().addChatSession(session);
|
||||||
|
export const updateChatSession = (session: ContractData | GroupData) =>
|
||||||
|
useCkChatStore.getState().updateChatSession(session);
|
||||||
|
export const deleteChatSession = (sessionId: string) =>
|
||||||
|
useCkChatStore.getState().deleteChatSession(sessionId);
|
||||||
|
|||||||
Reference in New Issue
Block a user