1. 程式人生 > 其它 >利用Vue+ElementUi實現評論功能

利用Vue+ElementUi實現評論功能

前言

這兩天在用vue重構之前寫的一個社群部落格專案,之前評論的樣式和效果都差強人意
在寫完這個功能後,由心覺得Vue真的非常好用。

話不多說,先上效果圖

程式碼

html程式碼:

 1 <template>
 2     <div>
 3         <div v-clickoutside="hideReplyBtn" @click="inputFocus" class="my-reply">
 4             <el-avatar class="header-img" :size="40" :src="myHeader"></
el-avatar> 5 <div class="reply-info" > 6 <div 7 tabindex="0" 8 contenteditable="true" 9 id="replyInput" 10 spellcheck="false" 11 placeholder="輸入評論..." 12 class
="reply-input" 13 @focus="showReplyBtn" 14 @input="onDivInput($event)" 15 > 16 </div> 17 </div> 18 <div class="reply-btn-box" v-show="btnShow"> 19 <el-button class="reply-btn"
size="medium" @click="sendComment" type="primary">發表評論</el-button> 20 </div> 21 </div> 22 <div v-for="(item,i) in comments" :key="i" class="author-title reply-father"> 23 <el-avatar class="header-img" :size="40" :src="item.headImg"></el-avatar> 24 <div class="author-info"> 25 <span class="author-name">{{item.name}}</span> 26 <span class="author-time">{{item.time}}</span> 27 </div> 28 <div class="icon-btn"> 29 <span @click="showReplyInput(i,item.name,item.id)"><i class="iconfont el-icon-s-comment"></i>{{item.commentNum}}</span> 30 <i class="iconfont el-icon-caret-top"></i>{{item.like}} 31 </div> 32 <div class="talk-box"> 33 <p> 34 <span class="reply">{{item.comment}}</span> 35 </p> 36 </div> 37 <div class="reply-box"> 38 <div v-for="(reply,j) in item.reply" :key="j" class="author-title"> 39 <el-avatar class="header-img" :size="40" :src="reply.fromHeadImg"></el-avatar> 40 <div class="author-info"> 41 <span class="author-name">{{reply.from}}</span> 42 <span class="author-time">{{reply.time}}</span> 43 </div> 44 <div class="icon-btn"> 45 <span @click="showReplyInput(i,reply.from,reply.id)"><i class="iconfont el-icon-s-comment"></i>{{reply.commentNum}}</span> 46 <i class="iconfont el-icon-caret-top"></i>{{reply.like}} 47 </div> 48 <div class="talk-box"> 49 <p> 50 <span>回覆 {{reply.to}}:</span> 51 <span class="reply">{{reply.comment}}</span> 52 </p> 53 </div> 54 <div class="reply-box"> 55 56 </div> 57 </div> 58 </div> 59 <div v-show="_inputShow(i)" class="my-reply my-comment-reply"> 60 <el-avatar class="header-img" :size="40" :src="myHeader"></el-avatar> 61 <div class="reply-info" > 62 <div tabindex="0" contenteditable="true" spellcheck="false" placeholder="輸入評論..." @input="onDivInput($event)" class="reply-input reply-comment-input"></div> 63 </div> 64 <div class=" reply-btn-box"> 65 <el-button class="reply-btn" size="medium" @click="sendCommentReply(i,j)" type="primary">發表評論</el-button> 66 </div> 67 </div> 68 </div> 69 </div> 70 </template>

