基于SpringBoot+Vue的练手项目

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 基于SpringBoot+Vue的练手项目

项目介绍

这是一款基于SpringBoot+Vue的前后端分离的项目,麻雀虽小,五脏俱全,值得拥有!

全部源码放到文章最后

页面展示:

  • 用户主页

前台主页.png

  • 管理员后台界面

后台主页.png

技术选型

后端    

  • SpringBoot    
  • Hutool工具类库(*超好用)    
  • Redis缓存 (缓解多次刷新数据库压力)    
  • Lombok(减少写get、set等方法)    
  • Mysql5.7+    
  • Mybatis 、Mybatis-Plus

前端

  • Vue2
  • Vue-Router
  • VueX
  • ElementUI
  • Apache ECharts (可视化图标插件)
  • Axios
  • 高德地图Api

数据库设计

Mysql5.7+  、Nvaicat(MySQL可视化工具)

数据库表.png

字符集

utf8mb4

排序规则

utf8mb4_unicode_ci

SpringBoot

目录结构

Springboot.png

配置类

改成自己配置即可

server:
ip: localhostport: 9090spring:
datasource:
driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/boot?serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=utf-8&useSSL=falseusername: rootpassword: 
redis:
host: localhostport: 6379password: 
servlet:
multipart:
max-file-size: 100MBmax-request-size: 100MBmybatis:
mapper-locations: classpath:mapper/*.xml  #扫描所有mybatis的xml文件#  configuration:#    log-impl: org.apache.ibatis.logging.stdout.StdOutImplmybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplfiles:upload:path: /usr/xmp/files/

坐标导入

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope><version>5.1.47</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!--代码生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity</artifactId><version>1.7</version></dependency><!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.20</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><!--JWT--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.10.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

Jwt、Token验证

packagesqgxy.xmp.springboot.config.interceptor;
importcn.hutool.core.util.StrUtil;
importcom.auth0.jwt.JWT;
importcom.auth0.jwt.JWTVerifier;
importcom.auth0.jwt.algorithms.Algorithm;
importcom.auth0.jwt.exceptions.JWTDecodeException;
importcom.auth0.jwt.exceptions.JWTVerificationException;
importsqgxy.xmp.springboot.common.Constants;
importsqgxy.xmp.springboot.config.AuthAccess;
importsqgxy.xmp.springboot.entity.User;
importsqgxy.xmp.springboot.exception.ServiceException;
importsqgxy.xmp.springboot.service.IUserService;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.method.HandlerMethod;
importorg.springframework.web.servlet.HandlerInterceptor;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
/*** @author xmp* @date 2022/5/9* @description JwtInterceptor* jwt拦截器*/publicclassJwtInterceptorimplementsHandlerInterceptor {
@AutowiredprivateIUserServiceuserService;
@OverridepublicbooleanpreHandle(HttpServletRequestrequest, HttpServletResponseresponse, Objecthandler) {
Stringtoken=request.getHeader("token");
// 如果不是映射到方法直接通过if(!(handlerinstanceofHandlerMethod)){
returntrue;
        } else {
HandlerMethodh= (HandlerMethod) handler;
AuthAccessauthAccess=h.getMethodAnnotation(AuthAccess.class);
if (authAccess!=null) {
returntrue;
            }
        }
// 执行认证if (StrUtil.isBlank(token)) {
thrownewServiceException(Constants.CODE_401, "无token,请重新登录");
        }
// 获取 token 中的 user idStringuserId;
try {
userId=JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeExceptionj) {
thrownewServiceException(Constants.CODE_401, "token验证失败,请重新登录");
        }
// 根据token中的userid查询数据库Useruser=userService.getById(userId);
if (user==null) {
thrownewServiceException(Constants.CODE_401, "用户不存在,请重新登录");
        }
// 用户密码加签验证 tokenJWTVerifierjwtVerifier=JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token); // 验证token        } catch (JWTVerificationExceptione) {
thrownewServiceException(Constants.CODE_401, "token验证失败,请重新登录");
        }
returntrue;
    }
}

自定义注解

package sqgxy.xmp.springboot.config;

import java.lang.annotation.*;

/**

* @author xmp

* date 2022/5/13

* @description 自定义注解

*/

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface AuthAccess {

}

