feat: 更新 '@capp/eden' 依赖至 0.0.12,优化实名认证表单逻辑,添加状态管理和图片上传功能

This commit is contained in:
2026-01-18 23:13:27 +07:00
parent 40ad132ce7
commit 706e347563
4 changed files with 112 additions and 25 deletions

14
pnpm-lock.yaml generated
View File

@@ -52,8 +52,8 @@ catalogs:
specifier: 8.0.0
version: 8.0.0
'@capp/eden':
specifier: http://192.168.1.2:9538/api/capp-eden-0.0.11.tgz
version: 0.0.11
specifier: http://192.168.1.2:9538/api/capp-eden-0.0.12.tgz
version: 0.0.12
'@cloudflare/workers-types':
specifier: ^4.20260113.0
version: 4.20260116.0
@@ -298,7 +298,7 @@ importers:
version: 8.0.0(@capacitor/core@8.0.0)
'@capp/eden':
specifier: 'catalog:'
version: http://192.168.1.2:9538/api/capp-eden-0.0.11.tgz(@elysiajs/eden@1.4.6(elysia@1.4.22(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)))
version: http://192.168.1.2:9538/api/capp-eden-0.0.12.tgz(@elysiajs/eden@1.4.6(elysia@1.4.22(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)))
'@elysiajs/eden':
specifier: 'catalog:'
version: 1.4.6(elysia@1.4.22(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3))
@@ -1182,9 +1182,9 @@ packages:
'@capacitor/synapse@1.0.4':
resolution: {integrity: sha512-/C1FUo8/OkKuAT4nCIu/34ny9siNHr9qtFezu4kxm6GY1wNFxrCFWjfYx5C1tUhVGz3fxBABegupkpjXvjCHrw==}
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.11.tgz':
resolution: {tarball: http://192.168.1.2:9538/api/capp-eden-0.0.11.tgz}
version: 0.0.11
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.12.tgz':
resolution: {tarball: http://192.168.1.2:9538/api/capp-eden-0.0.12.tgz}
version: 0.0.12
peerDependencies:
'@elysiajs/eden': ^1.4.6
@@ -6903,7 +6903,7 @@ snapshots:
'@capacitor/synapse@1.0.4': {}
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.11.tgz(@elysiajs/eden@1.4.6(elysia@1.4.22(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)))':
'@capp/eden@http://192.168.1.2:9538/api/capp-eden-0.0.12.tgz(@elysiajs/eden@1.4.6(elysia@1.4.22(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3)))':
dependencies:
'@elysiajs/eden': 1.4.6(elysia@1.4.22(@sinclair/typebox@0.34.47)(exact-mirror@0.2.6(@sinclair/typebox@0.34.47))(file-type@21.3.0)(openapi-types@12.1.3)(typescript@5.9.3))

View File

@@ -18,7 +18,7 @@ catalog:
'@capacitor/keyboard': 8.0.0
'@capacitor/share': ^8.0.0
'@capacitor/status-bar': 8.0.0
'@capp/eden': http://192.168.1.2:9538/api/capp-eden-0.0.11.tgz
'@capp/eden': http://192.168.1.2:9538/api/capp-eden-0.0.12.tgz
'@cloudflare/workers-types': ^4.20260113.0
'@elysiajs/eden': ^1.4.6
'@faker-js/faker': ^10.2.0

View File

@@ -3,19 +3,49 @@ import { Camera, CameraResultType, CameraSource } from "@capacitor/camera";
import { actionSheetController, toastController } from "@ionic/vue";
import { cameraOutline, cardOutline, checkmarkCircleOutline, imageOutline, personOutline } from "ionicons/icons";
import zod from "zod";
import { client } from "@/api";
import { client, safeClient } from "@/api";
const router = useRouter();
const formData = ref({
interface RealNameFormData {
kycMethod: "id_card";
documentName: string;
documentNumber: string;
fileId1: string;
fileId2: string;
}
const formData = ref<RealNameFormData>({
kycMethod: "id_card",
documentName: "",
documentNumber: "",
fileId1: "",
fileId2: "",
});
const image1 = ref<string>("");
const image2 = ref<string>("");
const isSubmitting = ref(false);
const status = ref<"unverified" | "pending" | "approved" | "rejected">("unverified");
// 计算属性:是否可以编辑
const canEdit = computed(() => {
return status.value === "unverified" || status.value === "rejected";
});
// 计算属性:状态文本和颜色
const statusInfo = computed(() => {
switch (status.value) {
case "pending":
return { text: "审核中", color: "#faad14", bg: "#fff7e6" };
case "approved":
return { text: "已通过", color: "#52c41a", bg: "#f6ffed" };
case "rejected":
return { text: "已拒绝", color: "#f5222d", bg: "#fff1f0" };
default:
return { text: "未认证", color: "#999", bg: "#f7f8fa" };
}
});
// 表单验证 Schema
const RealNameSchema = zod.object({
@@ -104,9 +134,11 @@ async function selectFromGallery(type: "front" | "back") {
if (image.dataUrl) {
if (type === "front") {
formData.value.fileId1 = fileId;
image1.value = image.dataUrl;
}
else {
formData.value.fileId2 = fileId;
image2.value = image.dataUrl;
}
}
}
@@ -149,11 +181,9 @@ async function handleSubmit() {
isSubmitting.value = true;
try {
// TODO: 调用实名认证 API
// const { data } = await safeClient(client.api.realname.post(formData.value));
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 1500));
await safeClient(client.api.kyc({ kycMethod: "id_card" }).post({
...formData.value,
}));
await showToast("实名认证提交成功,等待审核", "success");
router.back();
@@ -165,6 +195,22 @@ async function handleSubmit() {
isSubmitting.value = false;
}
}
onMounted(async () => {
const { data } = await safeClient(client.api.kyc({ kycMethod: "id_card" }).get());
if (data.value) {
formData.value.documentName = data.value.documentName || "";
formData.value.documentNumber = data.value.documentNumber || "";
formData.value.fileId1 = data.value.fileId1 || "";
formData.value.fileId2 = data.value.fileId2 || "";
status.value = data.value?.status || "unverified";
}
const { data: urls } = await safeClient(client.api.file_storage.access_urls.post({
fileIds: [formData.value.fileId1, formData.value.fileId2],
}));
image1.value = urls.value?.[0].url || "";
image2.value = urls.value?.[1].url || "";
});
</script>
<template>
@@ -180,8 +226,27 @@ async function handleSubmit() {
<ion-content class="ion-padding">
<div class="space-y-5">
<!-- 状态提示 -->
<div v-if="status !== 'unverified'" class="status-card" :style="{ background: statusInfo.bg, borderColor: statusInfo.color }">
<ion-icon :icon="checkmarkCircleOutline" class="text-2xl" :style="{ color: statusInfo.color }" />
<div class="flex-1">
<div class="text-sm font-semibold" :style="{ color: statusInfo.color }">
认证状态{{ statusInfo.text }}
</div>
<div v-if="status === 'pending'" class="text-xs text-[#666] mt-1">
您的认证资料正在审核中请耐心等待
</div>
<div v-else-if="status === 'approved'" class="text-xs text-[#666] mt-1">
您已完成实名认证
</div>
<div v-else-if="status === 'rejected'" class="text-xs text-[#666] mt-1">
认证未通过请重新提交资料
</div>
</div>
</div>
<!-- 提示信息 -->
<div class="info-card">
<div v-if="status === 'unverified'" class="info-card">
<ion-icon :icon="checkmarkCircleOutline" class="text-2xl text-[#c41e3a]" />
<div class="text-sm text-[#666] leading-relaxed">
为了保障您的账户安全请完成实名认证您的个人信息将严格保密
@@ -203,6 +268,7 @@ async function handleSubmit() {
type="text"
placeholder="请输入真实姓名"
class="custom-input"
:disabled="!canEdit"
/>
</ion-item>
</div>
@@ -220,6 +286,7 @@ async function handleSubmit() {
placeholder="请输入身份证号码"
class="custom-input"
:maxlength="18"
:disabled="!canEdit"
/>
</ion-item>
</div>
@@ -232,7 +299,8 @@ async function handleSubmit() {
</div>
<div
class="upload-box"
@click="showImageSourceOptions('front')"
:class="{ 'upload-disabled': !canEdit }"
@click="canEdit && showImageSourceOptions('front')"
>
<div v-if="!formData.fileId1" class="upload-placeholder">
<ion-icon :icon="cameraOutline" class="text-4xl text-[#999]" />
@@ -245,7 +313,7 @@ async function handleSubmit() {
</div>
<img
v-else
:src="formData.fileId1"
:src="image1"
alt="身份证正面"
class="uploaded-image"
>
@@ -260,7 +328,8 @@ async function handleSubmit() {
</div>
<div
class="upload-box"
@click="showImageSourceOptions('back')"
:class="{ 'upload-disabled': !canEdit }"
@click="canEdit && showImageSourceOptions('back')"
>
<div v-if="!formData.fileId2" class="upload-placeholder">
<ion-icon :icon="cameraOutline" class="text-4xl text-[#999]" />
@@ -273,7 +342,7 @@ async function handleSubmit() {
</div>
<img
v-else
:src="formData.fileId2"
:src="image2"
alt="身份证反面"
class="uploaded-image"
>
@@ -284,13 +353,14 @@ async function handleSubmit() {
<!-- 提交按钮 -->
<ion-button
v-if="canEdit"
expand="block"
class="submit-button"
:disabled="isSubmitting"
@click="handleSubmit"
>
<ion-spinner v-if="isSubmitting" name="crescent" />
<span v-else>提交认证</span>
<span v-else>{{ status === 'rejected' ? '重新提交' : '提交认证' }}</span>
</ion-button>
<!-- 注意事项 -->
@@ -369,6 +439,21 @@ async function handleSubmit() {
border-color: var(--ion-color-primary);
}
.upload-disabled {
opacity: 0.6;
cursor: not-allowed;
pointer-events: none;
}
.status-card {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 16px;
border-radius: 12px;
border: 1px solid;
}
.upload-placeholder {
display: flex;
flex-direction: column;

View File

@@ -1,8 +1,10 @@
<script lang='ts' setup>
import { toastController } from "@ionic/vue";
import { checkmarkCircleOutline, chevronForwardOutline, closeCircleOutline, keyOutline, lockClosedOutline, shieldCheckmarkOutline } from "ionicons/icons";
import { client, safeClient } from "@/api";
const router = useRouter();
const { data: status } = await safeClient(() => client.api.user.security["transaction-password"].status.get());
// 交易密码是否已启用
const hasPaymentPassword = ref(true);
@@ -82,15 +84,15 @@ async function handleTogglePaymentPassword() {
</div>
<div class="item-right">
<div class="flex items-center gap-2">
<div class="status-badge" :class="hasPaymentPassword ? 'enabled' : 'disabled'">
<div class="status-badge" :class="status?.enabled ? 'enabled' : 'disabled'">
<ion-icon
:icon="hasPaymentPassword ? checkmarkCircleOutline : closeCircleOutline"
:icon="status?.enabled ? checkmarkCircleOutline : closeCircleOutline"
class="text-sm"
/>
<span>{{ hasPaymentPassword ? '已启用' : '未启用' }}</span>
<span>{{ status?.enabled ? '已启用' : '未启用' }}</span>
</div>
<ion-toggle
:checked="hasPaymentPassword"
:checked="status?.enabled"
color="danger"
@ion-change="handleTogglePaymentPassword"
/>