el-select下拉框,搜索时,若是匹配后的数据有且只有一条,则当失去焦点时,默认选中该条数据
1、使用指令
当所需功能只能通过直接的 DOM 操作来实现时,才应该使用自定义指令。可使用方法2封装成共用函数,但用指令他人复用时比较便捷。
<el-table
v-loading="tableLoading"
border
:data="tableList"
default-expand-all
row-key="id"
:tree-props="{
children: 'children',
}"
>
<el-table-column
align="center"
label="下拉框"
prop="selectProp"
>
<template #default="{ row }">
<el-select
v-model="row.selectProp"
v-auto-select
clearable
filterable
placeholder="请选择下拉框数据"
>
<el-option
v-for="item in select_data_source"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
</el-table>
<script setup>
import vAutoSelect from '@/directives/auto_select_directive'
</script>
//auto_select_directive.ts文件
import type { Directive } from 'vue'
interface ExtendedElement extends HTMLElement {
_blurHandler?: (e: Event) => void
_focusHandler?: (e: Event) => void
_observer?: MutationObserver
_currentDropdown?: HTMLElement | null
}
const vAutoSelect: Directive = {
mounted(el: ExtendedElement) {
const selectInput = el.querySelector('.el-input__inner')
if (!selectInput) return
// 精准定位当前下拉框
const getCurrentDropdown = () => {
const activePopper = document.querySelector(
'.el-popper.el-select__popper[aria-hidden="false"]'
) as HTMLElement | null
const selectWrapper = selectInput.closest('.el-select')
if (activePopper && selectWrapper) {
// 可以根据实际情况添加更多的判断条件,确保是当前 select 对应的下拉框
return activePopper
}
return null
}
// 核心选择逻辑
const autoSelectHandler = () => {
const currentDropdown = getCurrentDropdown()
if (!currentDropdown) {
return
}
const visibleItems = currentDropdown.querySelectorAll<HTMLLIElement>(
'.el-select-dropdown__item:not([style*="display: none"])'
)
if (visibleItems.length === 1) {
visibleItems[0].click()
const component = (el as any).__vueParentComponent?.ctx
component?.handleClose?.()
}
}
// 事件处理器
const handleFocus = () => {
el._currentDropdown = getCurrentDropdown()
}
const handleBlur = () => {
autoSelectHandler()
}
// 智能监听器
const initObserver = () => {
//获取当前激活的下拉框元素
const dropdown = getCurrentDropdown()
if (dropdown) {
// MutationObserver 是一个用于监听 DOM 变化的 API,它接受一个回调函数作为参数。它允许你异步地监听 DOM 的变动,如节点的添加、删除、属性的修改等,并在变动发生时执行相应的回调函数
el._observer = new MutationObserver((mutations) => {
// mutations:一个包含所有变化记录(MutationRecord 对象)的数组。
mutations.forEach((mutation) => {
if (
mutation.type === 'attributes' &&
mutation.attributeName === 'style'
) {
autoSelectHandler()
}
})
})
el._observer.observe(dropdown, {
attributes: true, // 监听属性变化--布尔值,指示是否观察目标节点的属性变化
attributeFilter: ['style'], // 只监听 'style' 属性的变化--字符串数组,指定要观察的属性名称
})
}
}
// 事件绑定
selectInput.addEventListener('focus', handleFocus)
selectInput.addEventListener('blur', handleBlur)
el._focusHandler = handleFocus
el._blurHandler = handleBlur
// 初始化监听
initObserver()
},
beforeUnmount(el: ExtendedElement) {
// 清理事件
const selectInput = el.querySelector('.el-input__inner')
if (selectInput) {
if (el._focusHandler) {
selectInput.removeEventListener('focus', el._focusHandler)
}
if (el._blurHandler) {
selectInput.removeEventListener('blur', el._blurHandler)
}
}
// 清理 Observer
el._observer?.disconnect()//调用 disconnect 方法可以停止观察,释放资源
delete el._currentDropdown
},
}
export default vAutoSelect
2、封装成共用函数
在输入文本进行搜索时,获取不到el-select内部匹配后的数据,故在失去焦点时,模拟搜索,获取筛选后的结果,当有且只有一条数据时,将那条数据赋给v-model绑定的值.
<el-table
v-loading="tableLoading"
border
:data="tableList"
default-expand-all
row-key="id"
:tree-props="{
children: 'children',
}"
>
<el-table-column
align="center"
label="下拉框"
prop="selectProp"
>
<template #default="{ row }">
<el-select
v-model="row.selectProp"
clearable
filterable
placeholder="请选择下拉框数据"
@blur="
(e) => {
selectSearchAuto(
e,
(newValue) => (row.selectProp= newValue),
select_data_source
)
}
"
>
<el-option
v-for="item in select_data_source"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
</el-table>
<script setup>
import { selectSearchAuto } from '@/utils'
</script>
//utils/index.ts文件
export function selectSearchAuto(
event: { target: { value: string | undefined } },
setValueCallback: (arg0: any) => void,
dataList: any[]
) {
const query = event.target.value?.trim()
if (!query) return
const filteredOptions = dataList.filter((item) => item.label.includes(query))
if (filteredOptions.length === 1) {
setValueCallback(filteredOptions[0].value)
}
}
使用成本相对较高