xto-fronted 0.1.1 → 0.1.3

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 (67) hide show
  1. package/.env.development +3 -4
  2. package/.env.production +3 -4
  3. package/bin/cli.js +104 -0
  4. package/dist/{403-MQkNUulz.js → 403-DM5wfQkM.js} +6 -6
  5. package/dist/{404-BOFYLq4X.js → 404-BurAu5LC.js} +7 -7
  6. package/dist/api/auth.d.ts +9 -8
  7. package/dist/api/menu.d.ts +3 -0
  8. package/dist/api/user.d.ts +2 -12
  9. package/dist/composables/index.d.ts +8 -0
  10. package/dist/composables/useApp.d.ts +64 -0
  11. package/dist/composables/useAuth.d.ts +19 -4
  12. package/dist/composables/useMenu.d.ts +34 -0
  13. package/dist/config/index.d.ts +11 -0
  14. package/dist/index-BNiEld34.js +15 -0
  15. package/dist/index-Be9RiEfo.js +98 -0
  16. package/dist/index-BqRv1bdN.js +1185 -0
  17. package/dist/index-CQLVXvNJ.js +15 -0
  18. package/dist/index-CyiE8n2V.js +15 -0
  19. package/dist/index-xauR1bOL.js +15 -0
  20. package/dist/index.d.ts +7 -4
  21. package/dist/index.es.js +50 -66
  22. package/dist/index.umd.js +1 -1
  23. package/dist/stores/auth.d.ts +60 -23
  24. package/dist/stores/menu.d.ts +40 -29
  25. package/dist/stores/user.d.ts +63 -84
  26. package/dist/style.css +1 -1
  27. package/dist/utils/auth.d.ts +15 -7
  28. package/dist/utils/permission.d.ts +1 -6
  29. package/dist/views/system/menu/index.vue.d.ts +1 -3
  30. package/dist/views/system/role/index.vue.d.ts +1 -3
  31. package/dist/views/system/user/index.vue.d.ts +1 -3
  32. package/package.json +27 -19
  33. package/src/api/auth.ts +34 -25
  34. package/src/api/menu.ts +13 -0
  35. package/src/api/user.ts +11 -45
  36. package/src/components/Layout/Header.vue +334 -389
  37. package/src/components/Layout/Sidebar.vue +212 -296
  38. package/src/components/Layout/Tabs.vue +19 -133
  39. package/src/composables/index.ts +9 -0
  40. package/src/composables/useApp.ts +170 -0
  41. package/src/composables/useAuth.ts +69 -44
  42. package/src/composables/useMenu.ts +141 -0
  43. package/src/config/index.ts +19 -0
  44. package/src/directives/permission.ts +40 -37
  45. package/src/index.ts +9 -4
  46. package/src/router/index.ts +70 -80
  47. package/src/stores/auth.ts +44 -31
  48. package/src/stores/menu.ts +157 -79
  49. package/src/stores/user.ts +40 -72
  50. package/src/types/api.d.ts +102 -83
  51. package/src/types/xto.d.ts +148 -148
  52. package/src/utils/auth.ts +85 -61
  53. package/src/utils/permission.ts +29 -41
  54. package/src/utils/request.ts +125 -125
  55. package/src/utils/storage.ts +10 -1
  56. package/src/views/dashboard/index.vue +31 -283
  57. package/src/views/login/index.vue +140 -247
  58. package/src/views/system/menu/index.vue +31 -380
  59. package/src/views/system/role/index.vue +31 -303
  60. package/src/views/system/user/index.vue +31 -326
  61. package/vite.config.ts +3 -3
  62. package/dist/index-BJxYdNPy.js +0 -475
  63. package/dist/index-BvnIIBR1.js +0 -142
  64. package/dist/index-CEvAq6KE.js +0 -372
  65. package/dist/index-DPkqej__.js +0 -345
  66. package/dist/index-pq9Z5K62.js +0 -184
  67. package/dist/index-vVfjShJR.js +0 -1183
