xto-fronted 0.4.21 → 0.4.23
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/dist/components/Layout/MixTopMenu.vue.d.ts +4 -0
- package/dist/index-BAJakzRz.js +142 -0
- package/dist/index-BRMkw154.js +475 -0
- package/dist/index-BadGWG4q.js +475 -0
- package/dist/index-C0krZRWf.js +2859 -0
- package/dist/index-CoJBI1vE.js +345 -0
- package/dist/index-D6CC02F-.js +372 -0
- package/dist/index-Di2VPQjn.js +345 -0
- package/dist/index-Dpv1M9Hu.js +372 -0
- package/dist/index-Q56EoFor.js +142 -0
- package/dist/index-QxKMr6p0.js +2829 -0
- package/dist/index.es.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/stores/app.d.ts +8 -2
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/assets/styles/_dark.scss +67 -0
- package/src/components/Layout/MixTopMenu.vue +896 -0
- package/src/components/Layout/TopMenu.vue +851 -851
- package/src/components/Layout/index.vue +173 -31
- package/src/stores/app.ts +15 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from 'vue'
|
|
2
|
+
import { computed, watch, onMounted } from 'vue'
|
|
3
|
+
import { useRoute } from 'vue-router'
|
|
3
4
|
import { useAppStore } from '@/stores/app'
|
|
4
5
|
import { useMenuStore } from '@/stores/menu'
|
|
5
6
|
import Sidebar from './Sidebar.vue'
|
|
6
7
|
import Header from './Header.vue'
|
|
7
8
|
import TopMenu from './TopMenu.vue'
|
|
9
|
+
import MixTopMenu from './MixTopMenu.vue'
|
|
8
10
|
|
|
11
|
+
const route = useRoute()
|
|
9
12
|
const appStore = useAppStore()
|
|
10
13
|
const menuStore = useMenuStore()
|
|
11
14
|
|
|
@@ -16,45 +19,171 @@ const sidebarWidth = computed(() =>
|
|
|
16
19
|
// 布局模式
|
|
17
20
|
const layoutMode = computed(() => appStore.layout)
|
|
18
21
|
|
|
19
|
-
//
|
|
20
|
-
const showSidebar = computed(() =>
|
|
22
|
+
// 是否显示左侧菜单(sidebar模式始终显示,mix模式根据一级菜单是否有子菜单决定)
|
|
23
|
+
const showSidebar = computed(() => {
|
|
24
|
+
if (layoutMode.value === 'sidebar') return true
|
|
25
|
+
if (layoutMode.value === 'mix') {
|
|
26
|
+
return mixSubMenus.value.length > 0
|
|
27
|
+
}
|
|
28
|
+
return false
|
|
29
|
+
})
|
|
21
30
|
|
|
22
|
-
//
|
|
31
|
+
// 是否显示顶部菜单(仅 top 模式)
|
|
23
32
|
const showTopMenu = computed(() => layoutMode.value === 'top')
|
|
24
33
|
|
|
25
|
-
// 是否显示Header
|
|
26
|
-
const showHeader = computed(() => layoutMode.value
|
|
34
|
+
// 是否显示Header(顶部菜单模式和混合菜单模式下不显示)
|
|
35
|
+
const showHeader = computed(() => layoutMode.value === 'sidebar')
|
|
27
36
|
|
|
28
|
-
//
|
|
37
|
+
// 混合模式下当前选中一级菜单的子菜单
|
|
29
38
|
const mixSubMenus = computed(() => {
|
|
30
39
|
if (layoutMode.value !== 'mix') return menuStore.menuList
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
|
|
40
|
+
|
|
41
|
+
// 找到当前选中的一级菜单
|
|
42
|
+
const topMenuPath = appStore.activeTopMenuPath
|
|
43
|
+
console.log('mixSubMenus - topMenuPath:', topMenuPath, 'menuList:', menuStore.menuList)
|
|
44
|
+
|
|
45
|
+
if (!topMenuPath) {
|
|
46
|
+
// 如果没有选中一级菜单,默认返回第一个菜单的children(如果有)
|
|
47
|
+
if (menuStore.menuList.length > 0) {
|
|
48
|
+
const firstMenu = menuStore.menuList[0]
|
|
49
|
+
return firstMenu.children || []
|
|
50
|
+
}
|
|
51
|
+
return []
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const topMenu = menuStore.menuList.find(m => m.menuUrl === topMenuPath)
|
|
55
|
+
console.log('mixSubMenus - topMenu:', topMenu)
|
|
56
|
+
return topMenu?.children || []
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// 初始化一级菜单选中状态
|
|
60
|
+
const initActiveTopMenu = () => {
|
|
61
|
+
if (layoutMode.value === 'mix' && menuStore.menuList.length > 0) {
|
|
62
|
+
// 如果还没有设置activeTopMenuPath,设置默认值
|
|
63
|
+
if (!appStore.activeTopMenuPath) {
|
|
64
|
+
// 找到当前路由所属的一级菜单
|
|
65
|
+
let foundTopMenu: string | null = null
|
|
66
|
+
|
|
67
|
+
for (const menu of menuStore.menuList) {
|
|
68
|
+
if (menu.menuUrl === route.path) {
|
|
69
|
+
foundTopMenu = menu.menuUrl
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
if (menu.children && menu.children.length > 0) {
|
|
73
|
+
const found = menu.children.some(child =>
|
|
74
|
+
child.menuUrl === route.path ||
|
|
75
|
+
(child.children && child.children.some(c => c.menuUrl === route.path))
|
|
76
|
+
)
|
|
77
|
+
if (found) {
|
|
78
|
+
foundTopMenu = menu.menuUrl
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 如果没找到,默认选中第一个有子菜单的一级菜单,或者第一个菜单
|
|
85
|
+
if (!foundTopMenu) {
|
|
86
|
+
const menuWithChildren = menuStore.menuList.find(m => m.children && m.children.length > 0)
|
|
87
|
+
foundTopMenu = menuWithChildren ? menuWithChildren.menuUrl : menuStore.menuList[0].menuUrl
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
appStore.setActiveTopMenuPath(foundTopMenu)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 根据当前路由自动设置一级菜单选中状态
|
|
96
|
+
watch(
|
|
97
|
+
() => route.path,
|
|
98
|
+
(path) => {
|
|
99
|
+
if (layoutMode.value === 'mix' && menuStore.menuList.length > 0) {
|
|
100
|
+
// 找到当前路由所属的一级菜单
|
|
101
|
+
for (const menu of menuStore.menuList) {
|
|
102
|
+
if (menu.menuUrl === path) {
|
|
103
|
+
if (appStore.activeTopMenuPath !== menu.menuUrl) {
|
|
104
|
+
appStore.setActiveTopMenuPath(menu.menuUrl)
|
|
105
|
+
}
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
if (menu.children && menu.children.length > 0) {
|
|
109
|
+
const found = menu.children.some(child =>
|
|
110
|
+
child.menuUrl === path ||
|
|
111
|
+
(child.children && child.children.some(c => c.menuUrl === route.path))
|
|
112
|
+
)
|
|
113
|
+
if (found) {
|
|
114
|
+
if (appStore.activeTopMenuPath !== menu.menuUrl) {
|
|
115
|
+
appStore.setActiveTopMenuPath(menu.menuUrl)
|
|
116
|
+
}
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{ immediate: true }
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
// 监听菜单列表变化,重新初始化
|
|
127
|
+
watch(
|
|
128
|
+
() => menuStore.menuList,
|
|
129
|
+
() => {
|
|
130
|
+
if (layoutMode.value === 'mix') {
|
|
131
|
+
initActiveTopMenu()
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{ immediate: true }
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
onMounted(() => {
|
|
138
|
+
initActiveTopMenu()
|
|
34
139
|
})
|
|
35
140
|
</script>
|
|
36
141
|
|
|
37
142
|
<template>
|
|
38
143
|
<div class="layout" :class="`layout--${layoutMode}`">
|
|
39
|
-
<!--
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
144
|
+
<!-- 混合菜单模式 -->
|
|
145
|
+
<template v-if="layoutMode === 'mix'">
|
|
146
|
+
<!-- 混合模式顶部菜单(一级菜单) -->
|
|
147
|
+
<MixTopMenu />
|
|
148
|
+
|
|
149
|
+
<!-- 混合模式主体区域 -->
|
|
150
|
+
<div class="layout__mix-body">
|
|
151
|
+
<!-- 左侧子菜单(二级菜单) -->
|
|
152
|
+
<aside v-if="mixSubMenus.length > 0" class="layout__aside" :style="{ width: sidebarWidth }">
|
|
153
|
+
<Sidebar :menu-list="mixSubMenus" />
|
|
154
|
+
</aside>
|
|
155
|
+
|
|
156
|
+
<!-- 主内容区 -->
|
|
157
|
+
<div class="layout__main">
|
|
158
|
+
<main class="layout__content">
|
|
159
|
+
<router-view />
|
|
160
|
+
</main>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</template>
|
|
164
|
+
|
|
165
|
+
<!-- 其他布局模式 -->
|
|
166
|
+
<template v-else>
|
|
167
|
+
<!-- 左侧菜单布局 -->
|
|
168
|
+
<aside v-if="showSidebar" class="layout__aside" :style="{ width: sidebarWidth }">
|
|
169
|
+
<Sidebar :menu-list="menuStore.menuList" />
|
|
170
|
+
</aside>
|
|
171
|
+
|
|
172
|
+
<!-- 顶部菜单布局 -->
|
|
173
|
+
<div v-if="showTopMenu" class="layout__top-menu">
|
|
174
|
+
<TopMenu />
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div class="layout__main">
|
|
178
|
+
<!-- Header仅在sidebar模式下显示 -->
|
|
179
|
+
<header v-if="showHeader" class="layout__header">
|
|
180
|
+
<Header />
|
|
181
|
+
</header>
|
|
182
|
+
<main class="layout__content">
|
|
183
|
+
<router-view />
|
|
184
|
+
</main>
|
|
185
|
+
</div>
|
|
186
|
+
</template>
|
|
58
187
|
</div>
|
|
59
188
|
</template>
|
|
60
189
|
|
|
@@ -84,7 +213,21 @@ const mixSubMenus = computed(() => {
|
|
|
84
213
|
|
|
85
214
|
// 混合菜单模式
|
|
86
215
|
&--mix {
|
|
87
|
-
flex-direction:
|
|
216
|
+
flex-direction: column;
|
|
217
|
+
|
|
218
|
+
.layout__mix-body {
|
|
219
|
+
display: flex;
|
|
220
|
+
flex: 1;
|
|
221
|
+
overflow: hidden;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.layout__aside {
|
|
225
|
+
height: 100%;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.layout__main {
|
|
229
|
+
flex: 1;
|
|
230
|
+
}
|
|
88
231
|
}
|
|
89
232
|
|
|
90
233
|
&__aside {
|
|
@@ -102,7 +245,6 @@ const mixSubMenus = computed(() => {
|
|
|
102
245
|
}
|
|
103
246
|
|
|
104
247
|
&__main {
|
|
105
|
-
flex: 1;
|
|
106
248
|
display: flex;
|
|
107
249
|
flex-direction: column;
|
|
108
250
|
overflow: hidden;
|
package/src/stores/app.ts
CHANGED
|
@@ -22,6 +22,8 @@ export const useAppStore = defineStore('app', () => {
|
|
|
22
22
|
const showBreadcrumb = ref<boolean>(local.get<boolean>('showBreadcrumb') ?? true)
|
|
23
23
|
const primaryColor = ref<string>(local.get<string>('primaryColor') || '#409eff')
|
|
24
24
|
const cachedViews = ref<string[]>([])
|
|
25
|
+
// 混合模式下选中的一级菜单路径
|
|
26
|
+
const activeTopMenuPath = ref<string>(local.get<string>('activeTopMenuPath') || '')
|
|
25
27
|
|
|
26
28
|
// 计算属性
|
|
27
29
|
const themeClass = computed(() => (isDark.value ? 'dark' : 'light'))
|
|
@@ -74,6 +76,17 @@ export const useAppStore = defineStore('app', () => {
|
|
|
74
76
|
const setLayout = (mode: LayoutMode) => {
|
|
75
77
|
layout.value = mode
|
|
76
78
|
local.set('layout', mode)
|
|
79
|
+
// 切换布局时清除一级菜单选中状态
|
|
80
|
+
if (mode !== 'mix') {
|
|
81
|
+
activeTopMenuPath.value = ''
|
|
82
|
+
local.remove('activeTopMenuPath')
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 设置混合模式选中的一级菜单
|
|
87
|
+
const setActiveTopMenuPath = (path: string) => {
|
|
88
|
+
activeTopMenuPath.value = path
|
|
89
|
+
local.set('activeTopMenuPath', path)
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
// 切换标签页
|
|
@@ -144,6 +157,7 @@ export const useAppStore = defineStore('app', () => {
|
|
|
144
157
|
showBreadcrumb,
|
|
145
158
|
primaryColor,
|
|
146
159
|
cachedViews,
|
|
160
|
+
activeTopMenuPath,
|
|
147
161
|
themeClass,
|
|
148
162
|
setAppName,
|
|
149
163
|
setIndexPath,
|
|
@@ -151,6 +165,7 @@ export const useAppStore = defineStore('app', () => {
|
|
|
151
165
|
toggleCollapse,
|
|
152
166
|
setTheme,
|
|
153
167
|
setLayout,
|
|
168
|
+
setActiveTopMenuPath,
|
|
154
169
|
toggleTabs,
|
|
155
170
|
toggleFooter,
|
|
156
171
|
toggleBreadcrumb,
|