<template>
    <div
        v-click-outside.captures="onClickOutside"
        v-click-outside:mousedown.capture="onClickOutside"
        ref="wrapper"
        class="zz-query-selector">
        <Dropdown
            :transfer="transfer"
            :visible="visible"
            placement="bottom-start"
            :transfer-class-name="transfer?transferClassName:''"
            trigger="custom">
            <div class="zz-select-search">
                <div
                    class="show-selected"
                    ref="selectedTitleRef"
                    :class="{
                        'show-selected-disabled': disabled,
                        'show-selected-enabled': !disabled,
                    }"
                    @click="selectedClickHandle"
                    v-show="currentValue && focused === false">
                        <slot name="showSelected" v-bind:selected="currentSelected"><span :title="currentSelected[labelName]">{{currentSelected[labelName]}}</span></slot>
                        <Icon @click.stop.prevent="cleanHandle" type="ios-close" v-if="clearable&&!disabled" />
                </div>
                <Input
                    key="top"
                    class="input-top"
                    :disabled="disabled"
                    :style="{opacity: focused ? 1 : 0}"
                    v-show="!currentValue||focused"
                    v-model.trim="searchKey"
                    @on-change="changeHandle"
                    @on-keydown="inputKeydownHandle"
                    @click.native="clickInputHandle"
                    :clearable="false"
                    :placeholder="placeholder"
                    @on-focus="focusHandle" />
                <Input
                    key="bottom"
                    class="input-bottom"
                    :class="{'enabled': !disabled}"
                    v-show="!currentValue||focused"
                    :style="{opacity: focused ? 0 : 1}"
                    disabled
                    :placeholder="placeholder" />
            </div>
            <DropdownMenu slot="list">
                <div
                    :class="`zz-select-result ${dropdownMenuClassName}`">
                    <ul
                        v-if="searchKey.length > 0"
                        ref="scrollWrapper">
                        <li
                            :class="{
                                'item-active': choiceIndex === index
                            }"
                            ref="resultItem"
                            v-for="(item, index) in resultList"
                            :key="`${item[keyName]}${index}`"
                            @mousedown="selectHandle(item, index)">
                            <slot name="item" v-bind:item="item" v-bind:index="index"><span :title="item[labelName]">{{item[labelName]}}</span></slot>
                        </li>
                        <li
                            class="loading-tip-item"
                            v-show="resultList.length === 0
                            && loading === true
                            && searchKey.length > 0">
                            <Spin>
                              <Icon type="ios-loading" size=16 class="demo-spin-icon-load"></Icon>
                              <span>加载中</span>
                            </Spin>
                        </li>
                        <li
                            class="no-found-item"
                            v-show="resultList.length === 0
                            && loading === false
                            && searchKey.length > 0">
                            <span>暂无数据</span>
                        </li>
                    </ul>
                </div>
            </DropdownMenu>
        </Dropdown>
    </div>
</template>
<script>
import Icon from '../icon';
import Dropdown from '../dropdown';
import DropdownMenu from '../dropdown/dropdown-menu.vue';
import Input from '../input';
import Spin from '../spin';
import { directive as clickOutside } from '../../directives/v-click-outside-x';
import { scrollTop, isEqual } from '../../utils/assist';

