当前位置: 首页 > news >正文

SpringBoot + vue 管理系统

SpringBoot + vue 管理系统

文章目录
      • SpringBoot + vue 管理系统
        • 1、成品效果展示
        • 2、项目准备
        • 3、项目开发
          • 3.1、部门管理
            • 3.1.1、前端核心代码
            • 3.1.2、后端代码实现
          • 3.2、员工管理
            • 3.2.1、前端核心代码
            • 3.2.2、后端代码实现
          • 3.3、班级管理
            • 3.3.1、前端核心代码
            • 3.3.2、后端代码实现
          • 3.4、学生管理
            • 3.4.1、前端核心代码
            • 3.4.2、后端代码实现
          • 3.5、数据统计
            • 3.5.1、前端核心代码
            • 3.5.2、后端代码实现
          • 3.6、登录功能
            • 3.6.1、前端核心代码
            • 3.6.2、后端代码实现
1、成品效果展示

SpringBoot + vue 管理系统

2、项目准备

环境准备

image-20240714203052876

步骤:

  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>

创建项目工程目录结构

image-20240714203628782

配置文件application.yml

server:port: 8080mybatis:mapper-locations: classpath:mappers/*xmltype-aliases-package: com.tlias.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truespring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/tliasusername: rootpassword: 123456servlet:multipart:max-file-size: 10MBmax-request-size: 100MBpagehelper:reasonable: truealiyun:oss:endpoint: https://oss-cn-beijing.aliyuncs.comaccessKeyId: you_acesskeyaccessKeySecret: you_accessKeySecretbucketName: 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项目

image-20240714203803145

3、项目开发

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

3.1、部门管理

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

  1. 查询部门
  2. 删除部门
  3. 新增部门
  4. 更新部门
3.1.1、前端核心代码
<template><div style="margin-top: 20px; margin: 50px; margin-right: 100px"><!-- 按钮 --><el-row><el-buttonstyle="float: right"type="primary"@click="dialogFormVisible = true; dept={}">+ 新增部门</el-button></el-row><br><!-- 数据表格 --><template><el-tablehighlight-current-rowref="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-buttonsize="mini"type="primary"plain@click="selectById(scope.row.id)">编辑</el-button><el-buttonsize="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 {@Autowiredprivate DeptService service;/*** 查询部门* @return*/@GetMappingpublic 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*/@PostMappingpublic 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*/@PutMappingpublic 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 {@Autowiredprivate DeptMapper deptMapper;@Autowiredprivate EmpMapper empMapper;/*** 查询部门** @return*/@Overridepublic List<Dept> getDeptList() {List<Dept> list =  deptMapper.getDeptList();return list;}/*** 删除部门** @param id*/@Overridepublic void deleteDeptById(Integer id) {Integer result = empMapper.selectEmpByDeptId(id);if (result < 1 ){throw new RuntimeException("不能删除部门,部门下面存在员工");}deptMapper.deleteDeptById(id);}/*** 新增部门** @param dept*/@Overridepublic void save(Dept dept) {//添加修改时间dept.setCreateTime(LocalDateTime.now());dept.setUpdateTime(LocalDateTime.now());deptMapper.save(dept);}/*** 根据id查询部门** @param id* @return*/@Overridepublic Dept selectDeptById(Integer id) {Dept dept =  deptMapper.selectDeptById(id);return dept;}/*** 修改部门信息** @param dept*/@Overridepublic 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-inputv-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-pickerv-model="entrydate"clearablevalue-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-optionv-for="item in genderList":key="item.value":label="item.name":value="item.id"/></el-select></el-form-item><el-form-item label="头像"><el-uploadclass="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-optionv-for="item in jobList":key="item.value":label="item.name":value="item.id"/></el-select></el-form-item><el-form-item label="入职日期"><el-date-pickerv-model="emp.entrydate"clearabletype="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-optionv-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 {@Autowiredprivate EmpService service;/*** 分页查询* @param page* @param pageSize* @param name* @param gender* @param begin* @param end* @return*/@GetMappingpublic 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*/@PostMappingpublic 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);}@PutMappingpublic 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 {@Autowiredprivate EmpMapper empMapper;@Overridepublic 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*/@Overridepublic void deleteByIds(List<Integer> ids) {empMapper.deleteByIds(ids);}/*** 新增员工** @param emp*/@Overridepublic void save(Emp emp) {emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());empMapper.save(emp);}/*** 根据id查询员工** @param id* @return*/@Overridepublic Emp selectById(Integer id) {Emp emp = empMapper.selectById(id);return emp;}/*** 修改员工** @param emp*/@Overridepublic void update(Emp emp) {emp.setUpdateTime(LocalDateTime.now());empMapper.update(emp);}/*** 查询全部员工** @return*/@Overridepublic List<Emp> selectAll() {List<Emp> list = empMapper.selectAll();return list;}/*** 员工登录** @param emp* @return*/@Overridepublic 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 empwhere 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 {@Autowiredprivate 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 {@Autowiredprivate ClazzService clazzService;/*** 查询所有* @return*/@GetMappingpublic Result findAll(){log.info("findAll...");List<Clazz> list = clazzService.findAll();return Result.success(list);}/*** 新增班级* @param clazz* @return*/@PostMappingpublic 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*/@PutMappingpublic 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 {@Autowiredprivate ClazzMapper clazzMapper;/*** 查询所有班级** @return*/@Overridepublic List<Clazz> findAll() {List<Clazz> clazzList = clazzMapper.findAll();return clazzList;}/*** 新增班级** @param clazz*/@Overridepublic void save(Clazz clazz) {clazz.setCreateTime(LocalDateTime.now());clazz.setUpdateTime(LocalDateTime.now());clazzMapper.save(clazz);}/*** 根据id删除班级** @param id*/@Overridepublic void deleteById(Long id) {clazzMapper.deleteById(id);}/*** 根据id查询班级** @param id* @return*/@Overridepublic Clazz findById(Long id) {Clazz clazz = clazzMapper.findById(id);return clazz;}/*** 更新班级** @param clazz*/@Overridepublic 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-inputv-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-optionv-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-uploadclass="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-optionv-for="item in jobList":key="item.value":label="item.name":value="item.id"/></el-select></el-form-item> -->
<!-- <el-form-item label="入职日期"><el-date-pickerv-model="emp.entrydate"clearabletype="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-optionv-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 {@Autowiredprivate StudentService studentService;@GetMappingpublic 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*/@PutMappingpublic 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 {@Autowiredprivate StudentMapper studentMapper;/*** 分页查询** @param page* @param pageSize* @param name* @param gender* @return*/@Overridepublic 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*/@Overridepublic List<Student> findAll() {List<Student> studentList = studentMapper.findAll();return studentList;}/*** 新增学生** @param student*/@Overridepublic void save(Student student) {student.setCreateTime(LocalDateTime.now());student.setUpdateTime(LocalDateTime.now());studentMapper.save(student);}/*** 根据id查询学生** @param id* @return*/@Overridepublic Student findById(Long id) {return studentMapper.findById(id);}/*** 修改学生** @param student*/@Overridepublic void update(Student student) {student.setUpdateTime(LocalDateTime.now());studentMapper.update(student);}/*** 根据id删除** @param ids*/@Overridepublic 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 {@Autowiredprivate 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 {@Autowiredprivate EmpMapper empMapper;@Autowiredprivate StudentMapper studentMapper;/*** 员工性别统计** @return*/@Overridepublic List<PieChartData> getEmpGenderData() {List<PieChartData> list = empMapper.getEmpGenderData();return list;}/*** 员工职位信息统计** @return*/@Overridepublic 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*/@Overridepublic 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-inputref="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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1、获取请求urlString url = request.getRequestURI().toString();//3、获取请求头中的tokenString 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;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");}
}

controller层

@RestController
@Slf4j
public class LoginController {@Autowiredprivate 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("查无此用户");}
}

