Element-ui自定义组件:可折叠按钮列表

这篇具有很好参考价值的文章主要介绍了Element-ui自定义组件:可折叠按钮列表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

功能点

1、工具栏的功能按钮要超宽不换行,宽度不够折叠进”更多“按钮;

2、下拉菜单按钮和纯图标按钮默认不折叠;

3、折叠前后按钮组顺序保持不变。

实现思路

1、默认展开全量按钮,并对其宽度进行缓存;

2、循环计算展开按钮的总宽度 与 容器宽度 的差值,并进行按钮的折叠与释放处理;

3、监听窗口大小改变,不断进行步骤2;

4、难点:区分放大、缩小操作进行分别处理(当然也可以每次遍历全量按钮,这样只要考虑需要折叠的情况,这个比较简单,这里不展开说明)。

演示效果 

Element-ui自定义组件:可折叠按钮列表

 index.vue

<template>
  <el-container class="page-container">
    <el-header class="page-header">
      <h3 class="page-title">内页标题</h3>
      <el-form :inline="true" :model="formInline" class="demo-form-inline">
        <el-form-item label="审批人">
          <el-input v-model="formInline.user" placeholder="审批人"></el-input>
        </el-form-item>
        <el-form-item label="活动区域">
          <el-select v-model="formInline.region" placeholder="活动区域">
            <el-option label="区域一" value="shanghai"></el-option>
            <el-option label="区域二" value="beijing"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="onSubmit">查询</el-button>
        </el-form-item>
      </el-form>
    </el-header>
    <el-main class="page-body">
      <div class="table-conatainer">
        <div class="table-tools">
          <h4 class="table-title">表格标题</h4>
          <div class="table-actions">
            <vp-button-list :data="buttonData"></vp-button-list>
          </div>
        </div>
        <div class="table-body">
          <el-table :data="tableData" border stripe height="100%" max-height="100%"
            style="width: 100%;position: absolute;">
            <el-table-column prop="date" label="日期" width="180"></el-table-column>
            <el-table-column prop="name" label="姓名" width="180"></el-table-column>
            <el-table-column prop="address" label="地址"></el-table-column>
          </el-table>
        </div>
        <el-pagination align="right" :current-page="currentPage" :page-sizes="[100, 200, 300, 400]" :page-size="100"
          layout="total, sizes, prev, pager, next, jumper" :total="400" background>
        </el-pagination>
      </div>
    </el-main>
  </el-container>
</template>

<script>
export default {
  components: {
    VpButtonList: () => import('./vp-button-list.vue')
  },
  data() {
    return {
      formInline: {
        user: '',
        region: ''
      },
      buttonData: [
        {
          text: '按钮名称1',
          type: 'primary',
        },
        {
          text: '按钮名称2',
          type: 'primary',
        },
        {
          text: '按钮名称3',
          type: 'primary',
        },
        {
          text: '按钮名称4',
          type: 'primary',
        },
        {
          text: '按钮名称555555555555',
          type: 'primary',
        },
        {
          text: '按钮名称6',
          type: 'primary',
        },
        {
          text: '按钮名称777777777777',
          type: 'primary',
        },
        {
          text: '按钮名称8',
          type: 'primary',
        },
        {
          text: '按钮名称9999999999999',
          type: 'primary',
        },
        {
          text: '下拉菜单按钮',
          children: [
            { text: '按钮一' },
            { text: '按钮二' }
          ],
          type: 'primary',
        },
        {
          icon: 'el-icon-d-arrow-left',
          type: 'primary',
        },
        {
          icon: 'el-icon-d-arrow-right',
          type: 'primary',
        }
      ],
      tableData: [
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-04',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1517 弄'
        },
        {
          date: '2016-05-01',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1519 弄'
        },
        {
          date: '2016-05-03',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1516 弄'
        }
      ],
      currentPage: 1,
    }
  },
  methods: {
    onSubmit() {
      console.log('submit!');
    }
  }
}
</script>

