import { ref, reactive, computed, watch, onMounted, onBeforeUnmount, provide, nextTick, h } from "vue";
import { defineVxeComponent } from "../../ui/src/comp.mjs";
import "../../ui/index.mjs";
import { enNodeValue, deNodeValue, calcTreeLine } from "./util.mjs";
import { errLog } from "../../ui/src/log.mjs";
import { getCrossTreeDragNodeInfo } from "./store.mjs";
import XEUtils from "xe-utils";
import { getSlotVNs } from "../../ui/src/vn.mjs";
import { toCssUnit, getPaddingTopBottomSize, isScale, removeClass, getEventTargetNode, addClass, getTpImg, hasControlKey } from "../../ui/src/dom.mjs";
import { isEnableConf } from "../../ui/src/utils.mjs";
import { clearRowAnimate, moveRowAnimateToTb } from "../../ui/src/anime.mjs";
import { VxeLoading } from "../../loading/index.mjs";
import { VxeUI, renderEmptyElement } from "../../../../@vxe-ui/core/es/index.esm.mjs";
import { useSize } from "../../../../@vxe-ui/core/es/src/useFns.mjs";
import { globalResize } from "../../../../@vxe-ui/core/es/src/resize.mjs";
import { globalEvents, createEvent } from "../../../../@vxe-ui/core/es/src/event.mjs";
const { menus, getConfig, getI18n, getIcon } = VxeUI;
function getNodeUniqueId() {
  return XEUtils.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: ""
  };
}
const VxeTreeComponent = 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.uniqueId();
    const { computeSize } = useSize(props);
    const refElem = ref();
    const refHeaderWrapperElem = ref();
    const refFooterWrapperElem = ref();
    const refVirtualWrapper = ref();
    const refVirtualBody = ref();
    const refDragNodeLineElem = ref();
    const refDragTipElem = ref();
    const crossTreeDragNodeInfo = getCrossTreeDragNodeInfo();
    const internalData = createInternalData();
    const reactData = reactive(createReactData());
    const refMaps = {
      refElem
    };
    const computeTitleField = computed(() => {
      return props.titleField || "title";
    });
    const computeKeyField = computed(() => {
      return props.keyField || "id";
    });
    const computeValueField = computed(() => {
      const keyField = computeKeyField.value;
      return props.valueField || keyField;
    });
    const computeParentField = computed(() => {
      return props.parentField || "parentId";
    });
    const computeChildrenField = computed(() => {
      return props.childrenField || "children";
    });
    const computeMapChildrenField = computed(() => {
      return props.mapChildrenField || "mapChildren";
    });
    const computeHasChildField = computed(() => {
      return props.hasChildField || "hasChild";
    });
    const computeVirtualYOpts = computed(() => {
      return Object.assign({}, getConfig().tree.virtualYConfig, props.virtualYConfig);
    });
    const computeIsRowCurrent = computed(() => {
      const nodeOpts = computeNodeOpts.value;
      const { isCurrent } = nodeOpts;
      if (XEUtils.isBoolean(isCurrent)) {
        return isCurrent;
      }
      return props.isCurrent;
    });
    const computeIsRowHover = computed(() => {
      const nodeOpts = computeNodeOpts.value;
      const { isHover } = nodeOpts;
      if (XEUtils.isBoolean(isHover)) {
        return isHover;
      }
      return props.isHover;
    });
    const computeRadioOpts = computed(() => {
      return Object.assign({ showIcon: true }, getConfig().tree.radioConfig, props.radioConfig);
    });
    const computeCheckboxOpts = computed(() => {
      return Object.assign({ showIcon: true }, getConfig().tree.checkboxConfig, props.checkboxConfig);
    });
    const computeNodeOpts = computed(() => {
      return Object.assign({}, getConfig().tree.nodeConfig, props.nodeConfig);
    });
    const computeLoadingOpts = computed(() => {
      return Object.assign({}, getConfig().tree.loadingConfig, props.loadingConfig);
    });
    const computeDragOpts = computed(() => {
      return Object.assign({}, getConfig().tree.dragConfig, props.dragConfig);
    });
    const computeMenuOpts = computed(() => {
      return Object.assign({}, getConfig().tree.menuConfig, props.menuConfig);
    });
    const computeTreeStyle = computed(() => {
      const { customHeight, customMinHeight, customMaxHeight } = reactData;
      const stys = {};
      if (customHeight) {
        stys.height = toCssUnit(customHeight);
      }
      if (customMinHeight) {
        stys.minHeight = toCssUnit(customMinHeight);
      }
      if (customMaxHeight) {
        stys.maxHeight = toCssUnit(customMaxHeight);
      }
      return stys;
    });
    const computeFilterOpts = 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.get(node, valueField);
      return 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.isArray(nodeKeys)) {
          nodeKeys = [nodeKeys];
        }
        const nodeList = [];
        nodeKeys.forEach((nodeKey) => {
          const nodeid = enNodeValue(nodeKey);
          const nodeItem = nodeMaps[nodeid];
          if (nodeItem) {
            nodeList.push(nodeItem.item);
          }
        });
        handleCheckedCheckboxNode(nodeList, checked);
      }
      return 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.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, 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 (isScale(val)) {
            num = Math.floor((XEUtils.toInteger(val) || 1) / 100 * parentHeight);
          } else {
            num = XEUtils.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.set(item, valueField, nodeid);
        }
        return item;
      }));
    };
    const cacheNodeMap = () => {
      const { treeFullData } = internalData;
      const valueField = computeValueField.value;
      const childrenField = computeChildrenField.value;
      const keyMaps = {};
      XEUtils.eachTree(treeFullData, (item, index, items, path, parent2, nodes) => {
        let nodeid = getNodeId(item);
        if (!nodeid) {
          nodeid = getNodeUniqueId();
          XEUtils.set(item, valueField, nodeid);
        }
        keyMaps[nodeid] = {
          item,
          index,
          $index: -1,
          _index: -1,
          items,
          parent: parent2,
          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.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.searchTree(treeFullData, handleSearch, {
            original: true,
            isEvery: true,
            children: childrenField,
            mapChildren: mapChildrenField
          });
          fullList = treeList;
        } else {
          fullList = treeFullData.filter(handleSearch);
        }
        internalData.lastFilterValue = filterStr;
        nextTick(() => {
          if (autoExpandAll) {
            $xeTree.setAllExpandNode(true).then(() => {
              if (afterFilterMethod) {
                afterFilterMethod(bafParams);
              }
            });
          } else {
            if (afterFilterMethod) {
              afterFilterMethod(bafParams);
            }
          }
        });
      } else {
        if (transform) {
          treeList = XEUtils.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);
            }
            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.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.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.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)) {
          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;
      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 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;
        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.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.isNumber(scrollLeft)) {
          scrollTop = scrollLeft.top;
          scrollLeft = scrollLeft.left;
        }
      }
      if (scrollBodyElem) {
        if (XEUtils.isNumber(scrollLeft)) {
          scrollBodyElem.scrollLeft = scrollLeft;
        }
        if (XEUtils.isNumber(scrollTop)) {
          scrollBodyElem.scrollTop = scrollTop;
        }
      }
      if (reactData.scrollYLoad) {
        return new Promise((resolve) => {
          setTimeout(() => {
            nextTick(() => {
              resolve();
            });
          }, 50);
        });
      }
      return 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 = getPaddingTopBottomSize(parentEl);
          reactData.parentHeight = Math.max(headHeight + footHeight + rowHeight, parentEl.clientHeight - parentPaddingSize - headHeight - footHeight);
        }
        updateHeight();
        return computeScrollLoad().then(() => {
          updateHeight();
          updateYSpace();
        });
      }
      return 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 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 = 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 ? 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 (VxeUI.contextMenu) {
            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.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 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();
        }
      });
    };
    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];
            }
          });
        }
      }
      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;
              }
            }
          }
        });
      } else {
        validNodes.forEach((item) => {
          const itemNodeId = getNodeId(item);
          if (treeExpandedMaps[itemNodeId]) {
            delete treeExpandedMaps[itemNodeId];
          }
        });
      }
      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.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.eachTree(XEUtils.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.keys(selectCheckboxMaps);
      const value = nodeids.map(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.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 = 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 nextTick();
      },
      getCurrentNodeId() {
        const { currentNode } = reactData;
        if (currentNode) {
          return 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[enNodeValue(nodeKey)];
        reactData.currentNode = nodeItem ? nodeItem.item : null;
        return nextTick();
      },
      setCurrentNode(node) {
        reactData.currentNode = node;
        return nextTick();
      },
      clearRadioNode() {
        reactData.selectRadioKey = null;
        emitRadioMode(null);
        return 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 = enNodeValue(nodeKey);
        emitRadioMode(nodeKey);
        return nextTick();
      },
      setRadioNode(node) {
        if (node) {
          const nodeid = getNodeId(node);
          reactData.selectRadioKey = nodeid;
          emitRadioMode(deNodeValue(nodeid));
        } else {
          emitRadioMode(null);
        }
        return nextTick();
      },
      setCheckboxNode(nodeList, checked) {
        if (nodeList) {
          if (!XEUtils.isArray(nodeList)) {
            nodeList = [nodeList];
          }
          handleCheckedCheckboxNode(nodeList, checked);
        }
        emitCheckboxMode($xeTree.getCheckboxNodeIds());
        return nextTick();
      },
      setCheckboxByNodeId(nodeKeys, selected) {
        handleSetCheckboxByNodeId(nodeKeys, selected);
        emitCheckboxMode($xeTree.getCheckboxNodeIds());
        return nextTick();
      },
      getCheckboxNodeIds() {
        const { selectCheckboxMaps } = internalData;
        const nodeKeys = [];
        XEUtils.each(selectCheckboxMaps, (item, nodeId) => {
          nodeKeys.push(deNodeValue(nodeId));
        });
        return nodeKeys;
      },
      getCheckboxNodes() {
        const { nodeMaps, selectCheckboxMaps } = internalData;
        const list = [];
        XEUtils.each(selectCheckboxMaps, (item, nodeid) => {
          const nodeItem = nodeMaps[nodeid];
          if (nodeItem) {
            list.push(nodeItem.item);
          }
        });
        return list;
      },
      clearCheckboxNode() {
        internalData.indeterminateRowMaps = {};
        internalData.selectCheckboxMaps = {};
        reactData.updateCheckboxFlag++;
        emitCheckboxMode([]);
        updateCheckboxStatus();
        return nextTick().then(() => {
          return { checkNodeKeys: [], checkNodes: [] };
        });
      },
      setAllCheckboxNode(checked) {
        const { transform } = props;
        const selectMaps = {};
        const childrenField = computeChildrenField.value;
        const mapChildrenField = computeMapChildrenField.value;
        const checkKeys = [];
        const checkNodes = [];
        if (checked) {
          XEUtils.eachTree(internalData.afterTreeList, (node) => {
            const nodeid = getNodeId(node);
            checkKeys.push(nodeid);
            checkNodes.push(node);
            selectMaps[nodeid] = true;
          }, { children: transform ? mapChildrenField : childrenField });
        }
        internalData.indeterminateRowMaps = {};
        internalData.selectCheckboxMaps = selectMaps;
        reactData.updateCheckboxFlag++;
        updateCheckboxStatus();
        return nextTick().then(() => {
          return { checkNodeKeys: checkKeys, checkNodes };
        });
      },
      clearExpandNode() {
        return treeMethods.clearAllExpandNode();
      },
      clearAllExpandNode() {
        const { nodeMaps, scrollYStore } = internalData;
        XEUtils.each(nodeMaps, (nodeItem) => {
          nodeItem.treeLoaded = false;
        });
        internalData.treeExpandedMaps = {};
        reactData.updateExpandedFlag++;
        reactData.topSpaceHeight = 0;
        scrollYStore.startIndex = 0;
        scrollYStore.endIndex = 1;
        handleTreeToList();
        handleData();
        return recalculate();
      },
      setExpandByNodeId(nodeKeys, expanded) {
        const { treeExpandedMaps } = internalData;
        if (nodeKeys) {
          if (!XEUtils.isArray(nodeKeys)) {
            nodeKeys = [nodeKeys];
          }
          nodeKeys.forEach((nodeKey) => {
            const nodeid = enNodeValue(nodeKey);
            handleSetExpand(nodeid, expanded, treeExpandedMaps);
          });
          reactData.updateExpandedFlag++;
        }
        handleTreeToList();
        handleData();
        return recalculate();
      },
      getExpandNodeIds() {
        const { treeExpandedMaps } = internalData;
        const nodeKeys = [];
        XEUtils.each(treeExpandedMaps, (item, nodeId) => {
          nodeKeys.push(deNodeValue(nodeId));
        });
        return nodeKeys;
      },
      getExpandNodes() {
        const { nodeMaps, treeExpandedMaps } = internalData;
        const list = [];
        XEUtils.each(treeExpandedMaps, (item, nodeid) => {
          const nodeItem = nodeMaps[nodeid];
          if (nodeItem) {
            list.push(nodeItem.item);
          }
        });
        return list;
      },
      setExpandNode(nodes, expanded) {
        const { treeExpandedMaps } = internalData;
        if (nodes) {
          if (!XEUtils.isArray(nodes)) {
            nodes = [nodes];
          }
          nodes.forEach((node) => {
            const nodeid = getNodeId(node);
            handleSetExpand(nodeid, expanded, treeExpandedMaps);
          });
          reactData.updateExpandedFlag++;
        }
        handleTreeToList();
        handleData();
        return recalculate();
      },
      toggleExpandByNodeId(nodeKeys) {
        const { treeExpandedMaps } = internalData;
        if (nodeKeys) {
          if (!XEUtils.isArray(nodeKeys)) {
            nodeKeys = [nodeKeys];
          }
          nodeKeys.forEach((nodeKey) => {
            const nodeid = enNodeValue(nodeKey);
            handleSetExpand(nodeid, !treeExpandedMaps[`${nodeid}`], treeExpandedMaps);
          });
          reactData.updateExpandedFlag++;
        }
        handleTreeToList();
        handleData();
        return recalculate();
      },
      toggleExpandNode(nodes) {
        const { treeExpandedMaps } = internalData;
        if (nodes) {
          if (!XEUtils.isArray(nodes)) {
            nodes = [nodes];
          }
          nodes.forEach((node) => {
            const nodeid = getNodeId(node);
            handleSetExpand(nodeid, !treeExpandedMaps[nodeid], treeExpandedMaps);
          });
          reactData.updateExpandedFlag++;
        }
        handleTreeToList();
        handleData();
        return recalculate();
      },
      setAllExpandNode(expanded) {
        const { transform } = props;
        const { scrollYLoad } = reactData;
        const { scrollYStore, treeExpandedMaps } = internalData;
        const childrenField = computeChildrenField.value;
        const mapChildrenField = computeMapChildrenField.value;
        if (expanded) {
          XEUtils.eachTree(internalData.afterTreeList, (node) => {
            const childList = XEUtils.get(node, childrenField);
            const hasChild = childList && childList.length;
            if (hasChild) {
              const nodeid = getNodeId(node);
              treeExpandedMaps[nodeid] = true;
            }
          }, { children: transform ? mapChildrenField : childrenField });
        } else {
          internalData.treeExpandedMaps = {};
        }
        reactData.updateExpandedFlag++;
        reactData.topSpaceHeight = 0;
        scrollYStore.startIndex = 0;
        scrollYStore.endIndex = 1;
        handleTreeToList();
        handleData();
        return recalculate().then(() => {
          if (scrollYLoad) {
            loadYData();
          }
          return recalculate();
        });
      },
      reloadExpandNode(node) {
        const { lazy } = props;
        if (lazy) {
          treeMethods.clearExpandLoaded(node);
          return handleAsyncTreeExpandChilds(node);
        }
        return recalculate();
      },
      clearExpandLoaded(node) {
        const { lazy } = props;
        const { nodeMaps } = internalData;
        if (lazy) {
          const nodeItem = nodeMaps[getNodeId(node)];
          if (nodeItem) {
            nodeItem.treeLoaded = false;
          }
        }
        return recalculate();
      },
      /**
       * 用于树结构，给行数据加载子节点
       */
      loadChildrenNode(node, childRecords) {
        const { lazy, transform } = props;
        const { nodeMaps } = internalData;
        if (!lazy) {
          return Promise.resolve([]);
        }
        const childrenField = computeChildrenField.value;
        const mapChildrenField = computeMapChildrenField.value;
        const parentNodeItem = nodeMaps[getNodeId(node)];
        const parentLevel = parentNodeItem ? parentNodeItem.level : 0;
        const parentNodes = parentNodeItem ? parentNodeItem.nodes : [];
        return createNode(childRecords).then((nodeList) => {
          XEUtils.eachTree(nodeList, (childRow, index, items, path, parent2, nodes) => {
            const itemNodeId = getNodeId(childRow);
            nodeMaps[itemNodeId] = {
              item: childRow,
              index: -1,
              $index: -1,
              _index: -1,
              items,
              parent: parent2 || parentNodeItem.item,
              nodes: parentNodes.concat(nodes),
              level: parentLevel + nodes.length,
              treeIndex: -1,
              lineCount: 0,
              treeLoaded: false
            };
          }, { children: childrenField });
          node[childrenField] = nodeList;
          if (transform) {
            node[mapChildrenField] = nodeList;
          }
          updateAfterDataIndex();
          return nodeList;
        });
      },
      isExpandByNode,
      isCheckedByRadioNodeId,
      isCheckedByRadioNode,
      isCheckedByCheckboxNodeId,
      isIndeterminateByCheckboxNode,
      isCheckedByCheckboxNode,
      getCheckboxIndeterminateNodes() {
        const { nodeMaps, indeterminateRowMaps } = internalData;
        const list = [];
        XEUtils.each(indeterminateRowMaps, (item, nodeid) => {
          const nodeItem = nodeMaps[nodeid];
          if (nodeItem) {
            list.push(nodeItem.item);
          }
        });
        return list;
      },
      recalculate,
      getFullData() {
        const { treeFullData } = internalData;
        return treeFullData.slice(0);
      },
      scrollTo: handleScrollTo,
      scrollToNode(node) {
        return $xeTree.scrollToNodeId(getNodeId(node));
      },
      scrollToNodeId(nodeid) {
        const { transform } = props;
        const { scrollYStore, afterTreeList } = internalData;
        const childrenField = computeChildrenField.value;
        const mapChildrenField = computeMapChildrenField.value;
        const scrollBodyElem = refVirtualWrapper.value;
        if (nodeid && scrollBodyElem) {
          if (transform) {
            const matchObj = XEUtils.findTree(afterTreeList, (item) => getNodeId(item) === nodeid, { children: transform ? mapChildrenField : childrenField });
            if (matchObj) {
              return $xeTree.setExpandNode(matchObj.nodes, true).then(() => {
                const itemIndex = XEUtils.findIndexOf(internalData.afterVisibleList, (item) => getNodeId(item) === nodeid);
                if (itemIndex > -1) {
                  const targetTop = Math.max(0, (itemIndex - 1) * scrollYStore.rowHeight);
                  return handleScrollTo(scrollBodyElem.scrollLeft, targetTop);
                }
              });
            }
          } else {
            const itemEl = scrollBodyElem.querySelector(`.vxe-tree--node-wrapper[nodeid="${nodeid}"]`);
            if (itemEl) {
              return handleScrollTo(scrollBodyElem.scrollLeft, itemEl.offsetTop);
            }
          }
        }
        return recalculate();
      },
      clearScroll
    };
    const clearNodeDropOrigin = () => {
      const el = refElem.value;
      if (el) {
        const clss = "node--drag-origin";
        XEUtils.arrayEach(el.querySelectorAll(`.${clss}`), (elem) => {
          elem.draggable = false;
          removeClass(elem, clss);
        });
      }
    };
    const updateNodeDropOrigin = (node) => {
      const el = refElem.value;
      if (el) {
        const clss = "node--drag-origin";
        const nodeid = getNodeId(node);
        XEUtils.arrayEach(el.querySelectorAll(`.vxe-tree--node-wrapper[nodeid="${nodeid}"]`), (elem) => {
          addClass(elem, clss);
        });
      }
    };
    const updateRowDropTipContent = (itemEl) => {
      const { dragNode } = reactData;
      const dragOpts = computeDragOpts.value;
      const { tooltipMethod } = dragOpts;
      const rTooltipMethod = tooltipMethod;
      let tipContent = "";
      if (rTooltipMethod) {
        const rtParams = {
          $tree: $xeTree,
          node: dragNode
        };
        tipContent = `${rTooltipMethod(rtParams) || ""}`;
      } else {
        tipContent = getI18n("vxe.tree.dragTip", [itemEl.textContent || ""]);
      }
      reactData.dragTipText = tipContent;
    };
    const hideDropTip = () => {
      const rdTipEl = refDragTipElem.value;
      const rdLineEl = refDragNodeLineElem.value;
      if (rdTipEl) {
        rdTipEl.style.display = "";
      }
      if (rdLineEl) {
        rdLineEl.style.display = "";
      }
    };
    const clearCrossTreeDragStatus = () => {
      crossTreeDragNodeInfo.node = null;
    };
    const clearDragStatus = () => {
      const { dragNode } = reactData;
      if (dragNode) {
        hideDropTip();
        clearNodeDropOrigin();
        clearCrossTreeDragStatus();
        reactData.dragNode = null;
      }
    };
    const handleNodeDragMousedownEvent = (evnt, params) => {
      evnt.stopPropagation();
      const { node } = params;
      const dragConfig = computeDragOpts.value;
      const { trigger, dragStartMethod } = dragConfig;
      const dragEl = evnt.currentTarget;
      const itemEl = trigger === "node" ? dragEl : dragEl.parentElement.parentElement;
      clearNodeDropOrigin();
      if (dragStartMethod && !dragStartMethod(params)) {
        itemEl.draggable = false;
        reactData.dragNode = null;
        hideDropTip();
        return;
      }
      reactData.dragNode = node;
      itemEl.draggable = true;
      updateNodeDropOrigin(node);
      updateRowDropTipContent(itemEl);
      dispatchEvent("node-dragstart", params, evnt);
    };
    const handleNodeDragMouseupEvent = () => {
      clearDragStatus();
    };
    const showDropTip = (evnt, itemEl, showLine, dragPos) => {
      const wrapperEl = refElem.value;
      if (!wrapperEl) {
        return;
      }
      const { prevDragToChild } = internalData;
      const wrapperRect = wrapperEl.getBoundingClientRect();
      const wrapperHeight = wrapperEl.clientHeight;
      if (itemEl) {
        const rdLineEl = refDragNodeLineElem.value;
        if (rdLineEl) {
          if (showLine) {
            const trRect = itemEl.getBoundingClientRect();
            let itemHeight = itemEl.clientHeight;
            const offsetTop = Math.max(1, trRect.y - wrapperRect.y);
            if (offsetTop + itemHeight > wrapperHeight) {
              itemHeight = wrapperHeight - offsetTop;
            }
            rdLineEl.style.display = "block";
            rdLineEl.style.top = `${offsetTop}px`;
            rdLineEl.style.height = `${itemHeight}px`;
            rdLineEl.setAttribute("drag-pos", dragPos);
            rdLineEl.setAttribute("drag-to-child", prevDragToChild ? "y" : "n");
          } else {
            rdLineEl.style.display = "";
          }
        }
      }
      const rdTipEl = refDragTipElem.value;
      if (rdTipEl) {
        rdTipEl.style.display = "block";
        rdTipEl.style.top = `${Math.min(wrapperEl.clientHeight - wrapperEl.scrollTop - rdTipEl.clientHeight, evnt.clientY - wrapperRect.y)}px`;
        rdTipEl.style.left = `${Math.min(wrapperEl.clientWidth - wrapperEl.scrollLeft - rdTipEl.clientWidth - 1, evnt.clientX - wrapperRect.x)}px`;
        rdTipEl.setAttribute("drag-status", showLine ? prevDragToChild ? "sub" : "normal" : "disabled");
      }
    };
    const clearNodeDragData = () => {
      const wrapperEl = refElem.value;
      const dtClss = [".vxe-tree--node-wrapper"];
      hideDropTip();
      clearNodeDropOrigin();
      clearRowAnimate(wrapperEl, dtClss);
      internalData.prevDragToChild = false;
      reactData.dragNode = null;
    };
    const handleNodeDragSwapEvent = (evnt, dragNode, prevDragNode, prevDragPos, prevDragToChild) => {
      const { transform } = props;
      const { nodeMaps, treeFullData, afterVisibleList } = internalData;
      const dragConfig = computeDragOpts.value;
      const { animation, isCrossDrag, isPeerDrag, isSelfToChildDrag, dragEndMethod, dragToChildMethod } = dragConfig;
      const dEndMethod = dragEndMethod || (dragConfig ? dragConfig.dragEndMethod : null);
      const keyField = computeKeyField.value;
      const parentField = computeParentField.value;
      const childrenField = computeChildrenField.value;
      const mapChildrenField = computeMapChildrenField.value;
      const dragOffsetIndex = prevDragPos === "bottom" ? 1 : 0;
      const el = refElem.value;
      const errRest = {
        status: false
      };
      if (!(el && prevDragNode && dragNode)) {
        return Promise.resolve(errRest);
      }
      if (prevDragNode !== dragNode) {
        const dragParams = {
          oldNode: dragNode,
          newNode: prevDragNode,
          dragNode,
          dragPos: prevDragPos,
          dragToChild: !!prevDragToChild,
          offsetIndex: dragOffsetIndex
        };
        const dragNodeid = getNodeId(dragNode);
        const dragNodeRest = nodeMaps[dragNodeid] || {};
        const _dragNodeIndex = dragNodeRest._index;
        let dragNodeHeight = 0;
        let dragOffsetTop = -1;
        if (animation) {
          const prevItemEl = el.querySelector(`.vxe-tree--node-wrapper[nodeid="${prevDragNode}"]`);
          const oldItemEl = el.querySelector(`.vxe-tree--node-wrapper[nodeid="${dragNodeid}"]`);
          const targetItemEl = prevItemEl || oldItemEl;
          if (targetItemEl) {
            dragNodeHeight = targetItemEl.offsetHeight;
          }
          if (oldItemEl) {
            dragOffsetTop = oldItemEl.offsetTop;
          }
        }
        let oafIndex = -1;
        let nafIndex = -1;
        const oldAllMaps = {};
        let isSelfToChildStatus = false;
        const oldRest = dragNodeRest;
        const newNodeid = getNodeId(prevDragNode);
        const newRest = nodeMaps[newNodeid];
        if (transform) {
          if (oldRest && newRest) {
            const { level: oldLevel } = oldRest;
            const { level: newLevel } = newRest;
            XEUtils.eachTree([dragNode], (item) => {
              oldAllMaps[getNodeId(item)] = item;
            }, { children: mapChildrenField });
            if (oldLevel && newLevel) {
              if (isPeerDrag && !isCrossDrag) {
                if (oldRest.item[parentField] !== newRest.item[parentField]) {
                  clearNodeDragData();
                  clearCrossTreeDragStatus();
                  return errRest;
                }
              } else {
                if (!isCrossDrag) {
                  clearNodeDragData();
                  clearCrossTreeDragStatus();
                  return errRest;
                }
                if (oldAllMaps[newNodeid]) {
                  isSelfToChildStatus = true;
                  if (!(isCrossDrag && isSelfToChildDrag)) {
                    if (VxeUI.modal) {
                      VxeUI.modal.message({
                        status: "error",
                        content: getI18n("vxe.error.treeDragChild")
                      });
                    }
                    clearNodeDragData();
                    clearCrossTreeDragStatus();
                    return errRest;
                  }
                }
              }
            } else if (oldLevel) {
              if (!isCrossDrag) {
                clearNodeDragData();
                clearCrossTreeDragStatus();
                return errRest;
              }
            } else if (newLevel) {
              if (!isCrossDrag) {
                clearNodeDragData();
                clearCrossTreeDragStatus();
                return errRest;
              }
              if (oldAllMaps[newNodeid]) {
                isSelfToChildStatus = true;
                if (!(isCrossDrag && isSelfToChildDrag)) {
                  if (VxeUI.modal) {
                    VxeUI.modal.message({
                      status: "error",
                      content: getI18n("vxe.error.treeDragChild")
                    });
                  }
                  clearNodeDragData();
                  clearCrossTreeDragStatus();
                  return errRest;
                }
              }
            } else ;
          }
        }
        const isDragToChildFlag = isSelfToChildDrag && dragToChildMethod ? dragToChildMethod(dragParams) : prevDragToChild;
        return Promise.resolve(dEndMethod ? dEndMethod(dragParams) : true).then((status) => {
          if (!status) {
            return errRest;
          }
          if (transform) {
            if (oldRest && newRest) {
              const fullList = XEUtils.toTreeArray(internalData.afterTreeList, {
                key: keyField,
                parentKey: parentField,
                children: mapChildrenField
              });
              const otfIndex = $xeTree.findNodeIndexOf(fullList, dragNode);
              fullList.splice(otfIndex, 1);
              const ptfIndex = $xeTree.findNodeIndexOf(fullList, prevDragNode);
              const ntfIndex = ptfIndex + dragOffsetIndex;
              fullList.splice(ntfIndex, 0, dragNode);
              if (isSelfToChildStatus && (isCrossDrag && isSelfToChildDrag)) {
                XEUtils.each(dragNode[childrenField], (childNode) => {
                  childNode[parentField] = dragNode[parentField];
                });
              }
              dragNode[parentField] = isDragToChildFlag ? prevDragNode[keyField] : prevDragNode[parentField];
              internalData.treeFullData = XEUtils.toArrayTree(fullList, {
                key: keyField,
                parentKey: parentField,
                children: childrenField,
                mapChildren: mapChildrenField
              });
            }
          } else {
            oafIndex = $xeTree.findNodeIndexOf(afterVisibleList, dragNode);
            const otfIndex = $xeTree.findNodeIndexOf(treeFullData, dragNode);
            afterVisibleList.splice(oafIndex, 1);
            treeFullData.splice(otfIndex, 1);
            const pafIndex = $xeTree.findNodeIndexOf(afterVisibleList, prevDragNode);
            const ptfIndex = $xeTree.findNodeIndexOf(treeFullData, prevDragNode);
            nafIndex = pafIndex + dragOffsetIndex;
            const ntfIndex = ptfIndex + dragOffsetIndex;
            afterVisibleList.splice(nafIndex, 0, dragNode);
            treeFullData.splice(ntfIndex, 0, dragNode);
          }
          cacheNodeMap();
          handleData(transform);
          if (!transform) {
            updateAfterDataIndex();
          }
          updateCheckboxStatus();
          if (reactData.scrollYLoad) {
            updateYSpace();
          }
          if (evnt) {
            dispatchEvent("node-dragend", {
              oldNode: dragNode,
              newNode: prevDragNode,
              dragNode,
              dragPos: prevDragPos,
              dragToChild: isDragToChildFlag,
              offsetIndex: dragOffsetIndex,
              _index: {
                newIndex: nafIndex,
                oldIndex: oafIndex
              }
            }, evnt);
          }
          return nextTick().then(() => {
            if (animation) {
              const { treeList } = reactData;
              const { nodeMaps: nodeMaps2 } = internalData;
              const dragNodeRest2 = nodeMaps2[dragNodeid];
              const _newNodeIndex = dragNodeRest2._index;
              const firstNode = treeList[0];
              const firstNodeRest = nodeMaps2[getNodeId(firstNode)];
              const wrapperEl = el;
              if (firstNodeRest) {
                const _firstNodeIndex = firstNodeRest._index;
                const _lastNodeIndex = _firstNodeIndex + treeList.length;
                let rsIndex = -1;
                let reIndex = -1;
                let offsetRate = 1;
                if (_dragNodeIndex < _firstNodeIndex) {
                  rsIndex = 0;
                  reIndex = _newNodeIndex - _firstNodeIndex;
                } else if (_dragNodeIndex > _lastNodeIndex) {
                  const $newNodeIndex = dragNodeRest2.$index;
                  rsIndex = $newNodeIndex + 1;
                  reIndex = treeList.length;
                  offsetRate = -1;
                } else {
                  if (_newNodeIndex > _dragNodeIndex) {
                    rsIndex = _dragNodeIndex - _firstNodeIndex;
                    reIndex = rsIndex + _newNodeIndex - _dragNodeIndex;
                  } else {
                    rsIndex = _newNodeIndex - _firstNodeIndex;
                    reIndex = rsIndex + _dragNodeIndex - _newNodeIndex + 1;
                    offsetRate = -1;
                  }
                }
                const dragRangeList = treeList.slice(rsIndex, reIndex);
                if (dragRangeList.length) {
                  const dtClss = [];
                  dragRangeList.forEach((item) => {
                    const nodeid = getNodeId(item);
                    dtClss.push(`.vxe-tree--node-wrapper[nodeid="${nodeid}"]`);
                  });
                  const dtTrList = wrapperEl.querySelectorAll(dtClss.join(","));
                  moveRowAnimateToTb(dtTrList, offsetRate * dragNodeHeight);
                }
              }
              const drClss = [`.vxe-tree--node-wrapper[nodeid="${dragNodeid}"]`];
              const newDtTrList = wrapperEl.querySelectorAll(drClss.join(","));
              const newTrEl = newDtTrList[0];
              if (dragOffsetTop > -1 && newTrEl) {
                moveRowAnimateToTb(newDtTrList, dragOffsetTop - newTrEl.offsetTop);
              }
            }
            recalculate();
          }).then(() => {
            return {
              status: true
            };
          });
        }).catch(() => {
          return errRest;
        }).then((rest) => {
          clearNodeDragData();
          clearCrossTreeDragStatus();
          return rest;
        });
      }
      clearNodeDragData();
      clearCrossTreeDragStatus();
      return Promise.resolve(errRest);
    };
    const handleNodeDragDragstartEvent = (evnt) => {
      if (evnt.dataTransfer) {
        evnt.dataTransfer.setDragImage(getTpImg(), 0, 0);
      }
    };
    const handleNodeDragDragendEvent = (evnt) => {
      const { lazy } = props;
      const { dragNode } = reactData;
      const { nodeMaps, prevDragNode, prevDragPos, prevDragToChild } = internalData;
      const hasChildField = computeHasChildField.value;
      if (lazy && prevDragToChild) {
        const newNodeid = getNodeId(prevDragNode);
        const nodeRest = nodeMaps[newNodeid];
        if (prevDragNode[hasChildField]) {
          if (nodeRest && nodeRest.treeLoaded) {
            handleNodeDragSwapEvent(evnt, dragNode, prevDragNode, prevDragPos, prevDragToChild);
          }
        } else {
          handleNodeDragSwapEvent(evnt, dragNode, prevDragNode, prevDragPos, prevDragToChild);
        }
      } else {
        handleNodeDragSwapEvent(evnt, dragNode, prevDragNode, prevDragPos, prevDragToChild);
      }
    };
    const handleNodeDragDragoverEvent = (evnt) => {
      const { lazy, transform } = props;
      const { dragNode } = reactData;
      const { nodeMaps } = internalData;
      const dragConfig = computeDragOpts.value;
      const parentField = computeParentField.value;
      const hasChildField = computeHasChildField.value;
      const { isCrossDrag, isPeerDrag, isToChildDrag } = dragConfig;
      if (!dragNode && !isCrossDrag) {
        evnt.preventDefault();
      }
      const isControlKey = hasControlKey(evnt);
      const itemEl = evnt.currentTarget;
      const nodeid = itemEl.getAttribute("nodeid") || "";
      const nodeItem = nodeMaps[nodeid];
      if (nodeItem) {
        evnt.preventDefault();
        const node = nodeItem.item;
        const offsetY = evnt.clientY - itemEl.getBoundingClientRect().y;
        const dragPos = offsetY < itemEl.clientHeight / 2 ? "top" : "bottom";
        internalData.prevDragToChild = !!(transform && (isCrossDrag && isToChildDrag) && isControlKey);
        internalData.prevDragNode = node;
        internalData.prevDragPos = dragPos;
        if (dragNode && getNodeId(dragNode) === nodeid || isControlKey && lazy && node[hasChildField] && nodeItem && !nodeItem.treeLoaded || !isCrossDrag && transform && (isPeerDrag ? dragNode[parentField] !== node[parentField] : nodeItem.level)) {
          showDropTip(evnt, itemEl, false, dragPos);
          return;
        }
        showDropTip(evnt, itemEl, true, dragPos);
        dispatchEvent("node-dragover", {
          oldRNode: dragNode,
          targetNode: node,
          dragPos
        }, evnt);
      }
    };
    const treePrivateMethods = {
      handleData,
      cacheNodeMap,
      updateAfterDataIndex,
      updateCheckboxStatus,
      updateYSpace,
      findNodeIndexOf(list, node) {
        return node ? XEUtils.findIndexOf(list, (item) => $xeTree.eqNode(item, node)) : -1;
      },
      eqNode(node1, node2) {
        if (node1 && node2) {
          if (node1 === node2) {
            return true;
          }
          return getNodeId(node1) === getNodeId(node2);
        }
        return false;
      },
      handleCrossTreeNodeDragCancelEvent() {
        clearNodeDragData();
        clearCrossTreeDragStatus();
      },
      /**
       * 处理跨树拖拽完成
       */
      handleCrossTreeNodeDragFinishEvent() {
      },
      /**
       * 处理跨树拖至新的空树
       */
      handleCrossTreeNodeDragInsertEvent() {
      },
      /**
       * 处理跨树拖插入
       */
      handleCrossTreeNodeDragoverEmptyEvent() {
      },
      hideCrossTreeNodeDropClearStatus() {
        hideDropTip();
      }
    };
    Object.assign($xeTree, treeMethods, treePrivateMethods);
    const renderDragIcon = (node, nodeid) => {
      const { drag, transform } = props;
      const { nodeMaps } = internalData;
      const dragOpts = computeDragOpts.value;
      const { showIcon, isPeerDrag, isCrossDrag, visibleMethod } = dragOpts;
      const params = { node, $tree: $xeTree };
      if (drag && showIcon && transform && (!visibleMethod || visibleMethod(params))) {
        const nodeItem = nodeMaps[nodeid];
        if (nodeItem && (isPeerDrag || isCrossDrag || !nodeItem.level)) {
          const dragConfig = computeDragOpts.value;
          const { icon, trigger, disabledMethod } = dragConfig;
          const isDisabled = disabledMethod && disabledMethod(params);
          const ons = {};
          if (trigger !== "node") {
            ons.onMousedown = (evnt) => {
              if (!isDisabled) {
                handleNodeDragMousedownEvent(evnt, params);
              }
            };
            ons.onMouseup = handleNodeDragMouseupEvent;
          }
          return h("div", Object.assign({ class: ["vxe-tree--drag-handle", {
            "is--disabled": isDisabled
          }] }, ons), [
            h("i", {
              class: icon || getIcon().TREE_DRAG
            })
          ]);
        }
      }
      return renderEmptyElement();
    };
    const renderRadio = (node, nodeid, isChecked) => {
      const { showRadio } = props;
      const radioOpts = computeRadioOpts.value;
      const { showIcon, checkMethod, visibleMethod } = radioOpts;
      const isVisible = !visibleMethod || visibleMethod({ $tree: $xeTree, node });
      let isDisabled = !!checkMethod;
      if (showRadio && showIcon && isVisible) {
        if (checkMethod) {
          isDisabled = !checkMethod({ $tree: $xeTree, node });
        }
        return h("div", {
          class: ["vxe-tree--radio-option", {
            "is--checked": isChecked,
            "is--disabled": isDisabled
          }],
          onClick: (evnt) => {
            if (!isDisabled) {
              changeRadioEvent(evnt, node);
            }
          }
        }, [
          h("span", {
            class: ["vxe-radio--icon", isChecked ? getIcon().RADIO_CHECKED : getIcon().RADIO_UNCHECKED]
          })
        ]);
      }
      return renderEmptyElement();
    };
    const renderCheckbox = (node, nodeid, isChecked) => {
      const { showCheckbox } = props;
      const checkboxOpts = computeCheckboxOpts.value;
      const { showIcon, checkMethod, visibleMethod } = checkboxOpts;
      const isIndeterminate = isIndeterminateByCheckboxNodeid(nodeid);
      const isVisible = !visibleMethod || visibleMethod({ $tree: $xeTree, node });
      let isDisabled = !!checkMethod;
      if (showCheckbox && showIcon && isVisible) {
        if (checkMethod) {
          isDisabled = !checkMethod({ $tree: $xeTree, node });
        }
        return h("div", {
          class: ["vxe-tree--checkbox-option", {
            "is--checked": isChecked,
            "is--indeterminate": isIndeterminate,
            "is--disabled": isDisabled
          }],
          onClick: (evnt) => {
            if (!isDisabled) {
              changeCheckboxEvent(evnt, node);
            }
          }
        }, [
          h("span", {
            class: ["vxe-checkbox--icon", isIndeterminate ? getIcon().CHECKBOX_INDETERMINATE : isChecked ? getIcon().CHECKBOX_CHECKED : getIcon().CHECKBOX_UNCHECKED]
          })
        ]);
      }
      return renderEmptyElement();
    };
    const renderNode = (node, nodeid) => {
      const { lazy, drag, transform, showRadio, showCheckbox, showLine, indent, iconOpen, iconClose, iconLoaded, showIcon } = props;
      const { currentNode, selectRadioKey, updateExpandedFlag } = reactData;
      const { afterTreeList, nodeMaps, treeExpandedMaps, treeExpandLazyLoadedMaps } = internalData;
      const childrenField = computeChildrenField.value;
      const titleField = computeTitleField.value;
      const hasChildField = computeHasChildField.value;
      const childList = XEUtils.get(node, childrenField);
      const hasChild = childList && childList.length;
      const iconSlot = slots.icon;
      const titleSlot = slots.title;
      const extraSlot = slots.extra;
      const isExpand = updateExpandedFlag && treeExpandedMaps[nodeid];
      const nodeItem = nodeMaps[nodeid];
      const nodeValue = XEUtils.get(node, titleField);
      const nLevel = nodeItem.level;
      let isRadioChecked = false;
      if (showRadio) {
        isRadioChecked = nodeid === String(selectRadioKey);
      }
      let isCheckboxChecked = false;
      if (showCheckbox) {
        isCheckboxChecked = isCheckedByCheckboxNodeId(nodeid);
      }
      let hasLazyChilds = false;
      let isLazyLoading = false;
      let isLazyLoaded = false;
      if (lazy) {
        isLazyLoading = !!treeExpandLazyLoadedMaps[nodeid];
        hasLazyChilds = node[hasChildField];
        isLazyLoaded = !!nodeItem.treeLoaded;
      }
      const prevNode = nodeItem.items[nodeItem.treeIndex - 1];
      const nParams = { node, isExpand };
      const itemOn = {
        onMousedown(evnt) {
          handleNodeMousedownEvent(evnt, node);
        },
        onMouseup: handleNodeDragMouseupEvent,
        onClick(evnt) {
          handleNodeClickEvent(evnt, node);
        },
        onDblclick(evnt) {
          handleNodeDblclickEvent(evnt, node);
        },
        onContextmenu(evnt) {
          handleContextmenuEvent(evnt, node);
        }
      };
      if (drag && transform) {
        itemOn.onDragstart = handleNodeDragDragstartEvent;
        itemOn.onDragend = handleNodeDragDragendEvent;
        itemOn.onDragover = handleNodeDragDragoverEvent;
      }
      return h("div", Object.assign({ key: nodeid, class: ["vxe-tree--node-wrapper", `node--level-${nLevel}`], nodeid }, itemOn), [
        h("div", {
          class: ["vxe-tree--node-item", {
            "is--current": currentNode && nodeid === getNodeId(currentNode),
            "is-radio--checked": isRadioChecked,
            "is-checkbox--checked": isCheckboxChecked
          }],
          style: {
            paddingLeft: `${nLevel * (indent || 1)}px`
          }
        }, [
          showLine ? h("div", {
            class: "vxe-tree--node-line-wrapper"
          }, [
            h("div", {
              class: "vxe-tree--node-line",
              style: {
                height: `${getNodeId(afterTreeList[0]) === nodeid ? 1 : calcTreeLine($xeTree, node, prevNode)}px`
              }
            })
          ]) : renderEmptyElement(),
          h("div", {
            class: "vxe-tree--node-item-switcher"
          }, showIcon && (lazy ? isLazyLoaded ? hasChild : hasLazyChilds : hasChild) ? [
            h("div", {
              class: "vxe-tree--node-item-icon",
              onClick(evnt) {
                toggleExpandEvent(evnt, node);
              }
            }, iconSlot ? getSlotVNs(iconSlot(nParams)) : [
              h("i", {
                class: isLazyLoading ? iconLoaded || getIcon().TREE_NODE_LOADED : isExpand ? iconOpen || getIcon().TREE_NODE_OPEN : iconClose || getIcon().TREE_NODE_CLOSE
              })
            ])
          ] : []),
          renderDragIcon(node, nodeid),
          renderRadio(node, nodeid, isRadioChecked),
          renderCheckbox(node, nodeid, isCheckboxChecked),
          h("div", {
            class: "vxe-tree--node-item-inner"
          }, [
            h("div", {
              class: "vxe-tree--node-item-title"
            }, titleSlot ? getSlotVNs(titleSlot(nParams)) : `${nodeValue}`),
            extraSlot ? h("div", {
              class: "vxe-tree--node-item-extra"
            }, getSlotVNs(extraSlot(nParams))) : renderEmptyElement()
          ])
        ])
      ]);
    };
    const renderList = (treeList) => {
      const { transform } = props;
      const { treeExpandedMaps } = internalData;
      const childrenField = computeChildrenField.value;
      if (!treeList.length) {
        return [
          h("div", {
            class: "vxe-tree--empty-placeholder"
          }, getI18n("vxe.tree.searchEmpty"))
        ];
      }
      const nodeVNs = [];
      treeList.forEach(transform ? (node) => {
        const nodeid = getNodeId(node);
        nodeVNs.push(renderNode(node, nodeid));
      } : (node) => {
        const nodeid = getNodeId(node);
        nodeVNs.push(renderNode(node, nodeid));
        const childList = XEUtils.get(node, childrenField);
        const hasChild = childList && childList.length;
        if (hasChild && treeExpandedMaps[nodeid]) {
          nodeVNs.push(...renderList(childList));
        }
      });
      return nodeVNs;
    };
    const renderDragTip = () => {
      const { drag } = props;
      const { dragNode, dragTipText } = reactData;
      const dragOpts = computeDragOpts.value;
      const dNode = dragNode;
      if (drag) {
        return h("div", {
          class: "vxe-tree--drag-wrapper"
        }, [
          h("div", {
            ref: refDragNodeLineElem,
            class: ["vxe-tree--drag-node-line", {
              "is--guides": dragOpts.showGuidesStatus
            }]
          }),
          dNode && dragOpts.showDragTip ? h("div", {
            ref: refDragTipElem,
            class: "vxe-tree--drag-sort-tip"
          }, [
            h("div", {
              class: "vxe-tree--drag-sort-tip-wrapper"
            }, [
              h("div", {
                class: "vxe-tree--drag-sort-tip-status"
              }, [
                h("span", {
                  class: ["vxe-tree--drag-sort-tip-normal-status", getIcon().TREE_DRAG_STATUS_NODE]
                }),
                h("span", {
                  class: ["vxe-tree--drag-sort-tip-sub-status", getIcon().TREE_DRAG_STATUS_SUB_NODE]
                }),
                h("span", {
                  class: ["vxe-tree--drag-sort-tip-disabled-status", getIcon().TREE_DRAG_DISABLED]
                })
              ]),
              h("div", {
                class: "vxe-tree--drag-sort-tip-content"
              }, dragTipText)
            ])
          ]) : renderEmptyElement()
        ]);
      }
      return renderEmptyElement();
    };
    const renderVN = () => {
      const { loading, trigger, showLine } = props;
      const { bodyHeight, topSpaceHeight, treeList } = reactData;
      const vSize = computeSize.value;
      const radioOpts = computeRadioOpts.value;
      const checkboxOpts = computeCheckboxOpts.value;
      const loadingOpts = computeLoadingOpts.value;
      const isRowHover = computeIsRowHover.value;
      const treeStyle = computeTreeStyle.value;
      const loadingSlot = slots.loading;
      const headerSlot = slots.header;
      const footerSlot = slots.footer;
      return h("div", {
        ref: refElem,
        class: ["vxe-tree", {
          [`size--${vSize}`]: vSize,
          "show--line": showLine,
          "checkbox--highlight": checkboxOpts.highlight,
          "radio--highlight": radioOpts.highlight,
          "node--hover": isRowHover,
          "node--trigger": trigger === "node",
          "is--loading": loading
        }]
      }, [
        headerSlot ? h("div", {
          ref: refHeaderWrapperElem,
          class: "vxe-tree--header-wrapper"
        }, headerSlot({ $tree: $xeTree })) : renderEmptyElement(),
        h("div", {
          ref: refVirtualWrapper,
          class: "vxe-tree--node-list-wrapper",
          style: treeStyle,
          onScroll: scrollEvent
        }, [
          h("div", {
            class: "vxe-tree--y-space",
            style: {
              height: bodyHeight ? `${bodyHeight}px` : ""
            }
          }),
          h("div", {
            ref: refVirtualBody,
            class: "vxe-tree--node-list-body",
            style: {
              transform: `translateY(${topSpaceHeight}px)`
            }
          }, renderList(treeList))
        ]),
        footerSlot ? h("div", {
          ref: refFooterWrapperElem,
          class: "vxe-tree--footer-wrapper"
        }, footerSlot({ $tree: $xeTree })) : renderEmptyElement(),
        /**
         * 拖拽提示
         */
        renderDragTip(),
        /**
         * 加载中
         */
        h(VxeLoading, {
          class: "vxe-tree--loading",
          modelValue: loading,
          icon: loadingOpts.icon,
          text: loadingOpts.text
        }, loadingSlot ? {
          default: () => loadingSlot({ $tree: $xeTree })
        } : {})
      ]);
    };
    const dataFlag = ref(0);
    watch(() => props.data ? props.data.length : 0, () => {
      dataFlag.value++;
    });
    watch(() => props.data, () => {
      dataFlag.value++;
    });
    watch(dataFlag, () => {
      loadData(props.data || []);
    });
    watch(() => props.checkNodeKey, (nodeKey) => {
      reactData.selectRadioKey = enNodeValue(nodeKey);
    });
    const checkboxFlag = ref(0);
    watch(() => props.checkNodeKeys ? props.checkNodeKeys.length : 0, () => {
      checkboxFlag.value++;
    });
    watch(() => props.checkNodeKeys, () => {
      checkboxFlag.value++;
    });
    watch(checkboxFlag, () => {
      updateCheckboxChecked(props.checkNodeKeys || []);
    });
    watch(() => props.filterValue, () => {
      triggerSearchEvent(new Event("filter"));
    });
    const hFlag = ref(0);
    watch(() => props.height, () => {
      hFlag.value++;
    });
    watch(() => props.minHeight, () => {
      hFlag.value++;
    });
    watch(() => props.maxHeight, () => {
      hFlag.value++;
    });
    watch(hFlag, () => {
      recalculate();
    });
    onMounted(() => {
      const { transform, drag, menuConfig } = props;
      const dragOpts = computeDragOpts.value;
      if (drag && !transform) {
        errLog("vxe.error.notSupportProp", ["drag", "transform=false", "transform=true"]);
      }
      if (dragOpts.isCrossTreeDrag) {
        errLog("vxe.error.notProp", ["drag-config.isCrossTreeDrag"]);
      }
      const VxeUIContextMenu = VxeUI.getComponent("VxeContextMenu");
      if (menuConfig && !VxeUIContextMenu) {
        errLog("vxe.error.reqComp", ["vxe-context-menu"]);
      }
      if (props.autoResize) {
        const el = refElem.value;
        const parentEl = getParentElem();
        const resizeObserver = globalResize.create(() => {
          if (props.autoResize) {
            recalculate();
          }
        });
        if (el) {
          resizeObserver.observe(el);
        }
        if (parentEl) {
          resizeObserver.observe(parentEl);
        }
        internalData.resizeObserver = resizeObserver;
      }
      globalEvents.on($xeTree, "resize", handleGlobalResizeEvent);
    });
    onBeforeUnmount(() => {
      const { resizeObserver } = internalData;
      if (resizeObserver) {
        resizeObserver.disconnect();
      }
      globalEvents.off($xeTree, "resize");
      XEUtils.assign(reactData, createReactData());
      XEUtils.assign(internalData, createInternalData());
    });
    reactData.selectRadioKey = enNodeValue(props.checkNodeKey);
    loadData(props.data || []);
    $xeTree.renderVN = renderVN;
    provide("$xeTree", $xeTree);
    return $xeTree;
  },
  render() {
    return this.renderVN();
  }
});
export {
  VxeTreeComponent as default
};
