element-ui table 实现客户端自定义列的显示隐藏


前言

提示:功能实现过了好久了,突然想起来一直没做个总结。好记性不如烂笔头,gogogo!

使用 element-ui 的 <el-table> 来显示系统中各种各样的数据,感觉美滋滋啊,方便又美丽。直到那天,来了个需求:给表格添加上数据列自定义显示/隐藏的功能。开始研究文档…


提示:以下是本篇文章正文内容,篇幅有限,出现的代码仅为关键部分的代码。

一、最终实现的效果图

先上个最终实现的效果图吧!

在表头上鼠标右键,就能出现自定义列的操作菜单:
操作菜单
取消列的选中,就能隐藏对应的列:
隐藏列
列的显示隐藏信息被保存在浏览器的 localStorage 中了,只要不清缓存,下次就能沿用之前的列的显示/隐藏的设置。

二、实现步骤

1. 列设置数据定义

// 实现动态表格列数据
// colOptions,colSelect中内容的顺序必须与表格中表头的内容顺序保持一致
// colOptions: 选中的列/显示的列
 colOptions: parseArr("colOptions_inActs")
   ? parseArr("colOptions_inActs")
   : [
       "入库单号",
       "入库批次",
       "料箱号",
       "货主代码",
       "货主名称",
       "厂商代码",
       "厂商名称",
       "状态",
       "入库类型",
       "入库员",
       "入库时间"
     ],
 // 完整列数据,用于生成菜单中的选项
 colSelect: [
   "入库单号",
   "入库批次",
   "料箱号",
   "货主代码",
   "货主名称",
   "厂商代码",
   "厂商名称",
   "状态",
   "入库类型",
   "入库员",
   "入库时间"
 ],
 // 
 colData: parseObjArr("colData_inActs")
   ? parseObjArr("colData_inActs")
   : [
       { title: "入库单号", istrue: true },
       { title: "入库批次", istrue: true },
       { title: "料箱号", istrue: true },
       { title: "货主代码", istrue: true },
       { title: "货主名称", istrue: true },
       { title: "厂商代码", istrue: true },
       { title: "厂商名称", istrue: true },
       { title: "状态", istrue: true },
       { title: "入库类型", istrue: true },
       { title: "入库员", istrue: true },
       { title: "入库时间", istrue: true }
     ],
 // 标识设置菜单的显示与否
 menuVisible: false,
 // 设置菜单的显示位置与鼠标右键位置的偏移距离
 top: 0,
 left: 0,

2. 本地数据存取方法封装

为了便于与 localStorage 交互,封装了 4 个工具方法,代码也比较简单,就直接看下面的代码吧!

// 该文件中存放关于 json 的全局方法

/** 
 * 将对象数组存放到 localStorage 中
 * @param {string} key: String 数据键名
 * @param {Array[object]} data[]: [object] 原始数组数据
 * @returns null
*/
export function stringifyObjArr(key, data) {
	if (!Array.isArray(data)) {
		return false
	} else {
		let formatData = []
		for (let i = 0; i < data.length; i++) {
			const element = data[i]
			formatData.push(JSON.stringify(element))
		}
		localStorage.setItem(key, JSON.stringify(formatData))
	}
}

/** 
 * 将数据从 localStorage 取出并转化为对象数组格式
 * @param {string} key: String 数据键名
 * @returns {Array[number|string]} data[]: [object] 数组数据
*/
export function parseObjArr(key) {
	let rawData = JSON.parse(localStorage.getItem(key))
	if (!rawData || !Array.isArray(rawData)) {
		return false
	} else {
		let data = []
		for (let i = 0; i < rawData.length; i++) {
			const element = rawData[i]
			data.push(JSON.parse(element))
		}
		return data
	}
}

/** 
 * 数组转化为字符串 '[1, 2]' 后放入 localStorage
 * @param {string} key: 数据键名
 * @param {Array} data: 数组数据
 * @returns null
 */
export function stringifyArr(key, data) {
	if (!data) {
		return false
	} else {
		localStorage.setItem(key, JSON.stringify(data))
	}
}

/**
 * 从 localStorage 中拿出 '[1, 2]' 并格式化为数组
 * @param {string} key: 数据键名 
 * @returns {Array} : data[] 数组数据
 */
