Explorar el Código

完成评论 发表 查看

枫叶秋林 hace 2 años
padre
commit
2de98d1bf0

+ 2 - 0
components.d.ts

@@ -8,6 +8,7 @@ export {}
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
     Cod: typeof import('./src/components/cod/index.vue')['default']
+    Commentitem: typeof import('./src/components/post/commentitem.vue')['default']
     ElAffix: typeof import('element-plus/es')['ElAffix']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElButton: typeof import('element-plus/es')['ElButton']
@@ -19,6 +20,7 @@ declare module '@vue/runtime-core' {
     ElContainer: typeof import('element-plus/es')['ElContainer']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElDrawer: typeof import('element-plus/es')['ElDrawer']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
     ElFooter: typeof import('element-plus/es')['ElFooter']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']

BIN
dump.rdb


+ 1 - 0
src/components/auth/login.vue

@@ -19,6 +19,7 @@ const rules = reactive({
     { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' },
   ],
 })
+
 const formRef = ref()
 const bt_login = async (formEl: FormInstance | undefined) => {
   formEl?.validate(async (valid: boolean, e: any) => {

+ 4 - 0
src/components/auth/register.vue

@@ -76,10 +76,14 @@ const bt_resgister = async (formEl: FormInstance | undefined) => {
             token().token = res.data.token
             window.location.href = '/home'
             ElMessage.success('注册成功')
+          } else {
+            formRef.value?.resetFields()
+            ElMessage.error(res.data.msg)
           }
         })
         .catch((err) => {
           ElMessage.error(err.message)
+          formRef.value?.resetFields()
         })
     }
   })

+ 141 - 0
src/components/post/commentitem.vue

@@ -0,0 +1,141 @@
+<script setup lang="ts">
+import { defineProps, ref } from 'vue'
+import service from '../../plugins/axios'
+import { API_URL } from '../../config'
+import { token } from '../../plugins/pinia'
+const data = defineProps({
+  id: {
+    type: String,
+    required: true,
+  },
+  content: {
+    type: String,
+    required: true,
+  },
+  authorid: {
+    type: String,
+    required: true,
+  },
+  time: {
+    type: String,
+    required: true,
+  },
+})
+
+const edit = () => {
+  window.location.href = `/posting?postid=${data.id}`
+}
+const del = () => {
+  window.location.href = `/posting?postid=${data.id}`
+}
+async function IsMycomment() {
+  if (token().token === '') {
+    return false
+  }
+  const res = await (
+    await service({
+      url: `/auth/islogin`,
+      method: 'get',
+      headers: {
+        Authorization: `Bearer ${token().token}`,
+      },
+    })
+  ).data
+  if (data.authorid === res) {
+    return true
+  }
+  return false
+}
+const isMyComment = ref(await IsMycomment())
+//格式化时间几天前
+function formatDate(date: any) {
+  const d = new Date(date)
+  const now = Date.now()
+  const diff = (now - d.getTime()) / 1000
+  if (diff < 30) {
+    return '刚刚'
+  } else if (diff < 3600) {
+    // less 1 hour
+    return Math.ceil(diff / 60) + '分钟前'
+  } else if (diff < 3600 * 24) {
+    return Math.ceil(diff / 3600) + '小时前'
+  } else if (diff < 3600 * 24 * 2) {
+    return '1天前'
+  }
+  return Math.ceil(diff / (3600 * 24)) + '天前'
+}
+const userdata = await (
+  await service.get(`userinfo/getuser?id=${data.authorid}`)
+).data.data
+</script>
+
+<template>
+  <el-card style="margin: 10px 0;">
+
+    <template #header>
+      <div class="card-header">
+
+        <div v-show="isMyComment">
+          <el-button type="text"
+                     @click="edit">
+            编辑
+          </el-button>
+          <el-button type="text"
+                     @click="del">
+            删除
+          </el-button>
+        </div>
+        <el-tag type="success">
+          发表时间:{{ formatDate(data.time)}}
+        </el-tag>
+      </div>
+    </template>
+    <el-row>
+      <el-col :xs="24"
+              :sm="24"
+              :md="6"
+              :lg="6"
+              :xl="6">
+
+        <div class="c">
+
+          <el-avatar style="margin-right: 10px;"
+                     :size="100"
+                     :src="API_URL+userdata.user.avatar">
+          </el-avatar>
+        </div>
+        <div class="c">
+          <el-button type="text"
+                     style="margin-right: 10px;">
+            {{ userdata.user.nickname?userdata.user.nickname:userdata.username }}
+          </el-button>
+        </div>
+
+      </el-col>
+      <el-col :xs="24"
+              :sm="24"
+              :md="18"
+              :lg="18"
+              :xl="18">
+        <v-md-editor :model-value="data.content"
+                     mode="preview"
+                     class="md">
+        </v-md-editor>
+      </el-col>
+    </el-row>
+  </el-card>
+</template>
+
+<style scoped>
+.card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+/* 居中 */
+.c {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+</style>

+ 50 - 81
src/layouts/post.vue

@@ -6,84 +6,40 @@ import service from '../plugins/axios'
 import { useRoute, useRouter } from 'vue-router'
 import { ElMessage } from 'element-plus'
 import authorInformation from '../views/post/authorInformation.vue'
+import postarticle from '../views/post/postarticle.vue'
+import postcomment from '../views/post/comment.vue'
 import { useDark } from '@vueuse/core'
 import { watch } from 'vue'
 import { token } from '../plugins/pinia'
-const id = useRoute().params.id
+import { ChatSquare } from '@element-plus/icons-vue'
+const id = useRoute().params.id as string
 const data = await (await service.get(`/post?id=${id}`)).data
 const views = await (await service.get(`post/addview?id=${id}`)).data
-onMounted(() => {
-  const md = document.querySelector('.vuepress-markdown-body') as HTMLElement
-  // 每秒检测一次
-  const html = document.querySelector('html') as HTMLElement
-  //每秒检测一次
-  const timer = setInterval(() => {
-    if (md) {
-      if (html.className === 'dark') {
-        md.style.background = '#1d1e1f'
-        md.style.color = '#fff'
-      } else {
-        md.style.background = '#fff'
-        md.style.color = '#000'
-      }
-    }
-  }, 100)
-})
-async function IsMypost() {
-  if (token().token === '') {
-    return false
-  }
-  const res = await (
-    await service({
-      url: `/auth/islogin`,
-      method: 'get',
-      headers: {
-        Authorization: `Bearer ${token().token}`,
-      },
-    })
-  ).data
-  if (data.author.auth_id === res) {
-    return true
-  }
-  return false
-}
-const isMypost = ref(await IsMypost())
-
 if (data.cod === 400) {
   window.location.href = '/home'
 }
-// 编辑
-const edit = () => {
-  window.location.href = `/posting?plate=${data.plateId}&postid=${data.id}`
-}
-const del = async () => {
-  await service({
-    url: `/post?postid=${id}`,
-    method: 'delete',
-    headers: {
-      Authorization: `Bearer ${token().token}`,
-    },
-  })
-  ElMessage({
-    message: '删除成功',
-    type: 'success',
-  })
-  window.location.href = `/home`
+const router = useRouter()
+const bt_comment = () => {
+  if (token().token) {
+    window.location.href = `/reply?postid=${id}`
+    return
+  }
+  window.location.href = '/auth?type=login'
 }
 </script>
 
 <template>
-
   <el-row gutter="10">
     <el-col :xs="24"
             :sm="24"
             :md="6"
             :lg="6"
             :xl="6">
+
       <Suspense>
         <template #default>
           <authorInformation :id="data.author.auth_id"
-                             :numberOfReplies="data.comment.length"
+                             :numberOfReplies="data.comment"
                              :views="views.data" />
         </template>
         <template #fallback>
@@ -96,34 +52,42 @@ const del = async () => {
             :md="18"
             :lg="18"
             :xl="18">
-      <el-card style="margin: 10px 0px;"
-               class="post-card">
-        <template #header>
-
-          <div class="card-header">
-            <span>{{ data.title }}</span>
-            <!-- 按钮组 -->
-            <div v-show="isMypost">
-              <el-button type="text"
-                         @click="edit">编辑</el-button>
-              <el-divider direction="vertical" />
-              <el-popconfirm title="确定要删除这篇帖子吗?"
-                             @confirm="del">
-                <template #reference>
-                  <el-button type="text">删除</el-button>
-                </template>
-              </el-popconfirm>
-            </div>
-          </div>
+      <Suspense>
+        <template #default>
+          <postarticle :postid="id"
+                       :auth_id="data.author.auth_id"
+                       :title="data.title"
+                       :plateId="data.plateId"
+                       :content="data.content" />
         </template>
-        <v-md-editor :model-value="data.content"
-                     mode="preview"
-                     class="md"></v-md-editor>
-      </el-card>
-
+        <template #fallback>
+          <el-skeleton />
+        </template>
+      </Suspense>
     </el-col>
   </el-row>
+  <el-divider />
+  <el-affix position="bottom"
+            :offset="20">
+    <div class="fl">
+      <el-button type="primary"
+                 @click="bt_comment"
+                 size="large"
+                 :icon="ChatSquare"
+                 circle></el-button>
+    </div>
 
+  </el-affix>
+
+  <Suspense>
+    <template #default>
+      <postcomment :postid="id"
+                   class="commentid" />
+    </template>
+    <template #fallback>
+      <el-skeleton />
+    </template>
+  </Suspense>
 </template>
 
 <style scoped>
@@ -136,4 +100,9 @@ const del = async () => {
   justify-content: space-between;
   align-items: center;
 }
+.el-affix {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+}
 </style>

+ 121 - 0
src/layouts/reply.vue

@@ -0,0 +1,121 @@
+<script setup lang="ts">
+import { ref, reactive } from 'vue'
+import { useRoute } from 'vue-router'
+import service from '../plugins/axios'
+import { token } from '../plugins/pinia'
+import { ElMessage, FormInstance } from 'element-plus'
+
+const { postid } = useRoute().query
+console.log(postid)
+
+const submit = async (formEl: FormInstance | undefined) => {
+  formEl?.validate(async (valid, e) => {
+    if (valid) {
+      const res = await service({
+        method: 'post',
+        url: '/comment',
+        headers: {
+          Authorization: `Bearer ${token().token}`,
+          cod: ressuccess,
+        },
+        data: {
+          postid,
+          content: formDate.content,
+        },
+      })
+      if (res.data.cod === 200) {
+        window.location.href = `/post/${postid}`
+      } else {
+        formEl?.resetFields()
+        ElMessage.error(res.data.msg)
+      }
+    } else {
+      ElMessage.error('请检查表单')
+    }
+  })
+}
+const formDate = reactive({
+  cod: '',
+  content: '',
+})
+const formRef = ref<FormInstance>()
+const rule = reactive({
+  cod: [{ required: true, message: '请点击按钮进行验证', trigger: 'change' }],
+  content: [{ required: true, message: '请输入回复内容', trigger: 'change' }],
+})
+console.log(postid)
+
+const { title } = await (await service.get(`/post?id=${postid}`)).data
+let ressuccess = ''
+const codvisible = ref(false)
+const bt_disabled = ref(false)
+function codsuccess(success: string) {
+  codvisible.value = false
+  bt_disabled.value = true
+  ressuccess = success
+  formDate.cod = '验证成功'
+}
+</script>
+
+<template>
+  <el-card>
+    <el-form :rules="rule"
+             ref="formRef"
+             :model="formDate"
+             label-position="top"
+             hide-required-asterisk="false">
+
+      <el-form-item label="帖子标题">
+        <el-input disabled
+                  v-model="title" />
+      </el-form-item>
+      <el-form-item label="回复内容"
+                    prop="content">
+        <v-md-editor v-model="formDate.content"
+                     height="800px" />
+      </el-form-item>
+      <el-form-item label="验证码"
+                    prop="cod">
+        <el-input type="password"
+                  v-model="formDate.cod"
+                  style="display:none" />
+        <el-popover placement="top"
+                    :width="330"
+                    :visible="codvisible">
+          <template #reference>
+            <el-button :type="bt_disabled?'success':'info'"
+                       style="width: 100%;"
+                       :disabled="bt_disabled"
+                       @click="(codvisible = !codvisible)">{{bt_disabled?"✅验证通过":"点击进行验证"}}</el-button>
+          </template>
+          <Suspense>
+            <template #default>
+              <el-space>
+                <div class="center">
+                  <cod @success="codsuccess" />
+                </div>
+              </el-space>
+            </template>
+            <template #fallback>
+              验证码获取中
+            </template>
+          </Suspense>
+        </el-popover>
+      </el-form-item>
+      <el-form-item>
+
+        <el-button type="primary"
+                   style="width: 100%;"
+                   class="fr"
+                   @click="submit(formRef)">提交</el-button>
+
+      </el-form-item>
+    </el-form>
+  </el-card>
+</template>
+
+<style scoped >
+.fr {
+  float: right;
+}
+</style>

+ 5 - 0
src/plugins/router.ts

@@ -19,6 +19,11 @@ const router = createRouter({
           component: () => import("../layouts/posting.vue"),
           children: [],
         },
+        {
+          path: "/reply",
+          component: () => import("../layouts/reply.vue"),
+          children: [],
+        },
         {
           path: "/auth",
           component: () => import("../layouts/auth.vue"),

+ 14 - 8
src/views/post/authorInformation.vue

@@ -12,33 +12,36 @@ const dp = defineProps({
   numberOfReplies: {
     type: Number,
     required: true,
+    default: -1,
   },
   views: {
     type: Number,
     required: true,
+    default: -1,
   },
 })
 const to_gethub = () => {
   window.location.href = `https://github.com/${data.data.user.github}`
 }
 const data = await (await service.get(`userinfo/getuser?id=${dp.id}`)).data
-// /userinfo/count?id=3
 const count = await (await service.get(`/userinfo/count?id=${dp.id}`)).data
 </script>
 
 <template>
   <el-card style="margin: 10px 0px;"
-           class="post-card">
+           class="post-card-info">
     <template #header>
       <el-button type="text"
                  :href="data.data.user.github">
         {{ data.data.user.nickname?data.data.user.nickname:data.data.username }}
-
       </el-button>
-      <el-divider direction="vertical" />
-      回复:{{ numberOfReplies }}
-      <el-divider direction="vertical" />
-      浏览:{{ views }}
+      <span v-show="numberOfReplies!==-1">
+        <el-divider direction="vertical" />
+        回复:{{ numberOfReplies }}
+        <el-divider direction="vertical" />
+        浏览:{{ views }}
+      </span>
+
     </template>
 
     <div class="center">
@@ -51,7 +54,6 @@ const count = await (await service.get(`/userinfo/count?id=${dp.id}`)).data
     <el-button type="primary"
                style="width: 100%;margin:  0px;"
                @click="to_gethub">GitHub</el-button>
-    <!-- 用户组 -->
     <div class="center">
       <el-tag type="success">Lv.{{data.data.user.level}}</el-tag>
     </div>
@@ -110,4 +112,8 @@ const count = await (await service.get(`/userinfo/count?id=${dp.id}`)).data
   font-size: 14px;
   color: #606266;
 }
+post-card-info {
+  /* ml高度 */
+  height: 100%;
+}
 </style>

+ 48 - 0
src/views/post/comment.vue

@@ -0,0 +1,48 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import service from '../../plugins/axios'
+import commentitem from '../../components/post/commentitem.vue'
+const { postid } = defineProps({
+  postid: {
+    type: String,
+    required: true,
+  },
+})
+const page = async (v: number) => {
+  data.value = (
+    await service.get(`/comment?postId=${postid}&page=${v}&limit=10`)
+  ).data
+}
+const data = ref(
+  (await service.get(`/comment?postId=${postid}&page=${1}&limit=10`)).data
+)
+</script>
+
+<template>
+
+  <el-empty description="还没有人评论呢?"
+            v-show="!(data.total > 0)" />
+  <Suspense v-for="(item,i) of data.data"
+            :key="i">
+    <template #default>
+
+      <commentitem :id="item.id"
+                   :content="item.content"
+                   :authorid="item.authorId"
+                   :time="item.updatedAt" />
+
+    </template>
+    <template #fallback>
+      <el-skeleton />
+    </template>
+  </Suspense>
+  <!-- 分割线 -->
+  <el-divider />
+  <el-pagination layout="prev, pager, next"
+                 :page-count="data.totalPage"
+                 @current-change="page" />
+
+</template>
+
+<style scoped >
+</style>

+ 116 - 0
src/views/post/postarticle.vue

@@ -0,0 +1,116 @@
+<script setup lang="ts">
+import service from '../../plugins/axios'
+import { onMounted, ref } from 'vue'
+import { useRoute } from 'vue-router'
+import { token } from '../../plugins/pinia'
+import { ElMessage } from 'element-plus'
+const data = defineProps({
+  postid: {
+    type: String,
+    required: true,
+  },
+  plateId: {
+    type: String,
+    required: true,
+  },
+  auth_id: {
+    type: String,
+    required: true,
+  },
+  title: {
+    type: String,
+    required: true,
+  },
+  content: {
+    type: String,
+    required: true,
+  },
+})
+const edit = () => {
+  window.location.href = `/posting?plate=${data.plateId}&postid=${data.postid}`
+}
+const del = async () => {
+  await service({
+    url: `/post?postid=${data.postid}`,
+    method: 'delete',
+    headers: {
+      Authorization: `Bearer ${token().token}`,
+    },
+  })
+  ElMessage({
+    message: '删除成功',
+    type: 'success',
+  })
+  window.location.href = `/home`
+}
+async function IsMypost() {
+  if (token().token === '') {
+    return false
+  }
+  const res = await (
+    await service({
+      url: `/auth/islogin`,
+      method: 'get',
+      headers: {
+        Authorization: `Bearer ${token().token}`,
+      },
+    })
+  ).data
+  if (data.auth_id === res) {
+    return true
+  }
+  return false
+}
+onMounted(() => {
+  const md = document.querySelector('.vuepress-markdown-body') as HTMLElement
+  // 每秒检测一次
+  const html = document.querySelector('html') as HTMLElement
+  //每秒检测一次
+  const timer = setInterval(() => {
+    if (md) {
+      if (html.className === 'dark') {
+        md.style.background = '#1d1e1f'
+        md.style.color = '#fff'
+      } else {
+        md.style.background = '#fff'
+        md.style.color = '#000'
+      }
+    }
+  }, 100)
+})
+const isMypost = ref(await IsMypost())
+</script>
+
+<template>
+
+  <el-card style="margin: 10px 0px;"
+           class="post-card">
+    <template #header>
+      <div class="card-header">
+        <span>{{ data.title }}</span>
+        <div v-show="isMypost">
+          <el-button type="text"
+                     @click="edit">编辑</el-button>
+          <el-divider direction="vertical" />
+          <el-popconfirm title="确定要删除这篇帖子吗?"
+                         @confirm="del">
+            <template #reference>
+              <el-button type="text">删除</el-button>
+            </template>
+          </el-popconfirm>
+        </div>
+      </div>
+    </template>
+    <v-md-editor :model-value="data.content"
+                 mode="preview"
+                 class="md">
+    </v-md-editor>
+  </el-card>
+</template>
+
+<style scoped>
+.card-header {
+  display: flex;
+  justify-content: space-between;
+}
+</style>