相关文章:

SpringBoot + vue 管理系统

SpringBoot vue 管理系统 文章目录 SpringBoot vue 管理系统 1、成品效果展示2、项目准备3、项目开发 3.1、部门管理 3.1.1、前端核心代码3.1.2、后端代码实现 3.2、员工管理 3.2.1、前端核心代码3.2.2、后端代码实现 3.3、班级管理 3.3.1、前端核心代码3.3.2、后端代码实现 …...

Python语法核心架构与核心知识点:从理论到实践

一、Python的核心设计哲学 Python以“简洁优雅”为核心理念&#xff0c;遵循以下原则&#xff1a; # Zen of Python&#xff08;输入 import this 可查看&#xff09; >>> import this The Zen of Python, by Tim Peters ... Simple is better than complex. Readab…...

OpenHarmony子系统开发 - 编译构建Kconfig可视化配置指导

OpenHarmony子系统开发 - 编译构建Kconfig可视化配置指导 概述 功能简介 该功能基于Kconfiglib与Kconfig实现&#xff0c;方便用户个性化配置OpenHarmony产品子系统部件。 基于Kconfig实现的可视化配置功能具有以下优点&#xff1a; 能直观且全面地展示软件的部件选项。可…...

管中窥豹数字预失真(DPD)

管中窥豹数字预失真&#xff08;DPD&#xff09; 数字预失真在通信领域发挥了巨大的作用&#xff0c;对提高功放效率、改善误码率起了不可忽略的作用&#xff0c;广泛运用与通信、雷达等各种领域。但是对于普通用户&#xff0c;它显得及其高深神秘。今天就用这个短文&#xff…...

