视频链接
代码链接
1.创建一个文件夹,在文件加内输入cmd打开命令行窗口,然后检查node.js和npm的版本用命令:node -v npm -v
2.全局安装一个vue cli插件用命令
npm install -g @vue/cli
3.然后创建一个名字为springboot-vue-demo的项目
(在创建过程中遇到一个代理相关的问题,最后通过更换镜像源,切换代理最终解决问题)
vue create springboot-vue-demo
1.把创建好的项目拖进idea中,设置启动自动跳转页面在如下位置

然后出现以下问题:
网址为http://0.0.0.0:8080/ 的页面可能存在问题,或者已永久移动到新的网址
通过在编辑里边全局查找0.0.0.0 然后修改为localhost,最后成功在idea里边启动,如下图;

1.删除掉多余的Helloworld组件,以及相关的配置,然后自己创建一个Header(头部)组件,然后在HomeView.vue中引入Header组件,代码如下;
在Header.vue中创建一个大盒子然后再在其中创建三个小盒子的代码如下;
后台管理
时间
下拉框
2.再创建一个全局的css样式管理命名为global.css,代码如下
/*设置全局样式*/
*{
margin: 0;
padding: 0;
box-sizing:border-box ;
}
运行之后结果图为

3.在main.js中引入elementPlus组件
以下实在elementplus中的源码
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
需要引入的内容(蓝色部分和我标红的)

把文字部分改为自己需要的
用户 个人信息 退出系统
1.再定义一个vue组件Aside.vue,然后再App.vue中引入Aside.vue组件
App.vue中的代码为:
2.然后引入侧边栏导航,为App.vue设置样式
导航一 选项一 选项二 选项三 导航二 选项一 选项二 选项三 导航三 选项一 选项二 选项三
宽度长度

在Home.vue主体组件中引入主体table以及新增、导入、导出、搜索等按钮
基本都是从elementplus上边复制过来的,只是做了一些加外边距、改点样式的工作
新增
导入
导出
查询
编辑
删除
做完以上工作得到如下界面:

问题:放大缩小界面后下边会有一个不能自适应屏幕大小的问题

以上问题解决方案:
编辑
删除
按钮太大看着别扭,设置小号,可以让它看起来更舒服
如下如所示;

点击新增的时候弹出添加数据的显示框,用户可以在其中输入数据,需要为新增按钮绑定一个方法:

方法区:
methods:{
add(){
//表单其实一直都在,只是把它设置成为不可见得了
this.dialogVisible=true
// 新增的时候一定要清空一下表单域,不会影响下一次操作
this.form={}
},
handleEdit(){
},
handleSizeChange(){
},
handleCurrentChange(){}
}
再添加elementPlus组件(对话框设置了一个dialogVisible的模型,在数据区设置为默认不可见,在add方法内写入逻辑,当点击确认按钮触发add方法后,对话框就显示出来了):
男
女
取消
确认
很简单直接在idea里边创建,记得要添加web、mybatis、mysql、lombok(用来简化开发,少写一些东西)
1.这是用properties写的比较老的东西了
server.port=9090 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url= jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true spring.datasource.username= root spring.datasource.password= root
2.pom文件引入mybatisPlus依赖,目的是为了能够使用mybatisPlus提供的分页插件
com.baomidou mybatis-plus-boot-starter3.5.2
通用返回类
package com.example.common; public class Result{ private String code; private String msg; private T data; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } public Result() { } public Result(T data) { this.data = data; } public static Result success() { Result result = new Result<>(); result.setCode("0"); result.setMsg("成功"); return result; } public static Result success(T data) { Result result = new Result<>(data); result.setCode("0"); result.setMsg("成功"); return result; } public static Result error(String code, String msg) { Result result = new Result(); result.setCode(code); result.setMsg(msg); return result; } }
分页插件
package com.example.common;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus 分页插件
*/
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
实体类:
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
//引入lombok之后,用这个注解可以将这个实体类和数据库名字为user1的表关联起来
@TableName("user1")
//使用这个注解可以不用写get set方法
@Data
public class User {
// 设置主键自增,下边的id其实可以不用写,如果主键名字不是id的话,就需要写value ="id"
@TableId(value ="id", type = IdType.AUTO)
private Integer id;
private String username;
private String password;
private Integer age;
private String sex;
private String address;
}
因为这个项目比较简单,所以就没有写service层,只写了mapper和controller
mapper层:
package com.example.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.entity.User; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper extends BaseMapper{ }
controller层:
package com.example.controller;
import com.example.common.Result;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
@Resource
UserMapper userMapper;
// @RequestBody 的作用就是从前台拿过来的数据把它给封装成User实体类型的数据
@PostMapping
public Result> save(@RequestBody User user){
userMapper.insert(user);
return Result.success();
}
}
在新增表单输入数据,点击确认后前端发送axios请求(要引入一个js文件),写上请求路径(这里涉及一个跨域限制的问题针对本项目实际就是前端的8080端口不能访问后端的9090端口,可以在查到跨域问题的解决方案,就是在前端文件夹里引入一个vue.config.js文件),能够在后端数据库中看到新增的数据
如图;
1.引入axios相关的配置文件

