From 9b6c321aaa76975dba1ff948ae95448cf32c496a Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 5 Mar 2025 18:46:19 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/vben5/apps/app-antd/src/store/auth.ts | 15 ++++++- .../@abp/account/src/api/useProfileApi.ts | 28 ++++++++++++ .../components/components/BasicSettings.vue | 45 ++++++++++++++++--- .../@abp/account/src/types/profile.ts | 5 +++ 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/apps/vben5/apps/app-antd/src/store/auth.ts b/apps/vben5/apps/app-antd/src/store/auth.ts index d6700622b..818c1f3d7 100644 --- a/apps/vben5/apps/app-antd/src/store/auth.ts +++ b/apps/vben5/apps/app-antd/src/store/auth.ts @@ -8,7 +8,12 @@ import { useRouter } from 'vue-router'; import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants'; import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; -import { useQrCodeLoginApi, useTokenApi, useUserInfoApi } from '@abp/account'; +import { + useProfileApi, + useQrCodeLoginApi, + useTokenApi, + useUserInfoApi, +} from '@abp/account'; import { Events, useAbpStore, useEventBus } from '@abp/core'; import { notification } from 'ant-design-vue'; import { defineStore } from 'pinia'; @@ -22,6 +27,7 @@ export const useAuthStore = defineStore('auth', () => { const { loginApi: qrcodeLoginApi } = useQrCodeLoginApi(); const { getUserInfoApi } = useUserInfoApi(); const { getConfigApi } = useAbpConfigApi(); + const { getPictureApi } = useProfileApi(); const accessStore = useAccessStore(); const userStore = useUserStore(); const abpStore = useAbpStore(); @@ -76,6 +82,7 @@ export const useAuthStore = defineStore('auth', () => { let userInfo: null | (UserInfo & { [key: string]: any }) = null; const userInfoRes = await getUserInfoApi(); const abpConfig = await getConfigApi(); + const picture = await getPictureApi(); userInfo = { userId: userInfoRes.sub ?? abpConfig.currentUser.id, username: userInfoRes.uniqueName ?? abpConfig.currentUser.userName, @@ -83,7 +90,7 @@ export const useAuthStore = defineStore('auth', () => { userInfoRes.name ?? abpConfig.currentUser.name ?? abpConfig.currentUser.userName, - avatar: userInfoRes.avatarUrl ?? userInfoRes.picture ?? '', + avatar: URL.createObjectURL(picture) ?? '', desc: userInfoRes.uniqueName ?? userInfoRes.name, email: userInfoRes.email ?? userInfoRes.email, emailVerified: @@ -148,6 +155,10 @@ export const useAuthStore = defineStore('auth', () => { } function $reset() { + const userInfo = userStore.userInfo; + if (userInfo?.avatar) { + URL.revokeObjectURL(userInfo?.avatar); + } loginLoading.value = false; } diff --git a/apps/vben5/packages/@abp/account/src/api/useProfileApi.ts b/apps/vben5/packages/@abp/account/src/api/useProfileApi.ts index c0351e2d6..9a13b0242 100644 --- a/apps/vben5/packages/@abp/account/src/api/useProfileApi.ts +++ b/apps/vben5/packages/@abp/account/src/api/useProfileApi.ts @@ -2,6 +2,7 @@ import type { AuthenticatorDto, AuthenticatorRecoveryCodeDto, ChangePasswordInput, + ChangePictureInput, ConfirmEmailInput, ProfileDto, SendEmailConfirmCodeDto, @@ -48,6 +49,31 @@ export function useProfileApi() { }); } + /** + * 修改头像 + * @param input 参数 + */ + function changePictureApi(input: ChangePictureInput): Promise { + return request('/api/account/my-profile/picture', { + data: input, + headers: { + 'Content-Type': 'multipart/form-data', + }, + method: 'POST', + }); + } + + /** + * 获取头像 + * @returns 头像文件流 + */ + function getPictureApi(): Promise { + return request('/api/account/my-profile/picture', { + method: 'GET', + responseType: 'blob', + }); + } + /** * 获取二次认证启用状态 */ @@ -132,10 +158,12 @@ export function useProfileApi() { return { cancel, changePasswordApi, + changePictureApi, changeTwoFactorEnabledApi, confirmEmailApi, getApi, getAuthenticatorApi, + getPictureApi, getTwoFactorEnabledApi, resetAuthenticatorApi, sendEmailConfirmLinkApi, diff --git a/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue b/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue index eec631aa4..a850098a9 100644 --- a/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue +++ b/apps/vben5/packages/@abp/account/src/components/components/BasicSettings.vue @@ -12,7 +12,17 @@ import { useUserStore } from '@vben/stores'; import { useSettings } from '@abp/core'; import { UploadOutlined } from '@ant-design/icons-vue'; -import { Avatar, Button, Card, Form, Input, Upload } from 'ant-design-vue'; +import { + Avatar, + Button, + Card, + Form, + Input, + message, + Upload, +} from 'ant-design-vue'; + +import { useProfileApi } from '../../api/useProfileApi'; const props = defineProps<{ profile: ProfileDto; @@ -23,19 +33,40 @@ const emits = defineEmits<{ const FormItem = Form.Item; const formModel = ref({} as ProfileDto); +const pictureState = ref<{ + file?: any; + uploading: boolean; +}>({ + uploading: false, +}); const userStore = useUserStore(); const { isTrue } = useSettings(); +const { changePictureApi, getPictureApi } = useProfileApi(); const avatar = computed(() => { return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar; }); -function onAvatarChange(_param: UploadChangeParam) { - // TODO: oss模块集成后完成 - console.warn('等待oss模块集成完成...'); +async function onAvatarChange(_param: UploadChangeParam) { + pictureState.value.uploading = true; + try { + await changePictureApi({ + file: pictureState.value.file, + }); + if (userStore.userInfo?.avatar) { + URL.revokeObjectURL(userStore.userInfo.avatar); + } + const picture = await getPictureApi(); + userStore.$patch((state) => { + state.userInfo && (state.userInfo.avatar = URL.createObjectURL(picture)); + }); + message.success($t('AbpUi.SavedSuccessfully')); + } finally { + pictureState.value.uploading = false; + } } -function onBeforeUpload(_file: FileType) { - console.warn('等待oss模块集成完成...'); +function onBeforeUpload(file: FileType) { + pictureState.value.file = file; return false; } function onSubmit() { @@ -110,7 +141,7 @@ watchEffect(() => { name="file" @change="onAvatarChange" > - diff --git a/apps/vben5/packages/@abp/account/src/types/profile.ts b/apps/vben5/packages/@abp/account/src/types/profile.ts index e614f818d..f5a4d57ab 100644 --- a/apps/vben5/packages/@abp/account/src/types/profile.ts +++ b/apps/vben5/packages/@abp/account/src/types/profile.ts @@ -36,6 +36,10 @@ interface ChangePasswordInput { newPassword: string; } +interface ChangePictureInput { + file: File; +} + interface TwoFactorEnabledDto { /** 是否启用二次认证 */ enabled: boolean; @@ -69,6 +73,7 @@ export type { AuthenticatorDto, AuthenticatorRecoveryCodeDto, ChangePasswordInput, + ChangePictureInput, ConfirmEmailInput, ProfileDto, SendEmailConfirmCodeDto,