el-select 下拉框支持线上 SVG + 本地图片图标 展示

Vue3 + Element Plus 实现带图标的多选下拉框(附完整代码)

在数据可视化、BI 系统或报表配置页面中,我们经常需要让用户从多个"维度"字段中进行选择。为了提升用户体验和界面美观度,为每个选项添加图标是一个非常实用的设计。

本文将手把手教你使用 Vue 3 + <script setup> 语法 + Element Plus 实现一个支持图标显示、可搜索、可清除的带图标的维度多选下拉框,并展示如何自定义选项标签与下拉项的渲染内容。


🎯 效果预览

  • 支持多选
  • 每个选项左侧带有语义化图标(如用户、日历、地图等)
  • 已选项在输入框中同样显示图标和中文名+英文名
  • 支持搜索过滤
  • 自动排除类型为 "3" 的字段(模拟"指标"字段不作为维度)

💡 技术栈说明

  • Vue 3 :使用 Composition API + <script setup> 语法糖
  • Element Plus<el-select><el-option> 组件
  • Iconify:通过 CDN 动态加载 SVG 图标(无需额外安装图标库)

🧩 完整代码实现

1. 模板部分(Template)

vue 复制代码
<template>
  <div style="padding: 20px; max-width: 600px">
    <h3>带图标的维度多选下拉框</h3>
    <el-select
      v-model="selectedDimensions"
      multiple
      clearable
      filterable
      placeholder="请选择维度"
      class="dimension-select"
    >
      <!-- 自定义已选项的标签显示 -->
      <template #label="{ value }">
        <div class="dimension-option">
          <img :src="getDimensionIcon(value)" class="dimension-icon" />
          <span>{{ getDimensionLabel(value) }}</span>
        </div>
      </template>

      <!-- 下拉选项列表 -->
      <el-option
        v-for="item in dimensionOptions"
        :key="item.columnName"
        :value="item.columnName"
      >
        <div class="dimension-option">
          <img
            :src="getDimensionIcon(item.columnName)"
            class="dimension-icon"
          />
          <span>{{
            item.cnName ? `${item.cnName}(${item.columnName})` : item.columnName
          }}</span>
        </div>
      </el-option>
    </el-select>

    <p style="margin-top: 20px">已选:{{ selectedDimensions }}</p>
  </div>
</template>

✅ 关键点:

  • 使用 #label 插槽自定义已选项的显示内容(带图标)
  • el-option 内部也使用相同结构保持 UI 一致

2. 逻辑部分(Script Setup)

vue 复制代码
<script setup>
import { ref, computed } from "vue";
import { ElSelect, ElOption } from "element-plus";

// 模拟维度/指标字段列表
const measureOptions = ref([
  { columnName: "user_id", cnName: "用户ID", type: "1", icon: "user" },
  {
    columnName: "create_time",
    cnName: "创建时间",
    type: "2",
    icon: "calendar",
  },
  { columnName: "region", cnName: "地区", type: "1", icon: "location" },
  { columnName: "status", cnName: "状态", type: "1", icon: "default" },
]);

// 当前选中的维度
const selectedDimensions = ref([]);

// 过滤出 type !== "3" 的字段作为维度选项(假设 type=3 是指标)
const dimensionOptions = computed(() =>
  measureOptions.value.filter((item) => item.type !== "3")
);

// 获取显示文本
const getDimensionLabel = (columnName) => {
  const dim = measureOptions.value.find((d) => d.columnName === columnName);
  return dim
    ? dim.cnName
      ? `${dim.cnName}(${dim.columnName})`
      : dim.columnName
    : columnName;
};

// 根据 icon 字段映射到实际图标 URL(使用 Iconify 免费 CDN)
const getDimensionIcon = (columnName) => {
  const dim = measureOptions.value.find((d) => d.columnName === columnName);
  const name = dim?.icon || "default";
  const iconMap = {
    user: "https://api.iconify.design/mdi:user.svg",
    calendar: "https://api.iconify.design/mdi:calendar-month.svg",
    location: "https://api.iconify.design/mdi:map-marker.svg",
    default: "https://api.iconify.design/mdi:help-circle.svg",
  };
  return iconMap[name] || iconMap.default;
};
</script>

🔍 说明:

  • type 字段用于区分维度(dimension)和指标(measure),这里仅展示维度
  • 图标通过 Iconify 的 CDN 直接加载 SVG,轻量且无需打包

3. 样式部分(Scoped CSS)

vue 复制代码
<style scoped>
.dimension-select {
  width: 100%;
}

.dimension-option {
  display: flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}

.dimension-icon {
  width: 16px;
  height: 16px;
  object-fit: contain;
  vertical-align: middle;
}
</style>

✨ 小技巧:

  • 使用 gap 替代 margin 更简洁
  • object-fit: contain 确保图标比例不失真

🛠️ 扩展建议

  1. 图标本地化 :若项目对网络依赖敏感,可将 SVG 图标下载后放入 assets/icons 目录,通过 import 引入。
  2. 动态图标组件 :可封装 <DimensionIcon name="user" /> 组件,内部根据 name 渲染不同图标。
  3. 性能优化 :当选项非常多时,可考虑虚拟滚动(Element Plus 的 virtualized 属性)。
  4. 国际化支持cnName 可替换为 $t('dimension.user_id') 实现多语言。

✅ 总结

通过 Element Plus 的插槽机制(特别是 #label 插槽),我们可以轻松实现高度定制化的下拉选择器。结合语义化图标,不仅提升了界面美观度,也增强了用户对字段含义的理解。

这种"带图标的多选维度选择器"非常适合用在:

  • 报表配置面板
  • 数据看板筛选器
  • OLAP 查询构建器
  • 自助分析工具

希望本文对你有帮助!欢迎点赞、收藏、评论交流 👇

相关推荐
超绝大帅哥1 分钟前
为什么回调函数不是一种好的异步编程方式
javascript
不务正业的前端学徒5 分钟前
手写简单的call bind apply
前端
jump_jump8 分钟前
Ripple:一个现代的响应式 UI 框架
前端·javascript·前端框架
用户9047066835716 分钟前
Nuxt css 如何写?
前端
神秘的猪头16 分钟前
🎨 CSS 这种“烂大街”的技术,怎么在 React 和 Vue 里玩出花来?—— 模块化 CSS 深度避坑指南
css·vue.js·react.js
夏天想17 分钟前
element-plus的输入数字组件el-input-number 显示了 加减按钮(+ -) 和 小三角箭头(上下箭头),怎么去掉+,-或者箭头
前端·javascript·vue.js
0思必得018 分钟前
[Web自动化] Selenium基础介绍
前端·python·selenium·自动化·web自动化
Filotimo_20 分钟前
前端.d.ts文件作用
前端
进击的野人20 分钟前
Vue 3 响应式数据解构:toRef 与 toRefs 的深度解析
前端·vue.js·前端框架
ohyeah21 分钟前
CSS 作用域隔离实战:React、Vue 与 Styled Components 的三种范式
前端