1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Ant Design of Vue 表格使用 vue-draggable-resizable 封装表头问题汇总

Ant Design of Vue 表格使用 vue-draggable-resizable 封装表头问题汇总

时间:2019-02-26 23:09:49

相关推荐

Ant Design of Vue 表格使用 vue-draggable-resizable 封装表头问题汇总

背景

项目需要表格支持拉伸每列的宽度,查看了文档,官方建议是用vue-draggable-resizable插件结合 components 属性,给表头 header 增加一个可拖拽的功能。其实下面的前三个问题都很好解决,网上也有很多解决方案,但因为考虑到项目中表格很多,而且每个表格都要支持拉伸列宽度,所以不可能按官方文档那样,每个表格的 vue 组件都写重复的代码,所以就封装了一个方法,专门获取可拖拽的 header 的components。

问题

1、使用 ResizeableTitle 可能会出现命名冲突

报错:Module parse failed: Argument name clash

解决方式:

把 ResizeableTitle 改成 resizeableTitle 即可,有的时候会出现这个问题,有的时候没有。

2、添加表格选择框后报错

其实主要是官方文档这处代码(如下图)的问题

那是因为在 antd 的文档中,colums 是没有选择框的,那函数在遍历执行每列时,遇到选择框那一列就找不到对应的 col,col 会为 undefined ,所以必须得对选择框做一个判断处理,可以直接返回 th,不用执行后面的操作。

解决:

3、表头的一些按钮样式消失,像下图中的滑过样式和点击样式消失,是因为新的th没有继承原来的th的类名。

如下图官方文档的代码,我们可以看到返回的 th ,要么是没有类名,要么是只有 resize-table-th ,这就很敷衍。

解决:

其实原有元素的类名,我们可以从函数返回的参数中获取

打印的形参如下图:

4、表头里的排序功能失效,无法点击进行排序功能

上诉背景已说过,我已经把这个获取拖拽的方法封装成一个全局的方法。而出现这个问题的直接原因也正因如此,我把resizeableTitle封装成一个全局的getTableDragHeader方法,并放在一个 js 文件里面,而不是像官方文档写在调用表格组件的 vue 页面里面。因为不封装的话,那所有需要拖拽的表格都要重复写这些代码,这就很不方便,所以我就把它写成了一个全局的方法,供所有带有表格的页面使用。

除此之外我还发现了一个问题,在我封装的全局拖拽方法里,如果我把resizeableTitle改成其它名字,那么调用的方式就会不一样,原本resizeableTitle命名的函数只传一个参数过来,如下图:

打印 a 形参:

只写一个 a 形参就能拿到 props、children、data。

但改成其它任意名字的函数名,就传三个参数过来,官方文档的写法就是传三个参数的。

打印如下图,第一个参数是个function,第二个参数是props,第三个参数是children:

这就很莫名其妙,只是函数名字不一样就有这么大的区别。

原本使用resizeableTitle封装成全局的方法去调用获取可拖拽headers 的colums 是不会报错,只是排序功能无法点击。但当我改resizeableTitle的名字为resizeable后,就是变成和官方文档有三个参数的情况时,控制台就报错:Error in render: “ReferenceError: h is not defined”,如下图:

百度了下这个错误,其他人遇到这个问题,它们的解释是表格 colums 没有放在 data 函数里面,获取不到上下文,这是什么鬼模糊的解释。。。

这和我现在的情况关联起来,的确也和上下文有关,毕竟我封装成全局的 js 方法,也就是方法不是特属某一个 vue 实例里的,而是全局的 js 方法。

所以我还是先把方法改成局部的,再对比看看到底所谓的上下文到底是什么?

按 antd 文档那种形式,我把方法写回在 vue 页面里面,getTableDragHeader就是我封装成一个能根据 colums 返回拖拽header的方法,代码如下图:

然后去看,结果是不会出现排序功能失效的问题。那也就是说不是因为表头被 components 返回的 th 模板替换导致的事件失效,vue-draggable-resizable+components的拖拽方法是没问题的。

所以问题可能出现在方法的调用和声明上了。

