vue3 Element封装导航栏
鹏程933 人气:0效果预览
模拟数据
- 数据来源有很多,可以是自己写死的,也可以是后端调用得到的,也可以从别的组件中拿到
- 这里采用从路由中拿
- 定义数据源src/router/module.js/
const Login = () => import('../views/Login/Login.vue'); const Layout = () => import('../layout/layout.vue'); const Home = () => import('../views/Home.vue'); const User = () => import('../views/About.vue'); const Avatar = () => import('../views/Users/Avatar.vue'); const Password = () => import('../views/Users/Password.vue'); const routes = [ { path: '/', redirect: '/home', }, { path: '/', name: 'Layout', component: Layout, meta: { permission: true, }, children: [ { path: '/home', name: 'Home', component: Home, meta: { title: '首页', icon: '<span class="iconfont icon-shouye"/>', // iconfont图标 inSide: true, }, }, { path: '/user', name: 'User', component: User, meta: { title: '个人中心', icon: '<span class="iconfont icon-yonghuzhongxin1"/>', }, children: [ { path: '/user/avatar', name: 'Avatar', component: Avatar, meta: { title: '修改头像', }, children: [ { path: '/setUp/avatar', name: 'setUp', component: Avatar, meta: { title: '暂无', }, }, ], }, { path: '/user/password', name: 'Password', component: Password, meta: { title: '修改密码', }, }, ], }, { path: '/setUp', name: 'SetUp', meta: { title: '系统设置', icon: '<span class="iconfont icon-celveguanli"/>', }, children: [ { path: '/setUp/avatar', name: 'setUp', component: Avatar, meta: { title: '暂无', }, }, { path: '/setUp/avatar', name: 'setUp', component: Avatar, meta: { title: '暂无', }, }, ], }, ], }, { path: '/login', name: 'Login', component: Login, }, ]; export default routes;
递归实现导航栏渲染
- 对于导航栏渲染难点在于不知道有多少层级的导航,可能一级也可能两级或者更多
- 为了方便采用两个组件父组件aside.vue与子组件subAside.vue渲染导航
- 这时候就需要采用递归的方式
- 首先判断哪些数据需要渲染,需要的拿出来
- 判断是否有子节点需要渲染
- 有子节点,递归调用子组件本身
- 没有子节点,返回导航项进行渲染
父组件aside.vue
<template> <el-radio-group v-model="isCollapse" style="margin-bottom: 20px"> <el-radio-button :label="false">expand</el-radio-button> <el-radio-button :label="true">collapse</el-radio-button> </el-radio-group> <el-menu default-active="2" class="el-menu-vertical-demo" :collapse="isCollapse" select="handleSelect" router unique-opened > <!-- 将渲染导航每一项传给子组件渲染,item代表要渲染每一项 --> <SubAside :isCollapse="isCollapse" v-for="(item,index) in navs" :key="item.path" :menu="item" :index="item.path" /> </el-menu> </template> <script lang="ts" setup> import { ref } from 'vue'; import router from '../router/module'; const navs =router.filter((item) => item.meta?.permission)[0].children // 过滤拿到数据 console.log(navs); const isCollapse = ref(true); // 是否收起,默认不收起 </script> <style lang="scss" scoped></style>
父组件处理后的用于渲染的数据
[ { component: () => import('/src/views/Home.vue') meta: {title: '首页', icon: '<span class="iconfont icon-shouye"/>', inSide: true} name: "Home" path: "/home" }, { component: () => import('/src/views/About.vue') meta: {title: '个人中心', icon: '<span class="iconfont icon-yonghuzhongxin1"/>'} name: "User" path: "/user" chilren:[ { children: [{…}] component: () => import('/src/views/Users/Avatar.vue?t=1655544364909') meta: {title: '修改头像'} name: "Avatar" path: "/user/avatar" }, { component: () => import('/src/views/Users/Password.vue') meta: {title: '修改密码'} name: "Password" path: "/user/password" } ] }, { meta: {title: '系统设置', icon: '<span class="iconfont icon-celveguanli"/>'} name: "SetUp" path: "/setUp" chilren:[ { component: () => import('/src/views/Users/Avatar.vue?t=1655544364909') meta: {title: '暂无'} name: "setUp" path: "/setUp/avatar" }, { component: () => import('/src/views/Users/Avatar.vue?t=1655544364909') meta: {title: '暂无'} name: "setUp" path: "/setUp/avatar" } ] } ]
子组件subAside.vue
<template> <!-- 有子节点渲染这个 --> <el-sub-menu :index="menu.path" v-if="menu?.children"> <template #title> <el-icon v-html="menu?.meta.icon"></el-icon> <span>{{menu?.meta.title}}</span> </template> <!-- 递归调用本身,该组件在index.ts中全局注册了 --> <SubAside v-for="item in menu.children" :menu="item" :isCollapse="isCollapse"/> </el-sub-menu> <!-- 没有子节点渲染这个 --> <el-menu-item v-else :index="menu?.path"> <el-icon v-html="menu?.meta.icon"></el-icon> <span slot="title">{{menu?.meta.title}}</span> </el-menu-item > </template> <script lang="ts" setup> import { ref } from "vue" // 拿到父组件传入的值 defineProps({ isCollapse:Boolean, menu:Object }) </script> <style lang="scss" scoped> </style>
配置
版本
"vue": "^3.2.25", "element-plus": "^2.2.6",
main.ts中配置
import { createApp } from 'vue'; import App from './App.vue'; // import 'virtual:windi.css'; import router from './router/index'; /** * 引入elment */ import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import 'element-plus/theme-chalk/dark/css-vars.css' import './styles/dark/css-vars.css' // 引入 Pinia 状态管理工具 import pinia from './stores' const app=createApp(App) /** * 全局注册组件 */ import SubAside from './components/subAside.vue' app.component('SubAside', SubAside) // 注册Element全局可用 app.use(ElementPlus).use(router).use(pinia).mount('#app');
加载全部内容