Vue2+el-table实现表格行上下滚动,表格单元格内容溢出左右滚动 TextScroll、TableWrapper

Vue2+el-table实现表格行上下滚动表格单元格内容溢出左右滚动 TextScroll、TableWrapper

TextScroll 文本左右滚动容器组件

<template>
  <div ref="wrapper" class="scroll-wrapper" @mouseenter="mouseenter" @mouseleave="mouseleave">
    <div ref="text" class="text" :style="{transform: `translateX(${translateX}px)`}">
      <slot>{{ text }}</slot>
    </div>
  </div>
</template>

<script>
/**
 * 文本左右滚动容器组件
 */
export default {
  name: 'TextScroll',
  props: {
    text: [String, Number],
    step: {
      type: [String, Number],
      default: 0.2
    }
  },
  data() {
    return {
      translateX: 0,
      trip: 0,
      realStep: 1,
      frameId: null
    }
  },
  mounted() {
    const rect = this.$refs.text.getBoundingClientRect()
    const wrapperRect = this.$refs.wrapper.getBoundingClientRect()
    this.trip = rect.right - wrapperRect.width - rect.left
    this.mouseleave()
    this.$emit('hook:beforeDestroy', () => {
      this.mouseenter()
    })
  },
  methods: {
    mouseenter() {
      window.cancelAnimationFrame(this.frameId)
    },
    mouseleave() {
      if (this.trip > 8) {
        this.frameId = window.requestAnimationFrame(this.animate)
      }
    },
    animate() {
      if (Math.abs(this.translateX) >= this.trip) {
        this.realStep = this.step
      }
      if (this.translateX >= 0) {
        this.realStep = this.step * -1
      }
      this.translateX += this.realStep
      this.frameId = window.requestAnimationFrame(this.animate)
    }
  }
}
</script>

<style lang="scss" scoped>
.scroll-wrapper {
  overflow: hidden;
  .text {
    padding: 0 4px;
    display: inline-block;
    white-space: nowrap;
  }
}
</style>

TableWrapper 表格上下滚动容器组件

  • columns:表格列配置属性
    Eg. [{ label: '表头', prop: 'name', formatter: (content,row)=>content, style: { width: '160px' } }]
  • data:表格配置数据
    Eg.[{ name: '父类', child: [{name: '子类0'}] }]
  • full:表格在容器内平铺展示
  • tdspace:表格边框是否分隔
  • expand:是否开启子行
  • step:行上下滚动补偿
<template>
  <div :class="['table-wrapper',{full:full}]">
    <table>
      <thead>
        <tr>
          <th v-for="(column,index) in columns" :key="index" :class="`th-${index}`"
            :style="Object.assign({},column.style||{},column.headStyle||{})">
            {{ column.label }}</th>
        </tr>
      </thead>
    </table>
    <el-scrollbar ref="scroll">
      <table v-if="data.length" :class="['table-body',{'td-space':tdspace}]"
        :style="{transform: `translateY(${translateY}px)`}" @mouseenter="mouseenter"
        @mouseleave="mouseleave">
        <tbody>
          <template v-for="(item,key) in data">
            <tr :key="key" :class="[`tr-${key}`, `border-${item.key}`]"
              :style="{cursor: expand?'pointer':'auto'}" @click="doExpand(item.key)">
              <template v-for="(column,index) in columns">
                <td v-if="column.type === 'index'" :key="`${key}-${index}`" :class="`td-${index}`"
                  :style="column.style||{}">
                  {{ key+1 }}
                </td>
                <td v-else :key="`${key}-${index}`" :class="`td-${index}`"
                  :style="column.style||{}">
                  <TextScroll
                    :text="column.formatter?column.formatter(item[column.prop],item):item[column.prop]" />
                </td>
              </template>
            </tr>
            <template v-if="item.child && expandKey[item.key]">
              <tr v-for="(v,i) in item.child" :key="`${item.key}-${i}`"
                :class="[`tr-${key+i+1}`,`border-${item.key}`]" class="child">
                <template v-for="(column,index) in columns">
                  <td v-if="column.type === 'index'" :key="`${key+i+1}-${index}`"
                    :class="`td-${index}`" :style="column.style||{}">
                    {{ i+1 }}
                  </td>
                  <td v-else :key="`${key+i+1}-${index}`" :class="`td-${index}`"
                    :style="column.style||{}">
                    <TextScroll
                      :text="column.formatter?column.formatter(v[column.prop],v):v[column.prop]" />
                  </td>
                </template>
              </tr>
            </template>
          </template>
        </tbody>
      </table>
      <div v-else class="empty">暂无数据</div>
    </el-scrollbar>
  </div>
