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 小时前
Vue3动态组件Component的深度解析与应用
前端·vue.js·web components
lbh9 小时前
当我开始像写代码一样和AI对话,一切都变了
前端·openai·ai编程
We་ct10 小时前
LeetCode 918. 环形子数组的最大和:两种解法详解
前端·数据结构·算法·leetcode·typescript·动态规划·取反
qq_4061761410 小时前
深入浅出 Pinia:Vue3 时代的状态管理新选择
javascript·vue.js·ecmascript
wefly201711 小时前
m3u8live.cn 在线M3U8播放器,免安装高效验流排错
前端·后端·python·音视频·前端开发工具
C澒11 小时前
微前端容器标准化 —— 公共能力篇:通用打印
前端·架构
德育处主任Pro11 小时前
前端元素转图片,dom-to-image-more入门教程
前端·javascript·vue.js
木斯佳11 小时前
前端八股文面经大全:小红书前端一二面OC(下)·(2026-03-17)·面经深度解析
前端·vue3·proxy·八股·响应式
陈天伟教授12 小时前
人工智能应用- 预测新冠病毒传染性:04. 中国:强力措施遏制疫情
前端·人工智能·安全·xss·csrf
叫我一声阿雷吧12 小时前
JS 入门通关手册(23):JS 异步编程:回调函数与异步本质
javascript·es6·前端面试·回调函数·回调地狱·js异步编程·异步本质