commit 76cdec07b746d54c0d98b0ae9677d37dd5fddfe4
Author: Administrator <895938232@qq.com>
Date: Tue May 3 14:15:23 2022 +0800
hm
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4b1d0e3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,29 @@
+# Compiled class file
+*.class
+classes/
+
+# Log file
+*.log
+target/
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+*.iml
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+.idea/
+.settings/
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..639c818
--- /dev/null
+++ b/README.md
@@ -0,0 +1,84 @@
+# health-manager
+
+#### 介绍
+个人健康信息管理系统
+
+#### 软件架构
+spring+springmvc+mybatis+mysql+jsp+bootstrap
+
+#### 安装教程
+1. 数据库导入sql文件
+2. eclipse导入maven项目
+3. 设置项目project facets,配置版本Dynamic Web Model为[3.0,)
+4. 配置web容器,导入项目到容器中,启动容器
+
+#### 使用说明
+1. 访问地址:(http://localhost:{web容器端口}/{项目名称}/)
+
+#### 项目效果
+![alt 登录](https://service.beansprout.top/image/health-manager/login.jpg)
+![alt 首页](https://service.beansprout.top/image/health-manager/home.jpg)
+![alt 健康列表](https://service.beansprout.top/image/health-manager/health_list.jpg)
+![alt 健康统计](https://service.beansprout.top/image/health-manager/health_statistics.jpg)
+
+#### 附录
+##### Mybatis字段类型映射
+```
+JDBCType | JavaType
+----------------------------
+CHAR String
+VARCHAR String
+LONGVARCHAR String
+NUMERIC java.math.BigDecimal
+DECIMAL java.math.BigDecimal
+BIT boolean
+BOOLEAN boolean
+TINYINT byte
+SMALLINT short
+INTEGER int
+BIGINT long
+REAL float
+FLOAT double
+DOUBLE double
+BINARY byte[]
+VARBINARY byte[]
+LONGVARBINARY byte[]
+DATE java.sql.Date
+TIME java.sql.Time
+TIMESTAMP java.sql.Timestamp
+CLOB Clob
+BLOB Blob
+ARRAY Array
+DISTINCT mapping of underlying type
+STRUCT Struct
+REF Ref
+DATALINK java.net.URL[color=red][/color]
+```
+
+##### SpringMVC 入参校验注解
+```
+校验注解 可校验类型 具体类型
+@AssertTrue Boolean、boolean 属性必须是true
+@AssertFalse Boolean、boolean 属性必须是false
+@Null 基本类型除外 属性必须为null
+@NotNull 基本类型除外 属性必须不能为null
+@NotEmpty CharSequence、Collection、Map 属性不能为null,字符串和集合长度不能为0(无法校验空字符串)
+@NotBlank CharSequence 属性不能为null,并不能为空字符串
+@Size CharSequence、Collection、Map 属性长度必须在指定范围
+@Length CharSequence 属性长度必须在指定范围
+@Min Number 属性必须大于指定最小值
+@Max Number 属性必须小于指定最大值
+@Range Number 属性在指定返回内
+@Past Date、Calender 属性时间必须大于当前时间
+@Future Date、Calender 属性时间必须小于当前时间
+@Email CharSequence 属性必须是合法邮件格式
+@Pattern CharSequence 属性必须匹配正则表达式
+```
+
+#### 码云特技
+1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
+2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
+3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
+4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
+5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
+6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
\ No newline at end of file
diff --git a/health.sql b/health.sql
new file mode 100644
index 0000000..496822c
--- /dev/null
+++ b/health.sql
@@ -0,0 +1,80 @@
+/*
+ Navicat Premium Data Transfer
+
+ Source Server : 127.0.0.1
+ Source Server Type : MySQL
+ Source Server Version : 50644
+ Source Host : localhost:3306
+ Source Schema : health
+
+ Target Server Type : MySQL
+ Target Server Version : 50644
+ File Encoding : 65001
+
+ Date: 03/05/2020 15:46:09
+*/
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for t_body_info
+-- ----------------------------
+DROP TABLE IF EXISTS `t_body_info`;
+CREATE TABLE `t_body_info` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `creator` int(11) NULL DEFAULT NULL COMMENT '创建id',
+ `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+ `updater` int(11) NULL DEFAULT NULL COMMENT '修改id',
+ `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
+ `low_blood_pressure` int(10) NULL DEFAULT NULL COMMENT '舒张压',
+ `high_blood_pressure` int(10) NULL DEFAULT NULL COMMENT '收缩压',
+ `heart_rate` int(10) NULL DEFAULT NULL COMMENT '心率',
+ `temperature` double(10, 2) NULL DEFAULT NULL COMMENT '体温',
+ `appetite` int(11) NULL DEFAULT NULL COMMENT '食欲',
+ `weight` double(10, 2) NULL DEFAULT NULL COMMENT '体重',
+ `number_of_step` bigint(20) NULL DEFAULT NULL COMMENT '步数',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '身体信息表' ROW_FORMAT = Compact;
+
+-- ----------------------------
+-- Table structure for t_health_config
+-- ----------------------------
+DROP TABLE IF EXISTS `t_health_config`;
+CREATE TABLE `t_health_config` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `creator` int(11) NULL DEFAULT NULL COMMENT '创建人id',
+ `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+ `updater` int(11) NULL DEFAULT NULL COMMENT '修改人',
+ `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
+ `min_low_blood_pressure` int(10) NULL DEFAULT NULL COMMENT '最小低血压',
+ `max_low_blood_pressure` int(10) NULL DEFAULT NULL COMMENT '最高低血压',
+ `min_high_blood_pressure` int(10) NULL DEFAULT NULL COMMENT '最小高血压',
+ `max_high_blood_pressure` int(10) NULL DEFAULT NULL COMMENT '最大高血压',
+ `min_heart_rate` int(10) NULL DEFAULT NULL COMMENT '最小心率',
+ `max_heart_rate` int(10) NULL DEFAULT NULL COMMENT '最大心率',
+ `min_temperature` double(10, 2) NULL DEFAULT NULL COMMENT '最低体温',
+ `max_temperature` double(10, 2) NULL DEFAULT NULL COMMENT '最高体温',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '健康信息配置表' ROW_FORMAT = Compact;
+
+-- ----------------------------
+-- Table structure for t_user
+-- ----------------------------
+DROP TABLE IF EXISTS `t_user`;
+CREATE TABLE `t_user` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `creator` int(11) NULL DEFAULT NULL COMMENT '创建人',
+ `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
+ `updater` int(11) NULL DEFAULT NULL COMMENT '修改人',
+ `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
+ `nick_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用户昵称',
+ `user_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '登录账户',
+ `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '登录密码',
+ `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '邮箱',
+ `head_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '头像',
+ PRIMARY KEY (`id`) USING BTREE,
+ UNIQUE INDEX `user_name`(`user_name`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '用户表' ROW_FORMAT = Compact;
+
+SET FOREIGN_KEY_CHECKS = 1;
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..826ec24
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,239 @@
+
+ 4.0.0
+ top.beansprout
+ health
+ 0.0.1-SNAPSHOT
+ war
+ health-manager
+ 个人健康信息管理系统
+
+
+ UTF-8
+ 1.8
+ 5.1.8.RELEASE
+ 2.10.3
+
+
+
+
+
+ org.springframework
+ spring-core
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-context
+ ${spring.version}
+
+
+
+
+ org.springframework
+ spring-tx
+ ${spring.version}
+
+
+
+
+ org.springframework
+ spring-webmvc
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-web
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-context-support
+ ${spring.version}
+
+
+
+
+ org.springframework
+ spring-aspects
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-aop
+ ${spring.version}
+
+
+
+ aopalliance
+ aopalliance
+ 1.0
+
+
+
+ org.aspectj
+ aspectjrt
+ 1.6.8
+
+
+ org.aspectj
+ aspectjweaver
+ 1.6.8
+
+
+
+
+ org.springframework
+ spring-test
+ ${spring.version}
+
+
+ javax.servlet
+ javax.servlet-api
+ 4.0.1
+ provided
+
+
+ javax.servlet.jsp
+ jsp-api
+ 2.2
+ provided
+
+
+ javax.servlet.jsp.jstl
+ jstl-api
+ 1.2
+
+
+ org.glassfish.web
+ jstl-impl
+ 1.2
+
+
+
+
+ org.springframework
+ spring-orm
+ ${spring.version}
+
+
+
+ org.springframework
+ spring-jdbc
+ ${spring.version}
+
+
+ mysql
+ mysql-connector-java
+ 8.0.16
+
+
+ com.alibaba
+ druid
+ 1.1.21
+
+
+ org.mybatis
+ mybatis
+ 3.5.3
+
+
+ org.mybatis
+ mybatis-spring
+ 2.0.4
+
+
+
+ com.github.pagehelper
+ pagehelper
+ 5.1.0
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.8
+ provider
+
+
+
+ org.slf4j
+ slf4j-log4j12
+ 1.7.30
+
+
+ org.slf4j
+ jcl-over-slf4j
+ 1.7.30
+
+
+ org.slf4j
+ jul-to-slf4j
+ 1.7.30
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.30
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ ${jackson.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.7
+
+
+
+ org.hibernate.validator
+ hibernate-validator
+ 6.0.7.Final
+
+
+ commons-io
+ commons-io
+ 2.6
+
+
+ commons-fileupload
+ commons-fileupload
+ 1.4
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${java.version}
+ ${java.version}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/config/CrossXssFilter.java b/src/main/java/top/beansprout/health/config/CrossXssFilter.java
new file mode 100644
index 0000000..cb665a1
--- /dev/null
+++ b/src/main/java/top/beansprout/health/config/CrossXssFilter.java
@@ -0,0 +1,141 @@
+package top.beansprout.health.config;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.lang3.StringUtils;
+
+import lombok.extern.slf4j.Slf4j;
+import top.beansprout.health.constant.SysConstant;
+import top.beansprout.health.model.vo.RequestVo;
+import top.beansprout.health.model.vo.RequestVo.RequestVoBuilder;
+import top.beansprout.health.model.vo.UserLoginVo;
+import top.beansprout.health.util.JsonUtils;
+import top.beansprout.health.util.PublicUtils;
+
+/**
+ *
Title: CrossXssFilter
+ * Description: 对用户输入的表单信息进行检测过滤
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/22 22:47
+ */
+@Slf4j
+public class CrossXssFilter implements Filter {
+
+ /** 忽略资源地址 **/
+ private final String[] ignores = new String[] { "/druid", "/static" };
+ /** 忽略页面地址 **/
+ private final String[] filtrationViews = new String[] { "/login", "/user/login", "/register", "/user/register",
+ "/logout", "/user/logout" };
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
+ throws IOException, ServletException {
+ final HttpServletRequest request = init(servletRequest);
+ if (!isRequestValid(request)) {
+ chain.doFilter(servletRequest, servletResponse);
+ return;
+ }
+ servletRequest.setCharacterEncoding("UTF-8");
+ servletResponse.setContentType("text/html;charset=utf-8");
+ // sql,xss过滤
+ log.info("URI:{}", request.getRequestURI());
+ log.info("METHOD:{}", request.getMethod());
+ log.info("ParameterMap:{}", JsonUtils.toJson(request.getParameterMap()));
+ if (isOauthValid(request)) {
+ // 获取身份
+ final Object user = request.getSession().getAttribute(SysConstant.INIT_FIELD_USER_VO);
+ if (PublicUtils.isBlank(user)) {
+ final HttpServletResponse response = (HttpServletResponse) servletResponse;
+ if ((request.getHeader("x-requested-with") != null)
+ && "XMLHttpRequest".equals(request.getHeader("x-requested-with"))) {
+ // ajax请求
+ response.setHeader("sessionstatus", "timeout");
+ response.setStatus(403);
+ response.addHeader("loginPath", "login");
+ chain.doFilter(request, response);
+ return;
+ }
+ // 页面请求
+ response.setHeader("X-Frame-Options", "DENY");
+ response.sendRedirect("login");
+ return;
+ }
+ }
+ final XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper(request);
+ chain.doFilter(xssHttpServletRequestWrapper, servletResponse);
+ }
+
+ /** 初始化 **/
+ private HttpServletRequest init(ServletRequest servletRequest) {
+ final HttpServletRequest request = (HttpServletRequest) servletRequest;
+
+ final String basePath = PublicUtils.join(request.getScheme(), "://", request.getServerName(),
+ ":" + request.getServerPort(), request.getContextPath(), "/");
+
+ final RequestVoBuilder requestVo = RequestVo.builder().basePath(basePath);
+ final Object userStr = request.getSession().getAttribute(SysConstant.INIT_FIELD_USER_VO);
+ if (PublicUtils.isNotBlank(userStr)) {
+ requestVo.user((UserLoginVo) userStr);
+ }
+ request.setAttribute(SysConstant.INIT_FIELD_REQUEST_VO, JsonUtils.toJson(requestVo.build()));
+ return request;
+ }
+
+ /** 校验合法地址请求 **/
+ private boolean isRequestValid(HttpServletRequest request) {
+ URI uri;
+ try {
+ uri = new URI(request.getRequestURL().toString());
+ } catch (final URISyntaxException ex) {
+ return false;
+ }
+
+ if (uri.getHost() == null)
+ return false;
+ if (!uri.getScheme().equalsIgnoreCase("http") && !uri.getScheme().equalsIgnoreCase("https"))
+ return false;
+ // 忽略指定地址
+ final String path = StringUtils.removeStart(uri.getPath(), request.getContextPath());
+ for (final String ignore : ignores) {
+ if (path.startsWith(ignore))
+ return false;
+ }
+ // 忽略swagger的根路径
+ if (path.equalsIgnoreCase("/"))
+ return false;
+ return true;
+ }
+
+ /** 是否校验合法身份请求 **/
+ private boolean isOauthValid(HttpServletRequest request) {
+ // 忽略指定地址
+ final String path = StringUtils.removeStart(request.getRequestURI(), request.getContextPath());
+ for (final String filtrationView : filtrationViews) {
+ if (path.startsWith(filtrationView))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/config/ExceptionManager.java b/src/main/java/top/beansprout/health/config/ExceptionManager.java
new file mode 100644
index 0000000..1afb95e
--- /dev/null
+++ b/src/main/java/top/beansprout/health/config/ExceptionManager.java
@@ -0,0 +1,43 @@
+package top.beansprout.health.config;
+
+import java.util.List;
+
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import top.beansprout.health.model.vo.R;
+
+/**
+ * Title: ExceptionManager
+ * Description:
+ * @author cyy
+ * @date 2020年4月28日
+ */
+@ControllerAdvice
+public class ExceptionManager {
+
+ @ResponseBody
+ @ExceptionHandler({ BindException.class, MethodArgumentNotValidException.class })
+ public R handleException(Exception e) {
+ List fieldErrs = null;
+ if (e instanceof BindException) {
+ fieldErrs = ((BindException) e).getBindingResult().getFieldErrors();
+ }
+ if (e instanceof MethodArgumentNotValidException) {
+ fieldErrs = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
+ }
+ Object r = e.getMessage();
+ if (fieldErrs != null) {
+ for (final FieldError err : fieldErrs) {
+ r = err.getDefaultMessage();
+ break;
+ }
+ }
+ return R.budil().result(false).message(r.toString()).data(r);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/config/HTMLWrapper.java b/src/main/java/top/beansprout/health/config/HTMLWrapper.java
new file mode 100644
index 0000000..3d4eaa1
--- /dev/null
+++ b/src/main/java/top/beansprout/health/config/HTMLWrapper.java
@@ -0,0 +1,556 @@
+package top.beansprout.health.config;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Title: HTMLWrapper
+ * Description: HTML内容过滤
+ * HTML filtering utility for protecting against XSS (Cross Site Scripting).
+ *
+ * This code is licensed LGPLv3
+ *
+ * This code is a Java port of the original work in PHP by Cal Hendersen.
+ * http://code.iamcal.com/php/lib_filter/
+ *
+ * The trickiest part of the translation was handling the differences in regex handling
+ * between PHP and Java. These resources were helpful in the process:
+ *
+ * http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html
+ * http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php
+ * http://www.regular-expressions.info/modifiers.html
+ *
+ * A note on naming conventions: instance variables are prefixed with a "v"; global
+ * constants are in all caps.
+ *
+ * Sample use:
+ * String input = ...
+ * String clean = new HTMLWrapper().filter( input );
+ *
+ * The class is not thread safe. Create a new instance if in doubt.
+ *
+ * If you find bugs or have suggestions on improvement (especially regarding
+ * performance), please contact us. The latest version of this
+ * source, and our contact details, can be found at http://xss-html-filter.sf.net
+ *
+ * @author beansprout
+ * @date 2020/3/22 23:06
+ * @version 1.0
+ */
+@Slf4j
+public final class HTMLWrapper {
+
+ private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
+ private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL);
+ private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
+ private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
+ private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
+ private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
+ private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
+ private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
+ private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
+ private static final Pattern P_ENTITY = Pattern.compile("(\\d+);?");
+ private static final Pattern P_ENTITY_UNICODE = Pattern.compile("([0-9a-f]+);?");
+ private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
+ private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
+ private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
+ private static final Pattern P_END_ARROW = Pattern.compile("^>");
+ private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
+ private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
+ private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
+ private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
+ private static final Pattern P_AMP = Pattern.compile("&");
+ private static final Pattern P_QUOTE = Pattern.compile("<");
+ private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
+ private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
+ private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
+
+ // @xxx could grow large... maybe use sesat's ReferenceMap
+ private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap();
+ private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap();
+
+ /**
+ * set of allowed html elements, along with allowed attributes for each element
+ **/
+ private final Map> vAllowed;
+ /**
+ * counts of open tags for each (allowable) html element
+ **/
+ private final Map vTagCounts = new HashMap();
+
+ /**
+ * html elements which must always be self-closing (e.g. " ")
+ **/
+ private final String[] vSelfClosingTags;
+ /**
+ * html elements which must always have separate opening and closing tags (e.g. " ")
+ **/
+ private final String[] vNeedClosingTags;
+ /**
+ * set of disallowed html elements
+ **/
+ private final String[] vDisallowed;
+ /**
+ * attributes which should be checked for valid protocols
+ **/
+ private final String[] vProtocolAtts;
+ /**
+ * allowed protocols
+ **/
+ private final String[] vAllowedProtocols;
+ /**
+ * tags which should be removed if they contain no content (e.g. " " or " ")
+ **/
+ private final String[] vRemoveBlanks;
+ /**
+ * entities allowed within html markup
+ **/
+ private final String[] vAllowedEntities;
+ /**
+ * flag determining whether comments are allowed in input String.
+ */
+ private final boolean stripComment;
+ private final boolean encodeQuotes;
+ private boolean vDebug = false;
+ /**
+ * flag determining whether to try to make tags when presented with "unbalanced"
+ * angle brackets (e.g. "" becomes " text "). If set to false,
+ * unbalanced angle brackets will be html escaped.
+ */
+ private final boolean alwaysMakeTags;
+
+ /**
+ * Default constructor.
+ */
+ public HTMLWrapper() {
+ vAllowed = new HashMap<>();
+
+ final ArrayList a_atts = new ArrayList();
+ a_atts.add("href");
+ a_atts.add("target");
+ vAllowed.put("a", a_atts);
+
+ final ArrayList img_atts = new ArrayList();
+ img_atts.add("src");
+ img_atts.add("width");
+ img_atts.add("height");
+ img_atts.add("alt");
+ vAllowed.put("img", img_atts);
+
+ final ArrayList no_atts = new ArrayList();
+ vAllowed.put("b", no_atts);
+ vAllowed.put("strong", no_atts);
+ vAllowed.put("i", no_atts);
+ vAllowed.put("em", no_atts);
+
+ vSelfClosingTags = new String[]{"img"};
+ vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
+ vDisallowed = new String[]{};
+ vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
+ vProtocolAtts = new String[]{"src", "href"};
+ vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
+ vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
+ stripComment = true;
+ encodeQuotes = true;
+ alwaysMakeTags = true;
+ }
+
+ /**
+ * Set debug flag to true. Otherwise use default settings. See the default constructor.
+ *
+ * @param debug turn debug on with a true argument
+ */
+ public HTMLWrapper(final boolean debug) {
+ this();
+ vDebug = debug;
+ }
+
+ /**
+ * Map-parameter configurable constructor.
+ *
+ * @param conf map containing configuration. keys match field names.
+ */
+ @SuppressWarnings("unchecked")
+ public HTMLWrapper(final Map conf) {
+
+ assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
+ assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
+ assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
+ assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
+ assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
+ assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
+ assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
+ assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
+
+ vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed"));
+ vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
+ vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
+ vDisallowed = (String[]) conf.get("vDisallowed");
+ vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
+ vProtocolAtts = (String[]) conf.get("vProtocolAtts");
+ vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
+ vAllowedEntities = (String[]) conf.get("vAllowedEntities");
+ stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
+ encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
+ alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
+ }
+
+ private void reset() {
+ vTagCounts.clear();
+ }
+
+ private void debug(final String msg) {
+ if (vDebug) {
+ log.info(msg);
+ }
+ }
+
+ //---------------------------------------------------------------
+ // my versions of some PHP library functions
+ public static String chr(final int decimal) {
+ return String.valueOf((char) decimal);
+ }
+
+ public static String htmlSpecialChars(final String s) {
+ String result = s;
+ result = regexReplace(P_AMP, "&", result);
+ result = regexReplace(P_QUOTE, """, result);
+ result = regexReplace(P_LEFT_ARROW, "<", result);
+ result = regexReplace(P_RIGHT_ARROW, ">", result);
+ return result;
+ }
+
+ //---------------------------------------------------------------
+
+ /**
+ * given a user submitted input String, filter out any invalid or restricted
+ * html.
+ *
+ * @param input text (i.e. submitted by a user) than may contain html
+ * @return "clean" version of input, with only valid, whitelisted html elements allowed
+ */
+ public String filter(final String input) {
+ reset();
+ String s = input;
+
+ debug("************************************************");
+ debug(" INPUT: " + input);
+
+ s = escapeComments(s);
+ debug(" escapeComments: " + s);
+
+ s = balanceHTML(s);
+ debug(" balanceHTML: " + s);
+
+ s = checkTags(s);
+ debug(" checkTags: " + s);
+
+ s = processRemoveBlanks(s);
+ debug("processRemoveBlanks: " + s);
+
+ s = validateEntities(s);
+ debug(" validateEntites: " + s);
+
+ debug("************************************************\n\n");
+ return s;
+ }
+
+ public boolean isAlwaysMakeTags() {
+ return alwaysMakeTags;
+ }
+
+ public boolean isStripComments() {
+ return stripComment;
+ }
+
+ private String escapeComments(final String s) {
+ final Matcher m = P_COMMENTS.matcher(s);
+ final StringBuffer buf = new StringBuffer();
+ if (m.find()) {
+ final String match = m.group(1); //(.*?)
+ m.appendReplacement(buf, Matcher.quoteReplacement(""));
+ }
+ m.appendTail(buf);
+
+ return buf.toString();
+ }
+
+ private String balanceHTML(String s) {
+ if (alwaysMakeTags) {
+ //
+ // try and form html
+ //
+ s = regexReplace(P_END_ARROW, "", s);
+ s = regexReplace(P_BODY_TO_END, "<$1>", s);
+ s = regexReplace(P_XML_CONTENT, "$1<$2", s);
+
+ } else {
+ //
+ // escape stray brackets
+ //
+ s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
+ s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
+
+ //
+ // the last regexp causes '<>' entities to appear
+ // (we need to do a lookahead assertion so that the last bracket can
+ // be used in the next pass of the regexp)
+ //
+ s = regexReplace(P_BOTH_ARROWS, "", s);
+ }
+
+ return s;
+ }
+
+ private String checkTags(String s) {
+ final Matcher m = P_TAGS.matcher(s);
+
+ final StringBuffer buf = new StringBuffer();
+ while (m.find()) {
+ String replaceStr = m.group(1);
+ replaceStr = processTag(replaceStr);
+ m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
+ }
+ m.appendTail(buf);
+
+ s = buf.toString();
+
+ // these get tallied in processTag
+ // (remember to reset before subsequent calls to filter method)
+ for (final String key : vTagCounts.keySet()) {
+ for (int ii = 0; ii < vTagCounts.get(key); ii++) {
+ s += "" + key + ">";
+ }
+ }
+
+ return s;
+ }
+
+ private String processRemoveBlanks(final String s) {
+ String result = s;
+ for (final String tag : vRemoveBlanks) {
+ if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
+ P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>" + tag + ">"));
+ }
+ result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
+ if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
+ P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
+ }
+ result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
+ }
+
+ return result;
+ }
+
+ private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
+ final Matcher m = regex_pattern.matcher(s);
+ return m.replaceAll(replacement);
+ }
+
+ private String processTag(final String s) {
+ // ending tags
+ Matcher m = P_END_TAG.matcher(s);
+ if (m.find()) {
+ final String name = m.group(1).toLowerCase();
+ if (allowed(name)) {
+ if (!inArray(name, vSelfClosingTags)) {
+ if (vTagCounts.containsKey(name)) {
+ vTagCounts.put(name, vTagCounts.get(name) - 1);
+ return "" + name + ">";
+ }
+ }
+ }
+ }
+
+ // starting tags
+ m = P_START_TAG.matcher(s);
+ if (m.find()) {
+ final String name = m.group(1).toLowerCase();
+ final String body = m.group(2);
+ String ending = m.group(3);
+
+ //debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
+ if (allowed(name)) {
+ String params = "";
+
+ final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
+ final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
+ final List paramNames = new ArrayList();
+ final List paramValues = new ArrayList();
+ while (m2.find()) {
+ paramNames.add(m2.group(1)); //([a-z0-9]+)
+ paramValues.add(m2.group(3)); //(.*?)
+ }
+ while (m3.find()) {
+ paramNames.add(m3.group(1)); //([a-z0-9]+)
+ paramValues.add(m3.group(3)); //([^\"\\s']+)
+ }
+
+ String paramName, paramValue;
+ for (int ii = 0; ii < paramNames.size(); ii++) {
+ paramName = paramNames.get(ii).toLowerCase();
+ paramValue = paramValues.get(ii);
+
+// debug( "paramName='" + paramName + "'" );
+// debug( "paramValue='" + paramValue + "'" );
+// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
+
+ if (allowedAttribute(name, paramName)) {
+ if (inArray(paramName, vProtocolAtts)) {
+ paramValue = processParamProtocol(paramValue);
+ }
+ params += " " + paramName + "=\"" + paramValue + "\"";
+ }
+ }
+
+ if (inArray(name, vSelfClosingTags)) {
+ ending = " /";
+ }
+
+ if (inArray(name, vNeedClosingTags)) {
+ ending = "";
+ }
+
+ if ((ending == null) || (ending.length() < 1)) {
+ if (vTagCounts.containsKey(name)) {
+ vTagCounts.put(name, vTagCounts.get(name) + 1);
+ } else {
+ vTagCounts.put(name, 1);
+ }
+ } else {
+ ending = " /";
+ }
+ return "<" + name + params + ending + ">";
+ } else
+ return "";
+ }
+
+ // comments
+ m = P_COMMENT.matcher(s);
+ if (!stripComment && m.find())
+ return "<" + m.group() + ">";
+
+ return "";
+ }
+
+ private String processParamProtocol(String s) {
+ s = decodeEntities(s);
+ final Matcher m = P_PROTOCOL.matcher(s);
+ if (m.find()) {
+ final String protocol = m.group(1);
+ if (!inArray(protocol, vAllowedProtocols)) {
+ // bad protocol, turn into local anchor link instead
+ s = "#" + s.substring(protocol.length() + 1, s.length());
+ if (s.startsWith("#//")) {
+ s = "#" + s.substring(3, s.length());
+ }
+ }
+ }
+
+ return s;
+ }
+
+ private String decodeEntities(String s) {
+ StringBuffer buf = new StringBuffer();
+
+ Matcher m = P_ENTITY.matcher(s);
+ while (m.find()) {
+ final String match = m.group(1);
+ final int decimal = Integer.decode(match).intValue();
+ m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+ }
+ m.appendTail(buf);
+ s = buf.toString();
+
+ buf = new StringBuffer();
+ m = P_ENTITY_UNICODE.matcher(s);
+ while (m.find()) {
+ final String match = m.group(1);
+ final int decimal = Integer.valueOf(match, 16).intValue();
+ m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+ }
+ m.appendTail(buf);
+ s = buf.toString();
+
+ buf = new StringBuffer();
+ m = P_ENCODE.matcher(s);
+ while (m.find()) {
+ final String match = m.group(1);
+ final int decimal = Integer.valueOf(match, 16).intValue();
+ m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+ }
+ m.appendTail(buf);
+ s = buf.toString();
+
+ s = validateEntities(s);
+ return s;
+ }
+
+ private String validateEntities(final String s) {
+ final StringBuffer buf = new StringBuffer();
+
+ // validate entities throughout the string
+ final Matcher m = P_VALID_ENTITIES.matcher(s);
+ while (m.find()) {
+ final String one = m.group(1); //([^&;]*)
+ final String two = m.group(2); //(?=(;|&|$))
+ m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
+ }
+ m.appendTail(buf);
+
+ return encodeQuotes(buf.toString());
+ }
+
+ private String encodeQuotes(final String s) {
+ if (encodeQuotes) {
+ final StringBuffer buf = new StringBuffer();
+ final Matcher m = P_VALID_QUOTES.matcher(s);
+ while (m.find()) {
+ final String one = m.group(1); //(>|^)
+ final String two = m.group(2); //([^<]+?)
+ final String three = m.group(3); //(<|$)
+ m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three));
+ }
+ m.appendTail(buf);
+ return buf.toString();
+ } else
+ return s;
+ }
+
+ private String checkEntity(final String preamble, final String term) {
+
+ return ";".equals(term) && isValidEntity(preamble)
+ ? '&' + preamble
+ : "&" + preamble;
+ }
+
+ private boolean isValidEntity(final String entity) {
+ return inArray(entity, vAllowedEntities);
+ }
+
+ private static boolean inArray(final String s, final String[] array) {
+ for (final String item : array) {
+ if ((item != null) && item.equals(s))
+ return true;
+ }
+ return false;
+ }
+
+ private boolean allowed(final String name) {
+ return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
+ }
+
+ private boolean allowedAttribute(final String name, final String paramName) {
+ return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/config/MySimpleMappingExceptionResolver.java b/src/main/java/top/beansprout/health/config/MySimpleMappingExceptionResolver.java
new file mode 100644
index 0000000..799b200
--- /dev/null
+++ b/src/main/java/top/beansprout/health/config/MySimpleMappingExceptionResolver.java
@@ -0,0 +1,77 @@
+package top.beansprout.health.config;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.ModelAndView;
+
+import top.beansprout.health.model.vo.BusinessException;
+import top.beansprout.health.model.vo.R;
+import top.beansprout.health.util.JsonUtils;
+import top.beansprout.health.util.PublicUtils;
+
+/**
+ * Title: MySimpleMappingExceptionResolver
+ * Description: 异常处理
+ * @author cyy
+ * @date 2020年4月23日
+ */
+public class MySimpleMappingExceptionResolver implements HandlerExceptionResolver {
+
+ @Override
+ public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
+ Exception exception) {
+ // 判断是否ajax请求
+ if (!((request.getHeader("accept").indexOf("application/json") > -1)
+ || ((request.getHeader("X-Requested-With") != null)
+ && (request.getHeader("X-Requested-With").indexOf("XMLHttpRequest") > -1)))) {
+ // 这里需要手动将异常打印出来,由于没有配置log,实际生产环境应该打印到log里面
+ exception.printStackTrace();
+ if (exception instanceof BusinessException) {
+ final BusinessException be = (BusinessException) exception;
+ String path = be.getR().getPath();
+ if (PublicUtils.isBlank(path)) {
+ path = request.getHeader("Referer");
+ path = StringUtils.removeStart(path, request.getHeader("Origin"));
+ path = StringUtils.removeStart(path, request.getContextPath());
+ }
+ return R.failed(path, be.getR());
+ } else
+ // 对于非ajax请求,我们都统一跳转到error.jsp页面
+ return R.failed(R.budil().result(false).message("系统异常!"));
+ } else {
+ // 如果是ajax请求,JSON格式返回
+ if (exception instanceof BusinessException) {
+ final BusinessException be = (BusinessException) exception;
+ response(response, be.getR());
+ } else {
+ response(response, R.budil().result(false).message("系统异常!"));
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("deprecation")
+ private void response(HttpServletResponse response, Object data) {
+ response.setContentType("application/json;charset=UTF-8");
+ PrintWriter writer = null;
+ try {
+ response.setStatus(HttpStatus.OK.value());
+ writer = response.getWriter();
+ writer.write(JsonUtils.toJson(data));
+ writer.flush();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ } finally {
+ IOUtils.closeQuietly(writer);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/config/TaskExecutorConfig.java b/src/main/java/top/beansprout/health/config/TaskExecutorConfig.java
new file mode 100644
index 0000000..9e58ca1
--- /dev/null
+++ b/src/main/java/top/beansprout/health/config/TaskExecutorConfig.java
@@ -0,0 +1,30 @@
+package top.beansprout.health.config;
+
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+@Configuration
+@EnableAsync
+public class TaskExecutorConfig implements AsyncConfigurer { // 1
+
+ @Override
+ public Executor getAsyncExecutor() { // 2
+ ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
+ taskExecutor.setCorePoolSize(10);
+ taskExecutor.setMaxPoolSize(15);
+ taskExecutor.setQueueCapacity(25);
+ taskExecutor.initialize();
+ return taskExecutor;
+ }
+
+ @Override
+ public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+ return new SimpleAsyncUncaughtExceptionHandler();
+ }
+}
diff --git a/src/main/java/top/beansprout/health/config/XssHttpServletRequestWrapper.java b/src/main/java/top/beansprout/health/config/XssHttpServletRequestWrapper.java
new file mode 100644
index 0000000..6f94e85
--- /dev/null
+++ b/src/main/java/top/beansprout/health/config/XssHttpServletRequestWrapper.java
@@ -0,0 +1,198 @@
+package top.beansprout.health.config;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.MediaType;
+
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import top.beansprout.health.util.PublicUtils;
+
+/**
+ * Title: XssHttpServletRequestWrapper
+ * Description: XSS过滤处理
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/22 22:51
+ */
+@Slf4j
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
+
+ private HttpServletRequest orgRequest;
+ private final String requestUri;
+ private static HTMLWrapper htmlWrapper;
+ @Getter
+ private String requestBody;
+
+ private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+";
+ private static Set notAllowedKeyWords = new HashSet<>(0);
+ private static String replacedString = "INVALID";
+ // 指定uri需要进行html过滤
+ private static String []htmlUri = { };
+ private static List ignoreParams = new ArrayList<>();
+
+ static {
+ final String keyStr[] = key.split("\\|");
+ for (final String str : keyStr) {
+ notAllowedKeyWords.add(str);
+ }
+ htmlWrapper = new HTMLWrapper(true);
+ }
+
+ public XssHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
+ super(request);
+ orgRequest = request;
+ if (request instanceof XssHttpServletRequestWrapper) {
+ orgRequest = ((XssHttpServletRequestWrapper) request).getOrgRequest();
+ }
+ requestUri = orgRequest.getRequestURI();
+ requestBody = getAnalysisRequestBody();
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+ // 为空,直接返回
+ if (PublicUtils.isBlank(requestBody))
+ return super.getInputStream();
+
+ // xss过滤
+ requestBody = xssEncode(requestBody);
+ final ByteArrayInputStream bis = new ByteArrayInputStream(requestBody.getBytes("UTF-8"));
+ return new ServletInputStream() {
+ @Override
+ public boolean isFinished() {
+ return true;
+ }
+
+ @Override
+ public boolean isReady() {
+ return true;
+ }
+
+ @Override
+ public void setReadListener(ReadListener readListener) {
+ }
+
+ @Override
+ public int read() {
+ return bis.read();
+ }
+ };
+ }
+
+ @Override
+ public String getParameter(String name) {
+ final String value = super.getParameter(name);
+ if (value == null)
+ return null;
+ return xssEncode(value);
+ }
+
+ @Override
+ public String[] getParameterValues(String name) {
+ final String[] parameters = super.getParameterValues(name);
+ if ((parameters == null) || (parameters.length == 0))
+ return null;
+ final int count = parameters.length;
+ final String[] encodedValues = new String[count];
+ for (int i = 0; i < count; i++) {
+ encodedValues[i] = xssEncode(parameters[i]);
+ }
+ return encodedValues;
+ }
+
+ @Override
+ public Map getParameterMap() {
+ final Map parameters = super.getParameterMap();
+ if (parameters == null)
+ return null;
+ final Map result = new HashMap<>();
+ for (final String key : parameters.keySet()) {
+ final String encodedKey = xssEncode(key);
+ final int count = parameters.get(key).length;
+ final String[] encodedValues = new String[count];
+ for (int i = 0; i < count; i++) {
+ encodedValues[i] = xssEncode(parameters.get(key)[i]);
+ }
+ result.put(encodedKey, encodedValues);
+ }
+ return result;
+ }
+
+ /**
+ * 覆盖getHeader方法,将参数名和参数值都做xss过滤。
+ * 如果需要获得原始的值,则通过super.getHeaders(name)来获取
+ * getHeaderNames 也可能需要覆盖
+ */
+ @Override
+ public String getHeader(String name) {
+ final String value = super.getHeader(name);
+ if (value == null)
+ return null;
+ // 忽略指定字段不进行xss
+ if (ignoreParams.contains(name)) return value;
+ return xssEncode(value);
+ }
+
+ /** 获取请求body json 内容 **/
+ private String getAnalysisRequestBody() throws IOException {
+ final String contentType = super.getHeader("Content-Type");
+ if (PublicUtils.isBlank(contentType) || !MediaType.APPLICATION_JSON_VALUE.contentEquals(contentType))
+ return null;
+
+ return IOUtils.toString(super.getInputStream(), "UTF-8");
+ }
+
+ /** 获取最原始request **/
+ private HttpServletRequest getOrgRequest() {
+ return orgRequest;
+ }
+
+ /** 过滤 */
+ private String xssEncode(String value) {
+ // You'll need to remove the spaces from the html entities below
+ String result = value.replaceAll("<", "<").replaceAll(">", ">");
+ result = result.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
+ result = result.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
+ result = result.replaceAll("'", "& #39;");
+ result = result.replaceAll("eval\\((.*)\\)", "");
+ result = result.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
+ result = result.replaceAll("script", "");
+ result = sqlKeyFilterWords(result);
+ for (int i = 0; i < htmlUri.length; i ++) {
+ if (requestUri.contains(htmlUri[i])) {
+ result = htmlWrapper.filter(result);
+ }
+ }
+ return result;
+ }
+
+ /** sql注入过滤 **/
+ private String sqlKeyFilterWords(String value) {
+ String paramValue = value;
+ for (final String keyword : notAllowedKeyWords) {
+ if ((paramValue.length() > (keyword.length() + 1))
+ && (paramValue.contains(" " + keyword) || paramValue.contains(keyword + " ") || paramValue.contains(" " + keyword + " "))) {
+ paramValue = StringUtils.replace(paramValue, keyword, replacedString);
+ log.warn("CrosXssFilter 请求参数中包含不允许sql的关键词({});参数:{};过滤后的参数:{}", keyword, value, paramValue);
+ }
+ }
+ return paramValue;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/constant/SysConstant.java b/src/main/java/top/beansprout/health/constant/SysConstant.java
new file mode 100644
index 0000000..91905c2
--- /dev/null
+++ b/src/main/java/top/beansprout/health/constant/SysConstant.java
@@ -0,0 +1,31 @@
+package top.beansprout.health.constant;
+
+import javax.servlet.http.HttpServletRequest;
+
+import top.beansprout.health.model.vo.RequestVo;
+import top.beansprout.health.util.JsonUtils;
+import top.beansprout.health.util.PublicUtils;
+
+/**
+ * Title: SysConstant
+ * Description: 系统常量
+ * @author cyy
+ * @date 2020年4月23日
+ */
+public class SysConstant {
+
+ public static final String CHARACHER = "UTF-8";
+
+ public static final String INIT_FIELD_REQUEST_IP = "ip";
+ public static final String INIT_FIELD_REQUEST_REQUEST_ID = "requestId";
+ public static final String INIT_FIELD_REQUEST_USER_AGENT = "userAgent";
+ public static final String INIT_FIELD_REQUEST_BASE_PATH = "basePath";
+ public static final String INIT_FIELD_REQUEST_VO = "requestVo";
+ public static final String INIT_FIELD_USER_VO = "user";
+
+ /** 获取初始化数据 **/
+ public static RequestVo getRequestVo(HttpServletRequest request) {
+ return JsonUtils.toObj(PublicUtils.getAttribute(request, INIT_FIELD_REQUEST_VO), RequestVo.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/controller/BaseController.java b/src/main/java/top/beansprout/health/controller/BaseController.java
new file mode 100644
index 0000000..ec37435
--- /dev/null
+++ b/src/main/java/top/beansprout/health/controller/BaseController.java
@@ -0,0 +1,36 @@
+package top.beansprout.health.controller;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import top.beansprout.health.constant.SysConstant;
+import top.beansprout.health.model.vo.BusinessException;
+import top.beansprout.health.model.vo.UserLoginVo;
+import top.beansprout.health.util.PublicUtils;
+
+/**
+ * Title: BaseController
+ * Description: 基本信息处理
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月27日 上午12:36:51
+ * @Version: 1.0.0
+ */
+public class BaseController {
+
+ @Autowired
+ private HttpServletRequest request;
+
+ public UserLoginVo getUserInfo() {
+ final Object userObject = request.getSession().getAttribute(SysConstant.INIT_FIELD_USER_VO);
+ if (PublicUtils.isNotBlank(userObject))
+ return (UserLoginVo) userObject;
+ throw new BusinessException("login", "身份信息已过期,请重新登录");
+ }
+
+ public int getUserId() {
+ return getUserInfo().getId();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/controller/HealthController.java b/src/main/java/top/beansprout/health/controller/HealthController.java
new file mode 100644
index 0000000..998727d
--- /dev/null
+++ b/src/main/java/top/beansprout/health/controller/HealthController.java
@@ -0,0 +1,74 @@
+package top.beansprout.health.controller;
+
+import java.util.Date;
+
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import top.beansprout.health.model.dto.BodyInfoQuery;
+import top.beansprout.health.model.dto.BodyInfoSaveDto;
+import top.beansprout.health.model.vo.R;
+import top.beansprout.health.service.HealthService;
+
+/**
+ * Title: HealthController
+ * Description: 健康管理接口
+ *
+ * @author cyy
+ * @date 2020年4月27日
+ */
+@Controller
+@RequestMapping("/health")
+public class HealthController extends BaseController {
+
+ @Autowired
+ private HealthService healthService;
+
+ // 保存身体信息
+ @ResponseBody
+ @PostMapping("/saveBodyInfo")
+ public R saveBodyInfo(@RequestBody @Valid BodyInfoSaveDto bodyInfoSaveDto) {
+ healthService.saveBodyInfo(getUserId(), bodyInfoSaveDto);
+ return R.okAsAjax();
+ }
+
+ // 身体信息列表
+ @ResponseBody
+ @GetMapping("/bodyInfoList")
+ public R bodyInfoList(@Valid BodyInfoQuery bodyInfoQuery) {
+ return R.okAsAjax(healthService.bodyInfoList(getUserId(), bodyInfoQuery));
+ }
+
+ // 删除身体信息
+ @ResponseBody
+ @DeleteMapping("/{id}")
+ public R saveBodyInfo(@PathVariable int id) {
+ healthService.deleteBodyInfo(getUserId(), id);
+ return R.okAsAjax();
+ }
+
+ // 获取身体信息
+ @ResponseBody
+ @GetMapping("/{id}")
+ public R getBodyInfo(@PathVariable int id) {
+ return R.okAsAjax(healthService.getBodyInfo(getUserId(), id));
+ }
+
+ // 身体信息统计
+ @ResponseBody
+ @GetMapping("/bodyInfoStatistics")
+ public R bodyInfoStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
+ return R.okAsAjax(healthService.getBodyStatistics(getUserId(), date));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/controller/MailController.java b/src/main/java/top/beansprout/health/controller/MailController.java
new file mode 100644
index 0000000..c47001b
--- /dev/null
+++ b/src/main/java/top/beansprout/health/controller/MailController.java
@@ -0,0 +1,89 @@
+package top.beansprout.health.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import top.beansprout.health.model.dto.BodyInfoSaveDto;
+import top.beansprout.health.model.dto.UserUpdateInfoDto;
+import top.beansprout.health.model.entity.TBodyInfo;
+import top.beansprout.health.model.vo.R;
+import top.beansprout.health.service.HealthService;
+import top.beansprout.health.util.MailUtils;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * Title: MailController
+ * Description: 发送邮箱接口
+ *
+ * @author ateenliu
+ * @date 2022年4月6日
+ */
+@Controller
+@RequestMapping("/mail")
+public class MailController extends BaseController {
+
+ @Autowired
+ private HealthService healthService;
+
+ @ResponseBody
+ @PostMapping(value = "/verification")
+ public R sendVerificationMail(@Valid String email) {
+ MailUtils mailUtils = new MailUtils();
+ String verificationCode = mailUtils.createVertifyCode();
+ String mailSubject = "个人健康管理平台验证码";
+ String mailBody = "欢迎使用个人健康管理平台,您的邮箱验证码为:"+verificationCode;
+ if(mailUtils.sendingMimeMail(email,mailSubject,mailBody))
+ {
+ System.out.println("邮件发送成功!");
+ return R.okAsAjax(verificationCode);
+ }
+ else {
+ System.out.println("邮件发送失败!");
+ return R.okAsAjax();
+ }
+
+ }
+
+ @ResponseBody
+ @PostMapping(value = "/recentPush")
+ public R recentPushMail(){
+ if(getUserInfo().getEmail()==null)
+ return R.ofAsAjax(true,"暂未绑定邮箱","");
+
+ List tBodyInfoList = healthService.recentBodyInfoList(getUserId());
+ TBodyInfo t = new TBodyInfo();
+ MailUtils mailUtils = new MailUtils();
+ String mailSubject = "近期健康报告";
+ StringBuilder content = new StringBuilder("您的近期报告如下: ");
+ content.append("");
+ content.append("打卡时间 收缩压 舒张压 " +
+ "心率 体温 食欲 体重 步数 ");
+ for(int i=0;i"+t.getCreateTime()+" "+ ""+t.getHighBloodPressure()+" "
+ + ""+t.getLowBloodPressure()+" "+ ""+t.getHeartRate()+" "
+ + ""+t.getTemperature()+"度 "+ ""+t.getAppetiteDes(t.getAppetite())+" "
+ + ""+t.getWeight()+"kg "+ ""+t.getNumberOfStep()+"步 ");
+ }
+ content.append("
");
+ content.append("");
+
+
+ if(mailUtils.sendingMimeMail(getUserInfo().getEmail(), mailSubject,content.toString()))
+ {
+ System.out.println("邮件发送成功!");
+ return R.okAsAjax();
+ }
+ else {
+ System.out.println("邮件发送失败!");
+ return R.okAsAjax();
+ }
+ }
+
+}
diff --git a/src/main/java/top/beansprout/health/controller/PageController.java b/src/main/java/top/beansprout/health/controller/PageController.java
new file mode 100644
index 0000000..eacc217
--- /dev/null
+++ b/src/main/java/top/beansprout/health/controller/PageController.java
@@ -0,0 +1,71 @@
+package top.beansprout.health.controller;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+
+/**
+ * Title: PageController
+ * Description: 页面管理
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月23日 下午11:15:18
+ * @Version: 1.0.0
+ */
+@Controller
+public class PageController {
+
+ // 根页面
+ @GetMapping("/")
+ public String rootView() {
+ return "redirect:/login";
+ }
+
+ // 登录页面
+ @GetMapping("/login")
+ public String loginView() {
+ return "../../index";
+ }
+
+ // 注册页面
+ @GetMapping("/register")
+ public String registerView() {
+ return "register";
+ }
+
+ // 主页面
+ @GetMapping("/home")
+ public String homeView() {
+ return "home";
+ }
+
+ // 用户信息页面
+ @GetMapping("/userInfo")
+ public String userInfoView() {
+ return "userInfo";
+ }
+
+ // 用户信息页面
+ @GetMapping("/updatePassword")
+ public String updatePasswordView() {
+ return "updatePassword";
+ }
+
+ // 用户身体信息录入页面
+ @GetMapping("/bodyInfoInput")
+ public String bodyInfoInputView() {
+ return "bodyInfoInput";
+ }
+
+ // 用户身体信息列表页面
+ @GetMapping("/bodyInofList")
+ public String bodyInofListView() {
+ return "bodyInofList";
+ }
+
+ // 用户身体信息统计页面
+ @GetMapping("/bodyInofStatistics")
+ public String bodyInofStatisticsView() {
+ return "bodyInofStatistics";
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/controller/UserController.java b/src/main/java/top/beansprout/health/controller/UserController.java
new file mode 100644
index 0000000..19d0bac
--- /dev/null
+++ b/src/main/java/top/beansprout/health/controller/UserController.java
@@ -0,0 +1,74 @@
+package top.beansprout.health.controller;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import top.beansprout.health.model.dto.UserLoginDto;
+import top.beansprout.health.model.dto.UserRegisterDto;
+import top.beansprout.health.model.dto.UserUpdateInfoDto;
+import top.beansprout.health.model.dto.UserUpdatePasswordDto;
+import top.beansprout.health.model.vo.R;
+import top.beansprout.health.service.UserService;
+
+/**
+ * Title: UserController
+ * Description: 用户管理接口
+ *
+ * @author cyy
+ * @date 2020年4月23日
+ */
+@Controller
+@RequestMapping("/user")
+public class UserController extends BaseController {
+
+ @Autowired
+ private UserService userService;
+
+ // 登录
+ @ResponseBody
+ @PostMapping("/login")
+ public R login(@Valid UserLoginDto userLoginDto) {
+ return R.okAsAjax(userService.login(userLoginDto));
+ }
+
+ // 注册
+ @ResponseBody
+ @PostMapping("/register")
+ public R register(@Valid UserRegisterDto userRegisterDto) {
+ userService.register(userRegisterDto);
+ return R.okAsAjax();
+ }
+
+ // 登出
+ @GetMapping("/logout")
+ public String logout(HttpServletRequest request) {
+ userService.logout(request);
+ return "redirect:/login?target=redirect";
+ }
+
+ // 修改密码
+ @ResponseBody
+ @PutMapping("/updatePassword")
+ public R updatePassword(HttpServletRequest request, @RequestBody @Valid UserUpdatePasswordDto updatePasswordDto) {
+ userService.updatePassword(request, getUserId(), updatePasswordDto);
+ return R.okAsAjax();
+ }
+
+ // 修改用户信息
+ @ResponseBody
+ @PostMapping(value = "/updateUserInfo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ public R updateUserInfo(@Valid UserUpdateInfoDto userUpdateInfoDto) {
+ return R.okAsAjax(userService.updateUserInfo(getUserId(), userUpdateInfoDto));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/mapper/TBodyInfoMapper.java b/src/main/java/top/beansprout/health/mapper/TBodyInfoMapper.java
new file mode 100644
index 0000000..73ee646
--- /dev/null
+++ b/src/main/java/top/beansprout/health/mapper/TBodyInfoMapper.java
@@ -0,0 +1,43 @@
+package top.beansprout.health.mapper;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.ibatis.annotations.Delete;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import top.beansprout.health.model.entity.TBodyInfo;
+
+/**
+ * Title: TBodyInfoMapper
+ * Description: 身体健康操作
+ *
+ * @author cyy
+ * @date 2020年4月27日
+ */
+public interface TBodyInfoMapper {
+
+ @Insert("INSERT INTO t_body_info (creator, create_time, updater, update_time, low_blood_pressure, "
+ + "high_blood_pressure, heart_rate, temperature, appetite, weight, number_of_step) VALUE "
+ + "( #{creator},NOW(),#{updater},NOW(),#{lowBloodPressure},#{highBloodPressure},"
+ + "#{heartRate},#{temperature},#{appetite},#{weight},#{numberOfStep} )")
+ int insertByOne(TBodyInfo bodyInfo);
+
+ @Select("SELECT COUNT(*) FROM t_body_info WHERE creator = #{creator} AND create_time >= CURDATE() AND create_time <= CONCAT(CURDATE(), '23:59:59')")
+ int countByOneAsToDay(@Param("creator") int userId);
+
+ List selectByUserId(@Param("creator") int userId, @Param("minDate") Date minDate,
+ @Param("maxDate") Date maxDate);
+
+ @Delete("DELETE FROM t_body_info WHERE id = #{id} AND creator = #{creator}")
+ int deleteByOne(@Param("id") int id, @Param("creator") int userId);
+
+ @Select("SELECT * FROM t_body_info WHERE id = #{id} AND creator = #{creator}")
+ TBodyInfo selectByUserOne(@Param("id") int id, @Param("creator") int userId);
+
+ @Select("SELECT * FROM t_body_info WHERE creator = #{creator} LIMIT 7 ")
+ List selectRecentByUserId(@Param("creator") int userId);
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/mapper/THealthConfigMapper.java b/src/main/java/top/beansprout/health/mapper/THealthConfigMapper.java
new file mode 100644
index 0000000..713ca05
--- /dev/null
+++ b/src/main/java/top/beansprout/health/mapper/THealthConfigMapper.java
@@ -0,0 +1,12 @@
+package top.beansprout.health.mapper;
+
+/**
+ * Title: THealthConfigMapper
+ * Description: 健康配置操作
+ *
+ * @author cyy
+ * @date 2020年4月27日
+ */
+public interface THealthConfigMapper {
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/mapper/TUserMapper.java b/src/main/java/top/beansprout/health/mapper/TUserMapper.java
new file mode 100644
index 0000000..82b3dd9
--- /dev/null
+++ b/src/main/java/top/beansprout/health/mapper/TUserMapper.java
@@ -0,0 +1,36 @@
+package top.beansprout.health.mapper;
+
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import top.beansprout.health.model.entity.TUser;
+
+/**
+ * Title: TUserMapper
+ * Description: 用户数据操作
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月23日 下午9:24:05
+ * @Version: 1.0.0
+ */
+public interface TUserMapper {
+
+ @Select("SELECT * FROM t_user WHERE user_name = #{userName}")
+ TUser selectByUserName(@Param("userName") String userName);
+
+ @Select("SELECT * FROM t_user WHERE id = #{userId}")
+ TUser selectById(@Param("userId") int id);
+
+ @Insert("INSERT INTO t_user (creator, create_time, updater, update_time, nick_name, user_name, password, head_url) "
+ + "VALUE (0, NOW(), 0, NOW(), #{userName}, #{userName}, #{password}, #{headUrl})")
+ int insertByOne(TUser tUser);
+
+ @Update("UPDATE t_user SET updater = #{id}, update_time = NOW(), password = #{password} WHERE id = #{id}")
+ int updateByOne(TUser tUser);
+
+ @Update("UPDATE t_user SET updater = #{id}, update_time = NOW(), nick_name = #{nickName}, email = #{email}, head_url = #{headUrl} WHERE id = #{id}")
+ int updateByUserInfoOne(TUser tUser);
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/dto/BodyInfoQuery.java b/src/main/java/top/beansprout/health/model/dto/BodyInfoQuery.java
new file mode 100644
index 0000000..a411f91
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/dto/BodyInfoQuery.java
@@ -0,0 +1,28 @@
+package top.beansprout.health.model.dto;
+
+import java.util.Date;
+
+import org.springframework.format.annotation.DateTimeFormat;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: BodyInfoQuery
+ * Description: 身体信息查询
+ *
+ * @author cyy
+ * @date 2020年4月27日
+ */
+@Setter
+@Getter
+public class BodyInfoQuery extends PageDto {
+
+ private static final long serialVersionUID = 1L;
+
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private Date minDate;
+ @DateTimeFormat(pattern = "yyyy-MM-dd")
+ private Date maxDate;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/dto/BodyInfoSaveDto.java b/src/main/java/top/beansprout/health/model/dto/BodyInfoSaveDto.java
new file mode 100644
index 0000000..d3cbbeb
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/dto/BodyInfoSaveDto.java
@@ -0,0 +1,46 @@
+package top.beansprout.health.model.dto;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+
+import org.hibernate.validator.constraints.Range;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: BodyInfoSaveDto
+ * Description: 身体信息保存
+ *
+ * @author cyy
+ * @date 2020年4月27日
+ */
+@Setter
+@Getter
+public class BodyInfoSaveDto implements Serializable {
+
+ private static final long serialVersionUID = 80577054311531170L;
+
+ @NotNull(message = "低血压不能为空")
+ @Range(min = 0, max = 200, message = "低血压只能在0到200之间")
+ private Integer lowBloodPressure;
+ @NotNull(message = "高血压不能为空")
+ @Range(min = 0, max = 200, message = "高血压只能在0到200之间")
+ private Integer highBloodPressure;
+ @NotNull(message = "心率不能为空")
+ @Range(min = 40, max = 200, message = "心率只能在40到200之间")
+ private Integer heartRate;
+ @NotNull(message = "体温不能为空")
+ @Range(min = 33, max = 45, message = "体温只能在33到45之间")
+ private Double temperature;
+ @NotNull(message = "食欲不能为空")
+ @Range(min = 1, max = 6, message = "食欲只能在1到6之间")
+ private Integer appetite;
+ @NotNull(message = "体重不能为空")
+ @Range(min = 0, max = 200, message = "体重只能在33到40之间")
+ private Double weight;
+ // 步数
+ private int numberOfStep;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/dto/PageDto.java b/src/main/java/top/beansprout/health/model/dto/PageDto.java
new file mode 100644
index 0000000..571b989
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/dto/PageDto.java
@@ -0,0 +1,32 @@
+package top.beansprout.health.model.dto;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: PageDto
+ * Description: 列表请求
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/22 22:32
+ */
+@Setter
+@Getter
+public class PageDto implements Serializable {
+
+ private static final long serialVersionUID = -1796187511693230421L;
+
+ @Min(value = 1, message = "当前页最小页码为1")
+ private int page = 1;
+
+ @Min(value = 1, message = "每页展示条数最小为1")
+ @Max(value = 100, message = "每页展示条数最大为100")
+ private int pageSize = 10;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/dto/UserLoginDto.java b/src/main/java/top/beansprout/health/model/dto/UserLoginDto.java
new file mode 100644
index 0000000..5e8ccce
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/dto/UserLoginDto.java
@@ -0,0 +1,33 @@
+package top.beansprout.health.model.dto;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotBlank;
+
+import org.hibernate.validator.constraints.Length;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: UserLoginDto
+ * Description: 用户登录
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月25日 下午8:11:06
+ * @Version: 1.0.0
+ */
+@Setter
+@Getter
+public class UserLoginDto implements Serializable {
+
+ private static final long serialVersionUID = 6865880222397602631L;
+
+ @NotBlank(message = "账户不能为空")
+ @Length(max = 20, message = "账户不能超过20位")
+ private String userName;
+ @NotBlank(message = "密码不能为空")
+ @Length(max = 20, message = "密码不能超过20位")
+ private String passWord;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/dto/UserRegisterDto.java b/src/main/java/top/beansprout/health/model/dto/UserRegisterDto.java
new file mode 100644
index 0000000..28720df
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/dto/UserRegisterDto.java
@@ -0,0 +1,36 @@
+package top.beansprout.health.model.dto;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotBlank;
+
+import org.hibernate.validator.constraints.Length;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: UserRegisterDto
+ * Description: 用户注册
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月26日 上午12:54:09
+ * @Version: 1.0.0
+ */
+@Setter
+@Getter
+public class UserRegisterDto implements Serializable {
+
+ private static final long serialVersionUID = -4003225859721099921L;
+
+ @NotBlank(message = "用户名或姓名不能为空")
+ @Length(min = 2, max = 10, message = "用户名或姓名必须在2~10位之间")
+ private String userName;
+ @NotBlank(message = "密码不能为空")
+ @Length(min = 6, max = 10, message = "密码必须在6~10位之间")
+ private String passWord;
+ @NotBlank(message = "确认密码不能为空")
+ @Length(min = 6, max = 10, message = "确认密码必须在6~10位之间")
+ private String confirmPassWord;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/dto/UserUpdateInfoDto.java b/src/main/java/top/beansprout/health/model/dto/UserUpdateInfoDto.java
new file mode 100644
index 0000000..1e9fdb4
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/dto/UserUpdateInfoDto.java
@@ -0,0 +1,39 @@
+package top.beansprout.health.model.dto;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import org.hibernate.validator.constraints.Length;
+import org.springframework.web.multipart.MultipartFile;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: UserUpdateInfoDto
+ * Description: 用户信息修改
+ *
+ * @author cyy
+ * @date 2020年4月30日
+ */
+@Setter
+@Getter
+public class UserUpdateInfoDto implements Serializable {
+
+ private static final long serialVersionUID = 7815632813092576575L;
+
+ // 昵称
+ @NotBlank(message = "昵称不能为空")
+ @Length(min = 2, max = 10, message = "昵称只能为2~10个字符")
+ private String nickName;
+ // 邮件
+ @Email(message = "邮箱格式不正确")
+ private String email;
+ // 头像
+ @NotNull(message = "头像不能为空")
+ private MultipartFile headUrl;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/dto/UserUpdatePasswordDto.java b/src/main/java/top/beansprout/health/model/dto/UserUpdatePasswordDto.java
new file mode 100644
index 0000000..e4f3d1f
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/dto/UserUpdatePasswordDto.java
@@ -0,0 +1,33 @@
+package top.beansprout.health.model.dto;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotBlank;
+
+import org.hibernate.validator.constraints.Length;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: UserUpdatePasswordDto
+ * Description: 用户修改密码
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月27日 上午12:32:39
+ * @Version: 1.0.0
+ */
+@Setter
+@Getter
+public class UserUpdatePasswordDto implements Serializable {
+
+ private static final long serialVersionUID = 8674837059264557849L;
+
+ @NotBlank(message = "密码不能为空")
+ @Length(min = 6, max = 10, message = "密码必须在6~10位之间")
+ private String passWord;
+ @NotBlank(message = "确认密码不能为空")
+ @Length(min = 6, max = 10, message = "确认密码必须在6~10位之间")
+ private String confirmPassWord;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/entity/BaseEntity.java b/src/main/java/top/beansprout/health/model/entity/BaseEntity.java
new file mode 100644
index 0000000..e8dde47
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/entity/BaseEntity.java
@@ -0,0 +1,24 @@
+package top.beansprout.health.model.entity;
+
+import java.io.Serializable;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: BaseEntity
+ * Description: 实体类对象的父类-公共字段
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/22 16:50
+ */
+@Setter
+@Getter
+public abstract class BaseEntity implements Serializable {
+
+ private static final long serialVersionUID = 4402927657355642335L;
+
+ private int id;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/entity/BaseInsertEntity.java b/src/main/java/top/beansprout/health/model/entity/BaseInsertEntity.java
new file mode 100644
index 0000000..049e75c
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/entity/BaseInsertEntity.java
@@ -0,0 +1,30 @@
+package top.beansprout.health.model.entity;
+
+import java.util.Date;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: BaseUpdateEntity
+ * Description: 实体类插入对象的父类-公共字段
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/23 23:52
+ */
+@Setter
+@Getter
+public abstract class BaseInsertEntity extends BaseEntity {
+
+ private static final long serialVersionUID = -7314584145079382702L;
+
+ private Long creator;// 创建人id
+ private Date createTime;// 创建时间
+
+ public void init(long userId) {
+ this.creator = userId;
+ this.createTime = new Date();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/entity/BaseUpdateEntity.java b/src/main/java/top/beansprout/health/model/entity/BaseUpdateEntity.java
new file mode 100644
index 0000000..6e63e16
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/entity/BaseUpdateEntity.java
@@ -0,0 +1,38 @@
+package top.beansprout.health.model.entity;
+
+import java.util.Date;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: BaseUpdateEntity
+ * Description: 实体类用户对象的父类-公共字段
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/23 23:52
+ */
+@Setter
+@Getter
+public abstract class BaseUpdateEntity extends BaseEntity {
+
+ private static final long serialVersionUID = 1620758104430484420L;
+
+ private int creator;// 创建人id
+ private Date createTime;// 创建时间
+ private int updater;// 更新者id
+ private Date updateTime;// 更新时间
+
+ public void init(int userId) {
+ this.creator = userId;
+ this.createTime = new Date();
+ this.update(userId);
+ }
+
+ public void update(int userId) {
+ this.updater = userId;
+ this.updateTime = new Date();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/entity/TBodyInfo.java b/src/main/java/top/beansprout/health/model/entity/TBodyInfo.java
new file mode 100644
index 0000000..a4a9b9b
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/entity/TBodyInfo.java
@@ -0,0 +1,56 @@
+package top.beansprout.health.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * Title: TBodyInfo
+ * Description: 身体信息表
+ * @author cyy
+ * @date 2020年4月27日
+ */
+@Setter
+@Getter
+public class TBodyInfo extends BaseUpdateEntity {
+
+ private static final long serialVersionUID = -7707228772072459981L;
+
+ // 打开时间
+ private Date createTime;
+
+ // 舒张压
+ private int lowBloodPressure;
+ // 收缩压
+ private int highBloodPressure;
+ // 心率
+ private int heartRate;
+ // 体温
+ private double temperature;
+ // 食欲
+ private int appetite;
+ // 体重
+ private double weight;
+ // 步数
+ private int numberOfStep;
+
+ public String getAppetiteDes(int appetite){
+ switch (appetite){
+ case 1:
+ return "非常差";
+ case 2:
+ return "差";
+ case 3:
+ return "一般";
+ case 4:
+ return "良好";
+ case 5:
+ return "很好";
+ case 6:
+ return "非常好";
+ }
+ return "";
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/entity/THealthConfig.java b/src/main/java/top/beansprout/health/model/entity/THealthConfig.java
new file mode 100644
index 0000000..cacfb3c
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/entity/THealthConfig.java
@@ -0,0 +1,35 @@
+package top.beansprout.health.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: THealthConfig
+ * Description: 健康信息配置表
+ * @author cyy
+ * @date 2020年4月27日
+ */
+@Setter
+@Getter
+public class THealthConfig extends BaseUpdateEntity {
+
+ private static final long serialVersionUID = 6928075281678415808L;
+
+ // 最小低血压
+ private int minLowBloodPressure;
+ // 最大低血压
+ private int maxLowBloodPressure;
+ // 最小高血压
+ private int minHighBloodPressure;
+ // 最大高血压
+ private int maxHighBloodPressure;
+ // 最小心率
+ private int minHeartRate;
+ // 最大心率
+ private int maxHeartRate;
+ // 最小体温
+ private double minTemperature;
+ // 最大体温
+ private double maxTemperature;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/entity/TUser.java b/src/main/java/top/beansprout/health/model/entity/TUser.java
new file mode 100644
index 0000000..1105b63
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/entity/TUser.java
@@ -0,0 +1,30 @@
+package top.beansprout.health.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: TUser
+ * Description: 用户表
+ *
+ * @author beansprout
+ * @date 2020年4月22日
+ */
+@Setter
+@Getter
+public class TUser extends BaseUpdateEntity {
+
+ private static final long serialVersionUID = 7849174171033853904L;
+
+ // 用户名
+ private String nickName;
+ // 账户
+ private String userName;
+ // 密码
+ private String password;
+ // 邮箱
+ private String email;
+ // 头像
+ private String headUrl;
+
+}
diff --git a/src/main/java/top/beansprout/health/model/vo/AuthVo.java b/src/main/java/top/beansprout/health/model/vo/AuthVo.java
new file mode 100644
index 0000000..1253a15
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/AuthVo.java
@@ -0,0 +1,26 @@
+package top.beansprout.health.model.vo;
+
+import javax.mail.Authenticator;
+import javax.mail.PasswordAuthentication;
+
+/**
+ * Title: AuthVo
+ * Description: 发送邮箱用户
+ *
+ * @author ateenliu
+ * @date 2022年4月6日
+ */
+
+public class AuthVo extends Authenticator {
+ private String username = "";
+ private String password = "";
+
+ public AuthVo(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+ public PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+
+}
diff --git a/src/main/java/top/beansprout/health/model/vo/BodyInfoDetailVo.java b/src/main/java/top/beansprout/health/model/vo/BodyInfoDetailVo.java
new file mode 100644
index 0000000..e51c4c3
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/BodyInfoDetailVo.java
@@ -0,0 +1,65 @@
+package top.beansprout.health.model.vo;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: BodyInfoDetailVo
+ * Description: 身体信息详情
+ *
+ * @author cyy
+ * @date 2020年4月28日
+ */
+@Setter
+@Getter
+public class BodyInfoDetailVo implements Serializable {
+
+ private static final long serialVersionUID = -2831120593213278473L;
+
+ // id
+ private int id;
+ // 打卡时间
+ private Date createTime;
+
+ // 舒张压
+ private int lowBloodPressure;
+ // 舒张压状态
+ private int lowBloodPressureStatus;
+ // 舒张压描述
+ private String lowBloodPressureDesc;
+
+ // 收缩压
+ private int highBloodPressure;
+ // 收缩压状态
+ private int highBloodPressureStatus;
+ // 收缩压描述
+ private String highBloodPressureDesc;
+
+ // 血压
+ private String bloodPressureDesc;
+
+ // 心率
+ private int heartRate;
+ // 心率状态
+ private int heartRateStatus;
+ // 心率描述
+ private String heartRateDesc;
+
+ // 体温
+ private double temperature;
+ // 体温状态
+ private int temperatureStatus;
+ // 体温描述
+ private String temperatureDesc;
+
+ // 食欲
+ private int appetite;
+ // 体重
+ private double weight;
+ // 步数
+ private int numberOfStep;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/vo/BodyInfoStatisticsVo.java b/src/main/java/top/beansprout/health/model/vo/BodyInfoStatisticsVo.java
new file mode 100644
index 0000000..5e895a8
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/BodyInfoStatisticsVo.java
@@ -0,0 +1,43 @@
+package top.beansprout.health.model.vo;
+
+import java.io.Serializable;
+import java.util.List;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Title: BodyInfoStatisticsVo
+ * Description: 健康信息统计
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月28日 下午11:32:05
+ * @Version: 1.0.0
+ */
+@Setter
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class BodyInfoStatisticsVo implements Serializable {
+
+ private static final long serialVersionUID = 8627681890953284164L;
+
+ // 类别名字
+ private List typeNames;
+ // 类别数据
+ private List bodyInfoVos;
+
+ @Setter
+ @Getter
+ @NoArgsConstructor
+ @AllArgsConstructor
+ public static class BodyInfoVo {
+ // 类别名
+ private String typeName;
+ // 数据
+ private List datas;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/vo/BusinessException.java b/src/main/java/top/beansprout/health/model/vo/BusinessException.java
new file mode 100644
index 0000000..8bbf34f
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/BusinessException.java
@@ -0,0 +1,34 @@
+package top.beansprout.health.model.vo;
+
+import lombok.Getter;
+
+/**
+ * Title: BusinessException
+ * Description: 业务异常
+ * @author cyy
+ * @date 2020年4月23日
+ */
+@Getter
+public class BusinessException extends RuntimeException {
+
+ private static final long serialVersionUID = -5846808029174051527L;
+
+ private final R r;
+
+ public BusinessException() {
+ r = R.budil().result(false).message("系统异常");
+ }
+
+ public BusinessException(String message) {
+ r = R.budil().result(false).message(message);
+ }
+
+ public BusinessException(String path, String message) {
+ r = R.budil().result(false).message(message).path(path);
+ }
+
+ public BusinessException(String message, Object data) {
+ r = R.budil().result(false).message(message).data(data);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/vo/PageVo.java b/src/main/java/top/beansprout/health/model/vo/PageVo.java
new file mode 100644
index 0000000..253d76f
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/PageVo.java
@@ -0,0 +1,73 @@
+package top.beansprout.health.model.vo;
+
+import java.io.Serializable;
+import java.util.List;
+
+import com.github.pagehelper.PageInfo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: PageVo
+ * Description: 列表返回
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/22 22:15
+ */
+@Setter
+@Getter
+public class PageVo implements Serializable {
+
+ private static final long serialVersionUID = 7039024431017840683L;
+
+ // 当前页
+ private long page;
+ // 每页展示数量
+ private long pageSize;
+ // 是否是第一页true第一
+ private boolean first = false;
+ // 是否是最后一页true最后
+ private boolean last = false;
+ // 总页数
+ private int totalPages;
+ // 总数据条数
+ private int totalElements;
+ // 当前页数据条数
+ private int numberOfElements;
+ // 数据集
+ private List> content;
+
+ public PageVo(int page, int pageSize, int totalElements, List content) {
+ this.pageSize = pageSize;
+ this.totalElements = totalElements;
+ this.page = page <= 0 ? 1 : page;
+ this.totalPages = totalElements == 0 ? 0
+ : (totalElements % pageSize) == 0 ? totalElements / pageSize : (totalElements / pageSize) + 1;
+ this.first = page == 1 ? true : false;
+ this.last = ((page == this.totalPages) || (content.size() < 1)) ? true : false;
+ this.content = content;
+ this.numberOfElements = this.content.size();
+ }
+
+ public PageVo(PageInfo pageInfo) {
+ this.page = pageInfo.getPageNum();
+ this.pageSize = pageInfo.getPageSize();
+ this.totalElements = (int) pageInfo.getTotal();
+ this.totalPages = pageInfo.getPages();
+ this.first = page == 1 ? true : false;
+ this.content = pageInfo.getList();
+ this.last = ((page == this.totalPages) || (content.size() < 1)) ? true : false;
+ this.numberOfElements = this.content.size();
+ }
+
+ public static PageVo of(int page, int pageSize, int totalElements, List content) {
+ return new PageVo(page, pageSize, totalElements, content);
+ }
+
+ public static PageVo of(PageInfo pageInfo) {
+ return new PageVo(pageInfo);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/vo/R.java b/src/main/java/top/beansprout/health/model/vo/R.java
new file mode 100644
index 0000000..d2d9c7c
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/R.java
@@ -0,0 +1,110 @@
+package top.beansprout.health.model.vo;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.web.servlet.ModelAndView;
+
+import lombok.Getter;
+import lombok.Setter;
+import top.beansprout.health.constant.SysConstant;
+
+/**
+ * Title: R
+ * Description: 返回结果
+ * @author cyy
+ * @date 2020年4月23日
+ */
+@Setter
+@Getter
+public class R extends HashMap implements Serializable {
+
+ private static final long serialVersionUID = -7261945562236369523L;
+
+ public static final String FIELD_RESULT = "result";
+ public static final String FIELD_MESSAGE = "message";
+ public static final String FIELD_DATA = "data";
+ public static final String FIELD_PATH = "path";
+
+ public static final String FAILED_FIELD_REQUEST_ID = SysConstant.INIT_FIELD_REQUEST_REQUEST_ID;
+ public static final String FAILED_FIELD_TIME_STAMP = "timestamp";
+ public static final String FAILED_FIELD_PATH = "path";
+ public static final String FAILED_FIELD_ERROR = "error";
+ public static final String FAILED_FIELD_ERRORS = "errors";
+
+ // 结果
+ private boolean result;
+ // 描述
+ private String message;
+ // 数据
+ private Object data;
+ // 页面
+ private String path;
+
+ public static R budil() {
+ return new R();
+ }
+
+ public R result(boolean result) {
+ this.put(FIELD_RESULT, result);
+ this.result = result;
+ return this;
+ }
+
+ public R message(String message) {
+ this.put(FIELD_MESSAGE, message);
+ this.message = message;
+ return this;
+ }
+
+ public R data(Object data) {
+ this.put(FIELD_DATA, data);
+ this.data = data;
+ return this;
+ }
+
+ public R path(String path) {
+ this.put(FIELD_PATH, path);
+ this.path = path;
+ return this;
+ }
+
+ public static ModelAndView ok() {
+ return of("/", null);
+ }
+
+ public static R okAsAjax() {
+ return okAsAjax(null);
+ }
+
+ public static ModelAndView ok(Map data) {
+ return of("/", data);
+ }
+
+ public static R okAsAjax(Object data) {
+ return ofAsAjax(true, "成功", data);
+ }
+
+ public static ModelAndView failed() {
+ return of("/500", null);
+ }
+
+ public static ModelAndView failed(Map data) {
+ return of("/500", data);
+ }
+
+ public static ModelAndView failed(String path, Map data) {
+ return of(path, data);
+ }
+
+ public static ModelAndView of(String path, Map data) {
+ final ModelAndView modelAndView = new ModelAndView(path, data);
+ return modelAndView;
+ }
+
+ public static R ofAsAjax(boolean result, String message, Object data) {
+ return budil().result(result).message(message).data(data);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/vo/RequestVo.java b/src/main/java/top/beansprout/health/model/vo/RequestVo.java
new file mode 100644
index 0000000..e2b6885
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/RequestVo.java
@@ -0,0 +1,31 @@
+package top.beansprout.health.model.vo;
+
+import java.io.Serializable;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Title: RequestVo
+ * Description: 请求数据
+ * @author cyy
+ * @date 2020年4月23日
+ */
+@Setter
+@Getter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class RequestVo implements Serializable {
+
+ private static final long serialVersionUID = -2460516900904070899L;
+
+ // 基本项目地址
+ private String basePath;
+ // 用户信息
+ private UserLoginVo user;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/vo/UserInfoVo.java b/src/main/java/top/beansprout/health/model/vo/UserInfoVo.java
new file mode 100644
index 0000000..45b3137
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/UserInfoVo.java
@@ -0,0 +1,36 @@
+package top.beansprout.health.model.vo;
+
+import java.io.Serializable;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * Title: UserInfoVo
+ * Description: 用户信息
+ *
+ * @author cyy
+ * @date 2020年4月30日
+ */
+@Setter
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class UserInfoVo implements Serializable {
+
+ private static final long serialVersionUID = 8389631895704876733L;
+
+ // 用户id
+ private int id;
+ // 用户名
+ private String nickName;
+ // 账户
+ private String userName;
+ // 邮箱
+ private String email;
+ // 头像
+ private String headUrl;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/model/vo/UserLoginVo.java b/src/main/java/top/beansprout/health/model/vo/UserLoginVo.java
new file mode 100644
index 0000000..40496d5
--- /dev/null
+++ b/src/main/java/top/beansprout/health/model/vo/UserLoginVo.java
@@ -0,0 +1,33 @@
+package top.beansprout.health.model.vo;
+
+import java.io.Serializable;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Title: UserLoginVo
+ * Description:
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月25日 下午8:49:19
+ * @Version: 1.0.0
+ */
+@Setter
+@Getter
+public class UserLoginVo implements Serializable {
+
+ private static final long serialVersionUID = -7027379021523096696L;
+
+ // 用户id
+ private int id;
+ // 用户名
+ private String nickName;
+ // 账户
+ private String userName;
+ // 邮箱
+ private String email;
+ // 头像
+ private String headUrl;
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/service/HealthService.java b/src/main/java/top/beansprout/health/service/HealthService.java
new file mode 100644
index 0000000..595b26c
--- /dev/null
+++ b/src/main/java/top/beansprout/health/service/HealthService.java
@@ -0,0 +1,40 @@
+package top.beansprout.health.service;
+
+import java.util.Date;
+import java.util.List;
+
+import top.beansprout.health.model.dto.BodyInfoQuery;
+import top.beansprout.health.model.dto.BodyInfoSaveDto;
+import top.beansprout.health.model.entity.TBodyInfo;
+import top.beansprout.health.model.vo.BodyInfoDetailVo;
+import top.beansprout.health.model.vo.BodyInfoStatisticsVo;
+import top.beansprout.health.model.vo.PageVo;
+
+/**
+ * Title: HealthService
+ * Description: 健康业务接口
+ *
+ * @author cyy
+ * @date 2020年4月27日
+ */
+public interface HealthService {
+
+ /** 保存身体信息 **/
+ void saveBodyInfo(int userId, BodyInfoSaveDto bodyInfoSaveDto);
+
+ /** 获取身体信息列表 **/
+ PageVo bodyInfoList(int userId, BodyInfoQuery bodyInfoQuery);
+
+ /** 删除身体信息 **/
+ void deleteBodyInfo(int userId, int id);
+
+ /** 获取身体信息 **/
+ BodyInfoDetailVo getBodyInfo(int userId, int id);
+
+ /** 获取身体信息统计 **/
+ BodyInfoStatisticsVo getBodyStatistics(int userId, Date date);
+
+ /** 获取惊奇身体信息统计 **/
+ List recentBodyInfoList(int userId);
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/service/UserService.java b/src/main/java/top/beansprout/health/service/UserService.java
new file mode 100644
index 0000000..ff2610e
--- /dev/null
+++ b/src/main/java/top/beansprout/health/service/UserService.java
@@ -0,0 +1,37 @@
+package top.beansprout.health.service;
+
+import javax.servlet.http.HttpServletRequest;
+
+import top.beansprout.health.model.dto.UserLoginDto;
+import top.beansprout.health.model.dto.UserRegisterDto;
+import top.beansprout.health.model.dto.UserUpdateInfoDto;
+import top.beansprout.health.model.dto.UserUpdatePasswordDto;
+import top.beansprout.health.model.vo.UserInfoVo;
+import top.beansprout.health.model.vo.UserLoginVo;
+
+/**
+ * Title: UserService
+ * Description: 用户业务接口
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月25日 下午8:01:14
+ * @Version: 1.0.0
+ */
+public interface UserService {
+
+ /** 登录逻辑 **/
+ UserLoginVo login(UserLoginDto userLoginDto);
+
+ /** 注册逻辑 **/
+ void register(UserRegisterDto userRegisterDto);
+
+ /** 登出 **/
+ void logout(HttpServletRequest request);
+
+ /** 修改密码 **/
+ void updatePassword(HttpServletRequest request, int userId, UserUpdatePasswordDto updatePasswordDto);
+
+ /** 修改用户信息 **/
+ UserInfoVo updateUserInfo(int userId, UserUpdateInfoDto userUpdateInfoDto);
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/service/impl/HealthServiceImpl.java b/src/main/java/top/beansprout/health/service/impl/HealthServiceImpl.java
new file mode 100644
index 0000000..752b636
--- /dev/null
+++ b/src/main/java/top/beansprout/health/service/impl/HealthServiceImpl.java
@@ -0,0 +1,299 @@
+package top.beansprout.health.service.impl;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+
+import top.beansprout.health.mapper.TBodyInfoMapper;
+import top.beansprout.health.mapper.THealthConfigMapper;
+import top.beansprout.health.model.dto.BodyInfoQuery;
+import top.beansprout.health.model.dto.BodyInfoSaveDto;
+import top.beansprout.health.model.entity.TBodyInfo;
+import top.beansprout.health.model.vo.BodyInfoDetailVo;
+import top.beansprout.health.model.vo.BodyInfoStatisticsVo;
+import top.beansprout.health.model.vo.BodyInfoStatisticsVo.BodyInfoVo;
+import top.beansprout.health.model.vo.BusinessException;
+import top.beansprout.health.model.vo.PageVo;
+import top.beansprout.health.service.HealthService;
+import top.beansprout.health.util.DateUtils;
+import top.beansprout.health.util.PublicUtils;
+
+/**
+ * Title: HealthServiceImpl
+ * Description: 健康业务逻辑操作
+ *
+ * @author cyy
+ * @date 2020年4月27日
+ */
+@Service
+public class HealthServiceImpl implements HealthService {
+
+ @Autowired
+ private THealthConfigMapper healthConfigMapper;
+ @Autowired
+ private TBodyInfoMapper bodyInfoMapper;
+
+ @Override
+ @Transactional
+ public void saveBodyInfo(int userId, BodyInfoSaveDto bodyInfoSaveDto) {
+ // 判断今天是否已经录入过
+ final int countByOneAsToDay = bodyInfoMapper.countByOneAsToDay(userId);
+ if (countByOneAsToDay > 0)
+ throw new BusinessException("今天已经打过卡了");
+
+ final TBodyInfo tBodyInfo = new TBodyInfo();
+ PublicUtils.copyBean(bodyInfoSaveDto, tBodyInfo);
+ tBodyInfo.init(userId);
+ bodyInfoMapper.insertByOne(tBodyInfo);
+ }
+
+ @Override
+ public List recentBodyInfoList(int userId){
+ List tBodyInfoList = bodyInfoMapper.selectRecentByUserId(userId);
+ return tBodyInfoList;
+ }
+
+ @Override
+ public PageVo bodyInfoList(int userId, BodyInfoQuery bodyInfoQuery) {
+ if ((bodyInfoQuery.getMinDate() != null) && (bodyInfoQuery.getMaxDate() != null)) {
+ if (bodyInfoQuery.getMinDate().after(bodyInfoQuery.getMaxDate()))
+ throw new BusinessException("最小时间不能大于最大时间");
+ }
+ PageHelper.startPage(bodyInfoQuery.getPage(), bodyInfoQuery.getPageSize());
+ final PageInfo pageInfo = new PageInfo<>(
+ bodyInfoMapper.selectByUserId(userId, bodyInfoQuery.getMinDate(), bodyInfoQuery.getMaxDate()));
+ return PageVo.of(pageInfo);
+ }
+
+ @Override
+ @Transactional
+ public void deleteBodyInfo(int userId, int id) {
+ bodyInfoMapper.deleteByOne(id, userId);
+ }
+
+ @Override
+ public BodyInfoDetailVo getBodyInfo(int userId, int id) {
+ final TBodyInfo bodyInfo = bodyInfoMapper.selectByUserOne(id, userId);
+ if (bodyInfo == null)
+ throw new BusinessException("该身体信息不存在");
+
+ BodyInfoDetailVo bodyInfoDetailVo = new BodyInfoDetailVo();
+ PublicUtils.copyBean(bodyInfo, bodyInfoDetailVo);
+ bodyInfoDetailVo = bodyInfoCompare(bodyInfoDetailVo);
+ return bodyInfoDetailVo;
+ }
+
+ /** 健康信息比较 **/
+ private BodyInfoDetailVo bodyInfoCompare(BodyInfoDetailVo bodyInfoDetailVo) {
+ // TODO
+ // 血压
+ bodyInfoDetailVo = bloodPressureCompare(bodyInfoDetailVo);
+ // 心率
+ final int heartRate = bodyInfoDetailVo.getHeartRate();
+ bodyInfoDetailVo.setHeartRateStatus(heartRateStatus(heartRate));
+ bodyInfoDetailVo.setHeartRateDesc(statusDesc(bodyInfoDetailVo.getHeartRateStatus()));
+ // 体温
+ final double temperature = bodyInfoDetailVo.getTemperature();
+ bodyInfoDetailVo.setTemperatureStatus(temperatureStatus(temperature));
+ bodyInfoDetailVo.setTemperatureDesc(statusDesc(bodyInfoDetailVo.getTemperatureStatus()));
+ return bodyInfoDetailVo;
+ }
+
+ /** 血压判断 **/
+ private BodyInfoDetailVo bloodPressureCompare(BodyInfoDetailVo bodyInfoDetailVo) {
+ // 收缩压
+ // 正常范围收缩压90~139mmHg
+ final int minHighBloodPressure = 90;
+ final int maxHighBloodPressure = 139;
+ // 舒张压
+ // 舒张压60~89mmHg
+ final int minLowBloodPressure = 60;
+ final int maxLowBloodPressure = 89;
+ // 高血压:成人收缩压≥140mmHg和(或)舒张压≥90mmHg
+ // 低血压:血压低于90/60mmHg
+
+ if (bodyInfoDetailVo.getLowBloodPressure() > maxLowBloodPressure) {
+ bodyInfoDetailVo.setLowBloodPressureStatus(1);
+ bodyInfoDetailVo.setLowBloodPressureDesc(statusDesc(bodyInfoDetailVo.getLowBloodPressureStatus()));
+ } else if (bodyInfoDetailVo.getLowBloodPressure() < minLowBloodPressure) {
+ bodyInfoDetailVo.setLowBloodPressureStatus(-1);
+ bodyInfoDetailVo.setLowBloodPressureDesc(statusDesc(bodyInfoDetailVo.getLowBloodPressureStatus()));
+ } else {
+ bodyInfoDetailVo.setLowBloodPressureDesc(statusDesc(bodyInfoDetailVo.getLowBloodPressureStatus()));
+ }
+
+ if (bodyInfoDetailVo.getHighBloodPressure() > maxHighBloodPressure) {
+ bodyInfoDetailVo.setHighBloodPressureStatus(1);
+ bodyInfoDetailVo.setHighBloodPressureDesc(statusDesc(bodyInfoDetailVo.getHighBloodPressureStatus()));
+ } else if (bodyInfoDetailVo.getHighBloodPressure() < minHighBloodPressure) {
+ bodyInfoDetailVo.setHighBloodPressureStatus(-1);
+ bodyInfoDetailVo.setHighBloodPressureDesc(statusDesc(bodyInfoDetailVo.getHighBloodPressureStatus()));
+ } else {
+ bodyInfoDetailVo.setHighBloodPressureDesc(statusDesc(bodyInfoDetailVo.getHighBloodPressureStatus()));
+ }
+
+ if ((bodyInfoDetailVo.getLowBloodPressureStatus() == 1)
+ && (bodyInfoDetailVo.getHighBloodPressureStatus() == 1)) {
+ bodyInfoDetailVo.setBloodPressureDesc("该用户为高血压");
+ } else if ((bodyInfoDetailVo.getLowBloodPressureStatus() == -1)
+ && (bodyInfoDetailVo.getHighBloodPressureStatus() == -1)) {
+ bodyInfoDetailVo.setBloodPressureDesc("该用户为低血压");
+ } else if ((bodyInfoDetailVo.getLowBloodPressureStatus() == 0)
+ && (bodyInfoDetailVo.getHighBloodPressureStatus() == 0)) {
+ bodyInfoDetailVo.setBloodPressureDesc("该用户血压正常");
+ }
+
+ /*
+ * if ((bodyInfoDetailVo.getLowBloodPressure() > maxLowBloodPressure) ||
+ * (bodyInfoDetailVo.getHighBloodPressure() > maxHighBloodPressure)) {
+ * bodyInfoDetailVo.setLowBloodPressureStatus(1);
+ * bodyInfoDetailVo.setLowBloodPressureDesc(statusDesc(bodyInfoDetailVo.
+ * getLowBloodPressureStatus()));
+ * bodyInfoDetailVo.setHighBloodPressureStatus(1);
+ * bodyInfoDetailVo.setHighBloodPressureDesc(statusDesc(bodyInfoDetailVo.
+ * getHighBloodPressureStatus())); } else if
+ * ((bodyInfoDetailVo.getLowBloodPressure() < minLowBloodPressure) ||
+ * (bodyInfoDetailVo.getHighBloodPressure() < minHighBloodPressure)) {
+ * bodyInfoDetailVo.setLowBloodPressureStatus(-1);
+ * bodyInfoDetailVo.setLowBloodPressureDesc(statusDesc(bodyInfoDetailVo.
+ * getLowBloodPressureStatus()));
+ * bodyInfoDetailVo.setHighBloodPressureStatus(-1);
+ * bodyInfoDetailVo.setHighBloodPressureDesc(statusDesc(bodyInfoDetailVo.
+ * getHighBloodPressureStatus())); } else {
+ * bodyInfoDetailVo.setLowBloodPressureDesc(statusDesc(bodyInfoDetailVo.
+ * getLowBloodPressureStatus()));
+ * bodyInfoDetailVo.setHighBloodPressureDesc(statusDesc(bodyInfoDetailVo.
+ * getHighBloodPressureStatus())); }
+ */
+ return bodyInfoDetailVo;
+ }
+
+ /** 心率判断 **/
+ private int heartRateStatus(int heartRate) {
+ final int min = 60;
+ final int max = 80;
+ final int max1 = 100;
+ final int max2 = 120;
+ if (heartRate < min)
+ return -1;
+ else if ((heartRate >= min) && (heartRate <= max))
+ return 0;
+ else if ((heartRate > max) && (heartRate < max1))
+ return 1;
+ else if ((heartRate > max1) && (heartRate < max2))
+ return 2;
+ else
+ return 4;
+ }
+
+ /** 体温判断 **/
+ private int temperatureStatus(double temperature) {
+ final Double min = 36.1;
+ final Double max = 37.3;
+ final Double max1 = 38.1;
+ final Double max2 = 39.1;
+ final Double max3 = 41.1;
+ if (temperature < min)
+ return -1;
+ else if ((temperature >= min) && (temperature <= max))
+ return 0;
+ else if ((temperature > max) && (temperature < max1))
+ return 1;
+ else if ((temperature > max1) && (temperature < max2))
+ return 2;
+ else if ((temperature > max2) && (temperature < max3))
+ return 3;
+ else
+ return 4;
+ }
+
+ /** 状态描述语 **/
+ private String statusDesc(int status) {
+ switch (status) {
+ case -1:
+ return "低";
+ case 1:
+ return "高";
+ case 2:
+ return "很高";
+ case 3:
+ return "严重";
+ case 4:
+ return "非常严重";
+ default:
+ return "正常";
+ }
+ }
+
+ @Override
+ public BodyInfoStatisticsVo getBodyStatistics(int userId, Date date) {
+ LocalDateTime now = null;
+ if (date == null) {
+ now = LocalDateTime.now();
+ } else {
+ now = DateUtils.convertDateToLDT(date);
+ }
+ final LocalDateTime startWeekDateTime = DateUtils.getDayStart(DateUtils.getWeekBegin(now));
+ final Date startWeek = DateUtils.convertLDTToDate(startWeekDateTime);
+ final Date endWeek = DateUtils.convertLDTToDate(DateUtils.getDayEnd(DateUtils.getWeekEnd(now)));
+ final List bodyInfos = bodyInfoMapper.selectByUserId(userId, startWeek, endWeek);
+
+ final List bodyInfoVos = fillBodyInfoVo(startWeekDateTime, bodyInfos);
+
+ return new BodyInfoStatisticsVo(bodyInfoVos.stream().map(BodyInfoVo::getTypeName).collect(Collectors.toList()),
+ bodyInfoVos);
+ }
+
+ /** 填充数据 **/
+ private List fillBodyInfoVo(LocalDateTime startWeekDateTime, List bodyInfos) {
+ final List bodyInfoVos = new ArrayList<>();
+ final List lowBloodPressures = new ArrayList<>();
+ final List highBloodPressures = new ArrayList<>();
+ final List heartRates = new ArrayList<>();
+ final List temperatures = new ArrayList<>();
+ final List numberOfSetps = new ArrayList<>();
+ for (int i = 0; i < 7; i++) {
+ final Long targetTime = DateUtils.getMilliByTime(DateUtils.plus(startWeekDateTime, i, ChronoUnit.DAYS));
+ boolean isSign = false;
+ for (final TBodyInfo tBodyInfo : bodyInfos) {
+ if (targetTime.equals(DateUtils.getMilliByTime(
+ DateUtils.getDayStart(DateUtils.convertDateToLDT(tBodyInfo.getCreateTime()))))) {
+ // 已打卡
+ isSign = true;
+ lowBloodPressures.add(tBodyInfo.getLowBloodPressure());
+ highBloodPressures.add(tBodyInfo.getHighBloodPressure());
+ heartRates.add(tBodyInfo.getHeartRate());
+ temperatures.add(tBodyInfo.getTemperature());
+ numberOfSetps.add(tBodyInfo.getNumberOfStep());
+ break;
+ }
+ }
+ if (!isSign) {
+ // 未打卡
+ lowBloodPressures.add(0);
+ highBloodPressures.add(0);
+ heartRates.add(0);
+ temperatures.add(0);
+ numberOfSetps.add(0);
+ }
+ }
+ bodyInfoVos.add(new BodyInfoVo("舒张压", lowBloodPressures));
+ bodyInfoVos.add(new BodyInfoVo("收缩压", highBloodPressures));
+ bodyInfoVos.add(new BodyInfoVo("心率", heartRates));
+ bodyInfoVos.add(new BodyInfoVo("体温", temperatures));
+ bodyInfoVos.add(new BodyInfoVo("步数", numberOfSetps));
+
+ return bodyInfoVos;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/service/impl/UserServiceImpl.java b/src/main/java/top/beansprout/health/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000..5b97b5a
--- /dev/null
+++ b/src/main/java/top/beansprout/health/service/impl/UserServiceImpl.java
@@ -0,0 +1,151 @@
+package top.beansprout.health.service.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import top.beansprout.health.constant.SysConstant;
+import top.beansprout.health.mapper.TUserMapper;
+import top.beansprout.health.model.dto.UserLoginDto;
+import top.beansprout.health.model.dto.UserRegisterDto;
+import top.beansprout.health.model.dto.UserUpdateInfoDto;
+import top.beansprout.health.model.dto.UserUpdatePasswordDto;
+import top.beansprout.health.model.entity.TUser;
+import top.beansprout.health.model.vo.BusinessException;
+import top.beansprout.health.model.vo.UserInfoVo;
+import top.beansprout.health.model.vo.UserLoginVo;
+import top.beansprout.health.service.UserService;
+import top.beansprout.health.util.PublicUtils;
+
+/**
+ * Title: UserServiceImpl
+ * Description: 用户业务逻辑操作
+ *
+ * @Auther: cyy
+ * @Date: 2020年4月25日 下午8:02:42
+ * @Version: 1.0.0
+ */
+@Service
+public class UserServiceImpl implements UserService {
+
+ @Autowired
+ private HttpServletRequest request;
+
+ @Autowired
+ private TUserMapper userMapper;
+
+ @Override
+ public UserLoginVo login(UserLoginDto userLoginDto) {
+ final TUser user = userMapper.selectByUserName(userLoginDto.getUserName());
+ if (PublicUtils.isBlank(user))
+ throw new BusinessException("用户不存在");
+ if (!user.getPassword().equalsIgnoreCase(userLoginDto.getPassWord()))
+ throw new BusinessException("密码不正确");
+
+ final UserLoginVo userLoginVo = new UserLoginVo();
+ PublicUtils.copyBean(user, userLoginVo);
+ request.getSession().setAttribute(SysConstant.INIT_FIELD_USER_VO, userLoginVo);
+ return userLoginVo;
+ }
+
+ @Override
+ @Transactional
+ public void register(UserRegisterDto userRegisterDto) {
+ if (!userRegisterDto.getPassWord().equalsIgnoreCase(userRegisterDto.getConfirmPassWord()))
+ throw new BusinessException("两次密码不一致");
+ final TUser user = userMapper.selectByUserName(userRegisterDto.getUserName());
+ if (PublicUtils.isNotBlank(user))
+ throw new BusinessException("该账户已经注册");
+ final TUser tUser = new TUser();
+ tUser.setUserName(userRegisterDto.getUserName());
+ tUser.setNickName(tUser.getUserName());
+ tUser.setPassword(userRegisterDto.getPassWord());
+ tUser.setHeadUrl("static/imgs/default.png"); //默认头像
+ userMapper.insertByOne(tUser);
+ }
+
+ @Override
+ public void logout(HttpServletRequest request) {
+ // 清除session
+ final Enumeration em = request.getSession().getAttributeNames();
+ while (em.hasMoreElements()) {
+ request.getSession().removeAttribute(em.nextElement().toString());
+ }
+ request.getSession().invalidate();
+ // 请求初始化信息
+ request.removeAttribute(SysConstant.INIT_FIELD_REQUEST_VO);
+ }
+
+ @Override
+ public void updatePassword(HttpServletRequest request, int userId, UserUpdatePasswordDto updatePasswordDto) {
+ if (!updatePasswordDto.getPassWord().equalsIgnoreCase(updatePasswordDto.getConfirmPassWord()))
+ throw new BusinessException("两次密码不一致");
+ final TUser user = userMapper.selectById(userId);
+ if (PublicUtils.isBlank(user))
+ throw new BusinessException("该账户不存在");
+ user.setPassword(updatePasswordDto.getPassWord());
+ userMapper.updateByOne(user);
+ // 清空session数据
+ logout(request);
+ }
+
+ @Override
+ public UserInfoVo updateUserInfo(int userId, UserUpdateInfoDto userUpdateInfoDto) {
+ // 原始文件名
+ String sourceName = userUpdateInfoDto.getHeadUrl().getOriginalFilename();
+ final String fileType = sourceName.substring(sourceName.lastIndexOf("."));
+ if (!".jpg".equals(fileType.toLowerCase()) && !".png".equals(fileType.toLowerCase()))
+ throw new BusinessException("只能上传jpg、png格式的图片");
+
+ // 获取文件上传的路径,在webapp下的static/upload/中
+ final String base = "E:\\Workspaces\\HM_upload\\";
+ final File fileDir = new File(base);
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+
+ // 讲文件上传到upload目录
+ sourceName = PublicUtils.join(PublicUtils.randomString(10), fileType);
+ final File upload = new File(PublicUtils.join(base, sourceName));
+ //System.out.println(upload.toString());
+ try {
+ userUpdateInfoDto.getHeadUrl().transferTo(upload);
+ } catch (final IOException e) {
+ throw new BusinessException("上传错误");
+ }
+
+ final String relativePath = PublicUtils.join("/HM_upload/", sourceName);
+
+ final TUser user = userMapper.selectById(userId);
+ if (PublicUtils.isBlank(user))
+ throw new BusinessException("该账户不存在");
+
+ user.setNickName(userUpdateInfoDto.getNickName());
+ user.setEmail(userUpdateInfoDto.getEmail());
+ user.setHeadUrl(relativePath);
+ userMapper.updateByUserInfoOne(user);
+
+ final UserInfoVo userInfo = new UserInfoVo();
+ PublicUtils.copyBean(user, userInfo);
+ refreshSessionUserInfo(userInfo);
+
+ return userInfo;
+ }
+
+ /** 刷新session中的用户信息 **/
+ private void refreshSessionUserInfo(UserInfoVo userInfo) {
+ final HttpSession session = request.getSession();
+ final UserLoginVo userLoginVo = (UserLoginVo) session.getAttribute(SysConstant.INIT_FIELD_USER_VO);
+ PublicUtils.copyBean(userInfo, userLoginVo);
+
+ session.setAttribute(SysConstant.INIT_FIELD_USER_VO, userLoginVo);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/util/CollectionUtils.java b/src/main/java/top/beansprout/health/util/CollectionUtils.java
new file mode 100644
index 0000000..aaa591e
--- /dev/null
+++ b/src/main/java/top/beansprout/health/util/CollectionUtils.java
@@ -0,0 +1,69 @@
+package top.beansprout.health.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Title: CollectionUtils
+ * Description: 集合工具类
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/19 22:37
+ */
+public class CollectionUtils {
+
+ /** 判断集合是否为null或者size=0 **/
+ public static boolean isEmpty(Collection coll) {
+ return CollectionUtils.isEmpty(coll);
+ }
+
+ public static boolean isNotEmpty(Collection coll) {
+ return !isEmpty(coll);
+ }
+
+ /**
+ * 将Iterable映射成列表
+ *
+ * @param src
+ * @param mapper
+ * @return
+ */
+ public static List map(final Iterable src, final Function super T, ? extends R> mapper) {
+ final List r = new ArrayList<>();
+ for (final T t : src) {
+ r.add(mapper.apply(t));
+ }
+ return r;
+ }
+
+ /**
+ * 数组转集合
+ * @param key
+ * @return
+ */
+ public static List arrayToList(T... key) {
+ return Arrays.asList(key);
+ }
+
+ /**
+ * element 转换成指定集合
+ *
+ * @param sourceCollection 源集合
+ * @param collectionFactory 目标集合
+ * @return DEST
+ */
+ public static , DEST extends Collection> DEST
+ transformElements(SOURCETYPE sourceCollection, Supplier collectionFactory) {
+ final DEST dest = collectionFactory.get();
+ sourceCollection.forEach(t -> {
+ dest.add(t);
+ });
+ return dest;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/util/DateUtils.java b/src/main/java/top/beansprout/health/util/DateUtils.java
new file mode 100644
index 0000000..e9afc26
--- /dev/null
+++ b/src/main/java/top/beansprout/health/util/DateUtils.java
@@ -0,0 +1,286 @@
+package top.beansprout.health.util;
+
+import java.time.DayOfWeek;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
+import java.time.temporal.TemporalUnit;
+import java.util.Date;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Title: DateUtils
+ * Description: 时间工具类
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/19 23:14
+ */
+@Slf4j
+public class DateUtils {
+
+ /** HH:mm **/
+ public static final String HHmm = "HH:mm";
+ /** HH:mm:ss **/
+ public static final String HHmmss = "HH:mm:ss";
+ /** HH:mm:ss a **/
+ public static final String HHmmssa = "HH:mm:ss a";
+
+ /** yyyyMM **/
+ public static final String yyyyMM = "yyyyMM";
+ /** yyyyMMdd **/
+ public static final String yyyyMMdd = "yyyyMMdd";
+ /** yyyyMMddHH **/
+ public static final String yyyyMMddHH = "yyyyMMddHH";
+ /** yyyyMMddHHmm **/
+ public static final String yyyyMMddHHmm = "yyyyMMddHHmm";
+ /** yyyyMMddHHmmss **/
+ public static final String yyyyMMddHHmmss = "yyyyMMddHHmmss";
+ /** yyyyMMddHHmmss.SSS **/
+ public static final String yyyyMMddHHmmssSSS = "yyyyMMddHHmmss.SSS";
+ /** yyyyMMddTHHmmss.SSSZ **/
+ public static final String yyyyMMddTHHmmssSSSZ = "yyyyMMdd'T'HHmmss.SSS'Z'";
+
+ /** MM-dd HH:mm **/
+ public static final String MMddHHmm_LINE = "MM-dd HH:mm";
+ /** yyyy-MM **/
+ public static final String yyyyMM_LINE = "yyyy-MM";
+ /** yyyy-MM-dd **/
+ public static final String yyyyMMdd_LINE = "yyyy-MM-dd";
+ /** yyyy-MM-dd HH **/
+ public static final String yyyyMMddHH_LINE = "yyyy-MM-dd HH";
+ /** yyyy-MM-dd HH:mm **/
+ public static final String yyyyMMddHHmm_LINE = "yyyy-MM-dd HH:mm";
+ /** yyyy-MM-dd HH:mm:ss **/
+ public static final String yyyyMMddHHmmss_LINE = "yyyy-MM-dd HH:mm:ss";
+ /** yyyy-MM-ddTHH:mm:ss **/
+ public static final String yyyyMMddTHHmmss_LINE = "yyyy-MM-dd'T'HH:mm:ss";
+ /** yyyy-MM-dd HH:mm:ss.SSS **/
+ public static final String yyyyMMddHHmmssSSS_LINE = "yyyy-MM-dd HH:mm:ss.SSS";
+ /** yyyy-MM-ddTHH:mm:ss.SSSZ **/
+ public static final String yyyyMMddTHHmmssSSSZ_LINE = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+
+ /** yyyy.MM **/
+ public static final String yyyyMM_POINT = "yyyy.MM";
+ /** yyyy.MM.dd **/
+ public static final String yyyyMMdd_POINT = "yyyy.MM.dd";
+ /** yyyy.MM.dd HH **/
+ public static final String yyyyMMddHH_POINT = "yyyy.MM.dd HH";
+ /** yyyy.MM.dd HH:mm **/
+ public static final String yyyyMMddHHmm_POINT = "yyyy.MM.dd HH:mm";
+ /** yyyy.MM.dd HH:mm:ss **/
+ public static final String yyyyMMddHHmmss_POINT = "yyyy.MM.dd HH:mm:ss";
+ /** yyyy.MM.dd HH:mm:ss.SSS **/
+ public static final String yyyyMMddHHmmssSSS_POINT = "yyyy.MM.dd HH:mm:ss.SSS";
+ /** yyyy.MM.ddTHH:mm:ss.SSSZ **/
+ public static final String yyyyMMddTHHmmssSSSZ_POINT = "yyyy.MM.dd'T'HH:mm:ss.SSS'Z'";
+
+ /** MM/dd/yyyy, HH:mm:ss a **/
+ public static final String MMddyyyyHHmmssa_SLASH = "MM/dd/yyyy, HH:mm:ss a";
+ /** MM月MM **/
+ public static final String yyyyMM_SLASH = "yyyy/MM";
+ /** yyyy/MM/dd **/
+ public static final String yyyyMMdd_SLASH = "yyyy/MM/dd";
+ /** yyyy/MM/dd HH **/
+ public static final String yyyyMMddHH_SLASH = "yyyy/MM/dd HH";
+ /** yyyy/MM/dd HH:mm **/
+ public static final String yyyyMMddHHmm_SLASH = "yyyy/MM/dd HH:mm";
+ /** yyyy/MM/dd HH:mm:ss **/
+ public static final String yyyyMMddHHmmss_SLASH = "yyyy/MM/dd HH:mm:ss";
+ /** yyyy/MM/dd HH:mm:ss.SSS **/
+ public static final String yyyyMMddHHmmssSSS_SLASH = "yyyy/MM/dd HH:mm:ss.SSS";
+ /** yyyy/MM/ddTHH:mm:ss.SSSZ **/
+ public static final String yyyyMMddTHHmmssSSSZ_SLASH = "yyyy/MM/dd'T'HH:mm:ss.SSS'Z'";
+
+ /** yyyy年MM月 **/
+ public static final String yyyyMM_UNIT = "yyyy年MM月";
+ /** yyyy年MM月dd日 **/
+ public static final String yyyyMMdd_UNIT = "yyyy年MM月dd日";
+ /** yyyy年MM月dd日 HH **/
+ public static final String yyyyMMddHH_UNIT = "yyyy年MM月dd日 HH";
+ /** yyyy年MM月dd日 HH:mm **/
+ public static final String yyyyMMddHHmm_UNIT = "yyyy年MM月dd日 HH:mm";
+ /** yyyy年MM月dd日 HH:mm:ss **/
+ public static final String yyyyMMddHHmmss_UNIT = "yyyy年MM月dd日 HH:mm:ss";
+ /** yyyy年MM月dd日 HH:mm:ss.SSS **/
+ public static final String yyyyMMddHHmmssSSS_UNIT = "yyyy年MM月dd日 HH:mm:ss.SSS";
+ /** yyyy年MM月dd日THH:mm:ss.SSSZ **/
+ public static final String yyyyMMddTHHmmssSSSZ_UNIT = "yyyy年MM月dd日'T'HH:mm:ss.SSS'Z'";
+
+ public static final long SECONDS_TO_MINUTE = 60;
+ public static final long MINUTES_TO_HOUR = 60;
+ public static final long HOUR_TO_DAY = 24;
+ public static final long DAYS_TO_WEEK = 7;
+ public static final long DAYS_TO_MONTH = 31;
+ public static final long DAYS_TO_YEAR = 365;
+ public static final long MONTHES_TO_YEAR = 12;
+
+ public static final long SECONDS_TO_HOUR = SECONDS_TO_MINUTE * MINUTES_TO_HOUR;
+ public static final long SECONDS_TO_DAY = SECONDS_TO_MINUTE * MINUTES_TO_HOUR * HOUR_TO_DAY;
+ public static final long SECONDS_TO_WEEK = SECONDS_TO_MINUTE * MINUTES_TO_HOUR * HOUR_TO_DAY * DAYS_TO_WEEK;
+ public static final long SECONDS_TO_MONTH = SECONDS_TO_MINUTE * MINUTES_TO_HOUR * HOUR_TO_DAY * DAYS_TO_MONTH;// 暂定义位每月31天
+ public static final long SECONDS_TO_YEAR = SECONDS_TO_MINUTE * MINUTES_TO_HOUR * HOUR_TO_DAY * DAYS_TO_YEAR;
+
+ private static final String[] weekDaysName = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
+ private static final String[] weekDaysName_en = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
+ "Saturday" };
+ private static final String[] monthName = { "一月份", "二月份", "三月份", "四月份", "五月份", "六月份", "七月份", "八月份", "九月份", "十月份",
+ "十一月份", "十二月份" };
+
+ /**
+ * 格式化时间为LocalDateTime
+ * @param str
+ * @param pattern
+ * @return
+ */
+ public static LocalDateTime parse(String str, String pattern) {
+ final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
+ return LocalDateTime.parse(str, formatter);
+ }
+
+ /**
+ * 格式化时间为Date
+ * @param str
+ * @param pattern
+ * @return
+ */
+ public static Date parseDate(String str, String pattern) {
+ return convertLDTToDate(parse(str, pattern));
+ }
+
+ /**
+ * Date转换为LocalDateTime
+ * @param date
+ * @return
+ */
+ public static LocalDateTime convertDateToLDT(Date date) {
+ return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
+ }
+
+ /**
+ * LocalDateTime转换为Date
+ *
+ * @param time
+ * @return
+ */
+ public static Date convertLDTToDate(LocalDateTime time) {
+ return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
+ }
+
+ /**
+ * 获取指定日期的毫秒
+ * @param time
+ * @return
+ */
+ public static Long getMilliByTime(LocalDateTime time) {
+ return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+ }
+
+ /**
+ * 获取指定日期的秒
+ * @param time
+ * @return
+ */
+ public static Long getSecondsByTime(LocalDateTime time) {
+ return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
+ }
+
+ /**
+ * 获取指定时间的指定格式
+ * @param time
+ * @param pattern
+ * @return
+ */
+ public static String formatTime(LocalDateTime time, String pattern) {
+ return time.format(DateTimeFormatter.ofPattern(pattern));
+ }
+
+ /**
+ * 获取当前时间的指定格式
+ * @param pattern
+ * @return
+ */
+ public static String formatNow(String pattern) {
+ return formatTime(LocalDateTime.now(), pattern);
+ }
+
+ /**
+ * 日期加上一个数,根据field不同加不同值,field为ChronoUnit.*
+ * @param time
+ * @param number
+ * @param field
+ * @return
+ */
+ public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
+ return time.plus(number, field);
+ }
+
+ /**
+ * 日期减去一个数,根据field不同减不同值,field参数为ChronoUnit.*
+ * @param time
+ * @param number
+ * @param field
+ * @return
+ */
+ public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field) {
+ return time.minus(number, field);
+ }
+
+ /**
+ * 获取两个日期的差 field参数为ChronoUnit.*
+ * @param startTime
+ * @param endTime
+ * @param field 单位(年月日时分秒)
+ * @return
+ */
+ public static long betweenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field) {
+ final Period period = Period.between(LocalDate.from(startTime), LocalDate.from(endTime));
+ if (field == ChronoUnit.YEARS)
+ return period.getYears();
+ if (field == ChronoUnit.MONTHS)
+ return period.getYears() * 12 + period.getMonths();
+ return field.between(startTime, endTime);
+ }
+
+ /**
+ * 获取一天的开始时间,2017,7,22 00:00
+ * @param time
+ * @return
+ */
+ public static LocalDateTime getDayStart(LocalDateTime time) {
+ return time.withHour(0).withMinute(0).withSecond(0).withNano(0);
+ }
+
+ /**
+ * 获取一天的结束时间,2017,7,22 23:59:59.999999999
+ * @param time
+ * @return
+ */
+ public static LocalDateTime getDayEnd(LocalDateTime time) {
+ return time.withHour(23).withMinute(59).withSecond(59).withNano(999999999);
+ }
+
+ /**
+ * 获得指定时间的周一日期
+ * @param time
+ * @return
+ */
+ public static LocalDateTime getWeekBegin(LocalDateTime time) {
+ return time.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
+ }
+
+ /**
+ * 获得指定时间的周日日期
+ * @param time
+ * @return
+ */
+ public static LocalDateTime getWeekEnd(LocalDateTime time) {
+ return time.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/util/JsonUtils.java b/src/main/java/top/beansprout/health/util/JsonUtils.java
new file mode 100644
index 0000000..53d1b49
--- /dev/null
+++ b/src/main/java/top/beansprout/health/util/JsonUtils.java
@@ -0,0 +1,57 @@
+package top.beansprout.health.util;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Title: JsonUtils
+ * Description: Json工具类,依赖 jackson
+ * @author beansprout
+ * @date 2020/3/18 23:39
+ * @version 1.0
+ */
+@Slf4j
+public class JsonUtils {
+
+ private final static ObjectMapper objMapper = new ObjectMapper();
+
+ /**
+ * Json字符串转化成对象
+ * @param jsonString
+ * @param clazz
+ * @param
+ * @return
+ */
+ public static T toObj(String jsonString, Class clazz) {
+ objMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
+ try {
+ return objMapper.readValue(jsonString, clazz);
+ } catch (final IOException e) {
+ log.error("Json string conversion object failed {}", e);
+ }
+ return null;
+ }
+
+ /**
+ * javaBean 转化成json字符串
+ * @param obj
+ * @return
+ */
+ public static String toJson(Object obj) {
+ if((obj instanceof Integer) || (obj instanceof Long) || (obj instanceof Float) ||
+ (obj instanceof Double) || (obj instanceof Boolean) || (obj instanceof String))
+ return String.valueOf(obj);
+ try {
+ return objMapper.writeValueAsString(obj);
+ } catch (final JsonProcessingException e) {
+ log.error("Json object conversion string failed {}", e);
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/top/beansprout/health/util/MailUtils.java b/src/main/java/top/beansprout/health/util/MailUtils.java
new file mode 100644
index 0000000..782e453
--- /dev/null
+++ b/src/main/java/top/beansprout/health/util/MailUtils.java
@@ -0,0 +1,74 @@
+package top.beansprout.health.util;
+
+import top.beansprout.health.model.vo.AuthVo;
+
+import javax.mail.Message;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import java.util.Properties;
+
+public class MailUtils {
+ private Properties props; //系统属性
+ private Session mailSession; //邮件会话对象
+ private MimeMessage mimeMsg; //MIME邮件对象
+
+ //获取6位随机验证码
+ public String createVertifyCode(){
+ String[] letters = new String[] {
+ "q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m",
+ "A","W","E","R","T","Y","U","I","O","P","A","S","D","F","G","H","J","K","L","Z","X","C","V","B","N","M",
+ "0","1","2","3","4","5","6","7","8","9"};
+ String stringBuilder ="";
+ for (int i = 0; i < 6; i++) {
+ stringBuilder = stringBuilder + letters[(int)Math.floor(Math.random()*letters.length)];
+ }
+ return stringBuilder;
+ }
+
+ public MailUtils() {
+ AuthVo au = new AuthVo("ateenliu@hunnu.edu.cn","jiAQr2Qp84xWn7GD");
+ //设置系统属性
+ props= System.getProperties(); //获得系统属性对象
+ props.put("mail.smtp.host", "smtp.exmail.qq.com"); //设置SMTP主机
+ props.put("mail.smtp.port", "465"); //设置服务端口号
+ props.put("mail.smtp.auth", "true"); //同时通过验证
+ props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+ //获得邮件会话对象
+ mailSession = Session.getInstance(props, au);
+ }
+
+ public boolean sendingMimeMail(String MailTo, String MailSubject,
+ String MailBody) {
+ try {
+ //创建MIME邮件对象
+ mimeMsg=new MimeMessage(mailSession);
+ //设置发信人
+ mimeMsg.setFrom(new InternetAddress("ateenliu@hunnu.edu.cn"));
+ //设置收信人
+ if(MailTo!=null){
+ mimeMsg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(MailTo));
+ }
+// //设置抄送人
+// if(MailCopyTo!=null){
+// mimeMsg.setRecipients(Message.RecipientType.CC,InternetAddress.parse(MailCopyTo));
+// }
+// //设置暗送人
+// if(MailBCopyTo!=null){
+// mimeMsg.setRecipients(Message.RecipientType.BCC,InternetAddress.parse(MailBCopyTo));
+// }
+ //设置邮件主题
+ mimeMsg.setSubject(MailSubject,"utf-8");
+ //设置邮件内容,将邮件body部分转化为HTML格式
+ mimeMsg.setContent(MailBody,"text/html;charset=utf-8");
+ //发送邮件
+ Transport.send(mimeMsg);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+}
diff --git a/src/main/java/top/beansprout/health/util/PublicUtils.java b/src/main/java/top/beansprout/health/util/PublicUtils.java
new file mode 100644
index 0000000..5211a90
--- /dev/null
+++ b/src/main/java/top/beansprout/health/util/PublicUtils.java
@@ -0,0 +1,150 @@
+package top.beansprout.health.util;
+
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.springframework.beans.BeanUtils;
+
+/**
+ * Title: PublicUtils
+ * Description: 公共方法
+ *
+ * @author beansprout
+ * @version 1.0
+ * @date 2020/3/22 17:41
+ */
+public class PublicUtils {
+
+ /**
+ * 为空
+ * @param obj
+ * @return
+ */
+ public static boolean isBlank(Object obj) {
+ if (obj == null)
+ return true;
+ if (obj instanceof String)
+ return ((String) obj).trim().equals("");
+ if (obj instanceof CharSequence)
+ return ((CharSequence) obj).length() == 0;
+ if (obj instanceof Object[])
+ return ((Object[]) obj).length == 0;
+ if (obj instanceof Collection)
+ return ((Collection>) obj).isEmpty();
+ if (obj instanceof Map)
+ return ((Map, ?>) obj).isEmpty();
+ return false;
+ }
+
+ /**
+ * 不为空
+ * @param obj
+ * @return
+ */
+ public static boolean isNotBlank(Object obj) {
+ return !isBlank(obj);
+ }
+
+ /**
+ * 随机指定长度的数字+字母字符串
+ * @param leng
+ * @return
+ */
+ public static String randomString(int leng) {
+ return RandomStringUtils.randomAlphanumeric(leng);
+ }
+
+ /**
+ * 拼接字符串
+ * @param values
+ * @return
+ */
+ public static String join(Object ...values) {
+ if (values.length <= 0) return "";
+ final StringBuilder stringBuilder = new StringBuilder();
+ for (final Object value : values) {
+ stringBuilder.append(value);
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Base64加密
+ * @param value 原文
+ * @return
+ */
+ public static String encryptBase64(String value) {
+ if (isBlank(value)) return null;
+ final byte[] encode = Base64.getEncoder().encode(value.getBytes());
+ return new String(encode);
+ }
+
+ /**
+ * Base64解密
+ * @param encrypt 密文
+ * @return
+ */
+ public static String decryptBase64(String encrypt) {
+ if (isBlank(encrypt)) return null;
+ final byte[] decode = Base64.getDecoder().decode(encrypt);
+ return new String(decode);
+ }
+
+ /**
+ * 获取请求域参数
+ * @param request
+ * @param key
+ * @return
+ */
+ public static String getAttribute(HttpServletRequest request, String key) {
+ return getAttribute(request, key, String.class);
+ }
+
+ public static T getAttribute(HttpServletRequest request, String key, Class requiredType) {
+ if ((request == null) || isBlank(key)) return null;
+ final Object value = request.getAttribute(key);
+ return isBlank(value) ? null : castValue(value, requiredType);
+ }
+
+ /** Object类型转换 **/
+ private static T castValue(Object value, Class requiredType) {
+ if ((requiredType == Date.class) && (value instanceof Long)) {
+ value = new Date((Long)value);
+ }
+
+ if (value instanceof Integer) {
+ final int intValue = (Integer)value;
+ if (requiredType == Long.class) {
+ value = (long)intValue;
+ } else if ((requiredType == Short.class) && (-32768 <= intValue) && (intValue <= 32767)) {
+ value = (short)intValue;
+ } else if ((requiredType == Byte.class) && (-128 <= intValue) && (intValue <= 127)) {
+ value = (byte)intValue;
+ }
+ }
+
+ if (!requiredType.isInstance(value))
+ throw new RuntimeException(
+ "Expected value to be of type: " + requiredType + ", but was " + value.getClass());
+ else
+ return requiredType.cast(value);
+ }
+
+ /**
+ * Bean字段值复制
+ * @param source 源
+ * @param target 目标
+ * @return 目标
+ */
+ public static T copyBean(T source, T target) {
+ if (isBlank(source) && isBlank(target)) return null;
+ BeanUtils.copyProperties(source, target);
+ return target;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..77538f0
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,4 @@
+jdbc.driver=com.mysql.cj.jdbc.Driver
+jdbc.url=jdbc:mysql://localhost:3306/health?useUnicode=true&characterEncoding=UTF-8
+jdbc.username=health
+jdbc.password=health
\ No newline at end of file
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000..12b5cc8
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,32 @@
+### set log levels ###
+log4j.rootLogger=DEBUG , console , debug , error
+### console ###
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.Target=System.out
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n
+### log file ###
+#log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
+#log4j.appender.debug.File=log.log
+#log4j.appender.debug.Append=true
+#log4j.appender.debug.Threshold=INFO
+#log4j.appender.debug.layout=org.apache.log4j.PatternLayout
+#log4j.appender.debug.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n
+### exception ###
+log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
+log4j.appender.error.File=error-log.log
+log4j.appender.error.Append=true
+log4j.appender.error.Threshold=ERROR
+log4j.appender.error.layout=org.apache.log4j.PatternLayout
+log4j.appender.error.layout.ConversionPattern=%-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n
+###需要声明,然后下方才可以使druid sql输出,否则会抛出log4j.error.key not found
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %l %c%n%p: %m%n
+### druid sql ###
+log4j.logger.druid.sql=warn,stdout
+log4j.logger.druid.sql.DataSource=warn,stdout
+log4j.logger.druid.sql.Connection=warn,stdout
+log4j.logger.druid.sql.Statement=warn,stdout
+log4j.logger.druid.sql.ResultSet=warn,stdout
\ No newline at end of file
diff --git a/src/main/resources/mapper/TBodyInfoMapper.xml b/src/main/resources/mapper/TBodyInfoMapper.xml
new file mode 100644
index 0000000..9b8625f
--- /dev/null
+++ b/src/main/resources/mapper/TBodyInfoMapper.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ SELECT * FROM t_body_info
+
+ creator = #{ creator }
+
+ AND create_time>=#{minDate}
+
+
+
+
+
+ ORDER BY create_time DESC
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/mapper/THealthConfigMapper.xml b/src/main/resources/mapper/THealthConfigMapper.xml
new file mode 100644
index 0000000..05aafe4
--- /dev/null
+++ b/src/main/resources/mapper/THealthConfigMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/mapper/TUserMapper.xml b/src/main/resources/mapper/TUserMapper.xml
new file mode 100644
index 0000000..65f8c68
--- /dev/null
+++ b/src/main/resources/mapper/TUserMapper.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/spring/mybatis-config.xml b/src/main/resources/spring/mybatis-config.xml
new file mode 100644
index 0000000..a200d4f
--- /dev/null
+++ b/src/main/resources/spring/mybatis-config.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/spring/spring-dao.xml b/src/main/resources/spring/spring-dao.xml
new file mode 100644
index 0000000..507b330
--- /dev/null
+++ b/src/main/resources/spring/spring-dao.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ helperDialect=mysql
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/spring/spring-mvc.xml b/src/main/resources/spring/spring-mvc.xml
new file mode 100644
index 0000000..e30d3cc
--- /dev/null
+++ b/src/main/resources/spring/spring-mvc.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/spring/spring-service.xml b/src/main/resources/spring/spring-service.xml
new file mode 100644
index 0000000..948320a
--- /dev/null
+++ b/src/main/resources/spring/spring-service.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/spring/spring.xml b/src/main/resources/spring/spring.xml
new file mode 100644
index 0000000..28afb2b
--- /dev/null
+++ b/src/main/resources/spring/spring.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/error/401.jsp b/src/main/webapp/WEB-INF/error/401.jsp
new file mode 100644
index 0000000..047766b
--- /dev/null
+++ b/src/main/webapp/WEB-INF/error/401.jsp
@@ -0,0 +1,71 @@
+<%--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+--%>
+<%@ page session="false" trimDirectiveWhitespaces="true" %>
+
+
+
+ 401 Unauthorized
+
+
+
+ 401 Unauthorized
+
+ You are not authorized to view this page. If you have not changed
+ any configuration files, please examine the file
+ conf/tomcat-users.xml in your installation. That
+ file must contain the credentials to let you use this webapp.
+
+
+ For example, to add the admin-gui role to a user named
+ tomcat with a password of s3cret , add the following to the
+ config file listed above.
+
+
+<role rolename="admin-gui"/>
+<user username="tomcat" password="s3cret" roles="admin-gui"/>
+
+
+ Note that for Tomcat 7 onwards, the roles required to use the host manager
+ application were changed from the single admin role to the
+ following two roles. You will need to assign the role(s) required for
+ the functionality you wish to access.
+
+
+ admin-gui - allows access to the HTML GUI
+ admin-script - allows access to the text interface
+
+
+ The HTML interface is protected against CSRF but the text interface is not.
+ To maintain the CSRF protection:
+
+
+ Users with the admin-gui role should not be granted the
+ admin-script role.
+ If the text interface is accessed through a browser (e.g. for testing
+ since this interface is intended for tools not humans) then the browser
+ must be closed afterwards to terminate the session.
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/error/403.jsp b/src/main/webapp/WEB-INF/error/403.jsp
new file mode 100644
index 0000000..74e1e2d
--- /dev/null
+++ b/src/main/webapp/WEB-INF/error/403.jsp
@@ -0,0 +1,90 @@
+<%--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+--%>
+<%@ page session="false" trimDirectiveWhitespaces="true" %>
+
+
+
+ 403 Access Denied
+
+
+
+ 403 Access Denied
+
+ You are not authorized to view this page.
+
+
+ By default the Host Manager is only accessible from a browser running on the
+ same machine as Tomcat. If you wish to modify this restriction, you'll need
+ to edit the Host Manager's context.xml file.
+
+
+ If you have already configured the Host Manager application to allow access
+ and you have used your browsers back button, used a saved book-mark or
+ similar then you may have triggered the cross-site request forgery (CSRF)
+ protection that has been enabled for the HTML interface of the Host Manager
+ application. You will need to reset this protection by returning to the
+ main Host Manager page .
+ Once you return to this page, you will be able to continue using the Host
+ Manager application's HTML interface normally. If you continue to see this
+ access denied message, check that you have the necessary permissions to
+ access this application.
+
+ If you have not changed
+ any configuration files, please examine the file
+ conf/tomcat-users.xml in your installation. That
+ file must contain the credentials to let you use this webapp.
+
+
+ For example, to add the admin-gui role to a user named
+ tomcat with a password of s3cret , add the following to the
+ config file listed above.
+
+
+<role rolename="admin-gui"/>
+<user username="tomcat" password="s3cret" roles="admin-gui"/>
+
+
+ Note that for Tomcat 7 onwards, the roles required to use the host manager
+ application were changed from the single admin role to the
+ following two roles. You will need to assign the role(s) required for
+ the functionality you wish to access.
+
+
+ admin-gui - allows access to the HTML GUI
+ admin-script - allows access to the text interface
+
+
+ The HTML interface is protected against CSRF but the text interface is not.
+ To maintain the CSRF protection:
+
+
+ Users with the admin-gui role should not be granted the
+ admin-script role.
+ If the text interface is accessed through a browser (e.g. for testing
+ since this interface is intended for tools not humans) then the browser
+ must be closed afterwards to terminate the session.
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/error/404.jsp b/src/main/webapp/WEB-INF/error/404.jsp
new file mode 100644
index 0000000..2bf507a
--- /dev/null
+++ b/src/main/webapp/WEB-INF/error/404.jsp
@@ -0,0 +1,60 @@
+
+
+
+404 Not found
+
+
+
+ 404 Not found
+
+ The page you tried to access (<%=request.getAttribute("javax.servlet.error.request_uri")%>)
+ ${ baseurl }
+ does not exist.
+
+ The Host Manager application has been re-structured for Tomcat 7
+ onwards and some URLs have changed. All URLs used to access the
+ Manager application should now start with one of the following
+ options:
+
+ <%=request.getContextPath()%>/html for the HTML GUI
+ <%=request.getContextPath()%>/text for the text interface
+
+
+ Note that the URL for the text interface has changed from "<%=request.getContextPath()%>"
+ to "<%=request.getContextPath()%>/text".
+
+ You probably need to adjust the URL you are using to access the
+ Host Manager application. However, there is always a chance you have
+ found a bug in the Host Manager application. If you are sure you have
+ found a bug, and that the bug has not already been reported, please
+ report it to the Apache Tomcat team.
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/error/500.jsp b/src/main/webapp/WEB-INF/error/500.jsp
new file mode 100644
index 0000000..522b3d0
--- /dev/null
+++ b/src/main/webapp/WEB-INF/error/500.jsp
@@ -0,0 +1,12 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+
+
+
+
+错误页面
+
+
+<% System.out.print(request.getAttribute("message")); %>
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/base.jsp b/src/main/webapp/WEB-INF/view/base.jsp
new file mode 100644
index 0000000..5a5a108
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/base.jsp
@@ -0,0 +1,8 @@
+
+<%-- <%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%> --%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
+<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
+
+
diff --git a/src/main/webapp/WEB-INF/view/bodyInfoInput.jsp b/src/main/webapp/WEB-INF/view/bodyInfoInput.jsp
new file mode 100644
index 0000000..7405480
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/bodyInfoInput.jsp
@@ -0,0 +1,215 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="base.jsp"%>
+
+
+
+
+ 身体信息录入
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/bodyInofList.jsp b/src/main/webapp/WEB-INF/view/bodyInofList.jsp
new file mode 100644
index 0000000..d3090f8
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/bodyInofList.jsp
@@ -0,0 +1,234 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="base.jsp" %>
+
+
+
+ 用户身体信息列表
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/bodyInofStatistics.jsp b/src/main/webapp/WEB-INF/view/bodyInofStatistics.jsp
new file mode 100644
index 0000000..8e21eda
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/bodyInofStatistics.jsp
@@ -0,0 +1,175 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="base.jsp" %>
+
+
+
+ 用户身体信息统计
+
+
+
+
+
+
+ 时间
+
+ 搜索
+ 刷新
+ 近期推送
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/home.jsp b/src/main/webapp/WEB-INF/view/home.jsp
new file mode 100644
index 0000000..f8e7963
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/home.jsp
@@ -0,0 +1,181 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="base.jsp"%>
+
+
+
+
+ 首页
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/view/register.jsp b/src/main/webapp/WEB-INF/view/register.jsp
new file mode 100644
index 0000000..9c4c065
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/register.jsp
@@ -0,0 +1,150 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="base.jsp" %>
+
+
+
+ 注册
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/top.jsp b/src/main/webapp/WEB-INF/view/top.jsp
new file mode 100644
index 0000000..7b81120
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/top.jsp
@@ -0,0 +1,55 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+
+
+
+
+ 头部
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/updatePassword.jsp b/src/main/webapp/WEB-INF/view/updatePassword.jsp
new file mode 100644
index 0000000..72fcadc
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/updatePassword.jsp
@@ -0,0 +1,120 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="base.jsp"%>
+
+
+
+
+ 修改密码
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/view/userInfo.jsp b/src/main/webapp/WEB-INF/view/userInfo.jsp
new file mode 100644
index 0000000..aaf3bd7
--- /dev/null
+++ b/src/main/webapp/WEB-INF/view/userInfo.jsp
@@ -0,0 +1,200 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="base.jsp"%>
+
+
+
+
+ 用户信息
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..eee39cd
--- /dev/null
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,89 @@
+
+
+
+ 个人健康信息管理
+
+
+
+ contextConfigLocation
+ classpath:spring/spring.xml
+
+
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+ crossXssFilter
+ top.beansprout.health.config.CrossXssFilter
+
+
+ crossXssFilter
+ /*
+
+
+
+
+ characterEncodingFilter
+ org.springframework.web.filter.CharacterEncodingFilter
+
+
+ encoding
+ UTF-8
+
+
+
+ forceEncoding
+ true
+
+
+
+ characterEncodingFilter
+ /*
+
+
+
+
+ dispatcher
+ org.springframework.web.servlet.DispatcherServlet
+
+
+ contextConfigLocation
+ classpath:spring/spring-mvc.xml
+
+ 1
+
+
+ dispatcher
+ /
+
+
+
+ index.jsp
+
+
+ java.lang.Exception
+ /WEB-INF/error/500.jsp
+
+
+ 401
+ /WEB-INF/error/401.jsp
+
+
+ 403
+ /WEB-INF/error/403.jsp
+
+
+ 404
+ /WEB-INF/error/404.jsp
+
+
+ 405
+ /WEB-INF/error/404.jsp
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp
new file mode 100644
index 0000000..bfc8fec
--- /dev/null
+++ b/src/main/webapp/index.jsp
@@ -0,0 +1,128 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8"
+ pageEncoding="UTF-8"%>
+<%@include file="WEB-INF/view/base.jsp"%>
+
+
+
+ 登录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/static/css/base.css b/src/main/webapp/static/css/base.css
new file mode 100644
index 0000000..934a655
--- /dev/null
+++ b/src/main/webapp/static/css/base.css
@@ -0,0 +1,3381 @@
+/*
+==========================================================
+==========================================================
+
+Bootstrap 4 Admin Template
+
+https://bootstrapious.com/p/admin-template
+
+==========================================================
+==========================================================
+*/
+/*
+* ==========================================================
+* GENERAL STYLES
+* ==========================================================
+*/
+body {
+ overflow-x: hidden;
+}
+
+a,
+i,
+span {
+ display: inline-block;
+ text-decoration: none;
+ -webkit-transition: all 0.3s;
+ transition: all 0.3s;
+}
+
+a:hover, a:focus,
+i:hover,
+i:focus,
+span:hover,
+span:focus {
+ text-decoration: none;
+}
+
+section {
+ padding: 50px 0;
+}
+
+canvas {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.container-fluid {
+ padding: 0 30px;
+}
+
+@media (max-width: 575px) {
+ .container-fluid {
+ padding: 0 15px;
+ }
+}
+
+header.page-header {
+ padding: 20px 0;
+}
+
+table {
+ font-size: 0.9em;
+ color: #666;
+}
+
+.card-close {
+ position: absolute;
+ top: 15px;
+ right: 15px;
+}
+
+.card-close .dropdown-toggle {
+ color: #999;
+ background: none;
+ border: none;
+}
+
+.card-close .dropdown-toggle:after {
+ display: none;
+}
+
+.card-close .dropdown-menu {
+ border: none;
+ min-width: auto;
+ font-size: 0.9em;
+ border-radius: 0;
+ -webkit-box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.1), -2px -2px 3px rgba(0, 0, 0, 0.1);
+ box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.1), -2px -2px 3px rgba(0, 0, 0, 0.1);
+}
+
+.card-close .dropdown-menu a {
+ color: #999 !important;
+}
+
+.card-close .dropdown-menu a:hover {
+ background: #796AEE;
+ color: #fff !important;
+}
+
+.card-close .dropdown-menu a i {
+ margin-right: 10px;
+ -webkit-transition: none;
+ transition: none;
+}
+
+.content-inner {
+ position: relative;
+ width: calc(100% - 250px);
+ min-height: calc(100vh - 70px);
+ padding-bottom: 60px;
+}
+
+.content-inner.active {
+ width: calc(100% - 90px);
+}
+
+.page-header {
+ background: #fff;
+ padding: 20px;
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1);
+ position: relative;
+ z-index: 8;
+}
+
+*[class*="icon-"] {
+ -webkit-transform: translateY(3px);
+ transform: translateY(3px);
+}
+
+button,
+input {
+ outline: none !important;
+}
+
+.card {
+ margin-bottom: 30px;
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1), -1px 0 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1), -1px 0 2px rgba(0, 0, 0, 0.05);
+}
+
+.card-header {
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.05);
+}
+
+.card-header h1,
+.card-header h2,
+.card-header h3,
+.card-header h4,
+.card-header h5,
+.card-header h6 {
+ margin-bottom: 0;
+}
+
+.breadcrumb-holder {
+ background: #fff;
+}
+
+.breadcrumb {
+ background: #fff;
+ position: relative;
+ z-index: 7;
+ border-radius: 0;
+ padding: 15px 0;
+ margin-bottom: 0;
+}
+
+.breadcrumb li.breadcrumb-item {
+ color: #aaa;
+ font-weight: 300;
+}
+
+/*=== Helpers ===*/
+.text-bold {
+ font-weight: 700;
+}
+
+.text-small {
+ font-size: 0.9rem;
+}
+
+.text-xsmall {
+ font-size: 0.8rem;
+}
+
+.bg-red {
+ background: #ff7676 !important;
+ color: #fff;
+}
+
+.bg-red:hover {
+ color: #fff;
+}
+
+.bg-blue {
+ background: #85b4f2 !important;
+ color: #fff;
+}
+
+.bg-blue:hover {
+ color: #fff;
+}
+
+.bg-yellow {
+ background: #eef157 !important;
+ color: #fff;
+}
+
+.bg-yellow:hover {
+ color: #fff;
+}
+
+.bg-green {
+ background: #54e69d !important;
+ color: #fff;
+}
+
+.bg-green:hover {
+ color: #fff;
+}
+
+.bg-orange {
+ background: #ffc36d !important;
+ color: #fff;
+}
+
+.bg-orange:hover {
+ color: #fff;
+}
+
+.bg-violet {
+ background: #796AEE !important;
+ color: #fff;
+}
+
+.bg-violet:hover {
+ color: #fff;
+}
+
+.bg-gray {
+ background: #ced4da !important;
+}
+
+.bg-white {
+ background: #fff !important;
+}
+
+.text-red {
+ color: #ff7676;
+}
+
+.text-red:hover {
+ color: #ff7676;
+}
+
+.text-yellow {
+ color: #eef157;
+}
+
+.text-yellow:hover {
+ color: #eef157;
+}
+
+.text-green {
+ color: #54e69d;
+}
+
+.text-green:hover {
+ color: #54e69d;
+}
+
+.text-orange {
+ color: #ffc36d;
+}
+
+.text-orange:hover {
+ color: #ffc36d;
+}
+
+.text-violet {
+ color: #796AEE;
+}
+
+.text-violet:hover {
+ color: #796AEE;
+}
+
+.text-blue {
+ color: #85b4f2;
+}
+
+.text-blue:hover {
+ color: #85b4f2;
+}
+
+.text-gray {
+ color: #ced4da;
+}
+
+.text-gray:hover {
+ color: #ced4da;
+}
+
+.text-uppercase {
+ letter-spacing: 0.2em;
+}
+
+.lh-2 {
+ line-height: 2;
+}
+
+.page {
+ background: #EEF5F9;
+}
+
+.page .text-white {
+ color: #fff;
+}
+
+.no-padding {
+ padding: 0 !important;
+}
+
+.no-padding-bottom {
+ padding-bottom: 0 !important;
+}
+
+.no-padding-top {
+ padding-top: 0 !important;
+}
+
+.no-margin {
+ margin: 0 !important;
+}
+
+.no-margin-bottom {
+ margin-bottom: 0 !important;
+}
+
+.no-margin-top {
+ margin-top: 0 !important;
+}
+
+.page {
+ overflow-x: hidden;
+}
+
+.has-shadow {
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1), -1px 0 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1), -1px 0 2px rgba(0, 0, 0, 0.05);
+}
+
+.badge {
+ font-weight: 400;
+}
+
+.badge-rounded {
+ border-radius: 50px;
+}
+
+.list-group-item {
+ border-right: 0;
+ border-left: 0;
+}
+
+.list-group-item:first-child, .list-group-item:last-child {
+ border-radius: 0;
+}
+
+.overflow-hidden {
+ overflow: hidden;
+}
+
+.tile-link {
+ position: absolute;
+ cursor: pointer;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ z-index: 30;
+}
+
+/*
+* ==========================================================
+* SIDEBAR
+* ==========================================================
+*/
+nav.side-navbar {
+ background: #fff;
+ min-width: 250px;
+ max-width: 250px;
+ color: #686a76;
+ -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
+ box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
+ z-index: 9;
+ /*==== Sidebar Header ====*/
+ /*==== Sidebar Menu ====*/
+ /*==== Shrinked Sidebar ====*/
+}
+
+nav.side-navbar a {
+ color: inherit;
+ position: relative;
+ font-size: 0.9em;
+}
+
+nav.side-navbar a[data-toggle="collapse"]::before {
+ content: '\f104';
+ display: inline-block;
+ -webkit-transform: translateY(-50%);
+ transform: translateY(-50%);
+ font-family: 'FontAwesome';
+ position: absolute;
+ top: 50%;
+ right: 20px;
+}
+
+nav.side-navbar a[aria-expanded="true"] {
+ background: #EEF5F9;
+}
+
+nav.side-navbar a[aria-expanded="true"]::before {
+ content: '\f107';
+}
+
+nav.side-navbar a i {
+ font-size: 1.2em;
+ margin-right: 10px;
+ -webkit-transition: none;
+ transition: none;
+}
+
+nav.side-navbar .sidebar-header {
+ padding: 30px 15px;
+}
+
+nav.side-navbar .avatar {
+ width: 55px;
+ height: 55px;
+}
+
+nav.side-navbar .title {
+ margin-left: 10px;
+}
+
+nav.side-navbar .title h1 {
+ color: #333;
+}
+
+nav.side-navbar .title p {
+ font-size: 0.9em;
+ font-weight: 200;
+ margin-bottom: 0;
+ color: #aaa;
+}
+
+nav.side-navbar span.heading {
+ text-transform: uppercase;
+ font-weight: 400;
+ margin-left: 20px;
+ color: #ccc;
+ font-size: 0.8em;
+}
+
+nav.side-navbar ul {
+ padding: 15px 0;
+}
+
+nav.side-navbar ul li {
+ /* submenu item active */
+}
+
+nav.side-navbar ul li a {
+ padding: 10px 15px;
+ text-decoration: none;
+ display: block;
+ font-weight: 300;
+ border-left: 4px solid transparent;
+}
+
+nav.side-navbar ul li a:hover {
+ background: #796AEE;
+ border-left: 4px solid #3b25e6;
+ color: #fff;
+}
+
+nav.side-navbar ul li li a {
+ padding-left: 50px;
+ background: #EEF5F9;
+}
+
+nav.side-navbar ul li.active > a {
+ background: #796AEE;
+ color: #fff;
+ border-left: 4px solid #3b25e6;
+}
+
+nav.side-navbar ul li.active > a:hover {
+ background: #796AEE;
+}
+
+nav.side-navbar ul li li.active > a {
+ background: #8e81f1;
+}
+
+nav.side-navbar ul li ul {
+ padding: 0;
+}
+
+nav.side-navbar.shrinked {
+ min-width: 90px;
+ max-width: 90px;
+ text-align: center;
+}
+
+nav.side-navbar.shrinked span.heading {
+ margin: 0;
+}
+
+nav.side-navbar.shrinked ul li a {
+ padding: 15px 2px;
+ border: none;
+ font-size: 0.8em;
+ color: #aaa;
+ -webkit-transition: color 0.3s, background 0.3s;
+ transition: color 0.3s, background 0.3s;
+}
+
+nav.side-navbar.shrinked ul li a[data-toggle="collapse"]::before {
+ content: '\f107';
+ -webkit-transform: translateX(50%);
+ transform: translateX(50%);
+ position: absolute;
+ top: auto;
+ right: 50%;
+ bottom: 0;
+ left: auto;
+}
+
+nav.side-navbar.shrinked ul li a[data-toggle="collapse"][aria-expanded="true"]::before {
+ content: '\f106';
+}
+
+nav.side-navbar.shrinked ul li a:hover {
+ color: #fff;
+ border: none;
+}
+
+nav.side-navbar.shrinked ul li a:hover i {
+ color: #fff;
+}
+
+nav.side-navbar.shrinked ul li a i {
+ margin-right: 0;
+ margin-bottom: 2px;
+ display: block;
+ font-size: 1rem;
+ color: #333;
+ -webkit-transition: color 0.3s;
+ transition: color 0.3s;
+}
+
+nav.side-navbar.shrinked ul li.active > a {
+ color: #fff;
+}
+
+nav.side-navbar.shrinked ul li.active > a i {
+ color: #fff;
+}
+
+nav.side-navbar.shrinked .sidebar-header .title {
+ display: none;
+}
+
+/* SIDEBAR MEDIAQUERIES ----------------------------------- */
+@media (max-width: 1199px) {
+ nav.side-navbar {
+ margin-left: -90px;
+ min-width: 90px;
+ max-width: 90px;
+ text-align: center;
+ overflow: hidden;
+ }
+ nav.side-navbar span.heading {
+ margin: 0;
+ }
+ nav.side-navbar ul li a {
+ padding: 15px 2px;
+ border: none;
+ font-size: 0.8em;
+ color: #aaa;
+ -webkit-transition: color 0.3s, background 0.3s;
+ transition: color 0.3s, background 0.3s;
+ }
+ nav.side-navbar ul li a[data-toggle="collapse"]::before {
+ content: '\f107';
+ -webkit-transform: translateX(50%);
+ transform: translateX(50%);
+ position: absolute;
+ top: auto;
+ right: 50%;
+ bottom: 0;
+ left: auto;
+ }
+ nav.side-navbar ul li a[data-toggle="collapse"][aria-expanded="true"]::before {
+ content: '\f106';
+ }
+ nav.side-navbar ul li a:hover {
+ color: #fff;
+ border: none;
+ }
+ nav.side-navbar ul li a:hover i {
+ color: #fff;
+ }
+ nav.side-navbar ul li a i {
+ margin-right: 0;
+ margin-bottom: 5px;
+ display: block;
+ font-size: 1.6em;
+ color: #333;
+ -webkit-transition: color 0.3s;
+ transition: color 0.3s;
+ }
+ nav.side-navbar ul li.active > a {
+ color: #fff;
+ }
+ nav.side-navbar ul li.active > a i {
+ color: #fff;
+ }
+ nav.side-navbar .sidebar-header .title {
+ display: none;
+ }
+ nav.side-navbar.shrinked {
+ margin-left: 0;
+ }
+ .content-inner {
+ width: 100%;
+ }
+ .content-inner.active {
+ width: calc(100% - 90px);
+ }
+}
+
+/*
+* ==========================================================
+* MAIN NAVBAR
+* ==========================================================
+*/
+nav.navbar {
+ background: #2f333e;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ color: #fff;
+ position: relative;
+ border-radius: 0;
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.2);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.2);
+ z-index: 10;
+ padding-left: 0;
+ padding-right: 0;
+ /*==== Toggle Sidebar Btn ====*/
+ /*==== Navbar Items ====*/
+ /*==== Search Box ====*/
+ /*==== Dropdowns ====*/
+}
+
+nav.navbar .badge {
+ width: 22px;
+ height: 22px;
+ line-height: 22px;
+ text-align: center;
+ padding: 0;
+ border-radius: 50%;
+}
+
+nav.navbar .navbar-holder {
+ width: 100%;
+}
+
+nav.navbar a {
+ color: inherit;
+}
+
+nav.navbar .container-fluid {
+ width: 100%;
+}
+
+nav.navbar .menu-btn {
+ margin-right: 20px;
+ font-size: 1.2em;
+ -webkit-transition: all 0.7s;
+ transition: all 0.7s;
+}
+
+nav.navbar .menu-btn span {
+ width: 20px;
+ height: 2px;
+ background: #fff;
+ display: block;
+ margin: 4px auto 0;
+ -webkit-transition: all 0.3s cubic-bezier(0.81, -0.33, 0.345, 1.375);
+ transition: all 0.3s cubic-bezier(0.81, -0.33, 0.345, 1.375);
+}
+
+nav.navbar .menu-btn span:nth-of-type(2) {
+ position: relative;
+ width: 35px;
+ -webkit-transform: rotateY(180deg);
+ transform: rotateY(180deg);
+}
+
+nav.navbar .menu-btn span:nth-of-type(2)::before, nav.navbar .menu-btn span:nth-of-type(2)::after {
+ content: '';
+ width: 6px;
+ height: 2px;
+ display: block;
+ background: #fff;
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+ position: absolute;
+ top: 2px;
+ left: 0;
+ -webkit-transition: all 0.7s;
+ transition: all 0.7s;
+}
+
+nav.navbar .menu-btn span:nth-of-type(2)::after {
+ -webkit-transform: rotate(145deg);
+ transform: rotate(145deg);
+ position: absolute;
+ top: -2px;
+ left: 0;
+}
+
+nav.navbar .menu-btn.active span:first-of-type {
+ -webkit-transform: translateY(12px);
+ transform: translateY(12px);
+}
+
+nav.navbar .menu-btn.active span:nth-of-type(2) {
+ -webkit-transform: none;
+ transform: none;
+}
+
+nav.navbar .menu-btn.active span:last-of-type {
+ -webkit-transform: translateY(-12px);
+ transform: translateY(-12px);
+}
+
+nav.navbar .nav-link {
+ position: relative;
+}
+
+nav.navbar .nav-link span.badge-corner {
+ position: absolute;
+ top: 0;
+ right: 0;
+ font-weight: 400;
+ font-size: 0.7em;
+}
+
+nav.navbar .nav-link.language span {
+ margin-left: .3rem;
+ vertical-align: middle;
+}
+
+nav.navbar .nav-link.logout i {
+ margin-left: 10px;
+}
+
+nav.navbar .nav-menu {
+ margin-bottom: 0;
+}
+
+nav.navbar .search-box {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 0;
+ background: #fff;
+ z-index: 12;
+ border-radius: 0;
+ display: none;
+}
+
+nav.navbar .search-box .dismiss {
+ position: absolute;
+ top: 50%;
+ right: 20px;
+ -webkit-transform: translateY(-50%);
+ transform: translateY(-50%);
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-size: 1.5em;
+ color: #999;
+}
+
+nav.navbar .search-box form {
+ height: 100%;
+}
+
+nav.navbar .search-box form input {
+ height: 100%;
+ border: none;
+ padding: 20px;
+}
+
+nav.navbar .dropdown-toggle::after {
+ border: none !important;
+ content: '\f107';
+ vertical-align: baseline;
+ font-family: 'FontAwesome';
+ margin-left: .3rem;
+}
+
+nav.navbar .dropdown-menu {
+ right: 0;
+ min-width: 250px;
+ left: auto;
+ margin-top: 15px;
+ margin-bottom: 0;
+ padding: 15px 0;
+ max-width: 400px;
+ border-radius: 0;
+ border: none;
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1), -2px 0 2px rgba(0, 0, 0, 0.1);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.1), -2px 0 2px rgba(0, 0, 0, 0.1);
+}
+
+nav.navbar .dropdown-menu .dropdown-item {
+ background: #fff;
+ padding: 10px 20px;
+ font-size: 0.8rem;
+ color: #777;
+ width: 100%;
+}
+
+nav.navbar .dropdown-menu .dropdown-item:hover {
+ background: #f5f5f5;
+}
+
+nav.navbar .dropdown-menu .dropdown-item i {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
+ background: #796AEE;
+ text-align: center;
+ color: #fff;
+ border-radius: 50%;
+ margin-right: 10px;
+}
+
+nav.navbar .dropdown-menu .dropdown-item small {
+ margin-left: 40px;
+}
+
+nav.navbar .dropdown-menu span {
+ position: static;
+ font-size: 0.9em;
+ color: #999;
+}
+
+nav.navbar .dropdown-menu strong {
+ font-weight: 700;
+}
+
+nav.navbar .dropdown-menu .msg-profile {
+ width: 45px;
+ height: 45px;
+ margin-right: 10px;
+}
+
+nav.navbar .dropdown-menu h3 {
+ font-weight: 500;
+}
+
+/* MAIN NAVBAR MEDIAQUERIES ----------------------------------- */
+@media (max-width: 1199px) {
+ nav.navbar {
+ /*==== Toggle Sidebar Btn ====*/
+ }
+ nav.navbar .menu-btn {
+ margin-right: 20px;
+ font-size: 1.2em;
+ -webkit-transition: all 0.7s;
+ transition: all 0.7s;
+ }
+ nav.navbar .menu-btn span:first-of-type {
+ -webkit-transform: translateY(12px);
+ transform: translateY(12px);
+ }
+ nav.navbar .menu-btn span:nth-of-type(2) {
+ -webkit-transform: none;
+ transform: none;
+ }
+ nav.navbar .menu-btn span:nth-of-type(2)::before, nav.navbar .menu-btn span:nth-of-type(2)::after {
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+ }
+ nav.navbar .menu-btn span:nth-of-type(2)::after {
+ -webkit-transform: rotate(145deg);
+ transform: rotate(145deg);
+ position: absolute;
+ top: -2px;
+ left: 0;
+ }
+ nav.navbar .menu-btn span:last-of-type {
+ -webkit-transform: translateY(-12px);
+ transform: translateY(-12px);
+ }
+ nav.navbar .menu-btn.active span:first-of-type {
+ -webkit-transform: none;
+ transform: none;
+ }
+ nav.navbar .menu-btn.active span:nth-of-type(2) {
+ -webkit-transform: rotateY(180deg);
+ transform: rotateY(180deg);
+ }
+ nav.navbar .menu-btn.active span:last-of-type {
+ -webkit-transform: none;
+ transform: none;
+ }
+}
+
+@media (max-width: 575px) {
+ nav.navbar {
+ font-size: 14px;
+ }
+ nav.navbar .badge {
+ width: 19px;
+ height: 19px;
+ }
+ nav.navbar .nav-item > a {
+ font-size: 13px;
+ }
+ nav.navbar .dropdown-menu {
+ right: auto;
+ left: -50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+ }
+}
+
+/*
+* ==========================================================
+* DASHBOARD COUNTS SECTION
+* ==========================================================
+*/
+section.dashboard-counts .icon {
+ width: 40px;
+ height: 40px;
+ line-height: 40px;
+ text-align: center;
+ min-width: 40px;
+ max-width: 40px;
+ border-radius: 50%;
+}
+
+section.dashboard-counts .title {
+ font-size: 1.1em;
+ font-weight: 300;
+ color: #777;
+ margin: 0 20px;
+}
+
+section.dashboard-counts .progress {
+ margin-top: 10px;
+ height: 4px;
+}
+
+section.dashboard-counts .number {
+ font-size: 1.8em;
+ font-weight: 300;
+}
+
+section.dashboard-counts .number strong {
+ font-weight: 700;
+}
+
+section.dashboard-counts .row {
+ padding: 30px 15px;
+ margin: 0;
+}
+
+section.dashboard-counts div[class*='col-'] .item {
+ border-right: 1px solid #eee;
+ padding: 15px 0;
+}
+
+section.dashboard-counts div[class*='col-']:last-of-type .item {
+ border-right: none;
+}
+
+/* DASHBOARD COUNTS MEDIAQUERIES ------------------------ */
+@media (max-width: 1199px) {
+ section.dashboard-counts div[class*='col-']:nth-of-type(2) .item {
+ border-right: none;
+ }
+}
+
+@media (max-width: 575px) {
+ section.dashboard-counts div[class*='col-'] .item {
+ border-right: none;
+ }
+}
+
+/*
+* ==========================================================
+* DASHBOARD HEADER SECTION
+* ==========================================================
+*/
+.statistic {
+ padding: 20px 15px;
+ margin-bottom: 15px;
+}
+
+.statistic:last-of-type {
+ margin-bottom: 0;
+}
+
+.statistic strong {
+ font-size: 1.5em;
+ color: #333;
+ font-weight: 700;
+ line-height: 1;
+}
+
+.statistic small {
+ color: #aaa;
+ text-transform: uppercase;
+}
+
+.statistic .icon {
+ width: 40px;
+ height: 40px;
+ line-height: 40px;
+ text-align: center;
+ min-width: 40px;
+ max-width: 40px;
+ color: #fff;
+ border-radius: 50%;
+ margin-right: 15px;
+}
+
+.chart .title {
+ padding: 15px 0 0 15px;
+}
+
+.chart .title strong {
+ font-weight: 700;
+ font-size: 1.2em;
+}
+
+.chart .title small {
+ color: #aaa;
+ text-transform: uppercase;
+}
+
+.chart .line-chart {
+ width: 100%;
+ height: 100%;
+ padding: 20px 0;
+}
+
+.chart .line-chart canvas {
+ width: calc(100% - 30px) !important;
+}
+
+.chart .bar-chart {
+ margin-bottom: 15px;
+}
+
+.chart .bar-chart canvas {
+ padding: 15px;
+ width: 100%;
+ margin: 0;
+}
+
+/* DASHBOARD HEADER MEDIAQUERIES ------------------------*/
+@media (max-width: 991px) {
+ section.dashboard-header div[class*='col-'] {
+ margin-bottom: 20px;
+ }
+}
+
+/*
+* ==========================================================
+* PROJECTS SECTION
+* ==========================================================
+*/
+.project .row {
+ margin: 0;
+ padding: 15px 0;
+ margin-bottom: 15px;
+}
+
+.project div[class*='col-'] {
+ border-right: 1px solid #eee;
+}
+
+.project .text h3 {
+ margin-bottom: 0;
+ color: #555;
+}
+
+.project .text small {
+ color: #aaa;
+ font-size: 0.75em;
+}
+
+.project .project-date span {
+ font-size: 0.9em;
+ color: #999;
+}
+
+.project .image {
+ max-width: 50px;
+ min-width: 50px;
+ height: 50px;
+ margin-right: 15px;
+}
+
+.project .time,
+.project .comments,
+.project .project-progress {
+ color: #999;
+ font-size: 0.9em;
+ margin-right: 20px;
+}
+
+.project .time i,
+.project .comments i,
+.project .project-progress i {
+ margin-right: 5px;
+}
+
+.project .project-progress {
+ width: 200px;
+}
+
+.project .project-progress .progress {
+ height: 4px;
+}
+
+.project .card {
+ margin-bottom: 0;
+}
+
+/* PROJECTS SECTION ------------------------------------- */
+@media (max-width: 991px) {
+ .project .right-col {
+ margin-top: 20px;
+ margin-left: 65px;
+ }
+ .project .project-progress {
+ width: 150px;
+ }
+}
+
+@media (max-width: 480px) {
+ .project .project-progress {
+ display: none;
+ }
+}
+
+/*
+* ==========================================================
+* CLIENT SECTION
+* ==========================================================
+*/
+/*====== Work Amount Box ======*/
+.work-amount .chart {
+ margin: 40px auto;
+ position: relative;
+}
+
+.work-amount .chart .text {
+ display: inline-block;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ -webkit-transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%);
+}
+
+.work-amount .chart strong {
+ font-size: 1.5rem;
+}
+
+.work-amount .chart span {
+ color: #999;
+ font-weight: 300;
+}
+
+.work-amount li span {
+ font-size: 0.85em;
+ margin-bottom: 10px;
+ color: #777;
+ display: block;
+}
+
+.work-amount li span::before {
+ content: '';
+ display: inline-block;
+ margin-right: 10px;
+ width: 7px;
+ height: 7px;
+ line-height: 7px;
+ background: #85b4f2;
+ border-radius: 50%;
+}
+
+/*====== Client Profile Box ======*/
+.client .client-avatar {
+ width: 100px;
+ height: 100px;
+ margin: 0 auto;
+ position: relative;
+}
+
+.client .client-avatar .status {
+ content: '';
+ display: block;
+ width: 18px;
+ height: 18px;
+ border: 3px solid #fff;
+ border-radius: 50%;
+ position: absolute;
+ right: 4px;
+ bottom: 4px;
+}
+
+.client .client-title {
+ margin-top: 20px;
+}
+
+.client .client-title h3 {
+ font-weight: 500;
+ color: #555;
+}
+
+.client .client-title span {
+ font-size: 0.9em;
+ color: #aaa;
+ display: block;
+}
+
+.client .client-title a {
+ padding: 2px 30px;
+ border-radius: 40px;
+ background: #54e69d;
+ color: #fff;
+ margin-top: 5px;
+ font-size: 0.9em;
+ text-decoration: none;
+}
+
+.client .client-title a:hover {
+ background: #85b4f2;
+}
+
+.client .client-info {
+ margin-top: 20px;
+}
+
+.client .client-info strong {
+ font-weight: 700;
+}
+
+.client .client-social {
+ margin-top: 20px;
+}
+
+.client .client-social a {
+ color: #aaa;
+}
+
+/*====== Total Overdue Box ======*/
+.overdue .chart canvas {
+ width: 100% !important;
+ z-index: 1;
+}
+
+.overdue .card {
+ margin-bottom: 0;
+}
+
+.overdue .card-body {
+ padding: 20px;
+}
+
+.overdue .card-body small {
+ font-weight: 300;
+ color: #aaa;
+}
+
+.overdue .card-body h3 {
+ margin-bottom: 5px;
+}
+
+.overdue .number {
+ font-size: 1.8em;
+ font-weight: 400;
+ color: #555;
+ margin: 35px 0;
+}
+
+/*
+* ==========================================================
+* FEEDS SECTION
+* ==========================================================
+*/
+/*====== Checklist Box ======*/
+.checklist label {
+ font-size: 0.8em;
+ color: #999;
+ line-height: 1.8em;
+ margin-bottom: 0;
+}
+
+.checklist .item {
+ padding: 20px;
+}
+
+.checklist .item:nth-of-type(even) {
+ background: #fafafa;
+}
+
+/*====== Trending Articles Box ======*/
+.articles a {
+ text-decoration: none !important;
+ display: block;
+ margin-bottom: 0;
+ color: #555;
+}
+
+.articles .badge {
+ font-size: 0.7em;
+ padding: 5px 10px;
+ line-height: 1;
+ margin-left: 10px;
+}
+
+.articles .item {
+ padding: 20px;
+}
+
+.articles .item:nth-of-type(even) {
+ background: #fafafa;
+}
+
+.articles .item .image {
+ min-width: 50px;
+ max-width: 50px;
+ height: 50px;
+ margin-right: 15px;
+}
+
+.articles .item img {
+ padding: 3px;
+ border: 1px solid #28a745;
+}
+
+.articles .item h3 {
+ color: #555;
+ font-weight: 400;
+ margin-bottom: 0;
+}
+
+.articles .item small {
+ color: #aaa;
+ font-size: 0.75em;
+}
+
+/*
+* ==========================================================
+* UPDATES SECTION
+* ==========================================================
+*/
+/*====== Recent Updates Box ======*/
+.recent-updates .item {
+ padding: 20px;
+}
+
+.recent-updates .item:nth-of-type(even) {
+ background: #fafafa;
+}
+
+.recent-updates .icon {
+ margin-right: 10px;
+}
+
+.recent-updates h5 {
+ margin-bottom: 5px;
+ color: #333;
+ font-weight: 400;
+}
+
+.recent-updates p {
+ font-size: 0.8em;
+ color: #aaa;
+ margin-bottom: 0;
+}
+
+.recent-updates .date {
+ font-size: 0.9em;
+ color: #adadad;
+}
+
+.recent-updates .date strong {
+ font-size: 1.4em;
+ line-height: 0.8em;
+ display: block;
+}
+
+.recent-updates .date span {
+ font-size: 0.9em;
+ font-weight: 300;
+}
+
+/*====== Daily Feeds Box ======*/
+.daily-feeds .item {
+ padding: 20px;
+ border-bottom: 1px solid #eee;
+}
+
+.daily-feeds .feed-profile {
+ max-width: 50px;
+ min-width: 50px;
+ margin-right: 10px;
+}
+
+.daily-feeds h5 {
+ margin-bottom: 0;
+ color: #555;
+}
+
+.daily-feeds span {
+ font-size: 0.8em;
+ color: #999;
+}
+
+.daily-feeds .full-date {
+ font-size: 0.85em;
+ color: #aaa;
+ font-weight: 300;
+}
+
+.daily-feeds .CTAs {
+ margin-top: 5px;
+}
+
+.daily-feeds .CTAs a {
+ font-size: 0.7em;
+ padding: 3px 8px;
+ margin-right: 5px;
+}
+
+.daily-feeds .CTAs a i {
+ margin-right: 5px;
+}
+
+.daily-feeds .quote {
+ background: #fafafa;
+ margin-top: 5px;
+ border-radius: 0;
+ padding: 15px;
+ margin-left: 60px;
+}
+
+.daily-feeds .quote small {
+ font-size: 0.75em;
+ color: #777;
+}
+
+.daily-feeds .date {
+ font-size: 0.9em;
+ color: #aaa;
+}
+
+/*====== Recent Activities Box ======*/
+.recent-activities .item {
+ padding: 0 15px;
+ border-bottom: 1px solid #eee;
+}
+
+.recent-activities .item div[class*='col-'] {
+ padding: 15px;
+}
+
+.recent-activities h5 {
+ font-weight: 400;
+ color: #333;
+}
+
+.recent-activities p {
+ font-size: 0.75em;
+ color: #999;
+}
+
+.recent-activities .icon {
+ width: 35px;
+ height: 35px;
+ line-height: 35px;
+ background: #f5f5f5;
+ text-align: center;
+ display: inline-block;
+}
+
+.recent-activities .date {
+ font-size: 0.75em;
+ color: #999;
+ padding: 10px;
+}
+
+.recent-activities .date-holder {
+ padding: 0 !important;
+ border-right: 1px solid #eee;
+}
+
+/*
+* ==========================================================
+* FOOTER
+* ==========================================================
+*/
+footer.main-footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ background: #2f333e;
+ color: #fff;
+ padding: 20px 10px;
+}
+
+footer.main-footer a {
+ color: inherit;
+}
+
+footer.main-footer p {
+ margin: 0;
+ font-size: 0.8em;
+}
+
+/* FOOTER MEDIAQUERIES --------------------------------- */
+@media (max-width: 575px) {
+ footer.main-footer div[class*='col'] {
+ text-align: center !important;
+ }
+}
+
+section.charts div[class*="col-"] {
+ margin-bottom: 30px;
+}
+
+section.charts header {
+ padding: 30px 0;
+}
+
+section.charts div[class*="col-"] {
+ margin-bottom: 0;
+}
+
+/*
+* ==========================================================
+* LINE CHART
+* ==========================================================
+*/
+.line-chart-example .card-block {
+ padding: 20px;
+}
+
+/*
+* ==========================================================
+* POLAR CHART
+* ==========================================================
+*/
+/*
+* ==========================================================
+* POLAR CHART
+* ==========================================================
+*/
+.polar-chart-example canvas {
+ max-width: 350px;
+ max-height: 350px;
+ margin: 20px auto;
+}
+
+/*
+* ==========================================================
+* PIE CHART
+* ==========================================================
+*/
+.pie-chart-example canvas {
+ max-width: 350px;
+ max-height: 350px;
+ margin: 15px auto;
+}
+
+/*
+* ==========================================================
+* RADAR CHART
+* ==========================================================
+*/
+.radar-chart-example canvas {
+ max-width: 350px;
+ max-height: 350px;
+ margin: 20px auto;
+}
+
+/* FORMS MEDIAQUERIES -------------------------- */
+@media (max-width: 991px) {
+ section.charts h2 {
+ font-size: 1rem;
+ }
+}
+
+/*
+* ==========================================================
+* FORMS
+* ==========================================================
+*/
+.form-control {
+ height: calc(2.25rem + 2px);
+ border: 1px solid #dee2e6;
+ border-radius: 0;
+ padding: 0.375rem 0.75rem;
+}
+
+.form-control::-moz-placeholder {
+ font-size: 0.9em;
+ font-weight: 300;
+ font-family: "Poppins", sans-serif;
+ color: #aaa;
+}
+
+.form-control::-webkit-input-placeholder {
+ font-size: 0.9em;
+ font-weight: 300;
+ font-family: "Poppins", sans-serif;
+ color: #aaa;
+}
+
+.form-control:-ms-input-placeholder {
+ font-size: 0.9em;
+ font-weight: 300;
+ font-family: "Poppins", sans-serif;
+ color: #aaa;
+}
+
+.form-control:focus {
+ color: #495057;
+ background-color: #fff;
+ border-color: #796AEE;
+ outline: none;
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.25);
+ box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.25);
+}
+
+.form-control-sm {
+ height: calc(1.8125rem + 2px);
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+}
+
+.form-control-lg {
+ height: calc(2.875rem + 2px);
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+}
+
+textarea.form-control {
+ height: auto;
+}
+
+select.form-control[size], select.form-control[multiple] {
+ height: auto;
+}
+
+select.form-control option {
+ color: #999;
+}
+
+.input-group .dropdown-menu {
+ padding: 15px;
+ color: #777;
+ border-radius: 0;
+}
+
+.input-group .dropdown-menu a {
+ padding: 5px 0;
+ color: inherit;
+ text-decoration: none;
+}
+
+.input-group .dropdown-menu a:hover {
+ color: #796AEE;
+ background: none;
+}
+
+.input-group-text {
+ color: #868e96;
+ background: #f8f9fa;
+ border-color: #dee2e6;
+ border-radius: 0;
+}
+
+.input-group-text .checkbox-template, .input-group-text .radio-template {
+ -webkit-transform: none;
+ transform: none;
+}
+
+.form-control-label {
+ font-size: .9rem;
+ color: #777;
+}
+
+button, input[type='submit'] {
+ cursor: pointer;
+ font-family: inherit;
+ font-weight: 300 !important;
+}
+
+.input-group .dropdown-toggle {
+ background: #f5f5f5;
+ color: #777;
+}
+
+.checkbox-template, .radio-template {
+ -webkit-transform: translateY(3px);
+ transform: translateY(3px);
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ position: relative;
+}
+
+.checkbox-template + label, .radio-template + label {
+ margin-left: 10px;
+}
+
+.checkbox-template::before, .radio-template::before {
+ margin-right: 10px;
+ content: '';
+ display: inline-block;
+ -webkit-transform: translate(-2px, -2px);
+ transform: translate(-2px, -2px);
+ width: 18px;
+ height: 18px;
+ line-height: 18px;
+ text-align: center;
+ background: #dae2e7;
+ -webkit-transition: all 0.2s;
+ transition: all 0.2s;
+}
+
+.checkbox-template::after, .radio-template::after {
+ content: '\f00c';
+ width: 12px;
+ height: 12px;
+ line-height: 12px;
+ text-align: center;
+ display: block;
+ font-family: 'FontAwesome';
+ position: absolute;
+ top: 1px;
+ left: 1px;
+ font-size: 0.7em;
+ opacity: 0;
+ -webkit-transition: all 0.2s;
+ transition: all 0.2s;
+ color: #fff;
+}
+
+.checkbox-template:checked::before, .radio-template:checked::before {
+ background: #796AEE;
+}
+
+.checkbox-template:checked::after, .radio-template:checked::after {
+ opacity: 1;
+}
+
+.radio-template::before {
+ border-radius: 50%;
+ -webkit-transform: translate(-3px, -3px);
+ transform: translate(-3px, -3px);
+}
+
+.radio-template::after {
+ width: 6px;
+ height: 6px;
+ line-height: 6px;
+ text-align: center;
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ border-radius: 50%;
+ content: '';
+}
+
+.radio-template:checked::after {
+ background: #fff;
+}
+
+input.input-material {
+ width: 100%;
+ border: none;
+ border-bottom: 1px solid #eee;
+ padding: 10px 0;
+}
+
+input.input-material.is-invalid {
+ border-color: #dc3545 !important;
+}
+
+input.input-material:focus {
+ border-color: #796AEE;
+}
+
+input.input-material ~ label {
+ color: #aaa;
+ position: absolute;
+ top: 14px;
+ left: 0;
+ cursor: text;
+ -webkit-transition: all 0.2s;
+ transition: all 0.2s;
+ font-weight: 300;
+}
+
+input.input-material ~ label.active {
+ font-size: 0.8rem;
+ top: -10px;
+ color: #796AEE;
+}
+
+input.input-material.is-invalid ~ label {
+ color: #dc3545;
+}
+
+.form-group-material {
+ position: relative;
+ margin-bottom: 30px;
+}
+
+.modal-content {
+ border-radius: 0;
+}
+
+.i-checks {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+}
+
+/*
+* ==========================================================
+* FORM PAGE
+* ==========================================================
+*/
+.forms p {
+ font-size: 0.9em;
+ color: #555;
+}
+
+.forms form small {
+ font-size: 0.8em;
+ color: #999;
+ font-weight: 300;
+}
+
+.forms .line {
+ width: 100%;
+ height: 1px;
+ border-bottom: 1px dashed #eee;
+ margin: 30px 0;
+}
+
+/*
+
+=====================
+STYLE SWITCHER FOR DEMO
+=====================
+
+*/
+#style-switch-button {
+ position: fixed;
+ top: 120px;
+ right: 0px;
+ border-radius: 0;
+ z-index: 12;
+}
+
+#style-switch {
+ width: 300px;
+ padding: 20px;
+ position: fixed;
+ top: 160px;
+ right: 0;
+ background: #fff;
+ border: solid 1px #ced4da;
+ z-index: 12;
+}
+
+#style-switch h4 {
+ color: #495057;
+}
+
+/* =========================================
+ THEMING OF BOOTSTRAP COMPONENTS
+ ========================================= */
+/*
+ * 1. NAVBAR
+ */
+.navbar {
+ padding: 0.5rem 1rem;
+}
+
+.navbar-brand {
+ display: inline-block;
+ padding-top: 0.3125rem;
+ padding-bottom: 0.3125rem;
+ margin-right: 1rem;
+ font-size: 1.25rem;
+}
+
+.navbar-toggler {
+ padding: 0.25rem 0.75rem;
+ font-size: 1.25rem;
+ line-height: 1;
+ border: 1px solid transparent;
+ border-radius: 0.25rem;
+}
+
+.navbar-light .navbar-brand {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-brand:focus, .navbar-light .navbar-brand:hover {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-nav .nav-link {
+ color: rgba(0, 0, 0, 0.5);
+}
+
+.navbar-light .navbar-nav .nav-link:focus, .navbar-light .navbar-nav .nav-link:hover {
+ color: rgba(0, 0, 0, 0.7);
+}
+
+.navbar-light .navbar-nav .nav-link.disabled {
+ color: rgba(0, 0, 0, 0.3);
+}
+
+.navbar-light .navbar-nav .show > .nav-link,
+.navbar-light .navbar-nav .active > .nav-link,
+.navbar-light .navbar-nav .nav-link.show,
+.navbar-light .navbar-nav .nav-link.active {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-toggler {
+ color: rgba(0, 0, 0, 0.5);
+ border-color: rgba(0, 0, 0, 0.1);
+}
+
+.navbar-light .navbar-toggler-icon {
+ background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
+}
+
+.navbar-light .navbar-text {
+ color: rgba(0, 0, 0, 0.5);
+}
+
+.navbar-dark .navbar-brand {
+ color: #fff;
+}
+
+.navbar-dark .navbar-brand:focus, .navbar-dark .navbar-brand:hover {
+ color: #fff;
+}
+
+.navbar-dark .navbar-nav .nav-link {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.navbar-dark .navbar-nav .nav-link:focus, .navbar-dark .navbar-nav .nav-link:hover {
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.navbar-dark .navbar-nav .nav-link.disabled {
+ color: rgba(255, 255, 255, 0.25);
+}
+
+.navbar-dark .navbar-nav .show > .nav-link,
+.navbar-dark .navbar-nav .active > .nav-link,
+.navbar-dark .navbar-nav .nav-link.show,
+.navbar-dark .navbar-nav .nav-link.active {
+ color: #fff;
+}
+
+.navbar-dark .navbar-toggler {
+ color: rgba(255, 255, 255, 0.5);
+ border-color: rgba(255, 255, 255, 0.1);
+}
+
+.navbar-dark .navbar-toggler-icon {
+ background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
+}
+
+.navbar-dark .navbar-text {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+/*
+ * 2. BUTTONS
+ */
+.btn {
+ font-weight: 400;
+ border: 1px solid transparent;
+ padding: 0.375rem 0.75rem;
+ font-size: 1rem;
+ line-height: 1.5;
+ border-radius: 0.25rem;
+ -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out;
+}
+
+.btn:focus, .btn.focus {
+ outline: 0;
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.25);
+ box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.25);
+}
+
+.btn.disabled, .btn:disabled {
+ opacity: .65;
+}
+
+.btn:not([disabled]):not(.disabled):active, .btn:not([disabled]):not(.disabled).active {
+ background-image: none;
+}
+
+.btn-primary {
+ color: color-yiq(#796AEE);
+ background-color: #796AEE;
+ border-color: #796AEE;
+}
+
+.btn-primary:hover {
+ color: color-yiq(#5a48ea);
+ background-color: #5a48ea;
+ border-color: #503ce9;
+}
+
+.btn-primary:focus, .btn-primary.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+}
+
+.btn-primary.disabled, .btn-primary:disabled {
+ color: color-yiq(#796AEE);
+ background-color: #796AEE;
+ border-color: #796AEE;
+}
+
+.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,
+.show > .btn-primary.dropdown-toggle {
+ color: color-yiq(#503ce9);
+ background-color: #503ce9;
+ border-color: #4631e7;
+}
+
+.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-primary.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+}
+
+.btn-secondary {
+ color: color-yiq(#868e96);
+ background-color: #868e96;
+ border-color: #868e96;
+}
+
+.btn-secondary:hover {
+ color: color-yiq(#727b84);
+ background-color: #727b84;
+ border-color: #6c757d;
+}
+
+.btn-secondary:focus, .btn-secondary.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+}
+
+.btn-secondary.disabled, .btn-secondary:disabled {
+ color: color-yiq(#868e96);
+ background-color: #868e96;
+ border-color: #868e96;
+}
+
+.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,
+.show > .btn-secondary.dropdown-toggle {
+ color: color-yiq(#6c757d);
+ background-color: #6c757d;
+ border-color: #666e76;
+}
+
+.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-secondary.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+}
+
+.btn-success {
+ color: color-yiq(#28a745);
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-success:hover {
+ color: color-yiq(#218838);
+ background-color: #218838;
+ border-color: #1e7e34;
+}
+
+.btn-success:focus, .btn-success.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-success.disabled, .btn-success:disabled {
+ color: color-yiq(#28a745);
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,
+.show > .btn-success.dropdown-toggle {
+ color: color-yiq(#1e7e34);
+ background-color: #1e7e34;
+ border-color: #1c7430;
+}
+
+.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,
+.show > .btn-success.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-info {
+ color: color-yiq(#17a2b8);
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-info:hover {
+ color: color-yiq(#138496);
+ background-color: #138496;
+ border-color: #117a8b;
+}
+
+.btn-info:focus, .btn-info.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-info.disabled, .btn-info:disabled {
+ color: color-yiq(#17a2b8);
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,
+.show > .btn-info.dropdown-toggle {
+ color: color-yiq(#117a8b);
+ background-color: #117a8b;
+ border-color: #10707f;
+}
+
+.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,
+.show > .btn-info.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-warning {
+ color: color-yiq(#ffc107);
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-warning:hover {
+ color: color-yiq(#e0a800);
+ background-color: #e0a800;
+ border-color: #d39e00;
+}
+
+.btn-warning:focus, .btn-warning.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-warning.disabled, .btn-warning:disabled {
+ color: color-yiq(#ffc107);
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,
+.show > .btn-warning.dropdown-toggle {
+ color: color-yiq(#d39e00);
+ background-color: #d39e00;
+ border-color: #c69500;
+}
+
+.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,
+.show > .btn-warning.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-danger {
+ color: color-yiq(#dc3545);
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-danger:hover {
+ color: color-yiq(#c82333);
+ background-color: #c82333;
+ border-color: #bd2130;
+}
+
+.btn-danger:focus, .btn-danger.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-danger.disabled, .btn-danger:disabled {
+ color: color-yiq(#dc3545);
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,
+.show > .btn-danger.dropdown-toggle {
+ color: color-yiq(#bd2130);
+ background-color: #bd2130;
+ border-color: #b21f2d;
+}
+
+.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,
+.show > .btn-danger.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-light {
+ color: color-yiq(#f8f9fa);
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-light:hover {
+ color: color-yiq(#e2e6ea);
+ background-color: #e2e6ea;
+ border-color: #dae0e5;
+}
+
+.btn-light:focus, .btn-light.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-light.disabled, .btn-light:disabled {
+ color: color-yiq(#f8f9fa);
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,
+.show > .btn-light.dropdown-toggle {
+ color: color-yiq(#dae0e5);
+ background-color: #dae0e5;
+ border-color: #d3d9df;
+}
+
+.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,
+.show > .btn-light.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-dark {
+ color: color-yiq(#343a40);
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-dark:hover {
+ color: color-yiq(#23272b);
+ background-color: #23272b;
+ border-color: #1d2124;
+}
+
+.btn-dark:focus, .btn-dark.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-dark.disabled, .btn-dark:disabled {
+ color: color-yiq(#343a40);
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,
+.show > .btn-dark.dropdown-toggle {
+ color: color-yiq(#1d2124);
+ background-color: #1d2124;
+ border-color: #171a1d;
+}
+
+.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,
+.show > .btn-dark.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-default {
+ color: color-yiq(#ced4da);
+ background-color: #ced4da;
+ border-color: #ced4da;
+}
+
+.btn-default:hover {
+ color: color-yiq(#b8c1ca);
+ background-color: #b8c1ca;
+ border-color: #b1bbc4;
+}
+
+.btn-default:focus, .btn-default.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(206, 212, 218, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(206, 212, 218, 0.5);
+}
+
+.btn-default.disabled, .btn-default:disabled {
+ color: color-yiq(#ced4da);
+ background-color: #ced4da;
+ border-color: #ced4da;
+}
+
+.btn-default:not(:disabled):not(.disabled):active, .btn-default:not(:disabled):not(.disabled).active,
+.show > .btn-default.dropdown-toggle {
+ color: color-yiq(#b1bbc4);
+ background-color: #b1bbc4;
+ border-color: #aab4bf;
+}
+
+.btn-default:not(:disabled):not(.disabled):active:focus, .btn-default:not(:disabled):not(.disabled).active:focus,
+.show > .btn-default.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(206, 212, 218, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(206, 212, 218, 0.5);
+}
+
+.btn-outline-primary {
+ color: #796AEE;
+ background-color: transparent;
+ background-image: none;
+ border-color: #796AEE;
+}
+
+.btn-outline-primary:hover {
+ color: #fff;
+ background-color: #796AEE;
+ border-color: #796AEE;
+}
+
+.btn-outline-primary:focus, .btn-outline-primary.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+}
+
+.btn-outline-primary.disabled, .btn-outline-primary:disabled {
+ color: #796AEE;
+ background-color: transparent;
+}
+
+.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,
+.show > .btn-outline-primary.dropdown-toggle {
+ color: color-yiq(#796AEE);
+ background-color: #796AEE;
+ border-color: #796AEE;
+}
+
+.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-primary.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+}
+
+.btn-outline-secondary {
+ color: #868e96;
+ background-color: transparent;
+ background-image: none;
+ border-color: #868e96;
+}
+
+.btn-outline-secondary:hover {
+ color: #fff;
+ background-color: #868e96;
+ border-color: #868e96;
+}
+
+.btn-outline-secondary:focus, .btn-outline-secondary.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+}
+
+.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {
+ color: #868e96;
+ background-color: transparent;
+}
+
+.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,
+.show > .btn-outline-secondary.dropdown-toggle {
+ color: color-yiq(#868e96);
+ background-color: #868e96;
+ border-color: #868e96;
+}
+
+.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-secondary.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(134, 142, 150, 0.5);
+}
+
+.btn-outline-success {
+ color: #28a745;
+ background-color: transparent;
+ background-image: none;
+ border-color: #28a745;
+}
+
+.btn-outline-success:hover {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:focus, .btn-outline-success.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-outline-success.disabled, .btn-outline-success:disabled {
+ color: #28a745;
+ background-color: transparent;
+}
+
+.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,
+.show > .btn-outline-success.dropdown-toggle {
+ color: color-yiq(#28a745);
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-success.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-outline-info {
+ color: #17a2b8;
+ background-color: transparent;
+ background-image: none;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:hover {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:focus, .btn-outline-info.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-outline-info.disabled, .btn-outline-info:disabled {
+ color: #17a2b8;
+ background-color: transparent;
+}
+
+.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,
+.show > .btn-outline-info.dropdown-toggle {
+ color: color-yiq(#17a2b8);
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-info.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-outline-warning {
+ color: #ffc107;
+ background-color: transparent;
+ background-image: none;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:hover {
+ color: #fff;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:focus, .btn-outline-warning.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-outline-warning.disabled, .btn-outline-warning:disabled {
+ color: #ffc107;
+ background-color: transparent;
+}
+
+.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,
+.show > .btn-outline-warning.dropdown-toggle {
+ color: color-yiq(#ffc107);
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-warning.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-outline-danger {
+ color: #dc3545;
+ background-color: transparent;
+ background-image: none;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:hover {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:focus, .btn-outline-danger.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-outline-danger.disabled, .btn-outline-danger:disabled {
+ color: #dc3545;
+ background-color: transparent;
+}
+
+.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,
+.show > .btn-outline-danger.dropdown-toggle {
+ color: color-yiq(#dc3545);
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-danger.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-outline-light {
+ color: #f8f9fa;
+ background-color: transparent;
+ background-image: none;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:hover {
+ color: #fff;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:focus, .btn-outline-light.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-outline-light.disabled, .btn-outline-light:disabled {
+ color: #f8f9fa;
+ background-color: transparent;
+}
+
+.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,
+.show > .btn-outline-light.dropdown-toggle {
+ color: color-yiq(#f8f9fa);
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-light.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-outline-dark {
+ color: #343a40;
+ background-color: transparent;
+ background-image: none;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:hover {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:focus, .btn-outline-dark.focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-outline-dark.disabled, .btn-outline-dark:disabled {
+ color: #343a40;
+ background-color: transparent;
+}
+
+.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,
+.show > .btn-outline-dark.dropdown-toggle {
+ color: color-yiq(#343a40);
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-dark.dropdown-toggle:focus {
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-lg {
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.3rem;
+}
+
+.btn-sm {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+}
+
+/*
+ * 3. TYPE
+ */
+body {
+ font-family: "Poppins", sans-serif;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #212529;
+ background-color: #fff;
+}
+
+a {
+ color: #796AEE;
+ text-decoration: none;
+}
+
+a:focus, a:hover {
+ color: #3b25e6;
+ text-decoration: underline;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+ margin-bottom: 0.5rem;
+ font-family: inherit;
+ font-weight: 700;
+ line-height: 1.1;
+ color: inherit;
+}
+
+h1,
+.h1 {
+ font-size: 1.5rem;
+}
+
+h2,
+.h2 {
+ font-size: 1.3rem;
+}
+
+h3,
+.h3 {
+ font-size: 1.2rem;
+}
+
+h4,
+.h4 {
+ font-size: 1.1rem;
+}
+
+h5,
+.h5 {
+ font-size: 1rem;
+}
+
+h6,
+.h6 {
+ font-size: 0.95rem;
+}
+
+.lead {
+ font-size: 1.25rem;
+ font-weight: 300;
+}
+
+.display-1 {
+ font-size: 6rem;
+ font-weight: 300;
+ line-height: 1.1;
+}
+
+.display-2 {
+ font-size: 5.5rem;
+ font-weight: 300;
+ line-height: 1.1;
+}
+
+.display-3 {
+ font-size: 4.5rem;
+ font-weight: 300;
+ line-height: 1.1;
+}
+
+.display-4 {
+ font-size: 3.5rem;
+ font-weight: 300;
+ line-height: 1.1;
+}
+
+hr {
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+small,
+.small {
+ font-size: 80%;
+ font-weight: 400;
+}
+
+mark,
+.mark {
+ padding: 0.2em;
+ background-color: #fcf8e3;
+}
+
+.blockquote {
+ padding: 0.5rem 1rem;
+ margin-bottom: 2rem;
+ font-size: 1.25rem;
+ border-left: 5px solid #796AEE;
+}
+
+.blockquote-footer {
+ color: #868e96;
+}
+
+.blockquote-footer::before {
+ content: "\2014 \00A0";
+}
+
+.text-primary {
+ color: #796AEE !important;
+}
+
+a.text-primary:focus, a.text-primary:hover {
+ color: #503ce9 !important;
+}
+
+/*
+ * 4. PAGINATION
+ */
+.page-item:first-child .page-link {
+ border-top-left-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+}
+
+.page-item:last-child .page-link {
+ border-top-right-radius: 0.25rem;
+ border-bottom-right-radius: 0.25rem;
+}
+
+.page-item.active .page-link {
+ color: #fff;
+ background-color: #796AEE;
+ border-color: #796AEE;
+}
+
+.page-item.disabled .page-link {
+ color: #868e96;
+ background-color: #fff;
+ border-color: #ddd;
+}
+
+.page-link {
+ padding: 0.5rem 0.75rem;
+ margin-left: -1px;
+ line-height: 1.25;
+ color: #796AEE;
+ background-color: #fff;
+ border: 1px solid #ddd;
+}
+
+.page-link:hover {
+ z-index: 2;
+ color: #3b25e6;
+ background-color: #e9ecef;
+ border-color: #ddd;
+}
+
+.page-link:focus {
+ z-index: 2;
+ outline: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ text-decoration: none;
+}
+
+.pagination-lg .page-link {
+ padding: 0.75rem 1.5rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+}
+
+.pagination-lg .page-item:first-child .page-link {
+ border-top-left-radius: 0.3rem;
+ border-bottom-left-radius: 0.3rem;
+}
+
+.pagination-lg .page-item:last-child .page-link {
+ border-top-right-radius: 0.3rem;
+ border-bottom-right-radius: 0.3rem;
+}
+
+.pagination-sm .page-link {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+}
+
+.pagination-sm .page-item:first-child .page-link {
+ border-top-left-radius: 0.2rem;
+ border-bottom-left-radius: 0.2rem;
+}
+
+.pagination-sm .page-item:last-child .page-link {
+ border-top-right-radius: 0.2rem;
+ border-bottom-right-radius: 0.2rem;
+}
+
+/*
+* 5. UTILITIES
+*/
+.bg-primary {
+ background-color: #796AEE !important;
+}
+
+a.bg-primary:focus, a.bg-primary:hover {
+ background-color: #503ce9 !important;
+}
+
+.bg-secondary {
+ background-color: #868e96 !important;
+}
+
+a.bg-secondary:focus, a.bg-secondary:hover {
+ background-color: #6c757d !important;
+}
+
+.bg-success {
+ background-color: #28a745 !important;
+}
+
+a.bg-success:focus, a.bg-success:hover {
+ background-color: #1e7e34 !important;
+}
+
+.bg-info {
+ background-color: #17a2b8 !important;
+}
+
+a.bg-info:focus, a.bg-info:hover {
+ background-color: #117a8b !important;
+}
+
+.bg-warning {
+ background-color: #ffc107 !important;
+}
+
+a.bg-warning:focus, a.bg-warning:hover {
+ background-color: #d39e00 !important;
+}
+
+.bg-danger {
+ background-color: #dc3545 !important;
+}
+
+a.bg-danger:focus, a.bg-danger:hover {
+ background-color: #bd2130 !important;
+}
+
+.bg-light {
+ background-color: #f8f9fa !important;
+}
+
+a.bg-light:focus, a.bg-light:hover {
+ background-color: #dae0e5 !important;
+}
+
+.bg-dark {
+ background-color: #343a40 !important;
+}
+
+a.bg-dark:focus, a.bg-dark:hover {
+ background-color: #1d2124 !important;
+}
+
+.border-primary {
+ border-color: #796AEE !important;
+}
+
+.border-secondary {
+ border-color: #868e96 !important;
+}
+
+.border-success {
+ border-color: #28a745 !important;
+}
+
+.border-info {
+ border-color: #17a2b8 !important;
+}
+
+.border-warning {
+ border-color: #ffc107 !important;
+}
+
+.border-danger {
+ border-color: #dc3545 !important;
+}
+
+.border-light {
+ border-color: #f8f9fa !important;
+}
+
+.border-dark {
+ border-color: #343a40 !important;
+}
+
+.text-primary {
+ color: #796AEE !important;
+}
+
+a.text-primary:focus, a.text-primary:hover {
+ color: #503ce9 !important;
+}
+
+.text-secondary {
+ color: #868e96 !important;
+}
+
+a.text-secondary:focus, a.text-secondary:hover {
+ color: #6c757d !important;
+}
+
+.text-success {
+ color: #28a745 !important;
+}
+
+a.text-success:focus, a.text-success:hover {
+ color: #1e7e34 !important;
+}
+
+.text-info {
+ color: #17a2b8 !important;
+}
+
+a.text-info:focus, a.text-info:hover {
+ color: #117a8b !important;
+}
+
+.text-warning {
+ color: #ffc107 !important;
+}
+
+a.text-warning:focus, a.text-warning:hover {
+ color: #d39e00 !important;
+}
+
+.text-danger {
+ color: #dc3545 !important;
+}
+
+a.text-danger:focus, a.text-danger:hover {
+ color: #bd2130 !important;
+}
+
+.text-light {
+ color: #f8f9fa !important;
+}
+
+a.text-light:focus, a.text-light:hover {
+ color: #dae0e5 !important;
+}
+
+.text-dark {
+ color: #343a40 !important;
+}
+
+a.text-dark:focus, a.text-dark:hover {
+ color: #1d2124 !important;
+}
+
+.badge-primary {
+ color: color-yiq(#796AEE);
+ background-color: #796AEE;
+}
+
+.badge-primary[href]:focus, .badge-primary[href]:hover {
+ color: color-yiq(#796AEE);
+ text-decoration: none;
+ background-color: #503ce9;
+}
+
+.badge-secondary {
+ color: color-yiq(#868e96);
+ background-color: #868e96;
+}
+
+.badge-secondary[href]:focus, .badge-secondary[href]:hover {
+ color: color-yiq(#868e96);
+ text-decoration: none;
+ background-color: #6c757d;
+}
+
+.badge-success {
+ color: color-yiq(#28a745);
+ background-color: #28a745;
+}
+
+.badge-success[href]:focus, .badge-success[href]:hover {
+ color: color-yiq(#28a745);
+ text-decoration: none;
+ background-color: #1e7e34;
+}
+
+.badge-info {
+ color: color-yiq(#17a2b8);
+ background-color: #17a2b8;
+}
+
+.badge-info[href]:focus, .badge-info[href]:hover {
+ color: color-yiq(#17a2b8);
+ text-decoration: none;
+ background-color: #117a8b;
+}
+
+.badge-warning {
+ color: color-yiq(#ffc107);
+ background-color: #ffc107;
+}
+
+.badge-warning[href]:focus, .badge-warning[href]:hover {
+ color: color-yiq(#ffc107);
+ text-decoration: none;
+ background-color: #d39e00;
+}
+
+.badge-danger {
+ color: color-yiq(#dc3545);
+ background-color: #dc3545;
+}
+
+.badge-danger[href]:focus, .badge-danger[href]:hover {
+ color: color-yiq(#dc3545);
+ text-decoration: none;
+ background-color: #bd2130;
+}
+
+.badge-light {
+ color: color-yiq(#f8f9fa);
+ background-color: #f8f9fa;
+}
+
+.badge-light[href]:focus, .badge-light[href]:hover {
+ color: color-yiq(#f8f9fa);
+ text-decoration: none;
+ background-color: #dae0e5;
+}
+
+.badge-dark {
+ color: color-yiq(#343a40);
+ background-color: #343a40;
+}
+
+.badge-dark[href]:focus, .badge-dark[href]:hover {
+ color: color-yiq(#343a40);
+ text-decoration: none;
+ background-color: #1d2124;
+}
+
+/*
+* 6.CODE
+*/
+code {
+ padding: 0.2rem 0.4rem;
+ font-size: 90%;
+ color: #bd4147;
+ background-color: #f8f9fa;
+ border-radius: 0.25rem;
+}
+
+a > code {
+ padding: 0;
+ color: inherit;
+ background-color: inherit;
+}
+
+/*
+* 7. NAV
+*/
+.nav-link {
+ padding: 0.5rem 1rem;
+}
+
+.nav-link.disabled {
+ color: #868e96;
+}
+
+.nav-tabs .nav-item {
+ margin-bottom: -1px;
+}
+
+.nav-tabs .nav-link {
+ border: 1px solid transparent;
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.nav-tabs .nav-link:focus, .nav-tabs .nav-link:hover {
+ border-color: #e9ecef #e9ecef #dee2e6;
+}
+
+.nav-tabs .nav-link.disabled {
+ color: #868e96;
+}
+
+.nav-tabs .nav-link.active,
+.nav-tabs .nav-item.show .nav-link {
+ color: #495057;
+ background-color: #fff;
+}
+
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+}
+
+.nav-pills .nav-link {
+ border-radius: 0.25rem;
+}
+
+.nav-pills .nav-link.active,
+.nav-pills .show > .nav-link {
+ color: #fff;
+ background-color: #796AEE;
+}
+
+/*
+* 8. CARD
+*/
+.card {
+ background-color: #fff;
+ border: 0 solid #eee;
+ border-radius: 0;
+}
+
+.card > .list-group:first-child .list-group-item:first-child {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.card > .list-group:last-child .list-group-item:last-child {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.card-body {
+ padding: 1.25rem;
+}
+
+.card-title {
+ margin-bottom: 1rem;
+}
+
+.card-subtitle {
+ margin-top: -0.5rem;
+}
+
+.card-link + .card-link {
+ margin-left: 1.25rem;
+}
+
+.card-header {
+ padding: 1rem 1.25rem;
+ background-color: #fff;
+ border-bottom: 1px solid #eee;
+}
+
+.card-header:first-child {
+ border-radius: 0 0 0 0;
+}
+
+.card-header-transparent {
+ background-color: rgba(0, 0, 0, 0.3);
+ border-bottom: none;
+}
+
+.card-footer {
+ padding: 1rem 1.25rem;
+ background-color: #f8f9fa;
+ border-top: 1px solid #eee;
+}
+
+.card-footer:last-child {
+ border-radius: 0 0 0 0;
+}
+
+.card-header-tabs {
+ margin-right: -0.625rem;
+ margin-bottom: -1rem;
+ margin-left: -0.625rem;
+ border-bottom: 0;
+}
+
+.card-header-pills {
+ margin-right: -0.625rem;
+ margin-left: -0.625rem;
+}
+
+.card-img-overlay {
+ padding: 1.25rem;
+}
+
+.card-img-overlay-opacity {
+ background: rgba(0, 0, 0, 0.2);
+}
+
+.card-img {
+ border-radius: 0;
+}
+
+.card-img-top {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.card-img-bottom {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.card-deck .card {
+ margin-bottom: 15px;
+}
+
+@media (min-width: 576px) {
+ .card-deck {
+ margin-right: -15px;
+ margin-left: -15px;
+ }
+ .card-deck .card {
+ margin-right: 15px;
+ margin-left: 15px;
+ }
+}
+
+.custom-select {
+ height: calc(2.25rem + 2px);
+ padding: 0.375rem 1.75rem 0.375rem 0.75rem;
+ line-height: 1.5;
+ color: #495057;
+ vertical-align: middle;
+ background: #fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right 0.75rem center;
+ background-size: 8px 10px;
+ border: 1px solid #ced4da;
+ border-radius: 0;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+.custom-select:focus {
+ border-color: #796AEE;
+ outline: 0;
+ -webkit-box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+ box-shadow: 0 0 0 0.2rem rgba(121, 106, 238, 0.5);
+}
+
+.custom-select:focus::-ms-value {
+ color: #495057;
+ background-color: #fff;
+}
+
+.custom-select[multiple], .custom-select[size]:not([size="1"]) {
+ height: auto;
+ padding-right: 0.75rem;
+ background-image: none;
+}
+
+.custom-select:disabled {
+ color: #868e96;
+ background-color: #e9ecef;
+}
+
+.custom-select::-ms-expand {
+ opacity: 0;
+}
+
+.custom-select-sm {
+ height: calc(1.8125rem + 2px);
+ padding-top: 0.375rem;
+ padding-bottom: 0.375rem;
+ font-size: 75%;
+}
+
+.custom-select-lg {
+ height: calc(2.875rem + 2px);
+ padding-top: 0.375rem;
+ padding-bottom: 0.375rem;
+ font-size: 125%;
+}
\ No newline at end of file
diff --git a/src/main/webapp/static/css/login.css b/src/main/webapp/static/css/login.css
new file mode 100644
index 0000000..f520ab5
--- /dev/null
+++ b/src/main/webapp/static/css/login.css
@@ -0,0 +1,120 @@
+/*
+* ==========================================================
+* LOGIN PAGE
+* ==========================================================
+*/
+.login-page {
+ position: relative;
+}
+
+.login-page::before {
+ content: '';
+ width: 100%;
+ height: 100%;
+ display: block;
+ z-index: -1;
+ background: url(../imgs/login.jpg);
+ background-size: cover;
+ -webkit-filter: blur(10px);
+ filter: blur(10px);
+ z-index: 1;
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+.login-page .container {
+ min-height: 100vh;
+ z-index: 999;
+ padding: 20px;
+ position: relative;
+}
+
+.login-page .form-holder {
+ width: 100%;
+ border-radius: 5px;
+ overflow: hidden;
+ margin-bottom: 50px;
+}
+
+.login-page .form-holder .info,
+.login-page .form-holder .form {
+ min-height: 70vh;
+ padding: 40px;
+ height: 100%;
+}
+
+.login-page .form-holder div[class*='col-'] {
+ padding: 0;
+}
+
+.login-page .form-holder .info {
+ background: rgba(121, 106, 238, 0.9);
+ color: #fff;
+}
+
+.login-page .form-holder .info h1 {
+ font-size: 2.5em;
+ font-weight: 600;
+}
+
+.login-page .form-holder .info p {
+ font-weight: 300;
+}
+
+.login-page .form-holder .form .form-group {
+ position: relative;
+ margin-bottom: 30px;
+}
+
+.login-page .form-holder .form .content {
+ width: 100%;
+}
+
+.login-page .form-holder .form form {
+ width: 100%;
+ max-width: 400px;
+}
+
+.login-page .form-holder .form #login,
+.login-page .form-holder .form #register {
+ margin-bottom: 20px;
+ cursor: pointer;
+}
+
+.login-page .form-holder .form a.forgot-pass,
+.login-page .form-holder .form a.signup {
+ font-size: 0.9em;
+ color: #85b4f2;
+}
+
+.login-page .form-holder .form small {
+ color: #aaa;
+}
+
+.login-page .form-holder .form .terms-conditions label {
+ cursor: pointer;
+ color: #aaa;
+ font-size: 0.9em;
+}
+
+.login-page .copyrights {
+ width: 100%;
+ z-index: 9999;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ color: #fff;
+}
+
+@media (max-width: 991px) {
+ .login-page .info,
+ .login-page .form {
+ min-height: auto !important;
+ }
+
+ .login-page .info {
+ padding-top: 100px !important;
+ padding-bottom: 100px !important;
+ }
+}
diff --git a/src/main/webapp/static/css/util/bootstrap-datetimepicker.min.css b/src/main/webapp/static/css/util/bootstrap-datetimepicker.min.css
new file mode 100644
index 0000000..78485fe
--- /dev/null
+++ b/src/main/webapp/static/css/util/bootstrap-datetimepicker.min.css
@@ -0,0 +1,9 @@
+/*!
+ * Datetimepicker for Bootstrap
+ *
+ * Copyright 2012 Stefan Petre
+ * Improvements by Andrew Rowls
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */.datetimepicker{padding:4px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;direction:ltr}.datetimepicker-inline{width:220px}.datetimepicker.datetimepicker-rtl{direction:rtl}.datetimepicker.datetimepicker-rtl table tr td span{float:right}.datetimepicker-dropdown,.datetimepicker-dropdown-left{top:0;left:0}[class*=" datetimepicker-dropdown"]:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);position:absolute}[class*=" datetimepicker-dropdown"]:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute}[class*=" datetimepicker-dropdown-top"]:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,0.2);border-bottom:0}[class*=" datetimepicker-dropdown-top"]:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;border-bottom:0}.datetimepicker-dropdown-bottom-left:before{top:-7px;right:6px}.datetimepicker-dropdown-bottom-left:after{top:-6px;right:7px}.datetimepicker-dropdown-bottom-right:before{top:-7px;left:6px}.datetimepicker-dropdown-bottom-right:after{top:-6px;left:7px}.datetimepicker-dropdown-top-left:before{bottom:-7px;right:6px}.datetimepicker-dropdown-top-left:after{bottom:-6px;right:7px}.datetimepicker-dropdown-top-right:before{bottom:-7px;left:6px}.datetimepicker-dropdown-top-right:after{bottom:-6px;left:7px}.datetimepicker>div{display:none}.datetimepicker.minutes div.datetimepicker-minutes{display:block}.datetimepicker.hours div.datetimepicker-hours{display:block}.datetimepicker.days div.datetimepicker-days{display:block}.datetimepicker.months div.datetimepicker-months{display:block}.datetimepicker.years div.datetimepicker-years{display:block}.datetimepicker table{margin:0}.datetimepicker td,.datetimepicker th{text-align:center;width:20px;height:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:0}.table-striped .datetimepicker table tr td,.table-striped .datetimepicker table tr th{background-color:transparent}.datetimepicker table tr td.minute:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.hour:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.day:hover{background:#eee;cursor:pointer}.datetimepicker table tr td.old,.datetimepicker table tr td.new{color:#999}.datetimepicker table tr td.disabled,.datetimepicker table tr td.disabled:hover{background:0;color:#999;cursor:default}.datetimepicker table tr td.today,.datetimepicker table tr td.today:hover,.datetimepicker table tr td.today.disabled,.datetimepicker table tr td.today.disabled:hover{background-color:#fde19a;background-image:-moz-linear-gradient(top,#fdd49a,#fdf59a);background-image:-ms-linear-gradient(top,#fdd49a,#fdf59a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdd49a),to(#fdf59a));background-image:-webkit-linear-gradient(top,#fdd49a,#fdf59a);background-image:-o-linear-gradient(top,#fdd49a,#fdf59a);background-image:linear-gradient(to bottom,#fdd49a,#fdf59a);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a',endColorstr='#fdf59a',GradientType=0);border-color:#fdf59a #fdf59a #fbed50;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.datetimepicker table tr td.today:hover,.datetimepicker table tr td.today:hover:hover,.datetimepicker table tr td.today.disabled:hover,.datetimepicker table tr td.today.disabled:hover:hover,.datetimepicker table tr td.today:active,.datetimepicker table tr td.today:hover:active,.datetimepicker table tr td.today.disabled:active,.datetimepicker table tr td.today.disabled:hover:active,.datetimepicker table tr td.today.active,.datetimepicker table tr td.today:hover.active,.datetimepicker table tr td.today.disabled.active,.datetimepicker table tr td.today.disabled:hover.active,.datetimepicker table tr td.today.disabled,.datetimepicker table tr td.today:hover.disabled,.datetimepicker table tr td.today.disabled.disabled,.datetimepicker table tr td.today.disabled:hover.disabled,.datetimepicker table tr td.today[disabled],.datetimepicker table tr td.today:hover[disabled],.datetimepicker table tr td.today.disabled[disabled],.datetimepicker table tr td.today.disabled:hover[disabled]{background-color:#fdf59a}.datetimepicker table tr td.today:active,.datetimepicker table tr td.today:hover:active,.datetimepicker table tr td.today.disabled:active,.datetimepicker table tr td.today.disabled:hover:active,.datetimepicker table tr td.today.active,.datetimepicker table tr td.today:hover.active,.datetimepicker table tr td.today.disabled.active,.datetimepicker table tr td.today.disabled:hover.active{background-color:#fbf069}.datetimepicker table tr td.active,.datetimepicker table tr td.active:hover,.datetimepicker table tr td.active.disabled,.datetimepicker table tr td.active.disabled:hover{background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-ms-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc',endColorstr='#0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.datetimepicker table tr td.active:hover,.datetimepicker table tr td.active:hover:hover,.datetimepicker table tr td.active.disabled:hover,.datetimepicker table tr td.active.disabled:hover:hover,.datetimepicker table tr td.active:active,.datetimepicker table tr td.active:hover:active,.datetimepicker table tr td.active.disabled:active,.datetimepicker table tr td.active.disabled:hover:active,.datetimepicker table tr td.active.active,.datetimepicker table tr td.active:hover.active,.datetimepicker table tr td.active.disabled.active,.datetimepicker table tr td.active.disabled:hover.active,.datetimepicker table tr td.active.disabled,.datetimepicker table tr td.active:hover.disabled,.datetimepicker table tr td.active.disabled.disabled,.datetimepicker table tr td.active.disabled:hover.disabled,.datetimepicker table tr td.active[disabled],.datetimepicker table tr td.active:hover[disabled],.datetimepicker table tr td.active.disabled[disabled],.datetimepicker table tr td.active.disabled:hover[disabled]{background-color:#04c}.datetimepicker table tr td.active:active,.datetimepicker table tr td.active:hover:active,.datetimepicker table tr td.active.disabled:active,.datetimepicker table tr td.active.disabled:hover:active,.datetimepicker table tr td.active.active,.datetimepicker table tr td.active:hover.active,.datetimepicker table tr td.active.disabled.active,.datetimepicker table tr td.active.disabled:hover.active{background-color:#039}.datetimepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.datetimepicker .datetimepicker-hours span{height:26px;line-height:26px}.datetimepicker .datetimepicker-hours table tr td span.hour_am,.datetimepicker .datetimepicker-hours table tr td span.hour_pm{width:14.6%}.datetimepicker .datetimepicker-hours fieldset legend,.datetimepicker .datetimepicker-minutes fieldset legend{margin-bottom:inherit;line-height:30px}.datetimepicker .datetimepicker-minutes span{height:26px;line-height:26px}.datetimepicker table tr td span:hover{background:#eee}.datetimepicker table tr td span.disabled,.datetimepicker table tr td span.disabled:hover{background:0;color:#999;cursor:default}.datetimepicker table tr td span.active,.datetimepicker table tr td span.active:hover,.datetimepicker table tr td span.active.disabled,.datetimepicker table tr td span.active.disabled:hover{background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-ms-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc',endColorstr='#0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.datetimepicker table tr td span.active:hover,.datetimepicker table tr td span.active:hover:hover,.datetimepicker table tr td span.active.disabled:hover,.datetimepicker table tr td span.active.disabled:hover:hover,.datetimepicker table tr td span.active:active,.datetimepicker table tr td span.active:hover:active,.datetimepicker table tr td span.active.disabled:active,.datetimepicker table tr td span.active.disabled:hover:active,.datetimepicker table tr td span.active.active,.datetimepicker table tr td span.active:hover.active,.datetimepicker table tr td span.active.disabled.active,.datetimepicker table tr td span.active.disabled:hover.active,.datetimepicker table tr td span.active.disabled,.datetimepicker table tr td span.active:hover.disabled,.datetimepicker table tr td span.active.disabled.disabled,.datetimepicker table tr td span.active.disabled:hover.disabled,.datetimepicker table tr td span.active[disabled],.datetimepicker table tr td span.active:hover[disabled],.datetimepicker table tr td span.active.disabled[disabled],.datetimepicker table tr td span.active.disabled:hover[disabled]{background-color:#04c}.datetimepicker table tr td span.active:active,.datetimepicker table tr td span.active:hover:active,.datetimepicker table tr td span.active.disabled:active,.datetimepicker table tr td span.active.disabled:hover:active,.datetimepicker table tr td span.active.active,.datetimepicker table tr td span.active:hover.active,.datetimepicker table tr td span.active.disabled.active,.datetimepicker table tr td span.active.disabled:hover.active{background-color:#039}.datetimepicker table tr td span.old{color:#999}.datetimepicker th.switch{width:145px}.datetimepicker th span.glyphicon{pointer-events:none}.datetimepicker thead tr:first-child th,.datetimepicker tfoot th{cursor:pointer}.datetimepicker thead tr:first-child th:hover,.datetimepicker tfoot th:hover{background:#eee}.input-append.date .add-on i,.input-prepend.date .add-on i,.input-group.date .input-group-addon span{cursor:pointer;width:14px;height:14px}
\ No newline at end of file
diff --git a/src/main/webapp/static/imgs/default.png b/src/main/webapp/static/imgs/default.png
new file mode 100644
index 0000000..17410cb
Binary files /dev/null and b/src/main/webapp/static/imgs/default.png differ
diff --git a/src/main/webapp/static/imgs/login.jpg b/src/main/webapp/static/imgs/login.jpg
new file mode 100644
index 0000000..8e2ede8
Binary files /dev/null and b/src/main/webapp/static/imgs/login.jpg differ
diff --git a/src/main/webapp/static/js/common-css.js b/src/main/webapp/static/js/common-css.js
new file mode 100644
index 0000000..5571fdf
--- /dev/null
+++ b/src/main/webapp/static/js/common-css.js
@@ -0,0 +1,13 @@
+/* 移动端设置 */
+document.write(' ');
+document.write(' ');
+document.write(' ');
+/* 移动端兼容 */
+document.write(' ');
+/* 搜索引擎优化 */
+document.write(' ');
+document.write(' ');
+
+document.write(' ');
+/* 最新版本的 Bootstrap 核心 CSS 文件 */
+document.write(' ');
diff --git a/src/main/webapp/static/js/common-js.js b/src/main/webapp/static/js/common-js.js
new file mode 100644
index 0000000..34cf899
--- /dev/null
+++ b/src/main/webapp/static/js/common-js.js
@@ -0,0 +1,43 @@
+/* bootstrap */
+document.write('');
+document.write('');
+document.write('');
+
+// input浮点数处理
+function clearNoNum(obj){
+ obj.value = obj.value.replace(/[^\d.]/g,""); //清除“数字”和“.”以外的字符
+ obj.value = obj.value.replace(/\.{2,}/g,"."); //只保留第一个. 清除多余的
+ obj.value = obj.value.replace(".","$#$").replace(/\./g,"").replace("$#$",".");
+ obj.value = obj.value.replace(/^(\-)*(\d+)\.(\d).*$/,'$1$2.$3');//只能输入两个小数
+ //如果没有小数点,不能为类似 01、02的金额
+ if(obj.value.indexOf(".")< 0 && obj.value !=""){
+ obj.value= parseFloat(obj.value);
+ }
+ //如果有小数点,不能为类似 1.10的金额
+ if(obj.value.indexOf(".")> 0 && obj.value.indexOf("0")>2){
+ obj.value= parseFloat(obj.value);
+ }
+ //如果有小数点,不能为类似 0.00的金额
+ if(obj.value.indexOf(".")> 0 && obj.value.lastIndexOf("0")>1){
+ obj.value= parseFloat(obj.value);
+ }
+}
+function dateFormat(fmt, date) {
+ let ret;
+ const opt = {
+ "Y+": date.getFullYear().toString(), // 年
+ "m+": (date.getMonth() + 1).toString(), // 月
+ "d+": date.getDate().toString(), // 日
+ "H+": date.getHours().toString(), // 时
+ "M+": date.getMinutes().toString(), // 分
+ "S+": date.getSeconds().toString() // 秒
+ // 有其他格式化字符需求可以继续添加,必须转化成字符串
+ };
+ for (let k in opt) {
+ ret = new RegExp("(" + k + ")").exec(fmt);
+ if (ret) {
+ fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
+ };
+ };
+ return fmt;
+}
\ No newline at end of file
diff --git a/src/main/webapp/static/js/login.js b/src/main/webapp/static/js/login.js
new file mode 100644
index 0000000..835bbd4
--- /dev/null
+++ b/src/main/webapp/static/js/login.js
@@ -0,0 +1,175 @@
+$(document).ready(function () {
+
+ 'use strict';
+
+ // ------------------------------------------------------- //
+ // Search Box
+ // ------------------------------------------------------ //
+ $('#search').on('click', function (e) {
+ e.preventDefault();
+ $('.search-box').fadeIn();
+ });
+ $('.dismiss').on('click', function () {
+ $('.search-box').fadeOut();
+ });
+
+ // ------------------------------------------------------- //
+ // Card Close
+ // ------------------------------------------------------ //
+ $('.card-close a.remove').on('click', function (e) {
+ e.preventDefault();
+ $(this).parents('.card').fadeOut();
+ });
+
+ // ------------------------------------------------------- //
+ // Tooltips init
+ // ------------------------------------------------------ //
+
+ $('[data-toggle="tooltip"]').tooltip()
+
+
+ // ------------------------------------------------------- //
+ // Adding fade effect to dropdowns
+ // ------------------------------------------------------ //
+ $('.dropdown').on('show.bs.dropdown', function () {
+ $(this).find('.dropdown-menu').first().stop(true, true).fadeIn();
+ });
+ $('.dropdown').on('hide.bs.dropdown', function () {
+ $(this).find('.dropdown-menu').first().stop(true, true).fadeOut();
+ });
+
+
+ // ------------------------------------------------------- //
+ // Sidebar Functionality
+ // ------------------------------------------------------ //
+ $('#toggle-btn').on('click', function (e) {
+ e.preventDefault();
+ $(this).toggleClass('active');
+
+ $('.side-navbar').toggleClass('shrinked');
+ $('.content-inner').toggleClass('active');
+ $(document).trigger('sidebarChanged');
+
+ if ($(window).outerWidth() > 1183) {
+ if ($('#toggle-btn').hasClass('active')) {
+ $('.navbar-header .brand-small').hide();
+ $('.navbar-header .brand-big').show();
+ } else {
+ $('.navbar-header .brand-small').show();
+ $('.navbar-header .brand-big').hide();
+ }
+ }
+
+ if ($(window).outerWidth() < 1183) {
+ $('.navbar-header .brand-small').show();
+ }
+ });
+
+ // ------------------------------------------------------- //
+ // Universal Form Validation
+ // ------------------------------------------------------ //
+
+ $('.form-validate').each(function() {
+ $(this).validate({
+ errorElement: "div",
+ errorClass: 'is-invalid',
+ validClass: 'is-valid',
+ ignore: ':hidden:not(.summernote, .checkbox-template, .form-control-custom),.note-editable.card-block',
+ errorPlacement: function (error, element) {
+ // Add the `invalid-feedback` class to the error element
+ error.addClass("invalid-feedback");
+ if (element.prop("type") === "checkbox") {
+ error.insertAfter(element.siblings("label"));
+ }
+ else {
+ error.insertAfter(element);
+ }
+ }
+ });
+
+ });
+
+ // ------------------------------------------------------- //
+ // Material Inputs
+ // ------------------------------------------------------ //
+
+ var materialInputs = $('input.input-material');
+
+ // activate labels for prefilled values
+ materialInputs.filter(function() { return $(this).val() !== ""; }).siblings('.label-material').addClass('active');
+
+ // move label on focus
+ materialInputs.on('focus', function () {
+ $(this).siblings('.label-material').addClass('active');
+ });
+
+ // remove/keep label on blur
+ materialInputs.on('blur', function () {
+ $(this).siblings('.label-material').removeClass('active');
+
+ if ($(this).val() !== '') {
+ $(this).siblings('.label-material').addClass('active');
+ } else {
+ $(this).siblings('.label-material').removeClass('active');
+ }
+ });
+
+ // ------------------------------------------------------- //
+ // Footer
+ // ------------------------------------------------------ //
+
+ var contentInner = $('.content-inner');
+
+ $(document).on('sidebarChanged', function () {
+ adjustFooter();
+ });
+
+ $(window).on('resize', function () {
+ adjustFooter();
+ })
+
+ function adjustFooter() {
+ var footerBlockHeight = $('.main-footer').outerHeight();
+ contentInner.css('padding-bottom', footerBlockHeight + 'px');
+ }
+
+ // ------------------------------------------------------- //
+ // External links to new window
+ // ------------------------------------------------------ //
+ $('.external').on('click', function (e) {
+
+ e.preventDefault();
+ window.open($(this).attr("href"));
+ });
+
+ // ------------------------------------------------------ //
+ // For demo purposes, can be deleted
+ // ------------------------------------------------------ //
+
+ var stylesheet = $('link#theme-stylesheet');
+ $(" ").insertAfter(stylesheet);
+ var alternateColour = $('link#new-stylesheet');
+
+ if ($.cookie("theme_csspath")) {
+ alternateColour.attr("href", $.cookie("theme_csspath"));
+ }
+
+ $("#colour").change(function () {
+
+ if ($(this).val() !== '') {
+
+ var theme_csspath = 'css/style.' + $(this).val() + '.css';
+
+ alternateColour.attr("href", theme_csspath);
+
+ $.cookie("theme_csspath", theme_csspath, {
+ expires: 365,
+ path: document.URL.substr(0, document.URL.lastIndexOf('/'))
+ });
+
+ }
+
+ return false;
+ });
+
+});
\ No newline at end of file
diff --git a/src/main/webapp/static/js/util/bootstrap-datetimepicker.min.js b/src/main/webapp/static/js/util/bootstrap-datetimepicker.min.js
new file mode 100644
index 0000000..eba15ce
--- /dev/null
+++ b/src/main/webapp/static/js/util/bootstrap-datetimepicker.min.js
@@ -0,0 +1 @@
+(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{if(typeof exports==="object"){a(require("jquery"))}else{a(jQuery)}}}(function(d,f){if(!("indexOf" in Array.prototype)){Array.prototype.indexOf=function(k,j){if(j===f){j=0}if(j<0){j+=this.length}if(j<0){j=0}for(var l=this.length;jthis.endDate){o.push("disabled")}else{if(Math.floor(this.date.getUTCMinutes()/this.minuteStep)===Math.floor(n.getUTCMinutes()/this.minuteStep)){o.push("active")}}return o.concat((p?p:[]))};this.onRenderYear=function(o){var q=(j.onRenderYear||function(){return[]})(o);var p=["year"];if(typeof q==="string"){q=[q]}if(this.date.getUTCFullYear()===o.getUTCFullYear()){p.push("active")}var n=o.getUTCFullYear();var r=this.endDate.getUTCFullYear();if(or){p.push("disabled")}return p.concat((q?q:[]))};this.onRenderMonth=function(n){var p=(j.onRenderMonth||function(){return[]})(n);var o=["month"];if(typeof p==="string"){p=[p]}return o.concat((p?p:[]))};this.startDate=new Date(-8639968443048000);this.endDate=new Date(8639968443048000);this.datesDisabled=[];this.daysOfWeekDisabled=[];this.setStartDate(j.startDate||this.element.data("date-startdate"));this.setEndDate(j.endDate||this.element.data("date-enddate"));this.setDatesDisabled(j.datesDisabled||this.element.data("date-dates-disabled"));this.setDaysOfWeekDisabled(j.daysOfWeekDisabled||this.element.data("date-days-of-week-disabled"));this.setMinutesDisabled(j.minutesDisabled||this.element.data("date-minute-disabled"));this.setHoursDisabled(j.hoursDisabled||this.element.data("date-hour-disabled"));this.fillDow();this.fillMonths();this.update();this.showMode();if(this.isInline){this.show()}};g.prototype={constructor:g,_events:[],_attachEvents:function(){this._detachEvents();if(this.isInput){this._events=[[this.element,{focus:d.proxy(this.show,this),keyup:d.proxy(this.update,this),keydown:d.proxy(this.keydown,this)}]]}else{if(this.component&&this.hasInput){this._events=[[this.element.find("input"),{focus:d.proxy(this.show,this),keyup:d.proxy(this.update,this),keydown:d.proxy(this.keydown,this)}],[this.component,{click:d.proxy(this.show,this)}]];if(this.componentReset){this._events.push([this.componentReset,{click:d.proxy(this.reset,this)}])}}else{if(this.element.is("div")){this.isInline=true}else{this._events=[[this.element,{click:d.proxy(this.show,this)}]]}}}for(var j=0,k,l;j=this.startDate&&i<=this.endDate){this.date=i;this.setValue();this.viewDate=this.date;this.fill()}else{this.element.trigger({type:"outOfRange",date:i,startDate:this.startDate,endDate:this.endDate})}},setFormat:function(j){this.format=c.parseFormat(j,this.formatType);var i;if(this.isInput){i=this.element}else{if(this.component){i=this.element.find("input")}}if(i&&i.val()){this.setValue()}},setValue:function(){var i=this.getFormattedDate();if(!this.isInput){if(this.component){this.element.find("input").val(i)}this.element.data("date",i)}else{this.element.val(i)}if(this.linkField){d("#"+this.linkField).val(this.getFormattedDate(this.linkFormat))}},getFormattedDate:function(i){i=i||this.format;return c.formatDate(this.date,i,this.language,this.formatType,this.timezone)},setStartDate:function(i){this.startDate=i||this.startDate;if(this.startDate.valueOf()!==8639968443048000){this.startDate=c.parseDate(this.startDate,this.format,this.language,this.formatType,this.timezone)}this.update();this.updateNavArrows()},setEndDate:function(i){this.endDate=i||this.endDate;if(this.endDate.valueOf()!==8639968443048000){this.endDate=c.parseDate(this.endDate,this.format,this.language,this.formatType,this.timezone)}this.update();this.updateNavArrows()},setDatesDisabled:function(j){this.datesDisabled=j||[];if(!d.isArray(this.datesDisabled)){this.datesDisabled=this.datesDisabled.split(/,\s*/)}var i=this;this.datesDisabled=d.map(this.datesDisabled,function(k){return c.parseDate(k,i.format,i.language,i.formatType,i.timezone).toDateString()});this.update();this.updateNavArrows()},setTitle:function(i,j){return this.picker.find(i).find("th:eq(1)").text(this.title===false?j:this.title)},setDaysOfWeekDisabled:function(i){this.daysOfWeekDisabled=i||[];if(!d.isArray(this.daysOfWeekDisabled)){this.daysOfWeekDisabled=this.daysOfWeekDisabled.split(/,\s*/)}this.daysOfWeekDisabled=d.map(this.daysOfWeekDisabled,function(j){return parseInt(j,10)});this.update();this.updateNavArrows()},setMinutesDisabled:function(i){this.minutesDisabled=i||[];if(!d.isArray(this.minutesDisabled)){this.minutesDisabled=this.minutesDisabled.split(/,\s*/)}this.minutesDisabled=d.map(this.minutesDisabled,function(j){return parseInt(j,10)});this.update();this.updateNavArrows()},setHoursDisabled:function(i){this.hoursDisabled=i||[];if(!d.isArray(this.hoursDisabled)){this.hoursDisabled=this.hoursDisabled.split(/,\s*/)}this.hoursDisabled=d.map(this.hoursDisabled,function(j){return parseInt(j,10)});this.update();this.updateNavArrows()},place:function(){if(this.isInline){return}if(!this.zIndex){var j=0;d("div").each(function(){var o=parseInt(d(this).css("zIndex"),10);if(o>j){j=o}});this.zIndex=j+10}var n,m,l,k;if(this.container instanceof d){k=this.container.offset()}else{k=d(this.container).offset()}if(this.component){n=this.component.offset();l=n.left;if(this.pickerPosition==="bottom-left"||this.pickerPosition==="top-left"){l+=this.component.outerWidth()-this.picker.outerWidth()}}else{n=this.element.offset();l=n.left;if(this.pickerPosition==="bottom-left"||this.pickerPosition==="top-left"){l+=this.element.outerWidth()-this.picker.outerWidth()}}var i=document.body.clientWidth||window.innerWidth;if(l+220>i){l=i-220}if(this.pickerPosition==="top-left"||this.pickerPosition==="top-right"){m=n.top-this.picker.outerHeight()}else{m=n.top+this.height}m=m-k.top;l=l-k.left;this.picker.css({top:m,left:l,zIndex:this.zIndex})},hour_minute:"^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]",update:function(){var i,j=false;if(arguments&&arguments.length&&(typeof arguments[0]==="string"||arguments[0] instanceof Date)){i=arguments[0];j=true}else{i=(this.isInput?this.element.val():this.element.find("input").val())||this.element.data("date")||this.initialDate;if(typeof i==="string"){i=i.replace(/^\s+|\s+$/g,"")}}if(!i){i=new Date();j=false}if(typeof i==="string"){if(new RegExp(this.hour_minute).test(i)||new RegExp(this.hour_minute+":[0-5][0-9]").test(i)){i=this.getDate()}}this.date=c.parseDate(i,this.format,this.language,this.formatType,this.timezone);if(j){this.setValue()}if(this.datethis.endDate){this.viewDate=new Date(this.endDate)}else{this.viewDate=new Date(this.date)}}this.fill()},fillDow:function(){var i=this.weekStart,j="";while(i'+e[this.language].daysMin[(i++)%7]+""}j+=" ";this.picker.find(".datetimepicker-days thead").append(j)},fillMonths:function(){var l="";var m=new Date(this.viewDate);for(var k=0;k<12;k++){m.setUTCMonth(k);var j=this.onRenderMonth(m);l+=''+e[this.language].monthsShort[k]+" "}this.picker.find(".datetimepicker-months td").html(l)},fill:function(){if(!this.date||!this.viewDate){return}var E=new Date(this.viewDate),t=E.getUTCFullYear(),G=E.getUTCMonth(),n=E.getUTCDate(),A=E.getUTCHours(),w=this.startDate.getUTCFullYear(),B=this.startDate.getUTCMonth(),p=this.endDate.getUTCFullYear(),x=this.endDate.getUTCMonth()+1,q=(new h(this.date.getUTCFullYear(),this.date.getUTCMonth(),this.date.getUTCDate())).valueOf(),D=new Date();this.setTitle(".datetimepicker-days",e[this.language].months[G]+" "+t);if(this.formatViewType==="time"){var k=this.getFormattedDate();this.setTitle(".datetimepicker-hours",k);this.setTitle(".datetimepicker-minutes",k)}else{this.setTitle(".datetimepicker-hours",n+" "+e[this.language].months[G]+" "+t);this.setTitle(".datetimepicker-minutes",n+" "+e[this.language].months[G]+" "+t)}this.picker.find("tfoot th.today").text(e[this.language].today||e.en.today).toggle(this.todayBtn!==false);this.picker.find("tfoot th.clear").text(e[this.language].clear||e.en.clear).toggle(this.clearBtn!==false);this.updateNavArrows();this.fillMonths();var I=h(t,G-1,28,0,0,0,0),z=c.getDaysInMonth(I.getUTCFullYear(),I.getUTCMonth());I.setUTCDate(z);I.setUTCDate(z-(I.getUTCDay()-this.weekStart+7)%7);var j=new Date(I);j.setUTCDate(j.getUTCDate()+42);j=j.valueOf();var r=[];var F;while(I.valueOf()")}F=this.onRenderDay(I);if(I.getUTCFullYear()t||(I.getUTCFullYear()===t&&I.getUTCMonth()>G)){F.push("new")}}if(this.todayHighlight&&I.getUTCFullYear()===D.getFullYear()&&I.getUTCMonth()===D.getMonth()&&I.getUTCDate()===D.getDate()){F.push("today")}if(I.valueOf()===q){F.push("active")}if((I.valueOf()+86400000)<=this.startDate||I.valueOf()>this.endDate||d.inArray(I.getUTCDay(),this.daysOfWeekDisabled)!==-1||d.inArray(I.toDateString(),this.datesDisabled)!==-1){F.push("disabled")}r.push(''+I.getUTCDate()+" ");if(I.getUTCDay()===this.weekEnd){r.push("")}I.setUTCDate(I.getUTCDate()+1)}this.picker.find(".datetimepicker-days tbody").empty().append(r.join(""));r=[];var u="",C="",s="";var l=this.hoursDisabled||[];E=new Date(this.viewDate);for(var y=0;y<24;y++){E.setUTCHours(y);F=this.onRenderHour(E);if(l.indexOf(y)!==-1){F.push("disabled")}var v=h(t,G,n,y);if((v.valueOf()+3600000)<=this.startDate||v.valueOf()>this.endDate){F.push("disabled")}else{if(A===y){F.push("active")}}if(this.showMeridian&&e[this.language].meridiem.length===2){C=(y<12?e[this.language].meridiem[0]:e[this.language].meridiem[1]);if(C!==s){if(s!==""){r.push("")}r.push(''+C.toUpperCase()+" ")}s=C;u=(y%12?y%12:12);if(y<12){F.push("hour_am")}else{F.push("hour_pm")}r.push(''+u+" ");if(y===23){r.push(" ")}}else{u=y+":00";r.push(''+u+" ")}}this.picker.find(".datetimepicker-hours td").html(r.join(""));r=[];u="";C="";s="";var m=this.minutesDisabled||[];E=new Date(this.viewDate);for(var y=0;y<60;y+=this.minuteStep){if(m.indexOf(y)!==-1){continue}E.setUTCMinutes(y);E.setUTCSeconds(0);F=this.onRenderMinute(E);if(this.showMeridian&&e[this.language].meridiem.length===2){C=(A<12?e[this.language].meridiem[0]:e[this.language].meridiem[1]);if(C!==s){if(s!==""){r.push("")}r.push(''+C.toUpperCase()+" ")}s=C;u=(A%12?A%12:12);r.push(''+u+":"+(y<10?"0"+y:y)+" ");if(y===59){r.push(" ")}}else{u=y+":00";r.push(''+A+":"+(y<10?"0"+y:y)+" ")}}this.picker.find(".datetimepicker-minutes td").html(r.join(""));var J=this.date.getUTCFullYear();var o=this.setTitle(".datetimepicker-months",t).end().find(".month").removeClass("active");if(J===t){o.eq(this.date.getUTCMonth()).addClass("active")}if(tp){o.addClass("disabled")}if(t===w){o.slice(0,B).addClass("disabled")}if(t===p){o.slice(x).addClass("disabled")}r="";t=parseInt(t/10,10)*10;var H=this.setTitle(".datetimepicker-years",t+"-"+(t+9)).end().find("td");t-=1;E=new Date(this.viewDate);for(var y=-1;y<11;y++){E.setUTCFullYear(t);F=this.onRenderYear(E);if(y===-1||y===10){F.push(b)}r+=''+t+" ";t+=1}H.html(r);this.place()},updateNavArrows:function(){var m=new Date(this.viewDate),k=m.getUTCFullYear(),l=m.getUTCMonth(),j=m.getUTCDate(),i=m.getUTCHours();switch(this.viewMode){case 0:if(k<=this.startDate.getUTCFullYear()&&l<=this.startDate.getUTCMonth()&&j<=this.startDate.getUTCDate()&&i<=this.startDate.getUTCHours()){this.picker.find(".prev").css({visibility:"hidden"})}else{this.picker.find(".prev").css({visibility:"visible"})}if(k>=this.endDate.getUTCFullYear()&&l>=this.endDate.getUTCMonth()&&j>=this.endDate.getUTCDate()&&i>=this.endDate.getUTCHours()){this.picker.find(".next").css({visibility:"hidden"})}else{this.picker.find(".next").css({visibility:"visible"})}break;case 1:if(k<=this.startDate.getUTCFullYear()&&l<=this.startDate.getUTCMonth()&&j<=this.startDate.getUTCDate()){this.picker.find(".prev").css({visibility:"hidden"})}else{this.picker.find(".prev").css({visibility:"visible"})}if(k>=this.endDate.getUTCFullYear()&&l>=this.endDate.getUTCMonth()&&j>=this.endDate.getUTCDate()){this.picker.find(".next").css({visibility:"hidden"})}else{this.picker.find(".next").css({visibility:"visible"})}break;case 2:if(k<=this.startDate.getUTCFullYear()&&l<=this.startDate.getUTCMonth()){this.picker.find(".prev").css({visibility:"hidden"})}else{this.picker.find(".prev").css({visibility:"visible"})}if(k>=this.endDate.getUTCFullYear()&&l>=this.endDate.getUTCMonth()){this.picker.find(".next").css({visibility:"hidden"})}else{this.picker.find(".next").css({visibility:"visible"})}break;case 3:case 4:if(k<=this.startDate.getUTCFullYear()){this.picker.find(".prev").css({visibility:"hidden"})}else{this.picker.find(".prev").css({visibility:"visible"})}if(k>=this.endDate.getUTCFullYear()){this.picker.find(".next").css({visibility:"hidden"})}else{this.picker.find(".next").css({visibility:"visible"})}break}},mousewheel:function(j){j.preventDefault();j.stopPropagation();if(this.wheelPause){return}this.wheelPause=true;var i=j.originalEvent;var l=i.wheelDelta;var k=l>0?1:(l===0)?0:-1;if(this.wheelViewModeNavigationInverseDirection){k=-k}this.showMode(k);setTimeout(d.proxy(function(){this.wheelPause=false},this),this.wheelViewModeNavigationDelay)},click:function(m){m.stopPropagation();m.preventDefault();var n=d(m.target).closest("span, td, th, legend");if(n.is("."+this.icontype)){n=d(n).parent().closest("span, td, th, legend")}if(n.length===1){if(n.is(".disabled")){this.element.trigger({type:"outOfRange",date:this.viewDate,startDate:this.startDate,endDate:this.endDate});return}switch(n[0].nodeName.toLowerCase()){case"th":switch(n[0].className){case"switch":this.showMode(1);break;case"prev":case"next":var i=c.modes[this.viewMode].navStep*(n[0].className==="prev"?-1:1);switch(this.viewMode){case 0:this.viewDate=this.moveHour(this.viewDate,i);break;case 1:this.viewDate=this.moveDate(this.viewDate,i);break;case 2:this.viewDate=this.moveMonth(this.viewDate,i);break;case 3:case 4:this.viewDate=this.moveYear(this.viewDate,i);break}this.fill();this.element.trigger({type:n[0].className+":"+this.convertViewModeText(this.viewMode),date:this.viewDate,startDate:this.startDate,endDate:this.endDate});break;case"clear":this.reset();if(this.autoclose){this.hide()}break;case"today":var j=new Date();j=h(j.getFullYear(),j.getMonth(),j.getDate(),j.getHours(),j.getMinutes(),j.getSeconds(),0);if(jthis.endDate){j=this.endDate}}this.viewMode=this.startViewMode;this.showMode(0);this._setDate(j);this.fill();if(this.autoclose){this.hide()}break}break;case"span":if(!n.is(".disabled")){var p=this.viewDate.getUTCFullYear(),o=this.viewDate.getUTCMonth(),q=this.viewDate.getUTCDate(),r=this.viewDate.getUTCHours(),k=this.viewDate.getUTCMinutes(),s=this.viewDate.getUTCSeconds();if(n.is(".month")){this.viewDate.setUTCDate(1);o=n.parent().find("span").index(n);q=this.viewDate.getUTCDate();this.viewDate.setUTCMonth(o);this.element.trigger({type:"changeMonth",date:this.viewDate});if(this.viewSelect>=3){this._setDate(h(p,o,q,r,k,s,0))}}else{if(n.is(".year")){this.viewDate.setUTCDate(1);p=parseInt(n.text(),10)||0;this.viewDate.setUTCFullYear(p);this.element.trigger({type:"changeYear",date:this.viewDate});if(this.viewSelect>=4){this._setDate(h(p,o,q,r,k,s,0))}}else{if(n.is(".hour")){r=parseInt(n.text(),10)||0;if(n.hasClass("hour_am")||n.hasClass("hour_pm")){if(r===12&&n.hasClass("hour_am")){r=0}else{if(r!==12&&n.hasClass("hour_pm")){r+=12}}}this.viewDate.setUTCHours(r);this.element.trigger({type:"changeHour",date:this.viewDate});if(this.viewSelect>=1){this._setDate(h(p,o,q,r,k,s,0))}}else{if(n.is(".minute")){k=parseInt(n.text().substr(n.text().indexOf(":")+1),10)||0;this.viewDate.setUTCMinutes(k);this.element.trigger({type:"changeMinute",date:this.viewDate});if(this.viewSelect>=0){this._setDate(h(p,o,q,r,k,s,0))}}}}}if(this.viewMode!==0){var l=this.viewMode;this.showMode(-1);this.fill();if(l===this.viewMode&&this.autoclose){this.hide()}}else{this.fill();if(this.autoclose){this.hide()}}}break;case"td":if(n.is(".day")&&!n.is(".disabled")){var q=parseInt(n.text(),10)||1;var p=this.viewDate.getUTCFullYear(),o=this.viewDate.getUTCMonth(),r=this.viewDate.getUTCHours(),k=this.viewDate.getUTCMinutes(),s=this.viewDate.getUTCSeconds();if(n.is(".old")){if(o===0){o=11;p-=1}else{o-=1}}else{if(n.is(".new")){if(o===11){o=0;p+=1}else{o+=1}}}this.viewDate.setUTCFullYear(p);this.viewDate.setUTCMonth(o,q);this.element.trigger({type:"changeDay",date:this.viewDate});if(this.viewSelect>=2){this._setDate(h(p,o,q,r,k,s,0))}}var l=this.viewMode;this.showMode(-1);this.fill();if(l===this.viewMode&&this.autoclose){this.hide()}break}}},_setDate:function(i,k){if(!k||k==="date"){this.date=i}if(!k||k==="view"){this.viewDate=i}this.fill();this.setValue();var j;if(this.isInput){j=this.element}else{if(this.component){j=this.element.find("input")}}if(j){j.change()}this.element.trigger({type:"changeDate",date:this.getDate()});if(i===null){this.date=this.viewDate}},moveMinute:function(j,i){if(!i){return j}var k=new Date(j.valueOf());k.setUTCMinutes(k.getUTCMinutes()+(i*this.minuteStep));return k},moveHour:function(j,i){if(!i){return j}var k=new Date(j.valueOf());k.setUTCHours(k.getUTCHours()+i);return k},moveDate:function(j,i){if(!i){return j}var k=new Date(j.valueOf());k.setUTCDate(k.getUTCDate()+i);return k},moveMonth:function(j,k){if(!k){return j}var n=new Date(j.valueOf()),r=n.getUTCDate(),o=n.getUTCMonth(),m=Math.abs(k),q,p;k=k>0?1:-1;if(m===1){p=k===-1?function(){return n.getUTCMonth()===o}:function(){return n.getUTCMonth()!==q};q=o+k;n.setUTCMonth(q);if(q<0||q>11){q=(q+12)%12}}else{for(var l=0;l=this.startDate&&i<=this.endDate},keydown:function(o){if(this.picker.is(":not(:visible)")){if(o.keyCode===27){this.show()}return}var k=false,j,i,n;switch(o.keyCode){case 27:this.hide();o.preventDefault();break;case 37:case 39:if(!this.keyboardNavigation){break}j=o.keyCode===37?-1:1;var m=this.viewMode;if(o.ctrlKey){m+=2}else{if(o.shiftKey){m+=1}}if(m===4){i=this.moveYear(this.date,j);n=this.moveYear(this.viewDate,j)}else{if(m===3){i=this.moveMonth(this.date,j);n=this.moveMonth(this.viewDate,j)}else{if(m===2){i=this.moveDate(this.date,j);n=this.moveDate(this.viewDate,j)}else{if(m===1){i=this.moveHour(this.date,j);n=this.moveHour(this.viewDate,j)}else{if(m===0){i=this.moveMinute(this.date,j);n=this.moveMinute(this.viewDate,j)}}}}}if(this.dateWithinRange(i)){this.date=i;this.viewDate=n;this.setValue();this.update();o.preventDefault();k=true}break;case 38:case 40:if(!this.keyboardNavigation){break}j=o.keyCode===38?-1:1;m=this.viewMode;if(o.ctrlKey){m+=2}else{if(o.shiftKey){m+=1}}if(m===4){i=this.moveYear(this.date,j);n=this.moveYear(this.viewDate,j)}else{if(m===3){i=this.moveMonth(this.date,j);n=this.moveMonth(this.viewDate,j)}else{if(m===2){i=this.moveDate(this.date,j*7);n=this.moveDate(this.viewDate,j*7)}else{if(m===1){if(this.showMeridian){i=this.moveHour(this.date,j*6);n=this.moveHour(this.viewDate,j*6)}else{i=this.moveHour(this.date,j*4);n=this.moveHour(this.viewDate,j*4)}}else{if(m===0){i=this.moveMinute(this.date,j*4);n=this.moveMinute(this.viewDate,j*4)}}}}}if(this.dateWithinRange(i)){this.date=i;this.viewDate=n;this.setValue();this.update();o.preventDefault();k=true}break;case 13:if(this.viewMode!==0){var p=this.viewMode;this.showMode(-1);this.fill();if(p===this.viewMode&&this.autoclose){this.hide()}}else{this.fill();if(this.autoclose){this.hide()}}o.preventDefault();break;case 9:this.hide();break}if(k){var l;if(this.isInput){l=this.element}else{if(this.component){l=this.element.find("input")}}if(l){l.change()}this.element.trigger({type:"changeDate",date:this.getDate()})}},showMode:function(i){if(i){var j=Math.max(0,Math.min(c.modes.length-1,this.viewMode+i));if(j>=this.minView&&j<=this.maxView){this.element.trigger({type:"changeMode",date:this.viewDate,oldViewMode:this.viewMode,newViewMode:j});this.viewMode=j}}this.picker.find(">div").hide().filter(".datetimepicker-"+c.modes[this.viewMode].clsName).css("display","block");this.updateNavArrows()},reset:function(){this._setDate(null,"date")},convertViewModeText:function(i){switch(i){case 4:return"decade";case 3:return"year";case 2:return"month";case 1:return"day";case 0:return"hour"}}};var b=d.fn.datetimepicker;d.fn.datetimepicker=function(k){var i=Array.apply(null,arguments);i.shift();var j;this.each(function(){var n=d(this),m=n.data("datetimepicker"),l=typeof k==="object"&&k;if(!m){n.data("datetimepicker",(m=new g(this,d.extend({},d.fn.datetimepicker.defaults,l))))}if(typeof k==="string"&&typeof m[k]==="function"){j=m[k].apply(m,i);if(j!==f){return false}}});if(j!==f){return j}else{return this}};d.fn.datetimepicker.defaults={};d.fn.datetimepicker.Constructor=g;var e=d.fn.datetimepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa","Su"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],meridiem:["am","pm"],suffix:["st","nd","rd","th"],today:"Today",clear:"Clear"}};var c={modes:[{clsName:"minutes",navFnc:"Hours",navStep:1},{clsName:"hours",navFnc:"Date",navStep:1},{clsName:"days",navFnc:"Month",navStep:1},{clsName:"months",navFnc:"FullYear",navStep:1},{clsName:"years",navFnc:"FullYear",navStep:10}],isLeapYear:function(i){return(((i%4===0)&&(i%100!==0))||(i%400===0))},getDaysInMonth:function(i,j){return[31,(c.isLeapYear(i)?29:28),31,30,31,30,31,31,30,31,30,31][j]},getDefaultFormat:function(i,j){if(i==="standard"){if(j==="input"){return"yyyy-mm-dd hh:ii"}else{return"yyyy-mm-dd hh:ii:ss"}}else{if(i==="php"){if(j==="input"){return"Y-m-d H:i"}else{return"Y-m-d H:i:s"}}else{throw new Error("Invalid format type.")}}},validParts:function(i){if(i==="standard"){return/t|hh?|HH?|p|P|z|Z|ii?|ss?|dd?|DD?|mm?|MM?|yy(?:yy)?/g}else{if(i==="php"){return/[dDjlNwzFmMnStyYaABgGhHis]/g}else{throw new Error("Invalid format type.")}}},nonpunctuation:/[^ -\/:-@\[-`{-~\t\n\rTZ]+/g,parseFormat:function(l,j){var i=l.replace(this.validParts(j),"\0").split("\0"),k=l.match(this.validParts(j));if(!i||!i.length||!k||k.length===0){throw new Error("Invalid date format.")}return{separators:i,parts:k}},parseDate:function(A,y,v,j,r){if(A instanceof Date){var u=new Date(A.valueOf()-A.getTimezoneOffset()*60000);u.setMilliseconds(0);return u}if(/^\d{4}\-\d{1,2}\-\d{1,2}$/.test(A)){y=this.parseFormat("yyyy-mm-dd",j)}if(/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}$/.test(A)){y=this.parseFormat("yyyy-mm-dd hh:ii",j)}if(/^\d{4}\-\d{1,2}\-\d{1,2}[T ]\d{1,2}\:\d{1,2}\:\d{1,2}[Z]{0,1}$/.test(A)){y=this.parseFormat("yyyy-mm-dd hh:ii:ss",j)}if(/^[-+]\d+[dmwy]([\s,]+[-+]\d+[dmwy])*$/.test(A)){var l=/([-+]\d+)([dmwy])/,q=A.match(/([-+]\d+)([dmwy])/g),t,p;A=new Date();for(var x=0;x ',headTemplateV3:' ',contTemplate:' ',footTemplate:' '};c.template=''+c.headTemplate+c.contTemplate+c.footTemplate+'
'+c.headTemplate+c.contTemplate+c.footTemplate+'
'+c.headTemplate+" "+c.footTemplate+'
'+c.headTemplate+c.contTemplate+c.footTemplate+'
'+c.headTemplate+c.contTemplate+c.footTemplate+"
";c.templateV3=''+c.headTemplateV3+c.contTemplate+c.footTemplate+'
'+c.headTemplateV3+c.contTemplate+c.footTemplate+'
'+c.headTemplateV3+" "+c.footTemplate+'
'+c.headTemplateV3+c.contTemplate+c.footTemplate+'
'+c.headTemplateV3+c.contTemplate+c.footTemplate+"
";d.fn.datetimepicker.DPGlobal=c;d.fn.datetimepicker.noConflict=function(){d.fn.datetimepicker=b;return this};d(document).on("focus.datetimepicker.data-api click.datetimepicker.data-api",'[data-provide="datetimepicker"]',function(j){var i=d(this);if(i.data("datetimepicker")){return}j.preventDefault();i.datetimepicker("show")});d(function(){d('[data-provide="datetimepicker-inline"]').datetimepicker()})}));
\ No newline at end of file
diff --git a/src/main/webapp/static/js/util/bootstrap-datetimepicker.zh-CN.js b/src/main/webapp/static/js/util/bootstrap-datetimepicker.zh-CN.js
new file mode 100644
index 0000000..418fb30
--- /dev/null
+++ b/src/main/webapp/static/js/util/bootstrap-datetimepicker.zh-CN.js
@@ -0,0 +1,16 @@
+/**
+ * Simplified Chinese translation for bootstrap-datetimepicker
+ * Yuan Cheung
+ */
+;(function($){
+ $.fn.datetimepicker.dates['zh-CN'] = {
+ days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
+ daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
+ daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
+ months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+ monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
+ today: "今天",
+ suffix: [],
+ meridiem: ["上午", "下午"]
+ };
+}(jQuery));
diff --git a/src/main/webapp/static/js/util/jquery.validate.min.js b/src/main/webapp/static/js/util/jquery.validate.min.js
new file mode 100644
index 0000000..20402da
--- /dev/null
+++ b/src/main/webapp/static/js/util/jquery.validate.min.js
@@ -0,0 +1,4 @@
+/*! jQuery Validation Plugin - v1.17.0 - 7/29/2017
+ * https://jqueryvalidation.org/
+ * Copyright (c) 2017 Jörn Zaefferer; Licensed MIT */
+!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){a.extend(a.fn,{validate:function(b){if(!this.length)return void(b&&b.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing."));var c=a.data(this[0],"validator");return c?c:(this.attr("novalidate","novalidate"),c=new a.validator(b,this[0]),a.data(this[0],"validator",c),c.settings.onsubmit&&(this.on("click.validate",":submit",function(b){c.submitButton=b.currentTarget,a(this).hasClass("cancel")&&(c.cancelSubmit=!0),void 0!==a(this).attr("formnovalidate")&&(c.cancelSubmit=!0)}),this.on("submit.validate",function(b){function d(){var d,e;return c.submitButton&&(c.settings.submitHandler||c.formSubmitted)&&(d=a(" ").attr("name",c.submitButton.name).val(a(c.submitButton).val()).appendTo(c.currentForm)),!c.settings.submitHandler||(e=c.settings.submitHandler.call(c,c.currentForm,b),d&&d.remove(),void 0!==e&&e)}return c.settings.debug&&b.preventDefault(),c.cancelSubmit?(c.cancelSubmit=!1,d()):c.form()?c.pendingRequest?(c.formSubmitted=!0,!1):d():(c.focusInvalid(),!1)})),c)},valid:function(){var b,c,d;return a(this[0]).is("form")?b=this.validate().form():(d=[],b=!0,c=a(this[0].form).validate(),this.each(function(){b=c.element(this)&&b,b||(d=d.concat(c.errorList))}),c.errorList=d),b},rules:function(b,c){var d,e,f,g,h,i,j=this[0];if(null!=j&&(!j.form&&j.hasAttribute("contenteditable")&&(j.form=this.closest("form")[0],j.name=this.attr("name")),null!=j.form)){if(b)switch(d=a.data(j.form,"validator").settings,e=d.rules,f=a.validator.staticRules(j),b){case"add":a.extend(f,a.validator.normalizeRule(c)),delete f.messages,e[j.name]=f,c.messages&&(d.messages[j.name]=a.extend(d.messages[j.name],c.messages));break;case"remove":return c?(i={},a.each(c.split(/\s/),function(a,b){i[b]=f[b],delete f[b]}),i):(delete e[j.name],f)}return g=a.validator.normalizeRules(a.extend({},a.validator.classRules(j),a.validator.attributeRules(j),a.validator.dataRules(j),a.validator.staticRules(j)),j),g.required&&(h=g.required,delete g.required,g=a.extend({required:h},g)),g.remote&&(h=g.remote,delete g.remote,g=a.extend(g,{remote:h})),g}}}),a.extend(a.expr.pseudos||a.expr[":"],{blank:function(b){return!a.trim(""+a(b).val())},filled:function(b){var c=a(b).val();return null!==c&&!!a.trim(""+c)},unchecked:function(b){return!a(b).prop("checked")}}),a.validator=function(b,c){this.settings=a.extend(!0,{},a.validator.defaults,b),this.currentForm=c,this.init()},a.validator.format=function(b,c){return 1===arguments.length?function(){var c=a.makeArray(arguments);return c.unshift(b),a.validator.format.apply(this,c)}:void 0===c?b:(arguments.length>2&&c.constructor!==Array&&(c=a.makeArray(arguments).slice(1)),c.constructor!==Array&&(c=[c]),a.each(c,function(a,c){b=b.replace(new RegExp("\\{"+a+"\\}","g"),function(){return c})}),b)},a.extend(a.validator,{defaults:{messages:{},groups:{},rules:{},errorClass:"error",pendingClass:"pending",validClass:"valid",errorElement:"label",focusCleanup:!1,focusInvalid:!0,errorContainer:a([]),errorLabelContainer:a([]),onsubmit:!0,ignore:":hidden",ignoreTitle:!1,onfocusin:function(a){this.lastActive=a,this.settings.focusCleanup&&(this.settings.unhighlight&&this.settings.unhighlight.call(this,a,this.settings.errorClass,this.settings.validClass),this.hideThese(this.errorsFor(a)))},onfocusout:function(a){this.checkable(a)||!(a.name in this.submitted)&&this.optional(a)||this.element(a)},onkeyup:function(b,c){var d=[16,17,18,20,35,36,37,38,39,40,45,144,225];9===c.which&&""===this.elementValue(b)||a.inArray(c.keyCode,d)!==-1||(b.name in this.submitted||b.name in this.invalid)&&this.element(b)},onclick:function(a){a.name in this.submitted?this.element(a):a.parentNode.name in this.submitted&&this.element(a.parentNode)},highlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).addClass(c).removeClass(d):a(b).addClass(c).removeClass(d)},unhighlight:function(b,c,d){"radio"===b.type?this.findByName(b.name).removeClass(c).addClass(d):a(b).removeClass(c).addClass(d)}},setDefaults:function(b){a.extend(a.validator.defaults,b)},messages:{required:"This field is required.",remote:"Please fix this field.",email:"Please enter a valid email address.",url:"Please enter a valid URL.",date:"Please enter a valid date.",dateISO:"Please enter a valid date (ISO).",number:"Please enter a valid number.",digits:"Please enter only digits.",equalTo:"Please enter the same value again.",maxlength:a.validator.format("Please enter no more than {0} characters."),minlength:a.validator.format("Please enter at least {0} characters."),rangelength:a.validator.format("Please enter a value between {0} and {1} characters long."),range:a.validator.format("Please enter a value between {0} and {1}."),max:a.validator.format("Please enter a value less than or equal to {0}."),min:a.validator.format("Please enter a value greater than or equal to {0}."),step:a.validator.format("Please enter a multiple of {0}.")},autoCreateRanges:!1,prototype:{init:function(){function b(b){!this.form&&this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=a(this).attr("name"));var c=a.data(this.form,"validator"),d="on"+b.type.replace(/^validate/,""),e=c.settings;e[d]&&!a(this).is(e.ignore)&&e[d].call(c,this,b)}this.labelContainer=a(this.settings.errorLabelContainer),this.errorContext=this.labelContainer.length&&this.labelContainer||a(this.currentForm),this.containers=a(this.settings.errorContainer).add(this.settings.errorLabelContainer),this.submitted={},this.valueCache={},this.pendingRequest=0,this.pending={},this.invalid={},this.reset();var c,d=this.groups={};a.each(this.settings.groups,function(b,c){"string"==typeof c&&(c=c.split(/\s/)),a.each(c,function(a,c){d[c]=b})}),c=this.settings.rules,a.each(c,function(b,d){c[b]=a.validator.normalizeRule(d)}),a(this.currentForm).on("focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], [type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], [type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], [type='radio'], [type='checkbox'], [contenteditable], [type='button']",b).on("click.validate","select, option, [type='radio'], [type='checkbox']",b),this.settings.invalidHandler&&a(this.currentForm).on("invalid-form.validate",this.settings.invalidHandler)},form:function(){return this.checkForm(),a.extend(this.submitted,this.errorMap),this.invalid=a.extend({},this.errorMap),this.valid()||a(this.currentForm).triggerHandler("invalid-form",[this]),this.showErrors(),this.valid()},checkForm:function(){this.prepareForm();for(var a=0,b=this.currentElements=this.elements();b[a];a++)this.check(b[a]);return this.valid()},element:function(b){var c,d,e=this.clean(b),f=this.validationTargetFor(e),g=this,h=!0;return void 0===f?delete this.invalid[e.name]:(this.prepareElement(f),this.currentElements=a(f),d=this.groups[f.name],d&&a.each(this.groups,function(a,b){b===d&&a!==f.name&&(e=g.validationTargetFor(g.clean(g.findByName(a))),e&&e.name in g.invalid&&(g.currentElements.push(e),h=g.check(e)&&h))}),c=this.check(f)!==!1,h=h&&c,c?this.invalid[f.name]=!1:this.invalid[f.name]=!0,this.numberOfInvalids()||(this.toHide=this.toHide.add(this.containers)),this.showErrors(),a(b).attr("aria-invalid",!c)),h},showErrors:function(b){if(b){var c=this;a.extend(this.errorMap,b),this.errorList=a.map(this.errorMap,function(a,b){return{message:a,element:c.findByName(b)[0]}}),this.successList=a.grep(this.successList,function(a){return!(a.name in b)})}this.settings.showErrors?this.settings.showErrors.call(this,this.errorMap,this.errorList):this.defaultShowErrors()},resetForm:function(){a.fn.resetForm&&a(this.currentForm).resetForm(),this.invalid={},this.submitted={},this.prepareForm(),this.hideErrors();var b=this.elements().removeData("previousValue").removeAttr("aria-invalid");this.resetElements(b)},resetElements:function(a){var b;if(this.settings.unhighlight)for(b=0;a[b];b++)this.settings.unhighlight.call(this,a[b],this.settings.errorClass,""),this.findByName(a[b].name).removeClass(this.settings.validClass);else a.removeClass(this.settings.errorClass).removeClass(this.settings.validClass)},numberOfInvalids:function(){return this.objectLength(this.invalid)},objectLength:function(a){var b,c=0;for(b in a)void 0!==a[b]&&null!==a[b]&&a[b]!==!1&&c++;return c},hideErrors:function(){this.hideThese(this.toHide)},hideThese:function(a){a.not(this.containers).text(""),this.addWrapper(a).hide()},valid:function(){return 0===this.size()},size:function(){return this.errorList.length},focusInvalid:function(){if(this.settings.focusInvalid)try{a(this.findLastActive()||this.errorList.length&&this.errorList[0].element||[]).filter(":visible").focus().trigger("focusin")}catch(b){}},findLastActive:function(){var b=this.lastActive;return b&&1===a.grep(this.errorList,function(a){return a.element.name===b.name}).length&&b},elements:function(){var b=this,c={};return a(this.currentForm).find("input, select, textarea, [contenteditable]").not(":submit, :reset, :image, :disabled").not(this.settings.ignore).filter(function(){var d=this.name||a(this).attr("name");return!d&&b.settings.debug&&window.console&&console.error("%o has no name assigned",this),this.hasAttribute("contenteditable")&&(this.form=a(this).closest("form")[0],this.name=d),!(d in c||!b.objectLength(a(this).rules()))&&(c[d]=!0,!0)})},clean:function(b){return a(b)[0]},errors:function(){var b=this.settings.errorClass.split(" ").join(".");return a(this.settings.errorElement+"."+b,this.errorContext)},resetInternals:function(){this.successList=[],this.errorList=[],this.errorMap={},this.toShow=a([]),this.toHide=a([])},reset:function(){this.resetInternals(),this.currentElements=a([])},prepareForm:function(){this.reset(),this.toHide=this.errors().add(this.containers)},prepareElement:function(a){this.reset(),this.toHide=this.errorsFor(a)},elementValue:function(b){var c,d,e=a(b),f=b.type;return"radio"===f||"checkbox"===f?this.findByName(b.name).filter(":checked").val():"number"===f&&"undefined"!=typeof b.validity?b.validity.badInput?"NaN":e.val():(c=b.hasAttribute("contenteditable")?e.text():e.val(),"file"===f?"C:\\fakepath\\"===c.substr(0,12)?c.substr(12):(d=c.lastIndexOf("/"),d>=0?c.substr(d+1):(d=c.lastIndexOf("\\"),d>=0?c.substr(d+1):c)):"string"==typeof c?c.replace(/\r/g,""):c)},check:function(b){b=this.validationTargetFor(this.clean(b));var c,d,e,f,g=a(b).rules(),h=a.map(g,function(a,b){return b}).length,i=!1,j=this.elementValue(b);if("function"==typeof g.normalizer?f=g.normalizer:"function"==typeof this.settings.normalizer&&(f=this.settings.normalizer),f){if(j=f.call(b,j),"string"!=typeof j)throw new TypeError("The normalizer should return a string value.");delete g.normalizer}for(d in g){e={method:d,parameters:g[d]};try{if(c=a.validator.methods[d].call(this,j,b,e.parameters),"dependency-mismatch"===c&&1===h){i=!0;continue}if(i=!1,"pending"===c)return void(this.toHide=this.toHide.not(this.errorsFor(b)));if(!c)return this.formatAndAdd(b,e),!1}catch(k){throw this.settings.debug&&window.console&&console.log("Exception occurred when checking element "+b.id+", check the '"+e.method+"' method.",k),k instanceof TypeError&&(k.message+=". Exception occurred when checking element "+b.id+", check the '"+e.method+"' method."),k}}if(!i)return this.objectLength(g)&&this.successList.push(b),!0},customDataMessage:function(b,c){return a(b).data("msg"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase())||a(b).data("msg")},customMessage:function(a,b){var c=this.settings.messages[a];return c&&(c.constructor===String?c:c[b])},findDefined:function(){for(var a=0;aWarning: No message defined for "+b.name+""),e=/\$?\{(\d+)\}/g;return"function"==typeof d?d=d.call(this,c.parameters,b):e.test(d)&&(d=a.validator.format(d.replace(e,"{$1}"),c.parameters)),d},formatAndAdd:function(a,b){var c=this.defaultMessage(a,b);this.errorList.push({message:c,element:a,method:b.method}),this.errorMap[a.name]=c,this.submitted[a.name]=c},addWrapper:function(a){return this.settings.wrapper&&(a=a.add(a.parent(this.settings.wrapper))),a},defaultShowErrors:function(){var a,b,c;for(a=0;this.errorList[a];a++)c=this.errorList[a],this.settings.highlight&&this.settings.highlight.call(this,c.element,this.settings.errorClass,this.settings.validClass),this.showLabel(c.element,c.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(a=0;this.successList[a];a++)this.showLabel(this.successList[a]);if(this.settings.unhighlight)for(a=0,b=this.validElements();b[a];a++)this.settings.unhighlight.call(this,b[a],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return a(this.errorList).map(function(){return this.element})},showLabel:function(b,c){var d,e,f,g,h=this.errorsFor(b),i=this.idOrName(b),j=a(b).attr("aria-describedby");h.length?(h.removeClass(this.settings.validClass).addClass(this.settings.errorClass),h.html(c)):(h=a("<"+this.settings.errorElement+">").attr("id",i+"-error").addClass(this.settings.errorClass).html(c||""),d=h,this.settings.wrapper&&(d=h.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(d):this.settings.errorPlacement?this.settings.errorPlacement.call(this,d,a(b)):d.insertAfter(b),h.is("label")?h.attr("for",i):0===h.parents("label[for='"+this.escapeCssMeta(i)+"']").length&&(f=h.attr("id"),j?j.match(new RegExp("\\b"+this.escapeCssMeta(f)+"\\b"))||(j+=" "+f):j=f,a(b).attr("aria-describedby",j),e=this.groups[b.name],e&&(g=this,a.each(g.groups,function(b,c){c===e&&a("[name='"+g.escapeCssMeta(b)+"']",g.currentForm).attr("aria-describedby",h.attr("id"))})))),!c&&this.settings.success&&(h.text(""),"string"==typeof this.settings.success?h.addClass(this.settings.success):this.settings.success(h,b)),this.toShow=this.toShow.add(h)},errorsFor:function(b){var c=this.escapeCssMeta(this.idOrName(b)),d=a(b).attr("aria-describedby"),e="label[for='"+c+"'], label[for='"+c+"'] *";return d&&(e=e+", #"+this.escapeCssMeta(d).replace(/\s+/g,", #")),this.errors().filter(e)},escapeCssMeta:function(a){return a.replace(/([\\!"#$%&'()*+,.\/:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(a){return this.groups[a.name]||(this.checkable(a)?a.name:a.id||a.name)},validationTargetFor:function(b){return this.checkable(b)&&(b=this.findByName(b.name)),a(b).not(this.settings.ignore)[0]},checkable:function(a){return/radio|checkbox/i.test(a.type)},findByName:function(b){return a(this.currentForm).find("[name='"+this.escapeCssMeta(b)+"']")},getLength:function(b,c){switch(c.nodeName.toLowerCase()){case"select":return a("option:selected",c).length;case"input":if(this.checkable(c))return this.findByName(c.name).filter(":checked").length}return b.length},depend:function(a,b){return!this.dependTypes[typeof a]||this.dependTypes[typeof a](a,b)},dependTypes:{"boolean":function(a){return a},string:function(b,c){return!!a(b,c.form).length},"function":function(a,b){return a(b)}},optional:function(b){var c=this.elementValue(b);return!a.validator.methods.required.call(this,c,b)&&"dependency-mismatch"},startRequest:function(b){this.pending[b.name]||(this.pendingRequest++,a(b).addClass(this.settings.pendingClass),this.pending[b.name]=!0)},stopRequest:function(b,c){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[b.name],a(b).removeClass(this.settings.pendingClass),c&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(a(this.currentForm).submit(),this.submitButton&&a("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!c&&0===this.pendingRequest&&this.formSubmitted&&(a(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(b,c){return c="string"==typeof c&&c||"remote",a.data(b,"previousValue")||a.data(b,"previousValue",{old:null,valid:!0,message:this.defaultMessage(b,{method:c})})},destroy:function(){this.resetForm(),a(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(b,c){b.constructor===String?this.classRuleSettings[b]=c:a.extend(this.classRuleSettings,b)},classRules:function(b){var c={},d=a(b).attr("class");return d&&a.each(d.split(" "),function(){this in a.validator.classRuleSettings&&a.extend(c,a.validator.classRuleSettings[this])}),c},normalizeAttributeRule:function(a,b,c,d){/min|max|step/.test(c)&&(null===b||/number|range|text/.test(b))&&(d=Number(d),isNaN(d)&&(d=void 0)),d||0===d?a[c]=d:b===c&&"range"!==b&&(a[c]=!0)},attributeRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)"required"===c?(d=b.getAttribute(c),""===d&&(d=!0),d=!!d):d=f.attr(c),this.normalizeAttributeRule(e,g,c,d);return e.maxlength&&/-1|2147483647|524288/.test(e.maxlength)&&delete e.maxlength,e},dataRules:function(b){var c,d,e={},f=a(b),g=b.getAttribute("type");for(c in a.validator.methods)d=f.data("rule"+c.charAt(0).toUpperCase()+c.substring(1).toLowerCase()),this.normalizeAttributeRule(e,g,c,d);return e},staticRules:function(b){var c={},d=a.data(b.form,"validator");return d.settings.rules&&(c=a.validator.normalizeRule(d.settings.rules[b.name])||{}),c},normalizeRules:function(b,c){return a.each(b,function(d,e){if(e===!1)return void delete b[d];if(e.param||e.depends){var f=!0;switch(typeof e.depends){case"string":f=!!a(e.depends,c.form).length;break;case"function":f=e.depends.call(c,c)}f?b[d]=void 0===e.param||e.param:(a.data(c.form,"validator").resetElements(a(c)),delete b[d])}}),a.each(b,function(d,e){b[d]=a.isFunction(e)&&"normalizer"!==d?e(c):e}),a.each(["minlength","maxlength"],function(){b[this]&&(b[this]=Number(b[this]))}),a.each(["rangelength","range"],function(){var c;b[this]&&(a.isArray(b[this])?b[this]=[Number(b[this][0]),Number(b[this][1])]:"string"==typeof b[this]&&(c=b[this].replace(/[\[\]]/g,"").split(/[\s,]+/),b[this]=[Number(c[0]),Number(c[1])]))}),a.validator.autoCreateRanges&&(null!=b.min&&null!=b.max&&(b.range=[b.min,b.max],delete b.min,delete b.max),null!=b.minlength&&null!=b.maxlength&&(b.rangelength=[b.minlength,b.maxlength],delete b.minlength,delete b.maxlength)),b},normalizeRule:function(b){if("string"==typeof b){var c={};a.each(b.split(/\s/),function(){c[this]=!0}),b=c}return b},addMethod:function(b,c,d){a.validator.methods[b]=c,a.validator.messages[b]=void 0!==d?d:a.validator.messages[b],c.length<3&&a.validator.addClassRules(b,a.validator.normalizeRule(b))},methods:{required:function(b,c,d){if(!this.depend(d,c))return"dependency-mismatch";if("select"===c.nodeName.toLowerCase()){var e=a(c).val();return e&&e.length>0}return this.checkable(c)?this.getLength(b,c)>0:b.length>0},email:function(a,b){return this.optional(b)||/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(a)},url:function(a,b){return this.optional(b)||/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[\/?#]\S*)?$/i.test(a)},date:function(a,b){return this.optional(b)||!/Invalid|NaN/.test(new Date(a).toString())},dateISO:function(a,b){return this.optional(b)||/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(a)},number:function(a,b){return this.optional(b)||/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(a)},digits:function(a,b){return this.optional(b)||/^\d+$/.test(a)},minlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d},maxlength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e<=d},rangelength:function(b,c,d){var e=a.isArray(b)?b.length:this.getLength(b,c);return this.optional(c)||e>=d[0]&&e<=d[1]},min:function(a,b,c){return this.optional(b)||a>=c},max:function(a,b,c){return this.optional(b)||a<=c},range:function(a,b,c){return this.optional(b)||a>=c[0]&&a<=c[1]},step:function(b,c,d){var e,f=a(c).attr("type"),g="Step attribute on input type "+f+" is not supported.",h=["text","number","range"],i=new RegExp("\\b"+f+"\\b"),j=f&&!i.test(h.join()),k=function(a){var b=(""+a).match(/(?:\.(\d+))?$/);return b&&b[1]?b[1].length:0},l=function(a){return Math.round(a*Math.pow(10,e))},m=!0;if(j)throw new Error(g);return e=k(d),(k(b)>e||l(b)%l(d)!==0)&&(m=!1),this.optional(c)||m},equalTo:function(b,c,d){var e=a(d);return this.settings.onfocusout&&e.not(".validate-equalTo-blur").length&&e.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){a(c).valid()}),b===e.val()},remote:function(b,c,d,e){if(this.optional(c))return"dependency-mismatch";e="string"==typeof e&&e||"remote";var f,g,h,i=this.previousValue(c,e);return this.settings.messages[c.name]||(this.settings.messages[c.name]={}),i.originalMessage=i.originalMessage||this.settings.messages[c.name][e],this.settings.messages[c.name][e]=i.message,d="string"==typeof d&&{url:d}||d,h=a.param(a.extend({data:b},d.data)),i.old===h?i.valid:(i.old=h,f=this,this.startRequest(c),g={},g[c.name]=b,a.ajax(a.extend(!0,{mode:"abort",port:"validate"+c.name,dataType:"json",data:g,context:f.currentForm,success:function(a){var d,g,h,j=a===!0||"true"===a;f.settings.messages[c.name][e]=i.originalMessage,j?(h=f.formSubmitted,f.resetInternals(),f.toHide=f.errorsFor(c),f.formSubmitted=h,f.successList.push(c),f.invalid[c.name]=!1,f.showErrors()):(d={},g=a||f.defaultMessage(c,{method:e,parameters:b}),d[c.name]=i.message=g,f.invalid[c.name]=!0,f.showErrors(d)),i.valid=j,f.stopRequest(c,j)}},d)),"pending")}}});var b,c={};return a.ajaxPrefilter?a.ajaxPrefilter(function(a,b,d){var e=a.port;"abort"===a.mode&&(c[e]&&c[e].abort(),c[e]=d)}):(b=a.ajax,a.ajax=function(d){var e=("mode"in d?d:a.ajaxSettings).mode,f=("port"in d?d:a.ajaxSettings).port;return"abort"===e?(c[f]&&c[f].abort(),c[f]=b.apply(this,arguments),c[f]):b.apply(this,arguments)}),a});
\ No newline at end of file
diff --git a/version_diary b/version_diary
new file mode 100644
index 0000000..076e2b3
--- /dev/null
+++ b/version_diary
@@ -0,0 +1,10 @@
+#版本1.0.0为gitee开源版本修改了数据库为本地数据库之后的版本
+功能:每日健康打开、历史打卡记录、健康周报、登陆注册、头像上传、修改资料和修改密码等。
+
+#版本1.0.1修复了时间组件显示乱码问题
+
+#版本1.0.2修复了头像未上传成功的问题
+
+#版本2.0.2增加了默认头像,增加了邮箱认证功能,增加了近期推送功能
+
+#版本2.0.3修复了近期推送邮箱未绑定没有提示的问题
\ No newline at end of file