SpringBoot + vue 管理系统

SpringBoot + vue 管理系统

文章目录
1、成品效果展示

SpringBoot + vue 管理系统

2、项目准备

环境准备

步骤:

  1. 准备数据库表

  2. 创建springboot工程,引入对应的起步依赖(web、mybatis、mysql驱动、lombok)

  3. 配置文件application.properties中引入mybatis的配置信息,准备对应的实体类

  4. 准备对应的Mapper、Service(接口、实现类)、Controller基础结构

    -- 部门管理

    create table dept(

    id int unsigned primary key auto_increment comment '主键ID',

    name varchar(10) not null unique comment '部门名称',

    create_time datetime not null comment '创建时间',

    update_time datetime not null comment '修改时间'

    ) comment '部门表';

    insert into dept (id, name, create_time, update_time) values(1,'学工部',now(),now()),

    (2,'教研部',now(),now()),

    (3,'咨询部',now(),now()),

    (4,'就业部',now(),now()),

    (5,'人事部',now(),now());

    -- 员工管理(带约束)

    create table emp (

    id int unsigned primary key auto_increment comment 'ID',

    username varchar(20) not null unique comment '用户名',

    password varchar(32) default '123456' comment '密码',

    name varchar(10) not null comment '姓名',

    gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',

    image varchar(300) comment '图像',

    job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',

    entrydate date comment '入职时间',

    dept_id int unsigned comment '部门ID',

    create_time datetime not null comment '创建时间',

    update_time datetime not null comment '修改时间'

    ) comment '员工表';

    INSERT INTO emp

    (id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES

    (1,'jinyong','123456','金庸',1,'1.jpg',4,'2000-01-01',2,now(),now()),

    (2,'zhangwuji','123456','张无忌',1,'2.jpg',2,'2015-01-01',2,now(),now()),

    (3,'yangxiao','123456','杨逍',1,'3.jpg',2,'2008-05-01',2,now(),now()),

    (4,'weiyixiao','123456','韦一笑',1,'4.jpg',2,'2007-01-01',2,now(),now()),

    (5,'changyuchun','123456','常遇春',1,'5.jpg',2,'2012-12-05',2,now(),now()),

    (6,'xiaozhao','123456','小昭',2,'6.jpg',3,'2013-09-05',1,now(),now()),

    (7,'jixiaofu','123456','纪晓芙',2,'7.jpg',1,'2005-08-01',1,now(),now()),

    (8,'zhouzhiruo','123456','周芷若',2,'8.jpg',1,'2014-11-09',1,now(),now()),

    (9,'dingminjun','123456','丁敏君',2,'9.jpg',1,'2011-03-11',1,now(),now()),

    (10,'zhaomin','123456','赵敏',2,'10.jpg',1,'2013-09-05',1,now(),now()),

    (11,'luzhangke','123456','鹿杖客',1,'11.jpg',5,'2007-02-01',3,now(),now()),

    (12,'hebiweng','123456','鹤笔翁',1,'12.jpg',5,'2008-08-18',3,now(),now()),

    (13,'fangdongbai','123456','方东白',1,'13.jpg',5,'2012-11-01',3,now(),now()),

    (14,'zhangsanfeng','123456','张三丰',1,'14.jpg',2,'2002-08-01',2,now(),now()),

    (15,'yulianzhou','123456','俞莲舟',1,'15.jpg',2,'2011-05-01',2,now(),now()),

    (16,'songyuanqiao','123456','宋远桥',1,'16.jpg',2,'2007-01-01',2,now(),now()),

    (17,'chenyouliang','123456','陈友谅',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());

    CREATE TABLE clazz (

    id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID,主键',

    name varchar(30) NOT NULL UNIQUE COMMENT '班级名称',

    room varchar(20) DEFAULT NULL COMMENT '班级教室',

    begin_date date NOT NULL COMMENT '开课时间',

    end_date date NOT NULL COMMENT '结课时间',

    master_id int unsigned NOT NULL COMMENT '班主任ID, 关联员工表ID',

    create_time datetime NOT NULL COMMENT '创建时间',

    update_time datetime NOT NULL COMMENT '修改时间'

    ) COMMENT '班级表';

    INSERT INTO clazz VALUES (1,'黄埔班一期','212','2023-04-30','2023-06-29',10,'2023-06-01 17:08:23','2023-06-01 17:39:58'),

    (2,'黄埔班二期','211','2023-06-25','2023-12-31',1,'2023-06-01 17:34:16','2023-06-01 17:43:43'),

    (3,'黄埔班三期','210','2023-07-10','2024-01-20',3,'2023-06-01 17:45:12','2023-06-01 17:45:12'),

    (4,'JavaEE就业165期','108','2023-06-15','2023-12-25',6,'2023-06-01 17:45:40','2023-06-01 17:45:40'),

    (5,'JavaEE就业166期','105','2023-07-20','2024-02-20',20,'2023-06-01 17:46:10','2023-06-01 17:46:10'),

    (6,'黄埔四期','209','2023-08-01','2024-02-15',7,'2023-06-01 17:51:21','2023-06-01 17:51:21');

    CREATE TABLE student (

    id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID,主键',

    name varchar(10) NOT NULL COMMENT '姓名',

    no char(10) NOT NULL UNIQUE COMMENT '学号',

    gender tinyint unsigned NOT NULL COMMENT '性别, 1: 男, 2: 女',

    phone varchar(11) NOT NULL UNIQUE COMMENT '手机号',

    degree tinyint unsigned DEFAULT NULL COMMENT '最高学历, 1:初中, 2:高中, 3:大专, 4:本科, 5:硕士, 6:博士',

    violation_count tinyint unsigned NOT NULL DEFAULT '0' COMMENT '违纪次数',

    violation_score tinyint unsigned NOT NULL DEFAULT '0' COMMENT '违纪扣分',

    clazz_id int unsigned NOT NULL COMMENT '班级ID, 关联班级表ID',

    create_time datetime NOT NULL COMMENT '创建时间',

    update_time datetime NOT NULL COMMENT '修改时间'

    ) COMMENT '学生表';

    INSERT INTO student VALUES (1,'Tom','2023001001',1,'18909091212',4,0,0,1,'2023-06-01 18:28:58','2023-06-01 18:28:58'),

    (2,'Cat','2023001002',2,'18909092345',3,0,0,1,'2023-06-01 18:34:57','2023-06-01 18:34:57'),

    (3,'Lily','2023001003',2,'13309230912',4,2,5,1,'2023-06-01 18:35:23','2023-06-01 19:37:42'),

    (4,'Jerry','2023001004',1,'15309232323',4,1,2,1,'2023-06-01 18:35:48','2023-06-01 19:37:35'),

    (6,'Nacos','2023002001',1,'18809091212',1,3,10,2,'2023-06-01 18:57:32','2023-06-01 19:37:29');

生成的pom.xml文件

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.tlias</groupId>
    <artifactId>web-tlias</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>web-tlias</name>
    <description>web-tlias</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.7.6</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </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>
<!--        pagehelper分页查询插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.2</version>
        </dependency>

<!--        阿里云oss依赖-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.2</version>
        </dependency>
<!--        jwt依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.tlias.WebTliasApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

创建项目工程目录结构

配置文件application.yml

复制代码
server:
  port: 8080

mybatis:
  mapper-locations: classpath:mappers/*xml
  type-aliases-package: com.tlias.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/tlias
    username: root
    password: 123456
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB

pagehelper:
  reasonable: true

aliyun:
  oss:
    endpoint: https://oss-cn-beijing.aliyuncs.com
    accessKeyId: you_acesskey
    accessKeySecret: you_accessKeySecret
    bucketName: tlias-lwj

设置统一返回数据

复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result {
    private int code;
    private String msg;
    private Object data;

    public static Result success() {
        return new Result(1, "success", null);
    }

    public static Result success(Object data) {
        return new Result(1, "success", data);
    }

    public static  Result error(String msg) {
        return new Result(0,msg,null);
    }

}

构建一个vue项目

3、项目开发

功能模块分为六部分:班级管理、学员管理、部门管理、员工管理、员工信息统计、学员信息统计

3.1、部门管理

开发的部门管理功能包含:

  1. 查询部门
  2. 删除部门
  3. 新增部门
  4. 更新部门
3.1.1、前端核心代码
复制代码
<template>
  <div style="margin-top: 20px; margin: 50px; margin-right: 100px">
    <!-- 按钮 -->

    <el-row>
      <el-button
        style="float: right"
        type="primary"
        @click="dialogFormVisible = true; dept={}"
        >+ 新增部门</el-button
      >
    </el-row>
    <br>
    <!-- 数据表格 -->
    <template>
      <el-table
        highlight-current-row
        ref="multipleTable"
        :data="tableData"
        tooltip-effect="dark"
        style="width: 100%"
        border
      >
        <el-table-column type="index" width="100" label="序号" header-align="center" align="center"> </el-table-column>
        <el-table-column prop="name" label="部门名称" header-align="center" align="center"></el-table-column>
        <el-table-column label="最后操作时间" header-align="center" align="center">
          <template slot-scope="scope">
              {{scope.row.updateTime ? scope.row.updateTime.replace('T',' '):''}}
          </template>
        </el-table-column>

        <el-table-column label="操作" width="420" header-align="center" align="center">
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="primary"
              plain
              @click="selectById(scope.row.id)"
              >编辑</el-button
            >

            <el-button
              size="mini"
              type="danger"
              plain
              @click="deleteById(scope.row.id)"
              >删除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
    </template>

    <!-- 新建对话框 -->

    <el-dialog title="保存部门" :visible.sync="dialogFormVisible" >
      <el-form :model="dept" :rules="rules" ref="dept">
        <el-form-item label="部门名称" :label-width="formLabelWidth" prop="name">
          <el-input v-model="dept.name"  placeholder="请输入部门名称" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>

      <div slot="footer" class="dialog-footer">
        <el-button @click="cancel('dept')">取 消</el-button>
        <el-button type="primary" @click="save('dept')">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import { findAll, add, update, deleteById, selectById } from "@/api/dept.js";

export default {
  data() {
    return {
      formLabelWidth: "120px",
      dialogFormVisible: false, //控制对话框是否可见
      dept: {
        name: "",
      },
      tableData: [],
      rules: {
          name: [
            { required: true, message: '请输入部门名称', trigger: 'blur' },
            { min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'blur' }
          ]
      }    
    };
  },

  methods: {

    //删除部门
    deleteById(id) {
      this.$confirm("确认删除?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
          deleteById(id).then((result) => {
          	if(result.data.code == 1){
	          	this.$message({
	              message: "恭喜你,删除成功",
	              type: "success",
	            });
	        }else{
	        	this.$message.error(result.data.msg);
	        }
            //重新查询数据
            this.init();
          });
        }).catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },

    //根据ID查询部门 -- 回显
    selectById(id) {
      this.dialogFormVisible = true;
      selectById(id).then((result) => {
        this.dept = result.data.data;
      });
    },

    //保存方法
    save(formName) {
       this.$refs[formName].validate((valid) => {
          if(valid){
            let operator ;
            if (this.dept.id) {
              operator = update(this.dept); // 修改
            }else{
              operator = add(this.dept); //添加 
            }

            operator.then((result) => {
              if (result.data.code == 1) {
                //修改成功
                this.$message.success("恭喜你,保存成功");
                //重新查询数据
                this.init();
                // 关闭新建窗口
                this.dialogFormVisible = false;
                // 清空模型数据
                this.dept = {};
              } else {
                this.$message.error(result.data.msg);
              }
            });
          }
       })
    },

    //初始化 - 查询全部
    init() {
      findAll().then((result) => {
        console.log(result);
        if (result.data.code == 1) {
          this.tableData = result.data.data;
        }
      });
    },

    cancel(formName){
      this.dialogFormVisible = false;
      this.$refs[formName].resetFields();
    }
  },
  mounted() {
    //当页面加载完成后自动执行。
    this.init();
  },
};
</script>
3.1.2、后端代码实现

实体类

复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
    private Integer id;
    private String name;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

controller层

复制代码
@RestController
@RequestMapping("depts")
@Slf4j
public class DeptController {

    @Autowired
    private DeptService service;

    /**
     * 查询部门
     * @return
     */
    @GetMapping
    public Result getDept(){
        log.info("查询部门数据");
        List<Dept> list = service.getDeptList();
        return Result.success(list);
    }

    /**
     * 根据id删除部门
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")

    public Result deleteDeptById(@PathVariable("id") Integer id){
        log.info("删除部门id {}",id);
        service.deleteDeptById(id);
        return Result.success();
    }

    /**
     * 新增部门
     * @param dept
     * @return
     */
    @PostMapping
    public Result save(@RequestBody Dept dept){
        log.info("新增部门 {}",dept);
        service.save(dept);
        return Result.success();
    }

    /**
     * 根据id查询部门
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Result selectDeptById(@PathVariable("id") Integer id){
        log.info("根据id查询部门{}",id);
        Dept dept = service.selectDeptById(id);
        return Result.success(dept);
    }

    /**
     * 修改部门
     * @param dept
     * @return
     */
    @PutMapping
    public Result update(@RequestBody Dept dept){
        log.info("修改部门信息{}",dept);
        service.update(dept);
        return Result.success();
    }
}

