vxe-pc-ui
Version:
A vue based PC component library
1,660 lines (1,658 loc) • 96.7 kB
JavaScript
"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);