From 7e1edc0905b4cc1061d8e65603b8adc57d90aedc Mon Sep 17 00:00:00 2001 From: Odboy Date: Mon, 5 Feb 2024 19:59:04 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8F=9C=E5=8D=95=E8=B7=AF=E7=94=B1=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E5=8A=A0=E8=BD=BD=20ook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/system/domain/menu.go | 1 + web/src/main.js | 30 +++-- web/src/menu/index.js | 79 +++++++++--- web/src/router/index.js | 99 +++++++++++++-- web/src/router/routes.js | 113 ------------------ .../store/modules/d2admin/modules/account.js | 17 +-- .../store/modules/d2admin/modules/router.js | 57 +++++++++ web/src/views/system/login/page.vue | 10 +- 8 files changed, 247 insertions(+), 159 deletions(-) delete mode 100644 web/src/router/routes.js create mode 100644 web/src/store/modules/d2admin/modules/router.js diff --git a/src/modules/system/domain/menu.go b/src/modules/system/domain/menu.go index f9b666a..6ad489e 100644 --- a/src/modules/system/domain/menu.go +++ b/src/modules/system/domain/menu.go @@ -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"` } diff --git a/web/src/main.js b/web/src/main.js index b2d7075..2073033 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -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 () { // 展示系统信息 diff --git a/web/src/menu/index.js b/web/src/menu/index.js index fdcab4f..93bc709 100644 --- a/web/src/menu/index.js +++ b/web/src/menu/index.js @@ -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) diff --git a/web/src/router/index.js b/web/src/router/index.js index 50da5de..9530c6d 100755 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -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) // 验证当前路由所有的匹配中是否需要有登录验证的 diff --git a/web/src/router/routes.js b/web/src/router/routes.js deleted file mode 100644 index 7e3e934..0000000 --- a/web/src/router/routes.js +++ /dev/null @@ -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 -] diff --git a/web/src/store/modules/d2admin/modules/account.js b/web/src/store/modules/d2admin/modules/account.js index be7f06a..852d069 100644 --- a/web/src/store/modules/d2admin/modules/account.js +++ b/web/src/store/modules/d2admin/modules/account.js @@ -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 }) // 用户登录后从持久化数据加载一系列的设置 diff --git a/web/src/store/modules/d2admin/modules/router.js b/web/src/store/modules/d2admin/modules/router.js new file mode 100644 index 0000000..36b2be2 --- /dev/null +++ b/web/src/store/modules/d2admin/modules/router.js @@ -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 + } + } +} diff --git a/web/src/views/system/login/page.vue b/web/src/views/system/login/page.vue index 823c8af..5d9b5da 100644 --- a/web/src/views/system/login/page.vue +++ b/web/src/views/system/login/page.vue @@ -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('表单校验失败,请检查')