vue3+ant-design-vue

94 阅读13分钟

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>

这是没有添加样式时源代码

企业微信截图_17642939291330.png

<style lang="scss" scoped>
    :deep(.ant-pagination-options) {
        margin-left: 20px;
    }
</style>

进行样式穿透之后,样式就被加入进去,实现效果:条数选择间距变大 企业微信截图_1764293991149.png

关键注意事项

  1. 必须配合 scoped:只有 <style scoped> 才需要 :deep,全局样式(无 scoped)直接写选择器即可。

  2. 避免滥用:deep 会让样式作用于子组件,过度使用可能导致样式污染(建议配合父类名精准定位,比如 .custom-pagination:deep(...))。

  3. 选择器要精准:先通过浏览器开发者工具(F12)找到目标元素的类名(比如 Ant Design 分页的 “条数选择器” 类名是 .ant-pagination-options),再用 :deep 包裹。

  4. 若样式不生效,可在属性后加 !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;
    }
    
    

企业微信截图_17642944127493.png

效果:

企业微信截图_17642944306831.png

原本分页展示:

企业微信截图_176429448418.png

改自己的子组件样式

如果一个自定义子组件 <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> 包裹成一个根节点)。
  • 动画由 transition CSS 属性控制(也支持 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 会认为动画立即结束。
  1. 必须包裹单个根元素<Transition> 只能有一个直接子元素(列表用 <TransitionGroup>)。
  2. key 的重要性:动态组件、路由、列表项必须加 key,否则 Vue 可能复用元素,不触发过渡。
  3. 过渡模式mode="out-in"(先出后进)、mode="in-out"(先进后出),避免组件重叠。
  4. 样式作用域:如果用 scoped 样式,需用 ::v-deep 或 :deep() 穿透(如修改第三方组件的过渡样式)。
  5. 禁用过渡:通过 :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 属性)

只需添加 multiplev-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
optionsArray下拉选项数组,格式:[{ label: 显示文本, value: 选中值, disabled?: Boolean, ... }]
label-keyString自定义选项 “显示文本” 的字段名(默认是 label),如 label-key="name" 则用 option.name 显示
value-keyString自定义选项 “选中值” 的字段名(默认是 value),如 value-key="id" 则用 option.id 作为绑

外观与交互配置

属性名类型说明
placeholderString未选中时的提示文本
sizeString组件尺寸:small/medium/large(Element Plus);small/middle/large(AntD Vue)
allow-clearBoolean是否显示 “清空按钮”,点击后重置 v-model 为默认值(单选:空字符串 /undefined;多选:空数组)
disabledBoolean禁用整个组件,不可点击选择
readonlyBoolean只读状态,显示选中值但不可修改

下拉面板相关

属性名类型说明
dropdown-styleObject/String自定义下拉面板样式(内联 CSS),如之前的 { position: 'fixed', zIndex: 2000 }
dropdown-classString给下拉面板添加自定义 CSS 类(比 dropdown-style 更适合复杂样式)
popper-append-to-body(Element Plus)/ getPopupContainer(AntD Vue)Boolean/Function下拉面板是否挂载到 <body> 元素下(默认 true,解决父容器 overflow: hidden 导致的截断问题)
z-indexNumber下拉面板的层级(优先级:dropdown-style.zIndex > 组件 z-index

搜索与过滤相关

属性名类型说明
filterableBoolean开启下拉选项搜索功能(输入关键词过滤选项)
filter-methodFunction自定义搜索逻辑,参数:(keyword, option) => Boolean(返回 true 则选项显示)
remoteBoolean开启远程搜索(适用于选项过多,需要后端接口查询的场景)
remote-methodFunction远程搜索触发的方法,参数: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 号桌)。

组件开发者在设计组件时,会明确规定哪些插槽名称是可用的(比如 clearIconsuffixprefix 等),并在文档中说明每个插槽的作用(比如 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 组件的「选中值」实现双向同步

  1. 当 selection 的值主动修改时,Select 组件的选中状态会自动同步更新;

  2. 当用户在 Select 组件中操作(选择 / 取消选项、清空、搜索选择等)导致选中值变化时,selection 变量会被自动更新为新的选中值。

  3. 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组合框(可输入筛选)