当前位置:   article > 正文

实验管理系统springboot+vue+element ui项目开发_springboot elementui

springboot elementui

实验管理系统

某学院实验老师长期采用人工的形式完成药品试剂的入库、查询、出库的流程。但这种方式存在诸多问题和不便:

1. 在仓库运行流程中效率不高,容易出错。

2. 管理人员不能方便的了解每种物品的状态。

3. 数据量越来越大,难以维护。

因此,为了解决以上问题,提高办公效率,提高人力物力的利用率,对仓库工作进行信息化,规范化管理,结合使用单位的需求,开发出一套适合使用单位进行管理使用的系统。

2021.3.25~2021.4.1

本项目采用前后端分离开发

一、前端开发的环境配置

1.1 安装node.js

首先我们上node.js官网(nodejs.org/zh-cn/),下载最新的长期版本,直接运行安装完成之后,我们就已经具备了node和npm的环境了。

安装完成之后可以window+r在控制台检查版本信息以及是否安装成功:

配置环境变量

原因:环境配置的目的主要是为了改变安装Nodejs依赖的下载位置,更加方便日后的包管理,以及配置缓存Cache的路径。因为Nodejs在执行安装语句时,会将安装的模块默认安装到C:\Users\用户名\AppData\Roaming\npm,从而占用C盘的空间。

先在node.js安装目录下创建两个文件夹

 window+R,输入cmd打开命令窗口。输入:

  1. npm config set prefix "F:\a tool\node.js\node_cache"
  2. npm config set prefix "F:\a tool\node.js\node_global"

 点击我的电脑->右键->属性->高级系统设置->高级->环境变量->新建NODE_PATH系统变量

 注意:蓝色框部分是后期使用命令自动生成该文件目录,因此如果此时通过浏览目录去选择的话是没有该文件夹(一定不要选错文件目录),所以配置环境变量时务必将其补充上:

\node_modules

修改用户变量path:

1.2 安装vue的环境

window+r打开控制台分别输入以下两条语句,安装淘宝npm,cnpm是为了提高我们安装依赖的速度!!

  1. # 安装淘宝npm
  2. npm install -g cnpm --registry=https://registry.npm.taobao.org
  3. # vue-cli 安装依赖包
  4. cnpm install --g vue-cli

1.3 用vue ui新建项目

1、首先在我们要保存的项目文件夹里右键点击PowerShell窗口

如果没有PowerShell窗口,则shift+右键即可

2、输入vue ui,如果提示无法加载文件,则可能是vue版本太低的原因,可以去控制台输入vue -V检查版本,vue版本是2.X的输入vue ui是没有反应的。

解决方法:1、安装最新的脚手架,在控制台输入

cnpm i -g @vue/cli

解决方法2:如果上述方法不可行,先将原来的vue卸载掉,在控制台输入

npm uninstall -g vue-cli 

然后输入以下命令行,进行高版本的安装:

npm install -g @vue/cli

 如果上述方法还不行,那就需要

  1. 以管理员身份运行PowerShell(点开Win10开始菜单,再点开所有应用,再点开Windows系统,找到并右键单击Windows Powershell,右键菜单中点击选择“以管理员身份运行”就可以了!)
  2. 执行:get-ExecutionPolicy,如果显示Restricted,表示状态是禁止的
  3. 执行:set-ExecutionPolicy RemoteSigned
  4. 选择Y

ok,上述三个解决方法总有一个适合你!!

1.4 编译器的选择(这边我们先默认选择idea)

  • 选择idea的话需要先安装插件
    • 点击file-settings-plungins,输入vue,安装vue.js。

1.5 安装element-ui

接下来我们引入element-ui组件(element.eleme.cn

那么,idea怎么安装这个组件呢?

  1. 点击idea左下角的Terminal,
  2. 输入cnpm install element-ui --save,这里可能会遇到一个小问题,如果显示cnpm不是内部命令,但是在控制台输入cnpm -v有显示信息,那么就是idea默认的cmd配置问题
    1. 解决方法
      1. file-settings-Tools-Terminal更改shellpath地址为本地的cmd存储位置即可。
      2. 记住更改完一定要重启idea,重启idea,重启idea!!

安装完成后,我们打开项目src目录下的main.js,引入element-ui依赖。

  1. import Element from 'element-ui'
  2. import "element-ui/lib/theme-chalk/index.css"

在全局写入下面代码就可以使用element-ui的组件了

Vue.use(Element)
在idea按如下图片配置,即可通过按钮直接运行而不需要通过命令行npm run serve运行了

1.6 安装axios

我们来安装axios(www.axios-js.com/),axios是一个基于 promise 的 HTTP 库,这样我们进行前后端对接的时候,使用这个工具可以提高我们的开发效率。

安装命令

cnpm install axios --save

main.js中全局引入axios

  1. import axios from 'axios'
  2. Vue.prototype.$axios = axios

二、项目托管的选择

目前用得比较多的就是git(分布式版本控制系统的代表)和svn(集中式版本控制系统的代表)

2.1 那么,两者有什么区别呢

  • SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而工作的时候,用的都是自己的电脑,所以首先要从中央服务器得到最新的版本,然后工作,完成工作后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,对网络带宽要求较高。
  • Git是分布式版本控制系统,没有中央服务器,每个人的电脑就是一个完整的版本库,工作的时候不需要联网了,因为版本都在自己电脑上。协同的方法是这样的:比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。Git可以直接看到更新了哪些代码和文件!
  • Git是目前世界上最先进的分布式版本控制系统。

毋庸置疑,我们选择的是Git!!

2.2 git的安装和环境配置

详情可以看我这篇文章手把手教你 Git安装详细版本以及idea配置git_YKbsmn的博客-CSDN博客

2.3 git的基本用法

具体可以看码云官网:Git 大全 - Gitee.com

初学者推荐廖雪峰的文档:使用Gitee - 廖雪峰的官方网站

2.4 简单说下git分支

首先在实际开发中,一个仓库(通常只放一个项目)主要存放两条分支:master和develop。master分支是创建git仓库时自动生成的,develop分支需要我们自己创建。

  • master分支:最稳定的分支,这个分支代表项目处于可发布的状态
  • develop分支:作为开发的分支,平行于master
  • feature分支:也就是功能分支,是为了解决某一个问题而设立的,当这个问题解决后,代码会合并回主分支develop后删除。(必须从develop分支创建)

2.5 github和gitee的选择

因为github是国外的,国内访问会很慢,所以推荐还是使用码云gitee!!

2.6 生成SSH公钥

在码云上创建ssh密钥,点击用户头像-设置-ssh密钥,可以看码云官网说明,很详细了,生成/添加SSH公钥 - Gitee.com

2.7 把项目托管到码云上

然后新建仓库,新建完仓库会有以下页面出现

1、首先执行划线的两个句子,如果显示git不是内部命令就是没有安装git或者没有配置git环境变量,具体可以参考下面这篇文章

手把手教你 Git安装详细版本以及idea配置git_YKbsmn的博客-CSDN博客

2、操作本地仓库,在自己的项目路径下,shift+右键打开PowerShell窗口或者右键 git bash输入

  1. git status(如果提示:fatal: not a git repository (or any of the parent directories): .git),则先输入git init,在输入git status
  2.  git add . (注意最后有个点,是添加全部文件的意思)
  3.  git commit -m "add files"
  4. 最后再输入 git status,如果显示On branch master
    nothing to commit, working tree clean,即操作正确!!

3、把本地仓库上传到码云中

  1. 在本地仓库的PowerShell窗口输入(把自己的本地仓库和云端仓库做一个关联)
    git remote add origin https://gitee.com/ykbsmn/vue_lab.git(这句话是上面截图的最后一块的第二句,根据自己的内容输入)
  2. 在本地仓库的PowerShell窗口输入(第一次需要输入码云的账号密码)
  3. 弹出窗口,输入码云的账号密码
  4. 最后刷新自己的码云仓库,显示如下即上传成功

三、通过vue-element-admin进行二次开发

3.1 步骤

3.1.1 基础模板下载地址

https://github.com/PanJiaChen/vue-admin-template

或者使用git命令克隆

git clone https://github.com/PanJiaChen/vue-admin-template

3.1.2 导入idea

注意:需要在本地提前安装 node 和 git

3.1.3 安装相应依赖

1、在idea安装依赖,在控制台输入:

npm install --registry=https://registry.npm.taobao.org

2、在idea启动项目,控制台输入:

npm run dev

3.2 框架介绍 

3.2.1 前端框架入口

3.2.2 放项目构建的脚本文件

3.2.3 src目录

3.2.4 使用步骤

(1)添加路由(component的import设置路由对应的页面内容地址)

(2)在views创建view页面

(3)在api文件夹创建js文件,定义接口地址和参数

(4)在创建vue页面引入js文件,调用方法实现功能。

2021.4.1~2021.4.8

四、说下git的基本操作

  • 查看分支:git branch
  • 创建分支:git branch <name>
  • 切换分支:git checkout <name>或者git switch <name>
  • 创建+切换分支:git checkout -b <name>或者git switch -c <name>
  • 合并某分支到当前分支:git merge <name>
  • 删除分支:git branch -d <name>

前面也说过了master分支代表可发布的状态,所以说我们在做实际项目的时候,可以先创建一个分支dev,然后再这个分支上面进行内容的修改,修改完成后再进行git add . 提交,再进行git commit -m "xxx",那么,dev分支的使命也就完成了,这时候就可以切换到master分支,把dev分支合并到master分支。最后再删除dev分支,搞定!!

五、mybatis_plus的简单掌握

具体可以看我另一篇文章

Mybatis plus笔记记_YKbsmn的博客-CSDN博客

六、简单说下前后端分离

七、后端项目搭建

工程搭建

八、idea插件准备

file-setting-plugins下载下面两个插件即可

8.1 lombok

还需要导入依赖

  1. <dependency>
  2. <groupId>org.projectlombok</groupId>
  3. <artifactId>lombok</artifactId>
  4. <version>1.18.16</version>
  5. </dependency>

8.2 maven Helper

这个插件可以有效的排除maven依赖!!

九、通过mybatis_plus的代码生成器

9.1 步骤

  1. 导入依赖
    1. <dependency>
    2. <groupId>com.baomidou</groupId>
    3. <artifactId>mybatis-plus-generator</artifactId>
    4. <version>3.3.2</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.apache.velocity</groupId>
    8. <artifactId>velocity-engine-core</artifactId>
    9. <version>2.3</version>
    10. </dependency>
  2. 在tset文件夹下面创建生成器插件文件
    1. package com.atguigu;
    2. import com.baomidou.mybatisplus.annotation.DbType;
    3. import com.baomidou.mybatisplus.annotation.IdType;
    4. import com.baomidou.mybatisplus.generator.AutoGenerator;
    5. import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    6. import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    7. import com.baomidou.mybatisplus.generator.config.PackageConfig;
    8. import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    9. import com.baomidou.mybatisplus.generator.config.rules.DateType;
    10. import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    11. import org.junit.Test;
    12. /**
    13. * @author
    14. * @since 2018/12/13
    15. */
    16. public class CodeGenerator {
    17. @Test
    18. public void run() {
    19. // 1、创建代码生成器
    20. AutoGenerator mpg = new AutoGenerator();
    21. // 2、全局配置
    22. GlobalConfig gc = new GlobalConfig();
    23. String projectPath = System.getProperty("user.dir");
    24. //写入项目的绝对路径********
    25. gc.setOutputDir("F:\\a大三下学期课程\\springBoot项目\\education\\service\\service_edu" + "/src/main/java");
    26. gc.setAuthor("zyk");
    27. gc.setOpen(false); //生成后是否打开资源管理器
    28. gc.setFileOverride(false); //重新生成时文件是否覆盖
    29. //UserServie
    30. gc.setServiceName("%sService"); //去掉Service接口的首字母I
    31. gc.setIdType(IdType.ID_WORKER_STR); //主键策略
    32. gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
    33. gc.setSwagger2(true);//开启Swagger2模式
    34. mpg.setGlobalConfig(gc);
    35. // 3、数据源配置*************
    36. DataSourceConfig dsc = new DataSourceConfig();
    37. dsc.setUrl("jdbc:mysql://localhost:3306/education?serverTimezone=GMT%2B8");
    38. dsc.setDriverName("com.mysql.cj.jdbc.Driver");
    39. dsc.setUsername("root");
    40. dsc.setPassword("686868");
    41. dsc.setDbType(DbType.MYSQL);
    42. mpg.setDataSource(dsc);
    43. // 4、包配置
    44. PackageConfig pc = new PackageConfig();
    45. pc.setModuleName("eduservice"); //模块名
    46. //包 com.atguigu.eduservice
    47. pc.setParent("com.atguigu");
    48. //包 com.atguigu.eduservice.controller
    49. pc.setController("controller");
    50. pc.setEntity("entity");
    51. pc.setService("service");
    52. pc.setMapper("mapper");
    53. mpg.setPackageInfo(pc);
    54. // 5、策略配置
    55. StrategyConfig strategy = new StrategyConfig();
    56. strategy.setInclude("edu_teacher");
    57. strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
    58. strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
    59. strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
    60. strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
    61. strategy.setRestControllerStyle(true); //restful api风格控制器
    62. strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
    63. mpg.setStrategy(strategy);
    64. // 6、执行
    65. mpg.execute();
    66. }
    67. }

常见bug 

java.lang.NoSuchMethodError: com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotEmpty(Ljava/lang

注意: 报以上bug通常都是因为mybatis-plus 版本不兼容问题,之前我的>mybatis-plus-generator版本是3.0.5就报这个bug,改成3.3.2就不会了

9.2 统一返回的json时间格式

默认情况下json时间格式带有时区,并且是世界标准时间,和我们的时间差了八个小时。所以在application.yaml下配置:

  1. spring:
  2. jackson:
  3. date-format: yyyy-MM-dd HH:mm:ss
  4. time-zone: GMT+8

十、Swagger2介绍

前后端分离开发模式中,api文档是最好的沟通方式。
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。

步骤:

  1. 先创建maven子模块common,在common模块里面再创建maven子模块service_base,
  2. 然后再创建和service模块下的service_edu同级的包放配置文件SwaagerConfig
    1. package com.atguigu.config;
    2. import com.google.common.base.Predicates;
    3. import org.springframework.context.annotation.Bean;
    4. import org.springframework.context.annotation.Configuration;
    5. import springfox.documentation.builders.ApiInfoBuilder;
    6. import springfox.documentation.builders.PathSelectors;
    7. import springfox.documentation.service.ApiInfo;
    8. import springfox.documentation.service.Contact;
    9. import springfox.documentation.spi.DocumentationType;
    10. import springfox.documentation.spring.web.plugins.Docket;
    11. import springfox.documentation.swagger2.annotations.EnableSwagger2;
    12. /**
    13. * @author
    14. * @date 2021/4/7 0007 - 上午 11:12
    15. */
    16. @Configuration//配置类
    17. @EnableSwagger2 //swagger注解
    18. public class SwaggerConfig {
    19. @Bean
    20. public Docket webApiConfig(){
    21. return new Docket(DocumentationType.SWAGGER_2)
    22. .groupName("webApi")
    23. .apiInfo(webApiInfo())
    24. .select()
    25. .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
    26. .paths(Predicates.not(PathSelectors.regex("/error.*")))
    27. .build();
    28. }
    29. private ApiInfo webApiInfo(){
    30. return new ApiInfoBuilder()
    31. .title("网站-课程中心API文档")
    32. .description("本文档描述了课程中心微服务接口定义")
    33. .version("1.0")
    34. .contact(new Contact("java", "http://atguigu.com", "1123@qq.com"))
    35. .build();
    36. }
    37. }
  3. 在service子模块下引入依赖service_base依赖

    1. <dependency>
    2. <groupId>com.example</groupId>
    3. <artifactId>service_base</artifactId>
    4. <version>0.0.1-SNAPSHOT</version>
    5. </dependency>
  4. 在service子模块下的service_edu下的EduApplication加入注解@ComponentScan设置包扫描规则

    1. @SpringBootApplication
    2. @ComponentScan(basePackages = {"com.atguigu"})
    3. public class EduApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(EduApplication.class,args);
    6. }
    7. }

      