service层

service接口

复制代码
public interface DeptService {
    /**
     * 查询部门
     * @return
     */
    List<Dept> getDeptList();

    /**
     * 删除部门
     * @param id
     */
    void deleteDeptById(Integer id);

    /**
     * 新增部门
     * @param dept
     */
    void save(Dept dept);

    /**
     * 根据id查询部门
     * @param id
     * @return
     */
    Dept selectDeptById(Integer id);

    /**
     * 修改部门信息
     * @param dept
     */
    void update(Dept dept);
}

serviceImpl

复制代码
@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    private DeptMapper deptMapper;

    @Autowired
    private EmpMapper empMapper;

    /**
     * 查询部门
     *
     * @return
     */
    @Override
    public List<Dept> getDeptList() {
        List<Dept> list =  deptMapper.getDeptList();
        return list;
    }

    /**
     * 删除部门
     *
     * @param id
     */
    @Override
    public void deleteDeptById(Integer id) {
        Integer result = empMapper.selectEmpByDeptId(id);
        if (result < 1 ){
            throw new RuntimeException("不能删除部门,部门下面存在员工");
        }
        deptMapper.deleteDeptById(id);
    }

    /**
     * 新增部门
     *
     * @param dept
     */
    @Override
    public void save(Dept dept) {
        //添加修改时间
        dept.setCreateTime(LocalDateTime.now());
        dept.setUpdateTime(LocalDateTime.now());

        deptMapper.save(dept);
    }

    /**
     * 根据id查询部门
     *
     * @param id
     * @return
     */
    @Override
    public Dept selectDeptById(Integer id) {
        Dept dept =  deptMapper.selectDeptById(id);
        return dept;
    }

    /**
     * 修改部门信息
     *
     * @param dept
     */
    @Override
    public void update(Dept dept) {
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.update(dept);
    }
}

mapper层

复制代码
@Mapper
public interface DeptMapper {
    /**
     * 查询部门
     * @return
     */
    @Select("select * from dept")
    List<Dept> getDeptList();

    /**
     * 删除部门
     * @param id
     */
    @Delete("delete from dept where id = #{id}")
    void deleteDeptById(Integer id);

    /**
     * 新增部门
     * @param dept
     */
    @Insert("insert into dept values (null,#{name},#{createTime},#{updateTime})")
    void save(Dept dept);

    /**
     * 根据id查询部门
     * @param id
     * @return
     */
    @Select("select * from dept where id = #{id}")
    Dept selectDeptById(Integer id);

    /**
     * 修改部门信息
     * @param dept
     */
    @Update("update dept set name = #{name},update_time = #{updateTime} where id = #{id}")
    void update(Dept dept);
}
3.2、员工管理

我们可以把员工管理功能分为:

  1. 分页查询
  2. 带条件的分页查询
  3. 删除员工
  4. 新增员工
  5. 修改员工
3.2.1、前端核心代码
复制代码
<template>
  <div class="app-container">

    <!--搜索表单-->
    <el-form :inline="true" :model="searchEmp" class="demo-form-inline">
      <el-form-item label="姓名">
        <el-input
          v-model="searchEmp.name"
          placeholder="请输入员工姓名"
        ></el-input>
      </el-form-item>
      <el-form-item label="性别">
        <el-select v-model="searchEmp.gender" placeholder="请选择">
          <el-option label="男" value="1"></el-option>
          <el-option label="女" value="2"></el-option>
        </el-select>
      </el-form-item>

      <el-form-item label="入职时间">
        <el-date-picker
          v-model="entrydate"
          clearable
          value-format="yyyy-MM-dd"
          type="daterange"
          placeholder="选择日期"
          range-separator="至"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          style="width: 400px; margin-left: 20px"
        ></el-date-picker>
      </el-form-item>

      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
         <el-button type="info" @click="clear">清空</el-button>
      </el-form-item>
    </el-form>



    <!--按钮-->
    <el-row>
      <el-button type="danger" size="medium" @click="deleteByIds">- 批量删除</el-button>
      <el-button type="primary" size="medium" @click="dialogVisible = true; emp = { image: ''};" >+ 新增员工</el-button>
    </el-row>

    <!--添加数据对话框表单-->
    <el-dialog ref="form" title="编辑员工" :visible.sync="dialogVisible" width="30%">
      <el-form :model="emp" :rules="rules" ref="emp" label-width="80px" size="mini">
        <el-form-item label="用户名" prop="username">
          <el-input v-model="emp.username"></el-input>
        </el-form-item>
        <el-form-item label="员工姓名"  prop="name">
          <el-input v-model="emp.name"></el-input>
        </el-form-item>

        <el-form-item label="性别"  prop="gender">
          <el-select v-model="emp.gender" placeholder="请选择" style="width:100%" >
             <el-option
              v-for="item in genderList"
              :key="item.value"
              :label="item.name"
              :value="item.id"
            />
          </el-select>
        </el-form-item>

        <el-form-item label="头像">
          <el-upload
            class="avatar-uploader"
            action="/api/upload"
            :headers="token"
            name="image"
            :show-file-list="false"
            :on-success="handleAvatarSuccess"
            :before-upload="beforeAvatarUpload"
          >
            <img v-if="emp.image" :src="emp.image" class="avatar" />
            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
          </el-upload>
        </el-form-item>

        <el-form-item label="职位">
          <el-select v-model="emp.job" placeholder="请选择" value-key="emp.job" style="width:100%">
            <el-option
              v-for="item in jobList"
              :key="item.value"
              :label="item.name"
              :value="item.id"
            />
          </el-select>
        </el-form-item>

        <el-form-item label="入职日期">
          <el-date-picker
            v-model="emp.entrydate"
            clearable
            type="date"
            value-format="yyyy-MM-dd" 
            placeholder="选择日期"
            size="small"
            style="width:100%"
          ></el-date-picker>
        </el-form-item>

        <el-form-item label="归属部门">
          <el-select v-model="emp.deptId" placeholder="请选择" style="width:100%">
            <!-- 
            <el-option label="学工部" value="1"></el-option>
            <el-option label="教研部" value="2"></el-option>
             -->
            <el-option
              v-for="item in deptList"
              :key="item.value"
              :label="item.name"
              :value="item.id"
            />
          </el-select>
        </el-form-item>

        <el-form-item>
          <el-button type="primary" @click="save('emp')">提交</el-button>
          <el-button @click="cancel('emp')">取消</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>

    <br>

    <!--表格-->
    <template>
      <el-table :data="tableData" style="width: 100%" border @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55"  align="center"></el-table-column>
        <el-table-column  prop="name"  label="姓名"  align="center"></el-table-column>
        <el-table-column prop="image" label="头像" align="center">
          <template slot-scope="{ row }">
            <el-image style="width: auto; height: 40px; border: none; cursor: pointer" :src="row.image"></el-image>
          </template>
        </el-table-column>

        <el-table-column align="center" label="性别">
          <template slot-scope="scope">
            <span style="margin-right: 10px">
            {{scope.row.gender == "1" ? "男" : "女"}}</span>
          </template>
        </el-table-column>

        <el-table-column align="center" label="职位">
          <template slot-scope="scope">
            <span style="margin-right: 10px" v-if="scope.row.job == 1">班主任</span>
            <span style="margin-right: 10px" v-if="scope.row.job == 2">讲师</span>
            <span style="margin-right: 10px" v-if="scope.row.job == 3">学工主管</span>
            <span style="margin-right: 10px" v-if="scope.row.job == 4">教研主管</span>
          </template>
        </el-table-column>

        <el-table-column align="center" label="日职日期">
          <template slot-scope="scope">
            {{ scope.row.entrydate }}
          </template>
        </el-table-column>
        <el-table-column align="center" label="最后操作时间">
          <template slot-scope="scope">
            {{scope.row.updateTime ? scope.row.updateTime.replace('T',' '):''}}
          </template>
        </el-table-column>

        <el-table-column align="center" label="操作">
          <template slot-scope="scope">
            <el-button type="primary" size="small" @click="update(scope.row.id)">编辑</el-button>
            <el-button type="danger" size="small" @click="deleteById(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </template>

    <br>

    <!--分页工具条-->
    <el-pagination
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :background="background"
      :current-page="currentPage"
      :page-sizes="[5, 10, 15, 20]"
      :page-size="5"
      layout="total, sizes, prev, pager, next, jumper"
      :total="totalCount">
    </el-pagination>
  </div>
