UNPKG

vxe-pc-ui

Version:
1,660 lines (1,658 loc) 96.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _vue = require("vue"); var _comp = require("../../ui/src/comp"); var _ui = require("../../ui"); var _util = require("./util"); var _log = require("../../ui/src/log"); var _store = require("./store"); var _xeUtils = _interopRequireDefault(require("xe-utils")); var _vn = require("../../ui/src/vn"); var _dom = require("../../ui/src/dom"); var _utils = require("../../ui/src/utils"); var _anime = require("../../ui/src/anime"); var _loading = _interopRequireDefault(require("../../loading")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const { menus, getConfig, getI18n, getIcon } = _ui.VxeUI; /** * 生成节点的唯一主键 */ function getNodeUniqueId() { return _xeUtils.default.uniqueId('node_'); } function createInternalData() { return { // initialized: false, // lastFilterValue: '', treeFullData: [], afterTreeList: [], afterVisibleList: [], nodeMaps: {}, selectCheckboxMaps: {}, indeterminateRowMaps: {}, treeExpandedMaps: {}, treeExpandLazyLoadedMaps: {}, lastScrollLeft: 0, lastScrollTop: 0, scrollYStore: { startIndex: 0, endIndex: 0, visibleSize: 0, offsetSize: 0, rowHeight: 0 }, // prevDragNode: null, // prevDragToChild: false, // prevDragPos: '' lastScrollTime: 0 // hpTimeout: undefined }; } function createReactData() { return { parentHeight: 0, customHeight: 0, customMinHeight: 0, customMaxHeight: 0, currentNode: null, scrollYLoad: false, bodyHeight: 0, topSpaceHeight: 0, selectRadioKey: null, treeList: [], updateExpandedFlag: 1, updateCheckboxFlag: 1, dragNode: null, dragTipText: '' }; } // let crossTreeDragNodeObj: { // $oldTree: VxeTreeConstructor & VxeTreePrivateMethods // $newTree: (VxeTreeConstructor & VxeTreePrivateMethods) | null // } | null = null var _default = exports.default = (0, _comp.defineVxeComponent)({ name: 'VxeTree', props: { data: Array, autoResize: { type: Boolean, default: () => getConfig().tree.autoResize }, height: [String, Number], maxHeight: { type: [String, Number], default: () => getConfig().tree.maxHeight }, minHeight: { type: [String, Number], default: () => getConfig().tree.minHeight }, loading: Boolean, loadingConfig: Object, accordion: { type: Boolean, default: () => getConfig().tree.accordion }, childrenField: { type: String, default: () => getConfig().tree.childrenField }, valueField: { type: String, default: () => getConfig().tree.valueField }, keyField: { type: String, default: () => getConfig().tree.keyField }, parentField: { type: String, default: () => getConfig().tree.parentField }, titleField: { type: String, default: () => getConfig().tree.titleField }, hasChildField: { type: String, default: () => getConfig().tree.hasChildField }, mapChildrenField: { type: String, default: () => getConfig().tree.mapChildrenField }, transform: Boolean, // 已废弃 isCurrent: Boolean, // 已废弃 isHover: Boolean, expandAll: Boolean, expandNodeKeys: Array, showLine: { type: Boolean, default: () => getConfig().tree.showLine }, trigger: String, indent: { type: Number, default: () => getConfig().tree.indent }, showRadio: { type: Boolean, default: () => getConfig().tree.showRadio }, checkNodeKey: { type: [String, Number], default: () => getConfig().tree.checkNodeKey }, radioConfig: Object, showCheckbox: { type: Boolean, default: () => getConfig().tree.showCheckbox }, checkNodeKeys: { type: Array, default: () => getConfig().tree.checkNodeKeys }, checkboxConfig: Object, nodeConfig: Object, lazy: Boolean, toggleMethod: Function, loadMethod: Function, drag: { type: Boolean, default: () => getConfig().tree.drag }, dragConfig: Object, menuConfig: Object, showIcon: { type: Boolean, default: true }, iconOpen: { type: String, default: () => getConfig().tree.iconOpen }, iconClose: { type: String, default: () => getConfig().tree.iconClose }, iconLoaded: { type: String, default: () => getConfig().tree.iconLoaded }, filterValue: [String, Number], filterConfig: Object, size: { type: String, default: () => getConfig().tree.size || getConfig().size }, virtualYConfig: Object }, emits: ['update:modelValue', 'update:checkNodeKey', 'update:checkNodeKeys', 'node-click', 'node-dblclick', 'current-change', 'radio-change', 'checkbox-change', 'load-success', 'load-error', 'scroll', 'node-dragstart', 'node-dragover', 'node-dragend', 'node-expand', 'node-menu', 'menu-click'], setup(props, context) { const { emit, slots } = context; const xID = _xeUtils.default.uniqueId(); const { computeSize } = (0, _ui.useSize)(props); const refElem = (0, _vue.ref)(); const refHeaderWrapperElem = (0, _vue.ref)(); const refFooterWrapperElem = (0, _vue.ref)(); const refVirtualWrapper = (0, _vue.ref)(); const refVirtualBody = (0, _vue.ref)(); const refDragNodeLineElem = (0, _vue.ref)(); const refDragTipElem = (0, _vue.ref)(); const crossTreeDragNodeInfo = (0, _store.getCrossTreeDragNodeInfo)(); const internalData = createInternalData(); const reactData = (0, _vue.reactive)(createReactData()); const refMaps = { refElem }; const computeTitleField = (0, _vue.computed)(() => { return props.titleField || 'title'; }); const computeKeyField = (0, _vue.computed)(() => { return props.keyField || 'id'; }); const computeValueField = (0, _vue.computed)(() => { const keyField = computeKeyField.value; return props.valueField || keyField; }); const computeParentField = (0, _vue.computed)(() => { return props.parentField || 'parentId'; }); const computeChildrenField = (0, _vue.computed)(() => { return props.childrenField || 'children'; }); const computeMapChildrenField = (0, _vue.computed)(() => { return props.mapChildrenField || 'mapChildren'; }); const computeHasChildField = (0, _vue.computed)(() => { return props.hasChildField || 'hasChild'; }); const computeVirtualYOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().tree.virtualYConfig, props.virtualYConfig); }); const computeIsRowCurrent = (0, _vue.computed)(() => { const nodeOpts = computeNodeOpts.value; const { isCurrent } = nodeOpts; if (_xeUtils.default.isBoolean(isCurrent)) { return isCurrent; } return props.isCurrent; }); const computeIsRowHover = (0, _vue.computed)(() => { const nodeOpts = computeNodeOpts.value; const { isHover } = nodeOpts; if (_xeUtils.default.isBoolean(isHover)) { return isHover; } return props.isHover; }); const computeRadioOpts = (0, _vue.computed)(() => { return Object.assign({ showIcon: true }, getConfig().tree.radioConfig, props.radioConfig); }); const computeCheckboxOpts = (0, _vue.computed)(() => { return Object.assign({ showIcon: true }, getConfig().tree.checkboxConfig, props.checkboxConfig); }); const computeNodeOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().tree.nodeConfig, props.nodeConfig); }); const computeLoadingOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().tree.loadingConfig, props.loadingConfig); }); const computeDragOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().tree.dragConfig, props.dragConfig); }); const computeMenuOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().tree.menuConfig, props.menuConfig); }); const computeTreeStyle = (0, _vue.computed)(() => { const { indent } = props; const { customHeight, customMinHeight, customMaxHeight } = reactData; const stys = {}; if (customHeight) { stys.height = (0, _dom.toCssUnit)(customHeight); } if (customMinHeight) { stys.minHeight = (0, _dom.toCssUnit)(customMinHeight); } if (customMaxHeight) { stys.maxHeight = (0, _dom.toCssUnit)(customMaxHeight); } if (indent) { stys['--vxe-ui-tree-node-indent'] = (0, _dom.toCssUnit)(indent); } return stys; }); const computeFilterOpts = (0, _vue.computed)(() => { return Object.assign({}, getConfig().tree.filterConfig, props.filterConfig); }); const computeMaps = { computeKeyField, computeParentField, computeChildrenField, computeMapChildrenField, computeRadioOpts, computeCheckboxOpts, computeNodeOpts, computeDragOpts }; const $xeTree = { xID, props, context, internalData, reactData, getRefMaps: () => refMaps, getComputeMaps: () => computeMaps }; const getNodeId = node => { const valueField = computeValueField.value; const nodeKey = _xeUtils.default.get(node, valueField); return (0, _util.enNodeValue)(nodeKey); }; const isExpandByNode = node => { const { updateExpandedFlag } = reactData; const { treeExpandedMaps } = internalData; const nodeid = getNodeId(node); return !!(updateExpandedFlag && treeExpandedMaps[nodeid]); }; const isCheckedByRadioNodeId = nodeid => { const { selectRadioKey } = reactData; return selectRadioKey === nodeid; }; const isCheckedByRadioNode = node => { return isCheckedByRadioNodeId(getNodeId(node)); }; const isCheckedByCheckboxNodeId = nodeid => { const { updateCheckboxFlag } = reactData; const { selectCheckboxMaps } = internalData; return !!(updateCheckboxFlag && selectCheckboxMaps[nodeid]); }; const isCheckedByCheckboxNode = node => { return isCheckedByCheckboxNodeId(getNodeId(node)); }; const isIndeterminateByCheckboxNodeid = nodeid => { const { updateCheckboxFlag } = reactData; const { indeterminateRowMaps } = internalData; return !!(updateCheckboxFlag && indeterminateRowMaps[nodeid]); }; const isIndeterminateByCheckboxNode = node => { return isIndeterminateByCheckboxNodeid(getNodeId(node)); }; const emitCheckboxMode = value => { emit('update:checkNodeKeys', value); }; const emitRadioMode = value => { emit('update:checkNodeKey', value); }; const handleSetCheckboxByNodeId = (nodeKeys, checked) => { const { nodeMaps } = internalData; if (nodeKeys) { if (!_xeUtils.default.isArray(nodeKeys)) { nodeKeys = [nodeKeys]; } const nodeList = []; nodeKeys.forEach(nodeKey => { const nodeid = (0, _util.enNodeValue)(nodeKey); const nodeItem = nodeMaps[nodeid]; if (nodeItem) { nodeList.push(nodeItem.item); } }); handleCheckedCheckboxNode(nodeList, checked); } return (0, _vue.nextTick)(); }; const handleCheckedCheckboxNode = (nodeList, checked) => { const { transform } = props; const { selectCheckboxMaps } = internalData; const mapChildrenField = computeMapChildrenField.value; const childrenField = computeChildrenField.value; const checkboxOpts = computeCheckboxOpts.value; const { checkStrictly } = checkboxOpts; const handleSelect = node => { const nodeid = getNodeId(node); if (checked) { if (!selectCheckboxMaps[nodeid]) { selectCheckboxMaps[nodeid] = node; } } else { if (selectCheckboxMaps[nodeid]) { delete selectCheckboxMaps[nodeid]; } } }; if (checkStrictly) { nodeList.forEach(handleSelect); } else { _xeUtils.default.eachTree(nodeList, handleSelect, { children: transform ? mapChildrenField : childrenField }); } reactData.updateCheckboxFlag++; updateCheckboxStatus(); }; const updateCheckboxChecked = nodeKeys => { internalData.selectCheckboxMaps = {}; handleSetCheckboxByNodeId(nodeKeys, true); }; const handleSetExpand = (nodeid, expanded, expandedMaps) => { if (expanded) { if (!expandedMaps[nodeid]) { expandedMaps[nodeid] = true; } } else { if (expandedMaps[nodeid]) { delete expandedMaps[nodeid]; } } }; const dispatchEvent = (type, params, evnt) => { emit(type, (0, _ui.createEvent)(evnt, { $tree: $xeTree }, params)); }; const getParentElem = () => { const el = refElem.value; return el ? el.parentElement : null; }; const calcTreeHeight = key => { const { parentHeight } = reactData; const val = props[key]; let num = 0; if (val) { if (val === '100%' || val === 'auto') { num = parentHeight; } else { if ((0, _dom.isScale)(val)) { num = Math.floor((_xeUtils.default.toInteger(val) || 1) / 100 * parentHeight); } else { num = _xeUtils.default.toNumber(val); } num = Math.max(40, num); } } return num; }; const updateHeight = () => { reactData.customHeight = calcTreeHeight('height'); reactData.customMinHeight = calcTreeHeight('minHeight'); reactData.customMaxHeight = calcTreeHeight('maxHeight'); // 如果启用虚拟滚动,默认高度 if (reactData.scrollYLoad && !(reactData.customHeight || reactData.customMinHeight)) { reactData.customHeight = 300; } }; const createNode = records => { const valueField = computeValueField.value; return Promise.resolve(records.map(obj => { const item = Object.assign({}, obj); let nodeid = getNodeId(item); if (!nodeid) { nodeid = getNodeUniqueId(); _xeUtils.default.set(item, valueField, nodeid); } return item; })); }; const cacheNodeMap = () => { const { treeFullData } = internalData; const valueField = computeValueField.value; const childrenField = computeChildrenField.value; const keyMaps = {}; _xeUtils.default.eachTree(treeFullData, (item, index, items, path, parent, nodes) => { let nodeid = getNodeId(item); if (!nodeid) { nodeid = getNodeUniqueId(); _xeUtils.default.set(item, valueField, nodeid); } keyMaps[nodeid] = { item, index, $index: -1, _index: -1, items, parent, nodes, level: nodes.length - 1, treeIndex: index, lineCount: 0, treeLoaded: false }; }, { children: childrenField }); internalData.nodeMaps = keyMaps; }; const updateAfterDataIndex = () => { const { transform } = props; const { afterTreeList, nodeMaps } = internalData; const childrenField = computeChildrenField.value; const mapChildrenField = computeMapChildrenField.value; let vtIndex = 0; _xeUtils.default.eachTree(afterTreeList, (item, index, items) => { const nodeid = getNodeId(item); const nodeItem = nodeMaps[nodeid]; if (nodeItem) { nodeItem.items = items; nodeItem.treeIndex = index; nodeItem._index = vtIndex; } else { const rest = { item, index, $index: -1, _index: vtIndex, items, parent, nodes: [], level: 0, treeIndex: index, lineCount: 0, treeLoaded: false }; nodeMaps[nodeid] = rest; } vtIndex++; }, { children: transform ? mapChildrenField : childrenField }); }; const updateAfterFullData = () => { const { transform, filterValue } = props; const { treeFullData, lastFilterValue } = internalData; const titleField = computeTitleField.value; const childrenField = computeChildrenField.value; const mapChildrenField = computeMapChildrenField.value; const filterOpts = computeFilterOpts.value; const { autoExpandAll, beforeFilterMethod, filterMethod, afterFilterMethod } = filterOpts; let fullList = treeFullData; let treeList = fullList; let filterStr = ''; if (filterValue || filterValue === 0) { filterStr = `${filterValue}`; const handleSearch = filterMethod ? item => { return filterMethod({ $tree: $xeTree, node: item, filterValue: filterStr }); } : item => { return String(item[titleField]).toLowerCase().indexOf(filterStr.toLowerCase()) > -1; }; const bafParams = { $tree: $xeTree, filterValue: filterStr }; if (beforeFilterMethod) { beforeFilterMethod(bafParams); } if (transform) { treeList = _xeUtils.default.searchTree(treeFullData, handleSearch, { original: true, isEvery: true, children: childrenField, mapChildren: mapChildrenField }); fullList = treeList; } else { fullList = treeFullData.filter(handleSearch); } internalData.lastFilterValue = filterStr; (0, _vue.nextTick)(() => { // 筛选时自动展开 if (autoExpandAll) { $xeTree.setAllExpandNode(true).then(() => { if (afterFilterMethod) { afterFilterMethod(bafParams); } }); } else { if (afterFilterMethod) { afterFilterMethod(bafParams); } } }); } else { if (transform) { treeList = _xeUtils.default.searchTree(treeFullData, () => true, { original: true, isEvery: true, children: childrenField, mapChildren: mapChildrenField }); fullList = treeList; if (lastFilterValue) { const bafParams = { $tree: $xeTree, filterValue: filterStr }; if (beforeFilterMethod) { beforeFilterMethod(bafParams); } // 取消筛选时自动收起 (0, _vue.nextTick)(() => { if (autoExpandAll) { $xeTree.clearAllExpandNode().then(() => { if (afterFilterMethod) { afterFilterMethod(bafParams); } }); } else { if (afterFilterMethod) { afterFilterMethod(bafParams); } } }); } } internalData.lastFilterValue = ''; } internalData.afterVisibleList = fullList; internalData.afterTreeList = treeList; updateAfterDataIndex(); }; /** * 如果为虚拟树、则将树结构拍平 */ const handleTreeToList = () => { const { transform } = props; const { afterTreeList, treeExpandedMaps } = internalData; const mapChildrenField = computeMapChildrenField.value; const expandMaps = {}; if (transform) { const fullData = []; _xeUtils.default.eachTree(afterTreeList, (item, index, items, path, parentRow) => { const nodeid = getNodeId(item); const parentNodeid = getNodeId(parentRow); if (!parentRow || expandMaps[parentNodeid] && treeExpandedMaps[parentNodeid]) { expandMaps[nodeid] = 1; fullData.push(item); } }, { children: mapChildrenField }); updateScrollYStatus(fullData); internalData.afterVisibleList = fullData; return fullData; } return internalData.afterVisibleList; }; const handleData = force => { const { scrollYLoad } = reactData; const { scrollYStore, nodeMaps } = internalData; let fullList = internalData.afterVisibleList; if (force) { // 更新数据,处理筛选和排序 updateAfterFullData(); // 如果为虚拟树,将树结构拍平 fullList = handleTreeToList(); } const treeList = scrollYLoad ? fullList.slice(scrollYStore.startIndex, scrollYStore.endIndex) : fullList.slice(0); treeList.forEach((item, $index) => { const nodeid = getNodeId(item); const itemRest = nodeMaps[nodeid]; if (itemRest) { itemRest.$index = $index; } }); reactData.treeList = treeList; }; const triggerSearchEvent = _xeUtils.default.debounce(() => handleData(true), 350, { trailing: true }); const loadData = list => { const { expandAll, expandNodeKeys, transform } = props; const { initialized, scrollYStore } = internalData; const keyField = computeKeyField.value; const parentField = computeParentField.value; const childrenField = computeChildrenField.value; const fullData = transform ? _xeUtils.default.toArrayTree(list, { key: keyField, parentKey: parentField, mapChildren: childrenField }) : list ? list.slice(0) : []; internalData.treeFullData = fullData; Object.assign(scrollYStore, { startIndex: 0, endIndex: 1, visibleSize: 0 }); const sYLoad = updateScrollYStatus(fullData); cacheNodeMap(); handleData(true); if (sYLoad) { if (!(props.height || props.maxHeight)) { (0, _log.errLog)('vxe.error.reqProp', ['[tree] height | max-height | virtual-y-config.enabled=false']); } } return computeScrollLoad().then(() => { if (!initialized) { if (list && list.length) { internalData.initialized = true; if (expandAll) { $xeTree.setAllExpandNode(true); } else if (expandNodeKeys && expandNodeKeys.length) { $xeTree.setExpandByNodeId(expandNodeKeys, true); } handleSetCheckboxByNodeId(props.checkNodeKeys || [], true); } } updateHeight(); refreshScroll(); }); }; const updateScrollYStatus = fullData => { const { transform } = props; const virtualYOpts = computeVirtualYOpts.value; const allList = fullData || internalData.treeFullData; // 如果gt为0,则总是启用 const scrollYLoad = !!transform && !!virtualYOpts.enabled && virtualYOpts.gt > -1 && (virtualYOpts.gt === 0 || virtualYOpts.gt < allList.length); reactData.scrollYLoad = scrollYLoad; return scrollYLoad; }; const updateYSpace = () => { const { scrollYLoad } = reactData; const { scrollYStore, afterVisibleList } = internalData; reactData.bodyHeight = scrollYLoad ? afterVisibleList.length * scrollYStore.rowHeight : 0; reactData.topSpaceHeight = scrollYLoad ? Math.max(scrollYStore.startIndex * scrollYStore.rowHeight, 0) : 0; }; const updateYData = () => { handleData(); updateYSpace(); }; const computeScrollLoad = () => { return (0, _vue.nextTick)().then(() => { const { scrollYLoad } = reactData; const { scrollYStore } = internalData; const virtualBodyElem = refVirtualBody.value; const virtualYOpts = computeVirtualYOpts.value; let rowHeight = 0; let firstItemElem; if (virtualBodyElem) { if (!firstItemElem) { firstItemElem = virtualBodyElem.children[0]; } } if (firstItemElem) { rowHeight = firstItemElem.offsetHeight; } rowHeight = Math.max(20, rowHeight); scrollYStore.rowHeight = rowHeight; // 计算 Y 逻辑 if (scrollYLoad) { const scrollBodyElem = refVirtualWrapper.value; const visibleYSize = Math.max(8, scrollBodyElem ? Math.ceil(scrollBodyElem.clientHeight / rowHeight) : 0); const offsetYSize = Math.max(0, Math.min(2, _xeUtils.default.toNumber(virtualYOpts.oSize))); scrollYStore.offsetSize = offsetYSize; scrollYStore.visibleSize = visibleYSize; scrollYStore.endIndex = Math.max(scrollYStore.startIndex, visibleYSize + offsetYSize, scrollYStore.endIndex); updateYData(); } else { updateYSpace(); } }); }; /** * 如果有滚动条,则滚动到对应的位置 */ const handleScrollTo = (scrollLeft, scrollTop) => { const scrollBodyElem = refVirtualWrapper.value; if (scrollLeft) { if (!_xeUtils.default.isNumber(scrollLeft)) { scrollTop = scrollLeft.top; scrollLeft = scrollLeft.left; } } if (scrollBodyElem) { if (_xeUtils.default.isNumber(scrollLeft)) { scrollBodyElem.scrollLeft = scrollLeft; } if (_xeUtils.default.isNumber(scrollTop)) { scrollBodyElem.scrollTop = scrollTop; } } if (reactData.scrollYLoad) { return new Promise(resolve => { setTimeout(() => { (0, _vue.nextTick)(() => { resolve(); }); }, 50); }); } return (0, _vue.nextTick)(); }; /** * 刷新滚动条 */ const refreshScroll = () => { const { lastScrollLeft, lastScrollTop } = internalData; return clearScroll().then(() => { if (lastScrollLeft || lastScrollTop) { internalData.lastScrollLeft = 0; internalData.lastScrollTop = 0; return scrollTo(lastScrollLeft, lastScrollTop); } }); }; /** * 重新计算列表 */ const recalculate = () => { const { scrollYStore } = internalData; const { rowHeight } = scrollYStore; const el = refElem.value; if (el && el.clientWidth && el.clientHeight) { const parentEl = getParentElem(); const headerWrapperEl = refHeaderWrapperElem.value; const footerWrapperEl = refFooterWrapperElem.value; const headHeight = headerWrapperEl ? headerWrapperEl.clientHeight : 0; const footHeight = footerWrapperEl ? footerWrapperEl.clientHeight : 0; if (parentEl) { const parentPaddingSize = (0, _dom.getPaddingTopBottomSize)(parentEl); reactData.parentHeight = Math.max(headHeight + footHeight + rowHeight, parentEl.clientHeight - parentPaddingSize - headHeight - footHeight); } updateHeight(); return computeScrollLoad().then(() => { updateHeight(); updateYSpace(); }); } return (0, _vue.nextTick)(); }; const loadYData = () => { const { scrollYStore } = internalData; const { startIndex, endIndex, visibleSize, offsetSize, rowHeight } = scrollYStore; const scrollBodyElem = refVirtualWrapper.value; if (!scrollBodyElem) { return; } const scrollTop = scrollBodyElem.scrollTop; const toVisibleIndex = Math.floor(scrollTop / rowHeight); const offsetStartIndex = Math.max(0, toVisibleIndex - 1 - offsetSize); const offsetEndIndex = toVisibleIndex + visibleSize + offsetSize; if (toVisibleIndex <= startIndex || toVisibleIndex >= endIndex - visibleSize - 1) { if (startIndex !== offsetStartIndex || endIndex !== offsetEndIndex) { scrollYStore.startIndex = offsetStartIndex; scrollYStore.endIndex = offsetEndIndex; updateYData(); } } }; const scrollEvent = evnt => { const scrollBodyElem = evnt.target; const scrollTop = scrollBodyElem.scrollTop; const scrollLeft = scrollBodyElem.scrollLeft; const isX = scrollLeft !== internalData.lastScrollLeft; const isY = scrollTop !== internalData.lastScrollTop; internalData.lastScrollTop = scrollTop; internalData.lastScrollLeft = scrollLeft; if (reactData.scrollYLoad) { loadYData(); } internalData.lastScrollTime = Date.now(); dispatchEvent('scroll', { scrollLeft, scrollTop, isX, isY }, evnt); }; const clearScroll = () => { const scrollBodyElem = refVirtualWrapper.value; if (scrollBodyElem) { scrollBodyElem.scrollTop = 0; scrollBodyElem.scrollLeft = 0; } internalData.lastScrollTop = 0; internalData.lastScrollLeft = 0; return (0, _vue.nextTick)(); }; const handleNodeMousedownEvent = (evnt, node) => { const { drag } = props; const { nodeMaps } = internalData; const targetEl = evnt.currentTarget; const dragConfig = computeDragOpts.value; const { trigger, isCrossDrag, isPeerDrag, disabledMethod } = dragConfig; const nodeid = getNodeId(node); const triggerTreeNode = (0, _dom.getEventTargetNode)(evnt, targetEl, 'vxe-tree--node-item-switcher').flag; let isNodeDrag = false; if (drag) { isNodeDrag = trigger === 'node'; } if (!triggerTreeNode) { const params = { node, $tree: $xeTree }; const itemRest = nodeMaps[nodeid]; if (isNodeDrag && (isCrossDrag || isPeerDrag || itemRest && !itemRest.level) && !(disabledMethod && disabledMethod(params))) { handleNodeDragMousedownEvent(evnt, { node }); } } }; const handleNodeClickEvent = (evnt, node) => { const { showRadio, showCheckbox, trigger } = props; const radioOpts = computeRadioOpts.value; const checkboxOpts = computeCheckboxOpts.value; const isRowCurrent = computeIsRowCurrent.value; let triggerCurrent = false; let triggerRadio = false; let triggerCheckbox = false; let triggerExpand = false; if (isRowCurrent) { triggerCurrent = true; changeCurrentEvent(evnt, node); } else if (reactData.currentNode) { reactData.currentNode = null; } if (trigger === 'node') { triggerExpand = true; toggleExpandEvent(evnt, node); } if (showRadio && radioOpts.trigger === 'node') { triggerRadio = true; changeRadioEvent(evnt, node); } if (showCheckbox && checkboxOpts.trigger === 'node') { triggerCheckbox = true; changeCheckboxEvent(evnt, node); } dispatchEvent('node-click', { node, triggerCurrent, triggerRadio, triggerCheckbox, triggerExpand }, evnt); }; const handleNodeDblclickEvent = (evnt, node) => { dispatchEvent('node-dblclick', { node }, evnt); }; const handleContextmenuEvent = (evnt, node) => { const { menuConfig } = props; const isRowCurrent = computeIsRowCurrent.value; const menuOpts = computeMenuOpts.value; if (menuConfig ? (0, _utils.isEnableConf)(menuOpts) : menuOpts.enabled) { const { options, visibleMethod } = menuOpts; if (!visibleMethod || visibleMethod({ $tree: $xeTree, options, node })) { if (isRowCurrent) { changeCurrentEvent(evnt, node); } else if (reactData.currentNode) { reactData.currentNode = null; } if (_ui.VxeUI.contextMenu) { _ui.VxeUI.contextMenu.openByEvent(evnt, { options, events: { optionClick(eventParams) { const { option } = eventParams; const gMenuOpts = menus.get(option.code); const tmMethod = gMenuOpts ? gMenuOpts.treeMenuMethod : null; const params = { menu: option, node, $event: evnt, $tree: $xeTree, /** * @@deprecated */ option }; if (tmMethod) { tmMethod(params, evnt); } dispatchEvent('menu-click', params, eventParams.$event); } } }); } } } dispatchEvent('node-menu', { node }, evnt); }; const handleAsyncTreeExpandChilds = node => { const checkboxOpts = computeCheckboxOpts.value; const { loadMethod } = props; const { checkStrictly } = checkboxOpts; return new Promise(resolve => { if (loadMethod) { const { nodeMaps } = internalData; const nodeid = getNodeId(node); const nodeItem = nodeMaps[nodeid]; internalData.treeExpandLazyLoadedMaps[nodeid] = true; Promise.resolve(loadMethod({ $tree: $xeTree, node })).then(childRecords => { const { treeExpandLazyLoadedMaps } = internalData; nodeItem.treeLoaded = true; if (treeExpandLazyLoadedMaps[nodeid]) { treeExpandLazyLoadedMaps[nodeid] = false; } if (!_xeUtils.default.isArray(childRecords)) { childRecords = []; } if (childRecords) { return $xeTree.loadChildrenNode(node, childRecords).then(childRows => { const { treeExpandedMaps } = internalData; if (childRows.length && !treeExpandedMaps[nodeid]) { treeExpandedMaps[nodeid] = true; } reactData.updateExpandedFlag++; // 如果当前节点已选中,则展开后子节点也被选中 if (!checkStrictly && $xeTree.isCheckedByCheckboxNodeId(nodeid)) { handleCheckedCheckboxNode(childRows, true); } dispatchEvent('load-success', { node, data: childRecords }, new Event('load-success')); return (0, _vue.nextTick)(); }); } else { dispatchEvent('load-success', { node, data: childRecords }, new Event('load-success')); } }).catch(e => { const { treeExpandLazyLoadedMaps } = internalData; nodeItem.treeLoaded = false; if (treeExpandLazyLoadedMaps[nodeid]) { treeExpandLazyLoadedMaps[nodeid] = false; } dispatchEvent('load-error', { node, data: e }, new Event('load-error')); }).finally(() => { handleTreeToList(); handleData(); return recalculate(); }); } else { resolve(); } }); }; /** * 展开与收起树节点 * @param nodeList * @param expanded * @returns */ const handleBaseTreeExpand = (nodeList, expanded) => { const { lazy, accordion, toggleMethod } = props; const { treeExpandLazyLoadedMaps, treeExpandedMaps } = internalData; const { nodeMaps } = internalData; const childrenField = computeChildrenField.value; const hasChildField = computeHasChildField.value; const result = []; let validNodes = toggleMethod ? nodeList.filter(node => toggleMethod({ $tree: $xeTree, expanded, node })) : nodeList; if (accordion) { validNodes = validNodes.length ? [validNodes[validNodes.length - 1]] : []; // 同一级只能展开一个 const nodeid = getNodeId(validNodes[0]); const nodeItem = nodeMaps[nodeid]; if (nodeItem) { nodeItem.items.forEach(item => { const itemNodeId = getNodeId(item); if (treeExpandedMaps[itemNodeId]) { delete treeExpandedMaps[itemNodeId]; } }); } } const expandNodes = []; if (expanded) { validNodes.forEach(item => { const itemNodeId = getNodeId(item); if (!treeExpandedMaps[itemNodeId]) { const nodeItem = nodeMaps[itemNodeId]; const isLoad = lazy && item[hasChildField] && !nodeItem.treeLoaded && !treeExpandLazyLoadedMaps[itemNodeId]; // 是否使用懒加载 if (isLoad) { result.push(handleAsyncTreeExpandChilds(item)); } else { if (item[childrenField] && item[childrenField].length) { treeExpandedMaps[itemNodeId] = true; expandNodes.push(item); } } } }); } else { validNodes.forEach(item => { const itemNodeId = getNodeId(item); if (treeExpandedMaps[itemNodeId]) { delete treeExpandedMaps[itemNodeId]; expandNodes.push(item); } }); } reactData.updateExpandedFlag++; handleTreeToList(); handleData(); return Promise.all(result).then(() => recalculate()); }; const toggleExpandEvent = (evnt, node) => { const { lazy } = props; const { treeExpandedMaps, treeExpandLazyLoadedMaps } = internalData; const nodeid = getNodeId(node); const expanded = !treeExpandedMaps[nodeid]; evnt.stopPropagation(); if (!lazy || !treeExpandLazyLoadedMaps[nodeid]) { handleBaseTreeExpand([node], expanded); } dispatchEvent('node-expand', { node, expanded }, evnt); }; const updateCheckboxStatus = () => { const { transform } = props; const { selectCheckboxMaps, indeterminateRowMaps, afterTreeList } = internalData; const childrenField = computeChildrenField.value; const mapChildrenField = computeMapChildrenField.value; const checkboxOpts = computeCheckboxOpts.value; const { checkStrictly, checkMethod } = checkboxOpts; if (!checkStrictly) { const childRowMaps = {}; const childRowList = []; _xeUtils.default.eachTree(afterTreeList, node => { const nodeid = getNodeId(node); const childList = node[childrenField]; if (childList && childList.length && !childRowMaps[nodeid]) { childRowMaps[nodeid] = 1; childRowList.unshift([node, nodeid, childList]); } }, { children: transform ? mapChildrenField : childrenField }); childRowList.forEach(vals => { const node = vals[0]; const nodeid = vals[1]; const childList = vals[2]; let sLen = 0; // 已选 let hLen = 0; // 半选 let vLen = 0; // 有效子行 const cLen = childList.length; // 子行 childList.forEach(checkMethod ? item => { const childNodeid = getNodeId(item); const isSelect = selectCheckboxMaps[childNodeid]; if (checkMethod({ $tree: $xeTree, node: item })) { if (isSelect) { sLen++; } else if (indeterminateRowMaps[childNodeid]) { hLen++; } vLen++; } else { if (isSelect) { sLen++; } else if (indeterminateRowMaps[childNodeid]) { hLen++; } } } : item => { const childNodeid = getNodeId(item); const isSelect = selectCheckboxMaps[childNodeid]; if (isSelect) { sLen++; } else if (indeterminateRowMaps[childNodeid]) { hLen++; } vLen++; }); let isSelected = false; if (cLen > 0) { if (vLen > 0) { isSelected = (sLen > 0 || hLen > 0) && sLen >= vLen; } else { // 如果存在子项禁用 if (sLen > 0 && sLen >= vLen) { isSelected = true; } else if (selectCheckboxMaps[nodeid]) { isSelected = true; } else { isSelected = false; } } } else { // 如果无子项 isSelected = selectCheckboxMaps[nodeid]; } const halfSelect = !isSelected && (sLen > 0 || hLen > 0); if (isSelected) { selectCheckboxMaps[nodeid] = node; if (indeterminateRowMaps[nodeid]) { delete indeterminateRowMaps[nodeid]; } } else { if (selectCheckboxMaps[nodeid]) { delete selectCheckboxMaps[nodeid]; } if (halfSelect) { indeterminateRowMaps[nodeid] = node; } else { if (indeterminateRowMaps[nodeid]) { delete indeterminateRowMaps[nodeid]; } } } }); reactData.updateCheckboxFlag++; } }; const changeCheckboxEvent = (evnt, node) => { evnt.preventDefault(); evnt.stopPropagation(); const { transform } = props; const { selectCheckboxMaps } = internalData; const childrenField = computeChildrenField.value; const mapChildrenField = computeMapChildrenField.value; const checkboxOpts = computeCheckboxOpts.value; const { checkStrictly, checkMethod } = checkboxOpts; let isDisabled = !!checkMethod; if (checkMethod) { isDisabled = !checkMethod({ $tree: $xeTree, node }); } if (isDisabled) { return; } const nodeid = getNodeId(node); let isChecked = false; if (selectCheckboxMaps[nodeid]) { delete selectCheckboxMaps[nodeid]; } else { isChecked = true; selectCheckboxMaps[nodeid] = node; } if (!checkStrictly) { _xeUtils.default.eachTree(_xeUtils.default.get(node, transform ? mapChildrenField : childrenField), childNode => { const childNodeid = getNodeId(childNode); if (isChecked) { if (!selectCheckboxMaps[childNodeid]) { selectCheckboxMaps[childNodeid] = true; } } else { if (selectCheckboxMaps[childNodeid]) { delete selectCheckboxMaps[childNodeid]; } } }, { children: transform ? mapChildrenField : childrenField }); } reactData.updateCheckboxFlag++; updateCheckboxStatus(); const nodeids = _xeUtils.default.keys(selectCheckboxMaps); const value = nodeids.map(_util.deNodeValue); emitCheckboxMode(value); dispatchEvent('checkbox-change', { node, value, checked: isChecked }, evnt); }; const changeCurrentEvent = (evnt, node) => { evnt.preventDefault(); const nodeOpts = computeNodeOpts.value; const { currentMethod, trigger } = nodeOpts; const childrenField = computeChildrenField.value; const childList = _xeUtils.default.get(node, childrenField); const hasChild = childList && childList.length; let isDisabled = !!currentMethod; if (trigger === 'child') { if (hasChild) { return; } } else if (trigger === 'parent') { if (!hasChild) { return; } } if (currentMethod) { isDisabled = !currentMethod({ node }); } if (isDisabled) { return; } const isChecked = true; reactData.currentNode = node; dispatchEvent('current-change', { node, checked: isChecked }, evnt); }; const changeRadioEvent = (evnt, node) => { evnt.preventDefault(); evnt.stopPropagation(); const radioOpts = computeRadioOpts.value; const { checkMethod } = radioOpts; let isDisabled = !!checkMethod; if (checkMethod) { isDisabled = !checkMethod({ $tree: $xeTree, node }); } if (isDisabled) { return; } const isChecked = true; const nodeid = getNodeId(node); const value = (0, _util.deNodeValue)(nodeid); reactData.selectRadioKey = nodeid; emitRadioMode(value); dispatchEvent('radio-change', { node, value, checked: isChecked }, evnt); }; const handleGlobalResizeEvent = () => { const el = refElem.value; if (!el || !el.clientWidth) { return; } recalculate(); }; const treeMethods = { dispatchEvent, getNodeId, getNodeById(nodeid) { const { nodeMaps } = internalData; if (nodeid) { const nodeItem = nodeMaps[nodeid]; if (nodeItem) { return nodeItem.item; } } return null; }, loadData(data) { return loadData(data || []); }, reloadData(data) { return loadData(data || []); }, clearCurrentNode() { reactData.currentNode = null; return (0, _vue.nextTick)(); }, getCurrentNodeId() { const { currentNode } = reactData; if (currentNode) { return (0, _util.deNodeValue)(getNodeId(currentNode)); } return null; }, getCurrentNode() { const { currentNode } = reactData; const { nodeMaps } = internalData; if (currentNode) { const nodeItem = nodeMaps[getNodeId(currentNode)]; if (nodeItem) { return nodeItem.item; } } return null; }, setCurrentNodeId(nodeKey) { const { nodeMaps } = internalData; const nodeItem = nodeMaps[(0, _util.enNodeValue)(nodeKey)]; reactData.currentNode = nodeItem ? nodeItem.item : null; return (0, _vue.nextTick)(); }, setCurrentNode(node) { reactData.currentNode = node; return (0, _vue.nextTick)(); }, clearRadioNode() { reactData.selectRadioKey = null; emitRadioMode(null); return (0, _vue.nextTick)(); }, getRadioNodeId() { return reactData.selectRadioKey || null; }, getRadioNode() { const { selectRadioKey } = reactData; const { nodeMaps } = internalData; if (selectRadioKey) { const nodeItem = nodeMaps[selectRadioKey]; if (nodeItem) { return nodeItem.item; } } return null; }, setRadioNodeId(nodeKey) { reactData.selectRadioKey = (0, _util.enNodeValue)(nodeKey); emitRadioMode(nodeKey); return (0, _vue.nextTick)(); }, setRadioNode(node) { if (node) { const nodeid = getNodeId(node); reactData.selectRadioKey = nodeid; emitRadioMode((0, _util.deNodeValue)(nodeid)); } else { emitRadioMode(null); } return (0, _vue.nextTick)(); }, setCheckboxNode(nodeList, checked) { if (nodeList) { if (!_xeUtils.default.isArray(nodeList)) { nodeList = [nodeList]; } handleCheckedCheckboxNode(nodeList, checked); } emitCheckboxMode($xeTree.getCheckboxNodeIds()); return (0, _vue.nextTick)(); }, setCheckboxByNodeId(nodeKeys, selected) { handleSetCheckboxByNodeId(nodeKeys, selected); emitCheckboxMode($xeTree.getCheckboxNodeIds()); return (0, _vue.nextTick)(); }, getCheckboxNodeIds() { const { selectCheckboxMaps } = internalData; const nodeKeys = []; _xeUtils.default.each(selectCheckboxMaps, (item, nodeId) => { nodeKeys.push((0, _util.deNodeValue)(nodeId)); }); return nodeKeys; }, getCheckboxNodes() { const { nodeMaps, selectCheckboxMaps } = internalData; const list = []; _xeUtils.default.each(selectCheckboxMaps, (item, nodeid) => { const nodeItem = nodeMaps[nodeid]; if (nodeItem) { list.push(nodeItem.item);