springboot+vue小白升级之路07-快速实现批量删除、小白升级之路08-实现批量导入导出excel、小白升级之路09-模块之间关联、小白升级之路10-简单实用的权限控制

661 篇文章 4 订阅
112 篇文章 0 订阅

我们接着之前的内容,全部代码我贴一下,大家参考使用。

数据库

drop table if exists an_user;
create table an_user(
	id int not null auto_increment primary key comment '主键id',
	name varchar(255) not null unique comment '姓名',
	`password` varchar(255) not null comment '密码',
	age int(3) default null comment '年龄',
	sex varchar(10) default null comment '性别',
	phone varchar(20) default null comment '电话',
	role varchar(255) default 'ROLE_' comment '角色名'
) comment '用户表';
insert into an_user values (1,'admin','1234',33,'男','13300000000','ROLE_ADMIN');
insert into an_user values (2,'zhangsan','1234',13,'女','13400000000','ROLE_TEACHER');
insert into an_user values (3,'lisi','1234',13,'女','13400000000','ROLE_STUDENT');


drop table if exists an_book;
create table an_book(
	id int not null auto_increment primary key comment '主键id',
	name varchar(100) not null comment '图书名称',
	price decimal(6,2) default null comment '价格',
	author varchar(50) default null comment '作者',
	express varchar(200) default null comment '出版社',
	img varchar(255) default null comment '图书封面',
	type_id int not null comment '图书分类id'
) comment '图书表';
insert into an_book values(1,'三国演义',999.99,'罗贯中','商务出版社', 'http:',1);

drop table if exists an_book_type;
create table an_book_type(
	id int not null auto_increment primary key comment '主键id',
	name varchar(50) not null comment '分类名称',
	description varchar(100) default null comment '分类描述'
) comment '图书分类';
insert into an_book_type values (1,'工具类','全部工具类书籍');









项目截图

springboot后端

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>

        <!--swagger依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--swagger ui-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <!-- jwt验证       -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.3</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.7</version>
        </dependency>
        <!-- excel导出修复bug       -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.commons</groupId>-->
<!--            <artifactId>commons-compress</artifactId>-->
<!--            <version>1.21</version>-->
<!--        </dependency>-->
    </dependencies>

 application.properties

server.port=8089

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=mysql123
#日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置别名
mybatis-plus.type-aliases-package=com.shrimpking.pojo
#开启逻辑删除,标识字段
mybatis-plus.global-config.db-config.logic-delete-field=is_deleted
#删除
mybatis-plus.global-config.db-config.logic-delete-value=1
#未删除
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#swagger
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

 pojo

book.java

package com.shrimpking.pojo;

import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p>
 * 图书表
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-12
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("an_book")
@ApiModel(value="Book对象", description="图书表")
public class Book implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "主键id")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @ApiModelProperty(value = "图书名称")
    private String name;

    @ApiModelProperty(value = "价格")
    private Double price;

    @ApiModelProperty(value = "作者")
    private String author;

    @ApiModelProperty(value = "出版社")
    private String express;

    @ApiModelProperty(value = "图书封面")
    private String img;

    @ApiModelProperty(value = "图书分类id")
    private Integer typeId;
}

 bookType.java

package com.shrimpking.pojo;

import cn.hutool.core.annotation.Alias;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p>
 * 图书分类
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-13
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("an_book_type")
@ApiModel(value="BookType对象", description="图书分类")
public class BookType implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "主键id")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * alias注解,是excel自动导入需要标注的
     */
    @Alias("分类名称")
    @ApiModelProperty(value = "分类名称")
    private String name;

    @Alias("分类描述")
    @ApiModelProperty(value = "分类描述")
    private String description;


}

 TestOne.java

package com.shrimpking.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/9 20:51
 */
@Data
@AllArgsConstructor
public class TestOne
{
    private int id;
    private String name;
    private String sex;
    private String phone;
}


 User.java

package com.shrimpking.pojo;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p>
 * 用户表
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-09
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("an_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "主键id")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @ApiModelProperty(value = "姓名")
    private String name;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "年龄")
    private Integer age;

    @ApiModelProperty(value = "性别")
    private String sex;

    @ApiModelProperty(value = "电话")
    private String phone;

    @ApiModelProperty(value = "角色名")
    private String role;

    @TableField(exist = false)
    private String token;
}

mapper

bookMapper.java

package com.shrimpking.mapper;

import com.shrimpking.pojo.Book;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * <p>
 * 图书表 Mapper 接口
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-12
 */
public interface BookMapper extends BaseMapper<Book> {

}

bookTypeMapper.java

package com.shrimpking.mapper;

import com.shrimpking.pojo.BookType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * <p>
 * 图书分类 Mapper 接口
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-13
 */
public interface BookTypeMapper extends BaseMapper<BookType> {

}

UserMapper.java

package com.shrimpking.mapper;

import com.shrimpking.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.shrimpking.req.QueryParams;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * <p>
 * 用户表 Mapper 接口
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-09
 */
public interface UserMapper extends BaseMapper<User> {

    /**
     * 基于注解的方式
     * @return
     */
    @Select("select * from an_user")
    List<User> getUsers();

    /**
     * 基于MapperXml的方式
     * @return
     */
    List<User> getAllUsers();

    /**
     * 基于MapperXml的,有条件,获取全部数据
     * @param queryParams
     * @return
     */
    List<User> findBySearch2(@Param("queryParams") QueryParams queryParams);
}

 mapperxml

bookMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shrimpking.mapper.BookMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.shrimpking.pojo.Book">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="price" property="price" />
        <result column="author" property="author" />
        <result column="express" property="express" />
        <result column="img" property="img" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, price, author, express, img
    </sql>

</mapper>

 bookTypemapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shrimpking.mapper.BookTypeMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.shrimpking.pojo.BookType">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="description" property="description" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, description
    </sql>

</mapper>

 UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shrimpking.mapper.UserMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.shrimpking.pojo.User">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="password" property="password" />
        <result column="age" property="age" />
        <result column="sex" property="sex" />
        <result column="phone" property="phone" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, password, age, sex, phone
    </sql>

    <!-- 基于MapperXml的方式   -->
    <select id="getAllUsers" resultType="com.shrimpking.pojo.User">
        select <include refid="Base_Column_List"/> from an_user
    </select>

    <!-- 基于MapperXml的方式,有条件时,获取全部数据   -->
    <select id="findBySearch2" resultType="com.shrimpking.pojo.User">
        select <include refid="Base_Column_List"/> from an_user
        <where>
            <if test="queryParams != null and queryParams.name != null and queryParams.name != ''">
                or name like concat('%',#{queryParams.name},'%')
            </if>
            <if test="queryParams != null and queryParams.phone != null and queryParams.phone != ''">
                or phone like concat('%',#{queryParams.phone},'%')
            </if>
        </where>

    </select>

</mapper>

 service

bookService.java

package com.shrimpking.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.pojo.Book;
import com.baomidou.mybatisplus.extension.service.IService;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;

/**
 * <p>
 * 图书表 服务类
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-12
 */
public interface BookService extends IService<Book> {

    IPage<Book> findBySearchPage(BookParams bookParams);
}

 bookTypeService.java

package com.shrimpking.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.pojo.Book;
import com.shrimpking.pojo.BookType;
import com.baomidou.mybatisplus.extension.service.IService;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;

/**
 * <p>
 * 图书分类 服务类
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-13
 */
public interface BookTypeService extends IService<BookType> {

    IPage<BookType> findBySearchPage(QueryParams queryParams);
}

UserService.java

package com.shrimpking.service;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.shrimpking.req.QueryParams;
import com.shrimpking.res.Result;

import java.util.List;

/**
 * <p>
 * 用户表 服务类
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-09
 */
public interface UserService extends IService<User> {

    /**
     * 基础注解的方式
     * @return
     */
    List<User> getUsers();

    /**
     * 基于MapperXml的方式
     * @return
     */
    List<User> getAllUsers();

    /**
     * 有查询条件,获取全部数据
     * @param queryParams
     * @return
     */
    List<User> findBySearch(QueryParams queryParams);

    /**
     * 基于MapperXml的,有条件,获取全部数据
     * @param queryParams
     * @return
     */
    List<User> findBySearch2(QueryParams queryParams);

    /**
     * 有查询条件时,获取分页数据
     * @param queryParams
     * @return
     */
    IPage<User> findBySearchPage(QueryParams queryParams);

    User login(User user);
}

 serviceImpl

bookserviceImpl.java

package com.shrimpking.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shrimpking.pojo.Book;
import com.shrimpking.mapper.BookMapper;
import com.shrimpking.pojo.User;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;
import com.shrimpking.service.BookService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 图书表 服务实现类
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-12
 */
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {

    @Override
    public IPage<Book> findBySearchPage(BookParams bookParams)
    {
        //声明分页
        IPage<Book> page = new Page<>(bookParams.getCurrentPage(), bookParams.getPageSize());
        //声明查询条件
        LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
        //图书名称不为空,有条件值时,加入此条件
        queryWrapper.like(
                StringUtils.isNotBlank(bookParams.getName()),
                Book::getName, bookParams.getName())
                //作者不为空,有条件值时,加入此条件
                .or().like(
                StringUtils.isNotBlank(bookParams.getAuthor()),
                Book::getAuthor,bookParams.getAuthor())
                //出版社不为空,加入此条件
                .or().like(
                StringUtils.isNotBlank(bookParams.getExpress()),
                Book::getExpress,bookParams.getExpress())
                .orderByDesc(Book::getId);
        //返回结果
        return this.baseMapper.selectPage(page, queryWrapper);
    }
}

 bookTypeServiceImpl.java

package com.shrimpking.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shrimpking.pojo.Book;
import com.shrimpking.pojo.BookType;
import com.shrimpking.mapper.BookTypeMapper;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;
import com.shrimpking.service.BookTypeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 图书分类 服务实现类
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-13
 */
@Service
public class BookTypeServiceImpl extends ServiceImpl<BookTypeMapper, BookType> implements BookTypeService {

    @Override
    public IPage<BookType> findBySearchPage(QueryParams queryParams)
    {
        //声明分页
        IPage<BookType> page = new Page<>(queryParams.getCurrentPage(), queryParams.getPageSize());
        //声明查询条件
        LambdaQueryWrapper<BookType> queryWrapper = new LambdaQueryWrapper<>();
        //分类名称不为空,有条件值时,加入此条件
        queryWrapper.like(
                StringUtils.isNotBlank(queryParams.getName()),
                BookType::getName, queryParams.getName())
                .orderByDesc(BookType::getId);
        //返回结果
        return this.baseMapper.selectPage(page, queryWrapper);
    }
}

 UserServiceImpl.java

package com.shrimpking.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shrimpking.exception.CustomException;
import com.shrimpking.pojo.User;
import com.shrimpking.mapper.UserMapper;
import com.shrimpking.req.QueryParams;
import com.shrimpking.res.Result;
import com.shrimpking.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shrimpking.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-09
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    /**
     * 基于注解的方式
     * @return
     */
    @Override
    public List<User> getUsers()
    {
        return this.userMapper.getUsers();
    }

    /**
     * 基于MapperXml的方式
     * @return
     */
    @Override
    public List<User> getAllUsers()
    {
        return this.userMapper.getAllUsers();
    }

    /**
     * 有查询条件,获取全部数据
     * @param queryParams
     * @return
     */
    @Override
    public List<User> findBySearch(QueryParams queryParams)
    {
        //声明查询条件
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        //姓名不为空,有条件值时,加入此条件
        queryWrapper.like(
                StringUtils.isNotBlank(queryParams.getName()),
                User::getName,queryParams.getName())
        //电话不为空,有条件值时,加入此条件
                .or().like(
                StringUtils.isNotBlank(queryParams.getPhone()),
                User::getPhone,queryParams.getPhone());
        //返回结果
        return this.baseMapper.selectList(queryWrapper);
    }

    /**
     * 基于MapperXml的,有条件,获取全部数据
     * @param queryParams
     * @return
     */
    @Override
    public List<User> findBySearch2(QueryParams queryParams)
    {
        return this.userMapper.findBySearch2(queryParams);
    }

    /**
     * 有查询条件时,获取分页数据
     * @param queryParams
     * @return
     */
    @Override
    public IPage<User> findBySearchPage(QueryParams queryParams)
    {
        //声明分页
        IPage<User> page = new Page<>(queryParams.getCurrentPage(),queryParams.getPageSize());
        //声明查询条件
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        //姓名不为空,有条件值时,加入此条件
        queryWrapper.ne(User::getRole,"ROLE_ADMIN")
                .like(
                StringUtils.isNotBlank(queryParams.getName()),
                User::getName,queryParams.getName())
                //电话不为空,有条件值时,加入此条件
                .or().like(
                StringUtils.isNotBlank(queryParams.getPhone()),
                User::getPhone,queryParams.getPhone())
                .orderByDesc(User::getId);
        //返回结果
        return this.baseMapper.selectPage(page, queryWrapper);
    }

    @Override
    public User login(User user)
    {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getName,user.getName());
        User one = this.baseMapper.selectOne(queryWrapper);
        if(one  == null || !one.getPassword().equals(user.getPassword())){
            throw new CustomException("用户名或密码错误");
        }
        //如果查询出来,有用户,生成token,与user一起返回
        String token = JwtUtils.createToken(one.getId().toString(), one.getPassword());
        one.setToken(token);
        //屏蔽密码
        one.setPassword("***");
        return one;
    }

}

 controller

