xto-fronted 0.2.5 → 0.2.7

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 (107) hide show
  1. package/.env.development +4 -0
  2. package/.env.production +4 -0
  3. package/README.md +94 -196
  4. package/dist/{components/Layout/TopMenu.vue.d.ts → App.vue.d.ts} +1 -1
  5. package/dist/api/auth.d.ts +8 -10
  6. package/dist/api/system.d.ts +11 -12
  7. package/dist/api/user.d.ts +12 -3
  8. package/dist/components/Layout/Footer.vue.d.ts +1 -1
  9. package/dist/components/Layout/Header.vue.d.ts +3 -14
  10. package/dist/components/Layout/Sidebar.vue.d.ts +1 -1
  11. package/dist/components/Layout/Tabs.vue.d.ts +1 -1
  12. package/dist/components/Layout/index.vue.d.ts +1 -1
  13. package/dist/composables/useAuth.d.ts +4 -19
  14. package/dist/directives/permission.d.ts +0 -1
  15. package/dist/index-CWRs4WMN.js +372 -0
  16. package/dist/index-CpxpXTQX.js +1462 -0
  17. package/dist/index-Cu3Z2-PY.js +345 -0
  18. package/dist/index-DPEVEyik.js +475 -0
  19. package/dist/index-DYnXaqYf.js +142 -0
  20. package/dist/index.d.ts +12 -25
  21. package/dist/index.es.js +76 -1521
  22. package/dist/index.umd.js +1 -20
  23. package/dist/main.d.ts +0 -1
  24. package/dist/router/dynamicRoutes.d.ts +33 -17
  25. package/dist/router/index.d.ts +4 -26
  26. package/dist/router/layoutRoute.d.ts +18 -0
  27. package/dist/router/staticRoutes.d.ts +2 -18
  28. package/dist/setup.d.ts +17 -0
  29. package/dist/stores/app.d.ts +15 -9
  30. package/dist/stores/auth.d.ts +48 -62
  31. package/dist/stores/index.d.ts +3 -1
  32. package/dist/stores/menu.d.ts +29 -47
  33. package/dist/stores/user.d.ts +84 -64
  34. package/dist/style.css +1 -1
  35. package/dist/utils/auth.d.ts +10 -10
  36. package/dist/utils/permission.d.ts +10 -1
  37. package/dist/utils/request.d.ts +7 -23
  38. package/dist/{components/Layout/Breadcrumb.vue.d.ts → views/dashboard/index.vue.d.ts} +1 -1
  39. package/dist/{components/Error → views/error}/403.vue.d.ts +1 -1
  40. package/dist/{components/Error → views/error}/404.vue.d.ts +1 -1
  41. package/dist/views/login/index.vue.d.ts +4 -0
  42. package/dist/views/system/menu/index.vue.d.ts +4 -0
  43. package/dist/views/system/role/index.vue.d.ts +4 -0
  44. package/dist/views/system/user/index.vue.d.ts +4 -0
  45. package/dist/vite.svg +9 -9
  46. package/index.html +13 -0
  47. package/package.json +27 -31
  48. package/public/vite.svg +10 -0
  49. package/src/App.vue +20 -0
  50. package/src/api/auth.ts +26 -0
  51. package/src/api/system.ts +65 -0
  52. package/src/api/user.ts +46 -0
  53. package/src/assets/styles/_dark.scss +407 -0
  54. package/src/assets/styles/_reset.scss +126 -0
  55. package/src/assets/styles/_root.scss +140 -0
  56. package/src/assets/styles/_transition.scss +119 -0
  57. package/src/assets/styles/_variables.scss +45 -0
  58. package/src/assets/styles/index.scss +187 -0
  59. package/src/components/Layout/Footer.vue +17 -0
  60. package/src/components/Layout/Header.vue +390 -0
  61. package/src/components/Layout/Sidebar.vue +297 -0
  62. package/src/components/Layout/Tabs.vue +134 -0
  63. package/src/components/Layout/index.vue +62 -0
  64. package/src/composables/useAuth.ts +45 -0
  65. package/src/composables/useForm.ts +79 -0
  66. package/src/composables/useTable.ts +97 -0
  67. package/src/directives/permission.ts +38 -0
  68. package/src/enums/index.ts +63 -0
  69. package/src/env.d.ts +17 -0
  70. package/src/index.ts +48 -0
  71. package/src/main.ts +34 -0
  72. package/src/router/dynamicRoutes.ts +163 -0
  73. package/src/router/index.ts +81 -0
  74. package/src/router/layoutRoute.ts +45 -0
  75. package/src/router/staticRoutes.ts +43 -0
  76. package/src/setup.ts +54 -0
  77. package/src/stores/app.ts +163 -0
  78. package/src/stores/auth.ts +66 -0
  79. package/src/stores/index.ts +15 -0
  80. package/src/stores/menu.ts +80 -0
  81. package/src/stores/user.ts +73 -0
  82. package/src/style.css +11 -0
  83. package/src/types/api.d.ts +84 -0
  84. package/src/types/global.d.ts +45 -0
  85. package/src/types/router.d.ts +48 -0
  86. package/src/types/xto.d.ts +149 -0
  87. package/src/utils/auth.ts +62 -0
  88. package/src/utils/permission.ts +42 -0
  89. package/src/utils/request.ts +124 -0
  90. package/src/utils/storage.ts +63 -0
  91. package/src/views/dashboard/index.vue +284 -0
  92. package/src/views/error/403.vue +57 -0
  93. package/src/views/error/404.vue +57 -0
  94. package/src/views/login/index.vue +248 -0
  95. package/src/views/system/menu/index.vue +381 -0
  96. package/src/views/system/role/index.vue +304 -0
  97. package/src/views/system/user/index.vue +327 -0
  98. package/tsconfig.json +26 -0
  99. package/tsconfig.node.json +11 -0
  100. package/vite.config.ts +140 -0
  101. package/dist/api/menu.d.ts +0 -4
  102. package/dist/components/Login/index.vue.d.ts +0 -25
  103. package/dist/components/SettingDrawer/index.vue.d.ts +0 -19
  104. package/dist/composables/index.d.ts +0 -8
  105. package/dist/composables/useApp.d.ts +0 -65
  106. package/dist/composables/useMenu.d.ts +0 -34
  107. package/dist/config/index.d.ts +0 -31
