zhuli 1 долоо хоног өмнө
parent
commit
4a090b4fc4

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

@@ -69,3 +69,58 @@ export const deleteDraft = (params: { id: number }) => {
 export const getLocationList = (params: { keyword?: string }) => {
   return http.get(PORT_NONE + `/dynamic/getLocationList`, params);
 };
+
+// 获取我的动态列表
+export const getMyDynamicList = (params: { page: number; pageSize: number }) => {
+  return http.get(PORT_NONE + `/dynamic/getMyDynamicList`, params);
+};
+
+// 获取我赞过的列表
+export const getMyLikedList = (params: { page: number; pageSize: number }) => {
+  return http.get(PORT_NONE + `/dynamic/getMyLikedList`, params);
+};
+
+// 获取用户信息
+export const getUserInfo = () => {
+  return http.get(PORT_NONE + `/dynamic/getUserInfo`);
+};
+
+// 获取关系列表(好友/关注/粉丝)
+export const getRelationList = (params: { type: "friend" | "follow" | "fans" }) => {
+  return http.get(PORT_NONE + `/dynamic/getRelationList`, params);
+};
+
+// 关注用户
+export const followUser = (params: { userId: number }) => {
+  return http.post(PORT_NONE + `/dynamic/followUser`, params);
+};
+
+// 取消关注用户
+export const unfollowUser = (params: { userId: number }) => {
+  return http.post(PORT_NONE + `/dynamic/unfollowUser`, params);
+};
+
+// 举报动态
+export const reportDynamic = (params: { dynamicId: number; reason: string; description: string; images: string[] }) => {
+  return http.post(PORT_NONE + `/dynamic/reportDynamic`, params);
+};
+
+// 获取评论列表
+export const getCommentsList = (params: { dynamicId: number; sortType: string }) => {
+  return http.get(PORT_NONE + `/dynamic/getCommentsList`, params);
+};
+
+// 提交评论
+export const submitComment = (params: { dynamicId: number; content: string; parentId?: number }) => {
+  return http.post(PORT_NONE + `/dynamic/submitComment`, params);
+};
+
+// 点赞评论
+export const likeComment = (params: { commentId: number }) => {
+  return http.post(PORT_NONE + `/dynamic/likeComment`, params);
+};
+
+// 取消点赞评论
+export const unlikeComment = (params: { commentId: number }) => {
+  return http.post(PORT_NONE + `/dynamic/unlikeComment`, params);
+};

+ 21 - 0
src/api/modules/user.ts

@@ -0,0 +1,21 @@
+import { PORT_NONE } from "@/api/config/servicePort";
+import http from "@/api";
+
+/**
+ * 用户相关接口
+ */
+
+// 获取黑名单列表
+export const getBlacklist = () => {
+  return http.get(PORT_NONE + `/user/getBlacklist`);
+};
+
+// 解除拉黑用户
+export const unblockUser = (params: { userId: number }) => {
+  return http.post(PORT_NONE + `/user/unblockUser`, params);
+};
+
+// 拉黑用户
+export const blockUser = (params: { userId: number }) => {
+  return http.post(PORT_NONE + `/user/blockUser`, params);
+};

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

@@ -688,6 +688,33 @@
           }
         },
         {
+        "path": "/dynamicManagement/publishDynamic",
+        "name": "publishDynamic",
+        "component": "/dynamicManagement/publishDynamic",
+        "meta": {
+          "icon": "Setting",
+          "title": "动态发布",
+          "isLink": "",
+          "isHide": true,
+          "isFull": false,
+          "isAffix": false,
+          "isKeepAlive": false
+        }
+      },{
+        "path": "/dynamicManagement/myDynamic",
+        "name": "myDynamic",
+        "component": "/dynamicManagement/myDynamic",
+        "meta": {
+          "icon": "Setting",
+          "title": "我的动态",
+          "isLink": "",
+          "isHide": false,
+          "isFull": false,
+          "isAffix": false,
+          "isKeepAlive": false
+        }
+      },
+        {
           "path": "/dynamicManagement/reviewAppeal",
           "name": "reviewAppeal",
           "component": "/dynamicManagement/reviewAppeal",

+ 1 - 0
src/languages/modules/zh.ts

@@ -24,6 +24,7 @@ export default {
     exitFullScreen: "退出全屏",
     personalData: "个人信息",
     changePassword: "修改登录密码",
+    blacklist: "黑名单",
     logout: "退出登录"
   }
 };

+ 8 - 0
src/layouts/components/Header/components/Avatar.vue

@@ -11,6 +11,9 @@
         <el-dropdown-item @click="openDialog('passwordRef')">
           <el-icon><Edit /></el-icon>{{ $t("header.changePassword") }}
         </el-dropdown-item>
+        <el-dropdown-item @click="openDialog('blackListRef')">
+          <el-icon><Edit /></el-icon>{{ $t("header.blacklist") }}
+        </el-dropdown-item>
         <el-dropdown-item divided @click="logout">
           <el-icon><SwitchButton /></el-icon>{{ $t("header.logout") }}
         </el-dropdown-item>
@@ -21,6 +24,8 @@
   <InfoDialog ref="infoRef" @avatar-updated="handleAvatarUpdated" />
   <!-- passwordDialog -->
   <PasswordDialog ref="passwordRef" />
+  <!---blackListDialog--->
+  <BlackListDialog ref="blackListRef" />
 </template>
 
 <script setup lang="ts">
@@ -35,6 +40,7 @@ import { useKeepAliveStore } from "@/stores/modules/keepAlive";
 import { ElMessageBox, ElMessage } from "element-plus";
 import InfoDialog from "./InfoDialog.vue";
 import PasswordDialog from "./PasswordDialog.vue";
+import BlackListDialog from "./BlackListDialog.vue";
 import { getMerchantByPhone } from "@/api/modules/homeEntry";
 import { localGet, localSet } from "@/utils/index";
 import { resetRouter } from "@/routers/index";
