第十二章 动态路由、权限、典型框架页(参考)

  1. 首先需要了解的一些开发规则
  • 整个系统支持三种类型的子页面(在数据库里必须以menu.url字段来存储):
    (1) user/login:这个是针对的Vue框架下的页面组件,注意不能以/开头,对应views/user/login.vue
    (2) iframe:****:这个是Spring Boot框架提供的一些页面,比如swagger和druid监控页面。
    (3) http[s]://www.baidu.com,这个是第三方页面。
  • 菜单按钮权限的获取被设计在路由守卫中执行初始化。
  • 本系统保留动态菜单与路由守卫的功能,但在前期不进行启用,启用的时机要看具体的情况。
  1. 在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: {
  }
}

  1. 重写登录方法
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
      })
    }
  1. 导航守卫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来包含说明用户拥有此相关权限,否则设置当前操作按钮为不可用状态。
新建一个权限按钮的组件:






image.png

表格组件

六、框架页面设计
  1. Home页设计
    Home.vue主页由导航菜单、头部区域和主内容区域组成。




  
  1. 顶部菜单栏设计Headbar






  1. 菜单栏设计NavBar.vue





  1. 子页面框架设计MainContent





你可能感兴趣的:(第十二章 动态路由、权限、典型框架页(参考))