spring-boot-starter和spring-boot-starter-web的关联

maven的作用是方便jar包的管理&#xff0c;所以每一个依赖都是对应着相应的一个或者一些jar包&#xff0c;从网上看到很多对spring-boot-starter的描述就是“这是Spring Boot的核心启动器&#xff0c;包含了自动配置、日志和YAML。”没看太明白&#xff0c;所参与的项目上也一直…...

梯度计算中常用的矩阵微积分公式

标量对向量求导的常用数学公式 设标量函数 y f ( x ) y f(\boldsymbol{x}) yf(x)&#xff0c;其中 x ( x 1 , x 2 , ⋯ , x n ) T \boldsymbol{x} (x_1, x_2, \cdots, x_n)^{\rm T} x(x1​,x2​,⋯,xn​)T是一个 n n n维列向量。标量 y y y对向量 x \boldsymbol{x} x的导数…...

vim 编写/etc/docker/daemon.json文件时,E212: 无法打开并写入文件

目录 问题描述 解决方法 1、创建/etc/docker目录 2、打开/etc/docker目录 3、创建daemon.json文件 4、vim 编辑daemon.json文件 问题描述 当我们输入代码&#xff1a;vim /etc/docker/daemon.json时&#xff0c;报E212: 无法打开并写入文件错误&#xff0c;如下图 vim /e…...

http 模块的概念及作用详细介绍

目录 1. http 模块概述 2. http 模块的作用 3. http 服务器代码示例 运行代码 4. http 客户端代码示例 运行代码 5. 总结 1. http 模块概述 http 模块是 Node.js 内置的核心模块之一&#xff0c;它用于创建 HTTP 服务器和客户端&#xff0c;支持处理 HTTP 请求和响应。…...

重生之我在学Vue--第5天 Vue 3 路由管理(Vue Router)

重生之我在学Vue–第5天 Vue 3 路由管理&#xff08;Vue Router&#xff09; 文章目录 重生之我在学Vue--第5天 Vue 3 路由管理&#xff08;Vue Router&#xff09;前言一、路由配置与导航1.1 什么是 Vue Router&#xff1f;1.2 安装 Vue Router1.3 基本路由配置步骤代码示例 1…...

常见排序算法深度评测:从原理到10万级数据实战

