Skip to content

选择器 Select ​

赞助

下拉选择器

何时使用 ​

  • 弹出一个下拉菜单给用户选择操作,用于代替原生的选择器,或者需要一个更优雅的多选器时
  • 当选项少时(少于 5 项),建议直接将选项平铺,使用 Radio 是更好的选择

基本使用 ​

Lucy
Lucy
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { value: 'jack', label: 'Jack' },
  { value: 'lucy', label: 'Lucy' },
  { value: 'disabled', label: 'Disabled', disabled: true },
  { value: 'yiminghe', label: 'Yiminghe' }
])
const selectedValue = ref<SelectProps['value']>('lucy')
function onChange(value: string | number, option: SelectOption, index: number) {
  console.log('change', value, option, index)
}
function onFocus() {
  console.log('focus')
}
</script>
<template>
  <Select :options="options" v-model:value="selectedValue" :width="120" @change="onChange" @focus="onFocus" />
</template>

三种大小 ​

large
middle
small
选项 1
选项 1
选项 1, 选项 2
选项 1选项 2
选项 1, 选项 2
选项 1选项 2
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const sizeOptions: SelectOption[] = [...Array(25)].map((_, index) => ({
  value: `${index + 10}`,
  label: `选项 ${index + 1}`
}))
const size = ref<SelectProps['size']>('middle')
const selectedValue = ref<SelectProps['value']>('10')
const multipleValue = ref<SelectProps['value']>(['10', '11'])
const tagsValue = ref<SelectProps['value']>(['10', '11'])
const sizeRadios = [
  { label: 'large', value: 'large' },
  { label: 'middle', value: 'middle' },
  { label: 'small', value: 'small' }
]
function onPopupScroll() {
  console.log('popupScroll')
}
</script>
<template>
  <Space vertical>
    <Radio :options="sizeRadios" v-model:value="size" button button-style="solid" />
    <Space>
      <Select :options="sizeOptions" v-model:value="selectedValue" :size="size" :width="200" @popup-scroll="onPopupScroll" />
      <Select
        :options="sizeOptions"
        v-model:value="multipleValue"
        mode="multiple"
        :size="size"
        placeholder="Please select"
        :width="200"
        @popup-scroll="onPopupScroll"
      />
      <Select
        :options="sizeOptions"
        v-model:value="tagsValue"
        mode="tags"
        :size="size"
        placeholder="Please select"
        :width="200"
      />
    </Space>
  </Space>
</template>

无边框 ​

北京市
北京市
北京市
北京市
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(1)
</script>
<template>
  <Space>
    <Select :options="options" v-model:value="selectedValue" :bordered="false" :width="120" />
    <Select :options="options" v-model:value="selectedValue" :bordered="false" :width="120" disabled />
  </Space>
</template>

禁用 ​

伊斯坦布尔
伊斯坦布尔
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(5)
</script>
<template>
  <Select :options="options" v-model:value="selectedValue" disabled />
</template>

禁用选项 ​

伊斯坦布尔
伊斯坦布尔
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2, disabled: true },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(5)
</script>
<template>
  <Select :options="options" v-model:value="selectedValue" />
</template>

自定义状态 ​

status 可选 error 或 warning


北京市
北京市
北京市
北京市
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(1)
</script>
<template>
  <Space>
    <Select :options="options" v-model:value="selectedValue" status="error" />
    <Select :options="options" v-model:value="selectedValue" status="warning" />
  </Space>
</template>

多选 ​

从已有条目中选择多个值,下拉列表可滚动查看全部选项


选项 1, 选项 2
选项 1选项 2
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options: SelectOption[] = [...Array(25)].map((_, index) => ({
  value: `${index + 10}`,
  label: `选项 ${index + 1}`
}))
const selectedValue = ref<SelectProps['value']>(['10', '11'])
</script>
<template>
  <Select v-model:value="selectedValue" mode="multiple" placeholder="Please select" :options="options" :width="300" />
</template>

标签 ​

输入任意内容并回车即可创建为标签,下拉列表可滚动查看全部选项


Tags Mode
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options: SelectOption[] = [...Array(25)].map((_, index) => ({
  value: `${index + 10}`,
  label: `选项 ${index + 1}`
}))
const selectedValue = ref<SelectProps['value']>([])
</script>
<template>
  <Select v-model:value="selectedValue" mode="tags" placeholder="Tags Mode" :options="options" :width="300" />
</template>

最多显示多少个选项及选项最大长度 ​

超出 maxTagCount 的标签会折叠为省略提示,maxTagTextLength 截断过长的标签文本;maxTagCount 设为 responsive 时按容器宽度自动折叠(有性能消耗,不建议在大表单场景使用)


