qdt-admin-layout 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -0
- package/package.json +45 -0
- package/src/component/Aside/DefaultSidebar/index.vue +354 -0
- package/src/component/Aside/DefaultSidebar/style.scss +32 -0
- package/src/component/Aside/index.vue +30 -0
- package/src/component/Aside/style.scss +23 -0
- package/src/component/Aside/theme-dark.scss +19 -0
- package/src/component/Aside/theme-light.scss +18 -0
- package/src/component/Breadcrumb/index.vue +111 -0
- package/src/component/Breadcrumb/style.scss +27 -0
- package/src/component/CachedRouterView/index.vue +162 -0
- package/src/component/ContextMenu/functionalUse.js +26 -0
- package/src/component/ContextMenu/index.vue +140 -0
- package/src/component/ContextMenu/style.scss +30 -0
- package/src/component/ElMenu/item.vue +66 -0
- package/src/component/ElMenu/mixin.js +36 -0
- package/src/component/ElMenu/sub.vue +118 -0
- package/src/component/Hamburger/index.vue +27 -0
- package/src/component/Hamburger/style.scss +3 -0
- package/src/component/Header/index.vue +131 -0
- package/src/component/Header/style.scss +74 -0
- package/src/component/Header/theme-dark.scss +31 -0
- package/src/component/Header/theme-light.scss +15 -0
- package/src/component/HorizontalResizableMenu/GhostMenu.vue +101 -0
- package/src/component/HorizontalResizableMenu/index.vue +280 -0
- package/src/component/HorizontalScroller/index.vue +91 -0
- package/src/component/HorizontalScroller/style.scss +12 -0
- package/src/component/Layout/index.vue +153 -0
- package/src/component/Layout/style.scss +42 -0
- package/src/component/LoadingSpinner/index.vue +17 -0
- package/src/component/Logo/index.vue +41 -0
- package/src/component/Logo/style.scss +26 -0
- package/src/component/NavMenu/index.vue +206 -0
- package/src/component/NavMenu/style.scss +159 -0
- package/src/component/NavMenu/theme-dark.scss +59 -0
- package/src/component/NavMenu/theme-light.scss +81 -0
- package/src/component/Page/content.vue +58 -0
- package/src/component/Page/iframe.vue +63 -0
- package/src/component/Page/index.vue +22 -0
- package/src/component/Page/style.scss +48 -0
- package/src/component/Redirect/index.vue +19 -0
- package/src/component/TagsView/index.vue +255 -0
- package/src/component/TagsView/style.scss +51 -0
- package/src/component/TagsView/util.js +67 -0
- package/src/config/const.js +24 -0
- package/src/config/defaultRoute.js +23 -0
- package/src/config/index.js +4 -0
- package/src/config/logic.js +53 -0
- package/src/helper.js +43 -0
- package/src/index.js +15 -0
- package/src/mixin/menu.js +72 -0
- package/src/store/app.js +132 -0
- package/src/store/aside.js +92 -0
- package/src/store/header.js +37 -0
- package/src/store/index.js +20 -0
- package/src/store/page.js +67 -0
- package/src/store/tagsView.js +186 -0
- package/src/store/util.js +35 -0
- package/src/style/index.scss +23 -0
- package/src/style/maxViewHeight.scss +65 -0
- package/src/style/transition.scss +71 -0
- package/src/style/var.scss +81 -0
- package/src/util.js +69 -0
- package/types/config.d.ts +12 -0
- package/types/helper.d.ts +10 -0
- package/types/index.d.ts +5 -0
- package/types/menu.d.ts +17 -0
- package/types/route.d.ts +15 -0
- package/types/store.d.ts +156 -0
- package/types/vue-router.d.ts +7 -0
package/src/store/app.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import Vue from 'vue'
|
|
2
|
+
import { createGetters, createMutations } from './util'
|
|
3
|
+
import { isMobile } from '../helper'
|
|
4
|
+
|
|
5
|
+
const state = {
|
|
6
|
+
// 区分pc和移动端
|
|
7
|
+
isMobile: isMobile(),
|
|
8
|
+
|
|
9
|
+
// 标题,目前只配合logo使用
|
|
10
|
+
title: '',
|
|
11
|
+
|
|
12
|
+
// logo地址
|
|
13
|
+
logo: '',
|
|
14
|
+
|
|
15
|
+
// 点击logo后跳转的路由,作为router.push/replace的第一个参数
|
|
16
|
+
logoRoute: '/',
|
|
17
|
+
|
|
18
|
+
// 点击logo容器时触发,会替换原有的逻辑,(e) => any
|
|
19
|
+
onLogoClick: undefined,
|
|
20
|
+
|
|
21
|
+
// 是否显示侧边栏或顶栏的logo
|
|
22
|
+
showLogo: true,
|
|
23
|
+
|
|
24
|
+
// 当前激活的顶部菜单的fullPath
|
|
25
|
+
activeRootMenu: '',
|
|
26
|
+
|
|
27
|
+
// 所有的树形菜单,mix导航模式时,每个元素为顶部菜单,顶部菜单的子级(如果有)为侧边栏菜单
|
|
28
|
+
menus: [],
|
|
29
|
+
|
|
30
|
+
// 是否正在加载菜单,会让侧边栏菜单和顶栏菜单呈现加载状态
|
|
31
|
+
loadingMenu: false,
|
|
32
|
+
|
|
33
|
+
// 分层结构,上下('top-bottom')、左右('left-right')
|
|
34
|
+
struct: 'left-right',
|
|
35
|
+
|
|
36
|
+
// 导航模式,'aside'、'head'、'mix'
|
|
37
|
+
navMode: 'mix',
|
|
38
|
+
|
|
39
|
+
// 自定义渲染logo,(h, {img, title, props}) => VNode | VNode[]
|
|
40
|
+
logoSlot: undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const store = Vue.observable(state)
|
|
44
|
+
|
|
45
|
+
//加速查找menu的哈希表:<k: menu.fullPath, v: menu>
|
|
46
|
+
let MenuSearchMap = Object.create(null)
|
|
47
|
+
|
|
48
|
+
//对菜单进行排序、增加parent属性,并将转换后的菜单节点放入查找表中
|
|
49
|
+
function transformMenu(menus, parent) {
|
|
50
|
+
if (!menus) return
|
|
51
|
+
|
|
52
|
+
const copy = menus.map(menu => ({ ...menu }))
|
|
53
|
+
|
|
54
|
+
copy.sort((pre, next) => {
|
|
55
|
+
pre = getSortValue(pre)
|
|
56
|
+
next = getSortValue(next)
|
|
57
|
+
if (pre < next) return -1
|
|
58
|
+
if (pre === next) return 0
|
|
59
|
+
return 1
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
copy.forEach(menu => {
|
|
63
|
+
menu.parent = parent
|
|
64
|
+
MenuSearchMap[menu.fullPath] = menu
|
|
65
|
+
|
|
66
|
+
if (menu.children) {
|
|
67
|
+
menu.children = transformMenu(menu.children, menu)
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
return copy
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//菜单排序值的空值处理
|
|
75
|
+
function getSortValue(item) {
|
|
76
|
+
const sort = deepGetSortValue(item)
|
|
77
|
+
return sort == null ? 10000 : sort
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//获取菜单的排序值
|
|
81
|
+
function deepGetSortValue(item) {
|
|
82
|
+
const { children = [], meta: { sort } = {} } = item
|
|
83
|
+
|
|
84
|
+
if (sort != null) return sort
|
|
85
|
+
|
|
86
|
+
// 如果只有一个子节点,那么取子节点的排序值
|
|
87
|
+
if (children.length === 1) {
|
|
88
|
+
return deepGetSortValue(children[0])
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const getters = createGetters(store)
|
|
95
|
+
|
|
96
|
+
export const mutations = {
|
|
97
|
+
...createMutations(store),
|
|
98
|
+
|
|
99
|
+
menus(v) {
|
|
100
|
+
// 每次更新菜单时都需要清空加速表
|
|
101
|
+
MenuSearchMap = Object.create(null)
|
|
102
|
+
|
|
103
|
+
if (!Array.isArray(v)) {
|
|
104
|
+
return store.menus = []
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
store.menus = transformMenu(v)
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 修改某一个菜单的meta属性
|
|
112
|
+
*
|
|
113
|
+
* @param fullPath {string} 菜单的fullPath
|
|
114
|
+
* @param meta {MenuItemMeta}
|
|
115
|
+
*/
|
|
116
|
+
modifyMenuMeta(fullPath, meta) {
|
|
117
|
+
const menu = getMenuByFullPath(fullPath)
|
|
118
|
+
if (!menu) {
|
|
119
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
120
|
+
console.warn(`[appMutations.modifyMenu] 没有fullPath为${fullPath}的菜单`)
|
|
121
|
+
}
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
menu.meta = { ...menu.meta, ...meta }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
//根据菜单的fullPath快速查找菜单
|
|
130
|
+
export function getMenuByFullPath(fullPath) {
|
|
131
|
+
return MenuSearchMap[fullPath]
|
|
132
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 侧边栏的响应式数据
|
|
3
|
+
*/
|
|
4
|
+
import Vue from 'vue'
|
|
5
|
+
import { getters as appGetters } from './app'
|
|
6
|
+
import { createGetters, createMutations } from './util'
|
|
7
|
+
|
|
8
|
+
const state = {
|
|
9
|
+
// 抽屉模式时的显隐
|
|
10
|
+
show: false,
|
|
11
|
+
|
|
12
|
+
// 主题,light 或 dark
|
|
13
|
+
theme: 'dark',
|
|
14
|
+
|
|
15
|
+
// 手风琴效果
|
|
16
|
+
uniqueOpen: true,
|
|
17
|
+
|
|
18
|
+
// 是否折叠
|
|
19
|
+
collapse: false,
|
|
20
|
+
|
|
21
|
+
// 折叠时显示上级
|
|
22
|
+
showParentOnCollapse: false,
|
|
23
|
+
|
|
24
|
+
// 是否显示汉堡包
|
|
25
|
+
showHamburger: false,
|
|
26
|
+
|
|
27
|
+
// 自动隐藏
|
|
28
|
+
autoHide: false,
|
|
29
|
+
|
|
30
|
+
// 是否在没有菜单时也渲染侧边栏
|
|
31
|
+
alwaysRender: false,
|
|
32
|
+
|
|
33
|
+
// 在侧边栏渲染前对菜单数据进行操作的函数(menus => changedMenus:array),需要返回修改后的菜单数组!
|
|
34
|
+
postMenus: null,
|
|
35
|
+
|
|
36
|
+
// 传递给nav-menu,子菜单的单位缩进距离,默认为var.scss中的$menu-padding
|
|
37
|
+
inlineIndent: 26,
|
|
38
|
+
|
|
39
|
+
// 侧边栏菜单变化时的过渡动画名称,最终传递给transition的name属性,为空时不使用过渡动画
|
|
40
|
+
switchTransitionName: 'sidebar',
|
|
41
|
+
|
|
42
|
+
// 默认展开的菜单的fullPath数组
|
|
43
|
+
defaultOpeneds: [],
|
|
44
|
+
|
|
45
|
+
// 自定义渲染侧边栏内容,(h) => VNode
|
|
46
|
+
defaultSlot: undefined,
|
|
47
|
+
// 自定义渲染侧边栏头部内容,(h, logo) => VNode | VNode[]
|
|
48
|
+
headerSlot: undefined,
|
|
49
|
+
// 自定义渲染侧边栏底部内容,(h, hamburger) => VNode | VNode[]
|
|
50
|
+
footerSlot: undefined,
|
|
51
|
+
// 自定义渲染菜单图标,(h, {menu, depth}) => VNode
|
|
52
|
+
menuIconSlot: undefined,
|
|
53
|
+
// 自定义渲染菜单内容,(h, {menu, depth}) => VNode
|
|
54
|
+
menuContentSlot: undefined
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const store = Vue.observable(state)
|
|
58
|
+
|
|
59
|
+
export const getters = createGetters(store)
|
|
60
|
+
|
|
61
|
+
export const mutations = {
|
|
62
|
+
...createMutations(store),
|
|
63
|
+
|
|
64
|
+
/*移动端或设置了侧边栏自动隐藏时打开关闭抽屉,否则展开折叠*/
|
|
65
|
+
open() {
|
|
66
|
+
if (appGetters.isMobile || store.autoHide) {
|
|
67
|
+
store.show = true
|
|
68
|
+
}
|
|
69
|
+
else store.collapse = false
|
|
70
|
+
},
|
|
71
|
+
close() {
|
|
72
|
+
if (appGetters.isMobile || store.autoHide) {
|
|
73
|
+
store.show = false
|
|
74
|
+
}
|
|
75
|
+
else store.collapse = true
|
|
76
|
+
},
|
|
77
|
+
// 切换侧边栏的状态
|
|
78
|
+
switch(action) {
|
|
79
|
+
switch (action) {
|
|
80
|
+
case 'open':
|
|
81
|
+
return mutations.open()
|
|
82
|
+
case 'close':
|
|
83
|
+
return mutations.close()
|
|
84
|
+
default :
|
|
85
|
+
const open =
|
|
86
|
+
appGetters.isMobile
|
|
87
|
+
? !store.show
|
|
88
|
+
: store.collapse
|
|
89
|
+
open ? mutations.open() : mutations.close()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Vue from 'vue'
|
|
2
|
+
import { createGetters, createMutations } from './util'
|
|
3
|
+
|
|
4
|
+
const state = {
|
|
5
|
+
// 主题,light 或 dark
|
|
6
|
+
theme: 'light',
|
|
7
|
+
// 是否显示第一级水平菜单的展开折叠图标
|
|
8
|
+
showCollapseIcon: false,
|
|
9
|
+
|
|
10
|
+
// 头像地址
|
|
11
|
+
avatar: '',
|
|
12
|
+
|
|
13
|
+
// 用户名称
|
|
14
|
+
username: '',
|
|
15
|
+
|
|
16
|
+
// 下拉菜单项,{icon:图标, content:菜单内容, handler:点击时触发的方法}
|
|
17
|
+
dropdownItems: [],
|
|
18
|
+
|
|
19
|
+
// 自定义渲染下拉菜单项,(h) => VNode[]
|
|
20
|
+
dropdownItemsSlot: undefined,
|
|
21
|
+
// 自定义渲染左侧内容,(h, [logo, hamburger]) => VNode | VNode[]
|
|
22
|
+
leftSlot: undefined,
|
|
23
|
+
// 自定义渲染中部内容,(h, [headMenu]) => VNode | VNode[]
|
|
24
|
+
centerSlot: undefined,
|
|
25
|
+
// 自定义渲染右侧内容,(h, [refreshBtn, dropdown]) => VNode | VNode[]
|
|
26
|
+
rightSlot: undefined,
|
|
27
|
+
// 自定义渲染菜单图标,(h, {menu, depth}) => VNode
|
|
28
|
+
menuIconSlot: undefined,
|
|
29
|
+
// 自定义渲染菜单内容,(h, {menu, depth}) => VNode
|
|
30
|
+
menuContentSlot: undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const store = Vue.observable(state)
|
|
34
|
+
|
|
35
|
+
export const getters = createGetters(store)
|
|
36
|
+
|
|
37
|
+
export const mutations = createMutations(store)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { getters as appGetters, mutations as appMutations, getMenuByFullPath } from './app'
|
|
2
|
+
export { getters as asideGetters, mutations as asideMutations } from './aside'
|
|
3
|
+
export { getters as headerGetters, mutations as headerMutations } from './header'
|
|
4
|
+
export { getters as pageGetters, mutations as pageMutations } from './page'
|
|
5
|
+
export { getters as tagsViewGetters, mutations as tagsViewMutations } from './tagsView'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 简化从store中批量取值的过程
|
|
9
|
+
*
|
|
10
|
+
* @template T
|
|
11
|
+
* @param store {T}
|
|
12
|
+
* @param propNames {string[]}
|
|
13
|
+
* @return {{[key: string]: function(): any}}
|
|
14
|
+
*/
|
|
15
|
+
export function mapGetters(store, propNames) {
|
|
16
|
+
return propNames.reduce((map, prop) => {
|
|
17
|
+
map[prop] = () => store[prop]
|
|
18
|
+
return map
|
|
19
|
+
}, {})
|
|
20
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 路由页面的响应式数据
|
|
3
|
+
*/
|
|
4
|
+
import Vue from 'vue'
|
|
5
|
+
import { createGetters, createMutations } from './util'
|
|
6
|
+
|
|
7
|
+
const state = {
|
|
8
|
+
// 是否启用过渡动画
|
|
9
|
+
enableTransition: true,
|
|
10
|
+
// 路由过渡动画
|
|
11
|
+
transition: {
|
|
12
|
+
// 当未启用多页签时的路由动画
|
|
13
|
+
default: 'el-fade-in',
|
|
14
|
+
// 要访问的tab顺序高于上一个访问的tab时的路由动画
|
|
15
|
+
next: 'left-out',
|
|
16
|
+
// 要访问的tab顺序不高于上一个访问的tab时的路由动画
|
|
17
|
+
prev: 'right-out',
|
|
18
|
+
// 当前使用的路由动画
|
|
19
|
+
curr: 'el-fade-in'
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
/*iframe的控制*/
|
|
23
|
+
showIframe: false,
|
|
24
|
+
currentIframe: '',
|
|
25
|
+
iframeList: [],
|
|
26
|
+
|
|
27
|
+
// 是否显示页头
|
|
28
|
+
showHeader: true,
|
|
29
|
+
// 是否显示页脚
|
|
30
|
+
showFooter: true,
|
|
31
|
+
|
|
32
|
+
// 自定义渲染页头内容,(h) => VNode | VNode[]
|
|
33
|
+
headerSlot: undefined,
|
|
34
|
+
// 自定义渲染页脚内容,(h) => VNode | VNode[]
|
|
35
|
+
footerSlot: undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const store = Vue.observable(state)
|
|
39
|
+
|
|
40
|
+
export const getters = createGetters(store)
|
|
41
|
+
|
|
42
|
+
export const mutations = {
|
|
43
|
+
...createMutations(store),
|
|
44
|
+
|
|
45
|
+
// 修改transition时使用Object.assign
|
|
46
|
+
transition(obj) {
|
|
47
|
+
Object.assign(store.transition, obj)
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
addIframe(src) {
|
|
51
|
+
!store.iframeList.includes(src) && store.iframeList.push(src)
|
|
52
|
+
},
|
|
53
|
+
delIframe(src) {
|
|
54
|
+
const index = store.iframeList.findIndex(i => i === src)
|
|
55
|
+
index > -1 && store.iframeList.splice(index, 1)
|
|
56
|
+
},
|
|
57
|
+
openIframe(src) {
|
|
58
|
+
store.showIframe = true
|
|
59
|
+
store.currentIframe = src
|
|
60
|
+
mutations.addIframe(src)
|
|
61
|
+
},
|
|
62
|
+
closeIframe(src, del) {
|
|
63
|
+
store.showIframe = false
|
|
64
|
+
store.currentIframe = ''
|
|
65
|
+
del && mutations.delIframe(src)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 多页签的响应式数据
|
|
3
|
+
*/
|
|
4
|
+
import Vue from 'vue'
|
|
5
|
+
import { getters as pageGetters, mutations as pageMutations } from './page'
|
|
6
|
+
import { createGetters, createMutations } from './util'
|
|
7
|
+
import { isEmpty } from '../util'
|
|
8
|
+
import { getRouterKey } from '../config/logic'
|
|
9
|
+
|
|
10
|
+
const state = {
|
|
11
|
+
// 是否启用
|
|
12
|
+
enabled: true,
|
|
13
|
+
// 是否启用缓存功能
|
|
14
|
+
enableCache: true,
|
|
15
|
+
// 是否启用根据页签顺序来确定过渡动画的功能
|
|
16
|
+
enableChangeTransition: true,
|
|
17
|
+
|
|
18
|
+
// 自定义渲染页签,(h) => VNode
|
|
19
|
+
itemSlot: undefined,
|
|
20
|
+
|
|
21
|
+
// 显示的页签
|
|
22
|
+
visitedViews: [],
|
|
23
|
+
// 需要缓存的页签key的数组,用于<keep-router-view-alive/>:include
|
|
24
|
+
cachedViews: []
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const store = Vue.observable(state)
|
|
28
|
+
|
|
29
|
+
export const getters = createGetters(store)
|
|
30
|
+
|
|
31
|
+
export const mutations = {
|
|
32
|
+
...createMutations(store),
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 多页签的启用/停用
|
|
36
|
+
* 停用时会移除所有缓存,并且重置路由过渡动画
|
|
37
|
+
* @param v {boolean} 启用为true,停用为false
|
|
38
|
+
*/
|
|
39
|
+
enabled(v) {
|
|
40
|
+
store.enabled = v
|
|
41
|
+
|
|
42
|
+
if (!v) {
|
|
43
|
+
pageMutations.transition({ curr: pageGetters.transition.default })
|
|
44
|
+
mutations.delAllTagAndCache()
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 多页签缓存功能的启用/停用
|
|
50
|
+
* 停用时会移除所有缓存
|
|
51
|
+
* @param v {boolean}
|
|
52
|
+
*/
|
|
53
|
+
enableCache(v) {
|
|
54
|
+
store.enableCache = v
|
|
55
|
+
!v && mutations.delAllCache()
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 根据页签顺序来确定过渡动画功能的启用/停用
|
|
60
|
+
* 停用时会将pageGetters.transition.curr设为为默认值
|
|
61
|
+
* @param v {boolean}
|
|
62
|
+
*/
|
|
63
|
+
enableChangeTransition(v) {
|
|
64
|
+
store.enableChangeTransition = v
|
|
65
|
+
pageMutations.transition({ curr: pageGetters.transition.default })
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 在页签栏上添加一个页签,没有标题、已存在的不会重复添加
|
|
70
|
+
* @param view {View}
|
|
71
|
+
*/
|
|
72
|
+
addTagOnly(view) {
|
|
73
|
+
if (isEmpty(view.meta.title)) {
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const key = getRouterKey(view)
|
|
78
|
+
|
|
79
|
+
if (store.visitedViews.some(v => v.key === key)) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 增加key属性
|
|
84
|
+
store.visitedViews.push({ ...view, key })
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 将传入的页签加入缓存中
|
|
89
|
+
* 以下调用无效:设置了不缓存、已缓存
|
|
90
|
+
* @param view {View}
|
|
91
|
+
*/
|
|
92
|
+
addCacheOnly(view) {
|
|
93
|
+
const { noCache } = view.meta
|
|
94
|
+
|
|
95
|
+
const key = getRouterKey(view)
|
|
96
|
+
|
|
97
|
+
if (noCache === true || store.cachedViews.includes(key)) {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
store.cachedViews.push(key)
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 同时调用{@link #addTagOnly}、{@link #addCacheOnly}
|
|
106
|
+
* @param view {View}
|
|
107
|
+
*/
|
|
108
|
+
addTagAndCache(view) {
|
|
109
|
+
mutations.addTagOnly(view)
|
|
110
|
+
mutations.addCacheOnly(view)
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 从页签栏中移除一个页签
|
|
115
|
+
* @param view {View}
|
|
116
|
+
*/
|
|
117
|
+
delTagOnly(view) {
|
|
118
|
+
const key = getRouterKey(view)
|
|
119
|
+
const index = store.visitedViews.findIndex(i => i.key === key)
|
|
120
|
+
index > -1 && store.visitedViews.splice(index, 1)
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 删除对应的缓存
|
|
125
|
+
* @param view {View}
|
|
126
|
+
*/
|
|
127
|
+
delCacheOnly(view) {
|
|
128
|
+
const key = getRouterKey(view)
|
|
129
|
+
const index = store.cachedViews.indexOf(key)
|
|
130
|
+
index > -1 && store.cachedViews.splice(index, 1)
|
|
131
|
+
|
|
132
|
+
// 移除iframe
|
|
133
|
+
const { iframe } = view.meta
|
|
134
|
+
iframe && pageMutations.delIframe(iframe)
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 同时调用{@link #delTagOnly}、{@link #delCacheOnly}
|
|
139
|
+
* @param view {View}
|
|
140
|
+
*/
|
|
141
|
+
delTagAndCache(view) {
|
|
142
|
+
mutations.delTagOnly(view)
|
|
143
|
+
mutations.delCacheOnly(view)
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 从页签栏上移除其他的非固定页签以及其他的缓存
|
|
148
|
+
* @param view {View}
|
|
149
|
+
*/
|
|
150
|
+
delOtherTagAndCache(view) {
|
|
151
|
+
// 记录被移除的iframe
|
|
152
|
+
const removeIframe = []
|
|
153
|
+
|
|
154
|
+
const key = getRouterKey(view)
|
|
155
|
+
const cachedKey = store.cachedViews.find(i => i === key)
|
|
156
|
+
|
|
157
|
+
store.visitedViews = store.visitedViews.filter(v => {
|
|
158
|
+
if (v.meta.affix || v.key === key) {
|
|
159
|
+
return true
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
v.meta.iframe && removeIframe.push(v.meta.iframe)
|
|
163
|
+
|
|
164
|
+
return false
|
|
165
|
+
})
|
|
166
|
+
store.cachedViews = cachedKey ? [cachedKey] : []
|
|
167
|
+
|
|
168
|
+
pageMutations.iframeList(pageGetters.iframeList.filter(i => !removeIframe.includes(i)))
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 移除所有缓存
|
|
173
|
+
*/
|
|
174
|
+
delAllCache() {
|
|
175
|
+
store.cachedViews = []
|
|
176
|
+
pageMutations.iframeList([])
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 从页签栏上移除所有非固定页签,并且移除所有缓存
|
|
181
|
+
*/
|
|
182
|
+
delAllTagAndCache() {
|
|
183
|
+
store.visitedViews = store.visitedViews.filter(v => v.meta.affix)
|
|
184
|
+
mutations.delAllCache()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 为Vue.observer返回的对象设置getter
|
|
3
|
+
*
|
|
4
|
+
* @template T
|
|
5
|
+
* @param store {T}
|
|
6
|
+
* @returns {{[key in keyof T]: T[key]}}
|
|
7
|
+
*/
|
|
8
|
+
export function createGetters(store) {
|
|
9
|
+
const getters = Object.create(null)
|
|
10
|
+
Object.defineProperties(
|
|
11
|
+
getters,
|
|
12
|
+
Object.keys(store).reduce((obj, key) => {
|
|
13
|
+
obj[key] = {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: () => store[key]
|
|
16
|
+
}
|
|
17
|
+
return obj
|
|
18
|
+
}, {})
|
|
19
|
+
)
|
|
20
|
+
return getters
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 为Vue.observer返回的对象设置mutation
|
|
25
|
+
*
|
|
26
|
+
* @template T
|
|
27
|
+
* @param store {T}
|
|
28
|
+
* @returns {{[key in keyof T]: function(val: T[key]): void}}
|
|
29
|
+
*/
|
|
30
|
+
export function createMutations(store) {
|
|
31
|
+
return Object.keys(store).reduce((obj, key) => {
|
|
32
|
+
obj[key] = v => store[key] = v
|
|
33
|
+
return obj
|
|
34
|
+
}, Object.create(null))
|
|
35
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
@import './var';
|
|
2
|
+
|
|
3
|
+
@import './transition';
|
|
4
|
+
|
|
5
|
+
@import '../component/Layout/style';
|
|
6
|
+
@import '../component/Aside/style';
|
|
7
|
+
@import '../component/Header/style';
|
|
8
|
+
@import '../component/Page/style';
|
|
9
|
+
@import '../component/TagsView/style';
|
|
10
|
+
|
|
11
|
+
@import '../component/Breadcrumb/style';
|
|
12
|
+
@import '../component/ContextMenu/style';
|
|
13
|
+
@import '../component/Hamburger/style';
|
|
14
|
+
@import '../component/Logo/style';
|
|
15
|
+
@import '../component/NavMenu/style';
|
|
16
|
+
@import '../component/HorizontalScroller/style';
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@media only screen and (max-width: $max-mobile-width) {
|
|
20
|
+
.hide-on-mobile {
|
|
21
|
+
display: none !important
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* 辅助样式,使用max-view-height作为类名,获得路由页面的最大高度,需要自行引入 */
|
|
2
|
+
|
|
3
|
+
@import './var';
|
|
4
|
+
|
|
5
|
+
//可能达到的最大高度
|
|
6
|
+
$max-height: '100vh - #{$page-view-margin * 2}';
|
|
7
|
+
|
|
8
|
+
.max-view-height {
|
|
9
|
+
max-height: calc(#{$max-height});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//即使高度不够也占满
|
|
13
|
+
.max-view-height.full {
|
|
14
|
+
height: 100vh;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//有顶栏
|
|
18
|
+
.has-header .max-view-height {
|
|
19
|
+
max-height: calc(#{$max-height} - #{$header-height});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//有多页签栏
|
|
23
|
+
.has-tags-view .max-view-height {
|
|
24
|
+
max-height: calc(#{$max-height} - #{$tags-view-height});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//有页头
|
|
28
|
+
.has-page-header .max-view-height {
|
|
29
|
+
max-height: calc(#{$max-height} - #{$page-header-height});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//有页脚
|
|
33
|
+
.has-page-footer .max-view-height {
|
|
34
|
+
max-height: calc(#{$max-height} - #{$page-footer-height});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//同时有顶栏和多页签栏
|
|
38
|
+
.has-header.has-tags-view .max-view-height {
|
|
39
|
+
max-height: calc(#{$max-height} - #{$header-height} - #{$tags-view-height});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//同时有顶栏和页头
|
|
43
|
+
.has-header .has-page-header .max-view-height {
|
|
44
|
+
max-height: calc(#{$max-height} - #{$header-height} - #{$page-header-height});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//同时有顶栏和页脚
|
|
48
|
+
.has-header .has-page-footer .max-view-height {
|
|
49
|
+
max-height: calc(#{$max-height} - #{$header-height} - #{$page-footer-height});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//同时有顶栏、多页签栏、页头
|
|
53
|
+
.has-header.has-tags-view .has-page-header .max-view-height {
|
|
54
|
+
max-height: calc(#{$max-height} - #{$header-height} - #{$tags-view-height} - #{$page-header-height});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
//同时有顶栏、多页签栏、页脚
|
|
58
|
+
.has-header.has-tags-view .has-page-footer .max-view-height {
|
|
59
|
+
max-height: calc(#{$max-height} - #{$header-height} - #{$tags-view-height} - #{$page-footer-height});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//同时有顶栏、多页签栏、页头、页脚
|
|
63
|
+
.has-header.has-tags-view .has-page-header.has-page-footer .max-view-height {
|
|
64
|
+
max-height: calc(#{$max-height} - #{$header-height} - #{$tags-view-height} - #{$page-header-height} - #{$page-footer-height});
|
|
65
|
+
}
|