vue2server7 7.0.14 → 7.0.16
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/frontEnd/src/pages/PositionReportPage.vue +102 -65
- package/package.json +1 -1
- package/test/1/main.ts +52 -0
- package/test/1/utils/charts.ts +38 -0
- package/test/1/utils/color.ts +110 -0
- package/test/1/utils/date.ts +9 -0
- package/test/1/utils/request/Axios.ts +294 -0
- package/test/1/utils/request/AxiosCancel.ts +67 -0
- package/test/1/utils/request/AxiosTransform.ts +63 -0
- package/test/1/utils/request/index.ts +231 -0
- package/test/1/utils/request/utils.ts +53 -0
- package/test/1/utils/route/constant.ts +14 -0
- package/test/1/utils/route/index.ts +110 -0
- package/test/1/utils/version-check.worker.ts +58 -0
- package/test/1/utils/version-update.ts +135 -0
- package/test/1/version-hot-update.md +277 -0
- package/test/1/vite.config.ts +46 -0
|
@@ -257,71 +257,108 @@
|
|
|
257
257
|
</section>
|
|
258
258
|
</template>
|
|
259
259
|
|
|
260
|
-
<script>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
260
|
+
<script setup lang="ts">
|
|
261
|
+
import { reactive, ref } from 'vue'
|
|
262
|
+
import { useRouter } from 'vue-router'
|
|
263
|
+
import { ElMessage } from 'element-plus'
|
|
264
|
+
import type { FormInstance, FormRules } from 'element-plus'
|
|
265
|
+
|
|
266
|
+
const router = useRouter()
|
|
267
|
+
const formRef = ref<FormInstance>()
|
|
268
|
+
|
|
269
|
+
interface ReportForm {
|
|
270
|
+
reportNo: string
|
|
271
|
+
bizType: string
|
|
272
|
+
customerType: string
|
|
273
|
+
isGroup: string
|
|
274
|
+
orgCode: string
|
|
275
|
+
customerName: string
|
|
276
|
+
currency: string
|
|
277
|
+
amount: string
|
|
278
|
+
valueDate: string
|
|
279
|
+
accountName: string
|
|
280
|
+
planDate: string
|
|
281
|
+
prepareDate: string
|
|
282
|
+
createNo: string
|
|
283
|
+
isNegotiate: string
|
|
284
|
+
origBizNo: string
|
|
285
|
+
linkedBank: string
|
|
286
|
+
receivingBank: string
|
|
287
|
+
receivingBankCode: string
|
|
288
|
+
receivingBankName: string
|
|
289
|
+
emailProduct: string
|
|
290
|
+
multiCurrencyProduct: string
|
|
291
|
+
ftzFlag: boolean
|
|
292
|
+
reporter: string
|
|
293
|
+
phone: string
|
|
294
|
+
department: string
|
|
295
|
+
remark: string
|
|
296
|
+
register: string
|
|
297
|
+
useStatus: string
|
|
298
|
+
currentStatus: string
|
|
299
|
+
bizRegion: string
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const form = reactive<ReportForm>({
|
|
303
|
+
reportNo: '',
|
|
304
|
+
bizType: '跨境人民币业务(进口)',
|
|
305
|
+
customerType: '对公',
|
|
306
|
+
isGroup: '',
|
|
307
|
+
orgCode: '5903887-5',
|
|
308
|
+
customerName: '长期应收长期预收客户(5927)',
|
|
309
|
+
currency: 'CNY',
|
|
310
|
+
amount: '100,000,000.00',
|
|
311
|
+
valueDate: '',
|
|
312
|
+
accountName: '',
|
|
313
|
+
planDate: '2026-03-23',
|
|
314
|
+
prepareDate: '2026-03-23',
|
|
315
|
+
createNo: '183423',
|
|
316
|
+
isNegotiate: '否',
|
|
317
|
+
origBizNo: '',
|
|
318
|
+
linkedBank: 'CIPSCNSHXXX',
|
|
319
|
+
receivingBank: '',
|
|
320
|
+
receivingBankCode: 'BKCHCNBJXXX',
|
|
321
|
+
receivingBankName: '中国银行国际金融有限公司',
|
|
322
|
+
emailProduct: '',
|
|
323
|
+
multiCurrencyProduct: '',
|
|
324
|
+
ftzFlag: false,
|
|
325
|
+
reporter: '0601bx',
|
|
326
|
+
phone: '13051636/66',
|
|
327
|
+
department: 'HGS1:利率及外汇业务处',
|
|
328
|
+
remark: '',
|
|
329
|
+
register: '登记(后端周四)',
|
|
330
|
+
useStatus: '未使用',
|
|
331
|
+
currentStatus: '自行撤区',
|
|
332
|
+
bizRegion: '报关系统'
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
const rules: FormRules<ReportForm> = {
|
|
336
|
+
customerType: [{ required: true, message: '请选择客户分类', trigger: 'change' }],
|
|
337
|
+
orgCode: [{ required: true, message: '请输入对公组织架构代码', trigger: 'blur' }],
|
|
338
|
+
customerName: [{ required: true, message: '请输入客户名称', trigger: 'blur' }],
|
|
339
|
+
currency: [{ required: true, message: '请选择币种', trigger: 'change' }],
|
|
340
|
+
planDate: [{ required: true, message: '请选择计划日期', trigger: 'change' }],
|
|
341
|
+
reporter: [{ required: true, message: '请输入报备人', trigger: 'blur' }],
|
|
342
|
+
phone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }]
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function onFlowLog() {
|
|
346
|
+
ElMessage.info('查看操作流水')
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function onSubmit() {
|
|
350
|
+
formRef.value?.validate((valid) => {
|
|
351
|
+
if (!valid) return
|
|
352
|
+
ElMessage.success('提交成功')
|
|
353
|
+
})
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function onSave() {
|
|
357
|
+
ElMessage.success('保存成功')
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function onClose() {
|
|
361
|
+
router.back()
|
|
325
362
|
}
|
|
326
363
|
</script>
|
|
327
364
|
|
package/package.json
CHANGED
package/test/1/main.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 应用入口文件
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 创建 Vue 应用实例
|
|
6
|
+
* 2. 注册全局插件(UI 库、状态管理、路由、国际化)
|
|
7
|
+
* 3. 加载全局样式
|
|
8
|
+
* 4. 挂载到 DOM
|
|
9
|
+
* 5. 启动版本热更新检测
|
|
10
|
+
*
|
|
11
|
+
* 导入顺序说明(eslint simple-import-sort 已禁用):
|
|
12
|
+
* - 框架核心 → 第三方库 → 业务模块 → 样式 → 副作用模块
|
|
13
|
+
* 此处手动控制顺序以确保 permission 等副作用模块在路由注册之后加载
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* eslint-disable simple-import-sort/imports */
|
|
17
|
+
import { createApp } from 'vue';
|
|
18
|
+
import TDesign from 'tdesign-vue-next';
|
|
19
|
+
|
|
20
|
+
import App from './App.vue';
|
|
21
|
+
import router from './router';
|
|
22
|
+
import { store } from './store';
|
|
23
|
+
import i18n from './locales';
|
|
24
|
+
|
|
25
|
+
/** TDesign 组件库全局样式 */
|
|
26
|
+
import 'tdesign-vue-next/es/style/index.css';
|
|
27
|
+
/** 项目自定义全局样式(变量覆盖、通用布局等) */
|
|
28
|
+
import '@/style/index.less';
|
|
29
|
+
/**
|
|
30
|
+
* 路由权限守卫(副作用模块)
|
|
31
|
+
* 导入即执行 router.beforeEach,必须在 router 创建之后引入
|
|
32
|
+
*/
|
|
33
|
+
import './permission';
|
|
34
|
+
|
|
35
|
+
const app = createApp(App);
|
|
36
|
+
|
|
37
|
+
app.use(TDesign); // TDesign 组件全局注册(Dialog、Message 等可通过插件式 API 调用)
|
|
38
|
+
app.use(store); // Pinia 状态管理
|
|
39
|
+
app.use(router); // Vue Router
|
|
40
|
+
app.use(i18n); // Vue I18n 国际化
|
|
41
|
+
|
|
42
|
+
/** 挂载到 public/index.html 中的 #app 节点 */
|
|
43
|
+
app.mount('#app');
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 版本热更新检测
|
|
47
|
+
* 放在 mount 之后执行,确保 DOM 中的 <meta name="app-version"> 已就绪
|
|
48
|
+
* 详见 @/utils/version-update.ts
|
|
49
|
+
*/
|
|
50
|
+
import { initVersionCheck } from './utils/version-update';
|
|
51
|
+
|
|
52
|
+
initVersionCheck();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 获取表头数据
|
|
5
|
+
*
|
|
6
|
+
* @export
|
|
7
|
+
* @param {string[]} dateTime
|
|
8
|
+
* @param {number} divideNum
|
|
9
|
+
* @returns {string[]} timeArray
|
|
10
|
+
*/
|
|
11
|
+
export function getDateArray(dateTime: string[] = [], divideNum = 10): string[] {
|
|
12
|
+
const timeArray: string[] = [];
|
|
13
|
+
if (dateTime.length > 0) {
|
|
14
|
+
for (let i = 0; i < divideNum; i++) {
|
|
15
|
+
const dateAbsTime: number = (new Date(dateTime[1]).getTime() - new Date(dateTime[0]).getTime()) / divideNum;
|
|
16
|
+
const enhandTime: number = new Date(dateTime[0]).getTime() + dateAbsTime * i;
|
|
17
|
+
timeArray.push(dayjs(enhandTime).format('YYYY-MM-DD'));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return timeArray;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 获取随机数
|
|
26
|
+
*
|
|
27
|
+
* @param {number} [num]
|
|
28
|
+
* @returns {number} resultNum
|
|
29
|
+
*/
|
|
30
|
+
export function getRandomArray(num = 100): number {
|
|
31
|
+
let resultNum = Number((Math.random() * num).toFixed(0));
|
|
32
|
+
|
|
33
|
+
if (resultNum <= 1) {
|
|
34
|
+
resultNum = 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return resultNum;
|
|
38
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type * as echarts from 'echarts/core';
|
|
2
|
+
import trim from 'lodash/trim';
|
|
3
|
+
import { Color } from 'tvision-color';
|
|
4
|
+
|
|
5
|
+
import type { TColorToken } from '@/config/color';
|
|
6
|
+
import type { ModeType } from '@/types/interface';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 依据主题类型获取颜色
|
|
10
|
+
*
|
|
11
|
+
* @export
|
|
12
|
+
* @returns {Array<string>} themeColorList
|
|
13
|
+
*/
|
|
14
|
+
export function getColorFromTheme(): Array<string> {
|
|
15
|
+
const theme = trim(getComputedStyle(document.documentElement).getPropertyValue('--td-brand-color'));
|
|
16
|
+
const themeColorList = Color.getRandomPalette({
|
|
17
|
+
color: theme,
|
|
18
|
+
colorGamut: 'bright',
|
|
19
|
+
number: 8,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return themeColorList;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 图表颜色 */
|
|
26
|
+
export function getChartListColor(): Array<string> {
|
|
27
|
+
return getColorFromTheme();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 更改图表主题颜色
|
|
32
|
+
*
|
|
33
|
+
* @export
|
|
34
|
+
* @param {Array<string>} chartsList
|
|
35
|
+
*/
|
|
36
|
+
export function changeChartsTheme(chartsList: echarts.EChartsType[]): void {
|
|
37
|
+
if (chartsList && chartsList.length) {
|
|
38
|
+
const chartChangeColor = getChartListColor();
|
|
39
|
+
|
|
40
|
+
for (let index = 0; index < chartsList.length; index++) {
|
|
41
|
+
const elementChart = chartsList[index];
|
|
42
|
+
|
|
43
|
+
if (elementChart) {
|
|
44
|
+
const optionVal = elementChart.getOption();
|
|
45
|
+
|
|
46
|
+
// 更改主题颜色
|
|
47
|
+
optionVal.color = chartChangeColor;
|
|
48
|
+
|
|
49
|
+
elementChart.setOption(optionVal, true);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 根据当前主题色、模式等情景 计算最后生成的色阶
|
|
57
|
+
*/
|
|
58
|
+
export function generateColorMap(theme: string, colorPalette: Array<string>, mode: ModeType, brandColorIdx: number) {
|
|
59
|
+
const isDarkMode = mode === 'dark';
|
|
60
|
+
|
|
61
|
+
if (isDarkMode) {
|
|
62
|
+
colorPalette.reverse().map((color) => {
|
|
63
|
+
const [h, s, l] = Color.colorTransform(color, 'hex', 'hsl');
|
|
64
|
+
return Color.colorTransform([h, Number(s) - 4, l], 'hsl', 'hex');
|
|
65
|
+
});
|
|
66
|
+
brandColorIdx = 5;
|
|
67
|
+
colorPalette[0] = `${colorPalette[brandColorIdx]}20`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const colorMap: TColorToken = {
|
|
71
|
+
'--td-brand-color': colorPalette[brandColorIdx], // 主题色
|
|
72
|
+
'--td-brand-color-1': colorPalette[0], // light
|
|
73
|
+
'--td-brand-color-2': colorPalette[1], // focus
|
|
74
|
+
'--td-brand-color-3': colorPalette[2], // disabled
|
|
75
|
+
'--td-brand-color-4': colorPalette[3],
|
|
76
|
+
'--td-brand-color-5': colorPalette[4],
|
|
77
|
+
'--td-brand-color-6': colorPalette[5],
|
|
78
|
+
'--td-brand-color-7': brandColorIdx > 0 ? colorPalette[brandColorIdx - 1] : theme, // hover
|
|
79
|
+
'--td-brand-color-8': colorPalette[brandColorIdx], // 主题色
|
|
80
|
+
'--td-brand-color-9': brandColorIdx > 8 ? theme : colorPalette[brandColorIdx + 1], // click
|
|
81
|
+
'--td-brand-color-10': colorPalette[9],
|
|
82
|
+
};
|
|
83
|
+
return colorMap;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 将生成的样式嵌入头部
|
|
88
|
+
*/
|
|
89
|
+
export function insertThemeStylesheet(theme: string, colorMap: TColorToken, mode: ModeType) {
|
|
90
|
+
const isDarkMode = mode === 'dark';
|
|
91
|
+
const root = !isDarkMode ? `:root[theme-color='${theme}']` : `:root[theme-color='${theme}'][theme-mode='dark']`;
|
|
92
|
+
|
|
93
|
+
const styleSheet = document.createElement('style');
|
|
94
|
+
styleSheet.type = 'text/css';
|
|
95
|
+
styleSheet.textContent = `${root}{
|
|
96
|
+
--td-brand-color: ${colorMap['--td-brand-color']};
|
|
97
|
+
--td-brand-color-1: ${colorMap['--td-brand-color-1']};
|
|
98
|
+
--td-brand-color-2: ${colorMap['--td-brand-color-2']};
|
|
99
|
+
--td-brand-color-3: ${colorMap['--td-brand-color-3']};
|
|
100
|
+
--td-brand-color-4: ${colorMap['--td-brand-color-4']};
|
|
101
|
+
--td-brand-color-5: ${colorMap['--td-brand-color-5']};
|
|
102
|
+
--td-brand-color-6: ${colorMap['--td-brand-color-6']};
|
|
103
|
+
--td-brand-color-7: ${colorMap['--td-brand-color-7']};
|
|
104
|
+
--td-brand-color-8: ${colorMap['--td-brand-color-8']};
|
|
105
|
+
--td-brand-color-9: ${colorMap['--td-brand-color-9']};
|
|
106
|
+
--td-brand-color-10: ${colorMap['--td-brand-color-10']};
|
|
107
|
+
}`;
|
|
108
|
+
|
|
109
|
+
document.head.appendChild(styleSheet);
|
|
110
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// 获取常用时间
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
|
|
4
|
+
export const LAST_7_DAYS = [dayjs().subtract(6, 'day').format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD')];
|
|
5
|
+
|
|
6
|
+
export const LAST_30_DAYS = [
|
|
7
|
+
dayjs().subtract(30, 'day').format('YYYY-MM-DD'),
|
|
8
|
+
dayjs().subtract(1, 'day').format('YYYY-MM-DD'),
|
|
9
|
+
];
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AxiosError,
|
|
3
|
+
AxiosInstance,
|
|
4
|
+
AxiosRequestConfig,
|
|
5
|
+
AxiosRequestHeaders,
|
|
6
|
+
AxiosResponse,
|
|
7
|
+
InternalAxiosRequestConfig,
|
|
8
|
+
} from 'axios';
|
|
9
|
+
import axios from 'axios';
|
|
10
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
11
|
+
import debounce from 'lodash/debounce';
|
|
12
|
+
import isFunction from 'lodash/isFunction';
|
|
13
|
+
import throttle from 'lodash/throttle';
|
|
14
|
+
import { stringify } from 'qs';
|
|
15
|
+
|
|
16
|
+
import { ContentTypeEnum } from '@/constants';
|
|
17
|
+
import type { AxiosRequestConfigRetry, RequestOptions, Result } from '@/types/axios';
|
|
18
|
+
|
|
19
|
+
import { AxiosCanceler } from './AxiosCancel';
|
|
20
|
+
import type { CreateAxiosOptions } from './AxiosTransform';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Axios 模块
|
|
24
|
+
*/
|
|
25
|
+
export class VAxios {
|
|
26
|
+
/**
|
|
27
|
+
* Axios实例句柄
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
private instance: AxiosInstance;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Axios配置
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
private readonly options: CreateAxiosOptions;
|
|
37
|
+
|
|
38
|
+
constructor(options: CreateAxiosOptions) {
|
|
39
|
+
this.options = options;
|
|
40
|
+
this.instance = axios.create(options);
|
|
41
|
+
this.setupInterceptors();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 创建Axios实例
|
|
46
|
+
* @param config
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
private createAxios(config: CreateAxiosOptions): void {
|
|
50
|
+
this.instance = axios.create(config);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 获取数据处理类
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
private getTransform() {
|
|
58
|
+
const { transform } = this.options;
|
|
59
|
+
return transform;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 获取Axios实例
|
|
64
|
+
*/
|
|
65
|
+
getAxios(): AxiosInstance {
|
|
66
|
+
return this.instance;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 配置Axios
|
|
71
|
+
* @param config
|
|
72
|
+
*/
|
|
73
|
+
configAxios(config: CreateAxiosOptions) {
|
|
74
|
+
if (!this.instance) return;
|
|
75
|
+
this.createAxios(config);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 设置公共头部信息
|
|
80
|
+
* @param headers
|
|
81
|
+
*/
|
|
82
|
+
setHeader(headers: Record<string, string>): void {
|
|
83
|
+
if (!this.instance) return;
|
|
84
|
+
Object.assign(this.instance.defaults.headers, headers);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 设置拦截器
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
private setupInterceptors() {
|
|
92
|
+
const transform = this.getTransform();
|
|
93
|
+
if (!transform) return;
|
|
94
|
+
|
|
95
|
+
const { requestInterceptors, requestInterceptorsCatch, responseInterceptors, responseInterceptorsCatch } =
|
|
96
|
+
transform;
|
|
97
|
+
const axiosCanceler = new AxiosCanceler();
|
|
98
|
+
|
|
99
|
+
// 请求拦截器
|
|
100
|
+
this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
|
101
|
+
// 如果忽略取消令牌,则不会取消重复的请求
|
|
102
|
+
// @ts-expect-error 请求参数通过嵌套赋值
|
|
103
|
+
const { ignoreCancelToken } = config.requestOptions;
|
|
104
|
+
const ignoreCancel = ignoreCancelToken ?? this.options.requestOptions?.ignoreCancelToken;
|
|
105
|
+
if (!ignoreCancel) axiosCanceler.addPending(config);
|
|
106
|
+
|
|
107
|
+
if (requestInterceptors && isFunction(requestInterceptors)) {
|
|
108
|
+
config = requestInterceptors(config, this.options) as InternalAxiosRequestConfig;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return config;
|
|
112
|
+
}, undefined);
|
|
113
|
+
|
|
114
|
+
// 请求错误处理
|
|
115
|
+
if (requestInterceptorsCatch && isFunction(requestInterceptorsCatch)) {
|
|
116
|
+
this.instance.interceptors.request.use(undefined, requestInterceptorsCatch);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 响应结果处理
|
|
120
|
+
this.instance.interceptors.response.use((res: AxiosResponse) => {
|
|
121
|
+
if (res) axiosCanceler.removePending(res.config);
|
|
122
|
+
if (responseInterceptors && isFunction(responseInterceptors)) {
|
|
123
|
+
res = responseInterceptors(res);
|
|
124
|
+
}
|
|
125
|
+
return res;
|
|
126
|
+
}, undefined);
|
|
127
|
+
|
|
128
|
+
// 响应错误处理
|
|
129
|
+
if (responseInterceptorsCatch && isFunction(responseInterceptorsCatch)) {
|
|
130
|
+
this.instance.interceptors.response.use(undefined, (error) => responseInterceptorsCatch(error, this.instance));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 支持 FormData 请求格式
|
|
136
|
+
* @param config
|
|
137
|
+
*/
|
|
138
|
+
supportFormData(config: AxiosRequestConfig) {
|
|
139
|
+
const headers = config.headers || (this.options.headers as AxiosRequestHeaders);
|
|
140
|
+
const contentType = headers?.['Content-Type'] || headers?.['content-type'];
|
|
141
|
+
|
|
142
|
+
if (
|
|
143
|
+
contentType !== ContentTypeEnum.FormURLEncoded ||
|
|
144
|
+
!Reflect.has(config, 'data') ||
|
|
145
|
+
config.method?.toUpperCase() === 'GET'
|
|
146
|
+
) {
|
|
147
|
+
return config;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
...config,
|
|
152
|
+
data: stringify(config.data, { arrayFormat: 'brackets' }),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 支持 params 序列化
|
|
158
|
+
* @param config
|
|
159
|
+
*/
|
|
160
|
+
supportParamsStringify(config: AxiosRequestConfig) {
|
|
161
|
+
const headers = config.headers || this.options.headers;
|
|
162
|
+
const contentType = headers?.['Content-Type'] || headers?.['content-type'];
|
|
163
|
+
|
|
164
|
+
if (contentType === ContentTypeEnum.FormURLEncoded || !Reflect.has(config, 'params')) {
|
|
165
|
+
return config;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
...config,
|
|
170
|
+
paramsSerializer: (params: any) => stringify(params, { arrayFormat: 'brackets' }),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
|
175
|
+
return this.request({ ...config, method: 'GET' }, options);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
post<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
|
179
|
+
return this.request({ ...config, method: 'POST' }, options);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
put<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
|
183
|
+
return this.request({ ...config, method: 'PUT' }, options);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
delete<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
|
187
|
+
return this.request({ ...config, method: 'DELETE' }, options);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
patch<T = any>(config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
|
191
|
+
return this.request({ ...config, method: 'PATCH' }, options);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 上传文件封装
|
|
196
|
+
* @param key 文件所属的key
|
|
197
|
+
* @param file 文件
|
|
198
|
+
* @param config 请求配置
|
|
199
|
+
* @param options
|
|
200
|
+
*/
|
|
201
|
+
upload<T = any>(key: string, file: File, config: AxiosRequestConfig, options?: RequestOptions): Promise<T> {
|
|
202
|
+
const params: FormData = config.params ?? new FormData();
|
|
203
|
+
params.append(key, file);
|
|
204
|
+
|
|
205
|
+
return this.request(
|
|
206
|
+
{
|
|
207
|
+
...config,
|
|
208
|
+
method: 'POST',
|
|
209
|
+
headers: {
|
|
210
|
+
'Content-Type': ContentTypeEnum.FormData,
|
|
211
|
+
},
|
|
212
|
+
params,
|
|
213
|
+
},
|
|
214
|
+
options,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 请求封装
|
|
220
|
+
* @param config
|
|
221
|
+
* @param options
|
|
222
|
+
*/
|
|
223
|
+
request<T = any>(config: AxiosRequestConfigRetry, options?: RequestOptions): Promise<T> {
|
|
224
|
+
const { requestOptions } = this.options;
|
|
225
|
+
|
|
226
|
+
if (requestOptions.throttle !== undefined && requestOptions.debounce !== undefined) {
|
|
227
|
+
throw new Error('throttle and debounce cannot be set at the same time');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (requestOptions.throttle && requestOptions.throttle.delay !== 0) {
|
|
231
|
+
return new Promise((resolve) => {
|
|
232
|
+
throttle(() => resolve(this.synthesisRequest(config, options)), requestOptions.throttle.delay);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (requestOptions.debounce && requestOptions.debounce.delay !== 0) {
|
|
237
|
+
return new Promise((resolve) => {
|
|
238
|
+
debounce(() => resolve(this.synthesisRequest(config, options)), requestOptions.debounce.delay);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return this.synthesisRequest(config, options);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 请求方法
|
|
247
|
+
* @private
|
|
248
|
+
*/
|
|
249
|
+
private async synthesisRequest<T = any>(config: AxiosRequestConfigRetry, options?: RequestOptions): Promise<T> {
|
|
250
|
+
let conf: CreateAxiosOptions = cloneDeep(config);
|
|
251
|
+
const transform = this.getTransform();
|
|
252
|
+
|
|
253
|
+
const { requestOptions } = this.options;
|
|
254
|
+
|
|
255
|
+
const opt: RequestOptions = { ...requestOptions, ...options };
|
|
256
|
+
|
|
257
|
+
const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {};
|
|
258
|
+
if (beforeRequestHook && isFunction(beforeRequestHook)) {
|
|
259
|
+
conf = beforeRequestHook(conf, opt);
|
|
260
|
+
}
|
|
261
|
+
conf.requestOptions = opt;
|
|
262
|
+
|
|
263
|
+
conf = this.supportFormData(conf);
|
|
264
|
+
// 支持params数组参数格式化,因axios默认的toFormData即为brackets方式,无需配置paramsSerializer为qs,有需要可解除注释,参数参考qs文档
|
|
265
|
+
// conf = this.supportParamsStringify(conf);
|
|
266
|
+
|
|
267
|
+
return new Promise((resolve, reject) => {
|
|
268
|
+
this.instance
|
|
269
|
+
.request<any, AxiosResponse<Result>>(!config.retryCount ? conf : config)
|
|
270
|
+
.then((res: AxiosResponse<Result>) => {
|
|
271
|
+
if (transformRequestHook && isFunction(transformRequestHook)) {
|
|
272
|
+
try {
|
|
273
|
+
const ret = transformRequestHook(res, opt);
|
|
274
|
+
resolve(ret);
|
|
275
|
+
} catch (err) {
|
|
276
|
+
reject(err || new Error('请求错误!'));
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
resolve(res as unknown as Promise<T>);
|
|
281
|
+
})
|
|
282
|
+
.catch((e: Error | AxiosError) => {
|
|
283
|
+
if (requestCatchHook && isFunction(requestCatchHook)) {
|
|
284
|
+
reject(requestCatchHook(e, opt));
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (axios.isAxiosError(e)) {
|
|
288
|
+
// 在这里重写Axios的错误信息
|
|
289
|
+
}
|
|
290
|
+
reject(e);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|