From 424fc518ff3f8cc4985330ccef5dbae69653eb2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Tue, 23 Sep 2025 19:03:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=81=8A=E5=A4=A9=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E9=87=8D=E6=A7=8B=E8=88=87=E5=80=8B=E4=BA=BA=E8=B3=87=E6=96=99?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E5=84=AA=E5=8C=96):=20=E5=88=AA=E9=99=A4?= =?UTF-8?q?=E5=86=97=E9=A4=98=E7=9A=84ChatWindow=E7=B5=84=E4=BB=B6?= =?UTF-8?q?=E4=BB=A3=E7=A2=BC=EF=BC=8C=E9=87=8D=E6=A7=8BProfileCard?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E9=81=B8=E9=A0=85=E5=8D=A1=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E4=B8=A6=E6=95=B4=E5=90=88=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E8=AA=9E=E9=8C=84=E5=8F=8A=E6=9C=8B=E5=8F=8B=E5=9C=88=E6=A8=A1?= =?UTF-8?q?=E5=A1=8A=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8=E6=88=B6=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E5=8F=8B=E5=A5=BD=E6=80=A7=E5=92=8C=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ProfileCard/Person.module.scss | 237 --- .../components/FriendsCicle/api.ts | 106 ++ .../components/FriendsCicle/components/api.ts | 107 ++ .../FriendsCicle/components/friendCard.tsx | 320 ++++ .../components/FriendsCicle/index.data.ts | 71 + .../components/FriendsCicle/index.func.ts | 0 .../components/FriendsCicle/index.module.scss | 450 ++++++ .../components/FriendsCicle/index.tsx | 111 ++ .../ProfileModules/Person.module.scss | 236 +++ .../components/ProfileModules/index.tsx | 1338 +++++++++++++++ .../components/QuickWords/index.tsx | 137 ++ .../components/ProfileCard/index.tsx | 1437 +---------------- .../weChat/components/ChatWindow/demo.tsx | 669 -------- 13 files changed, 2932 insertions(+), 2287 deletions(-) create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/api.ts create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/api.ts create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/friendCard.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.data.ts create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.func.ts create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.module.scss create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/Person.module.scss create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx create mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx delete mode 100644 Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/demo.tsx 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 1ef281537..66cf4b59e 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 @@ -3,241 +3,4 @@ border-left: 1px solid #e8e8e8; height: 100%; overflow-y: auto; - - .profileContainer { - padding: 16px; - height: 100%; - display: flex; - flex-direction: column; - } - - .profileHeader { - display: flex; - justify-content: flex-end; - margin-bottom: 16px; - - .closeButton { - color: #8c8c8c; - - &:hover { - color: #262626; - background: #f5f5f5; - } - } - } - - .profileBasic { - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 24px; - padding-bottom: 24px; - border-bottom: 1px solid #f0f0f0; - - .profileInfo { - margin-top: 16px; - text-align: center; - width: 100%; - - .profileNickname { - margin: 0 0 8px 0; - font-size: 18px; - font-weight: 600; - color: #262626; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .profileRemark { - margin-bottom: 12px; - - .remarkText { - color: #8c8c8c; - font-size: 14px; - cursor: pointer; - - &:hover { - color: #1890ff; - } - } - } - - .profileStatus { - display: flex; - align-items: center; - justify-content: center; - gap: 6px; - color: #52c41a; - font-size: 14px; - - .statusDot { - width: 8px; - height: 8px; - border-radius: 50%; - background: #52c41a; - } - } - } - } - - .profileCard { - margin-bottom: 16px; - border-radius: 8px; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03); - - :global(.ant-card-head) { - padding: 0 16px; - min-height: 40px; - border-bottom: 1px solid #f0f0f0; - - :global(.ant-card-head-title) { - font-size: 14px; - font-weight: 500; - color: #262626; - } - } - - :global(.ant-card-body) { - padding: 16px; - } - - .infoItem { - display: flex; - align-items: center; - margin-bottom: 12px; - - &:last-child { - margin-bottom: 0; - } - - .infoIcon { - color: #8c8c8c; - margin-right: 8px; - width: 16px; - flex-shrink: 0; - } - - .infoLabel { - color: #8c8c8c; - font-size: 14px; - width: 60px; - flex-shrink: 0; - } - - .infoValue { - color: #262626; - font-size: 14px; - flex: 1; - word-break: break-all; - - // 备注编辑区域样式 - :global(.ant-input) { - font-size: 14px; - } - - :global(.ant-btn) { - font-size: 12px; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - } - } - } - - .tagsContainer { - display: flex; - flex-wrap: wrap; - gap: 8px; - - :global(.ant-tag) { - margin: 0; - border-radius: 4px; - } - } - - .bioText { - margin: 0; - color: #595959; - font-size: 14px; - line-height: 1.6; - word-break: break-word; - } - } - - .groupManagement { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 16px; - } - - .groupMemberList { - .groupMember { - display: flex; - align-items: center; - margin-bottom: 12px; - - &:last-child { - margin-bottom: 0; - } - - span { - margin-left: 8px; - font-size: 14px; - color: #262626; - } - } - } - - .profileActions { - margin-top: auto; - padding-top: 16px; - - :global(.ant-btn) { - border-radius: 6px; - font-weight: 500; - } - } -} - -// 响应式设计 -@media (max-width: 768px) { - .profileSider { - width: 280px !important; - - .profileContainer { - padding: 12px; - } - - .profileBasic { - .profileInfo { - .profileNickname { - font-size: 16px; - } - } - } - - .profileCard { - :global(.ant-card-body) { - padding: 12px; - } - - .infoItem { - margin-bottom: 10px; - - .infoLabel { - width: 50px; - font-size: 13px; - } - - .infoValue { - font-size: 13px; - } - } - } - } } diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/api.ts new file mode 100644 index 000000000..d801999e7 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/api.ts @@ -0,0 +1,106 @@ +// 朋友圈相关的API接口 +import { useWebSocketStore } from "@/store/module/websocket/websocket"; + +// 朋友圈请求参数接口 +export interface FetchMomentParams { + wechatAccountId: number; + wechatFriendId?: number; + createTimeSec?: number; + prevSnsId?: number; + count?: number; + isTimeline?: boolean; + seq?: number; +} + +// 获取朋友圈数据 +export const fetchFriendsCircleData = async (params: FetchMomentParams) => { + const { sendCommand } = useWebSocketStore.getState(); + sendCommand("CmdFetchMoment", params); +}; + +// 点赞朋友圈 +export const likeMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentInteract", + momentInteractType: 1, + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + snsId: params.snsId, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentInteract", requestData); +}; + +// 取消点赞 +export const cancelLikeMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentCancelInteract", + optType: 1, + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + CommentId2: "", + CommentTime: 0, + snsId: params.snsId, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentCancelInteract", requestData); +}; + +// 评论朋友圈 +export const commentMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + sendWord: string; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentInteract", + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + snsId: params.snsId, + sendWord: params.sendWord, + momentInteractType: 2, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentInteract", requestData); +}; + +// 撤销评论 +export const cancelCommentMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + CommentTime: number; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentCancelInteract", + optType: 2, + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + CommentId2: "", + CommentTime: params.CommentTime, + snsId: params.snsId, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentCancelInteract", requestData); +}; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/api.ts b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/api.ts new file mode 100644 index 000000000..311b51007 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/api.ts @@ -0,0 +1,107 @@ +// 朋友圈相关的API接口 +import { useWebSocketStore } from "@/store/module/websocket/websocket"; + +// 朋友圈请求参数接口 +export interface FetchMomentParams { + wechatAccountId: number; + wechatFriendId?: number; + createTimeSec?: number; + prevSnsId?: number; + count?: number; + isTimeline?: boolean; + seq?: number; +} + +// 获取朋友圈数据 +export const fetchFriendsCircleData = async (params: FetchMomentParams) => { + const { sendCommand } = useWebSocketStore.getState(); + sendCommand("CmdFetchMoment", params); +}; + +// 点赞朋友圈 +export const likeMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentInteract", + momentInteractType: 1, + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + snsId: params.snsId, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentInteract", requestData); +}; + +// 取消点赞 +export const cancelLikeMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentCancelInteract", + optType: 1, + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + CommentId2: "", + CommentTime: 0, + snsId: params.snsId, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentCancelInteract", requestData); +}; + +// 评论朋友圈 +export const commentMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + sendWord: string; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentInteract", + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + snsId: params.snsId, + sendWord: params.sendWord, + momentInteractType: 2, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentInteract", requestData); +}; + +// 撤销评论 +export const cancelCommentMoment = async (params: { + wechatAccountId: number; + wechatFriendId?: number; + snsId: string; + commentTime: number; + commentId2: number; + seq?: number; +}) => { + const { sendCommand } = useWebSocketStore.getState(); + const requestData = { + cmdType: "CmdMomentCancelInteract", + optType: 2, + wechatAccountId: params.wechatAccountId, + wechatFriendId: params.wechatFriendId || 0, + CommentId2: params.commentId2, + CommentTime: params.commentTime, + snsId: params.snsId, + seq: params.seq || Date.now(), + }; + + sendCommand("CmdMomentCancelInteract", requestData); +}; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/friendCard.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/friendCard.tsx new file mode 100644 index 000000000..ada75633e --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/components/friendCard.tsx @@ -0,0 +1,320 @@ +import React, { useState } from "react"; +import { Avatar, Button, Image, Spin, Input, message } from "antd"; +import { + HeartOutlined, + MessageOutlined, + LoadingOutlined, + SendOutlined, + DeleteOutlined, +} from "@ant-design/icons"; +import { + CommentItem, + likeListItem, + FriendCardProps, + MomentListProps, + FriendsCircleItem, +} from "@/pages/pc/ckbox/weChat/components/SidebarMenu/FriendsCicle/index.data"; +import styles from "../index.module.scss"; +import { + likeMoment, + cancelLikeMoment, + commentMoment, + cancelCommentMoment, +} from "./api"; +import { comfirm } from "@/utils/common"; + +import { useWeChatStore } from "@/store/module/weChat/weChat"; +// 单个朋友圈项目组件 +export const FriendCard: React.FC = ({ + monent, + isNotMy = false, + currentKf, + wechatFriendId, + formatTime, +}) => { + const content = monent?.momentEntity?.content || ""; + const images = monent?.momentEntity?.resUrls || []; + const time = formatTime(monent.createTime); + const likesCount = monent?.likeList?.length || 0; + const commentsCount = monent?.commentList?.length || 0; + const { updateLikeMoment, updateComment } = useWeChatStore(); + + // 评论相关状态 + const [showCommentInput, setShowCommentInput] = useState(false); + const [commentText, setCommentText] = useState(""); + + const handleLike = (moment: FriendsCircleItem) => { + console.log(currentKf); + + //判断是否已经点赞了 + const isLiked = moment?.likeList?.some( + (item: likeListItem) => item.wechatId === currentKf?.wechatId, + ); + if (isLiked) { + cancelLikeMoment({ + wechatAccountId: currentKf?.id || 0, + wechatFriendId: wechatFriendId || 0, + snsId: moment.snsId, + seq: Date.now(), + }); + // 更新点赞 + updateLikeMoment( + moment.snsId, + moment.likeList.filter(v => v.wechatId !== currentKf?.wechatId), + ); + } else { + likeMoment({ + wechatAccountId: currentKf?.id || 0, + wechatFriendId: wechatFriendId || 0, + snsId: moment.snsId, + seq: Date.now(), + }); + // 更新点赞 + updateLikeMoment(moment.snsId, [ + ...moment.likeList, + { + createTime: Date.now(), + nickName: currentKf?.nickname || "", + wechatId: currentKf?.wechatId || "", + }, + ]); + } + }; + + const handleSendComment = monent => { + if (!commentText.trim()) { + message.warning("请输入评论内容"); + return; + } + + // TODO: 调用发送评论的API + commentMoment({ + wechatAccountId: currentKf?.id || 0, + wechatFriendId: wechatFriendId || 0, + snsId: monent.snsId, + sendWord: commentText, + seq: Date.now(), + }); + // 更新评论 + updateComment(monent.snsId, [ + ...monent.commentList, + { + commentArg: 0, + commentId1: Date.now(), + commentId2: Date.now(), + commentTime: Date.now(), + content: commentText, + nickName: currentKf?.nickname || "", + wechatId: currentKf?.wechatId || "", + }, + ]); + // 清空输入框并隐藏 + setCommentText(""); + setShowCommentInput(false); + message.success("评论发送成功"); + }; + + const handleDeleteComment = (snsId: string, comment: CommentItem) => { + // TODO: 调用删除评论的API + comfirm("确定删除评论吗?").then(() => { + cancelCommentMoment({ + wechatAccountId: currentKf?.id || 0, + wechatFriendId: wechatFriendId || 0, + snsId: snsId, + seq: Date.now(), + commentId2: comment.commentId2, + commentTime: comment.commentTime, + }); + // 更新评论 + const commentList = monent.commentList.filter(v => { + return !( + v.commentId2 == comment.commentId2 && + v.wechatId == currentKf?.wechatId + ); + }); + updateComment(snsId, commentList); + message.success("评论删除成功"); + }); + }; + + return ( +
+ {isNotMy && ( +
+ +
+ )} +
+
+
+ {/*
{nickName}
*/} +
+
+ +
+
{content}
+ {images && images.length > 0 && ( +
+ {images.map((image, index) => ( + + ))} +
+ )} +
+ +
+
{time}
+
+ + +
+
+ + {/* 点赞和评论区域 */} + {(monent?.likeList?.length > 0 || monent?.commentList?.length > 0) && ( +
+ {/* 点赞列表 */} + {monent?.likeList?.length > 0 && ( +
+ + + {monent?.likeList?.map((like, index) => ( + + {like.nickName} + {index < (monent?.likeList?.length || 0) - 1 && "、"} + + ))} + +
+ )} + + {/* 评论列表 */} + {monent?.commentList?.length > 0 && ( +
+ {monent?.commentList?.map(comment => ( +
+ + {comment.nickName} + + : + + {comment.content} + +
+ ))} +
+ )} +
+ )} + + {/* 评论输入框 */} + {showCommentInput && ( +
+ setCommentText(e.target.value)} + placeholder="写评论..." + autoSize={{ minRows: 2, maxRows: 4 }} + className={styles.commentInput} + /> +
+ +
+
+ )} +
+
+ ); +}; + +// 朋友圈列表组件 +export const MomentList: React.FC = ({ + MomentCommon, + MomentCommonLoading, + formatTime, + currentKf, + loadMomentData, +}) => { + return ( +
+ {MomentCommon.length > 0 ? ( + <> + {MomentCommon.map((v, index) => ( +
+ +
+ ))} + {MomentCommonLoading && ( +
+ } /> 加载中... +
+ )} + {!MomentCommonLoading && ( +
+ +
+ )} + + ) : MomentCommonLoading ? ( +
+ } /> 加载中... +
+ ) : ( +

暂无我的朋友圈内容

+ )} +
+ ); +}; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.data.ts b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.data.ts new file mode 100644 index 000000000..d61c2870c --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.data.ts @@ -0,0 +1,71 @@ +// 评论数据类型 +export interface CommentItem { + commentArg: number; + commentId1: number; + commentId2: number; + commentTime: number; + content: string; + nickName: string; + wechatId: string; +} + +// 点赞数据类型 +export interface LikeItem { + createTime: number; + nickName: string; + wechatId: string; +} + +// 朋友圈实体数据类型 +export interface MomentEntity { + content: string; + createTime: number; + lat: number; + lng: number; + location: string; + objectType: number; + picSize: number; + resUrls: string[]; + snsId: string; + urls: string[]; + userName: string; +} + +// 朋友圈数据类型定义 +export interface FriendsCircleItem { + commentList: CommentItem[]; + createTime: number; + likeList: LikeItem[]; + momentEntity: MomentEntity; + snsId: string; + type: number; +} + +// API响应类型 +export interface ApiResponse { + list: FriendsCircleItem[]; + total: number; + hasMore: boolean; +} + +export interface FriendCardProps { + monent: FriendsCircleItem; + isNotMy?: boolean; + currentKf?: any; + wechatFriendId?: number; + formatTime: (time: number) => string; +} + +export interface MomentListProps { + MomentCommon: FriendsCircleItem[]; + MomentCommonLoading: boolean; + currentKf?: any; + wechatFriendId?: number; + formatTime: (time: number) => string; + loadMomentData: (loadMore: boolean) => void; +} +export interface likeListItem { + createTime: number; + nickName: string; + wechatId: string; +} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.func.ts b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.func.ts new file mode 100644 index 000000000..e69de29bb diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.module.scss new file mode 100644 index 000000000..1c39f04c9 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.module.scss @@ -0,0 +1,450 @@ +/* ===== 组件根容器 ===== */ +.friendsCircle { + height: 100%; + overflow-y: auto; + padding: 0; + background-color: #f5f5f5; + + /* 滚动条样式 */ + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; + } + + &::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; + + &:hover { + background: #a8a8a8; + } + } + + /* ===== 折叠面板样式 ===== */ + .collapseContainer { + margin-bottom: 1px; + + :global(.ant-collapse-item) { + border-bottom: 1px solid #e8e8e8; + + &:last-child { + border-bottom: 1px solid #e8e8e8; + } + } + + :global(.ant-collapse-header) { + padding: 12px 16px !important; + background-color: #ffffff; + + &:hover { + background-color: #f8f8f8; + } + } + + :global(.ant-collapse-content-box) { + padding: 16px; + background-color: #ffffff; + } + + /* 折叠面板头部 */ + .collapseHeader { + display: flex; + align-items: center; + gap: 6px; + .avatar { + width: 20px; + height: 20px; + border-radius: 5px; + } + /* 特殊头像样式 */ + .specialAvatar { + background-color: #1890ff; + } + + /* 群组头像样式 */ + .groupAvatars { + display: flex; + position: relative; + width: 32px; + height: 32px; + + .groupAvatar { + position: absolute; + border: 1px solid #fff; + background-color: #52c41a; + + &:nth-child(1) { + top: 0; + left: 0; + z-index: 4; + } + + &:nth-child(2) { + top: 0; + right: 0; + z-index: 3; + } + + &:nth-child(3) { + bottom: 0; + left: 0; + z-index: 2; + } + + &:nth-child(4) { + bottom: 0; + right: 0; + z-index: 1; + } + } + } + + /* 特殊文本样式 */ + .specialText { + font-size: 16px; + color: #333; + font-weight: 400; + } + } + } + + /* ===== 内容区域样式 ===== */ + .myCircleContent, + .squareContent { + padding: 0; + + /* 项目包装器 */ + .itemWrapper { + margin-bottom: 1px; + + &:last-child { + margin-bottom: 0; + } + + /* ===== 朋友圈项目样式 ===== */ + .circleItem { + background-color: #ffffff; + margin-bottom: 20px; + display: flex; + /* 头像样式 */ + .avatar { + margin-right: 10px; + } + /* 项目头部 */ + .itemHeader { + display: flex; + align-items: flex-start; + gap: 12px; + margin-bottom: 12px; + + /* 用户信息 */ + .userInfo { + flex: 1; + + .username { + font-size: 14px; + font-weight: 500; + color: #333; + line-height: 1.4; + } + } + } + + /* 项目内容 */ + .itemContent { + margin-bottom: 12px; + font-size: 12px; + .contentText { + color: #333; + line-height: 1.6; + margin-bottom: 8px; + word-wrap: break-word; + } + + /* 图片容器 */ + .imageContainer { + margin: 8px 0; + &::after { + content: ""; + display: table; + clear: both; + } + .contentImage { + width: 60px; + height: 60px; + object-fit: cover; + border-radius: 4px; + margin-right: 8px; + margin-bottom: 8px; + cursor: pointer; + transition: transform 0.2s; + float: left; + + &:hover { + transform: scale(1.05); + } + } + } + + /* 蓝色链接 */ + .blueLink { + color: #1890ff; + cursor: pointer; + &:hover { + text-decoration: underline; + } + } + } + + /* 项目底部 */ + .itemFooter { + display: flex; + align-items: center; + justify-content: space-between; + + /* 时间信息 */ + .timeInfo { + font-size: 12px; + color: #999; + } + + /* 操作按钮区域 */ + .actions { + display: flex; + align-items: center; + gap: 8px; + + .actionButton { + padding: 4px 8px; + color: #666; + + &:hover { + color: #1890ff; + background-color: #f0f8ff; + } + + .anticon { + font-size: 14px; + } + } + } + } + + // 点赞和评论交互区域 + .interactionArea { + margin-top: 8px; + padding: 8px 12px; + background-color: #f7f7f7; + border-radius: 4px; + font-size: 13px; + + .likeArea { + display: flex; + align-items: center; + margin-bottom: 4px; + + .likeIcon { + color: #ff6b6b; + margin-right: 6px; + font-size: 12px; + } + + .likeList { + color: #576b95; + + span { + cursor: pointer; + + &:hover { + text-decoration: underline; + } + } + } + } + + .commentArea { + .commentItem { + margin-bottom: 2px; + + .commentUser { + color: #576b95; + cursor: pointer; + + &:hover { + text-decoration: underline; + } + } + + .commentSeparator { + color: #666; + } + + .commentContent { + color: #333; + flex: 1; + } + + .deleteCommentBtn { + opacity: 1; + transition: opacity 0.2s; + margin-left: 8px; + padding: 0; + height: auto; + min-width: auto; + display: inline-block; + } + } + } + } + // 评论输入框样式 + .commentInputArea { + background-color: #f8f9fa; + border-radius: 8px; + border: 1px solid #e9ecef; + + .commentInput { + border: none; + background: transparent; + resize: none; + font-size: 12px; + color: #333; + + &:focus { + box-shadow: none; + } + } + + .commentInputActions { + text-align: right; + padding-right: 10px; + padding-bottom: 10px; + .sendCommentBtn { + height: 28px; + font-size: 12px; + } + } + } + } + } + + /* 空状态样式 */ + .emptyText { + padding: 20px; + text-align: center; + color: #999; + font-size: 14px; + margin: 0; + } + + /* 加载更多样式 */ + .loadingMore { + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + color: #666; + font-size: 14px; + gap: 8px; + + .anticon { + margin-right: 4px; + } + } + } +} + +/* ===== 图片预览Modal样式 ===== */ +.imagePreviewModal { + :global(.ant-modal-content) { + background: transparent; + box-shadow: none; + } + + :global(.ant-modal-header) { + display: none; + } + + :global(.ant-modal-close) { + position: fixed; + top: 20px; + right: 20px; + z-index: 1001; + color: white; + font-size: 24px; + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } + } + + :global(.ant-modal-body) { + padding: 0; + } +} + +.previewContainer { + position: relative; + width: 100%; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.9); + + .previewImage { + max-width: 90vw; + max-height: 90vh; + object-fit: contain; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); + } + + .navButton { + position: absolute; + top: 50%; + transform: translateY(-50%); + background: rgba(0, 0, 0, 0.5); + border: none; + color: white; + font-size: 24px; + width: 50px; + height: 50px; + border-radius: 50%; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s; + z-index: 1000; + + &:hover { + background: rgba(0, 0, 0, 0.7); + transform: translateY(-50%) scale(1.1); + } + + &.prevButton { + left: 20px; + } + + &.nextButton { + right: 20px; + } + } + + .imageCounter { + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + background: rgba(0, 0, 0, 0.7); + color: white; + padding: 8px 16px; + border-radius: 20px; + font-size: 14px; + z-index: 1000; + } +} diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.tsx new file mode 100644 index 000000000..d24ea48ad --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/FriendsCicle/index.tsx @@ -0,0 +1,111 @@ +import React, { useState, useEffect } from "react"; +import { Collapse } from "antd"; +import { ChromeOutlined } from "@ant-design/icons"; +import { MomentList } from "./components/friendCard"; + +import dayjs from "dayjs"; +import styles from "./index.module.scss"; +import { fetchFriendsCircleData } from "./api"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; +import { useWeChatStore } from "@/store/module/weChat/weChat"; + +interface FriendsCircleProps { + wechatFriendId?: number; +} + +const FriendsCircle: React.FC = ({ wechatFriendId }) => { + const currentKf = useCkChatStore(state => + state.kfUserList.find(kf => kf.id === state.kfSelected), + ); + const { clearMomentCommon, updateMomentCommonLoading } = useWeChatStore(); + const MomentCommon = useWeChatStore(state => state.MomentCommon); + const MomentCommonLoading = useWeChatStore( + state => state.MomentCommonLoading, + ); + + // 页面重新渲染时重置MomentCommonLoading状态 + useEffect(() => { + updateMomentCommonLoading(false); + }, []); + + // 状态管理 + const [expandedKeys, setExpandedKeys] = useState([]); + + // 加载更多我的朋友圈 + const loadMomentData = async (loadMore: boolean = false) => { + updateMomentCommonLoading(true); + // 加载数据; + const requestData = { + cmdType: "CmdFetchMoment", + wechatAccountId: currentKf?.id || 0, + wechatFriendId: wechatFriendId || 0, + createTimeSec: Math.floor(dayjs().subtract(2, "month").valueOf() / 1000), + prevSnsId: loadMore + ? Number(MomentCommon[MomentCommon.length - 1]?.snsId) || 0 + : 0, + count: 10, + isTimeline: expandedKeys.includes("1"), + seq: Date.now(), + }; + await fetchFriendsCircleData(requestData); + }; + + // 处理折叠面板展开/收起 + const handleCollapseChange = (keys: string | string[]) => { + const keyArray = Array.isArray(keys) ? keys : [keys]; + setExpandedKeys(keyArray); + if (!MomentCommonLoading && keys.length > 0) { + clearMomentCommon(); + loadMomentData(false); + } + }; + + // 格式化时间戳 + const formatTime = (timestamp: number) => { + const date = new Date(timestamp * 1000); + return date.toLocaleString("zh-CN", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + }); + }; + + const collapseItems = [ + { + key: "1", + label: ( +
+ + 好友朋友圈 +
+ ), + children: ( + + ), + }, + ]; + + return ( +
+ {/* 可折叠的特殊模块,包含所有朋友圈数据 */} + +
+ ); +}; + +export default FriendsCircle; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/Person.module.scss b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/Person.module.scss new file mode 100644 index 000000000..0a061cc43 --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/Person.module.scss @@ -0,0 +1,236 @@ +.profileContainer { + padding: 16px; + height: 100%; + display: flex; + flex-direction: column; +} + +.profileHeader { + display: flex; + justify-content: flex-end; + margin-bottom: 16px; + + .closeButton { + color: #8c8c8c; + + &:hover { + color: #262626; + background: #f5f5f5; + } + } +} + +.profileBasic { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 24px; + padding-bottom: 24px; + border-bottom: 1px solid #f0f0f0; + + .profileInfo { + margin-top: 16px; + text-align: center; + width: 100%; + + .profileNickname { + margin: 0 0 8px 0; + font-size: 18px; + font-weight: 600; + color: #262626; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .profileRemark { + margin-bottom: 12px; + + .remarkText { + color: #8c8c8c; + font-size: 14px; + cursor: pointer; + + &:hover { + color: #1890ff; + } + } + } + + .profileStatus { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + color: #52c41a; + font-size: 14px; + + .statusDot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #52c41a; + } + } + } +} + +.profileCard { + margin-bottom: 16px; + border-radius: 8px; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03); + + :global(.ant-card-head) { + padding: 0 16px; + min-height: 40px; + border-bottom: 1px solid #f0f0f0; + + :global(.ant-card-head-title) { + font-size: 14px; + font-weight: 500; + color: #262626; + } + } + + :global(.ant-card-body) { + padding: 16px; + } + + .infoItem { + display: flex; + align-items: center; + margin-bottom: 12px; + + &:last-child { + margin-bottom: 0; + } + + .infoIcon { + color: #8c8c8c; + margin-right: 8px; + width: 16px; + flex-shrink: 0; + } + + .infoLabel { + color: #8c8c8c; + font-size: 14px; + width: 60px; + flex-shrink: 0; + } + + .infoValue { + color: #262626; + font-size: 14px; + flex: 1; + word-break: break-all; + + // 备注编辑区域样式 + :global(.ant-input) { + font-size: 14px; + } + + :global(.ant-btn) { + font-size: 12px; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + } + } + } + + .tagsContainer { + display: flex; + flex-wrap: wrap; + gap: 8px; + + :global(.ant-tag) { + margin: 0; + border-radius: 4px; + } + } + + .bioText { + margin: 0; + color: #595959; + font-size: 14px; + line-height: 1.6; + word-break: break-word; + } +} + +.groupManagement { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 16px; +} + +.groupMemberList { + .groupMember { + display: flex; + align-items: center; + margin-bottom: 12px; + + &:last-child { + margin-bottom: 0; + } + + span { + margin-left: 8px; + font-size: 14px; + color: #262626; + } + } +} + +.profileActions { + margin-top: auto; + padding-top: 16px; + + :global(.ant-btn) { + border-radius: 6px; + font-weight: 500; + } +} + +// 响应式设计 +@media (max-width: 768px) { + .profileSider { + width: 280px !important; + + .profileContainer { + padding: 12px; + } + + .profileBasic { + .profileInfo { + .profileNickname { + font-size: 16px; + } + } + } + + .profileCard { + :global(.ant-card-body) { + padding: 12px; + } + + .infoItem { + margin-bottom: 10px; + + .infoLabel { + width: 50px; + font-size: 13px; + } + + .infoValue { + font-size: 13px; + } + } + } + } +} 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 new file mode 100644 index 000000000..f354c3b3a --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/ProfileModules/index.tsx @@ -0,0 +1,1338 @@ +import React, { useState, useEffect } from "react"; +import { + Input, + Button, + Avatar, + Tooltip, + Card, + Tag, + message, + Modal, +} from "antd"; +import { + PhoneOutlined, + UserOutlined, + TeamOutlined, + EnvironmentOutlined, + CloseOutlined, + EditOutlined, + CheckOutlined, + PlusOutlined, + MinusOutlined, + SwapOutlined, +} from "@ant-design/icons"; +import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; +import { useCkChatStore } from "@/store/module/ckchat/ckchat"; +import { useWebSocketStore } from "@/store/module/websocket/websocket"; +import { useWeChatStore } from "@/store/module/weChat/weChat"; +import { generateAiText } from "@/api/ai"; +import TwoColumnSelection from "@/components/TwoColumnSelection/TwoColumnSelection"; +import TwoColumnMemberSelection from "@/components/MemberSelection/TwoColumnMemberSelection"; +import { FriendSelectionItem } from "@/components/FriendSelection/data"; +import styles from "./Person.module.scss"; +interface PersonProps { + contract: ContractData | weChatGroup; + onToggleProfile?: () => void; +} + +const Person: React.FC = ({ contract, onToggleProfile }) => { + const [messageApi, contextHolder] = message.useMessage(); + const [isEditingRemark, setIsEditingRemark] = useState(false); + const [remarkValue, setRemarkValue] = useState(contract.conRemark || ""); + const [selectedTags, setSelectedTags] = useState( + contract.labels || [], + ); + const [allAvailableTags, setAllAvailableTags] = useState([]); + const [isAddingTag, setIsAddingTag] = useState(false); + const [newTagValue, setNewTagValue] = useState(""); + + // 判断是否为群聊 + const isGroup = "chatroomId" in contract; + + // 群聊相关状态 + const [isEditingGroupName, setIsEditingGroupName] = useState(false); + const [groupNameValue, setGroupNameValue] = useState(contract.name || ""); + const [groupNoticeValue, setGroupNoticeValue] = useState( + contract.notice || "", + ); + const [isEditingSelfDisplayName, setIsEditingSelfDisplayName] = + useState(false); + const [selfDisplayNameValue, setSelfDisplayNameValue] = useState( + contract.selfDisplyName || "", + ); + const [isGroupNoticeModalVisible, setIsGroupNoticeModalVisible] = + useState(false); + const [confirmLoading, setConfirmLoading] = useState(false); + const [aiGenerating, setAiGenerating] = useState(false); + const [isAiModalVisible, setIsAiModalVisible] = useState(false); + const [aiPrompt, setAiPrompt] = useState( + `请为我们的微信群生成一个友好、专业的群公告。 + +群公告应该包含: +1. 欢迎新成员加入 +2. 群聊的基本规则和礼仪 +3. 鼓励大家积极交流 +4. 保持群聊环境和谐 + +请用温馨友好的语调,字数控制在200字以内。`, + ); + const [aiGeneratedContent, setAiGeneratedContent] = useState(""); + + const currentGroupMembers = useWeChatStore( + state => state.currentGroupMembers, + ); + + const [hoveredMember, setHoveredMember] = useState(null); + const [isAddFriendModalVisible, setIsAddFriendModalVisible] = useState(false); + const [selectedMember, setSelectedMember] = useState(null); + const [greeting, setGreeting] = useState(""); + + // 群管理弹窗状态 + const [isFriendSelectionVisible, setIsFriendSelectionVisible] = + useState(false); + const [isMemberSelectionVisible, setIsMemberSelectionVisible] = + useState(false); + const [isAdminSelectionVisible, setIsAdminSelectionVisible] = useState(false); + const [isRemoveAdminSelectionVisible, setIsRemoveAdminSelectionVisible] = + useState(false); + const [isTransferOwnerSelectionVisible, setIsTransferOwnerSelectionVisible] = + useState(false); + const [selectedFriends, setSelectedFriends] = useState( + [], + ); + const [contractList, setContractList] = useState([]); + + const handleAddFriend = member => { + setSelectedMember(member); + setGreeting(`你好, 我来自群聊${contractInfo.name}`); + setIsAddFriendModalVisible(true); + }; + + // 群管理操作处理函数 + const handleAddMember = ( + selectedIds: number[], + selectedItems: FriendSelectionItem[], + ) => { + console.log("添加成员:", selectedIds, selectedItems); + sendCommand("CmdChatroomInvite", { + wechatChatroomId: contract.id, + wechatFriendIds: selectedIds, + }); + messageApi.success(`已添加 ${selectedItems.length} 个成员`); + setIsFriendSelectionVisible(false); + }; + + //删除群成员 + const handleRemoveMember = (selectedIds: string[]) => { + console.log("删除成员:", selectedIds); + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 2, + extra: JSON.stringify({ + friendIdList: selectedIds, + }), + }); + messageApi.success(`已删除 ${selectedIds.length} 个成员`); + setIsMemberSelectionVisible(false); + }; + //添加管理员 + const handleAddAdmin = (selectedIds: string[]) => { + console.log("添加管理员:", selectedIds); + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 12, + extra: JSON.stringify({ + wechatIds: selectedIds, + }), + }); + messageApi.success(`已添加 ${selectedIds.length} 个管理员`); + setIsAdminSelectionVisible(false); + }; + + //删除管理员 + const handleRemoveAdmin = (selectedIds: string[]) => { + console.log("删除管理员:", selectedIds); + + selectedIds.forEach(wechatId => { + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 8, // 8 for remove admin + extra: JSON.stringify({ + wechatId: wechatId, + }), + }); + }); + + messageApi.success(`已删除 ${selectedIds.length} 个管理员`); + setIsRemoveAdminSelectionVisible(false); + }; + + //群主转让 √ + const handleTransferOwner = (selectedIds: string[]) => { + if (selectedIds.length !== 1) { + messageApi.error("只能选择一个成员作为新群主"); + return; + } + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 10, + extra: JSON.stringify({ + wechatId: selectedIds[0], + }), + }); + messageApi.success("群主转让成功"); + setIsTransferOwnerSelectionVisible(false); + }; + + const handleSendFriendRequest = () => { + if (!selectedMember) return; + + sendCommand("CmdChatroomOperate", { + chatroomOperateTyp: 1, + extra: JSON.stringify({ + wechatId: selectedMember.wechatId, + sendWord: greeting, + }), + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + }); + + messageApi.success("好友请求已发送"); + setIsAddFriendModalVisible(false); + setSelectedMember(null); + setGreeting(""); + }; + + // 构建联系人或群聊详细信息 + + const kfSelectedUser = useCkChatStore(state => + state.getKfUserInfo(contract.wechatAccountId || 0), + ); + + const getSomeContractList = useCkChatStore( + state => state.getSomeContractList, + ); + + const { sendCommand } = useWebSocketStore(); + + // 权限控制:检查当前客服是否有群管理权限 + const hasGroupManagePermission = () => { + 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])]; + setAllAvailableTags(allTags); + } catch (error) { + console.error("获取标签失败:", error); + } + }; + fetchAvailableTags(); + }, [kfSelectedUser, contract.labels]); + + // 当contract变化时更新各种值 + useEffect(() => { + setRemarkValue(contract.conRemark || ""); + setIsEditingRemark(false); + setSelectedTags(contract.labels || []); + + if (isGroup) { + setGroupNameValue(contract.name || ""); + setIsEditingGroupName(false); + setGroupNoticeValue(contract.notice || ""); + setSelfDisplayNameValue(contract.selfDisplyName || ""); + setIsEditingSelfDisplayName(false); + } + }, [ + contract.conRemark, + contract.labels, + contract.name, + contract.notice, + contract.selfDisplyName, + isGroup, + ]); + + // 处理备注保存 + const handleSaveRemark = () => { + if (isGroup) { + // 群聊备注修改 + sendCommand("CmdModifyGroupRemark", { + wechatAccountId: contract.wechatAccountId, + chatroomId: contract.chatroomId, + newRemark: remarkValue, + }); + } else { + // 好友备注修改 + sendCommand("CmdModifyFriendRemark", { + wechatAccountId: contract.wechatAccountId, + wechatFriendId: contract.id, + newRemark: remarkValue, + }); + } + + messageApi.success("备注保存成功"); + setIsEditingRemark(false); + // 更新contract对象中的备注(实际项目中应该通过props回调或状态管理) + }; + + // 处理群名称保存 + const handleSaveGroupName = () => { + if (!hasGroupManagePermission()) { + messageApi.error("只有群主才能修改群名称"); + return; + } + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 6, + extra: `{"chatroomName":"${groupNameValue}"}`, + }); + + messageApi.success("群名称修改成功"); + setIsEditingGroupName(false); + }; + + // 点击编辑群名称按钮 + const handleEditGroupName = () => { + if (!hasGroupManagePermission()) { + messageApi.error("只有群主才能修改群名称"); + return; + } + setGroupNameValue(contractInfo.name || ""); + setIsEditingGroupName(true); + }; + + // 处理群公告保存 + const handleSaveGroupNotice = () => { + if (!hasGroupManagePermission()) { + messageApi.error("只有群主才能修改群公告"); + return; + } + setConfirmLoading(true); + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 5, + extra: `{"announce":"${groupNoticeValue}"}`, + }); + + // 模拟延迟 + setTimeout(() => { + messageApi.success("群公告修改成功"); + setIsGroupNoticeModalVisible(false); + setConfirmLoading(false); + }, 1000); + }; + + // 打开AI编写弹框 + const handleOpenAiModal = () => { + if (!hasGroupManagePermission()) { + messageApi.error("只有群主才能使用AI编写功能"); + return; + } + setIsAiModalVisible(true); + setAiGeneratedContent(""); // 清空之前生成的内容 + }; + + // 处理AI生成群公告 + const handleAiGenerateNotice = async () => { + setAiGenerating(true); + try { + // 调用AI接口生成群公告 + const aiResponse = await generateAiText(aiPrompt, { + wechatAccountId: contract.wechatAccountId, + groupId: contract.id, + }); + + setAiGeneratedContent(aiResponse); + messageApi.success("AI生成群公告成功!"); + } catch (error: any) { + console.error("AI生成失败:", error); + messageApi.error(error.message || "AI生成失败,请重试"); + } finally { + setAiGenerating(false); + } + }; + + // 确认使用AI生成的内容 + const handleConfirmAiContent = () => { + setGroupNoticeValue(aiGeneratedContent); + setIsAiModalVisible(false); + messageApi.success("已应用AI生成的群公告内容"); + }; + + // 点击编辑群公告按钮 + const handleEditGroupNotice = () => { + if (!hasGroupManagePermission()) { + messageApi.error("只有群主才能修改群公告"); + return; + } + setGroupNoticeValue(contract.notice || ""); + setIsGroupNoticeModalVisible(true); + }; + + // 处理我在本群中的昵称保存 + const handleSaveSelfDisplayName = () => { + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 8, + extra: `${selfDisplayNameValue}`, + }); + + messageApi.success("群昵称修改成功"); + setIsEditingSelfDisplayName(false); + }; + + // 点击编辑群昵称按钮 + const handleEditSelfDisplayName = () => { + setSelfDisplayNameValue(contract.selfDisplyName || ""); + setIsEditingSelfDisplayName(true); + }; + + // 处理取消编辑 + const handleCancelEdit = () => { + setRemarkValue(contract.conRemark || ""); + setIsEditingRemark(false); + }; + + // 处理标签点击切换 + const handleTagToggle = (tagName: string) => { + const newSelectedTags = selectedTags.includes(tagName) + ? selectedTags.filter(tag => tag !== tagName) + : [...selectedTags, tagName]; + + setSelectedTags(newSelectedTags); + + // 使用WebSocket发送修改标签命令 + if (isGroup) { + // 群聊标签修改 + sendCommand("CmdModifyGroupLabel", { + labels: newSelectedTags, + seq: +new Date(), + wechatAccountId: contract.wechatAccountId, + chatroomId: contract.chatroomId, + }); + } else { + // 好友标签修改 + sendCommand("CmdModifyFriendLabel", { + labels: newSelectedTags, + seq: +new Date(), + wechatAccountId: contract.wechatAccountId, + wechatFriendId: contract.id, + }); + } + + messageApi.success( + `标签"${tagName}"${selectedTags.includes(tagName) ? "已取消" : "已选中"}`, + ); + }; + + // 处理新增标签 + const handleAddTag = () => { + if (!newTagValue.trim()) { + messageApi.error("请输入标签名称"); + return; + } + + if (allAvailableTags.includes(newTagValue.trim())) { + messageApi.error("标签已存在"); + return; + } + + const newTag = newTagValue.trim(); + + // 添加到可用标签列表 + setAllAvailableTags(prev => [...prev, newTag]); + + // 自动选中新添加的标签 + setSelectedTags(prev => [...prev, newTag]); + + // 使用WebSocket发送新增标签命令 + if (isGroup) { + // 群聊标签修改 + sendCommand("CmdModifyGroupLabel", { + labels: [...selectedTags, newTag], + seq: +new Date(), + wechatAccountId: contract.wechatAccountId, + chatroomId: contract.chatroomId, + }); + } else { + // 好友标签修改 + sendCommand("CmdModifyFriendLabel", { + labels: [...selectedTags, newTag], + seq: +new Date(), + wechatAccountId: contract.wechatAccountId, + wechatFriendId: contract.id || contract.wechatId, + }); + } + + messageApi.success(`标签"${newTag}"添加成功`); + setNewTagValue(""); + setIsAddingTag(false); + }; + + // 处理取消新增标签 + const handleCancelAddTag = () => { + setNewTagValue(""); + setIsAddingTag(false); + }; + + // 处理退出群聊 + const handleLeaveGroup = () => { + Modal.confirm({ + title: "确定要退出群聊吗?", + content: "退出后将不再接收此群聊消息。", + okText: "确定", + cancelText: "取消", + onOk: () => { + sendCommand("CmdChatroomOperate", { + wechatAccountId: contract.wechatAccountId, + wechatChatroomId: contract.id, + chatroomOperateType: 4, // 4 for quit + }); + messageApi.success("已退出群聊"); + // 可能还需要一个回调来关闭侧边栏或切换到另一个聊天 + if (onToggleProfile) { + onToggleProfile(); + } + }, + }); + }; + + // 构建联系人或群聊详细信息 + const contractInfo = { + name: contract.name || contract.nickname, + nickname: contract.nickname, + conRemark: remarkValue, // 使用当前编辑的备注值 + alias: contract.alias, + wechatId: contract.wechatId, + chatroomId: isGroup ? contract.chatroomId : undefined, + chatroomOwner: isGroup ? contract.chatroomOwner : undefined, + avatar: contract.avatar || contract.chatroomAvatar, + phone: contract.phone || "-", + email: contract.email || "-", + department: contract.department || "-", + position: contract.position || "-", + company: contract.company || "-", + region: contract.region || "-", + joinDate: contract.joinDate || "-", + notice: isGroup ? contract.notice : undefined, + selfDisplyName: isGroup ? contract.selfDisplyName : undefined, + status: "在线", + tags: selectedTags, + bio: contract.bio || contract.signature || "-", + }; + + return ( + <> + {contextHolder} +
+ {/* 头像和基本信息 */} +
+ } /> +
+
+ + {contractInfo.status} +
+ +

+ {contractInfo.nickname || contractInfo.name} +

+
+
+
+ + {/* 详细信息卡片 */} + + {isGroup ? ( + // 群聊信息 + <> +
+ + 群名称: +
+ {isEditingGroupName ? ( +
+ setGroupNameValue(e.target.value)} + placeholder="请输入群名称" + size="small" + style={{ flex: 1 }} + /> +
+ ) : ( +
+ +

+ {contractInfo.nickname || contractInfo.name} +

+
+ {hasGroupManagePermission() && ( +
+ )} +
+
+
+ + 群ID: + + {contractInfo.chatroomId} + +
+
+ + 群主: + + {contractInfo.chatroomOwner} + +
+
+ + 群昵称: +
+ {isEditingSelfDisplayName ? ( +
+ setSelfDisplayNameValue(e.target.value)} + placeholder="请输入群昵称" + size="small" + style={{ flex: 1 }} + /> +
+ ) : ( +
+ + {contractInfo.selfDisplyName || "点击添加群昵称"} + +
+ )} +
+
+ + ) : ( + // 好友信息 + <> +
+ + 微信号: + + {contractInfo.alias || contractInfo.wechatId} + +
+
+ + 电话: + {contractInfo.phone} +
+
+ + 地区: + {contractInfo.region} +
+ + )} + {!isGroup && ( +
+ + 备注: +
+ {isEditingRemark ? ( +
+ setRemarkValue(e.target.value)} + placeholder="请输入备注" + size="small" + style={{ flex: 1 }} + /> +
+ ) : ( +
+ {contractInfo.conRemark || "点击添加备注"} +
+ )} +
+
+ )} +
+ + {/* 标签 - 仅在非群聊时显示 */} + {!isGroup && ( + +
+ {/* 渲染所有可用标签,选中的排在前面 */} + {[...new Set([...selectedTags, ...allAvailableTags])].map( + (tag, index) => { + const isSelected = selectedTags.includes(tag); + return ( + handleTagToggle(tag)} + > + {tag} + + ); + }, + )} + + {/* 新增标签区域 */} + {isAddingTag ? ( +
+ setNewTagValue(e.target.value)} + placeholder="请输入标签名称" + size="small" + style={{ width: "120px" }} + onPressEnter={handleAddTag} + /> +
+ ) : ( + setIsAddingTag(true)} + > + 新增标签 + + )} + + {allAvailableTags.length === 0 && !isAddingTag && ( + + 暂无可用标签 + + )} +
+
+ )} + + {/* 群公告 - 仅在群聊时显示 */} + {isGroup && ( + + {/* 群聊简介(原群公告) */} +
{ + setGroupNoticeValue(contractInfo.notice || ""); + setIsGroupNoticeModalVisible(true); + } + : undefined + } + style={{ + cursor: hasGroupManagePermission() ? "pointer" : "default", + }} + > +
+
+ {contractInfo.notice || "点击添加群公告"} +
+ {hasGroupManagePermission() && ( +
+
+
+ )} + + {isGroup && ( + +
+
+ + {hasGroupManagePermission() && ( + + )} +
+ {hasGroupManagePermission() && ( +
+ + + +
+ )} +
+
+ {currentGroupMembers.map((member, index) => ( + +
setHoveredMember(member.wechatId)} + onMouseLeave={() => setHoveredMember(null)} + style={{ + display: "flex", + alignItems: "center", + width: "100%", + }} + > + + + {member.nickname} + + {hoveredMember === member.wechatId && ( +
+
+ ))} +
+ + +
+ )} + + setIsAddFriendModalVisible(false)} + okText="确定" + cancelText="取消" + > + setGreeting(e.target.value)} + placeholder="请输入招呼语" + rows={4} + /> + +
+ + {/* 群公告编辑弹窗 */} + setIsGroupNoticeModalVisible(false)} + footer={[ + , + hasGroupManagePermission() && ( + + ), + , + ].filter(Boolean)} + > + {!hasGroupManagePermission() && ( +
+ ⚠️ 您不是群主,无法修改群公告 +
+ )} +
+ setGroupNoticeValue(e.target.value)} + placeholder={ + hasGroupManagePermission() + ? "请输入群公告内容,或点击AI编写按钮自动生成" + : "仅群主可以修改群公告" + } + rows={8} + style={{ + resize: "none", + whiteSpace: "pre-wrap", + wordBreak: "break-word", + lineHeight: "1.5", + }} + disabled={!hasGroupManagePermission()} + /> +
+
+ 💡 提示: + {hasGroupManagePermission() + ? "AI编写功能将根据默认模板生成专业的群公告内容,您可以在生成后进行个性化修改。" + : "只有群主才能编辑群公告内容。"} +
+
+ + {/* AI编写群公告弹框 */} + setIsAiModalVisible(false)} + width={800} + footer={[ + , + , + , + ]} + > +
+ {/* 提示词区域 */} +
+
+ 📝 AI提示词 +
+ setAiPrompt(e.target.value)} + placeholder="请输入AI生成群公告的提示词..." + rows={6} + style={{ resize: "none" }} + /> +
+ 💡 提示:详细的提示词能帮助AI生成更符合您需求的群公告内容 +
+
+ + {/* 生成内容区域 */} +
+
+ 🤖 AI生成的群公告 +
+ setAiGeneratedContent(e.target.value)} + placeholder={ + aiGenerating + ? "AI正在生成中,请稍候..." + : "点击上方'生成内容'按钮,AI将根据提示词生成群公告" + } + rows={8} + style={{ resize: "none" }} + disabled={aiGenerating} + /> + {aiGeneratedContent && ( +
+ ✅ 内容已生成,您可以编辑后点击“确认使用”应用到群公告 +
+ )} +
+
+
+ + {/* 群管理弹窗组件 */} + {/* 添加成员弹窗 */} + setIsFriendSelectionVisible(false)} + onConfirm={(selectedIds, selectedItems) => { + setSelectedFriends(selectedItems); + handleAddMember( + selectedIds.map(id => parseInt(id)), + selectedItems, + ); + }} + dataSource={contractList} + title="添加群成员" + /> + + {/* 删除成员弹窗 */} + ({ + id: member.wechatId, + nickname: member.nickname, + avatar: member.avatar, + }))} + onCancel={() => setIsMemberSelectionVisible(false)} + onConfirm={handleRemoveMember} + title="删除群成员" + allowMultiple={false} + /> + + {/* 添加管理员弹窗 */} + ({ + id: member.wechatId, + nickname: member.nickname, + avatar: member.avatar, + }))} + onCancel={() => setIsAdminSelectionVisible(false)} + onConfirm={handleAddAdmin} + title="添加管理员" + allowMultiple={false} + /> + + {/* 删除管理员弹窗 */} + ({ + id: member.wechatId, + nickname: member.nickname, + avatar: member.avatar, + }))} + onCancel={() => setIsRemoveAdminSelectionVisible(false)} + onConfirm={handleRemoveAdmin} + title="删除管理员" + allowMultiple={false} + /> + + {/* 转让群主弹窗 */} + ({ + id: member.wechatId, + nickname: member.nickname, + avatar: member.avatar, + }))} + onCancel={() => setIsTransferOwnerSelectionVisible(false)} + onConfirm={handleTransferOwner} + title="转让群主" + allowMultiple={false} + /> + + ); +}; + +export default Person; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx new file mode 100644 index 000000000..915dc873a --- /dev/null +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/components/QuickWords/index.tsx @@ -0,0 +1,137 @@ +import React, { useMemo, useState } from "react"; +import { Card, Input, Button, Space, List, Tag } from "antd"; + +export interface QuickWordItem { + id: string | number; + text?: string; // 兼容旧结构 + title?: string; + content?: string; + tag?: string; // 分类/标签 + usageCount?: number; +} + +export interface QuickWordsProps { + title?: string; + words: QuickWordItem[]; + onInsert?: (text: string) => void; + onAdd?: (text: string) => void; + onRemove?: (id: string | number) => void; +} + +const QuickWords: React.FC = ({ + title = "快捷语录", + words, + onInsert, + + onRemove, +}) => { + const [keyword, setKeyword] = useState(""); + const sorted = useMemo( + () => + [...(words || [])].sort((a, b) => + String(a.id).localeCompare(String(b.id)), + ), + [words], + ); + + return ( + + + setKeyword(e.target.value)} + onSearch={v => setKeyword(v)} + /> + + { + const text = `${item.title || ""}${item.content || ""}${item.text || ""}`; + return text.toLowerCase().includes(keyword.trim().toLowerCase()); + })} + renderItem={item => { + const displayTitle = item.title || item.text || "未命名"; + const displayContent = item.content || item.text || ""; + return ( + +
+
+
+ {item.tag && {item.tag}} + + {displayTitle} + +
+
+ {displayContent} +
+ {typeof item.usageCount === "number" && ( +
+ 使用 {item.usageCount} 次 +
+ )} +
+
+ {onRemove && ( + + )} + +
+
+
+ ); + }} + /> +
+
+ ); +}; + +export default QuickWords; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/index.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/index.tsx index 9bcc610df..56e2ccf34 100644 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/index.tsx +++ b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/components/ProfileCard/index.tsx @@ -1,1399 +1,74 @@ -import React, { useState, useEffect } from "react"; -import { - Layout, - Input, - Button, - Avatar, - Tooltip, - Card, - Tag, - message, - Modal, -} from "antd"; -import { - PhoneOutlined, - UserOutlined, - TeamOutlined, - EnvironmentOutlined, - CloseOutlined, - EditOutlined, - CheckOutlined, - PlusOutlined, - MinusOutlined, - SwapOutlined, -} from "@ant-design/icons"; +import React, { useState } from "react"; +import { Layout, Tabs } from "antd"; import { ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; -import { useCkChatStore } from "@/store/module/ckchat/ckchat"; -import { useWebSocketStore } from "@/store/module/websocket/websocket"; import styles from "./Person.module.scss"; -import { useWeChatStore } from "@/store/module/weChat/weChat"; -import { generateAiText } from "@/api/ai"; -import TwoColumnSelection from "@/components/TwoColumnSelection/TwoColumnSelection"; -import TwoColumnMemberSelection from "@/components/MemberSelection/TwoColumnMemberSelection"; -import { FriendSelectionItem } from "@/components/FriendSelection/data"; +import ProfileModules from "./components/ProfileModules"; +import QuickWords from "./components/QuickWords"; +import FriendsCircle from "./components/FriendsCicle"; +import LayoutFiexd from "@/components/Layout/LayoutFiexd"; const { Sider } = Layout; interface PersonProps { contract: ContractData | weChatGroup; - showProfile: boolean; onToggleProfile?: () => void; } -const Person: React.FC = ({ - contract, - showProfile, - onToggleProfile, -}) => { - const [messageApi, contextHolder] = message.useMessage(); - const [isEditingRemark, setIsEditingRemark] = useState(false); - const [remarkValue, setRemarkValue] = useState(contract.conRemark || ""); - const [selectedTags, setSelectedTags] = useState( - contract.labels || [], - ); - const [allAvailableTags, setAllAvailableTags] = useState([]); - const [isAddingTag, setIsAddingTag] = useState(false); - const [newTagValue, setNewTagValue] = useState(""); - - // 判断是否为群聊 +const Person: React.FC = ({ contract, onToggleProfile }) => { + const [activeKey, setActiveKey] = useState("profile"); const isGroup = "chatroomId" in contract; - - // 群聊相关状态 - const [isEditingGroupName, setIsEditingGroupName] = useState(false); - const [groupNameValue, setGroupNameValue] = useState(contract.name || ""); - const [isEditingGroupNotice, setIsEditingGroupNotice] = useState(false); - const [groupNoticeValue, setGroupNoticeValue] = useState( - contract.notice || "", - ); - const [isEditingSelfDisplayName, setIsEditingSelfDisplayName] = - useState(false); - const [selfDisplayNameValue, setSelfDisplayNameValue] = useState( - contract.selfDisplyName || "", - ); - const [isGroupNoticeModalVisible, setIsGroupNoticeModalVisible] = - useState(false); - const [confirmLoading, setConfirmLoading] = useState(false); - const [aiGenerating, setAiGenerating] = useState(false); - const [isAiModalVisible, setIsAiModalVisible] = useState(false); - const [aiPrompt, setAiPrompt] = useState( - `请为我们的微信群生成一个友好、专业的群公告。 - -群公告应该包含: -1. 欢迎新成员加入 -2. 群聊的基本规则和礼仪 -3. 鼓励大家积极交流 -4. 保持群聊环境和谐 - -请用温馨友好的语调,字数控制在200字以内。`, - ); - const [aiGeneratedContent, setAiGeneratedContent] = useState(""); - - const currentGroupMembers = useWeChatStore( - state => state.currentGroupMembers, - ); - - const [hoveredMember, setHoveredMember] = useState(null); - const [isAddFriendModalVisible, setIsAddFriendModalVisible] = useState(false); - const [selectedMember, setSelectedMember] = useState(null); - const [greeting, setGreeting] = useState(""); - - // 群管理弹窗状态 - const [isFriendSelectionVisible, setIsFriendSelectionVisible] = - useState(false); - const [isMemberSelectionVisible, setIsMemberSelectionVisible] = - useState(false); - const [isAdminSelectionVisible, setIsAdminSelectionVisible] = useState(false); - const [isRemoveAdminSelectionVisible, setIsRemoveAdminSelectionVisible] = - useState(false); - const [isTransferOwnerSelectionVisible, setIsTransferOwnerSelectionVisible] = - useState(false); - const [selectedFriends, setSelectedFriends] = useState( - [], - ); - const [contractList, setContractList] = useState([]); - - const handleAddFriend = member => { - setSelectedMember(member); - setGreeting(`你好, 我来自群聊${contractInfo.name}`); - setIsAddFriendModalVisible(true); - }; - - // 群管理操作处理函数 - const handleAddMember = ( - selectedIds: number[], - selectedItems: FriendSelectionItem[], - ) => { - console.log("添加成员:", selectedIds, selectedItems); - sendCommand("CmdChatroomInvite", { - wechatChatroomId: contract.id, - wechatFriendIds: selectedIds, - }); - messageApi.success(`已添加 ${selectedItems.length} 个成员`); - setIsFriendSelectionVisible(false); - }; - - //删除群成员 - const handleRemoveMember = (selectedIds: string[]) => { - console.log("删除成员:", selectedIds); - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 2, - extra: JSON.stringify({ - friendIdList: selectedIds, - }), - }); - messageApi.success(`已删除 ${selectedIds.length} 个成员`); - setIsMemberSelectionVisible(false); - }; - //添加管理员 - const handleAddAdmin = (selectedIds: string[]) => { - console.log("添加管理员:", selectedIds); - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 12, - extra: JSON.stringify({ - wechatIds: selectedIds, - }), - }); - messageApi.success(`已添加 ${selectedIds.length} 个管理员`); - setIsAdminSelectionVisible(false); - }; - - //删除管理员 - const handleRemoveAdmin = (selectedIds: string[]) => { - console.log("删除管理员:", selectedIds); - - selectedIds.forEach(wechatId => { - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 8, // 8 for remove admin - extra: JSON.stringify({ - wechatId: wechatId, - }), - }); - }); - - messageApi.success(`已删除 ${selectedIds.length} 个管理员`); - setIsRemoveAdminSelectionVisible(false); - }; - - //群主转让 √ - const handleTransferOwner = (selectedIds: string[]) => { - if (selectedIds.length !== 1) { - messageApi.error("只能选择一个成员作为新群主"); - return; - } - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 10, - extra: JSON.stringify({ - wechatId: selectedIds[0], - }), - }); - messageApi.success("群主转让成功"); - setIsTransferOwnerSelectionVisible(false); - }; - - const handleSendFriendRequest = () => { - if (!selectedMember) return; - - sendCommand("CmdChatroomOperate", { - chatroomOperateTyp: 1, - extra: JSON.stringify({ - wechatId: selectedMember.wechatId, - sendWord: greeting, - }), - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - }); - - messageApi.success("好友请求已发送"); - setIsAddFriendModalVisible(false); - setSelectedMember(null); - setGreeting(""); - }; - - // 构建联系人或群聊详细信息 - - const kfSelectedUser = useCkChatStore(state => - state.getKfUserInfo(contract.wechatAccountId || 0), - ); - - const getSomeContractList = useCkChatStore( - state => state.getSomeContractList, - ); - - const { sendCommand } = useWebSocketStore(); - - // 权限控制:检查当前客服是否有群管理权限 - const hasGroupManagePermission = () => { - 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])]; - setAllAvailableTags(allTags); - } catch (error) { - console.error("获取标签失败:", error); - } - }; - fetchAvailableTags(); - }, [kfSelectedUser, contract.labels]); - - // 当contract变化时更新各种值 - useEffect(() => { - setRemarkValue(contract.conRemark || ""); - setIsEditingRemark(false); - setSelectedTags(contract.labels || []); - - if (isGroup) { - setGroupNameValue(contract.name || ""); - setIsEditingGroupName(false); - setGroupNoticeValue(contract.notice || ""); - setIsEditingGroupNotice(false); - setSelfDisplayNameValue(contract.selfDisplyName || ""); - setIsEditingSelfDisplayName(false); - } - }, [ - contract.conRemark, - contract.labels, - contract.name, - contract.notice, - contract.selfDisplyName, - isGroup, - ]); - - // 处理备注保存 - const handleSaveRemark = () => { - if (isGroup) { - // 群聊备注修改 - sendCommand("CmdModifyGroupRemark", { - wechatAccountId: contract.wechatAccountId, - chatroomId: contract.chatroomId, - newRemark: remarkValue, - }); - } else { - // 好友备注修改 - sendCommand("CmdModifyFriendRemark", { - wechatAccountId: contract.wechatAccountId, - wechatFriendId: contract.id, - newRemark: remarkValue, - }); - } - - messageApi.success("备注保存成功"); - setIsEditingRemark(false); - // 更新contract对象中的备注(实际项目中应该通过props回调或状态管理) - }; - - // 处理群名称保存 - const handleSaveGroupName = () => { - if (!hasGroupManagePermission()) { - messageApi.error("只有群主才能修改群名称"); - return; - } - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 6, - extra: `{"chatroomName":"${groupNameValue}"}`, - }); - - messageApi.success("群名称修改成功"); - setIsEditingGroupName(false); - }; - - // 点击编辑群名称按钮 - const handleEditGroupName = () => { - if (!hasGroupManagePermission()) { - messageApi.error("只有群主才能修改群名称"); - return; - } - setGroupNameValue(contractInfo.name || ""); - setIsEditingGroupName(true); - }; - - // 处理群公告保存 - const handleSaveGroupNotice = () => { - if (!hasGroupManagePermission()) { - messageApi.error("只有群主才能修改群公告"); - return; - } - setConfirmLoading(true); - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 5, - extra: `{"announce":"${groupNoticeValue}"}`, - }); - - // 模拟延迟 - setTimeout(() => { - messageApi.success("群公告修改成功"); - setIsGroupNoticeModalVisible(false); - setConfirmLoading(false); - }, 1000); - }; - - // 打开AI编写弹框 - const handleOpenAiModal = () => { - if (!hasGroupManagePermission()) { - messageApi.error("只有群主才能使用AI编写功能"); - return; - } - setIsAiModalVisible(true); - setAiGeneratedContent(""); // 清空之前生成的内容 - }; - - // 处理AI生成群公告 - const handleAiGenerateNotice = async () => { - setAiGenerating(true); - try { - // 调用AI接口生成群公告 - const aiResponse = await generateAiText(aiPrompt, { - wechatAccountId: contract.wechatAccountId, - groupId: contract.id, - }); - - setAiGeneratedContent(aiResponse); - messageApi.success("AI生成群公告成功!"); - } catch (error: any) { - console.error("AI生成失败:", error); - messageApi.error(error.message || "AI生成失败,请重试"); - } finally { - setAiGenerating(false); - } - }; - - // 确认使用AI生成的内容 - const handleConfirmAiContent = () => { - setGroupNoticeValue(aiGeneratedContent); - setIsAiModalVisible(false); - messageApi.success("已应用AI生成的群公告内容"); - }; - - // 点击编辑群公告按钮 - const handleEditGroupNotice = () => { - if (!hasGroupManagePermission()) { - messageApi.error("只有群主才能修改群公告"); - return; - } - setGroupNoticeValue(contract.notice || ""); - setIsGroupNoticeModalVisible(true); - }; - - // 处理我在本群中的昵称保存 - const handleSaveSelfDisplayName = () => { - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 8, - extra: `${selfDisplayNameValue}`, - }); - - messageApi.success("群昵称修改成功"); - setIsEditingSelfDisplayName(false); - }; - - // 点击编辑群昵称按钮 - const handleEditSelfDisplayName = () => { - setSelfDisplayNameValue(contract.selfDisplyName || ""); - setIsEditingSelfDisplayName(true); - }; - - // 处理取消编辑 - const handleCancelEdit = () => { - setRemarkValue(contract.conRemark || ""); - setIsEditingRemark(false); - }; - - // 处理标签点击切换 - const handleTagToggle = (tagName: string) => { - const newSelectedTags = selectedTags.includes(tagName) - ? selectedTags.filter(tag => tag !== tagName) - : [...selectedTags, tagName]; - - setSelectedTags(newSelectedTags); - - // 使用WebSocket发送修改标签命令 - if (isGroup) { - // 群聊标签修改 - sendCommand("CmdModifyGroupLabel", { - labels: newSelectedTags, - seq: +new Date(), - wechatAccountId: contract.wechatAccountId, - chatroomId: contract.chatroomId, - }); - } else { - // 好友标签修改 - sendCommand("CmdModifyFriendLabel", { - labels: newSelectedTags, - seq: +new Date(), - wechatAccountId: contract.wechatAccountId, - wechatFriendId: contract.id, - }); - } - - messageApi.success( - `标签"${tagName}"${selectedTags.includes(tagName) ? "已取消" : "已选中"}`, - ); - }; - - // 处理新增标签 - const handleAddTag = () => { - if (!newTagValue.trim()) { - messageApi.error("请输入标签名称"); - return; - } - - if (allAvailableTags.includes(newTagValue.trim())) { - messageApi.error("标签已存在"); - return; - } - - const newTag = newTagValue.trim(); - - // 添加到可用标签列表 - setAllAvailableTags(prev => [...prev, newTag]); - - // 自动选中新添加的标签 - setSelectedTags(prev => [...prev, newTag]); - - // 使用WebSocket发送新增标签命令 - if (isGroup) { - // 群聊标签修改 - sendCommand("CmdModifyGroupLabel", { - labels: [...selectedTags, newTag], - seq: +new Date(), - wechatAccountId: contract.wechatAccountId, - chatroomId: contract.chatroomId, - }); - } else { - // 好友标签修改 - sendCommand("CmdModifyFriendLabel", { - labels: [...selectedTags, newTag], - seq: +new Date(), - wechatAccountId: contract.wechatAccountId, - wechatFriendId: contract.id || contract.wechatId, - }); - } - - messageApi.success(`标签"${newTag}"添加成功`); - setNewTagValue(""); - setIsAddingTag(false); - }; - - // 处理取消新增标签 - const handleCancelAddTag = () => { - setNewTagValue(""); - setIsAddingTag(false); - }; - - // 处理退出群聊 - const handleLeaveGroup = () => { - Modal.confirm({ - title: "确定要退出群聊吗?", - content: "退出后将不再接收此群聊消息。", - okText: "确定", - cancelText: "取消", - onOk: () => { - sendCommand("CmdChatroomOperate", { - wechatAccountId: contract.wechatAccountId, - wechatChatroomId: contract.id, - chatroomOperateType: 4, // 4 for quit - }); - messageApi.success("已退出群聊"); - // 可能还需要一个回调来关闭侧边栏或切换到另一个聊天 - if (onToggleProfile) { - onToggleProfile(); - } - }, - }); - }; - - // 构建联系人或群聊详细信息 - const contractInfo = { - name: contract.name || contract.nickname, - nickname: contract.nickname, - conRemark: remarkValue, // 使用当前编辑的备注值 - alias: contract.alias, - wechatId: contract.wechatId, - chatroomId: isGroup ? contract.chatroomId : undefined, - chatroomOwner: isGroup ? contract.chatroomOwner : undefined, - avatar: contract.avatar || contract.chatroomAvatar, - phone: contract.phone || "-", - email: contract.email || "-", - department: contract.department || "-", - position: contract.position || "-", - company: contract.company || "-", - region: contract.region || "-", - joinDate: contract.joinDate || "-", - notice: isGroup ? contract.notice : undefined, - selfDisplyName: isGroup ? contract.selfDisplyName : undefined, - status: "在线", - tags: selectedTags, - bio: contract.bio || contract.signature || "-", - }; - - if (!showProfile) { - return null; - } - + console.log(contract); return ( - <> - {contextHolder} - -
- {/* 关闭按钮 */} -
-
- - {/* 头像和基本信息 */} -
- } - /> -
-
- - {contractInfo.status} -
- -

- {contractInfo.nickname || contractInfo.name} -

-
-
-
- - {/* 详细信息卡片 */} - - {isGroup ? ( - // 群聊信息 - <> -
- - 群名称: -
- {isEditingGroupName ? ( -
- setGroupNameValue(e.target.value)} - placeholder="请输入群名称" - size="small" - style={{ flex: 1 }} - /> -
- ) : ( -
- -

- {contractInfo.nickname || contractInfo.name} -

-
- {hasGroupManagePermission() && ( -
- )} -
-
-
- - 群ID: - - {contractInfo.chatroomId} - -
-
- - 群主: - - {contractInfo.chatroomOwner} - -
-
- - 群昵称: -
- {isEditingSelfDisplayName ? ( -
- - setSelfDisplayNameValue(e.target.value) - } - placeholder="请输入群昵称" - size="small" - style={{ flex: 1 }} - /> -
- ) : ( -
- - {contractInfo.selfDisplyName || "点击添加群昵称"} - -
- )} -
-
- - ) : ( - // 好友信息 - <> -
- - 微信号: - - {contractInfo.alias || contractInfo.wechatId} - -
-
- - 电话: - {contractInfo.phone} -
-
- - 地区: - - {contractInfo.region} - -
- - )} - {!isGroup && ( -
- - 备注: -
- {isEditingRemark ? ( -
- setRemarkValue(e.target.value)} - placeholder="请输入备注" - size="small" - style={{ flex: 1 }} - /> -
- ) : ( -
- {contractInfo.conRemark || "点击添加备注"} -
- )} -
-
- )} -
- - {/* 标签 - 仅在非群聊时显示 */} - {!isGroup && ( - -
- {/* 渲染所有可用标签,选中的排在前面 */} - {[...new Set([...selectedTags, ...allAvailableTags])].map( - (tag, index) => { - const isSelected = selectedTags.includes(tag); - return ( - handleTagToggle(tag)} - > - {tag} - - ); - }, - )} - - {/* 新增标签区域 */} - {isAddingTag ? ( -
- setNewTagValue(e.target.value)} - placeholder="请输入标签名称" - size="small" - style={{ width: "120px" }} - onPressEnter={handleAddTag} - /> -
- ) : ( - setIsAddingTag(true)} - > - 新增标签 - - )} - - {allAvailableTags.length === 0 && !isAddingTag && ( - - 暂无可用标签 - - )} -
-
- )} - - {/* 群公告 - 仅在群聊时显示 */} - {isGroup && ( - - {/* 群聊简介(原群公告) */} -
{ - setGroupNoticeValue(contractInfo.notice || ""); - setIsGroupNoticeModalVisible(true); - } - : undefined - } - style={{ - cursor: hasGroupManagePermission() ? "pointer" : "default", - }} - > -
-
- {contractInfo.notice || "点击添加群公告"} -
- {hasGroupManagePermission() && ( -
-
-
- )} - - {isGroup && ( - -
-
- - {hasGroupManagePermission() && ( - - )} -
- {hasGroupManagePermission() && ( -
- - - -
- )} -
-
- {currentGroupMembers.map((member, index) => ( - -
setHoveredMember(member.wechatId)} - onMouseLeave={() => setHoveredMember(null)} - style={{ - display: "flex", - alignItems: "center", - width: "100%", - }} - > - - - {member.nickname} - - {hoveredMember === member.wechatId && ( -
-
- ))} -
- - -
- )} - - setIsAddFriendModalVisible(false)} - okText="确定" - cancelText="取消" - > - setGreeting(e.target.value)} - placeholder="请输入招呼语" - rows={4} - /> - -
-
- - {/* 群公告编辑弹窗 */} - setIsGroupNoticeModalVisible(false)} - footer={[ - , - hasGroupManagePermission() && ( - - ), - , - ].filter(Boolean)} - > - {!hasGroupManagePermission() && ( -
+ setActiveKey(key)} + tabBarStyle={{ + padding: "0 30px", }} - > - ⚠️ 您不是群主,无法修改群公告 -
- )} -
- setGroupNoticeValue(e.target.value)} - placeholder={ - hasGroupManagePermission() - ? "请输入群公告内容,或点击AI编写按钮自动生成" - : "仅群主可以修改群公告" - } - rows={8} - style={{ - resize: "none", - whiteSpace: "pre-wrap", - wordBreak: "break-word", - lineHeight: "1.5", - }} - disabled={!hasGroupManagePermission()} + items={[ + { + key: "quickwords", + label: "快捷语录", + }, + { + key: "profile", + label: isGroup ? "群资料" : "个人资料", + }, + + ...(!isGroup + ? [ + { + key: "moments", + label: "朋友圈", + }, + ] + : []), + ]} /> -
-
- 💡 提示: - {hasGroupManagePermission() - ? "AI编写功能将根据默认模板生成专业的群公告内容,您可以在生成后进行个性化修改。" - : "只有群主才能编辑群公告内容。"} -
-
- - {/* AI编写群公告弹框 */} - setIsAiModalVisible(false)} - width={800} - footer={[ - , - , - , - ]} + } > -
- {/* 提示词区域 */} -
-
- 📝 AI提示词 -
- setAiPrompt(e.target.value)} - placeholder="请输入AI生成群公告的提示词..." - rows={6} - style={{ resize: "none" }} - /> -
- 💡 提示:详细的提示词能帮助AI生成更符合您需求的群公告内容 -
-
- - {/* 生成内容区域 */} -
-
- 🤖 AI生成的群公告 -
- setAiGeneratedContent(e.target.value)} - placeholder={ - aiGenerating - ? "AI正在生成中,请稍候..." - : "点击上方'生成内容'按钮,AI将根据提示词生成群公告" - } - rows={8} - style={{ resize: "none" }} - disabled={aiGenerating} - /> - {aiGeneratedContent && ( -
- ✅ 内容已生成,您可以编辑后点击"确认使用"应用到群公告 -
- )} -
-
-
- - {/* 群管理弹窗组件 */} - {/* 添加成员弹窗 */} - setIsFriendSelectionVisible(false)} - onConfirm={(selectedIds, selectedItems) => { - setSelectedFriends(selectedItems); - handleAddMember( - selectedIds.map(id => parseInt(id)), - selectedItems, - ); - }} - dataSource={contractList} - title="添加群成员" - /> - - {/* 删除成员弹窗 */} - ({ - id: member.wechatId, - nickname: member.nickname, - avatar: member.avatar, - }))} - onCancel={() => setIsMemberSelectionVisible(false)} - onConfirm={handleRemoveMember} - title="删除群成员" - allowMultiple={false} - /> - - {/* 添加管理员弹窗 */} - ({ - id: member.wechatId, - nickname: member.nickname, - avatar: member.avatar, - }))} - onCancel={() => setIsAdminSelectionVisible(false)} - onConfirm={handleAddAdmin} - title="添加管理员" - allowMultiple={false} - /> - - {/* 删除管理员弹窗 */} - ({ - id: member.wechatId, - nickname: member.nickname, - avatar: member.avatar, - }))} - onCancel={() => setIsRemoveAdminSelectionVisible(false)} - onConfirm={handleRemoveAdmin} - title="删除管理员" - allowMultiple={false} - /> - - {/* 转让群主弹窗 */} - ({ - id: member.wechatId, - nickname: member.nickname, - avatar: member.avatar, - }))} - onCancel={() => setIsTransferOwnerSelectionVisible(false)} - onConfirm={handleTransferOwner} - title="转让群主" - allowMultiple={false} - /> - + {activeKey === "profile" && ( + + )} + {activeKey === "quickwords" && ( + {}} + onAdd={() => {}} + onRemove={() => {}} + /> + )} + {activeKey === "moments" && !isGroup && ( + + )} + + ); }; diff --git a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/demo.tsx b/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/demo.tsx deleted file mode 100644 index e1bfb7ca8..000000000 --- a/Touchkebao/src/pages/pc/ckbox/weChat/components/ChatWindow/demo.tsx +++ /dev/null @@ -1,669 +0,0 @@ -import React, { useEffect, useRef } from "react"; -import { Layout, Button, Avatar, Space, Dropdown, Menu, Tooltip } from "antd"; -import { - PhoneOutlined, - VideoCameraOutlined, - MoreOutlined, - UserOutlined, - DownloadOutlined, - FileOutlined, - FilePdfOutlined, - FileWordOutlined, - FileExcelOutlined, - FilePptOutlined, - PlayCircleFilled, - TeamOutlined, - FolderOutlined, - EnvironmentOutlined, -} from "@ant-design/icons"; -import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data"; -import styles from "./ChatWindow.module.scss"; -import { useWebSocketStore } from "@/store/module/websocket/websocket"; -import { formatWechatTime } from "@/utils/common"; -import ProfileCard from "./components/ProfileCard"; -import MessageEnter from "./components/MessageEnter"; -import { useWeChatStore } from "@/store/module/weChat/weChat"; -const { Header, Content } = Layout; - -interface ChatWindowProps { - contract: ContractData | weChatGroup; - showProfile?: boolean; - onToggleProfile?: () => void; -} - -const ChatWindow: React.FC = ({ - contract, - showProfile = true, - onToggleProfile, -}) => { - const messagesEndRef = useRef(null); - const currentMessages = useWeChatStore(state => state.currentMessages); - const prevMessagesRef = useRef(currentMessages); - - useEffect(() => { - const prevMessages = prevMessagesRef.current; - - const hasVideoStateChange = currentMessages.some((msg, index) => { - // 首先检查消息对象本身是否为null或undefined - if (!msg || !msg.content) return false; - - const prevMsg = prevMessages[index]; - if (!prevMsg || !prevMsg.content || prevMsg.id !== msg.id) return false; - - try { - const currentContent = - typeof msg.content === "string" - ? JSON.parse(msg.content) - : msg.content; - const prevContent = - typeof prevMsg.content === "string" - ? JSON.parse(prevMsg.content) - : prevMsg.content; - - // 检查视频状态是否发生变化(开始加载、完成加载、获得URL) - const currentHasVideo = - currentContent.previewImage && currentContent.tencentUrl; - const prevHasVideo = prevContent.previewImage && prevContent.tencentUrl; - - if (currentHasVideo && prevHasVideo) { - // 检查加载状态变化或视频URL变化 - return ( - currentContent.isLoading !== prevContent.isLoading || - currentContent.videoUrl !== prevContent.videoUrl - ); - } - - return false; - } catch (e) { - return false; - } - }); - - // 只有在没有视频状态变化时才自动滚动到底部 - if (!hasVideoStateChange) { - scrollToBottom(); - } - - // 更新上一次的消息状态 - prevMessagesRef.current = currentMessages; - }, [currentMessages]); - - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); - }; - - // 处理视频播放请求,发送socket请求获取真实视频地址 - const handleVideoPlayRequest = (tencentUrl: string, messageId: number) => { - console.log("发送视频下载请求:", { messageId, tencentUrl }); - - // 先设置加载状态 - useWeChatStore.getState().setVideoLoading(messageId, true); - - // 构建socket请求数据 - useWebSocketStore.getState().sendCommand("CmdDownloadVideo", { - chatroomMessageId: contract.chatroomId ? messageId : 0, - friendMessageId: contract.chatroomId ? 0 : messageId, - seq: `${+new Date()}`, // 使用时间戳作为请求序列号 - tencentUrl: tencentUrl, - wechatAccountId: contract.wechatAccountId, - }); - }; - - // 解析消息内容,判断消息类型并返回对应的渲染内容 - const parseMessageContent = ( - content: string | null | undefined, - msg: ChatRecord, - ) => { - // 处理null或undefined的内容 - if (content === null || content === undefined) { - return
消息内容不可用
; - } - // 检查是否为表情包 - if ( - typeof content === "string" && - content.includes("ac-weremote-s2.oss-cn-shenzhen.aliyuncs.com") && - content.includes("#") - ) { - return ( -
- 表情包 window.open(content, "_blank")} - /> -
- ); - } - - // 检查是否为带预览图的视频消息 - try { - if ( - typeof content === "string" && - content.trim().startsWith("{") && - content.trim().endsWith("}") - ) { - const videoData = JSON.parse(content); - // 处理视频消息格式 {"previewImage":"https://...", "tencentUrl":"...", "videoUrl":"...", "isLoading":true} - if (videoData.previewImage && videoData.tencentUrl) { - // 提取预览图URL,去掉可能的引号 - const previewImageUrl = videoData.previewImage.replace(/[`"']/g, ""); - - // 创建点击处理函数 - const handlePlayClick = (e: React.MouseEvent) => { - e.stopPropagation(); - // 如果没有视频URL且不在加载中,则发起下载请求 - if (!videoData.videoUrl && !videoData.isLoading) { - handleVideoPlayRequest(videoData.tencentUrl, msg.id); - } - }; - - // 如果已有视频URL,显示视频播放器 - if (videoData.videoUrl) { - return ( - - ); - } - - // 显示预览图,根据加载状态显示不同的图标 - return ( -
-
- 视频预览 -
- {videoData.isLoading ? ( -
- ) : ( - - )} -
-
-
- ); - } - // 保留原有的视频处理逻辑 - else if ( - videoData.type === "video" && - videoData.url && - videoData.thumb - ) { - return ( -
-
window.open(videoData.url, "_blank")} - > - 视频预览 -
- -
-
- e.stopPropagation()} - > - - -
- ); - } - } - } catch (e) { - // 解析JSON失败,不是视频消息 - console.log("解析视频消息失败:", e); - } - - // 检查是否为图片链接 - if ( - typeof content === "string" && - (content.match(/\.(jpg|jpeg|png|gif)$/i) || - (content.includes("oss-cn-shenzhen.aliyuncs.com") && - content.includes(".jpg"))) - ) { - return ( -
- 图片消息 window.open(content, "_blank")} - /> -
- ); - } - - // 检查是否为视频链接 - if ( - typeof content === "string" && - (content.match(/\.(mp4|avi|mov|wmv|flv)$/i) || - (content.includes("oss-cn-shenzhen.aliyuncs.com") && - content.includes(".mp4"))) - ) { - return ( - - ); - } - - // 检查是否为音频链接 - if ( - typeof content === "string" && - (content.match(/\.(mp3|wav|ogg|m4a)$/i) || - (content.includes("oss-cn-shenzhen.aliyuncs.com") && - content.includes(".mp3"))) - ) { - return ( - - ); - } - - // 检查是否为Office文件链接 - if ( - typeof content === "string" && - content.match(/\.(doc|docx|xls|xlsx|ppt|pptx|pdf)$/i) - ) { - const fileName = content.split("/").pop() || "文件"; - const fileExt = fileName.split(".").pop()?.toLowerCase(); - - // 根据文件类型选择不同的图标 - let fileIcon = ( - - ); - - if (fileExt === "pdf") { - fileIcon = ( - - ); - } else if (fileExt === "doc" || fileExt === "docx") { - fileIcon = ( - - ); - } else if (fileExt === "xls" || fileExt === "xlsx") { - fileIcon = ( - - ); - } else if (fileExt === "ppt" || fileExt === "pptx") { - fileIcon = ( - - ); - } - - return ( - - ); - } - - // 检查是否为文件消息(JSON格式) - try { - if ( - typeof content === "string" && - content.trim().startsWith("{") && - content.trim().endsWith("}") - ) { - const fileData = JSON.parse(content); - if (fileData.type === "file" && fileData.title) { - // 检查是否为Office文件 - const fileExt = fileData.title.split(".").pop()?.toLowerCase(); - let fileIcon = ( - - ); - - if (fileExt === "pdf") { - fileIcon = ( - - ); - } else if (fileExt === "doc" || fileExt === "docx") { - fileIcon = ( - - ); - } else if (fileExt === "xls" || fileExt === "xlsx") { - fileIcon = ( - - ); - } else if (fileExt === "ppt" || fileExt === "pptx") { - fileIcon = ( - - ); - } - - return ( -
- {fileIcon} -
-
- {fileData.title} -
- {fileData.totalLen && ( -
- {Math.round(fileData.totalLen / 1024)} KB -
- )} -
- { - e.stopPropagation(); - if (!fileData.url) { - console.log("文件URL不存在"); - } - }} - rel="noreferrer" - > - - -
- ); - } - } - } catch (e) { - // 解析JSON失败,不是文件消息 - } - - // 检查是否为位置信息 - if ( - typeof content === "string" && - (content.includes(" - -
-
{label}
- {coordinates && ( -
- {coordinates} -
- )} -
- - ); - } - - // 默认为文本消息 - return
{content}
; - }; - - // 用于分组消息并添加时间戳的辅助函数 - const groupMessagesByTime = (messages: ChatRecord[]) => { - return messages - .filter(msg => msg !== null && msg !== undefined) // 过滤掉null和undefined的消息 - .map(msg => ({ - time: formatWechatTime(msg?.wechatTime), - messages: [msg], - })); - }; - - const renderMessage = (msg: ChatRecord) => { - // 添加null检查,防止访问null对象的属性 - if (!msg) return null; - - const isOwn = msg?.isSend; - return ( -
-
- {!isOwn && ( - } - className={styles.messageAvatar} - /> - )} -
- {!isOwn && ( -
{msg?.senderName}
- )} - {parseMessageContent(msg?.content, msg)} -
-
-
- ); - }; - - const chatMenu = ( - - }> - 查看资料 - - }> - 语音通话 - - }> - 视频通话 - - - 置顶聊天 - 消息免打扰 - - - 清空聊天记录 - - - ); - - return ( - - {/* 聊天主体区域 */} - - {/* 聊天头部 */} -
-
- : - } - /> -
-
- {contract.nickname || contract.name} -
-
-
- - -
- - {/* 聊天内容 */} - -
- {groupMessagesByTime(currentMessages).map((group, groupIndex) => ( - -
{group.time}
- {group.messages.map(renderMessage)} -
- ))} -
-
- - - {/* 消息输入组件 */} - - - - {/* 右侧个人资料卡片 */} - - - ); -}; - -export default ChatWindow;