feat(朋友圈): 实现朋友圈点赞功能并优化组件结构

重构朋友圈组件结构,将核心逻辑拆分为独立组件。主要变更包括:
1. 新增点赞功能及相关API接口
2. 修改snsId类型从number到string以兼容接口
3. 在store中新增updateLikeMoment方法处理点赞状态
4. 提取FriendCard和MomentList为独立组件
5. 优化代码结构提升可维护性
This commit is contained in:
超级老白兔
2025-09-17 15:52:15 +08:00
parent 680c16c7da
commit 3477b5d575
9 changed files with 814 additions and 273 deletions

View File

@@ -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);
};

View File

@@ -0,0 +1,22 @@
import { FriendsCircleItem } from "../index.data";
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;
}

View File

@@ -0,0 +1,217 @@
import React from "react";
import { Avatar, Button, Image, Spin } from "antd";
import {
HeartOutlined,
MessageOutlined,
LoadingOutlined,
} from "@ant-design/icons";
import { FriendsCircleItem } from "../index.data";
import styles from "../index.module.scss";
import { likeMoment, cancelLikeMoment } from "./api";
import { likeListItem, FriendCardProps, MomentListProps } from "./data";
import { useWeChatStore } from "@/store/module/weChat/weChat";
// 单个朋友圈项目组件
export const FriendCard: React.FC<FriendCardProps> = ({
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 } = useWeChatStore();
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 handleComment = (id: string) => {
console.log("评论:", id);
};
return (
<div className={styles.circleItem}>
{isNotMy && (
<div className={styles.avatar}>
<Avatar size={36} shape="square" src="/public/assets/face/1.png" />
</div>
)}
<div className={styles.itemWrap}>
<div className={styles.itemHeader}>
<div className={styles.userInfo}>
{/* <div className={styles.username}>{nickName}</div> */}
</div>
</div>
<div className={styles.itemContent}>
<div className={styles.contentText}>{content}</div>
{images && images.length > 0 && (
<div className={styles.imageContainer}>
{images.map((image, index) => (
<Image
key={index}
src={image}
className={styles.contentImage}
/>
))}
</div>
)}
</div>
<div className={styles.itemFooter}>
<div className={styles.timeInfo}>{time}</div>
<div className={styles.actions}>
<Button
type="text"
size="small"
icon={<HeartOutlined />}
onClick={() => handleLike(monent)}
className={styles.actionButton}
>
{likesCount > 0 && <span>{likesCount}</span>}
</Button>
<Button
type="text"
size="small"
icon={<MessageOutlined />}
onClick={() => handleComment(monent.snsId)}
className={styles.actionButton}
>
{commentsCount > 0 && <span>{commentsCount}</span>}
</Button>
</div>
</div>
{/* 点赞和评论区域 */}
{(monent?.likeList?.length > 0 || monent?.commentList?.length > 0) && (
<div className={styles.interactionArea}>
{/* 点赞列表 */}
{monent?.likeList?.length > 0 && (
<div className={styles.likeArea}>
<HeartOutlined className={styles.likeIcon} />
<span className={styles.likeList}>
{monent?.likeList?.map((like, index) => (
<span key={`${like.wechatId}-${like.createTime}-${index}`}>
{like.nickName}
{index < (monent?.likeList?.length || 0) - 1 && "、"}
</span>
))}
</span>
</div>
)}
{/* 评论列表 */}
{monent?.commentList?.length > 0 && (
<div className={styles.commentArea}>
{monent?.commentList?.map(comment => (
<div
key={`${comment.wechatId}-${comment.commentTime}`}
className={styles.commentItem}
>
<span className={styles.commentUser}>
{comment.nickName}
</span>
<span className={styles.commentSeparator}>: </span>
<span className={styles.commentContent}>
{comment.content}
</span>
</div>
))}
</div>
)}
</div>
)}
</div>
</div>
);
};
// 朋友圈列表组件
export const MomentList: React.FC<MomentListProps> = ({
MomentCommon,
MomentCommonLoading,
formatTime,
currentKf,
loadMomentData,
}) => {
return (
<div className={styles.myCircleContent}>
{MomentCommon.length > 0 ? (
<>
{MomentCommon.map((v, index) => (
<div
key={`${v.snsId}-${v.createTime}-${index}`}
className={styles.itemWrapper}
>
<FriendCard
monent={v}
isNotMy={false}
formatTime={formatTime}
currentKf={currentKf}
/>
</div>
))}
{MomentCommonLoading && (
<div className={styles.loadingMore}>
<Spin indicator={<LoadingOutlined spin />} /> ...
</div>
)}
{!MomentCommonLoading && (
<div style={{ textAlign: "center" }}>
<Button
size="small"
type="primary"
onClick={() => loadMomentData(true)}
>
...
</Button>
</div>
)}
</>
) : MomentCommonLoading ? (
<div className={styles.loadingMore}>
<Spin indicator={<LoadingOutlined spin />} /> ...
</div>
) : (
<p className={styles.emptyText}></p>
)}
</div>
);
};

