赞
踩
该笔记是记录听课的笔记,在上一篇文章的基础上加上了前端的开发,实现前后端完整功能的实现。
此项目使用的是VUE+ELEMENTUI以及Springboot+mybatis-plus的技术进行的开发,基于IDEA软件。此笔记在上一篇文章:【项目笔记】后端框架的搭建用法以及一些常用功能的实现,使用Springboot+mybatis-plus等技术的基础上,记录了前端联合后端的开发。
此项目希望插入新用户的时候统一设置初始化密码,即在插入用户的时候不用输入密码自动填充
在后端的Admin类中password属性设置了自动填充。
直接在@TableField()括号里加上fill = FieldFill.INSERT



插入式填充:注意字段、类型和值
Admin.java在上一篇文章里自动生成架构的时候,后面的字段就直接生成为自动填充值的架构,故一点开就会发现后面的value、version等属性直接就有fill=FieldFill.INSERT
ps:逻辑删除字段需要加上@TableLogic注解

生成架构的关于自动填充的代码:

自动加了注解但还是需要在MyMetaObjectHandler中的insertFill插入填充的方法中编写设置了自动填充的字段、类型以及值

使用postman工具发起自动填充测试:

此项目设置在进行更新操作后,update_time自动进行填充此时的更新时间。

当进行更新后,Date.class类型的updateTime这个字段将更新为现在的时间new Date()

ps:平时测试后端功能的时候,url地址栏填写格式
url:http://localhost:8081/一级路径(到类)/二级路径(到方法)

删除的时候不是删除,而是将isdelete设置为1,而且将修改时间更新

在上一篇文章写了分页查询和条件查询分开的,此处将分页和条件进行合并查询。

