feat: 更新 API 地址,添加分类组件,优化市场页面布局和功能
This commit is contained in:
2
.env
2
.env
@@ -1 +1 @@
|
|||||||
VITE_API_URL=http://192.168.1.36:9527
|
VITE_API_URL=http://192.168.1.54:9528
|
||||||
20
components.d.ts
vendored
20
components.d.ts
vendored
@@ -24,17 +24,27 @@ declare module 'vue' {
|
|||||||
InputLabel: typeof import('./src/components/ui/input-label/index.vue')['default']
|
InputLabel: typeof import('./src/components/ui/input-label/index.vue')['default']
|
||||||
IonApp: typeof import('@ionic/vue')['IonApp']
|
IonApp: typeof import('@ionic/vue')['IonApp']
|
||||||
IonAvatar: typeof import('@ionic/vue')['IonAvatar']
|
IonAvatar: typeof import('@ionic/vue')['IonAvatar']
|
||||||
|
IonBackButton: typeof import('@ionic/vue')['IonBackButton']
|
||||||
IonButton: typeof import('@ionic/vue')['IonButton']
|
IonButton: typeof import('@ionic/vue')['IonButton']
|
||||||
|
IonButtons: typeof import('@ionic/vue')['IonButtons']
|
||||||
IonContent: typeof import('@ionic/vue')['IonContent']
|
IonContent: typeof import('@ionic/vue')['IonContent']
|
||||||
|
IonDatetime: typeof import('@ionic/vue')['IonDatetime']
|
||||||
|
IonDatetimeButton: typeof import('@ionic/vue')['IonDatetimeButton']
|
||||||
IonHeader: typeof import('@ionic/vue')['IonHeader']
|
IonHeader: typeof import('@ionic/vue')['IonHeader']
|
||||||
IonIcon: typeof import('@ionic/vue')['IonIcon']
|
IonIcon: typeof import('@ionic/vue')['IonIcon']
|
||||||
|
IonInputOtp: typeof import('@ionic/vue')['IonInputOtp']
|
||||||
IonItem: typeof import('@ionic/vue')['IonItem']
|
IonItem: typeof import('@ionic/vue')['IonItem']
|
||||||
IonLabel: typeof import('@ionic/vue')['IonLabel']
|
IonLabel: typeof import('@ionic/vue')['IonLabel']
|
||||||
IonList: typeof import('@ionic/vue')['IonList']
|
IonList: typeof import('@ionic/vue')['IonList']
|
||||||
IonModal: typeof import('@ionic/vue')['IonModal']
|
IonModal: typeof import('@ionic/vue')['IonModal']
|
||||||
|
IonNote: typeof import('@ionic/vue')['IonNote']
|
||||||
IonPage: typeof import('@ionic/vue')['IonPage']
|
IonPage: typeof import('@ionic/vue')['IonPage']
|
||||||
|
IonRadio: typeof import('@ionic/vue')['IonRadio']
|
||||||
|
IonRadioGroup: typeof import('@ionic/vue')['IonRadioGroup']
|
||||||
IonRouterOutlet: typeof import('@ionic/vue')['IonRouterOutlet']
|
IonRouterOutlet: typeof import('@ionic/vue')['IonRouterOutlet']
|
||||||
IonSearchbar: typeof import('@ionic/vue')['IonSearchbar']
|
IonSearchbar: typeof import('@ionic/vue')['IonSearchbar']
|
||||||
|
IonSelect: typeof import('@ionic/vue')['IonSelect']
|
||||||
|
IonSelectOption: typeof import('@ionic/vue')['IonSelectOption']
|
||||||
IonTabBar: typeof import('@ionic/vue')['IonTabBar']
|
IonTabBar: typeof import('@ionic/vue')['IonTabBar']
|
||||||
IonTabButton: typeof import('@ionic/vue')['IonTabButton']
|
IonTabButton: typeof import('@ionic/vue')['IonTabButton']
|
||||||
IonTabs: typeof import('@ionic/vue')['IonTabs']
|
IonTabs: typeof import('@ionic/vue')['IonTabs']
|
||||||
@@ -73,17 +83,27 @@ declare global {
|
|||||||
const InputLabel: typeof import('./src/components/ui/input-label/index.vue')['default']
|
const InputLabel: typeof import('./src/components/ui/input-label/index.vue')['default']
|
||||||
const IonApp: typeof import('@ionic/vue')['IonApp']
|
const IonApp: typeof import('@ionic/vue')['IonApp']
|
||||||
const IonAvatar: typeof import('@ionic/vue')['IonAvatar']
|
const IonAvatar: typeof import('@ionic/vue')['IonAvatar']
|
||||||
|
const IonBackButton: typeof import('@ionic/vue')['IonBackButton']
|
||||||
const IonButton: typeof import('@ionic/vue')['IonButton']
|
const IonButton: typeof import('@ionic/vue')['IonButton']
|
||||||
|
const IonButtons: typeof import('@ionic/vue')['IonButtons']
|
||||||
const IonContent: typeof import('@ionic/vue')['IonContent']
|
const IonContent: typeof import('@ionic/vue')['IonContent']
|
||||||
|
const IonDatetime: typeof import('@ionic/vue')['IonDatetime']
|
||||||
|
const IonDatetimeButton: typeof import('@ionic/vue')['IonDatetimeButton']
|
||||||
const IonHeader: typeof import('@ionic/vue')['IonHeader']
|
const IonHeader: typeof import('@ionic/vue')['IonHeader']
|
||||||
const IonIcon: typeof import('@ionic/vue')['IonIcon']
|
const IonIcon: typeof import('@ionic/vue')['IonIcon']
|
||||||
|
const IonInputOtp: typeof import('@ionic/vue')['IonInputOtp']
|
||||||
const IonItem: typeof import('@ionic/vue')['IonItem']
|
const IonItem: typeof import('@ionic/vue')['IonItem']
|
||||||
const IonLabel: typeof import('@ionic/vue')['IonLabel']
|
const IonLabel: typeof import('@ionic/vue')['IonLabel']
|
||||||
const IonList: typeof import('@ionic/vue')['IonList']
|
const IonList: typeof import('@ionic/vue')['IonList']
|
||||||
const IonModal: typeof import('@ionic/vue')['IonModal']
|
const IonModal: typeof import('@ionic/vue')['IonModal']
|
||||||
|
const IonNote: typeof import('@ionic/vue')['IonNote']
|
||||||
const IonPage: typeof import('@ionic/vue')['IonPage']
|
const IonPage: typeof import('@ionic/vue')['IonPage']
|
||||||
|
const IonRadio: typeof import('@ionic/vue')['IonRadio']
|
||||||
|
const IonRadioGroup: typeof import('@ionic/vue')['IonRadioGroup']
|
||||||
const IonRouterOutlet: typeof import('@ionic/vue')['IonRouterOutlet']
|
const IonRouterOutlet: typeof import('@ionic/vue')['IonRouterOutlet']
|
||||||
const IonSearchbar: typeof import('@ionic/vue')['IonSearchbar']
|
const IonSearchbar: typeof import('@ionic/vue')['IonSearchbar']
|
||||||
|
const IonSelect: typeof import('@ionic/vue')['IonSelect']
|
||||||
|
const IonSelectOption: typeof import('@ionic/vue')['IonSelectOption']
|
||||||
const IonTabBar: typeof import('@ionic/vue')['IonTabBar']
|
const IonTabBar: typeof import('@ionic/vue')['IonTabBar']
|
||||||
const IonTabButton: typeof import('@ionic/vue')['IonTabButton']
|
const IonTabButton: typeof import('@ionic/vue')['IonTabButton']
|
||||||
const IonTabs: typeof import('@ionic/vue')['IonTabs']
|
const IonTabs: typeof import('@ionic/vue')['IonTabs']
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"@elysiajs/eden": "^1.4.5",
|
"@elysiajs/eden": "^1.4.5",
|
||||||
"@ionic/vue": "^8.7.11",
|
"@ionic/vue": "^8.7.11",
|
||||||
"@ionic/vue-router": "^8.7.11",
|
"@ionic/vue-router": "^8.7.11",
|
||||||
"@riwa/api-types": "http://192.168.1.36:9527/api/riwa-api-types-0.0.22.tgz",
|
"@riwa/api-types": "http://192.168.1.36:9527/api/riwa-api-types-0.0.24.tgz",
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.1.18",
|
||||||
"@vee-validate/yup": "^4.15.1",
|
"@vee-validate/yup": "^4.15.1",
|
||||||
"@vueuse/core": "^14.1.0",
|
"@vueuse/core": "^14.1.0",
|
||||||
|
|||||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -36,8 +36,8 @@ importers:
|
|||||||
specifier: ^8.7.11
|
specifier: ^8.7.11
|
||||||
version: 8.7.11(@stencil/core@4.39.0)(vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))
|
version: 8.7.11(@stencil/core@4.39.0)(vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))
|
||||||
'@riwa/api-types':
|
'@riwa/api-types':
|
||||||
specifier: http://192.168.1.36:9527/api/riwa-api-types-0.0.22.tgz
|
specifier: http://192.168.1.36:9527/api/riwa-api-types-0.0.24.tgz
|
||||||
version: http://192.168.1.36:9527/api/riwa-api-types-0.0.22.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))
|
version: http://192.168.1.36:9527/api/riwa-api-types-0.0.24.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))
|
||||||
'@tailwindcss/vite':
|
'@tailwindcss/vite':
|
||||||
specifier: ^4.1.18
|
specifier: ^4.1.18
|
||||||
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
version: 4.1.18(vite@7.2.7(@types/node@24.10.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))
|
||||||
@@ -1248,9 +1248,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
|
||||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
||||||
|
|
||||||
'@riwa/api-types@http://192.168.1.36:9527/api/riwa-api-types-0.0.22.tgz':
|
'@riwa/api-types@http://192.168.1.36:9527/api/riwa-api-types-0.0.24.tgz':
|
||||||
resolution: {tarball: http://192.168.1.36:9527/api/riwa-api-types-0.0.22.tgz}
|
resolution: {tarball: http://192.168.1.36:9527/api/riwa-api-types-0.0.24.tgz}
|
||||||
version: 0.0.22
|
version: 0.0.24
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@elysiajs/eden': ^1.4.5
|
'@elysiajs/eden': ^1.4.5
|
||||||
|
|
||||||
@@ -5908,7 +5908,7 @@ snapshots:
|
|||||||
|
|
||||||
'@pkgr/core@0.2.9': {}
|
'@pkgr/core@0.2.9': {}
|
||||||
|
|
||||||
'@riwa/api-types@http://192.168.1.36:9527/api/riwa-api-types-0.0.22.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
'@riwa/api-types@http://192.168.1.36:9527/api/riwa-api-types-0.0.24.tgz(@elysiajs/eden@1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@elysiajs/eden': 1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
'@elysiajs/eden': 1.4.5(elysia@1.4.18(@sinclair/typebox@0.34.41)(exact-mirror@0.2.5(@sinclair/typebox@0.34.41))(file-type@21.1.1)(openapi-types@12.1.3)(typescript@5.9.3))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { App } from "@riwa/api-types";
|
import type { App } from "@riwa/api-types";
|
||||||
|
import type { Awaitable } from "@vueuse/core";
|
||||||
import { treaty } from "@elysiajs/eden";
|
import { treaty } from "@elysiajs/eden";
|
||||||
import { toastController } from "@ionic/vue";
|
import { toastController } from "@ionic/vue";
|
||||||
|
|
||||||
@@ -13,10 +14,17 @@ export interface SafeClientOptions {
|
|||||||
immediate?: boolean;
|
immediate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function safeClient<T, E>(
|
export interface SafeClientReturn<T, E> {
|
||||||
|
data: Ref<T | null>;
|
||||||
|
error: Ref<E | null>;
|
||||||
|
refresh: () => Promise<void>;
|
||||||
|
onFetchResponse: (callback: (data: T, error: E) => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function safeClient<T, E>(
|
||||||
requestPromise: () => Promise<{ data: T; error: E }>,
|
requestPromise: () => Promise<{ data: T; error: E }>,
|
||||||
options: SafeClientOptions = {},
|
options: SafeClientOptions = {},
|
||||||
) {
|
): SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>> {
|
||||||
const { immediate = true } = options;
|
const { immediate = true } = options;
|
||||||
const data = ref<T | null>(null);
|
const data = ref<T | null>(null);
|
||||||
const error = ref<E | null>(null);
|
const error = ref<E | null>(null);
|
||||||
@@ -61,11 +69,20 @@ export async function safeClient<T, E>(
|
|||||||
responseCallback = callback;
|
responseCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (immediate) {
|
const result: SafeClientReturn<T, E> = {
|
||||||
await execute();
|
data: data as Ref<T | null>,
|
||||||
}
|
error: error as Ref<E | null>,
|
||||||
|
refresh: execute,
|
||||||
|
onFetchResponse,
|
||||||
|
};
|
||||||
|
|
||||||
return { data, error, refresh: execute, onFetchResponse };
|
// 创建一个 Promise 并在其上添加属性
|
||||||
|
const promise = immediate ? execute().then(() => result) : Promise.resolve(result);
|
||||||
|
|
||||||
|
// 将 result 的属性添加到 promise 上
|
||||||
|
Object.assign(promise, result);
|
||||||
|
|
||||||
|
return promise as SafeClientReturn<T, E> & Promise<SafeClientReturn<T, E>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { client };
|
export { client };
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ export type DepositFiatBody = Parameters<typeof client.api.deposit.fiat.post>[0]
|
|||||||
assetCode: AssetCodeEnum;
|
assetCode: AssetCodeEnum;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TreatyQuery<T> = T extends (...args: any[]) => any
|
||||||
|
? NonNullable<NonNullable<Parameters<T>[0]>["query"]>
|
||||||
|
: never;
|
||||||
|
|
||||||
export type DepositFiatData = Treaty.Data<typeof client.api.deposit.fiat.post>;
|
export type DepositFiatData = Treaty.Data<typeof client.api.deposit.fiat.post>;
|
||||||
|
|
||||||
export type BalancesData = Treaty.Data<typeof client.api.asset.balances.get>;
|
export type BalancesData = Treaty.Data<typeof client.api.asset.balances.get>;
|
||||||
@@ -18,18 +22,22 @@ export type WithdrawBody = Omit<Parameters<typeof client.api.withdraw.post>[0],
|
|||||||
|
|
||||||
export type UserProfileData = Treaty.Data<typeof client.api.user.profile.get>["userProfile"];
|
export type UserProfileData = Treaty.Data<typeof client.api.user.profile.get>["userProfile"];
|
||||||
|
|
||||||
export type UpdateUserProfileBody = NonNullable<Parameters<typeof client.api.user.profile.put>[0]>;
|
export type UpdateUserProfileBody = TreatyQuery<typeof client.api.user.profile.put>;
|
||||||
|
|
||||||
export type RwaIssuanceProductsData = Treaty.Data<typeof client.api.rwa.issuance.products.bundle.post>;
|
export type RwaIssuanceProductsData = Treaty.Data<typeof client.api.rwa.issuance.products.bundle.post>;
|
||||||
|
|
||||||
export type RwaIssuanceProductBody = NonNullable<Parameters<typeof client.api.rwa.issuance.products.bundle.post>[0]>;
|
export type RwaIssuanceProductBody = TreatyQuery<typeof client.api.rwa.issuance.products.bundle.post>;
|
||||||
|
|
||||||
export type RwaIssuanceCategoriesData = Treaty.Data<typeof client.api.rwa.issuance.categories.get>;
|
export type RwaIssuanceCategoriesData = Treaty.Data<typeof client.api.rwa.issuance.categories.get>;
|
||||||
|
|
||||||
export type BankAccountsData = Treaty.Data<typeof client.api.bank_account.get>;
|
export type BankAccountsData = Treaty.Data<typeof client.api.bank_account.get>;
|
||||||
|
|
||||||
export type BankAccountBody = Parameters<typeof client.api.bank_account.post>[0];
|
export type BankAccountBody = TreatyQuery<typeof client.api.bank_account.post>;
|
||||||
|
|
||||||
export type BankAccountData = Treaty.Data<typeof client.api.bank_account.post>;
|
export type BankAccountData = Treaty.Data<typeof client.api.bank_account.post>;
|
||||||
|
|
||||||
export type SupportBanksData = Treaty.Data<typeof client.api.bank_account.banks.get>;
|
export type SupportBanksData = Treaty.Data<typeof client.api.bank_account.banks.get>;
|
||||||
|
|
||||||
|
export type AvailableSubscriptionData = Treaty.Data<typeof client.api.rwa.subscription.available_editions.get>;
|
||||||
|
|
||||||
|
export type AvailableSubscriptionBody = TreatyQuery<typeof client.api.rwa.subscription.available_editions.get>;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ interface TabsProps {
|
|||||||
closable?: boolean;
|
closable?: boolean;
|
||||||
addable?: boolean;
|
addable?: boolean;
|
||||||
placement?: "top" | "bottom" | "left" | "right";
|
placement?: "top" | "bottom" | "left" | "right";
|
||||||
|
sticky?: boolean;
|
||||||
|
stickyTop?: string | number;
|
||||||
tabStyle?: string | Record<string, any>;
|
tabStyle?: string | Record<string, any>;
|
||||||
tabClass?: string;
|
tabClass?: string;
|
||||||
paneStyle?: string | Record<string, any>;
|
paneStyle?: string | Record<string, any>;
|
||||||
@@ -27,6 +29,8 @@ const props = withDefaults(defineProps<TabsProps>(), {
|
|||||||
closable: false,
|
closable: false,
|
||||||
addable: false,
|
addable: false,
|
||||||
placement: "top",
|
placement: "top",
|
||||||
|
sticky: false,
|
||||||
|
stickyTop: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<TabsEmits>();
|
const emit = defineEmits<TabsEmits>();
|
||||||
@@ -229,14 +233,34 @@ const tabsClasses = computed(() => [
|
|||||||
`ui-tabs--${props.placement}`,
|
`ui-tabs--${props.placement}`,
|
||||||
{
|
{
|
||||||
"ui-tabs--animated": props.animated,
|
"ui-tabs--animated": props.animated,
|
||||||
|
"ui-tabs--sticky": props.sticky,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// 计算sticky样式
|
||||||
|
const stickyStyle = computed(() => {
|
||||||
|
if (!props.sticky)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const topValue = typeof props.stickyTop === "number"
|
||||||
|
? `${props.stickyTop}px`
|
||||||
|
: props.stickyTop;
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: "sticky" as const,
|
||||||
|
top: topValue,
|
||||||
|
zIndex: 100,
|
||||||
|
};
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="tabsClasses">
|
<div :class="tabsClasses">
|
||||||
<!-- 标签头部 -->
|
<div
|
||||||
<div class="ui-tabs__nav-wrapper" :class="[`ui-tabs__nav-wrapper--${placement}`]">
|
class="ui-tabs__nav-wrapper"
|
||||||
|
:class="[`ui-tabs__nav-wrapper--${placement}`]"
|
||||||
|
:style="props.sticky ? stickyStyle : {}"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
ref="tabsRef"
|
ref="tabsRef"
|
||||||
class="ui-tabs__nav"
|
class="ui-tabs__nav"
|
||||||
@@ -304,11 +328,11 @@ const tabsClasses = computed(() => [
|
|||||||
|
|
||||||
.ui-tabs {
|
.ui-tabs {
|
||||||
@apply w-full;
|
@apply w-full;
|
||||||
--ui-tabs-primary: var(--ion-color-primary, #007aff);
|
--ui-tabs-primary: var(--ion-color-primary);
|
||||||
--ui-tabs-primary-rgb: var(--ion-color-primary-rgb, 0, 122, 255);
|
--ui-tabs-primary-rgb: var(--ion-color-primary-rgb);
|
||||||
--ui-tabs-background: var(--ion-background-color, #ffffff);
|
--ui-tabs-background: var(--ion-background-color);
|
||||||
--ui-tabs-text: var(--ion-text-color, #000000);
|
--ui-tabs-text: var(--ion-text-color);
|
||||||
--ui-tabs-text-rgb: var(--ion-text-color-rgb, 0, 0, 0);
|
--ui-tabs-text-rgb: var(--ion-text-color-rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 导航包装器 */
|
/* 导航包装器 */
|
||||||
@@ -316,7 +340,15 @@ const tabsClasses = computed(() => [
|
|||||||
@apply relative;
|
@apply relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tabs__nav-wrapper--top {
|
/* Sticky 布局 */
|
||||||
|
.ui-tabs--sticky .ui-tabs__nav-wrapper {
|
||||||
|
background-color: var(--ui-tabs-background);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-tabs--sticky.ui-tabs--animated .ui-tabs__nav-wrapper {
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tabs__nav-wrapper--bottom {
|
.ui-tabs__nav-wrapper--bottom {
|
||||||
@@ -365,13 +397,13 @@ const tabsClasses = computed(() => [
|
|||||||
color: var(--ion-color-medium, #6b7280);
|
color: var(--ion-color-medium, #6b7280);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab:hover {
|
.ui-tab:hover:not(.ui-tab--disabled):not(.ui-tab--active) {
|
||||||
color: var(--ion-color-dark, #374151);
|
color: var(--ion-color-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab--active {
|
.ui-tab--active {
|
||||||
@apply font-medium;
|
@apply font-medium;
|
||||||
color: var(--ui-tabs-primary);
|
color: var(--ion-color-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab--disabled {
|
.ui-tab--disabled {
|
||||||
@@ -385,11 +417,7 @@ const tabsClasses = computed(() => [
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab--bar.ui-tab--active {
|
.ui-tab--bar.ui-tab--active {
|
||||||
border-color: var(--ui-tabs-primary);
|
border-color: var(--ion-color-primary) !important;
|
||||||
}
|
|
||||||
|
|
||||||
.ui-tab--bar:hover:not(.ui-tab--disabled):not(.ui-tab--active) {
|
|
||||||
background-color: rgba(var(--ui-tabs-primary-rgb), 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Line 类型 */
|
/* Line 类型 */
|
||||||
@@ -397,10 +425,6 @@ const tabsClasses = computed(() => [
|
|||||||
@apply py-3;
|
@apply py-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab--line:hover:not(.ui-tab--disabled):not(.ui-tab--active) {
|
|
||||||
background-color: rgba(var(--ui-tabs-primary-rgb), 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Segment 类型 */
|
/* Segment 类型 */
|
||||||
.ui-tab--segment {
|
.ui-tab--segment {
|
||||||
@apply px-3 py-2 rounded-md;
|
@apply px-3 py-2 rounded-md;
|
||||||
@@ -413,10 +437,6 @@ const tabsClasses = computed(() => [
|
|||||||
0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab--segment:hover:not(.ui-tab--disabled):not(.ui-tab--active) {
|
|
||||||
background-color: rgba(var(--ui-tabs-primary-rgb), 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 尺寸变化 */
|
/* 尺寸变化 */
|
||||||
.ui-tab--small {
|
.ui-tab--small {
|
||||||
@apply text-sm;
|
@apply text-sm;
|
||||||
@@ -574,6 +594,8 @@ const tabsClasses = computed(() => [
|
|||||||
/* Dark mode 支持 */
|
/* Dark mode 支持 */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.ui-tabs {
|
.ui-tabs {
|
||||||
|
--ui-tabs-primary: var(--ion-color-primary, #ffffff);
|
||||||
|
--ui-tabs-primary-rgb: var(--ion-color-primary-rgb, 255, 255, 255);
|
||||||
--ui-tabs-background: var(--ion-background-color, #1a1a1a);
|
--ui-tabs-background: var(--ion-background-color, #1a1a1a);
|
||||||
--ui-tabs-text: var(--ion-text-color, #ffffff);
|
--ui-tabs-text: var(--ion-text-color, #ffffff);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
import type { GenericObject } from "vee-validate";
|
import type { GenericObject } from "vee-validate";
|
||||||
import type { RwaIssuanceCategoriesData, RwaIssuanceProductBody } from "@/api/types";
|
import type { RwaIssuanceCategoriesData, RwaIssuanceProductBody } from "@/api/types";
|
||||||
import { toTypedSchema } from "@vee-validate/yup";
|
import { toTypedSchema } from "@vee-validate/yup";
|
||||||
import { ErrorMessage, Field, FieldArray, Form } from "vee-validate";
|
import { ErrorMessage, Field, Form } from "vee-validate";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
initialData: RwaIssuanceProductBody["product"];
|
initialData: RwaIssuanceProductBody["product"];
|
||||||
categories: RwaIssuanceCategoriesData;
|
categories: RwaIssuanceCategoriesData | null;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "next", values: GenericObject): void;
|
(e: "next", values: GenericObject): void;
|
||||||
@@ -64,7 +64,7 @@ function handleSubmit(values: GenericObject) {
|
|||||||
<Field name="categoryId" type="text">
|
<Field name="categoryId" type="text">
|
||||||
<template #default="{ field }">
|
<template #default="{ field }">
|
||||||
<ion-select class="ui-select" interface="action-sheet" toggle-icon="" v-bind="field" :label="t('asset.issue.apply.productType')" :placeholder="t('asset.issue.apply.chooseProductType')">
|
<ion-select class="ui-select" interface="action-sheet" toggle-icon="" v-bind="field" :label="t('asset.issue.apply.productType')" :placeholder="t('asset.issue.apply.chooseProductType')">
|
||||||
<ion-select-option v-for="item in categories" :key="item.id" :value="item.id">
|
<ion-select-option v-for="item in categories?.data" :key="item.id" :value="item.id">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</ion-select-option>
|
</ion-select-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const initialData: RwaIssuanceProductBody = {
|
|||||||
product: {
|
product: {
|
||||||
name: "",
|
name: "",
|
||||||
code: "",
|
code: "",
|
||||||
categoryId: categories.value!.length > 0 ? categories.value![0].id : "",
|
categoryId: categories.value?.data && categories.value?.data!.length > 0 ? categories.value?.data![0].id : "",
|
||||||
},
|
},
|
||||||
editions: [
|
editions: [
|
||||||
{
|
{
|
||||||
@@ -55,7 +55,7 @@ async function handleSubmit(editions: RwaIssuanceProductBody["editions"]) {
|
|||||||
</ion-toolbar>
|
</ion-toolbar>
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<IonContent :fullscreen="true" class="ion-padding">
|
<IonContent :fullscreen="true" class="ion-padding">
|
||||||
<Base v-if="step === 1" :initial-data="form.product" :categories="categories || []" @next="handleNext" />
|
<Base v-if="step === 1" :initial-data="form.product" :categories="categories" @next="handleNext" />
|
||||||
<IssuePeriod v-else-if="step === 2" :initial-data="form.editions" @submit="handleSubmit" />
|
<IssuePeriod v-else-if="step === 2" :initial-data="form.editions" @submit="handleSubmit" />
|
||||||
<Done v-else />
|
<Done v-else />
|
||||||
</IonContent>
|
</IonContent>
|
||||||
|
|||||||
46
src/views/market/components/category.vue
Normal file
46
src/views/market/components/category.vue
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<script lang='ts' setup>
|
||||||
|
import { client, safeClient } from "@/api";
|
||||||
|
|
||||||
|
const model = defineModel({ type: String, default: "" });
|
||||||
|
|
||||||
|
const { data: categories } = await safeClient(() => client.api.rwa.issuance.categories.get());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ion-content :scroll-x="true" :scroll-y="false" class="w-full h-10">
|
||||||
|
<div class="flex items-center whitespace-nowrap space-x-4">
|
||||||
|
<div class="item" :class="{ active: model === '' }" @click="model = ''">
|
||||||
|
全部
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="item in categories?.data"
|
||||||
|
:key="item.id"
|
||||||
|
class="item"
|
||||||
|
:class="{ active: model === item.id }"
|
||||||
|
@click="model = item.id"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@reference "tailwindcss";
|
||||||
|
|
||||||
|
ion-content::part(scroll) {
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
@apply px-3 py-1 rounded-full text-xs transition-all;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.item {
|
||||||
|
@apply bg-(--ion-color-step-800);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.item.active {
|
||||||
|
@apply bg-(--ion-color-success) text-white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,21 +1,43 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { AvailableSubscriptionBody } from "@/api/types";
|
||||||
|
import { client, safeClient } from "@/api";
|
||||||
|
import Category from "./components/category.vue";
|
||||||
|
|
||||||
|
const [query] = useResetRef<AvailableSubscriptionBody>({
|
||||||
|
status: "open",
|
||||||
|
pageIndex: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
categoryId: "",
|
||||||
|
});
|
||||||
|
const { data, refresh } = safeClient(() => client.api.rwa.subscription.available_editions.get({
|
||||||
|
query: query.value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
watch(query, () => {
|
||||||
|
refresh();
|
||||||
|
}, { deep: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<IonPage>
|
<IonPage>
|
||||||
<IonHeader>
|
<IonHeader>
|
||||||
<IonToolbar>
|
<IonToolbar class="ui-toolbar">
|
||||||
<IonTitle>Trade</IonTitle>
|
<ion-title>Market</ion-title>
|
||||||
</IonToolbar>
|
</IonToolbar>
|
||||||
|
<ion-toolbar class="ui-toolbar">
|
||||||
|
<ion-searchbar />
|
||||||
|
</ion-toolbar>
|
||||||
</IonHeader>
|
</IonHeader>
|
||||||
<IonContent :fullscreen="true">
|
<IonContent :fullscreen="true" class="ion-padding">
|
||||||
<IonHeader collapse="condense">
|
<ui-tabs sticky>
|
||||||
<IonToolbar>
|
<ui-tab-pane name="spot" title="Digital Products" class="py-5">
|
||||||
<IonTitle size="large">
|
<Category v-model="query!.categoryId" />
|
||||||
Trade
|
<ion-content :scroll-y="true" />
|
||||||
</IonTitle>
|
</ui-tab-pane>
|
||||||
</IonToolbar>
|
<ui-tab-pane name="futures" title="Tokenized Products" class="py-5">
|
||||||
</IonHeader>
|
<div>Futures Market Content</div>
|
||||||
|
</ui-tab-pane>
|
||||||
|
</ui-tabs>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
</IonPage>
|
</IonPage>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -51,6 +51,13 @@ const config: Config = {
|
|||||||
"align-items": "center",
|
"align-items": "center",
|
||||||
"justify-content": "center",
|
"justify-content": "center",
|
||||||
},
|
},
|
||||||
|
".scrollbar-hide": {
|
||||||
|
"-ms-overflow-style": "none",
|
||||||
|
"scrollbar-width": "none",
|
||||||
|
"&::-webkit-scrollbar": {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user