|
|
@@ -10,10 +10,8 @@
|
|
|
<el-button type="primary" @click="handlePublish"> 发布动态 </el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 内容区域 -->
|
|
|
+ <!-- 动态列表(推荐和关注共用) -->
|
|
|
<div v-if="dynamicList.length > 0" class="content-section">
|
|
|
- <!-- 动态卡片网格 -->
|
|
|
<div class="dynamic-grid">
|
|
|
<div v-for="item in paginatedList" :key="item.id" class="dynamic-card" @click="handleCardClick(item)">
|
|
|
<!-- 图片/视频区域 -->
|
|
|
@@ -49,10 +47,10 @@
|
|
|
</div>
|
|
|
|
|
|
<div class="like-section" @click.stop="handleLike(item)">
|
|
|
- <el-icon :size="18" :color="item.isLiked ? '#f56c6c' : '#999'" class="like-icon">
|
|
|
+ <el-icon :size="18" :color="item.isLike == '1' ? '#f56c6c' : '#999'" class="like-icon">
|
|
|
<Star />
|
|
|
</el-icon>
|
|
|
- <span class="like-count">{{ item.likeCount }}</span>
|
|
|
+ <span class="like-count">{{ item.dianzanCount }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -62,7 +60,7 @@
|
|
|
|
|
|
<!-- 空状态 -->
|
|
|
<div v-else class="empty-section">
|
|
|
- <el-empty description="暂无动态数据" />
|
|
|
+ <el-empty :description="activeTab === 'follow' ? '暂无关注的动态' : '暂无动态数据'" />
|
|
|
</div>
|
|
|
|
|
|
<!-- 分页 -->
|
|
|
@@ -156,13 +154,13 @@
|
|
|
<div class="author-details">
|
|
|
<div class="author-name">@{{ currentDetail.author?.name || currentDetail.userName }}</div>
|
|
|
<div class="publish-time">
|
|
|
- {{ currentDetail.publishTime }}
|
|
|
+ {{ currentDetail.createdTime }}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="detail-description">
|
|
|
- <p>{{ currentDetail.description }}</p>
|
|
|
+ <p>{{ currentDetail.context }}</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -192,13 +190,13 @@
|
|
|
<!-- 点赞 -->
|
|
|
<div class="action-item" @click="handleDetailLike">
|
|
|
<div class="action-icon">
|
|
|
- <el-icon :size="28" :color="currentDetail.isLiked ? '#f56c6c' : '#fff'">
|
|
|
- <StarFilled v-if="currentDetail.isLiked" />
|
|
|
+ <el-icon :size="28" :color="currentDetail.isLike == '1' ? '#f56c6c' : '#fff'">
|
|
|
+ <StarFilled v-if="currentDetail.isLike" />
|
|
|
<Star v-else />
|
|
|
</el-icon>
|
|
|
</div>
|
|
|
<div class="action-count">
|
|
|
- {{ currentDetail.likeCount }}
|
|
|
+ {{ currentDetail.dianzanCount }}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -238,31 +236,29 @@
|
|
|
<div class="more-actions-menu">
|
|
|
<!-- 如果是当前用户的动态,显示编辑和删除 -->
|
|
|
<template v-if="isMyDynamic">
|
|
|
- <div class="menu-item" @click="handleEditDynamic">
|
|
|
+ <div class="menu-item" style="display: flex; align-items: center; cursor: pointer" @click="handleDeleteDynamic">
|
|
|
<el-icon :size="18">
|
|
|
- <Edit />
|
|
|
- </el-icon>
|
|
|
- <span>编辑</span>
|
|
|
- </div>
|
|
|
- <div class="menu-item" @click="handleDeleteDynamic">
|
|
|
- <el-icon :size="18">
|
|
|
- <Delete />
|
|
|
- </el-icon>
|
|
|
+ <Delete /> </el-icon
|
|
|
+ >
|
|
|
<span>删除</span>
|
|
|
</div>
|
|
|
</template>
|
|
|
<!-- 如果不是当前用户的动态,显示举报和拉黑 -->
|
|
|
<template v-else>
|
|
|
- <div class="menu-item" @click="handleReportDynamic">
|
|
|
+ <div
|
|
|
+ class="menu-item"
|
|
|
+ style="display: flex; align-items: center; padding-bottom: 10px; cursor: pointer"
|
|
|
+ @click="handleReportDynamic"
|
|
|
+ >
|
|
|
<el-icon :size="18">
|
|
|
- <Warning />
|
|
|
- </el-icon>
|
|
|
+ <Warning /> </el-icon
|
|
|
+ >
|
|
|
<span>举报</span>
|
|
|
</div>
|
|
|
- <div class="menu-item" @click="handleBlockUserClick">
|
|
|
+ <div class="menu-item" style="display: flex; align-items: center; cursor: pointer" @click="handleBlockUserClick">
|
|
|
<el-icon :size="18">
|
|
|
- <CircleClose />
|
|
|
- </el-icon>
|
|
|
+ <CircleClose /> </el-icon
|
|
|
+ >
|
|
|
<span>拉黑</span>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -272,6 +268,99 @@
|
|
|
</div>
|
|
|
</el-drawer>
|
|
|
|
|
|
+ <!-- 评论侧边栏 -->
|
|
|
+ <el-drawer v-model="commentDrawerVisible" title="评论" direction="rtl" size="400px" destroy-on-close>
|
|
|
+ <!-- 评论列表 -->
|
|
|
+ <div class="comment-list-container">
|
|
|
+ <div v-if="commentListData.length > 0" class="comment-list">
|
|
|
+ <div v-for="comment in commentListData" :key="comment.id" class="comment-item">
|
|
|
+ <div class="comment-avatar">
|
|
|
+ <img v-if="comment.userAvatar" :src="comment.userAvatar" :alt="comment.userName" />
|
|
|
+ <el-icon v-else :size="32">
|
|
|
+ <Avatar />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="comment-content-wrapper">
|
|
|
+ <div class="comment-header">
|
|
|
+ <span class="comment-user-name">{{ comment.userName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="comment-text">
|
|
|
+ {{ comment.commentContent }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="comment-actions">
|
|
|
+ <span class="comment-action-item" @click="handleLikeComment(comment)">
|
|
|
+ <el-icon :size="16" :color="comment.isLiked ? '#f56c6c' : '#999'">
|
|
|
+ <Star />
|
|
|
+ </el-icon>
|
|
|
+ <span>{{ comment.likeCount || 0 }}</span>
|
|
|
+ </span>
|
|
|
+ <span class="comment-action-item" @click="handleReplyComment(comment)">
|
|
|
+ <el-icon :size="16">
|
|
|
+ <ChatDotRound />
|
|
|
+ </el-icon>
|
|
|
+ <span>回复</span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <!-- 商家回复 -->
|
|
|
+ <div v-for="item in comment.storeComment" :key="item.id" class="store-comment-wrapper">
|
|
|
+ <div class="store-comment-item">
|
|
|
+ <div class="store-comment-avatar">
|
|
|
+ <img v-if="item.userImage" :src="item.userImage" :alt="item.userName" />
|
|
|
+ <el-icon v-else :size="24">
|
|
|
+ <Avatar />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="store-comment-content">
|
|
|
+ <div class="store-comment-header">
|
|
|
+ <span class="store-comment-user-name">{{ item.userName || "商家" }}</span>
|
|
|
+ <span class="store-comment-time">{{ item.createdTime || item.createDate }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="store-comment-text">
|
|
|
+ {{ item.commentContent }}
|
|
|
+ <span
|
|
|
+ class="comment-action-item"
|
|
|
+ @click="handleLikeComment(item)"
|
|
|
+ style="display: flex; align-items: center"
|
|
|
+ >
|
|
|
+ <el-icon :size="16" :color="item.isLiked ? '#f56c6c' : '#999'">
|
|
|
+ <Star /> </el-icon
|
|
|
+ >
|
|
|
+ <span>{{ item.likeCount || 0 }}</span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-empty v-else description="暂无评论" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 评论输入框 -->
|
|
|
+ <div class="comment-input-wrapper">
|
|
|
+ <!-- 回复提示 -->
|
|
|
+ <div v-if="replyingComment" class="reply-hint">
|
|
|
+ <span class="reply-text">回复 @{{ replyingComment.userName }}</span>
|
|
|
+ <el-icon class="cancel-reply" @click="handleCancelReply">
|
|
|
+ <Close />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <el-input
|
|
|
+ v-model="commentInput"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ :placeholder="replyingComment ? '输入回复内容...' : '你要评论点什么呢~'"
|
|
|
+ maxlength="500"
|
|
|
+ show-word-limit
|
|
|
+ />
|
|
|
+ <el-button type="primary" :loading="commentSubmitting" @click="handleSubmitComment">
|
|
|
+ {{ replyingComment ? "回复" : "发送" }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
<!-- 举报对话框 -->
|
|
|
<el-dialog v-model="reportDialogVisible" title="举报理由" width="500px" destroy-on-close @close="handleCloseReportDialog">
|
|
|
<div class="report-dialog-content">
|
|
|
@@ -341,6 +430,63 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 分享对话框 -->
|
|
|
+ <el-dialog v-model="shareDialogVisible" title="分享给好友" width="500px" destroy-on-close @close="handleCloseShareDialog">
|
|
|
+ <div class="share-dialog-content">
|
|
|
+ <!-- 好友列表 -->
|
|
|
+ <div class="share-friend-list">
|
|
|
+ <div v-if="filteredShareFriendList.length > 0">
|
|
|
+ <div
|
|
|
+ v-for="friend in filteredShareFriendList"
|
|
|
+ :key="friend.id"
|
|
|
+ class="share-friend-item"
|
|
|
+ @click="handleSelectFriend(friend)"
|
|
|
+ >
|
|
|
+ <div class="friend-info">
|
|
|
+ <div class="friend-avatar">
|
|
|
+ <img v-if="friend.avatar" :src="friend.avatar" :alt="friend.name" />
|
|
|
+ <el-icon v-else :size="40">
|
|
|
+ <Avatar />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="friend-name">
|
|
|
+ {{ friend.name }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-icon v-if="selectedFriends.includes(friend.id)" :size="20" color="#409eff">
|
|
|
+ <CircleCheck />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <el-empty v-else description="暂无好友" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 已选择的好友 -->
|
|
|
+ <div v-if="selectedFriends.length > 0" class="selected-friends">
|
|
|
+ <div class="selected-title">已选择 {{ selectedFriends.length }} 位好友</div>
|
|
|
+ <div class="selected-list">
|
|
|
+ <el-tag v-for="friendId in selectedFriends" :key="friendId" closable @close="handleRemoveFriend(friendId)">
|
|
|
+ {{ shareFriendList.find(f => f.id === friendId)?.name }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="shareDialogVisible = false"> 取消 </el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ :loading="shareSubmitting"
|
|
|
+ :disabled="selectedFriends.length === 0"
|
|
|
+ @click="handleConfirmShare"
|
|
|
+ >
|
|
|
+ 确认分享
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -361,7 +507,9 @@ import {
|
|
|
Delete,
|
|
|
Warning,
|
|
|
CircleClose,
|
|
|
- Plus
|
|
|
+ Plus,
|
|
|
+ Search,
|
|
|
+ CircleCheck
|
|
|
} from "@element-plus/icons-vue";
|
|
|
import {
|
|
|
getUserDynamics,
|
|
|
@@ -373,8 +521,13 @@ import {
|
|
|
toggleFollowUser,
|
|
|
cancelFollowed,
|
|
|
likeDynamicNew,
|
|
|
- likeDynamicList
|
|
|
+ unlikeDynamicNew,
|
|
|
+ likeDynamicList,
|
|
|
+ likeComment,
|
|
|
+ unlikeComment
|
|
|
} from "@/api/modules/dynamicManagement";
|
|
|
+
|
|
|
+import { saveComment, commentList, getMutualAttention, addTransferCount } from "@/api/modules/newLoginApi";
|
|
|
import { uploadImg } from "@/api/modules/upload";
|
|
|
import { useUserStore } from "@/stores/modules/user";
|
|
|
|
|
|
@@ -406,6 +559,10 @@ interface MediaItem {
|
|
|
}
|
|
|
|
|
|
interface DynamicItem {
|
|
|
+ isLike: any;
|
|
|
+ dianzanCount: any;
|
|
|
+ createdTime: any;
|
|
|
+ context: string;
|
|
|
id: number;
|
|
|
title: string;
|
|
|
content: string;
|
|
|
@@ -421,6 +578,7 @@ interface DynamicItem {
|
|
|
userType?: number; // 发布者用户类型:1商家,2用户
|
|
|
phone?: string; // 发布者手机号
|
|
|
isFollowed?: number; // 是否已关注:0未关注,1已关注
|
|
|
+ isFollowThis?: number; // 是否已关注:0未关注,1已关注(用于判断关注按钮显示)
|
|
|
isVideo?: boolean; // 是否为视频
|
|
|
mediaType?: string; // 媒体类型:image 或 video
|
|
|
mediaList?: MediaItem[]; // 媒体列表(支持多张图片和视频)
|
|
|
@@ -432,8 +590,7 @@ interface DetailItem extends DynamicItem {
|
|
|
name: string;
|
|
|
avatar: string;
|
|
|
};
|
|
|
- description: string;
|
|
|
- publishTime: string;
|
|
|
+ context: string;
|
|
|
commentCount: number;
|
|
|
isFollowThis?: number; // 是否已关注:0未关注,1已关注(用于判断关注按钮显示)
|
|
|
}
|
|
|
@@ -488,6 +645,29 @@ const reportForm = reactive({
|
|
|
agreed: false
|
|
|
});
|
|
|
|
|
|
+// 分享对话框相关
|
|
|
+interface ShareFriend {
|
|
|
+ id: number;
|
|
|
+ name: string;
|
|
|
+ avatar: string;
|
|
|
+ phoneId?: string;
|
|
|
+}
|
|
|
+
|
|
|
+const shareDialogVisible = ref(false);
|
|
|
+const shareSubmitting = ref(false);
|
|
|
+const shareSearch = ref("");
|
|
|
+const shareFriendList = ref<ShareFriend[]>([]);
|
|
|
+const selectedFriends = ref<number[]>([]);
|
|
|
+
|
|
|
+// 过滤后的好友列表
|
|
|
+const filteredShareFriendList = computed(() => {
|
|
|
+ if (!shareSearch.value) {
|
|
|
+ return shareFriendList.value;
|
|
|
+ }
|
|
|
+ const keyword = shareSearch.value.toLowerCase();
|
|
|
+ return shareFriendList.value.filter(friend => friend.name.toLowerCase().includes(keyword));
|
|
|
+});
|
|
|
+
|
|
|
// 分页
|
|
|
const pagination = reactive({
|
|
|
page: 1,
|
|
|
@@ -564,7 +744,7 @@ const getUserTypeFromPhoneId = (phoneId: string | undefined): number => {
|
|
|
const loadDynamicList = async () => {
|
|
|
try {
|
|
|
// 获取店铺ID(从 userStore 中获取,如果没有则使用默认值)
|
|
|
- const phoneId = userStore.userInfo?.phoneId || "store_18900957960";
|
|
|
+ const phoneId = userStore.userInfo?.phoneId;
|
|
|
|
|
|
const res = await getUserDynamics({
|
|
|
type: 2, // 固定值,表示动态类型
|
|
|
@@ -572,7 +752,7 @@ const loadDynamicList = async () => {
|
|
|
myself: 0, // 0 表示他人的动态
|
|
|
page: pagination.page,
|
|
|
size: pagination.pageSize,
|
|
|
- phoneId
|
|
|
+ phoneId: `store_${userStore.userInfo?.phone}`
|
|
|
});
|
|
|
|
|
|
// 处理返回的数据
|
|
|
@@ -614,6 +794,9 @@ const loadDynamicList = async () => {
|
|
|
const userAvatar = item.userImage || item.userAvatar || item.avatar || item.headImg || "";
|
|
|
|
|
|
return {
|
|
|
+ // 保留接口返回的所有原始字段
|
|
|
+ ...item,
|
|
|
+ // 额外添加的处理字段
|
|
|
id: item.id || item.dynamicId,
|
|
|
title: item.title || item.content || item.dynamicContent || "这家店超好吃....",
|
|
|
content: item.content || item.dynamicContent || "",
|
|
|
@@ -630,6 +813,7 @@ const loadDynamicList = async () => {
|
|
|
userType: userType, // 用户类型:1商家,2用户
|
|
|
phone: phone, // 手机号(从 phoneId 或其他字段获取)
|
|
|
isFollowed: item.isFollowThis, // 使用isFollowThis字段:0未关注(显示按钮),1已关注(隐藏按钮)
|
|
|
+ isFollowThis: item.isFollowThis, // 是否已关注:0未关注,1已关注(用于判断关注按钮显示)
|
|
|
isVideo: isVideo, // 是否为视频
|
|
|
mediaType: mediaType // 媒体类型
|
|
|
};
|
|
|
@@ -647,11 +831,10 @@ const loadDynamicList = async () => {
|
|
|
};
|
|
|
|
|
|
// 点击卡片 - 查看详情(直接使用列表数据)
|
|
|
-const handleCardClick = (item: DynamicItem) => {
|
|
|
+const handleCardClick = async (item: DynamicItem) => {
|
|
|
console.log("点击动态:", item);
|
|
|
- console.log("isFollowThis值:", item, "(0=未关注显示按钮, 1=已关注隐藏按钮)");
|
|
|
+ console.log("isFollowThis值:", item.isFollowThis, "(0=未关注显示按钮, 1=已关注隐藏按钮)");
|
|
|
console.log("isMyDynamic:", isMyDynamic.value);
|
|
|
-
|
|
|
// 直接使用列表数据构建详情
|
|
|
currentDetail.value = {
|
|
|
...item,
|
|
|
@@ -660,15 +843,19 @@ const handleCardClick = (item: DynamicItem) => {
|
|
|
name: item.userName,
|
|
|
avatar: item.userAvatar
|
|
|
},
|
|
|
- description: item.content || item.title,
|
|
|
- publishTime: item.createTime,
|
|
|
+ context: item.context,
|
|
|
+ createdTime: item.createdTime,
|
|
|
+ dianzanCount: item.dianzanCount,
|
|
|
+ isLike: item.isLike,
|
|
|
+
|
|
|
commentCount: 0,
|
|
|
- isFollowThis: item.isFollowed // 添加isFollowThis字段用于判断关注按钮显示
|
|
|
+ isFollowThis: item.isFollowThis // 使用item.isFollowThis而不是item.isFollowed
|
|
|
};
|
|
|
-
|
|
|
- console.log("详情中的isFollowThis:", currentDetail.value.isFollowThis);
|
|
|
- console.log("按钮显示条件:", currentDetail.value.isFollowThis == 0);
|
|
|
+ console.log(currentDetail.value);
|
|
|
detailDrawerVisible.value = true;
|
|
|
+
|
|
|
+ // 打开抽屉时加载评论列表
|
|
|
+ await loadCommentList();
|
|
|
};
|
|
|
|
|
|
// 列表点赞/取消点赞
|
|
|
@@ -678,15 +865,21 @@ const handleLike = async (item: DynamicItem) => {
|
|
|
const phone = userStore.userInfo?.phone || "";
|
|
|
const currentUserPhoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
|
|
|
- // 调用列表点赞接口(表单方式提交)
|
|
|
- await likeDynamicList({
|
|
|
- dynamicId: item.id,
|
|
|
- phoneId: currentUserPhoneId
|
|
|
- });
|
|
|
+ const params = {
|
|
|
+ userId: currentUserPhoneId, // 当前用户phoneId
|
|
|
+ huifuId: item.id, // 动态ID
|
|
|
+ type: 2 // 2表示点赞
|
|
|
+ };
|
|
|
|
|
|
- // 切换点赞状态
|
|
|
- item.isLiked = !item.isLiked;
|
|
|
- item.likeCount += item.isLiked ? 1 : -1;
|
|
|
+ // 根据当前点赞状态调用不同的接口
|
|
|
+ if (item.isLiked) {
|
|
|
+ // 已点赞,调用取消点赞接口
|
|
|
+ await unlikeDynamicNew(params);
|
|
|
+ } else {
|
|
|
+ // 未点赞,调用点赞接口
|
|
|
+ await likeDynamicNew(params);
|
|
|
+ }
|
|
|
+ await loadDynamicList();
|
|
|
|
|
|
ElMessage.success(item.isLiked ? "点赞成功" : "取消点赞");
|
|
|
} catch (error) {
|
|
|
@@ -720,12 +913,21 @@ const handleDetailLike = async () => {
|
|
|
const phone = userStore.userInfo?.phone || "";
|
|
|
const currentUserPhoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
|
|
|
- // 调用详情点赞接口(表单方式提交)
|
|
|
- await likeDynamicNew({
|
|
|
+ const params = {
|
|
|
userId: currentUserPhoneId, // 当前用户phoneId
|
|
|
huifuId: currentDetail.value.id, // 动态ID
|
|
|
type: 2 // 2表示点赞
|
|
|
- });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 根据当前点赞状态调用不同的接口
|
|
|
+ if (currentDetail.value.isLiked) {
|
|
|
+ // 已点赞,调用取消点赞接口
|
|
|
+
|
|
|
+ await unlikeDynamicNew(params);
|
|
|
+ } else {
|
|
|
+ // 未点赞,调用点赞接口
|
|
|
+ await likeDynamicNew(params);
|
|
|
+ }
|
|
|
|
|
|
// 切换点赞状态
|
|
|
currentDetail.value.isLiked = !currentDetail.value.isLiked;
|
|
|
@@ -746,13 +948,252 @@ const handleDetailLike = async () => {
|
|
|
};
|
|
|
|
|
|
// 显示评论
|
|
|
+// 评论相关
|
|
|
+const commentDrawerVisible = ref(false);
|
|
|
+const commentListData = ref<any[]>([]);
|
|
|
+const commentInput = ref("");
|
|
|
+const commentSubmitting = ref(false);
|
|
|
+const currentCommentDynamicId = ref<number | string>("");
|
|
|
+const replyingComment = ref<any>(null); // 当前正在回复的评论
|
|
|
+
|
|
|
const handleShowComments = () => {
|
|
|
- ElMessage.info("评论功能开发中");
|
|
|
+ if (!currentDetail.value) return;
|
|
|
+ commentDrawerVisible.value = true;
|
|
|
+ currentCommentDynamicId.value = currentDetail.value.id;
|
|
|
+};
|
|
|
+
|
|
|
+// 加载评论列表
|
|
|
+const loadCommentList = async () => {
|
|
|
+ if (!currentDetail.value) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ let params = {
|
|
|
+ businessId: String(currentDetail.value.id),
|
|
|
+ businessType: "2",
|
|
|
+ replyStatus: 0,
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ commentType: 0,
|
|
|
+ days: "",
|
|
|
+ phoneId: `store_${userStore.userInfo?.phone}`
|
|
|
+ };
|
|
|
+ const res: any = await commentList(params);
|
|
|
+ if (res.code === 200) {
|
|
|
+ commentListData.value = res.data.records || [];
|
|
|
+ // 更新评论总数
|
|
|
+ if (currentDetail.value) {
|
|
|
+ currentDetail.value.commentCount = res.data.total || 0;
|
|
|
+ }
|
|
|
+ console.log("评论列表:", commentListData.value);
|
|
|
+ console.log("评论总数:", res.data.total);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("加载评论列表失败:", error);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 提交评论
|
|
|
+const handleSubmitComment = async () => {
|
|
|
+ if (!commentInput.value.trim()) {
|
|
|
+ ElMessage.warning("请输入评论内容");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!currentDetail.value) {
|
|
|
+ ElMessage.error("动态信息不存在");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ commentSubmitting.value = true;
|
|
|
+
|
|
|
+ // 判断是回复评论还是评论动态
|
|
|
+ const isReply = !!replyingComment.value;
|
|
|
+
|
|
|
+ const params: any = {
|
|
|
+ replyId: isReply ? replyingComment.value.id : "", // 回复评论时传评论ID,否则为空
|
|
|
+ commentContent: commentInput.value,
|
|
|
+ businessType: "2",
|
|
|
+ businessId: String(currentDetail.value.id),
|
|
|
+ storeId: userStore.userInfo?.storeId || userStore.userInfo?.createdId,
|
|
|
+ commentStar: "",
|
|
|
+ phoneId: `store_${userStore.userInfo?.phone}`
|
|
|
+ };
|
|
|
+
|
|
|
+ const res: any = await saveComment(params);
|
|
|
+ if (res.code === 200) {
|
|
|
+ ElMessage.success(isReply ? "回复成功" : "评论成功");
|
|
|
+ commentInput.value = "";
|
|
|
+ replyingComment.value = null; // 清空回复状态
|
|
|
+ await loadCommentList();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || (isReply ? "回复失败" : "评论失败"));
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("提交评论失败:", error);
|
|
|
+ ElMessage.error(replyingComment.value ? "回复失败" : "评论失败");
|
|
|
+ } finally {
|
|
|
+ commentSubmitting.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 点赞评论
|
|
|
+const handleLikeComment = async (comment: any) => {
|
|
|
+ console.log(comment);
|
|
|
+ try {
|
|
|
+ // 获取当前用户的手机号,并在前面拼接 "store_"
|
|
|
+ const phone = userStore.userInfo?.phone || "";
|
|
|
+ const currentUserPhoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
+
|
|
|
+ const params = {
|
|
|
+ userId: currentUserPhoneId, // 当前用户phoneId
|
|
|
+ huifuId: comment.id, // 动态ID
|
|
|
+ type: 1 // 2表示点赞
|
|
|
+ };
|
|
|
+
|
|
|
+ // 根据当前点赞状态调用不同的接口
|
|
|
+ if (comment.isLiked) {
|
|
|
+ // 已点赞,调用取消点赞接口
|
|
|
+ await unlikeDynamicNew(params);
|
|
|
+ } else {
|
|
|
+ // 未点赞,调用点赞接口
|
|
|
+ await likeDynamicNew(params);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 切换点赞状态
|
|
|
+ comment.isLiked = !comment.isLiked;
|
|
|
+ comment.likeCount += comment.isLiked ? 1 : -1;
|
|
|
+
|
|
|
+ ElMessage.success(comment.isLiked ? "点赞成功" : "取消点赞");
|
|
|
+ } catch (error) {
|
|
|
+ console.error("列表点赞操作失败:", error);
|
|
|
+ ElMessage.error("操作失败");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 回复评论
|
|
|
+const handleReplyComment = (comment: any) => {
|
|
|
+ replyingComment.value = comment;
|
|
|
+ commentInput.value = ``;
|
|
|
+ // 聚焦到输入框
|
|
|
+ setTimeout(() => {
|
|
|
+ const textarea = document.querySelector(".comment-input-wrapper textarea") as HTMLTextAreaElement;
|
|
|
+ if (textarea) {
|
|
|
+ textarea.focus();
|
|
|
+ }
|
|
|
+ }, 100);
|
|
|
+};
|
|
|
+
|
|
|
+// 取消回复
|
|
|
+const handleCancelReply = () => {
|
|
|
+ replyingComment.value = null;
|
|
|
+ commentInput.value = "";
|
|
|
};
|
|
|
|
|
|
// 分享
|
|
|
-const handleShare = () => {
|
|
|
- ElMessage.success("分享链接已复制");
|
|
|
+const handleShare = async () => {
|
|
|
+ shareDialogVisible.value = true;
|
|
|
+ await loadShareFriendList();
|
|
|
+};
|
|
|
+
|
|
|
+// 加载好友列表
|
|
|
+const loadShareFriendList = async () => {
|
|
|
+ try {
|
|
|
+ // 获取当前用户的手机号,并在前面拼接 "store_"
|
|
|
+ const phone = userStore.userInfo?.phone || "";
|
|
|
+ const fansId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
+
|
|
|
+ const res: any = await getMutualAttention({
|
|
|
+ page: 1,
|
|
|
+ size: 1000,
|
|
|
+ fansId: fansId,
|
|
|
+ name: ""
|
|
|
+ });
|
|
|
+
|
|
|
+ if (res.code === 200) {
|
|
|
+ const dataList = res.data?.records || res.data?.list || res.data || [];
|
|
|
+ shareFriendList.value = dataList.map((item: any) => ({
|
|
|
+ id: item.id || item.userId,
|
|
|
+ name: item.userName || item.nickname || item.name || "用户",
|
|
|
+ avatar: item.userImage || item.avatar || item.headImg || "",
|
|
|
+ phoneId: item.phoneId || item.fansId || ""
|
|
|
+ }));
|
|
|
+ console.log("加载好友列表成功:", shareFriendList.value);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("加载好友列表失败:", error);
|
|
|
+ ElMessage.error("加载好友列表失败");
|
|
|
+ shareFriendList.value = [];
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 搜索好友
|
|
|
+const handleShareSearch = () => {
|
|
|
+ // 搜索由计算属性自动处理
|
|
|
+};
|
|
|
+
|
|
|
+// 选择好友
|
|
|
+const handleSelectFriend = (friend: ShareFriend) => {
|
|
|
+ const index = selectedFriends.value.indexOf(friend.id);
|
|
|
+ if (index > -1) {
|
|
|
+ // 已选择,取消选择
|
|
|
+ selectedFriends.value.splice(index, 1);
|
|
|
+ } else {
|
|
|
+ // 未选择,添加选择
|
|
|
+ selectedFriends.value.push(friend.id);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 移除已选择的好友
|
|
|
+const handleRemoveFriend = (friendId: number) => {
|
|
|
+ const index = selectedFriends.value.indexOf(friendId);
|
|
|
+ if (index > -1) {
|
|
|
+ selectedFriends.value.splice(index, 1);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 确认分享
|
|
|
+const handleConfirmShare = async () => {
|
|
|
+ if (selectedFriends.value.length === 0) {
|
|
|
+ ElMessage.warning("请选择要分享的好友");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!currentDetail.value) {
|
|
|
+ ElMessage.error("动态信息不存在");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ shareSubmitting.value = true;
|
|
|
+
|
|
|
+ // 调用 addTransferCount 接口,传递动态 id
|
|
|
+ const res: any = await addTransferCount({
|
|
|
+ id: currentDetail.value.id
|
|
|
+ });
|
|
|
+
|
|
|
+ if (res.code === 200) {
|
|
|
+ ElMessage.success(`已分享给 ${selectedFriends.value.length} 位好友`);
|
|
|
+ shareDialogVisible.value = false;
|
|
|
+
|
|
|
+ // 可以在这里更新动态的分享数(如果需要的话)
|
|
|
+ console.log("分享成功,动态ID:", currentDetail.value.id);
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || "分享失败");
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error("分享失败:", error);
|
|
|
+ ElMessage.error("分享失败");
|
|
|
+ } finally {
|
|
|
+ shareSubmitting.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 关闭分享对话框
|
|
|
+const handleCloseShareDialog = () => {
|
|
|
+ shareSearch.value = "";
|
|
|
+ selectedFriends.value = [];
|
|
|
+ shareFriendList.value = [];
|
|
|
};
|
|
|
|
|
|
// 查看用户主页
|
|
|
@@ -796,6 +1237,7 @@ const handleFollowInDetail = async () => {
|
|
|
const listItem = dynamicList.value.find(item => item.id === currentDetail.value?.id);
|
|
|
if (listItem) {
|
|
|
listItem.isFollowed = 1;
|
|
|
+ listItem.isFollowThis = 1; // 同时更新列表项的isFollowThis状态
|
|
|
}
|
|
|
|
|
|
ElMessage.success("关注成功");
|
|
|
@@ -1269,9 +1711,7 @@ onMounted(() => {
|
|
|
padding: 8px;
|
|
|
font-size: 24px;
|
|
|
color: #ffffff;
|
|
|
- &:hover {
|
|
|
- background: rgb(255 255 255 / 10%);
|
|
|
- }
|
|
|
+ background: #ffffff;
|
|
|
}
|
|
|
}
|
|
|
.detail-content {
|
|
|
@@ -1628,5 +2068,251 @@ onMounted(() => {
|
|
|
justify-content: flex-end;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // 评论侧边栏样式
|
|
|
+ .comment-list-container {
|
|
|
+ flex: 1;
|
|
|
+ height: calc(100vh - 200px);
|
|
|
+ padding: 0 20px;
|
|
|
+ overflow-y: auto;
|
|
|
+ .comment-list {
|
|
|
+ .comment-item {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 16px 0;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+ &:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+ .comment-avatar {
|
|
|
+ display: flex;
|
|
|
+ flex-shrink: 0;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-radius: 50%;
|
|
|
+ img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .comment-content-wrapper {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ .comment-header,
|
|
|
+ .store-comment-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ .comment-user-name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+ .comment-time {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .comment-text {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 1.6;
|
|
|
+ color: #606266;
|
|
|
+ word-break: break-all;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 商家回复样式
|
|
|
+ .store-comment-wrapper {
|
|
|
+ padding: 12px;
|
|
|
+ margin-top: 12px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 8px;
|
|
|
+ .store-comment-item {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ .store-comment-avatar {
|
|
|
+ display: flex;
|
|
|
+ flex-shrink: 0;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #ffffff;
|
|
|
+ border-radius: 50%;
|
|
|
+ img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .store-comment-content {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ .store-comment-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ .store-comment-user-name {
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ .store-comment-time {
|
|
|
+ font-size: 11px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .store-comment-text {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ font-size: 13px;
|
|
|
+ line-height: 1.5;
|
|
|
+ color: #606266;
|
|
|
+ word-break: break-all;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .comment-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 20px;
|
|
|
+ .comment-action-item {
|
|
|
+ display: flex;
|
|
|
+ gap: 4px;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: color 0.3s;
|
|
|
+ &:hover {
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ span {
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .comment-input-wrapper {
|
|
|
+ position: absolute;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ padding: 16px 20px;
|
|
|
+ background: #ffffff;
|
|
|
+ border-top: 1px solid #e4e7ed;
|
|
|
+ box-shadow: 0 -2px 8px rgb(0 0 0 / 5%);
|
|
|
+ .reply-hint {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 8px 12px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+ .reply-text {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ .cancel-reply {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #909399;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: color 0.3s;
|
|
|
+ &:hover {
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ :deep(.el-textarea) {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+ .el-button {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分享对话框
|
|
|
+ .share-dialog-content {
|
|
|
+ .search-box {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+ .share-friend-list {
|
|
|
+ max-height: 400px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ overflow-y: auto;
|
|
|
+ .share-friend-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ border-radius: 8px;
|
|
|
+ transition: background-color 0.3s;
|
|
|
+ &:hover {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ }
|
|
|
+ .friend-info {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+ .friend-avatar {
|
|
|
+ display: flex;
|
|
|
+ flex-shrink: 0;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ overflow: hidden;
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-radius: 50%;
|
|
|
+ img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .friend-name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .selected-friends {
|
|
|
+ padding: 12px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 8px;
|
|
|
+ .selected-title {
|
|
|
+ margin-bottom: 8px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #606266;
|
|
|
+ }
|
|
|
+ .selected-list {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ .el-tag {
|
|
|
+ max-width: 120px;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|