@PostMapping("/selectAdminByConditionAndPage/{currentPage}/{pageSize}") public Result selectAdminByConditionAndPage(@PathVariable Long currentPage, @PathVariable Long pageSize, @RequestBody AdminCondition adminCondition) { String name = adminCondition.getName(); String username = adminCondition.getUsername(); String phone = adminCondition.getPhone(); String beginTime = adminCondition.getBeginTime(); String endTime = adminCondition.getEndTime(); QueryWrapper<Admin> adminQueryWrapper = new QueryWrapper<>(); if (!name.isEmpty()) { adminQueryWrapper.like("name", name); } if (!username.isEmpty()) { adminQueryWrapper.like("username", username); } if (!phone.isEmpty()) { adminQueryWrapper.like("phone", phone); } if (!beginTime.isEmpty() && !endTime.isEmpty()) { adminQueryWrapper.between("create_time", beginTime, endTime); } adminQueryWrapper.orderByDesc("create_time"); Page<Admin> adminPage = new Page<>(currentPage, pageSize); // List<Admin> list = adminService.list(adminQueryWrapper); Page<Admin> page = adminService.page(adminPage, adminQueryWrapper); List<Admin> list = page.getRecords(); long total = page.getTotal(); return Result.success().data("items", list).data("total", total);//返回两个结果,一个是结果集一个是总数 }
ps:加上required = false,查询的时候可以加上查询条件,也可以没有查询条件

对象里面的参数为null和这个对象为null都是代表没有条件的意思
在mybatis-plus中找到分页插件


实现分页查询还需要新建一个类:可以在此类中添加想操作的插件


ps:Version乐观锁可以用于不能两个人同时选择某一个东西。至此后端结束。
Public打包的
Src项目主要内容
Api负责接口的管理
Vue不是跳转而是在一个页面中不断渲染
API是负责发送的接口 controller是负责接收的接口
希望别人可以用就必须export导出

Vue通过路由进行跳转的,从导入组件import,children可能是二级菜单。

Views就是写的页面,建议一个文件夹对应一个页面,页面都叫index,只用修改文件夹的名字即可。
Token类似门票
Templete里面有且只有一个div,放标签。取代了body标签。
Script写js
Style写css

跨域:ip不同端口不同,在vue.config.js中

只要请求的url中有/dev-api就跨域:跨域连接到后端

只用将这段插入config.js中
proxy: {
'/dev-api': {
target: 'http://localhost:8081/',
ws: true,
changeOrigin: true,//允许跨域
pathRewrite: {
'^/dev-api': ''
}
}
},

后端:AdminController中的login方法
@PostMapping("/login") public Result login(@RequestBody Admin admin){ String username = admin.getUsername(); String password = admin.getPassword(); QueryWrapper<Admin> adminQueryWrapper = new QueryWrapper<>(); if (!username.isEmpty()) { adminQueryWrapper.eq("username", username); }else { return Result.fail().message("账号不能为空"); } Admin one = adminService.getOne(adminQueryWrapper);//getone用于唯一的学号确定某一个人 if (one == null) { return Result.fail().message("该账号不存在"); } if (one.getPassword().equals(password)) { Map<String, Object> data = new HashMap<>(); data.put("id", one.getId()); String token = JWTUtils.generateToken(data); return Result.success().message("登录成功").data("token", token); } return Result.fail().message("登陆失败"); }
此处需要提到token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
token的依赖:
<!--JWT的依赖:简单的token认证-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
关于token的方法:
@GetMapping("/info")
public Result info(@RequestParam("token") String token){
if (token == null) {
return Result.fail().message("token不能为空");
}
Map<String, Object> data = JWTUtils.parseToken(token);
if (data == null) {
return Result.fail().message("token错误");
}
String id = (String) data.get("id");
Admin admin = adminService.getById(id);
return Result.success().data("name", admin.getName()).data("avatar", "");
}
实际vue代码以及其对应页面上的展示元素

图标的使用可以在这个网站:

给属性绑定一个变量:

传对象用data,传参数用param


**ps:在字符串里面插入变量:不用单引号,而是使用反引号` **
// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?` // Hello Bob, how are you today?

后端写的返回data(“items”,list),则前端的adminList会去接收后端返回的items对应的list



可以通过{{scope.row.xxx}}拿到某条数据的xxx

此处是对拿到的对象的phone属性进行取子串的操作


数据的双向绑定:

无限封装成一个方法

HandleDelete是处理


图表可以使用echarts:

注意,要export后其他页面才能import导入
index.vue
<template> <div class="app-container"> <el-button type="primary" @click="handleInsertAdmin()">添加员工</el-button> <el-table ref="multipleTable" :data="adminList" tooltip-effect="dark" style="width: 100%; margin-top: 10px;" border> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column prop="username" label="学号"> </el-table-column> <el-table-column prop="name" label="姓名"> </el-table-column> <el-table-column prop="createTime" label="创建时间"> </el-table-column> <el-table-column> <template slot-scope="scope"> <el-button type="danger" size="mini" @click="">编辑</el-button> <el-button type="danger" size="mini" @click="handleDeleteAdmin(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> <el-upload ref="uploadFile" class="avatar-uploader" action="http://localhost:9528/dev-api/example.demo/users/uploadFile" :auto-upload="false" accept="image/png" :on-change="handelFileChange" :on-success="handleUploadSuccess"> <img v-if="imageUrl" :src="imageUrl" class="avatar"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> <el-button @click="handleUploadFile()">上传</el-button> <el-dialog title="收货地址" :visible.sync="dialogFormVisible"> <el-form :model="adminEntity"> <el-form-item label="姓名"> <el-input v-model="adminEntity.name"></el-input> </el-form-item> <el-form-item label="学号"> <el-input v-model="adminEntity.username"></el-input> </el-form-item> <el-form-item label="电话"> <el-input v-model="adminEntity.phone"></el-input> </el-form-item> <el-form-item label="权限"> <el-select v-model="adminEntity.level"> <el-option v-for="(item, index) in levelList" :label="item.label" :value="item.value"></el-option> </el-select> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="handleInsertAdminEntity()">确 定</el-button> </div> </el-dialog> </div> </template> <script> import adminApi from '@/api/admin' export default { data() { return { isSelete : false, adminEntity: {}, adminCondition: { name: '', username: '', phone: '', beginTime: '', endTime: '' }, currentPage: 1, pageSize: 20, adminList: [], dialogFormVisible: false, imageUrl : '', levelList: [ { value: 1, label: '员工' }, { value: 2, label: '管理员' }, { value: 3, label: '超级管理员' }, ] } }, created() { this.getAdminList() }, methods: { getAdminList() { adminApi.selectAdminByConditionAndPage(this.adminCondition, this.currentPage, this.pageSize).then(res => { this.adminList = res.data.items }) }, handleInsertAdmin() { this.dialogFormVisible = true }, handleInsertAdminEntity() { adminApi.insertAdmin(this.adminEntity).then(res => { if (res.success) { this.getAdminList() } this.dialogFormVisible = false this.adminEntity = {} }) }, handleDeleteAdmin(id) { adminApi.deleteAdmin(id).then(res => { if (res.success) { this.getAdminList() } }) }, handelFileChange(file, fileList) { // 当用户选择图片后,将图片回显到页面,实现预览功能 this.imageUrl = URL.createObjectURL(file.raw) }, handleUploadFile() { this.$refs.uploadFile.submit() }, handleUploadSuccess(response, file, fileList) { if (response.success) { // 成功消息提示框 this.$message({ message: '上传成功', type: 'success' }); }else { // 失败消息提示框 this.$message.error(response.message); } }, } } </script> <style> .avatar-uploader .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; margin-top: 10px; } .avatar-uploader .el-upload:hover { border-color: #409EFF; } .avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 178px; height: 178px; line-height: 178px; text-align: center; } .avatar { width: 178px; height: 178px; display: block; } </style>
admin.js
import request from '@/utils/request' const adminApi = { selectAdminByConditionAndPage(adminConditon, currentPage, pageSize) { return request({ url: `/example.demo/admin/selectAdminByConditionAndPage/${currentPage}/${pageSize}`, method: 'post', data: adminConditon }) }, insertAdmin(admin) { return request({ url: `/example.demo/admin/insertAdmin`, method: 'post', data: admin }) }, deleteAdmin(id) { return request({ url: `/example.demo/admin/deleteAdmin/${id}`, method: 'get', }) } } export default adminApi
AdminController.java
package com.example.demo.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.demo.entity.Admin; import com.example.demo.entity.Users; import com.example.demo.entity.condition.AdminCondition; import com.example.demo.entity.condition.UsersCondition; import com.example.demo.service.AdminService; import com.example.demo.utils.JWTUtils; import com.example.demo.utils.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/example.demo/admin") public class AdminController { @Autowired private AdminService adminService; @PostMapping("/insertAdmin") public Result insertAdmin(@RequestBody Admin admin) { boolean save = adminService.save(admin); if (save) { return Result.success().message("添加成功"); } return Result.fail().message("添加失败"); } @GetMapping("/deleteAdmin/{id}") public Result deleteAdmin(@PathVariable String id) { boolean flag = adminService.removeById(id); if (flag) { return Result.success(); } return Result.fail(); } @PostMapping("/updateAdmin") public Result updateAdmin(@RequestBody Admin admin) { boolean flag = adminService.updateById(admin); if (flag) { return Result.success().message("修改成功"); } return Result.fail().message("修改失败"); } @GetMapping("/selectAdmin") public Result selectAdmin(){ List<Admin> list = adminService.list(); return Result.success().data("items", list); } @PostMapping("/selectAdminByConditionAndPage/{currentPage}/{pageSize}") public Result selectAdminByConditionAndPage(@PathVariable Long currentPage, @PathVariable Long pageSize, @RequestBody AdminCondition adminCondition) { String name = adminCondition.getName(); String username = adminCondition.getUsername(); String phone = adminCondition.getPhone(); String beginTime = adminCondition.getBeginTime(); String endTime = adminCondition.getEndTime(); QueryWrapper<Admin> adminQueryWrapper = new QueryWrapper<>(); if (!name.isEmpty()) { adminQueryWrapper.like("name", name); } if (!username.isEmpty()) { adminQueryWrapper.like("username", username); } if (!phone.isEmpty()) { adminQueryWrapper.like("phone", phone); } if (!beginTime.isEmpty() && !endTime.isEmpty()) { adminQueryWrapper.between("create_time", beginTime, endTime); } adminQueryWrapper.orderByDesc("create_time"); Page<Admin> adminPage = new Page<>(currentPage, pageSize); // List<Admin> list = adminService.list(adminQueryWrapper); Page<Admin> page = adminService.page(adminPage, adminQueryWrapper); List<Admin> list = page.getRecords(); long total = page.getTotal(); return Result.success().data("items", list).data("total", total); } @PostMapping("/login") public Result login(@RequestBody Admin admin){ String username = admin.getUsername(); String password = admin.getPassword(); QueryWrapper<Admin> adminQueryWrapper = new QueryWrapper<>(); if (!username.isEmpty()) { adminQueryWrapper.eq("username", username); }else { return Result.fail().message("账号不能为空"); } Admin one = adminService.getOne(adminQueryWrapper); if (one == null) { return Result.fail().message("该账号不存在"); } if (one.getPassword().equals(password)) { Map<String, Object> data = new HashMap<>(); data.put("id", one.getId()); String token = JWTUtils.generateToken(data); return Result.success().message("登录成功").data("token", token); } return Result.fail().message("登陆失败"); } @GetMapping("/info") public Result info(@RequestParam("token") String token){ if (token == null) { return Result.fail().message("token不能为空"); } Map<String, Object> data = JWTUtils.parseToken(token); if (data == null) { return Result.fail().message("token错误"); } String id = (String) data.get("id"); Admin admin = adminService.getById(id); return Result.success().data("name", admin.getName()).data("avatar", ""); } /*上传文件*/ @PostMapping("/uploadFile") public Result uploadFile(@RequestParam("file") MultipartFile multipartFile){ //根路径 String bassPath = "D:\\IdeaProjects\\demo\\src\\main\\resources\\files"; //基本路径 // 时间路径,格式化时间,以时间对文件夹进行分类 String dataPath = new SimpleDateFormat("YYYY\\MM\\dd").format(new Date()); //基本路径加上时间路径形成上传的文件夹,先创建文件夹再创建文件 String dirPath = bassPath +"\\"+ dataPath; //System.out.println(dirPath); File dirFile = new File(dirPath);//使用io的File if(!dirFile.exists()){ boolean mkdirs = dirFile.mkdirs(); if(!mkdirs){//先创建文件夹再创建文件 return Result.fail().message("文件夹创建失败"); } } //originalname才是文件名,获取文件名 String originalFilename = multipartFile.getOriginalFilename(); if(originalFilename == null){ return Result.fail().message("文件名为空"); } //对文件名通过"."进行分割 比如"文件.exe" => {"文件","exe"} String[] fileName = originalFilename.split("\\."); //每次上传的文件前面添加了随机值防止文件上传覆盖,通过UUID随机生成的字符串并且去除其中的横线,再加上文件类型组成完整的文件名 String targetFileName = UUID.randomUUID().toString().replace("-","") + "." + fileName[1]; File targetFile = new File(dirFile, targetFileName); try( InputStream inputStream = multipartFile.getInputStream(); OutputStream outputStream = Files.newOutputStream(targetFile.toPath()); ){ byte[] buffer = new byte[1024]; int len = -1; while ((len = inputStream.read(buffer)) != -1){ //拷贝,-1就表示已经读取完毕 outputStream.write(buffer,0,len); } }catch (Exception e){ return Result.fail().message("保存失败"); } String path = targetFile.getPath(); return Result.success().data("url",path); } }
浅浅记录一下师兄给我们讲解的vue前端以及前后端是如何结合的,第一版笔记可能有点粗糙,后面会一点点进行精细的修改。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。