feat: 添加提现功能,新增提现页面及相关逻辑,优化交易密码验证流程
This commit is contained in:
@@ -124,6 +124,11 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: () => import("@/views/asset_center/index.vue"),
|
component: () => import("@/views/asset_center/index.vue"),
|
||||||
meta: { requiresAuth: true },
|
meta: { requiresAuth: true },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/withdraw",
|
||||||
|
component: () => import("@/views/withdraw/index.vue"),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
<script lang='ts' setup>
|
<script lang='ts' setup>
|
||||||
import { toastController } from "@ionic/vue";
|
import { alertController, toastController } from "@ionic/vue";
|
||||||
import { checkmarkCircleOutline, chevronForwardOutline, closeCircleOutline, keyOutline, lockClosedOutline, shieldCheckmarkOutline } from "ionicons/icons";
|
import { checkmarkCircleOutline, chevronForwardOutline, closeCircleOutline, keyOutline, lockClosedOutline, shieldCheckmarkOutline } from "ionicons/icons";
|
||||||
import { client, safeClient } from "@/api";
|
import { client, safeClient } from "@/api";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data: status } = await safeClient(() => client.api.user.security["transaction-password"].status.get());
|
const { data: status, refresh } = await safeClient(() => client.api.user.security["transaction-password"].status.get());
|
||||||
|
|
||||||
// 交易密码是否已启用
|
const hasPaymentPassword = computed(() => status.value?.enabled ?? false);
|
||||||
const hasPaymentPassword = ref(true);
|
|
||||||
|
|
||||||
async function showToast(message: string, color: "success" | "danger" | "warning" = "success") {
|
async function showToast(message: string, color: "success" | "danger" | "warning" = "success") {
|
||||||
const toast = await toastController.create({
|
const toast = await toastController.create({
|
||||||
@@ -28,15 +27,121 @@ function handleChangePaymentPassword() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleTogglePaymentPassword() {
|
async function handleTogglePaymentPassword() {
|
||||||
if (hasPaymentPassword.value) {
|
if (status.value?.enabled) {
|
||||||
// TODO: 禁用交易密码需要验证当前交易密码
|
// 禁用交易密码需要验证当前交易密码
|
||||||
|
const alert = await alertController.create({
|
||||||
|
header: "禁用交易密码",
|
||||||
|
message: "请输入当前交易密码以确认操作",
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: "oldPassword",
|
||||||
|
type: "password",
|
||||||
|
placeholder: "请输入当前交易密码",
|
||||||
|
attributes: {
|
||||||
|
maxlength: 20,
|
||||||
|
inputmode: "numeric",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "取消",
|
||||||
|
role: "cancel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "确认",
|
||||||
|
handler: async (data) => {
|
||||||
|
if (!data.oldPassword) {
|
||||||
|
await showToast("请输入交易密码", "danger");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data.oldPassword.length < 6) {
|
||||||
|
await showToast("交易密码至少6位", "danger");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error } = await safeClient(client.api.user.security["transaction-password"].delete({
|
||||||
|
oldPassword: data.oldPassword,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!error.value) {
|
||||||
await showToast("交易密码功能已关闭", "warning");
|
await showToast("交易密码功能已关闭", "warning");
|
||||||
|
await refresh();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await alert.present();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO: 启用交易密码需要设置新密码
|
// 启用交易密码需要设置新密码
|
||||||
await showToast("交易密码功能已开启");
|
const alert = await alertController.create({
|
||||||
|
header: "设置交易密码",
|
||||||
|
message: "交易密码用于提现、转账等资金操作,请设置6-20位数字密码",
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: "password",
|
||||||
|
type: "password",
|
||||||
|
placeholder: "请输入新交易密码",
|
||||||
|
attributes: {
|
||||||
|
maxlength: 20,
|
||||||
|
inputmode: "numeric",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "confirmPassword",
|
||||||
|
type: "password",
|
||||||
|
placeholder: "请再次输入交易密码",
|
||||||
|
attributes: {
|
||||||
|
maxlength: 20,
|
||||||
|
inputmode: "numeric",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "取消",
|
||||||
|
role: "cancel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "确认",
|
||||||
|
handler: async (data) => {
|
||||||
|
if (!data.password || !data.confirmPassword) {
|
||||||
|
await showToast("请输入交易密码", "danger");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data.password.length < 6) {
|
||||||
|
await showToast("交易密码至少6位", "danger");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (data.password !== data.confirmPassword) {
|
||||||
|
await showToast("两次输入的密码不一致", "danger");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!/^\d+$/.test(data.password)) {
|
||||||
|
await showToast("交易密码只能包含数字", "danger");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { error } = await safeClient(client.api.user.security["transaction-password"].post({
|
||||||
|
password: data.password,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!error.value) {
|
||||||
|
await showToast("交易密码设置成功");
|
||||||
|
await refresh();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await alert.present();
|
||||||
}
|
}
|
||||||
hasPaymentPassword.value = !hasPaymentPassword.value;
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
687
src/views/withdraw/index.vue
Normal file
687
src/views/withdraw/index.vue
Normal file
@@ -0,0 +1,687 @@
|
|||||||
|
<script lang='ts' setup>
|
||||||
|
import type { Treaty } from "@elysiajs/eden";
|
||||||
|
import { alertController, toastController } from "@ionic/vue";
|
||||||
|
import { cardOutline, checkmarkCircleOutline, documentTextOutline, keyOutline, logoAlipay, walletOutline } from "ionicons/icons";
|
||||||
|
import { client, safeClient } from "@/api";
|
||||||
|
|
||||||
|
type Wallet = Treaty.Data<typeof client.api.wallet.wallets.get>[number];
|
||||||
|
type Receipt = Treaty.Data<typeof client.api.receipt_method.get>["data"][number];
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const walletStore = useWalletStore();
|
||||||
|
const { wallets } = storeToRefs(walletStore);
|
||||||
|
const filterWallets = computed(() => wallets.value.filter(w => w.walletType.allowWithdraw === true));
|
||||||
|
const withdrawAmount = ref("");
|
||||||
|
const quickAmounts = [100, 500, 1000, 2000];
|
||||||
|
const selectedWallet = ref<Wallet | null>(null);
|
||||||
|
const transactionPassword = ref("");
|
||||||
|
const selectedBankAccount = ref<Receipt | null>(null);
|
||||||
|
const { data: bankAccounts } = await safeClient(client.api.receipt_method.get());
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await walletStore.syncWallets();
|
||||||
|
if (wallets.value.length > 0) {
|
||||||
|
selectedWallet.value = wallets.value[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 可提现余额
|
||||||
|
const availableBalance = computed(() => {
|
||||||
|
return Number(selectedWallet.value?.available) || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
function selectQuickAmount(amount: number) {
|
||||||
|
if (amount <= availableBalance.value) {
|
||||||
|
withdrawAmount.value = amount.toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showToast("提现金额不能大于可用余额", "warning");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAllAmount() {
|
||||||
|
withdrawAmount.value = availableBalance.value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectWallet(wallet: Wallet) {
|
||||||
|
selectedWallet.value = wallet;
|
||||||
|
// 重新验证提现金额
|
||||||
|
const amount = Number.parseFloat(withdrawAmount.value);
|
||||||
|
if (amount > availableBalance.value) {
|
||||||
|
withdrawAmount.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectBankAccount(account: Receipt) {
|
||||||
|
selectedBankAccount.value = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function showToast(message: string, color: "success" | "danger" | "warning" = "success") {
|
||||||
|
const toast = await toastController.create({
|
||||||
|
message,
|
||||||
|
duration: 2000,
|
||||||
|
position: "top",
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
await toast.present();
|
||||||
|
}
|
||||||
|
function getPaymentIcon(type: Receipt["type"]) {
|
||||||
|
return type === "bank_card" ? cardOutline : logoAlipay;
|
||||||
|
}
|
||||||
|
async function handleSubmit() {
|
||||||
|
const amount = Number.parseFloat(withdrawAmount.value);
|
||||||
|
|
||||||
|
// 验证提现金额
|
||||||
|
if (!withdrawAmount.value || Number.isNaN(amount) || amount <= 0) {
|
||||||
|
await showToast("请输入有效的提现金额", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount < 10) {
|
||||||
|
await showToast("提现金额不能低于10元", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount > availableBalance.value) {
|
||||||
|
await showToast("提现金额不能大于可用余额", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证钱包选择
|
||||||
|
if (!selectedWallet.value) {
|
||||||
|
await showToast("请选择提现钱包", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证收款方式
|
||||||
|
if (!selectedBankAccount.value) {
|
||||||
|
await showToast("请选择收款账户", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证交易密码
|
||||||
|
if (!transactionPassword.value) {
|
||||||
|
await showToast("请输入交易密码", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transactionPassword.value.length < 6) {
|
||||||
|
await showToast("交易密码至少6位", "warning");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const alert = await alertController.create({
|
||||||
|
header: "确认提现",
|
||||||
|
message: `
|
||||||
|
<div style="text-align: left; padding: 12px 0;">
|
||||||
|
<p style="margin: 8px 0;"><strong>提现金额:</strong>¥${amount.toFixed(2)}</p>
|
||||||
|
<p style="margin: 8px 0;"><strong>提现钱包:</strong>${selectedWallet.value.walletType.name}</p>
|
||||||
|
<p style="margin: 8px 0;"><strong>收款银行:</strong>${selectedBankAccount.value.bankName}</p>
|
||||||
|
<p style="margin: 8px 0;"><strong>收款账号:</strong>${selectedBankAccount.value.type === "bank_card" ? selectedBankAccount.value.bankCardNumber : selectedBankAccount.value.alipayAccount}</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "取消",
|
||||||
|
role: "cancel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "确认提现",
|
||||||
|
handler: async () => {
|
||||||
|
// TODO: 调用提现 API
|
||||||
|
// const { error } = await safeClient(client.api.withdraw.post({
|
||||||
|
// walletId: selectedWallet.value.id,
|
||||||
|
// amount,
|
||||||
|
// bankAccountId: selectedBankAccount.value.id,
|
||||||
|
// transactionPassword: transactionPassword.value,
|
||||||
|
// }));
|
||||||
|
|
||||||
|
await showToast("提现申请已提交,请等待审核");
|
||||||
|
withdrawAmount.value = "";
|
||||||
|
transactionPassword.value = "";
|
||||||
|
selectedBankAccount.value = null;
|
||||||
|
await walletStore.syncWallets();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
function getPaymentTypeName(type: Receipt["type"]) {
|
||||||
|
return type === "bank_card" ? "银行卡" : "支付宝";
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToRecords() {
|
||||||
|
// router.push("/withdraw/records");
|
||||||
|
showToast("功能开发中", "warning");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ion-page>
|
||||||
|
<ion-header class="ion-no-border">
|
||||||
|
<ion-toolbar class="ion-toolbar">
|
||||||
|
<ion-buttons slot="start">
|
||||||
|
<back-button />
|
||||||
|
</ion-buttons>
|
||||||
|
<ion-title>提现</ion-title>
|
||||||
|
<ion-buttons slot="end">
|
||||||
|
<ion-button color="light" size="small" @click="goToRecords">
|
||||||
|
<ion-icon slot="icon-only" :icon="documentTextOutline" />
|
||||||
|
</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<!-- 选择钱包 -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="section-title">
|
||||||
|
<ion-icon :icon="walletOutline" class="title-icon" />
|
||||||
|
选择提现钱包
|
||||||
|
</div>
|
||||||
|
<div v-if="filterWallets.length > 0" class="wallet-list">
|
||||||
|
<div
|
||||||
|
v-for="wallet in filterWallets"
|
||||||
|
:key="wallet.id"
|
||||||
|
class="wallet-item"
|
||||||
|
:class="{ active: selectedWallet?.id === wallet.id }"
|
||||||
|
@click="selectWallet(wallet)"
|
||||||
|
>
|
||||||
|
<div class="wallet-info">
|
||||||
|
<div class="wallet-name">
|
||||||
|
{{ wallet.walletType.name }}
|
||||||
|
</div>
|
||||||
|
<div class="wallet-balance">
|
||||||
|
可用余额:¥{{ Number(wallet.available).toFixed(2) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedWallet?.id === wallet.id" class="wallet-check">
|
||||||
|
<ion-icon :icon="checkmarkCircleOutline" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="no-wallets">
|
||||||
|
<empty title="暂无钱包" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 提现金额输入 -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="section-title">
|
||||||
|
提现金额
|
||||||
|
</div>
|
||||||
|
<div class="amount-input-wrapper">
|
||||||
|
<span class="currency-symbol">¥</span>
|
||||||
|
<input
|
||||||
|
v-model="withdrawAmount"
|
||||||
|
type="number"
|
||||||
|
inputmode="decimal"
|
||||||
|
placeholder="请输入提现金额"
|
||||||
|
class="amount-input"
|
||||||
|
:max="availableBalance"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 快速选择金额 -->
|
||||||
|
<div class="quick-amounts">
|
||||||
|
<div
|
||||||
|
v-for="amount in quickAmounts"
|
||||||
|
:key="amount"
|
||||||
|
class="quick-amount-btn"
|
||||||
|
:class="{
|
||||||
|
active: withdrawAmount === amount.toString(),
|
||||||
|
disabled: amount > availableBalance,
|
||||||
|
}"
|
||||||
|
@click="selectQuickAmount(amount)"
|
||||||
|
>
|
||||||
|
¥{{ amount }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="quick-amount-btn"
|
||||||
|
:class="{ active: withdrawAmount === availableBalance.toString() }"
|
||||||
|
@click="selectAllAmount"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="amount-hint">
|
||||||
|
<ion-icon :icon="checkmarkCircleOutline" class="hint-icon" />
|
||||||
|
可提现余额:¥{{ availableBalance.toFixed(2) }}(最低提现金额 ¥10)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 收款方式选择 -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="section-title">
|
||||||
|
<ion-icon :icon="cardOutline" class="title-icon" />
|
||||||
|
选择收款账户
|
||||||
|
</div>
|
||||||
|
<div v-if="bankAccounts?.data?.length === 0" class="empty-state">
|
||||||
|
<empty title="暂无收款账户">
|
||||||
|
<template #extra>
|
||||||
|
<ion-button size="small" @click="router.push('/bank_accounts/add')">
|
||||||
|
添加收款账户
|
||||||
|
</ion-button>
|
||||||
|
</template>
|
||||||
|
</empty>
|
||||||
|
</div>
|
||||||
|
<div v-else class="bank-accounts">
|
||||||
|
<div
|
||||||
|
v-for="payment in bankAccounts?.data"
|
||||||
|
:key="payment.id"
|
||||||
|
class="payment-card"
|
||||||
|
:class="{ active: selectedBankAccount?.id === payment.id }"
|
||||||
|
@click="selectBankAccount(payment)"
|
||||||
|
>
|
||||||
|
<!-- 支付方式图标 -->
|
||||||
|
<div class="payment-icon">
|
||||||
|
<ion-icon
|
||||||
|
:icon="getPaymentIcon(payment.type)"
|
||||||
|
:class="payment.type === 'alipay' ? 'text-[#1677ff]' : 'text-primary'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 收款方式信息 -->
|
||||||
|
<div class="payment-info">
|
||||||
|
<div class="payment-type-name">
|
||||||
|
{{ getPaymentTypeName(payment.type) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-details">
|
||||||
|
<template v-if="payment.type === 'bank_card'">
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">银行:</span>
|
||||||
|
<span class="detail-value">{{ payment.bankName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">账号:</span>
|
||||||
|
<span class="detail-value">{{ payment.bankCardNumber }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">姓名:</span>
|
||||||
|
<span class="detail-value">{{ payment.alipayName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<span class="detail-label">账号:</span>
|
||||||
|
<span class="detail-value">{{ payment.alipayAccount }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选中图标 -->
|
||||||
|
<div v-if="selectedBankAccount?.id === payment.id" class="payment-check">
|
||||||
|
<ion-icon :icon="checkmarkCircleOutline" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 交易密码 -->
|
||||||
|
<div class="section-card">
|
||||||
|
<div class="section-title">
|
||||||
|
<ion-icon :icon="keyOutline" class="title-icon" />
|
||||||
|
交易密码
|
||||||
|
</div>
|
||||||
|
<div class="password-input-wrapper">
|
||||||
|
<ion-input
|
||||||
|
v-model="transactionPassword"
|
||||||
|
type="password"
|
||||||
|
placeholder="请输入交易密码"
|
||||||
|
class="password-input"
|
||||||
|
inputmode="numeric"
|
||||||
|
:maxlength="20"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="password-hint">
|
||||||
|
交易密码用于验证提现操作,请妥善保管
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 提交按钮 -->
|
||||||
|
<div class="submit-wrapper">
|
||||||
|
<ion-button
|
||||||
|
expand="block"
|
||||||
|
class="submit-btn"
|
||||||
|
@click="handleSubmit"
|
||||||
|
>
|
||||||
|
确认提现
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 温馨提示 -->
|
||||||
|
<div class="notice-card">
|
||||||
|
<div class="notice-title">
|
||||||
|
温馨提示
|
||||||
|
</div>
|
||||||
|
<div class="notice-content">
|
||||||
|
<p>1. 提现将在1-3个工作日内到账,请耐心等待</p>
|
||||||
|
<p>2. 提现金额最低10元,单笔最高50000元</p>
|
||||||
|
<p>3. 提现手续费按实际收取标准执行</p>
|
||||||
|
<p>4. 提现记录可在资产明细中查看</p>
|
||||||
|
<p>5. 如遇问题,请及时联系客服处理</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='css' scoped>
|
||||||
|
.section-card {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: var(--ion-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-item.active {
|
||||||
|
background: #fff5f5;
|
||||||
|
border-color: var(--ion-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-item:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-name {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-balance {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wallet-check {
|
||||||
|
color: var(--ion-color-primary);
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-wallets {
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.currency-symbol {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-input::placeholder {
|
||||||
|
color: #bbb;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-amounts {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-amount-btn {
|
||||||
|
padding: 12px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-amount-btn.active {
|
||||||
|
background: white;
|
||||||
|
border-color: var(--ion-color-primary);
|
||||||
|
color: var(--ion-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-amount-btn.disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-amount-btn:not(.disabled):active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-hint {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--ion-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bank-accounts {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-card:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-card.active {
|
||||||
|
background: #fff5f5;
|
||||||
|
border-color: var(--ion-color-primary);
|
||||||
|
box-shadow: 0 2px 8px rgba(196, 30, 58, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-icon {
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 28px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-type-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-label {
|
||||||
|
color: #999;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-value {
|
||||||
|
color: #666;
|
||||||
|
font-weight: 500;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-check {
|
||||||
|
color: var(--ion-color-primary);
|
||||||
|
font-size: 26px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
animation: checkmark-pop 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes checkmark-pop {
|
||||||
|
0% {
|
||||||
|
transform: scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-accounts {
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-input-wrapper {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-input {
|
||||||
|
--padding-start: 8px;
|
||||||
|
--padding-end: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-wrapper {
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-btn {
|
||||||
|
--background: linear-gradient(135deg, #c41e3a 0%, #8b1a2e 100%);
|
||||||
|
--background-activated: linear-gradient(135deg, #8b1a2e 0%, #c41e3a 100%);
|
||||||
|
--border-radius: 12px;
|
||||||
|
--padding-top: 14px;
|
||||||
|
--padding-bottom: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-card {
|
||||||
|
background: #f9fafb;
|
||||||
|
padding: 16px 20px;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-content {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-content p {
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user