diff --git a/.env.test b/.env.test index 01a898f..ad07516 100644 --- a/.env.test +++ b/.env.test @@ -1,5 +1,5 @@ # backend service base url, test environment -VITE_SERVICE_BASE_URL=http://192.168.1.33:9528 +VITE_SERVICE_BASE_URL=http://192.168.1.27:9528 # other backend service base url, test environment VITE_OTHER_SERVICE_BASE_URL= `{}` diff --git a/src/components/table/table-base.vue b/src/components/table/table-base.vue index 5487ed4..30e8c02 100644 --- a/src/components/table/table-base.vue +++ b/src/components/table/table-base.vue @@ -108,6 +108,7 @@ defineExpose({ Promise import("@/views/deposit/fiat/index.vue"), home: () => import("@/views/home/index.vue"), rwa_product: () => import("@/views/rwa/product/index.vue"), - rwa_producttype: () => import("@/views/rwa/producttype/index.vue"), + rwa_producttype: () => import("@/views/rwa/productType/index.vue"), user_bank: () => import("@/views/user/bank/index.vue"), user_bankcard: () => import("@/views/user/bankcard/index.vue"), user_list: () => import("@/views/user/list/index.vue"), diff --git a/src/service/api/client.ts b/src/service/api/client.ts index 1c42bb2..fba4a73 100644 --- a/src/service/api/client.ts +++ b/src/service/api/client.ts @@ -1,8 +1,10 @@ -import { ref } from 'vue'; +import type { Ref, WatchSource } from 'vue'; +import { ref, watch } from 'vue'; import { treaty } from '@elysiajs/eden'; import type { App } from '@riwa/api-types'; import { getServiceBaseURL } from '@/utils/service'; import { localStg } from '@/utils/storage'; +import { $t } from '@/locales'; const isHttpProxy = import.meta.env.DEV && import.meta.env.VITE_HTTP_PROXY === 'Y'; const { baseURL } = getServiceBaseURL(import.meta.env, isHttpProxy); @@ -14,37 +16,104 @@ const client = treaty(baseURL, { } }); -async function safeClient( - requestFactory: () => Promise<{ data: T; error: E }>, - options: { silent?: boolean; immediate?: boolean } = {} -) { - const { immediate = true } = options; - const data = ref(); - const error = ref(); +export interface SafeClientOptions { + silent?: boolean; + immediate?: boolean; + watchSource?: WatchSource; // 用于监听的响应式数据源 +} - const executeRequest = async () => { - const res = await requestFactory(); +export interface SafeClientReturn { + data: Ref; + error: Ref; + isPending: Ref; + execute: () => Promise; + onFetchResponse: (callback: (data: T, error: E) => void) => void; + stopWatching?: () => void; +} - if (res.error) { +export type RequestPromise = Promise<{ data: T; error: E; status?: number; response?: Response }>; + +export function safeClient( + requestPromise: (() => RequestPromise) | RequestPromise, + options: SafeClientOptions = {} +): SafeClientReturn & Promise> { + const { immediate = true, watchSource } = options; + const data = ref(null); + const error = ref(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; + if (typeof requestPromise !== 'function') { + request = () => Promise.resolve(requestPromise); + } else { + request = requestPromise; + } + + const res = await request().finally(() => { + isPending.value = false; + }); + + if (res.error && res.status === 418) { if (!options.silent) { - window.$message?.error('An error occurred while processing your request.'); + const msg = $t((res.error as any).value.code, { + ...(res.error as any).value.context + }); + window.$message?.create(msg, { + type: 'error' + }); } throw res.error; } data.value = res.data; error.value = res.error; + + // 调用注册的回调函数 + if (responseCallback) { + responseCallback(res.data, res.error); + } }; - if (immediate) { - await executeRequest(); + function onFetchResponse(callback: (data: T, error: E) => void) { + responseCallback = callback; } - return { - data, - error, - refresh: executeRequest + function stopWatching() { + if (stopWatcher) { + stopWatcher(); + stopWatcher = undefined; + } + } + + // 如果提供了 watchSource,则监听其变化 + if (watchSource) { + stopWatcher = watch( + watchSource, + () => { + execute(); + }, + { immediate: false } // 不立即执行,避免与 immediate 选项冲突 + ); + } + + const result: SafeClientReturn = { + data: data as Ref, + error: error as Ref, + isPending, + execute, + onFetchResponse, + stopWatching }; + + const promise = immediate ? execute().then(() => result) : Promise.resolve(result); + + Object.assign(promise, result); + + return promise as SafeClientReturn & Promise>; } -export { client, safeClient }; +export { client }; diff --git a/src/typings/common.d.ts b/src/typings/common.d.ts index 652bc3b..52698c6 100644 --- a/src/typings/common.d.ts +++ b/src/typings/common.d.ts @@ -22,4 +22,6 @@ declare namespace CommonType { type RecordNullable = { [K in keyof T]?: T[K] | null; }; + + type TreatyBody = T extends (...args: any[]) => any ? NonNullable[0]> : never; } diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index 170cc05..2be9a25 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -47,6 +47,7 @@ declare module 'vue' { NButton: typeof import('naive-ui')['NButton'] NCard: typeof import('naive-ui')['NCard'] NCheckbox: typeof import('naive-ui')['NCheckbox'] + NCol: typeof import('naive-ui')['NCol'] NColorPicker: typeof import('naive-ui')['NColorPicker'] NDataTable: typeof import('naive-ui')['NDataTable'] NDialogProvider: typeof import('naive-ui')['NDialogProvider'] @@ -71,6 +72,7 @@ declare module 'vue' { NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] NPopconfirm: typeof import('naive-ui')['NPopconfirm'] NPopover: typeof import('naive-ui')['NPopover'] + NRow: typeof import('naive-ui')['NRow'] NScrollbar: typeof import('naive-ui')['NScrollbar'] NSelect: typeof import('naive-ui')['NSelect'] NSpace: typeof import('naive-ui')['NSpace'] @@ -81,6 +83,7 @@ declare module 'vue' { NTabs: typeof import('naive-ui')['NTabs'] NThing: typeof import('naive-ui')['NThing'] NTooltip: typeof import('naive-ui')['NTooltip'] + NUpload: typeof import('naive-ui')['NUpload'] NWatermark: typeof import('naive-ui')['NWatermark'] PinToggler: typeof import('./../components/common/pin-toggler.vue')['default'] ReloadButton: typeof import('./../components/common/reload-button.vue')['default'] @@ -135,6 +138,7 @@ declare global { const NButton: typeof import('naive-ui')['NButton'] const NCard: typeof import('naive-ui')['NCard'] const NCheckbox: typeof import('naive-ui')['NCheckbox'] + const NCol: typeof import('naive-ui')['NCol'] const NColorPicker: typeof import('naive-ui')['NColorPicker'] const NDataTable: typeof import('naive-ui')['NDataTable'] const NDialogProvider: typeof import('naive-ui')['NDialogProvider'] @@ -159,6 +163,7 @@ declare global { const NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] const NPopconfirm: typeof import('naive-ui')['NPopconfirm'] const NPopover: typeof import('naive-ui')['NPopover'] + const NRow: typeof import('naive-ui')['NRow'] const NScrollbar: typeof import('naive-ui')['NScrollbar'] const NSelect: typeof import('naive-ui')['NSelect'] const NSpace: typeof import('naive-ui')['NSpace'] @@ -169,6 +174,7 @@ declare global { const NTabs: typeof import('naive-ui')['NTabs'] const NThing: typeof import('naive-ui')['NThing'] const NTooltip: typeof import('naive-ui')['NTooltip'] + const NUpload: typeof import('naive-ui')['NUpload'] const NWatermark: typeof import('naive-ui')['NWatermark'] const PinToggler: typeof import('./../components/common/pin-toggler.vue')['default'] const ReloadButton: typeof import('./../components/common/reload-button.vue')['default'] diff --git a/src/views/rwa/product/components/add.vue b/src/views/rwa/product/components/add.vue new file mode 100644 index 0000000..536a9b7 --- /dev/null +++ b/src/views/rwa/product/components/add.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/views/rwa/product/index.vue b/src/views/rwa/product/index.vue index 0cdb630..0a74933 100644 --- a/src/views/rwa/product/index.vue +++ b/src/views/rwa/product/index.vue @@ -1,8 +1,11 @@