feat: 添加支付方式管理功能,创建支付方式列表和添加支付方式页面,集成表单验证和状态管理
This commit is contained in:
@@ -69,6 +69,16 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: () => import("@/views/address/add.vue"),
|
component: () => import("@/views/address/add.vue"),
|
||||||
meta: { requiresAuth: true },
|
meta: { requiresAuth: true },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/payment",
|
||||||
|
component: () => import("@/views/payment/index.vue"),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/payment/add",
|
||||||
|
component: () => import("@/views/payment/add.vue"),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
|||||||
474
src/views/payment/add.vue
Normal file
474
src/views/payment/add.vue
Normal file
@@ -0,0 +1,474 @@
|
|||||||
|
<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>
|
||||||
351
src/views/payment/index.vue
Normal file
351
src/views/payment/index.vue
Normal file
@@ -0,0 +1,351 @@
|
|||||||
|
<script lang='ts' setup>
|
||||||
|
import { alertController, toastController } from "@ionic/vue";
|
||||||
|
import { addOutline, cardOutline, checkmarkCircleOutline, createOutline, logoAlipay, trashOutline } from "ionicons/icons";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
interface PaymentMethod {
|
||||||
|
id: number;
|
||||||
|
type: "bank" | "alipay";
|
||||||
|
name: string;
|
||||||
|
account: string;
|
||||||
|
bankName?: string;
|
||||||
|
bankBranch?: string;
|
||||||
|
isDefault: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentMethods = ref<PaymentMethod[]>([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: "bank",
|
||||||
|
name: "张三",
|
||||||
|
account: "6222 **** **** 1234",
|
||||||
|
bankName: "中国工商银行",
|
||||||
|
bankBranch: "北京朝阳支行",
|
||||||
|
isDefault: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
type: "alipay",
|
||||||
|
name: "李四",
|
||||||
|
account: "138****8000",
|
||||||
|
isDefault: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
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 handleAdd() {
|
||||||
|
router.push("/payment/add");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEdit(payment: PaymentMethod) {
|
||||||
|
router.push(`/payment/add?id=${payment.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSetDefault(payment: PaymentMethod) {
|
||||||
|
if (payment.isDefault) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentMethods.value.forEach((item) => {
|
||||||
|
item.isDefault = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
payment.isDefault = true;
|
||||||
|
|
||||||
|
// TODO: 调用 API 更新默认收款方式
|
||||||
|
await showToast("已设为默认收款方式");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDelete(payment: PaymentMethod) {
|
||||||
|
const alert = await alertController.create({
|
||||||
|
header: "确认删除",
|
||||||
|
message: `确定要删除这个收款方式吗?`,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "取消",
|
||||||
|
role: "cancel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "删除",
|
||||||
|
role: "destructive",
|
||||||
|
handler: async () => {
|
||||||
|
if (payment.isDefault && paymentMethods.value.length > 1) {
|
||||||
|
const otherPayment = paymentMethods.value.find(item => item.id !== payment.id);
|
||||||
|
if (otherPayment) {
|
||||||
|
otherPayment.isDefault = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = paymentMethods.value.findIndex(item => item.id === payment.id);
|
||||||
|
if (index > -1) {
|
||||||
|
paymentMethods.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 调用 API 删除收款方式
|
||||||
|
await showToast("删除成功");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await alert.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPaymentIcon(type: string) {
|
||||||
|
return type === "bank" ? cardOutline : logoAlipay;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPaymentTypeName(type: string) {
|
||||||
|
return type === "bank" ? "银行卡" : "支付宝";
|
||||||
|
}
|
||||||
|
|
||||||
|
function maskAccount(account: string) {
|
||||||
|
// 如果已经是脱敏格式,直接返回
|
||||||
|
if (account.includes("*")) {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
// 否则进行脱敏处理
|
||||||
|
if (account.length > 8) {
|
||||||
|
return `${account.slice(0, 4)} **** **** ${account.slice(-4)}`;
|
||||||
|
}
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
</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 @click="handleAdd">
|
||||||
|
<ion-icon slot="icon-only" :icon="addOutline" />
|
||||||
|
</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content>
|
||||||
|
<div v-if="paymentMethods.length > 0" class="ion-padding">
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div
|
||||||
|
v-for="payment in paymentMethods"
|
||||||
|
:key="payment.id"
|
||||||
|
class="payment-card"
|
||||||
|
>
|
||||||
|
<!-- 收款方式信息 -->
|
||||||
|
<div class="payment-info">
|
||||||
|
<div class="flex items-center justify-between mb-3">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<ion-icon
|
||||||
|
:icon="getPaymentIcon(payment.type)"
|
||||||
|
class="text-2xl"
|
||||||
|
:class="payment.type === 'alipay' ? 'text-[#1677ff]' : 'text-primary'"
|
||||||
|
/>
|
||||||
|
<span class="payment-type">{{ getPaymentTypeName(payment.type) }}</span>
|
||||||
|
</div>
|
||||||
|
<ion-badge v-if="payment.isDefault" color="danger" class="default-badge">
|
||||||
|
默认
|
||||||
|
</ion-badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-details">
|
||||||
|
<div class="detail-row">
|
||||||
|
<span class="label">姓名</span>
|
||||||
|
<span class="value">{{ payment.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-row">
|
||||||
|
<span class="label">账号</span>
|
||||||
|
<span class="value">{{ maskAccount(payment.account) }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="payment.type === 'bank' && payment.bankName" class="detail-row">
|
||||||
|
<span class="label">银行</span>
|
||||||
|
<span class="value">{{ payment.bankName }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="payment.type === 'bank' && payment.bankBranch" class="detail-row">
|
||||||
|
<span class="label">开户行</span>
|
||||||
|
<span class="value">{{ payment.bankBranch }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<div class="payment-actions">
|
||||||
|
<ion-button
|
||||||
|
fill="clear"
|
||||||
|
size="small"
|
||||||
|
@click="handleSetDefault(payment)"
|
||||||
|
>
|
||||||
|
<ion-icon slot="start" :icon="checkmarkCircleOutline" />
|
||||||
|
{{ payment.isDefault ? '默认方式' : '设为默认' }}
|
||||||
|
</ion-button>
|
||||||
|
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<ion-button
|
||||||
|
fill="clear"
|
||||||
|
size="small"
|
||||||
|
color="medium"
|
||||||
|
@click="handleEdit(payment)"
|
||||||
|
>
|
||||||
|
<ion-icon slot="start" :icon="createOutline" />
|
||||||
|
编辑
|
||||||
|
</ion-button>
|
||||||
|
|
||||||
|
<ion-button
|
||||||
|
fill="clear"
|
||||||
|
size="small"
|
||||||
|
color="danger"
|
||||||
|
@click="handleDelete(payment)"
|
||||||
|
>
|
||||||
|
<ion-icon slot="start" :icon="trashOutline" />
|
||||||
|
删除
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-else class="empty-state">
|
||||||
|
<empty title="暂无收款方式">
|
||||||
|
<template #icon>
|
||||||
|
<ion-icon :icon="cardOutline" class="empty-icon" />
|
||||||
|
</template>
|
||||||
|
<template #extra>
|
||||||
|
<ion-button class="add-button" @click="handleAdd">
|
||||||
|
<ion-icon slot="start" :icon="addOutline" />
|
||||||
|
添加收款方式
|
||||||
|
</ion-button>
|
||||||
|
</template>
|
||||||
|
</empty>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部添加按钮 -->
|
||||||
|
<div v-if="paymentMethods.length > 0" class="fixed-bottom">
|
||||||
|
<ion-button expand="block" class="add-button" @click="handleAdd">
|
||||||
|
<ion-icon slot="start" :icon="addOutline" />
|
||||||
|
添加新收款方式
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
|
</ion-content>
|
||||||
|
</ion-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang='css' scoped>
|
||||||
|
.payment-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-info {
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-type {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.default-badge {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-details {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row .label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #999;
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row .value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: right;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-actions ion-button {
|
||||||
|
--padding-start: 8px;
|
||||||
|
--padding-end: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 60vh;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
font-size: 80px;
|
||||||
|
color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-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: 15px;
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixed-bottom {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: white;
|
||||||
|
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
z-index: 10;
|
||||||
|
padding-bottom: calc(12px + var(--ion-safe-area-bottom));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user