maxTagCount
1
2
3
5
responsive
Long Label: a10, Long Label: c12, Long Label: h17, Long Label: j19, Long Label: k20
Long Label: a10Long Label: c12Long Label: h17Long Label: j19Long Label: k20+ 3 ...
maxTagTextLength
5
10
20
Long Label: a10, Long Label: c12, Long Label: h17, Long Label: j19, Long Label: k20
Long Label...Long Label...Long Label...Long Label...Long Label...
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options: SelectOption[] = [...Array(26)].map((_, index) => {
  const value = `${(index + 10).toString(36)}${index + 10}`
  return { label: `Long Label: ${value}`, value }
})
const selectedValue = ref<SelectProps['value']>(['a10', 'c12', 'h17', 'j19', 'k20'])
const maxTagCount = ref<number | 'responsive'>(2)
const maxTagTextLength = ref(10)
const maxTagCountRadios = [
  { label: '1', value: 1 },
  { label: '2', value: 2 },
  { label: '3', value: 3 },
  { label: '5', value: 5 },
  { label: 'responsive', value: 'responsive' }
]
const maxTagTextLengthRadios = [
  { label: '5', value: 5 },
  { label: '10', value: 10 },
  { label: '20', value: 20 }
]
</script>
<template>
  <Flex vertical gap="large" align="start">
    <Flex vertical gap="middle" align="start">
      <Flex gap="small" align="center" wrap="wrap">
        <code>maxTagCount</code>
        <Radio :options="maxTagCountRadios" v-model:value="maxTagCount" button button-style="solid" />
      </Flex>
      <Select
        v-model:value="selectedValue"
        mode="multiple"
        placeholder="Select Item..."
        :max-tag-count="maxTagCount"
        :options="options"
        :width="360"
      >
        <template #maxTagPlaceholder="{ omittedValues }">
          <span style="color: red">+ {{ omittedValues.length }} ...</span>
        </template>
      </Select>
    </Flex>
    <Flex vertical gap="middle" align="start">
      <Flex gap="small" align="center" wrap="wrap">
        <code>maxTagTextLength</code>
        <Radio :options="maxTagTextLengthRadios" v-model:value="maxTagTextLength" button button-style="solid" />
      </Flex>
      <Select
        v-model:value="selectedValue"
        mode="multiple"
        placeholder="Select Item..."
        :max-tag-text-length="maxTagTextLength"
        :options="options"
        :width="360"
      />
    </Flex>
  </Flex>
</template>

隐藏已选择选项 ​

隐藏下拉列表中已选择的选项,已选项的标签由组件内部的选项缓存保留


Inserted are removed
Show Code
vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const source = ['Apples', 'Nails', 'Bananas', 'Helicopters']
const selectedValue = ref<SelectProps['value']>([])
const options = computed<SelectOption[]>(() => {
  const selected = Array.isArray(selectedValue.value) ? selectedValue.value : []
  return source.filter((item) => !selected.includes(item)).map((item) => ({ value: item }))
})
</script>
<template>
  <Select
    v-model:value="selectedValue"
    mode="multiple"
    placeholder="Inserted are removed"
    :options="options"
    :width="300"
  />
</template>

自动分词 ​

试下复制 特斯拉,哥斯拉 到输入框里。只在 tags 和 multiple 模式下可用


Automatic tokenization
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options: SelectOption[] = [{ value: 'a1', label: 'a1' }]
const selectedValue = ref<SelectProps['value']>([])
</script>
<template>
  <Select
    v-model:value="selectedValue"
    mode="tags"
    placeholder="Automatic tokenization"
    :token-separators="[',']"
    :options="options"
    :width="300"
  />
</template>

获得选项的文本 ​

开启 labelInValue 后 value 变为包含文本的对象:{ label, value, key, originLabel }


Lucy (101)
Lucy (101)
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options: SelectOption[] = [
  { value: 'jack', label: 'Jack (100)' },
  { value: 'lucy', label: 'Lucy (101)' },
  { value: 'tom', label: 'Tom (102)' },
  { value: 'jerry', label: 'Jerry (103)' },
  { value: 'bob', label: 'Bob (104)' },
  { value: 'alice', label: 'Alice (105)' },
  { value: 'david', label: 'David (106)' },
  { value: 'eva', label: 'Eva (107)' }
]
const value = ref<SelectProps['value']>({ value: 'lucy', label: 'Lucy (101)' })
function handleChange(value: SelectProps['value']) {
  console.log(value) // { label: 'Lucy (101)', value: 'lucy', key: 'lucy', originLabel: 'Lucy (101)' }
}
</script>
<template>
  <Select v-model:value="value" label-in-value :options="options" :width="120" @change="handleChange" />
</template>

分组 ​

用 SelectOptGroup / SelectOption 子组件或 options 的嵌套写法进行选项分组


Lucy
Lucy
Lucy
Lucy
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { UserOutlined } from '@ant-design/icons-vue'
import type { SelectProps } from 'vue-amazing-ui'
const value = ref<SelectProps['value']>('lucy')
const options = [
  {
    label: 'Manager',
    options: [
      { value: 'jack', label: 'Jack' },
      { value: 'lucy', label: 'Lucy' }
    ]
  },
  {
    label: 'Engineer',
    options: [{ value: 'yiminghe', label: 'Yiminghe' }]
  }
]
</script>
<template>
  <Space>
    <Select v-model:value="value" :width="200">
      <SelectOptGroup>
        <template #label>
          <span><UserOutlined /> Manager</span>
        </template>
        <SelectOption value="jack">Jack</SelectOption>
        <SelectOption value="lucy">Lucy</SelectOption>
      </SelectOptGroup>
      <SelectOptGroup label="Engineer">
        <SelectOption value="Yiminghe">yiminghe</SelectOption>
        <SelectOption value="Yiminghe1">yiminghe1</SelectOption>
      </SelectOptGroup>
    </Select>
    <Select v-model:value="value" :options="options" :width="200" />
  </Space>
