更多操作 ---> 评论组件的封装

     

评论:

  1、评论列表默认加载10条,下拉加载下一页。使用的是vant的list组件load事件

  2、回复默认只展示3条,超出隐藏,点击【展开全部评论】加载剩下的回复,点击【收起】回到默认状态

  3、点击评论者可以回复当前评论,input中显示回复的谁,并聚焦    this.$refs.inputRef.focus()

  4、点击【评论】按钮打开评论popup,而【操作】和【评论】是兄弟组件,所以这里使用observer传值的方式打开评论列表

 

Comment.vue

<template>
  <van-popup v-model="show" v-if="show" position="bottom" class="comment-popup" :style="{ height: '80%' }">
    <header>评论</header>
    <van-list v-model="loadMoreLoading" @load="loadMore" ref='vanListRef' offset='50'>
      <template v-if="listData.length > 0">
        <div class="crad" v-for="(item,index) in listData" :key="index">
          <div class="commentRow" @click="onCommentRow(item,index)">
            <div class="row head">
              <div class="left">{{item.userName}}</div>
              <div class="right">{{item.createTime}}</div>
            </div>
            <div class="row">
              <div class="content">{{item.content}}</div>
            </div>
          </div>
          <template v-if="item.replyVOList&&item.replyVOList.length">
            <!-- 展开展示全部 收起只展示三条 -->
            <div v-for="(obj,oIndex) in item.isOpen?item.replyVOList : item.replyVOList.slice(0,3)" :key="oIndex" class="commentRow replyRow"
              @click="onCommentReply(item,obj,index)">
              <div class="row head">
                <div class="left">{{obj.userName}}</div>
                <div class="right">{{obj.createTime}}</div>
              </div>
              <div class="row">
                <div class="content">回复<span class="contentSpan">@{{obj.replyUserName}}</span>:{{obj.content}}</div>
              </div>
            </div>
          </template>
          <van-loading v-if="item.loading" />
          <template v-if="item.replyNum&&item.replyNum>3&&!item.loading">
            <div class="shrink out replyRow" v-if="!item.isOpen" @click="onOpen(item,index,true)">
              展开全部评论
            </div>
            <div class="shrink open replyRow" v-else @click="onOpen(item,index,false)">
              收起
            </div>
          </template>
        </div>
      </template>
      <div v-else class="empty-data">暂无评论</div>
    </van-list>
    <!-- 底部 -->
    <footer>
      <van-field ref="inputRef" v-model="commentContent" maxlength="500" center clearable :placeholder="replyPlaceholder || '小小的鼓励,是团队的凝聚'" />
      <van-button @click="send">发送</van-button>
    </footer>
  </van-popup>
</template>

<script>
import { getToken } from '@/utils/token'
import {
  getCommentList,
  insertComment,
  addReply,
  getLastReplyList
} from '@/api/project'
export default {
  components: {},
  props: { commentParams: { type: Object, require: true } },
  data() {
    return {
      show: false,
      queryParams: {
        page: 1,
        pageSize: 10,
        mid: this.commentParams.mid, // 项目报:flowTrackId   拜访计划/拜访反馈:id
        mtype: this.commentParams.mtype // 1=项目报 2=拜访计划 3=拜访反馈
      },
      totalPages: 0, // 总页数
      listData: [], //评论列表
      sendItem: {},
      replyPlaceholder: '', // 评论输入框中的placeholder文案
      commentContent: '', // 评论输入框中输入的内容
      loadMoreLoading: false // 加载更多loading
    }
  },
  computed: {
    userId: () => {
      const token = getToken()
      return Number(token.split('-')[0])
    }
  },
  created() {
    this.$observer.$on('openComment', () => {
      this.queryParams.page = 1
      this.fetchProjectLog()
      this.show = true
    })
  },
  beforeDestroy() {
    this.$observer.$off('openComment')
  },
  methods: {
    onOpen(item, index, isOpen) {
      this.setOpen({ index, isOpen })
    },
    onCommentRow(item, index) {
      let { userId } = this
      if (item.userId != userId) {
        item.commentId = item.id
        item.index = index
        this.setComment(item)
      }
    },
    onCommentReply(item, obj, index) {
      let { userId } = this
      if (obj.userId != userId) {
        obj.commentId = item.id
        obj.index = index
        this.setComment(obj)
      }
    },
    fetchProjectLog() {
      const { queryParams } = this
      this.$vux.loading.show('加载中...')
      return getCommentList(queryParams)
        .then(res => {
          this.$vux.loading.hide()
          let { data, totalPages } = res
          if (queryParams.page === 1) {
            if (data && data.length) {
              let arr = data.map(item => {
                item.isOpen = false
                item.loading = false
                return item
              })
              this.listData = arr
            } else {
              this.listData = []
            }
          } else {
            if (data && data.length) {
              let arr = data.map(item => {
                item.isOpen = false
                item.loading = false
                return item
              })
              this.listData = [...this.listData, ...arr]
            } else {
              this.listData = []
            }
          }
          this.totalPages = totalPages
          return res
        })
        .catch(() => {
          this.$vux.loading.hide()
        })
    },
    setComment(item) {
      this.sendItem = { ...item }
      this.replyPlaceholder = '回复' + item.userName + ''
      this.$refs.inputRef.focus() // 聚焦
    },
    setOpen(obj) {
      let { index, isOpen } = obj
      let item = this.listData[index]
      if (isOpen && item.replyVOList.length === 3 && item.replyNum > 3) {
        item.loading = true
        getLastReplyList({ commentId: item.id }).then(result => {
          if (result.data && result.data.length) {
            item.replyVOList = [...item.replyVOList, ...result.data]
            item.loading = false
            item.isOpen = isOpen
          }
        })
      } else {
        item.isOpen = isOpen
      }
    },
    send() {
      const { mid, mtype } = this.commentParams
      const { commentContent, replyPlaceholder } = this
      if (replyPlaceholder && commentContent) {
        let { commentId, userId, index } = this.sendItem
        const params = { content: commentContent, commentId, replyUser: userId }
        addReply(params)
          .then(res => {
            if (res.data) {
              this.commentContent = ''
              let listData = [...this.listData]
              listData[index].replyNum++
              listData[index].replyVOList.push(res.data)
              this.$set(this, 'listData', listData)
              this.$vux.toast.text('回复成功')
              this.replyPlaceholder = ''
            } else {
              this.$vux.toast.text(res.msg || '回复失败')
            }
          })
          .catch(() => {
            this.$vux.toast.text('回复失败')
          })
      } else {
        const params = { mid, mtype, content: commentContent }
        insertComment(params)
          .then(res => {
            if (res.data) {
              this.commentContent = ''
              this.queryParams.page = 1
              this.fetchProjectLog()
              this.$vux.toast.text('评论成功')
            } else {
              this.$vux.toast.text(res.msg || '评论失败')
            }
          })
          .catch(() => {
            this.$vux.toast.text('评论失败')
          })
      }
    },
    loadMore() {
      const { totalPages } = this
      const { page } = this.queryParams
      if (totalPages > page) {
        this.queryParams.page++
        this.fetchProjectLog().then(res => {
          if (res.success) {
            this.loadMoreLoading = false
          }
        })
      } else {
        this.loadMoreLoading = false
      }
    }
  }
}
</script>