</template>

<script>
import { page, add, update, deleteById, selectById } from "@/api/emp.js";
import { findAll } from "@/api/dept.js";
import { getToken } from '@/utils/auth';

export default {
  data() {
    return {
      background: true,
      // 每页显示的条数
      pageSize: 5,
      // 总记录数
      totalCount: 100,
      // 当前页码
      currentPage: 1,
      // 添加数据对话框是否展示的标记
      dialogVisible: false,
      // 品牌模型数据
      searchEmp: {
        name: "",
        gender: "",
      },
      emp: {
        username: "",
        name: "",
        gender: "",
        image: "",
        job: "",
        entrydate: "",
        deptId: ""
      },
      deptList: [],
      genderList: [{id: 1,name: "男"},{id: 2,name: "女"}],
      jobList: [{id: 1,name: "班主任"},{id: 2,name: "讲师"},{id: 3, name: "学工主管"},{id: 4,name: "教研主管"}],
      beginTime: "",
      endTime: "",
      entrydate: "",
      // 被选中的id数组
      selectedIds: [],
      // 复选框选中数据集合
      multipleSelection: [],
      // 表格数据
      tableData: [],
      token: {token: getToken()},
      rules: {
          username: [
            {required: true, message: '请输入用户名', trigger: 'blur' },
            {min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
          ],
          name: [
            {required: true, message: '请输入姓名', trigger: 'blur' },
            {min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'blur' }
          ],
          gender: [
            {required: true, message: '请选择性别', trigger: 'change' }
          ]
        }
    };
  },
  mounted() {
    this.page(); //当页面加载完成后,发送异步请求,获取数据
    findAll().then((result) => {
      this.deptList = result.data.data;
    });
  },
  
  methods: {
    // 查询分页数据
    page() {
      page(
        this.searchEmp.name,
        this.searchEmp.gender,
        this.beginTime,
        this.endTime,
        this.currentPage,
        this.pageSize
      ).then((res) => {
        this.totalCount = res.data.data.total;
        this.tableData = res.data.data.rows;
      });
    },

    // 复选框选中后执行的方法
    handleSelectionChange(val) {
      this.multipleSelection = val;
    },

    // 查询方法
    onSubmit() {
      this.currentPage = 1;
      this.page();
    },

    clear(){
      this.searchEmp = {name: "", gender: ""};
      this.beginTime = "",
      this.endTime = "";
      this.entrydate = "";
      this.page();
    },
    
    // 添加数据
    save(formName) {
      //校验表单
      this.$refs[formName].validate((valid) => {
          if (valid) {
            let operator;
            if (this.emp.id) {//修改
              operator = update(this.emp);
            } else { //新增
              operator = add(this.emp);
            }
            operator.then((resp) => {
              if (resp.data.code == 1) {
                this.dialogVisible = false;
                this.page();
                this.$message({ message: "恭喜你,保存成功", type: "success" });
                this.emp = { image: "" };
              } else {
                this.$message.error(resp.data.msg);
              }
            });
          }
      });
    },
    update(id) {
      //1. 打开窗口
      this.dialogVisible = true;
      //2. 发送请求

      selectById(id).then((result) => {
        if (result.data.code == 1) {
          this.emp = result.data.data;
          this.emp;
        }
      });
    },

    
    //分页
    handleSizeChange(val) {
      // 重新设置每页显示的条数
      this.pageSize = val;
      this.page();
    },

    handleCurrentChange(val) {
      // 重新设置当前页码
      this.currentPage = val;
      this.page();
    },


    //删除员工信息
    deleteById(id){
      this.$confirm("此操作将删除该数据, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
          //2. 发送AJAX请求
          deleteById(id).then((resp) => {
            if (resp.data.code == 1) {
              //删除成功
              this.$message.success("恭喜你,删除成功");
              this.page();
            } else {
              this.$message.error(resp.data.msg);
            }
          });
      }).catch(() => {
          //用户点击取消按钮
          this.$message.info("已取消删除");
        });
    },


    // 批量删除员工信息
    deleteByIds() {
      // 弹出确认提示框
      this.$confirm("此操作将删除该数据, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
          //用户点击确认按钮
          //1. 创建id数组, 从 this.multipleSelection 获取即可
          for (let i = 0; i < this.multipleSelection.length; i++) {
            this.selectedIds[i] = this.multipleSelection[i].id;
          }

          //2. 发送AJAX请求
          deleteById(this.selectedIds).then((resp) => {
            if (resp.data.code == "1") {
              //删除成功
              this.$message.success("恭喜你,删除成功");
              this.page();
            } else {
              this.$message.error(resp.data.msg);
            }
          });
      }).catch(() => {
          //用户点击取消按钮
          this.$message.info("已取消删除");
        });
    },

    cancel(formName){
      this.dialogVisible = false;
      this.$refs[formName].resetFields();
    },

    //文件上传相关
    handleAvatarSuccess(res, file) {
      this.emp.image = res.data;
    },
    beforeAvatarUpload(file) {
      const isJPG = file.type === "image/jpeg";
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isJPG) {
        this.$message.error("上传头像图片只能是 JPG 格式!");
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!");
      }
      return isJPG && isLt2M;
    },
  },


  watch: {
    entrydate(val) {
      if (val && val.length >= 2) {
        this.beginTime = val[0];
        this.endTime = val[1];
      } else {
        this.beginTime = "";
        this.endTime = "";
      }
    },
  },
};
</script>
<style>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 100px;
  height: 100px;
  line-height: 100px;
  text-align: center;
}
.avatar {
  width: 100px;
  height: 100px;
  display: block;
}
</style>
3.2.2、后端代码实现

实体类

复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Short gender;
    private String image;
    private Short job;
    private LocalDate entrydate;
    private Integer deptId;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

controller

复制代码
@RestController
@RequestMapping("/emps")
@Slf4j
public class EmpController {

    @Autowired
    private EmpService service;

    /**
     * 分页查询
     * @param page
     * @param pageSize
     * @param name
     * @param gender
     * @param begin
     * @param end
     * @return
     */
    @GetMapping
    public Result page(@RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize,
                       String name, Short gender,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
        log.info("员工分页查询 page = {},pageSize = {}", page, pageSize);
        PageBean pageBean = service.page(page,pageSize,name,gender,begin,end);
        return Result.success(pageBean);
    }

    /**
     * 删除
     * @param ids
     * @return
     */
    @DeleteMapping("/{ids}")
    public Result deleteByIds(@PathVariable("ids") List<Integer> ids){
        log.info("删除 ids = {}", ids);
        service.deleteByIds(ids);
        return Result.success();
    }

    /**
     * 新增员工
     * @param emp
     * @return
     */
    @PostMapping
    public Result save(@RequestBody Emp emp){
        log.info("新增员工 {}", emp);
        service.save(emp);
        return Result.success();
    }