</template>

自定义 label、value、options 字段 ​

通过 fieldNames 指定选项的文本 / 值字段,以及分组子选项的字段(options)


Lucy
Lucy
Lucy
Lucy
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps } from 'vue-amazing-ui'
const options = [
  { id: 'jack', name: 'Jack' },
  { id: 'lucy', name: 'Lucy' },
  { id: 'disabled', name: 'Disabled', disabled: true },
  { id: 'yiminghe', name: 'Yiminghe' }
]
const fieldNames = { label: 'name', value: 'id' }
const selectedValue = ref<SelectProps['value']>('lucy')
// 分组子选项:由 fieldNames.options 指定分组字段(此处为 items),组条目自身不可选中
const groupOptions = [
  {
    name: 'Manager',
    items: [
      { id: 'jack', name: 'Jack' },
      { id: 'lucy', name: 'Lucy' }
    ]
  },
  {
    name: 'Engineer',
    items: [
      { id: 'yiminghe', name: 'Yiminghe' },
      { id: 'yiminghe1', name: 'Yiminghe1' }
    ]
  }
]
const groupFieldNames = { label: 'name', value: 'id', options: 'items' }
const groupValue = ref<SelectProps['value']>('lucy')
</script>
<template>
  <Space>
    <Select :options="options" :field-names="fieldNames" v-model:value="selectedValue" :width="200" />
    <Select :options="groupOptions" :field-names="groupFieldNames" v-model:value="groupValue" :width="200" />
  </Space>
</template>

搜索框 ​

请输入搜索内容
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const selectedValue = ref<SelectProps['value']>()
const options = ref<SelectOption[]>([])
function onSearch(keyword: string) {
  // 用 300ms 延迟模拟接口返回;未输入时返回空数组(配合 not-found-content 为 null,面板不展开)
  setTimeout(() => {
    options.value = keyword
      ? ['北京', '上海', '广州'].map((city, index) => ({ value: `${keyword}-${index}`, label: `${keyword} · ${city}` }))
      : []
  }, 300)
}
</script>
<template>
  <Select
    v-model:value="selectedValue"
    show-search
    placeholder="请输入搜索内容"
    :width="200"
    :default-active-first-option="false"
    :show-arrow="false"
    :filter-option="false"
    :not-found-content="null"
    :options="options"
    @search="onSearch"
  />
</template>

带搜索框 ​

请选择
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { value: 'jack', label: 'Jack' },
  { value: 'lucy', label: 'Lucy' },
  { value: 'tom', label: 'Tom' }
])
const selectedValue = ref<SelectProps['value']>()
// 默认按 value 字段过滤,可传入函数自定义过滤逻辑
function filterOption(input: string, option: SelectOption) {
  return String(option.value).toLowerCase().includes(input.toLowerCase())
}
function onBlur() {
  console.log('blur')
}
function onFocus() {
  console.log('focus')
}
</script>
<template>
  <Select
    v-model:value="selectedValue"
    show-search
    placeholder="请选择"
    :width="200"
    :options="options"
    :filter-option="filterOption"
    @blur="onBlur"
    @focus="onFocus"
  />
</template>

搜索用户 ​

远程搜索 + 节流控制 + 请求时序控制 + 加载状态的多选示例(示例数据由本地模拟,可输入姓名或账号搜索)


