組件
<template><el-autocompletev-model="selectedValue":fetch-suggestions="fetchSuggestions":placeholder="placeholder"@select="handleSelect"clearablev-bind="$attrs"/>
</template><script lang="ts" setup>
import { ref, computed, onMounted, withDefaults } from 'vue'
import type { AutocompleteProps, AutocompleteEmits } from 'element-plus'interface AutocompleteItem {value: string[key: string]: any
}interface Props {placeholder?: stringdebounce?: numberapiFn?: (query: string) => Promise<AutocompleteItem[]> // 模擬接口函數localData?: AutocompleteItem[] // 本地數據filterFn?: (query: string, item: AutocompleteItem) => boolean // 自定義過濾函數
}const props = withDefaults(defineProps<Props>(), {placeholder: '請輸入內容',debounce: 300,localData: () => [],filterFn: undefined
})const emit = defineEmits<{(e: 'update:modelValue', value: string): void(e: 'select', item: AutocompleteItem): void
}>()const selectedValue = ref('')
const loading = ref(false)
const suggestions = ref<AutocompleteItem[]>([])
let timeout: number | undefined// 默認過濾函數 - 開頭匹配(不區分大小寫)
const defaultFilter = (query: string) => {const lowerQuery = query.toLowerCase()return (item: AutocompleteItem) => item.value.toLowerCase().startsWith(lowerQuery)
}// 模擬接口獲取數據
const mockApiFetch = async (query: string): Promise<AutocompleteItem[]> => {// 這里模擬API請求延遲await new Promise(resolve => setTimeout(resolve, 500))// 模擬數據const mockData = [{ value: 'Vue', link: 'https://vuejs.org' },{ value: 'React', link: 'https://reactjs.org' },{ value: 'Angular', link: 'https://angular.io' },{ value: 'Svelte', link: 'https://svelte.dev' },{ value: 'TypeScript', link: 'https://www.typescriptlang.org' },]// 模擬接口過濾return mockData.filter(item => item.value.toLowerCase().includes(query.toLowerCase()))
}// 獲取建議列表
const fetchSuggestions = async (query: string, cb: (arg: AutocompleteItem[]) => void
) => {clearTimeout(timeout)if (!query) {cb(props.localData)return}loading.value = truetimeout = setTimeout(async () => {try {let results: AutocompleteItem[] = []if (props.apiFn) {// 使用傳入的接口函數獲取數據results = await props.apiFn(query)} else if (props.localData.length) {// 使用本地數據過濾const filter = props.filterFn ? (item: AutocompleteItem) => props.filterFn!(query, item): defaultFilter(query)results = props.localData.filter(filter)} else {// 使用默認模擬接口results = await mockApiFetch(query)}suggestions.value = resultscb(results)} catch (error) {console.error('獲取建議列表失敗:', error)cb([])} finally {loading.value = false}}, props.debounce) as unknown as number
}// 選擇事件
const handleSelect = (item: AutocompleteItem) => {emit('update:modelValue', item.value)emit('select', item)
}// 暴露方法
defineExpose({focus: () => {// 這里可以訪問el-autocomplete的方法// 需要在實際使用時通過ref獲取組件實例}
})
</script><style scoped>
.el-autocomplete {width: 100%;
}
</style>
基本用法(使用內置模擬接口)
<template><CustomAutocomplete v-model="selectedValue" />
</template><script setup lang="ts">
import { ref } from 'vue'
import CustomAutocomplete from './CustomAutocomplete.vue'const selectedValue = ref('')
</script>
使用自定義接口函數
<template><CustomAutocomplete v-model="selectedValue":api-fn="fetchDataFromApi"/>
</template><script setup lang="ts">
import { ref } from 'vue'
import CustomAutocomplete from './CustomAutocomplete.vue'const selectedValue = ref('')const fetchDataFromApi = async (query: string) => {// 這里可以替換為真實的API調用console.log('查詢:', query)return [{ value: `${query}-結果1`, id: 1 },{ value: `${query}-結果2`, id: 2 }]
}
</script>
自定義過濾邏輯
<template><CustomAutocomplete v-model="selectedValue":local-data="techList":filter-fn="customFilter"/>
</template><script setup lang="ts">
import { ref } from 'vue'
import CustomAutocomplete from './CustomAutocomplete.vue'const selectedValue = ref('')
const techList = ref([{ value: 'Vue.js', category: 'frontend' },{ value: 'React', category: 'frontend' },{ value: 'Express', category: 'backend' }
])const customFilter = (query: string, item: any) => {// 同時匹配value和categoryreturn item.value.toLowerCase().includes(query.toLowerCase()) || item.category.toLowerCase().includes(query.toLowerCase())
}
</script>