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 router from '@/router' |
||||
import store from '@/store' |
||||
import {mutations as tagsViewMutations} from "@/layout/store/tagsView" |
||||
|
||||
export function closeCurrentPage(next) { |
||||
return store |
||||
.dispatch('tagsView/delView', router.currentRoute) |
||||
.then(() => !isEmpty(next) && router.replace(next)) |
||||
tagsViewMutations.delView(router.currentPage) |
||||
!isEmpty(next) && router.replace(next) |
||||
} |
||||
|
||||
Loading…
Reference in new issue