公共组件、公共样式集合
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
5.8 KiB

3 weeks ago
<template>
<el-autocomplete class="school-auto-complete" :value="value" :fetch-suggestions="querySearchAsync"
:placeholder="placeholder" :debounce="debounce" :loading="loading" @select="handleSelect" @input="handleInput"
@clear="handleManualClear" :clearable="clearable" :size="size" :disabled="disabled">
<!-- 自定义下拉选项 -->
<template #default="{ item }">
<div class="flex-between school-option">
<span>{{ item.name }}</span>
<img v-if="showSelectedIcon && item.id === selectedSchoolId" src="../../assets/drop-selected.svg"
alt="selected" />
</div>
</template>
</el-autocomplete>
</template>
<script>
import './index.scss'
export default {
name: 'SearchInput',
props: {
url: {
type: String,
default: ''
},
// 双向绑定的值(学校名称)
value: {
type: String,
default: ''
},
// 选中的学校ID
selectedSchoolId: {
type: [String, Number],
default: null
},
// 占位符
placeholder: {
type: String,
default: '请输入名称'
},
// 防抖延迟时间
debounce: {
type: Number,
default: 300
},
// 是否显示清空按钮
clearable: {
type: Boolean,
default: true
},
// 输入框尺寸
size: {
type: String,
default: 'medium'
},
// 是否禁用
disabled: {
type: Boolean,
default: false
},
// 是否显示选中图标
showSelectedIcon: {
type: Boolean,
default: true
}
},
data() {
return {
loading: false,
timeout: null,
lastSearchKeyword: '',
schoolList: [],
// 新增:记录最后一次有效选择
lastValidSelection: null
}
},
mounted() {
// 初始化时记录当前选择
if (this.selectedSchoolId && this.value) {
this.lastValidSelection = {
id: this.selectedSchoolId,
name: this.value
}
}
},
beforeDestroy() {
clearTimeout(this.timeout)
},
methods: {
/**
* 异步搜索学校
*/
async querySearchAsync(queryString, callback) {
const keyword = queryString.trim()
if (!keyword || keyword === this.lastSearchKeyword) {
callback([])
return
}
this.lastSearchKeyword = keyword
this.loading = true
clearTimeout(this.timeout)
this.timeout = setTimeout(async () => {
try {
const schools = await this.searchSchools(keyword)
this.schoolList = schools
callback(schools)
} catch (error) {
console.error('搜索失败:', error)
callback([])
} finally {
this.loading = false
}
}, this.debounce)
},
/**
* 搜索学校API调用
*/
async searchSchools(keyword) {
try {
const response = await this.$http('POST', this.url, {
keyword
})
if (response.status && response.data) {
return response.data
}
return []
} catch (error) {
this.$message.error('搜索列表失败')
throw error
}
},
/**
* 处理选择事件
*/
handleSelect(item) {
console.log('选中学校:', item)
// 更新记录
this.lastValidSelection = {
id: item.id,
name: item.name
}
this.$emit('select', item)
this.$emit('update:selectedSchoolId', item.id)
this.$emit('input', item.name)
this.$emit('change', item)
},
/**
* 处理输入事件 - 安全版本
*/
handleInput(value) {
console.log('输入变化:', value, '当前选中:', this.selectedSchoolId)
// 只更新显示的值
this.$emit('input', value)
// 重要:这里不自动触发清空
// 清空只会在 handleManualClear 中触发
},
handleManualClear() {
console.log('用户手动清空')
this.clearSelection()
},
clearSelection() {
console.log('执行清空操作')
this.$emit('input', '')
this.$emit('update:selectedSchoolId', null)
this.$emit('clear')
this.lastSearchKeyword = ''
this.lastValidSelection = null
},
setSelectedSchool(school) {
if (school && school.id && school.name) {
this.lastValidSelection = {
id: school.id,
name: school.name
}
this.$emit('input', school.name)
this.$emit('update:selectedSchoolId', school.id)
this.$emit('select', school)
}
},
clear() {
this.clearSelection()
},
async triggerSearch(keyword) {
return await this.searchSchools(keyword || this.value)
},
/**
* 恢复上一次的有效选择
*/
restoreLastSelection() {
if (this.lastValidSelection) {
this.setSelectedSchool(this.lastValidSelection)
}
}
}
}
</script>