bookController.java

package com.shrimpking.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.pojo.Book;
import com.shrimpking.pojo.User;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;
import com.shrimpking.res.Result;
import com.shrimpking.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 * 图书表 前端控制器
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-12
 */
@RestController
@RequestMapping("/book")
public class BookController {

    @Autowired
    private BookService bookService;

    /**
     * 有查询条件时,获取分页数据
     * @param queryParams
     * @return
     */
    @GetMapping("/searchPage")
    public Result findBySearchPage(BookParams bookParams){
        IPage<Book> list = this.bookService.findBySearchPage(bookParams);
        return Result.success(list);
    }

    @PostMapping("/save")
    public Result save(@RequestBody Book book){
        //先查询有无同名图书
        LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Book::getName,book.getName());
        int count = this.bookService.count(queryWrapper);
        if(count > 0) return Result.error("此图书已存在");

        boolean save = this.bookService.save(book);
        if(!save) return Result.error("保存失败");
        return Result.success("保存成功");
    }

    @PostMapping("/update")
    public Result update(@RequestBody Book book){
        //先查询有无同名图书
        LambdaQueryWrapper<Book> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Book::getName,book.getName());
        Book one = this.bookService.getOne(queryWrapper);
        if(one != null && !one.getId().equals(book.getId())){
            return Result.error("此图书已存在");
        }

        boolean save = this.bookService.updateById(book);
        if(!save) return Result.error("更新失败");
        return Result.success("更新成功");
    }

    @DeleteMapping("/delete")
    public Result delete(@RequestParam("id") Integer id){
        boolean remove = this.bookService.removeById(id);
        if(!remove) return Result.error("删除失败");
        return Result.success("删除成功");
    }


}

 bookTypecontroller.java

package com.shrimpking.controller;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.exception.CustomException;
import com.shrimpking.pojo.Book;
import com.shrimpking.pojo.BookType;
import com.shrimpking.req.BookParams;
import com.shrimpking.req.QueryParams;
import com.shrimpking.res.Result;
import com.shrimpking.service.BookTypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 图书分类 前端控制器
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-13
 */
@RestController
@RequestMapping("/bookType")
public class BookTypeController {

    @Autowired
    private BookTypeService bookTypeService;

    /**
     * 有查询条件时,获取分页数据
     * @param queryParams
     * @return
     */
    @GetMapping("/searchPage")
    public Result findBySearchPage(QueryParams queryParams){
        IPage<BookType> list = this.bookTypeService.findBySearchPage(queryParams);
        return Result.success(list);
    }

    @PostMapping("/save")
    public Result save(@RequestBody BookType bookType){
        //先查询有无同名分类
        LambdaQueryWrapper<BookType> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BookType::getName,bookType.getName());
        int count = this.bookTypeService.count(queryWrapper);
        if(count > 0) return Result.error("此分类已存在");

        boolean save = this.bookTypeService.save(bookType);
        if(!save) return Result.error("保存失败");
        return Result.success("保存成功");
    }

    @PostMapping("/update")
    public Result update(@RequestBody BookType bookType){
        //先查询有无同名分类
        LambdaQueryWrapper<BookType> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BookType::getName,bookType.getName());
        BookType one = this.bookTypeService.getOne(queryWrapper);
        if(one != null && !one.getId().equals(bookType.getId())){
            return Result.error("此分类已存在");
        }

        boolean save = this.bookTypeService.updateById(bookType);
        if(!save) return Result.error("更新失败");
        return Result.success("更新成功");
    }

    @DeleteMapping("/delete")
    public Result delete(@RequestParam("id") Integer id){
        boolean remove = this.bookTypeService.removeById(id);
        if(!remove) return Result.error("删除失败");
        return Result.success("删除成功");
    }

    @PostMapping("/delBatch")
    public Result delBatch(@RequestBody List<BookType> list){
        //循环取出id
        List<Integer> collectList = list.stream().map(BookType::getId).collect(Collectors.toList());
        boolean remove = this.bookTypeService.removeByIds(collectList);
        if(!remove) return Result.error("删除失败");
        return Result.success("删除成功");
    }

    @GetMapping("/export")
    public Result exportData(HttpServletResponse response) throws IOException
    {
        //先获取全部数据
        List<BookType> all = this.bookTypeService.list();
        //定义list
        List<Map<String,Object>> list = new ArrayList<>(all.size());

        if(CollectionUtil.isEmpty(all)){
            //throw new CustomException("没有数据,无法导出");
            //定义list
            Map<String,Object> row = new HashMap<>();
            row.put("分类名称","");
            row.put("分类描述","");
            list.add(row);
        }else {
            //循环,定义一个map
            for (BookType bookType : all)
            {
                Map<String,Object> row = new HashMap<>();
                row.put("分类名称",bookType.getName());
                row.put("分类描述",bookType.getDescription());
                list.add(row);
            }
        }

//        //定义list
//        List<Map<String,Object>> list = new ArrayList<>(all.size());
//
//        //循环,定义一个map
//        for (BookType bookType : all)
//        {
//            Map<String,Object> row = new HashMap<>();
//            row.put("分类名称",bookType.getName());
//            row.put("分类描述",bookType.getDescription());
//            list.add(row);
//        }

        //获取excel的对象
        //参数二,true 文件格式为xlsx
        ExcelWriter excelWriter = ExcelUtil.getWriter(true);
        //参数二,true,输出标题
        excelWriter.write(list,true);

        //输出前端
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename=type.xlsx");
        ServletOutputStream out = response.getOutputStream();
        //参数二,true,关闭流
        excelWriter.flush(out,true);
        excelWriter.close();
        IoUtil.close(System.out);
        return Result.success("导出成功");
    }

    /**
     * 上传文件
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public Result upload(MultipartFile file) throws IOException{
        //从excel中获取数据
        List<BookType> bookTypes = ExcelUtil.getReader(file.getInputStream()).readAll(BookType.class);
        //记录获取的数量
        int count1 = bookTypes.size();
        //保存插入库中的数量
        int count2 = 0;
        //不为空时,批量插入
        if(!CollectionUtil.isEmpty(bookTypes)){

            for (BookType bookType : bookTypes)
            {
                //先查询有无同名分类
                LambdaQueryWrapper<BookType> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(BookType::getName,bookType.getName());
                int count = this.bookTypeService.count(queryWrapper);
                if(count > 0) {
                    //有同名的数据,跳过,不插入
                    continue;
                }else {
                    try
                    {
                        this.bookTypeService.save(bookType);
                        count2++;
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
        //如果插入数小于获取的数量,提示
        if(count2 < count1) return Result.error("部分数据导入失败");
        return Result.success("导入成功");
    }


    /**
     * 获取全部的图书分类数据
     * @return
     */
    @GetMapping("/list")
    public Result list(){
        return Result.success(this.bookTypeService.list());
    }


}

 fileController.java

package com.shrimpking.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.shrimpking.res.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/13 11:07
 */
@RestController
@RequestMapping("/files")
public class FileController
{
    /**
     * 文件上传存储路径
     */
    private static final String filePath = System.getProperty("user.dir") + "/file/";

    /**
     * 上传文件
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public Result upload(MultipartFile file){

        synchronized (FileController.class){
            //获取当前时间戳
            String flag = System.currentTimeMillis() + "";
            //获取原始文件名
            String fileName = file.getOriginalFilename();
            try
            {
                //如果文件夹不存在,在项目的根目录中创建文件夹
                if(!FileUtil.isDirectory(filePath)){
                    FileUtil.mkdir(filePath);
                }
                //文件存储形式: 时间戳 + 文件名
                FileUtil.writeBytes(file.getBytes(),filePath + flag + "-" + fileName);
                System.out.println(fileName + "--上传成功");
                Thread.sleep(1L);
            }catch (Exception e){
                System.err.println(fileName + "--上传失败");
            }
            return Result.success(flag);
        }
    }


    /**
     * 下载文件
     * @param flag
     * @param response
     */
    @GetMapping("/down/{flag}")
    public void avatarPath(@PathVariable String flag, HttpServletResponse response){
        //创建目录
        if(!FileUtil.isDirectory(filePath)){
            FileUtil.mkdir(filePath);
        }

        OutputStream os;
        //获取目录下全部文件列表
        List<String> fileNames = FileUtil.listFileNames(filePath);
        //逐一核对,获取该文件名称
        String avatar = fileNames.stream().filter(item -> item.contains(flag)).findAny().orElse("");

        try
        {
            if(StrUtil.isNotEmpty(avatar)){
                response.addHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(avatar,"UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(filePath + avatar);
                os = response.getOutputStream();
                os.write(bytes);
                os.flush();
                os.close();
            }
        }catch (Exception e){
            System.out.println("文件下载失败");
        }
    }
}

 testController.java

package com.shrimpking.controller;

import com.shrimpking.pojo.TestOne;
import com.shrimpking.res.Result;
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;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/9 20:45
 */
@RestController
@RequestMapping("/test")
public class TestController
{

    @RequestMapping(value = "/start",method = RequestMethod.GET)
    public String start(){
        return "这是你的第一个springboot工程项目!已经启动!";
        //http://localhost:8089/test/start
    }

    @GetMapping("/getOne")
    public TestOne getTestOne(){
        return new TestOne(1,"tom","男","13000000000");
        //http://localhost:8089/test/getOne
        //{"id":1,"name":"tom","sex":"男","phone":"13000000000"}
    }

    @GetMapping("/start2")
    public Result start2(){
        return Result.success("这是你的第一个springboot工程项目!已经启动!");
        //http://localhost:8089/test/start2
        //{"code":"200","msg":"成功","data":"这是你的第一个springboot工程项目!已经启动!"}
    }

    @GetMapping("/getOne2")
    public Result getTestOne2(){
        return Result.success(new TestOne(2,"jerry","女","138999988888"));
        //http://localhost:8089/test/getOne2
        //{"code":"200","msg":"成功","data":{"id":2,"name":"jerry","sex":"女","phone":"138999988888"}}
    }
}

 usercontroller.java

package com.shrimpking.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.exception.CustomException;
import com.shrimpking.pojo.User;
import com.shrimpking.req.QueryParams;
import com.shrimpking.res.Result;
import com.shrimpking.service.UserService;
import net.bytebuddy.implementation.bytecode.Throw;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;


/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author shrimpking
 * @since 2023-11-09
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 3基于mybatisplus的方式
     * @return
     */
    @GetMapping("/getList")
    public List<User> getUserList(){
        return  this.userService.list();
        //http://localhost:8089/user/getList
    }

    /**
     * 2基于注解的方式
     *
     * @return
     */
    @GetMapping("/getUsers")
    public List<User> getUsers(){
        return  this.userService.getUsers();
        //http://localhost:8089/user/getUsers
        //[{"id":1,"name":"zhangsan","password":"1234","age":33,"sex":"男","phone":"13300000000"},
        // {"id":2,"name":"lisi","password":"1234","age":13,"sex":"女","phone":"13400000000"}]
    }

    /**
     * 1基于MapperXml的方式
     * @return
     */
    @GetMapping("/getAll")
    public List<User> getAllUsers(){
        return this.userService.getAllUsers();
        //http://localhost:8089/user/getAll
        //[{"id":1,"name":"zhangsan","password":"1234","age":33,"sex":"男","phone":"13300000000"},
        // {"id":2,"name":"lisi","password":"1234","age":13,"sex":"女","phone":"13400000000"}]
    }

    /**
     * 3基于mybatisplus的方式
     * @return
     */
    @GetMapping("/getList2")
    public Result getUserList2(){
        return  Result.success(this.userService.list());
        //http://localhost:8089/user/getList2
        //{"code":"200","msg":"成功","data":[
        // {"id":1,"name":"zhangsan","password":"1234","age":33,"sex":"男","phone":"13300000000"},
        // {"id":2,"name":"lisi","password":"1234","age":13,"sex":"女","phone":"13400000000"}]}
    }

    /**
     * 2基于注解的方式
     *
     * @return
     */
    @GetMapping("/getUsers2")
    public Result getUsers2(){
        return  Result.success(this.userService.getUsers());
        //http://localhost:8089/user/getUsers2
        //{"code":"200","msg":"成功","data":[
        // {"id":1,"name":"zhangsan","password":"1234","age":33,"sex":"男","phone":"13300000000"},
        // {"id":2,"name":"lisi","password":"1234","age":13,"sex":"女","phone":"13400000000"}]}
    }

    /**
     * 1基于MapperXml的方式
     * @return
     */
    @GetMapping("/getAll2")
    public Result getAllUsers2(){
        return Result.success(this.userService.getUsers());
        //http://localhost:8089/user/getAll2
        //{"code":"200","msg":"成功","data":[
        // {"id":1,"name":"zhangsan","password":"1234","age":33,"sex":"男","phone":"13300000000"},
        // {"id":2,"name":"lisi","password":"1234","age":13,"sex":"女","phone":"13400000000"}]}
    }

    /**
     * 获取全部数据
     * @return
     */
    @GetMapping("/findAll")
    public Result findAll(){
        return Result.success(this.userService.list());
    }


    /**
     * 有查询条件时,获取全部数据
     * @param queryParams
     * @return
     */
    @GetMapping("/search")
    public Result findBySearch(QueryParams queryParams){
        List<User> list = this.userService.findBySearch(queryParams);
        return Result.success(list);
    }

    /**
     * 基于MapperXml的,有条件,获取全部数据
     * @param queryParams
     * @return
     */
    @GetMapping("/search2")
    public Result findBySearch2(QueryParams queryParams){
        List<User> list = this.userService.findBySearch2(queryParams);
        return Result.success(list);
    }

    /**
     * 有查询条件时,获取分页数据
     * @param queryParams
     * @return
     */
    @GetMapping("/searchPage")
    public Result findBySearchPage(QueryParams queryParams){
        IPage<User> list = this.userService.findBySearchPage(queryParams);
        return Result.success(list);
    }

    @PostMapping("/save")
    public Result save(@RequestBody User user){
        //先查询有无同名用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getName,user.getName());
        int count = this.userService.count(queryWrapper);
        if(count > 0) return Result.error("此用户名已存在");

        boolean save = this.userService.save(user);
        if(!save) return Result.error("保存失败");
        return Result.success("保存成功");
    }

    @PostMapping("/update")
    public Result update(@RequestBody User user){
        //先查询有无同名用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getName,user.getName());
        User one = this.userService.getOne(queryWrapper);
        if(one != null && !one.getId().equals(user.getId())){
            return Result.error("此用户名已存在");
        }

        boolean save = this.userService.updateById(user);
        if(!save) return Result.error("更新失败");
        return Result.success("更新成功");
    }

    @DeleteMapping("/delete")
    public Result delete(@RequestParam("id") Integer id){
        boolean remove = this.userService.removeById(id);
        if(!remove) return Result.error("删除失败");
        return Result.success("删除成功");
    }

    @PostMapping("/login")
    public Result login(@RequestBody User user){
        User login = this.userService.login(user);
        return Result.success(login);
    }

    @PostMapping("/register")
    public Result register(@RequestBody User user){
        //先查询有无同名用户
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getName,user.getName());
        int count = this.userService.count(queryWrapper);
        if(count > 0) {
            throw new CustomException("此用户已经存在,请重新注册!");
        }
        //默认是学生
        //user.setRole("ROLE_STUDENT");

        boolean save = this.userService.save(user);
        if(!save) return Result.error("注册失败");
        return Result.success("注册成功");
    }
}

 config

