xto-fronted 0.4.7 → 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 (75) 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.html +28 -0
  51. package/package.json +85 -85
  52. package/src/api/auth.ts +25 -25
  53. package/src/api/system.ts +66 -66
  54. package/src/assets/styles/_dark.scss +406 -406
  55. package/src/components/Layout/Header.vue +973 -973
  56. package/src/components/Layout/Sidebar.vue +273 -273
  57. package/src/components/Layout/index.vue +443 -63
  58. package/src/composables/useApp.ts +61 -61
  59. package/src/composables/useAuth.ts +16 -16
  60. package/src/directives/permission.ts +27 -27
  61. package/src/env.d.ts +18 -18
  62. package/src/index.ts +47 -47
  63. package/src/router/dynamicRoutes.ts +162 -162
  64. package/src/router/guards.ts +128 -128
  65. package/src/router/index.ts +79 -79
  66. package/src/stores/auth.ts +65 -65
  67. package/src/stores/menu.ts +48 -48
  68. package/src/stores/user.ts +50 -50
  69. package/src/types/api.d.ts +80 -80
  70. package/src/utils/auth.ts +99 -99
  71. package/src/utils/config.ts +80 -80
  72. package/src/utils/permission.ts +32 -32
  73. package/src/utils/request.ts +124 -124
  74. package/src/views/login/index.vue +194 -194
  75. package/vite.config.ts +135 -135
