系列文章:
SpringBoot + Vue前后端分离项目实战 || 一:Vue前端设计
SpringBoot + Vue前后端分离项目实战 || 二:Spring Boot后端与数据库连接
SpringBoot + Vue前后端分离项目实战 || 三:Spring Boot后端与Vue前端连接
SpringBoot + Vue前后端分离项目实战 || 四:用户管理功能实现
SpringBoot + Vue前后端分离项目实战 || 五:用户管理功能后续
之前介绍了用户管理界面的数据展示,接下来需要做新增、修改、删除用户的功能
在user.vue
编写弹窗对话框的代码,该代码同样来自Element-UI组件
<el-dialog @close="clearForm" :title="title" :visible.sync="dialogFormVisible">
<el-form :model="userForm" ref="userFormRef" :rules="rules">
<el-form-item label="用户名" prop="username" :label-width="formLabelWidth">
<el-input v-model="userForm.username" autocomplete="off">el-input>
el-form-item>
<el-form-item v-if="userForm.id == null || userForm.id == undefined"
label="登录密码" prop="password" :label-width="formLabelWidth">
<el-input type="password" v-model="userForm.password" autocomplete="off">el-input>
el-form-item>
<el-form-item label="联系电话" prop="phone" :label-width="formLabelWidth">
<el-input v-model="userForm.phone" autocomplete="off">el-input>
el-form-item>
<el-form-item label="用户状态" :label-width="formLabelWidth">
<el-switch v-model="userForm.status"
:active-value="1"
:inactive-value="0"
active-color="#13ce66"
inactive-color="#ff4949">
el-switch>
el-form-item>
<el-form-item label="电子邮件" prop="email" :label-width="formLabelWidth">
<el-input v-model="userForm.email" autocomplete="off">el-input>
el-form-item>
el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消el-button>
<el-button type="primary" @click="saveUser">确 定el-button>
div>
el-dialog>
methods
中:title
是变量,所以需要注册红:表单数据的一级变量名,若要访问里面的数据可用userForm.username
,这是input中的v-model
字段,该字段应与后端实体类名一致。 同时item中prop
字段也与后端一致
橙:ref
用于表单校验时的变量名,前面clearForm
中有用到
白:定义表单校验规则,如下代码可在Element-UI
官网中找到,代码写在data
的return
区域内,不是methods内
rules:{ // 表单校验
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入初始密码', trigger: 'blur' },
{ min: 6, max: 16, message: '长度在 6 到 16 个字符', trigger: 'blur' }
],
email: [
{ required: true, message: '请输入电子邮件', trigger: 'blur' },
{ validator: checkEmail, trigger: 'blur' }
],
phone: [
{ validator: checkPhone, trigger: 'blur' }
],
},
规则校验中有两个是自定义的规则校验:checkEmail
、checkPhone
,这两个为正则表达式,可百度搜索邮箱、电话的效验规则,此处拿过来直接用。注意这两个校验规则写在data
里,而不是data\return
里
var checkEmail = (rule, value, callback) => {
var reg = /^([a-zA-Z\d][\w-]{2,})@(\w{2,})\.([a-z]{2,})(\.[a-z]{2,})?$/
if (!reg.test(value)) {
return callback(new Error('邮箱格式错误'));
}
callback(); // 效验成功
};
var checkPhone = (rule, value, callback) => {
var reg = /^[1]+\d{10}$/
if (!reg.test(value)) {
return callback(new Error('请输入正确的手机号码'));
}
callback(); // 效验成功
};
userForm.id
访问,存在的话说明是修改用户,会将该用户的信息展示出来,再进行修改。取消
按钮,将对话框的显示属性设为false
,不可见保存
按钮触发的函数:saveUser
,此函数的操作流程在注释中已说明saveUser(){
// 触发表单验证
this.$refs.userFormRef.validate((valid) => {
if (valid) { // 验证通过
// 数据传给后端
userApi.saveUser(this.userForm).then(response=>{
// 提交成功后的操作
// 插入数据成功提示
this.$message({
message: response.message,
type: 'success'
});
// 关闭对话框,清除表单数据
this.dialogFormVisible = false;
this.clearForm();
// 刷新表格
this.getUserList();
});
} else { // 验证失败
console.log('error submit!!');
return false;
}
});
},
所有对话框表单的vue代码如下
<el-dialog @close="clearForm" :title="title" :visible.sync="dialogFormVisible">
<el-form :model="userForm" ref="userFormRef" :rules="rules">
<el-form-item label="用户名" prop="username" :label-width="formLabelWidth">
<el-input v-model="userForm.username" autocomplete="off">el-input>
el-form-item>
<el-form-item v-if="userForm.id == null || userForm.id == undefined"
label="登录密码" prop="password" :label-width="formLabelWidth">
<el-input type="password" v-model="userForm.password" autocomplete="off">el-input>
el-form-item>
<el-form-item label="联系电话" prop="phone" :label-width="formLabelWidth">
<el-input v-model="userForm.phone" autocomplete="off">el-input>
el-form-item>
<el-form-item label="用户状态" :label-width="formLabelWidth">
<el-switch v-model="userForm.status"
:active-value="1"
:inactive-value="0"
active-color="#13ce66"
inactive-color="#ff4949">
el-switch>
el-form-item>
<el-form-item label="电子邮件" prop="email" :label-width="formLabelWidth">
<el-input v-model="userForm.email" autocomplete="off">el-input>
el-form-item>
el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消el-button>
<el-button type="primary" @click="saveUser">确 定el-button>
div>
el-dialog>
前端接口的文件在路径src\api\userManage.js
中,注意增删改查对应的方法分别是post
/delete
/put
/get
import request from '@/utils/request'
export default{
getUserList(searchModel){
return request({
url:'/user/list',
method:'get',
params:{ // 传给后端的参数,对应后端的 @RequestParam
pageNo: searchModel.pageNo,
pageSize: searchModel.pageSize,
username: searchModel.username,
phone: searchModel.phone,
}
});
},
addUser(user){
return request({
url:'/user',
method:'post',
data:user // 传回后端的实体数据
});
},
saveUser(user){
if(user.id == null || user.id == undefined){
return this.addUser(user);
}else{
return this.updateUser(user);
}
},
updateUser(user){
return request({
url:'/user',
method:'put',
data:user
});
},
getUserById(id){
return request({
// url:'/user/' + id, // 写法一
url:`/user/${id}`, // 写法二 ,对应后端的 @PathVariable
method:'get'
});
},
deleteUserById(id){
return request({
url:`/user/${id}`, // 对应后端的 @PathVariable
method:'delete'
});
},
}
后端代码在UserController.java
中
// 前端addUser方法的url就是"/user" 故此处无url
@PostMapping
public Result<?> addUser(@RequestBody User user){
// @RequestBody 用于json转为实体对象
// 做了加盐处理
user.setPassword(passwordEncoder.encode(user.getPassword()));
userService.save(user);
return Result.success("新增用户成功");
}
加密操作
新增用户中有个密码加密功能,将用户密码写入数据库时,对其加密加盐,如下图,密码明文是123456
(加盐:针对同一个密码组合,密文也是不同的)
在pom.xml
中添加依赖:
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-coreartifactId>
dependency>
@Bean
public PasswordEncoder passwordEncoder(){
// 密码加密的工具类,来自依赖spring-security-core
return new BCryptPasswordEncoder();
}
修改登录逻辑
密码加密后,登录时不能简单匹配数据库的密码,需要有个解密匹配过程
修改后端文件service\impl\UserServiceImpl
@Override
public Map<String, Object> login(User user) {
// 根据用户名查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, user.getUsername());
User loginUser = this.baseMapper.selectOne(wrapper);
// 结果不为空,并且密码与数据库解密后的密码匹配,生成token,将用户信息存入redis
if (loginUser != null &&
passwordEncoder.matches(user.getPassword(), loginUser.getPassword()) // 匹配加密密码
) {
// 用UUID,终极方案是jwt
String key = "user:" + UUID.randomUUID();
// 存入redis
loginUser.setPassword(null); // 设置密码为空,密码没必要放入
redisTemplate.opsForValue().set(key, loginUser,10, TimeUnit.MINUTES); // timeout为登录时间
// 返回数据
Map<String, Object> data = new HashMap<>();
data.put("token",key);
return data;
}
// 结果不为空,生成token,前后端分离,前端无法使用session,可以使用token
// 并将用户信息存入redis
return null;
}
至此,新增用户功能完毕
@PutMapping
public Result<?> updateUser(@RequestBody User user){
// @RequestBody 用于json转为实体对象
user.setPassword(null);
userService.updateById(user);
return Result.success("修改用户成功");
}
@DeleteMapping("/{id}")
public Result<?> deleteUserById(@PathVariable("id") Integer id){
userService.removeById(id);
return Result.success("删除用户成功");
}
在公司项目管理中,一般都是使用逻辑删除,并不是真正删除数据库里的记录,而是在数据表中设置一个字段deleted
做一个标记位。0表示未删除,1表示已删除
resources\application.yml
中添加配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名,与数据库的字段名一致
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
这样,Spring会在查询时自动拼接SQL语句WHERE deleted = 0
配置好后,删除oioi用户前
的数据库
删除后,最后一个字段变为1
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable("id") Integer id){
User user = userService.getById(id);
return Result.success(user);
}