当前位置: 首页 > news >正文

SpringBoot3+Vue3实战(Vue3快速开发登录注册页面并对接后端接口)(4)

目录

一、SpringBoot3+Vue3实现基本增删改查。前后端通信交互、配置后端跨域请求。数据批量删除。(博客链接)

二、SpringBoot3+Vue3快速开发登录、注册页面并实现对接。

(1)操作数据表employee(员工信息表)。

<1>修改employee表的字段组成。

<2>填补对应数据库数据。

(2)修改Employee实体类。

<1>新增对应成员属性。

<2>新增对应getter、setter方法。

<3>修改之前的toString()方法。

(3)修改mapper层代码。

<1>修改新增SQL。

<2>修改更新SQL。

<3>EmployeeMapper接口代码示例。

(4)员工信息(Employee.vue)页面。

<1>前端代码示例。

<2>员工信息的页面渲染效果。

<3>对话框—新增—某些字段设为必填项。(表单校验)

表单标签(el-form)添加ref。

保存按钮绑定事件函数save中触发校验。

表单标签(el-form)绑定校验规则。(:rules)

针对username设置校验规则。(required、message、trigger)

在对话框的对应需校验表单项添加prop属性。

给名称(name)、工号(no)也绑定校验“必填项”。

关闭对话框销毁旧对话框对象。(属性destroy-on-close)

(5)新建登录页面。(Login.vue)

<1>登录页面基本框架。

配置路由。(不是嵌套子路由)

导航栏—退出登录。(index)

<2>登录页面代码示例。

前端登录页面渲染效果图示。

表单项(username、password)校验。(属性ref、:rules)

表单项对应规则。(属性prop)

表单项校验效果图示。(鼠标未输入、且失焦)

登录绑定login函数。(若校验成功—>post请求后端接口"/login")

(6)后端代码编写。(登录)

<1>controller层登录接口。

<2>service层的登录业务处理。

自定义异常。(CustomerException)

封装返回前端数据结果类。(Result)

全局异常捕获处理器。(GlobalExceptionHandler)

前端处理请求(request)和响应(response)的工具类。

配置前后端跨域配置类。

service层登录逻辑代码示例。

<3>mapper层SQL。

<4>登录页面进行登录操作图示。

<5>获取后端返回的员工对象。(实时渲染右上角用户名称)

存储用户信息。(localStorage.setItem(xxx))

主页面(Manager.vue)获取登录时存储的用户信息。

(7)新建注册页面。(Register.vue)


一、SpringBoot3+Vue3实现基本增删改查。前后端通信交互、配置后端跨域请求。数据批量删除。(博客链接)

  • SpringBoot3实战(SpringBoot3+Vue3基本增删改查、前后端通信交互、配置后端跨域请求、数据批量删除(超详细))(3)-CSDN博客

  • 注意:关于本篇博客的案例《员工信息的前端页面代码、后端接口(controller)、service、mapper层代码》基本都在上面那篇博客中已实现。需要的可以自取
  • 这篇博客的主要内容:学习《使用Vue3快速开发登录、注册页面并对接后台》。前端Vue页面与后端SpringBoot服务器进行对接。实现员工登录或注册进入系统的功能。

二、SpringBoot3+Vue3快速开发登录、注册页面并实现对接。

(1)操作数据表employee(员工信息表)。
<1>修改employee表的字段组成。
  • employee表字段原组成。


  • 添加3个字段:用户名(username)、密码(password)、角色(role)。


<2>填补对应数据库数据。

(2)修改Employee实体类。
<1>新增对应成员属性。
package com.hyl.entity;public class Employee {private Integer id;private String username;private String password;private String role;private String name;private String sex;private String no;private Integer age;private String description;//数据库字段:department_id (下划线命名)//实体类属性:departmentId (驼峰命名)private Integer departmentId;
}

<2>新增对应getter、setter方法。
 public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getRole() {return role;}public void setRole(String role) {this.role = role;}

<3>修改之前的toString()方法。
//toString方法@Overridepublic String toString() {return "Employee{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", role='" + role + '\'' +", name='" + name + '\'' +", sex='" + sex + '\'' +", no='" + no + '\'' +", age=" + age +", description='" + description + '\'' +", departmentId=" + departmentId +'}';}
(3)修改mapper层代码。
<1>修改新增SQL。


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hyl.mapper.EmployeeMapper"><insert id="insert" parameterType="com.hyl.entity.Employee">insert into `employee`(username,password,role,name,sex,no,age,description,department_id)values (#{username},#{password},#{role},#{name},#{sex},#{no},#{age},#{description},#{departmentId})</insert></mapper>

