【Vue3】elementPlus图标和自定义侧边栏图标

本文介绍了如何在Vite项目中使用Element Plus图标,并展示了如何自定义SVG图标,包括引入、配置、全局注册及侧边栏中根据图标类型动态展示的技巧。同时覆盖了在Vue3 TypeScript环境中处理自定义SVG图标的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

使用elementPlus图标

自定义图标

侧边栏自定义图标的应用

vite+vue3+ts中使用自定义图标


使用elementPlus图标

在项目中引入

npm install @element-plus/icons-vue
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

// 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
}

方法一(动态使用图标):

<el-icon>
    <component :is="menu.meta.icon" class="icon"></component>
</el-icon>

方法二(直接使用图标):

然后直接使用 

<el-icon><Expand /></el-icon>

具体详细图标可查看:https://element-plus.gitee.io/zh-CN/component/icon.html

自定义图标

1、创建src/icons/svg目录,将自己的图标放入此文件夹内。

2、npm install svg-sprite-loader --save-dev                  (实现加载svg自定义组件)

     npm i svgo@1.0.5 svgo-loader@2.2.0 --save-dev              (自动去除图标中的fill)

3、配置vue.config.js,没有在项目根目录新建一个即可

const path = require("path");
function resolve (dir) {
  return path.join(__dirname, dir);
}

module.exports = {
  chainWebpack: config => {
    // 清除svg默认配置对./src/icons文件夹的处理
    config.module
      .rule("svg")
      .exclude.add(resolve("src/icons")) // icons存放地(svg放的地方)
      .end()

    // 添加新的rule处理./src/icons文件夹的svg文件
    config.module
      .rule("svg-sprite")
      .test(/\.svg$/)
      .include
      .add(resolve("src/icons"))
      .end()
      .use("svg-sprite-loader")
      .loader("svg-sprite-loader")
      .options({
        symbolId: "icon-[name]",
        include: ["src/assets/icons"]
      })
      .end()
     //去除fill
      .before("svg-sprite-loader")
      .use("svgo-loader")
      .loader("svgo-loader")
      .options({
        plugins: [
          { removeAttrs: { attrs: "fill" } }
        ]
      })
      .end()
  },
}

5、 创建src/components/svg,封装svg组件

<template>
  <div
    v-if="External"
    :style="styleExternalIcon"
    class="svg-external-icon svg-icon"
    v-bind="$attrs"
  />
  <svg v-else :class="svgClass" aria-hidden="true" v-bind="$attrs">
    <use :xlink:href="symbolId" />
  </svg>
</template>

<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
import { defineComponent, reactive, ref, computed } from 'vue'

export default defineComponent({
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  setup (props) {
    const External = computed(() => {
      return isExternal(props.iconClass)
    })
    const symbolId = computed(() => {
      return `#icon-${props.iconClass}`
    })
    const svgClass = computed(() => {
      if (props.className) {
        return 'svg-icon ' + props.className
      } else {
        return 'svg-icon'
      }
    })
    const styleExternalIcon = computed(() => {
      return {
        mask: `url(${props.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${props.iconClass}) no-repeat 50% 50%`
      }
    })

    return {
      External,
      symbolId,
      svgClass,
      styleExternalIcon,
    }
  }
})
</script>

