xto-fronted 0.4.38 → 0.4.40

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.
@@ -11,8 +11,12 @@ import { Button, Icon } from '@xto/base'
11
11
  // Props
12
12
  const props = withDefaults(defineProps<{
13
13
  menuList?: any[]
14
+ showLogo?: boolean
15
+ showUser?: boolean
14
16
  }>(), {
15
- menuList: () => []
17
+ menuList: () => [],
18
+ showLogo: true,
19
+ showUser: true
16
20
  })
17
21
 
18
22
  const route = useRoute()
@@ -133,7 +137,7 @@ const iconExists = (iconName: string): boolean => {
133
137
  <template>
134
138
  <div class="sidebar" :class="{ 'sidebar--collapsed': isCollapsed }">
135
139
  <!-- Logo -->
136
- <div class="sidebar__logo">
140
+ <div v-if="props.showLogo" class="sidebar__logo">
137
141
  <img src="/vite.svg" alt="Logo" class="sidebar__logo-img" />
138
142
  <span v-show="!isCollapsed" class="sidebar__logo-text">{{ appStore.appName }}</span>
139
143
  </div>
@@ -190,7 +194,7 @@ const iconExists = (iconName: string): boolean => {
190
194
  </Menu>
191
195
 
192
196
  <!-- 用户信息 -->
193
- <div v-if="!isCollapsed" class="sidebar__user">
197
+ <div v-if="props.showUser && !isCollapsed" class="sidebar__user">
194
198
  <div class="sidebar__user-info">
195
199
  <span class="sidebar__user-name">{{ userStore.userName }}</span>
196
200
  <span class="sidebar__user-role">{{ userStore.departmentName }}</span>
@@ -1,125 +1,164 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import { useAppStore } from '@/stores/app'
4
- import { useMenuStore } from '@/stores/menu'
5
- import Sidebar from './Sidebar.vue'
6
- import Header from './Header.vue'
7
- import TopMenu from './TopMenu.vue'
8
-
9
- const appStore = useAppStore()
10
- const menuStore = useMenuStore()
11
-
12
- const sidebarWidth = computed(() =>
13
- appStore.isCollapsed ? '64px' : '210px'
14
- )
15
-
16
- // 布局模式
17
- const layoutMode = computed(() => appStore.layout)
18
-
19
- // 是否显示左侧菜单
20
- const showSidebar = computed(() => layoutMode.value === 'sidebar' || layoutMode.value === 'mix')
21
-
22
- // 是否显示顶部菜单
23
- const showTopMenu = computed(() => layoutMode.value === 'top')
24
-
25
- // 是否显示Header(顶部菜单模式下不显示,因为功能已移到TopMenu)
26
- const showHeader = computed(() => layoutMode.value !== 'top')
27
-
28
- // 混合模式下只显示当前顶部菜单的子菜单
29
- const mixSubMenus = computed(() => {
30
- if (layoutMode.value !== 'mix') return menuStore.menuList
31
- // 混合模式需要根据顶部选中的菜单显示子菜单
32
- // 这里暂时返回全部菜单,后续可根据实际需求调整
33
- return menuStore.menuList
34
- })
35
- </script>
36
-
37
- <template>
38
- <div class="layout" :class="`layout--${layoutMode}`">
39
- <!-- 左侧菜单布局 -->
40
- <aside v-if="showSidebar" class="layout__aside" :style="{ width: sidebarWidth }">
41
- <Sidebar :menu-list="layoutMode === 'mix' ? mixSubMenus : menuStore.menuList" />
42
- </aside>
43
-
44
- <!-- 顶部菜单布局(包含右侧操作功能) -->
45
- <div v-if="showTopMenu" class="layout__top-menu">
46
- <TopMenu />
47
- </div>
48
-
49
- <div class="layout__main">
50
- <!-- Header仅在sidebar和mix模式下显示 -->
51
- <header v-if="showHeader" class="layout__header">
52
- <Header />
53
- </header>
54
- <main class="layout__content">
55
- <router-view />
56
- </main>
57
- </div>
58
- </div>
59
- </template>
60
-
61
- <style lang="scss" scoped>
62
- .layout {
63
- display: flex;
64
- width: 100%;
65
- height: 100%;
66
-
67
- // 左侧菜单模式
68
- &--sidebar {
69
- flex-direction: row;
70
- }
71
-
72
- // 顶部菜单模式
73
- &--top {
74
- flex-direction: column;
75
-
76
- .layout__aside {
77
- display: none;
78
- }
79
-
80
- .layout__main {
81
- flex: 1;
82
- }
83
- }
84
-
85
- // 混合菜单模式
86
- &--mix {
87
- flex-direction: row;
88
- }
89
-
90
- &__aside {
91
- transition: width 0.3s;
92
- overflow: hidden;
93
- flex-shrink: 0;
94
- height: 100%;
95
- }
96
-
97
- &__top-menu {
98
- width: 100%;
99
- height: 50px;
100
- background-color: var(--bg-color);
101
- border-bottom: 1px solid var(--color-border-lighter);
102
- }
103
-
104
- &__main {
105
- flex: 1;
106
- display: flex;
107
- flex-direction: column;
108
- overflow: hidden;
109
- height: 100%;
110
- }
111
-
112
- &__header {
113
- height: 50px;
114
- background-color: var(--bg-color);
115
- border-bottom: 1px solid var(--color-border-lighter);
116
- flex-shrink: 0;
117
- }
118
-
119
- &__content {
120
- flex: 1;
121
- overflow: auto;
122
- background-color: var(--bg-color-page);
123
- }
124
- }
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { useAppStore } from '@/stores/app'
4
+ import { useMenuStore } from '@/stores/menu'
5
+ import Sidebar from './Sidebar.vue'
6
+ import Header from './Header.vue'
7
+ import TopMenu from './TopMenu.vue'
8
+ import MixTopMenu from './MixTopMenu.vue'
9
+
10
+ const appStore = useAppStore()
11
+ const menuStore = useMenuStore()
12
+
13
+ const sidebarWidth = computed(() =>
14
+ appStore.isCollapsed ? '64px' : '210px'
15
+ )
16
+
17
+ // 布局模式
18
+ const layoutMode = computed(() => appStore.layout)
19
+
20
+ // 是否显示左侧菜单(sidebar模式和mix模式都显示)
21
+ const showSidebar = computed(() => layoutMode.value === 'sidebar' || layoutMode.value === 'mix')
22
+
23
+ // 是否显示顶部菜单(top模式)
24
+ const showTopMenu = computed(() => layoutMode.value === 'top')
25
+
26
+ // 是否显示混合顶部菜单(mix模式)
27
+ const showMixTopMenu = computed(() => layoutMode.value === 'mix')
28
+
29
+ // 是否显示Header(仅在sidebar模式下显示)
30
+ const showHeader = computed(() => layoutMode.value === 'sidebar')
31
+
32
+ // 左侧菜单数据
33
+ const sidebarMenuList = computed(() => {
34
+ if (layoutMode.value === 'sidebar') {
35
+ return menuStore.menuList
36
+ } else if (layoutMode.value === 'mix') {
37
+ // mix模式下显示当前选中一级菜单的子菜单
38
+ return appStore.mixSubMenus
39
+ }
40
+ return []
41
+ })
42
+
43
+ // 是否显示左侧菜单(mix模式下只有在有子菜单时才显示)
44
+ const showMixSidebar = computed(() => {
45
+ if (layoutMode.value !== 'mix') return showSidebar.value
46
+ return appStore.mixSubMenus.length > 0
47
+ })
48
+ </script>
49
+
50
+ <template>
51
+ <div class="layout" :class="`layout--${layoutMode}`">
52
+ <!-- 左侧菜单布局 -->
53
+ <aside v-if="showMixSidebar" class="layout__aside" :style="{ width: sidebarWidth }">
54
+ <Sidebar :menu-list="sidebarMenuList" :show-logo="layoutMode !== 'mix'" :show-user="layoutMode !== 'mix'" />
55
+ </aside>
56
+
57
+ <!-- 顶部菜单布局(top模式) -->
58
+ <div v-if="showTopMenu" class="layout__top-menu">
59
+ <TopMenu />
60
+ </div>
61
+
62
+ <!-- 混合顶部菜单布局(mix模式) -->
63
+ <div v-if="showMixTopMenu" class="layout__mix-top-menu">
64
+ <MixTopMenu />
65
+ </div>
66
+
67
+ <div class="layout__main">
68
+ <!-- Header仅在sidebar模式下显示 -->
69
+ <header v-if="showHeader" class="layout__header">
70
+ <Header />
71
+ </header>
72
+ <main class="layout__content">
73
+ <router-view />
74
+ </main>
75
+ </div>
76
+ </div>
77
+ </template>
78
+
79
+ <style lang="scss" scoped>
80
+ .layout {
81
+ display: flex;
82
+ width: 100%;
83
+ height: 100%;
84
+
85
+ // 左侧菜单模式
86
+ &--sidebar {
87
+ flex-direction: row;
88
+ }
89
+
90
+ // 顶部菜单模式
91
+ &--top {
92
+ flex-direction: column;
93
+
94
+ .layout__aside {
95
+ display: none;
96
+ }
97
+
98
+ .layout__main {
99
+ flex: 1;
100
+ }
101
+ }
102
+
103
+ // 混合菜单模式
104
+ &--mix {
105
+ flex-direction: column;
106
+
107
+ .layout__main {
108
+ display: flex;
109
+ flex-direction: row;
110
+ flex: 1;
111
+ }
112
+
113
+ .layout__content {
114
+ flex: 1;
115
+ display: flex;
116
+ flex-direction: column;
117
+ }
118
+ }
119
+
120
+ &__aside {
121
+ transition: width 0.3s;
122
+ overflow: hidden;
123
+ flex-shrink: 0;
124
+ height: 100%;
125
+ }
126
+
127
+ &__top-menu {
128
+ width: 100%;
129
+ height: 50px;
130
+ background-color: var(--bg-color);
131
+ border-bottom: 1px solid var(--color-border-lighter);
132
+ flex-shrink: 0;
133
+ }
134
+
135
+ &__mix-top-menu {
136
+ width: 100%;
137
+ height: 50px;
138
+ background-color: var(--bg-color);
139
+ border-bottom: 1px solid var(--color-border-lighter);
140
+ flex-shrink: 0;
141
+ }
142
+
143
+ &__main {
144
+ flex: 1;
145
+ display: flex;
146
+ flex-direction: column;
147
+ overflow: hidden;
148
+ height: 100%;
149
+ }
150
+
151
+ &__header {
152
+ height: 50px;
153
+ background-color: var(--bg-color);
154
+ border-bottom: 1px solid var(--color-border-lighter);
155
+ flex-shrink: 0;
156
+ }
157
+
158
+ &__content {
159
+ flex: 1;
160
+ overflow: auto;
161
+ background-color: var(--bg-color-page);
162
+ }
163
+ }
125
164
  </style>
package/src/stores/app.ts CHANGED
@@ -22,6 +22,7 @@ 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
+ const mixSubMenus = ref<any[]>([])
25
26
 
26
27
  // 计算属性
27
28
  const themeClass = computed(() => (isDark.value ? 'dark' : 'light'))
@@ -121,6 +122,11 @@ export const useAppStore = defineStore('app', () => {
121
122
  cachedViews.value = []
122
123
  }
123
124
 
125
+ // 设置混合模式子菜单
126
+ const setMixSubMenus = (menus: any[]) => {
127
+ mixSubMenus.value = menus
128
+ }
129
+
124
130
  // 初始化主题
125
131
  const initTheme = () => {
126
132
  updateTheme()
@@ -144,6 +150,7 @@ export const useAppStore = defineStore('app', () => {
144
150
  showBreadcrumb,
145
151
  primaryColor,
146
152
  cachedViews,
153
+ mixSubMenus,
147
154
  themeClass,
148
155
  setAppName,
149
156
  setIndexPath,
@@ -158,6 +165,7 @@ export const useAppStore = defineStore('app', () => {
158
165
  addCachedView,
159
166
  removeCachedView,
160
167
  clearCachedViews,
168
+ setMixSubMenus,
161
169
  initTheme
162
170
  }
163
171
  })