xto-fronted 0.4.6 → 0.4.8

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.
Files changed (160) hide show
  1. package/.env.development +7 -7
  2. package/.env.production +7 -7
  3. package/dist/assets/403-AFBQifUI.js +1 -0
  4. package/dist/assets/403-BHEXXbt2.css +1 -0
  5. package/dist/assets/404-Ct_A1n7S.css +1 -0
  6. package/dist/assets/404-WFvpcD2_.js +1 -0
  7. package/dist/assets/_plugin-vue_export-helper-DlAUqK2U.js +1 -0
  8. package/dist/assets/index-1juADvYN.js +2 -0
  9. package/dist/assets/index-B-sX4Ru0.js +1 -0
  10. package/dist/assets/index-BMcziU5a.css +1 -0
  11. package/dist/assets/index-BRR97dc6.js +1 -0
  12. package/dist/assets/index-BZA0ksjx.css +1 -0
  13. package/dist/assets/index-BpV_8nl0.js +1 -0
  14. package/dist/assets/index-BvzhR4zp.js +1 -0
  15. package/dist/assets/index-CUh_s55Z.css +1 -0
  16. package/dist/assets/index-CVjdnIgR.css +1 -0
  17. package/dist/assets/index-CYq57-zj.js +1 -0
  18. package/dist/assets/index-CkL3sVAQ.js +2 -0
  19. package/dist/assets/index-CtrKVYJb.css +1 -0
  20. package/dist/assets/index-Cz2P_bsS.js +1 -0
  21. package/dist/assets/index-D9wlAuR_.js +1 -0
  22. package/dist/assets/index-DawJb02s.css +1 -0
  23. package/dist/assets/index-DfFR6NLf.js +1 -0
  24. package/dist/assets/index-DwVgMO8e.js +1 -0
  25. package/dist/assets/index-GDP-IkXE.css +1 -0
  26. package/dist/assets/index-Iaz1ZzPC.js +2 -0
  27. package/dist/assets/index-PfV8pzQz.css +1 -0
  28. package/dist/assets/index-Swfu6yvD.css +1 -0
  29. package/dist/assets/index-Te8_PRgJ.js +1 -0
  30. package/dist/assets/index-WyZ91RLx.css +1 -0
  31. package/dist/assets/index-tFYRoFdE.js +1 -0
  32. package/dist/assets/vendor-42ANG6Sg.js +6 -0
  33. package/dist/assets/vite-Dw-pgLOX.js +1 -0
  34. package/dist/assets/vue-vendor-Br-l7wbK.js +29 -0
  35. package/dist/assets/xto-base-C-IBqjVs.js +1 -0
  36. package/dist/assets/xto-base-C6eqMPdO.css +1 -0
  37. package/dist/assets/xto-business--V1F5Gwb.css +1 -0
  38. package/dist/assets/xto-core-DZK7Cyg0.js +1 -0
  39. package/dist/assets/xto-data-BFpiDgJi.js +1 -0
  40. package/dist/assets/xto-data-CnAQAQH2.css +1 -0
  41. package/dist/assets/xto-feedback-B7ipsTfz.js +1 -0
  42. package/dist/assets/xto-feedback-DBwJzoTj.css +1 -0
  43. package/dist/assets/xto-form-CrsyAjyr.css +1 -0
  44. package/dist/assets/xto-form-NRjKKNcY.js +1 -0
  45. package/dist/assets/xto-layout-BqU8RuWL.css +1 -0
  46. package/dist/assets/xto-navigation-BiSaXPfr.js +1 -0
  47. package/dist/assets/xto-navigation-C1cnSL2E.css +1 -0
  48. package/dist/assets/xto-navigation-CBPg4dCc.css +1 -0
  49. package/dist/assets/xto-navigation-CKabFu9d.js +1 -0
  50. package/dist/index-54irhCHL.js +1830 -0
  51. package/dist/{index-15Bu0M8D.js → index-BzRf1eoJ.js} +1 -1
  52. package/dist/{index-BO2Zf9u6.js → index-DH4aoCZb.js} +1 -1
  53. package/dist/{index-BBqvHkzE.js → index-Kqa7iZ9E.js} +1 -1
  54. package/dist/{index-BQqo0ZIb.js → index-pxkZlvBw.js} +1 -1
  55. package/dist/index.es.js +1 -1
  56. package/dist/index.html +28 -0
  57. package/dist/index.umd.js +1 -1
  58. package/dist/style.css +1 -1
  59. package/package.json +85 -86
  60. package/src/api/auth.ts +25 -25
  61. package/src/api/system.ts +66 -66
  62. package/src/assets/styles/_dark.scss +406 -406
  63. package/src/components/Layout/Header.vue +973 -973
  64. package/src/components/Layout/Sidebar.vue +273 -212
  65. package/src/components/Layout/index.vue +443 -63
  66. package/src/composables/useApp.ts +61 -61
  67. package/src/composables/useAuth.ts +16 -16
  68. package/src/directives/permission.ts +27 -27
  69. package/src/env.d.ts +18 -18
  70. package/src/index.ts +47 -47
  71. package/src/router/dynamicRoutes.ts +162 -162
  72. package/src/router/guards.ts +128 -128
  73. package/src/router/index.ts +79 -79
  74. package/src/stores/auth.ts +65 -65
  75. package/src/stores/menu.ts +48 -48
  76. package/src/stores/user.ts +50 -50
  77. package/src/types/api.d.ts +80 -80
  78. package/src/utils/auth.ts +99 -99
  79. package/src/utils/config.ts +80 -80
  80. package/src/utils/permission.ts +32 -32
  81. package/src/utils/request.ts +124 -124
  82. package/src/views/login/index.vue +194 -188
  83. package/vite.config.ts +135 -135
  84. package/dist/index-B3PLzNB0.js +0 -345
  85. package/dist/index-B6DTsC6l.js +0 -1715
  86. package/dist/index-B7etKk33.js +0 -372
  87. package/dist/index-B7mpL6Zf.js +0 -475
  88. package/dist/index-BC2PGkkJ.js +0 -1644
  89. package/dist/index-BGgbfcmf.js +0 -475
  90. package/dist/index-BGmUwemj.js +0 -372
  91. package/dist/index-BQFfQj5Q.js +0 -142
  92. package/dist/index-BkRneTya.js +0 -142
  93. package/dist/index-BlRrngsc.js +0 -475
  94. package/dist/index-BmVvM7sm.js +0 -345
  95. package/dist/index-Bn4ThpX9.js +0 -142
  96. package/dist/index-BwfjwDKr.js +0 -1477
  97. package/dist/index-BxIL2hrt.js +0 -475
  98. package/dist/index-C-3fhbN2.js +0 -1644
  99. package/dist/index-C0VN9nFF.js +0 -142
  100. package/dist/index-C0xyGOsz.js +0 -475
  101. package/dist/index-C3c8NAZq.js +0 -1477
  102. package/dist/index-C42VtP71.js +0 -142
  103. package/dist/index-C6Nm0r9k.js +0 -475
  104. package/dist/index-C6w0-8xN.js +0 -1648
  105. package/dist/index-CD364XjV.js +0 -142
  106. package/dist/index-CHww99-i.js +0 -345
  107. package/dist/index-CIgWYERJ.js +0 -1644
  108. package/dist/index-CTs6DTuQ.js +0 -345
  109. package/dist/index-CWRs4WMN.js +0 -372
  110. package/dist/index-Cb-SxHJp.js +0 -345
  111. package/dist/index-CeCysOnl.js +0 -345
  112. package/dist/index-Cg1UpC8D.js +0 -1644
  113. package/dist/index-Cgkqpyx2.js +0 -345
  114. package/dist/index-CiuDEfo-.js +0 -142
  115. package/dist/index-CmQfZC8r.js +0 -372
  116. package/dist/index-CmkjhpX_.js +0 -475
  117. package/dist/index-CpxpXTQX.js +0 -1462
  118. package/dist/index-CqXFk_ET.js +0 -345
  119. package/dist/index-Cqix1YLE.js +0 -1697
  120. package/dist/index-CtvB5J9E.js +0 -372
  121. package/dist/index-Cu3Z2-PY.js +0 -345
  122. package/dist/index-CvDxK7Ab.js +0 -372
  123. package/dist/index-D-FER0vJ.js +0 -372
  124. package/dist/index-D2fQ8TK8.js +0 -475
  125. package/dist/index-D3xVcFvg.js +0 -372
  126. package/dist/index-D4crnrO6.js +0 -142
  127. package/dist/index-D7EzwTM5.js +0 -372
  128. package/dist/index-D7TZamyY.js +0 -1664
  129. package/dist/index-D88fiqXR.js +0 -475
  130. package/dist/index-DEbpF-M4.js +0 -1457
  131. package/dist/index-DFXuyPge.js +0 -1627
  132. package/dist/index-DLgimJYb.js +0 -1667
  133. package/dist/index-DPEVEyik.js +0 -475
  134. package/dist/index-DWy_UGhI.js +0 -345
  135. package/dist/index-DYVtddfw.js +0 -142
  136. package/dist/index-DYnXaqYf.js +0 -142
  137. package/dist/index-DcvRPHuy.js +0 -372
  138. package/dist/index-DdC1uV2v.js +0 -1700
  139. package/dist/index-Dga14ZN7.js +0 -1774
  140. package/dist/index-Dk2V44uP.js +0 -372
  141. package/dist/index-DnJ481u1.js +0 -475
  142. package/dist/index-Do1CBqg8.js +0 -345
  143. package/dist/index-DqQRSPeF.js +0 -345
  144. package/dist/index-Jb4VMHIS.js +0 -142
  145. package/dist/index-MC3wWjNt.js +0 -475
  146. package/dist/index-MG0JePmx.js +0 -142
  147. package/dist/index-PRFGBLWt.js +0 -475
  148. package/dist/index-QgkT42dc.js +0 -372
  149. package/dist/index-TrLCW5xL.js +0 -372
  150. package/dist/index-YDlNLFVk.js +0 -142
  151. package/dist/index-ZAJgA3XD.js +0 -475
  152. package/dist/index-a_ilWAvi.js +0 -345
  153. package/dist/index-bi1TMGid.js +0 -372
  154. package/dist/index-fyarVCog.js +0 -475
  155. package/dist/index-mnTZtPFa.js +0 -345
  156. package/dist/index-orZCyV6I.js +0 -345
  157. package/dist/index-p3TbK44c.js +0 -142
  158. package/dist/index-sRwZYbZ4.js +0 -372
  159. package/dist/index-wATqKEcF.js +0 -142
  160. package/dist/setup.d.ts +0 -17
@@ -1,64 +1,444 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import { useAppStore } from '@/stores/app'
4
- import Sidebar from './Sidebar.vue'
5
- import Header from './Header.vue'
6
-
7
- const appStore = useAppStore()
8
-
9
- const sidebarWidth = computed(() =>
10
- appStore.isCollapsed ? '64px' : '210px'
11
- )
12
- </script>
13
-
14
- <template>
15
- <div class="layout">
16
- <aside class="layout__aside" :style="{ width: sidebarWidth }">
17
- <Sidebar />
18
- </aside>
19
- <div class="layout__main">
20
- <header class="layout__header">
21
- <Header />
22
- </header>
23
- <main class="layout__content">
24
- <router-view />
25
- </main>
26
- </div>
27
- </div>
28
- </template>
29
-
30
- <style lang="scss" scoped>
31
- .layout {
32
- display: flex;
33
- width: 100%;
34
- height: 100%;
35
-
36
- &__aside {
37
- transition: width 0.3s;
38
- overflow: hidden;
39
- flex-shrink: 0;
40
- height: 100%;
41
- }
42
-
43
- &__main {
44
- flex: 1;
45
- display: flex;
46
- flex-direction: column;
47
- overflow: hidden;
48
- height: 100%;
49
- }
50
-
51
- &__header {
52
- height: 50px;
53
- background-color: var(--bg-color);
54
- border-bottom: 1px solid var(--color-border-lighter);
55
- flex-shrink: 0;
56
- }
57
-
58
- &__content {
59
- flex: 1;
60
- overflow: auto;
61
- background-color: var(--bg-color-page);
62
- }
63
- }
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { useRoute, useRouter } from 'vue-router'
4
+ import { useAppStore } from '@/stores/app'
5
+ import { useMenuStore } from '@/stores/menu'
6
+ import Sidebar from './Sidebar.vue'
7
+ import Header from './Header.vue'
8
+ import { Menu, MenuItem, SubMenu } from '@xto/navigation'
9
+ import { Icon } from '@xto/base'
10
+
11
+ const route = useRoute()
12
+ const router = useRouter()
13
+ const appStore = useAppStore()
14
+ const menuStore = useMenuStore()
15
+
16
+ // 菜单选择处理
17
+ const handleMenuSelect = (index: string) => {
18
+ if (index && index !== route.path) {
19
+ router.push(index)
20
+ }
21
+ }
22
+
23
+ // 混合布局顶部菜单选择处理
24
+ const handleTopMenuSelect = (index: string) => {
25
+ const selectedMenu = menuStore.menuList.find(menu => menu.menuCode === index)
26
+ if (selectedMenu) {
27
+ if (selectedMenu.children && selectedMenu.children.length > 0) {
28
+ const firstChild = selectedMenu.children[0]
29
+ if (firstChild.menuUrl && firstChild.menuUrl !== route.path) {
30
+ router.push(firstChild.menuUrl)
31
+ }
32
+ } else if (selectedMenu.menuUrl && selectedMenu.menuUrl !== route.path) {
33
+ router.push(selectedMenu.menuUrl)
34
+ }
35
+ }
36
+ }
37
+
38
+ const sidebarWidth = computed(() =>
39
+ appStore.isCollapsed ? '64px' : '210px'
40
+ )
41
+
42
+ const currentLayout = computed(() => appStore.layout)
43
+ const currentPath = computed(() => route.path)
44
+
45
+ // 获取一级菜单
46
+ const topLevelMenus = computed(() => {
47
+ return menuStore.menuList.map(menu => ({
48
+ menuCode: menu.menuCode,
49
+ menuName: menu.menuName,
50
+ menuUrl: menu.menuUrl,
51
+ icon: menu.icon,
52
+ hasChildren: menu.children && menu.children.length > 0
53
+ }))
54
+ })
55
+
56
+ // 活动的顶部菜单
57
+ const activeTopMenu = computed(() => {
58
+ for (const menu of menuStore.menuList) {
59
+ if (menu.menuUrl === currentPath.value) return menu.menuCode
60
+ if (menu.children) {
61
+ for (const child of menu.children) {
62
+ if (child.menuUrl === currentPath.value) return menu.menuCode
63
+ }
64
+ }
65
+ }
66
+ return topLevelMenus.value[0]?.menuCode || ''
67
+ })
68
+
69
+ // 混合布局子菜单
70
+ const mixSubMenus = computed(() => {
71
+ if (currentLayout.value !== 'mix') return []
72
+ const selectedTopMenu = menuStore.menuList.find(
73
+ menu => menu.menuCode === activeTopMenu.value
74
+ )
75
+ return selectedTopMenu?.children || []
76
+ })
77
+
78
+ // 菜单主题
79
+ const menuBgColor = computed(() => appStore.isDark ? '#1d1e1f' : '#fff')
80
+ const menuTextColor = computed(() => appStore.isDark ? '#cfd3dc' : '#303133')
81
+ const menuActiveTextColor = computed(() => '#409eff')
82
+
83
+ // 图标列表
84
+ const knownIcons = new Set([
85
+ 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right',
86
+ 'caret-down', 'caret-right', 'plus', 'minus', 'close', 'check',
87
+ 'edit', 'delete', 'copy', 'download', 'upload', 'refresh', 'search',
88
+ 'filter', 'more', 'setting', 'share', 'loading', 'info', 'success',
89
+ 'warning', 'error', 'question', 'user', 'user-add', 'user-group',
90
+ 'logout', 'login', 'file', 'folder', 'folder-open', 'document',
91
+ 'image', 'video', 'music', 'camera', 'mail', 'phone', 'chat',
92
+ 'bell', 'message', 'eye', 'eye-off', 'calendar', 'clock', 'history',
93
+ 'timer', 'location', 'map', 'globe', 'star', 'heart', 'thumb-up',
94
+ 'link', 'external-link', 'lock', 'unlock', 'key', 'home', 'menu',
95
+ 'menu-fold', 'menu-unfold', 'sidebar-fold', 'sidebar-expand',
96
+ 'sidebar-left', 'dashboard', 'chart', 'chart-pie', 'chart-line',
97
+ 'report', 'analytics', 'system', 'permission', 'role', 'user-manage',
98
+ 'log', 'notification', 'app', 'list', 'grid', 'fullscreen',
99
+ 'fullscreen-exit', 'zoom-in', 'zoom-out', 'print', 'bookmark',
100
+ 'tag', 'code', 'terminal', 'database', 'server', 'cloud', 'gift',
101
+ 'moon', 'sun', 'theme', 'skin'
102
+ ])
103
+
104
+ const getMenuIcon = (icon?: string): string => {
105
+ if (!icon || icon === '') return ''
106
+ if (icon.startsWith('tineco-icon-')) {
107
+ return icon.replace('tineco-icon-', '')
108
+ }
109
+ return icon
110
+ }
111
+
112
+ const iconExists = (iconName: string): boolean => {
113
+ return knownIcons.has(iconName)
114
+ }
115
+
116
+ const getFirstChar = (name?: string): string => {
117
+ if (!name) return ''
118
+ return name.charAt(0)
119
+ }
120
+ </script>
121
+
122
+ <template>
123
+ <!-- 左侧菜单布局 -->
124
+ <div v-if="currentLayout === 'sidebar'" class="layout layout--sidebar">
125
+ <aside class="layout__aside" :style="{ width: sidebarWidth }">
126
+ <Sidebar />
127
+ </aside>
128
+ <div class="layout__main">
129
+ <header class="layout__header">
130
+ <Header />
131
+ </header>
132
+ <main class="layout__content">
133
+ <router-view />
134
+ </main>
135
+ </div>
136
+ </div>
137
+
138
+ <!-- 顶部菜单布局 -->
139
+ <div v-else-if="currentLayout === 'top'" class="layout layout--top">
140
+ <header class="layout__header-top">
141
+ <div class="layout__logo">
142
+ <img src="/vite.svg" alt="Logo" class="layout__logo-img" />
143
+ <span class="layout__logo-text">{{ appStore.appName }}</span>
144
+ </div>
145
+ <Menu
146
+ mode="horizontal"
147
+ :default-active="activeTopMenu"
148
+ class="layout__top-menu"
149
+ background-color="transparent"
150
+ :text-color="menuTextColor"
151
+ :active-text-color="menuActiveTextColor"
152
+ @select="handleMenuSelect"
153
+ >
154
+ <template v-for="menu in topLevelMenus" :key="menu.menuUrl">
155
+ <SubMenu v-if="menu.hasChildren" :index="menu.menuCode">
156
+ <template #title>
157
+ <span class="layout__menu-icon">
158
+ <Icon v-if="iconExists(getMenuIcon(menu.icon))" :name="getMenuIcon(menu.icon)" :size="16" />
159
+ <span v-else class="layout__menu-char">{{ getFirstChar(menu.menuName) }}</span>
160
+ </span>
161
+ <span>{{ menu.menuName }}</span>
162
+ </template>
163
+ <MenuItem
164
+ v-for="child in menuStore.menuList.find(m => m.menuCode === menu.menuCode)?.children"
165
+ :key="child.menuUrl"
166
+ :index="child.menuUrl"
167
+ >
168
+ <span>{{ child.menuName }}</span>
169
+ </MenuItem>
170
+ </SubMenu>
171
+ <MenuItem v-else :index="menu.menuUrl">
172
+ <span class="layout__menu-icon">
173
+ <Icon v-if="iconExists(getMenuIcon(menu.icon))" :name="getMenuIcon(menu.icon)" :size="16" />
174
+ <span v-else class="layout__menu-char">{{ getFirstChar(menu.menuName) }}</span>
175
+ </span>
176
+ <span>{{ menu.menuName }}</span>
177
+ </MenuItem>
178
+ </template>
179
+ </Menu>
180
+ <div class="layout__header-right">
181
+ <Header />
182
+ </div>
183
+ </header>
184
+ <main class="layout__content-top">
185
+ <router-view />
186
+ </main>
187
+ </div>
188
+
189
+ <!-- 混合布局 -->
190
+ <div v-else-if="currentLayout === 'mix'" class="layout layout--mix">
191
+ <header class="layout__header-mix">
192
+ <div class="layout__logo">
193
+ <img src="/vite.svg" alt="Logo" class="layout__logo-img" />
194
+ <span class="layout__logo-text">{{ appStore.appName }}</span>
195
+ </div>
196
+ <Menu
197
+ mode="horizontal"
198
+ :default-active="activeTopMenu"
199
+ class="layout__top-menu"
200
+ background-color="transparent"
201
+ :text-color="menuTextColor"
202
+ :active-text-color="menuActiveTextColor"
203
+ @select="handleTopMenuSelect"
204
+ >
205
+ <MenuItem
206
+ v-for="menu in topLevelMenus"
207
+ :key="menu.menuCode"
208
+ :index="menu.menuCode"
209
+ >
210
+ <span class="layout__menu-icon">
211
+ <Icon v-if="iconExists(getMenuIcon(menu.icon))" :name="getMenuIcon(menu.icon)" :size="16" />
212
+ <span v-else class="layout__menu-char">{{ getFirstChar(menu.menuName) }}</span>
213
+ </span>
214
+ <span>{{ menu.menuName }}</span>
215
+ </MenuItem>
216
+ </Menu>
217
+ <div class="layout__header-right">
218
+ <Header />
219
+ </div>
220
+ </header>
221
+ <div class="layout__mix-body">
222
+ <aside v-if="mixSubMenus.length > 0" class="layout__mix-aside" :style="{ width: sidebarWidth }">
223
+ <div class="layout__mix-sidebar">
224
+ <Menu
225
+ mode="vertical"
226
+ :collapse="appStore.isCollapsed"
227
+ :collapse-transition="false"
228
+ :default-active="currentPath"
229
+ :background-color="menuBgColor"
230
+ :text-color="menuTextColor"
231
+ :active-text-color="menuActiveTextColor"
232
+ class="layout__mix-menu"
233
+ @select="handleMenuSelect"
234
+ >
235
+ <MenuItem
236
+ v-for="child in mixSubMenus"
237
+ :key="child.menuUrl"
238
+ :index="child.menuUrl"
239
+ >
240
+ <span class="layout__menu-icon">
241
+ <Icon v-if="iconExists(getMenuIcon(child.icon))" :name="getMenuIcon(child.icon)" :size="16" />
242
+ <span v-else class="layout__menu-char">{{ getFirstChar(child.menuName) }}</span>
243
+ </span>
244
+ <span>{{ child.menuName }}</span>
245
+ </MenuItem>
246
+ </Menu>
247
+ </div>
248
+ </aside>
249
+ <main class="layout__content-mix">
250
+ <router-view />
251
+ </main>
252
+ </div>
253
+ </div>
254
+ </template>
255
+
256
+ <style lang="scss" scoped>
257
+ .layout {
258
+ display: flex;
259
+ width: 100%;
260
+ height: 100%;
261
+
262
+ // 左侧菜单布局
263
+ &--sidebar {
264
+ flex-direction: row;
265
+
266
+ .layout__aside {
267
+ transition: width 0.3s;
268
+ overflow: hidden;
269
+ flex-shrink: 0;
270
+ height: 100%;
271
+ }
272
+
273
+ .layout__main {
274
+ flex: 1;
275
+ display: flex;
276
+ flex-direction: column;
277
+ overflow: hidden;
278
+ height: 100%;
279
+ }
280
+
281
+ .layout__header {
282
+ height: 50px;
283
+ background-color: var(--bg-color);
284
+ border-bottom: 1px solid var(--color-border-lighter);
285
+ flex-shrink: 0;
286
+ }
287
+
288
+ .layout__content {
289
+ flex: 1;
290
+ overflow: auto;
291
+ background-color: var(--bg-color-page);
292
+ }
293
+ }
294
+
295
+ // 顶部菜单布局
296
+ &--top {
297
+ flex-direction: column;
298
+
299
+ .layout__header-top {
300
+ height: 60px;
301
+ display: flex;
302
+ align-items: center;
303
+ padding: 0 20px;
304
+ background-color: var(--bg-color);
305
+ border-bottom: 1px solid var(--color-border-lighter);
306
+ flex-shrink: 0;
307
+
308
+ .layout__logo {
309
+ display: flex;
310
+ align-items: center;
311
+ gap: 10px;
312
+ margin-right: 30px;
313
+ }
314
+
315
+ .layout__logo-img {
316
+ width: 32px;
317
+ height: 32px;
318
+ }
319
+
320
+ .layout__logo-text {
321
+ font-size: 16px;
322
+ font-weight: 600;
323
+ color: var(--color-primary);
324
+ }
325
+
326
+ .layout__top-menu {
327
+ flex: 1;
328
+ border-bottom: none;
329
+ background-color: transparent;
330
+ }
331
+
332
+ .layout__header-right {
333
+ display: flex;
334
+ align-items: center;
335
+ }
336
+ }
337
+
338
+ .layout__content-top {
339
+ flex: 1;
340
+ overflow: auto;
341
+ background-color: var(--bg-color-page);
342
+ }
343
+ }
344
+
345
+ // 混合布局
346
+ &--mix {
347
+ flex-direction: column;
348
+
349
+ .layout__header-mix {
350
+ height: 60px;
351
+ display: flex;
352
+ align-items: center;
353
+ padding: 0 20px;
354
+ background-color: var(--bg-color);
355
+ border-bottom: 1px solid var(--color-border-lighter);
356
+ flex-shrink: 0;
357
+
358
+ .layout__logo {
359
+ display: flex;
360
+ align-items: center;
361
+ gap: 10px;
362
+ margin-right: 30px;
363
+ }
364
+
365
+ .layout__logo-img {
366
+ width: 32px;
367
+ height: 32px;
368
+ }
369
+
370
+ .layout__logo-text {
371
+ font-size: 16px;
372
+ font-weight: 600;
373
+ color: var(--color-primary);
374
+ }
375
+
376
+ .layout__top-menu {
377
+ flex: 1;
378
+ border-bottom: none;
379
+ background-color: transparent;
380
+ }
381
+
382
+ .layout__header-right {
383
+ display: flex;
384
+ align-items: center;
385
+ }
386
+ }
387
+
388
+ .layout__mix-body {
389
+ flex: 1;
390
+ display: flex;
391
+ overflow: hidden;
392
+
393
+ .layout__mix-aside {
394
+ transition: width 0.3s;
395
+ overflow: hidden;
396
+ flex-shrink: 0;
397
+ background-color: var(--bg-color);
398
+ border-right: 1px solid var(--color-border-lighter);
399
+ }
400
+
401
+ .layout__mix-sidebar {
402
+ height: 100%;
403
+ display: flex;
404
+ flex-direction: column;
405
+ }
406
+
407
+ .layout__mix-menu {
408
+ flex: 1;
409
+ border-right: none;
410
+ overflow-y: auto;
411
+ }
412
+
413
+ .layout__content-mix {
414
+ flex: 1;
415
+ overflow: auto;
416
+ background-color: var(--bg-color-page);
417
+ }
418
+ }
419
+ }
420
+
421
+ // 公共样式
422
+ &__menu-icon {
423
+ display: inline-flex;
424
+ align-items: center;
425
+ justify-content: center;
426
+ width: 16px;
427
+ height: 16px;
428
+ margin-right: 8px;
429
+ }
430
+
431
+ &__menu-char {
432
+ display: inline-flex;
433
+ align-items: center;
434
+ justify-content: center;
435
+ width: 16px;
436
+ height: 16px;
437
+ font-size: 12px;
438
+ font-weight: 600;
439
+ color: var(--color-primary);
440
+ background-color: var(--color-primary-light-8);
441
+ border-radius: 4px;
442
+ }
443
+ }
64
444
  </style>
@@ -1,62 +1,62 @@
1
- /**
2
- * 应用组合函数
3
- * 提供应用级别的状态和方法
4
- */
5
-
6
- import { computed } from 'vue'
7
- import { useAppStore } from '@/stores/app'
8
- import { useUserStore } from '@/stores/user'
9
- import { useAuthStore } from '@/stores/auth'
10
-
11
- /**
12
- * 应用级别组合函数
13
- * @returns 应用状态和方法
14
- */
15
- export function useApp() {
16
- const appStore = useAppStore()
17
- const userStore = useUserStore()
18
- const authStore = useAuthStore()
19
-
20
- // 用户名
21
- const userName = computed(() => userStore.userName || '')
22
-
23
- // 用户信息
24
- const userInfo = computed(() => userStore.userInfo)
25
-
26
- // 应用名称
27
- const appName = computed(() => appStore.appName)
28
-
29
- // 是否已登录
30
- const isLoggedIn = computed(() => authStore.isLoggedIn)
31
-
32
- // 主题相关
33
- const isDark = computed(() => appStore.isDark)
34
- const theme = computed(() => appStore.theme)
35
-
36
- // 布局相关
37
- const isCollapsed = computed(() => appStore.isCollapsed)
38
- const layout = computed(() => appStore.layout)
39
-
40
- // 切换主题
41
- const toggleTheme = () => {
42
- appStore.toggleTheme()
43
- }
44
-
45
- // 切换菜单折叠
46
- const toggleCollapse = () => {
47
- appStore.toggleCollapse()
48
- }
49
-
50
- return {
51
- userName,
52
- userInfo,
53
- appName,
54
- isLoggedIn,
55
- isDark,
56
- theme,
57
- isCollapsed,
58
- layout,
59
- toggleTheme,
60
- toggleCollapse
61
- }
1
+ /**
2
+ * 应用组合函数
3
+ * 提供应用级别的状态和方法
4
+ */
5
+
6
+ import { computed } from 'vue'
7
+ import { useAppStore } from '@/stores/app'
8
+ import { useUserStore } from '@/stores/user'
9
+ import { useAuthStore } from '@/stores/auth'
10
+
11
+ /**
12
+ * 应用级别组合函数
13
+ * @returns 应用状态和方法
14
+ */
15
+ export function useApp() {
16
+ const appStore = useAppStore()
17
+ const userStore = useUserStore()
18
+ const authStore = useAuthStore()
19
+
20
+ // 用户名
21
+ const userName = computed(() => userStore.userName || '')
22
+
23
+ // 用户信息
24
+ const userInfo = computed(() => userStore.userInfo)
25
+
26
+ // 应用名称
27
+ const appName = computed(() => appStore.appName)
28
+
29
+ // 是否已登录
30
+ const isLoggedIn = computed(() => authStore.isLoggedIn)
31
+
32
+ // 主题相关
33
+ const isDark = computed(() => appStore.isDark)
34
+ const theme = computed(() => appStore.theme)
35
+
36
+ // 布局相关
37
+ const isCollapsed = computed(() => appStore.isCollapsed)
38
+ const layout = computed(() => appStore.layout)
39
+
40
+ // 切换主题
41
+ const toggleTheme = () => {
42
+ appStore.toggleTheme()
43
+ }
44
+
45
+ // 切换菜单折叠
46
+ const toggleCollapse = () => {
47
+ appStore.toggleCollapse()
48
+ }
49
+
50
+ return {
51
+ userName,
52
+ userInfo,
53
+ appName,
54
+ isLoggedIn,
55
+ isDark,
56
+ theme,
57
+ isCollapsed,
58
+ layout,
59
+ toggleTheme,
60
+ toggleCollapse
61
+ }
62
62
  }
@@ -1,17 +1,17 @@
1
- /**
2
- * 权限组合函数
3
- */
4
-
5
- import { computed } from 'vue'
6
- import { useUserStore } from '@/stores/user'
7
-
8
- export function useAuth() {
9
- const userStore = useUserStore()
10
-
11
- // 是否已登录
12
- const isLoggedIn = computed(() => userStore.isLoggedIn)
13
-
14
- return {
15
- isLoggedIn
16
- }
1
+ /**
2
+ * 权限组合函数
3
+ */
4
+
5
+ import { computed } from 'vue'
6
+ import { useUserStore } from '@/stores/user'
7
+
8
+ export function useAuth() {
9
+ const userStore = useUserStore()
10
+
11
+ // 是否已登录
12
+ const isLoggedIn = computed(() => userStore.isLoggedIn)
13
+
14
+ return {
15
+ isLoggedIn
16
+ }
17
17
  }