做一个管理后台,首先要设置路由,然后配置菜单(有时候还需要导航),再来一个动态tabs,最后加上权限判断。

这个是不是有点繁琐?尤其是路由的设置和菜单的配置,是不是很雷同?那么能不能简单一点呢?如果可以实现设置一次就全部搞定的话,那么是不会很香呢?

我们可以简单封装一下,实现这个愿望。

定义一个结构

我们可以参考 vue-router 的设置 和 el-menu 的参数,设置一个适合我们需求的结构:

  • ./router.js
import { createRouter } from '@naturefw/ui-elp'

import home from '../views/home.vue'

const router = {
/**
* 基础路径
*/
baseUrl: baseUrl, /**
* 首页
*/
home: home, menus: [
{
menuId: '1', // 相当于路由的 name
title: '全局状态', // 浏览器的标题
naviId: '0', // 导航ID
path: 'global', // 相当于 路由 的path
icon: FolderOpened, // 菜单里的图标
childrens: [ // 子菜单,不是子路由。
{
menuId: '1010', // 相当于路由的 name
title: '纯state',
path: 'state',
icon: Document,
// 加载的组件
component: () => import('../views/state-global/10-state.vue')
// 还可以有子菜单。
},
{
menuId: '1020',
title: '一般的状态',
path: 'standard',
icon: Document,
component: () => import('../views/state-global/20-standard.vue')
}
]
},
{
menuId: '2000',
title: '局部状态',
naviId: '0',
path: 'loacl',
icon: FolderOpened,
childrens: [
{
menuId: '2010',
title: '父子组件',
path: 'parent-son',
icon: Document,
component: () => import('../views/state-loacl/10-parent.vue')
}
]
}
]
} export default createRouter(router )

在 Router 的配置的基础上,加上 title、icon等菜单需要的属性,基本就搞定了。

  • baseUrl:如果不能发布到根目录的话,需要设置一个基础URL。
  • home:默认显示的组件,比如大屏。
  • menus:路由、菜单集合。
    • naviId:导航ID。
    • menuId:相当于路由的 name。
    • path:相当于 路由 的path。
    • title:浏览器的标题。
    • icon: 菜单里的图标。
    • childrens:子菜单,不是子路由。

main 里面加载。

设置之后,我们在main里面挂载一下即可。

import { createApp } from 'vue'
import App from './App.vue' // 简易路由
import router from './router' createApp(App)
.use(router)
.mount('#app')
  • 看看效果

https://naturefw-code.gitee.io/nf-rollup-state/class/object

这样就搞定了,是不是很简单,因为我们把其他代码都封装成了组件。

封装 n级菜单

我们可以基于 el-menu,封装一个动态n级菜单组件(nf-menu)。

菜单组件可以基于 el-menu 封装,也可以基于其他组件封装,或者自己写一个,这里以el-menu为例,介绍一下封装方式:

  • 父级菜单
  <el-menu
ref="domMenu"
class="el-menu-vertical-demo"
@select="select"
background-color="#6c747c"
text-color="#fff"
active-text-color="#ffd04b"
>
<sub-menu1
:subMenu="menus"
/>
</el-menu>

父级菜单比较简单,设置 el-menu 需要的属性,然后加载子菜单组件。

  • n级子菜单
  <template v-for="(item, index) in subMenu">
<!--树枝-->
<template v-if="item.childrens && item.childrens.length > 0">
<el-sub-menu
:key="item.menuId + '_' + index"
:index="item.menuId"
style="vertical-align: middle;" >
<template #title>
<component
:is="item.icon"
style="width: 1.5em; height: 1.5em; margin-right: 8px;vertical-align: middle;"
>
</component>
<span>{{item.title}}</span>
</template>
<!--递归子菜单-->
<my-sub-menu2
:subMenu="item.childrens"
/>
</el-sub-menu>
</template>
<!--树叶-->
<el-menu-item v-else
:index="item.menuId"
:key="item.menuId + 'son_' + index" >
<template #title>
<span style="float: left;">
<component
:is="item.icon"
style="width: 1.5em; height: 1.5em; margin-right: 8px;vertical-align: middle;"
>
</component>
<span >{{item.title}}</span>
</span>
</template>
</el-menu-item>
</template>
  • 树枝:含有子菜单的菜单,使用 el-sub-menu 实现,不加载组件。
  • 树叶:没有子菜单,使用 el-menu-item 实现,加载组件的菜单。
  • 图标:使用 component 加载图标组件。