@@ -1,297 +1,213 @@
1
- <script setup lang="ts">
2
- import { ref, computed } 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, MenuItem, SubMenu } from '@xto/navigation'
9
- import { Button } from '@xto/base'
10
- import { Input } from '@xto/form'
11
- import type { MenuItem as MenuItemType } from '@/types/api'
12
-
13
- const route = useRoute()
14
- const router = useRouter()
15
- const menuStore = useMenuStore()
16
- const userStore = useUserStore()
17
- const authStore = useAuthStore()
18
- const appStore = useAppStore()
19
-
20
- const searchKeyword = ref('')
21
- const isCollapsed = computed(() => appStore.isCollapsed)
22
- const activeMenu = computed(() => route.path)
23
-
24
- // 菜单主题相关
25
- const menuBgColor = computed(() => appStore.isDark ? '#1d1e1f' : '#fff')
26
- const menuTextColor = computed(() => appStore.isDark ? '#cfd3dc' : '#303133')
27
- const menuActiveTextColor = computed(() => '#409eff')
28
-
29
- // 扁平化菜单用于搜索
30
- const flattenMenus = (menus: MenuItemType[], parentTitle = ''): (MenuItemType & { parentTitle: string })[] => {
31
- const result: (MenuItemType & { parentTitle: string })[] = []
32
- menus.forEach(menu => {
33
- if (menu.children && menu.children.length > 0) {
34
- result.push(...flattenMenus(menu.children, menu.title))
35
- } else {
36
- result.push({ ...menu, parentTitle })
37
- }
38
- })
39
- return result
40
- }
41
-
42
- // 搜索结果
43
- const searchResults = computed(() => {
44
- if (!searchKeyword.value.trim()) return []
45
- const flatMenus = flattenMenus(menuStore.menuList)
46
- return flatMenus.filter(menu =>
47
- menu.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
48
- )
49
- })
50
-
51
- // 过滤后的菜单列表
52
- const filteredMenuList = computed(() => {
53
- if (!searchKeyword.value.trim()) return menuStore.menuList
54
-
55
- return menuStore.menuList.map(menu => {
56
- if (menu.children && menu.children.length > 0) {
57
- const filteredChildren = menu.children.filter(child =>
58
- child.title.toLowerCase().includes(searchKeyword.value.toLowerCase())
59
- )
60
- if (filteredChildren.length > 0) {
61
- return { ...menu, children: filteredChildren }
62
- }
63
- return null
64
- }
65
- if (menu.title.toLowerCase().includes(searchKeyword.value.toLowerCase())) {
66
- return menu
67
- }
68
- return null
69
- }).filter(Boolean) as MenuItemType[]
70
- })
71
-
72
- // 菜单选择
73
- const handleMenuSelect = (index: string) => {
74
- if (index && index !== route.path) {
75
- router.push(index)
76
- searchKeyword.value = ''
77
- }
78
- }
79
-
80
- // 搜索结果点击
81
- const handleSearchItemClick = (path: string) => {
82
- router.push(path)
83
- searchKeyword.value = ''
84
- }
85
-
86
- // 退出登录
87
- const handleLogout = () => {
88
- authStore.logout()
89
- userStore.clearUserInfo()
90
- menuStore.clearMenu()
91
- router.push('/login')
92
- }
93
-
94
- // 菜单图标
95
- const getMenuIcon = (icon?: string) => {
96
- const iconMap: Record<string, string> = {
97
- dashboard: '📊',
98
- system: '⚙️',
99
- user: '👤',
100
- role: '👥',
101
- menu: '📋',
102
- setting: '🔧'
103
- }
104
- return iconMap[icon || ''] || '📄'
105
- }
106
- </script>
107
-
108
- <template>
109
- <div class="sidebar">
110
- <!-- Logo -->
111
- <div class="sidebar__logo">
112
- <img src="/vite.svg" alt="Logo" class="sidebar__logo-img" />
113
- <span v-show="!isCollapsed" class="sidebar__logo-text">Xto Demo</span>
114
- </div>
115
-
116
- <!-- 搜索框 -->
117
- <div v-if="!isCollapsed" class="sidebar__search">
118
- <Input
119
- v-model="searchKeyword"
120
- placeholder="搜索菜单..."
121
- size="small"
122
- clearable
123
- />
124
- <!-- 搜索结果 -->
125
- <div v-if="searchResults.length > 0" class="sidebar__search-results">
126
- <div
127
- v-for="item in searchResults"
128
- :key="item.path"
129
- class="sidebar__search-item"
130
- @click="handleSearchItemClick(item.path)"
131
- >
132
- <span class="menu-icon">{{ getMenuIcon(item.icon) }}</span>
133
- <div class="sidebar__search-item-info">
134
- <span class="sidebar__search-item-title">{{ item.title }}</span>
135
- <span v-if="item.parentTitle" class="sidebar__search-item-parent">{{ item.parentTitle }}</span>
136
- </div>
137
- </div>
138
- </div>
139
- </div>
140
-
141
- <!-- 菜单 -->
142
- <Menu
143
- :default-active="activeMenu"
144
- :collapse="isCollapsed"
145
- :collapse-transition="false"
146
- :background-color="menuBgColor"
147
- :text-color="menuTextColor"
148
- :active-text-color="menuActiveTextColor"
149
- class="sidebar__menu"
150
- @select="handleMenuSelect"
151
- >
152
- <template v-for="menu in filteredMenuList" :key="menu.path">
153
- <!-- 有子菜单 -->
154
- <SubMenu v-if="menu.children && menu.children.length > 0" :index="menu.path">
155
- <template #title>
156
- <span class="menu-icon">{{ getMenuIcon(menu.icon) }}</span>
157
- <span>{{ menu.title }}</span>
158
- </template>
159
- <MenuItem
160
- v-for="child in menu.children"
161
- :key="child.path"
162
- :index="child.path"
163
- >
164
- <span class="menu-icon">{{ getMenuIcon(child.icon) }}</span>
165
- <span>{{ child.title }}</span>
166
- </MenuItem>
167
- </SubMenu>
168
- <!-- 无子菜单 -->
169
- <MenuItem v-else :index="menu.path">
170
- <span class="menu-icon">{{ getMenuIcon(menu.icon) }}</span>
171
- <span>{{ menu.title }}</span>
172
- </MenuItem>
173
- </template>
174
- </Menu>
175
-
176
- <!-- 用户信息 -->
177
- <div class="sidebar__user" v-if="!isCollapsed">
178
- <div class="sidebar__user-info">
179
- <span class="sidebar__user-name">{{ userStore.nickname }}</span>
180
- <span class="sidebar__user-role">{{ userStore.roles.join(', ') }}</span>
181
- </div>
182
- <Button type="text" size="small" @click="handleLogout">退出</Button>
183
- </div>
184
- </div>
185
- </template>
186
-
187
- <style lang="scss" scoped>
188
- .sidebar {
189
- height: 100%;
190
- display: flex;
191
- flex-direction: column;
192
- background-color: var(--bg-color);
193
-
194
- &__logo {
195
- height: 50px;
196
- display: flex;
197
- align-items: center;
198
- justify-content: center;
199
- gap: 10px;
200
- border-bottom: 1px solid var(--color-border-lighter);
201
- }
202
-
203
- &__logo-img {
204
- width: 32px;
205
- height: 32px;
206
- }
207
-
208
- &__logo-text {
209
- font-size: 16px;
210
- font-weight: 600;
211
- color: var(--color-primary);
212
- }
213
-
214
- &__search {
215
- padding: 10px;
216
- border-bottom: 1px solid var(--color-border-lighter);
217
- position: relative;
218
- }
219
-
220
- &__search-results {
221
- position: absolute;
222
- top: 100%;
223
- left: 0;
224
- right: 0;
225
- background-color: var(--bg-color);
226
- border: 1px solid var(--color-border-lighter);
227
- border-radius: var(--border-radius-base);
228
- box-shadow: var(--box-shadow);
229
- max-height: 300px;
230
- overflow-y: auto;
231
- z-index: 100;
232
- }
233
-
234
- &__search-item {
235
- display: flex;
236
- align-items: center;
237
- gap: 10px;
238
- padding: 10px 12px;
239
- cursor: pointer;
240
- transition: background-color 0.2s;
241
-
242
- &:hover {
243
- background-color: var(--color-fill);
244
- }
245
-
246
- &-info {
247
- display: flex;
248
- flex-direction: column;
249
- gap: 2px;
250
- }
251
-
252
- &-title {
253
- font-size: 14px;
254
- color: var(--color-text-primary);
255
- }
256
-
257
- &-parent {
258
- font-size: 12px;
259
- color: var(--color-text-secondary);
260
- }
261
- }
262
-
263
- &__menu {
264
- flex: 1;
265
- border-right: none;
266
- overflow-y: auto;
267
- }
268
-
269
- &__user {
270
- padding: 10px;
271
- border-top: 1px solid var(--color-border-lighter);
272
- display: flex;
273
- align-items: center;
274
- justify-content: space-between;
275
- }
276
-
277
- &__user-info {
278
- display: flex;
279
- flex-direction: column;
280
- gap: 2px;
281
- }
282
-
283
- &__user-name {
284
- font-size: 14px;
285
- font-weight: 500;
286
- }
287
-
288
- &__user-role {
289
- font-size: 12px;
290
- color: var(--color-text-secondary);
291
- }
292
- }
293
-
294
- .menu-icon {
295
- margin-right: 8px;
296
- }
1
+ <script setup lang="ts">
2
+ /**
3
+ * 侧边栏组件
4
+ * 使用统一的 useMenu useApp hooks
5
+ */
6
+ import { useMenu } from '@/composables'
7
+ import { useApp } from '@/composables'
8
+ import { Menu, MenuItem, SubMenu } from '@xto/navigation'
9
+ import { Button } from '@xto/base'
10
+ import { Input } from '@xto/form'
11
+ import config from '@/config'
12
+
13
+ // 使用统一的 hooks
14
+ const { logout, userName, userInfo } = useApp()
15
+ const {
16
+ searchKeyword,
17
+ activeMenu,
18
+ isCollapsed,
19
+ searchResults,
20
+ filteredMenuList,
21
+ getMenuIcon,
22
+ handleMenuSelect,
23
+ handleSearchItemClick
24
+ } = useMenu()
25
+ </script>
26
+
27
+ <template>
28
+ <div class="sidebar">
29
+ <!-- Logo -->
30
+ <div class="sidebar__logo">
31
+ <img src="/vite.svg" alt="Logo" class="sidebar__logo-img" />
32
+ <span v-show="!isCollapsed" class="sidebar__logo-text">{{ config.webTitle }}</span>
33
+ </div>
34
+
35
+ <!-- 搜索框 -->
36
+ <div v-if="!isCollapsed" class="sidebar__search">
37
+ <Input
38
+ v-model="searchKeyword"
39
+ placeholder="搜索菜单..."
40
+ size="small"
41
+ clearable
42
+ />
43
+ <!-- 搜索结果 -->
44
+ <div v-if="searchResults.length > 0" class="sidebar__search-results">
45
+ <div
46
+ v-for="item in searchResults"
47
+ :key="item.path"
48
+ class="sidebar__search-item"
49
+ @click="handleSearchItemClick(item.path)"
50
+ >
51
+ <span class="menu-icon">{{ getMenuIcon(item.icon) }}</span>
52
+ <div class="sidebar__search-item-info">
53
+ <span class="sidebar__search-item-title">{{ item.title }}</span>
54
+ <span v-if="item.parentTitle" class="sidebar__search-item-parent">{{ item.parentTitle }}</span>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <!-- 菜单 -->
61
+ <Menu
62
+ :default-active="activeMenu"
63
+ :collapse="isCollapsed"
64
+ :collapse-transition="false"
65
+ class="sidebar__menu"
66
+ @select="handleMenuSelect"
67
+ >
68
+ <template v-for="menu in filteredMenuList" :key="menu.path">
69
+ <!-- 有子菜单 -->
70
+ <SubMenu v-if="menu.children && menu.children.length > 0" :index="menu.path">
71
+ <template #title>
72
+ <span class="menu-icon">{{ getMenuIcon(menu.icon) }}</span>
73
+ <span>{{ menu.title }}</span>
74
+ </template>
75
+ <MenuItem
76
+ v-for="child in menu.children"
77
+ :key="child.path"
78
+ :index="child.path"
79
+ >
80
+ <span class="menu-icon">{{ getMenuIcon(child.icon) }}</span>
81
+ <span>{{ child.title }}</span>
82
+ </MenuItem>
83
+ </SubMenu>
84
+ <!-- 无子菜单 -->
85
+ <MenuItem v-else :index="menu.path">
86
+ <span class="menu-icon">{{ getMenuIcon(menu.icon) }}</span>
87
+ <span>{{ menu.title }}</span>
88
+ </MenuItem>
89
+ </template>
90
+ </Menu>
91
+
92
+ <!-- 用户信息 -->
93
+ <div class="sidebar__user" v-if="!isCollapsed">
94
+ <div class="sidebar__user-info">
95
+ <span class="sidebar__user-name">{{ userName || '用户' }}</span>
96
+ <span class="sidebar__user-role">{{ userInfo?.departmentName || '' }}</span>
97
+ </div>
98
+ <Button type="text" size="small" @click="logout">退出</Button>
99
+ </div>
100
+ </div>
101
+ </template>
102
+
103
+ <style lang="scss" scoped>
104
+ .sidebar {
105
+ height: 100%;
106
+ display: flex;
107
+ flex-direction: column;
108
+ background-color: var(--bg-color);
109
+
110
+ &__logo {
111
+ height: 50px;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ gap: 10px;
116
+ border-bottom: 1px solid var(--color-border-lighter);
117
+ }
118
+
119
+ &__logo-img {
120
+ width: 32px;
121
+ height: 32px;
122
+ }
123
+
124
+ &__logo-text {
125
+ font-size: 16px;
126
+ font-weight: 600;
127
+ color: var(--color-primary);
128
+ }
129
+
130
+ &__search {
131
+ padding: 10px;
132
+ border-bottom: 1px solid var(--color-border-lighter);
133
+ position: relative;
134
+ }
135
+
136
+ &__search-results {
137
+ position: absolute;
138
+ top: 100%;
139
+ left: 0;
140
+ right: 0;
141
+ background-color: var(--bg-color);
142
+ border: 1px solid var(--color-border-lighter);
143
+ border-radius: var(--border-radius-base);
144
+ box-shadow: var(--box-shadow);
145
+ max-height: 300px;
146
+ overflow-y: auto;
147
+ z-index: 100;
148
+ }
149
+
150
+ &__search-item {
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 10px;
154
+ padding: 10px 12px;
155
+ cursor: pointer;
156
+ transition: background-color 0.2s;
157
+
158
+ &:hover {
159
+ background-color: var(--color-fill);
160
+ }
161
+
162
+ &-info {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: 2px;
166
+ }
167
+
168
+ &-title {
169
+ font-size: 14px;
170
+ color: var(--color-text-primary);
171
+ }
172
+
173
+ &-parent {
174
+ font-size: 12px;
175
+ color: var(--color-text-secondary);
176
+ }
177
+ }
178
+
179
+ &__menu {
180
+ flex: 1;
181
+ border-right: none;
182
+ overflow-y: auto;
183
+ }
184
+
185
+ &__user {
186
+ padding: 10px;
187
+ border-top: 1px solid var(--color-border-lighter);
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: space-between;
191
+ }
192
+
193
+ &__user-info {
194
+ display: flex;
195
+ flex-direction: column;
196
+ gap: 2px;
197
+ }
198
+
199
+ &__user-name {
200
+ font-size: 14px;
201
+ font-weight: 500;
202
+ }
203
+
204
+ &__user-role {
205
+ font-size: 12px;
206
+ color: var(--color-text-secondary);
207
+ }
208
+ }
209
+
210
+ .menu-icon {
211
+ margin-right: 8px;
212
+ }
297
213
  </style>
