select-pagination-element-plus
一个基于 Vue 3 + Element Plus 的 <el-select> 封装组件:支持远程搜索 、分页加载(无限加载/触底加载) 、可选缓存 与可选回显(detailApi),适合"远程大数据下拉选择"的场景。
1. 安装
bash
npm i select-pagination-element-plus
2. 快速开始(最小可用)
下面示例展示最基本用法:输入关键词 → 远程查询 → 下拉触底继续加载。
vue
<template>
<SelectPagination v-model="value" :api="fetchUsers" placeholder="输入关键词搜索" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import SelectPagination from 'select-pagination-element-plus'
const value = ref('')
const fetchUsers = async (params: any) => {
// params 内默认会包含:page、size、name(可配置)
// 你也可以通过组件 props.params 传额外查询参数
// 约定返回结构:默认 responsePath = "data.data"
// responsePath 指向的对象必须包含:
// - content: 选项数组
// - totalElements: 总数量
return {
data: {
data: {
content: [
{ id: 'u-1', label: '用户 1', value: 'u-1' },
{ id: 'u-2', label: '用户 2', value: 'u-2' }
],
totalElements: 2
}
}
}
}
</script>
3. 后端返回结构(responsePath)
组件会通过 responsePath 在响应对象中定位分页数据,默认:
responsePath = "data.data"
该路径最终需要指向一个对象,且至少包含:
ts
{
content: any[]
totalElements: number
}
3.1 示例:后端返回是 { data: { list, total } }
你可以这么配置:
vue
<SelectPagination
v-model="value"
:api="fetchUsers"
response-path="data"
/>
并确保 fetchUsers 返回里 data 对象包含 content/totalElements,或者你在 fetchUsers 内转换成统一结构。
4. 参数说明(Props 字段含义)
4.1 数据与行为
| 参数 | 类型 | 默认值 | 含义(建议阅读) |
|---|---|---|---|
| modelValue | `string | number | any[]` |
| isRemote | boolean |
true |
是否启用 Element Plus 的 remote 模式;为 true 时会走远程搜索逻辑 |
| api | `(params) => Promise<any> | any或() => (params) => Promise<any> |
any` |
| apiAsync | boolean |
false |
为 true 时,api 被视为"工厂函数",会 await api() 得到真正的请求函数 |
| params | `object | () => object` | {} |
| initOptions | any[] |
[] |
初始选项(常用于预置、默认项、或者本地固定项),会合并进 options |
4.2 字段映射(label/value)
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| labelField | string |
'label' |
option 显示文本字段名 |
| valueField | string |
'value' |
option 实际值字段名 |
例如你的后端返回 [{ userId, userName }],可以:
vue
<SelectPagination
v-model="value"
:api="fetchUsers"
label-field="userName"
value-field="userId"
/>
4.3 分页与搜索参数(发给后端的字段名)
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| pageSize | number |
10 |
每页数量 |
| pageKey | string |
'page' |
页码字段名(发给后端的 key) |
| sizeKey | string |
'size' |
每页大小字段名 |
| keyword | string |
'name' |
搜索关键词字段名(也就是 query 的 key) |
举例:默认请求参数类似:
ts
{
page: 1,
size: 10,
name: "张"
}
如果你的后端是 current/limit/keyword:
vue
<SelectPagination
v-model="value"
:api="fetchUsers"
page-key="current"
size-key="limit"
keyword="keyword"
/>
4.4 响应路径
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| responsePath | string |
'data.data' |
用点号路径从响应中取分页对象(见第 3 节) |
4.5 缓存(提高体验,减少重复请求)
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| enableCache | boolean |
true |
是否缓存:已加载 options、页码、关键词等 |
| cacheKey | string |
'' |
缓存命名空间;为空时内部会用 api.name + params 生成一个 key |
缓存适用场景:下拉多次分页加载后关闭再打开,希望还停留在上次加载的结果与位置。
4.6 回显与详情(detailApi / changeDetail)
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| changeDetail | boolean |
false |
为 true 时,选中后会额外触发 change-detail 事件,把选中项的 option 数据回传 |
| detailApi | `(value) => Promise<any> | any` | null |
典型场景:表单编辑页从后端拿到 userId,但下拉列表还没加载;此时通过 detailApi(userId) 把该用户详情拉回来做回显。
4.7 样式与触底哨兵
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| popperClass | string |
'' |
额外传给 el-select 的 popper-class,用于定制下拉面板样式作用域 |
| footerSentinelId | string |
'' |
自定义 footer sentinel 的 DOM id;内部用它定位触底观察点(一般不需要传) |
5. api 的写法(最重要)
组件会在这些时机调用 api(params):
- 下拉打开且无缓存(或缓存为空)时
- 输入关键词触发远程搜索时
- 触底(或点击"加载更多")时拉取下一页
5.1 api(params) 直接是请求函数
ts
const api = async (params: any) => {
return request.get('/users', { params })
}
5.2 apiAsync = true:api 先返回请求函数(适合依赖异步初始化)
vue
<SelectPagination v-model="value" :api="createApi" :api-async="true" />
ts
const createApi = async () => {
const token = await getTokenSomehow()
return (params: any) => request.get('/users', { params, headers: { token } })
}
6. 事件(Events)
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:modelValue | any |
v-model 更新 |
| change | any |
同 Element Plus 的 change |
| change-detail | `any | any[] |
7. 插槽(Slots)
7.1 footer:完全自定义"加载中/没有更多/加载更多"
slot props:
loading:boolean当前是否在请求hasMore:boolean是否还有下一页options:any[]当前已加载的 optionsloadMore:() => void主动触发加载更多
示例:
vue
<SelectPagination v-model="value" :api="fetchUsers" footer-sentinel-id="user-select-footer">
<template #footer="{ loading, hasMore, loadMore }">
<div v-if="loading" style="padding: 8px 0; text-align: center">加载中...</div>
<div v-else-if="!hasMore" style="padding: 8px 0; text-align: center">没有更多了</div>
<div v-else style="padding: 8px 0; text-align: center; cursor: pointer" @click="loadMore">
点击加载更多
</div>
</template>
</SelectPagination>
8. 暴露方法(ref)
通过组件 ref 访问:
loadData(isReset?: boolean):加载数据,传true表示重置并从第一页开始clearCache():清除缓存restoreCache():手动恢复缓存(返回是否成功)updateValue(val):手动更新值(会触发 v-model/change)loadEchoData():手动触发回显数据拉取setPage(page: number):设置页码(一般调试用)setOptions(options: any[]):直接覆盖 options(一般调试用)options:当前 options 的 refcurrentPage:当前页码的 ref