feat: 实现发行申请提交

This commit is contained in:
2025-12-16 03:49:39 +07:00
parent 831bc78ec5
commit 2d1454d08e
21 changed files with 870 additions and 40 deletions

View File

@@ -9,7 +9,7 @@ const client = treaty<App>(window.location.origin, {
});
export async function safeClient<T, E>(
requestPromise: Promise<{ data: T; error: E | null }>,
requestPromise: Promise<{ data: T; error: E }>,
options: { silent?: boolean } = {},
) {
const { data, error } = await requestPromise;

View File

@@ -19,3 +19,9 @@ 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 UpdateUserProfileBody = NonNullable<Parameters<typeof client.api.user.profile.put>[0]>;
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 RwaIssuanceCategoriesData = Treaty.Data<typeof client.api.rwa.issuance.categories.get>;

View File

@@ -0,0 +1,2 @@
export { default as Collapse } from './index.vue';
export type * from './types';

View File

@@ -0,0 +1,138 @@
<script lang='ts' setup>
import type { CollapseEmits, CollapseProps } from "./types";
import { IonIcon } from "@ionic/vue";
import { chevronDownOutline } from "ionicons/icons";
import { nextTick, ref } from "vue";
const props = withDefaults(defineProps<CollapseProps>(), {
disabled: false,
bordered: true,
size: "medium",
});
defineEmits<CollapseEmits>();
const active = defineModel<boolean>("active", { type: Boolean, default: true });
const contentRef = ref<HTMLElement>();
async function toggle() {
if (props.disabled)
return;
if (!active.value) {
// 展开
active.value = true;
await nextTick();
if (contentRef.value) {
const height = contentRef.value.scrollHeight;
contentRef.value.style.height = "0px";
requestAnimationFrame(() => {
contentRef.value!.style.height = `${height}px`;
});
}
}
else {
// 收起
if (contentRef.value) {
const height = contentRef.value.scrollHeight;
contentRef.value.style.height = `${height}px`;
requestAnimationFrame(() => {
contentRef.value!.style.height = "0px";
});
}
setTimeout(() => {
active.value = false;
}, 300);
}
setTimeout(() => {
if (contentRef.value && active.value) {
contentRef.value.style.height = "auto";
}
}, 300);
}
function onTransitionEnd() {
if (contentRef.value && !active.value) {
contentRef.value.style.height = "0px";
}
}
// 暴露给父组件的方法和属性
defineExpose({
toggle,
});
</script>
<template>
<div
class="w-full overflow-hidden"
:class="{
'border border-gray-200 dark:border-gray-600 rounded-lg': bordered,
'opacity-60': disabled,
}"
>
<!-- 头部触发区域 -->
<div
class="flex items-center justify-between cursor-pointer transition-colors duration-200 select-none"
:class="[
{
'border-b border-gray-200 dark:border-gray-600': bordered,
},
{
'p-3 text-sm': size === 'small',
'p-4 text-base': size === 'medium',
'p-5 text-lg': size === 'large',
},
{
'cursor-not-allowed': disabled,
},
]"
@click="toggle"
>
<div class="font-medium text-gray-900 dark:text-white flex-1">
<slot name="title">
{{ title }}
</slot>
</div>
<IonIcon
:icon="chevronDownOutline"
class="text-gray-500 dark:text-gray-400 transition-transform duration-300"
:class="[
{
'w-4 h-4': size === 'small',
'w-5 h-5': size === 'medium',
'w-6 h-6': size === 'large',
},
{
'rotate-180': active,
},
]"
/>
</div>
<!-- 内容区域 -->
<div
ref="contentRef"
class="overflow-hidden transition-all duration-300 ease-in-out"
:style="{ height: active ? 'auto' : '0' }"
@transitionend="onTransitionEnd"
>
<div
class="p-4 text-gray-600 dark:text-gray-300 space-y-5"
>
<slot />
</div>
</div>
</div>
</template>
<style lang='css' scoped>
.select-none {
user-select: none;
}
</style>

View File

