多行文字显示固定行数+“···展开”,点击展开后显示收起

这个功能很常见,但是在vue这种数据驱动视图层的项目中,实现这个功能也要操作dom,主要是通过获取dom节点的高度,来判断是否截掉多余行数。

我实现的功能是:vue项目异步从后端获取多条留言,文字长度超过两行,多余文字截掉,显示····+展开,点击展开后,显示全部文本内容,“展开”变“收起”,点击收起,又变成两行显示。

效果图如下:

直接附上代码,代码中有注释。

 

<template>
  <div class="div-full list-view" v-loading="isLoading" >
    <div class="show-view list-view-title" >
      <h3>留言列表</h3>
      <div class="view-base-oper">
        <el-button v-if="userId=='50'" type="primary" v-has = "'leaveMessage'"  size="mini" @click="openLeaveMessage">我要留言</el-button>
      </div>
      <div class="list-pager-top">
        <el-pagination
          background
          layout="prev, slot, next"
          class="transparent-page-button"
          :page-size="pager.pageSize" :current-page="pager.currentPage" :total="total"
          @current-change="handleCurrentChange">
          <span style="color:#656565;font-size:12px;font-weight:normal;text-align:center;">
                <template v-if="total>0"><i style="font-style:normal;color:#ed8232;">{{pager.currentPage}}</i>/</template>
                <template v-else><i style="font-style:normal;color:#ed8232;"></i>&nbsp;</template>
                {{Math.ceil(total/pager.pageSize)}}
            </span>
        </el-pagination>
      </div>
    </div>
    <el-backtop target=".scroll-box .el-scrollbar__wrap" :bottom="64" :right="36">
      <div class="iconfont backtop" id="backBtn">&#xe73e;</div>
    </el-backtop>
    <el-scrollbar class="scroll-box" style="height:100%" >
      <div class="message-box">
        <!-- 内容填充部分 -->
        <ul class="message-list" id="messBox">
          <li v-for="(item,index) in showList" :key = "index">
            <h2 class="message-title">
              <span class="txt">{{item.messageTitle}}</span>
              <span class="type">{{item.messageType}}</span>
              <el-button v-if="userId != '50' && item.isReply == '0' " v-has = "'replyMessage'" type="primary" size="mini" @click="openReplyMessage(item.messageId)" style="float: right;">回复</el-button>
            </h2>
            <div class="message-content">
              <label class="left">留言:</label>
              <div class="right">
                <div class="content " >
                  <p :id="'leaveMess'+index" >{{item.messageContent}}</p>
                  <span :id="'leaveMessBtn'+index" class="show-all" @click="showAll($event,item,'leaveMess',index)">展开</span></span>
                </div>
                <p class="info">
                  <span>留言者:{{item.messageUserName}}</span>
                  <span>留言时间:{{item.messageTime}}</span>
                </p>
              </div>
            </div>
            <div class="message-content message-reply" v-if="item.isReply == '1'">
              <label class="left">回复:</label>
              <div class="right">
                <div class="content ">
                  <p :id="'replyMess'+index">{{item.replyContent}}</p>
                  <span :id="'replyMessBtn'+index" class="show-all" @click="showAll($event,item,'replyMess',index)">展开</span>
                </div>
                <p class="info">
                  <span>回复者:{{item.replyUserName}}</span>
                  <span>回复者单位:{{item.replyDeptName}}</span>
                  <span>回复时间:{{item.replyTime}}</span>
                </p>
              </div>
            </div>
          </li>
        </ul>
        <div class="no-data" v-if="total<=0">
          <h3 v-show="!isLoading">暂无数据</h3>
        </div>
      </div>
      <div class="list-pager" v-if="total>0" style="text-align:right; padding:0 20px;">
        <el-pagination
          background
          layout="prev, pager, next, total, jumper"
          :page-size="pager.pageSize" :current-page="pager.currentPage" :total="total"
          @current-change="handleCurrentChange">
        </el-pagination>
      </div>
    </el-scrollbar>
    <leaveMessage ref="leaveMessage" :messageTypeOptions = "messageTypeOptions" @reloadList = 'reloadData'/>
    <replyMessage ref="replyMessage" @reloadList = 'reloadData'/>
  </div>

</template>

