From de4a4ac50e46289573a3d7231a96b956cd8e98e7 Mon Sep 17 00:00:00 2001 From: yml Date: Mon, 25 May 2026 09:28:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E7=AB=AF=E4=B8=8A=E4=BC=A0=E6=9C=AC=E5=9C=B0=E7=85=A7=E7=89=87?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E8=87=AA=E5=AE=9A=E4=B9=89=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E5=85=81=E8=AE=B8=E5=85=AC=E5=BC=80=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E5=A4=B4=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/modules/file/handler.go | 2 +- backend/internal/modules/file/service.go | 2 +- .../src/views/mobile/MobileProfileView.vue | 122 +++++++++++++++++- 3 files changed, 117 insertions(+), 9 deletions(-) diff --git a/backend/internal/modules/file/handler.go b/backend/internal/modules/file/handler.go index 7c9e1a1..92df9ab 100644 --- a/backend/internal/modules/file/handler.go +++ b/backend/internal/modules/file/handler.go @@ -65,7 +65,7 @@ func (h *Handler) writeObject(c *gin.Context, publicOnly bool) { response.BadRequest(c, "文件 key 不正确") return } - if publicOnly && !strings.HasPrefix(key, "home-banner/") { + if publicOnly && !strings.HasPrefix(key, "home-banner/") && !strings.HasPrefix(key, "avatar/") { response.Error(c, http.StatusNotFound, "not_found", "文件不存在或暂不可访问") return } diff --git a/backend/internal/modules/file/service.go b/backend/internal/modules/file/service.go index 52292ec..a388da1 100644 --- a/backend/internal/modules/file/service.go +++ b/backend/internal/modules/file/service.go @@ -50,7 +50,7 @@ func (s *Service) Upload(req uploadRequest) (*UploadDTO, error) { return nil, err } fileURL := "/api/files/object?key=" + url.QueryEscape(key) - if scene == "home-banner" { + if scene == "home-banner" || scene == "avatar" { fileURL = "/api/public/files/object?key=" + url.QueryEscape(key) } return &UploadDTO{ diff --git a/frontend/src/views/mobile/MobileProfileView.vue b/frontend/src/views/mobile/MobileProfileView.vue index a09705a..988e20f 100644 --- a/frontend/src/views/mobile/MobileProfileView.vue +++ b/frontend/src/views/mobile/MobileProfileView.vue @@ -7,6 +7,7 @@ import MobileBottomNav from "@/components/MobileBottomNav.vue"; import { fetchWalletBalance, fetchWalletLedger, type WalletLedger } from "@/api/wallet"; import { formatDateMinute } from "@/utils/time"; +import { uploadFile } from "@/api/files"; const session = useSessionStore(); const router = useRouter(); @@ -22,6 +23,38 @@ const profileForm = reactive({ avatar_url: "", }); +const avatarFileInput = ref(null); +const uploadingAvatar = ref(false); + +function triggerAvatarUpload() { + avatarFileInput.value?.click(); +} + +async function handleAvatarFileChange(event: Event) { + const input = event.target as HTMLInputElement; + const file = input.files?.[0]; + input.value = ""; + if (!file) return; + + uploadingAvatar.value = true; + const toast = showToast({ + type: "loading", + message: "上传中...", + forbidClick: true, + duration: 0, + }); + + try { + const uploaded = await uploadFile(file, "avatar"); + profileForm.avatar_url = uploaded.url; + showToast({ message: "上传成功", icon: "passed" }); + } catch (error) { + showToast({ message: readError(error, "上传失败"), icon: "cross" }); + } finally { + uploadingAvatar.value = false; + } +} + // 收支明细列表 const showLedgers = ref(false); const loadingLedgers = ref(false); @@ -191,6 +224,14 @@ const maskedPhone = computed(() => : "登录后查看手机号" ); const avatarText = computed(() => displayName.value.slice(0, 1)); + +function resolveAvatarURL(url: string | undefined | null) { + if (!url) return ""; + if (url.includes("/api/files/object?key=avatar/")) { + return url.replace("/api/files/object", "/api/public/files/object"); + } + return url; +}