请选择用户
Show Code
vue
<script setup lang="ts">
import { onUnmounted, ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([])
const selectedValue = ref<SelectProps['value']>([])
const fetching = ref(false)
// 示例用本地数据模拟用户库,实际场景替换为接口请求
const pool = [
  { name: '张伟', account: 'zhangwei' },
  { name: '王芳', account: 'wangfang' },
  { name: '李娜', account: 'lina' },
  { name: '刘洋', account: 'liuyang' },
  { name: '陈静', account: 'chenjing' },
  { name: '杨帆', account: 'yangfan' }
]
let fetchId = 0
let timer: ReturnType<typeof setTimeout> | null = null
function onSearch(keyword: string) {
  // 节流:连续输入只在停顿 300ms 后发起一次请求
  if (timer) clearTimeout(timer)
  timer = setTimeout(() => {
    const currentId = (fetchId += 1)
    options.value = []
    fetching.value = true
    // 模拟接口 300ms 后返回;仅最后一次请求的结果生效,避免乱序返回覆盖(请求时序控制)
    setTimeout(() => {
      if (currentId !== fetchId) return
      const lowerKeyword = keyword.toLowerCase()
      options.value = pool
        .filter((user) => user.name.includes(keyword) || user.account.includes(lowerKeyword))
        .map((user) => ({ value: user.account, label: `${user.name}(${user.account})` }))
      fetching.value = false
    }, 300)
  }, 300)
}
onUnmounted(() => {
  if (timer) clearTimeout(timer)
})
</script>
<template>
  <Select
    v-model:value="selectedValue"
    mode="multiple"
    placeholder="请选择用户"
    :filter-option="false"
    :not-found-content="fetching ? undefined : null"
    :options="options"
    :width="300"
    @search="onSearch"
  >
    <template #notFoundContent>
      <Spin size="small" />
    </template>
  </Select>
</template>

联动 ​

北京市
北京市
东城区
东城区
Show Code
vue
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
const provinceData = ['北京市', '浙江']
const cityData: Record<string, string[]> = {
  北京市: ['东城区', '西城区'],
  浙江: ['杭州市', '湖州市']
}
const province = ref(provinceData[0])
const secondCity = ref(cityData[province.value][0])
const cities = computed(() => cityData[province.value])
watch(province, (value) => {
  secondCity.value = cityData[value][0]
})
</script>
<template>
  <Space>
    <Select :options="provinceData.map((pro) => ({ value: pro }))" v-model:value="province" :width="120" />
    <Select :options="cities.map((city) => ({ value: city }))" v-model:value="secondCity" :width="120" />
  </Space>
</template>

支持清除 ​

伊斯坦布尔
伊斯坦布尔
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(5)
</script>
<template>
  <Select :options="options" v-model:value="selectedValue" allow-clear />
</template>

键盘操作 ​

聚焦后按 ↑ ↓ 移动高亮(自动跳过禁用项、到达列表端点时环形回绕并滚入可视区),按 Enter 选中,按 Esc 关闭面板;面板收起时按 ↑ ↓ 可直接展开


按键说明
↑ / ↓上下移动高亮项(自动跳过禁用项,到达列表端点时环形回绕,并随面板滚动到可视区)
Enter选中当前高亮项
Esc关闭面板,不改变当前选中值
Tab焦点移出,面板关闭
伊斯坦布尔
伊斯坦布尔
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2, disabled: true },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(5)
</script>
<template>
  <Select :options="options" v-model:value="selectedValue" />
</template>

受控展开 ​

open 受控时面板显隐由外部驱动,配合 dropdownVisibleChange 同步开合


展开面板
北京市
北京市
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(1)
const open = ref(false)
// 单按钮切换开合:取 mousedown 时刻的状态为准 ——
// 点击按钮即「点击面板外部」,组件会在 mousedown 之后派发一次关闭请求(open 被置为 false),
// 若 click 时再按当时的状态取反,就会被反转回来,表现为「按钮关不掉」
let openBeforeToggle = false
function onToggleMousedown() {
  openBeforeToggle = open.value
}
function onToggleClick() {
  open.value = !openBeforeToggle
}
</script>
<template>
  <Space>
    <Button type="primary" @mousedown="onToggleMousedown" @click="onToggleClick">
      {{ open ? '关闭面板' : '展开面板' }}
    </Button>
    <Select
      :options="options"
      v-model:value="selectedValue"
      :open="open"
      @dropdown-visible-change="open = $event"
    />
  </Space>
</template>

后缀图标 ​

Lucy
Lucy
Lucy
Lucy
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { MehOutlined, SmileOutlined } from '@ant-design/icons-vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { value: 'jack', label: 'Jack' },
  { value: 'lucy', label: 'Lucy' },
  { value: 'disabled', label: 'Disabled', disabled: true },
  { value: 'yiminghe', label: 'Yiminghe' }
])
const selectedValue = ref<SelectProps['value']>('lucy')
</script>
<template>
  <Space>
    <Select :options="options" v-model:value="selectedValue" :width="120">
      <template #suffixIcon><SmileOutlined class="select-suffix" /></template>
    </Select>
    <Select :options="options" v-model:value="selectedValue" :width="120" disabled>
      <template #suffixIcon><MehOutlined class="select-suffix" /></template>
    </Select>
  </Space>
</template>

自定义选中标识 ​

通过 menuItemSelectedIcon 插槽自定义选中项的标识图标,单选模式默认不展示


伊斯坦布尔
伊斯坦布尔
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { CheckOutlined } from '@ant-design/icons-vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const selectedValue = ref<SelectProps['value']>(5)
</script>
<template>
  <Select :options="options" v-model:value="selectedValue" :width="180">
    <template #menuItemSelectedIcon="{ isSelected }">
      <CheckOutlined v-if="isSelected" style="color: #ff6900" />
    </template>
  </Select>
</template>

定制回填内容 ​

使用 optionLabelProp 指定回填到选择框的 option 字段;optionLabel 插槽可完全自定义回填节点