    /**
     * 根据id查询员工
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Result selectById(@PathVariable Integer id){
        log.info("根据id查询员工");
        Emp emp = service.selectById(id);
        return Result.success(emp);
    }

    @PutMapping
    public Result update(@RequestBody Emp emp){
        log.info("修改员工{}",emp);
        service.update(emp);
        return Result.success();
    }

    @GetMapping("/list")
    public Result selectAll(){
        log.info("查询全部员工");
        List<Emp> list = service.selectAll();
        return Result.success(list);
    }
}

service层

复制代码
public interface EmpService {
    /**
     * 员工分页查询
     *
     * @param page
     * @param pageSize
     * @param name
     * @param gender
     * @param begin
     * @param end
     * @return
     */
    PageBean page(Integer page, Integer pageSize, String name, Short gender, LocalDate begin, LocalDate end);

    /**
     * 删除
     * @param ids
     */
    void deleteByIds(List<Integer> ids);

    /**
     * 新增员工
     * @param emp
     */
    void save(Emp emp);

    /**
     * 根据id查询员工
     * @param id
     * @return
     */
    Emp selectById(Integer id);

    /**
     * 修改员工
     * @param emp
     */
    void update(Emp emp);

    /**
     * 查询全部员工
     * @return
     */
    List<Emp> selectAll();

    /**
     * 员工登录
     * @param emp
     * @return
     */
    Emp login(Emp emp);
}


@Service
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;


    @Override
    public PageBean page(Integer page, Integer pageSize, String name, Short gender, LocalDate begin, LocalDate end) {
        //设置分页参数
        PageHelper.startPage(page,pageSize);
        //进行查询
        List<Emp> list = empMapper.list(name,gender,begin,end);

        //获取分页结果
        Page<Emp> empPage = (Page<Emp>) list;

        return new PageBean(empPage.getTotal(),empPage.getResult());
    }

    /**
     * 删除
     *
     * @param ids
     */
    @Override
    public void deleteByIds(List<Integer> ids) {
        empMapper.deleteByIds(ids);
    }

    /**
     * 新增员工
     *
     * @param emp
     */
    @Override
    public void save(Emp emp) {
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.save(emp);
    }

    /**
     * 根据id查询员工
     *
     * @param id
     * @return
     */
    @Override
    public Emp selectById(Integer id) {
        Emp emp = empMapper.selectById(id);
        return emp;
    }

    /**
     * 修改员工
     *
     * @param emp
     */
    @Override
    public void update(Emp emp) {
        emp.setUpdateTime(LocalDateTime.now());
        empMapper.update(emp);
    }

    /**
     * 查询全部员工
     *
     * @return
     */
    @Override
    public List<Emp> selectAll() {
        List<Emp> list = empMapper.selectAll();
        return list;
    }

    /**
     * 员工登录
     *
     * @param emp
     * @return
     */
    @Override
    public Emp login(Emp emp) {
        return empMapper.getEmpByUsernameAndPassword(emp);
    }
}

mapper层

复制代码
@Mapper
public interface EmpMapper {
    /**
     * 查询部门下是否有员工
     * @param id
     * @return
     */
    @Select("select  count(*) from emp where dept_id = #{id}")
    Integer selectEmpByDeptId(Integer id);
    /**
     * 查询
     */
//    @Select("select * from emp")
    List<Emp> list(@Param("name") String name,@Param("gender") Short gender,
                   @Param("begin") LocalDate begin,@Param("end") LocalDate end);

    /**
     * 删除
     * @param ids
     */
    void deleteByIds(@Param("ids") List<Integer> ids);

    /**
     * 新增员工
     * @param emp
     */
    @Insert("insert into emp values (null,#{username},#{password},#{name},#{gender}" +
            ",#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")
    void save(Emp emp);

    /**
     * 根据id查询员工
     * @param id
     * @return
     */
    @Select("select * from emp where id = #{id}")
    Emp selectById(Integer id);

    /**
     * 修改员工
     * @param emp
     */
    @Update("update emp set username = #{username},password = #{password}," +
            "name = #{name},gender = #{gender},image = #{image},job = #{job}," +
            "entrydate = #{entrydate},dept_id = #{deptId},create_time = #{createTime}," +
            "emp.update_time = #{updateTime} where id = #{id}")
    void update(Emp emp);

    /**
     * 查询全部员工
     * @return
     */
    @Select("select * from emp")
    List<Emp> selectAll();

    /**
     * 员工性别统计
     * @return
     */
    List<PieChartData> getEmpGenderData();


    /**
     * 员工职位统计
     * @return
     */

    @Select("select (case job
" +
            "            when 1 then '班主任'" +
            "            when 2 then '讲师'" +
            "            when 3 then '学工主管'" +
            "            when 4 then '教研主管'" +
            "            when 4 then '咨询师'" +
            "            else '无' end) as job," +
            "       count(*) as jobcount" +
            " from emp" +
            " group by job")
   List<Map<String,Object>> getEmpJobData();

    /**
     * 员工登录
     * @param emp
     * @return
     */
    @Select("select * from emp where username = #{username} and  password = #{password}")
    Emp getEmpByUsernameAndPassword(Emp emp);
}

mapper.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.tlias.mapper.EmpMapper">


    <delete id="deleteByIds">
        delete from emp
        where id in
        <foreach collection="ids" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </delete>


    <select id="list" resultType="com.tlias.entity.Emp">
        select * from emp
        <where>
            <if test="name != null and name != ''">
                name like concat('%',#{name},'%')
            </if>
            <if test="gender != null">
                and gender = #{gender}
            </if>
            <if test="begin != null and end != null">
                and entrydate between #{begin} and #{end}
            </if>
        </where>
        order by update_time desc
    </select>
    <select id="getEmpGenderData" resultType="com.tlias.entity.PieChartData">
        select if(gender = 1,'男性员工','女性员工') as 'name',count(*) as 'value' from emp group by gender;
    </select>

</mapper>

因为需要涉及到oss文件上传所以我们还需要整合

复制代码
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class aliyunOssProperties {

    private String endpoint;

    private String accessKeyId;

    private String accessKeySecret;

    private String bucketName;
}


@Component
public class aliyunUtils {
    @Autowired
    private aliyunOssProperties properties;

    public String upload(MultipartFile multipartFile) throws IOException {

        String endpoint = properties.getEndpoint();
        String accessKeyId = properties.getAccessKeyId();
        String accessKeySecret = properties.getAccessKeySecret();
        String bucketName = properties.getBucketName();


        //生成上传名称
        String originalFilename = multipartFile.getOriginalFilename();
        String objectName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);

        try {
            InputStream inputStream = multipartFile.getInputStream();
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);

            // 上传文件。
            PutObjectResult result = ossClient.putObject(putObjectRequest);

            return endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "//" +objectName;
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}
3.3、班级管理

开发的班级管理功能包含:

  1. 查询班级
  2. 删除班级
  3. 新增班级
  4. 更新班级
3.3.1、前端核心代码
复制代码
<template>
  <div style="margin-top: 20px; margin: 50px; margin-right: 100px">
    <!-- 按钮 -->
    <el-row>
      <el-button style="float: right" type="primary" @click="dialogFormVisible = true; dept = {}">+ 新增班级</el-button>
    </el-row>
    <br>
    <!-- 数据表格 -->
    <template>
      <el-table highlight-current-row ref="multipleTable" :data="tableData" tooltip-effect="dark" style="width: 100%"
        border>
        <el-table-column type="index" width="100" label="序号" header-align="center" align="center"> </el-table-column>
        <el-table-column prop="name" label="班级名称" header-align="center" align="center"></el-table-column>
        <el-table-column prop="room" label="上课教室" header-align="center" align="center"></el-table-column>

        <el-table-column label="开课时间" header-align="center" align="center">
          <template slot-scope="scope">
            {{ scope.row.beginDate ? scope.row.beginDate.replace('T', ' ') : '' }}
          </template>
        </el-table-column>

        <el-table-column label="结课时间" header-align="center" align="center">
          <template slot-scope="scope">
            {{ scope.row.endDate ? scope.row.endDate.replace('T', ' ') : '' }}
          </template>
        </el-table-column>

        <el-table-column label="创建时间" header-align="center" align="center">
          <template slot-scope="scope">
            {{ scope.row.createTime ? scope.row.createTime.replace('T', ' ') : '' }}
          </template>
        </el-table-column>
        <el-table-column label="最后操作时间" header-align="center" align="center">
          <template slot-scope="scope">
            {{ scope.row.updateTime ? scope.row.updateTime.replace('T', ' ') : '' }}
          </template>
        </el-table-column>

        <!-- <el-table-column prop="masterId" label="教师id" header-align="center" align="center"></el-table-column> -->


        <el-table-column label="操作" width="420" header-a lign="center" align="center">
          <template slot-scope="scope">
            <el-button size="mini" type="primary" plain @click="selectById(scope.row.id)">编辑</el-button>

            <el-button size="mini" type="danger" plain @click="deleteById(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </template>

    <!-- 新建对话框 -->

    <el-dialog title="保存班级" :visible.sync="dialogFormVisible">
      <el-form :model="clazz" :rules="rules" ref="clazz">
        <el-form-item label="班级名称"  prop="name">
          <el-input v-model="clazz.name" placeholder="请输入班级名称" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="教室" prop="room">
          <el-input v-model="clazz.room" placeholder="请输入教室" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="开始日期" prop="beginDate">
          <el-date-picker v-model="clazz.beginDate" type="date" placeholder="选择开始日期"></el-date-picker>
        </el-form-item>
        <el-form-item label="结束日期" prop="endDate">
          <el-date-picker v-model="clazz.endDate" type="date" placeholder="选择结束日期"></el-date-picker>
        </el-form-item>
        <!-- <el-form-item label="班主任ID" prop="masterId">
          <el-input v-model="clazz.masterId" placeholder="请输入班主任ID" autocomplete="off"></el-input>
        </el-form-item> -->

      </el-form>

      <div slot="footer" class="dialog-footer">
        <el-button @click="cancel('clazz')">取 消</el-button>
        <el-button type="primary" @click="save('clazz')">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import { findAll, add, update, deleteById, selectById } from "@/api/clazz.js";

export default {
  data() {
    return {
      formLabelWidth: "120px",
      dialogFormVisible: false, //控制对话框是否可见
      clazz: {
        name: "",
        room: "",
        beginDate: "",
        endDate: "",
        masterId: ""
      },
      tableData: [],
      rules: {
        name: [
          { required: true, message: '请输入部门名称', trigger: 'blur' },
          { min: 2, max: 20, message: '长度在 2 到 10 个字符', trigger: 'blur' }
        ],
        room: [
          { required: true, message: '请输入教室', trigger: 'blur' }
        ],
        beginDate: [
          { required: true, message: '请选择开始日期', trigger: 'change' }
        ],
        endDate: [
          { required: true, message: '请选择结束日期', trigger: 'change' }
        ],
        // masterId: [
        //   { required: true, message: '请输入班主任ID', trigger: 'blur' }
        // ]
      }
    };
  },

  methods: {

    //删除部门
    deleteById(id) {
      this.$confirm("确认删除?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        deleteById(id).then((result) => {
          if (result.data.code == 1) {
            this.$message({
              message: "恭喜你,删除成功",
              type: "success",
            });
          } else {
            this.$message.error(result.data.msg);
          }
          //重新查询数据
          this.init();
        });
      }).catch(() => {
        this.$message({
          type: "info",
          message: "已取消删除",
        });
      });
    },

    //根据ID查询部门 -- 回显
    selectById(id) {
      this.dialogFormVisible = true;
      selectById(id).then((result) => {
        this.clazz = result.data.data;
      });
    },

    //保存方法
    save(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          let operator;
          if (this.clazz.id) {
            operator = update(this.clazz); // 修改
          } else {
            operator = add(this.clazz); //添加 
          }

          operator.then((result) => {
            if (result.data.code == 1) {
              //修改成功
              this.$message.success("恭喜你,保存成功");
              //重新查询数据
              this.init();
              // 关闭新建窗口
              this.dialogFormVisible = false;
              // 清空模型数据
              this.clazz = {};
            } else {
              this.$message.error(result.data.msg);
            }
          });
        }
      })
    },

    //初始化 - 查询全部
    init() {
      findAll().then((result) => {
        console.log(result);
        if (result.data.code == 1) {
          this.tableData = result.data.data;
        }
      });
    },

    cancel(formName) {
      this.dialogFormVisible = false;
      this.$refs[formName].resetFields();
    }
  },
  mounted() {
    //当页面加载完成后自动执行。
    this.init();
  },
};
</script>
<style></style>
3.3.2、后端代码实现

