af-mobile-client-vue3 1.6.12 → 1.6.14
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.
- package/package.json +1 -1
- package/src/stores/modules/setting.ts +96 -96
- package/src/stores/modules/user.ts +6 -1
- package/src/utils/Storage.ts +124 -124
- package/src/utils/http/index.ts +225 -225
package/package.json
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
import { getConfigByNameAsync } from '@af-mobile-client-vue3/services/api/common'
|
|
2
|
-
import { APP_WEB_CONFIG_KEY } from '@af-mobile-client-vue3/stores/mutation-type'
|
|
3
|
-
import { secureStorageBatchWrite } from '@af-mobile-client-vue3/utils/secureStorage'
|
|
4
|
-
import { createStorage } from '@af-mobile-client-vue3/utils/Storage'
|
|
5
|
-
import { defineStore } from 'pinia'
|
|
6
|
-
import { ref } from 'vue'
|
|
7
|
-
import { useHomeAppStore } from './homeApp'
|
|
8
|
-
|
|
9
|
-
export interface WebConfig {
|
|
10
|
-
isAttendance: boolean | false
|
|
11
|
-
systemName: string
|
|
12
|
-
routerName: string
|
|
13
|
-
systemDesc: string
|
|
14
|
-
homePage: any
|
|
15
|
-
defaultAvatarUrl: string
|
|
16
|
-
wechatLogin: boolean
|
|
17
|
-
wxLoginAge: boolean
|
|
18
|
-
wxAutoLogin: string
|
|
19
|
-
tenantName: string
|
|
20
|
-
systemLogo: string
|
|
21
|
-
homeAppList: Array<any>
|
|
22
|
-
slideshowList: Array<any>
|
|
23
|
-
registerRequire: boolean
|
|
24
|
-
requestEncrypt: boolean
|
|
25
|
-
phoneLoginVerification: boolean
|
|
26
|
-
hiddenTraditionalLogin: boolean
|
|
27
|
-
isFaceRecognition: boolean // 登录是否需要人脸识别
|
|
28
|
-
intervalFaceRecognition: boolean // 登录是否需要开启定时人脸识别
|
|
29
|
-
faceIntervalMinutes: number // 定时时间 单位分钟
|
|
30
|
-
faceRetryCount: number // 定时验证 重试次数
|
|
31
|
-
getPlatformUser: boolean // 获取外部用户信息
|
|
32
|
-
navigationStyle: any // app顶部导航区域样式控制
|
|
33
|
-
captchaEnable: boolean // 是否启用登录验证码(前台本地处理)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 存放 webConfig 中的 setting 配置
|
|
37
|
-
export const useSettingStore = defineStore('setting', () => {
|
|
38
|
-
const setting = ref<WebConfig>(undefined)
|
|
39
|
-
const isInitialized = ref(false)
|
|
40
|
-
|
|
41
|
-
const setSetting = (newSetting: WebConfig) => {
|
|
42
|
-
setting.value = newSetting
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const getSetting = () => {
|
|
46
|
-
return setting.value
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// 将 init 作为 store 的方法导出
|
|
50
|
-
const init = async () => {
|
|
51
|
-
if (isInitialized.value)
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
const useStore = createStorage()
|
|
55
|
-
const catchWebConfig = useStore.get(APP_WEB_CONFIG_KEY)
|
|
56
|
-
const homeAppStore = useHomeAppStore()
|
|
57
|
-
|
|
58
|
-
if (catchWebConfig?.setting) {
|
|
59
|
-
setSetting(catchWebConfig?.setting)
|
|
60
|
-
// homeAppList 仅在用户缓存没有时才回填,避免覆盖用户自定义
|
|
61
|
-
if (!homeAppStore.homeAppList?.length && catchWebConfig.setting.homeAppList) {
|
|
62
|
-
homeAppStore.setHomeAppList(catchWebConfig.setting.homeAppList)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
const res = await getConfigByNameAsync('webMobileConfig'
|
|
67
|
-
if (res.setting) {
|
|
68
|
-
useStore.set(APP_WEB_CONFIG_KEY, res)
|
|
69
|
-
setSetting(res.setting)
|
|
70
|
-
// homeAppList 仅在用户缓存没有时才回填
|
|
71
|
-
if (!homeAppStore.homeAppList?.length && res.setting.homeAppList) {
|
|
72
|
-
homeAppStore.setHomeAppList(res.setting.homeAppList)
|
|
73
|
-
}
|
|
74
|
-
// 如果有pos相关的配置存储到app安全存储中
|
|
75
|
-
if (res.setting.posConfig) {
|
|
76
|
-
secureStorageBatchWrite([
|
|
77
|
-
{
|
|
78
|
-
key: 'posConfig',
|
|
79
|
-
value: res.setting.posConfig,
|
|
80
|
-
},
|
|
81
|
-
])
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
isInitialized.value = true
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
setSetting,
|
|
90
|
-
getSetting,
|
|
91
|
-
init, // 导出 init 方法
|
|
92
|
-
isInitialized,
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
export default useSettingStore
|
|
1
|
+
import { getConfigByNameAsync } from '@af-mobile-client-vue3/services/api/common'
|
|
2
|
+
import { APP_WEB_CONFIG_KEY } from '@af-mobile-client-vue3/stores/mutation-type'
|
|
3
|
+
import { secureStorageBatchWrite } from '@af-mobile-client-vue3/utils/secureStorage'
|
|
4
|
+
import { createStorage } from '@af-mobile-client-vue3/utils/Storage'
|
|
5
|
+
import { defineStore } from 'pinia'
|
|
6
|
+
import { ref } from 'vue'
|
|
7
|
+
import { useHomeAppStore } from './homeApp'
|
|
8
|
+
|
|
9
|
+
export interface WebConfig {
|
|
10
|
+
isAttendance: boolean | false
|
|
11
|
+
systemName: string
|
|
12
|
+
routerName: string
|
|
13
|
+
systemDesc: string
|
|
14
|
+
homePage: any
|
|
15
|
+
defaultAvatarUrl: string
|
|
16
|
+
wechatLogin: boolean
|
|
17
|
+
wxLoginAge: boolean
|
|
18
|
+
wxAutoLogin: string
|
|
19
|
+
tenantName: string
|
|
20
|
+
systemLogo: string
|
|
21
|
+
homeAppList: Array<any>
|
|
22
|
+
slideshowList: Array<any>
|
|
23
|
+
registerRequire: boolean
|
|
24
|
+
requestEncrypt: boolean
|
|
25
|
+
phoneLoginVerification: boolean
|
|
26
|
+
hiddenTraditionalLogin: boolean
|
|
27
|
+
isFaceRecognition: boolean // 登录是否需要人脸识别
|
|
28
|
+
intervalFaceRecognition: boolean // 登录是否需要开启定时人脸识别
|
|
29
|
+
faceIntervalMinutes: number // 定时时间 单位分钟
|
|
30
|
+
faceRetryCount: number // 定时验证 重试次数
|
|
31
|
+
getPlatformUser: boolean // 获取外部用户信息
|
|
32
|
+
navigationStyle: any // app顶部导航区域样式控制
|
|
33
|
+
captchaEnable: boolean // 是否启用登录验证码(前台本地处理)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 存放 webConfig 中的 setting 配置
|
|
37
|
+
export const useSettingStore = defineStore('setting', () => {
|
|
38
|
+
const setting = ref<WebConfig>(undefined)
|
|
39
|
+
const isInitialized = ref(false)
|
|
40
|
+
|
|
41
|
+
const setSetting = (newSetting: WebConfig) => {
|
|
42
|
+
setting.value = newSetting
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const getSetting = () => {
|
|
46
|
+
return setting.value
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 将 init 作为 store 的方法导出
|
|
50
|
+
const init = async () => {
|
|
51
|
+
if (isInitialized.value)
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
const useStore = createStorage()
|
|
55
|
+
const catchWebConfig = useStore.get(APP_WEB_CONFIG_KEY)
|
|
56
|
+
const homeAppStore = useHomeAppStore()
|
|
57
|
+
|
|
58
|
+
if (catchWebConfig?.setting) {
|
|
59
|
+
setSetting(catchWebConfig?.setting)
|
|
60
|
+
// homeAppList 仅在用户缓存没有时才回填,避免覆盖用户自定义
|
|
61
|
+
if (!homeAppStore.homeAppList?.length && catchWebConfig.setting.homeAppList) {
|
|
62
|
+
homeAppStore.setHomeAppList(catchWebConfig.setting.homeAppList)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const res = await getConfigByNameAsync('webMobileConfig')
|
|
67
|
+
if (res.setting) {
|
|
68
|
+
useStore.set(APP_WEB_CONFIG_KEY, res)
|
|
69
|
+
setSetting(res.setting)
|
|
70
|
+
// homeAppList 仅在用户缓存没有时才回填
|
|
71
|
+
if (!homeAppStore.homeAppList?.length && res.setting.homeAppList) {
|
|
72
|
+
homeAppStore.setHomeAppList(res.setting.homeAppList)
|
|
73
|
+
}
|
|
74
|
+
// 如果有pos相关的配置存储到app安全存储中
|
|
75
|
+
if (res.setting.posConfig) {
|
|
76
|
+
secureStorageBatchWrite([
|
|
77
|
+
{
|
|
78
|
+
key: 'posConfig',
|
|
79
|
+
value: res.setting.posConfig,
|
|
80
|
+
},
|
|
81
|
+
])
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
isInitialized.value = true
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
setSetting,
|
|
90
|
+
getSetting,
|
|
91
|
+
init, // 导出 init 方法
|
|
92
|
+
isInitialized,
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
export default useSettingStore
|
|
@@ -126,6 +126,9 @@ export const useUserStore = defineStore('app-user', () => {
|
|
|
126
126
|
const getToken = () => {
|
|
127
127
|
return userState.value.token || Storage.get(ACCESS_TOKEN, '') as string
|
|
128
128
|
}
|
|
129
|
+
const getSessionKey = () => {
|
|
130
|
+
return Storage.get('v4-session-key', '') as string || localStorage.getItem('v4-session-key')
|
|
131
|
+
}
|
|
129
132
|
const getLastUpdateTime = () => {
|
|
130
133
|
return userState.value.lastUpdateTime
|
|
131
134
|
}
|
|
@@ -162,7 +165,7 @@ export const useUserStore = defineStore('app-user', () => {
|
|
|
162
165
|
const expiresInSeconds = expiresInMinutes * 60
|
|
163
166
|
const BUFFER_SECONDS = 30
|
|
164
167
|
const safeExpire = Math.max(expiresInSeconds - BUFFER_SECONDS, 0)
|
|
165
|
-
Storage.set('v4-session-key', key,
|
|
168
|
+
Storage.set('v4-session-key', key, safeExpire)
|
|
166
169
|
}
|
|
167
170
|
else {
|
|
168
171
|
localStorage.setItem('v4-session-key', key)
|
|
@@ -377,6 +380,7 @@ export const useUserStore = defineStore('app-user', () => {
|
|
|
377
380
|
Storage.remove(PLATFORM_TYPE)
|
|
378
381
|
Storage.remove(EXTERNAL_USER_INFO)
|
|
379
382
|
Storage.remove('LoginTicket')
|
|
383
|
+
Storage.remove('v4-session-key')
|
|
380
384
|
// 清除本地加密秘钥
|
|
381
385
|
localStorage.removeItem('v4-session-key')
|
|
382
386
|
secureStorageWrite('v4-session-key', '')
|
|
@@ -423,6 +427,7 @@ export const useUserStore = defineStore('app-user', () => {
|
|
|
423
427
|
loginExternal,
|
|
424
428
|
loginExternalMini,
|
|
425
429
|
getToken,
|
|
430
|
+
getSessionKey,
|
|
426
431
|
getLastUpdateTime,
|
|
427
432
|
logout,
|
|
428
433
|
registerClean,
|
package/src/utils/Storage.ts
CHANGED
|
@@ -1,124 +1,124 @@
|
|
|
1
|
-
// 默认缓存期限为7天
|
|
2
|
-
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 创建本地缓存对象
|
|
6
|
-
*/
|
|
7
|
-
export function createStorage({ prefixKey = '', storage = localStorage } = {}) {
|
|
8
|
-
/**
|
|
9
|
-
* 本地缓存类
|
|
10
|
-
* @class Storage
|
|
11
|
-
*/
|
|
12
|
-
const Storage = class {
|
|
13
|
-
private storage = storage
|
|
14
|
-
private prefixKey?: string = prefixKey
|
|
15
|
-
|
|
16
|
-
private getKey(key: string) {
|
|
17
|
-
return `${this.prefixKey}${key}`.toUpperCase()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @description 设置缓存
|
|
22
|
-
* @param {string} key 缓存键
|
|
23
|
-
* @param {*} value 缓存值
|
|
24
|
-
* @param expire
|
|
25
|
-
*/
|
|
26
|
-
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
|
27
|
-
const stringData = JSON.stringify({
|
|
28
|
-
value,
|
|
29
|
-
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
|
|
30
|
-
})
|
|
31
|
-
this.storage.setItem(this.getKey(key), stringData)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* 读取缓存
|
|
36
|
-
* @param {string} key 缓存键
|
|
37
|
-
* @param {*=} def 默认值
|
|
38
|
-
*/
|
|
39
|
-
get(key: string, def: any = null) {
|
|
40
|
-
const item = this.storage.getItem(this.getKey(key))
|
|
41
|
-
if (item) {
|
|
42
|
-
try {
|
|
43
|
-
const data = JSON.parse(item)
|
|
44
|
-
const { value, expire } = data
|
|
45
|
-
// 在有效期内直接返回
|
|
46
|
-
if (expire === null || expire >= Date.now())
|
|
47
|
-
return value
|
|
48
|
-
|
|
49
|
-
this.remove(key)
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return def
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return def
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 从缓存删除某项
|
|
60
|
-
* @param {string} key
|
|
61
|
-
*/
|
|
62
|
-
remove(key: string) {
|
|
63
|
-
this.storage.removeItem(this.getKey(key))
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 清空所有缓存
|
|
68
|
-
* @memberOf Cache
|
|
69
|
-
*/
|
|
70
|
-
clear(): void {
|
|
71
|
-
this.storage.clear()
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 设置cookie
|
|
76
|
-
* @param {string} name cookie 名称
|
|
77
|
-
* @param {*} value cookie 值
|
|
78
|
-
* @param {number=} expire 过期时间
|
|
79
|
-
* 如果过期时间为设置,默认关闭浏览器自动删除
|
|
80
|
-
* @example
|
|
81
|
-
*/
|
|
82
|
-
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
|
83
|
-
document.cookie = `${this.getKey(name)}=${value}; Max-Age=${expire}`
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 根据名字获取cookie值
|
|
88
|
-
* @param name
|
|
89
|
-
*/
|
|
90
|
-
getCookie(name: string): string {
|
|
91
|
-
const cookieArr = document.cookie.split('; ')
|
|
92
|
-
for (let i = 0, length = cookieArr.length; i < length; i++) {
|
|
93
|
-
const kv = cookieArr[i].split('=')
|
|
94
|
-
if (kv[0] === this.getKey(name))
|
|
95
|
-
return kv[1]
|
|
96
|
-
}
|
|
97
|
-
return ''
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* 根据名字删除指定的cookie
|
|
102
|
-
* @param {string} key
|
|
103
|
-
*/
|
|
104
|
-
removeCookie(key: string) {
|
|
105
|
-
this.setCookie(key, 1, -1)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* 清空cookie,使所有cookie失效
|
|
110
|
-
*/
|
|
111
|
-
clearCookie(): void {
|
|
112
|
-
const keys = document.cookie.match(/[^ =;]+(?==)/g)
|
|
113
|
-
if (keys) {
|
|
114
|
-
for (let i = keys.length; i--;)
|
|
115
|
-
document.cookie = `${keys[i]}=0;expire=${new Date(0).toUTCString()}`
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return new Storage()
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export const storage = createStorage()
|
|
123
|
-
|
|
124
|
-
export default Storage
|
|
1
|
+
// 默认缓存期限为7天
|
|
2
|
+
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 创建本地缓存对象
|
|
6
|
+
*/
|
|
7
|
+
export function createStorage({ prefixKey = '', storage = localStorage } = {}) {
|
|
8
|
+
/**
|
|
9
|
+
* 本地缓存类
|
|
10
|
+
* @class Storage
|
|
11
|
+
*/
|
|
12
|
+
const Storage = class {
|
|
13
|
+
private storage = storage
|
|
14
|
+
private prefixKey?: string = prefixKey
|
|
15
|
+
|
|
16
|
+
private getKey(key: string) {
|
|
17
|
+
return `${this.prefixKey}${key}`.toUpperCase()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @description 设置缓存
|
|
22
|
+
* @param {string} key 缓存键
|
|
23
|
+
* @param {*} value 缓存值
|
|
24
|
+
* @param expire
|
|
25
|
+
*/
|
|
26
|
+
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
|
27
|
+
const stringData = JSON.stringify({
|
|
28
|
+
value,
|
|
29
|
+
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
|
|
30
|
+
})
|
|
31
|
+
this.storage.setItem(this.getKey(key), stringData)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 读取缓存
|
|
36
|
+
* @param {string} key 缓存键
|
|
37
|
+
* @param {*=} def 默认值
|
|
38
|
+
*/
|
|
39
|
+
get(key: string, def: any = null) {
|
|
40
|
+
const item = this.storage.getItem(this.getKey(key))
|
|
41
|
+
if (item) {
|
|
42
|
+
try {
|
|
43
|
+
const data = JSON.parse(item)
|
|
44
|
+
const { value, expire } = data
|
|
45
|
+
// 在有效期内直接返回
|
|
46
|
+
if (expire === null || expire >= Date.now())
|
|
47
|
+
return value
|
|
48
|
+
|
|
49
|
+
this.remove(key)
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return def
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return def
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 从缓存删除某项
|
|
60
|
+
* @param {string} key
|
|
61
|
+
*/
|
|
62
|
+
remove(key: string) {
|
|
63
|
+
this.storage.removeItem(this.getKey(key))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 清空所有缓存
|
|
68
|
+
* @memberOf Cache
|
|
69
|
+
*/
|
|
70
|
+
clear(): void {
|
|
71
|
+
this.storage.clear()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 设置cookie
|
|
76
|
+
* @param {string} name cookie 名称
|
|
77
|
+
* @param {*} value cookie 值
|
|
78
|
+
* @param {number=} expire 过期时间
|
|
79
|
+
* 如果过期时间为设置,默认关闭浏览器自动删除
|
|
80
|
+
* @example
|
|
81
|
+
*/
|
|
82
|
+
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
|
83
|
+
document.cookie = `${this.getKey(name)}=${value}; Max-Age=${expire}`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 根据名字获取cookie值
|
|
88
|
+
* @param name
|
|
89
|
+
*/
|
|
90
|
+
getCookie(name: string): string {
|
|
91
|
+
const cookieArr = document.cookie.split('; ')
|
|
92
|
+
for (let i = 0, length = cookieArr.length; i < length; i++) {
|
|
93
|
+
const kv = cookieArr[i].split('=')
|
|
94
|
+
if (kv[0] === this.getKey(name))
|
|
95
|
+
return kv[1]
|
|
96
|
+
}
|
|
97
|
+
return ''
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 根据名字删除指定的cookie
|
|
102
|
+
* @param {string} key
|
|
103
|
+
*/
|
|
104
|
+
removeCookie(key: string) {
|
|
105
|
+
this.setCookie(key, 1, -1)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 清空cookie,使所有cookie失效
|
|
110
|
+
*/
|
|
111
|
+
clearCookie(): void {
|
|
112
|
+
const keys = document.cookie.match(/[^ =;]+(?==)/g)
|
|
113
|
+
if (keys) {
|
|
114
|
+
for (let i = keys.length; i--;)
|
|
115
|
+
document.cookie = `${keys[i]}=0;expire=${new Date(0).toUTCString()}`
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return new Storage()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const storage = createStorage()
|
|
123
|
+
|
|
124
|
+
export default Storage
|
package/src/utils/http/index.ts
CHANGED
|
@@ -1,225 +1,225 @@
|
|
|
1
|
-
import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
|
2
|
-
import { ContentTypeEnum, ResultEnum } from '@af-mobile-client-vue3/enums/requestEnum'
|
|
3
|
-
import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
|
|
4
|
-
import { ACCESS_TOKEN } from '@af-mobile-client-vue3/stores/mutation-type'
|
|
5
|
-
import { encryptUtil } from '@af-mobile-client-vue3/utils/EncryptUtil'
|
|
6
|
-
import axios from 'axios'
|
|
7
|
-
import { showToast } from 'vant'
|
|
8
|
-
|
|
9
|
-
// 默认 axios 实例请求配置
|
|
10
|
-
const configDefault = {
|
|
11
|
-
headers: {
|
|
12
|
-
'Content-Type': ContentTypeEnum.JSON,
|
|
13
|
-
},
|
|
14
|
-
// 请求超时时间
|
|
15
|
-
timeout: 20000,
|
|
16
|
-
// API 请求的默认前缀
|
|
17
|
-
baseURL: import.meta.env.VITE_APP_API_BASE_URL,
|
|
18
|
-
data: {},
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
class Http {
|
|
22
|
-
// 当前实例
|
|
23
|
-
private static axiosInstance: AxiosInstance
|
|
24
|
-
// 请求配置
|
|
25
|
-
private static axiosConfigDefault: AxiosRequestConfig
|
|
26
|
-
|
|
27
|
-
// 请求拦截
|
|
28
|
-
private httpInterceptorsRequest(): void {
|
|
29
|
-
Http.axiosInstance.interceptors.request.use(
|
|
30
|
-
(config) => {
|
|
31
|
-
// 发送请求前,可在此让每个请求携带自定义 token, 请根据实际情况修改
|
|
32
|
-
const savedToken = useUserStore().getToken()
|
|
33
|
-
// 如果 token 存在
|
|
34
|
-
if (savedToken)
|
|
35
|
-
config.headers[ACCESS_TOKEN] = savedToken
|
|
36
|
-
const v4SessionKey =
|
|
37
|
-
if (['post'].includes(config.method.toLowerCase()) && v4SessionKey) {
|
|
38
|
-
if (config.data && !(config.data instanceof FormData)) {
|
|
39
|
-
config.data = {
|
|
40
|
-
encrypted: encryptUtil.AESEncryptCBC(config.data, v4SessionKey),
|
|
41
|
-
}
|
|
42
|
-
config.headers['X-Sec'] = '1'
|
|
43
|
-
config.headers['X-Rand'] = Math.random().toString(36).substr(2, 5)
|
|
44
|
-
config.headers['X-Ts'] = Date.now()
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return config
|
|
48
|
-
},
|
|
49
|
-
(error: AxiosError) => {
|
|
50
|
-
showToast({
|
|
51
|
-
message: error.message,
|
|
52
|
-
position: 'bottom',
|
|
53
|
-
})
|
|
54
|
-
return Promise.reject(error)
|
|
55
|
-
},
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 响应拦截
|
|
60
|
-
private httpInterceptorsResponse(): void {
|
|
61
|
-
Http.axiosInstance.interceptors.response.use(
|
|
62
|
-
async (response: AxiosResponse) => {
|
|
63
|
-
// 判断是否需要解密
|
|
64
|
-
if (response.headers && response.headers['x-encrypted'] === '1') {
|
|
65
|
-
const v4SessionKey =
|
|
66
|
-
if (v4SessionKey && response.data) {
|
|
67
|
-
const decryptedData = encryptUtil.decryptResponse(response?.data, v4SessionKey)
|
|
68
|
-
// 如果解密成功且不等于原数据,说明解密有效
|
|
69
|
-
if (decryptedData !== response.data) {
|
|
70
|
-
// 响应解密成功
|
|
71
|
-
response.data = decryptedData
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
const compatible = import.meta.env.VITE_APP_COMPATIBLE
|
|
76
|
-
if (compatible !== 'V4') {
|
|
77
|
-
return response.data
|
|
78
|
-
}
|
|
79
|
-
// 与后端协定的返回字段
|
|
80
|
-
const { code, msg, data } = response.data
|
|
81
|
-
// 临时向v3请求上传服务,因为没有code,未来会改
|
|
82
|
-
if (code === undefined) {
|
|
83
|
-
return response.data
|
|
84
|
-
}
|
|
85
|
-
// 判断请求是否成功
|
|
86
|
-
const isSuccess = code === ResultEnum.SUCCESS
|
|
87
|
-
if (isSuccess) {
|
|
88
|
-
// 兼容 V3 请求没有data的情况
|
|
89
|
-
if (data) {
|
|
90
|
-
return data
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
return response.data
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
if (code === 401 && response.config.url?.indexOf('af-auth/logout') === -1) {
|
|
98
|
-
showToast({
|
|
99
|
-
message: '登录态已失效,请重新登录',
|
|
100
|
-
position: 'bottom',
|
|
101
|
-
})
|
|
102
|
-
await useUserStore().logout()
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
showToast({
|
|
106
|
-
message: msg,
|
|
107
|
-
position: 'bottom',
|
|
108
|
-
})
|
|
109
|
-
console.error(`请求失败,返回结果:${msg}`)
|
|
110
|
-
}
|
|
111
|
-
return Promise.reject(response.data)
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
(error: AxiosError) => {
|
|
115
|
-
// 处理 HTTP 网络错误
|
|
116
|
-
let message: string = error.response?.data as string
|
|
117
|
-
if (!message) {
|
|
118
|
-
// HTTP 状态码
|
|
119
|
-
const status = error.response?.status
|
|
120
|
-
switch (status) {
|
|
121
|
-
case 400:
|
|
122
|
-
message = '请求错误'
|
|
123
|
-
break
|
|
124
|
-
case 401:
|
|
125
|
-
message = '登录态已失效,请重新登录'
|
|
126
|
-
// 401状态码处理:自动登出并跳转到登录页
|
|
127
|
-
useUserStore().logout()
|
|
128
|
-
break
|
|
129
|
-
case 403:
|
|
130
|
-
message = '拒绝访问'
|
|
131
|
-
break
|
|
132
|
-
case 404:
|
|
133
|
-
message = `请求地址出错: ${error.response?.config?.url}`
|
|
134
|
-
break
|
|
135
|
-
case 408:
|
|
136
|
-
message = '请求超时'
|
|
137
|
-
break
|
|
138
|
-
case 500:
|
|
139
|
-
message = '服务器内部错误'
|
|
140
|
-
break
|
|
141
|
-
case 501:
|
|
142
|
-
message = '服务未实现'
|
|
143
|
-
break
|
|
144
|
-
case 502:
|
|
145
|
-
message = '网关错误'
|
|
146
|
-
break
|
|
147
|
-
case 503:
|
|
148
|
-
message = '服务不可用'
|
|
149
|
-
break
|
|
150
|
-
case 504:
|
|
151
|
-
message = '网关超时'
|
|
152
|
-
break
|
|
153
|
-
case 505:
|
|
154
|
-
message = 'HTTP版本不受支持'
|
|
155
|
-
break
|
|
156
|
-
default:
|
|
157
|
-
message = '网络连接故障'
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
showToast({
|
|
161
|
-
message,
|
|
162
|
-
position: 'bottom',
|
|
163
|
-
})
|
|
164
|
-
return Promise.reject(error)
|
|
165
|
-
},
|
|
166
|
-
)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
constructor(config: AxiosRequestConfig) {
|
|
170
|
-
Http.axiosConfigDefault = config
|
|
171
|
-
Http.axiosInstance = axios.create(config)
|
|
172
|
-
this.httpInterceptorsRequest()
|
|
173
|
-
this.httpInterceptorsResponse()
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// 通用请求函数
|
|
177
|
-
public request<T>(paramConfig: AxiosRequestConfig): Promise<T> {
|
|
178
|
-
const config = { ...Http.axiosConfigDefault, ...paramConfig }
|
|
179
|
-
return new Promise((resolve, reject) => {
|
|
180
|
-
Http.axiosInstance
|
|
181
|
-
.request(config)
|
|
182
|
-
.then((response: any) => {
|
|
183
|
-
resolve(response)
|
|
184
|
-
})
|
|
185
|
-
.catch((error) => {
|
|
186
|
-
reject(error)
|
|
187
|
-
})
|
|
188
|
-
})
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const METHOD = {
|
|
193
|
-
GET: 'get',
|
|
194
|
-
POST: 'post',
|
|
195
|
-
PUT: 'put',
|
|
196
|
-
DELETE: 'delete',
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* axios请求
|
|
200
|
-
* @param url 请求地址
|
|
201
|
-
* @param method {METHOD} http method
|
|
202
|
-
* @param params 请求参数
|
|
203
|
-
* @param config
|
|
204
|
-
* @returns {Promise<AxiosResponse<T>>}
|
|
205
|
-
*/
|
|
206
|
-
async function request(url, method, params, config) {
|
|
207
|
-
switch (method) {
|
|
208
|
-
case METHOD.GET:
|
|
209
|
-
return axios.get(url, { params, ...config })
|
|
210
|
-
case METHOD.POST:
|
|
211
|
-
return axios.post(url, params, config)
|
|
212
|
-
case METHOD.PUT:
|
|
213
|
-
return axios.put(url, params, config)
|
|
214
|
-
case METHOD.DELETE:
|
|
215
|
-
return axios.delete(url, { params, ...config })
|
|
216
|
-
default:
|
|
217
|
-
return axios.get(url, { params, ...config })
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export const http = new Http(configDefault)
|
|
222
|
-
export {
|
|
223
|
-
METHOD,
|
|
224
|
-
request,
|
|
225
|
-
}
|
|
1
|
+
import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
|
2
|
+
import { ContentTypeEnum, ResultEnum } from '@af-mobile-client-vue3/enums/requestEnum'
|
|
3
|
+
import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
|
|
4
|
+
import { ACCESS_TOKEN } from '@af-mobile-client-vue3/stores/mutation-type'
|
|
5
|
+
import { encryptUtil } from '@af-mobile-client-vue3/utils/EncryptUtil'
|
|
6
|
+
import axios from 'axios'
|
|
7
|
+
import { showToast } from 'vant'
|
|
8
|
+
|
|
9
|
+
// 默认 axios 实例请求配置
|
|
10
|
+
const configDefault = {
|
|
11
|
+
headers: {
|
|
12
|
+
'Content-Type': ContentTypeEnum.JSON,
|
|
13
|
+
},
|
|
14
|
+
// 请求超时时间
|
|
15
|
+
timeout: 20000,
|
|
16
|
+
// API 请求的默认前缀
|
|
17
|
+
baseURL: import.meta.env.VITE_APP_API_BASE_URL,
|
|
18
|
+
data: {},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class Http {
|
|
22
|
+
// 当前实例
|
|
23
|
+
private static axiosInstance: AxiosInstance
|
|
24
|
+
// 请求配置
|
|
25
|
+
private static axiosConfigDefault: AxiosRequestConfig
|
|
26
|
+
|
|
27
|
+
// 请求拦截
|
|
28
|
+
private httpInterceptorsRequest(): void {
|
|
29
|
+
Http.axiosInstance.interceptors.request.use(
|
|
30
|
+
(config) => {
|
|
31
|
+
// 发送请求前,可在此让每个请求携带自定义 token, 请根据实际情况修改
|
|
32
|
+
const savedToken = useUserStore().getToken()
|
|
33
|
+
// 如果 token 存在
|
|
34
|
+
if (savedToken)
|
|
35
|
+
config.headers[ACCESS_TOKEN] = savedToken
|
|
36
|
+
const v4SessionKey = useUserStore().getSessionKey()
|
|
37
|
+
if (['post'].includes(config.method.toLowerCase()) && v4SessionKey) {
|
|
38
|
+
if (config.data && !(config.data instanceof FormData)) {
|
|
39
|
+
config.data = {
|
|
40
|
+
encrypted: encryptUtil.AESEncryptCBC(config.data, v4SessionKey),
|
|
41
|
+
}
|
|
42
|
+
config.headers['X-Sec'] = '1'
|
|
43
|
+
config.headers['X-Rand'] = Math.random().toString(36).substr(2, 5)
|
|
44
|
+
config.headers['X-Ts'] = Date.now()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return config
|
|
48
|
+
},
|
|
49
|
+
(error: AxiosError) => {
|
|
50
|
+
showToast({
|
|
51
|
+
message: error.message,
|
|
52
|
+
position: 'bottom',
|
|
53
|
+
})
|
|
54
|
+
return Promise.reject(error)
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 响应拦截
|
|
60
|
+
private httpInterceptorsResponse(): void {
|
|
61
|
+
Http.axiosInstance.interceptors.response.use(
|
|
62
|
+
async (response: AxiosResponse) => {
|
|
63
|
+
// 判断是否需要解密
|
|
64
|
+
if (response.headers && response.headers['x-encrypted'] === '1') {
|
|
65
|
+
const v4SessionKey = useUserStore().getSessionKey()
|
|
66
|
+
if (v4SessionKey && response.data) {
|
|
67
|
+
const decryptedData = encryptUtil.decryptResponse(response?.data, v4SessionKey)
|
|
68
|
+
// 如果解密成功且不等于原数据,说明解密有效
|
|
69
|
+
if (decryptedData !== response.data) {
|
|
70
|
+
// 响应解密成功
|
|
71
|
+
response.data = decryptedData
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const compatible = import.meta.env.VITE_APP_COMPATIBLE
|
|
76
|
+
if (compatible !== 'V4') {
|
|
77
|
+
return response.data
|
|
78
|
+
}
|
|
79
|
+
// 与后端协定的返回字段
|
|
80
|
+
const { code, msg, data } = response.data
|
|
81
|
+
// 临时向v3请求上传服务,因为没有code,未来会改
|
|
82
|
+
if (code === undefined) {
|
|
83
|
+
return response.data
|
|
84
|
+
}
|
|
85
|
+
// 判断请求是否成功
|
|
86
|
+
const isSuccess = code === ResultEnum.SUCCESS
|
|
87
|
+
if (isSuccess) {
|
|
88
|
+
// 兼容 V3 请求没有data的情况
|
|
89
|
+
if (data) {
|
|
90
|
+
return data
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return response.data
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
if (code === 401 && response.config.url?.indexOf('af-auth/logout') === -1) {
|
|
98
|
+
showToast({
|
|
99
|
+
message: '登录态已失效,请重新登录',
|
|
100
|
+
position: 'bottom',
|
|
101
|
+
})
|
|
102
|
+
await useUserStore().logout()
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
showToast({
|
|
106
|
+
message: msg,
|
|
107
|
+
position: 'bottom',
|
|
108
|
+
})
|
|
109
|
+
console.error(`请求失败,返回结果:${msg}`)
|
|
110
|
+
}
|
|
111
|
+
return Promise.reject(response.data)
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
(error: AxiosError) => {
|
|
115
|
+
// 处理 HTTP 网络错误
|
|
116
|
+
let message: string = error.response?.data as string
|
|
117
|
+
if (!message) {
|
|
118
|
+
// HTTP 状态码
|
|
119
|
+
const status = error.response?.status
|
|
120
|
+
switch (status) {
|
|
121
|
+
case 400:
|
|
122
|
+
message = '请求错误'
|
|
123
|
+
break
|
|
124
|
+
case 401:
|
|
125
|
+
message = '登录态已失效,请重新登录'
|
|
126
|
+
// 401状态码处理:自动登出并跳转到登录页
|
|
127
|
+
useUserStore().logout()
|
|
128
|
+
break
|
|
129
|
+
case 403:
|
|
130
|
+
message = '拒绝访问'
|
|
131
|
+
break
|
|
132
|
+
case 404:
|
|
133
|
+
message = `请求地址出错: ${error.response?.config?.url}`
|
|
134
|
+
break
|
|
135
|
+
case 408:
|
|
136
|
+
message = '请求超时'
|
|
137
|
+
break
|
|
138
|
+
case 500:
|
|
139
|
+
message = '服务器内部错误'
|
|
140
|
+
break
|
|
141
|
+
case 501:
|
|
142
|
+
message = '服务未实现'
|
|
143
|
+
break
|
|
144
|
+
case 502:
|
|
145
|
+
message = '网关错误'
|
|
146
|
+
break
|
|
147
|
+
case 503:
|
|
148
|
+
message = '服务不可用'
|
|
149
|
+
break
|
|
150
|
+
case 504:
|
|
151
|
+
message = '网关超时'
|
|
152
|
+
break
|
|
153
|
+
case 505:
|
|
154
|
+
message = 'HTTP版本不受支持'
|
|
155
|
+
break
|
|
156
|
+
default:
|
|
157
|
+
message = '网络连接故障'
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
showToast({
|
|
161
|
+
message,
|
|
162
|
+
position: 'bottom',
|
|
163
|
+
})
|
|
164
|
+
return Promise.reject(error)
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
constructor(config: AxiosRequestConfig) {
|
|
170
|
+
Http.axiosConfigDefault = config
|
|
171
|
+
Http.axiosInstance = axios.create(config)
|
|
172
|
+
this.httpInterceptorsRequest()
|
|
173
|
+
this.httpInterceptorsResponse()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 通用请求函数
|
|
177
|
+
public request<T>(paramConfig: AxiosRequestConfig): Promise<T> {
|
|
178
|
+
const config = { ...Http.axiosConfigDefault, ...paramConfig }
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
Http.axiosInstance
|
|
181
|
+
.request(config)
|
|
182
|
+
.then((response: any) => {
|
|
183
|
+
resolve(response)
|
|
184
|
+
})
|
|
185
|
+
.catch((error) => {
|
|
186
|
+
reject(error)
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const METHOD = {
|
|
193
|
+
GET: 'get',
|
|
194
|
+
POST: 'post',
|
|
195
|
+
PUT: 'put',
|
|
196
|
+
DELETE: 'delete',
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* axios请求
|
|
200
|
+
* @param url 请求地址
|
|
201
|
+
* @param method {METHOD} http method
|
|
202
|
+
* @param params 请求参数
|
|
203
|
+
* @param config
|
|
204
|
+
* @returns {Promise<AxiosResponse<T>>}
|
|
205
|
+
*/
|
|
206
|
+
async function request(url, method, params, config) {
|
|
207
|
+
switch (method) {
|
|
208
|
+
case METHOD.GET:
|
|
209
|
+
return axios.get(url, { params, ...config })
|
|
210
|
+
case METHOD.POST:
|
|
211
|
+
return axios.post(url, params, config)
|
|
212
|
+
case METHOD.PUT:
|
|
213
|
+
return axios.put(url, params, config)
|
|
214
|
+
case METHOD.DELETE:
|
|
215
|
+
return axios.delete(url, { params, ...config })
|
|
216
|
+
default:
|
|
217
|
+
return axios.get(url, { params, ...config })
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const http = new Http(configDefault)
|
|
222
|
+
export {
|
|
223
|
+
METHOD,
|
|
224
|
+
request,
|
|
225
|
+
}
|