• 利用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        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        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>
  • 相关阅读:
    芯片产品介绍
    稀疏矩阵理论与实践
    EUV极紫外光刻技术
    国内AI与芯片企业列表
    中国软件外包现状对比
    GPU指令集技术分析
    寒武纪智能系统参数
    TVM量化路线图roadmap
    EUV光刻机市场与技术
    轻松部署 .NET 5 应用程序,这个指南一定不要错过
  • 原文地址:https://www.cnblogs.com/xikui/p/16049807.html
Copyright © 2020-2023  润新知