<2>修改更新SQL。


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hyl.mapper.EmployeeMapper"><update id="updateById" parameterType="com.hyl.entity.Employee">update `employee` set username = #{username},password = #{password},role = #{role}, name = #{name},sex=#{sex},no=#{no},age=#{age},description=#{description},department_id=#{departmentId}where id = #{id}</update></mapper>

<3>EmployeeMapper接口代码示例。
package com.hyl.mapper;import com.hyl.entity.Employee;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select;import java.util.List;public interface EmployeeMapper {//通过注解形式完成SQL语句的查询//简单SQL可以直接使用注解形式。动态SQL最好使用XML文件形式@Select("select * from `employee` where id = #{id}")Employee selectById(Integer id);List<Employee> selectAll(Employee employee);void insert(Employee employee);void updateById(Employee employee);@Delete("delete from `employee` where id = #{id}")void deleteById(Integer id);
}
(4)员工信息(Employee.vue)页面。
<1>前端代码示例。
 <el-table-column label="用户名" prop="username"/>
<el-table-column label="角色" prop="role"><template #default="scope"><span v-if="scope.row.role === 'EMP'">员工</span></template>
</el-table-column>

<2>员工信息的页面渲染效果。
  • 一般员工信息页面只会显示用户名、角色。而密码将不会直接显示。


<3>对话框—新增—某些字段设为必填项。(表单校验)
<el-form-item label="用户名"><el-input v-model="data.form.name" autocomplete="off" placeholder="请输入用户名"/>
</el-form-item>


  • 表单标签(el-form)添加ref。
<el-form ref="formRef" :model="data.form" label-width="80px" style="padding-right: 50px;padding-top: 20px">
import {reactive,ref} from "vue";const formRef = ref();

  • 保存按钮绑定事件函数save中触发校验。