跨域问题

packagesqgxy.xmp.springboot.config;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.web.cors.CorsConfiguration;
importorg.springframework.web.cors.UrlBasedCorsConfigurationSource;
importorg.springframework.web.filter.CorsFilter;
/*** @author xmp* @date 2022/5/9* @description 跨域问题*/@ConfigurationpublicclassCorsConfig {
// 当前跨域请求最大有效时长。这里默认1天privatestaticfinallongMAX_AGE=24*60*60;
@BeanpublicCorsFiltercorsFilter() {
UrlBasedCorsConfigurationSourcesource=newUrlBasedCorsConfigurationSource();
CorsConfigurationcorsConfiguration=newCorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法corsConfiguration.setMaxAge(MAX_AGE);
source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置returnnewCorsFilter(source);
    }
}

接口统一返回包装类

packagesqgxy.xmp.springboot.common;
importlombok.AllArgsConstructor;
importlombok.Data;
importlombok.NoArgsConstructor;
/*** @author xmp* @date 2022/5/10* 接口统一返回包装类*/@Data@NoArgsConstructor@AllArgsConstructorpublicclassResult {
privateStringcode;
privateStringmsg;
privateObjectdata;
publicstaticResultsuccess() {
returnnewResult(Constants.CODE_200, "", null);
    }
publicstaticResultsuccess(Objectdata) {
returnnewResult(Constants.CODE_200, "", data);
    }
publicstaticResulterror(Stringcode, Stringmsg) {
returnnewResult(code, msg, null);
    }
publicstaticResulterror() {
returnnewResult(Constants.CODE_500, "系统错误", null);
    }
}
自定义Codepackagesqgxy.xmp.springboot.common;
importlombok.AllArgsConstructor;
importlombok.Data;
importlombok.NoArgsConstructor;
/*** @author xmp* @date 2022/5/10* 接口统一返回包装类*/@Data@NoArgsConstructor@AllArgsConstructorpublicclassResult {
privateStringcode;
privateStringmsg;
privateObjectdata;
publicstaticResultsuccess() {
returnnewResult(Constants.CODE_200, "", null);
    }
publicstaticResultsuccess(Objectdata) {
returnnewResult(Constants.CODE_200, "", data);
    }
publicstaticResulterror(Stringcode, Stringmsg) {
returnnewResult(code, msg, null);
    }
publicstaticResulterror() {
returnnewResult(Constants.CODE_500, "系统错误", null);
    }
}

Mybatis-Plus分页插件

packagesqgxy.xmp.springboot.config;
importcom.baomidou.mybatisplus.annotation.DbType;
importcom.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
importcom.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
importorg.mybatis.spring.annotation.MapperScan;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
/*** @author xmp* @date 2022/5/9* @description MybatisPlusConfig mybatis-plus配置*/@Configuration@MapperScan("sqgxy.xmp.springboot.mapper")
publicclassMybatisPlusConfig {
// 最新版@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor() {
MybatisPlusInterceptorinterceptor=newMybatisPlusInterceptor();
interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.MYSQL));
returninterceptor;
    }
}

开发模式

以User为例

Entity

Entity实体层与数据库属性一一对应,lombok注解简化封装方法

packagesqgxy.xmp.springboot.entity;
importcom.baomidou.mybatisplus.annotation.IdType;
importcom.baomidou.mybatisplus.annotation.TableField;
importcom.baomidou.mybatisplus.annotation.TableId;
importcom.baomidou.mybatisplus.annotation.TableName;
importjava.io.Serializable;
importjava.util.Date;
importjava.util.List;
importio.swagger.annotations.ApiModel;
importio.swagger.annotations.ApiModelProperty;
importlombok.Getter;
importlombok.Setter;
importlombok.ToString;
@Getter@Setter@TableName("sys_user")
@ApiModel(value="User对象", description="")
@ToStringpublicclassUserimplementsSerializable {
privatestaticfinallongserialVersionUID=1L;
@ApiModelProperty("id")
@TableId(value="id", type=IdType.AUTO)
privateIntegerid;
@ApiModelProperty("用户名")
privateStringusername;
@ApiModelProperty("密码")
privateStringpassword;
@ApiModelProperty("昵称")
privateStringnickname;
@ApiModelProperty("邮箱")
privateStringemail;
@ApiModelProperty("电话")
privateStringphone;
@ApiModelProperty("地址")
privateStringaddress;
@ApiModelProperty("创建时间")
privateDatecreateTime;
@ApiModelProperty("头像")
privateStringavatarUrl;
@ApiModelProperty("角色")
privateStringrole;
@TableField(exist=false)
privateList<Course>courses;
@TableField(exist=false)
privateList<Course>stuCourses;
}