常见排序算法深度评测&#xff1a;从原理到10万级数据实战 摘要 本文系统解析冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序和基数排序8种经典算法&#xff0c;通过C语言实现10万随机数排序并统计耗时。测试显示&#xff1a;快速排序综合性能最优&…...

搭建BOA服务器

BOA服务器是嵌入式常用的服务器类型&#xff0c;嵌入式程序作为后端时候如果想配合网页进行显示&#xff0c;利用BOA服务器搭建网络界面是不错的选择 首先下载boa官方安装包 Boa Webserver 下载后传输到Ubuntu随便文件夹&#xff0c;解压 tar -xvf boa-0.94.13.tar.gz 进入…...

JSON.parse(JSON.stringify())深拷贝不会复制函数

深拷贝 是指创建一个新对象&#xff0c;并递归地复制原对象中所有层级的属性和值&#xff0c;从而确保新对象与原对象完全独立 深拷贝的实现方法 &#xff1a; 1. 使用 JSON.parse(JSON.stringify()) 函数会被忽略复制&#xff0c;比如&#xff0c;下面的对象的forma…...

debug_unpack_ios failed: Exception: Failed to codesign 解决方案(亲测有效)

debug_unpack_ios failed: Exception: Failed to codesign 解决方案&#xff08;亲测有效&#xff09; 背景原因解决方案tipsresult 背景 执行flutter doctor全通过后run项目依然报错 原因 1、检查flutter Mac的flutter项目在哪个文件夹内 2、检查flutter Sdk在哪个文件夹内 …...

Docker篇

1.docker环境搭建&#xff1a; 1.1软件仓库的配置rhel9&#xff1a; #cd/etc/yum.repos.d #vim docker.repo [docker] namedocker-ce baseurlhttps://mirrors.aliyun.com/docker-ce/linux/rhel/9/x86_64/stable gpgcheck0 1.2安装docker并且启动服务 yum install -y dock…...

【Linux】基本命令

目录 &#x1f525;一、基础命令 1.sudo su&#xff08;superuser do&#xff09; 2.pwd&#xff08;print working directory&#xff09; 3.ls&#xff08;list&#xff09; 4.cd&#xff08;change directory&#xff09; 5.mkdir&#xff08;make directory&#xff…...

win10电脑鼠标速度突然变的很慢?

电脑鼠标突然变很慢&#xff0c;杀毒检测后没问题&#xff0c;鼠标设置也没变&#xff0c;最后发现可能是误触鼠标的“DPI”调节键。 DPI调节键在鼠标滚轮下方&#xff0c;再次点击即可恢复正常鼠标速度。 如果有和-的按键&#xff0c;速度变快&#xff0c;-速度变慢。 图源&…...

前端(vue)学习笔记(CLASS 3):生命周期工程化开发入门

1、生命周期 Vue生命周期&#xff1a;一个Vue实例从创建到销毁的整个过程 生命周期四个阶段&#xff1a;创建、挂载、更新、销毁 1、创建阶段&#xff1a;响应式数据 2、挂载阶段&#xff1a;渲染模板 3、更新阶段&#xff1a;数据修改、更新视图&#xff08;执行多次&…...

Python写一个查星座的小程序,适合初学者练手——字典和if语句练习

一、界面预览 二、完整代码 # 导入必要的库 import tkinter as tk from tkinter import ttk # 导入ttk模块用于更现代的控件 from PIL import Image, ImageTk # 用于处理图片 import os # 用于文件路径操作class ZodiacApp:def __init__(self, root):self.root rootself.r…...

云上特权凭证攻防启示录:从根账号AK泄露到安全体系升级的深度实践

事件全景:一场持续17分钟的云上攻防战 2025年3月9日15:39,阿里云ActionTrail日志突现异常波纹——根账号acs:ram::123456789:root(已脱敏)从立陶宛IP(164.92.91.227)发起高危操作。攻击者利用泄露的AccessKey(AK)在17分钟内完成侦察→提权→持久化攻击链,完整操作序列…...

blazemeter工具使用--用于自动生成jmeter脚本并进行性能测试

1、安装blazemeter&#xff08;网上有很多详情的教程&#xff09; 2、开始录制&#xff1a;设置号你的文件名称后开始录制 3、录制完成后保存为jmeter(jmx)文件 4、在jmeter中打开文件 5、添加一个后置处理器&#xff1a;查看结果树&#xff0c;后运行看看能否成功&#xf…...

TypeScript系列07-类型声明文件

在现代前端开发中&#xff0c;TypeScript已成为提升代码质量和开发体验的利器。对于React和React Native项目&#xff0c;合理利用类型声明文件不仅能提供更好的智能提示和类型检查&#xff0c;还能显著减少运行时错误。本文将深入探讨类型声明文件的编写与使用。 1. 声明文件…...

【社交+陪玩服务】全场景陪玩系统源码 小程序+H5双端 社群互动+即时点单+搭建教程

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 找搭子系统源码&#xff0c;圈子源码、社交源码、陪玩源码&#xff0c;亲测 100% 可用&#xff0c;跟市场上卖 1w的那款一模一样&#xff0c;功能非常齐全&#xff0c;企业级别运营的…...

【Java并发】【synchronized】适合初学者体质入门的synchronized

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f4da;欢迎订阅专栏…...

经销商管理系统选型解析:8款产品详评

本文主要介绍了以下8款经销商管理系统&#xff1a;1.纷享销客&#xff1b; 2.用友T6经销商管理系统&#xff1b; 3.金蝶经销商管理系统&#xff1b; 4.鼎捷经销商管理系统&#xff1b; 5.浪潮经销商管理系统&#xff1b; 6.销售易&#xff1b; 7.SAP Business One Distributor …...

基于STM32的逻辑分析仪

目录 制约性能因素协议命令下位机回复CMD_ID的回复CMD_METADATA命令的回复上报的采样数 设置使用开源软件PulseView设置操作1&#xff0e;设置采样数2&#xff0e;设置采样频率3.使能或禁止通道4.设置通道的触发条件 实现准备汇编指令精确测量时间 程序C语言初实现采集数据上报…...

mapbox高阶,结合threejs(threebox)添加管道

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️threebox Tube静态对象二、🍀使用thr…...

使用PySpark进行大数据处理与机器学习实战指南

1. 技术介绍 1.1 PySpark概述 PySpark是Apache Spark的Python API&#xff0c;它结合了Python的易用性和Spark的分布式计算能力&#xff0c;能够高效处理PB级数据集。Spark基于内存计算的特性使其比传统Hadoop MapReduce快10-100倍&#xff0c;支持流处理、SQL查询、机器学习…...

注意力机制-学习

1. 自注意力机制 句子&#xff1a;"The animal didnt cross the street because it was too tired." 在这个句子中&#xff0c;“it”指的是“animal”. 在自注意力机制中&#xff0c;当我们处理到“it”这个词时&#xff0c;模型会计算“it”与句子中其他所有词的…...

华纳云:香港服务器出现带宽堵塞一般是什么原因?

香港服务器带宽堵塞的原因通常可以归结为以下几个方面&#xff1a; 1. 机房带宽资源有限 (1)香港本地国际带宽成本高 香港的国际出口带宽昂贵&#xff0c;机房通常提供的带宽较小(如默认1Mbps-5Mbps)&#xff0c;如果多个用户争抢有限的带宽&#xff0c;就会出现网络拥堵、丢包…...

我们在开发时,什么时候用到虚函数和纯虚函数?

在曾经学习面向对象的概念上&#xff0c;对虚函数和纯虚函数的区别&#xff0c;我们都会止于这样的理解层面&#xff1a;虚函数是用于被子类可继承可重写的函数&#xff0c;而纯虚函数是子类继承后就必须重写的函数。但是在开发工作上&#xff0c;却有很多开发者是没法彻底参透…...

