Skip to content
On this page

Select 选择器

简介

ElSelect 的痛点

  1. 需要传入特定结构的数据({label:string,value:number}[]),不够灵活;
  2. change 事件只返回了当前的 value 值,没能把整个对象返回,仍需使用 find()方法查一遍;
  3. 远程搜索或者编辑页回显时,当所选的值不在 options 中时会显示 id;
  4. 远程搜索时,当没有选项,没有出现“暂无数据”;
  5. 一些业务中,需要在点击选项时,先进行一些判断,然后不满足条件时阻止选中,即需要 beforeSelect 事件。

本组件优势

针对上述缺陷与不足,完全重写了下拉选择器。

  1. 解决上述所以痛点;
  2. 完全复用 ElSelect 样式,风格样式保持统一;
  3. 下拉选项使用虚拟列表,不惧数据量大。

基础用法

<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绑定值anyundefined
disabled是否禁用booleanfalse
multiple是否多选booleanfalse
props配置选项,具体看下表Objectundefined
options选项Object[][]
popperClass选择器下拉菜单的自定义类名string''
name选择器的原生 name 属性stringundefined
placeholderplaceholderstring'请选择'
suffixIcon选择器后缀图标iconPropTypeArrowDown
clearable是否可清空booleantrue
clearIcon清空图标iconPropTypeCircleClose
collapseTags多选时是否将选中值按文字的形式展示booleanfalse
collapseTagsTooltip当鼠标悬停于折叠标签的文本时,是否显示所有选中的标签。 只有当 collapse-tags 设置为 true 时才会生效。booleanfalse
tagTypetag 类型'success','warning','info''info'
tagsHeighttags 高度,超出显示滚动条string、numberundefined
loadingloadingbooleanfalse
loadingTextloading 文字string'加载中...'
emptyText无数据时文字string'暂无数据'
remote是否远程搜索booleanfalse
remoteMethod远程搜索方法functionundefined
filterable是否可过滤booleanfalse
filterMethod过滤方法functionundefined
beforeSelect选中之前的操作functionundefined
multipleLimit限制多选个数number0
multipleLimitText超出选中个数的文字string'选择个数超过限制'
itemHeight选项高度number32
height下拉框最大高度number192
scrollbarAlwaysOn滚动条是否一直在booleanfalse
isObject返回的 modelValue 是否是整个对象booleanfalse
valueOptions用于回显的 optionsarray[]

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下拉框底部

Released under the MIT License.