@/utils/utils#exchange JavaScript Examples

The following examples show how to use @/utils/utils#exchange. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: column.js    From el-table-draggable with MIT License 4 votes vote down vote up
config = {
    WRAPPER: ".el-table__header-wrapper thead tr",
    DRAGGABLE: "th",
    /**
     * @param {Map<Element, Vue>} context
     * @param {Vue} elTableInstance
     * @param {number} animation
     * @returns {import('@types/sortablejs').SortableOptions}
     */
    OPTION(context, elTableInstance, animation) {
        let isDragging = false // 正在拖拽
        let columnIsMoving = false // 列正在移动
        // 自动对齐
        function autoAlignmentTableByThList(thList) {
            if (!isDragging) {
                return
            }
            dom.alignmentTableByThList(thList)
            return requestAnimationFrame(() => {
                autoAlignmentTableByThList(thList)
            })
        }

        /** 列宽的虚拟dom */
        let colDomInfoList = []

        return {
            onStart() {
                const thList = Array.from(elTableInstance.$el.querySelector(WRAPPER).childNodes)

                colDomInfoList = thList.map(th => {
                    const col = dom.getColByTh(th)
                    const width = col ? col.getAttribute('width') : th.offsetWidth
                    return {
                        el: col,
                        thEl: th,
                        width: width,
                        originWidth: width
                    }
                })

                // dragging状态自动调用对齐
                isDragging = true
                autoAlignmentTableByThList(thList)
            },
            setData(dataTransfer, dragEl) {
                /**
                 * 在页面上创建一个当前table的wrapper,然后隐藏它,只显示那一列的部分作为拖拽对象
                 * 在下一个事件循环删除dom即可
                 */
                const { offsetLeft, offsetWidth, offsetHeight } = dragEl;
                const tableEl = elTableInstance.$el;

                const wrapper = document.createElement("div"); // 可视区域
                wrapper.style = `position: fixed; z-index: -1;overflow: hidden; width: ${offsetWidth}px`;
                const tableCloneWrapper = document.createElement("div"); // table容器,宽度和位移
                tableCloneWrapper.style = `position: relative; left: -${offsetLeft}px; width: ${tableEl.offsetWidth}px`;
                wrapper.appendChild(tableCloneWrapper);
                tableCloneWrapper.appendChild(tableEl.cloneNode(true));

                // 推进dom,让dataTransfer可以获取
                document.body.appendChild(wrapper);
                // 拖拽位置需要偏移到对应的列上
                dataTransfer.setDragImage(
                    wrapper,
                    (offsetLeft * 2) + (offsetWidth / 2),
                    offsetHeight / 2
                );
                setTimeout(() => {
                    document.body.removeChild(wrapper);
                });
            },
            onMove(evt, originEvent) {
                const { related, dragged, relatedRect, draggedRect } = evt;
                let { willInsertAfter } = evt;

                // 根据用户选择
                const onMove = getOnMove(elTableInstance);
                const onMoveResult = onMove(evt, originEvent)
                switch (onMoveResult) {
                    case 1: {
                        willInsertAfter = true;
                        break;
                    }
                    case -1: {
                        willInsertAfter = false;
                        break;
                    }
                    case false: {
                        return false;
                    }
                    default: {
                        break;
                    }
                }

                /**
                 * 对dom进行操作
                 */
                const thList = willInsertAfter ? [dragged, related] : [related, dragged];
                // 临时修改两个的宽度, 需要在下个循环触发,省的宽度不一致导致因为dom变化再次触发拖拽
                const colList = thList
                    .map(th => colDomInfoList.find(item => item.thEl === th))
                // 交换宽度
                if (colList.length !== 2) {
                    throw new Error('无法找到拖拽的th的信息,请检查是否跨表格拖拽了')
                    return true
                }
                const [fromCol, toCol] = colList
                setTimeout(() => {
                    dom.swapDom(fromCol.el, toCol.el)
                    // 交换colDomInfoList内位置
                    const oldIndex = colDomInfoList.indexOf(fromCol)
                    const newIndex = colDomInfoList.indexOf(toCol)
                    exchange(oldIndex, colDomInfoList, newIndex, colDomInfoList)
                })

                return true;
            },
            onEnd(evt) {
                const PROP = "columns";
                dom.cleanUp();
                // 清除所有临时交换产生的设定和变量
                colDomInfoList.forEach(({ el, originWidth }) => {
                    if (el) {
                        el.setAttribute('width', originWidth)
                    }
                })

                isDragging = false

                const { to, from, pullMode } = evt;
                const toContext = context.get(to);
                let toList = toContext[PROP];
                const fromContext = context.get(from);
                let fromList = fromContext[PROP];
                let { newIndex, oldIndex } = evt;

                // 交换dom位置
                exchange(oldIndex, fromList, newIndex, toList, pullMode);

                // 交换传递下来的column的value
                const fromValue = fromContext.$parent.value || [];
                const toValue = toContext.$parent.value || [];
                if (fromValue.length && toValue.length) {
                    exchange(oldIndex, fromValue, newIndex, toValue, pullMode);
                }

                // 通知对应的实例
                updateElTableInstance(from, to, context, function (tableContext) {
                    const draggableContext = tableContext.$parent;
                    const columns = draggableContext.value
                        ? draggableContext.value
                        : tableContext[PROP].map(({ property }) => ({ property }));
                    draggableContext.$emit("input", columns);
                });
            },
        };
    },
}
Example #2
Source File: row.js    From el-table-draggable with MIT License 4 votes vote down vote up
config = {
  WRAPPER,
  DRAGGABLE,
  /**
   * @param {Map<Element, Vue>} context
   * @param {Vue} elTableInstance
   * @param {number} animation
   * @returns {import('@types/sortablejs').SortableOptions}
   */
  OPTION(context, elTableInstance, animation) {
    const PROP = 'data'
    /**
     * 自动监听重建映射表
     */
    if (elTableInstance[DOM_MAPPING_NAME]) {
      elTableInstance[DOM_MAPPING_NAME].stop();
    }
    const mappingOberver = new MappingOberver(
      elTableInstance,
      WRAPPER,
    );

    elTableInstance[DOM_MAPPING_NAME] = mappingOberver;
    mappingOberver.rebuild();
    mappingOberver.start();
    let dommappingTimer = null
    let isTreeTable = checkIsTreeTable(elTableInstance) // 防止因为数据原因变化,所以每次选择都判断一次

    return {
      onChoose() {
        isTreeTable = checkIsTreeTable(elTableInstance)
        cleanUp()
        /**
         * 开始之前对所有表格做一定处理
         */
        for (const draggableTable of context.values()) {
          const domMapping = draggableTable[DOM_MAPPING_NAME];
          // 暂停dom监听,防止拖拽变化不停触发
          if (domMapping) {
            domMapping.stop();
          }

          if (isTreeTable) {
            addTreePlaceholderRows(
              domMapping.mapping,
              elTableInstance.treeProps,
              DRAGGABLE.replace('.', ''))
          }

          /**
           * 解决手动关闭后会有的错位问题
           * 导致原因,default-expanded-all
           * 需要记录一下当前打开的行,结束之后还原状态(待定)
           */
          draggableTable.store.states.defaultExpandAll = false;

          // 如果是空表格,增加一个css
          const tableEl = draggableTable.$el.querySelector(
            ".el-table__body-wrapper table"
          );
          if (tableEl.clientHeight === 0) {
            // body-wrapper增加样式,让overflw可显示同时table有个透明区域可拖动
            tableEl.parentNode.classList.add(EMPTY_FIX_CSS);
          }
        }
      },
      onStart(evt) {
        /**
         * expanded/树表格的处理, 关闭展开行
         */
        const { item } = evt;
        const domInfo = mappingOberver.mapping.get(item);
        // 收起拖动的行的已展开行
        dom.toggleExpansion(domInfo, false);
      },
      onMove(evt, originEvt) {
        const { related, dragged, to, from, willInsertAfter } = evt;
        const fromContext = context.get(from);
        const toContext = context.get(to);
        /** @type {DomInfo} */
        const draggedDomInfo =
          fromContext[DOM_MAPPING_NAME].mapping.get(dragged);
        /** @type {DomInfo} */
        const relatedDomInfo =
          toContext[DOM_MAPPING_NAME].mapping.get(related);

        /**
         * 树状表格的特殊处理,如果碰到的dom不是placeholder,则无视
         */
        if (isTreeTable) {
          if (relatedDomInfo.type !== 'placeholder') {
            return false
          }
        }

        /**
         * 判断是否需要修正当前dragged的对应level
         */
        // let targrtDomInfo = fixDomInfoByDirection(
        //   relatedDomInfo,
        //   draggedDomInfo,
        //   willInsertAfter
        // );
        let targrtDomInfo = relatedDomInfo

        const onMove = getOnMove(elTableInstance);
        if (onMove) {
          const onMoveResutl = onMove(evt, originEvt, {
            dragged: draggedDomInfo,
            related: targrtDomInfo,
          });

          /**
           * @todo 兼容willInserAfter属性
           */
          switch (onMoveResutl) {
            case 1: {
              if (!willInsertAfter) {
                targrtDomInfo = relatedDomInfo
                // fixDomInfoByDirection(
                //   relatedDomInfo,
                //   draggedDomInfo,
                //   true
                // );
              }
              break;
            }
            case -1: {
              if (willInsertAfter) {
                targrtDomInfo = relatedDomInfo
                // fixDomInfoByDirection(
                //   relatedDomInfo,
                //   draggedDomInfo,
                //   false
                // );
              }
              break;
            }
            case false: {
              return false;
            }
            default: {
              break;
            }
          }
        }

        /**
         * relatedDomInfo,自动将children插入到自身后方
         * @todo 需要增加动画效果,目标直接插入,需要在下一循环,位置变化好后再配置
         */
        setTimeout(() => {
          /** @type {import('types/DomInfo').DomInfo} */
          relatedDomInfo.childrenList.forEach((children) => {
            // expanded或者是影子行
            if (children.type === "proxy") {
              dom.insertAfter(children.el, relatedDomInfo.el);
            }
          });
        });

        const {
          states: { indent },
        } = fromContext.store;
        dom.changeDomInfoLevel(draggedDomInfo, targrtDomInfo.level, indent);
      },
      onEnd(evt) {
        const { to, from, pullMode, newIndex, item, oldIndex } = evt;
        const fromContext = context.get(from);
        const toContext = context.get(to);

        /** @type {DomInfo} */
        const fromDomInfo = fromContext[DOM_MAPPING_NAME].mapping.get(item);
        /**
         * @type {DomInfo[]}
         * 之前目标位置的dom元素, 因为dom已经换了,所以需要通过elIndex的方式重新找回来
         */
        const toDomInfoList = Array.from(
          toContext[DOM_MAPPING_NAME].mapping.values()
        );
        const toDomInfo =
          toDomInfoList.find((domInfo) => domInfo.elIndex === newIndex) ||
          toContext[DOM_MAPPING_NAME].mapping.get(to);
        // const toDomInfo = {
        //   ...fixDomInfoByDirection(
        //     originToDomInfo,
        //     fromDomInfo,
        //     from === to ? newIndex > oldIndex : false
        //   ),
        // };

        // 跨表格index修正
        if (
          from !== to &&
          to.querySelectorAll(DRAGGABLE).length <= 2
        ) {
          toDomInfo.index = newIndex;
        }

        /**
         * 数据层面的交换
         */
        // mapping层面的交换
        exchange(
          fromDomInfo.index,
          fromDomInfo.parent.childrenList,
          toDomInfo.index,
          toDomInfo.type === "root"
            ? toDomInfo.childrenList
            : toDomInfo.parent.childrenList,
          pullMode
        );

        // 数据层面的交换
        exchange(
          fromDomInfo.index,
          fromDomInfo.data,
          toDomInfo.index,
          toDomInfo.data,
          pullMode
        );

        // clone对象的话,需要从dom层面删除,防止el-table重复渲染
        if (pullMode === 'clone' && from !== to) {
          to.removeChild(fromDomInfo.el)
        }

        // 通知更新
        updateElTableInstance(from, to, context, function (tableContext) {
          const draggableContext = tableContext.$parent; // 包裹组件
          const data = tableContext[PROP];
          draggableContext.$emit("input", data);
        });

        /**
         * dom修正,因为exchange之后el-table可能会错乱,所以需要修正位置
         * 将原始的dom信息带回来children带回来
         * 删除一些临时加进去的行
         */
        // 根据mapping自动重新绘制, 最高一层就不用rebuild了
        if (toDomInfo.parent && toDomInfo.parent.parent) {
          dom.toggleExpansion(toDomInfo.parent, true);
        }
        // expanded部分
        dom.toggleExpansion(fromDomInfo, true);
        /** @todo 缓存是否强制expanded */
        toContext.toggleRowExpansion(fromDomInfo.data, true);

        cleanUp()
      },
      onUnchoose() {
        cleanUp()
        /**
         * 全局重新开始监听dom变化
         * 需要在之前dom操作完成之后进行
         */
        if (dommappingTimer) {
          clearTimeout(dommappingTimer)
        }
        dommappingTimer = setTimeout(() => {
          for (const draggableTable of context.values()) {
            const domMapping = draggableTable[DOM_MAPPING_NAME];
            if (domMapping) {
              domMapping.rebuild();
              domMapping.start();
            }
          }
        }, 100);
      },
    };
  },
}