Files
financial/src/views/payment/add.vue

475 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang='ts' setup>
import { toastController } from "@ionic/vue";
import { cardOutline, checkmarkCircleOutline, logoAlipay, personOutline } from "ionicons/icons";
import zod from "zod";
const router = useRouter();
const route = useRoute();
const paymentType = ref<"bank" | "alipay">("bank");
const bankFormData = ref({
name: "",
bankName: "",
bankBranch: "",
cardNumber: "",
isDefault: false,
});
const alipayFormData = ref({
name: "",
account: "",
isDefault: false,
});
const isSubmitting = ref(false);
const isEditMode = computed(() => !!route.query.id);
// 银行卡表单验证 Schema
const BankSchema = zod.object({
name: zod
.string()
.min(2, "请输入持卡人姓名")
.max(20, "姓名长度不能超过20个字符"),
bankName: zod
.string()
.min(2, "请输入银行名称")
.max(50, "银行名称长度不能超过50个字符"),
bankBranch: zod
.string()
.min(2, "请输入开户行")
.max(100, "开户行长度不能超过100个字符"),
cardNumber: zod
.string()
.min(1, "请输入银行卡号")
.regex(/^\d{16,19}$/, "请输入正确的银行卡号16-19位数字"),
});
// 支付宝表单验证 Schema
const AlipaySchema = zod.object({
name: zod
.string()
.min(2, "请输入真实姓名")
.max(20, "姓名长度不能超过20个字符"),
account: zod
.string()
.min(1, "请输入支付宝账号")
.refine(
(val) => {
// 支持手机号或邮箱
const phoneRegex = /^1[3-9]\d{9}$/;
const emailRegex = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
return phoneRegex.test(val) || emailRegex.test(val);
},
"请输入正确的支付宝账号(手机号或邮箱)",
),
});
async function showToast(message: string, color: "success" | "danger" | "warning" = "danger") {
const toast = await toastController.create({
message,
duration: 2000,
position: "top",
color,
});
await toast.present();
}
// 如果是编辑模式,加载数据
onMounted(() => {
if (isEditMode.value) {
// TODO: 根据 route.query.id 加载收款方式数据
// 模拟数据
paymentType.value = "bank";
bankFormData.value = {
name: "张三",
bankName: "中国工商银行",
bankBranch: "北京朝阳支行",
cardNumber: "6222021234567891234",
isDefault: true,
};
}
});
async function handleSubmit() {
let result;
if (paymentType.value === "bank") {
result = BankSchema.safeParse(bankFormData.value);
}
else {
result = AlipaySchema.safeParse(alipayFormData.value);
}
if (!result.success) {
const first = result.error.issues[0];
await showToast(first.message);
return;
}
isSubmitting.value = true;
try {
// TODO: 调用添加/编辑收款方式 API
// const formData = paymentType.value === "bank" ? bankFormData.value : alipayFormData.value;
// if (isEditMode.value) {
// await safeClient(client.api.payment[route.query.id].put({ ...formData, type: paymentType.value }));
// } else {
// await safeClient(client.api.payment.post({ ...formData, type: paymentType.value }));
// }
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1000));
await showToast(isEditMode.value ? "收款方式修改成功" : "收款方式添加成功", "success");
router.back();
}
catch (error) {
await showToast("操作失败,请重试", "danger");
}
finally {
isSubmitting.value = false;
}
}
</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>{{ isEditMode ? '编辑收款方式' : '添加收款方式' }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="space-y-5">
<!-- 提示信息 -->
<div class="info-card">
<ion-icon :icon="checkmarkCircleOutline" class="text-2xl text-[#c41e3a]" />
<div class="text-sm text-[#666] leading-relaxed">
请填写准确的收款信息确保能够正常收款
</div>
</div>
<!-- 类型选择 -->
<div v-if="!isEditMode" class="type-selector">
<div class="type-title">
选择收款方式类型
</div>
<div class="type-buttons">
<div
class="type-button"
:class="{ active: paymentType === 'bank' }"
@click="paymentType = 'bank'"
>
<ion-icon :icon="cardOutline" class="type-icon" />
<span>银行卡</span>
</div>
<div
class="type-button"
:class="{ active: paymentType === 'alipay' }"
@click="paymentType = 'alipay'"
>
<ion-icon :icon="logoAlipay" class="type-icon" />
<span>支付宝</span>
</div>
</div>
</div>
<!-- 银行卡表单 -->
<div v-if="paymentType === 'bank'" class="form-card">
<div class="space-y-4">
<!-- 持卡人姓名 -->
<div class="form-item">
<div class="flex items-center gap-2 mb-2">
<ion-icon :icon="personOutline" class="text-lg text-primary" />
<label class="form-label">持卡人姓名</label>
</div>
<ion-item lines="none" class="input-item">
<ion-input
v-model="bankFormData.name"
type="text"
placeholder="请输入持卡人姓名"
class="custom-input"
:maxlength="20"
/>
</ion-item>
</div>
<!-- 银行名称 -->
<div class="form-item">
<div class="flex items-center gap-2 mb-2">
<ion-icon :icon="cardOutline" class="text-lg text-primary" />
<label class="form-label">银行名称</label>
</div>
<ion-item lines="none" class="input-item">
<ion-input
v-model="bankFormData.bankName"
type="text"
placeholder="例如:中国工商银行"
class="custom-input"
:maxlength="50"
/>
</ion-item>
</div>
<!-- 开户行 -->
<div class="form-item">
<div class="flex items-center gap-2 mb-2">
<ion-icon :icon="cardOutline" class="text-lg text-primary" />
<label class="form-label">开户行</label>
</div>
<ion-item lines="none" class="input-item">
<ion-input
v-model="bankFormData.bankBranch"
type="text"
placeholder="例如:北京朝阳支行"
class="custom-input"
:maxlength="100"
/>
</ion-item>
</div>
<!-- 银行卡号 -->
<div class="form-item">
<div class="flex items-center gap-2 mb-2">
<ion-icon :icon="cardOutline" class="text-lg text-primary" />
<label class="form-label">银行卡号</label>
</div>
<ion-item lines="none" class="input-item">
<ion-input
v-model="bankFormData.cardNumber"
type="tel"
placeholder="请输入银行卡号"
class="custom-input"
:maxlength="19"
/>
</ion-item>
</div>
<!-- 设为默认 -->
<div class="form-item">
<div class="flex items-center justify-between">
<label class="form-label">设为默认收款方式</label>
<ion-toggle v-model="bankFormData.isDefault" color="danger" />
</div>
</div>
</div>
</div>
<!-- 支付宝表单 -->
<div v-else class="form-card">
<div class="space-y-4">
<!-- 支付宝姓名 -->
<div class="form-item">
<div class="flex items-center gap-2 mb-2">
<ion-icon :icon="personOutline" class="text-lg text-[#1677ff]" />
<label class="form-label">支付宝姓名</label>
</div>
<ion-item lines="none" class="input-item">
<ion-input
v-model="alipayFormData.name"
type="text"
placeholder="请输入支付宝实名姓名"
class="custom-input"
:maxlength="20"
/>
</ion-item>
</div>
<!-- 支付宝账号 -->
<div class="form-item">
<div class="flex items-center gap-2 mb-2">
<ion-icon :icon="logoAlipay" class="text-lg text-[#1677ff]" />
<label class="form-label">支付宝账号</label>
</div>
<ion-item lines="none" class="input-item">
<ion-input
v-model="alipayFormData.account"
type="text"
placeholder="请输入手机号或邮箱"
class="custom-input"
/>
</ion-item>
<div class="text-xs text-[#999] mt-1 ml-1">
支持手机号或邮箱格式
</div>
</div>
<!-- 设为默认 -->
<div class="form-item">
<div class="flex items-center justify-between">
<label class="form-label">设为默认收款方式</label>
<ion-toggle v-model="alipayFormData.isDefault" color="danger" />
</div>
</div>
</div>
</div>
<!-- 提交按钮 -->
<ion-button
expand="block"
class="submit-button"
:disabled="isSubmitting"
@click="handleSubmit"
>
<ion-spinner v-if="isSubmitting" name="crescent" />
<span v-else>{{ isEditMode ? '保存修改' : '保存收款方式' }}</span>
</ion-button>
<!-- 安全提示 -->
<div class="notice-card">
<div class="text-sm font-semibold text-[#333] mb-2">
安全提示
</div>
<ul class="text-xs text-[#666] space-y-1 pl-4">
<li> 请确保填写的收款信息准确无误</li>
<li> 收款信息仅用于提现和收款我们会严格保密</li>
<li> 银行卡需与实名认证信息一致</li>
<li> 如需修改收款方式请联系客服</li>
</ul>
</div>
</div>
</ion-content>
</ion-page>
</template>
<style lang='css' scoped>
.info-card {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 16px;
background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%);
border-radius: 12px;
border: 1px solid #ffe0e0;
}
.type-selector {
background: white;
border-radius: 16px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.type-title {
font-size: 15px;
font-weight: 600;
color: #333;
margin-bottom: 16px;
}
.type-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.type-button {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px 16px;
background: #f7f8fa;
border: 2px solid #e5e7eb;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
}
.type-button.active {
background: linear-gradient(135deg, #fff5f5 0%, #ffffff 100%);
border-color: #c41e3a;
}
.type-button:active {
transform: scale(0.98);
}
.type-icon {
font-size: 32px;
color: #666;
margin-bottom: 8px;
}
.type-button.active .type-icon {
color: #c41e3a;
}
.type-button span {
font-size: 14px;
font-weight: 600;
color: #666;
}
.type-button.active span {
color: #c41e3a;
}
.form-card {
background: white;
border-radius: 16px;
padding: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.form-item {
margin-bottom: 20px;
}
.form-item:last-child {
margin-bottom: 0;
}
.form-label {
font-size: 15px;
font-weight: 600;
color: #333;
}
.input-item {
--background: #f7f8fa;
--border-radius: 12px;
--padding-start: 16px;
--padding-end: 16px;
--min-height: 48px;
}
.custom-input {
--placeholder-color: #999;
--placeholder-opacity: 1;
font-size: 15px;
}
.submit-button {
--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;
margin-top: 8px;
text-transform: none;
letter-spacing: 0.5px;
}
.notice-card {
background: #f9fafb;
border-radius: 12px;
padding: 16px;
border: 1px solid #e5e7eb;
}
.notice-card ul li {
line-height: 1.8;
}
</style>