最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端初学者的第一个Vue后台管理项目总结3:根据路由生成菜单栏

    正文概述 掘金(争霸爱好者)   2021-02-27   857

    在上一篇文章,我们根据用户角色获取了路由,这一篇文章,我们就根据路由来生成菜单栏,这也是后台管理系统的一个必备功能。

    这篇文章将会从路由设计以及递归组件两个方面进行讲解,希望能够对大家有所帮助。

    项目地址:gitee

    系列文章地址:

    1. 项目基础架构

    2. 登录与权限控制

    路由设置

    目前网上大部分的实战课程在设置路由时,都会采用如下方式

    // 写法1
    const routes = [
      {
        path: '/',
        component: () => import('XXX')
        children: [
        	{
              path: 'a',
              component: () => import('A'),
            children [
              {
                path: 'aa',
                component: () => import('AA')
              }
            ]
          },
          {
            path: 'b',
            component: () => import('B')
          }
        ]
      }
    ]
    

    写法1符合我们的很直观想法,但是个人感觉这种写法不太清晰,因此笔者采用了写法2

    // 写法2
    const router = [
      // 路由1
      {
        path: '/a',
        redirect: '/a/aa',
        // 布局组件
        component: Layout,
        children: [
          {
            path: 'aa',
            component: () => import('AA'),
          },
          {
            path: 'bb',
            component: () => import('BB'),
          }
        ]
      },
      // 路由2
      {
        // ...
      }
      // ...
    ]
    
    

    个人感觉这种写法的优点在于,每个路由对象对应一个菜单项,结构比较清晰。

    其实这两种写法对于菜单的生成是没有影响的,大家可以自行选择。

    几个路由参数

    alwaysShow // 默认为false,当菜单栏只有一个子项时,会直接显示为一个el-menu-item
    					 //	如果设置为true,则该路由对应的菜单栏为el-submenu
    hidden: // 是否在菜单栏中显示该路由,比如/login.hidden = true
    meta: {
      icon // 菜单栏图标
      title // 菜单栏标题
    }
    

    递归生成菜单栏

    之前我们已经获取到了权限路由,可以定义一个getter来获取它

    // src/store/getters.js
    const getters = {
      // ...
      permission_routes: state => state.permission.routes
      // ...
    }
    

    这样,我们就能通过路由生成菜单栏。

    我们的菜单栏组件定义在src/Layout/SideBar文件夹内

    首先看SideBar/index.vue,过滤出可以显示的项

    created() {
      this.routes = this.permissionRoutes.filter(r => !r.hidden)
    }
    

    之后循环this.routes生成菜单

    <el-menu
      router
    >
      <sidebar-item
        v-for="route in routes"
        :key="route.path"
        :sidebarItemRoute="route"
        :base-path="route.path"
      />
    </el-menu>
    

    sidebar-item是自定义的组件,表示每个菜单项,该组件接收两个props

    props: {
      sidebarItemRoute: {
        type: Object,
        required: true
      },
      basePath: {
        type: String,
        default: ''
      }
    }
    

    菜单项组件

    sidebar-item是一个递归组件

    <template v-if="showAsMenuItem && !sidebarItemRoute.alwaysShow">
      <el-menu-item :index="resolvePath(menuItemRoute.path)">
        <i :class="menuItemRoute.meta.icon"></i>
        <span>{{ menuItemRoute.meta.title }}</span>
      </el-menu-item>
    </template>
    <el-submenu v-else :index="resolvePath(sidebarItemRoute.path)">
      <template slot="title">
        <i :class="sidebarItemRoute.meta.icon"></i>
        <span>{{ sidebarItemRoute.meta.title }}</span>
      </template>
      <sidebar-item
        v-for="child in sidebarItemRoute.children"
        :key="child.path"
        :sidebarItemRoute="child"
        :base-path="resolvePath(child.path)"
      />
    </el-submenu>
    

    showAsMenuItem === true并且alwaysShow === false时,当前路由展示为一个el-menu-itemshowAsMenuItem的判断在handleRoute方法中,基本逻辑为,如果当前路由没有子路由,则showAsMenuItemtrue,或者当前路由只有一个子路由,并且这个子路由没有子路由时,则showAsMenuItemtrue

    handleRoute() {
      // 如果没有子路由,递归结束
      if (!this.sidebarItemRoute.children) {
        this.showAsMenuItem = true
        return
      }
    
      // 过滤掉children中hidden的
      const showingChildren = this.sidebarItemRoute.children.filter(
        item => !item.hidden
      )
    
      // 当只有一个子路由,并且这个子路由没有children属性时
      if (showingChildren.length === 1 && !showingChildren[0].children) {
        this.showAsMenuItem = true
        this.menuItemRoute = showingChildren[0]
      }
    }
    

    另外一个需要处理的就是path,处理思路如下,父组件传递一个base-path,子组件将base-path与自己的path拼接得到完整的路径。因此我们能在模板中看到这样的代码:<el-menu-item :index="resolvePath(menuItemRoute.path)"><el-submenu v-else :index="resolvePath(sidebarItemRoute.path)">

    // js
    resolvePath(routePath) {
      return path.resolve(this.basePath, routePath)
    }
    

    但是有一个特殊情况需要处理,就是当路由没有子路由时,它的base-path就已经是完整的path了,但是在模板中需要将base-pathpath进行拼接,因此需要将它自己的path重置为空字符串

    handleRoute() {
      if (!this.sidebarItemRoute.children) {
        this.showAsMenuItem = true
        // 如果没有子路由,basePath就是这一层的路由,因此拼接一个空字符串
        this.menuItemRoute = {
          ...this.sidebarItemRoute,
          path: ''
        }
        return
      }
      // ...
    }
    

    这样就OK了。

    一个小BUG

    一个Vue组件必须有且仅有一个根元素,一般我们会使用div,但是在sidebar-item组件中使用div作为根元素会有一个bug,就是当我们点击菜单收缩时,菜单并没有收缩,这是因为我们把sidebar组件和sidebar-item组件分开写,导致el-menuel-sub-menu,el-menu-item之间多了一层div,导致element-ui内部的样式出现了问题,我们可以采用一个插件来解决:vue-fragment

    // main.js
    import Fragment from 'vue-fragment'
    Vue.use(Fragment.Plugin)
    

    sidebar-item组件内以<fragment>标签作为根元素就可以了。如果你有其他的解决方案,也可以评论告诉我!!


    起源地下载网 » 前端初学者的第一个Vue后台管理项目总结3:根据路由生成菜单栏

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元