前言
这可以说是我的第二篇博客了,这篇博客是在学习完SpringBoot课程之后,开始动手做的第一个项目,采用的是前后端分离的形式。但是前端页面并不是自己写的。做的工作只有后端接口的开发。这样对于第一次接触前后端分离项目的同学来说,是一件比较好的事情。通过这个小项目,我们进一步的了解到前后端分离的概念。体验到作为后端人员我们需要做哪些工作。
好了,接下来就进入正题了。
技术栈
- SpringBoot
- Mybatis
- 连接池:Durid
- 缓存:Redis
- 前端:Vue
- axios
需求分析
本次项目共分为用户模块和员工模块
用户模块:
1.用户登录
2.用户注册
3.验证码实现
4.用户展现
员工模块:
1.员工列表展现
2.员工列表添加
3.员工删除
4.员工列表加入redis缓存
数据库设计
1、数据表设计
用户表:
id | username | realname | password | sex | stauts | registerTime |
---|---|---|---|---|---|---|
员工表
id | name | path(头像) | salary | age |
---|---|---|---|---|
2、创建库表
# 数据库 emp
user emp
create table t_user(
id int(6) primary key auto_increment,
username varchar(60),
realname varchar(60),
password varchar(50),
sex varchar(4),
status varchar(4),
regsterTime timestamp
);
create table t_emp(
id int(6) primary key auto_increment,
name varchar(40),
path varchar(100),
salary double(10,2),
age int(3)
);
环境搭建
1、创建项目结构
项目名:ems_vue
包结构:
src/main/java
com.xxxx
controller
mapper
pojo
service
utils
src/main/resource
com.xxxx
mapper 用来存放mybatis的mapper配置文件
statis 用来存放静态资源
application.properties springboot 的配置文件
编码环节
1、配置文件的编写
# 配置项目访问的路径名
server.servlet.context-path=/ems_vue
# 数据库的配置
# 端口号
server.port=8080
# 采用的连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/emp?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
# 配置Mybatis的mapper的路径
mybatis.mapper-locations=classpath:com/feng/mapper/*.xml
# 起别名
mybatis.type-aliases-package=com.feng.pojo
# 打印日志的等级
logging.level.com,feng.dao=debug
logging.level.com,feng.service=info
logging.level.com,feng.controller=info
2、测试
此过程看看项目是否能够正常的运行
编写一个TextController
package com.feng.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TextController {
@RequestMapping("/hello")
public String hello()
{
return "hello";
}
}
当项目运行成功时,我们就可以进行我下一步的操作了。
3、引入静态资源
即Vue.js,axios,js机器其他的一些静态页面
4、pojo的实现
user类:
@Data
//链式编程
@Accessors(chain = true)
public class User {
private String id; //string类型的api相对较多
private String username;
private String realname;
private String password;
private String sex;
private String status;
private Date regsterTime;
}
业务实现
1、验证码的实现
在用户进行注册是需要进行验证码的验证。
首先我们需要在注册的页面进行验证码渲染。
<tr>
<td valign="middle" align="right">
验证码:
</td>
<td valign="middle" align="left">
<input type="text" v-model="code" class="inputgri" name="number" />
</td>
<td>
<img id="num" :src="url" />
<a href="javascript:;" @click="getImg">换一张</a>
</td>
</tr>
<!--引入Vue相关的JS-->
<script src="/ems_vue/js/vue.js"></script>
<script src="/ems_vue/js/axios.min.js"></script>
<script>
var app = new Vue({
el: "#wrap",
data: {
url: "",
user: {
sex: "男"
},
code: "",
},
methods: {
//更换验证码
getImg(){
this.getSrc();
},
//封装获取验证码的函数
getSrc(){
var _this = this;
//?time="+Math.random() 拼接一个随机的时间戳,用来防止浏览器的缓存
axios.get("http://localhost:8080/ems_vue/user/getImage?time="+Math.random()).then(res=>{
console.log(res.data);
_this.url = res.data;
});
},
//用来注册用户信息
register(){
axios.post("http://localhost:8080/ems_vue/user/register?code="+this.code,this.user).then(res=>{
console.log(res.data);
if(res.data.state){
alert(res.data.msg+",点击跳转志登录页面");
location.href="/ems_vue/login.html";
}else{
alert(res.data.msg);
}
});
}
},
//由于验证码是在页面加载时生成,因此需要Vue得生命周期函数
created(){
this.getSrc();
}
})
</script>
更换验证码的操作,需要通过@click="getImg"
给该链接绑定一个单击事件,就会执行methods中的getImg方法。
//封装获取验证码的函数
getSrc(){
var _this = this;
//?time="+Math.random() 拼接一个随机的时间戳,用来防止浏览器的缓存
axios.get("http://localhost:8080/ems_vue/user/getImage?time="+Math.random()).then(res=>{
console.log(res.data);
_this.url = res.data;
});
},
这部分就是验证码的具体请求:
由于在ES6语法中,this的指向改变了,因此我们需要保留this的指向。通过axios的get请求像后端发送请求,后端接口返回的base64类型字符串赋值给data中的url。然后image器进行解析。
后端接口的实现:
@RequestMapping("getImage")
public String getImageCode(HttpServletRequest request) throws IOException {
//1、使用工具类生成验证码
//参数代表着生成验证码的长度。
String code = VerifyCodeUtils.generateVerifyCode(4);
//2、放入servletContext作用域 (前后端分离的项目是没有session的概念的)
request.getServletContext().setAttribute("code",code);
//3、将图片转化为Base64类型输出
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
VerifyCodeUtils.outputImage(220,60,byteArrayOutputStream,code);
//如果image要解析base64,需要拼上一个处理 (拼接需要注意,不能少一个字符)
String s ="data:image/png;base64," + Base64Utils.encodeToString(byteArrayOutputStream.toByteArray());
return s;
2、用户的注册
<h1>
注册
</h1>
<form action="login.html" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td valign="middle" align="right">
用户名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" v-model="user.username" name="username" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
真实姓名:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" v-model="user.realname" name="name" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
密码:
</td>
<td valign="middle" align="left">
<input type="password" class="inputgri" v-model="user.password" name="pwd" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
性别:
</td>
<td valign="middle" align="left">
男
<input type="radio" class="inputgri" v-model="user.sex" name="sex" value="男" checked="checked"/>
女
<input type="radio" class="inputgri" v-model="user.sex" name="sex" value="女"/>
</td>
</tr>
<tr>
<td valign="middle" align="right">
验证码:
</td>
<td valign="middle" align="left">
<input type="text" v-model="code" class="inputgri" name="number" />
</td>
<td>
<img id="num" :src="url" />
<a href="javascript:;" @click="getImg">换一张</a>
</td>
</tr>
</table>
<p>
<input type="button" @click="register" class="button" value="Submit »" />
</p>
我们首先通过v-model
进行数据的双向绑定。将提交按钮添加一个单机事件,当提交时就会执行methods中的register
。执行方法中的axios请求。向后台发送请求,并且把输入的code数据返回给后端,进行判断验证码的正确。后端交给前端一个state状态,来进行判断时候注册成功。如果成功,就会跳转至登录页面,否则就会打印出来相关的错误信息。
//用来注册用户信息
register(){
axios.post("http://localhost:8080/ems_vue/user/register?code="+this.code,this.user).then(res=>{
console.log(res.data);
if(res.data.state){
alert(res.data.msg+",点击跳转志登录页面");
location.href="/ems_vue/login.html";
}else{
alert(res.data.msg);
}
});
后端注册接口的实现
mapper层:
@Mapper //用来创建dao对象
@Repository
public interface UserMapper {
//增加用户
int save(User user);
User findByUserName(String username);
}
<!--useGeneratedKeys: 把新增加的主键赋值到自己定义的keyProperty(id)中 -->
<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into emp.t_user values (#{id},#{username},#{realname},#{password},#{sex},#{status},#{regsterTime})
</insert>
<select id="findByUserName" parameterType="String" resultType="User">
select id,username,realname,password,sex,status,regsterTime
from emp.t_user where username = #{username}
</select>
service层:
public interface UserService {
//注册用户
void save(User user);
//根据名字查询用户
User findByUserName(String username);
//登录查询
User login(User user);
}
@Autowired
private UserMapper userMapper;
//根据用户名查询用户
@Override
public User findByUserName(String username) {
return userMapper.findByUserName(username);
}
@Override
public void save(User user) {
//第一步:要根据用户输入的用户名判断用户是否存在
//AlreadyUser: 已经存在得用户,如果用户已经存在则,注册失败。
User AlreadyUser = findByUserName(user.getUsername());
if(AlreadyUser == null)
{
//注册成功
user.setStatus("已激活");
user.setRegsterTime(new Date());
userMapper.save(user);
}
else
{
//注册失败抛出异常
throw new RuntimeException("用户名已经存在");
}
}
Controller层:
//处理用户注册
@RequestMapping("register")
//@RequestBody:后端接收前端传来的json字符串中的数据,同时,前端只能以post方式提交
public Map<String,Object> register(@RequestBody User user,String code,HttpServletRequest request)
{
//打印日志
log.info("用户信息:[{}]",user.toString());
log.info("用户输入的验证码信息:[{}]",code);
//用来保存返回给前端的一些数据
Map<String,Object> map = new HashMap<>();
try {
//拿到验证码
String key = (String) request.getServletContext().getAttribute("code");
System.out.println("验证码为"+key);
//equalsIgnoreCase :不区分大小写
if(key.equalsIgnoreCase(code))
{
map.put("state",true);
map.put("msg","提示:注册成功");
userService.save(user);
}
else
{
System.out.println("验证码错误");
throw new RuntimeException("验证码出现错误");
}
}catch (Exception e){
e.printStackTrace();
map.put("state",false);
map.put("msg","提示:"+e.getMessage());
}
return map;
}
3、用户登录
前端操作:
<h1>
login
</h1>
<form action="emplist.html" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<tr>
<td valign="middle" align="right">
username:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" v-model="user.username" name="name" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
password:
</td>
<td valign="middle" align="left">
<input type="password" class="inputgri" v-model="user.password" name="pwd" />
</td>
</tr>
</table>
<p>
<input type="button" @click="login" class="button" value="Submit »" />
<a href="/ems_vue/regist.html">注册</a>
</p>
</form>
<!--省略了一些其他的标签-->
.........
<!--引入Vue相关的JS-->
<script src="/ems_vue/js/vue.js"></script>
<script src="/ems_vue/js/axios.min.js"></script>
<script>
var app = new Vue({
el: "#wrap",
data: {
user:{}, //用来保存登陆的用户数据
},
methods: {
//用户登录的方法
login(){
console.log(this.user);
//发送登录请求
axios.post("http://localhost:8080/ems_vue/user/login",this.user).then(res=>{
console.log(res.data);
if(res.data.state){
alert(res.data.msg+",点击确定进入主页");
//将登录信息放到localStorage
localStorage.setItem("user",JSON.stringify(res.data.loginUser))
location.href="/ems_vue/emplist.html";
}else{
alert(res.data.msg);
}
})
}
}
});
</script>
- 通过
v-model
把表单中用户的信息和vue中data中的user进行了双向绑定 - 当点击提交按钮时,通过
@click=‘login’
绑定了一个单机事件,此时就会这些那个methods
中的login
方法。此时就会通过axios向后台提交表单数据,后端通过判断user是否存在,传递给前端一个state状态,如果为true,则会跳转到员工信息转世界面。并将员工信息保存在localStorage
,以防止用户的非法登录(相当于是一个拦截器的作用)。
后端接口实现:
mapper层省略,上文有
service层:
//根据用户名查询用户
@Override
public User findByUserName(String username) {
return userMapper.findByUserName(username);
}
//登录查询
@Override
public User login(User user) {
//1、根据用户名差
User loginUser = userMapper.findByUserName(user.getUsername());
if(loginUser != null)
{
//2、比较密码
if(loginUser.getPassword().equals(user.getPassword()))
{
return loginUser;
} else {
throw new RuntimeException("密码错误");
}
}
else{
throw new RuntimeException("用户名错误");
}
}
- 登录查询中调用一个
findByUserName
方法,根据表单提交的username进行判断, - 如果loginUser为空则登录失败,否则成功。
controller层:
@PostMapping("login")
public Map<String ,Object> login(@RequestBody User user)
{
log.info("当前登录用户的信息[{}]",user.toString());
Map<String, Object> map = new HashMap<>();
try {
User loginUser = userService.login(user);
map.put("state",true);
map.put("msg","登录成功");
map.put("loginUser",loginUser);
} catch (Exception e) {
e.printStackTrace();
map.put("state",false);
map.put("msg",e.getMessage());
}
return map;
}
这里应该没有什么好解释的,大家理解了整个流程思路也就差不多可以写出来了。
接下来就是员工的一些操作了。
员工业务操作
pojo
@Data
//链式编程
@Accessors(chain = true)
public class Emp {
private String id;
private String name;
private String path;
private Double salary;
private Integer age;
}
员工列表展现
前端核心代码:
<table class="table">
<tr class="table_header">
<td>
ID
</td>
<td>
Name
</td>
<td>
Photo
</td>
<td>
Salary
</td>
<td>
Age
</td>
<td>
Operation
</td>
</tr>
<tr v-for="(emp,index) in emps" :key="emp.id" :class="index%2==0?'row1':'row2'">
<td>
{{emp.id}}
</td>
<td>
{{emp.name}}
</td>
<td>
<img :src="'/ems_vue/'+emp.path" style="height: 60px;">
</td>
<td>
{{emp.salary}}
</td>
<td>
{{emp.age}}
</td>
<td>
<a href="javascript:;" @click="delEmp(emp.id)">delete emp</a> <a :href="'/ems_vue/updateEmp.html?id='+emp.id">update emp</a>
</td>
</tr>
</table>
<p>
<input type="button" class="button" value="Add Employee" onclick="location='addEmp.html'"/>
</p>
<p>
<input type="button" class="button" value="Add Employee" onclick="location='addEmp.html'"/>
</p>
<!--引入Vue相关的JS-->
<script src="/ems_vue/js/vue.js"></script>
<script src="/ems_vue/js/axios.min.js"></script>
<script>
var app = new Vue({
el: "#wrap",
data: {
user: { //用来保存用户登录的信息
},
emps: [],
time: ""
},
methods: {
//安全退出
logout(){
localStorage.removeItem("user");
location.reload(true); //刷新页面
},
//删除员工
delEmp(id){
if(window,confirm("确定要删除这条员工信息吗")){
var _this = this;
axios.get("http://localhost:8080/ems_vue/emp/delete?id="+id).then(res=>{
if(res.data.state){
alert(res.data.msg+"点击确定刷新数据");
_this.findAll(); //重新加载数据
}else{
alert(res.data.msg);
}
});
}
},
//查询员工信息列表的方法
findAll(){
var _this = this;
axios.get("http://localhost:8080/ems_vue/emp/findAll").then(res=>{
_this.emps = res.data;
});
}
},
created() {
var _this = this;
var userString = localStorage.getItem("user");
_this.time = new Date();
if(userString){
var Luser = JSON.parse(userString);
this.user = Luser;
}else{
alert("您尚未登录,点击确定跳转至登录");
location.href="/ems_vue/login.html";
}
//查询所有信息
this.findAll();
}
});
</script>
在展现用户列表时,我们呢只需要用到:findAll
和Vue自身的生命周期函数created()
即可
同样的,首先new 一个Vue的组件,并在data中生命了一个名为emp的数组,来用于接收我们数据。由于用户信息的渲染和页面的加载是同时进行的,因此,我们需要使用生命周期函数created()
。
我们在created()
中进行了用户user的判断,以防止非法登录。在用户登录的时候,我们在用户的信息存储到了浏览器的localStorage,此时,我们便可以进行登录拦截。如果此时在localStorage没有用户的信息,那么本次请求将会被拦截。
如果localStorage存在用户信息,那么便会执行findAll()
。同样的,我们在这里像后台发起一个axios请求。接收到后端传来的数据,这是我们便可以将数据渲染到页面中。
取数据时,是采用的Vue中的v-for指令。
后端接口
关于mapper和sql语句就不过多赘述
mapper层:
@Mapper
@Repository
public interface EmpMapper {
List<Emp> getAllEmp();
void save(Emp emp);
void deleteEmp(String id);
Emp findById(String id);
void update(Emp emp);
}
EmpMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.feng.mapper.EmpMapper">
<cache type="com.feng.cache.RedisCache"/>
<!--findAll-->
<select id="getAllEmp" resultType="Emp">
select id,name,path,salary,age from t_emp
</select>
<insert id="save" parameterType="Emp" useGeneratedKeys="true" keyProperty="id">
insert into emp.t_emp
values (#{id},#{name},#{path},#{salary},#{age})
</insert>
<delete id="deleteEmp" parameterType="String">
delete from emp.t_emp where id=#{id}
</delete>
<select id="findById" parameterType="String" resultType="Emp">
select id,name,path,salary,age from t_emp where id=#{id}
</select>
<update id="update" parameterType="Emp">
update emp.t_emp set name=#{name},path=#{path},salary=#{salary},age=#{age} where id=#{id};
</update>
</mapper>
service层:
@Service
@Transactional
public class EmpServiceImpl implements EmpService{
@Autowired
private EmpMapper empMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<Emp> getAllEmp() {
return empMapper.getAllEmp();
}
@Override
public void save(Emp emp)
{
empMapper.save(emp);
}
@Override
public void deleteEmp(String id)
{
empMapper.deleteEmp(id);
}
@Override
public Emp findById(String id) {
return empMapper.findById(id);
}
@Override
public void update(Emp emp) {
empMapper.update(emp);
}
}
controller层:
@GetMapping("findAll")
public List<Emp> findAll()
{
return empService.getAllEmp();
}
好像,这个业务并没有什么难点吧
员工添加
在员工添加
在员工的展现页面添加了一个超链接
<input type="button" class="button" value="Add Employee" onclick="location='addEmp.html'"/>
,
用于我们跳转到员工添加页面,具体的句法就不说了,之前说过了。
addEmp.html中的核心代码:
<h1>
add Emp info:
</h1>
<form action="emplist.html" method="post">
<table cellpadding="0" cellspacing="0" border="0" class="form_table">
<tr>
<td valign="middle" align="right">
name:
</td>
<td valign="middle" align="left">
<input type="text" v-model="emp.name" class="inputgri" name="name" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
photo:
</td>
<td valign="middle" align="left">
<input type="file" ref="myPhoto" name="photo" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
salary:
</td>
<td valign="middle" align="left">
<input type="text" v-model="emp.salary" class="inputgri" name="salary" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
age:
</td>
<td valign="middle" align="left">
<input type="text " v-model="emp.age" class="inputgri" name="age" />
</td>
</tr>
</table>
<p>
<input type="button" @click="saveEmp" class="button" value="Confirm" />
</p>
</form>
<!--引入Vue相关的JS-->
<script src="/ems_vue/js/vue.js"></script>
<script src="/ems_vue/js/axios.min.js"></script>
<script>
var app = new Vue({
el: "#wrap",
data: {
user: {}, //用来保存用户登录的信息
emp: {
}
},
methods: {
//安全退出
logout(){
localStorage.removeItem("user");
location.reload(true); //刷新页面
},
//保存员工信息
saveEmp(){
console.log(this.emp);
console.log(this.$refs.myPhoto.files[0]); //获取文件信息
//文件上传时,请求方式必须时post enctype必须为multipart/form-data
let formData = new FormData();
formData.append("name",this.emp.name);
formData.append("salary",this.emp.salary);
formData.append("age",this.emp.age);
formData.append("photo",this.$refs.myPhoto.files[0]);
var _this = this;
axios({
method: "post",
url: "http://localhost:8080/ems_vue/emp/save",
data: formData,
headers: {
'content-type': 'multipart/form-data'
}
}).then(res=>{
console.log(res.data);
if(res.data.state){
if(window.confirm(res.data.msg+",点击确定跳转到列表页")){
location.href="/ems_vue/emplist.html"
}else{
_this.emp = {};
}
}else{
alert(res.data.msg);
}
});
}
},
created() {
var userString = localStorage.getItem("user");
if(userString){
var Luser = JSON.parse(userString);
this.user = Luser;
}else{
alert("您尚未登录,点击确定跳转至登录");
location.href="/ems_vue/login.html";
}
}
});
</script>
同样的方式,我们new 一个vue组件,并声明了一个emp的对象数据,用来保存我们要添加的数据。和用户注册一样,首先通过v-model
进行了数据双向绑定。
在这里我们主要重点说的是关于员工头像的保存和传输。在图片上传中,并没有采用Vue中的双向绑定。当时我们在Vue中使用文件上传的功能,我们需要把相关文件绑定给Vue中的ref
。
文件上传时,请求方式必须时post enctype必须为multipart/form-data
此时,我们只能通过axios的构造函数,进行配置axios请求
axios({
method: "post",
url: "http://localhost:8080/ems_vue/emp/save",
data: formData,
headers: {
'content-type': 'multipart/form-data'
}
}).then(res=>{
console.log(res.data);
if(res.data.state){
if(window.confirm(res.data.msg+",点击确定跳转到列表页")){
location.href="/ems_vue/emplist.html"
}else{
_this.emp = {};
}
}else{
alert(res.data.msg);
}
});
这样的请求方式实际上还是采用表单提交的方式,因此我们需要构架一个表单对象,
let formData = new FormData();
formData.append("name",this.emp.name);
formData.append("salary",this.emp.salary);
formData.append("age",this.emp.age);
formData.append("photo",this.$refs.myPhoto.files[0]);
var _this = this;
这里的formdata模拟的就是一个表单,我们可以向formdata中appenpd emp的信息。这样我们就可以进行数据的传输了。
mapper层、service在上面。
controller层:
@Value("${photo.dir}")
private String realPath;
//保存员工信息
@PostMapping("save")
public Map<String,Object> save(Emp emp, MultipartFile photo){
log.info("员工信息:[{}]",emp.toString());
log.info("头像信息:[{}]",photo.getOriginalFilename());
Map<String, Object> map = new HashMap<>();
try {
//头像的保存
String newFileName = UUID.randomUUID().toString()+"."+ FilenameUtils.getExtension(photo.getOriginalFilename());
photo.transferTo(new File(realPath,newFileName));
//设置头像地址
emp.setPath(newFileName);
//保存员工信息
empService.save(emp);
map.put("state",true);
map.put("msp","员工信息保存成功");
} catch (Exception e) {
e.printStackTrace();
map.put("msg","员工信息保存失败");
}
return map;
}
在这里我们需要注意的就是关于文件的保存。首先我们需要在配置文件中声明一个文件的上传的目录。
# 提供web资源访问功能
spring.web.resources.static-locations=classpath:/static/,file:${photo.dir}
# 必须是根目录
photo.dir=D:\\JavaProject\\ems_vue\\src\\main\\resources\\static\\photos
为了避免用户头像的文件名一致,我们需要修改文件名,
//头像名称修改
String newFileName = UUID.randomUUID().toString()+"."+FilenameUtils.getExtension(photo.getOriginalFilename());
新的文件名我们使用的是一个UUID的方式。
getExtension()
:获取文件的扩展名
用户头像的保存:
transferTo
,指定文件的上传,参数是一个File对象,指定了上传的路径和上传的文件名
photo.transferTo(new File(realPath,newFileName));
好了,其他的就不需要说明了。
用户更新
<td>
<a :href="'/ems_vue/updateEmp.html?id='+emp.id">update emp</a>
</td>
updateEmp.html核心代码
<tr>
<td valign="middle" align="right">
old photo:
</td>
<td valign="middle" align="left">
<img :src="'/ems_vue/'+emp.path" style="height: 60px;">
</td>
</tr>
<tr>
<td valign="middle" align="right">
name:
</td>
<td valign="middle" align="left">
<input type="text" v-model="emp.name" class="inputgri" name="name"/>
</td>
</tr>
<tr>
<td valign="middle" align="right">
photo:
</td>
<td valign="middle" align="left">
<input type="file" ref="photo" name="photo" />
</td>
</tr>
<tr>
<td valign="middle" align="right">
salary:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="salary" v-model="emp.salary"/>
</td>
</tr>
<tr>
<td valign="middle" align="right">
age:
</td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="age"v-model="emp.age"/>
</td>
</tr>
</table>
<p>
<input type="button" @click="editEmp" class"button" value="Confirm" />
</p>
</form>
<!--引入Vue相关的JS-->
<script src="/ems_vue/js/vue.js"></script>
<script src="/ems_vue/js/axios.min.js"></script>
<script>
var app = new Vue({
el: "#wrap",
data: {
user: {}, //用来保存用户登录的信息
emp: {
}
},
methods: {
//安全退出
logout(){
localStorage.removeItem("user");
location.reload(true); //刷新页面
},
//处理员工信息的修改
editEmp(){
console.log(this.emp);
console.log(this.$refs.photo.files[0]);
//文件上传时,请求方式必须时post enctype必须为multipart/form-data
let formData = new FormData();
formData.append("id",this.emp.id);
formData.append("name",this.emp.name);
formData.append("path",this.emp.path);
formData.append("salary",this.emp.salary);
formData.append("age",this.emp.age);
formData.append("photo",this.$refs.photo.files[0]);
var _this = this;
axios({
method: "post",
url: "http://localhost:8080/ems_vue/emp/update",
data: formData,
headers: {
'content-type': 'multipart/form-data'
}
}).then(res=>{
console.log(res.data);
if(res.data.state){
if(window.confirm(res.data.msg+",点击确定跳转到列表页")){
location.href="/ems_vue/emplist.html"
}
}else{
alert(res.data.msg);
}
});
}
},
created() {
var userString = localStorage.getItem("user");
if(userString){
var Luser = JSON.parse(userString);
this.user = Luser;
}else{
alert("您尚未登录,点击确定跳转至登录");
location.href="/ems_vue/login.html";
}
//获取对应的id信息
var start = location.href.lastIndexOf("=");
var id = location.href.substring(start+1);
console.log(id);
//查询一个人信息
axios.get("http://localhost:8080/ems_vue/emp/findById?id="+id).then(res=>{
var _this = this;
_this.emp = res.data;
console.log(res.data);
});
}
});
</script>
更新操作类似于添加用户操作一样,我们只需要注意传递用户id以及回显响相应用户信息的操作。
在跳转页面时,将请求的id也传递过去,这样我们便可以在渲染页面的时候向后台发送一个带有id的参数的axios请求,后台根据id查询要修改的用户,渲染页面的同时回显要更新的用户信息。
此时我们便可以进行对用户信息的更改了,这里便也不在多说了。操作和添加操作一致。
后端接口
同样的mapper层和service层不在重复说了。
controller层:
//查询用户,回显信息
@GetMapping("findById")
public Emp findById(String id)
{
log.info("查询员工信息的id:[{}]",id);
return empService.findById(id);
}
//更新用户信息
@PostMapping("update")
public Map<String,Object> update(Emp emp,MultipartFile photo)
{
log.info("员工信息:[{}]",emp.toString());
Map<String, Object> map = new HashMap<>();
try {
if(photo != null && photo.getSize() != 0)
{
log.info("头像信息:[{}]",photo.getOriginalFilename());
//头像的保存
String newFileName = UUID.randomUUID().toString()+"."+ FilenameUtils.getExtension(photo.getOriginalFilename());
photo.transferTo(new File(realPath,newFileName));
//设置头像地址
emp.setPath(newFileName);
}
//保存员工信息
empService.update(emp);
map.put("state",true);
map.put("msp","员工信息保存成功");
} catch (Exception e) {
e.printStackTrace();
map.put("msg","员工信息保存失败");
}
return map;
}
在这里我们需要注意一点,如果照片为空的话,那样我们就不需要重新设置图片的地址了,这样能够提高我们的效率。
删除用户
<a href="javascript:;" @click="delEmp(emp.id)">delete emp</a>
这里的href
并不是要给具体超链接地址了。
javascript:;
:代表了当我们开始点击这个button
时,此时会执行一个代码块,也是就delEmp
,同时我们需要传递一个id属性。
//删除员工
delEmp(id){
if(window,confirm("确定要删除这条员工信息吗")){
var _this = this;
axios.get("http://localhost:8080/ems_vue/emp/delete?id="+id).then(res=>{
if(res.data.state){
alert(res.data.msg+"点击确定刷新数据");
_this.findAll(); //重新加载数据
}else{
alert(res.data.msg);
}
});
}
},
这里也不讲太多了,没有什么重要的知识了。
当然,还有一个退出的操作,这里就不贴出来,大家能否写出是怎么实现的吗?
添加Redis
添加依赖
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
添加redis的配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0
@Slf4j
public class RedisCache implements Cache {
private String id;
@Autowired
private RedisTemplate redisTemplate;
public RedisCache(String id) {
log.info("放入缓存的信息:[{}]",id);
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
//放入redis缓存
public void putObject(Object key, Object value)
{
log.info("放入缓存的key:[{}] 放入缓存的value[{}]",key,value);
getRedisTemplate().opsForHash().put(id,key.toString(),value);
}
@Override
//获取缓存
public Object getObject(Object key) {
log.info("获取缓存的key:[{}]",key.toString());
return getRedisTemplate().opsForHash().get(id,key.toString());
}
@Override
//删除指定缓存
public Object removeObject(Object o) {
return null;
}
@Override
public void clear() {
log.info("清楚所有缓存");
getRedisTemplate().delete(id);
}
@Override
public int getSize() {
return getRedisTemplate().opsForHash().size(id).intValue();
}
//封装获取redisTemplate
public RedisTemplate getRedisTemplate()
{
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
首先,我们需要实现Mybatis中的Cache接口,但是由于这个对象不是有Spring 工厂构建的,所以我们不用通过Spring的自动注入的方式获取到redisTemplate,因此我们需要开发一个工具类,用来获取到redisTemplate。
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static Object getBean (String name)
{
return applicationContext.getBean(name);
}
}
当我们实现了ApplicationContextAware
这个接口后,它会将Springboot自动创建好的工厂给我们。通过这个工具类,我们便可以拿到redisTemplate。
结束
这次的总结到了这里也就该结束了。有什么问题不妨可以留个言。或者有什么好的想法可以评论出来。一起进步