<style lang="scss" scoped>
.page-container {
  height: 100vh;

  .page-header {
    height: auto !important;

    .page-title {
      margin: 0 -20px 16px;
      padding: 16px;
      background: #409EFF;
      color: #fff;
    }
  }

  .page-body {
    background: #d5d9e4;
    display: flex;
    flex-direction: column;

    .table-conatainer {
      flex: 1;
      padding: 16px;
      background: #fff;
      display: flex;
      flex-direction: column;

      .table-tools {
        line-height: 40px;

        &::after {
          clear: both;
          content: "";
          display: block;
          height: 0;
          overflow: hidden;
          visibility: hidden;
        }

        .table-title {
          float: left;
        }

        .table-actions {
          float: right;
        }

        +.table-body {
          margin-top: 12px;
        }
      }

      .table-body {
        flex: 1;
        position: relative;
      }
    }
  }
}
</style>

vp-button-list.vue

<template>
    <div class="vp-button-list">
        <!-- 展示的按钮 -->
        <template v-for="(value, index) in expandedData">
            <el-dropdown v-if="value.children" :key="`dropdown-${index}`" :ref="`button-${index}`">
                <el-button v-bind="value">
                    <template v-if="value.text">{{ value.text }}</template><i class="el-icon-arrow-down el-icon--right"></i>
                </el-button>
                <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item v-for="(val, idx) in value.childlren" :key="`dropdown-item-${idx}`">
                        {{ val.text }}
                    </el-dropdown-item>
                </el-dropdown-menu>
            </el-dropdown>
            <el-button v-else :key="`button-${index}`" v-bind="value" :ref="`button-${index}`">
                <template v-if="value.text">{{ value.text }}</template>
            </el-button>
        </template>
        <!-- 更多按钮 -->
        <el-dropdown v-if="!rendered || buttonMoreShow" ref="button-more">
            <el-button v-bind="buttonMore"></el-button>
            <el-dropdown-menu slot="dropdown">
                <el-dropdown-item v-for="(val, idx) in collapseData" :key="`dropdown-item-${idx}`">
                    {{ val.text }}
                </el-dropdown-item>
            </el-dropdown-menu>
        </el-dropdown>
    </div>
</template>

<script>
import { throttle } from 'throttle-debounce';
function getMargin(el, containOffsetWidth = true) {
    const style = el.currentStyle || window.getComputedStyle(el);
    let styleValue = ['marginLeft', 'marginRight'].map(item => parseInt(style[item]));
    if (containOffsetWidth) {
        styleValue.push(el.offsetWidth);
    }
    return styleValue.reduce((pre, curr) => pre + curr);
}


/**
 * 可折叠的按钮列表
 * @desc 监听窗口改变自动折叠按钮进“更多”
 * @author zhengvipin
 * @date 2023/03/12 01:09
 */

