mirror of
https://github.com/iczer/vue-antd-admin
synced 2025-04-06 04:00:06 +08:00
feat: add support for async router; 🌟
新增:添加异步路由支持;
This commit is contained in:
parent
657c061b89
commit
f5b452f82b
@ -17,10 +17,9 @@ export default {
|
||||
}
|
||||
},
|
||||
created () {
|
||||
let _this = this
|
||||
this.setLanguage(this.lang)
|
||||
enquireScreen(isMobile => {
|
||||
_this.$store.commit('setting/setDevice', isMobile)
|
||||
this.$store.commit('setting/setDevice', isMobile)
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
|
23
src/bootstrap.js
vendored
Normal file
23
src/bootstrap.js
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// 应用启动时需要执行的操作放在这里
|
||||
import {loadRoutes} from '@/utils/routerUtil'
|
||||
|
||||
/**
|
||||
* 启动引导方法
|
||||
* @param router 应用的路由实例
|
||||
* @param store 应用的 vuex.store 实例
|
||||
* @param i18n 应用的 vue-i18n 实例
|
||||
*/
|
||||
function bootstrap({router, store, i18n}) {
|
||||
// 加载本地存储的异步路由
|
||||
const localRoutes = localStorage.getItem('routes')
|
||||
if (localRoutes) {
|
||||
try {
|
||||
const routesConfig = JSON.parse(localRoutes)
|
||||
loadRoutes(routesConfig, router, store, i18n)
|
||||
} catch (e) {
|
||||
console.error(e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default bootstrap
|
@ -35,8 +35,6 @@ import {mapState, mapMutations} from 'vuex'
|
||||
|
||||
const minHeight = window.innerHeight - 64 - 24 - 122
|
||||
|
||||
let menuData = []
|
||||
|
||||
export default {
|
||||
name: 'AdminLayout',
|
||||
components: {Setting, SideMenu, Drawer, PageFooter, AdminHeader},
|
||||
@ -44,12 +42,12 @@ export default {
|
||||
return {
|
||||
minHeight: minHeight,
|
||||
collapsed: false,
|
||||
menuData: menuData,
|
||||
showSetting: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('setting', ['isMobile', 'theme', 'layout', 'footerLinks', 'copyright', 'fixedHeader', 'fixedSideBar', 'hideSetting', 'pageMinHeight']),
|
||||
...mapState('setting', ['isMobile', 'theme', 'layout', 'footerLinks', 'copyright', 'fixedHeader', 'fixedSideBar',
|
||||
'hideSetting', 'menuData']),
|
||||
sideMenuWidth() {
|
||||
return this.collapsed ? '80px' : '256px'
|
||||
},
|
||||
@ -74,9 +72,6 @@ export default {
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.correctPageMinHeight(-minHeight + 1)
|
||||
},
|
||||
beforeCreate () {
|
||||
menuData = this.$router.options.routes.find((item) => item.path === '/').children
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -9,14 +9,16 @@ import store from './store'
|
||||
import 'animate.css/source/animate.css'
|
||||
import Plugins from '@/plugins'
|
||||
import {initI18n} from '@/utils/i18n'
|
||||
import bootstrap from '@/bootstrap'
|
||||
|
||||
const i18n = initI18n(router, 'CN', 'US')
|
||||
bootstrap({router, store, i18n})
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(Viser)
|
||||
Vue.use(Antd)
|
||||
Vue.use(Plugins)
|
||||
|
||||
const i18n = initI18n(router, 'CN', 'US')
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Mock from 'mockjs'
|
||||
import '@/mock/user/login'
|
||||
import '@/mock/user/routes'
|
||||
|
||||
// 设置全局延时
|
||||
Mock.setup({
|
||||
|
24
src/mock/user/routes.js
Normal file
24
src/mock/user/routes.js
Normal file
@ -0,0 +1,24 @@
|
||||
import Mock from 'mockjs'
|
||||
|
||||
Mock.mock('/routes', 'get', () => {
|
||||
let result = {}
|
||||
result.code = 0
|
||||
result.data = [{
|
||||
router: 'root',
|
||||
children: ['demo',
|
||||
{
|
||||
router: 'parent1',
|
||||
children: ['demo'],
|
||||
},
|
||||
{
|
||||
router: 'parent2',
|
||||
children: ['demo'],
|
||||
},
|
||||
{
|
||||
router: 'exception',
|
||||
children: ['exp404', 'exp403', 'exp500'],
|
||||
}
|
||||
]
|
||||
}]
|
||||
return result
|
||||
})
|
@ -75,8 +75,9 @@
|
||||
|
||||
<script>
|
||||
import CommonLayout from '@/layouts/CommonLayout'
|
||||
import {login} from '@/services'
|
||||
import {login, getRoutesConfig} from '@/services'
|
||||
import {setAuthorization} from '@/utils/request'
|
||||
import {loadRoutes} from '@/utils/routerUtil'
|
||||
|
||||
export default {
|
||||
name: 'Login',
|
||||
@ -107,15 +108,21 @@ export default {
|
||||
},
|
||||
afterLogin(res) {
|
||||
this.logging = false
|
||||
const result = res.data
|
||||
if (result.code >= 0) {
|
||||
const user = result.data.user
|
||||
setAuthorization({token: result.data.token, expireAt: new Date(result.data.expireAt)})
|
||||
this.$router.push('/parent1/demo1')
|
||||
this.$store.commit('account/setUser', user)
|
||||
this.$message.success(result.message, 3)
|
||||
const loginRes = res.data
|
||||
if (loginRes.code >= 0) {
|
||||
const user = loginRes.data.user
|
||||
setAuthorization({token: loginRes.data.token, expireAt: new Date(loginRes.data.expireAt)})
|
||||
// 获取路由配置
|
||||
getRoutesConfig().then(result => {
|
||||
const routesConfig = result.data.data
|
||||
localStorage.setItem('routes', JSON.stringify(routesConfig))
|
||||
loadRoutes(routesConfig, this.$router, this.$store, this.$i18n)
|
||||
this.$router.push('/parent1/demo')
|
||||
this.$store.commit('account/setUser', user)
|
||||
this.$message.success(loginRes.message, 3)
|
||||
})
|
||||
} else {
|
||||
this.error = result.message
|
||||
this.error = loginRes.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,92 +1,19 @@
|
||||
import Login from '@/pages/login/Login'
|
||||
import TabsView from '@/layouts/tabs/TabsView'
|
||||
import BlankView from '@/layouts/BlankView'
|
||||
import PageView from '@/layouts/PageView'
|
||||
import routerMap from './router.map'
|
||||
import {parseRoutes} from '@/utils/routerUtil'
|
||||
|
||||
// 路由配置
|
||||
const routesConfig = [
|
||||
'login',
|
||||
'root',
|
||||
{
|
||||
router: 'exp404',
|
||||
path: '*',
|
||||
name: '404'
|
||||
}
|
||||
]
|
||||
|
||||
const options = {
|
||||
routes: [
|
||||
{
|
||||
path: '/login',
|
||||
name: '登录页',
|
||||
component: Login
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
name: '404',
|
||||
component: () => import('@/pages/exception/404'),
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: '首页',
|
||||
component: TabsView,
|
||||
redirect: '/login',
|
||||
children: [
|
||||
{
|
||||
path: 'demo',
|
||||
name: '演示页0',
|
||||
meta: {
|
||||
icon: 'file-ppt'
|
||||
},
|
||||
component: () => import('@/pages/demo')
|
||||
},
|
||||
{
|
||||
path: 'parent1',
|
||||
name: '父级路由1',
|
||||
meta: {
|
||||
icon: 'dashboard'
|
||||
},
|
||||
component: BlankView,
|
||||
children: [
|
||||
{
|
||||
path: 'demo1',
|
||||
name: '演示页面1',
|
||||
component: () => import('@/pages/demo'),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'parent2',
|
||||
name: '父级路由2',
|
||||
meta: {
|
||||
icon: 'form'
|
||||
},
|
||||
component: PageView,
|
||||
children: [
|
||||
{
|
||||
path: 'demo2',
|
||||
name: '演示页面2',
|
||||
component: () => import('@/pages/demo'),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'exception',
|
||||
name: '异常页',
|
||||
meta: {
|
||||
icon: 'warning',
|
||||
},
|
||||
component: BlankView,
|
||||
children: [
|
||||
{
|
||||
path: '404',
|
||||
name: 'Exp404',
|
||||
component: () => import('@/pages/exception/404')
|
||||
},
|
||||
{
|
||||
path: '403',
|
||||
name: 'Exp403',
|
||||
component: () => import('@/pages/exception/403')
|
||||
},
|
||||
{
|
||||
path: '500',
|
||||
name: 'Exp500',
|
||||
component: () => import('@/pages/exception/500')
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
routes: parseRoutes(routesConfig, routerMap)
|
||||
}
|
||||
|
||||
// 不需要登录拦截的路由配置
|
||||
@ -103,4 +30,4 @@ const loginIgnore = {
|
||||
}
|
||||
}
|
||||
|
||||
export {options, loginIgnore}
|
||||
export {options, loginIgnore}
|
||||
|
@ -9,15 +9,15 @@ module.exports = {
|
||||
HK: {
|
||||
home: {name: '首頁'},
|
||||
demo: {
|
||||
name: '演示頁0'
|
||||
name: '演示頁'
|
||||
},
|
||||
parent1: {
|
||||
name: '父級路由1',
|
||||
demo1: {name: '演示頁面1'},
|
||||
demo: {name: '演示頁面1'},
|
||||
},
|
||||
parent2: {
|
||||
name: '父級路由2',
|
||||
demo2: {name: '演示頁面2'},
|
||||
demo: {name: '演示頁面2'},
|
||||
},
|
||||
exception: {
|
||||
name: '異常頁',
|
||||
|
62
src/router/router.map.js
Normal file
62
src/router/router.map.js
Normal file
@ -0,0 +1,62 @@
|
||||
// 视图组件
|
||||
const view = {
|
||||
tabs: () => import('@/layouts/tabs'),
|
||||
blank: () => import('@/layouts/BlankView'),
|
||||
page: () => import('@/layouts/PageView')
|
||||
}
|
||||
|
||||
// 路由组件注册
|
||||
const routerMap = {
|
||||
login: {
|
||||
authority: '*',
|
||||
path: '/login',
|
||||
component: () => import('@/pages/login')
|
||||
},
|
||||
demo: {
|
||||
authority: 'admin',
|
||||
name: '演示页',
|
||||
renderMenu: false,
|
||||
component: () => import('@/pages/demo')
|
||||
},
|
||||
exp403: {
|
||||
authority: 'admin',
|
||||
name: 'exp403',
|
||||
path: '403',
|
||||
component: () => import('@/pages/exception/403')
|
||||
},
|
||||
exp404: {
|
||||
authority: '*',
|
||||
name: 'exp404',
|
||||
path: '404',
|
||||
component: () => import('@/pages/exception/404')
|
||||
},
|
||||
exp500: {
|
||||
authority: 'admin',
|
||||
name: 'exp500',
|
||||
path: '500',
|
||||
component: () => import('@/pages/exception/500')
|
||||
},
|
||||
root: {
|
||||
path: '/',
|
||||
name: '首页',
|
||||
redirect: '/login',
|
||||
component: view.tabs
|
||||
},
|
||||
parent1: {
|
||||
name: '父级路由1',
|
||||
icon: 'dashboard',
|
||||
component: view.blank
|
||||
},
|
||||
parent2: {
|
||||
name: '父级路由2',
|
||||
icon: 'form',
|
||||
component: view.page
|
||||
},
|
||||
exception: {
|
||||
name: '异常页',
|
||||
icon: 'warning',
|
||||
component: view.blank
|
||||
}
|
||||
}
|
||||
export default routerMap
|
||||
|
@ -1,5 +1,6 @@
|
||||
// const BASE_URL = 'http://localhost:8080' your service base url
|
||||
const BASE_URL = '' // mock base url
|
||||
module.exports = {
|
||||
LOGIN: `${BASE_URL}/login`
|
||||
LOGIN: `${BASE_URL}/login`,
|
||||
ROUTES: `${BASE_URL}/routes`
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {login, logout} from './user'
|
||||
import {login, logout, getRoutesConfig} from './user'
|
||||
|
||||
export {
|
||||
login,
|
||||
logout
|
||||
logout,
|
||||
getRoutesConfig
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {LOGIN} from '@/services/api'
|
||||
import {LOGIN, ROUTES} from '@/services/api'
|
||||
import {request, METHOD, removeAuthorization} from '@/utils/request'
|
||||
|
||||
/**
|
||||
@ -14,11 +14,16 @@ async function login(name, password) {
|
||||
})
|
||||
}
|
||||
|
||||
async function getRoutesConfig() {
|
||||
return request(ROUTES, METHOD.GET)
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
function logout() {
|
||||
localStorage.removeItem('routes')
|
||||
removeAuthorization()
|
||||
}
|
||||
|
||||
export {login, logout}
|
||||
export {login, logout, getRoutesConfig}
|
||||
|
@ -12,5 +12,4 @@ const store = new Vuex.Store({modules})
|
||||
db.get('currUser')
|
||||
.then(doc => store.commit('account/setUser', doc.user))
|
||||
.catch(() => {})
|
||||
|
||||
export default store
|
||||
|
@ -8,6 +8,7 @@ export default {
|
||||
palettes: ADMIN.palettes,
|
||||
dustbins: [],
|
||||
pageMinHeight: 0,
|
||||
menuData: [],
|
||||
...config,
|
||||
},
|
||||
mutations: {
|
||||
@ -46,6 +47,9 @@ export default {
|
||||
},
|
||||
correctPageMinHeight(state, minHeight) {
|
||||
state.pageMinHeight += minHeight
|
||||
},
|
||||
setMenuData(state, menuData) {
|
||||
state.menuData = menuData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,20 +12,19 @@ import './Objects'
|
||||
*/
|
||||
function initI18n(router, locale, fallback) {
|
||||
Vue.use(VueI18n)
|
||||
const options = router.options.routes.find(item => item.path === '/').children
|
||||
formatOptions(options, '')
|
||||
const CN = generateI18n(new Object(), options, 'name')
|
||||
const US = generateI18n(new Object(), options, 'path')
|
||||
const i18n = new VueI18n({
|
||||
const rootRoute = router.options.routes.find(item => item.path === '/')
|
||||
const menuRoutes = rootRoute && rootRoute.children
|
||||
let i18nOptions = {
|
||||
locale,
|
||||
fallbackLocale: fallback,
|
||||
silentFallbackWarn: true,
|
||||
messages: {CN, US}
|
||||
})
|
||||
const messages = routesI18n.messages
|
||||
Object.keys(messages).forEach(key => {
|
||||
i18n.mergeLocaleMessage(key, messages[key])
|
||||
})
|
||||
messages: routesI18n.messages
|
||||
}
|
||||
const i18n = new VueI18n(i18nOptions)
|
||||
if (menuRoutes) {
|
||||
mergeI18nFromRoutes(i18n, menuRoutes)
|
||||
}
|
||||
|
||||
return i18n
|
||||
}
|
||||
|
||||
@ -62,6 +61,15 @@ function formatOptions(options, parentPath) {
|
||||
})
|
||||
}
|
||||
|
||||
function mergeI18nFromRoutes(i18n, routes) {
|
||||
formatOptions(routes, '')
|
||||
const CN = generateI18n(new Object(), routes, 'name')
|
||||
const US = generateI18n(new Object(), routes, 'path')
|
||||
i18n.mergeLocaleMessage('CN', CN)
|
||||
i18n.mergeLocaleMessage('US', US)
|
||||
}
|
||||
|
||||
export {
|
||||
initI18n
|
||||
}
|
||||
initI18n,
|
||||
mergeI18nFromRoutes
|
||||
}
|
||||
|
61
src/utils/routerUtil.js
Normal file
61
src/utils/routerUtil.js
Normal file
@ -0,0 +1,61 @@
|
||||
import routerMap from '@/router/router.map'
|
||||
import {mergeI18nFromRoutes} from '@/utils/i18n'
|
||||
import Router from 'vue-router'
|
||||
|
||||
/**
|
||||
* 根据 路由配置 和 路由组件注册 解析路由
|
||||
* @param routesConfig 路由配置
|
||||
* @param routerMap 本地路由组件注册配置
|
||||
*/
|
||||
function parseRoutes(routesConfig, routerMap) {
|
||||
let routes = []
|
||||
routesConfig.forEach(item => {
|
||||
// 读取 router,初始化 routeCfg
|
||||
let router = undefined, routeCfg = {}
|
||||
if (typeof item === 'string') {
|
||||
router = routerMap[item]
|
||||
routeCfg = {path: router.path || item, router: item}
|
||||
} else if (typeof item === 'object') {
|
||||
router = routerMap[item.router]
|
||||
routeCfg = item
|
||||
}
|
||||
// 从 register 和 routeCfg 解析路由
|
||||
if (!router) {
|
||||
console.warn(`can't find register for router ${routeCfg.router}, please register it in advance.`)
|
||||
} else {
|
||||
const route = {
|
||||
path: routeCfg.path || router.path || routeCfg.router,
|
||||
name: routeCfg.name || router.name,
|
||||
component: router.component,
|
||||
redirect: routeCfg.redirect || router.redirect,
|
||||
meta: {
|
||||
authority: routeCfg.authority || router.authority || '*',
|
||||
icon: routeCfg.icon || router.icon,
|
||||
page: routeCfg.page || router.page
|
||||
}
|
||||
}
|
||||
if (routeCfg.children && routeCfg.children.length > 0) {
|
||||
route.children = parseRoutes(routeCfg.children, routerMap)
|
||||
}
|
||||
routes.push(route)
|
||||
}
|
||||
})
|
||||
return routes
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载路由
|
||||
* @param routesConfig
|
||||
* @param router
|
||||
* @param store
|
||||
*/
|
||||
function loadRoutes(routesConfig, router, store, i18n) {
|
||||
const routes = parseRoutes(routesConfig, routerMap)
|
||||
router.matcher = new Router(router.options).matcher
|
||||
router.addRoutes(routes)
|
||||
const menuRoutes = routes.find(item => item.path === '/').children
|
||||
mergeI18nFromRoutes(i18n, menuRoutes)
|
||||
store.commit('setting/setMenuData', menuRoutes)
|
||||
}
|
||||
|
||||
export {parseRoutes, loadRoutes}
|
Loading…
x
Reference in New Issue
Block a user