踩坑日记:在ant-design-vue的table组件中集成vue-draggable-resizable实现可伸缩列遇到的坑


标题有点长,就如这踩坑的时间,那么就进入这次的踩坑日记。在Vue的Table组件中,实现可伸缩列,如果你使用的是Element-Ui那么这是一个现成的功能,如果你使用的是ant-design-vue,那么是需要集成一个vue-draggable-resizable插件的。详细使用这里不用多说,我想大多说开发者是会先把文档中的用法跑一下的,这期的坑就是,这个Demo跑不起来!其中有这样几点肯,我来记录一下。

  • Module parse failed: Argument name clash...
  • 表格没有拖动滑块
  • 拖动时不够流畅且宽度乱跳
  • 一些细节处理

下面就来一一解答一下问题。我们先将官方文档的代码拷贝下来





  • 如果你按照上面的文档操作了,那么你第一个一定会遇到

Module parse failed: Argument name clash

其意思是命名发生了冲突,我们有两种方案解决这个问题,

  1. propschildren提出来,并给h变量起一个别名。大概代码如下,当然取值的时候,你也可以使用ES6解构。
const ResizeableTitle = (h1) => {
  const props = h1.props
  const children = h1.children
  1. ResizeableTitle 改成 resizeableTitle(我也不知道这个是为啥,但确实解决了)

解决完上面问题后,项目可以正常加载出来了,但是你会发现

  • 列并不能拖动,鼠标划上去都没有可以拖动的反应。那么你可以这样修改Css
.resize-table-th {
    position: relative;
    .table-draggable-handle {
      transform: none !important;
      position: absolute;
      height: 100% !important;
      bottom: 0;
      left: auto !important;
      right: -5px;
      cursor: col-resize;
      touch-action: none;
    }
}

主要有两点.table-draggable-handle 里加上 transform: none !important; position: absolute;去掉style的scoped属性;这时拖动滑块也顺利出来了

  • 于是你就试着去滑动。你会发现滑动的并不像官网那样流程,甚至有一些莫名的跳动,大概描述为,向外拖的时候,拖动的点离鼠标距离很远,并且拖动的幅度很大,向内拖动的时候,列宽会忽然变宽一下,然后再变变成幅度很大的拖动,这显然是不符合我们的需求的,我们也要像官网的demo那样流畅。于是我开始读他的代码,大致找出以下毛病
  1. columns的Item中不一定都是key,(大部分不是key)
columns.forEach(col => {
  draggingMap[col.key] = col.width;
});

原代码如上,意思是遍历columns,取其每一项的宽度为值,每一项的key为键名,生成对象draggingMap,而实际开发中,columns的每一项中dataIndexkey一般是二选一的,虽然也有共存的情况(没必要也不推荐),但大多数还是二选一,就像原代码中的columns那样,他定义的都是dataIndex,而它代码中却取key,这显然是不严谨的。我们需要把代码改成这样

    columns.forEach((col) => {
      const k = col.dataIndex || col.key;
      draggingMap[k] = col.width;
    });

这样才能生成完整的draggingMap对象,当然,这个对代码功能没有实质性影响,因为后续对draggingMap的取值中,并不是操作每一个值,而是靠set的方式设置值,所以技术这个对象是一个残缺的,但不影响,我想着也导致作者直接没注意到这里写错了吧。至于何种残缺,自己打断点看,大概就是{undefined: 90, index: 80}类似这样的对象。接着往下看

  1. 宽度设置有误
      const onDrag = (x) => {
        draggingState[key] = 0;
        col.width = Math.max(x, 1);
      };
      const onDragstop = () => {
        draggingState[key] = thDom.getBoundingClientRect().width;
      };

上面代码的意思是,拖动的时候,所拖动的那一列在draggingState对象中的值,先置为0,然后将当前的columns当前操作项的width值设为当前鼠标的位置。拖拽完毕后,再将所拖动的那一列在draggingState对象中的值,置为当前列的实际宽度。
于是关于col.widthdraggingState[key]的来历和含义都明白了,其中,我们在初始化的时候,取col.width生成了draggingState[key]。这个时候col.width正是我们在设置columns时意为设置列宽的值,但是拖动过后,col.width表示成了拖动时鼠标的终止位置,而这个位置不能再代表宽度了,这时候draggingState[key]还是columns中宽度的映射,他始终是没有变化的,所以我们应该将draggingState[key]当成列宽来使用。所以做下面的改动

         { thDom = r; }}
          width={draggingState[key]}
          class="resize-table-th"
        >