@@ -0,0 +1,73 @@
1
+ /**
2
+ * 用户状态
3
+ */
4
+
5
+ import { defineStore } from 'pinia'
6
+ import { ref, computed } from 'vue'
7
+ import type { UserInfo } from '@/types/api'
8
+ import { local } from '@/utils/storage'
9
+
10
+ export const useUserStore = defineStore('user', () => {
11
+ // 状态
12
+ const userInfo = ref<UserInfo | null>(local.get<UserInfo>('userInfo'))
13
+ const roles = ref<string[]>(local.get<string[]>('roles') || [])
14
+ const permissions = ref<string[]>(local.get<string[]>('permissions') || [])
15
+
16
+ // 计算属性
17
+ const isLoggedIn = computed(() => !!userInfo.value)
18
+ const username = computed(() => userInfo.value?.username || '')
19
+ const nickname = computed(() => userInfo.value?.nickname || '')
20
+ const avatar = computed(() => userInfo.value?.avatar || '')
21
+ const userId = computed(() => userInfo.value?.id)
22
+
23
+ // 设置用户信息
24
+ const setUserInfo = (info: UserInfo) => {
25
+ userInfo.value = info
26
+ roles.value = info.roles || []
27
+ permissions.value = info.permissions || []
28
+ local.set('userInfo', info)
29
+ local.set('roles', info.roles || [])
30
+ local.set('permissions', info.permissions || [])
31
+ }
32
+
33
+ // 清除用户信息
34
+ const clearUserInfo = () => {
35
+ userInfo.value = null
36
+ roles.value = []
37
+ permissions.value = []
38
+ local.remove('userInfo')
39
+ local.remove('roles')
40
+ local.remove('permissions')
41
+ }
42
+
43
+ // 检查权限
44
+ const hasPermission = (permission: string | string[]): boolean => {
45
+ if (Array.isArray(permission)) {
46
+ return permission.some(p => permissions.value.includes(p))
47
+ }
48
+ return permissions.value.includes(permission)
49
+ }
50
+
51
+ // 检查角色
52
+ const hasRole = (role: string | string[]): boolean => {
53
+ if (Array.isArray(role)) {
54
+ return role.some(r => roles.value.includes(r))
55
+ }
56
+ return roles.value.includes(role)
57
+ }
58
+
59
+ return {
60
+ userInfo,
61
+ roles,
62
+ permissions,
63
+ isLoggedIn,
64
+ username,
65
+ nickname,
66
+ avatar,
67
+ userId,
68
+ setUserInfo,
69
+ clearUserInfo,
70
+ hasPermission,
71
+ hasRole
72
+ }
73
+ })
package/src/style.css ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * xto-fronted 样式入口
3
+ * 组件样式已包含在各自的 Vue 组件中(scoped)
4
+ */
5
+
6
+ /* 全局样式变量(可选) */
7
+ :root {
8
+ --bg-color: #ffffff;
9
+ --bg-color-page: #f5f7fa;
10
+ --color-border-lighter: #e4e7ed;
11
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * API 通用类型定义
3
+ */
4
+
5
+ // 基础响应
6
+ export interface ApiResponse<T = unknown> {
7
+ code: number
8
+ data: T
9
+ message: string
10
+ }
11
+
12
+ // 分页请求参数
13
+ export interface PageParams {
14
+ page: number
15
+ pageSize: number
16
+ [key: string]: unknown
17
+ }
18
+
19
+ // 分页响应
20
+ export interface PageResponse<T> {
21
+ list: T[]
22
+ total: number
23
+ page: number
24
+ pageSize: number
25
+ }
26
+
27
+ // 用户信息
28
+ export interface UserInfo {
29
+ id: number | string
30
+ username: string
31
+ nickname: string
32
+ avatar?: string
33
+ email?: string
34
+ phone?: string
35
+ status: number
36
+ roles: string[]
37
+ permissions: string[]
38
+ createTime?: string
39
+ updateTime?: string
40
+ }
41
+
42
+ // 登录请求
43
+ export interface LoginParams {
44
+ username: string
45
+ password: string
46
+ captcha?: string
47
+ }
48
+
49
+ // 登录响应
50
+ export interface LoginResult {
51
+ token: string
52
+ refreshToken: string
53
+ expireTime: number
54
+ user: UserInfo
55
+ }
56
+
57
+ // 菜单项
58
+ export interface MenuItem {
59
+ id: number | string
60
+ name: string
61
+ path: string
62
+ component?: string
63
+ redirect?: string
64
+ icon?: string
65
+ title: string
66
+ hidden?: boolean
67
+ keepAlive?: boolean
68
+ affix?: boolean
69
+ order?: number
70
+ parentId?: number | string | null
71
+ children?: MenuItem[]
72
+ }
73
+
74
+ // 角色信息
75
+ export interface RoleInfo {
76
+ id: number | string
77
+ name: string
78
+ code: string
79
+ description?: string
80
+ status: number
81
+ permissions: string[]
82
+ createTime?: string
83
+ updateTime?: string
84
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 全局类型定义
3
+ */
4
+
5
+ // 通用对象类型
6
+ export type AnyObject = Record<string, unknown>
7
+
8
+ // 通用函数类型
9
+ export type AnyFunction = (...args: unknown[]) => unknown
10
+
11
+ // 值类型
12
+ export type ValueOf<T> = T[keyof T]
13
+
14
+ // 可空类型
15
+ export type Nullable<T> = T | null
16
+
17
+ // 可选类型
18
+ export type Optional<T> = T | undefined
19
+
20
+ // 深度可选
21
+ export type DeepPartial<T> = {
22
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
23
+ }
24
+
25
+ // 深度必需
26
+ export type DeepRequired<T> = {
27
+ [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P]
28
+ }
29
+
30
+ // 深度只读
31
+ export type DeepReadonly<T> = {
32
+ readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
33
+ }
34
+
35
+ // 提取数组元素类型
36
+ export type ArrayElement<T> = T extends readonly (infer E)[] ? E : never
37
+
38
+ // 提取 Promise 值类型
39
+ export type Awaited<T> = T extends Promise<infer U> ? U : T
40
+
41
+ // 排除 null 和 undefined
42
+ export type NonNullable<T> = T extends null | undefined ? never : T
43
+
44
+ // 枚举值类型
45
+ export type EnumValue<T extends Record<string, string | number>> = T[keyof T]
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 路由类型定义
3
+ */
4
+
5
+ import 'vue-router'
6
+
7
+ declare module 'vue-router' {
8
+ interface RouteMeta {
9
+ // 页面标题
10
+ title?: string
11
+ // 图标
12
+ icon?: string
13
+ // 是否隐藏菜单
14
+ hidden?: boolean
15
+ // 是否缓存页面
16
+ keepAlive?: boolean
17
+ // 是否固定在 tabs 中
18
+ affix?: boolean
19
+ // 权限标识
20
+ permissions?: string[]
21
+ // 角色标识
22
+ roles?: string[]
23
+ // 面包屑
24
+ breadcrumb?: boolean
25
+ // 当前激活菜单
26
+ activeMenu?: string
27
+ }
28
+ }
29
+
30
+ // 路由项
31
+ export interface AppRoute {
32
+ path: string
33
+ name?: string
34
+ redirect?: string
35
+ component?: () => Promise<unknown>
36
+ meta?: RouteMeta
37
+ children?: AppRoute[]
38
+ }
39
+
40
+ // 动态路由
41
+ export interface DynamicRoute {
42
+ path: string
43
+ name: string
44
+ component: string
45
+ redirect?: string
46
+ meta?: RouteMeta
47
+ children?: DynamicRoute[]
48
+ }
@@ -0,0 +1,149 @@
1
+ declare module '@xto/core' {
2
+ export const version: string
3
+ export function install(app: any): void
4
+ }
5
+
6
+ declare module '@xto/core/es/index/index.mjs' {
7
+ export * from '@xto/core'
8
+ }
9
+
10
+ declare module '@xto/core/es/theme/index.mjs' {
11
+ export const theme: any
12
+ }
13
+
14
+ declare module '@xto/core/es/hooks/index.mjs' {
15
+ export const hooks: any
16
+ }
17
+
18
+ declare module '@xto/core/es/utils/index.mjs' {
19
+ export const utils: any
20
+ }
21
+
22
+ declare module '@xto/base' {
23
+ import { DefineComponent } from 'vue'
24
+ export const Button: DefineComponent<any, any, any>
25
+ export const Loading: DefineComponent<any, any, any>
26
+ export const Icon: DefineComponent<any, any, any>
27
+ export const Avatar: DefineComponent<any, any, any>
28
+ export const Badge: DefineComponent<any, any, any>
29
+ export const Tag: DefineComponent<any, any, any>
30
+ export const Divider: DefineComponent<any, any, any>
31
+ export const Space: DefineComponent<any, any, any>
32
+ }
33
+
34
+ declare module '@xto/base/es/style.css' {
35
+ const content: string
36
+ export default content
37
+ }
38
+
39
+ declare module '@xto/form' {
40
+ import { DefineComponent } from 'vue'
41
+ export const Input: DefineComponent<any, any, any>
42
+ export const InputNumber: DefineComponent<any, any, any>
43
+ export const Select: DefineComponent<any, any, any>
44
+ export const Checkbox: DefineComponent<any, any, any>
45
+ export const Radio: DefineComponent<any, any, any>
46
+ export const Switch: DefineComponent<any, any, any>
47
+ export const DatePicker: DefineComponent<any, any, any>
48
+ export const Form: DefineComponent<any, any, any>
49
+ export const FormItem: DefineComponent<any, any, any>
50
+ export const Upload: DefineComponent<any, any, any>
51
+ export const FormRule: any
52
+ }
53
+
54
+ declare module '@xto/form/es/style.css' {
55
+ const content: string
56
+ export default content
57
+ }
58
+
59
+ declare module '@xto/data' {
60
+ import { DefineComponent } from 'vue'
61
+ export const Table: DefineComponent<any, any, any>
62
+ export const Pagination: DefineComponent<any, any, any>
63
+ export const Tree: DefineComponent<any, any, any>
64
+ export const TreeNode: DefineComponent<any, any, any>
65
+ export const List: DefineComponent<any, any, any>
66
+ export const Card: DefineComponent<any, any, any>
67
+ export const Descriptions: DefineComponent<any, any, any>
68
+ export const Tag: DefineComponent<any, any, any>
69
+ export const Progress: DefineComponent<any, any, any>
70
+ }
71
+
72
+ declare module '@xto/data/es/style.css' {
73
+ const content: string
74
+ export default content
75
+ }
76
+
77
+ declare module '@xto/feedback' {
78
+ import { DefineComponent } from 'vue'
79
+ export const Message: {
80
+ success: (msg: string) => void
81
+ error: (msg: string) => void
82
+ warning: (msg: string) => void
83
+ info: (msg: string) => void
84
+ }
85
+ export const Notification: {
86
+ success: (options: any) => void
87
+ error: (options: any) => void
88
+ warning: (options: any) => void
89
+ info: (options: any) => void
90
+ }
91
+ export const Modal: DefineComponent<any, any, any>
92
+ export const Drawer: DefineComponent<any, any, any>
93
+ export const Popconfirm: DefineComponent<any, any, any>
94
+ export const Tooltip: DefineComponent<any, any, any>
95
+ export const Popover: DefineComponent<any, any, any>
96
+ }
97
+
98
+ declare module '@xto/feedback/es/style.css' {
99
+ const content: string
100
+ export default content
101
+ }
102
+
103
+ declare module '@xto/navigation' {
104
+ import { DefineComponent } from 'vue'
105
+ export const Menu: DefineComponent<any, any, any>
106
+ export const MenuItem: DefineComponent<any, any, any>
107
+ export const SubMenu: DefineComponent<any, any, any>
108
+ export const Tabs: DefineComponent<any, any, any>
109
+ export const TabPane: DefineComponent<any, any, any>
110
+ export const Breadcrumb: DefineComponent<any, any, any>
111
+ export const BreadcrumbItem: DefineComponent<any, any, any>
112
+ export const Dropdown: DefineComponent<any, any, any>
113
+ export const DropdownMenu: DefineComponent<any, any, any>
114
+ export const DropdownItem: DefineComponent<any, any, any>
115
+ }
116
+
117
+ declare module '@xto/navigation/es/style.css' {
118
+ const content: string
119
+ export default content
120
+ }
121
+
122
+ declare module '@xto/layout' {
123
+ import { DefineComponent } from 'vue'
124
+ export const Layout: DefineComponent<any, any, any>
125
+ export const Header: DefineComponent<any, any, any>
126
+ export const Sider: DefineComponent<any, any, any>
127
+ export const Content: DefineComponent<any, any, any>
128
+ export const Footer: DefineComponent<any, any, any>
129
+ export const Container: DefineComponent<any, any, any>
130
+ }
131
+
132
+ declare module '@xto/layout/es/style.css' {
133
+ const content: string
134
+ export default content
135
+ }
136
+
137
+ declare module '@xto/business' {
138
+ import { DefineComponent } from 'vue'
139
+ export const LoginForm: DefineComponent<any, any, any>
140
+ export const RegisterForm: DefineComponent<any, any, any>
141
+ export const SearchResult: DefineComponent<any, any, any>
142
+ export const Comment: DefineComponent<any, any, any>
143
+ export const Statistic: DefineComponent<any, any, any>
144
+ }
145
+
146
+ declare module '@xto/business/es/style.css' {
147
+ const content: string
148
+ export default content
149
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Token 管理
3
+ */
4
+
5
+ import { local } from './storage'
6
+
7
+ const TOKEN_KEY = 'token'
8
+ const REFRESH_TOKEN_KEY = 'refresh_token'
9
+ const TOKEN_EXPIRE_KEY = 'token_expire'
10
+
11
+ export interface TokenInfo {
12
+ token: string
13
+ refreshToken: string
14
+ expireTime: number
15
+ }
16
+
17
+ export const getToken = (): string | null => {
18
+ return local.get<string>(TOKEN_KEY)
19
+ }
20
+
21
+ export const setToken = (token: string): void => {
22
+ local.set(TOKEN_KEY, token)
23
+ }
24
+
25
+ export const getRefreshToken = (): string | null => {
26
+ return local.get<string>(REFRESH_TOKEN_KEY)
27
+ }
28
+
29
+ export const setRefreshToken = (refreshToken: string): void => {
30
+ local.set(REFRESH_TOKEN_KEY, refreshToken)
31
+ }
32
+
33
+ export const getTokenExpire = (): number | null => {
34
+ return local.get<number>(TOKEN_EXPIRE_KEY)
35
+ }
36
+
37
+ export const setTokenExpire = (expireTime: number): void => {
38
+ local.set(TOKEN_EXPIRE_KEY, expireTime)
39
+ }
40
+
41
+ export const setTokenInfo = (info: TokenInfo): void => {
42
+ setToken(info.token)
43
+ setRefreshToken(info.refreshToken)
44
+ setTokenExpire(info.expireTime)
45
+ }
46
+
47
+ export const clearToken = (): void => {
48
+ local.remove(TOKEN_KEY)
49
+ local.remove(REFRESH_TOKEN_KEY)
50
+ local.remove(TOKEN_EXPIRE_KEY)
51
+ }
52
+
53
+ export const isTokenExpired = (): boolean => {
54
+ const expireTime = getTokenExpire()
55
+ if (!expireTime) return true
56
+ return Date.now() > expireTime
57
+ }
58
+
59
+ export const hasToken = (): boolean => {
60
+ const token = getToken()
61
+ return !!token && !isTokenExpired()
62
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * 权限工具函数
3
+ */
4
+
5
+ import { useUserStore } from '@/stores/user'
6
+
7
+ /**
8
+ * 检查是否有权限
9
+ * @param permission 权限标识
10
+ */
11
+ export function hasPermission(permission: string | string[]): boolean {
12
+ const userStore = useUserStore()
13
+ const permissions = userStore.permissions
14
+
15
+ if (Array.isArray(permission)) {
16
+ return permission.some(p => permissions.includes(p))
17
+ }
18
+
19
+ return permissions.includes(permission)
20
+ }
21
+
22
+ /**
23
+ * 检查是否有角色
24
+ * @param role 角色标识
25
+ */
26
+ export function hasRole(role: string | string[]): boolean {
27
+ const userStore = useUserStore()
28
+ const roles = userStore.roles
29
+
30
+ if (Array.isArray(role)) {
31
+ return role.some(r => roles.includes(r))
32
+ }
33
+
34
+ return roles.includes(role)
35
+ }
36
+
37
+ /**
38
+ * 检查是否是管理员
39
+ */
40
+ export function isAdmin(): boolean {
41
+ return hasRole('admin')
42
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Axios 请求封装
3
+ */
4
+
5
+ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
6
+ import { getToken, clearToken } from './auth'
7
+ import { Message } from '@xto/feedback'
8
+
9
+ // 响应数据接口(Euler 框架 Result 格式)
10
+ export interface ApiResponse<T = unknown> {
11
+ code: number
12
+ data: T
13
+ message: string
14
+ }
15
+
16
+ // 分页参数(Euler 框架格式)
17
+ export interface PageParams {
18
+ pageNo: number
19
+ pageSize: number
20
+ }
21
+
22
+ // 分页响应(Euler 框架格式)
23
+ export interface PageResponse<T> {
24
+ records: T[]
25
+ total: number
26
+ }
27
+
28
+ // 创建 axios 实例
29
+ const createRequest = (): AxiosInstance => {
30
+ const instance = axios.create({
31
+ baseURL: import.meta.env.VITE_API_BASE_URL,
32
+ timeout: 30000,
33
+ headers: {
34
+ 'Content-Type': 'application/json'
35
+ }
36
+ })
37
+
38
+ // 请求拦截器
39
+ instance.interceptors.request.use(
40
+ (config: InternalAxiosRequestConfig) => {
41
+ const token = getToken()
42
+ if (token) {
43
+ config.headers.Authorization = `Bearer ${token}`
44
+ }
45
+ return config
46
+ },
47
+ (error) => {
48
+ return Promise.reject(error)
49
+ }
50
+ )
51
+
52
+ // 响应拦截器
53
+ instance.interceptors.response.use(
54
+ (response: AxiosResponse<ApiResponse>) => {
55
+ const { data } = response
56
+
57
+ // Euler 框架返回 Result 包装格式,成功时直接返回 data 中的实际数据
58
+ if (data.code === 200 || data.code === 0) {
59
+ return data.data as any
60
+ }
61
+
62
+ // 业务错误
63
+ Message.error(data.message || '请求失败')
64
+ return Promise.reject(new Error(data.message || '请求失败'))
65
+ },
66
+ (error) => {
67
+ const { response } = error
68
+
69
+ if (response) {
70
+ switch (response.status) {
71
+ case 401:
72
+ Message.error('登录已过期,请重新登录')
73
+ clearToken()
74
+ window.location.href = '/login'
75
+ break
76
+ case 403:
77
+ Message.error('没有权限访问')
78
+ break
79
+ case 404:
80
+ Message.error('请求资源不存在')
81
+ break
82
+ case 500:
83
+ Message.error('服务器错误')
84
+ break
85
+ default:
86
+ Message.error(response.data?.message || '请求失败')
87
+ }
88
+ } else {
89
+ Message.error('网络连接失败')
90
+ }
91
+
92
+ return Promise.reject(error)
93
+ }
94
+ )
95
+
96
+ return instance
97
+ }
98
+
99
+ const request = createRequest()
100
+
101
+ // 请求方法 - 直接返回实际数据,无需手动解包
102
+ export const http = {
103
+ get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<T> {
104
+ return request.get(url, config)
105
+ },
106
+
107
+ post<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {
108
+ return request.post(url, data, config)
109
+ },
110
+
111
+ put<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {
112
+ return request.put(url, data, config)
113
+ },
114
+
115
+ patch<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {
116
+ return request.patch(url, data, config)
117
+ },
118
+
119
+ delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<T> {
120
+ return request.delete(url, config)
121
+ }
122
+ }
123
+
124
+ export default request
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 本地存储封装
3
+ */
4
+
5
+ const prefix = 'xto_'
6
+
7
+ interface StorageWrapper {
8
+ get<T>(key: string): T | null
9
+ set(key: string, value: unknown): void
10
+ remove(key: string): void
11
+ clear(): void
12
+ }
13
+
14
+ const createStorage = (storage: globalThis.Storage): StorageWrapper => {
15
+ return {
16
+ get<T>(key: string): T | null {
17
+ const value = storage.getItem(prefix + key)
18
+ if (!value) return null
19
+ try {
20
+ return JSON.parse(value) as T
21
+ } catch {
22
+ return value as unknown as T
23
+ }
24
+ },
25
+ set(key: string, value: unknown): void {
26
+ if (value === null || value === undefined) {
27
+ storage.removeItem(prefix + key)
28
+ return
29
+ }
30
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value)
31
+ storage.setItem(prefix + key, stringValue)
32
+ },
33
+ remove(key: string): void {
34
+ storage.removeItem(prefix + key)
35
+ },
36
+ clear(): void {
37
+ const keys = Object.keys(storage)
38
+ keys.forEach(key => {
39
+ if (key.startsWith(prefix)) {
40
+ storage.removeItem(key)
41
+ }
42
+ })
43
+ }
44
+ }
45
+ }
46
+
47
+ export const localStorageUtil = createStorage(window.localStorage)
48
+ export const sessionStorageUtil = createStorage(window.sessionStorage)
49
+
50
+ // 快捷方法
51
+ export const local = {
52
+ get: localStorageUtil.get,
53
+ set: localStorageUtil.set,
54
+ remove: localStorageUtil.remove,
55
+ clear: localStorageUtil.clear
56
+ }
57
+
58
+ export const session = {
59
+ get: sessionStorageUtil.get,
60
+ set: sessionStorageUtil.set,
61
+ remove: sessionStorageUtil.remove,
62
+ clear: sessionStorageUtil.clear
63
+ }