功能

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

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

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

实现思路

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

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

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

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

演示效果 

 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"&gt;
        <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;

        &amp;::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 &amp;&amp; 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 &amp;&amp; !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://blog.csdn.net/zhengvipin/article/details/129471305

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_43094.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注