一、组件功能
- 支持分页加载下拉选项。分页加载用的是自定义指令v-loadmore
- 当输入和删除关键词时,按当前最新的关键词进行查询,并且页码回到第一页,滚动条置顶。这里要注意
el-select
的remote-method
没有处理clear
的场景,所以我们需要自己添加@clear="remoteMethod('')"
- 编辑时支持回显。需要借助sync修饰符
- 校验:原本使用change触发,但是在输入字符或者是复制粘贴进来的内容,期望的场景是清除校验
问题场景:
实际我们期望的应该是这样:
思路:使用blur事件,但是在改变关键词时手动触发校验
二、组件封装
RemoteSelect.vue
js
<template>
<el-select
v-model.trim="keyword"
v-loadmore:el-select-dropdown__list="loadMore"
class="remote-select"
:popper-append-to-body="false"
filterable
remote
reserve-keyword
clearable
placeholder="请输入关键字进行查询"
:remote-method="remoteMethod"
:loading="loading"
@change="handleChange"
@clear="remoteMethod('')"
>
<el-option
v-for="item in categoryDs"
:key="item.id"
:label="item.sageCategoryName"
:value="item.sageCategoryId"
/>
</el-select>
</template>
<script>
/*
主体逻辑:
1、第一次点击输入框,加载全部下拉,滚动到底部加载下一页
2、输入框输入关键字时,按关键字查询,滚动加载下一页
3、关键字进行补充和减少时,按当前最新的关键字进行查询,并且页码回到第一页,滚动加载下一页
4、点击输入框右侧的清空按钮时,需要手动触发remoteMethod,此时的name是''
5、编辑时的回显,需要父组件将name传过来,使用name作为关键字进行查询
*/
import { productCategoryDsApi } from '@/api/product/catesManager'
export default {
props: {
isEdit: { type: Boolean, default: false }, // 是否是编辑,编辑时需要回显
value: { type: String, default: '' },
sageCategoryName: { type: String, default: '' }, // 编辑时,必传此参数
},
data() {
return {
keyword: '', // 关键字模糊查询
categoryDs: [], // 下拉选项
loading: false,
pageLe: 20,
pageNo: 1,
total: 0,
flag: true,
}
},
watch: {
// 由于是分页加载,当编辑时,初次渲染需要将name传递过来,带上关键字查询列表。注意:只有编辑的初次渲染才需要设置categoryName
value: {
handler(val) {
if (this.flag && this.isEdit) {
this.keyword = this.sageCategoryName
this.getOptions()
this.flag = false
}
},
},
},
created() {
if (!this.isEdit) {
this.getOptions()
}
},
methods: {
// 选中某一项
handleChange(val) {
const item = this.categoryDs.find((n) => n.sageCategoryId === val) || {}
this.$emit('input', item.sageCategoryId)
this.$emit('update:sageCategoryName', item.sageCategoryName)
this.$emit('change', item)
},
// 加载更多
loadMore() {
if (this.total > this.categoryDs.length) {
this.pageNo++
this.getOptions()
}
},
// 当输入框内容发生变化时会触发remoteMethod,但是清空需要手动触发
remoteMethod(name) {
this.loading = true
this.keyword = name && name.trim()
this.reset() // 当用户输入不同的字符时,意味着需要重新加载下拉,需要将当前数据重置
this.getOptions()
if (!this.keyword) {
this.loading = false
}
},
// 重置当前页码,总数,下拉,并且将滚动条置顶
reset() {
this.pageNo = 1
this.total = 0
this.categoryDs = []
const targetDOM = document.querySelector(
'.remote-select .el-select-dropdown__list'
)
targetDOM.scrollTop = 0
this.$emit('clearValidate') // 清除校验
},
async getOptions() {
try {
const { keyword, pageLe, pageNo } = this
const params = { categoryName: keyword, pageLe, pageNo }
const { code, data } = await productCategoryDsApi(params)
this.loading = false
if (code === '000000' && Array.isArray(data.data)) {
const list = data.data.map((n) => ({
...n,
sageCategoryId: n.sageCategoryId + '',
sageCategoryName: n.categoryName,
}))
if (pageNo === 1) {
this.categoryDs = list
} else {
this.categoryDs.push(...list)
}
this.total = data.total
}
} catch (err) {
console.log(err)
this.loading = false
}
},
},
}
</script>
<style lang="scss" scoped>
.remote-select {
width: 100%;
/deep/ .el-select-dropdown__list {
height: 300px;
overflow-y: auto;
overflow-x: hidden;
li {
max-width: 470px;
}
}
}
</style>
三、使用
js
<el-form-item label="SAGE 平台类目" prop="sageCategoryId">
<RemoteSelect
:isEdit="type === 'edit'"
v-model="form.sageCategoryId"
:sageCategoryName.sync="form.sageCategoryName"
@clearValidate="clearValidate"
/>
</el-form-item>
isEdit
表示当前是否是编辑sageCategoryName.sync
用于回显时,带参查询列表v-model
用于绑定所选中的id值clearValidate
事件用于在输入或删除关键词时手动清除平台类目的校验
js
rules: {
sageCategoryId: [
{ required: true, message: '请选择平台类目', trigger: 'blur' }, // 这里不要用change事件
],
}
js
clearValidate() {
this.$refs.form.clearValidate('sageCategoryId')
}
因为使用了blur,这里我需要使用watch,实时地添加校验或去掉校验
js
watch: {
'form.sageCategoryId': {
handler(val) {
if (val) {
this.$refs.form.clearValidate('sageCategoryId')
} else {
this.$refs.form.validateField('sageCategoryId')
}
},
},
},
因为这个校验,在父组件中多写了一个clearValidate方法和watch监听,用起来很麻烦,能不能把这个逻辑封装到组件中,大神看到了解答一下,多谢!