feat:增加个人中心:20% 支持左侧的个人信息
parent
1662598488
commit
57c8d88bae
|
@ -1,2 +1 @@
|
|||
export * from './auth';
|
||||
export * from './user';
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import type { UserInfo } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
export async function getUserInfoApi() {
|
||||
return requestClient.get<UserInfo>('/system/user/profile/get');
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace SystemUserProfileApi {
|
||||
/** 社交用户信息 */
|
||||
export interface SocialUser {
|
||||
type: number;
|
||||
openid: string;
|
||||
}
|
||||
|
||||
/** 用户个人中心信息 */
|
||||
export interface UserProfileRespVO {
|
||||
id: number;
|
||||
username: string;
|
||||
nickname: string;
|
||||
email?: string;
|
||||
mobile?: string;
|
||||
sex?: number;
|
||||
avatar?: string;
|
||||
loginIp: string;
|
||||
loginDate: string;
|
||||
createTime: string;
|
||||
roles: any[];
|
||||
dept: any;
|
||||
posts: any[];
|
||||
socialUsers: SocialUser[];
|
||||
}
|
||||
|
||||
/** 更新密码请求 */
|
||||
export interface UpdatePasswordReqVO {
|
||||
oldPassword: string;
|
||||
newPassword: string;
|
||||
}
|
||||
|
||||
/** 更新个人信息请求 */
|
||||
export interface UpdateProfileReqVO {
|
||||
nickname: string;
|
||||
email?: string;
|
||||
mobile?: string;
|
||||
sex?: number;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取登录用户信息 */
|
||||
export function getUserProfile() {
|
||||
return requestClient.get<SystemUserProfileApi.UserProfileRespVO>('/system/user/profile/get');
|
||||
}
|
||||
|
||||
/** 修改用户个人信息 */
|
||||
export function updateUserProfile(data: SystemUserProfileApi.UpdateProfileReqVO) {
|
||||
return requestClient.put('/system/user/profile/update', data);
|
||||
}
|
||||
|
||||
/** 修改用户个人密码 */
|
||||
export function updateUserPassword(data: SystemUserProfileApi.UpdatePasswordReqVO) {
|
||||
return requestClient.put('/system/user/profile/update-password', data);
|
||||
}
|
||||
|
||||
/** 上传用户个人头像 */
|
||||
export function updateUserAvatar(file: File) {
|
||||
const formData = new FormData();
|
||||
formData.append('avatarFile', file);
|
||||
return requestClient.put('/system/user/profile/update-avatar', formData);
|
||||
}
|
|
@ -6,7 +6,7 @@ import { computed, ref, watch } from 'vue';
|
|||
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
||||
import { useWatermark } from '@vben/hooks';
|
||||
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
|
||||
import { BookOpenText, CircleHelp, MdiGithub, AntdProfileOutlined } from '@vben/icons';
|
||||
import {
|
||||
BasicLayout,
|
||||
LockScreen,
|
||||
|
@ -20,6 +20,7 @@ import { openWindow } from '@vben/utils';
|
|||
import { $t } from '#/locales';
|
||||
import { useAuthStore } from '#/store';
|
||||
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||
import { router } from '#/router';
|
||||
|
||||
const notifications = ref<NotificationItem[]>([
|
||||
{
|
||||
|
@ -61,6 +62,13 @@ const showDot = computed(() =>
|
|||
);
|
||||
|
||||
const menus = computed(() => [
|
||||
{
|
||||
handler: () => {
|
||||
router.push({ name: 'Profile' });
|
||||
},
|
||||
icon: AntdProfileOutlined,
|
||||
text: $t('ui.widgets.profile'),
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
openWindow(VBEN_DOC_URL, {
|
||||
|
|
|
@ -33,6 +33,16 @@ const routes: RouteRecordRaw[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Profile',
|
||||
path: '/profile',
|
||||
component: () => import('#/views/_core/profile/index.vue'),
|
||||
meta: {
|
||||
icon: 'ant-design:profile-outlined',
|
||||
title: $t('ui.widgets.profile'),
|
||||
hideInMenu: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<script setup lang="ts">
|
||||
import type { SystemUserProfileApi } from '#/api/system/user/profile';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { getUserProfile } from '#/api/system/user/profile';
|
||||
import { useAuthStore } from '#/store';
|
||||
import { Card, Tabs } from 'ant-design-vue';
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import ProfileUser from './modules/profile-user.vue';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const activeName = ref('basicInfo');
|
||||
|
||||
/** 加载个人信息 */
|
||||
const profile = ref<SystemUserProfileApi.UserProfileRespVO>();
|
||||
async function loadProfile() {
|
||||
profile.value = await getUserProfile();
|
||||
}
|
||||
|
||||
/** 刷新个人信息 */
|
||||
async function refreshProfile() {
|
||||
// 加载个人信息
|
||||
await loadProfile();
|
||||
|
||||
// 更新 store
|
||||
await authStore.fetchUserInfo();
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(loadProfile);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<div class="flex">
|
||||
<!-- 左侧 个人信息 -->
|
||||
<Card class="w-2/5" title="个人信息">
|
||||
<ProfileUser :profile="profile" @success="refreshProfile" />
|
||||
</Card>
|
||||
|
||||
<!-- 右侧 标签页 -->
|
||||
<Card class="ml-3 w-3/5">
|
||||
<Tabs v-model:active-key="activeName" class="-mt-4">
|
||||
<Tabs.TabPane key="basicInfo" tab="基本信息">
|
||||
<!-- <BaseSetting :profile="profile" /> -->
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="resetPwd" tab="修改密码">
|
||||
<!-- <ResetPwd /> -->
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="userSocial" tab="社交信息">
|
||||
<!-- <UserSocial :profile="profile" /> -->
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
</Page>
|
||||
</template>
|
|
@ -0,0 +1,116 @@
|
|||
<script setup lang="ts">
|
||||
import type { SystemUserProfileApi } from '#/api/system/user/profile';
|
||||
|
||||
import { Descriptions, DescriptionsItem, Tooltip } from 'ant-design-vue';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { computed } from 'vue';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { updateUserAvatar } from '#/api/system/user/profile';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
// import { CropperAvatar } from '#/components/cropper';
|
||||
|
||||
const props = defineProps<{ profile?: SystemUserProfileApi.UserProfileRespVO }>();
|
||||
|
||||
defineEmits<{
|
||||
// 头像上传完毕
|
||||
success: [];
|
||||
}>();
|
||||
|
||||
const avatar = computed(
|
||||
() => props.profile?.avatar || preferences.app.defaultAvatar,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="profile">
|
||||
<div class="flex flex-col items-center gap-[20px]">
|
||||
<Tooltip title="点击上传头像">
|
||||
<!-- TODO 芋艿:待实现 -->
|
||||
<CropperAvatar
|
||||
:show-btn="false"
|
||||
:upload-api="updateUserAvatar"
|
||||
:value="avatar"
|
||||
width="120"
|
||||
@change=""
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<Descriptions :column="2">
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:user-outlined" class="mr-1" />
|
||||
用户账号
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.username }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:user-switch-outlined" class="mr-1" />
|
||||
所属角色
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.roles.map(role => role.name).join(',') }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:phone-outlined" class="mr-1" />
|
||||
手机号码
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.mobile }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:mail-outlined" class="mr-1" />
|
||||
用户邮箱
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.email }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:team-outlined" class="mr-1" />
|
||||
所属部门
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.dept?.name }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:usergroup-add-outlined" class="mr-1" />
|
||||
所属岗位
|
||||
</div>
|
||||
</template>
|
||||
{{ profile.posts.map(post => post.name).join(',') }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:clock-circle-outlined" class="mr-1" />
|
||||
创建时间
|
||||
</div>
|
||||
</template>
|
||||
{{ formatDateTime(profile.createTime) }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<IconifyIcon icon="ant-design:login-outlined" class="mr-1" />
|
||||
登录时间
|
||||
</div>
|
||||
</template>
|
||||
{{ formatDateTime(profile.loginDate) }}
|
||||
</DescriptionsItem>
|
||||
</Descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -15,3 +15,5 @@ export const AntdDingTalk = createIconifyIcon('ant-design:dingtalk')
|
|||
export const MdiCheckboxMarkedCircleOutline = createIconifyIcon(
|
||||
'mdi:checkbox-marked-circle-outline',
|
||||
);
|
||||
|
||||
export const AntdProfileOutlined = createIconifyIcon('ant-design:profile-outlined');
|
|
@ -77,6 +77,7 @@
|
|||
},
|
||||
"widgets": {
|
||||
"document": "Document",
|
||||
"profile": "Profile",
|
||||
"qa": "Q&A",
|
||||
"setting": "Settings",
|
||||
"logoutTip": "Do you want to logout?",
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
},
|
||||
"widgets": {
|
||||
"document": "文档",
|
||||
"profile": "个人中心",
|
||||
"qa": "问题 & 帮助",
|
||||
"setting": "设置",
|
||||
"logoutTip": "是否退出登录?",
|
||||
|
|
Loading…
Reference in New Issue