• 添加评论功能


    评论框:src/views/articles/Content.vue

     1 <!-- 评论框 -->
     2 <div id="reply-box" class="reply-box form box-block">
     3   <div class="form-group comment-editor">
     4     <textarea v-if="auth" id="editor"></textarea>
     5     <textarea v-else disabled class="form-control" placeholder="需要登录后才能发表评论." style="height:172px"></textarea>
     6   </div>
     7   <div class="form-group reply-post-submit">
     8     <button id="reply-btn" :disabled="!auth" @click="comment" class="btn btn-primary">回复</button>
     9     <span class="help-inline">Ctrl+Enter</span>
    10   </div>
    11   <div v-show="commentHtml" id="preview-box" class="box preview markdown-body" v-html="commentHtml"></div>
    12 </div>

    未登录时,我们显示一个被禁用的评论框:

    HTML 格式的评论被保存在 commentHtml,我们使用 v-html 指令实时输出它:

    2、在 created 钩子后面添加 mounted 钩子,我们在这里创建一个 SimpleMDE 编辑器的实例:

    src/views/articles/Content.vue

     1 mounted() {
     2   // 已登录时,才开始创建
     3   if (this.auth) {
     4     // 自动高亮编辑器的内容
     5     window.hljs = hljs
     6 
     7     const simplemde = new SimpleMDE({
     8       element: document.querySelector('#editor'),
     9       placeholder: '请使用 Markdown 格式书写 ;-),代码片段黏贴时请注意使用高亮语法。',
    10       spellChecker: false,
    11       autoDownloadFontAwesome: false,
    12       // 不显示工具栏
    13       toolbar: false,
    14       // 不显示状态栏
    15       status: false,
    16       renderingConfig: {
    17         codeSyntaxHighlighting: true
    18       }
    19     })
    20 
    21     // 内容改变监听
    22     simplemde.codemirror.on('change', () => {
    23       // 更新 commentMarkdown 为编辑器的内容
    24       this.commentMarkdown = simplemde.value()
    25       // 更新 commentHtml,我们先替换原内容中的 emoji 标识,然后使用 markdown 方法将内容转成 HTML
    26       this.commentHtml = simplemde.markdown(emoji.emojify(this.commentMarkdown, name => name))
    27     })
    28 
    29     // 按键松开监听
    30     simplemde.codemirror.on('keyup', (codemirror, event) => {
    31       // 使用 Ctrl+Enter 时提交评论
    32       if (event.ctrlKey && event.keyCode === 13) {
    33         this.comment()
    34       }
    35     })
    36 
    37     // 将编辑器添加到当前实例
    38     this.simplemde = simplemde
    39   }
    40 },

    3.点击发表按钮时候的comment事件在 methods 选项中添加评论方法 comment

    src/views/articles/Content.vue

     1 comment() {
     2   // 编辑器的内容不为空时
     3   if (this.commentMarkdown && this.commentMarkdown.trim() !== '') {
     4     // 分发 comment 事件以提交评论
     5     this.$store.dispatch('comment', {
     6       comment: { content: this.commentMarkdown },
     7       articleId: this.articleId
     8     }).then((comments) => {
     9       // 在浏览器的控制台打印返回的评论列表
    10       console.log(comments)
    11     })
    12 
    13     // 清空编辑器
    14     this.simplemde.value('')
    15     // 使回复按钮获得焦点
    16     document.querySelector('#reply-btn').focus()
    17   }
    18 },

    打开 src/store/actions.js 文件,在代码的最后面,导出评论事件 comment

    src/store/actions.js

     1 .
     2 .
     3 .
     4 // 参数 articleId 是文章 ID;comment 是评论内容;commentId 是评论 ID
     5 export const comment = ({ commit, state }, { articleId, comment, commentId }) => {
     6   // 仓库的文章
     7   let articles = state.articles
     8   // 评论列表
     9   let comments = []
    10 
    11   if (!Array.isArray(articles)) articles = []
    12 
    13   for (let article of articles) {
    14     // 找到对应文章时
    15     if (parseInt(article.articleId) === parseInt(articleId)) {
    16       // 更新评论列表
    17       comments = Array.isArray(article.comments) ? article.comments : comments
    18 
    19       if (comment) {
    20         // 获取用户传入的评论内容,设置用户 ID 的默认值为 1
    21         const { uid = 1, content } = comment
    22         const date = new Date()
    23 
    24         if (commentId === undefined) {
    25           const lastComment = comments[comments.length - 1]
    26 
    27           // 新建 commentId
    28           if (lastComment) {
    29             commentId = parseInt(lastComment.commentId) + 1
    30           } else {
    31             commentId = comments.length + 1
    32           }
    33 
    34           // 在评论列表中加入当前评论
    35           comments.push({
    36             uid,
    37             commentId,
    38             content,
    39             date
    40           })
    41         }
    42       }
    43 
    44       // 更新文章的评论列表
    45       article.comments = comments
    46       break
    47     }
    48   }
    49 
    50   // 提交 UPDATE_ARTICLES 以更新所有文章
    51   commit('UPDATE_ARTICLES', articles)
    52   // 返回评论列表
    53   return comments
    54 }

    添加评论列表

    1、打开 src/views/articles/Content.vue 文件,在 data 中添加 comments

    src/views/articles/Content.vue

     1 data() {
     2   return {
     3     title: '', // 文章标题
     4     content: '', // 文章内容
     5     date: '', // 文章创建时间
     6     uid: 1, // 用户 ID
     7     likeUsers: [], // 点赞用户列表
     8     likeClass: '', // 点赞样式
     9     showQrcode: false, // 是否显示打赏弹窗
    10     commentHtml: '', // 评论 HTML
    11     comments: [], // 评论列表
    12   }
    13 },

    2、修改 created 钩子(注释部分是涉及的修改):

    src/views/articles/Content.vue 在页面渲染的时候将评论渲染出来

     1 created() {
     2   const articleId = this.$route.params.articleId
     3   const article = this.$store.getters.getArticleById(articleId)
     4 
     5   if (article) {
     6     // 获取文章的 comments
     7     let { uid, title, content, date, likeUsers, comments } = article
     8 
     9     this.uid = uid
    10     this.title = title
    11     this.content = SimpleMDE.prototype.markdown(emoji.emojify(content, name => name))
    12     this.date = date
    13     this.likeUsers = likeUsers || []
    14     this.likeClass = this.likeUsers.some(likeUser => likeUser.uid === 1) ? 'active' : ''
    15     // 渲染文章的 comments
    16     this.renderComments(comments)
    17 
    18     this.$nextTick(() => {
    19       this.$el.querySelectorAll('pre code').forEach((el) => {
    20         hljs.highlightBlock(el)
    21       })
    22     })
    23   }
    24 
    25   this.articleId = articleId
    26 },

    3、在 methods 选项中添加渲染评论方法 renderComments

    src/views/articles/Content.vue

     1 renderComments(comments) {
     2   if (Array.isArray(comments)) {
     3     // 深拷贝 comments 以不影响其原值
     4     const newComments = comments.map(comment => ({ ...comment }))
     5     const user = this.user || {}
     6 
     7     for (let comment of newComments) {
     8       comment.uname = user.name
     9       comment.uavatar = user.avatar
    10       // 将评论内容从 Markdown 转成 HTML
    11       comment.content = SimpleMDE.prototype.markdown(emoji.emojify(comment.content, name => name))
    12     }
    13 
    14     // 更新实例的 comments
    15     this.comments = newComments
    16     // 将 Markdown 格式的评论添加到当前实例
    17     this.commentsMarkdown = comments
    18   }
    19 },

    注:深拷贝 comments 是为了不影响已保存的文章,其方法等价于:

    const newComments = comments.map(function (comment) {
      return Object.assign({}, comment)
    })

    上面的方法只处理了对象的第一层数据,当对象能被 JSON 解析时,可以使用下面的方法进行完整的深拷贝:

    JSON.parse(JSON.stringify(comments))

    4、修改 comment 评论方法(注释部分是涉及的修改):

    src/views/articles/Content.vue

     1 comment() {
     2   if (this.commentMarkdown && this.commentMarkdown.trim() !== '') {
     3     this.$store.dispatch('comment', {
     4       comment: { content: this.commentMarkdown },
     5       articleId: this.articleId
     6     }).then(this.renderComments) // 在 .then 的回调里,调用 this.renderComments 渲染评论
     7 
     8     this.simplemde.value('')
     9     document.querySelector('#reply-btn').focus()
    10 
    11     // 将最后的评论滚动到页面的顶部
    12     this.$nextTick(() => {
    13       const lastComment = document.querySelector('#reply-list li:last-child')
    14       if (lastComment) lastComment.scrollIntoView(true)
    15     })
    16   }
    17 },

    5、查找 <Modal ,在其后面添加『评论列表』:

    src/views/articles/Content.vue

     1 <Modal :show.sync="showQrcode" class="text-center">
     2   .
     3   .
     4   .
     5 </Modal>
     6 
     7 <!-- 评论列表 -->
     8 <div class="replies panel panel-default list-panel replies-index">
     9   <div class="panel-heading">
    10     <div class="total">
    11       回复数量: <b>{{ comments.length }}</b>
    12     </div>
    13   </div>
    14   <div class="panel-body">
    15     <ul id="reply-list" class="list-group row">
    16       <li v-for="(comment, index) in comments" :key="comment.commentId" class="list-group-item media">
    17         <div class="avatar avatar-container pull-left">
    18           <router-link :to="`/${comment.uname}`">
    19             <img :src="comment.uavatar" class="media-object img-thumbnail avatar avatar-middle">
    20           </router-link>
    21         </div>
    22         <div class="infos">
    23           <div class="media-heading">
    24             <router-link :to="`/${comment.uname}`" class="remove-padding-left author rm-link-color">
    25               {{ comment.uname }}
    26             </router-link>
    27             <div class="meta">
    28               <a :id="`reply${index + 1}`" :href="`#reply${index + 1}`" class="anchor">#{{ index + 1 }}</a>
    29               <span> ⋅ </span>
    30               <abbr class="timeago">
    31                 {{ comment.date | moment('from', { startOf: 'second' }) }}
    32               </abbr>
    33             </div>
    34           </div>
    35 
    36           <div class="preview media-body markdown-reply markdown-body" v-html="comment.content"></div>
    37         </div>
    38       </li>
    39     </ul>
    40     <div v-show="!comments.length" class="empty-block">
    41       暂无评论~~
    42     </div>
    43   </div>
    44 </div>
  • 相关阅读:
    MySQL存储引擎与索引
    最长公共子序列
    最长递增子序列
    排序算法
    二分查找及其变种
    多线程中锁的种类。
    <LeetCode>136. 只出现一次的数字
    <LeetCode>121. 买卖股票的最佳时机
    Netty 粘包/拆包应用案例及解决方案分析
    微服务
  • 原文地址:https://www.cnblogs.com/yangguoe/p/9322064.html
Copyright © 2020-2023  润新知