China (中国)
China (中国)
China (中国)
🇨🇳 China (中国)
China (中国)
China (中国)
China (中国)
China (中国)  🇨🇳
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { value: 'china', label: 'China (中国)', icon: '🇨🇳' },
  { value: 'usa', label: 'USA (美国)', icon: '🇺🇸' },
  { value: 'japan', label: 'Japan (日本)', icon: '🇯🇵' },
  { value: 'korea', label: 'Korea (韩国)', icon: '🇰🇷' }
])
const selectedValue = ref<SelectProps['value']>('china')
const multipleValue = ref<SelectProps['value']>(['china'])
</script>
<template>
  <Flex vertical gap="middle" align="start">
    <!-- 单选:optionLabelProp 指定回填字段 vs optionLabel 插槽完全自定义回填节点 -->
    <Space>
      <Select :options="options" v-model:value="selectedValue" option-label-prop="label" :width="220">
        <template #option="{ icon, label }">{{ icon }} {{ label }}</template>
      </Select>
      <Select :options="options" v-model:value="selectedValue" :width="220">
        <template #option="{ icon, label }">{{ icon }} {{ label }}</template>
        <template #optionLabel="option">{{ option.icon }} {{ option.label }}</template>
      </Select>
    </Space>
    <!-- 多选:optionLabelProp 指定字段 + tagRender 自定义标签 -->
    <Space>
      <Select
        :options="options"
        v-model:value="multipleValue"
        mode="multiple"
        placeholder="select one country"
        option-label-prop="label"
        :width="320"
      >
        <template #option="{ icon, label }">{{ icon }} {{ label }}</template>
      </Select>
      <Select
        :options="options"
        v-model:value="multipleValue"
        mode="multiple"
        placeholder="select one country"
        :width="320"
      >
        <template #option="{ icon, label }">{{ icon }} {{ label }}</template>
        <template #tagRender="{ label, closable, onClose, option }">
          <Tag :closable="closable" color="volcano" style="margin: 2px 3px 2px 0" @close="onClose">
            {{ label }}&nbsp;&nbsp;{{ option.icon }}
          </Tag>
        </template>
      </Select>
    </Space>
  </Flex>
</template>

下拉面板弹出位置 ​

topLeft
topRight
bottomLeft
bottomRight
伊斯坦布尔
伊斯坦布尔
伊斯坦布尔
伊斯坦布尔
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const placement = ref<SelectProps['placement']>('bottomLeft')
const placementRadios = [
  { label: 'topLeft', value: 'topLeft' },
  { label: 'topRight', value: 'topRight' },
  { label: 'bottomLeft', value: 'bottomLeft' },
  { label: 'bottomRight', value: 'bottomRight' }
]
const placementValue = ref<SelectProps['value']>(5)
</script>
<template>
  <Space vertical>
    <Radio :options="placementRadios" v-model:value="placement" button button-style="solid" />
    <Space align="center" :size="24">
      <Select
        :options="options"
        v-model:value="placementValue"
        :placement="placement"
        :dropdown-match-select-width="200"
      />
      <Select
        :options="options"
        v-model:value="placementValue"
        :placement="placement"
        :dropdown-match-select-width="200"
        option-filter-prop="label"
        show-search
        allow-clear
      />
    </Space>
  </Space>
</template>

下拉面板宽度 ​

dropdownMatchSelectWidth 为 true 时与触发器等宽,为数字时指定面板宽度,为 false 时按内容自适应


选项 1
选项 1
选项 1
选项 1
短
短
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '短', value: 1 },
  { label: '中等长度选项', value: 2 },
  { label: '一段明显更长的选项文本', value: 3 }
])
const longOptions = ref<SelectOption[]>(
  [...Array(10)].map((_, index) => ({ label: `选项 ${index + 1}`, value: index + 1 }))
)
const selectedValue = ref<SelectProps['value']>(1)
</script>
<template>
  <Space :size="40">
    <Select :options="longOptions" v-model:value="selectedValue" :width="160" />
    <Select :options="longOptions" v-model:value="selectedValue" :width="160" :dropdown-match-select-width="240" />
    <Select :options="options" v-model:value="selectedValue" :width="160" :dropdown-match-select-width="false" />
  </Space>
</template>

下拉面板挂载容器 ​

不传 to 时面板优先挂到最近的承载层内容容器(Modal / Drawer / Dialog 卡片或上层浮层面板),无承载层时为 body;设为 false 时面板留在原地


选项 1
选项 1
选项 1
选项 1
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>(
  [...Array(10)].map((_, index) => ({ label: `选项 ${index + 1}`, value: index + 1 }))
)
const selectedValue = ref<SelectProps['value']>(1)
</script>
<template>
  <Space>
    <Select :options="options" v-model:value="selectedValue" :width="200" />
    <Select :options="options" v-model:value="selectedValue" :to="false" :width="200" />
  </Space>
</template>

下拉面板数 ​

maxDisplay 按项数限制面板高度(默认展示 8 项),listHeight 直接指定像素高度


选项 1
选项 1
选项 1
选项 1
选项 1
选项 1
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>(
  [...Array(10)].map((_, index) => ({ label: `选项 ${index + 1}`, value: index + 1 }))
)
const selectedValue = ref<SelectProps['value']>(1)
</script>
<template>
  <Space :size="40">
    <Select :options="options" v-model:value="selectedValue" />
    <Select :options="options" v-model:value="selectedValue" :max-display="4" />
    <Select :options="options" v-model:value="selectedValue" :list-height="160" />
  </Space>
</template>

下拉面板滚动条 ​

通过 scrollbarProps 定制面板内滚动条


选项 1
选项 1
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>(
  [...Array(10)].map((_, index) => ({ label: `选项 ${index + 1}`, value: index + 1 }))
)
const selectedValue = ref<SelectProps['value']>(1)
</script>
<template>
  <Select :options="options" v-model:value="selectedValue" :scrollbar-props="{ size: 8, delay: 2000 }" />