实体类

复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Clazz {
    private Integer id;
    private String name;
    private String room;
    private LocalDate beginDate;
    private LocalDate endDate;
    private Integer masterId;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

controller层

复制代码
@RestController
@RequestMapping("/clazzs")
@Slf4j
public class ClazzController {

    @Autowired
    private ClazzService clazzService;

    /**
     * 查询所有
     * @return
     */
    @GetMapping
    public Result findAll(){
        log.info("findAll...");
        List<Clazz> list = clazzService.findAll();
        return Result.success(list);
    }

    /**
     * 新增班级
     * @param clazz
     * @return
     */
    @PostMapping
    public Result save(@RequestBody Clazz clazz){
        log.info("save{}",clazz);
        clazzService.save(clazz);
        return Result.success();
    }

    /**
     * 根据id删除班级
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public Result deleteById(@PathVariable Long id){
        log.info("deleteById...{}",id);
        clazzService.deleteById(id);
        return Result.success();
    }

    /**
     * 根据id查询班级
     */
    @GetMapping("/{id}")
    public Result findById(@PathVariable Long id){
        log.info("findById...{}",id);
        Clazz clazz = clazzService.findById(id);
        return Result.success(clazz);
    }

    /**
     * 更新班级
     * @param clazz
     * @return
     */
    @PutMapping
    public Result update(@RequestBody Clazz clazz){
        log.info("update...{}",clazz);
        clazzService.update(clazz);
        return Result.success();
    }
}

service

复制代码
public interface ClazzService {
    /**
     * 查询所有班级
     * @return
     */
    List<Clazz> findAll();

    /**
     * 新增班级
     * @param clazz
     */
    void save(Clazz clazz);

    /**
     * 根据id删除班级
     * @param id
     */
    void deleteById(Long id);

    /**
     * 根据id查询班级
     * @param id
     * @return
     */
    Clazz findById(Long id);

    /**
     * 更新班级
     * @param clazz
     */
    void update(Clazz clazz);
}


@Service
public class ClazzServiceImpl implements ClazzService {

    @Autowired
    private ClazzMapper clazzMapper;

    /**
     * 查询所有班级
     *
     * @return
     */
    @Override
    public List<Clazz> findAll() {
        List<Clazz> clazzList = clazzMapper.findAll();
        return clazzList;
    }

    /**
     * 新增班级
     *
     * @param clazz
     */
    @Override
    public void save(Clazz clazz) {
        clazz.setCreateTime(LocalDateTime.now());
        clazz.setUpdateTime(LocalDateTime.now());
        clazzMapper.save(clazz);
    }

    /**
     * 根据id删除班级
     *
     * @param id
     */
    @Override
    public void deleteById(Long id) {
        clazzMapper.deleteById(id);
    }

    /**
     * 根据id查询班级
     *
     * @param id
     * @return
     */
    @Override
    public Clazz findById(Long id) {
        Clazz clazz = clazzMapper.findById(id);
        return clazz;
    }

    /**
     * 更新班级
     *
     * @param clazz
     */
    @Override
    public void update(Clazz clazz) {
        clazz.setUpdateTime(LocalDateTime.now());
        clazzMapper.update(clazz);
    }
}

mapper层

复制代码
@Mapper
public interface ClazzMapper {
    /**
     * 查询所有班级
     * @return
     */
    @Select("select * from clazz")
    List<Clazz> findAll();

    /**
     * 新增班级
     * @param clazz
     */
    @Insert("insert into clazz values (null,#{name},#{room},#{beginDate},#{endDate},#{masterId}," +
            "#{createTime},#{updateTime})")
    void save(Clazz clazz);

    /**
     * 根据id删除班级
     * @param id
     */
    @Delete("delete from clazz where id = #{id}")
    void deleteById(Long id);

    /**
     * 根据id查询班级
     * @param id
     * @return
     */
    @Select("select * from clazz where id = #{id};")
    Clazz findById(Long id);

    /**
     * 更新班级
     * @param clazz
     */
    @Update("update clazz set name = #{name},room = #{room},begin_date = #{beginDate}," +
            "end_date = #{endDate},master_id = #{masterId} where id = #{id}")
    void update(Clazz clazz);
}
3.4、学生管理

我们可以把员工管理功能分为:

  1. 分页查询
  2. 带条件的分页查询
  3. 删除员工
  4. 新增员工
  5. 修改员工
3.4.1、前端核心代码
复制代码
<template>
  <div class="app-container">

    <!--搜索表单-->
    <el-form :inline="true" :model="searchStudent" class="demo-form-inline">
      <el-form-item label="姓名">
        <el-input
          v-model="searchStudent.name"
          placeholder="请输入学生姓名"
        ></el-input>
      </el-form-item>
      <el-form-item label="性别">
        <el-select v-model="searchStudent.gender" placeholder="请选择">
          <el-option label="男" value="1"></el-option>
          <el-option label="女" value="2"></el-option>
        </el-select>
      </el-form-item>


      <el-form-item>
        <el-button type="primary" @click="onSubmit">查询</el-button>
         <el-button type="info" @click="clear">清空</el-button>
      </el-form-item>
    </el-form>



    <!--按钮-->
    <el-row>
      <el-button type="danger" size="medium" @click="deleteByIds">- 批量删除</el-button>
      <el-button type="primary" size="medium" @click="dialogVisible = true; student = { image: ''};" >+ 新增学生</el-button>
    </el-row>

    <!--添加数据对话框表单-->
    <el-dialog ref="form" title="编辑学生" :visible.sync="dialogVisible" width="30%">
      <el-form :model="student" :rules="rules" ref="student" label-width="80px" size="mini">
        <el-form-item label="姓名" prop="name">
          <el-input v-model="student.name"></el-input>
        </el-form-item>
        <el-form-item label="学号"  prop="no">
          <el-input v-model="student.no"></el-input>
        </el-form-item>

        <el-form-item label="性别"  prop="gender">
          <el-select v-model="student.gender" placeholder="请选择" style="width:100%" >
             <el-option
              v-for="item in genderList"
              :key="item.value"
              :label="item.name"
              :value="item.id"
            />
          </el-select>
        </el-form-item>

        <el-form-item label="手机"  prop="phone">
          <el-input v-model="student.phone"></el-input>
        </el-form-item>

        <el-form-item label="班级"  prop="phone">
          <el-input v-model="student.clazzId"></el-input>
        </el-form-item>

        <!-- <el-form-item label="头像">
          <el-upload
            class="avatar-uploader"
            action="/api/upload"
            :headers="token"
            name="image"
            :show-file-list="false"
            :on-success="handleAvatarSuccess"
            :before-upload="beforeAvatarUpload"
          >
            <img v-if="emp.image" :src="emp.image" class="avatar" />
            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
          </el-upload>
        </el-form-item> -->

        <!-- <el-form-item label="职位">
          <el-select v-model="emp.job" placeholder="请选择" value-key="emp.job" style="width:100%">
            <el-option
              v-for="item in jobList"
              :key="item.value"
              :label="item.name"
              :value="item.id"
            />
          </el-select>
        </el-form-item> -->
<!-- 
        <el-form-item label="入职日期">
          <el-date-picker
            v-model="emp.entrydate"
            clearable
            type="date"
            value-format="yyyy-MM-dd" 
            placeholder="选择日期"
            size="small"
            style="width:100%"
          ></el-date-picker>
        </el-form-item> -->

        <!-- <el-form-item label="班级">
          <el-select v-model="student.clazzId" placeholder="请选择" style="width:100%">
            <el-option
              v-for="item in clazzList"
              :key="item.value"
              :label="item.name"
              :value="item.id"
            />
          </el-select>
        </el-form-item> -->

        <el-form-item>
          <el-button type="primary" @click="save('student')">提交</el-button>
          <el-button @click="cancel('student')">取消</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>

    <br>

    <!--表格-->
    <template>
      <el-table :data="tableData" style="width: 100%" border @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55"  align="center"></el-table-column>
        <el-table-column  prop="name"  label="姓名"  align="center"></el-table-column>
        <!-- <el-table-column prop="image" label="头像" align="center">
          <template slot-scope="{ row }">
            <el-image style="width: auto; height: 40px; border: none; cursor: pointer" :src="row.image"></el-image>
          </template>
        </el-table-column> -->
        <el-table-column  prop="no"  label="学号"  align="center"></el-table-column>
        <el-table-column  prop="phone"  label="手机"  align="center"></el-table-column>
        <el-table-column align="center" label="性别">
          <template slot-scope="scope">
            <span style="margin-right: 10px">
            {{scope.row.gender == "1" ? "男" : "女"}}</span>
          </template>
        </el-table-column>

        <!-- <el-table-column  prop="degree"  label="学年"  align="center"></el-table-column> -->
     <!-- <el-table-column  prop="violationCount"  label="违纪总数"  align="center"></el-table-column> -->
        <!-- <el-table-column  prop="violationScore"  label="违纪分数"  align="center"></el-table-column> -->
        <el-table-column  prop="clazzId"  label="班级"  align="center"></el-table-column>

        <!-- <el-table-column align="center" label="职位">
          <template slot-scope="scope">
            <span style="margin-right: 10px" v-if="scope.row.job == 1">班主任</span>
            <span style="margin-right: 10px" v-if="scope.row.job == 2">讲师</span>
            <span style="margin-right: 10px" v-if="scope.row.job == 3">学工主管</span>
            <span style="margin-right: 10px" v-if="scope.row.job == 4">教研主管</span>
          </template>
        </el-table-column> -->

        <el-table-column align="center" label="入学日期">
          <template slot-scope="scope">
            {{ scope.row.createTime ? scope.row.createTime.replace('T', ' ') : '' }}
          </template>
        </el-table-column>
        <!-- <el-table-column align="center" label="最后操作时间">
          <template slot-scope="scope">
            {{scope.row.updateTime ? scope.row.updateTime.replace('T',' '):''}}
          </template>
        </el-table-column> -->

        <el-table-column align="center" label="操作">
          <template slot-scope="scope">
            <el-button type="primary" size="small" @click="update(scope.row.id)">编辑</el-button>
            <el-button type="danger" size="small" @click="deleteById(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </template>

    <br>

    <!--分页工具条-->
    <el-pagination
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :background="background"
      :current-page="currentPage"
      :page-sizes="[5, 10, 15, 20]"
      :page-size="5"
      layout="total, sizes, prev, pager, next, jumper"
      :total="totalCount">
    </el-pagination>
  </div>
</template>

<script>
import { page, add, update, deleteById, selectById , findAll} from "@/api/student.js";
// import { findAll } from "@/api/dept.js";
import { getToken } from '@/utils/auth';

export default {
  data() {
    return {
      background: true,
      // 每页显示的条数
      pageSize: 5,
      // 总记录数
      totalCount: 100,
      // 当前页码
      currentPage: 1,
      // 添加数据对话框是否展示的标记
      dialogVisible: false,
      // 模型数据
      searchStudent: {
        name: "",
        gender: ""
      },
      student: {
        name: "",
        no: "",
        gender: "",
        phone: "",
        degree: "",
        violationCount: "",
        violationScore: "",
        clazzId: ""
      },
      clazzList: [],
      genderList: [{id: 1,name: "男"},{id: 2,name: "女"}],

      selectedIds: [],
      // 复选框选中数据集合
      multipleSelection: [],
      // 表格数据
      tableData: [],
      token: {token: getToken()},
      rules: {
          name: [
            {required: true, message: '请输入姓名', trigger: 'blur' },
            {min: 2, max: 10, message: '长度在 2 到 10 个字符', trigger: 'blur' }
          ],
          gender: [
            {required: true, message: '请选择性别', trigger: 'change' }
          ]
        }
    };
  },

  mounted() {
    this.page(); //当页面加载完成后,发送异步请求,获取数据
    findAll().then((result) => {
      this.deptList = result.data.data;
    });
  },
  
  methods: {
    // 查询分页数据
    page() {
      page(
        this.searchStudent.name,
        this.searchStudent.gender,
        // this.beginTime,
        // this.endTime,
        this.currentPage,
        this.pageSize
      ).then((res) => {
        this.totalCount = res.data.data.total;
        this.tableData = res.data.data.rows;
      });
    },

    // 复选框选中后执行的方法
    handleSelectionChange(val) {
      this.multipleSelection = val;
    },

    // 查询方法
    onSubmit() {
      this.currentPage = 1;
      this.page();
    },

    clear(){
      this.searchStudent = {name: "", gender: ""};
      this.page();
    },
    
    // 添加数据
    save(formName) {
      //校验表单
      this.$refs[formName].validate((valid) => {
          if (valid) {
            let operator;
            if (this.student.id) {//修改
              operator = update(this.student);
            } else { //新增
              operator = add(this.student);
            }
            operator.then((resp) => {
              if (resp.data.code == 1) {
                this.dialogVisible = false;
                this.page();
                this.$message({ message: "恭喜你,保存成功", type: "success" });
                this.student = { image: "" };
              } else {
                this.$message.error(resp.data.msg);
              }
            });
          }
      });
    },


    update(id) {
      //1. 打开窗口
      this.dialogVisible = true;
      //2. 发送请求

      selectById(id).then((result) => {
        if (result.data.code == 1) {
          this.student = result.data.data;
          this.student;
        }
      });
    },

    
    //分页
    handleSizeChange(val) {
      // 重新设置每页显示的条数
      this.pageSize = val;
      this.page();
    },

    handleCurrentChange(val) {
      // 重新设置当前页码
      this.currentPage = val;
      this.page();
    },


    //删除员工信息
    deleteById(id){
      this.$confirm("此操作将删除该数据, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
          //2. 发送AJAX请求
          deleteById(id).then((resp) => {
            if (resp.data.code == 1) {
              //删除成功
              this.$message.success("恭喜你,删除成功");
              this.page();
            } else {
              this.$message.error(resp.data.msg);
            }
          });
      }).catch(() => {
          //用户点击取消按钮
          this.$message.info("已取消删除");
        });
    },


    // 批量删除员工信息
    deleteByIds() {
      // 弹出确认提示框
      this.$confirm("此操作将删除该数据, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
          //用户点击确认按钮
          //1. 创建id数组, 从 this.multipleSelection 获取即可
          for (let i = 0; i < this.multipleSelection.length; i++) {
            this.selectedIds[i] = this.multipleSelection[i].id;
          }

          //2. 发送AJAX请求
          deleteById(this.selectedIds).then((resp) => {
            if (resp.data.code == "1") {
              //删除成功
              this.$message.success("恭喜你,删除成功");
              this.page();
            } else {
              this.$message.error(resp.data.msg);
            }
          });
      }).catch(() => {
          //用户点击取消按钮
          this.$message.info("已取消删除");
        });
    },

    cancel(formName){
      this.dialogVisible = false;
      this.$refs[formName].resetFields();
    },

    //文件上传相关
    handleAvatarSuccess(res, file) {
      this.student.image = res.data;
    },
    beforeAvatarUpload(file) {
      const isJPG = file.type === "image/jpeg";
      const isLt2M = file.size / 1024 / 1024 < 2;
      if (!isJPG) {
        this.$message.error("上传头像图片只能是 JPG 格式!");
      }
      if (!isLt2M) {
        this.$message.error("上传头像图片大小不能超过 2MB!");
      }
      return isJPG && isLt2M;
    },
  },


  watch: {
    entrydate(val) {
      if (val && val.length >= 2) {
        this.beginTime = val[0];
        this.endTime = val[1];
      } else {
        this.beginTime = "";
        this.endTime = "";
      }
    },
  },
};
</script>
<style>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 100px;
  height: 100px;
  line-height: 100px;
  text-align: center;
}
.avatar {
  width: 100px;
  height: 100px;
  display: block;
}
</style>
3.4.2、后端代码实现

