parent
a3ce8e86d7
commit
47c1de01c0
@ -0,0 +1,35 @@ |
|||||||
|
import Vue from 'vue' |
||||||
|
import {createGetters} from "@/util/observable" |
||||||
|
|
||||||
|
const state = { |
||||||
|
show: false, |
||||||
|
current: '', |
||||||
|
list: [] |
||||||
|
} |
||||||
|
|
||||||
|
const store = Vue.observable(state) |
||||||
|
|
||||||
|
function add(src) { |
||||||
|
!store.list.includes(src) && store.list.push(src) |
||||||
|
} |
||||||
|
|
||||||
|
function del(src) { |
||||||
|
const index = store.list.findIndex(i => i === src) |
||||||
|
index > -1 && store.list.splice(index, 1) |
||||||
|
} |
||||||
|
|
||||||
|
function open({src}) { |
||||||
|
store.show = true |
||||||
|
store.current = src |
||||||
|
add(src) |
||||||
|
} |
||||||
|
|
||||||
|
function close({src, del: needDelete}) { |
||||||
|
store.show = false |
||||||
|
store.current = '' |
||||||
|
needDelete && del(src) |
||||||
|
} |
||||||
|
|
||||||
|
export const getters = createGetters(store) |
||||||
|
|
||||||
|
export const mutations = {del, open, close} |
||||||
@ -0,0 +1,66 @@ |
|||||||
|
import Vue from 'vue' |
||||||
|
import {isEmpty} from "@/util" |
||||||
|
import {isMobile} from "@/util/browser" |
||||||
|
import {createGetters, createMutations} from "@/util/observable" |
||||||
|
import {getLocalPersonalSettings} from "@/util/storage" |
||||||
|
|
||||||
|
const localSettings = getLocalPersonalSettings() |
||||||
|
|
||||||
|
const state = { |
||||||
|
//区分pc和移动端(mobile)
|
||||||
|
device: isMobile() ? 'mobile' : 'pc', |
||||||
|
|
||||||
|
//右侧块是否含有导航栏,这里初始值是为了转为boolean类型
|
||||||
|
hasNav: !!!localSettings.headerAutoHidden, |
||||||
|
|
||||||
|
//当前激活的顶部菜单的fullPath
|
||||||
|
activeRootMenu: '', |
||||||
|
|
||||||
|
//所有的树形菜单,每个元素为顶部菜单,顶部菜单的子级(如果有)为侧边栏菜单
|
||||||
|
menus: [], |
||||||
|
} |
||||||
|
|
||||||
|
const store = Vue.observable(state) |
||||||
|
|
||||||
|
//菜单排序
|
||||||
|
function sort(routes) { |
||||||
|
const getSortValue = item => { |
||||||
|
const sort = deepTap(item) |
||||||
|
return isEmpty(sort) ? 10000 : sort |
||||||
|
} |
||||||
|
const deepTap = item => { |
||||||
|
const {name, children = [], meta: {title, hidden, sort} = {}} = item |
||||||
|
if (hidden) return null |
||||||
|
if (!isEmpty(sort)) return sort |
||||||
|
//如果是类似首页那样的路由层级
|
||||||
|
if (isEmpty(name, title) && children.length === 1) { |
||||||
|
return deepTap(children[0]) |
||||||
|
} |
||||||
|
return null |
||||||
|
} |
||||||
|
|
||||||
|
routes.sort((pre, next) => { |
||||||
|
pre = getSortValue(pre) |
||||||
|
next = getSortValue(next) |
||||||
|
if (pre < next) return -1 |
||||||
|
else if (pre === next) return 0 |
||||||
|
else return 1 |
||||||
|
}) |
||||||
|
routes.forEach(route => { |
||||||
|
const {children} = route |
||||||
|
if (children && children.length) { |
||||||
|
sort(children) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
export const getters = createGetters(store) |
||||||
|
|
||||||
|
export const mutations = { |
||||||
|
...createMutations(store), |
||||||
|
|
||||||
|
menus(v) { |
||||||
|
sort(v) |
||||||
|
store.menus = v |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
import Vue from 'vue' |
||||||
|
import {route as routeConfig} from "@/config" |
||||||
|
import {mutations as iframeMutations} from "@/layout/store/iframe" |
||||||
|
import {createGetters, createMutations} from "@/util/observable" |
||||||
|
|
||||||
|
const state = { |
||||||
|
//显示的页签,{...route,title:route.meta.title}对象数组
|
||||||
|
visitedViews: [], |
||||||
|
|
||||||
|
//缓存的页签,用于<keep-router-view-alive/>:include
|
||||||
|
cachedViews: [], |
||||||
|
|
||||||
|
//路由过渡动画名称
|
||||||
|
transitionName: routeConfig.animate.default |
||||||
|
} |
||||||
|
|
||||||
|
const store = Vue.observable(state) |
||||||
|
|
||||||
|
function getCachedViewKey(view) { |
||||||
|
const {usePathKey, useFullPathKey} = view.meta || {} |
||||||
|
return usePathKey ? view.path : useFullPathKey ? view.fullPath : view.name |
||||||
|
} |
||||||
|
|
||||||
|
function addVisitedView(view) { |
||||||
|
const {title = '暂无标题'} = view.meta || {} |
||||||
|
|
||||||
|
if (store.visitedViews.some(v => v.path === view.path)) return |
||||||
|
|
||||||
|
store.visitedViews.push({...view, title}) |
||||||
|
} |
||||||
|
|
||||||
|
function addCachedView(view) { |
||||||
|
const {noCache, iframe, usePathKey, useFullPathKey} = view.meta || {} |
||||||
|
|
||||||
|
if (noCache || iframe || !view.name && !usePathKey && !useFullPathKey) return |
||||||
|
|
||||||
|
const key = getCachedViewKey(view) |
||||||
|
|
||||||
|
if (store.cachedViews.includes(key)) return |
||||||
|
|
||||||
|
store.cachedViews.push(key) |
||||||
|
} |
||||||
|
|
||||||
|
function delVisitedView(view) { |
||||||
|
const index = store.visitedViews.findIndex(i => i.path === view.path) |
||||||
|
index > -1 && store.visitedViews.splice(index, 1) |
||||||
|
} |
||||||
|
|
||||||
|
function delCachedView(view) { |
||||||
|
const key = getCachedViewKey(view) |
||||||
|
const index = store.cachedViews.indexOf(key) |
||||||
|
index > -1 && store.cachedViews.splice(index, 1) |
||||||
|
} |
||||||
|
|
||||||
|
export const getters = createGetters(store) |
||||||
|
|
||||||
|
export const mutations = { |
||||||
|
...createMutations(store), |
||||||
|
|
||||||
|
addVisitedView, |
||||||
|
addCachedView, |
||||||
|
addView(view) { |
||||||
|
addVisitedView(view) |
||||||
|
addCachedView(view) |
||||||
|
}, |
||||||
|
|
||||||
|
delVisitedView, |
||||||
|
delCachedView, |
||||||
|
delView(view) { |
||||||
|
delVisitedView(view) |
||||||
|
delCachedView(view) |
||||||
|
iframeMutations.del(view.meta ? view.meta.iframe : null) |
||||||
|
}, |
||||||
|
delOthersViews(view) { |
||||||
|
const visitedViews = store.visitedViews.filter(v => v.meta.affix || v.path === view.path) |
||||||
|
const name = store.cachedViews.find(name => name === getCachedViewKey(view)) |
||||||
|
|
||||||
|
store.visitedViews = visitedViews |
||||||
|
store.cachedViews = name ? [name] : [] |
||||||
|
}, |
||||||
|
delAllViews() { |
||||||
|
store.visitedViews = store.visitedViews.filter(tag => tag.meta && tag.meta.affix) |
||||||
|
store.cachedViews = [] |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
import {isEmpty} from "@/util" |
||||||
|
|
||||||
|
const beforeEach = (to, from, next) => { |
||||||
|
//若是共用组件的路由页面之间的跳转,借助redirect避免组件复用
|
||||||
|
const a = to.meta.commonModule, b = from.meta.commonModule |
||||||
|
const isComponentReuse = !isEmpty(a) && a === b |
||||||
|
if (isComponentReuse) { |
||||||
|
//这里vue-router会报redirect错误,已在main.js中忽略
|
||||||
|
return next(`/redirect${to.fullPath}`) |
||||||
|
} |
||||||
|
next() |
||||||
|
} |
||||||
|
|
||||||
|
export default function (router) { |
||||||
|
router.beforeEach(beforeEach) |
||||||
|
} |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
import {mutations as iframeMutations} from "@/layout/store/iframe" |
||||||
|
|
||||||
|
const beforeEach = (to, from, next) => { |
||||||
|
//从iframe页面离开时,判断是否需要删除iframe
|
||||||
|
if (from.meta.iframe) { |
||||||
|
let del = false |
||||||
|
//如果设置了无缓存或是进行了刷新,那么移除iframe
|
||||||
|
if (from.meta.noCache || to.path === `/redirect${from.path}`) { |
||||||
|
del = true |
||||||
|
} |
||||||
|
iframeMutations.close({src: from.meta.iframe, del}) |
||||||
|
} |
||||||
|
|
||||||
|
//跳转至iframe页面时,打开iframe
|
||||||
|
if (to.meta.iframe) { |
||||||
|
iframeMutations.open({src: to.meta.iframe}) |
||||||
|
} |
||||||
|
|
||||||
|
next() |
||||||
|
} |
||||||
|
|
||||||
|
export default function (router) { |
||||||
|
router.beforeEach(beforeEach) |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
import nprogress from './nprogress' |
||||||
|
import avoidReuse from './avoidReuse' |
||||||
|
import setInfo from './setInfo' |
||||||
|
import security from './security' |
||||||
|
import iframe from './iframe' |
||||||
|
|
||||||
|
export default function (router) { |
||||||
|
nprogress(router) |
||||||
|
avoidReuse(router) |
||||||
|
setInfo(router) |
||||||
|
security(router) |
||||||
|
iframe(router) |
||||||
|
} |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
import NProgress from 'nprogress' |
||||||
|
|
||||||
|
NProgress.configure({showSpinner: false}) |
||||||
|
|
||||||
|
const beforeEach = (to, from, next) => { |
||||||
|
//使用redirect进行跳转时不显示进度条
|
||||||
|
!to.path.startsWith('/redirect') && NProgress.start() |
||||||
|
|
||||||
|
next() |
||||||
|
} |
||||||
|
|
||||||
|
const afterEach = () => NProgress.done() |
||||||
|
|
||||||
|
export default function (router) { |
||||||
|
router.beforeEach(beforeEach) |
||||||
|
router.afterEach(afterEach) |
||||||
|
} |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
import {pathToRegexp} from 'path-to-regexp' |
||||||
|
import store from "@/store" |
||||||
|
import {auth, needAuth} from "@/util/auth" |
||||||
|
import {isUserExist} from "@/util/storage" |
||||||
|
|
||||||
|
const whiteList = ['/login', '/register', '/403', '/404', '/500'].map(url => pathToRegexp(url)) |
||||||
|
|
||||||
|
const beforeEach = async (to, from, next) => { |
||||||
|
//白名单内不需要进行权限控制
|
||||||
|
if (whiteList.some(reg => reg.test(to.path))) { |
||||||
|
return next() |
||||||
|
} |
||||||
|
|
||||||
|
//未登录时返回登录页
|
||||||
|
if (!isUserExist()) { |
||||||
|
return next({path: '/login', query: {redirect: to.fullPath}}) |
||||||
|
} |
||||||
|
|
||||||
|
//初始化路由和菜单权限
|
||||||
|
if (!store.state.resource.init) { |
||||||
|
await store.dispatch('resource/init', {...store.state.user, addRoutes: true}) |
||||||
|
return next({...to, replace: true}) |
||||||
|
} |
||||||
|
|
||||||
|
//页面不需要鉴权或有访问权限时通过
|
||||||
|
if (!needAuth(to) || auth(getAuthorizedPath(to))) { |
||||||
|
return next() |
||||||
|
} |
||||||
|
|
||||||
|
//用户无权限访问时的动作
|
||||||
|
next('/403') |
||||||
|
} |
||||||
|
|
||||||
|
//获取进行权限验证的路由地址,使用了动态路由的会用到
|
||||||
|
function getAuthorizedPath(route) { |
||||||
|
const {params, path, matched} = route |
||||||
|
const isDynamic = Object.values(params).some(v => path.includes(v)) |
||||||
|
return isDynamic ? matched[matched.length - 1].path : path |
||||||
|
} |
||||||
|
|
||||||
|
export default function (router) { |
||||||
|
router.beforeEach(beforeEach) |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
import {title} from "@/config" |
||||||
|
|
||||||
|
const beforeEach = (to, from, next) => { |
||||||
|
const {path, meta} = to |
||||||
|
|
||||||
|
if (!path.startsWith('/redirect')) { |
||||||
|
if (typeof meta.dynamicTitle === 'function') { |
||||||
|
meta.title = meta.dynamicTitle(to, from) |
||||||
|
} |
||||||
|
|
||||||
|
const pageTitle = meta.title |
||||||
|
document.title = pageTitle ? `${pageTitle} - ${title}` : title |
||||||
|
} |
||||||
|
|
||||||
|
next() |
||||||
|
} |
||||||
|
|
||||||
|
export default function (router) { |
||||||
|
router.beforeEach(beforeEach) |
||||||
|
} |
||||||
@ -1,38 +0,0 @@ |
|||||||
import {createMutations} from "@/util" |
|
||||||
|
|
||||||
const state = { |
|
||||||
show: false, |
|
||||||
current: '', |
|
||||||
list: [] |
|
||||||
} |
|
||||||
|
|
||||||
const mutations = { |
|
||||||
...createMutations(state), |
|
||||||
add(state, src) { |
|
||||||
!state.list.includes(src) && state.list.push(src) |
|
||||||
}, |
|
||||||
del(state, src) { |
|
||||||
const index = state.list.findIndex(i => i === src) |
|
||||||
index > -1 && state.list.splice(index, 1) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const actions = { |
|
||||||
open({commit}, {src}) { |
|
||||||
commit('show', true) |
|
||||||
commit('current', src) |
|
||||||
commit('add', src) |
|
||||||
}, |
|
||||||
close({commit}, {src, del}) { |
|
||||||
commit('show', false) |
|
||||||
commit('current', '') |
|
||||||
del && commit('del', src) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export default { |
|
||||||
namespaced: true, |
|
||||||
state, |
|
||||||
mutations, |
|
||||||
actions |
|
||||||
} |
|
||||||
@ -1,82 +0,0 @@ |
|||||||
import {route as routeConfig} from '@/config' |
|
||||||
import {createMutations} from "@/util" |
|
||||||
|
|
||||||
const state = { |
|
||||||
//显示的页签,{...route,title:route.meta.title}对象数组
|
|
||||||
visitedViews: [], |
|
||||||
|
|
||||||
//缓存的页签,用于<keep-router-view-alive/>:include
|
|
||||||
cachedViews: [], |
|
||||||
|
|
||||||
//路由过渡动画名称
|
|
||||||
transitionName: routeConfig.animate.default |
|
||||||
} |
|
||||||
|
|
||||||
const mutations = { |
|
||||||
...createMutations(state), |
|
||||||
|
|
||||||
addVisitedView(state, view) { |
|
||||||
const {title = '暂无标题'} = view.meta || {} |
|
||||||
|
|
||||||
if (state.visitedViews.some(v => v.path === view.path)) return |
|
||||||
|
|
||||||
state.visitedViews.push({...view, title}) |
|
||||||
}, |
|
||||||
addCachedView(state, view) { |
|
||||||
const {noCache, iframe, usePathKey, useFullPathKey} = view.meta || {} |
|
||||||
|
|
||||||
if (noCache || iframe || !view.name && !usePathKey && !useFullPathKey) return |
|
||||||
|
|
||||||
const key = getCachedViewKey(view) |
|
||||||
|
|
||||||
if (state.cachedViews.includes(key)) return |
|
||||||
|
|
||||||
state.cachedViews.push(key) |
|
||||||
}, |
|
||||||
|
|
||||||
delVisitedView(state, view) { |
|
||||||
const index = state.visitedViews.findIndex(i => i.path === view.path) |
|
||||||
index > -1 && state.visitedViews.splice(index, 1) |
|
||||||
}, |
|
||||||
delCachedView(state, view) { |
|
||||||
const key = getCachedViewKey(view) |
|
||||||
const index = state.cachedViews.indexOf(key) |
|
||||||
index > -1 && state.cachedViews.splice(index, 1) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const actions = { |
|
||||||
addView({commit}, view) { |
|
||||||
commit('addVisitedView', view) |
|
||||||
commit('addCachedView', view) |
|
||||||
}, |
|
||||||
delView({commit}, view) { |
|
||||||
commit('delVisitedView', view) |
|
||||||
commit('delCachedView', view) |
|
||||||
commit('iframe/del', view.meta ? view.meta.iframe : null, {root: true}) |
|
||||||
}, |
|
||||||
|
|
||||||
delOthersViews({state, commit}, view) { |
|
||||||
const visitedViews = state.visitedViews.filter(v => v.meta.affix || v.path === view.path) |
|
||||||
const name = state.cachedViews.find(name => name === getCachedViewKey(view)) |
|
||||||
|
|
||||||
commit('visitedViews', visitedViews) |
|
||||||
commit('cachedViews', name ? [name] : []) |
|
||||||
}, |
|
||||||
delAllViews({state, commit}) { |
|
||||||
commit('visitedViews', state.visitedViews.filter(tag => tag.meta && tag.meta.affix)) |
|
||||||
commit('cachedViews', []) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
function getCachedViewKey(view) { |
|
||||||
const {usePathKey, useFullPathKey} = view.meta || {} |
|
||||||
return usePathKey ? view.path : useFullPathKey ? view.fullPath : view.name |
|
||||||
} |
|
||||||
|
|
||||||
export default { |
|
||||||
namespaced: true, |
|
||||||
state, |
|
||||||
mutations, |
|
||||||
actions |
|
||||||
} |
|
||||||
@ -0,0 +1,36 @@ |
|||||||
|
import {getInitialValue} from "@/util" |
||||||
|
|
||||||
|
//为Vue.observer返回的对象设置getter
|
||||||
|
export function createGetters(store) { |
||||||
|
const getters = Object.create({}) |
||||||
|
Object.defineProperties( |
||||||
|
getters, |
||||||
|
Object.keys(store).reduce((obj, key) => { |
||||||
|
obj[key] = { |
||||||
|
enumerable: true, |
||||||
|
get() { |
||||||
|
return store[key] |
||||||
|
} |
||||||
|
} |
||||||
|
return obj |
||||||
|
}, {}) |
||||||
|
) |
||||||
|
return getters |
||||||
|
} |
||||||
|
|
||||||
|
//设置mutation
|
||||||
|
export function createMutations(store, all = false) { |
||||||
|
const keys = Object.keys(store) |
||||||
|
const obj = {} |
||||||
|
keys.forEach(key => { |
||||||
|
obj[key] = v => store[key] = v |
||||||
|
}) |
||||||
|
if (all) { |
||||||
|
obj['$all'] = v => { |
||||||
|
keys.forEach(key => { |
||||||
|
store[key] = v && v[key] || getInitialValue(store[key]) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
return obj |
||||||
|
} |
||||||
@ -1,9 +1,8 @@ |
|||||||
import {isEmpty} from "@/util" |
import {isEmpty} from "@/util" |
||||||
import router from '@/router' |
import router from '@/router' |
||||||
import store from '@/store' |
import {mutations as tagsViewMutations} from "@/layout/store/tagsView" |
||||||
|
|
||||||
export function closeCurrentPage(next) { |
export function closeCurrentPage(next) { |
||||||
return store |
tagsViewMutations.delView(router.currentPage) |
||||||
.dispatch('tagsView/delView', router.currentRoute) |
!isEmpty(next) && router.replace(next) |
||||||
.then(() => !isEmpty(next) && router.replace(next)) |
|
||||||
} |
} |
||||||
|
|||||||
Loading…
Reference in new issue