View File

@@ -0,0 +1,412 @@
/* ===== 组件根容器 ===== */
.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;
line-height: 1.4;
.commentUser {
color: #576b95;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
.commentSeparator {
color: #666;
}
.commentContent {
color: #333;
}
}
}
}
}
}
/* 空状态样式 */
.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;
}
}

View File

@@ -37,7 +37,7 @@ export interface FriendsCircleItem {
createTime: number;
likeList: LikeItem[];
momentEntity: MomentEntity;
snsId: number;
snsId: string;
type: number;
}

View File

@@ -1,22 +1,16 @@
import React, { useState, useEffect } from "react";
import { Avatar, Button, Collapse, Spin, Modal } from "antd";
import {
HeartOutlined,
ChromeOutlined,
MessageOutlined,
LoadingOutlined,
CloseOutlined,
} from "@ant-design/icons";
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 { FriendsCircleItem } from "./index.data";
import { fetchFriendsCircleData } from "./api";
import { useCkChatStore } from "@/store/module/ckchat/ckchat";
import { useWeChatStore } from "@/store/module/weChat/weChat";
interface FriendsCircleProps {
wechatFriendId: number;
wechatFriendId?: number;
}
const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
@@ -36,10 +30,6 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
// 状态管理
const [expandedKeys, setExpandedKeys] = useState<string[]>([]);
// 图片预览相关状态
const [previewVisible, setPreviewVisible] = useState(false);
const [previewImages, setPreviewImages] = useState<string[]>([]);
const [currentImageIndex, setCurrentImageIndex] = useState(0);
// 加载更多我的朋友圈
const loadMomentData = async (loadMore: boolean = false) => {
@@ -51,8 +41,8 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
wechatFriendId: wechatFriendId || 0,
createTimeSec: Math.floor(dayjs().subtract(2, "month").valueOf() / 1000),
prevSnsId: loadMore
? 0
: MomentCommon[MomentCommon.length - 1]?.snsId || 0,
? Number(MomentCommon[MomentCommon.length - 1]?.snsId) || 0
: 0,
count: 10,
isTimeline: expandedKeys.includes("1"),
seq: Date.now(),
@@ -64,49 +54,12 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
const handleCollapseChange = (keys: string | string[]) => {
const keyArray = Array.isArray(keys) ? keys : [keys];
setExpandedKeys(keyArray);
console.log("MomentCommonLoading", MomentCommonLoading);
if (!MomentCommonLoading && keys.length > 0) {
clearMomentCommon();
loadMomentData(false);
}
};
const handleLike = (id: number) => {
console.log("点赞:", id);
};
const handleComment = (id: number) => {
console.log("评论:", id);
};
// 处理图片预览
const handleImagePreview = (images: string[], index: number) => {
setPreviewImages(images);
setCurrentImageIndex(index);
setPreviewVisible(true);
};
// 关闭图片预览
const handleClosePreview = () => {
setPreviewVisible(false);
setPreviewImages([]);
setCurrentImageIndex(0);
};
// 切换到上一张图片
const handlePrevImage = () => {
setCurrentImageIndex(prev =>
prev > 0 ? prev - 1 : previewImages.length - 1,
);
};
// 切换到下一张图片
const handleNextImage = () => {
setCurrentImageIndex(prev =>
prev < previewImages.length - 1 ? prev + 1 : 0,
);
};
// 格式化时间戳
const formatTime = (timestamp: number) => {
const date = new Date(timestamp * 1000);
@@ -119,168 +72,6 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
});
};
// 获取用户昵称
const getUserNickName = (item: FriendsCircleItem) => {
// 优先从点赞列表或评论列表中获取昵称
if (item?.likeList?.length > 0) {
return item.likeList[0].nickName;
}
if (item.commentList.length > 0) {
return item.commentList[0].nickName;
}
return item.momentEntity.userName;
};
const renderNormalItem = (monent: FriendsCircleItem, isNotMy?: boolean) => {
// const nickName = getUserNickName(monent);
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;
return (
<div className={styles.circleItem}>
{isNotMy && (
<div className={styles.avatar}>
<Avatar size={36} shape="square" src="/public/assets/face/1.png" />
</div>
)}
<div className={styles.itemWrap}>
<div className={styles.itemHeader}>
<div className={styles.userInfo}>
{/* <div className={styles.username}>{nickName}</div> */}
</div>
</div>
<div className={styles.itemContent}>
<div className={styles.contentText}>{content}</div>
{images && images.length > 0 && (
<div className={styles.imageContainer}>
{images.map((image, index) => (
<img
key={index}
src={image}
className={styles.contentImage}
onClick={() => handleImagePreview(images, index)}
/>
))}
</div>
)}
</div>
<div className={styles.itemFooter}>
<div className={styles.timeInfo}>{time}</div>
<div className={styles.actions}>
<Button
type="text"
size="small"
icon={<HeartOutlined />}
onClick={() => handleLike(monent.snsId)}
className={styles.actionButton}
>
{likesCount > 0 && <span>{likesCount}</span>}
</Button>
<Button
type="text"
size="small"
icon={<MessageOutlined />}
onClick={() => handleComment(monent.snsId)}
className={styles.actionButton}
>
{commentsCount > 0 && <span>{commentsCount}</span>}
</Button>
</div>
</div>
{/* 点赞和评论区域 */}
{(monent?.likeList?.length > 0 ||
monent?.commentList?.length > 0) && (
<div className={styles.interactionArea}>
{/* 点赞列表 */}
{monent?.likeList?.length > 0 && (
<div className={styles.likeArea}>
<HeartOutlined className={styles.likeIcon} />
<span className={styles.likeList}>
{monent?.likeList?.map((like, index) => (
<span
key={`${like.wechatId}-${like.createTime}-${index}`}
>
{like.nickName}
{index < (monent?.likeList?.length || 0) - 1 && "、"}
</span>
))}
</span>
</div>
)}
{/* 评论列表 */}
{monent?.commentList?.length > 0 && (
<div className={styles.commentArea}>
{monent?.commentList?.map(comment => (
<div
key={`${comment.wechatId}-${comment.commentTime}`}
className={styles.commentItem}
>
<span className={styles.commentUser}>
{comment.nickName}
</span>
<span className={styles.commentSeparator}>: </span>
<span className={styles.commentContent}>
{comment.content}
</span>
</div>
))}
</div>
)}
</div>
)}
</div>
</div>
);
};
const renderMomentCommon = () => {
return (
<div className={styles.myCircleContent}>
{MomentCommon.length > 0 ? (
<>
{MomentCommon.map((v, index) => (
<div
key={`${v.snsId}-${v.createTime}-${index}`}
className={styles.itemWrapper}
>
{renderNormalItem(v, false)}
</div>
))}
{MomentCommonLoading && (
<div className={styles.loadingMore}>
<Spin indicator={<LoadingOutlined spin />} /> ...
</div>
)}
{!MomentCommonLoading && (
<div style={{ textAlign: "center" }}>
<Button
size="small"
type="primary"
onClick={() => loadMomentData(true)}
>
...
</Button>
</div>
)}
</>
) : MomentCommonLoading ? (
<div className={styles.loadingMore}>
<Spin indicator={<LoadingOutlined spin />} /> ...
</div>
) : (
<p className={styles.emptyText}></p>
)}
</div>
);
};
const collapseItems = [
{
key: "1",
@@ -294,7 +85,15 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
<span className={styles.specialText}></span>
</div>
),
children: renderMomentCommon(),
children: (
<MomentList
currentKf={currentKf}
MomentCommon={MomentCommon}
MomentCommonLoading={MomentCommonLoading}
loadMomentData={loadMomentData}
formatTime={formatTime}
/>
),
},
{
key: "2",
@@ -304,7 +103,15 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
<span className={styles.specialText}>广</span>
</div>
),
children: renderMomentCommon(),
children: (
<MomentList
currentKf={currentKf}
MomentCommon={MomentCommon}
MomentCommonLoading={MomentCommonLoading}
loadMomentData={loadMomentData}
formatTime={formatTime}
/>
),
},
...(wechatFriendId
? [
@@ -316,7 +123,15 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
<span className={styles.specialText}></span>
</div>
),
children: renderMomentCommon(),
children: (
<MomentList
currentKf={currentKf}
MomentCommon={MomentCommon}
MomentCommonLoading={MomentCommonLoading}
loadMomentData={loadMomentData}
formatTime={formatTime}
/>
),
},
]
: []),
@@ -333,58 +148,6 @@ const FriendsCircle: React.FC<FriendsCircleProps> = ({ wechatFriendId }) => {
activeKey={expandedKeys}
onChange={handleCollapseChange}
/>
{/* 图片预览Modal */}
<Modal
open={previewVisible}
footer={null}
onCancel={handleClosePreview}
width="100vw"
style={{ top: 0, paddingBottom: 0 }}
bodyStyle={{
padding: 0,
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgba(0, 0, 0, 0.9)",
}}
closeIcon={<CloseOutlined style={{ color: "white", fontSize: 24 }} />}
className={styles.imagePreviewModal}
>
<div className={styles.previewContainer}>
{previewImages.length > 0 && (
<>
<img
src={previewImages[currentImageIndex]}
alt="预览图片"
className={styles.previewImage}
/>
{previewImages.length > 1 && (
<>
<button
className={`${styles.navButton} ${styles.prevButton}`}
onClick={handlePrevImage}
>
</button>
<button
className={`${styles.navButton} ${styles.nextButton}`}
onClick={handleNextImage}
>
</button>
<div className={styles.imageCounter}>
{currentImageIndex + 1} / {previewImages.length}
</div>
</>
)}
</>
)}
</div>
</Modal>
</div>
);
};

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { Input, Skeleton } from "antd";
import { SearchOutlined, ChromeOutlined } from "@ant-design/icons";
import WechatFriends from "./WechatFriends";
@@ -26,7 +26,9 @@ const SidebarMenu: React.FC<SidebarMenuProps> = ({ loading = false }) => {
clearSearchKeyword();
};
// 过滤逻辑已移至store中这里直接使用store返回的已过滤数据
useEffect(() => {
setActiveTab("chats");
}, [kfSelected]);
// 渲染骨架屏
const renderSkeleton = () => (

View File

@@ -1,5 +1,10 @@
import { ChatRecord, ContractData, weChatGroup } from "@/pages/pc/ckbox/data";
import { FriendsCircleItem } from "@/pages/pc/ckbox/weChat/components/SidebarMenu/FriendsCicle/index.data";
interface likeListItem {
createTime: number;
nickName: string;
wechatId: string;
}
// 微信聊天相关的类型定义
export interface WeChatState {
// 当前选中的联系人/群组
@@ -26,6 +31,7 @@ export interface WeChatState {
MomentCommonLoading: boolean;
// MomentCommon 相关方法
updateMomentCommonLoading: (loading: boolean) => void;
updateLikeMoment: (snsId: string, likeList: likeListItem[]) => void;
loadChatMessages: (Init: boolean, To?: number) => Promise<void>;
SearchMessage: (params: {

View File

@@ -292,6 +292,19 @@ export const useWeChatStore = create<WeChatState>()(
updateMomentCommon: moments => {
set({ MomentCommon: moments });
},
updateLikeMoment: (snsId, likeList) => {
set(state => ({
MomentCommon: state.MomentCommon.map(item => {
if (item.snsId === snsId) {
return {
...item,
likeList: likeList,
};
}
return item;
}),
}));
},
}),
{
name: "wechat-storage",