Python:lambda结合if判断,内置函数与拆包

lambda的应用&#xff1a; #a,b是形参&#xff0c;比较大小 complambda a,b:"a<b" if a<b else "a>b" print(comp(8,5)) 特点&#xff1a;lambda只能实现简单的逻辑&#xff0c;如果逻辑复杂且代码量较大&#xff0c;不建议使用lambda降低代码的…...

【Java学习】包装类

面向对象系列九 包装类变量 一、装箱 1.实例化包装对象 2.静态缓存池 3.写法 二、拆箱 包装类变量 每个基本数据类型都有对应的基本类型的包装类变量&#xff0c;将基本数据类型通过对应的包装类对象载入着进入到类与对象面向对象体系 一、装箱 Integer.valueOf(int) —…...

从新手到专家:嵌入式代码空间优化技巧

目录 一、基本概念 内存开销 优化目标 二、存储管理 数据类型选择 变量作用域 结构体对齐 三、代码结构 循环优化 函数调用 分支语句 查表法的动态扩展 查表法与算法结合 指针替代数组(续) 指针与动态内存结合 指针与函数指针结合 常量优化(续) 常量传播与…...

本地部署Navidrome个人云音乐平台随时随地畅听本地音乐文件

文章目录 前言1. 安装Docker2. 创建并启动Navidrome容器3. 公网远程访问本地Navidrome3.1 内网穿透工具安装3.2 创建远程连接公网地址3.3 使用固定公网地址远程访问 前言 今天我要给大家安利一个超酷的私有化音乐神器——Navidrome&#xff01;它不仅让你随时随地畅享本地音乐…...

AI自动化编程初探

先说vscodeclinemodelscope方案&#xff0c;后面体验trae或者cursor再写写其它的。vscode和trae方案目前来说是免费的&#xff0c;cursor要用claud需要付费&#xff0c;而且不便宜&#xff0c;当然效果可能是最好的。 vscode方案&#xff0c;我的经验是最好在ubuntu上&#xff…...

KUKA机器人:智能制造的先锋力量

在科技日新月异的今天&#xff0c;自动化和智能化已成为推动制造业转型升级的重要引擎。作为全球领先的智能、资源节约型自动化解决方案供应商&#xff0c;KUKA机器人在这一浪潮中扮演着举足轻重的角色。本文将带您深入了解KUKA机器人的发展现状&#xff0c;探索其在智能制造领…...

2021 年 9 月青少年软编等考 C 语言六级真题解析

目录 T1. 合法出栈序列思路分析T2. 奇怪的括号思路分析T3. 区间合并思路分析T4. 双端队列思路分析T1. 合法出栈序列 题目链接:SOJ D1110 给定一个由不同小写字母构成的长度不超过 8 8 8 的字符串 x x x,现在要将该字符串的字符依次压入栈中,然后再全部弹出。要求左边的字…...

java快速输入