export default {
    name: 'VpButtonList',
    componentName: 'VpButtonList',
    props: {
        data: {
            type: Array,
            default: () => []
        },
        // 组件所在的容器,基于此做宽度自适应计算
        container: {
            type: String,
            default: '.table-tools',
            require: true
        },
        // container下需要减去的占据空间的子类
        excluded: {
            type: Array,
            default: () => [
                '.table-title'
            ]
        },
        buttonMore: {
            type: Object,
            default: () => ({
                type: 'danger',
                icon: 'el-icon-more'
            })
        }
    },
    data() {
        return {
            expandedData: [],// 展开按钮数组
            collapseData: [],// 折叠按钮数组
            containerNode: null,// 容器节点
            excludedMargin: 0,// 可排除的间距
            actualWidthStack: [],// 展开按钮实际宽度历史栈
            devicePixelRatioStack: [],// 设备分辨率历史栈
            rendered: false,// 初始化标识
        }
    },
    computed: {
        buttonMoreShow() {
            return this.collapseData.length > 0; // 当折叠数组为空时,不展示“更多”按钮
        }
    },
    methods: {
        render() {
            // 1、存储容器dom
            this.containerNode = this.$el.closest(this.container);
            if (!this.containerNode) {// 容错:当 container 找不到时,默认把父节点当作容器
                this.containerNode = this.$el.parentNode;
            }
            // 2、计算可排除间距
            let excludedMargin = 0;
            if (this.excluded.length > 0) { // 子节点
                for (let tag of this.excluded) {
                    const tagNode = this.containerNode.querySelector(tag);
                    if (tagNode) {
                        excludedMargin += getMargin(tagNode);
                    }
                }
            }
            excludedMargin += getMargin(this.$el, false); // 组件本身
            excludedMargin += getMargin(this.$refs['button-more'].$el);// “更多”按钮
            this.excludedMargin = excludedMargin;
            // 3、补充按钮宽度
            let actualWidth = 0;
            this.expandedData.forEach((item, index) => {
                let buttonWidth = getMargin(this.$refs[`button-${index}`][0].$el);
                item.width = buttonWidth;
                actualWidth += buttonWidth;
            })
            // 4、初始化设备像素比队列,用于判断滚动监听时是放大还是缩小
            this.devicePixelRatioStack.push(window.devicePixelRatio);
            // 5、初始化可视按钮实际宽度队列,用于计算滚动监听后变动的差值
            this.actualWidthStack.push(actualWidth);
            // 6、初始化完毕
            this.rendered = true;
        },
        // todo: 还有个点可以优化,就是当更多按钮不出现时,可视区按钮宽度刚好不超宽,这种情况也要考虑~~~后续再处理~~
        onResize() {
            const expectedWidth = this.containerNode.clientWidth - this.excludedMargin;// 展开按钮期望宽度
            let actualWidth = this.actualWidthStack[this.actualWidthStack.length - 1]; // 展开按钮实际宽度
            let devicePixelRatio = window.devicePixelRatio;
            const scaleUp = devicePixelRatio - this.devicePixelRatioStack[this.devicePixelRatioStack.length - 1] >= 0;// 初始加载的时候也是考虑折叠情况
            let button;
            if (scaleUp) {// 放大:展开数据从右往左依次折叠按钮
                console.log('scaleUp');
                let i = this.expandedData.length - 1;
                while (actualWidth > expectedWidth) {// 如果超宽就折叠
                    button = this.expandedData[i];// 提前声明,用于跳过特定按钮
                    let { alwaysShow, width } = button;
                    if (!alwaysShow) {// 纯图标按钮和下拉菜单按钮不折叠
                        this.expandedData.splice(i, 1);
                        this.collapseData.unshift(button);
                        actualWidth -= width;
                    }
                    i--;
                }
            } else {// 缩小:折叠数组从左往右依次释放按钮
                console.log('scaleDown');
                while (this.collapseData.length > 0 && actualWidth < expectedWidth) {// 如果有折叠按钮且宽度没铺满就释放
                    button = this.collapseData[0];// 提前声明,用于超宽回退
                    let { width, order } = button;
                    if (actualWidth + width > expectedWidth) break;
                    this.collapseData.shift();
                    this.expandedData.splice(order, 0, button);// 释放后要对展开数组重新排下序
                    actualWidth += width;
                }
            }
            this.devicePixelRatioStack.push(devicePixelRatio);
            this.actualWidthStack.push(actualWidth);
        }
    },
    created() {
        this.expandedData = this.data.map((item, order) => {
            return {
                alwaysShow: !!(item.children || (item.icon && !item.text)),
                ...item,
                order
            }
        });// 初始化默认展开所有按钮,用于缓存宽度
    },
    mounted() {
        this.render();
        this.onResize();
        this.throttleResizeHandler = throttle(50, this.onResize);// 加了节流,缩放窗口速度过快会有个闪烁的情况,可以酌情考虑加不加
        window.addEventListener('resize', this.throttleResizeHandler)
    },
    beforeDestroy() {
        window.removeEventListener('resize', this.throttleResizeHandler)
    }
}
</script>

<style lang="scss" scoped>
.el-dropdown+.el-button,
.el-dropdown+.el-dropdown,
.el-button+.el-dropdown {
    margin-left: 10px;
}
</style>