apiconfig.java

package com.shrimpking.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/12 14:39
 */
@Configuration
public class ApiConfig implements WebMvcConfigurer
{
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer)
    {
        //指定controller的统一接口前缀,增加接口前缀
        configurer.addPathPrefix("/api",clazz-> clazz.isAnnotationPresent(RestController.class));
    }
}

 corsconfig.java

package com.shrimpking.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

// 案例 一
@Configuration
public class CorsConfig implements WebMvcConfigurer
{

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                //是否发送Cookie
                .allowCredentials(true)
                //放行哪些原始域
                .allowedOriginPatterns("*")
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                .allowedHeaders("*")
                .exposedHeaders("*");
    }

    /**
     * 增加了自定义拦截器后,需要使用此方法,才可以解决跨域,
     * 前一个方法maps,自动失效了。
     * @return
     */
    @Bean
    public CorsFilter corsFilter(){
        //添加cors配置
        CorsConfiguration config = new CorsConfiguration();
        //允许的域,不要写*号
        config.addAllowedOrigin("http://localhost:8080");
        //是否发送cookie
        config.setAllowCredentials(true);
        //允许的请求方式
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        //允许的头信息
        config.addAllowedHeader("*");
        //添加映射路径
        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
        configurationSource.registerCorsConfiguration("/**",config);

        return new CorsFilter(configurationSource);

    }
}

 jwtconfig.java

package com.shrimpking.config;

import com.shrimpking.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/12 14:39
 */
@Configuration
public class JwtConfig implements WebMvcConfigurer
{

    @Autowired
    private JwtInterceptor jwtInterceptor;
    /**
     * 添加jwt拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry)
    {
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/user/login")
                .excludePathPatterns("/api/user/register")
                .excludePathPatterns("/api/files/**")
                .excludePathPatterns("/api/bookType/upload");
    }


}

 mybatisplusconfig.java

package com.shrimpking.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.shrimpking.mapper")
public class MybatisPlusConfig
{
    /**
     * 配置分页插件的
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor()
    {
        MybatisPlusInterceptor interceptor
                = new MybatisPlusInterceptor();
        //分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //防止全表更新插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }
}

 swaggerconfig.java

package com.shrimpking.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.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/9/10 14:58
 */
@Configuration
@EnableSwagger2
public class SwaggerConfig
{
    @Bean
    public Docket createRestApi(){
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any()).build();
        
    }

    private ApiInfo apiInfo(){
        return new ApiInfoBuilder()
                .title("系统接口文档")
                .description("接口文档的描述")
                .version("1.0")
                .contact(new Contact("weixin","http://www.baidu.com","1@1.com"))
                .build();
    }
}

 exception

customException.java

package com.shrimpking.exception;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/11 11:04
 */
public class CustomException extends RuntimeException
{
    private String message;

    public CustomException(String message)
    {
        this.message = message;
    }

    @Override
    public String getMessage()
    {
        return this.message;
    }


}

 globalException.java

package com.shrimpking.exception;

import com.shrimpking.res.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/11 11:01
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler
{
    @ExceptionHandler(Exception.class)
    public Result error(HttpServletRequest request,Exception e){
        log.error("异常信息:",e);
        return Result.error("系统异常");
    }

    @ExceptionHandler(CustomException.class)
    public Result customException(HttpServletRequest request,CustomException e){
        log.error("异常信息:",e);
        return Result.error(e.getMessage());
    }
}

 interceptor

jwtInterceptor.java

package com.shrimpking.interceptor;


import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.shrimpking.exception.CustomException;
import com.shrimpking.service.UserService;
import com.shrimpking.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/12 16:21
 */
@Component
@Slf4j
public class JwtInterceptor implements HandlerInterceptor
{
    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        //获取token,从请求头部获取
        String token = request.getHeader("z-token");
        if(StringUtils.isBlank(token)){
            //token为空,从请求参数获取
            token = request.getParameter("token");
        }

        //token为空,重新登录
        if(StringUtils.isBlank(token)){
            throw new CustomException("无token,获取失败,请重新登录!");
        }
        //验证解析token
        return JwtUtils.verify(token);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
    {

    }
}

 req

bookParams.java

package com.shrimpking.req;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/12 21:10
 */

@Data
@EqualsAndHashCode(callSuper = true)
public class BookParams extends QueryParams
{
    private String author;
    private String express;
}

 queryParams.java

package com.shrimpking.req;

import lombok.Data;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/10 12:27
 */
@Data
public class QueryParams
{
    private String name;
    private String phone;
    private Long currentPage;
    private Long pageSize;
}

 res

result.java

package com.shrimpking.res;

import lombok.Data;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/10 11:03
 */
@Data
public class Result
{
    public static final String SUCCESS = "200";
    public static final String ERROR = "400";

    private String code;
    private String msg;
    private Object data;

    public static Result success(){
        Result result = new Result();
        result.setCode(SUCCESS);
        result.setMsg("成功");
        return result;
    }

    public static Result success(Object data){
        Result result = new Result();
        result.setCode(SUCCESS);
        result.setMsg("成功");
        result.setData(data);
        return result;
    }

    public static Result error(String msg){
        Result result = new Result();
        result.setCode(ERROR);
        result.setMsg(msg);
        return result;
    }



}

 utils

jwtutils.java

package com.shrimpking.utils;

import ch.qos.logback.classic.turbo.TurboFilter;
import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.shrimpking.exception.CustomException;
import com.shrimpking.pojo.User;
import com.shrimpking.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Objects;

/**
 * Created by IntelliJ IDEA.
 *
 * @Author : Shrimpking
 * @create 2023/11/12 14:49
 */
@Slf4j
@Component
public class JwtUtils
{
    private static UserService staticUserService;

    @Autowired
    private UserService userService;

    @PostConstruct
    public void setUserService(){
        staticUserService = userService;
    }

    /**
     * 生成token
     * @param adminId
     * @param sign
     * @return
     */
    public static String createToken(String userId,String pwdToSign){
        return JWT.create()
                //将user id保存到里面,作为载荷
                .withAudience(userId)
                //2个小时以后过期
                .withExpiresAt(DateUtil.offsetHour(new Date(),2))
                //以password作为签名
                .sign(Algorithm.HMAC256(pwdToSign));
    }

    /**
     * 获取当前用户
     * @return
     */
    public static User getCurrentUser(){
        //声明token
        String token = null;
        try
        {
            //获取请求
            HttpServletRequest request
                    = ((ServletRequestAttributes)Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            //从请求头部获取token
            token = request.getHeader("z-token");

            //如果头部没有,请请求参数获取token
            if(StringUtils.isBlank(token)){
                token = request.getParameter("token");
            }

            //如果token没有,返回null
            if(StringUtils.isBlank(token)){
                log.error("获取当前登录用户信息失败,token={}",token);
                return null;
            }
            //解析token
            String userId = JWT.decode(token).getAudience().get(0);
            //返回用户
            return staticUserService.getById(userId);
        }catch (Exception e){
            log.error("获取当前登录用户信息失败,token={}",token,e);
            return null;
        }
    }

    /**
     * 验证token
     * @param token
     * @return
     */
    public static Boolean verify(String token){

        //用户id
        String userId;
        //用户
        User user;
        try
        {
            //解析token,获取id
            userId = JWT.decode(token).getAudience().get(0);
            //根据id,获取用户
            user = staticUserService.getById(userId);
        }catch (Exception e){
            String errorMsg = "非法token,验证失败,请重新登录!";
            log.error(errorMsg + ",token=" + token ,e);
            throw  new CustomException(errorMsg);
        }
        //未查到用户
        if(user == null){
            throw new CustomException("用户不存在,请重新登录!");
        }

        try
        {
            //解析过程中,无异常,说明验证成功
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
            verifier.verify(token);
        }catch (JWTVerificationException e){
            throw new CustomException("token验证失败,请重新登录");
        }

        return true;
    }
}

 启动类

package com.shrimpking;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.shrimpking.mapper")
public class SpringbootVueTest31Application
{

    public static void main(String[] args)
    {
        SpringApplication.run(SpringbootVueTest31Application.class, args);
    }

}

vue前端

 global.css


html,body{
    margin: 0;
    padding: 0;
}

* {
    box-sizing: border-box;
}

 router

import Vue from 'vue'
import VueRouter from 'vue-router'


Vue.use(VueRouter)

const routes = [
  {
    path: '/login',
    name:'LoginView',
    component: ()=> import('@/views/LoginView.vue'),
  },
  {
    path:'/register',
    name: 'Register',
    component: ()=> import('@/views/RegisterView.vue'),
  },
  {
    path: '/',
    redirect: '/home',
    name: 'Layout',
    component: ()=> import('@/views/Layout.vue'),
    children:[
      {
        path: 'home',
        name: 'HomeView',
        component: ()=> import('@/views/HomeView.vue')
      },
      {
        path: 'admin',
        name: 'AdminView',
        component: ()=> import('@/views/User/AdminView.vue'),
      },
      {
        path:'user',
        name:'UserView',
        component: ()=> import('@/views/User/UserView.vue'),
      },
      {
        path:'book',
        name:'BookView',
        component: ()=> import('@/views/Info/BookView.vue'),
      },
      {
        path:'type',
        name:'BookType',
        component: ()=> import('@/views/Info/BookType.vue'),
      },
    ]
  },
]

const router = new VueRouter({
  routes
})

//白名单
const IGNORE_URLS = ['/login','/register'];

//前置守卫
router.beforeEach((to, from, next) => {
  //在白名单中,放行
  if(IGNORE_URLS.includes(to.path)){
    next();
  }
  //获取用户
  let admin = JSON.parse(window.localStorage.getItem('access-admin'));
  if(!admin && !IGNORE_URLS.includes(to.path)){
    //没有登录 ,没有在白名单中,跳转登录
    return next('/login');
  }

  next();
});

export default router