<script>
import ViewBase from '@/components/ViewBase'
import ViewBaseMixins from '@/mixins/ViewBaseMixins'
import { getMessageList} from '@/api/modules/message/message'
import leaveMessage from '@/views/modules/message/subModules/leaveMessage/operMessageDialog/leaveMessage.vue'
import replyMessage from '@/views/modules/message/subModules/leaveMessage/operMessageDialog/replyMessage'

export default {
  props:{
    messageTypeOptions:Array,
  },
  components:{
    leaveMessage,
    replyMessage,
  },
  mixins: [ViewBaseMixins],
  data(){
    return{
      isLoading:false,
      filterData:{
        messageStatus: "",
        messageType: "",
        messageUserId: "",
        replyUserId: ""
      },
      pager: {
        currentPage: 1,
        orderBy: "updateTime",
        orderType: "desc",
        pageSize: 20
      },
      total:0,
      showList:[],
      textHeight: null,
      showContent:{
        transition: "all 2s",
        height:"auto",
        whiteSpace:"normal",
        overflow:"auto",
        textOverflow:"normal",
      },
      upContent:{
        transition: "all 2s",
        height:"28px",
        whiteSpace:"nowrap",
        overflow:"hidden",
        textOverflow:"ellipsis",
      }

    }
  },
  computed:{
    userId () {
      if(this.$isCommonUser()){           //地方用户
        return "50"
      } else if(this.$isAcceptUser()){   //81中心受理用户
        return "81"
      }else if(this.$isReViewUser()){    //82中心复核用户
        return "82"
      }
    }

  },
  mounted() {

    this.loadData()
    // this.$EventBus.$off('EB_filterData');  // 记得销毁
    // this.$EventBus.$on('EB_filterData',(filterData) => {
    //    Object.keys(filterData).forEach(key=>{this.filterData[key]=filterData[key]})
    //   this.loadData()
    // })

  },
  methods: {
    /**
     * 获取用户列表
     */
    loadData(){
      const _this = this
      _this.isLoading =true
      let filterData = {
        messageStatus: _this.filterData.messageStatus,
        messageType: _this.filterData.messageType,
        messageUserId: _this.filterData.messageUserId,
        replyUserId: _this.filterData.replyUserId,
        pager: _this.pager
      }
      this.showList = []
      getMessageList(filterData).then(response => {
        _this.showList = response.list
        _this.total = response.total
        //判断留言和回复内容行高,确定是否显示展开收起按钮
        this.$nextTick(() => {
          for(let i=0; i< response.total; i++){
            // console.log(document.getElementById('replyMess'+i))
            let leaveMess = document.getElementById('leaveMess'+i)
            let leaveMessBtn = document.getElementById('leaveMessBtn'+i)
            this.computeLineHeight(leaveMess,leaveMessBtn)

            let replyMess = document.getElementById('replyMess'+i)
            let replyMessBtn = document.getElementById('replyMessBtn'+i)
            this.computeLineHeight(replyMess,replyMessBtn)
          }

        })
      }).catch(error => {
        console.log(`角色列表获取失败!${error}`)
      }).finally( error => {
        _this.isLoading = false
      })
    },
    reloadData(filterData){
      if(filterData){
        Object.keys(filterData).forEach(key=>{this.filterData[key]=filterData[key]})
      }
      this.handleCurrentChange(1)
    },
    openLeaveMessage(userId){
      this.$refs.leaveMessage.show();
    },
    openReplyMessage(messageId){
      this.$refs.replyMessage.show(messageId);
    },
    showAll(e,item,type,index,lineCount=2,lineHeight = 28){
        let btnDom = document.getElementById(type+index)
        let contentHeight = e.target.getAttribute('data-height')

      if(e.currentTarget.innerText == '展开'){
        btnDom.style.height = contentHeight +"px"
        btnDom.style.transitionDuration = ".5s"

        e.currentTarget.innerText = '收起'
        e.currentTarget.classList.add("active")
      }else if(e.currentTarget.innerText == '收起'){

        btnDom.style.height = lineCount * lineHeight +'px'
        btnDom.style.overflow = "hidden"
        btnDom.style.transitionDuration = ".5s"

        e.currentTarget.innerText = '展开'
        e.currentTarget.classList.remove("active")
      }
    },
    /**
     * 点击分页触发方法
     */
    handleCurrentChange(value) {
      this.pager.currentPage = value
      this.loadData()
      // this.$emit('func-page-change',value)
      //翻页返回顶部
      this.toTop()
    },
    toTop(){
      let btn = document.getElementById('backBtn')
      if(btn){
        btn.click();
      }
    },
    //计算行高,显示收起展示
    computeLineHeight(contentDom,btnDom,lineCount=2,lineHeight = 28){
      // let leaveMess = document.getElementById('leaveMess'+i)
      if(contentDom){
        let domHeight = contentDom.offsetHeight
          btnDom.setAttribute('data-height',domHeight)
        if(domHeight > lineHeight * lineCount){
          contentDom.style.height = lineHeight * lineCount +'px'
          contentDom.style.overflow = "hidden"
        }else{
          btnDom.style.display = 'none'
        }
      }else{
        return false
      }

    }

  }

}
</script>
<style lang="scss" scoped>
.message-box{
  margin:20px;
}
.message-list{
  li{
    padding:0 20px;
    margin-bottom:20px;
    border-bottom:1px solid #ddd;
    .message-title{
      height:30px;
      .txt{
        line-height:30px;
        font-size:14px;
      }
      .type{
        padding:0 10px;
        line-height:30px;
        font-size:12px;
        color:#306abb;
        font-weight:normal;
      }
    }
    .message-content{
      overflow:hidden;
      .left{
        width:50px;
        white-space:nowrap;
        float:left;
        line-height: 28px;
        font-size: 14px;
        color:#306abb ;
      }
      .right{
        margin-left:50px;
        .content{
          position: relative;
          padding:0 10px;
          min-height:28px;
          line-height: 28px;
          font-size: 12px;
          background-color: #f4f4f5;
          p{
            width:100%;
            line-height:28px;
            word-break: break-all;
            transition: height 2s;
          }
          .con{
            transition: all 2s;
            height:28px;
            white-space:nowrap;
            overflow:hidden;
            text-overflow:ellipsis;
          }
          .show-all{
            cursor: pointer;
            position: absolute;
            bottom:0;
            right:10px;
            padding-left:5px;
            line-height:28px;
            font-size:12px;
            color:#306abb ;
            background-color: #f4f4f5;
            &:before{
              content: '...';
              position: absolute;
              bottom: 0;
              left: -50px;
              width: 50px;
              text-align:right;
              background: linear-gradient(to right, transparent, #f4f4f5 );
            }
            i{
              padding:0 3px;
              line-height:28px;
              font-size:12px;
            }
            &.active{
              position: static;
              display:block;
              text-align:right;
              &:before{
                display:none;
              }
            }
          }
        }
        .info{
          text-align: right;
          span{
            line-height:28px;
            padding:0 10px;

          }
        }
      }
    }
  }


}

.text {
  position: relative;
  padding:0 10px 30px 10px;
  min-height:28px;
  line-height: 28px;
  font-size: 12px;
  background-color: #f4f4f5;
}

.retract {
  position: relative;
  overflow: hidden;
}

.retract:after {
  content: '...';
  position: absolute;
  bottom: 0;
  right: 0;
  width: 25px;
  padding-left: 30px;
  background: linear-gradient(to right, transparent, #f4f4f5 45%);
}

.btn {
  position: absolute;
  right: -2px;
  bottom: 0;
  font-size: 12px;
  line-height: 28px;
  letter-spacing: 2px;
  color:#306abb ;
  cursor: pointer;
  i{
    font-size: 12px;
  }
}

.more {
  font-size: 14px;
  line-height: 20px;
  letter-spacing: 2px;
  color: #666666;
  visibility: hidden;
}
</style>

 

因为为没有搭建后台环境,所以用mock模拟的后台数据,引入的mock.js文件:

//引入mock模块
import Mock from 'mockjs';

Mock.mock('/messageList', { //输出数据
    list:[
      {
          "id":'1',
        'messageTitle': '随便起的名字1', 
        'messageUserName': '@name', //随机生成姓名
        'messageContent': '1111111111111留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容', 
        'messageTime|10-20': 10
      },
      {
        "id":'2',
        'messageTitle': '随便起的名字2', 
        'messageUserName': '@name', //随机生成姓名
        'messageContent': '2222222222留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容留言内容', 
        'messageTime|10-20': 10
      }  
    ]
    
});

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值