Vue + ElementPlus 实现权限管理系统(十一): 实现个人中心页面
在后台管理系统中,一般都会有个人中心页面用于修改查看我们的账户信息、修改密码等。本篇文章将使用vue
和elementplus
来实现我们后台权限管理系统的个人中心页面。最终效果如下
可以查看我们当前的账户信息,更换头像以及修改基本信息及密码。接下来我们就来实现这些功能。
接口引入
这个页面需要四个接口,分别是获取当前用户信息、头像上传、修改密码、修改基本信息接口。访问本地地址http://localhost:3000/fs_admin/api
可以看到对应的丝袜哥接口文档
在src/api/user/index.ts
导出这三个接口(上传接口使用element
提供的upload
组件)。
//获取用户信息
export const getProfile = () => {
return request({
url: "/user/profile",
method: "get",
loading: false,
});
};
//修改密码
export const updatePassword = (data: {
oldPassword: string,
newPassword: string,
}) => {
return request({
url: "/user/updatePassword",
method: "put",
data,
});
};
//修改个人信息
export const updateUserInfo = (data: Form) => {
return request({
url: "/user/updateUserInfo",
method: "put",
data,
});
};
页面布局
页面布局使用elementplus
的el-col
将页面分为左右两部分。左边是使用el-card
组件实现个人信息展示区域。右边是修改个人信息区域,同时使用el-tabs
实现修改密码和修改基本信息两个选项卡。我们src/views
下新建profile/index.vue
来实现这个页面。
<template>
<el-row class="fs_profile" :gutter="20">
<el-col :span="8">
<el-card>
<template #header>
<div class="card-header">
<span>我的信息</span>
</div>
</template>
<el-upload
class="avatar-uploader"
:action="uploadParams.uploadUrl"
:headers="uploadParams.headers"
:on-success="handleAvatarSuccess"
:show-file-list="false"
>
<img
v-if="proFileData.avatar"
:src="proFileData.avatar"
class="avatar"
/>
<el-icon v-else class="avatar-uploader-icon">
<Plus />
</el-icon>
</el-upload>
<ul>
<li class="profile">
<div class="profile_label">
<User class="profile_label_icon" />
<div class="profile_label_text">用户名</div>
</div>
<div>{{ proFileData.username }}</div>
</li>
<li class="profile">
<div class="profile_label">
<Avatar class="profile_label_icon" />
<div class="profile_label_text">昵称</div>
</div>
<div>{{ proFileData.nickname }}</div>
</li>
<li class="profile">
<div class="profile_label">
<Iphone class="profile_label_icon" />
<div class="profile_label_text">手机号</div>
</div>
<div>{{ proFileData.telephone }}</div>
</li>
<li class="profile">
<div class="profile_label">
<Message class="profile_label_icon" />
<div class="profile_label_text">邮箱</div>
</div>
<div>{{ proFileData.email }}</div>
</li>
<li class="profile">
<div class="profile_label">
<Calendar class="profile_label_icon" />
<div class="profile_label_text">创建时间</div>
</div>
<div>{{ proFileData.create_time }}</div>
</li>
</ul>
</el-card>
</el-col>
<el-col :span="16">
<el-card>
<template v-slot:header>
<div>
<span>个人资料</span>
</div>
</template>
<el-tabs v-model="activeName">
<el-tab-pane label="基本资料" name="userinfo">
<userInfo :user="proFileData" @submit="updateUser" />
</el-tab-pane>
<el-tab-pane label="修改密码" name="resetPwd">
<resetPassWord @submit="resetPwd" />
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
</template>
其中userInfo
和resetPassWord
是两个子组件,分别用来实现修改基本信息和修改密码的功能,后面再讲。
同时这次样式我们使用 scss 来写(用 tailwind 太麻烦)。
.fs_profile {
min-width: 1000px;
.avatar-uploader {
text-align: center;
margin: 30px 0;
.el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 50%;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
margin: 0 auto;
}
.avatar {
width: 150px;
height: 150px;
object-fit: cover;
}
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
.profile {
display: flex;
justify-content: space-between;
padding: 15px 5px;
border-top: 1px solid lightgray;
.profile_label {
display: flex;
color: gray;
align-items: center;
.profile_label_icon {
width: 20px;
height: 20px;
margin-right: 4px;
}
.profile_label_text {
flex: none;
}
}
}
}
信息查询
我们先看信息查询功能,实现还是比较简单的,调用接口获取用户信息,然后将数据绑定到页面上即可
import { getProfile } from "@/api/user/index";
import type { ResetForm, UserInfo } from "./types";
const proFileData = ref < Partial < UserInfo >> {};
const getProFileData = async () => {
const { data } = await getProfile();
proFileData.value = data;
};
其中用到了UserInfo
类型,这些类型我们放在types
目录下
export type UserInfo = {
username: string,
nickname: string,
telephone: string,
email: string,
avatar: string,
create_time: string,
};
export type ResetForm = {
oldPassword: string,
newPassword: string,
confirmPassword?: string,
};
这样我们的查询功能就实现了。
基本信息修改
前面我们提到了userInfo
和resetPassWord
两个子组件,分别用来实现修改基本信息和修改密码的功能。我们先来看基本信息userInfo
组件。
<template>
<el-form ref="userRef" :model="user" label-width="80px">
<el-form-item label="用户昵称" prop="nickName">
<el-input v-model="user.nickname" maxlength="30" />
</el-form-item>
<el-form-item label="手机号码" prop="telephone">
<el-input v-model="user.telephone" maxlength="11" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="user.email" maxlength="50" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit">保存资料</el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { toRefs } from "vue";
import type { UserInfo } from "../types";
const userProps = defineProps<{ user: Partial<UserInfo> }>();
//结构解出user且是响应式的
const { user } = toRefs(userProps);
type Emits = {
(e: "submit", user: Partial<UserInfo>): void;
};
const emits = defineEmits<Emits>();
const submit = () => {
emits("submit", userProps.user);
};
</script>
然后在父组件中使用它,同时传入user
参数以及接收submit
修改事件
<userInfo :user="proFileData" @submit="updateUser" />
最后在updateUser
函数中实现修改基本信息的功能即可
const updateUser = async (data: Partial<UserInfo>) => {
await updateUserInfo(data);
ElMessage.success("修改成功");
getProFileData();
};
修改密码
修改密码需要输入旧密码、新密码、确认密码。我们先来看子组件resetPassWord
。
<template>
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="80px">
<el-form-item label="旧密码" prop="oldPassword">
<el-input
v-model="user.oldPassword"
placeholder="请输入旧密码"
type="password"
show-password
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="user.newPassword"
placeholder="请输入新密码"
type="password"
show-password
/>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input
v-model="user.confirmPassword"
placeholder="请确认密码"
type="password"
show-password
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit(pwdRef)">确认修改</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { FormInstance } from "element-plus";
import { ref } from "vue";
import { ResetForm } from "../types";
const pwdRef = ref<FormInstance>();
const user = ref<ResetForm>({
oldPassword: "",
newPassword: "",
confirmPassword: "",
});
type Emits = {
(e: "submit", user: ResetForm): void;
};
const emits = defineEmits<Emits>();
const equalToPassword = (
rule: any,
value: string,
callback: (arg0?: Error) => void
) => {
if (user.value.newPassword !== value) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
}
};
const rules = ref({
oldPassword: [
{ required: true, message: "旧密码不能为空", trigger: "blur" },
],
newPassword: [
{ required: true, message: "新密码不能为空", trigger: "blur" },
{ min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" },
],
confirmPassword: [
{ required: true, message: "确认密码不能为空", trigger: "blur" },
{ required: true, validator: equalToPassword, trigger: "blur" },
],
});
function submit(pwdRef: FormInstance | undefined) {
if (!pwdRef) return;
pwdRef.validate((valid) => {
if (valid) {
emits("submit", user.value);
}
});
}
</script>
这里使用了element-plus
的表单验证功能,验证规则在rules
中,定义了新旧密码不能为空及两次输入的密码一不一致等情况。比如如果不满足页面就会有所提示。
验证通过将会给父组件发送一个submit
事件同时传入修改后的用户信息。父组件拿到这些信息调用修改密码接口即可。
const resetPwd = async (data: ResetForm) => {
await updatePassword(data);
ElMessage.success("修改成功");
getProFileData();
};
头像上传
头像上传使用了element-plus
的upload
组件,在页面是我们是这么使用的
<el-upload
class="avatar-uploader"
:action="uploadParams.uploadUrl"
:headers="uploadParams.headers"
:on-success="handleAvatarSuccess"
:show-file-list="false"
></el-upload>
其中action
是上传地址,headers
是上传接口的请求头,这里我们需要和其它接口一样加上token
不然是禁止请求的。handleAvatarSuccess
是上传成功后的回调函数,可以接收到成功后的返回信息,show-file-list
是是否显示文件列表。
我们看一下实现逻辑代码
import { Storage } from "@/utils/storage";
// 上传相关参数
const uploadParams = {
uploadUrl: import.meta.env.VITE_APP_API + "/user/uploadAvatar",
headers: { authorization: "Bearer " + Storage.get("token") },
};
//上传成功回调
const handleAvatarSuccess = (res: any) => {
if (res.code == 200) {
ElMessage.success("头像设置成功");
getProFileData();
} else {
ElMessage.error(res.describe);
}
};
就是这么简单。
到这里就实现了个人中心页面。下一篇文章我们将介绍如何使用 NestJS 来实现这些接口。