评论功能的实现

530 阅读2分钟

1.需求解析

之前在做一个项目时,需要用到评论这个功能模块,第一时间是去网上找现成的,但是看完一圈后,发现都不是很满意,于是索性就自己实现一个

2.准备工作

我是采用组件化思想,将他封装成一个vue组件的,其中也用到了element-ui这个组件、moment.js工具(获取当前的时间)以及nanoid(用于生成唯一的id标识,主要是标识新添加的评论),如果不熟悉的话,这里说明一下:

element-ui

先需要在控制台输入以下命令安装一下element-ui

npm i element-ui -S

再在main.js中引入一下样式以及组件就可以使用了

import Vue from 'vue'; 
import ElementUI from 'element-ui'; 
import 'element-ui/lib/theme-chalk/index.css'; 
import App from './App.vue';
Vue.use(ElementUI);
new Vue(
{ 
  el: '#app', 
  render: h => h(App) 
});

moment

直接在控制台输入下面命令

npm install moment --save

nanoid 同样直接在控制台输入下面命令安装

npm i nanoid

3.说明介绍

我这里将整个组件分为了三个模块

1)一个是输入评论功能,我这里命名为 commentHeader.vue

image.png

2)在一个就是评论内容展示,并命名为 commentItem.vue

image.png

3)最后就是将前面两个组件合理使用,组成整个评论功能的组件,命名为 discuss.vue

4.具体实现

commentHeader.vue

<template>
    <div @click.stop>
        <div class="header">
            <div class="peopleicon">
                <el-avatar icon="el-icon-user-solid" :src="selfinfo.headPictureUrl"></el-avatar>
            </div>
            <div class="input">
                <el-input type="textarea" autosize placeholder="请输入评论..." v-model="inputContent">
                </el-input>
            </div>
        </div>
        <div class="btn">
            <el-button @click.native="submit" type="primary">发表评论</el-button>
        </div>
    </div>

</template>

<script>
import { getbaseinfo } from '@/network/getbaseinfo'
import moment from 'moment'
// import 'moment/locale/zh-cn';
export default {
    name: 'commentHeader',
    props: {
        index: {
            type: String,
            default: '-1'
        }

    },
    data() {
        return {
            inputContent: '',
            selfinfo:{}
        }
    },
    created() {
        getbaseinfo('/userinfo').then(res => {
            this.selfinfo = res.data.userinfo;
        })
    },
    methods: {
        submit() {
            if (this.inputContent == '') {
                this.$message({ message: '评论不能为空' });
            } else {
                this.$emit('subCommet', {
                    name: '不过',
                    prio:1,
                    to:'不过',
                    toId:this.index,
                    headImg:'https://w.wallhaven.cc/full/k7/wallhaven-k7y1x7.png',
                    comment: this.inputContent,
                    time: moment().format('YYYY年MM月DD日 HH:mm'),
                    reply: []
                });
                this.inputContent = '';
                this.$message({
                    showClose: true,
                    message: '评论成功',
                    type: 'success'
                });
            }
        }
    }
}
</script>
<style scoped>
.header {
    margin: 0 2%;
    display: flex;
    justify-content: space-around;
}
.peopleicon {
    flex: 2;

}
.input {
    flex: 20;
}
.btn {
    margin: 10px 3% 0 0;
    display: flex;
    justify-content: right;
}
</style>

commentItem.vue

<template>
    <div class="comItem">
        <div class="header-img">
            <el-avatar :src="itemcomment.headImg" :size="50"></el-avatar>
        </div>
        <div class="baseinfo">
            <div class="author-info">
                <span class="author-name">{{ itemcomment.name }}</span>
                <span class="author-time">{{ itemcomment.time }}</span>
          <li class="el-icon-chat-dot-square" id="icon" @click.stop="showcombtn" > </li>
            </div>
            <div class="datainfo">
                <span> {{ itemcomment.comment }}</span>
            </div>
            <slot></slot>
        </div>
    </div>

</template>

<script>
import commentHeader from './commentHeader.vue'
export default {
    name: 'commentItem',
    components: {
        commentHeader
    },
    props: {
        itemcomment: {
            type: Object,
            default: {}
        },
        index: {
            type: String,
            default: '-1'
        }
    },
    methods: {
        showcombtn() {
            this.$emit('getindex',this.itemcomment)
        },
    }
}
</script>

<style scoped>
.comItem {
    margin: 10px auto;
    padding-top: 5px;
    display: flex;
    border-top: 1px solid rgb(128, 125, 125);
}

.header-img {
    margin: 0 1%;
}

.baseinfo {
    flex: 18;
}

.author-info {
    display: flex;
    position: relative;
    flex-direction: column;
}

#icon {
    font-size: 20px;
    position: absolute;
    top: 10%;
    right: 20%
}

.author-name {
    font-size: 20px;
    font-weight: bold;

}

.datainfo {
    margin: 10px 30px 0 0;
    font-size: 18px;
    white-space: pre-line;
    word-break: break-all;
}
</style>