export default {
    name: 'QuerySelector',
    components: {
        Icon,
        Dropdown,
        DropdownMenu,
        Input,
        Spin
    },
    directives: {
        clickOutside,
    },
    props: {
        // 是否添加清空功能
        clearable: {
            type: Boolean,
            default: true
        },
        disabled: {
            type: Boolean,
            default: false
        },
        // 接收选中的value
        value: {
            type: [String, Number],
            default: ''
        },
        // 接收空白时候的placeholder
        placeholder: {
            type: String,
            default: '请输入'
        },
        // 是否把弹框插入body
        transfer: {
            type: Boolean,
            default: false
        },
        // 是否触发dispatch change事件
        noEmitChange: {
            type: Boolean,
            default: false
        },
        // 当transfer为true的时候给外层添加样式
        transferClassName: {
            type: String,
            default: 'transfer-query-selector'
        },
        // DropdownMenu class名字，用来覆盖内部默认200px宽度
        dropdownMenuClassName: {
            type: String,
            default: ''
        },
        // 接收列表遍历key的名称
        keyName: {
            type: String,
            required: true
        },
        // 选中后默认显示在选中项位置展示，以及默认列表里显示的内容
        labelName: {
            type: String,
            default: ''
        },
        // 接收选中对象
        selected: {
            type: Object,
            default: () => ({})
        },
        remoteMethod: {
            type: Function,
            default: () => () => {}
        },
    },
    data () {
        return {
            // 当前通过向上，向下选中的下拉项选中值，默认-1表示无选中
            choiceIndex: -1,
            // dropdown是否显示搜索结果弹框
            visible: false,
            // 搜索关键字
            searchKey: '',
            // 备份一份searchKey
            searchKeyCopy: '',
            // 搜索结果
            resultList: [],
            // loading
            loading: false,
            // 保存当前选中项
            currentValue: this.value,
            // 保存选中项
            currentSelected: {
                ...this.selected
            },
            // 记录一个状态focused,只有input是focused,当searchKey改变的时候才触发请求
            focused: false,
        };
    },
    watch: {
        selected: {
            handler (val, oldVal) {
                if (isEqual(val, oldVal)) return;
                this.currentSelected = {
                    ...val
                };
                if (typeof val === 'object') {
                    this.currentValue = val[this.keyName];
                    this.$emit('input', this.currentValue);
                }
            }
        },
        value: {
            handler (val) {
                if (val !== this.currentValue) {
                    this.currentValue = val;
                }
            }
        },
        data: {
            handler (val, oldVal) {
                if (isEqual(val, oldVal)) return;
                this.choiceIndex = -1;
                this.resultList = val;
            }
        }
    },
    methods: {
        // 清空回调
        cleanHandle () {
            this.currentValue = '';
            this.searchKey = '';
            this.searchKeyCopy = '';
            this.resultList = [];
            this.currentSelected = {};
            this.$emit('on-change', '');
            this.$emit('on-clear');
            this.$emit('input', '');
        },
        selectedClickHandle () {
            // 如果是disabled则直接返回
            if (this.disabled) return;
            this.focused = true;
            this.visible = true;
            this.$nextTick(() => {
                this.$refs.wrapper.querySelector('input').focus();
            });
        },
        changeHandle (event) {
            event.target.value = event.target.value.trim();
            if (event.target.value === this.searchKeyCopy) {
                // 兼容IE触发focus就触发了change回调
                return false;
            }
            this.searchKeyCopy = event.target.value;
            this.$emit('on-query-change', event.target.value);
            if (!this.focused) {
                return false;
            }
            this.visible = true;
            this.getData();
            return false;
        },
        clickInputHandle () {
            this.focusHandle();
        },
        focusHandle () {
            this.focused = true;
            if (this.searchKey.trim() !== '') {
                this.visible = true;
            }
        },
        inputKeydownHandle (event) {
            // 监听向上，向下，以及按回车;
            if (event.keyCode === 38) {
                // 表示按了向上键
                this.upHandle();
                event.preventDefault();
            } else if (event.keyCode === 40) {
                // 表示按了向上键
                this.downHandle();
                event.preventDefault();
            } else if (event.keyCode === 13) {
                this.enterHandle();
                event.preventDefault();
            } else if (event.keyCode === 9) {
                // 如果按的是tab，则触发消失
                this.onClickOutside();
            }
        },
        upHandle () {
            if (this.resultList.length === 0) {
                return;
            }
            if (this.choiceIndex === -1 || this.choiceIndex === 0) {
                this.choiceIndex = this.resultList.length - 1;
            } else {
                this.choiceIndex -= 1;
            }
            this.changeScroll(this.choiceIndex, 'up');
        },
        downHandle () {
            if (this.resultList.length === 0) {
                return;
            }
            if (this.choiceIndex === -1 || this.choiceIndex === this.resultList.length - 1) {
                this.choiceIndex = 0;
            } else {
                this.choiceIndex += 1;
            }
            this.changeScroll(this.choiceIndex, 'down');
        },
        enterHandle () {
            if (this.resultList.length === 0 || this.choiceIndex === -1) {
                return;
            }
            this.selectHandle(this.resultList[this.choiceIndex], this.choiceIndex);
        },
        changeScroll (currentIndex) {
            // 判断选中项目有没有在滚动轴范围内，
            const wrapper = this.$refs.scrollWrapper;
            const active = this.$refs.scrollWrapper.children[currentIndex];
            if (wrapper && active) {
                const wrapperHeight = wrapper.clientHeight;
                const wrapperScrollTop = wrapper.scrollTop;
                const activeHeight = active.clientHeight;
                const activeTop = active.offsetTop;
                let scrollTo = wrapperScrollTop;
                if (wrapperScrollTop >= activeTop) {
                    scrollTo = wrapperScrollTop - (wrapperScrollTop - activeTop);
                    scrollTop(wrapper, wrapperScrollTop, scrollTo, 100);
                } else if (wrapperHeight + wrapperScrollTop <= activeTop + activeHeight) {
                    scrollTo = wrapperScrollTop + activeTop + activeHeight - wrapperHeight - wrapperScrollTop;
                    scrollTop(wrapper, wrapperScrollTop, scrollTo, 100);
                } 
            }
        },
        selectHandle (item, index) {
            this.choiceIndex = index;
            this.visible = false;
            this.focused = false;
            // 触发on-select回调
            this.$nextTick(() => {
                this.$emit('on-select', { ...item });
            });
            this.currentValue = item[this.keyName];
            this.currentSelected = { ...item };
            this.$emit('on-change', { ...item });
            this.$emit('input', this.currentValue);
        },
        reset () {
            // 重置方法
            this.visible = false;
            // 搜索关键词
            this.searchKey = '';
            this.searchKeyCopy = '';
             // 搜索结果
            this.resultList = [];
            this.choiceIndex = -1;
            // 保存当前选中项的员工id
            this.currentValue = '';
            // 及时$emit出去
            this.$emit('input', '');
            // 记录一个状态focused，只有input是focused，当searchKey改变的时候才触发请求
            this.focused = false;
        },
        async getData () {
            if (this.searchKey === '') {
                this.resultList = [];
                this.loading = false;
                this.choiceIndex = -1;
                return;
            }
            if (this.disabled) {
                return;
            }
            if (typeof this.remoteMethod === 'function') {
                this.resultList = [];
                this.loading = true;
                try {
                    this.resultList = await this.remoteMethod(this.searchKey);
                } catch (err) {
                    this.resultList = [];
                } finally {
                    this.loading = false;
                    this.choiceIndex = -1;
                }
            }
        },
        onClickOutside () {
            if (this.disabled) {
                return;
            }
            this.visible = false;
            this.focused = false;
        }
    },
};
</script>