export function parseArr(key) {
	return JSON.parse(localStorage.getItem(key))
}

2. 给表格添加右键出现的菜单

这使用了 el-table 的 header-contextmenu 事件
header-contextmenu
关键代码如下:

<template>
	<el-table 
	 ......
	 ref="table"
	 @header-contextmenu="contextmenu"
	>
	......
	</el-table>

	<!-- colOptions:右键设置菜单内容 -->
    <div
      v-show="menuVisible"
      :style="{top:top+ &quot;px&quot;,left:left+ &quot;px&quot;}"
      class="select-menu"
    >
      <p>定义显示的表格列</p>
      <el-checkbox-group v-model="colOptions" :min="1">
        <el-checkbox v-for="item in colSelect" :key="item" :label="item" class="custom-checkbox" />
      </el-checkbox-group>
    </div>
</template>

<script>
	// 控制表格列数据的动态显示
    contextmenu(row, event) {
      this.menuVisible = false; // 先把右键菜单关死
      this.menuVisible = true; // 显示自定义菜单
      window.event.returnValue = false; // 取消浏览器右击默认事件
      document.addEventListener("click", this.closeMenu);
      // 获取鼠标点坐标,设置右击菜单位置
      this.top = event.clientY;
      this.left = event.clientX;
    },
    // 关闭设置菜单
    closeMenu() {
      this.menuVisible = false; // 关闭右键菜单
      document.removeEventListener("click", this.closeMenu); // 取消监听事件
    },
</script>

3. 列设置数据与表格列进行关联

在表格列上,使用 v-if 来控制列的显示/隐藏(用 v-show 是不起作用的,原因:display: table-cell 的优先级高于 display: none)。关键代码如下:

<el-table 
	......
	 ref="table"
	@header-contextmenu="contextmenu"
>
	<el-table-column
	  prop="storageNo"
	  v-if="colData[0].istrue"
	  label="入库单号"
	  width="280"
	  :show-overflow-tooltip="true"
	/>
	<el-table-column
	  prop="batchNo"
	  v-if="colData[1].istrue"
	  label="入库批次"
	  width="120"
	  :show-overflow-tooltip="true"
	/>
	......
</el-table>

监听 colOptions 数据,当设置数据变化时,更新与表格列关联的 colData 数据,并将更新后的设置数据保存到 localStorage 中。

watch: {
    // 动态计算要显示的数据列
    colOptions(valArr) {
      var arr = this.colSelect.filter(i => valArr.indexOf(i) < 0); // 未选中
      this.colData.filter(i => {
        if (arr.indexOf(i.title) !== -1) {
          i.istrue = false;
        } else {
          i.istrue = true;
        }
      });
      // 本地保存数据列显示与否的设置数据
      stringifyObjArr("colData_inActs", this.colData);
      stringifyArr("colOptions_inActs", valArr);
    }
  },

这里要特别注意,列设置数据与实际列的顺序一定要保持一致。

4. 其它问题

到步骤 3,其实已经基本完成了客户端自定义列的显示隐藏功能了,但是,在实际使用中,还存在着一些问题。下面是我遇到的问题以及找到的解决办法。

1. 列由隐藏,设置为显示后,表格抖动、样式错乱?

设置列显示/隐藏,的确实现了,可是,发现了新的问题:表头抖动的厉害,表格出现了样式错乱的情况,如下图:
操作后,表格样式错乱
在官方文档中,找到了解决方案:
doLayout方法
在表格更新前,使用 doLayout 方法 来对表格进行重新布局,解决样式错乱的问题,关键代码如下:

beforeUpdate() {
    // 重新布局表格
    this.$nextTick(() => {
      this.$refs.table.doLayout();
    });
  },

总结

最后,简单总结下吧:

  1. 准备好列设置数据,注意顺序一致;
  2. 使用表格的 header-contextmenu 方法,调出右键菜单;
  3. 使用 v-if 控制表格列的显示/隐藏;
  4. 使用监听属性,及时更新设置数据;
  5. 使用 doLayout 方法 在更新表格前,重新布局表格;
  6. 结合 localStorage 来实现列设置数据的持久化(如果有需要,可以改为后端保存列设置数据,这样就不用担心清了缓存了!)
  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值