diff --git a/apps/web-antdv-next/src/api/core/index.ts b/apps/web-antdv-next/src/api/core/index.ts index ba64a03..14c6521 100644 --- a/apps/web-antdv-next/src/api/core/index.ts +++ b/apps/web-antdv-next/src/api/core/index.ts @@ -2,4 +2,5 @@ export * from './auth'; export * from './dict'; export * from './menu'; export * from './timezone'; +export * from './upload'; export * from './user'; diff --git a/apps/web-antdv-next/src/api/core/upload.ts b/apps/web-antdv-next/src/api/core/upload.ts new file mode 100644 index 0000000..4318757 --- /dev/null +++ b/apps/web-antdv-next/src/api/core/upload.ts @@ -0,0 +1,41 @@ +import { requestClient } from '#/api/request'; + +export interface UploadFileOptions { + bucketName?: string; + objectName?: string; +} + +export interface UploadFileResult { + bucketName: string; + etag: string; + mimeType: string; + objectName: string; + size: number; + url: string; +} + +/** + * 统一文件上传入口,业务侧只传文件和可选对象名。 + */ +export async function uploadFileApi( + file: Blob | File, + options: UploadFileOptions = {}, +) { + return requestClient.upload('/minio/upload', { + ...options, + file, + }); +} + +export function createUploadedFileDownloadUrl(file: UploadFileResult) { + const baseUrl = requestClient.getBaseUrl() || ''; + const params = new URLSearchParams({ + objectName: file.objectName, + }); + + if (file.bucketName) { + params.set('bucketName', file.bucketName); + } + + return `${baseUrl.replace(/\/$/, '')}/minio/download?${params.toString()}`; +} diff --git a/apps/web-antdv-next/src/api/core/user.ts b/apps/web-antdv-next/src/api/core/user.ts index 7e28ea8..2713b8e 100644 --- a/apps/web-antdv-next/src/api/core/user.ts +++ b/apps/web-antdv-next/src/api/core/user.ts @@ -2,9 +2,24 @@ import type { UserInfo } from '@vben/types'; import { requestClient } from '#/api/request'; +export interface CurrentUserProfileInput { + avatar?: string; + homePath?: string; + realName?: string; +} + /** * 获取用户信息 */ export async function getUserInfoApi() { return requestClient.get('/user/info'); } + +/** + * 更新当前用户基础资料 + */ +export async function updateCurrentUserProfileApi( + data: CurrentUserProfileInput, +) { + return requestClient.put('/user/profile', data); +} diff --git a/apps/web-antdv-next/src/layouts/basic.vue b/apps/web-antdv-next/src/layouts/basic.vue index 74b0854..4cb7d6d 100644 --- a/apps/web-antdv-next/src/layouts/basic.vue +++ b/apps/web-antdv-next/src/layouts/basic.vue @@ -32,7 +32,7 @@ const router = useRouter(); const { destroyWatermark, updateWatermark } = useWatermark(); const avatar = computed(() => { - return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar; + return userStore.userInfo?.avatar || preferences.app.defaultAvatar; }); const userDropdownMenus = computed(() => [ diff --git a/apps/web-antdv-next/src/views/_core/profile/base-setting.vue b/apps/web-antdv-next/src/views/_core/profile/base-setting.vue index aa8a4c2..4713ed2 100644 --- a/apps/web-antdv-next/src/views/_core/profile/base-setting.vue +++ b/apps/web-antdv-next/src/views/_core/profile/base-setting.vue @@ -1,65 +1,70 @@ diff --git a/apps/web-antdv-next/src/views/_core/profile/index.vue b/apps/web-antdv-next/src/views/_core/profile/index.vue index 8740894..31e4e2d 100644 --- a/apps/web-antdv-next/src/views/_core/profile/index.vue +++ b/apps/web-antdv-next/src/views/_core/profile/index.vue @@ -1,49 +1,220 @@ diff --git a/packages/effects/common-ui/src/ui/profile/profile.vue b/packages/effects/common-ui/src/ui/profile/profile.vue index 5ab39e6..3afe2cd 100644 --- a/packages/effects/common-ui/src/ui/profile/profile.vue +++ b/packages/effects/common-ui/src/ui/profile/profile.vue @@ -10,6 +10,7 @@ import { TabsTrigger, VbenAvatar, } from '@vben-core/shadcn-ui'; +import { cn } from '@vben-core/shared/utils'; import { Page } from '../../components'; @@ -17,34 +18,58 @@ defineOptions({ name: 'ProfileUI', }); -withDefaults(defineProps(), { +const props = withDefaults(defineProps(), { + avatarEditable: false, title: '关于项目', tabs: () => [], }); +const emit = defineEmits<{ + avatarClick: []; +}>(); + const tabsValue = defineModel('modelValue'); + +function handleAvatarClick() { + if (props.avatarEditable) { + emit('avatarClick'); + } +}