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 查询构建器
  • 自助分析工具

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

相关推荐
L、2182 小时前
Flutter + OpenHarmony 分布式能力融合:实现跨设备 UI 共享与协同控制(终极篇)
javascript·分布式·flutter·ui·智能手机·harmonyos
Hao_Harrision2 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨| FAQ Collapse(问题解答折叠面板)
前端·typescript·react·vite7·tailwildcss
fruge2 小时前
深入理解 JavaScript 事件循环:宏任务与微任务的执行机制
开发语言·javascript·ecmascript
Youyzq2 小时前
css样式用flex 布局的时候元素尺寸展示不对
前端·javascript·css
cc蒲公英2 小时前
less和sass区别
前端·less·sass
小明记账簿2 小时前
利用 Less 循环高效生成多组 CSS 间距工具类
前端·css·less
yilan_n2 小时前
鸿蒙应用上传
vue.js·华为·uni-app
请叫我欧皇i3 小时前
免费开源!Vue2 + OpenStreetMap 打造动态地图:标记点与弹窗高级定制
前端·vue.js·开源
大雨倾城3 小时前
网页端和桌面端的electron通信Webview
javascript·vue.js·react.js·electron