mapper

mapper层对数据库进行数据持久化操作

packagesqgxy.xmp.springboot.mapper;
importcom.baomidou.mybatisplus.extension.plugins.pagination.Page;
importsqgxy.xmp.springboot.controller.dto.UserPasswordDTO;
importsqgxy.xmp.springboot.entity.User;
importcom.baomidou.mybatisplus.core.mapper.BaseMapper;
importorg.apache.ibatis.annotations.Param;
importorg.apache.ibatis.annotations.Update;
/*** @author xmp* @date 2022/5/12*/publicinterfaceUserMapperextendsBaseMapper<User> {
@Update("update sys_user set password = #{newPassword} where username = #{username} and password = #{password}")
intupdatePassword(UserPasswordDTOuserPasswordDTO);
Page<User>findPage(Page<User>page, @Param("username") Stringusername, @Param("email") Stringemail, @Param("address") Stringaddress);
}
UserMapper.xml<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="sqgxy.xmp.springboot.mapper.UserMapper"><resultMapid="pageUser"type="sqgxy.xmp.springboot.entity.User"><resultcolumn="id"property="id"/><resultcolumn="username"property="username"/><resultcolumn="nickname"property="nickname"/><resultcolumn="email"property="email"/><resultcolumn="phone"property="phone"/><resultcolumn="address"property="address"/><resultcolumn="create_time"property="createTime"/><resultcolumn="avatar_url"property="avatarUrl"/><resultcolumn="role"property="role"/><collectionproperty="courses"javaType="java.util.ArrayList"ofType="sqgxy.xmp.springboot.entity.Course"><resultcolumn="teacherCourseName"property="name"/><resultcolumn="teacherScore"property="score"/></collection><collectionproperty="stuCourses"javaType="java.util.ArrayList"ofType="sqgxy.xmp.springboot.entity.Course"><resultcolumn="stuCourseName"property="name"/><resultcolumn="stuScore"property="score"/></collection></resultMap><selectid="findPage"resultMap="pageUser">selectsys_user.*, sc.nameasstuCourseName, tc.nameasteacherCourseName, tc.scoreasteacherScore,
sc.scoreasstuScorefromsys_userleftjoinstudent_courseonsys_user.id=student_course.student_idleftjoincoursesconstudent_course.course_id=sc.idleftjoincoursetconsys_user.id=tc.teacher_id<where><iftest="username != null and username != ''">andsys_user.usernamelikeconcat('%', #{username} ,'%')
</if><iftest="email != null and email != ''">andsys_user.emaillikeconcat('%', #{email} ,'%')
</if><iftest="address != null and address != ''">andsys_user.addresslikeconcat('%', #{address} ,'%')
</if></where></select></mapper>

service层

即为业务逻辑层,可以理解为对一个或者多个dao进行得再次封装,主要是针对具体的问题的操作,把一些数据层的操作进行组合,间接与数据库打交道(提供操作数据库的方法)。要做这一层的话,要先设计接口,再实现类

packagesqgxy.xmp.springboot.service;
importcom.baomidou.mybatisplus.extension.plugins.pagination.Page;
importsqgxy.xmp.springboot.controller.dto.UserDTO;
importsqgxy.xmp.springboot.controller.dto.UserPasswordDTO;
importsqgxy.xmp.springboot.entity.User;
importcom.baomidou.mybatisplus.extension.service.IService;
/*** @author xmp* @date 2022/5/12*/publicinterfaceIUserServiceextendsIService<User> {
UserDTOlogin(UserDTOuserDTO);
Userregister(UserDTOuserDTO);
voidupdatePassword(UserPasswordDTOuserPasswordDTO);
Page<User>findPage(Page<User>objectPage, Stringusername, Stringemail, Stringaddress);
}
packagesqgxy.xmp.springboot.service.impl;
importcn.hutool.core.bean.BeanUtil;
importcn.hutool.log.Log;
importcom.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
importcom.baomidou.mybatisplus.extension.plugins.pagination.Page;
importsqgxy.xmp.springboot.common.Constants;
importsqgxy.xmp.springboot.common.RoleEnum;
importsqgxy.xmp.springboot.controller.dto.UserDTO;
importsqgxy.xmp.springboot.controller.dto.UserPasswordDTO;
importsqgxy.xmp.springboot.entity.Menu;
importsqgxy.xmp.springboot.entity.User;
importsqgxy.xmp.springboot.exception.ServiceException;
importsqgxy.xmp.springboot.mapper.RoleMapper;
importsqgxy.xmp.springboot.mapper.RoleMenuMapper;
importsqgxy.xmp.springboot.mapper.UserMapper;
importsqgxy.xmp.springboot.service.IMenuService;
importsqgxy.xmp.springboot.service.IUserService;
importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
importsqgxy.xmp.springboot.utils.TokenUtils;
importorg.springframework.stereotype.Service;
importjavax.annotation.Resource;
importjava.util.ArrayList;
importjava.util.List;
/*** @author xmp* @date 2022/5/12*/@ServicepublicclassUserServiceImplextendsServiceImpl<UserMapper, User>implementsIUserService {
privatestaticfinalLogLOG=Log.get();
@ResourceprivateUserMapperuserMapper;
@ResourceprivateRoleMapperroleMapper;
@ResourceprivateRoleMenuMapperroleMenuMapper;
@ResourceprivateIMenuServicemenuService;
@OverridepublicUserDTOlogin(UserDTOuserDTO) {
Userone=getUserInfo(userDTO);
if (one!=null) {
BeanUtil.copyProperties(one, userDTO, true);
// 设置tokenStringtoken=TokenUtils.genToken(one.getId().toString(), one.getPassword());
userDTO.setToken(token);
Stringrole=one.getRole(); // ROLE_ADMIN// 设置用户的菜单列表List<Menu>roleMenus=getRoleMenus(role);
userDTO.setMenus(roleMenus);
returnuserDTO;
        } else {
thrownewServiceException(Constants.CODE_600, "用户名或密码错误");
        }
    }
@OverridepublicUserregister(UserDTOuserDTO) {
Userone=getUserInfo(userDTO);
if (one==null) {
one=newUser();
BeanUtil.copyProperties(userDTO, one, true);
// 默认一个普通用户的角色one.setRole(RoleEnum.ROLE_ADMIN.toString());
save(one);  // 把 copy完之后的用户对象存储到数据库        } else {
thrownewServiceException(Constants.CODE_600, "用户已存在");
        }
returnone;
    }
@OverridepublicvoidupdatePassword(UserPasswordDTOuserPasswordDTO) {
intupdate=userMapper.updatePassword(userPasswordDTO);
if (update<1) {
thrownewServiceException(Constants.CODE_600, "密码错误");
        }
    }
@OverridepublicPage<User>findPage(Page<User>page, Stringusername, Stringemail, Stringaddress) {
returnuserMapper.findPage(page, username, email, address);
    }
privateUsergetUserInfo(UserDTOuserDTO) {
QueryWrapper<User>queryWrapper=newQueryWrapper<>();
queryWrapper.eq("username", userDTO.getUsername());
queryWrapper.eq("password", userDTO.getPassword());
Userone;
try {
one=getOne(queryWrapper); // 从数据库查询用户信息        } catch (Exceptione) {
LOG.error(e);
thrownewServiceException(Constants.CODE_500, "系统错误");
        }
returnone;
    }
/*** 获取当前角色的菜单列表* @param roleFlag* @return*/privateList<Menu>getRoleMenus(StringroleFlag) {
IntegerroleId=roleMapper.selectByFlag(roleFlag);
// 当前角色的所有菜单id集合List<Integer>menuIds=roleMenuMapper.selectByRoleId(roleId);
// 查出系统所有的菜单(树形)List<Menu>menus=menuService.findMenus("");
// new一个最后筛选完成之后的listList<Menu>roleMenus=newArrayList<>();
// 筛选当前用户角色的菜单for (Menumenu : menus) {
if (menuIds.contains(menu.getId())) {
roleMenus.add(menu);
            }
List<Menu>children=menu.getChildren();
// removeIf()  移除 children 里面不在 menuIds集合中的 元素children.removeIf(child->!menuIds.contains(child.getId()));
        }
returnroleMenus;
    }
}

controller层

负责请求转发,接收页面过来的参数,传给service处理,接到返回值,并再次传给页面。

package sqgxy.xmp.springboot.controller;



import cn.hutool.core.collection.CollUtil;

import cn.hutool.core.util.StrUtil;

import cn.hutool.poi.excel.ExcelReader;

import cn.hutool.poi.excel.ExcelUtil;

import cn.hutool.poi.excel.ExcelWriter;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import sqgxy.xmp.springboot.common.Constants;

import sqgxy.xmp.springboot.common.Result;

import sqgxy.xmp.springboot.controller.dto.UserDTO;

import sqgxy.xmp.springboot.controller.dto.UserPasswordDTO;

import sqgxy.xmp.springboot.entity.User;

import sqgxy.xmp.springboot.service.IUserService;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.*;

import org.springframework.web.multipart.MultipartFile;


import javax.annotation.Resource;

import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServletResponse;

import java.io.InputStream;

import java.net.URLEncoder;

import java.util.List;


/**

* @author xmp

* @since 2022-01-26

*/

@RestController

@RequestMapping("/user")

public class UserController {


   @Value("${files.upload.path}")

   private String filesUploadPath;


   @Resource

   private IUserService userService;


   @PostMapping("/login")

   public Result login(@RequestBody UserDTO userDTO) {

       String username = userDTO.getUsername();

       String password = userDTO.getPassword();

       if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {

           return Result.error(Constants.CODE_400, "参数错误");

       }

       UserDTO dto = userService.login(userDTO);

       return Result.success(dto);

   }


   @PostMapping("/register")

   public Result register(@RequestBody UserDTO userDTO) {

       String username = userDTO.getUsername();

       String password = userDTO.getPassword();

       if (StrUtil.isBlank(username) || StrUtil.isBlank(password)) {

           return Result.error(Constants.CODE_400, "参数错误");

       }

       return Result.success(userService.register(userDTO));

   }


   // 新增或者更新

   @PostMapping

   public Result save(@RequestBody User user) {

       if (user.getId() == null && user.getPassword() == null) {  // 新增用户默认密码

           user.setPassword("123");

       }

       return Result.success(userService.saveOrUpdate(user));

   }


   /**

    * 修改密码

    * @param userPasswordDTO

    * @return

    */

   @PostMapping("/password")

   public Result password(@RequestBody UserPasswordDTO userPasswordDTO) {

       userService.updatePassword(userPasswordDTO);

       return Result.success();

   }


   @DeleteMapping("/{id}")

   public Result delete(@PathVariable Integer id) {

       return Result.success(userService.removeById(id));

   }


   @PostMapping("/del/batch")

   public Result deleteBatch(@RequestBody List<Integer> ids) {

       return Result.success(userService.removeByIds(ids));

   }


   @GetMapping

   public Result findAll() {

       return Result.success(userService.list());

   }


   @GetMapping("/role/{role}")

   public Result findUsersByRole(@PathVariable String role) {

       QueryWrapper<User> queryWrapper = new QueryWrapper<>();

       queryWrapper.eq("role", role);

       List<User> list = userService.list(queryWrapper);

       return Result.success(list);

   }


   @GetMapping("/{id}")

   public Result findOne(@PathVariable Integer id) {

       return Result.success(userService.getById(id));

   }


   @GetMapping("/username/{username}")

   public Result findByUsername(@PathVariable String username) {

       QueryWrapper<User> queryWrapper = new QueryWrapper<>();

       queryWrapper.eq("username", username);

       return Result.success(userService.getOne(queryWrapper));

   }


   @GetMapping("/page")

   public Result findPage(@RequestParam Integer pageNum,

                              @RequestParam Integer pageSize,

                              @RequestParam(defaultValue = "") String username,

                              @RequestParam(defaultValue = "") String email,

                              @RequestParam(defaultValue = "") String address)

   {

       return Result.success(userService.findPage(new Page<>(pageNum, pageSize), username, email, address));

   }


   /**

    * 导出接口

    */

   @GetMapping("/export")

   public void export(HttpServletResponse response) throws Exception {

       // 从数据库查询出所有的数据

       List<User> list = userService.list();

       // 通过工具类创建writer 写出到磁盘路径

//        ExcelWriter writer = ExcelUtil.getWriter(filesUploadPath + "/用户信息.xlsx");

       // 在内存操作,写出到浏览器

       ExcelWriter writer = ExcelUtil.getWriter(true);

       //自定义标题别名

       writer.addHeaderAlias("username", "用户名");

       writer.addHeaderAlias("password", "密码");

       writer.addHeaderAlias("nickname", "昵称");

       writer.addHeaderAlias("email", "邮箱");

       writer.addHeaderAlias("phone", "电话");

       writer.addHeaderAlias("address", "地址");

       writer.addHeaderAlias("createTime", "创建时间");

       writer.addHeaderAlias("avatarUrl", "头像");


       // 一次性写出list内的对象到excel,使用默认样式,强制输出标题

       writer.write(list, true);


       // 设置浏览器响应的格式

       response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");

       String fileName = URLEncoder.encode("用户信息", "UTF-8");

       response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");


       ServletOutputStream out = response.getOutputStream();

       writer.flush(out, true);

       out.close();

       writer.close();


   }


   /**

    * excel 导入

    * @param file

    * @throws Exception

    */

   @PostMapping("/import")

   public Result imp(MultipartFile file) throws Exception {

       InputStream inputStream = file.getInputStream();

       ExcelReader reader = ExcelUtil.getReader(inputStream);

       // 方式1:(推荐) 通过 javabean的方式读取Excel内的对象,但是要求表头必须是英文,跟javabean的属性要对应起来

//        List<User> list = reader.readAll(User.class);


       // 方式2:忽略表头的中文,直接读取表的内容

       List<List<Object>> list = reader.read(1);

       List<User> users = CollUtil.newArrayList();

       for (List<Object> row : list) {

           User user = new User();

           user.setUsername(row.get(0).toString());

           user.setPassword(row.get(1).toString());

           user.setNickname(row.get(2).toString());

           user.setEmail(row.get(3).toString());

           user.setPhone(row.get(4).toString());

           user.setAddress(row.get(5).toString());

           user.setAvatarUrl(row.get(6).toString());

           users.add(user);

       }


       userService.saveBatch(users);

       return Result.success(true);

   }


}

Vue

目录结构

Vue.png

跨域问题

安装axios:

npmiaxios-S

解决跨域问题,前端8080端口,请求数据后端9090端口

importaxiosfrom'axios'importrouterfrom"@/router";
import {serverIp} from"../../public/config";
constrequest=axios.create({
baseURL: `http://${serverIp}:9090`,timeout: 30000})
// request 拦截器// 可以自请求发送前对请求做一些处理// 比如统一加token,对请求参数统一加密request.interceptors.request.use(config=> {
config.headers['Content-Type'] ='application/json;charset=utf-8';
letuser=localStorage.getItem("user") ?JSON.parse(localStorage.getItem("user")) : nullif (user) {
config.headers['token'] =user.token;  // 设置请求头    }
returnconfig}, error=> {
returnPromise.reject(error)
});
// response 拦截器// 可以在接口响应后统一处理结果request.interceptors.response.use(
response=> {
letres=response.data;
// 如果是返回的文件if (response.config.responseType==='blob') {
returnres        }
// 兼容服务端返回的字符串数据if (typeofres==='string') {
res=res?JSON.parse(res) : res        }
// 当权限验证不通过的时候给出提示if (res.code==='401') {
// ElementUI.Message({//     message: res.msg,//     type: 'error'// });router.push("/login")
        }
