blob: cd7e7bfd2cea29df5dea5b4d0a92e55b298b2486 [file] [log] [blame]
Xing Jinwenff16b1e2025-06-05 00:29:26 +08001<template>
vulgar5201c4345b12025-06-09 18:48:06 +08002 <Navbar />
Xing Jinwenff16b1e2025-06-05 00:29:26 +08003 <div class="forum-page">
vulgar5201c4345b12025-06-09 18:48:06 +08004
Xing Jinwenff16b1e2025-06-05 00:29:26 +08005 <div class="page-container">
6 <!-- 论坛头部 -->
7 <div class="forum-header">
8 <div class="header-content">
9 <h1>社区论坛</h1>
10 <p class="header-description">与其他用户交流讨论,分享经验心得</p>
11 <div class="header-actions">
12 <el-button type="primary" :icon="Edit" @click="showNewTopicDialog = true">
13 发布新帖
14 </el-button>
15 </div>
16 </div>
17 </div>
18
19 <!-- 论坛统计 -->
20 <div class="forum-stats">
21 <div class="stats-grid">
22 <div class="stat-item">
23 <el-icon size="32" color="#409eff"><ChatDotRound /></el-icon>
24 <div class="stat-info">
25 <h3>{{ forumStats.totalTopics }}</h3>
26 <p>主题总数</p>
27 </div>
28 </div>
29 <div class="stat-item">
30 <el-icon size="32" color="#67c23a"><Comment /></el-icon>
31 <div class="stat-info">
32 <h3>{{ forumStats.totalReplies }}</h3>
33 <p>回复总数</p>
34 </div>
35 </div>
36 <div class="stat-item">
37 <el-icon size="32" color="#e6a23c"><User /></el-icon>
38 <div class="stat-info">
39 <h3>{{ forumStats.activeUsers }}</h3>
40 <p>活跃用户</p>
41 </div>
42 </div>
43 <div class="stat-item">
44 <el-icon size="32" color="#f56c6c"><View /></el-icon>
45 <div class="stat-info">
46 <h3>{{ forumStats.todayPosts }}</h3>
47 <p>今日发帖</p>
48 </div>
49 </div>
50 </div>
51 </div>
52
53 <!-- 版块列表 -->
54 <div class="forum-sections">
55 <h2 class="section-title">论坛版块</h2>
56 <div class="sections-list">
57 <div
58 v-for="section in forumSections"
59 :key="section.id"
60 class="section-card"
61 @click="navigateToSection(section.id)"
62 >
63 <div class="section-icon">
64 <el-icon size="48" :color="section.color">
65 <component :is="section.icon" />
66 </el-icon>
67 </div>
68 <div class="section-info">
69 <h3 class="section-name">{{ section.name }}</h3>
70 <p class="section-description">{{ section.description }}</p>
208159515458d95702025-06-09 14:46:58 +080071<!-- <div class="section-stats">-->
72<!-- <span class="stat">{{ section.topics }} 主题</span>-->
73<!-- <span class="stat">{{ section.replies }} 回复</span>-->
74<!-- </div>-->
Xing Jinwenff16b1e2025-06-05 00:29:26 +080075 </div>
76 <div class="section-latest">
77 <div v-if="section.latestTopic" class="latest-topic">
78 <p class="topic-title">{{ section.latestTopic.title }}</p>
79 <div class="topic-meta">
80 <span class="author">{{ section.latestTopic.author }}</span>
81 <span class="time">{{ formatTime(section.latestTopic.time) }}</span>
82 </div>
83 </div>
84 </div>
85 </div>
86 </div>
87 </div>
88
89 <!-- 热门主题 -->
90 <div class="hot-topics">
91 <div class="section-header">
92 <h2 class="section-title">热门主题</h2>
93 <el-button type="primary" text @click="$router.push('/forum/topics')">
94 查看全部 <el-icon><ArrowRight /></el-icon>
95 </el-button>
96 </div>
97 <div class="topics-list">
98 <div
99 v-for="topic in hotTopics"
100 :key="topic.id"
101 class="topic-item"
102 @click="navigateToTopic(topic.id)"
103 >
104 <div class="topic-content">
105 <div class="topic-header">
106 <h4 class="topic-title">{{ topic.title }}</h4>
107 <div class="topic-tags">
108 <el-tag
109 v-for="tag in topic.tags"
110 :key="tag"
111 size="small"
112 type="info"
113 >
114 {{ tag }}
115 </el-tag>
116 </div>
117 </div>
118 <div class="topic-meta">
119 <div class="author-info">
120 <el-avatar :size="24">{{ topic.author.charAt(0) }}</el-avatar>
121 <span class="author-name">{{ topic.author }}</span>
122 </div>
123 <div class="topic-stats">
124 <span class="stat-item">
125 <el-icon><View /></el-icon>
126 {{ topic.views }}
127 </span>
128 <span class="stat-item">
129 <el-icon><Comment /></el-icon>
130 {{ topic.replies }}
131 </span>
132 <span class="time">{{ formatTime(topic.lastReply) }}</span>
133 </div>
134 </div>
135 </div>
136 <div class="topic-status">
137 <el-tag v-if="topic.pinned" type="warning" size="small">置顶</el-tag>
138 <el-tag v-if="topic.hot" type="danger" size="small">热门</el-tag>
139 </div>
140 </div>
141 </div>
142 </div>
143
144 <!-- 最新回复 -->
145 <div class="recent-replies">
146 <h2 class="section-title">最新回复</h2>
147 <div class="replies-list">
148 <div
149 v-for="reply in recentReplies"
150 :key="reply.id"
151 class="reply-item"
152 @click="navigateToTopic(reply.topicId)"
153 >
154 <div class="reply-avatar">
155 <el-avatar :size="40">{{ reply.author.charAt(0) }}</el-avatar>
156 </div>
157 <div class="reply-content">
158 <div class="reply-header">
159 <span class="reply-author">{{ reply.author }}</span>
160 <span class="reply-action">回复了主题</span>
161 <span class="topic-title">{{ reply.topicTitle }}</span>
162 </div>
163 <div class="reply-text">{{ reply.content }}</div>
164 <div class="reply-time">{{ formatTime(reply.time) }}</div>
165 </div>
166 </div>
167 </div>
168 </div>
169 </div>
170
171 <!-- 发布新帖对话框 -->
172 <el-dialog
173 v-model="showNewTopicDialog"
174 title="发布新主题"
175 width="600px"
176 :before-close="handleCloseDialog"
177 >
178 <el-form
179 ref="topicFormRef"
180 :model="newTopic"
181 :rules="topicRules"
182 label-width="80px"
183 >
184 <el-form-item label="版块" prop="sectionId">
185 <el-select v-model="newTopic.sectionId" placeholder="选择版块">
186 <el-option
187 v-for="section in forumSections"
188 :key="section.id"
189 :label="section.name"
190 :value="section.id"
191 />
192 </el-select>
193 </el-form-item>
194
195 <el-form-item label="标题" prop="title">
196 <el-input
197 v-model="newTopic.title"
198 placeholder="请输入主题标题"
199 maxlength="100"
200 show-word-limit
201 />
202 </el-form-item>
203
204 <el-form-item label="标签">
205 <div class="tags-input">
206 <el-tag
207 v-for="tag in newTopic.tags"
208 :key="tag"
209 closable
210 @close="removeTopicTag(tag)"
211 >
212 {{ tag }}
213 </el-tag>
214 <el-input
215 v-if="tagInputVisible"
216 ref="tagInputRef"
217 v-model="tagInputValue"
218 size="small"
219 @keyup.enter="addTopicTag"
220 @blur="addTopicTag"
221 style="width: 100px;"
222 />
223 <el-button
224 v-else
225 size="small"
226 @click="showTagInput"
227 >
228 + 添加标签
229 </el-button>
230 </div>
231 </el-form-item>
232
233 <el-form-item label="内容" prop="content">
234 <el-input
235 v-model="newTopic.content"
236 type="textarea"
237 :rows="8"
238 placeholder="请输入主题内容..."
239 maxlength="5000"
240 show-word-limit
241 />
242 </el-form-item>
243 </el-form>
244
245 <template #footer>
246 <el-button @click="handleCloseDialog">取消</el-button>
247 <el-button type="primary" @click="submitNewTopic" :loading="submitting">
248 发布主题
249 </el-button>
250 </template>
251 </el-dialog>
252 </div>
253</template>
254
255<script>
256import { ref, reactive, onMounted, nextTick } from 'vue'
257import { useRouter } from 'vue-router'
258import { ElMessage, ElMessageBox } from 'element-plus'
vulgar5201c4345b12025-06-09 18:48:06 +0800259import {
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800260 Edit,
261 ChatDotRound,
262 Comment,
263 User,
264 View,
265 ArrowRight,
266 Film,
267 Headphones,
268 Monitor,
269 GamePad,
270 ChatLineRound,
271 QuestionFilled,
272 Bell
273} from '@element-plus/icons-vue'
208159515458d95702025-06-09 14:46:58 +0800274import {
275 getAllTopics,
276 getTopicsByForum,
277 createTopic,
278 searchTopics
279} from '@/api/topic'
280import {
281 getAllForums,
282 getForumById
283} from '@/api/forum'
284import Navbar from "@/components/Navbar.vue";
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800285
286export default {
287 name: 'ForumView',
208159515458d95702025-06-09 14:46:58 +0800288 components: {Navbar},
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800289 setup() {
290 const router = useRouter()
291 const topicFormRef = ref(null)
292 const tagInputRef = ref(null)
293
294 const showNewTopicDialog = ref(false)
295 const submitting = ref(false)
296 const tagInputVisible = ref(false)
297 const tagInputValue = ref('')
298
299 const forumStats = reactive({
208159515458d95702025-06-09 14:46:58 +0800300 totalTopics: 0,
301 totalReplies: 0,
302 activeUsers: 0,
303 todayPosts: 0
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800304 })
305
306 const newTopic = reactive({
307 sectionId: '',
308 title: '',
309 content: '',
310 tags: []
311 })
312
313 const topicRules = {
314 sectionId: [
315 { required: true, message: '请选择版块', trigger: 'change' }
316 ],
317 title: [
318 { required: true, message: '请输入标题', trigger: 'blur' },
319 { min: 5, max: 100, message: '标题长度在 5 到 100 个字符', trigger: 'blur' }
320 ],
321 content: [
322 { required: true, message: '请输入内容', trigger: 'blur' },
323 { min: 10, max: 5000, message: '内容长度在 10 到 5000 个字符', trigger: 'blur' }
324 ]
325 }
326
208159515458d95702025-06-09 14:46:58 +0800327 const forumSections = ref([])
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800328
208159515458d95702025-06-09 14:46:58 +0800329 const hotTopics = ref([])
330 const recentReplies = ref([])
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800331
208159515458d95702025-06-09 14:46:58 +0800332 const loadForumData = async () => {
333 try {
334 // 获取所有论坛
335 const forums = await getAllForums()
336 forumSections.value = forums.map(forum => ({
337 id: forum.id,
338 name: forum.name,
339 description: forum.description,
340 icon: getForumIcon(forum.name),
341 color: getForumColor(forum.name),
342 topics: forum.topicCount || 0,
343 replies: forum.replyCount || 0,
344 latestTopic: forum.latestTopic ? {
345 title: forum.latestTopic.title,
346 author: forum.latestTopic.user?.username || '匿名用户',
347 time: forum.latestTopic.createTime
348 } : null
349 }))
350
351 // 获取所有主题
352 const topics = await getAllTopics()
353 hotTopics.value = topics.slice(0, 3).map(topic => ({
354 id: topic.id,
355 title: topic.title,
356 author: topic.user?.username || '匿名用户',
357 views: topic.views || 0,
358 replies: topic.replies || 0,
359 lastReply: topic.lastReplyTime,
360 tags: topic.tags || [],
361 pinned: topic.pinned || false,
362 hot: topic.views > 1000
363 }))
364
365 // 更新统计数据
366 forumStats.totalTopics = topics.length
367 forumStats.totalReplies = topics.reduce((sum, topic) => sum + (topic.replies || 0), 0)
368 forumStats.activeUsers = new Set(topics.map(topic => topic.user?.id)).size
369 forumStats.todayPosts = topics.filter(topic => {
370 const topicDate = new Date(topic.createTime)
371 const today = new Date()
372 return topicDate.toDateString() === today.toDateString()
373 }).length
374
375 } catch (error) {
376 console.error('加载论坛数据失败:', error)
377 ElMessage.error('加载论坛数据失败')
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800378 }
208159515458d95702025-06-09 14:46:58 +0800379 }
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800380
208159515458d95702025-06-09 14:46:58 +0800381 const getForumIcon = (name) => {
382 const iconMap = {
383 '电影讨论': 'Film',
384 '音乐分享': 'Headphones',
385 '软件技术': 'Monitor',
386 '游戏天地': 'GamePad',
387 '站务公告': 'Bell',
388 '新手求助': 'QuestionFilled'
389 }
390 return iconMap[name] || 'ChatLineRound'
391 }
392
393 const getForumColor = (name) => {
394 const colorMap = {
395 '电影讨论': '#409eff',
396 '音乐分享': '#67c23a',
397 '软件技术': '#e6a23c',
398 '游戏天地': '#f56c6c',
399 '站务公告': '#909399',
400 '新手求助': '#606266'
401 }
402 return colorMap[name] || '#409eff'
403 }
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800404
405 const formatTime = (timeString) => {
406 const date = new Date(timeString)
407 const now = new Date()
408 const diff = now - date
409 const hours = Math.floor(diff / (1000 * 60 * 60))
410
411 if (hours < 1) return '刚刚'
412 if (hours < 24) return `${hours}小时前`
413 const days = Math.floor(hours / 24)
414 return `${days}天前`
415 }
416
417 const navigateToSection = (sectionId) => {
418 router.push(`/forum/section/${sectionId}`)
419 }
420
421 const navigateToTopic = (topicId) => {
422 router.push(`/forum/topic/${topicId}`)
423 }
424
425 const showTagInput = () => {
426 tagInputVisible.value = true
427 nextTick(() => {
428 tagInputRef.value?.focus()
429 })
430 }
431
432 const addTopicTag = () => {
433 const tag = tagInputValue.value.trim()
434 if (tag && !newTopic.tags.includes(tag)) {
435 newTopic.tags.push(tag)
436 }
437 tagInputVisible.value = false
438 tagInputValue.value = ''
439 }
440
441 const removeTopicTag = (tag) => {
442 const index = newTopic.tags.indexOf(tag)
443 if (index > -1) {
444 newTopic.tags.splice(index, 1)
445 }
446 }
447
448 const handleCloseDialog = () => {
449 if (newTopic.title || newTopic.content) {
450 ElMessageBox.confirm(
451 '确定要关闭吗?未保存的内容将会丢失。',
452 '提示',
453 {
454 confirmButtonText: '确定',
455 cancelButtonText: '取消',
456 type: 'warning'
457 }
458 ).then(() => {
459 resetForm()
460 showNewTopicDialog.value = false
461 }).catch(() => {
462 // 用户取消
463 })
464 } else {
465 resetForm()
466 showNewTopicDialog.value = false
467 }
468 }
469
470 const submitNewTopic = async () => {
471 try {
472 await topicFormRef.value?.validate()
473
474 submitting.value = true
475
208159515458d95702025-06-09 14:46:58 +0800476 const topicData = {
477 title: newTopic.title,
478 content: newTopic.content,
479 forumId: newTopic.sectionId,
480 tags: newTopic.tags
481 }
482
483 const response = await createTopic(topicData)
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800484
485 ElMessage.success('主题发布成功!')
486 resetForm()
487 showNewTopicDialog.value = false
488
208159515458d95702025-06-09 14:46:58 +0800489 // 刷新论坛数据
490 await loadForumData()
491
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800492 // 跳转到新创建的主题页面
208159515458d95702025-06-09 14:46:58 +0800493 router.push(`/forum/topic/${response.id}`)
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800494
495 } catch (error) {
208159515458d95702025-06-09 14:46:58 +0800496 console.error('发布主题失败:', error)
497 ElMessage.error('发布主题失败')
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800498 } finally {
499 submitting.value = false
500 }
501 }
502
503 const resetForm = () => {
504 topicFormRef.value?.resetFields()
505 newTopic.sectionId = ''
506 newTopic.title = ''
507 newTopic.content = ''
508 newTopic.tags = []
509 }
510
208159515458d95702025-06-09 14:46:58 +0800511 onMounted(() => {
512 loadForumData()
513 })
514
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800515 return {
516 showNewTopicDialog,
517 submitting,
518 tagInputVisible,
519 tagInputValue,
520 topicFormRef,
521 tagInputRef,
522 forumStats,
523 forumSections,
524 hotTopics,
525 recentReplies,
526 newTopic,
527 topicRules,
528 formatTime,
529 navigateToSection,
530 navigateToTopic,
531 showTagInput,
532 addTopicTag,
533 removeTopicTag,
534 handleCloseDialog,
535 submitNewTopic,
536 Edit,
537 ChatDotRound,
538 Comment,
539 User,
540 View,
541 ArrowRight,
542 Film,
543 Headphones,
544 Monitor,
545 GamePad,
546 Bell,
208159515458d95702025-06-09 14:46:58 +0800547 QuestionFilled,
548 loadForumData,
549 getForumIcon,
550 getForumColor
Xing Jinwenff16b1e2025-06-05 00:29:26 +0800551 }
552 }
553}
554</script>
555
556<style lang="scss" scoped>
557.forum-page {
558 max-width: 1200px;
559 margin: 0 auto;
560 padding: 24px;
561 background: #f5f5f5;
562 min-height: 100vh;
563}
564
565.forum-header {
566 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
567 border-radius: 12px;
568 padding: 48px 32px;
569 margin-bottom: 24px;
570 color: white;
571 text-align: center;
572
573 h1 {
574 font-size: 36px;
575 font-weight: 600;
576 margin: 0 0 12px 0;
577 }
578
579 .header-description {
580 font-size: 18px;
581 margin: 0 0 24px 0;
582 opacity: 0.9;
583 }
584}
585
586.forum-stats {
587 background: #fff;
588 border-radius: 12px;
589 padding: 24px;
590 margin-bottom: 24px;
591 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
592
593 .stats-grid {
594 display: grid;
595 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
596 gap: 24px;
597
598 .stat-item {
599 display: flex;
600 align-items: center;
601 gap: 16px;
602
603 .stat-info {
604 h3 {
605 font-size: 24px;
606 font-weight: 600;
607 color: #2c3e50;
608 margin: 0 0 4px 0;
609 }
610
611 p {
612 font-size: 14px;
613 color: #7f8c8d;
614 margin: 0;
615 }
616 }
617 }
618 }
619}
620
621.forum-sections {
622 background: #fff;
623 border-radius: 12px;
624 padding: 24px;
625 margin-bottom: 24px;
626 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
627
628 .section-title {
629 font-size: 20px;
630 font-weight: 600;
631 color: #2c3e50;
632 margin: 0 0 20px 0;
633 }
634
635 .sections-list {
636 .section-card {
637 display: flex;
638 align-items: center;
639 gap: 20px;
640 padding: 20px;
641 border: 1px solid #f0f0f0;
642 border-radius: 8px;
643 margin-bottom: 12px;
644 cursor: pointer;
645 transition: all 0.3s ease;
646
647 &:hover {
648 background: #f8f9fa;
649 border-color: #409eff;
650 transform: translateX(4px);
651 }
652
653 .section-info {
654 flex: 1;
655
656 .section-name {
657 font-size: 18px;
658 font-weight: 600;
659 color: #2c3e50;
660 margin: 0 0 8px 0;
661 }
662
663 .section-description {
664 font-size: 14px;
665 color: #7f8c8d;
666 margin: 0 0 12px 0;
667 }
668
669 .section-stats {
670 display: flex;
671 gap: 16px;
672
673 .stat {
674 font-size: 12px;
675 color: #909399;
676 }
677 }
678 }
679
680 .section-latest {
681 width: 200px;
682
683 .latest-topic {
684 .topic-title {
685 font-size: 14px;
686 color: #2c3e50;
687 margin: 0 0 8px 0;
688 overflow: hidden;
689 text-overflow: ellipsis;
690 display: -webkit-box;
691 -webkit-line-clamp: 2;
692 -webkit-box-orient: vertical;
693 }
694
695 .topic-meta {
696 font-size: 12px;
697 color: #909399;
698
699 .author {
700 margin-right: 8px;
701 }
702 }
703 }
704 }
705 }
706 }
707}
708
709.hot-topics {
710 background: #fff;
711 border-radius: 12px;
712 padding: 24px;
713 margin-bottom: 24px;
714 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
715
716 .section-header {
717 display: flex;
718 justify-content: space-between;
719 align-items: center;
720 margin-bottom: 20px;
721
722 .section-title {
723 font-size: 20px;
724 font-weight: 600;
725 color: #2c3e50;
726 margin: 0;
727 }
728 }
729
730 .topics-list {
731 .topic-item {
732 display: flex;
733 justify-content: space-between;
734 align-items: center;
735 padding: 16px;
736 border: 1px solid #f0f0f0;
737 border-radius: 8px;
738 margin-bottom: 12px;
739 cursor: pointer;
740 transition: all 0.3s ease;
741
742 &:hover {
743 background: #f8f9fa;
744 border-color: #409eff;
745 }
746
747 .topic-content {
748 flex: 1;
749
750 .topic-header {
751 display: flex;
752 align-items: center;
753 gap: 12px;
754 margin-bottom: 8px;
755
756 .topic-title {
757 font-size: 16px;
758 font-weight: 500;
759 color: #2c3e50;
760 margin: 0;
761 }
762
763 .topic-tags {
764 .el-tag {
765 margin-right: 4px;
766 }
767 }
768 }
769
770 .topic-meta {
771 display: flex;
772 justify-content: space-between;
773 align-items: center;
774
775 .author-info {
776 display: flex;
777 align-items: center;
778 gap: 8px;
779
780 .author-name {
781 font-size: 14px;
782 color: #7f8c8d;
783 }
784 }
785
786 .topic-stats {
787 display: flex;
788 align-items: center;
789 gap: 16px;
790 font-size: 12px;
791 color: #909399;
792
793 .stat-item {
794 display: flex;
795 align-items: center;
796 gap: 4px;
797 }
798 }
799 }
800 }
801
802 .topic-status {
803 .el-tag {
804 margin-left: 8px;
805 }
806 }
807 }
808 }
809}
810
811.recent-replies {
812 background: #fff;
813 border-radius: 12px;
814 padding: 24px;
815 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
816
817 .section-title {
818 font-size: 20px;
819 font-weight: 600;
820 color: #2c3e50;
821 margin: 0 0 20px 0;
822 }
823
824 .replies-list {
825 .reply-item {
826 display: flex;
827 flex-direction: column;
828 gap: 12px;
829 padding: 16px;
830 border: 1px solid #f0f0f0;
831 border-radius: 8px;
832 margin-bottom: 12px;
833 cursor: pointer;
834 transition: all 0.3s ease;
835
836 &:hover {
837 background: #f8f9fa;
838 border-color: #409eff;
839 }
840
841 .reply-content {
842 flex: 1;
843
844 .reply-header {
845 font-size: 14px;
846 margin-bottom: 8px;
847
848 .reply-author {
849 font-weight: 600;
850 color: #2c3e50;
851 }
852
853 .reply-action {
854 color: #7f8c8d;
855 margin: 0 4px;
856 }
857
858 .topic-title {
859 color: #409eff;
860 font-weight: 500;
861 }
862 }
863
864 .reply-text {
865 font-size: 14px;
866 color: #5a6c7d;
867 margin-bottom: 8px;
868 overflow: hidden;
869 text-overflow: ellipsis;
870 display: -webkit-box;
871 -webkit-line-clamp: 2;
872 -webkit-box-orient: vertical;
873 }
874
875 .reply-time {
876 font-size: 12px;
877 color: #909399;
878 }
879 }
880 }
881 }
882}
883</style>
884
885.tags-input {
886 display: flex;
887 flex-wrap: wrap;
888 gap: 8px;
889 align-items: center;
890
891 .el-tag {
892 margin: 0;
893 }
894}
895
896@media (max-width: 768px) {
897 .forum-page {
898 padding: 16px;
899 }
900
901 .forum-header {
902 padding: 32px 24px;
903
904 h1 {
905 font-size: 28px;
906 }
907
908 .header-description {
909 font-size: 16px;
910 }
911 }
912
913 .stats-grid {
914 grid-template-columns: repeat(2, 1fr);
915 }
916
917 .section-card {
918 flex-direction: column;
919 text-align: center;
920
921 .section-latest {
922 width: 100%;
923 margin-top: 16px;
924 }
925 }
926
927 .topic-item {
928 flex-direction: column;
929 align-items: flex-start;
930
931 .topic-status {
932 margin-top: 12px;
933 align-self: flex-end;
934 }
935 }
xingjinwend652cc62025-06-04 19:52:19 +0800936}