zoukankan      html  css  js  c++  java
  • 更多操作 > 评论组件的封装 吴小明

         

    评论:

      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}' />

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

  • 相关阅读:
    S02_CH09_UART串口中断实验
    S02_CH08_ ZYNQ 定时器中断实验
    S02_CH07_ ZYNQ PL中断请求
    S02_CH06_XADC实验
    S02_CH05_UBOOT实验Enter a post title
    S02_CH04_User_IP实验Enter a post title
    S02_CH03_EMIO实验Enter a post title
    S02_CH02_MIO实验Enter a post title
    S02_CH01_Hello World实验
    bzoj4868 [Shoi2017]期末考试
  • 原文地址:https://www.cnblogs.com/wuqilang/p/15750373.html
Copyright © 2011-2022 走看看