returnres;
    },
error=> {
console.log('err'+error) // for debugreturnPromise.reject(error)
    }
)
exportdefaultrequest


页面开发

看文档、B站讲解

Element-Ui

官网地址:https://element.eleme.io/#/zh-CN/component/installation

Apache ECharts

官网地址:https://echarts.apache.org/handbook/zh/get-started/

项目部署

环境:

centos7、jdk8、mysql、Redis、nginx

远程连接工具Xshell、Xftp

Vue打包

dist文件夹:

npmrunbuild

SpringBoot打包

Snipaste_2022-05-14_17-05-00.png

按步骤

先清除,在编译,最后打包

上传之后

nginx

配置一下前端页面

location/ {
root/public/app/dist;      ===>自己的路径indexindex.htmlindex.htmlindex.htm;
  }

Boot

后台启动命令

nohupjava-jarxxx&

学习渠道

鱼皮的编程导航https://www.code-nav.cn/

B站的黑马程序员https://space.bilibili.com/37974444?spm_id_from=333.788.b_765f7570696e666f.2

B站的程序员青戈https://space.bilibili.com/402779077?spm_id_from=333.788.b_765f7570696e666f.2

源码分享

码云:https://gitee.com/love-code-bear/springboot-vue.git

github:https://github.com/xmpjava/-SpringBoot-Vue-.git

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
18天前
|
JavaScript 数据可视化
vue-cli学习一:vue脚手架的 vue-cli2和vue-cli3版本 创建vue项目,vue的初始化详解
这篇文章介绍了如何使用vue-cli 2和3版本来创建Vue项目,并详细说明了两者之间的主要区别。
67 5
vue-cli学习一:vue脚手架的 vue-cli2和vue-cli3版本 创建vue项目,vue的初始化详解
|
12天前
|
JavaScript 容器
乾坤qiankun框架搭建 主应用为vue3的项目。
乾坤qiankun框架搭建 主应用为vue3的项目。
94 2
|
17天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
101 1
|
1天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
76 62
|
2天前
|
数据采集 监控 JavaScript
在 Vue 项目中使用预渲染技术
【10月更文挑战第23天】在 Vue 项目中使用预渲染技术是提升 SEO 效果的有效途径之一。通过选择合适的预渲染工具,正确配置和运行预渲染操作,结合其他 SEO 策略,可以实现更好的搜索引擎优化效果。同时,需要不断地监控和优化预渲染效果,以适应不断变化的搜索引擎环境和用户需求。
|
18天前
|
JavaScript 数据可视化
vue-cli学习二:vue-cli3版本 创建vue项目后,Runtime-Compiler和Runtime-only两个模式详解;vue项目管理器;配置文件的配置在哪,以及如何配置
这篇文章详细介绍了Vue CLI 3版本创建项目时的Runtime-Compiler和Runtime-only两种模式的区别、Vue程序的运行过程、render函数的使用、eslint的关闭方法,以及Vue CLI 2和3版本配置文件的不同和脚手架3版本创建项目的配置文件配置方法。
27 3
vue-cli学习二:vue-cli3版本 创建vue项目后,Runtime-Compiler和Runtime-only两个模式详解;vue项目管理器;配置文件的配置在哪,以及如何配置
|
18天前
|
JavaScript 前端开发 Java
解决跨域问题大集合:vue-cli项目 和 java/springboot(6种方式) 两端解决(完美解决)
这篇文章详细介绍了如何在前端Vue项目和后端Spring Boot项目中通过多种方式解决跨域问题。
229 1
解决跨域问题大集合:vue-cli项目 和 java/springboot(6种方式) 两端解决(完美解决)
|
5天前
|
JavaScript
如何在 Vue 项目中选择合适的模块格式
【10月更文挑战第20天】选择合适的模块格式需要综合考虑多个因素,没有一种绝对正确的选择。需要根据项目的具体情况进行权衡和分析。在实际选择过程中,要保持灵活性,根据项目的发展和变化适时调整模块格式。
15 7
|
1天前
Vue3 项目的 setup 函数
【10月更文挑战第23天】setup` 函数是 Vue3 中非常重要的一个概念,掌握它的使用方法对于开发高效、灵活的 Vue3 组件至关重要。通过不断的实践和探索,你将能够更好地利用 `setup` 函数来构建优秀的 Vue3 项目。
|
5天前
|
JavaScript 前端开发 编译器
在 Vue 项目中使用 ES 模块格式的优点
【10月更文挑战第20天】在 Vue 项目中使用 ES 模块格式具有众多优点,这些优点共同作用,使得项目能够更高效、更可靠地开发和运行。当然,在实际应用中,还需要根据项目的具体情况和需求进行合理的选择和配置。
16 6