然后设置属性即可,这样一个n级菜单就搞定了。

封装一个动态tabs

菜单有了,下一步就是tabs,为了满足不同的需求,这里封装两个组件,一个单tab的,一个是动态多tabs的。

  • 单 tab

    参考 Router 的 router-view 封装一个组件 nf-router-view:
  <component :is="$router.getComponent()">
</component>

直接使用 component 加载组件即可。

  • 动态多tabs

    基于 el-tabs 封装一个动态多tabs组件 nf-router-view-tabs:
  <el-tabs
v-model="$router.currentRoute.key"
type="border-card"
>
<el-tab-pane label="桌面" name="home">
<component :is="$router.home">
</component>
</el-tab-pane>
<el-tab-pane
v-for="key in $router.tabs"
:key="key"
:label="$router.menuList[key].title"
:name="key"
>
<template #label>
<span>{{$router.menuList[key].title}} &nbsp;
<circle-close-filled
style="width: 1.0em; height: 1.0em; margin-top: 8px;"
@click.stop="$router.removeTab(key)" />
</span>
</template>
<component :is="$router.menuList[key].component">
</component>
</el-tab-pane>
</el-tabs>

为了保持状态,这里采用了一个笨办法,点击菜单加载的组件都放在 el-tab-pane 里面,通过切换 tab 的方式显示组件。

源码:https://gitee.com/naturefw-code/nf-rollup-ui-controller

做一个简单的路由

看了半天,你有没有发现,似乎缺少了一个重要环节?

你猜对了,路由的封装还没有介绍。

这里并不想设计一个像 vue-router那样的全能路由,而是设计一个适合管理后台的简易路由。

菜单是多级的,url 也是多级的和菜单对应,但是路由是单级的,不嵌套。

也就是说,点击任意一级的(树叶)菜单,加载的都是同级的组件。

另外暂时不考虑加载组件后的路由的设置。我觉得,这个可以交给加载的组件自行实现。

import { defineAsyncComponent, reactive, watch, inject } from 'vue'

const flag = Symbol('nf-router-menu___')

/**
* 一个简单的路由
* @param { string } baseUrl 基础路径
* @param { components } home 基础路径
* @param { array } menus 路由设置,数组,多级
* * [{
* * * menuId: '菜单ID'
* * * naviId: '0', // 导航ID,可以不设置
* * * title: '标题',
* * * path: '路径',
* * * icon: Edit, // 图标组件
* * * component: () => import('./views/xxx.vue') // 要加载的组件,可以不设置
* * * childrens: [ // 子菜单,可以多级
* * * * {
* * * * * menuId: '菜单ID'
* * * * * title: '标题',
* * * * * path: '路径',
* * * * * icon: Edit, // 图标组件
* * * * * component: () => import('./views/xxx.vue') // 要加载的组件
* * * * }
* * * ]
* * },
* * 其他菜单
* * ]
* @returns
*/
class Router {
constructor (info) {
// 设置当前选择的路由
this.currentRoute = reactive({
key: 'home', // 默认的首页
paths: [] // 记录打开的多级菜单的信息
})
this.baseUrl = info.baseUrl // 基础路径,应对网站的二级目录
this.baseTitle = document.title // 初始的标题
this.isRefresh = false // 是否刷新进入
this.home = info.home // 默认的首页
this.menus = reactive(info.menus) // 菜单集合,数组形式,支持多级,可以设置导航ID
this.menuList = {} // 变成单层的树,便于用key查找。
this.tabs = reactive(new Set([])) // 点击过且没有关闭的二级菜单,做成动态tab标签 this.setup()
} /**
* 初始化设置
*/
setup = () => {
// 监听当前路由,设置 tabs 和标题、url
watch(() => this.currentRoute.key, (key) => {

})
} /**
* 添加新路由,主要是实现根据用户权限加载对应的菜单。
*/
addRoute = (newMenus, props = {}) => {

} /**
* 删除路由
* @param { array } path 菜单的路径,[] 表示根菜单
* @param { string | number } id 要删除的菜单ID
*/
removeRoute = (path = [], id = '') => {

} /**
* 刷新时依据url加载组件
*/
refresh = () => {

} /**
* 加载路由指定的组件
* @returns
*/
getComponent = () => {
if (this.currentRoute.key === '' || this.currentRoute.key === 'home') {
return this.home
} else {
return this.menuList[this.currentRoute.key].component
}
} /**
* 删除tab
* @param { string } key
* @returns
*/
removeTab = (key) => {

} /**
* 安装插件
* @param {*} app
*/
install = (app) => {
// 便于模板获取
app.config.globalProperties.$router = this
// 便于代码获取
app.provide(flag, this)
}
} /**
* 创建简易路由
*/
const createRouter = (info) => {
// 创建路由,
const router = new Router(info)
// 判断url,是否需要加载组件
setTimeout(() => {
router.refresh()
}, 300)
// 使用vue的插件,设置全局路由
return router
} /**
* 获取路由
* @returns
*/
const useRouter = () => {
return inject(flag)
} export {
createRouter,
useRouter
}

