Version: Next
10. 前后端分离——Vue结合BootStrap与SpringBoot
一个完整的前后端分离案例
新建SpringBoot项目
下载Vue、Bootstrap和axios
删除
resources
目录下的static
和templates
文件夹新建
/src/main/webapp
文件夹在项目中,引入Vue.js、axios.js,BootStrap的css和fonts
导入fastjson
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.45</version></dependency>
10.1 基本页面
users.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="submit" class="btn btn-info">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr>
<td>1</td>
<td>Alice</td>
<td>123456</td>
<td>
<button class="btn btn-primary btn-sm">修改</button> <button class="btn btn-danger btn-sm">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="password">
</div>
<button type="button" class="btn btn-info">提交</button>
<button type="button" class="btn btn-danger">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
</body>
</html>
10.2 查询所有
将页面要展示的信息绑定到Vue中,将整个页面从导航到末尾用一个新
div
标签包裹起来
当页面一加载完
,我们就希望前端向后端Controller请求数据,并把响应显示在页面上
users
数据复制应该来源于后端服务接口,并再页面加载完之后users
中包含数据
- 生命周期
created
阶段——此时可访问到data中的数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="submit" class="btn btn-info">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr v-for="u in users" :key="u.id">
<td>{ {u.id} }</td>
<td>{ {u.name} }</td>
<td>{ {u.password} }</td>
<td>
<button class="btn btn-primary btn-sm">修改</button> <button class="btn btn-danger btn-sm">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="password">
</div>
<button type="button" class="btn btn-info">提交</button>
<button type="button" class="btn btn-danger">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
</div>
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
<script>
const vue = new Vue({
el: "#app",
data: {
users: [] // 从后端请求然后填充
},
methods: {},
//生命周期函数created
created() {
//发送查询所有用户信息操作
axios.get("")
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
})
}
})
</script>
</body>
</html>
新建数据库表
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30) DEFAULT NULL,
PASSWORD VARCHAR(20) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
SpringBoot整合Mybatis、Druid (具体看SpringBoot笔记)
- pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
- application.yaml
spring:
datasource:
username: root
password: root
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 配置使用Druid数据源
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Accessors(chain = true)
public class User {
private Integer id;
private String name;
private String password;
}
mapper & mapper.xml
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
}
<?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.example.demo.dao.UserMapper">
<select id="findAll" resultType="com.example.demo.pojo.User">
SELECT * FROM USER;
</select>
</mapper>
Service & ServiceImpl
public interface UserService {
//查询所有用户
List<User> findAll();
}
@Service
@Transactional //事务
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
}
Controller
- 没有模板引擎的情况下,纯HTML访问后端接口,需要开启跨域
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findAll")
@CrossOrigin // 跨域
public List<User> findAll(){
List<User> users = userService.findAll();
return users;
}
}
前端页面:
- 修改
axios
部分代码,将异步请求路径设为后端定义的URL- 获取后端响应,将响应的
data
域赋值到Vue的data
域
<script>
const vue = new Vue({
el: "#app",
data: {
users: [] // 从后端请求然后填充
},
methods: {},
//生命周期函数created
created() {
//发送查询所有用户信息操作
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
})
</script>
10.3 添加
前端:
- 为提交按钮绑定单击事件
saveUserInfo
- 获取表单数据
- 在
data
域单独定义一个user
对象- 用
v-model
来完成数据双向绑定
v-model="user.id"
v-model="user.name"
v-model="user.password"
- 在
saveUserInfo
方法中发送axios.post
请求到后端服务器,传递data
中的user
对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="submit" class="btn btn-info">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr v-for="u in users" :key="u.id">
<td>{ {u.id} }</td>
<td>{ {u.name} }</td>
<td>{ {u.password} }</td>
<td>
<button class="btn btn-primary btn-sm">修改</button> <button class="btn btn-danger btn-sm">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名" v-model="user.name">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="密码" v-model="user.password">
</div>
<button type="button" class="btn btn-info" @click="saveUserInfo">提交</button>
<button type="button" class="btn btn-danger">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
</div>
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
<script>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
console.log(response.data);
})
.catch(function(error) {
console.log(error);
});
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
})
</script>
</body>
</html>
后端:
- mapper定义保存用户抽象方法
- mapper.xml定义sql语句
- service层
- 添加
/save
路由,接受user
对象,插入数据库- 向前端响应一个Map,包含
success
键包含boolean
类型值说明添加成功或失败;message
键包含说明信息
- mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
int save(User user);
}
- mapper.xml
<insert id="save" parameterType="com.example.demo.pojo.User">
insert into user (id, name, password) values (null, #{name}, #{password})
</insert>
- Service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
}
@Service
@Transactional //事务
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public int save(User user) {
return userMapper.save(user);
}
}
- Controller
@PostMapping("/save")
@CrossOrigin
public Map<String, Object> save(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
try {
userService.save(user);
map.put("success", true);
map.put("message","用户保存成功");
} catch (Exception e) {
map.put("success", false);
map.put("message","用户保存失败");
}
return map;
}
前端:
- 将
查询所有
的代码封装为独立方法findAll
- 根据后端响应的
scuccess
键,决定是否调用findAll
方法刷新页面列表
<script>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
this.findAll();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})
</script>
10.4 删除
10.4.1 表单重置按钮
- 在
重置
按钮上添加单击事件- 在事件中将
data
域中的user
对象置为{}
<button type="button" class="btn btn-danger" @click="reset">重置</button>
reset() {
this.user = {};
}
10.4.2 删除用户
前端
- 单击
删除
按钮,绑定单击事件,事件触发时传递id
- 在事件中向后端发送
delete
请求,传递用户id
- 接收后端响应
success
,以此决定是否执行findAll
刷新页面列表
//省略添加单击事件的代码
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
},
reset() {
this.user = {};
},
deleteUser(id) {
axios.delete("http://localhost:8080/user/delete/" + id)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
});
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})
后端
- 接受前端发送的
id
- 根据
id
删除数据库对应记录- 向前端响应结果信息
success
- Mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
int save(User user);
void delete(Integer id);
}
- mapper.xml
<delete id="delete">
delete from user where id = #{id}
</delete>
- service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
void delete(Integer id);
}
@Service
@Transactional //事务
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public int save(User user) {
return userMapper.save(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void delete(Integer id) {
userMapper.delete(id);
}
}
- controller
@DeleteMapping("/delete/{id}")
public Map<String, Object> delete(@PathVariable("id") Integer id) {
Map<String, Object> map = new HashMap<>();
try {
userService.delete(id);
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "删除失败");
}
return null;
}
10.5 修改
- 当单击
修改
按钮时,将对应用户信息显示在页面右侧输入框中提交
行为根据表单中有没有id
属性来决定具体行为
- 有
id
:单击提交
进行修改- 无
id
:单击提交
进行添加
<button class="btn btn-primary btn-sm" @click="findOneUser(u)">修改</button>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {}
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
vue.findAll();
vue.reset();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
},
reset() {
this.user = {};
},
deleteUser(id) {
axios.delete("http://localhost:8080/user/delete/" + id)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
});
},
findOneUser(u) {
this.user = u;
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})
后端:
- 定义根据
id
修改用户信息的方法
- mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
//修改一个人
void update(User user);
int save(User user);
void delete(Integer id);
}
- mapper.xml
<update id="update" parameterType="com.example.demo.pojo.User">
update user set name = #{name}, password = #{password} where id = #{id}
</update>
- service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
void delete(Integer id);
void update(User user);
}
- ServiceImpl
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void update(User user) {
userMapper.update(user);
}
- controller
修改
/save
路由,判断传进来的user对象是否有id
属性- 有
id
:修改 - 没
id
:添加
- 有
@PostMapping("/save")
public Map<String, Object> save(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
try {
if (null != user.getId()) {
userService.update(user);
} else {
userService.save(user);
}
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "操作失败");
}
return map;
}
10.6 搜索
根据
姓名
或者id
进行模糊搜索
后端
- 使用动态SQL实现模糊查询
- 响应查询用户列表给前端
- mapper
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(@Param("name") String name,@Param("id") Integer id);
- mapper.xml
在mybatis中模糊查询使用
concat
表达式
<!--模糊搜索-->
<select id="searchByNameOrId" resultType="com.example.demo.pojo.User">
SELECT * FROM USER
<where>
<if test="name!='' and name!=null">
NAME LIKE concat('%', #{name}, '%')
</if>
<if test="id!='' and id!=null">
OR id LIKE concat('%', #{id}, '%')
</if>
</where>
</select>
- service
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(String name, Integer id);
- serviceImpl
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> searchByNameOrId(String name, Integer id) {
return userMapper.searchByNameOrId(name, id);
}
- controller
- 利用
fastJson
的JSONObject
类接收json类型的请求参数
- 利用
//模糊搜索 利用fastJson接收json格式的参数
@PostMapping("/search")
public List<User> searchByNameOrId(@RequestBody JSONObject jsonParams) {
String name = jsonParams.get("name") + "";
Integer id;
if (null == jsonParams.get("id") || "".equals(jsonParams.get("id"))) {
id = null;
} else {
id = Integer.parseInt(jsonParams.get("id").toString());
}
return userService.searchByNameOrId(name, id);
}
前端
- 给
搜索
按钮绑定单击事件,使用对象searchObj
封装name
和id
- POST方式提交
name
,id
值到/search
路由- 将后端响应的用户列表进行显示
- 搜索组件与单击事件
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" v-model="searchObj.name" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" v-model="searchObj.id" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="button" class="btn btn-info" @click="search">搜索</button>
</form>
</div>
</div>
- 事件函数
search() { //搜索方法
axios.post("http://localhost:8080/user/search", this.searchObj)
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
data
域添加searchObj
data: {
users: [], // 从后端请求然后填充
user: {},
searchObj: {} // 搜索实体
},
10.7 完整代码
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理界面</title>
<!-- 引入bootstrap-->
<link rel="stylesheet" href="./bootvue/css/bootstrap.css">
<link rel="stylesheet" href="./bootvue/css/bootstrap-theme.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"
aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">用户管理系统</a>
</div>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">欢迎xx进入系统</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<!-- 中心布局-->
<div class="container-fluid">
<!-- 搜索-->
<div class="row">
<div class="col-md-8 col-md-offset-1">
<form class="form-inline">
<div class="form-group">
<label for="searchName">姓名</label>
<input type="text" v-model="searchObj.name" class="form-control" id="searchName" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="searchId">ID</label>
<input type="text" v-model="searchObj.id" class="form-control" id="searchId" placeholder="请输入id">
</div>
<button type="button" class="btn btn-info" @click="search">搜索</button>
</form>
</div>
</div>
<!-- 表格-->
<div class="row" style="margin-top: 20px">
<div class="col-md-8">
<!-- 用户信息展示-->
<table class="table table-hover table-striped">
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>操作</th>
</tr>
<tr v-for="u in users" :key="u.id">
<td>{ {u.id} }</td>
<td>{ {u.name} }</td>
<td>{ {u.password} }</td>
<td>
<button class="btn btn-primary btn-sm" @click="findOneUser(u)">修改</button> <button class="btn btn-danger btn-sm"
@click="deleteUser(u.id)">删除
</button>
</td>
</tr>
</table>
</div>
<!--右侧-->
<div class="col-md-4">
<form>
<div class="form-group">
<label for="name">姓名</label>
<input type="text" class="form-control" id="name" placeholder="姓名" v-model="user.name">
</div>
<div class="form-group">
<label for="password">password</label>
<input type="text" class="form-control" id="password" placeholder="密码" v-model="user.password">
</div>
<button type="button" class="btn btn-info" @click="saveUserInfo">提交</button>
<button type="button" class="btn btn-danger" @click="reset">重置</button>
</form>
</div>
<!--右侧-->
</div>
</div>
<!-- 中心布局-->
</div>
<!--引入vue-->
<!--引入axios-->
<script src="./bootvue/js/vue.js"></script>
<script src="./bootvue/js/axios.min.js"></script>
<script>
const vue = new Vue({
el: "#app",
data: {
users: [], // 从后端请求然后填充
user: {},
searchObj: {} // 搜索实体
},
methods: {
saveUserInfo() {
axios.post("http://localhost:8080/user/save", this.user)
.then(function(response) {
if (response.data.success) {
vue.findAll();
vue.reset();
} else {
alert(response.data.message);
}
})
.catch(function(error) {
console.log(error);
});
},
findAll() {
axios.get("http://localhost:8080/user/findAll")
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
},
reset() {
this.user = {};
},
deleteUser(id) {
axios.delete("http://localhost:8080/user/delete/" + id)
.then(function(response) {
if (response.data.success) {
vue.findAll();
} else {
alert(response.data.message);
}
});
},
findOneUser(u) {
this.user = u;
},
search() { //搜索方法
axios.post("http://localhost:8080/user/search", this.searchObj)
.then(function(response) {
vue.users = response.data;
})
.catch(function(error) {
console.log(error);
})
}
},
//生命周期函数created
created() {
//发送查询所有用户信息操作
this.findAll();
}
})
</script>
</body>
</html>
后端
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- application.yaml
spring:
datasource:
username: root
password: root
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 配置使用Druid数据源
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- mapper
@Mapper
@Repository
public interface UserMapper {
//查询所有
List<User> findAll();
//修改一个人
void update(User user);
int save(User user);
void delete(Integer id);
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(@Param("name") String name,@Param("id") Integer id);
}
- mapper.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.example.demo.dao.UserMapper">
<select id="findAll" resultType="com.example.demo.pojo.User">
SELECT * FROM USER;
</select>
<insert id="save" parameterType="com.example.demo.pojo.User">
insert into user (id, name, password) values (null, #{name}, #{password})
</insert>
<delete id="delete">
delete from user where id = #{id}
</delete>
<update id="update" parameterType="com.example.demo.pojo.User">
update user set name = #{name}, password = #{password} where id = #{id}
</update>
<!--模糊搜索-->
<select id="searchByNameOrId" resultType="com.example.demo.pojo.User">
SELECT * FROM USER
<where>
<if test="name!='' and name!=null">
NAME LIKE concat('%', #{name}, '%')
</if>
<if test="id!='' and id!=null">
OR id LIKE concat('%', #{id}, '%')
</if>
</where>
</select>
</mapper>
- service
public interface UserService {
//查询所有用户
List<User> findAll();
int save(User user);
void delete(Integer id);
void update(User user);
//根据姓名或者id进行模糊搜索
List<User> searchByNameOrId(String name, Integer id);
}
- serviceImpl
@Service
@Transactional //事务
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findAll() {
return userMapper.findAll();
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public int save(User user) {
return userMapper.save(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void delete(Integer id) {
userMapper.delete(id);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public void update(User user) {
userMapper.update(user);
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> searchByNameOrId(String name, Integer id) {
return userMapper.searchByNameOrId(name, id);
}
}
- Controller
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/findAll")
public List<User> findAll() {
List<User> users = userService.findAll();
return users;
}
@PostMapping("/save")
public Map<String, Object> save(@RequestBody User user) {
Map<String, Object> map = new HashMap<>();
try {
if (null != user.getId()) {
userService.update(user);
} else {
userService.save(user);
}
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "操作失败");
}
return map;
}
@DeleteMapping("/delete/{id}")
public Map<String, Object> delete(@PathVariable("id") Integer id) {
Map<String, Object> map = new HashMap<>();
try {
userService.delete(id);
map.put("success", true);
} catch (Exception e) {
map.put("success", false);
map.put("message", "删除失败");
}
return map;
}
//模糊搜索 利用fastJson接收json格式的参数
@PostMapping("/search")
public List<User> searchByNameOrId(@RequestBody JSONObject jsonParams) {
String name = jsonParams.get("name") + "";
Integer id;
if (null == jsonParams.get("id") || "".equals(jsonParams.get("id"))) {
id = null;
} else {
id = Integer.parseInt(jsonParams.get("id").toString());
}
return userService.searchByNameOrId(name, id);
}
}