|
|
@@ -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>
|