代码:
import axios from 'axios'
import router from "@/router";
const request = axios.create({
baseURL: "/api",
timeout: 5000
})
// 请求白名单,如果请求在白名单里面,将不会被拦截校验权限
const whiteUrls = ["/user/login", '/user/register']
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
// 取出sessionStorage里面缓存的用户信息
let userJson = sessionStorage.getItem("user")
if (!whiteUrls.includes(config.url)) { // 校验请求白名单
if(!userJson) {
router.push("/login")
} else {
let user = JSON.parse(userJson);
config.headers['token'] = user.token; // 设置请求头
}
}
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === 'blob') {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
// 验证token
if (res.code === '401') {
console.error("token过期,重新登录")
router.push("/login")
}
return res;
},
error => {
console.log('err' + error) // for debug
return Promise.reject(error)
}
)
export default request
2.设置访问的请求路径
确认按钮添加上save方法

在save方法内设置请求路径

3.涉及一个跨域限制的问题针对本项目实际就是前端的8080端口不能访问后端的9090端口,可以在查到跨域问题的解决方案,就是在前端文件夹里引入一个vue.config.js文件(注意要在前端的工程的那个文件夹下)

代码:
// 跨域配置
module.exports = {
devServer: { //记住,别写错了devServer//设置本地默认端口 选填
port: 9876,
proxy: { //设置代理,必须填
'/api': { //设置拦截器 拦截器格式 斜杠+拦截器名字,名字可以自己定
target: 'http://localhost:9090', //代理的目标地址
changeOrigin: true, //是否设置同源,输入是的
pathRewrite: { //路径重写
'/api': '' //选择忽略拦截器里面的单词
}
}
}
}
}
在UserController里边打一个断点,用debug模式启动

在输入框输入数据

此时idea响应并且成功接收到数据

放开断点,让程序启动

在数据库看到新添加的数据

