feat:增加个人中心:20% 支持左侧的个人信息
parent
1662598488
commit
57c8d88bae
|
|
@ -1,2 +1 @@
|
||||||
export * from './auth';
|
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 { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||||
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
|
||||||
import { useWatermark } from '@vben/hooks';
|
import { useWatermark } from '@vben/hooks';
|
||||||
import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
|
import { BookOpenText, CircleHelp, MdiGithub, AntdProfileOutlined } from '@vben/icons';
|
||||||
import {
|
import {
|
||||||
BasicLayout,
|
BasicLayout,
|
||||||
LockScreen,
|
LockScreen,
|
||||||
|
|
@ -20,6 +20,7 @@ import { openWindow } from '@vben/utils';
|
||||||
import { $t } from '#/locales';
|
import { $t } from '#/locales';
|
||||||
import { useAuthStore } from '#/store';
|
import { useAuthStore } from '#/store';
|
||||||
import LoginForm from '#/views/_core/authentication/login.vue';
|
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||||
|
import { router } from '#/router';
|
||||||
|
|
||||||
const notifications = ref<NotificationItem[]>([
|
const notifications = ref<NotificationItem[]>([
|
||||||
{
|
{
|
||||||
|
|
@ -61,6 +62,13 @@ const showDot = computed(() =>
|
||||||
);
|
);
|
||||||
|
|
||||||
const menus = computed(() => [
|
const menus = computed(() => [
|
||||||
|
{
|
||||||
|
handler: () => {
|
||||||
|
router.push({ name: 'Profile' });
|
||||||
|
},
|
||||||
|
icon: AntdProfileOutlined,
|
||||||
|
text: $t('ui.widgets.profile'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
handler: () => {
|
handler: () => {
|
||||||
openWindow(VBEN_DOC_URL, {
|
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;
|
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(
|
export const MdiCheckboxMarkedCircleOutline = createIconifyIcon(
|
||||||
'mdi:checkbox-marked-circle-outline',
|
'mdi:checkbox-marked-circle-outline',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const AntdProfileOutlined = createIconifyIcon('ant-design:profile-outlined');
|
||||||
|
|
@ -77,6 +77,7 @@
|
||||||
},
|
},
|
||||||
"widgets": {
|
"widgets": {
|
||||||
"document": "Document",
|
"document": "Document",
|
||||||
|
"profile": "Profile",
|
||||||
"qa": "Q&A",
|
"qa": "Q&A",
|
||||||
"setting": "Settings",
|
"setting": "Settings",
|
||||||
"logoutTip": "Do you want to logout?",
|
"logoutTip": "Do you want to logout?",
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@
|
||||||
},
|
},
|
||||||
"widgets": {
|
"widgets": {
|
||||||
"document": "文档",
|
"document": "文档",
|
||||||
|
"profile": "个人中心",
|
||||||
"qa": "问题 & 帮助",
|
"qa": "问题 & 帮助",
|
||||||
"setting": "设置",
|
"setting": "设置",
|
||||||
"logoutTip": "是否退出登录?",
|
"logoutTip": "是否退出登录?",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue