xto-fronted 0.4.93 → 0.4.95

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 (92) hide show
  1. package/dist/assets/404-C9Uh6Uu-.css +1 -0
  2. package/dist/assets/404-zjGLLssH.js +1 -0
  3. package/dist/assets/_plugin-vue_export-helper-DlAUqK2U.js +1 -0
  4. package/dist/assets/index-B5xc4gQB.css +1 -0
  5. package/dist/assets/index-BDgOY6Rp.js +1 -0
  6. package/dist/assets/index-BIoRANs0.js +1 -0
  7. package/dist/assets/index-BRR97dc6.js +1 -0
  8. package/dist/assets/index-Bz0BgZQ1.js +1 -0
  9. package/dist/assets/index-CAdztNsv.css +1 -0
  10. package/dist/assets/index-CCXrcISf.css +1 -0
  11. package/dist/assets/index-CfpZmcpk.css +1 -0
  12. package/dist/assets/index-CwJSA85U.js +1 -0
  13. package/dist/assets/index-CwRA10ac.js +1 -0
  14. package/dist/assets/index-D8NDxq9d.js +1 -0
  15. package/dist/assets/index-DEB6-Iv_.js +2 -0
  16. package/dist/assets/index-DM4Ezclc.css +1 -0
  17. package/dist/assets/index-DYv7nImj.css +1 -0
  18. package/dist/assets/index-t-2Y0KhA.css +1 -0
  19. package/dist/assets/vendor-CUVPinTg.js +13 -0
  20. package/dist/assets/vue-vendor-DeJXJVbN.js +29 -0
  21. package/dist/assets/xto-base-CL2NKZJJ.css +1 -0
  22. package/dist/assets/xto-base-PwLGsxxb.js +1 -0
  23. package/dist/assets/xto-business--V1F5Gwb.css +1 -0
  24. package/dist/assets/xto-core-CtL4zKiV.js +1 -0
  25. package/dist/assets/xto-data-MxZsiJgi.css +1 -0
  26. package/dist/assets/xto-data-bCXQa7fT.js +1 -0
  27. package/dist/assets/xto-feedback-Bxx38c3P.css +1 -0
  28. package/dist/assets/xto-feedback-CPydp0kn.js +1 -0
  29. package/dist/assets/xto-form-Cu6q3VLG.css +1 -0
  30. package/dist/assets/xto-form-bywohdAf.js +1 -0
  31. package/dist/assets/xto-layout-BDD6sSlM.css +1 -0
  32. package/dist/assets/xto-navigation-Bbdpine9.js +1 -0
  33. package/dist/assets/xto-navigation-XfpyMpEo.css +1 -0
  34. package/dist/components/Layout/MixTopMenu.vue.d.ts +6 -1
  35. package/dist/components/Layout/Sidebar.vue.d.ts +2 -0
  36. package/dist/components/Layout/TopMenu.vue.d.ts +6 -1
  37. package/dist/components/Layout/index.vue.d.ts +6 -1
  38. package/dist/{index-D59X6HmM.js → index-3ekBp4iW.js} +2 -2
  39. package/dist/{index-Cp8kqjtv.js → index-58aI1w0v.js} +2 -2
  40. package/dist/{index-me_Uu2lk.js → index-A_B_Ap_A.js} +1560 -1590
  41. package/dist/{index-lJrh5CFc.js → index-B5DLfOYb.js} +23 -23
  42. package/dist/index-BAmYUT0G.js +189 -0
  43. package/dist/{index-B4U8Dy2W.js → index-BK4Mut6H.js} +2 -2
  44. package/dist/index-BRvi9qW-.js +515 -0
  45. package/dist/index-BVGW4DDQ.js +189 -0
  46. package/dist/index-BXg94yA2.js +515 -0
  47. package/dist/index-Bmf0YbVq.js +189 -0
  48. package/dist/index-C2-a5KSQ.js +4233 -0
  49. package/dist/index-CAHSv7LK.js +4285 -0
  50. package/dist/index-CeZ0CSSs.js +641 -0
  51. package/dist/index-Cf8E7FM1.js +4270 -0
  52. package/dist/index-CgyQqbdx.js +189 -0
  53. package/dist/index-ChowNrlU.js +641 -0
  54. package/dist/index-D25KzR0I.js +479 -0
  55. package/dist/index-DCBIjLHy.js +515 -0
  56. package/dist/index-DEYOivza.js +641 -0
  57. package/dist/index-DReodgBw.js +4233 -0
  58. package/dist/index-DjERNRXX.js +515 -0
  59. package/dist/index-LSdsO2Ox.js +479 -0
  60. package/dist/index-UJixTdep.js +479 -0
  61. package/dist/index-gBlRG4kk.js +479 -0
  62. package/dist/index-xWU3J3OH.js +641 -0
  63. package/dist/index.es.js +1 -1
  64. package/dist/index.html +28 -0
  65. package/dist/index.umd.js +8 -8
  66. package/dist/style.css +1 -1
  67. package/package.json +91 -91
  68. package/src/App.vue +48 -48
  69. package/src/assets/styles/_dark.scss +639 -639
  70. package/src/assets/styles/_root.scss +183 -183
  71. package/src/assets/styles/_variables.scss +69 -69
  72. package/src/assets/styles/index.scss +460 -460
  73. package/src/components/Layout/MixTopMenu.vue +8 -1
  74. package/src/components/Layout/Sidebar.vue +200 -198
  75. package/src/components/Layout/SidebarMenuItem.vue +158 -158
  76. package/src/components/Layout/TopMenu.vue +1177 -1170
  77. package/src/components/Layout/index.vue +199 -192
  78. package/src/composables/useI18n.ts +43 -43
  79. package/src/index.ts +100 -100
  80. package/src/router/layoutRoute.ts +59 -59
  81. package/src/stores/index.ts +15 -15
  82. package/src/stores/locale.ts +66 -66
  83. package/src/types/json-bigint.d.ts +18 -18
  84. package/src/types/xto.d.ts +172 -172
  85. package/src/utils/request.ts +184 -184
  86. package/src/views/dashboard/index.vue +545 -545
  87. package/src/views/error/403.vue +251 -251
  88. package/src/views/error/404.vue +253 -253
  89. package/src/views/login/index.vue +586 -586
  90. package/src/views/system/menu/index.vue +690 -690
  91. package/src/views/system/role/index.vue +583 -583
  92. package/src/views/system/user/index.vue +655 -655