经过很多次的尝试,考虑过this的指向、引入的vue实例对象方式,但似乎都没关系,重点好像是方法必须在组件里声明,必须得要在调用的组件实例下。那这样还怎么封装成全局的方法供所有组件使用?不是只能在一个一个组件里面写了吗?

我回过头,重新点开报错地方,直接查看他的源码:

好像是 TableHeaderRow 是提供 render 给 vue 来渲染函数 return template 的方式,其中的 h 就是创建元素的方法。

综上所述,按我的理解,其实就是 return 返回的模板,如下图:

必须得要在 vue 实例里面写,而不是单纯的写在一个 js 文件,然后才在 vue 里面调用。百度时,网友说这种报错的解决方式是 colums 写在 data 里面,也无非是 它们的 colums 带有customRender这种需要渲染模板的选项而已。所以类似我封装的getTableDragHeader方法,这种带有 template 模板返回的方法,是不能在 vue 实例外声明的,必须得在某一个 vue 实例中声明。所以官方文档中在 vue 实例外声明,如下图,也是有问题的。

官方控制台不报错,是因为函数名是ResizeableTitle,改成其它函数名会立即报错,而且还是会出现上述的 h is not defined 报错,因为返回模板的方法没有写在 vue 实例里面。

解决:

既然知道必须得将方法写在任意 vue 实例里面,但是我又不想一个一个声明,所以我得要有一个全局的 vue 实例可供所有 vue 页面调用。这就想到了 $root 根组件 和 $Bus 。因为不想让 $root 变得复杂和冗乱,所以就创建了一个 $Bus 公共 vue 实例,用于提供给全部组件调用和通讯。

代码如下:

import Vue from 'vue';import VueDraggableResizable from 'vue-draggable-resizable';ponent('vue-draggable-resizable', VueDraggableResizable);const Bus = new Vue({methods:{getTableDragHeader(columns) {const draggingMap = {};columns.forEach((col) => {draggingMap[col.key] = col.width;});const draggingState = Vue.observable(draggingMap);const resizeable = (a,b,c) => {let thDom = null;const props = b;const children = c;const {key, ...restProps } = props;let col = null;if (key === "selection-column") {// 当前column为全选的时候,要返回全选的 th ,否则无法出现全选return <th {...restProps} class={props.class}>{children}</th>} else {col = columns.find((item) => {const k = item.dataIndex || item.key;return k === key;});}if (!col.width) {return <th {...restProps} class={props.class}>{children}</th>;}const onDrag = (x) => {draggingState[key] = 0;const maxWidth = thDom.parentNode.offsetWidth/2 || 100; // 拖拽最长长度不能超过表格的一半col.width = Math.min(Math.max(x, 50), maxWidth);};const onDragstop = () => {draggingState[key] = thDom.getBoundingClientRect().width;};return (<th {...restProps} v-ant-ref={(r) => (thDom = r)} width={col.width} class={props.class+" resize-table-th"}>{children}<vue-draggable-resizablekey={col.key}class="table-draggable-handle"w={10}x={draggingState[key] || col.width}z={1}axis="x"draggable={true}resizable={false}onDragging={onDrag}onDragstop={onDragstop}></vue-draggable-resizable></th>);};return {header: {cell: resizeable ,},}}}})export default Bus;

我将getTableDragHeader方法写在了 Bus 这个 vue 实例下面。

最后我在 main.js 中,引入上诉导出的 Bus 并挂载在 Vue 实例下成为 $Bus,其实 $Bus.getTableDragHeader就可以调用获取可拖拽的 header colums,但因为之前封装的方法 $YGetTableDragHeader,代码很多地方已经用 $YGetTableDragHeader的形式调用获取了,所以只能把 $Bus.getTableDragHeader这个新方法再重新赋值给它,那其它地方的代码就不用改了。

终言

antd 表格的 components 文档可查资料很少,表格列之间的伸缩文档也只是给了一个小例子,而且这个例子的问题很多,在我看来本身就是有问题的。希望这篇文章可以提供给大家作一个参考并解决遇到的问题,若使用 vue-draggable-resizable 有其它问题,可给我留言,我看到也会帮忙解决的。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。