vue element UI 学习总结笔记(九)_ 导航菜单与路由
获取用户信息
我们把 login
和get_user_info
两件事分开处理,我们在守卫路由中获取路由信息。
router.beforeEach((to, from, next) => {if (!(store.getters.savestate === 0)) {Message({showClose: true,message: '正在编辑状态中,请先保存',type: 'error'})next(false)return}NProgress.start()// 如果已经获取了tokenif (getToken()) {// 如果将要跳转到的页面是/loginif (to.path === '/login') {// 那么就直接调转到/next({ path: '/' })// 进度条结束NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it} else {// 如果角色长度为零,即没有获取到角色// 判断当前用户是否已拉取完user_info信息if (store.getters.roles.length === 0) {// 调用action派遣事件发送请求获取用户信息(包括角色权限)。store.dispatch('GetInfo').then(res => { // 拉取用户信息// const roles = res.data.rolesconsole.log('菜单树信息为:', res.data)var { admdivcode, admdivname, year, name, businessNb, officename } = res.data// 以后接口字段待调整。businessNb = '001财政局非税'var userInfo = { admdivcode, admdivname, year, name, businessNb, officename }store.dispatch('SetUserInfo', userInfo)store.dispatch('SetAllUserInfo', res.data.userinfo)store.dispatch('GenerateRoutes', res.data).then(() => { // 生成可访问的路由表router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表next({ ...to, replace: true })// hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record})}).catch((err) => {// getInfo的api报错时,打印错误并返回根目录。store.dispatch('FedLogOut').then(() => {Message.error(err || 'Verification failed, please login again')next({ path: '/' })})})} else {// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓if (hasPermission(store.getters.roles, to.meta.roles)) {next()} else {next({ path: '/401', replace: true, query: { noGoBack: true }})}// 可删 ↑}}} else {// 如果没有获取到token,但是要跳转的页面在白名单中。if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入next()} else {// 如果没有获取到token,且要跳转的页面不在白名单中。// next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页,且记住之前的页面。next(`/login`) // 否则全部重定向到登录页,且记住之前的页面。NProgress.done()}}
})
后台数据库存储的资源(菜单)信息
上图中 ,资源编码(如 zcgl_wjgl)等是构成前端页面路由的基础。
我们会从后端/user/info 获取用户信息:返回的结构结构如下:
{"code": "200","data": {"userShortcut": [],"year": "2019","roles": ["admin"],"admdivcode": "420000","menutree": [.....],"version": "2020","oldYear": false,"admdivname": "省本级","officename": "**局","name": "**","setid": "1234","officecode": "80","usercode": "8043","userinfo": {"passwordquestion": null,"nid": "DCBFA88EF11C4D7AB2DD56AD06C9688B","oid": "80","cid": null,"guid": "DCBFA88EF11C4D7AB2DD56AD06C9688B","year": "2019","admdivcode": "420000","pername": "**",......"clientType": "1","admdivname": "省本级","officename": "**局","officecode": "80","ishall": null,"mack": "2020","setid": "1234"},"username": "**"}
}
其中,形成菜单的节点为:
"menutree": [{},{"id": "402880b3348289190134835596f700ec","text": "政策法规管理","state": null,"checked": false,"attributes": {},"children": [{"id": "402880b33482891901348355d21c00ee","text": "政策法规文件管理","state": null,"checked": false,"attributes": {"butns": ["/zcfggl/zcfgwjgl/add", "/zcfggl/zcfgwjgl/delete", "/zcfggl/zcfgwjgl/update", "/zcfggl/zcfgwjgl/upload", "/zcfggl/zcfgwjgl/download", "/zcfggl/zcfgwjgl/print", "/zcfggl/zcfgwjgl/set", "/zcfggl/zcfgwjgl/export"]},"children": [],"parentId": "402880b3348289190134835596f700ec","hasParent": true,"hasChildren": false,"icon": "i-zcfgwjgl","btn": false,"menuId": "/zcfggl/zcfgwjgl","levelnum": 0}, {"id": "402880b334828919013483561add00f0","text": "政策法规文件查询","state": null,"checked": false,"attributes": {"butns": ["/zcfggl/zcfgwjcx/print", "/zcfggl/zcfgwjcx/set", "/zcfggl/zcfgwjcx/export"]},"children": [],"parentId": "402880b3348289190134835596f700ec","hasParent": true,"hasChildren": false,"icon": "i-zcfgwjcx","btn": false,"menuId": "/zcfggl/zcfgwjcx","levelnum": 0}, {"id": "402880b334828919013483565ff500f2","text": "非税收入项目库查询","state": null,"checked": false,"attributes": {"butns": ["/zcfggl/fssrxmkcx/print", "/zcfggl/fssrxmkcx/set", "/zcfggl/fssrxmkcx/export"]},"children": [],"parentId": "402880b3348289190134835596f700ec","hasParent": true,"hasChildren": false,"icon": "i-fssrxmkcx","btn": false,"menuId": "/zcfggl/fssrxmkcx","levelnum": 0}, {"id": "402880b33482891901348357ddab00f6","text": "单位非税收入项目查询","state": null,"checked": false,"attributes": {"butns": ["/zcfggl/dwfssrxmcx/print", "/zcfggl/dwfssrxmcx/set", "/zcfggl/dwfssrxmcx/export"]},"children": [],"parentId": "402880b3348289190134835596f700ec","hasParent": true,"hasChildren": false,"icon": "i-dwfssrxmcx","btn": false,"menuId": "/zcfggl/dwfssrxmcx","levelnum": 0}],"parentId": "-1","hasParent": false,"hasChildren": true,"icon": "i-zcfggl","btn": false,"menuId": "/zcfggl","levelnum": 0},......{}],
前端根据后台泛返回的数据,构建前端菜单与路由(主要根据类似"menuId": "/zcfggl/zcfgwjgl"的节点)
router:
本段涉及:什么是路由,路由的使用步骤 动态路由 路由懒加载
路由是根据不同的 url 地址展示不同的内容或页面。
要使用router,必须先搞个路由地图。
静态路由:
1、定义组件
const Foo = {template:'<div>foo</div>'};const Bar = {template:'<div>bar</div>'}
2、定义路由地图
const routes = [{path:'/foo',component:Foo,},{path:'/bar',component:Bar}];
3、根据路由地图创建路由实例:
const router = new VueRouter({routes:routes//可以简写 routes:routes})
4、路由实例注入到根实例:
const app = new Vue({router}).$mount('#app')
5、 整个应用都具有路由功能,项目可以根据自己创建的路由,导航到到不同的页面
<div id="app"><h1>hello app</h1><p><!-- 使用router-link组件来导航 --><router-link to="/foo">Go to Foo</router-link><router-link to="/bar">Go to Bar</router-link></p><!-- 路由出口路由匹配到的在这里--><router-view></router-view></div>
总结一下:route 是一条路由,routes 是一组路由,router是一个机制,相当于管理者,他来管理路由
通过注入路由器,我们可以在任何组件内通过 this.$router
访问路由器,也可以通过 this.$route
访问当前路由.
官方文档通篇都常使用 router
实例。留意一下 this.$router
和 router
使用起来完全一样。我们使用 this.$router
的原因是我们并不想在每个独立需要封装路由的组件中都导入路由。
路由的编程式导航:
静态方式下,使用router-link组件来导航,router-link根据“导航地图”,“渲染”相应router-view。
编程式导航,就是用router.push(...)代替rout-link组件,事实上,router-link其实调用的就是router.push(...)方法。
嵌套路由
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件.
从main.js中,可见,最外层是#app
import Vue from 'vue'
import App from './App'
......new Vue({el: '#app',router,store,render: h => h(App)
})
app.vue如下:
<template><div id="app"><!-- <div class="main-app">123</div> --><router-view/></div>
</template>
<script>
export default {name: 'App',data() {return {}}
}
</script>
从router中可见到,"/"即为Layout:
//路由
{path: '/',component: Layout,redirect: '/dashboard',name: 'Dashboard',hidden: true,children: [{path: 'dashboard',component: () => import('@/views/dashboard/index')}]
}
Layout.vue结构如下:
<template><div :class="classObj" class="app-wrapper"><div v-if="disabled" class="shield" /><div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" /><head-top/><sidebar class="sidebar-container" /><div class="main-container"><navbar class="toggle-sidebar" /><app-main/></div><foot/></div>
</template><script>
import { Navbar, Sidebar, AppMain } from './components'
import HeadTop from '@/components/headTop'
import Foot from '@/components/foot'
import ResizeMixin from './mixin/ResizeHandler'
import { mapState } from 'vuex'
import Bus from '@/utils/Bus'
import { getCodeLabelDictionary } from '@/api/CommonApi'
import { getNowDate } from '@/api/login'export default {name: 'Layout',components: {Navbar,Sidebar,AppMain,HeadTop,Foot},mixins: [ResizeMixin],computed: {...},watch: {...},created() {...},methods: {...}
}
</script>
<style lang="scss">...
</style>
Layout.vue当中,可以看出,有左侧导航栏的区域了。
我们是动态路由和静态路由混用的:
import Vue from 'vue'
import Router from 'vue-router'Vue.use(Router)/* Layout */
import Layout from '../views/layout/Layout'//静态路由部分
export const constantRouterMap = [{path: '/login',component: () => import(`@/views/login/index`),hidden: true
},
{path: '/404',component: () => import('@/views/404'),hidden: true
},
{path: '/',component: Layout,redirect: '/dashboard',name: 'Dashboard',hidden: true,children: [{path: 'dashboard',component: () => import('@/views/dashboard/index')}]
}
]export default new Router({// mode: 'history', //后端支持可开scrollBehavior: () => ({y: 0}),routes: constantRouterMap
})// 异步挂载的路由
// 动态需要根据权限加载的路由表
export const asyncRouterMap = [
]// component: () => import('@/components/permision'),
参考1:vue-router八个重要知识点应用图解
参考2:vue-router 基本使用
参考3:vue-router 快速入门
ps:嵌套路由与路由参数的比较,我前端同事给的解释如下:
嵌套路由的表现形式在url上是这样的 : /path/pathtwo/paththree
路由参数在url上表现出是这样的: /path/pathtwo/paththree/154/887,这种形式需要在路由的path配置的时候写成 /path/pathtwo/paththree/:appid/:productid
或者以查询字符串形式带问号的那种:/path/pathtwo/paththree?appid=154&productid=887
如果路由参数以非查询字符串形式表示,确实可能和嵌套路由表现出的地址一样 。但是两者在路由router中的path是完全不一样的 。
如 /path/:paramsone/:paramstwo ,结果paramsone的值为'pathtwo' ,paramstwo 的值为paththree' ,那么最终的路由地址是
/path/pathtwo/paththree,
而如果此时你正好也写了一个子路由,path为 :/path/pathtwo/paththree
这两个url看起来一样 ,但是匹配的path和组件页面是完全不一样的 。前者匹配的是/path对应的页面 ,后面的paramsone,paramstwo 只不过是路由里面的参数 。
后者匹配的就是/path/pathtwo/paththree对应的页面 ,且无路由参数 。
参考:vue-router+vuex实现加载动态路由和菜单
1、路由 什么时候 什么地方从后端请求的
permission.js中有个守护路由router.beforeEach方法,是取得路由的核心代码。
ps:Promise vs permission:
Promise:a declaration or assurance that one will do a particular thing or that a particular thing will happen.es6中,用这个词表示是异步函数。
permission:consent; authorization.。
改进点:
后端返回的是该用户可访问的全部菜单 ,并不是全部页面的菜单。
已经与用户绑定了,所以这里的权限过滤并没有起效 。
而且,后端并没有根据用户不同配置不同的角色状态,只是写死了每个登陆的用户都是admin,所以这里的else里永远不会触发,也就是这里的权限过滤还是没有使用。
这种方式是不是值得商榷呢?
2、登录跳转
验证成功就直接跳转首页,跳转的过程,就会触发Navigation Guards,Navigation Guards中,除了验证用户是否登录外,如果是首次登录,还会从后端查询用户的功能菜单,组装成前端的router.
3、路由和左边文件夹导航
如何和左边导航栏关联
导航菜单
通过注入路由器,我们可以在任何组件内通过 this.$router
访问路由器,也可以通过 this.$route
访问当前路由.
官方文档通篇都常使用 router
实例。留意一下 this.$router
和 router
使用起来完全一样。我们使用 this.$router
的原因是我们并不想在每个独立需要封装路由的组件中都导入路由。
主页面布局:
布局层次示意图:
(单页面布局示意图)
Layout的template:
AppMain.vue 右边的视图窗口
appmain 中,有router-view class="view"
element-ui菜单menu:
它有四个
- el-menu
- el-submenu
- el-menu-item-group
- el-menu-item
这里el-menu定义了当前的导航菜单及属性,
el-submenu定义了子菜单栏
el-menu-item-group定义了菜单分组 我们项目暂时没用这个
el-menu-item 定义菜单项目
和router 结合,进行动态路由跳转有二种方式:
方式一:在<el-menu>里加上router属性的方式,官网描述如下:
这样就很方便,只要你<el-menu-item>里的index属性值正确就可以直接跳转了,不用再写js方法了
要理解这个element-ui 与router 形成的菜单,先看一个静态的例子:
<el-menu router :default-active="$route.path" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" theme="dark"> <el-submenu index="1"> <template slot="title"> <i class="el-icon-location"></i> <span>用户信息</span> </template> <el-menu-item-group> <el-menu-item index="/user/account">账号信息</el-menu-item> <el-menu-item index="/user/password">修改密码</el-menu-item> </el-submenu> <el-submenu index="2"> <template slot="title"> <i class="el-icon-location"></i> <span>公司信息</span> </template> <el-menu-item-group> <el-menu-item index="/company/userManager">用户管理</el-menu-item> <el-menu-item index="/company/editUser">添加/编辑用户</el-menu-item> </el-menu-item-group> </el-submenu>
</el-menu>
1.要实现路由跳转,先要在el-menu标签上添加router属性,然后只要在每个el-menu-item标签内的index属性设置一下url即可实现点击el-menu-item实现路由跳转。
2.导航当前项,在el-menu标签中绑定 :default-active="$route.path",注意是绑定属性,不要忘了加“:”,当$route.path等于el-menu-item标签中的index属性值时则该item为当前项。
方式二 利用link 或者 点击事件,进行路由跳转:
我们是采取这个思路解决问题。
<template><el-scrollbar wrap-class="scrollbar-wrapper"><el-menu :show-timeout="200" :default-active="$route.path" :collapse="isCollapse" :background-color="variables.menuBg" :text-color="variables.menuText" :active-text-color="variables.menuActiveText" unique-opened mode="vertical"><sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path" /></el-menu></el-scrollbar>
</template>
这里,我们看到el-menu没有router的属性了。sidebar-item 是我们自定义的一个组件。
<template><div v-if="!item.hidden&&item.children" class="menu-wrapper">
<!--如果是末级菜单,可以直接链接--><template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"><app-link :to="resolvePath(onlyOneChild.path)"><el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"><item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon||item.meta.icon" :title="onlyOneChild.meta.title" /></el-menu-item></app-link></template>
<!--如果是非末级菜单的情况--><el-submenu v-else :index="resolvePath(item.path)">.......</el-submenu></div></template><script>
import path from 'path'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'export default {name: 'SidebarItem',components: { Item, AppLink },props: {item: {type: Object,required: true},isNest: {type: Boolean,default: false},basePath: {type: String,default: ''}},data() {return {onlyOneChild: null}},methods: {hasOneShowingChild(children, parent) {.......},resolvePath(routePath) {if (this.isExternalLink(routePath)) {return routePath}return path.resolve(this.basePath, routePath)},isExternalLink(routePath) {return isExternal(routePath)}}
}
</script>
<style rel='stylesheet/scss' lang='scss' scoped>
......
</style>
AppLink是我们自定义的一个带插槽的组件。插入的当然是menu_item.
ps:vue 利用component组件和is属性实现动态组件
vue element UI 学习总结笔记(九)_ 导航菜单与路由相关推荐
- vue + element ui 的后台管理系统框架_从零开始搭建 VUE + Element UI后台管理系统框架...
点击右上方红色按钮关注"web秀",让你真正秀起来 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条号.百家号等 ...
- 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Table表格增删查改、Pagination分页、搜索框
文章目录 目标 代码 0.结构 1.按钮-删除 2.按钮-编辑 3.debug 4.样式 5.分页Pagination:功能 6.分页Pagination:样式 7.搜索框:功能 8.搜索框:样式 总 ...
- 【前端】Vue+Element UI案例:通用后台管理系统-用户管理:Form表单填写、Dialog对话框弹出
文章目录 目标 代码 0.页面结构 1.新增按钮和弹出表单:结构 2.新增按钮和弹出表单:点击新增弹出表单 3.表单样式 4.表单验证 5.表单的提交和取消功能:接口.mock相关准备 6.表单的提交 ...
- 【前端】Vue+Element UI案例:通用后台管理系统-登陆不同用户显示不同菜单、动态添加路由
文章目录 目标 代码 0.动态地显示菜单:store 1.动态注册路由 2.解决刷新后摆平问题 总代码 本篇修改的代码文件 tab.js 参考视频: VUE项目,VUE项目实战,vue后台管理系统,前 ...
- 【前端】Vue+Element UI案例:通用后台管理系统-代码总结(已开源)
文章目录 前言 项目文件目录 api mockServe home.js permission.js index.js mock.js user.js assert components Common ...
- nodeJs + webpack+vue+ element ui 环境安装
一.安装nodeJs 1.打开NodeJs官网:https://nodejs.org/en/download/ 点击下载 2.双击安装,安装过程基本直接"NEXT"就可以了.(w ...
- 【Vue 快速入门】从零开始搭建 VUE + Element UI后台管理系统框架
[Vue 快速入门]从零开始搭建 VUE + Element UI后台管理系统框架 前言 后台管理系统前端框架,现在很流行的形式都是,上方和左侧都是导航菜单,中间是具体的内容.比如阿里云.七牛云.头条 ...
- 基于vue(element ui) + ssm + shiro 的权限框架
zhcc 基于vue(element ui) + ssm + shiro 的权限框架 引言 心声 现在的Java世界,各种资源很丰富,不得不说,从分布式,服务化,orm,再到前端控制,权限等等玲琅满目 ...
- Element UI学习记录之布局
目录 Element UI学习记录之布局 一.Layout布局 二.Container布局容器 Element UI学习记录之布局 一.Layout布局 基本概念:一行通过分割为24栅格栏进行布局,如 ...
最新文章
- Ubuntu12.04 安装 mongodb
- ITK:KMeans聚类
- 撩课-Python-每天5道面试题-第2天
- Linux下Vim工具常用命令
- 《Essential C++》笔记之迭代器Iterator(泛型指针)
- Lua笔记2 变量、循环和流程控制
- 李子奈《计量经济学》第四版笔记和课后答案
- 树莓派摄像头模块(Pi Cam)的安装使用
- python爬虫-《笔趣看》网小说《悟空看私聊》
- “FCoE全解系列”之网络融合交换机类型
- Win10 x64 安装Eplan P8 2.7 小结
- MySQL中的极限值
- 操作系统期末复习-第一章:操作系统概论
- 利用GPS北斗卫星信号开发设计NTP网络时间服务器
- 单目-线激光三维扫描系统中光刀平面的标定
- 基于C++6.0的Gh0st远控源码研究及在VS2019下的编译修正和测试
- 淘宝逛逛,一个0成本适合新手的副业项目
- 子豪兄教你从零开始实现人脸识别
- 64位百度云 catia v6_CATIA V6R2015百度云
- window编译最新的spiderMonkey
热门文章
- 众里寻他千百度 哪家单位让你再见如初?这里上市公司、股份集团任你选 | 大数据周聘汇
- 手机配合termux部署DDbot 教程
- vb sendmessage 详解1
- [2021.07.12]Android系统广播机制(Broadcast Receiver)
- 使用python画等边三角形的程序-运用Python的turtle库绘制等边三角形
- 抖音引流创业容易被人收割的地方
- 产品设计培训个人总结
- php do while(),php do while用法详解
- ZT:“再看OA”系列讲座之三:反思档案管理
- Shellcode免杀,绕过360安全卫士、火绒安全、腾讯管家