feat(@vben/common-ui): 添加个人中心功能- 新增个人中心页面,包含用户信息展示和修改、密码修改等功能

This commit is contained in:
Kven 2025-05-29 16:44:02 +08:00
parent 5026ae0bbd
commit d3c25dd899
8 changed files with 262 additions and 30 deletions

View File

@ -93,6 +93,14 @@ export namespace UserApi {
permissions?: string[] | undefined;
authorities?: string[];
}
export interface UserUpdateRecord {
username?: string;
email?: string;
phone?: string;
address?: string;
avatar?: string;
}
}
/**
@ -147,7 +155,7 @@ export function updateUser(id: any, data: UserApi.UserRecord) {
return requestClient.patch<UserApi.Res>(`/rest/user/${id}`, data);
}
export function selfUpdate(data: UserApi.User) {
export function selfUpdate(data: UserApi.UserUpdateRecord) {
return requestClient.patch<UserApi.Res>(`/rest/user/self`, data);
}

View File

@ -0,0 +1,19 @@
import type { RouteRecordRaw } from 'vue-router';
import { MaterPerson } from '@vben/icons';
const routes: RouteRecordRaw[] = [
{
name: 'Person',
path: '/person',
component: () => import('#/views/person/list.vue'),
meta: {
icon: MaterPerson,
title: '个人',
order: 5,
authority: ['dashboard'],
},
},
];
export default routes;

View File

@ -0,0 +1,47 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Card, Tabs } from 'ant-design-vue';
import { getUserInfo } from '#/api';
import BasicForm from '#/views/person/modules/basic-form.vue';
import PasswordForm from '#/views/person/modules/password-form.vue';
import UserPanel from '#/views/person/modules/user-panel.vue';
const userInfo = ref();
const permissions = ref();
getUserInfo().then((res) => {
userInfo.value = res.user;
permissions.value = res.permissions;
});
</script>
<template>
<div class="flex flex-col gap-4 p-4" style="height: 75vh">
<Card>
<UserPanel :user-info="userInfo" :permissions="permissions" />
</Card>
<Card>
<!-- 使用 Tabs 切换组件 -->
<Tabs default-active-key="BasicForm" class="px-4">
<Tabs.TabPane key="BasicForm" tab="用户信息">
<BasicForm :user-info="userInfo" />
</Tabs.TabPane>
<Tabs.TabPane key="PasswordForm" tab="修改密码">
<PasswordForm />
</Tabs.TabPane>
</Tabs>
</Card>
</div>
</template>
<style scoped>
:deep(.ant-card-body) {
padding: 0;
}
:deep(.ant-tabs-content) {
padding: 16px;
}
</style>

View File

@ -0,0 +1,67 @@
<script setup lang="ts">
import { toRefs } from 'vue';
import { Button, Form, Input, notification } from 'ant-design-vue';
import { selfUpdate } from '#/api';
const props = defineProps({
//
userInfo: {
type: Object,
default: () => ({
username: '',
email: '',
phone: '',
address: '',
}),
},
});
const { userInfo } = toRefs(props);
//
const handleSubmit = async () => {
const res = await selfUpdate({
username: userInfo.value.username,
phone: userInfo.value.phone,
email: userInfo.value.email,
address: userInfo.value.address,
});
if (res.code === 200) {
notification.success({
message: '修改成功',
description: '用户信息已更新',
duration: 3,
});
} else {
notification.error({
message: '修改失败',
description: res.msg,
});
}
};
</script>
<template>
<Form
:model="userInfo"
layout="vertical"
@finish="handleSubmit"
class="p-4"
style="max-width: 500px; margin: 0 auto"
>
<Form.Item label="用户名">
<Input v-model:value="userInfo.username" />
</Form.Item>
<Form.Item label="邮箱" name="email">
<Input v-model:value="userInfo.email" />
</Form.Item>
<Form.Item label="联系电话">
<Input v-model:value="userInfo.phone" />
</Form.Item>
<Form.Item label="地址">
<Input v-model:value="userInfo.address" />
</Form.Item>
<Button type="primary" html-type="submit">保存修改</Button>
</Form>
</template>

View File

@ -0,0 +1,83 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Button, Form, Input, notification } from 'ant-design-vue';
import { resetPassword } from '#/api';
//
const passwordForm = ref({
oldPassword: '',
password: '',
confirmPassword: '',
});
//
const handleSubmit = async () => {
if (passwordForm.value.password !== passwordForm.value.confirmPassword) {
notification.success({
message: '两次输入的新密码不一致',
duration: 3,
});
return;
}
// API
const res = await resetPassword(passwordForm.value);
if (res === true) {
notification.success({
message: '密码修改成功',
description: '密码已修改',
duration: 3,
});
passwordForm.value = {
oldPassword: '',
password: '',
confirmPassword: '',
};
} else {
notification.error({
message: '密码修改失败',
description: res.msg,
});
}
};
</script>
<template>
<Form
:model="passwordForm"
layout="vertical"
@finish="handleSubmit"
class="p-4"
style="max-width: 500px; margin: 0 auto"
>
<Form.Item
label="原密码"
name="oldPassword"
:rules="[{ required: true, message: '请输入原密码' }]"
>
<Input.Password v-model:value="passwordForm.oldPassword" />
</Form.Item>
<Form.Item
label="新密码"
name="password"
:rules="[{ required: true, message: '请输入新密码' }]"
>
<Input.Password v-model:value="passwordForm.password" />
</Form.Item>
<Form.Item
label="确认新密码"
name="confirmPassword"
:rules="[{ required: true, message: '请确认新密码' }]"
>
<Input.Password v-model:value="passwordForm.confirmPassword" />
</Form.Item>
<Button type="primary" html-type="submit">保存密码</Button>
</Form>
</template>
<style scoped>
:deep(.ant-form) {
max-width: 500px;
}
</style>

View File

@ -1,37 +1,43 @@
<script setup lang="ts">
import { ref } from 'vue';
import { toRefs } from 'vue';
import { Descriptions, Space, Upload } from 'ant-design-vue';
import { Avatar, Descriptions, Space } from 'ant-design-vue';
const fileList = ref([
{
uid: '-1',
name: 'image.png',
status: 'done',
url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
// const fileList = ref([
// {
// uid: '-1',
// name: 'image.png',
// status: 'done',
// url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
// },
// ]);
const props = defineProps({
userInfo: {
type: Object,
default: () => ({}),
},
]);
//
const userInfo = {
username: '张三',
email: 'zhangsan@example.com',
role: '管理员',
department: '技术部',
phone: '123-456-7890',
};
permissions: {
type: String,
default: '',
},
});
const { userInfo, permissions } = toRefs(props);
</script>
<template>
<Space :size="54">
<Space :size="64" class="p-4">
<!-- 用户头像上传组件 -->
<Upload
list-type="picture-card"
:file-list="fileList"
:show-upload-button="false"
>
<!-- 自定义显示头像 -->
</Upload>
<!-- <Upload-->
<!-- list-type="picture-card"-->
<!-- :file-list="fileList"-->
<!-- :show-upload-button="false"-->
<!-- >-->
<!-- &lt;!&ndash; 自定义显示头像 &ndash;&gt;-->
<!-- </Upload>-->
<Avatar :size="64">
{{ userInfo.username }}
</Avatar>
<!-- 用户信息展示 -->
<Descriptions title="" size="middle">
@ -39,9 +45,9 @@ const userInfo = {
{{ userInfo.username }}
</Descriptions.Item>
<Descriptions.Item label="邮箱">{{ userInfo.email }}</Descriptions.Item>
<Descriptions.Item label="角色">{{ userInfo.role }}</Descriptions.Item>
<Descriptions.Item label="角色">{{ permissions }}</Descriptions.Item>
<Descriptions.Item label="部门">
{{ userInfo.department }}
{{ userInfo.dept?.name }}
</Descriptions.Item>
<Descriptions.Item label="联系电话">
{{ userInfo.phone }}

View File

@ -97,7 +97,7 @@ export function useFormSchema(): VbenFormSchema[] {
labelField: 'name',
valueField: 'id',
},
fieldName: 'roleIds',
fieldName: 'roleId',
label: '所属角色',
},
{

View File

@ -36,6 +36,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
const { valid } = await formApi.validate();
if (!valid) return;
const values = await formApi.getValues();
values.roleIds = values.roleId;
delete values.roleId;
drawerApi.lock();
(id.value ? updateUser(id.value, values) : createUser(values))
.then(() => {