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("&#x([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 += ""; + } + } + + 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[^>]*)?>")); + } + 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 ""; + } + } + } + } + + // 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"+ "" + + ""+ "" + + ""+ "" + + ""+ ""); + } + content.append("
打卡时间收缩压舒张压心率体温食欲体重步数
"+t.getCreateTime()+""+t.getHighBloodPressure()+""+t.getLowBloodPressure()+""+t.getHeartRate()+""+t.getTemperature()+"度"+t.getAppetiteDes(t.getAppetite())+""+t.getWeight()+"kg"+t.getNumberOfStep()+"步
"); + 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 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 @@ + + + + + + + + + + + + + + \ 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"%> + + + + + 身体信息录入 + + + + + +
+
+
+
+ 昵称 +
+
+ 舒张压 +
舒张压必须在0~200之间
+
+
+ 收缩压 +
收缩压必须在0~200之间
+
+
+ 心率 +
心率必须在40~200之间
+
+
+ 体温 +
体温必须在33~45度之间
+
+
+ 食欲 +
请选择食欲
+
+
+ 体重KG +
体重必须在0~200之间
+
+
+ 步数 +
+
+ + +
+
+
+
+ + + + 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" %> + + + + 用户身体信息列表 + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
序号打卡时间收缩压舒张压心率体温操作
1/2 +
+
+ + + + + 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"%> + + + + + 修改密码 + + + + + +
+
+
+
+ +
+
+ +
+ 密码必须在6~10位之间 +
+
+
+ +
+ 两次密码必须相同 且在6~10位之间 +
+
+
+ +
+
+
+
+ + + + 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"%> + + + + + 用户信息 + + + + +
+
+
+
+
+ 昵称 +
昵称必须在2~10之间
+
+
+ 账号 +
+
+ 邮箱 +
邮箱不能为空
+ + +
+
+ 验证 +
验证码不能为空
+
+
+ 头像 +
请选择头像文件
+
+ +
+ + + +
+
+
+
+
+ + + + 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