Эх сурвалжийг харах

专利验收论坛静态页面

liyanbo 1 долоо хоног өмнө
parent
commit
3df9479686

+ 1 - 0
src/types/auto-components.d.ts

@@ -88,6 +88,7 @@ declare module 'vue' {
     ElMain: typeof import('element-plus/es')['ElMain']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
+    ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
     ElPopover: typeof import('element-plus/es')['ElPopover']
     ElProgress: typeof import('element-plus/es')['ElProgress']
     ElRadio: typeof import('element-plus/es')['ElRadio']

+ 230 - 1
src/views/Home/Index.vue

@@ -76,6 +76,139 @@
       </el-card>
     </el-col>
   </el-row>
+
+
+  <el-row class="mt-8px" :gutter="8" justify="space-between">
+    <el-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24" class="mb-8px">
+      <el-card shadow="never">
+        <template #header>
+          <div class="h-3 flex justify-between">
+            <span>【论坛】</span>
+          </div>
+        </template>
+        <el-skeleton :loading="loading" animated>
+          <el-row>
+            <el-col :span="24" class="mb-8px">
+              <!-- 发帖区域 -->
+              <div class="forum-post-area">
+                <el-input
+                  v-model="newPostContent"
+                  type="textarea"
+                  :rows="3"
+                  placeholder="分享你的想法..."
+                  maxlength="500"
+                  show-word-limit
+                />
+                <div class="flex justify-end mt-8px">
+                  <el-button type="primary" :disabled="!newPostContent.trim()" @click="publishPost">
+                    发布
+                  </el-button>
+                </div>
+              </div>
+
+              <el-divider />
+
+              <!-- 帖子列表 -->
+              <div v-if="forumPosts.length === 0" class="text-center text-gray-400 py-20px">
+                暂无帖子,快来发布第一条吧~
+              </div>
+              <div v-for="post in forumPosts" :key="post.id" class="forum-post-item">
+                <div class="flex items-start">
+                  <el-avatar :size="40" class="mr-12px flex-shrink-0">
+                    <img :src="post.avatar" alt="" />
+                  </el-avatar>
+                  <div class="flex-1 min-w-0">
+                    <div class="flex items-center justify-between">
+                      <div>
+                        <span class="text-14px font-medium">{{ post.author }}</span>
+                        <span class="text-12px text-gray-400 ml-8px">{{ post.time }}</span>
+                      </div>
+                      <el-popconfirm
+                        v-if="post.author === username"
+                        title="确定删除该帖子吗?"
+                        confirm-button-text="确定"
+                        cancel-button-text="取消"
+                        @confirm="deletePost(post)"
+                      >
+                        <template #reference>
+                          <span class="text-12px text-gray-400 cursor-pointer hover:text-red-500">删除</span>
+                        </template>
+                      </el-popconfirm>
+                    </div>
+                    <div class="text-14px mt-8px text-gray-700" style="white-space: pre-wrap">{{ post.content }}</div>
+                    <div class="flex items-center mt-10px text-12px text-gray-400">
+                      <span class="cursor-pointer flex items-center mr-16px" @click="toggleLike(post)">
+                        <Icon
+                          :icon="post.liked ? 'ant-design:like-filled' : 'ant-design:like-outlined'"
+                          :class="post.liked ? 'text-blue-500' : ''"
+                          class="mr-4px"
+                        />
+                        {{ post.likes > 0 ? post.likes : '点赞' }}
+                      </span>
+                      <span class="cursor-pointer flex items-center" @click="post.showComments = !post.showComments">
+                        <Icon icon="ant-design:message-outlined" class="mr-4px" />
+                        {{ post.comments.length > 0 ? post.comments.length : '评论' }}
+                      </span>
+                    </div>
+
+                    <!-- 评论区 -->
+                    <div v-if="post.showComments" class="mt-12px bg-gray-50 rounded p-12px">
+                      <div v-for="comment in post.comments" :key="comment.id" class="mb-8px">
+                        <div class="flex items-start">
+                          <el-avatar :size="28" class="mr-8px flex-shrink-0">
+                            <img :src="comment.avatar" alt="" />
+                          </el-avatar>
+                          <div class="flex-1">
+                            <div class="flex items-center justify-between">
+                              <div>
+                                <span class="font-medium">{{ comment.author }}</span>
+                                <span class="text-gray-400 ml-6px">{{ comment.time }}</span>
+                              </div>
+                              <el-popconfirm
+                                v-if="comment.author === username"
+                                title="确定删除该评论吗?"
+                                confirm-button-text="确定"
+                                cancel-button-text="取消"
+                                @confirm="deleteComment(post, comment)"
+                              >
+                                <template #reference>
+                                  <span class="text-12px text-gray-400 cursor-pointer hover:text-red-500">删除</span>
+                                </template>
+                              </el-popconfirm>
+                            </div>
+                            <div class="text-13px text-gray-600 mt-2px">{{ comment.content }}</div>
+                          </div>
+                        </div>
+                      </div>
+                      <!-- 评论输入框 -->
+                      <div class="flex items-center mt-8px">
+                        <el-input
+                          v-model="commentInputs[post.id]"
+                          size="small"
+                          placeholder="写下你的评论..."
+                          @keyup.enter="addComment(post)"
+                        />
+                        <el-button
+                          size="small"
+                          type="primary"
+                          :disabled="!commentInputs[post.id]?.trim()"
+                          class="ml-8px"
+                          @click="addComment(post)"
+                        >
+                          发送
+                        </el-button>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+                <el-divider />
+              </div>
+            </el-col>
+          </el-row>
+        </el-skeleton>
+      </el-card>
+    </el-col>
+  </el-row>
 </template>
 <script lang="ts" setup>
 import { set } from 'lodash-es'
@@ -84,7 +217,7 @@ import { formatTime } from '@/utils'
 
 import { useUserStore } from '@/store/modules/user'
 // import { useWatermark } from '@/hooks/web/useWatermark'
-import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
+import type { WorkplaceTotal, Project, Notice, Shortcut, ForumPost, ForumComment } from './types'
 import { pieOptions, barOptions } from './echarts-data'
 import { useRouter } from 'vue-router'
 
@@ -259,6 +392,102 @@ const getWeeklyUserActivity = async () => {
   ])
 }
 
