两种方式实现表格折叠操作按钮过多效果

4,151 阅读2分钟

「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」。

前言

大家在做管理后台系统的时候,是否有遇到操作按钮太多(三个以上)的情况呢?如下图

fold.gif

实现思路

  • 总体利用插槽 slot+ref实现,但是可分两种方式实现,本组件都结合到一起了。
  • 公共html代码:
<div :ref="`operation+${customRef}`" class="operation-container">
    <div :ref="`operation+${customRef}_button`">
      <slot />
    </div>
    <el-popover
      popper-class="btns-container"
      :placement="placement"
      width="auto"
      trigger="hover"
    >
      <slot />
      <i v-show="isFold" slot="reference" class="el-icon-more fold-icon" />
    </el-popover>
 </div>

方式一:

先来一个简单的实现方式,只要超过len(如:len=3)则出现 ··· ,通过
const children = this.$refs[operation+${this.customRef}_button].children
当children.length>=len,则满足折叠条件,则isFold=true,并且第lenchildren.length-1的DOM节点隐藏,即可:

this.isFold = children.length > this.len
// 当按钮数量与len不相等,则从len-1开始隐藏,从而使得 ··· 按钮显示
const isLen = children.length === this.len ? this.len : this.len - 1 
children.forEach((child, ins) => {
       child.style.display = ins >= isLen ? 'none' : 'inline-block'
})

方式二:

首先了解一下offsetWidthscrollWidth的使用,可点击前往查看
当父级容器宽度小于内容宽度,则需要出现 ··· 按钮,超出父容器宽度的按钮,则隐藏即可,关键如何获取这两个宽度,则需要上面两个方法了,其他说明也在代码中有注释。具体实现如下
注:const childrenWidth = children.map(item => item.offsetWidth) // Array.isArray(children)===false

// 获取父容器宽度(包含边线)
const offsetWidth = this.$refs[`operation+${this.customRef}`].offsetWidth
// 获取本Dom下内容的宽度
const scrollWidth = this.$refs[`operation+${this.customRef}`].scrollWidth
this.isFold = offsetWidth < scrollWidth
if (this.isFold) {
    // const childrenWidth = children.map(item => item.offsetWidth) // Array.isArray(children)===false
    const childrenWidth = []
    for (let i = 0; i < children.length; i++) {
          childrenWidth.push(children[i].offsetWidth)
    }
    let maxCount = 0
    const showMaxIndex = childrenWidth.findIndex((item, ins) => {
           maxCount = item + maxCount + (ins ? 10 : 0)
           return maxCount > offsetWidth
     })
     children.forEach((item, index) => {
           item.style.display = index >= showMaxIndex ? 'none' : 'inline-block'
      })
      maxCount = null // 空变量,释放
}

完整的组件代码

props说明
customRef任意数字/字符串
lenlen=0:根据宽度计算显示个数;len>0:则表示默认显示len个按钮
placement同el-popover的 placement的配置项
<template>
  <div :ref="`operation+${customRef}`" class="operation-container">
    <div :ref="`operation+${customRef}_button`">
      <slot />
    </div>
    <el-popover
      popper-class="btns-container"
      :placement="placement"
      width="auto"
      trigger="hover"
    >
      <slot />
      <i v-show="isFold" slot="reference" class="el-icon-more fold-icon" />
    </el-popover>
  </div>
</template>
<script>
export default {
  name: 'ColumnOperation',
  props: {
    customRef: {
      type: [Number, String],
      default: 0
    },
    len: {
      type: Number,
      default: 0 // len=0:根据宽度计算显示个数;len>0:则表示默认显示len个按钮
    },
    placement: {
      type: String,
      default: 'left'
    }
  },
  data() {
    return {
      width: '',
      isFold: false
    }
  },
  mounted() {
    this.domInit()
  },
  methods: {
    domInit() {
      const children = this.$refs[`operation+${this.customRef}_button`].children // type: Array
      this.$nextTick(() => {
        if (this.len) {
          this.isFold = children.length > this.len
          const isLen = children.length === this.len ? this.len : this.len - 1
          children.forEach((child, ins) => {
            child.style.display = ins >= isLen ? 'none' : 'inline-block'
          })
        } else {
          // 获取父容器宽度(包含边线)
          const offsetWidth = this.$refs[`operation+${this.customRef}`]
            .offsetWidth
          // 获取本Dom下内容的宽度
          const scrollWidth = this.$refs[`operation+${this.customRef}`]
            .scrollWidth
          this.isFold = offsetWidth < scrollWidth
          if (this.isFold) {
            // const childrenWidth = children.map(item => item.offsetWidth) // Array.isArray(children)===false
            const childrenWidth = []
            for (let i = 0; i < children.length; i++) {
              childrenWidth.push(children[i].offsetWidth)
            }
            let maxCount = 0
            const showMaxIndex = childrenWidth.findIndex((item, ins) => {
              maxCount = item + maxCount + (ins ? 10 : 0)
              return maxCount > offsetWidth
            })
            children.forEach((item, index) => {
              item.style.display =
                index >= showMaxIndex ? 'none' : 'inline-block'
            })
            maxCount = null // 空变量,释放
          }
        }
      })
    }
  }
}
</script>

<style lang="scss">
@import '../../styles/variables';
.operation-container {
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
  min-width: 60px;
  display: flex;
  align-items: center;
  justify-content: start;
  .fold-icon {
    // position: absolute;
    // right: 12px;
    // top: calc(50% - 5px);
    color: $comTheme;
    cursor: pointer;
    margin-left: 10px;
  }
}
.btns-container {
  min-width: 60px;
  //   & > * {
  //     margin-right: 5px;
  //   }
}
</style>
  • 其他组件调用
<ColumnOperation>
     <el-button type="text" size="small">详情</el-button>
     // ...
     <el-button type="text" size="small">编辑</el-button>
     <el-button type="text" size="small">删除</el-button>
</ColumnOperation>               

总结

看完代码是不是感觉挺少的,嘿嘿...
写这个比业务舒服多了,可以适当的调试一下代码,写业务是不断的“调业务”,重复而且不过脑子... 呜呜呜