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
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="horizontal-scroller" @wheel="handleScroll">
|
|
3
|
+
<slot/>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script>
|
|
8
|
+
/**
|
|
9
|
+
* 水平滚动区域,能够通过鼠标滚轮来进行水平滚动,用于页签栏
|
|
10
|
+
* 借鉴[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
name: 'HorizontalScroller',
|
|
15
|
+
|
|
16
|
+
props: {
|
|
17
|
+
// 元素之间的距离(px)
|
|
18
|
+
between: { type: Number, default: 4 }
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
methods: {
|
|
22
|
+
/**
|
|
23
|
+
* @param e {WheelEvent}
|
|
24
|
+
*/
|
|
25
|
+
handleScroll(e) {
|
|
26
|
+
const eventDelta = e.deltaY
|
|
27
|
+
const { scrollLeft, scrollWidth, clientWidth } = this.$el
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ①滚轮向上滚动并且是最左边
|
|
31
|
+
* ②滚轮向下滚动并且是最右边
|
|
32
|
+
*/
|
|
33
|
+
if (eventDelta < 0 && scrollLeft === 0
|
|
34
|
+
|| eventDelta > 0 && scrollWidth <= scrollLeft + clientWidth) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.$el.scrollLeft += eventDelta
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 将指定元素以及其相邻元素移动至视窗内,外部调用
|
|
43
|
+
* @param target {Element}
|
|
44
|
+
*/
|
|
45
|
+
moveToTarget(target) {
|
|
46
|
+
const nodes = Array.from(this.$el.children)
|
|
47
|
+
const { offsetWidth: containerWidth, scrollWidth, scrollLeft } = this.$el
|
|
48
|
+
|
|
49
|
+
if (containerWidth >= scrollWidth) return
|
|
50
|
+
|
|
51
|
+
let first = null
|
|
52
|
+
let last = null
|
|
53
|
+
|
|
54
|
+
// find first el and last el
|
|
55
|
+
if (nodes.length > 0) {
|
|
56
|
+
first = nodes[0]
|
|
57
|
+
last = nodes[nodes.length - 1]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (first === target) {
|
|
61
|
+
return this.scrollLeft(0)
|
|
62
|
+
}
|
|
63
|
+
if (last === target) {
|
|
64
|
+
return this.scrollLeft(scrollWidth - containerWidth)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// find prev and next
|
|
68
|
+
const currentIndex = nodes.findIndex(item => item === target)
|
|
69
|
+
const prev = nodes[currentIndex - 1]
|
|
70
|
+
const next = nodes[currentIndex + 1]
|
|
71
|
+
|
|
72
|
+
// the el's offsetLeft after of next
|
|
73
|
+
const afterNextOffsetLeft = next.offsetLeft + next.offsetWidth + this.between
|
|
74
|
+
|
|
75
|
+
// the el's offsetLeft before of prev
|
|
76
|
+
const beforePrevOffsetLeft = prev.offsetLeft - this.between
|
|
77
|
+
|
|
78
|
+
if (afterNextOffsetLeft > scrollLeft + containerWidth) {
|
|
79
|
+
this.scrollLeft(afterNextOffsetLeft - containerWidth)
|
|
80
|
+
}
|
|
81
|
+
else if (beforePrevOffsetLeft < scrollLeft) {
|
|
82
|
+
this.scrollLeft(beforePrevOffsetLeft)
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
scrollLeft(val) {
|
|
87
|
+
this.$el.scrollTo({ left: val, behavior: 'smooth' })
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { Const } from '../../config'
|
|
3
|
+
import Aside from '../Aside'
|
|
4
|
+
import Header from '../Header'
|
|
5
|
+
import TagsView from '../TagsView'
|
|
6
|
+
import Page from '../Page'
|
|
7
|
+
import {
|
|
8
|
+
appGetters,
|
|
9
|
+
appMutations,
|
|
10
|
+
asideMutations,
|
|
11
|
+
headerMutations,
|
|
12
|
+
tagsViewGetters,
|
|
13
|
+
tagsViewMutations,
|
|
14
|
+
pageMutations
|
|
15
|
+
} from '../../store'
|
|
16
|
+
import { isMobile } from '../../helper'
|
|
17
|
+
import { debounce } from '../../util'
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
name: 'ElAdminLayout',
|
|
21
|
+
|
|
22
|
+
computed: {
|
|
23
|
+
isLeftRight() {
|
|
24
|
+
return appGetters.struct === 'left-right'
|
|
25
|
+
},
|
|
26
|
+
renderAside() {
|
|
27
|
+
return appGetters.isMobile || ['aside', 'mix'].includes(appGetters.navMode)
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
methods: {
|
|
32
|
+
// render时调用,根据插槽的变化修改store数据
|
|
33
|
+
mutateStoreSlot() {
|
|
34
|
+
const cache = this.$_cachedScopedSlots
|
|
35
|
+
const curr = this.$scopedSlots
|
|
36
|
+
|
|
37
|
+
// 减少的
|
|
38
|
+
Object.keys(cache).forEach(k => {
|
|
39
|
+
// 缓存中有,本次没有
|
|
40
|
+
if (!curr[k]) {
|
|
41
|
+
const f = this.getMutationBySlot(k)
|
|
42
|
+
f(undefined)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// 新增的
|
|
47
|
+
Object.keys(curr).forEach(k => {
|
|
48
|
+
// 本次有,缓存中没有
|
|
49
|
+
if (!cache[k]) {
|
|
50
|
+
const f = this.getMutationBySlot(k)
|
|
51
|
+
// scopedSlots: (props) => VNode
|
|
52
|
+
// render: (h, props) => VNode
|
|
53
|
+
f((h, props) => curr[k](props))
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
this.$_cachedScopedSlots = curr
|
|
58
|
+
},
|
|
59
|
+
getMutations(prefix) {
|
|
60
|
+
switch (prefix) {
|
|
61
|
+
case 'aside':
|
|
62
|
+
return asideMutations
|
|
63
|
+
case 'header':
|
|
64
|
+
return headerMutations
|
|
65
|
+
case 'page':
|
|
66
|
+
return pageMutations
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
getMutationBySlot(slot) {
|
|
70
|
+
const cache = this.$_slotMutationsMap
|
|
71
|
+
|
|
72
|
+
if (cache[slot]) return cache[slot]
|
|
73
|
+
|
|
74
|
+
// 不规则的
|
|
75
|
+
if (slot === 'logo') {
|
|
76
|
+
cache[slot] = appMutations.logoSlot
|
|
77
|
+
return cache[slot]
|
|
78
|
+
}
|
|
79
|
+
if (slot === 'tags-view-item') {
|
|
80
|
+
cache[slot] = tagsViewMutations.itemSlot
|
|
81
|
+
return cache[slot]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// aside -> asideMutations.defaultSlot
|
|
85
|
+
// aside-header -> asideMutations.headerSlot
|
|
86
|
+
// aside-menu-icon -> asideMutations.menuIconSlot
|
|
87
|
+
const arr = slot.split('-')
|
|
88
|
+
const mutations = this.getMutations(arr[0])
|
|
89
|
+
|
|
90
|
+
if (arr.length === 1) {
|
|
91
|
+
cache[slot] = mutations.defaultSlot
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
let method = arr[1]
|
|
95
|
+
|
|
96
|
+
for (let i = 2, len = arr.length; i < len; i++) {
|
|
97
|
+
method += arr[i][0].toUpperCase() + [...arr[i].slice(1)].join('')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
cache[slot] = mutations[method += 'Slot']
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return cache[slot]
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
beforeCreate() {
|
|
108
|
+
// 缓存当前的插槽
|
|
109
|
+
this.$_cachedScopedSlots = Object.create(null)
|
|
110
|
+
|
|
111
|
+
// 用于加速根据插槽名查找store mutation的缓存map
|
|
112
|
+
this.$_slotMutationsMap = Object.create(null)
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
mounted() {
|
|
116
|
+
this.$_resize = debounce(() => {
|
|
117
|
+
!document.hidden && appMutations.isMobile(isMobile())
|
|
118
|
+
})
|
|
119
|
+
window.addEventListener('resize', this.$_resize)
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
beforeDestroy() {
|
|
123
|
+
if (this.$_resize) {
|
|
124
|
+
window.removeEventListener('resize', this.$_resize)
|
|
125
|
+
delete this.$_resize
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
delete this.$_cachedScopedSlots
|
|
129
|
+
delete this.$_slotMutationsMap
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
render() {
|
|
133
|
+
Const.enableLayoutSlot && this.mutateStoreSlot()
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<div class={{
|
|
137
|
+
'el-admin-layout': true,
|
|
138
|
+
'has-header': true,
|
|
139
|
+
'has-tags-view': tagsViewGetters.enabled,
|
|
140
|
+
'left-right': this.isLeftRight
|
|
141
|
+
}}>
|
|
142
|
+
<Header ref="header"/>
|
|
143
|
+
|
|
144
|
+
{tagsViewGetters.enabled && <TagsView ref="tags-view"/>}
|
|
145
|
+
|
|
146
|
+
{this.renderAside && <Aside ref="aside"/>}
|
|
147
|
+
|
|
148
|
+
<Page ref="page"/>
|
|
149
|
+
</div>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
</script>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
.el-admin-layout {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-columns: min-content 1fr;
|
|
4
|
+
grid-template-rows: min-content min-content 1fr;
|
|
5
|
+
height: 100%;
|
|
6
|
+
width: 100%;
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
|
|
9
|
+
> header {
|
|
10
|
+
grid-column-start: 1;
|
|
11
|
+
grid-column-end: 3;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
> nav {
|
|
15
|
+
grid-column: 2 / 3;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
> aside {
|
|
19
|
+
display: flex;
|
|
20
|
+
grid-row-start: 2;
|
|
21
|
+
grid-row-end: 4;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
> main {
|
|
25
|
+
grid-area: 3 / 2 / 4 / 3;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 左右结构
|
|
29
|
+
&.left-right {
|
|
30
|
+
> header {
|
|
31
|
+
grid-column-start: 2;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
> aside {
|
|
35
|
+
grid-row-start: 1;
|
|
36
|
+
|
|
37
|
+
// 左右结构时,侧边栏z-index需大于顶栏
|
|
38
|
+
// 上下结构时,侧边栏z-index需小于顶栏
|
|
39
|
+
z-index: $header-z-index + 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<template v-once>
|
|
2
|
+
<div class="el-loading-spinner">
|
|
3
|
+
<svg class="circular" viewBox="25 25 50 50">
|
|
4
|
+
<circle class="path" cx="50" cy="50" r="20" fill="none"/>
|
|
5
|
+
</svg>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
/**
|
|
11
|
+
* 加载圈,用于侧边栏菜单、顶栏菜单的加载状态
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
name: 'LoadingSpinner'
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { appGetters } from '../../store'
|
|
3
|
+
import { isEmpty } from '../../util'
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
name: 'Logo',
|
|
7
|
+
|
|
8
|
+
props: { showTitle: Boolean },
|
|
9
|
+
|
|
10
|
+
methods: {
|
|
11
|
+
onClick(e) {
|
|
12
|
+
const { onLogoClick } = appGetters
|
|
13
|
+
if (onLogoClick) return onLogoClick(e)
|
|
14
|
+
|
|
15
|
+
const to = appGetters.logoRoute
|
|
16
|
+
const method = typeof to === 'object' && to.replace ? 'replace' : 'push'
|
|
17
|
+
|
|
18
|
+
this.$router[method](to, () => undefined)
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
render(h) {
|
|
23
|
+
const { logo: src, title: txt, logoSlot } = appGetters
|
|
24
|
+
|
|
25
|
+
const img = src && <img src={src}/>
|
|
26
|
+
const title = !isEmpty(txt) && this.showTitle && <h1>{txt}</h1>
|
|
27
|
+
|
|
28
|
+
const children = logoSlot ? logoSlot(h, { img, title, props: this.$props }) : [img, title]
|
|
29
|
+
|
|
30
|
+
if (!children || Array.isArray(children) && children.filter(Boolean).length === 0) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div class="logo-container" on-click={this.onClick}>
|
|
36
|
+
{children}
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.logo-container {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: $header-height;
|
|
6
|
+
cursor: pointer;
|
|
7
|
+
padding: 0 $menu-padding;
|
|
8
|
+
box-sizing: border-box;
|
|
9
|
+
|
|
10
|
+
img {
|
|
11
|
+
height: $logo-size;
|
|
12
|
+
|
|
13
|
+
& + h1 {
|
|
14
|
+
margin-left: 12px;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
h1 {
|
|
19
|
+
margin: 0;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
text-overflow: ellipsis;
|
|
22
|
+
white-space: nowrap;
|
|
23
|
+
font-size: 18px;
|
|
24
|
+
font-weight: $--font-weight-primary;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
/**
|
|
3
|
+
* 基于el-menu封装的无限级菜单
|
|
4
|
+
* 自带亮色、暗色两种主题
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import MenuItem from "../../component/ElMenu/item";
|
|
8
|
+
import SubMenu from "../../component/ElMenu/sub";
|
|
9
|
+
import { isEmpty } from "../../util";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 判断菜单是否没有子级,没有则返回true,否则false
|
|
13
|
+
*
|
|
14
|
+
* @param menu {MenuItem}
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function isSingleMenu(menu) {
|
|
18
|
+
return !Array.isArray(menu.children) || menu.children.length === 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
name: "NavMenu",
|
|
23
|
+
|
|
24
|
+
props: {
|
|
25
|
+
// 路由配置项组成的树形数组
|
|
26
|
+
menus: { type: Array, default: () => [] },
|
|
27
|
+
|
|
28
|
+
// 主题,light 或 dark
|
|
29
|
+
theme: { type: String, default: "light" },
|
|
30
|
+
|
|
31
|
+
// 垂直模式下子菜单的单位缩进距离
|
|
32
|
+
inlineIndent: Number,
|
|
33
|
+
|
|
34
|
+
// 水平模式下是否显示展开折叠图标
|
|
35
|
+
showCollapseIcon: Boolean,
|
|
36
|
+
|
|
37
|
+
// 折叠时的展开菜单是否显示父级
|
|
38
|
+
showParentOnCollapse: Boolean,
|
|
39
|
+
|
|
40
|
+
// menus变化时的过渡动画名称
|
|
41
|
+
switchTransitionName: String,
|
|
42
|
+
|
|
43
|
+
// 自定义渲染菜单图标,(h, {menu, depth}) => VNode
|
|
44
|
+
menuIconSlot: Function,
|
|
45
|
+
|
|
46
|
+
// 自定义渲染菜单内容,(h, {menu, depth}) => VNode
|
|
47
|
+
menuContentSlot: Function,
|
|
48
|
+
|
|
49
|
+
/*--------------el-menu原有props开始-------------*/
|
|
50
|
+
/*https://element.eleme.cn/#/zh-CN/component/menu*/
|
|
51
|
+
|
|
52
|
+
mode: { type: String, default: "vertical" }, // 在el-menu原效果上加了样式名
|
|
53
|
+
collapse: Boolean,
|
|
54
|
+
defaultActive: String,
|
|
55
|
+
defaultOpeneds: Array,
|
|
56
|
+
uniqueOpened: Boolean,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
computed: {
|
|
60
|
+
children() {
|
|
61
|
+
const h = this.$createElement;
|
|
62
|
+
let children = this.renderMenus(h, this.menus);
|
|
63
|
+
|
|
64
|
+
const { switchTransitionName: name } = this;
|
|
65
|
+
if (!isEmpty(name)) {
|
|
66
|
+
children = h("transition-group", { props: { name } }, children);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return children;
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
themeClass() {
|
|
73
|
+
return `el-menu--${this.theme}`;
|
|
74
|
+
},
|
|
75
|
+
menuClass() {
|
|
76
|
+
return [
|
|
77
|
+
`el-menu--${this.mode}`,
|
|
78
|
+
this.themeClass,
|
|
79
|
+
!this.showCollapseIcon && "hide-collapse-icon",
|
|
80
|
+
];
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
methods: {
|
|
85
|
+
// 将el-menu的select事件传递给外部
|
|
86
|
+
onSelect(...args) {
|
|
87
|
+
this.$emit("select", ...args);
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// 渲染菜单图标
|
|
91
|
+
renderMenuIcon(h, menu, depth) {
|
|
92
|
+
const slot = this.menuIconSlot;
|
|
93
|
+
|
|
94
|
+
if (slot) return slot(h, { menu, depth });
|
|
95
|
+
|
|
96
|
+
const icon = menu.meta.icon;
|
|
97
|
+
return icon && <i style="margin-top:-18px" class={`menu-icon ${icon}`} />;
|
|
98
|
+
},
|
|
99
|
+
// 渲染菜单内容
|
|
100
|
+
renderMenuContent(h, menu, depth) {
|
|
101
|
+
const slot = this.menuContentSlot;
|
|
102
|
+
return slot ? slot(h, { menu, depth }) : <span>{menu.meta.title}</span>;
|
|
103
|
+
},
|
|
104
|
+
// 渲染无子级的菜单
|
|
105
|
+
renderSingleMenu(h, menu, depth) {
|
|
106
|
+
const { fullPath } = menu;
|
|
107
|
+
return (
|
|
108
|
+
<MenuItem
|
|
109
|
+
key={fullPath}
|
|
110
|
+
index={fullPath}
|
|
111
|
+
inline-indent={this.inlineIndent}
|
|
112
|
+
>
|
|
113
|
+
<template slot="title">
|
|
114
|
+
{this.renderMenuContent(h, menu, depth)}
|
|
115
|
+
</template>
|
|
116
|
+
</MenuItem>
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
// 渲染有子级的菜单
|
|
120
|
+
renderSubMenu(h, menu, children, depth) {
|
|
121
|
+
const { fullPath } = menu;
|
|
122
|
+
const noContent =
|
|
123
|
+
depth === 1 && this.collapse && this.mode === "vertical";
|
|
124
|
+
return (
|
|
125
|
+
<div key="index">
|
|
126
|
+
<SubMenu
|
|
127
|
+
key={fullPath}
|
|
128
|
+
index={fullPath}
|
|
129
|
+
inline-indent={this.inlineIndent}
|
|
130
|
+
popper-class={this.themeClass}
|
|
131
|
+
popper-append-to-body
|
|
132
|
+
>
|
|
133
|
+
<template slot="title">
|
|
134
|
+
{depth == 1 && (
|
|
135
|
+
<div>
|
|
136
|
+
{this.renderMenuIcon(h, menu, depth)}
|
|
137
|
+
<div style="margin-top:-24px;font-size:14px;margin-left:-2px">
|
|
138
|
+
{menu.meta.title}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
{!noContent && this.renderMenuContent(h, menu, depth)}
|
|
143
|
+
</template>
|
|
144
|
+
{children}
|
|
145
|
+
</SubMenu>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
},
|
|
149
|
+
// 渲染有子级且需要显示父级的菜单
|
|
150
|
+
renderChildrenWithParentMenu(h, menu, children, depth) {
|
|
151
|
+
let data = [];
|
|
152
|
+
menu.children.forEach((x) => {
|
|
153
|
+
let childrens = [];
|
|
154
|
+
x.children.forEach((y) => {
|
|
155
|
+
childrens.push(this.renderSingleMenu(h, y, depth + 1));
|
|
156
|
+
});
|
|
157
|
+
data.push(
|
|
158
|
+
<div>
|
|
159
|
+
<div class="popover-menu__title">
|
|
160
|
+
{this.renderMenuContent(h, x, depth)}
|
|
161
|
+
</div>
|
|
162
|
+
<div class="el-menu el-menu--inline">{childrens}</div>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
return data;
|
|
167
|
+
},
|
|
168
|
+
// 渲染菜单项
|
|
169
|
+
renderMenus(h, menus, depth = 1) {
|
|
170
|
+
return menus.map((menu) => {
|
|
171
|
+
let children = [];
|
|
172
|
+
//弹出菜单显示父级信息;
|
|
173
|
+
if (this.collapse && this.showParentOnCollapse) {
|
|
174
|
+
// 这里认为父级的深度应该+1
|
|
175
|
+
children = this.renderChildrenWithParentMenu(
|
|
176
|
+
h,
|
|
177
|
+
menu,
|
|
178
|
+
children,
|
|
179
|
+
depth + 1
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
// 一级菜单
|
|
183
|
+
return this.renderSubMenu(h, menu, children, depth);
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
render() {
|
|
189
|
+
return (
|
|
190
|
+
<el-menu
|
|
191
|
+
ref="el-menu"
|
|
192
|
+
class={this.menuClass}
|
|
193
|
+
mode={this.mode}
|
|
194
|
+
collapse={this.collapse}
|
|
195
|
+
collapse-transition={false}
|
|
196
|
+
unique-opened={this.uniqueOpened}
|
|
197
|
+
default-active={this.defaultActive}
|
|
198
|
+
default-openeds={this.defaultOpeneds}
|
|
199
|
+
on-select={this.onSelect}
|
|
200
|
+
>
|
|
201
|
+
{this.children}
|
|
202
|
+
</el-menu>
|
|
203
|
+
);
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
</script>
|