精华内容
下载资源
问答
  • 员工管理系统基于狂神老师的SpringBoot教程:https://www.bilibili.com/video/BV1PE411i7CV?p=20项目成效图:(一)环境搭建1. 新建一个SpringBoot项目选择配件时勾选SpringWeb和Thymeleaf 点击next,然后finish...

    f6d3f15e88850284d47b1b7a2960155c.png

    本员工管理系统基于狂神老师的SpringBoot教程:https://www.bilibili.com/video/BV1PE411i7CV?p=20

    项目成效图

    7f035e8cbd941c1078416fd52d6d4b12.png

    (一)环境搭建

    1. 新建一个SpringBoot项目

    578c799bfde9f39fb70278070fe64967.png

    5c1a6b39aa7e8c42a4ff06fbe769e01a.png

    选择配件时勾选SpringWebThymeleaf

    801b404dce16e4ab3cce76770c8457bc.png

    点击next,然后finish创建完成即可

    2. 导入静态资源

    首先创建不存在的静态资源目录publicresources

    27ffaf7e169feab2e1c1e31d4e38faeb.png

    html静态资源放置templates目录下

    a042023eeab98f1d46e0036d5947cfdf.png

    asserts目录下的cssimgjs等静态资源放置static目录下

    65284658f24544f278b47f816768da96.png

    3. 模拟数据库

    1. 创建数据库实体类

    在主程序同级目录下新建pojo包,用来存放实体类
    pojo包下创建一个部门表Department和一个员工表Employee

    267f044ae19da3d99df2ae77416b38cf.png

    为了方便,我们导入lombok

    <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
    </dependency>

    部门表

    package com.zsr.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    //部门表
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Department {
        private Integer id;
        private String departmentName;
    }

    员工表

    package com.zsr.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.util.Date;
    
    //员工表
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Employee {
        private Integer id;
        private String lastName;
        private String email;
        private Integer gender;//0:女 1:男
        private Department department;
        private Date date;
    }

    2. 编写dao层(模拟数据)

    在主程序同级目录下新建dao
    然后分别编写DepartmentDaoEmployeeDao,并在其中模拟数据库的数据

    18bd1e7eb208180351055d37e7d0edce.png

    DepartmentDao

    package com.zsr.dao;
    
    import com.zsr.pojo.Department;
    import org.springframework.stereotype.Repository;
    
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    //注册到IOC容器中
    @Repository
    public class DepartmentDao {
        //模拟数据库中的数据
        private static Map<Integer, Department> departments = null;
    
        static {
            departments = new HashMap<>();//创建一个部门表
            departments.put(1, new Department(1, "技术部"));
            departments.put(2, new Department(2, "市场部"));
            departments.put(3, new Department(3, "调研部"));
            departments.put(4, new Department(4, "后勤部"));
            departments.put(5, new Department(5, "运营部"));
        }
    
        //获得部门的所有信息
        public Collection<Department> departments() {
            return departments.values();
        }
    
        //通过id得到部门
        public Department getDepartmentById(int id) {
            return departments.get(id);
        }
    }

    EmployeeDao:

    package com.zsr.dao;
    
    import com.zsr.pojo.Department;
    import com.zsr.pojo.Employee;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    
    import java.util.Collection;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    //注册到IOC容器中
    @Repository
    public class EmployeeDao {
        //模拟数据库中员工表的数据
        static private Map<Integer, Employee> employees;
        
        @Autowired//自动
        private DepartmentDao departmentDao;
    
        static {
            employees = new HashMap<>();//创建一个员工表
            employees.put(1, new Employee(1, "zsr", "1234@qq.com", 1, new Department(1, "技术部"), new Date()));
            employees.put(2, new Employee(2, "lyr", "1345@qq.com", 1, new Department(2, "市场部"), new Date()));
            employees.put(3, new Employee(3, "gcc", "5665@qq.com", 0, new Department(3, "调研部"), new Date()));
            employees.put(4, new Employee(4, "zyx", "7688@qq.com", 1, new Department(4, "后勤部"), new Date()));
            employees.put(5, new Employee(5, "zch", "8089@qq.com", 1, new Department(5, "运营部"), new Date()));
        }
    
        //主键自增
        private static Integer initialID = 6;
    
        //增加一个员工
        public void addEmployee(Employee employee) {
            if (employee.getId() == null)
                employee.setId(initialID);
            employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
            employees.put(employee.getId(), employee);
        }
    
        //查询全部员工信息
        public Collection<Employee> getAllEmployees() {
            return employees.values();
        }
    
        //通过id查询员工
        public Employee getEmployeeByID(Integer id) {
            return employees.get(id);
        }
    
        //通过id删除员工
        public void deleteEmployeeByID(int id) {
            employees.remove(id);
        }
    }

    (二)首页实现

    在主程序同级目录下新建config包用来存放自己的配置类
    在其中新建一个自己的配置类MyMvcConfig,进行视图跳转

    746a42794bb707ac4fbfc2f7ce45f84f.png
    package com.zsr.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("index");
            registry.addViewController("/index.html").setViewName("index");
        }
    }

    我们启动主程序访问测试一下,访问localhost:8080/或者locahost:8080/index.html

    出现以下页面则成功

    27294d674a602e761e26cdc64b80d4b0.png
    上述测试可以看到页面有图片没有加载出来,且没有css和js的样式,这就是因为我们html页面中静态资源引入的语法出了问题,在SpringBoot中,推荐使用Thymeleaf作为模板引擎,我们将其中的语法改为Thymeleaf,所有页面的静态资源都需要使用其接管

    注意所有html都需要引入Thymeleaf命名空间

    xmlns:th="http://www.thymeleaf.org"

    然后修改所有页面静态资源的引入,使用@{...} 链接表达式

    例如index.html中:

    注意:第一个/代表项目的classpath,也就是这里的resources目录

    c77fbd0b6454bc4fabbf6547818fdb87.png

    其他页面亦是如此,再次测试访问,正确显示页面

    01a6af9ddc8b252555cc149ddd685d41.png

    (三)页面国际化

    1. 统一properties编码

    首先在IDEA中统一设置properties的编码为UTF-8

    dfafee7cba3b7719cfb44e51670b3675.png

    2. 编写i18n国际化资源文件

    resources目录下新建一个i18n包,其中放置国际化相关的配置

    b5c850eee4606be9647c053e56b2be53.png

    其中新建三个配置文件,用来配置语言:

    • login.properties:无语言配置时候生效
    • login_en_US.properties:英文生效
    • login_zh_CN.properties:中文生效

    命名方式是下划线的组合:文件名_语言_国家.properties;

    以此方式命名,IDEA会帮我们识别这是个国际化配置包,自动绑定在一起转换成如下的模式:

    5a45a1d2f5fbcab24a1a1a9c66dc9535.png

    绑定在一起后,我们想要添加更过语言配置,只需要在大的资源包右键添加到该绑定配置文件即可

    739b27ab3e3a8745d942990de5360d51.png

    此时只需要输入区域名即可创建成功,比如输入en_US,就会自动识别

    d51b806a0117c21e484c2bb912501317.png

    然后打开英文或者中文语言的配置文件,点击Resource Bundle进入可视化编辑页面

    78b350520869c98a9fe2bc2d51d3f585.png

    进入到可视化编辑页面后,点击加号,添加属性,首先新建一个login.tip代表首页中的提示

    212339241781d4352130e6c4eda0f45a.png

    然后对该提示分别做三种情况的语言配置,在三个对应的输入框输入即可(注意:IDEA2020.1可能无法保存,建议直接在配置文件中编写

    52a8c0af52d3a9fb05391f1e7c553d7d.png

    接下来再配置所有要转换语言的变量(注意:IDEA2020.1可能无法保存,建议直接在配置文件中编写

    915c7ff673c05347f575e05ba8dff325.png

    然后打开三个配置文件的查看其中的文本内容,可以看到已经做好了全部的配置

    login.properties

    login.tip=请登录
    login.password=密码
    login.remember=记住我
    login.btn=登录
    login.username=用户名

    login_en_US.properties

    login.tip=Please sign in
    login.password=password
    login.remember=remember me
    login.btn=login
    login.username=username

    login_zh_CN.properties

    login.tip=请登录
    login.password=密码
    login.remember=记住我
    login.btn=登录
    login.username=用户名

    3. 配置国际化资源文件名称

    在Spring程序中,国际化主要是通过ResourceBundleMessageSource这个类来实现的
    Spring Boot通过MessageSourceAutoConfiguration为我们自动配置好了管理国际化资源文件的组件

    我们在IDEA中查看以下MessageSourceAutoConfiguration

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Conditional(ResourceBundleCondition.class)
    @EnableConfigurationProperties
    public class MessageSourceAutoConfiguration {
    
    	private static final Resource[] NO_RESOURCES = {};
    
    	@Bean
    	@ConfigurationProperties(prefix = "spring.messages")
    	public MessageSourceProperties messageSourceProperties() {
    		return new MessageSourceProperties();
    	}
    
    	@Bean
    	public MessageSource messageSource(MessageSourceProperties properties) {
    		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    		if (StringUtils.hasText(properties.getBasename())) {
    			messageSource.setBasenames(StringUtils
    					.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
    		}
    		if (properties.getEncoding() != null) {
    			messageSource.setDefaultEncoding(properties.getEncoding().name());
    		}
    		messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
    		Duration cacheDuration = properties.getCacheDuration();
    		if (cacheDuration != null) {
    			messageSource.setCacheMillis(cacheDuration.toMillis());
    		}
    		messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
    		messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
    		return messageSource;
    	}
    	//......
    }

    主要了解messageSource()这个方法:

    public MessageSource messageSource(MessageSourceProperties properties);

    可以看到,它的参数为MessageSourceProperties对象,我们看看这个类

    public class MessageSourceProperties {
    
    	/**
    	 * Comma-separated list of basenames (essentially a fully-qualified classpath
    	 * location), each following the ResourceBundle convention with relaxed support for
    	 * slash based locations. If it doesn't contain a package qualifier (such as
    	 * "org.mypackage"), it will be resolved from the classpath root.
    	 */
    	private String basename = "messages";
    
    	/**
    	 * Message bundles encoding.
    	 */
    	private Charset encoding = StandardCharsets.UTF_8;
    

    类中首先声明了一个属性basename,默认值为messages;

    我们翻译其注释:

    07d14017f4f3d8334afc5b3a8ced5ab4.png
    - 逗号分隔的基名列表(本质上是完全限定的类路径位置)
    - 每个都遵循ResourceBundle约定并轻松支持于斜杠的位置
    - 如果不包含包限定符(例如"org.mypackage"),它将从类路径根目录中解析

    意思是

    • 如果你不在springboot配置文件中指定以.分隔开的国际化资源文件名称的话
    • 它默认会去类路径下找messages.properties作为国际化资源文件

    这里我们自定义了国际化资源文件,因此我们需要在SpringBoot配置文件application.properties中加入以下配置指定我们配置文件的名称

    spring.messages.basename=i18n.login

    其中i18n是存放资源的文件夹名,login是资源文件的基本名称。

    4. 首页获取显示国际化值

    利用#{...} 消息表达式,去首页index.html获取国际化的值

    9ed115be47ab1b9caf3a6329e852fd51.png

    重启项目,访问首页,可以发现已经自动识别为中文

    400947276ca9793fa8e688eaa37fcfcd.png

    5. 配置国际化组件实现中英文切换

    1. 添加中英文切换标签链接

    上述实现了登录首页显示为中文,我们在index.html页面中可以看到两个标签

    <a class="btn btn-sm">中文</a>
    <a class="btn btn-sm">English</a>

    也就对应着视图中的

    63ae2261ce701ccb4f3e36eb7d0728a9.png

    那么我们怎么通过这两个标签实现中英文切换呢?

    首先在这两个标签上加上跳转链接并带上相应的参数

    <!--这里传入参数不需要使用?使用key=value-->
    <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>

    2. 自定义地区解析器组件

    怎么实现我们自定义的地区解析器呢?我们首先来分析一波源码
    在Spring中有关于国际化的两个类:
    • Locale:代表地区,每一个Locale对象都代表了一个特定的地理、政治和文化地区
    • LocaleResolver:地区解析器

    首先搜索WebMvcAutoConfiguration,可以在其中找到关于一个方法localeResolver()

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
    public LocaleResolver localeResolver() {
        //如果用户配置了,则使用用户配置好的
       if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
          return new FixedLocaleResolver(this.mvcProperties.getLocale());
       }
        //用户没有配置,则使用默认的
       AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
       localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
       return localeResolver;
    }

    该方法就是获取LocaleResolver地区对象解析器:

    • 如果用户配置了则使用用户配置的地区解析器;
    • 如果用户没有配置,则使用默认的地区解析器

    我们可以看到默认地区解析器的是AcceptHeaderLocaleResolver对象,我们点入该类查看源码

    e9d16ba8ad4c24efc33396e71c17d385.png

    可以发现它继承了LocaleResolver接口,实现了地区解析

    因此我们想要实现上述自定义的国际化资源生效,只需要编写一个自己的地区解析器,继承LocaleResolver接口,重写其方法即可

    我们在config包下新建MyLocaleResolver,作为自己的国际化地区解析器

    4dbac3224176e6bfc33b9927e9f3b6f4.png

    我们在index.html中,编写了对应的请求跳转

    • 如果点击中文按钮,则跳转到/index.html(l='zh_CN')页面
    • 如果点击English按钮,则跳转到/index.html(l='en_US')页面

    1363765404432e117116f1931ca294ef.png

    因此我们自定义的地区解析器MyLocaleResolver中,需要处理这两个带参数的链接请求

    package com.zsr.config;
    
    import org.springframework.cglib.core.Local;
    import org.springframework.util.StringUtils;
    import org.springframework.web.servlet.LocaleResolver;
    
    import javax.servl et.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Locale;
    
    public class MyLocaleResolver implements LocaleResolver {
        //解析请求
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            //获取请求中的国际化参数
            String language = request.getParameter("l");
            //默认的地区
            Locale locale = Locale.getDefault();
            //如果请求的链接参数不为空,携带了国际化参数
            if (!StringUtils.isEmpty(language)) {
                String[] split = language.split("_");//zh_CN(语言_地区)
                locale = new Locale(split[0], split[1]);
            }
            return locale;
        }
    
        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
    
        }
    }

    为了让我们的区域化信息能够生效,我们需要再配置一下这个组件!在自己的MvcConofig配置类下添加bean;

    //自定义的国际化组件生效
    @Bean
    public LocaleResolver localeResolver() {
        return new MyLocaleResolver();
    }

    我们重启项目,来访问一下,发现点击按钮可以实现成功切换!

    点击中文按钮,跳转到http://localhost:8080/index.html?l=zh_CN,显示为中文

    da38e1e2b6794cf672e5512936170ad3.png

    点击English按钮,跳转到http://localhost:8080/index.html?l=en_US,显示为英文

    6f44818d621878779f7a2ee92d751b38.png

    (四)登录功能的实现

    登录,也就是当我们点击登录按钮的时候,会进入一个页面,这里进入dashboard页面

    因此我们首先在index.html中的表单编写一个提交地址/user/login,并给名称和密码输入框添加name属性为了后面的传参

    3da53d8c244040551fa6b3c0d43b3d3a.png

    然后编写对应的controller

    在主程序同级目录下新建controller包,在其中新建类loginController,处理登录请求
    package com.zsr.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class LoginController {
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) {
            //如果用户名和密码正确
            if ("admin".equals(username) && "123456".equals(password))
                return "dashboard";//跳转到dashboard页面
                //如果用户名或者密码不正确
            else {
                model.addAttribute("msg", "用户名或者密码错误");//显示错误信息
                return "index";//跳转到首页
            }
        }
    }

    然后我们在index.html首页中加一个标签用来显示controller返回的错误信息

    <p style="color: red" th:text="${msg}"></p>

    b6313c3b991ca7fd6f8561b1f9f8e686.png

    我们再测试一下,启动主程序,访问localhost:8080

    如果我们输入正确的用户名和密码

    be4c1ac25f3c7ed64ac213d6908709b7.png

    则重新跳转到dashboard页面,浏览器url为http://localhost:8080/user/login?username=admin&password=123456

    f9899e3afcbea95735565b9833bc4a41.png

    随便输入错误的用户名12,输入错误的密码12

    浏览器url为http://localhost:8080/user/login?username=12&password=123456,页面上附有错误提示信息

    36272dc194dfbcebacce6f3d7741a1d2.png

    到此我们的登录功能实现完毕,但是有一个很大的问题,浏览器的url暴露了用户的用户名和密码,这在实际开发中可是重大的漏洞,泄露了用户信息,因此我们需要编写一个映射

    我们在自定义的配置类MyMvcConfig中加一句代码

    registry.addViewController("/main.html").setViewName("dashboard");

    也就是访问/main.html页面就跳转到dashboard页面

    然后我们稍稍修改一下LoginController,当登录成功时重定向到main.html页面,也就跳转到了dashboard页面

    package com.zsr.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class LoginController {
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) {
            //如果用户名和密码正确
            if ("admin".equals(username) && "123456".equals(password))
                return "redirect:/main.html";//重定向到main.html页面,也就是跳转到dashboard页面
                //如果用户名或者密码不正确
            else {
                model.addAttribute("msg", "用户名或者密码错误");//显示错误信息
                return "index";//跳转到首页
            }
        }
    }

    我们再次重启测试,输入正确的用户名和密码登陆成功后,浏览器不再携带泄露信息

    add82f50e46fb64565b782da377db373.png

    但是这又出现了新的问题,无论登不登陆,我们访问localhost/main.html都会跳转到dashboard的页面,这就引入了接下来的拦截器

    (五)登录拦截器

    为了解决上述遗留的问题,我们需要自定义一个拦截器;
    config目录下,新建一个登录拦截器类LoginHandlerInterceptor

    用户登录成功后,后台会得到用户信息;如果没有登录,则不会有任何的用户信息;

    我们就可以利用这一点通过拦截器进行拦截

    • 当用户登录时将用户信息存入session中,访问页面时首先判断session中有没有用户的信息
    • 如果没有,拦截器进行拦截;
    • 如果有,拦截器放行

    因此我们首先在LoginController中当用户登录成功后,存入用户信息到session中

    package com.zsr.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.servlet.http.HttpSession;
    
    @Controller
    public class LoginController {
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session) {
            //如果用户名和密码正确
            if ("admin".equals(username) && "123456".equals(password)) {
                session.setAttribute("LoginUser", username);
                return "redirect:/main.html";//重定向到main.html页面,也就是跳转到dashboard页面
            }
            //如果用户名或者密码不正确
            else {
                model.addAttribute("msg", "用户名或者密码错误");//显示错误信息
                return "index";//跳转到首页
            }
        }
    }

    然后再在实现自定义的登录拦截器,继承HandlerInterceptor接口

    • 其中获取存入的session进行判断,如果不为空,则放行;
    • 如果为空,则返回错误消息,并且返回到首页,不放行。
    package com.zsr.config;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.concurrent.ForkJoinPool;
    
    public class LoginHandlerInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //用户登录成功后,应该有自己的session
            Object session = request.getSession().getAttribute("LoginUser");
            if (session == null) {
                request.setAttribute("msg", "权限不够,请先登录");
                request.getRequestDispatcher("/index.html").forward(request, response);
                return false;
            } else {
                return true;
            }
        }
    }

    然后配置到bean中注册,在MyMvcConfig配置类中,重写关于拦截器的方法,添加我们自定义的拦截器,注意屏蔽静态资源及主页以及相关请求的拦截

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index.html", "/", "/user/login", "/css/**", "/js/**", "/img/**");
    }

    然后重启主程序进行测试,直接访问http://localhost:8080/main.html

    a7088d4015a12d5edf0827a33482fcf4.png

    提示权限不够,请先登录,我们登录一下

    6e766c94acdcbae3c06c327982dffa3f.png

    进入到dashboard页面

    如果我们再直接重新访问http://localhost:8080/main.html,也可以直接直接进入到dashboard页面,这是因为session里面存入了用户的信息,拦截器放行通过

    696375650512fc227fb7d66597419364.png

    (六)展示员工信息——查

    1. 实现Customers视图跳转

    目标:点击dashboard.html页面中的Customers展示跳转到list.html页面显示所有员工信息

    8bd99d35afc1746f6961e4490905fc5e.png

    因此,我们首先给dashboard.html页面中Customers部分标签添加href属性,实现点击该标签请求/emps路径跳转到list.html展示所有的员工信息

    22180dd9b6f96d41cf2dde3ffff74cc8.png
    <li class="nav-item">
        <a class="nav-link" th:href="@{/emps}">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                 stroke-linejoin="round" class="feather feather-users">
                <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                <circle cx="9" cy="7" r="4"></circle>
                <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
            </svg>
            Customers
        </a>
    </li>

    同样修改list.html对应该的代码为上述代码

    7bc8c0de07d2e9a523028950fb6f15c4.png

    我们在templates目录下新建一个包emp,用来放所有关于员工信息的页面,我们将list.html页面移入该包中

    4bf23321f5ce18c121f191ec982cfcda.png

    然后编写请求对应的controller,处理/emps请求,在controller包下,新建一个EmployeeController

    package com.zsr.controller;
    
    import com.zsr.dao.EmployeeDao;
    import com.zsr.pojo.Employee;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import java.util.Collection;
    
    @Controller
    public class EmployeeController {
        @Autowired
        private EmployeeDao employeeDao;
    
        @RequestMapping("/emps")
        public String list(Model model) {
            Collection<Employee> employees = employeeDao.getAllEmployees();
            model.addAttribute(employees);
            return "emp/list";//返回到list页面
        }
    }

    然后我们重启主程序进行测试,登录到dashboard页面,再点击Customers,成功跳转到/emps

    371016743a6b1832f50dd7c3c7b44e31.png

    但是有些问题

    1. 我们点击了Customers后,它应该处于高亮状态,但是这里点击后还是普通的样子,高亮还是在Dashboard
    2. list.htmldashboard.html页面的侧边栏和顶部栏是相同的,可以抽取出来

    2. 提取页面公共部分

    templates目录下新建一个commons包,其中新建commons.html用来放置公共页面代码

    1b267226684d104f0b22d066375b309a.png

    利用th:fragment标签抽取公共部分(顶部导航栏和侧边栏)

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
        
    <!--顶部导航栏,利用th:fragment提取出来,命名为topbar-->
    <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
        <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company
            name</a>
        <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
        <ul class="navbar-nav px-3">
            <li class="nav-item text-nowrap">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a>
            </li>
        </ul>
    </nav>
        
    <!--侧边栏,利用th:fragment提取出来,命名为sidebar-->
    <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="siderbar">
        <div class="sidebar-sticky">
            <ul class="nav flex-column">
                <li class="nav-item">
                    <a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-home">
                            <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                            <polyline points="9 22 9 12 15 12 15 22"></polyline>
                        </svg>
                        Dashboard <span class="sr-only">(current)</span>
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file">
                            <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path>
                            <polyline points="13 2 13 9 20 9"></polyline>
                        </svg>
                        Orders
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-shopping-cart">
                            <circle cx="9" cy="21" r="1"></circle>
                            <circle cx="20" cy="21" r="1"></circle>
                            <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
                        </svg>
                        Products
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" th:href="@{/emps}">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-users">
                            <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                            <circle cx="9" cy="7" r="4"></circle>
                            <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                            <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                        </svg>
                        Customers
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-bar-chart-2">
                            <line x1="18" y1="20" x2="18" y2="10"></line>
                            <line x1="12" y1="20" x2="12" y2="4"></line>
                            <line x1="6" y1="20" x2="6" y2="14"></line>
                        </svg>
                        Reports
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-layers">
                            <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
                            <polyline points="2 17 12 22 22 17"></polyline>
                            <polyline points="2 12 12 17 22 12"></polyline>
                        </svg>
                        Integrations
                    </a>
                </li>
            </ul>
    
            <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
                <span>Saved reports</span>
                <a class="d-flex align-items-center text-muted"
                   href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                         stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
                         class="feather feather-plus-circle">
                        <circle cx="12" cy="12" r="10"></circle>
                        <line x1="12" y1="8" x2="12" y2="16"></line>
                        <line x1="8" y1="12" x2="16" y2="12"></line>
                    </svg>
                </a>
            </h6>
            <ul class="nav flex-column mb-2">
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Current month
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Last quarter
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Social engagement
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Year-end sale
                    </a>
                </li>
            </ul>
        </div>
    </nav>
    </html>

    然后删除dashboard.htmllist.html中顶部导航栏和侧边栏的代码

    f2b1bd93bbfb602186a7bdfa6813e999.png

    我们再次重启主程序测试一下,登陆成功后,可以看到已经没有了顶部导航栏和侧边栏

    1489e5d794b5ee55d7367138013325e6.png

    这是因为我们删除了公共部分,还没有引入,我们分别在dashboard.htmllist.html删除的部分插入提取出来的公共部分topbarsidebar

    <!--顶部导航栏-->
    <div th:replace="~{commons/commons::topbar}" }></div>
    <!--侧边栏-->
    <div th:replace="~{commons/commons::siderbar}"></div>

    f7984543ab67491907d326289cb0751f.png

    再次重启主程序进行测试,登陆成功后,成功看到侧边栏和顶部栏,代表我们插入成功

    93bb1f25b2ffa65a72c8f36ca7ab9245.png

    3. 点击高亮处理

    在页面中,使高亮的代码是class="nav-link active"属性

    4fb829e349afcf57606a1c7774686382.png

    我们可以传递参数判断点击了哪个标签实现相应的高亮

    首先在dashboard.html的侧边栏标签传递参数activedashboard.html

    eb6a867166a37a0cbb08e3a3a471ffa6.png
    <!--侧边栏-->
    <div th:replace="~{commons/commons::siderbar(active='dashboard.html')}"></div>

    同样在list.html的侧边栏标签传递参数activelist.html

    703df9db593c0503e6234ae39e2d7f37.png
    <!--侧边栏-->
    <div th:replace="~{commons/commons::siderbar(active='list.html')}"></div>

    然后我们在公共页面commons.html相应标签部分利用thymeleaf接收参数active,利用三元运算符判断决定是否高亮

    07d1bb0a0d9759279d028cc7cdd0b535.png

    再次重启主程序测试,登录成功后,首先Dashboard高亮

    d585ba9bd6cff2d6332406bac459e575.png

    再点击CustomersCustomers高亮,成功

    33c66c168e9b0938f4d728b411b5d2ef.png

    4. 显示员工信息

    修改list.html页面,显示我们自己的数据值

    a60cdd7c8b94f3710c2964489190d53a.png

    修改完成后,重启主程序,登录完成后查看所有员工信息,成功显示

    98886e2284957020f49d4a7c5e1b8d0e.png

    接下来修改一下性别的显示和date的显示,并添加编辑删除两个标签,为后续做准备

    <thead>
        <tr>
            <th>id</th>
            <th>lastName</th>
            <th>email</th>
            <th>gender</th>
            <th>department</th>
            <th>date</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="emp:${emps}">
            <td th:text="${emp.getId()}"></td>
            <td th:text="${emp.getLastName()}"></td>
            <td th:text="${emp.getEmail()}"></td>
            <td th:text="${emp.getGender()==0?'女':'男'}"></td>
            <td th:text="${emp.getDepartment().getDepartmentName()}"></td>
            <td th:text="${#dates.format(emp.getDate(),'yyyy-MM-dd HH:mm:ss')}"></td>
            <td>
                <a class="btn btn-sm btn-primary">编辑</a>
                <a class="btn btn-sm btn-danger">删除</a>
            </td>
        </tr>
    </tbody>

    再次重启主程序测试,成功

    8329ebc6ec7fecd1ee005b8ffbebe3ad.png

    (七)增加员工实现——增

    1. list页面增加添加员工按钮

    首先在list.html页面增添一个增加员工按钮,点击该按钮时发起一个请求/add

    4f866875cc5fbe8a47befb8360739aa6.png
    <h2><a class="btn btn-sm btn-success" th:href="@{/add}">添加员工</a></h2>

    b3ebbee71a051b34d6ba9507897f9e21.png

    然后编写对应的controller,处理点击添加员工的请求

    这里通过get方式提交请求,在EmployeeController中添加一个方法add用来处理list页面点击提交按钮的操作,返回到add.html添加员工页面,我们即将创建

    @GetMapping("/add")
    public String add(Model model) {
        //查出所有的部门信息,添加到departments中,用于前端接收
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "emp/add";//返回到添加员工页面
    }

    2. 创建添加员工页面add

    templates/emp下新建一个add.html

    ca45aa44e042b6a3a28d2090037f5751.png

    我们复制list.html中的内容,修改其中表格为:

    <form>
        <div class="form-group">
            <label>LastName</label>
            <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
        </div>
        <div class="form-group">
            <label>Email</label>
            <input type="email" name="email" class="form-control" placeholder="email:xxxxx@qq.com">
        </div>
        <div class="form-group">
            <label>Gender</label><br/>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="gender" value="1">
                <label class="form-check-label"></label>
            </div>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="gender" value="0">
                <label class="form-check-label"></label>
            </div>
        </div>
        <div class="form-group">
            <label>department</label>
            <!--注意这里的name是department.id因为传入的参数为id-->
            <select class="form-control" name="department.id">
                <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
            </select>
        </div>
        <div class="form-group">
            <label>Birth</label>
            <!--springboot默认的日期格式为yy/MM/dd-->
            <input type="text" name="date" class="form-control" placeholder="birth:yyyy/MM/dd">
        </div>
        <button type="submit" class="btn btn-primary">添加</button>
    </form>

    我们重启主程序看看

    48ba06eb26ed076aed9d644e2860b6db.png

    点击添加员工,成功跳转到add.html页面

    62cf6faa7051fb913436e0b7fc773807.png

    下拉框中的内容不应该是1、2、3、4、5;应该是所有的部门名,我们遍历得到

    <!--其中th:value用来表示部门的id,我们实际传入的值为id-->
    <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>

    重启测试,成功显示所有部门

    eee0dda112357dbbdcd6cea8a50efbdf.png

    到此,添加员工页面编写完成

    3. add页面添加员工请求

    add.html页面,当我们填写完信息,点击添加按钮,应该完成添加返回到list页面,展示新的员工信息;因此在add.html点击添加按钮的一瞬间,我们同样发起一个请求/add,与上述提交按钮发出的请求路径一样,但这里发出的是post请求

    b0862423d8fa009bf2d2f5e444129c50.png

    然后编写对应的controller,同样在EmployeeController中添加一个方法addEmp用来处理点击添加按钮的操作

    @PostMapping("/add")
    public String addEmp(Employee employee) {
        employeeDao.addEmployee(employee);//添加一个员工
        return "redirect:/emps";//重定向到/emps,刷新列表,返回到list页面
    }

    我们重启主程序,进行测试,进入添加页面,填写相关信息,注意日期格式默认为yyyy/MM/dd

    82273292c59523f9c52cd99fe79ea1fb.png

    然后点击添加按钮,成功实现添加员工

    d4c9df0f962cdb378c69919475d412bf.png

    我们也可以添加多个员工

    6a51efcf9e7bab18aea484069000aa9b.png

    (八)修改员工信息——改

    1. list页面编辑按钮增添请求

    0182e65aa4c5910431c69e4ce4d0b6f1.png

    当我们点击编辑标签时,应该跳转到编辑页面edit.html(我们即将创建)进行编辑
    因此首先将list.html页面的编辑标签添加href属性,实现点击请求/edit/id号到编辑页面

    <a class="btn btn-sm btn-primary" th:href="@{/edit/{id}(id=${emp.getId()})}">编辑</a>

    e1f1ad1590cf0ec4cb74726e81191b55.png

    然后编写对应的controller,在EmployeeController中添加一个方法edit用来处理list页面点击编辑按钮的操作,返回到edit.html编辑员工页面,我们即将创建

    //restful风格接收参数
    @RequestMapping("/edit/{id}")
    public String edit(@PathVariable("id") int id, Model model) {
        //查询指定id的员工,添加到empByID中,用于前端接收
        Employee employeeByID = employeeDao.getEmployeeByID(id);
        model.addAttribute("empByID", employeeByID);
        //查出所有的部门信息,添加到departments中,用于前端接收
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "/emp/edit";//返回到编辑员工页面
    }

    2. 创建编辑员工页面edit

    templates/emp下新建一个edit.html

    a3b9010f847c7fd9ffd5f62a36bbc63c.png

    复制add.html中的代码,稍作修改

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <form th:action="@{/edit}" method="post">
            <div class="form-group">
                <label>LastName</label>
                <input th:value="${empByID.getLastName()}" type="text" name="lastName" class="form-control"
                       placeholder="lastname:zsr">
            </div>
            <div class="form-group">
                <label>Email</label>
                <input th:value="${empByID.getEmail()}" type="email" name="email" class="form-control"
                       placeholder="email:xxxxx@qq.com">
            </div>
            <div class="form-group">
                <label>Gender</label><br/>
                <div class="form-check form-check-inline">
                    <input th:checked="${empByID.getGender()==1}" class="form-check-input" type="radio"
                           name="gender" value="1">
                    <label class="form-check-label"></label>
                </div>
                <div class="form-check form-check-inline">
                    <input th:checked="${empByID.getGender()==0}" class="form-check-input" type="radio"
                           name="gender" value="0">
                    <label class="form-check-label"></label>
                </div>
            </div>
            <div class="form-group">
                <label>department</label>
                <!--注意这里的name是department.id因为传入的参数为id-->
                <select class="form-control" name="department.id">
                    <option th:selected="${department.getId()==empByID.department.getId()}"
                            th:each="department:${departments}" th:text="${department.getDepartmentName()}"
                            th:value="${department.getId()}">
                    </option>
                </select>
            </div>
            <div class="form-group">
                <label>Birth</label>
                <!--springboot默认的日期格式为yy/MM/dd-->
                <input th:value="${empByID.getDate()}" type="text" name="date" class="form-control"
                       placeholder="birth:yy/MM/dd">
            </div>
            <button type="submit" class="btn btn-primary">修改</button>
        </form>
    </main>

    启动主程序测试,点击编辑1号用户

    7c4dc2bac6fe1ceaac9afdacf11c109b.png

    成功跳转到edit.html,且所选用户信息正确

    9cae64cec29b862b11bc3e5b25fea8a7.png

    但是日期的格式不太正确,我们规定一下显示的日期格式

    <!--springboot默认的日期格式为yy/MM/dd-->
    <input th:value="${#dates.format(empByID.getDate(),'yyyy/MM/dd')}" type="text" name="date" class="form-control"
           placeholder="birth:yy/MM/dd">

    3. edit页面编辑完成提交请求

    edit.html点击修改按钮的一瞬间,我们需要返回到list页面,更新员工信息,因此我们需要添加href属性,实现点击按钮时发起一个请求/edit

    7f5789f8d2fb9ee7cee3d53c8d5a5972.png

    然后编写对应的controller,处理点击修改按钮的请求

    同样在EmployeeController中添加一个方法EditEmp用来处理edit页面点击添加的操作

    @PostMapping("/add")
    public String EditEmp(Employee employee) {
        employeeDao.addEmployee(employee);//添加一个员工
        return "redirect:/emps";//添加完成重定向到/emps,刷新列表
    }

    然后指定修改人的id

    <input type="hidden" name="id" th:value="${empByID.getId()}">

    重启测试,同样修改1号用户名称为dddd

    5573d4d8ef2d15ab9fd5b3e111f73f50.png

    然后点击修改

    ff92d3a0619d8670d083f5d5b4e6ee2c.png

    成功修改并返回到list.html

    (九)删除员工信息——删

    15ee63373f81348f100b4d0ddccf1bc9.png

    当我们点击删除标签时,应该发起一个请求,删除指定的用户,然后重新返回到list页面显示员工数据

    <a class="btn btn-sm btn-success" th:href="@{/delete/{id}(id=${emp.getId()})}">删除</a>

    然后编写对应的controller,处理点击删除按钮的请求,删除指定员工,重定向到/emps请求,更新员工信息

    @GetMapping("/delete/{id}")
    public String delete(@PathVariable("id") Integer id) {
        employeeDao.deleteEmployeeByID(id);
        return "redirect:/emps";
    }

    重启测试,点击删除按钮即可删除指定员工

    (十)404页面定制

    只需要在templates目录下新建一个error包,然后将404.html放入其中,报错SpringBoot就会自动找到这个页面

    6313dc1fe7fb8ce5283bbf01ed2977c5.png

    我们可以启动程序测试,随便访问一个不存在的页面

    bd43dc5c29868fb55a0385943c2eb6c6.png

    出现的404页面即是我们自己的404.html

    (十一)注销操作

    在我们提取出来的公共commons页面,顶部导航栏处中的标签添加href属性,实现点击发起请求/user/logout

    d45fd05e39fbbc4661a06de6b0459131.png

    然后编写对应的controller,处理点击注销标签的请求,在LoginController中编写对应的方法,清除session,并重定向到首页

    @RequestMapping("/user/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/index.html";
    }

    重启测试,登录成功后,点击log out即可退出到首页

    ed230dc487e5b44ce875c9755d9d442b.png

    静态资源:

    百度云链接:https://pan.baidu.com/s/1om3FItYstCmnyKD4lGsDTA
    提取码:pxpb

    d7e0a926ac7c685df969e5dedd1cb8b1.png

    员工管理系统最终完整项目资源:

    百度云链接:https://pan.baidu.com/s/162RdDOCuNWm0JDqShucLGA
    提取码:vt2x

    1aa2c34dadb903ad8232ff2d4858c4f1.png

    7e0fe16a1038a43f02f6d0ef245bfea7.png
    作者:Baret-H
    链接:https://blog.csdn.net/qq_45173404/article/details/108934414
    来源:CSDN博客
    展开全文
  • 基于C语言、Sqlite3编写的员工管理系统小项目,运用到进程、线程、网络套接字、数据库、函数封装等知识
  • 配置国际化资源文件名称 在Spring程序中,国际化主要是通过ResourceBundleMessageSource这个类来实现的 Spring Boot通过MessageSourceAutoConfiguration为我们自动配置好了管理国际化资源文件的组件 我们在IDEA中...

    ab44f5b253dc70bc02736f0deb86af01.png

    不废话~~~ 先看效果图

    项目成效图

    0a1060a0ccd40e75e836c0588860be96.png

    (一)环境搭建

    1. 新建一个SpringBoot项目

    9f4ff747259a879962a88fd441457e95.png

    d4eb034f8a4fe350dc5621507d3f6221.png

    选择配件时勾选SpringWebThymeleaf

    75b966195d7ea96b39049d6c352d003c.png

    点击next,然后finish创建完成即可

    2. 导入静态资源

    首先创建不存在的静态资源目录publicresources

    cc24b23504614aac8b2912c963c052f5.png

    html静态资源放置templates目录下

    49e640f04ac7bd1ccfed70e32078d67e.png

    asserts目录下的cssimgjs等静态资源放置static目录下

    49e640f04ac7bd1ccfed70e32078d67e.png

    3. 模拟数据库

    1. 创建数据库实体类

    在主程序同级目录下新建pojo包,用来存放实体类
    pojo包下创建一个部门表Department和一个员工表Employee

    49e640f04ac7bd1ccfed70e32078d67e.png

    为了方便,我们导入lombok

    <dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
    </dependency>

    部门表

    package com.zsr.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    //部门表
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Department {
        private Integer id;
        private String departmentName;
    }

    员工表

    package com.zsr.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.util.Date;
    
    //员工表
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Employee {
        private Integer id;
        private String lastName;
        private String email;
        private Integer gender;//0:女 1:男
        private Department department;
        private Date date;
    }

    2. 编写dao层(模拟数据)

    在主程序同级目录下新建dao
    然后分别编写DepartmentDaoEmployeeDao,并在其中模拟数据库的数据

    49e640f04ac7bd1ccfed70e32078d67e.png

    DepartmentDao

    package com.zsr.dao;
    
    import com.zsr.pojo.Department;
    import org.springframework.stereotype.Repository;
    
    import java.util.Collection;
    import java.util.HashMap;
    import java.util.Map;
    
    //注册到IOC容器中
    @Repository
    public class DepartmentDao {
        //模拟数据库中的数据
        private static Map<Integer, Department> departments = null;
    
        static {
            departments = new HashMap<>();//创建一个部门表
            departments.put(1, new Department(1, "技术部"));
            departments.put(2, new Department(2, "市场部"));
            departments.put(3, new Department(3, "调研部"));
            departments.put(4, new Department(4, "后勤部"));
            departments.put(5, new Department(5, "运营部"));
        }
    
        //获得部门的所有信息
        public Collection<Department> departments() {
            return departments.values();
        }
    
        //通过id得到部门
        public Department getDepartmentById(int id) {
            return departments.get(id);
        }
    }

    EmployeeDao:

    package com.zsr.dao;
    
    import com.zsr.pojo.Department;
    import com.zsr.pojo.Employee;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
    
    import java.util.Collection;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    //注册到IOC容器中
    @Repository
    public class EmployeeDao {
        //模拟数据库中员工表的数据
        static private Map<Integer, Employee> employees;
        
        @Autowired//自动
        private DepartmentDao departmentDao;
    
        static {
            employees = new HashMap<>();//创建一个员工表
            employees.put(1, new Employee(1, "zsr", "1234@qq.com", 1, new Department(1, "技术部"), new Date()));
            employees.put(2, new Employee(2, "lyr", "1345@qq.com", 1, new Department(2, "市场部"), new Date()));
            employees.put(3, new Employee(3, "gcc", "5665@qq.com", 0, new Department(3, "调研部"), new Date()));
            employees.put(4, new Employee(4, "zyx", "7688@qq.com", 1, new Department(4, "后勤部"), new Date()));
            employees.put(5, new Employee(5, "zch", "8089@qq.com", 1, new Department(5, "运营部"), new Date()));
        }
    
        //主键自增
        private static Integer initialID = 6;
    
        //增加一个员工
        public void addEmployee(Employee employee) {
            if (employee.getId() == null)
                employee.setId(initialID);
            employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
            employees.put(employee.getId(), employee);
        }
    
        //查询全部员工信息
        public Collection<Employee> getAllEmployees() {
            return employees.values();
        }
    
        //通过id查询员工
        public Employee getEmployeeByID(Integer id) {
            return employees.get(id);
        }
    
        //通过id删除员工
        public void deleteEmployeeByID(int id) {
            employees.remove(id);
        }
    }

    (二)首页实现

    在主程序同级目录下新建config包用来存放自己的配置类
    在其中新建一个自己的配置类MyMvcConfig,进行视图跳转

    49e640f04ac7bd1ccfed70e32078d67e.png
    package com.zsr.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("index");
            registry.addViewController("/index.html").setViewName("index");
        }
    }

    我们启动主程序访问测试一下,访问localhost:8080/或者locahost:8080/index.html

    出现以下页面则成功

    49e640f04ac7bd1ccfed70e32078d67e.png
    上述测试可以看到页面有图片没有加载出来,且没有css和js的样式,这就是因为我们html页面中静态资源引入的语法出了问题,在SpringBoot中,推荐使用Thymeleaf作为模板引擎,我们将其中的语法改为Thymeleaf,所有页面的静态资源都需要使用其接管

    注意所有html都需要引入Thymeleaf命名空间

    xmlns:th="http://www.thymeleaf.org"

    然后修改所有页面静态资源的引入,使用@{...} 链接表达式

    例如index.html中:

    注意:第一个/代表项目的classpath,也就是这里的resources目录

    49e640f04ac7bd1ccfed70e32078d67e.png

    其他页面亦是如此,再次测试访问,正确显示页面

    49e640f04ac7bd1ccfed70e32078d67e.png

    (三)页面国际化

    1. 统一properties编码

    首先在IDEA中统一设置properties的编码为UTF-8

    49e640f04ac7bd1ccfed70e32078d67e.png

    2. 编写i18n国际化资源文件

    resources目录下新建一个i18n包,其中放置国际化相关的配置

    c8b99685dbf3ebcd719c7df1aaccdc35.png

    其中新建三个配置文件,用来配置语言:

    • login.properties:无语言配置时候生效
    • login_en_US.properties:英文生效
    • login_zh_CN.properties:中文生效

    命名方式是下划线的组合:文件名_语言_国家.properties;

    以此方式命名,IDEA会帮我们识别这是个国际化配置包,自动绑定在一起转换成如下的模式:

    3df9aab4dfdb50173de1875ed54b1435.png

    绑定在一起后,我们想要添加更过语言配置,只需要在大的资源包右键添加到该绑定配置文件即可

    6819816db7112bb822fb8ba0ac8e75de.png

    此时只需要输入区域名即可创建成功,比如输入en_US,就会自动识别

    340ef2564a1e2b2982f6e19638837279.png

    然后打开英文或者中文语言的配置文件,点击Resource Bundle进入可视化编辑页面

    f9b099bf6b0e77adad8ab8ccd0398b94.png

    进入到可视化编辑页面后,点击加号,添加属性,首先新建一个login.tip代表首页中的提示

    a4e5abc1885316a1d10facc7d249ff7d.png

    然后对该提示分别做三种情况的语言配置,在三个对应的输入框输入即可(注意:IDEA2020.1可能无法保存,建议直接在配置文件中编写

    f3eeccb940f0a69781b5cbd3d5e87b8c.png

    接下来再配置所有要转换语言的变量(注意:IDEA2020.1可能无法保存,建议直接在配置文件中编写

    e662fe492f69ab1acd717c2bd9680e86.png

    然后打开三个配置文件的查看其中的文本内容,可以看到已经做好了全部的配置

    login.properties

    login.tip=请登录
    login.password=密码
    login.remember=记住我
    login.btn=登录
    login.username=用户名

    login_en_US.properties

    login.tip=Please sign in
    login.password=password
    login.remember=remember me
    login.btn=login
    login.username=username

    login_zh_CN.properties

    login.tip=请登录
    login.password=密码
    login.remember=记住我
    login.btn=登录
    login.username=用户名

    3. 配置国际化资源文件名称

    在Spring程序中,国际化主要是通过ResourceBundleMessageSource这个类来实现的
    Spring Boot通过MessageSourceAutoConfiguration为我们自动配置好了管理国际化资源文件的组件

    我们在IDEA中查看以下MessageSourceAutoConfiguration

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Conditional(ResourceBundleCondition.class)
    @EnableConfigurationProperties
    public class MessageSourceAutoConfiguration {
    
    	private static final Resource[] NO_RESOURCES = {};
    
    	@Bean
    	@ConfigurationProperties(prefix = "spring.messages")
    	public MessageSourceProperties messageSourceProperties() {
    		return new MessageSourceProperties();
    	}
    
    	@Bean
    	public MessageSource messageSource(MessageSourceProperties properties) {
    		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    		if (StringUtils.hasText(properties.getBasename())) {
    			messageSource.setBasenames(StringUtils
    					.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
    		}
    		if (properties.getEncoding() != null) {
    			messageSource.setDefaultEncoding(properties.getEncoding().name());
    		}
    		messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
    		Duration cacheDuration = properties.getCacheDuration();
    		if (cacheDuration != null) {
    			messageSource.setCacheMillis(cacheDuration.toMillis());
    		}
    		messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
    		messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
    		return messageSource;
    	}
    	//......
    }

    主要了解messageSource()这个方法:

    public MessageSource messageSource(MessageSourceProperties properties);

    可以看到,它的参数为MessageSourceProperties对象,我们看看这个类

    public class MessageSourceProperties {
    
    	/**
    	 * Comma-separated list of basenames (essentially a fully-qualified classpath
    	 * location), each following the ResourceBundle convention with relaxed support for
    	 * slash based locations. If it doesn't contain a package qualifier (such as
    	 * "org.mypackage"), it will be resolved from the classpath root.
    	 */
    	private String basename = "messages";
    
    	/**
    	 * Message bundles encoding.
    	 */
    	private Charset encoding = StandardCharsets.UTF_8;

    类中首先声明了一个属性basename,默认值为messages;

    我们翻译其注释:

    49e640f04ac7bd1ccfed70e32078d67e.png
    - 逗号分隔的基名列表(本质上是完全限定的类路径位置)
    - 每个都遵循ResourceBundle约定,并轻松支持于斜杠的位置
    - 如果不包含包限定符(例如"org.mypackage"),它将从类路径根目录中解析

    意思是

    • 如果你不在springboot配置文件中指定以.分隔开的国际化资源文件名称的话
    • 它默认会去类路径下找messages.properties作为国际化资源文件

    这里我们自定义了国际化资源文件,因此我们需要在SpringBoot配置文件application.properties中加入以下配置指定我们配置文件的名称

    spring.messages.basename=i18n.login

    其中i18n是存放资源的文件夹名,login是资源文件的基本名称。

    4. 首页获取显示国际化值

    利用#{...} 消息表达式,去首页index.html获取国际化的值

    49e640f04ac7bd1ccfed70e32078d67e.png

    重启项目,访问首页,可以发现已经自动识别为中文

    57682f5f301832d04532e2084bce685c.png

    5. 配置国际化组件实现中英文切换

    1. 添加中英文切换标签链接

    上述实现了登录首页显示为中文,我们在index.html页面中可以看到两个标签

    <a class="btn btn-sm">中文</a>
    <a class="btn btn-sm">English</a>

    也就对应着视图中的

    0497f6e2e85de19ab3503918ffe5d300.png

    那么我们怎么通过这两个标签实现中英文切换呢?

    首先在这两个标签上加上跳转链接并带上相应的参数

    <!--这里传入参数不需要使用?使用key=value-->
    <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>

    2. 自定义地区解析器组件

    怎么实现我们自定义的地区解析器呢?我们首先来分析一波源码
    在Spring中有关于国际化的两个类:
    • Locale:代表地区,每一个Locale对象都代表了一个特定的地理、政治和文化地区
    • LocaleResolver:地区解析器

    首先搜索WebMvcAutoConfiguration,可以在其中找到关于一个方法localeResolver()

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
    public LocaleResolver localeResolver() {
        //如果用户配置了,则使用用户配置好的
       if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
          return new FixedLocaleResolver(this.mvcProperties.getLocale());
       }
        //用户没有配置,则使用默认的
       AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
       localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
       return localeResolver;
    }

    该方法就是获取LocaleResolver地区对象解析器:

    • 如果用户配置了则使用用户配置的地区解析器;
    • 如果用户没有配置,则使用默认的地区解析器

    我们可以看到默认地区解析器的是AcceptHeaderLocaleResolver对象,我们点入该类查看源码

    fe9fa30a6d3c758282c2cb86c7808a12.png

    可以发现它继承了LocaleResolver接口,实现了地区解析

    因此我们想要实现上述自定义的国际化资源生效,只需要编写一个自己的地区解析器,继承LocaleResolver接口,重写其方法即可

    我们在config包下新建MyLocaleResolver,作为自己的国际化地区解析器

    3bb9f26ef1a15f0cd199b91a8b5cb498.png

    我们在index.html中,编写了对应的请求跳转

    • 如果点击中文按钮,则跳转到/index.html(l='zh_CN')页面
    • 如果点击English按钮,则跳转到/index.html(l='en_US')页面

    02c11e86c136df77ec918a87c1eb014e.png

    因此我们自定义的地区解析器MyLocaleResolver中,需要处理这两个带参数的链接请求

    package com.zsr.config;
    
    import org.springframework.cglib.core.Local;
    import org.springframework.util.StringUtils;
    import org.springframework.web.servlet.LocaleResolver;
    
    import javax.servl et.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Locale;
    
    public class MyLocaleResolver implements LocaleResolver {
        //解析请求
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            //获取请求中的国际化参数
            String language = request.getParameter("l");
            //默认的地区
            Locale locale = Locale.getDefault();
            //如果请求的链接参数不为空,携带了国际化参数
            if (!StringUtils.isEmpty(language)) {
                String[] split = language.split("_");//zh_CN(语言_地区)
                locale = new Locale(split[0], split[1]);
            }
            return locale;
        }
    
        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
    
        }
    }

    为了让我们的区域化信息能够生效,我们需要再配置一下这个组件!在自己的MvcConofig配置类下添加bean;

    //自定义的国际化组件生效
    @Bean
    public LocaleResolver localeResolver() {
        return new MyLocaleResolver();
    }

    我们重启项目,来访问一下,发现点击按钮可以实现成功切换!

    点击中文按钮,跳转到http://localhost:8080/index.html?l=zh_CN,显示为中文

    e6597bbcb2c31df3c637f76fef67ea54.png

    点击English按钮,跳转到http://localhost:8080/index.html?l=en_US,显示为英文

    b52ebf1b3fca2f133f4b1099815bf066.png

    (四)登录功能的实现

    登录,也就是当我们点击登录按钮的时候,会进入一个页面,这里进入dashboard页面

    因此我们首先在index.html中的表单编写一个提交地址/user/login,并给名称和密码输入框添加name属性为了后面的传参

    bc49da3f8390d8e1f501fa84330b1f95.png

    然后编写对应的controller

    在主程序同级目录下新建controller包,在其中新建类loginController,处理登录请求
    package com.zsr.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class LoginController {
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) {
            //如果用户名和密码正确
            if ("admin".equals(username) && "123456".equals(password))
                return "dashboard";//跳转到dashboard页面
                //如果用户名或者密码不正确
            else {
                model.addAttribute("msg", "用户名或者密码错误");//显示错误信息
                return "index";//跳转到首页
            }
        }
    }

    然后我们在index.html首页中加一个标签用来显示controller返回的错误信息

    <p style="color: red" th:text="${msg}"></p>

    49e640f04ac7bd1ccfed70e32078d67e.png

    我们再测试一下,启动主程序,访问localhost:8080

    如果我们输入正确的用户名和密码

    49e640f04ac7bd1ccfed70e32078d67e.png

    则重新跳转到dashboard页面,浏览器url为http://localhost:8080/user/login?username=admin&password=123456

    49e640f04ac7bd1ccfed70e32078d67e.png

    随便输入错误的用户名12,输入错误的密码12

    浏览器url为http://localhost:8080/user/login?username=12&password=123456,页面上附有错误提示信息

    49e640f04ac7bd1ccfed70e32078d67e.png

    到此我们的登录功能实现完毕,但是有一个很大的问题,浏览器的url暴露了用户的用户名和密码,这在实际开发中可是重大的漏洞,泄露了用户信息,因此我们需要编写一个映射

    我们在自定义的配置类MyMvcConfig中加一句代码

    registry.addViewController("/main.html").setViewName("dashboard");

    也就是访问/main.html页面就跳转到dashboard页面

    然后我们稍稍修改一下LoginController,当登录成功时重定向到main.html页面,也就跳转到了dashboard页面

    package com.zsr.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class LoginController {
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model) {
            //如果用户名和密码正确
            if ("admin".equals(username) && "123456".equals(password))
                return "redirect:/main.html";//重定向到main.html页面,也就是跳转到dashboard页面
                //如果用户名或者密码不正确
            else {
                model.addAttribute("msg", "用户名或者密码错误");//显示错误信息
                return "index";//跳转到首页
            }
        }
    }

    我们再次重启测试,输入正确的用户名和密码登陆成功后,浏览器不再携带泄露信息

    49e640f04ac7bd1ccfed70e32078d67e.png

    但是这又出现了新的问题,无论登不登陆,我们访问localhost/main.html都会跳转到dashboard的页面,这就引入了接下来的拦截器

    (五)登录拦截器

    为了解决上述遗留的问题,我们需要自定义一个拦截器;
    config目录下,新建一个登录拦截器类LoginHandlerInterceptor

    用户登录成功后,后台会得到用户信息;如果没有登录,则不会有任何的用户信息;

    我们就可以利用这一点通过拦截器进行拦截

    • 当用户登录时将用户信息存入session中,访问页面时首先判断session中有没有用户的信息
    • 如果没有,拦截器进行拦截;
    • 如果有,拦截器放行

    因此我们首先在LoginController中当用户登录成功后,存入用户信息到session中

    package com.zsr.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.servlet.http.HttpSession;
    
    @Controller
    public class LoginController {
        @RequestMapping("/user/login")
        public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session) {
            //如果用户名和密码正确
            if ("admin".equals(username) && "123456".equals(password)) {
                session.setAttribute("LoginUser", username);
                return "redirect:/main.html";//重定向到main.html页面,也就是跳转到dashboard页面
            }
            //如果用户名或者密码不正确
            else {
                model.addAttribute("msg", "用户名或者密码错误");//显示错误信息
                return "index";//跳转到首页
            }
        }
    }

    然后再在实现自定义的登录拦截器,继承HandlerInterceptor接口

    • 其中获取存入的session进行判断,如果不为空,则放行;
    • 如果为空,则返回错误消息,并且返回到首页,不放行。
    package com.zsr.config;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.concurrent.ForkJoinPool;
    
    public class LoginHandlerInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            //用户登录成功后,应该有自己的session
            Object session = request.getSession().getAttribute("LoginUser");
            if (session == null) {
                request.setAttribute("msg", "权限不够,请先登录");
                request.getRequestDispatcher("/index.html").forward(request, response);
                return false;
            } else {
                return true;
            }
        }
    }

    然后配置到bean中注册,在MyMvcConfig配置类中,重写关于拦截器的方法,添加我们自定义的拦截器,注意屏蔽静态资源及主页以及相关请求的拦截

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index.html", "/", "/user/login", "/css/**", "/js/**", "/img/**");
    }

    然后重启主程序进行测试,直接访问http://localhost:8080/main.html

    49e640f04ac7bd1ccfed70e32078d67e.png

    提示权限不够,请先登录,我们登录一下

    49e640f04ac7bd1ccfed70e32078d67e.png

    进入到dashboard页面

    如果我们再直接重新访问http://localhost:8080/main.html,也可以直接直接进入到dashboard页面,这是因为session里面存入了用户的信息,拦截器放行通过

    49e640f04ac7bd1ccfed70e32078d67e.png

    (六)展示员工信息——查

    1. 实现Customers视图跳转

    目标:点击dashboard.html页面中的Customers展示跳转到list.html页面显示所有员工信息

    0e79b601cb8be49c86924ff22e20ee03.png

    因此,我们首先给dashboard.html页面中Customers部分标签添加href属性,实现点击该标签请求/emps路径跳转到list.html展示所有的员工信息

    a5fd6eb3122bb1f82f96fe0b056a92c4.png
    <li class="nav-item">
        <a class="nav-link" th:href="@{/emps}">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                 stroke-linejoin="round" class="feather feather-users">
                <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                <circle cx="9" cy="7" r="4"></circle>
                <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
            </svg>
            Customers
        </a>
    </li>

    同样修改list.html对应该的代码为上述代码

    ac6aaa1e196b37b2f2013049c604acaa.png

    我们在templates目录下新建一个包emp,用来放所有关于员工信息的页面,我们将list.html页面移入该包中

    49e640f04ac7bd1ccfed70e32078d67e.png

    然后编写请求对应的controller,处理/emps请求,在controller包下,新建一个EmployeeController

    package com.zsr.controller;
    
    import com.zsr.dao.EmployeeDao;
    import com.zsr.pojo.Employee;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import java.util.Collection;
    
    @Controller
    public class EmployeeController {
        @Autowired
        private EmployeeDao employeeDao;
    
        @RequestMapping("/emps")
        public String list(Model model) {
            Collection<Employee> employees = employeeDao.getAllEmployees();
            model.addAttribute(employees);
            return "emp/list";//返回到list页面
        }
    }

    然后我们重启主程序进行测试,登录到dashboard页面,再点击Customers,成功跳转到/emps

    49e640f04ac7bd1ccfed70e32078d67e.png

    但是有些问题

    1. 我们点击了Customers后,它应该处于高亮状态,但是这里点击后还是普通的样子,高亮还是在Dashboard
    2. list.htmldashboard.html页面的侧边栏和顶部栏是相同的,可以抽取出来

    2. 提取页面公共部分

    templates目录下新建一个commons包,其中新建commons.html用来放置公共页面代码

    49e640f04ac7bd1ccfed70e32078d67e.png

    利用th:fragment标签抽取公共部分(顶部导航栏和侧边栏)

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
        
    <!--顶部导航栏,利用th:fragment提取出来,命名为topbar-->
    <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
        <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company
            name</a>
        <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
        <ul class="navbar-nav px-3">
            <li class="nav-item text-nowrap">
                <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign out</a>
            </li>
        </ul>
    </nav>
        
    <!--侧边栏,利用th:fragment提取出来,命名为sidebar-->
    <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="siderbar">
        <div class="sidebar-sticky">
            <ul class="nav flex-column">
                <li class="nav-item">
                    <a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-home">
                            <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path>
                            <polyline points="9 22 9 12 15 12 15 22"></polyline>
                        </svg>
                        Dashboard <span class="sr-only">(current)</span>
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file">
                            <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path>
                            <polyline points="13 2 13 9 20 9"></polyline>
                        </svg>
                        Orders
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-shopping-cart">
                            <circle cx="9" cy="21" r="1"></circle>
                            <circle cx="20" cy="21" r="1"></circle>
                            <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
                        </svg>
                        Products
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" th:href="@{/emps}">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-users">
                            <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
                            <circle cx="9" cy="7" r="4"></circle>
                            <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
                            <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
                        </svg>
                        Customers
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-bar-chart-2">
                            <line x1="18" y1="20" x2="18" y2="10"></line>
                            <line x1="12" y1="20" x2="12" y2="4"></line>
                            <line x1="6" y1="20" x2="6" y2="14"></line>
                        </svg>
                        Reports
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-layers">
                            <polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
                            <polyline points="2 17 12 22 22 17"></polyline>
                            <polyline points="2 12 12 17 22 12"></polyline>
                        </svg>
                        Integrations
                    </a>
                </li>
            </ul>
    
            <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
                <span>Saved reports</span>
                <a class="d-flex align-items-center text-muted"
                   href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                    <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                         stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
                         class="feather feather-plus-circle">
                        <circle cx="12" cy="12" r="10"></circle>
                        <line x1="12" y1="8" x2="12" y2="16"></line>
                        <line x1="8" y1="12" x2="16" y2="12"></line>
                    </svg>
                </a>
            </h6>
            <ul class="nav flex-column mb-2">
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Current month
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Last quarter
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Social engagement
                    </a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
                        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                             fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                             stroke-linejoin="round" class="feather feather-file-text">
                            <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
                            <polyline points="14 2 14 8 20 8"></polyline>
                            <line x1="16" y1="13" x2="8" y2="13"></line>
                            <line x1="16" y1="17" x2="8" y2="17"></line>
                            <polyline points="10 9 9 9 8 9"></polyline>
                        </svg>
                        Year-end sale
                    </a>
                </li>
            </ul>
        </div>
    </nav>
    </html>

    然后删除dashboard.htmllist.html中顶部导航栏和侧边栏的代码

    49e640f04ac7bd1ccfed70e32078d67e.png

    我们再次重启主程序测试一下,登陆成功后,可以看到已经没有了顶部导航栏和侧边栏

    72cdfbc355f938e78b4816e60215516d.png

    这是因为我们删除了公共部分,还没有引入,我们分别在dashboard.htmllist.html删除的部分插入提取出来的公共部分topbarsidebar

    <!--顶部导航栏-->
    <div th:replace="~{commons/commons::topbar}" }></div>
    <!--侧边栏-->
    <div th:replace="~{commons/commons::siderbar}"></div>

    89a0c3cf2cb72975228404bb0a087a82.png

    再次重启主程序进行测试,登陆成功后,成功看到侧边栏和顶部栏,代表我们插入成功

    fe25c871146f4b95e3295da6676540b5.png

    3. 点击高亮处理

    在页面中,使高亮的代码是class="nav-link active"属性

    756e2becb403a0ff3af9701134c02a91.png

    我们可以传递参数判断点击了哪个标签实现相应的高亮

    首先在dashboard.html的侧边栏标签传递参数activedashboard.html

    49e640f04ac7bd1ccfed70e32078d67e.png
    <!--侧边栏-->
    <div th:replace="~{commons/commons::siderbar(active='dashboard.html')}"></div>

    同样在list.html的侧边栏标签传递参数activelist.html

    49e640f04ac7bd1ccfed70e32078d67e.png
    <!--侧边栏-->
    <div th:replace="~{commons/commons::siderbar(active='list.html')}"></div>

    然后我们在公共页面commons.html相应标签部分利用thymeleaf接收参数active,利用三元运算符判断决定是否高亮

    49e640f04ac7bd1ccfed70e32078d67e.png

    再次重启主程序测试,登录成功后,首先Dashboard高亮

    49e640f04ac7bd1ccfed70e32078d67e.png

    再点击CustomersCustomers高亮,成功

    49e640f04ac7bd1ccfed70e32078d67e.png

    4. 显示员工信息

    修改list.html页面,显示我们自己的数据值

    49e640f04ac7bd1ccfed70e32078d67e.png

    修改完成后,重启主程序,登录完成后查看所有员工信息,成功显示

    49e640f04ac7bd1ccfed70e32078d67e.png

    接下来修改一下性别的显示和date的显示,并添加编辑删除两个标签,为后续做准备

    <thead>
        <tr>
            <th>id</th>
            <th>lastName</th>
            <th>email</th>
            <th>gender</th>
            <th>department</th>
            <th>date</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="emp:${emps}">
            <td th:text="${emp.getId()}"></td>
            <td th:text="${emp.getLastName()}"></td>
            <td th:text="${emp.getEmail()}"></td>
            <td th:text="${emp.getGender()==0?'女':'男'}"></td>
            <td th:text="${emp.getDepartment().getDepartmentName()}"></td>
            <td th:text="${#dates.format(emp.getDate(),'yyyy-MM-dd HH:mm:ss')}"></td>
            <td>
                <a class="btn btn-sm btn-primary">编辑</a>
                <a class="btn btn-sm btn-danger">删除</a>
            </td>
        </tr>
    </tbody>

    再次重启主程序测试,成功

    49e640f04ac7bd1ccfed70e32078d67e.png

    (七)增加员工实现——增

    1. list页面增加添加员工按钮

    首先在list.html页面增添一个增加员工按钮,点击该按钮时发起一个请求/add

    977f512370da8ce674ddf1a2cda6eed3.png
    <h2><a class="btn btn-sm btn-success" th:href="@{/add}">添加员工</a></h2>

    558455235094a41b820340ffac29af3e.png

    然后编写对应的controller,处理点击添加员工的请求

    这里通过get方式提交请求,在EmployeeController中添加一个方法add用来处理list页面点击提交按钮的操作,返回到add.html添加员工页面,我们即将创建

    @GetMapping("/add")
    public String add(Model model) {
        //查出所有的部门信息,添加到departments中,用于前端接收
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "emp/add";//返回到添加员工页面
    }

    2. 创建添加员工页面add

    templates/emp下新建一个add.html

    9cf803143cbdc1f9d48e40fd59f96c21.png

    我们复制list.html中的内容,修改其中表格为:

    <form>
        <div class="form-group">
            <label>LastName</label>
            <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
        </div>
        <div class="form-group">
            <label>Email</label>
            <input type="email" name="email" class="form-control" placeholder="email:xxxxx@qq.com">
        </div>
        <div class="form-group">
            <label>Gender</label><br/>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="gender" value="1">
                <label class="form-check-label">男</label>
            </div>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="gender" value="0">
                <label class="form-check-label">女</label>
            </div>
        </div>
        <div class="form-group">
            <label>department</label>
            <!--注意这里的name是department.id,因为传入的参数为id-->
            <select class="form-control" name="department.id">
                <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
            </select>
        </div>
        <div class="form-group">
            <label>Birth</label>
            <!--springboot默认的日期格式为yy/MM/dd-->
            <input type="text" name="date" class="form-control" placeholder="birth:yyyy/MM/dd">
        </div>
        <button type="submit" class="btn btn-primary">添加</button>
    </form>

    我们重启主程序看看

    9ede524211a2e989d1593974232bfb98.png

    点击添加员工,成功跳转到add.html页面

    91dfbd4452d2f301cbe8955b1e329592.png

    下拉框中的内容不应该是1、2、3、4、5;应该是所有的部门名,我们遍历得到

    <!--其中th:value用来表示部门的id,我们实际传入的值为id-->
    <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>

    重启测试,成功显示所有部门

    c9f76fd556b69c2673d39b04bbdc94f9.png

    到此,添加员工页面编写完成

    3. add页面添加员工请求

    add.html页面,当我们填写完信息,点击添加按钮,应该完成添加返回到list页面,展示新的员工信息;因此在add.html点击添加按钮的一瞬间,我们同样发起一个请求/add,与上述提交按钮发出的请求路径一样,但这里发出的是post请求

    0a37e02606a872661959a02a554c6019.png

    然后编写对应的controller,同样在EmployeeController中添加一个方法addEmp用来处理点击添加按钮的操作

    @PostMapping("/add")
    public String addEmp(Employee employee) {
        employeeDao.addEmployee(employee);//添加一个员工
        return "redirect:/emps";//重定向到/emps,刷新列表,返回到list页面
    }

    我们重启主程序,进行测试,进入添加页面,填写相关信息,注意日期格式默认为yyyy/MM/dd

    e651349bacc6b6aa2682b56c5af466d0.png

    然后点击添加按钮,成功实现添加员工

    388b29a3e610f3014d5c667e786e3886.png

    我们也可以添加多个员工

    70add5b5ac0e94ed8e0ddba822285c0d.png

    (八)修改员工信息——改

    1. list页面编辑按钮增添请求

    56c30bc9acce892ee5e88091226cae50.png

    当我们点击编辑标签时,应该跳转到编辑页面edit.html(我们即将创建)进行编辑
    因此首先将list.html页面的编辑标签添加href属性,实现点击请求/edit/id号到编辑页面

    <a class="btn btn-sm btn-primary" th:href="@{/edit/{id}(id=${emp.getId()})}">编辑</a>

    754e220970808aab4e20adfb5a2beba2.png

    然后编写对应的controller,在EmployeeController中添加一个方法edit用来处理list页面点击编辑按钮的操作,返回到edit.html编辑员工页面,我们即将创建

    //restful风格接收参数
    @RequestMapping("/edit/{id}")
    public String edit(@PathVariable("id") int id, Model model) {
        //查询指定id的员工,添加到empByID中,用于前端接收
        Employee employeeByID = employeeDao.getEmployeeByID(id);
        model.addAttribute("empByID", employeeByID);
        //查出所有的部门信息,添加到departments中,用于前端接收
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "/emp/edit";//返回到编辑员工页面
    }

    2. 创建编辑员工页面edit

    templates/emp下新建一个edit.html

    870c032d19811912656bc476cf2e7349.png

    复制add.html中的代码,稍作修改

    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <form th:action="@{/edit}" method="post">
            <div class="form-group">
                <label>LastName</label>
                <input th:value="${empByID.getLastName()}" type="text" name="lastName" class="form-control"
                       placeholder="lastname:zsr">
            </div>
            <div class="form-group">
                <label>Email</label>
                <input th:value="${empByID.getEmail()}" type="email" name="email" class="form-control"
                       placeholder="email:xxxxx@qq.com">
            </div>
            <div class="form-group">
                <label>Gender</label><br/>
                <div class="form-check form-check-inline">
                    <input th:checked="${empByID.getGender()==1}" class="form-check-input" type="radio"
                           name="gender" value="1">
                    <label class="form-check-label">男</label>
                </div>
                <div class="form-check form-check-inline">
                    <input th:checked="${empByID.getGender()==0}" class="form-check-input" type="radio"
                           name="gender" value="0">
                    <label class="form-check-label">女</label>
                </div>
            </div>
            <div class="form-group">
                <label>department</label>
                <!--注意这里的name是department.id,因为传入的参数为id-->
                <select class="form-control" name="department.id">
                    <option th:selected="${department.getId()==empByID.department.getId()}"
                            th:each="department:${departments}" th:text="${department.getDepartmentName()}"
                            th:value="${department.getId()}">
                    </option>
                </select>
            </div>
            <div class="form-group">
                <label>Birth</label>
                <!--springboot默认的日期格式为yy/MM/dd-->
                <input th:value="${empByID.getDate()}" type="text" name="date" class="form-control"
                       placeholder="birth:yy/MM/dd">
            </div>
            <button type="submit" class="btn btn-primary">修改</button>
        </form>
    </main>

    启动主程序测试,点击编辑1号用户

    6b0a3caa1d051e4f7c5554506138a2cf.png

    成功跳转到edit.html,且所选用户信息正确

    c7899316e46a99f91a383d3da4f0d60c.png

    但是日期的格式不太正确,我们规定一下显示的日期格式

    <!--springboot默认的日期格式为yy/MM/dd-->
    <input th:value="${#dates.format(empByID.getDate(),'yyyy/MM/dd')}" type="text" name="date" class="form-control"
           placeholder="birth:yy/MM/dd">

    3. edit页面编辑完成提交请求

    edit.html点击修改按钮的一瞬间,我们需要返回到list页面,更新员工信息,因此我们需要添加href属性,实现点击按钮时发起一个请求/edit

    7fe98e0450b54442b507e66028f7a70a.png

    然后编写对应的controller,处理点击修改按钮的请求

    同样在EmployeeController中添加一个方法EditEmp用来处理edit页面点击添加的操作

    @PostMapping("/add")
    public String EditEmp(Employee employee) {
        employeeDao.addEmployee(employee);//添加一个员工
        return "redirect:/emps";//添加完成重定向到/emps,刷新列表
    }

    然后指定修改人的id

    <input type="hidden" name="id" th:value="${empByID.getId()}">

    重启测试,同样修改1号用户名称为dddd

    4fdf946c5d33eb42fbe670d18d4fd09d.png

    然后点击修改

    faaff64e477137f4ca61c548c28e2703.png

    成功修改并返回到list.html

    (九)删除员工信息——删

    2a96ccd2a58c0318d38aed01e6367de4.png

    当我们点击删除标签时,应该发起一个请求,删除指定的用户,然后重新返回到list页面显示员工数据

    <a class="btn btn-sm btn-success" th:href="@{/delete/{id}(id=${emp.getId()})}">删除</a>

    然后编写对应的controller,处理点击删除按钮的请求,删除指定员工,重定向到/emps请求,更新员工信息

    @GetMapping("/delete/{id}")
    public String delete(@PathVariable("id") Integer id) {
        employeeDao.deleteEmployeeByID(id);
        return "redirect:/emps";
    }

    重启测试,点击删除按钮即可删除指定员工

    (十)404页面定制

    只需要在templates目录下新建一个error包,然后将404.html放入其中,报错SpringBoot就会自动找到这个页面

    c3fb11595246eb739b636642b4c53998.png

    我们可以启动程序测试,随便访问一个不存在的页面

    6b21f1398da3e6554cf7568091ed119c.png

    出现的404页面即是我们自己的404.html

    (十一)注销操作

    在我们提取出来的公共commons页面,顶部导航栏处中的标签添加href属性,实现点击发起请求/user/logout

    b7040e604573a1f54c73c10c185f038e.png

    然后编写对应的controller,处理点击注销标签的请求,在LoginController中编写对应的方法,清除session,并重定向到首页

    @RequestMapping("/user/logout")
    public String logout(HttpSession session) {
        session.invalidate();
        return "redirect:/index.html";
    }

    重启测试,登录成功后,点击log out即可退出到首页

    af431304bd4c685f68e5d868e11c1765.png

    静态资源:

    百度云链接:https://pan.baidu.com/s/1om3FItYstCmnyKD4lGsDTA
    提取码:pxpb

    8378e5094547711125ef236e7b4e7ae9.png

    员工管理系统最终完整项目资源:

    百度云链接:https://pan.baidu.com/s/162RdDOCuNWm0JDqShucLGA
    提取码:vt2x

    494c260eff81c4a32336d8125610c4d8.png

    dfb262eccda4fd84e8a8f636d69cac55.png
    本员工管理系统基于狂神老师的SpringBoot教程:https://www.bilibili.com/video/BV1PE411i7CV?p=20
    作者:Baret-H
    链接:https://blog.csdn.net/qq_45173404/article/details/108934414
    来源:CSDN博客
    展开全文
  • 直接上代码不解释,文章末尾有惊喜!typedef struct book{ char*name; int id; int count; int status; structbook *next;Book *head = NULL;intnbook =0;int main(){ welcome(); return0;}void welcome(){ p...

    108cab7128e5bf824454871d5845b426.png

    直接上代码不解释,文章末尾有惊喜!

    a79544a59d937e0a8ddefab3f2eaca26.png


    typedef struct book{

    char*name;

    int id;

    int count;

    int status;

    structbook *next;

    f183191f15910222e01c54a61ab56876.png

    Book *head = NULL;

    intnbook =0;

    int main(){

    welcome();

    return0;

    }void welcome(){

    printf("*************************欢迎使用图书管理系统*************************n");

    while(1){

    printf("-------------------------------------n");

    printf("请输入项目前编号执行相关操作:n");

    printf("n[1] 管理员入口nn[2] 学生入口nn[0] 退出nn");

    switch(land()){

    case'1':

    while(1){

    if(usrmanager()){

    continue;

    }else{

    break;

    }

    }

    break;

    case'2':

    while(1){

    if(usrstudent()){

    continue;

    }else{

    break;

    }

    }

    break;

    case'0':

    exit(0);

    default:

    error("采集项目参数失败");

    }

    }

    }char land(){

    char c, flag;

    int i;

    while(1){

    for(i =0; (c = getchar()) !='n'; flag = c, ++i)

    ;

    if(i ==1){

    if(flag =='1'|| flag =='2'|| flag =='0'){

    return flag;

    }

    }elseif(i ==0){

    continue;

    }

    error("[请输入命令: 1 / 2 / 0 选择项目]");

    }

    return'0';

    }voiderror(char*s){

    fprintf(stderr, "%sn", s);

    }char dostudent(){

    char c, flag;

    int i;

    while(1){

    for(i =0; (c = getchar()) !='n'; flag = c, ++i )

    ;

    if(i ==1){

    if(flag =='1'|| flag =='2'|| flag =='3'|| flag =='0'){

    return flag;

    }

    }elseif(i ==0){

    continue;

    }

    error("[请输入命令: 1 / 2 / 3/ 0 选择项目]");

    }

    }int usrstudent(){

    printf("-------------------------------------n");

    printf("请输入项目前编号执行相关操作:nn");

    printf("[1] 借阅图书n[2] 归还图书n[3] 查看馆存图书n");

    printf("[0] 返回主页n");

    switch(dostudent()){

    case'1':

    borrowbook();

    break;

    case'2':

    returnbook();

    break;

    case'3':

    viewbook();

    break;

    case'0':

    return0;

    default:

    error("采集项目参数失败");

    break;

    }

    return1;

    }int borrowbook(){

    system(CLEAR);

    char sname[MAXNAME];

    printf("搜索图书:n");

    scanf("%s", sname);

    getchar();

    Book *bp;

    char*namep = NULL;

    if((bp = lookup(namep = strdup(sname),0)) == NULL){

    printf("图书 %s 不存在n", namep);

    fflush(stdin);

    return-1;

    }else{

    printbook(bp);

    if(! bp->status){

    printf("借阅图书: Y Nn");

    }else{

    printf("图书外借中n");

    return0;

    }

    while(1){

    charc ='0';

    scanf("%c", &c);

    getchar();

    if(!(c =='Y'|| c =='y'|| c =='N'|| c =='n')){

    fflush(stdin);

    error("指令无效");

    continue;

    }

    if(c =='Y'|| c =='y'){

    --bp->count > -1? printf("成功n") : printf("失败n");

    if(! bp->count){

    bp->status = UNAVAILABLE;

    }

    returnbp->id;

    }else{

    return0;

    }

    }

    }

    return-1;

    }int returnbook(){

    system(CLEAR);

    char sname[MAXNAME];

    printf("归还图书:n");

    scanf("%s", sname);

    getchar();

    Book *bp;

    char*namep;

    if((bp = lookup(namep = strdup(sname),0)) == NULL){

    fflush(stdin);

    printf("此图书并不是馆内图书n");

    return-1;

    }else{

    printbook(bp);

    printf("归还图书: Y Nn");

    while(1){

    charc ='0';

    scanf("%c", &c);

    getchar();

    if(!(c =='Y'|| c =='y'|| c =='N'|| c =='n')){

    fflush(stdin);

    error("指令无效");

    }elseif(c =='Y'|| c =='y'){

    bp->count++ ? : (bp->status = AVAILABLE);

    printf("成功n");

    returnbp->id;

    }else{

    return0;

    }

    }

    }

    return-1;

    }char domanager(){

    char c, flag;

    int i;

    while(1){

    for(i =0; (c = getchar()) !='n'; flag = c, ++i)

    ;

    if(i ==1){

    if(flag =='1'|| flag =='2'|| flag =='3'|| flag =='4'|| flag =='0'){

    return flag;

    }

    }elseif(i ==0){

    continue;

    }

    error("[请输入命令: 1 / 2 / 3/ 4 / 0 选择项目]");

    }

    return'0';

    }int usrmanager(){

    printf("-------------------------------------n");

    printf("请输入项目前编号执行相关操作:nn");

    printf("[1] 查看已存图书n[2] 注册新图书n[3] 修改已有图书信息n[4] 注销现存图书n");

    printf("[0] 返回主页n");

    switch(domanager()){

    case'1':

    viewbook();

    break;

    case'2':

    addbook();

    break;

    case'3':

    modbook();

    break;

    case'4':

    delbook();

    break;

    case'0':

    return0;

    default:

    error("采集项目参数失败");

    break;

    }

    return1;

    }int viewbook(){

    system(CLEAR); ///Book *bp;

    for(bp =head; bp != NULL; bp = bp->next){

    printbook(bp);

    }

    if(nbook){

    return nbook;

    }else{

    printf("图书馆还没有图书哦n");

    return0;

    }

    return-1;

    }voidprintbook(Book *bp){

    printf("图书编号:%dn", bp->id);

    printf("图书名:%sn", bp->name);

    printf("图书存数:%dn", bp->count);

    printf("图书状态:%sn", bp->status ?"不可借":"可借");

    }int addbook(){

    system(CLEAR); ///char sname[MAXNAME];

    int id, count;

    char*namep;

    while(1){

    id =0;

    count =0;

    namep = NULL;

    printf("请输入图书名:n");

    scanf("%s", sname);

    getchar();

    printf("请输入小于1000的图书编号:n");

    scanf("%d", &id);

    getchar();

    if(!(id >0&& id <1000)){

    error("输入编号不符合要求");

    fflush(stdin);

    continue;

    }

    printf("请输入小于50的图书数量:n");

    scanf("%d", &count);

    getchar();

    if(!(count >0&& count <50)){

    error("输入数目不符合要求");

    fflush(stdin);

    continue;

    }

    Book *bp;

    if((bp = lookup(namep = strdup(sname), id)) == NULL){

    bp = (Book *)malloc(sizeof (Book));

    if(bp == NULL || (bp->name = namep) == NULL){

    error("注册图书失败");

    fflush(stdin);

    continue;

    }

    bp->id = id;

    bp->count = count;

    bp->status = AVAILABLE;

    ++nbook;

    bp->next = head;

    head = bp;

    printf("图书 %s 注册成功n", sname);

    return id;

    }else{

    fflush(stdin);

    error("图书名或编号已经被占用");

    }

    }

    return-1;

    }

    Book *lookup(char*s,int id){

    Book *bp;

    for(bp = head; bp != NULL; bp = bp->next){

    if(id == bp->id || strcmp(s, bp->name) ==0){

    return bp;

    }

    }

    return NULL;

    }intmodbook(){// 修改除id之外的信息 因为要修改id 直接删掉后重新添加会更好system(CLEAR);///Book *bp = NULL;

    while(1){

    intid =0;

    printf("请输入要修改信息的图书编号:n");

    scanf("%d", &id);

    getchar();

    if(!(id >0&& id <1000)){

    fflush(stdin);

    error("图书编号不符合要求");

    continue;

    }

    if((bp = lookup("", id)) != NULL){

    printf("以下为要修改的图书的现有信息:n");

    printbook(bp);

    printf("[1] 修改图书名n[2] 修改图书数量n[3] 修改图书状态n[0] 放弃修改n");

    while(1){

    int i;

    scanf("%d", &i);

    getchar();

    if(!(i ==1|| i ==2|| i ==3|| i ==0)){

    fflush(stdin);

    error("输入项目前编号选择相关项目");

    continue;

    }

    switch(i){

    case1:

    while(1){

    charnewname[MAXNAME], *namep;

    printf("图书名:n");

    scanf("%s", newname);

    getchar();

    if((lookup(namep = strdup(newname),0)) != NULL){

    fflush(stdin);

    error("图书名已存在");

    continue;

    }

    if((bp->name = namep) != NULL){

    printf("修改成功n");

    return id;

    }

    }

    break;

    case2:

    while(1){

    int bnum;

    printf("输入图书数量:n");

    scanf("%d", &bnum);

    getchar();

    if(!(bnum >0|| bnum <1000)){

    fflush(stdin);

    error("数目不符合要求");

    continue;

    }

    bp->count = bnum;

    printf("修改成功n");

    return id;

    }

    break;

    case3:

    while(1){

    char c;

    int status;

    status = bp->status;

    status == AVAILABLE ? printf("修改状态为[不可借]: Y Nn") : printf("修改状态为[可借]: Y Nn");

    scanf("%c", &c);

    getchar();

    if(!( c =='Y'|| c =='y'|| c =='N'|| c =='n')){

    fflush(stdin);

    error("指令无效");

    continue;

    }

    if(c =='Y'|| c =='y'){

    status == AVAILABLE ? bp->status = UNAVAILABLE : (bp->status = AVAILABLE);

    printf("修改成功n");

    return id;

    }else{

    printf("放弃修改n");

    return0;

    }

    }

    break;

    case0:

    return0;

    default:

    error("获取参数失败");

    break;

    }

    }

    }else{

    printf("没有此图书n");

    return0;

    }

    }

    return-1;

    }int delbook(){

    system(CLEAR); ///Book *bp, *fbp;

    intid =0;

    printf("请输入要注销的图书编号:n");

    scanf("%d", &id);

    getchar();

    if(!(id >0&& id <1000)){

    fflush(stdin);

    error("图书编号不符合要求");

    return-1;

    }

    for(fbp = bp = head; bp != NULL; fbp = bp, bp = bp->next){

    if(bp->id == id){

    printbook(bp);

    break;

    }

    }

    if(bp != NULL){

    while(1){

    printf("确定要注销此图书:Y Nn");

    char YN;

    scanf("%c", &YN);

    getchar();

    if(!((YN =='Y'|| YN =='y') || (YN =='N'|| YN =='n'))){

    fflush(stdin);

    error("指令无效n");

    continue;

    }

    if(YN =='Y'|| YN =='y'){

    bp == head ? head = bp->next : (fbp->next = bp->next);

    free(bp->name); //2018-05-26修改:添加释放name这块内存//bp内存的只是指向name的指针

    free(bp);

    bp = NULL; printf(

    "图书注销成功n");

    break;

    }else{

    printf("放弃注销n");

    return0;

    }

    }

    return id;

    }else{

    printf("要注销的图书编号不存在n");

    return-1;

    }

    return-1;

    }

    ------------------------------------------------

    “我是一名从事了10年开发的老程序员,最近我花了一些时间整理关于C语言、C++,自己有做的材料的整合,一个完整的学习C语言、C++的路线,学习材料和工具。全球最大的C/C++、编程爱好者的聚集地就在我这里<专栏里面>!欢迎初学和进阶中的小伙伴。希望你也能凭自己的努力,成为下一个优秀的程序员。工作需要、感兴趣、为了入行、转行需要学习C/C++的伙伴可以跟我一起学习!”

    关注我和我的专栏,带你遨游代码世界!

    程序猿zhuanlan.zhihu.com
    f849150ec7770ca8065b590cb84c489c.png
    展开全文
  • 员工管理系统代码

    2021-01-16 11:40:11
    员工管理系统代码
  • 呼叫代码员工管理系统
  • Delphi员工档案管理系统代码,包括了档案浏览、档案打印以及简历管理等功能。本实例来自一本Delphi数据库实例书籍的随书光盘,未经允许,不得商用。
  • 项目截图: 数据库脚本:CREATE DATABASE `company` default charset=utf8; CREATE TABLE `employer` ( `id` int(20) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) ... 由最代码官方编辑于2016-10-9 9:59:42

    项目截图:

    5f44669d9023f819b9cfa4031178418d.png

    数据库脚本:CREATE DATABASE `company` default charset=utf8;

    CREATE TABLE `employer` (

    `id` int(20) unsigned NOT NULL AUTO_INCREMENT,

    `name` varchar(20) NOT NULL,

    `address` varchar(50) NOT NULL,

    `create_date` date NOT NULL,

    PRIMARY KEY (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

    配置文件:

    pom.xml

    4.0.0

    dyl

    ssh

    0.0.1-SNAPSHOT

    http://maven.apache.org

    java

    java official repository

    http://download.java.net/maven/2/

    com.sun

    tools

    1.5.0

    org.apache.struts

    struts2-core

    2.1.8

    org.apache.struts

    struts2-spring-plugin

    2.1.8

    mysql

    mysql-connector-java

    5.1.9

    org.springframework

    spring

    2.5.6

    org.springframework

    spring-web

    2.5.6

    org.hibernate

    hibernate

    3.2.7.ga

    dom4j

    dom4j

    1.6.1

    commons-logging

    commons-logging

    1.1.1

    commons-collections

    commons-collections

    3.2.1

    cglib

    cglib

    2.1

    antlr

    antlr

    2.7.7

    javassist

    javassist

    3.12.0.GA

    web.xml<?xml version="1.0" encoding="UTF-8"?>

    index.html

    structs-tags

    org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter

    structs-tags

    /*

    org.springframework.web.context.ContextLoaderListener

    contextConfigLocation

    /WEB-INF/classes/SpringBeans.xml

    struts.xml<?xml version="1.0" encoding="UTF-8" ?>

    /p>

    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"

    "http://struts.apache.org/dtds/struts-2.0.dtd">

    class="employerAction" method="addEmployer" >

    /WEB-INF/views/Employer.jsp

    class="employerAction" method="listCustomer" >

    /WEB-INF/views/Employer.jsp

    springBeans.xml<?xml version="1.0" encoding="UTF-8"?>

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    运行截图:

    5f44669d9023f819b9cfa4031178418d.png

    由最代码官方编辑于2016-10-9 9:59:42

    展开全文
  • 对于员工的增加、删除、更新、查询和添加功能
  • 员工管理系统的源代码

    热门讨论 2011-07-19 22:11:10
    关于员工管理系统的源程序,包括各种模块,信息,工资哦等等
  • 系统拥有鲜明的层次结构,代码注释详细,便于广大读者阅读、学习。
  • package dao.jdbc;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.util.ArrayList;import util.DBConfig;import dao.UserDAO;import entity.User;...
  • 分享:C语言学生成绩管理系统设计《C语言程序设计》实训报告 扫描下方公众号,发送成绩系统4个字,获取下载实训源码。 ... ...回复系统大全,即可获得关于...今天分享:C语言员工信息管理系统代码 /************...
  • PAGE PAGE # / 16 数据结构 -员工管理信息系统源程序 #include<iostream> #include<string> #include <stdlib.h> #include <stdio.h> using namespace std; #define OK 1 #define ERROR 0 #define OVERFLOW -2 ...
  • 员工管理系统

    2019-04-10 20:17:05
    基于ssh框架开发的员工管理系统完整代码(前台+后台)
  • 数据结构 - 员工管理信息系统源程序 #include<iostream> #include<string> #include <stdlib.h> #include <stdio.h> using namespace std; #define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef int Status; ...
  • 这是一份关于公司员工管理系统的源代码,是以前上课时候的综合实验内容,希望对大家有所帮助。
  • C员工工资管理系 统源代码 #include <iomanip> #include <iostream> #include <fstream> #include <malloc.h> #include <stdlib.h> #include <string.h> using namespace std; #define NULL 0 #define LEN sizeof...
  • SSH员工管理系统以及Demo代码下载 源代码下载地址:http://www.zuidaima.com/share/1839695185513472.htm 项目截图: 数据库脚本: CREATE DATABASE `company` default charset=utf8; CREATE TABLE ...
  • 员工管理系统基于狂神老师的SpringBoot教程:https://www.bilibili.com/video/BV1PE411i7CV?p=20项目成效图:(一)环境搭建1. 新建一个SpringBoot项目选择配件时勾选SpringWeb和Thymeleaf 点击next,然后finish...
  • jsp+servlet+mysql员工管理系统代码下载 源代码下载地址:http://www.zuidaima.com/share/1550463498996736.htm
  • C 语言企业员工管理系统 代码部分 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct employee { int num; char duty[10]; char name[10]; char sex[3]; unsigned char age; char edu[10]...
  • C 语言企业员工管理系统 代码部分 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct employee { int num; char duty[10]; char name[10]; char sex[3]; unsigned char age; char edu[10]...
  • C语言企业员工管理系统 代码部分 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct employee { int num; char duty[10]; char name[10]; char sex[3]; unsigned char age; char edu[10];...
  • 用jsp语言来实现员工工资管理系统 其中包括员工信息的查询、员工修改自己的信息 另一部分是管理员修改、添加、删除、查询等信息
  • 非常完整的员工管理系统代码,比较实用,值得下载看看。
  • JAVA JAVA 程序设计 课程设计报告 同组姓名 课 题: 学生信息管理系统 姓 学 名 号 指导教师设计时间 指导教师 设计时间 评阅意见 目 录 一系统描述 2 1需要实现的功能 3 2设计目的 3 二分析与设计 3 1功能模块划分 3...
  • 利用C语言实现公司管理员工工资的高效程序,也可以通过自己的改变增加新的功能,可以转化成任何信息规律程序,例如图书管理系统,学生信息管理系统
  • jsp+servlet+mysql员工管理系统项目截图注册页面登录页面添加员工编辑员工员工列表数据库建表语句/*Navicat MySQL Data TransferSource Server : localhostSource Server Version : 50509Source Host : localhost:...
  • 员工管理系统源码

    2016-11-17 15:42:51
    员工管理系统代码,SSH框架,数据库使用MySQL。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 995
精华内容 398
关键字:

员工管理系统代码