feat: 添加银行卡管理功能,更新多语言支持,优化表单验证和用户界面

This commit is contained in:
2025-12-17 01:44:53 +07:00
parent 3cdb71effc
commit d375d12583
7 changed files with 166 additions and 42 deletions

View File

@@ -41,6 +41,68 @@
"validCryptoAddressError": "Please enter a valid crypto address.", "validCryptoAddressError": "Please enter a valid crypto address.",
"submit": "Submit" "submit": "Submit"
}, },
"bankCard": {
"management": "Bank Card Management",
"add": "Add Bank Card",
"empty": {
"title": "No Bank Cards",
"description": "Add a bank card for quick deposits and withdrawals",
"addButton": "Add Bank Card"
},
"list": {
"addCard": "Add Bank Card",
"boundCards": "Bound Bank Cards",
"defaultCard": "Default Card",
"setDefault": "Set as Default",
"edit": "Edit",
"delete": "Delete",
"cancel": "Cancel",
"debitCard": "Debit Card"
},
"form": {
"tips": {
"title": "Tips",
"description": "To ensure the security of your funds, please make sure the bank card information is authentic and valid. The added bank card will be used for deposit and withdrawal operations."
},
"bankName": "Bank Card",
"bankNamePlaceholder": "Please select your bank",
"accountNumber": "Bank Card Number",
"accountNumberPlaceholder": "Please enter your bank card number",
"accountNumberHelper": "Supports 16-19 digit bank card numbers",
"accountName": "Cardholder Name",
"accountNamePlaceholder": "Please enter cardholder name",
"accountNameHelper": "Please enter the real name when opening the bank card",
"security": {
"title": "Security Guarantee",
"encryption": "All bank card information is encrypted via SSL transmission",
"standard": "We strictly follow bank-level security standards",
"privacy": "Your personal information will be properly protected"
},
"submit": "Confirm Add Bank Card",
"validation": {
"bankRequired": "Please select a bank",
"accountNumberRequired": "Please enter bank card number",
"accountNameRequired": "Please enter cardholder name"
}
},
"messages": {
"addSuccess": "Bank card added successfully",
"setDefaultSuccess": "Set as default bank card successfully",
"deleteSuccess": "Bank card deleted successfully",
"deleteConfirm": "Delete Bank Card",
"deleteMessage": "Are you sure you want to delete {bankName} ({accountName})? This operation cannot be undone."
},
"tips": {
"encryption": "Bank card information is encrypted and protected",
"support": "Supports mainstream banks for fast deposits and withdrawals"
}
},
"trade": {
"title": "Trade",
"settings": {
"bankManagement": "Bank Card"
}
},
"asset": { "asset": {
"issue": { "issue": {
"issuingAsset": "Issuing Asset", "issuingAsset": "Issuing Asset",

View File

@@ -6,8 +6,8 @@ export type MessageSchema = typeof enUS;
const i18n = createI18n<MessageSchema, "en-US" | "zh-CN">({ const i18n = createI18n<MessageSchema, "en-US" | "zh-CN">({
legacy: false, legacy: false,
locale: "zh-CN", locale: "en-US",
fallbackLocale: "zh-CN", fallbackLocale: "en-US",
messages: { messages: {
"en-US": enUS, "en-US": enUS,
"zh-CN": zhCN, "zh-CN": zhCN,

View File

@@ -41,6 +41,68 @@
"validCryptoAddressError": "请输入有效的加密货币地址。", "validCryptoAddressError": "请输入有效的加密货币地址。",
"submit": "提交" "submit": "提交"
}, },
"bankCard": {
"management": "银行卡管理",
"add": "添加银行卡",
"empty": {
"title": "暂无银行卡",
"description": "添加银行卡以便快速充值和提现",
"addButton": "添加银行卡"
},
"list": {
"addCard": "添加银行卡",
"boundCards": "已绑定银行卡",
"defaultCard": "默认银行卡",
"setDefault": "设为默认",
"edit": "编辑",
"delete": "删除",
"cancel": "取消",
"debitCard": "储蓄卡"
},
"form": {
"tips": {
"title": "温馨提示",
"description": "为了保障您的资金安全,请确保银行卡信息真实有效。添加的银行卡将用于充值和提现操作。"
},
"bankName": "银行卡",
"bankNamePlaceholder": "请选择您的银行",
"accountNumber": "银行卡号",
"accountNumberPlaceholder": "请输入银行卡号",
"accountNumberHelper": "支持16-19位银行卡号",
"accountName": "持卡人姓名",
"accountNamePlaceholder": "请输入持卡人姓名",
"accountNameHelper": "请输入银行卡开户时的真实姓名",
"security": {
"title": "安全保障",
"encryption": "所有银行卡信息均经过SSL加密传输",
"standard": "我们严格遵循银行级安全标准",
"privacy": "您的个人信息将被妃善保护"
},
"submit": "确认添加银行卡",
"validation": {
"bankRequired": "请选择银行",
"accountNumberRequired": "请输入银行卡号",
"accountNameRequired": "请输入持卡人姓名"
}
},
"messages": {
"addSuccess": "银行卡添加成功",
"setDefaultSuccess": "已设置为默认银行卡",
"deleteSuccess": "银行卡删除成功",
"deleteConfirm": "删除银行卡",
"deleteMessage": "确定要删除 {bankName} ({accountName}) 吗?此操作无法撤销。"
},
"tips": {
"encryption": "银行卡信息经过加密保护",
"support": "支持主流银行快速充值提现"
}
},
"trade": {
"title": "交易",
"settings": {
"bankManagement": "银行卡管理"
}
},
"asset": { "asset": {
"issue": { "issue": {
"issuingAsset": "发行资产", "issuingAsset": "发行资产",

View File

@@ -27,11 +27,11 @@ const bankList = [
// 表单验证 Schema // 表单验证 Schema
const schema = toTypedSchema( const schema = toTypedSchema(
yup.object({ yup.object({
bankName: yup.string().required("请选择银行"), bankName: yup.string().required(t("bankCard.form.validation.bankRequired")),
accountNumber: yup accountNumber: yup
.string() .string()
.required("请输入银行卡号"), .required(t("bankCard.form.validation.accountNumberRequired")),
accountName: yup.string().required("请输入持卡人姓名"), accountName: yup.string().required(t("bankCard.form.validation.accountNameRequired")),
}), }),
); );
@@ -43,7 +43,7 @@ async function handleSubmit(values: GenericObject) {
accountName: values.accountName, accountName: values.accountName,
})); }));
const toast = await toastController.create({ const toast = await toastController.create({
message: "银行卡添加成功", message: t("bankCard.messages.addSuccess"),
duration: 2000, duration: 2000,
position: "bottom", position: "bottom",
color: "success", color: "success",
@@ -71,7 +71,7 @@ function formatCardNumber(value: string) {
<ion-header> <ion-header>
<ion-toolbar class="ui-toolbar"> <ion-toolbar class="ui-toolbar">
<ion-back-button slot="start" /> <ion-back-button slot="start" />
<ion-title>添加银行卡</ion-title> <ion-title>{{ t('bankCard.add') }}</ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
@@ -87,10 +87,10 @@ function formatCardNumber(value: string) {
/> />
<div class="text-sm text-blue-700 dark:text-blue-300"> <div class="text-sm text-blue-700 dark:text-blue-300">
<p class="font-medium mb-1"> <p class="font-medium mb-1">
温馨提示 {{ t('bankCard.form.tips.title') }}
</p> </p>
<p class="leading-relaxed"> <p class="leading-relaxed">
为了保障您的资金安全请确保银行卡信息真实有效添加的银行卡将用于充值和提现操作 {{ t('bankCard.form.tips.description') }}
</p> </p>
</div> </div>
</div> </div>
@@ -99,7 +99,7 @@ function formatCardNumber(value: string) {
<Form :validation-schema="schema" class="space-y-5" @submit="handleSubmit"> <Form :validation-schema="schema" class="space-y-5" @submit="handleSubmit">
<div class="space-y-4"> <div class="space-y-4">
<Field v-slot="{ field }" name="bankName"> <Field v-slot="{ field }" name="bankName">
<ion-select class="ui-select" interface="action-sheet" toggle-icon="" v-bind="field" label="银行卡" placeholder="请选择您的银行"> <ion-select class="ui-select" interface="action-sheet" toggle-icon="" v-bind="field" :label="t('bankCard.form.bankName')" :placeholder="t('bankCard.form.bankNamePlaceholder')">
<ion-select-option v-for="item in bankList" :key="item.code" :value="item.code"> <ion-select-option v-for="item in bankList" :key="item.code" :value="item.code">
{{ item.name }} {{ item.name }}
</ion-select-option> </ion-select-option>
@@ -111,12 +111,12 @@ function formatCardNumber(value: string) {
<template #default="{ field }"> <template #default="{ field }">
<ui-input-label <ui-input-label
v-bind="field" v-bind="field"
label="银行卡号" :label="t('bankCard.form.accountNumber')"
placeholder="请输入银行卡号" :placeholder="t('bankCard.form.accountNumberPlaceholder')"
type="text" type="text"
inputmode="numeric" inputmode="numeric"
:maxlength="23" :maxlength="23"
helper-text="支持16-19位银行卡号" :helper-text="t('bankCard.form.accountNumberHelper')"
required required
@input="(e:any) => field.value = formatCardNumber(e.target.value)" @input="(e:any) => field.value = formatCardNumber(e.target.value)"
/> />
@@ -130,10 +130,10 @@ function formatCardNumber(value: string) {
<template #default="{ field }"> <template #default="{ field }">
<ui-input-label <ui-input-label
v-bind="field" v-bind="field"
label="持卡人姓名" :label="t('bankCard.form.accountName')"
placeholder="请输入持卡人姓名" :placeholder="t('bankCard.form.accountNamePlaceholder')"
type="text" type="text"
helper-text="请输入银行卡开户时的真实姓名" :helper-text="t('bankCard.form.accountNameHelper')"
required required
/> />
</template> </template>
@@ -149,19 +149,19 @@ function formatCardNumber(value: string) {
/> />
<div class="text-sm text-amber-700 dark:text-amber-300"> <div class="text-sm text-amber-700 dark:text-amber-300">
<p class="font-medium mb-1"> <p class="font-medium mb-1">
安全保障 {{ t('bankCard.form.security.title') }}
</p> </p>
<ul class="space-y-1 text-xs leading-relaxed"> <ul class="space-y-1 text-xs leading-relaxed">
<li> 所有银行卡信息均经过SSL加密传输</li> <li> {{ t('bankCard.form.security.encryption') }}</li>
<li> 我们严格遵循银行级安全标准</li> <li> {{ t('bankCard.form.security.standard') }}</li>
<li> 您的个人信息将被妥善保护</li> <li> {{ t('bankCard.form.security.privacy') }}</li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<ion-button type="submit" expand="block"> <ion-button type="submit" expand="block">
确认添加银行卡 {{ t('bankCard.form.submit') }}
</ion-button> </ion-button>
</div> </div>
</Form> </Form>

View File

@@ -24,7 +24,7 @@ async function handleCardOptions(card: any) {
header: card.bankName, header: card.bankName,
buttons: [ buttons: [
{ {
text: card.isDefault ? "默认银行卡" : "设为默认", text: card.isDefault ? t("bankCard.list.defaultCard") : t("bankCard.list.setDefault"),
handler: () => { handler: () => {
if (!card.isDefault) { if (!card.isDefault) {
handleSetDefault(card); handleSetDefault(card);
@@ -32,20 +32,20 @@ async function handleCardOptions(card: any) {
}, },
}, },
{ {
text: "编辑", text: t("bankCard.list.edit"),
handler: () => { handler: () => {
router.push(`/trade-settings/bank-management/edit/${card.id}`); router.push(`/trade-settings/bank-management/edit/${card.id}`);
}, },
}, },
{ {
text: "删除", text: t("bankCard.list.delete"),
role: "destructive", role: "destructive",
handler: () => { handler: () => {
handleDeleteCard(card); handleDeleteCard(card);
}, },
}, },
{ {
text: "取消", text: t("bankCard.list.cancel"),
role: "cancel", role: "cancel",
}, },
], ],
@@ -58,7 +58,7 @@ async function handleCardOptions(card: any) {
async function handleSetDefault(card: any) { async function handleSetDefault(card: any) {
await safeClient(() => client.api.bank_account({ id: card.id }).set_default.post()); await safeClient(() => client.api.bank_account({ id: card.id }).set_default.post());
const toast = await toastController.create({ const toast = await toastController.create({
message: "已设置为默认银行卡", message: t("bankCard.messages.setDefaultSuccess"),
duration: 2000, duration: 2000,
position: "bottom", position: "bottom",
color: "success", color: "success",
@@ -84,7 +84,7 @@ async function handleDeleteCard(card: any) {
handler: async () => { handler: async () => {
await safeClient(() => client.api.bank_account({ id: card.id }).delete()); await safeClient(() => client.api.bank_account({ id: card.id }).delete());
const toast = await toastController.create({ const toast = await toastController.create({
message: "银行卡删除成功", message: t("bankCard.messages.deleteSuccess"),
duration: 2000, duration: 2000,
position: "bottom", position: "bottom",
color: "success", color: "success",
@@ -137,7 +137,7 @@ onUpdated(() => {
<ion-header> <ion-header>
<ion-toolbar class="ui-toolbar"> <ion-toolbar class="ui-toolbar">
<ion-back-button slot="start" /> <ion-back-button slot="start" />
<ion-title>银行卡管理</ion-title> <ion-title>{{ t('bankCard.management') }}</ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
@@ -148,10 +148,10 @@ onUpdated(() => {
<ion-icon :icon="cardOutline" class="text-4xl text-white" /> <ion-icon :icon="cardOutline" class="text-4xl text-white" />
</div> </div>
<h3 class="text-xl font-semibold text-#151515 dark:text-white mb-2"> <h3 class="text-xl font-semibold text-#151515 dark:text-white mb-2">
暂无银行卡 {{ t('bankCard.empty.title') }}
</h3> </h3>
<p class="text-gray-600 dark:text-gray-400 text-sm leading-relaxed mb-8"> <p class="text-gray-600 dark:text-gray-400 text-sm leading-relaxed mb-8">
添加银行卡以便快速充值和提现 {{ t('bankCard.empty.description') }}
</p> </p>
<ion-button <ion-button
expand="block" expand="block"
@@ -159,7 +159,7 @@ onUpdated(() => {
@click="handleAddCard" @click="handleAddCard"
> >
<ion-icon slot="start" :icon="addOutline" /> <ion-icon slot="start" :icon="addOutline" />
添加银行卡 {{ t('bankCard.empty.addButton') }}
</ion-button> </ion-button>
</div> </div>
@@ -172,14 +172,14 @@ onUpdated(() => {
<div class="w-10 h-10 rounded-full bg-linear-to-r from-indigo-500 to-purple-600 flex items-center justify-center mr-3"> <div class="w-10 h-10 rounded-full bg-linear-to-r from-indigo-500 to-purple-600 flex items-center justify-center mr-3">
<ion-icon :icon="addOutline" class="text-white text-xl" /> <ion-icon :icon="addOutline" class="text-white text-xl" />
</div> </div>
<span class="font-semibold text-#151515 dark:text-white">添加银行卡</span> <span class="font-semibold text-#151515 dark:text-white">{{ t('bankCard.list.addCard') }}</span>
</div> </div>
</div> </div>
<!-- 银行卡列表 --> <!-- 银行卡列表 -->
<div class="mb-8"> <div class="mb-8">
<div class="mb-4"> <div class="mb-4">
<span class="text-sm text-gray-600 dark:text-gray-400 font-medium">已绑定银行卡 ({{ bankCards?.length }})</span> <span class="text-sm text-gray-600 dark:text-gray-400 font-medium">{{ t('bankCard.list.boundCards') }} ({{ bankCards?.length }})</span>
</div> </div>
<div <div
@@ -204,7 +204,7 @@ onUpdated(() => {
v-if="card.isDefault" v-if="card.isDefault"
class="bg-linear-to-r from-indigo-500 to-purple-600 text-white text-xs px-2 py-0.5 rounded-lg font-medium" class="bg-linear-to-r from-indigo-500 to-purple-600 text-white text-xs px-2 py-0.5 rounded-lg font-medium"
> >
默认 {{ t('bankCard.list.defaultCard') }}
</span> </span>
</div> </div>
<p class="text-gray-600 dark:text-gray-400 text-base m-0 mb-2 tracking-wider"> <p class="text-gray-600 dark:text-gray-400 text-base m-0 mb-2 tracking-wider">
@@ -212,7 +212,7 @@ onUpdated(() => {
</p> </p>
<div class="flex gap-4 text-sm text-gray-500 dark:text-gray-400"> <div class="flex gap-4 text-sm text-gray-500 dark:text-gray-400">
<span class="px-2 py-0.5 bg-gray-100 dark:bg-gray-700 rounded-md text-xs"> <span class="px-2 py-0.5 bg-gray-100 dark:bg-gray-700 rounded-md text-xs">
储蓄卡 {{ t('bankCard.list.debitCard') }}
</span> </span>
<span>{{ card.accountName }}</span> <span>{{ card.accountName }}</span>
</div> </div>
@@ -233,7 +233,7 @@ onUpdated(() => {
<!-- 默认标识条 --> <!-- 默认标识条 -->
<div v-if="card.isDefault" class="bg-linear-to-r from-indigo-500 to-purple-600 text-white px-5 py-2 flex items-center gap-2 text-sm font-medium"> <div v-if="card.isDefault" class="bg-linear-to-r from-indigo-500 to-purple-600 text-white px-5 py-2 flex items-center gap-2 text-sm font-medium">
<ion-icon :icon="star" class="text-base" /> <ion-icon :icon="star" class="text-base" />
<span>默认银行卡</span> <span>{{ t('bankCard.list.defaultCard') }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -242,11 +242,11 @@ onUpdated(() => {
<div class="rounded-xl p-4 mt-4 b-2px bg-gray-50 dark:bg-gray-900"> <div class="rounded-xl p-4 mt-4 b-2px bg-gray-50 dark:bg-gray-900">
<div class="flex items-center gap-3 mb-3 text-sm text-gray-600 dark:text-gray-400"> <div class="flex items-center gap-3 mb-3 text-sm text-gray-600 dark:text-gray-400">
<ion-icon :icon="checkmarkCircle" class="text-base text-green-500" /> <ion-icon :icon="checkmarkCircle" class="text-base text-green-500" />
<span>银行卡信息经过加密保护</span> <span>{{ t('bankCard.tips.encryption') }}</span>
</div> </div>
<div class="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400"> <div class="flex items-center gap-3 text-sm text-gray-600 dark:text-gray-400">
<ion-icon :icon="checkmarkCircle" class="text-base text-green-500" /> <ion-icon :icon="checkmarkCircle" class="text-base text-green-500" />
<span>支持主流银行快速充值提现</span> <span>{{ t('bankCard.tips.support') }}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -5,7 +5,7 @@ const { t } = useI18n();
</script> </script>
<template> <template>
<div class="mt-10"> <div class="mt-7">
<ion-label class="text-xs font-medium text-text-300"> <ion-label class="text-xs font-medium text-text-300">
{{ t("asset.revenue.myRevenue") }} {{ t("asset.revenue.myRevenue") }}
</ion-label> </ion-label>

View File

@@ -5,15 +5,15 @@ const { t } = useI18n();
</script> </script>
<template> <template>
<div class="mt-10"> <div class="mt-7">
<ion-label class="text-xs font-medium text-text-300"> <ion-label class="text-xs font-medium text-text-300">
交易 {{ t('trade.title') }}
</ion-label> </ion-label>
<div class="grid grid-cols-4 mt-5"> <div class="grid grid-cols-4 mt-5">
<div class="col-span-1 flex-col-center gap-2" @click="$router.push('/trade-settings/bank-management')"> <div class="col-span-1 flex-col-center gap-2" @click="$router.push('/trade-settings/bank-management')">
<ion-icon :icon="cardOutline" /> <ion-icon :icon="cardOutline" />
<div class="text-xs"> <div class="text-xs">
银行卡管理 {{ t('trade.settings.bankManagement') }}
</div> </div>
</div> </div>
</div> </div>