实体类

复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private String no;
    private Short gender;
    private String phone;
    private Short degree;
    private Short violationCount;
    private Short violationScore;
    private Short clazzId;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}


@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentData {
    private List categoryList;
    private List dataList;
}

controller

复制代码
package com.tlias.controller;

import com.github.pagehelper.Page;
import com.tlias.entity.PageBean;
import com.tlias.entity.Student;
import com.tlias.result.Result;
import com.tlias.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/students")
@Slf4j
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping
    public Result page(String name, Short gender,
                       @RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize){
        log.info("员工分页查询 page={},pageSize={}", page, pageSize);
        PageBean pageBean = studentService.page(page,pageSize,name,gender);
        return Result.success(pageBean);
    }

    /**
     * 查询全部
     * @return
     */
    @GetMapping("/list")
    public Result findAll(){
        log.info("findAll...");
        List<Student> studentList = studentService.findAll();
        return Result.success(studentList);
    }

    /**
     * 新增学生
     * @param student
     * @return
     */
    @PostMapping()
    public Result save(@RequestBody Student student){
        log.info("save student...{}",student);
        studentService.save(student);
        return Result.success();
    }

    /**
     * 根据id查询学生
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public Result findById(@PathVariable Long id){
        log.info("findById...{}",id);
        Student student = studentService.findById(id);
        return Result.success(student);
    }

    /**
     * 修改学生
     * @param student
     * @return
     */
    @PutMapping
    public Result update(@RequestBody Student student){
        log.info("update student...{}",student);
        studentService.update(student);
        return Result.success();
    }

    /**
     * 删除
     * @param ids
     * @return
     */
    @DeleteMapping("/{ids}")
    public Result deleteById(@PathVariable List<Integer> ids){
        log.info("deleteById...{}",ids);
        studentService.deleteById(ids);
        return Result.success();
    }

}