+// ==================== 论坛功能 ====================
+const FORUM_CACHE_KEY = 'forum_posts'
+
+const newPostContent = ref('')
+const commentInputs = reactive<Record<string, string>>({})
+const forumPosts = reactive<ForumPost[]>([])
+
+const generateId = (): string => {
+  return Date.now().toString(36) + Math.random().toString(36).substring(2, 9)
+}
+
+const getCurrentTime = (): string => {
+  return formatTime(new Date(), 'yyyy-MM-dd HH:mm')
+}
+
+const loadForumPosts = () => {
+  try {
+    const cached = localStorage.getItem(FORUM_CACHE_KEY)
+    if (cached) {
+      const parsed = JSON.parse(cached) as ForumPost[]
+      forumPosts.splice(0, forumPosts.length, ...parsed)
+    }
+  } catch {
+    localStorage.removeItem(FORUM_CACHE_KEY)
+  }
+}
+
+const saveForumPosts = () => {
+  localStorage.setItem(FORUM_CACHE_KEY, JSON.stringify(forumPosts))
+}
+
+const publishPost = () => {
+  const content = newPostContent.value.trim()
+  if (!content) return
+
+  const newPost: ForumPost = {
+    id: generateId(),
+    author: username,
+    avatar: avatar,
+    content,
+    time: getCurrentTime(),
+    likes: 0,
+    liked: false,
+    comments: [],
+    showComments: false
+  }
+
+  forumPosts.unshift(newPost)
+  newPostContent.value = ''
+  saveForumPosts()
+}
+
+const toggleLike = (post: ForumPost) => {
+  post.liked = !post.liked
+  post.likes += post.liked ? 1 : -1
+  saveForumPosts()
+}
+
+const addComment = (post: ForumPost) => {
+  const content = commentInputs[post.id]?.trim()
+  if (!content) return
+
+  const newComment: ForumComment = {
+    id: generateId(),
+    postId: post.id,
+    author: username,
+    avatar: avatar,
+    content,
+    time: getCurrentTime()
+  }
+
+  post.comments.push(newComment)
+  commentInputs[post.id] = ''
+  saveForumPosts()
+}
+
+const deletePost = (post: ForumPost) => {
+  if (post.author !== username) return
+  const index = forumPosts.findIndex(p => p.id === post.id)
+  if (index > -1) {
+    forumPosts.splice(index, 1)
+    saveForumPosts()
+  }
+}
+
+const deleteComment = (post: ForumPost, comment: ForumComment) => {
+  if (comment.author !== username) return
+  const index = post.comments.findIndex(c => c.id === comment.id)
+  if (index > -1) {
+    post.comments.splice(index, 1)
+    saveForumPosts()
+  }
+}
+
+loadForumPosts()
+
 const getAllApi = async () => {
   await Promise.all([
     getCount(),

+ 21 - 0
src/views/Home/types.ts

@@ -55,3 +55,24 @@ export type MonthlySales = {
   estimate: number
   actual: number
 }
+
+export type ForumComment = {
+  id: string
+  postId: string
+  author: string
+  avatar: string
+  content: string
+  time: string
+}
+
+export type ForumPost = {
+  id: string
+  author: string
+  avatar: string
+  content: string
+  time: string
+  likes: number
+  liked: boolean
+  comments: ForumComment[]
+  showComments: boolean
+}