菜单路由动态加载 ook
This commit is contained in:
parent
fb0c0a7565
commit
7e1edc0905
|
@ -12,6 +12,7 @@ type Menu struct {
|
|||
RouterName string `gorm:"not null" json:"routerName"`
|
||||
RouterAuth uint `gorm:"not null" json:"routerAuth"`
|
||||
RouterHidden uint `gorm:"not null" json:"routerHidden"`
|
||||
RouterCache uint `gorm:"not null" json:"routerCache"`
|
||||
RouterComponentPath string `gorm:"not null" json:"routerComponentPath"`
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ import d2Admin from '@/plugin/d2admin'
|
|||
import store from '@/store/index'
|
||||
|
||||
// 菜单和路由设置
|
||||
import router from './router'
|
||||
import { menuHeader, menuAside } from '@/menu'
|
||||
import { frameInRoutes } from '@/router/routes'
|
||||
import router, { constantRoutes } from './router'
|
||||
import api from '@/api'
|
||||
import { flushMenus } from '@/menu'
|
||||
|
||||
// 核心插件
|
||||
Vue.use(d2Admin)
|
||||
|
@ -20,15 +20,23 @@ new Vue({
|
|||
store,
|
||||
i18n,
|
||||
render: h => h(App),
|
||||
methods: {
|
||||
async loadMenuRouter () {
|
||||
// 设置路由菜单
|
||||
const menuTree = await api.queryAllMenus({})
|
||||
const menuData = flushMenus(menuTree) || []
|
||||
// 处理路由 得到每一级的路由设置
|
||||
this.$store.commit('d2admin/page/init', constantRoutes)
|
||||
// 设置顶栏菜单
|
||||
// this.$store.commit('d2admin/menu/headerSet', menuData)
|
||||
// 设置侧边栏菜单
|
||||
this.$store.commit('d2admin/menu/asideSet', menuData)
|
||||
// 初始化菜单搜索功能
|
||||
this.$store.commit('d2admin/search/init', menuData)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
// 处理路由 得到每一级的路由设置
|
||||
this.$store.commit('d2admin/page/init', frameInRoutes)
|
||||
// 设置顶栏菜单
|
||||
this.$store.commit('d2admin/menu/headerSet', menuHeader)
|
||||
// 设置侧边栏菜单
|
||||
this.$store.commit('d2admin/menu/asideSet', menuAside)
|
||||
// 初始化菜单搜索功能
|
||||
this.$store.commit('d2admin/search/init', menuHeader)
|
||||
this.loadMenuRouter()
|
||||
},
|
||||
mounted () {
|
||||
// 展示系统信息
|
||||
|
|
|
@ -5,7 +5,7 @@ import { uniqueId } from 'lodash'
|
|||
* @description https://github.com/d2-projects/d2-admin/issues/209
|
||||
* @param {Array} menu 原始的菜单数据
|
||||
*/
|
||||
function supplementPath (menu) {
|
||||
export function supplementPath (menu) {
|
||||
return menu.map(e => ({
|
||||
...e,
|
||||
path: e.path || uniqueId('d2-menu-empty-'),
|
||||
|
@ -15,19 +15,68 @@ function supplementPath (menu) {
|
|||
}))
|
||||
}
|
||||
|
||||
const menuData = [
|
||||
{ path: '/index', title: '首页', icon: 'home' },
|
||||
{
|
||||
title: '页面',
|
||||
icon: 'folder-o',
|
||||
children: [
|
||||
{ path: '/page1', title: '页面 1' },
|
||||
{ path: '/page2', title: '页面 2' },
|
||||
{ path: '/page3', title: '页面 3' }
|
||||
]
|
||||
/**
|
||||
* 设置菜单
|
||||
* @returns {{path: string, icon: string, title: string}[]}
|
||||
*/
|
||||
export function flushMenus (menuTree = []) {
|
||||
let menuData = [{ path: '/index', title: '首页', icon: 'home' }]
|
||||
try {
|
||||
// 权限菜单
|
||||
const d2adminMenus = []
|
||||
if (menuTree && menuTree.length > 0) {
|
||||
for (const menuItem of menuTree) {
|
||||
let menuTemp
|
||||
// 一级
|
||||
if (menuItem.menuPath) {
|
||||
menuTemp = { id: menuItem.id, title: menuItem.menuTitle, ico: menuItem.menuIcon, path: menuItem.menuPath }
|
||||
} else {
|
||||
// path不能为undefined会报错, 详情看源码'src/layout/header-aside/components/libs/util.menu.js'
|
||||
menuTemp = { id: menuItem.id, title: menuItem.menuTitle, ico: menuItem.menuIcon, path: '' }
|
||||
}
|
||||
// 二级
|
||||
const children = menuItem.children
|
||||
if (children && children.length > 0) {
|
||||
for (const childrenMenuItem of children) {
|
||||
const childrenMenuTemp = {
|
||||
id: menuItem.id,
|
||||
title: childrenMenuItem.menuTitle,
|
||||
ico: childrenMenuItem.menuIcon,
|
||||
path: childrenMenuItem.menuPath
|
||||
}
|
||||
if (menuTemp.children && menuTemp.children.length > 0) {
|
||||
menuTemp.children.push(childrenMenuTemp)
|
||||
} else {
|
||||
menuTemp.children = []
|
||||
menuTemp.children.push(childrenMenuTemp)
|
||||
}
|
||||
}
|
||||
}
|
||||
d2adminMenus.push(menuTemp)
|
||||
}
|
||||
}
|
||||
// 拼合菜单
|
||||
menuData = menuData.concat(d2adminMenus)
|
||||
return menuData
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return menuData
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export const menuHeader = supplementPath(menuData)
|
||||
|
||||
export const menuAside = supplementPath(menuData)
|
||||
// const menuData = [
|
||||
// { path: '/index', title: '首页', icon: 'home' },
|
||||
// {
|
||||
// title: '页面',
|
||||
// icon: 'folder-o',
|
||||
// children: [
|
||||
// { path: '/page1', title: '页面 1' },
|
||||
// { path: '/page2', title: '页面 2' },
|
||||
// { path: '/page3', title: '页面 3' }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
// export const menuHeader = supplementPath(menuData)
|
||||
//
|
||||
// export const menuAside = supplementPath(menuData)
|
||||
|
|
|
@ -7,9 +7,9 @@ import 'nprogress/nprogress.css'
|
|||
|
||||
import store from '@/store/index'
|
||||
import util from '@/libs/util.js'
|
||||
|
||||
// 路由数据
|
||||
import routes from './routes'
|
||||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
// 由于懒加载页面太多的话会造成webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
// fix vue-router NavigationDuplicated
|
||||
const VueRouterPush = VueRouter.prototype.push
|
||||
|
@ -23,22 +23,107 @@ VueRouter.prototype.replace = function replace (location) {
|
|||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
// 导出路由 在 main.js 里使用
|
||||
const router = new VueRouter({
|
||||
export function buildRouter (menuItem) {
|
||||
return {
|
||||
path: menuItem.routerPath,
|
||||
name: menuItem.routerName,
|
||||
meta: {
|
||||
title: menuItem.menuTitle,
|
||||
auth: menuItem.routerAuth === 1,
|
||||
hidden: menuItem.routerHidden === 1,
|
||||
cache: menuItem.routerHidden === 1
|
||||
},
|
||||
component: _import(menuItem.routerComponentPath)
|
||||
}
|
||||
}
|
||||
|
||||
export function buildRouterList (menuTree) {
|
||||
// 权限路由, 全是一级路由
|
||||
const routerData = []
|
||||
for (const menuItem of menuTree) {
|
||||
const children = menuItem.children
|
||||
if (children && children.length > 0) {
|
||||
for (const childMenuItem of children) {
|
||||
if (childMenuItem.routerPath && childMenuItem.routerComponentPath) {
|
||||
routerData.push(buildRouter(childMenuItem))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return routerData
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 创建在 layout 中显示的路由设置
|
||||
* @param {Array} routes 动态路由设置
|
||||
*/
|
||||
export function createRoutesInLayout (routes = []) {
|
||||
return [
|
||||
{
|
||||
path: '/',
|
||||
redirect: { name: 'index' },
|
||||
component: layoutHeaderAside,
|
||||
children: [
|
||||
{ path: 'index', name: 'index', meta: { title: '首页', auth: true }, component: _import('system/index') },
|
||||
{ path: 'log', name: 'log', meta: { title: '前端日志', auth: true }, component: _import('system/log') },
|
||||
...routes
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 在 layout 之外显示的路由
|
||||
export const routesOutLayout = [
|
||||
// 刷新页面 必须保留
|
||||
{ path: '/refresh', name: 'refresh', component: _import('system/function/refresh'), hidden: true },
|
||||
// 页面重定向 必须保留
|
||||
{ path: '/redirect/:route*', name: 'redirect', component: _import('system/function/redirect'), hidden: true },
|
||||
// 登陆页面 必须保留
|
||||
{ path: '/login', name: 'login', component: _import('system/login'), hidden: true },
|
||||
{ path: '*', name: '404', component: _import('system/error/404'), hidden: true }
|
||||
]
|
||||
|
||||
// 默认的路由
|
||||
export const constantRoutes = createRoutesInLayout().concat(routesOutLayout)
|
||||
|
||||
/**
|
||||
* @description 创建路由
|
||||
* @param {Array} routes 路由设置
|
||||
*/
|
||||
const createRouter = (routes = []) => new VueRouter({
|
||||
scrollBehavior: () => ({ y: 0 }),
|
||||
routes
|
||||
})
|
||||
|
||||
// 导出路由 在 main.js 里使用
|
||||
const router = createRouter(constantRoutes)
|
||||
|
||||
/**
|
||||
* @description 重新设置路由
|
||||
* @param {Array} routes 额外追加的路由
|
||||
*/
|
||||
export function resetRouter (routes = []) {
|
||||
router.matcher = createRouter(routes).matcher
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由拦截
|
||||
* 权限验证
|
||||
*/
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 进度条
|
||||
NProgress.start()
|
||||
console.log('即将进入的路由:', to);
|
||||
console.log('当前离开的路由:', from);
|
||||
// 获取路由参数或查询参数
|
||||
console.log('路由参数:', to.params);
|
||||
console.log('查询参数:', to.query);
|
||||
// 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
|
||||
await store.dispatch('d2admin/page/isLoaded')
|
||||
// 确认已经加载组件尺寸设置 https://github.com/d2-projects/d2-admin/issues/198
|
||||
await store.dispatch('d2admin/size/isLoaded')
|
||||
// 进度条
|
||||
NProgress.start()
|
||||
// 加载动态路由 内部已经做了对登录状态和是否已经加载动态路由的判断
|
||||
await store.dispatch('d2admin/router/load', { to: to.fullPath })
|
||||
// 关闭搜索面板
|
||||
store.commit('d2admin/search/set', false)
|
||||
// 验证当前路由所有的匹配中是否需要有登录验证的
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
import layoutHeaderAside from '@/layout/header-aside'
|
||||
|
||||
// 由于懒加载页面太多的话会造成webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载
|
||||
const _import = require('@/libs/util.import.' + process.env.NODE_ENV)
|
||||
|
||||
console.log('================= 刷新会导致路由重新加载')
|
||||
|
||||
/**
|
||||
* 在主框架内显示
|
||||
*/
|
||||
const frameIn = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: { name: 'index' },
|
||||
component: layoutHeaderAside,
|
||||
children: [
|
||||
// 首页
|
||||
{
|
||||
path: 'index',
|
||||
name: 'index',
|
||||
meta: {
|
||||
auth: true
|
||||
},
|
||||
component: _import('system/index')
|
||||
},
|
||||
// 系统 前端日志
|
||||
{
|
||||
path: 'log',
|
||||
name: 'log',
|
||||
meta: {
|
||||
title: '前端日志',
|
||||
auth: true
|
||||
},
|
||||
component: _import('system/log')
|
||||
},
|
||||
// 刷新页面 必须保留
|
||||
{
|
||||
path: 'refresh',
|
||||
name: 'refresh',
|
||||
hidden: true,
|
||||
component: _import('system/function/refresh')
|
||||
},
|
||||
// 页面重定向 必须保留
|
||||
{
|
||||
path: 'redirect/:route*',
|
||||
name: 'redirect',
|
||||
hidden: true,
|
||||
component: _import('system/function/redirect')
|
||||
},
|
||||
// 演示页面
|
||||
{
|
||||
path: 'page1',
|
||||
name: 'page1',
|
||||
meta: {
|
||||
title: '页面 1',
|
||||
auth: true
|
||||
},
|
||||
component: _import('demo/page1')
|
||||
},
|
||||
{
|
||||
path: 'page2',
|
||||
name: 'page2',
|
||||
meta: {
|
||||
title: '页面 2',
|
||||
auth: true
|
||||
},
|
||||
component: _import('demo/page2')
|
||||
},
|
||||
{
|
||||
path: 'page3',
|
||||
name: 'page3',
|
||||
meta: {
|
||||
title: '页面 3',
|
||||
auth: true
|
||||
},
|
||||
component: _import('demo/page3')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 在主框架之外显示
|
||||
*/
|
||||
const frameOut = [
|
||||
// 登录
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: _import('system/login')
|
||||
}
|
||||
]
|
||||
|
||||
/**
|
||||
* 错误页面
|
||||
*/
|
||||
const errorPage = [
|
||||
{
|
||||
path: '*',
|
||||
name: '404',
|
||||
component: _import('system/error/404')
|
||||
}
|
||||
]
|
||||
|
||||
// 导出需要显示菜单的
|
||||
export const frameInRoutes = frameIn
|
||||
|
||||
// 重新组织后导出
|
||||
export default [
|
||||
...frameIn,
|
||||
...frameOut,
|
||||
...errorPage
|
||||
]
|
|
@ -5,6 +5,10 @@ import api from '@/api'
|
|||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
// 用户登录状态
|
||||
isLogged: !!util.cookies.get('token')
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* @description 登录
|
||||
|
@ -13,10 +17,7 @@ export default {
|
|||
* @param {Object} payload password {String} 密码
|
||||
* @param {Object} payload route {Object} 登录成功后定向的路由对象 任何 vue-router 支持的格式
|
||||
*/
|
||||
async login ({ dispatch }, {
|
||||
username = '',
|
||||
password = ''
|
||||
} = {}) {
|
||||
async login ({ commit, dispatch }, { username = '', password = '', to = '/' } = {}) {
|
||||
const res = await api.login({ username, password })
|
||||
// 设置 cookie 一定要存 uuid 和 token 两个 cookie
|
||||
// 整个系统依赖这两个数据进行校验和存储
|
||||
|
@ -27,10 +28,10 @@ export default {
|
|||
util.cookies.set('username', res.username)
|
||||
util.cookies.set('name', res.name)
|
||||
util.cookies.set('token', res.token)
|
||||
// 设置路由菜单
|
||||
console.log('========= 设置路由菜单')
|
||||
const menuTree = await api.queryAllMenus({})
|
||||
console.log('========= ', menuTree)
|
||||
// 设置用户已经登陆
|
||||
commit('isLoggedSet', true)
|
||||
// 加载路由权限
|
||||
await dispatch('d2admin/router/load', { focus: true, to }, { root: true })
|
||||
// 设置 vuex 用户信息
|
||||
await dispatch('d2admin/user/set', { name: res.name }, { root: true })
|
||||
// 用户登录后从持久化数据加载一系列的设置
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import router, { buildRouterList, createRoutesInLayout, resetRouter, routesOutLayout } from '@/router'
|
||||
import api from '@/api'
|
||||
import { flushMenus, supplementPath } from '@/menu'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
// 是否已经加载
|
||||
isLoaded: false,
|
||||
// 用户权限
|
||||
permissions: []
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* @description 用户登录后从持久化数据加载一系列的设置
|
||||
* @param {Object} context
|
||||
*/
|
||||
async load ({ state, rootState, commit, dispatch }, { focus = false, to = '', data }) {
|
||||
// 取消请求 - 没有登录
|
||||
if (!data && !rootState.d2admin.account.isLogged) return
|
||||
// 取消请求 - 已经加载过动态路由
|
||||
if (!focus && state.isLoaded) return
|
||||
// 获取接口原始数据
|
||||
const menuTree = await api.queryAllMenus({})
|
||||
console.log('=================== loadXXXXXX')
|
||||
// [ 菜单 ] 计算菜单
|
||||
const menuData = flushMenus(menuTree)
|
||||
const menus = supplementPath(menuData)
|
||||
// [ 菜单 ] 设置侧栏菜单
|
||||
commit('d2admin/menu/asideSet', menus, { root: true })
|
||||
// [ 路由 ] 计算路由
|
||||
const routes = createRoutesInLayout(buildRouterList(menuTree)).concat(routesOutLayout)
|
||||
// [ 路由 ] 重新设置路由
|
||||
resetRouter(routes)
|
||||
// [ 路由 ] 重新设置多标签页池
|
||||
commit('d2admin/page/init', routes, { root: true })
|
||||
// [ 标签页 ] 重新计算多标签页数据
|
||||
dispatch('d2admin/page/openedLoad', { filter: true }, { root: true })
|
||||
// [ 搜索 ] 初始化搜索数据
|
||||
commit('d2admin/search/init', menus, { root: true })
|
||||
// [ 路由 ] 重新访问
|
||||
if (to) router.replace(to)
|
||||
// 标记已经加载过动态路由
|
||||
commit('isLoadedSet', true)
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
/**
|
||||
* @description 设置动态路由加载状态
|
||||
* @param {Object} state state
|
||||
* @param {Boolean} value 是否已经加载动态路由
|
||||
*/
|
||||
isLoadedSet (state, value) {
|
||||
state.isLoaded = value
|
||||
}
|
||||
}
|
||||
}
|
|
@ -175,12 +175,12 @@ export default {
|
|||
// 具体需要传递的数据请自行修改代码
|
||||
this.login({
|
||||
username: this.formLogin.username,
|
||||
password: this.formLogin.password
|
||||
password: this.formLogin.password,
|
||||
to: this.$route.query.redirect || '/'
|
||||
}).then(() => {
|
||||
// 重定向对象不存在则返回顶层路径
|
||||
this.$router.replace(this.$route.query.redirect || '/')
|
||||
})
|
||||
.then(() => {
|
||||
// 重定向对象不存在则返回顶层路径
|
||||
this.$router.replace(this.$route.query.redirect || '/')
|
||||
})
|
||||
} else {
|
||||
// 登录表单校验失败
|
||||
this.$message.error('表单校验失败,请检查')
|
||||
|
|
Loading…
Reference in New Issue