</template>

自定义下拉面板 ​

通过 popupClassName 自定义面板类名、dropdownMenuStyle 设置面板样式,两者均落在 Teleport 后的面板上,需写在全局样式中;zIndex 用于覆盖面板层级(默认 1050)


北京市
北京市
北京市
北京市
北京市
北京市
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps, SelectOption } from 'vue-amazing-ui'
const options = ref<SelectOption[]>([
  { label: '北京市', value: 1 },
  { label: '上海市', value: 2 },
  { label: '纽约市', value: 3 },
  { label: '旧金山', value: 4 },
  { label: '伊斯坦布尔', value: 5 },
  { label: '君士坦丁堡', value: 6 }
])
const customPanelStyle = {
  background: 'rgba(255, 105, 0, 0.05)',
  border: '1px solid #ff6900',
  borderRadius: '12px',
  boxShadow: '0 8px 20px rgba(255, 105, 0, 0.25)'
}
const selectedValue = ref<SelectProps['value']>(1)
const zIndexValue = ref<SelectProps['value']>(1)
</script>
<template>
  <Space>
    <Select :options="options" v-model:value="selectedValue" :width="180" />
    <Select
      :options="options"
      v-model:value="selectedValue"
      :width="180"
      popup-class-name="custom-select-panel"
      :dropdown-menu-style="customPanelStyle"
    />
    <Select :options="options" v-model:value="zIndexValue" :width="180" :z-index="1100" />
  </Space>
</template>

<style lang="less">
/* 面板经 Teleport 挂载,scoped 样式无法命中,故用 popupClassName 下发类名 + 全局样式;
   选项规则把类名重复一次以提升特异性,覆盖带 scope 属性的组件内规则 */
@demo-primary: #ff6900;

.custom-select-panel {
  &.custom-select-panel .select-options-panel .select-option {
    color: darken(@demo-primary, 12%);
    font-weight: 500;
  }
  .select-options-panel .select-option.option-hover {
    background: fade(@demo-primary, 10%);
  }
  .select-options-panel .select-option.option-selected {
    color: darken(@demo-primary, 12%);
    background: fade(@demo-primary, 16%);
  }
}
</style>

扩展菜单 ​

使用 dropdownRender 对下拉菜单自由扩展,menuNode 为内置菜单节点


custom dropdown render
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import type { SelectProps } from 'vue-amazing-ui'
const items = ref(['jack', 'lucy'])
const selectedValue = ref<SelectProps['value']>()
const name = ref('')
let index = 0
function addItem() {
  items.value = [...items.value, name.value || `New item ${(index += 1)}`]
  name.value = ''
}
</script>
<template>
  <Select
    v-model:value="selectedValue"
    placeholder="custom dropdown render"
    :width="300"
    :options="items.map((item) => ({ value: item }))"
  >
    <template #dropdownRender="{ menuNode }">
      <component :is="menuNode" />
      <Divider style="margin: 4px 0" />
      <Space style="padding: 4px 8px">
        <Input v-model:value="name" placeholder="Please enter item" />
        <Button type="text" @click="addItem">
          <template #icon><PlusOutlined /></template>
          Add item
        </Button>
      </Space>
    </template>
  </Select>
</template>

大数据 ​

virtual 默认开启,只渲染可视区选项;关闭开关后渲染全部 10 万项,可对比两者的滚动表现(页面会明显变慢)


virtual
重庆 000010, 上海 000012
重庆 000010上海 000012
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import type { SelectProps } from 'vue-amazing-ui'
// 10 万项数据:label 用「城市 + 6 位序号」而非裸字符串,滚动时更易辨识
const cities = ['北京', '上海', '广州', '深圳', '杭州', '成都', '武汉', '西安', '南京', '重庆']
const options = Array.from({ length: 100000 }, (_, index) => ({
  value: `item-${index + 1}`,
  label: `${cities[index % cities.length]} ${String(index + 1).padStart(6, '0')}`
}))
const value = ref<SelectProps['value']>(['item-10', 'item-12'])
const virtual = ref(true)
</script>
<template>
  <Flex vertical gap="middle" align="start">
    <Flex gap="small" align="center">
      <Switch v-model:value="virtual" />
      <code>virtual</code>
    </Flex>
    <Select
      v-model:value="value"
      mode="multiple"
      placeholder="Please select"
      :options="options"
      :virtual="virtual"
      :width="300"
    />
  </Flex>
</template>

空数据 ​

选项为空时展示 notFoundContent,传 null 时不展开面板


默认空态
请选择
不展开面板
Show Code
vue
<script setup lang="ts">
import type { SelectOption } from 'vue-amazing-ui'
const options: SelectOption[] = []
</script>
<template>
  <Space>
    <Select :options="options" :width="180" placeholder="默认空态" />
    <Select :options="options" :width="180" not-found-content="暂时没有数据" />
    <Select :options="options" :width="180" :not-found-content="null" placeholder="不展开面板" />
  </Space>
</template>

APIs ​

Select ​

参数说明类型默认值
value
v-model
当前选中的 option 条目值,mode 为 multiple / tags 时为数组;labelInValue 开启时为 { label, value, key, originLabel } 对象number | string | SelectLabeledValue | (number | string | SelectLabeledValue)[]undefined
open是否展开下拉菜单(受控,不传时由组件内部维护)booleanundefined
searchValue搜索文本(受控,配对 update:searchValue)stringundefined
options选项数据SelectOption[][]
fieldNames选项的文本 / 值字段名配置,options 为分组子选项的字段名{ label?: string, value?: string, options?: string }{ label: 'label', value: 'value', options: 'options' }
mode设置多选模式,'multiple' 为多选,'tags' 为标签(可输入并创建新条目),不传为单选'multiple' | 'tags'undefined
labelInValue是否把每个选项的 label 包装到 value 中,value 由原始值变为 { label, value, key, originLabel } 对象booleanfalse
optionLabelProp回填到选择框的 option 属性值,未指定时取 label 字段stringundefined
width选择器宽度,单位 pxstring | number'auto'
height选择器高度,单位 pxnumberundefined
size选择器大小'small' | 'middle' | 'large''middle'
placeholder默认占位文本string'请选择'
bordered是否有边框booleantrue
status设置校验状态'error' | 'warning'undefined
disabled是否禁用booleanfalse
loading是否处于加载状态,展开面板时后缀图标变为加载中booleanfalse
allowClear是否支持清除booleanfalse
clearIcon自定义清除图标VNode | (() => VNode)undefined
suffixIcon自定义的选择框后缀图标VNode | (() => VNode)undefined
showArrow是否显示下拉小箭头booleanundefined
menuItemSelectedIcon自定义当前选中的条目图标VNode | (() => VNode)undefined
showSearch是否支持搜索;未指定时多选(multiple / tags)默认开启,单选默认关闭booleanundefined
optionFilterProp搜索时过滤对应的 option 属性,不支持 childrenstringundefined
filterOption根据输入项进行筛选:
  • 默认为 true 时,筛选每个选项 optionFilterProp 字段(未传时为 value)是否包含输入项,包含返回 true,反之返回 false
  • 当其为 false 时不筛选,显示全部选项(常用于远程搜索)
  • 当其为函数 Function 时,接受 inputValue option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false
  • boolean | ((inputValue: string, option: SelectOption) => boolean)true
    filterSort搜索时对筛选结果项的排序函数(optionA: SelectOption, optionB: SelectOption) => numberundefined
    maxTagCount最多显示多少个 tag,超出后折叠为省略提示;设为 'responsive' 时按容器宽度自动折叠(有性能消耗,不建议在大表单场景下使用)number | 'responsive'undefined
    maxTagPlaceholder隐藏 tag 时显示的内容,为函数时接收被折叠的选项数组string | VNode | ((omittedValues: SelectOption[]) => VNode)undefined
    maxTagTextLengthtag 上显示文本的最大长度,超出部分以 ... 截断numberundefined
    tagRender自定义 tag 的渲染内容,作用域参数含 label(已按 maxTagTextLength 截断)/ value / disabled / closable / onClose / option(params: { label: unknown, value?: string | number, disabled: boolean, closable: boolean, onClose: (e?: MouseEvent) => void, option: SelectOption }) => VNodeundefined
    removeIcon自定义 tag 的移除图标VNode | (() => VNode)undefined
    tokenSeparators自动分词的分隔符,输入命中后按分隔符拆分并直接选中string[][]
    autoClearSearchValue多选模式下选中项后是否清空搜索框booleantrue
    autofocus是否自动获取焦点booleanfalse
    defaultOpen是否默认展开下拉菜单(非受控)booleanfalse
    defaultActiveFirstOption是否默认高亮第一个选项booleantrue
    firstActiveValue默认高亮的选项string | number | (string | number)[]undefined
    dropdownRender自定义下拉框内容,作用域参数 menuNode 为内置菜单节点(params: { menuNode: () => VNode[] }) => VNodeundefined
    notFoundContent当下拉列表为空时显示的内容,传 null 时不展开空面板string | VNode | nullundefined
    maxDisplay下拉面板最多能展示的项数,超过后滚动显示number8
    listHeight下拉面板滚动高度,单位 px(未传时回落 maxDisplay × 32)numberundefined
    virtual是否开启虚拟滚动,大数据量时仅渲染可视区选项(dropdownMatchSelectWidth 为 false 时自动关闭)booleantrue
    listItemHeight虚拟滚动的列表项高度,单位 px,需与选项实际行高一致(自定义选项高矮时调整)number32
    scrollbarProps下拉面板滚动条 scrollbar 组件属性配置,参考 Scrollbar PropsScrollbarProps{}
    placement下拉面板弹出位置'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight''bottomLeft'
    flip下拉面板被浏览器窗口或最近可滚动父元素遮挡时自动调整弹出位置booleantrue
    to下拉面板挂载的容器节点:显式传入时按此挂载(元素标签名 (例如 'body') 或元素本身,false 会待在原地);不传时优先挂到最近的承载层内容容器(Modal / Drawer / Dialog 卡片或上层浮层面板),无承载层时为 bodystring | HTMLElement | falseundefined
    popupClassName下拉面板的类名,用于自定义面板样式stringundefined
    dropdownMenuStyle下拉面板自定义样式,可覆盖定位(与 AutoComplete 的同名属性语义一致)CSSPropertiesundefined
    dropdownMatchSelectWidth下拉菜单和选择器同宽,为数字时指定下拉菜单宽度(单位 px),为 false 时按内容自适应boolean | numbertrue
    zIndex下拉面板层级,优先级最高(覆盖默认层级与 ConfigProvider 的 baseZIndex 自动分配)numberundefined
    id组件 id,用于 aria-controls / aria-activedescendant 关联,未传时内部生成stringundefined

    Option Type ​

    名称说明类型默认值
    label?选项名stringundefined
    value?选项值string | numberundefined
    disabled?是否禁用选项booleanfalse
    [propName: string]用于包含带有任意数量的其他属性,#option 插槽会透传原始数据对象anyundefined

    LabeledValue Type ​


    labelInValue 开启时 value 的元素类型(入口导出名 SelectLabeledValue):

    名称说明类型默认值
    label选项文本(子组件式写法下由默认插槽求值而来,可能为 string 或 VNode[])unknownundefined
    value选项值string | numberundefined
    key?选项唯一键,缺省时与 value 一致string | numberundefined
    originLabel?原始选项文本(子组件式写法下为默认插槽函数)unknownundefined

    SelectOption ​


    <Select> 默认插槽中的选项子组件(配置式写法请用 SelectOption 数据):

    参数说明类型默认值
    value选项值,未传时回落 <SelectOption> 的 keystring | numberundefined
    label选项文本,优先于默认插槽文本stringundefined
    disabled是否禁用该选项(<SelectOption disabled /> 亦可)booleanfalse
    [propName: string]用于包含带有任意数量的其他属性,#option 插槽会透传原始数据对象anyundefined

    SelectOptGroup ​


    <Select> 默认插槽中的分组子组件,组内书写 SelectOption:

    参数说明类型默认值
    label分组标题,优先于 #label 插槽stringundefined

    ℹ️ options 与默认插槽(子组件)同时提供时,本库以子组件为准,沿用本库「插槽优先于 prop」的统一约定。

    Events ​

    名称说明类型
    change选项值改变后的回调,多选(multiple / tags)下 value / option 为数组且第 3 参为 undefined(value: string | number | (string | number)[], option: SelectOption | SelectOption[], index: number | undefined) => void
    deselect移除已选项(tag)时回调(value: string | number, option: SelectOption) => void
    select选中选项时回调(value: string | number, option: SelectOption) => void
    clear清除时的回调() => void
    search搜索文本变化时回调(tokenSeparators 分词命中后清空输入不触发)(value: string) => void
    focus获得焦点时的回调() => void
    blur失去焦点时的回调() => void
    openChange下拉菜单展开收起的回调(与 dropdownVisibleChange 为同一事件的两个名称,同参同时派发)(open: boolean) => void
    dropdownVisibleChange下拉菜单展开收起的回调,openChange 的别名(open: boolean) => void
    popupScroll下拉列表滚动时的回调(e: Event) => void
    mouseenter鼠标移入时的回调(e: MouseEvent) => void
    mouseleave鼠标移出时的回调(e: MouseEvent) => void
    inputKeyDown输入框按下键时的回调(e: KeyboardEvent) => void
    update:searchValue搜索文本变化时同步(配合 searchValue 受控)(value: string) => void

    Slots ​

    名称说明用法
    default子组件式选项,直接书写 SelectOption / SelectOptGroup;与 options 同时提供时以此为准v-slot:default
    option自定义选项内容,作用域为当前选项数据,SelectOption 的自定义字段会一并透传v-slot:option="{ label, value, ...rest }"
    optionLabel自定义回填到选择框的内容,作用域为当前选中项数据v-slot:optionLabel="{ label, value, ...rest }"
    placeholder自定义占位内容v-slot:placeholder
    suffixIcon自定义的选择框后缀图标v-slot:suffixIcon
    clearIcon自定义清除图标,作用域参数 clear 为清除方法v-slot:clearIcon="{ clear }"
    menuItemSelectedIcon自定义当前选中的条目图标,作用域参数 isSelected 标识该项是否被选中v-slot:menuItemSelectedIcon="{ isSelected }"
    notFoundContent自定义空数据内容v-slot:notFoundContent
    dropdownRender自定义下拉框内容,作用域参数 menuNode 为内置菜单节点v-slot:dropdownRender="{ menuNode }"
    tagRender自定义 tag 的渲染内容,作用域参数同 tagRender 属性v-slot:tagRender="{ label, value, disabled, closable, onClose, option }"
    maxTagPlaceholder隐藏 tag 时显示的内容,作用域参数 omittedValues 为被折叠的选项数组(本库统一以具名参数对象传参)v-slot:maxTagPlaceholder="{ omittedValues }"
    removeIcon自定义 tag 的移除图标v-slot:removeIcon

    Methods ​

    名称说明类型
    focus获取输入框焦点() => void
    blur使输入框失去焦点() => void
    scrollTo滚动面板选项:传数字按下标定位,传对象按顶部偏移定位(arg: number | { index?: number; top?: number }) => void

    Released under the MIT License.