css:

<style lang='less' scoped>
.comment-popup {
  line-height: 1;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  > header {
    color: #4d5c82;
    font-size: 18px;
    font-family: PingFangSC, PingFangSC-Medium;
    font-weight: 600;
    padding: 15px 15px 20px 15px;
  }
  .van-list {
    flex: 1;
    overflow: auto;
    padding: 0 15px;
    .crad {
      margin-bottom: 15px;
      font-size: 14px;
      color: #4d5c82;
      .row {
        display: flex;
        .left {
          flex: 1;
          font-weight: 600;
        }
        .content {
          line-height: 22px;
          font-size: 12px;
          .contentSpan {
            color: #1288fe;
          }
        }
      }
      .replyRow {
        padding-left: 15px;
      }
      .head {
        margin-top: 5px;
        margin-bottom: 5px;
      }
      .shrink {
        margin-top: 10px;
        color: #1288fe;
        font-size: 12px;
      }
      .van-loading {
        display: flex;
        justify-content: center;
      }
    }
  }
  > footer {
    display: flex;
    align-items: center;
    padding: 20px 12px;
    .van-field {
      background-color: #f3f6f9;
      border-radius: 20px;
      height: 40px;
      margin-right: 15px;
    }
    .van-button {
      border: none;
      padding: 0;
      height: 30px;
      .van-button__content {
        width: 58px;
        background-color: #1288fe;
        border-radius: 4px;
        color: #fff;
      }
      &::before {
        border: none;
      }
    }
  }
}
</style>
View Code

 

MoreOperations.vue

<template>
  <!-- 更多操作:编辑,删除,评论 -->
  <div class="more-operations">
    <transition name="popup">
      <div class="popup" v-show="isShow" @click="handleClose">
        <div class="btns">
          <div @click="handleEdit">编辑</div>
          <div @click.stop="handleDelete">删除</div>
          <div @click="handleComment">评论</div>
        </div>
      </div>
    </transition>
  </div>
</template>
<script>
export default {
  props: { visible: { type: Boolean, require: true } },
  computed: {
    isShow: {
      get() {
        return this.visible
      },
      set(flag) {
        this.$emit('update:visible', flag)
      }
    }
  },
  methods: {
    handleEdit() {
      this.$emit('edit')
    },
    handleDelete() {
      this.$emit('delete')
    },
    handleClose() {
      this.isShow = false
    },
    handleComment() {
      this.$observer.$emit('openComment')
    }
  }
}
</script>

css:

<style lang="less" scoped>
.more-operations {
  .popup-enter-active,
  .popup-leave-active {
    transition: opacity 0.2s linear;
  }
  .popup-enter,
  .popup-leave-to {
    // transform: translate3d(100%, 0, 0);
    opacity: 0;
  }
  .popup-enter-to,
  .popup-leave {
    // transform: translate3d(0, 0, 0);
    opacity: 1;
  }
  .popup {
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: rgba(#000, 0.6);
    .btns {
      position: absolute;
      bottom: 70px;
      left: 0;
      right: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      > div {
        width: calc(100% - 60px);
        line-height: 50px;
        text-align: center;
        border-radius: 25px;
        background-color: #fff;
        font-size: 18px;
        color: #1288fe;
        margin-top: 15px;
        &:first-of-type {
          margin-top: 0;
        }
      }
      > div:nth-child(2) {
        color: #fc5e5e;
      }
    }
  }
}
</style>
View Code

 

【拜访计划】使用【更多操作】和【评论】组件:

    <!-- 更多操作 -->
    <MoreOperations :visible.sync='isShow' @edit='handleEdit' @delete='handleDelete' />
    <!-- 评论 -->
    <Comment :commentParams='{mid:$route.query.id,mtype:2}' />

  注:评论组件是可以重复使用的,在引入时传入不同的参数以供接口请求

 

posted @ 2021-12-30 20:03  吴小明-  阅读(460)  评论(0编辑  收藏  举报