Appearance
Select 选择器
当选项过多时,使用下拉菜单展示并选择内容。
基础用法
适用广泛的基础单选
选项1
数据0
选项1
选项2
选项3
选项4
选项5
选项6
选项7
选项8
选项9
选项10
选项11
选项12
选项13
vue
<template>
<Select v-model="value" placeholder="请选择">
<Option value="0">数据0</Option>
<Option label="选项1" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
<Option label="选项4" value="4" />
<Option label="选项5" value="5" />
<Option label="选项6" value="6" />
<Option label="选项7" value="7" />
<Option label="选项8" value="8" />
<Option label="选项9" value="9" />
<Option label="选项10" value="10" />
<Option label="选项11" value="11" />
<Option label="选项12" value="12" />
<Option label="选项13" value="13" />
</Select>
</template>
<script setup>
import { ref } from 'vue';
const value = ref('1');
</script>不同类型
支持 6 种主题类型:default、primary、success、warning、danger、info
请选择
默认
选项2
选项3
请选择
主要
选项2
选项3
请选择
成功
选项2
选项3
请选择
信息
选项2
选项3
请选择
警告
选项2
选项3
请选择
危险
选项2
选项3
vue
<template>
<Row :gutter="[20, 20]">
<Col :xl="12" :md="24">
<Select v-model="value1" placeholder="请选择" type="default">
<Option label="默认" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
<Col :xl="12" :md="24">
<Select v-model="value2" placeholder="请选择" type="primary">
<Option label="主要" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
<Col :xl="12" :md="24">
<Select v-model="value3" placeholder="请选择" type="success">
<Option label="成功" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
<Col :xl="12" :md="24">
<Select v-model="value4" placeholder="请选择" type="info">
<Option label="信息" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
<Col :xl="12" :md="24">
<Select v-model="value5" placeholder="请选择" type="warning">
<Option label="警告" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
<Col :xl="12" :md="24">
<Select v-model="value6" placeholder="请选择" type="danger">
<Option label="危险" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
</Row>
</template>
<script setup>
import { ref } from "vue";
const value1 = ref("");
const value2 = ref("");
const value3 = ref("");
const value4 = ref("");
const value5 = ref("");
const value6 = ref("");
</script>多选模式
支持多选,通过设置 multiple 属性为 true
请选择多个选项
中央
上海
广州
深圳
重庆
请选择多个选项
北京
上海
广州
深圳
重庆
请选择多个选项
北京
上海
广州
深圳
重庆
vue
<template>
<Row :gutter="[20, 20]">
<Col :span="24">
<Select v-model="value8" placeholder="请选择多个选项" multiple>
<Option label="北京" value="beijing">中央</Option>
<Option label="上海" value="shanghai" />
<Option label="广州" value="guangzhou" />
<Option label="深圳" value="shenzhen" />
<Option label="重庆" value="chongqing" />
</Select>
</Col>
<Col :span="24">
<Select v-model="value8" placeholder="请选择多个选项" multiple type="primary">
<Option label="北京" value="beijing" />
<Option label="上海" value="shanghai" />
<Option label="广州" value="guangzhou" />
<Option label="深圳" value="shenzhen" />
<Option label="重庆" value="chongqing" />
</Select>
</Col>
<Col :span="24">
<Select v-model="value8" placeholder="请选择多个选项" multiple type="danger">
<Option label="北京" value="beijing" />
<Option label="上海" value="shanghai" />
<Option label="广州" value="guangzhou" />
<Option label="深圳" value="shenzhen" />
<Option label="重庆" value="chongqing" />
</Select>
</Col>
</Row>
</template>
<script setup>
import { ref } from "vue";
const value8 = ref([]);
</script>禁用选项
可以通过设置 disabled 属性来禁用整个选择器或特定选项
禁用的选择器
选项1
选项2
包含禁用选项
可用选项1
禁用选项
可用选项2
vue
<template>
<Row :gutter="[20, 20]">
<Col :span="12">
<Select v-model="value7" placeholder="禁用的选择器" disabled>
<Option label="选项1" value="1" />
<Option label="选项2" value="2" />
</Select>
</Col>
<Col :span="12">
<Select v-model="value9" placeholder="包含禁用选项">
<Option label="可用选项1" value="1" />
<Option label="禁用选项" value="2" disabled />
<Option label="可用选项2" value="3" />
</Select>
</Col>
</Row>
</template>
<script setup>
import { ref } from "vue";
const value7 = ref("");
const value9 = ref("");
</script>可清空
通过设置 clearable 属性可以清空已选择的值
可清空的选择
选项1
选项2
选项3
vue
<template>
<Select v-model="value9" placeholder="可清空的选择" clearable>
<Option label="选项1" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</template>
<script setup>
import { ref } from "vue";
const value9 = ref("");
</script>可搜索
通过设置 filterable 属性启用搜索功能
JavaScript
TypeScript
Python
Java
Go
vue
<template>
<Select v-model="value10" placeholder="可搜索的选择" filterable>
<Option label="JavaScript" value="js" />
<Option label="TypeScript" value="ts" />
<Option label="Python" value="py" />
<Option label="Java" value="java" />
<Option label="Go" value="go" />
</Select>
</template>
<script setup>
import { ref } from "vue";
const value10 = ref("");
</script>不同尺寸
支持三种尺寸:small、medium、large
小尺寸
小尺寸
选项2
默认尺寸
默认尺寸
选项2
大尺寸
大尺寸
选项2
vue
<template>
<Row :gutter="[20, 20]">
<Col :span="24">
<Select v-model="value11" placeholder="小尺寸" size="small">
<Option label="小尺寸" value="1" />
<Option label="选项2" value="2" />
</Select>
</Col>
<Col :span="24">
<Select v-model="value11" placeholder="默认尺寸" size="medium">
<Option label="默认尺寸" value="1" />
<Option label="选项2" value="2" />
</Select>
</Col>
<Col :span="24">
<Select v-model="value11" placeholder="大尺寸" size="large">
<Option label="大尺寸" value="1" />
<Option label="选项2" value="2" />
</Select>
</Col>
</Row>
</template>
<script setup>
import { ref } from "vue";
const value11 = ref("");
</script>使用 options 属性
除了使用 Option 子组件,还可以通过 options 属性传入选项数组
使用 options 属性
选项1
选项2
选项3
vue
<template>
<Select
v-model="value12"
type="primary"
:options="[
{ label: '选项1', value: '1' },
{ label: '选项2', value: '2' },
{ label: '选项3', value: '3', disabled: true },
]"
placeholder="使用 options 属性"
/>
</template>
<script setup>
import { ref } from "vue";
const value12 = ref("");
</script>图标位置
可以通过 icon-position 属性设置选中图标的位置
图标在左侧
选项1
选项2
选项3
图标在右侧
选项1
选项2
选项3
vue
<template>
<Row :gutter="[20, 20]">
<Col :span="12">
<Select v-model="value1" placeholder="图标在左侧" icon-position="left">
<Option label="选项1" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
<Col :span="12">
<Select v-model="value2" placeholder="图标在右侧" icon-position="right">
<Option label="选项1" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
</Select>
</Col>
</Row>
</template>
<script setup>
import { ref } from "vue";
const value1 = ref("");
const value2 = ref("");
</script>自定义图标
使用 icon 插槽自定义选中图标
自定义图标
选项1
选项2
右侧自定义图标
选项1
选项2
vue
<template>
<Row :gutter="[20, 20]">
<Col :span="12">
<Select v-model="value3" placeholder="自定义图标" icon-position="left">
<Option label="选项1" value="1">
<template #icon>
<span style="color: var(--color-primary);">★</span>
</template>
选项1
</Option>
<Option label="选项2" value="2">
<template #icon>
<span style="color: var(--color-success);">●</span>
</template>
选项2
</Option>
</Select>
</Col>
<Col :span="12">
<Select
v-model="value4"
placeholder="右侧自定义图标"
icon-position="right"
>
<Option label="选项1" value="1">
<template #icon>
<span style="color: var(--color-warning);">✓</span>
</template>
选项1
</Option>
<Option label="选项2" value="2">
<template #icon>
<span style="color: var(--color-danger);">✗</span>
</template>
选项2
</Option>
</Select>
</Col>
</Row>
</template>
<script setup>
import { ref } from "vue";
const value3 = ref("");
const value4 = ref("");
</script>分组
使用 OptionGroup 组件可以对选项进行分组
分组选项
基础分组测试
选项1
选项2
vue
<template>
<Select v-model="value" placeholder="分组选项" icon-position="left">
<OptionGroup label="基础分组测试">
<Option label="选项1" value="1">
<template #icon>
<span style="color: var(--color-primary)">★</span>
</template>
选项1
</Option>
<Option label="选项2" value="2">
<template #icon>
<span style="color: var(--color-success)">●</span>
</template>
选项2
</Option>
</OptionGroup>
</Select>
</template>
<script setup>
import { ref } from 'vue';
const value = ref('');
</script>可折叠分组
通过设置 collapsible 属性可以让分组支持折叠功能,使用 default-collapsed 设置默认折叠状态
可折叠分组
前端技术
Vue.js
React
Angular
后端技术
Node.js
Python
Java
数据库
MySQL
PostgreSQL
MongoDB
vue
<template>
<Select v-model="value" placeholder="可折叠分组">
<OptionGroup
label="前端技术"
:collapsible="true"
:default-collapsed="false"
>
<Option label="Vue.js" value="vue" />
<Option label="React" value="react" />
<Option label="Angular" value="angular" />
</OptionGroup>
<OptionGroup label="后端技术" :collapsible="true" :default-collapsed="true">
<Option label="Node.js" value="nodejs" />
<Option label="Python" value="python" />
<Option label="Java" value="java" />
</OptionGroup>
<OptionGroup label="数据库" :collapsible="true" :default-collapsed="false">
<Option label="MySQL" value="mysql" />
<Option label="PostgreSQL" value="postgresql" />
<Option label="MongoDB" value="mongodb" />
</OptionGroup>
</Select>
</template>
<script setup>
import { ref } from 'vue';
const value = ref('');
</script>嵌套分组
支持多级嵌套分组,每个嵌套层级都可以独立设置折叠状态
嵌套分组
前端技术
HTML
CSS
JavaScript 框架
Vue.js
React
状态管理
Vuex
Pinia
Redux
构建工具
Vite
Webpack
Rollup
后端技术
Node.js
Python
Java
vue
<template>
<Select v-model="value" placeholder="嵌套分组">
<OptionGroup
label="前端技术"
:collapsible="true"
:default-collapsed="false"
>
<Option label="HTML" value="html" />
<Option label="CSS" value="css" />
<OptionGroup
label="JavaScript 框架"
:collapsible="true"
:default-collapsed="false"
>
<Option label="Vue.js" value="vue" />
<Option label="React" value="react" />
<OptionGroup
label="状态管理"
:collapsible="true"
:default-collapsed="false"
>
<Option label="Vuex" value="vuex" />
<Option label="Pinia" value="pinia" />
<Option label="Redux" value="redux" />
</OptionGroup>
</OptionGroup>
<OptionGroup
label="构建工具"
:collapsible="true"
:default-collapsed="true"
>
<Option label="Vite" value="vite" />
<Option label="Webpack" value="webpack" />
<Option label="Rollup" value="rollup" />
</OptionGroup>
</OptionGroup>
<OptionGroup
label="后端技术"
:collapsible="true"
:default-collapsed="false"
>
<Option label="Node.js" value="nodejs" />
<Option label="Python" value="python" />
<Option label="Java" value="java" />
</OptionGroup>
</Select>
</template>
<script setup>
import { ref } from 'vue';
const value = ref('');
</script>折叠图标位置
可以通过 icon-position 属性设置折叠图标的位置:left(左侧)、after-text(文字后)、right(右侧)
左侧图标
基础组件
Button
Input
布局组件
Grid
Layout
文字后图标
基础组件
Button
Input
布局组件
Grid
Layout
右侧图标
基础组件
Button
Input
布局组件
Grid
Layout
vue
<template>
<Row :gutter="[20, 20]">
<Col :span="8">
<Select v-model="value1" placeholder="左侧图标">
<OptionGroup
label="基础组件"
:collapsible="true"
:default-collapsed="false"
icon-position="left"
>
<Option label="Button" value="button" />
<Option label="Input" value="input" />
</OptionGroup>
<OptionGroup
label="布局组件"
:collapsible="true"
:default-collapsed="false"
icon-position="left"
>
<Option label="Grid" value="grid" />
<Option label="Layout" value="layout" />
</OptionGroup>
</Select>
</Col>
<Col :span="8">
<Select v-model="value2" placeholder="文字后图标">
<OptionGroup
label="基础组件"
:collapsible="true"
:default-collapsed="false"
icon-position="after-text"
>
<Option label="Button" value="button" />
<Option label="Input" value="input" />
</OptionGroup>
<OptionGroup
label="布局组件"
:collapsible="true"
:default-collapsed="false"
icon-position="after-text"
>
<Option label="Grid" value="grid" />
<Option label="Layout" value="layout" />
</OptionGroup>
</Select>
</Col>
<Col :span="8">
<Select v-model="value3" placeholder="右侧图标">
<OptionGroup
label="基础组件"
:collapsible="true"
:default-collapsed="false"
icon-position="right"
>
<Option label="Button" value="button" />
<Option label="Input" value="input" />
</OptionGroup>
<OptionGroup
label="布局组件"
:collapsible="true"
:default-collapsed="false"
icon-position="right"
>
<Option label="Grid" value="grid" />
<Option label="Layout" value="layout" />
</OptionGroup>
</Select>
</Col>
</Row>
</template>
<script setup>
import { ref } from 'vue';
const value1 = ref('');
const value2 = ref('');
const value3 = ref('');
</script>可搜索的分组
可折叠分组功能与搜索功能可以同时使用
编程语言
JavaScript
TypeScript
Python
Java
函数式语言
Haskell
Lisp
Clojure
框架库
Vue.js
React
Angular
CSS 框架类型
原始css
css in js
原子化css
CSS 框架
Bootstrap
Tailwind CSS
Bulma
vue
<template>
<Select v-model="value" placeholder="可搜索的可折叠分组" :filterable="true">
<OptionGroup
label="编程语言"
:collapsible="true"
:default-collapsed="false"
icon-position="right"
>
<Option label="JavaScript" value="javascript" />
<Option label="TypeScript" value="typescript" />
<Option label="Python" value="python" />
<Option label="Java" value="java" />
<OptionGroup
label="函数式语言"
:collapsible="true"
:default-collapsed="false"
icon-position="right"
>
<Option label="Haskell" value="haskell" />
<Option label="Lisp" value="lisp" />
<Option label="Clojure" value="clojure" />
</OptionGroup>
</OptionGroup>
<OptionGroup
label="框架库"
:collapsible="true"
:default-collapsed="false"
icon-position="right"
>
<Option label="Vue.js" value="vue" />
<Option label="React" value="react" />
<Option label="Angular" value="angular" />
<OptionGroup
label="CSS 框架类型"
:collapsible="true"
:default-collapsed="true"
>
<Option label="原始css" value="bootstrap" />
<Option label="css in js" value="tailwind" />
<Option label="原子化css" value="bulma" />
<OptionGroup
label="CSS 框架"
:collapsible="true"
:default-collapsed="true"
>
<Option label="Bootstrap" value="bootstrap" />
<Option label="Tailwind CSS" value="tailwind" />
<Option label="Bulma" value="bulma" />
</OptionGroup>
</OptionGroup>
</OptionGroup>
</Select>
</template>
<script setup>
import { ref } from 'vue';
import { Select, Option, OptionGroup } from '../../../src/components/Select';
const value = ref('');
</script>远程搜索
通过设置 remote 和 remote-method 属性启用远程搜索功能,可以从服务器动态获取数据
JavaScript
TypeScript
vue
<template>
<div>
<Select
ref="selectRef"
v-model="value"
placeholder="搜索编程语言"
filterable
remote
:remote-method="remoteMethod"
:loading="loading"
:options="options"
loading-text="搜索中..."
no-match-text="无匹配结果"
/>
</div>
</template>
<script setup>
import { ref, watch, nextTick } from 'vue';
import { Select } from '../../../src/components/Select';
const value = ref('');
const options = ref([
{ value: 'javascript', label: 'JavaScript' },
{ value: 'typescript', label: 'TypeScript' },
]);
const loading = ref(false);
const selectRef = ref(null);
// 模拟远程数据源
const allOptions = [
{ value: 'javascript', label: 'JavaScript' },
{ value: 'typescript', label: 'TypeScript' },
{ value: 'python', label: 'Python' },
{ value: 'java', label: 'Java' },
{ value: 'csharp', label: 'C#' },
{ value: 'golang', label: 'Go' },
{ value: 'rust', label: 'Rust' },
{ value: 'swift', label: 'Swift' },
{ value: 'kotlin', label: 'Kotlin' },
{ value: 'dart', label: 'Dart' },
{ value: 'php', label: 'PHP' },
{ value: 'ruby', label: 'Ruby' },
{ value: 'scala', label: 'Scala' },
{ value: 'clojure', label: 'Clojure' },
{ value: 'haskell', label: 'Haskell' },
];
const remoteMethod = query => {
if (query) {
loading.value = true;
// 模拟网络延迟
setTimeout(() => {
const filteredResults = allOptions.filter(item =>
item.label.toLowerCase().includes(query.toLowerCase())
);
options.value = filteredResults;
loading.value = false;
}, 300);
} else {
options.value = [];
}
};
</script>远程搜索进阶
远程搜索支持多选、分组等高级功能,并提供防抖优化减少请求频率
基础远程搜索
No data
当前值:
多选远程搜索
选择多个技术栈
No data
分组远程搜索
No data
当前值:
vue
<template>
<div style="display: flex; flex-direction: column; gap: 20px">
<!-- 基础远程搜索 -->
<div>
<h4>基础远程搜索</h4>
<Select
v-model="singleValue"
placeholder="输入搜索编程语言"
filterable
remote
:remote-method="searchLanguages"
:loading="singleLoading"
:options="singleOptions"
loading-text="搜索中..."
no-match-text="无匹配结果"
clearable
/>
<p>当前值: {{ singleValue }}</p>
</div>
<!-- 多选远程搜索 -->
<div>
<h4>多选远程搜索</h4>
<Select
ref="multipleSelectRef"
v-model="multipleValue"
placeholder="选择多个技术栈"
filterable
remote
multiple
:remote-method="searchTechStack"
:loading="multipleLoading"
:options="multipleOptions"
loading-text="搜索中..."
no-match-text="无匹配结果"
clearable
@change="handleMultipleChange"
@search="handleMultipleSearch"
/>
<div
style="
margin-top: 10px;
padding: 10px;
border-radius: 4px;
font-size: 14px;
"
></div>
</div>
<!-- 带分组的远程搜索 -->
<div>
<h4>分组远程搜索</h4>
<Select
v-model="groupValue"
placeholder="搜索工具和框架"
filterable
remote
:remote-method="searchGroupedItems"
:loading="groupLoading"
loading-text="搜索中..."
no-match-text="无匹配结果"
>
<template v-for="group in groupedOptions" :key="group.label">
<OptionGroup :label="group.label">
<Option
v-for="option in group.options"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</OptionGroup>
</template>
</Select>
<p>当前值: {{ groupValue }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { Select, Option, OptionGroup } from '../../../src/components/Select';
// 单选远程搜索
const singleValue = ref('');
const singleOptions = ref([]);
const singleLoading = ref(false);
// 多选远程搜索
const multipleValue = ref([]);
const multipleOptions = ref([]);
const multipleLoading = ref(false);
const multipleSelectRef = ref(null);
const multipleStatus = ref(null);
// 分组远程搜索
const groupValue = ref('');
const groupedOptions = ref([]);
const groupLoading = ref(false);
// 模拟数据
const languages = [
{ value: 'javascript', label: 'JavaScript' },
{ value: 'typescript', label: 'TypeScript' },
{ value: 'python', label: 'Python' },
{ value: 'java', label: 'Java' },
{ value: 'csharp', label: 'C#' },
{ value: 'golang', label: 'Go' },
{ value: 'rust', label: 'Rust' },
{ value: 'swift', label: 'Swift' },
{ value: 'kotlin', label: 'Kotlin' },
{ value: 'dart', label: 'Dart' },
];
const techStack = [
{ value: 'vue', label: 'Vue.js' },
{ value: 'react', label: 'React' },
{ value: 'angular', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'nodejs', label: 'Node.js' },
{ value: 'express', label: 'Express' },
{ value: 'nestjs', label: 'NestJS' },
{ value: 'spring', label: 'Spring Boot' },
{ value: 'django', label: 'Django' },
{ value: 'flask', label: 'Flask' },
];
const groupedData = {
前端框架: [
{ value: 'vue', label: 'Vue.js' },
{ value: 'react', label: 'React' },
{ value: 'angular', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
],
后端框架: [
{ value: 'express', label: 'Express' },
{ value: 'nestjs', label: 'NestJS' },
{ value: 'spring', label: 'Spring Boot' },
{ value: 'django', label: 'Django' },
{ value: 'flask', label: 'Flask' },
],
构建工具: [
{ value: 'webpack', label: 'Webpack' },
{ value: 'vite', label: 'Vite' },
{ value: 'rollup', label: 'Rollup' },
{ value: 'parcel', label: 'Parcel' },
],
数据库: [
{ value: 'mysql', label: 'MySQL' },
{ value: 'postgresql', label: 'PostgreSQL' },
{ value: 'mongodb', label: 'MongoDB' },
{ value: 'redis', label: 'Redis' },
],
};
// 远程搜索方法
const searchLanguages = query => {
console.log('🔍 单选远程搜索:', query);
if (query) {
singleLoading.value = true;
setTimeout(() => {
singleOptions.value = languages.filter(item =>
item.label.toLowerCase().includes(query.toLowerCase())
);
singleLoading.value = false;
console.log('📊 单选搜索结果:', singleOptions.value);
}, 200);
} else {
singleOptions.value = [];
console.log('🚫 清空单选搜索结果');
}
};
const searchTechStack = query => {
console.log('🔍 多选远程搜索:', query);
console.log('🔍 搜索前状态:', {
currentValue: multipleValue.value,
currentOptions: multipleOptions.value.length,
loading: multipleLoading.value,
});
if (query) {
multipleLoading.value = true;
setTimeout(() => {
const filteredResults = techStack.filter(item =>
item.label.toLowerCase().includes(query.toLowerCase())
);
multipleOptions.value = filteredResults;
multipleLoading.value = false;
console.log('📊 多选搜索结果:', {
query: query,
resultsCount: filteredResults.length,
results: filteredResults.map(item => `${item.value}:${item.label}`),
loading: multipleLoading.value,
});
}, 250);
} else {
multipleOptions.value = [];
console.log('🚫 清空多选搜索结果');
}
};
// 多选远程搜索调试方法
const handleMultipleChange = newValue => {
console.log('📝 多选值变化:', multipleValue.value, '->', newValue);
// 延迟检查标签显示状态
setTimeout(() => {
diagnoseMultipleTags();
}, 100);
};
const handleMultipleSearch = query => {
console.log('🔍 多选搜索事件:', query);
};
// 获取期望的标签
const getExpectedLabel = value => {
const option = techStack.find(item => item.value === value);
return option ? option.label : value;
};
// 测试多选远程搜索
const testMultipleRemoteSearch = async () => {
console.log('🧪 ================= 开始多选远程搜索测试 =================');
// 1. 清空当前状态
multipleValue.value = [];
multipleOptions.value = [];
await new Promise(resolve => setTimeout(resolve, 100));
// 2. 模拟搜索 "react"
console.log('🔍 模拟搜索: react');
searchTechStack('react');
// 3. 等待搜索结果
await new Promise(resolve => setTimeout(resolve, 300));
// 4. 模拟选择 React
if (multipleOptions.value.some(opt => opt.value === 'react')) {
console.log('🎯 选择 React');
multipleValue.value = ['react'];
await new Promise(resolve => setTimeout(resolve, 100));
}
// 5. 模拟搜索 "vue"
console.log('🔍 模拟搜索: vue');
searchTechStack('vue');
// 6. 等待搜索结果
await new Promise(resolve => setTimeout(resolve, 300));
// 7. 模拟添加 Vue.js
if (multipleOptions.value.some(opt => opt.value === 'vue')) {
console.log('🎯 添加 Vue.js');
multipleValue.value = [...multipleValue.value, 'vue'];
await new Promise(resolve => setTimeout(resolve, 100));
}
// 8. 检查结果
diagnoseMultipleTags();
console.log('🧪 ================= 多选远程搜索测试完成 =================');
};
// 测试多选输入功能
const testMultipleInput = async () => {
console.log('⌨️ ================= 开始测试多选输入功能 =================');
if (!multipleSelectRef.value) {
console.error('❌ 无法获取多选 Select 组件引用');
return;
}
const inputElement =
multipleSelectRef.value.$el?.querySelector('.x-select__input');
if (!inputElement) {
console.error('❌ 找不到输入框元素');
multipleStatus.value = {
color: '#f8d7da',
title: '❌ 找不到输入框',
message: '无法找到输入框元素,请检查组件是否正确配置 filterable 属性',
};
return;
}
console.log('⌨️ 1. 输入框基本信息:');
console.log(' - 是否禁用:', inputElement.disabled);
console.log(' - 是否只读:', inputElement.readOnly);
console.log(' - placeholder:', inputElement.placeholder);
console.log(' - 当前值:', `"${inputElement.value}"`);
// 测试输入功能
try {
console.log('⌨️ 2. 测试输入功能:');
// 聚焦输入框
inputElement.focus();
await new Promise(resolve => setTimeout(resolve, 100));
// 模拟输入 "re"
console.log(' - 模拟输入 "re"');
inputElement.value = 're';
inputElement.dispatchEvent(new Event('input', { bubbles: true }));
await new Promise(resolve => setTimeout(resolve, 100));
// 检查输入结果
console.log(' - 输入后的值:', `"${inputElement.value}"`);
// 继续输入 "act"
console.log(' - 模拟输入 "act"');
inputElement.value = 'react';
inputElement.dispatchEvent(new Event('input', { bubbles: true }));
await new Promise(resolve => setTimeout(resolve, 300));
console.log(' - 最终输入值:', `"${inputElement.value}"`);
// 检查是否有搜索结果
if (multipleOptions.value.length > 0) {
console.log(
'✅ 输入测试成功: 已触发远程搜索,获得',
multipleOptions.value.length,
'个结果'
);
multipleStatus.value = {
color: '#d1ecf1',
title: '✅ 输入测试成功',
message: `多选远程搜索输入功能正常,获得${multipleOptions.value.length}个搜索结果`,
};
} else {
console.log('⚠️ 输入测试部分成功: 可以输入,但没有搜索结果');
multipleStatus.value = {
color: '#fff3cd',
title: '⚠️ 部分成功',
message: '可以输入内容,但没有触发远程搜索或没有结果',
};
}
} catch (error) {
console.error('❌ 输入测试失败:', error.message);
multipleStatus.value = {
color: '#f8d7da',
title: '❌ 输入测试失败',
message: `输入测试过程中发生错误: ${error.message}`,
};
}
console.log('⌨️ ================= 多选输入测试完成 =================');
};
// 诊断多选标签显示
const diagnoseMultipleTags = () => {
console.log('🔍 ================= 诊断多选标签显示 =================');
if (!multipleSelectRef.value) {
console.error('❌ 无法获取多选 Select 组件引用');
return;
}
const selectEl = multipleSelectRef.value.$el;
const tagElements = selectEl?.querySelectorAll('.x-select__tag');
console.log('🔍 1. 基本信息:');
console.log(' - 选中值:', multipleValue.value);
console.log(' - 期望标签:', multipleValue.value.map(getExpectedLabel));
console.log(' - DOM 中的标签数量:', tagElements ? tagElements.length : 0);
if (tagElements && tagElements.length > 0) {
console.log('🔍 2. DOM 标签内容:');
Array.from(tagElements).forEach((tag, index) => {
const textElement = tag.querySelector('.x-select__tag-text');
const displayText = textElement ? textElement.textContent : '未知';
console.log(` ${index}: "${displayText}"`);
});
}
// 检查是否有问题
const hasIssues = multipleValue.value.some((value, index) => {
const expectedLabel = getExpectedLabel(value);
const tagElement = tagElements?.[index];
const actualLabel = tagElement?.querySelector(
'.x-select__tag-text'
)?.textContent;
return actualLabel !== expectedLabel;
});
if (hasIssues) {
multipleStatus.value = {
color: '#f8d7da',
title: '❌ 发现问题',
message: '有些标签显示与期望不一致,请检查控制台日志',
};
console.error('❌ 多选标签显示存在问题');
// 详细分析可能的原因
console.log('🔍 可能的原因分析:');
if (multipleOptions.value.length === 0) {
console.log(' → 原因可能: 当前没有可用选项,组件无法查找到对应的标签');
}
multipleValue.value.forEach(value => {
const existsInOptions = multipleOptions.value.some(
opt => opt.value === value
);
if (!existsInOptions) {
console.log(
` → 原因可能: 选中值 "${value}" 在当前选项列表中不存在(远程搜索缓存问题)`
);
}
});
} else if (multipleValue.value.length === 0) {
multipleStatus.value = {
color: '#d1ecf1',
title: 'ℹ️ 信息',
message: '当前没有选中任何选项',
};
} else {
multipleStatus.value = {
color: '#d1ecf1',
title: '✅ 显示正常',
message: '所有标签显示都与期望一致',
};
console.log('✅ 多选标签显示正常');
}
console.log('🔍 ================= 诊断完成 =================');
};
const searchGroupedItems = query => {
console.log('🔍 分组远程搜索:', query);
if (query) {
groupLoading.value = true;
setTimeout(() => {
const results = [];
const searchTerm = query.toLowerCase();
Object.keys(groupedData).forEach(groupName => {
const filteredOptions = groupedData[groupName].filter(item =>
item.label.toLowerCase().includes(searchTerm)
);
if (filteredOptions.length > 0) {
results.push({
label: groupName,
options: filteredOptions,
});
}
});
groupedOptions.value = results;
groupLoading.value = false;
console.log('📊 分组搜索结果:', groupedOptions.value);
}, 300);
} else {
groupedOptions.value = [];
console.log('🚫 清空分组搜索结果');
}
};
</script>自定义尾部
可以通过 #footer 插槽自定义下拉列表的尾部内容
请选择
选项1
选项2
选项3
vue
<template>
<Select v-model="selectValue" placeholder="请选择">
<Option label="选项1" value="1" />
<Option label="选项2" value="2" />
<Option label="选项3" value="3" />
<template #footer>
<Row style="padding: 10px">
<Col :span="18" style="padding-right: 10px">
<div>
<Input
placeholder="输入相关值"
v-model="inputValue"
width="100%"
></Input>
</div>
</Col>
<Col :span="6">
<div>
<Button @click="handleOk">确定</Button>
</div>
</Col>
</Row>
</template>
</Select>
</template>
<script setup>
import { ref } from 'vue';
const selectValue = ref('');
const inputValue = ref('');
const handleOk = () => {
selectValue.value = inputValue.value;
};
</script>属性
Select 属性
| 属性名 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| model-value / v-model | 绑定值 | string / number / boolean / object / array | — | — |
| multiple | 是否多选 | boolean | — | false |
| disabled | 是否禁用 | boolean | — | false |
| clearable | 是否可以清空选项 | boolean | — | false |
| filterable | 是否可搜索 | boolean | — | false |
| remote | 是否为远程搜索 | boolean | — | false |
| remote-method | 远程搜索方法 | function | — | — |
| filter-method | 本地搜索方法 | function | — | — |
| placeholder | 占位符 | string | — | Select |
| type | 主题类型 | string | default / primary / success / warning / danger / info | default |
| size | 尺寸 | string | small / medium / large | medium |
| options | 选项数组 | array | — | [] |
| collapse-tags | 是否折叠标签 | boolean | — | false |
| loading | 是否正在加载 | boolean | — | false |
| loading-text | 加载时显示的文字 | string | — | Loading... |
| no-data-text | 无数据时显示的文字 | string | — | No data |
| no-match-text | 无匹配结果时显示的文字 | string | — | No matching data |
| icon-position | 选中图标位置 | string | left / right | left |
OptionGroup 属性
| 属性名 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| label | 分组标签 | string | — | — |
| disabled | 是否禁用该分组 | boolean | — | false |
| collapsible | 是否可折叠 | boolean | — | false |
| default-collapsed | 默认折叠状态 | boolean | — | false |
| icon-position | 折叠图标位置 | string | left / after-text / right | left |
| level | 手动设置层级 | number | — | — |
事件
Select 事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| change | 选中值发生变化时触发 | 当前选中值 |
| visible-change | 下拉框出现/隐藏时触发 | 出现则为 true,隐藏则为 false |
| remove-tag | 多选模式下移除 tag 时触发 | 移除的 tag 值 |
| clear | 可清空的单选模式下用户点击清空按钮时触发 | — |
| focus | 获得焦点时触发 | (event: Event) |
| blur | 失去焦点时触发 | (event: Event) |
| search | 搜索查询发生变化时触发 | 搜索查询字符串 |
OptionGroup 事件
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| collapse-change | 折叠状态发生变化时触发 | { collapsed: boolean, label: string } |
插槽
| 插槽名 | 说明 |
|---|---|
| default | Option 组件列表 |
| header | 下拉框头部内容 |
| footer | 下拉框底部内容 |
| empty | 无选项时的内容 |
| loading | 自定义加载文案 |