 store

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

 utils

request.js

import axios from 'axios'

const request  = axios.create({
    baseURL: 'http://localhost:8089/api',  //
    timeout: 5000,
});

//request 拦截器
request.interceptors.request.use( config =>{
    config.headers['Content-Type'] =  'application/json;charset=utf-8';
    //获取token
    const admin = JSON.parse(window.localStorage.getItem('access-admin'));
    if(admin){
        config.headers['z-token'] = admin.token;
    }
    return config;
},error => {
    return Promise.reject(error);
});

//respose 拦截器
request.interceptors.response.use( response => {
    //response.data即为后端返回的result, 也就是脱壳
    let res = response.data;

    //兼容服务端返回的字符串数据
    if(typeof res === 'string'){
        res = res ? JSON.parse(res) : res;
    }
    return res;
},error => {
    console.log('error:' + error);
    return Promise.reject(error);
});

export default request;

views

bookType.vue

<template>
    <div>
        <!-- 搜索区域       -->
        <div style="margin-bottom:15px;">
            <el-input
                    v-model="searchForm.name"
                    style="width:200px;"
                    placeholder="请输入分类名称"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-button
                    type="warning"
                    style="margin-left: 10px;"
                    icon="el-icon-search"
                    @click="doSearch">查询</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-toilet-paper"
                    @click="clearSearch">清空</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-plus" @click="addBtn"
                    v-if="admin.role === 'ROLE_ADMIN'">新增</el-button>
            <el-button
                    type="danger"
                    style="margin-left: 10px;"
                    icon="el-icon-delete"
                    @click="delBatchBtn"
                    v-if="admin.role === 'ROLE_ADMIN'">批量删除</el-button>
            <el-button
                    type="success"
                    style="margin-left: 10px;"
                    icon="el-icon-d-arrow-right"
                    @click="exportBtn"
                    v-if="admin.role === 'ROLE_ADMIN'">导出数据</el-button>
            <el-upload
                    :action="'http://localhost:8089/api/bookType/upload'"
                    :on-success="successUpload"
                    :show-file-list="false"
                    style="display: inline-block;margin-left:10px;"
                    v-if="admin.role === 'ROLE_ADMIN'">
                <el-button type="primary" icon="el-icon-d-arrow-left">批量导入</el-button>
            </el-upload>
        </div>

        <!-- 表格区域       -->
        <el-table
                ref="table1"
                :data="tableData"
                border
                row-key="id"
                @selection-change="handleSelectionChange"
                tooltip-effect="dark"
                style="width: 100%">
            <el-table-column
                    type="selection"
                    :reserve-selection="true"
                    width="55">
            </el-table-column>
            <el-table-column
                    prop="id"
                    label="ID">
            </el-table-column>
            <el-table-column
                    prop="name"
                    label="分类名称"
                    show-overflow-tooltip>
            </el-table-column>
            <el-table-column
                    prop="description"
                    label="分类描述"
                    show-overflow-tooltip>
            </el-table-column>
            <el-table-column label="操作" width="260px" v-if="admin.role === 'ROLE_ADMIN'">
                <template slot-scope="scope">
                    <el-button
                            type="primary"
                            icon="el-icon-edit"
                            @click="editBtn(scope.row)"
                            v-if="admin.role === 'ROLE_ADMIN'">编辑</el-button>
                    <el-button
                            type="danger"
                            icon="el-icon-delete"
                            @click="deleteBtn(scope.row)"
                            v-if="admin.role === 'ROLE_ADMIN'">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <!-- 分页区域       -->
        <div style="margin-top:15px;">
            <el-pagination
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="searchForm.currentPage"
                    :page-sizes="[2, 5, 10, 20]"
                    :page-size="searchForm.pageSize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="total">
            </el-pagination>
        </div>

        <!--  对话框      -->
        <div>
            <el-dialog
                    :title="dialogTitle"
                    :visible.sync="dialogFormVisible"
                    :close-on-click-modal="false"
                    @close="closeDialog"
                    width="35%">
                <el-form
                        :model="addForm"
                        :rules="rules"
                        ref="addForm"
                        :label-width="formLabelWidth"
                        label-postion="left">
                    <el-form-item label="分类名称" prop="name">
                        <el-input v-model="addForm.name" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="分类描述" prop="description">
                        <el-input v-model="addForm.description" clearable></el-input>
                    </el-form-item>
                </el-form>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="resetBtn" v-show="dialogTitle === '新增分类'">重 置</el-button>
                    <el-button type="primary" @click="submitBtn">确 定</el-button>
                </div>
            </el-dialog>
        </div>
    </div>
</template>

<script>
    import request from "@/utils/request";

    export default {
        name: "BookType",
        computed: {
            admin(){
                return JSON.parse(window.localStorage.getItem('access-admin')) || { name: '未登录'};
            }
        },
        data() {
            return {

                //多选的结果
                multipleSelection: [],
                //添加表单
                addForm:{
                    name:'',
                    description:'',
                },
                rules:{
                    name:[{required: true, message: '请输入分类名称', trigger: 'blur'}],
                    description:[{required: true, message: '请输入分类描述', trigger: 'blur'}],
                },
                //表单标题宽度
                formLabelWidth:'80px',
                //对话框标题
                dialogTitle:'',
                //对话框
                dialogFormVisible: false,
                //搜索条件
                searchForm:{
                    name: '',
                    currentPage: 1,
                    pageSize: 5
                },
                tableData: [],
                total:0
            }
        },
        methods: {
            //批量导入回调
            successUpload(res){
                if(res.code === '200'){
                    this.$message.success('批量导入成功');
                    this.doSearch();
                }else {
                    this.$message.error(res.msg);
                    this.doSearch();
                }
            },
            //导出
            exportBtn(){
                //方式一
                let admin = JSON.parse(window.localStorage.getItem('access-admin'));
                window.location.href = 'http://localhost:8089/api/bookType/export?token=' + admin.token;
            },
            //表格多选的结果变化
            handleSelectionChange(val) {
                this.multipleSelection = val;
            },
            //批量删除
            delBatchBtn(){
                if(this.multipleSelection.length <= 0){
                    this.$message.warning("请先勾选数据,再删除!");
                    return;
                }

                this.$confirm(`您确定要删除这些数据吗`,'删除提示',{
                    confirmButtonText:'删除',
                    cancelButtonText:'取消',
                    type:'warning',
                }).then(()=>{
                    //todo
                    request.post('/bookType/delBatch',this.multipleSelection)
                        .then(res => {
                            if(res.code === '200'){
                                this.$message.success(res.data);
                                this.doSearch();
                            }
                    })
                }).catch(_=>{
                    this.$message.warning('已取消删除');
                })
            },
            //删除
            deleteBtn(row){
                this.$confirm(`您确定要删除【${row.name}】吗`,'删除提示',{
                    confirmButtonText:'删除',
                    cancelButtonText:'取消',
                    type:'warning',
                }).then(()=>{
                    request.delete('/bookType/delete',{
                        params:{ id : row.id}
                    }).then(res => {
                        if(res.code === '200'){
                            this.$message.success(res.data);
                            this.doSearch();
                        }
                    })
                }).catch(_=>{
                    this.$message.warning('已取消删除');
                })
            },
            //编辑
            editBtn(row){
                let obj = JSON.parse(JSON.stringify(row));
                this.addForm = obj;
                this.dialogTitle = "编辑分类";
                this.dialogFormVisible = true;
            },
            //关闭对话框
            closeDialog(){
                this.resetBtn();
                this.dialogFormVisible = false;
            },
            //新增保存
            submitBtn(){
                this.$refs.addForm.validate((valid)=>{
                    if(valid){
                        //校验通过
                        //有id,编辑,没有id是新增
                        request.post(this.addForm.id ? '/bookType/update':'/bookType/save',this.addForm)
                            .then(res=>{
                                if(res.code === '200'){
                                    this.$message.success(res.data);
                                    this.resetBtn();
                                    this.dialogFormVisible = false;
                                    this.doSearch();
                                }else {
                                    this.$message.error(res.msg);
                                }
                            })
                    }
                })
            },
            //新增重置
            resetBtn(){
                this.$refs.addForm.resetFields();
                //修复bug
                this.addForm = {};
            },
            addBtn(){
                this.dialogTitle = '新增分类';
                this.dialogFormVisible = true;
            },
            clearSearch(){
                this.searchForm.name = '';
                this.doSearch();
            },
            //搜索
            doSearch(){
                //修复bug
                this.searchForm.currentPage = 1;
                this.getData();

            },
            handleSizeChange(val) {
                this.searchForm.pageSize = val;
                this.searchForm.currentPage = 1;
                this.getData();
            },
            handleCurrentChange(val) {
                this.searchForm.currentPage = val;
                this.getData();
            },
            //获取数据
            getData(){
                request.get('/bookType/searchPage',{
                    params: this.searchForm
                }).then(res=>{
                    if(res.code === '200'){
                        this.tableData = res.data.records; //数据
                        this.searchForm.currentPage = res.data.current; //当前页
                        this.searchForm.pageSize = res.data.size; //页条数
                        this.total = res.data.total; //总条数
                    }else {
                        this.$message.error(res.msg);
                    }
                });
            }
        },
        created(){
            //获取数据
            this.getData();
        }
    }
</script>

<style scoped>

</style>

bookView.vue

<template>
    <div>
        <!-- 搜索区域       -->
        <div style="margin-bottom:15px;">
            <el-input
                    v-model="searchForm.name"
                    style="width:200px;"
                    placeholder="请输入图书名"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-input
                    v-model="searchForm.author"
                    style="width:200px;margin-left: 10px;"
                    placeholder="请输入作者"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-input
                    v-model="searchForm.express"
                    style="width:200px;margin-left: 10px;"
                    placeholder="请输入出版社"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-button
                    type="warning"
                    style="margin-left: 10px;"
                    icon="el-icon-search"
                    @click="doSearch">查询</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-toilet-paper"
                    @click="clearSearch">清空</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-plus" @click="addBtn"
                    v-if="admin.role !== 'ROLE_STUDENT'">新增</el-button>
        </div>

