blob: ccb88f506353c2f3a3df34dbcded0d2dfcfcd0e4 [file] [log] [blame]
Xing Jinwenff16b1e2025-06-05 00:29:26 +08001<template>
2 <div class="section-page">
208159515458d95702025-06-09 14:46:58 +08003 <Navbar />
Xing Jinwenff16b1e2025-06-05 00:29:26 +08004 <div class="page-container">
5 <!-- 面包屑导航 -->
6 <div class="breadcrumb">
7 <el-breadcrumb separator="/">
8 <el-breadcrumb-item :to="{ path: '/forum' }">论坛首页</el-breadcrumb-item>
9 <el-breadcrumb-item>{{ sectionInfo.name }}</el-breadcrumb-item>
10 </el-breadcrumb>
11 </div>
12
13 <!-- 版块信息 -->
14 <div class="section-header">
15 <div class="section-info">
16 <div class="section-icon">
17 <el-icon size="48" :color="sectionInfo.color">
18 <component :is="sectionInfo.icon" />
19 </el-icon>
20 </div>
21 <div class="section-details">
22 <h1 class="section-name">{{ sectionInfo.name }}</h1>
23 <p class="section-description">{{ sectionInfo.description }}</p>
24 <div class="section-stats">
25 <div class="stat-item">
26 <el-icon><ChatDotRound /></el-icon>
27 <span>{{ sectionInfo.topics }} 主题</span>
28 </div>
Xing Jinwenff16b1e2025-06-05 00:29:26 +080029 </div>
30 </div>
31 </div>
32
33 <div class="section-actions">
34 <el-button type="primary" :icon="Edit" @click="showNewTopicDialog = true">
35 发布新主题
36 </el-button>
37 </div>
38 </div>
39
40 <!-- 筛选和搜索 -->
41 <div class="filter-section">
42 <div class="filter-left">
43 <el-input
44 v-model="searchQuery"
45 placeholder="搜索主题..."
46 :prefix-icon="Search"
47 @keyup.enter="handleSearch"
48 clearable
49 style="width: 300px;"
50 />
51 <el-button type="primary" @click="handleSearch">搜索</el-button>
52 </div>
53
54 <div class="filter-right">
55 <el-select v-model="sortBy" placeholder="排序方式" @change="handleFilter">
56 <el-option label="最新回复" value="last_reply" />
57 <el-option label="发布时间" value="create_time" />
58 <el-option label="回复数量" value="replies" />
59 <el-option label="浏览次数" value="views" />
60 </el-select>
61
62 <el-select v-model="filterType" placeholder="主题类型" @change="handleFilter">
63 <el-option label="全部主题" value="" />
64 <el-option label="置顶主题" value="pinned" />
65 <el-option label="热门主题" value="hot" />
66 <el-option label="精华主题" value="featured" />
67 </el-select>
68 </div>
69 </div>
70
71 <!-- 置顶主题 -->
72 <div v-if="pinnedTopics.length > 0" class="pinned-topics">
73 <h3 class="section-title">置顶主题</h3>
74 <div class="topics-list">
75 <div
76 v-for="topic in pinnedTopics"
77 :key="topic.id"
78 class="topic-item pinned"
79 @click="navigateToTopic(topic.id)"
80 >
81 <div class="topic-status">
82 <el-icon class="pin-icon"><Top /></el-icon>
83 </div>
84
85 <div class="topic-content">
86 <div class="topic-header">
87 <h4 class="topic-title">{{ topic.title }}</h4>
88 <div class="topic-tags">
89 <el-tag type="warning" size="small">置顶</el-tag>
90 <el-tag v-if="topic.hot" type="danger" size="small">热门</el-tag>
91 <el-tag v-if="topic.featured" type="success" size="small">精华</el-tag>
92 </div>
93 </div>
94
95 <div class="topic-meta">
96 <div class="author-info">
208159515458d95702025-06-09 14:46:58 +080097 <el-avatar :size="24">{{ topic.user?.username ? topic.user.username.charAt(0) : 'A' }}</el-avatar>
98 <span class="author-name">{{ topic.user?.username || '匿名' }}</span>
Xing Jinwenff16b1e2025-06-05 00:29:26 +080099 <span class="create-time">{{ formatTime(topic.createTime) }}</span>
100 </div>
101
102 <div class="topic-stats">
103 <span class="stat-item">
104 <el-icon><View /></el-icon>
105 {{ topic.views }}
106 </span>
107 <span class="stat-item">
108 <el-icon><Comment /></el-icon>
109 {{ topic.replies }}
110 </span>
111 </div>
112 </div>
113 </div>
114
115 <div class="last-reply">
116 <div v-if="topic.lastReply" class="reply-info">
117 <div class="reply-author">{{ topic.lastReply.author }}</div>
118 <div class="reply-time">{{ formatTime(topic.lastReply.time) }}</div>
119 </div>
120 </div>
121 </div>
122 </div>
123 </div>
124
125 <!-- 普通主题列表 -->
126 <div class="normal-topics">
127 <div class="section-header">
128 <h3 class="section-title">主题列表</h3>
129 <div class="results-info">
130 共 {{ totalTopics }} 个主题
131 </div>
132 </div>
133
134 <div class="topics-list" v-loading="loading">
135 <div
136 v-for="topic in topics"
137 :key="topic.id"
138 class="topic-item"
139 @click="navigateToTopic(topic.id)"
140 >
141 <div class="topic-status">
142 <el-icon v-if="topic.hasNewReplies" class="new-icon" color="#f56c6c">
143 <ChatDotRound />
144 </el-icon>
145 <el-icon v-else class="normal-icon" color="#909399">
146 <ChatLineRound />
147 </el-icon>
148 </div>
149
150 <div class="topic-content">
151 <div class="topic-header">
152 <h4 class="topic-title">{{ topic.title }}</h4>
153 <div class="topic-tags">
154 <el-tag v-if="topic.hot" type="danger" size="small">热门</el-tag>
155 <el-tag v-if="topic.featured" type="success" size="small">精华</el-tag>
156 <el-tag v-if="topic.closed" type="info" size="small">已关闭</el-tag>
157 </div>
158 </div>
159
160 <div class="topic-meta">
161 <div class="author-info">
208159515458d95702025-06-09 14:46:58 +0800162 <el-avatar :size="24">{{ topic.user?.username ? topic.user.username.charAt(0) : 'A' }}</el-avatar>
163 <span class="author-name">{{ topic.user?.username || '匿名' }}</span>
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800164 <span class="create-time">{{ formatTime(topic.createTime) }}</span>
165 </div>
166
167 <div class="topic-stats">
168 <span class="stat-item">
169 <el-icon><View /></el-icon>
170 {{ topic.views }}
171 </span>
172 <span class="stat-item">
173 <el-icon><Comment /></el-icon>
174 {{ topic.replies }}
175 </span>
176 </div>
177 </div>
178 </div>
179
180 <div class="last-reply">
181 <div v-if="topic.lastReply" class="reply-info">
182 <div class="reply-author">{{ topic.lastReply.author }}</div>
183 <div class="reply-time">{{ formatTime(topic.lastReply.time) }}</div>
184 </div>
185 <div v-else class="no-reply">暂无回复</div>
186 </div>
187 </div>
188
189 <div v-if="topics.length === 0 && !loading" class="no-topics">
190 暂无主题,快来发布第一个主题吧!
191 </div>
192 </div>
193
194 <!-- 分页 -->
195 <div class="pagination-wrapper">
196 <el-pagination
208159515458d95702025-06-09 14:46:58 +0800197 v-model="currentPage"
198 :page-size="pageSize"
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800199 :page-sizes="[20, 50, 100]"
200 :total="totalTopics"
201 layout="total, sizes, prev, pager, next, jumper"
202 @size-change="handleSizeChange"
203 @current-change="handleCurrentChange"
204 />
205 </div>
206 </div>
207 </div>
208
209 <!-- 发布新主题对话框 -->
210 <el-dialog
211 v-model="showNewTopicDialog"
212 title="发布新主题"
213 width="600px"
214 :before-close="handleCloseDialog"
215 >
216 <el-form
217 ref="topicFormRef"
218 :model="newTopic"
219 :rules="topicRules"
220 label-width="80px"
221 >
222 <el-form-item label="主题标题" prop="title">
223 <el-input
224 v-model="newTopic.title"
225 placeholder="请输入主题标题"
226 maxlength="100"
227 show-word-limit
228 />
229 </el-form-item>
230
231 <el-form-item label="主题标签">
232 <div class="tags-input">
233 <el-tag
234 v-for="tag in newTopic.tags"
235 :key="tag"
236 closable
237 @close="removeTopicTag(tag)"
238 >
239 {{ tag }}
240 </el-tag>
241 <el-input
242 v-if="tagInputVisible"
243 ref="tagInputRef"
244 v-model="tagInputValue"
245 size="small"
246 @keyup.enter="addTopicTag"
247 @blur="addTopicTag"
248 style="width: 100px;"
249 />
250 <el-button
251 v-else
252 size="small"
253 @click="showTagInput"
254 >
255 + 添加标签
256 </el-button>
257 </div>
258 </el-form-item>
259
260 <el-form-item label="主题内容" prop="content">
261 <el-input
262 v-model="newTopic.content"
263 type="textarea"
264 :rows="8"
265 placeholder="请输入主题内容..."
266 maxlength="5000"
267 show-word-limit
268 />
269 </el-form-item>
270
271 <el-form-item label="主题选项">
272 <el-checkbox-group v-model="newTopic.options">
273 <el-checkbox label="hot">申请热门</el-checkbox>
274 <el-checkbox label="featured">申请精华</el-checkbox>
275 </el-checkbox-group>
276 </el-form-item>
277 </el-form>
278
279 <template #footer>
280 <el-button @click="handleCloseDialog">取消</el-button>
281 <el-button type="primary" @click="submitNewTopic" :loading="submitting">
282 发布主题
283 </el-button>
284 </template>
285 </el-dialog>
286 </div>
287</template>
288
289<script>
290import { ref, reactive, onMounted, nextTick } from 'vue'
291import { useRoute, useRouter } from 'vue-router'
292import { ElMessage, ElMessageBox } from 'element-plus'
293import {
294 Edit,
295 Search,
296 ChatDotRound,
297 Comment,
298 User,
299 View,
300 Top,
301 ChatLineRound,
302 Film,
303 Headphones,
304 Monitor,
305 GamePad,
306 Bell,
307 QuestionFilled
308} from '@element-plus/icons-vue'
208159515458d95702025-06-09 14:46:58 +0800309import { getTopicsByForum, createTopic } from '@/api/topic'
310import Navbar from "@/components/Navbar.vue";
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800311
312export default {
313 name: 'ForumSectionView',
208159515458d95702025-06-09 14:46:58 +0800314 components: {Navbar},
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800315 setup() {
316 const route = useRoute()
317 const router = useRouter()
318 const topicFormRef = ref(null)
319 const tagInputRef = ref(null)
320
321 const loading = ref(false)
322 const showNewTopicDialog = ref(false)
323 const submitting = ref(false)
324 const tagInputVisible = ref(false)
325 const tagInputValue = ref('')
326
327 const searchQuery = ref('')
328 const sortBy = ref('last_reply')
329 const filterType = ref('')
330 const currentPage = ref(1)
331 const pageSize = ref(20)
332 const totalTopics = ref(0)
333
334 const sectionInfo = ref({
335 id: 1,
336 name: '电影讨论',
337 description: '分享和讨论电影资源,交流观影心得',
338 icon: 'Film',
339 color: '#409eff',
340 topics: 3256,
341 replies: 18934,
342 members: 1234
343 })
344
345 const newTopic = reactive({
346 title: '',
347 content: '',
348 tags: [],
349 options: []
350 })
351
352 const topicRules = {
353 title: [
354 { required: true, message: '请输入标题', trigger: 'blur' },
355 { min: 5, max: 100, message: '标题长度在 5 到 100 个字符', trigger: 'blur' }
356 ],
357 content: [
358 { required: true, message: '请输入内容', trigger: 'blur' },
359 { min: 10, max: 5000, message: '内容长度在 10 到 5000 个字符', trigger: 'blur' }
360 ]
361 }
362
363 const pinnedTopics = ref([
364 {
365 id: 1,
366 title: '【公告】本版块发帖规则和注意事项',
367 author: 'Admin',
368 createTime: '2025-05-01T10:00:00',
369 views: 5678,
370 replies: 23,
371 hot: false,
372 featured: true,
373 lastReply: {
374 author: 'User123',
375 time: '2025-06-02T15:30:00'
376 }
377 }
378 ])
379
208159515458d95702025-06-09 14:46:58 +0800380 const topics = ref([])
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800381
382 onMounted(() => {
383 const sectionId = route.params.id
384 fetchSectionData(sectionId)
208159515458d95702025-06-09 14:46:58 +0800385 fetchTopics()
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800386 })
387
388 const fetchSectionData = async (id) => {
389 loading.value = true
390 try {
391 // 模拟API调用
392 console.log('获取版块数据:', id)
393
394 // 根据版块ID设置不同的版块信息
395 const sections = {
396 1: { name: '电影讨论', description: '分享和讨论电影资源,交流观影心得', icon: 'Film', color: '#409eff' },
397 2: { name: '音乐分享', description: '音乐资源分享,音乐制作技术交流', icon: 'Headphones', color: '#67c23a' },
398 3: { name: '软件技术', description: '软件资源分享,技术问题讨论', icon: 'Monitor', color: '#e6a23c' },
399 4: { name: '游戏天地', description: '游戏资源分享,游戏攻略讨论', icon: 'GamePad', color: '#f56c6c' },
400 5: { name: '站务公告', description: '网站公告,规则说明,意见建议', icon: 'Bell', color: '#909399' },
401 6: { name: '新手求助', description: '新手问题解答,使用教程分享', icon: 'QuestionFilled', color: '#606266' }
402 }
403
404 const sectionData = sections[id] || sections[1]
405 sectionInfo.value = {
406 id: parseInt(id),
407 ...sectionData,
208159515458d95702025-06-09 14:46:58 +0800408 topics: 0 // 初始化为0,会在fetchTopics中更新为真实数量
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800409 }
410
411 totalTopics.value = 156
412
413 } catch (error) {
414 ElMessage.error('获取版块数据失败')
415 } finally {
416 loading.value = false
417 }
418 }
419
420 const formatTime = (timeString) => {
421 const date = new Date(timeString)
422 const now = new Date()
423 const diff = now - date
424 const hours = Math.floor(diff / (1000 * 60 * 60))
425
426 if (hours < 1) return '刚刚'
427 if (hours < 24) return `${hours}小时前`
428 const days = Math.floor(hours / 24)
429 if (days < 7) return `${days}天前`
430
431 return date.toLocaleDateString('zh-CN', {
432 month: '2-digit',
433 day: '2-digit',
434 hour: '2-digit',
435 minute: '2-digit'
436 })
437 }
438
439 const navigateToTopic = (topicId) => {
440 router.push(`/forum/topic/${topicId}`)
441 }
442
443 const handleSearch = () => {
444 currentPage.value = 1
445 fetchTopics()
446 }
447
448 const handleFilter = () => {
449 currentPage.value = 1
450 fetchTopics()
451 }
452
453 const fetchTopics = async () => {
454 loading.value = true
455 try {
208159515458d95702025-06-09 14:46:58 +0800456 // 调用后端API获取主题列表
457 const res = await getTopicsByForum(sectionInfo.value.id)
458 topics.value = res.data || res // 兼容不同返回结构
459 totalTopics.value = topics.value.length
460 // 同时更新顶部显示的主题数量
461 sectionInfo.value.topics = topics.value.length
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800462 } catch (error) {
463 ElMessage.error('获取主题列表失败')
464 } finally {
465 loading.value = false
466 }
467 }
468
469 const handleSizeChange = (size) => {
470 pageSize.value = size
471 currentPage.value = 1
472 fetchTopics()
473 }
474
475 const handleCurrentChange = (page) => {
476 currentPage.value = page
477 fetchTopics()
478 }
479
480 const showTagInput = () => {
481 tagInputVisible.value = true
482 nextTick(() => {
483 tagInputRef.value?.focus()
484 })
485 }
486
487 const addTopicTag = () => {
488 const tag = tagInputValue.value.trim()
489 if (tag && !newTopic.tags.includes(tag)) {
490 newTopic.tags.push(tag)
491 }
492 tagInputVisible.value = false
493 tagInputValue.value = ''
494 }
495
496 const removeTopicTag = (tag) => {
497 const index = newTopic.tags.indexOf(tag)
498 if (index > -1) {
499 newTopic.tags.splice(index, 1)
500 }
501 }
502
503 const handleCloseDialog = () => {
504 if (newTopic.title || newTopic.content) {
505 ElMessageBox.confirm(
506 '确定要关闭吗?未保存的内容将会丢失。',
507 '提示',
508 {
509 confirmButtonText: '确定',
510 cancelButtonText: '取消',
511 type: 'warning'
512 }
513 ).then(() => {
514 resetForm()
515 showNewTopicDialog.value = false
516 }).catch(() => {
517 // 用户取消
518 })
519 } else {
520 resetForm()
521 showNewTopicDialog.value = false
522 }
523 }
524
525 const submitNewTopic = async () => {
526 try {
527 await topicFormRef.value?.validate()
528
529 submitting.value = true
530
208159515458d95702025-06-09 14:46:58 +0800531 // 构建主题数据
532 const topicData = {
533 title: newTopic.title,
534 content: newTopic.content,
535 forumId: sectionInfo.value.id, // 使用当前版块ID
536 tags: newTopic.tags,
537 isPinned: newTopic.options.includes('hot'),
538 isLocked: false
539 }
540
541 console.log('提交主题数据:', topicData)
542
543 // 调用API创建主题
544 const response = await createTopic(topicData)
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800545
546 ElMessage.success('主题发布成功!')
547 resetForm()
548 showNewTopicDialog.value = false
549
550 // 刷新主题列表
551 fetchTopics()
552
553 } catch (error) {
208159515458d95702025-06-09 14:46:58 +0800554 console.error('发布主题失败:', error)
555 ElMessage.error('发布主题失败,请重试')
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800556 } finally {
557 submitting.value = false
558 }
559 }
560
561 const resetForm = () => {
562 topicFormRef.value?.resetFields()
563 newTopic.title = ''
564 newTopic.content = ''
565 newTopic.tags = []
566 newTopic.options = []
567 }
568
569 return {
570 loading,
571 showNewTopicDialog,
572 submitting,
573 tagInputVisible,
574 tagInputValue,
575 searchQuery,
576 sortBy,
577 filterType,
578 currentPage,
579 pageSize,
580 totalTopics,
581 sectionInfo,
582 pinnedTopics,
583 topics,
584 newTopic,
585 topicRules,
586 topicFormRef,
587 tagInputRef,
588 formatTime,
589 navigateToTopic,
590 handleSearch,
591 handleFilter,
592 handleSizeChange,
593 handleCurrentChange,
594 showTagInput,
595 addTopicTag,
596 removeTopicTag,
597 handleCloseDialog,
598 submitNewTopic,
599 Edit,
600 Search,
601 ChatDotRound,
602 Comment,
603 User,
604 View,
605 Top,
606 ChatLineRound,
607 Film,
608 Headphones,
609 Monitor,
610 GamePad,
611 Bell,
612 QuestionFilled
613 }
614 }
615}
616</script>
617
618<style lang="scss" scoped>
619.section-page {
620 max-width: 1200px;
621 margin: 0 auto;
622 padding: 24px;
623 background: #f5f5f5;
624 min-height: 100vh;
625}
626
627.breadcrumb {
628 margin-bottom: 16px;
629}
630
631.section-header {
632 background: #fff;
633 border-radius: 12px;
634 padding: 32px;
635 margin-bottom: 24px;
636 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
637
638 display: flex;
639 justify-content: space-between;
640 align-items: center;
641 gap: 24px;
642
643 .section-info {
644 display: flex;
645 align-items: center;
646 gap: 20px;
647 flex: 1;
648
649 .section-details {
650 .section-name {
651 font-size: 28px;
652 font-weight: 600;
653 color: #2c3e50;
654 margin: 0 0 8px 0;
655 }
656
657 .section-description {
658 font-size: 16px;
659 color: #7f8c8d;
660 margin: 0 0 16px 0;
661 }
662
663 .section-stats {
664 display: flex;
665 gap: 24px;
666
667 .stat-item {
668 display: flex;
669 align-items: center;
670 gap: 8px;
671 font-size: 14px;
672 color: #606266;
673 }
674 }
675 }
676 }
677
678 .section-actions {
679 flex-shrink: 0;
680 }
681}
682
683.filter-section {
684 background: #fff;
685 border-radius: 12px;
686 padding: 20px 24px;
687 margin-bottom: 24px;
688 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
689
690 display: flex;
691 justify-content: space-between;
692 align-items: center;
693 gap: 20px;
694
695 .filter-left {
696 display: flex;
697 align-items: center;
698 gap: 12px;
699 }
700
701 .filter-right {
702 display: flex;
703 align-items: center;
704 gap: 12px;
705
706 .el-select {
707 width: 120px;
708 }
709 }
710}
711
712.pinned-topics, .normal-topics {
713 background: #fff;
714 border-radius: 12px;
715 padding: 24px;
716 margin-bottom: 24px;
717 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
718
719 .section-header {
720 display: flex;
721 justify-content: space-between;
722 align-items: center;
723 margin-bottom: 20px;
724 background: none;
725 padding: 0;
726 box-shadow: none;
727
728 .section-title {
729 font-size: 18px;
730 font-weight: 600;
731 color: #2c3e50;
732 margin: 0;
733 }
734
735 .results-info {
736 font-size: 14px;
737 color: #909399;
738 }
739 }
740}
741
742.topics-list {
743 .topic-item {
744 display: flex;
745 align-items: center;
746 gap: 16px;
747 padding: 16px;
748 border: 1px solid #f0f0f0;
749 border-radius: 8px;
750 margin-bottom: 12px;
751 cursor: pointer;
752 transition: all 0.3s ease;
753
754 &:hover {
755 background: #f8f9fa;
756 border-color: #409eff;
757 transform: translateX(2px);
758 }
759
760 &.pinned {
761 background: linear-gradient(90deg, #fff7e6 0%, #fff 100%);
762 border-color: #e6a23c;
763 }
764
765 .topic-status {
766 width: 32px;
767 text-align: center;
768
769 .pin-icon {
770 color: #e6a23c;
771 }
772
773 .new-icon {
774 animation: pulse 2s infinite;
775 }
776 }
777
778 .topic-content {
779 flex: 1;
780
781 .topic-header {
782 display: flex;
783 align-items: center;
784 gap: 12px;
785 margin-bottom: 8px;
786
787 .topic-title {
788 font-size: 16px;
789 font-weight: 500;
790 color: #2c3e50;
791 margin: 0;
792 flex: 1;
793
794 &:hover {
795 color: #409eff;
796 }
797 }
798
799 .topic-tags {
800 .el-tag {
801 margin-left: 4px;
802 }
803 }
804 }
805
806 .topic-meta {
807 display: flex;
808 justify-content: space-between;
809 align-items: center;
810
811 .author-info {
812 display: flex;
813 align-items: center;
814 gap: 8px;
815
816 .author-name {
817 font-size: 14px;
818 font-weight: 500;
819 color: #606266;
820 }
821
822 .create-time {
823 font-size: 12px;
824 color: #909399;
825 }
826 }
827
828 .topic-stats {
829 display: flex;
830 gap: 16px;
831
832 .stat-item {
833 display: flex;
834 align-items: center;
835 gap: 4px;
836 font-size: 12px;
837 color: #909399;
838 }
839 }
840 }
841 }
842
843 .last-reply {
844 width: 150px;
845 text-align: right;
846
847 .reply-info {
848 .reply-author {
849 font-size: 14px;
850 font-weight: 500;
851 color: #606266;
852 margin-bottom: 4px;
853 }
854
855 .reply-time {
856 font-size: 12px;
857 color: #909399;
858 }
859 }
860
861 .no-reply {
862 font-size: 12px;
863 color: #c0c4cc;
864 }
865 }
866 }
867
868 .no-topics {
869 text-align: center;
870 color: #909399;
871 padding: 60px 0;
872 font-size: 16px;
873 }
874}
875
876.pagination-wrapper {
877 margin-top: 24px;
878 text-align: center;
879}
880
881.tags-input {
882 display: flex;
883 flex-wrap: wrap;
884 gap: 8px;
885 align-items: center;
886
887 .el-tag {
888 margin: 0;
889 }
890}
891
892@keyframes pulse {
893 0% {
894 transform: scale(1);
895 }
896 50% {
897 transform: scale(1.1);
898 }
899 100% {
900 transform: scale(1);
901 }
902}
903
904@media (max-width: 768px) {
905 .section-page {
906 padding: 16px;
907 }
908
909 .section-header {
910 flex-direction: column;
911 align-items: flex-start;
912 gap: 16px;
913
914 .section-info {
915 flex-direction: column;
916 text-align: center;
917
918 .section-stats {
919 justify-content: center;
920 }
921 }
922
923 .section-actions {
924 width: 100%;
925 text-align: center;
926 }
927 }
928
929 .filter-section {
930 flex-direction: column;
931 gap: 16px;
932
933 .filter-left, .filter-right {
934 width: 100%;
935 justify-content: center;
936 }
937
938 .filter-right {
939 .el-select {
940 width: 140px;
941 }
942 }
943 }
944
945 .topic-item {
946 flex-direction: column;
947 align-items: flex-start;
948 gap: 12px;
949
950 .topic-status {
951 align-self: flex-start;
952 }
953
954 .topic-content {
955 width: 100%;
956
957 .topic-meta {
958 flex-direction: column;
959 align-items: flex-start;
960 gap: 8px;
961 }
962 }
963
964 .last-reply {
965 width: 100%;
966 text-align: left;
967 }
968 }
969}
xingjinwend652cc62025-06-04 19:52:19 +0800970</style>