discuss.vue

<template>
  <div @click="seting">
    <comment-header @subCommet="addNewComment"></comment-header>
    <div v-for="(item,key) in comments" :key="key">
      <comment-item :itemcomment="item" :index="key" @getindex="getindex">
        <div v-show="item.prio == 1" class="reply">
          <div v-for="(singe, k) in item.reply" :key="k">
            <comment-item :itemcomment="singe" @getindex="getindex"></comment-item>
          </div>
        </div>
        <div class="commentbtn">
          <comment-header v-show="currentId == key" :index="key" @subCommet="subCommet"></comment-header>
        </div>
      </comment-item>

    </div>
    <h4 class="combottom">到底了~~</h4>
    <back-top></back-top>
  </div>
</template>

<script>
import commentHeader from './discussChild/commentHeader.vue'
import commentItem from './discussChild/commentItem.vue'
import backTop from '@/components/common/backtop/backTop'
import { nanoid } from "nanoid";
export default {
  name: 'discuss',
  components: {
    commentHeader,
    commentItem,
    backTop
  },
  data() {
    return {
      currentId: -1,
      currentcomment: {},
      comments: {
        1: {
          name: 'Lana',
          headImg: 'https://w.wallhaven.cc/full/72/wallhaven-727md3.png',
          to: 'Lana',
          toId: 1,
          comment: '我发布一张新专辑Norman Fucking Rockwell,大家快来听啊',
          time: '2019年9月16日 18:43',
          prio: 1,
          reply: [
            {
              name: 'Taylor Swift',
              headImg: 'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
              to: 'Lana',
              toId: 1,
              comment: '我很喜欢你的新专辑!!',
              time: '2019年9月16日 18:43',
              prio: 2,
              reply: []
            },
          ]
        },
        2: {
          name: 'Taylor Swift',
          to: 'Taylor Swift',
          toId: 2,
          headImg: 'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
          comment: '我发行了我的新专辑Lover',
          time: '2019年9月16日 18:43',
          prio: 1,
          reply: [
            {
              name: 'Lana',
              headImg: 'https://w.wallhaven.cc/full/72/wallhaven-727md3.png',
              to: 'Taylor Swift',
              toId: 2,
              comment: '新专辑和speak now 一样棒!',
              time: '2019年9月16日 18:43',
              prio: 2,
              reply: []
            }, {
              name: 'Rey',
              toId: 2,
              headImg: 'https://w.wallhaven.cc/full/k7/wallhaven-k7q9m7.png',
              to: 'Taylor Swift',
              comment: '新专辑和speak now 一样棒!',
              time: '2019年9月16日 18:43',
              prio: 2,
              reply: []
            }, {
              name: 'Del',
              headImg: 'https://w.wallhaven.cc/full/k7/wallhaven-k7yww1.jpg',
              to: 'Taylor Swift',
              toId: 2,
              comment: '新专辑和speak now 一样棒!',
              time: '2019年9月16日 18:43',
              prio: 2,
              reply: []
            }
          ]
        },
        3: {
          name: 'Rockwell',
          to: 'Rockwell',
          toId: 3,
          headImg: 'https://ae01.alicdn.com/kf/Hdd856ae4c81545d2b51fa0c209f7aa28Z.jpg',
          comment: 'https://w.wallhaven.cc/full/o3/wallhaven-o3pj1m.jpg',
          time: '2019年9月16日 18:43',
          prio: 1,
          reply: []
        },
      }
    }
  },
  methods: {
    getindex(value) {
      this.currentcomment = value;
      this.currentId = value.toId;
    },
    subCommet(value) {
      if (this.currentcomment.prio == 2) {
         value=this.initcomment(value)
         value.comment = `@${this.currentcomment.name}: ${value.comment}`
        this.comments[this.currentId].reply.push(value)
      }
      else {
        this.comments[this.currentId].reply.push(this.initcomment(value))
      }

      this.seting();
    },
    seting() {
      this.currentId = -1;
      this.currentcomment = {}
    },
    addNewComment(value) {
      const id=nanoid()
      value.toId=id
      this.$set(this.comments,id,value);
    },
    initcomment(value) {
      value.prio = 2;
      value.to = this.currentcomment.name;
      value.toId = this.currentcomment.toId;
      return value;
    }
  },
}
</script>
<style scoped >
.reply {
  width: 90%;
  margin: auto;

}

.commentbtn {
  margin-top: 30px;
}

.combottom {
  margin-top: 30px;
  text-align: center;
}
</style>

最后在你需要的地方导入使用就行了

实例:

<template>

 <discuss></discuss>
 
</template>
<script>
import  discuss from './discuss/discuss';

export default {
    name: 'articleContent',
    components:{
     discuss,
    },
}
</script>

<style scoped>

</style>

我这里呈现的效果大致是这样,因为数据的原因大家最后呈现的结果跟我的有些出入

image.png

image.png

5.总结

其实能够不借助其他组件,自己实现这样的评论功能,还挺有收获的,一定程度上也提高了个人的代码编写能力