Files
financial/src/api/index.ts

128 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { App } from "@capp/eden";
import type { WatchSource } from "vue";
import { treaty } from "@elysiajs/eden";
import { i18n } from "@/locales";
const baseURL = import.meta.env.DEV ? window.location.origin : import.meta.env.VITE_API_URL;
export const client = treaty<App>(baseURL, {
fetch: {
credentials: "include",
},
headers() {
const token = localStorage.getItem("user-token") || "";
const headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append("Authorization", token ? `Bearer ${token}` : "");
return headers;
},
});
export interface SafeClientOptions {
silent?: boolean;
immediate?: boolean;
watchSource?: WatchSource; // 用于监听的响应式数据源
}
export interface SafeClientReturn<T, E> {
data: Ref<T | null>;
error: Ref<E | null>;
isPending: Ref<boolean>;
execute: () => Promise<void>;
onFetchResponse: (callback: (data: T, error: E) => void) => void;
stopWatching?: () => void;
}
export type RequestPromise<T, E> = Promise<{ data: T; error: E; status?: number; response?: Response }>;
export function safeClient<T, E>(
requestPromise: (() => RequestPromise<T, E>) | RequestPromise<T, E>,
options: SafeClientOptions = {},
): SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>> {
const { immediate = true, watchSource } = options;
const data = ref<T | null>(null);
const error = ref<E | null>(null);
const isPending = ref(false);
let responseCallback: ((data: T, error: E) => void) | null = null;
let stopWatcher: (() => void) | undefined;
const execute = async () => {
isPending.value = true;
let request: () => RequestPromise<T, E>;
if (typeof requestPromise !== "function") {
request = () => Promise.resolve(requestPromise);
}
else {
request = requestPromise;
}
const res = await request().finally(() => {
isPending.value = false;
});
if (res.error) {
if (!options.silent && res.status === 418) {
showToast(i18n.global.t((res.error as any).value.code, {
...(res.error as any).value.context,
}));
}
else if (res.status === 401) {
setTimeout(() => {
showToast("登录状态已过期2秒后将跳转到登录页面请重新登录");
localStorage.removeItem("user-token");
window.location.reload();
}, 2000);
}
else if (!options.silent) {
showToast((res.error as any).message || i18n.global.t("network_error"));
}
throw res.error;
}
data.value = res.data;
error.value = res.error;
// 调用注册的回调函数
if (responseCallback) {
responseCallback(res.data, res.error);
}
};
function onFetchResponse(callback: (data: T, error: E) => void) {
responseCallback = callback;
}
function stopWatching() {
if (stopWatcher) {
stopWatcher();
stopWatcher = undefined;
}
}
// 如果提供了 watchSource则监听其变化
if (watchSource) {
stopWatcher = watch(
watchSource,
() => {
execute();
},
{ immediate: false }, // 不立即执行,避免与 immediate 选项冲突
);
}
const result: SafeClientReturn<T, E> = {
data: data as Ref<T | null>,
error: error as Ref<E | null>,
isPending,
execute,
onFetchResponse,
stopWatching,
};
const promise = immediate ? execute().then(() => result) : Promise.resolve(result);
Object.assign(promise, result);
return promise as SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>>;
}