diff --git a/public/data.json b/public/data.json new file mode 100644 index 0000000..e2465ec --- /dev/null +++ b/public/data.json @@ -0,0 +1,3 @@ +{ + "aa": "1231" +} \ No newline at end of file diff --git a/src/plugins/http/base.ts b/src/plugins/http/base.ts index ed4200c..2ed8679 100644 --- a/src/plugins/http/base.ts +++ b/src/plugins/http/base.ts @@ -27,7 +27,7 @@ export class Plugin implements IPlugin { export abstract class PluginsManager { static plugins: IPlugin[] = [] - static use(plugin: IPlugin) { + static use(plugin: IPlugin | IPlugin[]) { if (Array.isArray(plugin)) { PluginsManager.plugins = this.plugins.concat(plugin) } else PluginsManager.plugins.push(plugin) @@ -75,7 +75,7 @@ export abstract class httpBase extends PluginsManager { }, error => { const argu = { error } this.callPlugin("beforeRequestError", argu) - return Promise.reject(error) + return Promise.reject(argu.error) }) this.responseInterceptorId = this.instance.interceptors.response.use(response => { const argu = { response } @@ -84,7 +84,7 @@ export abstract class httpBase extends PluginsManager { }, (error) => { const argu = { error } this.callPlugin("beforeResponseError", argu) - return Promise.reject(error) + return Promise.reject(argu.error) }) return this.instance } diff --git a/src/plugins/http/index.ts b/src/plugins/http/index.ts index 872c3a9..a8248f8 100644 --- a/src/plugins/http/index.ts +++ b/src/plugins/http/index.ts @@ -1,10 +1,16 @@ import { httpBase, IPlugin } from "./base" import { CreateAxiosDefaults, AxiosRequestConfig, AxiosResponse } from "axios" -import { RepeatPlugin } from "./Repeat" const FlyPoll: Map = new Map() class Fly extends httpBase { + static disposeAll() { + if (FlyPoll.size) { + Array.from(FlyPoll.keys()).forEach(v => { + Fly.dispose(v) + }) + } + } static dispose(name: string) { if (FlyPoll.has(name)) { const instance = FlyPoll.get(name) @@ -23,7 +29,9 @@ class Fly extends httpBase { static invoke(name?: string): Fly { if (!name) name = "$defalut" const instance = FlyPoll.get(name) - if (!instance) throw new Error("未初始化此实例") + if (!instance) { + throw new Error("未初始化此实例") + } return instance } @@ -44,7 +52,7 @@ class Fly extends httpBase { } default: CreateAxiosDefaults = { - // baseURL: process.env.VUE_APP_BASEURL, + baseURL: import.meta.env.BASE_URL, timeout: 10000, headers: { 'Content-Type': Fly.contentType.FORM, @@ -87,22 +95,3 @@ class Fly extends httpBase { export { Fly } - -// https://api.52vmy.cn/ -// Fly.invoke().request({ method: "get", url: "http://localhost:5173/", global: false }).then((res) => console.log(res.data)).catch(console.log) -// Fly.invoke().request({ method: "get", url: "http://localhost:5173/", global: true }).then((res) => console.log(res.data)).catch(console.log) - -// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log) - -// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log) - -// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/wl/yan/bay" }).then((res) => console.log(res.data)).catch(console.log) - -// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/img/tu/girl" }).then((res) => console.log(res.data)).catch(console.log) - - -// setTimeout(()=>{ -// // Fly.invoke("empty").request({method: "get", url: "https://api.52vmy.cn/api/img/tu/man"}).then((res)=>console.log(res.data)).catch(console.log) - -// Fly.invoke().callPluginByName(RepeatPlugin.name, "clearPendingPool") -// }, 0) diff --git a/src/plugins/http/plugins.ts b/src/plugins/http/plugins.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/plugins/http/plugins/Loading.ts b/src/plugins/http/plugins/Loading.ts new file mode 100644 index 0000000..f6379cc --- /dev/null +++ b/src/plugins/http/plugins/Loading.ts @@ -0,0 +1,97 @@ +import { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios" +import { IPlugin } from "../base" +import { Id, toast } from "react-toastify" + +declare module "axios" { + interface AxiosRequestConfig { + loadingAttrs?: Partial + } +} + +type AxiosRequestConfigPlus = AxiosRequestConfig & { + $loading_ToastID?: Id + $loading_CreateTimeStamp?: number + $loading_ToastShowFn?: () => void + $loading_ToastHideFn?: () => void +} + +interface IConfig { + text: string + min: number + minShow: boolean +} + +// 取消重复请求 +export class LoadingPlugin implements IPlugin { + static name: string = "loading" + name: string = "loading" + + defaultConfig: IConfig = { + text: "加载中", + min: 1000, + minShow: false, + } + config: IConfig = JSON.parse(JSON.stringify(this.defaultConfig)) + + constructor(config: Partial = {}) { + ; (Object.keys(config) as (keyof IConfig)[]).forEach(v => { + if (config[v]) { + (this.config as any)[v] = config[v] + } + }) + } + + getConfig(key: T, config: Partial = {}) { + const keys = Object.keys(config) as T[] + for (let i = 0; i < keys.length; i++) { + const v = keys[i]; + if (v === key) { + return (config as IConfig)[key] + } + } + return this.config[key] + } + + beforeRequestConfig({ config }: { config: AxiosRequestConfigPlus }) { + const text = this.getConfig("text", config.loadingAttrs) + config.$loading_ToastShowFn = () => { + const id = toast.loading(text) + config.$loading_CreateTimeStamp = new Date().getTime() + config.$loading_ToastID = id + } + if (!this.getConfig("minShow", config.loadingAttrs)) { + config.$loading_ToastShowFn() + delete config.$loading_ToastShowFn + } + } + beforeResponse({ response }: { response: AxiosResponse }) { + const { config } = response as { config: AxiosRequestConfigPlus } + if (!this.getConfig("minShow", config.loadingAttrs)) { + const min = this.getConfig("min", config.loadingAttrs) + if (config.$loading_ToastID) { + const [ToastID, showTimeStamp] = [config.$loading_ToastID, config.$loading_CreateTimeStamp] + const oft = new Date().getTime() - showTimeStamp! + if (oft < min) { + setTimeout(() => { + toast.dismiss(ToastID) + }, min - oft); + } else { + toast.dismiss(ToastID) + } + delete config.$loading_ToastID + delete config.$loading_CreateTimeStamp + } + } else { + + } + + } + beforeResponseError(argu: { error: AxiosError }) { + const requestConfig = argu.error.config! as AxiosRequestConfigPlus + if (requestConfig.$loading_ToastID) { + const [ToastID] = requestConfig.$loading_ToastID.split("$") + toast.dismiss(ToastID) + delete requestConfig.$loading_ToastID + } + } +} diff --git a/src/plugins/http/Repeat.ts b/src/plugins/http/plugins/Repeat.ts similarity index 58% rename from src/plugins/http/Repeat.ts rename to src/plugins/http/plugins/Repeat.ts index 0dc20c2..436061e 100644 --- a/src/plugins/http/Repeat.ts +++ b/src/plugins/http/plugins/Repeat.ts @@ -1,5 +1,5 @@ import { isCancel, AxiosRequestConfig, AxiosResponse, AxiosError } from "axios" -import { IPlugin } from "./base" +import { IPlugin } from "../base" declare module "axios" { interface AxiosRequestConfig { @@ -7,14 +7,33 @@ declare module "axios" { * 全局请求,不会被RepeatPlugin插件的clearPendingPool清除 */ global?: boolean + cancelAttrs?: Partial } } +interface IConfig { + text: string +} + // 取消重复请求 export class RepeatPlugin implements IPlugin { static name: string = "repeat" name: string = "repeat" pendingPool = new Map() + + defaultConfig: IConfig = { + text: "${url}请求取消" + } + config: IConfig = JSON.parse(JSON.stringify(this.defaultConfig)) + + constructor(config: Partial = {}) { + ; (Object.keys(config) as (keyof IConfig)[]).forEach(v => { + if (config[v]) { + this.config[v] = config[v] + } + }) + } + clearPendingPool(whiteList: string[] = []) { if (!this.pendingPool.size) return const pendingUrlList = Array.from(this.pendingPool.keys()).filter(url => !whiteList.includes(url)) @@ -22,7 +41,7 @@ export class RepeatPlugin implements IPlugin { pendingUrlList.forEach(pendingUrl => { if (!this.pendingPool.get(pendingUrl).global) { - this.pendingPool.get(pendingUrl).cancelFn(`${pendingUrl} 请求取消`) + this.pendingPool.get(pendingUrl).cancelFn(`${this.config.text}`.replace(/\$\{(.*?)}/g, (_, t) => t === "url" ? pendingUrl : '')) this.pendingPool.delete(pendingUrl) } }) @@ -40,12 +59,13 @@ export class RepeatPlugin implements IPlugin { const { config } = response this.pendingPool.delete(config.url) } - beforeResponseError({ error }: { error: AxiosError }) { - const requestConfig = error.config! - if (!isCancel(error)) this.pendingPool.delete(requestConfig.url) - if (!error) return - if (isCancel(error)) { - throw new Error((requestConfig.signal as AbortSignal)?.reason || error.message || `请求'${requestConfig.url}' 被取消`) + beforeResponseError(argu: { error: AxiosError }) { + const requestConfig = argu.error.config! + if (!isCancel(argu.error)) this.pendingPool.delete(requestConfig.url) + if (!argu.error) return + if (isCancel(argu.error)) { + argu.error.message = (requestConfig.signal as AbortSignal)?.reason || argu.error.message || `请求'${requestConfig.url}' 被取消` + return } } } diff --git a/src/plugins/request.ts b/src/plugins/request.ts index 46f0d0a..4f79207 100644 --- a/src/plugins/request.ts +++ b/src/plugins/request.ts @@ -1,14 +1,39 @@ import { Fly } from "./http" -import { RepeatPlugin } from "./http/Repeat" +import { RepeatPlugin } from "./http/plugins/Repeat" +import { LoadingPlugin } from "./http/plugins/Loading" +const allPlugin = { + loading: new LoadingPlugin(), + repeat: new RepeatPlugin(), +} + +Fly.use([allPlugin.loading]) Fly.init("$defalut", { timeout: 10000, -}, [new RepeatPlugin()]) +}, [allPlugin.repeat]) Fly.init("empty", { timeout: 10000, }) +// Fly.disposeAll() +// https://api.52vmy.cn/ +// Fly.invoke().request({ method: "get", url: "http://localhost:5173/asd", global: false }).then((res) => console.log(res.data)).catch(console.log) +// Fly.invoke().request({ method: "get", url: "http://localhost:5173/", global: true }).then((res) => console.log(res.data)).catch(console.log) + +// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log) + +// Fly.invoke().request({ method: "get", url: "https://api.52vmy.cn/api/wl/word/bing/tu", global: true }).then((res) => console.log(res.data)).catch(console.log) + +// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/wl/yan/bay" }).then((res) => console.log(res.data)).catch(console.log) + +// Fly.invoke("empty").request({ method: "get", url: "https://api.52vmy.cn/api/img/tu/girl" }).then((res) => console.log(res.data)).catch(console.log) + +// setTimeout(()=>{ +// Fly.invoke("empty").request({method: "get", url: "https://api.52vmy.cn/api/img/tu/man"}).then((res)=>console.log(res.data)).catch(console.log) + +// Fly.invoke().callPluginByName(RepeatPlugin.name, "clearPendingPool") +// }, 0) export { Fly diff --git a/src/views/Home/index.tsx b/src/views/Home/index.tsx index 47711fd..f8b48db 100644 --- a/src/views/Home/index.tsx +++ b/src/views/Home/index.tsx @@ -2,21 +2,27 @@ import withPage from "@/base/withPage" import { Hero } from "@/ui/Hero" import { Button } from "@blueprintjs/core" import { ReactNode, useCallback } from "react" -// import { Fly } from "@/plugins/request" +import { Fly } from "@/plugins/request" import { toast } from "react-toastify" interface IProps { // children: ReactNode } -export default function Home({}: IProps) { +export default function Home({ }: IProps) { const onClick = useCallback(() => { - toast.success("Wow so easy !") - // Fly.invoke().request<{a: number}>({ - // method: "get", - // url: "http://api.juheapi.com/japi/toh", - // global: false - // }).then((res) => console.log(res.data)).catch(console.log) + // toast.success("Wow so easy !") + Fly.invoke().request<{ a: number }>({ + method: "get", + url: "/data.json", + global: false, + loadingAttrs: { + text: "aasd", + minShow: false + } + }).then((res) => console.log(res.data)).catch(err => { + toast.error(err.stack) + }) }, []) return (