From 9710c80a1b3d69d3aae4c91179da991c0b21e421 Mon Sep 17 00:00:00 2001 From: Seven Date: Tue, 20 Jan 2026 01:31:26 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=BF=94=E5=9B=9E=E6=96=87=E4=BB=B6?= =?UTF-8?q?ID=E5=92=8C=E5=85=AC=E5=85=B1URL=EF=BC=9B=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=AE=9E=E5=90=8D=E8=AE=A4=E8=AF=81=E8=A1=A8=E5=8D=95=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 8 -------- src/utils/aws/s3.ts | 12 ++++++++---- src/views/real_name/index.vue | 34 +++++++++++++++------------------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/components.d.ts b/components.d.ts index 22b8917..3ebabaf 100644 --- a/components.d.ts +++ b/components.d.ts @@ -21,7 +21,6 @@ declare module 'vue' { IonButtons: typeof import('@ionic/vue')['IonButtons'] IonCheckbox: typeof import('@ionic/vue')['IonCheckbox'] IonContent: typeof import('@ionic/vue')['IonContent'] - IonDatetime: typeof import('@ionic/vue')['IonDatetime'] IonHeader: typeof import('@ionic/vue')['IonHeader'] IonIcon: typeof import('@ionic/vue')['IonIcon'] IonInfiniteScroll: typeof import('@ionic/vue')['IonInfiniteScroll'] @@ -29,15 +28,12 @@ declare module 'vue' { IonInput: typeof import('@ionic/vue')['IonInput'] IonItem: typeof import('@ionic/vue')['IonItem'] IonLabel: typeof import('@ionic/vue')['IonLabel'] - IonList: typeof import('@ionic/vue')['IonList'] IonPage: typeof import('@ionic/vue')['IonPage'] IonRefresher: typeof import('@ionic/vue')['IonRefresher'] IonRefresherContent: typeof import('@ionic/vue')['IonRefresherContent'] IonRouterOutlet: typeof import('@ionic/vue')['IonRouterOutlet'] IonSegment: typeof import('@ionic/vue')['IonSegment'] IonSegmentButton: typeof import('@ionic/vue')['IonSegmentButton'] - IonSelect: typeof import('@ionic/vue')['IonSelect'] - IonSelectOption: typeof import('@ionic/vue')['IonSelectOption'] IonSpinner: typeof import('@ionic/vue')['IonSpinner'] IonTabBar: typeof import('@ionic/vue')['IonTabBar'] IonTabButton: typeof import('@ionic/vue')['IonTabButton'] @@ -63,7 +59,6 @@ declare global { const IonButtons: typeof import('@ionic/vue')['IonButtons'] const IonCheckbox: typeof import('@ionic/vue')['IonCheckbox'] const IonContent: typeof import('@ionic/vue')['IonContent'] - const IonDatetime: typeof import('@ionic/vue')['IonDatetime'] const IonHeader: typeof import('@ionic/vue')['IonHeader'] const IonIcon: typeof import('@ionic/vue')['IonIcon'] const IonInfiniteScroll: typeof import('@ionic/vue')['IonInfiniteScroll'] @@ -71,15 +66,12 @@ declare global { const IonInput: typeof import('@ionic/vue')['IonInput'] const IonItem: typeof import('@ionic/vue')['IonItem'] const IonLabel: typeof import('@ionic/vue')['IonLabel'] - const IonList: typeof import('@ionic/vue')['IonList'] const IonPage: typeof import('@ionic/vue')['IonPage'] const IonRefresher: typeof import('@ionic/vue')['IonRefresher'] const IonRefresherContent: typeof import('@ionic/vue')['IonRefresherContent'] const IonRouterOutlet: typeof import('@ionic/vue')['IonRouterOutlet'] const IonSegment: typeof import('@ionic/vue')['IonSegment'] const IonSegmentButton: typeof import('@ionic/vue')['IonSegmentButton'] - const IonSelect: typeof import('@ionic/vue')['IonSelect'] - const IonSelectOption: typeof import('@ionic/vue')['IonSelectOption'] const IonSpinner: typeof import('@ionic/vue')['IonSpinner'] const IonTabBar: typeof import('@ionic/vue')['IonTabBar'] const IonTabButton: typeof import('@ionic/vue')['IonTabButton'] diff --git a/src/utils/aws/s3.ts b/src/utils/aws/s3.ts index 140a05c..2e20d8a 100644 --- a/src/utils/aws/s3.ts +++ b/src/utils/aws/s3.ts @@ -9,21 +9,22 @@ interface UploadOptions { export type UploadFetchOptions = TreatyBody; -export async function uploadToS3(file: File, options: UploadOptions): Promise { +export async function uploadToS3(file: File, options: UploadOptions): Promise<{ fileId: string; publicUrl: string }> { const { onProgress, signal, fetchOptions } = options; // 1. 获取预签名 URL const { data, error } = await safeClient(client.api.file_storage.upload_url.post({ ...fetchOptions, + accessControl: "public", })); if (error.value || !data.value) { throw new Error("获取上传 URL 失败"); } - const { fileId, uploadUrl, method, headers } = toRefs(data.value); + const { fileId, uploadUrl, method, headers, publicUrl } = toRefs(data.value); // 2. 上传文件到 S3 - return new Promise((resolve, reject) => { + return new Promise<{ fileId: string; publicUrl: string }>((resolve, reject) => { const xhr = new XMLHttpRequest(); // 监听上传进度 @@ -37,7 +38,10 @@ export async function uploadToS3(file: File, options: UploadOptions): Promise { if (xhr.status >= 200 && xhr.status < 300) { - resolve(fileId.value); + resolve({ + fileId: fileId.value, + publicUrl: publicUrl!.value!, + }); } else { reject(new Error(`上传失败: ${xhr.status} ${xhr.statusText}`)); diff --git a/src/views/real_name/index.vue b/src/views/real_name/index.vue index d210916..e2758b1 100644 --- a/src/views/real_name/index.vue +++ b/src/views/real_name/index.vue @@ -8,7 +8,6 @@ import { client, safeClient } from "@/api"; const router = useRouter(); interface RealNameFormData { - kycMethod: "id_card"; documentName: string; documentNumber: string; fileId1: string; @@ -16,14 +15,11 @@ interface RealNameFormData { } const formData = ref({ - kycMethod: "id_card", documentName: "", documentNumber: "", fileId1: "", fileId2: "", }); -const image1 = ref(""); -const image2 = ref(""); const isSubmitting = ref(false); const status = ref<"unverified" | "pending" | "approved" | "rejected">("unverified"); @@ -123,7 +119,7 @@ async function selectFromGallery(type: "front" | "back") { "upload.jpeg", { type: "image/jpeg" }, ); - const fileId = await uploadToS3(file, { + const { publicUrl } = await uploadToS3(file, { fetchOptions: { fileName: `idCard_${Date.now()}.jpeg`, fileSize: image.dataUrl?.length || 0, @@ -133,12 +129,10 @@ async function selectFromGallery(type: "front" | "back") { if (image.dataUrl) { if (type === "front") { - formData.value.fileId1 = fileId; - image1.value = image.dataUrl; + formData.value.fileId1 = publicUrl; } else { - formData.value.fileId2 = fileId; - image2.value = image.dataUrl; + formData.value.fileId2 = publicUrl; } } } @@ -181,9 +175,16 @@ async function handleSubmit() { isSubmitting.value = true; try { - await safeClient(client.api.kyc({ kycMethod: "id_card" }).post({ - ...formData.value, - })); + if (status.value !== "unverified") { + await safeClient(client.api.kyc({ kycMethod: "id_card" }).put({ + ...formData.value, + })); + } + else { + await safeClient(client.api.kyc({ kycMethod: "id_card" }).post({ + ...formData.value, + })); + } await showToast("实名认证提交成功,等待审核", "success"); router.back(); @@ -205,11 +206,6 @@ onMounted(async () => { 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 || ""; }); @@ -313,7 +309,7 @@ onMounted(async () => { 身份证正面 @@ -342,7 +338,7 @@ onMounted(async () => { 身份证反面