        <!-- 表格区域       -->
        <el-table
                :data="tableData"
                style="width: 100%">
            <el-table-column
                    prop="id"
                    label="ID">
            </el-table-column>
            <el-table-column
                    prop="name"
                    label="图书名称">
            </el-table-column>
            <el-table-column
                    prop="author"
                    label="作者">
            </el-table-column>
            <el-table-column
                    prop="express"
                    label="出版社">
            </el-table-column>
            <el-table-column
                    prop="price"
                    label="价格">
            </el-table-column>
            <el-table-column
                    prop="img"
                    label="图书封面">
                <template slot-scope="scope">
                    <el-image
                            :src="scope.row.img"
                            :preview-src-list="[scope.row.img]"
                            style="width:60px;height:60px;border-radius: 50%;"></el-image>
                </template>
            </el-table-column>
            <el-table-column
                    prop="typeId"
                    label="分类id">
                <template slot-scope="scope">
                    <span> {{ bookTypeList.find((item) => item.id === scope.row.typeId).name }}</span>
                </template>
            </el-table-column>
            <el-table-column label="操作" width="260px">
                <template slot-scope="scope">
                    <el-button
                            type="primary"
                            icon="el-icon-edit"
                            @click="editBtn(scope.row)"
                            v-if="admin.role === 'ROLE_ADMIN'">编辑</el-button>
                    <el-button
                            type="danger"
                            icon="el-icon-delete"
                            @click="deleteBtn(scope.row)"
                            v-if="admin.role === 'ROLE_ADMIN'">删除</el-button>
                    <el-button
                            icon="el-icon-download"
                            @click="downloadBtn(scope.row)">下载</el-button>
                </template>
            </el-table-column>
        </el-table>
        <!-- 分页区域       -->
        <div style="margin-top:15px;">
            <el-pagination
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="searchForm.currentPage"
                    :page-sizes="[2, 5, 10, 20]"
                    :page-size="searchForm.pageSize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="total">
            </el-pagination>
        </div>
        <!--  对话框      -->
        <div>
            <el-dialog
                    :title="dialogTitle"
                    :visible.sync="dialogFormVisible"
                    :close-on-click-modal="false"
                    @close="closeDialog"
                    width="35%">
                <el-form
                        :model="addForm"
                        :rules="rules"
                        ref="addForm"
                        :label-width="formLabelWidth"
                        label-postion="left">
                    <el-form-item label="图书名称" prop="name">
                        <el-input v-model="addForm.name" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="作者" prop="author">
                        <el-input v-model="addForm.author" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="出版社" prop="express">
                        <el-input v-model="addForm.express" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="价格" prop="price">
                        <el-input-number v-model="addForm.price" :max="9999.9" :min="0.1" :step="0.5"></el-input-number>
                    </el-form-item>
                    <el-form-item label="图片封面" prop="img">
<!--                        <el-input v-model="addForm.img" clearable></el-input>-->
                        <el-upload :action="uploadPath" :on-success="successUpload">
                            <el-button size="small" type="primary">点击上传</el-button>
                        </el-upload>
                    </el-form-item>
                    <el-form-item label="图书分类" prop="typeId">
                        <el-select v-model="addForm.typeId" clearable>
                            <el-option
                                    v-for="item in bookTypeList"
                                    :key="item.id"
                                    :label="item.name"
                                    :value="item.id"></el-option>
                        </el-select>
                    </el-form-item>
                </el-form>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="resetBtn" v-show="dialogTitle === '新增图书'">重 置</el-button>
                    <el-button type="primary" @click="submitBtn">确 定</el-button>
                </div>
            </el-dialog>
        </div>
    </div>
</template>

<script>
    import request from "@/utils/request";

    export default {
        name: "BookView",
        computed: {
            admin(){
                return JSON.parse(window.localStorage.getItem('access-admin')) || { name: '未登录'};
            }
        },
        data() {
            return {
                //图书分类
                bookTypeList:[],
                //上传路径
                uploadPath:'http://localhost:8089/api/files/upload/',
                //下载路径
                downloadPath:'http://localhost:8089/api/files/down/',
                //添加表单
                addForm:{
                    name:'',
                    author:'',
                    express:'',
                    price:'',
                    img:'',
                    typeId:'',
                },
                rules:{
                    name:[{required: true, message: '请输入图书名称', trigger: 'blur'}],
                    author:[{required: true, message: '请输入作者', trigger: 'blur'}],
                    express:[{required: true, message: '请输入出版社', trigger: 'blur'}],
                    price:[{required: true, message: '请输入价格', trigger: 'blur'}],
                    img:[{required: true, message: '请输入封面地址', trigger: 'blur'}],
                    typeId:[{required: true, message: '请选择图书分类', trigger: 'change'}],
                },
                //表单标题宽度
                formLabelWidth:'80px',
                //对话框标题
                dialogTitle:'',
                //对话框
                dialogFormVisible: false,
                //搜索条件
                searchForm:{
                    name: '',
                    author: '',
                    express:'',
                    currentPage: 1,
                    pageSize: 5
                },
                tableData: [],
                total:0
            }
        },
        methods: {
            //获取全部的图书分类
            getBookTypeList(){
                request.get('/bookType/list').then(res=>{
                    if(res.code === '200'){
                        this.bookTypeList = res.data;
                    }
                })
            },
            //下载按钮
            downloadBtn(row){
                window.location.href = row.img;
            },
            //文件上传成功的回调
            successUpload(res){
                this.addForm.img = this.downloadPath + res.data;
            },
            //删除
            deleteBtn(row){
                this.$confirm(`您确定要删除【${row.name}】吗`,'删除提示',{
                    confirmButtonText:'删除',
                    cancelButtonText:'取消',
                    type:'warning',
                }).then(()=>{
                    request.delete('/book/delete',{
                        params:{ id : row.id}
                    }).then(res => {
                        if(res.code === '200'){
                            this.$message.success(res.data);
                            this.doSearch();
                        }
                    })
                }).catch(_=>{
                    this.$message.warning('已取消删除');
                })
            },
            //编辑
            editBtn(row){
                let obj = JSON.parse(JSON.stringify(row));
                this.addForm = obj;
                this.dialogTitle = "编辑图书";
                this.dialogFormVisible = true;
            },
            //关闭对话框
            closeDialog(){
                this.resetBtn();
                this.dialogFormVisible = false;
            },
            //新增保存
            submitBtn(){
                this.$refs.addForm.validate((valid)=>{
                    if(valid){
                        //校验通过
                        //有id,编辑,没有id是新增
                        request.post(this.addForm.id ? '/book/update':'/book/save',this.addForm)
                            .then(res=>{
                                if(res.code === '200'){
                                    this.$message.success(res.data);
                                    this.resetBtn();
                                    this.dialogFormVisible = false;
                                    this.doSearch();
                                }else {
                                    this.$message.error(res.msg);
                                }
                            })
                    }
                })
            },
            //新增重置
            resetBtn(){
                this.$refs.addForm.resetFields();
                //修复bug
                this.addForm = {};
            },
            addBtn(){
                this.dialogTitle = '新增图书';
                this.dialogFormVisible = true;
            },
            clearSearch(){
                this.searchForm.name = '';
                this.searchForm.author = '';
                this.searchForm.express = '';
                this.doSearch();
            },
            //搜索
            doSearch(){
                //修复bug
                this.searchForm.currentPage = 1;
                this.getData();

            },
            handleSizeChange(val) {
                this.searchForm.pageSize = val;
                this.searchForm.currentPage = 1;
                this.getData();
            },
            handleCurrentChange(val) {
                this.searchForm.currentPage = val;
                this.getData();
            },
            //获取数据
            getData(){
                request.get('/book/searchPage',{
                    params: this.searchForm
                }).then(res=>{
                    if(res.code === '200'){
                        this.tableData = res.data.records; //数据
                        this.searchForm.currentPage = res.data.current; //当前页
                        this.searchForm.pageSize = res.data.size; //页条数
                        this.total = res.data.total; //总条数
                    }else {
                        this.$message.error(res.msg);
                    }
                });
            }
        },
        created(){
            //获取数据
            this.getData();
            //获取图书分类
            this.getBookTypeList();
        }
    }
</script>

<style scoped>

</style>