@@ -1,134 +1,20 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import { useRoute, useRouter } from 'vue-router'
4
- import { Tabs, TabPane } from '@xto/navigation'
5
-
6
- const route = useRoute()
7
- const router = useRouter()
8
-
9
- // 标签页列表
10
- const tabList = computed(() => {
11
- const tabs: { name: string; title: string; path: string; affix: boolean }[] = []
12
-
13
- // 添加固定标签
14
- route.matched.forEach(r => {
15
- if (r.meta?.affix) {
16
- tabs.push({
17
- name: r.name as string,
18
- title: r.meta.title as string,
19
- path: r.path,
20
- affix: true
21
- })
22
- }
23
- })
24
-
25
- return tabs
26
- })
27
-
28
- // 当前激活的标签
29
- const activeTab = computed(() => route.path)
30
-
31
- // 点击标签
32
- const handleClick = (path: string) => {
33
- router.push(path)
34
- }
35
-
36
- // 关闭标签
37
- const handleClose = (path: string) => {
38
- // 固定标签不能关闭
39
- const tab = tabList.value.find(t => t.path === path)
40
- if (tab?.affix) return
41
-
42
- // 如果关闭的是当前标签,跳转到前一个标签
43
- if (path === activeTab.value) {
44
- const index = tabList.value.findIndex(t => t.path === path)
45
- const nextTab = tabList.value[index - 1] || tabList.value[index + 1]
46
- if (nextTab) {
47
- router.push(nextTab.path)
48
- }
49
- }
50
- }
51
- </script>
52
-
53
- <template>
54
- <div class="tabs-wrapper">
55
- <Tabs v-model="activeTab" type="card" @tab-click="handleClick">
56
- <TabPane
57
- v-for="tab in tabList"
58
- :key="tab.path"
59
- :name="tab.path"
60
- :label="tab.title"
61
- :closable="!tab.affix"
62
- >
63
- <template #label>
64
- <span class="tab-label">
65
- {{ tab.title }}
66
- <span
67
- v-if="!tab.affix"
68
- class="tab-close"
69
- @click.stop="handleClose(tab.path)"
70
- >
71
-
72
- </span>
73
- </span>
74
- </template>
75
- </TabPane>
76
- </Tabs>
77
- </div>
78
- </template>
79
-
80
- <style lang="scss" scoped>
81
- .tabs-wrapper {
82
- width: 100%;
83
- height: 100%;
84
- padding: 0 10px;
85
-
86
- :deep(.t-tabs) {
87
- height: 100%;
88
- }
89
-
90
- :deep(.t-tabs__header) {
91
- margin: 0;
92
- border-bottom: none;
93
- }
94
-
95
- :deep(.t-tabs__nav) {
96
- border: none;
97
- }
98
-
99
- :deep(.t-tabs__item) {
100
- height: 32px;
101
- line-height: 32px;
102
- border: 1px solid var(--color-border-lighter);
103
- margin-right: 5px;
104
- border-radius: var(--border-radius-base);
105
- padding: 0 15px;
106
-
107
- &.is-active {
108
- background-color: var(--color-primary-light-9);
109
- border-color: var(--color-primary);
110
- }
111
- }
112
- }
113
-
114
- .tab-label {
115
- display: flex;
116
- align-items: center;
117
- gap: 5px;
118
- }
119
-
120
- .tab-close {
121
- width: 14px;
122
- height: 14px;
123
- display: flex;
124
- align-items: center;
125
- justify-content: center;
126
- font-size: 10px;
127
- border-radius: 50%;
128
-
129
- &:hover {
130
- background-color: var(--color-danger-light);
131
- color: var(--color-danger);
132
- }
133
- }
1
+ <script setup lang="ts">
2
+ </script>
3
+
4
+ <template>
5
+ <div class="tabs-wrapper">
6
+ <!-- 标签页功能后期自行开发 -->
7
+ </div>
8
+ </template>
9
+
10
+ <style lang="scss" scoped>
11
+ .tabs-wrapper {
12
+ width: 100%;
13
+ height: 100%;
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ color: var(--color-text-secondary);
18
+ font-size: 12px;
19
+ }
134
20
  </style>
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Composables 统一导出
3
+ */
4
+
5
+ export { useApp } from './useApp'
6
+ export { useAuth } from './useAuth'
7
+ export { useMenu } from './useMenu'
8
+ export { useForm } from './useForm'
9
+ export { useTable } from './useTable'