功能点
1、工具栏的功能按钮要超宽不换行,宽度不够折叠进”更多“按钮;
2、下拉菜单按钮和纯图标按钮默认不折叠;
3、折叠前后按钮组顺序保持不变。
实现思路
1、默认展开全量按钮,并对其宽度进行缓存;
2、循环计算展开按钮的总宽度 与 容器宽度 的差值,并进行按钮的折叠与释放处理;
3、监听窗口大小改变,不断进行步骤2;
4、难点:区分放大、缩小操作进行分别处理(当然也可以每次遍历全量按钮,这样只要考虑需要折叠的情况,这个比较简单,这里不展开说明)。
演示效果
文章来源:https://www.toymoban.com/news/detail-511355.html
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模板网!