04. 带你开发用户登录、注册、个人信息、修改密码功能
用户登录后端接口实现
/**
* 登录
*/
@PostMapping("/login")
public Result login(@RequestBody Account account) {
Account ac = null;
if ("管理员".equals(account.getRole())) {
ac = adminService.login(account);
}
if ("普通用户".equals(account.getRole())) {
ac = userService.login(account);
}
if (ac == null) {
return Result.error("登录失败,用户不存在");
}
return Result.success(ac);
}
在 service 里面做一些查询处理
public Account login(Account account) {
User dbUser = userMapper.selectByUsername(account.getUsername());
if (ObjectUtil.isNull(dbUser)) {
throw new CustomException("用户不存在");
}
if (!account.getPassword().equals(dbUser.getPassword())) {
throw new CustomException("账号或密码错误");
}
return dbUser;
}
登录前台实现
<el-form-item prop="role">
<el-select size="large" style="width: 100%" v-model="data.form.role">
<el-option value="普通用户" label="普通用户"></el-option>
<el-option value="管理员" label="管理员"></el-option>
</el-select>
</el-form-item>
// 点击登录按钮的时候会触发这个方法
const login = () => {
formRef.value.validate((valid => {
if (valid) {
// 调用后台的接口
request.post('/login', data.form).then(res => {
if (res.code === '200') {
ElMessage.success("登录成功")
localStorage.setItem('system-user', JSON.stringify(res.data))
if (res.data.role === '管理员') {
router.push('/manager/home')
} else {
router.push('/front/home')
}
} else {
ElMessage.error(res.msg)
}
})
}
})).catch(error => {
console.error(error)
})
}
前端路由
{
path: '/front',
component: () => import('@/views/Front.vue'),
redirect: '/front/home',
children: [
{ path: 'home', component: () => import('@/views/front/Home.vue')},
]
},
注册后端接口
/**
* 注册
*/
@PostMapping("/register")
public Result register(@RequestBody User user) {
if (!user.getPassword().equals(user.getNewPassword())) {
return Result.error("两次输入密码不一致");
}
userService.add(user);
return Result.success();
}
注册页面
<template>
<div class="login-container">
<div class="login-box">
<div style="font-weight: bold; font-size: 30px; text-align: center; margin-bottom: 30px; color: #0c9c7a">欢 迎 注 册</div>
<el-form :model="data.form" ref="formRef" :rules="data.rules">
<el-form-item prop="username">
<el-input :prefix-icon="User" size="large" v-model="data.form.username" placeholder="请输入账号" />
</el-form-item>
<el-form-item prop="password">
<el-input :prefix-icon="Lock" size="large" v-model="data.form.password" placeholder="请输入密码" show-password />
</el-form-item>
<el-form-item prop="newPassword">
<el-input :prefix-icon="Lock" size="large" v-model="data.form.newPassword" placeholder="请确认密码" show-password />
</el-form-item>
<el-form-item>
<el-button size="large" style="width: 100%; background-color: #0c9c7a; border-color: #0c9c7a; color: white" @click="register">注 册</el-button>
</el-form-item>
</el-form>
<div style="text-align: right;">
已有账号?请 <a href="/login">登录</a>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
import { User, Lock } from "@element-plus/icons-vue";
import request from "@/utils/request";
import {ElMessage} from "element-plus";
import router from "@/router";
const data = reactive({
form: { role: '普通用户' },
rules: {
username: [
{ required: true, message: '请输入账号', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
],
newPassword: [
{ required: true, message: '请确认密码', trigger: 'blur' },
],
}
})
const formRef = ref()
// 点击登录按钮的时候会触发这个方法
const register = () => {
formRef.value.validate((valid => {
if (valid) {
// 调用后台的接口
request.post('/register', data.form).then(res => {
if (res.code === '200') {
ElMessage.success("恭喜您!注册成功")
router.push('/login')
} else {
ElMessage.error(res.msg)
}
})
}
})).catch(error => {
console.error(error)
})
}
</script>
<style scoped>
.login-container {
height: 100vh;
overflow:hidden;
display: flex;
justify-content: center;
align-items: center;
background: #0c9c7a;
background-size: cover;
}
.login-box {
width: 350px;
padding: 50px 30px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
background-color: #fff;
}
</style>
用户前台框架 Front.vue
<template>
<div>
<div style="height: 60px; background-color: #2e3143; display: flex; align-items: center; border-bottom: 1px solid #ddd">
<div style="flex: 1">
<div style="padding-left: 20px; display: flex; align-items: center">
<img src="@/assets/imgs/logo.png" alt="" style="width: 40px">
<div style="font-weight: bold; font-size: 24px; margin-left: 5px; color: #fff">校园小卖部</div>
</div>
</div>
<div style="width: fit-content; padding-right: 10px;">
<el-dropdown>
<div style="display: flex; align-items: center;">
<img style="width: 40px; height: 40px; border-radius: 50%" :src="data.user.avatar || 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png'" alt="">
<span style="color: #fff; margin-left: 5px">{{ data.user.name || '代码小白' }}</span>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<div style="background-color: #f0f2ff">
<router-view />
</div>
</div>
</template>
<script setup>
import { reactive } from "vue";
import router from "@/router";
import {ElMessage} from "element-plus";
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}')
})
const logout = () => {
localStorage.removeItem('system-user')
router.push('/login')
ElMessage.success('退出成功')
}
</script>
<style>
.el-tooltip__trigger {
cursor: pointer;
outline: none !important;
}
</style>
个人信息页面 front/Person.vue
<template>
<div class="front-container" style="width: 40%">
<div class="card" style="padding: 20px">
<div style="font-size: 20px; margin-bottom: 40px; text-align: center">个人信息页</div>
<el-form ref="formRef" :model="data.user" :rules="data.rules" label-width="80px" style="padding-right: 30px">
<el-form-item prop="avatar" label="头像">
<el-upload
class="avatar-uploader"
:action="baseUrl + '/files/upload'"
:show-file-list="false"
:on-success="handleFileUpload"
>
<img v-if="data.user.avatar" :src="data.user.avatar" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
<el-form-item prop="username" label="账号">
<el-input :disabled="data.user.id !== undefined" v-model="data.user.username" placeholder="请输入账号" autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="name" label="姓名">
<el-input v-model="data.user.name" placeholder="请输入姓名" autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="account" label="账户余额">
<div style="color: red; font-weight: bold">¥{{ data.user.account }}</div>
</el-form-item>
<div style="text-align: center">
<el-button type="primary" size="large" @click="update">保 存</el-button>
</div>
</el-form>
</div>
</div>
</template>
<script setup>
import { reactive,ref } from "vue";
import request from "@/utils/request";
import {ElMessage} from "element-plus";
const emit = defineEmits(['updateUser'])
const baseUrl = import.meta.env.VITE_BASE_URL
const formRef = ref()
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}'),
rules: {
username: [
{ required: true, message: '请输入账号', trigger: 'blur' },
]
}
})
const loadUser = () => {
request.get('/user/selectById/' + data.user.id).then(res => {
data.user = res.data
// 存储最新的用户信息
localStorage.setItem('system-user', JSON.stringify(res.data))
emit('updateUser')
})
}
loadUser()
const handleFileUpload = (res) => {
data.user.avatar = res.data
}
const update = () => {
request.put('/user/update', data.user).then(res => {
if (res.code === '200') {
ElMessage.success('更新成功')
loadUser()
} else {
ElMessage.error(res.msg)
}
})
}
</script>
<style scoped>
.avatar-uploader .avatar {
width: 130px;
height: 130px;
display: block;
}
</style>
<style>
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 130px;
height: 130px;
text-align: center;
}
</style>
<div style="background-color: #f0f2ff">
<router-view @updateUser="updateUser" />
</div>
// 更新Front里面的user对象为最新值
const updateUser = () => {
data.user = JSON.parse(localStorage.getItem('system-user') || '{}')
}
修改密码 front/Password.vue
<template>
<div class="front-container" style="width: 40%">
<div class="card" style="padding: 20px">
<div style="font-size: 20px; margin-bottom: 40px; text-align: center">修改密码</div>
<el-form ref="formRef" :model="data.user" :rules="data.rules" label-width="100px" style="padding-right: 30px">
<el-form-item prop="password" label="原密码">
<el-input v-model="data.user.password" autocomplete="off" show-password></el-input>
</el-form-item>
<el-form-item prop="newPassword" label="新密码">
<el-input v-model="data.user.newPassword" autocomplete="off" show-password></el-input>
</el-form-item>
<el-form-item prop="confirmPassword" label="确认新密码">
<el-input v-model="data.user.confirmPassword" autocomplete="off" show-password></el-input>
</el-form-item>
<div style="text-align: center">
<el-button type="primary" size="large" @click="updatePassword">保 存</el-button>
</div>
</el-form>
</div>
</div>
</template>
<script setup>
import { reactive,ref } from "vue";
import request from "@/utils/request";
import {ElMessage} from "element-plus";
import router from "@/router";
const formRef = ref()
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}'),
rules: {
password: [
{ required: true, message: '请输入原密码', trigger: 'blur' },
],
newPassword: [
{ required: true, message: '请输入新密码', trigger: 'blur' },
],
confirmPassword: [
{ required: true, message: '请确认新密码', trigger: 'blur' },
]
}
})
const updatePassword = () => {
if (data.user.newPassword !== data.user.confirmPassword) {
ElMessage.warning('两次输入的新密码不同,请确认!')
return
}
request.put('/updatePassword', data.user).then(res => {
if (res.code === '200') {
ElMessage.success('更新成功')
logout()
} else {
ElMessage.error(res.msg)
}
})
}
const logout = () => {
router.push('/login')
localStorage.removeItem('system-user')
}
</script>