xto-fronted 0.1.0 → 0.1.1

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 (54) hide show
  1. package/.env.development +4 -0
  2. package/.env.production +4 -0
  3. package/index.html +13 -0
  4. package/package.json +18 -10
  5. package/public/vite.svg +10 -0
  6. package/src/App.vue +20 -0
  7. package/src/api/auth.ts +26 -0
  8. package/src/api/system.ts +65 -0
  9. package/src/api/user.ts +46 -0
  10. package/src/assets/styles/_dark.scss +407 -0
  11. package/src/assets/styles/_reset.scss +126 -0
  12. package/src/assets/styles/_root.scss +140 -0
  13. package/src/assets/styles/_transition.scss +119 -0
  14. package/src/assets/styles/_variables.scss +45 -0
  15. package/src/assets/styles/index.scss +187 -0
  16. package/src/components/Layout/Footer.vue +17 -0
  17. package/src/components/Layout/Header.vue +390 -0
  18. package/src/components/Layout/Sidebar.vue +297 -0
  19. package/src/components/Layout/Tabs.vue +134 -0
  20. package/src/components/Layout/index.vue +62 -0
  21. package/src/composables/useAuth.ts +45 -0
  22. package/src/composables/useForm.ts +79 -0
  23. package/src/composables/useTable.ts +97 -0
  24. package/src/directives/permission.ts +38 -0
  25. package/src/enums/index.ts +63 -0
  26. package/src/env.d.ts +17 -0
  27. package/src/index.ts +39 -0
  28. package/src/main.ts +34 -0
  29. package/src/router/dynamicRoutes.ts +163 -0
  30. package/src/router/index.ts +81 -0
  31. package/src/router/staticRoutes.ts +43 -0
  32. package/src/stores/app.ts +145 -0
  33. package/src/stores/auth.ts +32 -0
  34. package/src/stores/index.ts +15 -0
  35. package/src/stores/menu.ts +80 -0
  36. package/src/stores/user.ts +73 -0
  37. package/src/types/api.d.ts +84 -0
  38. package/src/types/global.d.ts +45 -0
  39. package/src/types/router.d.ts +48 -0
  40. package/src/types/xto.d.ts +149 -0
  41. package/src/utils/auth.ts +62 -0
  42. package/src/utils/permission.ts +42 -0
  43. package/src/utils/request.ts +126 -0
  44. package/src/utils/storage.ts +63 -0
  45. package/src/views/dashboard/index.vue +284 -0
  46. package/src/views/error/403.vue +57 -0
  47. package/src/views/error/404.vue +57 -0
  48. package/src/views/login/index.vue +248 -0
  49. package/src/views/system/menu/index.vue +381 -0
  50. package/src/views/system/role/index.vue +304 -0
  51. package/src/views/system/user/index.vue +327 -0
  52. package/tsconfig.json +26 -0
  53. package/tsconfig.node.json +11 -0
  54. package/vite.config.ts +139 -0
@@ -0,0 +1,4 @@
1
+ # 开发环境
2
+ VITE_APP_TITLE = Xto Demo - 开发环境
3
+ VITE_API_BASE_URL = /api
4
+ VITE_USE_MOCK = true
@@ -0,0 +1,4 @@
1
+ # 生产环境
2
+ VITE_APP_TITLE = Xto Demo
3
+ VITE_API_BASE_URL = /api
4
+ VITE_USE_MOCK = false
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Xto Demo - 后台管理系统</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.ts"></script>
12
+ </body>
13
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xto-fronted",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "XTO 前端应用框架",
@@ -16,8 +16,23 @@
16
16
  "./style.css": "./dist/style.css"
17
17
  },
18
18
  "files": [
19
- "dist"
19
+ "dist",
20
+ "src",
21
+ "public",
22
+ "index.html",
23
+ "vite.config.ts",
24
+ "tsconfig.json",
25
+ "tsconfig.node.json",
26
+ ".env.development",
27
+ ".env.production"
20
28
  ],
