赞
踩
技术选型
前端: vue + axios
后端: springboot + mysql + tomcat +Redis
用户模块
员工管理模块
用户表
create table t_user(
id int(6) primary key auto_increment,
username varchar(60),
realname varchar(60),
password varchar(60),
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)
);
数据库 emp
环节搭建
springboot + mybatis + mysql 引入员工管理系统页面
项目名: ems_vue
项目中包: src/main/java
src/main/resource
application.yml(properties) springboot配置文件
application-dev.yml 测试
application-prod.yml 生产配置
com/onlylmf/mapper/ 用来存放mybatis的mapper的配置文件
com/onlylmf/sql/ 用来存放项目中数据库文件
static 用来存放静态资源
准备工作, 创建user emp实体类
@Data @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor @ToString 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 registerTime; } --------------------------------------- @Data @Accessors(chain = true) //Serializable 序列化 public class Emp implements Serializable { private String id; private String name; private String path; private Double salary; private Integer age; }
前端img标签显示base64格式的图片使用方式
例如直接在后端生成base64字节的同时前面进行拼接
String s = "data:image/png;base64," + Base64Utils.encodeToString(byteArrayOutputStream.toByteArray());
实现前台验证码的展示以及换一张
<script> const app = new Vue({ el: "#wrap", data: { url: "", }, methods:{ //用来更换验证码实现 getImg(){ var _this = this; axios.get("http://localhost:8989/ems_vue/user/getImage"+Math.random()).then(res=>{ console.log(res.data); _this.url = res.data; }) } }, created() { var _this = this; //在请求路径后加了一个时间戳,防止浏览器有缓存无法刷新出新的验证码 axios.get("http://localhost:8989/ems_vue/user/getImage"+Math.random()).then(res=>{ console.log(res.data); _this.url = res.data; }) } }) </script>
由于有两段代码重复使用 不太好,可以进行优化
<script> const app = new Vue({ el: "#wrap", data: { url: "", }, methods:{ //用来更换验证码实现 getImg(){ this.getSrc(); }, //代码的复用 getSrc(){ var _this = this; //在路径后添加random随机戳是为了浏览器出现缓存的情况验证码不会刷新 axios.get("http://localhost:8989/ems_vue/user/getImage"+Math.random()).then(res=>{ console.log(res.data); _this.url = res.data; }) } }, created() { this.getSrc(); } }) </script>
由于是前后端分离项目 无法将用户信息存储至session中
所以可以将信息存放至浏览器的Local Storage中保存
获取数据库数据并展示
empDao
@Mapper
@Repository
public interface EmpDao {
List<Emp> findAll();
}
EmpDaoMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.onlylmf.dao.EmpDao">
<!--findAll-->
<select id="findAll" resultType="Emp">
select id,name,path,salary,age from t_emp
</select>
</mapper>
EmpService
public interface EmpService {
List<Emp> findAll();
}
EmpServiceImpl
/** * 业务层处理的三件事 * 1. 处理业务逻辑 * 2. 调用DAO * 3. 控制事务 */ @Service @Transactional public class EmpServiceImpl implements EmpService{ @Autowired private EmpDao empDao; @Override @Transactional(propagation = Propagation.SUPPORTS) public List<Emp> findAll() { return empDao.findAll(); } }
EmoController
@RestController
@RequestMapping("emp")
@CrossOrigin //允许跨域
public class EmpController {
@Autowired
private EmpService empService;
//获取员工列表的方法
@GetMapping("findAll")
public List<Emp> findAll(){
return empService.findAll();
}
}
前台html页面展示
<script> const app = new Vue({ el: "#wrap", data: { //数据 user: {}, //用来存放用户登录信息 emps: [], //接收查询emp的数据 }, methods: { //自定义函数 logout(){ //安全退出 localStorage.removeItem("user"); location.reload(true); } }, created(){ //声明周期函数 在页面加载前运行 var userString = localStorage.getItem("user"); //判断如果没有登录就返回登录页面(判断localStorage中有无数据) if (userString){ this.user = JSON.parse(userString); }else { alert("很抱歉,您尚未登录,点击确定跳转登录") location.href="http://localhost:8989/ems_vue/login.html"; } //查询所有员工 var _this = this; axios.get("http://localhost:8989/ems_vue/emp/findAll").then(res=>{ console.log(res); _this.emps = res.data; }); } }) </script>
在页面中进行数据展示
<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="emp.path" style="height: 60px;"> </td> <td> {{emp.salary}} </td> <td> {{emp.age}} </td> <td> <a href="emplist.html">delete emp</a> <a href="updateEmp.html">update emp</a> </td> </tr>
添加员工并上传图片
首先将addEmp.html页面对应的input标签添加v-model="xxx"属性
上传文件的input标签添加 ref="myPhoto"标签(myPhoto为自己定义的方法)
//先在data中定义一个emp: [],用来存员工信息
//在methods中定义myPhoto方法
//保存员工信息
saveEmp(){
console.log(this.emp); //获取员工信息
console.log(this.$refs.myPhoto.files[0]); //获取文件信息
}
测试,在控制台可以得到信息
依次在empDao EmpDaoMapper empService创建对应的save方法以及EmpServiceImpl实现类
//接口
void save(User user);
//xml
<!--save-->
<insert id="save" parameterType="Emp" useGeneratedKeys="true" keyProperty="id">
insert into t_emp values (#{id},#{name},#{path},#{salary},#{age});
</insert>
// EmpServiceImpl
@Override
public void save(Emp emp) {
empDao.save(emp);
}
在static目录中创建photo文件夹存放图片
然在application.properties配置文件中定义一个图片存储路径以及开启访问本地静态资源目录
#开放访问本地静态资源目录
spring.web.resources.static-locations=classpath:/static/,file:${photo.dir}
#定义一个图片存储的绝对路径,在项目中引入(就是photo的本地路径)
photo.dir=E:\\idea\\springBoot\\ems_vue\\src\\main\\resources\\static\\photos
在EmpController中创建save方法
@Value("${photo.dir}") private String realPath; //保存用户信息 @PostMapping("save") public Map<String,Object> save(Emp emp, MultipartFile photo) throws IOException { log.info("员工信息[{}]",emp.toString()); log.info("头像信息[{}]",photo.getOriginalFilename()); Map<String,Object> map = new HashMap<>(); try { //头像保存 String newFileName = UUID.randomUUID().toString() + "." + photo.getOriginalFilename(); photo.transferTo(new File(realPath,newFileName)); //设置头像地址 emp.setPath(newFileName); //保存信息 empService.save(emp); map.put("state",true); map.put("msg","员工信息保存成功"); } catch (Exception e) { e.printStackTrace(); map.put("state",false); map.put("msg","员工信息保存失败"); } return map; }
前台页面配置
<script> const 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 var 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:8989/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"); //判断如果没有登录就返回登录页面(判断localStorage中有无数据) if (userString){ this.user = JSON.parse(userString); }else { alert("很抱歉,您尚未登录,点击确定跳转登录") location.href="http://localhost:8989/ems_vue/login.html"; } } }) </script>
随后在emplist.html员工展示页面进行图片展示的设置
以此来获取图片的本地地址进行回显
一般上传数据时有文件存在会使用formData
formData
的append
方法和set方法都是会在对象里的某个key
设置一个新的值,如果该
key
不存在,则添加。
set()
和 FormData.append
不同之处在于:如果某个 key 已经存在,set()
会直接覆盖所有该 key 对应的值,而 FormData.append
则是在该 key 的最后位置再追加一个值。
删除功能
在empDao , EmpDaoMapper.xml, EmpService, EmpServiceImpl添加接口以及实现了和删除语句
//empDao, EmpService void delete(String id); Emp findOne(String id); // EmpDaoMapper.xml <!--delete--> <delete id="delete" parameterType="String"> delete from t_emp where id = #{id} </delete> <!--findOne--> <select id="findOne" parameterType="String" resultType="Emp"> select id,name,path,salary,age from t_emp where id = #{id} </select> //EmpServiceImpl @Override public void delete(String id) { empDao.delete(id); } @Override public Emp findOne(String id) { return empDao.findOne(id); }
在EmpController中编写删除方法
根据路径将本地的图片也一并删除
//员工的删除 @GetMapping("delete") public Map<String,Object> delete(String id){ log.info("删除员工的id: [{}]",id); Map<String, Object> map = new HashMap<>(); try { //删除员工头像 Emp one = empService.findOne(id); File file = new File(realPath, one.getPath()); if (file.exists()){ file.delete(); //删除头像 } //删除员工信息 empService.delete(id); map.put("state",true); map.put("msg","删除成功"); } catch (Exception e) { e.printStackTrace(); map.put("state",false); map.put("msg","删除失败"); } return map; }
页面的处理
给删除链接绑定一个方法
在methods中定义一个delEmp方法
//删除用户 delEmp(id){ if (window.confirm("确定要删除这条员工信息吗?")){ var _this = this; axios.get("http://localhost:8989/ems_vue/emp/delete?id=" + id).then(res=>{ if (res.data.state){ alert(res.data.msg + "点击确定刷新数据") //重新加载数据,相当于再次查询了员工列表 _this.findAll() }else{ alert(res.data.msg) } }); } } ---------------------------------------- // _this.findAll是定义的一个共用的方法 //查询员工列表 findAll(){ var _this = this; axios.get("http://localhost:8989/ems_vue/emp/findAll").then(res=>{ console.log(res); _this.emps = res.data; }); }
修改功能
因为修改需要查询该用户的信息,所以用到该用户的id
在员工列表页update emp链接拼上id
依次创建接口和方法
EmpDao、EmpService
void update(Emp emp);
EmpDaoMapper.xml
<!--update-->
<update id="update" parameterType="Emp">
update t_emp set
name =#{name},
path = #{path},
salary = #{salary},
age = #{age}
where
id = #{id}
</update>
EmpServiceImpl
@Override
public void update(Emp emp) {
empDao.update(emp);
}
在EmpController中编写update方法
//修改用户信息 @PostMapping("update") public Map<String,Object> update(Emp emp, MultipartFile photo) throws IOException { 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() + "." + photo.getOriginalFilename(); photo.transferTo(new File(realPath,newFileName)); //设置头像地址 emp.setPath(newFileName); } //保存信息 empService.update(emp); map.put("state",true); map.put("msg","员工信息保存成功"); } catch (Exception e) { e.printStackTrace(); map.put("state",false); map.put("msg","员工信息保存失败"); } return map; }
在updateEmp.html页面进行用户的信息显示
使用v-model进行绑定数据
使图片显示,则使用拼贴路径的方式展示
数据显示结束后,给input提交按钮添加单击事件
编写js代码
<script> var app = new Vue({ el: "#wrap", data: { //数据 user:{ realname:"", },//用来存放用户登录信息 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 var 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:8989/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){ this.user = JSON.parse(userString); }else{ alert("您尚未登录,点击确定跳转至登录页面!"); location.href ="/ems_vue/login.html"; } //获取对应id信息 var start = location.href.lastIndexOf("="); var id = location.href.substring(start+1); console.log(id); //查询一个人信息 var _this = this; axios.get("http://localhost:8989/ems_vue/emp/findOne?id="+id).then(res=>{ console.log(res.data); _this.emp = res.data; }); } }); </script>
整合Redis实现缓存
将redis服务启动
打开redis-server 和 redis-cli
导入springboot-redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置redis信息
#Redis配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0
新建cache包下建立RedisCache
实现Cache接口
package com.onlylmf.cache; import com.onlylmf.utils.ApplicationContextUtils; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.cache.Cache; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Slf4j public class RedisCache implements Cache { private String id; public RedisCache(String 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//从redis缓存获取 public Object getObject(Object key) { log.info("放入的缓存key: [{}]",key.toString()); return getRedisTemplate().opsForHash().get(id,key.toString()); } @Override//删除指定的缓存信息 public Object removeObject(Object key) { log.info("放入的缓存key: [{}]",key); return getRedisTemplate().opsForHash().delete(id,key); } @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; } }
由于已经实现了接口所以无法直接注入
创建工具类ApplicationContextUtils
并实现ApplicationContextAware接口
@Component public class ApplicationContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } //redisTemplate stringRedisTemplate public static Object getBean(String name){ return applicationContext.getBean(name); } }
在mapper中加入cache标签
<cache type="com.onlylmf.cache.RedisCache"/>
启动测试
}
}
由于已经实现了接口所以无法直接注入
创建工具类ApplicationContextUtils
并实现ApplicationContextAware接口
@Component public class ApplicationContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } //redisTemplate stringRedisTemplate public static Object getBean(String name){ return applicationContext.getBean(name); } }
在mapper中加入cache标签
<cache type="com.onlylmf.cache.RedisCache"/>
启动测试
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。