添加評論功能


評論框: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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM