ivue-material-plus
Version:
A high quality UI components Library with Vue.js
1 lines • 56.9 kB
Source Map (JSON)
{"version":3,"file":"index2.mjs","sources":["../../../../../src/components/ivue-select/index.vue"],"sourcesContent":["<template>\r\n <div\r\n ref=\"selectWrapper\"\r\n :class=\"classes\"\r\n v-click-outside:[capture]=\"handleClickOutside\"\r\n v-click-outside:[capture].mousedown=\"handleClickOutside\"\r\n >\r\n <div\r\n ref=\"reference\"\r\n :class=\"selectionClasses\"\r\n :tabindex=\"selectTabindex\"\r\n @blur=\"handleHeaderFocus\"\r\n @focus=\"handleHeaderFocus\"\r\n @click=\"handleToggleMenu\"\r\n @mouseenter=\"data.hasMouseHover = true\"\r\n @mouseleave=\"data.hasMouseHover = false\"\r\n @keydown.down.stop.prevent=\"handleKeyDown\"\r\n @keydown.up.stop.prevent=\"handleKeyDown\"\r\n @keydown.enter.stop.prevent=\"handleKeyDown\"\r\n @keydown.esc.stop.prevent=\"handleKeyDown\"\r\n @keydown.tab=\"handleKeyDown\"\r\n >\r\n <slot name=\"input\">\r\n <input type=\"hidden\" :name=\"name\" :value=\"currentSelectValue\" />\r\n <!-- 头部 -->\r\n <select-head\r\n :id=\"inputId\"\r\n :prefix=\"prefix\"\r\n :filterable=\"filterable\"\r\n :multiple=\"multiple\"\r\n :maxTagCount=\"maxTagCount\"\r\n :multipleIcon=\"multipleIcon\"\r\n :maxTagPlaceholder=\"maxTagPlaceholder\"\r\n :placeholder=\"placeholder\"\r\n :arrowDownIcon=\"arrowDownIcon\"\r\n :clearable=\"canClearable\"\r\n :values=\"data.values\"\r\n :isSearchMethod=\"isSearchMethod\"\r\n :resetSelectIcon=\"resetSelectIcon\"\r\n :disabled=\"inputDisabled\"\r\n :filterQueryProp=\"data.filterQuery\"\r\n :allowCreate=\"allowCreate\"\r\n :showCreateItem=\"showCreateItem\"\r\n @on-clear=\"clearSingleSelect\"\r\n @on-filter-query-change=\"handleFilterQueryChange\"\r\n @on-input-focus=\"data.isFocused = true\"\r\n @on-input-blur=\"data.isFocused = false\"\r\n @on-enter=\"handleCreateItem\"\r\n ref=\"selectHead\"\r\n >\r\n <template #prefix v-if=\"$slots.prefix\">\r\n <slot name=\"prefix\"></slot>\r\n </template>\r\n </select-head>\r\n </slot>\r\n </div>\r\n <!-- 下拉菜单 -->\r\n <transition name=\"transition-drop\">\r\n <drop-down\r\n :transfer=\"transfer\"\r\n :data-transfer=\"transfer\"\r\n :placement=\"placement\"\r\n :class=\"dropdownClasses\"\r\n v-transfer-dom\r\n key=\"IvueSelectDropdown\"\r\n ref=\"dropdown\"\r\n v-show=\"dropVisible\"\r\n >\r\n <!-- 没有找到数据时的提示 -->\r\n <ul\r\n :class=\"[`${prefixCls}-not-find`]\"\r\n v-show=\"showNotFindText && !allowCreate && notFindText\"\r\n >\r\n <li>{{ notFindText }}</li>\r\n </ul>\r\n <!-- 列表 -->\r\n <ul\r\n :class=\"`${prefixCls}-dropdown-list`\"\r\n v-if=\"!isSearchMethod || (isSearchMethod && !loading)\"\r\n >\r\n <!-- 创建选项 -->\r\n <ivue-option\r\n :class=\"createItemClasses\"\r\n :allowCreate=\"allowCreate\"\r\n :showCreateItem=\"showCreateItem\"\r\n :filterQuery=\"data.filterQuery\"\r\n @on-create=\"handleCreateItem\"\r\n v-if=\"showCreateItem\"\r\n >\r\n <!-- 搜索内容 -->\r\n <p class=\"text\">{{ data.filterQuery }}</p>\r\n <!-- 图标 -->\r\n <ivue-icon>{{ allowCreateIcon }}</ivue-icon>\r\n </ivue-option>\r\n <!-- slot -->\r\n <slot></slot>\r\n </ul>\r\n <!-- 加载中 -->\r\n <ul v-show=\"loading\" :class=\"`${prefixCls}-loading`\">\r\n <div :class=\"`${prefixCls}-loading--load`\">\r\n <div class=\"load\" v-ivue-loading=\"true\"></div>\r\n <!-- loadingText -->\r\n <span>{{ loadingText }}</span>\r\n </div>\r\n </ul>\r\n </drop-down>\r\n </transition>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\">\r\nimport {\r\n defineComponent,\r\n computed,\r\n reactive,\r\n getCurrentInstance,\r\n provide,\r\n ref,\r\n nextTick,\r\n watch,\r\n onMounted,\r\n inject,\r\n} from 'vue';\r\nimport mitt from 'mitt';\r\n\r\n// 输入框\r\nimport SelectHead from './select-head.vue';\r\n// 下拉框\r\nimport DropDown from './drop-down.vue';\r\nimport IvueOption from './option.vue';\r\nimport IvueIcon from '../ivue-icon/index.vue';\r\nimport IvueLoading from '../ivue-loading/directive';\r\n\r\n// 注册外部点击事件插件\r\nimport { ClickOutside, TransferDom } from '../../utils/directives';\r\nimport { oneOf } from '../../utils/assist';\r\nimport { useFormItem, useFormItemInputId, useDisabled } from '../../hooks';\r\nimport { debugWarn } from '../../utils/error';\r\n\r\n// type\r\nimport { PopoverContextKey } from '../ivue-popover/types/popover';\r\nimport {\r\n Props,\r\n Data,\r\n DropDownInstance,\r\n SelectHeadInstance,\r\n SelectContextKey,\r\n OptionInstance,\r\n} from './types/select';\r\nimport type { OptionData } from './types/option';\r\n\r\nconst prefixCls = 'ivue-select';\r\n\r\nexport default defineComponent({\r\n name: prefixCls,\r\n // 注册局部指令\r\n directives: { ClickOutside, TransferDom, IvueLoading },\r\n emits: [\r\n 'update:modelValue',\r\n 'on-change',\r\n 'on-clear',\r\n 'on-menu-open',\r\n 'on-filter-query-change',\r\n 'on-set-default-options',\r\n 'on-create',\r\n 'on-select',\r\n ],\r\n props: {\r\n /**\r\n * 设置选择的值\r\n *\r\n * @type {String, Number, Array}\r\n */\r\n modelValue: {\r\n type: [String, Number, Array],\r\n default: '',\r\n },\r\n /**\r\n * 远程搜索时,显示默认 label,详见示例\r\n *\r\n * @type {String, Number, Array}\r\n */\r\n defaultLabel: {\r\n type: [String, Number, Array],\r\n default: '',\r\n },\r\n /**\r\n * 选择选项时的颜色\r\n *\r\n * @type {String | Array}\r\n */\r\n selectedColor: {\r\n type: [String, Array],\r\n default: '#5B8EFF',\r\n },\r\n /**\r\n * hover 选择选项时的颜色\r\n *\r\n * @type {String | Array}\r\n */\r\n hoverColor: {\r\n type: [String, Array],\r\n default: () => '#5B8EFF',\r\n },\r\n /**\r\n * 是否开启多选\r\n *\r\n * @type {Boolean}\r\n */\r\n multiple: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 是否开启多选过滤收起时清除输入\r\n *\r\n * @type {Boolean}\r\n */\r\n multipleFilterableClear: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 设置多选删除图标\r\n *\r\n * @type {Boolean}\r\n */\r\n multipleIcon: {\r\n type: String,\r\n default: 'cancel',\r\n },\r\n /**\r\n * 是否禁用选择组件\r\n *\r\n * @type {Boolean}\r\n */\r\n disabled: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 是否开启外部点击的 capture 模式,可通过全局配置\r\n *\r\n * @type {Boolean}\r\n */\r\n capture: {\r\n type: Boolean,\r\n default() {\r\n const global = getCurrentInstance().appContext.config.globalProperties;\r\n return !global.$IVUE ? true : global.$IVUE.capture;\r\n },\r\n },\r\n /**\r\n * 是否支持搜索\r\n *\r\n * @type {Boolean}\r\n */\r\n filterable: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * input name\r\n *\r\n * @type {String}\r\n */\r\n name: {\r\n type: String,\r\n },\r\n /**\r\n * 是否将label和value一起返回\r\n *\r\n * @type {Boolean}\r\n */\r\n labelAndValue: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 在 Select 内显示图标\r\n *\r\n * @type {String}\r\n */\r\n prefix: {\r\n type: String,\r\n },\r\n /**\r\n * 多选时最多显示多少个 tag\r\n *\r\n * @type {Number}\r\n */\r\n maxTagCount: {\r\n type: Number,\r\n },\r\n /**\r\n * 隐藏 tag 时显示的内容,参数是剩余项数量\r\n *\r\n * @type {Function}\r\n */\r\n maxTagPlaceholder: {\r\n type: Function,\r\n },\r\n /**\r\n * 输入提示\r\n *\r\n * @type {String}\r\n */\r\n placeholder: {\r\n type: String,\r\n default: '请选择',\r\n },\r\n /**\r\n * 是否允许用户创建新条目,需开启 filterable\r\n *\r\n * @type {Boolean}\r\n */\r\n allowCreate: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 创建新条目按钮图标\r\n *\r\n * @type {Boolean}\r\n */\r\n allowCreateIcon: {\r\n type: String,\r\n default: 'reply',\r\n },\r\n /**\r\n * 没有找到数据时的提示\r\n *\r\n * @type {String}\r\n */\r\n notFindText: {\r\n type: String,\r\n default: '无匹配数据',\r\n },\r\n /**\r\n * 搜索方法\r\n *\r\n * @type {Function}\r\n */\r\n searchMethod: {\r\n type: Function,\r\n },\r\n /**\r\n * 下拉图标\r\n *\r\n * @type {String}\r\n */\r\n arrowDownIcon: {\r\n type: String,\r\n default: 'keyboard_arrow_down',\r\n },\r\n /**\r\n * 是否可以清除选择\r\n *\r\n * @type {Boolean}\r\n */\r\n clearable: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 清除选择图标\r\n *\r\n * @type {String}\r\n */\r\n resetSelectIcon: {\r\n type: String,\r\n default: 'cancel',\r\n },\r\n /**\r\n * 开启搜索时,隐藏group组件头\r\n *\r\n * @type {Boolean}\r\n */\r\n filterableHiddenGroup: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 加载中\r\n *\r\n * @type {Boolean}\r\n */\r\n loading: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 加载中的文字提示\r\n *\r\n * @type {String}\r\n */\r\n loadingText: {\r\n type: String,\r\n default: '加载中',\r\n },\r\n /**\r\n * 搜索时,只按 label 进行搜索\r\n *\r\n * @type {Boolean}\r\n */\r\n filterByLabel: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 是否开启还原输入框内容(仅在单选时生效)\r\n *\r\n * @type {Boolean}\r\n */\r\n restoreInputOption: {\r\n type: Boolean,\r\n default: true,\r\n },\r\n /**\r\n * 是否将弹层放置于 body 内,在 Tabs、\r\n * 带有 fixed 的 Table 列内使用时,\r\n * 建议添加此属性,它将不受父级样式影响,\r\n * 从而达到更好的效果\r\n *\r\n * @type {Boolean}\r\n */\r\n transfer: {\r\n type: Boolean,\r\n default() {\r\n const global = getCurrentInstance().appContext.config.globalProperties;\r\n\r\n return !global.$IVUE || global.$IVUE.transfer === ''\r\n ? false\r\n : global.$IVUE.transfer;\r\n },\r\n },\r\n /**\r\n * 弹窗的展开方向\r\n *\r\n * @type {String}\r\n */\r\n placement: {\r\n type: String,\r\n validator(value: string) {\r\n return oneOf(value, [\r\n 'top',\r\n 'bottom',\r\n 'top-start',\r\n 'bottom-start',\r\n 'top-end',\r\n 'bottom-end',\r\n ]);\r\n },\r\n default: 'bottom-start',\r\n },\r\n /**\r\n * 开启 transfer 时,给浮层添加额外的 class 名称\r\n *\r\n * @type {String}\r\n */\r\n transferClassName: {\r\n type: String,\r\n },\r\n /**\r\n * 自动完成\r\n *\r\n * @type {Boolean}\r\n */\r\n autoComplete: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n /**\r\n * 外部输入框输入数据\r\n *\r\n * @type {String}\r\n */\r\n filterQueryProp: {\r\n type: String,\r\n default: '',\r\n },\r\n /**\r\n * 输入时是否触发表单的校验\r\n *\r\n * @type {Boolean}\r\n */\r\n validateEvent: {\r\n type: Boolean,\r\n default: true,\r\n },\r\n /**\r\n * id\r\n *\r\n * @type {String}\r\n */\r\n id: {\r\n type: String,\r\n },\r\n },\r\n setup(props: Props, { emit, slots }) {\r\n // vm\r\n const { proxy } = getCurrentInstance();\r\n\r\n // 事件发射器/发布订阅\r\n const selectEmitter = mitt();\r\n\r\n // dom\r\n const selectWrapper = ref<HTMLDivElement>(null);\r\n const reference = ref<HTMLDivElement>(null);\r\n const dropdown = ref<DropDownInstance>();\r\n const selectHead = ref<SelectHeadInstance>();\r\n\r\n // 输入框禁用\r\n const inputDisabled = useDisabled();\r\n\r\n // ivue-popover\r\n const popover = inject(PopoverContextKey, {\r\n default: null,\r\n });\r\n\r\n // 设置表单对应的输入框id\r\n const { formItem } = useFormItem();\r\n\r\n // 输入框id\r\n const { inputId } = useFormItemInputId(\r\n props,\r\n {\r\n formItemContext: formItem,\r\n },\r\n slots\r\n );\r\n\r\n // data\r\n const data = reactive<Data>({\r\n /**\r\n * 是否显示菜单\r\n *\r\n * @type {Boolean}\r\n */\r\n visibleMenu: false,\r\n /**\r\n * 判断是否有焦点\r\n *\r\n * @type {Boolean}\r\n */\r\n isFocused: false,\r\n /**\r\n * 最终渲染的数据\r\n *\r\n * @type {Array}\r\n */\r\n values: [],\r\n /**\r\n * 子选项 dom\r\n *\r\n * @type {Array}\r\n */\r\n options: [],\r\n /**\r\n * 焦点项\r\n *\r\n * @type {Number}\r\n */\r\n focusIndex: -1,\r\n /**\r\n * 是否开始输入框支持搜索\r\n *\r\n * @type {Boolean}\r\n */\r\n filterQueryChange: false,\r\n /**\r\n * 搜索输入框输入数据\r\n *\r\n * @type {String}\r\n */\r\n filterQuery: props.filterQueryProp || '',\r\n /**\r\n * 鼠标悬浮\r\n *\r\n * @type {Boolean}\r\n */\r\n hasMouseHover: false,\r\n /**\r\n * 上一次搜索输入\r\n *\r\n * @type {String}\r\n */\r\n lastSearchQuery: '',\r\n /**\r\n * 事件发射器/发布订阅\r\n *\r\n * @type {Function}\r\n */\r\n selectEmitter,\r\n /**\r\n * 当前有 value 值\r\n *\r\n * @type {Boolean}\r\n */\r\n hasExpectedValue: false,\r\n /**\r\n * 过滤输入内容\r\n *\r\n * @type {String}\r\n */\r\n _filterQuery: '',\r\n /**\r\n * 禁用菜单\r\n *\r\n * @type {Boolean}\r\n */\r\n disableMenu: false,\r\n });\r\n\r\n // computed\r\n\r\n // 外部样式\r\n const classes = computed(() => {\r\n return [\r\n `${prefixCls}`,\r\n {\r\n // 默认\r\n [`${prefixCls}-default`]: !props.multiple,\r\n // 是否支持多选\r\n [`${prefixCls}-multiple`]: props.multiple,\r\n // 是否禁用\r\n [`${prefixCls}-disabled`]: inputDisabled.value,\r\n // 是否显示菜单\r\n [`${prefixCls}-visible`]: data.visibleMenu,\r\n },\r\n ];\r\n });\r\n\r\n // 选择框样式\r\n const selectionClasses = computed(() => {\r\n return [\r\n `${prefixCls}-selection-default`,\r\n {\r\n [`${prefixCls}-selection`]: !props.autoComplete,\r\n [`${prefixCls}-selection-focused`]: data.isFocused,\r\n },\r\n ];\r\n });\r\n\r\n // 创建列表选项样式\r\n const createItemClasses = computed(() => {\r\n return [`${prefixCls}-item`, `${prefixCls}-item--create`];\r\n });\r\n\r\n // tab 键顺序\r\n const selectTabindex = computed(() => {\r\n // 是否禁用选择组件 | 是否支持搜索\r\n return inputDisabled.value || props.filterable ? -1 : 0;\r\n });\r\n\r\n // 当前选择的值\r\n const currentSelectValue = computed(() => {\r\n const _value = data.values;\r\n\r\n // 判断是否有开启返回label和value\r\n if (props.labelAndValue) {\r\n return props.multiple ? _value : _value[0];\r\n } else {\r\n return props.multiple\r\n ? _value.map((option: { value: any }) => option.value)\r\n : (_value[0] || {}).value;\r\n }\r\n });\r\n\r\n // 显示没有数据时的文本\r\n const showNotFindText = computed(() => {\r\n // 是否禁用选择组件 | 是否支持搜索\r\n return (\r\n selectOptions.value &&\r\n selectOptions.value.length === 0 &&\r\n (!isSearchMethod.value || (isSearchMethod.value && !props.loading))\r\n );\r\n });\r\n\r\n // 判断是搜索是否是一个方法\r\n const isSearchMethod = computed(() => {\r\n return typeof props.searchMethod === 'function';\r\n });\r\n\r\n // 获取菜单选择列表\r\n const selectOptions = computed(() => {\r\n // 获取选项的列表\r\n const selectOptions = [];\r\n\r\n // 选项计数器,计算有多少个选项\r\n let optionCounter = -1;\r\n\r\n // 当前焦点项\r\n const currentIndex = data.focusIndex;\r\n\r\n // 获取选择选项的value\r\n const selectedValues = data.values\r\n .filter(Boolean)\r\n .map(({ value }) => value);\r\n\r\n // 判断是否有自动输入\r\n if (props.autoComplete) {\r\n for (const option of data.options) {\r\n option.data.visible = true;\r\n\r\n //如果没有 filterQueryChange ,则忽略选项\r\n const optionFilter = props.filterable\r\n ? validateOption(option)\r\n : option;\r\n\r\n // 不符合条件的选项\r\n if (!optionFilter) {\r\n option.data.visible = false;\r\n } else {\r\n option.data.visible = true;\r\n }\r\n\r\n // autoComplete 过滤输入的内容\r\n if (data._filterQuery === '') {\r\n option.data.visible = true;\r\n }\r\n\r\n // 有显示选项 加入计数器\r\n if (option.data.visible) {\r\n // 选项计数器\r\n optionCounter = optionCounter + 1;\r\n\r\n selectOptions.push(\r\n handleOption(\r\n option,\r\n selectedValues,\r\n optionCounter === currentIndex\r\n )\r\n );\r\n }\r\n }\r\n\r\n return selectOptions;\r\n }\r\n\r\n // 选项的数据\r\n for (const option of data.options) {\r\n //如果没有 filterQueryChange ,则忽略选项\r\n if (data.filterQueryChange || data.filterQuery) {\r\n const optionFilter = props.filterable\r\n ? validateOption(option)\r\n : option;\r\n\r\n // 不符合条件的选项\r\n if (!optionFilter) {\r\n option.data.visible = false;\r\n } else {\r\n option.data.visible = true;\r\n }\r\n\r\n // 过滤数组\r\n if (!optionFilter) {\r\n continue;\r\n }\r\n } else {\r\n option.data.visible = true;\r\n }\r\n\r\n // 有显示选项 加入计数器\r\n if (option.data.visible) {\r\n // 选项计数器\r\n optionCounter = optionCounter + 1;\r\n\r\n selectOptions.push(\r\n handleOption(option, selectedValues, optionCounter === currentIndex)\r\n );\r\n }\r\n }\r\n\r\n // 这是一个组件数组 [components]\r\n return selectOptions;\r\n });\r\n\r\n // 显示下拉菜单\r\n const dropVisible = computed(() => {\r\n let status = true;\r\n\r\n // 没有选项\r\n const noSelectOptions =\r\n selectOptions.value && selectOptions.value.length === 0;\r\n\r\n // 没有加载中\r\n if (\r\n !props.loading &&\r\n data.filterQuery === '' &&\r\n noSelectOptions &&\r\n isSearchMethod.value\r\n ) {\r\n status = false;\r\n }\r\n\r\n // autoComplete\r\n if (props.autoComplete && noSelectOptions && !props.loading) {\r\n status = false;\r\n }\r\n\r\n return data.visibleMenu && status;\r\n });\r\n\r\n // 是否可以显示重置选择按钮\r\n const canClearable = computed(() => {\r\n // 多选模式下无效\r\n return (\r\n data.hasMouseHover &&\r\n props.clearable &&\r\n !props.multiple &&\r\n !inputDisabled.value\r\n );\r\n });\r\n\r\n // 当前选择的值\r\n const selectValue = computed(() => {\r\n // 判断是否有开启返回label和value\r\n if (props.labelAndValue) {\r\n return props.multiple ? data.values : data.values[0];\r\n } else {\r\n return props.multiple\r\n ? data.values.map((option) => option.value)\r\n : (data.values[0] || {}).value;\r\n }\r\n });\r\n\r\n // 显示创建的选项\r\n const showCreateItem = computed(() => {\r\n let state = false;\r\n\r\n // 开启了创建选项, 有搜索内容\r\n if (props.allowCreate && data.filterQuery !== '') {\r\n state = true;\r\n\r\n // 是否有选项列表\r\n if (data.options && data.options.length) {\r\n if (data.options.find((item) => item.getLabel === data.filterQuery))\r\n state = false;\r\n }\r\n }\r\n\r\n return state;\r\n });\r\n\r\n // 下拉框样式\r\n const dropdownClasses = computed(() => {\r\n return {\r\n [`${prefixCls}-dropdown--transfer`]: props.transfer,\r\n [`${prefixCls}-multiple`]: props.multiple && props.transfer,\r\n ['ivue-auto-complete']: props.autoComplete,\r\n ['ivue-auto-complete--notdata']:\r\n selectOptions.value.length === 0 &&\r\n props.autoComplete &&\r\n !props.loading,\r\n [props.transferClassName]: props.transferClassName,\r\n };\r\n });\r\n\r\n // methods\r\n\r\n // 点击外部\r\n const handleClickOutside = (event: Event) => {\r\n // 判断是否显示了菜单\r\n if (data.visibleMenu) {\r\n if (event.type === 'mousedown') {\r\n event.preventDefault();\r\n\r\n return;\r\n }\r\n\r\n // 是否将弹层放置于 body 内\r\n if (props.transfer) {\r\n const { $el } = dropdown.value;\r\n if ($el === event.target || $el.contains(event.target)) {\r\n return;\r\n }\r\n }\r\n\r\n // 取消焦点\r\n data.isFocused = false;\r\n\r\n // 不是自动完成组件\r\n if (!props.autoComplete) {\r\n event.stopPropagation();\r\n }\r\n\r\n event.preventDefault();\r\n\r\n // 点击后隐藏菜单\r\n hideMenu();\r\n }\r\n // 关闭\r\n else {\r\n // 取消焦点\r\n data.isFocused = false;\r\n }\r\n };\r\n\r\n // 头部输入框获取焦点\r\n const handleHeaderFocus = ({ type }) => {\r\n if (inputDisabled.value) {\r\n return;\r\n }\r\n\r\n data.isFocused = type === 'focus';\r\n };\r\n\r\n // 提取选项数据\r\n const handleOption = (\r\n option: OptionInstance,\r\n values: string[],\r\n isFocused: boolean\r\n ) => {\r\n // 如果选项节点没有子组件就直接返回选项节点\r\n // 是否获取到焦点\r\n option.data.isFocused = isFocused;\r\n\r\n return {\r\n option,\r\n };\r\n };\r\n\r\n // 点击菜单\r\n const handleToggleMenu = (event: Event, force?: boolean) => {\r\n // 在Popover嵌套中\r\n if (data.disableMenu && event) {\r\n return false;\r\n }\r\n\r\n // 选择组件是否禁用\r\n if (inputDisabled.value) {\r\n return false;\r\n }\r\n\r\n // 设置菜单状态\r\n data.visibleMenu =\r\n typeof force !== 'undefined' ? force : !data.visibleMenu;\r\n\r\n // 菜单收起\r\n if (!data.visibleMenu) {\r\n // 取消焦点\r\n if (props.filterable) {\r\n const input = proxy.$el.querySelector('input[type=\"text\"]');\r\n\r\n input.blur();\r\n }\r\n\r\n // 更新下拉框\r\n dropdown.value.update();\r\n }\r\n };\r\n\r\n // 隐藏菜单\r\n const hideMenu = () => {\r\n handleToggleMenu(null, false);\r\n };\r\n\r\n // 选项菜单点击\r\n const handleOptionClick = (option: OptionData, status?: string) => {\r\n // 在Popover嵌套中\r\n if (popover.data) {\r\n data.disableMenu = true;\r\n }\r\n\r\n // 判断是否开启了多选\r\n if (props.multiple) {\r\n const selected = data.values.find(\r\n ({ value }) => value === option.value\r\n );\r\n\r\n // 判断是搜索是否是一个方法\r\n if (isSearchMethod.value) {\r\n // 下一个搜索请求 || 搜索输入框输入数据\r\n data.lastSearchQuery = data.lastSearchQuery || data.filterQuery;\r\n } else {\r\n // 下一个搜索请求\r\n data.lastSearchQuery = '';\r\n }\r\n\r\n // 当前是否有对应的选项 && 不是创建新列表\r\n if (selected) {\r\n data.values = data.values.filter(\r\n ({ value }) => value !== option.value\r\n );\r\n }\r\n // 没有当前选项\r\n else {\r\n data.values = data.values.concat(option);\r\n }\r\n\r\n // 多选项没有\r\n if (data.values.length === 0) {\r\n // 重置焦点项\r\n data.focusIndex = -1;\r\n }\r\n\r\n // 鼠标点击选项元素后放回焦点\r\n data.isFocused = true;\r\n\r\n // 多选删除了\r\n if (status === 'delete-multiple') {\r\n setTimeout(() => {\r\n // 重置焦点项\r\n data.focusIndex = -1;\r\n });\r\n }\r\n }\r\n // 单选\r\n else {\r\n // 点击后隐藏菜单\r\n hideMenu();\r\n\r\n // 过滤输入框数据\r\n data.filterQuery = String(option.label).trim();\r\n\r\n // 最终渲染的数据\r\n data.values = [option];\r\n\r\n // 搜索请求\r\n data.lastSearchQuery = '';\r\n }\r\n\r\n // 是否支持搜索\r\n if (props.filterable) {\r\n const input = proxy.$el.querySelector('input[type=\"text\"]');\r\n\r\n // 不是自动输入\r\n if (!props.autoComplete) {\r\n // 输入框获取焦点\r\n nextTick(() => {\r\n input.focus();\r\n });\r\n }\r\n }\r\n\r\n // 选择项目时触发\r\n emit('on-select', option);\r\n\r\n // 更新下拉框\r\n dropdown.value.update();\r\n\r\n nextTick(() => {\r\n // 有展开菜单才设置焦点\r\n if (data.visibleMenu) {\r\n // 获取焦点项\r\n data.focusIndex = setFocusIndex(option);\r\n } else {\r\n data.focusIndex = -1;\r\n }\r\n });\r\n\r\n // 判断是否进行了过滤输入\r\n if (data.filterQueryChange) {\r\n // 等菜单动画执行完再清除\r\n setTimeout(() => {\r\n data.filterQueryChange = false;\r\n }, 300);\r\n }\r\n // 没有开启过滤\r\n else {\r\n // 等菜单动画执行完再清除\r\n nextTick(() => {\r\n data.filterQueryChange = false;\r\n });\r\n }\r\n };\r\n\r\n // 设置选项焦点位置\r\n const setFocusIndex = (option: OptionData) => {\r\n return selectOptions.value.findIndex((item) => {\r\n if (typeof option === 'object') {\r\n // 判断是否有key 使用key匹配选项\r\n return item.option.value === option.value;\r\n } else {\r\n return item.option.value === option;\r\n }\r\n });\r\n };\r\n\r\n // 清楚单项选择数据\r\n const clearSingleSelect = () => {\r\n // 非多选清除数据\r\n if (!props.multiple) {\r\n // 清除上一次搜索输入\r\n data.lastSearchQuery = '';\r\n\r\n emit('update:modelValue', '');\r\n }\r\n\r\n // API 点击清空按钮时触发\r\n emit('on-clear');\r\n\r\n // 隐藏菜单\r\n hideMenu();\r\n\r\n // 初始化数据\r\n if (props.clearable) {\r\n resetData();\r\n }\r\n };\r\n\r\n // 重置数据\r\n const resetData = () => {\r\n // 焦点项\r\n data.focusIndex = -1;\r\n // 最终渲染的数据\r\n data.values = [];\r\n // 鼠标悬浮\r\n data.hasMouseHover = false;\r\n\r\n // 等菜单动画消失后再清除\r\n // 搜索输入框输入数据\r\n data.filterQuery = '';\r\n // 是否开始输入框支持搜索\r\n data.filterQueryChange = false;\r\n };\r\n\r\n // 初始化值\r\n const getInitialValue = () => {\r\n let initialValue = Array.isArray(props.modelValue)\r\n ? props.modelValue\r\n : [props.modelValue];\r\n\r\n // 判断 值 === undefined 或者是 ''而且 参数不是无穷大\r\n if (\r\n typeof initialValue[0] === 'undefined' ||\r\n (String(initialValue[0]).trim() === '' &&\r\n !Number.isFinite(initialValue[0]))\r\n ) {\r\n initialValue = [];\r\n }\r\n\r\n // 开启搜索,没有多选\r\n if (isSearchMethod.value && !props.multiple && props.modelValue) {\r\n // 获取选项数据\r\n const optionData = getOptionData(props.modelValue);\r\n\r\n // 过滤输入框数据\r\n data.filterQuery = optionData\r\n ? optionData.label\r\n : String(props.modelValue);\r\n }\r\n\r\n return initialValue.filter((item) => {\r\n return Boolean(item) || item === 0;\r\n });\r\n };\r\n\r\n // 获取选项的值\r\n const getOptionData = (value: string | number) => {\r\n const option = data.options.find((option) => {\r\n return option.value === value || option.getLabel === value;\r\n });\r\n\r\n if (!option) {\r\n return null;\r\n }\r\n\r\n // 不是自动完成\r\n if (!props.autoComplete) {\r\n // 有展开菜单 -> 获取焦点项\r\n if (data.visibleMenu) {\r\n data.focusIndex = setFocusIndex(option as OptionData);\r\n }\r\n }\r\n\r\n return {\r\n value: option.value,\r\n label: option.getLabel,\r\n disabled: option.isDisabled,\r\n };\r\n };\r\n\r\n // 过滤输入框输入\r\n const handleFilterQueryChange = (filterQuery: string) => {\r\n // 是否显示下拉菜单\r\n if (filterQuery.length > 0 && filterQuery !== data.filterQuery) {\r\n if (props.autoComplete) {\r\n // 输入框是否获取焦点\r\n const isInputFocused =\r\n document.hasFocus &&\r\n document.hasFocus() &&\r\n document.activeElement === proxy.$el.querySelector('input');\r\n\r\n data.visibleMenu = isInputFocused;\r\n } else {\r\n data.visibleMenu = true;\r\n }\r\n }\r\n\r\n // 输入框需要过滤的数据\r\n data.filterQuery = filterQuery;\r\n\r\n // 输入框过滤输入开始\r\n data.filterQueryChange = true;\r\n };\r\n\r\n // 过滤选项组件\r\n const validateOption = (options: OptionInstance) => {\r\n // value\r\n const value = options.value;\r\n // label\r\n const label = options.label || options.value;\r\n // elm\r\n const elm = options.$el || '';\r\n\r\n // 文本内容 将数组元素计算为一个值(从左到右)\r\n const textContent = elm.textContent;\r\n\r\n // 把数据转换成字符串\r\n const stringValues = props.filterByLabel\r\n ? [label].toString()\r\n : [value, label, textContent].toString();\r\n\r\n // 把输入框数据转换成小写去除前后空格\r\n const filterQuery = data.filterQuery.toLowerCase().trim();\r\n\r\n // 是否开启搜索 && 没有多选 && 没有输入 && 没有上一个熟人\r\n if (\r\n props.filterable &&\r\n !props.multiple &&\r\n filterQuery === '' &&\r\n !data.lastSearchQuery\r\n ) {\r\n return false;\r\n }\r\n\r\n // 判断 stringValues 数组中是否包含 filterQuery 中的值 includes()\r\n return stringValues.toLowerCase().includes(filterQuery);\r\n };\r\n\r\n // 设置滚动条滚动\r\n const focusScroll = (index: number) => {\r\n if (index < 0 || props.autoComplete) {\r\n return;\r\n }\r\n\r\n if (data.options[index]) {\r\n // update scroll\r\n const options = data.options[index];\r\n\r\n // dropdown\r\n const _dropdown = dropdown.value.$el;\r\n // 底部距离\r\n const bottomOverflowDistance =\r\n options.$el.getBoundingClientRect().bottom -\r\n _dropdown.getBoundingClientRect().bottom;\r\n\r\n // 顶部距离\r\n const topOverflowDistance =\r\n options.$el.getBoundingClientRect().top -\r\n _dropdown.getBoundingClientRect().top;\r\n\r\n // 滚动到底部\r\n if (bottomOverflowDistance > 0) {\r\n _dropdown.scrollTop += bottomOverflowDistance;\r\n }\r\n\r\n // 滚动到顶部\r\n if (topOverflowDistance < 0) {\r\n _dropdown.scrollTop += topOverflowDistance;\r\n }\r\n }\r\n };\r\n\r\n // 选项销毁\r\n const handleOptionDestroy = (index: number) => {\r\n if (index > -1) {\r\n data.options.splice(index, 1);\r\n }\r\n };\r\n\r\n // 按键\r\n const handleKeyDown = (event) => {\r\n const key = event.key || event.code;\r\n const keyCode = event.keyCode || event.which;\r\n\r\n // Backspace\r\n if (key === 'Backspace' || keyCode === 8) {\r\n return; // so we don't call preventDefault\r\n }\r\n\r\n // 展开\r\n if (data.visibleMenu) {\r\n // Tab\r\n if (key === 'Tab') {\r\n hideMenu();\r\n }\r\n\r\n // Esc slide-up\r\n if (key === 'Escape') {\r\n hideMenu();\r\n }\r\n\r\n // next\r\n if (key === 'ArrowUp') {\r\n navigateOptions(-1);\r\n }\r\n\r\n // prev\r\n if (key === 'ArrowDown') {\r\n navigateOptions(1);\r\n }\r\n\r\n // enter\r\n if (key === 'Enter') {\r\n if (data.focusIndex === -1) {\r\n return hideMenu();\r\n }\r\n\r\n const optionItem = selectOptions.value[data.focusIndex];\r\n\r\n if (optionItem) {\r\n // 获取选项数据\r\n const option = getOptionData(optionItem.option.value);\r\n\r\n handleOptionClick(option);\r\n } else {\r\n hideMenu();\r\n }\r\n }\r\n } else {\r\n const keysCanOpenMenu = ['ArrowDown', 'ArrowUp'];\r\n\r\n // 如果焦点在当前选择框的话按 next or prev 可以打开选项菜单\r\n if (keysCanOpenMenu.includes(event.key)) {\r\n handleToggleMenu(null, true);\r\n }\r\n }\r\n };\r\n\r\n // 下一个选项\r\n const navigateOptions = (direction: number) => {\r\n const optionLength = selectOptions.value.length - 1;\r\n\r\n let index = data.focusIndex + direction;\r\n\r\n if (index < 0) {\r\n index = optionLength;\r\n }\r\n\r\n // 重置下标\r\n if (index > optionLength) {\r\n index = 0;\r\n }\r\n\r\n // 寻找下标选项\r\n if (direction > 0) {\r\n let activeOption = -1;\r\n for (let i = 0; i < selectOptions.value.length; i++) {\r\n // 判断选项是否可选\r\n const isActiveOption = !selectOptions.value[i].option.disabled;\r\n\r\n // 设置激活的选项\r\n if (isActiveOption) {\r\n activeOption = i;\r\n }\r\n\r\n if (activeOption >= index) {\r\n break;\r\n }\r\n }\r\n\r\n index = activeOption;\r\n } else {\r\n let activeOption = selectOptions.value.length;\r\n\r\n for (let i = optionLength; i >= 0; i--) {\r\n // 判断选项是否可选\r\n const isActiveOption = !selectOptions.value[i].option.disabled;\r\n\r\n // 设置激活的选项\r\n if (isActiveOption) {\r\n activeOption = i;\r\n }\r\n\r\n if (activeOption <= index) {\r\n break;\r\n }\r\n }\r\n\r\n index = activeOption;\r\n }\r\n\r\n // 设置焦点选项\r\n data.focusIndex = index;\r\n };\r\n\r\n // 检查值是否相等\r\n const checkValuesNotEqual = (\r\n value: OptionData,\r\n publicValue: string[],\r\n values: OptionData[]\r\n ) => {\r\n const strValue = JSON.stringify(value);\r\n const strPublic = JSON.stringify(publicValue);\r\n const strValues = JSON.stringify(\r\n values.map((item) => {\r\n return item.value;\r\n })\r\n );\r\n\r\n return (\r\n strValue !== strPublic ||\r\n strValue !== strValues ||\r\n strValues !== strPublic\r\n );\r\n };\r\n\r\n // 检查当前值更新状态\r\n const checkUpdateStatus = () => {\r\n if (getInitialValue().length > 0 && selectOptions.value.length === 0) {\r\n data.hasExpectedValue = true;\r\n }\r\n };\r\n\r\n // 创建新列表\r\n const handleCreateItem = (event: Event) => {\r\n if (\r\n props.allowCreate &&\r\n data.filterQuery !== '' &&\r\n showCreateItem.value\r\n ) {\r\n // 创建新列表点击确认阻止上层 key up Enter 事件触发\r\n event.stopPropagation();\r\n\r\n const filterQuery = data.filterQuery;\r\n\r\n emit('on-create', filterQuery);\r\n\r\n // 清除输入\r\n data.filterQuery = '';\r\n\r\n const option = {\r\n value: filterQuery,\r\n label: filterQuery,\r\n disabled: false,\r\n };\r\n\r\n // 多选\r\n if (props.multiple) {\r\n handleOptionClick(option, 'create-item');\r\n } else {\r\n // 单选时如果不在 nextTick 里执行,无法赋值\r\n nextTick(() => {\r\n handleOptionClick(option);\r\n });\r\n }\r\n }\r\n };\r\n\r\n // 设置过滤输入框输入\r\n const setQuery = (query: string) => {\r\n // 过滤输入框输入\r\n data._filterQuery = query;\r\n\r\n // 重置焦点项\r\n data.focusIndex = -1;\r\n\r\n if (query) {\r\n // 过滤输入框输入\r\n handleFilterQueryChange(query);\r\n\r\n return;\r\n }\r\n\r\n // 输入框未空\r\n if (query === '') {\r\n props.searchMethod(query);\r\n }\r\n\r\n // 没有输入值\r\n if (query === null) {\r\n // 过滤输入框输入\r\n handleFilterQueryChange('');\r\n\r\n // 最终渲染的数据\r\n data.values = [];\r\n\r\n // 清空搜索关键词后,重新搜索相同的关键词没有触发远程搜索\r\n data.lastSearchQuery = '';\r\n }\r\n };\r\n\r\n // 获取焦点\r\n const focus = () => {\r\n if (props.filterable) {\r\n (selectHead.value.$refs.input as HTMLElement).focus();\r\n\r\n // 展开下拉框\r\n handleToggleMenu(null, true);\r\n }\r\n };\r\n\r\n // 初始化输入\r\n data.options = [];\r\n\r\n // onMounted\r\n onMounted(() => {\r\n // 设置初始值\r\n if (!isSearchMethod.value && selectOptions.value.length > 0) {\r\n data.values = getInitialValue()\r\n .map((value) => {\r\n if (typeof value === 'undefined' && !value) {\r\n return null;\r\n }\r\n return getOptionData(value);\r\n })\r\n .filter(Boolean);\r\n }\r\n\r\n // 有搜索方法,设置默认标签\r\n if (isSearchMethod.value && props.defaultLabel) {\r\n // 单选\r\n if (!props.multiple) {\r\n data.filterQuery = props.defaultLabel;\r\n\r\n data.hasExpectedValue = true;\r\n }\r\n // 多选\r\n else if (\r\n props.multiple &&\r\n props.defaultLabel instanceof Array &&\r\n props.modelValue.length === props.defaultLabel.length\r\n ) {\r\n const values = props.modelValue.map((item, index) => {\r\n return {\r\n value: item,\r\n label: props.defaultLabel[index],\r\n };\r\n });\r\n\r\n emit('on-set-default-options', values);\r\n\r\n nextTick(() => {\r\n data.values = values;\r\n });\r\n }\r\n }\r\n });\r\n\r\n // provide\r\n provide(\r\n SelectContextKey,\r\n reactive({\r\n props,\r\n selectWrapper,\r\n reference,\r\n dropVisible,\r\n options: data.options,\r\n values: data.values,\r\n focusIndex: data.focusIndex,\r\n handleOptionClick,\r\n selectEmitter: data.selectEmitter,\r\n onOptionDestroy: handleOptionDestroy,\r\n })\r\n );\r\n\r\n // watch\r\n\r\n // 监听设置的值\r\n watch(\r\n () => props.modelValue,\r\n (value) => {\r\n // 检查当前值更新状态\r\n checkUpdateStatus();\r\n\r\n if (value === '') {\r\n data.values = [];\r\n\r\n data.filterQueryChange = true;\r\n }\r\n // 当前值是否相等\r\n else if (checkValuesNotEqual(value, selectValue.value, data.values)) {\r\n // 修改适配\r\n nextTick(() => {\r\n if (data.options.length > 0) {\r\n const values = getInitialValue()\r\n .map((value) => {\r\n if (typeof value === 'undefined' && !value) {\r\n return null;\r\n }\r\n\r\n return getOptionData(value);\r\n })\r\n .filter(Boolean);\r\n\r\n data.values = values;\r\n }\r\n });\r\n }\r\n\r\n // 在Popover嵌套中\r\n if (popover.data) {\r\n data.disableMenu = true;\r\n popover.data.disableCloseUnderTransfer = true;\r\n\r\n setTimeout(() => {\r\n popover.handleCancel();\r\n }, 300);\r\n\r\n setTimeout(() => {\r\n data.disableMenu = false;\r\n }, 1000);\r\n }\r\n\r\n // 输入时是否触发表单的校验\r\n if (props.validateEvent) {\r\n formItem?.validate?.('change').catch((err) => debugWarn(err));\r\n }\r\n }\r\n );\r\n\r\n // 监听最终渲染的数据的变化\r\n watch(\r\n () => data.values,\r\n (newValue, oldValue) => {\r\n const _newValue = JSON.stringify(newValue);\r\n const _oldValue = JSON.stringify(oldValue);\r\n\r\n // 设置v-model的值\r\n const vModelValue =\r\n props.labelAndValue && selectValue.value\r\n ? props.multiple\r\n ? selectValue.value.map(({ value }) => value)\r\n : selectValue.value.value\r\n : selectValue.value;\r\n\r\n // 输入框值\r\n const emitInput =\r\n _newValue !== _oldValue && vModelValue !== props.modelValue;\r\n\r\n if (emitInput) {\r\n // 更新 v-model\r\n emit('update:modelValue', vModelValue);\r\n\r\n emit('on-change', selectValue.value);\r\n }\r\n }\r\n );\r\n\r\n // 监听菜单显示隐藏\r\n watch(\r\n () => data.visibleMenu,\r\n (state) => {\r\n // API 下拉框展开或收起时触发\r\n emit('on-menu-open', state);\r\n\r\n // 显示时\r\n if (state) {\r\n if (props.filterable) {\r\n // 触发选项组显示\r\n data.selectEmitter.emit('ivueOptionGroupQueryChange');\r\n }\r\n }\r\n\r\n // 恢复单选查询值\r\n const [option] = data.values;\r\n\r\n // 清除输入\r\n if (\r\n option &&\r\n !data.isFocused &&\r\n props.filterable &&\r\n props.multiple &&\r\n props.multipleFilterableClear\r\n ) {\r\n data.filterQuery = '';\r\n\r\n // 等菜单动画执行完再清除\r\n nextTick(() => {\r\n data.filterQueryChange = false;\r\n });\r\n }\r\n\r\n // 在Popover嵌套中\r\n if (popover.data) {\r\n popover.data.closeDelay = state ? 300 : 0;\r\n }\r\n }\r\n );\r\n\r\n // 焦点项\r\n watch(\r\n () => data.focusIndex,\r\n (index) => {\r\n focusScroll(index);\r\n }\r\n );\r\n\r\n // 监听过滤输入框输入数据\r\n watch(\r\n () => data.filterQuery,\r\n (filterQuery) => {\r\n // 没有输入内容取消焦点\r\n if (filterQuery === '') {\r\n data.focusIndex = -1;\r\n }\r\n\r\n // API 搜索词改变时触发\r\n emit('on-filter-query-change', filterQuery);\r\n\r\n // 是否是有效查询\r\n const hasValidQuery =\r\n filterQuery !== '' &&\r\n (filterQuery !== data.lastSearchQuery || !data.lastSearchQuery);\r\n\r\n // 是否有搜索方法\r\n const shouldCallRemoteMethod =\r\n // 是否是有效查询\r\n props.searchMethod && hasValidQuery;\r\n\r\n // 是否有搜索方法\r\n if (shouldCallRemoteMethod) {\r\n data.focusIndex = -1;\r\n\r\n props.searchMethod(filterQuery);\r\n }\r\n\r\n // 开启了远程搜索 && 有上一次输入 && 当前输入为空\r\n if (data.lastSearchQuery && filterQuery === '' && props.searchMethod) {\r\n data.focusIndex = -1;\r\n\r\n props.searchMethod(filterQuery, 'reset');\r\n }\r\n\r\n // 正常搜索\r\n if (filterQuery !== '' && isSearchMethod.value) {\r\n data.lastSearchQuery = filterQuery;\r\n }\r\n\r\n // 输入为空隐藏下拉框 非多选\r\n if (filterQuery === '' && !props.multiple && !props.searchMethod) {\r\n // 没有获取焦点隐藏下拉框\r\n if (!data.isFocused) {\r\n data.visibleMenu = false;\r\n }\r\n\r\n // 等菜单动画执行完再清除\r\n nextTick(() => {\r\n data.filterQueryChange = false;\r\n });\r\n }\r\n\r\n // 触发选项组显示;\r\n data.selectEmitter.emit('ivueOptionGroupQueryChange');\r\n }\r\n );\r\n\r\n // 监听焦点\r\n watch(\r\n () => data.isFocused,\r\n (focused) => {\r\n // 输入框获取焦点,移除焦点\r\n const el = props.filterable\r\n ? proxy.$el.querySelector('input[type=\"text\"]')\r\n : proxy.$el;\r\n\r\n el[focused ? 'focus' : 'blur']();\r\n\r\n // 恢复单选查询值\r\n const [option] = data.values;\r\n\r\n // 判断是否有点击删除按钮在进行还原输入框内容\r\n if (\r\n option &&\r\n props.filterable &&\r\n !props.multiple &&\r\n !focused &&\r\n props.restoreInputOption\r\n ) {\r\n const label = String(option.label || option.value).trim();\r\n\r\n if (label && data.filterQuery !== label) {\r\n data.filterQuery = label;\r\n\r\n nextTick(() => {\r\n data.filterQueryChange = false;\r\n\r\n // 获取焦点项\r\n data.focusIndex = setFocusIndex(option);\r\n });\r\n }\r\n }\r\n\r\n // 没有开启还原输入框内容\r\n if (\r\n option &&\r\n props.filterable &&\r\n !props.multiple &&\r\n !focused &&\r\n !props.restoreInputOption\r\n ) {\r\n // 更新 v-model\r\n emit('update:modelValue', data.filterQuery);\r\n }\r\n }\r\n );\r\n\r\n // 监听选项变化\r\n watch(\r\n () => selectOptions.value,\r\n (value) => {\r\n // 初始化值\r\n if (data.hasExpectedValue && value.length > 0) {\r\n data.values = getInitialValue()\r\n .map((value) => {\r\n if (typeof value === 'undefined' && !value) {\r\n return null;\r\n }\r\n\r\n return getOptionData(value);\r\n })\r\n .filter(Boolean);\r\n\r\n data.hasExpectedValue = false;\r\n }\r\n\r\n // 更新下拉框\r\n dropdown.value && dropdown.value.update();\r\n }\r\n );\r\n\r\n // 监听下拉框\r\n watch(\r\n () => dropVisible.value,\r\n (value) => {\r\n if (value) {\r\n // 更新下拉框\r\n dropdown.value.update();\r\n } else {\r\n // 销毁下拉框\r\n dropdown.value.destroy();\r\n }\r\n }\r\n );\r\n\r\n // 在Popover嵌套中\r\n if (popover.data) {\r\n watch(\r\n () => popover.visible,\r\n (value) => {\r\n if (value) {\r\n data.disableMenu = false;\r\n }\r\n }\r\n );\r\n }\r\n\r\n return {\r\n prefixCls,\r\n\r\n // dom\r\n selectWrapper,\r\n reference,\r\n dropdown,\r\n selectHead,\r\n\r\n // inject\r\n popover,\r\n\r\n // data\r\n data,\r\n inputId,\r\n inputDisabled,\r\n\r\n // computed\r\n classes,\r\n selectionClasses,\r\n createItemClasses,\r\n selectTabindex,\r\n currentSelectValue,\r\n isSearchMethod,\r\n showNotFindText,\r\n dropVisible,\r\n canClearable,\r\n showCreateItem,\r\n dropdownClasses,\r\n\r\n // methods\r\n handleClickOutside,\r\n handleHeaderFocus,\r\n handleOption,\r\n handleToggleMenu,\r\n handleFilterQueryChange,\r\n handleKeyDown,\r\n handleCreateItem,\r\n setFocusIndex,\r\n setQuery,\r\n clearSingleSelect,\r\n focus,\r\n resetData,\r\n };\r\n },\r\n components: {\r\n SelectHead,\r\n DropDown,\r\n IvueOption,\r\n IvueIcon,\r\n },\r\n});\r\n</script>\r\n"],"names":["_withDirectives","_openBlock","_createElementBlock","_normalizeClass","_withKeys","_withModifiers","_createElementVNode","_createCommentVNode","_createVNode","_createSlots","_withCtx","_Transition","_createBlock","_toDisplayString","_vShow"],"mappings":"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