29
+ "scripts": {
30
+ "dev": "vite",
31
+ "build": "vue-tsc && vite build",
32
+ "build:lib": "vue-tsc && vite build --mode lib",
33
+ "preview": "vite preview",
34
+ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"
35
+ },
21
36
  "peerDependencies": {
22
37
  "axios": "^1.6.0",
23
38
  "pinia": "^2.1.0",
@@ -60,12 +75,5 @@
60
75
  "repository": {
61
76
  "type": "git",
62
77
  "url": ""
63
- },
64
- "scripts": {
65
- "dev": "vite",
66
- "build": "vue-tsc && vite build",
67
- "build:lib": "vue-tsc && vite build --mode lib",
68
- "preview": "vite preview",
69
- "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"
70
78
  }
71
- }
79
+ }
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256">
2
+ <defs>
3
+ <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#409eff;stop-opacity:1" />
5
+ <stop offset="100%" style="stop-color:#67c23a;stop-opacity:1" />
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="256" height="256" rx="32" fill="url(#gradient)"/>
9
+ <text x="128" y="160" text-anchor="middle" fill="white" font-size="120" font-weight="bold" font-family="Arial">X</text>
10
+ </svg>
package/src/App.vue ADDED
@@ -0,0 +1,20 @@
1
+ <script setup lang="ts">
2
+ import { useAppStore } from '@/stores/app'
3
+
4
+ const appStore = useAppStore()
5
+ </script>
6
+
7
+ <template>
8
+ <div class="app" :class="{ dark: appStore.isDark }">
9
+ <router-view />
10
+ </div>
11
+ </template>
12
+
13
+ <style lang="scss">
14
+ .app {
15
+ width: 100%;
16
+ height: 100%;
17
+ background-color: var(--bg-color-page);
18
+ transition: background-color 0.3s;
19
+ }
20
+ </style>
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 认证 API
3
+ */
4
+
5
+ import { http } from '@/utils/request'
6
+ import type { LoginParams, LoginResult, UserInfo } from '@/types/api'
7
+
8
+ // 登录
9
+ export function login(data: LoginParams) {
10
+ return http.post<LoginResult>('/auth/login', data)
11
+ }
12
+
13
+ // 登出
14
+ export function logout() {
15
+ return http.post('/auth/logout')
16
+ }
17
+
18
+ // 获取用户信息
19
+ export function getUserInfo() {
20
+ return http.get<UserInfo>('/user/info')
21
+ }
22
+
23
+ // 刷新 Token
24
+ export function refreshToken(refreshToken: string) {
25
+ return http.post<{ token: string; expireTime: number }>('/auth/refresh', { refreshToken })
26
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * 系统管理 API
3
+ */
4
+
5
+ import { http, type PageParams, type PageResponse } from '@/utils/request'
6
+ import type { RoleInfo, MenuItem } from '@/types/api'
7
+
8
+ // ========== 角色管理 ==========
9
+
10
+ // 获取角色列表
11
+ export function getRoleList(params: PageParams & { status?: number; keyword?: string }) {
12
+ return http.get<PageResponse<RoleInfo>>('/role/list', { params })
13
+ }
14
+
15
+ // 获取角色详情
16
+ export function getRoleDetail(id: string | number) {
17
+ return http.get<RoleInfo>(`/role/${id}`)
18
+ }
19
+
20
+ // 创建角色
21
+ export function createRole(data: Partial<RoleInfo>) {
22
+ return http.post<RoleInfo>('/role', data)
23
+ }
24
+
25
+ // 更新角色
26
+ export function updateRole(id: string | number, data: Partial<RoleInfo>) {
27
+ return http.put<RoleInfo>(`/role/${id}`, data)
28
+ }
29
+
30
+ // 删除角色
31
+ export function deleteRole(id: string | number) {
32
+ return http.delete(`/role/${id}`)
33
+ }
34
+
35
+ // 更新角色状态
36
+ export function updateRoleStatus(id: string | number, status: number) {
37
+ return http.patch(`/role/${id}/status`, { status })
38
+ }
39
+
40
+ // ========== 菜单管理 ==========
41
+
42
+ // 获取菜单列表
43
+ export function getMenuList() {
44
+ return http.get<MenuItem[]>('/menu/list')
45
+ }
46
+
47
+ // 获取菜单树
48
+ export function getMenuTree() {
49
+ return http.get<MenuItem[]>('/menu/tree')
50
+ }
51
+
52
+ // 创建菜单
53
+ export function createMenu(data: Partial<MenuItem>) {
54
+ return http.post<MenuItem>('/menu', data)
55
+ }
56
+
57
+ // 更新菜单
58
+ export function updateMenu(id: string | number, data: Partial<MenuItem>) {
59
+ return http.put<MenuItem>(`/menu/${id}`, data)
60
+ }
61
+
62
+ // 删除菜单
63
+ export function deleteMenu(id: string | number) {
64
+ return http.delete(`/menu/${id}`)
65
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 用户 API
3
+ */
4
+
5
+ import { http, type PageParams, type PageResponse } from '@/utils/request'
6
+ import type { UserInfo } from '@/types/api'
7
+
8
+ // 获取用户列表
9
+ export function getUserList(params: PageParams & { status?: number; keyword?: string }) {
10
+ return http.get<PageResponse<UserInfo>>('/user/list', { params })
11
+ }
12
+
13
+ // 获取用户详情
14
+ export function getUserDetail(id: string | number) {
15
+ return http.get<UserInfo>(`/user/${id}`)
16
+ }
17
+
18
+ // 创建用户
19
+ export function createUser(data: Partial<UserInfo>) {
20
+ return http.post<UserInfo>('/user', data)
21
+ }
22
+
23
+ // 更新用户
24
+ export function updateUser(id: string | number, data: Partial<UserInfo>) {
25
+ return http.put<UserInfo>(`/user/${id}`, data)
26
+ }
27
+
28
+ // 删除用户
29
+ export function deleteUser(id: string | number) {
30
+ return http.delete(`/user/${id}`)
31
+ }
32
+
33
+ // 批量删除用户
34
+ export function batchDeleteUsers(ids: (string | number)[]) {
35
+ return http.post('/user/batch-delete', { ids })
36
+ }
37
+
38
+ // 更新用户状态
39
+ export function updateUserStatus(id: string | number, status: number) {
40
+ return http.patch(`/user/${id}/status`, { status })
41
+ }
42
+
43
+ // 重置密码
44
+ export function resetPassword(id: string | number) {
45
+ return http.post(`/user/${id}/reset-password`)
46
+ }
@@ -0,0 +1,407 @@
1
+ // ==============================================
2
+ // Dark Theme Override - 暗黑主题覆盖
3
+ // ==============================================
4
+
5
+ .dark {
6
+ // 侧边栏
7
+ .sidebar,
8
+ .layout__aside {
9
+ background-color: #1d1e1f;
10
+ }
11
+
12
+ .sidebar {
13
+ border-right: 1px solid #363637;
14
+ }
15
+
16
+ .sidebar__search-results {
17
+ background-color: #1d1e1f;
18
+ border-color: #363637;
19
+ }
20
+
21
+ .sidebar__search-item:hover {
22
+ background-color: #262727;
23
+ }
24
+
25
+ .sidebar__search-item-title {
26
+ color: #cfd3dc;
27
+ }
28
+
29
+ .sidebar__search-item-parent {
30
+ color: #a3a6ad;
31
+ }
32
+
33
+ .sidebar__logo {
34
+ border-bottom-color: #363637;
35
+ }
36
+
37
+ .sidebar__user {
38
+ border-top-color: #363637;
39
+ }
40
+
41
+ .sidebar__user-name {
42
+ color: #cfd3dc;
43
+ }
44
+
45
+ .sidebar__user-role {
46
+ color: #a3a6ad;
47
+ }
48
+
49
+ // 菜单
50
+ .x-menu {
51
+ background-color: #1d1e1f !important;
52
+ border-color: #363637;
53
+ color: #cfd3dc;
54
+
55
+ &.x-menu--vertical {
56
+ border-right-color: #363637;
57
+ }
58
+
59
+ &.x-menu--horizontal {
60
+ border-bottom-color: #363637;
61
+ }
62
+ }
63
+
64
+ .x-menu-item {
65
+ color: #cfd3dc !important;
66
+
67
+ &:hover {
68
+ background-color: #262727 !important;
69
+ }
70
+
71
+ &.is-active {
72
+ color: #409eff !important;
73
+ background-color: rgba(64, 158, 255, 0.1) !important;
74
+
75
+ &::before {
76
+ background-color: #409eff;
77
+ }
78
+ }
79
+ }
80
+
81
+ .x-sub-menu {
82
+ &__title {
83
+ color: #cfd3dc !important;
84
+
85
+ &:hover {
86
+ background-color: #262727 !important;
87
+ }
88
+ }
89
+
90
+ &__menu {
91
+ background-color: #1d1e1f !important;
92
+ border: 1px solid #363637;
93
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
94
+ }
95
+
96
+ &__arrow {
97
+ color: #a3a6ad;
98
+ }
99
+
100
+ &.is-opened > .x-sub-menu__title {
101
+ background-color: #262727 !important;
102
+ }
103
+
104
+ &.is-active > .x-sub-menu__title {
105
+ color: #409eff !important;
106
+ }
107
+ }
108
+
109
+ // 顶部导航
110
+ .header {
111
+ background-color: #1d1e1f;
112
+ border-bottom-color: #363637;
113
+ }
114
+
115
+ .header__collapse {
116
+ color: #cfd3dc;
117
+ }
118
+
119
+ .header__breadcrumb {
120
+ color: #a3a6ad;
121
+ }
122
+
123
+ .header__user-name {
124
+ color: #cfd3dc;
125
+ }
126
+
127
+ .header__user-arrow {
128
+ color: #a3a6ad;
129
+ }
130
+
131
+ .header__action:hover {
132
+ background-color: #262727;
133
+ }
134
+
135
+ .header__user-trigger:hover {
136
+ background-color: #262727;
137
+ }
138
+
139
+ // 下拉菜单
140
+ .header__dropdown {
141
+ background-color: #1d1e1f;
142
+ border: 1px solid #363637;
143
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
144
+ }
145
+
146
+ .header__dropdown-name {
147
+ color: #e5eaf3;
148
+ }
149
+
150
+ .header__dropdown-role {
151
+ color: #a3a6ad;
152
+ }
153
+
154
+ .header__dropdown-divider {
155
+ background-color: #363637;
156
+ }
157
+
158
+ .header__dropdown-item {
159
+ color: #cfd3dc;
160
+
161
+ &:hover {
162
+ background-color: #262727;
163
+ color: #e5eaf3;
164
+ }
165
+ }
166
+
167
+ // 按钮
168
+ .x-button {
169
+ &.x-button--default {
170
+ background-color: #1d1e1f;
171
+ border-color: #4c4d4f;
172
+ color: #cfd3dc;
173
+
174
+ &:hover {
175
+ border-color: #409eff;
176
+ color: #409eff;
177
+ }
178
+ }
179
+
180
+ &.x-button--text {
181
+ color: #409eff;
182
+ }
183
+ }
184
+
185
+ // 表单
186
+ .x-form-item__label {
187
+ color: #cfd3dc;
188
+ }
189
+
190
+ .x-form-item__error {
191
+ color: #f56c6c;
192
+ }
193
+
194
+ // 输入框
195
+ .x-input {
196
+ background-color: #1d1e1f;
197
+ border-color: #4c4d4f;
198
+
199
+ &.is-focus {
200
+ border-color: #409eff;
201
+ }
202
+
203
+ .x-input__inner {
204
+ background-color: transparent;
205
+ color: #e5eaf3;
206
+
207
+ &::placeholder {
208
+ color: #6c6e72;
209
+ }
210
+ }
211
+
212
+ .x-input__suffix,
213
+ .x-input__prefix {
214
+ color: #a3a6ad;
215
+ }
216
+ }
217
+
218
+ // 选择器
219
+ .x-select {
220
+ background-color: #1d1e1f;
221
+ border-color: #4c4d4f;
222
+ }
223
+
224
+ // 卡片
225
+ .x-card {
226
+ background-color: #1d1e1f;
227
+ border-color: #363637;
228
+ }
229
+
230
+ .x-card__header {
231
+ border-bottom-color: #363637;
232
+ color: #e5eaf3;
233
+ }
234
+
235
+ // 表格
236
+ .data-table {
237
+ th {
238
+ background-color: #262727;
239
+ color: #cfd3dc;
240
+ border-bottom-color: #363637;
241
+ }
242
+
243
+ td {
244
+ border-bottom-color: #363637;
245
+ color: #cfd3dc;
246
+ }
247
+
248
+ tr:hover td {
249
+ background-color: #262727;
250
+ }
251
+ }
252
+
253
+ // 弹窗
254
+ .x-modal {
255
+ &__wrapper {
256
+ background-color: rgba(0, 0, 0, 0.7);
257
+ }
258
+
259
+ &__container {
260
+ background-color: #1d1e1f;
261
+ border: 1px solid #363637;
262
+ }
263
+
264
+ &__header {
265
+ border-bottom-color: #363637;
266
+ color: #e5eaf3;
267
+ }
268
+
269
+ &__footer {
270
+ border-top-color: #363637;
271
+ }
272
+ }
273
+
274
+ // 消息提示
275
+ .x-message {
276
+ background-color: #1d1e1f;
277
+ border: 1px solid #363637;
278
+ }
279
+
280
+ // 标签
281
+ .x-tag {
282
+ &.x-tag--primary {
283
+ background-color: rgba(64, 158, 255, 0.2);
284
+ color: #79bbff;
285
+ border-color: rgba(64, 158, 255, 0.3);
286
+ }
287
+
288
+ &.x-tag--success {
289
+ background-color: rgba(103, 194, 58, 0.2);
290
+ color: #95d475;
291
+ border-color: rgba(103, 194, 58, 0.3);
292
+ }
293
+
294
+ &.x-tag--warning {
295
+ background-color: rgba(230, 162, 60, 0.2);
296
+ color: #eebe77;
297
+ border-color: rgba(230, 162, 60, 0.3);
298
+ }
299
+
300
+ &.x-tag--danger {
301
+ background-color: rgba(245, 108, 108, 0.2);
302
+ color: #fab6b6;
303
+ border-color: rgba(245, 108, 108, 0.3);
304
+ }
305
+
306
+ &.x-tag--info {
307
+ background-color: rgba(144, 147, 153, 0.2);
308
+ color: #c0c4cc;
309
+ border-color: rgba(144, 147, 153, 0.3);
310
+ }
311
+ }
312
+
313
+ // 开关
314
+ .x-switch {
315
+ background-color: #4c4d4f;
316
+
317
+ &.is-checked {
318
+ background-color: #409eff;
319
+ }
320
+ }
321
+
322
+ // 分页
323
+ .x-pagination {
324
+ color: #cfd3dc;
325
+
326
+ &__item {
327
+ background-color: #1d1e1f;
328
+ border-color: #4c4d4f;
329
+ color: #cfd3dc;
330
+
331
+ &:hover {
332
+ color: #409eff;
333
+ }
334
+
335
+ &.is-active {
336
+ background-color: #409eff;
337
+ border-color: #409eff;
338
+ color: #fff;
339
+ }
340
+ }
341
+ }
342
+
343
+ // 登录页面
344
+ .login {
345
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
346
+ }
347
+
348
+ .login__container {
349
+ background-color: #1d1e1f;
350
+ border: 1px solid #363637;
351
+ }
352
+
353
+ .login__title {
354
+ color: #409eff;
355
+ }
356
+
357
+ .login__subtitle {
358
+ color: #a3a6ad;
359
+ }
360
+
361
+ .login__footer {
362
+ color: #6c6e72;
363
+ }
364
+
365
+ // 仪表盘
366
+ .stat-card__title {
367
+ color: #a3a6ad;
368
+ }
369
+
370
+ .quick-link {
371
+ background-color: #262727;
372
+
373
+ &:hover {
374
+ background-color: rgba(64, 158, 255, 0.1);
375
+ }
376
+
377
+ &__title {
378
+ color: #cfd3dc;
379
+ }
380
+ }
381
+
382
+ .activity-item {
383
+ border-bottom-color: #363637;
384
+
385
+ &__action {
386
+ color: #cfd3dc;
387
+ }
388
+ }
389
+
390
+ // 搜索卡片
391
+ .search-card {
392
+ background-color: #1d1e1f;
393
+ border: 1px solid #363637;
394
+ }
395
+
396
+ // 表格卡片
397
+ .table-card {
398
+ background-color: #1d1e1f;
399
+ border: 1px solid #363637;
400
+ }
401
+
402
+ // Popconfirm
403
+ .x-popconfirm {
404
+ background-color: #1d1e1f;
405
+ border: 1px solid #363637;
406
+ }
407
+ }