利用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>

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM