当前位置:首页 » 《休闲阅读》 » 正文

springboot+vue

25 人参与  2024年04月05日 13:25  分类 : 《休闲阅读》  评论

点击全文阅读


视频链接
代码链接

一、前端环境搭建

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

二、在idea中启动

1.把创建好的项目拖进idea中,设置启动自动跳转页面在如下位置

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

三、前端布局以及页面的制作

1.删除不要的组件

1.删除掉多余的Helloworld组件,以及相关的配置,然后自己创建一个Header(头部)组件,然后在HomeView.vue中引入Header组件,代码如下;

<template><!--  引入Header组件--><div> <Header/> </div>  <router-view/></template><style></style><!--导入Header组件--><script >import Header from "@/components/Header";export default{  name:"Layout",  components:{    //光标对着Header然后alt+enter,进行引入    Header  }}</script>

在Header.vue中创建一个大盒子然后再在其中创建三个小盒子的代码如下;

<template >  <div style="height:50px; line-height:50px; border-bottom: 1px solid #ccc; display: flex">    <div style="width: 200px">后台管理</div>    <div style="flex:1">时间</div>    <div style="width: 100px">下拉框</div>  </div></template><script>export default {  name: "Header"}</script><style scoped></style>

2.设置全局样式

2.再创建一个全局的css样式管理命名为global.css,代码如下

/*设置全局样式*/*{    margin: 0;    padding: 0;    box-sizing:border-box ;}

运行之后结果图为
在这里插入图片描述
3.在main.js中引入elementPlus组件
以下实在elementplus中的源码

// main.tsimport { 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')

需要引入的内容(蓝色部分和我标红的)
在这里插入图片描述

3.引入elementplus组件

1.引入下拉框

把文字部分改为自己需要的

<el-dropdown>    <span class="el-dropdown-link" style="padding:  18px">      用户      <el-icon class="el-icon--right">        <arrow-down />      </el-icon>    </span>          <template #dropdown>            <el-dropdown-menu>              <el-dropdown-item>个人信息</el-dropdown-item>              <el-dropdown-item>退出系统</el-dropdown-item>            </el-dropdown-menu>          </template>        </el-dropdown>

2.引入侧边栏

1.再定义一个vue组件Aside.vue,然后再App.vue中引入Aside.vue组件
App.vue中的代码为:

<template><!--  引入Header组件--><div><!--  头部-->  <Header/><!--  主体 侧边栏再定义一个div flex布局-->  <div style="display: flex"><!--    侧边栏-->    <Aside/><!--    内容区域-->    <router-view style="flex: 1"/>  </div></div></template><style></style><!--导入Header组件--><script >import Header from "@/components/Header";import Aside from "@/components/Aside";export default{  name:"Layout",  components:{    //光标对着Header然后alt+enter,进行引入    Header,    //引入侧边栏vue组件    Aside  }}</script>

2.然后引入侧边栏导航,为App.vue设置样式

<template><div>  <el-menu      default-active="2"      class="el-menu-vertical-demo"      style="width: 200px; min-height: calc(100vh - 50px)"  >    <el-sub-menu index="1">      <template #title>        <el-icon><location /></el-icon>        <span>导航一</span>      </template>        <el-menu-item index="1-3">选项一</el-menu-item>        <el-menu-item index="1-3">选项二</el-menu-item>        <el-menu-item index="1-3">选项三</el-menu-item>    </el-sub-menu>    <el-sub-menu index="2">      <template #title>        <el-icon><location /></el-icon>        <span>导航二</span>      </template>      <el-menu-item index="2-3">选项一</el-menu-item>      <el-menu-item index="2-3">选项二</el-menu-item>      <el-menu-item index="2-3">选项三</el-menu-item>    </el-sub-menu>    <el-sub-menu index="3">      <template #title>        <el-icon><location /></el-icon>        <span>导航三</span>      </template>      <el-menu-item index="3-3">选项一</el-menu-item>      <el-menu-item index="3-3">选项二</el-menu-item>      <el-menu-item index="3-3">选项三</el-menu-item>    </el-sub-menu>  </el-menu></div></template><script>export default {  name: "Aside"}</script><style scoped></style>

宽度长度
在这里插入图片描述

3.引入主体表格等内容

在Home.vue主体组件中引入主体table以及新增、导入、导出、搜索等按钮
基本都是从elementplus上边复制过来的,只是做了一些加外边距、改点样式的工作

<template>  <div style="padding: 10px"><!--    功能区-->    <div style="margin: 10px 0">      <el-button type="primary">新增</el-button>      <el-button type="primary">导入</el-button>      <el-button type="primary">导出</el-button>    </div><!--    搜索区-->    <div style="margin:10px 0">      <el-input v-model="search" placeholder="请输入..." style="width: 20%"/>      <el-button type="primary" style="margin-left: 5px">查询</el-button>    </div><!--    表格-->    <el-table :data="tableData"              border              stripe              style="width: 100%"><!--      sortable是为时间加上排序-->      <el-table-column prop="date" label="日期" sortable/>      <el-table-column prop="name" label="姓名" />      <el-table-column prop="address" label="地址" />      <el-table-column fixed="right" label="操作" width="120"><!--        操作部分的按钮-->        <template #default>          <el-button link type="primary"  @click="handleEdit"          >编辑</el-button>          <el-popconfirm title="确认删除吗?">            <template #reference>              <el-button type="text">删除</el-button>            </template>          </el-popconfirm>        </template>      </el-table-column>    </el-table><!--    分页组件-->    <div style="margin: 10px 0">      <el-pagination          v-model:current-page="currentPage"          v-model:page-size="pageSize"          :page-sizes="[5, 10, 20]"          :small="small"          :disabled="disabled"          :background="background"          layout="total, sizes, prev, pager, next, jumper"          :total="10"          @size-change="handleSizeChange"          @current-change="handleCurrentChange"      />    </div>  </div></template><script>// @ is an alias to /srcexport default {  name: 'HomeView',  components: {  },  //先为表格填入假数据  data(){    return{      search:'',      pageSize:10,      currentPage:1,      tableData:[{        date: '2016-05-03',        name: 'Tom',        address: 'No. 189, Grove St, Los Angeles',      },        {          date: '2016-05-02',          name: 'Tom',          address: 'No. 189, Grove St, Los Angeles',        },        {          date: '2016-05-04',          name: 'Tom',          address: 'No. 189, Grove St, Los Angeles',        },        {          date: '2016-05-01',          name: 'Tom',          address: 'No. 189, Grove St, Los Angeles',        },]    }  },  methods:{    handleEdit(){    },    handleSizeChange(){    },    handleCurrentChange(){}  },}</script>

做完以上工作得到如下界面:

在这里插入图片描述
问题:放大缩小界面后下边会有一个不能自适应屏幕大小的问题
在这里插入图片描述
以上问题解决方案:

<template #default="scope">          <el-button link type="primary" size="mini"  @click="handleEdit(scope.$index,scope.row)"          >编辑</el-button>          <el-popconfirm title="确认删除吗?">            <template #reference>              <el-button size="mini" type="danger" @click="handleDelete(scope.$index,scope.row)">删除</el-button>            </template>          </el-popconfirm>        </template>

4.细节完善

按钮太大看着别扭,设置小号,可以让它看起来更舒服
如下如所示;
在这里插入图片描述

5.加新增弹出框

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

在这里插入图片描述

方法区:

methods:{    add(){      //表单其实一直都在,只是把它设置成为不可见得了      this.dialogVisible=true    //  新增的时候一定要清空一下表单域,不会影响下一次操作      this.form={}    },    handleEdit(){    },    handleSizeChange(){    },    handleCurrentChange(){}  }

再添加elementPlus组件(对话框设置了一个dialogVisible的模型,在数据区设置为默认不可见,在add方法内写入逻辑,当点击确认按钮触发add方法后,对话框就显示出来了):

<!--    弹出对话框  -->      <el-dialog          v-model="dialogVisible"          title="提示"          width="30%"><!--     表单组件   -->        <el-form :model="form" label-width="120px">          <el-form-item label="用户名">            <el-input v-model="form.username" style="width:80%"/>          </el-form-item>          <el-form-item label="昵称">            <el-input v-model="form.nickName" style="width:80%"/>          </el-form-item>          <el-form-item label="性别">              <el-radio v-model="form.sex" label="1" size="large">男</el-radio>              <el-radio v-model="form.sex" label="2" size="large">女</el-radio>          </el-form-item>          <el-form-item label="年龄">            <el-input v-model="form.age" style="width:80%"/>          </el-form-item>          <el-form-item label="地址">            <el-input v-model="form.address"  style="width:80%"/>          </el-form-item>          </el-form>        <template #footer>      <span class="dialog-footer">        <el-button @click="dialogVisible = false">取消</el-button>        <el-button type="primary" @click="dialogVisible = false">          确认        </el-button>      </span>        </template>      </el-dialog>

四、后端架子以及新增功能

1.创建一个springboot工程

很简单直接在idea里边创建,记得要添加web、mybatis、mysql、lombok(用来简化开发,少写一些东西)

2.配置application.properties文件

1.这是用properties写的比较老的东西了

server.port=9090spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url= jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=truespring.datasource.username= rootspring.datasource.password= root

2.pom文件引入mybatisPlus依赖,目的是为了能够使用mybatisPlus提供的分页插件

<dependency>            <groupId>com.baomidou</groupId>            <artifactId>mybatis-plus-boot-starter</artifactId>            <version>3.5.2</version>        </dependency>

3.引入一些通用类

通用返回类

package com.example.common;public class Result<T> {    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 <T> Result<T> success(T data) {        Result<T> 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方法@Datapublic 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;}

4.写接口

因为这个项目比较简单,所以就没有写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;@Mapperpublic interface UserMapper extends BaseMapper<User> {}

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();    }}

5.实现前后端交互

在新增表单输入数据,点击确认后前端发送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': ''                     //选择忽略拦截器里面的单词                }            }        }    }}

6.测试是否能从前端接收到数据

在UserController里边打一个断点,用debug模式启动
在这里插入图片描述
在输入框输入数据

此时idea响应并且成功接收到数据
在这里插入图片描述
放开断点,让程序启动
在这里插入图片描述
在数据库看到新添加的数据
在这里插入图片描述

7.至此完成自己第一个前后台交互的功能

以前自己也偷偷学过相关的内容,但是一遇到一点问题自己就坚持不下去,然后就放弃了,这一次真的不错,虽然还是遇到许多问题,但是都通过自己的努力都得到了解决,感谢自己一点一滴的坚持,加油!
对每一位正在学习的朋友们说一句,问题肯定会有的,但是只要你肯坚持,那么就一定可以战胜,最难不过坚持!

五、将数据在页面显示

前端

分页组件:

<!--    分页组件-->    <div style="margin: 10px 0">      <el-pagination          @size-change="handleSizeChange"          @current-change="handleCurrentChange"          :current-page="currentPage"          :page-sizes="[5, 10, 20]"          :page-size="pageSize"          :small="small"          :disabled="disabled"          :background="background"          layout="total, sizes, prev, pager, next, jumper"          :total="total"      />

后端

hutool的工具类:官网上搜索,然后引入依赖,可以用来判断某个字段是否为空

//    TODO 差点死在这,草 要长记性呀,对于这种查询某个字段一定要看他是不是为空,要对他进行一个判断,不然会出问题的        if (StrUtil.isNotBlank(search)){            queryWrapper.like(User::getNickName,search);        }       Page<User>  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<User> queryWrapper=new LambdaQueryWrapper<>();//    TODO 差点死在这,草 要长记性呀,对于这种查询某个字段一定要看他是不是为空,要对他进行一个判断,不然会出问题的        if (StrUtil.isNotBlank(search)){            queryWrapper.like(User::getNickName,search);        }       Page<User>  userPage= userMapper.selectPage(new Page<>(pageNum,pageSize),queryWrapper);        return Result.success(userPage);    }

六、查询功能

前端

查询按钮处:

<!--    搜索区-->    <div style="margin:10px 0">      <el-input v-model="search" placeholder="请输入..." style="width: 20%"  clearable ></el-input>      <el-button type="primary" style="margin-left: 5px" @click="load"  >查询</el-button>    </div>

对应的方法:

 // 加载的方法,在加载的时候调用  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      })    }

后端

七、编辑功能

前端

点击编辑的时候弹出一个可以编辑的对话框(和新增时候弹出来的是一个,不同的是它会有数据的回显)
在这里插入图片描述
代码:

<!--      操作框-->      <el-table-column fixed="right" label="操作" >        <template #default="scope">          <el-popconfirm title="确认删除吗?">            <template #reference>              <el-button size="small" type="danger" @click="handleDelete(scope.$index,scope.row)">删除</el-button>            </template>          </el-popconfirm>          <el-button link type="primary" size="small"   @click="handleEdit(scope.row)">编辑</el-button>        </template>      </el-table-column>

方法内:

 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 */@PutMappingpublic Result<?> update(@RequestBody User user){    userMapper.updateById(user);    return Result.success();}

过程中遇到的一个bug:在这里插入图片描述
解决方案
自己重新把那里的代码敲了一遍,觉得最有可能的原因就是当初选择类型的时候form没有选正确,导致的错误,唉服了,在这里插入图片描述
生气!
在这里插入图片描述

八、分页功能

前端

<!--    分页组件-->    <div style="margin: 10px 0">      <el-pagination          @size-change="handleSizeChange"          @current-change="handleCurrentChange"          :current-page="currentPage"          :page-sizes="[5, 10, 20]"          :page-size="pageSize"          :small="small"          :disabled="disabled"          :background="background"          layout="total, sizes, prev, pager, next, jumper"          :total="total"      />

对方法的实现

//改变每页的条数    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

<template> <div>   <!--  头部-->   <Header/>   <!--  主体 侧边栏再定义一个div flex布局-->   <div style="display: flex">     <!--    侧边栏-->     <Aside/>     <!--    内容区域-->     <router-view style="flex: 1"/>   </div> </div></template><script>import Header from "@/components/Header";import Aside from "@/components/Aside";export default {  name: "Layout",  components:{    //光标对着Header然后alt+enter,进行引入    Header,    //引入侧边栏vue组件    Aside  }}</script><style scoped></style>

解决方案:

在router文件夹下的index.js文件中的路由改成Layout
在这里插入图片描述

再次输入http://lcoalhost:9876/home时会有如下界面
在这里插入图片描述

登录功能

前端

在创建一个login.vue,里边写登录页面

<template><!--  100vh表示高度整个屏幕 overflow:hidden表示隐藏空白处--><div style="width: 100%; height:100vh; background-color: darkslateblue; overflow:hidden ">  <div style="width: 400px; margin: 150px auto">   <div style="color: #cccccc; text-align: center; font-size:30px; padding: 30px 0">欢迎登陆</div><!--    对应的数据变量如果没有变紫色,表示没有用成功-->    <el-form  ref="form" :model="form" size="normal" :rules="rules">      <el-form-item prop="username">        <el-input   v-model="form.username">        </el-input>      </el-form-item>      <el-form-item prop="password">        <el-input  v-model="form.password"  show-password>        </el-input>      </el-form-item>      <el-form-item>        <el-button style="width: 100% " type="primary" @click="login">登录</el-button>      </el-form-item>    </el-form>     </div></div></template><script>import request from "@/utils/request";export default {  name: "Login",  data(){     return {       form: {},       rules:{         username: [           {required: true, message: '请输入用户名', trigger: 'blur'},         ],         password: [           {required: true, message: '请输入密码', trigger: 'blur'},         ]       },     }  },  methods:{    //登录    login(){      //表单验证内容,满足一下验证需求,他才会执行下面的请求      this.$refs['form'].validate((valid) => {        if (valid) {          request.post("/user/login",this.form).then(res => {            if (res.code ==='0'){              this.$message({                type:"success",                message:"登陆成功"              })              //  登陆成功后页面跳转              this.$router.push("/")            }else {              this.$message({                type:"error",                message:res.msg              })            }          })        }      })    },  },}</script><style scoped></style>

在登录方法里把请求路径写好

在这里插入图片描述
在前端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.<User>lambdaQuery().eq(User::getUsername,user.getUsername()).eq(User::getPassword,user.getPassword()));        if(res == null){            return  Result.error("-1","用户名密码错误");        }        return Result.success();    }

注册功能

前端

<template><!--  100vh表示高度整个屏幕 overflow:hidden表示隐藏空白处--><div style="width: 100%; height:100vh; background-color: darkslateblue; overflow:hidden ">  <div style="width: 400px; margin: 150px auto">   <div style="color: #cccccc; text-align: center; font-size:30px; padding: 30px 0">欢迎注册</div><!--    对应的数据变量如果没有变紫色,表示没有用成功-->    <el-form  ref="form" :model="form" size="normal" :rules="rules">      <el-form-item prop="username">        <el-input   v-model="form.username">        </el-input>      </el-form-item>      <el-form-item prop="password">        <el-input  v-model="form.password"  show-password>        </el-input>      </el-form-item><!--      确认密码-->      <el-form-item  prop="confirm">        <el-input  v-model="form.confirm"  show-password>        </el-input>      </el-form-item>      <el-form-item>        <el-button style="width: 100% " type="primary" @click="register">注册</el-button>      </el-form-item>    </el-form>     </div></div></template><script>import request from "@/utils/request";export default {  name: "register",  data(){     return {       form:{},       rules:{         username: [           {required: true, message: '请输入用户名', trigger: 'blur'},         ],         password: [           {required: true, message: '请输入密码', trigger: 'blur'},         ],         confirm: [           {required: true, message: '请确认密码', trigger: 'blur'},         ]       }     }  },  methods:{    //表单验证    register(){      if (this.form.password != this.form.confirm){        this.$message({          type:"error",          message:"两次密码输入不一致!"        })        return      }      //表单验证      this.$refs[form].validate((valid) => {        if (valid) {          request.post("/user/register",this.form).then(res => {            if (res.code ==='0'){              this.$message({                type:"success",                message:"注册成功"              })              //  成功后页面跳转              this.$router.push("/login")            }else {              this.$message({                type:"error",                message:res.msg              })            }          })        }})    },  },}</script><style scoped></style>

同登录功能一样,设置路由
在这里插入图片描述

后端

1.查询用户名是否存在
2.如果没有设置密码,默认为******

    @PostMapping("/register")    public Result<?> register(@RequestBody User user){//        查询用户名,密码是否存在        User res= userMapper.selectOne(Wrappers.<User>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

<template>  <div>    <el-card style="width: 40%; margin: 10px">      <el-form ref="form" :model="form" label-width="80px">        <el-form-item style="text-align: center" label-width="0">          <el-upload              class="avatar-uploader"              action="http://localhost:9090/files/upload"              :show-file-list="false"              :on-success="handleAvatarSuccess"          >            <img v-if="form.avatar" :src="form.avatar" class="avatar">            <i v-else class="el-icon-plus avatar-uploader-icon"></i>          </el-upload>        </el-form-item>        <el-form-item label="用户名">          <el-input v-model="form.username" disabled></el-input>        </el-form-item>        <el-form-item label="昵称">          <el-input v-model="form.nickName"></el-input>        </el-form-item>        <el-form-item label="年龄">          <el-input v-model="form.age"></el-input>        </el-form-item>        <el-form-item label="性别">          <el-input v-model="form.sex"></el-input>        </el-form-item>        <el-form-item label="地址">          <el-input v-model="form.address"></el-input>        </el-form-item><!--        <el-form-item label="密码">--><!--          <el-input v-model="form.password" show-password></el-input>--><!--        </el-form-item>-->        <el-form-item label="余额(¥)">          <el-input v-model="form.account" show-password></el-input>        </el-form-item>      </el-form>      <div style="text-align: center">        <el-button type="primary" @click="update">保存</el-button>      </div>    </el-card>  </div></template><script>import request from "@/utils/request";export default {  name: "Person",  data() {    return {      form: {}    }  },  created() {    let str = sessionStorage.getItem("user") || "{}"    this.form = JSON.parse(str)  },  methods: {    handleAvatarSuccess(res) {      this.form.avatar = res.data      this.$message.success("上传成功")      // this.update()    },    update() {      request.put("/user", this.form).then(res => {        console.log(res)        if (res.code === '0') {          this.$message({            type: "success",            message: "更新成功"          })          sessionStorage.setItem("user", JSON.stringify(this.form))          // 触发Layout更新用户信息          this.$emit("userInfo")        } else {          this.$message({            type: "error",            message: res.msg          })        }      })    }  }}</script><style>.avatar-uploader .el-upload {  border: 1px dashed #d9d9d9;  border-radius: 6px;  cursor: pointer;  position: relative;  overflow: hidden;}.avatar-uploader .el-upload:hover {  border-color: #409EFF;}.avatar-uploader-icon {  font-size: 28px;  color: #8c939d;  width: 120px;  height: 120px;  line-height: 120px;  text-align: center;}.avatar {  width: 178px;  height: 178px;  display: block;}</style>

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和新增表单里边的关键词,还有一些路径)

<template>  <div style="padding: 10px">    <!--    功能区-->    <div style="margin: 10px 0">      <el-button type="primary" @click="add">新增</el-button>      <el-button type="primary">导入</el-button>      <el-button type="primary">导出</el-button>    </div>    <!--    搜索区-->    <div style="margin:10px 0">      <el-input v-model="search" placeholder="请输入..." style="width: 20%"  clearable ></el-input>      <el-button type="primary" style="margin-left: 5px" @click="load"  >查询</el-button>    </div>……    <!--    表格-->    <el-table :data="tableData"  border stripe style="width: 100%">      <el-table-column prop="id" label="ID" width="150" />      <el-table-column prop="name" label="名称" />      <el-table-column prop="price" label="价格"  />      <el-table-column prop="author" label="作者"/>      <el-table-column prop="createTime" label="出版时间"  />      <!--      操作框-->      <el-table-column fixed="right" label="操作" >        <template #default="scope">          <el-button  size="mini"   @click="handleEdit(scope.row)">编辑</el-button>          <!--elementPlus提供的方法  因为要根据id进行删除,索引就需要传入id-->          <el-popconfirm title="确认删除吗?" @confirm="handleDelete(scope.row.id)">            <template #reference>              <el-button size="mini" type="danger" >删除</el-button>            </template>          </el-popconfirm>        </template>      </el-table-column>    </el-table>    <!--    分页组件-->    <div style="margin: 10px 0">      <el-pagination          @size-change="handleSizeChange"          @current-change="handleCurrentChange"          :current-page="currentPage"          :page-sizes="[5, 10, 20]"          :page-size="pageSize"          :small="small"          :disabled="disabled"          :background="background"          layout="total, sizes, prev, pager, next, jumper"          :total="total"      />      <!--    弹出对话框  -->      <el-dialog v-model="dialogVisible" title="提示" width="30%">        <!--     表单组件   -->        <el-form :model="form" label-width="120px">          <el-form-item label="名称">            <el-input v-model="form.name" style="width:80%"/>          </el-form-item>          <el-form-item label="价格">            <el-input v-model="form.price" style="width:80%"/>          </el-form-item>          <el-form-item label="作者">            <el-input v-model="form.author" style="width:80%"/>          </el-form-item>          <el-form-item label="出版时间">            <el-date-picker v-model="form.createTime" value-format="YYYY-MM-DD"  type="date" style="width:80%" clearable></el-date-picker>          </el-form-item>        </el-form>        <template #footer>           <span class="dialog-footer">             <el-button @click="dialogVisible = false">取消</el-button>             <el-button type="primary" @click="save">确认</el-button>           </span>        </template>      </el-dialog>    </div>  </div></template><script>// @ is an alias to /srcimport request from "@/utils/request";export default {  name: 'HomeView',  components: {  },  //先为表格填入假数据  data(){    return{      form:{},      dialogVisible:false,      search:'',      pageSize:10,      currentPage:1,      tableData:[],      total:0,    }  },  // 加载的方法,在加载的时候调用  created() {    this.load()  },  methods:{    //刷新表格的数据    load(){      request.get("/book",{        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      })    },    add(){      //表单其实一直都在,只是把它设置成为不可见得了      this.dialogVisible=true      //  新增的时候一定要清空一下表单域,不会影响下一次操作      this.form={}    },    // 点击确认按钮后触发的添加保存方法    save(){      if (this.form.id){ //更新        request.put("/book",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("/book",this.form).then(res => {          console.log(res)          if(res.code === '0'){            this.$message({              type:"success",              message:"新增成功"            })            this.load()            this.dialogVisible=false          }else {            this.message({              type:"error",              message:res.msg            })          }        })      }    },    handleDelete(id){      console.log(id)      //删除 + id      request.delete("/book/"+ id ).then(res => {        if(res.code === '0'){          this.$message({            type:"success",            message:"删除成功"          })        }else {          this.message({            type:"error",            message:res.msg          })        }        this.load() //重新加载      })    },    handleEdit(row){      //弹出编辑框,和新增框一样的请求路径,深克隆      this.form=JSON.parse(JSON.stringify(row))      //弹出对话框      this.dialogVisible=true    },    //改变每页的条数    handleSizeChange(pageSize){      this.pageSize=pageSize      this.load()    },    //改变当前页    handleCurrentChange(pageNum){      this.currentPage=pageNum      this.load()    }  },}</script>

改的路径
在这里插入图片描述
然后就是设置一下路由 (在Layout下设置的子路由,以Layout为背景)在这里插入图片描述

日期格式化:

问题
在这里插入图片描述
怎么做:
后端实体类数据上加 @JsonFormat

效果:
在这里插入图片描述

高亮显示不正确

在这里插入图片描述
基本功能就这么多啦!


点击全文阅读


本文链接:http://zhangshiyu.com/post/90761.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1