篇幅有限,这里只介绍了路由的整体结构,具体实现方式可以看源码:

源码:https://gitee.com/naturefw-code/nf-rollup-ui-controller

菜单与权限

上面是静态的路由和导航的设置方式,对于管理后台,必备的一个需求就是,根据用户的权限来加载路由和菜单。

所以我们提供了一个 addRoute 方法,实现动态添加路由的功能,这样可以等用户登录之后,得到用户的权限,然后按照权限加载路由和菜单。

  const router = useRouter()

   router.addRoute([
{
menuId: 'dt-100',
title: '添加根菜单',
naviId: '0',
path: 'new-router',
icon: FolderOpened,
childrens: [
{
menuId: '100-10',
title: '动态菜单',
path: 'ui',
icon: Document,
component: () => import('../ui/base/c-01html.vue')
}
]
}
], { index: 1 })

同时也可以加上权限判断。菜单是基于 el-menu 实现的,可以加上 select 事件,然后在事件里面判断权限,如果没有权限可以跳转到登录组件。


const router = useRouter() const myselect = (index, indexPath) => {
// 验证权限,如果没有权限,加载登录组件
if (没有权限) {
router.currentRoute.paths = ''
router.currentRoute.key = '登录组件的key'
}
}

示例项目

https://gitee.com/naturefw-code/nf-rollup-state

