1.:deep
scoped 会限制样式仅作用于当前组件的元素,deep() 是用于穿透 scoped 样式作用域的语法,核心作用是修改子组件 / 第三方组件(如 Ant Design Vue)内部的样式。
当 <style> 标签加上 scoped 时,Vue 会给当前组件的所有元素自动添加一个唯一的属性(如 data-v-xxxxxx),样式会被编译为「选择器 + 该属性」的形式(比如 .my-class[data-v-xxxxxx])。
子组件 / 第三方组件(如 Ant Design 的 Pagination)的内部元素不会继承这个属性,所以直接写子组件的类名(比如 .ant-pagination-options)会失效 —— 此时需要用 :deep() 包裹选择器,让样式 “穿透” 到子组件内部。
<template>
<!-- 你的分页组件 -->
<Pagination
v-model:current="pageIndex"
v-model:pageSize="pageSize"
:total="total"
show-size-changer
/>
</template>
这是没有添加样式时源代码
<style lang="scss" scoped>
:deep(.ant-pagination-options) {
margin-left: 20px;
}
</style>
进行样式穿透之后,样式就被加入进去,实现效果:条数选择间距变大
关键注意事项
-
必须配合
scoped:只有<style scoped>才需要:deep,全局样式(无scoped)直接写选择器即可。 -
避免滥用:
:deep会让样式作用于子组件,过度使用可能导致样式污染(建议配合父类名精准定位,比如.custom-pagination:deep(...))。 -
选择器要精准:先通过浏览器开发者工具(F12)找到目标元素的类名(比如 Ant Design 分页的 “条数选择器” 类名是
.ant-pagination-options),再用:deep包裹。 -
若样式不生效,可在属性后加
!important强制覆盖。<Pagination class="custom-pagination" //额外增加类名,防止使用:deep穿透造成污染 v-model:current="pageIndex" v-model:pageSize="pageSize" :total="total" :show-total="(total) => `共 ${total} 条`" /> .custom-pagination:deep(.ant-pagination-options) { margin-left: 80px; }
效果:
原本分页展示:
改自己的子组件样式
如果一个自定义子组件 <ChildComp>,其内部有类名 .child-item,父组件要修改它:
<template>
<!-- 父组件中使用子组件 -->
<ChildComp />
</template>
<style scoped>
/* 穿透到 ChildComp 内部,修改 .child-item 的样式 */
:deep(.child-item) {
color: red; /* 子组件的 .child-item 文字变成红色 */
}
</style>
样式预处理器兼容(SCSS/Sass)
SCSS/Sass,:deep 的写法不变
<style scoped lang="scss">
.custom-pagination {
// 嵌套写法也支持
:deep(..custom-pagination) {
margin-right: 10px;
&:hover {
border-color: red; // hover时的边框颜色
}
}
}
</style>
Vue2 与 Vue3 的写法区别
- Vue2 中是
/deep/、::v-deep、>>>(不同预处理器写法不同)。 - Vue3 在
<style scoped>中统一推荐用:deep(),兼容性更好。
2.Transition
Vue3 中的 <Transition> 是内置的过渡动画组件,用于给 单个元素 / 组件 的 进入 / 离开 提供平滑动画效果(如淡入淡出、滑动等)。核心原理是通过动态添加 / 移除 CSS 类名,控制动画的生命周期。
1. 过渡生命周期与类名
Vue3 中过渡类名相比 Vue2 有调整(更语义化),共 6 个核心类名(默认前缀为 v-,可通过 name 属性自定义):
| 类名 | 作用时机 | 说明 |
|---|---|---|
v-enter-from | 进入动画开始前(初始状态) | 动画开始时添加,下一帧移除 |
v-enter-active | 进入动画进行中(过渡状态) | 动画开始时添加,动画结束后移除 |
v-enter-to | 进入动画结束后(目标状态) | 下一帧添加,动画结束后移除 |
v-leave-from | 离开动画开始前(初始状态) | 动画开始时添加,下一帧移除 |
v-leave-active | 离开动画进行中(过渡状态) | 动画开始时添加,动画结束后移除 |
v-leave-to | 离开动画结束后(目标状态) | 下一帧添加,动画结束后移除 |
简单示例:
<!-- 币别搜索过滤框:货币符合触发搜索时调用,在预览页面显示。 Transition vue组件 实现过渡效果 -->
<Transition name="fade">
<searching
v-if="showSearchFilter"
v-model:visible="showSearchFilter"
/>
</Transition>
/* 淡入淡出过渡效果 */
.fade-enter-from {
opacity: 0; /* 初始状态:完全透明 */
}
.fade-enter-active {
transition: opacity 0.3s ease-out; /* 进入动画:0.3 秒淡出效果 */
}
.fade-leave-from {
opacity: 1; /* 离开开始状态:完全不透明 */
}
.fade-leave-active {
transition: opacity 0.3s ease-in; /* 离开动画:0.3 秒淡入效果 */
opacity: 0; /* 离开结束状态:完全透明 */
}
2. 触发条件
- 条件渲染(
v-if/v-show) - 动态组件(
<component :is="xxx">) - 路由切换(结合
vue-router) - 元素的
key变化
<template>
<!-- 1. 用 Transition 包裹目标元素 -->
<Transition name="fade">
<!-- 2. 触发条件:v-if/v-show -->
<div v-if="isShow" class="box">过渡动画示例</div>
</Transition>
<button @click="isShow = !isShow">切换显示</button>
</template>
<script setup>
import { ref } from 'vue'
const isShow = ref(false) // 控制元素显示/隐藏
</script>
<!-- 3. 编写过渡动画 CSS -->
<style scoped>
.box {
width: 200px;
height: 200px;
background: #409eff;
color: white;
text-align: center;
line-height: 200px;
}
/* 进入动画:淡入 + 缩放 */
.fade-enter-from {
opacity: 0; /* 初始透明 */
transform: scale(0.8); /* 初始缩小 */
}
.fade-enter-active {
transition: all 0.5s ease; /* 过渡时长和曲线 */
}
.fade-enter-to {
opacity: 1; /* 结束不透明 */
transform: scale(1); /* 结束原尺寸 */
}
/* 离开动画:淡出 + 缩放 */
.fade-leave-from {
opacity: 1;
transform: scale(1);
}
.fade-leave-active {
transition: all 0.5s ease;
}
.fade-leave-to {
opacity: 0;
transform: scale(0.8);
}
</style>
name="fade":指定过渡类名前缀,此时类名从v-xxx变为fade-xxx(避免全局样式冲突)。- 必须包裹 单个根元素(若需多个元素,用
<div>包裹成一个根节点)。 - 动画由
transitionCSS 属性控制(也支持animation动画)。
3、使用 Animation 动画(而非 Transition)
如果需要更复杂的动画(如循环、关键帧),可使用 CSS animation 配合 <Transition>:
<template>
<Transition name="bounce">
<div v-if="isShow" class="box">动画示例</div>
</Transition>
</template>
<style scoped>
/* 定义关键帧动画 */
@keyframes bounce-in {
0% { transform: scale(0); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
@keyframes bounce-out {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(0); }
}
/* 进入动画:使用 animation */
.bounce-enter-active {
animation: bounce-in 0.5s ease;
}
/* 离开动画:使用 animation */
.bounce-leave-active {
animation: bounce-out 0.5s ease;
}
/* 可选:设置动画结束后的状态(避免闪回) */
.bounce-enter-to,
.bounce-leave-from {
transform: scale(1);
}
</style>
常见场景拓展
1. 动态组件过渡
给动态切换的组件加过渡:
<template>
<Transition name="fade" mode="out-in">
<component :is="currentComponent" key="currentComponent" />
</Transition>
<button @click="currentComponent = currentComponent === 'A' ? 'B' : 'A'">
切换组件
</button>
</template>
<script setup>
import { ref } from 'vue'
import A from './A.vue'
import B from './B.vue'
const currentComponent = ref('A')
</script>
mode="out-in":过渡模式,先执行离开动画,再执行进入动画(避免两个组件重叠)。- 必加
key:确保组件切换时触发过渡。
2. 路由过渡(结合 vue-router)
给路由切换加全局过渡:
<!-- App.vue -->
<template>
<router-view v-slot="{ Component }">
<Transition name="route-fade">
<component :is="Component" />
</Transition>
</router-view>
</template>
<style>
/* 路由过渡样式 */
.route-fade-enter-from,
.route-fade-leave-to {
opacity: 0;
transform: translateX(20px);
}
.route-fade-enter-active,
.route-fade-leave-active {
transition: all 0.3s ease;
}
</style>
3. 列表过渡(TransitionGroup)
<Transition> 仅支持单个元素,列表过渡需用 <TransitionGroup>(包裹多个元素,需给每个元素加 key):
<template>
<TransitionGroup name="list" tag="ul">
<li v-for="item in list" :key="item.id" class="list-item">
{{ item.name }}
</li>
</TransitionGroup>
<button @click="addItem">添加项</button>
</template>
<script setup>
import { ref } from 'vue'
const list = ref([{ id: 1, name: '项1' }, { id: 2, name: '项2' }])
const addItem = () => {
list.value.push({ id: Date.now(), name: `项${list.value.length + 1}` })
}
</script>
<style scoped>
ul { list-style: none; padding: 0; }
.list-item {
margin: 10px 0;
padding: 10px;
background: #f5f5f5;
}
/* 列表项过渡样式 */
.list-enter-from {
opacity: 0;
transform: translateY(10px);
}
.list-enter-active {
transition: all 0.3s ease;
}
.list-leave-active {
transition: all 0.3s ease;
opacity: 0;
transform: translateY(-10px);
}
</style>
tag="ul":指定<TransitionGroup>渲染为<ul>标签(默认不渲染根标签)。- 每个列表项必须有唯一
key。
4.JavaScript 钩子(控制复杂动画)
如果需要通过 JS 控制动画(如回调、异步动画),可使用 <Transition> 的钩子函数:
<template>
<Transition
@enter="onEnter"
@leave="onLeave"
:css="false" <!-- 禁用 CSS 过渡,完全由 JS 控制 -->
>
<div v-if="isShow" ref="boxRef" class="box">JS 控制动画</div>
</Transition>
</template>
<script setup>
import { ref } from 'vue'
const isShow = ref(false)
const boxRef = ref(null)
// 进入动画钩子
const onEnter = (el, done) => {
el.style.opacity = 0
el.style.transform = 'scale(0.8)'
// 用 requestAnimationFrame 触发动画
requestAnimationFrame(() => {
el.style.transition = 'all 0.5s ease'
el.style.opacity = 1
el.style.transform = 'scale(1)'
// 动画结束后调用 done() 通知 Vue
el.addEventListener('transitionend', done)
})
}
// 离开动画钩子
const onLeave = (el, done) => {
el.style.transition = 'all 0.5s ease'
el.style.opacity = 0
el.style.transform = 'scale(0.8)'
el.addEventListener('transitionend', done)
}
</script>
:css="false":必须设置,避免 Vue 自动添加 CSS 类名干扰。- 钩子函数的
done参数:必须在动画结束后调用(如transitionend事件),否则 Vue 会认为动画立即结束。
- 必须包裹单个根元素:
<Transition>只能有一个直接子元素(列表用<TransitionGroup>)。 key的重要性:动态组件、路由、列表项必须加key,否则 Vue 可能复用元素,不触发过渡。- 过渡模式:
mode="out-in"(先出后进)、mode="in-out"(先进后出),避免组件重叠。 - 样式作用域:如果用
scoped样式,需用::v-deep或:deep()穿透(如修改第三方组件的过渡样式)。 - 禁用过渡:通过
:disabled="true"动态禁用过渡(如某些条件下不需要动画)。
3.Select 的下拉弹窗
Select 是 Vue 生态Ant Design Vue 中最常用的下拉选择组件,提供更丰富的交互、样式定制和功能扩展,核心作用是让用户从预设选项中选择单个或多个值。
接收用户输入的 “选择型数据”,将预设的 options 选项以下拉面板形式展示,通过 v-model 双向绑定选中值,支持单选 / 多选、搜索、清空、禁用等增强功能。
<template>
<!-- 核心:v-model:value 绑定选中值,options 传入选项 -->
<el-select
v-model:value="selectedId" <!-- 绑定选中值(通常是 option 的 value) -->
placeholder="请选择"
size="small"
allow-clear <!-- 允许清空选中值 -->
:options="options" <!-- 选项数组 -->
@change="handleChange" <!-- 选中值变化时触发 -->
/>
</template>
<script setup>
import { ref } from 'vue';
// 选中值(与 option 的 value 类型一致,如 Number/String)
const selectedId = ref('');
// 选项数组:格式为 [{ label: '显示文本', value: '选中值', disabled?: true }]
const options = ref([
{ label: '选项1', value: '1' },
{ label: '选项2', value: '2', disabled: true }, // 禁用选项
{ label: '选项3', value: '3' }
]);
// 选中值变化触发的事件
const handleChange = (value, option) => {
console.log('选中值:', value); // 如 '1'(option 的 value)
console.log('选中的完整选项:', option); // 如 { label: '选项1', value: '1' }
};
</script>
多选(multiple 属性)
只需添加 multiple,v-model:value 会自动变为数组类型:
<el-select
v-model:value="selectedIds" <!-- 多选时绑定数组,如 ['1', '3'] -->
placeholder="请选择(可多选)"
multiple <!-- 开启多选 -->
:options="options"
@change="handleMultiChange"
/>
<script setup>
const selectedIds = ref([]); // 多选绑定数组
const handleMultiChange = (values, options) => {
console.log('选中的所有值:', values); // 如 ['1', '3']
console.log('选中的所有选项:', options); // 如 [{ label: '选项1', value: '1' }, ...]
};
</script>
自定义 label/value 字段
<el-select
v-model:value="selectedUserId"
:options="userList" <!-- 选项格式:[{ id: 1, name: '张三' }, { id: 2, name: '李四' }] -->
label-key="name" <!-- 用 name 作为显示文本 -->
value-key="id" <!-- 用 id 作为绑定值 -->
/>
数据绑定相关
| 属性名 | 类型 | 说明 |
|---|---|---|
v-model:value | 单选:String/Number多选:Array | 双向绑定选中值(Element Plus 用 v-model:value,AntD Vue 直接用 v-model) |
options | Array | 下拉选项数组,格式:[{ label: 显示文本, value: 选中值, disabled?: Boolean, ... }] |
label-key | String | 自定义选项 “显示文本” 的字段名(默认是 label),如 label-key="name" 则用 option.name 显示 |
value-key | String | 自定义选项 “选中值” 的字段名(默认是 value),如 value-key="id" 则用 option.id 作为绑 |
外观与交互配置
| 属性名 | 类型 | 说明 |
|---|---|---|
placeholder | String | 未选中时的提示文本 |
size | String | 组件尺寸:small/medium/large(Element Plus);small/middle/large(AntD Vue) |
allow-clear | Boolean | 是否显示 “清空按钮”,点击后重置 v-model 为默认值(单选:空字符串 /undefined;多选:空数组) |
disabled | Boolean | 禁用整个组件,不可点击选择 |
readonly | Boolean | 只读状态,显示选中值但不可修改 |
下拉面板相关
| 属性名 | 类型 | 说明 |
|---|---|---|
dropdown-style | Object/String | 自定义下拉面板样式(内联 CSS),如之前的 { position: 'fixed', zIndex: 2000 } |
dropdown-class | String | 给下拉面板添加自定义 CSS 类(比 dropdown-style 更适合复杂样式) |
popper-append-to-body(Element Plus)/ getPopupContainer(AntD Vue) | Boolean/Function | 下拉面板是否挂载到 <body> 元素下(默认 true,解决父容器 overflow: hidden 导致的截断问题) |
z-index | Number | 下拉面板的层级(优先级:dropdown-style.zIndex > 组件 z-index) |
搜索与过滤相关
| 属性名 | 类型 | 说明 |
|---|---|---|
filterable | Boolean | 开启下拉选项搜索功能(输入关键词过滤选项) |
filter-method | Function | 自定义搜索逻辑,参数:(keyword, option) => Boolean(返回 true 则选项显示) |
remote | Boolean | 开启远程搜索(适用于选项过多,需要后端接口查询的场景) |
remote-method | Function | 远程搜索触发的方法,参数:keyword(用户输入的关键词),需手动更新 options |
解决下拉面板被截断 / 遮挡
<!-- 方案1:用 dropdown-style 固定定位+提高层级 -->
<el-select
:dropdown-style="{ position: 'fixed', zIndex: 3000 }"
/>
<!-- 方案2:强制挂载到 body(默认已开启,如需关闭再开启) -->
<el-select
popper-append-to-body="true"
/>
<!-- 方案3:自定义下拉面板挂载容器(如挂载到某个 div 下) -->
<el-select
:get-popup-container="() => document.getElementById('container')"
/>
远程搜索(后端接口查询选项)
<el-select
v-model:value="selectedValue"
filterable
remote <!-- 开启远程搜索 -->
:remote-method="fetchRemoteOptions" <!-- 输入关键词触发接口请求 -->
:loading="isLoading" <!-- 显示加载状态 -->
:options="remoteOptions"
placeholder="搜索选项..."
/>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const isLoading = ref(false);
const remoteOptions = ref([]);
// 远程搜索方法
const fetchRemoteOptions = async (keyword) => {
isLoading.value = true;
try {
// 调用后端接口,传入关键词查询选项
const res = await axios.get('/api/options', { params: { keyword } });
remoteOptions.value = res.data.map(item => ({
label: item.name,
value: item.id
}));
} catch (err) {
console.error('远程搜索失败:', err);
} finally {
isLoading.value = false;
}
};
</script>
常用事件(交互反馈)
| 事件名 | 触发时机 | 回调参数 |
|---|---|---|
@change | 选中值发生变化时(单选 / 多选均触发) | 单选:(value, option)多选:(values, options) |
@focus | 组件获得焦点时(点击输入框) | - |
@blur | 组件失去焦点时(点击其他区域) | - |
@clear | 点击 “清空按钮” 时(需开启 allow-clear) | - |
@visible-change | 下拉面板显示 / 隐藏时 | visible(Boolean,true 显示,false 隐藏) |
@select | 点击某个选项时(无论是否变化) | option(选中的选项对象) |
监听下拉面板显示 / 隐藏
<el-select
@visible-change="handleVisibleChange"
/>
<script setup>
const handleVisibleChange = (visible) => {
if (visible) {
console.log('下拉面板打开了,可以在这里加载选项数据');
// 比如:fetchOptions(); // 懒加载选项
} else {
console.log('下拉面板关闭了');
}
};
</script>
自定义渲染
1. 自定义单个选项(Element Plus:#option 插槽)
<el-select v-model:value="selectedValue" :options="options">
<!-- 自定义每个选项的渲染结构 -->
<template #option="scope">
<!-- scope 包含当前选项的所有属性:scope.option(选项对象)、scope.index(索引) -->
<div class="custom-option">
<!-- 带图标 -->
<el-icon style="margin-right: 8px;">
<User />
</el-icon>
<!-- 显示选项文本和额外信息 -->
<span>{{ scope.option.label }}</span>
<span class="option-desc">(ID:{{ scope.option.value }})</span>
</div>
</template>
</el-select>
<style scoped>
.custom-option {
display: flex;
align-items: center;
}
.option-desc {
color: #999;
font-size: 12px;
margin-left: 8px;
}
</style>
2. 选项分组(用 el-option-group 或 options 嵌套格式)
<!-- 方式1:用组件嵌套 -->
<el-select v-model:value="selectedValue">
<el-option-group label="分组1">
<el-option label="选项1" value="1"></el-option>
<el-option label="选项2" value="2"></el-option>
</el-option-group>
<el-option-group label="分组2">
<el-option label="选项3" value="3"></el-option>
</el-option-group>
</el-select>
<!-- 方式2:用 options 嵌套格式(更简洁) -->
<el-select
v-model:value="selectedValue"
:options="[
{
label: '分组1',
options: [{ label: '选项1', value: '1' }, { label: '选项2', value: '2' }]
},
{
label: '分组2',
options: [{ label: '选项3', value: '3' }]
}
]"
常见问题与解决方案
下拉面板被父容器截断或被其他元素遮挡
- 原因:父容器设置了
overflow: hidden,下拉面板(默认position: absolute)被裁剪。
用 fixed 定位脱离父容器,设置较高的zIndex
:dropdown-style="{ position: 'fixed', zIndex: 2000 }"
多选时,v-model 绑定的值不是数组?
原因:未添加 multiple 属性,或初始化时 v-model 不是数组
<el-select multiple v-model:value="selectedIds" />
<script setup>
const selectedIds = ref([]); // 必须初始化为数组
</script>
远程搜索时,输入关键词不触发接口请求
原因:未开启 remote 属性,或 remote-method 未正确绑定
<el-select
filterable
remote <!-- 必须开启 remote -->
:remote-method="fetchRemoteOptions" <!-- 绑定远程搜索方法 -->
/>
选项的 label 是 HTML 文本,如何渲染成富文本
原因:默认 label 会被转义为纯文本,需关闭转义
<el-select
v-model:value="selectedValue"
:options="options"
:render-label="(option) => h('span', { innerHTML: option.label })"
/>
<!-- 或用插槽 -->
<template #option="scope">
<span v-html="scope.option.label"></span>
</template>
4.template #xxx
具名插槽(Named Slot)作用是自定义 UI 组件:template #xxx 是 v-slot:xxx 的简写,专门用于具名插槽(组件提前定义好名称的 “预留位置”)
#suffix 对应的是组件的后缀区域—— 通常在输入框(或 Select 输入框)的右侧,输入内容的后面,是组件预留的 “附加功能位置”(默认可能为空,或显示组件自带的图标)。
<template #suffix>
<SearchOutlined
v-if="!input" <!-- 条件渲染:当 input 为假值时显示 -->
class="search-icon" <!-- 自定义图标样式类 -->
@click.stop.prevent="More" <!-- 点击事件 + 事件修饰符 -->
/>
</template>
# 后面的字段(比如 clearIcon)是 组件预先定义好的 “插槽名称” ,必须和组件开发者预留的插槽名称完全一致,才能被组件识别并正确渲染 —— 随便写一个不存在的名称,插槽内容会 “失效”(不显示、不生效)。
具名插槽的 “约定性”
template #xxx 是 具名插槽 的语法,这里的 xxx 不是你自定义的变量,而是组件对外暴露的 “接口名称”—— 就像你去餐厅吃饭,只能坐餐厅预留的 “座位号”(1 号桌、2 号桌)。
组件开发者在设计组件时,会明确规定哪些插槽名称是可用的(比如 clearIcon、suffix、prefix 等),并在文档中说明每个插槽的作用(比如 clearIcon 对应清空图标的位置,suffix 对应输入框后缀位置)。只有你的插槽名称和组件预留的名称完全匹配,组件才知道 “把你写的内容(比如 CloseCircleOutlined 图标)渲染到哪个位置”。
官方文档中明确列出了支持的具名插槽(部分):
prefix:输入框前缀位置suffix:输入框后缀位置clearIcon:清空图标位置option:下拉选项自定义渲染empty:无数据时的提示内容
如果代码写随便加一个不存在的名称:
<!-- 错误:#myIcon 是自定义名称,ElSelect 没有预留这个插槽 -->
<template #myIcon>
<CloseCircleOutlined @click="clear" />
</template>
结果会是:这个 CloseCircleOutlined 图标不会显示在任何位置—— 因为 ElSelect 根本不认识 myIcon 这个插槽名称,不知道该把图标渲染到哪
5.design-vue (antd-vue) Select
v-model:value="selection" 是 Vue3 中针对表单组件的双向数据绑定语法糖,作用是让 selection 变量与 Select 组件的「选中值」实现双向同步
-
当
selection的值主动修改时,Select 组件的选中状态会自动同步更新; -
当用户在 Select 组件中操作(选择 / 取消选项、清空、搜索选择等)导致选中值变化时,
selection变量会被自动更新为新的选中值。 -
v-model:value而非直接v-model的原因:Vue3 中v-model默认绑定的是组件的modelValue属性,而 antd-vue 的 Select 组件将「选中值」的属性名定义为value(而非modelValue),因此需要显式指定v-model:value来匹配组件的属性名。
<Select v-model:value="selection" />
// 等价于:
<Select :value="selection" @update:value="val => selection = val" />
简单示例:
<template>
<Select
v-model:value="selection"
mode="multiple"
:options="[{ label: '选项1', value: '1' }, { label: '选项2', value: '2' }]"
/>
<!-- 实时显示选中值 -->
<div>当前选中值:{{ selection }}</div>
<!-- 手动修改选中值 -->
<button @click="selection = ['1']">选中选项1</button>
</template>
<script setup>
import { ref } from 'vue'
// 定义绑定的变量(多选模式用数组,单选用字符串/数字)
const selection = ref([])
</script>
mode="multiple" 是控制下拉选择器多选功能的核心属性
-
启用多选模式:设置后用户可同时选择多个选项,选中值将以数组形式存储(如
[1, 3, 5]) -
UI表现变化:
- 选中项会以标签(Tag)形式展示在输入框内
- 支持通过点击标签的删除按钮取消选择
// 1. 初始化选中值
selection.value = props.multiple ?
(Array.isArray(props.modelValue) ? props.modelValue : []) : // 多选→数组
props.modelValue // 单选→基础类型
// 2. 处理选中逻辑
if (props.multiple) {
// 多选:筛选所有选中项的label并拼接
const selectedItems = selectOptions.value.filter(item =>
val.includes(item[selectProps.value])
)
selectedLabels = selectedItems.map(item => item[selectProps.label]).join(',')
}
单选多选切换
:mode="multiple ? 'multiple' : 'single'"
:mode="multiple ? 'multiple' : 'single'" 是通过三元运算符 动态控制 antd-vue Select 组件的选择模
- 当变量
multiple的值为true时,Select 组件启用「多选模式」(mode="multiple"); - 当变量
multiple的值为false时,Select 组件启用「单选模式」(mode="single")。
mode 是 antd-vue Select 组件原生预设的属性(官方文档明确定义),用来指定选择模式,其可选值是组件固定的(核心可选值:single/multiple,还有 tags/combobox 等扩展值),不能自定义 mode 的属性名,也不能传入非预设的取值(比如写 mode="abc" 会无效)。
multiple 是「自定义变量」,可灵活更改
这里的 multiple 是自己定义的变量(组件的 props 入参、内部 ref 变量,或父组件传递的参数),属于自定义逻辑层的参数,完全可以动态修改:
- 比如通过
props让父组件控制:props: { multiple: { type: Boolean, default: false } }; - 比如内部通过按钮切换:
const multiple = ref(false); const toggleMode = () => multiple.value = !multiple.value;。
1. 当 multiple = true 时
mode="multiple" → Select 进入多选模式:
- 可选中多个选项,选中值
selection是数组类型(比如['1', '2']); - 选中的选项会以 “标签(Tag)” 形式显示在输入框内,可单独删除某一个;
- 组件交互逻辑适配多选(比如回车选中、取消选中)。
2. 当 multiple = false 时
mode="single" → Select 进入单选模式:
- 只能选中一个选项,选中值
selection是单个值(比如'1'); - 输入框内只显示当前选中的选项文本,无法单独删除(只能重新选或清空);
- 组件交互逻辑适配单选(选中新选项会覆盖旧选项)。
| 值 | 说明 |
|---|---|
single | 单选(默认值) |
multiple | 多选 |
tags | 标签模式(可输入新增) |
combobox | 组合框(可输入筛选) |