@@ -0,0 +1,29 @@
export interface CollapseProps {
/** 面板标题 */
title?: string;
/** 是否禁用 */
disabled?: boolean;
/** 是否显示边框 */
bordered?: boolean;
/** 尺寸大小 */
size?: "small" | "medium" | "large";
/** 是否展开 (v-model:active) */
active?: boolean;
}
export interface CollapseEmits {
/** 展开状态变化时触发 */
"update:active": [value: boolean];
}
export interface CollapseSlots {
/** 默认插槽 - 面板内容 */
default: () => any;
/** 标题插槽 - 自定义标题内容 */
title: () => any;
}
export interface CollapseInstance {
/** 切换展开/收起状态 */
toggle: () => void;
}

View File

@@ -0,0 +1,39 @@
<script lang='ts' setup>
import type { FieldBindingObject } from "vee-validate";
interface Props extends FieldBindingObject {
label?: string;
}
const props = defineProps<Props>();
function handleChange(value: string) {
const formattedValue = useDateFormat(value, "YYYY/MM/DD").value;
props.onChange(formattedValue);
}
</script>
<template>
<div class="flex flex-col items-start">
<ion-label class="text-sm font-bold color-(--ion-text-color-secondary) mb-3.5">
{{ props.label }}
</ion-label>
<ion-datetime-button datetime="datetime" color="primary">
<div slot="date-target">
{{ props.value }}
</div>
</ion-datetime-button>
<ion-modal :keep-contents-mounted="true">
<ion-datetime
id="datetime"
class="ui-datetime"
done-text="Done"
presentation="date"
:show-default-buttons="true"
@ion-change="handleChange($event.detail.value as string)"
/>
</ion-modal>
</div>
</template>
<style lang='css' scoped></style>

View File

@@ -1,9 +0,0 @@
<script lang='ts' setup>
</script>
<template>
hello world
</template>
<style lang='css' scoped></style>

View File

@@ -0,0 +1,22 @@
<script lang='ts' setup>
defineProps<{
icon?: string;
title?: string;
description?: string;
}>();
</script>
<template>
<div class="flex-col-center space-y-3">
<ion-icon :icon="icon" class="text-5xl text-lime-500" />
<div class="text-xl font-semibold">
{{ title }}
</div>
<p class="text-center text-text-400 max-w-xs">
{{ description }}
</p>
<slot name="extra" />
</div>
</template>
<style lang='css' scoped></style>

View File

@@ -47,7 +47,63 @@
"myCompany": "My Company",
"issuingApply": "Issuing Apply",
"audit": "Audit",
"supervision": "Supervision"
"supervision": "Supervision",
"apply": {
"title": "Issuing Apply",
"base": "Base",
"issuePeriod": "Issue Period",
"productName": "Product Name",
"enterProductName": "Please enter product name",
"productCode": "Product Code",
"enterProductCode": "Please enter product code",
"productType": "Product Type",
"chooseProductType": "Please choose product type",
"productValue": "Product Valuation",
"enterProductValue": "Please enter product valuation (USD)",
"assetProof": "Asset Proof",
"enterAssetProof": "Please enter asset proof",
"totalSupplyLimit": "Total Supply Limit",
"enterTotalSupplyLimit": "Please enter total supply limit",
"editionName": "Edition Name",
"enterEditionName": "Please enter edition name",
"launchDate": "Launch Date",
"enterLaunchDate": "Please enter launch date",
"perUserLimit": "Per User Limit",
"enterPerUserLimit": "Please enter per user limit",
"totalSupply": "Total Supply",
"enterTotalSupply": "Please enter total supply",
"subscriptionDeadline": "Subscription Deadline",
"enterSubscriptionDeadline": "Please enter subscription deadline",
"unitPrice": "Unit Price",
"enterUnitPrice": "Please enter unit price",
"dividendRate": "Dividend Rate",
"enterDividendRate": "Dividend rate (e.g., 0.01 for 1%)",
"issuePeriodIndex": "Issue Period {index}",
"realEstate": "Real Estate",
"snickers": "Snickers",
"next": "Next",
"back": "Back",
"submit": "Submit",
"addStep": "Add Step",
"removeItem": "Remove Item",
"validation": {
"nameRequired": "Name is required",
"codeRequired": "Code is required",
"categoryRequired": "Type is required",
"editionNameRequired": "Edition name is required",
"launchDateRequired": "Launch date is required",
"perUserLimitRequired": "Per user limit is required",
"totalSupplyRequired": "Total supply is required",
"subscriptionDeadlineRequired": "Subscription deadline is required",
"unitPriceRequired": "Unit price is required",
"dividendRateRequired": "Dividend rate is required"
},
"done": {
"title": "Application Submitted Successfully",
"description": "Your issuance application has been submitted successfully. We will review your application within business days. Please wait patiently for the review results.",
"viewProducts": "View My Products"
}
}
},
"purchase": {
"purchaseAsset": "Purchase Assets",

View File

@@ -47,7 +47,63 @@
"myCompany": "我的企业",
"issuingApply": "发行申请",
"audit": "审核审计",
"supervision": "委托监管"
"supervision": "委托监管",
"apply": {
"title": "发行申请",
"base": "基础信息",
"issuePeriod": "发行期",
"productName": "产品名称",
"enterProductName": "请输入产品名称",
"productCode": "产品编码",
"enterProductCode": "请输入产品编码",
"productType": "产品类型",
"chooseProductType": "请选择产品类型",
"productValue": "产品估值",
"enterProductValue": "请输入产品估值(美元)",
"assetProof": "资产证明",
"enterAssetProof": "请输入资产证明",
"totalSupplyLimit": "总发行量上限",
"enterTotalSupplyLimit": "请输入总发行量上限",
"editionName": "发行期名称",
"enterEditionName": "请输入发行期名称",
"launchDate": "发行日期",
"enterLaunchDate": "请输入发行日期",
"perUserLimit": "个人申购上限",
"enterPerUserLimit": "请输入个人申购上限",
"totalSupply": "发行总量",
"enterTotalSupply": "请输入发行总量",
"subscriptionDeadline": "申购截止日期",
"enterSubscriptionDeadline": "请输入申购截止日期",
"unitPrice": "单价",
"enterUnitPrice": "请输入单价",
"dividendRate": "分红率",
"enterDividendRate": "分红率(如0.01代表1%)",
"issuePeriodIndex": "发行期 {index}",
"realEstate": "房地产",
"snickers": "糖果",
"next": "下一步",
"back": "返回",
"submit": "提交",
"addStep": "添加发行期",
"removeItem": "移除项目",
"validation": {
"nameRequired": "产品名称是必填项",
"codeRequired": "产品编码是必填项",
"categoryRequired": "产品类型是必填项",
"editionNameRequired": "发行期名称是必填项",
"launchDateRequired": "发行日期是必填项",
"perUserLimitRequired": "个人申购上限是必填项",
"totalSupplyRequired": "发行总量是必填项",
"subscriptionDeadlineRequired": "申购截止日期是必填项",
"unitPriceRequired": "单价是必填项",
"dividendRateRequired": "分红率是必填项"
},
"done": {
"title": "申请提交成功",
"description": "您的发行申请已成功提交,我们将在工作日内对您的申请进行审核。请耐心等待审核结果。",
"viewProducts": "查看我的产品"
}
}
},
"purchase": {
"purchaseAsset": "购买资产",

View File

@@ -54,6 +54,7 @@ const routes: Array<RouteRecordRaw> = [
},
{
path: "/issue/issuing-apply",
props: ({ query, params }) => ({ query, params }),
component: () => import("@/views/issue/issuing-apply/index.vue"),
},
];

View File

@@ -2,3 +2,30 @@
--background: var(--ion-color-primary-contrast);
--min-height: 50px;
}
.ui-select::part(label){
font-size: 14px;
font-weight: 500;
}
.ui-button {
--border-radius: 8px;
}
ion-datetime.ui-datetime {
--background: rgb(255 255 255);
--background-rgb: 255, 255, 255;
--wheel-highlight-background: rgb(194 194 194);
--wheel-fade-background-rgb: 255, 255, 255;
border-radius: 16px;
overflow: hidden;
}
@media (prefers-color-scheme: dark) {
ion-datetime.ui-datetime {
--background: rgb(15, 15, 15);
--background-rgb: 15, 15, 15;
--wheel-highlight-background: rgb(50, 50, 50);
--wheel-highlight-border-radius: 48px;
--wheel-fade-background-rgb: 15, 15, 15;
}
}

View File

@@ -0,0 +1,127 @@
<script lang='ts' setup>
import type { GenericObject } from "vee-validate";
import type { RwaIssuanceCategoriesData, RwaIssuanceProductBody } from "@/api/types";
import { toTypedSchema } from "@vee-validate/yup";
import { ErrorMessage, Field, FieldArray, Form } from "vee-validate";
import { useI18n } from "vue-i18n";
import * as yup from "yup";
import { client, safeClient } from "@/api";
const props = defineProps<{
initialData: RwaIssuanceProductBody["product"];
categories: RwaIssuanceCategoriesData;
}>();
const emit = defineEmits<{
(e: "next", values: GenericObject): void;
}>();
const { t } = useI18n();
const schema = toTypedSchema(
yup.object({
name: yup.string().required(t("asset.issue.apply.validation.nameRequired")),
code: yup.string().required(t("asset.issue.apply.validation.codeRequired")),
categoryId: yup.string().required(t("asset.issue.apply.validation.categoryRequired")),
}),
);
function handleSubmit(values: GenericObject) {
emit("next", values);
}
</script>
<template>
<Form
:validation-schema="schema" :initial-values="initialData"
@submit="handleSubmit"
>
<div class="space-y-5">
<div>
<Field name="name" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
:label="t('asset.issue.apply.productName')"
:placeholder="t('asset.issue.apply.enterProductName')"
/>
</template>
</Field>
<ErrorMessage name="name" />
</div>
<div>
<Field name="code" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
:label="t('asset.issue.apply.productCode')"
:placeholder="t('asset.issue.apply.enterProductCode')"
/>
</template>
</Field>
<ErrorMessage name="code" />
</div>
<div>
<Field name="categoryId" type="text">
<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-option v-for="item in categories" :key="item.id" :value="item.id">
{{ item.name }}
</ion-select-option>
</ion-select>
</template>
</Field>
<ErrorMessage name="categoryId" />
</div>
<div>
<Field name="estimatedValue" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
type="text"
inputmode="numeric"
:label="t('asset.issue.apply.productValue')"
:placeholder="t('asset.issue.apply.enterProductValue')"
/>
</template>
</Field>
<ErrorMessage name="estimatedValue" />
</div>
<div>
<Field name="proofDocuments" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
:label="t('asset.issue.apply.assetProof')"
:placeholder="t('asset.issue.apply.enterAssetProof')"
/>
</template>
</Field>
<ErrorMessage name="proofDocuments" />
</div>
<div>
<Field name="totalSupplyLimit" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
type="number"
inputmode="numeric"
:label="t('asset.issue.apply.totalSupplyLimit')"
:placeholder="t('asset.issue.apply.enterTotalSupplyLimit')"
/>
</template>
</Field>
<ErrorMessage name="totalSupplyLimit" />
</div>
<ion-button type="submit" expand="block">
{{ t('asset.issue.apply.next') }}
</ion-button>
</div>
</Form>
</template>
<style lang='css' scoped></style>

View File

@@ -0,0 +1,27 @@
<script lang='ts' setup>
import { ribbonOutline } from "ionicons/icons";
const { t } = useI18n();
</script>
<template>
<div>
<ui-result
:icon="ribbonOutline"
:title="t('asset.issue.apply.done.title')"
:description="t('asset.issue.apply.done.description')"
>
<template #extra>
<ion-button
expand="block"
color="primary"
router-link="/issue/issuance-products"
>
{{ t('asset.issue.apply.done.viewProducts') }}
</ion-button>
</template>
</ui-result>
</div>
</template>
<style lang='css' scoped></style>

View File

