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、项目准备
环境准备
步骤:
- 准备数据库表
- 创建springboot工程,引入对应的起步依赖(web、mybatis、mysql驱动、lombok)
- 配置文件application.properties中引入mybatis的配置信息,准备对应的实体类
- 准备对应的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、部门管理
开发的部门管理功能包含:
- 查询部门
- 删除部门
- 新增部门
- 更新部门
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、员工管理
我们可以把员工管理功能分为:
- 分页查询
- 带条件的分页查询
- 删除员工
- 新增员工
- 修改员工
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\n" +
" 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、班级管理
开发的班级管理功能包含:
- 查询班级
- 删除班级
- 新增班级
- 更新班级
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、学生管理
我们可以把员工管理功能分为:
- 分页查询
- 带条件的分页查询
- 删除员工
- 新增员工
- 修改员工
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令牌最典型的应用场景就是登录认证:
- 在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。
- 前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。
- 服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。
在JWT登录认证的场景中我们发现,整个流程当中涉及到两步操作:
- 在登录成功之后,要生成令牌。
- 每一次请求当中,要接收令牌并对令牌进行校验。
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("查无此用户");
}
}