3 changed files with 667 additions and 353 deletions
@ -0,0 +1,247 @@ |
|||
<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/site/dropdown_chose_ic.svg" |
|||
alt="selected" |
|||
/> |
|||
</div> |
|||
</template> |
|||
</el-autocomplete> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'SchoolAutoComplete', |
|||
|
|||
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> |
|||
<style lang="scss" scoped> |
|||
.school-auto-complete { |
|||
width: 100%; |
|||
} |
|||
|
|||
.school-option { |
|||
width: 100%; |
|||
span { |
|||
flex: 1; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
img { |
|||
width: 16px; |
|||
height: 16px; |
|||
margin-left: 8px; |
|||
} |
|||
} |
|||
</style> |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue