代码演示
如何引入
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
</script>
选项写作 <Select.Option> 与 <Select.OptGroup>,也可以使用具名导出的 SelectOption、SelectOptGroup。
基本使用
每个 Option 标签都必须声明 value 属性,Option 的默认插槽内容或 label 将会被渲染至下拉列表中
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
</script>
<template>
<Select aria-label="业务线" defaultValue="douyin" style="width: 120px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option disabled value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="业务线" defaultValue="douyin" disabled style="width: 120px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
</Select>
<br />
<br />
<Select aria-label="业务线" placeholder="请选择业务线" style="width: 120px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option disabled value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</template>以数组形式传入 Option
可以直接通过optionList传入一个对象数组,每个对象必须包含 value/label 属性(当然其他属性也可以通过此方式传入)
label 只接受字符串或数字;带标记的选项内容写在 Select.Option 的默认插槽里。
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
const list = [
{ value: 'douyin', label: '抖音', otherKey: 0 },
{ value: 'ulikecam', label: '轻颜相机', disabled: true, otherKey: 1 },
{ value: 'jianying', label: '剪映', otherKey: 2 },
{ value: 'toutiao', label: '今日头条', otherKey: 3 },
];
</script>
<template>
<Select aria-label="业务线" :optionList="list" placeholder="请选择业务线" style="width: 180px" />
</template>多选
自 v2.28后,select 的选择器会自带 maxHeight 270,内容超出后可以通过垂直滚动查看。
配置multiple属性,可以支持多选
配置 maxTagCount 可以限制已选项展示的数量,超出部分将以+N 的方式展示
配置 ellipsisTrigger (>= v2.28.0) 对溢出部分的 tag 做自适应处理,当宽度不足时,最后一个tag内容作截断处理。开启该功能后会有一定性能损耗,不推荐在大表单场景下使用
配置 expandRestTagsOnClick (>= v2.28.0) 可以在设置 maxTagCount 情况下通过点击展示全剩余的tag
使用 showRestTagsPopover (>= v2.22.0) 可以设置在超出 maxTagCount 后,hover +N 是否显示 Popover,默认为 false。并且,还可以在 restTagsPopoverProps 属性中配置 Popover。
配置 max 属性可限制最大可选的数量,超出最大限制数量后无法选中,同时会触发 exceed 事件
代码
<script setup lang="ts">
import { Select, Toast } from '@fibrous/semi-ui-vue';
const onExceed = () => Toast.warning('最多只允许选择两项');
</script>
<template>
<Select aria-label="Select 示例" :defaultValue="['douyin', 'ulikecam']" multiple style="width: 320px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" :defaultValue="['douyin', 'ulikecam', 'jianying']" :maxTagCount="2" multiple :restTagsPopoverProps="{ position: 'top' }" showRestTagsPopover style="width: 320px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" :defaultValue="['douyin']" :max="2" multiple style="width: 320px" @exceed="onExceed">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" :defaultValue="['xigua', 'ulikecam', 'jianying', 'douyin']" ellipsisTrigger expandRestTagsOnClick :maxTagCount="2" multiple :restTagsPopoverProps="{ position: 'top' }" showRestTagsPopover style="width: 220px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</template>分组
用 Select.OptGroup 进行分组(分组功能仅支持通过默认插槽声明选项使用,不支持 optionList 方式传入)。分组标题用 label 属性,带标记的标题用 #label 插槽
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
</script>
<template>
<Select aria-label="地区" filter placeholder="" style="width: 180px">
<Select.OptGroup label="Asia">
<Select.Option value="a-1">China</Select.Option>
<Select.Option value="a-2">Korea</Select.Option>
</Select.OptGroup>
<Select.OptGroup label="Europe">
<Select.Option value="b-1">Germany</Select.Option>
<Select.Option value="b-2">France</Select.Option>
</Select.OptGroup>
<Select.OptGroup label="South America">
<Select.Option value="c-1">Peru</Select.Option>
</Select.OptGroup>
</Select>
</template>代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
const data = [
{
label: 'Asia',
children: [
{ value: 'a-1', label: 'China' },
{ value: 'a-2', label: 'Korea' },
],
},
{
label: 'Europe',
children: [
{ value: 'b-1', label: 'Germany' },
{ value: 'b-2', label: 'France' },
],
},
{
label: 'South America',
children: [{ value: 'c-1', label: 'Peru' }],
},
];
</script>
<template>
<Select aria-label="地区" filter placeholder="" style="width: 180px">
<Select.OptGroup v-for="group in data" :key="group.label" :label="group.label">
<Select.Option v-for="option in group.children" :key="option.value" :label="option.label" :value="option.value" />
</Select.OptGroup>
</Select>
</template>不同尺寸
通过 Size 控制选择器的大小尺寸: small / default / large
代码
<script setup lang="ts">
import { Select, SelectOption } from '@fibrous/semi-ui-vue';
</script>
<template>
<Select aria-label="small" placeholder="请选择业务线" size="small" :style="{ width: '180px' }">
<SelectOption value="ulikecam">轻颜相机</SelectOption>
</Select>
<br />
<br />
<Select aria-label="default" placeholder="请选择业务线" :style="{ width: '180px' }">
<SelectOption value="ulikecam">轻颜相机</SelectOption>
</Select>
<br />
<br />
<Select aria-label="large" placeholder="请选择业务线" size="large" :style="{ width: '180px' }">
<SelectOption value="ulikecam">轻颜相机</SelectOption>
</Select>
</template>不同校验状态样式
validateStatus: default / warning / error
仅影响背景颜色等样式表现
代码
<script setup lang="ts">
import { Select, SelectOption } from '@fibrous/semi-ui-vue';
</script>
<template>
<Select aria-label="default" :style="{ width: '180px' }">
<SelectOption value="ulikecam">轻颜相机</SelectOption>
</Select>
<br />
<br />
<Select aria-label="warning" :style="{ width: '180px' }" validateStatus="warning">
<SelectOption value="ulikecam">轻颜相机</SelectOption>
</Select>
<br />
<br />
<Select aria-label="error" :style="{ width: '180px' }" validateStatus="error">
<SelectOption value="ulikecam">轻颜相机</SelectOption>
</Select>
</template>配置前缀、后缀、清除按钮
- 可以通过
#prefix插槽传入选择框前缀,通过#suffix插槽传入选择框后缀,可以为文本或者节点
当插槽内容为文本或者 Icon 时,会自动带上左右间隔,若为自定义节点,则左右间隔为 0
- 通过
showClear控制清除按钮是否展示 - 通过
showArrow控制右侧下拉箭头是否展示
代码
<script setup lang="ts">
import { IconGift, IconVigoLogo } from '@fibrous/semi-icons-vue';
import { Select } from '@fibrous/semi-ui-vue';
</script>
<template>
<Select aria-label="Select 示例" defaultValue="ulikecam" showClear style="width: 320px">
<template #prefix>
<IconVigoLogo />
</template>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="Select 示例" defaultValue="ulikecam" :showArrow="false" style="width: 320px">
<template #prefix>
<IconVigoLogo />
</template>
<template #suffix>
<IconGift />
</template>
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</template>在顶部/底部渲染附加项
我们在弹出层顶部、底部分别预留了插槽,当你需要在弹出层中添加自定义 node 时
可以通过innerBottomSlot或者outerBottomSlot传入,自定义 node 将会被渲染在弹出层底部;可以通过innerTopSlot或者outerTopSlot传入,自定义 node 将会被渲染在弹出层顶部。
在 Vue 中它们是同名的具名插槽,写作 #innerBottomSlot、#outerBottomSlot、#innerTopSlot、#outerTopSlot。
innerTopSlot和innerBottomSlot将会被渲染在 optionList 内部,当滚动到 optionList 顶部/底部时展现outerTopSlot和outerBottomSlot将会被渲染为与 optionList 平级,无论 optionList 是否滚动,都会始终展现
outerBottomSlot:
innerBottomSlot:
代码
<script setup lang="ts">
import { Select, SelectOption } from '@fibrous/semi-ui-vue';
const innerSlotStyle = {
backgroundColor: 'var(--color-white)',
height: '36px',
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
paddingLeft: '32px',
borderTop: '1px solid var(--semi-color-border)',
borderRadius: '0 0 6px 6px',
color: 'var(--semi-color-link)',
};
const outSlotStyle = {
backgroundColor: 'var(--semi-color-fill-0)',
height: '36px',
display: 'flex',
paddingLeft: '32px',
color: 'var(--semi-color-link)',
alignItems: 'center',
cursor: 'pointer',
borderTop: '1px solid var(--semi-color-border)',
borderRadius: '0 0 6px 6px',
};
</script>
<template>
<div>
<p>outerBottomSlot:</p>
<Select aria-label="outerBottomSlot" :autoAdjustOverflow="false" defaultOpen :dropdownStyle="{ width: '180px' }" :maxHeight="150" placeholder="自定义外侧底部slot,始终显示" position="bottom" :style="{ width: '300px' }">
<SelectOption value="douyin">抖音</SelectOption>
<SelectOption value="ulikecam">轻颜相机</SelectOption>
<SelectOption value="jianying">剪映</SelectOption>
<SelectOption value="duoshan">多闪</SelectOption>
<SelectOption value="xigua">西瓜视频</SelectOption>
<template #outerBottomSlot>
<div :style="outSlotStyle">
<span :style="{ color: 'var(--semi-color-link)' }">未找到应用?</span>
</div>
</template>
</Select>
<p :style="{ marginTop: '200px' }">innerBottomSlot:</p>
<Select aria-label="innerBottomSlot" :dropdownStyle="{ width: '180px' }" :maxHeight="150" placeholder="自定义内侧底部slot,滚动至底部显示" :style="{ width: '300px' }">
<SelectOption value="douyin">抖音</SelectOption>
<SelectOption value="ulikecam">轻颜相机</SelectOption>
<SelectOption value="jianying">剪映</SelectOption>
<SelectOption value="duoshan">多闪</SelectOption>
<SelectOption value="xigua">西瓜视频</SelectOption>
<template #innerBottomSlot>
<div :style="innerSlotStyle">点击加载更多</div>
</template>
</Select>
</div>
</template>通过 outerTopSlot 将内容插入顶部插槽
代码
<script setup lang="ts">
import { ref } from 'vue';
import { RadioGroup, Select, type RadioChangeEvent } from '@fibrous/semi-ui-vue';
interface Item {
value: string;
label: string;
}
const list: Record<string, Item[]> = {
component: [
{ value: 'select', label: '选择器' },
{ value: 'tabs', label: '标签' },
{ value: 'avatar', label: '头像' },
{ value: 'button', label: '按钮' },
],
design: [
{ value: 'color', label: '颜色' },
{ value: 'dark', label: '暗色模式' },
{ value: 'icon', label: '图标' },
{ value: 'font', label: '字体' },
],
feedback: [
{ value: 'faq', label: '常见问题' },
{ value: 'join', label: '加入用户群' },
{ value: 'hornbill', label: '犀鸟反馈问题' },
],
};
const categoryOptions = [
{ value: 'component', label: '组件' },
{ value: 'design', label: '设计' },
{ value: 'feedback', label: '反馈' },
];
const categoryWrapper = {
padding: '8px 12px',
borderBottom: '0.5px solid var(--semi-color-border)',
};
const key = ref('component');
const value = ref<Item | undefined>({ value: 'faq', label: '常见问题' });
const changeCategory = (event: RadioChangeEvent) => {
key.value = String(event.target.value);
};
</script>
<template>
<Select v-model="value" aria-label="选项" :autoAdjustOverflow="false" changeWithObject defaultOpen :optionList="list[key]" :style="{ width: '200px' }">
<template #outerTopSlot>
<!-- The radio group switches with arrow keys, so categories need no tabpanel association -->
<div :style="categoryWrapper">
<RadioGroup aria-label="选项分类" buttonSize="small" :modelValue="key" :options="categoryOptions" type="button" @change="changeCategory" />
</div>
</template>
</Select>
</template>受控组件
用 v-model(即 modelValue)绑定后 Select 为受控组件,所选中的值完全由它决定。modelValue 为 undefined 时按非受控处理。
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Select } from '@fibrous/semi-ui-vue';
const value = ref<string | undefined>('xigua');
</script>
<template>
<Select v-model="value" aria-label="业务线" placeholder="受控的Select" style="width: 300px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</template>动态修改 Options
如果需要动态更新 Options,应该使用受控的 modelValue
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Button, Select } from '@fibrous/semi-ui-vue';
const options = ref([1, 2, 3, 4]);
function add() {
const length = Math.ceil(Math.random() * 10);
options.value = Array.from({ length }, (_, i) => i + 1);
}
</script>
<template>
<Select aria-label="数字" :modelValue="4" placeholder="请选择" style="width: 180px">
<Select.Option v-for="option in options" :key="option" :value="option">{{ option }}</Select.Option>
</Select>
<br />
<br />
<Button @click="add">changeOptions Dynamic</Button>
</template>联动
使用受控的 modelValue,实现不同 Select 之间的联动。如果是带有层级关系的复杂联动建议直接使用Cascader组件
代码
<script setup lang="ts">
import { computed, ref } from 'vue';
import { Select } from '@fibrous/semi-ui-vue';
const provinces = ['四川', '广东'];
const maps: Record<string, string[]> = {
四川: ['成都', '都江堰'],
广东: ['广州', '深圳', '东莞'],
};
const province = ref(provinces[0]);
const cities = computed(() => maps[province.value]);
const city = ref<string | undefined>(cities.value[0]);
function changeProvince(newProvince?: string) {
if (!newProvince) {
return;
}
province.value = newProvince;
city.value = maps[newProvince][0];
}
</script>
<template>
<Select aria-label="省份" :modelValue="province" style="width: 150px; margin: 10px" @change="changeProvince">
<Select.Option v-for="item in provinces" :key="item" :value="item">{{ item }}</Select.Option>
</Select>
<Select v-model="city" aria-label="城市" style="width: 150px; margin: 10px">
<Select.Option v-for="item in cities" :key="item" :value="item">{{ item }}</Select.Option>
</Select>
</template>开启搜索
将 filter 置为 true,开启搜索能力。默认搜索策略将为 input 输入值与 option 的 label 值进行 include 对比
默认情况下,多选选中后会自动清空搜索关键字。若你希望保留,可以通过 autoClearSearchValue 设为 false 关闭默认行为(v2.3 后提供)
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
</script>
<template>
<Select aria-label="单选" filter placeholder="带搜索功能的单选" style="width: 180px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="多选" :autoClearSearchValue="false" filter multiple placeholder="带搜索功能的多选" style="width: 300px">
<Select.Option value="semi-0">Semi-0</Select.Option>
<Select.Option value="semi-1">Semi-1</Select.Option>
<Select.Option value="semi-2">Semi-2</Select.Option>
<Select.Option value="semi-3">Semi-3</Select.Option>
<Select.Option value="semi-4">Semi-4</Select.Option>
</Select>
</template>搜索框位置
默认搜索框展示于 Select 的 Trigger 触发器上。通过 searchPosition 可以指定不同的位置,可选 dropdown、trigger。 在 v2.61.0后提供若希望定制位于 dropdown 中的 Input 搜索框的 placeholder,可以通过 searchPlaceholder 控制
若 searchPosition 值为 trigger,当showClear=true 时,点击Trigger区域的清空按钮,将同时清空已选项以及搜索框中的文本
若 searchPosition 值为 dropdown,当showClear=true 时,点击Trigger区域清空按钮,仅清空已选项。点击搜索框中的清空按钮,仅清空搜索文本
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
</script>
<template>
<Select aria-label="单选" defaultValue="ulikecam" filter placeholder="我的搜索框在下拉菜单中" searchPlaceholder="带搜索功能的单选" searchPosition="dropdown" style="width: 200px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
<br />
<br />
<Select aria-label="多选" :autoClearSearchValue="false" :defaultValue="['semi-1']" filter multiple placeholder="我的搜索框在下拉菜单中" searchPlaceholder="带搜索功能的多选" searchPosition="dropdown" style="width: 300px">
<Select.Option value="semi-0">Semi-0</Select.Option>
<Select.Option value="semi-1">Semi-1</Select.Option>
<Select.Option value="semi-2">Semi-2</Select.Option>
<Select.Option value="semi-3">Semi-3</Select.Option>
<Select.Option value="semi-4">Semi-4</Select.Option>
</Select>
</template>远程搜索
带有远程搜索,防抖请求,加载状态的多选示例
通过filter开启搜索能力
将remote设置为 true 关闭对当前数据的筛选过滤通过动态更新optionList更新下拉菜单中的备选项
使用受控的 modelValue,通过 @search 事件获取搜索内容
代码
<script setup lang="ts">
import { ref } from 'vue';
import { debounce } from 'es-toolkit';
import { Select } from '@fibrous/semi-ui-vue';
interface Item {
value: string;
label: string;
type: number;
}
const mockSearch = (inputValue: string): Item[] => {
const length = Math.ceil(Math.random() * 100);
return Array.from({ length }, (_v, i) => {
return { value: inputValue + i, label: `相近业务 ${inputValue}${i}`, type: i + 1 };
});
};
const loading = ref(false);
const list = ref<Item[]>([
{ value: 'douyin', label: '抖音', type: 1 },
{ value: 'xingtu', label: '醒图', type: 2 },
{ value: 'jianying', label: '剪映', type: 3 },
{ value: 'toutiao', label: '今日头条', type: 4 },
]);
const value = ref<Item[]>([]);
const handleSearch = debounce((inputValue: string) => {
loading.value = true;
if (inputValue) {
const result = mockSearch(inputValue);
setTimeout(() => {
loading.value = false;
list.value = result;
}, 1000);
} else {
loading.value = false;
}
}, 1000);
</script>
<template>
<Select v-model="value" aria-label="远程搜索" changeWithObject filter :loading="loading" multiple :optionList="list" remote style="width: 300px" @search="handleSearch">
<template #emptyContent />
</Select>
</template>自定义搜索逻辑
可以将 filter 置为自定义函数,定制你想要的搜索策略
如下例子,选项 label 值都是大写,默认的检索策略是字符串 include 对比,会区分大小写。
通过传入自定义 filter 函数,检索时输入小写字母也能搜到相应内容。
代码
<script setup lang="ts">
import { Select, type SelectOptionProps } from '@fibrous/semi-ui-vue';
function searchLabel(sugInput: string, option: SelectOptionProps) {
const label = String(option.label).toUpperCase();
const sug = sugInput.toUpperCase();
return label.includes(sug);
}
</script>
<template>
<Select aria-label="业务线" :filter="searchLabel" placeholder="try douyin" style="width: 180px">
<Select.Option value="douyin">douyin</Select.Option>
<Select.Option value="ulikecam">HOTSOON</Select.Option>
<Select.Option value="jianying">PIPIXIA</Select.Option>
<Select.Option value="xigua">XIGUA</Select.Option>
</Select>
</template>自定义已选项标签渲染
默认情况下,选中选项后会将选项的 label 或默认插槽的内容回填到选择框中
但你可以通过作用域插槽自定义选择框中已选项的渲染结构,参数为 { option, index, disabled, onClose }
#selectedItem:单选时替换选择框中显示的内容;多选时只替换内置标签里的内容,标签仍带有背景色以及关闭按钮#tag:多选时替换整个标签,同时提供时优先于#selectedItem- 调用
onClose移除该选项,单选时清空选中值
shenyue@example.com代码
<script setup lang="ts">
import { Avatar, Select, Tag } from '@fibrous/semi-ui-vue';
interface ListItem {
name: string;
email: string;
avatar: string;
}
const list: ListItem[] = [
{
name: '夏可漫',
email: 'xiakeman@example.com',
avatar: '/assets/placeholder/avatar-a.svg',
},
{
name: '申悦',
email: 'shenyue@example.com',
avatar: '/assets/root-web-sites/bag.jpeg',
},
{
name: '曲晨一',
email: 'quchenyi@example.com',
avatar: '/assets/placeholder/avatar-c.svg',
},
{
name: '文嘉茂',
email: 'wenjiamao@example.com',
avatar: '/assets/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png',
},
];
const onChange = (value: unknown) => console.log(value);
const optionStyle = {
display: 'flex',
paddingLeft: '24px',
paddingTop: '10px',
paddingBottom: '10px',
};
</script>
<template>
<Select aria-label="单选" defaultValue="申悦" placeholder="请选择" style="width: 280px; height: 40px" @change="onChange">
<template #selectedItem="{ option }">
<div style="display: flex; align-items: center">
<Avatar alt="" size="small" :src="option.avatar" />
<span style="margin-left: 8px">{{ option.email }}</span>
</div>
</template>
<Select.Option v-for="item in list" :key="item.email" v-bind="item" showTick :style="optionStyle" :value="item.name">
<Avatar alt="" size="small" :src="item.avatar" />
<div style="margin-left: 8px">
<div style="font-size: 14px">{{ item.name }}</div>
<div style="color: var(--color-text-2); font-size: 12px; line-height: 16px; font-weight: normal">
{{ item.email }}
</div>
</div>
</Select.Option>
</Select>
<Select aria-label="多选圆形头像" :defaultValue="['申悦', '曲晨一']" :maxTagCount="2" multiple placeholder="请选择" style="width: 280px; margin-top: 20px" @change="onChange">
<template #tag="{ option, onClose }">
<Tag :avatarSrc="option.avatar" avatarShape="circle" closable size="large" @close="onClose">
{{ option.name }}
</Tag>
</template>
<Select.Option v-for="item in list" :key="item.email" v-bind="item" showTick :style="optionStyle" :value="item.name">
<Avatar alt="" size="small" :src="item.avatar" />
<div style="margin-left: 8px">
<div style="font-size: 14px">{{ item.name }}</div>
<div style="color: var(--color-text-2); font-size: 12px; line-height: 16px; font-weight: normal">
{{ item.email }}
</div>
</div>
</Select.Option>
</Select>
<Select aria-label="多选方形头像" :defaultValue="['申悦', '曲晨一']" :maxTagCount="2" multiple placeholder="请选择" style="width: 280px; margin-top: 20px" @change="onChange">
<template #tag="{ option, onClose }">
<Tag :avatarSrc="option.avatar" avatarShape="square" closable size="large" @close="onClose">
{{ option.name }}
</Tag>
</template>
<Select.Option v-for="item in list" :key="item.email" v-bind="item" showTick :style="optionStyle" :value="item.name">
<Avatar alt="" size="small" :src="item.avatar" />
<div style="margin-left: 8px">
<div style="font-size: 14px">{{ item.name }}</div>
<div style="color: var(--color-text-2); font-size: 12px; line-height: 16px; font-weight: normal">
{{ item.email }}
</div>
</div>
</Select.Option>
</Select>
</template>自定义弹出层样式
你可以通过 dropdownClassName、dropdownStyle 控制弹出层的样式
例如当自定义弹出层的宽度时,可以通过 dropdownStyle 传入 width
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
const dropdownStyle = { width: '250px' };
</script>
<template>
<Select aria-label="业务线" :dropdownStyle="dropdownStyle" dropdownClassName="test" placeholder="自定义弹出层样式的" style="width: 180px">
<Select.Option value="douyin">抖音</Select.Option>
<Select.Option value="ulikecam">轻颜相机</Select.Option>
<Select.Option value="jianying">剪映</Select.Option>
<Select.Option value="xigua">西瓜视频</Select.Option>
</Select>
</template>获取选项的其他属性
默认情况下 change 事件只能拿到 value,如果需要拿选中节点的其他属性,可以使用 changeWithObject 属性(它是布尔属性,不是事件)
此时 change 与 update:modelValue 的载荷将会是 object,包含 option 的各种属性,例如 { value, label, ...rest }
onChange回调:
onChange回调:
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Select, TextArea } from '@fibrous/semi-ui-vue';
interface Item {
value: string;
label: string;
type: number;
}
const list: Item[] = [
{ value: 'douyin', label: '抖音', type: 1 },
{ value: 'ulikecam', label: '轻颜相机', type: 2 },
{ value: 'jianying', label: '剪映', type: 3 },
{ value: 'toutiao', label: '今日头条', type: 4 },
];
const cbValue = ref<Item>();
const multipleCbValue = ref<Item[]>();
// Select infers its value type from the value, not from optionList, so an empty typed default gives change Item[]
const noItems: Item[] = [];
const onChange = (value?: Item) => {
cbValue.value = value;
console.log(value);
};
const onMultipleChange = (value: Item[]) => {
multipleCbValue.value = value;
console.log(value);
};
</script>
<template>
<div>
<div>
<Select aria-label="单选" changeWithObject :defaultValue="list[0]" :optionList="list" placeholder="单选" style="width: 150px" @change="onChange" />
<h4 id="select-on-change-with-object-single">onChange回调:</h4>
<TextArea aria-labelledby="select-on-change-with-object-single" autosize :modelValue="JSON.stringify(cbValue)" :rows="2" style="width: 320px; margin-bottom: 48px" />
</div>
<div>
<Select aria-label="多选" changeWithObject :defaultValue="noItems" multiple :optionList="list" placeholder="多选" style="width: 320px" @change="onMultipleChange" />
<h4 id="select-on-change-with-object-multiple">onChange回调:</h4>
<TextArea aria-labelledby="select-on-change-with-object-multiple" autosize :modelValue="JSON.stringify(multipleCbValue)" style="width: 320px" />
</div>
</div>
</template>创建条目
设置allowCreate,可以创建并选中选项中不存在的条目
允许通过 #createItem="{ inputValue, focus, style }" 作用域插槽自定义创建标签时的内容显示(注意你需要自定义样式,与虚拟化结合使用时必须把 style 用在自定义元素上),不提供时显示语言包中的「创建」加输入内容。创建选项时触发 create 事件
可以配合defaultActiveFirstOption属性使用,自动选中第一项,当输入完内容直接回车时,可立即创建
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
const optionList = [
{ value: 'douyin', label: '抖音' },
{ value: 'ulikecam', label: '轻颜相机' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
</script>
<template>
<Select allowCreate aria-label="业务线" defaultActiveFirstOption filter multiple :optionList="optionList" style="width: 400px" @change="(value) => console.log(value)" />
<br />
<br />
<Select allowCreate aria-label="业务线" defaultActiveFirstOption filter multiple :optionList="optionList" placeholder="With renderCreateItem" style="width: 400px" @change="(value) => console.log(value)">
<template #createItem="{ inputValue, style }">
<div :style="[{ padding: '10px' }, style]">Create Item:{{ inputValue }}</div>
</template>
</Select>
</template>虚拟化
传入virtualize时开启列表虚拟化,用于大量 Option 节点的情况优化性能
virtualize 是一个包含下列值的对象:
- height: Option 列表高度值,默认 270 (v2.20.8 前为 300)
- width: Option 列表宽度值,默认 100%
- itemSize: 每行 Option 的高度,必传
代码
<script setup lang="ts">
import { Select } from '@fibrous/semi-ui-vue';
const options = Array.from({ length: 3000 }, (_, i) => ({ label: `option-${i}`, value: i }));
const virtualize = {
height: 270,
width: '100%',
itemSize: 36,
};
</script>
<template>
<Select aria-label="virtualize" filter :optionList="options" placeholder="拥有3k个Option的Select" :style="{ width: '260px' }" :virtualize="virtualize" />
</template>自定义触发器
如果 Select 默认的触发器样式满足不了你的需求,可以用 #trigger 作用域插槽自定义选择框的内容
如果想保留搜索筛选能力,又不希望自己渲染 Input 相关的结构,可以同时通过 searchPosition='dropdown',将默认的搜索框置于下拉列表中
#trigger 的参数如下
interface SelectTriggerSlotProps {
value: SelectOptionProps[]; // 当前所有已选中的 option
inputValue: string; // 当前搜索关键字
placeholder?: string; // Select 的 placeholder
disabled: boolean; // 是否禁用 Select
componentProps: Record<string, unknown>; // 所有传给 Select 的 props
onSearch: (inputValue: string) => void; // 更新搜索关键字,用于把自定义 Input 的值同步给 Select 内部。需同时设置 filter
onClear: (event?: MouseEvent) => void; // 清空已选值。绑在触发器内的控件上时传入点击事件,避免这次点击重新打开下拉列表
onRemove: (option: SelectOptionProps) => void; // 移除单个已选项,option 至少需带有 label、value
}
不同背景色的触发器
使用 circle Tag 作为触发器
代码
<script setup lang="ts">
import { ref } from 'vue';
import { IconAppCenter, IconChevronDown } from '@fibrous/semi-icons-vue';
import { Select, Tag } from '@fibrous/semi-ui-vue';
const valList = ref(['douyin', 'ulikecam']);
const list = [
{ value: 'douyin', label: '抖音' },
{ value: 'ulikecam', label: '轻颜相机' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
</script>
<template>
<div>
<h4>不同背景色的触发器</h4>
<Select v-model="valList" aria-label="不同背景色的触发器" filter multiple :optionList="list" searchPosition="dropdown" :style="{ width: '240px' }">
<template #trigger="{ value }">
<div
:style="{
minWidth: '112px',
backgroundColor: 'var(--semi-color-primary-light-default)',
height: '32px',
display: 'flex',
alignItems: 'center',
paddingLeft: '12px',
borderRadius: '3px',
color: 'var(--semi-color-primary)',
}"
>
<div :style="{ fontWeight: 600, flexShrink: 0, fontSize: '14px' }">业务线</div>
<div
:style="{
margin: '4px',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
flexGrow: 1,
overflow: 'hidden',
}"
>
{{ value.map((item) => item.label).join(' , ') }}
</div>
<IconAppCenter :style="{ marginRight: '8px', flexShrink: 0 }" />
</div>
</template>
</Select>
<br />
<br />
<h4>使用 circle Tag 作为触发器</h4>
<Select v-model="valList" aria-label="circle Tag 触发器" filter multiple :optionList="list" searchPosition="dropdown" :style="{ width: '240px', marginTop: '20px', outline: 0 }">
<template #trigger="{ value }">
<div
:style="{
margin: '4px',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
flexGrow: 1,
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
}"
>
<Tag color="cyan" shape="circle" size="large">
{{ value.map((item) => item.label).join(' / ') }}
<template #suffixIcon>
<IconChevronDown />
</template>
</Tag>
</div>
</template>
</Select>
</div>
</template>下例是更复杂的例子:复用了 TagInput 拖拽排序能力,通过自定义触发器为 Select 增加排序
可对已选项拖拽重新排序的 Select
douyin,ulikecam
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Select, TagInput } from '@fibrous/semi-ui-vue';
const valList = ref(['douyin', 'ulikecam']);
const inputVal = ref('');
const list = [
{ value: 'douyin', label: '抖音' },
{ value: 'ulikecam', label: '轻颜相机' },
{ value: 'jianying', label: '剪映' },
{ value: 'toutiao', label: '今日头条' },
];
const handleSort = (currentLabels: string[]) => {
valList.value = currentLabels.flatMap((item) => list.find((i) => i.label === item)?.value ?? []);
};
</script>
<template>
<h4>可对已选项拖拽重新排序的 Select</h4>
<Select v-model="valList" aria-label="可拖拽排序的 Select" filter multiple :optionList="list" :style="{ width: '240px' }">
<template #trigger="{ value, onSearch }">
<div role="presentation" @keydown.stop>
<TagInput
:allowDuplicates="false"
draggable
:inputValue="inputVal"
:modelValue="value.map((item) => String(item.label))"
showClear
@change="handleSort"
@inputChange="
(word) => {
onSearch(word);
inputVal = word;
}
"
/>
</div>
</template>
</Select>
</template>自定义候选项渲染
- 简单的自定义:把带标记的内容写在
Select.Option的默认插槽里,你可以控制候选项的渲染,此时内容会自动带上内边距、背景色等样式 - 完全自定义:通过
#optionItem作用域插槽,你可以完全接管列表中候选项的渲染,并且从插槽参数中,获取到相关的状态值。实现更高自由度的结构渲染
参数为{ id, value, label, selected, focused, disabled, inputValue, class, style, onClick, onMouseenter },注意事项:- 参数中的
style需在 wrapper dom 上进行消费,否则在虚拟化场景下会无法正常使用 - 参数中的
class、onMouseenter需在 wrapper dom 上进行消费,否则上下键盘操作时显示会有问题 - 选中(selected)、聚焦(focused)、禁用(disabled)等状态的样式需自行加上,你可以从参数中获取到相对的 boolean 值
- 点击选中需要把参数中的
onClick绑定到 wrapper dom 上 - 参数中的
id需与role="option"、aria-selected、aria-disabled一起写在 wrapper dom 上:Select 的aria-activedescendant指向该 id,读屏才能读出方向键移到的候选项;打开下拉或方向键移动时也按该 id 把候选项滚进列表可视区
- 参数中的
代码
<script setup lang="ts">
import { ref } from 'vue';
import { Checkbox, Highlight, Select } from '@fibrous/semi-ui-vue';
const inputValue = ref('');
const optionList = [
{ value: 'douyin', label: '抖音', otherKey: 0 },
{ value: 'ulikecam', label: '轻颜相机', disabled: true, otherKey: 1 },
{ value: 'jianying', label: '剪映', otherKey: 2 },
{ value: 'toutiao', label: '今日头条', otherKey: 3 },
];
</script>
<template>
<Select aria-label="单选" dropdownClassName="components-select-demo-renderOptionItem" filter :optionList="optionList" placeholder="单选" style="width: 180px" @search="inputValue = $event">
<!--
Put style on the outer DOM or the virtual list breaks; add selected/focused/disabled styles from the slot
params; bind onMouseenter and class on the outer DOM or arrow keys display wrongly; put id there so screen
readers announce the option the arrow keys reach
-->
<template #optionItem="{ id, disabled, selected, label, focused, class: className, style, onClick, onMouseenter }">
<div
:id="id"
:aria-disabled="disabled"
:aria-selected="selected"
:class="[
'custom-option-render',
{
'custom-option-render-focused': focused,
'custom-option-render-disabled': disabled,
'custom-option-render-selected': selected,
},
className,
]"
role="option"
:style="style"
@click="onClick"
@mouseenter="onMouseenter"
>
<Checkbox :modelValue="selected" />
<div class="option-right">
<Highlight :searchWords="[inputValue]" :sourceString="String(label)" />
</div>
</div>
</template>
</Select>
<br />
<br />
<Select aria-label="多选" dropdownClassName="components-select-demo-renderOptionItem" filter multiple :optionList="optionList" placeholder="多选" style="width: 320px" @search="inputValue = $event">
<template #optionItem="{ id, disabled, selected, label, focused, class: className, style, onClick, onMouseenter }">
<div
:id="id"
:aria-disabled="disabled"
:aria-selected="selected"
:class="[
'custom-option-render',
{
'custom-option-render-focused': focused,
'custom-option-render-disabled': disabled,
'custom-option-render-selected': selected,
},
className,
]"
role="option"
:style="style"
@click="onClick"
@mouseenter="onMouseenter"
>
<Checkbox :modelValue="selected" />
<div class="option-right">
<Highlight :searchWords="[inputValue]" :sourceString="String(label)" />
</div>
</div>
</template>
</Select>
</template>.components-select-demo-renderOptionItem {
.custom-option-render {
display: flex;
font-size: 14px;
line-height: 20px;
word-break: break-all;
padding-left: 12px;
padding-right: 12px;
padding-top: 8px;
padding-bottom: 8px;
color: var(--semi-color-text-0);
position: relative;
display: flex;
align-items: center;
cursor: pointer;
box-sizing: border-box;
.option-right {
margin-left: 8px;
display: inline-flex;
align-items: center;
}
&:active {
background-color: var(--semi-color-fill-1);
}
&-focused {
background-color: var(--semi-color-fill-0);
}
&-selected {
//font-weight: 700;
}
&-disabled {
color: var(--semi-color-disabled-text);
cursor: not-allowed;
}
&:first-of-type {
margin-top: 4px;
}
&:last-of-type {
margin-bottom: 4px;
}
}
}
API 参考
Select Props
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 当前选中的值,用 v-model 绑定;多选时为数组。传入后即为受控组件,值为 undefined 时按非受控处理并读取 defaultValue。 | TypeSelectValue<T> | undefined |
| defaultValue | 初始选中的值,只在挂载时读取;多选时为数组。 | TypeSelectValue<T> | semiGlobalDefault("Select", "defaultValue") |
| multiple | 是否多选。它决定 change 与 update:modelValue 的载荷类型:多选为数组,单选为单个值或 undefined。全局默认值不能设置它。 | Multiple | undefined |
| optionList | 以数组形式传入选项,每项须带 value 与 label(label 为字符串或数字),其余字段随选项记录一起回传。数组非空时优先于默认插槽;不支持分组。 | SelectOptionProps[] | semiGlobalDefault("Select", "optionList") |
| filter | 是否可搜索。传入 true 时开启搜索,默认策略为选项 label(含所在分组的标题)包含输入值;传入函数时接收输入值与选项,返回 true 表示命中。 | Typeboolean | ((inputValue: string, option: SelectOptionProps) => boolean) | undefined |
| remote | 是否开启远程搜索。开启后输入内容变化时不再在本地筛选,由使用方根据 search 事件更新 optionList。 | boolean | semiDefault("Select", "remote", false) |
| allowCreate | 是否允许创建下拉列表中不存在的条目,需配合 filter 使用。创建时触发 create 事件,条目的渲染可用 createItem 插槽定制。 | boolean | semiDefault("Select", "allowCreate", false) |
| autoClearSearchValue | 多选并开启搜索时,选中选项后是否自动清空搜索关键字。 | boolean | semiDefault("Select", "autoClearSearchValue", true) |
| autofocus | 挂载后是否自动聚焦。 | boolean | semiDefault("Select", "autofocus", false) |
| defaultOpen | 是否默认展开下拉列表。 | boolean | semiDefault("Select", "defaultOpen", false) |
| clickToHide | 下拉列表已展开时,点击选择框是否收起它。 | boolean | semiDefault("Select", "clickToHide", false) |
| defaultActiveFirstOption | 没有选中值时,打开下拉列表是否默认高亮第一个未禁用的选项(按 Enter 可直接选中)。已有选中值时总是高亮第一个已选的选项。 | boolean | semiDefault("Select", "defaultActiveFirstOption", true) |
| changeWithObject | 布尔属性而不是事件:设为 true 时,change、update:modelValue 与 modelValue、defaultValue 使用选项对象,对象须含 value 与 label。 | boolean | semiDefault("Select", "changeWithObject", false) |
| max | 最多可选几项,仅多选时生效;超出后无法继续选中并触发 exceed 事件。 | number | semiGlobalDefault("Select", "max") |
| maxTagCount | 多选时最多显示的标签数,超出的部分以 +N 显示。 | number | semiGlobalDefault("Select", "maxTagCount") |
| maxHeight | 下拉列表的最大高度,数字按像素处理,字符串原样使用。虚拟列表的 height 大于默认值时,需同时设置为相同的值,避免出现双滚动条。 | string | number | semiDefault("Select", "maxHeight", numbers.LIST_HEIGHT) |
| showRestTagsPopover | 超出 maxTagCount 后,hover 到 +N 时是否用 Popover 显示剩余的标签。 | boolean | semiDefault("Select", "showRestTagsPopover", false) |
| restTagsPopoverProps | 显示剩余标签的 Popover 的配置,可设置 position、trigger 等。 | PopoverProps | semiGlobalDefault("Select", "restTagsPopoverProps") |
| expandRestTagsOnClick | 多选并设置 maxTagCount 时,下拉列表展开期间是否显示全部标签。 | boolean | semiDefault("Select", "expandRestTagsOnClick", false) |
| ellipsisTrigger | 多选并设置 maxTagCount 时,宽度不足是否截断最后一个标签的内容。开启后有一定性能损耗,不建议在大表单中使用。 | boolean | semiDefault("Select", "ellipsisTrigger", false) |
| size | 大小,可选 small、default、large。 | Size | semiGlobalDefault("Select", "size") |
| disabled | 是否禁用。 | boolean | semiDefault("Select", "disabled", false) |
| borderless | 是否使用无边框样式。 | boolean | semiDefault("Select", "borderless", false) |
| validateStatus | 校验状态,可选 default、warning、error,只影响背景色等样式。 | "default" | "error" | "warning" | "success" | semiGlobalDefault("Select", "validateStatus") |
| placeholder | 选择框中没有选中值时显示的占位文字。 | string | semiGlobalDefault("Select", "placeholder") |
| searchPlaceholder | 搜索框在下拉列表中(searchPosition 为 dropdown)时,搜索框的占位文字。 | string | semiGlobalDefault("Select", "searchPlaceholder") |
| searchPosition | 开启搜索时搜索框的位置:trigger 在选择框内,dropdown 在下拉列表顶部。trigger 时点击选择框的清除按钮同时清空搜索文本,dropdown 时只清空已选项。 | "trigger" | "dropdown" | semiDefault("Select", "searchPosition", "trigger") |
| loading | 下拉列表是否显示加载动画。 | boolean | semiDefault("Select", "loading", false) |
| showArrow | 是否显示右侧下拉箭头。 | boolean | semiDefault("Select", "showArrow", true) |
| showClear | 是否显示清除按钮。有已选值或搜索文本且未禁用时,清除按钮出现在选择框内。 | boolean | semiDefault("Select", "showClear", false) |
| insetLabelId | insetLabel 元素的 id,combobox 的 aria-labelledby 指向它。未传时由组件生成。 | string | undefined |
| inputProps | 开启搜索时,搜索框 Input 的额外属性,监听器用 Vue 的写法如 onFocus。不要传 modelValue、defaultValue 与 onChange,否则会覆盖 Select 自身的状态与搜索处理。 | SelectInputProps | semiGlobalDefault("Select", "inputProps") |
| dropdownClassName | 下拉弹出层的 class。 | string | semiGlobalDefault("Select", "dropdownClassName") |
| dropdownStyle | 下拉弹出层的样式,例如设置 width。根元素上的 style 不会传给弹出层。 | StyleValue | semiGlobalDefault("Select", "dropdownStyle") |
| dropdownMatchSelectWidth | 下拉弹出层的最小宽度是否等于选择框。 | boolean | semiDefault("Select", "dropdownMatchSelectWidth", true) |
| position | 弹出层展开的位置,可选值同 Tooltip 的 position。 | Type"top" | "bottom" | "left" | "right" | "topLeft" | "topRight" | "leftTop" | "leftBottom" | "rightTop" | "rightBottom" | "bottomLeft" | "bottomRight" | "leftTopOver" | "rightTopOver" | "leftBottomOver" | "rightBottomOver" | semiGlobalDefault("Select", "position") |
| spacing | 弹出层与选择框的距离,单位为像素。 | number | { x: number; y: number; } | semiGlobalDefault("Select", "spacing") |
| motion | 弹出层是否使用动画。 | boolean | semiDefault("Select", "motion", true) |
| autoAdjustOverflow | 弹出层被遮挡时是否自动调整方向。 | boolean | semiDefault("Select", "autoAdjustOverflow", true) |
| stopPropagation | 是否阻止弹出层上的点击事件冒泡。 | boolean | semiDefault("Select", "stopPropagation", true) |
| virtualize | 开启列表虚拟化,用于选项很多时优化性能。itemSize 为每行高度,必传;height 默认 270,width 默认 100%。 | SelectVirtualize | semiGlobalDefault("Select", "virtualize") |
| preventScroll | 聚焦时是否阻止浏览器滚动到该元素,作用于组件内部的 focus 调用。 | boolean | semiGlobalDefault("Select", "preventScroll") |
| id | 触发器的 id,选项的 id 由它派生。未传时由组件生成,不读取全局默认值。 | string | undefined |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| update:modelValue | 选中值变化时触发,v-model 使用,载荷与 change 相同,先于 change 触发。 | Type[value: SelectChangeValue<T, Multiple>] |
| change | 选中值变化时触发。单选时载荷为值或 undefined,多选时为数组;开启 changeWithObject 后为选项对象。会遮住根元素上同名的原生事件。 | Type[value: SelectChangeValue<T, Multiple>] |
| dropdownVisibleChange | 下拉列表展开或收起时触发。组件在 KeepAlive 中停用时会关闭下拉列表并发出 false,不发出 blur。 | [visible: boolean] |
| search | 搜索关键字变化时触发,第二个参数是触发变化的事件:输入时为输入事件,点击外部关闭后清空时为原生 MouseEvent。通过 ref 的 search 或 trigger 插槽的 onSearch 调用时没有事件。 | [value: string, event?: Event | undefined] |
| select | 选项被选中时触发,value 总是选项的原始 value,option 为选项记录。会遮住根元素上同名的原生事件。 | [value: string | number, option: SelectOptionProps] |
| deselect | 取消选中时触发,仅多选时有效,value 同 select。 | [value: string | number, option: SelectOptionProps] |
| exceed | 试图选中的数量超出 max 时触发,仅多选时有效,载荷为被拒绝的选项。 | [option: SelectOptionProps] |
| create | allowCreate 开启并创建了新选项时触发,载荷为新选项记录。 | [option: SelectOptionProps] |
| clear | 点击或用键盘触发清除按钮时触发。 | [] |
| focus | 焦点进入 Select(含搜索框、清除按钮)时触发一次,焦点在其内部移动不重复触发。会遮住根元素上同名的原生事件。 | [event: FocusEvent] |
| blur | 焦点离开 Select 时触发一次;在已展开的下拉列表外按下鼠标时,载荷为该次 mousedown 事件。会遮住根元素上同名的原生事件。 | [event: FocusEvent | MouseEvent] |
| keydown | 按键事件,在 Select 处理完按键之后触发,所以在监听器中调用 preventDefault 不影响 Select 自身的处理。会遮住根元素上同名的原生事件。 | [event: KeyboardEvent] |
| listScroll | 下拉列表滚动时触发。 | [event: Event] |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| default | 选项,只接受 Select.Option 与 Select.OptGroup 作为直接子节点,v-for、v-if 与 template 可以展开。optionList 非空时不使用。 | any |
| prefix | 选择框的前缀。文本或图标会自动带上左右间隔,自定义节点则没有。 | any |
| suffix | 选择框的后缀。文本或图标会自动带上左右间隔,自定义节点则没有。 | any |
| insetLabel | 选择框内的内嵌标签,同时作为 combobox 与下拉列表的名称来源。有 prefix 时不渲染,也不作名称。 | any |
| arrowIcon | 自定义右侧下拉箭头;插槽内容为空时隐藏箭头。showClear 开启且有值时,hover 优先显示清除按钮。 | any |
| clearIcon | 自定义清除按钮的图标,showClear 为 true 时有效。 | any |
| emptyContent | 没有可选项或搜索无结果时的内容;插槽内容为空时隐藏空状态,未提供时显示语言包中的文案。 | any |
| outerTopSlot | 渲染在下拉弹出层顶部、与选项列表平级的内容,不随列表滚动,始终可见。 | any |
| innerTopSlot | 渲染在选项列表内部顶部的内容,滚动到列表顶部时可见。 | any |
| outerBottomSlot | 渲染在下拉弹出层底部、与选项列表平级的内容,不随列表滚动,始终可见。 | any |
| innerBottomSlot | 渲染在选项列表内部底部的内容,滚动到列表底部时可见。 | any |
| optionItem | 替换整个选项行。参数含 id、value、label、selected、focused、disabled、inputValue、class、style 以及 onClick、onMouseenter;class、style、onMouseenter 须写在行元素上,id 与 role、aria-selected、aria-disabled 写在同一个行元素上。 | SelectOptionItemSlotProps |
| createItem | allowCreate 开启时,用关键字创建选项的那一行的内容。参数为 inputValue、focus、style;虚拟列表下须把 style 用在自定义元素上。 | SelectCreateItemSlotProps |
| selectedItem | 自定义已选项:单选时是选择框中显示的内容,多选时是内置标签里的内容。参数为 option、index、disabled、onClose。 | SelectSelectedItemSlotProps |
| tag | 多选时替换每个标签,同时提供时优先于 selectedItem。参数同 selectedItem。 | SelectSelectedItemSlotProps |
| trigger | 替换触发器的内容。参数含已选选项 value、输入值 inputValue、placeholder、disabled、componentProps 以及 onSearch、onClear、onRemove;onClear 绑在触发器内的控件上时要传入点击事件,避免这次点击重新打开下拉列表。需要保留搜索能力时可同时设置 filter 与 searchPosition 为 dropdown。 | SelectTriggerSlotProps |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| 无 |
class、style、data-* 与原生监听器落在根元素上,style 不会传给下拉弹出层(用 dropdownStyle)。aria-label、aria-labelledby、aria-describedby、aria-errormessage、aria-invalid、aria-required 落在 combobox 上;可搜索时搜索框挂载后它就是 combobox,这些属性随之移过去。
Option Props
选项以 value 作为标识,同一个 Select 内 value 不应重复;label 可以重复
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| value | 选项的值,用来识别选项,每个选项都应声明。 | string | number | undefined |
| label | 选项的文本,为字符串或数字。显示与匹配时优先取 label,没有时取默认插槽中的纯文本,再取 value;带标记的内容写在默认插槽里,显示时优先于 label。 | string | number | undefined |
| disabled | 是否禁用。 | boolean | undefined |
| showTick | 选中时是否显示 √ 图标。 | boolean | undefined |
| empty | 供 Select 内部使用,渲染空状态行,使用方不需要设置。 | boolean | undefined |
| selected | 供 Select 内部使用,表示选项被选中,使用方不需要设置。 | boolean | undefined |
| focused | 供 Select 内部使用,表示选项被键盘或指针高亮,使用方不需要设置。 | boolean | undefined |
| current | 供 Select 与 AutoComplete 内部使用,表示 aria-activedescendant 指向该行,使用方不需要设置。 | boolean | undefined |
| inputValue | 供 Select 内部使用,纯文本 label 中需要高亮的搜索关键字,使用方不需要设置。 | string | undefined |
| semiOptionId | 供 Select 内部使用,选项行的 id,使用方不需要设置。 | string | undefined |
| prefixCls | 供 Select 与 AutoComplete 内部使用,选项行的 CSS 类名前缀,使用方不需要设置。 | string | cssClasses.PREFIX_OPTION |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| select | 供 Select 内部使用,点击未禁用的选项行时触发,载荷为选项记录与点击事件。使用方请监听 Select 的 select 事件。 | [option: SelectOptionProps, event: MouseEvent] |
| mouseenter | 供 Select 内部使用,指针进入选项行时触发,用来移动高亮。 | [event: MouseEvent] |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| default | 选项的内容。纯文本参与搜索高亮;带标记的内容原样渲染在选项行中,选中后也原样回填到选择框,此时搜索按 label 匹配,没有 label 时按 value。 | any |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| 无 |
Select.Option 在 Select 的默认插槽里只被读取,它的属性成为选项记录。class、style、data-* 落在选项行上;其他未声明的属性不会进入 DOM,随选项记录在 select 事件,以及开启 changeWithObject 后的 change 载荷中回传。
OptGroup Props
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| label | 分组标题的文本,为字符串或数字,也参与默认搜索;带标记的标题使用 label 插槽。 | string | number | undefined |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| 无 |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| default | 分组内的选项,只接受 Select.Option 作为直接子节点。 | any |
| label | 自定义分组标题,优先于 label 属性。 | any |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| 无 |
class、style 等 attrs 落在分组标题元素上。
Methods
Select 通过模板 ref 暴露下列方法,类型为 SelectHandle:
| 方法 | 说明 |
|---|---|
| open() | 手动展开下拉列表 |
| close() | 手动关闭下拉列表 |
| focus() | 手动聚焦:可搜索时聚焦搜索框并展开下拉列表,否则聚焦触发器,不展开 |
| clearInput() | 手动清空搜索框的值 |
| selectAll() | 选中所有可见且未禁用的选项,仅多选时有效 |
| deselectAll() | 清空所有已选项 |
| search(value: string, event?: Event) | 按输入了该值来搜索,该值会被置给搜索框;不传事件时 search 事件的第二个参数为 undefined |
| rePosition() | 按触发器当前位置立即重新计算下拉层位置;下拉层已随滚动、窗口尺寸变化、触发器移动以及触发器或列表的尺寸变化自动重新定位,一般无需调用;跟随触发器移动会晚一帧,需要同一帧内对齐时调用 |
<script setup lang="ts">
import { useTemplateRef } from 'vue';
import { Select } from '@fibrous/semi-ui-vue';
import type { SelectHandle } from '@fibrous/semi-ui-vue';
const select = useTemplateRef<SelectHandle>('select');
const open = () => select.value?.open();
</script>
<template>
<Select ref="select" />
</template>
Accessibility
ARIA
- 按 ARIA 1.2 的 combobox 写法,任一时刻只有一个元素是 combobox,弹出层的 role 为 listbox,可选项的 role 为 option:
- 不可搜索,或搜索框在下拉内(
searchPosition="dropdown")时,Select trigger 是 select-only combobox,选中的内容是它的值 - 搜索框在 trigger 内时,搜索框本身是 combobox,带
aria-autocomplete="list",trigger 不再有角色和 Tab 停靠点。单选的搜索框只在搜索时出现,未搜索时 trigger 仍是 combobox;搜索框在输入前为空,已选文本以aria-describedby关联为它的描述。多选的搜索框一直存在 - 下拉内的搜索框保持文本框角色,带
aria-controls、aria-autocomplete、aria-activedescendant,名称与 combobox 相同
- 不可搜索,或搜索框在下拉内(
- combobox 的名称按优先级取:
aria-labelledby、aria-label、实际渲染出来的 insetLabel(combobox 的aria-labelledby指向它,未传insetLabelId时组件生成 id;有 prefix 时 insetLabel 不渲染,也不作名称),都没有时为语言包中的「选择」。建议传入与表单标签一致的名称。listbox 与下拉内的搜索框使用同一个名称来源,不指向 combobox 自身。选中的内容是 combobox 的值,不计入名称 - combobox 具有 aria-haspopup、aria-expanded、aria-controls 属性,表示它与弹出层的关系
- 多选时,listbox aria-multiselectable 为 true,表示当前可以多选
- Option 选中时,aria-selected 为 true;当 Option 禁用时,aria-disabled 为 true
- 带 label 的 OptGroup 中的选项位于
role="group"内,以分组标题为名称(aria-labelledby),读屏会读出选项所属的分组;虚拟列表不显示分组标题,不包分组 - 下拉打开时,属性 aria-activedescendant 指向键盘聚焦的 option,保证在朗读旁白时识别到当前的选择的 option;完全自定义候选项渲染时请把参数中的
id写在 option 元素上(更多用法请参考Managing Focus in Composites Using aria-activedescendant)
键盘和焦点
不带 Filter 功能,或搜索框在下拉内(searchPosition="dropdown")的 Select:
- Select 聚焦后,键盘用户可以通过
空格、上箭头、下箭头(含Alt+下箭头)或Enter键打开下拉菜单。已有选中值时焦点落在第一个已选的选项上,直接按 Enter 不改变值;没有选中值时保留当前高亮,初始高亮为第一个未禁用的选项(defaultActiveFirstOption默认为 true) Home、End打开下拉菜单,并把焦点移到第一个、最后一个可用选项- 输入字符打开下拉菜单,并把焦点移到下一个以该字符开头的选项;快速连续输入时按整串匹配,重复输入同一字符时在以它开头的选项间轮换
- 当下拉菜单打开时:
- 使用
Esc键或Tab键可以关闭菜单 - 使用
上箭头或下箭头可以切换选项,Home、End移到首尾,输入字符移到以它开头的选项 - 被聚焦的选项可以通过
Enter或空格选中:单选选中后收起面板,多选切换选中状态并保持打开。快速连续输入字符的过程中,空格计入匹配的字符串,不选中选项。焦点在下拉内的搜索框时,Home、End、空格与字符用于编辑搜索内容,不移动选项焦点
- 使用
- 多选时焦点在已选标签上,按键只作用于标签:
Backspace、Delete删除标签,Enter、空格不打开下拉菜单、不改变选中值 - 当焦点在下拉菜单中,且用户使用的
innerBottomSlot或outerBottomSlot属性的自定义 slot 中含有可交互元素时:- 可以使用
Tab键切换到这些可交互元素上 - 当焦点在自定义 slot 的首个可交互元素上时,使用
Shift+Tab,焦点回到 Select 框上
- 可以使用
带 Filter 功能的 Select:
- Select 聚焦后,键盘用户可以通过
上箭头或下箭头或Enter键打开下拉菜单。此时焦点仍然处于 Select 框,用户可以输入内容,同时也能使用上箭头或下箭头切换选项 - 当下拉菜单打开时:使用
上箭头或下箭头切换选项;Enter选中被聚焦的选项,单选选中后收起面板,多选切换选中状态并保持打开;Esc或Tab关闭菜单。Home、End、空格与字符留给 Select 框内的输入框,用于编辑搜索内容,不移动选项焦点 - 当焦点在 Select 框上,且用户使用的
innerBottomSlot或outerBottomSlot属性的自定义 slot 中含有可交互元素时:- 可以使用
Tab键切换到这些可交互元素上 - 当焦点在自定义 slot 的首个可交互元素上时,使用
Shift+Tab,焦点回到 Select 框上
- 可以使用
清除按钮:
showClear开启、有已选值或搜索文本且未禁用时,trigger 内渲染清除按钮。它是原生button,名称为语言包中的「清空已选值」,在 hover、下拉打开或焦点位于 Select 内时显示- 焦点在 Select 框上时按
Tab移到清除按钮,Enter或Space清空,效果与鼠标点击相同;清空后焦点回到 Select 框
文案规范
- 选择器标签
- 用 1-3 个词描述需要用户所做的输入
- 使用语句书写规范(首字母大写,其余小写)
- 避免使用标点符号和介词(“the”, “an”, “a”)
- 标签需是独立语句。不要让标签是前半句语句,选项是后半句语句。
- 使用描述性语句,而不是指示性语句。如果选项需要更多解释,可以在选择框下使用帮助文本。
- 选择器选项
- 如果没有默认选项,就使用“Select”做占位文案
- 选项要按首字母顺序或者其他有逻辑的排列顺序,使用户更好地找到选项
- 使用语句书写规范(首字母大写,其余小写),避免在句尾使用逗号和分号
- 清晰表达出选项所表示的选择目的
设计变量
| Sass 变量 | 值 | 用法 |
|---|---|---|
| $transition_duration-select-bg | var(--semi-transition_duration-none) | 选择器-背景色-动画持续时间 |
| $transition_function-select-bg | var(--semi-transition_function-easeIn) | 选择器-背景色-过渡曲线 |
| $transition_delay-select-bg | var(--semi-transition_delay-none) | 选择器-背景色-延迟时间 |
| $transition_duration-select-border | var(--semi-transition_duration-none) | 选择器-边框-动画持续时间 |
| $transition_function-select-border | var(--semi-transition_function-easeIn) | 选择器-边框-过渡曲线 |
| $transition_delay-select-border | var(--semi-transition_delay-none) | 选择器-边框-延迟时间 |
| $transition_duration-select_option-bg | var(--semi-transition_duration-none) | 选择器-选项-动画持续时间 |
| $transition_function-select_option-bg | var(--semi-transition_function-easeIn) | 选择器-选项-过渡曲线 |
| $transition_delay-select_option-bg | var(--semi-transition_delay-none) | 选择器-选项-延迟时间 |
| $transform_scale-select | var(--semi-transform_scale-none) | 选择框-变大 |
| $transform_rotate-select-arrow | var(--semi-transform_rotate-none) | 选择框-箭头-旋转 |
| $color-select-bg-default | var(--semi-color-fill-0) | 选择器输入框背景色 - 默认态 |
| $color-select-bg-hover | var(--semi-color-fill-1) | 选择器输入框背景色 - 悬停态 |
| $color-select-bg-active | var(--semi-color-fill-2) | 选择器输入框背景色 - 按下态 |
| $color-select-bg-focus | var(--semi-color-fill-0) | 选择器输入框背景色 - 聚焦态 |
| $color-select-border-default | transparent | 选择器输入框描边颜色 |
| $color-select-border-hover | $color-select-border-default | 选择器输入框描边颜色 - 悬浮 |
| $color-select-border-active | var(--semi-color-focus-border) | 选择器输入框描边颜色 - 按下态 |
| $color-select-border-focus | $color-select-border-active | 选择器输入框描边颜色 - 选中态 |
| $color-select_dropdown_input-border | $color-select-border-default | 下拉搜索框底部描边颜色 |
| $color-select_warning-bg-default | var(--semi-color-warning-light-default) | 警示选择器输入框背景色 - 默认态 |
| $color-select_warning-border-default | var(--semi-color-warning-light-default) | 警示选择器输入框描边颜色 - 默认态 |
| $color-select_warning-bg-hover | var(--semi-color-warning-light-hover) | 警示选择器输入框背景色 - 悬停态 |
| $color-select_warning-border-hover | var(--semi-color-warning-light-hover) | 警示选择器输入框描边颜色 - 悬停态 |
| $color-select_warning-bg-focus | var(--semi-color-warning-light-default) | 警示选择器输入框背景色 - 选中态 |
| $color-select_warning-border-focus | var(--semi-color-warning) | 警示选择器输入框描边颜色 - 选中态 |
| $color-select_warning-bg-active | var(--semi-color-warning-light-active) | 警示选择器输入框背景色 - 按下态 |
| $color-select_warning-border-active | var(--semi-color-warning-light-active) | 警示选择器输入框描边颜色 - 按下态 |
| $color-select_danger-bg-default | var(--semi-color-danger-light-default) | 报错选择器输入框背景色 - 默认态 |
| $color-select_danger-border-default | var(--semi-color-danger-light-default) | 报错选择器输入框描边颜色 - 默认态 |
| $color-select_danger-bg-hover | var(--semi-color-danger-light-hover) | 报错选择器输入框背景色 - 悬停态 |
| $color-select_danger-border-hover | var(--semi-color-danger-light-hover) | 报错选择器输入框描边颜色 - 悬停态 |
| $color-select_danger-bg-focus | var(--semi-color-danger-light-default) | 报错选择器输入框背景色 - 选中态 |
| $color-select_danger-border-focus | var(--semi-color-danger) | 报错选择器输入框描边颜色 - 选中态 |
| $color-select_danger-bg-active | var(--semi-color-danger-light-active) | 报错选择器输入框背景色 - 按下态 |
| $color-select_danger-border-active | var(--semi-color-danger-light-active) | 报错选择器输入框描边颜色 - 按下态 |
| $color-select_default-border-focus | var(--semi-color-focus-border) | 默认选择器输入框描边颜色 - 选中态 |
| $color-select_main-text-default | var(--semi-color-text-0) | 选择器输入框回填内容文本颜色 |
| $color-select-icon-default | var(--semi-color-text-2) | 选择器输入框图标颜色 |
| $color-select_clearBtn-text-default | var(--semi-color-text-2) | 选择器输入框清空按钮颜色 - 默认态 |
| $color-select_clearBtn-text-hover | var(--semi-color-primary) | 选择器输入框清空按钮颜色 - 悬停态 |
| $color-select_input_disabled-bg | var(--semi-color-disabled-fill) | 禁用选择器输入框背景色 |
| $color-select_input_disabled-border-focus | transparent | 禁用选择器输入框描边颜色 - 聚焦态 |
| $color-select_input_disabled-text | var(--semi-color-disabled-text) | 禁用选择器输入框回填内容文字颜色 |
| $color-select_input_disabled-bg-hover | var(--semi-color-disabled-fill) | 禁用选择器输入框背景色 - 悬停态 |
| $color-select_input_disabled-bg-focus | var(--semi-color-disabled-fill) | 禁用选择器输入框背景色 - 聚焦态 |
| $color-select_input_placeholder-text | var(--semi-color-text-2) | 选择器输入框占位文本文字颜色 |
| $color-select_option_main-text | var(--semi-color-text-0) | 选择器下拉菜单选项文本颜色 |
| $color-select_option_keyword-text | var(--semi-color-primary) | 选择器下拉菜单选项匹配搜索结果文本颜色 |
| $color-select_option-icon-default | transparent | 选择器下拉菜单选项图标颜色 - 默认态 |
| $color-select_option-bg-hover | var(--semi-color-fill-0) | 选择器下拉菜单选项背景颜色 - 悬停态 |
| $color-select_option-bg-active | var(--semi-color-fill-1) | 选择器下拉菜单选项背景颜色 - 按下态 |
| $color-select_option_disabled-text | var(--semi-color-disabled-text) | 禁用选择器下拉菜单选项文字颜色 |
| $color-select_option-icon-active | var(--semi-color-text-2) | 选择器下拉菜单选项图标颜色 - 选中态 |
| $color-select_option-border-default | var(--semi-color-border) | 选择器下拉菜单分组标题分割线颜色 |
| $color-select-option-bg-selected | transparent | 选择器下拉菜单选项背景颜色 - 选中态 |
| $color-select_create_tips-text | var(--semi-color-text-2) | 选择器下拉菜单新建选项提示文本颜色 |
| $color-select_group-text | var(--semi-color-text-2) | 选择器下拉菜单分组标题文本颜色 |
| $color-select_prefix_suffix_text-default | var(--semi-color-text-2) | 选择器输入框前后缀文本颜色 |
| $height-select_large | $height-control-large | 选择器输入框高度 - 大尺寸 |
| $height-select_small | $height-control-small | 选择器输入框高度 - 小尺寸 |
| $height-select_default | $height-control-default | 选择器输入框高度 - 默认尺寸 |
| $width-select_option_tick | $width-icon-small | 选择器下拉菜单项选中对勾图标大小 |
| $width-select-border | 1px | 选择器输入框描边宽度 |
| $width-select_arrow | 32px | 选择器输入框下拉箭头宽度 |
| $width-select_arrow_empty | 12px | 选择器输入框下拉箭头为空时(有suffix icon)宽度 |
| $width-select_clear-icon | 32px | 选择器输入框清空按钮宽度 |
| $width-select_group_top-border | $border-thickness-control | 选择器下拉菜单分组标题描边宽度 |
| $height-select_multiple_input_small | 20px | 小尺寸多项选择器输入框Input框高度 |
| $height-select_multiple_input_default | 24px | 默认多项选择器输入框Input框高度 |
| $height-select_multiple_input_large | 24px | 大尺寸多项选择器输入框Input框高度 |
| $width-select-border-hover | $width-select-border | 选择器输入框描边宽度 - 悬浮 |
| $width-select-border-focus | $width-select-border-hover | 选择器输入框描边宽度 - 按下 |
| $width-select-border-active | $width-select-border-focus | 选择器输入框描边宽度 - 选中 |
| $spacing-select_option_tick-marginRight | $spacing-tight | 选择器下拉菜单选中对勾右侧外边距 |
| $spacing-select_prefix_suffix_text-marginX | $spacing-base-tight | 选择器输入框前后缀文本水平内边距 |
| $spacing-select_prefix_suffix_text-marginY | 0px | 选择器输入框前后缀文本垂直内边距 |
| $spacing-select_prefix_suffix_icon-marginX | $spacing-tight | 选择器输入框前后缀图标水平内边距 |
| $spacing-select_prefix_suffix_icon-marginY | 0px | 选择器输入框前后缀图标垂直内边距 |
| $spacing-select_create_tips-marginRight | $spacing-extra-tight | 创建新选项右侧外边距 |
| $spacing-select_group-marginTop | $spacing-extra-tight | 选择器下拉菜单分组标题顶部外边距 |
| $spacing-select_group-paddingTop | $spacing-base-tight | 选择器下拉菜单分组标题顶部内边距 |
| $spacing-select_group-paddingBottom | $spacing-extra-tight | 选择器下拉菜单分组标题底部内边距 |
| $spacing-select_group-paddingLeft | $spacing-base-tight + $width-select_option_tick + $spacing-select_option_tick-marginRight | 选择器下拉菜单分组标题左侧内边距 |
| $spacing-select_group-paddingRight | $spacing-base | 选择器下拉菜单分组标题右侧内边距 |
| $spacing-select_loading_wrapper-paddingLeft | $spacing-base | 选择器加载左侧内边距 |
| $spacing-select_loading_wrapper-paddingRight | $spacing-base | 选择器加载右侧内边距 |
| $spacing-select_loading_wrapper-paddingTop | $spacing-tight | 选择器加载顶部内边距 |
| $spacing-select_loading_wrapper-paddingBottom | $spacing-tight | 选择器加载底部内边距 |
| $spacing-select_multiple_content_wrapper_empty-marginLeft | $spacing-tight | 多项选择器空回填内容为空时左侧内边距 |
| $spacing-select_multiple_selection-marginLeft | $spacing-extra-tight | 多项选择器回填内容左侧内边距 |
| $spacing-select_option-paddingLeft | $spacing-base-tight | 选择器下拉菜单项左侧内边距 |
| $spacing-select_option-paddingRight | $spacing-base-tight | 选择器下拉菜单项右侧内边距 |
| $spacing-select_option-paddingTop | $spacing-tight | 选择器下拉菜单项顶部内边距 |
| $spacing-select_option-paddingBottom | $spacing-tight | 选择器下拉菜单项底部内边距 |
| $spacing-select_tag-marginTop | $spacing-super-tight - 1px | 多项选择器标签顶部外边距 |
| $spacing-select_tag-marginRight | $spacing-extra-tight | 多项选择器标签右侧外边距 |
| $spacing-select_tag-marginBottom | $spacing-super-tight - 1px | 多项选择器标签底部外边距 |
| $spacing-select_selection-marginLeft | $spacing-base-tight | 选择器内容区左侧外边距 |
| $spacing-select_option_list-paddingTop | $spacing-extra-tight | 选择器内容区顶部内边距 |
| $spacing-select_option_list-paddingRight | 0px | 选择器内容区右侧内边距 |
| $spacing-select_option_list-paddingBottom | $spacing-extra-tight | 选择器内容区底部内边距 |
| $spacing-select_option_list-paddingLeft | 0px | 选择器内容区左侧内边距 |
| $spacing-select_dropdown_input_paddingTop | 8px | 下拉搜索框顶部内边距 |
| $spacing-select_dropdown_input_paddingBottom | 8px | 下拉搜索框底部内边距 |
| $spacing-select_dropdown_input_paddingRight | 12px | 下拉搜索框右侧内边距 |
| $spacing-select_dropdown_input_paddingLeft | 12px | 下拉搜索框左侧内边距 |
| $radius-select | var(--semi-border-radius-small) | 选择器输入框圆角 |
| $radius-select_option | 0px | 选择器待选项圆角 |
| $font-select-fontWeight | $font-weight-regular | 选择器文本字重 |
| $font-select_keyword-fontWeight | 600 | 选择器搜索结果命关键词中文本字重 |
| $font-select_prefix_suffix-fontWeight | $font-weight-bold | 选择器输入框前后缀文本字重 |
| $opacity-select_selection_text_inactive | 0.4 |
TypeScript 泛型支持
Select 是泛型组件,有两个泛型参数:T 是选项 value 的类型(设置 changeWithObject 时是 option 对象的类型),Multiple 是 multiple 属性的类型。单选时 change 与 update:modelValue 的载荷为 T | undefined,多选时为 T[];multiple 是 boolean 变量时为 T | T[] | undefined。select、deselect 的载荷是选项的原始 value(string | number),不随 T 变化。两个参数通常由 modelValue 与 multiple 推断;全局默认值不能设置 multiple,因为给它一个默认值会破坏这一推断。
<script setup lang="ts">
import { ref } from 'vue';
import { Select } from '@fibrous/semi-ui-vue';
// 由 modelValue 推断 T 为 string
const value = ref<string | undefined>();
// 多选时 modelValue 为 string[]
const values = ref<string[]>([]);
</script>
<template>
<Select v-model="value">
<Select.Option value="option1">选项 1</Select.Option>
<Select.Option value="option2">选项 2</Select.Option>
</Select>
<Select v-model="values" multiple>
<Select.Option value="option1">选项 1</Select.Option>
<Select.Option value="option2">选项 2</Select.Option>
</Select>
</template>
FAQ
-
可搜索的 Select,使用远程数据动态更新
optionList,为什么在异步请求完成之前有时候会出现暂无数据?
请检查是否开启了remote,不开启 remote 的情况下,默认会将 input 框输入值与当前的 optionList 进行一次对比筛选,如果无匹配时,就会显示暂无数据。
可以通过开启 remote 关闭对本地当前数据的匹配筛选。 -
Select 会自动限制下拉菜单的宽度吗?
- 会给 minWidth,但不会写死 width。如果有需要的话,可以自己通过 dropdownStyle 来添加。
-
为什么单选选择选项后没有触发 blur 事件?
- 在 V2.17.0 前,Select 单选选择后,会触发 Select 的 blur 事件。
- 在 V2.17.0 后,Select 增加了 A11y 支持,不会触发 Select 的 blur 事件。
- 单选选择中,Select 浮层关闭,依然保持焦点在 trigger(此时可以通过 Enter 回车键再次打开 Select 浮层)
- 无论单选或多选,按下 Esc,仅 Select 浮层关闭,trigger 保持焦点(此时可以通过 Enter 回车键再次打开 Select 浮层)