前提
本教程主要是面向全栈开发的快速上手教程,具备Java及前端基础即可学习!
主要内容
开发工具:IDEA+VSCode
后端:SpringBoot+MyBatisPlus
前端:Vue+VueRouter+Vuex+ElementUI
接口调试:Swagger
数据模拟:MockJS
身份认证:JWT
后台管理:vue-admin-template
项目部署:阿里云ECS+Nginx+MySQL
快速搭建一个springboot项目
springboot默认整合了Tomcat
创建项目的方式有如下两种方法
方法1:idea选择Spring initializr方式创建
1、idea选择Spring initializr,点击next
注:选择Spring initializr表示创建Springboot项目,springboot项目是包含了Maven项目的内容的,即含有如下内容结构:
2、按如下配置参数,配置完后点击next
3、这里选择web,然后选择Spring web,这样就会有springmvc的相关依赖。这里一定要选择大版本是2的,如果选择3,那么你的jdk必须要是17以上,SpringBoot3以上版本不支持JDK11和JDK8,支持的最低版本是JDK17。
点击next
4、最后可以修改项目名也可以不修改,点击【finish】,完成Springboot项目的创建:
方法2:导入文件夹,然后自己创建需要的内容
我用的是这种方式,上一个方法我在创建后存依赖下载不了的问题。
1、文件结构如下:
2、pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- pom模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 父级项目 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- 项目信息 -->
<groupId>com.lxj</groupId><!-- 项目唯一标识 -->
<artifactId>Springboot-test</artifactId><!-- 项目名 -->
<version>0.0.1-SNAPSHOT</version><!-- 版本 -->
<name>Springboot-test</name><!-- 项目名 -->
<description>Demo project for Spring Boot</description>
<!-- 属性设置 -->
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、启动类
package com.xj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class StartUpApplication {
public static void main(String[] args) {
SpringApplication.run(StartUpApplication.class, args);
}
}
简单验证Springboot项目
1、新建一个controller包,再新建一个类
代码如下:
package com.xj.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
// http://localhost:8080/hello
// http:// 协议
// localhost:8080 域名
// /hello 路径
@GetMapping("/hello")
public String hello(){
return "hello SpringBoot";
}
}
2、启动Springboot的启动类
3、Springboot的内置的Tomcat默认的协议是http,默认的启动端口是8080
4、浏览器访问:http://localhost:8080/hello,如下出现controller类,“/hello”路径下的方法的返回值,则表示你的Springboot项目搭建没有问题。
Springboot的热部署
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
2、Springboot的配置文件中添加配置
配置文件位置:
添加的代码如下:
#热部署
spring.devtools.restart.enabled=true
spring.devtools.restart.additional-paths=src/main/java
3、idea的settings的如下位置,勾选,然后点击应用
4、在代码编辑页面,ctrl+shft+alt+/ ,点击第一个
进入后,勾选如下图中选项即可,勾选后点击【close】
这样就完成了热部署的配置,之后修改代码后:ctrl+s 即可,不用在重启
Springboot Controller
@RestController
controller 控制器
Spring Boot提供了@Controller和@RestController两种注解来标识此类负责接受和处理http请求。如果请求需要返回的是页面和数据,使用@controller,如果只是返回数据则可以使用@RestController
我们现在主要是前后端分离,@Controller需要返回一个视图,@RestController返回的可以是JSON也可以是字符串,所以RestController使用的比较多。
@RequestMapping
@GetMapping(“/hello”) 这个写法等价于:@RequestMapping(value = “/hello”,method = RequestMethod.GET)
@RequestMapping(“/hello”) //表示可以接受任何请求,只要是访问这个地址就可以接受到
@RequestMapping(value = “/hello”,method = RequestMethod.GET) //表示只能接受get请求,浏览器地址栏输入的都是get请求
package com.xj.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
// http://localhost:8080/hello
// http:// 协议
// localhost:8080 域名
// /hello 路径
// @GetMapping("/hello") 这个写法等价于:@RequestMapping(value = "/hello",method = RequestMethod.GET)
// @RequestMapping("/hello") //表示可以接受任何请求,只要是访问这个地址就可以接受到
@RequestMapping(value = "/hello",method = RequestMethod.GET) //表示只能接受get请求,浏览器地址栏输入的都是get请求
public String hello(){
return "hello SpringBoot";
}
}
以下展示了@RequestMapping的传参等用法
1、get方式传参
方法中接受的参数和浏览器地址中传递的参数名一致,则直接传递即可,如果不一致,则需要做特殊的处理才可以传递。
代码如下:
package com.xj.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ParaController {
/**
* 这是最简单的
* */
@RequestMapping(value = "/getTest1",method = RequestMethod.GET)
public String getTest1(){
return "getTest1";
}
/**
* controller接受参数,方法中的参数和浏览器传过来的参数一致
* */
@RequestMapping(value = "/getTest2",method = RequestMethod.GET)
// http://localhost:8023/getTest2?nickname=zhangsan&phone=123
// 方法中的参数必须和浏览器传过来的参数名字要保持一致,这样才可以被方法接受到
public String getTest2(String nickname,String phone){
System.out.println("nickname:"+nickname);
System.out.println("phone:"+phone);
return "getTest2";
}
/**
* controller接受参数,方法中的参数名和浏览器传过来的不一致,必须加@RequestParam(value = "nickname")
* 这里浏览器如果不传的话就会报400的错
* 如果不传,但是不想它报错后面加:required = false
* */
@RequestMapping(value = "/getTest3",method = RequestMethod.GET)
// http://localhost:8023/getTest3?nickname=zhangsan
public String getTest3(@RequestParam(value = "nickname",required = false) String name){
System.out.println("name:"+name);
return "getTest3";
}
}
这里如果注释后面不加 required = false 的话,浏览器不传递 nickename = zhangsan,访问这个方法会报错,报错如下:
通常来说4开头的错误都是客户端的问题。
2、post方式传参
浏览器无法直接输入地址访问post请求,需要借助一个工具调试,这里用的是比较广泛的工具postman
post请求传参一般是在body中传,使用的传参方式如下:
也可以在param中传,这样的话是会拼接到地址后面,参数少可以这样,参数多不建议在这里传
具体代码如下:
package com.xj.controller;
import com.xj.entity.User;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ParaControllerPost {
@RequestMapping(value = "/postTest1", method = RequestMethod.POST)
public String postTest1() {
return "POST1请求";
}
@RequestMapping(value = "/postTest2", method = RequestMethod.POST)
public String postTest2(String username, String password) {
System.out.println("username:" + username);
System.out.println("password:" + password);
return "POST2请求";
}
@RequestMapping(value = "/postTest3", method = RequestMethod.POST)
public String postTest3(User user) {
System.out.println(user);
return "POST3请求";
}
@RequestMapping(value = "/postTest4", method = RequestMethod.POST)
public String postTest4(@RequestBody User user) {
System.out.println(user);
return "POST4请求";
}
}
user实体
package com.xj.entity;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
请求3中,我们只需要传入和User对应的属性名相同的参数,就会自动封装到User对象中,如下:
这里的参数和User类中的属性名相同:
后台获取到传过来的参数,并封装到User对象中
请求4中@RequestBody表示只接受json方式传参,如果还是用之前的传参方式访问请求4,就会报如下错误:
前台的传参必须是json,并且参数的数据类型需要和后台对应,应如下传参:
Springboot 文件上传+拦截器
静态文件目录
static这个目录Springboot默认做了一个映射,前台可以直接访问,如下即可说明:
前台访问效果:
如果你需要在static文件夹中新建一个目录,浏览器访问的时候则需要再加一个目录结构
Springboot文件上传
方法中有MultipartFile类接收前端传过来的文件,用如下这种方式发送请求:
具体代码如下:
package com.xj.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
@RestController
public class fileUploadController {
@PostMapping("/upload") //等价于@RequestMapping(value = "/upload",method = RequestMethod.POST)
public String uploadFile(String nickname, MultipartFile photo, HttpServletRequest request){
System.out.println(nickname);
//获取图片的原始名称
System.out.println(photo.getOriginalFilename());
//获取文件类型
System.out.println(photo.getContentType());
//文件上传后暂时存放路径
String path = request.getServletContext().getRealPath("/upload");
System.out.println(path);
return "上传成功";
}
}
Springboot拦截器
前端访问地址,先会经过拦截器,拦截器返回true则可以继续访问Controller
具体代码如下
package com.xj.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// request 可以拿到前端传过来的一些数据,比如cookie,response可以返回一些信息给前端
System.out.println("loginInterceptor"); //这里可以代表做一些操作
return true; //返回true,表示通过拦截器,false表示不通过拦截器,不会继续往下访问Controller
}
}
拦截器要注册和限定拦截请求路径
package com.xj.config;
import com.xj.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册拦截器,拦截的地址是 /user下面的
// registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/user/**");
//注册拦截器,拦截的地址是所有
registry.addInterceptor(new LoginInterceptor());
}
}
访问之前的post3请求,先会打印拦截器中的内容,请求如下:
结果:
loginInterceptor
username:zhangsan
password:123
RestFul服务+Swagger
Springboot中的restful
restful 是一种编程规范和约束
如下是Springboot体现的restful编程规范
package com.xj.controller;
import com.xj.entity.User;
import org.springframework.web.bind.annotation.*;
@RestController
public class UserController {
@GetMapping("/user/{id}")
public String getUserById(@PathVariable int id) { //@PathVariable 注解配合 @GetMapping("/user/{id}") 中的{id}可以在请求路径中传递参数值并获取到参数值
System.out.println(id);
return "根据id获取用户信息";
}
@PostMapping("/user")
public String save(User user) {
return "添加用户";
}
@PutMapping("/user")
public String update(User user) {
return "跟新用户";
}
@DeleteMapping("/user/{id}")
public String deleteById(@PathVariable int id) {
System.out.println(id);
return "根据ID删除用户";
}
}
postman验证,需要选择不同的请求方式
第一个验证根据用户id获取用户信息
第二个验证,添加用户
第三个验证,更新用户
第四个验证:删除用户
Springboot整合swagger
swagger主要是用来监控接口的,swagger可以检测到后台提供给前台的接口,并且可以在监控页面中调试
1、配置类
com包下的所有api都交给swagger管理,配置如下
package com.xj.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com")) //com包下的所有api都交给swagger管理
.paths(PathSelectors.any())
.build();
}
/**
* 创建该API的基本信息(这些基本信息会展现在文档页面中)
* 访问地址:http://项目实际地址/swagger-ui.html
*
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Spring Boot中使用Swagger2")
.description("")
.version("1.0")
.build();
}
}
2、访问地址:http://项目实际地址/swagger-ui.html
这里有描述是因为在Controller中添加了 @ApiOperation(value = “根据id获取用户信息”)
3、可以在swagger进行调试
注意:
在添加swagger配置类后,本人的Springboot在启动的时候报错,报错信息如下:
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
完整报错:
2023-09-03 18:19:48.806 WARN 8548 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
2023-09-03 18:19:49.042 INFO 8548 --- [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2023-09-03 18:19:49.057 INFO 8548 --- [ restartedMain] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-09-03 18:19:49.080 ERROR 8548 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.22.jar:5.3.22]
at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_152]
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.22.jar:5.3.22]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.3.jar:2.7.3]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) [spring-boot-2.7.3.jar:2.7.3]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.3.jar:2.7.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) [spring-boot-2.7.3.jar:2.7.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) [spring-boot-2.7.3.jar:2.7.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) [spring-boot-2.7.3.jar:2.7.3]
at com.xj.StartUpApplication.main(StartUpApplication.java:9) [classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_152]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_152]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_152]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_152]
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.7.3.jar:2.7.3]
Caused by: java.lang.NullPointerException: null
at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:112) ~[springfox-spi-2.9.2.jar:null]
at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:109) ~[springfox-spi-2.9.2.jar:null]
at com.google.common.collect.ComparatorOrdering.compare(ComparatorOrdering.java:37) ~[guava-20.0.jar:na]
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) ~[na:1.8.0_152]
at java.util.TimSort.sort(TimSort.java:220) ~[na:1.8.0_152]
at java.util.Arrays.sort(Arrays.java:1438) ~[na:1.8.0_152]
at com.google.common.collect.Ordering.sortedCopy(Ordering.java:855) ~[guava-20.0.jar:na]
at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:57) ~[springfox-spring-web-2.9.2.jar:null]
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:138) ~[springfox-spring-web-2.9.2.jar:null]
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:135) ~[springfox-spring-web-2.9.2.jar:null]
at com.google.common.collect.Iterators$7.transform(Iterators.java:750) ~[guava-20.0.jar:na]
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47) ~[guava-20.0.jar:na]
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47) ~[guava-20.0.jar:na]
at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:52) ~[guava-20.0.jar:na]
at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:50) ~[guava-20.0.jar:na]
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:249) ~[guava-20.0.jar:na]
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:209) ~[guava-20.0.jar:na]
at com.google.common.collect.FluentIterable.toList(FluentIterable.java:614) ~[guava-20.0.jar:na]
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.defaultContextBuilder(DocumentationPluginsBootstrapper.java:111) ~[springfox-spring-web-2.9.2.jar:null]
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.buildContext(DocumentationPluginsBootstrapper.java:96) ~[springfox-spring-web-2.9.2.jar:null]
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:167) ~[springfox-spring-web-2.9.2.jar:null]
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-5.3.22.jar:5.3.22]
... 19 common frames omitted
后查阅得出报错原因是因为本人使用的Springboot版本是2.7.3(Springboot 2.6.x以上和swagger2不兼容),之后我是修改Springboot版本号解决的(修改成了2.1.12.RELEASE),还有一种解决方法是在Springboot的启动文件中添加相关配置(本人没有去尝试),具体可以查看这边博客 https://blog.csdn.net/weixin_48568302/article/details/126163107
MyBatisPlus
mybatis + Springboot
mybatis以前叫ibatis
Springboot整合mybatis
1、Springboot的配置文件中添加如下配置:
#Springboot + MybatisPlus + alibaba连接池 连接mysql数据库 配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ruoyi?userSSL=false
spring.datasource.username=root
spring.datasource.password=123456
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis和mybatisplus整合Springboot的Springboot配置文件是一样配置的
2、Springboot的启动类中添加如下注解:
@MapperScan("com.xj.mapper")
这个注解告诉Springboot扫描mapper的路径
3、mapper接口
package com.xj.mapper;
import com.xj.entity.Role;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface RoleMapper {
//查询所有角色
@Select("select role_name as roleName,role_key as roleKey from sys_role")
public List<Role> queryAllRole();
}
这里的sql是写在注解中的,还有一种方式是用xml来写sql
查询sql中用as是因为这样可以和实体中的属性名称保持一致
4、实体:
package com.xj.entity;
public class Role {
private String roleName;
private String roleKey;
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleKey() {
return roleKey;
}
public void setRoleKey(String roleKey) {
this.roleKey = roleKey;
}
@Override
public String toString() {
return "Role{" +
"roleName='" + roleName + '\'' +
", roleKey='" + roleKey + '\'' +
'}';
}
}
5、controller 直接调用mapper中的查询方法
package com.xj.controller;
import com.xj.entity.Role;
import com.xj.mapper.RoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RoleController {
@Autowired
private RoleMapper roleMapper;
@GetMapping("/role")
public List queryRole(){
List<Role> list = roleMapper.queryAllRole();
return list;
}
}
mybatisplus + springboot
1、Springboot配置文件,和整合mybatis的一样
2、Springboot的启动类添加扫描注解,和整合mybatis的一样
3、mapper接口,不用写接口方法,继承baseMapper即可
package com.xj.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xj.entity.Role;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RoleMapper extends BaseMapper<Role> {
}
4、实体,如果表名和实体名不一致,需要用注解写明数据库表名如 @TableName(“sys_role”),如果表中属性名和实体属性名不一致,也需要用注解写名数据库的属性名 @TableField(“role_key”),具体如下:
package com.xj.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.List;
@TableName("sys_role")
public class Role {
@TableField("role_id")
private String roleId;
@TableField("role_name")
private String roleName;
@TableField("role_key")
private String roleKey;
//exist = false加这个表示数据库没有查到对于字段也不会报错
@TableField(exist = false)
List<RoleMenu> roleMenus;
public List<RoleMenu> getRoleMenus() {
return roleMenus;
}
public void setRoleMenus(List<RoleMenu> roleMenus) {
this.roleMenus = roleMenus;
}
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleKey() {
return roleKey;
}
public void setRoleKey(String roleKey) {
this.roleKey = roleKey;
}
@Override
public String toString() {
return "Role{" +
"roleId='" + roleId + '\'' +
", roleName='" + roleName + '\'' +
", roleKey='" + roleKey + '\'' +
'}';
}
}
5、Controller直接调用mapper中的方法,因为其继承了BaseMapper,所以直接调用BaseMapper中的操作数据库方法即可(mybatisplus只支持单表操作不用去手写sql,对于多表查询还是要依赖于mybatis并且需要写sql)。
package com.xj.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xj.entity.Role;
import com.xj.mapper.RoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RoleController {
@Autowired
private RoleMapper roleMapper;
@GetMapping("/role")
public List queryRole(){
List<Role> list = roleMapper.selectList(null);
return list;
}
@GetMapping("/role/find")
public List queryRoleByRoleName(){
QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("role_key","admin");
List<Role> roles = roleMapper.selectList(queryWrapper);
return roles;
}
@GetMapping("/roleMenus")
public List queryRoleMenus(){
List<Role> list = roleMapper.selectAllRoleAndRoleMenu();
return list;
}
@GetMapping("/role/findByPage")
public IPage findRoleByPage(){
//设置起始值和每页数据条数
Page<Role> page = new Page<>(0,5);
IPage iPage = roleMapper.selectPage(page,null);
return iPage;
}
}
此处调用的查询方法selectList我们不需要传值,所以传个null即可。
mybatis和mybatisplus可以一起使用
因为mybatisplus提供了单表的crud方法,但是多表的不支持,所以可以用mybatis的Mapper类,写我们自己想要的多表查询sql
查询角色下有多少菜单,这里用到了多表查询,使用的是mybatis
1、实体
实体RoleMenu,角色和菜单的关系表,相应数据如下:
代码如下:
package com.xj.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("sys_role_menu")
public class RoleMenu {
@TableField("role_id")
private String roleId;
@TableField("menu_id")
private String menuId;
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
public String getMenuId() {
return menuId;
}
public void setMenuId(String menuId) {
this.menuId = menuId;
}
}
实体Role,数据库数据如下:
代码如下,这里主要添加了 List roleMenus; 用来接收菜单列表:
package com.xj.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.List;
@TableName("sys_role")
public class Role {
@TableField("role_id")
private String roleId;
@TableField("role_name")
private String roleName;
@TableField("role_key")
private String roleKey;
//exist = false加这个表示数据库没有查到对于字段也不会报错
@TableField(exist = false)
List<RoleMenu> roleMenus;
public List<RoleMenu> getRoleMenus() {
return roleMenus;
}
public void setRoleMenus(List<RoleMenu> roleMenus) {
this.roleMenus = roleMenus;
}
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleKey() {
return roleKey;
}
public void setRoleKey(String roleKey) {
this.roleKey = roleKey;
}
@Override
public String toString() {
return "Role{" +
"roleId='" + roleId + '\'' +
", roleName='" + roleName + '\'' +
", roleKey='" + roleKey + '\'' +
'}';
}
}
2、Mapper
RoleMenuMapper
package com.xj.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xj.entity.RoleMenu;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface RoleMenuMapper extends BaseMapper<RoleMenu> {
@Select("select * from sys_role_menu where role_id = #{roleId}")
List<RoleMenu> selectRoleMenuByRoleId(int roleId);
}
RoleMapper
package com.xj.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xj.entity.Role;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface RoleMapper extends BaseMapper<Role> {
@Select("select * from sys_role")
@Results(
value = {
//column 数据库表字段,property 实体字段
@Result(column = "role_id",property = "roleId"),
@Result(column = "role_name",property = "roleName"),
@Result(column = "role_key",property = "roleKey"),
//这里 我们调用了 RoleMenuMapper.selectRoleMenuByRoleId 的方法,@Many 一对多的意思
@Result(column = "role_id",property = "roleMenus",javaType = List.class,
many = @Many(select = "com.xj.mapper.RoleMenuMapper.selectRoleMenuByRoleId"))
}
)
List<Role> selectAllRoleAndRoleMenu();
}
3、Controller
访问/role 的时候roleMenus为null,访问roleMenus的时候就有值了。
package com.xj.controller;
import com.xj.entity.Role;
import com.xj.mapper.RoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class RoleController {
@Autowired
private RoleMapper roleMapper;
@GetMapping("/role")
public List queryRole(){
List<Role> list = roleMapper.selectList(null);
return list;
}
@GetMapping("/roleMenus")
public List queryRoleMenus(){
List<Role> list = roleMapper.selectAllRoleAndRoleMenu();
return list;
}
}
mybatisplus的条件查询
在Controller中加入如下代码:
@GetMapping("/role/find")
public List queryRoleByRoleName(){
QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("role_key","admin");
List<Role> roles = roleMapper.selectList(queryWrapper);
return roles;
}
结果如下:
mybatisplus的分页
1、分页的配置文件
package com.xj.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor pageSplitInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
2、在Controller写如下代码即可实现分页
@GetMapping("/roleMenu/findByPage")
public IPage findRoleMenuBySplitPage(){
//设置起始值和每页条数
Page<RoleMenu> roleMenuPage = new Page<>(0,5);
IPage iPage = roleMenuMapper.selectPage(roleMenuPage, null);
return iPage;
}
3、具体效果如下:
Vue
基本用法
基本用法的代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 1、导入vue 的脚本文件,也可以下载到本地,导入本地路径文件 -->
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<!--2、 声明要被vue所控制的DOM区域 -->
<div id="app">
{{message}}
</div>
<!-- 3、创建vue的实例对象,这里的实例对象是hello -->
<script>
const hello = {
//指定数据源,即mvvm中的Model
data:function(){
return{
message:'hello vue3 !'
}
}
}
const app = Vue.createApp(hello)
app.mount('#app')
</script>
</body>
</html>
小技巧:在vscode中如果是html文件,写一个英文感叹号即可获取到html的结构代码。
内容渲染指令
要想html的字符串渲染成html效果,可以用v-html指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<p>姓名:{{username}}</p>
<p>性别:{{gender}}</p>
<p>{{desc}}</p>
<!-- 内容渲染指令 -->
<p v-html="desc"></p>
</div>
<script>
const person = {
data:function(){
return{
username:'zhangsan',
gender:'男',
desc:'<a href="http://www.baidu.com">百度</a>'
}
}
}
const app = Vue.createApp(person)
app.mount('#app')
</script>
</body>
</html>
效果如下:
属性绑定指令
属性绑定指令方式
:属性名=属性值,属性值从vue声明的变量中获取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<a :href="link">百度</a>
<input type="text" :placeholder="inputValue"></input>
<img :src="imgSrc" :style="{width:w}">
</div>
<script>
const test03 = {
data:function(){
return{
link:'http://www.baidu.com',
// 文本框的占位内容
inputValue:'请输入内容',
// 图片的src地址
imgSrc:'https://i.niupic.com/images/2020/07/18/8qaW.jpg',
w:'500px'
}
}
}
const app = Vue.createApp(test03)
app.mount('#app')
</script>
</body>
</html>
效果如下:
使用JavaScript表达式
可以使用表达式,但是不可以使用语句,如for循环语句
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<p>{{number+1}}</p>
<p>{{ok?"True":"False"}}</p>
<p>{{message.split("").reverse().join("")}}</p>
<p :id="'list'+id">xxx</p>
<p>{{user.name}}</p>
</div>
<script>
const test04 = {
data:function(){
return{
number:9,
ok:false,
message:"abc",
id:3,
user:{
name:"zhangsan"
}
}
}
}
const app = Vue.createApp(test04)
app.mount('#app')
</script>
</body>
</html>
结果如下:
事件绑定指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<h3>count 的值为:{{count}}</h3>
<!-- click事件绑定addCount方法,click有两种写法,如下 -->
<button v-on:click="addCount">+1</button>
<button @click="addCount">+1</button>
<!-- 另一种方式实现+1,click点击事件绑定表达式方式 -->
<button @click = "count +=1">+1</button>
</div>
<script>
const test05 = {
data:function(){
return{
count:0
}
},
// method 要写在定义的test05对象里面
methods:{
addCount(){
// 这里的this代表的就是 test05对象
this.count += 1
}
}
}
const app = Vue.createApp(test05)
app.mount('#app')
</script>
</body>
</html>
效果如下:
条件渲染指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<button @click="flag=!flag">点击对flag取反</button>
<!-- 为false的时候,标签不会被创建 -->
<p v-if="flag">v-if控制,显示</p>
<!-- 为false的时候,标签会创建,只是不显示,如果是频繁的切换显示效果,建议用v-show -->
<p v-show="flag">v-show控制,显示</p>
</div>
<script>
const test06 = {
data:function(){
return{
flag:false
}
}
}
const app = Vue.createApp(test06)
app.mount('#app')
</script>
</body>
</html>
效果如下:
v-if 和v-else-if指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<p v-if="num>0.5">随机数大于0.5</p>
<p v-else>随机数小于或等于0.5</p>
<hr/>
<p v-if="type==='a'">优秀</p>
<p v-else-if="type==='b'">良好</p>
<p v-else-if="type==='c'">一般</p>
<p v-else="type==='d'">一般</p>
</div>
<script>
const test07 = {
data:function(){
return{
num:Math.random(),
type:"b"
}
}
}
const app = Vue.createApp(test07)
app.mount('#app')
</script>
</body>
</html>
效果如下:
列表渲染指令
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<li v-for="(user,i) in userList">
索引是:{{i}},
姓名是:{{user.name}}
</li>
</div>
<script>
const test08 = {
data:function(){
return{
userList:[
{id:1,name:"aa"},
{id:2,name:"bb"},
{id:3,name:"cc"},
]
}
}
}
const app = Vue.createApp(test08)
app.mount('#app')
</script>
</body>
</html>
效果如下:
v-for
v-for中的key
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/vue@3"></script>
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<!-- v-model可以实现双向绑定,页面可以拿到定义的变量值,变量也可以拿到页面的值,:value只可以读取到变量值 -->
<input type="text" v-model="name">
<button @click="addNewUser">添加</button>
</div>
<!-- 用户列表展示区域 -->
<ul>
<!-- for循环如果不加索引,系统有的时候会报错,如果不报错,也会出现系统沿用默认的索引,这样我们在选中bb后,再添加一个dd,则选中内容会变成aa -->
<!-- 如果用默认的index,也会出现这种情况,因为这样添加后,默认的index指向的数据会发生变化 -->
<!-- 最为正确的是应该添加userlist中的id -->
<li v-for="(user,index) in userList" :key="user.id">
<input type="checkbox"/>
姓名:{{user.name}}
</li>
</ul>
</div>
<script>
const test08 = {
data:function(){
return{
userList:[
{id:1,name:"aa"},
{id:2,name:"bb"},
{id:3,name:"cc"},
],
//输入的用户名,这里如果不设置为空的话,点击添加,输入框会保留之前添加的
name:'',
// 下一个可用的id值
nextid:4
}
},
methods:{
//添加按钮调用方法
addNewUser(){
//在原来列表的上方添加内容
this.userList.unshift({id:this.nextid,name:this.name})
this.name = ''
this.nextid++
}
}
}
const app = Vue.createApp(test08)
app.mount('#app')
</script>
</body>
</html>
效果如下:
npm(node package manager)
node.js的包管理工具,依赖node.js,用来管理前端框架,可以像maven那样下载所需要的依赖包。
vue cli 创建vue项目
是vue官方提供的构建工具,通常成为脚手架,用于快速搭建一个带有热重载(在代码修改后不必刷新页面即可呈现修改后的效果)
1、安装vue cli
在你要创建项目的代码目录运行如下命令:
npm install -g @vue/cli
2、创建vue项目,输入命令
vue create 项目名称
3、选择配置
一开始初学,建议选择最后一个选项
回车后进入下图,去掉这个选项如下图所示(空格可以控制取消和选中):
4、这里选择vue3
5、选择配置信息记录在哪个文件下,这里一般是选择package.json,类似maven中的pom.xml
6、是否保存为模版,这里不保存,输入N即可
7、开始下载相关依赖,出现如下所示,则表示项目创建完成(下载的时候话的时间有点长,应该不用下载那么多依赖)
8、运行项目
npm run serve
9、访问上面的地址,成功访问则表示项目创建是OK的
项目代码
main.js
// 通过vue,导入vue中的createApp方法
import { createApp } from 'vue'
// 导入App.vue文件
import App from './App.vue'
// 把App内容,注入到app标签中
createApp(App).mount('#app')
vue中组件开发
1、新建文件test.vue,组件代码
// 组件内容
<template>
<h1> 组件测试</h1>
</template>
// 组件动作
<script>
</script>
// 组件样式
<style>
</style>
2、App.vue中使用:
// 这个文件其实也是一个组件,最终是渲染到了index.html中,因为main.js引用了这个文件内容,最后注入到了app这个id中。
<template>
<img alt="Vue logo" src="./assets/logo.png">
<!-- 使用组件 -->
<test></test>
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
// 引入test组件
import test from './components/test.vue'
export default {
name: 'App',
components: {
HelloWorld,
test
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
第三方组件element-ui
创建一个vue项目,基于vue 2.x
一个简单的电影组件:
Movie.vue
<template>
<div>
<h1>{{title}}</h1>
<span>{{rating}}</span>
<button @click="funSc">点击收藏</button>
</div>
</template>
<script>
export default {
name:"Movie",
props:["title","rating"],
data:function(){
return{
}
},
methods:{
funSc(){
alert("收藏成功")
}
}
}
</script>
App.vue 组件中使用 Movie.vue 组件
App.vue
<template>
<div id="app">
<Movie v-for="movie in movies" :key="movie.id" :title="movie.title" :rating="movie.rating"></Movie>
</div>
</template>
<script>
import Movie from './components/Movie.vue'
export default {
name: 'App',
data:function(){
return{
movies:[
{id:1,title:"叶问1",rating:9.4},
{id:2,title:"叶问2",rating:8.5},
{id:3,title:"叶问3",rating:8.9}
]
}
},
components: {
Movie
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
element-ui介绍
Element是国内饿了么公司提供的一套开源前端框架
文档地址:https://element.eleme.cn/#/zh-CN/
element-ui安装
安装命令:npm i element-ui -S
这里提示下载了9个包,这9个包放到了node_modules中,并且在package.json中会有记录
我们在把项目传递给其他人的时候,不需要把node_modules中的包也发过去,只需要把package.json文件发过去就可以了,然后在项目的控制台运行 npm install 命令即可把package.json中的依赖下载会node_modules目录中。所以如果你在网上下载下来的项目运行不了(npm run serve),你应该先去检查一下有没有node_modules目录,如果没有则运行一下npm install,然后再去运行项目。
element-ui引入
在 main.js 中写入以下内容:
import Vue from 'vue'
//引入 ElementUI
import ElementUI from 'element-ui';
//引入 ElementUI 的css供ElementUI中的组件使用
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'
//全局注册
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
element-ui使用,以table表格为例
参考官方文档table组件
添加组件HelloElTable,代码如下,
<template>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName"
>
<el-table-column prop="date" label="日期" width="180"> </el-table-column>
<el-table-column prop="name" label="姓名" width="180"> </el-table-column>
<el-table-column prop="address" label="地址"> </el-table-column>
</el-table>
</template>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
<script>
export default {
methods: {
tableRowClassName({ row, rowIndex }) {
if (rowIndex === 1) {
return "warning-row";
} else if (rowIndex === 3) {
return "success-row";
}
return "";
},
},
data() {
return {
tableData: [
{
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
],
};
},
};
</script>
在App.vue中引入
如下:
代码如下:
<template>
<div id="app">
<Movie v-for="movie in movies" :key="movie.id" :title="movie.title" :rating="movie.rating"></Movie>
<HelloElTable></HelloElTable>
</div>
</template>
<script>
import Movie from './components/Movie.vue'
import HelloElTable from './components/HelloElTable.vue'
export default {
name: 'App',
data:function(){
return{
movies:[
{id:1,title:"叶问1",rating:9.4},
{id:2,title:"叶问2",rating:8.5},
{id:3,title:"叶问3",rating:8.9}
]
}
},
components: {
Movie,
HelloElTable
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
index引入App.vue中的id
代码如下:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
启动vue项目,index.html显示如下:
第三方图标库
由于element ui提供的字体图标较少,一般会采用其他图标库,如著名的font awesome
文档地址:https://fontawesome.dashgame.com/
安装
控制台输入命令:npm install font-awesome
使用
在man.js中引入 import ‘font-awesome/css/font-awesome.min.css’
具体代码如下:
import Vue from 'vue'
//引入 ElementUI
import ElementUI from 'element-ui';
//引入 ElementUI 的css供ElementUI中的组件使用
import 'element-ui/lib/theme-chalk/index.css';
//引入图标库
import 'font-awesome/css/font-awesome.min.css'
import App from './App.vue'
//全局注册
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
HelloElTable.vue 组件中使用如下:
<template>
<!-- template中只能放一个标签,所以可以在最外层套一个div标签,这样就可以并列放其他标签,如图标标签i -->
<div>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName"
>
<el-table-column prop="date" label="日期" width="180"> </el-table-column>
<el-table-column prop="name" label="姓名" width="180"> </el-table-column>
<el-table-column prop="address" label="地址"> </el-table-column>
</el-table>
<i class="fa fa-camera-retro fa-lg">fa-lg</i>
<i class="fa fa-automobile fa-lg">fa-lg</i>
</div>
</template>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
<script>
export default {
methods: {
tableRowClassName({ row, rowIndex }) {
if (rowIndex === 1) {
return "warning-row";
} else if (rowIndex === 3) {
return "success-row";
}
return "";
},
},
data() {
return {
tableData: [
{
date: "2016-05-02",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-01",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1518 弄",
},
],
};
},
};
</script>
效果如下:
如果要使用其他的字体图标,直接在官网中点击图标右侧的赋值图标标签,然后替换图标即可
除了上面npm install 添加包然后本地引入外还可以用其他方式引入,如:
将以下代码粘贴到网页HTML代码的 部分
<link href="//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
Axios网络请求
Axios介绍
用于前端页面访问后端服务器
Axios安装
npm install axios
官网地址:https://www.axios-http.cn/
Axios引入
//引入Axios
import axios from 'axios';
Axios使用
vue生命周期
Movie.vue
<template>
<div>
<h1>{{title}}</h1>
<span>{{rating}}</span>
<button @click="funSc">点击收藏</button>
</div>
</template>
<script>
export default {
name:"Movie",
props:["title","rating"],
data:function(){
return{
}
},
//vue的生命周期函数,如created,mounted
created:function(){
console.log("Movie 组件被创建")
},
//method中写的是自定义方法
methods:{
funSc(){
alert("收藏成功")
}
}
}
</script>
App.vue
<template>
<div id="app">
<Movie v-for="movie in movies" :key="movie.id" :title="movie.title" :rating="movie.rating"></Movie>
<HelloElTable></HelloElTable>
</div>
</template>
<script>
import Movie from './components/Movie.vue'
import HelloElTable from './components/HelloElTable.vue'
export default {
name: 'App',
data:function(){
return{
movies:[
{id:1,title:"叶问1",rating:9.4},
{id:2,title:"叶问2",rating:8.5},
{id:3,title:"叶问3",rating:8.9}
]
}
},
created:function(){
console.log("App 组件被创建")
},
mounted:function(){
console.log("App 组件挂载完毕")
},
components: {
Movie,
HelloElTable
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
显示结果如下:
这里movie组件被创建多次,是因为在for循环中使用了这个组件:
跨域问题
为什么会出现跨域问题:
为了保证浏览器的安全,不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源,称为同源策略,同源策略是浏览器安全的基石。
所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
当一个请求url的协议、域名、端口三者之间任意一个与当前页面所在的url不同,即视为跨域
App.vue
<template>
<div id="app">
<Movie v-for="movie in movies" :key="movie.id" :title="movie.title" :rating="movie.rating"></Movie>
<HelloElTable></HelloElTable>
</div>
</template>
<script>
import Movie from './components/Movie.vue'
import HelloElTable from './components/HelloElTable.vue'
import axios from 'axios'
export default {
name: 'App',
data:function(){
return{
movies:[
{id:1,title:"叶问1",rating:9.4},
{id:2,title:"叶问2",rating:8.5},
{id:3,title:"叶问3",rating:8.9}
]
}
},
created:function(){
console.log("App 组件被创建")
//一般组件请求都是写在这里的
//这里如果不写协议、域名、端口的话,系统会默认访问当前页面的协议、域名、端口。
// axios.get("/user/findAll").then(function(res){
// console.log(res)
// })
axios.get("http://localhost:8023//roleMenus").then(function(res){
console.log(res)
})
},
mounted:function(){
console.log("App 组件挂载完毕")
},
components: {
Movie,
HelloElTable
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
出现如下跨域问题
跨域问题解决
CORS(Cross-Origin Resource Sharing)是由W3C制定的一种跨域资源共享技术,其目的就是为了解决前端的跨域请求。
CORS可以再不破坏既有规则的情况下,通过后端服务器实现CORS接口,从而实现跨域通信。
Springboot提供了在Controller中添加注解的方式来解决跨域问题:
@CrossOrigin
package com.xj.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xj.entity.Role;
import com.xj.mapper.RoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@CrossOrigin
public class RoleController {
@Autowired
private RoleMapper roleMapper;
@GetMapping("/role")
public List queryRole(){
List<Role> list = roleMapper.selectList(null);
return list;
}
@GetMapping("/role/find")
public List queryRoleByRoleName(){
QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("role_key","admin");
List<Role> roles = roleMapper.selectList(queryWrapper);
return roles;
}
@GetMapping("/roleMenus")
public List queryRoleMenus(){
List<Role> list = roleMapper.selectAllRoleAndRoleMenu();
return list;
}
@GetMapping("/role/findByPage")
public IPage findRoleByPage(){
//设置起始值和每页数据条数
Page<Role> page = new Page<>(0,5);
IPage iPage = roleMapper.selectPage(page,null);
return iPage;
}
}
HelloElTable.vue
<template>
<!-- template中只能放一个标签,所以可以在最外层套一个div标签,这样就可以并列放其他标签,如图标标签i -->
<div>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName"
>
<el-table-column prop="roleName" label="角色名称" width="180"> </el-table-column>
<el-table-column prop="roleKey" label="角色key" width="180"> </el-table-column>
</el-table>
<i class="fa fa-camera-retro fa-lg">fa-lg</i>
<i class="fa fa-automobile fa-lg">fa-lg</i>
</div>
</template>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
<script>
import axios from 'axios'
export default {
methods: {
tableRowClassName({ row, rowIndex }) {
if (rowIndex === 1) {
return "warning-row";
} else if (rowIndex === 3) {
return "success-row";
}
return "";
},
},
created: function () {
//function(res){} 无法继承方法外的this
//(res)=>{}这种方式定义函数,函数内可以继承外面的this作用域
axios.get("http://localhost:8023//roleMenus").then((res)=>{
this.tableData = res.data
})
},
data() {
return {
tableData: [],
};
},
};
</script>
最终成功访问,展示效果如下:
这里注意一下:在实际开发中,我们axios的引入在main.js中就可以了
代码如下:
main.js
import Vue from 'vue'
import App from './App.vue'
//引入 ElementUI
import ElementUI from 'element-ui';
//引入 ElementUI 的css供ElementUI中的组件使用
import 'element-ui/lib/theme-chalk/index.css';
//引入图标库
import 'font-awesome/css/font-awesome.min.css'
//引入Axios
import axios from 'axios';
//这里注册baseUrl,这样的话修改了域名和端口号就不用每个地址都去修改了
axios.defaults.baseURL = "http://localhost:8023"
Vue.prototype.$http = axios
//全局注册
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
HelloElTable.vue
<template>
<!-- template中只能放一个标签,所以可以在最外层套一个div标签,这样就可以并列放其他标签,如图标标签i -->
<div>
<el-table
:data="tableData"
style="width: 100%"
:row-class-name="tableRowClassName"
>
<el-table-column prop="roleName" label="角色名称" width="180"> </el-table-column>
<el-table-column prop="roleKey" label="角色key" width="180"> </el-table-column>
</el-table>
<i class="fa fa-camera-retro fa-lg">fa-lg</i>
<i class="fa fa-automobile fa-lg">fa-lg</i>
</div>
</template>
<style>
.el-table .warning-row {
background: oldlace;
}
.el-table .success-row {
background: #f0f9eb;
}
</style>
<script>
import axios from 'axios'
export default {
methods: {
tableRowClassName({ row, rowIndex }) {
if (rowIndex === 1) {
return "warning-row";
} else if (rowIndex === 3) {
return "success-row";
}
return "";
},
},
created: function () {
//function(res){} 无法继承方法外的this
//(res)=>{}这种方式定义函数,函数内可以继承外面的this作用域
this.$http.get("/roleMenus").then((res)=>{
this.tableData = res.data
})
},
data() {
return {
tableData: [],
};
},
};
</script>
前端路由VueRouter
介绍
适用于单页面
安装
命令:npm install vue-router 默认安装最新的版本
安装3版本对应vue2,命令:npm install vue-router@3
安装4版本对应vue3,命令:npm install vue-router@4
使用
1、组件目录(components)新建如下组件
Discover.vue
<template>
<div>
<h1>发现音乐</h1>
</div>
</template>
Friends.vue
<template>
<div>
<h1>关注</h1>
</div>
</template>
My.vue
<template>
<div>
<h1>我的音乐</h1>
</div>
</template>
2、src目录下新建router目录,目录下新建index.js
代码如下:
import Vue from 'vue';
import VueRouter from 'vue-router';
import Discover from '../components/Discover.vue'
import Friends from '../components/Friends.vue'
import My from '../components/My.vue'
//全局注册
Vue.use(VueRouter);
const router = new VueRouter({
//制定属性和组件的对应关系
routes:[
{path:'/discover',component:Discover},
{path:'/friends',component:Friends},
{path:'/my',component:My},
]
})
export default router
3、main.js中导入router
如图
具体代码如下:
import Vue from 'vue'
import App from './App.vue'
//引入 ElementUI
import ElementUI from 'element-ui';
//引入 ElementUI 的css供ElementUI中的组件使用
import 'element-ui/lib/theme-chalk/index.css';
//引入图标库
import 'font-awesome/css/font-awesome.min.css'
//引入Axios
import axios from 'axios';
//js的名字为index,可以不用再往下写名字了
// import router from './router/index'
import router from './router'
//这里注册baseUrl,这样的话修改了域名和端口号就不用每个地址都去修改了
axios.defaults.baseURL = "http://localhost:8023"
Vue.prototype.$http = axios
//全局注册
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
3、在App.vue中使用
代码如下:
<template>
<div id="app">
<!-- <Movie v-for="movie in movies" :key="movie.id" :title="movie.title" :rating="movie.rating"></Movie>
<HelloElTable></HelloElTable> -->
<!-- 声明路由链接 -->
<router-link to="/discover">发现音乐 </router-link>
<router-link to="/my">我的音乐 </router-link>
<router-link to="/friends">关注</router-link>
<!-- 声明路由占位标签,选择上面中的一个就会填充到下面的占位标签 -->
<router-view></router-view>
</div>
</template>
<script>
import Movie from './components/Movie.vue'
import HelloElTable from './components/HelloElTable.vue'
import axios from 'axios'
export default {
name: 'App',
data:function(){
return{
movies:[
{id:1,title:"叶问1",rating:9.4},
{id:2,title:"叶问2",rating:8.5},
{id:3,title:"叶问3",rating:8.9}
]
}
},
created:function(){
console.log("App 组件被创建")
//一般组件请求都是写在这里的
//这里如果不写协议、域名、端口的话,系统会默认访问当前页面的协议、域名、端口。
// axios.get("/user/findAll").then(function(res){
// console.log(res)
// })
},
mounted:function(){
console.log("App 组件挂载完毕")
},
components: {
Movie,
HelloElTable
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
4、如果要首页定为其中一个组件,可以在index.js中加如下代码:
Vuex
介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
安装
npm install vuex@3
使用
基本的使用
src下创建store目录,store目录下新建index.js,代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
}
})
// 导出
export default store
在mian.js中引入,这样在所有的组件中就可以使用了,代码如下:
// 引入vuex
import store from './store';
new Vue({
render: h => h(App),
store
}).$mount('#app')
在组件中使用,如下即可拿到count的取值。
<template>
<div>
{{this.$store.state.count}}
</div>
</template>
如果你要给count做+1,可以调用 mutations 中的方法
具体代码如下:
<template>
<div>
{{this.$store.state.count}}
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
methods:{
add(){
this.$store.commit("increment")
}
}
}
</script>
{{this.$store.state.count}} 这种在标签的写法可以简写成
<template>
<div>
{{count}}
<!-- {{ this.$store.state.count }} -->
<button @click="add">+1</button>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: mapState([
// 映射 this.count 为 store.state.count
"count",
]),
methods: {
add() {
this.$store.commit("increment");
},
},
};
</script>
Vue-Element-admin
vue-element-admin是一个后台前端解决方案,它基于vue和element-ui实现
下载项目
官网:https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/#%E5%8A%9F%E8%83%BD
这里以基础版下载为例
git 下载:git clone https://github.com/PanJiaChen/vue-admin-template.git
也可直接到代码库下载zip包,地址:https://github.com/PanJiaChen/vue-admin-template
安装和使用
进入项目目录
cd vue-admin-template
安装依赖
npm install
启动服务
npm run dev
具体逻辑看代码
云服务器的使用
云服务器概念
云服务器(Elastic Computer Service,ECS ),Elastic 弹性的,是一种简单高效、安全可靠、处理能力可弹性伸缩的计算服务。其管理方式比物理服务器更加简单高效。
阿里云ECS的使用
地址:https://www.aliyun.com/
1、我们一般选择云服务器(ECS)
2、购买云服务器地域选择,用户集中在哪就应该选择离用户近的
3、选择规格,如下选择的是:CPU两核内存2g