- 首先需要了解的一些开发规则
- 整个系统支持三种类型的子页面(在数据库里必须以menu.url字段来存储):
(1) user/login:这个是针对的Vue框架下的页面组件,注意不能以/开头,对应views/user/login.vue
(2) iframe:****:这个是Spring Boot框架提供的一些页面,比如swagger和druid监控页面。
(3) http[s]://www.baidu.com,这个是第三方页面。 - 菜单按钮权限的获取被设计在路由守卫中执行初始化。
- 本系统保留动态菜单与路由守卫的功能,但在前期不进行启用,启用的时机要看具体的情况。
- 在store/modules下的app.js中添加一个动态菜单设置状态、动态菜单、用户权限设置状态、用户权限。
export default {
state: {
dynamicRouteLoaded: false, // 菜单和路由是否已经加载
dynamicRoutes: [], // 用户的权限菜单
permissionLoaded: false, // 用户权限是否已经加载
permissions: [] // 用户的按钮权限
},
getters: {
dynamicRouteLoaded (state) {
return state.dynamicRouteLoaded
},
dynamicRoutes (state) {
return state.dynamicRoutes
},
permissionLoaded (state) {
return state.permissionLoaded
},
permissions (state) {
return state.permissions
}
},
mutations: {
dynamicRouteLoaded (state, dynamicRouteLoaded) { // 改变菜单和路由的加载状态
state.dynamicRouteLoaded = dynamicRouteLoaded
},
dynamicRoutes (state, dynamicRoutes) { // 设置用户的权限菜单
state.dynamicRoutes = dynamicRoutes
},
permissionLoaded (state, permissionLoaded) { // 改变用户权限的加载状态
state.permissionLoaded = permissionLoaded
},
permissions (state, permissions) { // 设置用户的按钮权限
state.permissions = permissions
}
},
actions: {
}
}
- 重写登录方法
login () {
var data = {account: '', password: ''}
this.$api.login.login(data).then(res => {
if (res.code !== 200) {
// 其它处理
} else {
Cookies.set('token', res.data.token) // 放置token到cookie
sessionStorage.setItem('user', data.account) // 保存用户到本地会话
this.$store.commit('dynamicRouteLoaded', false) // 要求重新加载导航菜单
this.$store.commit('permissionLoaded', false) // 要求重新加载导航菜单
this.$router.push('/') // 登录成功, 跳转到主页
}
this.loading = false
})
}
- 导航守卫src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import store from '@/store'
import api from '@/http/api'
import { getIFramePath, getIFrameUrl } from '@/utils/iframe'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: '首页',
component: HelloWorld,
children: [
{
path: '',
name: '系统介绍',
component: HelloWorld,
meta: {
icon: 'fa fa-home fa-lg',
index: 0
}
}
]
}
]
})
/**
* 导航守卫
*/
router.beforeEach((to, from, next) => {
// 登录成功之后,会把用户信息保存在会话,用户信息的存在时间为会话生命周期,页面关闭即失效
let userName = sessionStorage.getItem('user')
if (to.path === '/login') {
// 如果是访问登录界面,如果用户会话信息存在,代表已登录过,跳转到主页
if (userName) {
next({ path: '/' })
} else {
next()
}
} else {
if (!userName) {
// 如果访问非登录界面,且户会话信息不存在,代表未登录,则跳转到登录界面
next({ path: '/login' })
} else {
// 加载动态菜单和路由
addDynamicMenuAndRoutes(userName, to, from)
next()
}
}
})
/**
* 加载动态菜单和路由
*/
function addDynamicMenuAndRoutes (userName, to, from) {
// 处理IFrame嵌套页面
handleIFrameUrl(to.path)
if (store.state.app.menuRouteLoaded) {
return
}
api.menu.findNavTree({'userName': userName}).then(res => {
// 添加动态路由
let dynamicRoutes = addDynamicRoutes(res.data)
// 处理静态组件绑定路由
router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)
router.addRoutes(router.options.routes)
// 保存加载状态
store.commit('menuRouteLoaded', true)
// 保存菜单树
store.commit('setNavTree', res.data)
}).then(res => {
api.user.findPermissions({'name': userName}).then(res => {
// 保存用户权限标识集合
store.commit('setPerms', res.data)
})
}).catch(function (res) {
})
}
/**
* 处理IFrame嵌套页面
* 此处主要去匹配在store中存储的iframe路径列表, 通过匹配Path获取iframe真正的url,将url存储到store中
*/
function handleIFrameUrl (path) {
// 嵌套页面,保存iframeUrl到store,供IFrame组件读取展示
let url = path
let length = store.state.iframe.iframeUrls.length
for (let i = 0; i < length; i++) {
let iframe = store.state.iframe.iframeUrls[i]
if (path != null && path.endsWith(iframe.path)) {
url = iframe.url
store.commit('setIFrameUrl', url)
break
}
}
}
/**
* 添加动态(菜单)路由
* @param {*} menuList 菜单列表
* @param {*} routes 递归创建的动态(菜单)路由
*/
function addDynamicRoutes (menuList = [], routes = []) {
var temp = []
for (var i = 0; i < menuList.length; i++) {
if (menuList[i].children && menuList[i].children.length >= 1) {
temp = temp.concat(menuList[i].children)
} else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, '')
// 创建路由配置
var route = {
path: menuList[i].url,
component: null,
name: menuList[i].name,
meta: {
icon: menuList[i].icon,
index: menuList[i].id
}
}
let path = getIFramePath(menuList[i].url)
if (path) {
// 如果是嵌套页面, 通过iframe展示
route['path'] = path
route['component'] = resolve => require([`@/views/IFrame/IFrame`], resolve)
// 存储嵌套页面路由路径和访问URL
let url = getIFrameUrl(menuList[i].url)
let iFrameUrl = {'path': path, 'url': url}
store.commit('addIFrameUrl', iFrameUrl)
} else {
try {
// 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储
// 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到
let array = menuList[i].url.split('/')
let url = ''
for (let i = 0; i < array.length; i++) {
url += array[i].substring(0, 1).toUpperCase() + array[i].substring(1) + '/'
}
url = url.substring(0, url.length - 1)
route['component'] = resolve => require([`@/views/${url}`], resolve)
} catch (e) {}
}
routes.push(route)
}
}
if (temp.length >= 1) {
addDynamicRoutes(temp, routes)
} else {
console.log(routes)
}
return routes
}
export default router
- src\utils\iframe.js
/**
* 嵌套页面IFrame模块
*/
import { baseUrl } from '@/utils/global'
/**
* 嵌套页面URL地址
* iframe:**** -> ****
* http[s]://**** -> ****
* @param {*} url
*/
export function getIFramePath (url) {
let iframeUrl = ''
// 匹配以iframe:开头的任意长度的字符串
if (/^iframe:.*/.test(url)) {
iframeUrl = url.replace('iframe:', '')
} else if (/^http[s]?:\/\/.*/.test(url)) {
iframeUrl = url.replace('http://', '')
iframeUrl = url.replace('https://', '')
if (iframeUrl.indexOf(':') !== -1) {
iframeUrl = iframeUrl.substring(iframeUrl.lastIndexOf(':') + 1)
}
}
return iframeUrl
}
/**
* 获取嵌套页面路由的路径
* iframe:druid/index.html -> baseUrl + druid/index.html
* http[s]://druid/index.html -> 不变
* @param {*} url
*/
export function getIFrameUrl (url) {
let iframeUrl = ''
if (/^iframe:.*/.test(url)) {
iframeUrl = baseUrl + url.replace('iframe:', '')
} else if (/^http[s]?:\/\/.*/.test(url)) {
iframeUrl = url
}
return iframeUrl
}
- src/views/IFrame/IFrame.js
真正投入使用的router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/',
name: '首页',
component: HelloWorld,
children: [
{
path: '',
name: '系统介绍',
component: HelloWorld,
meta: {
icon: 'fa fa-home fa-lg',
index: 0
}
}
]
}
]
})
/**
* 导航守卫
*/
router.beforeEach((to, from, next) => {
let userName = sessionStorage.getItem('user')
if (to.path === '/login') {
if (userName) {
next({ path: '/' })
} else {
next()
}
} else {
if (!userName) {
next({ path: '/login' })
} else {
next()
}
}
})
export default router
导航事件:
let path = getIFramePath(menu.url)
if (!path)
path = menu.url
this.$router.push("/" + path);
七、页面权限控制
权限标识是对页面资源进行权限控制的唯一标识,主要是增、删、改、查的权限控制。权限标识主要包含四种,以用户管理为例,权限标识包括sys:user:add(新增)、sys:user:edit(编辑)、sys:user:delete(删除)、sys:user:view(查看)。
src\permission/index.js
import store from '@/store'
/**
* 判断用户是否拥有操作权限
* 根据传入的权限标识,查看是否存在用户权限标识集合
* @param perms
*/
export function hasPermission (perms) {
let hasPermission = false
let permissions = store.state.user.perms
for(let i=0, len=permissions.length; i
页面操作按钮提供perms属性绑定权限标识,使用disable属性绑定权限判断方法的返回值,权限判断方法hasPerms(perms)通过查找上一步保存的用户权限标识集合是否包含perms来包含说明用户拥有此相关权限,否则设置当前操作按钮为不可用状态。
新建一个权限按钮的组件:
{{label}}
表格组件
六、框架页面设计
- Home页设计
Home.vue主页由导航菜单、头部区域和主内容区域组成。
- 顶部菜单栏设计Headbar
- 菜单栏设计NavBar.vue
- 子页面框架设计MainContent
关闭当前标签
关闭其它标签
关闭全部标签
刷新当前标签
{{item.title}}