<style scoped>
.svg-icon {
  width: 1.4em;
  height: 1.4em;
  margin: 0 0 0 2px;
  vertical-align: -0.1em;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>

其中utils/validate.ts

/**
 * Created by PanJiaChen on 16/11/18.
 */

/**
 * @param {string} path
 * @returns {Boolean}
 */
 export function isExternal(path: string) {
    const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
    return isExternal;
  }
  

6、使用全局注册,这样就不用图标一个一个引入了,创建src/icons/index.ts

import SvgIcon from '@/components/svg/index.vue' // svg component

/**
 * 一次性的引入某个文件夹下的所有文件
* require.context(directory, useSubdirectories,regExp)
* 形参: 
* directory:需要引入文件的目录
* useSubdirectories:是否查找该目录下的子级目录
* regExp:匹配引入文件的正则表达式
*/

const registerSvgIcon = (app: any) => {
    app.component('svg-icon', SvgIcon) // 注册全局组件

    const req = require.context('./svg', false, /\.svg$/);
    const requireAll = (requireContext: any) => requireContext.keys().map(requireContext);
    requireAll(req);
}

export default registerSvgIcon;

5、main.ts引入

import registerSvgIcon from "@/icons/index";
const app = createApp(App)
registerSvgIcon(app)

6、页面使用

在所需页面中也需要引入svgIcon组件

<SvgIcon icon-class="yonghu"/>

侧边栏自定义图标的应用

侧边栏使用图标的时候需要判断一下,使用的是自定义图标还是elementPlus图标。

像elementUI图标他每一个图标都是以el-icon开头的,所以只要判断一下图标是否包含‘el-icon’,不包含的就是自定义图标。而elementPlus图标相反,所以我们可以在自定义图标前添加一个标识即可。 

我这里没有使用render进行对图标封装,因为我还没弄明白怎么返回elementPlus图标,所以有大佬如果知道,还请留言指教

重点代码:

<!-- 判断有没有图标并且是否是自定义图标,有就展示,没有则去除icon的位置 -->
          <SvgIcon
            v-if="
              item.meta && item.meta.icon && item.meta.icon.includes('icon-')
            "
            :icon-class="item.meta.icon"
          />
          <el-icon v-else>
            <component
              v-if="item.meta && item.meta.icon"
              :is="item.meta.icon"
              class="icon"
            ></component>
          </el-icon>

完整代码:

<template>
  <div v-if="!item.meta || !item.meta.hide">
    <!-- 一级 -->
    <template
      v-if="
        hasOneShowingChild(item.children, item) &&
        (!onlyOneChild.children || onlyOneChild.noShowingChildren) &&
        (!item.meta || !item.alwaysShow)
      "
    >
      <app-link v-if="item.meta" :to="item.path">
        <el-menu-item
          :index="item.children ? item.children[0].path : item.path"
          :class="{ 'submenu-title-noDropdown': item.class_true }"
        >
          <!-- 判断有没有图标并且是否是自定义图标,有就展示,没有则去除icon的位置 -->
          <SvgIcon
            v-if="
              item.meta && item.meta.icon && item.meta.icon.includes('icon-')
            "
            :icon-class="item.meta.icon"
          />
          <el-icon v-else>
            <component
              v-if="item.meta && item.meta.icon"
              :is="item.meta.icon"
              class="icon"
            ></component>
          </el-icon>

          <template #title>
            {{ item.meta.title }}
          </template>
        </el-menu-item>
      </app-link>
    </template>
    <!-- 二级 -->
    <template v-else>
      <el-sub-menu ref="subMenu" :index="item.path" popper-append-to-body>
        <template #title>
          <SvgIcon
            v-if="
              item.meta && item.meta.icon && item.meta.icon.includes('icon-')
            "
            :icon-class="item.meta.icon"
          />
          <el-icon v-else>
            <component
              v-if="item.meta && item.meta.icon"
              :is="item.meta.icon"
              class="icon"
            ></component>
          </el-icon>
          <span>{{ item.meta.title }}</span>
        </template>
        <el-menu-item-group>
          <SidebarItem
            v-for="v in item.children"
            :item="v"
            :key="v.path"
            :base-path="v.path"
            :is-nest="true"
          ></SidebarItem>
        </el-menu-item-group>
      </el-sub-menu>
    </template>
  </div>
</template>

<script>
import { defineComponent, reactive, ref } from 'vue'
import AppLink from './Link.vue'
import SvgIcon from "@/components/svg/index.vue";
import Item from './item.vue'

export default defineComponent({
  name: 'SidebarItem',
  components: {
    AppLink,
    SvgIcon,
    Item
  },
  props: {
    item: {
      type: Object,
      required: true,
    },
    isNest: {
      type: Boolean,
      required: false,
    },
    basePath: {
      type: String,
      required: true,
    },
  },
  setup () {
    const onlyOneChild = ref();

    function hasOneShowingChild (children, parent) {
      if (!children) {
        children = [];
      }
      const showingChildren = children.filter((item) => {
        if (item.meta && item.meta.hide) {
          return false;
        } else {
          // Temp set(will be used if only has one showing child)
          onlyOneChild.value = item;
          return true;
        }
      });

      // When there is only one child router, the child router is displayed by default
      if (showingChildren.length === 1) {
        return true;
      }

      // Show parent if there are no child router to display
      if (showingChildren.length === 0) {
        onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
        return true;
      }

      return false;
    }

    return {
      onlyOneChild,
      hasOneShowingChild,
    }
  }
})
</script>

<style lang="scss">
.icon {
  font-size: 20px;
}

.submenu-title-noDropdown {
  color: #3fa9f5;
}
</style>

vite+vue3+ts中使用自定义图标

1、创建src/icons/svg目录,将自己的图标放入此文件夹内。

2、下载插件

npm i vite-plugin-svg-icons -D

3、vite.config.ts 中的配置插件

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path";
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    createSvgIconsPlugin({
      // 指定需要缓存的图标文件夹
      iconDirs: [path.resolve(process.cwd(), 'src/icons')],
      // 指定symbolId格式
      symbolId: 'icon-[dir]-[name]',
    }),
  ],
})

4、main.ts

import 'virtual:svg-icons-register'

5、 创建src/components/svg,封装svg组件

<template>
  <div
    v-if="External"
    :style="styleExternalIcon"
    class="svg-external-icon svg-icon"
    v-bind="$attrs"
  />
  <svg v-else :class="svgClass" aria-hidden="true" v-bind="$attrs">
    <use :xlink:href="symbolId" />
  </svg>
</template>

<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
import { defineComponent, reactive, ref, computed } from 'vue'

