@epxing/core 5.3.0-beta.1 → 5.3.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +30 -27
- package/README.md +5 -4
- package/dist/.cache/route-chart-release/frontend/frame/public/js/ecStat.min.js +4 -0
- package/dist/.cache/route-chart-release/frontend/frame/public/js/echarts-wordcloud.min.js +4 -0
- package/dist/.cache/route-chart-release/frontend/frame/public/js/echarts.min.js +4 -0
- package/dist/_virtual/_plugin-vue_export-helper.js +10 -0
- package/dist/_virtual/jsencrypt.min.js +1 -1
- package/dist/assets/ecStat.min-BrTYPOXX.js +1 -0
- package/dist/assets/echarts-wordcloud.min-nWR4Hzny.js +3 -0
- package/dist/assets/echarts.min-DROS9E5X.js +45 -0
- package/dist/assets/role_select-DTqRlFNz.webp +0 -0
- package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/jsencrypt@3.5.4/_vendor/jsencrypt/bin/jsencrypt.min.js +1 -1
- package/dist/src/Container.js +1 -0
- package/dist/src/RootApp.js +54 -0
- package/dist/src/api/config.js +1 -0
- package/dist/src/api/createHttp.js +7 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/app-layout.js +1 -0
- package/dist/src/app-ui-settings.js +49 -0
- package/dist/src/app.js +1 -0
- package/dist/src/axios.js +11 -15
- package/dist/src/capture-ref.js +1 -0
- package/dist/src/chart-runtime-assets.js +9 -0
- package/dist/src/components/draggable/index.js +1 -0
- package/dist/src/create-app.js +4 -6
- package/dist/src/directives/clickoutside.js +1 -0
- package/dist/src/directives/global.js +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/lazy-library.js +1 -0
- package/dist/src/loadComponent.js +3 -2
- package/dist/src/loadLibrary.js +1 -0
- package/dist/src/menu-route.js +12 -0
- package/dist/src/mixins/component.js +4 -0
- package/dist/src/mixins/custom-option.js +1 -0
- package/dist/src/mixins/el-locale.js +1 -0
- package/dist/src/mixins/global.js +1 -0
- package/dist/src/mixins/index.js +1 -0
- package/dist/src/mixins/methods-to-prototype.js +1 -0
- package/dist/src/mixins/optimized-global.js +1 -0
- package/dist/src/mixins/popper.js +1 -0
- package/dist/src/navigation-context.js +47 -0
- package/dist/src/navigation.js +44 -12
- package/dist/src/package-install.js +4 -2
- package/dist/src/plugins/element-plus.js +9 -8
- package/dist/src/plugins/i18n.js +1 -0
- package/dist/src/plugins/library-locale.js +1 -0
- package/dist/src/progress-bar.js +1 -0
- package/dist/src/public-api.js +5 -2
- package/dist/src/register-components.js +1 -0
- package/dist/src/remote-deps.js +1 -0
- package/dist/src/route-components.js +9 -0
- package/dist/src/router/config.js +8 -8
- package/dist/src/router/index.js +1 -0
- package/dist/src/router/public.js +1 -0
- package/dist/src/runtime-services.js +2 -1
- package/dist/src/runtime-utils/fieldFactory.js +1 -0
- package/dist/src/runtime-utils/meta.js +4 -0
- package/dist/src/runtime-utils/pdf.viewer.js +32 -15
- package/dist/src/runtime-utils/util.js +30 -5
- package/dist/src/shallow-same.js +1 -0
- package/dist/src/shared-packages.js +2 -3
- package/dist/src/store/actions.js +1 -0
- package/dist/src/store/create-store.js +1 -0
- package/dist/src/store/index.js +1 -0
- package/dist/src/store/lru.js +1 -0
- package/dist/src/store/modules/config.js +1 -0
- package/dist/src/store/modules/file.js +1 -0
- package/dist/src/store/modules/index.js +1 -0
- package/dist/src/store/modules/list.js +26 -3
- package/dist/src/store/modules/locale.js +1 -0
- package/dist/src/store/modules/meta.js +50 -7
- package/dist/src/store/modules/page.js +1 -0
- package/dist/src/store/modules/router.js +1 -0
- package/dist/src/store/modules/tabs.js +1 -0
- package/dist/src/store/modules/user.js +1 -0
- package/dist/src/style-presets.js +5 -4
- package/dist/src/ui-settings.js +91 -18
- package/dist/src/viewport.js +1 -0
- package/dist/src/views/App.js +48 -0
- package/dist/src/views/AppRoleSelect.js +233 -0
- package/dist/src/views/Error.js +210 -0
- package/dist/src/views/Module.js +173 -0
- package/dist/src/vue.bus.js +1 -0
- package/dist/src/vue2-cancel-token-compat.js +1 -0
- package/dist/src/vue2-remote-shim.js +1 -0
- package/dist/src/vue2-render-compat.js +1 -0
- package/dist/src/vue2-select-style-compat.js +1 -0
- package/dist/src/watermark.js +1 -0
- package/dist/src/xss.js +1 -0
- package/dist/src/z-index.js +1 -0
- package/dist/style.css +251 -0
- package/package.json +6 -5
- package/src/RootApp.vue +75 -0
- package/src/api/createHttp.js +7 -0
- package/src/api/index.js +47 -47
- package/src/app-ui-settings.js +31 -0
- package/src/axios.js +311 -318
- package/src/chart-runtime-assets.js +7 -0
- package/src/create-app.js +3 -7
- package/src/index.js +28 -28
- package/src/install.js +157 -157
- package/src/loadComponent.js +187 -187
- package/src/loadLibrary.js +99 -99
- package/src/menu-route.js +12 -1
- package/src/mixins/component.js +1464 -1461
- package/src/navigation-context.js +49 -0
- package/src/navigation.js +208 -176
- package/src/package-install.js +2 -1
- package/src/plugins/element-plus.js +302 -302
- package/src/plugins/frame.js +178 -178
- package/src/public-api.js +1 -0
- package/src/route-components.js +7 -0
- package/src/router/config.js +861 -864
- package/src/router/public.js +1 -1
- package/src/runtime-services.js +1 -1
- package/src/runtime-utils/fieldFactory.js +179 -179
- package/src/runtime-utils/meta.js +399 -378
- package/src/runtime-utils/pdf.viewer.js +672 -644
- package/src/runtime-utils/util.js +1357 -1330
- package/src/shared-packages.js +1 -3
- package/src/store/modules/config.js +351 -351
- package/src/store/modules/list.js +30 -3
- package/src/store/modules/locale.js +236 -236
- package/src/store/modules/meta.js +314 -265
- package/src/style-presets.js +4 -4
- package/src/ui-settings.js +84 -23
- package/src/views/App.vue +53 -0
- package/src/views/AppRoleSelect.vue +334 -0
- package/src/views/Error.vue +63 -0
- package/src/views/Module.vue +210 -0
- package/src/xss.js +26 -26
- package/types/app.d.ts +10 -10
- package/types/axios.d.ts +1 -1
- package/types/component-mixin.d.ts +1 -1
- package/types/field-factory.d.ts +1 -1
- package/types/global-mixin.d.ts +1 -1
- package/types/http.d.ts +1 -1
- package/types/index.d.ts +3 -0
- package/types/meta.d.ts +1 -1
- package/types/navigation.d.ts +21 -21
- package/types/pdf-viewer.d.ts +1 -1
- package/types/runtime.d.ts +13 -13
- package/types/store.d.ts +1 -1
- package/types/util.d.ts +2 -2
- /package/dist/{build → .cache/route-chart-release/frontend/frame/build}/stubs/empty-components.js +0 -0
- /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-icons.names.js +0 -0
- /package/dist/{src → .cache/route-chart-release/frontend/frame/src}/element-plus.lazy.js +0 -0
- /package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/element-plus@2.14.3_vue@3.5.39_typescript@5.9.3_/_vendor/element-plus/dist/locale/en.js +0 -0
- /package/dist/{_vendor → frontend/frame/_vendor}/.pnpm/element-plus@2.14.3_vue@3.5.39_typescript@5.9.3_/_vendor/element-plus/dist/locale/zh-cn.js +0 -0
package/src/axios.js
CHANGED
|
@@ -1,318 +1,311 @@
|
|
|
1
|
-
import axios from 'axios'
|
|
2
|
-
import util from '@epxing/core/src/runtime-utils/util'
|
|
3
|
-
import { getXsrfToken, setXsrfToken } from '@epxing/utils/src/storage'
|
|
4
|
-
import { consumeUrlToken, getStoredToken, setStoredToken, removeStoredToken } from '@epxing/utils/src/token-storage'
|
|
5
|
-
import { ElMessage } from 'element-plus/es/components/message/index.mjs'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
clientCode,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
let
|
|
65
|
-
let
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
import('
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const JSEncrypt = jsEncryptModule.default
|
|
77
|
-
// 取 RSA 公钥(v2 只返回 publicKey,不再下发 aesKey,见 store/modules/config.js GET_DECRYPT_KEY)
|
|
78
|
-
let rsaKey = window.store.state.config.decryptKey.rsaKey
|
|
79
|
-
if (!rsaKey) {
|
|
80
|
-
const decryptKey = await window.store.dispatch('GET_DECRYPT_KEY')
|
|
81
|
-
rsaKey = decryptKey.rsaKey
|
|
82
|
-
}
|
|
83
|
-
// 本地随机生成 AES key(不再从服务器获取、不全局复用明文)
|
|
84
|
-
sessionAesKey = CryptoJS.lib.WordArray.random(16)
|
|
85
|
-
// RSA 公钥加密 AES key,上送 Base64
|
|
86
|
-
const encrypt = new JSEncrypt()
|
|
87
|
-
encrypt.setPublicKey(rsaKey)
|
|
88
|
-
encryptedAesKey = encrypt.encrypt(CryptoJS.enc.Base64.stringify(sessionAesKey))
|
|
89
|
-
return CryptoJS
|
|
90
|
-
})()
|
|
91
|
-
return cryptoReadyPromise
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const init = () => {
|
|
95
|
-
const instance = axios.create({
|
|
96
|
-
timeout: 1200 * 1000,
|
|
97
|
-
baseURL: ''
|
|
98
|
-
})
|
|
99
|
-
instance.defaults.withCredentials = !!window.WITH_CREDENTIALS
|
|
100
|
-
// 站点不使用 Cookie:关闭 axios 内置的「读 XSRF-TOKEN cookie 回填请求头」逻辑,
|
|
101
|
-
// 防 CSRF 的 xsrf_token 改由响应头下发、存 sessionStorage(见下方响应拦截器)
|
|
102
|
-
instance.defaults.xsrfCookieName = null
|
|
103
|
-
// 直接 import axios 的零散调用(如登录页取图形验证码)也走全局默认,一并关掉
|
|
104
|
-
axios.defaults.xsrfCookieName = null
|
|
105
|
-
// 添加请求拦截器
|
|
106
|
-
instance.interceptors.request.use(async config => {
|
|
107
|
-
// 仅旧产品令牌走懒加载垫片,新令牌和 AbortSignal 保持原契约。
|
|
108
|
-
const legacyCancelToken = config.cancelToken
|
|
109
|
-
if ((typeof __EPX_VUE2_COMPAT__ === 'undefined' || __EPX_VUE2_COMPAT__) &&
|
|
110
|
-
window.__EPX_VUE2_COMPAT__ !== false && legacyCancelToken &&
|
|
111
|
-
typeof legacyCancelToken.subscribe !== 'function' && typeof legacyCancelToken.promise?.then === 'function') {
|
|
112
|
-
const { adaptLegacyCancelToken } = await import('./vue2-cancel-token-compat')
|
|
113
|
-
config = adaptLegacyCancelToken(config)
|
|
114
|
-
}
|
|
115
|
-
// 在发送请求之前做些什么
|
|
116
|
-
if (window.IP) {
|
|
117
|
-
config.baseURL = window.IP
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
config.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return Promise
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
return instance
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export default init()
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import util from '@epxing/core/src/runtime-utils/util'
|
|
3
|
+
import { getXsrfToken, setXsrfToken } from '@epxing/utils/src/storage'
|
|
4
|
+
import { consumeUrlToken, getStoredToken, setStoredToken, removeStoredToken } from '@epxing/utils/src/token-storage'
|
|
5
|
+
import { ElMessage } from 'element-plus/es/components/message/index.mjs'
|
|
6
|
+
import { resolveRouteRole } from '@epxing/core/src/menu-route'
|
|
7
|
+
|
|
8
|
+
/* global __EPX_VUE2_COMPAT__ */
|
|
9
|
+
|
|
10
|
+
let message = null
|
|
11
|
+
let timer = null
|
|
12
|
+
const normalizeBearerToken = authorization => {
|
|
13
|
+
if (typeof authorization !== 'string') return ''
|
|
14
|
+
return authorization.replace(/^Bearer\s+/i, '').trim()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const requestAuthorization = config => {
|
|
18
|
+
if (!config || !config.headers) return ''
|
|
19
|
+
return normalizeBearerToken(config.headers.Authorization || config.headers.authorization)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const tokenContext = (config, fallbackClientCode = '') => {
|
|
23
|
+
if (config && config.epxingTokenContext) return config.epxingTokenContext
|
|
24
|
+
const clientCode = fallbackClientCode || ''
|
|
25
|
+
return {
|
|
26
|
+
clientCode,
|
|
27
|
+
storageKey: clientCode + '-token',
|
|
28
|
+
requestToken: requestAuthorization(config)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 一次登录失效响应,只有在它对应的正是"当前生效"的那个 token 时才能清;
|
|
33
|
+
// 否则就是一条晚到的旧请求,把刚登录换来的新 token 误杀
|
|
34
|
+
const canApplyTokenResponse = context => {
|
|
35
|
+
if (!context.requestToken) return true
|
|
36
|
+
return getStoredToken(context.storageKey) === context.requestToken
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const isStaleTokenResponse = context => {
|
|
40
|
+
const currentToken = getStoredToken(context.storageKey)
|
|
41
|
+
return !!(context.requestToken && currentToken && currentToken !== context.requestToken)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const delayToLogin = function (msg) {
|
|
45
|
+
if (timer) clearTimeout(timer)
|
|
46
|
+
timer = setTimeout(() => {
|
|
47
|
+
if (message) message.close()
|
|
48
|
+
message = ElMessage({
|
|
49
|
+
message: msg || '登录失效,请重新登录',
|
|
50
|
+
type: 'error'
|
|
51
|
+
})
|
|
52
|
+
// 存储上一次未登录页面
|
|
53
|
+
if (util.toLogin) {
|
|
54
|
+
util.toLogin()
|
|
55
|
+
}
|
|
56
|
+
}, 100)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ===== 混合加密(RSA + AES)=====
|
|
60
|
+
// 对称密钥不在 server→client 明文传输。
|
|
61
|
+
// AES key 由前端本地随机生成,用服务器 RSA 公钥加密后随请求上送;
|
|
62
|
+
// 服务器用 RSA 私钥解出该 key,再用它加密响应,前端用本地 key/iv 解密。
|
|
63
|
+
let cryptoReadyPromise = null
|
|
64
|
+
let sessionAesKey = null // 本会话本地随机生成的 AES key(CryptoJS WordArray,16 字节 = AES-128)
|
|
65
|
+
let encryptedAesKey = null // RSA 公钥加密后的 AES key(Base64),随请求头上送
|
|
66
|
+
|
|
67
|
+
// 懒加载加密依赖、获取公钥、本地生成并 RSA 加密 AES key(整个会话仅执行一次)
|
|
68
|
+
const ensureCrypto = () => {
|
|
69
|
+
if (cryptoReadyPromise) return cryptoReadyPromise
|
|
70
|
+
cryptoReadyPromise = (async () => {
|
|
71
|
+
const [cryptoModule, jsEncryptModule] = await Promise.all([
|
|
72
|
+
import('crypto-js'),
|
|
73
|
+
import('jsencrypt/bin/jsencrypt.min')
|
|
74
|
+
])
|
|
75
|
+
const CryptoJS = 'default' in cryptoModule ? cryptoModule.default : cryptoModule
|
|
76
|
+
const JSEncrypt = jsEncryptModule.default
|
|
77
|
+
// 取 RSA 公钥(v2 只返回 publicKey,不再下发 aesKey,见 store/modules/config.js GET_DECRYPT_KEY)
|
|
78
|
+
let rsaKey = window.store.state.config.decryptKey.rsaKey
|
|
79
|
+
if (!rsaKey) {
|
|
80
|
+
const decryptKey = await window.store.dispatch('GET_DECRYPT_KEY')
|
|
81
|
+
rsaKey = decryptKey.rsaKey
|
|
82
|
+
}
|
|
83
|
+
// 本地随机生成 AES key(不再从服务器获取、不全局复用明文)
|
|
84
|
+
sessionAesKey = CryptoJS.lib.WordArray.random(16)
|
|
85
|
+
// RSA 公钥加密 AES key,上送 Base64
|
|
86
|
+
const encrypt = new JSEncrypt()
|
|
87
|
+
encrypt.setPublicKey(rsaKey)
|
|
88
|
+
encryptedAesKey = encrypt.encrypt(CryptoJS.enc.Base64.stringify(sessionAesKey))
|
|
89
|
+
return CryptoJS
|
|
90
|
+
})()
|
|
91
|
+
return cryptoReadyPromise
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const init = () => {
|
|
95
|
+
const instance = axios.create({
|
|
96
|
+
timeout: 1200 * 1000,
|
|
97
|
+
baseURL: ''
|
|
98
|
+
})
|
|
99
|
+
instance.defaults.withCredentials = !!window.WITH_CREDENTIALS
|
|
100
|
+
// 站点不使用 Cookie:关闭 axios 内置的「读 XSRF-TOKEN cookie 回填请求头」逻辑,
|
|
101
|
+
// 防 CSRF 的 xsrf_token 改由响应头下发、存 sessionStorage(见下方响应拦截器)
|
|
102
|
+
instance.defaults.xsrfCookieName = null
|
|
103
|
+
// 直接 import axios 的零散调用(如登录页取图形验证码)也走全局默认,一并关掉
|
|
104
|
+
axios.defaults.xsrfCookieName = null
|
|
105
|
+
// 添加请求拦截器
|
|
106
|
+
instance.interceptors.request.use(async config => {
|
|
107
|
+
// 仅旧产品令牌走懒加载垫片,新令牌和 AbortSignal 保持原契约。
|
|
108
|
+
const legacyCancelToken = config.cancelToken
|
|
109
|
+
if ((typeof __EPX_VUE2_COMPAT__ === 'undefined' || __EPX_VUE2_COMPAT__) &&
|
|
110
|
+
window.__EPX_VUE2_COMPAT__ !== false && legacyCancelToken &&
|
|
111
|
+
typeof legacyCancelToken.subscribe !== 'function' && typeof legacyCancelToken.promise?.then === 'function') {
|
|
112
|
+
const { adaptLegacyCancelToken } = await import('./vue2-cancel-token-compat')
|
|
113
|
+
config = adaptLegacyCancelToken(config)
|
|
114
|
+
}
|
|
115
|
+
// 在发送请求之前做些什么
|
|
116
|
+
if (window.IP) {
|
|
117
|
+
config.baseURL = window.IP
|
|
118
|
+
}
|
|
119
|
+
const locationUrl = window.location.hash.split('?')[0]
|
|
120
|
+
const currentRoute = window.router?.currentRoute?.value
|
|
121
|
+
// 首轮导航尚未确认时也按路由定义解析命名参数,不能将菜单路径段猜成角色。
|
|
122
|
+
const route = currentRoute?.name ? currentRoute : window.router?.resolve?.(locationUrl.slice(1)) || currentRoute
|
|
123
|
+
const { name = null, params = {} } = route || {}
|
|
124
|
+
var reg = /(app\/role\/select|hall\/role\/select|app|hall\/login|hall|login|system|client|service\/setting|recruit\/register)\/(\w+)\/?([\w-]+)?\/(\w+)\/?([\w-_]+)?/
|
|
125
|
+
var regResult = reg.exec(locationUrl)
|
|
126
|
+
const regClientCode = regResult ? RegExp.$2 === 'login' ? RegExp.$3 : RegExp.$2 : null
|
|
127
|
+
const regSystemCode = regResult ? RegExp.$3 : null
|
|
128
|
+
const regAppCode = regResult ? RegExp.$1 === 'hall' ? 'FM_SERVICE_HALL' : RegExp.$4 : null
|
|
129
|
+
// 根据sysId获取请求服务
|
|
130
|
+
const { clientConfig = {}, appConfig = {}, temporaryRole } = window.store.state.config
|
|
131
|
+
const { CODE: CLIENT_CODE = '' } = clientConfig
|
|
132
|
+
const { CODE: APP_CODE = '', EXTEND_OF_COMPETENCE } = appConfig
|
|
133
|
+
const clientCode = params.client || regClientCode || CLIENT_CODE
|
|
134
|
+
const appCode = params.app || APP_CODE || regAppCode
|
|
135
|
+
// 角色与导航使用同一来源;大厅没有系统段,不能拿菜单或角色段作为存储作用域。
|
|
136
|
+
let roleCode = resolveRouteRole({ params: { ...params, client: clientCode, system: params.client ? params.system : regSystemCode } })
|
|
137
|
+
if (name === 'Config') roleCode = 'SYSTEM'
|
|
138
|
+
const token = consumeUrlToken(clientCode ? clientCode + '-token' : '')
|
|
139
|
+
if (window.IP && token) {
|
|
140
|
+
config.headers.Authorization = `Bearer ${token}`
|
|
141
|
+
}
|
|
142
|
+
config.epxingTokenContext = {
|
|
143
|
+
clientCode,
|
|
144
|
+
storageKey: clientCode + '-token',
|
|
145
|
+
requestToken: token || ''
|
|
146
|
+
}
|
|
147
|
+
config.headers.Language = config.headers.Language || util.getCurrentLanguage()
|
|
148
|
+
if (name === 'manage' || locationUrl.startsWith('#/manage')) config.headers.Mode = 'manage'
|
|
149
|
+
config.headers.Client = clientCode
|
|
150
|
+
config.headers['App-Agent'] = appCode
|
|
151
|
+
if (params.system || regSystemCode) {
|
|
152
|
+
config.headers.System = params.system || regSystemCode
|
|
153
|
+
}
|
|
154
|
+
if ((EXTEND_OF_COMPETENCE !== 'allRole' && roleCode) || temporaryRole) {
|
|
155
|
+
config.headers.Role = temporaryRole?.code || roleCode
|
|
156
|
+
}
|
|
157
|
+
const xsrfToken = getXsrfToken()
|
|
158
|
+
if (xsrfToken) {
|
|
159
|
+
config.headers.xsrf_token = xsrfToken
|
|
160
|
+
}
|
|
161
|
+
config.params = {
|
|
162
|
+
...config.params,
|
|
163
|
+
nonceStr: util.uuid().replaceAll('-', ''),
|
|
164
|
+
timestamp: new Date().getTime()
|
|
165
|
+
}
|
|
166
|
+
// 混合加密:随请求上送「RSA 加密后的 AES key」+「本次请求随机 IV」,
|
|
167
|
+
// 供服务器在 respEncrypt=YES 时用其加密响应。公钥接口本身不参与,避免递归。
|
|
168
|
+
if (!config.url || !/\/api\/v1\/rsa\/public/.test(config.url)) {
|
|
169
|
+
try {
|
|
170
|
+
const CryptoJS = await ensureCrypto()
|
|
171
|
+
const iv = CryptoJS.lib.WordArray.random(16) // 每次请求新生成 IV,不复用、不等于 key
|
|
172
|
+
config.headers['X-Encrypted-Key'] = encryptedAesKey
|
|
173
|
+
config.headers['X-Encrypt-Iv'] = CryptoJS.enc.Base64.stringify(iv)
|
|
174
|
+
// 暂存本次请求的 key / iv,供响应拦截器解密使用
|
|
175
|
+
config.encryptAesKey = sessionAesKey
|
|
176
|
+
config.encryptIv = iv
|
|
177
|
+
} catch (e) {
|
|
178
|
+
// 加密初始化失败(如公钥获取失败)时不阻断请求,按明文发送
|
|
179
|
+
console.error('混合加密初始化失败', e)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return config
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
instance.interceptors.response.use(async function (response) {
|
|
186
|
+
let params = {}
|
|
187
|
+
if (window.router) {
|
|
188
|
+
params = window.router.currentRoute.value.params
|
|
189
|
+
}
|
|
190
|
+
const reg = /(login|hall\/login|hall|app\/role\/select|app|system|client|service\/setting)\/(\w+)/
|
|
191
|
+
reg.exec(window.location.hash)
|
|
192
|
+
// 根据sysId获取请求服务
|
|
193
|
+
const { 'CLIENT|CODE': CLIENT_CODE = '' } = window.store.state.config.appConfig
|
|
194
|
+
const clientCode = params.client || RegExp.$2 || CLIENT_CODE
|
|
195
|
+
const context = tokenContext(response.config, clientCode)
|
|
196
|
+
const authorization = response.headers.authorization
|
|
197
|
+
if (authorization) {
|
|
198
|
+
const token = normalizeBearerToken(authorization)
|
|
199
|
+
if (token && canApplyTokenResponse(context)) {
|
|
200
|
+
setStoredToken(context.storageKey, token)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// 后端若通过响应头下发防 CSRF 令牌(xsrf_token / X-XSRF-TOKEN),存 sessionStorage,
|
|
204
|
+
// 后续请求由请求拦截器回填到 xsrf_token 请求头;不再依赖服务端 Set-Cookie
|
|
205
|
+
const xsrfHeader = response.headers.xsrf_token || response.headers['x-xsrf-token']
|
|
206
|
+
if (xsrfHeader) setXsrfToken(xsrfHeader)
|
|
207
|
+
const { data = {} } = response
|
|
208
|
+
if (data instanceof Blob) {
|
|
209
|
+
const content = response.headers['content-disposition'] || ''
|
|
210
|
+
const filename = content.replace(/(.+)filename="(.+)"/, '$2')
|
|
211
|
+
return Promise.resolve({ blob: data, filename: decodeURIComponent(filename) })
|
|
212
|
+
} else if (data.code === '0') {
|
|
213
|
+
// 处理成功
|
|
214
|
+
if (data.responseEncrypt) {
|
|
215
|
+
// aes 解密:使用本次请求本地生成、并随请求上送给服务器的 key / iv,
|
|
216
|
+
// 服务器用同一把 key/iv 加密响应。密钥不再由服务器明文下发。
|
|
217
|
+
const key = response.config.encryptAesKey
|
|
218
|
+
const iv = response.config.encryptIv
|
|
219
|
+
return import('crypto-js').then(cryptoModule => {
|
|
220
|
+
const CryptoJS = 'default' in cryptoModule ? cryptoModule.default : cryptoModule
|
|
221
|
+
const decrypted = CryptoJS.AES.decrypt(data.data, key, {
|
|
222
|
+
iv,
|
|
223
|
+
mode: CryptoJS.mode.CBC,
|
|
224
|
+
padding: CryptoJS.pad.Pkcs7
|
|
225
|
+
})
|
|
226
|
+
const decryptedData = JSON.parse(decrypted.toString(CryptoJS.enc.Utf8))
|
|
227
|
+
return Promise.resolve(decryptedData)
|
|
228
|
+
})
|
|
229
|
+
} else {
|
|
230
|
+
return Promise.resolve(data.data)
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
// 处理失败
|
|
234
|
+
if (data.code === 'error.nologin' || data.code === 'login.expired' || data.code === '401') {
|
|
235
|
+
if (isStaleTokenResponse(context)) {
|
|
236
|
+
data.message = data.msg
|
|
237
|
+
return Promise.reject(data)
|
|
238
|
+
}
|
|
239
|
+
removeStoredToken(context.storageKey)
|
|
240
|
+
// 单点登录
|
|
241
|
+
if (data.urlToRedirectTo) {
|
|
242
|
+
window.location.href = data.urlToRedirectTo
|
|
243
|
+
} else {
|
|
244
|
+
delayToLogin(data.msg)
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
data.message = data.msg
|
|
248
|
+
return Promise.reject(data)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}, function (error) {
|
|
252
|
+
// T34 axios 0.19→1.x:取消请求的错误对象从 Cancel(toString()==='Cancel')变成
|
|
253
|
+
// CanceledError(toString()==='CanceledError: canceled')—— 字符串比对会失配,
|
|
254
|
+
// 取消的请求会掉进下面的错误分支弹「处理失败」。改用 axios.isCancel(两个版本都有)。
|
|
255
|
+
if (axios.isCancel(error)) {
|
|
256
|
+
// 取消请求
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
const {
|
|
260
|
+
data = {
|
|
261
|
+
code: '500',
|
|
262
|
+
msg: '处理失败'
|
|
263
|
+
}, status
|
|
264
|
+
} = error.response || {}
|
|
265
|
+
if (status === 304) {
|
|
266
|
+
return Promise.resolve({
|
|
267
|
+
notModified: true,
|
|
268
|
+
etag: error.response.headers.etag || ''
|
|
269
|
+
})
|
|
270
|
+
} else if (data instanceof Blob) {
|
|
271
|
+
const fileReader = new FileReader()
|
|
272
|
+
fileReader.readAsText(data, 'utf-8')
|
|
273
|
+
return new Promise((resolve, reject) => {
|
|
274
|
+
fileReader.onload = function () {
|
|
275
|
+
const errors = JSON.parse(fileReader.result)
|
|
276
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
277
|
+
reject({ message: errors.msg, ...errors })
|
|
278
|
+
}
|
|
279
|
+
})
|
|
280
|
+
} else if (data.code === 'error.nologin' || data.code === 'login.expired' || data.code === '401' || status === 401) {
|
|
281
|
+
let params = {}
|
|
282
|
+
if (window.router) {
|
|
283
|
+
params = window.router.currentRoute.value.params
|
|
284
|
+
}
|
|
285
|
+
const reg = /(login|hall\/login|hall|app\/role\/select|app|system|client|service\/setting)\/(\w+)/
|
|
286
|
+
reg.exec(window.location.hash)
|
|
287
|
+
// 根据sysId获取请求服务
|
|
288
|
+
const { 'CLIENT|CODE': CLIENT_CODE = '' } = window.store.state.config.appConfig
|
|
289
|
+
const clientCode = params.client || RegExp.$2 || CLIENT_CODE
|
|
290
|
+
const context = tokenContext(error.config, clientCode)
|
|
291
|
+
if (isStaleTokenResponse(context)) {
|
|
292
|
+
if (data.msg) data.message = data.msg
|
|
293
|
+
return Promise.reject(data)
|
|
294
|
+
}
|
|
295
|
+
removeStoredToken(context.storageKey)
|
|
296
|
+
// 单点登录
|
|
297
|
+
if (data.urlToRedirectTo) {
|
|
298
|
+
window.location.href = data.urlToRedirectTo
|
|
299
|
+
} else {
|
|
300
|
+
delayToLogin(data.msg)
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
if (data.msg) data.message = data.msg
|
|
304
|
+
return Promise.reject(data)
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
return instance
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export default init()
|