<template> | |
<div class="section-page"> | |
<Navbar /> | |
<div class="page-container"> | |
<!-- 面包屑导航 --> | |
<div class="breadcrumb"> | |
<el-breadcrumb separator="/"> | |
<el-breadcrumb-item :to="{ path: '/forum' }">论坛首页</el-breadcrumb-item> | |
<el-breadcrumb-item>{{ sectionInfo.name }}</el-breadcrumb-item> | |
</el-breadcrumb> | |
</div> | |
<!-- 版块信息 --> | |
<div class="section-header"> | |
<div class="section-info"> | |
<div class="section-icon"> | |
<el-icon size="48" :color="sectionInfo.color"> | |
<component :is="sectionInfo.icon" /> | |
</el-icon> | |
</div> | |
<div class="section-details"> | |
<h1 class="section-name">{{ sectionInfo.name }}</h1> | |
<p class="section-description">{{ sectionInfo.description }}</p> | |
<div class="section-stats"> | |
<div class="stat-item"> | |
<el-icon><ChatDotRound /></el-icon> | |
<span>{{ sectionInfo.topics }} 主题</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="section-actions"> | |
<el-button type="primary" :icon="Edit" @click="showNewTopicDialog = true"> | |
发布新主题 | |
</el-button> | |
</div> | |
</div> | |
<!-- <!– 筛选和搜索 –>--> | |
<!-- <div class="filter-section">--> | |
<!-- <div class="filter-left">--> | |
<!-- <el-input--> | |
<!-- v-model="searchQuery"--> | |
<!-- placeholder="搜索主题..."--> | |
<!-- :prefix-icon="Search"--> | |
<!-- @keyup.enter="handleSearch"--> | |
<!-- clearable--> | |
<!-- style="width: 300px;"--> | |
<!-- />--> | |
<!-- <el-button type="primary" @click="handleSearch">搜索</el-button>--> | |
<!-- </div>--> | |
<!-- --> | |
<!-- <div class="filter-right">--> | |
<!-- <el-select v-model="sortBy" placeholder="排序方式" @change="handleFilter">--> | |
<!-- <el-option label="最新回复" value="last_reply" />--> | |
<!-- <el-option label="发布时间" value="create_time" />--> | |
<!-- <el-option label="回复数量" value="replies" />--> | |
<!-- <el-option label="浏览次数" value="views" />--> | |
<!-- </el-select>--> | |
<!-- --> | |
<!-- <el-select v-model="filterType" placeholder="主题类型" @change="handleFilter">--> | |
<!-- <el-option label="全部主题" value="" />--> | |
<!-- <el-option label="置顶主题" value="pinned" />--> | |
<!-- <el-option label="热门主题" value="hot" />--> | |
<!-- <el-option label="精华主题" value="featured" />--> | |
<!-- </el-select>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- 置顶主题 --> | |
<!-- <div v-if="pinnedTopics.length > 0" class="pinned-topics">--> | |
<!-- <h3 class="section-title">置顶主题</h3>--> | |
<!-- <div class="topics-list">--> | |
<!-- <div--> | |
<!-- v-for="topic in pinnedTopics"--> | |
<!-- :key="topic.id"--> | |
<!-- class="topic-item pinned"--> | |
<!-- @click="navigateToTopic(topic.id)"--> | |
<!-- >--> | |
<!-- <div class="topic-status">--> | |
<!-- <el-icon class="pin-icon"><Top /></el-icon>--> | |
<!-- </div>--> | |
<!-- --> | |
<!-- <div class="topic-content">--> | |
<!-- <div class="topic-header">--> | |
<!-- <h4 class="topic-title">{{ topic.title }}</h4>--> | |
<!-- <div class="topic-tags">--> | |
<!-- <el-tag type="warning" size="small">置顶</el-tag>--> | |
<!-- <el-tag v-if="topic.hot" type="danger" size="small">热门</el-tag>--> | |
<!-- <el-tag v-if="topic.featured" type="success" size="small">精华</el-tag>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- --> | |
<!-- <div class="topic-meta">--> | |
<!-- <div class="author-info">--> | |
<!-- <el-avatar :size="24">{{ topic.user?.username ? topic.user.username.charAt(0) : 'A' }}</el-avatar>--> | |
<!-- <span class="author-name">{{ topic.user?.username || '匿名' }}</span>--> | |
<!-- <span class="create-time">{{ formatTime(topic.createTime) }}</span>--> | |
<!-- </div>--> | |
<!-- --> | |
<!-- <div class="topic-stats">--> | |
<!-- <span class="stat-item">--> | |
<!-- <el-icon><View /></el-icon>--> | |
<!-- {{ topic.views }}--> | |
<!-- </span>--> | |
<!-- <span class="stat-item">--> | |
<!-- <el-icon><Comment /></el-icon>--> | |
<!-- {{ topic.replies }}--> | |
<!-- </span>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- --> | |
<!-- <div class="last-reply">--> | |
<!-- <div v-if="topic.lastReply" class="reply-info">--> | |
<!-- <div class="reply-author">{{ topic.lastReply.author }}</div>--> | |
<!-- <div class="reply-time">{{ formatTime(topic.lastReply.time) }}</div>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- </div>--> | |
<!-- 普通主题列表 --> | |
<div class="normal-topics"> | |
<div class="section-header"> | |
<h3 class="section-title">主题列表</h3> | |
<div class="results-info"> | |
共 {{ totalTopics }} 个主题 | |
</div> | |
</div> | |
<div class="topics-list" v-loading="loading"> | |
<div | |
v-for="topic in topics" | |
:key="topic.id" | |
class="topic-item" | |
@click="navigateToTopic(topic.id)" | |
> | |
<div class="topic-status"> | |
<el-icon v-if="topic.hasNewReplies" class="new-icon" color="#f56c6c"> | |
<ChatDotRound /> | |
</el-icon> | |
<el-icon v-else class="normal-icon" color="#909399"> | |
<ChatLineRound /> | |
</el-icon> | |
</div> | |
<div class="topic-content"> | |
<div class="topic-header"> | |
<h4 class="topic-title">{{ topic.title }}</h4> | |
<div class="topic-tags"> | |
<el-tag v-if="topic.isPinned" type="warning" size="small">置顶</el-tag> | |
<el-tag v-if="topic.views > 1000" type="danger" size="small">热门</el-tag> | |
<el-tag v-if="topic.featured" type="success" size="small">精华</el-tag> | |
<el-tag v-if="topic.isLocked" type="info" size="small">已关闭</el-tag> | |
</div> | |
</div> | |
<div class="topic-meta"> | |
<div class="author-info"> | |
<el-avatar :size="24">{{ topic.user?.username ? topic.user.username.charAt(0) : 'A' }}</el-avatar> | |
<span class="author-name">{{ topic.user?.username || '匿名' }}</span> | |
<!-- <span class="create-time">{{ formatTime(topic.createdAt) }}</span>--> | |
</div> | |
<!-- <div class="topic-stats">--> | |
<!-- <span class="stat-item">--> | |
<!-- <el-icon><View /></el-icon>--> | |
<!-- {{ topic.views || 0 }}--> | |
<!-- </span>--> | |
<!-- <span class="stat-item">--> | |
<!-- <el-icon><Comment /></el-icon>--> | |
<!-- {{ topic.replies || 0 }}--> | |
<!-- </span>--> | |
<!-- </div>--> | |
</div> | |
</div> | |
<div class="last-reply"> | |
<div v-if="topic.updatedAt" class="reply-info"> | |
<div class="reply-author">最后回复</div> | |
<div class="reply-time">{{ formatTime(topic.updatedAt) }}</div> | |
</div> | |
</div> | |
</div> | |
<div v-if="topics.length === 0 && !loading" class="no-topics"> | |
暂无主题,快来发布第一个主题吧! | |
</div> | |
</div> | |
<!-- 分页 --> | |
<div class="pagination-wrapper"> | |
<el-pagination | |
v-model="currentPage" | |
:page-size="pageSize" | |
:page-sizes="[20, 50, 100]" | |
:total="totalTopics" | |
layout="total, sizes, prev, pager, next, jumper" | |
@size-change="handleSizeChange" | |
@current-change="handleCurrentChange" | |
/> | |
</div> | |
</div> | |
</div> | |
<!-- 发布新主题对话框 --> | |
<el-dialog | |
v-model="showNewTopicDialog" | |
title="发布新主题" | |
width="600px" | |
:before-close="handleCloseDialog" | |
> | |
<el-form | |
ref="topicFormRef" | |
:model="newTopic" | |
:rules="topicRules" | |
label-width="80px" | |
> | |
<el-form-item label="主题标题" prop="title"> | |
<el-input | |
v-model="newTopic.title" | |
placeholder="请输入主题标题" | |
maxlength="100" | |
show-word-limit | |
/> | |
</el-form-item> | |
<el-form-item label="主题标签"> | |
<div class="tags-input"> | |
<el-tag | |
v-for="tag in newTopic.tags" | |
:key="tag" | |
closable | |
@close="removeTopicTag(tag)" | |
> | |
{{ tag }} | |
</el-tag> | |
<el-input | |
v-if="tagInputVisible" | |
ref="tagInputRef" | |
v-model="tagInputValue" | |
size="small" | |
@keyup.enter="addTopicTag" | |
@blur="addTopicTag" | |
style="width: 100px;" | |
/> | |
<el-button | |
v-else | |
size="small" | |
@click="showTagInput" | |
> | |
+ 添加标签 | |
</el-button> | |
</div> | |
</el-form-item> | |
<el-form-item label="主题内容" prop="content"> | |
<el-input | |
v-model="newTopic.content" | |
type="textarea" | |
:rows="8" | |
placeholder="请输入主题内容..." | |
maxlength="5000" | |
show-word-limit | |
/> | |
</el-form-item> | |
<el-form-item label="主题选项"> | |
<el-checkbox-group v-model="newTopic.options"> | |
<el-checkbox label="hot">申请热门</el-checkbox> | |
<el-checkbox label="featured">申请精华</el-checkbox> | |
</el-checkbox-group> | |
</el-form-item> | |
</el-form> | |
<template #footer> | |
<el-button @click="handleCloseDialog">取消</el-button> | |
<el-button type="primary" @click="submitNewTopic" :loading="submitting"> | |
发布主题 | |
</el-button> | |
</template> | |
</el-dialog> | |
</div> | |
</template> | |
<script> | |
import { ref, reactive, onMounted, nextTick } from 'vue' | |
import { useRoute, useRouter } from 'vue-router' | |
import { ElMessage, ElMessageBox } from 'element-plus' | |
import { | |
Edit, | |
Search, | |
ChatDotRound, | |
Comment, | |
User, | |
View, | |
Top, | |
ChatLineRound, | |
Film, | |
Headphones, | |
Monitor, | |
GamePad, | |
Bell, | |
QuestionFilled, | |
Plus | |
} from '@element-plus/icons-vue' | |
import { getTopicsByForum, createTopic } from '@/api/topic' | |
import { getForumById } from '@/api/forum' | |
import Navbar from "@/components/Navbar.vue"; | |
export default { | |
name: 'ForumSectionView', | |
components: {Navbar}, | |
setup() { | |
const route = useRoute() | |
const router = useRouter() | |
const topicFormRef = ref(null) | |
const tagInputRef = ref(null) | |
const loading = ref(false) | |
const showNewTopicDialog = ref(false) | |
const submitting = ref(false) | |
const tagInputVisible = ref(false) | |
const tagInputValue = ref('') | |
const searchQuery = ref('') | |
const sortBy = ref('last_reply') | |
const filterType = ref('') | |
const currentPage = ref(1) | |
const pageSize = ref(20) | |
const totalTopics = ref(0) | |
const sectionInfo = ref({}) | |
const newTopic = reactive({ | |
title: '', | |
content: '', | |
tags: [], | |
options: [] | |
}) | |
const topicRules = { | |
title: [ | |
{ required: true, message: '请输入标题', trigger: 'blur' }, | |
{ min: 5, max: 100, message: '标题长度在 5 到 100 个字符', trigger: 'blur' } | |
], | |
content: [ | |
{ required: true, message: '请输入内容', trigger: 'blur' }, | |
{ min: 10, max: 5000, message: '内容长度在 10 到 5000 个字符', trigger: 'blur' } | |
] | |
} | |
const pinnedTopics = ref([ | |
{ | |
id: 1, | |
title: '【公告】本版块发帖规则和注意事项', | |
author: 'Admin', | |
createTime: '2025-05-01T10:00:00', | |
views: 5678, | |
replies: 23, | |
hot: false, | |
featured: true, | |
lastReply: { | |
author: 'User123', | |
time: '2025-06-02T15:30:00' | |
} | |
} | |
]) | |
const topics = ref([]) | |
onMounted(() => { | |
const sectionId = route.params.id | |
fetchSectionData(sectionId) | |
}) | |
const fetchSectionData = async (id) => { | |
loading.value = true | |
try { | |
console.log('🏁 fetchSectionData 开始,id:', id) | |
const res = await getForumById(id) | |
console.log('📥 getForumById 响应:', res) | |
sectionInfo.value = { | |
...res, | |
icon: getForumIcon(res.name), | |
color: getForumColor(res.name), | |
topics: 0 | |
} | |
console.log('📋 sectionInfo 设置完成:', sectionInfo.value) | |
console.log('🚀 准备调用 fetchTopics') | |
// sectionInfo有数据后再请求主题列表 | |
fetchTopics() | |
console.log('✅ fetchTopics 调用完成') | |
} catch (error) { | |
console.error('❌ fetchSectionData 错误:', error) | |
ElMessage.error('获取版块数据失败') | |
} finally { | |
loading.value = false | |
} | |
} | |
const formatTime = (timeString) => { | |
// 处理Java LocalDateTime数组格式: [年, 月, 日, 时, 分, 秒, 纳秒] | |
if (Array.isArray(timeString) && timeString.length >= 6) { | |
const [year, month, day, hour, minute, second] = timeString | |
const date = new Date(year, month - 1, day, hour, minute, second) // 月份需要减1 | |
const now = new Date() | |
const diff = now - date | |
const hours = Math.floor(diff / (1000 * 60 * 60)) | |
if (hours < 1) return '刚刚' | |
if (hours < 24) return `${hours}小时前` | |
const days = Math.floor(hours / 24) | |
if (days < 7) return `${days}天前` | |
return date.toLocaleDateString('zh-CN', { | |
month: '2-digit', | |
day: '2-digit', | |
hour: '2-digit', | |
minute: '2-digit' | |
}) | |
} | |
// 处理普通字符串格式 | |
const date = new Date(timeString) | |
if (isNaN(date.getTime())) return '时间未知' | |
const now = new Date() | |
const diff = now - date | |
const hours = Math.floor(diff / (1000 * 60 * 60)) | |
if (hours < 1) return '刚刚' | |
if (hours < 24) return `${hours}小时前` | |
const days = Math.floor(hours / 24) | |
if (days < 7) return `${days}天前` | |
return date.toLocaleDateString('zh-CN', { | |
month: '2-digit', | |
day: '2-digit', | |
hour: '2-digit', | |
minute: '2-digit' | |
}) | |
} | |
const navigateToTopic = (topicId) => { | |
router.push(`/forum/topic/${topicId}`) | |
} | |
const handleSearch = () => { | |
currentPage.value = 1 | |
fetchTopics() | |
} | |
const handleFilter = () => { | |
currentPage.value = 1 | |
fetchTopics() | |
} | |
const fetchTopics = async () => { | |
loading.value = true | |
try { | |
// 调试信息:确认方法被调用和参数值 | |
console.log('🔍 fetchTopics 被调用,sectionInfo.value:', sectionInfo.value) | |
console.log('🔍 sectionInfo.value.id:', sectionInfo.value.id) | |
if (!sectionInfo.value.id) { | |
console.error('❌ sectionInfo.value.id 为空,无法请求主题列表') | |
return | |
} | |
// 调用后端API获取主题列表 | |
console.log('🚀 正在请求主题列表,forumId:', sectionInfo.value.id) | |
const res = await getTopicsByForum(sectionInfo.value.id) | |
console.log('✅ 主题列表响应:', res) | |
topics.value = res.data || res // 兼容不同返回结构 | |
totalTopics.value = topics.value.length | |
// 同时更新顶部显示的主题数量 | |
sectionInfo.value.topics = topics.value.length | |
console.log('✅ topics.value:', topics.value) | |
console.log('✅ totalTopics.value:', totalTopics.value) | |
} catch (error) { | |
console.error('❌ fetchTopics 错误:', error) | |
ElMessage.error('获取主题列表失败') | |
} finally { | |
loading.value = false | |
} | |
} | |
const handleSizeChange = (size) => { | |
pageSize.value = size | |
currentPage.value = 1 | |
fetchTopics() | |
} | |
const handleCurrentChange = (page) => { | |
currentPage.value = page | |
fetchTopics() | |
} | |
const showTagInput = () => { | |
tagInputVisible.value = true | |
nextTick(() => { | |
tagInputRef.value?.focus() | |
}) | |
} | |
const addTopicTag = () => { | |
const tag = tagInputValue.value.trim() | |
if (tag && !newTopic.tags.includes(tag)) { | |
newTopic.tags.push(tag) | |
} | |
tagInputVisible.value = false | |
tagInputValue.value = '' | |
} | |
const removeTopicTag = (tag) => { | |
const index = newTopic.tags.indexOf(tag) | |
if (index > -1) { | |
newTopic.tags.splice(index, 1) | |
} | |
} | |
const handleCloseDialog = () => { | |
if (newTopic.title || newTopic.content) { | |
ElMessageBox.confirm( | |
'确定要关闭吗?未保存的内容将会丢失。', | |
'提示', | |
{ | |
confirmButtonText: '确定', | |
cancelButtonText: '取消', | |
type: 'warning' | |
} | |
).then(() => { | |
resetForm() | |
showNewTopicDialog.value = false | |
}).catch(() => { | |
// 用户取消 | |
}) | |
} else { | |
resetForm() | |
showNewTopicDialog.value = false | |
} | |
} | |
const submitNewTopic = async () => { | |
try { | |
await topicFormRef.value?.validate() | |
submitting.value = true | |
// 构建主题数据 | |
const topicData = { | |
title: newTopic.title, | |
content: newTopic.content, | |
forumId: sectionInfo.value.id, // 使用当前版块ID | |
tags: newTopic.tags, | |
isPinned: newTopic.options.includes('hot'), | |
isLocked: false | |
} | |
console.log('提交主题数据:', topicData) | |
// 调用API创建主题 | |
const response = await createTopic(topicData) | |
ElMessage.success('主题发布成功!') | |
resetForm() | |
showNewTopicDialog.value = false | |
// 刷新主题列表 | |
fetchTopics() | |
} catch (error) { | |
console.error('发布主题失败:', error) | |
ElMessage.error('发布主题失败,请重试') | |
} finally { | |
submitting.value = false | |
} | |
} | |
const resetForm = () => { | |
topicFormRef.value?.resetFields() | |
newTopic.title = '' | |
newTopic.content = '' | |
newTopic.tags = [] | |
newTopic.options = [] | |
} | |
// icon和color映射函数,和首页保持一致 | |
const getForumIcon = (name) => { | |
const iconMap = { | |
'电影讨论': 'Film', | |
'音乐分享': 'Headphones', | |
'软件技术': 'Monitor', | |
'游戏天地': 'GamePad', | |
'站务公告': 'Bell', | |
'新手求助': 'QuestionFilled' | |
} | |
return iconMap[name] || 'ChatLineRound' | |
} | |
const getForumColor = (name) => { | |
const colorMap = { | |
'电影讨论': '#409eff', | |
'音乐分享': '#67c23a', | |
'软件技术': '#e6a23c', | |
'游戏天地': '#f56c6c', | |
'站务公告': '#909399', | |
'新手求助': '#606266' | |
} | |
return colorMap[name] || '#409eff' | |
} | |
return { | |
loading, | |
showNewTopicDialog, | |
submitting, | |
tagInputVisible, | |
tagInputValue, | |
searchQuery, | |
sortBy, | |
filterType, | |
currentPage, | |
pageSize, | |
totalTopics, | |
sectionInfo, | |
pinnedTopics, | |
topics, | |
newTopic, | |
topicRules, | |
topicFormRef, | |
tagInputRef, | |
formatTime, | |
navigateToTopic, | |
handleSearch, | |
handleFilter, | |
handleSizeChange, | |
handleCurrentChange, | |
showTagInput, | |
addTopicTag, | |
removeTopicTag, | |
handleCloseDialog, | |
submitNewTopic, | |
Edit, | |
Search, | |
ChatDotRound, | |
Comment, | |
User, | |
View, | |
Top, | |
ChatLineRound, | |
Film, | |
Headphones, | |
Monitor, | |
GamePad, | |
Bell, | |
QuestionFilled, | |
Plus | |
} | |
} | |
} | |
</script> | |
<style lang="scss" scoped> | |
.section-page { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 24px; | |
background: #f5f5f5; | |
min-height: 100vh; | |
} | |
.breadcrumb { | |
margin-bottom: 16px; | |
} | |
.section-header { | |
background: #fff; | |
border-radius: 12px; | |
padding: 32px; | |
margin-bottom: 24px; | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
gap: 24px; | |
.section-info { | |
display: flex; | |
align-items: center; | |
gap: 20px; | |
flex: 1; | |
.section-details { | |
.section-name { | |
font-size: 28px; | |
font-weight: 600; | |
color: #2c3e50; | |
margin: 0 0 8px 0; | |
} | |
.section-description { | |
font-size: 16px; | |
color: #7f8c8d; | |
margin: 0 0 16px 0; | |
} | |
.section-stats { | |
display: flex; | |
gap: 24px; | |
.stat-item { | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
font-size: 14px; | |
color: #606266; | |
} | |
} | |
} | |
} | |
.section-actions { | |
flex-shrink: 0; | |
} | |
} | |
.filter-section { | |
background: #fff; | |
border-radius: 12px; | |
padding: 20px 24px; | |
margin-bottom: 24px; | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
gap: 20px; | |
.filter-left { | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
} | |
.filter-right { | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
.el-select { | |
width: 120px; | |
} | |
} | |
} | |
.pinned-topics, .normal-topics { | |
background: #fff; | |
border-radius: 12px; | |
padding: 24px; | |
margin-bottom: 24px; | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | |
.section-header { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
margin-bottom: 20px; | |
background: none; | |
padding: 0; | |
box-shadow: none; | |
.section-title { | |
font-size: 18px; | |
font-weight: 600; | |
color: #2c3e50; | |
margin: 0; | |
} | |
.results-info { | |
font-size: 14px; | |
color: #909399; | |
} | |
} | |
} | |
.topics-list { | |
.topic-item { | |
display: flex; | |
align-items: center; | |
gap: 16px; | |
padding: 16px; | |
border: 1px solid #f0f0f0; | |
border-radius: 8px; | |
margin-bottom: 12px; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
&:hover { | |
background: #f8f9fa; | |
border-color: #409eff; | |
transform: translateX(2px); | |
} | |
&.pinned { | |
background: linear-gradient(90deg, #fff7e6 0%, #fff 100%); | |
border-color: #e6a23c; | |
} | |
.topic-status { | |
width: 32px; | |
text-align: center; | |
.pin-icon { | |
color: #e6a23c; | |
} | |
.new-icon { | |
animation: pulse 2s infinite; | |
} | |
} | |
.topic-content { | |
flex: 1; | |
.topic-header { | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
margin-bottom: 8px; | |
.topic-title { | |
font-size: 16px; | |
font-weight: 500; | |
color: #2c3e50; | |
margin: 0; | |
flex: 1; | |
&:hover { | |
color: #409eff; | |
} | |
} | |
.topic-tags { | |
.el-tag { | |
margin-left: 4px; | |
} | |
} | |
} | |
.topic-meta { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
.author-info { | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
.author-name { | |
font-size: 14px; | |
font-weight: 500; | |
color: #606266; | |
} | |
.create-time { | |
font-size: 12px; | |
color: #909399; | |
} | |
} | |
.topic-stats { | |
display: flex; | |
gap: 16px; | |
.stat-item { | |
display: flex; | |
align-items: center; | |
gap: 4px; | |
font-size: 12px; | |
color: #909399; | |
} | |
} | |
} | |
} | |
.last-reply { | |
width: 150px; | |
text-align: right; | |
.reply-info { | |
.reply-author { | |
font-size: 14px; | |
font-weight: 500; | |
color: #606266; | |
margin-bottom: 4px; | |
} | |
.reply-time { | |
font-size: 12px; | |
color: #909399; | |
} | |
} | |
.no-reply { | |
font-size: 12px; | |
color: #c0c4cc; | |
} | |
} | |
} | |
.no-topics { | |
text-align: center; | |
color: #909399; | |
padding: 60px 0; | |
font-size: 16px; | |
} | |
} | |
.pagination-wrapper { | |
margin-top: 24px; | |
text-align: center; | |
} | |
.tags-input { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 8px; | |
align-items: center; | |
.el-tag { | |
margin: 0; | |
} | |
} | |
@keyframes pulse { | |
0% { | |
transform: scale(1); | |
} | |
50% { | |
transform: scale(1.1); | |
} | |
100% { | |
transform: scale(1); | |
} | |
} | |
@media (max-width: 768px) { | |
.section-page { | |
padding: 16px; | |
} | |
.section-header { | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 16px; | |
.section-info { | |
flex-direction: column; | |
text-align: center; | |
.section-stats { | |
justify-content: center; | |
} | |
} | |
.section-actions { | |
width: 100%; | |
text-align: center; | |
} | |
} | |
.filter-section { | |
flex-direction: column; | |
gap: 16px; | |
.filter-left, .filter-right { | |
width: 100%; | |
justify-content: center; | |
} | |
.filter-right { | |
.el-select { | |
width: 140px; | |
} | |
} | |
} | |
.topic-item { | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 12px; | |
.topic-status { | |
align-self: flex-start; | |
} | |
.topic-content { | |
width: 100%; | |
.topic-meta { | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 8px; | |
} | |
} | |
.last-reply { | |
width: 100%; | |
text-align: left; | |
} | |
} | |
} | |
</style> |