From d6a0ddea4c47e7377aee49718f63315d73c31193 Mon Sep 17 00:00:00 2001 From: Jin Mao Date: Sat, 23 Aug 2025 18:53:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(effects):=20=E6=B7=BB=E5=8A=A0=20Alova=20?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=BA=93=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Alova 客户端类,提供请求拦截器和响应处理功能 - 在 package.json 中添加 Alova相关配置 - 更新 pnpm-workspace.yaml,添加 Alova依赖 --- packages/effects/plugins/package.json | 5 + packages/effects/plugins/src/alova/index.ts | 175 ++++++++++++++++++++ pnpm-workspace.yaml | 1 + 3 files changed, 181 insertions(+) create mode 100644 packages/effects/plugins/src/alova/index.ts diff --git a/packages/effects/plugins/package.json b/packages/effects/plugins/package.json index add6e78e9..bcc1172ca 100644 --- a/packages/effects/plugins/package.json +++ b/packages/effects/plugins/package.json @@ -25,6 +25,10 @@ "./motion": { "types": "./src/motion/index.ts", "default": "./src/motion/index.ts" + }, + "./alova": { + "types": "./src/alova/index.ts", + "default": "./src/alova/index.ts" } }, "dependencies": { @@ -41,6 +45,7 @@ "@vueuse/motion": "catalog:", "echarts": "catalog:", "vue": "catalog:", + "alova": "catalog:", "vxe-pc-ui": "catalog:", "vxe-table": "catalog:" } diff --git a/packages/effects/plugins/src/alova/index.ts b/packages/effects/plugins/src/alova/index.ts new file mode 100644 index 000000000..1164cb3e1 --- /dev/null +++ b/packages/effects/plugins/src/alova/index.ts @@ -0,0 +1,175 @@ +import type { + Alova, + AlovaGenerics, + AlovaMethodCommonConfig, + AlovaMethodCreateConfig, + AlovaOptions, + Method, + RequestBody, + RespondedAlovaGenerics, + ResponseCompleteHandler, + ResponseErrorHandler, +} from 'alova'; +import type { + AlovaRequestAdapterUnified, + ClientTokenAuthenticationOptions, +} from 'alova/client'; + +import { createAlova } from 'alova'; +import { createServerTokenAuthentication } from 'alova/client'; +import adapterFetch from 'alova/fetch'; +import VueHook from 'alova/vue'; + +type RequestMethod = (method: Method) => Promise | void; + +class AlovaClient { + public readonly instance: Alova; + + public requestInterceptor: RequestMethod[] = []; + public responseCompleteInterceptor: ResponseCompleteHandler[] = + []; + public responseErrorInterceptor: ResponseErrorHandler[] = []; + // public responseSuccessInterceptor: RespondedHandler[] = []; + public responseSuccessInterceptor: (( + Json: Record, + ) => Promise>)[] = []; + constructor( + options: AlovaOptions, + tokenOptions?: ClientTokenAuthenticationOptions, + ) { + const { baseURL = '' } = options; + // const { onAuthRequired, onResponseRefreshToken } = + // createClientTokenAuthentication({ + // // ... + // ...tokenOptions, + // }); + const { onAuthRequired, onResponseRefreshToken } = + createServerTokenAuthentication({ + // ... + ...tokenOptions, + }); + + this.instance = createAlova({ + baseURL, + requestAdapter: adapterFetch(), + statesHook: VueHook, + beforeRequest: onAuthRequired(async (method) => { + for (const interceptor of this.requestInterceptor) { + await interceptor(method); + } + }), + // 使用 responded 对象分别指定请求成功的拦截器和请求失败的拦截器 + responded: onResponseRefreshToken({ + // 请求成功的拦截器 + // 当使用 `alova/fetch` 请求适配器时,第一个参数接收Response对象 + // 第二个参数为当前请求的method实例,你可以用它同步请求前后的配置信息 + onSuccess: async (response, method) => { + if (response.status >= 400) { + throw new Error(response.statusText); + } + const json = await response.json(); + + let result; + for (const interceptor of this.responseSuccessInterceptor) { + result = await interceptor(json); + } + + return result; + }, + + onError: async (err, method) => { + console.log(222); + for (const interceptor of this.responseErrorInterceptor) { + await interceptor(err, method); + } + }, + + // 请求完成的拦截器 + // 当你需要在请求不论是成功、失败、还是命中缓存都需要执行的逻辑时,可以在创建alova实例时指定全局的`onComplete`拦截器,例如关闭请求 loading 状态。 + // 接收当前请求的method实例 + onComplete: async (method) => { + // 处理请求完成逻辑 + for (const interceptor of this.responseCompleteInterceptor) { + await interceptor(method); + } + }, + }), + }); + } + public addRequestInterceptor( + method: (method: Method) => Promise | void, + ) { + this.requestInterceptor.push(method); + } + + public addResponseCompleteInterceptor( + method: ResponseCompleteHandler, + ) { + this.responseCompleteInterceptor.push(method); + } + public addResponseErrorInterceptor( + method: ResponseErrorHandler, + ) { + this.responseErrorInterceptor.push(method); + } + public addResponseSuccessInterceptor( + method: (Json: Record) => Promise>, + ) { + this.responseSuccessInterceptor.push(method); + } + public delete( + url: string, + data?: RequestBody, + config?: AlovaMethodCreateConfig, + ): Method> { + return this.instance.Delete(url, config, data); + } + public get( + url: string, + config?: AlovaMethodCreateConfig, + ): Method> { + return this.instance.Get(url, config); + } + public head( + url: string, + config?: AlovaMethodCreateConfig, + ): Method> { + return this.instance.Head(url, config); + } + public options( + url: string, + config?: AlovaMethodCreateConfig, + ): Method> { + return this.instance.Options(url, config); + } + public patch( + url: string, + data?: RequestBody, + config?: AlovaMethodCreateConfig, + ): Method> { + return this.instance.Patch(url, data, config); + } + public post( + url: string, + data?: RequestBody, + config?: AlovaMethodCreateConfig, + ): Method> { + return this.instance.Post(url, data, config); + } + public put( + url: string, + data?: RequestBody, + config?: AlovaMethodCreateConfig, + ): Method> { + return this.instance.Put(url, data, config); + } + public request( + config: AlovaMethodCommonConfig, + ): Method> { + return this.instance.Request(config); + } +} + +export { AlovaClient }; +export * from 'alova'; +export * from 'alova/client'; diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b87283b6d..58f438586 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -73,6 +73,7 @@ catalog: axios-mock-adapter: ^2.1.0 cac: ^6.7.14 chalk: ^5.4.1 + alova: ^3.3.4 cheerio: ^1.1.0 circular-dependency-scanner: ^2.3.0 class-variance-authority: ^0.7.1