@@ -185,9 +191,11 @@ const logout = () => {
 // 打开修改密码和个人信息弹窗
 const infoRef = ref<InstanceType<typeof InfoDialog> | null>(null);
 const passwordRef = ref<InstanceType<typeof PasswordDialog> | null>(null);
+const blackListRef = ref<InstanceType<typeof BlackListDialog> | null>(null);
 const openDialog = (ref: string) => {
   if (ref == "infoRef") infoRef.value?.openDialog();
   if (ref == "passwordRef") passwordRef.value?.openDialog();
+  if (ref == "blackListRef") blackListRef.value?.openDialog();
 };
 </script>
 

+ 228 - 0
src/layouts/components/Header/components/BlackListDialog.vue

@@ -0,0 +1,228 @@
+<template>
+  <el-dialog v-model="dialogVisible" title="黑名单" width="600px" destroy-on-close @close="handleClose">
+    <div class="blacklist-content">
+      <!-- 黑名单列表 -->
+      <div class="blacklist-list">
+        <div v-for="user in blacklist" :key="user.id" class="blacklist-item">
+          <div class="user-info">
+            <div class="user-avatar">
+              <img v-if="user.avatar" :src="user.avatar" :alt="user.name" />
+              <el-icon v-else :size="40">
+                <Avatar />
+              </el-icon>
+            </div>
+            <span class="user-name">{{ user.name }}</span>
+          </div>
+          <el-button type="primary" plain size="small" :loading="user.unblocking" @click="handleUnblock(user)">
+            解除拉黑
+          </el-button>
+        </div>
+
+        <!-- 空状态 -->
+        <el-empty v-if="blacklist.length === 0" description="暂无黑名单用户" />
+      </div>
+
+      <!-- 已解除拉黑的用户提示 -->
+      <div v-if="unblockedUsers.length > 0" class="unblocked-section">
+        <div class="unblocked-title">已解除拉黑 ({{ unblockedUsers.length }})</div>
+        <div class="unblocked-list">
+          <el-tag v-for="user in unblockedUsers" :key="user.id" type="info" closable @close="removeUnblockedUser(user.id)">
+            {{ user.name }}
+          </el-tag>
+        </div>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { ref } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Avatar } from "@element-plus/icons-vue";
+// import { getBlacklist, unblockUser } from '@/api/modules/user';
+
+interface BlacklistUser {
+  id: number;
+  name: string;
+  avatar: string;
+  unblocking: boolean;
+}
+
+const dialogVisible = ref(false);
+const blacklist = ref<BlacklistUser[]>([]);
+const unblockedUsers = ref<BlacklistUser[]>([]);
+const submitting = ref(false);
+
+// 打开对话框
+const openDialog = () => {
+  dialogVisible.value = true;
+  loadBlacklist();
+  unblockedUsers.value = [];
+};
+
+// 暴露方法给父组件使用
+defineExpose({
+  openDialog
+});
+
+// 加载黑名单列表
+const loadBlacklist = async () => {
+  try {
+    // TODO: 集成真实接口时,取消下面的注释
+    // const res = await getBlacklist();
+    // blacklist.value = res.data.map(user => ({ ...user, unblocking: false }));
+
+    // 临时方案:使用模拟数据
+    const mockData: BlacklistUser[] = [
+      {
+        id: 1,
+        name: "甜品店",
+        avatar: "",
+        unblocking: false
+      },
+      {
+        id: 2,
+        name: "甜品店",
+        avatar: "",
+        unblocking: false
+      },
+      {
+        id: 3,
+        name: "甜品店",
+        avatar: "",
+        unblocking: false
+      }
+    ];
+
+    blacklist.value = mockData;
+  } catch (error) {
+    ElMessage.error("加载黑名单失败");
+  }
+};
+
+// 解除拉黑
+const handleUnblock = async (user: BlacklistUser) => {
+  try {
+    await ElMessageBox.confirm(`确定要将"${user.name}"移出黑名单吗?`, "解除拉黑", {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning"
+    });
+
+    user.unblocking = true;
+
+    // TODO: 集成真实接口时,取消下面的注释
+    // await unblockUser({ userId: user.id });
+
+    // 模拟延迟
+    await new Promise(resolve => setTimeout(resolve, 500));
+
+    // 从黑名单中移除
+    const index = blacklist.value.findIndex(u => u.id === user.id);
+    if (index > -1) {
+      const removedUser = blacklist.value.splice(index, 1)[0];
+      unblockedUsers.value.push(removedUser);
+    }
+
+    ElMessage.success("解除拉黑成功");
+  } catch (error) {
+    if (error !== "cancel") {
+      ElMessage.error("解除拉黑失败");
+    }
+  } finally {
+    user.unblocking = false;
+  }
+};
+
+// 移除已解除的用户标签
+const removeUnblockedUser = (userId: number) => {
+  const index = unblockedUsers.value.findIndex(u => u.id === userId);
+  if (index > -1) {
+    unblockedUsers.value.splice(index, 1);
+  }
+};
+
+// 提交(关闭对话框)
+const handleSubmit = () => {
+  if (unblockedUsers.value.length > 0) {
+    ElMessage.success(`已成功解除 ${unblockedUsers.value.length} 位用户的拉黑`);
+  }
+  handleClose();
+};
+
+// 关闭对话框
+const handleClose = () => {
+  dialogVisible.value = false;
+  unblockedUsers.value = [];
+};
+</script>
+
+<style scoped lang="scss">
+.blacklist-content {
+  .blacklist-list {
+    max-height: 400px;
+    overflow-y: auto;
+    .blacklist-item {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 16px 0;
+      border-bottom: 1px solid #f0f0f0;
+      &:last-child {
+        border-bottom: none;
+      }
+      .user-info {
+        display: flex;
+        flex: 1;
+        gap: 12px;
+        align-items: center;
+        .user-avatar {
+          display: flex;
+          flex-shrink: 0;
+          align-items: center;
+          justify-content: center;
+          width: 40px;
+          height: 40px;
+          overflow: hidden;
+          background: #f5f7fa;
+          border-radius: 50%;
+          img {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+          }
+        }
+        .user-name {
+          font-size: 15px;
+          font-weight: 500;
+          color: #303133;
+        }
+      }
+    }
+  }
+  .unblocked-section {
+    padding-top: 24px;
+    margin-top: 24px;
+    border-top: 1px solid #e4e7ed;
+    .unblocked-title {
+      margin-bottom: 12px;
+      font-size: 14px;
+      font-weight: 500;
+      color: #606266;
+    }
+    .unblocked-list {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 8px;
+      .el-tag {
+        font-size: 13px;
+      }
+    }
+  }
+}
+.dialog-footer {
+  display: flex;
+  gap: 12px;
+  justify-content: flex-end;
+}
+</style>

+ 7 - 192
src/views/dynamicManagement/index.vue

@@ -73,62 +73,17 @@
         @current-change="handleCurrentChange"
       />
     </div>
-
-    <!-- 发布动态对话框 -->
-    <el-dialog v-model="publishDialogVisible" title="发布动态" width="600px" @close="handleCloseDialog">
-      <el-form ref="formRef" :model="formData" :rules="rules" label-width="80px">
-        <el-form-item label="动态标题" prop="title">
-          <el-input v-model="formData.title" placeholder="请输入动态标题" maxlength="50" show-word-limit />
-        </el-form-item>
-
-        <el-form-item label="动态内容" prop="content">
-          <el-input
-            v-model="formData.content"
-            type="textarea"
-            :rows="4"
-            placeholder="请输入动态内容"
-            maxlength="200"
-            show-word-limit
-          />
-        </el-form-item>
-
-        <el-form-item label="上传图片" prop="imageUrl">
-          <el-upload
-            v-model:file-list="fileList"
-            list-type="picture-card"
-            :limit="1"
-            :on-preview="handlePicturePreview"
-            :on-remove="handleRemoveImage"
-            :before-upload="beforeImageUpload"
-            :http-request="handleImageUpload"
-            accept="image/*"
-          >
-            <el-icon><Plus /></el-icon>
-          </el-upload>
-        </el-form-item>
-      </el-form>
-
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="publishDialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="handleSubmit">发布</el-button>
-        </span>
-      </template>
-    </el-dialog>
-
-    <!-- 图片预览对话框 -->
-    <el-dialog v-model="previewDialogVisible" width="800px">
-      <img :src="previewImageUrl" alt="预览图片" style="width: 100%" />
-    </el-dialog>
   </div>
 </template>
 