带解析 package Month3; import java.util.*; import java.io.*; public class Demo100843 {static class Reader{BufferedReader bf new BufferedReader(new InputStreamReader(System.in));StringTokenizer st new StringTokenizer("");String next() throws IO…...

C/C++蓝桥杯算法真题打卡(Day3)

一、P8598 [蓝桥杯 2013 省 AB] 错误票据 - 洛谷 算法代码&#xff1a; #include<bits/stdc.h> using namespace std;int main() {int N;cin >> N; // 读取数据行数unordered_map<int, int> idCount; // 用于统计每个ID出现的次数vector<int> ids; …...

AntV_G6实现UI树

UI 树的实现 背景 目前需要实现一个 UI 树&#xff0c;用于展示设备树&#xff0c;以及设备树中设备的属性。与树状列表不同&#xff0c;UI 树需要有特定的交互方式&#xff0c;支持边以及当前节点的点击事件。 实现效果【复制到.html文件夹就看见了】 总体效果 点击节点效果…...

【0016】Python数据类型-不可变集合详解

如果你觉得我的文章写的不错&#xff0c;请关注我哟&#xff0c;请点赞、评论&#xff0c;收藏此文章&#xff0c;谢谢&#xff01; 本文内容体系结构如下&#xff1a; 在Python中&#xff0c;除了我们常见的可变集合&#xff08;Set&#xff09;外&#xff0c;还有一种不可…...

学习资料电子版 免费下载的网盘网站(非常全!)

我分享一个私人收藏的电子书免费下载的网盘网站&#xff08;学习资料为主&#xff09;&#xff1a; link3.cc/sbook123 所有资料都保存在网盘了&#xff0c;直接转存即可&#xff0c;非常的便利&#xff01; 包括了少儿&#xff0c;小学&#xff0c;初中&#xff0c;中职&am…...

ROS2学习笔记2

前言 本篇文章属于ROS2humble的学习笔记&#xff0c;来源于B站鱼香ROSup主。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 《ROS 2机器人开发从入门到实践》课程介绍_哔哩哔哩_bilibili …...

为什么大模型网站使用 SSE 而不是 WebSocket?

在大模型网站&#xff08;如 ChatGPT、Claude、Gemini 等&#xff09;中&#xff0c;前端通常使用 EventSource&#xff08;Server-Sent Events, SSE&#xff09; 来与后端对接&#xff0c;而不是 WebSocket。这是因为 SSE 更适合类似流式文本生成的场景。下面我们详细对比 SSE…...

利用阿里云Atlas地区选择器与Plotly.js实现数据可视化与交互

在数据科学与可视化领域&#xff0c;交互式图表和地图应用越来越成为数据分析和展示的重要手段。本文将介绍如何结合阿里云Atlas地区选择器与Plotly.js&#xff0c;创建动态交互式的数据可视化应用。 一、阿里云Atlas地区选择器简介 阿里云Atlas是阿里云的一款数据可视化产品…...

尚硅谷TS快速入门笔记(个人笔记用)

TypeScript 快速上手 &#x1faa9; 禹神&#xff1a;三小时快速上手TypeScript&#xff0c;TS速通教程_哔哩哔哩_bilibili ⼀、TypeScript 简介 TypeScript 由微软开发,是基于 JavaScript 的⼀个扩展语⾔。 TypeScript 包含了 JavaScript 的所有内容,即: TypeScript 是 Jav…...

python: DDD+ORM using oracle 21c

sql script: create table GEOVINDU.School --創建表 ( SchoolId char(5) NOT NULL, -- SchoolName nvarchar2(500) NOT NULL, SchoolTelNo varchar(8) NULL, PRIMARY KEY (SchoolId) --#主鍵 );create table GEOVINDU.Teacher ( TeacherId char(5) NOT NULL , TeacherFirstNa…...

KidneyTalk-open系统,RAG在医疗场景的真实落地:用于解决肾脏疾病的医疗问答问题

如何在保护隐私的前提下,本地部署大型语言模型(LLMs),以支持肾脏疾病的医学决策支持。难点包括:云端LLMs的数据泄露风险、本地部署的复杂性、通用LLMs在医学知识整合方面的不足、检索增强系统在医学文档处理和临床可用性方面的挣扎。Med-PaLM 2和MedFound在医学问答和临床…...

flask-定时任务

文章目录 前言一、APScheduler是什么二、APScheduler 主要功能&#xff1a;三、主要组成部分&#xff1a;四、典型使用场景&#xff1a;五、具体使用1.安装 APScheduler2.假设我们有一个需要五分钟请求一次http接口的任务1.定义一个scheduler.py去专门处理定时2.启动文件处理3.…...

6-langchang多模态输入和自定义输出

6-langchang多模态输入和自定义输出 多模态数据输入urlbase64url list工具调用自定义输出: JSON, XML, YAML如何解析 JSON 输出json如何解析xmlYAML解析器多模态数据输入 这里我们演示如何将多模态输入直接传递给模型。我们目前期望所有输入都以与OpenAI 期望的格式相同的格式…...