service

复制代码
public interface StudentService {
    /**
     * 分页查询
     * @param page
     * @param pageSize
     * @param name
     * @param gender
     * @return
     */
    PageBean page(Integer page, Integer pageSize, String name, Short gender);

    /**
     * 查询全部
     * @return
     */
    List<Student> findAll();

    /**
     * 新增学生
     * @param student
     */
    void save(Student student);

    /**
     * 根据id查询学生
     * @param id
     * @return
     */
    Student findById(Long id);

    /**
     * 修改学生
     * @param student
     */
    void update(Student student);

    /**
     * 根据id删除
     * @param ids
     */
    void deleteById(List<Integer> ids);
}



@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private StudentMapper studentMapper;

    /**
     * 分页查询
     *
     * @param page
     * @param pageSize
     * @param name
     * @param gender
     * @return
     */
    @Override
    public PageBean page(Integer page, Integer pageSize, String name, Short gender) {
        PageHelper.startPage(page,pageSize);
        List<Student> list = studentMapper.page(name,gender);
        Page<Student> studentPage = (Page<Student>) list;
        return new PageBean(studentPage.getTotal(),studentPage.getResult());
    }

    /**
     * 查询全部
     *
     * @return
     */
    @Override
    public List<Student> findAll() {
        List<Student> studentList = studentMapper.findAll();
        return studentList;
    }

    /**
     * 新增学生
     *
     * @param student
     */
    @Override
    public void save(Student student) {
        student.setCreateTime(LocalDateTime.now());
        student.setUpdateTime(LocalDateTime.now());
        studentMapper.save(student);
    }

    /**
     * 根据id查询学生
     *
     * @param id
     * @return
     */
    @Override
    public Student findById(Long id) {
        return studentMapper.findById(id);
    }

    /**
     * 修改学生
     *
     * @param student
     */
    @Override
    public void update(Student student) {
        student.setUpdateTime(LocalDateTime.now());
        studentMapper.update(student);
    }

    /**
     * 根据id删除
     *
     * @param ids
     */
    @Override
    public void deleteById(List<Integer> ids) {
        studentMapper.deleteById(ids);
    }

}

mapper

复制代码
@Mapper
public interface StudentMapper {
    @Select("SELECT clazz.`name` as 'class',COUNT(student.clazz_id) as 'classcount' " +
            "FROM clazz LEFT JOIN student ON student.clazz_id = clazz.id GROUP BY clazz.`name` ;")
    List<Map<String, Object>> getStudentData();

    /**
     * 分页查询
     * @param name
     * @param gender
     * @return
     */
    List<Student> page(@Param("name") String name,@Param("gender") Short gender);

    /**
     * 查询全部
     * @return
     */
    @Select("select * from student")
    List<Student> findAll();

    /**
     * 新增学生
     * @param student
     */
    @Insert("insert into student(name, no, gender, phone,clazz_id, create_time, update_time) " +
            "value(#{name},#{no},#{gender},#{phone},#{clazzId},#{createTime},#{updateTime})")
    void save(Student student);

    /**
     * 根据id查询学生
     * @param id
     * @return
     */
    @Select("select * from student where id = #{id};")
    Student findById(Long id);

    /**
     * 修改学生
     * @param student
     */
    @Update("update student set name = #{name},no = #{no},gender = #{gender}," +
            "phone = #{phone},clazz_id = #{clazzId} where id = #{id}")
    void update(Student student);

    /**
     * 根据id删除
     * @param ids
     */
    void deleteById(@Param("ids") List<Integer> ids);
}