在弄明白两个变量的含义后,关于vue-draggable-resizable组件中的x属性设置,查看了github的文档后,发现,这个参数意思为开始拖动时的很初始位置,而这个值正和col.width的含义类似,他是上次拖动的终止位置,将x设为col.width,应该就可以保证每次拖动的时候,初始位置是上次拖动的终止位置了。而现在的代码中是

x={draggingState[key] || col.width}

也就是初始位置为列宽,如果没有列宽那就取上次的位置,所以这就导致了每次拖动的时候,列宽都要跳动这样,因为他先取了实际宽度,随后draggingState[key]变成0,才又取的col.width,这一块说了半天有点绕哈,反正,他写反了,你要改成x={ col.width || draggingState[key] }才对。
然后

这段代码也是需要改成这样,同上面的第一个点,你不能保证columns中每一项都有key,反而大多数是dataIndex这一问题。
经过上面一番review和分析后,我的表格终于可以像官网那样丝滑了。


下面是我的核心代码,对于某些变量我做了修改,但这一定不会影响聪明的你。我是在混入中写的这些逻辑,你可以自由参照,理性搬运。

    const draggingMap = {};
    this.columns.forEach((col) => {
      const k = col.dataIndex || col.key;
      draggingMap[k] = col.width;
    });
    const draggingState = Vue.observable(draggingMap);
    const resizeableTitle = (h, props, children) => {
      let thDom = null;
      const { key, ...restProps } = props;
      const col = this.columns.find((item) => {
        const k = item.dataIndex || item.key;
        return k === key;
      });
      if (!col.width) {
        return {children};
      }
      const onDrag = (x) => {
        draggingState[key] = 0;
        col.width = Math.max(x, 1);
      };
      const onDragstop = () => {
        draggingState[key] = thDom.getBoundingClientRect().width;
      };
      return (
         { thDom = r; }}
          width={draggingState[key]}
          class="resize-table-th"
        >
          {children}
          
        
      );
    };
    this.components = {
      header: {
        cell: resizeableTitle,
      },
    };
  },

// less代码如下。
.resize-table-th {
    position: relative;
    .table-draggable-handle {
      transform: none !important;
      position: absolute;
      height: 100% !important;
      bottom: 0;
      left: auto !important;
      right: -5px;
      cursor: col-resize;
      touch-action: none;
    }
}

上面是我在开发中真实踩到的坑,和真实的分析思路以及上网查的资料。如果有不同意见,请私信我。

补充(加入复选框后报错)

在文章发布后,收到了一些私信,其中有一个问题是,有的同学在表格选择使用复选框后出现问题,这里说明一下,本文章中是对官网demo的修改,官网中 没有封装带复选框的情况,我也就忘了,这里深表歉意。
如果复制上面的代码,加个复选框的情况下,会报下列的错误

Cannot read property 'width' of undefined

这是因为这里这段逻辑导致的

      const { key, ...restProps } = props;
      const col = this.columns.find((item) => {
        const k = item.dataIndex || item.key;
        return k === key;
      });

这段代码的意思是,我们逐个去遍历每一列,然后拿到当前列的对象,并对其做一些属性的操作,而,我们加了复选框后,在遍历的时候就会有一个key为‘selection-column’的列,导致了col为undefined,所以在下面的col.width的逻辑中就报错了。如果要解决此问题,只需要将这个列单独处理即可。

那么你可以这样改造那段逻辑

      const { key, ...restProps } = props;
      let col;
      if (key === 'selection-column') {
        col = {};
      } else {
        col = this.columns.find((item) => {
          const k = item.dataIndex || item.key;
          return k === key;
        });
      }

这就OK了~2021年3月4日更新。

你可能感兴趣的:(踩坑日记:在ant-design-vue的table组件中集成vue-draggable-resizable实现可伸缩列遇到的坑)