# ByCommonSelector 通用选择器组件
一个基于业务场景的通用选择器组件,支持多种选择模式和多样的交互效果。
注意:该组件通常在ByPageSearch组件中使用(可以单独使用),为了使选择器有一定间隙,务必给ByPageSearch中的formConfig.itemStyle设置间距:padding: '0px 20px 2px 0px'。
# 基础用法
# 下拉选择器
<template>
<ByCommonSelector
v-model="selectedValue"
:options="options"
type="select"
placeholder="请选择用户"
@change="handleChange"
/>
</template>
<script>
import ByCommonSelector from '@/components/common-selector/ByCommonSelector.vue'
export default {
components: {
ByCommonSelector
},
data() {
return {
selectedValue: '',
options: [
{ id: 1, name: '何其灿', initial: 'H', status: 1 },
{ id: 2, name: '张三', initial: 'Z', status: 1 },
{ id: 3, name: '李四', initial: 'L', status: 1 },
{ id: 4, name: '王五', initial: 'W', status: 0 },
{ id: 5, name: '赵六', initial: 'Z', status: 1 },
{ id: 6, name: '孙七', initial: 'S', status: 1 },
{ id: 7, name: '周八', initial: 'Z', status: 1 },
{ id: 8, name: '吴九', initial: 'W', status: 1 },
{ id: 9, name: '郑十', initial: 'Z', status: 1 }
]
}
},
methods: {
handleChange(value) {
console.log('选中值:', value)
}
}
}
</script>
# 异步加载数据
<template>
<ByCommonSelector
v-model="selectedValue"
:loadOptions="fetchUserList"
type="select"
placeholder="请选择用户"
@change="handleChange"
/>
</template>
<script>
import ByCommonSelector from '@/components/common-selector/ByCommonSelector.vue'
export default {
components: {
ByCommonSelector
},
data() {
return {
selectedValue: ''
}
},
methods: {
// 异步加载数据函数,返回Promise
fetchUserList() {
return new Promise(resolve => {
// 模拟API请求
setTimeout(() => {
resolve([
{ id: 1, name: '何其灿', initial: 'H', status: 1 },
{ id: 2, name: '张三', initial: 'Z', status: 1 },
{ id: 3, name: '李四', initial: 'L', status: 1 },
{ id: 4, name: '王五', initial: 'W', status: 0 },
{ id: 5, name: '赵六', initial: 'Z', status: 1 },
{ id: 6, name: '孙七', initial: 'S', status: 1 },
{ id: 7, name: '周八', initial: 'Z', status: 1 },
{ id: 8, name: '吴九', initial: 'W', status: 1 },
{ id: 9, name: '郑十', initial: 'Z', status: 1 }
])
}, 1000)
})
},
handleChange(value) {
console.log('选中值:', value)
}
}
}
</script>
# 展开式选择器
<template>
<ByCommonSelector
v-model="selectedValue"
:options="options"
type="checkbox"
:multiple="true"
placeholder="请选择多个用户"
@change="handleChange"
/>
</template>
# Props 参数
参数 | 说明 | 类型 | 默认值 | 必填 |
---|---|---|---|---|
value | 绑定值 | String/Number/Array | - | 是 |
options | 选项数据 | Array | [] | 是 |
loadOptions | 异步加载数据的函数 | Function | null | 否 |
multiple | 是否多选 | Boolean | false | 否 |
type | 选择器类型 | String | 'select' | 否 |
placeholder | 占位符文本 | String | '请选择'(enhancedSelect 类型默认为 '请输入用户名,手机号搜索') | 否 |
size | 组件尺寸 | String | 'default' | 否 |
showSearchBar | 是否显示搜索栏(A~Z+搜索框) | Boolean | false | 否 |
pagination | 是否启用分页 | Boolean | false | 否 |
pageSize | 每页显示数量 | Number | 50 | 否 |
needShowLoading | 非select模式下是否显示加载状态 | Boolean | false | 否 |
forceNoExpand | 是否强制不展开 | Boolean | false | 否 |
hiddenAllOptions | 是否隐藏"全部"选项 | Boolean | false | 否 |
showSuffixInput | 是否显示后置输入框 | Boolean | false | 否 |
suffixInputPlaceholder | 后置输入框占位符 | String | '请输入' | 否 |
suffixInputValue | 后置输入框的值 | String | '' | 否 |
suffixInputUnit | 后置输入框的单位 | String | '' | 否 |
suffixInputOptions | 后置输入框的配置选项 | Object | {} | 否 |
showSuffixBatchInput | 是否显示后置批量输入按钮 | Boolean | false | 否 |
suffixBatchInputPopoverTitle | 后置批量输入弹窗的标题 | String | '批量输入' | 否 |
suffixBatchInputPopoverWidth | 后置批量输入弹窗宽度 | String/Number | '300' | 否 |
enhancedAllOptions | 增强下拉:所有选项数据(用于默认值回显与对象映射) | Array | [] | 建议 |
enhancedTitleKey | 增强下拉:标题字段名 | String | 'name' | 否 |
enhancedDescriptionKey | 增强下拉:描述字段名 | String | 'mobile' | 否 |
enhancedDisabledKey | 增强下拉:禁用标识字段名(0 置灰) | String | 'status' | 否 |
enhancedDescriptionDisplay | 增强下拉:是否显示描述 | Boolean | true | 否 |
enhancedCollapseTags | 增强下拉:多选是否折叠标签 | Boolean | false | 否 |
enhancedDisabledCanSelect | 增强下拉:禁用项是否可选 | Boolean | false | 否 |
# type 可选值
select
: 下拉选择器enhancedSelect
: 增强下拉选择器(支持远程搜索、对象回传、默认值智能回显)checkbox
: 展开式选择器(无边框样式)border
: 带边框的展开式选择器
# size 可选值
mini
: 迷你尺寸small
: 小尺寸medium
: 中等尺寸default
: 默认尺寸
# 选项数据格式
const options = [
{
id: 1, // 选项值(必填)
name: '张三', // 显示文本(必填)
initial: 'Z', // 首字母,用于快速筛选(可选,如果未提供会自动根据name生成)
status: 1 // 状态,0表示禁用/离职等(可选)
}
]
# Events 事件
事件名 | 说明 | 回调参数 |
---|---|---|
change | 选中值发生变化时触发 | 普通类型:(value: String/Number/Array);enhancedSelect:(obj: Object 或 Array<Object>) |
input | 绑定值发生变化时触发 | (value: String/Number/Array) |
suffix-input-change | 后置输入框值变化时触发 | (value: String) |
suffix-batch-input | 批量输入确定时触发 | (value: String) |
search | enhancedSelect 远程搜索触发 | (query: String) |
# Slots 插槽
插槽名 | 说明 |
---|---|
suffixSlot | 后置插槽,在选择器和后置输入框后添加内容 |
# 使用示例
# 1. 基础单选
<ByCommonSelector v-model="selectedUser" :options="userList" type="select" placeholder="请选择用户" />
# 2. 多选下拉
<ByCommonSelector
v-model="selectedUsers"
:options="userList"
type="select"
:multiple="true"
placeholder="请选择多个用户"
/>
# 3. 异步加载数据
<ByCommonSelector v-model="selectedUser" :loadOptions="fetchUserOptions" type="select" placeholder="请选择用户" />
methods: {
fetchUserOptions() {
return this.$api.getUserList().then(response => {
return response.data.map(user => ({
id: user.id,
name: user.name,
initial: user.pinyin.charAt(0).toUpperCase(),
status: user.status
}))
})
}
}
# 4. 展开式单选
<ByCommonSelector v-model="selectedUser" :options="userList" type="checkbox" placeholder="请选择用户" />
# 5. 展开式多选
<ByCommonSelector
v-model="selectedUsers"
:options="userList"
type="checkbox"
:multiple="true"
placeholder="请选择多个用户"
/>
# 6. 带边框样式
<ByCommonSelector v-model="selectedUsers" :options="userList" type="border" :multiple="true" placeholder="请选择用户" />
# 7. 禁用搜索
<ByCommonSelector
v-model="selectedUser"
:options="userList"
type="checkbox"
:showSearchBar="false"
placeholder="请选择用户"
/>
# 8. 带后置输入框
<ByCommonSelector
v-model="selectedUser"
:options="userList"
type="checkbox"
:showSuffixInput="true"
suffixInputPlaceholder="请输入备注"
suffixInputUnit="元"
v-model:suffixInputValue="remarkValue"
@suffix-input-change="handleRemarkChange"
/>
data() {
return {
selectedUser: '',
remarkValue: '',
userList: [
// ...用户列表
]
}
},
methods: {
handleRemarkChange(value) {
console.log('备注值变化:', value)
}
}
# 9. 使用后置插槽
<ByCommonSelector v-model="selectedUser" :options="userList" type="checkbox">
<template #suffixSlot>
<el-button size="small" type="primary" style="margin-left: 5px">确定</el-button>
</template>
</ByCommonSelector>
# 10. 增强下拉选择器(enhancedSelect)
增强下拉用于本地搜索(模拟远程搜索交互),支持标题+描述展示,默认值智能回显,变更时向外抛出对象(或对象数组)。
单选示例:
<template>
<ByCommonSelector
v-model="selectedUserId"
:options="enhancedSearchResults"
:enhancedAllOptions="allUsers"
type="enhancedSelect"
enhancedTitleKey="name"
enhancedDescriptionKey="mobile"
:enhancedDescriptionDisplay="true"
placeholder="请输入用户名,手机号搜索"
@search="handleEnhancedSearch"
@change="handleEnhancedChange"
/>
</template>
<script>
export default {
data() {
return {
selectedUserId: '',
enhancedSearchResults: [], // 搜索结果(用于下拉展示)
allUsers: [] // 全量数据(用于默认值回显与对象映射)
}
},
methods: {
async handleEnhancedSearch(query) {
// 根据 query 远程查询,并设置 enhancedSearchResults
const { data } = await this.$http.get('/api/users/search', { params: { q: query } })
this.enhancedSearchResults = data.list
// 建议在首次加载或独立接口中填充 allUsers(全量或足量数据以支持默认值回显)
},
handleEnhancedChange(userObj) {
// userObj 形如:{ id, name, mobile }
console.log('选中对象:', userObj)
}
}
}
</script>
多选示例:
<ByCommonSelector
v-model="selectedUserIds"
:options="enhancedSearchResults"
:enhancedAllOptions="allUsers"
type="enhancedSelect"
:multiple="true"
:enhancedCollapseTags="true"
enhancedTitleKey="name"
enhancedDescriptionKey="mobile"
@search="handleEnhancedSearch"
@change="handleEnhancedChangeMultiple"
/>
注意:
- enhancedSelect 下
change
事件返回对象(多选返回对象数组),input
仍然是原始值(id 或 id 数组)。 - 为保证默认值正确回显,需提供
enhancedAllOptions
(至少包含已选中的项)。 enhancedDisabledKey
指定置灰字段,配合enhancedDisabledCanSelect
决定置灰项是否可选。- 当
enhancedDescriptionDisplay=true
时,选项以“标题 描述”形式展示。
# 注意事项
- 数据格式: 确保传入的
options
数据包含id
和name
字段 - 首字母筛选: 如果需要使用首字母快速筛选功能,请确保数据包含
initial
字段或启用showSearchBar
。如果未提供initial字段,组件会自动根据name生成:- 对于中文,会提取拼音首字母并转为大写
- 对于非中文,直接取首字母并转为大写
- 状态显示: 如果需要显示选项状态(如离职状态),请确保数据包含
status
字段,0表示禁用/离职 - 大量数据: 当选项数量超过pageSize时,会自动启用分页功能(如果pagination设为true)
- 样式定制: 组件样式已提取到
@/style/common-selector.scss
文件中,可根据需要进行定制 - 选中按钮: 展开式选择器(checkbox/border类型)通过CSS隐藏选中按钮,只通过边框和背景色表示选中状态
- 智能展开按钮: 组件会根据选项的实际渲染宽度自动判断是否需要显示展开/收起按钮,当内容不超过一行时不会显示展开按钮
- 响应式设计: 当浏览器窗口大小变化或页面缩放时,组件会自动重新计算是否需要显示展开按钮
- 后置输入框: 后置输入框仅在选择器展开时显示,可用于输入额外信息,如备注、说明等
- 批量输入功能: 批量输入功能仅在显示后置输入框时生效,支持多种分隔符,自动处理输入内容并双向回显
# 新增特性
# 1. 智能展开/收起按钮
组件会根据选项的实际渲染宽度自动判断是否需要显示展开/收起按钮:
- 当选项内容不超过一行时,不会显示展开按钮
- 当选项内容超过一行时,会显示展开按钮
- 当浏览器窗口大小变化或页面缩放时,会自动重新计算是否需要显示展开按钮
- 当开启分页功能且数据量超过pageSize时,会强制显示展开按钮
# 2. 自动生成首字母
# 3. 可控的加载状态显示
通过 needShowLoading
属性可以控制非select模式下是否显示加载状态:
<ByCommonSelector v-model="selectedValue" :loadOptions="fetchUserList" type="checkbox" :needShowLoading="true" />
当 needShowLoading
设置为 true
时,在异步加载数据过程中会显示"加载中..."的状态提示。
# 4. 后置输入框
组件支持在选择器后添加输入框,用于输入额外信息:
<ByCommonSelector
v-model="selectedUser"
:options="userList"
type="checkbox"
:showSuffixInput="true"
suffixInputPlaceholder="请输入备注"
suffixInputUnit="元"
v-model:suffixInputValue="remarkValue"
@suffix-input-change="handleRemarkChange"
>
<template #suffixSlot>
<el-button size="small" type="primary" style="margin-left: 5px">确定</el-button>
</template>
</ByCommonSelector>
后置输入框特性:
- 通过
showSuffixInput
控制是否显示 - 通过
suffixInputPlaceholder
设置占位符文本 - 通过
v-model:suffixInputValue
双向绑定输入框的值 - 通过
@suffix-input-change
监听输入框值变化 - 通过
suffixInputOptions
传递其他el-input支持的属性 - 通过
suffixInputUnit
设置后置输入框的单位 - 通过
suffixSlot
插槽添加自定义内容
# 5. 批量输入功能
组件支持批量输入功能,方便用户一次性输入多个值:
<ByCommonSelector
v-model="selectedUser"
:options="userList"
type="checkbox"
:showSuffixInput="true"
:showSuffixBatchInput="true"
suffixInputPlaceholder="请输入用户ID"
suffixBatchInputPopoverTitle="批量输入用户ID"
suffixBatchInputPopoverWidth="400"
v-model:suffixInputValue="userIds"
@suffix-batch-input="handleBatchInput"
/>
批量输入功能特性:
- 通过
showSuffixBatchInput
控制是否显示批量输入按钮 - 通过
suffixBatchInputPopoverTitle
设置弹窗标题 - 通过
suffixBatchInputPopoverWidth
设置弹窗宽度 - 支持多种分隔符:换行、逗号、分号、空格
- 自动过滤空项和去除首尾空格
- 双向回显:打开弹窗时会回显后置输入框中的内容
- 确定后会将处理后的内容用逗号分隔显示到后置输入框
- 通过
@suffix-batch-input
监听批量输入确定事件
# 完整示例
<template>
<div class="selector-demo">
<h3>用户选择器</h3>
<!-- 单选下拉 -->
<div class="demo-item">
<label>单选用户:</label>
<ByCommonSelector
v-model="singleUser"
:options="userOptions"
type="select"
placeholder="请选择用户"
@change="onSingleChange"
/>
<span>选中: {{ singleUser }}</span>
</div>
<!-- 多选展开 -->
<div class="demo-item">
<label>多选用户:</label>
<ByCommonSelector
v-model="multipleUsers"
:options="userOptions"
type="checkbox"
:multiple="true"
placeholder="请选择多个用户"
@change="onMultipleChange"
/>
<span>选中: {{ multipleUsers }}</span>
</div>
<!-- 带后置输入框 -->
<div class="demo-item">
<label>带备注选择:</label>
<ByCommonSelector
v-model="selectedWithRemark"
:options="userOptions"
type="checkbox"
:showSuffixInput="true"
suffixInputPlaceholder="请输入备注"
suffixInputUnit="元"
v-model:suffixInputValue="remarkValue"
@suffix-input-change="onRemarkChange"
>
<template #suffixSlot>
<el-button size="small" type="text" style="margin-left: 5px">确认</el-button>
</template>
</ByCommonSelector>
<span>选中: {{ selectedWithRemark }}, 备注: {{ remarkValue }}</span>
</div>
<!-- 批量输入功能 -->
<div class="demo-item">
<label>批量输入:</label>
<ByCommonSelector
v-model="selectedWithBatch"
:options="userOptions"
type="checkbox"
:showSuffixInput="true"
:showSuffixBatchInput="true"
suffixInputPlaceholder="请输入用户ID"
suffixBatchInputPopoverTitle="批量输入用户ID"
suffixBatchInputPopoverWidth="400"
v-model:suffixInputValue="batchUserIds"
@suffix-batch-input="onBatchInput"
/>
<span>选中: {{ selectedWithBatch }}, 用户ID: {{ batchUserIds }}</span>
</div>
</div>
</template>
<script>
import ByCommonSelector from '@/components/common-selector/ByCommonSelector.vue'
export default {
name: 'SelectorDemo',
components: {
ByCommonSelector
},
data() {
return {
singleUser: '',
multipleUsers: [],
selectedWithRemark: '',
remarkValue: '',
selectedWithBatch: '',
batchUserIds: '',
userOptions: [
{ id: 1, name: '张三', initial: 'Z', status: 1 },
{ id: 2, name: '李四', initial: 'L', status: 1 },
{ id: 3, name: '王五', initial: 'W', status: 0 },
{ id: 4, name: '赵六', initial: 'Z', status: 1 },
{ id: 5, name: '钱七', initial: 'Q', status: 1 }
]
}
},
methods: {
onSingleChange(value) {
console.log('单选变化:', value)
},
onMultipleChange(value) {
console.log('多选变化:', value)
},
onRemarkChange(value) {
console.log('备注变化:', value)
},
onBatchInput(value) {
console.log('批量输入内容:', value)
// 这里可以处理批量输入的内容,比如解析用户ID等
}
}
}
</script>
<style lang="scss" scoped>
.selector-demo {
padding: 20px;
.demo-item {
margin-bottom: 20px;
label {
display: inline-block;
width: 100px;
font-weight: bold;
}
span {
margin-left: 10px;
color: #666;
}
}
}
</style>