@@ -1,274 +1,274 @@
1
- <script setup lang="ts">
2
- import { 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, Icon } from '@xto/base'
10
-
11
- const route = useRoute()
12
- const router = useRouter()
13
- const menuStore = useMenuStore()
14
- const userStore = useUserStore()
15
- const authStore = useAuthStore()
16
- const appStore = useAppStore()
17
-
18
- const isCollapsed = computed(() => appStore.isCollapsed)
19
- const activeMenu = computed(() => route.path)
20
-
21
- // 菜单主题相关
22
- const menuBgColor = computed(() => appStore.isDark ? '#1d1e1f' : '#fff')
23
- const menuTextColor = computed(() => appStore.isDark ? '#cfd3dc' : '#303133')
24
- const menuActiveTextColor = computed(() => '#409eff')
25
-
26
- // 菜单选择
27
- const handleMenuSelect = (index: string) => {
28
- if (index && index !== route.path) {
29
- router.push(index)
30
- }
31
- }
32
-
33
- // 退出登录
34
- const handleLogout = () => {
35
- authStore.logout()
36
- userStore.clearUserInfo()
37
- menuStore.clearMenu()
38
- router.push('/login')
39
- }
40
-
41
- // 已知的图标名称列表(来自 @xto/base/icons.ts)
42
- const knownIcons = new Set([
43
- 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right',
44
- 'caret-down', 'caret-right', 'plus', 'minus', 'close', 'check',
45
- 'edit', 'delete', 'copy', 'download', 'upload', 'refresh', 'search',
46
- 'filter', 'more', 'setting', 'share', 'loading', 'info', 'success',
47
- 'warning', 'error', 'question', 'user', 'user-add', 'user-group',
48
- 'logout', 'login', 'file', 'folder', 'folder-open', 'document',
49
- 'image', 'video', 'music', 'camera', 'mail', 'phone', 'chat',
50
- 'bell', 'message', 'eye', 'eye-off', 'calendar', 'clock', 'history',
51
- 'timer', 'location', 'map', 'globe', 'star', 'heart', 'thumb-up',
52
- 'link', 'external-link', 'lock', 'unlock', 'key', 'home', 'menu',
53
- 'menu-fold', 'menu-unfold', 'sidebar-fold', 'sidebar-expand',
54
- 'sidebar-left', 'dashboard', 'chart', 'chart-pie', 'chart-line',
55
- 'report', 'analytics', 'system', 'permission', 'role', 'user-manage',
56
- 'log', 'notification', 'app', 'list', 'grid', 'fullscreen',
57
- 'fullscreen-exit', 'zoom-in', 'zoom-out', 'print', 'bookmark',
58
- 'tag', 'code', 'terminal', 'database', 'server', 'cloud', 'gift',
59
- 'moon', 'sun', 'theme', 'skin'
60
- ])
61
-
62
- // 获取菜单图标名称
63
- const getMenuIcon = (icon?: string): string => {
64
- // 无图标时返回空
65
- if (!icon || icon === '') return ''
66
-
67
- // 处理 tineco-ui 的图标类名(如 tineco-icon-home)
68
- if (icon.startsWith('tineco-icon-')) {
69
- const iconName = icon.replace('tineco-icon-', '')
70
- // 常见的 tineco icon 映射
71
- const tinecoIconMap: Record<string, string> = {
72
- home: 'home',
73
- dashboard: 'dashboard',
74
- system: 'system',
75
- user: 'user',
76
- role: 'role',
77
- menu: 'list',
78
- setting: 'setting',
79
- file: 'file',
80
- folder: 'folder',
81
- chart: 'chart',
82
- report: 'report',
83
- analytics: 'analytics'
84
- }
85
- return tinecoIconMap[iconName] || iconName
86
- }
87
-
88
- // 常见业务图标映射
89
- const iconMap: Record<string, string> = {
90
- dashboard: 'dashboard',
91
- system: 'system',
92
- user: 'user',
93
- role: 'role',
94
- menu: 'list',
95
- setting: 'setting',
96
- home: 'home',
97
- chart: 'chart',
98
- report: 'report',
99
- analytics: 'analytics',
100
- permission: 'permission',
101
- log: 'log',
102
- notification: 'notification',
103
- app: 'app',
104
- list: 'list',
105
- grid: 'grid'
106
- }
107
-
108
- return iconMap[icon] || icon
109
- }
110
-
111
- // 获取菜单名称第一个字
112
- const getFirstChar = (name?: string): string => {
113
- if (!name) return ''
114
- return name.charAt(0)
115
- }
116
-
117
- // 判断图标是否存在
118
- const iconExists = (iconName: string): boolean => {
119
- return knownIcons.has(iconName)
120
- }
121
- </script>
122
-
123
- <template>
124
- <div class="sidebar" :class="{ 'sidebar--collapsed': isCollapsed }">
125
- <!-- Logo -->
126
- <div class="sidebar__logo">
127
- <img src="/vite.svg" alt="Logo" class="sidebar__logo-img" />
128
- <span v-show="!isCollapsed" class="sidebar__logo-text">{{ appStore.appName }}</span>
129
- </div>
130
-
131
- <!-- 菜单 -->
132
- <Menu
133
- :default-active="activeMenu"
134
- mode="vertical"
135
- :collapse="isCollapsed"
136
- :collapse-transition="false"
137
- :background-color="menuBgColor"
138
- :text-color="menuTextColor"
139
- :active-text-color="menuActiveTextColor"
140
- class="sidebar__menu"
141
- @select="handleMenuSelect"
142
- >
143
- <template v-for="menu in menuStore.menuList" :key="menu.menuUrl">
144
- <!-- 有子菜单 -->
145
- <SubMenu v-if="menu.children && menu.children.length > 0" :index="menu.menuUrl">
146
- <template #title>
147
- <span class="sidebar__menu-icon">
148
- <Icon v-if="iconExists(getMenuIcon(menu.icon))" :name="getMenuIcon(menu.icon)" :size="16" />
149
- <span v-else class="sidebar__menu-char">{{ getFirstChar(menu.menuName) }}</span>
150
- </span>
151
- <span>{{ menu.menuName }}</span>
152
- </template>
153
- <MenuItem
154
- v-for="child in menu.children"
155
- :key="child.menuUrl"
156
- :index="child.menuUrl"
157
- >
158
- <span class="sidebar__menu-icon">
159
- <Icon v-if="iconExists(getMenuIcon(child.icon))" :name="getMenuIcon(child.icon)" :size="16" />
160
- <span v-else class="sidebar__menu-char">{{ getFirstChar(child.menuName) }}</span>
161
- </span>
162
- <span>{{ child.menuName }}</span>
163
- </MenuItem>
164
- </SubMenu>
165
- <!-- 无子菜单 -->
166
- <MenuItem v-else :index="menu.menuUrl">
167
- <span class="sidebar__menu-icon">
168
- <Icon v-if="iconExists(getMenuIcon(menu.icon))" :name="getMenuIcon(menu.icon)" :size="16" />
169
- <span v-else class="sidebar__menu-char">{{ getFirstChar(menu.menuName) }}</span>
170
- </span>
171
- <span>{{ menu.menuName }}</span>
172
- </MenuItem>
173
- </template>
174
- </Menu>
175
-
176
- <!-- 用户信息 -->
177
- <div v-if="!isCollapsed" class="sidebar__user">
178
- <div class="sidebar__user-info">
179
- <span class="sidebar__user-name">{{ userStore.userName }}</span>
180
- <span class="sidebar__user-role">{{ userStore.departmentName }}</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
- border-right: 1px solid var(--color-border-lighter);
194
-
195
- &--collapsed {
196
- .sidebar__logo {
197
- justify-content: center;
198
- padding: 0;
199
- }
200
- }
201
-
202
- &__logo {
203
- height: 50px;
204
- display: flex;
205
- align-items: center;
206
- padding: 0 20px;
207
- gap: 10px;
208
- border-bottom: 1px solid var(--color-border-lighter);
209
- }
210
-
211
- &__logo-img {
212
- width: 32px;
213
- height: 32px;
214
- }
215
-
216
- &__logo-text {
217
- font-size: 16px;
218
- font-weight: 600;
219
- color: var(--color-primary);
220
- }
221
-
222
- &__menu {
223
- flex: 1;
224
- border-right: none;
225
- overflow-y: auto;
226
- }
227
-
228
- &__menu-icon {
229
- display: inline-flex;
230
- align-items: center;
231
- justify-content: center;
232
- width: 16px;
233
- height: 16px;
234
- margin-right: 8px;
235
- }
236
-
237
- &__menu-char {
238
- display: inline-flex;
239
- align-items: center;
240
- justify-content: center;
241
- width: 16px;
242
- height: 16px;
243
- font-size: 12px;
244
- font-weight: 600;
245
- color: var(--color-primary);
246
- background-color: var(--color-primary-light-8);
247
- border-radius: 4px;
248
- }
249
-
250
- &__user {
251
- padding: 10px;
252
- border-top: 1px solid var(--color-border-lighter);
253
- display: flex;
254
- align-items: center;
255
- justify-content: space-between;
256
- }
257
-
258
- &__user-info {
259
- display: flex;
260
- flex-direction: column;
261
- gap: 2px;
262
- }
263
-
264
- &__user-name {
265
- font-size: 14px;
266
- font-weight: 500;
267
- }
268
-
269
- &__user-role {
270
- font-size: 12px;
271
- color: var(--color-text-secondary);
272
- }
273
- }
1
+ <script setup lang="ts">
2
+ import { 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, Icon } from '@xto/base'
10
+
11
+ const route = useRoute()
12
+ const router = useRouter()
13
+ const menuStore = useMenuStore()
14
+ const userStore = useUserStore()
15
+ const authStore = useAuthStore()
16
+ const appStore = useAppStore()
17
+
18
+ const isCollapsed = computed(() => appStore.isCollapsed)
19
+ const activeMenu = computed(() => route.path)
20
+
21
+ // 菜单主题相关
22
+ const menuBgColor = computed(() => appStore.isDark ? '#1d1e1f' : '#fff')
23
+ const menuTextColor = computed(() => appStore.isDark ? '#cfd3dc' : '#303133')
24
+ const menuActiveTextColor = computed(() => '#409eff')
25
+
26
+ // 菜单选择
27
+ const handleMenuSelect = (index: string) => {
28
+ if (index && index !== route.path) {
29
+ router.push(index)
30
+ }
31
+ }
32
+
33
+ // 退出登录
34
+ const handleLogout = () => {
35
+ authStore.logout()
36
+ userStore.clearUserInfo()
37
+ menuStore.clearMenu()
38
+ router.push('/login')
39
+ }
40
+
41
+ // 已知的图标名称列表(来自 @xto/base/icons.ts)
42
+ const knownIcons = new Set([
43
+ 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right',
44
+ 'caret-down', 'caret-right', 'plus', 'minus', 'close', 'check',
45
+ 'edit', 'delete', 'copy', 'download', 'upload', 'refresh', 'search',
46
+ 'filter', 'more', 'setting', 'share', 'loading', 'info', 'success',
47
+ 'warning', 'error', 'question', 'user', 'user-add', 'user-group',
48
+ 'logout', 'login', 'file', 'folder', 'folder-open', 'document',
49
+ 'image', 'video', 'music', 'camera', 'mail', 'phone', 'chat',
50
+ 'bell', 'message', 'eye', 'eye-off', 'calendar', 'clock', 'history',
51
+ 'timer', 'location', 'map', 'globe', 'star', 'heart', 'thumb-up',
52
+ 'link', 'external-link', 'lock', 'unlock', 'key', 'home', 'menu',
53
+ 'menu-fold', 'menu-unfold', 'sidebar-fold', 'sidebar-expand',
54
+ 'sidebar-left', 'dashboard', 'chart', 'chart-pie', 'chart-line',
55
+ 'report', 'analytics', 'system', 'permission', 'role', 'user-manage',
56
+ 'log', 'notification', 'app', 'list', 'grid', 'fullscreen',
57
+ 'fullscreen-exit', 'zoom-in', 'zoom-out', 'print', 'bookmark',
58
+ 'tag', 'code', 'terminal', 'database', 'server', 'cloud', 'gift',
59
+ 'moon', 'sun', 'theme', 'skin'
60
+ ])
61
+
62
+ // 获取菜单图标名称
63
+ const getMenuIcon = (icon?: string): string => {
64
+ // 无图标时返回空
65
+ if (!icon || icon === '') return ''
66
+
67
+ // 处理 tineco-ui 的图标类名(如 tineco-icon-home)
68
+ if (icon.startsWith('tineco-icon-')) {
69
+ const iconName = icon.replace('tineco-icon-', '')
70
+ // 常见的 tineco icon 映射
71
+ const tinecoIconMap: Record<string, string> = {
72
+ home: 'home',
73
+ dashboard: 'dashboard',
74
+ system: 'system',
75
+ user: 'user',
76
+ role: 'role',
77
+ menu: 'list',
78
+ setting: 'setting',
79
+ file: 'file',
80
+ folder: 'folder',
81
+ chart: 'chart',
82
+ report: 'report',
83
+ analytics: 'analytics'
84
+ }
85
+ return tinecoIconMap[iconName] || iconName
86
+ }
87
+
88
+ // 常见业务图标映射
89
+ const iconMap: Record<string, string> = {
90
+ dashboard: 'dashboard',
91
+ system: 'system',
92
+ user: 'user',
93
+ role: 'role',
94
+ menu: 'list',
95
+ setting: 'setting',
96
+ home: 'home',
97
+ chart: 'chart',
98
+ report: 'report',
99
+ analytics: 'analytics',
100
+ permission: 'permission',
101
+ log: 'log',
102
+ notification: 'notification',
103
+ app: 'app',
104
+ list: 'list',
105
+ grid: 'grid'
106
+ }
107
+
108
+ return iconMap[icon] || icon
109
+ }
110
+
111
+ // 获取菜单名称第一个字
112
+ const getFirstChar = (name?: string): string => {
113
+ if (!name) return ''
114
+ return name.charAt(0)
115
+ }
116
+
117
+ // 判断图标是否存在
118
+ const iconExists = (iconName: string): boolean => {
119
+ return knownIcons.has(iconName)
120
+ }
121
+ </script>
122
+
123
+ <template>
124
+ <div class="sidebar" :class="{ 'sidebar--collapsed': isCollapsed }">
125
+ <!-- Logo -->
126
+ <div class="sidebar__logo">
127
+ <img src="/vite.svg" alt="Logo" class="sidebar__logo-img" />
128
+ <span v-show="!isCollapsed" class="sidebar__logo-text">{{ appStore.appName }}</span>
129
+ </div>
130
+
131
+ <!-- 菜单 -->
132
+ <Menu
133
+ :default-active="activeMenu"
134
+ mode="vertical"
135
+ :collapse="isCollapsed"
136
+ :collapse-transition="false"
137
+ :background-color="menuBgColor"
138
+ :text-color="menuTextColor"
139
+ :active-text-color="menuActiveTextColor"
140
+ class="sidebar__menu"
141
+ @select="handleMenuSelect"
142
+ >
143
+ <template v-for="menu in menuStore.menuList" :key="menu.menuUrl">
144
+ <!-- 有子菜单 -->
145
+ <SubMenu v-if="menu.children && menu.children.length > 0" :index="menu.menuUrl">
146
+ <template #title>
147
+ <span class="sidebar__menu-icon">
148
+ <Icon v-if="iconExists(getMenuIcon(menu.icon))" :name="getMenuIcon(menu.icon)" :size="16" />
149
+ <span v-else class="sidebar__menu-char">{{ getFirstChar(menu.menuName) }}</span>
150
+ </span>
151
+ <span>{{ menu.menuName }}</span>
152
+ </template>
153
+ <MenuItem
154
+ v-for="child in menu.children"
155
+ :key="child.menuUrl"
156
+ :index="child.menuUrl"
157
+ >
158
+ <span class="sidebar__menu-icon">
159
+ <Icon v-if="iconExists(getMenuIcon(child.icon))" :name="getMenuIcon(child.icon)" :size="16" />
160
+ <span v-else class="sidebar__menu-char">{{ getFirstChar(child.menuName) }}</span>
161
+ </span>
162
+ <span>{{ child.menuName }}</span>
163
+ </MenuItem>
164
+ </SubMenu>
165
+ <!-- 无子菜单 -->
166
+ <MenuItem v-else :index="menu.menuUrl">
167
+ <span class="sidebar__menu-icon">
168
+ <Icon v-if="iconExists(getMenuIcon(menu.icon))" :name="getMenuIcon(menu.icon)" :size="16" />
169
+ <span v-else class="sidebar__menu-char">{{ getFirstChar(menu.menuName) }}</span>
170
+ </span>
171
+ <span>{{ menu.menuName }}</span>
172
+ </MenuItem>
173
+ </template>
174
+ </Menu>
175
+
176
+ <!-- 用户信息 -->
177
+ <div v-if="!isCollapsed" class="sidebar__user">
178
+ <div class="sidebar__user-info">
179
+ <span class="sidebar__user-name">{{ userStore.userName }}</span>
180
+ <span class="sidebar__user-role">{{ userStore.departmentName }}</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
+ border-right: 1px solid var(--color-border-lighter);
194
+
195
+ &--collapsed {
196
+ .sidebar__logo {
197
+ justify-content: center;
198
+ padding: 0;
199
+ }
200
+ }
201
+
202
+ &__logo {
203
+ height: 50px;
204
+ display: flex;
205
+ align-items: center;
206
+ padding: 0 20px;
207
+ gap: 10px;
208
+ border-bottom: 1px solid var(--color-border-lighter);
209
+ }
210
+
211
+ &__logo-img {
212
+ width: 32px;
213
+ height: 32px;
214
+ }
215
+
216
+ &__logo-text {
217
+ font-size: 16px;
218
+ font-weight: 600;
219
+ color: var(--color-primary);
220
+ }
221
+
222
+ &__menu {
223
+ flex: 1;
224
+ border-right: none;
225
+ overflow-y: auto;
226
+ }
227
+
228
+ &__menu-icon {
229
+ display: inline-flex;
230
+ align-items: center;
231
+ justify-content: center;
232
+ width: 16px;
233
+ height: 16px;
234
+ margin-right: 8px;
235
+ }
236
+
237
+ &__menu-char {
238
+ display: inline-flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ width: 16px;
242
+ height: 16px;
243
+ font-size: 12px;
244
+ font-weight: 600;
245
+ color: var(--color-primary);
246
+ background-color: var(--color-primary-light-8);
247
+ border-radius: 4px;
248
+ }
249
+
250
+ &__user {
251
+ padding: 10px;
252
+ border-top: 1px solid var(--color-border-lighter);
253
+ display: flex;
254
+ align-items: center;
255
+ justify-content: space-between;
256
+ }
257
+
258
+ &__user-info {
259
+ display: flex;
260
+ flex-direction: column;
261
+ gap: 2px;
262
+ }
263
+
264
+ &__user-name {
265
+ font-size: 14px;
266
+ font-weight: 500;
267
+ }
268
+
269
+ &__user-role {
270
+ font-size: 12px;
271
+ color: var(--color-text-secondary);
272
+ }
273
+ }
274
274
  </style>