Js 程式碼如下
我把模擬的資料寫在了data裡面,顯得js有點長。如果要更改資料的格式的話,記得也要改Html不然會出錯。

 

  1 <script>
  2 const clickoutside = {
  3     // 初始化指令
  4     bind(el, binding, vnode) {
  5     function documentHandler(e) {
  6     // 這裡判斷點選的元素是否是本身,是本身,則返回
  7         if (el.contains(e.target)) {
  8             return false;
  9         }
 10     // 判斷指令中是否綁定了函式
 11         if (binding.expression) {
 12             // 如果綁定了函式 則呼叫那個函式,此處binding.value就是handleClose方法
 13             binding.value(e);
 14         }
 15     }
 16     // 給當前元素繫結個私有變數,方便在unbind中可以解除事件監聽
 17     el.vueClickOutside = documentHandler;
 18     document.addEventListener('click', documentHandler);
 19     },
 20     update() {},
 21     unbind(el, binding) {
 22     // 解除事件監聽
 23     document.removeEventListener('click', el.vueClickOutside);
 24     delete el.vueClickOutside;
 25   },
 26 };
 27 export default {
 28     name:'ArticleComment',
 29     data(){
 30         return{
 31             btnShow: false,
 32             index:'0',
 33             replyComment:'',
 34             myName:'Lana Del Rey',
 35             myHeader:'https://ae01.alicdn.com/kf/Hd60a3f7c06fd47ae85624badd32ce54dv.jpg',
 36             myId:19870621,
 37             to:'',
 38             toId:-1,
 39             comments:[
 40                 {
 41                     name:'Lana Del Rey',
 42                     id:19870621,
 43                     headImg:'https://ae01.alicdn.com/kf/Hd60a3f7c06fd47ae85624badd32ce54dv.jpg',
 44                     comment:'我釋出一張新專輯Norman Fucking Rockwell,大家快來聽啊',
 45                     time:'2019年9月16日 18:43',
 46                     commentNum:2,
 47                     like:15,
 48                     inputShow:false,
 49                     reply:[
 50                         {
 51                             from:'Taylor Swift',
 52                             fromId:19891221,
 53                             fromHeadImg:'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
 54                             to:'Lana Del Rey',
 55                             toId:19870621,
 56                             comment:'我很喜歡你的新專輯!!',
 57                             time:'2019年9月16日 18:43',
 58                             commentNum:1,
 59                             like:15,
 60                             inputShow:false
 61                         },
 62                         {
 63                             from:'Ariana Grande',
 64                             fromId:1123,
 65                             fromHeadImg:'https://ae01.alicdn.com/kf/Hf6c0b4a7428b4edf866a9fbab75568e6U.jpg',
 66                             to:'Lana Del Rey',
 67                             toId:19870621,
 68                             comment:'別忘記宣傳我們的合作單曲啊',
 69                             time:'2019年9月16日 18:43',
 70                             commentNum:0,
 71                             like:5,
 72                             inputShow:false
 73 
 74                         }
 75                     ]
 76                 },
 77                 {
 78                     name:'Taylor Swift',
 79                     id:19891221,
 80                     headImg:'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
 81                     comment:'我發行了我的新專輯Lover',
 82                     time:'2019年9月16日 18:43',
 83                     commentNum:1,
 84                     like:5,
 85                     inputShow:false,
 86                     reply:[
 87                         {
 88                             from:'Lana Del Rey',
 89                             fromId:19870621,
 90                             fromHeadImg:'https://ae01.alicdn.com/kf/Hd60a3f7c06fd47ae85624badd32ce54dv.jpg',
 91                             to:'Taylor Swift',
 92                             toId:19891221,
 93                             comment:'新專輯和speak now 一樣棒!',
 94                             time:'2019年9月16日 18:43',
 95                             commentNum:25,
 96                             like:5,
 97                             inputShow:false
 98 
 99                         }
100                     ]
101                 },
102                 {
103                     name:'Norman Fucking Rockwell',
104                     id:20190830,
105                     headImg:'https://ae01.alicdn.com/kf/Hdd856ae4c81545d2b51fa0c209f7aa28Z.jpg',
106                     comment:'Plz buy Norman Fucking Rockwell on everywhere',
107                     time:'2019年9月16日 18:43',
108                     commentNum:0,
109                     like:5,
110                     inputShow:false,
111                     reply:[]
112                 },
113             ]
114         }
115     },
116     directives: {clickoutside},
117     methods: {
118         inputFocus(){
119             var replyInput = document.getElementById('replyInput');
120             replyInput.style.padding= "8px 8px"
121             replyInput.style.border ="2px solid blue"
122             replyInput.focus()
123         },  
124         showReplyBtn(){
125             this.btnShow = true
126         },
127         hideReplyBtn(){
128             this.btnShow = false
129             replyInput.style.padding= "10px"
130             replyInput.style.border ="none"
131         },
132         showReplyInput(i,name,id){
133             this.comments[this.index].inputShow = false
134             this.index =i
135             this.comments[i].inputShow = true
136             this.to = name
137             this.toId = id
138         },
139         _inputShow(i){
140             return this.comments[i].inputShow 
141         },
142         sendComment(){
143             if(!this.replyComment){
144                  this.$message({
145                     showClose: true,
146                     type:'warning',
147                     message:'評論不能為空'
148                 })
149             }else{
150                 let a ={}
151                 let input =  document.getElementById('replyInput')
152                 let timeNow = new Date().getTime();
153                 let time= this.dateStr(timeNow);
154                 a.name= this.myName
155                 a.comment =this.replyComment
156                 a.headImg = this.myHeader
157                 a.time = time
158                 a.commentNum = 0
159                 a.like = 0
160                 this.comments.push(a)
161                 this.replyComment = ''
162                 input.innerHTML = '';
163 
164             }
165         },
166         sendCommentReply(i,j){
167             if(!this.replyComment){
168                  this.$message({
169                     showClose: true,
170                     type:'warning',
171                     message:'評論不能為空'
172                 })
173             }else{
174                 let a ={}
175                 let timeNow = new Date().getTime();
176                 let time= this.dateStr(timeNow);
177                 a.from= this.myName
178                 a.to = this.to
179                 a.fromHeadImg = this.myHeader
180                 a.comment =this.replyComment
181                 a.time = time
182                 a.commentNum = 0
183                 a.like = 0
184                 this.comments[i].reply.push(a)
185                 this.replyComment = ''
186                 document.getElementsByClassName("reply-comment-input")[i].innerHTML = ""
187             }
188         },
189         onDivInput: function(e) {
190             this.replyComment = e.target.innerHTML;
191         },
192         dateStr(date){
193             //獲取js 時間戳
194             var time=new Date().getTime();
195             //去掉 js 時間戳後三位,與php 時間戳保持一致
196             time=parseInt((time-date)/1000);
197             //儲存轉換值 
198             var s;
199             if(time<60*10){//十分鐘內
200                 return '剛剛';
201             }else if((time<60*60)&&(time>=60*10)){
202                 //超過十分鐘少於1小時
203                 s = Math.floor(time/60);
204                 return  s+"分鐘前";
205             }else if((time<60*60*24)&&(time>=60*60)){ 
206                 //超過1小時少於24小時
207                 s = Math.floor(time/60/60);
208                 return  s+"小時前";
209             }else if((time<60*60*24*30)&&(time>=60*60*24)){ 
210                 //超過1天少於30天內
211                 s = Math.floor(time/60/60/24);
212                 return s+"天前";
213             }else{ 
214                 //超過30天ddd
215                 var date= new Date(parseInt(date));
216                 return date.getFullYear()+"/"+(date.getMonth()+1)+"/"+date.getDate();
217             }
218         }
219     },    
220 }
221 </script>

