首先上图:
(完整源码在最后)
然后具体说一下实现思路吧:
我们首先有一张Excel表格,里面有员工的具体打卡信息:
然后我们需要的就是将Excel表格中的信息导入到数据库中然后通过一系列计算后在前端展示,表中有每一个员工的名字,来公司的时间,离开的时间等等考勤相关打卡信息,岗位,然后最后我们需要输出的是上面那张图的信息,也就是后端需要做排序和薪资计算的处理
公司对于考勤与薪资的具体对应:
考勤规则说明:
-
1:提前打卡:可以计入有效工时,例如:8:40 打卡,那么 8:40 至 9:00 之间的 20 分钟可以计入有效工时。但提前打卡最多只能提前 30 分钟,例如 8:20 打卡,计入有效工时的部分只能是 8:30~9:00 之间的半小时;
-
2:滞后打卡:可以计入有效工时,例如:12:20 打卡,那么 12:00 至 12:20 之间的20 分钟可以计入有效工时。但滞后打卡最多只能滞后 30 分钟,例如 12:40 打卡,计入有效工时的部分只能是 12:00~12:30 之间的半小时;
-
3:迟到遵从下列规则:
a) 5 分钟内不算迟到;
b) 5~10 分钟:扣除 1 小时的时薪;
c) 10~30 分钟:扣除 2 小时时薪;
d) 30 分钟以上:扣除 4 小时时薪(封顶)。
a) 5 分钟内不算早退;
b) 5~10 分钟:扣除 1 小时的时薪;
c) 10~30 分钟:扣除 2 小时时薪;
d) 30 分钟以上:扣除 4 小时时薪(封顶)。
-
5:加班规则:仅计入 20:00~22:00 之间的有效工时,无论提前或滞后都无效。例如 19:30~22:30,那么只计算 20:00~22:00 之间的工时;例如 20:10~21:50,那么也仅计入这段时间内的有效工时。
具体说一下完成的制作步骤
首先我们创建好一个javaweb项目后,初始化一些包,分层:
然后先写好数据库连接的工具层:
package com.ftz.demo.util;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* @author ${范涛之}
* @Description
* @create 2021-12-08 10:08
*/
public class DBUtil {
/**
* 得到配置文件对象
*/
private static Properties properties = new Properties();
static {
try {
//加载配置文件(输入流)
InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
// 通过load()方法将数日六的内容加载到配置文件对象中
properties.load(in);
// 通过配置文件对象的getProperty()方法获取驱动名,拿到驱动名之后加载驱动
Class.forName(properties.getProperty("jdbcName"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接
* @return
*/
public static Connection getConnection(){
Connection connection = null;
try {
//得到数据库链接的相关性息
String dbUrl = properties.getProperty("dbUrl");
String dbName = properties.getProperty("dbName");
String dbPwd = properties.getProperty("dbPwd");
connection = DriverManager.getConnection(dbUrl,dbName,dbPwd);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return connection;
}
/**
* 关闭资源
* @param resultSet
* @param preparedStatement
* @param connection
*/
public static void close(ResultSet resultSet,
PreparedStatement preparedStatement,
Connection connection){
//判断资源对象如果不为空则关闭
try {
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
}catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
db.properties文件:
# 连接MYSQL数据库的配置文件 注:等号的前后不要写空格
# 驱动名
jdbcName=com.mysql.cj.jdbc.Driver
# 数据库连接 ( db_lezijie_note是数据库的名称)
dbUrl=jdbc:mysql://localhost:3306/wagetable?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
# 数据库的连接账号 (账号基本上都是root)
dbName=root
# 数据库的连接密码 (每个人的数据库密码可能不一致,需要修改)
dbPwd=root
然后写po层用户实体类:
package com.ftz.demo.po;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
/**
* @author ${范涛之}
* @Description
* @create 2021-12-11 14:00
*/
@Getter
@Setter
public class User {
/**
* 用户信息
*/
String username;
String post;
/**
* 早中晚工作时间
*/
Date mortimebegin;
Date mortimeoff;
Date afttimebegin;
Date afttimeoff;
Date nigtimebegin;
Date nigtimeoff;
/**
* 早中晚有效工时
*/
double morrealtime;
double aftrealtime;
double nigrealtime;
double totalEffectiveWorkTime;
/**
* 薪资
*/
double wage;
/**
* 薪资排名
*/
String user_rank;
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("User{");
sb.append("username='").append(username).append('\'');
sb.append(", post='").append(post).append('\'');
sb.append(", mortimebegin=").append(mortimebegin);
sb.append(", mortimeoff=").append(mortimeoff);
sb.append(", afttimebegin=").append(afttimebegin);
sb.append(", afttimeoff=").append(afttimeoff);
sb.append(", nigtimebegin=").append(nigtimebegin);
sb.append(", nigtimeoff=").append(nigtimeoff);
sb.append(", morrealtime=").append(morrealtime);
sb.append(", aftrealtime=").append(aftrealtime);
sb.append(", nigrealtime=").append(nigrealtime);
sb.append(", totalEffectiveWorkTime=").append(totalEffectiveWorkTime);
sb.append(", wage=").append(wage);
sb.append(", rank='").append(user_rank).append('\'');
sb.append('}');
return sb.toString();
}
}
这里说一下自己遇到的问题,就是在最好不要使用lombok去生成含有Data属性的实体类,后期容易报错,自己生成就好!
然后我们就去写UserDao:
这一层写的最多了,因为我们整体先去规划一下我们一共需要写多少个方法步骤
-
1:首先是我们要写一个方法,可以去读取Excel表格中的数据,并且我们要创建一个
List<User>
去接受获取的数据然后返回出来,在中间我们会用到一个SimpleDateFormat
的方法用具将读取到的时间转换为格式化的日期类型
/**
* 方法 1:
* readExcel 方法,其功能为接受一个传入的字符串(路径),并将其数据存储到 User 数组中,然后将这个数组返回。
*/
public List<User> readExcel(String path) throws Exception{
List<User> users = new ArrayList<User>();
// 获取工作普
XSSFWorkbook workbook = new XSSFWorkbook(path);
// 获取工作表
XSSFSheet sheet = workbook.getSheetAt(0);
// 获取总行
int rowNumber = sheet.getLastRowNum();
System.out.println("总行数是"+rowNumber);
// 获取单元格
for (int i = 2; i <rowNumber; i++) {
XSSFRow row = sheet.getRow(i);
int cellNum = row.getLastCellNum(); //获取总共列
// 获取单元格内容
User user = new User();
for (int j = 0; j <cellNum; j++) {
Cell cell = row.getCell(j);
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
switch (j) {
//姓名
case 0:
String name = cell.getStringCellValue();
user.setUsername(name);
break;
//岗位
case 1:
String post = cell.getStringCellValue();
user.setPost(post);
break;
//上午
case 2:
String m1 = cell.getStringCellValue();
Date dateM1 = sdf.parse(m1);
user.setMortimebegin(dateM1);
break;
case 3:
String m2 = cell.getStringCellValue();
Date dateM2 = sdf.parse(m2);
user.setMortimeoff(dateM2);
break;
//下午
case 4:
String a1 = cell.getStringCellValue();
Date dateA1 = sdf.parse(a1);
user.setAfttimebegin(dateA1);
break;
case 5:
String a2 = cell.getStringCellValue();
Date dateA2 = sdf.parse(a2);
user.setAfttimeoff(dateA2);
break;
//晚上
case 6:
String n1 = cell.getStringCellValue();
Date dateN1 = sdf.parse(n1);
user.setNigtimebegin(dateN1);
break;
case 7:
String n2 = cell.getStringCellValue();
Date dateN2 = sdf.parse(n2);
user.setNigtimeoff(dateN2);
break;
default:
break;
}
}
users.add(user);
}
return users;
}
-
2:第二个方法就是我们在读取了Excel表格并且返回了数据以后需要做第一个事情就是去计算每一个员工的具体工作时常,我这里首先将所有的特殊的时间点给列出来,然后通过一系列的详细的员工来的时间去做判断,最后也是将计算出来的信息存储到User实体类的对应对象中:
/**
* 方法2:判断用户有效工作时长
* @param users
* @throws ParseException
*/
public void realWorkTime(List<User> users) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
/**
* 上午的有效时间
*/
//上午上班的几个时间点
long t0830 = sdf.parse("8:30").getTime();
long t0900 = sdf.parse("9:00").getTime();
long t0905 = sdf.parse("9:05").getTime();
long t0910 = sdf.parse("9:10").getTime();
long t0930 = sdf.parse("9:30").getTime();
//上午下班的几个时间点
long t1130 = sdf.parse("11:30").getTime();
long t1150 = sdf.parse("11:50").getTime();
long t1155 = sdf.parse("11:55").getTime();
long t1200 = sdf.parse("12:00").getTime();
long t1230 = sdf.parse("12:30").getTime();
//下午上班的几个时间点
long t1300 = sdf.parse("13:00").getTime();
long t1330 = sdf.parse("13:30").getTime();
long t1335 = sdf.parse("13:35").getTime();
long t1340 = sdf.parse("13:40").getTime();
long t1400 = sdf.parse("14:00").getTime();
//下午下班的几个时间点
long t1800 = sdf.parse("18:00").getTime();
long t1820 = sdf.parse("18:20").getTime();
long t1825 = sdf.parse("18:25").getTime();
long t1830 = sdf.parse("18:30").getTime();
long t1900 = sdf.parse("19:00").getTime();
//晚上加班的几个时间点
long t2000 = sdf.parse("20:00").getTime();
long t2200 = sdf.parse("22:00").getTime();
for (User user :users){
long morefftime = 0; //早上额外工作的时间
long atfefftime = 0; //下午额外工作的时间
long nigefftime = 0; //晚上额外工作的时间
long mtime1 = user.getMortimebegin().getTime(); //员工早上到达公司的时间
long mtime2 = user.getMortimeoff().getTime(); //员工早上离开公司的时间
long atime1 = user.getAfttimebegin().getTime(); //员工下午到达公司的时间
long atime2 = user.getAfttimeoff().getTime(); //员工下午离开公司的时间
long ntime1 = user.getNigtimebegin().getTime(); //员工晚上到达公司的时间
long ntime2 = user.getNigtimeoff().getTime(); //员工晚上离开公司的时间
/**
* 上午打卡时间
*/
if (t0830 - mtime1>=0){ // 员工在8:30之前来到公司
morefftime = 30*60*1000;
}else if (t0900-mtime1>=0 || t0830-mtime1<=0){ //员工在8:30-9:00之间来到公司
morefftime = t0900-mtime1;
}else if (t0905-mtime1>=0 || t0900-mtime1<=0){ //员工在9:00-9:05来到公司
morefftime = 0;
}else if (t0910-mtime1>=0 || t0905-mtime1<=0){ //员工在9:05-9:10来到公司
morefftime = -60*60*1000;
}else if (t0930-mtime1>=0 || t0910-mtime1<=0){ //员工在9:10-9:30来到公司
morefftime = -60*60*1000*2;
}else if (t0930-mtime1<=0){ //员工在9:30以后来到公司
morefftime = -60*60*1000*4;
}else if (mtime2-t1130<0){ //员工在11:30之前离开公司
morefftime = -60*60*1000*4;
}else if (t1130-mtime2<0 || t1150-mtime2>0){ //员工在11:30-11:50离开公司
morefftime = -60*60*1000*4;
}else if (t1150-mtime2<0 || t1155-mtime2>0){ //员工在11:50-11:55离开公司
morefftime = -60*60*1000;
}else if (t1155-mtime2<0 || t1200-mtime2>0){ //员工在11:55-12:00离开公司
morefftime = 0;
}else if (t1200-mtime2<0 || t1230-mtime2>0){ //员工在12:00-12:30离开公司
morefftime = mtime2-t1200;
}else if (t1230-mtime2<0){ //员工在12:30以后离开公司
morefftime = 30*60*1000;
}
/**
* 将早上的实际工作时间存储到user中
*/
user.setMorrealtime(morefftime/1000/60/60.0+3);
/**
* 下午打卡时间
*/
if (t1300 - atime1>=0){ // 员工在13:00之前来到公司
atfefftime = 30*60*1000;
}else if (t1300-atime1>=0 || t1330-atime1<=0){ //员工在13:00-13:30之间来到公司
atfefftime = t1330-atime1;
}else if (t1330-atime1>=0 || t1335-atime1<=0){ //员工在13:30-13:35来到公司
atfefftime = 0;
}else if (t1335-atime1>=0 || t1340-atime1<=0){ //员工在13:35-13:40来到公司
atfefftime = -60*60*1000;
}else if (t1340-atime1>=0 || t1400-atime1<=0){ //员工在13:40-14:00来到公司
atfefftime = -60*60*1000*2;
}else if (t1400-atime1<=0){ //员工在14:00以后来到公司
atfefftime = -60*60*1000*4;
}else if (atime2-t1800<0){ //员工在18:00之前离开公司
atfefftime = -60*60*1000*4;
}else if (t1800-atime2<0 || t1820-atime2>0){ //员工在18:00-18:20离开公司
atfefftime = -60*60*1000*4;
}else if (t1820-atime2<0 || t1825-atime2>0){ //员工在18:20-18:25离开公司
atfefftime = -60*60*1000;
}else if (t1825-atime2<0 || t1830-atime2>0){ //员工在18:25-18:30离开公司
atfefftime = 0;
}else if (t1830-atime2<0 || t1900-atime2>0){ //员工在18:30-19:00离开公司
atfefftime = atime2-t1200;
}else if (t1900-atime2<0){ //员工在19:00以后离开公司
atfefftime = 30*60*1000;
}
/**
* 将下午的实际工作时间存储到user中
*/
user.setAftrealtime(atfefftime/1000/60/60.0+5);
/**
* 晚上打卡时间
*/
if (ntime1-t2000>0 || ntime2-t2200<0){ //员工在20:00-22:00加班
nigefftime = ntime2-ntime1;
}else if (ntime1-t2000<0 || ntime2-t2200<0){ //员工在20:00之前来 22:00之前回
nigefftime= ntime2-t2000;
}else if (ntime1-t2000<0 || ntime2-t2200>0){ //员工在20:00之前来 22:00之后回
nigefftime = 2*60*60*1000;
}
/**
* 将晚上的实际工作时间存储到user中
*/
user.setNigrealtime(nigefftime/1000/60/60.0+2);
/**
* 将全天的实际工作时间存储到user中
*/
// long alltime = (long) ((morefftime+atfefftime+nigefftime)/1000/60/60.0+10);
user.setTotalEffectiveWorkTime(((morefftime+atfefftime+nigefftime)/1000/60/60.0)+10);
}
}
说明一下上面由于.gettime获取到的是精确到毫秒的,所以我们遇到的比如说一个小时这种就需要做一些转换:比如一小时就等于:16060*1000
第三个方法是用来去计算薪资根据传过来的User对象:只需要区分一些管理员和普通人:
/**
* 方法 3:
* figureWage 方法,其作用为:接收传入的一个 User 数组,算出其当日的应得工资
*/
public void figureWage(List<User> users){
for (User user :users){
if (user.getTotalEffectiveWorkTime()<=0){
user.setWage(0);
}
else if ("普通员工".equals(user.getPost())){
double wage =50.0*user.getTotalEffectiveWorkTime();
user.setWage((long) wage);
user.setPost("普通员工");
}
else if ("管理员".equals(user.getPost())){
double wage = 80.0*user.getTotalEffectiveWorkTime();
user.setWage((long) wage);
user.setPost("管理员");
}
}
}
第四个方法是去根据接收到的薪资进行排序:
/**
* 方法 4:
* sortByWage 方法,其作用为,获取所有人的工资,并按工资排序,
*/
public void sortByWage(List<User> users){
List<Double> list = new ArrayList<Double>();
for (User user:users){
list.add((double) user.getWage());
}
list.sort((o1,o2)->o2.compareTo(o1));
int rankNum = 3;
for (int i = 0; i <=rankNum; i++) {
for (User user:users){
if (list.get(i) == user.getWage()){
user.setUser_rank("第" + (list.indexOf(list.get(i)) + 1) + "名");
}
}
}
for (int j = rankNum; j < list.size(); j++) {
for (User user : users) {
if (user.getWage() == list.get(j)) {
user.setUser_rank(" ");
}
}
}
}
第五个方法是:在之前我们基本上就是将实体类的所有数据通过依次给填满了,这里就开始进行将完整的数据存入数据库中,只存我们会用到的数据:
/**
* 方法 5:
* addUserToDatabase 方法,其作用为将 User 对象的数据保存至数据库
*/
public void addUserToDatabase(User user) throws Exception{
String sql = "insert into wagetable(username,post,wage,user_rank) values (?,?,?,?)";
Class.forName("com.mysql.cj.jdbc.Driver");
PreparedStatement pr = DBUtil.getConnection().prepareStatement(sql);
pr.setString(1,user.getUsername());
pr.setString(2,user.getPost());
pr.setDouble(3,user.getWage());
pr.setString(4,user.getUser_rank());
pr.executeUpdate();
}
第六个方法:我们此刻数据库当中已经是有了值的,所以这里我们需要写一个可以从数据库中查出我们所需的对象的方法:
/**
* 方法 6:
* userList 方法,其作用为读取数据库,返回一个 User 数组,数组中的对象只包含 名字、应得工资、排名、岗位 4 个信息
*/
public List<User> userList() throws Exception {
List<User> users = new ArrayList<>();
Class.forName("com.mysql.cj.jdbc.Driver");
String sql = "select username,post,wage,user_rank from wagetable;";
PreparedStatement pstmt = DBUtil.getConnection().prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
User user = new User();
user.setPost(rs.getString("post"));
user.setUsername(rs.getString("username"));
user.setWage(rs.getDouble("wage"));
user.setUser_rank(rs.getString("user_rank"));
users.add(user);
}
return users;
}
数据库建表语句:
CREATE TABLE `wagetable` (
`username` varchar(32) NOT NULL,
`post` varchar(8) NOT NULL,
`wage` float NOT NULL,
`user_rank` char(8) DEFAULT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
到这里我们的dao层就“基本”写好啦,当然现在你们会问为什么是基本呀?因为后面其实还会有一个坑!这里大家先记住一下!
接下来就是我们的Conroller层:其实控制层的代码就很好些了,都是一些调用,我这里直接说一下我们会遇到的问题哇!我在上面写道dao层代码基本完成其实还差一步,大家想想我们的dao层是不是有一个将用户添加到数据库的方法,我们现在要调用这个方法就会导致,倘若我们重启了一次项目,然后又运行了一下,数据库就相当于将两次的Excel中的数据都存了一下,这不就一直重复报错了??这里其实是由两种解决办法的1,一种就是通过使用UUID!,那么什么是UUID呢?其实他就是一个标识符,它可以为数据库的主键添加一个特定的标识符用来防止数据的重复!第二种方法比较简单明了,那就是在dao层再写一个0号方法:初始化数据库的方法,也就是我们每运行一次代码就会将数据库中的表先删掉然后再创建再执行,这样就解决了我们上面的困扰了!
UserDao的初始方法:
/**
* 方法 0:
* initialTable 方法
* 其功能为:在数据库中创建表,如果改变存在则先删除表再创建
*/
public void intialTable() throws Exception {
//连接数据库、执行 sql 语句
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//初始化的 sql 语句,如果表存在则 DROP ,不存在则创建
String sql = "DROP TABLE IF EXISTS wagetable;";
String sql2 = "CREATE TABLE wagetable(username VARCHAR(32) NOT NULL,post VARCHAR(8) NOT NULL,wage FLOAT(8) NOT NULL,user_rank CHAR(8),PRIMARY KEY(username));";
PreparedStatement pstmt = DBUtil.getConnection().prepareStatement(sql);
pstmt.execute(sql);
pstmt.execute(sql2);
}
最后写controller:
package com.ftz.demo.controller;
import com.ftz.demo.dao.UserDao;
import com.ftz.demo.po.User;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
/**
* @author ${范涛之}
* @Description
* @create 2021-12-11 23:14
*/
@WebServlet("/user")
public class UserController extends HttpServlet {
protected void function() throws Exception {
String path = "D:\\C桌面\\test\\test.xlsx";
UserDao userDao = new UserDao();
/**
* 初始化数据库
*/
userDao.intialTable();
/**
* 先读取Excel表格数据返回结果
*/
List<User> userlist = userDao.readExcel(path);
userDao.realWorkTime(userlist);
userDao.figureWage(userlist);
userDao.sortByWage(userlist);
/**
* 将数据存入数据库
*/
for (User user : userlist) {
System.out.println(user);
userDao.addUserToDatabase(user);
}
}
@Test
public void userEdit(HttpServletRequest request,HttpServletResponse response) throws Exception {
function();
UserDao userDao = new UserDao();
/**
* 查出需要的数据
*/
List<User> users = userDao.userList();
for (User user:users){
System.out.println(user);
}
request.setAttribute("all",users);
}
@SneakyThrows
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
function();
userEdit(req, resp);
req.getRequestDispatcher("user.jsp").forward(req,resp);
}
}
分割线················································································
接下来是前端代码,前端一共就两部分:一个index的流光按钮,一个用户显示列表:![请添加图片描述](https://i-blog.csdnimg.cn/blog_migrate/bf40e333d71f8953764a8bc2197313f5.png)
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<style type="text/css">
body{
margin: 0; /*外边距*/
padding: 0; /*内边距*/
background-color: #000; /*背景颜色*/
}
a{
position: absolute; /*绝对对位*/
top: 50%; /*距上部*/
left: 50%;
transform: translate(-50%,-50%); /*移动,根据X,Y轴*/
width: 400px; /*宽*/
height: 120px; /*高*/
text-align: center; /*字体水平居中*/
font-size: 45px; /*字体大小*/
line-height: 120px; /*行高*/
color: #fff;
text-transform: uppercase; /*字体大写*/
text-decoration: none; /*字体增加装饰:去除下划线*/
font-family: sans-serif; /*非衬线体*/
box-sizing: border-box; /*盒模型大小规则*/
background: linear-gradient(
90deg,#03a9f4, #f441a5, #ffeb3b,
#03a9f4, #f441a5, #ffeb3b, #03a9f4); /*渐变背景*/
border-radius: 60px; /*边框圆角*/
background-size: 400%; /*背景大小*/
z-index: 1; /*层叠定位*/
}
a:hover{
animation: animate 8s linear infinite alternate; /*动画: 名称 时间 线性 循环 播放完回退播放*/
}
@keyframes animate{
0%{
background-position: 0%; /*修改背景定位,实现渐变色炫光*/
}
50%{
background-position: 100%;
}
100%{
background-position: 0%;
}
}
a::before{ /*之前添加*/
content: ''; /*内容*/
position: absolute; /*绝对定位*/
top:-5px; /*当设置对立的2个定位属性时,元素的大小将由对立的大小决定*/
left: -5px;
right: -5px;
bottom: -5px; /*当设置对立的2个定位属性时,元素的大小将由对立的大小决定*/
z-index: -1;
background: linear-gradient(
90deg,#03a9f4, #f441a5, #ffeb3b, #03a9f4,
#f441a5, #ffeb3b, #03a9f4);
border-radius: 40px;
background-size: 400%;
filter: blur(20px); /*过渡:模糊*/
opacity: 0; /*透明度*/
transition: 1s; /*过渡时间*/
}
a:hover::before{
filter: blur(20px);
opacity: 1;
animation: animate 8s linear infinite; /*注意动画名称统一*/
}
</style>
</head>
<body>
<a href="user">签到表</a>
</body>
</html>
user.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Submissions #2 (table, flex)</title>
<link rel="stylesheet" href="static/css/style.css">
</head>
<body>
<div class="subbox">
<table>
<thead>
<tr>
<th>员工姓名</th>
<th>员工职务</th>
<th>薪资</th>
<th>薪资排名</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<c:forEach items="${all}" var="user">
<tr>
<!-- -->
<td>
<span class="name">${user.username}</span>
</td>
<td>
<span class="email">${user.post}</span>
</td>
<!-- -->
<td>
<span class="date">${user.wage}</span>
</td>
<td>
<span class="date">${user.user_rank}</span>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>
到此这个项目就讲完了,需要源码的在这里哦:免费开源:
链接:https://pan.baidu.com/s/1_VMLhYZ4fYEJzKIrwArtDQ
提取码:ib63
--来自百度网盘超级会员V3的分享