feat: internationalize various components and views with i18n translations
This commit is contained in:
@@ -33,7 +33,7 @@ function validate(value: string) {
|
||||
|
||||
async function onSubmit() {
|
||||
const loading = await loadingController.create({
|
||||
message: "提交中...",
|
||||
message: t("recharge.fiat.submitting"),
|
||||
});
|
||||
await loading.present();
|
||||
await safeClient(client.api.deposit.fiat.post(form.value)).finally(async () => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import html2canvas from "html2canvas";
|
||||
import MaterialSymbolsDownload from "~icons/material-symbols/download";
|
||||
import MaterialSymbolsUpload from "~icons/material-symbols/upload";
|
||||
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const { userProfile } = storeToRefs(userStore);
|
||||
const url = computed(() => {
|
||||
@@ -63,7 +64,7 @@ async function captureQRCode(): Promise<string> {
|
||||
|
||||
async function handleShare() {
|
||||
const loading = await loadingController.create({
|
||||
message: "准备分享...",
|
||||
message: t("onchainAddress.preparing"),
|
||||
});
|
||||
await loading.present();
|
||||
|
||||
@@ -78,8 +79,8 @@ async function handleShare() {
|
||||
const file = new File([blob], "qrcode.png", { type: "image/png" });
|
||||
|
||||
await share({
|
||||
title: "我的转账二维码",
|
||||
text: `我的 ID: ${userProfile.value?.uid}`,
|
||||
title: t("onchainAddress.shareTitle"),
|
||||
text: `${t("onchainAddress.myId")}: ${userProfile.value?.uid}`,
|
||||
files: [file],
|
||||
});
|
||||
}
|
||||
@@ -87,7 +88,7 @@ async function handleShare() {
|
||||
// 不支持则提示复制链接
|
||||
await navigator.clipboard.writeText(url.value);
|
||||
const toast = await toastController.create({
|
||||
message: "链接已复制到剪贴板",
|
||||
message: t("onchainAddress.linkCopied"),
|
||||
duration: 2000,
|
||||
color: "success",
|
||||
});
|
||||
@@ -107,17 +108,17 @@ async function handleShare() {
|
||||
});
|
||||
|
||||
await Share.share({
|
||||
title: "我的转账二维码",
|
||||
text: `我的 ID: ${userProfile.value?.uid}`,
|
||||
title: t("onchainAddress.shareTitle"),
|
||||
text: `${t("onchainAddress.myId")}: ${userProfile.value?.uid}`,
|
||||
url: result.uri,
|
||||
dialogTitle: "分享二维码",
|
||||
dialogTitle: t("onchainAddress.shareDialogTitle"),
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Share error:", error);
|
||||
const toast = await toastController.create({
|
||||
message: "分享失败",
|
||||
message: t("onchainAddress.shareFailed"),
|
||||
duration: 2000,
|
||||
color: "danger",
|
||||
});
|
||||
@@ -133,7 +134,7 @@ async function handleShare() {
|
||||
*/
|
||||
async function handleDownload() {
|
||||
const loading = await loadingController.create({
|
||||
message: "正在保存...",
|
||||
message: t("onchainAddress.saving"),
|
||||
});
|
||||
await loading.present();
|
||||
|
||||
@@ -159,7 +160,7 @@ async function handleDownload() {
|
||||
});
|
||||
|
||||
const toast = await toastController.create({
|
||||
message: "二维码已保存到文件",
|
||||
message: t("onchainAddress.saveSuccess"),
|
||||
duration: 2000,
|
||||
color: "success",
|
||||
});
|
||||
@@ -169,7 +170,7 @@ async function handleDownload() {
|
||||
catch (error) {
|
||||
console.error("Download error:", error);
|
||||
const toast = await toastController.create({
|
||||
message: "保存失败",
|
||||
message: t("onchainAddress.saveFailed"),
|
||||
duration: 2000,
|
||||
color: "danger",
|
||||
});
|
||||
@@ -188,7 +189,7 @@ async function handleDownload() {
|
||||
<ion-buttons slot="start">
|
||||
<ui-back-button />
|
||||
</ion-buttons>
|
||||
<ion-title>转账二维码</ion-title>
|
||||
<ion-title>{{ t("onchainAddress.title") }}</ion-title>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
<IonContent :fullscreen="true" class="ion-padding">
|
||||
@@ -210,7 +211,7 @@ async function handleDownload() {
|
||||
<div class="mt-4">
|
||||
<ion-text color="secondary">
|
||||
<div class="text-sm text-text-400 font-semibold">
|
||||
我的 ID
|
||||
{{ t("onchainAddress.myId") }}
|
||||
</div>
|
||||
</ion-text>
|
||||
<ion-text>
|
||||
|
||||
@@ -6,6 +6,7 @@ import IconParkOutlineDownload from "~icons/icon-park-outline/download";
|
||||
import IconParkOutlinePhone from "~icons/icon-park-outline/phone";
|
||||
import IconParkOutlineShare from "~icons/icon-park-outline/share";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { canInstall, isInstalled, install, isIOS, isIOSSafari } = usePWAInstall();
|
||||
const platform = usePlatform();
|
||||
|
||||
@@ -39,9 +40,9 @@ const shouldShowDownloadButton = computed(() => {
|
||||
async function handleInstall() {
|
||||
if (isIOSSafari) {
|
||||
const alert = await alertController.create({
|
||||
header: "iOS 安装指引",
|
||||
message: "请点击浏览器底部的分享按钮,然后选择\"添加到主屏幕\"",
|
||||
buttons: ["知道了"],
|
||||
header: t("pwa.download.iosInstallHeader"),
|
||||
message: t("pwa.download.iosInstallMessage"),
|
||||
buttons: [t("pwa.download.iosInstallButton")],
|
||||
});
|
||||
await alert.present();
|
||||
return;
|
||||
@@ -55,17 +56,17 @@ async function handleInstall() {
|
||||
localStorage.setItem("pwa-was-installed", "true");
|
||||
|
||||
const alert = await alertController.create({
|
||||
header: "安装成功",
|
||||
message: "应用已成功安装到您的设备",
|
||||
buttons: ["确定"],
|
||||
header: t("pwa.download.installSuccessHeader"),
|
||||
message: t("pwa.download.installSuccessMessage"),
|
||||
buttons: [t("pwa.download.installSuccessButton")],
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
else if (result.outcome === "ios-instruction") {
|
||||
const alert = await alertController.create({
|
||||
header: "iOS 安装指引",
|
||||
message: "请点击浏览器底部的分享按钮,然后选择\"添加到主屏幕\"",
|
||||
buttons: ["知道了"],
|
||||
header: t("pwa.download.iosInstallHeader"),
|
||||
message: t("pwa.download.iosInstallMessage"),
|
||||
buttons: [t("pwa.download.iosInstallButton")],
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
@@ -90,7 +91,7 @@ watch(isInstalled, (installed) => {
|
||||
<ion-page>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ion-title>下载应用</ion-title>
|
||||
<ion-title>{{ t("pwa.download.title") }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -103,7 +104,7 @@ watch(isInstalled, (installed) => {
|
||||
{{ appName }}
|
||||
</h1>
|
||||
<p class="text-text-500 text-center text-sm">
|
||||
随时随地,管理您的数字资产
|
||||
{{ t("pwa.download.subtitle") }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -112,10 +113,10 @@ watch(isInstalled, (installed) => {
|
||||
<div class="bg-success/10 border border-success/20 rounded-2xl p-6 text-center">
|
||||
<IconParkOutlineCheckOne class="text-5xl text-success mx-auto mb-4" />
|
||||
<h2 class="text-lg font-semibold text-success mb-2">
|
||||
应用已安装
|
||||
{{ t("pwa.download.installed") }}
|
||||
</h2>
|
||||
<p class="text-sm text-text-500">
|
||||
您可以在主屏幕找到应用图标
|
||||
{{ t("pwa.download.installedDesc") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -126,7 +127,7 @@ watch(isInstalled, (installed) => {
|
||||
<div class="flex items-center mb-4">
|
||||
<IconParkOutlinePhone class="text-2xl text-primary mr-3" />
|
||||
<h2 class="text-lg font-semibold">
|
||||
iOS 安装步骤
|
||||
{{ t("pwa.download.iosInstallTitle") }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -138,9 +139,9 @@ watch(isInstalled, (installed) => {
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm">
|
||||
点击 Safari 底部的
|
||||
{{ t("pwa.download.iosStep1") }}
|
||||
<IconParkOutlineShare class="inline-block text-primary mx-1" />
|
||||
<strong>分享</strong> 按钮
|
||||
<strong>{{ t("pwa.download.iosStep1Button") }}</strong> 按钮
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,8 +153,8 @@ watch(isInstalled, (installed) => {
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm">
|
||||
在弹出的菜单中,向下滚动找到
|
||||
<strong>"添加到主屏幕"</strong>
|
||||
{{ t("pwa.download.iosStep2") }}
|
||||
<strong>"{{ t("pwa.download.iosStep2Option") }}"</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -165,7 +166,7 @@ watch(isInstalled, (installed) => {
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm">
|
||||
点击 <strong>"添加"</strong> 完成安装
|
||||
{{ t("pwa.download.iosStep3") }} <strong>"{{ t("pwa.download.iosStep3Button") }}"</strong> {{ t("pwa.download.iosStep3Complete") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -176,7 +177,7 @@ watch(isInstalled, (installed) => {
|
||||
<div class="bg-warning/10 border border-warning/20 rounded-xl p-4 text-sm text-text-600">
|
||||
<p class="flex-center">
|
||||
<span class="mr-2">💡</span>
|
||||
<span>请使用 Safari 浏览器进行安装</span>
|
||||
<span>{{ t("pwa.download.iosTip") }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -186,10 +187,10 @@ watch(isInstalled, (installed) => {
|
||||
<div class="bg-background-secondary rounded-2xl p-6 mb-6 text-center">
|
||||
<IconParkOutlineDownload class="text-5xl text-primary mx-auto mb-4" />
|
||||
<h2 class="text-xl font-semibold mb-2">
|
||||
{{ wasUninstalled ? '重新安装应用' : '安装到设备' }}
|
||||
{{ wasUninstalled ? t("pwa.download.reinstallButton") : t("pwa.download.installButton") }}
|
||||
</h2>
|
||||
<p class="text-sm text-text-500 mb-6">
|
||||
{{ wasUninstalled ? '快速重新安装应用到您的设备' : '一键安装,无需下载,即刻使用' }}
|
||||
{{ wasUninstalled ? t("pwa.download.reinstallDesc") : t("pwa.download.installDesc") }}
|
||||
</p>
|
||||
|
||||
<ion-button
|
||||
@@ -201,7 +202,7 @@ watch(isInstalled, (installed) => {
|
||||
>
|
||||
<IconParkOutlineDownload v-if="!isLoading" class="mr-2" />
|
||||
<ion-spinner v-if="isLoading" name="crescent" class="mr-2" />
|
||||
{{ isLoading ? '安装中...' : wasUninstalled ? '重新安装' : '立即安装' }}
|
||||
{{ isLoading ? t("pwa.download.installing") : wasUninstalled ? t("pwa.download.reinstall") : t("pwa.download.install") }}
|
||||
</ion-button>
|
||||
</div>
|
||||
|
||||
@@ -211,19 +212,19 @@ watch(isInstalled, (installed) => {
|
||||
<div class="w-8 h-8 rounded-full bg-success/10 flex items-center justify-center mr-3">
|
||||
<IconParkOutlineCheckOne class="text-success" />
|
||||
</div>
|
||||
<span class="text-text-600">无需应用商店,快速安装</span>
|
||||
<span class="text-text-600">{{ t("pwa.download.advantage1") }}</span>
|
||||
</div>
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="w-8 h-8 rounded-full bg-success/10 flex items-center justify-center mr-3">
|
||||
<IconParkOutlineCheckOne class="text-success" />
|
||||
</div>
|
||||
<span class="text-text-600">占用空间小,运行流畅</span>
|
||||
<span class="text-text-600">{{ t("pwa.download.advantage2") }}</span>
|
||||
</div>
|
||||
<div class="flex items-center text-sm">
|
||||
<div class="w-8 h-8 rounded-full bg-success/10 flex items-center justify-center mr-3">
|
||||
<IconParkOutlineCheckOne class="text-success" />
|
||||
</div>
|
||||
<span class="text-text-600">自动更新,始终最新版本</span>
|
||||
<span class="text-text-600">{{ t("pwa.download.advantage3") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -233,10 +234,10 @@ watch(isInstalled, (installed) => {
|
||||
<div class="bg-background-secondary rounded-2xl p-6 text-center">
|
||||
<IconParkOutlineCheckOne class="text-5xl text-success mx-auto mb-4" />
|
||||
<h2 class="text-lg font-semibold mb-2">
|
||||
您正在使用原生应用
|
||||
{{ t("pwa.download.nativeAppTitle") }}
|
||||
</h2>
|
||||
<p class="text-sm text-text-500">
|
||||
已经是最新版本,无需下载
|
||||
{{ t("pwa.download.nativeAppDesc") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -245,10 +246,10 @@ watch(isInstalled, (installed) => {
|
||||
<div v-else class="w-full max-w-md">
|
||||
<div class="bg-warning/10 border border-warning/20 rounded-2xl p-6 text-center">
|
||||
<p class="text-sm text-text-600">
|
||||
当前浏览器暂不支持应用安装
|
||||
{{ t("pwa.download.notSupportedTitle") }}
|
||||
</p>
|
||||
<p class="text-xs text-text-500 mt-2">
|
||||
建议使用 Chrome、Safari 或 Edge 浏览器
|
||||
{{ t("pwa.download.notSupportedDesc") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,19 +4,21 @@ import type { EaringsSummaryData } from "@/api/types";
|
||||
defineProps<{
|
||||
data: EaringsSummaryData | null;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-text-900 rounded-xl p-7">
|
||||
<div>
|
||||
<div class="text-sm mb-2">
|
||||
本月总收益
|
||||
{{ t("revenue.monthly.monthTotalRevenue") }}
|
||||
</div>
|
||||
<div class="text-4xl font-bold">
|
||||
{{ formatAmountWithSplit(data?.realized.thisMonth) }}
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs mt-1">
|
||||
<div>昨日收益</div>
|
||||
<div>{{ t("revenue.monthly.yesterdayRevenue") }}</div>
|
||||
<div>+{{ formatAmountWithSplit(data?.realized.yesterday) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,7 +29,7 @@ defineProps<{
|
||||
{{ formatAmountWithSplit(data?.realized.lastMonth) }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
上月收益
|
||||
{{ t("revenue.monthly.lastMonthRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-auto w-px bg-text-800" />
|
||||
@@ -36,7 +38,7 @@ defineProps<{
|
||||
{{ formatAmountWithSplit(data?.unrealized.total) }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
累计总收益
|
||||
{{ t("revenue.monthly.totalRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang='ts' setup>
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { data } = await safeClient(client.api.earnings.details.post({
|
||||
limit: 6,
|
||||
offset: 0,
|
||||
@@ -10,9 +12,9 @@ const { data } = await safeClient(client.api.earnings.details.post({
|
||||
<template>
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<h4>本月记录</h4>
|
||||
<h4>{{ t("revenue.monthly.monthRecords") }}</h4>
|
||||
<ion-button v-show="data?.data.length" class="text-sm">
|
||||
查看全部
|
||||
{{ t("revenue.monthly.viewAll") }}
|
||||
</ion-button>
|
||||
</ion-list-header>
|
||||
<ui-empty v-if="!data || data.data.length === 0" class="mt-5" />
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import dayjs from "dayjs";
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const now = dayjs();
|
||||
|
||||
const { data } = safeClient(client.api.earnings.type_summary.post({
|
||||
@@ -12,7 +14,7 @@ const { data } = safeClient(client.api.earnings.type_summary.post({
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h4>本月收益来源</h4>
|
||||
<h4>{{ t("revenue.monthly.monthSource") }}</h4>
|
||||
<ui-empty v-if="data?.data.length === 0" />
|
||||
<template v-else>
|
||||
<div v-for="item in data?.data" :key="item.type" class="card">
|
||||
@@ -31,7 +33,7 @@ const { data } = safeClient(client.api.earnings.type_summary.post({
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 " />
|
||||
<div class="text-xs">
|
||||
{{ item.count }}项
|
||||
{{ item.count }}{{ t("revenue.monthly.itemsCount") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-text-200">
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import dayjs from "dayjs";
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const tradingViewInst = useTemplateRef<HTMLDivElement>("tradingViewInst");
|
||||
const now = dayjs();
|
||||
|
||||
@@ -31,7 +33,7 @@ useTradingView(tradingViewInst, {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h4>本月收益趋势</h4>
|
||||
<h4>{{ t("revenue.monthly.monthTrend") }}</h4>
|
||||
<ui-empty v-if="!data || data.data.length === 0" class="mt-5" />
|
||||
<div v-else ref="tradingViewInst" />
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ function getStatusColor(status?: string) {
|
||||
}
|
||||
|
||||
function getStatusText(status?: string) {
|
||||
return status === "pending" ? "待确认" : "处理中";
|
||||
return status === "pending" ? t("revenue.pending.statusPending") : t("revenue.pending.statusProcessing");
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -41,7 +41,7 @@ function getStatusText(status?: string) {
|
||||
<!-- 待确认总金额卡片 -->
|
||||
<div class="bg-text-900 rounded-xl p-6 mb-4">
|
||||
<div class="text-sm text-text-400 mb-2">
|
||||
待确认总金额
|
||||
{{ t("revenue.pending.totalPending") }}
|
||||
</div>
|
||||
<div class="flex items-end gap-2">
|
||||
<div class="text-3xl font-bold">
|
||||
@@ -53,14 +53,14 @@ function getStatusText(status?: string) {
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mt-3 text-xs text-text-400">
|
||||
<i-ic-baseline-info class="text-base" />
|
||||
<span>收益将在预计日期后1-3个工作日内到账</span>
|
||||
<span>{{ t("revenue.pending.accountTip") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 待确认收益列表 -->
|
||||
<div class="mb-3">
|
||||
<div class="text-base font-medium mb-3">
|
||||
待确认明细
|
||||
{{ t("revenue.pending.detailTitle") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -97,7 +97,7 @@ function getStatusText(status?: string) {
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 text-xs text-text-400">
|
||||
<i-ic-outline-access-time class="text-sm" />
|
||||
<span>预计到账:{{ useDateFormat(item.expectedAt, 'YY/MM/DD') }}</span>
|
||||
<span>{{ t("revenue.pending.expectedDate") }}{{ useDateFormat(item.expectedAt, 'YY/MM/DD') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ion-item>
|
||||
@@ -110,12 +110,12 @@ function getStatusText(status?: string) {
|
||||
<!-- <i-ic-baseline-lightbulb-outline class="text-yellow-500 text-lg mt-0.5 flex-shrink-0" /> -->
|
||||
<div class="text-xs text-text-400 leading-relaxed">
|
||||
<div class="font-medium mb-2">
|
||||
待确认收益说明:
|
||||
{{ t("revenue.pending.noteTitle") }}
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<div>• 分红收益:预计在分红日后2-3个工作日到账</div>
|
||||
<div>• 资产增值:预计在结算日后1-2个工作日到账</div>
|
||||
<div>• 交易收益:预计在交易完成后1个工作日到账</div>
|
||||
<div>{{ t("revenue.pending.dividendNote") }}</div>
|
||||
<div>{{ t("revenue.pending.appreciationNote") }}</div>
|
||||
<div>{{ t("revenue.pending.tradeNote") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -120,7 +120,7 @@ onBeforeMount(() => {
|
||||
{{ item.type }}
|
||||
</ion-badge>
|
||||
<ion-badge color="success" class="text-xs px-2 py-0.5">
|
||||
已完成
|
||||
{{ t("revenue.records.completed") }}
|
||||
</ion-badge>
|
||||
</div>
|
||||
<div class="text-base font-semibold mb-1">
|
||||
@@ -147,7 +147,7 @@ onBeforeMount(() => {
|
||||
<ion-infinite-scroll threshold="100px" @ion-infinite="handleInfinite">
|
||||
<ion-infinite-scroll-content
|
||||
loading-spinner="bubbles"
|
||||
loading-text="加载中..."
|
||||
:loading-text="t('revenue.records.loading')"
|
||||
/>
|
||||
</ion-infinite-scroll>
|
||||
</ion-content>
|
||||
|
||||
@@ -4,19 +4,21 @@ import type { EaringsSummaryData } from "@/api/types";
|
||||
defineProps<{
|
||||
data: EaringsSummaryData | null;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-text-900 rounded-xl p-7">
|
||||
<div>
|
||||
<div class="text-sm mb-2">
|
||||
累计总收益
|
||||
{{ t("revenue.total.totalRevenue") }}
|
||||
</div>
|
||||
<div class="text-4xl font-bold">
|
||||
{{ formatAmountWithSplit(data?.realized.total) }}
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs mt-1">
|
||||
<div>昨日收益</div>
|
||||
<div>{{ t("revenue.total.yesterdayRevenue") }}</div>
|
||||
<div>+{{ formatAmountWithSplit(data?.realized.yesterday) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,7 +29,7 @@ defineProps<{
|
||||
{{ formatAmountWithSplit(data?.realized.thisMonth) }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
本月收益
|
||||
{{ t("revenue.total.monthRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-auto w-px bg-text-800" />
|
||||
@@ -36,7 +38,7 @@ defineProps<{
|
||||
{{ formatAmountWithSplit(data?.unrealized.total) }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
待确认收益
|
||||
{{ t("revenue.total.pendingRevenue") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang='ts' setup>
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { data } = await safeClient(client.api.earnings.details.post({
|
||||
limit: 6,
|
||||
offset: 0,
|
||||
@@ -10,9 +12,9 @@ const { data } = await safeClient(client.api.earnings.details.post({
|
||||
<template>
|
||||
<ion-list>
|
||||
<ion-list-header>
|
||||
<h4>最近记录</h4>
|
||||
<h4>{{ t("revenue.total.recentRecords") }}</h4>
|
||||
<ion-button v-show="data?.data.length" class="text-sm">
|
||||
查看全部
|
||||
{{ t("revenue.total.viewAll") }}
|
||||
</ion-button>
|
||||
</ion-list-header>
|
||||
<ui-empty v-if="!data || data.data.length === 0" class="mt-5" />
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<script lang='ts' setup>
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { data } = safeClient(client.api.earnings.type_summary.post());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h4>收益来源</h4>
|
||||
<h4>{{ t("revenue.total.revenueSource") }}</h4>
|
||||
<ui-empty v-if="data?.data.length === 0" />
|
||||
<template v-else>
|
||||
<div v-for="item in data?.data" :key="item.type" class="card">
|
||||
@@ -25,7 +27,7 @@ const { data } = safeClient(client.api.earnings.type_summary.post());
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-2 h-2 " />
|
||||
<div class="text-xs">
|
||||
{{ item.count }}项
|
||||
{{ item.count }}{{ t("revenue.total.itemsCount") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-text-200">
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import dayjs from "dayjs";
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const tradingViewInst = useTemplateRef<HTMLDivElement>("tradingViewInst");
|
||||
const now = dayjs();
|
||||
|
||||
@@ -31,7 +33,7 @@ useTradingView(tradingViewInst, {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h4>收益趋势</h4>
|
||||
<h4>{{ t("revenue.total.revenueTrend") }}</h4>
|
||||
<ui-empty v-if="!data || data.data.length === 0" class="mt-5" />
|
||||
<div v-else ref="tradingViewInst" />
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script lang='ts' setup>
|
||||
import { toastController } from "@ionic/vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const scanner = useTemplateRef<InstanceType<typeof QrScanner>>("scanner");
|
||||
|
||||
async function handleSuccess(value: string) {
|
||||
const toast = await toastController.create({
|
||||
message: "扫描成功",
|
||||
message: t("scanQr.scanSuccess"),
|
||||
duration: 1000,
|
||||
position: "bottom",
|
||||
color: "success",
|
||||
@@ -21,7 +22,7 @@ async function handleSuccess(value: string) {
|
||||
|
||||
function handleError(error: Error) {
|
||||
toastController.create({
|
||||
message: `扫描失败: ${error.message}`,
|
||||
message: `${t("scanQr.scanFailed")}${error.message}`,
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
color: "danger",
|
||||
|
||||
@@ -40,7 +40,7 @@ async function handleSubmit(values: GenericObject) {
|
||||
router.back();
|
||||
}
|
||||
catch (error) {
|
||||
console.error("添加银行卡失败:", error);
|
||||
console.error(t("tradeSettings.bankManagement.addError"), error);
|
||||
}
|
||||
}
|
||||
function handleBankChange(event: any) {
|
||||
|
||||
@@ -70,15 +70,15 @@ async function handleSetDefault(card: any) {
|
||||
// 删除银行卡
|
||||
async function handleDeleteCard(card: any) {
|
||||
const alert = await alertController.create({
|
||||
header: "删除银行卡",
|
||||
message: `确定要删除 ${card.bankName} (${card.accountName}) 吗?此操作无法撤销。`,
|
||||
header: t("tradeSettings.bankManagement.deleteTitle"),
|
||||
message: `${t("common.confirmDelete")} ${card.bankName} (${card.accountName}) ${t("tradeSettings.bankManagement.deleteMessage")}`,
|
||||
buttons: [
|
||||
{
|
||||
text: "取消",
|
||||
text: t("tradeSettings.bankManagement.cancel"),
|
||||
role: "cancel",
|
||||
},
|
||||
{
|
||||
text: "删除",
|
||||
text: t("tradeSettings.bankManagement.delete"),
|
||||
role: "destructive",
|
||||
handler: async () => {
|
||||
await safeClient(() => client.api.bank_account({ id: card.id }).delete());
|
||||
|
||||
@@ -4,6 +4,8 @@ import type { MyIssueRwaData } from "@/api/types";
|
||||
const props = defineProps<{
|
||||
data: MyIssueRwaData | null;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -11,7 +13,7 @@ const props = defineProps<{
|
||||
<!-- document -->
|
||||
<div class="mt-5">
|
||||
<div class="font-semibold">
|
||||
相关文档
|
||||
{{ t('tradeSettings.myIssues.relatedDocs') }}
|
||||
</div>
|
||||
<div class="text-xs mt-2">
|
||||
{{ data?.proofDocumentIds }}
|
||||
|
||||
@@ -41,7 +41,7 @@ const { t } = useI18n();
|
||||
<!-- Rwa status -->
|
||||
<div class="mt-5">
|
||||
<div class="font-semibold">
|
||||
资产状态
|
||||
{{ t('tradeSettings.myIssues.assetStatus') }}
|
||||
</div>
|
||||
<div class="text-xs mt-2">
|
||||
{{ data?.status }}
|
||||
@@ -51,7 +51,7 @@ const { t } = useI18n();
|
||||
<!-- Rwa status history -->
|
||||
<div class="mt-5">
|
||||
<div class="font-semibold">
|
||||
状态历史
|
||||
{{ t('tradeSettings.myIssues.statusHistory') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,7 @@ function gotoEdit() {
|
||||
<div class="flex items-center my-3" @click="gotoEdit">
|
||||
<IcSharpEditNote class="inline-block text-xl mr-1 text-text-500" />
|
||||
<div class="text-xs">
|
||||
编辑资产
|
||||
{{ t('tradeSettings.myIssues.editAsset') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -73,7 +73,8 @@ function gotoEdit() {
|
||||
<ui-tab-pane name="overview" :title="t('market.tradeRwa.tabs.overview')">
|
||||
<RwaBase :data="data" />
|
||||
</ui-tab-pane>
|
||||
<ui-tab-pane name="about" title="相关文档">
|
||||
<ui-tab-pane name="about" :title="t('tradeSettings.myIssues.relatedDocs')">
|
||||
,
|
||||
<RwaAbout :data="data" />
|
||||
</ui-tab-pane>
|
||||
</ui-tabs>
|
||||
|
||||
@@ -19,21 +19,21 @@ function gotoMySubscribe(id: string) {
|
||||
<ion-grid>
|
||||
<ion-row class="ion-align-items-center text-xs text-text-500">
|
||||
<ion-col size="6">
|
||||
<div>名称/编号</div>
|
||||
<div>{{ t('tradeSettings.mySubscribe.nameOrNumber') }}</div>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<div class="text-right">
|
||||
单价
|
||||
{{ t('tradeSettings.mySubscribe.unitPrice') }}
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-col size="2">
|
||||
<div class="text-right">
|
||||
状态
|
||||
{{ t('tradeSettings.mySubscribe.status') }}
|
||||
</div>
|
||||
</ion-col>
|
||||
<ion-col size="3">
|
||||
<div class="text-right">
|
||||
总金额
|
||||
{{ t('tradeSettings.mySubscribe.totalAmount') }}
|
||||
</div>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
||||
@@ -41,7 +41,7 @@ const { data } = safeClient(client.api.rwa.subscription({ orderId: props.id }).g
|
||||
<!-- 申购状态 -->
|
||||
<div class="bg-faint rounded-2xl p-4">
|
||||
<div class="text-sm text-text-500 mb-2">
|
||||
申购状态
|
||||
{{ t('tradeSettings.mySubscribe.subscribeStatus') }}
|
||||
</div>
|
||||
<div class="text-lg font-semibold">
|
||||
{{ t(`trade.subscribeStatus.${data?.status}`) }}
|
||||
@@ -51,17 +51,17 @@ const { data } = safeClient(client.api.rwa.subscription({ orderId: props.id }).g
|
||||
<!-- 申购信息详情 -->
|
||||
<div class="bg-faint rounded-2xl p-4 space-y-4">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">申购数量</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.subscribeQuantity') }}</span>
|
||||
<span class="text-base font-semibold">{{ data?.quantity }}</span>
|
||||
</div>
|
||||
<ui-divider />
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">单价</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.unitPrice') }}</span>
|
||||
<span class="text-base font-semibold">${{ Number(data?.unitPrice).toFixed(2) }}</span>
|
||||
</div>
|
||||
<ui-divider />
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">总金额</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.totalAmount') }}</span>
|
||||
<span class="text-lg font-bold text-primary">${{ Number(data?.totalAmount).toFixed(2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,20 +69,20 @@ const { data } = safeClient(client.api.rwa.subscription({ orderId: props.id }).g
|
||||
<!-- 产品信息 -->
|
||||
<div class="bg-faint rounded-2xl p-4 space-y-4">
|
||||
<div class="text-base font-semibold mb-3">
|
||||
产品信息
|
||||
{{ t('tradeSettings.mySubscribe.productInfo') }}
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">产品类别</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.productCategory') }}</span>
|
||||
<span class="text-sm">{{ data?.edition.product.categoryId }}</span>
|
||||
</div>
|
||||
<ui-divider />
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">产品编码</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.productCode') }}</span>
|
||||
<span class="text-sm">{{ data?.edition.product.code }}</span>
|
||||
</div>
|
||||
<ui-divider />
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">产品名称</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.productName') }}</span>
|
||||
<span class="text-sm">{{ data?.edition.product.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,10 +90,10 @@ const { data } = safeClient(client.api.rwa.subscription({ orderId: props.id }).g
|
||||
<!-- 发行期信息 -->
|
||||
<div v-if="data?.edition" class="bg-faint rounded-2xl p-4 space-y-4">
|
||||
<div class="text-base font-semibold mb-3">
|
||||
发行期信息
|
||||
{{ t('tradeSettings.mySubscribe.periodInfo') }}
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">发行期编号</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.periodNumber') }}</span>
|
||||
<span class="text-sm">{{ data?.edition.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,15 +101,15 @@ const { data } = safeClient(client.api.rwa.subscription({ orderId: props.id }).g
|
||||
<!-- 时间信息 -->
|
||||
<div class="bg-faint rounded-2xl p-4 space-y-4">
|
||||
<div class="text-base font-semibold mb-3">
|
||||
时间信息
|
||||
{{ t('tradeSettings.mySubscribe.timeInfo') }}
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">申购时间</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.subscribeTime') }}</span>
|
||||
<span class="text-sm">{{ new Date(data?.createdAt!).toLocaleString('zh-CN') }}</span>
|
||||
</div>
|
||||
<ui-divider />
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-text-500">更新时间</span>
|
||||
<span class="text-sm text-text-500">{{ t('tradeSettings.mySubscribe.updateTime') }}</span>
|
||||
<span class="text-sm">{{ new Date(data?.updatedAt!).toLocaleString('zh-CN') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,8 @@ import { client, safeClient } from "@/api";
|
||||
import Category from "./components/category.vue";
|
||||
import MySubscribeList from "./components/my-subscribe-list.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const [query] = useResetRef<MySubscribeRwaBody>({
|
||||
categoryId: "",
|
||||
limit: 20,
|
||||
@@ -61,7 +63,7 @@ onBeforeMount(() => {
|
||||
<ion-header>
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>我的申购</ion-title>
|
||||
<ion-title>{{ t('tradeSettings.mySubscribe.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -76,7 +78,7 @@ onBeforeMount(() => {
|
||||
<ion-infinite-scroll threshold="100px" @ion-infinite="handleInfinite">
|
||||
<ion-infinite-scroll-content
|
||||
loading-spinner="bubbles"
|
||||
loading-text="加载中..."
|
||||
:loading-text="t('tradeSettings.mySubscribe.loading')"
|
||||
/>
|
||||
</ion-infinite-scroll>
|
||||
</ion-content>
|
||||
|
||||
@@ -12,7 +12,7 @@ const { t } = useI18n();
|
||||
<div>
|
||||
<div>
|
||||
<div class="font-semibold">
|
||||
关于
|
||||
{{ t("tradeTokenized.about.about") }}
|
||||
</div>
|
||||
<div class="text-xs mt-2">
|
||||
{{ data?.product?.description || t('market.tradeRwa.noDescription') }}
|
||||
@@ -22,7 +22,7 @@ const { t } = useI18n();
|
||||
<!-- document -->
|
||||
<div class="mt-5">
|
||||
<div class="font-semibold mb-4">
|
||||
相关文档
|
||||
{{ t("tradeTokenized.about.relatedDocs") }}
|
||||
</div>
|
||||
|
||||
<ui-file-preview :file-ids="props.data?.product?.proofDocumentIds || []" />
|
||||
|
||||
@@ -42,12 +42,12 @@ useTradingView(tradingViewInst, {
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="font-semibold">
|
||||
代币信息
|
||||
{{ t("tradeTokenized.base.tokenInfo") }}
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
代币符号
|
||||
{{ t("tradeTokenized.base.tokenSymbol") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ data?.assetCode }}
|
||||
@@ -55,7 +55,7 @@ useTradingView(tradingViewInst, {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
关联资产
|
||||
{{ t("tradeTokenized.base.relatedAsset") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ data?.product?.name }}
|
||||
@@ -63,7 +63,7 @@ useTradingView(tradingViewInst, {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
总供应量
|
||||
{{ t("tradeTokenized.base.totalSupply") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ Number(data?.totalSupply).toString() }}
|
||||
@@ -71,7 +71,7 @@ useTradingView(tradingViewInst, {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
市值
|
||||
{{ t("tradeTokenized.base.marketCap") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
${{ Number(data?.product?.estimatedValue).toString() }}
|
||||
@@ -79,7 +79,7 @@ useTradingView(tradingViewInst, {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
24小时成交量
|
||||
{{ t("tradeTokenized.base.volume24h") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
$500000
|
||||
@@ -87,7 +87,7 @@ useTradingView(tradingViewInst, {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
创建时间
|
||||
{{ t("tradeTokenized.base.createTime") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ useDateFormat(data?.createdAt, 'YYYY/MM/DD') }}
|
||||
@@ -97,18 +97,18 @@ useTradingView(tradingViewInst, {
|
||||
|
||||
<div class="mt-5 space-y-4">
|
||||
<div class="font-semibold">
|
||||
市场数据
|
||||
{{ t("tradeTokenized.base.marketData") }}
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Icon icon="hugeicons:trade-up" class="text-success" />
|
||||
<div class="text-sm">
|
||||
价格变动
|
||||
{{ t("tradeTokenized.base.priceChange") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
24小时
|
||||
{{ t("tradeTokenized.base.hours24") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ tickerData?.changePercent || '-' }}%
|
||||
@@ -116,7 +116,7 @@ useTradingView(tradingViewInst, {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
7天
|
||||
{{ t("tradeTokenized.base.days7") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
-%
|
||||
@@ -124,7 +124,7 @@ useTradingView(tradingViewInst, {
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="label">
|
||||
30天
|
||||
{{ t("tradeTokenized.base.days30") }}
|
||||
</div>
|
||||
<div class="value">
|
||||
-%
|
||||
|
||||
@@ -10,6 +10,8 @@ const props = defineProps<{
|
||||
data: TradableData | null;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const tradingviewOptions: Partial<ChartingLibraryWidgetOptions> = {
|
||||
disabled_features: [
|
||||
"create_volume_indicator_by_default",
|
||||
@@ -79,7 +81,7 @@ const priceChangeClass = computed(() => {
|
||||
<div class="grid grid-cols-3 gap-3 text-sm">
|
||||
<div>
|
||||
<div class="text-text-500 text-xs mb-1">
|
||||
24h 高
|
||||
{{ t("tradeTokenized.market.high24h") }}
|
||||
</div>
|
||||
<div class="font-medium">
|
||||
{{ formatPrice(tickerData?.high) }}
|
||||
@@ -87,7 +89,7 @@ const priceChangeClass = computed(() => {
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-text-500 text-xs mb-1">
|
||||
24h 低
|
||||
{{ t("tradeTokenized.market.low24h") }}
|
||||
</div>
|
||||
<div class="font-medium">
|
||||
{{ formatPrice(tickerData?.low) }}
|
||||
@@ -95,7 +97,7 @@ const priceChangeClass = computed(() => {
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-text-500 text-xs mb-1">
|
||||
24h 量
|
||||
{{ t("tradeTokenized.market.volume24h") }}
|
||||
</div>
|
||||
<div class="font-medium">
|
||||
{{ formatVolume(tickerData?.baseVolume) }}
|
||||
@@ -107,7 +109,7 @@ const priceChangeClass = computed(() => {
|
||||
<TradingViewChart v-if="symbol" ref="tradingViewInst" class="my-5" height="300px" :symbol="symbol" :options="tradingviewOptions" />
|
||||
|
||||
<ui-tabs size="small" class="my-3">
|
||||
<ui-tab-pane name="order_book" title="订单表">
|
||||
<ui-tab-pane name="order_book" :title="t('tradeTokenized.market.orderBookTab')">
|
||||
<OrderBook :symbol="symbol" />
|
||||
</ui-tab-pane>
|
||||
</ui-tabs>
|
||||
|
||||
@@ -14,6 +14,7 @@ interface Item {
|
||||
|
||||
const props = defineProps<{ symbol: string }>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const data = ref<Item>({
|
||||
asks: [],
|
||||
bids: [],
|
||||
@@ -108,10 +109,10 @@ onUnmounted(() => {
|
||||
<!-- 标题 -->
|
||||
<div class="grid grid-cols-2 gap-2 border-b border-(--ion-border-color) px-3 py-2 text-xs text-(--ion-color-medium)">
|
||||
<div class="text-left">
|
||||
价格
|
||||
{{ t("tradeTokenized.orderBook.price") }}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
数量
|
||||
{{ t("tradeTokenized.orderBook.quantity") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -146,10 +147,10 @@ onUnmounted(() => {
|
||||
<!-- 标题 -->
|
||||
<div class="grid grid-cols-2 gap-2 border-b border-(--ion-border-color) px-3 py-2 text-xs text-(--ion-color-medium)">
|
||||
<div class="text-left">
|
||||
价格
|
||||
{{ t("tradeTokenized.orderBook.price") }}
|
||||
</div>
|
||||
<div class="text-right">
|
||||
数量
|
||||
{{ t("tradeTokenized.orderBook.quantity") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -56,10 +56,10 @@ function gotoTrade(mode: TradeTypeEnum) {
|
||||
<ui-tab-pane name="overview" :title="t('market.tradeRwa.tabs.overview')" lazy>
|
||||
<Base :data="data" />
|
||||
</ui-tab-pane>
|
||||
<ui-tab-pane name="market" title="行情" lazy>
|
||||
<ui-tab-pane name="market" :title="t('tradeTokenized.index.marketTab')" lazy>
|
||||
<Market :data="data" />
|
||||
</ui-tab-pane>
|
||||
<ui-tab-pane name="about" title="相关文档" lazy>
|
||||
<ui-tab-pane name="about" :title="t('tradeTokenized.index.aboutTab')" lazy>
|
||||
<About :data="data" />
|
||||
</ui-tab-pane>
|
||||
</ui-tabs>
|
||||
@@ -69,10 +69,10 @@ function gotoTrade(mode: TradeTypeEnum) {
|
||||
<ion-toolbar class="ion-padding-horizontal ion-toolbar">
|
||||
<div class="flex justify-center my-2 gap-5">
|
||||
<ion-button shape="round" expand="block" color="success" @click="gotoTrade(TradeTypeEnum.BUY)">
|
||||
买入
|
||||
{{ t("tradeTokenized.index.buy") }}
|
||||
</ion-button>
|
||||
<ion-button shape="round" expand="block" color="danger" @click="gotoTrade(TradeTypeEnum.SELL)">
|
||||
卖出
|
||||
{{ t("tradeTokenized.index.sell") }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-toolbar>
|
||||
|
||||
@@ -112,7 +112,7 @@ onUnmounted(() => {
|
||||
<ion-icon slot="icon-only" :icon="arrowBackOutline" />
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-title>用户设置</ion-title>
|
||||
<ion-title>{{ t('userSettings.index.email') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
|
||||
@@ -4,21 +4,22 @@ import { arrowBackOutline } from "ionicons/icons";
|
||||
import TdesignCopy from "~icons/tdesign/copy";
|
||||
import { authClient } from "@/auth";
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const { userProfile } = storeToRefs(userStore);
|
||||
|
||||
async function handleSignOut() {
|
||||
const alert = await alertController.create({
|
||||
header: "Sign Out",
|
||||
message: "Are you sure you want to sign out?",
|
||||
header: t("userSettings.index.signOut"),
|
||||
message: t("userSettings.index.signOutConfirm"),
|
||||
buttons: [
|
||||
{
|
||||
text: "Cancel",
|
||||
text: t("userSettings.index.cancel"),
|
||||
role: "cancel",
|
||||
},
|
||||
{
|
||||
text: "Sign Out",
|
||||
text: t("userSettings.index.signOut"),
|
||||
role: "destructive",
|
||||
handler: async () => {
|
||||
userStore.signOut();
|
||||
@@ -39,7 +40,7 @@ function handleCopyUid() {
|
||||
writeText(userProfile.value.uid);
|
||||
toastController
|
||||
.create({
|
||||
message: "UID copied to clipboard",
|
||||
message: t("userSettings.index.uidCopied"),
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
})
|
||||
@@ -53,7 +54,7 @@ function handleCopyUid() {
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>用户设置</ion-title>
|
||||
<ion-title>{{ t('userSettings.index.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -97,7 +98,7 @@ function handleCopyUid() {
|
||||
<div class="flex justify-between w-full items-center">
|
||||
<div class="flex-center space-x-2">
|
||||
<div class="text-sm font-semibold">
|
||||
昵称
|
||||
{{ t('userSettings.index.nickname') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="end">
|
||||
@@ -110,7 +111,7 @@ function handleCopyUid() {
|
||||
<div class="flex justify-between w-full items-center">
|
||||
<div class="flex-center space-x-2">
|
||||
<div class="text-sm font-semibold">
|
||||
邮箱
|
||||
{{ t('userSettings.index.email') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="end">
|
||||
@@ -133,7 +134,7 @@ function handleCopyUid() {
|
||||
<!-- Action Buttons -->
|
||||
<div class="mt-10">
|
||||
<ion-button expand="block" @click="handleSignOut">
|
||||
Sign Out
|
||||
{{ t('userSettings.index.signOut') }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { toastController } from "@ionic/vue";
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const { userProfile } = storeToRefs(userStore);
|
||||
const nickname = ref(userProfile.value?.nickname || "");
|
||||
@@ -10,7 +11,7 @@ const { updateProfile } = useUserStore();
|
||||
async function handleSave() {
|
||||
if (!usernamePattern.test(nickname.value)) {
|
||||
const toast = await toastController.create({
|
||||
message: "昵称格式不正确",
|
||||
message: t("userSettings.nickname.invalidFormat"),
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
color: "danger",
|
||||
@@ -24,7 +25,7 @@ async function handleSave() {
|
||||
if (data) {
|
||||
updateProfile();
|
||||
const toast = await toastController.create({
|
||||
message: "昵称更新成功",
|
||||
message: t("userSettings.nickname.updateSuccess"),
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
color: "success",
|
||||
@@ -39,7 +40,7 @@ async function handleSave() {
|
||||
<ion-header>
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>昵称设置</ion-title>
|
||||
<ion-title>{{ t('userSettings.nickname.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -47,16 +48,16 @@ async function handleSave() {
|
||||
<div class="space-y-3">
|
||||
<ui-input
|
||||
v-model="nickname"
|
||||
placeholder="请输入昵称"
|
||||
:placeholder="t('userSettings.nickname.placeholder')"
|
||||
class="w-full"
|
||||
/>
|
||||
|
||||
<div class="text-xs text-gray-500">
|
||||
仅支持字母、数字、下划线,长度 3-20 个字符
|
||||
{{ t('userSettings.nickname.hint') }}
|
||||
</div>
|
||||
|
||||
<ion-button expand="block" class="mt-5" @click="handleSave">
|
||||
保存
|
||||
{{ t('userSettings.nickname.save') }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { toastController } from "@ionic/vue";
|
||||
import { safeClient } from "@/api";
|
||||
import { authClient } from "@/auth";
|
||||
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const { userProfile } = storeToRefs(userStore);
|
||||
const username = ref(userProfile.value?.user?.username || "");
|
||||
@@ -11,7 +12,7 @@ const { updateProfile } = useUserStore();
|
||||
async function handleSave() {
|
||||
if (!usernamePattern.test(username.value)) {
|
||||
const toast = await toastController.create({
|
||||
message: "用户名格式不正确",
|
||||
message: t("userSettings.username.invalidFormat"),
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
color: "danger",
|
||||
@@ -25,7 +26,7 @@ async function handleSave() {
|
||||
if (data) {
|
||||
updateProfile();
|
||||
const toast = await toastController.create({
|
||||
message: "用户名更新成功",
|
||||
message: t("userSettings.username.updateSuccess"),
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
color: "success",
|
||||
@@ -40,7 +41,7 @@ async function handleSave() {
|
||||
<ion-header>
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>用户设置</ion-title>
|
||||
<ion-title>{{ t('userSettings.username.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -48,16 +49,16 @@ async function handleSave() {
|
||||
<div class="space-y-3">
|
||||
<ui-input
|
||||
v-model="username"
|
||||
placeholder="请输入用户名"
|
||||
:placeholder="t('userSettings.username.placeholder')"
|
||||
class="w-full"
|
||||
/>
|
||||
|
||||
<div class="text-xs text-gray-500">
|
||||
仅支持字母、数字、下划线,长度 3-20 个字符
|
||||
{{ t('userSettings.username.hint') }}
|
||||
</div>
|
||||
|
||||
<ion-button expand="block" class="mt-5" @click="handleSave">
|
||||
保存
|
||||
{{ t('userSettings.username.save') }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</ion-content>
|
||||
|
||||
@@ -5,6 +5,7 @@ import IconParkOutlineTransaction from "~icons/icon-park-outline/transaction";
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const props = defineProps<{ code: string; type?: "funding" | "trading" }>();
|
||||
const { t } = useI18n();
|
||||
const [query, resetQuery] = useResetRef<AssetRecordBody>({
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
@@ -74,7 +75,7 @@ onBeforeMount(() => {
|
||||
</ion-refresher>
|
||||
|
||||
<div class="ion-padding-horizontal text-md font-semibold my-4">
|
||||
资产记录
|
||||
{{ t('wallet.assetRecord.title') }}
|
||||
</div>
|
||||
|
||||
<!-- 资产记录列表 -->
|
||||
@@ -108,7 +109,7 @@ onBeforeMount(() => {
|
||||
<div class="flex items-center justify-between text-xs">
|
||||
<div class="flex items-center space-x-6">
|
||||
<div>
|
||||
<span class="text-text-500">余额</span>
|
||||
<span class="text-text-500">{{ t('wallet.assetRecord.balance') }}</span>
|
||||
<span class="ml-2 font-medium">{{ Number(item.availableAfter).toFixed(2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,7 +117,7 @@ onBeforeMount(() => {
|
||||
|
||||
<div class="pt-3">
|
||||
<div class="text-xs text-text-500">
|
||||
<span>备注:</span>
|
||||
<span>{{ t('wallet.assetRecord.memo') }}:</span>
|
||||
<span>{{ item.memo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -131,14 +132,14 @@ onBeforeMount(() => {
|
||||
<div v-else class="flex flex-col items-center justify-center py-20 text-text-500">
|
||||
<IconParkOutlineTransaction class="text-6xl mb-4 opacity-30" />
|
||||
<div class="text-sm">
|
||||
暂无资产记录
|
||||
{{ t('wallet.assetRecord.empty') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-infinite-scroll threshold="100px" @ion-infinite="handleInfinite">
|
||||
<ion-infinite-scroll-content
|
||||
loading-spinner="bubbles"
|
||||
loading-text="加载更多中..."
|
||||
:loading-text="t('wallet.assetRecord.loadingMore')"
|
||||
/>
|
||||
</ion-infinite-scroll>
|
||||
</ion-content>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import Deposit from "./components/deposit.vue";
|
||||
import Withdraw from "./components/withdraw.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
const activeTab = ref("deposit");
|
||||
</script>
|
||||
|
||||
@@ -10,12 +11,12 @@ const activeTab = ref("deposit");
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-back-button slot="start" text="" />
|
||||
<ion-title>账单</ion-title>
|
||||
<ion-title>{{ t('wallet.bill.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-tabs v-model="activeTab" size="small" class="px-2">
|
||||
<ui-tab-pane name="deposit" title="充值记录" />
|
||||
<ui-tab-pane name="withdraw" title="提现记录" />
|
||||
<ui-tab-pane name="deposit" :title="t('wallet.bill.depositTab')" />
|
||||
<ui-tab-pane name="withdraw" :title="t('wallet.bill.withdrawTab')" />
|
||||
</ui-tabs>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -7,6 +7,7 @@ import CryptocurrencyColorNuls from "~icons/cryptocurrency-color/nuls";
|
||||
import { client, safeClient } from "@/api";
|
||||
import { DepositTypeEnum } from "@/api/enum";
|
||||
|
||||
const { t } = useI18n();
|
||||
const [query] = useResetRef<UserDepositOrderBody>({
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
@@ -49,14 +50,14 @@ async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
||||
}
|
||||
async function handleCancel(id: string) {
|
||||
const alert = await alertController.create({
|
||||
header: "确认取消充值?",
|
||||
header: t("wallet.deposit.confirmCancel"),
|
||||
buttons: [
|
||||
{
|
||||
text: "取消",
|
||||
text: t("wallet.deposit.cancel"),
|
||||
role: "cancel",
|
||||
},
|
||||
{
|
||||
text: "确认取消",
|
||||
text: t("wallet.deposit.confirmCancelButton"),
|
||||
role: "destructive",
|
||||
handler: async () => {
|
||||
await safeClient(client.api.deposit.orders({ orderId: id }).cancel.post());
|
||||
@@ -102,7 +103,7 @@ onBeforeMount(() => {
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col size="3" class="flex flex-col">
|
||||
<div class="text-xs text-text-500">
|
||||
金额
|
||||
{{ t('wallet.deposit.amount') }}
|
||||
</div>
|
||||
<div class="text-sm font-bold">
|
||||
{{ Number(item.amount).toFixed(2) }}
|
||||
@@ -110,7 +111,7 @@ onBeforeMount(() => {
|
||||
</ion-col>
|
||||
<ion-col size="3" class="flex flex-col">
|
||||
<div class="text-xs text-text-500">
|
||||
充值方式
|
||||
{{ t('wallet.deposit.depositMethod') }}
|
||||
</div>
|
||||
<div class="text-xs font-bold">
|
||||
{{ item.depositType }}
|
||||
@@ -118,7 +119,7 @@ onBeforeMount(() => {
|
||||
</ion-col>
|
||||
<ion-col size="6" class="flex flex-col">
|
||||
<div class="text-xs text-text-500 text-right">
|
||||
创建时间
|
||||
{{ t('wallet.deposit.createdAt') }}
|
||||
</div>
|
||||
<div class="text-xs font-bold text-right">
|
||||
{{ useDateFormat(item.createdAt, 'MM/DD HH:mm') }}
|
||||
@@ -129,7 +130,7 @@ onBeforeMount(() => {
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col size="6" class="flex flex-col">
|
||||
<div class="text-xs text-text-500">
|
||||
订单号
|
||||
{{ t('wallet.deposit.orderNo') }}
|
||||
</div>
|
||||
<div class="text-xs font-bold">
|
||||
{{ item.orderNo }}
|
||||
@@ -137,7 +138,7 @@ onBeforeMount(() => {
|
||||
</ion-col>
|
||||
<ion-col size="6" class="text-right">
|
||||
<ion-button v-if="item.status === 'pending'" size="small" fill="outline" color="success" @click.stop="handleCancel(item.id)">
|
||||
取消充值
|
||||
{{ t('wallet.deposit.cancelDeposit') }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { alertController } from "@ionic/vue";
|
||||
import CryptocurrencyColorNuls from "~icons/cryptocurrency-color/nuls";
|
||||
import { client, safeClient } from "@/api";
|
||||
|
||||
const { t } = useI18n();
|
||||
const [query] = useResetRef<UserWithdrawOrderBody>({
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
@@ -46,14 +47,14 @@ async function handleInfinite(event: InfiniteScrollCustomEvent) {
|
||||
}
|
||||
async function handleCancel(id: string) {
|
||||
const alert = await alertController.create({
|
||||
header: "确认取消提现?",
|
||||
header: t("wallet.withdrawDetail.confirmCancel"),
|
||||
buttons: [
|
||||
{
|
||||
text: "取消",
|
||||
text: t("wallet.withdrawDetail.cancel"),
|
||||
role: "cancel",
|
||||
},
|
||||
{
|
||||
text: "确认取消",
|
||||
text: t("wallet.withdrawDetail.confirmCancelButton"),
|
||||
role: "destructive",
|
||||
handler: async () => {
|
||||
await safeClient(client.api.withdraw({ orderId: id }).delete());
|
||||
@@ -98,7 +99,7 @@ onBeforeMount(() => {
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col size="3" class="flex flex-col">
|
||||
<div class="text-xs text-text-500">
|
||||
金额
|
||||
{{ t('wallet.withdrawDetail.amount') }}
|
||||
</div>
|
||||
<div class="text-sm font-bold">
|
||||
{{ Number(item.amount).toFixed(2) }}
|
||||
@@ -106,7 +107,7 @@ onBeforeMount(() => {
|
||||
</ion-col>
|
||||
<ion-col size="3" class="flex flex-col">
|
||||
<div class="text-xs text-text-500">
|
||||
实际到账
|
||||
{{ t('wallet.withdrawDetail.actualAmount') }}
|
||||
</div>
|
||||
<div class="text-sm font-bold">
|
||||
{{ Number(item.actualAmount).toFixed(2) }}
|
||||
@@ -114,7 +115,7 @@ onBeforeMount(() => {
|
||||
</ion-col>
|
||||
<ion-col size="6" class="flex flex-col">
|
||||
<div class="text-xs text-text-500 text-right">
|
||||
创建时间
|
||||
{{ t('wallet.withdrawDetail.createdAt') }}
|
||||
</div>
|
||||
<div class="text-xs font-bold text-right">
|
||||
{{ useDateFormat(item.createdAt, 'MM/DD HH:mm') }}
|
||||
@@ -124,7 +125,7 @@ onBeforeMount(() => {
|
||||
<ion-row class="ion-align-items-center">
|
||||
<ion-col size="3" class="flex flex-col">
|
||||
<div class="text-xs text-text-500">
|
||||
手续费
|
||||
{{ t('wallet.withdrawDetail.fee') }}
|
||||
</div>
|
||||
<div class="text-sm font-bold">
|
||||
{{ Number(item.fee).toFixed(2) }}
|
||||
@@ -132,7 +133,7 @@ onBeforeMount(() => {
|
||||
</ion-col>
|
||||
<ion-col size="3" class="flex flex-col">
|
||||
<div class="text-xs text-text-500">
|
||||
提现方式
|
||||
{{ t('wallet.withdrawDetail.withdrawMethod') }}
|
||||
</div>
|
||||
<div class="text-xs font-bold">
|
||||
<span>{{ item.withdrawMethod }}</span>
|
||||
@@ -141,7 +142,7 @@ onBeforeMount(() => {
|
||||
</ion-col>
|
||||
<ion-col size="6" class="text-right">
|
||||
<ion-button v-if="item.status === 'pending'" size="small" fill="outline" color="success" @click.stop="handleCancel(item.id)">
|
||||
取消提现
|
||||
{{ t('wallet.withdrawDetail.cancelWithdraw') }}
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang='ts' setup>
|
||||
import { eyeOffOutline, eyeOutline } from "ionicons/icons";
|
||||
|
||||
const { t } = useI18n();
|
||||
const walletStore = useWalletStore();
|
||||
const { fundingBalances, totalAssetValue } = storeToRefs(walletStore);
|
||||
const fundingBalanceVisible = useStorage("funding-balances-visible", true);
|
||||
@@ -15,14 +16,14 @@ onMounted(() => {
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>资金账户</ion-title>
|
||||
<ion-title>{{ t('wallet.funding.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true">
|
||||
<div class="flex flex-col gap-1 ion-padding border-b border-text-900 mb-2">
|
||||
<div class="text-sm text-gray-500 flex items-center gap-2" @click="fundingBalanceVisible = !fundingBalanceVisible">
|
||||
<div class="text-md">
|
||||
总资产估值
|
||||
{{ t('wallet.funding.totalAssets') }}
|
||||
</div>
|
||||
<ion-icon :icon="fundingBalanceVisible ? eyeOffOutline : eyeOutline" />
|
||||
</div>
|
||||
@@ -37,7 +38,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="ion-padding-horizontal text-md font-semibold my-4">
|
||||
资产
|
||||
{{ t('wallet.funding.assets') }}
|
||||
</div>
|
||||
|
||||
<ion-list lines="none" class="space-y-5 mt-2!">
|
||||
@@ -50,10 +51,10 @@ onMounted(() => {
|
||||
{{ asset.assetCode }}
|
||||
</div>
|
||||
<div class="text-xs text-text-500 font-semibold">
|
||||
总共: ${{ Number(asset.total).toFixed(2) }}
|
||||
{{ t('wallet.funding.total') }}: ${{ Number(asset.total).toFixed(2) }}
|
||||
</div>
|
||||
<div class="text-xs text-text-500 font-semibold">
|
||||
冻结: ${{ Number(asset.frozen).toFixed(2) }}
|
||||
{{ t('wallet.funding.frozen') }}: ${{ Number(asset.frozen).toFixed(2) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang='ts' setup>
|
||||
import { eyeOffOutline, eyeOutline } from "ionicons/icons";
|
||||
|
||||
const { t } = useI18n();
|
||||
const walletStore = useWalletStore();
|
||||
const { tradingBalances, totalAssetValue } = storeToRefs(walletStore);
|
||||
const tradingBalanceVisible = useStorage("trading-balances-visible", true);
|
||||
@@ -15,7 +16,7 @@ onMounted(() => {
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ui-back-button slot="start" />
|
||||
<ion-title>交易账户</ion-title>
|
||||
<ion-title>{{ t('wallet.trading.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
@@ -23,7 +24,7 @@ onMounted(() => {
|
||||
<div class="flex flex-col gap-1 ion-padding border-b border-text-900 mb-2">
|
||||
<div class="text-sm text-gray-500 flex items-center gap-2" @click="tradingBalanceVisible = !tradingBalanceVisible">
|
||||
<div class="text-md">
|
||||
总资产估值
|
||||
{{ t('wallet.trading.totalAssets') }}
|
||||
</div>
|
||||
<ion-icon :icon="tradingBalanceVisible ? eyeOffOutline : eyeOutline" />
|
||||
</div>
|
||||
@@ -38,7 +39,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<div class="ion-padding-horizontal text-md font-semibold my-4">
|
||||
资产
|
||||
{{ t('wallet.trading.assets') }}
|
||||
</div>
|
||||
|
||||
<ion-list lines="none" class="space-y-5 mt-2!">
|
||||
@@ -51,10 +52,10 @@ onMounted(() => {
|
||||
{{ asset.assetCode }}
|
||||
</div>
|
||||
<div class="text-xs text-text-500 font-semibold">
|
||||
总共: ${{ Number(asset.total).toFixed(2) }}
|
||||
{{ t('wallet.trading.total') }}: ${{ Number(asset.total).toFixed(2) }}
|
||||
</div>
|
||||
<div class="text-xs text-text-500 font-semibold">
|
||||
冻结: ${{ Number(asset.frozen).toFixed(2) }}
|
||||
{{ t('wallet.trading.frozen') }}: ${{ Number(asset.frozen).toFixed(2) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ const props = defineProps<{
|
||||
id: string;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
const walletStore = useWalletStore();
|
||||
const { fundingBalances } = storeToRefs(walletStore);
|
||||
@@ -40,11 +41,11 @@ const availableBalance = computed(() => {
|
||||
|
||||
// 验证规则
|
||||
const schema = computed(() => toTypedSchema(z.object({
|
||||
assetCode: z.string().min(1, "请选择币种"),
|
||||
assetCode: z.string().min(1, t("wallet.transferToUser.selectCurrencyRequired")),
|
||||
amount: z
|
||||
.number({ required_error: "请输入转账金额", invalid_type_error: "请输入有效的数字" })
|
||||
.positive("转账金额必须大于0")
|
||||
.refine(value => value <= Number(availableBalance.value), `可用余额不足,当前余额:${availableBalance.value}`),
|
||||
.number({ required_error: t("wallet.transferToUser.enterAmountRequired"), invalid_type_error: t("wallet.transferToUser.enterValidNumber") })
|
||||
.positive(t("wallet.transferToUser.amountMustBePositive"))
|
||||
.refine(value => value <= Number(availableBalance.value), t("wallet.transferToUser.insufficientBalance", { balance: availableBalance.value })),
|
||||
memo: z.string().optional(),
|
||||
})));
|
||||
|
||||
@@ -72,7 +73,7 @@ function setMaxAmount() {
|
||||
// 获取目标用户信息
|
||||
async function fetchTargetUser() {
|
||||
const loading = await loadingController.create({
|
||||
message: "加载用户信息...",
|
||||
message: t("wallet.transferToUser.loadingUserInfo"),
|
||||
});
|
||||
await loading.present();
|
||||
|
||||
@@ -85,7 +86,7 @@ async function fetchTargetUser() {
|
||||
}
|
||||
else {
|
||||
const toast = await toastController.create({
|
||||
message: "用户不存在或已注销, 即将返回上一页",
|
||||
message: t("wallet.transferToUser.userNotFound"),
|
||||
duration: 2000,
|
||||
color: "warning",
|
||||
});
|
||||
@@ -99,7 +100,7 @@ async function fetchTargetUser() {
|
||||
// 提交转账
|
||||
async function onSubmit(values: GenericObject) {
|
||||
const loading = await loadingController.create({
|
||||
message: "转账中...",
|
||||
message: t("wallet.transferToUser.transferring"),
|
||||
});
|
||||
await loading.present();
|
||||
|
||||
@@ -114,7 +115,7 @@ async function onSubmit(values: GenericObject) {
|
||||
|
||||
if (!error.value) {
|
||||
const toast = await toastController.create({
|
||||
message: "转账成功",
|
||||
message: t("wallet.transferToUser.transferSuccess"),
|
||||
duration: 2000,
|
||||
position: "bottom",
|
||||
color: "success",
|
||||
@@ -143,7 +144,7 @@ onMounted(() => {
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button default-href="/layout/user" />
|
||||
</ion-buttons>
|
||||
<ion-title>转账给用户</ion-title>
|
||||
<ion-title>{{ t('wallet.transferToUser.title') }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content :fullscreen="true" class="ion-padding">
|
||||
@@ -174,7 +175,7 @@ onMounted(() => {
|
||||
<Field name="assetCode">
|
||||
<template #default="{ value }">
|
||||
<ion-label class="block text-sm font-medium mb-2">
|
||||
选择币种
|
||||
{{ t('wallet.transferToUser.selectCurrency') }}
|
||||
</ion-label>
|
||||
<div
|
||||
class="flex items-center justify-between bg-text-900 rounded-2xl p-4 cursor-pointer"
|
||||
@@ -198,8 +199,8 @@ onMounted(() => {
|
||||
<div class="relative">
|
||||
<ui-input-label
|
||||
v-bind="field"
|
||||
label="转账金额"
|
||||
placeholder="请输入转账金额"
|
||||
:label="t('wallet.transferToUser.amount')"
|
||||
:placeholder="t('wallet.transferToUser.enterAmount')"
|
||||
type="number"
|
||||
inputmode="decimal"
|
||||
/>
|
||||
@@ -209,7 +210,7 @@ onMounted(() => {
|
||||
class="absolute right-0 top-8 text-sm font-semibold"
|
||||
@click="setMaxAmount"
|
||||
>
|
||||
全部
|
||||
{{ t('wallet.transferToUser.all') }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -219,7 +220,7 @@ onMounted(() => {
|
||||
|
||||
<!-- 可用余额 -->
|
||||
<div class="flex items-center justify-between px-1">
|
||||
<span class="text-sm text-text-400">可用余额</span>
|
||||
<span class="text-sm text-text-400">{{ t('wallet.transferToUser.availableBalance') }}</span>
|
||||
<span class="text-sm font-medium">{{ Number(availableBalance).toFixed(2) }}</span>
|
||||
</div>
|
||||
|
||||
@@ -229,8 +230,8 @@ onMounted(() => {
|
||||
<template #default="{ field }">
|
||||
<ui-input-label
|
||||
v-bind="field"
|
||||
label="备注(可选)"
|
||||
placeholder="请输入备注信息"
|
||||
:label="t('wallet.transferToUser.memo')"
|
||||
:placeholder="t('wallet.transferToUser.enterMemo')"
|
||||
type="text"
|
||||
/>
|
||||
</template>
|
||||
@@ -245,7 +246,7 @@ onMounted(() => {
|
||||
shape="round"
|
||||
class="mt-6 h-12 font-semibold"
|
||||
>
|
||||
确认转账
|
||||
{{ t('wallet.transferToUser.confirmTransfer') }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -168,7 +168,7 @@ function getAccountTypeName(type: AccountType) {
|
||||
<Field name="assetCode">
|
||||
<template #default="{ value }">
|
||||
<ion-label class="block text-sm font-medium mb-2">
|
||||
选择币种
|
||||
{{ t('wallet.transfer.selectCurrency') }}
|
||||
</ion-label>
|
||||
<div
|
||||
class="flex items-center justify-between bg-faint rounded-2xl p-4 cursor-pointer"
|
||||
@@ -239,7 +239,7 @@ function getAccountTypeName(type: AccountType) {
|
||||
class="absolute right-0 top-10.5 text-sm font-semibold z-10"
|
||||
@click="setMaxAmount"
|
||||
>
|
||||
全部
|
||||
{{ t('wallet.transfer.all') }}
|
||||
</ion-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -6,6 +6,8 @@ const emit = defineEmits<{
|
||||
select: [id: BankAccountsData];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const walletStore = useWalletStore();
|
||||
const { bankAccounts } = storeToRefs(walletStore);
|
||||
|
||||
@@ -18,7 +20,7 @@ function handleSelect(item: BankAccountsData) {
|
||||
<template>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ion-title>选择银行账户</ion-title>
|
||||
<ion-title>{{ t("wallet.selectBankAccount.title") }}</ion-title>
|
||||
</ion-toolbar>
|
||||
|
||||
<ion-content>
|
||||
|
||||
@@ -5,6 +5,8 @@ const emit = defineEmits<{
|
||||
select: [code: string];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const walletStore = useWalletStore();
|
||||
const { fundingBalances } = storeToRefs(walletStore);
|
||||
|
||||
@@ -21,7 +23,7 @@ onMounted(() => {
|
||||
<template>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ion-title>选择货币</ion-title>
|
||||
<ion-title>{{ t("wallet.selectCurrency.title") }}</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ async function openSelectBankAccount() {
|
||||
|
||||
async function onSubmit(values: GenericObject) {
|
||||
const loading = await loadingController.create({
|
||||
message: "提交中...",
|
||||
message: t("withdraw.submitting"),
|
||||
});
|
||||
await loading.present();
|
||||
|
||||
@@ -108,7 +108,7 @@ async function onSubmit(values: GenericObject) {
|
||||
<Field name="assetCode">
|
||||
<template #default="{ value }">
|
||||
<ion-label class="block text-sm font-medium mb-2">
|
||||
选择币种
|
||||
{{ t("withdraw.selectCurrency") }}
|
||||
</ion-label>
|
||||
<div
|
||||
class="flex items-center justify-between bg-faint rounded-2xl p-4 cursor-pointer"
|
||||
|
||||
Reference in New Issue
Block a user