css 程式碼

 

  1 <style lang="scss" scoped>
  2 .my-reply {
  3   padding: 10px;
  4   background-color: #fafbfc;
  5   .header-img {
  6     display: inline-block;
  7     vertical-align: top;
  8   }
  9 
 10   .reply-info {
 11     display: inline-block;
 12     margin-left: 5px;
 13     width: 90%;
 14     @media screen and (max-width: 1200px) {
 15       width: 80%;
 16     }
 17     .reply-input {
 18       min-height: 20px;
 19       line-height: 22px;
 20       padding: 10px 10px;
 21       color: #ccc;
 22       background-color: #fff;
 23       border-radius: 5px;
 24       &:empty:before {
 25         content: attr(placeholder);
 26       }
 27       &:focus:before {
 28         content: none;
 29       }
 30       &:focus {
 31         padding: 8px 8px;
 32         border: 2px solid blue;
 33         box-shadow: none;
 34         outline: none;
 35       }
 36     }
 37   }
 38   .reply-btn-box {
 39     height: 25px;
 40     margin: 10px 0;
 41     .reply-btn {
 42       position: relative;
 43       float: right;
 44       margin-right: 15px;
 45     }
 46   }
 47 }
 48 .my-comment-reply {
 49   margin-left: 50px;
 50   .reply-input {
 51     width: flex;
 52   }
 53 }
 54 
 55 .author-title:not(:last-child) {
 56   border-bottom: 1px solid rgba(178, 186, 194, 0.3);
 57 }
 58 
 59 .author-title {
 60   padding: 10px;
 61   .header-img {
 62     display: inline-block;
 63     vertical-align: top;
 64   }
 65   .author-info {
 66     display: inline-block;
 67     margin-left: 5px;
 68     width: 60%;
 69     height: 40px;
 70     line-height: 20px;
 71     > span {
 72       display: block;
 73       cursor: pointer;
 74       overflow: hidden;
 75       white-space: nowrap;
 76       text-overflow: ellipsis;
 77     }
 78     .author-name {
 79       color: #000;
 80       font-size: 18px;
 81       font-weight: bold;
 82     }
 83 
 84     .author-time {
 85       font-size: 14px;
 86     }
 87   }
 88   .icon-btn {
 89     width: 30%;
 90     padding: 0 !important ;
 91     float: right;
 92     @media screen and (max-width: 1200px) {
 93       width: 20%;
 94       padding: 7px;
 95     }
 96     > span {
 97       cursor: pointer;
 98     }
 99     .iconfont {
100       margin: 0 5px;
101     }
102   }
103   .talk-box {
104     margin: 0 50px;
105     > p {
106       margin: 0;
107     }
108     .reply {
109       font-size: 16px;
110       color: #000;
111     }
112   }
113   .reply-box {
114     margin: 10px 0 0 50px;
115     background-color: #efefef;
116   }
117 }
118 </style>