@@ -1,14 +1,62 @@
<script lang='ts' setup>
import type { GenericObject } from "vee-validate";
import type { RwaIssuanceProductBody } from "@/api/types";
import { useRouteQuery } from "@vueuse/router";
import { useI18n } from "vue-i18n";
import { client, safeClient } from "@/api";
import Base from "./base.vue";
import Done from "./done.vue";
import IssuePeriod from "./issue-period.vue";
const { t } = useI18n();
const now = useNow();
const { data: categories = [] } = await safeClient(client.api.rwa.issuance.categories.get());
const step = useRouteQuery<number>("step", 1, { transform: v => Number(v), mode: "push" });
const initialData: RwaIssuanceProductBody = {
product: {
name: "",
code: "",
categoryId: categories!.length > 0 ? categories![0].id : "",
},
editions: [
{
editionName: "",
launchDate: useDateFormat(now.value, "YYYY/MM/DD").value,
perUserLimit: "",
subscriptionDeadline: useDateFormat(now.value, "YYYY/MM/DD").value,
totalSupply: "",
unitPrice: "",
dividendRate: "",
},
],
};
const form = useStorage<RwaIssuanceProductBody>("issuing-apply-form", { ...initialData });
function handleNext(values: GenericObject) {
form.value.product = { ...values as any };
step.value = 2;
}
async function handleSubmit(editions: RwaIssuanceProductBody["editions"]) {
form.value.editions = editions;
await client.api.rwa.issuance.products.bundle.post(form.value);
form.value = { ...initialData };
step.value = 3;
}
</script>
<template>
<IonPage>
<ion-header>
<ion-toolbar class="ui-toolbar">
<ion-title>Issuing Apply</ion-title>
<ion-title>{{ t('asset.issue.apply.title') }}</ion-title>
</ion-toolbar>
</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" />
<IssuePeriod v-else-if="step === 2" :initial-data="form.editions" @submit="handleSubmit" />
<Done v-else />
</IonContent>
</IonPage>
</template>

View File