@@ -9,6 +9,13 @@ import { Menu, MenuItem } from '@xto/navigation'
9
9
  import { Icon } from '@xto/base'
10
10
  import { Drawer } from '@xto/feedback'
11
11
 
12
+ // Props
13
+ const props = withDefaults(defineProps<{
14
+ logoSrc?: string
15
+ }>(), {
16
+ logoSrc: '/vite.svg'
17
+ })
18
+
12
19
  const route = useRoute()
13
20
  const router = useRouter()
14
21
  const menuStore = useMenuStore()
@@ -353,7 +360,7 @@ onUnmounted(() => {
353
360
  <div class="mix-top-menu">
354
361
  <!-- 左侧 Logo -->
355
362
  <div class="mix-top-menu__logo">
356
- <img src="/vite.svg" alt="Logo" class="mix-top-menu__logo-img" />
363
+ <img :src="props.logoSrc" alt="Logo" class="mix-top-menu__logo-img" />
357
364
  <span class="mix-top-menu__logo-text">{{ appStore.appName }}</span>
358
365
  </div>
359
366
 
@@ -1,199 +1,201 @@
1
- <script setup lang="ts">
2
- import { computed, ref, watch } from 'vue'
3
- import { useRoute, useRouter } from 'vue-router'
4
- import { useMenuStore } from '@/stores/menu'
5
- import { useUserStore } from '@/stores/user'
6
- import { useAuthStore } from '@/stores/auth'
7
- import { useAppStore } from '@/stores/app'
8
- import { Menu } from '@xto/navigation'
9
- import { Button } from '@xto/base'
10
- import { local } from '@/utils/storage'
11
- import SidebarMenuItem from './SidebarMenuItem.vue'
12
-
13
- // Props
14
- const props = withDefaults(defineProps<{
15
- menuList?: any[]
16
- showLogo?: boolean
17
- showUser?: boolean
18
- }>(), {
19
- menuList: () => [],
20
- showLogo: true,
21
- showUser: true
22
- })
23
-
24
- const route = useRoute()
25
- const router = useRouter()
26
- const menuStore = useMenuStore()
27
- const userStore = useUserStore()
28
- const authStore = useAuthStore()
29
- const appStore = useAppStore()
30
-
31
- // 使用传入的菜单列表,如果没有传入则使用 store 中的
32
- const displayMenuList = computed(() => props.menuList.length > 0 ? props.menuList : menuStore.menuList)
33
-
34
- const isCollapsed = computed(() => appStore.isCollapsed)
35
- const activeMenu = computed(() => route.path)
36
-
37
- // 菜单展开状态持久化
38
- const OPENED_MENUS_KEY = 'sidebar_opened_menus'
39
- const openedMenus = ref<string[]>(local.get<string[]>(OPENED_MENUS_KEY) || [])
40
-
41
- // 监听变化并持久化
42
- watch(openedMenus, (val) => {
43
- local.set(OPENED_MENUS_KEY, val)
44
- }, { deep: true })
45
-
46
- // 递归查找当前路由对应的父菜单路径
47
- const findMenuPath = (menus: any[], path: string, parentUrls: string[] = []): string[] | null => {
48
- for (const menu of menus) {
49
- // 当前菜单匹配
50
- if (path === menu.menuUrl || path.startsWith(menu.menuUrl + '/')) {
51
- return [...parentUrls, menu.menuUrl]
52
- }
53
- // 递归查找子菜单
54
- if (menu.children?.length) {
55
- const result = findMenuPath(menu.children, path, [...parentUrls, menu.menuUrl])
56
- if (result) return result
57
- }
58
- }
59
- return null
60
- }
61
-
62
- // 监听路由变化,确保当前路由的父菜单展开
63
- watch([() => route.path, displayMenuList], ([path, menus]) => {
64
- if (menus.length > 0) {
65
- const menuPath = findMenuPath(menus, path)
66
- if (menuPath) {
67
- // 获取需要展开的父菜单(不包括当前页面本身)
68
- const parentsToOpen = menuPath.slice(0, -1)
69
- // 合并已展开的菜单,确保父菜单保持展开状态
70
- const newOpenedMenus = new Set([...openedMenus.value, ...parentsToOpen])
71
- openedMenus.value = Array.from(newOpenedMenus)
72
- }
73
- }
74
- }, { immediate: true })
75
-
76
- // 菜单主题相关
77
- const menuBgColor = computed(() => appStore.isDark ? '#1d1e1f' : '#fff')
78
- const menuTextColor = computed(() => appStore.isDark ? '#cfd3dc' : '#303133')
79
- const menuActiveTextColor = computed(() => '#409eff')
80
-
81
- // 菜单选择
82
- const handleMenuSelect = (index: string) => {
83
- if (index && index !== route.path) {
84
- router.push(index)
85
- }
86
- }
87
-
88
- // 退出登录
89
- const handleLogout = () => {
90
- authStore.logout()
91
- userStore.clearUserInfo()
92
- menuStore.clearMenu()
93
- router.push('/login')
94
- }
95
- </script>
96
-
97
- <template>
98
- <div class="sidebar" :class="{ 'sidebar--collapsed': isCollapsed }">
99
- <!-- Logo -->
100
- <div v-if="props.showLogo" class="sidebar__logo">
101
- <img src="/vite.svg" alt="Logo" class="sidebar__logo-img" />
102
- <span v-show="!isCollapsed" class="sidebar__logo-text">{{ appStore.appName }}</span>
103
- </div>
104
-
105
- <!-- 菜单 -->
106
- <Menu
107
- v-model="activeMenu"
108
- v-model:openeds="openedMenus"
109
- mode="vertical"
110
- :collapse="isCollapsed"
111
- :collapse-transition="false"
112
- :background-color="menuBgColor"
113
- :text-color="menuTextColor"
114
- :active-text-color="menuActiveTextColor"
115
- class="sidebar__menu"
116
- @select="handleMenuSelect"
117
- >
118
- <template v-for="menu in displayMenuList" :key="menu.menuUrl">
119
- <SidebarMenuItem :menu="menu" />
120
- </template>
121
- </Menu>
122
-
123
- <!-- 用户信息 -->
124
- <div v-if="props.showUser && !isCollapsed" class="sidebar__user">
125
- <div class="sidebar__user-info">
126
- <span class="sidebar__user-name">{{ userStore.userName }}</span>
127
- <span class="sidebar__user-role">{{ userStore.departmentName }}</span>
128
- </div>
129
- <Button type="text" size="small" @click="handleLogout">退出</Button>
130
- </div>
131
- </div>
132
- </template>
133
-
134
- <style lang="scss" scoped>
135
- .sidebar {
136
- height: 100%;
137
- display: flex;
138
- flex-direction: column;
139
- background-color: var(--bg-color);
140
- border-right: 1px solid var(--color-border-lighter);
141
-
142
- &--collapsed {
143
- .sidebar__logo {
144
- justify-content: center;
145
- padding: 0;
146
- }
147
- }
148
-
149
- &__logo {
150
- height: 50px;
151
- display: flex;
152
- align-items: center;
153
- padding: 0 20px;
154
- gap: 10px;
155
- border-bottom: 1px solid var(--color-border-lighter);
156
- }
157
-
158
- &__logo-img {
159
- width: 32px;
160
- height: 32px;
161
- }
162
-
163
- &__logo-text {
164
- font-size: 16px;
165
- font-weight: 600;
166
- color: var(--color-primary);
167
- }
168
-
169
- &__menu {
170
- flex: 1;
171
- border-right: none;
172
- overflow-y: auto;
173
- }
174
-
175
- &__user {
176
- padding: 10px;
177
- border-top: 1px solid var(--color-border-lighter);
178
- display: flex;
179
- align-items: center;
180
- justify-content: space-between;
181
- }
182
-
183
- &__user-info {
184
- display: flex;
185
- flex-direction: column;
186
- gap: 2px;
187
- }
188
-
189
- &__user-name {
190
- font-size: 14px;
191
- font-weight: 500;
192
- }
193
-
194
- &__user-role {
195
- font-size: 12px;
196
- color: var(--color-text-secondary);
197
- }
198
- }
1
+ <script setup lang="ts">
2
+ import { computed, ref, watch } from 'vue'
3
+ import { useRoute, useRouter } from 'vue-router'
4
+ import { useMenuStore } from '@/stores/menu'
5
+ import { useUserStore } from '@/stores/user'
6
+ import { useAuthStore } from '@/stores/auth'
7
+ import { useAppStore } from '@/stores/app'
8
+ import { Menu } from '@xto/navigation'
9
+ import { Button } from '@xto/base'
10
+ import { local } from '@/utils/storage'
11
+ import SidebarMenuItem from './SidebarMenuItem.vue'
12
+
13
+ // Props
14
+ const props = withDefaults(defineProps<{
15
+ menuList?: any[]
16
+ showLogo?: boolean
17
+ showUser?: boolean
18
+ logoSrc?: string
19
+ }>(), {
20
+ menuList: () => [],
21
+ showLogo: true,
22
+ showUser: true,
23
+ logoSrc: '/vite.svg'
24
+ })
25
+
26
+ const route = useRoute()
27
+ const router = useRouter()
28
+ const menuStore = useMenuStore()
29
+ const userStore = useUserStore()
30
+ const authStore = useAuthStore()
31
+ const appStore = useAppStore()
32
+
33
+ // 使用传入的菜单列表,如果没有传入则使用 store 中的
34
+ const displayMenuList = computed(() => props.menuList.length > 0 ? props.menuList : menuStore.menuList)
35
+
36
+ const isCollapsed = computed(() => appStore.isCollapsed)
37
+ const activeMenu = computed(() => route.path)
38
+
39
+ // 菜单展开状态持久化
40
+ const OPENED_MENUS_KEY = 'sidebar_opened_menus'
41
+ const openedMenus = ref<string[]>(local.get<string[]>(OPENED_MENUS_KEY) || [])
42
+
43
+ // 监听变化并持久化
44
+ watch(openedMenus, (val) => {
45
+ local.set(OPENED_MENUS_KEY, val)
46
+ }, { deep: true })
47
+
48
+ // 递归查找当前路由对应的父菜单路径
49
+ const findMenuPath = (menus: any[], path: string, parentUrls: string[] = []): string[] | null => {
50
+ for (const menu of menus) {
51
+ // 当前菜单匹配
52
+ if (path === menu.menuUrl || path.startsWith(menu.menuUrl + '/')) {
53
+ return [...parentUrls, menu.menuUrl]
54
+ }
55
+ // 递归查找子菜单
56
+ if (menu.children?.length) {
57
+ const result = findMenuPath(menu.children, path, [...parentUrls, menu.menuUrl])
58
+ if (result) return result
59
+ }
60
+ }
61
+ return null
62
+ }
63
+
64
+ // 监听路由变化,确保当前路由的父菜单展开
65
+ watch([() => route.path, displayMenuList], ([path, menus]) => {
66
+ if (menus.length > 0) {
67
+ const menuPath = findMenuPath(menus, path)
68
+ if (menuPath) {
69
+ // 获取需要展开的父菜单(不包括当前页面本身)
70
+ const parentsToOpen = menuPath.slice(0, -1)
71
+ // 合并已展开的菜单,确保父菜单保持展开状态
72
+ const newOpenedMenus = new Set([...openedMenus.value, ...parentsToOpen])
73
+ openedMenus.value = Array.from(newOpenedMenus)
74
+ }
75
+ }
76
+ }, { immediate: true })
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 handleMenuSelect = (index: string) => {
85
+ if (index && index !== route.path) {
86
+ router.push(index)
87
+ }
88
+ }
89
+
90
+ // 退出登录
91
+ const handleLogout = () => {
92
+ authStore.logout()
93
+ userStore.clearUserInfo()
94
+ menuStore.clearMenu()
95
+ router.push('/login')
96
+ }
97
+ </script>
98
+
99
+ <template>
100
+ <div class="sidebar" :class="{ 'sidebar--collapsed': isCollapsed }">
101
+ <!-- Logo -->
102
+ <div v-if="props.showLogo" class="sidebar__logo">
103
+ <img :src="props.logoSrc" alt="Logo" class="sidebar__logo-img" />
104
+ <span v-show="!isCollapsed" class="sidebar__logo-text">{{ appStore.appName }}</span>
105
+ </div>
106
+
107
+ <!-- 菜单 -->
108
+ <Menu
109
+ v-model="activeMenu"
110
+ v-model:openeds="openedMenus"
111
+ mode="vertical"
112
+ :collapse="isCollapsed"
113
+ :collapse-transition="false"
114
+ :background-color="menuBgColor"
115
+ :text-color="menuTextColor"
116
+ :active-text-color="menuActiveTextColor"
117
+ class="sidebar__menu"
118
+ @select="handleMenuSelect"
119
+ >
120
+ <template v-for="menu in displayMenuList" :key="menu.menuUrl">
121
+ <SidebarMenuItem :menu="menu" />
122
+ </template>
123
+ </Menu>
124
+
125
+ <!-- 用户信息 -->
126
+ <div v-if="props.showUser && !isCollapsed" class="sidebar__user">
127
+ <div class="sidebar__user-info">
128
+ <span class="sidebar__user-name">{{ userStore.userName }}</span>
129
+ <span class="sidebar__user-role">{{ userStore.departmentName }}</span>
130
+ </div>
131
+ <Button type="text" size="small" @click="handleLogout">退出</Button>
132
+ </div>
133
+ </div>
134
+ </template>
135
+
136
+ <style lang="scss" scoped>
137
+ .sidebar {
138
+ height: 100%;
139
+ display: flex;
140
+ flex-direction: column;
141
+ background-color: var(--bg-color);
142
+ border-right: 1px solid var(--color-border-lighter);
143
+
144
+ &--collapsed {
145
+ .sidebar__logo {
146
+ justify-content: center;
147
+ padding: 0;
148
+ }
149
+ }
150
+
151
+ &__logo {
152
+ height: 50px;
153
+ display: flex;
154
+ align-items: center;
155
+ padding: 0 20px;
156
+ gap: 10px;
157
+ border-bottom: 1px solid var(--color-border-lighter);
158
+ }
159
+
160
+ &__logo-img {
161
+ width: 32px;
162
+ height: 32px;
163
+ }
164
+
165
+ &__logo-text {
166
+ font-size: 16px;
167
+ font-weight: 600;
168
+ color: var(--color-primary);
169
+ }
170
+
171
+ &__menu {
172
+ flex: 1;
173
+ border-right: none;
174
+ overflow-y: auto;
175
+ }
176
+
177
+ &__user {
178
+ padding: 10px;
179
+ border-top: 1px solid var(--color-border-lighter);
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: space-between;
183
+ }
184
+
185
+ &__user-info {
186
+ display: flex;
187
+ flex-direction: column;
188
+ gap: 2px;
189
+ }
190
+
191
+ &__user-name {
192
+ font-size: 14px;
193
+ font-weight: 500;
194
+ }
195
+
196
+ &__user-role {
197
+ font-size: 12px;
198
+ color: var(--color-text-secondary);
199
+ }
200
+ }
199
201
  </style>