zhuli 6 napja
szülő
commit
fba5eeff4a

+ 18 - 0
src/api/modules/dynamicManagement.ts

@@ -181,6 +181,19 @@ export const likeDynamicNew = (params: {
   return httpStore.post(PORT_NONE + `/comment/like`, formData);
 };
 
+// 取消点赞动态(新接口)- 表单方式提交
+export const unlikeDynamicNew = (params: {
+  userId: string; // 当前用户phoneId
+  huifuId: number; // 动态ID
+  type: number; // 类型:2表示点赞
+}) => {
+  const formData = new FormData();
+  formData.append("userId", params.userId);
+  formData.append("huifuId", String(params.huifuId));
+  formData.append("type", String(params.type));
+  return httpStore.post(PORT_NONE + `/comment/cancelLike`, formData);
+};
+
 // 列表点赞动态 - 表单方式提交
 export const likeDynamicList = (params: {
   dynamicId: number; // 动态ID
@@ -267,3 +280,8 @@ export const likeComment = (params: { commentId: number }) => {
 export const unlikeComment = (params: { commentId: number }) => {
   return http.post(PORT_NONE + `/dynamic/unlikeComment`, params);
 };
+
+//评论列表
+export const commentList = (params: { commentId: number }) => {
+  return http.post(PORT_NONE + `/storeComment/getList`, params);
+};

+ 56 - 0
src/api/modules/newLoginApi.ts

@@ -88,6 +88,16 @@ export const getMutualAttention = (params: any) => {
   return httpLogin.get(`/alienStore/stores/getMutualAttention`, params);
 };
 
+//关注列表
+export const getMyFollowed = (params: any) => {
+  return httpLogin.get(`/alienStore/stores/getMyFollowed`, params);
+};
+
+//粉丝列表
+export const getMyUserFans = (params: any) => {
+  return httpLogin.get(`/alienStore/stores/getMyUserFans`, params);
+};
+
 // 优惠券列表
 export const getCouponList = (params: any) => {
   return httpLogin.get(`/alienStore/life-discount-coupon/getStoreAllCouponListPaginateNot`, params);
@@ -142,3 +152,49 @@ export const getAppealDetail = (params: any) => {
 export const addAppealNew = (params: any) => {
   return httpLogin.post(`/alienStore/storeCommentAppeal/addAppealNew`, params);
 };
+
+//发表评论
+export const saveComment = (params: any) => {
+  const formData = new FormData();
+  Object.keys(params).forEach(key => {
+    if (params[key] !== undefined && params[key] !== null) {
+      formData.append(key, params[key]);
+    }
+  });
+  return httpLogin.post(`/alienStore/storeComment/saveComment`, formData, {
+    "Content-Type": "multipart/form-data"
+  });
+};
+
+//评论列表
+export const commentList = (params: any) => {
+  return httpLogin.get(`/alienStore/storeComment/getList`, params);
+};
+
+//我的动态
+export const getUserDynamics = (params: any) => {
+  return httpLogin.get(`/alienStore/userDynamics/getUserDynamics`, params);
+};
+
+//本地草稿
+export const getUserDraftDynamics = (params: any) => {
+  return httpLogin.get(`/alienStore/userDynamics/getUserDraftDynamics`, params);
+};
+//我的动态   好友   关注    粉丝
+export const getHomePageInfo = (params: any) => {
+  return httpLogin.get(`/alienStore/stores/getHomePageInfo`, params);
+};
+
+//我的动态   赞过列表
+export const getDianZanList = (params: any) => {
+  return httpLogin.get(`/alienStore/userDynamics/getDianZanList`, params);
+};
+
+//确认分享好友
+export const addTransferCount = (params: any) => {
+  return httpLogin.get(`/alienStore/userDynamics/addTransferCount`, params);
+};
+//删除草稿
+export const deleteDynamicsById = (params: any) => {
+  return httpLogin.get(`/alienStore/userDynamics/deleteDynamicsById`, params);
+};

+ 15 - 0
src/assets/json/authMenuList.json

@@ -816,6 +816,21 @@
             "isAffix": false,
             "isKeepAlive": false
           }
+        },
+        {
+          "path": "/dynamicManagement/draftDynamic",
+          "name": "draftDynamic",
+          "component": "/dynamicManagement/draftDynamic",
+          "meta": {
+            "icon": "Document",
+            "title": "好友优惠券详情",
+            "activeMenu": "/dynamicManagement/draftDynamic",
+            "isLink": "",
+            "isHide": true,
+            "isFull": false,
+            "isAffix": false,
+            "isKeepAlive": false
+          }
         }
       ]
     }

+ 435 - 0
src/views/dynamicManagement/draftDynamic.vue

@@ -0,0 +1,435 @@
+<template>
+  <div class="draft-dynamic-container">
+    <!-- 头部 -->
+    <div class="header-section">
+      <el-button @click="handleGoBack"> 返回 </el-button>
+      <div class="header-title">本地草稿</div>
+      <div class="header-right" />
+    </div>
+
+    <!-- 草稿列表 -->
+    <div v-if="draftList.length > 0" class="content-section">
+      <div class="content-grid">
+        <div v-for="item in draftList" :key="item.id" class="content-card" @click="handleEditDraft(item)">
+          <!-- 封面图片/视频区域 -->
+          <div class="content-cover-wrapper">
+            <img v-if="item.coverUrl" :src="item.coverUrl" :alt="item.title" class="content-cover" />
+            <div v-else class="cover-placeholder">
+              <el-icon :size="48" color="#999">
+                <Picture />
+              </el-icon>
+            </div>
+
+            <!-- 视频播放按钮 -->
+            <div v-if="item.type === 'video'" class="play-overlay">
+              <el-icon :size="40" color="#fff">
+                <VideoPlay />
+              </el-icon>
+            </div>
+          </div>
+
+          <!-- 底部信息 -->
+          <div class="content-info">
+            <div class="content-title">
+              {{ item.title || "未命名" }}
+            </div>
+            <div class="content-meta">
+              <span class="meta-time">{{ formatTime(item.createdTime) }}</span>
+            </div>
+          </div>
+
+          <!-- 操作按钮 -->
+          <div class="card-actions">
+            <el-button size="small" type="primary" plain @click.stop="handleEditDraft(item)">
+              <el-icon>
+                <Edit />
+              </el-icon>
+              编辑
+            </el-button>
+            <el-button size="small" type="danger" plain @click.stop="handleDeleteDraft(item)">
+              <el-icon>
+                <Delete />
+              </el-icon>
+              删除
+            </el-button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 空状态 -->
+    <div v-else class="empty-section">
+      <el-empty description="暂无数据" />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="draftDynamic">
+import { ref, onMounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { ArrowLeft, Picture, VideoPlay, Edit, Delete, Plus } from "@element-plus/icons-vue";
+import { getUserDraftDynamics, deleteDynamicsById } from "@/api/modules/newLoginApi";
+import { deleteDraft } from "@/api/modules/dynamicManagement";
+import { useUserStore } from "@/stores/modules/user";
+
+const router = useRouter();
+const userStore = useUserStore();
+
+// 接口定义
+interface DraftItem {
+  id: number;
+  title: string;
+  coverUrl: string;
+  type: "image" | "video";
+  createdTime: string;
+  context?: string;
+  imagePath?: string;
+  addressName?: string;
+  address?: string;
+  addressProvince?: string;
+}
+
+// 响应式数据
+const draftList = ref<DraftItem[]>([]);
+const loading = ref(false);
+
+// 返回上一页
+const handleGoBack = () => {
+  router.back();
+};
+
+// 格式化时间
+const formatTime = (time: string) => {
+  if (!time) return "";
+
+  const date = new Date(time);
+  const now = new Date();
+  const diff = now.getTime() - date.getTime();
+
+  // 小于1分钟
+  if (diff < 60000) {
+    return "刚刚";
+  }
+
+  // 小于1小时
+  if (diff < 3600000) {
+    return `${Math.floor(diff / 60000)}分钟前`;
+  }
+
+  // 小于1天
+  if (diff < 86400000) {
+    return `${Math.floor(diff / 3600000)}小时前`;
+  }
+
+  // 小于7天
+  if (diff < 604800000) {
+    return `${Math.floor(diff / 86400000)}天前`;
+  }
+
+  // 显示具体日期
+  const year = date.getFullYear();
+  const month = String(date.getMonth() + 1).padStart(2, "0");
+  const day = String(date.getDate()).padStart(2, "0");
+
+  if (year === now.getFullYear()) {
+    return `${month}-${day}`;
+  }
+
+  return `${year}-${month}-${day}`;
+};
+
+// 加载草稿列表
+const loadDraftList = async () => {
+  try {
+    loading.value = true;
+
+    // 获取当前用户的手机号,并在前面拼接 "store_"
+    const phone = userStore.userInfo?.phone || "";
+    const phoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
+
+    // 调用接口获取草稿列表
+    const res: any = await getUserDraftDynamics({
+      phoneId: phoneId,
+      page: 1,
+      size: 1000
+    });
+
+    if (res.code === 200) {
+      const dataList = res.data?.records || res.data?.list || [];
+
+      draftList.value = dataList.map((item: any) => {
+        const firstImage = item.imagePath ? item.imagePath.split(",")[0] : "";
+        const isVideo = firstImage.toLowerCase().endsWith(".mp4");
+
+        return {
+          id: item.id,
+          title: item.title || item.context || "未命名",
+          coverUrl: firstImage,
+          type: isVideo ? "video" : "image",
+          createdTime: item.createdTime || item.createdTime || new Date().toISOString(),
+          // 保留原始数据,用于编辑
+          ...item
+        };
+      });
+
+      console.log("加载草稿列表成功:", draftList.value);
+    }
+  } catch (error) {
+    console.error("加载草稿列表失败:", error);
+    ElMessage.error("加载草稿列表失败");
+  } finally {
+    loading.value = false;
+  }
+};
+
+// 编辑草稿
+const handleEditDraft = (item: DraftItem) => {
+  // 跳转到发布动态页面,并传递草稿ID
+  router.push({
+    path: "/dynamicManagement/publishDynamic",
+    query: {
+      draftId: item.id,
+      mode: "edit"
+    }
+  });
+};
+
+// 删除草稿
+const handleDeleteDraft = async (item: DraftItem) => {
+  try {
+    await ElMessageBox.confirm("确定要删除这条草稿吗?删除后将无法恢复。", "删除确认", {
+      confirmButtonText: "确定删除",
+      cancelButtonText: "取消",
+      type: "warning",
+      confirmButtonClass: "el-button--danger"
+    });
+
+    // 调用删除接口
+    await deleteDynamicsById({
+      id: item.id
+    });
+
+    ElMessage.success("删除成功");
+
+    // 从列表中移除该项
+    const index = draftList.value.findIndex(draft => draft.id === item.id);
+    if (index > -1) {
+      draftList.value.splice(index, 1);
+    }
+  } catch (error: any) {
+    // 用户取消删除或删除失败
+    if (error !== "cancel") {
+      console.error("删除草稿失败:", error);
+      ElMessage.error("删除草稿失败");
+    }
+  }
+};
+
+// 创建新动态
+const handleCreateNew = () => {
+  router.push("/dynamicManagement/publishDynamic");
+};
+
+// 初始化
+onMounted(() => {
+  loadDraftList();
+});
+</script>
+
+<style scoped lang="scss">
+.draft-dynamic-container {
+  min-height: calc(100vh - 120px);
+  padding: 20px;
+  background: #ffffff;
+
+  // 头部区域
+  .header-section {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding-bottom: 16px;
+    margin-bottom: 24px;
+    border-bottom: 1px solid #e4e7ed;
+    .back-btn {
+      display: flex;
+      gap: 4px;
+      align-items: center;
+      padding: 8px 16px;
+      font-size: 15px;
+      color: #606266;
+      transition: all 0.3s;
+      &:hover {
+        color: #409eff;
+        background: #ecf5ff;
+      }
+    }
+    .header-title {
+      flex: 1;
+      font-size: 20px;
+      font-weight: 600;
+      color: #303133;
+      text-align: center;
+    }
+    .header-right {
+      width: 100px;
+    }
+  }
+
+  // 内容区域
+  .content-section {
+    margin-top: 20px;
+  }
+
+  // 内容网格布局
+  .content-grid {
+    display: grid;
+    grid-template-columns: repeat(4, 1fr);
+    gap: 20px;
+
+    @media (width <= 1400px) {
+      grid-template-columns: repeat(3, 1fr);
+    }
+
+    @media (width <= 1024px) {
+      grid-template-columns: repeat(2, 1fr);
+    }
+
+    @media (width <= 768px) {
+      grid-template-columns: repeat(1, 1fr);
+    }
+  }
+
+  // 内容卡片
+  .content-card {
+    position: relative;
+    overflow: hidden;
+    cursor: pointer;
+    background: #ffffff;
+    border: 1px solid #e4e7ed;
+    border-radius: 8px;
+    transition: all 0.3s;
+    &:hover {
+      border-color: #409eff;
+      box-shadow: 0 2px 12px rgb(0 0 0 / 10%);
+      transform: translateY(-2px);
+      .card-actions {
+        opacity: 1;
+      }
+      .play-overlay {
+        opacity: 1;
+      }
+    }
+
+    // 封面区域
+    .content-cover-wrapper {
+      position: relative;
+      width: 100%;
+      aspect-ratio: 16 / 9;
+      overflow: hidden;
+      background: #f5f7fa;
+      .content-cover {
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+      }
+      .cover-placeholder {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 100%;
+        height: 100%;
+        background: #f5f7fa;
+      }
+
+      // 播放按钮覆盖层
+      .play-overlay {
+        position: absolute;
+        inset: 0;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        background: rgb(0 0 0 / 30%);
+        opacity: 0;
+        transition: opacity 0.3s;
+        .el-icon {
+          filter: drop-shadow(0 2px 4px rgb(0 0 0 / 20%));
+        }
+      }
+
+      // 草稿标签
+      .draft-label {
+        position: absolute;
+        top: 8px;
+        left: 8px;
+        display: flex;
+        gap: 4px;
+        align-items: center;
+        padding: 4px 10px;
+        font-size: 12px;
+        color: #ffffff;
+        background: rgb(255 152 0 / 90%);
+        backdrop-filter: blur(4px);
+        border-radius: 4px;
+        .el-icon {
+          font-size: 12px;
+        }
+      }
+    }
+
+    // 内容信息
+    .content-info {
+      padding: 12px;
+      .content-title {
+        margin-bottom: 8px;
+        overflow: hidden;
+        font-size: 15px;
+        font-weight: 500;
+        line-height: 1.4;
+        color: #303133;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+      .content-meta {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        font-size: 13px;
+        color: #909399;
+        .meta-time {
+          display: flex;
+          gap: 4px;
+          align-items: center;
+        }
+      }
+    }
+
+    // 操作按钮
+    .card-actions {
+      position: absolute;
+      inset: 0;
+      display: flex;
+      gap: 12px;
+      align-items: center;
+      justify-content: center;
+      background: rgb(0 0 0 / 60%);
+      opacity: 0;
+      transition: opacity 0.3s;
+    }
+  }
+
+  // 空状态
+  .empty-section {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-height: 400px;
+    padding: 80px 0;
+    :deep(.el-empty) {
+      .el-button {
+        margin-top: 16px;
+      }
+    }
+  }
+}
+</style>

+ 0 - 1
src/views/dynamicManagement/friendCouponDetail.vue

@@ -284,7 +284,6 @@ const getStatusText = () => {
     margin: 0 0 20px;
     font-size: 16px;
     color: #303133;
-    border-bottom: 2px solid #e4e7ed;
   }
 }
 

+ 744 - 58
src/views/dynamicManagement/index.vue

@@ -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
+                  >&nbsp;&nbsp;
                   <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
+                  >&nbsp;&nbsp;
                   <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
+                  >&nbsp;&nbsp;
                   <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
+                        >&nbsp;
+                        <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>

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 746 - 108
src/views/dynamicManagement/myDynamic.vue


+ 239 - 71
src/views/dynamicManagement/publishDynamic.vue

@@ -1,7 +1,10 @@
 <template>
   <div class="publish-dynamic-container">
     <!-- 头部导航 -->
-
+    <div class="header-section">
+      <el-button @click="handleGoBack" class="back-btn"> 返回 </el-button>
+      <div class="header-title">动态发布</div>
+    </div>
     <!-- 表单内容 -->
     <div class="form-container">
       <el-form ref="formRef" :model="formData" :rules="rules" label-position="top">
@@ -67,21 +70,26 @@
 
         <!-- 位置 -->
         <el-form-item label="位置" prop="location">
-          <el-input v-model="formData.location" placeholder="请选择或许位置" size="large" readonly @click="handleSelectLocation">
-            <template #suffix>
-              <el-icon color="#999">
-                <Location />
-              </el-icon>
-            </template>
-          </el-input>
+          <el-select
+            v-model="formData.storePosition"
+            filterable
+            placeholder="请输入地址进行查询"
+            remote
+            reserve-keyword
+            :remote-method="getLonAndLat"
+            @change="selectAddress"
+          >
+            <el-option v-for="item in addressList" :key="item.id" :label="item.name" :value="item.location">
+              <span style="float: left">{{ item.name }}</span>
+              <span style="float: right; font-size: 13px; color: var(--el-text-color-secondary)">{{ item.district }}</span>
+            </el-option>
+          </el-select>
         </el-form-item>
       </el-form>
-    </div>
-
-    <!-- 底部操作按钮 -->
-    <div class="footer-actions">
-      <el-button size="large" class="draft-btn" @click="handleSaveDraft"> 保存草稿 </el-button>
-      <el-button type="primary" size="large" class="publish-btn" :loading="publishing" @click="handlePublish"> 发布 </el-button>
+      <div class="footer-actions">
+        <el-button size="large" class="draft-btn" @click="handleSaveDraft"> 保存草稿 </el-button>
+        <el-button type="primary" size="large" class="publish-btn" :loading="publishing" @click="handlePublish"> 发布 </el-button>
+      </div>
     </div>
 
     <!-- 图片/视频预览对话框 -->
@@ -94,7 +102,7 @@
 
     <!-- 位置选择对话框 -->
     <el-dialog v-model="locationDialogVisible" title="选择位置" width="500px" append-to-body>
-      <el-input v-model="locationSearch" placeholder="搜索位置" clearable @input="handleSearchLocation">
+      <el-input v-model="locationSearch" placeholder="搜索位置" clearable>
         <template #prefix>
           <el-icon>
             <Search />
@@ -116,7 +124,7 @@
 
 <script setup lang="ts" name="publishDynamic">
 import { ref, reactive, onMounted } from "vue";
-import { useRouter } from "vue-router";
+import { useRouter, useRoute } from "vue-router";
 import { ElMessage, ElMessageBox } from "element-plus";
 import { ArrowLeft, Plus, Location, Search, ZoomIn, Delete } from "@element-plus/icons-vue";
 import type { FormInstance, FormRules, UploadUserFile, UploadFile } from "element-plus";
@@ -124,8 +132,10 @@ import type { FormInstance, FormRules, UploadUserFile, UploadFile } from "elemen
 import { addOrUpdateDynamic } from "@/api/modules/dynamicManagement";
 import { uploadImg } from "@/api/modules/upload";
 import { useUserStore } from "@/stores/modules/user";
+import { getUserDraftDynamics, getInputPrompt } from "@/api/modules/newLoginApi";
 
 const router = useRouter();
+const route = useRoute();
 const userStore = useUserStore();
 
 // 接口定义
@@ -137,6 +147,10 @@ interface FormData {
   locationId?: string;
   address?: string; // 经纬度
   addressProvince?: string; // 省市
+  storePosition: string;
+  queryAddress: string;
+  storePositionLongitude: string;
+  storePositionLatitude: string;
 }
 
 interface LocationItem {
@@ -160,6 +174,8 @@ const locationList = ref<LocationItem[]>([]);
 const publishing = ref(false);
 const pendingUploadFiles = ref<UploadFile[]>([]);
 const uploading = ref(false);
+const currentDraftId = ref<number | string | null>(null); // 当前编辑的草稿ID
+const isEditMode = ref(false); // 是否为编辑模式
 
 // 表单数据
 const formData = reactive<FormData>({
@@ -168,8 +184,57 @@ const formData = reactive<FormData>({
   images: [],
   location: "",
   address: "", // 经纬度
-  addressProvince: "" // 省市
+  addressProvince: "", // 省市
+  storePosition: "",
+  queryAddress: "",
+  storePositionLongitude: "",
+  storePositionLatitude: ""
 });
+// 经纬度查询
+const addressMap = new Map();
+const addressList = ref<any[]>([]);
+const getLonAndLat = async (keyword: string) => {
+  if (keyword) {
+    console.log("地址查询", keyword);
+    let param = {
+      addressName: keyword
+    };
+    let res: any = await getInputPrompt(param as any);
+    if (res.code == "200") {
+      res.data.tips.forEach((item: any) => {
+        addressMap.set(item.location, item.name);
+      });
+      addressList.value = res?.data?.tips || [];
+      console.log("res", res);
+    } else {
+      ElMessage.error("新增失败!");
+    }
+  } else {
+    addressList.value = [];
+  }
+};
+const selectAddress = async (param: any) => {
+  if (!formData.storePosition || typeof formData.storePosition !== "string") {
+    ElMessage.warning("地址格式不正确,请重新选择");
+    return;
+  }
+  if (!formData.storePosition.includes(",")) {
+    ElMessage.warning("地址格式不正确,缺少经纬度信息");
+    return;
+  }
+  // 安全地分割地址字符串
+  let locationList = formData.storePosition.split(",");
+  // 查找对应的地址名称
+  addressList.value.forEach((item: any) => {
+    console.log(item);
+    if (item.location == formData.storePosition) {
+      formData.queryAddress = item.name;
+      formData.addressProvince = item.district;
+    }
+  });
+  formData.storePositionLongitude = locationList[0]?.trim();
+  formData.storePositionLatitude = locationList[1]?.trim();
+};
 
 // 表单验证规则
 const rules = reactive<FormRules<FormData>>({
@@ -425,38 +490,6 @@ const handleRemoveFile = (file: UploadUserFile) => {
 };
 
 // 选择位置
-const handleSelectLocation = () => {
-  locationDialogVisible.value = true;
-  // 加载位置列表
-  loadLocationList();
-};
-
-// 搜索位置
-const handleSearchLocation = () => {
-  // TODO: 实现位置搜索
-  loadLocationList(locationSearch.value);
-};
-
-// 加载位置列表
-const loadLocationList = (keyword?: string) => {
-  // TODO: 集成真实接口
-  // 模拟数据
-  const mockLocations: LocationItem[] = [
-    { id: "1", name: "天安门广场", address: "北京市东城区" },
-    { id: "2", name: "故宫博物院", address: "北京市东城区景山前街4号" },
-    { id: "3", name: "颐和园", address: "北京市海淀区新建宫门路19号" },
-    { id: "4", name: "长城", address: "北京市延庆区" },
-    { id: "5", name: "鸟巢", address: "北京市朝阳区国家体育场南路1号" }
-  ];
-
-  if (keyword) {
-    locationList.value = mockLocations.filter(loc => loc.name.includes(keyword) || loc.address.includes(keyword));
-  } else {
-    locationList.value = mockLocations;
-  }
-};
-
-// 选择位置
 const handleChooseLocation = (location: LocationItem) => {
   formData.location = location.name;
   formData.locationId = location.id;
@@ -498,7 +531,7 @@ const handleSaveDraft = async () => {
     const createId = userStore.userInfo?.id || userStore.userInfo?.userId || 0;
 
     // 调用保存草稿接口
-    await addOrUpdateDynamic({
+    const params: any = {
       title: formData.title || "未命名",
       context: formData.content || "", // 正文
       imagePath: formData.images.join(","), // 多个图片/视频用逗号分隔
@@ -506,10 +539,17 @@ const handleSaveDraft = async () => {
       createId: createId, // 创建者ID
       type: "2", // 动态类型
       draft: 1, // ✅ 1表示草稿
-      addressName: formData.location || "", // 地址名称
-      address: formData.address || "", // 经纬度
+      addressName: formData.queryAddress, // 地址名称
+      address: formData.storePositionLongitude + "," + formData.storePositionLatitude, // 经纬度
       addressProvince: formData.addressProvince || "" // 省市
-    });
+    };
+
+    // 如果是编辑模式,传递ID
+    if (isEditMode.value && currentDraftId.value) {
+      params.id = currentDraftId.value;
+    }
+
+    await addOrUpdateDynamic(params);
 
     ElMessage.success("草稿保存成功");
     router.back();
@@ -546,7 +586,7 @@ const handlePublish = async () => {
         const createId = userStore.userInfo?.id || userStore.userInfo?.userId || 0;
 
         // 调用发布动态接口
-        await addOrUpdateDynamic({
+        const params: any = {
           title: formData.title,
           context: formData.content, // 正文
           imagePath: formData.images.join(","), // 多个图片/视频用逗号分隔
@@ -554,10 +594,17 @@ const handlePublish = async () => {
           createId: createId, // 创建者ID
           type: "2", // 动态类型
           draft: 0, // 0表示发布
-          addressName: formData.location || "", // 地址名称
-          address: formData.address || "", // 经纬度
+          addressName: formData.queryAddress, // 地址名称
+          address: formData.storePositionLongitude + "," + formData.storePositionLatitude, // 经纬度
           addressProvince: formData.addressProvince || "" // 省市
-        });
+        };
+
+        // 如果是编辑模式,传递ID
+        if (isEditMode.value && currentDraftId.value) {
+          params.id = currentDraftId.value;
+        }
+
+        await addOrUpdateDynamic(params);
 
         ElMessage.success("发布成功");
         router.back();
@@ -571,17 +618,139 @@ const handlePublish = async () => {
   });
 };
 
+// 加载草稿数据
+const loadDraftData = async (draftId: string | number) => {
+  try {
+    console.log("加载草稿数据,ID:", draftId);
+
+    // 获取当前用户的手机号
+    const phone = userStore.userInfo?.phone || "";
+    const phoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
+
+    // 调用接口获取草稿列表
+    const res: any = await getUserDraftDynamics({
+      phoneId: phoneId,
+      page: 1,
+      size: 1000
+    });
+
+    if (res.code === 200) {
+      const dataList = res.data?.records || res.data?.list || [];
+
+      // 查找指定的草稿
+      const draft = dataList.find((item: any) => item.id == draftId);
+
+      if (draft) {
+        console.log("找到草稿数据:", draft);
+
+        // 回显标题
+        formData.title = draft.title || "";
+
+        // 回显正文
+        formData.content = draft.context || "";
+
+        // 回显位置
+        formData.location = draft.addressName || "";
+        formData.address = draft.address || "";
+        formData.addressProvince = draft.addressProvince || "";
+        formData.storePosition = draft.address || ""; // 回显到下拉框(经纬度格式)
+        formData.queryAddress = draft.addressName || ""; // 回显地址名称
+        formData.storePositionLongitude = draft.address ? draft.address.split(",")[0]?.trim() : "";
+        formData.storePositionLatitude = draft.address ? draft.address.split(",")[1]?.trim() : "";
+
+        // 如果有地址,需要将其添加到 addressList 中以便下拉框显示
+        if (draft.address && draft.addressName) {
+          addressList.value = [
+            {
+              id: draft.id || Date.now(),
+              name: draft.addressName,
+              location: draft.address,
+              district: draft.addressProvince || ""
+            }
+          ];
+        }
+
+        // 回显图片/视频
+        if (draft.imagePath) {
+          const imageUrls = draft.imagePath.split(",").filter((url: string) => url.trim());
+          formData.images = imageUrls;
+
+          // 构建文件列表用于显示
+          fileList.value = imageUrls.map((url: string, index: number) => {
+            const isVideo = url.toLowerCase().endsWith(".mp4");
+            return {
+              uid: Date.now() + index,
+              name: isVideo ? `video_${index + 1}.mp4` : `image_${index + 1}.jpg`,
+              status: "success" as const,
+              url: url,
+              response: { url: url }
+            };
+          });
+
+          console.log("回显图片/视频列表:", fileList.value);
+        }
+
+        ElMessage.success("草稿数据加载成功");
+      } else {
+        ElMessage.warning("未找到指定的草稿");
+      }
+    }
+  } catch (error) {
+    console.error("加载草稿数据失败:", error);
+    ElMessage.error("加载草稿数据失败");
+  }
+};
+
 // 初始化
 onMounted(() => {
-  // 可以在这里加载草稿数据
+  // 检查是否为编辑草稿模式
+  const draftId = route.query.draftId;
+  const mode = route.query.mode;
+
+  if (draftId && mode === "edit") {
+    console.log("进入编辑草稿模式, draftId:", draftId);
+    isEditMode.value = true;
+    currentDraftId.value = draftId as string | number;
+    loadDraftData(draftId as string | number);
+  }
 });
 </script>
 
 <style scoped lang="scss">
+.header-section {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 16px 0;
+  border-bottom: 1px solid #e4e7ed;
+  .back-btn {
+    display: flex;
+    gap: 4px;
+    align-items: center;
+    padding: 8px 16px;
+    margin-left: 20px;
+    font-size: 15px;
+    color: #606266;
+    transition: all 0.3s;
+    &:hover {
+      color: #409eff;
+      background: #ecf5ff;
+    }
+  }
+  .header-title {
+    flex: 1;
+    font-size: 20px;
+    font-weight: 600;
+    color: #303133;
+    text-align: center;
+  }
+  .header-right {
+    width: 100px;
+  }
+}
 .publish-dynamic-container {
   display: flex;
   flex-direction: column;
-  min-height: 100vh;
   background: #ffffff;
 
   // 头部导航
@@ -628,13 +797,13 @@ onMounted(() => {
     flex: 1;
     width: 100%;
     max-width: 800px;
-    padding: 30px 20px 100px;
+    padding: 30px 20px;
     margin: 0 auto;
     :deep(.el-form) {
       .el-form-item {
-        margin-bottom: 30px;
+        margin-bottom: 15px;
         .el-form-item__label {
-          padding-bottom: 12px;
+          padding-bottom: 5px;
           font-size: 16px;
           font-weight: 600;
           color: #303133;
@@ -650,9 +819,9 @@ onMounted(() => {
         gap: 8px;
       }
       :deep(.el-upload--picture-card) {
-        width: 148px;
-        height: 148px;
-        border: 2px dashed #dcdfe6;
+        width: 138px;
+        height: 138px;
+        border: 1px dashed #dcdfe6;
         border-radius: 8px;
         transition: all 0.3s;
         &:hover {
@@ -672,8 +841,8 @@ onMounted(() => {
         }
       }
       :deep(.el-upload-list--picture-card .el-upload-list__item) {
-        width: 148px;
-        height: 148px;
+        width: 138px;
+        height: 138px;
         margin: 0;
         border-radius: 8px;
       }
@@ -741,13 +910,12 @@ onMounted(() => {
 
   // 底部操作按钮
   .footer-actions {
-    z-index: 99;
     display: flex;
-    gap: 16px;
     justify-content: center;
     .el-button {
-      min-width: 180px;
-      height: 48px;
+      min-width: 150px;
+      height: 45px;
+      margin-top: 20px;
       font-size: 16px;
       border-radius: 8px;
     }

+ 44 - 15
src/views/dynamicManagement/reviewAppeal.vue

@@ -83,7 +83,7 @@
           </div>
 
           <div v-for="(itm, idx) in review.storeComment" :key="idx">
-            <div class="sjhf">我的回复: {{ itm.commentContent }}</div>
+            <div class="sjhf">商家回复: {{ itm.commentContent }}</div>
           </div>
 
           <div v-if="review.images && review.images.length > 0" class="review-images">
@@ -98,7 +98,16 @@
           </div>
 
           <div class="review-footer">
-            <el-button type="primary" link :disabled="review.isAppealed" @click="delReviewAppeal(review)"> 申诉删除 </el-button>
+            <el-button type="warning" link v-if="review.appealStatus == 0"> 审核中 </el-button>
+            <el-button
+              type="danger"
+              link
+              v-if="review.appealStatus != 0"
+              :disabled="review.appealFlag == 1 && review.appealStatus != 1 ? true : false"
+              @click="delReviewAppeal(review)"
+            >
+              申诉删除
+            </el-button>
 
             <el-button type="primary" link @click="openReplayDialog(review)"> 回复 </el-button>
           </div>
@@ -132,7 +141,7 @@
             type="textarea"
             :rows="4"
             placeholder="请输入申诉原因"
-            maxlength="500"
+            maxlength="300"
             show-word-limit
           />
         </el-form-item>
@@ -188,10 +197,12 @@ import { useRouter } from "vue-router";
 import { ElMessage } from "element-plus";
 import { User, Plus } from "@element-plus/icons-vue";
 import type { FormInstance, FormRules, UploadUserFile } from "element-plus";
-import { getList, addAppealNew, uploadImg } from "@/api/modules/newLoginApi";
+import { getList, addAppealNew, saveComment, uploadImg } from "@/api/modules/newLoginApi";
 import { localGet } from "@/utils";
+import { useUserStore } from "@/stores/modules/user";
 
 const router = useRouter();
+const userStore = useUserStore();
 
 // 店铺名称
 const storeName = ref("重庆老火锅");
@@ -241,7 +252,8 @@ const appealFormData = reactive({
 });
 
 const appealFormRules = reactive<FormRules>({
-  reason: [{ required: true, message: "请输入申诉原因", trigger: "blur" }]
+  reason: [{ required: true, message: "请输入申诉原因", trigger: "blur" }],
+  images: [{ required: true, message: "请上传申诉凭证", trigger: "blur" }]
 });
 
 // 回复对话框
@@ -411,16 +423,34 @@ const submitReply = async () => {
   await replyFormRef.value.validate(async (valid: boolean) => {
     if (valid) {
       try {
-        // TODO: 集成真实接口
-        // await submitReviewReply({
-        //   reviewId: currentReplyReview.value.id,
-        //   content: replyFormData.content
-        // });
-
-        ElMessage.success("回复提交成功");
-        closeReplyDialog();
-        loadReviewList();
+        // 获取当前用户的手机号,并在前面拼接 "store_"
+        const phone = userStore.userInfo?.phone || "";
+        const phoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
+
+        // 调用 saveComment 接口
+        const params = {
+          businessId: currentReplyReview.value.id, // 评论ID
+          businessType: "1", // 业务类型:1表示订单评价的回复
+          userId: userStore.userInfo?.userId || userStore.userInfo?.id || "",
+          storeId: userStore.userInfo?.storeId || localGet("createdId") || "",
+          commentContent: replyFormData.content, // 回复内容
+          phoneId: phoneId, // 店铺phoneId
+          replyId: currentReplyReview.value.id // 被回复的评论ID
+        };
+
+        console.log("提交回复参数:", params);
+
+        const res: any = await saveComment(params);
+
+        if (res.code === 200) {
+          ElMessage.success("回复提交成功");
+          closeReplyDialog();
+          loadReviewList();
+        } else {
+          ElMessage.error(res.message || "回复提交失败");
+        }
       } catch (error) {
+        console.error("回复提交失败:", error);
         ElMessage.error("回复提交失败");
       }
     }
@@ -528,7 +558,6 @@ onMounted(() => {
 <style lang="scss" scoped>
 .review-appeal-container {
   min-height: calc(100vh - 120px);
-  padding: 20px;
   background: #f5f7fa;
 
   // 统计数据区域

+ 906 - 13
src/views/dynamicManagement/userDynamic.vue

@@ -62,6 +62,7 @@
       <!-- 动态卡片网格 -->
       <div class="content-grid">
         <div v-for="item in paginatedList" :key="item.id" class="content-card" @click="handleCardClick(item)">
+          {{ item }}
           <!-- 封面图片区域 -->
           <div class="content-cover-wrapper">
             <img v-if="item.coverUrl" :src="item.coverUrl" :alt="item.title" class="content-cover" />
@@ -123,14 +124,44 @@
       <div v-if="currentDetail" class="detail-content">
         <!-- 主内容区域 -->
         <div class="detail-main">
-          <!-- 图片展示 -->
+          <!-- 图片/视频轮播展示 -->
           <div class="media-container">
-            <img v-if="currentDetail.coverUrl" :src="currentDetail.coverUrl" :alt="currentDetail.title" class="detail-media" />
+            <!-- 多媒体轮播 -->
+            <el-carousel
+              v-if="currentDetail.mediaList && currentDetail.mediaList.length > 0"
+              :autoplay="false"
+              :loop="false"
+              indicator-position="outside"
+              arrow="always"
+              height="100%"
+              class="media-carousel"
+              @change="handleCarouselChange"
+            >
+              <el-carousel-item v-for="(media, index) in currentDetail.mediaList" :key="index">
+                <!-- 视频 -->
+                <video
+                  v-if="media.type === 'video'"
+                  :ref="el => setVideoRef(el, index)"
+                  :src="media.url"
+                  class="detail-media detail-video"
+                  controls
+                  preload="metadata"
+                  @play="handleVideoPlay(index)"
+                />
+                <!-- 图片 -->
+                <img v-else :src="media.url" :alt="currentDetail.title" class="detail-media detail-image" />
+              </el-carousel-item>
+            </el-carousel>
+            <!-- 占位符 -->
             <div v-else class="media-placeholder">
               <el-icon :size="80" color="#dcdfe6">
                 <Picture />
               </el-icon>
             </div>
+            <!-- 媒体计数指示器 -->
+            <div v-if="currentDetail.mediaList && currentDetail.mediaList.length > 1" class="media-counter">
+              {{ currentCarouselIndex + 1 }} / {{ currentDetail.mediaList.length }}
+            </div>
           </div>
 
           <!-- 底部信息 -->
@@ -187,6 +218,18 @@
             </div>
           </div>
 
+          <!-- 评论 -->
+          <div class="action-item" @click="handleShowComments">
+            <div class="action-icon">
+              <el-icon :size="28" color="#fff">
+                <ChatDotRound />
+              </el-icon>
+            </div>
+            <div class="action-count">
+              {{ currentDetail.commentCount || 0 }}
+            </div>
+          </div>
+
           <!-- 分享 -->
           <div class="action-item" @click="handleShare">
             <div class="action-icon">
@@ -196,10 +239,191 @@
             </div>
             <div class="action-count">分享</div>
           </div>
+
+          <!-- 更多(仅在他人主页显示举报和拉黑) -->
+          <el-popover v-if="!isMyPage" placement="left" :width="120" trigger="click" popper-class="more-actions-popover">
+            <template #reference>
+              <div class="action-item">
+                <div class="action-icon">
+                  <el-icon :size="28" color="#fff">
+                    <MoreFilled />
+                  </el-icon>
+                </div>
+              </div>
+            </template>
+            <div class="more-actions-menu">
+              <div
+                class="menu-item"
+                style="display: flex; align-items: center; padding-bottom: 10px; cursor: pointer"
+                @click="handleReportDynamic"
+              >
+                <el-icon :size="18">
+                  <Warning /> </el-icon
+                >&nbsp;&nbsp;
+                <span>举报</span>
+              </div>
+              <div
+                class="menu-item"
+                style="display: flex; align-items: center; cursor: pointer"
+                @click="handleBlockUserFromDrawer"
+              >
+                <el-icon :size="18">
+                  <CircleClose /> </el-icon
+                >&nbsp;&nbsp;
+                <span>拉黑</span>
+              </div>
+            </div>
+          </el-popover>
         </div>
       </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(comment)">
+                        <el-icon :size="16" :color="comment.isLiked ? '#f56c6c' : '#999'">
+                          <Star />
+                        </el-icon>
+                        <span>{{ comment.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="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>
+
     <!-- 举报对话框 -->
     <el-dialog v-model="reportDialogVisible" title="举报理由" width="500px" destroy-on-close @close="handleCloseReportDialog">
       <div class="report-dialog-content">
@@ -287,7 +511,9 @@ import {
   MoreFilled,
   Plus,
   Warning,
-  CircleClose
+  CircleClose,
+  ChatDotRound,
+  CircleCheck
 } from "@element-plus/icons-vue";
 import {
   getUserDynamicsList,
@@ -299,6 +525,7 @@ import {
 } from "@/api/modules/dynamicManagement";
 import { uploadImg } from "@/api/modules/upload";
 import { useUserStore } from "@/stores/modules/user";
+import { saveComment, commentList, getMutualAttention, addTransferCount } from "@/api/modules/newLoginApi";
 
 const route = useRoute();
 const router = useRouter();
@@ -312,6 +539,11 @@ const targetUserAvatar = ref((route.query.userAvatar as string) || "");
 const targetUserPhone = ref((route.query.phone as string) || "");
 
 // 接口定义
+interface MediaItem {
+  url: string;
+  type: "image" | "video";
+}
+
 interface ContentItem {
   id: number;
   title: string;
@@ -320,6 +552,15 @@ interface ContentItem {
   likeCount: number;
   isLiked: boolean;
   createTime: string;
+  commentCount?: number;
+  mediaList?: MediaItem[];
+}
+
+interface ShareFriend {
+  id: number;
+  name: string;
+  avatar: string;
+  phoneId?: string;
 }
 
 // 响应式数据
@@ -331,6 +572,10 @@ const isFollowed = ref(false); // 是否已关注
 const detailDrawerVisible = ref(false);
 const currentDetail = ref<ContentItem | null>(null);
 
+// 视频轮播相关
+const videoRefs = ref<HTMLVideoElement[]>([]);
+const currentCarouselIndex = ref(0);
+
 // 举报对话框相关
 const reportDialogVisible = ref(false);
 const reportSubmitting = ref(false);
@@ -341,6 +586,29 @@ const reportForm = reactive({
   agreed: false
 });
 
+// 评论相关
+const commentDrawerVisible = ref(false);
+const commentListData = ref<any[]>([]);
+const commentInput = ref("");
+const commentSubmitting = ref(false);
+const replyingComment = ref<any>(null);
+
+// 分享对话框相关
+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 violationTypeMap: Record<string, number> = {
   用户头像: 1,
@@ -416,19 +684,82 @@ const handleCurrentChange = (val: number) => {
 };
 
 // 点击卡片
-const handleCardClick = (item: ContentItem) => {
-  currentDetail.value = item;
-  detailDrawerVisible.value = true;
+const handleCardClick = async (item: any) => {
+  try {
+    // 解析媒体列表(支持多张图片和视频)
+    const mediaUrl = item.imagePath || item.coverUrl || "";
+    const mediaUrls = mediaUrl
+      .split(",")
+      .map((url: string) => url.trim())
+      .filter((url: string) => url);
+    const mediaList: MediaItem[] = mediaUrls.map((url: string) => ({
+      url,
+      type: url.toLowerCase().endsWith(".mp4") ? ("video" as const) : ("image" as const)
+    }));
+
+    const firstUrl = mediaUrls[0] || "";
+    const isVideo = firstUrl.toLowerCase().endsWith(".mp4");
+
+    currentDetail.value = {
+      ...item,
+      mediaList,
+      coverUrl: firstUrl,
+      type: isVideo ? "video" : "image"
+    };
+
+    detailDrawerVisible.value = true;
+
+    // 重置轮播索引
+    currentCarouselIndex.value = 0;
+  } catch (error) {
+    console.error("加载详情失败:", error);
+    ElMessage.error("加载详情失败");
+  }
 };
 
 // 关闭详情
 const handleCloseDetail = () => {
   detailDrawerVisible.value = false;
+  // 暂停所有视频
+  videoRefs.value.forEach(video => {
+    if (video && !video.paused) {
+      video.pause();
+    }
+  });
   setTimeout(() => {
     currentDetail.value = null;
+    videoRefs.value = [];
   }, 300);
 };
 
+// 设置视频引用
+const setVideoRef = (el: any, index: number) => {
+  if (el) {
+    videoRefs.value[index] = el as HTMLVideoElement;
+  }
+};
+
+// 轮播切换
+const handleCarouselChange = (newIndex: number) => {
+  // 暂停所有视频
+  videoRefs.value.forEach(video => {
+    if (video && !video.paused) {
+      video.pause();
+    }
+  });
+  currentCarouselIndex.value = newIndex;
+};
+
+// 视频播放
+const handleVideoPlay = (index: number) => {
+  // 暂停其他视频
+  videoRefs.value.forEach((video, i) => {
+    if (i !== index && video && !video.paused) {
+      video.pause();
+    }
+  });
+};
+
 // 详情页点赞
 const handleDetailLike = () => {
   if (!currentDetail.value) return;
@@ -439,9 +770,222 @@ const handleDetailLike = () => {
   ElMessage.success(currentDetail.value.isLiked ? "点赞成功" : "取消点赞");
 };
 
+// 显示评论
+const handleShowComments = async () => {
+  if (!currentDetail.value) return;
+  commentDrawerVisible.value = true;
+  await loadCommentList();
+};
+
+// 加载评论列表
+const loadCommentList = async () => {
+  if (!currentDetail.value) return;
+
+  try {
+    const phone = userStore.userInfo?.phone || "";
+    const phoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
+
+    let params = {
+      businessId: String(currentDetail.value.id),
+      businessType: "2",
+      replyStatus: 0,
+      pageNum: 1,
+      pageSize: 100,
+      commentType: 0,
+      days: "",
+      phoneId: phoneId
+    };
+    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 handleLikeComment = async (comment: any) => {
+  try {
+    comment.isLiked = !comment.isLiked;
+    comment.likeCount = comment.isLiked ? (comment.likeCount || 0) + 1 : (comment.likeCount || 1) - 1;
+  } catch (error) {
+    console.error("点赞评论失败:", 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 handleSubmitComment = async () => {
+  if (!commentInput.value.trim()) {
+    ElMessage.warning("请输入评论内容");
+    return;
+  }
+
+  if (!currentDetail.value) {
+    ElMessage.error("动态信息不存在");
+    return;
+  }
+
+  try {
+    commentSubmitting.value = true;
+
+    const phone = userStore.userInfo?.phone || "";
+    const phoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
+
+    // 判断是回复评论还是评论动态
+    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: phoneId
+    };
+
+    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 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 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 = [];
 };
 
 // 关注/取消关注(顶部按钮)
@@ -513,11 +1057,21 @@ const handleReportUser = () => {
   reportDialogVisible.value = true;
 };
 
+// 举报动态(从详情页)
+const handleReportDynamic = () => {
+  reportDialogVisible.value = true;
+};
+
 // 拉黑用户(点击菜单项)
 const handleBlockUserClick = () => {
   handleBlockUser(false);
 };
 
+// 从详情页拉黑用户
+const handleBlockUserFromDrawer = () => {
+  handleBlockUser(false);
+};
+
 // 拉黑用户
 const handleBlockUser = async (skipConfirm: boolean = false) => {
   try {
@@ -893,8 +1447,11 @@ onMounted(() => {
   // 内容卡片
   .content-card {
     cursor: pointer;
+    border: 1px solid #e4e7ed;
+    border-radius: 8px;
     transition: all 0.3s;
     &:hover {
+      box-shadow: 0 2px 12px rgb(0 0 0 / 10%);
       transform: translateY(-2px);
     }
 
@@ -971,9 +1528,7 @@ onMounted(() => {
         padding: 8px;
         font-size: 24px;
         color: #ffffff;
-        &:hover {
-          background: rgb(255 255 255 / 10%);
-        }
+        background: #ffffff;
       }
     }
     .detail-content {
@@ -991,18 +1546,73 @@ onMounted(() => {
         justify-content: center;
         padding: 80px 120px 40px 40px;
         .media-container {
+          position: relative;
           display: flex;
           flex: 1;
           align-items: center;
           justify-content: center;
           width: 100%;
           max-width: 800px;
+          min-height: 400px;
+          .media-carousel {
+            width: 100%;
+            height: 100%;
+            min-height: 400px;
+            max-height: 70vh;
+            :deep(.el-carousel__container) {
+              height: 100% !important;
+              min-height: 400px;
+            }
+            :deep(.el-carousel__item) {
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              background: transparent;
+            }
+            :deep(.el-carousel__arrow) {
+              width: 48px;
+              height: 48px;
+              font-size: 24px;
+              color: #ffffff;
+              background-color: rgb(0 0 0 / 40%);
+              border: none;
+              &:hover {
+                background-color: rgb(0 0 0 / 60%);
+              }
+            }
+            :deep(.el-carousel__indicators) {
+              bottom: -30px;
+              .el-carousel__indicator {
+                .el-carousel__button {
+                  width: 8px;
+                  height: 8px;
+                  background-color: rgb(255 255 255 / 40%);
+                  border-radius: 50%;
+                }
+                &.is-active .el-carousel__button {
+                  background-color: #409eff;
+                }
+              }
+            }
+          }
           .detail-media {
             max-width: 100%;
-            max-height: 100%;
+            max-height: 65vh;
+            object-fit: contain;
+            border-radius: 8px;
+          }
+          .detail-image {
+            max-width: 100%;
+            max-height: 65vh;
             object-fit: contain;
             border-radius: 8px;
           }
+          .detail-video {
+            width: 100%;
+            max-height: 65vh;
+            background: #000000;
+            border-radius: 8px;
+          }
           .media-placeholder {
             display: flex;
             align-items: center;
@@ -1012,6 +1622,16 @@ onMounted(() => {
             background: rgb(255 255 255 / 5%);
             border-radius: 8px;
           }
+          .media-counter {
+            position: absolute;
+            right: 16px;
+            bottom: -24px;
+            padding: 4px 12px;
+            font-size: 14px;
+            color: #ffffff;
+            background: rgb(0 0 0 / 50%);
+            border-radius: 12px;
+          }
         }
         .detail-info {
           width: 100%;
@@ -1148,7 +1768,34 @@ onMounted(() => {
     }
   }
 
-  // 举报对话框
+  // 更多操作 Popover
+  :deep(.more-actions-popover) {
+    min-width: 120px;
+    padding: 8px 0;
+    background: rgb(0 0 0 / 90%);
+    backdrop-filter: blur(10px);
+    border: 1px solid rgb(255 255 255 / 20%);
+    .more-actions-menu {
+      .menu-item {
+        display: flex;
+        gap: 8px;
+        align-items: center;
+        padding: 10px 16px;
+        font-size: 14px;
+        color: #ffffff;
+        cursor: pointer;
+        transition: background 0.3s;
+        &:hover {
+          background: rgb(255 255 255 / 10%);
+        }
+        .el-icon {
+          font-size: 18px;
+        }
+      }
+    }
+  }
+
+  // 举报对话框和分享对话框
   :deep(.el-dialog) {
     .report-dialog-content {
       .report-tip {
@@ -1214,11 +1861,257 @@ onMounted(() => {
         }
       }
     }
+
+    // 分享对话框
+    .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;
+          }
+        }
+      }
+    }
     .dialog-footer {
       display: flex;
       gap: 12px;
       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%;
+    }
+  }
 }
 </style>

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott