Element Table 可拖动改变列宽度的实现

3,837 阅读1分钟

Element Table组件的可拖动改变列宽度需要当table为带边框表格时并且resizable属性为true时可以拖动改变列宽度,具体实现在table-header组件中 column.gif

鼠标在相对应的列移动时

handleMouseMove(event, column) {
      if (column.children && column.children.length > 0) return;
      let target = event.target;
      // 找到事件对象th
      while (target && target.tagName !== "TH") {
        target = target.parentNode;
      }
      if (!column || !column.resizable) return;
      // border为真且列没有在移动时
      if (!this.dragging && this.border) {
        // 获取事件对象的DOMRect对象
        let rect = target.getBoundingClientRect();
        const bodyStyle = document.body.style;
        // 边界条件 满足这个条件时 添加上‘col-resize’样式
        if (rect.width > 12 && rect.right - event.pageX < 8) {
          bodyStyle.cursor = "col-resize";
          if (hasClass(target, "is-sortable")) {
            target.style.cursor = "col-resize";
          }
          // 将正在移动的列赋值给draggingColumn
          this.draggingColumn = column;
        } else if (!this.dragging) {
          bodyStyle.cursor = "";
          if (hasClass(target, "is-sortable")) {
            target.style.cursor = "pointer";
          }
          this.draggingColumn = null;
        }
      }
    },

这一段代码主要做的就是当鼠标移动到满足条件时,将光标样式改为可移动光标样式,再将当前列赋值给draggingColumn

鼠标按下时

handleMouseDown(event, column) {
      if (column.children && column.children.length > 0) return;
      if (this.draggingColumn && this.border) {
        this.dragging = true;
        // 控制table组件ref为resizeProxy的显示 当我们按下鼠标拖动时跟着走的那根线
        this.$parent.resizeProxyVisible = true;
        const table = this.$parent;
        const tableEl = table.$el;
        const tableLeft = tableEl.getBoundingClientRect().left;
        const columnEl = this.$el.querySelector(`th.${column.id}`);
        const columnRect = columnEl.getBoundingClientRect();
        const minLeft = columnRect.left - tableLeft + 30;

        addClass(columnEl, "noclick");
        // 拖拽时的状态,用于后面的计算
        this.dragState = {
          startMouseLeft: event.clientX,
          startLeft: columnRect.right - tableLeft,
          startColumnLeft: columnRect.left - tableLeft,
          tableLeft,
        };
        const resizeProxy = table.$refs.resizeProxy;
        // resizeProxy是使用绝对定位 赋值给left
        resizeProxy.style.left = this.dragState.startLeft + "px";

        const handleMouseMove = (event) => {
          const deltaLeft = event.clientX - this.dragState.startMouseLeft;
          const proxyLeft = this.dragState.startLeft + deltaLeft;
          // resizeProxy最终的位置
          resizeProxy.style.left = Math.max(minLeft, proxyLeft) + "px";
        };
        const handleMouseUp = () => {
          if (this.dragging) {
            const { startColumnLeft, startLeft } = this.dragState;
            const finalLeft = parseInt(resizeProxy.style.left, 10);
            const columnWidth = finalLeft - startColumnLeft;
            // column最终的宽度
            column.width = column.realWidth = columnWidth;

            this.store.scheduleLayout();
            document.body.style.cursor = "";
            this.dragging = false;
            this.draggingColumn = null;
            this.dragState = {};

            table.resizeProxyVisible = false;
          }
          document.removeEventListener("mousemove", handleMouseMove);
          document.removeEventListener("mouseup", handleMouseUp);

          setTimeout(function () {
            removeClass(columnEl, "noclick");
          }, 0);
        };
        document.addEventListener("mousemove", handleMouseMove);
        document.addEventListener("mouseup", handleMouseUp);
      }

当鼠标按下移动时,监听mousemove事件,执行handleMouseMove函数,在鼠标松开时将计算后的width赋值给列的宽度,最终在DOM宽度上的改变是在store上的scheduleLayout函数

onColumnsChange

onColumnsChange(layout) {
      const cols = this.$el.querySelectorAll("colgroup > col");
      if (!cols.length) return;
      const flattenColumns = layout.getFlattenColumns();
      const columnsMap = {};
      flattenColumns.forEach((column) => {
        columnsMap[column.id] = column;
      });
      for (let i = 0, j = cols.length; i < j; i++) {
        const col = cols[i];
        const name = col.getAttribute("name");
        const column = columnsMap[name];
        if (column) {
          col.setAttribute("width", column.realWidth || column.width);
        }
      }
    },

这段代码好理解,获取到列后使用setAttribute设置列最终的宽度,至此列宽度更新完成