 adminView.vue

<template>
    <div>
        <!-- 搜索区域       -->
        <div style="margin-bottom:15px;">
            <el-input
                    v-model="searchForm.name"
                    style="width:200px;"
                    placeholder="请输入姓名"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-input
                    v-model="searchForm.phone"
                    style="width:200px;margin-left: 10px;"
                    placeholder="请输入电话"
                    @clear="doSearch"
                    @keypress.native.enter="doSearch"
                    clearable>
            </el-input>
            <el-button
                    type="warning"
                    style="margin-left: 10px;"
                    icon="el-icon-search"
                    @click="doSearch">查询</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-toilet-paper"
                    @click="clearSearch">清空</el-button>
            <el-button
                    type="primary"
                    style="margin-left: 10px;"
                    icon="el-icon-plus" @click="addBtn">新增</el-button>
        </div>
        <!-- 表格区域       -->
        <el-table
                :data="tableData"
                style="width: 100%">
            <el-table-column
                    prop="id"
                    label="ID">
            </el-table-column>
            <el-table-column
                    prop="name"
                    label="姓名">
            </el-table-column>
            <el-table-column
                    prop="password"
                    label="密码">
            </el-table-column>
            <el-table-column
                    prop="age"
                    label="年龄">
            </el-table-column>
            <el-table-column
                    prop="sex"
                    label="性别">
            </el-table-column>
            <el-table-column
                    prop="phone"
                    label="电话">
            </el-table-column>
            <el-table-column
                    prop="role"
                    label="角色">
                <template slot-scope="scope">
                    <span v-show="scope.row.role === 'ROLE_TEACHER'">教师<i class="el-icon-trophy" ></i></span>
                    <span v-show="scope.row.role === 'ROLE_STUDENT'">学生<i class="el-icon-headset" ></i></span>
                </template>
            </el-table-column>
            <el-table-column label="操作">
                <template slot-scope="scope">
                    <el-button type="primary" icon="el-icon-edit" @click="editBtn(scope.row)">编辑</el-button>
                    <el-button type="danger" icon="el-icon-delete" @click="deleteBtn(scope.row)">删除</el-button>
                </template>
            </el-table-column>
        </el-table>
        <!-- 分页区域       -->
        <div style="margin-top:15px;">
            <el-pagination
                    @size-change="handleSizeChange"
                    @current-change="handleCurrentChange"
                    :current-page="searchForm.currentPage"
                    :page-sizes="[2, 5, 10, 20]"
                    :page-size="searchForm.pageSize"
                    layout="total, sizes, prev, pager, next, jumper"
                    :total="total">
            </el-pagination>
        </div>
        <!--  对话框      -->
        <div>
            <el-dialog
                    :title="dialogTitle"
                    :visible.sync="dialogFormVisible"
                    :close-on-click-modal="false"
                    @close="closeDialog"
                    width="35%">
                <el-form
                        :model="addForm"
                        :rules="rules"
                        ref="addForm"
                        :label-width="formLabelWidth"
                        label-position="left">
                    <el-form-item label="姓名" prop="name">
                        <el-input v-model="addForm.name" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="密码" prop="password">
                        <el-input v-model="addForm.password" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="年龄" prop="age">
                        <el-input-number v-model="addForm.age" :max="199" :min="1" label="描述文字"></el-input-number>
                    </el-form-item>
                    <el-form-item label="性别" prop="sex">
                        <el-radio v-model="addForm.sex" label="男">男</el-radio>
                        <el-radio v-model="addForm.sex" label="女">女</el-radio>
                    </el-form-item>
                    <el-form-item label="电话" prop="phone">
                        <el-input v-model="addForm.phone" clearable></el-input>
                    </el-form-item>
                    <el-form-item label="角色名" prop="role">
                        <el-select v-model="addForm.role" clearable>
                            <el-option label="教师" value="ROLE_TEACHER"></el-option>
                            <el-option label="学生" value="ROLE_STUDENT"></el-option>
                        </el-select>
                    </el-form-item>
                </el-form>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="resetBtn" v-show="dialogTitle === '新增用户'">重 置</el-button>
                    <el-button type="primary" @click="submitBtn">确 定</el-button>
                </div>
            </el-dialog>
        </div>
    </div>
</template>

<script>
    import request from "@/utils/request";

    export default {
        name: "AdminView",
        data() {
            return {
                //添加表单
                addForm:{
                    name:'',
                    password:'',
                    age:'',
                    sex:'',
                    phone:'',
                    role: '',
                },
                rules:{
                    name:[{required: true, message: '请输入姓名', trigger: 'blur'}],
                    password:[{required: true, message: '请输入密码', trigger: 'blur'}],
                    age:[{required: true, message: '请输入年龄', trigger: 'blur'}],
                    sex:[{required: true, message: '请选择性别', trigger: 'blur'}],
                    phone:[{required: true, message: '请输入电话', trigger: 'blur'}],
                    role:[{required: true, message: '请选择角色名', trigger: 'change'}],
                },
                //表单标题宽度
                formLabelWidth:'80px',
                //对话框标题
                dialogTitle:'',
                //对话框
                dialogFormVisible: false,
                //搜索条件
                searchForm:{
                    name: '',
                    phone: '',
                    currentPage: 1,
                    pageSize: 5
                },
                tableData: [],
                total:0
            }
        },
        methods: {
            //删除
            deleteBtn(row){
                this.$confirm(`您确定要删除【${row.name}】吗`,'删除提示',{
                    confirmButtonText:'删除',
                    cancelButtonText:'取消',
                    type:'warning',
                }).then(()=>{
                    request.delete('/user/delete',{
                        params:{ id : row.id}
                    }).then(res => {
                        if(res.code === '200'){
                            this.$message.success(res.data);
                            this.doSearch();
                        }
                    })
                }).catch(_=>{
                    this.$message.warning('已取消删除');
                })
            },
            //编辑
            editBtn(row){
                let obj = JSON.parse(JSON.stringify(row));
                this.addForm = obj;
                this.dialogTitle = "编辑用户";
                this.dialogFormVisible = true;
            },
            //关闭对话框
            closeDialog(){
                this.resetBtn();
                this.dialogFormVisible = false;
            },
            //新增保存
            submitBtn(){
                this.$refs.addForm.validate((valid)=>{
                    if(valid){
                        //校验通过
                        //有id,编辑,没有id是新增
                        request.post(this.addForm.id ? '/user/update':'/user/save',this.addForm)
                            .then(res=>{
                                if(res.code === '200'){
                                    this.$message.success(res.data);
                                    this.resetBtn();
                                    this.dialogFormVisible = false;
                                    this.doSearch();
                                }else {
                                    this.$message.error(res.msg);
                                }
                            })
                    }
                })
            },
            //新增重置
            resetBtn(){
                this.$refs.addForm.resetFields();
                //修复bug
                this.addForm = {};
                //this.$refs.addForm.id = '';
            },
            addBtn(){
                this.dialogTitle = '新增用户';
                this.dialogFormVisible = true;
            },
            clearSearch(){
                this.searchForm.name = '';
                this.searchForm.phone = '';
                this.doSearch();
            },
            //搜索
            doSearch(){
                // request.get('/user/search',{
                //     params: this.searchForm
                // }).then(res=>{
                //     if(res.code === '200'){
                //         this.tableData = res.data;
                //     }else {
                //         this.$message.error("查询失败");
                //     }
                // })

                //修复bug
                this.searchForm.currentPage = 1;
                this.getData();

            },
            handleSizeChange(val) {
                this.searchForm.pageSize = val;
                this.searchForm.currentPage = 1;
                this.getData();
            },
            handleCurrentChange(val) {
                this.searchForm.currentPage = val;
                this.getData();
            },
            //获取数据
            getData(){
                // request.get('/user/findAll')
                //     .then(res => {
                //         if(res.code === '200'){
                //             this.tableData = res.data;
                //         }else {
                //             this.$message.error('获取数据失败');
                //         }
                //     });
                request.get('/user/searchPage',{
                    params: this.searchForm
                }).then(res=>{
                    if(res.code === '200'){
                        //console.log(res)
                        this.tableData = res.data.records; //数据
                        this.searchForm.currentPage = res.data.current; //当前页
                        this.searchForm.pageSize = res.data.size; //页条数
                        this.total = res.data.total; //总条数
                    }else {
                        this.$message.error(res.msg);
                    }
                });
            }
        },
        created(){
            //获取数据
            this.getData();
        }
    }
</script>

<style scoped>

</style>

 userview.vue

<template>
    <div>UserView</div>
</template>

<script>
    export default {
        name: "UserView"
    }
</script>

<style scoped>

</style>

 homeview.vue

<template>
    <div>
        <div style="font-size: 18px;margin: 15px 0;">手牵手教小白做毕设</div>
        <div style="font-size: 18px;margin: 15px 0;">大家给个一键三连</div>
        <div style="font-size: 18px;margin: 15px 0;">感谢大家</div>
    </div>
</template>

<script>
    export default {
        name: "HomeView"
    }
</script>

<style scoped>

</style>