十一、统一

11.1 统一返回格式

因为在实际开发中并不是我们一个人单独开发一个项目,而是一个团队在一起协作开发,那么每个人的习惯不同就会照成数据表达方式的不同,所以统一返回格式就可以解决上述问题。项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一,使前端(iOSAndroid,Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容。

步骤:

  1. 定义统一的返回数据格式和数据返回状态码
    1. @Data
    2. public class R {
    3. @ApiModelProperty(value = "是否成功")
    4. private Boolean success;
    5. @ApiModelProperty(value = "返回码")
    6. private Integer code;
    7. @ApiModelProperty(value = "返回消息")
    8. private String message;
    9. @ApiModelProperty(value = "返回数据")
    10. private Map<String ,Object> data=new HashMap<String, Object>();
    11. //构造方法私有化
    12. private R(){}
    13. public static R ok(){
    14. R r=new R();
    15. r.setSuccess(true);
    16. r.setCode(ResultCode.SUCCESS);
    17. r.setMessage("成功");
    18. return r;
    19. }
    20. public static R error(){
    21. R r=new R();
    22. r.setSuccess(false);
    23. r.setCode(ResultCode.ERROR);
    24. r.setMessage("失败");
    25. return r;
    26. }
    27. public R success(Boolean success){
    28. this.setSuccess(success);
    29. return this;
    30. }
    31. public R message(String message){
    32. this.setMessage(message);
    33. return this;
    34. }
    35. public R code(Integer code){
    36. this.setCode(code);
    37. return this;
    38. }
    39. public R data(String key, Object value){
    40. this.data.put(key, value);
    41. return this;
    42. }
    43. public R data(Map<String, Object> map){
    44. this.setData(map);
    45. return this;
    46. }
    47. }
    1. public interface ResultCode {
    2. Integer SUCCESS=20000;
    3. Integer ERROR=20001;
    4. }
  2. controller层的返回数据统一为R类型即可 !!

11.2 统一异常

11.2.1 全局异常处理

步骤:

  1. 创建一个统一异常处理类,添加全局异常处理规则
    1. @ControllerAdvice
    2. public class GlobalExceptionHandler {
    3. @ExceptionHandler(Exception.class)//指定出现什么异常时执行这个方法
    4. @ResponseBody//可以返回数据
    5. public R error(Exception e){
    6. e.printStackTrace();
    7. return R.error().message("程序员正在加班改bug中......");
    8. }
    9. }

2021.4.8~2021.4.15

11.2.2 特定异常处理

特定异常实际用得不是特别多,实则就是定向指定遇到什么异常执行什么操作。(springboot会先查找是否有特定异常,没有的话再执行全局异常)

  1. //特定异常处理
  2. @ExceptionHandler(ArithmeticException.class)
  3. @ResponseBody
  4. public R error(ArithmeticException e){
  5. e.printStackTrace();
  6. return R.error().message("执行了ArithmeticException异常");
  7. }

11.2.3 自定义异常处理

第一步:创建自定义异常类

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class ZykException extends RuntimeException{
  5. private Integer code;//状态码
  6. private String msg;//异常信息
  7. }

第二步:在统一异常类(全局异常类那里已经创建了)添加规则

  1. //自定义异常处理
  2. @ExceptionHandler(ZykException.class)
  3. @ResponseBody
  4. public R error(ZykException e){
  5. e.printStackTrace();
  6. return R.error().code(e.getCode()).message(e.getMsg());
  7. }

 第三步:因为这个异常不是系统自带的,所以系统是不认识它的,需要我们主动去抛出它。

  1. try {
  2. int i=10/0;
  3. }catch (Exception e){
  4. throw new ZykException(20001,"执行了自定义异常处理");
  5. }

11.3 统一日志处理

11.3.1 配置日志级别

日志记录器(Logger)的行为是分等级的。如下表所示:
分为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL(后面的会包含前面的信息,比如日志级别为FATAL,那么他会包含OFF的信息)
默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别,怎么配置呢?在application.yaml输入一下信息

  1. logging:
  2. level: info

注意:这种方式的日志只能打印在控制台上

11.3.2 LogBack

spring boot内部使用Logback作为日志实现的框架,可以把日志输出到文件和控制台。和log4j很相似

步骤:

  1. 删除application.yaml的日志配置文件
  2. 在resources下创建logback.xml文件

十二、vsCode

12.1 vsCode创建工作区

步骤:

  1. 先创建一个空文件夹
  2. 再用vscode打开这个文件夹
  3. 然后再另存为工作区
  4. 然后再用vscode打开工作区即可 

12.2 取消eslint

如果你不想使用 ESLint 校验(不推荐取消),只要找到 vue.config.js 文件。 进行如下设置 lintOnSave: false 即可。

  1. //lintOnSave: process.env.NODE_ENV === 'development',
  2. lintOnSave: false,

十三、前端知识的简单掌握

13.1 ECMAScript、JavaScript、babel 可以看我这篇文章

ECMAScript、JavaScript、babel_YKbsmn的博客-CSDN博客

13.2 vue.js以及axios可以看这篇

Vue简单入门和axios_YKbsmn的博客-CSDN博客

13.3 node.js简单入门

node.js简单入门_YKbsmn的博客-CSDN博客

13.4 前端模块化

前端模块化_YKbsmn的博客-CSDN博客

13.5 wbpack简单入门

webpack简单入门_YKbsmn的博客-CSDN博客

十四、前后端对接出现的问题

前端使用ajax请求连接后端接口,取得需要的数据,然后在前端进行渲染。

14.1 跨域问题

通过一个地址去访问另外一个地址,这个过程中如果有三个地方中的任何一个不一样,就会产生跨域问题,这三个地方分别为:

访问协议:http https        ip地址:192.168.1.1172.11.11.11       端口号:9528 8080

解决方法:

(1)在后端接口controller加入@CrossOrigin

(2)使用网关解决

14.2 前端设置访问的接口地址

在vue.config.js文件,在module.exports括号下面增加下面代码:

  1. proxy: {
  2. // change xxx-api/login => mock/login
  3. // detail: https://cli.vuejs.org/config/#devserver-proxy
  4. // 解决跨域问题: 当你请求是以/admin开头的接口,则我帮你代理访问到 http://localhost:8007
  5. // '/admin/*': {
  6. // target: 'http://localhost:8007', // 你接口的域名
  7. // // secure: false, // 如果是https接口,需要配置这个参数
  8. // changeOrigin: true, // 如果接口跨域,需要进行这个参数配置
  9. // }
  10. // ,
  11. [process.env.VUE_APP_BASE_API]: {
  12. target: `http://localhost:8007`,
  13. changeOrigin: true,
  14. pathRewrite: {
  15. ['^' + process.env.VUE_APP_BASE_API]: ''
  16. }
  17. }
  18. },

14.3 改完访问接口之后原来的登陆失效了的解决方法

(1)在后端controller创建一个模拟登陆类

  1. @RestController
  2. @RequestMapping("/adminService/user")
  3. @CrossOrigin
  4. public class staffLoginController {
  5. @PostMapping("login")
  6. public R login(){
  7. return R.ok().data("token","admin");
  8. }
  9. @GetMapping("info")
  10. public R info(){
  11. return R.ok().data("roles","[admin]").data("name","admin");
  12. }
  13. }

(2)前端的src/api/user.js,把login方法的url地址改成我们后端模拟登陆的地址即可

  1. export function login(data) {
  2. return request({
  3. url: '/adminService/user/login',
  4. method: 'post',
  5. data
  6. })

十五、前端页面实现的逻辑

15.1 条件查询

(1)通过element-ui组件实现,在列表上面添加条件输入表单,使用v-model双向绑定数据。

(2)查询调用getList()方法即可

15.2 逻辑删除

(1)需要在src/api/labStaff.js定义删除接口的地址

  1. deleteLabStaffId(id) {
  2. return request({
  3. url:`/adminService/lab_staff/${id}`
  4. method: 'delete'
  5. })
  6. }

(2)在views/lab/labStaff/list.vue引入/labStaff.js,添加弹窗组件提高用户体验感

(3)在弹窗组件方法中的then添加逻辑删除方法,成功删除返回提示信息,并重新显示数据刷新页面。

15.3 修改实验人员

(1)点击修改按钮,进入表单界面,根据id回显数据

(2)通过路由跳转进入数据回显页面,在index.js添加隐藏路由

  1. {
  2. path: 'edit/:id', //:id相当于占位符,里面要传参数
  3. name: 'labStaffEdit',
  4. component: () => import('@/views/lab/labStaff/save'),
  5. meta: { title: '编辑实验人员', noCache: true },
  6. hidden: true //****** 隐藏路由
  7. }

在list.vue调用这个路由:

  1. <router-link :to="'/labStaff/edit/'+scope.row.id">
  2. <el-button type="primary" size="mini" icon="el-icon-edit">修改</el-button>
  3. </router-link>

(3)在src/api/labStaff.js定义根据id查找实验人员接口地址

  1. getLabStaff(id){
  2. return request({
  3. url:`/adminService/lab_staff/getLabStaff/${id}`,
  4. method:'get'
  5. })
  6. }

(4)在save.vue里面调用(3)的方法实现数据回显方法,但是这个方法还没调用到哦!!

(5)页面加载前调用的方法只能在created里面调用

  1. created() {
  2. //判断路径有id值,做修改($route.params表示路由中的参数)
  3. if(this.$route.params&&this.$route.params.id){
  4. //从路径获取id值
  5. const id=this.$route.params.id
  6. //调用根据id查询的方法
  7. this.getInfo(id)
  8. }else{//路径没有id值,做添加
  9. //清空表单
  10. this.labStaff={}
  11. }
  12. },

注:因为我们这里添加和修改用的是同一个save页面,所以需要进行判断区分,因为只有修改的时候才会传入id进行数据回显,所以如果有id就执行修改方法,没有则执行添加方法

  1. saveOrUpdate(){
  2. //判断修改还是添加
  3. //根据labStaff是否有id
  4. if(this.labStaff.id){
  5. //修改
  6. this.updateLabStaff()
  7. }else{
  8. //添加
  9. this.saveLabStaff()
  10. }
  11. },

2021.4.15 ~ 2021.4.22

十六、阿里云对象存储oss

详情看我这篇文章:阿里云对象存储oss上传头像_YKbsmn的博客-CSDN博客_阿里云上传头像

十七、niginx

详情看我这篇文章:niginx反向代理_YKbsmn的博客-CSDN博客_nigix反向代理

十八、整合EasyExcel

详情看我这篇文章:EasyExcel的简单使用_YKbsmn的博客-CSDN博客_easyexcel使用

2021.4.23 ~ 2021.4.29

十九、供货商模块

19.1 添加完成之后怎么实现自动刷新页面

还是简单的增删查改,但是有一个需要注意的地方:

问题:就是添加完成之后页面不会自动刷新显示添加之后的数据。怎么解决?

分析:因为我们这里的添加是用对话框的形式打开的,本质上还是在同一个页面,而Created()方法在多次跳转到同一个页面的时候只会执行一次。

解决方法:在弹窗消息的关闭的时候顺带更新页面数据

  1. addData(){
  2. manufacturerApi.addManufacturer(this.manufacturer)
  3. .then(response => {
  4. this.$message({
  5. type: 'success',
  6. message: '添加成功!!',
  7. onClose:()=>{
  8. this.getList() //刷新页面数据
  9. }
  10. });
  11. })
  12. this.dialogFormVisible = false
  13. },

19.2 表单验证

(1) 定义验证规则(required为true表示这个值必定要填,trigger为blur表示鼠标失去焦点的时候会显示message的内容

  1. rules:{
  2. name:[
  3. {required:true,message:'请输入供货商',trigger: 'blur'}
  4. ],
  5. person:[
  6. {required: true,message: '请输入供货商负责人',trigger: 'blur'}
  7. ],
  8. phone:[
  9. {required: true,message: '请输入手机号码',trigger: 'blur'}
  10. ],
  11. }

 (2)在表单项添加prop属性,值和上面规则定义的名字一一对应

  1. <el-form-item label="供货商名字" prop="name">
  2. <el-input v-model="manufacturer.name"></el-input>
  3. </el-form-item>

 (3)注意在表单添加model和ref!!

<el-form ref="form"  :model="manufacturer" :rules="rules"  label-width="100px">

注意

  • 这里的:model不要写成v-model,v-model是双向绑定 。不然会报[Element Warn][Form]model is required for validate错误。然后这个model的值写的是你表单项的内容传递给的对象
  • ref可以理解为这个表单的一个id,后面方法可以引用他表示引用整个表单

(4)按钮

<el-button type="primary" @click="addData('form')">确 定</el-button>

(5)添加方法

  1. addData(formName){
  2. this.$refs[formName].validate((valid) =>{
  3. if(valid){
  4. manufacturerApi.addManufacturer(this.manufacturer)
  5. .then(response => {
  6. this.$message({
  7. type: 'success',
  8. message: '添加成功!!',
  9. onClose:()=>{
  10. this.getList()
  11. }
  12. });
  13. })
  14. this.dialogFormVisible = false
  15. }else{
  16. return false;
  17. }
  18. });
  19. },

2021.4.29 ~ 2021.4.30

二十 进货信息模块

20.1 简单说明

  • 进货信息和供应商信息以及物品信息都是有关联的
  • 进货信息表包含供应商id和物品id,通过id获取他们的名字显示在表格中
  • 添加进货信息之后不会马上增加库存,需要经过进货审核成功之后才会增加库存。
  • 进货信息再没有审核之前是可以修改和删除的

20.2 进货信息批量删除

批量删除的话需要用到element ui的type="selection"属性添加到表格。

(1)表单项添加可勾选属性,表格添加@selection-change="handleSelectionChange" 方法

<el-table-column prop="id" label="编号" width="120" type="selection" >
  1. <el-table :data="list" element-loading-text="数据加载中" border stripe fit highlight-current-row
  2. style="width: 100%" @selection-change="handleSelectionChange" ref="multipleTable">

 (2)新增添加data属性

  1. data(){
  2. return{
  3. multipleSelection:[],//数组:通过这个数组可以获取勾选到的数组的内容
  4. delBtnStu:true //批量删除按钮默认为不可选中状态
  5. }
  6. }

 (3)添加批量删除按钮 (disable属性为true表示禁用,由(3)知默认delBtnStu属性为true)(注意这里传参要传null,原因看后面这个方法的解释)

 <el-button type="danger" @click="deleteSelection(null)" :disabled="delBtnStu">批量删除</el-button>

(4) 定义(1)表格添加的方法,如果数组长度不为0,表明有选项被勾选,那么批量删除按钮就不会被禁止

  1. handleSelectionChange(val) {
  2. this.multipleSelection = val;
  3. //通过数组长度来判断是否有选中进货信息
  4. this.delBtnStu= val.length==0
  5. }

(5)定义批量删除方法

 通过是否有id判断是批量删除还是普通删除!!

  1. deleteSelection(id){
  2. this.$confirm('此操作将永久删除选中文件, 是否继续?', '提示', {
  3. confirmButtonText: '确定',
  4. cancelButtonText: '取消',
  5. type: 'warning'
  6. }).then(() => {
  7. var ids=[]
  8. if(id){ //如果id不为空表明是单条数据删除
  9. ids.push(id)
  10. }else{ //如果id不为空表明是批量删除,需要遍历multipleSelection数组获取所有id
  11. this.multipleSelection.forEach( row => {
  12. ids.push(row.id)
  13. })
  14. }
  15. importApi.delSelected(ids)
  16. .then(response => {
  17. //提示信息
  18. this.$message({
  19. type: 'success',
  20. message: '删除成功!'
  21. });
  22. //刷新数据
  23. this.getList()
  24. })
  25. })
  26. }

2021.4.3. ~ 2021.5.6 

21. 单点登录

21.1 单点登录逻辑

https://blog.csdn.net/YKbsmn/article/details/116231374?spm=1001.2014.3001.5501

21.2 前端登录逻辑

可以看这篇文章,内容讲得很细致

https://www.cnblogs.com/codeluojay/p/13123494.html

2021.5.6 ~2021.5.13

22. 图片验证码

22.1 验证码逻辑

  1. 客户端发起一个请求,进入 Security 过滤器链。
  2. 当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 logoutSuccessHandler 登出成功处理。如果不是登出路径则直接进入下一个过滤器。
  3. 当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler ,登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。

使用用户名密码来登录的,然后我们还想添加图片验证码,那么security给我们提供的UsernamePasswordAuthenticationFilter能使用吗?

首先security的所有过滤器都是没有图片验证码这回事的,但其实这里我们可以灵活点,如果你依然想沿用自带的UsernamePasswordAuthenticationFilter,那么我们就在这过滤器之前添加一个图片验证码过滤器。当然了我们也可以通过自定义过滤器继承UsernamePasswordAuthenticationFilter,然后自己把验证码验证逻辑和认证逻辑写在一起,这也是一种解决方式。

我们这次解决方式是在UsernamePasswordAuthenticationFilter之前自定义一个图片过滤器CaptchaFilter,提前校验验证码是否正确,这样我们就可以使用UsernamePasswordAuthenticationFilter了,然后登录正常或失败我们都可以通过对应的Handler来返回我们特定格式的封装结果数据。

权限管理器

security的ProviderManager会去委托AuthenticationProvider的时候,会根据UserDetailService的实现类(我们自己定义的),去委托给DaoAuthenticatioonProvider去搜索我们的数据库

常见bug总结

1.1 可能是springboot版本和spring-boot-starter版本不一致

10:33:58.146 [main] DEBUG org.springframework.boot.context.logging.ClasspathLoggingApplicationListen

解决方法:改成版本统一即可。 

1.2 生成的Mapper代码没有进行@Mapper映射。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'labStaffServiceImpl': Unsatisfied dependency expressed through field 'baseMapper'; 

解决方法:在mapper代码上加@Mapper,但是mapper文件多的话就比较麻烦。所以推荐可以创建一个专门放配置的包放配置类。(不要觉得麻烦,以后的插件什么的都可以放到这个类里面)

  1. @Configuration
  2. @MapperScan("com.zyk.adminService.mapper")
  3. public class LabConfig {
  4. }

1.3 前后端对接时候,前端数据显示超时

后端报下面这个错误

Caused by: java.io.EOFException: Unexpected EOF read on the socket

之所以会报这个错误的原因:

        前端mock把requestBody给使用掉了,导致传到后端的时候没有body,后端在解析的时候发现request请求头的content-length是59,就一直等待接收数据,与此同时,前端也在等待后端返回结果。1分钟后,前端等待超时,前端发EOF请求,后端收到以后,发现和预期的消息不一致,就报了EOF这个错误。

解决方法:

      在vue.config.js文件下找到

before: require('./mock/mock-server.js')

    ctrl+点击mock-server.js

      把这两句话注释掉即可!!

1.4 @postMapping报错

前端页面报这个错误

org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported

解决方法:

  1. @POSTMapping改为@RequestMapping
  2. @GetMapping
    用于将HTTP GET请求映射到特定处理程序方法的注释。具体来说,@GetMapping是一个作为快捷方式的组合注释
    @RequestMapping(method = RequestMethod.GET)。

    @PostMapping
    用于将HTTP POST请求映射到特定处理程序方法的注释。具体来说,@PostMapping是一个作为快捷方式的组合注释@RequestMapping(method = RequestMethod.POST)。

    @RequestMapping:
    一般情况下都是用@RequestMapping(method=RequestMethod.),因为@RequestMapping可以直接替代以上两个注解,但是以上两个注解并不能替代@RequestMapping,@RequestMapping相当于以上两个注解的父类!

1.5 前端报is not defined

vue.runtime.esm.js?2b0e:619 [Vue warn]: Property or method "captchaImg" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

错误的主要原因是我的captcha没有在data里面定义就直接用了

解决方法:在data里面定义captcha即可

1.6 security的默认登陆表单和自定义表单提交方式的不同

Access to XMLHttpRequest at 'http://localhost:9001/login' (redirected from 'http://localhost:9528/ucenterService/lab-member/login') from origin 'http://localhost:9528' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

出现这个问题其实还不是因为跨域问题导致的,而是因为我们后端SecurityConfig选用的是formLogin()方式去提交表单

它会提供一个默认的登陆页面,但是现在我们并没有去使用这个默认界面,而是自己定义一个登陆界面。因为我定义的登陆界面是采用json数据提交,而formLogin()默认的提交方式是form表单,所以我们需要把自定义的登陆界面改成form表单的提交形式。

改成

注意:这里的qs是一个流行的查询参数序列化和解析库。可以将一个普通的object序列化成一个查询字符串,或者反过来将一个查询字符串解析成一个object,帮助我们查询字符串解析和序列化字符串。

  • 打开控制台输入:npm install qs
  • 在main.js中导入qs插件:import qs from 'qs'
  • 在main.js中配置全局属性:Vue.prototype.$qs = qs
  • 之后在任意组件内可以使用this.$qs获取qs对象

1.7 跨域问题

在配置类写如下配置文件

  1. @Configuration
  2. public class CorsConfig implements WebMvcConfigurer {
  3. private CorsConfiguration buildConfig() {
  4. CorsConfiguration corsConfiguration = new CorsConfiguration();
  5. corsConfiguration.addAllowedOrigin("*");
  6. corsConfiguration.addAllowedHeader("*");
  7. corsConfiguration.addAllowedMethod("*");
  8. corsConfiguration.addExposedHeader("Authorization");
  9. return corsConfiguration;
  10. }
  11. @Bean
  12. public CorsFilter corsFilter() {
  13. UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  14. source.registerCorsConfiguration("/**", buildConfig());
  15. return new CorsFilter(source);
  16. }
  17. @Override
  18. public void addCorsMappings(CorsRegistry registry) {
  19. registry.addMapping("/**")
  20. .allowedOrigins("*")
  21. // .allowCredentials(true)
  22. .allowedMethods("GET", "POST", "DELETE", "PUT")
  23. .maxAge(3600);
  24. }
  25. }

在SecurityConfig配置文件下写如下代码:

  1. @Configuration
  2. @EnableWebSecurity
  3. @EnableGlobalMethodSecurity(prePostEnabled = true)
  4. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  5. @Override
  6. protected void configure(HttpSecurity http) throws Exception {
  7. //解决跨域
  8. http.cors().and().csrf().disable();
  9. }
  10. }

1.8 数据格式问题

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

原因:提交json字符串自动绑定到pojo入参时,类型需要是"application/json;charset=UTF-8",否则会抛"not supported"异常。

解决方法:

(1)后端模块添加下面语句转换类型即可

response.setContentType("application/json;charset=UTF-8");

 (2)注意:@RequestBody接受的是一个json对象的字符串,而不是Json对象,在请求时往往都是Json对象,前端用JSON.stringify(data)的方式就能将对象变成json字符串。

前端请求传Json对象则后端使用@RequestParam;

前端请求传Json对象的字符串则后端使用@RequestBody。

或者前端方法请求头里面添加:headers: { "Content-Type": "application/json;charset=utf-8" }

1.9  unknown mutation type: SET_TOKEN

前端报

 unknown mutation type: SET_TOKEN

再store/index.js下面添加mutations:模块的声明

  1. const store = new Vuex.Store({
  2. modules: {
  3. app,
  4. settings,
  5. user
  6. },
  7. mutations: { //在这个模块里面添加报错unknown的名字
  8. SET_TOKEN: (state, token) => {
  9. state.token = token
  10. localStorage.setItem("token", token)
  11. },
  12. },
  13. getters
  14. })

1.10 版本问题

Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/context/properties/Configuration

出现上面bug一般都是springboot和其他依赖之间版本的不对应。

我这里报错是因为Spring Cloud Alibaba和Spring Cloud以及 Spring Boot的版本对应问题

Spring Cloud Alibaba 项目都是基于 Spring Cloud,而 Spring Cloud 项目又是基于 Spring Boot 进行开发,并且都是使用 Maven 做项目管理工具,以下为我们使用的版本列表:

名称版本
JAVA JDK1.8.0_211
Spring Boot2.1.6.RELEASE
Spring Boot Admin2.1.5
Spring Cloudzhi'yGreenwich.SR2
Spring Cloud Alibaba0.9.0.RELEASE

1.11 前端页面报302错误,终极bug

前端页面可以登录,但是其他请求后端的接口报302的错误。

原因分析:

       报302一般都是因为后端使用了springsecurity框架,需要经过登录之后才能访问相应的内容,那么可以登录但是确不可以访问后端接口,只有一个原因,你配置springsecurity的端口和后端业务不在同一个端口里面,但是这两个端口你都导入了spring-boot-starter-security包,只要导入 spring-boot-starter-security 启动器后,Spring Security 就已经生效,默认拦截全部请求,如果用户没有登录,跳转到内置登录页面。 但是你登陆那个地方有进行配置所以才可以通过拦截成功访问,而另一个端口你并没有进行相应的配置。

解决方法:

      只需要在一个端口导入spring-boot-starter-security启动器包就行了,这是很重要的一点,我就是因为把spring-boot-starter-security放入了公共模块里面,其他业务模块又去引入这个公共模块,导致每个端口都有spring-boot-starter-security启动器,进而导致前端的业务无法访问后端

1.12 xml文件无法加载或者启动器没有扫描到mapper.xml

 Property ‘mapperLocations‘ was not specified &Invalid bound statement

原因分析:

       Idea默认是不解析xml的,我在配置文件中配置了mybatis-plus.mapper-locations=classpath*:**/mapper/xml/*.xml,但启动器一直没扫描到,之后百度了一下才知道是IDEA 默认不会编译源码文件夹(即src/main/java)中的 XML 文件。果然我在pom.xml中没有指明解析xml,在启动后生成的target 目录下文件的mapper包下没有找到相应的xml。

解决办法:

首先,配置文件加入如下:

    注意!Maven 多模块项目的扫描路径需以 classpath*: 开头 (即加载多个 jar 包下的 XML 文件)比如:

  1. mybatis-plus:
  2. mapper-locations: classpath*:com/zyk/ucenterService/mapper/xml/**Mapper.xml

然后:对于 Maven 项目,可指定 POM 文件的 resource。

  1. <build>
  2. <resources>
  3. <resource>
  4. <!-- xml放在java目录下-->
  5. <directory>src/main/java</directory>
  6. <includes>
  7. <include>**/*.xml</include>
  8. </includes>
  9. </resource>
  10. <!--指定资源的位置(xml放在resources下,可以不用指定)-->
  11. <resource>
  12. <directory>src/main/resources</directory>
  13. </resource>
  14. </resources>
  15. </build>

     如果上述都解决不了的话,在每个mapper接口上加上注解

@Mapper

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/100307
推荐阅读
相关标签
  

闽ICP备14008679号