-<script setup lang="ts">
+<script setup lang="ts" name="dynamicManagementIndex">
 import { ref, reactive, computed, onMounted } from "vue";
+import { useRouter } from "vue-router";
 import { ElMessage } from "element-plus";
-import { Picture, Avatar, Star, Plus } from "@element-plus/icons-vue";
-import type { FormInstance, FormRules, UploadUserFile } from "element-plus";
-// import { getDynamicList, publishDynamic, uploadDynamicImage } from "@/api/modules/dynamicManagement";
+import { Picture, Avatar, Star } from "@element-plus/icons-vue";
+// import { getDynamicList, likeDynamic, unlikeDynamic } from "@/api/modules/dynamicManagement";
+
+const router = useRouter();
 
 // 接口定义
 interface DynamicItem {
@@ -143,20 +98,9 @@ interface DynamicItem {
   createTime: string;
 }
 
-interface FormData {
-  title: string;
-  content: string;
-  imageUrl: string;
-}
-
 // 响应式数据
 const activeTab = ref("recommend");
 const dynamicList = ref<DynamicItem[]>([]);
-const publishDialogVisible = ref(false);
-const previewDialogVisible = ref(false);
-const previewImageUrl = ref("");
-const fileList = ref<UploadUserFile[]>([]);
-const formRef = ref<FormInstance>();
 
 // 分页
 const pagination = reactive({
@@ -165,19 +109,6 @@ const pagination = reactive({
   total: 0
 });
 
-// 表单数据
-const formData = reactive<FormData>({
-  title: "",
-  content: "",
-  imageUrl: ""
-});
-
-// 表单验证规则
-const rules = reactive<FormRules<FormData>>({
-  title: [{ required: true, message: "请输入动态标题", trigger: "blur" }],
-  content: [{ required: true, message: "请输入动态内容", trigger: "blur" }]
-});
-
 // 计算分页后的列表
 const paginatedList = computed(() => {
   const start = (pagination.page - 1) * pagination.pageSize;
@@ -193,7 +124,7 @@ const handleTabClick = () => {
 
 // 发布动态
 const handlePublish = () => {
-  publishDialogVisible.value = true;
+  router.push("/dynamicManagement/publishDynamic");
 };
 
 // 分页大小改变
@@ -207,105 +138,6 @@ const handleCurrentChange = (val: number) => {
   pagination.page = val;
 };
 
-// 图片预览
-const handlePicturePreview = (uploadFile: UploadUserFile) => {
-  previewImageUrl.value = uploadFile.url!;
-  previewDialogVisible.value = true;
-};
-
-// 移除图片
-const handleRemoveImage = () => {
-  formData.imageUrl = "";
-  fileList.value = [];
-};
-
-// 图片上传前验证
-const beforeImageUpload = (file: File) => {
-  const isImage = file.type.startsWith("image/");
-  const isLt5M = file.size / 1024 / 1024 < 5;
-
-  if (!isImage) {
-    ElMessage.error("只能上传图片文件!");
-    return false;
-  }
-  if (!isLt5M) {
-    ElMessage.error("图片大小不能超过 5MB!");
-    return false;
-  }
-  return true;
-};
-
-// 自定义上传
-const handleImageUpload = async (options: any) => {
-  const { file } = options;
-
-  try {
-    // TODO: 集成真实上传接口时,取消下面的注释
-    // const uploadFormData = new FormData();
-    // uploadFormData.append('file', file);
-    // const res = await uploadDynamicImage(uploadFormData);
-    // formData.imageUrl = res.data.url;
-    // ElMessage.success('图片上传成功');
-
-    // 临时方案:使用 FileReader 模拟上传
-    const reader = new FileReader();
-    reader.onload = e => {
-      formData.imageUrl = e.target?.result as string;
-      ElMessage.success("图片上传成功");
-    };
-    reader.readAsDataURL(file);
-  } catch (error) {
-    ElMessage.error("图片上传失败");
-  }
-};
-
-// 提交表单
-const handleSubmit = async () => {
-  if (!formRef.value) return;
-
-  await formRef.value.validate(async (valid: boolean) => {
-    if (valid) {
-      try {
-        // TODO: 集成真实接口时,取消下面的注释
-        // await publishDynamic({
-        //   title: formData.title,
-        //   content: formData.content,
-        //   imageUrl: formData.imageUrl
-        // });
-
-        // 临时方案:模拟添加到列表
-        const newDynamic: DynamicItem = {
-          id: Date.now(),
-          title: formData.title,
-          content: formData.content,
-          imageUrl: formData.imageUrl,
-          userName: "甜品店",
-          userAvatar: "",
-          likeCount: 0,
-          isLiked: false,
-          createTime: new Date().toISOString()
-        };
-
-        dynamicList.value.unshift(newDynamic);
-        pagination.total = dynamicList.value.length;
-
-        ElMessage.success("发布成功");
-        publishDialogVisible.value = false;
-        handleCloseDialog();
-      } catch (error) {
-        ElMessage.error("发布失败");
-      }
-    }
-  });
-};
-
-// 关闭对话框
-const handleCloseDialog = () => {
-  formRef.value?.resetFields();
-  fileList.value = [];
-  formData.imageUrl = "";
-};
-
 // 加载动态列表
 const loadDynamicList = async () => {
   try {
@@ -532,22 +364,5 @@ onMounted(() => {
     padding: 20px 0;
     margin-top: 30px;
   }
-
-  // 对话框
-  :deep(.el-dialog) {
-    .el-upload--picture-card {
-      width: 148px;
-      height: 148px;
-    }
-    .el-upload-list--picture-card .el-upload-list__item {
-      width: 148px;
-      height: 148px;
-    }
-  }
-  .dialog-footer {
-    display: flex;
-    gap: 10px;
-    justify-content: flex-end;
-  }
 }
 </style>

+ 1439 - 0
src/views/dynamicManagement/myDynamic.vue

@@ -0,0 +1,1439 @@
+<template>
+  <div class="my-dynamic-container">
+    <!-- 用户信息卡片 -->
+    <div class="user-card">
+      <div class="user-header">
+        <div class="user-avatar-section">
+          <div class="user-avatar-large">
+            <img v-if="userInfo.avatar" :src="userInfo.avatar" :alt="userInfo.name" />
+            <el-icon v-else :size="60">
+              <Avatar />
+            </el-icon>
+          </div>
+          <div class="user-info-text">
+            <div class="user-name">
+              {{ userInfo.name }}
+            </div>
+            <div class="user-stats">
+              <span class="stat-item" @click="openRelationDialog('friend')">{{ userInfo.followCount }} 好友</span>
+              <span class="stat-divider">|</span>
+              <span class="stat-item" @click="openRelationDialog('follow')">{{ userInfo.fansCount }} 关注</span>
+              <span class="stat-divider">|</span>
+              <span class="stat-item" @click="openRelationDialog('fans')">{{ userInfo.likeCount }} 粉丝</span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="user-bio">
+        {{ userInfo.bio }}
+      </div>
+    </div>
+
+    <!-- 标签页 -->
+    <div class="tabs-section">
+      <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+        <el-tab-pane label="动态" name="dynamic" />
+        <el-tab-pane label="赞过" name="liked" />
+      </el-tabs>
+    </div>
+
+    <!-- 内容区域 -->
+    <div v-if="contentList.length > 0" class="content-section">
+      <!-- 动态卡片网格 -->
+      <div class="content-grid">
+        <div v-for="item in paginatedList" :key="item.id" class="content-card" @click="handleCardClick(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 v-if="item.showLabel" class="content-label">
+              {{ item.labelText }}
+            </div>
+          </div>
+
+          <!-- 观看次数 -->
+          <div class="content-footer">
+            <el-icon :size="14" color="#666">
+              <View />
+            </el-icon>
+            <span class="view-count">{{ item.viewCount }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 空状态 -->
+    <div v-else class="empty-section">
+      <el-empty :description="activeTab === 'dynamic' ? '暂无动态' : '暂无赞过的内容'" />
+    </div>
+
+    <!-- 分页 -->
+    <div v-if="contentList.length > 0" class="pagination-section">
+      <el-pagination
+        v-model:current-page="pagination.page"
+        v-model:page-size="pagination.pageSize"
+        :page-sizes="[12, 24, 36, 48]"
+        :total="pagination.total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+
+    <!-- 动态详情 Drawer -->
+    <el-drawer
+      v-model="detailDrawerVisible"
+      direction="rtl"
+      size="90%"
+      :show-close="false"
+      destroy-on-close
+      class="detail-drawer"
+    >
+      <template #header>
+        <div class="drawer-header">
+          <el-button class="close-btn" text @click="handleCloseDetail">
+            <el-icon :size="24">
+              <Close />
+            </el-icon>
+          </el-button>
+        </div>
+      </template>
+
+      <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" />
+            <div v-else class="media-placeholder">
+              <el-icon :size="80" color="#dcdfe6">
+                <Picture />
+              </el-icon>
+            </div>
+          </div>
+
+          <!-- 底部信息 -->
+          <div class="detail-info">
+            <div class="author-info">
+              <div class="author-avatar">
+                <img v-if="currentDetail.author?.avatar" :src="currentDetail.author.avatar" :alt="currentDetail.author.name" />
+                <el-icon v-else :size="32">
+                  <Avatar />
+                </el-icon>
+              </div>
+              <div class="author-details">
+                <div class="author-name">@{{ currentDetail.author?.name || "用户名" }}</div>
+                <div class="publish-time">
+                  {{ currentDetail.publishTime }}
+                </div>
+              </div>
+            </div>
+
+            <div class="detail-description">
+              <p>{{ currentDetail.description }}</p>
+              <span class="expand-btn">展开</span>
+            </div>
+          </div>
+        </div>
+
+        <!-- 右侧操作栏 -->
+        <div class="action-bar">
+          <!-- 作者头像 -->
+          <div class="action-item author-action">
+            <div class="action-avatar">
+              <img v-if="currentDetail.author?.avatar" :src="currentDetail.author.avatar" :alt="currentDetail.author.name" />
+              <el-icon v-else :size="40" color="#fff">
+                <Avatar />
+              </el-icon>
+            </div>
+          </div>
+
+          <!-- 点赞 -->
+          <div class="action-item" @click="handleDetailLike">
+            <div class="action-icon">
+              <el-icon :size="28" :color="currentDetail.isLiked ? '#f56c6c' : '#fff'">
+                <Star v-if="currentDetail.isLiked" />
+                <StarFilled v-else />
+              </el-icon>
+            </div>
+            <div class="action-count">
+              {{ currentDetail.likeCount }}
+            </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 }}
+            </div>
+          </div>
+
+          <!-- 分享 -->
+          <div class="action-item" @click="handleShare">
+            <div class="action-icon">
+              <el-icon :size="28" color="#fff">
+                <Share />
+              </el-icon>
+            </div>
+            <div class="action-count">分享</div>
+          </div>
+
+          <!-- 更多 -->
+          <el-popover 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" @click="handleEditDynamic">
+                <el-icon :size="18">
+                  <Edit />
+                </el-icon>
+                <span>编辑</span>
+              </div>
+              <div class="menu-item" @click="handleDeleteDynamic">
+                <el-icon :size="18">
+                  <Delete />
+                </el-icon>
+                <span>删除</span>
+              </div>
+              <div class="menu-item" @click="handleReportDynamic">
+                <el-icon :size="18">
+                  <Warning />
+                </el-icon>
+                <span>举报</span>
+              </div>
+            </div>
+          </el-popover>
+        </div>
+      </div>
+    </el-drawer>
+
+    <!-- 好友/关注/粉丝对话框 -->
+    <el-dialog
+      v-model="relationDialogVisible"
+      :title="relationDialogTitle"
+      width="600px"
+      destroy-on-close
+      @close="handleCloseRelationDialog"
+    >
+      <div class="relation-dialog-content">
+        <!-- 标签页 -->
+        <el-tabs v-model="relationActiveTab" @tab-click="handleRelationTabClick">
+          <el-tab-pane label="好友" name="friend" />
+          <el-tab-pane label="关注" name="follow" />
+          <el-tab-pane label="粉丝" name="fans" />
+        </el-tabs>
+
+        <!-- 搜索框 -->
+        <div class="search-box">
+          <el-input v-model="relationSearch" placeholder="请输入手机号或昵称" clearable @input="handleRelationSearch">
+            <template #prefix>
+              <el-icon>
+                <Search />
+              </el-icon>
+            </template>
+          </el-input>
+        </div>
+
+        <!-- 用户列表 -->
+        <div class="relation-list">
+          <div v-for="user in filteredRelationList" :key="user.id" class="relation-item">
+            <div class="user-info-row">
+              <div class="user-avatar-small">
+                <img v-if="user.avatar" :src="user.avatar" :alt="user.name" />
+                <el-icon v-else :size="40">
+                  <Avatar />
+                </el-icon>
+              </div>
+              <div class="user-details">
+                <div class="user-name-text">
+                  {{ user.name }}
+                </div>
+                <div class="user-desc">
+                  {{ user.description }}
+                </div>
+              </div>
+            </div>
+            <div class="action-button">
+              <el-button
+                v-if="user.relationStatus === 'following'"
+                type="primary"
+                plain
+                size="small"
+                @click="handleUnfollow(user)"
+              >
+                已关注
+              </el-button>
+              <el-button v-else-if="user.relationStatus === 'mutual'" type="primary" size="small" @click="handleUnfollow(user)">
+                互相关注
+              </el-button>
+              <el-button v-else type="primary" plain size="small" @click="handleFollow(user)"> 关注 </el-button>
+            </div>
+          </div>
+
+          <!-- 空状态 -->
+          <el-empty v-if="filteredRelationList.length === 0" description="暂无数据" />
+        </div>
+      </div>
+    </el-dialog>
+
+    <!-- 举报对话框 -->
+    <el-dialog v-model="reportDialogVisible" title="举报理由" width="500px" destroy-on-close @close="handleCloseReportDialog">
+      <div class="report-dialog-content">
+        <div class="report-tip">请选择最符合的原因,以便于我们进行的处理</div>
+
+        <!-- 举报原因选项 -->
+        <div class="report-reasons">
+          <el-radio-group v-model="reportForm.reason">
+            <el-radio label="用户头像"> 用户头像 </el-radio>
+            <el-radio label="名称/昵称"> 名称/昵称 </el-radio>
+            <el-radio label="违法违规"> 违法违规 </el-radio>
+            <el-radio label="低俗色情、暴力恐怖、政治谣言"> 低俗色情、暴力恐怖、政治谣言 </el-radio>
+            <el-radio label="涉嫌诈骗"> 涉嫌诈骗 </el-radio>
+            <el-radio label="人身攻击"> 人身攻击 </el-radio>
+            <el-radio label="侵犯版权"> 侵犯版权 </el-radio>
+            <el-radio label="恶意骚扰"> 恶意骚扰 </el-radio>
+            <el-radio label="虚假/过度宣传"> 虚假/过度宣传 </el-radio>
+            <el-radio label="诱导点赞分享"> 诱导点赞分享 </el-radio>
+            <el-radio label="传播人身安全"> 传播人身安全 </el-radio>
+            <el-radio label="侵权举报"> 侵权举报 </el-radio>
+            <el-radio label="其他举报"> 其他举报 </el-radio>
+          </el-radio-group>
+        </div>
+
+        <!-- 详细描述 -->
+        <div class="report-description">
+          <el-input
+            v-model="reportForm.description"
+            type="textarea"
+            :rows="4"
+            placeholder="请描述任何涉及举报内容的其体情况,我们会综合一判断合举政采!(必填)"
+            maxlength="300"
+            show-word-limit
+          />
+        </div>
+
+        <!-- 上传凭证 -->
+        <div class="report-upload">
+          <div class="upload-title">上传凭证</div>
+          <el-upload
+            v-model:file-list="reportForm.fileList"
+            list-type="picture-card"
+            :limit="9"
+            :on-preview="handleReportPreview"
+            :on-remove="handleReportRemove"
+            :before-upload="beforeReportUpload"
+            :http-request="handleReportUpload"
+            accept="image/*"
+            multiple
+          >
+            <el-icon :size="24">
+              <Plus />
+            </el-icon>
+          </el-upload>
+        </div>
+
+        <!-- 同意协议 -->
+        <div class="report-agreement">
+          <el-checkbox v-model="reportForm.agreed"> 同意发票用户协议 </el-checkbox>
+        </div>
+      </div>
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="reportDialogVisible = false"> 取消 </el-button>
+          <el-button type="primary" :loading="reportSubmitting" @click="handleSubmitReport"> 提交 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="myDynamic">
+import { ref, reactive, computed, onMounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import {
+  Avatar,
+  Picture,
+  VideoPlay,
+  View,
+  Search,
+  Close,
+  Star,
+  StarFilled,
+  ChatDotRound,
+  Share,
+  MoreFilled,
+  Edit,
+  Delete,
+  Warning,
+  Plus
+} from "@element-plus/icons-vue";
+// import { getMyDynamicList, getMyLikedList, getRelationList, followUser, unfollowUser, getDynamicDetail } from "@/api/modules/dynamicManagement";
+
+const router = useRouter();
+
+// 接口定义
+interface UserInfo {
+  name: string;
+  avatar: string;
+  bio: string;
+  followCount: number;
+  fansCount: number;
+  likeCount: number;
+}
+
+interface ContentItem {
+  id: number;
+  title: string;
+  coverUrl: string;
+  type: "image" | "video";
+  viewCount: number;
+  showLabel?: boolean;
+  labelText?: string;
+  createTime: string;
+}
+
+interface DetailItem extends ContentItem {
+  author?: {
+    id: number;
+    name: string;
+    avatar: string;
+  };
+  description: string;
+  publishTime: string;
+  likeCount: number;
+  commentCount: number;
+  isLiked: boolean;
+}
+
+interface RelationUser {
+  id: number;
+  name: string;
+  avatar: string;
+  description: string;
+  relationStatus: "following" | "mutual" | "none"; // following: 已关注, mutual: 互相关注, none: 未关注
+}
+
+// 响应式数据
+const activeTab = ref("dynamic");
+const contentList = ref<ContentItem[]>([]);
+
+// 详情 Drawer 相关
+const detailDrawerVisible = ref(false);
+const currentDetail = ref<DetailItem | null>(null);
+
+// 关系对话框相关
+const relationDialogVisible = ref(false);
+const relationActiveTab = ref("friend");
+const relationSearch = ref("");
+const relationList = ref<RelationUser[]>([]);
+
+// 举报对话框相关
+const reportDialogVisible = ref(false);
+const reportSubmitting = ref(false);
+const reportForm = reactive({
+  reason: "",
+  description: "",
+  fileList: [] as any[],
+  agreed: false
+});
+
+// 用户信息
+const userInfo = reactive<UserInfo>({
+  name: "白己的流浪主页",
+  avatar: "",
+  bio: "一家好吃的火锅店",
+  followCount: 19,
+  fansCount: 10,
+  likeCount: 10
+});
+
+// 分页
+const pagination = reactive({
+  page: 1,
+  pageSize: 12,
+  total: 0
+});
+
+// 计算分页后的列表
+const paginatedList = computed(() => {
+  const start = (pagination.page - 1) * pagination.pageSize;
+  const end = start + pagination.pageSize;
+  return contentList.value.slice(start, end);
+});
+
+// 对话框标题
+const relationDialogTitle = computed(() => {
+  const titles = {
+    friend: "好友",
+    follow: "关注",
+    fans: "粉丝"
+  };
+  return titles[relationActiveTab.value as keyof typeof titles];
+});
+
+// 过滤后的关系列表
+const filteredRelationList = computed(() => {
+  if (!relationSearch.value) {
+    return relationList.value;
+  }
+  const keyword = relationSearch.value.toLowerCase();
+  return relationList.value.filter(
+    user => user.name.toLowerCase().includes(keyword) || user.description.toLowerCase().includes(keyword)
+  );
+});
+
+// 标签切换
+const handleTabClick = () => {
+  pagination.page = 1;
+  loadContentList();
+};
+
+// 分页大小改变
+const handleSizeChange = (val: number) => {
+  pagination.pageSize = val;
+  pagination.page = 1;
+};
+
+// 当前页改变
+const handleCurrentChange = (val: number) => {
+  pagination.page = val;
+};
+
+// 点击卡片
+const handleCardClick = async (item: ContentItem) => {
+  try {
+    // TODO: 集成真实接口时,取消下面的注释
+    // const res = await getDynamicDetail({ id: item.id });
+    // currentDetail.value = res.data;
+
+    // 临时方案:使用模拟数据
+    currentDetail.value = {
+      ...item,
+      author: {
+        id: 1,
+        name: "探店达人小唐",
+        avatar: ""
+      },
+      description: "谁懂啊!住老社区转角误打误撞 展开",
+      publishTime: "2025年2月27日 10:20",
+      likeCount: Math.floor(Math.random() * 1000),
+      commentCount: Math.floor(Math.random() * 100),
+      isLiked: false
+    };
+
+    detailDrawerVisible.value = true;
+  } catch (error) {
+    ElMessage.error("加载详情失败");
+  }
+};
+
+// 关闭详情
+const handleCloseDetail = () => {
+  detailDrawerVisible.value = false;
+  setTimeout(() => {
+    currentDetail.value = null;
+  }, 300);
+};
+
+// 详情页点赞
+const handleDetailLike = () => {
+  if (!currentDetail.value) return;
+
+  currentDetail.value.isLiked = !currentDetail.value.isLiked;
+  currentDetail.value.likeCount += currentDetail.value.isLiked ? 1 : -1;
+
+  ElMessage.success(currentDetail.value.isLiked ? "点赞成功" : "取消点赞");
+};
+
+// 显示评论
+const handleShowComments = () => {
+  ElMessage.info("评论功能开发中");
+  // TODO: 实现评论功能
+};
+
+// 分享
+const handleShare = () => {
+  ElMessage.success("分享链接已复制");
+  // TODO: 实现分享功能
+};
+
+// 编辑动态
+const handleEditDynamic = () => {
+  if (!currentDetail.value) return;
+
+  // 关闭详情页
+  detailDrawerVisible.value = false;
+
+  // 跳转到编辑页面
+  router.push({
+    path: "/dynamicManagement/publishDynamic",
+    query: { id: currentDetail.value.id }
+  });
+};
+
+// 删除动态
+const handleDeleteDynamic = async () => {
+  if (!currentDetail.value) return;
+
+  try {
+    await ElMessageBox.confirm("确定要删除这条动态吗?删除后将无法恢复。", "删除确认", {
+      confirmButtonText: "确定删除",
+      cancelButtonText: "取消",
+      type: "warning",
+      confirmButtonClass: "el-button--danger"
+    });
+
+    // TODO: 集成真实接口时,取消下面的注释
+    // await deleteDynamic({ id: currentDetail.value.id });
+
+    ElMessage.success("删除成功");
+
+    // 关闭详情页
+    detailDrawerVisible.value = false;
+
+    // 从列表中移除该项
+    const index = contentList.value.findIndex(item => item.id === currentDetail.value?.id);
+    if (index > -1) {
+      contentList.value.splice(index, 1);
+      pagination.total = contentList.value.length;
+    }
+
+    // 重新加载列表
+    loadContentList();
+  } catch {
+    // 用户取消删除
+  }
+};
+
+// 举报动态
+const handleReportDynamic = () => {
+  reportDialogVisible.value = true;
+};
+
+// 举报图片预览
+const handleReportPreview = (uploadFile: any) => {
+  // 可以添加图片预览功能
+  console.log("预览图片", uploadFile);
+};
+
+// 移除举报图片
+const handleReportRemove = (uploadFile: any, uploadFiles: any[]) => {
+  // 已由 v-model:file-list 自动处理
+};
+
+// 举报图片上传前验证
+const beforeReportUpload = (file: File) => {
+  const isImage = file.type.startsWith("image/");
+  const isLt5M = file.size / 1024 / 1024 < 5;
+
+  if (!isImage) {
+    ElMessage.error("只能上传图片文件!");
+    return false;
+  }
+  if (!isLt5M) {
+    ElMessage.error("图片大小不能超过 5MB!");
+    return false;
+  }
+  return true;
+};
+
+// 自定义举报图片上传
+const handleReportUpload = async (options: any) => {
+  const { file } = options;
+
+  try {
+    // TODO: 集成真实上传接口时,取消下面的注释
+    // const uploadFormData = new FormData();
+    // uploadFormData.append('file', file);
+    // const res = await uploadDynamicImage(uploadFormData);
+    // // 上传成功后更新文件列表
+    // return res;
+
+    // 临时方案:使用 FileReader 模拟上传
+    return new Promise((resolve, reject) => {
+      const reader = new FileReader();
+      reader.onload = e => {
+        resolve({
+          url: e.target?.result
+        });
+      };
+      reader.onerror = reject;
+      reader.readAsDataURL(file);
+    });
+  } catch (error) {
+    ElMessage.error("图片上传失败");
+    throw error;
+  }
+};
+
+// 提交举报
+const handleSubmitReport = async () => {
+  // 验证表单
+  if (!reportForm.reason) {
+    ElMessage.warning("请选择举报原因");
+    return;
+  }
+
+  if (!reportForm.description.trim()) {
+    ElMessage.warning("请填写详细描述");
+    return;
+  }
+
+  if (!reportForm.agreed) {
+    ElMessage.warning("请同意发票用户协议");
+    return;
+  }
+
+  reportSubmitting.value = true;
+
+  try {
+    // TODO: 集成真实接口时,取消下面的注释
+    // await reportDynamic({
+    //   dynamicId: currentDetail.value?.id,
+    //   reason: reportForm.reason,
+    //   description: reportForm.description,
+    //   images: reportForm.fileList.map(f => f.url)
+    // });
+
+    // 模拟提交延迟
+    await new Promise(resolve => setTimeout(resolve, 1000));
+
+    ElMessage.success("举报提交成功,我们会尽快处理");
+    reportDialogVisible.value = false;
+  } catch (error) {
+    ElMessage.error("举报提交失败");
+  } finally {
+    reportSubmitting.value = false;
+  }
+};
+
+// 关闭举报对话框
+const handleCloseReportDialog = () => {
+  reportForm.reason = "";
+  reportForm.description = "";
+  reportForm.fileList = [];
+  reportForm.agreed = false;
+};
+
+// 加载内容列表
+const loadContentList = async () => {
+  try {
+    // TODO: 集成真实接口时,取消下面的注释
+    // const apiMethod = activeTab.value === 'dynamic' ? getMyDynamicList : getMyLikedList;
+    // const res = await apiMethod({
+    //   page: pagination.page,
+    //   pageSize: pagination.pageSize
+    // });
+    // contentList.value = res.data.list;
+    // pagination.total = res.data.total;
+
+    // 临时方案:使用模拟数据
+    const mockData: ContentItem[] = Array.from({ length: 24 }, (_, i) => ({
+      id: i + 1,
+      title: i === 0 ? "本地遍辑" : `动态标题 ${i + 1}`,
+      coverUrl: "",
+      type: i % 3 === 0 ? "image" : "video",
+      viewCount: Math.floor(Math.random() * 100) + 10,
+      showLabel: i === 0,
+      labelText: "本地遍辑",
+      createTime: new Date().toISOString()
+    }));
+
+    contentList.value = mockData;
+    pagination.total = mockData.length;
+  } catch (error) {
+    ElMessage.error("加载内容失败");
+  }
+};
+
+// 打开关系对话框
+const openRelationDialog = (type: "friend" | "follow" | "fans") => {
+  relationActiveTab.value = type;
+  relationDialogVisible.value = true;
+  loadRelationList(type);
+};
+
+// 关系标签切换
+const handleRelationTabClick = () => {
+  relationSearch.value = "";
+  loadRelationList(relationActiveTab.value as "friend" | "follow" | "fans");
+};
+
+// 搜索关系列表
+const handleRelationSearch = () => {
+  // 搜索由计算属性自动处理
+};
+
+// 加载关系列表
+const loadRelationList = async (type: "friend" | "follow" | "fans") => {
+  try {
+    // TODO: 集成真实接口时,取消下面的注释
+    // const res = await getRelationList({ type });
+    // relationList.value = res.data.list;
+
+    // 临时方案:使用模拟数据
+    const mockData: RelationUser[] = Array.from({ length: 10 }, (_, i) => ({
+      id: i + 1,
+      name: "好吃的店",
+      avatar: "",
+      description: i === 0 ? "欢迎来领这里" : "欢迎来这里",
+      relationStatus: i === 0 ? "mutual" : i % 2 === 0 ? "following" : "none"
+    }));
+
+    relationList.value = mockData;
+  } catch (error) {
+    ElMessage.error("加载列表失败");
+  }
+};
+
+// 关注用户
+const handleFollow = async (user: RelationUser) => {
+  try {
+    // TODO: 集成真实接口时,取消下面的注释
+    // await followUser({ userId: user.id });
+
+    user.relationStatus = "following";
+    ElMessage.success("关注成功");
+
+    // 更新用户统计信息
+    if (relationActiveTab.value === "fans") {
+      userInfo.followCount++;
+    }
+  } catch (error) {
+    ElMessage.error("关注失败");
+  }
+};
+
+// 取消关注用户
+const handleUnfollow = async (user: RelationUser) => {
+  try {
+    await ElMessageBox.confirm("确定要取消关注吗?", "提示", {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning"
+    });
+
+    // TODO: 集成真实接口时,取消下面的注释
+    // await unfollowUser({ userId: user.id });
+
+    user.relationStatus = "none";
+    ElMessage.success("已取消关注");
+
+    // 更新用户统计信息
+    if (relationActiveTab.value === "follow") {
+      userInfo.followCount--;
+    }
+  } catch {
+    // 用户取消操作
+  }
+};
+
+// 关闭关系对话框
+const handleCloseRelationDialog = () => {
+  relationSearch.value = "";
+  relationList.value = [];
+};
+
+// 初始化
+onMounted(() => {
+  loadContentList();
+
+  // 为统计数据添加点击事件监听
+  // 这部分通过模板中的 @click 直接处理
+});
+</script>
+
+<style scoped lang="scss">
+.my-dynamic-container {
+  min-height: calc(100vh - 120px);
+  padding: 20px;
+  background: #ffffff;
+
+  // 用户信息卡片
+  .user-card {
+    padding: 24px;
+    margin-bottom: 20px;
+    background: #ffffff;
+    border: 1px solid #e4e7ed;
+    border-radius: 8px;
+    .user-header {
+      margin-bottom: 16px;
+      .user-avatar-section {
+        display: flex;
+        gap: 16px;
+        align-items: center;
+        .user-avatar-large {
+          display: flex;
+          flex-shrink: 0;
+          align-items: center;
+          justify-content: center;
+          width: 80px;
+          height: 80px;
+          overflow: hidden;
+          background: #f5f7fa;
+          border-radius: 50%;
+          img {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+          }
+        }
+        .user-info-text {
+          flex: 1;
+          .user-name {
+            margin-bottom: 8px;
+            font-size: 20px;
+            font-weight: 600;
+            color: #303133;
+          }
+          .user-stats {
+            display: flex;
+            gap: 8px;
+            align-items: center;
+            font-size: 14px;
+            color: #606266;
+            .stat-item {
+              cursor: pointer;
+              transition: color 0.3s;
+              &:hover {
+                color: #409eff;
+              }
+            }
+            .stat-divider {
+              color: #dcdfe6;
+            }
+          }
+        }
+      }
+    }
+    .user-bio {
+      font-size: 14px;
+      line-height: 1.6;
+      color: #606266;
+    }
+  }
+
+  // 标签页区域
+  .tabs-section {
+    margin-bottom: 20px;
+    border-bottom: 1px solid #e4e7ed;
+    :deep(.el-tabs) {
+      .el-tabs__header {
+        margin-bottom: 0;
+      }
+      .el-tabs__nav-wrap::after {
+        display: none;
+      }
+    }
+  }
+
+  // 内容区域
+  .content-section {
+    margin-top: 20px;
+  }
+
+  // 内容网格布局
+  .content-grid {
+    display: grid;
+    grid-template-columns: repeat(4, 1fr);
+    gap: 16px;
+    margin-bottom: 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 {
+    cursor: pointer;
+    transition: all 0.3s;
+    &:hover {
+      transform: translateY(-2px);
+      .content-cover-wrapper {
+        .play-overlay {
+          opacity: 1;
+        }
+      }
+    }
+
+    // 封面区域
+    .content-cover-wrapper {
+      position: relative;
+      width: 100%;
+      aspect-ratio: 16 / 9;
+      margin-bottom: 8px;
+      overflow: hidden;
+      background: #f5f7fa;
+      border-radius: 8px;
+      .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%));
+        }
+      }
+
+      // 标题标签
+      .content-label {
+        position: absolute;
+        top: 8px;
+        left: 8px;
+        padding: 4px 12px;
+        font-size: 12px;
+        color: #ffffff;
+        background: rgb(0 0 0 / 60%);
+        backdrop-filter: blur(4px);
+        border-radius: 4px;
+      }
+    }
+
+    // 底部信息
+    .content-footer {
+      display: flex;
+      gap: 4px;
+      align-items: center;
+      padding: 0 4px;
+      .view-count {
+        font-size: 13px;
+        color: #666666;
+      }
+    }
+  }
+
+  // 空状态
+  .empty-section {
+    padding: 80px 0;
+    text-align: center;
+  }
+
+  // 分页
+  .pagination-section {
+    display: flex;
+    justify-content: center;
+    padding: 20px 0;
+    margin-top: 30px;
+  }
+
+  // 详情 Drawer
+  :deep(.detail-drawer) {
+    .el-drawer__header {
+      position: absolute;
+      top: 0;
+      right: 0;
+      left: 0;
+      z-index: 10;
+      padding: 0;
+      margin: 0;
+      background: transparent;
+    }
+    .el-drawer__body {
+      padding: 0;
+      background: #000000;
+    }
+    .drawer-header {
+      padding: 20px;
+      .close-btn {
+        padding: 8px;
+        font-size: 24px;
+        color: #ffffff;
+        &:hover {
+          background: rgb(255 255 255 / 10%);
+        }
+      }
+    }
+    .detail-content {
+      position: relative;
+      display: flex;
+      width: 100%;
+      height: 100%;
+
+      // 主内容区域
+      .detail-main {
+        display: flex;
+        flex: 1;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        padding: 80px 120px 40px 40px;
+        .media-container {
+          display: flex;
+          flex: 1;
+          align-items: center;
+          justify-content: center;
+          width: 100%;
+          max-width: 800px;
+          .detail-media {
+            max-width: 100%;
+            max-height: 100%;
+            object-fit: contain;
+            border-radius: 8px;
+          }
+          .media-placeholder {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            width: 400px;
+            height: 400px;
+            background: rgb(255 255 255 / 5%);
+            border-radius: 8px;
+          }
+        }
+        .detail-info {
+          width: 100%;
+          max-width: 800px;
+          padding: 20px 0;
+          .author-info {
+            display: flex;
+            gap: 12px;
+            align-items: center;
+            margin-bottom: 16px;
+            .author-avatar {
+              display: flex;
+              align-items: center;
+              justify-content: center;
+              width: 40px;
+              height: 40px;
+              overflow: hidden;
+              background: rgb(255 255 255 / 10%);
+              border-radius: 50%;
+              img {
+                width: 100%;
+                height: 100%;
+                object-fit: cover;
+              }
+            }
+            .author-details {
+              flex: 1;
+              .author-name {
+                margin-bottom: 4px;
+                font-size: 16px;
+                font-weight: 500;
+                color: #ffffff;
+              }
+              .publish-time {
+                font-size: 13px;
+                color: rgb(255 255 255 / 60%);
+              }
+            }
+          }
+          .detail-description {
+            font-size: 15px;
+            line-height: 1.6;
+            color: #ffffff;
+            p {
+              display: inline;
+              margin: 0;
+            }
+            .expand-btn {
+              margin-left: 4px;
+              color: rgb(255 255 255 / 60%);
+              cursor: pointer;
+              &:hover {
+                color: #ffffff;
+              }
+            }
+          }
+        }
+      }
+
+      // 右侧操作栏
+      .action-bar {
+        position: fixed;
+        right: 40px;
+        bottom: 100px;
+        z-index: 10;
+        display: flex;
+        flex-direction: column;
+        gap: 24px;
+        .action-item {
+          display: flex;
+          flex-direction: column;
+          gap: 6px;
+          align-items: center;
+          cursor: pointer;
+          transition: transform 0.3s;
+          &:hover {
+            transform: scale(1.1);
+          }
+          &.author-action {
+            cursor: default;
+            &:hover {
+              transform: none;
+            }
+          }
+          .action-avatar {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            width: 48px;
+            height: 48px;
+            overflow: hidden;
+            background: rgb(255 255 255 / 20%);
+            border: 2px solid #ffffff;
+            border-radius: 50%;
+            img {
+              width: 100%;
+              height: 100%;
+              object-fit: cover;
+            }
+          }
+          .action-icon {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            width: 48px;
+            height: 48px;
+            background: rgb(0 0 0 / 50%);
+            backdrop-filter: blur(10px);
+            border-radius: 50%;
+          }
+          .action-count {
+            font-size: 13px;
+            color: #ffffff;
+            text-align: center;
+            text-shadow: 0 1px 3px rgb(0 0 0 / 50%);
+          }
+        }
+      }
+    }
+  }
+
+  // 更多操作 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 / 10%);
+    .el-popper__arrow::before {
+      background: rgb(0 0 0 / 90%);
+      border: 1px solid rgb(255 255 255 / 10%);
+    }
+    .more-actions-menu {
+      .menu-item {
+        display: flex;
+        gap: 12px;
+        align-items: center;
+        padding: 12px 16px;
+        font-size: 14px;
+        color: #ffffff;
+        cursor: pointer;
+        transition: all 0.3s;
+        &:hover {
+          background: rgb(255 255 255 / 10%);
+        }
+        .el-icon {
+          color: #ffffff;
+        }
+        span {
+          flex: 1;
+        }
+      }
+    }
+  }
+
+  // 对话框样式(关系对话框 + 举报对话框)
+  :deep(.el-dialog) {
+    // 关系对话框
+    .relation-dialog-content {
+      .el-tabs {
+        margin-bottom: 16px;
+        .el-tabs__header {
+          margin-bottom: 16px;
+        }
+      }
+      .search-box {
+        margin-bottom: 16px;
+      }
+      .relation-list {
+        max-height: 400px;
+        overflow-y: auto;
+        .relation-item {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          padding: 12px 0;
+          border-bottom: 1px solid #f5f7fa;
+          &:last-child {
+            border-bottom: none;
+          }
+          .user-info-row {
+            display: flex;
+            flex: 1;
+            gap: 12px;
+            align-items: center;
+            .user-avatar-small {
+              display: flex;
+              flex-shrink: 0;
+              align-items: center;
+              justify-content: center;
+              width: 48px;
+              height: 48px;
+              overflow: hidden;
+              background: #f5f7fa;
+              border-radius: 50%;
+              img {
+                width: 100%;
+                height: 100%;
+                object-fit: cover;
+              }
+            }
+            .user-details {
+              flex: 1;
+              min-width: 0;
+              .user-name-text {
+                margin-bottom: 4px;
+                font-size: 15px;
+                font-weight: 500;
+                color: #303133;
+              }
+              .user-desc {
+                overflow: hidden;
+                font-size: 13px;
+                color: #909399;
+                text-overflow: ellipsis;
+                white-space: nowrap;
+              }
+            }
+          }
+          .action-button {
+            margin-left: 12px;
+            .el-button {
+              min-width: 80px;
+            }
+          }
+        }
+      }
+    }
+
+    // 举报对话框
+    .report-dialog-content {
+      .report-tip {
+        margin-bottom: 20px;
+        font-size: 14px;
+        line-height: 1.6;
+        color: #606266;
+      }
+      .report-reasons {
+        margin-bottom: 20px;
+        .el-radio-group {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 12px 16px;
+          .el-radio {
+            height: auto;
+            margin-right: 0;
+            white-space: nowrap;
+            .el-radio__label {
+              font-size: 14px;
+              color: #303133;
+            }
+          }
+        }
+      }
+      .report-description {
+        margin-bottom: 20px;
+        :deep(.el-textarea__inner) {
+          font-size: 14px;
+        }
+      }
+      .report-upload {
+        margin-bottom: 20px;
+        .upload-title {
+          margin-bottom: 12px;
+          font-size: 14px;
+          font-weight: 500;
+          color: #303133;
+        }
+        :deep(.el-upload-list) {
+          display: flex;
+          flex-wrap: wrap;
+          gap: 8px;
+        }
+        :deep(.el-upload--picture-card) {
+          width: 100px;
+          height: 100px;
+          border-radius: 4px;
+        }
+        :deep(.el-upload-list--picture-card .el-upload-list__item) {
+          width: 100px;
+          height: 100px;
+          margin: 0;
+          border-radius: 4px;
+        }
+      }
+      .report-agreement {
+        .el-checkbox {
+          .el-checkbox__label {
+            font-size: 14px;
+            color: #606266;
+          }
+        }
+      }
+    }
+    .dialog-footer {
+      display: flex;
+      gap: 12px;
+      justify-content: flex-end;
+    }
+  }
+}
+</style>