 layout.vue

<template>
    <div>
        <el-container class="container">
            <el-header class="header-area">
                <img src="@/assets/logo.png" alt="logo" class="logo">
                <span class="title">手牵手带小白做毕设</span>
                <span class="admin-info">
                <el-dropdown @command="handleCommand">
                    <span class="el-dropdown-link">
                        用户:&nbsp;&nbsp; <strong>{{ admin.name }}</strong>
                        <i class="el-icon-arrow-down el-icon--right"></i>
                    </span>
                    <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item command="logout">退出</el-dropdown-item>
                    </el-dropdown-menu>
                    </el-dropdown>
                </span>
            </el-header>
            <el-container class="middle-area">
                <el-aside  class="left-aside">
                    <el-menu
                            :default-active="$route.path"
                            class="el-menu-vertical-demo"
                            background-color="#545c64"
                            text-color="#fff"
                            active-text-color="#ffd04b"
                            router>
                        <el-menu-item index="/home">
                            <i class="el-icon-menu"></i>
                            <span slot="title">系统首页</span>
                        </el-menu-item>
                        <el-submenu index="/admin" v-if="admin.role === 'ROLE_ADMIN'">
                            <template slot="title">
                                <i class="el-icon-location"></i>
                                <span>用户管理</span>
                            </template>
                            <el-menu-item-group>
                                <el-menu-item index="/admin">用户信息</el-menu-item>
                            </el-menu-item-group>
                        </el-submenu>
                        <el-submenu index="/book">
                            <template slot="title">
                                <i class="el-icon-location"></i>
                                <span>信息管理</span>
                            </template>
                            <el-menu-item-group>
                                <el-menu-item index="/book">图书信息</el-menu-item>
                                <el-menu-item index="/type">图书分类</el-menu-item>
                            </el-menu-item-group>
                        </el-submenu>
                    </el-menu>
                </el-aside>
                <el-main>
                    <router-view/>
                </el-main>
            </el-container>
        </el-container>
    </div>
</template>

<script>
    export default {
        name: "Layout",
        computed: {
            admin(){
                return JSON.parse(window.localStorage.getItem('access-admin')) || { name: '未登录'};
            }
        },
        methods: {
            //下拉菜单命令
            handleCommand(command){
                if (command === 'logout') {
                    this.logout();
                }
            },
            //退出
            logout(){
                window.localStorage.clear();
                this.$message.success('退出成功!');
                this.$router.replace('/login');
            }
        }
    }
</script>

<style lang="scss" scoped>
    .container{
        height: 100vh;

        .header-area{
            background-color: #4c535a;

            .logo {
                width: 40px;
                position: relative;
                top: 10px;
            }

            .title{
                font-size: 20px;
                margin-left: 15px;
                color: white;
            }

            .admin-info{
                float: right;
                margin-right: 30px;
                line-height: 60px;
                .el-dropdown-link{
                    color: #cccccc;
                }
            }

        }

        .middle-area{

            .left-aside{
                overflow: hidden;
                height: 100%;
                /*background-color: #545c64;*/
                width:230px  !important;

                .el-menu-vertical-demo{
                    height: 100%;
                }


            }



        }

    }
</style>

 loginview.vue

<template>
    <div class="register-area">
        <h1 class="title">手牵手带小白做毕设</h1>
        <el-form
                :model="loginForm"
                :rules="rules"
                ref="loginForm"
                class="form-demo"
                label-width="100px;"
                label-postion="left">
            <el-form-item prop="name">
                <el-input
                        v-model="loginForm.name"
                        placeholder="用户名"
                        clearable>
                    <template slot="prepend"><i class="el-icon-user-solid"></i></template>
                </el-input>
            </el-form-item>
            <el-form-item prop="password">
                <el-input
                        v-model="loginForm.password"
                        placeholder="密码"
                        show-password
                        clearable>
                    <template slot="prepend"><i class="el-icon-lock"></i></template>
                </el-input>
            </el-form-item>

            <el-button type="primary" @click="login" class="login-btn">登 录</el-button>
            <el-button type="text" @click="toRegister" class="register-btn">没有账号,注册一下</el-button>
        </el-form>
        <div>

        </div>
    </div>
</template>

<script>
    import request from "@/utils/request";

    export default {
        name: "LoginView",
        data(){
            return {
                loginForm: {
                    name:'',
                    password:''
                },
                rules: {
                    name: [{ required: true ,message: '请输入用户名', trigger: 'blur'}],
                    password: [{ required: true ,message: '请输入密码', trigger: 'blur'}],
                }
            }
        },
        methods: {
            //注册
            toRegister(){
                this.$router.replace('/register');
            },
            login(){
                this.$refs.loginForm.validate((valid)=>{
                    if(valid){
                        request.post('/user/login',this.loginForm)
                            .then(res=>{
                                if(res.code === '200'){
                                    this.$message.success(res.msg);
                                    window.localStorage.setItem('access-admin',JSON.stringify(res.data));
                                    this.$router.replace('/home');
                                }else {
                                    this.$message.error(res.msg);
                                }
                            })
                    }
                })
            }
        }
    }
</script>

<style lang="scss" scoped>
    .register-area{
        width: 400px;
        height: 310px;
        border-radius: 15px;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%,-75%);
        padding: 10px;
        background-color: rgba(107,149,224,0.5);

        .title{
            text-align: center;
            margin-bottom: 30px;

        }

        .form-demo{
            width: 80%;
            margin: 0 auto;

            .login-btn{
                width: 100%;
                margin-top: 24px;
                height: 40px;
            }

            .register-btn{
                width: 100%;
                margin-top: 5px;
                text-align: center;
                color: #ffffff;
            }
        }


    }
</style>

 regisert.vue

<template>
    <div class="register-area">
        <h1 class="title">注册用户</h1>
        <el-form
                :model="registerForm"
                :rules="rules"
                ref="registerForm"
                class="form-demo"
                label-width="100px;"
                label-postion="left">
            <el-form-item prop="name">
                <el-input
                        v-model="registerForm.name"
                        placeholder="用户名"
                        clearable>
                    <template slot="prepend"><i class="el-icon-user-solid"></i></template>
                </el-input>
            </el-form-item>
            <el-form-item prop="password">
                <el-input
                        v-model="registerForm.password"
                        placeholder="密码"
                        show-password
                        clearable>
                    <template slot="prepend"><i class="el-icon-lock"></i></template>
                </el-input>
            </el-form-item>
            <el-form-item prop="role">
                <el-select v-model="registerForm.role" placeholder="请选择角色" style="width:100%;" clearable>
                    <el-option label="教师" value="ROLE_TEACHER"></el-option>
                    <el-option label="学生" value="ROLE_STUDENT"></el-option>
                </el-select>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="resetBtn" class="reset-btn">重 置</el-button>
            </el-form-item>
            <el-button type="primary" @click="register" class="register-btn">注 册</el-button>
            <el-button type="text" @click="toLogin" class="login-btn">返回登录</el-button>
        </el-form>

    </div>
</template>

<script>
    import request from "@/utils/request";

    export default {
        name: "RegisterView",
        data(){
            return {
                registerForm: {
                    name:'',
                    password:'',
                    role:'',
                },
                rules: {
                    name: [{ required: true ,message: '请输入用户名', trigger: 'blur'}],
                    password: [{ required: true ,message: '请输入密码', trigger: 'blur'}],
                    role: [{ required: true ,message: '请选择角色', trigger: 'change'}],
                }
            }
        },
        methods: {
            //重置
            resetBtn(){
                this.$refs.registerForm.resetFields();
            },
            //返回登录
            toLogin(){
                this.$router.replace('/login');
            },
            //注册
            register(){
                this.$refs.registerForm.validate((valid)=>{
                    if(valid){
                        //默认是学生
                        //this.registerForm.role = 'ROLE_STUDENT';
                        request.post('/user/register',this.registerForm)
                            .then(res=>{
                                if(res.code === '200'){
                                    this.$message.success(res.msg);
                                    this.$router.replace('/login');
                                }else {
                                    this.$message.error(res.msg);
                                }
                            })
                    }
                })
            }
        }
    }
</script>

<style lang="scss" scoped>
    .register-area{
        width: 400px;
        height: 500px;
        border: 1px solid rgba(107,149,224,0.5);
        border-radius: 15px;
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%,-75%);
        padding: 10px;

        .title{
            text-align: center;
            margin-bottom: 30px;

        }

        .form-demo{
            width: 80%;
            margin: 0 auto;

            .reset-btn{
                width: 100%;
                margin-top: 24px;
                height: 40px;
            }

            .register-btn{
                width: 100%;
                height: 40px;
            }

            .login-btn{
                width: 100%;
                margin-top: 5px;
                text-align: center;
            }
        }


    }
</style>

 app.vue

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<style lang="scss">

</style>

main.js

import Vue from 'vue'
import App from './App.vue'
import store from '@/store'
import router from "@/router";
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from "axios";
import * as echarts from 'echarts'
import '@/assets/global.css'

Vue.prototype.$echarts = echarts;
axios.defaults.baseURL='http://localhost:8089';
Vue.prototype.$http = axios;
Vue.use(ElementUI,{size:'small'});
Vue.config.productionTip = false;

new Vue({
  render: h => h(App),
  store,
  router
}).$mount('#app');

 测试

登录

 注册

 首页

 用户管理

 图书信息

新增图书

 下载

 图书分类

 批量删除

 导出数据

 教师登录

 学生登录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实现Spring Boot Vue Element UI Mybatis-Plus的文件分享,可以按照以下步骤进行: 1. 首先,需要搭建一个基于Spring Boot的后端应用程序。使用Mybatis-Plus来操作数据库,设计合适的实体类和数据库表,用于存储文件的相关信息,例如文件名、路径、大小等。 2. 在后端应用程序中创建一个文件上传的接口,用于接收前端传递的文件。可以使用Spring Boot内置的MultipartFile类来处理文件上传,将文件保存到指定的目录中,并将文件相关信息保存到数据库中。 3. 创建一个文件下载的接口,用于访问和下载已上传的文件。通过接口可以获取文件的相关信息,包括文件的路径和文件名。使用Java的File类或者相关的工具类来读取文件,并将文件以流的形式返回给前端。 4. 在前端应用程序中使用Vue和Element UI来实现文件分享页面。可以设计一个文件列表的组件,展示已上传的文件信息,例如文件名、大小、上传时间等。使用Element UI的Table组件可以方便地展示数据。 5. 在文件列表组件中,为每个文件增加下载功能的按钮或链接。按钮或链接的点击事件调用后端提供的文件下载接口,将文件下载到用户本地。 6. 可以考虑增加文件的搜索和排序功能,方便用户查找和管理文件。可以使用Element UI的Input组件和Table组件的排序功能来实现。 7. 为了提高用户体验,可以加入文件预览的功能。可以使用第三方插件,如Viewer.js,来实现文件的在线预览。 总结来说,实现Spring Boot Vue Element UI Mybatis-Plus的文件分享需要搭建一个后端应用程序来处理文件的上传和下载,使用数据库存储文件相关信息;在前端应用程序中使用Vue和Element UI来展示和管理文件,实现文件的搜索、排序和预览功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虾米大王

有你的支持,我会更有动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值