feat: 更新 API 地址,修改首页和支付页面的逻辑,优化收款方式管理功能
This commit is contained in:
@@ -1 +1 @@
|
|||||||
VITE_API_URL=http://192.168.1.2:9538
|
VITE_API_URL=http://192.168.1.2:9537
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 161 KiB |
@@ -4,7 +4,7 @@ export const actions = [
|
|||||||
{ id: "check_in", name: "签到", icon: calendarOutline, color: "#c32120" },
|
{ id: "check_in", name: "签到", icon: calendarOutline, color: "#c32120" },
|
||||||
{ id: "team", name: "团队中心", icon: peopleOutline, color: "#c32120" },
|
{ id: "team", name: "团队中心", icon: peopleOutline, color: "#c32120" },
|
||||||
{ id: "invite", name: "邀请好友", icon: rocketOutline, color: "#c32120" },
|
{ id: "invite", name: "邀请好友", icon: rocketOutline, color: "#c32120" },
|
||||||
{ id: "support", name: "在线客服", icon: chatbubblesOutline, color: "#c32120" },
|
{ id: "support", name: "在线对话", icon: chatbubblesOutline, color: "#c32120" },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type Action = (typeof actions)[number];
|
export type Action = (typeof actions)[number];
|
||||||
|
|||||||
@@ -59,10 +59,18 @@ function handleNewsClick(news: NewsItem) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ion-page>
|
<ion-page>
|
||||||
|
<ion-header class="ion-no-border">
|
||||||
|
<ion-toolbar class="ion-toolbar">
|
||||||
|
<img slot="start" src="@/assets/images/icon-1.png" class="h-8 w-8 ml-2" alt="Logo">
|
||||||
|
<ion-title>国务院深化改革战略推进委员会</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
<ion-content :fullscreen="true" class="home-page">
|
<ion-content :fullscreen="true" class="home-page">
|
||||||
<img src="@/assets/images/home-banner.jpg" class="h-60 w-full object-cover" alt="首页横幅">
|
|
||||||
|
|
||||||
<div class="ion-padding-horizontal">
|
<div class="ion-padding-horizontal">
|
||||||
|
<div class="rounded-2xl overflow-hidden relative mt-4 shadow-md">
|
||||||
|
<img src="@/assets/images/home-banner.jpg" class="h-50 w-full object-cover" alt="首页横幅">
|
||||||
|
</div>
|
||||||
|
|
||||||
<section class="my-5 grid grid-cols-4 gap-4">
|
<section class="my-5 grid grid-cols-4 gap-4">
|
||||||
<div
|
<div
|
||||||
v-for="action in actions"
|
v-for="action in actions"
|
||||||
|
|||||||
@@ -1,26 +1,17 @@
|
|||||||
<script lang='ts' setup>
|
<script lang='ts' setup>
|
||||||
|
import type { Treaty } from "@elysiajs/eden";
|
||||||
|
import type { TreatyQuery } from "@/api/types";
|
||||||
import { toastController } from "@ionic/vue";
|
import { toastController } from "@ionic/vue";
|
||||||
import { cardOutline, checkmarkCircleOutline, logoAlipay, personOutline } from "ionicons/icons";
|
import { cardOutline, checkmarkCircleOutline, logoAlipay, personOutline } from "ionicons/icons";
|
||||||
import zod from "zod";
|
import zod from "zod";
|
||||||
|
import { client, safeClient } from "@/api";
|
||||||
|
|
||||||
|
type Receipt = Treaty.Data<typeof client.api.receipt_method.get>["data"][number];
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const paymentType = ref<"bank" | "alipay">("bank");
|
const paymentType = ref<"bank_card" | "alipay">("bank_card");
|
||||||
|
|
||||||
const bankFormData = ref({
|
|
||||||
name: "",
|
|
||||||
bankName: "",
|
|
||||||
bankBranch: "",
|
|
||||||
cardNumber: "",
|
|
||||||
isDefault: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const alipayFormData = ref({
|
|
||||||
name: "",
|
|
||||||
account: "",
|
|
||||||
isDefault: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const isSubmitting = ref(false);
|
const isSubmitting = ref(false);
|
||||||
const isEditMode = computed(() => !!route.query.id);
|
const isEditMode = computed(() => !!route.query.id);
|
||||||
@@ -82,23 +73,15 @@ async function showToast(message: string, color: "success" | "danger" | "warning
|
|||||||
// 如果是编辑模式,加载数据
|
// 如果是编辑模式,加载数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (isEditMode.value) {
|
if (isEditMode.value) {
|
||||||
// TODO: 根据 route.query.id 加载收款方式数据
|
const id = route.query.id as string;
|
||||||
// 模拟数据
|
safeClient(client.api.receipt_method({ id }).get());
|
||||||
paymentType.value = "bank";
|
|
||||||
bankFormData.value = {
|
|
||||||
name: "张三",
|
|
||||||
bankName: "中国工商银行",
|
|
||||||
bankBranch: "北京朝阳支行",
|
|
||||||
cardNumber: "6222021234567891234",
|
|
||||||
isDefault: true,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
let result;
|
let result;
|
||||||
|
|
||||||
if (paymentType.value === "bank") {
|
if (paymentType.value === "bank_card") {
|
||||||
result = BankSchema.safeParse(bankFormData.value);
|
result = BankSchema.safeParse(bankFormData.value);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -114,12 +97,12 @@ async function handleSubmit() {
|
|||||||
isSubmitting.value = true;
|
isSubmitting.value = true;
|
||||||
try {
|
try {
|
||||||
// TODO: 调用添加/编辑收款方式 API
|
// TODO: 调用添加/编辑收款方式 API
|
||||||
// const formData = paymentType.value === "bank" ? bankFormData.value : alipayFormData.value;
|
if (isEditMode.value) {
|
||||||
// if (isEditMode.value) {
|
await safeClient(client.api.receipt_method.post({ ...formData, type: paymentType.value }));
|
||||||
// await safeClient(client.api.payment[route.query.id].put({ ...formData, type: paymentType.value }));
|
}
|
||||||
// } else {
|
else {
|
||||||
// await safeClient(client.api.payment.post({ ...formData, type: paymentType.value }));
|
await safeClient(client.api.payment.post({ ...formData, type: paymentType.value }));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 模拟 API 调用
|
// 模拟 API 调用
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
@@ -165,8 +148,8 @@ async function handleSubmit() {
|
|||||||
<div class="type-buttons">
|
<div class="type-buttons">
|
||||||
<div
|
<div
|
||||||
class="type-button"
|
class="type-button"
|
||||||
:class="{ active: paymentType === 'bank' }"
|
:class="{ active: paymentType === 'bank_card' }"
|
||||||
@click="paymentType = 'bank'"
|
@click="paymentType = 'bank_card'"
|
||||||
>
|
>
|
||||||
<ion-icon :icon="cardOutline" class="type-icon" />
|
<ion-icon :icon="cardOutline" class="type-icon" />
|
||||||
<span>银行卡</span>
|
<span>银行卡</span>
|
||||||
@@ -183,7 +166,7 @@ async function handleSubmit() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 银行卡表单 -->
|
<!-- 银行卡表单 -->
|
||||||
<div v-if="paymentType === 'bank'" class="form-card">
|
<div v-if="paymentType === 'bank_card'" class="form-card">
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<!-- 持卡人姓名 -->
|
<!-- 持卡人姓名 -->
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
|
|||||||
@@ -1,37 +1,13 @@
|
|||||||
<script lang='ts' setup>
|
<script lang='ts' setup>
|
||||||
|
import type { Treaty } from "@elysiajs/eden";
|
||||||
import { alertController, toastController } from "@ionic/vue";
|
import { alertController, toastController } from "@ionic/vue";
|
||||||
import { addOutline, cardOutline, checkmarkCircleOutline, createOutline, logoAlipay, trashOutline } from "ionicons/icons";
|
import { addOutline, cardOutline, createOutline, logoAlipay, trashOutline } from "ionicons/icons";
|
||||||
|
import { client, safeClient } from "@/api";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
type Receipt = Treaty.Data<typeof client.api.receipt_method.get>["data"][number];
|
||||||
|
|
||||||
interface PaymentMethod {
|
const { data } = await safeClient(client.api.receipt_method.get({ query: { offset: 0, limit: 20 } }));
|
||||||
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") {
|
async function showToast(message: string, color: "success" | "danger" | "warning" = "success") {
|
||||||
const toast = await toastController.create({
|
const toast = await toastController.create({
|
||||||
@@ -47,26 +23,11 @@ function handleAdd() {
|
|||||||
router.push("/payment/add");
|
router.push("/payment/add");
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(payment: PaymentMethod) {
|
function handleEdit(payment: Receipt) {
|
||||||
router.push(`/payment/add?id=${payment.id}`);
|
router.push(`/payment/add?id=${payment.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSetDefault(payment: PaymentMethod) {
|
async function handleDelete(payment: Receipt) {
|
||||||
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({
|
const alert = await alertController.create({
|
||||||
header: "确认删除",
|
header: "确认删除",
|
||||||
message: `确定要删除这个收款方式吗?`,
|
message: `确定要删除这个收款方式吗?`,
|
||||||
@@ -79,19 +40,11 @@ async function handleDelete(payment: PaymentMethod) {
|
|||||||
text: "删除",
|
text: "删除",
|
||||||
role: "destructive",
|
role: "destructive",
|
||||||
handler: async () => {
|
handler: async () => {
|
||||||
if (payment.isDefault && paymentMethods.value.length > 1) {
|
const index = data.value?.data.findIndex(item => item.id === payment.id);
|
||||||
const otherPayment = paymentMethods.value.find(item => item.id !== payment.id);
|
if (index && index > -1) {
|
||||||
if (otherPayment) {
|
data.value?.data.splice(index, 1);
|
||||||
otherPayment.isDefault = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
await safeClient(client.api.receipt_method({ id: payment.id }).delete());
|
||||||
const index = paymentMethods.value.findIndex(item => item.id === payment.id);
|
|
||||||
if (index > -1) {
|
|
||||||
paymentMethods.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 调用 API 删除收款方式
|
|
||||||
await showToast("删除成功");
|
await showToast("删除成功");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -101,24 +54,12 @@ async function handleDelete(payment: PaymentMethod) {
|
|||||||
await alert.present();
|
await alert.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPaymentIcon(type: string) {
|
function getPaymentIcon(type: Receipt["type"]) {
|
||||||
return type === "bank" ? cardOutline : logoAlipay;
|
return type === "bank_card" ? cardOutline : logoAlipay;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPaymentTypeName(type: string) {
|
function getPaymentTypeName(type: Receipt["type"]) {
|
||||||
return type === "bank" ? "银行卡" : "支付宝";
|
return type === "bank_card" ? "银行卡" : "支付宝";
|
||||||
}
|
|
||||||
|
|
||||||
function maskAccount(account: string) {
|
|
||||||
// 如果已经是脱敏格式,直接返回
|
|
||||||
if (account.includes("*")) {
|
|
||||||
return account;
|
|
||||||
}
|
|
||||||
// 否则进行脱敏处理
|
|
||||||
if (account.length > 8) {
|
|
||||||
return `${account.slice(0, 4)} **** **** ${account.slice(-4)}`;
|
|
||||||
}
|
|
||||||
return account;
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -139,10 +80,25 @@ function maskAccount(account: string) {
|
|||||||
</ion-header>
|
</ion-header>
|
||||||
|
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<div v-if="paymentMethods.length > 0" class="ion-padding">
|
<!-- 空状态 -->
|
||||||
|
<div v-if="data?.data?.length === 0" 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-else class="ion-padding">
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<div
|
<div
|
||||||
v-for="payment in paymentMethods"
|
v-for="payment in data?.data"
|
||||||
:key="payment.id"
|
:key="payment.id"
|
||||||
class="payment-card"
|
class="payment-card"
|
||||||
>
|
>
|
||||||
@@ -157,42 +113,30 @@ function maskAccount(account: string) {
|
|||||||
/>
|
/>
|
||||||
<span class="payment-type">{{ getPaymentTypeName(payment.type) }}</span>
|
<span class="payment-type">{{ getPaymentTypeName(payment.type) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<ion-badge v-if="payment.isDefault" color="danger" class="default-badge">
|
|
||||||
默认
|
|
||||||
</ion-badge>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="payment-details">
|
<div class="payment-details">
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="label">姓名</span>
|
<span class="label">姓名</span>
|
||||||
<span class="value">{{ payment.name }}</span>
|
<span class="value">{{ payment.fullName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-row">
|
<div class="detail-row">
|
||||||
<span class="label">账号</span>
|
<span class="label">账号</span>
|
||||||
<span class="value">{{ maskAccount(payment.account) }}</span>
|
<span class="value">{{ payment.bankCardNumber || payment.alipayAccount }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="payment.type === 'bank' && payment.bankName" class="detail-row">
|
<div v-if="payment.type === 'bank_card' && payment.bankName" class="detail-row">
|
||||||
<span class="label">银行</span>
|
<span class="label">银行</span>
|
||||||
<span class="value">{{ payment.bankName }}</span>
|
<span class="value">{{ payment.bankName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="payment.type === 'bank' && payment.bankBranch" class="detail-row">
|
<div v-if="payment.type === 'bank_card' && payment.bankBranchName" class="detail-row">
|
||||||
<span class="label">开户行</span>
|
<span class="label">开户行</span>
|
||||||
<span class="value">{{ payment.bankBranch }}</span>
|
<span class="value">{{ payment.bankBranchName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<div class="payment-actions">
|
<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">
|
<div class="flex gap-2">
|
||||||
<ion-button
|
<ion-button
|
||||||
fill="clear"
|
fill="clear"
|
||||||
@@ -219,23 +163,8 @@ function maskAccount(account: string) {
|
|||||||
</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">
|
<div v-if="data?.data && data.data.length > 0" class="fixed-bottom">
|
||||||
<ion-button expand="block" class="add-button" @click="handleAdd">
|
<ion-button expand="block" class="add-button" @click="handleAdd">
|
||||||
<ion-icon slot="start" :icon="addOutline" />
|
<ion-icon slot="start" :icon="addOutline" />
|
||||||
添加新收款方式
|
添加新收款方式
|
||||||
|
|||||||
@@ -2,14 +2,7 @@
|
|||||||
import type { Treaty } from "@elysiajs/eden";
|
import type { Treaty } from "@elysiajs/eden";
|
||||||
import type { InfiniteScrollCustomEvent } from "@ionic/vue";
|
import type { InfiniteScrollCustomEvent } from "@ionic/vue";
|
||||||
import type { TreatyQuery } from "@/api/types";
|
import type { TreatyQuery } from "@/api/types";
|
||||||
import {
|
import { calendarOutline, cardOutline, timeOutline, trendingUpOutline } from "ionicons/icons";
|
||||||
calendarOutline,
|
|
||||||
cardOutline,
|
|
||||||
chevronForwardOutline,
|
|
||||||
timeOutline,
|
|
||||||
trendingUpOutline,
|
|
||||||
walletOutline,
|
|
||||||
} from "ionicons/icons";
|
|
||||||
import { client, safeClient } from "@/api";
|
import { client, safeClient } from "@/api";
|
||||||
|
|
||||||
type Product = Treaty.Data<typeof client.api.subscription.products.get>["data"][number];
|
type Product = Treaty.Data<typeof client.api.subscription.products.get>["data"][number];
|
||||||
|
|||||||
Reference in New Issue
Block a user