【摸鱼神器】一次搞定 vue3的 路由 + 菜单 + tabs的更多相关文章

  1. 【摸鱼神器】UI库秒变LowCode工具——列表篇(一)设计与实现

    内容摘要: 需求分析 定义 interface 定义 json 文件 定义列表控件的 props 基于 el-table 封装,实现依赖 json 渲染 实现内置功能:选择行(单选.多选),格式化.锁 ...

  2. Thief-Book 上班摸鱼神器

    Thief-Book 上班摸鱼神器 介绍 Thief-Book 是一款真正的摸鱼神器,可以更加隐秘性大胆的看小说. 隐蔽性 自定义透明背景,随意调整大小,完美融入各种软件界面 快捷性 三个快捷键,实现 ...

  3. vscode插件(摸鱼神器-小霸王游戏机

    vscode插件(摸鱼神器-小霸王游戏机 步骤 vscode扩展搜索小霸王,点击下载即可. 使用 默认有一个demo小游戏,即超级玛丽. 本地仓库 可以通过local菜单上的添加按钮添加本地nes r ...

  4. 【转】让Chrome化身成为摸鱼神器,利用Chorme运行布卡漫画以及其他安卓APK应用教程

    下周就是十一了,无论是学生党还是工作党,大家的大概都会有点心不在焉,为了让大家更好的心不在焉,更好的在十一前最后一周愉快的摸鱼,今天就写一个如何让Chrome(google浏览器)运行安卓APK应用的 ...

  5. 【摸鱼神器】UI库秒变低代码工具——表单篇(二)子控件

    上一篇介绍了表单控件,这一篇介绍一下表单里面的各种子控件的封装方式. 主要内容 需求分析 子控件的分类 子控件属性的分类 定义 interface. 定义子控件的的 props. 定义 json 文件 ...

  6. 【摸鱼神器】基于SSM风格的Java源代码生成器 单表生成 一对一、一对多、多对多连接查询生成

    一.序言 UCode Cms 是一款Maven版的Java源代码生成器,是快速构建项目的利器.代码生成器模块属于可拆卸模块,即按需引入.代码生成器生成SSM(Spring.SpringBoot.Myb ...

  7. 【摸鱼神器】UCode Cms管理系统 内置超好用的代码生成器 解决多表连接痛点

    一.序言 UCode Cms管理系统是面向企业级应用软件开发的脚手架.当前版本1.3.4.快速体验: git clone https://gitee.com/decsa/demo-cms.git (一 ...

  8. 【摸鱼神器】UI库秒变LowCode工具——列表篇(二)维护json的小工具

    上一篇介绍了一下如何实现一个可以依赖 json 渲染的列表控件,既然需要 json 文件,那么要如何维护这个 json 文件就成了重点,如果没有好的维护方案的话,那么还不如直接用UI库. 所以需要我们 ...

  9. 【摸鱼神器】UI库秒变低代码工具——表单篇(一)设计

    前面说了列表的低代码化的方法,本篇介绍一下表单的低代码化. 内容摘要 需求分析. 定义 interface. 定义表单控件的 props. 定义 json 文件. 基于 el-form 封装,实现依赖 ...

随机推荐

  1. java集合(arraylist详解)

    一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类 ...

  2. 动态div点击事件传递对象参数格式-草稿889

    <button type='button' style='border: 1px solid #eeeeee;color: #717070;height: 20px;border-radius: ...

  3. JWT&RSA实现单点登录(详细介绍)

    今天给大家讲一下基于JWT&RSA的单点登录(Single Sign On,简称SSO)解决方案 概念 首先要了解几个概念 单点登录(Single Sign On) JWT RSA 背景 为什 ...

  4. MyBatis起步搭建

    1 步骤 数据库环境 创建Maven项目 导入依赖 编写MyBatis配置文件 编写MyBatis工具类 编写实体类 编写Mapper 测试 2 数据库环境 MySQL 8.0版本 create da ...

  5. Mybatis-plugins分页助手实现查询数据分页

    其他具体代码接上文->mybatis自定义处理器 1.导入坐标 <dependency> <groupId>com.github.pagehelper</group ...

  6. typora简单使用手册

    typora简单使用手册讲解`` 下载网站 网址:https://typoraio.cn/ 苹果电脑:https://typora.en.softonic.com/ 正版呢当然是收费 破解版自行百度 ...

  7. 学生管理系统(python实现)

    # 定一个列表,用来存储所有的学生信息(每个学生是一个字典) info_list = [] def print_menu(): print("------------------" ...

  8. 2021.11.09 P2292 [HNOI2004]L语言(trie树+AC自动机)

    2021.11.09 P2292 [HNOI2004]L语言(trie树+AC自动机) https://www.luogu.com.cn/problem/P2292 题意: 标点符号的出现晚于文字的出 ...

  9. 浅谈MatrixOne如何用Go语言设计与实现高性能哈希表

    目录 MatrixOne数据库是什么? 哈希表数据结构基础 哈希表基本设计与对性能的影响 碰撞处理 链地址法 开放寻址法 Max load factor Growth factor 空闲桶探测方法 一 ...

  10. Linux的Docker安装教程

    Docker下载的官方文档地址:https://docs.docker.com/engine/install/centos/ 卸载旧版本 sudo yum remove docker \ docker ...