<?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.tlias.mapper.StudentMapper">

    <delete id="deleteById">
        delete from student where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>
    <select id="page" resultType="com.tlias.entity.Student">
        select * from student
        <where>
            <if test="name != null and name != ''">
                name like concat('%',#{name},'%')
            </if>
            <if test="gender != null">
                and gender != #{gender}
            </if>
        </where>
    </select>
</mapper>
3.5、数据统计
3.5.1、前端核心代码
复制代码
<template>
  <div class="chart-container">

    <div  style="width: 50%; ">
      <h1>员工性别统计</h1> <br>
      <div id="myChart1"  style="width: 100%; height: 500px"></div>
    </div>

    <div  style="width: 50%; ">
      <h1>员工职位统计</h1> <br>
      <div id="myChart2"  style="width: 100%; height: 500px"></div>
    </div>

  </div>
</template>

<script>
import * as echarts from 'echarts'
import { getGenderData, getJobData } from "@/api/report.js";

export default {
  data() {
    return {
      genderCountList: [],
      jobCategoryList:[],
      jobDataList:[]
    }
  },
  mounted() { 
    this.getGenderData(); //获取员工性别统计数据
    this.getJobData(); //获取员工职位统计数据
  },

  methods: {
    //获取员工性别统计数据
    getGenderData(){
      getGenderData().then((result) => {
          if(result.data.code == 1){
             this.genderCountList = result.data.data;
             
             this.initChart1();
          }
      });
    },

    //获取员工职位统计数据
    getJobData(){
      getJobData().then((result) => {
          if(result.data.code == 1){
             this.jobCategoryList = result.data.data.categoryList;
             this.jobDataList = result.data.data.dataList;

             this.initChart2();
          }
      });
    },

    initChart1() {
      // 获取图表容器, 创建图表实例
      let myChart = echarts.init(document.querySelector('#myChart1'));
       
      let option = {
        tooltip: {
          trigger: 'item'
        },
        legend: {
          top: '5%',
          left: 'center'
        },
        series: [{
            name: '员工性别统计',
            type: 'pie',
            radius: ['40%', '70%'],
            avoidLabelOverlap: false,
            itemStyle: {
              borderRadius: 10,
              borderColor: '#fff',
              borderWidth: 2
            },
            label: {
              show: false,
              position: 'center'
            },
            emphasis: {
              label: {
                show: true,
                fontSize: 40,
                fontWeight: 'bold'
              }
            },
            labelLine: {
              show: false
            },
            data: this.genderCountList
          }
        ]
      };

      // 绘制图表
      myChart.setOption(option);
    },


    initChart2() {
      // 获取图表容器, 创建图表实例
      let myChart = echarts.init(document.querySelector('#myChart2'));
       
      let option = {
        xAxis: {
          type: 'category',
          data: this.jobCategoryList
        },
        yAxis: {
          type: 'value'
        },
        series: [
          {
            data: this.jobDataList,
            type: 'bar'
          }
        ]
      };
      
      // 绘制图表
      myChart.setOption(option);
    }
  }
};
</script>

<style>
  h1 {
    text-align: center;
  }

  .chart-container {
    display: flex;
  }
  .chart-container > div {
    flex: 1;
  }
</style>
3.5.2、后端代码实现

实体类

复制代码
@Data
public class PieChartData {
    private String name;
    private String value;
}


@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentData {
    private List categoryList;
    private List dataList;
}

controller

复制代码
@RestController
@RequestMapping("/report")
@Slf4j
public class ReportController {

    @Autowired
    private ReportService service;

    @GetMapping("/empGenderData")
    public Result getEmpGenderData(){
        log.info("员工性别信息信息");
        List<PieChartData> list = service.getEmpGenderData();
        return Result.success(list);
    }

    @GetMapping("/empJobData")
    public Result getEmpJobData(){
        log.info("员工职位信息");
        JobData jobData  = service.getEmpJobData();
        return Result.success(jobData);
    }

    @GetMapping("/studentData")
    public Result getStudentData(){
        log.info("学员人数统计");
        StudentData studentData = service.getStudentData();
        return Result.success(studentData);
    }
}

service层

复制代码
public interface ReportService {
    /**
     * 员工性别统计
     * @return
     */
    List<PieChartData> getEmpGenderData();

    /**
     * 员工职位信息统计
     * @return
     */
    JobData getEmpJobData();

    /**
     * 学员统计
     * @return
     */
    StudentData getStudentData();
}


@Service
public class ReportServiceImpl implements ReportService {

    @Autowired
    private EmpMapper empMapper;

    @Autowired
    private StudentMapper studentMapper;

    /**
     * 员工性别统计
     *
     * @return
     */
    @Override
    public List<PieChartData> getEmpGenderData() {
        List<PieChartData> list = empMapper.getEmpGenderData();
        return list;
    }

    /**
     * 员工职位信息统计
     *
     * @return
     */
    @Override
    public JobData getEmpJobData() {
        List<Map<String,Object>> mapList = empMapper.getEmpJobData();
        System.out.println(mapList);
        if (!CollectionUtils.isEmpty(mapList)) {
            List<Object> categoryList = mapList.stream().map(map -> {
                return map.get("job");
            }).collect(Collectors.toList());

            List<Object> dataList = mapList.stream().map(map -> {
                return map.get("jobcount");
            }).collect(Collectors.toList());

            JobData jobData = new JobData();
            jobData.setCategoryList(categoryList);
            jobData.setDataList(dataList);
            System.out.println(jobData);
            return jobData;
        }

        return null;
    }

    /**
     * 学员统计
     *
     * @return
     */
    @Override
    public StudentData getStudentData() {
        List<Map<String,Object>> mapList = studentMapper.getStudentData();
        System.out.println(mapList);

        if (!CollectionUtils.isEmpty(mapList)){
            List<Object> categoryList = mapList.stream().map(map -> {
                return map.get("class");
            }).collect(Collectors.toList());

            List<Object> dataList = mapList.stream().map(map -> {
                return map.get("classcount");
            }).collect(Collectors.toList());

            return new StudentData(categoryList,dataList);
        }
        return null;
    }
}
3.6、登录功能

我们已经实现了部门管理、员工管理的基本功能,但是大家会发现,我们并没有登录,就直接访问到了后台。 这是不安全的,所以要做登录认证。 最终我们要实现的效果就是用户必须登录之后,才可以访问后台系统中的功能

JWT令牌最典型的应用场景就是登录认证:

  1. 在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。
  2. 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。
  3. 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。

在JWT登录认证的场景中我们发现,整个流程当中涉及到两步操作:

  1. 在登录成功之后,要生成令牌。
  2. 每一次请求当中,要接收令牌并对令牌进行校验。
3.6.1、前端核心代码
复制代码
<template>
  <div class="login-container">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">

      <div class="title-container">
        <h3 class="title">Tlias智能学习辅助系统</h3>
      </div>

      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="username"
          v-model="loginForm.username"
          placeholder="Username"
          name="username"
          type="text"
          tabindex="1"
          auto-complete="on"
        />
      </el-form-item>

      <el-form-item prop="password">
        <span class="svg-container">
          <svg-icon icon-class="password" />
        </span>
        <el-input
          :key="passwordType"
          ref="password"
          v-model="loginForm.password"
          :type="passwordType"
          placeholder="Password"
          name="password"
          tabindex="2"
          auto-complete="on"
          @keyup.enter.native="handleLogin"
        />
        <span class="show-pwd" @click="showPwd">
          <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
        </span>
      </el-form-item>

      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>

    </el-form>
  </div>
</template>

<script>
import { validUsername } from '@/utils/validate'
import { login } from '@/api/user'
import { setToken } from '@/utils/auth'
export default {
  name: 'Login',
  data() {

    //用户名校验规则
    const validateUsername = (rule, value, callback) => {
      if (!validUsername(value)) {
        callback(new Error('请输入正确的用户名'))
      } else {
        callback()
      }
    }

    //用户名校验规则
    const validatePassword = (rule, value, callback) => {
      if (value.length < 6) {
        callback(new Error('密码长度至少为6位'))
      } else {
        callback()
      }
    }

    //数据模型
    return {
      loginForm: {
        username: 'jinyong',
        password: '123456'
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
      },
      loading: false,
      passwordType: 'password',
      redirect: undefined
    }
  },

  methods: {
    //展示密码
    showPwd() {
      if (this.passwordType === 'password') {
        this.passwordType = ''
      } else {
        this.passwordType = 'password'
      }
      this.$nextTick(() => {
        this.$refs.password.focus()
      })
    },
    
    //登录方法
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          
          //调用登录后端接口
          login(this.loginForm).then((result) => {
            console.log(result)
            if (result.data.code == 1) {
              setToken(result.data.data);
              console.log('login success');
              this.$router.push('/');
            } else {
              this.$message.error(result.data.msg);
              this.loading = false
            }
          });
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }
  }
}
</script>

<style lang="scss">
/* 修复input 背景不协调 和光标变色 */
/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */

$bg:#283443;
$light_gray:#fff;
$cursor: #fff;

@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
  .login-container .el-input input {
    color: $cursor;
  }
}

/* reset element-ui css */
.login-container {
  .el-input {
    display: inline-block;
    height: 47px;
    width: 85%;

    input {
      background: transparent;
      border: 0px;
      -webkit-appearance: none;
      border-radius: 0px;
      padding: 12px 5px 12px 15px;
      color: $light_gray;
      height: 47px;
      caret-color: $cursor;

      &:-webkit-autofill {
        box-shadow: 0 0 0px 1000px $bg inset !important;
        -webkit-text-fill-color: $cursor !important;
      }
    }
  }

  .el-form-item {
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(0, 0, 0, 0.1);
    border-radius: 5px;
    color: #454545;
  }
}
</style>

<style lang="scss" scoped>
$bg:#2d3a4b;
$dark_gray:#889aa4;
$light_gray:#eee;

.login-container {
  min-height: 100%;
  width: 100%;
  background-color: $bg;
  overflow: hidden;

  .login-form {
    position: relative;
    width: 520px;
    max-width: 100%;
    padding: 160px 35px 0;
    margin: 0 auto;
    overflow: hidden;
  }

  .tips {
    font-size: 14px;
    color: #fff;
    margin-bottom: 10px;

    span {
      &:first-of-type {
        margin-right: 16px;
      }
    }
  }

  .svg-container {
    padding: 6px 5px 6px 15px;
    color: $dark_gray;
    vertical-align: middle;
    width: 30px;
    display: inline-block;
  }

  .title-container {
    position: relative;

    .title {
      font-size: 26px;
      color: $light_gray;
      margin: 0px auto 40px auto;
      text-align: center;
      font-weight: bold;
      font-family: '楷体';
    }
  }

  .show-pwd {
    position: absolute;
    right: 10px;
    top: 7px;
    font-size: 16px;
    color: $dark_gray;
    cursor: pointer;
    user-select: none;
  }
}
</style>
3.6.2、后端代码实现

创建工具类,实现jwt令牌的发放和解析

复制代码
public class JwtUtils {

    private static String signKey = "jwtHelloWorld";
    private static Long expire = 60 * 60 * 1000L;

    public static String generateToken(Map<String, Object> claims) {
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return token;
    }

    public static Claims parseToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(token)
                .getBody();
        return claims;
    }
}

创建拦截器,进行拦截

复制代码
/**
 * 自定义拦截器
 */
@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //1、获取请求url
        String url = request.getRequestURI().toString();

        //3、获取请求头中的token
        String token = request.getHeader("token");
        if (!StringUtils.hasLength(token)){ //不存在
            log.info("获取令牌为空");
            Result notLogin = Result.error("NOT_LOGIN");
            String json = JSONObject.toJSONString(notLogin);
            response.getWriter().write(json);
            return false;
        }

        //4、解析token,失败则未登录
        try {
            Claims claims = JwtUtils.parseToken(token);
        } catch (Exception e) {
            log.info("解析令牌错误");
            Result notLogin = Result.error("NOT_LOGIN");
            String json = JSONObject.toJSONString(notLogin);
            response.getWriter().write(json);
            return false;
        }
        //5、放行
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}

controller层

复制代码
@RestController
@Slf4j
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        log.info("员工登录{}",emp);
        Emp empLogin = empService.login(emp);
        if (empLogin != null){
            Map<String, Object> map = new HashMap<>();
            map.put("id", empLogin.getId());
            map.put("username", empLogin.getUsername());
            String token = JwtUtils.generateToken(map);
            return Result.success(token);
        }
        return Result.error("查无此用户");
    }
}
相关推荐
李昊哲小课2 小时前
Spring Boot SSE 实时推送教程
spring boot·websocket·flux·sseemitter
狗头大军之江苏分军2 小时前
《潮水漫过十七岁》开学了
后端
苏三说技术3 小时前
如何看待GPT-6在UP主众测中碾压夺冠?它是现在最强大模型吗?
后端
mldong3 小时前
一份 JSON,一条能跑的审批流:把报销流程送上工作流引擎
后端·架构
wno7043 小时前
Spring Boot WebFlux增删改查
java·spring boot·后端
Captaincc3 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
aramae4 小时前
模拟实现strlen()函数 (C语言)
c语言·开发语言·后端
行百里er5 小时前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring cloud·监控
IT_陈寒5 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
分支预测失败5 小时前
RISC-V 时间子系统深度专题:mtime 访问路径、SBI 定时器与 Linux tickless 协同
后端