以前自己也偷偷学过相关的内容,但是一遇到一点问题自己就坚持不下去,然后就放弃了,这一次真的不错,虽然还是遇到许多问题,但是都通过自己的努力都得到了解决,感谢自己一点一滴的坚持,加油!
对每一位正在学习的朋友们说一句,问题肯定会有的,但是只要你肯坚持,那么就一定可以战胜,最难不过坚持!
分页组件:
后端
hutool的工具类:官网上搜索,然后引入依赖,可以用来判断某个字段是否为空
// TODO 差点死在这,草 要长记性呀,对于这种查询某个字段一定要看他是不是为空,要对他进行一个判断,不然会出问题的
if (StrUtil.isNotBlank(search)){
queryWrapper.like(User::getNickName,search);
}
Page userPage= userMapper.selectPage(new Page<>(pageNum,pageSize),queryWrapper);
return Result.success(userPage);
分页插件:
package com.example.common;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus 分页插件
*/
@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
注意:对某一个字段进行查询的时候一定要判断它是否为空,不然可能查不出来
/**
* 查询的方法
* @param pageNum
* @param pageSize
* @param search
* @return
*/
@GetMapping
// defaultValue就是设置默认访问值,
public Result> findPage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize,
@RequestParam(defaultValue = "") String search){
LambdaQueryWrapper queryWrapper=new LambdaQueryWrapper<>();
// TODO 差点死在这,草 要长记性呀,对于这种查询某个字段一定要看他是不是为空,要对他进行一个判断,不然会出问题的
if (StrUtil.isNotBlank(search)){
queryWrapper.like(User::getNickName,search);
}
Page userPage= userMapper.selectPage(new Page<>(pageNum,pageSize),queryWrapper);
return Result.success(userPage);
}
六、查询功能
前端
查询按钮处:
查询
对应的方法:
// 加载的方法,在加载的时候调用
created() {
this.load()
},
methods:{
load(){
request.get("/user",{params:{
pageNum:this.currentPage,
pageSize:this.pageSize,
search:this.search,
}}).then(res=>{
console.log(res)
this.tableData = res.data.records
this.total=res.data.total
})
}
后端
七、编辑功能
前端
点击编辑的时候弹出一个可以编辑的对话框(和新增时候弹出来的是一个,不同的是它会有数据的回显)

代码:
删除
编辑
方法内:
handleEdit(row){
//弹出编辑框,和新增框一样的请求路径,深克隆
this.form=JSON.parse(JSON.stringify(row))
//弹出对话框
this.dialogVisible=true
},
在前端页面点击编辑按钮,弹出对话框,编辑后点击确认按钮,执行save方法

save方法内需要判断执行的是新增还是编辑,通过id是否为空进行判断
// 点击确认按钮后触发的添加保存方法
save(){
if (this.form.id){ //更新
request.put("/user",this.form).then(res => {
console.log(res)
if(res.code === '0'){
this.$message({
type:"success",
message:"更新成功"
})
}else {
this.message({
type:"error",
message:res.msg
})
}
this.load() //刷新表格数据
// 关闭弹窗
this.dialogVisible=false
})
}else {
//新增
request.post("/user",this.form).then(res => {
console.log(res)
if(res.code === '0'){
this.$message({
type:"success",
message:"新增成功"
})
}else {
this.message({
type:"error",
message:res.msg
})
}
})
}
}
后端
在UserController中,新增一个方法
/**
* 实现user对象的更新,修改功能
* @param user
* @return
*/
@PutMapping
public Result> update(@RequestBody User user){
userMapper.updateById(user);
return Result.success();
}
过程中遇到的一个bug:
解决方案
自己重新把那里的代码敲了一遍,觉得最有可能的原因就是当初选择类型的时候form没有选正确,导致的错误,唉服了,
生气!

八、分页功能
前端
对方法的实现
//改变每页的条数
handleSizeChange(pageSize){
this.pageSize=pageSize
this.load()
},
//改变当前页
handleCurrentChange(pageNum){
this.currentPage=pageNum
this.load()
}
后端
后端内容没有添加
遇到的bug
无论怎么切换每页的条数,下方控制台给出的响应都是pageNum=1&pageSize=10,故问题出在这里

查看elementPlus分页组件的使用方法后发现需要给分页对应的方法传递参数

运行效果图:

九、删除功能
需要注意的就是在后端controller中它的路径,如下图所示,因为它前端的

如果不写id


那么就会报如下错误

前端
注意 request.delete(“/user/”+ id ) 这个里边的路径写法和连接方式
handleDelete(id){
console.log(id)
//删除 + id
request.delete("/user/"+ id ).then(res => {
if(res.code === '0'){
this.$message({
type:"success",
message:"删除成功"
})
}else {
this.message({
type:"error",
message:res.msg
})
}
this.load() //重新加载
})
},
后端
/**
* 删除
* @param id
* @return
*/
@DeleteMapping("/{id}")
public Result> delete( @PathVariable Long id){
userMapper.deleteById(id);
return Result.success();
}
运行结果图



十、路由设置
原理
在router文件夹下的index.js文件中也把App.vue设置为根路由,因为布局相关的代码都在里边,所以以后访问登录页面的时候也会有布局信息出来,如下图

把App.vue中的布局代码拷贝到一个心新的vue中命名为Layout.vue
解决方案:
在router文件夹下的index.js文件中的路由改成Layout

再次输入http://lcoalhost:9876/home时会有如下界面

登录功能
前端
在创建一个login.vue,里边写登录页面
欢迎登陆
登录
在登录方法里把请求路径写好

在前端router文件夹里边找到index.js这个文件,设置路由
import { createRouter, createWebHistory } from 'vue-router'
import Layout from '../layout/Layout.vue'
const routes = [
{
path: '/',
name: 'Layout',
component: Layout,
//输入/的时候页面会自动跳转到/home页面
redirect:"/home",
children: [
{
path:'home',
name:'Home',
component: () => import("@/views/HomeView"),
}
]
},
{
//写登录页面路由
path:'/login',
name:'Login',
//导入Login页面
component: () => import("@/views/Login")
},
// 写注册页面路由
{
path:'/register',
name:'Register',
component: () => import("@/views/register")
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
后端
在controller中写的PostMapping
@PostMapping("/login")
public Result> login(@RequestBody User user){
// 查询用户名,密码是否存在
User res= userMapper.selectOne(Wrappers.lambdaQuery().eq(User::getUsername,user.getUsername()).eq(User::getPassword,user.getPassword()));
if(res == null){
return Result.error("-1","用户名密码错误");
}
return Result.success();
}
注册功能
前端
欢迎注册
注册
同登录功能一样,设置路由

后端
1.查询用户名是否存在
2.如果没有设置密码,默认为******
@PostMapping("/register")
public Result> register(@RequestBody User user){
// 查询用户名,密码是否存在
User res= userMapper.selectOne(Wrappers.lambdaQuery().eq(User::getUsername,user.getUsername()));
if(res != null){
return Result.error("-1","用户名重复");
}
if(user.getPassword()==null){
user.setPassword("123456");
}
userMapper.insert(user);
return Result.success();
}
表单验证功能
效果图:

怎样实现:
1.在表单位置引入验证
3.验证表单内容,只有满足这个条件才执行下面的请求路径

bug:

解决方案:这种错误一般都是这个单词对应的代码格式写的不正确,导致没有识别出来

个人信息查看
1.写一个Person.vue
保存
2.然后在Layout路由下设置一个子路由
import { createRouter, createWebHistory } from 'vue-router'
import Layout from '../layout/Layout.vue'
const routes = [
{
path: '/',
name: 'Layout',
component: Layout,
//输入/的时候页面会自动跳转到/home页面
redirect:"/home",
children: [
{
path:'home',
name:'Home',
component: () => import("@/views/HomeView"),
},
// 个人信息界面
{
path:'person',
name:'Person',
component: () => import("@/views/Person"),
}
]
},
{
path:'/login',
name:'Login',
//导入Login页面
component: () => import("@/views/Login")
},
// 写路由
{
path:'/register',
name:'Register',
component: () => import("@/views/register")
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router

3.给“个人信息”绑定点击事件,实现跳转


鸡肋拦截器
没有写(不会写,哈哈)
页面跳转
点击书籍管理的时候,它会跳转到新的一个功能页

在侧边栏vue中这样写

然后添加一个Book.vue(到这里其实就可以直接把User.vue复制过来,然后改一下table和新增表单里边的关键词,还有一些路径)
新增
导入
导出
查询
……
编辑
删除
取消
确认
改的路径

然后就是设置一下路由 (在Layout下设置的子路由,以Layout为背景)
日期格式化:
问题

怎么做:
后端实体类数据上加 @JsonFormat

效果:

高亮显示不正确

基本功能就这么多啦!
上一篇:Vue常用方法汇总【更新中】