//对话框的保存按钮事件
const save = () =>{formRef.value.validate((valid)=>{//通过校验才能点击更新或新增的保存操作if(valid){//有id进行更新操作。无id进行新增操作data.form.id ? update() : add()}})
}

  • 表单标签(el-form)绑定校验规则。(:rules)
<el-form ref="formRef" :rules="data.rules" :model="data.form" label-width="80px" style="padding-right: 50px;padding-top: 20px">
  • 针对username设置校验规则。(required、message、trigger)
  • 设置了required为true表示该字段必填message是当未满足必填条件时显示的提示信息 “请输入用户名”trigger为blur表示在输入框失去焦点时触发验证
import {reactive,ref} from "vue";const formRef = ref();const data = reactive({rules:{username:[{ required:true , message: '请输入用户名' , trigger:'blur'}]}
})

  • 在对话框的对应需校验表单项添加prop属性。
<el-form-item label="用户名" prop="username"><el-input v-model="data.form.username" autocomplete="off" placeholder="请输入用户名"/>
</el-form-item>

  • 给名称(name)、工号(no)也绑定校验“必填项”。
<el-form-item label="名称" prop="name"><el-input v-model="data.form.name" autocomplete="off" placeholder="请输入名称"/>
</el-form-item><el-form-item label="工号" prop="no"><el-input v-model="data.form.no" autocomplete="off" placeholder="请输入工号"/>
</el-form-item>

import {reactive,ref} from "vue";const data = reactive({rules:{username:[{ required:true , message: '请输入用户名' , trigger:'blur'}],name:[{ required:true , message: '请输入名称' , trigger:'blur'}],no:[{ required:true , message: '请输入工号' , trigger:'blur'}]}
})

  • 页面渲染效果。


  • 关闭对话框销毁旧对话框对象。(属性destroy-on-close)
<el-dialog title="员工信息" v-model="data.formVisible" width="500" destroy-on-close><el-form ref="formRef" :rules="data.rules" :model="data.form" label-width="80px" style="padding-right: 50px;padding-top: 20px"><el-form-item label="用户名" prop="username"><el-input v-model="data.form.username" autocomplete="off" placeholder="请输入用户名"/></el-form-item><el-form-item label="名称" prop="name"><el-input v-model="data.form.name" autocomplete="off" placeholder="请输入名称"/></el-form-item><el-form-item label="性别"><el-radio-group v-model="data.form.sex"><el-radio value="男">男</el-radio><el-radio value="女">女</el-radio></el-radio-group></el-form-item><el-form-item label="工号" prop="no"><el-input v-model="data.form.no" autocomplete="off" placeholder="请输入工号"/></el-form-item><!--   设置最小年龄18  最大年龄100   --><el-form-item label="年龄"><el-input-number v-model="data.form.age" :min="18" :max="100" style="width: 250px" autocomplete="off" placeholder="年龄>=18与年龄<=100"/></el-form-item><!--  设置类型:文本域  :rows设置默认显示三行  --><el-form-item label="个人简介"><el-input type="textarea" :rows="3" v-model="data.form.description" autocomplete="off" placeholder="请输入个人简介"/></el-form-item></el-form><template #footer><div class="dialog-footer"><el-button @click="data.formVisible = false">取消</el-button><el-button type="primary" @click="save">保存</el-button></div></template>
</el-dialog>

  • 这样点击“取消”关闭某次对话框时,下次新对话框就不会存留着上次的校验失败提示。

(5)新建登录页面。(Login.vue)
<1>登录页面基本框架。


  • 配置路由。(不是嵌套子路由)


  • 导航栏—退出登录。(index)


<2>登录页面代码示例。
<template><div><div class="login-container">
<!--   登录表单容器   --><div class="login-box"><div style="padding: 40px 30px;background-color: #fbfcf8;margin-left: 200px;border-radius: 5px"><div style="margin-bottom: 30px;font-weight: bold;font-size: 24px;color: #514343;text-align: center" >欢迎登录hyl信息管理系统</div><el-form ref="formRef" :model="data.form" style="width: 350px"><el-form-item><el-input v-model="data.form.username" placeholder="请输入用户名" prefix-icon="User"></el-input></el-form-item><el-form-item><el-input v-model="data.form.password" placeholder="请输入密码" prefix-icon="Lock" show-password></el-input></el-form-item><div style="margin-top: 20px;margin-bottom: 20px"><el-button size="large" style="width: 100%;" type="primary">登 录</el-button></div><div style="margin-bottom: 20px"><el-button size="large" style="width: 100%;" type="primary">注 册</el-button></div><!--  可以使用text-decoration:none 让链接不显示下划线  --><div style="text-align: right">没有账号?请 <a style="color: #1967e3;" href="/register">注册</a></div></el-form></div></div></div></div>
</template><script setup>import {reactive} from "vue";const data = reactive({form:{},
})</script><style scoped>
.login-container {/*占满可视高度*/height: 100vh;/*隐藏超出高度*/overflow: hidden;/*背景图*/background-image: url("@/assets/bimage2.jpeg");/*背景图片大小*/background-size: cover;/*设置背景图移动*/background-position: -450px -120px;
}.login-box {/*绝对定位*/position: absolute;width: 50%;height: 100vh;/*靠右*/right: 0;/*flex布局*/display: flex;align-items: center;background-color: #f1ebe2;
}</style>
  • 前端登录页面渲染效果图示。


  • 表单项(username、password)校验。(属性ref、:rules)
<el-form ref="formRef" :rules="data.rules" :model="data.form" style="width: 350px">
import {reactive,ref} from "vue";const formRef = ref()const data = reactive({form:{},rules:{username :[{ required: true ,message:'请输入用户名',trigger:'blur'}],password :[{ required: true ,message:'请输入密码',trigger:'blur'}],},
})

  • 表单项对应规则。(属性prop)
<el-form-item prop="username"><el-input v-model="data.form.username" placeholder="请输入用户名" prefix-icon="User"></el-input>
</el-form-item><el-form-item prop="password"><el-input v-model="data.form.password" placeholder="请输入密码" prefix-icon="Lock" show-password></el-input>
</el-form-item>

  • 表单项校验效果图示。(鼠标未输入、且失焦)


  • 登录绑定login函数。(若校验成功—>post请求后端接口"/login")
<div style="margin-top: 20px;margin-bottom: 20px"><el-button size="large" style="width: 100%;" type="primary" @click="login">登 录</el-button>
</div>
import {reactive,ref} from "vue";
import request from "@/utils/request.js";
import {ElMessage} from "element-plus";const formRef = ref()//登录按钮操作方法
const login = () =>{//开始校验formRef.value.validate((valid)=>{if(valid){//表单项(字段)验证通过request.post("/login",data.form).then(res=>{if(res.code === '200'){  //登录成功ElMessage.success('登录成功')//登录成功后,跳转主页。等500ms进行跳转setTimeout(()=>{location.href = '/manager/home'},500)}else {  //登录失败ElMessage.error(res.msg)}})}})
}
(6)后端代码编写。(登录)
<1>controller层登录接口。
package com.hyl.controller;import com.hyl.common.Result;
import com.hyl.entity.Employee;import com.hyl.service.EmployeeService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;//表示对外提供可访问接口的类
@RestController
public class WebController {@Resourceprivate EmployeeService employeeService;//测试接口(不必理会)@GetMapping("/hello")public Result hello(){return Result.success("hello");}//登录接口//返回登录成功后的员工对象通过@RestController响应成JSON对象给前端//前端(res.data)再将JSON对象转换成JSON字符串临时存储用户信息@PostMapping("/login")public Result login(@RequestBody Employee employee){Employee dbEmployee = employeeService.login(employee);return Result.success(dbEmployee);}}

<2>service层的登录业务处理。
  • 自定义异常。(CustomerException)
package com.hyl.exception;public class CustomerException extends RuntimeException{private String code;private String msg;public CustomerException(String code, String msg) {this.code = code;this.msg = msg;}//getter|setter方法public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}

  • 封装返回前端数据结果类。(Result)
package com.hyl.common;/*** 统一后端返回的数据结果类型*/
public class Result {//请求状态码private String code;//成功或错误信息private String msg;//返回数据结果private Object data;//常用静态方法//请求成功且无数据返回public static Result success(){Result result = new Result();result.setCode("200");result.setMsg("请求成功");return result;}//请求成功且有数据返回。public static Result success(Object data) {//直接调用静态方法设置状态码、请求成功信息Result result = Result.success();result.setData(data);return result;}//请求失败且无数据返回public static Result error(){Result result = new Result();result.setCode("500");result.setMsg("系统出错了!");return result;}//请求失败且无数据返回。适配自定义异常(传递code、msg)public static Result error(String code,String msg){Result result = new Result();result.setCode(code);result.setMsg(msg);return result;}//getter、setter方法public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}
}

  • 全局异常捕获处理器。(GlobalExceptionHandler)
package com.hyl.exception;import com.hyl.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;//标识全局异常处理器并设置捕获路径
//因为异常产生最终都会归宿到controller中,所有异常统一在controller包中接口中处理。
@ControllerAdvice("com.hyl.controller")
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class) //捕获所有系统异常@ResponseBody//返回json串public Result error(Exception e) {//更清晰看到后端控制台报的错误e.printStackTrace();//返回Result的error()方法设置的错误信息(code、msg)给前端return Result.error();}@ExceptionHandler(CustomerException.class) //捕获自定义异常@ResponseBody//返回json串public Result error(CustomerException e) {  //自定义异常的参数code、msg//更清晰看到后端控制台报的错误e.printStackTrace();//Result的error(String code,String Msg)方法设置获取到的错误信息(code、msg)return Result.error(e.getCode(),e.getMsg());}}

  • 前端处理请求(request)和响应(response)的工具类。
import { ElMessage } from 'element-plus'
import axios from "axios";const request = axios.create({//设置后台请求地址baseURL: 'http://localhost:9090',timeout: 30000  // 后台接口超时时间设置
})// request 拦截器(数据请求)
// 可以自请求发送前对请求做一些处理
request.interceptors.request.use(config => {//设置统一的数据传输格式json、数据传输编码utf-8config.headers['Content-Type'] = 'application/json;charset=utf-8';return config
}, error => {return Promise.reject(error)
});// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(response => {//响应对象response中提取实际数据部分,存储在变量res中let res = response.data;// 兼容服务端返回的字符串数据//如果res是字符串且不为空字符串,则使用JSON.parse方法将其解析为JavaScript对象;//如果 res 为空字符串,则保持原样。if (typeof res === 'string') {res = res ? JSON.parse(res) : res}return res;},error => {if(error.response.status === 404){//404 状态码表示请求的资源未找到,通常意味着请求的接口不存在ElMessage.error('未找到请求接口')}else if(error.response.status === 500){//500:之前后端设置的全局系统异常处理捕获//500 状态码表示服务器内部错误,通常是由于后端代码出现异常ElMessage.error('系统异常,请查看后端控制台报错')}else{//其它情况统一打印错误信息console.error(error.message)}//将错误继续抛出,以便后续的代码可以继续处理该错误return Promise.reject(error)}
)export default request

  • 配置前后端跨域配置类。
package com.hyl.common;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;/*** 跨域配置类。用于解决前端和后端由于不同源(协议、域名、端口不同)导致的跨域请求问题* @Configuration 让该配置类注入到Spring容器中并能够扫描到其下类下注解*/
@Configuration
public class CorsConfig {/*** 创建并注册CorsFilter bean,用于处理跨域请求* @return CorsFilter实例,Spring会在请求处理过程中使用该过滤器处理跨域请求* @Bean 注解会让该方法的返回值注入到Spring容器中*/@Beanpublic CorsFilter corsFilter() {// 创建一个基于URL的跨域配置源对象,用于存储和管理不同URL路径的跨域配置UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();// 创建一个跨域配置对象,用于设置具体的跨域规则CorsConfiguration corsConfiguration = new CorsConfiguration();// 设置允许访问的源地址,这里使用通配符"*",表示允许所有源地址访问corsConfiguration.addAllowedOrigin("*");// 设置允许的请求头,"*"表示允许所有请求头,即前端可以在请求中携带任意请求头corsConfiguration.addAllowedHeader("*");// 设置允许的请求方法,"*"表示允许所有的HTTP请求方法,如GET、POST、PUT、DELETE等corsConfiguration.addAllowedMethod("*");// 将跨域配置应用到所有的接口路径上,"/**"表示匹配所有路径source.registerCorsConfiguration("/**", corsConfiguration);// 创建并返回CorsFilter实例,传入配置源对象,Spring会使用该过滤器处理跨域请求return new CorsFilter(source);}
}

  • service层登录逻辑代码示例。
public Employee login(Employee employee){//用户名String username = employee.getUsername();//先查询数据库中是否有该用户名Employee dbEmployee = employeeMapper.selectByUserName(username);if(Objects.isNull(dbEmployee)){ //未查询到用户//返回自定义异常。交给全局异常捕获器处理//将错误状态码返回前端,前端捕获返回的res.code。提示登录失败与错误信息:用户不存在throw new CustomerException("500","用户不存在");}//用户存在//判断密码是否正确String password = employee.getPassword();if(!dbEmployee.getPassword().equals(password)){//用户输入的密码与数据库的密码不匹配//返回自定义异常。交给全局异常捕获器处理//将错误状态码返回前端,前端捕获返回的res.code。提示登录失败与错误信息:账号或密码错误throw new CustomerException("500","账号或密码错误");}//所有逻辑判断没有问题,返回数据库的Employee对象return dbEmployee;}

<3>mapper层SQL。
@Select("select * from `employee` where username = #{username}")Employee selectByUserName(String username);

<4>登录页面进行登录操作图示。





<5>获取后端返回的员工对象。(实时渲染右上角用户名称)
  • 存储用户信息。(localStorage.setItem(xxx))
//登录按钮操作方法
const login = () =>{//开始校验formRef.value.validate((valid)=>{if(valid){//表单项(字段)验证通过request.post("/login",data.form).then(res=>{if(res.code === '200'){  //登录成功ElMessage.success('登录成功')//登录成功后,跳转主页。等500ms进行跳转setTimeout(()=>{location.href = '/manager/home'},500)//存储后台返回的员工对象信息//把后端传来的JSON对象转换成JSON字符串存储localStorage.setItem('xm-pro-user',JSON.stringify(res.data))}else {  //登录失败ElMessage.error(res.msg)}})}})
}

  • 主页面(Manager.vue)获取登录时存储的用户信息。
import {reactive} from "vue";const data = reactive({//记得将拿到的JSON字符串转换成JSON对象。这样才能获取到员工名称:data.user.nameuser: JSON.parse(localStorage.getItem('xm-pro-user'))
})


<!--  右半部分-头像  --><div style="width: 150px;display: flex;align-items: center"><img src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png" alt="" style="width: 40px;height: 40px"><span style="margin-left: 5px;color: white">{{data.user.name}}</span></div>


(7)新建注册页面。(Register.vue)
  • 后面补充。!!!!


http://www.mrgr.cn/news/95059.html

相关文章:

  • Lombok常用注解
  • 男女搭配(数学思维)
  • YOLO魔改之频率分割模块(FDM)
  • stm32第七天震动传感器
  • 【模拟】从 0 到 1:模拟算法的深度剖析与实战指南
  • python实现接口自动化
  • 【MySQL数据库】多表查询(笛卡尔积现象,联合查询、内连接、左外连接、右外连接、子查询)-通过练习快速掌握法
  • 4.好事多磨 1
  • C++项目:高并发内存池_上
  • 【深度学习与大模型基础】第6章-对角矩阵,对称矩阵,正交矩阵
  • 20250315-OpenAI-AgentSDK实验
  • 计算机网络面试篇
  • Linux:(socket套接字——TCP协议,守护进程)
  • 博客图床 VsCode + PicGo + 阿里云OSS
  • 网络华为HCIA+HCIP VLAN间通信
  • S32K144入门笔记(十三):LPIT的API函数解读
  • C语言经典代码练习题
  • 使用Redis如何实现分布式锁?(超卖)
  • 02 windows qt配置ffmpeg开发环境搭建
  • linux du和df