c语言员工管理系统代码_SpringBoot员工管理系统(附完整代码及实现教程)

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/1om3FIt YstCmnyKD4lGsDTA
提取码:pxpb

d7e0a926ac7c685df969e5dedd1cb8b1.png

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

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

1aa2c34dadb903ad8232ff2d4858c4f1.png

7e0fe16a1038a43f02f6d0ef245bfea7.png
作者:Baret-H
链接: https:// blog.csdn.net/qq_451734 04/article/details/108934414
来源:CSDN博客
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值