diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 69fbc74..bab465d 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -6,8 +6,7 @@ - - + diff --git a/admin/src/main/java/com/example/action/GlobalController.java b/admin/src/main/java/com/example/action/GlobalController.java deleted file mode 100644 index e6496fb..0000000 --- a/admin/src/main/java/com/example/action/GlobalController.java +++ /dev/null @@ -1,247 +0,0 @@ -package com.example.action; - -import com.example.frame.base.FileUploadResponse; -import com.example.frame.base.BaseResponse; -import com.example.frame.base.ErrorType; -import com.example.frame.base.Screen; -import com.example.frame.utils.LocalData; -import com.example.config.ActionConfig; -import com.example.frame.utils.MapperUtil; -import org.springframework.beans.BeansException; -import org.apache.commons.io.FileUtils; -import org.springframework.boot.web.servlet.error.ErrorController; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.context.request.ServletRequestAttributes; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.multipart.MultipartHttpServletRequest; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.IOException; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * 全局请求Controller,如果无特殊请求,则不需再增加其他Controller - * 全局htm后缀入口{@link GlobalController#hold} - * 全局异常捕捉{@link GlobalController#excepitonHandler} - * 全局上传接口{@link GlobalController#upload} - * 全局下载接口{@link GlobalController#download} - *

- * 说明Request命名规则,驼峰式命名 - * Api#Example#Request ==> 目标#动作#Request - * - * @author author - * @version 0.0.1 - * @since 2019-06-16 - */ -@Controller -@ControllerAdvice -public class GlobalController implements ErrorController { - - @Value("${web.welcome.page}") - private String homePage; - - /** - * 全局异常捕捉 - * - * @param request - * @param response - * @param exception 要捕获的异常 - * @return - */ - @ExceptionHandler(Exception.class) - public String excepitonHandler(HttpServletRequest request, HttpServletResponse response, Model model, Exception exception) { - StringBuffer msg = new StringBuffer(""); - if (exception != null) { - msg = new StringBuffer(""); - String message = exception.toString(); - int length = exception.getStackTrace().length; - if (length > 0) { - msg.append("").append(message).append("
"); - for (int i = 0; i < length; i++) { - msg.append("").append(exception.getStackTrace()[i]).append("
"); - } - } else { - msg.append(message); - } - } - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - model.addAttribute("msg", msg.toString()); - return "500"; - } - - private final static String ERROR_PATH = "/error"; - - @Override - public String getErrorPath() { - return ERROR_PATH; - } - - @RequestMapping(value = ERROR_PATH) - public String error(HttpServletRequest request) { - Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); - - switch (statusCode) { - case 404: - return "404"; - case 403: - try { - LocalData.getResponse().sendRedirect("/login.htm"); - } catch (IOException e) { - e.printStackTrace(); - } - return "403"; - case 500: - return "500"; - default: - return "403"; - } - } - - @RequestMapping("/") - public String home() { - return "forward:" + homePage; - } - - /** - * 当未明确指定控制器时,走该请求,默认返回对应的layout布局和screen视图 - * 当需要使用layout时,不需要返回值,ViewNameTranslator会处理对应关系 - * - * @param model - * @param request - */ - @RequestMapping({"/**/*.htm"}) - public void hold(HttpServletRequest request, Model model) { - HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); - - // 尝试执行Target Screen执行器(服务器渲染),不存在则直接返回视图模板(Ajax渲染) - Screen screenExec = null; - try { - String target = LocalData.getTarget(); - target = target.replaceAll("/", ".").toLowerCase(); - screenExec = LocalData.getApplicationContext().getBean(ActionConfig.SCREEN_PREFIX + target, Screen.class); - screenExec.exec(model, request, response); - } catch (BeansException e) { - - } - - //todo 可在此获取共性数据(也可以在全局拦截器GlobalHandlerInterceptor、拦截器作用域比此更高), - //todo 例如用户信息等。其他业务数据在页面渲染后通过Ajax请求 - } - - @RequestMapping("/upload") - @ResponseBody - public BaseResponse upload(HttpServletRequest request) { - FileUploadResponse fileUploadResponse = new FileUploadResponse(); - MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request; - MultipartFile target = multipartHttpServletRequest.getFile("file"); - - String fileName = target.getOriginalFilename(); - - //======== - //处理文件 - //======== - fileUploadResponse.setId(1L); - fileUploadResponse.setUrl("example.com\\img\\1.jpg"); - fileUploadResponse.setDownloadUrl("example.com\\img\\1.jpg"); - - - if (target != null) { - fileUploadResponse.addError(ErrorType.BUSINESS_ERROR, "文件上传成功,但未处理文件[" + fileName + "]!"); - } else { - fileUploadResponse.addError(ErrorType.BUSINESS_ERROR, "文件上传失败!"); - } - - return fileUploadResponse; - } - - @RequestMapping("/download") - @ResponseBody - public ResponseEntity download(@RequestParam(value = "file", required = false) String file) throws IOException { - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); - - //======== - //下载DEMO - //======== - if (file == null) { - file = "test.txt"; - headers.setContentDispositionFormData("attachment", new String(file.getBytes("UTF-8"), "iso-8859-1")); - return new ResponseEntity("test".getBytes(), - headers, HttpStatus.CREATED); - } - - return new ResponseEntity(FileUtils.readFileToByteArray(new File(file)), - headers, HttpStatus.CREATED); - } - - private static ConcurrentHashMap sseMap = new ConcurrentHashMap(); - - /** - * Sse推送服务,服务器向js推送自定义消息 - * Sse容器{@link GlobalController#sseMap} - * Sse批量推送{@link GlobalController#pushAll} - */ - @RequestMapping(value = "/sse/{userId}", produces = "text/event-stream;charset=UTF-8") - public SseEmitter sse(@PathVariable String userId) { - SseEmitter sseEmitter = new SseEmitter(10000000L); - sseEmitter.onError(new Consumer() { - private String key = userId; - - @Override - public void accept(Throwable throwable) { - sseMap.remove(key); - } - }); - sseEmitter.onCompletion(new Runnable() { - private String key = userId; - - @Override - public void run() { - sseMap.remove(key); - } - }); - if (sseMap.get(userId) != null) { - sseMap.remove(userId); - } - sseMap.put(userId, sseEmitter); - - return sseEmitter; - } - - /** - * Sse批量推送 - * - * @param data 推送对象 - */ - public void pushAll(Object data) { - for (String s : sseMap.keySet()) { - try { - sseMap.get(s).send(MapperUtil.toJson(data), MediaType.APPLICATION_JSON); - } catch (IOException e) { - sseMap.remove(s); - } - } - } -} diff --git a/admin/src/main/java/com/example/config/ActionConfig.java b/admin/src/main/java/com/example/config/ActionConfig.java deleted file mode 100644 index e087d8d..0000000 --- a/admin/src/main/java/com/example/config/ActionConfig.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.example.config; - -import com.example.frame.base.Control; -import com.example.frame.base.Screen; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.beans.factory.support.BeanNameGenerator; -import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.TypeFilter; - -import java.io.IOException; - -/** - * 请求处理器配置类,Screen及Control - *

- * 注册扫描Screen处理器 {@link ActionConfig#registryScreen} - * 注册扫描Control处理器 {@link ActionConfig#registryControl} - * - * @author wangbing - * @version 0.0.1 - * @since 2017-01-01 - */ -@Configuration -public class ActionConfig implements BeanDefinitionRegistryPostProcessor { - public static final String SCREEN_PREFIX = "screen"; - public static final String CONTROL_PREFIX = "control"; - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { - String aPackage = this.getClass().getPackage().getName(); - int i = registryScreen("com.example.action.screen", beanDefinitionRegistry); - int i1 = registryControl("com.example.action.control", beanDefinitionRegistry); - System.out.println(); - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { - - } - - private int registryScreen(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { - ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); - classPathBeanDefinitionScanner.addIncludeFilter(new TypeFilter() { - @Override - public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { - if (metadataReader.getClassMetadata().getSuperClassName().equals(Screen.class.getName())) { - return true; - } - return false; - } - }); - classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { - @Override - public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { - String beanClassName = beanDefinition.getBeanClassName(); - String s = beanClassName.replaceAll(basePackage, SCREEN_PREFIX); - return s.toLowerCase(); - } - }); - return classPathBeanDefinitionScanner.scan(basePackage); - } - - private int registryControl(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { - ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); - classPathBeanDefinitionScanner.addIncludeFilter(new TypeFilter() { - @Override - public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { - if (metadataReader.getClassMetadata().getSuperClassName().equals(Control.class.getName())) { - return true; - } - return false; - } - }); - classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { - @Override - public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { - String beanClassName = beanDefinition.getBeanClassName(); - String s = beanClassName.replaceAll(basePackage, CONTROL_PREFIX); - return s.toLowerCase(); - } - }); - return classPathBeanDefinitionScanner.scan(basePackage); - } -} diff --git a/admin/src/main/java/com/example/module/admin/rsp/LoginResponse.java b/admin/src/main/java/com/example/module/admin/rsp/LoginResponse.java deleted file mode 100644 index fe40dae..0000000 --- a/admin/src/main/java/com/example/module/admin/rsp/LoginResponse.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.example.module.admin.rsp; - -import com.example.frame.base.BaseResponse; - - -public class LoginResponse extends BaseResponse { - - -} diff --git a/admin/src/main/java/com/example/Application.java b/admin/src/main/java/xyz/wbsite/Application.java similarity index 97% rename from admin/src/main/java/com/example/Application.java rename to admin/src/main/java/xyz/wbsite/Application.java index a223156..28fa3d4 100644 --- a/admin/src/main/java/com/example/Application.java +++ b/admin/src/main/java/xyz/wbsite/Application.java @@ -1,4 +1,4 @@ -package com.example; +package xyz.wbsite; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; diff --git a/admin/src/main/java/com/example/action/AjaxController.java b/admin/src/main/java/xyz/wbsite/action/AjaxController.java similarity index 90% rename from admin/src/main/java/com/example/action/AjaxController.java rename to admin/src/main/java/xyz/wbsite/action/AjaxController.java index b5abcba..1252f2f 100644 --- a/admin/src/main/java/com/example/action/AjaxController.java +++ b/admin/src/main/java/xyz/wbsite/action/AjaxController.java @@ -1,26 +1,26 @@ -package com.example.action; +package xyz.wbsite.action; -import com.example.frame.base.BaseResponse; -import com.example.frame.base.Error; -import com.example.frame.base.ErrorType; -import com.example.frame.base.Token; -import com.example.frame.utils.LocalData; -import com.example.frame.utils.LogUtil; -import com.example.frame.utils.MD5Util; -import com.example.frame.utils.MapperUtil; -import com.example.frame.utils.Message; -import com.example.frame.utils.ProcessUtil; -import com.example.frame.utils.ValidationUtil; -import com.example.module.admin.ent.Mapping; -import com.example.module.admin.ent.NginxCtrl; -import com.example.module.admin.mgr.MappingManager; -import com.example.module.admin.req.LoginRequest; -import com.example.module.admin.req.MappingCreateRequest; -import com.example.module.admin.req.MappingDeleteRequest; -import com.example.module.admin.req.MappingFindRequest; -import com.example.module.admin.req.MappingUpdateRequest; -import com.example.module.admin.rsp.LoginResponse; -import com.example.module.admin.rsp.MappingFindResponse; +import xyz.wbsite.frame.base.BaseResponse; +import xyz.wbsite.frame.base.Error; +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.base.Token; +import xyz.wbsite.frame.utils.LocalData; +import xyz.wbsite.frame.utils.LogUtil; +import xyz.wbsite.frame.utils.MD5Util; +import xyz.wbsite.frame.utils.MapperUtil; +import xyz.wbsite.frame.utils.Message; +import xyz.wbsite.frame.utils.ProcessUtil; +import xyz.wbsite.frame.utils.ValidationUtil; +import xyz.wbsite.module.admin.ent.Mapping; +import xyz.wbsite.module.admin.ent.NginxCtrl; +import xyz.wbsite.module.admin.mgr.MappingManager; +import xyz.wbsite.module.admin.req.LoginRequest; +import xyz.wbsite.module.admin.req.MappingCreateRequest; +import xyz.wbsite.module.admin.req.MappingDeleteRequest; +import xyz.wbsite.module.admin.req.MappingFindRequest; +import xyz.wbsite.module.admin.req.MappingUpdateRequest; +import xyz.wbsite.module.admin.rsp.LoginResponse; +import xyz.wbsite.module.admin.rsp.MappingFindResponse; import freemarker.template.Template; import freemarker.template.TemplateException; import org.springframework.beans.factory.annotation.Autowired; diff --git a/admin/src/main/java/xyz/wbsite/action/GlobalController.java b/admin/src/main/java/xyz/wbsite/action/GlobalController.java new file mode 100644 index 0000000..c046bce --- /dev/null +++ b/admin/src/main/java/xyz/wbsite/action/GlobalController.java @@ -0,0 +1,454 @@ +package xyz.wbsite.action; + +import com.fasterxml.jackson.core.TreeNode; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; +import xyz.wbsite.config.ActionConfig; +import xyz.wbsite.frame.base.BaseRequest; +import xyz.wbsite.frame.base.BaseResponse; +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.base.Screen; +import xyz.wbsite.frame.base.Token; +import xyz.wbsite.frame.utils.AESUtil; +import xyz.wbsite.frame.utils.LocalData; +import xyz.wbsite.frame.utils.LogUtil; +import xyz.wbsite.frame.utils.MD5Util; +import xyz.wbsite.frame.utils.MapperUtil; +import xyz.wbsite.frame.utils.RequestUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 全局请求Controller,如果无特殊请求,则不需再增加其他Controller + * 全局htm后缀入口{@link GlobalController#action(Model, HttpServletRequest, HttpServletResponse)} + * 全局ajax入口{@link GlobalController#ajax(String, String, String, HttpServletRequest, HttpServletResponse, String, MultipartFile)} + * 全局异常捕捉{@link GlobalController#exceptionHandler(HttpServletRequest, HttpServletResponse, Model, Exception)} + * 全局消息订阅{@link GlobalController#sse(String)} + *

+ * 说明Request命名规则,驼峰式命名 + * Api#Example#Request ==> 目标#动作#Request + * + * @author author + * @version 0.0.1 + * @since 2017-01-01 + */ +@Controller +@ControllerAdvice +public class GlobalController implements ErrorController { + + @Value("${r'${server.servlet.context-path}'}") + private String context; + @Value("${r'${web.home.page}'}") + private String homePage; + @Value("${r'${web.login.page}'}") + private String loginPage; + @Autowired + private FreeMarkerViewResolver viewResolver; + + /** + * 全局异常捕捉 + * + * @param request + * @param response + * @param exception 要捕获的异常 + * @return + */ + @ExceptionHandler(Exception.class) + public String exceptionHandler(HttpServletRequest request, HttpServletResponse response, Model model, Exception exception) { + StringBuffer msg = new StringBuffer(""); + if (exception != null) { + msg = new StringBuffer(""); + String message = exception.toString(); + int length = exception.getStackTrace().length; + if (length > 0) { + msg.append("").append(message).append("
"); + for (int i = 0; i < length; i++) { + msg.append("").append(exception.getStackTrace()[i]).append("
"); + } + } else { + msg.append(message); + } + } + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + model.addAttribute("msg", msg.toString()); + return "500"; + } + + private final static String ERROR_PATH = "/error"; + + @Override + public String getErrorPath() { + return ERROR_PATH; + } + + @RequestMapping(value = ERROR_PATH) + public String error(HttpServletRequest request, HttpServletResponse response) { + switch (response.getStatus()) { + case 404: + return "404"; + case 403: + String errorUrl = RequestUtil.getErrorUrl(request); + errorUrl = errorUrl.replaceFirst(context, ""); + if ((errorUrl.equals(homePage) || errorUrl.equals("/")) && LocalData.getToken() == null) { + try { + response.sendRedirect(context + loginPage); + } catch (IOException e) { + e.printStackTrace(); + } + } + return "403"; + case 500: + return "500"; + default: + return "403"; + } + } + + @RequestMapping("/") + public String home() { + Token token = LocalData.getToken(); + if (token == null) { + return "redirect:" + loginPage; + } else { + return "redirect:" + homePage; + } + } + + /** + * 当未明确指定控制器时,走该请求,默认返回对应的layout布局和screen视图 + * 当需要使用layout时,不需要返回值,ViewNameTranslator会处理对应关系 + * + * @param model + * @param request + */ + @RequestMapping({"/**/*.htm"}) + public String action(Model model, HttpServletRequest request, HttpServletResponse response) { + String servletPath = request.getServletPath();// /**/*.htm + String layout = "/layout/default"; + String action = LocalData.getAction();// **/* + + Pattern compile = Pattern.compile("^/(.+)\\.htm"); + Matcher matcher = compile.matcher(servletPath); + if (matcher.find()) { + action = matcher.group(1); + LocalData.setAction(action); + } + + try { + LocaleResolver localeResolver = (LocaleResolver) request.getAttribute(DispatcherServlet.LOCALE_RESOLVER_ATTRIBUTE); + Locale locale = localeResolver.resolveLocale(request); + + {//查询screen + String[] split = action.split("/"); + StringBuilder sb = new StringBuilder(""); + sb.append("screen"); + for (int i = 0; i < split.length; i++) { + sb.append(File.separator); + sb.append(split[i]); + } + layout = sb.toString(); + View view = viewResolver.resolveViewName(layout, locale); + if (view == null) { + response.sendError(HttpServletResponse.SC_NOT_FOUND, ""); + return null; + } + + // 尝试执行Screen执行器(服务器渲染),并返回视图模板 + try { + String beanClassName = (ActionConfig.SCREEN_PREFIX + action).toLowerCase(); + Screen screenExec = LocalData.getApplicationContext().getBean(beanClassName, Screen.class); + screenExec.exec(model, request, response); + + if (response.getStatus() != HttpServletResponse.SC_OK) { + response.sendError(response.getStatus(), ""); + return null; + } + } catch (BeansException e) { + + } + } + + {//查找layout + String[] split = action.split("/"); + + int lt = split.length; + while (lt > 0) { + + StringBuilder sb = new StringBuilder(""); + sb.append("layout"); + for (int i = 0; i < lt - 1; i++) { + sb.append(File.separator); + sb.append(split[i]); + } + + layout = sb.toString() + File.separator + split[split.length - 1]; + + View view = viewResolver.resolveViewName(layout, locale); + //无法找到对应layout,使用默认layout + if (view == null) { + layout = sb.toString() + File.separator + "default"; + View defaultView = viewResolver.resolveViewName(layout, locale); + if (null == defaultView && lt == 1) { + System.err.println("can not find layout/default.ftl"); + } else if (null == defaultView) { + lt--; + } else { + break; + } + } else { + break; + } + } + } + } catch (Exception e) { + return exceptionHandler(request, response, model, e); + } + + // todo 可在此获取共性数据(也可以在全局拦截器GlobalHandlerInterceptor、拦截器作用域比此更高), + // todo 例如用户信息等。其他业务数据在页面渲染后通过Ajax请求 + return layout; + } + + @RequestMapping("/ajax/{module}/{target}/{method}") + @ResponseBody + public Object ajax( + @PathVariable String module, + @PathVariable String target, + @PathVariable String method, + HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, + @RequestBody(required = false) String data, + @RequestParam(name = "file", required = false) MultipartFile file) { + try { + String beanClassName = (ActionConfig.AJAX_PREFIX + module + "/" + target).toLowerCase(); + Object ajax = LocalData.getApplicationContext().getBean(beanClassName); + + Class ajaxClass = ajax.getClass(); + + Method[] methods = ajaxClass.getDeclaredMethods(); + + Method methodC = null; + for (Method meth : methods) { + if (meth.getName().equals(method)) { + methodC = meth; + } + } + + if (methodC == null) { + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "未找到对应的方法!"); + return baseResponse; + } + + Parameter[] parameters = methodC.getParameters(); + Object[] arg = new Object[parameters.length]; + + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + if (parameter.getType() == HttpServletRequest.class) { + arg[i] = httpServletRequest; + } else if (parameter.getType() == HttpServletResponse.class) { + arg[i] = httpServletResponse; + } else if (parameter.getType() == TreeNode.class) { + arg[i] = MapperUtil.toTree(data); + } else if (parameter.getType() == String.class) { + arg[i] = data; + } else if (parameter.getType() == MultipartFile.class) { + arg[i] = file; + } else if (BaseRequest.class.isAssignableFrom(parameter.getType())) { + arg[i] = MapperUtil.toJava(data, parameter.getType()); + } else if (parameter.getType() == Token.class) { + arg[i] = LocalData.getToken(); + } else { + arg[i] = null; + } + } + return methodC.invoke(ajax, arg); + } catch (BeansException e) { + e.printStackTrace(); + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "未找到对应的目标!"); + return baseResponse; + } catch (IllegalAccessException e) { + e.printStackTrace(); + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "方法执必须公开!"); + return baseResponse; + } catch (InvocationTargetException e) { + e.getTargetException().printStackTrace(); + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "方法执行错误[" + e.getTargetException().getMessage() + "]"); + return baseResponse; + } + } + + + @RequestMapping(path = "/api/{module}/{target}/{method}", method = RequestMethod.POST) + @ResponseBody + public String api( + @PathVariable String module, + @PathVariable String target, + @PathVariable String method, + @RequestParam(required = false) String appKey, + @RequestParam(required = false) String sign, + @RequestParam(required = false) Long timestamp, + @RequestParam(required = false) String token, + @RequestParam(required = false) String encryptData, + HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { + BaseResponse response = new BaseResponse(); + if (appKey == null) { + response.addError(ErrorType.BUSINESS_ERROR, "应用码参数[appKey]不存在!"); + return MapperUtil.toJson(response); + } else if (sign == null) { + response.addError(ErrorType.BUSINESS_ERROR, "签名参数[sign]不存在!"); + return MapperUtil.toJson(response); + } else if (timestamp == null) { + response.addError(ErrorType.BUSINESS_ERROR, "时间戳参数[timestamp]不存在!"); + return MapperUtil.toJson(response); + } + + String data = null; + String appSecret = "1234567890123456"; + // 解码 + try { + data = AESUtil.decrypt2String(encryptData, appSecret); + } catch (Exception e) { + response.addError(ErrorType.BUSINESS_ERROR, "解码失败,请确认编码是否正确!"); + return MapperUtil.toJson(response); + } + + // 验证签名 + String sign_ = MD5Util.encode(appSecret + data + timestamp); + if (!sign_.equals(sign)) { + response.addError(ErrorType.BUSINESS_ERROR, "签名验证失败!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + // 时效性验证 + long currentTime = System.currentTimeMillis(); + if (currentTime - timestamp > 2 * 60 * 1000) { + response.addError(ErrorType.BUSINESS_ERROR, "请求过期, 或本地时间错误!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + // 权限验证 + if (!LocalData.getToken().hasRes(httpServletRequest.getServletPath())) { + response.addError(ErrorType.BUSINESS_ERROR, "[" + httpServletRequest.getServletPath() + "]未授权的资源!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + // 开始处理业务 + try { + String beanClassName = (ActionConfig.API_PREFIX + module + "/" + target).toLowerCase(); + Object ajax = LocalData.getApplicationContext().getBean(beanClassName); + Class ajaxClass = ajax.getClass(); + Method[] methods = ajaxClass.getDeclaredMethods(); + + Method methodC = null; + for (Method meth : methods) { + if (meth.getName().equals(method)) { + methodC = meth; + } + } + + if (methodC == null) { + response.addError(ErrorType.BUSINESS_ERROR, "未找到对应的方法!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + Parameter[] parameters = methodC.getParameters(); + Object[] arg = new Object[parameters.length]; + + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + if (parameter.getType() == HttpServletRequest.class) { + arg[i] = httpServletRequest; + } else if (parameter.getType() == HttpServletResponse.class) { + arg[i] = httpServletResponse; + } else if (BaseRequest.class.isAssignableFrom(parameter.getType())) { + arg[i] = MapperUtil.toJava(data, parameter.getType()); + } else if (parameter.getType() == TreeNode.class) { + arg[i] = MapperUtil.toTree(data); + } else if (parameter.getType() == Token.class) { + arg[i] = LocalData.getToken(); + } else { + arg[i] = null; + } + } + response = (BaseResponse) methodC.invoke(ajax, arg); + } catch (BeansException e) { + e.printStackTrace(); + response.addError(ErrorType.BUSINESS_ERROR, "未找到对应的目标!"); + } catch (IllegalAccessException e) { + e.printStackTrace(); + response.addError(ErrorType.BUSINESS_ERROR, "方法执必须公开!"); + } catch (InvocationTargetException e) { + LogUtil.dumpException(e.getTargetException()); + e.getTargetException().printStackTrace(); + response.addError(ErrorType.BUSINESS_ERROR, "方法执行错误[" + e.getTargetException().getMessage() + "]"); + } + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + private static ConcurrentHashMap sseMap = new ConcurrentHashMap(); + + /** + * Sse推送服务,服务器向js推送自定义消息 + * Sse容器{@link GlobalController#sseMap} + * Sse批量推送{@link GlobalController#pushAll} + */ + @RequestMapping(value = "/sse/{userId}", produces = "text/event-stream;charset=UTF-8") + public SseEmitter sse(@PathVariable String userId) { + SseEmitter sseEmitter = new SseEmitter(10000000L); + if (sseMap.get(userId) != null) { + sseMap.remove(userId); + } + sseMap.put(userId, sseEmitter); + return sseEmitter; + } + + /** + * Sse批量推送 + * + * @param data 推送对象 + */ + public static void pushAll(Object data) { + for (String s : sseMap.keySet()) { + try { + sseMap.get(s).send(MapperUtil.toJson(data), MediaType.APPLICATION_JSON); + } catch (IOException e) { + sseMap.remove(s); + } + } + } +} diff --git a/admin/src/main/java/xyz/wbsite/action/ajax/MappingAjax.java b/admin/src/main/java/xyz/wbsite/action/ajax/MappingAjax.java new file mode 100644 index 0000000..fe52e63 --- /dev/null +++ b/admin/src/main/java/xyz/wbsite/action/ajax/MappingAjax.java @@ -0,0 +1,21 @@ +package xyz.wbsite.action.ajax; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.multipart.MultipartFile; +import xyz.wbsite.frame.base.BaseResponse; +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.utils.LogUtil; +import xyz.wbsite.frame.utils.MapperUtil; +import xyz.wbsite.frame.utils.ValidationUtil; +import xyz.wbsite.module.admin.mgr.MappingManager; + +import java.io.IOException; +import java.util.List; + +public class MappingAjax { + + @Autowired + private MappingManager mappingManager; + + +} diff --git a/admin/src/main/java/com/example/action/control/Footer.java b/admin/src/main/java/xyz/wbsite/action/control/Footer.java similarity index 79% rename from admin/src/main/java/com/example/action/control/Footer.java rename to admin/src/main/java/xyz/wbsite/action/control/Footer.java index a53ce95..adaaf85 100644 --- a/admin/src/main/java/com/example/action/control/Footer.java +++ b/admin/src/main/java/xyz/wbsite/action/control/Footer.java @@ -1,6 +1,6 @@ -package com.example.action.control; +package xyz.wbsite.action.control; -import com.example.frame.base.Control; +import xyz.wbsite.frame.base.Control; import org.springframework.ui.Model; import javax.servlet.http.HttpServletRequest; diff --git a/admin/src/main/java/com/example/action/control/Header.java b/admin/src/main/java/xyz/wbsite/action/control/Header.java similarity index 79% rename from admin/src/main/java/com/example/action/control/Header.java rename to admin/src/main/java/xyz/wbsite/action/control/Header.java index 32e8be5..c365fb7 100644 --- a/admin/src/main/java/com/example/action/control/Header.java +++ b/admin/src/main/java/xyz/wbsite/action/control/Header.java @@ -1,6 +1,6 @@ -package com.example.action.control; +package xyz.wbsite.action.control; -import com.example.frame.base.Control; +import xyz.wbsite.frame.base.Control; import org.springframework.ui.Model; import javax.servlet.http.HttpServletRequest; diff --git a/admin/src/main/java/com/example/action/screen/Index.java b/admin/src/main/java/xyz/wbsite/action/screen/Index.java similarity index 89% rename from admin/src/main/java/com/example/action/screen/Index.java rename to admin/src/main/java/xyz/wbsite/action/screen/Index.java index b06dd67..30211fa 100644 --- a/admin/src/main/java/com/example/action/screen/Index.java +++ b/admin/src/main/java/xyz/wbsite/action/screen/Index.java @@ -1,6 +1,6 @@ -package com.example.action.screen; +package xyz.wbsite.action.screen; -import com.example.frame.base.Screen; +import xyz.wbsite.frame.base.Screen; import org.springframework.ui.Model; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; diff --git a/admin/src/main/java/com/example/action/screen/Mapping.java b/admin/src/main/java/xyz/wbsite/action/screen/Mapping.java similarity index 84% rename from admin/src/main/java/com/example/action/screen/Mapping.java rename to admin/src/main/java/xyz/wbsite/action/screen/Mapping.java index cf63946..19d2957 100644 --- a/admin/src/main/java/com/example/action/screen/Mapping.java +++ b/admin/src/main/java/xyz/wbsite/action/screen/Mapping.java @@ -1,7 +1,7 @@ -package com.example.action.screen; +package xyz.wbsite.action.screen; -import com.example.frame.base.Screen; -import com.example.module.admin.ent.NginxCtrl; +import xyz.wbsite.frame.base.Screen; +import xyz.wbsite.module.admin.ent.NginxCtrl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.ui.Model; diff --git a/admin/src/main/java/xyz/wbsite/config/ActionConfig.java b/admin/src/main/java/xyz/wbsite/config/ActionConfig.java new file mode 100644 index 0000000..ad8f7a6 --- /dev/null +++ b/admin/src/main/java/xyz/wbsite/config/ActionConfig.java @@ -0,0 +1,139 @@ +package xyz.wbsite.config; + + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import xyz.wbsite.frame.base.Control; +import xyz.wbsite.frame.base.Screen; +import xyz.wbsite.frame.utils.LogUtil; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 请求处理器配置类,Screen及Control + *

+ * 注册扫描Screen处理器 {@link ActionConfig#registryScreen} + * 注册扫描Control处理器 {@link ActionConfig#registryControl} + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Configuration +public class ActionConfig implements BeanDefinitionRegistryPostProcessor { + + public static String SCREEN_PREFIX = "/screen/"; + public static String CONTROL_PREFIX = "/control/"; + public static String AJAX_PREFIX = "/ajax/"; + public static String API_PREFIX = "/api/"; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { + String aPackage = this.getClass().getPackage().getName(); + Pattern compile = Pattern.compile("(.*)\\.config"); + Matcher matcher = compile.matcher(aPackage); + if (matcher.find()) { + String basePackage = matcher.group(1); + registryScreen(basePackage + ".action.screen", beanDefinitionRegistry); + registryControl(basePackage + ".action.control", beanDefinitionRegistry); + registryAjax(basePackage + ".action.ajax", beanDefinitionRegistry); + registryApi(basePackage + ".action.api", beanDefinitionRegistry); + } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { + + } + + private int registryScreen(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.resetFilters(false); + classPathBeanDefinitionScanner.addIncludeFilter(new AssignableTypeFilter(Screen.class)); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + String beamName = beanClassName.replaceAll(basePackage + ".", SCREEN_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry screen " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } + + private int registryControl(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.resetFilters(false); + classPathBeanDefinitionScanner.addIncludeFilter(new AssignableTypeFilter(Control.class)); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + String beamName = beanClassName.replaceAll(basePackage + ".", CONTROL_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry control " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } + + private int registryAjax(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.resetFilters(false); + classPathBeanDefinitionScanner.addIncludeFilter(new TypeFilter() { + @Override + public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { + return true; + } + }); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + if (beanClassName != null && beanClassName.endsWith("Ajax")) { + beanClassName = beanClassName.substring(0, beanClassName.length() - 4); + } + String beamName = beanClassName.replaceAll(basePackage + ".", AJAX_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry ajax " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } + + private int registryApi(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.addIncludeFilter(new TypeFilter() { + @Override + public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { + return true; + } + }); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + if (beanClassName != null && beanClassName.endsWith("Api")) { + beanClassName = beanClassName.substring(0, beanClassName.length() - 3); + } + String beamName = beanClassName.replaceAll(basePackage + ".", API_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry api " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } +} diff --git a/admin/src/main/java/com/example/config/NginxConfig.java b/admin/src/main/java/xyz/wbsite/config/NginxConfig.java similarity index 89% rename from admin/src/main/java/com/example/config/NginxConfig.java rename to admin/src/main/java/xyz/wbsite/config/NginxConfig.java index a367194..65eeb05 100644 --- a/admin/src/main/java/com/example/config/NginxConfig.java +++ b/admin/src/main/java/xyz/wbsite/config/NginxConfig.java @@ -1,9 +1,8 @@ -package com.example.config; +package xyz.wbsite.config; -import com.example.frame.utils.FileUtil; -import com.example.frame.utils.ZipUtil; -import com.example.module.admin.ent.Mapping; -import com.example.module.admin.ent.NginxCtrl; +import xyz.wbsite.frame.utils.FileUtil; +import xyz.wbsite.frame.utils.ZipUtil; +import xyz.wbsite.module.admin.ent.NginxCtrl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.system.ApplicationHome; import org.springframework.context.annotation.Bean; @@ -13,12 +12,9 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.util.StringUtils; import xyz.wbsite.wsqlite.ObjectClient; -import javax.annotation.PostConstruct; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.sql.SQLException; -import java.util.ArrayList; @Configuration diff --git a/admin/src/main/java/com/example/config/SecurityConfig.java b/admin/src/main/java/xyz/wbsite/config/SecurityConfig.java similarity index 94% rename from admin/src/main/java/com/example/config/SecurityConfig.java rename to admin/src/main/java/xyz/wbsite/config/SecurityConfig.java index da51b8e..b9d949b 100644 --- a/admin/src/main/java/com/example/config/SecurityConfig.java +++ b/admin/src/main/java/xyz/wbsite/config/SecurityConfig.java @@ -1,6 +1,6 @@ -package com.example.config; +package xyz.wbsite.config; -import com.example.frame.utils.MD5Util; +import xyz.wbsite.frame.utils.MD5Util; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -10,13 +10,11 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.Authentication; -import com.example.frame.base.Token; -import com.example.frame.utils.CookieUtil; -import com.example.frame.utils.LocalData; +import xyz.wbsite.frame.base.Token; +import xyz.wbsite.frame.utils.CookieUtil; +import xyz.wbsite.frame.utils.LocalData; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; -import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; diff --git a/admin/src/main/java/com/example/config/SqliteConfig.java b/admin/src/main/java/xyz/wbsite/config/SqliteConfig.java similarity index 87% rename from admin/src/main/java/com/example/config/SqliteConfig.java rename to admin/src/main/java/xyz/wbsite/config/SqliteConfig.java index cc56b16..7a9d558 100644 --- a/admin/src/main/java/com/example/config/SqliteConfig.java +++ b/admin/src/main/java/xyz/wbsite/config/SqliteConfig.java @@ -1,7 +1,6 @@ -package com.example.config; +package xyz.wbsite.config; -import com.example.frame.utils.IDgenerator; -import com.example.module.admin.ent.Mapping; +import xyz.wbsite.module.admin.ent.Mapping; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.system.ApplicationHome; import org.springframework.context.annotation.Bean; @@ -9,13 +8,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; import xyz.wbsite.wsqlite.ObjectClient; -import xyz.wbsite.wsqlite.Where; -import javax.annotation.PostConstruct; import java.io.File; import java.sql.SQLException; import java.util.ArrayList; -import java.util.List; @Configuration diff --git a/admin/src/main/java/com/example/config/TaskConfig.java b/admin/src/main/java/xyz/wbsite/config/TaskConfig.java similarity index 80% rename from admin/src/main/java/com/example/config/TaskConfig.java rename to admin/src/main/java/xyz/wbsite/config/TaskConfig.java index c9f10df..2e4c5b4 100644 --- a/admin/src/main/java/com/example/config/TaskConfig.java +++ b/admin/src/main/java/xyz/wbsite/config/TaskConfig.java @@ -1,15 +1,12 @@ -package com.example.config; +package xyz.wbsite.config; -import com.example.action.GlobalController; -import com.example.frame.base.Message; -import com.example.frame.base.MessageType; -import com.example.frame.utils.LogUtil; -import com.example.frame.utils.ProcessUtil; -import com.example.module.admin.ent.NginxCtrl; -import com.example.module.admin.ent.State; +import xyz.wbsite.action.GlobalController; +import xyz.wbsite.frame.base.Message; +import xyz.wbsite.frame.base.MessageType; +import xyz.wbsite.module.admin.ent.NginxCtrl; +import xyz.wbsite.module.admin.ent.State; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.SchedulingConfigurer; diff --git a/admin/src/main/java/com/example/config/ThreadPoolConfig.java b/admin/src/main/java/xyz/wbsite/config/ThreadPoolConfig.java similarity index 98% rename from admin/src/main/java/com/example/config/ThreadPoolConfig.java rename to admin/src/main/java/xyz/wbsite/config/ThreadPoolConfig.java index 764e30d..354e761 100644 --- a/admin/src/main/java/com/example/config/ThreadPoolConfig.java +++ b/admin/src/main/java/xyz/wbsite/config/ThreadPoolConfig.java @@ -1,4 +1,4 @@ -package com.example.config; +package xyz.wbsite.config; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Configuration; diff --git a/admin/src/main/java/com/example/config/WebMvcConfig.java b/admin/src/main/java/xyz/wbsite/config/WebMvcConfig.java similarity index 97% rename from admin/src/main/java/com/example/config/WebMvcConfig.java rename to admin/src/main/java/xyz/wbsite/config/WebMvcConfig.java index 96f12b5..4a8d960 100644 --- a/admin/src/main/java/com/example/config/WebMvcConfig.java +++ b/admin/src/main/java/xyz/wbsite/config/WebMvcConfig.java @@ -1,11 +1,11 @@ -package com.example.config; +package xyz.wbsite.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import com.example.frame.base.Token; -import com.example.frame.utils.LocalData; -import com.example.frame.utils.LogUtil; +import xyz.wbsite.frame.base.Token; +import xyz.wbsite.frame.utils.LocalData; +import xyz.wbsite.frame.utils.LogUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; @@ -21,8 +21,6 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.List; - @Configuration public class WebMvcConfig implements WebMvcConfigurer { diff --git a/admin/src/main/java/com/example/frame/base/BaseEntity.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseEntity.java similarity index 98% rename from admin/src/main/java/com/example/frame/base/BaseEntity.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseEntity.java index 9e2afd9..7a4f3ac 100644 --- a/admin/src/main/java/com/example/frame/base/BaseEntity.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseEntity.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; diff --git a/admin/src/main/java/com/example/frame/base/BaseFindRequest.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseFindRequest.java similarity index 94% rename from admin/src/main/java/com/example/frame/base/BaseFindRequest.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseFindRequest.java index 834c624..f7d804a 100644 --- a/admin/src/main/java/com/example/frame/base/BaseFindRequest.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseFindRequest.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * BaseFindRequest - 基类 diff --git a/admin/src/main/java/com/example/frame/base/BaseFindResponse.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseFindResponse.java similarity index 94% rename from admin/src/main/java/com/example/frame/base/BaseFindResponse.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseFindResponse.java index ce3b0aa..d351c2e 100644 --- a/admin/src/main/java/com/example/frame/base/BaseFindResponse.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseFindResponse.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; import java.util.List; diff --git a/admin/src/main/java/com/example/frame/base/BaseGetAllRequest.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseGetAllRequest.java similarity index 94% rename from admin/src/main/java/com/example/frame/base/BaseGetAllRequest.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseGetAllRequest.java index 7fd7162..8f718fa 100644 --- a/admin/src/main/java/com/example/frame/base/BaseGetAllRequest.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseGetAllRequest.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * BaseFindRequest - 基类 diff --git a/admin/src/main/java/com/example/frame/base/BaseRequest.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseRequest.java similarity index 79% rename from admin/src/main/java/com/example/frame/base/BaseRequest.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseRequest.java index 848e47a..a24b7c9 100644 --- a/admin/src/main/java/com/example/frame/base/BaseRequest.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseRequest.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * BaseRequest - 基类 diff --git a/admin/src/main/java/com/example/frame/base/BaseResponse.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseResponse.java similarity index 95% rename from admin/src/main/java/com/example/frame/base/BaseResponse.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseResponse.java index cbcbe9d..8766d33 100644 --- a/admin/src/main/java/com/example/frame/base/BaseResponse.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseResponse.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; import java.util.ArrayList; import java.util.List; diff --git a/admin/src/main/java/com/example/frame/base/BaseSearchRequest.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseSearchRequest.java similarity index 92% rename from admin/src/main/java/com/example/frame/base/BaseSearchRequest.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseSearchRequest.java index 94ea40c..62cd20f 100644 --- a/admin/src/main/java/com/example/frame/base/BaseSearchRequest.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseSearchRequest.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * BaseSearchRequest - 基类 diff --git a/admin/src/main/java/com/example/frame/base/BaseUpdateRequest.java b/admin/src/main/java/xyz/wbsite/frame/base/BaseUpdateRequest.java similarity index 91% rename from admin/src/main/java/com/example/frame/base/BaseUpdateRequest.java rename to admin/src/main/java/xyz/wbsite/frame/base/BaseUpdateRequest.java index eebc3ab..9a8578d 100644 --- a/admin/src/main/java/com/example/frame/base/BaseUpdateRequest.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/BaseUpdateRequest.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * BaseUpdateRequest - 基类 diff --git a/admin/src/main/java/com/example/frame/base/Control.java b/admin/src/main/java/xyz/wbsite/frame/base/Control.java similarity index 89% rename from admin/src/main/java/com/example/frame/base/Control.java rename to admin/src/main/java/xyz/wbsite/frame/base/Control.java index 2f862af..1074730 100644 --- a/admin/src/main/java/com/example/frame/base/Control.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/Control.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; import org.springframework.ui.Model; diff --git a/admin/src/main/java/com/example/frame/base/Error.java b/admin/src/main/java/xyz/wbsite/frame/base/Error.java similarity index 95% rename from admin/src/main/java/com/example/frame/base/Error.java rename to admin/src/main/java/xyz/wbsite/frame/base/Error.java index 06fd8e0..72cc105 100644 --- a/admin/src/main/java/com/example/frame/base/Error.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/Error.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * Error - 错误基类 diff --git a/admin/src/main/java/com/example/frame/base/ErrorType.java b/admin/src/main/java/xyz/wbsite/frame/base/ErrorType.java similarity index 88% rename from admin/src/main/java/com/example/frame/base/ErrorType.java rename to admin/src/main/java/xyz/wbsite/frame/base/ErrorType.java index c99e7da..17fb52c 100644 --- a/admin/src/main/java/com/example/frame/base/ErrorType.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/ErrorType.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * ErrorType - 错误类型 diff --git a/admin/src/main/java/com/example/frame/base/FileUploadResponse.java b/admin/src/main/java/xyz/wbsite/frame/base/FileUploadResponse.java similarity index 94% rename from admin/src/main/java/com/example/frame/base/FileUploadResponse.java rename to admin/src/main/java/xyz/wbsite/frame/base/FileUploadResponse.java index 74fe555..ee7ee2c 100644 --- a/admin/src/main/java/com/example/frame/base/FileUploadResponse.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/FileUploadResponse.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; public class FileUploadResponse extends BaseResponse { diff --git a/admin/src/main/java/com/example/frame/base/Message.java b/admin/src/main/java/xyz/wbsite/frame/base/Message.java similarity index 91% rename from admin/src/main/java/com/example/frame/base/Message.java rename to admin/src/main/java/xyz/wbsite/frame/base/Message.java index f373b82..be95b63 100644 --- a/admin/src/main/java/com/example/frame/base/Message.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/Message.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; public class Message { diff --git a/admin/src/main/java/com/example/frame/base/MessageType.java b/admin/src/main/java/xyz/wbsite/frame/base/MessageType.java similarity index 57% rename from admin/src/main/java/com/example/frame/base/MessageType.java rename to admin/src/main/java/xyz/wbsite/frame/base/MessageType.java index 73406da..57a573b 100644 --- a/admin/src/main/java/com/example/frame/base/MessageType.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/MessageType.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; public enum MessageType { diff --git a/admin/src/main/java/com/example/frame/base/Screen.java b/admin/src/main/java/xyz/wbsite/frame/base/Screen.java similarity index 89% rename from admin/src/main/java/com/example/frame/base/Screen.java rename to admin/src/main/java/xyz/wbsite/frame/base/Screen.java index 41c10e8..c2c66ed 100644 --- a/admin/src/main/java/com/example/frame/base/Screen.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/Screen.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; import org.springframework.ui.Model; diff --git a/admin/src/main/java/com/example/frame/base/SortType.java b/admin/src/main/java/xyz/wbsite/frame/base/SortType.java similarity index 83% rename from admin/src/main/java/com/example/frame/base/SortType.java rename to admin/src/main/java/xyz/wbsite/frame/base/SortType.java index 3670757..3351d9d 100644 --- a/admin/src/main/java/com/example/frame/base/SortType.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/SortType.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; /** * SortTypeEnum - 排序方式 diff --git a/admin/src/main/java/com/example/frame/base/Token.java b/admin/src/main/java/xyz/wbsite/frame/base/Token.java similarity index 97% rename from admin/src/main/java/com/example/frame/base/Token.java rename to admin/src/main/java/xyz/wbsite/frame/base/Token.java index 88d79a5..736790e 100644 --- a/admin/src/main/java/com/example/frame/base/Token.java +++ b/admin/src/main/java/xyz/wbsite/frame/base/Token.java @@ -1,4 +1,4 @@ -package com.example.frame.base; +package xyz.wbsite.frame.base; import java.io.Serializable; import java.util.HashSet; diff --git a/admin/src/main/java/com/example/frame/freemarker/Layout.java b/admin/src/main/java/xyz/wbsite/frame/freemarker/Layout.java similarity index 95% rename from admin/src/main/java/com/example/frame/freemarker/Layout.java rename to admin/src/main/java/xyz/wbsite/frame/freemarker/Layout.java index ea35850..4dea0cf 100644 --- a/admin/src/main/java/com/example/frame/freemarker/Layout.java +++ b/admin/src/main/java/xyz/wbsite/frame/freemarker/Layout.java @@ -1,7 +1,7 @@ -package com.example.frame.freemarker; +package xyz.wbsite.frame.freemarker; import java.io.File; -import com.example.config.ActionConfig; +import xyz.wbsite.config.ActionConfig; import freemarker.template.TemplateModelException; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; @@ -16,8 +16,8 @@ import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale; -import com.example.frame.base.Control; -import com.example.frame.utils.LocalData; +import xyz.wbsite.frame.base.Control; +import xyz.wbsite.frame.utils.LocalData; /** * 布局帮助类 diff --git a/admin/src/main/java/com/example/frame/freemarker/Uri.java b/admin/src/main/java/xyz/wbsite/frame/freemarker/Uri.java similarity index 96% rename from admin/src/main/java/com/example/frame/freemarker/Uri.java rename to admin/src/main/java/xyz/wbsite/frame/freemarker/Uri.java index 7489a53..7b74726 100644 --- a/admin/src/main/java/com/example/frame/freemarker/Uri.java +++ b/admin/src/main/java/xyz/wbsite/frame/freemarker/Uri.java @@ -1,4 +1,4 @@ -package com.example.frame.freemarker; +package xyz.wbsite.frame.freemarker; import freemarker.template.TemplateModelException; import org.springframework.stereotype.Component; diff --git a/admin/src/main/java/com/example/frame/freemarker/ViewNameTranslator.java b/admin/src/main/java/xyz/wbsite/frame/freemarker/ViewNameTranslator.java similarity index 97% rename from admin/src/main/java/com/example/frame/freemarker/ViewNameTranslator.java rename to admin/src/main/java/xyz/wbsite/frame/freemarker/ViewNameTranslator.java index d1f33d3..7494a4f 100644 --- a/admin/src/main/java/com/example/frame/freemarker/ViewNameTranslator.java +++ b/admin/src/main/java/xyz/wbsite/frame/freemarker/ViewNameTranslator.java @@ -1,6 +1,6 @@ -package com.example.frame.freemarker; +package xyz.wbsite.frame.freemarker; -import com.example.frame.utils.LocalData; +import xyz.wbsite.frame.utils.LocalData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.DispatcherServlet; diff --git a/admin/src/main/java/com/example/frame/utils/AESUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/AESUtil.java similarity index 99% rename from admin/src/main/java/com/example/frame/utils/AESUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/AESUtil.java index 44d5106..7f42706 100644 --- a/admin/src/main/java/com/example/frame/utils/AESUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/AESUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; diff --git a/admin/src/main/java/com/example/frame/utils/Base64Util.java b/admin/src/main/java/xyz/wbsite/frame/utils/Base64Util.java similarity index 99% rename from admin/src/main/java/com/example/frame/utils/Base64Util.java rename to admin/src/main/java/xyz/wbsite/frame/utils/Base64Util.java index 253fddb..614c782 100644 --- a/admin/src/main/java/com/example/frame/utils/Base64Util.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/Base64Util.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import java.util.Arrays; diff --git a/admin/src/main/java/com/example/frame/utils/CookieUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/CookieUtil.java similarity index 96% rename from admin/src/main/java/com/example/frame/utils/CookieUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/CookieUtil.java index 3f2f512..11fe8d3 100644 --- a/admin/src/main/java/com/example/frame/utils/CookieUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/CookieUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import javax.servlet.http.Cookie; diff --git a/admin/src/main/java/com/example/frame/utils/FileUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/FileUtil.java similarity index 99% rename from admin/src/main/java/com/example/frame/utils/FileUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/FileUtil.java index 55dd2c8..1450306 100644 --- a/admin/src/main/java/com/example/frame/utils/FileUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/FileUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import java.io.*; import java.net.MalformedURLException; diff --git a/admin/src/main/java/com/example/frame/utils/IDgenerator.java b/admin/src/main/java/xyz/wbsite/frame/utils/IDgenerator.java similarity index 98% rename from admin/src/main/java/com/example/frame/utils/IDgenerator.java rename to admin/src/main/java/xyz/wbsite/frame/utils/IDgenerator.java index 390ae19..ba4cf2a 100644 --- a/admin/src/main/java/com/example/frame/utils/IDgenerator.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/IDgenerator.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import java.io.BufferedReader; import java.io.IOException; diff --git a/admin/src/main/java/com/example/frame/utils/LocalData.java b/admin/src/main/java/xyz/wbsite/frame/utils/LocalData.java similarity index 97% rename from admin/src/main/java/com/example/frame/utils/LocalData.java rename to admin/src/main/java/xyz/wbsite/frame/utils/LocalData.java index 6cee16a..3c5d210 100644 --- a/admin/src/main/java/com/example/frame/utils/LocalData.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/LocalData.java @@ -1,6 +1,6 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; -import com.example.frame.base.Token; +import xyz.wbsite.frame.base.Token; import org.springframework.context.ApplicationContext; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; diff --git a/admin/src/main/java/com/example/frame/utils/LogUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/LogUtil.java similarity index 98% rename from admin/src/main/java/com/example/frame/utils/LogUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/LogUtil.java index ea9bc7f..434d1dc 100644 --- a/admin/src/main/java/com/example/frame/utils/LogUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/LogUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/admin/src/main/java/com/example/frame/utils/MD5Util.java b/admin/src/main/java/xyz/wbsite/frame/utils/MD5Util.java similarity index 97% rename from admin/src/main/java/com/example/frame/utils/MD5Util.java rename to admin/src/main/java/xyz/wbsite/frame/utils/MD5Util.java index b5b4d23..6656176 100644 --- a/admin/src/main/java/com/example/frame/utils/MD5Util.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/MD5Util.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import com.fasterxml.jackson.core.TreeNode; import java.security.MessageDigest; diff --git a/admin/src/main/java/com/example/frame/utils/MapperUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/MapperUtil.java similarity index 99% rename from admin/src/main/java/com/example/frame/utils/MapperUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/MapperUtil.java index 2746acd..bdaee60 100644 --- a/admin/src/main/java/com/example/frame/utils/MapperUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/MapperUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; diff --git a/admin/src/main/java/com/example/frame/utils/Message.java b/admin/src/main/java/xyz/wbsite/frame/utils/Message.java similarity index 96% rename from admin/src/main/java/com/example/frame/utils/Message.java rename to admin/src/main/java/xyz/wbsite/frame/utils/Message.java index d788d93..24c0d81 100644 --- a/admin/src/main/java/com/example/frame/utils/Message.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/Message.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; /** diff --git a/admin/src/main/java/com/example/frame/utils/ProcessUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/ProcessUtil.java similarity index 98% rename from admin/src/main/java/com/example/frame/utils/ProcessUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/ProcessUtil.java index a6248c3..039cac2 100644 --- a/admin/src/main/java/com/example/frame/utils/ProcessUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/ProcessUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import java.awt.*; import java.io.BufferedReader; diff --git a/admin/src/main/java/com/example/frame/utils/RSAUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/RSAUtil.java similarity index 99% rename from admin/src/main/java/com/example/frame/utils/RSAUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/RSAUtil.java index 9e4e207..daf49a3 100644 --- a/admin/src/main/java/com/example/frame/utils/RSAUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/RSAUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import javax.crypto.Cipher; import java.io.ByteArrayOutputStream; diff --git a/admin/src/main/java/xyz/wbsite/frame/utils/RequestUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/RequestUtil.java new file mode 100644 index 0000000..a9fe557 --- /dev/null +++ b/admin/src/main/java/xyz/wbsite/frame/utils/RequestUtil.java @@ -0,0 +1,79 @@ +package xyz.wbsite.frame.utils; + +import javax.servlet.http.HttpServletRequest; + +/** + * RequestUtil + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class RequestUtil { + + /** + * 获取请求放IP + * + * @param request + * @return + */ + public static String getIp(HttpServletRequest request) { + String ip = request.getHeader("X-Forwarded-For"); + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + } else if (ip.length() > 15) { + String[] ips = ip.split(","); + for (int index = 0; index < ips.length; index++) { + String strIp = (String) ips[index]; + if (!("unknown".equalsIgnoreCase(strIp))) { + ip = strIp; + break; + } + } + } + return ip; + } + + /** + * 获取情况方客户端信息 + * + * @param request 请求 + * @return 客户端信息 + */ + public static String getUserAgent(HttpServletRequest request) { + return request.getHeader("User-Agent"); + } + + /** + * 获取转发至错误页之前的请求URL + * + * @param request 请求 + * @return 请求URL + */ + public static String getErrorUrl(HttpServletRequest request) { + if (request.getAttribute("javax.servlet.error.request_uri") != null) { + return (String) request.getAttribute("javax.servlet.error.request_uri"); + } else { + return ""; + } + } +} diff --git a/admin/src/main/java/com/example/frame/utils/ValidationUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/ValidationUtil.java similarity index 78% rename from admin/src/main/java/com/example/frame/utils/ValidationUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/ValidationUtil.java index 8f0cc6e..3e6ef64 100644 --- a/admin/src/main/java/com/example/frame/utils/ValidationUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/ValidationUtil.java @@ -1,20 +1,13 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; -import com.example.frame.base.BaseRequest; -import com.example.frame.base.BaseResponse; -import com.example.frame.base.ErrorType; +import xyz.wbsite.frame.base.BaseRequest; +import xyz.wbsite.frame.base.BaseResponse; +import xyz.wbsite.frame.base.ErrorType; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * 验证工具类。提供一些通用简单的数据验证功能 diff --git a/admin/src/main/java/com/example/frame/utils/WebUtils.java b/admin/src/main/java/xyz/wbsite/frame/utils/WebUtils.java similarity index 99% rename from admin/src/main/java/com/example/frame/utils/WebUtils.java rename to admin/src/main/java/xyz/wbsite/frame/utils/WebUtils.java index bf7da8f..e071ac2 100644 --- a/admin/src/main/java/com/example/frame/utils/WebUtils.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/WebUtils.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import javax.net.ssl.*; import java.io.*; diff --git a/admin/src/main/java/com/example/frame/utils/ZipUtil.java b/admin/src/main/java/xyz/wbsite/frame/utils/ZipUtil.java similarity index 99% rename from admin/src/main/java/com/example/frame/utils/ZipUtil.java rename to admin/src/main/java/xyz/wbsite/frame/utils/ZipUtil.java index 536254c..4707c1b 100644 --- a/admin/src/main/java/com/example/frame/utils/ZipUtil.java +++ b/admin/src/main/java/xyz/wbsite/frame/utils/ZipUtil.java @@ -1,4 +1,4 @@ -package com.example.frame.utils; +package xyz.wbsite.frame.utils; import java.io.*; import java.util.Enumeration; diff --git a/admin/src/main/java/com/example/frame/validation/Dict.java b/admin/src/main/java/xyz/wbsite/frame/validation/Dict.java similarity index 91% rename from admin/src/main/java/com/example/frame/validation/Dict.java rename to admin/src/main/java/xyz/wbsite/frame/validation/Dict.java index f96f392..fc253ed 100644 --- a/admin/src/main/java/com/example/frame/validation/Dict.java +++ b/admin/src/main/java/xyz/wbsite/frame/validation/Dict.java @@ -1,4 +1,4 @@ -package com.example.frame.validation; +package xyz.wbsite.frame.validation; import javax.validation.Constraint; import javax.validation.Payload; diff --git a/admin/src/main/java/com/example/frame/validation/DictValidator.java b/admin/src/main/java/xyz/wbsite/frame/validation/DictValidator.java similarity index 96% rename from admin/src/main/java/com/example/frame/validation/DictValidator.java rename to admin/src/main/java/xyz/wbsite/frame/validation/DictValidator.java index 10fa99a..aa8bd8f 100644 --- a/admin/src/main/java/com/example/frame/validation/DictValidator.java +++ b/admin/src/main/java/xyz/wbsite/frame/validation/DictValidator.java @@ -1,4 +1,4 @@ -package com.example.frame.validation; +package xyz.wbsite.frame.validation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; diff --git a/admin/src/main/java/com/example/module/admin/ent/Mapping.java b/admin/src/main/java/xyz/wbsite/module/admin/ent/Mapping.java similarity index 98% rename from admin/src/main/java/com/example/module/admin/ent/Mapping.java rename to admin/src/main/java/xyz/wbsite/module/admin/ent/Mapping.java index 821bb51..af0457e 100644 --- a/admin/src/main/java/com/example/module/admin/ent/Mapping.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/ent/Mapping.java @@ -1,4 +1,4 @@ -package com.example.module.admin.ent; +package xyz.wbsite.module.admin.ent; import com.fasterxml.jackson.annotation.JsonFormat; import xyz.wbsite.wsqlite.anonation.TableField; diff --git a/admin/src/main/java/com/example/module/admin/ent/NginxCtrl.java b/admin/src/main/java/xyz/wbsite/module/admin/ent/NginxCtrl.java similarity index 94% rename from admin/src/main/java/com/example/module/admin/ent/NginxCtrl.java rename to admin/src/main/java/xyz/wbsite/module/admin/ent/NginxCtrl.java index 83b04df..cf513b3 100644 --- a/admin/src/main/java/com/example/module/admin/ent/NginxCtrl.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/ent/NginxCtrl.java @@ -1,7 +1,7 @@ -package com.example.module.admin.ent; +package xyz.wbsite.module.admin.ent; -import com.example.frame.utils.ProcessUtil; +import xyz.wbsite.frame.utils.ProcessUtil; import java.io.File; diff --git a/admin/src/main/java/com/example/module/admin/ent/State.java b/admin/src/main/java/xyz/wbsite/module/admin/ent/State.java similarity index 85% rename from admin/src/main/java/com/example/module/admin/ent/State.java rename to admin/src/main/java/xyz/wbsite/module/admin/ent/State.java index 0f19da8..b678748 100644 --- a/admin/src/main/java/com/example/module/admin/ent/State.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/ent/State.java @@ -1,4 +1,4 @@ -package com.example.module.admin.ent; +package xyz.wbsite.module.admin.ent; /** * 运行状态 diff --git a/admin/src/main/java/com/example/module/admin/mgr/FreeMarkerManager.java b/admin/src/main/java/xyz/wbsite/module/admin/mgr/FreeMarkerManager.java similarity index 98% rename from admin/src/main/java/com/example/module/admin/mgr/FreeMarkerManager.java rename to admin/src/main/java/xyz/wbsite/module/admin/mgr/FreeMarkerManager.java index 3363e0b..b31519d 100644 --- a/admin/src/main/java/com/example/module/admin/mgr/FreeMarkerManager.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/mgr/FreeMarkerManager.java @@ -1,4 +1,4 @@ -package com.example.module.admin.mgr; +package xyz.wbsite.module.admin.mgr; import freemarker.cache.ClassTemplateLoader; import freemarker.cache.MultiTemplateLoader; diff --git a/admin/src/main/java/com/example/module/admin/mgr/MappingManager.java b/admin/src/main/java/xyz/wbsite/module/admin/mgr/MappingManager.java similarity index 64% rename from admin/src/main/java/com/example/module/admin/mgr/MappingManager.java rename to admin/src/main/java/xyz/wbsite/module/admin/mgr/MappingManager.java index b9d657e..23b7add 100644 --- a/admin/src/main/java/com/example/module/admin/mgr/MappingManager.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/mgr/MappingManager.java @@ -1,8 +1,16 @@ -package com.example.module.admin.mgr; +package xyz.wbsite.module.admin.mgr; import com.example.module.admin.req.*; import com.example.module.admin.rsp.*; -import com.example.frame.base.Token; +import xyz.wbsite.frame.base.Token; +import xyz.wbsite.module.admin.req.MappingCreateRequest; +import xyz.wbsite.module.admin.req.MappingDeleteRequest; +import xyz.wbsite.module.admin.req.MappingFindRequest; +import xyz.wbsite.module.admin.req.MappingUpdateRequest; +import xyz.wbsite.module.admin.rsp.MappingCreateResponse; +import xyz.wbsite.module.admin.rsp.MappingDeleteResponse; +import xyz.wbsite.module.admin.rsp.MappingFindResponse; +import xyz.wbsite.module.admin.rsp.MappingUpdateResponse; /** * 映射 diff --git a/admin/src/main/java/com/example/module/admin/mgr/MappingManagerImpl.java b/admin/src/main/java/xyz/wbsite/module/admin/mgr/MappingManagerImpl.java similarity index 85% rename from admin/src/main/java/com/example/module/admin/mgr/MappingManagerImpl.java rename to admin/src/main/java/xyz/wbsite/module/admin/mgr/MappingManagerImpl.java index c8a5e0c..6a45d5d 100644 --- a/admin/src/main/java/com/example/module/admin/mgr/MappingManagerImpl.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/mgr/MappingManagerImpl.java @@ -1,19 +1,19 @@ -package com.example.module.admin.mgr; - -import com.example.frame.base.ErrorType; -import com.example.frame.base.Token; -import com.example.frame.utils.IDgenerator; -import com.example.frame.utils.MapperUtil; -import com.example.frame.utils.ValidationUtil; -import com.example.module.admin.ent.Mapping; -import com.example.module.admin.req.MappingCreateRequest; -import com.example.module.admin.req.MappingDeleteRequest; -import com.example.module.admin.req.MappingFindRequest; -import com.example.module.admin.req.MappingUpdateRequest; -import com.example.module.admin.rsp.MappingCreateResponse; -import com.example.module.admin.rsp.MappingDeleteResponse; -import com.example.module.admin.rsp.MappingFindResponse; -import com.example.module.admin.rsp.MappingUpdateResponse; +package xyz.wbsite.module.admin.mgr; + +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.base.Token; +import xyz.wbsite.frame.utils.IDgenerator; +import xyz.wbsite.frame.utils.MapperUtil; +import xyz.wbsite.frame.utils.ValidationUtil; +import xyz.wbsite.module.admin.ent.Mapping; +import xyz.wbsite.module.admin.req.MappingCreateRequest; +import xyz.wbsite.module.admin.req.MappingDeleteRequest; +import xyz.wbsite.module.admin.req.MappingFindRequest; +import xyz.wbsite.module.admin.req.MappingUpdateRequest; +import xyz.wbsite.module.admin.rsp.MappingCreateResponse; +import xyz.wbsite.module.admin.rsp.MappingDeleteResponse; +import xyz.wbsite.module.admin.rsp.MappingFindResponse; +import xyz.wbsite.module.admin.rsp.MappingUpdateResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import xyz.wbsite.wsqlite.ObjectClient; diff --git a/admin/src/main/java/com/example/module/admin/req/LoginRequest.java b/admin/src/main/java/xyz/wbsite/module/admin/req/LoginRequest.java similarity index 84% rename from admin/src/main/java/com/example/module/admin/req/LoginRequest.java rename to admin/src/main/java/xyz/wbsite/module/admin/req/LoginRequest.java index b3a7e05..f532f6e 100644 --- a/admin/src/main/java/com/example/module/admin/req/LoginRequest.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/req/LoginRequest.java @@ -1,9 +1,8 @@ -package com.example.module.admin.req; +package xyz.wbsite.module.admin.req; -import com.example.frame.base.BaseRequest; +import xyz.wbsite.frame.base.BaseRequest; import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; /** * MappingCreateRequest - 映射新增 diff --git a/admin/src/main/java/com/example/module/admin/req/MappingCreateRequest.java b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingCreateRequest.java similarity index 85% rename from admin/src/main/java/com/example/module/admin/req/MappingCreateRequest.java rename to admin/src/main/java/xyz/wbsite/module/admin/req/MappingCreateRequest.java index 2f274ad..143ccfa 100644 --- a/admin/src/main/java/com/example/module/admin/req/MappingCreateRequest.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingCreateRequest.java @@ -1,17 +1,10 @@ -package com.example.module.admin.req; +package xyz.wbsite.module.admin.req; -import com.example.frame.base.BaseRequest; +import xyz.wbsite.frame.base.BaseRequest; import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; -import org.hibernate.validator.constraints.Length; -import xyz.wbsite.wsqlite.anonation.TableField; - -import java.util.regex.Matcher; - /** * MappingCreateRequest - 映射新增 * diff --git a/admin/src/main/java/com/example/module/admin/req/MappingDeleteRequest.java b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingDeleteRequest.java similarity index 83% rename from admin/src/main/java/com/example/module/admin/req/MappingDeleteRequest.java rename to admin/src/main/java/xyz/wbsite/module/admin/req/MappingDeleteRequest.java index 6c65a34..d5531ce 100644 --- a/admin/src/main/java/com/example/module/admin/req/MappingDeleteRequest.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingDeleteRequest.java @@ -1,6 +1,6 @@ -package com.example.module.admin.req; +package xyz.wbsite.module.admin.req; -import com.example.frame.base.BaseUpdateRequest; +import xyz.wbsite.frame.base.BaseUpdateRequest; import javax.validation.constraints.NotNull; /** diff --git a/admin/src/main/java/com/example/module/admin/req/MappingFindRequest.java b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingFindRequest.java similarity index 79% rename from admin/src/main/java/com/example/module/admin/req/MappingFindRequest.java rename to admin/src/main/java/xyz/wbsite/module/admin/req/MappingFindRequest.java index 049846c..2ccdc56 100644 --- a/admin/src/main/java/com/example/module/admin/req/MappingFindRequest.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingFindRequest.java @@ -1,6 +1,6 @@ -package com.example.module.admin.req; +package xyz.wbsite.module.admin.req; -import com.example.frame.base.BaseFindRequest; +import xyz.wbsite.frame.base.BaseFindRequest; /** * MappingRequest - 映射查询 diff --git a/admin/src/main/java/com/example/module/admin/req/MappingUpdateRequest.java b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingUpdateRequest.java similarity index 89% rename from admin/src/main/java/com/example/module/admin/req/MappingUpdateRequest.java rename to admin/src/main/java/xyz/wbsite/module/admin/req/MappingUpdateRequest.java index c186e64..d6933c0 100644 --- a/admin/src/main/java/com/example/module/admin/req/MappingUpdateRequest.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/req/MappingUpdateRequest.java @@ -1,14 +1,10 @@ -package com.example.module.admin.req; +package xyz.wbsite.module.admin.req; -import com.example.frame.base.BaseUpdateRequest; +import xyz.wbsite.frame.base.BaseUpdateRequest; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import org.hibernate.validator.constraints.Length; -import xyz.wbsite.wsqlite.anonation.TableField; - -import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; /** diff --git a/admin/src/main/java/xyz/wbsite/module/admin/rsp/LoginResponse.java b/admin/src/main/java/xyz/wbsite/module/admin/rsp/LoginResponse.java new file mode 100644 index 0000000..2376f5b --- /dev/null +++ b/admin/src/main/java/xyz/wbsite/module/admin/rsp/LoginResponse.java @@ -0,0 +1,9 @@ +package xyz.wbsite.module.admin.rsp; + +import xyz.wbsite.frame.base.BaseResponse; + + +public class LoginResponse extends BaseResponse { + + +} diff --git a/admin/src/main/java/com/example/module/admin/rsp/MappingCreateResponse.java b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingCreateResponse.java similarity index 79% rename from admin/src/main/java/com/example/module/admin/rsp/MappingCreateResponse.java rename to admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingCreateResponse.java index bf966c1..c604407 100644 --- a/admin/src/main/java/com/example/module/admin/rsp/MappingCreateResponse.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingCreateResponse.java @@ -1,6 +1,6 @@ -package com.example.module.admin.rsp; +package xyz.wbsite.module.admin.rsp; -import com.example.frame.base.BaseResponse; +import xyz.wbsite.frame.base.BaseResponse; /** * MappingCreateResponse - 映射 diff --git a/admin/src/main/java/com/example/module/admin/rsp/MappingDeleteResponse.java b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingDeleteResponse.java similarity index 81% rename from admin/src/main/java/com/example/module/admin/rsp/MappingDeleteResponse.java rename to admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingDeleteResponse.java index 5d6687c..daa1eba 100644 --- a/admin/src/main/java/com/example/module/admin/rsp/MappingDeleteResponse.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingDeleteResponse.java @@ -1,6 +1,6 @@ -package com.example.module.admin.rsp; +package xyz.wbsite.module.admin.rsp; -import com.example.frame.base.BaseResponse; +import xyz.wbsite.frame.base.BaseResponse; /** * MappingDeleteResponse - 映射 diff --git a/admin/src/main/java/com/example/module/admin/rsp/MappingFindResponse.java b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingFindResponse.java similarity index 56% rename from admin/src/main/java/com/example/module/admin/rsp/MappingFindResponse.java rename to admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingFindResponse.java index 93e074b..a1ddc80 100644 --- a/admin/src/main/java/com/example/module/admin/rsp/MappingFindResponse.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingFindResponse.java @@ -1,8 +1,8 @@ -package com.example.module.admin.rsp; +package xyz.wbsite.module.admin.rsp; -import com.example.frame.base.BaseFindResponse; -import com.example.module.admin.ent.Mapping; +import xyz.wbsite.frame.base.BaseFindResponse; +import xyz.wbsite.module.admin.ent.Mapping; /** * MappingFindResponse - 映射 diff --git a/admin/src/main/java/com/example/module/admin/rsp/MappingUpdateResponse.java b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingUpdateResponse.java similarity index 81% rename from admin/src/main/java/com/example/module/admin/rsp/MappingUpdateResponse.java rename to admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingUpdateResponse.java index 7aaa0eb..4d524b1 100644 --- a/admin/src/main/java/com/example/module/admin/rsp/MappingUpdateResponse.java +++ b/admin/src/main/java/xyz/wbsite/module/admin/rsp/MappingUpdateResponse.java @@ -1,6 +1,6 @@ -package com.example.module.admin.rsp; +package xyz.wbsite.module.admin.rsp; -import com.example.frame.base.BaseResponse; +import xyz.wbsite.frame.base.BaseResponse; /** * MappingUpdateResponse - 映射 diff --git a/admin/src/main/resources/templates/nginx.conf.ftl b/admin/src/main/resources/templates/nginx.conf.ftl index ad7566b..c7d1e8c 100644 --- a/admin/src/main/resources/templates/nginx.conf.ftl +++ b/admin/src/main/resources/templates/nginx.conf.ftl @@ -34,20 +34,9 @@ http { <#list services?keys as key> server { - listen ${key} <#if key == '443'>ssl; + listen ${key}; server_name wbsite.xyz; - <#if key == '443'> - ssl on; - ssl_certificate 3380768_www.wbsite.xyz.crt; - ssl_certificate_key 3380768_www.wbsite.xyz.key; - ssl_session_timeout 5m; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; - ssl_prefer_server_ciphers on; - - access_log logs/host.access.log main; - <#list services[key] as mapping> <#if mapping.type="HTTP"> location ${mapping.context} { @@ -65,8 +54,6 @@ http { index index.html index.htm; autoindex on; } -<#elseif mapping.type="转HTTPS"> - return 301 https://$server_name$request_uri; diff --git a/admin/src/main/resources/templates/screen/mapping.ftl b/admin/src/main/resources/templates/screen/mapping.ftl index a61820a..28c5f69 100644 --- a/admin/src/main/resources/templates/screen/mapping.ftl +++ b/admin/src/main/resources/templates/screen/mapping.ftl @@ -149,8 +149,6 @@ placeholder="代理地址http://"> - - 4.0.0 - - - admin - wsqlite - - org.springframework.boot spring-boot-starter-parent 2.1.2.RELEASE - xyz.wbsite - nginx-admin-parent - 1.0-SNAPSHOT - pom + nginx-admin + 0.0.1-SNAPSHOT + jar + + nginx-admin + project for Spring Boot UTF-8 @@ -32,22 +27,86 @@ 1.1.0 5.5.1 Greenwich.RC2 - 1.0-SNAPSHOT + 3.8 - aliyun-repos - http://maven.aliyun.com/nexus/content/groups/public/ - - false - + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + org.springframework.boot + spring-boot-starter-web + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + org.mybatis.caches + mybatis-ehcache + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + org.springframework.boot + spring-boot-devtools + provided + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-security + + + + net.sf.dozer + dozer + + + + org.apache.poi + poi-ooxml + + + + + org.xerial + sqlite-jdbc + 3.21.0.1 + + + + - org.springframework.cloud spring-cloud-dependencies @@ -55,24 +114,94 @@ pom import - + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper-version} + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-version} + + + org.mybatis.caches + mybatis-ehcache + ${ehcache-version} + net.sf.dozer dozer ${dozer-version} - - - commons-io - commons-io - ${commons-io-version} - - - xyz.wbsite - wsqlite - ${wsqlite} + org.apache.poi + poi-ooxml + ${poi-ooxml-version} - \ No newline at end of file + + + + ${artifactId}-${version} + + src/main/java + + src/test/java + + + src/main/resources + + + + src/main/java + + **/*Mapper.xml + + + + + ${basedir}/src/main/resources/lib + BOOT-INF/lib/ + + *.jar + + + + + ${basedir}/src/main/resources + + *.bat + + ${basedir}/target + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-war-plugin + + + + ${project.basedir}/src/main/resources/lib + WEB-INF/lib + + *.jar + + + + + + + + diff --git a/src/main/java/xyz/wbsite/Application.java b/src/main/java/xyz/wbsite/Application.java new file mode 100644 index 0000000..f2ecd72 --- /dev/null +++ b/src/main/java/xyz/wbsite/Application.java @@ -0,0 +1,22 @@ +package xyz.wbsite; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import xyz.wbsite.frame.auth.LocalData; + +@SpringBootApplication +@EnableAutoConfiguration +public class Application extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(Application.class); + } + + public static void main(String[] args) { + LocalData.setApplicationContext(SpringApplication.run(Application.class, args)); + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/action/GlobalController.java b/src/main/java/xyz/wbsite/action/GlobalController.java new file mode 100644 index 0000000..5c304a5 --- /dev/null +++ b/src/main/java/xyz/wbsite/action/GlobalController.java @@ -0,0 +1,454 @@ +package xyz.wbsite.action; + +import com.fasterxml.jackson.core.TreeNode; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; +import xyz.wbsite.config.ActionConfig; +import xyz.wbsite.frame.auth.LocalData; +import xyz.wbsite.frame.base.BaseRequest; +import xyz.wbsite.frame.base.BaseResponse; +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.base.Screen; +import xyz.wbsite.frame.auth.Token; +import xyz.wbsite.frame.utils.AESUtil; +import xyz.wbsite.frame.utils.LogUtil; +import xyz.wbsite.frame.utils.MD5Util; +import xyz.wbsite.frame.utils.MapperUtil; +import xyz.wbsite.frame.utils.RequestUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 全局请求Controller,如果无特殊请求,则不需再增加其他Controller + * 全局htm后缀入口{@link GlobalController#action(Model, HttpServletRequest, HttpServletResponse)} + * 全局ajax入口{@link GlobalController#ajax(String, String, String, HttpServletRequest, HttpServletResponse, String, MultipartFile)} + * 全局异常捕捉{@link GlobalController#exceptionHandler(HttpServletRequest, HttpServletResponse, Model, Exception)} + * 全局消息订阅{@link GlobalController#sse(String)} + *

+ * 说明Request命名规则,驼峰式命名 + * Api#Example#Request ==> 目标#动作#Request + * + * @author author + * @version 0.0.1 + * @since 2017-01-01 + */ +@Controller +@ControllerAdvice +public class GlobalController implements ErrorController { + + @Value("${server.servlet.context-path}") + private String context; + @Value("${web.home.page}") + private String homePage; + @Value("${web.login.page}") + private String loginPage; + @Autowired + private FreeMarkerViewResolver viewResolver; + + /** + * 全局异常捕捉 + * + * @param request + * @param response + * @param exception 要捕获的异常 + * @return + */ + @ExceptionHandler(Exception.class) + public String exceptionHandler(HttpServletRequest request, HttpServletResponse response, Model model, Exception exception) { + StringBuffer msg = new StringBuffer(""); + if (exception != null) { + msg = new StringBuffer(""); + String message = exception.toString(); + int length = exception.getStackTrace().length; + if (length > 0) { + msg.append("").append(message).append("
"); + for (int i = 0; i < length; i++) { + msg.append("").append(exception.getStackTrace()[i]).append("
"); + } + } else { + msg.append(message); + } + } + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + model.addAttribute("msg", msg.toString()); + return "500"; + } + + private final static String ERROR_PATH = "/error"; + + @Override + public String getErrorPath() { + return ERROR_PATH; + } + + @RequestMapping(value = ERROR_PATH) + public String error(HttpServletRequest request, HttpServletResponse response) { + switch (response.getStatus()) { + case 404: + return "404"; + case 403: + String errorUrl = RequestUtil.getErrorUrl(request); + errorUrl = errorUrl.replaceFirst(context, ""); + if ((errorUrl.equals(homePage) || errorUrl.equals("/")) && LocalData.getToken() == null) { + try { + response.sendRedirect(context + loginPage); + } catch (IOException e) { + e.printStackTrace(); + } + } + return "403"; + case 500: + return "500"; + default: + return "403"; + } + } + + @RequestMapping("/") + public String home() { + Token token = LocalData.getToken(); + if (token == null) { + return "redirect:" + loginPage; + } else { + return "redirect:" + homePage; + } + } + + /** + * 当未明确指定控制器时,走该请求,默认返回对应的layout布局和screen视图 + * 当需要使用layout时,不需要返回值,ViewNameTranslator会处理对应关系 + * + * @param model + * @param request + */ + @RequestMapping({"/**/*.htm"}) + public String action(Model model, HttpServletRequest request, HttpServletResponse response) { + String servletPath = request.getServletPath();// /**/*.htm + String layout = "/layout/default"; + String action = LocalData.getAction();// **/* + + Pattern compile = Pattern.compile("^/(.+)\\.htm"); + Matcher matcher = compile.matcher(servletPath); + if (matcher.find()) { + action = matcher.group(1); + LocalData.setAction(action); + } + + try { + LocaleResolver localeResolver = (LocaleResolver) request.getAttribute(DispatcherServlet.LOCALE_RESOLVER_ATTRIBUTE); + Locale locale = localeResolver.resolveLocale(request); + + {//查询screen + String[] split = action.split("/"); + StringBuilder sb = new StringBuilder(""); + sb.append("screen"); + for (int i = 0; i < split.length; i++) { + sb.append(File.separator); + sb.append(split[i]); + } + layout = sb.toString(); + View view = viewResolver.resolveViewName(layout, locale); + if (view == null) { + response.sendError(HttpServletResponse.SC_NOT_FOUND,""); + return null; + } + + // 尝试执行Screen执行器(服务器渲染),并返回视图模板 + try { + String beanClassName = (ActionConfig.SCREEN_PREFIX + action).toLowerCase(); + Screen screenExec = LocalData.getApplicationContext().getBean(beanClassName, Screen.class); + screenExec.exec(model, request, response); + + if (response.getStatus() != HttpServletResponse.SC_OK) { + response.sendError(response.getStatus(),""); + return null; + } + } catch (BeansException e) { + + } + } + + {//查找layout + String[] split = action.split("/"); + + int lt = split.length; + while (lt > 0) { + + StringBuilder sb = new StringBuilder(""); + sb.append("layout"); + for (int i = 0; i < lt - 1; i++) { + sb.append(File.separator); + sb.append(split[i]); + } + + layout = sb.toString() + File.separator + split[split.length - 1]; + + View view = viewResolver.resolveViewName(layout, locale); + //无法找到对应layout,使用默认layout + if (view == null) { + layout = sb.toString() + File.separator + "default"; + View defaultView = viewResolver.resolveViewName(layout, locale); + if (null == defaultView && lt == 1) { + System.err.println("can not find layout/default.ftl"); + } else if (null == defaultView) { + lt--; + } else { + break; + } + } else { + break; + } + } + } + } catch (Exception e) { + return exceptionHandler(request, response, model, e); + } + + // todo 可在此获取共性数据(也可以在全局拦截器GlobalHandlerInterceptor、拦截器作用域比此更高), + // todo 例如用户信息等。其他业务数据在页面渲染后通过Ajax请求 + return layout; + } + + @RequestMapping("/ajax/{module}/{target}/{method}") + @ResponseBody + public Object ajax( + @PathVariable String module, + @PathVariable String target, + @PathVariable String method, + HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse, + @RequestBody(required = false) String data, + @RequestParam(name = "file", required = false) MultipartFile file) { + try { + String beanClassName = (ActionConfig.AJAX_PREFIX + module + "/" + target).toLowerCase(); + Object ajax = LocalData.getApplicationContext().getBean(beanClassName); + + Class ajaxClass = ajax.getClass(); + + Method[] methods = ajaxClass.getDeclaredMethods(); + + Method methodC = null; + for (Method meth : methods) { + if (meth.getName().equals(method)) { + methodC = meth; + } + } + + if (methodC == null) { + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "未找到对应的方法!"); + return baseResponse; + } + + Parameter[] parameters = methodC.getParameters(); + Object[] arg = new Object[parameters.length]; + + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + if (parameter.getType() == HttpServletRequest.class) { + arg[i] = httpServletRequest; + } else if (parameter.getType() == HttpServletResponse.class) { + arg[i] = httpServletResponse; + } else if (parameter.getType() == TreeNode.class) { + arg[i] = MapperUtil.toTree(data); + } else if (parameter.getType() == String.class) { + arg[i] = data; + } else if (parameter.getType() == MultipartFile.class) { + arg[i] = file; + } else if (BaseRequest.class.isAssignableFrom(parameter.getType())) { + arg[i] = MapperUtil.toJava(data, parameter.getType()); + } else if (parameter.getType() == Token.class) { + arg[i] = LocalData.getToken(); + } else { + arg[i] = null; + } + } + return methodC.invoke(ajax, arg); + } catch (BeansException e) { + e.printStackTrace(); + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "未找到对应的目标!"); + return baseResponse; + } catch (IllegalAccessException e) { + e.printStackTrace(); + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "方法执必须公开!"); + return baseResponse; + } catch (InvocationTargetException e) { + e.getTargetException().printStackTrace(); + BaseResponse baseResponse = new BaseResponse(); + baseResponse.addError(ErrorType.BUSINESS_ERROR, "方法执行错误[" + e.getTargetException().getMessage() + "]"); + return baseResponse; + } + } + + + @RequestMapping(path = "/api/{module}/{target}/{method}", method = RequestMethod.POST) + @ResponseBody + public String api( + @PathVariable String module, + @PathVariable String target, + @PathVariable String method, + @RequestParam(required = false) String appKey, + @RequestParam(required = false) String sign, + @RequestParam(required = false) Long timestamp, + @RequestParam(required = false) String token, + @RequestParam(required = false) String encryptData, + HttpServletRequest httpServletRequest, + HttpServletResponse httpServletResponse) { + BaseResponse response = new BaseResponse(); + if (appKey == null) { + response.addError(ErrorType.BUSINESS_ERROR, "应用码参数[appKey]不存在!"); + return MapperUtil.toJson(response); + } else if (sign == null) { + response.addError(ErrorType.BUSINESS_ERROR, "签名参数[sign]不存在!"); + return MapperUtil.toJson(response); + } else if (timestamp == null) { + response.addError(ErrorType.BUSINESS_ERROR, "时间戳参数[timestamp]不存在!"); + return MapperUtil.toJson(response); + } + + String data = null; + String appSecret = "1234567890123456"; + // 解码 + try { + data = AESUtil.decrypt2String(encryptData, appSecret); + } catch (Exception e) { + response.addError(ErrorType.BUSINESS_ERROR, "解码失败,请确认编码是否正确!"); + return MapperUtil.toJson(response); + } + + // 验证签名 + String sign_ = MD5Util.encode(appSecret + data + timestamp); + if (!sign_.equals(sign)) { + response.addError(ErrorType.BUSINESS_ERROR, "签名验证失败!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + // 时效性验证 + long currentTime = System.currentTimeMillis(); + if (currentTime - timestamp > 2 * 60 * 1000) { + response.addError(ErrorType.BUSINESS_ERROR, "请求过期, 或本地时间错误!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + // 权限验证 + if (!LocalData.getToken().hasRes(httpServletRequest.getServletPath())) { + response.addError(ErrorType.BUSINESS_ERROR, "[" + httpServletRequest.getServletPath() + "]未授权的资源!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + // 开始处理业务 + try { + String beanClassName = (ActionConfig.API_PREFIX + module + "/" + target).toLowerCase(); + Object ajax = LocalData.getApplicationContext().getBean(beanClassName); + Class ajaxClass = ajax.getClass(); + Method[] methods = ajaxClass.getDeclaredMethods(); + + Method methodC = null; + for (Method meth : methods) { + if (meth.getName().equals(method)) { + methodC = meth; + } + } + + if (methodC == null) { + response.addError(ErrorType.BUSINESS_ERROR, "未找到对应的方法!"); + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + Parameter[] parameters = methodC.getParameters(); + Object[] arg = new Object[parameters.length]; + + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + if (parameter.getType() == HttpServletRequest.class) { + arg[i] = httpServletRequest; + } else if (parameter.getType() == HttpServletResponse.class) { + arg[i] = httpServletResponse; + } else if (BaseRequest.class.isAssignableFrom(parameter.getType())) { + arg[i] = MapperUtil.toJava(data, parameter.getType()); + } else if (parameter.getType() == TreeNode.class) { + arg[i] = MapperUtil.toTree(data); + } else if (parameter.getType() == Token.class) { + arg[i] = LocalData.getToken(); + } else { + arg[i] = null; + } + } + response = (BaseResponse) methodC.invoke(ajax, arg); + } catch (BeansException e) { + e.printStackTrace(); + response.addError(ErrorType.BUSINESS_ERROR, "未找到对应的目标!"); + } catch (IllegalAccessException e) { + e.printStackTrace(); + response.addError(ErrorType.BUSINESS_ERROR, "方法执必须公开!"); + } catch (InvocationTargetException e) { + LogUtil.dumpException(e.getTargetException()); + e.getTargetException().printStackTrace(); + response.addError(ErrorType.BUSINESS_ERROR, "方法执行错误[" + e.getTargetException().getMessage() + "]"); + } + return AESUtil.encrypt2Base64(MapperUtil.toJson(response).getBytes(), appSecret); + } + + private static ConcurrentHashMap sseMap = new ConcurrentHashMap(); + + /** + * Sse推送服务,服务器向js推送自定义消息 + * Sse容器{@link GlobalController#sseMap} + * Sse批量推送{@link GlobalController#pushAll} + */ + @RequestMapping(value = "/sse/{userId}", produces = "text/event-stream;charset=UTF-8") + public SseEmitter sse(@PathVariable String userId) { + SseEmitter sseEmitter = new SseEmitter(10000000L); + if (sseMap.get(userId) != null) { + sseMap.remove(userId); + } + sseMap.put(userId, sseEmitter); + return sseEmitter; + } + + /** + * Sse批量推送 + * + * @param data 推送对象 + */ + public static void pushAll(Object data) { + for (String s : sseMap.keySet()) { + try { + sseMap.get(s).send(MapperUtil.toJson(data), MediaType.APPLICATION_JSON); + } catch (IOException e) { + sseMap.remove(s); + } + } + } +} diff --git a/src/main/java/xyz/wbsite/action/ajax/conf/MappingAjax.java b/src/main/java/xyz/wbsite/action/ajax/conf/MappingAjax.java new file mode 100644 index 0000000..1cc0ff2 --- /dev/null +++ b/src/main/java/xyz/wbsite/action/ajax/conf/MappingAjax.java @@ -0,0 +1,56 @@ +package xyz.wbsite.action.ajax.conf; + +import com.fasterxml.jackson.core.TreeNode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.multipart.MultipartFile; +import xyz.wbsite.frame.excel.WExcel; +import xyz.wbsite.frame.utils.LogUtil; +import xyz.wbsite.frame.auth.LocalData; +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.utils.MapperUtil; +import xyz.wbsite.frame.base.BaseResponse; +import xyz.wbsite.frame.utils.ResponseUtil; +import xyz.wbsite.frame.utils.ValidationUtil; +import xyz.wbsite.frame.excel.exception.ReadErrorException; +import xyz.wbsite.frame.excel.exception.TemplateNotMatchException; +import xyz.wbsite.module.conf.ent.Mapping; +import xyz.wbsite.module.conf.mgr.MappingManager; +import xyz.wbsite.module.conf.req.MappingCreateRequest; +import xyz.wbsite.module.conf.req.MappingDeleteRequest; +import xyz.wbsite.module.conf.req.MappingFindRequest; +import xyz.wbsite.module.conf.req.MappingGetRequest; +import xyz.wbsite.module.conf.req.MappingUpdateRequest; +import xyz.wbsite.module.conf.rsp.MappingCreateResponse; +import xyz.wbsite.module.conf.rsp.MappingDeleteResponse; +import xyz.wbsite.module.conf.rsp.MappingFindResponse; +import xyz.wbsite.module.conf.rsp.MappingGetResponse; +import xyz.wbsite.module.conf.rsp.MappingUpdateResponse; + +import java.io.IOException; +import java.util.List; + +public class MappingAjax{ + + @Autowired + private MappingManager mappingManager; + + public MappingCreateResponse create(MappingCreateRequest request) { + return mappingManager.create(request, LocalData.getToken()); + } + + public MappingDeleteResponse delete(MappingDeleteRequest request) { + return mappingManager.delete(request, LocalData.getToken()); + } + + public MappingUpdateResponse update(MappingUpdateRequest request) { + return mappingManager.update(request, LocalData.getToken()); + } + + public MappingFindResponse find(MappingFindRequest request) { + return mappingManager.find(request, LocalData.getToken()); + } + + public MappingGetResponse get(MappingGetRequest request) { + return mappingManager.get(request, LocalData.getToken()); + } +} diff --git a/src/main/java/xyz/wbsite/action/ajax/system/UserAjax.java b/src/main/java/xyz/wbsite/action/ajax/system/UserAjax.java new file mode 100644 index 0000000..cf2fefa --- /dev/null +++ b/src/main/java/xyz/wbsite/action/ajax/system/UserAjax.java @@ -0,0 +1,63 @@ +package xyz.wbsite.action.ajax.system; + +import org.springframework.beans.factory.annotation.Value; +import xyz.wbsite.frame.auth.LocalData; +import xyz.wbsite.frame.auth.Token; +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.utils.CookieUtil; +import xyz.wbsite.frame.utils.IDgenerator; +import xyz.wbsite.frame.utils.MD5Util; +import xyz.wbsite.frame.utils.ValidationUtil; +import xyz.wbsite.module.system.req.UserLoginRequest; +import xyz.wbsite.module.system.req.UserLogoutRequest; +import xyz.wbsite.module.system.rsp.UserLoginResponse; +import xyz.wbsite.module.system.rsp.UserLogoutResponse; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Calendar; +import java.util.Date; + +public class UserAjax { + + @Value("${web.url.auth.admin}") + private String admin; + @Value("${web.url.auth.pwd}") + private String pwd; + + public UserLoginResponse login(UserLoginRequest request, Token token, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + UserLoginResponse response = new UserLoginResponse(); + + ValidationUtil.validate(request, response); + if (response.hasError()) { + return response; + } + + if (request.getUsername().equals(admin)) {//超级管理员登录 + String generatePwd = MD5Util.generatePwd(request.getPassword()); + if (!generatePwd.equals(pwd)) { + response.addError(ErrorType.BUSINESS_ERROR, "用户名或密码错误!"); + } else { + Date current = new Date(); + Calendar instance = Calendar.getInstance(); + instance.setTime(current); + instance.add(Calendar.HOUR_OF_DAY, 1);//默认一个小时内有效 + + Token sysToken = LocalData.getSysToken(); + sysToken.setToken(IDgenerator.nextUUID()); + + Cookie cookie = CookieUtil.newCookie("token", sysToken.getToken()); + httpServletResponse.addCookie(cookie); + response.setToken(sysToken.getToken()); + } + } + return response; + } + + public UserLogoutResponse logout(UserLogoutRequest request, Token token) { + UserLogoutResponse response = new UserLogoutResponse(); + CookieUtil.clearCookie("token"); + return response; + } +} diff --git a/src/main/java/xyz/wbsite/action/api/conf/MappingApi.java b/src/main/java/xyz/wbsite/action/api/conf/MappingApi.java new file mode 100644 index 0000000..ec6c567 --- /dev/null +++ b/src/main/java/xyz/wbsite/action/api/conf/MappingApi.java @@ -0,0 +1,33 @@ +package xyz.wbsite.action.api.conf; + +import xyz.wbsite.frame.auth.LocalData; +import xyz.wbsite.module.conf.mgr.MappingManager; +import xyz.wbsite.module.conf.req.*; +import xyz.wbsite.module.conf.rsp.*; +import org.springframework.beans.factory.annotation.Autowired; + +public class MappingApi{ + + @Autowired + private MappingManager mappingManager; + + public MappingCreateResponse create(MappingCreateRequest request) { + return mappingManager.create(request, LocalData.getToken()); + } + + public MappingDeleteResponse delete(MappingDeleteRequest request) { + return mappingManager.delete(request, LocalData.getToken()); + } + + public MappingUpdateResponse update(MappingUpdateRequest request) { + return mappingManager.update(request, LocalData.getToken()); + } + + public MappingFindResponse find(MappingFindRequest request) { + return mappingManager.find(request, LocalData.getToken()); + } + + public MappingGetResponse get(MappingGetRequest request) { + return mappingManager.get(request, LocalData.getToken()); + } +} diff --git a/src/main/java/xyz/wbsite/action/control/Header.java b/src/main/java/xyz/wbsite/action/control/Header.java new file mode 100644 index 0000000..c365fb7 --- /dev/null +++ b/src/main/java/xyz/wbsite/action/control/Header.java @@ -0,0 +1,15 @@ +package xyz.wbsite.action.control; + +import xyz.wbsite.frame.base.Control; +import org.springframework.ui.Model; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class Header extends Control { + + @Override + public void exec(Model model, HttpServletRequest request, HttpServletResponse response) { + + } +} diff --git a/src/main/java/xyz/wbsite/action/screen/Index.java b/src/main/java/xyz/wbsite/action/screen/Index.java new file mode 100644 index 0000000..261e2e5 --- /dev/null +++ b/src/main/java/xyz/wbsite/action/screen/Index.java @@ -0,0 +1,22 @@ +package xyz.wbsite.action.screen; + +import xyz.wbsite.frame.base.Screen; +import org.springframework.ui.Model; +import java.util.HashMap; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class Index extends Screen { + + @Override + public void exec(Model model, HttpServletRequest request, HttpServletResponse response) { + + // 获取配置 + HashMap prop = new HashMap<>(); + prop.put("open", true);//是否保持一个子菜单展开 + prop.put("coll", false);//左侧菜单是否收缩 + prop.put("tran", false);//是否展示动画 + prop.put("full", false);//是否全屏 + model.addAttribute("prop", prop); + } +} diff --git a/src/main/java/xyz/wbsite/config/ActionConfig.java b/src/main/java/xyz/wbsite/config/ActionConfig.java new file mode 100644 index 0000000..7d2e71e --- /dev/null +++ b/src/main/java/xyz/wbsite/config/ActionConfig.java @@ -0,0 +1,138 @@ +package xyz.wbsite.config; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.core.type.filter.TypeFilter; +import xyz.wbsite.frame.base.Control; +import xyz.wbsite.frame.base.Screen; +import xyz.wbsite.frame.utils.LogUtil; + +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 请求处理器配置类,Screen及Control + *

+ * 注册扫描Screen处理器 {@link ActionConfig#registryScreen} + * 注册扫描Control处理器 {@link ActionConfig#registryControl} + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Configuration +public class ActionConfig implements BeanDefinitionRegistryPostProcessor { + + public static String SCREEN_PREFIX = "/screen/"; + public static String CONTROL_PREFIX = "/control/"; + public static String AJAX_PREFIX = "/ajax/"; + public static String API_PREFIX = "/api/"; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { + String aPackage = this.getClass().getPackage().getName(); + Pattern compile = Pattern.compile("(.*)\\.config"); + Matcher matcher = compile.matcher(aPackage); + if (matcher.find()) { + String basePackage = matcher.group(1); + registryScreen(basePackage + ".action.screen", beanDefinitionRegistry); + registryControl(basePackage + ".action.control", beanDefinitionRegistry); + registryAjax(basePackage + ".action.ajax", beanDefinitionRegistry); + registryApi(basePackage + ".action.api", beanDefinitionRegistry); + } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { + + } + + private int registryScreen(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.resetFilters(false); + classPathBeanDefinitionScanner.addIncludeFilter(new AssignableTypeFilter(Screen.class)); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + String beamName = beanClassName.replaceAll(basePackage + ".", SCREEN_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry screen " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } + + private int registryControl(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.resetFilters(false); + classPathBeanDefinitionScanner.addIncludeFilter(new AssignableTypeFilter(Control.class)); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + String beamName = beanClassName.replaceAll(basePackage + ".", CONTROL_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry control " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } + + private int registryAjax(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.resetFilters(false); + classPathBeanDefinitionScanner.addIncludeFilter(new TypeFilter() { + @Override + public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { + return true; + } + }); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + if (beanClassName != null && beanClassName.endsWith("Ajax")) { + beanClassName = beanClassName.substring(0, beanClassName.length() - 4); + } + String beamName = beanClassName.replaceAll(basePackage + ".", AJAX_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry ajax " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } + + private int registryApi(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.addIncludeFilter(new TypeFilter() { + @Override + public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { + return true; + } + }); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + if (beanClassName != null && beanClassName.endsWith("Api")) { + beanClassName = beanClassName.substring(0, beanClassName.length() - 3); + } + String beamName = beanClassName.replaceAll(basePackage + ".", API_PREFIX).replaceAll("\\.","/").toLowerCase(); + LogUtil.i("registry api " + beamName); + return beamName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } +} diff --git a/src/main/java/xyz/wbsite/config/CacheConfig.java b/src/main/java/xyz/wbsite/config/CacheConfig.java new file mode 100644 index 0000000..aed761c --- /dev/null +++ b/src/main/java/xyz/wbsite/config/CacheConfig.java @@ -0,0 +1,45 @@ +package xyz.wbsite.config; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.config.CacheConfiguration; +import net.sf.ehcache.config.DiskStoreConfiguration; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.ehcache.EhCacheCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class CacheConfig { + + public static final String TOKEN_CACHE = "tokenCache"; + + @Bean + public EhCacheCacheManager getCacheManager() { + net.sf.ehcache.config.Configuration configuration = new net.sf.ehcache.config.Configuration(); + // todo 需根据服务器物理内存配置 + configuration.setMaxBytesLocalHeap("128M"); + configuration.updateCheck(false); + configuration.addDiskStore(new DiskStoreConfiguration().path("java.io.tmpdir")); + CacheManager cacheManager = CacheManager.create(configuration); + + // 添加token缓存 + cacheManager.addCache(buildTokenCache()); + return new EhCacheCacheManager(cacheManager); + } + + /** + * 构建TokenCache + * + * @return 缓存 + */ + private Cache buildTokenCache() { + CacheConfiguration config = new CacheConfiguration(); + config.setMemoryStoreEvictionPolicy("LFU");//最少使用 + config.setTimeToLiveSeconds(60 * 60);//最长有效时间 + config.setTimeToIdleSeconds(60 * 60);//无访问最长有效时间 + config.setName(TOKEN_CACHE); + return new Cache(config); + } +} diff --git a/src/main/java/xyz/wbsite/config/FreeMarkerConfig.java b/src/main/java/xyz/wbsite/config/FreeMarkerConfig.java new file mode 100644 index 0000000..513c9d5 --- /dev/null +++ b/src/main/java/xyz/wbsite/config/FreeMarkerConfig.java @@ -0,0 +1,124 @@ +package xyz.wbsite.config; + +import freemarker.template.SimpleScalar; +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.TemplateModelException; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.support.BindingAwareModelMap; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.View; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; +import xyz.wbsite.frame.auth.LocalData; +import xyz.wbsite.frame.base.Control; +import xyz.wbsite.frame.utils.UrlUtil; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.util.List; +import java.util.Locale; + +import static xyz.wbsite.config.ActionConfig.CONTROL_PREFIX; +import static xyz.wbsite.config.ActionConfig.SCREEN_PREFIX; + +@Configuration +public class FreeMarkerConfig { + @Autowired + private FreeMarkerViewResolver viewResolver; + @Autowired + private freemarker.template.Configuration configuration; + @Value("${server.servlet.context-path}") + private String context; + + private String suffix = ".ftl"; + + @PostConstruct + public void setSharedVariable() throws TemplateModelException { + // 全局共享变量、函数 + configuration.setSharedVariable("context", context); + configuration.setSharedVariable("screenHolder", new ScreenHolder()); + configuration.setSharedVariable("controlHolder", new ControlHolder()); + configuration.setSharedVariable("UrlUtil", new UrlUtil()); + } + + private class ScreenHolder implements TemplateMethodModelEx { + + @Override + public Object exec(List list) throws TemplateModelException { + try { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + LocaleResolver localeResolver = (LocaleResolver) request.getAttribute(DispatcherServlet.LOCALE_RESOLVER_ATTRIBUTE); + String servletPath = LocalData.getAction(); + servletPath = servletPath.replaceAll("^/", ""); + + String[] split = servletPath.split("/"); + StringBuilder sb = new StringBuilder(""); + + // 分割组装路径 + for (int i = 0; i < split.length; i++) { + sb.append(split[i]); + if (i != split.length - 1) { + sb.append(File.separator); + } + } + + Locale locale = localeResolver.resolveLocale(request); + String viewName = "screen" + File.separator + sb.toString(); + View view = viewResolver.resolveViewName(viewName, locale); + //无法找到对应screen + if (view == null) { + return ""; + } else { + return SCREEN_PREFIX + servletPath + suffix; + } + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + } + + private class ControlHolder implements TemplateMethodModelEx { + + @Override + public Object exec(List list) throws TemplateModelException { + String control = ""; + if (list.size() != 1) { + return ""; + } + Object o = list.get(0); + if (o instanceof SimpleScalar) { + control = ((SimpleScalar) o).getAsString(); + } + + // 查找是否存在对应控制面板执行器 + Control controlExec = null; + try { + String beanClassName = (CONTROL_PREFIX + control).toLowerCase(); + controlExec = LocalData.getApplicationContext().getBean(beanClassName, Control.class); + + HttpServletRequest request = LocalData.getRequest(); + HttpServletResponse response = LocalData.getResponse(); + + BindingAwareModelMap modelMap = new BindingAwareModelMap(); + controlExec.exec(modelMap, request, response); + + for (String key : modelMap.keySet()) { + request.setAttribute(key, modelMap.get(key)); + } + } catch (BeansException e) { + + } + + control = control.replaceAll("/", File.separator); + return CONTROL_PREFIX + control + suffix; + } + } +} diff --git a/src/main/java/xyz/wbsite/config/SQLiteConfig.java b/src/main/java/xyz/wbsite/config/SQLiteConfig.java new file mode 100644 index 0000000..f85b259 --- /dev/null +++ b/src/main/java/xyz/wbsite/config/SQLiteConfig.java @@ -0,0 +1,32 @@ +package xyz.wbsite.config; + +import xyz.wbsite.frame.utils.ResourceUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Configuration +public class SQLiteConfig { + + @Value("${spring.datasource.url}") + private String url; + + @PostConstruct + public void generateDB() { + Pattern compile = Pattern.compile("jdbc:sqlite:(.*.db).*"); + Matcher matcher = compile.matcher(url); + if (matcher.find()) { + String group = matcher.group(1); + File file = new File(group); + if (!file.exists()) { + File path = file.getAbsoluteFile().getParentFile(); + if (!path.exists()) path.mkdirs(); + ResourceUtil.copyResource2File("nginx-admin.db", file); + } + } + } +} diff --git a/src/main/java/xyz/wbsite/config/ScheduleConfig.java b/src/main/java/xyz/wbsite/config/ScheduleConfig.java new file mode 100644 index 0000000..f5c9ed8 --- /dev/null +++ b/src/main/java/xyz/wbsite/config/ScheduleConfig.java @@ -0,0 +1,91 @@ +package xyz.wbsite.config; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Component; +import xyz.wbsite.frame.schedule.RunTask; +import xyz.wbsite.frame.utils.LogUtil; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 计划调度配置,可以指定环境生效,根据实际情况是否需要开启 + * 开启方法:将@Profile("dev") 删除(所有环境生效) 或改为对应环境 + */ +@Configuration +@Component +public class ScheduleConfig extends ThreadPoolTaskScheduler implements BeanDefinitionRegistryPostProcessor { + + private Map> classMap; + + private Map> futureMap; + + public ScheduleConfig() { + classMap = new HashMap<>(); + futureMap = new HashMap<>(); + setPoolSize(4); + initialize(); + } + + public boolean createOrRepeat(RunTask task) { + if (futureMap.containsKey(task.taskId())) { + ScheduledFuture scheduledFuture = futureMap.get(task.taskId()); + scheduledFuture.cancel(false); + } + classMap.put(task.taskId(), (Class) task.getClass()); + futureMap.put(task.taskId(), task.schedule(this)); + return true; + } + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException { + String aPackage = this.getClass().getPackage().getName(); + Pattern compile = Pattern.compile("(.*)\\.config"); + Matcher matcher = compile.matcher(aPackage); + if (matcher.find()) { + String basePackage = matcher.group(1); + registryTask(basePackage, beanDefinitionRegistry); + } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { + + } + + private int registryTask(String basePackage, BeanDefinitionRegistry beanDefinitionRegistry) { + ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry); + classPathBeanDefinitionScanner.resetFilters(false); + classPathBeanDefinitionScanner.addIncludeFilter(new AssignableTypeFilter(RunTask.class)); + classPathBeanDefinitionScanner.setBeanNameGenerator(new BeanNameGenerator() { + @Override + public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) { + String beanClassName = beanDefinition.getBeanClassName(); + try { + Class aClass = Class.forName(beanClassName); + Object instance = aClass.newInstance(); + RunTask task = (RunTask) instance; + ScheduleConfig.this.createOrRepeat(task); + } catch (Exception e) { + e.printStackTrace(); + } + LogUtil.i("registry task " + beanClassName); + return beanClassName; + } + }); + return classPathBeanDefinitionScanner.scan(basePackage); + } +} diff --git a/src/main/java/xyz/wbsite/config/SecurityConfig.java b/src/main/java/xyz/wbsite/config/SecurityConfig.java new file mode 100644 index 0000000..52be9be --- /dev/null +++ b/src/main/java/xyz/wbsite/config/SecurityConfig.java @@ -0,0 +1,130 @@ +package xyz.wbsite.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import xyz.wbsite.frame.auth.LocalData; +import xyz.wbsite.frame.auth.Token; +import xyz.wbsite.frame.utils.CookieUtil; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Configuration +@EnableGlobalMethodSecurity(securedEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Value("${web.url.auth.included}") + private String[] included; + @Value("${web.url.auth.excluded}") + private String[] excluded; + @Value("${spring.mvc.static-path-pattern}") + private String[] staticPath; + @Value("${web.login.page}") + private String loginPage; + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().mvcMatchers(staticPath); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .addFilterBefore(new TokenFilter(), FilterSecurityInterceptor.class)// 过滤器用于处理Token + .authorizeRequests() + .antMatchers(excluded).permitAll()// 放行排除的URL + .antMatchers(included).access("@Authorization.hasPermission(request,authentication)")// 需要权限的URL + .and().cors() + .and().headers().frameOptions().disable() + .and().csrf().disable(); + } + + /** + * 此方法不要删除 用于屏蔽默认用户密码生成 + *

+ * 例如 Using generated security password: f6b42a66-71b1-4c31-b6a8-942838c81408 + * + * @return + * @throws Exception + */ + @Bean + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + public static class TokenFilter implements Filter { + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + String token = request.getParameter("token"); + if (token == null || token.isEmpty()) { + token = CookieUtil.getCookieValue(request.getCookies(), "token"); + } + + // 组装Token ~ 这边根据实际的业务组装Token + if (token != null) { + LocalData.setToken( LocalData.getSysToken()); + } else { + LocalData.setToken(null); + } + + // Action + String servletPath = request.getServletPath().toLowerCase(); + Pattern compile = Pattern.compile("^/(.+)\\.htm"); + Matcher matcher = compile.matcher(servletPath); + if (matcher.find()) { + LocalData.setAction(matcher.group(1)); + } + + try { + filterChain.doFilter(servletRequest, servletResponse); + } catch (AccessDeniedException e) { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } catch (Exception e) { + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + } + + @Bean("Authorization") + public Object getAuthorization() { + return new Object() { + public boolean hasPermission(HttpServletRequest request, Authentication authentication) { + + Token token_ = LocalData.getToken(); + if (token_ == null) { + return false; + } + + String path = request.getServletPath(); + if (token_.hasRes(path)) { + return true; + } + + return false; + } + }; + } + +} diff --git a/src/main/java/xyz/wbsite/config/ThreadPoolConfig.java b/src/main/java/xyz/wbsite/config/ThreadPoolConfig.java new file mode 100644 index 0000000..354e761 --- /dev/null +++ b/src/main/java/xyz/wbsite/config/ThreadPoolConfig.java @@ -0,0 +1,56 @@ +package xyz.wbsite.config; + +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.Nullable; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.lang.reflect.Method; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; + +@Configuration +@EnableAsync +public class ThreadPoolConfig implements AsyncConfigurer { + private ThreadPoolTaskExecutor executor; + + private int corePoolSize = 10;//线程池维护线程的最少数量 + + private int maxPoolSize = 30;//线程池维护线程的最大数量 + + private int queueCapacity = 8; //缓存队列 + + private int keepAlive = 60;//允许的空闲时间 + + @Nullable + @Override + public Executor getAsyncExecutor() { + executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(corePoolSize); + executor.setMaxPoolSize(maxPoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setThreadNamePrefix("mqExecutor-"); + // rejection-policy:当pool已经达到max size的时候,如何处理新任务 + // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //对拒绝task的处理策略 + executor.setKeepAliveSeconds(keepAlive); + executor.initialize(); + return executor; + } + + + @Nullable + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return new AsyncUncaughtExceptionHandler() { + + @Override + public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { + System.out.println("-------------》》》捕获线程异常信息"); + } + }; + } + +} diff --git a/src/main/java/xyz/wbsite/config/WebMvcConfig.java b/src/main/java/xyz/wbsite/config/WebMvcConfig.java new file mode 100644 index 0000000..6c023a8 --- /dev/null +++ b/src/main/java/xyz/wbsite/config/WebMvcConfig.java @@ -0,0 +1,122 @@ +package xyz.wbsite.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; +import xyz.wbsite.frame.utils.LogUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Value("${web.home.page}") + private String homePage; + @Value("${spring.mvc.static-path-pattern}") + private String[] staticPath; + + /** + * 增加全局拦截器,可用于异常日志的收集 + * + * @param registry + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + // 全局异常收集拦截器 + registry.addInterceptor(new HandlerInterceptorAdapter() { + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + super.afterCompletion(request, response, handler, ex); + + if (ex != null) { + LogUtil.dumpException(ex); + } + } + + }).addPathPatterns("/**").excludePathPatterns(staticPath); + } + + /** + * Jackson序列化时的转化规则配置 + *

+ * 1、Long或long类型在序列化时转String,防止出现javascript中Number精度丢失的情况。 + * + * @param converters + */ + @Override + public void extendMessageConverters(List> converters) { + for (HttpMessageConverter converter : converters) { + if (converter instanceof MappingJackson2HttpMessageConverter) { + ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper(); + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(Long.class, ToStringSerializer.instance); + simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); + objectMapper.registerModule(simpleModule); + } + } + } + +// @Bean +// @Profile("prod") +// public ServletWebServerFactory servletContainer() { +// TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); +// +// // 基本参数 +// String keyStore = "1754557_www.wbsite.xyz.pfx"; +// String keyStorePassword = "s98n7CLd"; +// String keyStoreType = "PKCS12"; +// int httpsPort = 443; +// +// File keystore = null; +// // 正常开发可以通过getFile()获取,打包jar后无法直接获取File对象,需将文件考出 +// try { +// keystore = new ClassPathResource(keyStore).getFile(); +// } catch (IOException ex) { +// try { +// ApplicationHome home = new ApplicationHome(getClass()); +// // 当前运行jar文件 +// File jarFile = home.getSource(); +// //jar同目录 +// keystore = new File(jarFile.getParent(), keyStore); +// +// InputStream inputStream = new ClassPathResource(keyStore).getInputStream(); +// byte[] bytes = new byte[inputStream.available()]; +// +// inputStream.read(bytes); +// +// inputStream.close(); +// +// FileOutputStream fileOutputStream = new FileOutputStream(keystore); +// fileOutputStream.write(bytes); +// fileOutputStream.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// +// // 创建Connector +// Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); +// Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); +// connector.setScheme("https"); +// connector.setSecure(true); +// connector.setPort(httpsPort); +// protocol.setSSLEnabled(true); +// protocol.setKeystoreFile(keystore.getAbsolutePath()); +// protocol.setKeystorePass(keyStorePassword); +// protocol.setKeystoreType(keyStoreType); +// +// // 添加 +// tomcat.addAdditionalTomcatConnectors(connector); +// return tomcat; +// } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/frame/auth/LocalData.java b/src/main/java/xyz/wbsite/frame/auth/LocalData.java new file mode 100644 index 0000000..cd6f41b --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/auth/LocalData.java @@ -0,0 +1,95 @@ +package xyz.wbsite.frame.auth; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import xyz.wbsite.frame.auth.Token; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * LocalData - 本地数据存放类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class LocalData { + + private static ApplicationContext applicationContext = null; + + private static Token system = null; + + static { + // 组装系统Token + system = new Token(); + system.setId(0); + system.setUserId(0); + system.setUserName("system"); + system.putRes(".*"); + } + + public static Token getSysToken() { + return system; + } + + /** + * 当请求目标 target = '/aa/bb' + */ + private static final ThreadLocal actionHolder = new ThreadLocal(); + + public static String getAction() { + return actionHolder.get(); + } + + public static void setAction(String action) { + actionHolder.set(action); + } + + /** + * 当前用户的通行证 + */ + private static final ThreadLocal tokenHolder = new ThreadLocal(); + + public static Token getToken() { + return tokenHolder.get(); + } + + public static void setToken(Token token) { + tokenHolder.set(token); + } + + public static HttpServletRequest getRequest() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + } + + public static HttpServletResponse getResponse() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); + } + + public static ApplicationContext getApplicationContext() { + return LocalData.applicationContext; + } + + public static void setApplicationContext(ApplicationContext applicationContext) { + LocalData.applicationContext = applicationContext; + } + + public static T getBean(Class t) { + if (getApplicationContext() == null) { + return null; + } + try { + return getApplicationContext().getBean(t); + } catch (BeansException ignored) { + return null; + } + } + + public static Environment getEnvironment() { + return getBean(Environment.class); + } +} diff --git a/src/main/java/xyz/wbsite/frame/auth/Token.java b/src/main/java/xyz/wbsite/frame/auth/Token.java new file mode 100644 index 0000000..ac4274f --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/auth/Token.java @@ -0,0 +1,91 @@ +package xyz.wbsite.frame.auth; + +import xyz.wbsite.frame.utils.IDgenerator; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * Token - 通行证类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class Token implements Serializable { + private static final Long serialVersionUID = 1L; + /** + * ID + */ + private long id; + /** + * TOKEN + */ + private String token; + /** + * 用户ID + */ + private long userId; + /** + * 用户名称 + */ + private String userName; + + private Set resSet = new HashSet<>(); + + public boolean hasRes(String res) { + for (String s : resSet) { + if (res.matches(s)) { + return true; + } + } + + return false; + } + + public void putRes(String resource) { + resSet.add(resource); + } + + public void putRes(Set resourceSet) { + this.resSet.addAll(resourceSet); + } + + + public Set getResSet() { + return resSet; + } + + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/src/main/java/xyz/wbsite/frame/base/BaseEntity.java b/src/main/java/xyz/wbsite/frame/base/BaseEntity.java new file mode 100644 index 0000000..2bdb6f1 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/BaseEntity.java @@ -0,0 +1,110 @@ +package xyz.wbsite.frame.base; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.io.Serializable; +import java.util.Date; + +/** + * Base - 基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BaseEntity implements Serializable { + + /** + * 主键 + */ + private long id; + + /** + * 行版本 + */ + private long rowVersion; + + /** + * 创建用户 + */ + @JsonIgnore + private long createBy; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 最后更新用户 + */ + @JsonIgnore + private long lastUpdateBy; + + /** + * 最后更新时间 + */ + @JsonIgnore + private Date lastUpdateTime; + + /** + * 是否删除 + */ + @JsonIgnore + private boolean isDeleted; + + public long getRowVersion() { + return rowVersion; + } + + public void setRowVersion(long rowVersion) { + this.rowVersion = rowVersion; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getCreateBy() { + return createBy; + } + + public void setCreateBy(long createBy) { + this.createBy = createBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public long getLastUpdateBy() { + return lastUpdateBy; + } + + public void setLastUpdateBy(long lastUpdateBy) { + this.lastUpdateBy = lastUpdateBy; + } + + public Date getLastUpdateTime() { + return lastUpdateTime; + } + + public void setLastUpdateTime(Date lastUpdateTime) { + this.lastUpdateTime = lastUpdateTime; + } + + public boolean getIsDeleted() { + return isDeleted; + } + + public void setIsDeleted(boolean deleted) { + isDeleted = deleted; + } +} diff --git a/src/main/java/xyz/wbsite/frame/base/BaseFindRequest.java b/src/main/java/xyz/wbsite/frame/base/BaseFindRequest.java new file mode 100644 index 0000000..448fbe6 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/BaseFindRequest.java @@ -0,0 +1,52 @@ +package xyz.wbsite.frame.base; + +/** + * BaseFindRequest - 基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BaseFindRequest extends BaseRequest { + + private int pageNumber = 1; + + private int pageSize = 10; + + private String sortKey; + + private SortType sortType; + + public int getPageNumber() { + return pageNumber; + } + + public void setPageNumber(int pageNumber) { + this.pageNumber = pageNumber; + } + + public int getPageSize() { + return pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public String getSortKey() { + return sortKey; + } + + public void setSortKey(String sortKey) { + this.sortKey = sortKey; + } + + public SortType getSortType() { + return sortType; + } + + public void setSortType(SortType sortType) { + this.sortType = sortType; + } + +} diff --git a/src/main/java/xyz/wbsite/frame/base/BaseFindResponse.java b/src/main/java/xyz/wbsite/frame/base/BaseFindResponse.java new file mode 100644 index 0000000..d351c2e --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/BaseFindResponse.java @@ -0,0 +1,33 @@ +package xyz.wbsite.frame.base; + +import java.util.List; + +/** + * BaseFindResponse - 基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BaseFindResponse extends BaseResponse{ + + private List result; + + private Long totalCount; + + public List getResult() { + return result; + } + + public void setResult(List result) { + this.result = result; + } + + public Long getTotalCount() { + return totalCount; + } + + public void setTotalCount(Long totalCount) { + this.totalCount = totalCount; + } +} diff --git a/src/main/java/xyz/wbsite/frame/base/BaseRequest.java b/src/main/java/xyz/wbsite/frame/base/BaseRequest.java new file mode 100644 index 0000000..a24b7c9 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/BaseRequest.java @@ -0,0 +1,12 @@ +package xyz.wbsite.frame.base; + +/** + * BaseRequest - 基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BaseRequest { + +} diff --git a/src/main/java/xyz/wbsite/frame/base/BaseResponse.java b/src/main/java/xyz/wbsite/frame/base/BaseResponse.java new file mode 100644 index 0000000..8766d33 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/BaseResponse.java @@ -0,0 +1,38 @@ +package xyz.wbsite.frame.base; + +import java.util.ArrayList; +import java.util.List; + +/** + * BaseResponse - 基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BaseResponse { + private List errors = new ArrayList(); + + public void addError(Error error){ + this.errors.add(error); + } + + public void addError(ErrorType type,String message){ + this.errors.add(new Error(type,message)); + } + + public void addErrors(List errors){ + this.errors.addAll(errors); + } + + public boolean hasError(){ + return this.errors.size() > 0; + } + + /** + * 获取全部的错误信息,返回的是副本 + */ + public List getErrors() { + return new ArrayList(errors); + } +} diff --git a/src/main/java/xyz/wbsite/frame/base/BaseSearchRequest.java b/src/main/java/xyz/wbsite/frame/base/BaseSearchRequest.java new file mode 100644 index 0000000..62cd20f --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/BaseSearchRequest.java @@ -0,0 +1,24 @@ +package xyz.wbsite.frame.base; + +/** + * BaseSearchRequest - 基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BaseSearchRequest extends BaseFindRequest { + + /** + * 模糊查询的关键字。 + */ + private String keyword; + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } +} diff --git a/src/main/java/xyz/wbsite/frame/base/BaseUpdateRequest.java b/src/main/java/xyz/wbsite/frame/base/BaseUpdateRequest.java new file mode 100644 index 0000000..9a8578d --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/BaseUpdateRequest.java @@ -0,0 +1,24 @@ +package xyz.wbsite.frame.base; + +/** + * BaseUpdateRequest - 基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BaseUpdateRequest extends BaseRequest{ + + /** + * 版本戳 + */ + private long rowVersion; + + public long getRowVersion() { + return rowVersion; + } + + public void setRowVersion(long rowVersion) { + this.rowVersion = rowVersion; + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/frame/base/Control.java b/src/main/java/xyz/wbsite/frame/base/Control.java new file mode 100644 index 0000000..1074730 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/Control.java @@ -0,0 +1,10 @@ +package xyz.wbsite.frame.base; + +import org.springframework.ui.Model; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public abstract class Control { + public abstract void exec(Model model, HttpServletRequest request, HttpServletResponse response); +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/frame/base/Error.java b/src/main/java/xyz/wbsite/frame/base/Error.java new file mode 100644 index 0000000..72cc105 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/Error.java @@ -0,0 +1,41 @@ +package xyz.wbsite.frame.base; + +/** + * Error - 错误基类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class Error { + + /*错误类型*/ + private ErrorType type; + + /*错误内容*/ + private String message; + + public Error() { + } + + public Error(ErrorType type, String message) { + this.type = type; + this.message = message; + } + + public ErrorType getType() { + return type; + } + + public void setType(ErrorType type) { + this.type = type; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/src/main/java/xyz/wbsite/frame/base/ErrorType.java b/src/main/java/xyz/wbsite/frame/base/ErrorType.java new file mode 100644 index 0000000..17fb52c --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/ErrorType.java @@ -0,0 +1,17 @@ +package xyz.wbsite.frame.base; + +/** + * ErrorType - 错误类型 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public enum ErrorType { + BUSINESS_ERROR, + SYSTEM_ERROR, + UNKNOWN_ERROR, + UNIQUENESS_ERROR, + EXPECTATION_NULL, + INVALID_PARAMETER +} diff --git a/src/main/java/xyz/wbsite/frame/base/Screen.java b/src/main/java/xyz/wbsite/frame/base/Screen.java new file mode 100644 index 0000000..c2c66ed --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/Screen.java @@ -0,0 +1,10 @@ +package xyz.wbsite.frame.base; + +import org.springframework.ui.Model; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public abstract class Screen { + public abstract void exec(Model model, HttpServletRequest request, HttpServletResponse response); +} diff --git a/src/main/java/xyz/wbsite/frame/base/SortType.java b/src/main/java/xyz/wbsite/frame/base/SortType.java new file mode 100644 index 0000000..3351d9d --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/base/SortType.java @@ -0,0 +1,17 @@ +package xyz.wbsite.frame.base; + +/** + * SortTypeEnum - 排序方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public enum SortType { + + //升序 + ASC, + + //降序 + DESC +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/frame/excel/WCell.java b/src/main/java/xyz/wbsite/frame/excel/WCell.java new file mode 100644 index 0000000..a1cc37b --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/WCell.java @@ -0,0 +1,47 @@ +package xyz.wbsite.frame.excel; + +import java.io.Serializable; + +/** + * WCell - Excel单元格对象 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class WCell implements Serializable { + + /** + * 单元格的值,任意格式都将转化为字符类型 + */ + private String value; + + /** + * 单元格存在的错误,只在excel导入时会产生错误 + */ + private String error; + + public WCell() { + this.value = ""; + } + + public WCell(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/WColumn.java b/src/main/java/xyz/wbsite/frame/excel/WColumn.java new file mode 100644 index 0000000..0fbcb2f --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/WColumn.java @@ -0,0 +1,146 @@ +package xyz.wbsite.frame.excel; + + +import xyz.wbsite.frame.excel.converter.Converter; + +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * WColumn - Excel列对象(包含列名,长度,必须项,列描述,指定转换器) + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class WColumn implements Serializable { + /** + * 列名 + */ + private String name = ""; + /** + * 列宽度 + */ + private int cellWidth = 8; + /** + * 是否是必输列 + */ + private boolean isRequired = false; + /** + * 该列的描述字段 + */ + private String description = ""; + /** + * 列转换器 + */ + private Converter converter; + /** + * Field对象 + */ + private Field field; + /** + * set方法 + */ + private Method setMethod; + /** + * get方法 + */ + private Method getMethod; + /** + * ExcelType + */ + private int cellType = 1; + /** + * 下拉列表 + */ + private String[] cellList; + + public WColumn() { + this.name = ""; + } + + public WColumn(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Field getField() { + return field; + } + + public void setField(Field field) { + this.field = field; + } + + public int getCellWidth() { + return cellWidth; + } + + public void setCellWidth(int cellWidth) { + this.cellWidth = cellWidth; + } + + public boolean isRequired() { + return isRequired; + } + + public void setRequired(boolean isRequired) { + this.isRequired = isRequired; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Converter getConverter() { + return converter; + } + + public void setConverter(Converter converter) { + this.converter = converter; + } + + public int getCellType() { + return cellType; + } + + public void setCellType(int cellType) { + this.cellType = cellType; + } + + public Method getSetMethod() { + return setMethod; + } + + public void setSetMethod(Method setMethod) { + this.setMethod = setMethod; + } + + public Method getGetMethod() { + return getMethod; + } + + public void setGetMethod(Method getMethod) { + this.getMethod = getMethod; + } + + public String[] getCellList() { + return cellList; + } + + public void setCellList(String[] cellList) { + this.cellList = cellList; + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/WExcel.java b/src/main/java/xyz/wbsite/frame/excel/WExcel.java new file mode 100644 index 0000000..0032f59 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/WExcel.java @@ -0,0 +1,681 @@ +package xyz.wbsite.frame.excel; + +import org.apache.poi.hssf.usermodel.HSSFDateUtil; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFComment; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.ss.util.NumberToTextConverter; +import xyz.wbsite.frame.excel.annotation.ColumnDescription; +import xyz.wbsite.frame.excel.annotation.ColumnList; +import xyz.wbsite.frame.excel.annotation.ColumnName; +import xyz.wbsite.frame.excel.annotation.Convert; +import xyz.wbsite.frame.excel.annotation.Ignore; +import xyz.wbsite.frame.excel.annotation.ParentFirst; +import xyz.wbsite.frame.excel.annotation.SheetName; +import xyz.wbsite.frame.excel.converter.BooleanConverter; +import xyz.wbsite.frame.excel.converter.ByteConverter; +import xyz.wbsite.frame.excel.converter.CharacterConverter; +import xyz.wbsite.frame.excel.converter.Converter; +import xyz.wbsite.frame.excel.converter.DateConverter; +import xyz.wbsite.frame.excel.converter.DoubleConverter; +import xyz.wbsite.frame.excel.converter.FloatConverter; +import xyz.wbsite.frame.excel.converter.IntegerConverter; +import xyz.wbsite.frame.excel.converter.LongConverter; +import xyz.wbsite.frame.excel.converter.ShortConverter; +import xyz.wbsite.frame.excel.converter.StringConverter; +import xyz.wbsite.frame.excel.exception.ReadErrorException; +import xyz.wbsite.frame.excel.exception.TemplateNotMatchException; +import xyz.wbsite.frame.excel.exception.ValueConverterException; +import xyz.wbsite.frame.excel.style.DataCellStyle; +import xyz.wbsite.frame.excel.style.ErrorCellStyle; +import xyz.wbsite.frame.excel.style.HeadCellStyle; +import xyz.wbsite.frame.excel.style.RedFont; +import xyz.wbsite.frame.excel.style.SuccessCellStyle; +import xyz.wbsite.frame.utils.ClassUtil; +import xyz.wbsite.frame.utils.FileUtil; +import xyz.wbsite.frame.utils.LogUtil; +import xyz.wbsite.frame.utils.ValidationUtil; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + * WExcel - 抽象的Excel对象数据集合 + * + * 导出 + * new WExcel(Dept.class).loadData(depts).toFile(file);
+ * + * 导入 + * byte[] bytes = FileUtil.readFileToByteArray(new File("E:\\E.xlsx"));
+ * WExcel check = new WExcel(Dept.class).loadData(bytes); + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class WExcel implements Serializable, Cloneable { + + /** + * 表名 + */ + private String name; + + /** + * 是否冻结第一行 + */ + private boolean freezeFirst; + + /** + * 模板类 + */ + private Class templateClass; + + /** + * 表头的集合 + */ + private List columnList = new ArrayList<>(); + + /** + * 单元格里存放的对象 + */ + private List rowList = new ArrayList<>(); + + /** + * 根据输入的模板类打印下载模板。 + */ + public WExcel(Class tClass) { + this.templateClass = tClass; + initColumns(tClass); + } + + /** + * 根据模板类对DataTable添加相应的列。 + * + * @return WColumn集合 + */ + private List initColumns(Class clazz) { + //获取工作簿名称,没有则以类名为默认工作簿名称 + if (clazz.isAnnotationPresent(SheetName.class)) { + SheetName sheetName = clazz.getAnnotation(SheetName.class); + this.setName(sheetName.value()); + this.setFreezeFirst(sheetName.freezeFirst()); + } else { + this.setName(clazz.getSimpleName()); + } + + //是否关注父类属性 + boolean parentFirst = clazz.isAnnotationPresent(ParentFirst.class) && clazz.getAnnotation(ParentFirst.class).value(); + Field[] fields = ClassUtil.getFields(clazz, parentFirst); + for (Field field : fields) { + if (field.isAnnotationPresent(Ignore.class) && field.getAnnotation(Ignore.class).value()) { + continue; + } + + WColumn WColumn = new WColumn(); + WColumn.setField(field); + + Method set = ClassUtil.setMethod(field.getName(), clazz, field.getType()); + WColumn.setSetMethod(set); + + Method get = ClassUtil.getMethod(field.getName(), clazz); + WColumn.setGetMethod(get); + + //获取列名称 + if (!field.isAnnotationPresent(ColumnName.class)) { + WColumn.setName(field.getName()); + } else { + ColumnName columnColumnName = field.getAnnotation(ColumnName.class); + WColumn.setName(columnColumnName.value()); + WColumn.setCellWidth(columnColumnName.width()); + WColumn.setRequired(columnColumnName.required()); + } + //获取列填写说明或描述 + if (field.isAnnotationPresent(ColumnDescription.class)) { + WColumn.setDescription(field.getAnnotation(ColumnDescription.class).value()); + } + //获取下拉列表 + if (field.isAnnotationPresent(ColumnList.class)) { + WColumn.setCellList(field.getAnnotation(ColumnList.class).value()); + } + + //获取列类型 + if (field.isAnnotationPresent(Convert.class)) { + Convert converter = field.getAnnotation(Convert.class); + Class target = converter.target(); + try { + WColumn.setConverter((Converter) target.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } else { + if (field.getType() == boolean.class || field.getType() == Boolean.class) { + WColumn.setConverter(new BooleanConverter()); + WColumn.setCellType(Cell.CELL_TYPE_BOOLEAN); + } else if (field.getType() == byte.class || field.getType() == Byte.class) { + WColumn.setConverter(new ByteConverter()); + WColumn.setCellType(Cell.CELL_TYPE_NUMERIC); + } else if (field.getType() == char.class || field.getType() == Character.class) { + WColumn.setConverter(new CharacterConverter()); + WColumn.setCellType(Cell.CELL_TYPE_STRING); + } else if (field.getType() == short.class || field.getType() == Short.class) { + WColumn.setConverter(new ShortConverter()); + WColumn.setCellType(Cell.CELL_TYPE_NUMERIC); + } else if (field.getType() == int.class || field.getType() == Integer.class) { + WColumn.setConverter(new IntegerConverter()); + WColumn.setCellType(Cell.CELL_TYPE_NUMERIC); + } else if (field.getType() == long.class || field.getType() == Long.class) { + WColumn.setConverter(new LongConverter()); + WColumn.setCellType(Cell.CELL_TYPE_NUMERIC); + } else if (field.getType() == float.class || field.getType() == Float.class) { + WColumn.setConverter(new FloatConverter()); + WColumn.setCellType(Cell.CELL_TYPE_NUMERIC); + } else if (field.getType() == double.class || field.getType() == Double.class) { + WColumn.setConverter(new DoubleConverter()); + WColumn.setCellType(Cell.CELL_TYPE_NUMERIC); + } else if (field.getType() == Date.class) { + WColumn.setConverter(new DateConverter()); + WColumn.setCellType(Cell.CELL_TYPE_NUMERIC); + } else if (field.getType() == String.class) { + WColumn.setConverter(new StringConverter()); + WColumn.setCellType(Cell.CELL_TYPE_STRING); + } else { + throw new RuntimeException("Can not find Converter"); + } + } + + columnList.add(WColumn); + } + return columnList; + } + + public WExcel loadData(List list) { + return loadData(list, null); + } + + /** + * 此构造方法仅用于转换对象列表。
+ * 并不会对数据的格式和合法性进行检验。 + * + * @param list 需要导出的对象列表 + */ + public WExcel loadData(List list, Processor processor) { + if (list.size() > 0) { + for (T t : list) { + WRow row = new WRow(); + for (WColumn column : columnList) { + if (column == null) { + continue; + } + Method method = column.getGetMethod(); + try { + Object value = method.invoke(t); + if (null == value) { + row.put(column.getName(), new WCell()); + } else { + String string = column.getConverter().string(value); + row.put(column.getName(), new WCell(string)); + } + } catch (IllegalAccessException e) { + LogUtil.w("can not invoke get method!"); + } catch (InvocationTargetException e) { + LogUtil.w(method.getName() + " unexpected exception!"); + } + // <2层检查>检查模板注解验证是否通过 + row.addErrors(ValidationUtil.validate(t)); + // <3层检查>如果没有错误,则可以执行处理器进行业务处理 + if (!row.hasError() && processor != null) { + List exec = processor.exec(t); + row.addErrors(exec != null ? exec : Collections.EMPTY_LIST); + } + } + this.rowList.add(row); + } + } + return this; + } + + public WExcel loadData(File file) throws TemplateNotMatchException, ReadErrorException, IOException { + return loadData(FileUtil.readFileToByteArray(file), null); + } + + public WExcel loadData(File file, Processor processor) throws TemplateNotMatchException, ReadErrorException, IOException { + return loadData(FileUtil.readFileToByteArray(file), processor); + } + + public WExcel loadData(byte[] bytes) throws TemplateNotMatchException, ReadErrorException { + return loadData(bytes, null); + } + + /** + * 以Excel文件的字节数组为数据源,为自己赋值的同时,与指定的模板类进行数据校验。
+ * 如果匹配过程中发现了不符合的数据,会在对应的单元中添加错误信息。
+ */ + public WExcel loadData(byte[] bytes, Processor processor) throws TemplateNotMatchException, ReadErrorException { + Workbook workbook = null; + InputStream is = null; + boolean flag; + try { + flag = true; + is = new ByteArrayInputStream(bytes); //读取文件流 + workbook = new HSSFWorkbook(is); + } catch (Exception e) { + flag = false; + } + if (!flag) { + try { + flag = true; + is = new ByteArrayInputStream(bytes); //读取文件流 + workbook = new XSSFWorkbook(is); + } catch (Exception e) { + flag = false; + } + } + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (!flag) { + throw new ReadErrorException("读取Excel文件错误"); + } + //第一张Sheet表 + Sheet sheet = workbook.getSheetAt(0); + //获取Sheet名称 + if (templateClass.isAnnotationPresent(SheetName.class)) {//如果模板存在注解则使用注解名称 + SheetName sheetName = templateClass.getAnnotation(SheetName.class); + this.setName(sheetName.value()); + } else {//将类名设为表名,如果类名不存在,将Excel表名设为表名 + this.setName(sheet.getSheetName()); + } + + //读取表头 + Row headRow = sheet.getRow(0); + //获取Excel列的总数 + int columnSum = headRow.getPhysicalNumberOfCells(); + + //检查列数量 + if (columnList.size() != columnSum) { + throw new TemplateNotMatchException("与模板列数量不同。"); + } else { + for (int i = 0; i < columnList.size(); i++) { + WColumn wColumn = columnList.get(i); + Cell cell = headRow.getCell(i); + String headValue = getValue(cell); + headValue = headValue.replace("*", ""); + headValue = headValue.replace(" ", ""); + + if (!wColumn.getName().equals(headValue)) { + throw new TemplateNotMatchException("第" + (i + 1) + "项,不匹配的列名," + wColumn.getName() + "和" + headValue); + } + } + } + + //Excel文件的总行数 + int maxRowNumber = sheet.getLastRowNum(); + // 逐行读取导入文件的数据 + for (int i = 0; i < maxRowNumber; i++) { + //Excel中的一行数据,第0行为表头,所以要加1 + Row inputRow = sheet.getRow(i + 1); + WRow row = new WRow(); + rowList.add(row); + + if (null != inputRow) { + for (int j = 0; j < columnList.size(); j++) { + WColumn wcolumn = columnList.get(j); + + /* 取得当前格子的值 */ + Cell excelCell = inputRow.getCell(j); + WCell WCell = new WCell(); + row.put(wcolumn.getName(), WCell); + + String value = ""; + if (null != excelCell) { + value = getValue(excelCell); + } + value = value.trim(); + + WCell.setValue(value); + } + } + + try { + T t = templateClass.newInstance(); + // <1层检查>检查数据格式是否正确 + row.addErrors(transferMap(row, t)); + // <2层检查>检查模板注解验证是否通过 + row.addErrors(ValidationUtil.validate(t)); + // <3层检查>如果没有错误,则可以执行处理器进行业务处理 + if (!row.hasError() && processor != null) { + List exec = processor.exec(t); + row.addErrors(exec != null ? exec : Collections.EMPTY_LIST); + } + } catch (InstantiationException | IllegalAccessException e) { + row.addError("模板对象默认构造函数错误"); + } + } + return this; + } + + /** + * 转换某行为一个对象 + * + * @param row 行对象 + * @param target 模板对象 + * @return 模板对象 + */ + public List transferMap(WRow row, T target) { + List err = new ArrayList<>(); + for (String key : row.keySet()) { + for (WColumn column : columnList) { + String name = column.getName(); + Method setMethod = column.getSetMethod(); + + if (key.equals(name)) { + WCell WCell = row.get(column.getName()); + if (null != WCell) { + String value = WCell.getValue(); + //获取转换器 + Converter converter = column.getConverter(); + try { + setMethod.invoke(target, converter.convert(value)); + } catch (ValueConverterException e) { + e.printStackTrace(); + err.add(e.getMessage()); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + err.add("[" + setMethod.getName() + "]执行错误"); + } + } + break; + } + } + } + return err; + } + + /** + * 获取表名 + * + * @return 表名 + */ + public String getName() { + return name; + } + + /** + * 设置表名 + * + * @param name 表名 + */ + public void setName(String name) { + this.name = name; + } + + public boolean getFreezeFirst() { + return freezeFirst; + } + + public void setFreezeFirst(boolean freezeFirst) { + this.freezeFirst = freezeFirst; + } + + public final String toCSV() { + StringBuilder sb = new StringBuilder(); + + for (WColumn WColumn : this.columnList) { + if (WColumn != null) { + sb.append(WColumn.getName()).append(","); + } + } + sb.append("\n"); + + for (int i = 0; i < this.rowList.size(); i++) { + for (int j = 0; j < this.columnList.size(); j++) { + WColumn wColumn = this.columnList.get(j); + WCell wCell = this.rowList.get(i).get(wColumn.getName()); + sb.append(wCell.getValue()).append(","); + } + sb.append("\n"); + } + + return sb.toString(); + } + + /** + * 判断DataTable是否包含错误信息 + * + * @return 是否包含错误. + */ + public final boolean hasError() { + for (WRow wRow : rowList) { + if (wRow.hasError()) { + return true; + } + } + return false; + } + + public byte[] getBytes() { + XSSFWorkbook workbook = getExcel(); + if (workbook != null) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + workbook.write(outputStream); + } catch (IOException e) { + return null; + } + return outputStream.toByteArray(); + } + return null; + } + + public XSSFWorkbook getExcel() { + XSSFWorkbook workbook = new XSSFWorkbook(); + //创建一个Sheet表 + XSSFSheet sheet = workbook.createSheet(name); + sheet.setDefaultRowHeightInPoints(18); + Row firstRow = sheet.createRow(0); // 下标为0的行开始 + XSSFDrawing drawing = sheet.createDrawingPatriarch(); + + int offset = 0; + // 添加结果和错误说明列 + if (hasError()) { + offset++; + {// 第一栏结果栏 + Cell firstCell = firstRow.createCell(0); + firstCell.setCellStyle(new HeadCellStyle(workbook).getStyle()); + firstCell.setCellValue(new XSSFRichTextString("执行结果")); + XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6); + XSSFComment comment = drawing.createCellComment(anchor); + comment.setString("此列为检查结果,导入时请处理完错误,并删除该列!"); + firstCell.setCellComment(comment); + } + offset++; + {// 第二栏错误信息栏 + Cell secondCell = firstRow.createCell(1); + secondCell.setCellStyle(new HeadCellStyle(workbook).getStyle()); + secondCell.setCellValue(new XSSFRichTextString("结果说明")); + sheet.setColumnWidth(1, 18 * 256); + XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6); + XSSFComment comment = drawing.createCellComment(anchor); + comment.setString("此列为检查结果信息列,导入时请处理完错误,并删除该列!"); + secondCell.setCellComment(comment); + } + } + + for (int j = 0; j < this.columnList.size(); j++) { + WColumn column = this.columnList.get(j); + Cell firstCell = firstRow.createCell(j + offset); + String columnName = column.getName(); + XSSFRichTextString textString; + if (column.isRequired()) { + textString = new XSSFRichTextString("*" + columnName); + textString.applyFont(0, 1, new RedFont(workbook).getFont()); + textString.applyFont(1, textString.length(), workbook.createFont()); + } else { + textString = new XSSFRichTextString(columnName); + textString.applyFont(workbook.createFont()); + } + StringBuilder sb = new StringBuilder(); + sb.append(column.getDescription()).append("\n"); + + // 如果填写了注释信息 + if (sb.length() > 1) { + XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6); + XSSFComment comment = drawing.createCellComment(anchor); + comment.setString(sb.toString()); + firstCell.setCellComment(comment); + } + firstCell.setCellValue(textString); + firstCell.setCellStyle(new HeadCellStyle(workbook).getStyle()); + sheet.setColumnWidth(j + offset, (4 + column.getCellWidth()) * 256); + + if (column.getCellList() != null){ + CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 65535, j+offset, j+offset); + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = helper.createExplicitListConstraint(column.getCellList()); + DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList); + //处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + + dataValidation.setEmptyCellAllowed(true); + dataValidation.setShowPromptBox(true); + dataValidation.createPromptBox("规则", "请选择下拉框里面的数据"); + sheet.addValidationData(dataValidation); + } + } + + // 冻结第一行 + if (freezeFirst) { + sheet.createFreezePane(255, 1); + } + + // 填充数据 + for (int i = 0; i < this.rowList.size(); i++) { + WRow wRow = this.rowList.get(i); + Row row = sheet.createRow(i + 1); + + if (offset > 0) { + if (this.rowList.get(i).hasError()) { + // 添加结果 + List errorList = wRow.getErrorList(); + Cell resultCell = row.createCell(0); + resultCell.setCellStyle(new ErrorCellStyle(workbook).getStyle()); + resultCell.setCellValue("错误"); + // 添加错误信息详细说明 + Cell errsCell = row.createCell(1); + errsCell.setCellStyle(new ErrorCellStyle(workbook).getStyle()); + String join = String.join(";\n", errorList); + errsCell.setCellValue(new XSSFRichTextString(join)); + } else { + // 添加结果 + Cell resultCell = row.createCell(0); + resultCell.setCellStyle(new SuccessCellStyle(workbook).getStyle()); + resultCell.setCellValue("成功"); + // 添加错误信息详细说明 + Cell errsCell = row.createCell(1); + errsCell.setCellStyle(new SuccessCellStyle(workbook).getStyle()); + errsCell.setCellValue(""); + } + } + + for (int j = 0; j < this.columnList.size(); j++) { + WColumn column = this.columnList.get(j); + + Cell xssfCell = row.createCell(j + offset); + WCell WCell = this.rowList.get(i).get(column.getName()); + if (null == WCell) { + continue; + } + String value = WCell.getValue(); + xssfCell.setCellType(column.getCellType()); + if (column.getCellType() == Cell.CELL_TYPE_NUMERIC || column.getCellType() == Cell.CELL_TYPE_BOOLEAN) { + xssfCell.setCellStyle(new DataCellStyle(workbook, CellStyle.ALIGN_RIGHT, wRow.hasError()).getStyle()); + } else { + xssfCell.setCellStyle(new DataCellStyle(workbook, CellStyle.ALIGN_LEFT, wRow.hasError()).getStyle()); + } + + xssfCell.setCellValue(value); + } + } + return workbook; + } + + /** + * 获取单元格的值 + * + * @param cell 要获取值的单元格 + * @return 单元格的值 + */ + public static String getValue(Cell cell) { + if (cell == null) { + return ""; + } + switch (cell.getCellType()) { + case Cell.CELL_TYPE_STRING: + return cell.getStringCellValue(); + case Cell.CELL_TYPE_BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + case Cell.CELL_TYPE_NUMERIC: + if (HSSFDateUtil.isCellDateFormatted(cell)) { + Date value = cell.getDateCellValue(); + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(value); + } else { + return NumberToTextConverter.toText(cell.getNumericCellValue()); + } + default: + return cell.getStringCellValue(); + } + } + + public interface Processor { + List exec(T t); + } + + public void toFile(File file) throws IOException { + File parentFile = file.getParentFile(); + if (!parentFile.exists() && !parentFile.mkdirs()) { + throw new RuntimeException("路径创建失败"); + } + if (!file.exists() && !file.createNewFile()) { + throw new RuntimeException("文件创建失败"); + } + FileOutputStream fileOutputStream = new FileOutputStream(file); + fileOutputStream.write(getBytes()); + fileOutputStream.close(); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/WRow.java b/src/main/java/xyz/wbsite/frame/excel/WRow.java new file mode 100644 index 0000000..2e998dd --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/WRow.java @@ -0,0 +1,34 @@ +package xyz.wbsite.frame.excel; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * WRow - Excel行对象,即数据WCell容器
继承至HashMap,key为列名,value为单元对象 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class WRow extends HashMap implements Serializable { + + private List errorList = new ArrayList<>(); + + public final boolean hasError() { + return errorList.size() > 0; + } + + public final void addError(String errorMsg) { + errorList.add(errorMsg); + } + + public final void addErrors(List errorMsgs) { + errorList.addAll(errorMsgs); + } + + public List getErrorList() { + return errorList; + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnDescription.java b/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnDescription.java new file mode 100644 index 0000000..ac06c42 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnDescription.java @@ -0,0 +1,22 @@ +package xyz.wbsite.frame.excel.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * ColumnDescription - Excel列描述注解 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ColumnDescription { + String value() default ""; + +} diff --git a/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnList.java b/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnList.java new file mode 100644 index 0000000..c8a9abb --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnList.java @@ -0,0 +1,25 @@ +package xyz.wbsite.frame.excel.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * ColumnList - 单元格下拉选项 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ColumnList { + + /** + * 下拉列表 + */ + String[] value(); +} diff --git a/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnName.java b/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnName.java new file mode 100644 index 0000000..0df4b63 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/annotation/ColumnName.java @@ -0,0 +1,40 @@ +package xyz.wbsite.frame.excel.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * ColumnName - Excel列名称注解,当Excel模板没有该注解则使用字段名作为列名称 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ColumnName { + + /** + * 列名 + */ + String value(); + + /** + * 默认宽度 + */ + int width() default 8; + + /** + * *标志 + */ + boolean required() default false; + + /** + * 日期格式化 + */ + String dateFormat() default "yyyy-MM-dd"; +} diff --git a/src/main/java/xyz/wbsite/frame/excel/annotation/Convert.java b/src/main/java/xyz/wbsite/frame/excel/annotation/Convert.java new file mode 100644 index 0000000..8b332a1 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/annotation/Convert.java @@ -0,0 +1,21 @@ +package xyz.wbsite.frame.excel.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Converter - Excel列转化器注解 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Convert { + Class target(); +} diff --git a/src/main/java/xyz/wbsite/frame/excel/annotation/Ignore.java b/src/main/java/xyz/wbsite/frame/excel/annotation/Ignore.java new file mode 100644 index 0000000..9540028 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/annotation/Ignore.java @@ -0,0 +1,21 @@ +package xyz.wbsite.frame.excel.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Ignore - Excel列忽略注解,在导入导出过程中不会对存在该注解的字典进行解析 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Ignore { + boolean value() default true; +} diff --git a/src/main/java/xyz/wbsite/frame/excel/annotation/ParentFirst.java b/src/main/java/xyz/wbsite/frame/excel/annotation/ParentFirst.java new file mode 100644 index 0000000..c169b8d --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/annotation/ParentFirst.java @@ -0,0 +1,21 @@ +package xyz.wbsite.frame.excel.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * ParentFirst - 是否读取父类的属性,默认否 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface ParentFirst { + boolean value() default true; +} diff --git a/src/main/java/xyz/wbsite/frame/excel/annotation/SheetName.java b/src/main/java/xyz/wbsite/frame/excel/annotation/SheetName.java new file mode 100644 index 0000000..e1b7f24 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/annotation/SheetName.java @@ -0,0 +1,23 @@ +package xyz.wbsite.frame.excel.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * SheetName - 工作表名称,当Excel模板没有该注解则使用Class类名作为工作表默认名称 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SheetName { + String value(); + + boolean freezeFirst() default false; +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/BooleanConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/BooleanConverter.java new file mode 100644 index 0000000..a233558 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/BooleanConverter.java @@ -0,0 +1,46 @@ +package xyz.wbsite.frame.excel.converter; + +import xyz.wbsite.frame.excel.exception.ValueConverterException; + +/** + * BooleanConverter - Boolean转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class BooleanConverter implements Converter { + + @Override + public Boolean convert(String var) throws ValueConverterException { + if (null == var || "".equals(var)) { + return false; + } + + String lowerCase = var.toLowerCase(); + + if (lowerCase.matches("y|n")) { + return "y".equals(lowerCase); + } else if (lowerCase.matches("yes|no")) { + return "yes".equals(lowerCase); + } else if (lowerCase.matches("true|false")) { + return "true".equals(lowerCase); + } else if (lowerCase.matches("是|否")) { + return "是".equals(lowerCase); + } else if (lowerCase.matches("有|无")) { + return "有".equals(lowerCase); + } else if (lowerCase.matches("是|不是")) { + return "是".equals(lowerCase); + } else { + throw new ValueConverterException("[" + var + "] can not convert to Boolean"); + } + } + + @Override + public String string(Boolean var) { + if (var == null) { + return ""; + } + return var ? "是" : "否"; + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/ByteConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/ByteConverter.java new file mode 100644 index 0000000..b05ecef --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/ByteConverter.java @@ -0,0 +1,28 @@ +package xyz.wbsite.frame.excel.converter; + +/** + * ByteConverter - Byte转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ByteConverter implements Converter { + + @Override + public Byte convert(String var) { + try { + return Byte.parseByte(var); + } catch (Exception e) { + return 0; + } + } + + @Override + public String string(Byte var) { + if (var == null) { + return ""; + } + return String.valueOf(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/CharacterConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/CharacterConverter.java new file mode 100644 index 0000000..795dacb --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/CharacterConverter.java @@ -0,0 +1,28 @@ +package xyz.wbsite.frame.excel.converter; + +/** + * CharacterConverter - Character转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class CharacterConverter implements Converter { + + @Override + public Character convert(String var) { + if (var == null || var.equals("")) { + return '0'; + } else { + return var.charAt(0); + } + } + + @Override + public String string(Character var) { + if (var == null) { + return ""; + } + return String.valueOf(var).trim(); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/Converter.java b/src/main/java/xyz/wbsite/frame/excel/converter/Converter.java new file mode 100644 index 0000000..13243cb --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/Converter.java @@ -0,0 +1,17 @@ +package xyz.wbsite.frame.excel.converter; + +import xyz.wbsite.frame.excel.exception.ValueConverterException; + +/** + * Converter - 转化器接口,所有转化器必须实现该接口 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public interface Converter { + + T convert(String var) throws ValueConverterException; + + String string(T var); +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/DateConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/DateConverter.java new file mode 100644 index 0000000..ab3e13f --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/DateConverter.java @@ -0,0 +1,51 @@ +package xyz.wbsite.frame.excel.converter; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * DateConverter - Date转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class DateConverter implements Converter { + + @Override + public Date convert(String var) { + if (var == null) { + return null; + } + Date date = null; + try { + var = var.trim(); + if (var.matches("[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}")) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.parse(var); + } else if (var.matches("[0-9]{4}/[0-9]{1,2}/[0-9]{1,2} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}")) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + return sdf.parse(var); + } else if (var.matches("[0-9]{4}-[0-9]{1,2}-[0-9]{1,2} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}")) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + date = sdf.parse(var); + } else if (var.matches("[0-9]{4}年[0-9]{1,2}月[0-9]{1,2}日 [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}")) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); + date = sdf.parse(var); + } + + } catch (ParseException e) { + e.printStackTrace(); + } + return date; + } + + @Override + public String string(Date var) { + if (var == null) { + return ""; + } + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/DoubleConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/DoubleConverter.java new file mode 100644 index 0000000..06f55ea --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/DoubleConverter.java @@ -0,0 +1,28 @@ +package xyz.wbsite.frame.excel.converter; + +/** + * DoubleConverter - Double转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class DoubleConverter implements Converter { + + @Override + public Double convert(String var) { + try { + return Double.parseDouble(var); + } catch (Exception e) { + return 0d; + } + } + + @Override + public String string(Double var) { + if (var == null) { + return ""; + } + return String.valueOf(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/FloatConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/FloatConverter.java new file mode 100644 index 0000000..647a289 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/FloatConverter.java @@ -0,0 +1,33 @@ +package xyz.wbsite.frame.excel.converter; + +/** + * FloatConverter - Float转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class FloatConverter implements Converter { + + @Override + public Float convert(String var) { + try { + //增加对1.0一类的的优化显示 + if (var.matches("(\\d+)\\.0")) { + var = var.replaceAll("\\.0$", ""); + } + + return Float.parseFloat(var); + } catch (Exception e) { + return 0f; + } + } + + @Override + public String string(Float var) { + if (var == null) { + return ""; + } + return String.valueOf(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/IntegerConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/IntegerConverter.java new file mode 100644 index 0000000..264228a --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/IntegerConverter.java @@ -0,0 +1,28 @@ +package xyz.wbsite.frame.excel.converter; + +/** + * IntegerConverter - Integer转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class IntegerConverter implements Converter { + + @Override + public Integer convert(String var) { + try { + return Integer.parseInt(var); + } catch (Exception e) { + return 0; + } + } + + @Override + public String string(Integer var) { + if (var == null) { + return ""; + } + return String.valueOf(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/LongConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/LongConverter.java new file mode 100644 index 0000000..526de2f --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/LongConverter.java @@ -0,0 +1,40 @@ +package xyz.wbsite.frame.excel.converter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * LongConverter - Long转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class LongConverter implements Converter { + + @Override + public Long convert(String var) { + try { + //增加对1.0一类的的兼容性 + if (var.matches("(\\d+)\\.(\\d*)")) { + Pattern compile = Pattern.compile("(\\d+)\\.(\\d*)"); + Matcher matcher = compile.matcher(var); + if (matcher.find()) { + var = matcher.group(1); + } + } + + return Long.parseLong(var); + } catch (Exception e) { + return 0L; + } + } + + @Override + public String string(Long var) { + if (var == null) { + return ""; + } + return String.valueOf(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/ShortConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/ShortConverter.java new file mode 100644 index 0000000..5ddebb1 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/ShortConverter.java @@ -0,0 +1,28 @@ +package xyz.wbsite.frame.excel.converter; + +/** + * ShortConverter - Short转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ShortConverter implements Converter { + + @Override + public Short convert(String var) { + try { + return Short.parseShort(var); + } catch (Exception e) { + return 0; + } + } + + @Override + public String string(Short var) { + if (var == null) { + return ""; + } + return String.valueOf(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/converter/StringConverter.java b/src/main/java/xyz/wbsite/frame/excel/converter/StringConverter.java new file mode 100644 index 0000000..db00521 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/converter/StringConverter.java @@ -0,0 +1,24 @@ +package xyz.wbsite.frame.excel.converter; + +/** + * StringConverter - String转化器,重写了对象到String,String到对象的转化方式 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class StringConverter implements Converter { + + @Override + public String convert(String var) { + return var != null ? var.trim() : null; + } + + @Override + public String string(String var) { + if (var == null) { + return ""; + } + return String.valueOf(var); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/exception/ReadErrorException.java b/src/main/java/xyz/wbsite/frame/excel/exception/ReadErrorException.java new file mode 100644 index 0000000..9dac0cf --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/exception/ReadErrorException.java @@ -0,0 +1,17 @@ +package xyz.wbsite.frame.excel.exception; + +/** + * Excel文件读取失败异常 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ReadErrorException extends Exception { + public ReadErrorException() { + } + + public ReadErrorException(String s) { + super(s); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/exception/TemplateNotMatchException.java b/src/main/java/xyz/wbsite/frame/excel/exception/TemplateNotMatchException.java new file mode 100644 index 0000000..37887cf --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/exception/TemplateNotMatchException.java @@ -0,0 +1,17 @@ +package xyz.wbsite.frame.excel.exception; + +/** + * Excel文件与模板不匹配时异常 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class TemplateNotMatchException extends Exception { + public TemplateNotMatchException() { + } + + public TemplateNotMatchException(String s) { + super(s); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/exception/ValueConverterException.java b/src/main/java/xyz/wbsite/frame/excel/exception/ValueConverterException.java new file mode 100644 index 0000000..b848c46 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/exception/ValueConverterException.java @@ -0,0 +1,15 @@ +package xyz.wbsite.frame.excel.exception; + +/** + * 值转换异常 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ValueConverterException extends Exception { + + public ValueConverterException(String s) { + super(s); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/BaseCellStyle.java b/src/main/java/xyz/wbsite/frame/excel/style/BaseCellStyle.java new file mode 100644 index 0000000..d336a29 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/BaseCellStyle.java @@ -0,0 +1,57 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Created on 2015/1/29. + * + * @author + * @since 2.0.0 + */ +public class BaseCellStyle { + /** + * 样式 + */ + protected CellStyle style; + + public BaseCellStyle(Workbook workbook) { + style = workbook.createCellStyle(); + style.setFillPattern(CellStyle.NO_FILL); + //下边框 + style.setBorderBottom(CellStyle.SOLID_FOREGROUND); + //下边框颜色 + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + //左边框 + style.setBorderLeft(CellStyle.SOLID_FOREGROUND); + //左边框颜色 + style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + //右边框 + style.setBorderRight(CellStyle.SOLID_FOREGROUND); + //右边框颜色 + style.setRightBorderColor(IndexedColors.BLACK.getIndex()); + //上边框 + style.setBorderTop(CellStyle.SOLID_FOREGROUND); + //上边框颜色 + style.setTopBorderColor(IndexedColors.BLACK.getIndex()); + + style.setVerticalAlignment(CellStyle.VERTICAL_CENTER); //上下居中 + style.setBorderBottom(CellStyle.SOLID_FOREGROUND); //下边框 + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); //下边框颜色 + style.setBorderLeft(CellStyle.SOLID_FOREGROUND); //左边框 + style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); //左边框颜色 + style.setBorderRight(CellStyle.SOLID_FOREGROUND); //右边框 + style.setRightBorderColor(IndexedColors.BLACK.getIndex()); //右边框颜色 + style.setBorderTop(CellStyle.SOLID_FOREGROUND); //上边框 + style.setTopBorderColor(IndexedColors.BLACK.getIndex()); //上边框颜色 + } + + public CellStyle getStyle() { + return style; + } + + public void setStyle(CellStyle style) { + this.style = style; + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/BaseFont.java b/src/main/java/xyz/wbsite/frame/excel/style/BaseFont.java new file mode 100644 index 0000000..e03c689 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/BaseFont.java @@ -0,0 +1,29 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Created on 2015/1/29. + * + * @author + * @since 2.0.0 + */ +public class BaseFont { + /** + * 字体 + */ + protected Font font; + + public BaseFont(Workbook workbook) { + font = workbook.createFont(); + } + + public Font getFont() { + return font; + } + + public void setFont(Font font) { + this.font = font; + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/DataCellStyle.java b/src/main/java/xyz/wbsite/frame/excel/style/DataCellStyle.java new file mode 100644 index 0000000..583921c --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/DataCellStyle.java @@ -0,0 +1,29 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Workbook; + +public class DataCellStyle extends BaseCellStyle { + + public DataCellStyle(Workbook workbook, short align, boolean error) { + super(workbook); + style.setAlignment(align); + if (error) { + style.setFillForegroundColor(HSSFColor.RED.index); //背景颜色红色 + style.setFillPattern(CellStyle.SOLID_FOREGROUND); //设置单元格填充样式 + } + } + + public DataCellStyle(Workbook workbook, short align) { + this(workbook, align, false); + } + + public DataCellStyle(Workbook workbook, boolean error) { + this(workbook, CellStyle.ALIGN_LEFT, error); + } + + public DataCellStyle(Workbook workbook) { + this(workbook, CellStyle.ALIGN_LEFT, false); + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/ErrorCellStyle.java b/src/main/java/xyz/wbsite/frame/excel/style/ErrorCellStyle.java new file mode 100644 index 0000000..b8afb2b --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/ErrorCellStyle.java @@ -0,0 +1,22 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Workbook; + +public class ErrorCellStyle extends BaseCellStyle { + + public ErrorCellStyle(Workbook workbook) { + super(workbook); + style.setFillForegroundColor(HSSFColor.RED.index); //背景颜色红色 + style.setFillPattern(CellStyle.SOLID_FOREGROUND); //设置单元格填充样式 + } + + public CellStyle getStyle() { + return style; + } + + public void setStyle(CellStyle style) { + this.style = style; + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/HeadCellStyle.java b/src/main/java/xyz/wbsite/frame/excel/style/HeadCellStyle.java new file mode 100644 index 0000000..10eb9ab --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/HeadCellStyle.java @@ -0,0 +1,15 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Workbook; + +public class HeadCellStyle extends BaseCellStyle { + public HeadCellStyle(Workbook workbook) { + super(workbook); + style.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index); //背景颜色-灰色 + style.setFillPattern(CellStyle.SOLID_FOREGROUND); // 设置单元格填充样式 + style.setAlignment(CellStyle.ALIGN_CENTER); // 居中 + style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);//上下居中 + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/NormalFont.java b/src/main/java/xyz/wbsite/frame/excel/style/NormalFont.java new file mode 100644 index 0000000..f4cf5bb --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/NormalFont.java @@ -0,0 +1,18 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * 普通字体.颜色黑 + * Created on 2015/1/29. + * + * @author + * @since 2.0.0 + */ +public class NormalFont extends BaseFont { + public NormalFont(Workbook workbook) { + super(workbook); + font.setColor(HSSFColor.BLACK.index); //字体颜色-黑色 + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/RedFont.java b/src/main/java/xyz/wbsite/frame/excel/style/RedFont.java new file mode 100644 index 0000000..9d93a92 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/RedFont.java @@ -0,0 +1,18 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * 普通字体.颜色黑 + * Created on 2015/1/29. + * + * @author + * @since 2.0.0 + */ +public class RedFont extends BaseFont { + public RedFont(Workbook workbook) { + super(workbook); + font.setColor(HSSFColor.RED.index); //字体颜色-黑色 + } +} diff --git a/src/main/java/xyz/wbsite/frame/excel/style/SuccessCellStyle.java b/src/main/java/xyz/wbsite/frame/excel/style/SuccessCellStyle.java new file mode 100644 index 0000000..136c474 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/excel/style/SuccessCellStyle.java @@ -0,0 +1,22 @@ +package xyz.wbsite.frame.excel.style; + +import org.apache.poi.hssf.util.HSSFColor; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Workbook; + +public class SuccessCellStyle extends BaseCellStyle { + + public SuccessCellStyle(Workbook workbook) { + super(workbook); + style.setFillForegroundColor(HSSFColor.GREEN.index); //背景颜色红色 + style.setFillPattern(CellStyle.SOLID_FOREGROUND); //设置单元格填充样式 + } + + public CellStyle getStyle() { + return style; + } + + public void setStyle(CellStyle style) { + this.style = style; + } +} diff --git a/src/main/java/xyz/wbsite/frame/schedule/RunCronTask.java b/src/main/java/xyz/wbsite/frame/schedule/RunCronTask.java new file mode 100644 index 0000000..edbb08f --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/schedule/RunCronTask.java @@ -0,0 +1,18 @@ +package xyz.wbsite.frame.schedule; + +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.util.Assert; + +import java.util.concurrent.ScheduledFuture; + +public abstract class RunCronTask extends RunTask { + + abstract String cron(); + + @Override + public ScheduledFuture schedule(ThreadPoolTaskScheduler poolTaskScheduler) { + Assert.notNull(poolTaskScheduler, "ThreadPoolTaskScheduler must not be null"); + return poolTaskScheduler.schedule(this, new CronTrigger(cron())); + } +} diff --git a/src/main/java/xyz/wbsite/frame/schedule/RunDelayRepeatTask.java b/src/main/java/xyz/wbsite/frame/schedule/RunDelayRepeatTask.java new file mode 100644 index 0000000..f52c639 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/schedule/RunDelayRepeatTask.java @@ -0,0 +1,18 @@ +package xyz.wbsite.frame.schedule; + +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.util.Assert; + +import java.time.Duration; +import java.util.concurrent.ScheduledFuture; + +public abstract class RunDelayRepeatTask extends RunTask { + + public abstract Duration interval(); + + @Override + public ScheduledFuture schedule(ThreadPoolTaskScheduler poolTaskScheduler) { + Assert.notNull(poolTaskScheduler, "ThreadPoolTaskScheduler must not be null"); + return poolTaskScheduler.scheduleWithFixedDelay(this, interval()); + } +} diff --git a/src/main/java/xyz/wbsite/frame/schedule/RunFixRepeatTask.java b/src/main/java/xyz/wbsite/frame/schedule/RunFixRepeatTask.java new file mode 100644 index 0000000..1abf7e6 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/schedule/RunFixRepeatTask.java @@ -0,0 +1,18 @@ +package xyz.wbsite.frame.schedule; + +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.util.Assert; + +import java.time.Duration; +import java.util.concurrent.ScheduledFuture; + +public abstract class RunFixRepeatTask extends RunTask { + + public abstract Duration interval(); + + @Override + public ScheduledFuture schedule(ThreadPoolTaskScheduler poolTaskScheduler) { + Assert.notNull(poolTaskScheduler, "ThreadPoolTaskScheduler must not be null"); + return poolTaskScheduler.scheduleAtFixedRate(this, interval()); + } +} diff --git a/src/main/java/xyz/wbsite/frame/schedule/RunSqlTask.java b/src/main/java/xyz/wbsite/frame/schedule/RunSqlTask.java new file mode 100644 index 0000000..5858ed7 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/schedule/RunSqlTask.java @@ -0,0 +1,40 @@ +package xyz.wbsite.frame.schedule; + +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import xyz.wbsite.frame.auth.LocalData; +import xyz.wbsite.frame.utils.LogUtil; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public abstract class RunSqlTask extends RunFixRepeatTask { + + private Connection connection; + + @Override + public void run() { + try { + if (connection == null || connection.isClosed()) { + SqlSessionFactory factory = LocalData.getBean(SqlSessionFactory.class); + SqlSession sqlSession = factory.openSession(true); + connection = sqlSession.getConnection(); + } + } catch (Exception e) { + e.printStackTrace(); + LogUtil.e("schedule: get connection failed!"); + return; + } + try { + PreparedStatement preparedStatement = connection.prepareStatement(getSql()); + preparedStatement.execute(); + preparedStatement.close(); + } catch (SQLException e) { + e.printStackTrace(); + LogUtil.e("RunSqlTask exec failed! SQL:[" + getSql() + "]"); + } + } + + public abstract String getSql(); +} diff --git a/src/main/java/xyz/wbsite/frame/schedule/RunTask.java b/src/main/java/xyz/wbsite/frame/schedule/RunTask.java new file mode 100644 index 0000000..897dea6 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/schedule/RunTask.java @@ -0,0 +1,21 @@ +package xyz.wbsite.frame.schedule; + + +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +import java.util.Date; +import java.util.concurrent.ScheduledFuture; + +public abstract class RunTask implements Runnable { + + public abstract String taskId(); + + public abstract ScheduledFuture schedule(ThreadPoolTaskScheduler poolTaskScheduler); + + public void configChange(ThreadPoolTaskScheduler scheduler) { + ScheduledFuture schedule = scheduler.schedule(this, new Date()); + if (!schedule.cancel(true)) { + + } + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/AESUtil.java b/src/main/java/xyz/wbsite/frame/utils/AESUtil.java new file mode 100644 index 0000000..7f42706 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/AESUtil.java @@ -0,0 +1,110 @@ +package xyz.wbsite.frame.utils; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +/** + * AESUtil 对称加密和解密工具类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class AESUtil { + + private static final String ALGORITHM = "AES"; + private static final String ALGORITHM_STR = "AES/ECB/PKCS5Padding"; + + /** + * 加密 + * + * @param data 待加密字节数组 + * @param secret 密钥 + * @return base64字符串 + */ + public static byte[] encrypt(byte[] data, String secret) { + try { + if (secret.length() != 16) { + throw new IllegalArgumentException("secret's length is not 16"); + } + SecretKeySpec key = new SecretKeySpec(secret.getBytes(), ALGORITHM); + Cipher cipher = Cipher.getInstance(ALGORITHM_STR); // 创建密码器 + cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 + return cipher.doFinal(data);// 加密 + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 加密 + * + * @param data 待加密字节数组 + * @param secret 密钥 + * @return base64字符串 + */ + public static String encrypt2Base64(byte[] data, String secret) { + byte[] encrypt = encrypt(data, secret); + return Base64Util.encodeToString(encrypt, false); + } + + /** + * 解密 + * + * @param data 待解密字节数组 + * @param secret 密钥 + * @return + */ + public static byte[] decrypt(byte[] data, String secret) { + try { + if (secret.length() != 16) { + throw new IllegalArgumentException("secret's length is not 16"); + } + SecretKeySpec key = new SecretKeySpec(secret.getBytes(), ALGORITHM); + Cipher cipher = Cipher.getInstance(ALGORITHM_STR); + cipher.init(Cipher.DECRYPT_MODE, key); + return cipher.doFinal(data); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } + return null; + } + + public static byte[] decryptBase64(String base64Data, String secret) { + byte[] decode = Base64Util.decode(base64Data); + return decrypt(decode, secret); + } + + public static String decrypt2String(String base64Data, String secret) { + byte[] bytes = decryptBase64(base64Data, secret); + try { + return new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/frame/utils/Base64Util.java b/src/main/java/xyz/wbsite/frame/utils/Base64Util.java new file mode 100644 index 0000000..614c782 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/Base64Util.java @@ -0,0 +1,497 @@ +package xyz.wbsite.frame.utils; + +import java.util.Arrays; + +/** + * Base64Util + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class Base64Util { + private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); + private static final int[] IA = new int[256]; + + static { + Arrays.fill(IA, -1); + for (int i = 0, iS = CA.length; i < iS; i++) + IA[CA[i]] = i; + IA['='] = 0; + } + + public final static String encodeToString(byte[] sArr) { + return new String(encodeToChar(sArr, false)); + } + + public final static char[] encodeToChar(byte[] sArr, boolean lineSep) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new char[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + char[] dArr = new char[dLen]; + + // Encode even 24-bits + for (int s = 0, d = 0, cc = 0; s < eLen; ) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = CA[(i >>> 18) & 0x3f]; + dArr[d++] = CA[(i >>> 12) & 0x3f]; + dArr[d++] = CA[(i >>> 6) & 0x3f]; + dArr[d++] = CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = CA[i >> 12]; + dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + /** + * Encodes a raw byte array into a BASE64 String representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static String encodeToString(byte[] sArr, boolean lineSep) { + // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower. + return new String(encodeToChar(sArr, lineSep)); + } + + /** + * Encodes a raw byte array into a BASE64 byte[] representation i accordance with RFC 2045. + * + * @param sArr The bytes to convert. If null or length 0 an empty array will be returned. + * @param lineSep Optional "\r\n" after 76 characters, unless end of file.
+ * No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a + * little faster. + * @return A BASE64 encoded array. Never null. + */ + public final static byte[] encodeToByte(byte[] sArr, boolean lineSep) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new byte[0]; + + int eLen = (sLen / 3) * 3; // Length of even 24-bits. + int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count + int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array + byte[] dArr = new byte[dLen]; + + // Encode even 24-bits + for (int s = 0, d = 0, cc = 0; s < eLen; ) { + // Copy next three bytes into lower 24 bits of int, paying attension to sign. + int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); + + // Encode the int into four chars + dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; + dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[d++] = (byte) CA[i & 0x3f]; + + // Add optional line separator + if (lineSep && ++cc == 19 && d < dLen - 2) { + dArr[d++] = '\r'; + dArr[d++] = '\n'; + cc = 0; + } + } + + // Pad and encode last bits if source isn't an even 24 bits. + int left = sLen - eLen; // 0 - 2. + if (left > 0) { + // Prepare the int + int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); + + // Set last four chars + dArr[dLen - 4] = (byte) CA[i >> 12]; + dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; + dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; + dArr[dLen - 1] = '='; + } + return dArr; + } + + public final static byte[] decode(char[] sArr) { + // Check special case + int sLen = sArr != null ? sArr.length : 0; + if (sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[i]] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0; ) + if (sArr[i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++]]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** + * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with + * and without line separators. + * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(byte[] sArr) { + // Check special case + int sLen = sArr.length; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[sArr[i] & 0xff] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + int pad = 0; + for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0; ) + if (sArr[i] == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[sArr[s++] & 0xff]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + + return dArr; + } + + /** + * Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as + * fast as {@link =#=decode(byte[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(byte[] sArr) { + // Check special case + int sLen = sArr.length; + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + /** + * Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as + * fast as {@link =#=decode(char[])}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param sArr The source array. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(char[] sArr) { + // Check special case + int sLen = sArr.length; + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[sArr[sIx]] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[sArr[eIx]] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[sArr[sIx++]] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } + + /** + * Decodes a BASE64 encoded String. All illegal characters will be ignored and can handle both strings with + * and without line separators.
+ * Note! It can be up to about 2x the speed to call decode(str.toCharArray()) instead. That + * will create a temporary array though. This version will use str.charAt(i) to iterate the string. + * + * @param str The source string. null or length 0 will return an empty array. + * @return The decoded array of bytes. May be of length 0. Will be null if the legal characters + * (including '=') isn't divideable by 4. (I.e. definitely corrupted). + */ + public final static byte[] decode(String str) { + // Check special case + int sLen = str != null ? str.length() : 0; + if (sLen == 0) + return new byte[0]; + + // Count illegal characters (including '\r', '\n') to know what size the returned array will be, + // so we don't have to reallocate & copy it later. + int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...) + for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out. + if (IA[str.charAt(i)] < 0) + sepCnt++; + + // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045. + if ((sLen - sepCnt) % 4 != 0) + return null; + + // Count '=' at end + int pad = 0; + for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0; ) + if (str.charAt(i) == '=') + pad++; + + int len = ((sLen - sepCnt) * 6 >> 3) - pad; + + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + for (int s = 0, d = 0; d < len; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = 0; + for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. + int c = IA[str.charAt(s++)]; + if (c >= 0) + i |= c << (18 - j * 6); + else + j--; + } + // Add the bytes + dArr[d++] = (byte) (i >> 16); + if (d < len) { + dArr[d++] = (byte) (i >> 8); + if (d < len) + dArr[d++] = (byte) i; + } + } + return dArr; + } + + /** + * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as + * fast as {@link =#=decode(String)}. The preconditions are:
+ * + The array must have a line length of 76 chars OR no line separators at all (one line).
+ * + Line separator must be "\r\n", as specified in RFC 2045 + * + The array must not contain illegal characters within the encoded string
+ * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.
+ * + * @param str The source string. Length 0 will return an empty array. null will throw an exception. + * @return The decoded array of bytes. May be of length 0. + */ + public final static byte[] decodeFast(String str) { + // Check special case + int sLen = str.length(); + if (sLen == 0) + return new byte[0]; + + int sIx = 0, eIx = sLen - 1; // Start and end index after trimming. + + // Trim illegal chars from start + while (sIx < eIx && IA[str.charAt(sIx) & 0xff] < 0) + sIx++; + + // Trim illegal chars from end + while (eIx > 0 && IA[str.charAt(eIx) & 0xff] < 0) + eIx--; + + // get the padding count (=) (0, 1 or 2) + int pad = str.charAt(eIx) == '=' ? (str.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end. + int cCnt = eIx - sIx + 1; // Content count including possible separators + int sepCnt = sLen > 76 ? (str.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; + + int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes + byte[] dArr = new byte[len]; // Preallocate byte[] of exact length + + // Decode all but the last 0 - 2 bytes. + int d = 0; + for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) { + // Assemble three bytes into an int from four "valid" characters. + int i = IA[str.charAt(sIx++)] << 18 | IA[str.charAt(sIx++)] << 12 | IA[str.charAt(sIx++)] << 6 | IA[str.charAt(sIx++)]; + + // Add the bytes + dArr[d++] = (byte) (i >> 16); + dArr[d++] = (byte) (i >> 8); + dArr[d++] = (byte) i; + + // If line separator, jump over it. + if (sepCnt > 0 && ++cc == 19) { + sIx += 2; + cc = 0; + } + } + + if (d < len) { + // Decode last 1-3 bytes (incl '=') into 1-3 bytes + int i = 0; + for (int j = 0; sIx <= eIx - pad; j++) + i |= IA[str.charAt(sIx++)] << (18 - j * 6); + + for (int r = 16; d < len; r -= 8) + dArr[d++] = (byte) (i >> r); + } + + return dArr; + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/ClassUtil.java b/src/main/java/xyz/wbsite/frame/utils/ClassUtil.java new file mode 100644 index 0000000..30c915a --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/ClassUtil.java @@ -0,0 +1,114 @@ +package xyz.wbsite.frame.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; + +/** + * ClassUtil + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ClassUtil { + + /** + * 获取模板类所有字段 + * + * @param clazz 模板对象 + * @param parentFirst 是否关注父类的字段 + * @return + */ + public static Field[] getFields(Class clazz, boolean parentFirst) { + Field[] fields = clazz.getDeclaredFields(); + + if (parentFirst) { + Field[] parentFields = getParentFields(clazz.getSuperclass()); + return concat(fields, parentFields); + } + return fields; + } + + /** + * 判断是不是集合的实现类 + * + * @param clazz + * @return + */ + public static boolean isCollection(Class clazz) { + return Collection.class.isAssignableFrom(clazz); + } + + /** + * 获取GET方法 + * + * @param name 成员变量名 + * @param pojoClass POJO对象 + * @return 方法 + */ + public static Method getMethod(String name, Class pojoClass) throws RuntimeException { + String getMethodName = "get" + StringUtil.upperFirstWord(name); + try { + return pojoClass.getMethod(getMethodName); + } catch (Exception e) { + getMethodName = "is" + StringUtil.upperFirstWord(name); + try { + return pojoClass.getMethod(getMethodName); + } catch (NoSuchMethodException e1) { + throw new RuntimeException("can not find[" + name + "]method"); + } + } + } + + /** + * 获取SET方法 + * + * @param name 成员变量名 + * @param pojoClass POJO对象 + * @return 方法 + */ + public static Method setMethod(String name, Class pojoClass, Class type) { + String setMethodName = "set" + StringUtil.upperFirstWord(name); + try { + return pojoClass.getMethod(setMethodName, type); + } catch (Exception e) { + return null; + } + } + + public static boolean isJavaClass(Field field) { + Class fieldType = field.getType(); + boolean isBaseClass = false; + if (fieldType.isArray()) { + isBaseClass = false; + } else if (fieldType.isPrimitive() || fieldType.getPackage() == null + || fieldType.getPackage().getName().equals("java.lang") + || fieldType.getPackage().getName().equals("java.math") + || fieldType.getPackage().getName().equals("java.sql") + || fieldType.getPackage().getName().equals("java.util")) { + isBaseClass = true; + } + return isBaseClass; + } + + public static Field[] getFields(Class clazz) { + return getFields(clazz, false); + } + + private static Field[] getParentFields(Class parentClazz) { + if (parentClazz != null) { + Field[] fields = parentClazz.getDeclaredFields(); + Field[] parentFields = getParentFields(parentClazz.getSuperclass()); + return concat(fields, parentFields); + } + return new Field[0]; + } + + private static Field[] concat(Field[] f1, Field[] f2) { + Field[] fields = new Field[f1.length + f2.length]; + System.arraycopy(f1, 0, fields, 0, f1.length); + System.arraycopy(f2, 0, fields, f1.length, f2.length); + return fields; + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/CookieUtil.java b/src/main/java/xyz/wbsite/frame/utils/CookieUtil.java new file mode 100644 index 0000000..c407f25 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/CookieUtil.java @@ -0,0 +1,67 @@ +package xyz.wbsite.frame.utils; + +import xyz.wbsite.frame.auth.LocalData; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * CookieUtil + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class CookieUtil { + + /** + * 通过Cookies获取内容 + * + * @param cookies Cookies + * @return passportID + */ + public static String getCookieValue(Cookie[] cookies, String key) { + if (cookies != null) { + for (Cookie cookie : cookies) { + // Cookie中存放的为passport的id ,Cookie名称通过ConfigToolObject获取 + if (cookie != null && cookie.getName().equals(key)) { + try { + return cookie.getValue(); + } catch (Exception ignored) { + } + } + } + } + return null; + } + + /** + * 生成一个默认的Cookie + * + * @param name 键 + * @param value 值 + */ + public static Cookie newCookie(String name, String value) { + HttpServletRequest request = LocalData.getRequest(); + Cookie cookie = new Cookie(name, value); + cookie.setDomain(request.getServerName()); + cookie.setMaxAge(-1); + cookie.setPath("/"); + return cookie; + } + + /** + * 删除一个Cookie + * + * @param name 键 + */ + public static void clearCookie(String name) { + HttpServletRequest request = LocalData.getRequest(); + HttpServletResponse response = LocalData.getResponse(); + Cookie cookie = new Cookie(name, null); + cookie.setDomain(request.getServerName()); + cookie.setMaxAge(0); + cookie.setPath("/"); + response.addCookie(cookie); + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/frame/utils/FileUtil.java b/src/main/java/xyz/wbsite/frame/utils/FileUtil.java new file mode 100644 index 0000000..e2b015b --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/FileUtil.java @@ -0,0 +1,137 @@ +package xyz.wbsite.frame.utils; + +import java.io.*; + +public class FileUtil { + + public static String readFileToString(File file) throws IOException { + byte[] bytes = readFileToByteArray(file); + return new String(bytes, "UTF-8"); + } + + public static byte[] readFileToByteArray(File file) throws IOException { + InputStream in = openInputStream(file); + Throwable var2 = null; + + byte[] var5; + try { + long fileLength = file.length(); + var5 = fileLength > 0L ? toByteArray(in, fileLength) : toByteArray(in); + } catch (Throwable var14) { + var2 = var14; + throw var14; + } finally { + if (in != null) { + if (var2 != null) { + try { + in.close(); + } catch (Throwable var13) { + var2.addSuppressed(var13); + } + } else { + in.close(); + } + } + + } + + return var5; + } + + public static FileInputStream openInputStream(File file) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } else if (!file.canRead()) { + throw new IOException("File '" + file + "' cannot be read"); + } else { + return new FileInputStream(file); + } + } else { + throw new FileNotFoundException("File '" + file + "' does not exist"); + } + } + + public static byte[] toByteArray(InputStream input, long size) throws IOException { + if (size > 2147483647L) { + throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + size); + } else { + return toByteArray(input, (int) size); + } + } + + public static byte[] toByteArray(InputStream input, int size) throws IOException { + if(size < 0) { + throw new IllegalArgumentException("Size must be equal or greater than zero: " + size); + } else if(size == 0) { + return new byte[0]; + } else { + byte[] data = new byte[size]; + + int offset; + int read; + for(offset = 0; offset < size && (read = input.read(data, offset, size - offset)) != -1; offset += read) { + ; + } + + if(offset != size) { + throw new IOException("Unexpected read size. current: " + offset + ", expected: " + size); + } else { + return data; + } + } + } + + public static byte[] toByteArray(InputStream input) throws IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + Throwable var2 = null; + + byte[] var3; + try { + copy((InputStream) input, (OutputStream) output); + var3 = output.toByteArray(); + } catch (Throwable var12) { + var2 = var12; + throw var12; + } finally { + if (output != null) { + if (var2 != null) { + try { + output.close(); + } catch (Throwable var11) { + var2.addSuppressed(var11); + } + } else { + output.close(); + } + } + + } + + return var3; + } + + public static int copy(InputStream input, OutputStream output) throws IOException { + long count = copyLarge(input, output); + return count > 2147483647L ? -1 : (int) count; + } + + + public static long copyLarge(InputStream input, OutputStream output) throws IOException { + return copy(input, output, 4096); + } + + public static long copy(InputStream input, OutputStream output, int bufferSize) throws IOException { + return copyLarge(input, output, new byte[bufferSize]); + } + + public static long copyLarge(InputStream input, OutputStream output, byte[] buffer) throws IOException { + long count; + int n; + for (count = 0L; -1 != (n = input.read(buffer)); count += (long) n) { + output.write(buffer, 0, n); + } + + return count; + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/IDgenerator.java b/src/main/java/xyz/wbsite/frame/utils/IDgenerator.java new file mode 100644 index 0000000..b14dd36 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/IDgenerator.java @@ -0,0 +1,75 @@ +package xyz.wbsite.frame.utils; + +import java.util.UUID; + +/** + * IDgenerator - ID生成器 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class IDgenerator { + + private static long workerId = 1L; + private static long datacenterId = 1L; + private static long sequence = 0L; + // 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) + private static long twepoch = 1531130104852L; + // 机器标识位数 + private static long workerIdBits = 2L;//分布应用可设置为5,这里减少占位增加可支持时间 + // 数据中心标识位数 + private static long datacenterIdBits = 2L;//分布应用可设置为5,这里减少占位增加可支持时间 + // 机器ID最大值 + private static long maxWorkerId = -1L ^ (-1L << workerIdBits); + // 数据中心ID最大值 + private static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + // 毫秒内自增位 + private static long sequenceBits = 12L; + // 机器ID偏左移12位 + private static long workerIdShift = sequenceBits; + // 数据中心ID左移17位 + private static long datacenterIdShift = sequenceBits + workerIdBits; + // 时间毫秒左移22位 + private static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + private static long sequenceMask = -1L ^ (-1L << sequenceBits); + // 上次生产id时间戳 + private static long lastTimestamp = -1L; + + public static synchronized long nextId() { + long timestamp = timeGen(); + + if (timestamp < lastTimestamp) { + throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + if (sequence == 0) { + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0L; + } + + lastTimestamp = timestamp; + + return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence; + } + + protected static long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + protected static long timeGen() { + return System.currentTimeMillis(); + } + + public static String nextUUID() { + return UUID.randomUUID().toString(); + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/LogUtil.java b/src/main/java/xyz/wbsite/frame/utils/LogUtil.java new file mode 100644 index 0000000..434d1dc --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/LogUtil.java @@ -0,0 +1,70 @@ +package xyz.wbsite.frame.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * LogUtil - 日志记录工具类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class LogUtil { + + //定义一个全局的记录器,通过LoggerFactory获取 + private final static Logger logger = LoggerFactory.getLogger(LogUtil.class); + + /** + * 输出info日志 + * @param context 日志内容 + */ + public static void i(String context){ + logger.info(context); + } + + /** + * 输出debug日志 + * @param context 日志内容 + */ + public static void d(String context){ + logger.debug(context); + } + + /** + * 输出warn日志 + * @param context 日志内容 + */ + public static void w(String context){ + logger.warn(context); + } + + /** + * 输出error日志 + * @param context 日志内容 + */ + public static void e(String context){ + logger.error(context); + } + + /** + * 获取异常详细堆栈信息 + */ + public static void dumpException(Throwable e){ + StringBuffer msg = new StringBuffer("null"); + if (e != null) { + msg = new StringBuffer(""); + String message = e.toString(); + int length = e.getStackTrace().length; + if (length > 0) { + msg.append(message).append("\n"); + for (int i = 0; i < length; i++) { + msg.append("\t").append(e.getStackTrace()[i]).append("\n"); + } + } else { + msg.append(message); + } + } + logger.error(msg.toString()); + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/MD5Util.java b/src/main/java/xyz/wbsite/frame/utils/MD5Util.java new file mode 100644 index 0000000..db3831f --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/MD5Util.java @@ -0,0 +1,91 @@ +package xyz.wbsite.frame.utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * MD5Util - MD5工具类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class MD5Util { + + /** + * 生成加密密码 + * + * @param value 待加密字符串 + * @param salts 加密盐 + * @return 加密字符串 + */ + public static String generatePwd(String value, String... salts) { + String pwd = encode(value); + for (String s : salts) { + pwd = encode(s + value + s); + } + return pwd; + } + + /** + * 生成加密密码 + * + * @param value 待加密字符串 + * @return 加密字符串 + */ + public static String generatePwd(String value) { + return generatePwd(value, "MD5"); + } + + /** + * 加密字符串 + * + * @param value 待加密字符串 + * @return 信息摘要Hex字符串 + */ + public static String encode(String value) { + try { + MessageDigest md = MessageDigest.getInstance("md5"); + byte[] e = md.digest(value.getBytes()); + return toHexString(e); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return value; + } + } + + /** + * 加密字符串 + * + * @param bytes 待加密字节数组 + * @return 信息摘要Hex字符串 + */ + public static String encode(byte[] bytes) { + try { + MessageDigest md = MessageDigest.getInstance("md5"); + byte[] e = md.digest(bytes); + return toHexString(e); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return ""; + } + } + + /** + * @param bytes 信息摘要字节数组 + * @return Hex字符串 + */ + private static String toHexString(byte bytes[]) { + StringBuilder hs = new StringBuilder(); + String stmp = ""; + for (int n = 0; n < bytes.length; n++) { + stmp = Integer.toHexString(bytes[n] & 0xff); + if (stmp.length() == 1) + hs.append("0").append(stmp); + else + hs.append(stmp); + } + + return hs.toString(); + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/MapperUtil.java b/src/main/java/xyz/wbsite/frame/utils/MapperUtil.java new file mode 100644 index 0000000..215c500 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/MapperUtil.java @@ -0,0 +1,145 @@ +package xyz.wbsite.frame.utils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.TreeNode; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; +import org.dozer.DozerBeanMapper; +import org.dozer.Mapper; +import xyz.wbsite.frame.auth.LocalData; + +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.List; + +/** + * MapUtil - 映射转化工具类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class MapperUtil { + private static ObjectMapper om; + private static Mapper mapper; + + static { + try {// 优先获取SpringBoot默认配置 + om = LocalData.getBean(ObjectMapper.class); + } catch (Exception e) { + e.printStackTrace(); + } + if (null == om) { + //初始化 + om = new ObjectMapper(); + // 日期格式化 + om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + //序列化时忽略null属性 + om.setSerializationInclusion(JsonInclude.Include.NON_NULL); + //序列化时排序 + om.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true); + //反序列化是忽略多余字段 + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + //支持空类序列化时出错InvalidDefinitionException + om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + } + + mapper = new DozerBeanMapper(); + } + + public static TreeNode toTree(String json) { + try { + return om.readTree(json); + } catch (IOException e) { + e.printStackTrace(); + return om.createObjectNode(); + } + } + + public static TreeNode toTree(Object json) { + return om.valueToTree(json); + } + + public static String toJson(Object object) { + return toJson(object, false); + } + + public static String toJson(Object object, boolean pretty) { + try { + if (pretty) { + return om.writerWithDefaultPrettyPrinter().writeValueAsString(object); + } else { + return om.writeValueAsString(object); + } + } catch (JsonProcessingException e) { + e.printStackTrace(); + return "{}"; + } + } + + public static String toJson(JsonNode jsonNode) { + return jsonNode.asText(); + } + + public static T toJava(String json, Class cls) { + try { + if (json == null || "".equals(json)) { + return cls.newInstance(); + } + return om.readValue(json, cls); + } catch (IOException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } + return null; + } + + public static T toJava(TreeNode treeNode, Class cls) { + try { + return om.treeToValue(treeNode, cls); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + public static T toJava(String json, TypeReference valueTypeRef) { + try { + return om.readValue(json, valueTypeRef); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static List toJavaList(String json, TypeReference> reference) { + try { + return om.readValue(json, reference); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static List toJavaList(InputStream json, TypeReference> reference) { + try { + return om.readValue(json, reference); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static T map(Object o, Class aClass) { + return mapper.map(o, aClass); + } + + public static void map(Object o, Object o1) { + mapper.map(o, o1); + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/Message.java b/src/main/java/xyz/wbsite/frame/utils/Message.java new file mode 100644 index 0000000..8be65c7 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/Message.java @@ -0,0 +1,20 @@ +package xyz.wbsite.frame.utils; + +/** + * Message - 基本消息类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class Message { + public static final String ERROR_500 = "服务器走了下神,稍后再试一次"; + public static final String NOT_EXIST_METHOD = "调用的方法不存在"; + + public static String CREATE_FAILURE = "创建失败,请刷新后重新尝试"; + public static String DELETE_FAILURE = "删除失败,请刷新后重新尝试"; + public static String UPDATE_FAILURE = "更新失败,请刷新后重新尝试"; + public static String FIND_FAILURE = "查询失败,请刷新后重新尝试"; + public static String GET_FAILURE = "未获取相应内容"; + public static String INSERT_DUPLICATE = "已经存在相同数据,返回列表页面确认"; +} diff --git a/src/main/java/xyz/wbsite/frame/utils/ProcessUtil.java b/src/main/java/xyz/wbsite/frame/utils/ProcessUtil.java new file mode 100644 index 0000000..b282322 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/ProcessUtil.java @@ -0,0 +1,117 @@ +package xyz.wbsite.frame.utils; + +import java.awt.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * 程序操作工具 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ProcessUtil { + /** + * 启动windows系统下的exe文件 + * @param path 可执行exe文件路径 + */ + public static void execExe(String path) { + Runtime rn = Runtime.getRuntime(); + Process p = null; + try { + p = rn.exec(path); + } catch (Exception e) { + System.out.println("Error exec!"); + } + } + + /** + * 执行windows批处理文件路 + * @param path 可执行批处理文件路径 + */ + public static void execBat(String path) { + Runtime rn = Runtime.getRuntime(); + Process p = null; + try { + p = rn.exec(path); + } catch (Exception e) { + System.out.println("Error exec!"); + } + } + + /** + * 执行windows cmd命令 + * @param command cmd命令 + */ + public static String execCmd(String command) { + return exec("cmd /c " + command); + } + + + /** + * 执行运行环境命令 + * @param command cmd命令 + */ + public static String exec(String command) { + StringBuilder build = new StringBuilder(); + Runtime runtime = Runtime.getRuntime(); + Process process = null; + try { + process = runtime.exec(command); + BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8")); + String line = null; + while ((line = br.readLine()) != null) { + build.append(line); + } + process.destroy(); + } catch (IOException e) { + e.printStackTrace(); + } + return build.toString(); + } + + /** + * @throws IOException + * @desc 杀死进程 + * @author zp + * @date 2018-3-29 + */ + public static void killProc(String processName) throws IOException { + if (processName != null && !"".equals(processName)) { + execCmd("taskkill /F /IM " + processName); + } + } + + /** + * @desc 判断进程是否开启 + * @author zp + * @date 2018-3-29 + */ + public static boolean findProcess(String processName) { + BufferedReader bufferedReader = null; + try { + Process proc = Runtime.getRuntime().exec("tasklist -fi " + '"' + "imagename eq " + processName + '"'); + bufferedReader = new BufferedReader(new InputStreamReader(proc.getInputStream())); + String line = null; + while ((line = bufferedReader.readLine()) != null) { + if (line.contains(processName)) { + return true; + } + } + return false; + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } finally { + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (Exception ex) { + } + } + } + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/RSAUtil.java b/src/main/java/xyz/wbsite/frame/utils/RSAUtil.java new file mode 100644 index 0000000..daf49a3 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/RSAUtil.java @@ -0,0 +1,308 @@ +package xyz.wbsite.frame.utils; + +import javax.crypto.Cipher; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; + +/** + * RSAUtil - RSA工具类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class RSAUtil { + + private static String cryptPublicKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCTrwfsrJjCF+pP4S3A/wrD4U1txg53EuBC1mPt" + + "3vGXvSK2U0YNRVR3Q65ooHnPKmk4LwI8v+7+ATTxUg3qkuRiDuzBa5zLkYKM50LOgEWSdOKzbnbx" + + "a5FnE7IXawNt1p8+MVN1TTI7J/fZy6g1x0WBy1odE5Osru4WfZNOqQtjHwIDAQAB"; + private static String cryptPrivateKeyBase64 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJOvB+ysmMIX6k/hLcD/CsPhTW3G" + + "DncS4ELWY+3e8Ze9IrZTRg1FVHdDrmigec8qaTgvAjy/7v4BNPFSDeqS5GIO7MFrnMuRgoznQs6A" + + "RZJ04rNudvFrkWcTshdrA23Wnz4xU3VNMjsn99nLqDXHRYHLWh0Tk6yu7hZ9k06pC2MfAgMBAAEC" + + "gYBjLRjKRMI1HfBZgmPChsPI9YWU4XuXVVLLL8Rd2uktOHOWM2gIw3VMvmPimVoT2GxesZr0BwTN" + + "CSxvnuX/kHPTqtsIu1r5Iup3mGbvlj3sn8RvG0yvUDglDN7QVDqqN7XWvHJSBVfBzDXeExA/WGnE" + + "6BOocNT9qkqA/UWNbCXGKQJBAN0Fd/P2D6EvCd2RztHhzVE6V8s/LwOTDnGn/YhdMpddy9TwZpBi" + + "r7I6lzcLWQ1HfDUive3t+DGXqPqr/4FfkG0CQQCrDlZKf216QrXOmJ70LQSbflgvGYU+b6kLFyEh" + + "+15HcIBfKUQCU+XUK4UzLMQDYxdngTNMNyq4AQ9Sh0tUTUI7AkEAtkq9XayzxWhLhcCtyTOoqPcq" + + "1Aqf1x3iCuHYXTEo+ek1pcJFhY6vhJuIfrDQWQB9tEGcTvI4A4cnquBTkzvjnQJAYid58ImqYmuB" + + "M6l0HJzwdeFL7MryIF+mWozNIFjDQq8VmoVtVwCZcuP+LN1VJLRpq6UBsIw/YRKKnkqwORGUHQJA" + + "UuR0G/3Hai+vKDA14tIYIH6C4zNmbULxAEuQVh9thfafWNmiDcifApvkxQ2ewXwEGeJtz44zv6iY" + + "3f3yq+a2OQ=="; + + private static String signPublicKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCTrwfsrJjCF+pP4S3A/wrD4U1txg53EuBC1mPt" + + "3vGXvSK2U0YNRVR3Q65ooHnPKmk4LwI8v+7+ATTxUg3qkuRiDuzBa5zLkYKM50LOgEWSdOKzbnbx" + + "a5FnE7IXawNt1p8+MVN1TTI7J/fZy6g1x0WBy1odE5Osru4WfZNOqQtjHwIDAQAB"; + private static String signPrivateKeyBase64 = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJOvB+ysmMIX6k/hLcD/CsPhTW3G" + + "DncS4ELWY+3e8Ze9IrZTRg1FVHdDrmigec8qaTgvAjy/7v4BNPFSDeqS5GIO7MFrnMuRgoznQs6A" + + "RZJ04rNudvFrkWcTshdrA23Wnz4xU3VNMjsn99nLqDXHRYHLWh0Tk6yu7hZ9k06pC2MfAgMBAAEC" + + "gYBjLRjKRMI1HfBZgmPChsPI9YWU4XuXVVLLL8Rd2uktOHOWM2gIw3VMvmPimVoT2GxesZr0BwTN" + + "CSxvnuX/kHPTqtsIu1r5Iup3mGbvlj3sn8RvG0yvUDglDN7QVDqqN7XWvHJSBVfBzDXeExA/WGnE" + + "6BOocNT9qkqA/UWNbCXGKQJBAN0Fd/P2D6EvCd2RztHhzVE6V8s/LwOTDnGn/YhdMpddy9TwZpBi" + + "r7I6lzcLWQ1HfDUive3t+DGXqPqr/4FfkG0CQQCrDlZKf216QrXOmJ70LQSbflgvGYU+b6kLFyEh" + + "+15HcIBfKUQCU+XUK4UzLMQDYxdngTNMNyq4AQ9Sh0tUTUI7AkEAtkq9XayzxWhLhcCtyTOoqPcq" + + "1Aqf1x3iCuHYXTEo+ek1pcJFhY6vhJuIfrDQWQB9tEGcTvI4A4cnquBTkzvjnQJAYid58ImqYmuB" + + "M6l0HJzwdeFL7MryIF+mWozNIFjDQq8VmoVtVwCZcuP+LN1VJLRpq6UBsIw/YRKKnkqwORGUHQJA" + + "UuR0G/3Hai+vKDA14tIYIH6C4zNmbULxAEuQVh9thfafWNmiDcifApvkxQ2ewXwEGeJtz44zv6iY" + + "3f3yq+a2OQ=="; + + /** + * 创建密钥和私钥 + */ + public static void createKey() { + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + //公钥 + RSAPublicKey aPublic = (RSAPublicKey) keyPair.getPublic(); + //私钥 + RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate(); + //把密钥对象对应的字节转为Base64字符存储 + System.err.println("publicKeyBase64-->" + Base64Util.encodeToString(aPublic.getEncoded())); + System.err.println("privateKeyBase64-->" + Base64Util.encodeToString(aPrivate.getEncoded())); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public static String encrypt2Base64(byte[] data) { + byte[] encrypt = encrypt(data); + return Base64Util.encodeToString(encrypt); + } + + public static byte[] encrypt(String data) { + return encrypt(data.getBytes()); + } + + /** + * 加密 + * + * @param data 待加密数据 + */ + public static byte[] encrypt(byte[] data) { + try { + //生成公钥对象 + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64Util.decode(cryptPublicKeyBase64)); + PublicKey aPublic = keyFactory.generatePublic(x509EncodedKeySpec); + + //分段加密开始 + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + Cipher rsa = Cipher.getInstance("RSA"); + rsa.init(Cipher.ENCRYPT_MODE, aPublic); + int offset = 0; + while (offset < data.length) { + byte[] bytes = rsa.doFinal(Arrays.copyOfRange(data, offset, Math.min(offset + 117, data.length))); + bs.write(bytes); + offset += 117; + } + return bs.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 加密 + * + * @param data 待加密明文 + */ + public static byte[] encrypt(String data, PublicKey aPublic) { + try { + if (aPublic == null) { + System.err.println("PublicKey can not be null"); + return null; + } + + //分段加密开始 + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + Cipher rsa = Cipher.getInstance("RSA"); + rsa.init(Cipher.ENCRYPT_MODE, aPublic); + int offset = 0; + byte[] b = data.getBytes(); + while (offset < b.length) { + byte[] bytes = rsa.doFinal(Arrays.copyOfRange(b, offset, Math.min(offset + 117, b.length))); + bs.write(bytes); + offset += 117; + } + return bs.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * @param base64String base64编码字符串 + * @return + */ + public static byte[] decrypt(String base64String) { + return decrypt(Base64Util.decode(base64String)); + } + + public static String decrypt2String(String base64String) { + byte[] decrypt = decrypt(Base64Util.decode(base64String)); + try { + return new String(decrypt, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 解密 + * + * @param data 已加密字节 + */ + public static byte[] decrypt(byte[] data) { + try { + //生成私钥对象 + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64Util.decode(cryptPrivateKeyBase64)); + PrivateKey aPrivate = keyFactory.generatePrivate(pkcs8EncodedKeySpec); + + Cipher rsa = Cipher.getInstance("RSA"); + rsa.init(Cipher.DECRYPT_MODE, aPrivate); + //获得密文字节 + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + int offset = 0; + while (offset < data.length) { + byte[] bytes = rsa.doFinal(Arrays.copyOfRange(data, offset, Math.min(offset + 128, data.length))); + bs.write(bytes); + offset += 128; + } + return bs.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 解密 + * + * @param data 已加密字节 + * @param aPrivate 公钥 + * @return 解密后的字节 + */ + public static byte[] decrypt(byte[] data, PublicKey aPrivate) { + try { + if (aPrivate == null) { + System.err.println("PublicKey can not be null"); + return null; + } + + Cipher rsa = Cipher.getInstance("RSA"); + rsa.init(Cipher.DECRYPT_MODE, aPrivate); + //获得密文字节 + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + int offset = 0; + while (offset < data.length) { + byte[] bytes = rsa.doFinal(Arrays.copyOfRange(data, offset, Math.min(offset + 128, data.length))); + bs.write(bytes); + offset += 128; + } + return bs.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static String sign2Base64(byte[] data) { + return sign2Base64(data, signPrivateKeyBase64); + } + + public static String sign2Base64(byte[] data, String privateKey) { + byte[] sign = sign(data, privateKey); + return Base64Util.encodeToString(sign); + } + + /** + * RSA签名 + * + * @param data 待签名数据 + * @param privateKey 私钥 + * @return 签名字节数组 + */ + public static byte[] sign(byte[] data, String privateKey) { + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64Util.decode(privateKey)); + PrivateKey aPrivate = keyFactory.generatePrivate(priPKCS8); + + Signature signature = Signature.getInstance("SHA1WithRSA"); + + signature.initSign(aPrivate); + signature.update(data); + return signature.sign(); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + /** + * RSA验签名检查 + * + * @param data 待签名数据 + * @param sign base64 签名字符串 + * @param publicKey 公钥 + * @return 布尔值 + */ + public static boolean doCheck(byte[] data, String sign, String publicKey) { + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + byte[] encodedKey = Base64Util.decode(publicKey); + PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey)); + Signature signature = Signature.getInstance("SHA1WithRSA"); + signature.initVerify(pubKey); + signature.update(data); + return signature.verify(Base64Util.decode(sign)); + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + public static boolean doCheck(byte[] data, String sign) { + return doCheck(data, sign, signPublicKeyBase64); + } + + public static PublicKey parsePublicKey(String cryptPublicKeyBase64) { + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64Util.decode(cryptPublicKeyBase64)); + return keyFactory.generatePublic(x509EncodedKeySpec); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + } + return null; + } + + public static PrivateKey parsePrivateKey(String cryptPrivateKeyBase64) { + try { + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64Util.decode(cryptPrivateKeyBase64)); + return keyFactory.generatePrivate(priPKCS8); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/RequestUtil.java b/src/main/java/xyz/wbsite/frame/utils/RequestUtil.java new file mode 100644 index 0000000..a9fe557 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/RequestUtil.java @@ -0,0 +1,79 @@ +package xyz.wbsite.frame.utils; + +import javax.servlet.http.HttpServletRequest; + +/** + * RequestUtil + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class RequestUtil { + + /** + * 获取请求放IP + * + * @param request + * @return + */ + public static String getIp(HttpServletRequest request) { + String ip = request.getHeader("X-Forwarded-For"); + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + } else if (ip.length() > 15) { + String[] ips = ip.split(","); + for (int index = 0; index < ips.length; index++) { + String strIp = (String) ips[index]; + if (!("unknown".equalsIgnoreCase(strIp))) { + ip = strIp; + break; + } + } + } + return ip; + } + + /** + * 获取情况方客户端信息 + * + * @param request 请求 + * @return 客户端信息 + */ + public static String getUserAgent(HttpServletRequest request) { + return request.getHeader("User-Agent"); + } + + /** + * 获取转发至错误页之前的请求URL + * + * @param request 请求 + * @return 请求URL + */ + public static String getErrorUrl(HttpServletRequest request) { + if (request.getAttribute("javax.servlet.error.request_uri") != null) { + return (String) request.getAttribute("javax.servlet.error.request_uri"); + } else { + return ""; + } + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/ResourceUtil.java b/src/main/java/xyz/wbsite/frame/utils/ResourceUtil.java new file mode 100644 index 0000000..eab3c72 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/ResourceUtil.java @@ -0,0 +1,144 @@ +package xyz.wbsite.frame.utils; + +import org.springframework.boot.system.ApplicationHome; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.ResourceUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Resource资源文件工具 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ResourceUtil extends ResourceUtils { + + /** + * 获取资源目录下所有文件名 如: /modules/dir/ + * + * @param resourcePath 路径 + * @return + */ + public static List getResourceFiles(String resourcePath) { + List result = new ArrayList<>(); + File applicationHome = getApplicationHome(); + if (applicationHome.getAbsolutePath().endsWith(".jar")) { + try { + Pattern pattern = Pattern.compile(".*" + resourcePath + "(.+\\..+)"); + JarFile jarFile = new JarFile(applicationHome); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry jarEntry = entries.nextElement(); + String name = jarEntry.getName(); + + if (name.matches(".*" + resourcePath + "(.+\\..+)")) { + Matcher matcher = pattern.matcher(name); + if (matcher.find()) result.add(matcher.group(1)); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } else { + try { + ClassPathResource cpr = new ClassPathResource(resourcePath); + File in = cpr.getFile(); + for (File file : in.listFiles()) { + result.add(file.getName()); + } + return result; + } catch (IOException e) { + e.printStackTrace(); + } + } + + return result; + } + + /** + * 获取当前运行jar文件 + * + * @return jar路径 + */ + public static File getApplicationHome() { + ApplicationHome home = new ApplicationHome(ResourceUtil.class); + return home.getSource(); + } + + /** + * 获取resource下文件 + * + * @return 文件路径 + */ + public static InputStream getResourceInput(String resource) { + try { + ClassPathResource classPathResource = new ClassPathResource(resource); + return classPathResource.getInputStream(); + } catch (IOException e) { + return null; + } + } + + /** + * 复制jar下的资源文件到指定文件 + * + * @return 成功或失败情况 + */ + public static boolean copyResource2File(String resource, File file) { + InputStream resourceInput = getResourceInput(resource); + FileOutputStream fileOutputStream = null; + try { + if (!file.exists()) { + file.createNewFile(); + } + fileOutputStream = new FileOutputStream(file); + FileUtil.copy(resourceInput, fileOutputStream); + } catch (IOException e) { + e.printStackTrace(); + return false; + } finally { + try { + if (fileOutputStream != null) fileOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return true; + } + + /** + * 获取资源的的字节数组 + * + * @return 字节数组 + */ + public static byte[] getResourceBytes(String resource) { + InputStream is = null; + byte[] result = null; + try { + is = getResourceInput(resource); + result = new byte[is.available()]; + is.read(result); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (is != null) is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return result; + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/ResponseUtil.java b/src/main/java/xyz/wbsite/frame/utils/ResponseUtil.java new file mode 100644 index 0000000..48d02e7 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/ResponseUtil.java @@ -0,0 +1,42 @@ +package xyz.wbsite.frame.utils; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import xyz.wbsite.frame.excel.WExcel; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +/** + * ResponseUtil + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ResponseUtil { + + /** + * 包装ResponseEntity + * + * @param bytes 字节数组 + * @param fileName 文件名 + * @return + */ + public static ResponseEntity apply(byte[] bytes, String fileName) { + try { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + headers.setContentDispositionFormData("attachment", URLEncoder.encode(fileName, "utf-8")); + return new ResponseEntity<>(bytes, headers, HttpStatus.OK); + } catch (UnsupportedEncodingException e) { + return null; + } + } + + public static ResponseEntity apply(WExcel sheet) { + return apply(sheet.getBytes(), sheet.getName() + ".xlsx"); + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/StringUtil.java b/src/main/java/xyz/wbsite/frame/utils/StringUtil.java new file mode 100644 index 0000000..767dbe8 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/StringUtil.java @@ -0,0 +1,87 @@ +package xyz.wbsite.frame.utils; + +public class StringUtil { + + public static int getByteLength(String str) { + int length = str.replaceAll("[^\\x00-\\xff]", "**").length(); + return length; + } + + public static String upperFirstWord(String str) { + String temp = str.substring(0, 1); + return temp.toUpperCase() + str.substring(1); + } + + public static boolean isEmpty(String value) { + int strLen; + if (value == null || (strLen = value.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if ((Character.isWhitespace(value.charAt(i)) == false)) { + return false; + } + } + return true; + } + + + public static boolean isNotEmpty(String value) { + return !isEmpty(value); + } + + + /** + * 检查对象是否为数字型字符串,包含负数开头的。 + */ + public static boolean isNumeric(Object obj) { + if (obj == null) { + return false; + } + char[] chars = obj.toString().toCharArray(); + int length = chars.length; + if (length < 1) + return false; + + int i = 0; + if (length > 1 && chars[0] == '-') + i = 1; + + for (; i < length; i++) { + if (!Character.isDigit(chars[i])) { + return false; + } + } + return true; + } + + /** + * 把通用字符编码的字符串转化为汉字编码。 + */ + public static String unicodeToChinese(String unicode) { + StringBuilder out = new StringBuilder(); + if (!isEmpty(unicode)) { + for (int i = 0; i < unicode.length(); i++) { + out.append(unicode.charAt(i)); + } + } + return out.toString(); + } + + + public static String toUnderlineStyle(String name) { + StringBuilder newName = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (Character.isUpperCase(c)) { + if (i > 0) { + newName.append("_"); + } + newName.append(Character.toLowerCase(c)); + } else { + newName.append(c); + } + } + return newName.toString(); + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/UrlUtil.java b/src/main/java/xyz/wbsite/frame/utils/UrlUtil.java new file mode 100644 index 0000000..73e2944 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/UrlUtil.java @@ -0,0 +1,41 @@ +package xyz.wbsite.frame.utils; + +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.util.Locale; + +/** + * Url工具类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@Component +public class UrlUtil { + + public String getUrl(String url) { + if (url == null) { + return ""; + } + + if (!url.startsWith("/")) { + return "/" + url; + } + + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + // 协议 + String scheme = request.getScheme(); + // 域名 + String serverName = request.getServerName(); + // 端口 + int serverPort = request.getServerPort(); + // 上下文路径 + String context = request.getContextPath(); + + return String.format(Locale.CHINA, "%s://%s:%d%s%s", scheme, serverName, serverPort, context, url); + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/ValidationUtil.java b/src/main/java/xyz/wbsite/frame/utils/ValidationUtil.java new file mode 100644 index 0000000..78b5d04 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/ValidationUtil.java @@ -0,0 +1,72 @@ +package xyz.wbsite.frame.utils; + +import xyz.wbsite.frame.base.BaseRequest; +import xyz.wbsite.frame.base.BaseResponse; +import xyz.wbsite.frame.base.ErrorType; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * 验证工具类。提供一些通用简单的数据验证功能 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class ValidationUtil { + + private static ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + + public static T validate(BaseRequest req, T response) { + + if (req == null) { + response.addError(ErrorType.EXPECTATION_NULL, "请求对象不能为空"); + return response; + } + + try { + Validator validator = factory.getValidator(); + + Set> constraintViolations = validator.validate(req); + + if (constraintViolations.size() > 0) { + for (ConstraintViolation violation : constraintViolations) { + response.addError(ErrorType.INVALID_PARAMETER, violation.getMessage()); + } + } + } catch (Exception e) { + e.printStackTrace(); + LogUtil.dumpException(e); + response.addError(ErrorType.BUSINESS_ERROR, e.getMessage()); + } + return response; + } + + public static List validate(Object req) { + List validResult = new ArrayList<>(); + if (req == null) { + validResult.add("当前数据为空"); + return validResult; + } + + try { + Validator validator = factory.getValidator(); + Set> constraintViolations = validator.validate(req); + if (constraintViolations.size() > 0) { + for (ConstraintViolation violation : constraintViolations) { + validResult.add(violation.getMessage()); + } + } + } catch (Exception e) { + e.printStackTrace(); + validResult.add("数据检查错误"); + } + return validResult; + } +} diff --git a/src/main/java/xyz/wbsite/frame/utils/WebUtils.java b/src/main/java/xyz/wbsite/frame/utils/WebUtils.java new file mode 100644 index 0000000..e071ac2 --- /dev/null +++ b/src/main/java/xyz/wbsite/frame/utils/WebUtils.java @@ -0,0 +1,459 @@ +package xyz.wbsite.frame.utils; + +import javax.net.ssl.*; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * WebUtils - 模拟http访问工具类 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class WebUtils { + private static final String DEFAULT_CHARSET = "UTF-8"; + private static final String METHOD_POST = "POST"; + private static final String METHOD_GET = "GET"; + //private static final Certificate verisign; // + private static boolean ignoreSSLCheck; // 忽略SSL检查 + + private static class TrustAllTrustManager implements X509TrustManager { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + } + + private WebUtils() { + } + + public static void setIgnoreSSLCheck(boolean ignoreSSLCheck) { + WebUtils.ignoreSSLCheck = ignoreSSLCheck; + } + + /** + * 执行HTTP POST请求。 + * + * @param url 请求地址 + * @param params 请求参数 + * @return 响应字符串 + */ + public static String doPost(String url, Map params, int connectTimeout, int readTimeout) throws IOException { + return doPost(url, params, DEFAULT_CHARSET, connectTimeout, readTimeout); + } + + /** + * 执行HTTP POST请求。 + * + * @param url 请求地址 + * @param params 请求参数 + * @param charset 字符集,如UTF-8, GBK, GB2312 + * @return 响应字符串 + */ + public static String doPost(String url, Map params, String charset, int connectTimeout, int readTimeout) throws IOException { + return doPost(url, params, charset, connectTimeout, readTimeout, null); + } + + public static String doPost(String url, Map params, String charset, int connectTimeout, int readTimeout, Map headerMap) throws IOException { + String ctype = "application/x-www-form-urlencoded;charset=" + charset; + String query = buildQuery(params, charset); + byte[] content = {}; + if (query != null) { + content = query.getBytes(charset); + } + return _doPost(url, ctype, content, connectTimeout, readTimeout, headerMap); + } + + /** + * 执行HTTP POST请求。 + * + * @param url 请求地址 + * @param ctype 请求类型 + * @param content 请求字节数组 + * @return 响应字符串 + */ + public static String doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout) throws IOException { + return _doPost(url, ctype, content, connectTimeout, readTimeout, null); + } + + private static String _doPost(String url, String ctype, byte[] content, int connectTimeout, int readTimeout, Map headerMap) throws IOException { + HttpURLConnection conn = null; + OutputStream out = null; + String rsp = null; + try { + try { + conn = getConnection(new URL(url), METHOD_POST, ctype, headerMap); + conn.setConnectTimeout(connectTimeout); + conn.setReadTimeout(readTimeout); + } catch (IOException e) { + throw e; + } + try { + out = conn.getOutputStream(); + out.write(content); + rsp = getResponseAsString(conn); + } catch (IOException e) { + throw e; + } + } finally { + if (out != null) { + out.close(); + } + if (conn != null) { + conn.disconnect(); + } + } + + return rsp; + } + + private static byte[] getTextEntry(String fieldName, String fieldValue, String charset) throws IOException { + StringBuilder entry = new StringBuilder(); + entry.append("Content-Disposition:form-data;name=\""); + entry.append(fieldName); + entry.append("\"\r\nContent-Type:text/plain\r\n\r\n"); + entry.append(fieldValue); + return entry.toString().getBytes(charset); + } + + private static byte[] getFileEntry(String fieldName, String fileName, String mimeType, String charset) throws IOException { + StringBuilder entry = new StringBuilder(); + entry.append("Content-Disposition:form-data;name=\""); + entry.append(fieldName); + entry.append("\";filename=\""); + entry.append(fileName); + entry.append("\"\r\nContent-Type:"); + entry.append(mimeType); + entry.append("\r\n\r\n"); + return entry.toString().getBytes(charset); + } + + /** + * 执行HTTP GET请求。 + * + * @param url 请求地址 + * @param params 请求参数 + * @return 响应字符串 + */ + public static String doGet(String url, Map params) throws IOException { + return doGet(url, params, DEFAULT_CHARSET); + } + + /** + * 执行HTTP GET请求。 + * + * @param url 请求地址 + * @param params 请求参数 + * @param charset 字符集,如UTF-8, GBK, GB2312 + * @return 响应字符串 + */ + public static String doGet(String url, Map params, String charset) throws IOException { + HttpURLConnection conn = null; + String rsp = null; + + try { + String ctype = "application/x-www-form-urlencoded;charset=" + charset; + String query = buildQuery(params, charset); + try { + conn = getConnection(buildGetUrl(url, query), METHOD_GET, ctype, null); + } catch (IOException e) { + throw e; + } + + try { + rsp = getResponseAsString(conn); + } catch (IOException e) { + throw e; + } + } finally { + if (conn != null) { + conn.disconnect(); + } + } + + return rsp; + } + + private static HttpURLConnection getConnection(URL url, String method, String ctype, Map headerMap) throws IOException { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + if (conn instanceof HttpsURLConnection) { + HttpsURLConnection connHttps = (HttpsURLConnection) conn; + if (ignoreSSLCheck) { + try { + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(null, new TrustManager[]{new TrustAllTrustManager()}, new SecureRandom()); + connHttps.setSSLSocketFactory(ctx.getSocketFactory()); + connHttps.setHostnameVerifier(new HostnameVerifier() { + public boolean verify(String hostname, SSLSession session) { + return true; + } + }); + } catch (Exception e) { + throw new IOException(e); + } + } else { + try { + //SSLContext ctx = SSLContext.getInstance("TLS"); + //ctx.init(null, new TrustManager[] { new VerisignTrustManager() }, new SecureRandom()); + //connHttps.setSSLSocketFactory(ctx.getSocketFactory()); + } catch (Exception e) { + throw new IOException(e); + } + } + conn = connHttps; + } + + conn.setRequestMethod(method); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setRequestProperty("Accept", "text/xml,text/javascript"); + conn.setRequestProperty("User-Agent", "top-sdk-java"); + conn.setRequestProperty("Content-Type", ctype); + if (headerMap != null) { + for (Map.Entry entry : headerMap.entrySet()) { + conn.setRequestProperty(entry.getKey(), entry.getValue()); + } + } + return conn; + } + + private static URL buildGetUrl(String strUrl, String query) throws IOException { + URL url = new URL(strUrl); + if (isEmpty(query)) { + return url; + } + + if (isEmpty(url.getQuery())) { + if (strUrl.endsWith("?")) { + strUrl = strUrl + query; + } else { + strUrl = strUrl + "?" + query; + } + } else { + if (strUrl.endsWith("&")) { + strUrl = strUrl + query; + } else { + strUrl = strUrl + "&" + query; + } + } + + return new URL(strUrl); + } + + public static String buildQuery(Map params, String charset) throws IOException { + if (params == null || params.isEmpty()) { + return null; + } + + StringBuilder query = new StringBuilder(); + Set> entries = params.entrySet(); + boolean hasParam = false; + + for (Map.Entry entry : entries) { + String name = entry.getKey(); + String value = entry.getValue(); + // 忽略参数名或参数值为空的参数 + if (areNotEmpty(name, value)) { + if (hasParam) { + query.append("&"); + } else { + hasParam = true; + } + + query.append(name).append("=").append(URLEncoder.encode(value, charset)); + } + } + + return query.toString(); + } + + public static boolean isEmpty(String value) { + int strLen; + if (value == null || (strLen = value.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if ((Character.isWhitespace(value.charAt(i)) == false)) { + return false; + } + } + return true; + } + + /** + * 检查指定的字符串列表是否不为空。 + */ + public static boolean areNotEmpty(String... values) { + boolean result = true; + if (values == null || values.length == 0) { + result = false; + } else { + for (String value : values) { + result &= !isEmpty(value); + } + } + return result; + } + + protected static String getResponseAsString(HttpURLConnection conn) throws IOException { + String charset = getResponseCharset(conn.getContentType()); + InputStream es = conn.getErrorStream(); + if (es == null) { + return getStreamAsString(conn.getInputStream(), charset); + } else { + String msg = getStreamAsString(es, charset); + if (isEmpty(msg)) { + throw new IOException(conn.getResponseCode() + ":" + conn.getResponseMessage()); + } else { + throw new IOException(msg); + } + } + } + + private static String getStreamAsString(InputStream stream, String charset) throws IOException { + try { + Reader reader = new InputStreamReader(stream, charset); + StringBuilder response = new StringBuilder(); + + final char[] buff = new char[1024]; + int read = 0; + while ((read = reader.read(buff)) > 0) { + response.append(buff, 0, read); + } + + return response.toString(); + } finally { + if (stream != null) { + stream.close(); + } + } + } + + private static String getResponseCharset(String ctype) { + String charset = DEFAULT_CHARSET; + + if (!isEmpty(ctype)) { + String[] params = ctype.split(";"); + for (String param : params) { + param = param.trim(); + if (param.startsWith("charset")) { + String[] pair = param.split("=", 2); + if (pair.length == 2) { + if (!isEmpty(pair[1])) { + charset = pair[1].trim(); + } + } + break; + } + } + } + + return charset; + } + + /** + * 使用默认的UTF-8字符集反编码请求参数值。 + * + * @param value 参数值 + * @return 反编码后的参数值 + */ + public static String decode(String value) { + return decode(value, DEFAULT_CHARSET); + } + + /** + * 使用默认的UTF-8字符集编码请求参数值。 + * + * @param value 参数值 + * @return 编码后的参数值 + */ + public static String encode(String value) { + return encode(value, DEFAULT_CHARSET); + } + + /** + * 使用指定的字符集反编码请求参数值。 + * + * @param value 参数值 + * @param charset 字符集 + * @return 反编码后的参数值 + */ + public static String decode(String value, String charset) { + String result = null; + if (!isEmpty(value)) { + try { + result = URLDecoder.decode(value, charset); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return result; + } + + /** + * 使用指定的字符集编码请求参数值。 + * + * @param value 参数值 + * @param charset 字符集 + * @return 编码后的参数值 + */ + public static String encode(String value, String charset) { + String result = null; + if (!isEmpty(value)) { + try { + result = URLEncoder.encode(value, charset); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return result; + } + + private static Map getParamsFromUrl(String url) { + Map map = null; + if (url != null && url.indexOf('?') != -1) { + map = splitUrlQuery(url.substring(url.indexOf('?') + 1)); + } + if (map == null) { + map = new HashMap(); + } + return map; + } + + /** + * 从URL中提取所有的参数。 + * + * @param query URL地址 + * @return 参数映射 + */ + public static Map splitUrlQuery(String query) { + Map result = new HashMap(); + + String[] pairs = query.split("&"); + if (pairs != null && pairs.length > 0) { + for (String pair : pairs) { + String[] param = pair.split("=", 2); + if (param != null && param.length == 2) { + result.put(param[0], param[1]); + } + } + } + + return result; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/ent/Mapping.java b/src/main/java/xyz/wbsite/module/conf/ent/Mapping.java new file mode 100644 index 0000000..6621b38 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/ent/Mapping.java @@ -0,0 +1,86 @@ +package xyz.wbsite.module.conf.ent; + +import xyz.wbsite.frame.base.BaseEntity; + +/** + * MAPPING - 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class Mapping extends BaseEntity { + + /** + * NAME - 名称 + */ + private String name; + /** + * PORT - 端口 + */ + private String port; + /** + * PATH - 路径 + */ + private String path; + /** + * TYPE - 类型 + */ + private String type; + /** + * LOCATION - 代理地址 + */ + private String location; + /** + * NOTE - 备注 + */ + private String note; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPort() { + return this.port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getLocation() { + return this.location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getNote() { + return this.note; + } + + public void setNote(String note) { + this.note = note; + } +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/module/conf/mgr/MappingManager.java b/src/main/java/xyz/wbsite/module/conf/mgr/MappingManager.java new file mode 100644 index 0000000..dd5d124 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/mgr/MappingManager.java @@ -0,0 +1,68 @@ +package xyz.wbsite.module.conf.mgr; + +import xyz.wbsite.module.conf.req.MappingCreateRequest; +import xyz.wbsite.module.conf.req.MappingDeleteRequest; +import xyz.wbsite.module.conf.req.MappingFindRequest; +import xyz.wbsite.module.conf.req.MappingGetRequest; +import xyz.wbsite.module.conf.req.MappingUpdateRequest; +import xyz.wbsite.module.conf.rsp.MappingCreateResponse; +import xyz.wbsite.module.conf.rsp.MappingDeleteResponse; +import xyz.wbsite.module.conf.rsp.MappingFindResponse; +import xyz.wbsite.module.conf.rsp.MappingGetResponse; +import xyz.wbsite.module.conf.rsp.MappingUpdateResponse; +import xyz.wbsite.frame.auth.Token; + +/** + * 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public interface MappingManager { + + /** + * 插入 + * + * @param request 请求对象 + * @param token 令牌 + * @return + */ + MappingCreateResponse create(MappingCreateRequest request, Token token); + + /** + * 逻辑删除 + * + * @param request 请求对象 + * @param token 令牌 + * @return + */ + MappingDeleteResponse delete(MappingDeleteRequest request, Token token); + + /** + * 更新 + * + * @param request 请求对象 + * @param token 令牌 + * @return + */ + MappingUpdateResponse update(MappingUpdateRequest request, Token token); + + /** + * 查询 + * + * @param request 请求对象 + * @param token 令牌 + * @return + */ + MappingFindResponse find(MappingFindRequest request, Token token); + + /** + * 获得对象 + * + * @param request 请求对象 + * @param token 令牌 + * @return + */ + MappingGetResponse get(MappingGetRequest request, Token token); +} diff --git a/src/main/java/xyz/wbsite/module/conf/mgr/MappingManagerImpl.java b/src/main/java/xyz/wbsite/module/conf/mgr/MappingManagerImpl.java new file mode 100644 index 0000000..cf6b2aa --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/mgr/MappingManagerImpl.java @@ -0,0 +1,177 @@ +package xyz.wbsite.module.conf.mgr; + +import xyz.wbsite.frame.utils.IDgenerator; +import xyz.wbsite.frame.utils.Message; +import xyz.wbsite.frame.base.ErrorType; +import xyz.wbsite.frame.auth.Token; +import xyz.wbsite.frame.utils.MapperUtil; +import xyz.wbsite.frame.utils.ValidationUtil; +import xyz.wbsite.module.conf.ent.Mapping; +import xyz.wbsite.module.conf.mpr.MappingMapper; +import xyz.wbsite.module.conf.req.MappingCreateRequest; +import xyz.wbsite.module.conf.req.MappingDeleteRequest; +import xyz.wbsite.module.conf.req.MappingFindRequest; +import xyz.wbsite.module.conf.req.MappingGetRequest; +import xyz.wbsite.module.conf.req.MappingUpdateRequest; +import xyz.wbsite.module.conf.rsp.MappingCreateResponse; +import xyz.wbsite.module.conf.rsp.MappingDeleteResponse; +import xyz.wbsite.module.conf.rsp.MappingFindResponse; +import xyz.wbsite.module.conf.rsp.MappingGetResponse; +import xyz.wbsite.module.conf.rsp.MappingUpdateResponse; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.github.pagehelper.util.StringUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * MAPPING - 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +@Transactional +@Service +public class MappingManagerImpl implements MappingManager { + + @Autowired + private MappingMapper mappingMapper; + + /** + * 插入 + * + * @param request 请求对象 + * @param token 令牌 + * @return 响应 + */ + public MappingCreateResponse create(MappingCreateRequest request, Token token) { + MappingCreateResponse response = new MappingCreateResponse(); + + ValidationUtil.validate(request, response); + if (response.hasError()) { + return response; + } + + long id = IDgenerator.nextId(); + Mapping entity = MapperUtil.map(request, Mapping.class); + entity.setId(id); + + long result = mappingMapper.insert(entity, token); + if (1L != result) { + response.addError(ErrorType.BUSINESS_ERROR, Message.CREATE_FAILURE); + return response; + } + response.setId(id); + + return response; + } + + /** + * 逻辑删除 + * + * @param request 请求对象 + * @param token 令牌 + * @return 响应 + */ + public MappingDeleteResponse delete(MappingDeleteRequest request, Token token) { + MappingDeleteResponse response = new MappingDeleteResponse(); + + ValidationUtil.validate(request, response); + if (response.hasError()) { + return response; + } + + long result = mappingMapper.delete(request, token); + if (1L != result) { + response.addError(ErrorType.BUSINESS_ERROR, Message.DELETE_FAILURE); + return response; + } + response.setResult(result); + + return response; + } + + /** + * 更新 + * + * @param request 请求对象 + * @param token 令牌 + * @return 响应 + */ + public MappingUpdateResponse update(MappingUpdateRequest request, Token token) { + MappingUpdateResponse response = new MappingUpdateResponse(); + + ValidationUtil.validate(request, response); + if (response.hasError()) { + return response; + } + + long result = mappingMapper.update(request, token); + if (1L != result) { + response.addError(ErrorType.BUSINESS_ERROR, Message.UPDATE_FAILURE); + return response; + } + response.setResult(result); + + return response; + } + + /** + * 查询 + * + * @param request 请求对象 + * @param token 令牌 + * @return 响应 + */ + @Transactional(readOnly = true) + public MappingFindResponse find(MappingFindRequest request, Token token) { + MappingFindResponse response = new MappingFindResponse(); + + ValidationUtil.validate(request, response); + if (response.hasError()) { + return response; + } + + if (request.getPageSize() != 0) { + PageHelper.startPage(request.getPageNumber(), request.getPageSize()); + } + if (StringUtil.isNotEmpty(request.getSortKey())) { + PageHelper.orderBy(request.getSortKey() + " " + request.getSortType()); + } + PageInfo pageInfo = new PageInfo<>(mappingMapper.find(request, token)); + + response.setResult(pageInfo.getList()); + response.setTotalCount(pageInfo.getTotal()); + + return response; + } + + /** + * 获得对象 + * + * @param request 请求对象 + * @param token 令牌 + * @return 响应 + */ + @Transactional(readOnly = true) + public MappingGetResponse get(MappingGetRequest request, Token token) { + MappingGetResponse response = new MappingGetResponse(); + + ValidationUtil.validate(request, response); + if (response.hasError()) { + return response; + } + + Mapping po = mappingMapper.get(request, token); + + if (po != null) { + response.setMapping(po); + } else { + response.addError(ErrorType.BUSINESS_ERROR, Message.GET_FAILURE); + } + + return response; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/mpr/MappingMapper.java b/src/main/java/xyz/wbsite/module/conf/mpr/MappingMapper.java new file mode 100644 index 0000000..7d76000 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/mpr/MappingMapper.java @@ -0,0 +1,78 @@ +package xyz.wbsite.module.conf.mpr; + +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +import xyz.wbsite.module.conf.ent.Mapping; +import xyz.wbsite.module.conf.req.MappingCreateRequest; +import xyz.wbsite.module.conf.req.MappingDeleteRequest; +import xyz.wbsite.module.conf.req.MappingFindRequest; +import xyz.wbsite.module.conf.req.MappingGetRequest; +import xyz.wbsite.module.conf.req.MappingUpdateRequest; +import xyz.wbsite.frame.auth.Token; + +/** + * MAPPING - 映射 + * + * @author wangbing + * @date 2020-03-18 + */ +@Mapper +public interface MappingMapper { + + /** + * 插入 + * + * @param request 请求对象 + * @param token 令牌 + * @return 返回数量 + */ + long insert(@Param("request") Mapping request, @Param("token") Token token); + + /** + * 批量插入 + * + * @param request 请求对象 + * @param token 令牌 + * @return 返回数量 + */ + long insertBatch(@Param("list") List request, @Param("token") Token token); + + /** + * 逻辑删除 + * + * @param request 请求对象 + * @param token 令牌 + * @return 返回数量 + */ + long delete(@Param("request") MappingDeleteRequest request, @Param("token") Token token); + + /** + * 更新 + * + * @param request 请求对象 + * @param token 令牌 + * @return 返回数量 + */ + long update(@Param("request") MappingUpdateRequest request, @Param("token") Token token); + + /** + * 查询 + * + * @param request 请求对象 + * @param token 令牌 + * @return 返回对象 + */ + List find(@Param("request") MappingFindRequest request, @Param("token") Token token); + + /** + * 获得对象 + * + * @param request 请求对象 + * @param token 令牌 + * @return 返回对象 + */ + Mapping get(@Param("request") MappingGetRequest request, @Param("token") Token token); +} diff --git a/src/main/java/xyz/wbsite/module/conf/mpr/MappingMapper.xml b/src/main/java/xyz/wbsite/module/conf/mpr/MappingMapper.xml new file mode 100644 index 0000000..bc02744 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/mpr/MappingMapper.xml @@ -0,0 +1,151 @@ + + + + + "CONFMAPPING" + + + "ID","NAME","PORT","PATH","TYPE","LOCATION","NOTE","ROW_VERSION","IS_DELETED","CREATE_BY","CREATE_TIME","LAST_UPDATE_BY","LAST_UPDATE_TIME" + + + + + + + + + + + + + + + + + + + + + + + + INSERT INTO + + ( + + ) + VALUES + ( + #{request.id}, + #{request.name,jdbcType=VARCHAR}, + #{request.port,jdbcType=VARCHAR}, + #{request.path,jdbcType=VARCHAR}, + #{request.type,jdbcType=VARCHAR}, + #{request.location,jdbcType=VARCHAR}, + #{request.note,jdbcType=VARCHAR}, + 0, + 0, + #{token.userId,jdbcType=NUMERIC}, + datetime('now','localtime'), + NULL, + NULL + ) + + + + INSERT INTO + + ( + + ) + VALUES + + ( + #{item.id}, + #{item.name,jdbcType=VARCHAR}, + #{item.port,jdbcType=VARCHAR}, + #{item.path,jdbcType=VARCHAR}, + #{item.type,jdbcType=VARCHAR}, + #{item.location,jdbcType=VARCHAR}, + #{item.note,jdbcType=VARCHAR}, + 0, + 0, + #{token.userId,jdbcType=NUMERIC}, + datetime('now','localtime'), + NULL, + NULL + ) + + + + + UPDATE + + SET "IS_DELETED" = 1 + WHERE "IS_DELETED" = 0 + AND "ID" = #{request.id} + + + + UPDATE + + SET + NAME = #{request.name,jdbcType=VARCHAR}, + PORT = #{request.port,jdbcType=VARCHAR}, + PATH = #{request.path,jdbcType=VARCHAR}, + TYPE = #{request.type,jdbcType=VARCHAR}, + LOCATION = #{request.location,jdbcType=VARCHAR}, + NOTE = #{request.note,jdbcType=VARCHAR}, + "ROW_VERSION" = "ROW_VERSION" + 1, + "LAST_UPDATE_BY" = #{token.userId}, + "LAST_UPDATE_TIME" = datetime('now','localtime') + WHERE + "IS_DELETED" = 0 + AND "ID" = #{request.id} + AND "ROW_VERSION" = #{request.rowVersion} + + + + \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/module/conf/req/MappingCreateRequest.java b/src/main/java/xyz/wbsite/module/conf/req/MappingCreateRequest.java new file mode 100644 index 0000000..2ff9b82 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/req/MappingCreateRequest.java @@ -0,0 +1,105 @@ +package xyz.wbsite.module.conf.req; + +import xyz.wbsite.frame.base.BaseRequest; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.NotBlank; +import org.hibernate.validator.constraints.Length; + +/** + * MappingCreateRequest - 映射新增 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingCreateRequest extends BaseRequest { + + /** + * 名称 + */ + @NotBlank(message = "[name]名称不能为空") + @Length(min = 0, max = 50, message = "[name]名称长度不合法(0-50)") + private String name; + + /** + * 端口 + */ + @NotBlank(message = "[port]端口不能为空") + @Length(min = 0, max = 50, message = "[port]端口长度不合法(0-50)") + private String port; + + /** + * 路径 + */ + @NotBlank(message = "[path]路径不能为空") + @Length(min = 0, max = 50, message = "[path]路径长度不合法(0-50)") + private String path; + + /** + * 类型 + */ + @NotBlank(message = "[type]类型不能为空") + @Length(min = 0, max = 50, message = "[type]类型长度不合法(0-50)") + private String type; + + /** + * 代理地址 + */ + @NotBlank(message = "[location]代理地址不能为空") + @Length(min = 0, max = 50, message = "[location]代理地址长度不合法(0-50)") + private String location; + + /** + * 备注 + */ + @Length(min = 0, max = 50, message = "[note]备注长度不合法(0-50)") + private String note; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPort() { + return this.port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getLocation() { + return this.location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getNote() { + return this.note; + } + + public void setNote(String note) { + this.note = note; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/req/MappingDeleteRequest.java b/src/main/java/xyz/wbsite/module/conf/req/MappingDeleteRequest.java new file mode 100644 index 0000000..831d4be --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/req/MappingDeleteRequest.java @@ -0,0 +1,28 @@ +package xyz.wbsite.module.conf.req; + +import xyz.wbsite.frame.base.BaseUpdateRequest; +import javax.validation.constraints.NotNull; + +/** + * MappingDeleteRequest - 映射删除 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingDeleteRequest extends BaseUpdateRequest { + + /** + * 主键 + */ + @NotNull(message = "[id]主键不能为空") + private long id; + + public long getId() { + return this.id; + } + + public void setId(long id) { + this.id = id; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/req/MappingFindRequest.java b/src/main/java/xyz/wbsite/module/conf/req/MappingFindRequest.java new file mode 100644 index 0000000..f51f7d1 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/req/MappingFindRequest.java @@ -0,0 +1,78 @@ +package xyz.wbsite.module.conf.req; + +import xyz.wbsite.frame.base.BaseFindRequest; + +/** + * MappingRequest - 映射查询 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingFindRequest extends BaseFindRequest { + + /** + * 名称 + */ + private String name; + + /** + * 端口 + */ + private String port; + + /** + * 路径 + */ + private String path; + + /** + * 类型 + */ + private String type; + + /** + * 代理地址 + */ + private String location; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPort() { + return this.port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getLocation() { + return this.location; + } + + public void setLocation(String location) { + this.location = location; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/req/MappingGetRequest.java b/src/main/java/xyz/wbsite/module/conf/req/MappingGetRequest.java new file mode 100644 index 0000000..c217d84 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/req/MappingGetRequest.java @@ -0,0 +1,28 @@ +package xyz.wbsite.module.conf.req; + +import xyz.wbsite.frame.base.BaseRequest; +import javax.validation.constraints.NotNull; + +/** + * MappingGetRequest - 映射获取 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingGetRequest extends BaseRequest { + + /** + * 主键 + */ + @NotNull(message = "[id]主键不能为空") + private long id; + + public long getId() { + return this.id; + } + + public void setId(long id) { + this.id = id; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/req/MappingUpdateRequest.java b/src/main/java/xyz/wbsite/module/conf/req/MappingUpdateRequest.java new file mode 100644 index 0000000..dbcffb0 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/req/MappingUpdateRequest.java @@ -0,0 +1,120 @@ +package xyz.wbsite.module.conf.req; + +import xyz.wbsite.frame.base.BaseUpdateRequest; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import org.hibernate.validator.constraints.Length; +import javax.validation.constraints.NotEmpty; + +/** + * MappingUpdateRequest - 映射更新 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingUpdateRequest extends BaseUpdateRequest { + + /** + * 主键 + */ + @NotNull(message = "[id]主键不能为NULL") + private Long id; + + /** + * 名称 + */ + @NotBlank(message = "[name]名称不能为空") + @Length(min = 0, max = 50, message = "[name]名称长度不合法(0-50)") + private String name; + + /** + * 端口 + */ + @NotBlank(message = "[port]端口不能为空") + @Length(min = 0, max = 50, message = "[port]端口长度不合法(0-50)") + private String port; + + /** + * 路径 + */ + @NotBlank(message = "[path]路径不能为空") + @Length(min = 0, max = 50, message = "[path]路径长度不合法(0-50)") + private String path; + + /** + * 类型 + */ + @NotBlank(message = "[type]类型不能为空") + @Length(min = 0, max = 50, message = "[type]类型长度不合法(0-50)") + private String type; + + /** + * 代理地址 + */ + @NotBlank(message = "[location]代理地址不能为空") + @Length(min = 0, max = 50, message = "[location]代理地址长度不合法(0-50)") + private String location; + + /** + * 备注 + */ + @Length(min = 0, max = 50, message = "[note]备注长度不合法(0-50)") + private String note; + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPort() { + return this.port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getLocation() { + return this.location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getNote() { + return this.note; + } + + public void setNote(String note) { + this.note = note; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/rsp/MappingCreateResponse.java b/src/main/java/xyz/wbsite/module/conf/rsp/MappingCreateResponse.java new file mode 100644 index 0000000..65f71bf --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/rsp/MappingCreateResponse.java @@ -0,0 +1,26 @@ +package xyz.wbsite.module.conf.rsp; + +import xyz.wbsite.frame.base.BaseResponse; + +/** + * MappingCreateResponse - 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingCreateResponse extends BaseResponse { + + /** + * 主键 + */ + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/rsp/MappingDeleteResponse.java b/src/main/java/xyz/wbsite/module/conf/rsp/MappingDeleteResponse.java new file mode 100644 index 0000000..c05b13b --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/rsp/MappingDeleteResponse.java @@ -0,0 +1,26 @@ +package xyz.wbsite.module.conf.rsp; + +import xyz.wbsite.frame.base.BaseResponse; + +/** + * MappingDeleteResponse - 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingDeleteResponse extends BaseResponse { + + /** + * 删除数目 + */ + private Long result; + + public Long getResult() { + return this.result; + } + + public void setResult(Long result) { + this.result = result; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/rsp/MappingFindResponse.java b/src/main/java/xyz/wbsite/module/conf/rsp/MappingFindResponse.java new file mode 100644 index 0000000..a937a62 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/rsp/MappingFindResponse.java @@ -0,0 +1,14 @@ +package xyz.wbsite.module.conf.rsp; + +import xyz.wbsite.frame.base.BaseFindResponse; +import xyz.wbsite.module.conf.ent.Mapping; + +/** + * MappingFindResponse - 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingFindResponse extends BaseFindResponse { +} \ No newline at end of file diff --git a/src/main/java/xyz/wbsite/module/conf/rsp/MappingGetResponse.java b/src/main/java/xyz/wbsite/module/conf/rsp/MappingGetResponse.java new file mode 100644 index 0000000..576b3c9 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/rsp/MappingGetResponse.java @@ -0,0 +1,27 @@ +package xyz.wbsite.module.conf.rsp; + +import xyz.wbsite.module.conf.ent.Mapping; +import xyz.wbsite.frame.base.BaseResponse; + +/** + * MappingGetResponse - 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingGetResponse extends BaseResponse { + + /** + * 映射 + */ + private Mapping mapping; + + public Mapping getMapping() { + return this.mapping; + } + + public void setMapping(Mapping mapping) { + this.mapping = mapping; + } +} diff --git a/src/main/java/xyz/wbsite/module/conf/rsp/MappingUpdateResponse.java b/src/main/java/xyz/wbsite/module/conf/rsp/MappingUpdateResponse.java new file mode 100644 index 0000000..00ecbcf --- /dev/null +++ b/src/main/java/xyz/wbsite/module/conf/rsp/MappingUpdateResponse.java @@ -0,0 +1,26 @@ +package xyz.wbsite.module.conf.rsp; + +import xyz.wbsite.frame.base.BaseResponse; + +/** + * MappingUpdateResponse - 映射 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +public class MappingUpdateResponse extends BaseResponse { + + /** + * 更新数目 + */ + private Long result; + + public Long getResult() { + return this.result; + } + + public void setResult(Long result) { + this.result = result; + } +} diff --git a/src/main/java/xyz/wbsite/module/system/req/UserLoginRequest.java b/src/main/java/xyz/wbsite/module/system/req/UserLoginRequest.java new file mode 100644 index 0000000..728f851 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/system/req/UserLoginRequest.java @@ -0,0 +1,37 @@ +package xyz.wbsite.module.system.req; + +import xyz.wbsite.frame.base.BaseRequest; + +import javax.validation.constraints.NotBlank; + +/** + * UserLoginRequest - 登录 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class UserLoginRequest extends BaseRequest { + + @NotBlank(message = "[username]用户名不能为空") + private String username; + + @NotBlank(message = "[password]用户密码不能为空") + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/src/main/java/xyz/wbsite/module/system/req/UserLogoutRequest.java b/src/main/java/xyz/wbsite/module/system/req/UserLogoutRequest.java new file mode 100644 index 0000000..a3e9853 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/system/req/UserLogoutRequest.java @@ -0,0 +1,14 @@ +package xyz.wbsite.module.system.req; + +import xyz.wbsite.frame.base.BaseRequest; + +/** + * UserLogoutRequest - 用户登出 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class UserLogoutRequest extends BaseRequest { + +} diff --git a/src/main/java/xyz/wbsite/module/system/rsp/UserLoginResponse.java b/src/main/java/xyz/wbsite/module/system/rsp/UserLoginResponse.java new file mode 100644 index 0000000..4445950 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/system/rsp/UserLoginResponse.java @@ -0,0 +1,26 @@ +package xyz.wbsite.module.system.rsp; + +import xyz.wbsite.frame.base.BaseResponse; + +/** + * UserLoginResponse - 登录响应 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class UserLoginResponse extends BaseResponse { + + /** + * token + */ + private String token; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } +} diff --git a/src/main/java/xyz/wbsite/module/system/rsp/UserLogoutResponse.java b/src/main/java/xyz/wbsite/module/system/rsp/UserLogoutResponse.java new file mode 100644 index 0000000..16e99a1 --- /dev/null +++ b/src/main/java/xyz/wbsite/module/system/rsp/UserLogoutResponse.java @@ -0,0 +1,14 @@ +package xyz.wbsite.module.system.rsp; + +import xyz.wbsite.frame.base.BaseResponse; + +/** + * UserLoginResponse - 用户登出 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +public class UserLogoutResponse extends BaseResponse { + +} diff --git a/src/main/java/xyz/wbsite/task/TokenTask.java b/src/main/java/xyz/wbsite/task/TokenTask.java new file mode 100644 index 0000000..7dbb187 --- /dev/null +++ b/src/main/java/xyz/wbsite/task/TokenTask.java @@ -0,0 +1,40 @@ +package xyz.wbsite.task; + +import xyz.wbsite.frame.schedule.RunDelayRepeatTask; + +import java.time.Duration; + +public class TokenTask extends RunDelayRepeatTask { + + @Override + public String taskId() { + return "task1"; + } + + @Override + public Duration interval() { + return Duration.ofMinutes(1); + } + + @Override + public void run() { +// TokensManager tokensManager = LocalData.getBean(TokensManager.class); +// if (tokensManager == null) { +// return; +// } +// +// TokensFindRequest tokensFindRequest = new TokensFindRequest(); +// tokensFindRequest.setPageSize(0); +// tokensFindRequest.setValid(true); +// TokensFindResponse tokensFindResponse = tokensManager.find(tokensFindRequest, LocalData.getSysToken()); +// +// Date current = new Date(); +// for (Tokens tokens : tokensFindResponse.getResult()) { +// if (tokens.getValidTime().before(current)) { +// TokensLogoutRequest tokensLogoutRequest = new TokensLogoutRequest(); +// tokensLogoutRequest.setToken(tokens.getToken()); +// TokensLogoutResponse tokensLogoutResponse = tokensManager.logout(tokensLogoutRequest, LocalData.getSysToken()); +// } +// } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..ad43af9 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,71 @@ +# 开发环境 +server.port=8888 +server.servlet.context-path= +spring.mvc.static-path-pattern=/static/** +spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:/file-upload +spring.application.name=nginx-admin +spring.main.banner-mode=OFF +spring.devtools.restart.enabled=true +# 编码配置 +spring.http.encoding.force=true +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +server.tomcat.uri-encoding=UTF-8 +# 日志配置 +logging.path=D:// +logging.levels=INFO +# SQLite spring.datasource.url=jdbc:sqlite::resource:example.db +spring.datasource.driver-class-name=org.sqlite.JDBC +spring.datasource.url=jdbc:sqlite:nginx-admin.db?date_string_format=yyyy-MM-dd HH:mm:ss +spring.datasource.username=test +spring.datasource.password=123456 +# mybatis +mybatis.mapper-locations=classpath:**/mpr/*.xml +mybatis.configuration.map-underscore-to-camel-case=true +# pagehelper +pagehelper.autoRuntimeDialect=true +pagehelper.reasonable=false +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql +# jackson 相关配置 +spring.jackson.date-format=yyyy-MM-dd HH:mm:ss +spring.jackson.time-zone=GMT+8 +spring.jackson.default-property-inclusion=non_null +spring.jackson.mapper.sort-properties-alphabetically=true +spring.jackson.deserialization.fail-on-unknown-properties=false +# freemarker +spring.freemarker.enabled=true +spring.freemarker.allow-request-override=false +spring.freemarker.cache=true +spring.freemarker.check-template-location=true +spring.freemarker.charset=UTF-8 +spring.freemarker.content-type=text/html +spring.freemarker.expose-request-attributes=false +spring.freemarker.expose-session-attributes=false +spring.freemarker.expose-spring-macro-helpers=false +spring.freemarker.settings.template_update_delay=1 +spring.freemarker.settings.locale=zh_CN +spring.freemarker.settings.datetime_format=yyyy-MM-dd HH:mm:ss +spring.freemarker.settings.date_format=yyyy-MM-dd +spring.freemarker.settings.number_format=#.## +spring.freemarker.settings.classic_compatible=true +spring.freemarker.settings.whitespace_stripping=true +spring.freemarker.settings.url_escaping_charset=utf-8 +# 文件上传配置 +spring.servlet.multipart.resolveLazily=false +spring.servlet.multipart.max-file-size=100MB +spring.servlet.multipart.max-request-size=100MB +server.tomcat.max-http-post-size=-1 + +# 自定义配置 +# 根路径默认页,'/'跳转至该页 +web.home.page=/index.htm +# 登录页 +web.login.page=/login.htm +# 拦截验证 +web.url.auth.included=/,/**/*.htm,/ajax/**,/api/** +# 直接放行 +web.url.auth.excluded=/login.htm,/ajax/system/User/login,/ajax/system/User/logout +# 超级管理员 +web.url.auth.admin=admin +web.url.auth.pwd=17fac3376f76d65943d1d26d1f7cb1e5 \ No newline at end of file diff --git a/src/main/resources/dbtool/conf_table/SQLite_ALL_TABLE.sql b/src/main/resources/dbtool/conf_table/SQLite_ALL_TABLE.sql new file mode 100644 index 0000000..6b33372 --- /dev/null +++ b/src/main/resources/dbtool/conf_table/SQLite_ALL_TABLE.sql @@ -0,0 +1,21 @@ +-- ---------------------------- +-- Table structure for MAPPING - 映射 +-- Target : SQLite +-- Author : wangbing +-- Date: : 2020-03-18 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS CONFMAPPING ( + "ID" BIGINT PRIMARY KEY NOT NULL, + "NAME" VARCHAR(50) NOT NULL, + "PORT" VARCHAR(50) NOT NULL, + "PATH" VARCHAR(50) NOT NULL, + "TYPE" VARCHAR(50) NOT NULL, + "LOCATION" VARCHAR(50) NOT NULL, + "NOTE" VARCHAR(50), + "ROW_VERSION" BIGINT NOT NULL, + "IS_DELETED" BOOLEAN NOT NULL, + "CREATE_BY" BIGINT NOT NULL, + "CREATE_TIME" DATETIME NOT NULL, + "LAST_UPDATE_BY" BIGINT, + "LAST_UPDATE_TIME" DATETIME +); diff --git a/src/main/resources/dbtool/conf_table/SQLite_MAPPING.sql b/src/main/resources/dbtool/conf_table/SQLite_MAPPING.sql new file mode 100644 index 0000000..6b33372 --- /dev/null +++ b/src/main/resources/dbtool/conf_table/SQLite_MAPPING.sql @@ -0,0 +1,21 @@ +-- ---------------------------- +-- Table structure for MAPPING - 映射 +-- Target : SQLite +-- Author : wangbing +-- Date: : 2020-03-18 +-- ---------------------------- +CREATE TABLE IF NOT EXISTS CONFMAPPING ( + "ID" BIGINT PRIMARY KEY NOT NULL, + "NAME" VARCHAR(50) NOT NULL, + "PORT" VARCHAR(50) NOT NULL, + "PATH" VARCHAR(50) NOT NULL, + "TYPE" VARCHAR(50) NOT NULL, + "LOCATION" VARCHAR(50) NOT NULL, + "NOTE" VARCHAR(50), + "ROW_VERSION" BIGINT NOT NULL, + "IS_DELETED" BOOLEAN NOT NULL, + "CREATE_BY" BIGINT NOT NULL, + "CREATE_TIME" DATETIME NOT NULL, + "LAST_UPDATE_BY" BIGINT, + "LAST_UPDATE_TIME" DATETIME +); diff --git a/src/main/resources/dbtool/nginx-admin.xml b/src/main/resources/dbtool/nginx-admin.xml new file mode 100644 index 0000000..150f679 --- /dev/null +++ b/src/main/resources/dbtool/nginx-admin.xml @@ -0,0 +1,35 @@ + + +nginx-admin +xyz.wbsite +wangbing +false +false + + +配置 +CONF +conf +true + + + + + + + + + + + + + + + + + +
+
+
+
+
diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..39f7b30 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + ${root}log/all/all.log + + ${root}log/all/all-%d{yyyy-MM-dd}-%i.log + 30 + + 10MB + + + + [%-4level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{36}-%method] %msg%n + + + INFO + + + + + + ${root}log/error/error.log + + ${root}log/error/error-%d{yyyy-MM-dd}-%i.log + 30 + + 10MB + + + + [%-4level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{36}-%method] %msg%n + + + ERROR + ACCEPT + DENY + + + + + + ${root}log/warn/warn.log + + ${root}log/warn/warn-%d{yyyy-MM-dd}-%i.log + 30 + + 10MB + + + + [%-4level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{36}-%method] %msg%n + + + WARN + ACCEPT + DENY + + + + + + ${root}log/info/info.log + + ${root}log/info/info-%d{yyyy-MM-dd}-%i.log + 30 + + 10MB + + + + [%-4level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%logger{36}-%method] %msg%n + + + INFO + ACCEPT + DENY + + + + + + debug级别及以上 + + DEBUG + + + %highlight(%d{yyyy-MM-dd HH:mm:ss.SSS} [%-4level] [%thread] [%logger{36}-%method] %ex %msg%n) + + + + + + + + + + + + + + + diff --git a/src/main/resources/nginx-admin.db b/src/main/resources/nginx-admin.db new file mode 100644 index 0000000..be9b6fb Binary files /dev/null and b/src/main/resources/nginx-admin.db differ diff --git a/src/main/resources/start-dev.bat b/src/main/resources/start-dev.bat new file mode 100644 index 0000000..f0acb47 --- /dev/null +++ b/src/main/resources/start-dev.bat @@ -0,0 +1,3 @@ +@echo off +cd %cd% +start java -Dfile.encoding=UTF-8 -jar nginx-admin-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev \ No newline at end of file diff --git a/src/main/resources/start-prod.bat b/src/main/resources/start-prod.bat new file mode 100644 index 0000000..f3b3495 --- /dev/null +++ b/src/main/resources/start-prod.bat @@ -0,0 +1,3 @@ +@echo off +cd %cd% +start java -Dfile.encoding=UTF-8 -jar nginx-admin-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod \ No newline at end of file diff --git a/src/main/resources/static/css/base.css b/src/main/resources/static/css/base.css new file mode 100644 index 0000000..1def058 --- /dev/null +++ b/src/main/resources/static/css/base.css @@ -0,0 +1,711 @@ +* { + -webkit-box-sizing: border-box; + box-sizing: border-box; + -webkit-tap-highlight-color: transparent; + margin: 0; + padding: 0; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-thumb { + background-color: #797979; + border-radius: 3px; +} + +::-webkit-scrollbar-track { + background-color: #f9f9f9; +} + +html { + line-height: 1.15; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0; + font-size: 12px; + line-height: 1.5; + color: #515a6e; + background-color: #fff; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale +} + +html, body { + width: 100%; + height: 100%; +} + +article, aside, footer, header, nav, section { + display: block +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +pre { + font-family: monospace, monospace; + font-size: 1em +} + +button, input, optgroup, select, textarea { + font-family: sans-serif; + font-size: 100%; + line-height: 1.15; + margin: 0 +} + +button, select { + text-transform: none +} + +[type=reset], [type=submit], button, html [type=button] { + -webkit-appearance: button +} + +[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner { + border-style: none; + padding: 0 +} + +[type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring, button:-moz-focusring { + outline: 1px dotted ButtonText +} + +fieldset { + border: 1px solid silver; + margin: 0 2px; + padding: .35em .625em .75em +} + +legend { + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal +} + +progress { + display: inline-block; + vertical-align: baseline +} + +textarea { + overflow: auto; + resize: vertical +} + +[type=checkbox], [type=radio] { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0 +} + +[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button { + height: auto +} + +[type=search] { + -webkit-appearance: textfield; + outline-offset: -2px +} + +[type=search]::-webkit-search-cancel-button, [type=search]::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit +} + +details, menu { + display: block +} + +summary { + display: list-item +} + +canvas { + display: inline-block +} + +template { + display: none +} + +[hidden] { + display: none +} + +:after, :before { + -webkit-box-sizing: border-box; + box-sizing: border-box +} + +a { + text-decoration: none; + outline: 0; + cursor: pointer; + -webkit-transition: color .2s ease; + transition: color .2s ease; + -webkit-text-decoration-skip: objects; + color: #757575; +} + +a:active, a:hover { + outline-width: 0 +} + +a:hover { + opacity: 0.7; +} + +a:active { + opacity: 0.9; +} + +a:active, a:hover { + outline: 0; + text-decoration: none +} + +a[disabled] { + color: #ccc; + cursor: not-allowed; + pointer-events: none +} + +code, kbd, pre, samp { + font-family: Consolas, Menlo, Courier, monospace +} + +a { + text-decoration: none; +} + +/*主标题*/ +h1 { + font-size: 20px; + padding: 10px 0px; + margin: 20px 0px 10px 0px; +} + +/*标题*/ +h2 { + font-size: 18px; + padding: 9px 0px; + margin: 18px 0px 8px 0px; +} + +/*小标题*/ +h3 { + font-size: 16px; + padding: 8px 0px; + margin: 16px 0px 6px 0px; +} + +/*次要标题*/ +h4 { + font-size: 14px; + padding: 4px 0px; + margin: 4px 0px 4px 0px; +} + +p { + font-size: 14px; + padding: 7px 0px; + color: #545C64;; +} + +p.text-indent { + text-indent: 2em +} + +/*正文*/ +.text-regular { + font-size: 14px; +} + +/*正文(小)*/ +.text-small { + font-size: 13px +} + +/*辅助文字*/ +.text-smaller { + font-size: 12px +} + +/*中文字体*/ +.typo-PingFang { + font-family: PingFang SC +} + +.typo-Hiragino { + font-family: Hiragino Sans GB +} + +.typo-Microsoft { + font-family: Microsoft YaHei +} + +/*英文/数字字体*/ +.typo-Helvetica-Neue { + font-family: Helvetica Neue +} + +.typo-Helvetica { + font-family: Helvetica +} + +.typo-Arial { + font-family: Arial +} + +.bg-blue { + background-color: #409eff +} + +.bg-success { + background-color: #67c23a +} + +.bg-warning { + background-color: #e6a23c +} + +.bg-danger { + background-color: #f56c6c +} + +.bg-info { + background-color: #909399 +} + +.text-color-primary { + color: #303133 +} + +.text-color-regular { + color: #606266 +} + +.text-color-secondary { + color: #909399 +} + +.text-color-placeholder { + color: #c0c4cc +} + +.bg-border-base { + background-color: #dcdfe6 +} + +.bg-border-light { + background-color: #e4e7ed +} + +.bg-border-lighter { + background-color: #ebeef5 +} + +.bg-border-extra-light { + background-color: #f2f6fc +} + +[class*="bg-border-"] { + color: #303133 +} + +code { + background-color: #f9fafc; + padding: 0 4px; + border: 1px solid #eaeefb; + border-radius: 4px; + display: inline-block; +} + +.remarks { + font-size: 12px; + color: #909399 +} + +.table { + display: table; +} + +.table-cell { + display: table-cell; +} + +.table-cell.center { + text-align: center; + vertical-align: middle; +} + +.table-cell.v-center { + vertical-align: middle; +} + +.table-cell.h-center { + text-align: center; +} + +.table-cell.right { + text-align: right; +} + +.table-cell.left { + text-align: left; +} + +.table-cell.top { + vertical-align: top; +} + +.table-cell.bottom { + vertical-align: bottom; +} + +.m-t-10 { + margin-top: 10px; +} + +.m-t-20 { + margin-top: 20px; +} + +.m-t-30 { + margin-top: 30px; +} + +.m-t-40 { + margin-top: 40px; +} + +.m-t-50 { + margin-top: 50px; +} + +.center { + text-align: center; +} + +.full { + width: 100%; + height: 100%; +} + +.nowrap { + white-space: nowrap; + overflow: hidden; + word-break: keep-all; +} + +[v-cloak] { + display: none; +} + +/* 可以设置不同的进入和离开动画 */ +/* 设置持续时间和动画函数 */ +.left-in-right-out-enter-active, .left-in-right-out-leave-active { + transition: all 0.3s linear; + position: absolute; +} + +.left-in-right-out-enter + /* .slide-fade-leave-active for below version 2.1.8 */ +{ + transform: translateX(-100%); + opacity: 0; +} + +.left-in-right-out-leave-to + /* .slide-fade-leave-active for below version 2.1.8 */ +{ + transform: translateX(100%); + opacity: 0; +} + +.right-in-left-out-enter-active, .right-in-left-out-leave-active { + transition: all .3s linear; + position: absolute; +} + +.right-in-left-out-enter { + transform: translateX(100%); +} + +.right-in-left-out-leave-to { + transform: translateX(-100%); +} + +.el-switch__input:focus ~ .el-switch__core { + position: relative; + box-sizing: border-box; + outline: none !important; +} + +.row { + position: relative; + box-sizing: border-box; +} + +[class*=col-] { + float: left; + box-sizing: border-box; +} + +.col-1 { + width: 4.16667%; +} + +.col-2 { + width: 8.33333%; +} + +.col-3 { + width: 12.5%; +} + +.col-4 { + width: 16.66667%; +} + +.col-5 { + width: 20.83333%; +} + +.col-6 { + width: 25%; +} + +.col-7 { + width: 29.16667%; +} + +.col-8 { + width: 33.33333%; +} + +.col-9 { + width: 37.5%; +} + +.col-10 { + width: 41.66667%; +} + +.col-11 { + width: 45.83333%; +} + +.col-12 { + width: 50%; +} + +.col-13 { + width: 54.16667%; +} + +.col-14 { + width: 58.33333%; +} + +.col-15 { + width: 62.5%; +} + +.col-16 { + width: 66.66667%; +} + +.col-17 { + width: 70.83333%; +} + +.col-18 { + width: 75%; +} + +.col-19 { + width: 79.16667%; +} + +.col-20 { + width: 83.33333%; +} + +.col-21 { + width: 87.5%; +} + +.col-22 { + width: 91.66667%; +} + +.col-23 { + width: 95.83333%; +} + +.col-24 { + width: 100%; +} + +.page_list { + display: flex; +} + +.page_list a { + display: inline-block; + height: 35px; + width: 35px; + line-height: 35px; + text-align: center; + cursor: pointer; + background-color: #2a2a2a; + color: #ffffff; +} + +.page_list a.disabled { + opacity: 0.7; +} + +.page_list a.current { + background-color: #16C0F8; + color: #ffffff; +} + +.page_list a:hover { + background-color: #16C0F8; + color: #ffffff; +} + +.page_list a.disabled:hover { + background-color: #373737; + color: #ffffff; +} + +.page_list a.top_page, +.page_list a.page_prev, +.page_list a.page_next, +.page_list a.end_page { + width: 55px; + padding: 0px 5px; +} + +.page_list p { + padding: 0px 20px; + line-height: 35px; +} + +.el-scrollbar.hidden_x > .el-scrollbar__wrap { + overflow-x: hidden !important; +} + +.el-scrollbar.hidden_x > .el-scrollbar__wrap > .el-scrollbar__view { + height: 100%; +} + +.el-menu--horizontal { + border-bottom: solid 0px #e6e6e6 !important; +} + +.el-message { + min-width: 100px; + padding: 10px 60px 10px 20px; + background-color: #ffffff; + box-shadow: 0 1px 6px rgba(0, 0, 0, .2); +} + +/* 带有固定title布局,title保持固定*/ +.wb-layout-title-fix { + width: 100%; + height: 100%; + overflow: hidden; +} + +.wb-layout-title-fix > .wb-head { + height: 60px; + overflow: hidden; +} + +.wb-layout-title-fix > .wb-body { + height: calc(100% - 60px); +} + +/* 保持页脚绝对底部,当内容足够或高于屏幕,会自动将页面撑开,底部依旧保持在底部*/ +.wb-layout-foot-absolute { + position: relative; + width: 100%; + height: auto; + min-height: 100%; +} + +.wb-layout-foot-absolute > .wb-body { + padding-bottom: 50px; +} + +.wb-layout-foot-absolute > .wb-foot { + position: absolute; + bottom: 0; + left: 0; + height: 50px; + width: 100%; +} + +/* 以下为APP样式 */ +#app { + padding: 0px; +} + +.box-card { + margin: 10px; + min-width: 1060px; +} +.el-dialog.dialog { + width: 700px; +} +.el-dialog.dialog .el-form-item__content, +.el-dialog.dialog .el-form-item__content > div, +.el-form.form .el-form-item__content, +.el-form.form .el-form-item__content > div, +.el-form.search .el-form-item__content, +.el-form.search .el-form-item__content > div { + width: 200px; +} +.el-transfer-panel__item { + height: 30px!important; + line-height: 30px!important; + padding-left: 15px!important; + display: block!important; +} + + +.el-table.data { + margin-top: 10px; + width: 100%; +} + +.el-table.data .cell { + max-height: 46px; + min-height: 23px; + text-overflow: ellipsis; + word-break: keep-all; +} + +/* 以上为APP样式 */ \ No newline at end of file diff --git a/src/main/resources/static/dist/fonts/element-icons.ttf b/src/main/resources/static/dist/fonts/element-icons.ttf new file mode 100644 index 0000000..91b74de Binary files /dev/null and b/src/main/resources/static/dist/fonts/element-icons.ttf differ diff --git a/src/main/resources/static/dist/fonts/element-icons.woff b/src/main/resources/static/dist/fonts/element-icons.woff new file mode 100644 index 0000000..02b9a25 Binary files /dev/null and b/src/main/resources/static/dist/fonts/element-icons.woff differ diff --git a/src/main/resources/static/dist/fonts/w-e-icon.woff b/src/main/resources/static/dist/fonts/w-e-icon.woff new file mode 100644 index 0000000..6dc5b5a Binary files /dev/null and b/src/main/resources/static/dist/fonts/w-e-icon.woff differ diff --git a/src/main/resources/static/dist/index.min.css b/src/main/resources/static/dist/index.min.css new file mode 100644 index 0000000..27abc60 --- /dev/null +++ b/src/main/resources/static/dist/index.min.css @@ -0,0 +1 @@ +@charset "UTF-8";.el-pagination--small .arrow.disabled,.el-table .hidden-columns,.el-table td.is-hidden>*,.el-table th.is-hidden>*,.el-table--hidden{visibility:hidden}.el-dropdown .el-dropdown-selfdefine:focus:active,.el-dropdown .el-dropdown-selfdefine:focus:not(.focusing),.el-message__closeBtn:focus,.el-message__content:focus,.el-popover:focus,.el-popover:focus:active,.el-popover__reference:focus:hover,.el-popover__reference:focus:not(.focusing),.el-rate:active,.el-rate:focus,.el-tooltip:focus:hover,.el-tooltip:focus:not(.focusing),.el-upload-list__item.is-success:active,.el-upload-list__item.is-success:not(.focusing):focus{outline-width:0}.el-input__suffix,.el-tree.is-dragging .el-tree-node__content *{pointer-events:none}@font-face{font-family:element-icons;src:url(fonts/element-icons.woff) format("woff"),url(fonts/element-icons.ttf) format("truetype");font-weight:400;font-display:"auto";font-style:normal}[class*=" el-icon-"],[class^=el-icon-]{font-family:element-icons!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;vertical-align:baseline;display:inline-block;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-icon-ice-cream-round:before{content:"\e6a0"}.el-icon-ice-cream-square:before{content:"\e6a3"}.el-icon-lollipop:before{content:"\e6a4"}.el-icon-potato-strips:before{content:"\e6a5"}.el-icon-milk-tea:before{content:"\e6a6"}.el-icon-ice-drink:before{content:"\e6a7"}.el-icon-ice-tea:before{content:"\e6a9"}.el-icon-coffee:before{content:"\e6aa"}.el-icon-orange:before{content:"\e6ab"}.el-icon-pear:before{content:"\e6ac"}.el-icon-apple:before{content:"\e6ad"}.el-icon-cherry:before{content:"\e6ae"}.el-icon-watermelon:before{content:"\e6af"}.el-icon-grape:before{content:"\e6b0"}.el-icon-refrigerator:before{content:"\e6b1"}.el-icon-goblet-square-full:before{content:"\e6b2"}.el-icon-goblet-square:before{content:"\e6b3"}.el-icon-goblet-full:before{content:"\e6b4"}.el-icon-goblet:before{content:"\e6b5"}.el-icon-cold-drink:before{content:"\e6b6"}.el-icon-coffee-cup:before{content:"\e6b8"}.el-icon-water-cup:before{content:"\e6b9"}.el-icon-hot-water:before{content:"\e6ba"}.el-icon-ice-cream:before{content:"\e6bb"}.el-icon-dessert:before{content:"\e6bc"}.el-icon-sugar:before{content:"\e6bd"}.el-icon-tableware:before{content:"\e6be"}.el-icon-burger:before{content:"\e6bf"}.el-icon-knife-fork:before{content:"\e6c1"}.el-icon-fork-spoon:before{content:"\e6c2"}.el-icon-chicken:before{content:"\e6c3"}.el-icon-food:before{content:"\e6c4"}.el-icon-dish-1:before{content:"\e6c5"}.el-icon-dish:before{content:"\e6c6"}.el-icon-moon-night:before{content:"\e6ee"}.el-icon-moon:before{content:"\e6f0"}.el-icon-cloudy-and-sunny:before{content:"\e6f1"}.el-icon-partly-cloudy:before{content:"\e6f2"}.el-icon-cloudy:before{content:"\e6f3"}.el-icon-sunny:before{content:"\e6f6"}.el-icon-sunset:before{content:"\e6f7"}.el-icon-sunrise-1:before{content:"\e6f8"}.el-icon-sunrise:before{content:"\e6f9"}.el-icon-heavy-rain:before{content:"\e6fa"}.el-icon-lightning:before{content:"\e6fb"}.el-icon-light-rain:before{content:"\e6fc"}.el-icon-wind-power:before{content:"\e6fd"}.el-icon-baseball:before{content:"\e712"}.el-icon-soccer:before{content:"\e713"}.el-icon-football:before{content:"\e715"}.el-icon-basketball:before{content:"\e716"}.el-icon-ship:before{content:"\e73f"}.el-icon-truck:before{content:"\e740"}.el-icon-bicycle:before{content:"\e741"}.el-icon-mobile-phone:before{content:"\e6d3"}.el-icon-service:before{content:"\e6d4"}.el-icon-key:before{content:"\e6e2"}.el-icon-unlock:before{content:"\e6e4"}.el-icon-lock:before{content:"\e6e5"}.el-icon-watch:before{content:"\e6fe"}.el-icon-watch-1:before{content:"\e6ff"}.el-icon-timer:before{content:"\e702"}.el-icon-alarm-clock:before{content:"\e703"}.el-icon-map-location:before{content:"\e704"}.el-icon-delete-location:before{content:"\e705"}.el-icon-add-location:before{content:"\e706"}.el-icon-location-information:before{content:"\e707"}.el-icon-location-outline:before{content:"\e708"}.el-icon-location:before{content:"\e79e"}.el-icon-place:before{content:"\e709"}.el-icon-discover:before{content:"\e70a"}.el-icon-first-aid-kit:before{content:"\e70b"}.el-icon-trophy-1:before{content:"\e70c"}.el-icon-trophy:before{content:"\e70d"}.el-icon-medal:before{content:"\e70e"}.el-icon-medal-1:before{content:"\e70f"}.el-icon-stopwatch:before{content:"\e710"}.el-icon-mic:before{content:"\e711"}.el-icon-copy-document:before{content:"\e718"}.el-icon-full-screen:before{content:"\e719"}.el-icon-switch-button:before{content:"\e71b"}.el-icon-aim:before{content:"\e71c"}.el-icon-crop:before{content:"\e71d"}.el-icon-odometer:before{content:"\e71e"}.el-icon-time:before{content:"\e71f"}.el-icon-bangzhu:before{content:"\e724"}.el-icon-close-notification:before{content:"\e726"}.el-icon-microphone:before{content:"\e727"}.el-icon-turn-off-microphone:before{content:"\e728"}.el-icon-position:before{content:"\e729"}.el-icon-postcard:before{content:"\e72a"}.el-icon-message:before{content:"\e72b"}.el-icon-chat-line-square:before{content:"\e72d"}.el-icon-chat-dot-square:before{content:"\e72e"}.el-icon-chat-dot-round:before{content:"\e72f"}.el-icon-chat-square:before{content:"\e730"}.el-icon-chat-line-round:before{content:"\e731"}.el-icon-chat-round:before{content:"\e732"}.el-icon-set-up:before{content:"\e733"}.el-icon-turn-off:before{content:"\e734"}.el-icon-open:before{content:"\e735"}.el-icon-connection:before{content:"\e736"}.el-icon-link:before{content:"\e737"}.el-icon-cpu:before{content:"\e738"}.el-icon-thumb:before{content:"\e739"}.el-icon-female:before{content:"\e73a"}.el-icon-male:before{content:"\e73b"}.el-icon-guide:before{content:"\e73c"}.el-icon-news:before{content:"\e73e"}.el-icon-price-tag:before{content:"\e744"}.el-icon-discount:before{content:"\e745"}.el-icon-wallet:before{content:"\e747"}.el-icon-coin:before{content:"\e748"}.el-icon-money:before{content:"\e749"}.el-icon-bank-card:before{content:"\e74a"}.el-icon-box:before{content:"\e74b"}.el-icon-present:before{content:"\e74c"}.el-icon-sell:before{content:"\e6d5"}.el-icon-sold-out:before{content:"\e6d6"}.el-icon-shopping-bag-2:before{content:"\e74d"}.el-icon-shopping-bag-1:before{content:"\e74e"}.el-icon-shopping-cart-2:before{content:"\e74f"}.el-icon-shopping-cart-1:before{content:"\e750"}.el-icon-shopping-cart-full:before{content:"\e751"}.el-icon-smoking:before{content:"\e752"}.el-icon-no-smoking:before{content:"\e753"}.el-icon-house:before{content:"\e754"}.el-icon-table-lamp:before{content:"\e755"}.el-icon-school:before{content:"\e756"}.el-icon-office-building:before{content:"\e757"}.el-icon-toilet-paper:before{content:"\e758"}.el-icon-notebook-2:before{content:"\e759"}.el-icon-notebook-1:before{content:"\e75a"}.el-icon-files:before{content:"\e75b"}.el-icon-collection:before{content:"\e75c"}.el-icon-receiving:before{content:"\e75d"}.el-icon-suitcase-1:before{content:"\e760"}.el-icon-suitcase:before{content:"\e761"}.el-icon-film:before{content:"\e763"}.el-icon-collection-tag:before{content:"\e765"}.el-icon-data-analysis:before{content:"\e766"}.el-icon-pie-chart:before{content:"\e767"}.el-icon-data-board:before{content:"\e768"}.el-icon-data-line:before{content:"\e76d"}.el-icon-reading:before{content:"\e769"}.el-icon-magic-stick:before{content:"\e76a"}.el-icon-coordinate:before{content:"\e76b"}.el-icon-mouse:before{content:"\e76c"}.el-icon-brush:before{content:"\e76e"}.el-icon-headset:before{content:"\e76f"}.el-icon-umbrella:before{content:"\e770"}.el-icon-scissors:before{content:"\e771"}.el-icon-mobile:before{content:"\e773"}.el-icon-attract:before{content:"\e774"}.el-icon-monitor:before{content:"\e775"}.el-icon-search:before{content:"\e778"}.el-icon-takeaway-box:before{content:"\e77a"}.el-icon-paperclip:before{content:"\e77d"}.el-icon-printer:before{content:"\e77e"}.el-icon-document-add:before{content:"\e782"}.el-icon-document:before{content:"\e785"}.el-icon-document-checked:before{content:"\e786"}.el-icon-document-copy:before{content:"\e787"}.el-icon-document-delete:before{content:"\e788"}.el-icon-document-remove:before{content:"\e789"}.el-icon-tickets:before{content:"\e78b"}.el-icon-folder-checked:before{content:"\e77f"}.el-icon-folder-delete:before{content:"\e780"}.el-icon-folder-remove:before{content:"\e781"}.el-icon-folder-add:before{content:"\e783"}.el-icon-folder-opened:before{content:"\e784"}.el-icon-folder:before{content:"\e78a"}.el-icon-edit-outline:before{content:"\e764"}.el-icon-edit:before{content:"\e78c"}.el-icon-date:before{content:"\e78e"}.el-icon-c-scale-to-original:before{content:"\e7c6"}.el-icon-view:before{content:"\e6ce"}.el-icon-loading:before{content:"\e6cf"}.el-icon-rank:before{content:"\e6d1"}.el-icon-sort-down:before{content:"\e7c4"}.el-icon-sort-up:before{content:"\e7c5"}.el-icon-sort:before{content:"\e6d2"}.el-icon-finished:before{content:"\e6cd"}.el-icon-refresh-left:before{content:"\e6c7"}.el-icon-refresh-right:before{content:"\e6c8"}.el-icon-refresh:before{content:"\e6d0"}.el-icon-video-play:before{content:"\e7c0"}.el-icon-video-pause:before{content:"\e7c1"}.el-icon-d-arrow-right:before{content:"\e6dc"}.el-icon-d-arrow-left:before{content:"\e6dd"}.el-icon-arrow-up:before{content:"\e6e1"}.el-icon-arrow-down:before{content:"\e6df"}.el-icon-arrow-right:before{content:"\e6e0"}.el-icon-arrow-left:before{content:"\e6de"}.el-icon-top-right:before{content:"\e6e7"}.el-icon-top-left:before{content:"\e6e8"}.el-icon-top:before{content:"\e6e6"}.el-icon-bottom:before{content:"\e6eb"}.el-icon-right:before{content:"\e6e9"}.el-icon-back:before{content:"\e6ea"}.el-icon-bottom-right:before{content:"\e6ec"}.el-icon-bottom-left:before{content:"\e6ed"}.el-icon-caret-top:before{content:"\e78f"}.el-icon-caret-bottom:before{content:"\e790"}.el-icon-caret-right:before{content:"\e791"}.el-icon-caret-left:before{content:"\e792"}.el-icon-d-caret:before{content:"\e79a"}.el-icon-share:before{content:"\e793"}.el-icon-menu:before{content:"\e798"}.el-icon-s-grid:before{content:"\e7a6"}.el-icon-s-check:before{content:"\e7a7"}.el-icon-s-data:before{content:"\e7a8"}.el-icon-s-opportunity:before{content:"\e7aa"}.el-icon-s-custom:before{content:"\e7ab"}.el-icon-s-claim:before{content:"\e7ad"}.el-icon-s-finance:before{content:"\e7ae"}.el-icon-s-comment:before{content:"\e7af"}.el-icon-s-flag:before{content:"\e7b0"}.el-icon-s-marketing:before{content:"\e7b1"}.el-icon-s-shop:before{content:"\e7b4"}.el-icon-s-open:before{content:"\e7b5"}.el-icon-s-management:before{content:"\e7b6"}.el-icon-s-ticket:before{content:"\e7b7"}.el-icon-s-release:before{content:"\e7b8"}.el-icon-s-home:before{content:"\e7b9"}.el-icon-s-promotion:before{content:"\e7ba"}.el-icon-s-operation:before{content:"\e7bb"}.el-icon-s-unfold:before{content:"\e7bc"}.el-icon-s-fold:before{content:"\e7a9"}.el-icon-s-platform:before{content:"\e7bd"}.el-icon-s-order:before{content:"\e7be"}.el-icon-s-cooperation:before{content:"\e7bf"}.el-icon-bell:before{content:"\e725"}.el-icon-message-solid:before{content:"\e799"}.el-icon-video-camera:before{content:"\e772"}.el-icon-video-camera-solid:before{content:"\e796"}.el-icon-camera:before{content:"\e779"}.el-icon-camera-solid:before{content:"\e79b"}.el-icon-download:before{content:"\e77c"}.el-icon-upload2:before{content:"\e77b"}.el-icon-upload:before{content:"\e7c3"}.el-icon-picture-outline-round:before{content:"\e75f"}.el-icon-picture-outline:before{content:"\e75e"}.el-icon-picture:before{content:"\e79f"}.el-icon-close:before{content:"\e6db"}.el-icon-check:before{content:"\e6da"}.el-icon-plus:before{content:"\e6d9"}.el-icon-minus:before{content:"\e6d8"}.el-icon-help:before{content:"\e73d"}.el-icon-s-help:before{content:"\e7b3"}.el-icon-circle-close:before{content:"\e78d"}.el-icon-circle-check:before{content:"\e720"}.el-icon-circle-plus-outline:before{content:"\e723"}.el-icon-remove-outline:before{content:"\e722"}.el-icon-zoom-out:before{content:"\e776"}.el-icon-zoom-in:before{content:"\e777"}.el-icon-error:before{content:"\e79d"}.el-icon-success:before{content:"\e79c"}.el-icon-circle-plus:before{content:"\e7a0"}.el-icon-remove:before{content:"\e7a2"}.el-icon-info:before{content:"\e7a1"}.el-icon-question:before{content:"\e7a4"}.el-icon-warning-outline:before{content:"\e6c9"}.el-icon-warning:before{content:"\e7a3"}.el-icon-goods:before{content:"\e7c2"}.el-icon-s-goods:before{content:"\e7b2"}.el-icon-star-off:before{content:"\e717"}.el-icon-star-on:before{content:"\e797"}.el-icon-more-outline:before{content:"\e6cc"}.el-icon-more:before{content:"\e794"}.el-icon-phone-outline:before{content:"\e6cb"}.el-icon-phone:before{content:"\e795"}.el-icon-user:before{content:"\e6e3"}.el-icon-user-solid:before{content:"\e7a5"}.el-icon-setting:before{content:"\e6ca"}.el-icon-s-tools:before{content:"\e7ac"}.el-icon-delete:before{content:"\e6d7"}.el-icon-delete-solid:before{content:"\e7c9"}.el-icon-eleme:before{content:"\e7c7"}.el-icon-platform-eleme:before{content:"\e7ca"}.el-icon-loading{-webkit-animation:rotating 2s linear infinite;animation:rotating 2s linear infinite}.el-icon--right{margin-left:5px}.el-icon--left{margin-right:5px}@-webkit-keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}@keyframes rotating{0%{-webkit-transform:rotateZ(0);transform:rotateZ(0)}100%{-webkit-transform:rotateZ(360deg);transform:rotateZ(360deg)}}.el-pagination{white-space:nowrap;padding:2px 5px;color:#303133;font-weight:700}.el-pagination::after,.el-pagination::before{display:table;content:""}.el-pagination::after{clear:both}.el-pagination button,.el-pagination span:not([class*=suffix]){display:inline-block;font-size:13px;min-width:35.5px;height:28px;line-height:28px;vertical-align:top;-webkit-box-sizing:border-box;box-sizing:border-box}.el-pagination .el-input__inner{text-align:center;-moz-appearance:textfield;line-height:normal}.el-pagination .el-input__suffix{right:0;-webkit-transform:scale(.8);transform:scale(.8)}.el-pagination .el-select .el-input{width:100px;margin:0 5px}.el-pagination .el-select .el-input .el-input__inner{padding-right:25px;border-radius:3px}.el-pagination button{border:none;padding:0 6px;background:0 0}.el-pagination button:focus{outline:0}.el-pagination button:hover{color:#409EFF}.el-pagination button:disabled{color:#C0C4CC;background-color:#FFF;cursor:not-allowed}.el-pagination .btn-next,.el-pagination .btn-prev{background:center center no-repeat #FFF;background-size:16px;cursor:pointer;margin:0;color:#303133}.el-pagination .btn-next .el-icon,.el-pagination .btn-prev .el-icon{display:block;font-size:12px;font-weight:700}.el-pagination .btn-prev{padding-right:12px}.el-pagination .btn-next{padding-left:12px}.el-pagination .el-pager li.disabled{color:#C0C4CC;cursor:not-allowed}.el-pager li,.el-pager li.btn-quicknext:hover,.el-pager li.btn-quickprev:hover{cursor:pointer}.el-pagination--small .btn-next,.el-pagination--small .btn-prev,.el-pagination--small .el-pager li,.el-pagination--small .el-pager li.btn-quicknext,.el-pagination--small .el-pager li.btn-quickprev,.el-pagination--small .el-pager li:last-child{border-color:transparent;font-size:12px;line-height:22px;height:22px;min-width:22px}.el-pagination--small .more::before,.el-pagination--small li.more::before{line-height:24px}.el-pagination--small button,.el-pagination--small span:not([class*=suffix]){height:22px;line-height:22px}.el-pagination--small .el-pagination__editor,.el-pagination--small .el-pagination__editor.el-input .el-input__inner{height:22px}.el-pagination__sizes{margin:0 10px 0 0;font-weight:400;color:#606266}.el-pagination__sizes .el-input .el-input__inner{font-size:13px;padding-left:8px}.el-pagination__sizes .el-input .el-input__inner:hover{border-color:#409EFF}.el-pagination__total{margin-right:10px;font-weight:400;color:#606266}.el-pagination__jump{margin-left:24px;font-weight:400;color:#606266}.el-pagination__jump .el-input__inner{padding:0 3px}.el-pagination__rightwrapper{float:right}.el-pagination__editor{line-height:18px;padding:0 2px;height:28px;text-align:center;margin:0 2px;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:3px}.el-pager,.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev{padding:0}.el-pagination__editor.el-input{width:50px}.el-pagination__editor.el-input .el-input__inner{height:28px}.el-pagination__editor .el-input__inner::-webkit-inner-spin-button,.el-pagination__editor .el-input__inner::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.el-pagination.is-background .btn-next,.el-pagination.is-background .btn-prev,.el-pagination.is-background .el-pager li{margin:0 5px;background-color:#f4f4f5;color:#606266;min-width:30px;border-radius:2px}.el-pagination.is-background .btn-next.disabled,.el-pagination.is-background .btn-next:disabled,.el-pagination.is-background .btn-prev.disabled,.el-pagination.is-background .btn-prev:disabled,.el-pagination.is-background .el-pager li.disabled{color:#C0C4CC}.el-pagination.is-background .el-pager li:not(.disabled):hover{color:#409EFF}.el-pagination.is-background .el-pager li:not(.disabled).active{background-color:#409EFF;color:#FFF}.el-dialog,.el-pager li{background:#FFF;-webkit-box-sizing:border-box}.el-pagination.is-background.el-pagination--small .btn-next,.el-pagination.is-background.el-pagination--small .btn-prev,.el-pagination.is-background.el-pagination--small .el-pager li{margin:0 3px;min-width:22px}.el-pager,.el-pager li{vertical-align:top;margin:0;display:inline-block}.el-pager{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;list-style:none;font-size:0}.el-date-table,.el-table th{-webkit-user-select:none;-ms-user-select:none}.el-pager .more::before{line-height:30px}.el-pager li{padding:0 4px;font-size:13px;min-width:35.5px;height:28px;line-height:28px;box-sizing:border-box;text-align:center}.el-menu--collapse .el-menu .el-submenu,.el-menu--popup{min-width:200px}.el-pager li.btn-quicknext,.el-pager li.btn-quickprev{line-height:28px;color:#303133}.el-pager li.btn-quicknext.disabled,.el-pager li.btn-quickprev.disabled{color:#C0C4CC}.el-pager li.active+li{border-left:0}.el-pager li:hover{color:#409EFF}.el-pager li.active{color:#409EFF;cursor:default}@-webkit-keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{100%{opacity:0}}.el-dialog{position:relative;margin:0 auto 50px;border-radius:2px;-webkit-box-shadow:0 1px 3px rgba(0,0,0,.3);box-shadow:0 1px 3px rgba(0,0,0,.3);box-sizing:border-box;width:50%}.el-dialog.is-fullscreen{width:100%;margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog__header{padding:20px 20px 10px}.el-dialog__headerbtn{position:absolute;top:20px;right:20px;padding:0;background:0 0;border:none;outline:0;cursor:pointer;font-size:16px}.el-dialog__headerbtn .el-dialog__close{color:#909399}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:#409EFF}.el-dialog__title{line-height:24px;font-size:18px;color:#303133}.el-dialog__body{padding:30px 20px;color:#606266;font-size:14px;word-break:break-all}.el-dialog__footer{padding:10px 20px 20px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px 25px 30px}.el-dialog--center .el-dialog__footer{text-align:inherit}.dialog-fade-enter-active{-webkit-animation:dialog-fade-in .3s;animation:dialog-fade-in .3s}.dialog-fade-leave-active{-webkit-animation:dialog-fade-out .3s;animation:dialog-fade-out .3s}@-webkit-keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes dialog-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-autocomplete{position:relative;display:inline-block}.el-autocomplete-suggestion{margin:5px 0;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:4px;border:1px solid #E4E7ED;-webkit-box-sizing:border-box;box-sizing:border-box;background-color:#FFF}.el-dropdown-menu,.el-menu--collapse .el-submenu .el-menu{z-index:10;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-autocomplete-suggestion__wrap{max-height:280px;padding:10px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-autocomplete-suggestion__list{margin:0;padding:0}.el-autocomplete-suggestion li{padding:0 20px;margin:0;line-height:34px;cursor:pointer;color:#606266;font-size:14px;list-style:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-autocomplete-suggestion li.highlighted,.el-autocomplete-suggestion li:hover{background-color:#F5F7FA}.el-autocomplete-suggestion li.divider{margin-top:6px;border-top:1px solid #000}.el-autocomplete-suggestion li.divider:last-child{margin-bottom:-6px}.el-autocomplete-suggestion.is-loading li{text-align:center;height:100px;line-height:100px;font-size:20px;color:#999}.el-autocomplete-suggestion.is-loading li::after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-autocomplete-suggestion.is-loading li:hover{background-color:#FFF}.el-autocomplete-suggestion.is-loading .el-icon-loading{vertical-align:middle}.el-dropdown{display:inline-block;position:relative;color:#606266;font-size:14px}.el-dropdown .el-button-group{display:block}.el-dropdown .el-button-group .el-button{float:none}.el-dropdown .el-dropdown__caret-button{padding-left:5px;padding-right:5px;position:relative;border-left:none}.el-dropdown .el-dropdown__caret-button::before{content:'';position:absolute;display:block;width:1px;top:5px;bottom:5px;left:0;background:rgba(255,255,255,.5)}.el-dropdown .el-dropdown__caret-button.el-button--default::before{background:rgba(220,223,230,.5)}.el-dropdown .el-dropdown__caret-button:hover::before{top:0;bottom:0}.el-dropdown .el-dropdown__caret-button .el-dropdown__icon{padding-left:0}.el-dropdown__icon{font-size:12px;margin:0 3px}.el-dropdown-menu{position:absolute;top:0;left:0;padding:10px 0;margin:5px 0;background-color:#FFF;border:1px solid #EBEEF5;border-radius:4px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-dropdown-menu__item{list-style:none;line-height:36px;padding:0 20px;margin:0;font-size:14px;color:#606266;cursor:pointer;outline:0}.el-dropdown-menu__item:focus,.el-dropdown-menu__item:not(.is-disabled):hover{background-color:#ecf5ff;color:#66b1ff}.el-dropdown-menu__item i{margin-right:5px}.el-dropdown-menu__item--divided{position:relative;margin-top:6px;border-top:1px solid #EBEEF5}.el-dropdown-menu__item--divided:before{content:'';height:6px;display:block;margin:0 -20px;background-color:#FFF}.el-dropdown-menu__item.is-disabled{cursor:default;color:#bbb;pointer-events:none}.el-dropdown-menu--medium{padding:6px 0}.el-dropdown-menu--medium .el-dropdown-menu__item{line-height:30px;padding:0 17px;font-size:14px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:6px}.el-dropdown-menu--medium .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:6px;margin:0 -17px}.el-dropdown-menu--small{padding:6px 0}.el-dropdown-menu--small .el-dropdown-menu__item{line-height:27px;padding:0 15px;font-size:13px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:4px}.el-dropdown-menu--small .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:4px;margin:0 -15px}.el-dropdown-menu--mini{padding:3px 0}.el-dropdown-menu--mini .el-dropdown-menu__item{line-height:24px;padding:0 10px;font-size:12px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided{margin-top:3px}.el-dropdown-menu--mini .el-dropdown-menu__item.el-dropdown-menu__item--divided:before{height:3px;margin:0 -10px}.el-menu{border-right:solid 1px #e6e6e6;list-style:none;position:relative;margin:0;padding-left:0;background-color:#FFF}.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,.el-menu--horizontal>.el-submenu .el-submenu__title:hover{background-color:#fff}.el-menu::after,.el-menu::before{display:table;content:""}.el-menu::after{clear:both}.el-menu.el-menu--horizontal{border-bottom:solid 1px #e6e6e6}.el-menu--horizontal{border-right:none}.el-menu--horizontal>.el-menu-item{float:left;height:60px;line-height:60px;margin:0;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-menu-item a,.el-menu--horizontal>.el-menu-item a:hover{color:inherit}.el-menu--horizontal>.el-submenu{float:left}.el-menu--horizontal>.el-submenu:focus,.el-menu--horizontal>.el-submenu:hover{outline:0}.el-menu--horizontal>.el-submenu:focus .el-submenu__title,.el-menu--horizontal>.el-submenu:hover .el-submenu__title{color:#303133}.el-menu--horizontal>.el-submenu.is-active .el-submenu__title{border-bottom:2px solid #409EFF;color:#303133}.el-menu--horizontal>.el-submenu .el-submenu__title{height:60px;line-height:60px;border-bottom:2px solid transparent;color:#909399}.el-menu--horizontal>.el-submenu .el-submenu__icon-arrow{position:static;vertical-align:middle;margin-left:8px;margin-top:-3px}.el-menu--horizontal .el-menu .el-menu-item,.el-menu--horizontal .el-menu .el-submenu__title{background-color:#FFF;float:none;height:36px;line-height:36px;padding:0 10px;color:#909399}.el-menu--horizontal .el-menu .el-menu-item.is-active,.el-menu--horizontal .el-menu .el-submenu.is-active>.el-submenu__title{color:#303133}.el-menu--horizontal .el-menu-item:not(.is-disabled):focus,.el-menu--horizontal .el-menu-item:not(.is-disabled):hover{outline:0;color:#303133}.el-menu--horizontal>.el-menu-item.is-active{border-bottom:2px solid #409EFF;color:#303133}.el-menu--collapse{width:64px}.el-menu--collapse>.el-menu-item [class^=el-icon-],.el-menu--collapse>.el-submenu>.el-submenu__title [class^=el-icon-]{margin:0;vertical-align:middle;width:24px;text-align:center}.el-menu--collapse>.el-menu-item .el-submenu__icon-arrow,.el-menu--collapse>.el-submenu>.el-submenu__title .el-submenu__icon-arrow{display:none}.el-menu--collapse>.el-menu-item span,.el-menu--collapse>.el-submenu>.el-submenu__title span{height:0;width:0;overflow:hidden;visibility:hidden;display:inline-block}.el-menu--collapse>.el-menu-item.is-active i{color:inherit}.el-menu--collapse .el-submenu{position:relative}.el-menu--collapse .el-submenu .el-menu{position:absolute;margin-left:5px;top:0;left:100%;border:1px solid #E4E7ED;border-radius:2px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu-item,.el-submenu__title{height:56px;line-height:56px;position:relative;-webkit-box-sizing:border-box;white-space:nowrap;list-style:none}.el-menu--collapse .el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:none;transform:none}.el-menu--popup{z-index:100;border:none;padding:5px 0;border-radius:2px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-menu--popup-bottom-start{margin-top:5px}.el-menu--popup-right-start{margin-left:5px;margin-right:5px}.el-menu-item{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;box-sizing:border-box}.el-menu-item *{vertical-align:middle}.el-menu-item i{color:#909399}.el-menu-item:focus,.el-menu-item:hover{outline:0;background-color:#ecf5ff}.el-menu-item.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-menu-item [class^=el-icon-]{margin-right:5px;width:24px;text-align:center;font-size:18px;vertical-align:middle}.el-menu-item.is-active{color:#409EFF}.el-menu-item.is-active i{color:inherit}.el-submenu{list-style:none;margin:0;padding-left:0}.el-submenu__title{font-size:14px;color:#303133;padding:0 20px;cursor:pointer;-webkit-transition:border-color .3s,background-color .3s,color .3s;transition:border-color .3s,background-color .3s,color .3s;box-sizing:border-box}.el-submenu__title *{vertical-align:middle}.el-submenu__title i{color:#909399}.el-submenu__title:focus,.el-submenu__title:hover{outline:0;background-color:#ecf5ff}.el-submenu__title.is-disabled{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu__title:hover{background-color:#ecf5ff}.el-submenu .el-menu{border:none}.el-submenu .el-menu-item{height:50px;line-height:50px;padding:0 45px;min-width:200px}.el-submenu__icon-arrow{position:absolute;top:50%;right:20px;margin-top:-7px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:12px}.el-submenu.is-active .el-submenu__title{border-bottom-color:#409EFF}.el-submenu.is-opened>.el-submenu__title .el-submenu__icon-arrow{-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg)}.el-submenu.is-disabled .el-menu-item,.el-submenu.is-disabled .el-submenu__title{opacity:.25;cursor:not-allowed;background:0 0!important}.el-submenu [class^=el-icon-]{vertical-align:middle;margin-right:5px;width:24px;text-align:center;font-size:18px}.el-menu-item-group>ul{padding:0}.el-menu-item-group__title{padding:7px 0 7px 20px;line-height:normal;font-size:12px;color:#909399}.el-radio-button__inner,.el-radio-group{display:inline-block;line-height:1;vertical-align:middle}.horizontal-collapse-transition .el-submenu__title .el-submenu__icon-arrow{-webkit-transition:.2s;transition:.2s;opacity:0}.el-radio-group{font-size:0}.el-radio-button{position:relative;display:inline-block;outline:0}.el-radio-button__inner{white-space:nowrap;background:#FFF;border:1px solid #DCDFE6;font-weight:500;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;position:relative;cursor:pointer;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-radio-button__inner.is-round{padding:12px 20px}.el-radio-button__inner:hover{color:#409EFF}.el-radio-button__inner [class*=el-icon-]{line-height:.9}.el-radio-button__inner [class*=el-icon-]+span{margin-left:5px}.el-radio-button:first-child .el-radio-button__inner{border-left:1px solid #DCDFE6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-radio-button__orig-radio{opacity:0;outline:0;position:absolute;z-index:-1}.el-radio-button__orig-radio:checked+.el-radio-button__inner{color:#FFF;background-color:#409EFF;border-color:#409EFF;-webkit-box-shadow:-1px 0 0 0 #409EFF;box-shadow:-1px 0 0 0 #409EFF}.el-radio-button__orig-radio:disabled+.el-radio-button__inner{color:#C0C4CC;cursor:not-allowed;background-image:none;background-color:#FFF;border-color:#EBEEF5;-webkit-box-shadow:none;box-shadow:none}.el-radio-button__orig-radio:disabled:checked+.el-radio-button__inner{background-color:#F2F6FC}.el-radio-button:last-child .el-radio-button__inner{border-radius:0 4px 4px 0}.el-popover,.el-radio-button:first-child:last-child .el-radio-button__inner{border-radius:4px}.el-radio-button--medium .el-radio-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-radio-button--medium .el-radio-button__inner.is-round{padding:10px 20px}.el-radio-button--small .el-radio-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-radio-button--small .el-radio-button__inner.is-round{padding:9px 15px}.el-radio-button--mini .el-radio-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-radio-button--mini .el-radio-button__inner.is-round{padding:7px 15px}.el-radio-button:focus:not(.is-focus):not(:active):not(.is-disabled){-webkit-box-shadow:0 0 2px 2px #409EFF;box-shadow:0 0 2px 2px #409EFF}.el-switch{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative;font-size:14px;line-height:20px;height:20px;vertical-align:middle}.el-switch__core,.el-switch__label{display:inline-block;cursor:pointer}.el-switch.is-disabled .el-switch__core,.el-switch.is-disabled .el-switch__label{cursor:not-allowed}.el-switch__label{-webkit-transition:.2s;transition:.2s;height:20px;font-size:14px;font-weight:500;vertical-align:middle;color:#303133}.el-switch__label.is-active{color:#409EFF}.el-switch__label--left{margin-right:10px}.el-switch__label--right{margin-left:10px}.el-switch__label *{line-height:1;font-size:14px;display:inline-block}.el-switch__input{position:absolute;width:0;height:0;opacity:0;margin:0}.el-switch__core{margin:0;position:relative;width:40px;height:20px;border:1px solid #DCDFE6;outline:0;border-radius:10px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#DCDFE6;-webkit-transition:border-color .3s,background-color .3s;transition:border-color .3s,background-color .3s;vertical-align:middle}.el-switch__core:after{content:"";position:absolute;top:1px;left:1px;border-radius:100%;-webkit-transition:all .3s;transition:all .3s;width:16px;height:16px;background-color:#FFF}.el-switch.is-checked .el-switch__core{border-color:#409EFF;background-color:#409EFF}.el-switch.is-checked .el-switch__core::after{left:100%;margin-left:-17px}.el-switch.is-disabled{opacity:.6}.el-switch--wide .el-switch__label.el-switch__label--left span{left:10px}.el-switch--wide .el-switch__label.el-switch__label--right span{right:10px}.el-switch .label-fade-enter,.el-switch .label-fade-leave-active{opacity:0}.el-select-dropdown{position:absolute;z-index:1001;border:1px solid #E4E7ED;border-radius:4px;background-color:#FFF;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected{color:#409EFF;background-color:#FFF}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected.hover{background-color:#F5F7FA}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected::after{position:absolute;right:20px;font-family:element-icons;content:"\e6da";font-size:12px;font-weight:700;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.el-select-dropdown .el-scrollbar.is-empty .el-select-dropdown__list{padding:0}.el-select-dropdown__empty{padding:10px 0;margin:0;text-align:center;color:#999;font-size:14px}.el-select-dropdown__wrap{max-height:274px}.el-select-dropdown__list{list-style:none;padding:6px 0;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-select-dropdown__item{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#606266;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.el-select .el-tag,.el-table{-webkit-box-sizing:border-box}.el-select-dropdown__item.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-select-dropdown__item.is-disabled:hover{background-color:#FFF}.el-select-dropdown__item.hover,.el-select-dropdown__item:hover{background-color:#F5F7FA}.el-select-dropdown__item.selected{color:#409EFF;font-weight:700}.el-select-group{margin:0;padding:0}.el-select-group__wrap{position:relative;list-style:none;margin:0;padding:0}.el-select-group__wrap:not(:last-of-type){padding-bottom:24px}.el-select-group__wrap:not(:last-of-type)::after{content:'';position:absolute;display:block;left:20px;right:20px;bottom:12px;height:1px;background:#E4E7ED}.el-select-group__title{padding-left:20px;font-size:12px;color:#909399;line-height:30px}.el-select-group .el-select-dropdown__item{padding-left:20px}.el-select{display:inline-block;position:relative}.el-select .el-select__tags>span{display:contents}.el-select:hover .el-input__inner{border-color:#C0C4CC}.el-select .el-input__inner{cursor:pointer;padding-right:35px}.el-select .el-input__inner:focus{border-color:#409EFF}.el-select .el-input .el-select__caret{color:#C0C4CC;font-size:14px;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);cursor:pointer}.el-select .el-input .el-select__caret.is-reverse{-webkit-transform:rotateZ(0);transform:rotateZ(0)}.el-select .el-input .el-select__caret.is-show-close{font-size:14px;text-align:center;-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg);border-radius:100%;color:#C0C4CC;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-select .el-input .el-select__caret.is-show-close:hover{color:#909399}.el-select .el-input.is-disabled .el-input__inner{cursor:not-allowed}.el-select .el-input.is-disabled .el-input__inner:hover{border-color:#E4E7ED}.el-select .el-input.is-focus .el-input__inner{border-color:#409EFF}.el-select>.el-input{display:block}.el-select__input{border:none;outline:0;padding:0;margin-left:15px;color:#666;font-size:14px;-webkit-appearance:none;-moz-appearance:none;appearance:none;height:28px;background-color:transparent}.el-select__input.is-mini{height:14px}.el-select__close{cursor:pointer;position:absolute;top:8px;z-index:1000;right:25px;color:#C0C4CC;line-height:18px;font-size:14px}.el-select__close:hover{color:#909399}.el-select__tags{position:absolute;line-height:normal;white-space:normal;z-index:1;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-select .el-tag__close{margin-top:-2px}.el-select .el-tag{box-sizing:border-box;border-color:transparent;margin:2px 0 2px 6px;background-color:#f0f2f5}.el-select .el-tag__close.el-icon-close{background-color:#C0C4CC;right:-7px;top:0;color:#FFF}.el-select .el-tag__close.el-icon-close:hover{background-color:#909399}.el-table,.el-table__expanded-cell{background-color:#FFF}.el-select .el-tag__close.el-icon-close::before{display:block;-webkit-transform:translate(0,.5px);transform:translate(0,.5px)}.el-table{position:relative;overflow:hidden;box-sizing:border-box;-webkit-box-flex:1;-ms-flex:1;flex:1;width:100%;max-width:100%;font-size:14px;color:#606266}.el-table--mini,.el-table--small,.el-table__expand-icon{font-size:12px}.el-table__empty-block{min-height:60px;text-align:center;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-table__empty-text{line-height:60px;width:50%;color:#909399}.el-table__expand-column .cell{padding:0;text-align:center}.el-table__expand-icon{position:relative;cursor:pointer;color:#666;-webkit-transition:-webkit-transform .2s ease-in-out;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out;height:20px}.el-table__expand-icon--expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-table__expand-icon>.el-icon{position:absolute;left:50%;top:50%;margin-left:-5px;margin-top:-5px}.el-table__expanded-cell[class*=cell]{padding:20px 50px}.el-table__expanded-cell:hover{background-color:transparent!important}.el-table__placeholder{display:inline-block;width:20px}.el-table__append-wrapper{overflow:hidden}.el-table--fit{border-right:0;border-bottom:0}.el-table--fit td.gutter,.el-table--fit th.gutter{border-right-width:1px}.el-table--scrollable-x .el-table__body-wrapper{overflow-x:auto}.el-table--scrollable-y .el-table__body-wrapper{overflow-y:auto}.el-table thead{color:#909399;font-weight:500}.el-table thead.is-group th{background:#F5F7FA}.el-table th,.el-table tr{background-color:#FFF}.el-table td,.el-table th{padding:12px 0;min-width:0;-webkit-box-sizing:border-box;box-sizing:border-box;text-overflow:ellipsis;vertical-align:middle;position:relative;text-align:left}.el-table td.is-center,.el-table th.is-center{text-align:center}.el-table td.is-right,.el-table th.is-right{text-align:right}.el-table td.gutter,.el-table th.gutter{width:15px;border-right-width:0;border-bottom-width:0;padding:0}.el-table--medium td,.el-table--medium th{padding:10px 0}.el-table--small td,.el-table--small th{padding:8px 0}.el-table--mini td,.el-table--mini th{padding:6px 0}.el-table .cell,.el-table th div{padding-right:10px;overflow:hidden;text-overflow:ellipsis}.el-table .cell,.el-table th div,.el-table--border td:first-child .cell,.el-table--border th:first-child .cell{padding-left:10px}.el-table tr input[type=checkbox]{margin:0}.el-table td,.el-table th.is-leaf{border-bottom:1px solid #EBEEF5}.el-table th.is-sortable{cursor:pointer}.el-table th{white-space:nowrap;overflow:hidden;-moz-user-select:none;user-select:none}.el-table th div{display:inline-block;line-height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;white-space:nowrap}.el-table th>.cell{position:relative;word-wrap:normal;text-overflow:ellipsis;display:inline-block;vertical-align:middle;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-table th>.cell.highlight{color:#409EFF}.el-table th.required>div::before{display:inline-block;content:"";width:8px;height:8px;border-radius:50%;background:#ff4d51;margin-right:5px;vertical-align:middle}.el-table td div{-webkit-box-sizing:border-box;box-sizing:border-box}.el-table td.gutter{width:0}.el-table .cell{-webkit-box-sizing:border-box;box-sizing:border-box;white-space:normal;word-break:break-all;line-height:23px}.el-table .cell.el-tooltip{white-space:nowrap;min-width:50px}.el-table--border,.el-table--group{border:1px solid #EBEEF5}.el-table--border::after,.el-table--group::after,.el-table::before{content:'';position:absolute;background-color:#EBEEF5;z-index:1}.el-table--border::after,.el-table--group::after{top:0;right:0;width:1px;height:100%}.el-table::before{left:0;bottom:0;width:100%;height:1px}.el-table--border{border-right:none;border-bottom:none}.el-table--border.el-loading-parent--relative{border-color:transparent}.el-table--border td,.el-table--border th,.el-table__body-wrapper .el-table--border.is-scrolling-left~.el-table__fixed{border-right:1px solid #EBEEF5}.el-table--border th.gutter:last-of-type{border-bottom:1px solid #EBEEF5;border-bottom-width:1px}.el-table--border th,.el-table__fixed-right-patch{border-bottom:1px solid #EBEEF5}.el-table__fixed,.el-table__fixed-right{position:absolute;top:0;left:0;overflow-x:hidden;overflow-y:hidden;-webkit-box-shadow:0 0 10px rgba(0,0,0,.12);box-shadow:0 0 10px rgba(0,0,0,.12)}.el-table__fixed-right::before,.el-table__fixed::before{content:'';position:absolute;left:0;bottom:0;width:100%;height:1px;background-color:#EBEEF5;z-index:4}.el-table__fixed-right-patch{position:absolute;top:-1px;right:0;background-color:#FFF}.el-table__fixed-right{top:0;left:auto;right:0}.el-table__fixed-right .el-table__fixed-body-wrapper,.el-table__fixed-right .el-table__fixed-footer-wrapper,.el-table__fixed-right .el-table__fixed-header-wrapper{left:auto;right:0}.el-table__fixed-header-wrapper{position:absolute;left:0;top:0;z-index:3}.el-table__fixed-footer-wrapper{position:absolute;left:0;bottom:0;z-index:3}.el-table__fixed-footer-wrapper tbody td{border-top:1px solid #EBEEF5;background-color:#F5F7FA;color:#606266}.el-table__fixed-body-wrapper{position:absolute;left:0;top:37px;overflow:hidden;z-index:3}.el-table__body-wrapper,.el-table__footer-wrapper,.el-table__header-wrapper{width:100%}.el-table__footer-wrapper{margin-top:-1px}.el-table__footer-wrapper td{border-top:1px solid #EBEEF5}.el-table__body,.el-table__footer,.el-table__header{table-layout:fixed;border-collapse:separate}.el-table__footer-wrapper,.el-table__header-wrapper{overflow:hidden}.el-table__footer-wrapper tbody td,.el-table__header-wrapper tbody td{background-color:#F5F7FA;color:#606266}.el-table__body-wrapper{overflow:hidden;position:relative}.el-table__body-wrapper.is-scrolling-left~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed,.el-table__body-wrapper.is-scrolling-none~.el-table__fixed-right,.el-table__body-wrapper.is-scrolling-right~.el-table__fixed-right{-webkit-box-shadow:none;box-shadow:none}.el-picker-panel,.el-table-filter{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-table__body-wrapper .el-table--border.is-scrolling-right~.el-table__fixed-right{border-left:1px solid #EBEEF5}.el-table .caret-wrapper{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;width:24px;vertical-align:middle;cursor:pointer;overflow:initial;position:relative}.el-table .sort-caret{width:0;height:0;border:5px solid transparent;position:absolute;left:7px}.el-table .sort-caret.ascending{border-bottom-color:#C0C4CC;top:5px}.el-table .sort-caret.descending{border-top-color:#C0C4CC;bottom:7px}.el-table .ascending .sort-caret.ascending{border-bottom-color:#409EFF}.el-table .descending .sort-caret.descending{border-top-color:#409EFF}.el-table .hidden-columns{position:absolute;z-index:-1}.el-table--striped .el-table__body tr.el-table__row--striped td{background:#FAFAFA}.el-table--striped .el-table__body tr.el-table__row--striped.current-row td{background-color:#ecf5ff}.el-table__body tr.hover-row.current-row>td,.el-table__body tr.hover-row.el-table__row--striped.current-row>td,.el-table__body tr.hover-row.el-table__row--striped>td,.el-table__body tr.hover-row>td{background-color:#F5F7FA}.el-table__body tr.current-row>td{background-color:#ecf5ff}.el-table__column-resize-proxy{position:absolute;left:200px;top:0;bottom:0;width:0;border-left:1px solid #EBEEF5;z-index:10}.el-table__column-filter-trigger{display:inline-block;line-height:34px;cursor:pointer}.el-table__column-filter-trigger i{color:#909399;font-size:12px;-webkit-transform:scale(.75);transform:scale(.75)}.el-table--enable-row-transition .el-table__body td{-webkit-transition:background-color .25s ease;transition:background-color .25s ease}.el-table--enable-row-hover .el-table__body tr:hover>td{background-color:#F5F7FA}.el-table--fluid-height .el-table__fixed,.el-table--fluid-height .el-table__fixed-right{bottom:0;overflow:hidden}.el-table [class*=el-table__row--level] .el-table__expand-icon{display:inline-block;width:20px;line-height:20px;height:20px;text-align:center;margin-right:3px}.el-table-column--selection .cell{padding-left:14px;padding-right:14px}.el-table-filter{border:1px solid #EBEEF5;border-radius:2px;background-color:#FFF;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:2px 0}.el-date-table td,.el-date-table td div{height:30px;-webkit-box-sizing:border-box}.el-table-filter__list{padding:5px 0;margin:0;list-style:none;min-width:100px}.el-table-filter__list-item{line-height:36px;padding:0 10px;cursor:pointer;font-size:14px}.el-table-filter__list-item:hover{background-color:#ecf5ff;color:#66b1ff}.el-table-filter__list-item.is-active{background-color:#409EFF;color:#FFF}.el-table-filter__content{min-width:100px}.el-table-filter__bottom{border-top:1px solid #EBEEF5;padding:8px}.el-table-filter__bottom button{background:0 0;border:none;color:#606266;cursor:pointer;font-size:13px;padding:0 3px}.el-date-table td.in-range div,.el-date-table td.in-range div:hover,.el-date-table.is-week-mode .el-date-table__row.current div,.el-date-table.is-week-mode .el-date-table__row:hover div{background-color:#F2F6FC}.el-table-filter__bottom button:hover{color:#409EFF}.el-table-filter__bottom button:focus{outline:0}.el-table-filter__bottom button.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-table-filter__wrap{max-height:280px}.el-table-filter__checkbox-group{padding:10px}.el-table-filter__checkbox-group label.el-checkbox{display:block;margin-right:5px;margin-bottom:8px;margin-left:5px}.el-table-filter__checkbox-group .el-checkbox:last-child{margin-bottom:0}.el-date-table{font-size:12px;-moz-user-select:none;user-select:none}.el-slider__button-wrapper,.el-time-panel{-webkit-user-select:none;-ms-user-select:none}.el-date-table.is-week-mode .el-date-table__row:hover td.available:hover{color:#606266}.el-date-table.is-week-mode .el-date-table__row:hover td:first-child div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table.is-week-mode .el-date-table__row:hover td:last-child div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td{width:32px;padding:4px 0;box-sizing:border-box;text-align:center;cursor:pointer;position:relative}.el-date-table td div{padding:3px 0;box-sizing:border-box}.el-date-table td span{width:24px;height:24px;display:block;margin:0 auto;line-height:24px;position:absolute;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);border-radius:50%}.el-date-table td.next-month,.el-date-table td.prev-month{color:#C0C4CC}.el-date-table td.today{position:relative}.el-date-table td.today span{color:#409EFF;font-weight:700}.el-date-table td.today.end-date span,.el-date-table td.today.start-date span{color:#FFF}.el-date-table td.available:hover{color:#409EFF}.el-date-table td.current:not(.disabled) span{color:#FFF;background-color:#409EFF}.el-date-table td.end-date div,.el-date-table td.start-date div{color:#FFF}.el-date-table td.end-date span,.el-date-table td.start-date span{background-color:#409EFF}.el-date-table td.start-date div{margin-left:5px;border-top-left-radius:15px;border-bottom-left-radius:15px}.el-date-table td.end-date div{margin-right:5px;border-top-right-radius:15px;border-bottom-right-radius:15px}.el-date-table td.disabled div{background-color:#F5F7FA;opacity:1;cursor:not-allowed;color:#C0C4CC}.el-date-table td.selected div{margin-left:5px;margin-right:5px;background-color:#F2F6FC;border-radius:15px}.el-date-table td.selected div:hover{background-color:#F2F6FC}.el-date-table td.selected span{background-color:#409EFF;color:#FFF;border-radius:15px}.el-date-table td.week{font-size:80%;color:#606266}.el-month-table,.el-year-table{font-size:12px;border-collapse:collapse}.el-date-table th{padding:5px;color:#606266;font-weight:400;border-bottom:solid 1px #EBEEF5}.el-month-table{margin:-1px}.el-month-table td{text-align:center;padding:8px 0;cursor:pointer}.el-month-table td div{height:48px;padding:6px 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-month-table td.today .cell{color:#409EFF;font-weight:700}.el-month-table td.today.end-date .cell,.el-month-table td.today.start-date .cell{color:#FFF}.el-month-table td.disabled .cell{background-color:#F5F7FA;cursor:not-allowed;color:#C0C4CC}.el-month-table td.disabled .cell:hover{color:#C0C4CC}.el-month-table td .cell{width:60px;height:36px;display:block;line-height:36px;color:#606266;margin:0 auto;border-radius:18px}.el-month-table td .cell:hover{color:#409EFF}.el-month-table td.in-range div,.el-month-table td.in-range div:hover{background-color:#F2F6FC}.el-month-table td.end-date div,.el-month-table td.start-date div{color:#FFF}.el-month-table td.end-date .cell,.el-month-table td.start-date .cell{color:#FFF;background-color:#409EFF}.el-month-table td.start-date div{border-top-left-radius:24px;border-bottom-left-radius:24px}.el-month-table td.end-date div{border-top-right-radius:24px;border-bottom-right-radius:24px}.el-month-table td.current:not(.disabled) .cell{color:#409EFF}.el-year-table{margin:-1px}.el-year-table .el-icon{color:#303133}.el-year-table td{text-align:center;padding:20px 3px;cursor:pointer}.el-year-table td.today .cell{color:#409EFF;font-weight:700}.el-year-table td.disabled .cell{background-color:#F5F7FA;cursor:not-allowed;color:#C0C4CC}.el-year-table td.disabled .cell:hover{color:#C0C4CC}.el-year-table td .cell{width:48px;height:32px;display:block;line-height:32px;color:#606266;margin:0 auto}.el-year-table td .cell:hover,.el-year-table td.current:not(.disabled) .cell{color:#409EFF}.el-date-range-picker{width:646px}.el-date-range-picker.has-sidebar{width:756px}.el-date-range-picker table{table-layout:fixed;width:100%}.el-date-range-picker .el-picker-panel__body{min-width:513px}.el-date-range-picker .el-picker-panel__content{margin:0}.el-date-range-picker__header{position:relative;text-align:center;height:28px}.el-date-range-picker__header [class*=arrow-left]{float:left}.el-date-range-picker__header [class*=arrow-right]{float:right}.el-date-range-picker__header div{font-size:16px;font-weight:500;margin-right:50px}.el-date-range-picker__content{float:left;width:50%;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:16px}.el-date-range-picker__content.is-left{border-right:1px solid #e4e4e4}.el-date-range-picker__content .el-date-range-picker__header div{margin-left:50px;margin-right:50px}.el-date-range-picker__editors-wrap{-webkit-box-sizing:border-box;box-sizing:border-box;display:table-cell}.el-date-range-picker__editors-wrap.is-right{text-align:right}.el-date-range-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-range-picker__time-header>.el-icon-arrow-right{font-size:20px;vertical-align:middle;display:table-cell;color:#303133}.el-date-range-picker__time-picker-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-range-picker__time-picker-wrap .el-picker-panel{position:absolute;top:13px;right:0;z-index:1;background:#FFF}.el-date-picker{width:322px}.el-date-picker.has-sidebar.has-time{width:434px}.el-date-picker.has-sidebar{width:438px}.el-date-picker.has-time .el-picker-panel__body-wrapper{position:relative}.el-date-picker .el-picker-panel__content{width:292px}.el-date-picker table{table-layout:fixed;width:100%}.el-date-picker__editor-wrap{position:relative;display:table-cell;padding:0 5px}.el-date-picker__time-header{position:relative;border-bottom:1px solid #e4e4e4;font-size:12px;padding:8px 5px 5px;display:table;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.el-date-picker__header{margin:12px;text-align:center}.el-date-picker__header--bordered{margin-bottom:0;padding-bottom:12px;border-bottom:solid 1px #EBEEF5}.el-date-picker__header--bordered+.el-picker-panel__content{margin-top:0}.el-date-picker__header-label{font-size:16px;font-weight:500;padding:0 5px;line-height:22px;text-align:center;cursor:pointer;color:#606266}.el-date-picker__header-label.active,.el-date-picker__header-label:hover{color:#409EFF}.el-date-picker__prev-btn{float:left}.el-date-picker__next-btn{float:right}.el-date-picker__time-wrap{padding:10px;text-align:center}.el-date-picker__time-label{float:left;cursor:pointer;line-height:30px;margin-left:10px}.time-select{margin:5px 0;min-width:0}.time-select .el-picker-panel__content{max-height:200px;margin:0}.time-select-item{padding:8px 10px;font-size:14px;line-height:20px}.time-select-item.selected:not(.disabled){color:#409EFF;font-weight:700}.time-select-item.disabled{color:#E4E7ED;cursor:not-allowed}.time-select-item:hover{background-color:#F5F7FA;font-weight:700;cursor:pointer}.el-date-editor{position:relative;display:inline-block;text-align:left}.el-date-editor.el-input,.el-date-editor.el-input__inner{width:220px}.el-date-editor--monthrange.el-input,.el-date-editor--monthrange.el-input__inner{width:300px}.el-date-editor--daterange.el-input,.el-date-editor--daterange.el-input__inner,.el-date-editor--timerange.el-input,.el-date-editor--timerange.el-input__inner{width:350px}.el-date-editor--datetimerange.el-input,.el-date-editor--datetimerange.el-input__inner{width:400px}.el-date-editor--dates .el-input__inner{text-overflow:ellipsis;white-space:nowrap}.el-date-editor .el-icon-circle-close{cursor:pointer}.el-date-editor .el-range__icon{font-size:14px;margin-left:-5px;color:#C0C4CC;float:left;line-height:32px}.el-date-editor .el-range-input,.el-date-editor .el-range-separator{height:100%;margin:0;text-align:center;display:inline-block;font-size:14px}.el-date-editor .el-range-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;outline:0;padding:0;width:39%;color:#606266}.el-date-editor .el-range-input::-webkit-input-placeholder{color:#C0C4CC}.el-date-editor .el-range-input::-ms-input-placeholder{color:#C0C4CC}.el-date-editor .el-range-input::placeholder{color:#C0C4CC}.el-date-editor .el-range-separator{padding:0 5px;line-height:32px;width:5%;color:#303133}.el-date-editor .el-range__close-icon{font-size:14px;color:#C0C4CC;width:25px;display:inline-block;float:right;line-height:32px}.el-range-editor.el-input__inner{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:3px 10px}.el-range-editor .el-range-input{line-height:1}.el-range-editor.is-active,.el-range-editor.is-active:hover{border-color:#409EFF}.el-range-editor--medium.el-input__inner{height:36px}.el-range-editor--medium .el-range-separator{line-height:28px;font-size:14px}.el-range-editor--medium .el-range-input{font-size:14px}.el-range-editor--medium .el-range__close-icon,.el-range-editor--medium .el-range__icon{line-height:28px}.el-range-editor--small.el-input__inner{height:32px}.el-range-editor--small .el-range-separator{line-height:24px;font-size:13px}.el-range-editor--small .el-range-input{font-size:13px}.el-range-editor--small .el-range__close-icon,.el-range-editor--small .el-range__icon{line-height:24px}.el-range-editor--mini.el-input__inner{height:28px}.el-range-editor--mini .el-range-separator{line-height:20px;font-size:12px}.el-range-editor--mini .el-range-input{font-size:12px}.el-range-editor--mini .el-range__close-icon,.el-range-editor--mini .el-range__icon{line-height:20px}.el-range-editor.is-disabled{background-color:#F5F7FA;border-color:#E4E7ED;color:#C0C4CC;cursor:not-allowed}.el-range-editor.is-disabled:focus,.el-range-editor.is-disabled:hover{border-color:#E4E7ED}.el-range-editor.is-disabled input{background-color:#F5F7FA;color:#C0C4CC;cursor:not-allowed}.el-range-editor.is-disabled input::-webkit-input-placeholder{color:#C0C4CC}.el-range-editor.is-disabled input::-ms-input-placeholder{color:#C0C4CC}.el-range-editor.is-disabled input::placeholder{color:#C0C4CC}.el-range-editor.is-disabled .el-range-separator{color:#C0C4CC}.el-picker-panel{color:#606266;border:1px solid #E4E7ED;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);background:#FFF;border-radius:4px;line-height:30px;margin:5px 0}.el-popover,.el-time-panel{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-picker-panel__body-wrapper::after,.el-picker-panel__body::after{content:"";display:table;clear:both}.el-picker-panel__content{position:relative;margin:15px}.el-picker-panel__footer{border-top:1px solid #e4e4e4;padding:4px;text-align:right;background-color:#FFF;position:relative;font-size:0}.el-picker-panel__shortcut{display:block;width:100%;border:0;background-color:transparent;line-height:28px;font-size:14px;color:#606266;padding-left:12px;text-align:left;outline:0;cursor:pointer}.el-picker-panel__shortcut:hover{color:#409EFF}.el-picker-panel__shortcut.active{background-color:#e6f1fe;color:#409EFF}.el-picker-panel__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-picker-panel__btn[disabled]{color:#ccc;cursor:not-allowed}.el-picker-panel__icon-btn{font-size:12px;color:#303133;border:0;background:0 0;cursor:pointer;outline:0;margin-top:8px}.el-picker-panel__icon-btn:hover{color:#409EFF}.el-picker-panel__icon-btn.is-disabled{color:#bbb}.el-picker-panel__icon-btn.is-disabled:hover{cursor:not-allowed}.el-picker-panel__link-btn{vertical-align:middle}.el-picker-panel [slot=sidebar],.el-picker-panel__sidebar{position:absolute;top:0;bottom:0;width:110px;border-right:1px solid #e4e4e4;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;background-color:#FFF;overflow:auto}.el-picker-panel [slot=sidebar]+.el-picker-panel__body,.el-picker-panel__sidebar+.el-picker-panel__body{margin-left:110px}.el-time-spinner.has-seconds .el-time-spinner__wrapper{width:33.3%}.el-time-spinner__wrapper{max-height:190px;overflow:auto;display:inline-block;width:50%;vertical-align:top;position:relative}.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default){padding-bottom:15px}.el-time-spinner__input.el-input .el-input__inner,.el-time-spinner__list{padding:0;text-align:center}.el-time-spinner__wrapper.is-arrow{-webkit-box-sizing:border-box;box-sizing:border-box;text-align:center;overflow:hidden}.el-time-spinner__wrapper.is-arrow .el-time-spinner__list{-webkit-transform:translateY(-32px);transform:translateY(-32px)}.el-time-spinner__wrapper.is-arrow .el-time-spinner__item:hover:not(.disabled):not(.active){background:#FFF;cursor:default}.el-time-spinner__arrow{font-size:12px;color:#909399;position:absolute;left:0;width:100%;z-index:1;text-align:center;height:30px;line-height:30px;cursor:pointer}.el-time-spinner__arrow:hover{color:#409EFF}.el-time-spinner__arrow.el-icon-arrow-up{top:10px}.el-time-spinner__arrow.el-icon-arrow-down{bottom:10px}.el-time-spinner__input.el-input{width:70%}.el-time-spinner__list{margin:0;list-style:none}.el-time-spinner__list::after,.el-time-spinner__list::before{content:'';display:block;width:100%;height:80px}.el-time-spinner__item{height:32px;line-height:32px;font-size:12px;color:#606266}.el-time-spinner__item:hover:not(.disabled):not(.active){background:#F5F7FA;cursor:pointer}.el-time-spinner__item.active:not(.disabled){color:#303133;font-weight:700}.el-time-spinner__item.disabled{color:#C0C4CC;cursor:not-allowed}.el-time-panel{margin:5px 0;border:1px solid #E4E7ED;background-color:#FFF;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);border-radius:2px;position:absolute;width:180px;left:0;z-index:1000;-moz-user-select:none;user-select:none;-webkit-box-sizing:content-box;box-sizing:content-box}.el-time-panel__content{font-size:0;position:relative;overflow:hidden}.el-time-panel__content::after,.el-time-panel__content::before{content:"";top:50%;position:absolute;margin-top:-15px;height:32px;z-index:-1;left:0;right:0;-webkit-box-sizing:border-box;box-sizing:border-box;padding-top:6px;text-align:left;border-top:1px solid #E4E7ED;border-bottom:1px solid #E4E7ED}.el-time-panel__content::after{left:50%;margin-left:12%;margin-right:12%}.el-time-panel__content::before{padding-left:50%;margin-right:12%;margin-left:12%}.el-time-panel__content.has-seconds::after{left:calc(100% / 3 * 2)}.el-time-panel__content.has-seconds::before{padding-left:calc(100% / 3)}.el-time-panel__footer{border-top:1px solid #e4e4e4;padding:4px;height:36px;line-height:25px;text-align:right;-webkit-box-sizing:border-box;box-sizing:border-box}.el-time-panel__btn{border:none;line-height:28px;padding:0 5px;margin:0 5px;cursor:pointer;background-color:transparent;outline:0;font-size:12px;color:#303133}.el-time-panel__btn.confirm{font-weight:800;color:#409EFF}.el-time-range-picker{width:354px;overflow:visible}.el-time-range-picker__content{position:relative;text-align:center;padding:10px}.el-time-range-picker__cell{-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:4px 7px 7px;width:50%;display:inline-block}.el-time-range-picker__header{margin-bottom:5px;text-align:center;font-size:14px}.el-time-range-picker__body{border-radius:2px;border:1px solid #E4E7ED}.el-popover{position:absolute;background:#FFF;min-width:150px;border:1px solid #EBEEF5;padding:12px;z-index:2000;color:#606266;line-height:1.4;text-align:justify;font-size:14px;box-shadow:0 2px 12px 0 rgba(0,0,0,.1);word-break:break-all}.el-popover--plain{padding:18px 20px}.el-popover__title{color:#303133;font-size:16px;line-height:1;margin-bottom:12px}.v-modal-enter{-webkit-animation:v-modal-in .2s ease;animation:v-modal-in .2s ease}.v-modal-leave{-webkit-animation:v-modal-out .2s ease forwards;animation:v-modal-out .2s ease forwards}@keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-out{100%{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:.5;background:#000}.el-popup-parent--hidden{overflow:hidden}.el-message-box{display:inline-block;width:420px;padding-bottom:10px;vertical-align:middle;background-color:#FFF;border-radius:4px;border:1px solid #EBEEF5;font-size:18px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);text-align:left;overflow:hidden;-webkit-backface-visibility:hidden;backface-visibility:hidden}.el-message-box__wrapper{position:fixed;top:0;bottom:0;left:0;right:0;text-align:center}.el-message-box__wrapper::after{content:"";display:inline-block;height:100%;width:0;vertical-align:middle}.el-message-box__header{position:relative;padding:15px 15px 10px}.el-message-box__title{padding-left:0;margin-bottom:0;font-size:18px;line-height:1;color:#303133}.el-message-box__headerbtn{position:absolute;top:15px;right:15px;padding:0;border:none;outline:0;background:0 0;font-size:16px;cursor:pointer}.el-form-item.is-error .el-input__inner,.el-form-item.is-error .el-input__inner:focus,.el-form-item.is-error .el-textarea__inner,.el-form-item.is-error .el-textarea__inner:focus,.el-message-box__input input.invalid,.el-message-box__input input.invalid:focus{border-color:#F56C6C}.el-message-box__headerbtn .el-message-box__close{color:#909399}.el-message-box__headerbtn:focus .el-message-box__close,.el-message-box__headerbtn:hover .el-message-box__close{color:#409EFF}.el-message-box__content{position:relative;padding:10px 15px;color:#606266;font-size:14px}.el-message-box__input{padding-top:15px}.el-message-box__status{position:absolute;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);font-size:24px!important}.el-message-box__status::before{padding-left:1px}.el-message-box__status+.el-message-box__message{padding-left:36px;padding-right:12px}.el-message-box__status.el-icon-success{color:#67C23A}.el-message-box__status.el-icon-info{color:#909399}.el-message-box__status.el-icon-warning{color:#E6A23C}.el-message-box__status.el-icon-error{color:#F56C6C}.el-message-box__message{margin:0}.el-message-box__message p{margin:0;line-height:24px}.el-message-box__errormsg{color:#F56C6C;font-size:12px;min-height:18px;margin-top:2px}.el-message-box__btns{padding:5px 15px 0;text-align:right}.el-message-box__btns button:nth-child(2){margin-left:10px}.el-message-box__btns-reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.el-container,.el-container.is-vertical,.el-drawer,.el-link,.el-steps--vertical{-webkit-box-direction:normal}.el-message-box--center{padding-bottom:30px}.el-message-box--center .el-message-box__header{padding-top:30px}.el-message-box--center .el-message-box__title{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message-box--center .el-message-box__status{position:relative;top:auto;padding-right:5px;text-align:center;-webkit-transform:translateY(-1px);transform:translateY(-1px)}.el-message-box--center .el-message-box__message{margin-left:0}.el-message-box--center .el-message-box__btns,.el-message-box--center .el-message-box__content{text-align:center}.el-message-box--center .el-message-box__content{padding-left:27px;padding-right:27px}.msgbox-fade-enter-active{-webkit-animation:msgbox-fade-in .3s;animation:msgbox-fade-in .3s}.msgbox-fade-leave-active{-webkit-animation:msgbox-fade-out .3s;animation:msgbox-fade-out .3s}@-webkit-keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes msgbox-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes msgbox-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes msgbox-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-breadcrumb{font-size:14px;line-height:1}.el-breadcrumb::after,.el-breadcrumb::before{display:table;content:""}.el-breadcrumb::after{clear:both}.el-breadcrumb__separator{margin:0 9px;font-weight:700;color:#C0C4CC}.el-breadcrumb__separator[class*=icon]{margin:0 6px;font-weight:400}.el-breadcrumb__item{float:left}.el-breadcrumb__inner{color:#606266}.el-breadcrumb__inner a,.el-breadcrumb__inner.is-link{font-weight:700;text-decoration:none;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1);color:#303133}.el-breadcrumb__inner a:hover,.el-breadcrumb__inner.is-link:hover{color:#409EFF;cursor:pointer}.el-breadcrumb__item:last-child .el-breadcrumb__inner,.el-breadcrumb__item:last-child .el-breadcrumb__inner a,.el-breadcrumb__item:last-child .el-breadcrumb__inner a:hover,.el-breadcrumb__item:last-child .el-breadcrumb__inner:hover{font-weight:400;color:#606266;cursor:text}.el-breadcrumb__item:last-child .el-breadcrumb__separator{display:none}.el-form--label-left .el-form-item__label{text-align:left}.el-form--label-top .el-form-item__label{float:none;display:inline-block;text-align:left;padding:0 0 10px}.el-form--inline .el-form-item{display:inline-block;margin-right:10px;vertical-align:top}.el-form--inline .el-form-item__label{float:none;display:inline-block}.el-form--inline .el-form-item__content{display:inline-block;vertical-align:top}.el-form--inline.el-form--label-top .el-form-item__content{display:block}.el-form-item{margin-bottom:22px}.el-form-item::after,.el-form-item::before{display:table;content:""}.el-form-item::after{clear:both}.el-form-item .el-form-item{margin-bottom:0}.el-form-item--mini.el-form-item,.el-form-item--small.el-form-item{margin-bottom:18px}.el-form-item .el-input__validateIcon{display:none}.el-form-item--medium .el-form-item__content,.el-form-item--medium .el-form-item__label{line-height:36px}.el-form-item--small .el-form-item__content,.el-form-item--small .el-form-item__label{line-height:32px}.el-form-item--small .el-form-item__error{padding-top:2px}.el-form-item--mini .el-form-item__content,.el-form-item--mini .el-form-item__label{line-height:28px}.el-form-item--mini .el-form-item__error{padding-top:1px}.el-form-item__label-wrap{float:left}.el-form-item__label-wrap .el-form-item__label{display:inline-block;float:none}.el-form-item__label{text-align:right;vertical-align:middle;float:left;font-size:14px;color:#606266;line-height:40px;padding:0 12px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-form-item__content{line-height:40px;position:relative;font-size:14px}.el-form-item__content::after,.el-form-item__content::before{display:table;content:""}.el-form-item__content::after{clear:both}.el-form-item__content .el-input-group{vertical-align:top}.el-form-item__error{color:#F56C6C;font-size:12px;line-height:1;padding-top:4px;position:absolute;top:100%;left:0}.el-form-item__error--inline{position:relative;top:auto;left:auto;display:inline-block;margin-left:10px}.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap>.el-form-item__label:before,.el-form-item.is-required:not(.is-no-asterisk)>.el-form-item__label:before{content:'*';color:#F56C6C;margin-right:4px}.el-form-item.is-error .el-input-group__append .el-input__inner,.el-form-item.is-error .el-input-group__prepend .el-input__inner{border-color:transparent}.el-form-item.is-error .el-input__validateIcon{color:#F56C6C}.el-form-item--feedback .el-input__validateIcon{display:inline-block}.el-tabs__header{padding:0;position:relative;margin:0 0 15px}.el-tabs__active-bar{position:absolute;bottom:0;left:0;height:2px;background-color:#409EFF;z-index:1;-webkit-transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:-webkit-transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1);transition:transform .3s cubic-bezier(.645,.045,.355,1),-webkit-transform .3s cubic-bezier(.645,.045,.355,1);list-style:none}.el-tabs__new-tab{float:right;border:1px solid #d3dce6;height:18px;width:18px;line-height:18px;margin:12px 0 9px 10px;border-radius:3px;text-align:center;font-size:12px;color:#d3dce6;cursor:pointer;-webkit-transition:all .15s;transition:all .15s}.el-collapse-item__arrow,.el-tabs__nav{-webkit-transition:-webkit-transform .3s}.el-tabs__new-tab .el-icon-plus{-webkit-transform:scale(.8,.8);transform:scale(.8,.8)}.el-tabs__new-tab:hover{color:#409EFF}.el-tabs__nav-wrap{overflow:hidden;margin-bottom:-1px;position:relative}.el-tabs__nav-wrap::after{content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;background-color:#E4E7ED;z-index:1}.el-tabs--border-card>.el-tabs__header .el-tabs__nav-wrap::after,.el-tabs--card>.el-tabs__header .el-tabs__nav-wrap::after{content:none}.el-tabs__nav-wrap.is-scrollable{padding:0 20px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs__nav-scroll{overflow:hidden}.el-tabs__nav-next,.el-tabs__nav-prev{position:absolute;cursor:pointer;line-height:44px;font-size:12px;color:#909399}.el-tabs__nav-next{right:0}.el-tabs__nav-prev{left:0}.el-tabs__nav{white-space:nowrap;position:relative;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;float:left;z-index:2}.el-tabs__nav.is-stretch{min-width:100%;display:-webkit-box;display:-ms-flexbox;display:flex}.el-tabs__nav.is-stretch>*{-webkit-box-flex:1;-ms-flex:1;flex:1;text-align:center}.el-tabs__item{padding:0 20px;height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:40px;display:inline-block;list-style:none;font-size:14px;font-weight:500;color:#303133;position:relative}.el-tabs__item:focus,.el-tabs__item:focus:active{outline:0}.el-tabs__item:focus.is-active.is-focus:not(:active){-webkit-box-shadow:0 0 2px 2px #409EFF inset;box-shadow:0 0 2px 2px #409EFF inset;border-radius:3px}.el-tabs__item .el-icon-close{border-radius:50%;text-align:center;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);margin-left:5px}.el-tabs__item .el-icon-close:before{-webkit-transform:scale(.9);transform:scale(.9);display:inline-block}.el-tabs__item .el-icon-close:hover{background-color:#C0C4CC;color:#FFF}.el-tabs__item.is-active{color:#409EFF}.el-tabs__item:hover{color:#409EFF;cursor:pointer}.el-tabs__item.is-disabled{color:#C0C4CC;cursor:default}.el-tabs__content{overflow:hidden;position:relative}.el-tabs--card>.el-tabs__header{border-bottom:1px solid #E4E7ED}.el-tabs--card>.el-tabs__header .el-tabs__nav{border:1px solid #E4E7ED;border-bottom:none;border-radius:4px 4px 0 0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-tabs--card>.el-tabs__header .el-tabs__active-bar{display:none}.el-tabs--card>.el-tabs__header .el-tabs__item .el-icon-close{position:relative;font-size:12px;width:0;height:14px;vertical-align:middle;line-height:15px;overflow:hidden;top:-1px;right:-2px;-webkit-transform-origin:100% 50%;transform-origin:100% 50%}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable .el-icon-close,.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover .el-icon-close{width:14px}.el-tabs--card>.el-tabs__header .el-tabs__item{border-bottom:1px solid transparent;border-left:1px solid #E4E7ED;-webkit-transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1);transition:color .3s cubic-bezier(.645,.045,.355,1),padding .3s cubic-bezier(.645,.045,.355,1)}.el-tabs--card>.el-tabs__header .el-tabs__item:first-child{border-left:none}.el-tabs--card>.el-tabs__header .el-tabs__item.is-closable:hover{padding-left:13px;padding-right:13px}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:#FFF}.el-tabs--card>.el-tabs__header .el-tabs__item.is-active.is-closable{padding-left:20px;padding-right:20px}.el-tabs--border-card{background:#FFF;border:1px solid #DCDFE6;-webkit-box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04);box-shadow:0 2px 4px 0 rgba(0,0,0,.12),0 0 6px 0 rgba(0,0,0,.04)}.el-tabs--border-card>.el-tabs__content{padding:15px}.el-tabs--border-card>.el-tabs__header{background-color:#F5F7FA;border-bottom:1px solid #E4E7ED;margin:0}.el-tabs--border-card>.el-tabs__header .el-tabs__item{-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);border:1px solid transparent;margin-top:-1px;color:#909399}.el-tabs--border-card>.el-tabs__header .el-tabs__item+.el-tabs__item,.el-tabs--border-card>.el-tabs__header .el-tabs__item:first-child{margin-left:-1px}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active{color:#409EFF;background-color:#FFF;border-right-color:#DCDFE6;border-left-color:#DCDFE6}.el-tabs--border-card>.el-tabs__header .el-tabs__item:not(.is-disabled):hover{color:#409EFF}.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-disabled{color:#C0C4CC}.el-tabs--border-card>.el-tabs__header .is-scrollable .el-tabs__item:first-child{margin-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:nth-child(2),.el-tabs--bottom .el-tabs__item.is-top:nth-child(2),.el-tabs--top .el-tabs__item.is-bottom:nth-child(2),.el-tabs--top .el-tabs__item.is-top:nth-child(2){padding-left:0}.el-tabs--bottom .el-tabs__item.is-bottom:last-child,.el-tabs--bottom .el-tabs__item.is-top:last-child,.el-tabs--top .el-tabs__item.is-bottom:last-child,.el-tabs--top .el-tabs__item.is-top:last-child{padding-right:0}.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:nth-child(2),.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:nth-child(2){padding-left:20px}.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child,.el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:last-child{padding-right:20px}.el-tabs--bottom .el-tabs__header.is-bottom{margin-bottom:0;margin-top:10px}.el-tabs--bottom.el-tabs--border-card .el-tabs__header.is-bottom{border-bottom:0;border-top:1px solid #DCDFE6}.el-tabs--bottom.el-tabs--border-card .el-tabs__nav-wrap.is-bottom{margin-top:-1px;margin-bottom:0}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom:not(.is-active){border:1px solid transparent}.el-tabs--bottom.el-tabs--border-card .el-tabs__item.is-bottom{margin:0 -1px -1px}.el-tabs--left,.el-tabs--right{overflow:hidden}.el-tabs--left .el-tabs__header.is-left,.el-tabs--left .el-tabs__header.is-right,.el-tabs--left .el-tabs__nav-scroll,.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__header.is-left,.el-tabs--right .el-tabs__header.is-right,.el-tabs--right .el-tabs__nav-scroll,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{height:100%}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__active-bar.is-right,.el-tabs--right .el-tabs__active-bar.is-left,.el-tabs--right .el-tabs__active-bar.is-right{top:0;bottom:auto;width:2px;height:auto}.el-tabs--left .el-tabs__nav-wrap.is-left,.el-tabs--left .el-tabs__nav-wrap.is-right,.el-tabs--right .el-tabs__nav-wrap.is-left,.el-tabs--right .el-tabs__nav-wrap.is-right{margin-bottom:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{height:30px;line-height:30px;width:100%;text-align:center;cursor:pointer}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next i,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev i{-webkit-transform:rotateZ(90deg);transform:rotateZ(90deg)}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-prev,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-prev{left:auto;top:0}.el-tabs--left .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--left .el-tabs__nav-wrap.is-right>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-left>.el-tabs__nav-next,.el-tabs--right .el-tabs__nav-wrap.is-right>.el-tabs__nav-next{right:auto;bottom:0}.el-tabs--left .el-tabs__active-bar.is-left,.el-tabs--left .el-tabs__nav-wrap.is-left::after{right:0;left:auto}.el-tabs--left .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--left .el-tabs__nav-wrap.is-right.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-left.is-scrollable,.el-tabs--right .el-tabs__nav-wrap.is-right.is-scrollable{padding:30px 0}.el-tabs--left .el-tabs__nav-wrap.is-left::after,.el-tabs--left .el-tabs__nav-wrap.is-right::after,.el-tabs--right .el-tabs__nav-wrap.is-left::after,.el-tabs--right .el-tabs__nav-wrap.is-right::after{height:100%;width:2px;bottom:auto;top:0}.el-tabs--left .el-tabs__nav.is-left,.el-tabs--left .el-tabs__nav.is-right,.el-tabs--right .el-tabs__nav.is-left,.el-tabs--right .el-tabs__nav.is-right{float:none}.el-tabs--left .el-tabs__item.is-left,.el-tabs--left .el-tabs__item.is-right,.el-tabs--right .el-tabs__item.is-left,.el-tabs--right .el-tabs__item.is-right{display:block}.el-tabs--left.el-tabs--card .el-tabs__active-bar.is-left,.el-tabs--right.el-tabs--card .el-tabs__active-bar.is-right{display:none}.el-tabs--left .el-tabs__header.is-left{float:left;margin-bottom:0;margin-right:10px}.el-tabs--left .el-tabs__nav-wrap.is-left{margin-right:-1px}.el-tabs--left .el-tabs__item.is-left{text-align:right}.el-tabs--left.el-tabs--card .el-tabs__item.is-left{border-left:none;border-right:1px solid #E4E7ED;border-bottom:none;border-top:1px solid #E4E7ED;text-align:left}.el-tabs--left.el-tabs--card .el-tabs__item.is-left:first-child{border-right:1px solid #E4E7ED;border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active{border:1px solid #E4E7ED;border-right-color:#fff;border-left:none;border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:first-child{border-top:none}.el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active:last-child{border-bottom:none}.el-tabs--left.el-tabs--card .el-tabs__nav{border-radius:4px 0 0 4px;border-bottom:1px solid #E4E7ED;border-right:none}.el-tabs--left.el-tabs--card .el-tabs__new-tab{float:none}.el-tabs--left.el-tabs--border-card .el-tabs__header.is-left{border-right:1px solid #dfe4ed}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left{border:1px solid transparent;margin:-1px 0 -1px -1px}.el-tabs--left.el-tabs--border-card .el-tabs__item.is-left.is-active{border-color:#d1dbe5 transparent}.el-tabs--right .el-tabs__header.is-right{float:right;margin-bottom:0;margin-left:10px}.el-tabs--right .el-tabs__nav-wrap.is-right{margin-left:-1px}.el-tabs--right .el-tabs__nav-wrap.is-right::after{left:0;right:auto}.el-tabs--right .el-tabs__active-bar.is-right{left:0}.el-tabs--right.el-tabs--card .el-tabs__item.is-right{border-bottom:none;border-top:1px solid #E4E7ED}.el-tabs--right.el-tabs--card .el-tabs__item.is-right:first-child{border-left:1px solid #E4E7ED;border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active{border:1px solid #E4E7ED;border-left-color:#fff;border-right:none;border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:first-child{border-top:none}.el-tabs--right.el-tabs--card .el-tabs__item.is-right.is-active:last-child{border-bottom:none}.el-tabs--right.el-tabs--card .el-tabs__nav{border-radius:0 4px 4px 0;border-bottom:1px solid #E4E7ED;border-left:none}.el-tabs--right.el-tabs--border-card .el-tabs__header.is-right{border-left:1px solid #dfe4ed}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right{border:1px solid transparent;margin:-1px -1px -1px 0}.el-tabs--right.el-tabs--border-card .el-tabs__item.is-right.is-active{border-color:#d1dbe5 transparent}.slideInLeft-transition,.slideInRight-transition{display:inline-block}.slideInRight-enter{-webkit-animation:slideInRight-enter .3s;animation:slideInRight-enter .3s}.slideInRight-leave{position:absolute;left:0;right:0;-webkit-animation:slideInRight-leave .3s;animation:slideInRight-leave .3s}.slideInLeft-enter{-webkit-animation:slideInLeft-enter .3s;animation:slideInLeft-enter .3s}.slideInLeft-leave{position:absolute;left:0;right:0;-webkit-animation:slideInLeft-leave .3s;animation:slideInLeft-leave .3s}@-webkit-keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInRight-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@keyframes slideInRight-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(100%);transform:translateX(100%);opacity:0}}@-webkit-keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideInLeft-enter{0%{opacity:0;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}to{opacity:1;-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}@keyframes slideInLeft-leave{0%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(0);transform:translateX(0);opacity:1}100%{-webkit-transform-origin:0 0;transform-origin:0 0;-webkit-transform:translateX(-100%);transform:translateX(-100%);opacity:0}}.el-tree{position:relative;cursor:default;background:#FFF;color:#606266}.el-tree__empty-block{position:relative;min-height:60px;text-align:center;width:100%;height:100%}.el-tree__empty-text{position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#909399}.el-tree__drop-indicator{position:absolute;left:0;right:0;height:1px;background-color:#409EFF}.el-tree-node{white-space:nowrap;outline:0}.el-tree-node:focus>.el-tree-node__content{background-color:#F5F7FA}.el-tree-node.is-drop-inner>.el-tree-node__content .el-tree-node__label{background-color:#409EFF;color:#fff}.el-tree-node__content{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:26px;cursor:pointer}.el-tree-node__content>.el-tree-node__expand-icon{padding:6px}.el-tree-node__content>label.el-checkbox{margin-right:8px}.el-tree-node__content:hover{background-color:#F5F7FA}.el-tree.is-dragging .el-tree-node__content{cursor:move}.el-tree.is-dragging.is-drop-not-allow .el-tree-node__content{cursor:not-allowed}.el-tree-node__expand-icon{cursor:pointer;color:#C0C4CC;font-size:12px;-webkit-transform:rotate(0);transform:rotate(0);-webkit-transition:-webkit-transform .3s ease-in-out;transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out}.el-tree-node__expand-icon.expanded{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-tree-node__expand-icon.is-leaf{color:transparent;cursor:default}.el-tree-node__label{font-size:14px}.el-tree-node__loading-icon{margin-right:8px;font-size:14px;color:#C0C4CC}.el-tree-node>.el-tree-node__children{overflow:hidden;background-color:transparent}.el-tree-node.is-expanded>.el-tree-node__children{display:block}.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content{background-color:#f0f7ff}.el-alert{width:100%;padding:8px 16px;margin:0;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;position:relative;background-color:#FFF;overflow:hidden;opacity:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:opacity .2s;transition:opacity .2s}.el-alert.is-light .el-alert__closebtn{color:#C0C4CC}.el-alert.is-dark .el-alert__closebtn,.el-alert.is-dark .el-alert__description{color:#FFF}.el-alert.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-alert--success.is-light{background-color:#f0f9eb;color:#67C23A}.el-alert--success.is-light .el-alert__description{color:#67C23A}.el-alert--success.is-dark{background-color:#67C23A;color:#FFF}.el-alert--info.is-light{background-color:#f4f4f5;color:#909399}.el-alert--info.is-dark{background-color:#909399;color:#FFF}.el-alert--info .el-alert__description{color:#909399}.el-alert--warning.is-light{background-color:#fdf6ec;color:#E6A23C}.el-alert--warning.is-light .el-alert__description{color:#E6A23C}.el-alert--warning.is-dark{background-color:#E6A23C;color:#FFF}.el-alert--error.is-light{background-color:#fef0f0;color:#F56C6C}.el-alert--error.is-light .el-alert__description{color:#F56C6C}.el-alert--error.is-dark{background-color:#F56C6C;color:#FFF}.el-alert__content{display:table-cell;padding:0 8px}.el-alert__icon{font-size:16px;width:16px}.el-alert__icon.is-big{font-size:28px;width:28px}.el-alert__title{font-size:13px;line-height:18px}.el-alert__title.is-bold{font-weight:700}.el-alert .el-alert__description{font-size:12px;margin:5px 0 0}.el-alert__closebtn{font-size:12px;opacity:1;position:absolute;top:12px;right:15px;cursor:pointer}.el-alert-fade-enter,.el-alert-fade-leave-active,.el-loading-fade-enter,.el-loading-fade-leave-active,.el-notification-fade-leave-active{opacity:0}.el-alert__closebtn.is-customed{font-style:normal;font-size:13px;top:9px}.el-notification{display:-webkit-box;display:-ms-flexbox;display:flex;width:330px;padding:14px 26px 14px 13px;border-radius:8px;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #EBEEF5;position:fixed;background-color:#FFF;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s;transition:opacity .3s,transform .3s,left .3s,right .3s,top .4s,bottom .3s,-webkit-transform .3s;overflow:hidden}.el-notification.right{right:16px}.el-notification.left{left:16px}.el-notification__group{margin-left:13px;margin-right:8px}.el-notification__title{font-weight:700;font-size:16px;color:#303133;margin:0}.el-notification__content{font-size:14px;line-height:21px;margin:6px 0 0;color:#606266;text-align:justify}.el-notification__content p{margin:0}.el-notification__icon{height:24px;width:24px;font-size:24px}.el-notification__closeBtn{position:absolute;top:18px;right:15px;cursor:pointer;color:#909399;font-size:16px}.el-notification__closeBtn:hover{color:#606266}.el-notification .el-icon-success{color:#67C23A}.el-notification .el-icon-error{color:#F56C6C}.el-notification .el-icon-info{color:#909399}.el-notification .el-icon-warning{color:#E6A23C}.el-notification-fade-enter.right{right:0;-webkit-transform:translateX(100%);transform:translateX(100%)}.el-notification-fade-enter.left{left:0;-webkit-transform:translateX(-100%);transform:translateX(-100%)}.el-input-number{position:relative;display:inline-block;width:180px;line-height:38px}.el-input-number .el-input{display:block}.el-input-number .el-input__inner{-webkit-appearance:none;padding-left:50px;padding-right:50px;text-align:center}.el-input-number__decrease,.el-input-number__increase{position:absolute;z-index:1;top:1px;width:40px;height:auto;text-align:center;background:#F5F7FA;color:#606266;cursor:pointer;font-size:13px}.el-input-number__decrease:hover,.el-input-number__increase:hover{color:#409EFF}.el-input-number__decrease:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled),.el-input-number__increase:hover:not(.is-disabled)~.el-input .el-input__inner:not(.is-disabled){border-color:#409EFF}.el-input-number__decrease.is-disabled,.el-input-number__increase.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-input-number__increase{right:1px;border-radius:0 4px 4px 0;border-left:1px solid #DCDFE6}.el-input-number__decrease{left:1px;border-radius:4px 0 0 4px;border-right:1px solid #DCDFE6}.el-input-number.is-disabled .el-input-number__decrease,.el-input-number.is-disabled .el-input-number__increase{border-color:#E4E7ED;color:#E4E7ED}.el-input-number.is-disabled .el-input-number__decrease:hover,.el-input-number.is-disabled .el-input-number__increase:hover{color:#E4E7ED;cursor:not-allowed}.el-input-number--medium{width:200px;line-height:34px}.el-input-number--medium .el-input-number__decrease,.el-input-number--medium .el-input-number__increase{width:36px;font-size:14px}.el-input-number--medium .el-input__inner{padding-left:43px;padding-right:43px}.el-input-number--small{width:130px;line-height:30px}.el-input-number--small .el-input-number__decrease,.el-input-number--small .el-input-number__increase{width:32px;font-size:13px}.el-input-number--small .el-input-number__decrease [class*=el-icon],.el-input-number--small .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.9);transform:scale(.9)}.el-input-number--small .el-input__inner{padding-left:39px;padding-right:39px}.el-input-number--mini{width:130px;line-height:26px}.el-input-number--mini .el-input-number__decrease,.el-input-number--mini .el-input-number__increase{width:28px;font-size:12px}.el-input-number--mini .el-input-number__decrease [class*=el-icon],.el-input-number--mini .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number--mini .el-input__inner{padding-left:35px;padding-right:35px}.el-input-number.is-without-controls .el-input__inner{padding-left:15px;padding-right:15px}.el-input-number.is-controls-right .el-input__inner{padding-left:15px;padding-right:50px}.el-input-number.is-controls-right .el-input-number__decrease,.el-input-number.is-controls-right .el-input-number__increase{height:auto;line-height:19px}.el-input-number.is-controls-right .el-input-number__decrease [class*=el-icon],.el-input-number.is-controls-right .el-input-number__increase [class*=el-icon]{-webkit-transform:scale(.8);transform:scale(.8)}.el-input-number.is-controls-right .el-input-number__increase{border-radius:0 4px 0 0;border-bottom:1px solid #DCDFE6}.el-input-number.is-controls-right .el-input-number__decrease{right:1px;bottom:1px;top:auto;left:auto;border-right:none;border-left:1px solid #DCDFE6;border-radius:0 0 4px}.el-input-number.is-controls-right[class*=medium] [class*=decrease],.el-input-number.is-controls-right[class*=medium] [class*=increase]{line-height:17px}.el-input-number.is-controls-right[class*=small] [class*=decrease],.el-input-number.is-controls-right[class*=small] [class*=increase]{line-height:15px}.el-input-number.is-controls-right[class*=mini] [class*=decrease],.el-input-number.is-controls-right[class*=mini] [class*=increase]{line-height:13px}.el-tooltip__popper{position:absolute;border-radius:4px;padding:10px;z-index:2000;font-size:12px;line-height:1.2;min-width:10px;word-wrap:break-word}.el-tooltip__popper .popper__arrow,.el-tooltip__popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-tooltip__popper .popper__arrow{border-width:6px}.el-tooltip__popper .popper__arrow::after{content:" ";border-width:5px}.el-progress-bar__inner::after,.el-row::after,.el-row::before,.el-slider::after,.el-slider::before,.el-slider__button-wrapper::after,.el-upload-cover::after{content:""}.el-tooltip__popper[x-placement^=top]{margin-bottom:12px}.el-tooltip__popper[x-placement^=top] .popper__arrow{bottom:-6px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-5px;border-top-color:#303133;border-bottom-width:0}.el-tooltip__popper[x-placement^=bottom]{margin-top:12px}.el-tooltip__popper[x-placement^=bottom] .popper__arrow{top:-6px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-5px;border-top-width:0;border-bottom-color:#303133}.el-tooltip__popper[x-placement^=right]{margin-left:12px}.el-tooltip__popper[x-placement^=right] .popper__arrow{left:-6px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=right] .popper__arrow::after{bottom:-5px;left:1px;border-right-color:#303133;border-left-width:0}.el-tooltip__popper[x-placement^=left]{margin-right:12px}.el-tooltip__popper[x-placement^=left] .popper__arrow{right:-6px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-5px;margin-left:-5px;border-right-width:0;border-left-color:#303133}.el-tooltip__popper.is-dark{background:#303133;color:#FFF}.el-tooltip__popper.is-light{background:#FFF;border:1px solid #303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow{border-top-color:#303133}.el-tooltip__popper.is-light[x-placement^=top] .popper__arrow::after{border-top-color:#FFF}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow{border-bottom-color:#303133}.el-tooltip__popper.is-light[x-placement^=bottom] .popper__arrow::after{border-bottom-color:#FFF}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow{border-left-color:#303133}.el-tooltip__popper.is-light[x-placement^=left] .popper__arrow::after{border-left-color:#FFF}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow{border-right-color:#303133}.el-tooltip__popper.is-light[x-placement^=right] .popper__arrow::after{border-right-color:#FFF}.el-slider::after,.el-slider::before{display:table}.el-slider__button-wrapper .el-tooltip,.el-slider__button-wrapper::after{vertical-align:middle;display:inline-block}.el-slider::after{clear:both}.el-slider__runway{width:100%;height:6px;margin:16px 0;background-color:#E4E7ED;border-radius:3px;position:relative;cursor:pointer;vertical-align:middle}.el-slider__runway.show-input{margin-right:160px;width:auto}.el-slider__runway.disabled{cursor:default}.el-slider__runway.disabled .el-slider__bar{background-color:#C0C4CC}.el-slider__runway.disabled .el-slider__button{border-color:#C0C4CC}.el-slider__runway.disabled .el-slider__button-wrapper.dragging,.el-slider__runway.disabled .el-slider__button-wrapper.hover,.el-slider__runway.disabled .el-slider__button-wrapper:hover{cursor:not-allowed}.el-slider__runway.disabled .el-slider__button.dragging,.el-slider__runway.disabled .el-slider__button.hover,.el-slider__runway.disabled .el-slider__button:hover{-webkit-transform:scale(1);transform:scale(1);cursor:not-allowed}.el-slider__button-wrapper,.el-slider__stop{-webkit-transform:translateX(-50%);position:absolute}.el-slider__input{float:right;margin-top:3px;width:130px}.el-slider__input.el-input-number--mini{margin-top:5px}.el-slider__input.el-input-number--medium{margin-top:0}.el-slider__input.el-input-number--large{margin-top:-2px}.el-slider__bar{height:6px;background-color:#409EFF;border-top-left-radius:3px;border-bottom-left-radius:3px;position:absolute}.el-slider__button-wrapper{height:36px;width:36px;z-index:1001;top:-15px;transform:translateX(-50%);background-color:transparent;text-align:center;-moz-user-select:none;user-select:none;line-height:normal}.el-slider__button,.el-step__icon-inner{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-slider__button-wrapper::after{height:100%}.el-slider__button-wrapper.hover,.el-slider__button-wrapper:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button-wrapper.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__button{width:16px;height:16px;border:2px solid #409EFF;background-color:#FFF;border-radius:50%;-webkit-transition:.2s;transition:.2s;user-select:none}.el-slider__button.dragging,.el-slider__button.hover,.el-slider__button:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.el-slider__button.hover,.el-slider__button:hover{cursor:-webkit-grab;cursor:grab}.el-slider__button.dragging{cursor:-webkit-grabbing;cursor:grabbing}.el-slider__stop{height:6px;width:6px;border-radius:100%;background-color:#FFF;transform:translateX(-50%)}.el-slider__marks{top:0;left:12px;width:18px;height:100%}.el-slider__marks-text{position:absolute;-webkit-transform:translateX(-50%);transform:translateX(-50%);font-size:14px;color:#909399;margin-top:15px}.el-slider.is-vertical{position:relative}.el-slider.is-vertical .el-slider__runway{width:6px;height:100%;margin:0 16px}.el-slider.is-vertical .el-slider__bar{width:6px;height:auto;border-radius:0 0 3px 3px}.el-slider.is-vertical .el-slider__button-wrapper{top:auto;left:-15px;-webkit-transform:translateY(50%);transform:translateY(50%)}.el-slider.is-vertical .el-slider__stop{-webkit-transform:translateY(50%);transform:translateY(50%)}.el-slider.is-vertical.el-slider--with-input{padding-bottom:58px}.el-slider.is-vertical.el-slider--with-input .el-slider__input{overflow:visible;float:none;position:absolute;bottom:22px;width:36px;margin-top:15px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input__inner{text-align:center;padding-left:5px;padding-right:5px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{top:32px;margin-top:-1px;border:1px solid #DCDFE6;line-height:20px;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__decrease{width:18px;right:18px;border-bottom-left-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase{width:19px;border-bottom-right-radius:4px}.el-slider.is-vertical.el-slider--with-input .el-slider__input .el-input-number__increase~.el-input .el-input__inner{border-bottom-left-radius:0;border-bottom-right-radius:0}.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:hover .el-input-number__increase{border-color:#C0C4CC}.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__decrease,.el-slider.is-vertical.el-slider--with-input .el-slider__input:active .el-input-number__increase{border-color:#409EFF}.el-slider.is-vertical .el-slider__marks-text{margin-top:0;left:15px;-webkit-transform:translateY(50%);transform:translateY(50%)}.el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{position:absolute;z-index:2000;background-color:rgba(255,255,255,.9);margin:0;top:0;right:0;bottom:0;left:0;-webkit-transition:opacity .3s;transition:opacity .3s}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:-25px}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:50px;width:50px}.el-loading-spinner{top:50%;margin-top:-21px;width:100%;text-align:center;position:absolute}.el-col-pull-0,.el-col-pull-1,.el-col-pull-10,.el-col-pull-11,.el-col-pull-13,.el-col-pull-14,.el-col-pull-15,.el-col-pull-16,.el-col-pull-17,.el-col-pull-18,.el-col-pull-19,.el-col-pull-2,.el-col-pull-20,.el-col-pull-21,.el-col-pull-22,.el-col-pull-23,.el-col-pull-24,.el-col-pull-3,.el-col-pull-4,.el-col-pull-5,.el-col-pull-6,.el-col-pull-7,.el-col-pull-8,.el-col-pull-9,.el-col-push-0,.el-col-push-1,.el-col-push-10,.el-col-push-11,.el-col-push-12,.el-col-push-13,.el-col-push-14,.el-col-push-15,.el-col-push-16,.el-col-push-17,.el-col-push-18,.el-col-push-19,.el-col-push-2,.el-col-push-20,.el-col-push-21,.el-col-push-22,.el-col-push-23,.el-col-push-24,.el-col-push-3,.el-col-push-4,.el-col-push-5,.el-col-push-6,.el-col-push-7,.el-col-push-8,.el-col-push-9,.el-row{position:relative}.el-loading-spinner .el-loading-text{color:#409EFF;margin:3px 0;font-size:14px}.el-loading-spinner .circular{height:42px;width:42px;-webkit-animation:loading-rotate 2s linear infinite;animation:loading-rotate 2s linear infinite}.el-loading-spinner .path{-webkit-animation:loading-dash 1.5s ease-in-out infinite;animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:#409EFF;stroke-linecap:round}.el-loading-spinner i{color:#409EFF}@-webkit-keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}100%{stroke-dasharray:90,150;stroke-dashoffset:-120px}}.el-row{-webkit-box-sizing:border-box;box-sizing:border-box}.el-row::after,.el-row::before{display:table}.el-row::after{clear:both}.el-row--flex{display:-webkit-box;display:-ms-flexbox;display:flex}.el-col-0,.el-row--flex:after,.el-row--flex:before{display:none}.el-row--flex.is-justify-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-row--flex.is-justify-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.el-row--flex.is-justify-space-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.el-row--flex.is-justify-space-around{-ms-flex-pack:distribute;justify-content:space-around}.el-row--flex.is-align-middle{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-row--flex.is-align-bottom{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}[class*=el-col-]{float:left;-webkit-box-sizing:border-box;box-sizing:border-box}.el-upload--picture-card,.el-upload-dragger{-webkit-box-sizing:border-box;cursor:pointer}.el-col-0{width:0%}.el-col-offset-0{margin-left:0}.el-col-pull-0{right:0}.el-col-push-0{left:0}.el-col-1{width:4.16667%}.el-col-offset-1{margin-left:4.16667%}.el-col-pull-1{right:4.16667%}.el-col-push-1{left:4.16667%}.el-col-2{width:8.33333%}.el-col-offset-2{margin-left:8.33333%}.el-col-pull-2{right:8.33333%}.el-col-push-2{left:8.33333%}.el-col-3{width:12.5%}.el-col-offset-3{margin-left:12.5%}.el-col-pull-3{right:12.5%}.el-col-push-3{left:12.5%}.el-col-4{width:16.66667%}.el-col-offset-4{margin-left:16.66667%}.el-col-pull-4{right:16.66667%}.el-col-push-4{left:16.66667%}.el-col-5{width:20.83333%}.el-col-offset-5{margin-left:20.83333%}.el-col-pull-5{right:20.83333%}.el-col-push-5{left:20.83333%}.el-col-6{width:25%}.el-col-offset-6{margin-left:25%}.el-col-pull-6{right:25%}.el-col-push-6{left:25%}.el-col-7{width:29.16667%}.el-col-offset-7{margin-left:29.16667%}.el-col-pull-7{right:29.16667%}.el-col-push-7{left:29.16667%}.el-col-8{width:33.33333%}.el-col-offset-8{margin-left:33.33333%}.el-col-pull-8{right:33.33333%}.el-col-push-8{left:33.33333%}.el-col-9{width:37.5%}.el-col-offset-9{margin-left:37.5%}.el-col-pull-9{right:37.5%}.el-col-push-9{left:37.5%}.el-col-10{width:41.66667%}.el-col-offset-10{margin-left:41.66667%}.el-col-pull-10{right:41.66667%}.el-col-push-10{left:41.66667%}.el-col-11{width:45.83333%}.el-col-offset-11{margin-left:45.83333%}.el-col-pull-11{right:45.83333%}.el-col-push-11{left:45.83333%}.el-col-12{width:50%}.el-col-offset-12{margin-left:50%}.el-col-pull-12{position:relative;right:50%}.el-col-push-12{left:50%}.el-col-13{width:54.16667%}.el-col-offset-13{margin-left:54.16667%}.el-col-pull-13{right:54.16667%}.el-col-push-13{left:54.16667%}.el-col-14{width:58.33333%}.el-col-offset-14{margin-left:58.33333%}.el-col-pull-14{right:58.33333%}.el-col-push-14{left:58.33333%}.el-col-15{width:62.5%}.el-col-offset-15{margin-left:62.5%}.el-col-pull-15{right:62.5%}.el-col-push-15{left:62.5%}.el-col-16{width:66.66667%}.el-col-offset-16{margin-left:66.66667%}.el-col-pull-16{right:66.66667%}.el-col-push-16{left:66.66667%}.el-col-17{width:70.83333%}.el-col-offset-17{margin-left:70.83333%}.el-col-pull-17{right:70.83333%}.el-col-push-17{left:70.83333%}.el-col-18{width:75%}.el-col-offset-18{margin-left:75%}.el-col-pull-18{right:75%}.el-col-push-18{left:75%}.el-col-19{width:79.16667%}.el-col-offset-19{margin-left:79.16667%}.el-col-pull-19{right:79.16667%}.el-col-push-19{left:79.16667%}.el-col-20{width:83.33333%}.el-col-offset-20{margin-left:83.33333%}.el-col-pull-20{right:83.33333%}.el-col-push-20{left:83.33333%}.el-col-21{width:87.5%}.el-col-offset-21{margin-left:87.5%}.el-col-pull-21{right:87.5%}.el-col-push-21{left:87.5%}.el-col-22{width:91.66667%}.el-col-offset-22{margin-left:91.66667%}.el-col-pull-22{right:91.66667%}.el-col-push-22{left:91.66667%}.el-col-23{width:95.83333%}.el-col-offset-23{margin-left:95.83333%}.el-col-pull-23{right:95.83333%}.el-col-push-23{left:95.83333%}.el-col-24{width:100%}.el-col-offset-24{margin-left:100%}.el-col-pull-24{right:100%}.el-col-push-24{left:100%}@media only screen and (max-width:767px){.el-col-xs-0{display:none;width:0%}.el-col-xs-offset-0{margin-left:0}.el-col-xs-pull-0{position:relative;right:0}.el-col-xs-push-0{position:relative;left:0}.el-col-xs-1{width:4.16667%}.el-col-xs-offset-1{margin-left:4.16667%}.el-col-xs-pull-1{position:relative;right:4.16667%}.el-col-xs-push-1{position:relative;left:4.16667%}.el-col-xs-2{width:8.33333%}.el-col-xs-offset-2{margin-left:8.33333%}.el-col-xs-pull-2{position:relative;right:8.33333%}.el-col-xs-push-2{position:relative;left:8.33333%}.el-col-xs-3{width:12.5%}.el-col-xs-offset-3{margin-left:12.5%}.el-col-xs-pull-3{position:relative;right:12.5%}.el-col-xs-push-3{position:relative;left:12.5%}.el-col-xs-4{width:16.66667%}.el-col-xs-offset-4{margin-left:16.66667%}.el-col-xs-pull-4{position:relative;right:16.66667%}.el-col-xs-push-4{position:relative;left:16.66667%}.el-col-xs-5{width:20.83333%}.el-col-xs-offset-5{margin-left:20.83333%}.el-col-xs-pull-5{position:relative;right:20.83333%}.el-col-xs-push-5{position:relative;left:20.83333%}.el-col-xs-6{width:25%}.el-col-xs-offset-6{margin-left:25%}.el-col-xs-pull-6{position:relative;right:25%}.el-col-xs-push-6{position:relative;left:25%}.el-col-xs-7{width:29.16667%}.el-col-xs-offset-7{margin-left:29.16667%}.el-col-xs-pull-7{position:relative;right:29.16667%}.el-col-xs-push-7{position:relative;left:29.16667%}.el-col-xs-8{width:33.33333%}.el-col-xs-offset-8{margin-left:33.33333%}.el-col-xs-pull-8{position:relative;right:33.33333%}.el-col-xs-push-8{position:relative;left:33.33333%}.el-col-xs-9{width:37.5%}.el-col-xs-offset-9{margin-left:37.5%}.el-col-xs-pull-9{position:relative;right:37.5%}.el-col-xs-push-9{position:relative;left:37.5%}.el-col-xs-10{width:41.66667%}.el-col-xs-offset-10{margin-left:41.66667%}.el-col-xs-pull-10{position:relative;right:41.66667%}.el-col-xs-push-10{position:relative;left:41.66667%}.el-col-xs-11{width:45.83333%}.el-col-xs-offset-11{margin-left:45.83333%}.el-col-xs-pull-11{position:relative;right:45.83333%}.el-col-xs-push-11{position:relative;left:45.83333%}.el-col-xs-12{width:50%}.el-col-xs-offset-12{margin-left:50%}.el-col-xs-pull-12{position:relative;right:50%}.el-col-xs-push-12{position:relative;left:50%}.el-col-xs-13{width:54.16667%}.el-col-xs-offset-13{margin-left:54.16667%}.el-col-xs-pull-13{position:relative;right:54.16667%}.el-col-xs-push-13{position:relative;left:54.16667%}.el-col-xs-14{width:58.33333%}.el-col-xs-offset-14{margin-left:58.33333%}.el-col-xs-pull-14{position:relative;right:58.33333%}.el-col-xs-push-14{position:relative;left:58.33333%}.el-col-xs-15{width:62.5%}.el-col-xs-offset-15{margin-left:62.5%}.el-col-xs-pull-15{position:relative;right:62.5%}.el-col-xs-push-15{position:relative;left:62.5%}.el-col-xs-16{width:66.66667%}.el-col-xs-offset-16{margin-left:66.66667%}.el-col-xs-pull-16{position:relative;right:66.66667%}.el-col-xs-push-16{position:relative;left:66.66667%}.el-col-xs-17{width:70.83333%}.el-col-xs-offset-17{margin-left:70.83333%}.el-col-xs-pull-17{position:relative;right:70.83333%}.el-col-xs-push-17{position:relative;left:70.83333%}.el-col-xs-18{width:75%}.el-col-xs-offset-18{margin-left:75%}.el-col-xs-pull-18{position:relative;right:75%}.el-col-xs-push-18{position:relative;left:75%}.el-col-xs-19{width:79.16667%}.el-col-xs-offset-19{margin-left:79.16667%}.el-col-xs-pull-19{position:relative;right:79.16667%}.el-col-xs-push-19{position:relative;left:79.16667%}.el-col-xs-20{width:83.33333%}.el-col-xs-offset-20{margin-left:83.33333%}.el-col-xs-pull-20{position:relative;right:83.33333%}.el-col-xs-push-20{position:relative;left:83.33333%}.el-col-xs-21{width:87.5%}.el-col-xs-offset-21{margin-left:87.5%}.el-col-xs-pull-21{position:relative;right:87.5%}.el-col-xs-push-21{position:relative;left:87.5%}.el-col-xs-22{width:91.66667%}.el-col-xs-offset-22{margin-left:91.66667%}.el-col-xs-pull-22{position:relative;right:91.66667%}.el-col-xs-push-22{position:relative;left:91.66667%}.el-col-xs-23{width:95.83333%}.el-col-xs-offset-23{margin-left:95.83333%}.el-col-xs-pull-23{position:relative;right:95.83333%}.el-col-xs-push-23{position:relative;left:95.83333%}.el-col-xs-24{width:100%}.el-col-xs-offset-24{margin-left:100%}.el-col-xs-pull-24{position:relative;right:100%}.el-col-xs-push-24{position:relative;left:100%}}@media only screen and (min-width:768px){.el-col-sm-0{display:none;width:0%}.el-col-sm-offset-0{margin-left:0}.el-col-sm-pull-0{position:relative;right:0}.el-col-sm-push-0{position:relative;left:0}.el-col-sm-1{width:4.16667%}.el-col-sm-offset-1{margin-left:4.16667%}.el-col-sm-pull-1{position:relative;right:4.16667%}.el-col-sm-push-1{position:relative;left:4.16667%}.el-col-sm-2{width:8.33333%}.el-col-sm-offset-2{margin-left:8.33333%}.el-col-sm-pull-2{position:relative;right:8.33333%}.el-col-sm-push-2{position:relative;left:8.33333%}.el-col-sm-3{width:12.5%}.el-col-sm-offset-3{margin-left:12.5%}.el-col-sm-pull-3{position:relative;right:12.5%}.el-col-sm-push-3{position:relative;left:12.5%}.el-col-sm-4{width:16.66667%}.el-col-sm-offset-4{margin-left:16.66667%}.el-col-sm-pull-4{position:relative;right:16.66667%}.el-col-sm-push-4{position:relative;left:16.66667%}.el-col-sm-5{width:20.83333%}.el-col-sm-offset-5{margin-left:20.83333%}.el-col-sm-pull-5{position:relative;right:20.83333%}.el-col-sm-push-5{position:relative;left:20.83333%}.el-col-sm-6{width:25%}.el-col-sm-offset-6{margin-left:25%}.el-col-sm-pull-6{position:relative;right:25%}.el-col-sm-push-6{position:relative;left:25%}.el-col-sm-7{width:29.16667%}.el-col-sm-offset-7{margin-left:29.16667%}.el-col-sm-pull-7{position:relative;right:29.16667%}.el-col-sm-push-7{position:relative;left:29.16667%}.el-col-sm-8{width:33.33333%}.el-col-sm-offset-8{margin-left:33.33333%}.el-col-sm-pull-8{position:relative;right:33.33333%}.el-col-sm-push-8{position:relative;left:33.33333%}.el-col-sm-9{width:37.5%}.el-col-sm-offset-9{margin-left:37.5%}.el-col-sm-pull-9{position:relative;right:37.5%}.el-col-sm-push-9{position:relative;left:37.5%}.el-col-sm-10{width:41.66667%}.el-col-sm-offset-10{margin-left:41.66667%}.el-col-sm-pull-10{position:relative;right:41.66667%}.el-col-sm-push-10{position:relative;left:41.66667%}.el-col-sm-11{width:45.83333%}.el-col-sm-offset-11{margin-left:45.83333%}.el-col-sm-pull-11{position:relative;right:45.83333%}.el-col-sm-push-11{position:relative;left:45.83333%}.el-col-sm-12{width:50%}.el-col-sm-offset-12{margin-left:50%}.el-col-sm-pull-12{position:relative;right:50%}.el-col-sm-push-12{position:relative;left:50%}.el-col-sm-13{width:54.16667%}.el-col-sm-offset-13{margin-left:54.16667%}.el-col-sm-pull-13{position:relative;right:54.16667%}.el-col-sm-push-13{position:relative;left:54.16667%}.el-col-sm-14{width:58.33333%}.el-col-sm-offset-14{margin-left:58.33333%}.el-col-sm-pull-14{position:relative;right:58.33333%}.el-col-sm-push-14{position:relative;left:58.33333%}.el-col-sm-15{width:62.5%}.el-col-sm-offset-15{margin-left:62.5%}.el-col-sm-pull-15{position:relative;right:62.5%}.el-col-sm-push-15{position:relative;left:62.5%}.el-col-sm-16{width:66.66667%}.el-col-sm-offset-16{margin-left:66.66667%}.el-col-sm-pull-16{position:relative;right:66.66667%}.el-col-sm-push-16{position:relative;left:66.66667%}.el-col-sm-17{width:70.83333%}.el-col-sm-offset-17{margin-left:70.83333%}.el-col-sm-pull-17{position:relative;right:70.83333%}.el-col-sm-push-17{position:relative;left:70.83333%}.el-col-sm-18{width:75%}.el-col-sm-offset-18{margin-left:75%}.el-col-sm-pull-18{position:relative;right:75%}.el-col-sm-push-18{position:relative;left:75%}.el-col-sm-19{width:79.16667%}.el-col-sm-offset-19{margin-left:79.16667%}.el-col-sm-pull-19{position:relative;right:79.16667%}.el-col-sm-push-19{position:relative;left:79.16667%}.el-col-sm-20{width:83.33333%}.el-col-sm-offset-20{margin-left:83.33333%}.el-col-sm-pull-20{position:relative;right:83.33333%}.el-col-sm-push-20{position:relative;left:83.33333%}.el-col-sm-21{width:87.5%}.el-col-sm-offset-21{margin-left:87.5%}.el-col-sm-pull-21{position:relative;right:87.5%}.el-col-sm-push-21{position:relative;left:87.5%}.el-col-sm-22{width:91.66667%}.el-col-sm-offset-22{margin-left:91.66667%}.el-col-sm-pull-22{position:relative;right:91.66667%}.el-col-sm-push-22{position:relative;left:91.66667%}.el-col-sm-23{width:95.83333%}.el-col-sm-offset-23{margin-left:95.83333%}.el-col-sm-pull-23{position:relative;right:95.83333%}.el-col-sm-push-23{position:relative;left:95.83333%}.el-col-sm-24{width:100%}.el-col-sm-offset-24{margin-left:100%}.el-col-sm-pull-24{position:relative;right:100%}.el-col-sm-push-24{position:relative;left:100%}}@media only screen and (min-width:992px){.el-col-md-0{display:none;width:0%}.el-col-md-offset-0{margin-left:0}.el-col-md-pull-0{position:relative;right:0}.el-col-md-push-0{position:relative;left:0}.el-col-md-1{width:4.16667%}.el-col-md-offset-1{margin-left:4.16667%}.el-col-md-pull-1{position:relative;right:4.16667%}.el-col-md-push-1{position:relative;left:4.16667%}.el-col-md-2{width:8.33333%}.el-col-md-offset-2{margin-left:8.33333%}.el-col-md-pull-2{position:relative;right:8.33333%}.el-col-md-push-2{position:relative;left:8.33333%}.el-col-md-3{width:12.5%}.el-col-md-offset-3{margin-left:12.5%}.el-col-md-pull-3{position:relative;right:12.5%}.el-col-md-push-3{position:relative;left:12.5%}.el-col-md-4{width:16.66667%}.el-col-md-offset-4{margin-left:16.66667%}.el-col-md-pull-4{position:relative;right:16.66667%}.el-col-md-push-4{position:relative;left:16.66667%}.el-col-md-5{width:20.83333%}.el-col-md-offset-5{margin-left:20.83333%}.el-col-md-pull-5{position:relative;right:20.83333%}.el-col-md-push-5{position:relative;left:20.83333%}.el-col-md-6{width:25%}.el-col-md-offset-6{margin-left:25%}.el-col-md-pull-6{position:relative;right:25%}.el-col-md-push-6{position:relative;left:25%}.el-col-md-7{width:29.16667%}.el-col-md-offset-7{margin-left:29.16667%}.el-col-md-pull-7{position:relative;right:29.16667%}.el-col-md-push-7{position:relative;left:29.16667%}.el-col-md-8{width:33.33333%}.el-col-md-offset-8{margin-left:33.33333%}.el-col-md-pull-8{position:relative;right:33.33333%}.el-col-md-push-8{position:relative;left:33.33333%}.el-col-md-9{width:37.5%}.el-col-md-offset-9{margin-left:37.5%}.el-col-md-pull-9{position:relative;right:37.5%}.el-col-md-push-9{position:relative;left:37.5%}.el-col-md-10{width:41.66667%}.el-col-md-offset-10{margin-left:41.66667%}.el-col-md-pull-10{position:relative;right:41.66667%}.el-col-md-push-10{position:relative;left:41.66667%}.el-col-md-11{width:45.83333%}.el-col-md-offset-11{margin-left:45.83333%}.el-col-md-pull-11{position:relative;right:45.83333%}.el-col-md-push-11{position:relative;left:45.83333%}.el-col-md-12{width:50%}.el-col-md-offset-12{margin-left:50%}.el-col-md-pull-12{position:relative;right:50%}.el-col-md-push-12{position:relative;left:50%}.el-col-md-13{width:54.16667%}.el-col-md-offset-13{margin-left:54.16667%}.el-col-md-pull-13{position:relative;right:54.16667%}.el-col-md-push-13{position:relative;left:54.16667%}.el-col-md-14{width:58.33333%}.el-col-md-offset-14{margin-left:58.33333%}.el-col-md-pull-14{position:relative;right:58.33333%}.el-col-md-push-14{position:relative;left:58.33333%}.el-col-md-15{width:62.5%}.el-col-md-offset-15{margin-left:62.5%}.el-col-md-pull-15{position:relative;right:62.5%}.el-col-md-push-15{position:relative;left:62.5%}.el-col-md-16{width:66.66667%}.el-col-md-offset-16{margin-left:66.66667%}.el-col-md-pull-16{position:relative;right:66.66667%}.el-col-md-push-16{position:relative;left:66.66667%}.el-col-md-17{width:70.83333%}.el-col-md-offset-17{margin-left:70.83333%}.el-col-md-pull-17{position:relative;right:70.83333%}.el-col-md-push-17{position:relative;left:70.83333%}.el-col-md-18{width:75%}.el-col-md-offset-18{margin-left:75%}.el-col-md-pull-18{position:relative;right:75%}.el-col-md-push-18{position:relative;left:75%}.el-col-md-19{width:79.16667%}.el-col-md-offset-19{margin-left:79.16667%}.el-col-md-pull-19{position:relative;right:79.16667%}.el-col-md-push-19{position:relative;left:79.16667%}.el-col-md-20{width:83.33333%}.el-col-md-offset-20{margin-left:83.33333%}.el-col-md-pull-20{position:relative;right:83.33333%}.el-col-md-push-20{position:relative;left:83.33333%}.el-col-md-21{width:87.5%}.el-col-md-offset-21{margin-left:87.5%}.el-col-md-pull-21{position:relative;right:87.5%}.el-col-md-push-21{position:relative;left:87.5%}.el-col-md-22{width:91.66667%}.el-col-md-offset-22{margin-left:91.66667%}.el-col-md-pull-22{position:relative;right:91.66667%}.el-col-md-push-22{position:relative;left:91.66667%}.el-col-md-23{width:95.83333%}.el-col-md-offset-23{margin-left:95.83333%}.el-col-md-pull-23{position:relative;right:95.83333%}.el-col-md-push-23{position:relative;left:95.83333%}.el-col-md-24{width:100%}.el-col-md-offset-24{margin-left:100%}.el-col-md-pull-24{position:relative;right:100%}.el-col-md-push-24{position:relative;left:100%}}@media only screen and (min-width:1200px){.el-col-lg-0{display:none;width:0%}.el-col-lg-offset-0{margin-left:0}.el-col-lg-pull-0{position:relative;right:0}.el-col-lg-push-0{position:relative;left:0}.el-col-lg-1{width:4.16667%}.el-col-lg-offset-1{margin-left:4.16667%}.el-col-lg-pull-1{position:relative;right:4.16667%}.el-col-lg-push-1{position:relative;left:4.16667%}.el-col-lg-2{width:8.33333%}.el-col-lg-offset-2{margin-left:8.33333%}.el-col-lg-pull-2{position:relative;right:8.33333%}.el-col-lg-push-2{position:relative;left:8.33333%}.el-col-lg-3{width:12.5%}.el-col-lg-offset-3{margin-left:12.5%}.el-col-lg-pull-3{position:relative;right:12.5%}.el-col-lg-push-3{position:relative;left:12.5%}.el-col-lg-4{width:16.66667%}.el-col-lg-offset-4{margin-left:16.66667%}.el-col-lg-pull-4{position:relative;right:16.66667%}.el-col-lg-push-4{position:relative;left:16.66667%}.el-col-lg-5{width:20.83333%}.el-col-lg-offset-5{margin-left:20.83333%}.el-col-lg-pull-5{position:relative;right:20.83333%}.el-col-lg-push-5{position:relative;left:20.83333%}.el-col-lg-6{width:25%}.el-col-lg-offset-6{margin-left:25%}.el-col-lg-pull-6{position:relative;right:25%}.el-col-lg-push-6{position:relative;left:25%}.el-col-lg-7{width:29.16667%}.el-col-lg-offset-7{margin-left:29.16667%}.el-col-lg-pull-7{position:relative;right:29.16667%}.el-col-lg-push-7{position:relative;left:29.16667%}.el-col-lg-8{width:33.33333%}.el-col-lg-offset-8{margin-left:33.33333%}.el-col-lg-pull-8{position:relative;right:33.33333%}.el-col-lg-push-8{position:relative;left:33.33333%}.el-col-lg-9{width:37.5%}.el-col-lg-offset-9{margin-left:37.5%}.el-col-lg-pull-9{position:relative;right:37.5%}.el-col-lg-push-9{position:relative;left:37.5%}.el-col-lg-10{width:41.66667%}.el-col-lg-offset-10{margin-left:41.66667%}.el-col-lg-pull-10{position:relative;right:41.66667%}.el-col-lg-push-10{position:relative;left:41.66667%}.el-col-lg-11{width:45.83333%}.el-col-lg-offset-11{margin-left:45.83333%}.el-col-lg-pull-11{position:relative;right:45.83333%}.el-col-lg-push-11{position:relative;left:45.83333%}.el-col-lg-12{width:50%}.el-col-lg-offset-12{margin-left:50%}.el-col-lg-pull-12{position:relative;right:50%}.el-col-lg-push-12{position:relative;left:50%}.el-col-lg-13{width:54.16667%}.el-col-lg-offset-13{margin-left:54.16667%}.el-col-lg-pull-13{position:relative;right:54.16667%}.el-col-lg-push-13{position:relative;left:54.16667%}.el-col-lg-14{width:58.33333%}.el-col-lg-offset-14{margin-left:58.33333%}.el-col-lg-pull-14{position:relative;right:58.33333%}.el-col-lg-push-14{position:relative;left:58.33333%}.el-col-lg-15{width:62.5%}.el-col-lg-offset-15{margin-left:62.5%}.el-col-lg-pull-15{position:relative;right:62.5%}.el-col-lg-push-15{position:relative;left:62.5%}.el-col-lg-16{width:66.66667%}.el-col-lg-offset-16{margin-left:66.66667%}.el-col-lg-pull-16{position:relative;right:66.66667%}.el-col-lg-push-16{position:relative;left:66.66667%}.el-col-lg-17{width:70.83333%}.el-col-lg-offset-17{margin-left:70.83333%}.el-col-lg-pull-17{position:relative;right:70.83333%}.el-col-lg-push-17{position:relative;left:70.83333%}.el-col-lg-18{width:75%}.el-col-lg-offset-18{margin-left:75%}.el-col-lg-pull-18{position:relative;right:75%}.el-col-lg-push-18{position:relative;left:75%}.el-col-lg-19{width:79.16667%}.el-col-lg-offset-19{margin-left:79.16667%}.el-col-lg-pull-19{position:relative;right:79.16667%}.el-col-lg-push-19{position:relative;left:79.16667%}.el-col-lg-20{width:83.33333%}.el-col-lg-offset-20{margin-left:83.33333%}.el-col-lg-pull-20{position:relative;right:83.33333%}.el-col-lg-push-20{position:relative;left:83.33333%}.el-col-lg-21{width:87.5%}.el-col-lg-offset-21{margin-left:87.5%}.el-col-lg-pull-21{position:relative;right:87.5%}.el-col-lg-push-21{position:relative;left:87.5%}.el-col-lg-22{width:91.66667%}.el-col-lg-offset-22{margin-left:91.66667%}.el-col-lg-pull-22{position:relative;right:91.66667%}.el-col-lg-push-22{position:relative;left:91.66667%}.el-col-lg-23{width:95.83333%}.el-col-lg-offset-23{margin-left:95.83333%}.el-col-lg-pull-23{position:relative;right:95.83333%}.el-col-lg-push-23{position:relative;left:95.83333%}.el-col-lg-24{width:100%}.el-col-lg-offset-24{margin-left:100%}.el-col-lg-pull-24{position:relative;right:100%}.el-col-lg-push-24{position:relative;left:100%}}@media only screen and (min-width:1920px){.el-col-xl-0{display:none;width:0%}.el-col-xl-offset-0{margin-left:0}.el-col-xl-pull-0{position:relative;right:0}.el-col-xl-push-0{position:relative;left:0}.el-col-xl-1{width:4.16667%}.el-col-xl-offset-1{margin-left:4.16667%}.el-col-xl-pull-1{position:relative;right:4.16667%}.el-col-xl-push-1{position:relative;left:4.16667%}.el-col-xl-2{width:8.33333%}.el-col-xl-offset-2{margin-left:8.33333%}.el-col-xl-pull-2{position:relative;right:8.33333%}.el-col-xl-push-2{position:relative;left:8.33333%}.el-col-xl-3{width:12.5%}.el-col-xl-offset-3{margin-left:12.5%}.el-col-xl-pull-3{position:relative;right:12.5%}.el-col-xl-push-3{position:relative;left:12.5%}.el-col-xl-4{width:16.66667%}.el-col-xl-offset-4{margin-left:16.66667%}.el-col-xl-pull-4{position:relative;right:16.66667%}.el-col-xl-push-4{position:relative;left:16.66667%}.el-col-xl-5{width:20.83333%}.el-col-xl-offset-5{margin-left:20.83333%}.el-col-xl-pull-5{position:relative;right:20.83333%}.el-col-xl-push-5{position:relative;left:20.83333%}.el-col-xl-6{width:25%}.el-col-xl-offset-6{margin-left:25%}.el-col-xl-pull-6{position:relative;right:25%}.el-col-xl-push-6{position:relative;left:25%}.el-col-xl-7{width:29.16667%}.el-col-xl-offset-7{margin-left:29.16667%}.el-col-xl-pull-7{position:relative;right:29.16667%}.el-col-xl-push-7{position:relative;left:29.16667%}.el-col-xl-8{width:33.33333%}.el-col-xl-offset-8{margin-left:33.33333%}.el-col-xl-pull-8{position:relative;right:33.33333%}.el-col-xl-push-8{position:relative;left:33.33333%}.el-col-xl-9{width:37.5%}.el-col-xl-offset-9{margin-left:37.5%}.el-col-xl-pull-9{position:relative;right:37.5%}.el-col-xl-push-9{position:relative;left:37.5%}.el-col-xl-10{width:41.66667%}.el-col-xl-offset-10{margin-left:41.66667%}.el-col-xl-pull-10{position:relative;right:41.66667%}.el-col-xl-push-10{position:relative;left:41.66667%}.el-col-xl-11{width:45.83333%}.el-col-xl-offset-11{margin-left:45.83333%}.el-col-xl-pull-11{position:relative;right:45.83333%}.el-col-xl-push-11{position:relative;left:45.83333%}.el-col-xl-12{width:50%}.el-col-xl-offset-12{margin-left:50%}.el-col-xl-pull-12{position:relative;right:50%}.el-col-xl-push-12{position:relative;left:50%}.el-col-xl-13{width:54.16667%}.el-col-xl-offset-13{margin-left:54.16667%}.el-col-xl-pull-13{position:relative;right:54.16667%}.el-col-xl-push-13{position:relative;left:54.16667%}.el-col-xl-14{width:58.33333%}.el-col-xl-offset-14{margin-left:58.33333%}.el-col-xl-pull-14{position:relative;right:58.33333%}.el-col-xl-push-14{position:relative;left:58.33333%}.el-col-xl-15{width:62.5%}.el-col-xl-offset-15{margin-left:62.5%}.el-col-xl-pull-15{position:relative;right:62.5%}.el-col-xl-push-15{position:relative;left:62.5%}.el-col-xl-16{width:66.66667%}.el-col-xl-offset-16{margin-left:66.66667%}.el-col-xl-pull-16{position:relative;right:66.66667%}.el-col-xl-push-16{position:relative;left:66.66667%}.el-col-xl-17{width:70.83333%}.el-col-xl-offset-17{margin-left:70.83333%}.el-col-xl-pull-17{position:relative;right:70.83333%}.el-col-xl-push-17{position:relative;left:70.83333%}.el-col-xl-18{width:75%}.el-col-xl-offset-18{margin-left:75%}.el-col-xl-pull-18{position:relative;right:75%}.el-col-xl-push-18{position:relative;left:75%}.el-col-xl-19{width:79.16667%}.el-col-xl-offset-19{margin-left:79.16667%}.el-col-xl-pull-19{position:relative;right:79.16667%}.el-col-xl-push-19{position:relative;left:79.16667%}.el-col-xl-20{width:83.33333%}.el-col-xl-offset-20{margin-left:83.33333%}.el-col-xl-pull-20{position:relative;right:83.33333%}.el-col-xl-push-20{position:relative;left:83.33333%}.el-col-xl-21{width:87.5%}.el-col-xl-offset-21{margin-left:87.5%}.el-col-xl-pull-21{position:relative;right:87.5%}.el-col-xl-push-21{position:relative;left:87.5%}.el-col-xl-22{width:91.66667%}.el-col-xl-offset-22{margin-left:91.66667%}.el-col-xl-pull-22{position:relative;right:91.66667%}.el-col-xl-push-22{position:relative;left:91.66667%}.el-col-xl-23{width:95.83333%}.el-col-xl-offset-23{margin-left:95.83333%}.el-col-xl-pull-23{position:relative;right:95.83333%}.el-col-xl-push-23{position:relative;left:95.83333%}.el-col-xl-24{width:100%}.el-col-xl-offset-24{margin-left:100%}.el-col-xl-pull-24{position:relative;right:100%}.el-col-xl-push-24{position:relative;left:100%}}@-webkit-keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}.el-upload{display:inline-block;text-align:center;cursor:pointer;outline:0}.el-upload__input{display:none}.el-upload__tip{font-size:12px;color:#606266;margin-top:7px}.el-upload iframe{position:absolute;z-index:-1;top:0;left:0;opacity:0;filter:alpha(opacity=0)}.el-upload--picture-card{background-color:#fbfdff;border:1px dashed #c0ccda;border-radius:6px;box-sizing:border-box;width:148px;height:148px;line-height:146px;vertical-align:top}.el-upload--picture-card i{font-size:28px;color:#8c939d}.el-upload--picture-card:hover,.el-upload:focus{border-color:#409EFF;color:#409EFF}.el-upload:focus .el-upload-dragger{border-color:#409EFF}.el-upload-dragger{background-color:#fff;border:1px dashed #d9d9d9;border-radius:6px;box-sizing:border-box;width:360px;height:180px;text-align:center;position:relative;overflow:hidden}.el-upload-dragger .el-icon-upload{font-size:67px;color:#C0C4CC;margin:40px 0 16px;line-height:50px}.el-upload-dragger+.el-upload__tip{text-align:center}.el-upload-dragger~.el-upload__files{border-top:1px solid #DCDFE6;margin-top:7px;padding-top:5px}.el-upload-dragger .el-upload__text{color:#606266;font-size:14px;text-align:center}.el-upload-dragger .el-upload__text em{color:#409EFF;font-style:normal}.el-upload-dragger:hover{border-color:#409EFF}.el-upload-dragger.is-dragover{background-color:rgba(32,159,255,.06);border:2px dashed #409EFF}.el-upload-list{margin:0;padding:0;list-style:none}.el-upload-list__item{-webkit-transition:all .5s cubic-bezier(.55,0,.1,1);transition:all .5s cubic-bezier(.55,0,.1,1);font-size:14px;color:#606266;line-height:1.8;margin-top:5px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:4px;width:100%}.el-upload-list__item .el-progress{position:absolute;top:20px;width:100%}.el-upload-list__item .el-progress__text{position:absolute;right:0;top:-13px}.el-upload-list__item .el-progress-bar{margin-right:0;padding-right:0}.el-upload-list__item:first-child{margin-top:10px}.el-upload-list__item .el-icon-upload-success{color:#67C23A}.el-upload-list__item .el-icon-close{display:none;position:absolute;top:5px;right:5px;cursor:pointer;opacity:.75;color:#606266}.el-upload-list__item .el-icon-close:hover{opacity:1}.el-upload-list__item .el-icon-close-tip{display:none;position:absolute;top:5px;right:5px;font-size:12px;cursor:pointer;opacity:1;color:#409EFF}.el-upload-list__item:hover{background-color:#F5F7FA}.el-upload-list__item:hover .el-icon-close{display:inline-block}.el-upload-list__item:hover .el-progress__text{display:none}.el-upload-list__item.is-success .el-upload-list__item-status-label{display:block}.el-upload-list__item.is-success .el-upload-list__item-name:focus,.el-upload-list__item.is-success .el-upload-list__item-name:hover{color:#409EFF;cursor:pointer}.el-upload-list__item.is-success:focus:not(:hover) .el-icon-close-tip{display:inline-block}.el-upload-list__item.is-success:active .el-icon-close-tip,.el-upload-list__item.is-success:focus .el-upload-list__item-status-label,.el-upload-list__item.is-success:hover .el-upload-list__item-status-label,.el-upload-list__item.is-success:not(.focusing):focus .el-icon-close-tip{display:none}.el-upload-list.is-disabled .el-upload-list__item:hover .el-upload-list__item-status-label{display:block}.el-upload-list__item-name{color:#606266;display:block;margin-right:40px;overflow:hidden;padding-left:4px;text-overflow:ellipsis;-webkit-transition:color .3s;transition:color .3s;white-space:nowrap}.el-upload-list__item-name [class^=el-icon]{height:100%;margin-right:7px;color:#909399;line-height:inherit}.el-upload-list__item-status-label{position:absolute;right:5px;top:0;line-height:inherit;display:none}.el-upload-list__item-delete{position:absolute;right:10px;top:0;font-size:12px;color:#606266;display:none}.el-upload-list__item-delete:hover{color:#409EFF}.el-upload-list--picture-card{margin:0;display:inline;vertical-align:top}.el-upload-list--picture-card .el-upload-list__item{overflow:hidden;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;width:148px;height:148px;margin:0 8px 8px 0;display:inline-block}.el-upload-list--picture-card .el-upload-list__item .el-icon-check,.el-upload-list--picture-card .el-upload-list__item .el-icon-circle-check{color:#FFF}.el-upload-list--picture-card .el-upload-list__item .el-icon-close,.el-upload-list--picture-card .el-upload-list__item:hover .el-upload-list__item-status-label{display:none}.el-upload-list--picture-card .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture-card .el-upload-list__item-name{display:none}.el-upload-list--picture-card .el-upload-list__item-thumbnail{width:100%;height:100%}.el-upload-list--picture-card .el-upload-list__item-status-label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-list--picture-card .el-upload-list__item-status-label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture-card .el-upload-list__item-actions{position:absolute;width:100%;height:100%;left:0;top:0;cursor:default;text-align:center;color:#fff;opacity:0;font-size:20px;background-color:rgba(0,0,0,.5);-webkit-transition:opacity .3s;transition:opacity .3s}.el-upload-list--picture-card .el-upload-list__item-actions::after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-upload-list--picture-card .el-upload-list__item-actions span{display:none;cursor:pointer}.el-upload-list--picture-card .el-upload-list__item-actions span+span{margin-left:15px}.el-upload-list--picture-card .el-upload-list__item-actions .el-upload-list__item-delete{position:static;font-size:inherit;color:inherit}.el-upload-list--picture-card .el-upload-list__item-actions:hover{opacity:1}.el-upload-list--picture-card .el-upload-list__item-actions:hover span{display:inline-block}.el-upload-list--picture-card .el-progress{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);bottom:auto;width:126px}.el-upload-list--picture-card .el-progress .el-progress__text{top:50%}.el-upload-list--picture .el-upload-list__item{overflow:hidden;z-index:0;background-color:#fff;border:1px solid #c0ccda;border-radius:6px;-webkit-box-sizing:border-box;box-sizing:border-box;margin-top:10px;padding:10px 10px 10px 90px;height:92px}.el-upload-list--picture .el-upload-list__item .el-icon-check,.el-upload-list--picture .el-upload-list__item .el-icon-circle-check{color:#FFF}.el-upload-list--picture .el-upload-list__item:hover .el-upload-list__item-status-label{background:0 0;-webkit-box-shadow:none;box-shadow:none;top:-2px;right:-12px}.el-upload-list--picture .el-upload-list__item:hover .el-progress__text{display:block}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name{line-height:70px;margin-top:0}.el-upload-list--picture .el-upload-list__item.is-success .el-upload-list__item-name i{display:none}.el-upload-list--picture .el-upload-list__item-thumbnail{vertical-align:middle;display:inline-block;width:70px;height:70px;float:left;position:relative;z-index:1;margin-left:-80px;background-color:#FFF}.el-upload-list--picture .el-upload-list__item-name{display:block;margin-top:20px}.el-upload-list--picture .el-upload-list__item-name i{font-size:70px;line-height:1;position:absolute;left:9px;top:10px}.el-upload-list--picture .el-upload-list__item-status-label{position:absolute;right:-17px;top:-7px;width:46px;height:26px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 1px 1px #ccc;box-shadow:0 1px 1px #ccc}.el-upload-list--picture .el-upload-list__item-status-label i{font-size:12px;margin-top:12px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}.el-upload-list--picture .el-progress{position:relative;top:-7px}.el-upload-cover{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;z-index:10;cursor:default}.el-upload-cover::after{display:inline-block;height:100%;vertical-align:middle}.el-upload-cover img{display:block;width:100%;height:100%}.el-upload-cover__label{position:absolute;right:-15px;top:-6px;width:40px;height:24px;background:#13ce66;text-align:center;-webkit-transform:rotate(45deg);transform:rotate(45deg);-webkit-box-shadow:0 0 1pc 1px rgba(0,0,0,.2);box-shadow:0 0 1pc 1px rgba(0,0,0,.2)}.el-upload-cover__label i{font-size:12px;margin-top:11px;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);color:#fff}.el-upload-cover__progress{display:inline-block;vertical-align:middle;position:static;width:243px}.el-upload-cover__progress+.el-upload__inner{opacity:0}.el-upload-cover__content{position:absolute;top:0;left:0;width:100%;height:100%}.el-upload-cover__interact{position:absolute;bottom:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,.72);text-align:center}.el-upload-cover__interact .btn{display:inline-block;color:#FFF;font-size:14px;cursor:pointer;vertical-align:middle;-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);margin-top:60px}.el-upload-cover__interact .btn span{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.el-upload-cover__interact .btn:not(:first-child){margin-left:35px}.el-upload-cover__interact .btn:hover{-webkit-transform:translateY(-13px);transform:translateY(-13px)}.el-upload-cover__interact .btn:hover span{opacity:1}.el-upload-cover__interact .btn i{color:#FFF;display:block;font-size:24px;line-height:inherit;margin:0 auto 5px}.el-upload-cover__title{position:absolute;bottom:0;left:0;background-color:#FFF;height:36px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;text-align:left;padding:0 10px;margin:0;line-height:36px;font-size:14px;color:#303133}.el-upload-cover+.el-upload__inner{opacity:0;position:relative;z-index:1}.el-progress{position:relative;line-height:1}.el-progress__text{font-size:14px;color:#606266;display:inline-block;vertical-align:middle;margin-left:10px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;-webkit-transform:translate(0,-50%);transform:translate(0,-50%)}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.el-progress-bar,.el-progress-bar__inner::after,.el-progress-bar__innerText,.el-spinner{display:inline-block;vertical-align:middle}.el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:#67C23A}.el-progress.is-success .el-progress__text{color:#67C23A}.el-progress.is-warning .el-progress-bar__inner{background-color:#E6A23C}.el-progress.is-warning .el-progress__text{color:#E6A23C}.el-progress.is-exception .el-progress-bar__inner{background-color:#F56C6C}.el-progress.is-exception .el-progress__text{color:#F56C6C}.el-progress-bar{padding-right:50px;width:100%;margin-right:-55px;-webkit-box-sizing:border-box;box-sizing:border-box}.el-progress-bar__outer{height:6px;border-radius:100px;background-color:#EBEEF5;overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:#409EFF;text-align:right;border-radius:100px;line-height:1;white-space:nowrap;-webkit-transition:width .6s ease;transition:width .6s ease}.el-card,.el-message{border-radius:4px;overflow:hidden}.el-progress-bar__inner::after{height:100%}.el-progress-bar__innerText{color:#FFF;font-size:12px;margin:0 5px}@keyframes progress{0%{background-position:0 0}100%{background-position:32px 0}}.el-time-spinner{width:100%;white-space:nowrap}.el-spinner-inner{-webkit-animation:rotate 2s linear infinite;animation:rotate 2s linear infinite;width:50px;height:50px}.el-spinner-inner .path{stroke:#ececec;stroke-linecap:round;-webkit-animation:dash 1.5s ease-in-out infinite;animation:dash 1.5s ease-in-out infinite}@-webkit-keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotate{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}@keyframes dash{0%{stroke-dasharray:1,150;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-35}100%{stroke-dasharray:90,150;stroke-dashoffset:-124}}.el-message{min-width:380px;-webkit-box-sizing:border-box;box-sizing:border-box;border-width:1px;border-style:solid;border-color:#EBEEF5;position:fixed;left:50%;top:20px;-webkit-transform:translateX(-50%);transform:translateX(-50%);background-color:#edf2fc;-webkit-transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,top .4s,-webkit-transform .4s;transition:opacity .3s,transform .4s,top .4s;transition:opacity .3s,transform .4s,top .4s,-webkit-transform .4s;padding:15px 15px 15px 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-message.is-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-message.is-closable .el-message__content{padding-right:16px}.el-message p{margin:0}.el-message--info .el-message__content{color:#909399}.el-message--success{background-color:#f0f9eb;border-color:#e1f3d8}.el-message--success .el-message__content{color:#67C23A}.el-message--warning{background-color:#fdf6ec;border-color:#faecd8}.el-message--warning .el-message__content{color:#E6A23C}.el-message--error{background-color:#fef0f0;border-color:#fde2e2}.el-message--error .el-message__content{color:#F56C6C}.el-message__icon{margin-right:10px}.el-message__content{padding:0;font-size:14px;line-height:1}.el-message__closeBtn{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);transform:translateY(-50%);cursor:pointer;color:#C0C4CC;font-size:16px}.el-message__closeBtn:hover{color:#909399}.el-message .el-icon-success{color:#67C23A}.el-message .el-icon-error{color:#F56C6C}.el-message .el-icon-info{color:#909399}.el-message .el-icon-warning{color:#E6A23C}.el-message-fade-enter,.el-message-fade-leave-active{opacity:0;-webkit-transform:translate(-50%,-100%);transform:translate(-50%,-100%)}.el-badge{position:relative;vertical-align:middle;display:inline-block}.el-badge__content{background-color:#F56C6C;border-radius:10px;color:#FFF;display:inline-block;font-size:12px;height:18px;line-height:18px;padding:0 6px;text-align:center;white-space:nowrap;border:1px solid #FFF}.el-badge__content.is-fixed{position:absolute;top:0;right:10px;-webkit-transform:translateY(-50%) translateX(100%);transform:translateY(-50%) translateX(100%)}.el-rate__icon,.el-rate__item{position:relative;display:inline-block}.el-badge__content.is-fixed.is-dot{right:5px}.el-badge__content.is-dot{height:8px;width:8px;padding:0;right:0;border-radius:50%}.el-badge__content--primary{background-color:#409EFF}.el-badge__content--success{background-color:#67C23A}.el-badge__content--warning{background-color:#E6A23C}.el-badge__content--info{background-color:#909399}.el-badge__content--danger{background-color:#F56C6C}.el-card{border:1px solid #EBEEF5;background-color:#FFF;color:#303133;-webkit-transition:.3s;transition:.3s}.el-card.is-always-shadow,.el-card.is-hover-shadow:focus,.el-card.is-hover-shadow:hover{-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-card__header{padding:18px 20px;border-bottom:1px solid #EBEEF5;-webkit-box-sizing:border-box;box-sizing:border-box}.el-card__body{padding:20px}.el-rate{height:20px;line-height:1}.el-rate__item{font-size:0;vertical-align:middle}.el-rate__icon{font-size:18px;margin-right:6px;color:#C0C4CC;-webkit-transition:.3s;transition:.3s}.el-rate__decimal,.el-rate__icon .path2{position:absolute;top:0;left:0}.el-rate__icon.hover{-webkit-transform:scale(1.15);transform:scale(1.15)}.el-rate__decimal{display:inline-block;overflow:hidden}.el-step.is-vertical,.el-steps{display:-webkit-box;display:-ms-flexbox}.el-rate__text{font-size:14px;vertical-align:middle}.el-steps{display:flex}.el-steps--simple{padding:13px 8%;border-radius:4px;background:#F5F7FA}.el-steps--horizontal{white-space:nowrap}.el-steps--vertical{height:100%;-webkit-box-orient:vertical;-ms-flex-flow:column;flex-flow:column}.el-step{position:relative;-ms-flex-negative:1;flex-shrink:1}.el-step:last-of-type .el-step__line{display:none}.el-step:last-of-type.is-flex{-ms-flex-preferred-size:auto!important;flex-basis:auto!important;-ms-flex-negative:0;flex-shrink:0;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0}.el-step:last-of-type .el-step__description,.el-step:last-of-type .el-step__main{padding-right:0}.el-step__head{position:relative;width:100%}.el-step__head.is-process{color:#303133;border-color:#303133}.el-step__head.is-wait{color:#C0C4CC;border-color:#C0C4CC}.el-step__head.is-success{color:#67C23A;border-color:#67C23A}.el-step__head.is-error{color:#F56C6C;border-color:#F56C6C}.el-step__head.is-finish{color:#409EFF;border-color:#409EFF}.el-step__icon{position:relative;z-index:1;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:24px;height:24px;font-size:14px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#FFF;-webkit-transition:.15s ease-out;transition:.15s ease-out}.el-step__icon.is-text{border-radius:50%;border:2px solid;border-color:inherit}.el-step__icon.is-icon{width:40px}.el-step__icon-inner{display:inline-block;user-select:none;text-align:center;font-weight:700;line-height:1;color:inherit}.el-button,.el-checkbox,.el-image-viewer__btn{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:25px;font-weight:400}.el-step__icon-inner.is-status{-webkit-transform:translateY(1px);transform:translateY(1px)}.el-step__line{position:absolute;border-color:inherit;background-color:#C0C4CC}.el-step__line-inner{display:block;border-width:1px;border-style:solid;border-color:inherit;-webkit-transition:.15s ease-out;transition:.15s ease-out;-webkit-box-sizing:border-box;box-sizing:border-box;width:0;height:0}.el-step__main{white-space:normal;text-align:left}.el-step__title{font-size:16px;line-height:38px}.el-step__title.is-process{font-weight:700;color:#303133}.el-step__title.is-wait{color:#C0C4CC}.el-step__title.is-success{color:#67C23A}.el-step__title.is-error{color:#F56C6C}.el-step__title.is-finish{color:#409EFF}.el-step__description{padding-right:10%;margin-top:-5px;font-size:12px;line-height:20px;font-weight:400}.el-step__description.is-process{color:#303133}.el-step__description.is-wait{color:#C0C4CC}.el-step__description.is-success{color:#67C23A}.el-step__description.is-error{color:#F56C6C}.el-step__description.is-finish{color:#409EFF}.el-step.is-horizontal{display:inline-block}.el-step.is-horizontal .el-step__line{height:2px;top:11px;left:0;right:0}.el-step.is-vertical{display:flex}.el-step.is-vertical .el-step__head{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;width:24px}.el-step.is-vertical .el-step__main{padding-left:10px;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-vertical .el-step__title{line-height:24px;padding-bottom:8px}.el-step.is-vertical .el-step__line{width:2px;top:0;bottom:0;left:11px}.el-step.is-vertical .el-step__icon.is-icon{width:24px}.el-step.is-center .el-step__head,.el-step.is-center .el-step__main{text-align:center}.el-step.is-center .el-step__description{padding-left:20%;padding-right:20%}.el-step.is-center .el-step__line{left:50%;right:-50%}.el-step.is-simple{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-step.is-simple .el-step__head{width:auto;font-size:0;padding-right:10px}.el-step.is-simple .el-step__icon{background:0 0;width:16px;height:16px;font-size:12px}.el-step.is-simple .el-step__icon-inner[class*=el-icon]:not(.is-status){font-size:18px}.el-step.is-simple .el-step__icon-inner.is-status{-webkit-transform:scale(.8) translateY(1px);transform:scale(.8) translateY(1px)}.el-step.is-simple .el-step__main{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.el-step.is-simple .el-step__title{font-size:16px;line-height:20px}.el-step.is-simple:not(:last-of-type) .el-step__title{max-width:50%;word-break:break-all}.el-step.is-simple .el-step__arrow{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.el-step.is-simple .el-step__arrow::after,.el-step.is-simple .el-step__arrow::before{content:'';display:inline-block;position:absolute;height:15px;width:1px;background:#C0C4CC}.el-step.is-simple .el-step__arrow::before{-webkit-transform:rotate(-45deg) translateY(-4px);transform:rotate(-45deg) translateY(-4px);-webkit-transform-origin:0 0;transform-origin:0 0}.el-step.is-simple .el-step__arrow::after{-webkit-transform:rotate(45deg) translateY(4px);transform:rotate(45deg) translateY(4px);-webkit-transform-origin:100% 100%;transform-origin:100% 100%}.el-step.is-simple:last-of-type .el-step__arrow{display:none}.el-carousel{position:relative}.el-carousel--horizontal{overflow-x:hidden}.el-carousel--vertical{overflow-y:hidden}.el-carousel__container{position:relative;height:300px}.el-carousel__arrow{border:none;outline:0;padding:0;margin:0;height:36px;width:36px;cursor:pointer;-webkit-transition:.3s;transition:.3s;border-radius:50%;background-color:rgba(31,45,61,.11);color:#FFF;position:absolute;top:50%;z-index:10;-webkit-transform:translateY(-50%);transform:translateY(-50%);text-align:center;font-size:12px}.el-carousel__arrow--left{left:16px}.el-carousel__arrow--right{right:16px}.el-carousel__arrow:hover{background-color:rgba(31,45,61,.23)}.el-carousel__arrow i{cursor:pointer}.el-carousel__indicators{position:absolute;list-style:none;margin:0;padding:0;z-index:2}.el-carousel__indicators--horizontal{bottom:0;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.el-carousel__indicators--vertical{right:0;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-carousel__indicators--outside{bottom:26px;text-align:center;position:static;-webkit-transform:none;transform:none}.el-carousel__indicators--outside .el-carousel__indicator:hover button{opacity:.64}.el-carousel__indicators--outside button{background-color:#C0C4CC;opacity:.24}.el-carousel__indicators--labels{left:0;right:0;-webkit-transform:none;transform:none;text-align:center}.el-carousel__indicators--labels .el-carousel__button{height:auto;width:auto;padding:2px 18px;font-size:12px}.el-carousel__indicators--labels .el-carousel__indicator{padding:6px 4px}.el-carousel__indicator{background-color:transparent;cursor:pointer}.el-carousel__indicator:hover button{opacity:.72}.el-carousel__indicator--horizontal{display:inline-block;padding:12px 4px}.el-carousel__indicator--vertical{padding:4px 12px}.el-carousel__indicator--vertical .el-carousel__button{width:2px;height:15px}.el-carousel__indicator.is-active button{opacity:1}.el-carousel__button{display:block;opacity:.48;width:30px;height:2px;background-color:#FFF;border:none;outline:0;padding:0;margin:0;cursor:pointer;-webkit-transition:.3s;transition:.3s}.el-carousel__item,.el-carousel__mask{height:100%;top:0;left:0;position:absolute}.carousel-arrow-left-enter,.carousel-arrow-left-leave-active{-webkit-transform:translateY(-50%) translateX(-10px);transform:translateY(-50%) translateX(-10px);opacity:0}.carousel-arrow-right-enter,.carousel-arrow-right-leave-active{-webkit-transform:translateY(-50%) translateX(10px);transform:translateY(-50%) translateX(10px);opacity:0}.el-carousel__item{width:100%;display:inline-block;overflow:hidden;z-index:0}.el-carousel__item.is-active{z-index:2}.el-carousel__item.is-animating{-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card{width:50%;-webkit-transition:-webkit-transform .4s ease-in-out;transition:-webkit-transform .4s ease-in-out;transition:transform .4s ease-in-out;transition:transform .4s ease-in-out,-webkit-transform .4s ease-in-out}.el-carousel__item--card.is-in-stage{cursor:pointer;z-index:1}.el-carousel__item--card.is-in-stage.is-hover .el-carousel__mask,.el-carousel__item--card.is-in-stage:hover .el-carousel__mask{opacity:.12}.el-carousel__item--card.is-active{z-index:2}.el-carousel__mask{width:100%;background-color:#FFF;opacity:.24;-webkit-transition:.2s;transition:.2s}.el-fade-in-enter,.el-fade-in-leave-active,.el-fade-in-linear-enter,.el-fade-in-linear-leave,.el-fade-in-linear-leave-active,.fade-in-linear-enter,.fade-in-linear-leave,.fade-in-linear-leave-active{opacity:0}.fade-in-linear-enter-active,.fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-linear-enter-active,.el-fade-in-linear-leave-active{-webkit-transition:opacity .2s linear;transition:opacity .2s linear}.el-fade-in-enter-active,.el-fade-in-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter-active,.el-zoom-in-center-leave-active{-webkit-transition:all .3s cubic-bezier(.55,0,.1,1);transition:all .3s cubic-bezier(.55,0,.1,1)}.el-zoom-in-center-enter,.el-zoom-in-center-leave-active{opacity:0;-webkit-transform:scaleX(0);transform:scaleX(0)}.el-zoom-in-top-enter-active,.el-zoom-in-top-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center top;transform-origin:center top}.el-zoom-in-top-enter,.el-zoom-in-top-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-bottom-enter-active,.el-zoom-in-bottom-leave-active{opacity:1;-webkit-transform:scaleY(1);transform:scaleY(1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}.el-zoom-in-bottom-enter,.el-zoom-in-bottom-leave-active{opacity:0;-webkit-transform:scaleY(0);transform:scaleY(0)}.el-zoom-in-left-enter-active,.el-zoom-in-left-leave-active{opacity:1;-webkit-transform:scale(1,1);transform:scale(1,1);-webkit-transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1);transition:transform .3s cubic-bezier(.23,1,.32,1),opacity .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);-webkit-transform-origin:top left;transform-origin:top left}.el-zoom-in-left-enter,.el-zoom-in-left-leave-active{opacity:0;-webkit-transform:scale(.45,.45);transform:scale(.45,.45)}.collapse-transition{-webkit-transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out;transition:.3s height ease-in-out,.3s padding-top ease-in-out,.3s padding-bottom ease-in-out}.horizontal-collapse-transition{-webkit-transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out;transition:.3s width ease-in-out,.3s padding-left ease-in-out,.3s padding-right ease-in-out}.el-list-enter-active,.el-list-leave-active{-webkit-transition:all 1s;transition:all 1s}.el-list-enter,.el-list-leave-active{opacity:0;-webkit-transform:translateY(-30px);transform:translateY(-30px)}.el-opacity-transition{-webkit-transition:opacity .3s cubic-bezier(.55,0,.1,1);transition:opacity .3s cubic-bezier(.55,0,.1,1)}.el-collapse{border-top:1px solid #EBEEF5;border-bottom:1px solid #EBEEF5}.el-collapse-item.is-disabled .el-collapse-item__header{color:#bbb;cursor:not-allowed}.el-collapse-item__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:48px;line-height:48px;background-color:#FFF;color:#303133;cursor:pointer;border-bottom:1px solid #EBEEF5;font-size:13px;font-weight:500;-webkit-transition:border-bottom-color .3s;transition:border-bottom-color .3s;outline:0}.el-collapse-item__arrow{margin:0 8px 0 auto;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-weight:300}.el-collapse-item__arrow.is-active{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.el-collapse-item__header.focusing:focus:not(:hover){color:#409EFF}.el-collapse-item__header.is-active{border-bottom-color:transparent}.el-collapse-item__wrap{will-change:height;background-color:#FFF;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;border-bottom:1px solid #EBEEF5}.el-cascader__tags,.el-tag{-webkit-box-sizing:border-box}.el-collapse-item__content{padding-bottom:25px;font-size:13px;color:#303133;line-height:1.769230769230769}.el-collapse-item:last-child{margin-bottom:-1px}.el-popper .popper__arrow,.el-popper .popper__arrow::after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.el-popper .popper__arrow{border-width:6px;-webkit-filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03));filter:drop-shadow(0 2px 12px rgba(0, 0, 0, .03))}.el-popper .popper__arrow::after{content:" ";border-width:6px}.el-popper[x-placement^=top]{margin-bottom:12px}.el-popper[x-placement^=top] .popper__arrow{bottom:-6px;left:50%;margin-right:3px;border-top-color:#EBEEF5;border-bottom-width:0}.el-popper[x-placement^=top] .popper__arrow::after{bottom:1px;margin-left:-6px;border-top-color:#FFF;border-bottom-width:0}.el-popper[x-placement^=bottom]{margin-top:12px}.el-popper[x-placement^=bottom] .popper__arrow{top:-6px;left:50%;margin-right:3px;border-top-width:0;border-bottom-color:#EBEEF5}.el-popper[x-placement^=bottom] .popper__arrow::after{top:1px;margin-left:-6px;border-top-width:0;border-bottom-color:#FFF}.el-popper[x-placement^=right]{margin-left:12px}.el-popper[x-placement^=right] .popper__arrow{top:50%;left:-6px;margin-bottom:3px;border-right-color:#EBEEF5;border-left-width:0}.el-popper[x-placement^=right] .popper__arrow::after{bottom:-6px;left:1px;border-right-color:#FFF;border-left-width:0}.el-popper[x-placement^=left]{margin-right:12px}.el-popper[x-placement^=left] .popper__arrow{top:50%;right:-6px;margin-bottom:3px;border-right-width:0;border-left-color:#EBEEF5}.el-popper[x-placement^=left] .popper__arrow::after{right:1px;bottom:-6px;margin-left:-6px;border-right-width:0;border-left-color:#FFF}.el-tag{background-color:#ecf5ff;border-color:#d9ecff;display:inline-block;height:32px;padding:0 10px;line-height:30px;font-size:12px;color:#409EFF;border-width:1px;border-style:solid;border-radius:4px;box-sizing:border-box;white-space:nowrap}.el-tag.is-hit{border-color:#409EFF}.el-tag .el-tag__close{color:#409eff}.el-tag .el-tag__close:hover{color:#FFF;background-color:#409eff}.el-tag.el-tag--info{background-color:#f4f4f5;border-color:#e9e9eb;color:#909399}.el-tag.el-tag--info.is-hit{border-color:#909399}.el-tag.el-tag--info .el-tag__close{color:#909399}.el-tag.el-tag--info .el-tag__close:hover{color:#FFF;background-color:#909399}.el-tag.el-tag--success{background-color:#f0f9eb;border-color:#e1f3d8;color:#67c23a}.el-tag.el-tag--success.is-hit{border-color:#67C23A}.el-tag.el-tag--success .el-tag__close{color:#67c23a}.el-tag.el-tag--success .el-tag__close:hover{color:#FFF;background-color:#67c23a}.el-tag.el-tag--warning{background-color:#fdf6ec;border-color:#faecd8;color:#e6a23c}.el-tag.el-tag--warning.is-hit{border-color:#E6A23C}.el-tag.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag.el-tag--warning .el-tag__close:hover{color:#FFF;background-color:#e6a23c}.el-tag.el-tag--danger{background-color:#fef0f0;border-color:#fde2e2;color:#f56c6c}.el-tag.el-tag--danger.is-hit{border-color:#F56C6C}.el-tag.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag.el-tag--danger .el-tag__close:hover{color:#FFF;background-color:#f56c6c}.el-tag .el-icon-close{border-radius:50%;text-align:center;position:relative;cursor:pointer;font-size:12px;height:16px;width:16px;line-height:16px;vertical-align:middle;top:-1px;right:-5px}.el-tag .el-icon-close::before{display:block}.el-tag--dark{background-color:#409eff;border-color:#409eff;color:#fff}.el-tag--dark.is-hit{border-color:#409EFF}.el-tag--dark .el-tag__close{color:#fff}.el-tag--dark .el-tag__close:hover{color:#FFF;background-color:#66b1ff}.el-tag--dark.el-tag--info{background-color:#909399;border-color:#909399;color:#fff}.el-tag--dark.el-tag--info.is-hit{border-color:#909399}.el-tag--dark.el-tag--info .el-tag__close{color:#fff}.el-tag--dark.el-tag--info .el-tag__close:hover{color:#FFF;background-color:#a6a9ad}.el-tag--dark.el-tag--success{background-color:#67c23a;border-color:#67c23a;color:#fff}.el-tag--dark.el-tag--success.is-hit{border-color:#67C23A}.el-tag--dark.el-tag--success .el-tag__close{color:#fff}.el-tag--dark.el-tag--success .el-tag__close:hover{color:#FFF;background-color:#85ce61}.el-tag--dark.el-tag--warning{background-color:#e6a23c;border-color:#e6a23c;color:#fff}.el-tag--dark.el-tag--warning.is-hit{border-color:#E6A23C}.el-tag--dark.el-tag--warning .el-tag__close{color:#fff}.el-tag--dark.el-tag--warning .el-tag__close:hover{color:#FFF;background-color:#ebb563}.el-tag--dark.el-tag--danger{background-color:#f56c6c;border-color:#f56c6c;color:#fff}.el-tag--dark.el-tag--danger.is-hit{border-color:#F56C6C}.el-tag--dark.el-tag--danger .el-tag__close{color:#fff}.el-tag--dark.el-tag--danger .el-tag__close:hover{color:#FFF;background-color:#f78989}.el-tag--plain{background-color:#fff;border-color:#b3d8ff;color:#409eff}.el-tag--plain.is-hit{border-color:#409EFF}.el-tag--plain .el-tag__close{color:#409eff}.el-tag--plain .el-tag__close:hover{color:#FFF;background-color:#409eff}.el-tag--plain.el-tag--info{background-color:#fff;border-color:#d3d4d6;color:#909399}.el-tag--plain.el-tag--info.is-hit{border-color:#909399}.el-tag--plain.el-tag--info .el-tag__close{color:#909399}.el-tag--plain.el-tag--info .el-tag__close:hover{color:#FFF;background-color:#909399}.el-tag--plain.el-tag--success{background-color:#fff;border-color:#c2e7b0;color:#67c23a}.el-tag--plain.el-tag--success.is-hit{border-color:#67C23A}.el-tag--plain.el-tag--success .el-tag__close{color:#67c23a}.el-tag--plain.el-tag--success .el-tag__close:hover{color:#FFF;background-color:#67c23a}.el-tag--plain.el-tag--warning{background-color:#fff;border-color:#f5dab1;color:#e6a23c}.el-tag--plain.el-tag--warning.is-hit{border-color:#E6A23C}.el-tag--plain.el-tag--warning .el-tag__close{color:#e6a23c}.el-tag--plain.el-tag--warning .el-tag__close:hover{color:#FFF;background-color:#e6a23c}.el-tag--plain.el-tag--danger{background-color:#fff;border-color:#fbc4c4;color:#f56c6c}.el-tag--plain.el-tag--danger.is-hit{border-color:#F56C6C}.el-tag--plain.el-tag--danger .el-tag__close{color:#f56c6c}.el-tag--plain.el-tag--danger .el-tag__close:hover{color:#FFF;background-color:#f56c6c}.el-tag--medium{height:28px;line-height:26px}.el-tag--medium .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--small{height:24px;padding:0 8px;line-height:22px}.el-tag--small .el-icon-close{-webkit-transform:scale(.8);transform:scale(.8)}.el-tag--mini{height:20px;padding:0 5px;line-height:19px}.el-tag--mini .el-icon-close{margin-left:-3px;-webkit-transform:scale(.7);transform:scale(.7)}.el-cascader{display:inline-block;position:relative;font-size:14px;line-height:40px}.el-cascader:not(.is-disabled):hover .el-input__inner{cursor:pointer;border-color:#C0C4CC}.el-cascader .el-input .el-input__inner:focus,.el-cascader .el-input.is-focus .el-input__inner{border-color:#409EFF}.el-cascader .el-input{cursor:pointer}.el-cascader .el-input .el-input__inner{text-overflow:ellipsis}.el-cascader .el-input .el-icon-arrow-down{-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s;font-size:14px}.el-cascader .el-input .el-icon-arrow-down.is-reverse{-webkit-transform:rotateZ(180deg);transform:rotateZ(180deg)}.el-cascader .el-input .el-icon-circle-close:hover{color:#909399}.el-cascader--medium{font-size:14px;line-height:36px}.el-cascader--small{font-size:13px;line-height:32px}.el-cascader--mini{font-size:12px;line-height:28px}.el-cascader.is-disabled .el-cascader__label{z-index:2;color:#C0C4CC}.el-cascader__dropdown{margin:5px 0;font-size:14px;background:#FFF;border:1px solid #E4E7ED;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-cascader__tags{position:absolute;left:0;right:30px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;line-height:normal;text-align:left;box-sizing:border-box}.el-cascader__tags .el-tag{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;max-width:100%;margin:2px 0 2px 6px;text-overflow:ellipsis;background:#f0f2f5}.el-cascader__tags .el-tag:not(.is-hit){border-color:transparent}.el-cascader__tags .el-tag>span{-webkit-box-flex:1;-ms-flex:1;flex:1;overflow:hidden;text-overflow:ellipsis}.el-cascader__tags .el-tag .el-icon-close{-webkit-box-flex:0;-ms-flex:none;flex:none;background-color:#C0C4CC;color:#FFF}.el-cascader__tags .el-tag .el-icon-close:hover{background-color:#909399}.el-cascader__suggestion-panel{border-radius:4px}.el-cascader__suggestion-list{max-height:204px;margin:0;padding:6px 0;font-size:14px;color:#606266;text-align:center}.el-cascader__suggestion-item{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;height:34px;padding:0 15px;text-align:left;outline:0;cursor:pointer}.el-cascader__suggestion-item:focus,.el-cascader__suggestion-item:hover{background:#F5F7FA}.el-cascader__suggestion-item.is-checked{color:#409EFF;font-weight:700}.el-cascader__suggestion-item>span{margin-right:10px}.el-cascader__empty-text{margin:10px 0;color:#C0C4CC}.el-cascader__search-input{-webkit-box-flex:1;-ms-flex:1;flex:1;height:24px;min-width:60px;margin:2px 0 2px 15px;padding:0;color:#606266;border:none;outline:0;-webkit-box-sizing:border-box;box-sizing:border-box}.el-cascader__search-input::-webkit-input-placeholder{color:#C0C4CC}.el-cascader__search-input::-ms-input-placeholder{color:#C0C4CC}.el-cascader__search-input::placeholder{color:#C0C4CC}.el-color-predefine{display:-webkit-box;display:-ms-flexbox;display:flex;font-size:12px;margin-top:8px;width:280px}.el-color-predefine__colors{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-wrap:wrap;flex-wrap:wrap}.el-color-predefine__color-selector{margin:0 0 8px 8px;width:20px;height:20px;border-radius:4px;cursor:pointer}.el-color-predefine__color-selector:nth-child(10n+1){margin-left:0}.el-color-predefine__color-selector.selected{-webkit-box-shadow:0 0 3px 2px #409EFF;box-shadow:0 0 3px 2px #409EFF}.el-color-predefine__color-selector>div{display:-webkit-box;display:-ms-flexbox;display:flex;height:100%;border-radius:3px}.el-color-predefine__color-selector.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-hue-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background-color:red;padding:0 2px}.el-color-hue-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%);height:100%}.el-color-hue-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-hue-slider.is-vertical{width:12px;height:180px;padding:2px 0}.el-color-hue-slider.is-vertical .el-color-hue-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(red),color-stop(17%,#ff0),color-stop(33%,#0f0),color-stop(50%,#0ff),color-stop(67%,#00f),color-stop(83%,#f0f),to(red));background:linear-gradient(to bottom,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}.el-color-hue-slider.is-vertical .el-color-hue-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-svpanel{position:relative;width:280px;height:180px}.el-color-svpanel__black,.el-color-svpanel__white{position:absolute;top:0;left:0;right:0;bottom:0}.el-color-svpanel__white{background:-webkit-gradient(linear,left top,right top,from(#fff),to(rgba(255,255,255,0)));background:linear-gradient(to right,#fff,rgba(255,255,255,0))}.el-color-svpanel__black{background:-webkit-gradient(linear,left bottom,left top,from(#000),to(rgba(0,0,0,0)));background:linear-gradient(to top,#000,rgba(0,0,0,0))}.el-color-svpanel__cursor{position:absolute}.el-color-svpanel__cursor>div{cursor:head;width:4px;height:4px;-webkit-box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);box-shadow:0 0 0 1.5px #fff,inset 0 0 1px 1px rgba(0,0,0,.3),0 0 1px 2px rgba(0,0,0,.4);border-radius:50%;-webkit-transform:translate(-2px,-2px);transform:translate(-2px,-2px)}.el-color-alpha-slider{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;width:280px;height:12px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-alpha-slider__bar{position:relative;background:-webkit-gradient(linear,left top,right top,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to right,rgba(255,255,255,0) 0,#fff 100%);height:100%}.el-color-alpha-slider__thumb{position:absolute;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;left:0;top:0;width:4px;height:100%;border-radius:1px;background:#fff;border:1px solid #f0f0f0;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);z-index:1}.el-color-alpha-slider.is-vertical{width:20px;height:180px}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__bar{background:-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,0)),to(white));background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fff 100%)}.el-color-alpha-slider.is-vertical .el-color-alpha-slider__thumb{left:0;top:0;width:100%;height:4px}.el-color-dropdown{width:300px}.el-color-dropdown__main-wrapper{margin-bottom:6px}.el-color-dropdown__main-wrapper::after{content:"";display:table;clear:both}.el-color-dropdown__btns{margin-top:6px;text-align:right}.el-color-dropdown__value{float:left;line-height:26px;font-size:12px;color:#000;width:160px}.el-color-dropdown__btn{border:1px solid #dcdcdc;color:#333;line-height:24px;border-radius:2px;padding:0 20px;cursor:pointer;background-color:transparent;outline:0;font-size:12px}.el-color-dropdown__btn[disabled]{color:#ccc;cursor:not-allowed}.el-color-dropdown__btn:hover{color:#409EFF;border-color:#409EFF}.el-color-dropdown__link-btn{cursor:pointer;color:#409EFF;text-decoration:none;padding:15px;font-size:12px}.el-color-dropdown__link-btn:hover{color:tint(#409EFF,20%)}.el-color-picker{display:inline-block;position:relative;line-height:normal;height:40px}.el-color-picker.is-disabled .el-color-picker__trigger{cursor:not-allowed}.el-color-picker--medium{height:36px}.el-color-picker--medium .el-color-picker__trigger{height:36px;width:36px}.el-color-picker--medium .el-color-picker__mask{height:34px;width:34px}.el-color-picker--small{height:32px}.el-color-picker--small .el-color-picker__trigger{height:32px;width:32px}.el-color-picker--small .el-color-picker__mask{height:30px;width:30px}.el-color-picker--small .el-color-picker__empty,.el-color-picker--small .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker--mini{height:28px}.el-color-picker--mini .el-color-picker__trigger{height:28px;width:28px}.el-color-picker--mini .el-color-picker__mask{height:26px;width:26px}.el-color-picker--mini .el-color-picker__empty,.el-color-picker--mini .el-color-picker__icon{-webkit-transform:translate3d(-50%,-50%,0) scale(.8);transform:translate3d(-50%,-50%,0) scale(.8)}.el-color-picker__mask{height:38px;width:38px;border-radius:4px;position:absolute;top:1px;left:1px;z-index:1;cursor:not-allowed;background-color:rgba(255,255,255,.7)}.el-color-picker__trigger{display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px;width:40px;padding:4px;border:1px solid #e6e6e6;border-radius:4px;font-size:0;position:relative;cursor:pointer}.el-color-picker__color{position:relative;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;border:1px solid #999;border-radius:2px;width:100%;height:100%;text-align:center}.el-color-picker__color.is-alpha{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==)}.el-color-picker__color-inner{position:absolute;left:0;top:0;right:0;bottom:0}.el-color-picker__empty,.el-color-picker__icon{top:50%;left:50%;font-size:12px;position:absolute}.el-color-picker__empty{color:#999;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.el-color-picker__icon{display:inline-block;width:100%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0);color:#FFF;text-align:center}.el-color-picker__panel{position:absolute;z-index:10;padding:6px;-webkit-box-sizing:content-box;box-sizing:content-box;background-color:#FFF;border:1px solid #EBEEF5;border-radius:4px;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1)}.el-textarea{position:relative;display:inline-block;width:100%;vertical-align:bottom;font-size:14px}.el-textarea__inner{display:block;resize:vertical;padding:5px 15px;line-height:1.5;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;font-size:inherit;color:#606266;background-color:#FFF;background-image:none;border:1px solid #DCDFE6;border-radius:4px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1)}.el-textarea__inner::-webkit-input-placeholder{color:#C0C4CC}.el-textarea__inner::-ms-input-placeholder{color:#C0C4CC}.el-textarea__inner::placeholder{color:#C0C4CC}.el-textarea__inner:hover{border-color:#C0C4CC}.el-textarea__inner:focus{outline:0;border-color:#409EFF}.el-textarea .el-input__count{color:#909399;background:#FFF;position:absolute;font-size:12px;bottom:5px;right:10px}.el-textarea.is-disabled .el-textarea__inner{background-color:#F5F7FA;border-color:#E4E7ED;color:#C0C4CC;cursor:not-allowed}.el-textarea.is-disabled .el-textarea__inner::-webkit-input-placeholder{color:#C0C4CC}.el-textarea.is-disabled .el-textarea__inner::-ms-input-placeholder{color:#C0C4CC}.el-textarea.is-disabled .el-textarea__inner::placeholder{color:#C0C4CC}.el-textarea.is-exceed .el-textarea__inner{border-color:#F56C6C}.el-textarea.is-exceed .el-input__count{color:#F56C6C}.el-input{position:relative;font-size:14px;display:inline-block;width:100%}.el-input::-webkit-scrollbar{z-index:11;width:6px}.el-input::-webkit-scrollbar:horizontal{height:6px}.el-input::-webkit-scrollbar-thumb{border-radius:5px;width:6px;background:#b4bccc}.el-input::-webkit-scrollbar-corner{background:#fff}.el-input::-webkit-scrollbar-track{background:#fff}.el-input::-webkit-scrollbar-track-piece{background:#fff;width:6px}.el-input .el-input__clear{color:#C0C4CC;font-size:14px;cursor:pointer;-webkit-transition:color .2s cubic-bezier(.645,.045,.355,1);transition:color .2s cubic-bezier(.645,.045,.355,1)}.el-input .el-input__clear:hover{color:#909399}.el-input .el-input__count{height:100%;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#909399;font-size:12px}.el-input .el-input__count .el-input__count-inner{background:#FFF;line-height:initial;display:inline-block;padding:0 5px}.el-input__inner{-webkit-appearance:none;background-color:#FFF;background-image:none;border-radius:4px;border:1px solid #DCDFE6;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;display:inline-block;font-size:inherit;height:40px;line-height:40px;outline:0;padding:0 15px;-webkit-transition:border-color .2s cubic-bezier(.645,.045,.355,1);transition:border-color .2s cubic-bezier(.645,.045,.355,1);width:100%}.el-button,.el-transfer-panel{-webkit-box-sizing:border-box}.el-input__prefix,.el-input__suffix{position:absolute;top:0;-webkit-transition:all .3s;height:100%;color:#C0C4CC;text-align:center}.el-input__inner::-webkit-input-placeholder{color:#C0C4CC}.el-input__inner::-ms-input-placeholder{color:#C0C4CC}.el-input__inner::placeholder{color:#C0C4CC}.el-input__inner:hover{border-color:#C0C4CC}.el-input.is-active .el-input__inner,.el-input__inner:focus{border-color:#409EFF;outline:0}.el-input__suffix{right:5px;transition:all .3s}.el-input__suffix-inner{pointer-events:all}.el-input__prefix{left:5px;transition:all .3s}.el-input__icon{height:100%;width:25px;text-align:center;-webkit-transition:all .3s;transition:all .3s;line-height:40px}.el-input__icon:after{content:'';height:100%;width:0;display:inline-block;vertical-align:middle}.el-input__validateIcon{pointer-events:none}.el-input.is-disabled .el-input__inner{background-color:#F5F7FA;border-color:#E4E7ED;color:#C0C4CC;cursor:not-allowed}.el-input.is-disabled .el-input__inner::-webkit-input-placeholder{color:#C0C4CC}.el-input.is-disabled .el-input__inner::-ms-input-placeholder{color:#C0C4CC}.el-input.is-disabled .el-input__inner::placeholder{color:#C0C4CC}.el-input.is-disabled .el-input__icon{cursor:not-allowed}.el-input.is-exceed .el-input__inner{border-color:#F56C6C}.el-input.is-exceed .el-input__suffix .el-input__count{color:#F56C6C}.el-input--suffix .el-input__inner{padding-right:30px}.el-input--prefix .el-input__inner{padding-left:30px}.el-input--medium{font-size:14px}.el-input--medium .el-input__inner{height:36px;line-height:36px}.el-input--medium .el-input__icon{line-height:36px}.el-input--small{font-size:13px}.el-input--small .el-input__inner{height:32px;line-height:32px}.el-input--small .el-input__icon{line-height:32px}.el-input--mini{font-size:12px}.el-input--mini .el-input__inner{height:28px;line-height:28px}.el-input--mini .el-input__icon{line-height:28px}.el-input-group{line-height:normal;display:inline-table;width:100%;border-collapse:separate;border-spacing:0}.el-input-group>.el-input__inner{vertical-align:middle;display:table-cell}.el-input-group__append,.el-input-group__prepend{background-color:#F5F7FA;color:#909399;vertical-align:middle;display:table-cell;position:relative;border:1px solid #DCDFE6;border-radius:4px;padding:0 20px;width:1px;white-space:nowrap}.el-input-group--prepend .el-input__inner,.el-input-group__append{border-top-left-radius:0;border-bottom-left-radius:0}.el-input-group--append .el-input__inner,.el-input-group__prepend{border-top-right-radius:0;border-bottom-right-radius:0}.el-input-group__append:focus,.el-input-group__prepend:focus{outline:0}.el-input-group__append .el-button,.el-input-group__append .el-select,.el-input-group__prepend .el-button,.el-input-group__prepend .el-select{display:inline-block;margin:-10px -20px}.el-input-group__append button.el-button,.el-input-group__append div.el-select .el-input__inner,.el-input-group__append div.el-select:hover .el-input__inner,.el-input-group__prepend button.el-button,.el-input-group__prepend div.el-select .el-input__inner,.el-input-group__prepend div.el-select:hover .el-input__inner{border-color:transparent;background-color:transparent;color:inherit;border-top:0;border-bottom:0}.el-input-group__append .el-button,.el-input-group__append .el-input,.el-input-group__prepend .el-button,.el-input-group__prepend .el-input{font-size:inherit}.el-input-group__prepend{border-right:0}.el-input-group__append{border-left:0}.el-input-group--append .el-select .el-input.is-focus .el-input__inner,.el-input-group--prepend .el-select .el-input.is-focus .el-input__inner{border-color:transparent}.el-input__inner::-ms-clear{display:none;width:0;height:0}.el-button{display:inline-block;line-height:1;white-space:nowrap;cursor:pointer;background:#FFF;border:1px solid #DCDFE6;color:#606266;-webkit-appearance:none;text-align:center;box-sizing:border-box;outline:0;margin:0;-webkit-transition:.1s;transition:.1s;font-weight:500;padding:12px 20px;font-size:14px;border-radius:4px}.el-button+.el-button{margin-left:10px}.el-button:focus,.el-button:hover{color:#409EFF;border-color:#c6e2ff;background-color:#ecf5ff}.el-button:active{color:#3a8ee6;border-color:#3a8ee6;outline:0}.el-button::-moz-focus-inner{border:0}.el-button [class*=el-icon-]+span{margin-left:5px}.el-button.is-plain:focus,.el-button.is-plain:hover{background:#FFF;border-color:#409EFF;color:#409EFF}.el-button.is-active,.el-button.is-plain:active{color:#3a8ee6;border-color:#3a8ee6}.el-button.is-plain:active{background:#FFF;outline:0}.el-button.is-disabled,.el-button.is-disabled:focus,.el-button.is-disabled:hover{color:#C0C4CC;cursor:not-allowed;background-image:none;background-color:#FFF;border-color:#EBEEF5}.el-link,.el-transfer-panel__filter .el-icon-circle-close{cursor:pointer}.el-button.is-disabled.el-button--text{background-color:transparent}.el-button.is-disabled.is-plain,.el-button.is-disabled.is-plain:focus,.el-button.is-disabled.is-plain:hover{background-color:#FFF;border-color:#EBEEF5;color:#C0C4CC}.el-button.is-loading{position:relative;pointer-events:none}.el-button.is-loading:before{pointer-events:none;content:'';position:absolute;left:-1px;top:-1px;right:-1px;bottom:-1px;border-radius:inherit;background-color:rgba(255,255,255,.35)}.el-button.is-round{border-radius:20px;padding:12px 23px}.el-button.is-circle{border-radius:50%;padding:12px}.el-button--primary{color:#FFF;background-color:#409EFF;border-color:#409EFF}.el-button--primary:focus,.el-button--primary:hover{background:#66b1ff;border-color:#66b1ff;color:#FFF}.el-button--primary.is-active,.el-button--primary:active{background:#3a8ee6;border-color:#3a8ee6;color:#FFF}.el-button--primary:active{outline:0}.el-button--primary.is-disabled,.el-button--primary.is-disabled:active,.el-button--primary.is-disabled:focus,.el-button--primary.is-disabled:hover{color:#FFF;background-color:#a0cfff;border-color:#a0cfff}.el-button--primary.is-plain{color:#409EFF;background:#ecf5ff;border-color:#b3d8ff}.el-button--primary.is-plain:focus,.el-button--primary.is-plain:hover{background:#409EFF;border-color:#409EFF;color:#FFF}.el-button--primary.is-plain:active{background:#3a8ee6;border-color:#3a8ee6;color:#FFF;outline:0}.el-button--primary.is-plain.is-disabled,.el-button--primary.is-plain.is-disabled:active,.el-button--primary.is-plain.is-disabled:focus,.el-button--primary.is-plain.is-disabled:hover{color:#8cc5ff;background-color:#ecf5ff;border-color:#d9ecff}.el-button--success{color:#FFF;background-color:#67C23A;border-color:#67C23A}.el-button--success:focus,.el-button--success:hover{background:#85ce61;border-color:#85ce61;color:#FFF}.el-button--success.is-active,.el-button--success:active{background:#5daf34;border-color:#5daf34;color:#FFF}.el-button--success:active{outline:0}.el-button--success.is-disabled,.el-button--success.is-disabled:active,.el-button--success.is-disabled:focus,.el-button--success.is-disabled:hover{color:#FFF;background-color:#b3e19d;border-color:#b3e19d}.el-button--success.is-plain{color:#67C23A;background:#f0f9eb;border-color:#c2e7b0}.el-button--success.is-plain:focus,.el-button--success.is-plain:hover{background:#67C23A;border-color:#67C23A;color:#FFF}.el-button--success.is-plain:active{background:#5daf34;border-color:#5daf34;color:#FFF;outline:0}.el-button--success.is-plain.is-disabled,.el-button--success.is-plain.is-disabled:active,.el-button--success.is-plain.is-disabled:focus,.el-button--success.is-plain.is-disabled:hover{color:#a4da89;background-color:#f0f9eb;border-color:#e1f3d8}.el-button--warning{color:#FFF;background-color:#E6A23C;border-color:#E6A23C}.el-button--warning:focus,.el-button--warning:hover{background:#ebb563;border-color:#ebb563;color:#FFF}.el-button--warning.is-active,.el-button--warning:active{background:#cf9236;border-color:#cf9236;color:#FFF}.el-button--warning:active{outline:0}.el-button--warning.is-disabled,.el-button--warning.is-disabled:active,.el-button--warning.is-disabled:focus,.el-button--warning.is-disabled:hover{color:#FFF;background-color:#f3d19e;border-color:#f3d19e}.el-button--warning.is-plain{color:#E6A23C;background:#fdf6ec;border-color:#f5dab1}.el-button--warning.is-plain:focus,.el-button--warning.is-plain:hover{background:#E6A23C;border-color:#E6A23C;color:#FFF}.el-button--warning.is-plain:active{background:#cf9236;border-color:#cf9236;color:#FFF;outline:0}.el-button--warning.is-plain.is-disabled,.el-button--warning.is-plain.is-disabled:active,.el-button--warning.is-plain.is-disabled:focus,.el-button--warning.is-plain.is-disabled:hover{color:#f0c78a;background-color:#fdf6ec;border-color:#faecd8}.el-button--danger{color:#FFF;background-color:#F56C6C;border-color:#F56C6C}.el-button--danger:focus,.el-button--danger:hover{background:#f78989;border-color:#f78989;color:#FFF}.el-button--danger.is-active,.el-button--danger:active{background:#dd6161;border-color:#dd6161;color:#FFF}.el-button--danger:active{outline:0}.el-button--danger.is-disabled,.el-button--danger.is-disabled:active,.el-button--danger.is-disabled:focus,.el-button--danger.is-disabled:hover{color:#FFF;background-color:#fab6b6;border-color:#fab6b6}.el-button--danger.is-plain{color:#F56C6C;background:#fef0f0;border-color:#fbc4c4}.el-button--danger.is-plain:focus,.el-button--danger.is-plain:hover{background:#F56C6C;border-color:#F56C6C;color:#FFF}.el-button--danger.is-plain:active{background:#dd6161;border-color:#dd6161;color:#FFF;outline:0}.el-button--danger.is-plain.is-disabled,.el-button--danger.is-plain.is-disabled:active,.el-button--danger.is-plain.is-disabled:focus,.el-button--danger.is-plain.is-disabled:hover{color:#f9a7a7;background-color:#fef0f0;border-color:#fde2e2}.el-button--info{color:#FFF;background-color:#909399;border-color:#909399}.el-button--info:focus,.el-button--info:hover{background:#a6a9ad;border-color:#a6a9ad;color:#FFF}.el-button--info.is-active,.el-button--info:active{background:#82848a;border-color:#82848a;color:#FFF}.el-button--info:active{outline:0}.el-button--info.is-disabled,.el-button--info.is-disabled:active,.el-button--info.is-disabled:focus,.el-button--info.is-disabled:hover{color:#FFF;background-color:#c8c9cc;border-color:#c8c9cc}.el-button--info.is-plain{color:#909399;background:#f4f4f5;border-color:#d3d4d6}.el-button--info.is-plain:focus,.el-button--info.is-plain:hover{background:#909399;border-color:#909399;color:#FFF}.el-button--info.is-plain:active{background:#82848a;border-color:#82848a;color:#FFF;outline:0}.el-button--info.is-plain.is-disabled,.el-button--info.is-plain.is-disabled:active,.el-button--info.is-plain.is-disabled:focus,.el-button--info.is-plain.is-disabled:hover{color:#bcbec2;background-color:#f4f4f5;border-color:#e9e9eb}.el-button--text,.el-button--text.is-disabled,.el-button--text.is-disabled:focus,.el-button--text.is-disabled:hover,.el-button--text:active{border-color:transparent}.el-button--medium{padding:10px 20px;font-size:14px;border-radius:4px}.el-button--mini,.el-button--small{font-size:12px;border-radius:3px}.el-button--medium.is-round{padding:10px 20px}.el-button--medium.is-circle{padding:10px}.el-button--small,.el-button--small.is-round{padding:9px 15px}.el-button--small.is-circle{padding:9px}.el-button--mini,.el-button--mini.is-round{padding:7px 15px}.el-button--mini.is-circle{padding:7px}.el-button--text{color:#409EFF;background:0 0;padding-left:0;padding-right:0}.el-button--text:focus,.el-button--text:hover{color:#66b1ff;border-color:transparent;background-color:transparent}.el-button--text:active{color:#3a8ee6;background-color:transparent}.el-button-group{display:inline-block;vertical-align:middle}.el-button-group::after,.el-button-group::before{display:table;content:""}.el-button-group::after{clear:both}.el-button-group>.el-button{float:left;position:relative}.el-button-group>.el-button+.el-button{margin-left:0}.el-button-group>.el-button.is-disabled{z-index:1}.el-button-group>.el-button:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.el-button-group>.el-button:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.el-button-group>.el-button:first-child:last-child{border-radius:4px}.el-button-group>.el-button:first-child:last-child.is-round{border-radius:20px}.el-button-group>.el-button:first-child:last-child.is-circle{border-radius:50%}.el-button-group>.el-button:not(:first-child):not(:last-child){border-radius:0}.el-button-group>.el-button:not(:last-child){margin-right:-1px}.el-button-group>.el-button.is-active,.el-button-group>.el-button:active,.el-button-group>.el-button:focus,.el-button-group>.el-button:hover{z-index:1}.el-button-group>.el-dropdown>.el-button{border-top-left-radius:0;border-bottom-left-radius:0;border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--primary:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--primary:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--primary:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--success:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--success:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--success:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--warning:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--warning:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--warning:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--danger:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--danger:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--danger:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--info:first-child{border-right-color:rgba(255,255,255,.5)}.el-button-group .el-button--info:last-child{border-left-color:rgba(255,255,255,.5)}.el-button-group .el-button--info:not(:first-child):not(:last-child){border-left-color:rgba(255,255,255,.5);border-right-color:rgba(255,255,255,.5)}.el-transfer{font-size:14px}.el-transfer__buttons{display:inline-block;vertical-align:middle;padding:0 30px}.el-transfer__button{display:block;margin:0 auto;padding:10px;border-radius:50%;color:#FFF;background-color:#409EFF;font-size:0}.el-transfer__button.is-with-texts{border-radius:4px}.el-transfer__button.is-disabled,.el-transfer__button.is-disabled:hover{border:1px solid #DCDFE6;background-color:#F5F7FA;color:#C0C4CC}.el-transfer__button:first-child{margin-bottom:10px}.el-transfer__button:nth-child(2){margin:0}.el-transfer__button i,.el-transfer__button span{font-size:14px}.el-transfer__button [class*=el-icon-]+span{margin-left:0}.el-transfer-panel{border:1px solid #EBEEF5;border-radius:4px;overflow:hidden;background:#FFF;display:inline-block;vertical-align:middle;width:200px;max-height:100%;box-sizing:border-box;position:relative}.el-transfer-panel__body{height:246px}.el-transfer-panel__body.is-with-footer{padding-bottom:40px}.el-transfer-panel__list{margin:0;padding:6px 0;list-style:none;height:246px;overflow:auto;-webkit-box-sizing:border-box;box-sizing:border-box}.el-transfer-panel__list.is-filterable{height:194px;padding-top:0}.el-transfer-panel__item{height:30px;line-height:30px;padding-left:15px;display:block}.el-transfer-panel__item+.el-transfer-panel__item{margin-left:0;display:block!important}.el-transfer-panel__item.el-checkbox{color:#606266}.el-transfer-panel__item:hover{color:#409EFF}.el-transfer-panel__item.el-checkbox .el-checkbox__label{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;-webkit-box-sizing:border-box;box-sizing:border-box;padding-left:24px;line-height:30px}.el-transfer-panel__item .el-checkbox__input{position:absolute;top:8px}.el-transfer-panel__filter{text-align:center;margin:15px;-webkit-box-sizing:border-box;box-sizing:border-box;display:block;width:auto}.el-transfer-panel__filter .el-input__inner{height:32px;width:100%;font-size:12px;display:inline-block;-webkit-box-sizing:border-box;box-sizing:border-box;border-radius:16px;padding-right:10px;padding-left:30px}.el-transfer-panel__filter .el-input__icon{margin-left:5px}.el-transfer-panel .el-transfer-panel__header{height:40px;line-height:40px;background:#F5F7FA;margin:0;padding-left:15px;border-bottom:1px solid #EBEEF5;-webkit-box-sizing:border-box;box-sizing:border-box;color:#000}.el-transfer-panel .el-transfer-panel__header .el-checkbox{display:block;line-height:40px}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label{font-size:16px;color:#303133;font-weight:400}.el-transfer-panel .el-transfer-panel__header .el-checkbox .el-checkbox__label span{position:absolute;right:15px;color:#909399;font-size:12px;font-weight:400}.el-divider__text,.el-link{font-weight:500;font-size:14px}.el-transfer-panel .el-transfer-panel__footer{height:40px;background:#FFF;margin:0;padding:0;border-top:1px solid #EBEEF5;position:absolute;bottom:0;left:0;width:100%;z-index:1}.el-transfer-panel .el-transfer-panel__footer::after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-container,.el-timeline-item__node{display:-webkit-box;display:-ms-flexbox}.el-transfer-panel .el-transfer-panel__footer .el-checkbox{padding-left:20px;color:#606266}.el-transfer-panel .el-transfer-panel__empty{margin:0;height:30px;line-height:30px;padding:6px 15px 0;color:#909399;text-align:center}.el-transfer-panel .el-checkbox__label{padding-left:8px}.el-transfer-panel .el-checkbox__inner{height:14px;width:14px;border-radius:3px}.el-transfer-panel .el-checkbox__inner::after{height:6px;width:3px;left:4px}.el-container{display:flex;-webkit-box-orient:horizontal;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-sizing:border-box;box-sizing:border-box;min-width:0}.el-aside,.el-header{-webkit-box-sizing:border-box}.el-container.is-vertical{-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column}.el-header{padding:0 20px;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-aside{overflow:auto;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-footer,.el-main{-webkit-box-sizing:border-box}.el-main{display:block;-webkit-box-flex:1;-ms-flex:1;flex:1;-ms-flex-preferred-size:auto;flex-basis:auto;overflow:auto;box-sizing:border-box;padding:20px}.el-footer{padding:0 20px;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0}.el-timeline{margin:0;font-size:14px;list-style:none}.el-timeline .el-timeline-item:last-child .el-timeline-item__tail{display:none}.el-timeline-item{position:relative;padding-bottom:20px}.el-timeline-item__wrapper{position:relative;padding-left:28px;top:-3px}.el-timeline-item__tail{position:absolute;left:4px;height:100%;border-left:2px solid #E4E7ED}.el-timeline-item__icon{color:#FFF;font-size:13px}.el-timeline-item__node{position:absolute;background-color:#E4E7ED;border-radius:50%;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image__error,.el-timeline-item__dot{display:-webkit-box;display:-ms-flexbox}.el-timeline-item__node--normal{left:-1px;width:12px;height:12px}.el-timeline-item__node--large{left:-2px;width:14px;height:14px}.el-timeline-item__node--primary{background-color:#409EFF}.el-timeline-item__node--success{background-color:#67C23A}.el-timeline-item__node--warning{background-color:#E6A23C}.el-timeline-item__node--danger{background-color:#F56C6C}.el-timeline-item__node--info{background-color:#909399}.el-timeline-item__dot{position:absolute;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-timeline-item__content{color:#303133}.el-timeline-item__timestamp{color:#909399;line-height:1;font-size:13px}.el-timeline-item__timestamp.is-top{margin-bottom:8px;padding-top:4px}.el-timeline-item__timestamp.is-bottom{margin-top:8px}.el-link{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:horizontal;-ms-flex-direction:row;flex-direction:row;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;vertical-align:middle;position:relative;text-decoration:none;outline:0;padding:0}.el-link.is-underline:hover:after{content:"";position:absolute;left:0;right:0;height:0;bottom:0;border-bottom:1px solid #409EFF}.el-link.el-link--default:after,.el-link.el-link--primary.is-underline:hover:after,.el-link.el-link--primary:after{border-color:#409EFF}.el-link.is-disabled{cursor:not-allowed}.el-link [class*=el-icon-]+span{margin-left:5px}.el-link.el-link--default{color:#606266}.el-link.el-link--default:hover{color:#409EFF}.el-link.el-link--default.is-disabled{color:#C0C4CC}.el-link.el-link--primary{color:#409EFF}.el-link.el-link--primary:hover{color:#66b1ff}.el-link.el-link--primary.is-disabled{color:#a0cfff}.el-link.el-link--danger.is-underline:hover:after,.el-link.el-link--danger:after{border-color:#F56C6C}.el-link.el-link--danger{color:#F56C6C}.el-link.el-link--danger:hover{color:#f78989}.el-link.el-link--danger.is-disabled{color:#fab6b6}.el-link.el-link--success.is-underline:hover:after,.el-link.el-link--success:after{border-color:#67C23A}.el-link.el-link--success{color:#67C23A}.el-link.el-link--success:hover{color:#85ce61}.el-link.el-link--success.is-disabled{color:#b3e19d}.el-link.el-link--warning.is-underline:hover:after,.el-link.el-link--warning:after{border-color:#E6A23C}.el-link.el-link--warning{color:#E6A23C}.el-link.el-link--warning:hover{color:#ebb563}.el-link.el-link--warning.is-disabled{color:#f3d19e}.el-link.el-link--info.is-underline:hover:after,.el-link.el-link--info:after{border-color:#909399}.el-link.el-link--info{color:#909399}.el-link.el-link--info:hover{color:#a6a9ad}.el-link.el-link--info.is-disabled{color:#c8c9cc}.el-divider{background-color:#DCDFE6;position:relative}.el-divider--horizontal{display:block;height:1px;width:100%;margin:24px 0}.el-divider--vertical{display:inline-block;width:1px;height:1em;margin:0 8px;vertical-align:middle;position:relative}.el-divider__text{position:absolute;background-color:#FFF;padding:0 20px;color:#303133}.el-image__error,.el-image__placeholder{background:#F5F7FA}.el-divider__text.is-left{left:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-divider__text.is-center{left:50%;-webkit-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%)}.el-divider__text.is-right{right:20px;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.el-image__error,.el-image__inner,.el-image__placeholder{width:100%;height:100%}.el-image{position:relative;display:inline-block;overflow:hidden}.el-image__inner{vertical-align:top}.el-image__inner--center{position:relative;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);display:block}.el-image__error{display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;font-size:14px;color:#C0C4CC;vertical-align:middle}.el-image__preview{cursor:pointer}.el-image-viewer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0}.el-image-viewer__btn{position:absolute;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;border-radius:50%;opacity:.8;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box;user-select:none}.el-image-viewer__close{top:40px;right:40px;width:40px;height:40px;font-size:40px}.el-image-viewer__canvas{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.el-image-viewer__actions{left:50%;bottom:30px;-webkit-transform:translateX(-50%);transform:translateX(-50%);width:282px;height:44px;padding:0 23px;background-color:#606266;border-color:#fff;border-radius:22px}.el-image-viewer__actions__inner{width:100%;height:100%;text-align:justify;cursor:default;font-size:23px;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-pack:distribute;justify-content:space-around}.el-image-viewer__next,.el-image-viewer__prev{top:50%;width:44px;height:44px;font-size:24px;color:#fff;background-color:#606266;border-color:#fff}.el-image-viewer__prev{-webkit-transform:translateY(-50%);transform:translateY(-50%);left:40px}.el-image-viewer__next{-webkit-transform:translateY(-50%);transform:translateY(-50%);right:40px;text-indent:2px}.el-image-viewer__mask{position:absolute;width:100%;height:100%;top:0;left:0;opacity:.5;background:#000}.viewer-fade-enter-active{-webkit-animation:viewer-fade-in .3s;animation:viewer-fade-in .3s}.viewer-fade-leave-active{-webkit-animation:viewer-fade-out .3s;animation:viewer-fade-out .3s}@-webkit-keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes viewer-fade-in{0%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes viewer-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}@keyframes viewer-fade-out{0%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}100%{-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0);opacity:0}}.el-calendar{background-color:#fff}.el-calendar__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:12px 20px;border-bottom:1px solid #EBEEF5}.el-backtop,.el-page-header{display:-webkit-box;display:-ms-flexbox}.el-calendar__title{color:#000;-ms-flex-item-align:center;align-self:center}.el-calendar__body{padding:12px 20px 35px}.el-calendar-table{table-layout:fixed;width:100%}.el-calendar-table thead th{padding:12px 0;color:#606266;font-weight:400}.el-calendar-table:not(.is-range) td.next,.el-calendar-table:not(.is-range) td.prev{color:#C0C4CC}.el-backtop,.el-calendar-table td.is-today{color:#409EFF}.el-calendar-table td{border-bottom:1px solid #EBEEF5;border-right:1px solid #EBEEF5;vertical-align:top;-webkit-transition:background-color .2s ease;transition:background-color .2s ease}.el-calendar-table td.is-selected{background-color:#F2F8FE}.el-calendar-table tr:first-child td{border-top:1px solid #EBEEF5}.el-calendar-table tr td:first-child{border-left:1px solid #EBEEF5}.el-calendar-table tr.el-calendar-table__row--hide-border td{border-top:none}.el-calendar-table .el-calendar-day{-webkit-box-sizing:border-box;box-sizing:border-box;padding:8px;height:85px}.el-calendar-table .el-calendar-day:hover{cursor:pointer;background-color:#F2F8FE}.el-backtop{position:fixed;background-color:#FFF;width:40px;height:40px;border-radius:50%;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:20px;-webkit-box-shadow:0 0 6px rgba(0,0,0,.12);box-shadow:0 0 6px rgba(0,0,0,.12);cursor:pointer;z-index:5}.el-backtop:hover{background-color:#F2F6FC}.el-page-header{display:flex;line-height:24px}.el-page-header__left{display:-webkit-box;display:-ms-flexbox;display:flex;cursor:pointer;margin-right:40px;position:relative}.el-page-header__left::after{content:"";position:absolute;width:1px;height:16px;right:-20px;top:50%;-webkit-transform:translateY(-50%);transform:translateY(-50%);background-color:#DCDFE6}.el-checkbox,.el-checkbox__input{display:inline-block;position:relative;white-space:nowrap}.el-page-header__left .el-icon-back{font-size:18px;margin-right:6px;-ms-flex-item-align:center;align-self:center}.el-page-header__title{font-size:14px;font-weight:500}.el-page-header__content{font-size:18px;color:#303133}.el-checkbox{color:#606266;font-weight:500;font-size:14px;cursor:pointer;user-select:none;margin-right:30px}.el-checkbox-button__inner,.el-radio{font-weight:500;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.el-checkbox.is-bordered{padding:9px 20px 9px 10px;border-radius:4px;border:1px solid #DCDFE6;-webkit-box-sizing:border-box;box-sizing:border-box;line-height:normal;height:40px}.el-checkbox.is-bordered.is-checked{border-color:#409EFF}.el-checkbox.is-bordered.is-disabled{border-color:#EBEEF5;cursor:not-allowed}.el-checkbox.is-bordered+.el-checkbox.is-bordered{margin-left:10px}.el-checkbox.is-bordered.el-checkbox--medium{padding:7px 20px 7px 10px;border-radius:4px;height:36px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__label{line-height:17px;font-size:14px}.el-checkbox.is-bordered.el-checkbox--medium .el-checkbox__inner{height:14px;width:14px}.el-checkbox.is-bordered.el-checkbox--small{padding:5px 15px 5px 10px;border-radius:3px;height:32px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:15px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__inner::after{height:6px;width:2px}.el-checkbox.is-bordered.el-checkbox--mini{padding:3px 15px 3px 10px;border-radius:3px;height:28px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__label{line-height:12px;font-size:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner{height:12px;width:12px}.el-checkbox.is-bordered.el-checkbox--mini .el-checkbox__inner::after{height:6px;width:2px}.el-checkbox__input{cursor:pointer;outline:0;line-height:1;vertical-align:middle}.el-checkbox__input.is-disabled .el-checkbox__inner{background-color:#edf2fc;border-color:#DCDFE6;cursor:not-allowed}.el-checkbox__input.is-disabled .el-checkbox__inner::after{cursor:not-allowed;border-color:#C0C4CC}.el-checkbox__input.is-disabled .el-checkbox__inner+.el-checkbox__label{cursor:not-allowed}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner{background-color:#F2F6FC;border-color:#DCDFE6}.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner::after{border-color:#C0C4CC}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner{background-color:#F2F6FC;border-color:#DCDFE6}.el-checkbox__input.is-disabled.is-indeterminate .el-checkbox__inner::before{background-color:#C0C4CC;border-color:#C0C4CC}.el-checkbox__input.is-checked .el-checkbox__inner,.el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#409EFF;border-color:#409EFF}.el-checkbox__input.is-disabled+span.el-checkbox__label{color:#C0C4CC;cursor:not-allowed}.el-checkbox__input.is-checked .el-checkbox__inner::after{-webkit-transform:rotate(45deg) scaleY(1);transform:rotate(45deg) scaleY(1)}.el-checkbox__input.is-checked+.el-checkbox__label{color:#409EFF}.el-checkbox__input.is-focus .el-checkbox__inner{border-color:#409EFF}.el-checkbox__input.is-indeterminate .el-checkbox__inner::before{content:'';position:absolute;display:block;background-color:#FFF;height:2px;-webkit-transform:scale(.5);transform:scale(.5);left:0;right:0;top:5px}.el-checkbox__input.is-indeterminate .el-checkbox__inner::after{display:none}.el-checkbox__inner{display:inline-block;position:relative;border:1px solid #DCDFE6;border-radius:2px;-webkit-box-sizing:border-box;box-sizing:border-box;width:14px;height:14px;background-color:#FFF;z-index:1;-webkit-transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46);transition:border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46)}.el-checkbox__inner:hover{border-color:#409EFF}.el-checkbox__inner::after{-webkit-box-sizing:content-box;box-sizing:content-box;content:"";border:1px solid #FFF;border-left:0;border-top:0;height:7px;left:4px;position:absolute;top:1px;-webkit-transform:rotate(45deg) scaleY(0);transform:rotate(45deg) scaleY(0);width:3px;-webkit-transition:-webkit-transform .15s ease-in .05s;transition:-webkit-transform .15s ease-in .05s;transition:transform .15s ease-in .05s;transition:transform .15s ease-in .05s,-webkit-transform .15s ease-in .05s;-webkit-transform-origin:center;transform-origin:center}.el-checkbox__original{opacity:0;outline:0;position:absolute;margin:0;width:0;height:0;z-index:-1}.el-checkbox-button,.el-checkbox-button__inner{display:inline-block;position:relative}.el-checkbox__label{display:inline-block;padding-left:10px;line-height:19px;font-size:14px}.el-checkbox:last-of-type{margin-right:0}.el-checkbox-button__inner{line-height:1;white-space:nowrap;vertical-align:middle;cursor:pointer;background:#FFF;border:1px solid #DCDFE6;border-left:0;color:#606266;-webkit-appearance:none;text-align:center;-webkit-box-sizing:border-box;box-sizing:border-box;outline:0;margin:0;-webkit-transition:all .3s cubic-bezier(.645,.045,.355,1);transition:all .3s cubic-bezier(.645,.045,.355,1);padding:12px 20px;font-size:14px;border-radius:0}.el-checkbox-button__inner.is-round{padding:12px 20px}.el-checkbox-button__inner:hover{color:#409EFF}.el-checkbox-button__inner [class*=el-icon-]{line-height:.9}.el-radio,.el-radio__input{line-height:1;outline:0;white-space:nowrap}.el-checkbox-button__inner [class*=el-icon-]+span{margin-left:5px}.el-checkbox-button__original{opacity:0;outline:0;position:absolute;margin:0;z-index:-1}.el-radio,.el-radio__inner,.el-radio__input{position:relative;display:inline-block}.el-checkbox-button.is-checked .el-checkbox-button__inner{color:#FFF;background-color:#409EFF;border-color:#409EFF;-webkit-box-shadow:-1px 0 0 0 #8cc5ff;box-shadow:-1px 0 0 0 #8cc5ff}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner{border-left-color:#409EFF}.el-checkbox-button.is-disabled .el-checkbox-button__inner{color:#C0C4CC;cursor:not-allowed;background-image:none;background-color:#FFF;border-color:#EBEEF5;-webkit-box-shadow:none;box-shadow:none}.el-checkbox-button.is-disabled:first-child .el-checkbox-button__inner{border-left-color:#EBEEF5}.el-checkbox-button:first-child .el-checkbox-button__inner{border-left:1px solid #DCDFE6;border-radius:4px 0 0 4px;-webkit-box-shadow:none!important;box-shadow:none!important}.el-checkbox-button.is-focus .el-checkbox-button__inner{border-color:#409EFF}.el-checkbox-button:last-child .el-checkbox-button__inner{border-radius:0 4px 4px 0}.el-checkbox-button--medium .el-checkbox-button__inner{padding:10px 20px;font-size:14px;border-radius:0}.el-checkbox-button--medium .el-checkbox-button__inner.is-round{padding:10px 20px}.el-checkbox-button--small .el-checkbox-button__inner{padding:9px 15px;font-size:12px;border-radius:0}.el-checkbox-button--small .el-checkbox-button__inner.is-round{padding:9px 15px}.el-checkbox-button--mini .el-checkbox-button__inner{padding:7px 15px;font-size:12px;border-radius:0}.el-checkbox-button--mini .el-checkbox-button__inner.is-round{padding:7px 15px}.el-checkbox-group{font-size:0}.el-radio,.el-radio--medium.is-bordered .el-radio__label{font-size:14px}.el-radio{color:#606266;cursor:pointer;margin-right:30px}.el-cascader-node>.el-radio,.el-radio:last-child{margin-right:0}.el-radio.is-bordered{padding:12px 20px 0 10px;border-radius:4px;border:1px solid #DCDFE6;-webkit-box-sizing:border-box;box-sizing:border-box;height:40px}.el-radio.is-bordered.is-checked{border-color:#409EFF}.el-radio.is-bordered.is-disabled{cursor:not-allowed;border-color:#EBEEF5}.el-radio__input.is-disabled .el-radio__inner,.el-radio__input.is-disabled.is-checked .el-radio__inner{background-color:#F5F7FA;border-color:#E4E7ED}.el-radio.is-bordered+.el-radio.is-bordered{margin-left:10px}.el-radio--medium.is-bordered{padding:10px 20px 0 10px;border-radius:4px;height:36px}.el-radio--mini.is-bordered .el-radio__label,.el-radio--small.is-bordered .el-radio__label{font-size:12px}.el-radio--medium.is-bordered .el-radio__inner{height:14px;width:14px}.el-radio--small.is-bordered{padding:8px 15px 0 10px;border-radius:3px;height:32px}.el-radio--small.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio--mini.is-bordered{padding:6px 15px 0 10px;border-radius:3px;height:28px}.el-radio--mini.is-bordered .el-radio__inner{height:12px;width:12px}.el-radio__input{cursor:pointer;vertical-align:middle}.el-radio__input.is-disabled .el-radio__inner{cursor:not-allowed}.el-radio__input.is-disabled .el-radio__inner::after{cursor:not-allowed;background-color:#F5F7FA}.el-radio__input.is-disabled .el-radio__inner+.el-radio__label{cursor:not-allowed}.el-radio__input.is-disabled.is-checked .el-radio__inner::after{background-color:#C0C4CC}.el-radio__input.is-disabled+span.el-radio__label{color:#C0C4CC;cursor:not-allowed}.el-radio__input.is-checked .el-radio__inner{border-color:#409EFF;background:#409EFF}.el-radio__input.is-checked .el-radio__inner::after{-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}.el-radio__input.is-checked+.el-radio__label{color:#409EFF}.el-radio__input.is-focus .el-radio__inner{border-color:#409EFF}.el-radio__inner{border:1px solid #DCDFE6;border-radius:100%;width:14px;height:14px;background-color:#FFF;cursor:pointer;-webkit-box-sizing:border-box;box-sizing:border-box}.el-radio__inner:hover{border-color:#409EFF}.el-radio__inner::after{width:4px;height:4px;border-radius:100%;background-color:#FFF;content:"";position:absolute;left:50%;top:50%;-webkit-transform:translate(-50%,-50%) scale(0);transform:translate(-50%,-50%) scale(0);-webkit-transition:-webkit-transform .15s ease-in;transition:-webkit-transform .15s ease-in;transition:transform .15s ease-in;transition:transform .15s ease-in,-webkit-transform .15s ease-in}.el-radio__original{opacity:0;outline:0;position:absolute;z-index:-1;top:0;left:0;right:0;bottom:0;margin:0}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{-webkit-box-shadow:0 0 2px 2px #409EFF;box-shadow:0 0 2px 2px #409EFF}.el-radio__label{font-size:14px;padding-left:10px}.el-scrollbar{overflow:hidden;position:relative}.el-scrollbar:active>.el-scrollbar__bar,.el-scrollbar:focus>.el-scrollbar__bar,.el-scrollbar:hover>.el-scrollbar__bar{opacity:1;-webkit-transition:opacity 340ms ease-out;transition:opacity 340ms ease-out}.el-scrollbar__wrap{overflow:scroll;height:100%}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{width:0;height:0}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:rgba(144,147,153,.3);-webkit-transition:.3s background-color;transition:.3s background-color}.el-scrollbar__thumb:hover{background-color:rgba(144,147,153,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px;opacity:0;-webkit-transition:opacity 120ms ease-out;transition:opacity 120ms ease-out}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-cascader-panel{display:-webkit-box;display:-ms-flexbox;display:flex;border-radius:4px;font-size:14px}.el-cascader-node,.el-drawer{display:-webkit-box;display:-ms-flexbox}.el-cascader-panel.is-bordered{border:1px solid #E4E7ED;border-radius:4px}.el-cascader-menu{min-width:180px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;border-right:solid 1px #E4E7ED}.el-cascader-menu:last-child{border-right:none}.el-cascader-menu:last-child .el-cascader-node{padding-right:20px}.el-cascader-menu__wrap{height:204px}.el-cascader-menu__list{position:relative;min-height:100%;margin:0;padding:6px 0;list-style:none;-webkit-box-sizing:border-box;box-sizing:border-box}.el-avatar,.el-drawer{-webkit-box-sizing:border-box;overflow:hidden}.el-cascader-menu__hover-zone{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.el-cascader-menu__empty-text{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);text-align:center;color:#C0C4CC}.el-cascader-node{position:relative;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0 30px 0 20px;height:34px;line-height:34px;outline:0}.el-cascader-node.is-selectable.in-active-path{color:#606266}.el-cascader-node.in-active-path,.el-cascader-node.is-active,.el-cascader-node.is-selectable.in-checked-path{color:#409EFF;font-weight:700}.el-cascader-node:not(.is-disabled){cursor:pointer}.el-cascader-node:not(.is-disabled):focus,.el-cascader-node:not(.is-disabled):hover{background:#F5F7FA}.el-cascader-node.is-disabled{color:#C0C4CC;cursor:not-allowed}.el-cascader-node__prefix{position:absolute;left:10px}.el-cascader-node__postfix{position:absolute;right:10px}.el-cascader-node__label{-webkit-box-flex:1;-ms-flex:1;flex:1;padding:0 10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.el-cascader-node>.el-radio .el-radio__label{padding-left:0}.el-avatar{display:inline-block;box-sizing:border-box;text-align:center;color:#fff;background:#C0C4CC;width:40px;height:40px;line-height:40px;font-size:14px}.el-avatar>img{display:block;height:100%;vertical-align:middle}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:4px}.el-avatar--icon{font-size:18px}.el-avatar--large{width:40px;height:40px;line-height:40px}.el-avatar--medium{width:36px;height:36px;line-height:36px}.el-avatar--small{width:28px;height:28px;line-height:28px}.el-drawer.btt,.el-drawer.ttb,.el-drawer__container{left:0;right:0;width:100%}.el-drawer.ltr,.el-drawer.rtl,.el-drawer__container{top:0;bottom:0;height:100%}@-webkit-keyframes el-drawer-fade-in{0%{opacity:0}100%{opacity:1}}@keyframes el-drawer-fade-in{0%{opacity:0}100%{opacity:1}}@-webkit-keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes rtl-drawer-in{0%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes rtl-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}}@keyframes rtl-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(100%,0);transform:translate(100%,0)}}@-webkit-keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes ltr-drawer-in{0%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes ltr-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}}@keyframes ltr-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(-100%,0);transform:translate(-100%,0)}}@-webkit-keyframes ttb-drawer-in{0%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes ttb-drawer-in{0%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes ttb-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}}@keyframes ttb-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,-100%);transform:translate(0,-100%)}}@-webkit-keyframes btt-drawer-in{0%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@keyframes btt-drawer-in{0%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}100%{-webkit-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes btt-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}}@keyframes btt-drawer-out{0%{-webkit-transform:translate(0,0);transform:translate(0,0)}100%{-webkit-transform:translate(0,100%);transform:translate(0,100%)}}.el-drawer{position:absolute;box-sizing:border-box;background-color:#FFF;display:flex;-webkit-box-orient:vertical;-ms-flex-direction:column;flex-direction:column;-webkit-box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.el-drawer.rtl{-webkit-animation:rtl-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;animation:rtl-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;right:0}.el-drawer__open .el-drawer.rtl{-webkit-animation:rtl-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s;animation:rtl-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s}.el-drawer.ltr{-webkit-animation:ltr-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;animation:ltr-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;left:0}.el-drawer__open .el-drawer.ltr{-webkit-animation:ltr-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s;animation:ltr-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s}.el-drawer.ttb{-webkit-animation:ttb-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;animation:ttb-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;top:0}.el-drawer__open .el-drawer.ttb{-webkit-animation:ttb-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s;animation:ttb-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s}.el-drawer.btt{-webkit-animation:btt-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;animation:btt-drawer-out 225ms cubic-bezier(0,0,.2,1) 0s;bottom:0}.el-drawer__open .el-drawer.btt{-webkit-animation:btt-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s;animation:btt-drawer-in 225ms cubic-bezier(0,0,.2,1) 0s}.el-drawer__header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#72767b;display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:32px;padding:20px 20px 0}.el-drawer__header>:first-child{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__title{margin:0;-webkit-box-flex:1;-ms-flex:1;flex:1;line-height:inherit;font-size:1rem}.el-drawer__close-btn{border:none;cursor:pointer;font-size:20px;color:inherit;background-color:transparent}.el-drawer__body{-webkit-box-flex:1;-ms-flex:1;flex:1}.el-drawer__body>*{-webkit-box-sizing:border-box;box-sizing:border-box}.el-drawer__container{position:relative}.el-drawer-fade-enter-active{-webkit-animation:el-drawer-fade-in 225ms cubic-bezier(0,0,.2,1) 0s;animation:el-drawer-fade-in 225ms cubic-bezier(0,0,.2,1) 0s}.el-drawer-fade-leave-active{animation:el-drawer-fade-in 225ms cubic-bezier(0,0,.2,1) 0s reverse} \ No newline at end of file diff --git a/src/main/resources/static/dist/index.min.js b/src/main/resources/static/dist/index.min.js new file mode 100644 index 0000000..6c64797 --- /dev/null +++ b/src/main/resources/static/dist/index.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("vue")):"function"==typeof define&&define.amd?define("ELEMENT",["vue"],t):"object"==typeof exports?exports.ELEMENT=t(require("vue")):e.ELEMENT=t(e.Vue)}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function i(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/dist/",i(i.s=49)}([function(t,i){t.exports=e},function(e,t,i){var n=i(4);e.exports=function(e,t,i){return void 0===i?n(e,t,!1):n(e,i,!1!==t)}},function(e,t,i){var n;!function(r){"use strict";var s={},a=/d{1,4}|M{1,4}|yy(?:yy)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g,o="[^\\s]+",l=/\[([^]*?)\]/gm,u=function(){};function c(e,t){for(var i=[],n=0,r=e.length;n3?0:(e-e%10!=10)*e%10]}};var g={D:function(e){return e.getDay()},DD:function(e){return d(e.getDay())},Do:function(e,t){return t.DoFn(e.getDate())},d:function(e){return e.getDate()},dd:function(e){return d(e.getDate())},ddd:function(e,t){return t.dayNamesShort[e.getDay()]},dddd:function(e,t){return t.dayNames[e.getDay()]},M:function(e){return e.getMonth()+1},MM:function(e){return d(e.getMonth()+1)},MMM:function(e,t){return t.monthNamesShort[e.getMonth()]},MMMM:function(e,t){return t.monthNames[e.getMonth()]},yy:function(e){return d(String(e.getFullYear()),4).substr(2)},yyyy:function(e){return d(e.getFullYear(),4)},h:function(e){return e.getHours()%12||12},hh:function(e){return d(e.getHours()%12||12)},H:function(e){return e.getHours()},HH:function(e){return d(e.getHours())},m:function(e){return e.getMinutes()},mm:function(e){return d(e.getMinutes())},s:function(e){return e.getSeconds()},ss:function(e){return d(e.getSeconds())},S:function(e){return Math.round(e.getMilliseconds()/100)},SS:function(e){return d(Math.round(e.getMilliseconds()/10),2)},SSS:function(e){return d(e.getMilliseconds(),3)},a:function(e,t){return e.getHours()<12?t.amPm[0]:t.amPm[1]},A:function(e,t){return e.getHours()<12?t.amPm[0].toUpperCase():t.amPm[1].toUpperCase()},ZZ:function(e){var t=e.getTimezoneOffset();return(t>0?"-":"+")+d(100*Math.floor(Math.abs(t)/60)+Math.abs(t)%60,4)}},b={d:["\\d\\d?",function(e,t){e.day=t}],Do:["\\d\\d?"+o,function(e,t){e.day=parseInt(t,10)}],M:["\\d\\d?",function(e,t){e.month=t-1}],yy:["\\d\\d?",function(e,t){var i=+(""+(new Date).getFullYear()).substr(0,2);e.year=""+(t>68?i-1:i)+t}],h:["\\d\\d?",function(e,t){e.hour=t}],m:["\\d\\d?",function(e,t){e.minute=t}],s:["\\d\\d?",function(e,t){e.second=t}],yyyy:["\\d{4}",function(e,t){e.year=t}],S:["\\d",function(e,t){e.millisecond=100*t}],SS:["\\d{2}",function(e,t){e.millisecond=10*t}],SSS:["\\d{3}",function(e,t){e.millisecond=t}],D:["\\d\\d?",u],ddd:[o,u],MMM:[o,h("monthNamesShort")],MMMM:[o,h("monthNames")],a:[o,function(e,t,i){var n=t.toLowerCase();n===i.amPm[0]?e.isPm=!1:n===i.amPm[1]&&(e.isPm=!0)}],ZZ:["[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z",function(e,t){var i,n=(t+"").match(/([+-]|\d\d)/gi);n&&(i=60*n[1]+parseInt(n[2],10),e.timezoneOffset="+"===n[0]?i:-i)}]};b.dd=b.d,b.dddd=b.ddd,b.DD=b.D,b.mm=b.m,b.hh=b.H=b.HH=b.h,b.MM=b.M,b.ss=b.s,b.A=b.a,s.masks={default:"ddd MMM dd yyyy HH:mm:ss",shortDate:"M/D/yy",mediumDate:"MMM d, yyyy",longDate:"MMMM d, yyyy",fullDate:"dddd, MMMM d, yyyy",shortTime:"HH:mm",mediumTime:"HH:mm:ss",longTime:"HH:mm:ss.SSS"},s.format=function(e,t,i){var n=i||s.i18n;if("number"==typeof e&&(e=new Date(e)),"[object Date]"!==Object.prototype.toString.call(e)||isNaN(e.getTime()))throw new Error("Invalid Date in fecha.format");t=s.masks[t]||t||s.masks.default;var r=[];return(t=(t=t.replace(l,function(e,t){return r.push(t),"@@@"})).replace(a,function(t){return t in g?g[t](e,n):t.slice(1,t.length-1)})).replace(/@@@/g,function(){return r.shift()})},s.parse=function(e,t,i){var n=i||s.i18n;if("string"!=typeof t)throw new Error("Invalid format in fecha.parse");if(t=s.masks[t]||t,e.length>1e3)return null;var r={},o=[],u=[];t=t.replace(l,function(e,t){return u.push(t),"@@@"});var c,h=(c=t,c.replace(/[|\\{()[^$+*?.-]/g,"\\$&")).replace(a,function(e){if(b[e]){var t=b[e];return o.push(t[1]),"("+t[0]+")"}return e});h=h.replace(/@@@/g,function(){return u.shift()});var d=e.match(new RegExp(h,"i"));if(!d)return null;for(var p=1;pe?u():!0!==t&&(r=setTimeout(n?function(){r=void 0}:u,void 0===n?e-o:e))}}},function(e,t){var i=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=i)},function(e,t){var i=/^(attrs|props|on|nativeOn|class|style|hook)$/;function n(e,t){return function(){e&&e.apply(this,arguments),t&&t.apply(this,arguments)}}e.exports=function(e){return e.reduce(function(e,t){var r,s,a,o,l;for(a in t)if(r=e[a],s=t[a],r&&i.test(a))if("class"===a&&("string"==typeof r&&(l=r,e[a]=r={},r[l]=!0),"string"==typeof s&&(l=s,t[a]=s={},s[l]=!0)),"on"===a||"nativeOn"===a||"hook"===a)for(o in s)r[o]=n(r[o],s[o]);else if(Array.isArray(r))e[a]=r.concat(s);else if(Array.isArray(s))e[a]=[r].concat(s);else for(o in s)r[o]=s[o];else e[a]=t[a];return e},{})}},function(e,t){var i={}.hasOwnProperty;e.exports=function(e,t){return i.call(e,t)}},function(e,t,i){"use strict";t.__esModule=!0;var n,r=i(56),s=(n=r)&&n.__esModule?n:{default:n};t.default=s.default||function(e){for(var t=1;t0?n:i)(e)}},function(e,t,i){var n=i(28)("keys"),r=i(21);e.exports=function(e){return n[e]||(n[e]=r(e))}},function(e,t,i){var n=i(14),r=i(5),s=r["__core-js_shared__"]||(r["__core-js_shared__"]={});(e.exports=function(e,t){return s[e]||(s[e]=void 0!==t?t:{})})("versions",[]).push({version:n.version,mode:i(20)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t){e.exports={}},function(e,t,i){var n=i(10).f,r=i(7),s=i(13)("toStringTag");e.exports=function(e,t,i){e&&!r(e=i?e:e.prototype,s)&&n(e,s,{configurable:!0,value:t})}},function(e,t,i){t.f=i(13)},function(e,t,i){var n=i(5),r=i(14),s=i(20),a=i(33),o=i(10).f;e.exports=function(e){var t=r.Symbol||(r.Symbol=s?{}:n.Symbol||{});"_"==e.charAt(0)||e in t||o(t,e,{value:a.f(e)})}},function(e,t,i){var n=i(4),r=i(1);e.exports={throttle:n,debounce:r}},function(e,t,i){e.exports=!i(11)&&!i(16)(function(){return 7!=Object.defineProperty(i(37)("div"),"a",{get:function(){return 7}}).a})},function(e,t,i){var n=i(15),r=i(5).document,s=n(r)&&n(r.createElement);e.exports=function(e){return s?r.createElement(e):{}}},function(e,t,i){var n=i(7),r=i(12),s=i(62)(!1),a=i(27)("IE_PROTO");e.exports=function(e,t){var i,o=r(e),l=0,u=[];for(i in o)i!=a&&n(o,i)&&u.push(i);for(;t.length>l;)n(o,i=t[l++])&&(~s(u,i)||u.push(i));return u}},function(e,t,i){var n=i(40);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==n(e)?e.split(""):Object(e)}},function(e,t){var i={}.toString;e.exports=function(e){return i.call(e).slice(8,-1)}},function(e,t,i){var n=i(25);e.exports=function(e){return Object(n(e))}},function(e,t,i){"use strict";var n=i(20),r=i(23),s=i(43),a=i(9),o=i(31),l=i(69),u=i(32),c=i(72),h=i(13)("iterator"),d=!([].keys&&"next"in[].keys()),p=function(){return this};e.exports=function(e,t,i,f,m,v,g){l(i,t,f);var b,y,w,_=function(e){if(!d&&e in S)return S[e];switch(e){case"keys":case"values":return function(){return new i(this,e)}}return function(){return new i(this,e)}},x=t+" Iterator",C="values"==m,k=!1,S=e.prototype,D=S[h]||S["@@iterator"]||m&&S[m],$=D||_(m),E=m?C?_("entries"):$:void 0,T="Array"==t&&S.entries||D;if(T&&(w=c(T.call(new e)))!==Object.prototype&&w.next&&(u(w,x,!0),n||"function"==typeof w[h]||a(w,h,p)),C&&D&&"values"!==D.name&&(k=!0,$=function(){return D.call(this)}),n&&!g||!d&&!k&&S[h]||a(S,h,$),o[t]=$,o[x]=p,m)if(b={values:C?$:_("values"),keys:v?$:_("keys"),entries:E},g)for(y in b)y in S||s(S,y,b[y]);else r(r.P+r.F*(d||k),t,b);return b}},function(e,t,i){e.exports=i(9)},function(e,t,i){var n=i(17),r=i(70),s=i(29),a=i(27)("IE_PROTO"),o=function(){},l=function(){var e,t=i(37)("iframe"),n=s.length;for(t.style.display="none",i(71).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" + + + + diff --git a/src/main/resources/templates/404.ftl b/src/main/resources/templates/404.ftl new file mode 100644 index 0000000..894aadc --- /dev/null +++ b/src/main/resources/templates/404.ftl @@ -0,0 +1,90 @@ + + + + + + 404-对不起!您访问的页面不存在 + + + + + +
+
+ + + +
+ +
+

抱歉,您所请求的页面不存在或暂时不可用、也可能已被永久移除!

+
+ +
+ + diff --git a/src/main/resources/templates/500.ftl b/src/main/resources/templates/500.ftl new file mode 100644 index 0000000..a71bb99 --- /dev/null +++ b/src/main/resources/templates/500.ftl @@ -0,0 +1,94 @@ + + + + + + 500-服务器走了下神! + + + + + +
+
+ + + +
+ +
+

服务器内部错误,错误信息如下:

+ +

${msg}

+
+ +
+ + + + diff --git a/src/main/resources/templates/control/macro.ftl b/src/main/resources/templates/control/macro.ftl new file mode 100644 index 0000000..2a92c3c --- /dev/null +++ b/src/main/resources/templates/control/macro.ftl @@ -0,0 +1,63 @@ +<#macro print items > +<#nested>: + <#list items as item> + ${item} + + +<#-- + 参数说明: + pageNumber:当前的页码 + pageSize:页码大小 + totalCount:总数 + showPages:显示的页码个数 + callback:回调函数 +--> +<#macro page pageNumber pageSize totalCount showPages callback> +<#if totalCount gt 0> +
+ <#assign totalPage = 0/> + <#assign totalPage = (totalCount/pageSize)?ceiling/> + <#if pageNumber==1> + 首页 + 上一页 + + <#if pageNumber!=1> + 首页 + 上一页 + + <#if pageNumber-showPages/2 gt 0> + <#assign start = pageNumber-(showPages-1)/2/> + <#if showPages gt totalPage> + <#assign start = 1/> + + <#else> + <#assign start = 1/> + + <#if totalPage gt showPages> + <#assign end = (start+showPages-1)/> + <#if end gt totalPage> + <#assign start = totalPage-showPages+1/> + <#assign end = totalPage/> + + <#else> + <#assign end = totalPage/> + + <#assign pages=start..end/> + <#list pages as page> + <#if page==pageNumber> + ${page} + <#else> + ${page} + + + <#if pageNumber==totalPage> + 下一页 + 尾页 + + <#if pageNumber!=totalPage> + 下一页 + 尾页 + +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/control/nav.ftl b/src/main/resources/templates/control/nav.ftl new file mode 100644 index 0000000..c87789a --- /dev/null +++ b/src/main/resources/templates/control/nav.ftl @@ -0,0 +1,779 @@ + \ No newline at end of file diff --git a/src/main/resources/templates/layout/default.ftl b/src/main/resources/templates/layout/default.ftl new file mode 100644 index 0000000..fa93dd2 --- /dev/null +++ b/src/main/resources/templates/layout/default.ftl @@ -0,0 +1,15 @@ + + + + + + + + + + + +<#include controlHolder("nav")/> +<#include screenHolder()/> + + diff --git a/src/main/resources/templates/screen/home.ftl b/src/main/resources/templates/screen/home.ftl new file mode 100644 index 0000000..68bb4fc --- /dev/null +++ b/src/main/resources/templates/screen/home.ftl @@ -0,0 +1,1175 @@ +
+ + + +
+ 主要颜色 +
+ + +
Success +
#67C23A
+
+
+ +
Warning +
#E6A23C
+
+
+ +
Danger +
#F56C6C
+
+
+ +
Info +
#909399
+
+
+
+
+ +
+ 中性色 +
+ + +
+
+ 主要文字 +
#303133
+
+
+ 常规文字 +
#606266
+
+
+ 次要文字 +
#909399
+
+
+ 占位文字 +
#C0C4CC
+
+
+
+ +
+
+ +
+
一级边框 +
#DCDFE6
+
+
二级边框 +
#E4E7ED
+
+
三级边框 +
#EBEEF5
+
+
四级边框 +
#F2F6FC
+
+
+
+
+
+
+ + + +

主要框架

+ +
    +
  1. + SpringBoot v2.1.2.RELEASE +
  2. +
  3. + Freemarker v2.1.2.RELEASE + Html模板引擎 +
  4. +
  5. + pagehelper v1.2.5 + 分页插件、提供便捷的分页功能 +
  6. +
  7. + dozer v5.5.1 + 对象复制拷贝 +
  8. +
  9. + logback v1.2.3 + 日志框架 +
  10. +
  11. + jQuery v3.2.1 + 前端JavaScript框架(保留),打包进base.min.js,但请尽量勿使用jQuery直接操作Dom +
  12. +
  13. + Vue v2.5.17 + DOM渲染引擎 +
  14. +
  15. + ElementUI v2.8.2 + 前端UI框架,提供统一风格控件 +
  16. +
  17. + Maven + 后端构建管理工具 +
  18. +
  19. + JDK 8+ + SpringBoot2.x开始不再支持JDK 7及以下,所以请将JDk升级到8+ +
  20. +
+ +

+
+ + + + + + 调用AJAX + + + +

this.$ajax.login({}).then(function (response) {

+ +

if (response.errors.length > 0) {

+ +

nav.e(response.errors[0].message);

+ +

} else {

+ +

nav.i("Ajax调用成功!");

+ +

}

+ +

})

+ +
+ + + + 点击上传 +
只能上传jpg/png文件,且不超过500kb
+
+ + + +

this.$ajax.upload(req.file).then(function (response) {

+ +

if (response.errors.length > 0) {

+ +

req.onError();

+ +

nav.e(response.errors[0].message);

+ +

} else {

+ +

req.onSuccess();

+ +

nav.i("文件上传成功!");

+ +

}

+ +

})

+
+ +
+ + + + + + 开始推送 + + 停止推送 + + + +

var evtSource = new EventSource('http://localhost:8080/sse/1');

+ +

evtSource.addEventListener('message', function (e) {

+ +

console.log(e.data);

+ +

});

+ +
+
+ + + + 普通消息 + 错误消息 + 成功消息 + 警告消息 + + + + 加载提示框 + 关闭提示框 + + + + 加载提示条 + 成功 + 失败 + + + + + + +
+ 占位输出 +
+ +

正常输出 + ${r'${hello}'} +

+ +

HTML输出 + ${r"${'<a>a标签</a>'?html}"} +

+ +

$表达式 + ${r"${r'${hello}'}"} +

+ +

布尔值输出 + ${r"${true?c}"} +

+ +

三元表达式 + ${r"${true?string('yes','no')}"} +

+ +

输出当前日期 + ${r'${.now?string["yyyy-MM-dd HH:mm:ss"]}'} +

+ +
+ + + +
+ 条件输出 +
+ +

${'<#if status == 0>'}

+ +

status is 0

+ +

${'<#elseif status ==1>'}

+ +

status = 1

+ +

${'<#else>'}

+ +

status != 0 && status !=1

+ +

${'</#if>'}

+ +
+ + + + +
+ 遍历集合 +
+ +

${'<#list citys as city>'}

+ +

${r' ${city_index} --> ${city}'}

+ +

${'</#list>'}

+ +
+ +
+ + + +
+

1、表单控件集合及验证

+
+ + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 立即创建 + 重置 + + + +
+ +
+

2、表格数据

+
+ + + + + + + + + + + + + + + + +
+ + +
+

3、分页

+
+ + + + +
+ + +
+

4、按钮

+
+ + + 默认按钮 + 主要按钮 + 成功按钮 + 信息按钮 + 警告按钮 + 危险按钮 + + + + 朴素按钮 + 主要按钮 + 成功按钮 + 信息按钮 + 警告按钮 + 危险按钮 + + + + 圆角按钮 + 主要按钮 + 成功按钮 + 信息按钮 + 警告按钮 + + + + 危险按钮 + 加载中 + + + + + + + + + + 默认按钮 + 主要按钮 + 成功按钮 + 信息按钮 + 警告按钮 + 危险按钮 + + + + 朴素按钮 + 主要按钮 + 成功按钮 + 信息按钮 + 警告按钮 + 危险按钮 + + + 文字按钮 + 文字按钮 + + + + + + 搜索 + 上传 + + + + + 上一页 + 下一页 + + + + + + + + + + 默认按钮 + 中等按钮 + 小型按钮 + 超小按钮 + + + 默认按钮 + 中等按钮 + 小型按钮 + 超小按钮 + + +
+ +
+

5、Link 文字链接

+
+ + + 默认链接 + 主要链接 + 成功链接 + 警告链接 + 危险链接 + 信息链接 + + + + 默认链接 + 主要链接 + 成功链接 + 警告链接 + 危险链接 + 信息链接 + + + + 无下划线 + 有下划线 + 编辑 + 查看 + + +
+ +
+

6、Tabs 标签页

+
+ + + 用户管理 + 配置管理 + 角色管理 + 定时任务补偿 + + +

+ Tabs位置共有4个位置分别为: + + top + + right + + bottom + + left + +

+
+ +
+

7、弹框、对话框、通知

+
+ + + + 消息 + + + 通知 + + + 消息Box + + + 消息确认Box + + + + hover 激活 + + + + + 对话框Dialog + + + 这是一段信息 + + 取 消 + 确 定 + + + + + +
+ + +
+

6、其他小控件

+
+ + + + + + + + + + + + + 标签一 + 标签二 + 标签三 + 标签四 + 标签五 + + + + 标签一 + 标签二 + 标签三 + 标签四 + 标签五 + + + + 默认标签 + 中等标签 + 小型标签 + 超小标签 + + + + + 评论 + + + 回复 + + + 评论 + + + 回复 + + + 数据查询 + + + + + + + + 评论(最大值) + + + 回复(最大值) + + + + 评论 + + + 回复 + + + + + + + + + + + + + + + + + + 首页 + 活动管理 + 活动列表 + 活动详情 + + + + + + 下拉菜单 + + 黄金糕 + 狮子头 + 螺蛳粉 + 双皮奶 + 蚵仔煎 + + + + + 默认尺寸 + + 黄金糕 + 狮子头 + 螺蛳粉 + 双皮奶 + 蚵仔煎 + + + + + 中等尺寸 + + 黄金糕 + 狮子头 + 螺蛳粉 + 双皮奶 + 蚵仔煎 + + + + + 小型尺寸 + + 黄金糕 + 狮子头 + 螺蛳粉 + 双皮奶 + 蚵仔煎 + + + + + 超小尺寸 + + 黄金糕 + 狮子头 + 螺蛳粉 + 双皮奶 + 蚵仔煎 + + + + + 头上一片晴天,心中一个想念 + 少年包青天 + 饿了别叫妈, 叫饿了么 + + 为了无法计算的价值 + 阿里云 + + + + 雨纷纷 + + 旧故里 + + 草木深 + + +
+ +
+ + + +
+

1、页面初始化前闪现源代码

+
+ +

${''?html}

+ + +

${'

'?html}

+ +

${'...'?html}

+ +

${'

'?html}

+
+ + +
+

2、为什么Long类型返回json字符串变为String

+
+ +

因为Long可支持的长度比JavaScript中Number支持的位数更多,所以当Long数值很大时转为Json再转为Number就会丢失精度,造成难以发现的Bug,所以本系统默认处理了该问题。

+
+ + +
+

3、Date类型格式化问题

+
+ +

默认启用全局Date格式化 + yyyy-MM-dd HH:mm:ss + ,需要关闭请在 + application-* + 中关闭。 +

+ +

对于个别返回结果的Date格式化可以通过注解方式 + @JsonFormat(pattern = "yyyy-MM-dd") +

+ +
+ + +
+

4、权限问题

+
+ +

1、本系统启用自定义Token来控制权限,任何访问都会有一个全局Token,任何一处都可以通过 + LocalData.getToken() + 来获取 + 当前访问对象的Token。其中Token充当着访问对象的通行证,其中包含访问对象的 + 用户ID + 、 + 用户名 + 、 + 可访问资源 + 等信息。 +

+ +

2、对于未登录对象会提供临时(访客) + LocalData.getTempToken() + (有限的权限)来提供访问系统。 +

+ +

3、如果系统存在Task等定时任务时,是不存在访问对象,通过 + LocalData.getSystemToken() + 来获取系统层的Token来操作系统。 +

+ +
+ + +
+

5、Token配置

+
+ +

Springboot环境配置文件application-*中配置了系统中需要验证权限和不需要验证的url。而需要验证权限的URl会在 + SecurityConfig.getAuthorization() + 方法中通过Parameter或Cookies中获取当前访问对象的登录信息,从而为当前访问对象组装Token对象。 + +

+ +
+
+
+
+ + diff --git a/src/main/resources/templates/screen/index.ftl b/src/main/resources/templates/screen/index.ftl new file mode 100644 index 0000000..b5325a6 --- /dev/null +++ b/src/main/resources/templates/screen/index.ftl @@ -0,0 +1,381 @@ +
+
+ +
+ +
+ + +
+ +
+ +
+ + + + + +
+
+
+ + + diff --git a/src/main/resources/templates/screen/login.ftl b/src/main/resources/templates/screen/login.ftl new file mode 100644 index 0000000..fea5e3f --- /dev/null +++ b/src/main/resources/templates/screen/login.ftl @@ -0,0 +1,138 @@ +
+
+ +
+ +
+ + + diff --git a/src/test/java/xyz/wbsite/UtilTest.java b/src/test/java/xyz/wbsite/UtilTest.java new file mode 100644 index 0000000..a7db808 --- /dev/null +++ b/src/test/java/xyz/wbsite/UtilTest.java @@ -0,0 +1,99 @@ +package xyz.wbsite; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; +import xyz.wbsite.frame.utils.AESUtil; +import xyz.wbsite.frame.utils.Base64Util; +import xyz.wbsite.frame.utils.IDgenerator; +import xyz.wbsite.frame.utils.MD5Util; +import xyz.wbsite.frame.utils.ProcessUtil; +import xyz.wbsite.frame.utils.RSAUtil; + +/** + * UtilTest - - 测试用例 + * + * @author wangbing + * @version 0.0.1 + * @since 2017-01-01 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +@Transactional +public class UtilTest { + + @Test + public void testGeneratePwd() { + System.out.print("[123456]加密="); + System.out.println(MD5Util.generatePwd("123456")); + } + + @Test + public void testIDgenerator() { + for (int i = 0; i < 10; i++) { + long l = IDgenerator.nextId(); + System.out.println(l); + } + } + + @Test + public void testAESUtil() { + // 加密 + String data = "我有一个苹果"; + String secret = "1234567890123456"; + System.out.println("加密后的Base64密文是:" + AESUtil.encrypt2Base64(data.getBytes(), secret)); + + // 解密 + String encrypt2Base64 = AESUtil.encrypt2Base64(data.getBytes(), secret); + byte[] decrypt = AESUtil.decrypt(Base64Util.decode(encrypt2Base64), secret); + System.out.println("解密后的明文是:" + new String(decrypt)); + } + + @Test + public void testBase64Util() { + String s = Base64Util.encodeToString(("我搜搜").getBytes()); + System.out.println(s); + + byte[] decode = Base64Util.decode(s); + System.out.println(new String(decode)); + } + + @Test + public void testMD5Util() { + String encode = MD5Util.encode("123456"); + System.out.println(encode); + } + + @Test + public void testProcessUtil() { + ProcessUtil.execExe("D:\\example.exe"); + ProcessUtil.execBat("D:\\example.bat"); + } + + @Test + public void testRSAUtil() { + {//创建秘钥对 + RSAUtil.createKey(); + } + + {//加解密 + + //加密 + String encrypt = RSAUtil.encrypt2Base64("我有一个苹果".getBytes()); + System.out.println(encrypt); + + //解密 + String decrypt = RSAUtil.decrypt2String(encrypt); + System.out.println(decrypt); + } + + + String sign = RSAUtil.sign2Base64("我有一个苹果".getBytes()); + System.out.println(sign); + + boolean b = RSAUtil.doCheck("我有一个苹果".getBytes(), sign); + System.out.println(b); + } +} diff --git a/src/test/java/xyz/wbsite/conf/MappingTest.java b/src/test/java/xyz/wbsite/conf/MappingTest.java new file mode 100644 index 0000000..7d9a79e --- /dev/null +++ b/src/test/java/xyz/wbsite/conf/MappingTest.java @@ -0,0 +1,148 @@ +package xyz.wbsite.conf; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; +import xyz.wbsite.frame.auth.Token; +import xyz.wbsite.module.conf.mgr.MappingManager; +import xyz.wbsite.module.conf.req.MappingCreateRequest; +import xyz.wbsite.module.conf.req.MappingDeleteRequest; +import xyz.wbsite.module.conf.req.MappingFindRequest; +import xyz.wbsite.module.conf.req.MappingGetRequest; +import xyz.wbsite.module.conf.req.MappingUpdateRequest; +import xyz.wbsite.module.conf.rsp.MappingCreateResponse; +import xyz.wbsite.module.conf.rsp.MappingDeleteResponse; +import xyz.wbsite.module.conf.rsp.MappingFindResponse; +import xyz.wbsite.module.conf.rsp.MappingGetResponse; +import xyz.wbsite.module.conf.rsp.MappingUpdateResponse; + +import static junit.framework.TestCase.assertTrue; + +/** + * MappingTest - - 映射测试用例 + * + * @author wangbing + * @version 0.0.1 + * @since 2020-03-18 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +@Transactional +public class MappingTest { + + @Autowired + private Token token; + + @Autowired + private MappingManager mappingManager; + + @Test + public void testCreate() { + MappingCreateRequest request = new MappingCreateRequest(); + request.setName("名称"); + request.setPort("端口"); + request.setPath("路径"); + request.setType("类型"); + request.setLocation("代理地址"); + request.setNote("备注"); + + MappingCreateResponse response = mappingManager.create(request,token); + + assertTrue(!response.hasError()); + } + + @Test + public void testDelete() { + + //创建数据 + MappingCreateRequest createRequest = new MappingCreateRequest(); + createRequest.setName("名称"); + createRequest.setPort("端口"); + createRequest.setPath("路径"); + createRequest.setType("类型"); + createRequest.setLocation("代理地址"); + createRequest.setNote("备注"); + + MappingCreateResponse createResponse = mappingManager.create(createRequest,token); + + assertTrue(!createResponse.hasError() && createResponse.getId() > 0); + //删除数据 + MappingDeleteRequest request = new MappingDeleteRequest(); + request.setId(createResponse.getId()); + + MappingDeleteResponse response = mappingManager.delete(request,token); + + assertTrue(!response.hasError() && response.getResult() == 1L); + } + + @Test + public void testUpdate() { + //创建数据 + MappingCreateRequest createRequest = new MappingCreateRequest(); + createRequest.setName("名称"); + createRequest.setPort("端口"); + createRequest.setPath("路径"); + createRequest.setType("类型"); + createRequest.setLocation("代理地址"); + createRequest.setNote("备注"); + + MappingCreateResponse createResponse = mappingManager.create(createRequest, token); + + assertTrue(!createResponse.hasError()); + + //更新数据 + MappingUpdateRequest request = new MappingUpdateRequest(); + request.setId(createResponse.getId()); + request.setName("名称"); + request.setPort("端口"); + request.setPath("路径"); + request.setType("类型"); + request.setLocation("代理地址"); + request.setNote("备注"); + + MappingUpdateResponse response = mappingManager.update(request,token); + + assertTrue(!response.hasError() && response.getResult() == 1L); + } + + @Test + public void testFind() { + MappingFindRequest request = new MappingFindRequest(); + request.setName("名称"); + request.setPort("端口"); + request.setPath("路径"); + request.setType("类型"); + request.setLocation("代理地址"); + + MappingFindResponse response = mappingManager.find(request,token); + + assertTrue(!response.hasError()); + } + + @Test + public void testGet() { + //创建数据 + MappingCreateRequest createRequest = new MappingCreateRequest(); + createRequest.setName("名称"); + createRequest.setPort("端口"); + createRequest.setPath("路径"); + createRequest.setType("类型"); + createRequest.setLocation("代理地址"); + createRequest.setNote("备注"); + + MappingCreateResponse createResponse = mappingManager.create(createRequest, token); + + assertTrue(!createResponse.hasError()); + + //获得数据 + MappingGetRequest request = new MappingGetRequest(); + request.setId(createResponse.getId()); + + MappingGetResponse response = mappingManager.get(request,token); + + assertTrue(!response.hasError() && response.getMapping() != null); + } +} diff --git a/src/test/java/xyz/wbsite/config/TestConfig.java b/src/test/java/xyz/wbsite/config/TestConfig.java new file mode 100644 index 0000000..e9222ce --- /dev/null +++ b/src/test/java/xyz/wbsite/config/TestConfig.java @@ -0,0 +1,17 @@ +package xyz.wbsite.config; + +import xyz.wbsite.frame.auth.Token; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +@Configuration +public class TestConfig { + + @Bean + public Token getTestToken() { + Token token = new Token(); + token.setId(0); + token.setUserId(0); + token.setUserName("system"); + return token; + } +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..b4fa94c --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,71 @@ +# 开发环境 +server.port=8080 +server.servlet.context-path= +spring.mvc.static-path-pattern=/static/** +spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:/file-upload +spring.application.name=nginx-admin +spring.main.banner-mode=CONSOLE +spring.devtools.restart.enabled=true +# 编码配置 +spring.http.encoding.force=true +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +server.tomcat.uri-encoding=UTF-8 +# 日志配置 +logging.path=D:// +logging.levels=DEBUG +# SQLite spring.datasource.url=jdbc:sqlite::resource:example.db +spring.datasource.driver-class-name=org.sqlite.JDBC +spring.datasource.url=jdbc:sqlite:nginx-admin.db?date_string_format=yyyy-MM-dd HH:mm:ss +spring.datasource.username=test +spring.datasource.password=123456 +# mybatis +mybatis.mapper-locations=classpath:**/mpr/*.xml +mybatis.configuration.map-underscore-to-camel-case=true +# pagehelper +pagehelper.autoRuntimeDialect=true +pagehelper.reasonable=false +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql +# jackson 相关配置 +spring.jackson.date-format=yyyy-MM-dd HH:mm:ss +spring.jackson.time-zone=GMT+8 +spring.jackson.default-property-inclusion=non_null +spring.jackson.mapper.sort-properties-alphabetically=true +spring.jackson.deserialization.fail-on-unknown-properties=false +# freemarker +spring.freemarker.enabled=true +spring.freemarker.allow-request-override=false +spring.freemarker.cache=true +spring.freemarker.check-template-location=true +spring.freemarker.charset=UTF-8 +spring.freemarker.content-type=text/html +spring.freemarker.expose-request-attributes=false +spring.freemarker.expose-session-attributes=false +spring.freemarker.expose-spring-macro-helpers=false +spring.freemarker.settings.template_update_delay=1 +spring.freemarker.settings.locale=zh_CN +spring.freemarker.settings.datetime_format=yyyy-MM-dd HH:mm:ss +spring.freemarker.settings.date_format=yyyy-MM-dd +spring.freemarker.settings.number_format=#.## +spring.freemarker.settings.classic_compatible=true +spring.freemarker.settings.whitespace_stripping=true +spring.freemarker.settings.url_escaping_charset=utf-8 +# 文件上传配置 +spring.servlet.multipart.resolveLazily=false +spring.servlet.multipart.max-file-size=100MB +spring.servlet.multipart.max-request-size=100MB +server.tomcat.max-http-post-size=-1 + +# 自定义配置 +# 根路径默认页,'/'跳转至该页 +web.home.page=/index.htm +# 登录页 +web.login.page=/login.htm +# 拦截验证 +web.url.auth.included=/,/**/*.htm,/ajax/**,/api/** +# 直接放行 +web.url.auth.excluded=/login.htm,/ajax/system/User/login +# 超级管理员 +web.url.auth.admin=admin +web.url.auth.pwd=17fac3376f76d65943d1d26d1f7cb1e5 \ No newline at end of file diff --git a/src/test/resources/logback-spring.xml b/src/test/resources/logback-spring.xml new file mode 100644 index 0000000..5d80b9f --- /dev/null +++ b/src/test/resources/logback-spring.xml @@ -0,0 +1,22 @@ + + + + + + debug级别及以上 + + DEBUG + + + %highlight(%d{yyyy-MM-dd HH:mm:ss.SSS} [%-4level] [%thread] [%logger{36}-%method] %ex %msg%n) + + + + + + + + + + +