Vue2 + ant design vue1.7.8版本 Table组件 手动拖拽、可伸缩列功能

低版本的antd table组件 官网文档上有可伸缩列功能,但是用起来有很明显的bug,无法直接拿来用;接下来给大家提供一个手写的拖拽方案:

1.首先,写好v-table组件,这里最重要的就是table组件中的‘components’属性,官方对component的描述是"覆盖默认的 table 元素",意思就是对table进行重写


2.然后用计算属性去定义这个components,这里的“ResizeHeader”就是重写table组件的一个方法,这里面还引入了‘customHeaderCell, ResizeColumnProvide’这两个方法后面会介绍

import ResizeHeader, { customHeaderCell, ResizeColumnProvide } from './header'
computed: {
        components() {
            return {
                header: {
                    cell: ResizeHeader,
                },
            }
        },
}
mixins: [ResizeColumnProvide],

3.我们创建一个header.js 文件  用来放ResizeHeader方法

//重构table组件的核心渲染函数
const ResizeHeader = (h, props, children) => {
  const { key, column, ...restProps } = props;
  let content = [].concat(children);
  if (column?.resizable) {
    const handlerVNode = h(DragHandler, {
      props: {
        width: column.width,
        minWidth: Math.min(column.width, 100),
        column: column
      }
    }, []);
    content = content.concat(
      handlerVNode
    );
  }

  return h("th", {
    key,
    props: { column },
    ...Object.assign({}, restProps, { class: (restProps.class || "") + " nio-header-th" })
  }, content);
};

4.ResizeHeader方法里面“DragHandler”组件是对table组件重写后的一些拖拽操作的事件监听处理

const events = {
  start: "mousedown",
  move: "mousemove",
  stop: "mouseup",
};
//对table组件进行重构的核心方法组件
const DragHandler = {
  name: "nio-drag-handler",
  //获取混入的方法(重新设置表头宽度)
  inject: {
    onResizeColumn: {
      default: noop
    }
  },
  props: {
    column: { type: Object, required: true },
    minWidth: { type: Number, required: true }
  },

  data() {
    return {
      dragging: false,
      thWidth: 0,// 列头的宽度,用于计算
      startX: 0,
      moveEvent: {
        remove: noop
      },
      stopEvent: {
        remove: noop
      }
    };
  },
  //销毁监听
  beforeDestroy() {
    this.removeEvents();
  },
  methods: {
    //鼠标开始拖拽
    handleMouseDown(e) {
      e.stopPropagation();
      e.preventDefault();
      this.handleStart(e);
    },

    handleStart(e) {
      this.dragging = true;
      this.removeEvents();
      this.thWidth = this.$el.parentNode.getBoundingClientRect().width;
      if (e instanceof MouseEvent && e.which !== 1) {
        return;
      }
      if (e.stopPropagation) e.stopPropagation();
      this.startX = e.pageX;
      this.moveEvent = addEventListenerWrap(document.documentElement, events.move, this.handleMove);
      this.stopEvent = addEventListenerWrap(document.documentElement, events.stop, this.handleStop);
    },

    handleMove(e) {
      this.updateWidth(e);
    },
    handleStop(e) {
      this.dragging = false;
      this.updateWidth(e);
      this.removeEvents();
    },

    updateWidth(e) {
      let pageX = e.pageX;
      const tmpDeltaX = this.startX - pageX;
      let w = Math.max(this.thWidth - tmpDeltaX, this.minWidth);
      w = Math.min(w, Infinity);
      this.onResizeColumn(w, this.column);
    },

    removeEvents() {
      this.moveEvent.remove();
      this.stopEvent.remove();
    }
  },
  //渲染div 绑定拖拽
  render(h) {
    const line = h("div", { class: "nio-drag-handler-line" }, []);
    return h("div", { class: "nio-drag-handler", on: { mousedown: this.handleMouseDown } }, [line]);
  }
};

//监听鼠标移动的方法
function addEventListenerWrap(target, eventType, cb) {
  if (target && target.addEventListener) {
    target.addEventListener(eventType, cb);
  }

  return {
    remove: () => {
      if (target && target.removeEventListener) {
        target.removeEventListener(eventType, cb);
      }
    },
  };
}

5.DragHandler组件中 最重要的方法是“onResizeColumn”方法,废话不多说 上代码

重点:ResizeColumnProvide 方法要以mixins混入的方式 引入到table组件的页面中,这样“DragHandler”就能用到this.onResizeColumn()方法,对列的宽度进行改变

//在table组件中混入的一个方法 provide => onResizeColumn 
//目的是在table重写中(DragHandler方法)
//可以用到构造的onResizeColumn方法(重新设置表头的宽度)
export const ResizeColumnProvide = {
  name: "ResizeColumnProvideMixin",
  provide() {
    return {
      onResizeColumn: this.onResizeColumn
    };
  },

  data() {
    const onResizeColumn = throttle2(this._resizeColumn, 30);
    return {
      columnsKey: "columns", //这个属性是table组件表头的集合名称 :columns="columns"
      moving:false, //这个属性是为了在拖拽时 阻止表头触发排序的功能
      onResizeColumn
    };
  },

  methods: {
    _resizeColumn(w, column) {
      const columns = this[this.columnsKey];
      this.moving = true
      let time = setTimeout(() => {
        this.moving = false
        clearTimeout(time)
      }, 2000);
      //在拖拽时找到对应表头 重新设置表头的宽度   
      //这就是为什么表头column要绑定customHeaderCell的原因           
      const matchedIndex = columns.findIndex(item => item.key === column.key);
      columns[matchedIndex].width = w;
    }
  }
};

6.上面的方法中的‘throttle2’是对拖拽的一个节流操作

//拖拽的节流方法
const throttle2 = (fn, delay) => {
  let valid = true;
  return (...args) => {
    if (!valid) {
      return;
    }
    valid = false;
    setTimeout(() => {
      fn(...args);
      valid = true;
    }, delay);
  };
};

7.在第一步中 table组件 ,这里用到这个方法 是因为antd的table组件在用components属性进行重写时,无法获取到column(对应的表头数据),但是在拖拽的操作中,又需要拿到对应的表头数据,这里只能用table内置的customHeaderCell方法,在生成表头数据的时候,给每个表头数据绑定customHeaderCell方法

//table组件内置的一个方法
export function customHeaderCell(column) {
  return Object.assign({
    column: column
  });
}

//给column绑定   不绑定的话ResizeHeader方法中的参数props无法获取到column信息  
//这里的list 就是表头的集合
this.columns = list.map((item) => Object.assign({}, item, { customHeaderCell }))

8.最后附加上css样式吧

.nio-header-th {
  position: relative;
  isolation: isolate;
}

.nio-drag-handler {
  position: absolute;
  right: 0;
  top: 0;
  height: 100%;
  width: 2px;
  z-index: 1;
  border: 1px dashed #eee;

  &:hover,
  &:focus {
    cursor: col-resize;
    border: 1px dashed #0bd9d9;
  }
}

你可能感兴趣的:(前端,vue.js,html,javascript,anti-design-vue)