export default defineComponent({
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  setup (props) {
    const External = computed(() => {
      return isExternal(props.iconClass)
    })
    const symbolId = computed(() => {
      return `#icon-${props.iconClass}`
    })
    const svgClass = computed(() => {
      if (props.className) {
        return 'svg-icon ' + props.className
      } else {
        return 'svg-icon'
      }
    })
    const styleExternalIcon = computed(() => {
      return {
        mask: `url(${props.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${props.iconClass}) no-repeat 50% 50%`
      }
    })

    return {
      External,
      symbolId,
      svgClass,
      styleExternalIcon,
    }
  }
})
</script>

<style scoped>
.svg-icon {
  width: 1.4em;
  height: 1.4em;
  margin: 0 0 0 2px;
  vertical-align: -0.1em;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>

 其中utils/validate.ts

/**
 * Created by PanJiaChen on 16/11/18.
 */

/**
 * @param {string} path
 * @returns {Boolean}
 */
 export function isExternal(path: string) {
    const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
    return isExternal;
  }
  

6、全局注册mian.ts

import 'virtual:svg-icons-register'
import SvgIcon from "@/components/svg/index.vue";

const app = createApp(App)
app.use(store)
    .use(router)
    .use(ElementPlus)
    .component('svg-icon', SvgIcon)
    .mount("#app");

7、页面使用

 在所需页面中也需要引入svgIcon组件

<SvgIcon icon-class="yonghu"/>
### Vue3 Element Plus 实现可折叠导航栏设计 #### 创建项目并安装依赖库 为了创建基于 Vue3 的应用,需先初始化一个新的 Vue 项目,并安装 `element-plus` 库。 ```bash npm init vue@latest cd project-name npm install --save element-plus ``` #### 配置全局引入 Element Plus 在项目的入口文件 main.js 中配置全局样式以及按需加载组件: ```javascript import { createApp } from &#39;vue&#39; import App from &#39;./App.vue&#39; // Import the entire library and its CSS file. import ElementPlus from &#39;element-plus&#39;; import &#39;element-plus/dist/index.css&#39;; const app = createApp(App); app.use(ElementPlus); app.mount(&#39;#app&#39;); ``` #### 定义侧边栏数据结构 定义一个包含路径、名称、组件懒加载函数及元信息的对象数组作为菜单项列表。其中 `meta.icon` 字段指定图标的类名来自 Element Plus 图标集[^1]。 ```json [ { "path": "/dashboard", "name": "Dashboard", "component": () => import(&#39;@/views/DashboardView.vue&#39;), "meta": { "text": "仪表盘", "icon": "HomeFilled" } }, ... ] ``` #### 编写模板代码展示侧边栏布局 通过 `<el-menu>` 组件构建左侧导航条目,利用 `v-for` 指令遍历上述定义好的路由对象集合来动态生成链接;同时设置属性 `collapse` 控制展开收缩状态变化[^2]。 ```html <template> <div class="layout"> <!-- 左侧菜单 --> <aside :class="{ &#39;is-collapse&#39;: isCollapse }"> <el-menu default-active="1-4-1" @open="handleOpen" @close="handleClose" :collapse="isCollapse"> <router-link v-for="(item, index) in routes" :key="index" :to="item.path"> <el-menu-item :index="String(index)"> <el-icon><component :is="item.meta.icon"></component></el-icon> <span>{{ item.meta.text }}</span> </el-menu-item> </router-link> </el-menu> </aside> <!-- 主体内容区 --> <section> <header>Header</header> <main> <router-view /> </main> </section> <!-- 切换按钮 --> <button @click="toggleSidebar">Toggle Sidebar</button> </div> </template> ``` #### 添加自定义样式调整视觉效果 针对不同状态下(正常显示 vs 折叠模式)的元素尺寸与间距作出相应修改,确保用户体验良好。 ```css <style scoped> /* 设置默认字体大小 */ .el-menu-item, .el-sub-menu__title { font-size: 18px; } .is-collapse { width: 64px !important; } </style> <style> /* 调整子菜单弹窗最小宽度 */ .el-menu--popup { min-width: 100px !important; } </style> ``` #### JavaScript 方法处理交互逻辑 编写方法响应用户的操作事件,比如点击切换按钮改变布尔变量 `isCollapse` 来触发视图更新。 ```javascript <script setup lang="ts"> import { ref } from &#39;vue&#39;; import { useRouter } from &#39;vue-router&#39;; let isCollapse = ref(false); // 是否处于收起状态,默认不收起 const toggleSidebar = () => (isCollapse.value = !isCollapse.value); function handleOpen(key: string | number, keyPath: Array<string | number>) {} function handleClose(key: string | number, keyPath: Array<string | number>) {} const router = useRouter(); const routes = computed(() => router.getRoutes().filter((route) => route.meta && route.meta.text) ); </script> ``` 以上即是在 Vue3 中运用 Element Plus 构建具备折叠功能的导航栏设计方案的具体实现过程。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值