@@ -0,0 +1,173 @@
<script lang='ts' setup>
import type { GenericObject } from "vee-validate";
import type { RwaIssuanceProductBody } from "@/api/types";
import { toTypedSchema } from "@vee-validate/yup";
import { addOutline, removeOutline } from "ionicons/icons";
import { ErrorMessage, Field, FieldArray, Form } from "vee-validate";
import { useI18n } from "vue-i18n";
import * as yup from "yup";
const props = defineProps<{
initialData: RwaIssuanceProductBody["editions"];
}>();
const emit = defineEmits<{
(e: "submit", values: RwaIssuanceProductBody["editions"]): void;
}>();
const { t } = useI18n();
const now = useNow();
const initialIssuePeriod: RwaIssuanceProductBody["editions"][0] = {
editionName: "",
launchDate: useDateFormat(now.value, "YYYY/MM/DD").value,
perUserLimit: "",
subscriptionDeadline: useDateFormat(now.value, "YYYY/MM/DD").value,
totalSupply: "",
unitPrice: "",
dividendRate: "",
};
const initialValues = ref({
editions: props.initialData || [{ ...initialIssuePeriod }],
});
const schema = toTypedSchema(yup.object({
editions: yup.array().of(
yup.object({
editionName: yup.string().required(t("asset.issue.apply.validation.editionNameRequired")),
launchDate: yup.string().required(t("asset.issue.apply.validation.launchDateRequired")),
perUserLimit: yup.string().required(t("asset.issue.apply.validation.perUserLimitRequired")),
totalSupply: yup.string().required(t("asset.issue.apply.validation.totalSupplyRequired")),
subscriptionDeadline: yup.string().required(t("asset.issue.apply.validation.subscriptionDeadlineRequired")),
unitPrice: yup.string().required(t("asset.issue.apply.validation.unitPriceRequired")),
dividendRate: yup.string().required(t("asset.issue.apply.validation.dividendRateRequired")),
}),
),
}));
function handleSubmit(values: GenericObject) {
emit("submit", values.editions);
}
</script>
<template>
<Form :validation-schema="schema" :initial-values="initialValues" class="space-y-5" @submit="handleSubmit">
<FieldArray v-slot="{ fields, push, remove }" name="editions">
<ui-collapse v-for="(entry, idx) in fields" :key="entry.key" :title="t('asset.issue.apply.issuePeriodIndex', { index: idx + 1 })" size="small">
<div>
<Field :name="`editions[${idx}].editionName`">
<template #default="{ field }">
<ui-input-label
v-bind="field"
:label="t('asset.issue.apply.editionName')"
:placeholder="t('asset.issue.apply.enterEditionName')"
/>
</template>
</Field>
<ErrorMessage :name="`editions[${idx}].editionName`" />
</div>
<div>
<Field :name="`editions[${idx}].launchDate`">
<template #default="{ field }">
<ui-datetime
v-bind="field"
:label="t('asset.issue.apply.launchDate')"
/>
</template>
</Field>
<ErrorMessage :name="`editions[${idx}].launchDate`" />
</div>
<div>
<Field :name="`editions[${idx}].perUserLimit`" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
type="number"
inputmode="numeric"
:label="t('asset.issue.apply.perUserLimit')"
:placeholder="t('asset.issue.apply.enterPerUserLimit')"
/>
</template>
</Field>
<ErrorMessage :name="`editions[${idx}].perUserLimit`" />
</div>
<div>
<Field :name="`editions[${idx}].totalSupply`" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
type="number"
inputmode="numeric"
:label="t('asset.issue.apply.totalSupply')"
:placeholder="t('asset.issue.apply.enterTotalSupply')"
/>
</template>
</Field>
<ErrorMessage :name="`editions[${idx}].totalSupply`" />
</div>
<div>
<Field :name="`editions[${idx}].subscriptionDeadline`">
<template #default="{ field }">
<ui-datetime
v-bind="field"
:label="t('asset.issue.apply.subscriptionDeadline')"
/>
</template>
</Field>
<ErrorMessage :name="`editions[${idx}].subscriptionDeadline`" />
</div>
<div>
<Field :name="`editions[${idx}].unitPrice`" type="text">
<template #default="{ field }">
<ui-input-label
v-bind="field"
type="number"
inputmode="numeric"
:label="t('asset.issue.apply.unitPrice')"
:placeholder="t('asset.issue.apply.enterUnitPrice')"
/>
</template>
</Field>
<ErrorMessage :name="`editions[${idx}].unitPrice`" />
</div>
<div>
<Field :name="`editions[${idx}].dividendRate`" type="number">
<template #default="{ field }">
<ui-input-label
v-bind="field"
type="number"
inputmode="numeric"
:label="t('asset.issue.apply.dividendRate')"
:placeholder="t('asset.issue.apply.enterDividendRate')"
/>
</template>
</Field>
<ErrorMessage :name="`editions[${idx}].dividendRate`" />
</div>
<ion-button color="tertiary" size="small" class="ui-button" @click="remove(idx)">
<ion-icon slot="start" :icon="removeOutline" />
<span>{{ t('asset.issue.apply.removeItem') }}</span>
</ion-button>
</ui-collapse>
<ion-button expand="block" color="tertiary" fill="outline" @click="push({ ...initialIssuePeriod })">
<ion-icon slot="icon-only" :icon="addOutline" />
<span>{{ t('asset.issue.apply.addStep') }}</span>
</ion-button>
</FieldArray>
<ion-button expand="block" @click="$router.back()">
{{ t('asset.issue.apply.back') }}
</ion-button>
<ion-button expand="block" type="submit">
{{ t('asset.issue.apply.submit') }}
</ion-button>
</Form>
</template>
<style lang='css' scoped></style>

View File

@@ -30,7 +30,6 @@ import WalletCard from "./components/wallet-card.vue";
<WalletCard />
<IssuingAsset />
<PurchaseAsset />
<AssetBalance />
<MyRevenue />
</IonContent>
</IonPage>

View File

@@ -160,6 +160,7 @@ onMounted(() => {
<ion-modal :keep-contents-mounted="true">
<ion-datetime
id="datetime"
class="ui-datetime"
done-text="Done"
presentation="date"
:value="userProfile?.birthday"
@@ -187,21 +188,4 @@ ion-avatar {
ion-item {
--min-height: 60px;
}
ion-datetime {
--background: rgb(255 255 255);
--background-rgb: 255, 255, 255;
--wheel-highlight-background: rgb(194 194 194);
--wheel-fade-background-rgb: 255, 255, 255;
border-radius: 16px;
overflow: hidden;
}
@media (prefers-color-scheme: dark) {
ion-datetime {
--background: rgb(15, 15, 15);
--background-rgb: 15, 15, 15;
--wheel-highlight-background: rgb(50, 50, 50);
--wheel-highlight-border-radius: 48px;
--wheel-fade-background-rgb: 15, 15, 15;
}
}
</style>