</template>

<script>
/**
 * 列表
 */
import TextScroll from './TextScroll.vue'
export default {
  name: 'TablerWrapper',
  components: {
    TextScroll
  },
  props: {
    /* 表格配置列属性 */
    columns: Array,
    /* 表格配置数据 */
    data: Array,
    /* 数据不滚动,进行平铺展示 */
    full: Boolean,
    /* 表格边框是否分隔 */
    tdspace: {
      type: Boolean,
      default: false
    },
    /* 是否开启表格子行 */
    expand: Boolean,
    /* 行自动上下滚动补偿 */
    step: {
      type: [String, Number],
      default: 0.3
    }
  },
  data() {
    return {
      translateY: 0,
      staticTranslateY: 0,
      trip: 0,
      realStep: 1,
      frameId: null,
      expandKey: {}
    }
  },
  watch: {
    data(n) {
      if (n.length) {
        this.expandKey = this.data.reduce((s, v) => {
          s[v.key] = true
          return s
        }, {})
        this.$nextTick(() => {
          this.initScroll()
          this.mouseleave()
        })
      }
    }
  },
  methods: {
    initScroll() {
      window.cancelAnimationFrame(this.frameId)
      const rect = this.$refs.scroll.$el.querySelector('.table-body').getBoundingClientRect()
      const wrapperRect = this.$refs.scroll.$el.querySelector('.el-scrollbar__view').getBoundingClientRect()
      this.trip = rect.bottom - wrapperRect.height - rect.top
    },
    mouseenter() {
      window.cancelAnimationFrame(this.frameId)
      const translateY = this.translateY
      this.translateY = 0
      this.$nextTick(() => {
        this.$refs.scroll.$el.querySelector('.el-scrollbar__wrap').scrollTop = Math.abs(translateY)
      })
    },
    mouseleave() {
      const translateY = this.$refs.scroll.$el.querySelector('.el-scrollbar__wrap').scrollTop
      this.$refs.scroll.$el.querySelector('.el-scrollbar__wrap').scrollTop = 0
      this.translateY = translateY * -1
      if (this.trip > 8) {
        this.frameId = window.requestAnimationFrame(this.animate)
      }
    },
    animate() {
      if (Math.abs(this.translateY) >= this.trip) {
        this.realStep = this.step
      }
      if (this.translateY >= 0) {
        this.realStep = this.step * -1
      }
      this.translateY += this.realStep
      this.frameId = window.requestAnimationFrame(this.animate)
    },
    doExpand(key) {
      this.expandKey = { ...this.expandKey, [key]: !this.expandKey[key] }
      this.$nextTick(() => {
        this.initScroll()
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.table-wrapper {
  color: #fff;
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow: hidden;
  table {
    table-layout: fixed;
    border-collapse: separate;
    border-spacing: 0 3px;
    width: 100%;
    thead th {
      font-size: 14px;
      text-align: center;
      vertical-align: bottom;
      padding: 10px 5px;
      background: rgba(69, 167, 248, 0.2980392156862745);
    }
    td {
      color: #fff;
      font-size: 18px;
      padding: 6px 6px;
      text-align: center;
      line-height: 16px;
      vertical-align: middle;
      background: rgba(69, 167, 248, 0.09803921568627451);
    }
    .child {
      td {
        background: rgba(60, 165, 250, 0.298);
      }
    }
    td,
    th {
      &:first-child {
        border-radius: 4px 0 0 4px;
      }
      &:last-child {
        border-radius: 0 4px 4px 0;
      }
    }
  }

  .table-body.td-space {
    border-collapse: separate;
    border-spacing: 3px 3px;
  }

  .el-scrollbar {
    position: relative;
    flex: 1;
    overflow: hidden;
  }
}
.full {
  ::v-deep .el-scrollbar__view {
    height: 100%;
  }
  .table-body {
    min-height: 100%;
    td {
      vertical-align: middle;
    }
  }
}
.empty {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  font-size: 12px;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值