import { defineVxeComponent } from '../../ui/src/comp';
import XEUtils from 'xe-utils';
import { VxeUI, getConfig, getIcon, getI18n, globalEvents, GLOBAL_EVENT_KEYS, createEvent, globalMixins, renderEmptyElement } from '../../ui';
import { getEventTargetNode, toCssUnit, updatePanelPlacement } from '../../ui/src/dom';
import { getLastZIndex, nextZIndex, getFuncText, eqEmptyValue } from '../../ui/src/utils';
import { getSlotVNs } from '../../ui/src/vn';
import { createComponentLog } from '../../ui/src/log';
import VxeInputComponent from '../../input/src/input';
import VxeButtonComponent from '../../button/src/button';
const { errLog } = createComponentLog('select');
function isOptionVisible(option) {
    return option.visible !== false;
}
function getOptUniqueId() {
    return XEUtils.uniqueId('opt_');
}
function createReactData() {
    return {
        initialized: false,
        scrollYLoad: false,
        bodyHeight: 0,
        topSpaceHeight: 0,
        optList: [],
        staticOptions: [],
        reactFlag: 0,
        currentOption: null,
        searchValue: '',
        searchLoading: false,
        panelIndex: 0,
        panelStyle: {},
        panelPlacement: null,
        triggerFocusPanel: false,
        visiblePanel: false,
        isAniVisible: false,
        isActivated: false
    };
}
function createInternalData() {
    return {
        // isLoaded: false,
        synchData: [],
        fullData: [],
        afterVisibleList: [],
        optAddMaps: {},
        optGroupKeyMaps: {},
        optFullValMaps: {},
        remoteValMaps: {},
        lastScrollLeft: 0,
        lastScrollTop: 0,
        scrollYStore: {
            startIndex: 0,
            endIndex: 0,
            visibleSize: 0,
            offsetSize: 0,
            rowHeight: 0
        },
        lastScrollTime: 0,
        hpTimeout: undefined
    };
}
export default {
    name: 'VxeSelect',
    mixins: [
        globalMixins.sizeMixin
    ],
    model: {
        prop: 'value',
        event: 'modelValue'
    },
    props: {
        value: [String, Number, Boolean, Array],
        defaultConfig: Object,
        clearable: {
            type: Boolean,
            default: () => getConfig().select.clearable
        },
        placeholder: String,
        readonly: {
            type: Boolean,
            default: null
        },
        loading: Boolean,
        disabled: {
            type: Boolean,
            default: null
        },
        multiple: Boolean,
        multiCharOverflow: {
            type: [Number, String],
            default: () => getConfig().select.multiCharOverflow
        },
        prefixIcon: String,
        allowCreate: {
            type: Boolean,
            default: () => getConfig().select.allowCreate
        },
        placement: String,
        lazyOptions: Array,
        options: Array,
        optionProps: Object,
        optionGroups: Array,
        optionGroupProps: Object,
        optionConfig: Object,
        className: [String, Function],
        /**
         * 已废弃，请使用 popupConfig.className
         * @deprecated
         */
        popupClassName: [String, Function],
        max: {
            type: [String, Number],
            default: null
        },
        showRadio: {
            type: Boolean,
            default: () => getConfig().select.showRadio
        },
        radioConfig: Object,
        showCheckbox: {
            type: Boolean,
            default: () => getConfig().select.showCheckedButton
        },
        checkboxConfig: Object,
        /**
         * 已废弃，请使用 popupConfig.zIndex
         * @deprecated
         */
        zIndex: Number,
        size: {
            type: String,
            default: () => getConfig().select.size || getConfig().size
        },
        filterable: Boolean,
        /**
         * 已废弃，被 filter-config.filterMethod 替换
         * @deprecated
         */
        filterMethod: Function,
        filterConfig: Object,
        remote: Boolean,
        remoteConfig: Object,
        emptyText: String,
        checkedClosable: {
            type: Boolean,
            default: () => getConfig().select.checkedClosable
        },
        clearClosable: {
            type: Boolean,
            default: () => getConfig().select.clearClosable
        },
        showCloseButton: {
            type: Boolean,
            default: () => getConfig().select.showCloseButton
        },
        showTotalButton: {
            type: Boolean,
            default: () => getConfig().select.showTotalButton
        },
        showCheckedButton: {
            type: Boolean,
            default: () => getConfig().select.showCheckedButton
        },
        showClearButton: {
            type: Boolean,
            default: () => getConfig().select.showClearButton
        },
        transfer: {
            type: Boolean,
            default: null
        },
        popupConfig: Object,
        virtualYConfig: Object,
        scrollY: Object,
        /**
         * 已废弃，被 remote-config.queryMethod 替换
         * @deprecated
         */
        remoteMethod: Function,
        /**
         * 已废弃，被 option-config.keyField 替换
         * @deprecated
         */
        optionId: {
            type: String,
            default: () => getConfig().select.optionId
        },
        /**
         * 已废弃，被 option-config.useKey 替换
         * @deprecated
         */
        optionKey: Boolean
    },
    inject: {
        $xeModal: {
            default: null
        },
        $xeDrawer: {
            default: null
        },
        $xeTable: {
            default: null
        },
        $xeForm: {
            default: null
        },
        formItemInfo: {
            from: 'xeFormItemInfo',
            default: null
        }
    },
    provide() {
        const $xeSelect = this;
        return {
            $xeSelect
        };
    },
    data() {
        const xID = XEUtils.uniqueId();
        const reactData = createReactData();
        return Object.assign(Object.assign({}, {}), { xID,
            reactData });
    },
    computed: Object.assign(Object.assign({}, {}), { computeFormReadonly() {
            const $xeSelect = this;
            const props = $xeSelect;
            const $xeForm = $xeSelect.$xeForm;
            const { readonly } = props;
            if (readonly === null) {
                if ($xeForm) {
                    return $xeForm.readonly;
                }
                return false;
            }
            return readonly;
        },
        computeIsDisabled() {
            const $xeSelect = this;
            const props = $xeSelect;
            const $xeForm = $xeSelect.$xeForm;
            const { disabled } = props;
            if (disabled === null) {
                if ($xeForm) {
                    return $xeForm.disabled;
                }
                return false;
            }
            return disabled;
        },
        computeBtnTransfer() {
            const $xeSelect = this;
            const props = $xeSelect;
            const $xeTable = $xeSelect.$xeTable;
            const $xeModal = $xeSelect.$xeModal;
            const $xeDrawer = $xeSelect.$xeDrawer;
            const $xeForm = $xeSelect.$xeForm;
            const { transfer } = props;
            const popupOpts = $xeSelect.computePopupOpts;
            if (XEUtils.isBoolean(popupOpts.transfer)) {
                return popupOpts.transfer;
            }
            if (transfer === null) {
                const globalTransfer = getConfig().select.transfer;
                if (XEUtils.isBoolean(globalTransfer)) {
                    return globalTransfer;
                }
                if ($xeTable || $xeModal || $xeDrawer || $xeForm) {
                    return true;
                }
            }
            return transfer;
        },
        computeInpPlaceholder() {
            const $xeSelect = this;
            const props = $xeSelect;
            const { placeholder } = props;
            if (placeholder) {
                return getFuncText(placeholder);
            }
            const globalPlaceholder = getConfig().select.placeholder;
            if (globalPlaceholder) {
                return getFuncText(globalPlaceholder);
            }
            return getI18n('vxe.base.pleaseSelect');
        },
        computeDefaultOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, props.defaultConfig);
        },
        computePropsOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, props.optionProps);
        },
        computeGroupPropsOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, props.optionGroupProps);
        },
        computeLabelField() {
            const $xeSelect = this;
            const propsOpts = $xeSelect.computePropsOpts;
            return propsOpts.label || 'label';
        },
        computeValueField() {
            const $xeSelect = this;
            const propsOpts = $xeSelect.computePropsOpts;
            return propsOpts.value || 'value';
        },
        computeGroupLabelField() {
            const $xeSelect = this;
            const groupPropsOpts = $xeSelect.computeGroupPropsOpts;
            return groupPropsOpts.label || 'label';
        },
        computeGroupOptionsField() {
            const $xeSelect = this;
            const groupPropsOpts = $xeSelect.computeGroupPropsOpts;
            return groupPropsOpts.options || 'options';
        },
        computeIsMaximize() {
            const $xeSelect = this;
            const selectVals = $xeSelect.computeSelectVals;
            return $xeSelect.checkMaxLimit(selectVals);
        },
        computePopupOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, getConfig().select.popupConfig, props.popupConfig);
        },
        computeVirtualYOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, getConfig().select.virtualYConfig || getConfig().select.scrollY, props.virtualYConfig || props.scrollY);
        },
        computeRemoteOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, getConfig().select.remoteConfig, props.remoteConfig);
        },
        computeFilterOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, getConfig().select.filterConfig, props.filterConfig);
        },
        computeOptionOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, getConfig().select.optionConfig, props.optionConfig);
        },
        computeRadioOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, getConfig().select.radioConfig, props.radioConfig);
        },
        computeCheckboxOpts() {
            const $xeSelect = this;
            const props = $xeSelect;
            return Object.assign({}, getConfig().select.checkboxConfig, props.checkboxConfig);
        },
        computeIsGroup() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            return reactData.fullGroupList.some((item) => item.options && item.options.length);
        },
        computeMultiMaxCharNum() {
            const $xeSelect = this;
            const props = $xeSelect;
            return XEUtils.toNumber(props.multiCharOverflow);
        },
        computePopupWrapperStyle() {
            const $xeSelect = this;
            const popupOpts = $xeSelect.computePopupOpts;
            const { height, width } = popupOpts;
            const stys = {};
            if (width) {
                stys.width = toCssUnit(width);
            }
            if (height) {
                stys.height = toCssUnit(height);
                stys.maxHeight = toCssUnit(height);
            }
            return stys;
        },
        computeSelectVals() {
            const $xeSelect = this;
            const props = $xeSelect;
            const { value: modelValue, multiple } = props;
            let vals = [];
            if (XEUtils.isArray(modelValue)) {
                vals = modelValue;
            }
            else {
                if (multiple) {
                    if (!eqEmptyValue(modelValue)) {
                        vals = `${modelValue}`.indexOf(',') > -1 ? `${modelValue}`.split(',') : [modelValue];
                    }
                }
                else {
                    vals = modelValue === null || modelValue === undefined ? [] : [modelValue];
                }
            }
            return vals;
        },
        computeFullLabel() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const { remote } = props;
            const { reactFlag } = reactData;
            const selectVals = $xeSelect.computeSelectVals;
            if (remote && reactFlag) {
                return selectVals.map(val => $xeSelect.getRemoteSelectLabel(val)).join(', ');
            }
            return selectVals.map((val) => $xeSelect.getSelectLabel(val)).join(', ');
        },
        computeSelectLabel() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const { remote, multiple } = props;
            const { reactFlag } = reactData;
            const multiMaxCharNum = $xeSelect.computeMultiMaxCharNum;
            const selectVals = $xeSelect.computeSelectVals;
            if (remote && reactFlag) {
                return selectVals.map(val => $xeSelect.getRemoteSelectLabel(val)).join(', ');
            }
            const labels = selectVals.map((val) => $xeSelect.getSelectLabel(val));
            if (multiple && multiMaxCharNum > 0 && labels.length > multiMaxCharNum) {
                return `${labels.slice(0, multiMaxCharNum)}...`;
            }
            return labels.join(', ');
        } }),
    methods: {
        //
        // Method
        //
        dispatchEvent(type, params, evnt) {
            const $xeSelect = this;
            $xeSelect.$emit(type, createEvent(evnt, { $select: $xeSelect }, params));
        },
        emitModel(value) {
            const $xeSelect = this;
            const { _events } = $xeSelect;
            if (_events && _events.modelValue) {
                $xeSelect.$emit('modelValue', value);
            }
            else {
                $xeSelect.$emit('model-value', value);
            }
        },
        emitDefaultValue(value) {
            const $xeSelect = this;
            $xeSelect.emitModel(value);
            $xeSelect.dispatchEvent('default-change', { value }, null);
        },
        isPanelVisible() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            return reactData.visiblePanel;
        },
        togglePanel() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            if (reactData.visiblePanel) {
                $xeSelect.hideOptionPanel();
            }
            else {
                $xeSelect.showOptionPanel();
            }
            return $xeSelect.$nextTick();
        },
        hidePanel() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            if (reactData.visiblePanel) {
                $xeSelect.hideOptionPanel();
            }
            return $xeSelect.$nextTick();
        },
        showPanel() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            if (!reactData.visiblePanel) {
                $xeSelect.showOptionPanel();
            }
            return $xeSelect.$nextTick();
        },
        focus() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const $input = $xeSelect.$refs.refInput;
            if ($input) {
                $input.blur();
            }
            reactData.isActivated = true;
            return $xeSelect.$nextTick();
        },
        blur() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const $input = $xeSelect.$refs.refInput;
            if ($input) {
                $input.blur();
            }
            reactData.isActivated = false;
            return $xeSelect.$nextTick();
        },
        callSlot(slotFunc, params, h) {
            const $xeSelect = this;
            const slots = $xeSelect.$scopedSlots;
            if (slotFunc) {
                if (XEUtils.isString(slotFunc)) {
                    slotFunc = slots[slotFunc] || null;
                }
                if (XEUtils.isFunction(slotFunc)) {
                    return getSlotVNs(slotFunc.call($xeSelect, params, h));
                }
            }
            return [];
        },
        getOptKey() {
            const $xeSelect = this;
            const props = $xeSelect;
            const optionOpts = $xeSelect.computeOptionOpts;
            return optionOpts.keyField || props.optionId || '_X_OPTION_KEY';
        },
        getOptId(option) {
            const $xeSelect = this;
            const optid = option[$xeSelect.getOptKey()];
            return optid ? encodeURIComponent(optid) : '';
        },
        checkMaxLimit(selectVals) {
            const $xeSelect = this;
            const props = $xeSelect;
            const { multiple, max } = props;
            if (multiple && max) {
                return selectVals.length >= XEUtils.toNumber(max);
            }
            return false;
        },
        getRemoteSelectLabel(value) {
            const $xeSelect = this;
            const props = $xeSelect;
            const internalData = $xeSelect.internalData;
            const { lazyOptions } = props;
            const { remoteValMaps, optFullValMaps } = internalData;
            const valueField = $xeSelect.computeValueField;
            const labelField = $xeSelect.computeLabelField;
            const remoteItem = remoteValMaps[value] || optFullValMaps[value];
            const item = remoteItem ? remoteItem.item : null;
            if (item) {
                return XEUtils.toValueString(item[labelField]);
            }
            if (lazyOptions) {
                const lazyItem = lazyOptions.find(item => item[valueField] === value);
                if (lazyItem) {
                    return lazyItem[labelField];
                }
            }
            return value;
        },
        getSelectLabel(value) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { lazyOptions } = props;
            const { optFullValMaps } = internalData;
            const valueField = $xeSelect.computeValueField;
            const labelField = $xeSelect.computeLabelField;
            const cacheItem = reactData.reactFlag ? optFullValMaps[value] : null;
            if (cacheItem) {
                return cacheItem.item[labelField];
            }
            if (lazyOptions) {
                const lazyItem = lazyOptions.find(item => item[valueField] === value);
                if (lazyItem) {
                    return lazyItem[labelField];
                }
            }
            return value;
        },
        getOptkey() {
            const $xeSelect = this;
            const props = $xeSelect;
            const optionOpts = $xeSelect.computeOptionOpts;
            return optionOpts.keyField || props.optionId || '_X_OPTION_KEY';
        },
        getOptid(option) {
            const $xeSelect = this;
            const optid = option[$xeSelect.getOptkey()];
            return optid ? encodeURIComponent(optid) : '';
        },
        /**
         * 处理选项，当选项被动态显示/隐藏时可能会用到
         */
        handleOption() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { remote, value, filterable } = props;
            const { searchValue } = reactData;
            const { fullData, optFullValMaps } = internalData;
            const labelField = $xeSelect.computeLabelField;
            const valueField = $xeSelect.computeValueField;
            const filterOpts = $xeSelect.computeFilterOpts;
            const frMethod = filterOpts.filterMethod || props.filterMethod;
            const searchStr = `${searchValue || ''}`.toLowerCase();
            let avList = [];
            if (remote) {
                avList = fullData.filter(isOptionVisible);
            }
            else {
                if (filterable && frMethod) {
                    avList = fullData.filter(option => isOptionVisible(option) && frMethod({ $select: $xeSelect, group: null, option, searchValue, value }));
                }
                else if (filterable) {
                    avList = fullData.filter(option => isOptionVisible(option) && (!searchStr || `${option[labelField] || option[valueField]}`.toLowerCase().indexOf(searchStr) > -1));
                }
                else {
                    avList = fullData.filter(isOptionVisible);
                }
            }
            avList.forEach((item, index) => {
                const cacheItem = optFullValMaps[item[valueField]];
                if (cacheItem) {
                    cacheItem._index = index;
                }
            });
            internalData.afterVisibleList = avList;
            return $xeSelect.$nextTick();
        },
        refreshOption() {
            const $xeSelect = this;
            $xeSelect.handleOption();
            $xeSelect.updateYData();
            return $xeSelect.$nextTick();
        },
        cacheItemMap(datas) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const groupOptionsField = $xeSelect.computeGroupOptionsField;
            const valueField = $xeSelect.computeValueField;
            const key = $xeSelect.getOptKey();
            const groupKeyMaps = {};
            const fullKeyMaps = {};
            const list = [];
            const handleOptItem = (item) => {
                list.push(item);
                let optid = $xeSelect.getOptId(item);
                if (!optid) {
                    optid = getOptUniqueId();
                    item[key] = optid;
                }
                fullKeyMaps[item[valueField]] = {
                    key: optid,
                    item,
                    _index: -1
                };
            };
            datas.forEach((group) => {
                handleOptItem(group);
                if (group[groupOptionsField]) {
                    groupKeyMaps[group[key]] = group;
                    group[groupOptionsField].forEach(handleOptItem);
                }
            });
            internalData.fullData = list;
            internalData.optGroupKeyMaps = groupKeyMaps;
            internalData.optFullValMaps = fullKeyMaps;
            reactData.reactFlag++;
            $xeSelect.handleOption();
        },
        setCurrentOption(option) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            if (option) {
                reactData.currentOption = option;
            }
        },
        scrollToOption(option, isAlignBottom) {
            const $xeSelect = this;
            return $xeSelect.$nextTick().then(() => {
                if (option) {
                    const optWrapperElem = $xeSelect.$refs.refOptionWrapper;
                    const panelElem = $xeSelect.$refs.refOptionPanel;
                    const optElem = panelElem.querySelector(`[optid='${$xeSelect.getOptid(option)}']`);
                    if (optWrapperElem && optElem) {
                        const wrapperHeight = optWrapperElem.offsetHeight;
                        const offsetPadding = 5;
                        if (isAlignBottom) {
                            if (optElem.offsetTop + optElem.offsetHeight - optWrapperElem.scrollTop > wrapperHeight) {
                                optWrapperElem.scrollTop = optElem.offsetTop + optElem.offsetHeight - wrapperHeight;
                            }
                        }
                        else {
                            if (optElem.offsetTop + offsetPadding < optWrapperElem.scrollTop || optElem.offsetTop + offsetPadding > optWrapperElem.scrollTop + optWrapperElem.clientHeight) {
                                optWrapperElem.scrollTop = optElem.offsetTop - offsetPadding;
                            }
                        }
                    }
                }
            });
        },
        updateZindex() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const popupOpts = $xeSelect.computePopupOpts;
            const customZIndex = popupOpts.zIndex || props.zIndex;
            if (customZIndex) {
                reactData.panelIndex = XEUtils.toNumber(customZIndex);
            }
            else if (reactData.panelIndex < getLastZIndex()) {
                reactData.panelIndex = nextZIndex();
            }
        },
        updateZIndex() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            if (reactData.panelIndex < getLastZIndex()) {
                reactData.panelIndex = nextZIndex();
            }
        },
        updatePlacement() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const { placement } = props;
            const { panelIndex } = reactData;
            const targetElem = $xeSelect.$refs.refElem;
            const panelElem = $xeSelect.$refs.refOptionPanel;
            const btnTransfer = $xeSelect.computeBtnTransfer;
            const popupOpts = $xeSelect.computePopupOpts;
            const handleStyle = () => {
                const ppObj = updatePanelPlacement(targetElem, panelElem, {
                    placement: popupOpts.placement || placement,
                    defaultPlacement: popupOpts.defaultPlacement,
                    teleportTo: btnTransfer
                });
                const panelStyle = Object.assign(ppObj.style, {
                    zIndex: panelIndex
                });
                reactData.panelStyle = panelStyle;
                reactData.panelPlacement = ppObj.placement;
            };
            handleStyle();
            return $xeSelect.$nextTick().then(handleStyle);
        },
        handleScrollSelect() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            $xeSelect.$nextTick(() => {
                const { isAniVisible, visiblePanel } = reactData;
                const { optFullValMaps } = internalData;
                const selectVals = $xeSelect.computeSelectVals;
                if (selectVals.length && isAniVisible && visiblePanel) {
                    const cacheItem = reactData.reactFlag ? optFullValMaps[`${selectVals[0]}`] : null;
                    if (cacheItem) {
                        reactData.currentOption = cacheItem.item;
                        $xeSelect.handleScrollToOption(cacheItem.item);
                    }
                }
            });
        },
        showOptionPanel() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { loading, filterable, remote } = props;
            const { fullData, hpTimeout } = internalData;
            const isDisabled = $xeSelect.computeIsDisabled;
            const remoteOpts = $xeSelect.computeRemoteOpts;
            const selectVals = $xeSelect.computeSelectVals;
            if (!loading && !isDisabled) {
                if (hpTimeout) {
                    clearTimeout(hpTimeout);
                    internalData.hpTimeout = undefined;
                }
                if (!reactData.initialized) {
                    reactData.initialized = true;
                    const btnTransfer = $xeSelect.computeBtnTransfer;
                    const panelElem = $xeSelect.$refs.refOptionPanel;
                    if (btnTransfer && panelElem) {
                        document.body.appendChild(panelElem);
                    }
                }
                reactData.isActivated = true;
                reactData.isAniVisible = true;
                if (filterable) {
                    if (remote && remoteOpts.enabled && ((remoteOpts.autoLoad && !fullData.length) || (fullData.length && remoteOpts.clearOnClose))) {
                        $xeSelect.handleSearchEvent();
                    }
                    else {
                        $xeSelect.handleOption();
                        $xeSelect.updateYData();
                    }
                }
                setTimeout(() => {
                    reactData.visiblePanel = true;
                    $xeSelect.handleFocusSearch();
                    $xeSelect.recalculate().then(() => {
                        if (selectVals && selectVals.length) {
                            $xeSelect.handleScrollSelect();
                        }
                        else {
                            $xeSelect.refreshScroll();
                        }
                    });
                    $xeSelect.updatePlacement();
                }, 10);
                setTimeout(() => {
                    $xeSelect.recalculate().then(() => $xeSelect.refreshScroll());
                }, 100);
                $xeSelect.updateZIndex();
                $xeSelect.updatePlacement();
                $xeSelect.dispatchEvent('visible-change', { visible: true }, null);
            }
        },
        hideOptionPanel(evnt) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { filterable, remote } = props;
            const filterOpts = $xeSelect.computeFilterOpts;
            const remoteOpts = $xeSelect.computeRemoteOpts;
            if (remote) {
                if (remoteOpts.clearOnClose) {
                    reactData.searchValue = '';
                }
            }
            else if (filterable) {
                if (filterOpts.clearOnClose) {
                    reactData.searchValue = '';
                }
            }
            else {
                reactData.searchValue = '';
            }
            reactData.searchLoading = false;
            reactData.visiblePanel = false;
            internalData.hpTimeout = setTimeout(() => {
                reactData.isAniVisible = false;
            }, 350);
            $xeSelect.dispatchEvent('visible-change', { visible: false }, evnt || null);
        },
        changeEvent(evnt, selectValue, option) {
            const $xeSelect = this;
            const props = $xeSelect;
            const $xeForm = $xeSelect.$xeForm;
            const formItemInfo = $xeSelect.formItemInfo;
            $xeSelect.emitModel(selectValue);
            if (selectValue !== props.value) {
                $xeSelect.dispatchEvent('change', { value: selectValue, option }, evnt);
                // 自动更新校验状态
                if ($xeForm && formItemInfo) {
                    $xeForm.triggerItemEvent(evnt, formItemInfo.itemConfig.field, selectValue);
                }
            }
        },
        clearValueEvent(evnt, selectValue) {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            internalData.remoteValMaps = {};
            $xeSelect.changeEvent(evnt, selectValue, null);
            $xeSelect.dispatchEvent('clear', { value: selectValue }, evnt);
        },
        clearEvent(params) {
            const $xeSelect = this;
            const { $event } = params;
            $xeSelect.clearValueEvent($event, null);
            $xeSelect.hideOptionPanel($event);
        },
        allCheckedPanelEvent(params) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const { $event } = params;
            const { multiple, max, checkedClosable } = props;
            const { optList } = reactData;
            const valueField = $xeSelect.computeValueField;
            if (multiple) {
                const selectVals = $xeSelect.computeSelectVals;
                const currVlas = selectVals.slice(0);
                for (let i = 0; i < optList.length; i++) {
                    const option = optList[i];
                    const selectValue = option[valueField];
                    // 检测是否超过最大可选数量
                    if ($xeSelect.checkMaxLimit(currVlas)) {
                        if (VxeUI) {
                            VxeUI.modal.message({
                                content: getI18n('vxe.select.overSizeErr', [max]),
                                status: 'warning'
                            });
                        }
                        break;
                    }
                    if (!currVlas.some(val => val === selectValue)) {
                        currVlas.push(selectValue);
                    }
                }
                $xeSelect.changeEvent($event, currVlas, optList[0]);
                if (checkedClosable) {
                    $xeSelect.hideOptionPanel($event);
                }
                $xeSelect.dispatchEvent('all-change', { value: currVlas }, $event);
            }
        },
        clearCheckedPanelEvent(params) {
            const $xeSelect = this;
            const props = $xeSelect;
            const { clearClosable } = props;
            const { $event } = params;
            $xeSelect.clearValueEvent($event, null);
            if (clearClosable) {
                $xeSelect.hideOptionPanel($event);
            }
        },
        closePanelEvent(params) {
            const $xeSelect = this;
            const { $event } = params;
            $xeSelect.hideOptionPanel($event);
        },
        changeOptionEvent(evnt, option) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { multiple } = props;
            const { remoteValMaps } = internalData;
            const valueField = $xeSelect.computeValueField;
            const selectValue = option[valueField];
            const remoteItem = remoteValMaps[selectValue];
            if (!reactData.visiblePanel) {
                return;
            }
            if (remoteItem) {
                remoteItem.item = option;
            }
            else {
                remoteValMaps[selectValue] = {
                    key: $xeSelect.getOptId(option),
                    item: option,
                    _index: -1
                };
            }
            if (multiple) {
                let multipleValue = [];
                const selectVals = $xeSelect.computeSelectVals;
                const index = XEUtils.findIndexOf(selectVals, val => val === selectValue);
                if (index === -1) {
                    multipleValue = selectVals.concat([selectValue]);
                }
                else {
                    multipleValue = selectVals.filter((val) => val !== selectValue);
                }
                $xeSelect.changeEvent(evnt, multipleValue, option);
            }
            else {
                $xeSelect.changeEvent(evnt, selectValue, option);
                $xeSelect.hideOptionPanel(evnt);
            }
            reactData.reactFlag++;
        },
        handleGlobalMousewheelEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const { visiblePanel } = reactData;
            const isDisabled = $xeSelect.computeIsDisabled;
            if (!isDisabled) {
                if (visiblePanel) {
                    const panelElem = $xeSelect.$refs.refOptionPanel;
                    if (getEventTargetNode(evnt, panelElem).flag) {
                        $xeSelect.updatePlacement();
                    }
                    else {
                        $xeSelect.hideOptionPanel(evnt);
                    }
                }
            }
        },
        handleGlobalMousedownEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const { visiblePanel } = reactData;
            const isDisabled = $xeSelect.computeIsDisabled;
            const popupOpts = $xeSelect.computePopupOpts;
            const { trigger } = popupOpts;
            if (!isDisabled) {
                const el = $xeSelect.$refs.refElem;
                const panelElem = $xeSelect.$refs.refOptionPanel;
                reactData.isActivated = getEventTargetNode(evnt, el).flag || getEventTargetNode(evnt, panelElem).flag;
                if (visiblePanel && !reactData.isActivated) {
                    if (trigger !== 'manual') {
                        $xeSelect.hideOptionPanel(evnt);
                    }
                }
            }
        },
        validOffsetOption(option) {
            const $xeSelect = this;
            const isDisabled = option.disabled;
            const optid = $xeSelect.getOptId(option);
            if (!isDisabled && !$xeSelect.hasOptGroupById(optid)) {
                return true;
            }
            return false;
        },
        findOffsetOption(option, isDwArrow) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { allowCreate } = props;
            const { optList } = reactData;
            const { optFullValMaps, optAddMaps, afterVisibleList } = internalData;
            const valueField = $xeSelect.computeValueField;
            let fullList = afterVisibleList;
            let offsetAddIndex = 0;
            if (allowCreate && optList.length) {
                const firstItem = optList[0];
                const optid = $xeSelect.getOptId(firstItem);
                if (optAddMaps[optid]) {
                    offsetAddIndex = 1;
                    fullList = [optAddMaps[optid]].concat(fullList);
                }
            }
            if (!option) {
                if (isDwArrow) {
                    for (let i = 0; i < fullList.length; i++) {
                        const item = fullList[i];
                        if ($xeSelect.validOffsetOption(item)) {
                            return item;
                        }
                    }
                }
                else {
                    for (let len = fullList.length - 1; len >= 0; len--) {
                        const item = fullList[len];
                        if ($xeSelect.validOffsetOption(item)) {
                            return item;
                        }
                    }
                }
            }
            let avIndex = 0;
            const cacheItem = option ? optFullValMaps[option[valueField]] : null;
            if (cacheItem) {
                avIndex = cacheItem._index + offsetAddIndex;
            }
            if (avIndex > -1) {
                if (isDwArrow) {
                    for (let i = avIndex + 1; i <= fullList.length - 1; i++) {
                        const item = fullList[i];
                        if ($xeSelect.validOffsetOption(item)) {
                            return item;
                        }
                    }
                }
                else {
                    if (avIndex > 0) {
                        for (let len = avIndex - 1; len >= 0; len--) {
                            const item = fullList[len];
                            if ($xeSelect.validOffsetOption(item)) {
                                return item;
                            }
                        }
                    }
                }
            }
            return null;
        },
        handleGlobalKeydownEvent(evnt) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const { clearable } = props;
            const { visiblePanel, currentOption } = reactData;
            const isDisabled = $xeSelect.computeIsDisabled;
            const popupOpts = $xeSelect.computePopupOpts;
            const { trigger } = popupOpts;
            if (!isDisabled) {
                const isTab = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.TAB);
                const isEnter = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ENTER);
                const isEsc = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ESCAPE);
                const isUpArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_UP);
                const isDwArrow = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_DOWN);
                const isDel = globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.DELETE);
                if (isTab) {
                    reactData.isActivated = false;
                }
                if (visiblePanel) {
                    if (isEsc || isTab) {
                        if (trigger !== 'manual') {
                            $xeSelect.hideOptionPanel(evnt);
                        }
                    }
                    else if (isEnter) {
                        if (currentOption) {
                            evnt.preventDefault();
                            evnt.stopPropagation();
                            $xeSelect.changeOptionEvent(evnt, currentOption);
                        }
                    }
                    else if (isUpArrow || isDwArrow) {
                        evnt.preventDefault();
                        let offsetOption = $xeSelect.findOffsetOption(currentOption, isDwArrow);
                        // 如果不匹配，默认最接近一个
                        if (!offsetOption) {
                            offsetOption = $xeSelect.findOffsetOption(null, isDwArrow);
                        }
                        if (offsetOption) {
                            $xeSelect.setCurrentOption(offsetOption);
                            $xeSelect.handleScrollToOption(offsetOption, isDwArrow);
                        }
                    }
                }
                else if ((isUpArrow || isDwArrow || isEnter) && reactData.isActivated) {
                    evnt.preventDefault();
                    $xeSelect.showOptionPanel();
                }
                if (reactData.isActivated) {
                    if (isDel && clearable) {
                        $xeSelect.clearValueEvent(evnt, null);
                    }
                }
            }
        },
        handleGlobalBlurEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const { visiblePanel, isActivated } = reactData;
            const popupOpts = $xeSelect.computePopupOpts;
            const { trigger } = popupOpts;
            if (visiblePanel) {
                if (trigger !== 'manual') {
                    $xeSelect.hideOptionPanel(evnt);
                }
            }
            if (isActivated) {
                reactData.isActivated = false;
            }
            if (visiblePanel || isActivated) {
                const $input = $xeSelect.$refs.refInput;
                if ($input) {
                    $input.blur();
                }
            }
        },
        handleGlobalResizeEvent() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const { visiblePanel } = reactData;
            if (visiblePanel) {
                $xeSelect.updatePlacement();
            }
        },
        handleFocusSearch() {
            const $xeSelect = this;
            const props = $xeSelect;
            if (props.filterable) {
                $xeSelect.$nextTick(() => {
                    const inpSearch = $xeSelect.$refs.refInpSearch;
                    if (inpSearch) {
                        inpSearch.focus();
                    }
                });
            }
        },
        focusEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const isDisabled = $xeSelect.computeIsDisabled;
            const popupOpts = $xeSelect.computePopupOpts;
            const { trigger } = popupOpts;
            if (!isDisabled) {
                if (!reactData.visiblePanel) {
                    if (!trigger || trigger === 'default') {
                        reactData.triggerFocusPanel = true;
                        $xeSelect.showOptionPanel();
                        setTimeout(() => {
                            reactData.triggerFocusPanel = false;
                        }, 500);
                    }
                }
            }
            $xeSelect.dispatchEvent('focus', {}, evnt);
        },
        clickEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const popupOpts = $xeSelect.computePopupOpts;
            const { trigger } = popupOpts;
            if (!trigger || trigger === 'default') {
                $xeSelect.togglePanelEvent(evnt);
            }
            $xeSelect.dispatchEvent('click', { triggerButton: false, visible: reactData.visiblePanel }, evnt);
        },
        blurEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            reactData.isActivated = false;
            $xeSelect.dispatchEvent('blur', {}, evnt);
        },
        suffixClickEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const popupOpts = $xeSelect.computePopupOpts;
            const { trigger } = popupOpts;
            if (!trigger || trigger === 'default' || trigger === 'icon') {
                $xeSelect.togglePanelEvent(evnt);
            }
            $xeSelect.dispatchEvent('click', { triggerButton: true, visible: reactData.visiblePanel }, evnt);
        },
        modelSearchEvent(value) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            reactData.searchValue = value;
        },
        focusSearchEvent() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            reactData.isActivated = true;
        },
        handleSearchEvent() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const { value, remote } = props;
            const { searchValue } = reactData;
            const remoteOpts = $xeSelect.computeRemoteOpts;
            const qyMethod = remoteOpts.queryMethod || props.remoteMethod;
            if (remote && qyMethod && remoteOpts.enabled) {
                reactData.searchLoading = true;
                Promise.resolve(qyMethod({ $select: $xeSelect, searchValue, value })).then(() => $xeSelect.$nextTick())
                    .catch(() => $xeSelect.$nextTick())
                    .finally(() => {
                    reactData.searchLoading = false;
                    $xeSelect.handleOption();
                    $xeSelect.updateYData();
                });
            }
            else {
                $xeSelect.handleOption();
                $xeSelect.updateYData();
            }
        },
        triggerSearchEvent: XEUtils.debounce(function () {
            const $xeSelect = this;
            $xeSelect.handleSearchEvent();
        }, 350, { trailing: true }),
        togglePanelEvent(params) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const { $event } = params;
            $event.preventDefault();
            if (reactData.triggerFocusPanel) {
                reactData.triggerFocusPanel = false;
            }
            else {
                if (reactData.visiblePanel) {
                    $xeSelect.hideOptionPanel($event);
                }
                else {
                    $xeSelect.showOptionPanel();
                }
            }
        },
        checkOptionDisabled(isSelected, option) {
            const $xeSelect = this;
            if (option.disabled) {
                return true;
            }
            const isMaximize = $xeSelect.computeIsMaximize;
            if (isMaximize && !isSelected) {
                return true;
            }
            return false;
        },
        updateYSpace() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            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;
        },
        handleData() {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { filterable, allowCreate } = props;
            const { scrollYLoad, searchValue } = reactData;
            const { optAddMaps, scrollYStore, afterVisibleList } = internalData;
            const labelField = $xeSelect.computeLabelField;
            const valueField = $xeSelect.computeValueField;
            const restList = scrollYLoad ? afterVisibleList.slice(scrollYStore.startIndex, scrollYStore.endIndex) : afterVisibleList.slice(0);
            if (filterable && allowCreate && searchValue) {
                if (!restList.some(option => option[labelField] === searchValue)) {
                    const addItem = optAddMaps[searchValue] || {
                        [$xeSelect.getOptKey()]: searchValue,
                        [labelField]: searchValue,
                        [valueField]: searchValue
                    };
                    optAddMaps[searchValue] = addItem;
                    restList.unshift(addItem);
                }
            }
            reactData.optList = restList;
            return $xeSelect.$nextTick();
        },
        updateYData() {
            const $xeSelect = this;
            $xeSelect.handleData();
            $xeSelect.updateYSpace();
        },
        computeScrollLoad() {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            return $xeSelect.$nextTick().then(() => {
                const { scrollYLoad } = reactData;
                const { scrollYStore } = internalData;
                const virtualBodyElem = $xeSelect.$refs.refVirtualBody;
                const virtualYOpts = $xeSelect.computeVirtualYOpts;
                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 = $xeSelect.$refs.refVirtualWrapper;
                    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);
                    $xeSelect.updateYData();
                }
                else {
                    $xeSelect.updateYSpace();
                }
            });
        },
        handleScrollToOption(option, isDwArrow) {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            const { optFullValMaps, scrollYStore } = internalData;
            const valueField = $xeSelect.computeValueField;
            const cacheItem = optFullValMaps[option[valueField]];
            if (cacheItem) {
                const optid = cacheItem.key;
                const avIndex = cacheItem._index;
                if (avIndex > -1) {
                    const optWrapperElem = $xeSelect.$refs.refVirtualWrapper;
                    const panelElem = $xeSelect.$refs.refOptionPanel;
                    const optElem = panelElem.querySelector(`[optid='${optid}']`);
                    if (optWrapperElem) {
                        if (optElem) {
                            const wrapperHeight = optWrapperElem.offsetHeight;
                            const offsetPadding = 1;
                            if (isDwArrow) {
                                if (Math.ceil(optElem.offsetTop + optElem.offsetHeight - optWrapperElem.scrollTop) >= wrapperHeight) {
                                    optWrapperElem.scrollTop = optElem.offsetTop + optElem.offsetHeight - wrapperHeight;
                                }
                                else if (optElem.offsetTop + offsetPadding < optWrapperElem.scrollTop || optElem.offsetTop + offsetPadding > optWrapperElem.scrollTop + optWrapperElem.clientHeight) {
                                    optWrapperElem.scrollTop = optElem.offsetTop - offsetPadding;
                                }
                            }
                            else {
                                if (optElem.offsetTop + offsetPadding < optWrapperElem.scrollTop || optElem.offsetTop + offsetPadding > optWrapperElem.scrollTop + optWrapperElem.clientHeight) {
                                    optWrapperElem.scrollTop = optElem.offsetTop - offsetPadding;
                                }
                                else if (optElem.offsetTop + optElem.offsetHeight - optWrapperElem.scrollTop > wrapperHeight) {
                                    optWrapperElem.scrollTop = optElem.offsetTop + optElem.offsetHeight - wrapperHeight;
                                }
                            }
                        }
                        else {
                            if (isDwArrow) {
                                optWrapperElem.scrollTop = avIndex * scrollYStore.rowHeight - optWrapperElem.clientHeight + scrollYStore.rowHeight;
                            }
                            else {
                                optWrapperElem.scrollTop = avIndex * scrollYStore.rowHeight;
                            }
                        }
                    }
                }
            }
        },
        /**
         * 如果有滚动条，则滚动到对应的位置
         * @param {Number} scrollLeft 左距离
         * @param {Number} scrollTop 上距离
         */
        scrollTo(scrollLeft, scrollTop) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const scrollBodyElem = $xeSelect.$refs.refVirtualWrapper;
            if (scrollBodyElem) {
                if (XEUtils.isNumber(scrollLeft)) {
                    scrollBodyElem.scrollLeft = scrollLeft;
                }
                if (XEUtils.isNumber(scrollTop)) {
                    scrollBodyElem.scrollTop = scrollTop;
                }
            }
            if (reactData.scrollYLoad) {
                return new Promise(resolve => {
                    setTimeout(() => {
                        $xeSelect.$nextTick(() => {
                            resolve();
                        });
                    }, 50);
                });
            }
            return $xeSelect.$nextTick();
        },
        /**
         * 刷新滚动条
         */
        refreshScroll() {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            const { lastScrollLeft, lastScrollTop } = internalData;
            return $xeSelect.clearScroll().then(() => {
                if (lastScrollLeft || lastScrollTop) {
                    internalData.lastScrollLeft = 0;
                    internalData.lastScrollTop = 0;
                    return $xeSelect.scrollTo(lastScrollLeft, lastScrollTop);
                }
            });
        },
        /**
         * 重新计算列表
         */
        recalculate() {
            const $xeSelect = this;
            const el = $xeSelect.$refs.refElem;
            if (el && el.clientWidth && el.clientHeight) {
                return $xeSelect.computeScrollLoad();
            }
            return Promise.resolve();
        },
        loadYData(evnt) {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            const { scrollYStore } = internalData;
            const { startIndex, endIndex, visibleSize, offsetSize, rowHeight } = scrollYStore;
            const scrollBodyElem = evnt.target;
            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;
                    $xeSelect.updateYData();
                }
            }
        },
        // 滚动、拖动过程中不需要触发
        isVMScrollProcess() {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            const delayHover = 250;
            const { lastScrollTime } = internalData;
            return !!(lastScrollTime && Date.now() < lastScrollTime + delayHover);
        },
        scrollEvent(evnt) {
            const $xeSelect = this;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            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) {
                $xeSelect.loadYData(evnt);
            }
            internalData.lastScrollTime = Date.now();
            $xeSelect.dispatchEvent('scroll', { scrollLeft, scrollTop, isX, isY }, evnt);
        },
        /**
         * 加载数据
         * @param {Array} datas 数据
         */
        loadData(datas) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            $xeSelect.cacheItemMap(datas || []);
            const { multiple } = props;
            const { isLoaded, fullData, scrollYStore } = internalData;
            const defaultOpts = $xeSelect.computeDefaultOpts;
            const virtualYOpts = $xeSelect.computeVirtualYOpts;
            const valueField = $xeSelect.computeValueField;
            Object.assign(scrollYStore, {
                startIndex: 0,
                endIndex: 1,
                visibleSize: 0
            });
            internalData.synchData = datas || [];
            // 如果gt为0，则总是启用
            reactData.scrollYLoad = !!virtualYOpts.enabled && virtualYOpts.gt > -1 && (virtualYOpts.gt === 0 || virtualYOpts.gt <= fullData.length);
            $xeSelect.handleData();
            if (!isLoaded) {
                const { selectMode } = defaultOpts;
                if (datas.length > 0 && XEUtils.eqNull(props.value)) {
                    if (selectMode === 'all') {
                        if (multiple) {
                            $xeSelect.$nextTick(() => {
                                $xeSelect.emitDefaultValue(datas.map(item => item[valueField]));
                            });
                        }
                        else {
                            errLog('vxe.error.notConflictProp', ['default-config.selectMode=all', 'multiple=true']);
                        }
                    }
                    else if (selectMode === 'first' || selectMode === 'last') {
                        const selectItem = XEUtils[selectMode](datas);
                        if (selectItem) {
                            $xeSelect.$nextTick(() => {
                                if (XEUtils.eqNull(props.value)) {
                                    $xeSelect.emitDefaultValue(selectItem[valueField]);
                                }
                            });
                        }
                    }
                    internalData.isLoaded = true;
                }
            }
            return $xeSelect.computeScrollLoad().then(() => {
                $xeSelect.refreshScroll();
            });
        },
        reloadData(datas) {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            internalData.isLoaded = false;
            $xeSelect.clearScroll();
            return $xeSelect.loadData(datas);
        },
        clearScroll() {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            const scrollBodyElem = $xeSelect.$refs.refVirtualWrapper;
            if (scrollBodyElem) {
                scrollBodyElem.scrollTop = 0;
                scrollBodyElem.scrollLeft = 0;
            }
            internalData.lastScrollTop = 0;
            internalData.lastScrollLeft = 0;
            return $xeSelect.$nextTick();
        },
        hasOptGroupById(optid) {
            const $xeSelect = this;
            const internalData = $xeSelect.internalData;
            const { optGroupKeyMaps } = internalData;
            return !!optGroupKeyMaps[optid];
        },
        //
        // Render
        //
        renderRadio(h, option, isOptGroup, isChecked, isDisabled) {
            const $xeSelect = this;
            const radioOpts = $xeSelect.computeRadioOpts;
            const { showIcon, trigger } = radioOpts;
            if (isOptGroup || showIcon === false) {
                return renderEmptyElement($xeSelect);
            }
            const ons = {};
            if (!trigger || trigger === 'default') {
                ons.click = (evnt) => {
                    if (!isDisabled && !isOptGroup) {
                        $xeSelect.changeOptionEvent(evnt, option);
                    }
                };
            }
            return h('div', {
                key: 2,
                class: ['vxe-select-option--radio', {
                        'is--checked': isChecked,
                        'is--disabled': isDisabled
                    }],
                on: ons
            }, [
                h('span', {
                    class: ['vxe-radio--icon', isChecked ? getIcon().RADIO_CHECKED : getIcon().RADIO_UNCHECKED]
                })
            ]);
        },
        renderCheckbox(h, option, isOptGroup, isChecked, isDisabled) {
            const $xeSelect = this;
            const checkboxOpts = $xeSelect.computeCheckboxOpts;
            const { showIcon, trigger } = checkboxOpts;
            if (isOptGroup || showIcon === false) {
                return renderEmptyElement($xeSelect);
            }
            const ons = {};
            if (!trigger || trigger === 'default') {
                ons.click = (evnt) => {
                    if (!isDisabled && !isOptGroup) {
                        $xeSelect.changeOptionEvent(evnt, option);
                    }
                };
            }
            return h('div', {
                key: 3,
                class: ['vxe-select-option--checkbox', {
                        'is--checked': isChecked,
                        'is--disabled': isDisabled
                    }],
                on: ons
            }, [
                h('span', {
                    class: ['vxe-checkbox--icon', isChecked ? getIcon().CHECKBOX_CHECKED : getIcon().CHECKBOX_UNCHECKED]
                })
            ]);
        },
        renderOption(h, list) {
            const $xeSelect = this;
            const props = $xeSelect;
            const slots = $xeSelect.$scopedSlots;
            const reactData = $xeSelect.reactData;
            const internalData = $xeSelect.internalData;
            const { allowCreate, optionKey, multiple, showRadio, showCheckbox } = props;
            const { currentOption } = reactData;
            const { optAddMaps } = internalData;
            const optionOpts = $xeSelect.computeOptionOpts;
            const labelField = $xeSelect.computeLabelField;
            const valueField = $xeSelect.computeValueField;
            const groupLabelField = $xeSelect.computeGroupLabelField;
            const selectVals = $xeSelect.computeSelectVals;
            const radioOpts = $xeSelect.computeRadioOpts;
            const checkboxOpts = $xeSelect.computeCheckboxOpts;
            const { useKey, height } = optionOpts;
            const optionSlot = slots.option;
            const isCheckboxMode = showCheckbox && multiple;
            const isRadioMode = showRadio && !multiple;
            return list.map((option, cIndex) => {
                const { slots, className } = option;
                const optid = $xeSelect.getOptId(option);
                const optionValue = option[valueField];
                const isOptGroup = $xeSelect.hasOptGroupById(optid);
                const isAdd = !!(allowCreate && optAddMaps[optid]);
                const isSelected = !isAdd && selectVals.indexOf(optionValue) > -1;
                const isVisible = isAdd || (!isOptGroup || isOptionVisible(option));
                const isDisabled = !isAdd && $xeSelect.checkOptionDisabled(isSelected, option);
                const defaultSlot = slots ? slots.default : null;
                const optSlot = defaultSlot || optionSlot;
                const optParams = { option, group: isOptGroup ? option : null, $select: $xeSelect };
                let optLabel = '';
                const optVNs = [];
                if (isCheckboxMode) {
                    optVNs.push($xeSelect.renderCheckbox(h, option, isOptGroup, isSelected, isDisabled));
                }
                else if (isRadioMode) {
                    optVNs.push($xeSelect.renderRadio(h, option, isOptGroup, isSelected, isDisabled));
                }
                let otlbVNs = [];
                if (optSlot) {
                    otlbVNs = $xeSelect.callSlot(optSlot, optParams, h);
                }
                else {
                    optLabel = getFuncText(option[(isOptGroup ? groupLabelField : labelField)] || optionValue);
                    otlbVNs = [optLabel];
                }
                optVNs.push(h('div', {
                    key: 1,
                    class: 'vxe-select-option--label'
                }, otlbVNs));
                const ons = {
                    mousedown: (evnt) => {
                        const isLeftBtn = evnt.button === 0;
                        if (isLeftBtn) {
                            evnt.stopPropagation();
                        }
                    },
                    mouseenter: () => {
                        if (!isDisabled && !isOptGroup && !$xeSelect.isVMScrollProcess()) {
                            $xeSelect.setCurrentOption(option);
                        }
                    }
                };
                if (isCheckboxMode ? checkboxOpts.trigger === 'option' : (isRadioMode ? radioOpts.trigger === 'option' : true)) {
                    ons.click = (evnt) => {
                        if (!isDisabled && !isOptGroup) {
                            $xeSelect.changeOptionEvent(evnt, option);
                        }
                    };
                }
                return isVisible
                    ? h('div', {
                        key: useKey || optionKey ? optid : cIndex,
                        class: ['vxe-select-option', className ? (XEUtils.isFunction(className) ? className(optParams) : className) : '', {
                                'vxe-select-optgroup': isOptGroup,
                                'is--disabled': isDisabled,
                                'is--selected': isSelected,
                                'is--add': isAdd,
                                'is--hover': currentOption && $xeSelect.getOptId(currentOption) === optid
                            }],
                        attrs: {
                            optid: optid,
                            title: optLabel || null
                        },
                        style: height
                            ? {
                                height: toCssUnit(height)
                            }
                            : undefined,
                        on: ons
                    }, allowCreate
                        ? [
                            h('span', {
                                key: 1,
                                class: 'vxe-select-option--add-label'
                            }, optVNs),
                            isAdd
                                ? h('span', {
                                    key: 2,
                                    class: 'vxe-select-option--add-icon'
                                }, [
                                    h('i', {
                                        class: getIcon().SELECT_ADD_OPTION
                                    })
                                ])
                                : renderEmptyElement($xeSelect)
                        ]
                        : optVNs)
                    : renderEmptyElement($xeSelect);
            });
        },
        renderOpts(h) {
            const $xeSelect = this;
            const props = $xeSelect;
            const reactData = $xeSelect.reactData;
            const { optList, searchLoading } = reactData;
            if (searchLoading) {
                return [
                    h('div', {
                        class: 'vxe-select--search-loading'
                    }, [
                        h('i', {
                            class: ['vxe-select--search-icon', getIcon().SELECT_LOADED]
                        }),
                        h('span', {
                            class: 'vxe-select--search-text'
                        }, getI18n('vxe.select.loadingText'))
                    ])
                ];
            }
            if (optList.length) {
                return $xeSelect.renderOption(h, optList);
            }
            return [
                h('div', {
                    class: 'vxe-select--empty-placeholder'
                }, props.emptyText || getI18n('vxe.select.emptyText'))
            ];
        },
        renderVN(h) {
            const $xeSelect = this;
            const props = $xeSelect;
            const slots = $xeSelect.$scopedSlots;
            const reactData = $xeSelect.reactData;
            const { className, multiple, loading, filterable, showTotalButton, showCheckedButton, showClearButton, showCloseButton } = props;
            const { initialized, isActivated, isAniVisible, optList, visiblePanel, bodyHeight, topSpaceHeight } = reactData;
            const vSize = $xeSelect.computeSize;
            const isDisabled = $xeSelect.computeIsDisabled;
            const selectLabel = $xeSelect.computeSelectLabel;
            const fullLabel = $xeSelect.computeFullLabel;
            const btnTransfer = $xeSelect.computeBtnTransfer;
            const formReadonly = $xeSelect.computeFormReadonly;
            const inpPlaceholder = $xeSelect.computeInpPlaceholder;
            const popupOpts = $xeSelect.computePopupOpts;
            const popupWrapperStyle = $xeSelect.computePopupWrapperStyle;
            const defaultSlot = slots.default;
            const headerSlot = slots.header;
            const footerSlot = slots.footer;
            const prefixSlot = slots.prefix;
            const ppClassName = popupOpts.className || props.popupClassName;
            if (formReadonly) {
                return h('div', {
                    ref: 'refElem',
                    class: ['vxe-select--readonly', className]
                }, [
                    h('div', {
                        class: 'vxe-select-slots',
                        ref: 'hideOption'
                    }, defaultSlot ? $xeSelect.callSlot(defaultSlot, {}, h) : []),
                    h('span', {
                        class: 'vxe-select-label',
                        attrs: {
                            fullLabel
                        }
                    }, selectLabel)
                ]);
            }
            const selectVals = $xeSelect.computeSelectVals;
            return h('div', {
                ref: 'refElem',
                class: ['vxe-select', className ? (XEUtils.isFunction(className) ? className({ $select: $xeSelect }) : className) : '', {
                        [`size--${vSize}`]: vSize,
                        'is--visible': visiblePanel,
                        'is--disabled': isDisabled,
                        'is--filter': filterable,
                        'is--loading': loading,
                        'is--active': isActivated
                    }]
            }, [
                h('div', {
                    class: 'vxe-select-slots',
                    ref: 'hideOption'
                }, defaultSlot ? defaultSlot.call($xeSelect, {}) : []),
                h(VxeInputComponent, {
                    ref: 'refInput',
                    props: {
                        clearable: props.clearable,
                        placeholder: inpPlaceholder,
                        editable: false,
                        disabled: isDisabled,
                        type: 'text',
                        prefixIcon: props.prefixIcon,
                        suffixIcon: loading ? getIcon().SELECT_LOADED : (visiblePanel ? getIcon().SELECT_OPEN : getIcon().SELECT_CLOSE),
                        autoFocus: false,
                        title: fullLabel,
                        value: selectLabel
                    },
                    on: {
                        clear: $xeSelect.clearEvent,
                        click: $xeSelect.clickEvent,
                        focus: $xeSelect.focusEvent,
                        blur: $xeSelect.blurEvent,
                        'suffix-click': $xeSelect.suffixClickEvent
                    },
                    scopedSlots: prefixSlot
                        ? {
                            prefix: () => prefixSlot({})
                        }
                        : {}
                }),
                h('div', {
                    ref: 'refOptionPanel',
                    class: ['vxe-table--ignore-clear vxe-select--panel', ppClassName ? (XEUtils.isFunction(ppClassName) ? ppClassName({ $select: $xeSelect }) : ppClassName) : '', {
                            [`size--${vSize}`]: vSize,
                            'is--transfer': btnTransfer,
                            'ani--leave': !loading && isAniVisible,
                            'ani--enter': !loading && visiblePanel
                        }],
                    attrs: {
                        placement: reactData.panelPlacement
                    },
                    style: reactData.panelStyle
                }, initialized && (visiblePanel || isAniVisible)
                    ? [
                        h('div', {
                            class: 'vxe-select--panel-wrapper',
                            style: popupWrapperStyle
                        }, [
                            filterable
                                ? h('div', {
                                    class: 'vxe-select--panel-search'
                                }, [
                                    h(VxeInputComponent, {
                                        ref: 'refInpSearch',
                                        class: 'vxe-select-search--input',
                                        props: {
                                            value: reactData.searchValue,
                                            type: 'text',
                                            clearable: true,
                                            disabled: false,
                                            readonly: false,
                                            placeholder: getI18n('vxe.select.search'),
                                            prefixIcon: getIcon().INPUT_SEARCH
                                        },
                                        on: {
                                            'model-value': $xeSelect.modelSearchEvent,
                                            focus: $xeSelect.focusSearchEvent,
                                            change: $xeSelect.triggerSearchEvent,
                                            search: $xeSelect.triggerSearchEvent
                                        }
                                    })
                                ])
                                : renderEmptyElement($xeSelect),
                            (showCheckedButton && multiple) || showClearButton || headerSlot
                                ? h('div', {
                                    class: 'vxe-select--panel-header'
                                }, headerSlot
                                    ? $xeSelect.callSlot(headerSlot, {}, h)
                                    : [
                                        h('div', {
                                            class: 'vxe-tree-select--header-button'
                                        }, [
                                            h('div', {
                                                class: 'vxe-tree-select--header-btns'
                                            }, [
                                                (showCheckedButton && multiple)
                                                    ? h(VxeButtonComponent, {
                                                        props: {
                                                            content: getI18n('vxe.select.allChecked'),
                                                            mode: 'text'
                                                        },
                                                        on: {
                                                            click: $xeSelect.allCheckedPanelEvent
                                                        }
                                                    })
                                                    : renderEmptyElement($xeSelect),
                                                showClearButton
                                                    ? h(VxeButtonComponent, {
                                                        props: {
                                                            content: getI18n('vxe.select.clear'),
                                                            mode: 'text'
                                                        },
                                                        on: {
                                                            click: $xeSelect.clearCheckedPanelEvent
                                                        }
                                                    })
                                                    : renderEmptyElement($xeSelect)
                                            ])
                                        ])
                                    ])
                                : renderEmptyElement($xeSelect),
                            h('div', {
                                class: 'vxe-select--panel-body'
                            }, [
                                h('div', {
                                    ref: 'refVirtualWrapper',
                                    class: 'vxe-select-option--wrapper',
                                    on: {
                                        scroll: $xeSelect.scrollEvent
                                    }
                                }, [
                                    h('div', {
                                        class: 'vxe-select--y-space',
                                        style: {
                                            height: bodyHeight ? `${bodyHeight}px` : ''
                                        }
                                    }),
                                    h('div', {
                                        ref: 'refVirtualBody',
                                        class: 'vxe-select--body',
                                        style: {
                                            transform: `translateY(${topSpaceHeight}px)`
                                        }
                                    }, $xeSelect.renderOpts(h))
                                ])
                            ]),
                            footerSlot || showTotalButton || (showCloseButton && multiple)
                                ? h('div', {
                                    class: 'vxe-select--panel-footer'
                                }, footerSlot
                                    ? $xeSelect.callSlot(footerSlot, {}, h)
                                    : [
                                        h('div', {
                                            class: 'vxe-select--footer-button'
                                        }, [
                                            showTotalButton
                                                ? h('div', {
                                                    class: 'vxe-select--total-btns'
                                                }, getI18n('vxe.select.total', [selectVals.length, optList.length]))
                                                : renderEmptyElement($xeSelect),
                                            showCloseButton && multiple
                                                ? h('div', {
                                                    class: 'vxe-select--oper-btns'
                                                }, [
                                                    h(VxeButtonComponent, {
                                                        props: {
                                                            content: getI18n('vxe.select.close'),
                                                            mode: 'text'
                                                        },
                                                        on: {
                                                            click: $xeSelect.closePanelEvent
                                                        }
                                                    })
                                                ])
                                                : renderEmptyElement($xeSelect)
                                        ])
                                    ])
                                : renderEmptyElement($xeSelect)
                        ])
                    ]
                    : [])
            ]);
        }
    },
    watch: {
        'reactData.staticOptions'(val) {
            const $xeSelect = this;
            $xeSelect.loadData(val);
        },
        options(val) {
            const $xeSelect = this;
            $xeSelect.loadData(val);
        },
        optionGroups(val) {
            const $xeSelect = this;
            $xeSelect.loadData(val);
        }
    },
    created() {
        const $xeSelect = this;
        $xeSelect.internalData = createInternalData();
    },
    mounted() {
        const $xeSelect = this;
        const props = $xeSelect;
        $xeSelect.$nextTick(() => {
            const { options, optionGroups } = props;
            if (optionGroups) {
                $xeSelect.loadData(optionGroups);
            }
            else if (options) {
                $xeSelect.loadData(options);
            }
        });
        globalEvents.on($xeSelect, 'mousewheel', $xeSelect.handleGlobalMousewheelEvent);
        globalEvents.on($xeSelect, 'mousedown', $xeSelect.handleGlobalMousedownEvent);
        globalEvents.on($xeSelect, 'keydown', $xeSelect.handleGlobalKeydownEvent);
        globalEvents.on($xeSelect, 'blur', $xeSelect.handleGlobalBlurEvent);
        globalEvents.on($xeSelect, 'resize', $xeSelect.handleGlobalResizeEvent);
    },
    beforeDestroy() {
        const $xeSelect = this;
        const panelElem = $xeSelect.$refs.refOptionPanel;
        if (panelElem && panelElem.parentNode) {
            panelElem.parentNode.removeChild(panelElem);
        }
        globalEvents.off($xeSelect, 'mousewheel');
        globalEvents.off($xeSelect, 'mousedown');
        globalEvents.off($xeSelect, 'keydown');
        globalEvents.off($xeSelect, 'blur');
        globalEvents.off($xeSelect, 'resize');
    },
    destroyed() {
        const $xeSelect = this;
        const reactData = $xeSelect.reactData;
        const internalData = $xeSelect.internalData;
        XEUtils.assign(reactData, createReactData());
        XEUtils.assign(internalData, createInternalData());
    },
    render(h) {
        return this.renderVN(h);
    }
};
