/* eslint-disable no-unreachable */ /* eslint-disable no-unused-vars */ import { DOM_MAPPING_NAME } from './constant' import { // fixDomInfoByDirection, MappingOberver, getOnMove, exchange, updateElTableInstance, checkIsTreeTable, addTreePlaceholderRows } from "@/utils/utils"; import dom, { cleanUp, EMPTY_FIX_CSS, } from "@/utils/dom"; export const WRAPPER = '.el-table__body-wrapper tbody' export const DRAGGABLE = '.el-table__row' export const 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); }, }; }, } export default config