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