Select 选择器
简介
ElSelect 的痛点
- 需要传入特定结构的数据({label:string,value:number}[]),不够灵活;
- change 事件只返回了当前的 value 值,没能把整个对象返回,仍需使用 find()方法查一遍;
- 远程搜索或者编辑页回显时,当所选的值不在 options 中时会显示 id;
- 远程搜索时,当没有选项,没有出现“暂无数据”;
- 一些业务中,需要在点击选项时,先进行一些判断,然后不满足条件时阻止选中,即需要 beforeSelect 事件。
本组件优势
针对上述缺陷与不足,完全重写了下拉选择器。
- 解决上述所以痛点;
- 完全复用
ElSelect
样式,风格样式保持统一; - 下拉选项使用虚拟列表,不惧数据量大。
基础用法
<template>
<CxzSelect v-model="value" :options="options" filterable />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref([
{ label: 'label1', value: 1 },
{ label: 'label2', value: 2 },
{ label: 'label3', value: 3 },
{ label: 'label4', value: 4 },
{ label: 'label5', value: 5 }
])
const value = ref()
</script>
多选
<template>
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<CxzSelect v-model="value" :options="options" filterable multiple />
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<CxzSelect
v-model="value"
:options="options"
filterable
multiple
collapse-tags
collapse-tags-tooltip
/>
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref([
{
label: 'label1',
value: 1
},
{
label: 'label2',
value: 2
},
{
label: 'label3',
value: 3
},
{
label: 'label4',
value: 4
},
{
label: 'label5',
value: 5
}
])
const value = ref()
</script>
任意对象数组
通过 props 确定 label、value 的键
<template>
<CxzSelect
v-model="value"
:options="options"
:props="{ label: 'a', value: 'b' }"
filterable
/>
值:{{ value }}
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref([
{ a: 'label1', b: 1, c: 'c1', d: 'd1' },
{ a: 'label2', b: 2, c: 'c1', d: 'd1' },
{ a: 'label3', b: 3, c: 'c1', d: 'd1' },
{ a: 'label4', b: 4, c: 'c1', d: 'd1' },
{ a: 'label5', b: 5, c: 'c1', d: 'd1' }
])
const value = ref()
</script>
change 事件
值:
详情:
值:
详情:
<template>
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<CxzSelect
v-model="value1"
:options="options"
:props="{ label: 'a', value: 'b' }"
filterable
@change="handleChange1"
/>
<div>值:{{ value1 }}</div>
<div>详情:{{ option1 }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<CxzSelect
v-model="value2"
:options="options"
:props="{ label: 'a', value: 'b' }"
filterable
multiple
@change="handleChange2"
/>
<div>值:{{ value2 }}</div>
<div>详情:{{ option2 }}</div>
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref([
{ a: 'label1', b: 1, c: 'c1', d: 'd1' },
{ a: 'label2', b: 2, c: 'c1', d: 'd1' },
{ a: 'label3', b: 3, c: 'c1', d: 'd1' },
{ a: 'label4', b: 4, c: 'c1', d: 'd1' },
{ a: 'label5', b: 5, c: 'c1', d: 'd1' }
])
const value1 = ref()
const option1 = ref()
const handleChange1 = (val1: number, val2: Record<string, any>) => {
option1.value = val2
}
const value2 = ref()
const option2 = ref()
const handleChange2 = (val1: number[], val2: Record<string, any>[]) => {
option2.value = val2
}
</script>
远程搜索
<template>
<CxzSelect
v-model="value"
:options="options"
filterable
remote
:remote-method="remoteMethod"
:loading="loading"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref()
const value = ref()
const loading = ref(false)
const remoteMethod = (query: string) => {
loading.value = true
setTimeout(() => {
if (query) {
options.value = Array(10)
.fill(1)
.map((el, index) => ({
label: 'label' + (index + 10),
value: index + 10
}))
} else {
options.value = []
}
loading.value = false
}, 1000)
}
</script>
将整个对象作为值
值:{
"label": "label1",
"value": 1
}
值:
<template>
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<CxzSelect v-model="value1" :options="options" filterable is-object />
<div>值:{{ value1 }}</div>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<CxzSelect
v-model="value2"
:options="options"
filterable
multiple
is-object
/>
<div>值:{{ value2 }}</div>
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref([
{ label: 'label1', value: 1 },
{ label: 'label2', value: 2 },
{ label: 'label3', value: 3 },
{ label: 'label4', value: 4 },
{ label: 'label5', value: 5 }
])
const value1 = ref({ label: 'label1', value: 1 })
const value2 = ref()
</script>
编辑页面回显
1. 当value值在options中,自动回显
2. 当options中没有,valueOptions中有
3. 当value值为object,回显object中label
<template>
<el-row :gutter="20">
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
1. 当value值在options中,自动回显
<CxzSelect v-model="value1" :options="options" />
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
2. 当options中没有,valueOptions中有
<CxzSelect
v-model="value2"
:options="options"
:value-options="valueOptions"
/>
</el-col>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
3. 当value值为object,回显object中label
<CxzSelect v-model="value3" :options="options" />
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref([
{ label: 'label1', value: 1 },
{ label: 'label2', value: 2 },
{ label: 'label3', value: 3 },
{ label: 'label4', value: 4 },
{ label: 'label5', value: 5 }
])
const value1 = ref(1)
const valueOptions = ref([{ label: 'label20', value: 20 }])
const value2 = ref(20)
const value3 = ref({ label: 'label30', value: 30 })
</script>
选中之前判断
第一项不能被选择
<template>
<CxzSelect
v-model="value"
:options="options"
filterable
:before-select="beforeSelect"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
import { ElMessage } from 'element-plus'
const options = ref([
{ label: 'label1', value: 1 },
{ label: 'label2', value: 2 },
{ label: 'label3', value: 3 },
{ label: 'label4', value: 4 },
{ label: 'label5', value: 5 }
])
const value = ref()
const beforeSelect = (
value: number,
option: Record<string, any>,
isSelected: boolean
) => {
if (value === 1) {
ElMessage.error('不能选择哦!')
return false
}
return true
}
</script>
分组
<template>
<CxzSelect
v-model="value"
:options="options"
multiple
:props="{
children: 'options'
}"
clearable
/>
</template>
<script lang="ts" setup>
import { CxzSelect } from 'cxz-ui'
import { ref } from 'vue'
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
const value = ref([])
const options = Array.from({ length: 10 }).map((_, idx) => {
const label = idx + 1
return {
value: `Group ${label}`,
label: `Group ${label}`,
options: Array.from({ length: 10 }).map((_, idx) => ({
value: `Option ${idx + 1 + 10 * label}`,
label: `${initials[idx % 10]}${idx + 1 + 10 * label}`
}))
}
})
</script>
插槽
默认插槽
<template>
<CxzSelect
v-model="value"
:options="options"
:props="{
label: 'name',
value: 'id'
}"
:item-height="50"
>
<template #default="{ option }">
<div class="option-container">
<div class="left"><el-avatar :src="option.img" :size="32" /></div>
<div class="right">
<div>{{ option.name }}</div>
<div class="msg">
<span>工号:{{ option.id }}</span>
<span>手机号:{{ option.phone }}</span>
</div>
</div>
</div>
</template>
</CxzSelect>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { CxzSelect } from 'cxz-ui'
const options = ref([
{
id: '11111',
name: '杨延昭',
phone: '177****1234',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
},
{
id: '11112',
name: '宋江',
phone: '178****2222',
img: 'https://fuss10.elemecdn.com/1/34/19aa98b1fcb2781c4fba33d850549jpeg.jpeg'
},
{
id: '11113',
name: '朱元璋',
phone: '156****8989',
img: 'https://fuss10.elemecdn.com/0/6f/e35ff375812e6b0020b6b4e8f9583jpeg.jpeg'
},
{
id: '11114',
name: '花木兰',
phone: '139****0312',
img: 'https://fuss10.elemecdn.com/9/bb/e27858e973f5d7d3904835f46abbdjpeg.jpeg'
},
{
id: '11115',
name: '妹喜',
phone: '138****2347',
img: 'https://fuss10.elemecdn.com/d/e6/c4d93a3805b3ce3f323f7974e6f78jpeg.jpeg'
}
])
const value = ref()
</script>
<style lang="scss" scoped>
.option-container {
display: flex;
height: 50px;
overflow: hidden;
align-items: center;
width: 100%;
.left {
display: flex;
align-items: center;
}
.right {
flex: 1;
display: flex;
line-height: 22px;
flex-direction: column;
margin-left: 12px;
.msg {
color: #86909c;
font-weight: 400;
span + span {
margin-left: 20px;
}
}
}
}
</style>
Select 属性
属性名 | 说明 | 类型 | 默认值 |
---|---|---|---|
v-model | 绑定值 | any | undefined |
disabled | 是否禁用 | boolean | false |
multiple | 是否多选 | boolean | false |
props | 配置选项,具体看下表 | Object | undefined |
options | 选项 | Object[] | [] |
popperClass | 选择器下拉菜单的自定义类名 | string | '' |
name | 选择器的原生 name 属性 | string | undefined |
placeholder | placeholder | string | '请选择' |
suffixIcon | 选择器后缀图标 | iconPropType | ArrowDown |
clearable | 是否可清空 | boolean | true |
clearIcon | 清空图标 | iconPropType | CircleClose |
collapseTags | 多选时是否将选中值按文字的形式展示 | boolean | false |
collapseTagsTooltip | 当鼠标悬停于折叠标签的文本时,是否显示所有选中的标签。 只有当 collapse-tags 设置为 true 时才会生效。 | boolean | false |
tagType | tag 类型 | 'success','warning','info' | 'info' |
tagsHeight | tags 高度,超出显示滚动条 | string、number | undefined |
loading | loading | boolean | false |
loadingText | loading 文字 | string | '加载中...' |
emptyText | 无数据时文字 | string | '暂无数据' |
remote | 是否远程搜索 | boolean | false |
remoteMethod | 远程搜索方法 | function | undefined |
filterable | 是否可过滤 | boolean | false |
filterMethod | 过滤方法 | function | undefined |
beforeSelect | 选中之前的操作 | function | undefined |
multipleLimit | 限制多选个数 | number | 0 |
multipleLimitText | 超出选中个数的文字 | string | '选择个数超过限制' |
itemHeight | 选项高度 | number | 32 |
height | 下拉框最大高度 | number | 192 |
scrollbarAlwaysOn | 滚动条是否一直在 | boolean | false |
isObject | 返回的 modelValue 是否是整个对象 | boolean | false |
valueOptions | 用于回显的 options | array | [] |
Select props 配置项
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
value | 绑定值 | string | 'value' |
label | 显示的字段 | string | 'label' |
disabled | 是否禁用字段 | string | 'disabled' |
children | 树的子树,或分组的内容 | string | '__children' |
Select 事件
事件名 | 说明 | 回调参数 |
---|---|---|
change | 选中值发生变化时触发 | value 选中的值,option 整个选项对象 |
visible-change | 下拉框出现/隐藏时触发 | val,出现则为 true,隐藏则为 false |
remove-tag | 多选模式下移除 tag 时触发 | val,移除的 tag 值 |
clear | 可清空的单选模式下用户点击清空按钮时触发 | —— |
blur | 失焦 | —— |
focus | 聚焦 | —— |
Select Expose
名称 | 说明 |
---|---|
selectedMaps | 选中值详情的集合 |
Select 插槽
插槽名 | 说明 |
---|---|
default | 参数 option: 当前选项,index,disable:是否禁用 |
empty | 自定义当选项为空时的内容 |
prefix | 输入框的前缀 |
content | 自定义输入框回显内容。 作用域参数为 maps,选中值的 map |
tag | 作用域参数为 value: 选中的值, option:当前选项对象 |
header | 下拉框头部 |
footer | 下拉框底部 |