From c6d0c57bf9c32e990c986dc7370dc53c34e51e7d Mon Sep 17 00:00:00 2001 From: Wenjie Zhang Date: Mon, 1 Jun 2026 22:29:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=A4=B4=E5=83=8F=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/AccountSettingsComponent.vue | 11 +-- web/src/components/UserInfoComponent.vue | 12 +-- .../components/UserManagementComponent.vue | 18 +---- .../dashboard/FeedbackModalComponent.vue | 5 +- web/src/utils/__tests__/pixelAvatar.test.js | 43 +++++++++++ web/src/utils/pixelAvatar.js | 74 +++++++++++++++++++ 6 files changed, 130 insertions(+), 33 deletions(-) create mode 100644 web/src/utils/__tests__/pixelAvatar.test.js create mode 100644 web/src/utils/pixelAvatar.js diff --git a/web/src/components/AccountSettingsComponent.vue b/web/src/components/AccountSettingsComponent.vue index a0beea2e..1760042c 100644 --- a/web/src/components/AccountSettingsComponent.vue +++ b/web/src/components/AccountSettingsComponent.vue @@ -106,8 +106,7 @@ import { message } from 'ant-design-vue' import { CircleUser, RefreshCw, Upload } from 'lucide-vue-next' import ApiKeyManagementComponent from '@/components/ApiKeyManagementComponent.vue' import { useUserStore } from '@/stores/user' - -const DEFAULT_AVATAR_URL = 'https://xerrors.oss-cn-shanghai.aliyuncs.com/github/default.jpeg' +import { generatePixelAvatar } from '@/utils/pixelAvatar' const userStore = useUserStore() const avatarUploading = ref(false) @@ -117,7 +116,6 @@ const editingField = ref('') const usernameInput = ref(null) const phoneInput = ref(null) const avatarLoadFailed = ref(false) -const defaultAvatarLoadFailed = ref(false) const profileDraft = reactive({ username: '', phone_number: '' @@ -125,8 +123,7 @@ const profileDraft = reactive({ const avatarSrc = computed(() => { if (userStore.avatar && !avatarLoadFailed.value) return userStore.avatar - if (!defaultAvatarLoadFailed.value) return DEFAULT_AVATAR_URL - return '' + return generatePixelAvatar(userStore.uid) }) const userRoleText = computed(() => { @@ -150,9 +147,7 @@ const syncProfileDraft = () => { const handleAvatarError = () => { if (userStore.avatar && !avatarLoadFailed.value) { avatarLoadFailed.value = true - return false } - defaultAvatarLoadFailed.value = true return false } @@ -276,7 +271,6 @@ const handleAvatarChange = async (info) => { avatarUploading.value = true await userStore.uploadAvatar(info.file.originFileObj || info.file) avatarLoadFailed.value = false - defaultAvatarLoadFailed.value = false message.success('头像上传成功!') } catch (error) { console.error('头像上传失败:', error) @@ -290,7 +284,6 @@ watch( () => userStore.avatar, () => { avatarLoadFailed.value = false - defaultAvatarLoadFailed.value = false } ) diff --git a/web/src/components/UserInfoComponent.vue b/web/src/components/UserInfoComponent.vue index d6eb8d3a..2064fa34 100644 --- a/web/src/components/UserInfoComponent.vue +++ b/web/src/components/UserInfoComponent.vue @@ -74,14 +74,13 @@ import DebugComponent from '@/components/DebugComponent.vue' import { message } from 'ant-design-vue' import { CircleUser, BookOpen, Sun, Moon, LogOut, Settings, Terminal } from 'lucide-vue-next' import { useThemeStore } from '@/stores/theme' +import { generatePixelAvatar } from '@/utils/pixelAvatar' const router = useRouter() const userStore = useUserStore() const themeStore = useThemeStore() const slots = useSlots() -const DEFAULT_AVATAR_URL = 'https://xerrors.oss-cn-shanghai.aliyuncs.com/github/default.jpeg' - // 调试面板状态 const showDebug = ref(false) @@ -89,28 +88,23 @@ const showDebug = ref(false) const { openSettingsModal } = inject('settingsModal', {}) const avatarLoadFailed = ref(false) -const defaultAvatarLoadFailed = ref(false) -// 用户头像不可用时回退到公共默认头像;默认头像异常时再显示图标占位。 +// 用户头像不可用时回退到基于 UID 生成的本地像素头像。 const avatarSrc = computed(() => { if (userStore.avatar && !avatarLoadFailed.value) return userStore.avatar - if (!defaultAvatarLoadFailed.value) return DEFAULT_AVATAR_URL - return '' + return generatePixelAvatar(userStore.uid) }) const handleAvatarError = () => { if (userStore.avatar && !avatarLoadFailed.value) { avatarLoadFailed.value = true - return } - defaultAvatarLoadFailed.value = true } watch( () => userStore.avatar, () => { avatarLoadFailed.value = false - defaultAvatarLoadFailed.value = false } ) diff --git a/web/src/components/UserManagementComponent.vue b/web/src/components/UserManagementComponent.vue index 5a18d088..3c81b1fa 100644 --- a/web/src/components/UserManagementComponent.vue +++ b/web/src/components/UserManagementComponent.vue @@ -73,15 +73,7 @@