PS:如果这篇博客能对您提供一点点的帮助,烦请一键三连,给博主提供更新动力o(* ̄▽ ̄*)ブ。文章来源地址https://www.toymoban.com/news/detail-511355.html

到了这里,关于Element-ui自定义组件:可折叠按钮列表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Element-UI技巧分享:自定义拖拽列表的制作方法

    部分数据来源: ChatGPT 引言         当我们使用 Element-UI 的自定义拖拽列表时,有时候需要根据拖拽的字段位置,将数据组成不同的列表进行显示。这种情况下,我们可以通过一些简单的操作,来实现自定义拖拽列表中的分类展示。 在本文中,我们将介绍如何使用 Eleme

    2024年02月08日
    浏览(31)
  • element-ui中el-table点击其他自定义按钮展开table中某一行

    在日常开发中,我们遇见了会有点击某些按钮,使得表格行展开的需求,这时候去查看文档 element-ui(table) 这里官方提供了示例为在行最左侧有一个展开合并icon,但是点其他地方不能展开,我们又想点其他地方而展开 我们看见是在table-column上定了type为expand,而该列里面的

    2024年01月18日
    浏览(35)
  • Vue Element UI 自定义描述列表组件

    效果图  写在前面 由于vue使用的版本太低,vue element UI 的描述列表不生效,但是有时候又不想换版本的可以自定义一个描述列表。 实现哪些功能 1、每行的高度根据改行中某一列的最大高度自动撑开 2、 列宽度自动补全,避免最后一列出现残缺的情况 3、支持纯文本与HTML插槽

    2024年02月03日
    浏览(62)
  • element-ui折叠面板怎么修改样式

    修改前 因为组件封装,要使用样式穿透来修改… 注意需要把需要样式穿透的类单独拿出来,不能包裹在scss格式的类里了 修改后 展开… 最后这个缝隙可以用边框来填补,至于伪类加横杠最好不要用,因为展开和折叠的时候会出现一瞬间白线

    2024年02月12日
    浏览(51)
  • vue项目element-ui上传组件自定义方法无法获取进度

    原因:element-ui中的up-load组件使用时,若用自定义上传http-request,会重新申明XMLHttpRequest,on-progress里的申明则被覆盖,无法使用该钩子,无法添加进度条 方案一:假进度条;写个假进度条优化用户体验,使用el-upload组件里on-change方法的status状态,配合定时器 ,以vue3为例 ,

    2024年02月15日
    浏览(50)
  • element-ui table-自定义表格某列的表头样式或者功能

    自带表格 自定义表格某列的表头样式或者功能

    2024年02月03日
    浏览(49)
  • element UI中table操作栏更多按钮展示与折叠的实现

    1、然后给大家看下动态图 2、解决思路: ​ 一开始我的第一反应就是可以手写一个定位布局来点击弹出更多按钮弹框,后面看了下elementUI文档上有个组件可以利用,那就是 Popover 弹出框 废话不多说,直接上代码吧 html代码块: 最后做出来的效果图如下:

    2024年02月11日
    浏览(39)
  • 如何实现element UI中table操作栏更多按钮的展示与折叠?

    解决思路: ​ 直接使用elementUI文档上 Popover 弹出框组件 废话不多说,直接上代码吧 el-table :data=\\\"locationList\\\" v-loading=\\\"loading\\\" border class=\\\"table\\\" ref=\\\"multipleTable\\\" @selection-change=\\\"handleSelectionChange\\\"     el-table-column type=\\\"selection\\\" width=\\\"55\\\"/el-table-column     el-table-column v-for=\\\"(item,index) in ta

    2024年02月11日
    浏览(30)
  • Element Ui 树形组件自定义样式与功能

    一、功能描述:可实现树节点内容修改、增加节点、删除节点等,根据层级不同显示不同的图标等,已封装成组件。 二、调用组件示例: 三、效果图如下:

    2024年02月15日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包