xusl 2 anos atrás
pai
commit
314db2b682
35 arquivos alterados com 2141 adições e 314 exclusões
  1. 26 1
      backend/pom.xml
  2. 3 3
      backend/src/main/java/com/jiayue/ssi/SsiApplication.java
  3. 4 1
      backend/src/main/java/com/jiayue/ssi/config/MybatisPlusConfig.java
  4. 6 4
      backend/src/main/java/com/jiayue/ssi/config/security/CustomAuthenticationFailureHandler.java
  5. 4 2
      backend/src/main/java/com/jiayue/ssi/config/security/CustomAuthenticationSuccessHandler.java
  6. 4 2
      backend/src/main/java/com/jiayue/ssi/config/security/JwtAuthenticationTokenFilter.java
  7. 76 0
      backend/src/main/java/com/jiayue/ssi/config/security/MailCodeFilter.java
  8. 1 1
      backend/src/main/java/com/jiayue/ssi/config/security/UserServiceImpl.java
  9. 73 0
      backend/src/main/java/com/jiayue/ssi/config/security/VerifyCodeFilter.java
  10. 26 4
      backend/src/main/java/com/jiayue/ssi/config/security/WebSecurityConfig.java
  11. 49 0
      backend/src/main/java/com/jiayue/ssi/constant/CacheConstants.java
  12. 114 0
      backend/src/main/java/com/jiayue/ssi/controller/SysUserController.java
  13. 119 0
      backend/src/main/java/com/jiayue/ssi/controller/UserLoginController.java
  14. 3 0
      backend/src/main/java/com/jiayue/ssi/entity/SysUser.java
  15. 1 1
      backend/src/main/java/com/jiayue/ssi/mapper/SysUserMapper.java
  16. 41 0
      backend/src/main/java/com/jiayue/ssi/service/SysUserService.java
  17. 70 0
      backend/src/main/java/com/jiayue/ssi/service/impl/SysUserServiceImpl.java
  18. 49 0
      backend/src/main/java/com/jiayue/ssi/util/IdUtils.java
  19. 9 6
      backend/src/main/java/com/jiayue/ssi/util/JwtTokenUtil.java
  20. 79 0
      backend/src/main/java/com/jiayue/ssi/util/LocalCache.java
  21. 27 0
      backend/src/main/java/com/jiayue/ssi/util/RandomUtil.java
  22. 484 0
      backend/src/main/java/com/jiayue/ssi/util/UUID.java
  23. 26 0
      backend/src/main/java/com/jiayue/ssi/util/UtilException.java
  24. 1 1
      backend/src/main/resources/banner.txt
  25. 1 1
      backend/src/test/java/com/jiayue/ssi/BaseTest.java
  26. 1 46
      backend/src/test/java/com/jiayue/ssi/service/DataHandleServiceTest.java
  27. 7 0
      pom.xml
  28. BIN
      ui/src/assets/images/login-background.jpg
  29. 1 0
      ui/src/icons/svg/mailbox.svg
  30. 1 0
      ui/src/icons/svg/verifyCode.svg
  31. 1 1
      ui/src/main.js
  32. 6 6
      ui/src/router/index.js
  33. 4 0
      ui/src/utils/request.js
  34. 282 234
      ui/src/views/login/index.vue
  35. 542 0
      ui/src/views/sysManager/userManager/index.vue

+ 26 - 1
backend/pom.xml

@@ -150,7 +150,32 @@
             <artifactId>commons-lang3</artifactId>
             <version>3.12.0</version>
         </dependency>
-
+        <!--配置文件处理器-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!--jasypt配置文件加解密-->
+        <dependency>
+            <groupId>com.github.ulisesbocchio</groupId>
+            <artifactId>jasypt-spring-boot-starter</artifactId>
+            <version>${jasypt-boot.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+            <version>1.6.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 3 - 3
backend/src/main/java/com/jiayue/ssi/Application.java → backend/src/main/java/com/jiayue/ssi/SsiApplication.java

@@ -11,10 +11,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
  * @version 3.0
  */
 @SpringBootApplication
-@MapperScan("com.backcore.mapper")
-public class Application {
+@MapperScan("com.jiayue.ssi.mapper")
+public class SsiApplication {
     public static void main(String[] args) {
-        SpringApplication.run(Application.class, args);
+        SpringApplication.run(SsiApplication.class, args);
     }
 
 }

+ 4 - 1
backend/src/main/java/com/jiayue/ssi/config/MybatisPlusConfig.java

@@ -16,7 +16,10 @@ import java.util.Properties;
  */
 @Configuration
 public class MybatisPlusConfig {
-    // 最新版
+    /**
+     * MybatisPlus注册
+     * @return MybatisPlusInterceptor
+     */
     @Bean
     public MybatisPlusInterceptor mybatisPlusInterceptor() {
         PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);

+ 6 - 4
backend/src/main/java/com/jiayue/ssi/config/security/CustomAuthenticationFailureHandler.java

@@ -10,19 +10,21 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
 /**
- * 失败认证处理
- */
+* 失败认证处理
+* @author xsl
+* @date 2023/2/16
+*/
 @Component("customAuthenticationFailureHandler")
 public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
 
     @Override
     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
-                                        AuthenticationException e) throws IOException, ServletException {
+        AuthenticationException e) throws IOException, ServletException {
         response.addHeader("Access-Control-Allow-Origin", "*");
         response.setContentType("text/html;charset=UTF-8");
         response.setStatus(401);
         response.getWriter().write("用户名或密码错误!");
-//
+        //
     }
 
 }

+ 4 - 2
backend/src/main/java/com/jiayue/ssi/config/security/CustomAuthenticationSuccessHandler.java

@@ -16,8 +16,10 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 
 /**
- * 认证成功处理器
- */
+* 成功认证处理
+* @author xsl
+* @date 2023/2/16
+*/
 @Component("customAuthenticationSuccessHandler")
 public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
     @Autowired

+ 4 - 2
backend/src/main/java/com/jiayue/ssi/config/security/JwtAuthenticationTokenFilter.java

@@ -2,6 +2,7 @@ package com.jiayue.ssi.config.security;
 
 import com.jiayue.ssi.util.JwtTokenUtil;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
@@ -22,10 +23,11 @@ import java.io.IOException;
  * @create: 2020-03-19 13:05
  **/
 @Component
+@Order(10)
 public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
 
     @Autowired
-    private UserService userService;
+    private UserServiceImpl userServiceImpl;
     @Autowired
     private JwtTokenUtil jwtTokenUtil;
 
@@ -37,7 +39,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
         if (!StringUtils.isEmpty(token)) {
             String username = jwtTokenUtil.getUsernameFromToken(token);
             if (username != null && SecurityContextHolder.getContext().getAuthentication() == null){
-                UserDetails userDetails = userService.loadUserByUsername(username);
+                UserDetails userDetails = userServiceImpl.loadUserByUsername(username);
                 if (jwtTokenUtil.validateToken(token, userDetails)){
                     // 将用户信息存入 authentication,方便后续校验
                     UsernamePasswordAuthenticationToken

+ 76 - 0
backend/src/main/java/com/jiayue/ssi/config/security/MailCodeFilter.java

@@ -0,0 +1,76 @@
+package com.jiayue.ssi.config.security;
+
+import com.jiayue.ssi.constant.CacheConstants;
+import com.jiayue.ssi.util.LocalCache;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.GenericFilterBean;
+
+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;
+
+/**
+* 邮箱口令过滤器
+*
+* @author xsl
+* @since 2023/02/20
+*/
+@Component
+@Order(2)
+public class MailCodeFilter extends GenericFilterBean {
+    private String defaultFilterProcessUrl = "/user/login";
+
+    @Override
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+            throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) req;
+        HttpServletResponse response = (HttpServletResponse) res;
+        if ("POST".equalsIgnoreCase(request.getMethod()) && defaultFilterProcessUrl.equals(request.getServletPath())) {
+            // 验证码验证
+            String username = request.getParameter("username");
+            Object mailCode = LocalCache.get(CacheConstants.MAIL_CODE_KEY + username);
+
+            // 校验服务端验证码
+            if (mailCode==null || "".equals(mailCode)){
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("邮箱口令失效!");
+                return;
+            }
+            // 页面录入的邮箱口令
+            String mailbox = request.getParameter("mailbox");
+
+            // 校验页面验证码
+            if (StringUtils.isEmpty(mailbox)) {
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("非法访问,邮箱口令错误!");
+                return;
+            }
+            if (mailbox.length()!=6){
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("非法访问,邮箱口令错误!");
+                return;
+            }
+            if (!String.valueOf(mailCode).toLowerCase().equals(mailbox.toLowerCase())) {
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("邮箱口令错误!");
+                return;
+            }
+        }
+        chain.doFilter(request, response);
+    }
+}

+ 1 - 1
backend/src/main/java/com/jiayue/ssi/config/security/UserService.java → backend/src/main/java/com/jiayue/ssi/config/security/UserServiceImpl.java

@@ -16,7 +16,7 @@ import org.springframework.stereotype.Service;
  * @version 3.0
  */
 @Service
-public class UserService implements UserDetailsService {
+public class UserServiceImpl implements UserDetailsService {
     @Autowired
     SysUserMapper sysUserMapper;
 

+ 73 - 0
backend/src/main/java/com/jiayue/ssi/config/security/VerifyCodeFilter.java

@@ -0,0 +1,73 @@
+package com.jiayue.ssi.config.security;
+
+import com.jiayue.ssi.constant.CacheConstants;
+import com.jiayue.ssi.util.LocalCache;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.GenericFilterBean;
+
+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;
+
+/**
+* 验证码过滤器
+*
+* @author xsl
+* @since 2023/02/20
+*/
+@Component
+@Order(1)
+public class VerifyCodeFilter extends GenericFilterBean {
+    private String defaultFilterProcessUrl = "/user/login";
+
+    @Override
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+            throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) req;
+        HttpServletResponse response = (HttpServletResponse) res;
+        if ("POST".equalsIgnoreCase(request.getMethod()) && defaultFilterProcessUrl.equals(request.getServletPath())) {
+            // 验证码验证
+            String requestCaptcha = request.getParameter("code");
+            String verifyuuid = request.getParameter("verifyuuid");
+            Object uuidObj = LocalCache.get(CacheConstants.CAPTCHA_CODE_KEY + verifyuuid);
+            // 校验服务端验证码
+            if (uuidObj==null || "".equals(uuidObj)){
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("验证码失效!");
+                return;
+            }
+            // 校验页面验证码
+            if (StringUtils.isEmpty(requestCaptcha)) {
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("非法访问,验证码错误!");
+                return;
+            }
+            if (requestCaptcha.length()!=4){
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("非法访问,验证码错误!");
+                return;
+            }
+            if (!String.valueOf(uuidObj).toLowerCase().equals(requestCaptcha.toLowerCase())) {
+                response.addHeader("Access-Control-Allow-Origin", "*");
+                response.setContentType("text/html;charset=UTF-8");
+                response.setStatus(401);
+                response.getWriter().write("验证码错误!");
+                return;
+            }
+        }
+        chain.doFilter(request, response);
+    }
+}

+ 26 - 4
backend/src/main/java/com/jiayue/ssi/config/security/WebSecurityConfig.java

@@ -7,6 +7,7 @@ import org.springframework.http.HttpMethod;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 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.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.http.SessionCreationPolicy;
@@ -14,12 +15,17 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
+/**
+* WebSecurityConfig
+* @author xsl
+* @date 2023/2/16
+*/
 @Configuration
 @EnableWebSecurity
 @EnableGlobalMethodSecurity(prePostEnabled = true)
 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     @Autowired
-    UserService userService;
+    UserServiceImpl userServiceImpl;
     @Autowired
     CustomAuthenticationFailureHandler customAuthenticationFailureHandler;
     @Autowired
@@ -30,6 +36,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
     @Autowired
     RestAccessDeniedHandler restAccessDeniedHandler;
+    @Autowired
+    VerifyCodeFilter verifyCodeFilter;
+    @Autowired
+    MailCodeFilter mailCodeFilter;
 
     @Bean
     public PasswordEncoder passwordEncoder() {
@@ -39,25 +49,37 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
     @Override
     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-        auth.userDetailsService(userService);
+        auth.userDetailsService(userServiceImpl);
     }
 
     @Override
     protected void configure(HttpSecurity httpSecurity) throws Exception {
+        httpSecurity.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
+        httpSecurity.addFilterBefore(mailCodeFilter, UsernamePasswordAuthenticationFilter.class);
         httpSecurity
                 // 由于使用的是JWT,我们这里不需要csrf
                 .csrf().disable()
                 // 基于token,所以不需要session
                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                 .and().authorizeRequests()
-                .antMatchers("/user/login","/captchaImage").anonymous()
+//                .antMatchers("/user/login","/captchaImage").permitAll()
                 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
+                .antMatchers("/getVerifyCode","/getMailCode").permitAll()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and().headers().cacheControl();
-        httpSecurity.formLogin().loginProcessingUrl("/user/login").successHandler(customAuthenticationSuccessHandler).failureHandler(customAuthenticationFailureHandler);
+        httpSecurity.formLogin().loginProcessingUrl("/user/login")
+                .successHandler(customAuthenticationSuccessHandler)
+                .failureHandler(customAuthenticationFailureHandler);
         httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
         httpSecurity.exceptionHandling().authenticationEntryPoint(entryPointUnauthorizedHandler).accessDeniedHandler(restAccessDeniedHandler);;
 
     }
+
+    //针对静态资源放行
+    @Override
+    public void configure(WebSecurity web) throws Exception {
+        /*super.configure(web);*/
+        web.ignoring().antMatchers("/static/**", "/assets/**");
+    }
 }

+ 49 - 0
backend/src/main/java/com/jiayue/ssi/constant/CacheConstants.java

@@ -0,0 +1,49 @@
+package com.jiayue.ssi.constant;
+
+/**
+ * 缓存的key 常量
+ *
+ * @author ruoyi
+ */
+public class CacheConstants
+{
+    /**
+     * 登录用户 redis key
+     */
+    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
+
+    /**
+     * 验证码 redis key
+     */
+    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+    /**
+     * 邮箱口令 key
+     */
+    public static final String MAIL_CODE_KEY = "mail_codes:";
+
+    /**
+     * 参数管理 cache key
+     */
+    public static final String SYS_CONFIG_KEY = "sys_config:";
+
+    /**
+     * 字典管理 cache key
+     */
+    public static final String SYS_DICT_KEY = "sys_dict:";
+
+    /**
+     * 防重提交 redis key
+     */
+    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
+
+    /**
+     * 限流 redis key
+     */
+    public static final String RATE_LIMIT_KEY = "rate_limit:";
+
+    /**
+     * 登录账户密码错误次数 redis key
+     */
+    public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
+}

+ 114 - 0
backend/src/main/java/com/jiayue/ssi/controller/SysUserController.java

@@ -0,0 +1,114 @@
+package com.jiayue.ssi.controller;
+
+import com.jiayue.ssi.entity.SysUser;
+import com.jiayue.ssi.service.SysUserService;
+import com.jiayue.ssi.util.ResponseVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 用户信息controller
+ *
+ * @author xsl
+ * @version 3.0
+ */
+@RestController
+@RequestMapping("/sysUserController" )
+@Slf4j
+public class SysUserController {
+    @Autowired
+    SysUserService sysUserService;
+
+    /**
+     * 获取用户信息
+     *
+     * @return 用户信息
+     */
+
+    @GetMapping(value = "/getAll")
+    public ResponseVO getAll() {
+        try {
+            List<SysUser> ps = sysUserService.queryAllUsers();
+            return ResponseVO.success(ps);
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error(" 获取所有用户异常");
+            return ResponseVO.error(null);
+        }
+    }
+
+    /**
+     * 保存用户信息
+     *
+     * @param sysUser 参数
+     * @return 执行结果
+     */
+    @PostMapping(value = "sysUser/")
+    public ResponseVO save(@RequestBody SysUser sysUser) {
+        try {
+            boolean bo = sysUserService.saveUser(sysUser);
+            if (bo){
+                return ResponseVO.success("保存用户信息成功");
+            }
+            else{
+                log.error("保存用户信息失败");
+                return ResponseVO.fail();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("保存用户信息异常");
+            return ResponseVO.fail("保存用户信息异常");
+        }
+    }
+
+    /**
+     * 更新用户信息
+     *
+     * @param sysUser 参数
+     * @return 执行结果
+     */
+    @PutMapping(value = "sysUser/")
+    public ResponseVO update(@RequestBody SysUser sysUser) {
+        try {
+            boolean bo = sysUserService.updateUser(sysUser);
+            if (bo){
+                return ResponseVO.success("修改用户信息成功");
+            }
+            else{
+                log.error("更新用户信息失败");
+                return ResponseVO.fail();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("更新用户信息异常");
+            return ResponseVO.fail();
+        }
+    }
+
+    /**
+     * 删除用户信息
+     *
+     * @param ids 删除id
+     * @return 执行结果
+     */
+    @DeleteMapping(value = "sysUser/{ids}")
+    public ResponseVO delete(@PathVariable("ids") String ids) {
+        try {
+            boolean bo = sysUserService.deleteUser(ids);
+            if (bo){
+                return ResponseVO.success("删除用户信息成功");
+            }
+            else{
+                log.error("删除用户信息失败");
+                return ResponseVO.fail();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("删除用户信息异常");
+            return ResponseVO.fail("删除用户信息异常");
+        }
+    }
+}

+ 119 - 0
backend/src/main/java/com/jiayue/ssi/controller/UserLoginController.java

@@ -0,0 +1,119 @@
+package com.jiayue.ssi.controller;
+
+import cn.hutool.cache.CacheUtil;
+import com.jiayue.ssi.constant.CacheConstants;
+import com.jiayue.ssi.entity.SysUser;
+import com.jiayue.ssi.service.SysUserService;
+import com.jiayue.ssi.util.IdUtils;
+import com.jiayue.ssi.util.LocalCache;
+import com.jiayue.ssi.util.RandomUtil;
+import com.jiayue.ssi.util.ResponseVO;
+import com.wf.captcha.SpecCaptcha;
+import com.wf.captcha.base.Captcha;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.mail.internet.MimeMessage;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 用户登录控制类
+ *
+ * @author xsl
+ * @since 2023/02/20
+ */
+@RestController
+public class UserLoginController {
+    @Autowired
+    SysUserService sysUserService;
+    @Autowired
+    JavaMailSender javaMailSender;
+    @Value("${spring.mail.username}")
+    String fromMailAddress;
+
+    /**
+     * 生成验证码
+     *
+     * @param httpServletResponse
+     * @throws IOException
+     */
+    @GetMapping("/getVerifyCode")
+    public ResponseVO getVerifyCode(HttpServletResponse httpServletResponse) throws IOException {
+        // gif类型
+        // GifCaptcha captcha = new GifCaptcha(130, 48);
+        // 中文类型
+        // ChineseCaptcha captcha = new ChineseCaptcha(130, 48);
+        // 中文gif类型
+        // ChineseGifCaptcha captcha = new ChineseGifCaptcha(130, 48);
+        // 算术类型
+        // ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
+        // png类型
+        // 三个参数分别为宽、高、位数
+        SpecCaptcha captcha = new SpecCaptcha(150, 40, 4);
+        /**
+         * 验证码字符类型 TYPE_DEFAULT 数字和字母混合 TYPE_ONLY_NUMBER 纯数字 TYPE_ONLY_CHAR 纯字母 TYPE_ONLY_UPPER 纯大写字母 TYPE_ONLY_LOWER
+         * 纯小写字母 TYPE_NUM_AND_UPPER 数字和大写字母
+         **/
+        // 设置类型 数字和字母混合
+        captcha.setCharType(Captcha.TYPE_DEFAULT);
+        // 设置字体
+        captcha.setCharType(Captcha.FONT_9);
+
+        String uuid = IdUtils.simpleUUID();
+        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
+        // uuid存入缓存,失效时间默认5分钟
+        LocalCache.set(verifyKey, captcha.text());
+        System.out.println("uuid:" + uuid);
+        // 输出图片流
+        // captcha.out(httpServletResponse.getOutputStream());
+        String base64 = captcha.toBase64();
+        int index = base64.indexOf(",");
+        String substring = base64.substring(index + 1);
+
+        Map<String, String> dataMap = new HashMap<>(16);
+        dataMap.put("uuid", uuid);
+        dataMap.put("imgBase64", substring);
+        return ResponseVO.success(dataMap);
+    }
+
+    /**
+     * 获取邮箱口令
+     *
+     * @param httpServletRequest
+     * @param httpServletResponse
+     * @throws IOException
+     */
+    @PostMapping("/getMailCode")
+    public ResponseVO getMailCode(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
+        throws Exception {
+        String username = httpServletRequest.getParameter("username");
+        // 口令保存到服务器
+        String mailKey = CacheConstants.MAIL_CODE_KEY + username;
+
+        SysUser sysUser = sysUserService.queryUserName(username);
+        if (sysUser != null) {
+            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
+            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true, "utf-8");
+            mimeMessageHelper.setFrom(fromMailAddress);
+            mimeMessageHelper.setTo(sysUser.getMailBox());
+            mimeMessageHelper.setSubject("邮箱验证码");
+            // 生成6位邮箱口令
+            String mailRandom = RandomUtil.mailRandom();
+            // uuid存入缓存,失效时间4分钟
+            LocalCache.set(mailKey, mailRandom, 60000 * 4);
+            mimeMessageHelper.setText("口令:" + mailRandom + ",有效期4分钟。", true);
+            javaMailSender.send(mimeMessage);
+            System.out.println("邮箱发送成功。。。。。。");
+        }
+        return ResponseVO.success();
+    }
+}

+ 3 - 0
backend/src/main/java/com/jiayue/ssi/entity/SysUser.java

@@ -45,6 +45,9 @@ public class SysUser implements UserDetails {
     @TableField("C_USERNAME")
     private String username;
 
+    @TableField("C_MAIL_BOX")
+    private String mailBox;
+
     @Override
     public boolean isEnabled() {
         return true;

+ 1 - 1
backend/src/main/java/com/jiayue/ssi/mapper/SysUserMapper.java

@@ -6,7 +6,7 @@ import org.apache.ibatis.annotations.Mapper;
 
 /**
  * <p>
- *  Mapper
+ *  用户管理Mapper
  * </p>
  *
  * @author xsl

+ 41 - 0
backend/src/main/java/com/jiayue/ssi/service/SysUserService.java

@@ -0,0 +1,41 @@
+package com.jiayue.ssi.service;
+
+import com.jiayue.ssi.entity.SysUser;
+import java.util.List;
+
+/**
+* 用户管理接口
+* @author xsl
+* @date 2023/2/16
+*/
+public interface SysUserService {
+    /**
+    * 获取所有用户
+    * @return  List<SysUser>
+    */
+    List<SysUser> queryAllUsers();
+    /**
+     * 保存用户
+     * @param sysUser
+     * @return boolean
+     */
+    boolean saveUser(SysUser sysUser);
+    /**
+     * 更新用户
+     * @param sysUser
+     * @return boolean
+     */
+    boolean updateUser(SysUser sysUser);
+    /**
+     * 删除用户
+     * @param id
+     * @return
+     */
+    boolean deleteUser(String id);
+    /**
+     * 根据账号查找
+     * @param username
+     * @return SysUser
+     */
+    SysUser queryUserName(String username);
+}

+ 70 - 0
backend/src/main/java/com/jiayue/ssi/service/impl/SysUserServiceImpl.java

@@ -0,0 +1,70 @@
+package com.jiayue.ssi.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.jiayue.ssi.entity.SysUser;
+import com.jiayue.ssi.mapper.SysUserMapper;
+import com.jiayue.ssi.service.SysUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+import java.util.List;
+
+/**
+* 用户管理服务类
+* @author xsl
+* @date 2023/2/16
+*/
+@Service
+public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
+    @Autowired
+    SysUserMapper sysUserMapper;
+    /**
+     * 获取所有用户
+     * @return List<SysUser>
+     */
+    @Override
+    public List<SysUser> queryAllUsers(){
+        return this.list();
+    }
+    /**
+     * 保存用户
+     * @param sysUser
+     * @return boolean
+     */
+    @Override
+    public boolean saveUser(SysUser sysUser){
+       return this.save(sysUser);
+    }
+    /**
+     * 更新用户
+     * @param sysUser
+     * @return boolean
+     */
+    @Override
+    public boolean updateUser(SysUser sysUser){
+        return this.updateById(sysUser);
+    }
+    /**
+     * 删除用户
+     * @param id
+     * @return
+     */
+    @Override
+    public boolean deleteUser(String id){
+        return this.removeById(id);
+    }
+    /**
+     * 根据账号查找
+     * @param username
+     * @return SysUser
+     */
+    @Override
+    public SysUser queryUserName(String username){
+        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(SysUser::getUsername,username);
+        SysUser sysUser = sysUserMapper.selectOne(queryWrapper);
+        return sysUser;
+    }
+}

+ 49 - 0
backend/src/main/java/com/jiayue/ssi/util/IdUtils.java

@@ -0,0 +1,49 @@
+package com.jiayue.ssi.util;
+
+/**
+ * ID生成器工具类
+ *
+ * @author ruoyi
+ */
+public class IdUtils
+{
+    /**
+     * 获取随机UUID
+     *
+     * @return 随机UUID
+     */
+    public static String randomUUID()
+    {
+        return UUID.randomUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线
+     *
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String simpleUUID()
+    {
+        return UUID.randomUUID().toString(true);
+    }
+
+    /**
+     * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
+     *
+     * @return 随机UUID
+     */
+    public static String fastUUID()
+    {
+        return UUID.fastUUID().toString();
+    }
+
+    /**
+     * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
+     *
+     * @return 简化的UUID,去掉了横线
+     */
+    public static String fastSimpleUUID()
+    {
+        return UUID.fastUUID().toString(true);
+    }
+}

+ 9 - 6
backend/src/main/java/com/jiayue/ssi/util/JwtTokenUtil.java

@@ -12,19 +12,22 @@ import java.util.HashMap;
 import java.util.Map;
 
 /**
- * 生成令牌,验证等等一些操作
- *
- */
+* 生成令牌,验证等等一些操作
+* @author xsl
+* @date 2023/2/16
+*/
 @Data
 @Component
 public class JwtTokenUtil {
 
     private String secret = "ssqq";
 
-    // 过期时间毫秒20年
+    /**
+     * 过期时长
+     */
     private Long expiration = 630720000000L;
 
-    private String Authorization;
+    private String authorization;
 
     /**
      * 从数据声明生成令牌
@@ -61,7 +64,7 @@ public class JwtTokenUtil {
      * @return 令牌
      */
     public String generateToken(UserDetails userDetails) {
-        Map<String, Object> claims = new HashMap<String, Object>();
+        Map<String, Object> claims = new HashMap<String, Object>(16);
         claims.put(Claims.SUBJECT, userDetails.getUsername());
         claims.put(Claims.ISSUED_AT, new Date());
         return generateToken(claims);

+ 79 - 0
backend/src/main/java/com/jiayue/ssi/util/LocalCache.java

@@ -0,0 +1,79 @@
+package com.jiayue.ssi.util;
+
+import cn.hutool.cache.CacheUtil;
+import cn.hutool.cache.impl.TimedCache;
+import cn.hutool.core.date.DateUnit;
+
+import java.util.Set;
+
+/**
+* 缓存工具
+*
+* @author xsl
+* @since 2023/02/22
+*/
+public class LocalCache {
+    /**
+     * 默认缓存时长5分钟
+     */
+    private static final long DEFAULT_TIMEOUT = 5 * DateUnit.MINUTE.getMillis();
+    /**
+     * 默认清理间隔时间
+     */
+    private static final long CLEAN_TIMEOUT = 5 * DateUnit.MINUTE.getMillis();
+    /**
+     * 缓存对象
+     */
+    private static final TimedCache<String, Object> TIMED_CACHE = CacheUtil.newTimedCache(DEFAULT_TIMEOUT);
+
+    static {
+        //启动定时任务
+        TIMED_CACHE.schedulePrune(CLEAN_TIMEOUT);
+    }
+
+    public static void set(String key, Object value) {
+        TIMED_CACHE.put(key, value);
+    }
+
+    public static void set(String key, Object value, long expire) {
+        TIMED_CACHE.put(key, value, expire);
+    }
+
+    /**
+     * 获取并重新计算过期时间
+     */
+    public static Object getWithUpdateLastAccess(String key) {
+        return TIMED_CACHE.get(key);
+    }
+
+    /**
+     * 获取
+     *
+     * @param key
+     * @return
+     */
+    public static Object get(String key) {
+        return TIMED_CACHE.get(key, false);
+    }
+
+    public static Set<String> keySet() {
+        return TIMED_CACHE.keySet();
+    }
+
+
+    public static void remove(String key) {
+        TIMED_CACHE.remove(key);
+    }
+
+    public static void clear() {
+        TIMED_CACHE.clear();
+    }
+
+    public static class Constants {
+        public static final String DICT_LIST_PREFIX = "dict:list:";
+        public static final String DICT_ONE_PREFIX = "dict:one:";
+        public static final String DICT_MAP_PREFIX = "dict:map:";
+
+        public static final String SCRIPT = "script:";
+    }
+}

+ 27 - 0
backend/src/main/java/com/jiayue/ssi/util/RandomUtil.java

@@ -0,0 +1,27 @@
+package com.jiayue.ssi.util;
+
+import java.security.SecureRandom;
+
+/**
+* 随机数工具类
+*
+* @author xsl
+* @since 2023/02/22
+*/
+public class RandomUtil {
+    /**
+     * 邮箱口令随机数
+     * @return String
+     */
+    public static String mailRandom(){
+        String basic = "123456789qwertyuiopasdfghjklzxcvbnm";
+        char[] basicArray = basic.toCharArray();
+        SecureRandom random = new SecureRandom();
+        char[] result = new char[6];
+        for (int i = 0; i < result.length; i++) {
+            int index = random.nextInt(100) % (basicArray.length);
+            result[i] = basicArray[index];
+        }
+        return new String(result);
+    }
+}

+ 484 - 0
backend/src/main/java/com/jiayue/ssi/util/UUID.java

@@ -0,0 +1,484 @@
+package com.jiayue.ssi.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+
+/**
+ * 提供通用唯一识别码(universally unique identifier)(UUID)实现
+ *
+ * @author ruoyi
+ */
+public final class UUID implements java.io.Serializable, Comparable<UUID>
+{
+    private static final long serialVersionUID = -1185015143654744140L;
+
+    /**
+     * SecureRandom 的单例
+     *
+     */
+    private static class Holder
+    {
+        static final SecureRandom numberGenerator = getSecureRandom();
+    }
+
+    /** 此UUID的最高64有效位 */
+    private final long mostSigBits;
+
+    /** 此UUID的最低64有效位 */
+    private final long leastSigBits;
+
+    /**
+     * 私有构造
+     *
+     * @param data 数据
+     */
+    private UUID(byte[] data)
+    {
+        long msb = 0;
+        long lsb = 0;
+        assert data.length == 16 : "data must be 16 bytes in length";
+        for (int i = 0; i < 8; i++)
+        {
+            msb = (msb << 8) | (data[i] & 0xff);
+        }
+        for (int i = 8; i < 16; i++)
+        {
+            lsb = (lsb << 8) | (data[i] & 0xff);
+        }
+        this.mostSigBits = msb;
+        this.leastSigBits = lsb;
+    }
+
+    /**
+     * 使用指定的数据构造新的 UUID。
+     *
+     * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
+     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
+     */
+    public UUID(long mostSigBits, long leastSigBits)
+    {
+        this.mostSigBits = mostSigBits;
+        this.leastSigBits = leastSigBits;
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID fastUUID()
+    {
+        return randomUUID(false);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID()
+    {
+        return randomUUID(true);
+    }
+
+    /**
+     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
+     *
+     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
+     * @return 随机生成的 {@code UUID}
+     */
+    public static UUID randomUUID(boolean isSecure)
+    {
+        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
+
+        byte[] randomBytes = new byte[16];
+        ng.nextBytes(randomBytes);
+        randomBytes[6] &= 0x0f; /* clear version */
+        randomBytes[6] |= 0x40; /* set to version 4 */
+        randomBytes[8] &= 0x3f; /* clear variant */
+        randomBytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(randomBytes);
+    }
+
+    /**
+     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
+     *
+     * @param name 用于构造 UUID 的字节数组。
+     *
+     * @return 根据指定数组生成的 {@code UUID}
+     */
+    public static UUID nameUUIDFromBytes(byte[] name)
+    {
+        MessageDigest md;
+        try
+        {
+            md = MessageDigest.getInstance("MD5");
+        }
+        catch (NoSuchAlgorithmException nsae)
+        {
+            throw new InternalError("MD5 not supported");
+        }
+        byte[] md5Bytes = md.digest(name);
+        md5Bytes[6] &= 0x0f; /* clear version */
+        md5Bytes[6] |= 0x30; /* set to version 3 */
+        md5Bytes[8] &= 0x3f; /* clear variant */
+        md5Bytes[8] |= 0x80; /* set to IETF variant */
+        return new UUID(md5Bytes);
+    }
+
+    /**
+     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
+     *
+     * @param name 指定 {@code UUID} 字符串
+     * @return 具有指定值的 {@code UUID}
+     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
+     *
+     */
+    public static UUID fromString(String name)
+    {
+        String[] components = name.split("-");
+        if (components.length != 5)
+        {
+            throw new IllegalArgumentException("Invalid UUID string: " + name);
+        }
+        for (int i = 0; i < 5; i++)
+        {
+            components[i] = "0x" + components[i];
+        }
+
+        long mostSigBits = Long.decode(components[0]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[1]).longValue();
+        mostSigBits <<= 16;
+        mostSigBits |= Long.decode(components[2]).longValue();
+
+        long leastSigBits = Long.decode(components[3]).longValue();
+        leastSigBits <<= 48;
+        leastSigBits |= Long.decode(components[4]).longValue();
+
+        return new UUID(mostSigBits, leastSigBits);
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最低有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中的最低有效 64 位。
+     */
+    public long getLeastSignificantBits()
+    {
+        return leastSigBits;
+    }
+
+    /**
+     * 返回此 UUID 的 128 位值中的最高有效 64 位。
+     *
+     * @return 此 UUID 的 128 位值中最高有效 64 位。
+     */
+    public long getMostSignificantBits()
+    {
+        return mostSigBits;
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
+     * <p>
+     * 版本号具有以下含意:
+     * <ul>
+     * <li>1 基于时间的 UUID
+     * <li>2 DCE 安全 UUID
+     * <li>3 基于名称的 UUID
+     * <li>4 随机生成的 UUID
+     * </ul>
+     *
+     * @return 此 {@code UUID} 的版本号
+     */
+    public int version()
+    {
+        // Version is bits masked by 0x000000000000F000 in MS long
+        return (int) ((mostSigBits >> 12) & 0x0f);
+    }
+
+    /**
+     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
+     * <p>
+     * 变体号具有以下含意:
+     * <ul>
+     * <li>0 为 NCS 向后兼容保留
+     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
+     * <li>6 保留,微软向后兼容
+     * <li>7 保留供以后定义使用
+     * </ul>
+     *
+     * @return 此 {@code UUID} 相关联的变体号
+     */
+    public int variant()
+    {
+        // This field is composed of a varying number of bits.
+        // 0 - - Reserved for NCS backward compatibility
+        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
+        // 1 1 0 Reserved, Microsoft backward compatibility
+        // 1 1 1 Reserved for future definition.
+        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
+    }
+
+    /**
+     * 与此 UUID 相关联的时间戳值。
+     *
+     * <p>
+     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
+     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
+     *
+     * <p>
+     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
+     */
+    public long timestamp() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (mostSigBits & 0x0FFFL) << 48//
+                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
+                | mostSigBits >>> 32;
+    }
+
+    /**
+     * 与此 UUID 相关联的时钟序列值。
+     *
+     * <p>
+     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
+     * <p>
+     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
+     * UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的时钟序列
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public int clockSequence() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
+    }
+
+    /**
+     * 与此 UUID 相关的节点值。
+     *
+     * <p>
+     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
+     * <p>
+     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
+     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
+     *
+     * @return 此 {@code UUID} 的节点值
+     *
+     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
+     */
+    public long node() throws UnsupportedOperationException
+    {
+        checkTimeBase();
+        return leastSigBits & 0x0000FFFFFFFFFFFFL;
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @return 此{@code UUID} 的字符串表现形式
+     * @see #toString(boolean)
+     */
+    @Override
+    public String toString()
+    {
+        return toString(false);
+    }
+
+    /**
+     * 返回此{@code UUID} 的字符串表现形式。
+     *
+     * <p>
+     * UUID 的字符串表示形式由此 BNF 描述:
+     *
+     * <pre>
+     * {@code
+     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
+     * time_low               = 4*<hexOctet>
+     * time_mid               = 2*<hexOctet>
+     * time_high_and_version  = 2*<hexOctet>
+     * variant_and_sequence   = 2*<hexOctet>
+     * node                   = 6*<hexOctet>
+     * hexOctet               = <hexDigit><hexDigit>
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * </pre>
+     *
+     * </blockquote>
+     *
+     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
+     * @return 此{@code UUID} 的字符串表现形式
+     */
+    public String toString(boolean isSimple)
+    {
+        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
+        // time_low
+        builder.append(digits(mostSigBits >> 32, 8));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_mid
+        builder.append(digits(mostSigBits >> 16, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // time_high_and_version
+        builder.append(digits(mostSigBits, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // variant_and_sequence
+        builder.append(digits(leastSigBits >> 48, 4));
+        if (!isSimple)
+        {
+            builder.append('-');
+        }
+        // node
+        builder.append(digits(leastSigBits, 12));
+
+        return builder.toString();
+    }
+
+    /**
+     * 返回此 UUID 的哈希码。
+     *
+     * @return UUID 的哈希码值。
+     */
+    @Override
+    public int hashCode()
+    {
+        long hilo = mostSigBits ^ leastSigBits;
+        return ((int) (hilo >> 32)) ^ (int) hilo;
+    }
+
+    /**
+     * 将此对象与指定对象比较。
+     * <p>
+     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
+     *
+     * @param obj 要与之比较的对象
+     *
+     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
+     */
+    @Override
+    public boolean equals(Object obj)
+    {
+        if ((null == obj) || (obj.getClass() != UUID.class))
+        {
+            return false;
+        }
+        UUID id = (UUID) obj;
+        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
+    }
+
+    // Comparison Operations
+
+    /**
+     * 将此 UUID 与指定的 UUID 比较。
+     *
+     * <p>
+     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
+     *
+     * @param val 与此 UUID 比较的 UUID
+     *
+     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
+     *
+     */
+    @Override
+    public int compareTo(UUID val)
+    {
+        // The ordering is intentionally set up so that the UUIDs
+        // can simply be numerically compared as two numbers
+        return (this.mostSigBits < val.mostSigBits ? -1 : //
+                (this.mostSigBits > val.mostSigBits ? 1 : //
+                        (this.leastSigBits < val.leastSigBits ? -1 : //
+                                (this.leastSigBits > val.leastSigBits ? 1 : //
+                                        0))));
+    }
+
+    // -------------------------------------------------------------------------------------------------------------------
+    // Private method start
+    /**
+     * 返回指定数字对应的hex值
+     *
+     * @param val 值
+     * @param digits 位
+     * @return 值
+     */
+    private static String digits(long val, int digits)
+    {
+        long hi = 1L << (digits * 4);
+        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
+    }
+
+    /**
+     * 检查是否为time-based版本UUID
+     */
+    private void checkTimeBase()
+    {
+        if (version() != 1)
+        {
+            throw new UnsupportedOperationException("Not a time-based UUID");
+        }
+    }
+
+    /**
+     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
+     *
+     * @return {@link SecureRandom}
+     */
+    public static SecureRandom getSecureRandom()
+    {
+        try
+        {
+            return SecureRandom.getInstance("SHA1PRNG");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new UtilException(e);
+        }
+    }
+
+    /**
+     * 获取随机数生成器对象<br>
+     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
+     *
+     * @return {@link ThreadLocalRandom}
+     */
+    public static ThreadLocalRandom getRandom()
+    {
+        return ThreadLocalRandom.current();
+    }
+}

+ 26 - 0
backend/src/main/java/com/jiayue/ssi/util/UtilException.java

@@ -0,0 +1,26 @@
+package com.jiayue.ssi.util;
+
+/**
+ * 工具类异常
+ *
+ * @author ruoyi
+ */
+public class UtilException extends RuntimeException
+{
+    private static final long serialVersionUID = 8247610319171014183L;
+
+    public UtilException(Throwable e)
+    {
+        super(e.getMessage(), e);
+    }
+
+    public UtilException(String message)
+    {
+        super(message);
+    }
+
+    public UtilException(String message, Throwable throwable)
+    {
+        super(message, throwable);
+    }
+}

+ 1 - 1
backend/src/main/resources/banner.txt

@@ -8,4 +8,4 @@
            \|___|/
 
 
-Spring Boot版本:${spring-boot.version}
+Spring Boot版本:${spring-boot.version}

+ 1 - 1
backend/src/test/java/com/jiayue/ssi/BaseTest.java

@@ -16,7 +16,7 @@ import org.springframework.web.context.WebApplicationContext;
  * @since 2018/10/11 12:47
  */
 @RunWith(SpringJUnit4ClassRunner.class)
-@SpringBootTest(classes = Application.class, properties = "spring.config.location=classpath:/application.yml")
+@SpringBootTest(classes = SsiApplication.class, properties = "spring.config.location=classpath:/application.yml")
 @WebAppConfiguration(value = "src/main/java")
 //@ContextHierarchy({ @ContextConfiguration(name = "parent", classes = { WebSecurityConfig.class}) })
 //@Transactional

+ 1 - 46
backend/src/test/java/com/jiayue/ssi/service/DataHandleServiceTest.java

@@ -2,6 +2,7 @@ package com.jiayue.ssi.service;
 
 
 import com.jiayue.ssi.BaseTest;
+import com.jiayue.ssi.util.LocalCache;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -15,51 +16,5 @@ import java.util.List;
  * @version 3.0
  */
 public class DataHandleServiceTest extends BaseTest {
-    @Autowired
-    DataHandleService dataHandleService;
-    @Autowired
-    SsqDataService ssqDataService;
-    @Autowired
-    HadnleRedService hadnleRedService;
-//    @Autowired
-//    SsqDataMapper ssqDataMapper;
 
-    @Test
-    public void sameBlue() throws Exception{
-
-        List<SsqData> ssqDataList = ssqDataService.queryListForIssue("17001","22086");
-        hadnleRedService.findEveryRedPossible(ssqDataList);
-        hadnleRedService.qjCompare(ssqDataList);
-
-//        QueryWrapper<SsqData> lqw = new QueryWrapper<>();
-//        lqw.le("c_issue", "22084");
-//        lqw.ge("c_issue", "08001");
-//
-//        List<SsqData> tempList = ssqDataService.list(lqw);
-//        List<SsqData> ssqDataList = tempList.stream().sorted(Comparator.comparing(SsqData::getIssue)).collect(Collectors.toList());
-
-//        dataHandleService.sameBlue(ssqDataList,"1");
-//        System.out.println("蓝比结束");
-//        dataHandleService.sameBlue(ssqDataList,"2");
-//        System.out.println("区间比结束");
-//        dataHandleService.sameBlue(ssqDataList,"3");
-//        System.out.println("蓝比+区间结束");
-//        dataHandleService.sameBlue(ssqDataList,"4");
-//        System.out.println("奇偶结束");
-//        dataHandleService.sameBlue(ssqDataList,"5");
-//        System.out.println("蓝比+奇偶结束");
-//        dataHandleService.sameBlue(ssqDataList,"6");
-//        System.out.println("2期蓝结束");
-//        dataHandleService.sameBlue(ssqDataList,"7");
-//        System.out.println("3期蓝结束");
-//
-        dataHandleService.searchRed(ssqDataList,"1");
-        System.out.println("区间结束");
-        dataHandleService.searchRed(ssqDataList,"2");
-        System.out.println("奇偶结束");
-//        dataHandleService.searchRed(ssqDataList,"4");
-//        System.out.println("2期奇偶结束");
-        dataHandleService.searchRed(ssqDataList,"3");
-//        System.out.println("综合结束");
-    }
 }

+ 7 - 0
pom.xml

@@ -3,6 +3,13 @@
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <groupId>org.springframework.boot</groupId>
+        <version>2.2.1.RELEASE</version>
+    </parent>
+
     <groupId>com.jiayue.ssi</groupId>
     <artifactId>ssi</artifactId>
     <packaging>pom</packaging>

BIN
ui/src/assets/images/login-background.jpg


+ 1 - 0
ui/src/icons/svg/mailbox.svg

@@ -0,0 +1 @@
+<svg t="1676598067155" class="icon" viewBox="0 0 1296 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24624" width="200" height="200"><path d="M1098.346563 371.732817v454.009681H198.257502V288.464666l472.844144 329.107454 372.724104-279.543078 252.778316-188.344628V0H0v1024h1296.604066V224.030978zM900.089061 198.257502L667.136496 373.715392 415.349468 198.257502z"  p-id="24625"></path></svg>

+ 1 - 0
ui/src/icons/svg/verifyCode.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1676598168456" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="26052" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M963.8 218.17a72.85 72.85 0 0 0-57.513-63.903 1357.957 1357.957 0 0 1-185.96-42.816 690.8 690.8 0 0 1-166.15-97.134 76.046 76.046 0 0 0-88.827 0 435.824 435.824 0 0 1-167.428 96.495 624.34 624.34 0 0 1-180.848 44.733A67.738 67.738 0 0 0 60.2 219.449s-1.917 160.398-1.917 301.626c0 255.615 301.626 502.923 452.439 502.923S917.153 849.54 958.05 524.909c10.224-191.712 3.195-306.1 3.195-306.1zM799.567 415.634L484.52 712.787a42.816 42.816 0 0 1-52.4 5.112l-8.308-7.03-173.82-180.848a42.816 42.816 0 0 1 63.905-58.79l143.145 150.173 283.733-268.397a42.816 42.816 0 1 1 58.791 63.904" p-id="26053"></path></svg>

+ 1 - 1
ui/src/main.js

@@ -164,7 +164,7 @@ Vue.prototype.$axios.interceptors.response.use(
                     removeToken()
                     resetRouter()
                     Message({
-                        message: error.response.data.data,
+                        message: error.response.data,
                         type: 'error',
                         duration: 5 * 1000
                     })

+ 6 - 6
ui/src/router/index.js

@@ -53,14 +53,14 @@ export const constantRoutes = [
     }]
   },
   {
-    path: '/history',
+    path: '/sysManager',
     component: Layout,
-    redirect: '/history',
+    redirect: '/sysManager',
     children: [{
-      path: 'history',
-      name: '查历史',
-      component: () => import('@/views/history/index'),
-      meta: { title: '查历史', icon: 'dashboard' }
+      path: 'sysManager',
+      name: '系统管理',
+      component: () => import('@/views/sysManager/userManager/index'),
+      meta: { title: '用户管理'}
     }]
   },
   {

+ 4 - 0
ui/src/utils/request.js

@@ -14,6 +14,7 @@ const service = axios.create({
 // request interceptor
 service.interceptors.request.use(
   config => {
+    alert(1)
     // do something before request is sent
     if (store.getters.token) {
       // let each request carry token
@@ -44,6 +45,7 @@ service.interceptors.response.use(
    */
   response => {
     const res = response.data
+    alert('request.js')
     // if the custom code is not 20000, it is judged as an error.
     if (res.code !== 0) {
       Message({
@@ -74,6 +76,8 @@ service.interceptors.response.use(
     if (error.response) {
       switch (error.response.status) {
         case 401:
+          console.log(error)
+          console.log(1111)
           console.log('用户验证失败!')
           // 返回 401 清除token信息并跳转到登录页面
           removeToken()

+ 282 - 234
ui/src/views/login/index.vue

@@ -1,274 +1,322 @@
 <template>
-  <div class="login-container" :style="login_bg_style">
-    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on"
-             label-position="left">
+  <div class="login">
+    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
+
       <el-form-item prop="username">
-        <span class="svg-container">
-          <svg-icon icon-class="user"/>
-        </span>
         <el-input
-          ref="username"
           v-model="loginForm.username"
-          placeholder="Username"
-          name="username"
           type="text"
+          name="username"
+          auto-complete="off"
+          placeholder="账号"
           tabindex="1"
-          auto-complete="on"
-        />
+          maxlength="20"
+          @input="onUsernameChange"
+        >
+          <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
+        </el-input>
       </el-form-item>
 
       <el-form-item prop="password">
-        <span class="svg-container">
-          <svg-icon icon-class="password"/>
-        </span>
         <el-input
-          :key="passwordType"
-          ref="password"
           v-model="loginForm.password"
-          :type="passwordType"
-          placeholder="Password"
-          name="password"
+          type="password"
+          auto-complete="off"
+          placeholder="密码"
           tabindex="2"
-          auto-complete="on"
-          @keyup.enter.native="handleLogin"
-        />
-        <!--        <span class="show-pwd" @click="showPwd">-->
-        <!--          <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />-->
-        <!--        </span>-->
+          maxlength="20"
+        >
+          <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
+        </el-input>
       </el-form-item>
 
-      <el-button :loading="loading" type="primary"
-                 style="width:100%;height:37px;line-height:1px;margin-bottom:30px;"
-                 @click.native.prevent="handleLogin">登陆
-      </el-button>
+      <el-form-item prop="verifyCode">
+        <el-input
+          v-model="loginForm.verifyCode"
+          auto-complete="off"
+          placeholder="验证码"
+          style="width: 63%"
+          tabindex="3"
+          maxlength="4"
+        >
+          <svg-icon slot="prefix" icon-class="verifyCode" class="el-input__icon input-icon" />
+        </el-input>
+        <div class="login-code">
+          <img :src="captchaUrl" @click="updateCaptcha" class="login-code-img"/>
+        </div>
+      </el-form-item>
+
+      <el-form-item prop="mailbox">
+        <el-input
+          v-model="loginForm.mailbox"
+          auto-complete="off"
+          placeholder="邮箱验证码"
+          style="width: 63%"
+          tabindex="4"
+          maxlength="6"
+        >
+          <svg-icon slot="prefix" icon-class="mailbox" class="el-input__icon input-icon" />
+        </el-input>
+        <div class="mail-box">
+          <el-button type="success" size="small" @click.prevent="sendMailCode" :disabled="isMailSend">{{sendBtnText}}</el-button>
+        </div>
+      </el-form-item>
+      <el-form-item style="width:100%;">
+        <el-button
+          :loading="loading"
+          size="medium"
+          type="primary"
+          style="width:100%;"
+          @click.native.prevent="handleLogin"
+        >
+          <span v-if="!loading">登 录</span>
+          <span v-else>登 录 中...</span>
+        </el-button>
+      </el-form-item>
     </el-form>
   </div>
 </template>
 
 <script>
-  import {validUsername} from '@/utils/validate'
 
-  export default {
-    name: 'Login',
-    data() {
-      const validateUsername = (rule, value, callback) => {
-        if (!validUsername(value)) {
-          callback(new Error('Please enter the correct user name'))
-        } else {
-          callback()
-        }
-      }
-      const validatePassword = (rule, value, callback) => {
-        if (value.length < 6) {
-          callback(new Error('The password can not be less than 6 digits'))
-        } else {
-          callback()
-        }
+export default {
+  name: 'Login',
+  data() {
+    return {
+      loginForm: {
+        // 用户名
+        username: '',
+        // 密码
+        password: '',
+        // 验证码
+        verifyCode: '',
+        // 邮箱验证码
+        mailbox:''
+      },
+      verifyuuid:'',
+      // 是否已经发送了验证码
+      isMailSend: false,
+      // 计时器对象
+      timer: null,
+      // 倒数60秒
+      counter: 60,
+      // 文本
+      sendBtnText: '点击发送邮箱',
+      captchaUrl: '/getVerifyCode',
+      loginRules: {
+        /*  username: [{ required: true, trigger: 'blur', validator: validateUsername }],
+          password: [{ required: true, trigger: 'blur', validator: validatePassword }]*/
+        username: [{required: true, trigger: 'blur',message: '请输入用户名'}],
+        password: [{required: true, trigger: 'blur',message: '请输入密码'}],
+        verifyCode: [{required: true, trigger: 'blur',message: '请输入验证码'}],
+        mailbox: [{required: true, trigger: 'blur',message: '请输入邮箱验证码'}]
+      },
+      loading: false,
+      redirect: undefined
+    }
+  },
+  watch: {
+    $route: {
+      handler: function (route) {
+        this.redirect = route.query && route.query.redirect
+      },
+      immediate: true
+    }
+  },
+  mounted() {
+    this.getCaptcha()
+  },
+  methods: {
+    // 当账号变化时,重置发送按钮
+    onUsernameChange () {
+      this.reset()
+    },
+    /**
+     * 重置倒计时
+     */
+    reset () {
+      // 重置按钮可用
+      this.isMailSend = false
+      // 重置文本内容
+      this.sendBtnText = '点击发送邮箱'
+      if (this.timer) {
+        // 存在计时器对象,则清除
+        clearInterval(this.timer)
+        // 重置秒数,防止下次混乱
+        this.counter = 60
+        // 计时器对象重置为空
+        this.timer = null
       }
-      return {
-        loginForm: {
-          username: '',
-          password: ''
-        },
-        loginRules: {
-          /*  username: [{ required: true, trigger: 'blur', validator: validateUsername }],
-            password: [{ required: true, trigger: 'blur', validator: validatePassword }]*/
-          username: [{required: true, trigger: 'blur'}],
-          password: [{required: true, trigger: 'blur'}]
-        },
-        loading: false,
-        passwordType: 'password',
-        redirect: undefined,
-        login_bg_style: {
-          backgroundImage: ''
+    },
+    /**
+     * 倒计时
+     */
+    countDown () {
+      // 将setInterval()方法赋值给前面定义的timer计时器对象,等用clearInterval()方法时方便清空这个计时器对象
+      this.timer = setInterval(() => {
+        // 替换文本,用es6里面的``这个来创建字符串模板,让秒实时改变
+        this.sendBtnText = `(${this.counter}秒)后重新发送`
+        this.counter--
+        if (this.counter < 0) {
+          // 当计时小于零时,取消该计时器
+          this.reset()
         }
-      }
+      }, 1000)
     },
-    watch: {
-      $route: {
-        handler: function (route) {
-          this.redirect = route.query && route.query.redirect
-        },
-        immediate: true
+    /**
+     * 发送邮箱验证码
+     */
+    sendMailCode () {
+      // 判断账户是否已经输入
+      if (!this.loginForm.username) {
+        this.$message.error('请输入账号')
+        return false
       }
+      // 将按钮禁用,防止再次点击
+      this.isMailSend = true
+      // 开始倒计时,60s之后才能再次点击
+      this.countDown()  // 这里实现倒计时的功能,文章下面开始介绍
+
+      const param = new URLSearchParams()
+      param.append('username', this.loginForm.username)
+      this.$axios.post('/getMailCode', param,
+      ).then((res) => {
+          this.$message.success('邮件发送成功')
+      })
     },
-    mounted() {
-      this.login_bg_style.backgroundImage = 'url(' + require('../../assets/login.png') + ')'
+    // 获取验证码
+    getCaptcha() {
+      this.$axios.get('/getVerifyCode').then((res) => {
+        this.verifyuuid = res.data.uuid
+        this.captchaUrl = 'data:image/gif;base64,' + res.data.imgBase64;
+      })
     },
-    methods: {
-      showPwd() {
-        if (this.passwordType === 'password') {
-          this.passwordType = ''
-        } else {
-          this.passwordType = 'password'
-        }
-        this.$nextTick(() => {
-          this.$refs.password.focus()
-        })
-      },
-      handleLogin() {
-        this.$refs.loginForm.validate(valid => {
-          if (valid) {
-            this.loading = true
-            const param = new URLSearchParams()
-            param.append('username', this.loginForm.username)
-            param.append('password', this.loginForm.password)
-            this.$axios.post('/user/login', param,
-            ).then((res) => {
-              const {data} = res
-              // sessionStorage.setItem('token', data)
+    updateCaptcha() {
+      // 更新验证码
+      this.getCaptcha()
+    },
+    handleLogin() {
+      this.$refs.loginForm.validate(valid => {
+        if (valid) {
+          this.loading = true
+          const param = new URLSearchParams()
+          param.append('username', this.loginForm.username)
+          param.append('password', this.loginForm.password)
+          param.append('code', this.loginForm.verifyCode)
+          param.append('verifyuuid', this.verifyuuid)
+          param.append('mailbox', this.loginForm.mailbox)
+          this.$axios.post('/user/login', param,
+          ).then((res) => {
+            const {data} = res
+            // sessionStorage.setItem('token', data)
 
-              document.cookie = "token=" + data;
-              document.cookie = "user=".concat(this.loginForm.username)
-              sessionStorage.setItem('user', this.loginForm.username)
-              // if (this.loginForm.username !== 'admin') {
-              //   document.cookie = "user=yw"
-              //   // sessionStorage.setItem('user', 'yw')
-              // } else {
-              //   document.cookie = "user=admin"
-              //   // sessionStorage.setItem('user', 'admin')
-              // }
-              console.log('login user is :' + this.loginForm.username)
-              this.$router.push('/')
-              this.loading = false
-            }).catch((error) => {
-              this.loading = false
-            })
+            document.cookie = "token=" + data;
+            document.cookie = "user=".concat(this.loginForm.username)
+            sessionStorage.setItem('user', this.loginForm.username)
+            // if (this.loginForm.username !== 'admin') {
+            //   document.cookie = "user=yw"
+            //   // sessionStorage.setItem('user', 'yw')
+            // } else {
+            //   document.cookie = "user=admin"
+            //   // sessionStorage.setItem('user', 'admin')
+            // }
+            console.log('login user is :' + this.loginForm.username)
+            this.$router.push('/')
+            this.loading = false
+          }).catch((error) => {
+            // 登录失败刷新验证码
+            this.updateCaptcha()
+            this.loading = false
+          })
 
-            /*    this.$store.dispatch('user/login', this.loginForm).then(() => {
-              this.$router.push('/')
-              this.loading = false
-            }).catch(() => {
-              this.loading = false
-            })*/
-          } else {
-            console.log('error submit!!')
-            return false
-          }
-        })
-      }
+          /*    this.$store.dispatch('user/login', this.loginForm).then(() => {
+            this.$router.push('/')
+            this.loading = false
+          }).catch(() => {
+            this.loading = false
+          })*/
+        } else {
+          console.log('error submit!!')
+          return false
+        }
+      })
     }
   }
+}
 </script>
 
-<style lang="scss">
-  /* 修复input 背景不协调 和光标变色 */
-  /* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
-
-  $bg: #283443;
-  $light_gray: #fff;
-  $cursor: #fff;
+<style rel="stylesheet/scss" lang="scss">
+.login {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+  background-image: url("../../assets/images/login-background.jpg");
+  background-size: cover;
+}
+.title {
+  margin: 0px auto 30px auto;
+  text-align: center;
+  color: #707070;
+}
 
-  @supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
-    .login-container .el-input input {
-      color: $cursor;
+.login-form {
+  border-radius: 6px;
+  background: #ffffff;
+  width: 400px;
+  padding: 25px 25px 5px 25px;
+  .el-input {
+    height: 38px;
+    input {
+      height: 38px;
     }
   }
-
-  /* reset element-ui css */
-  .login-container {
-    .el-input {
-      display: inline-block;
-      height: 47px;
-      width: 85%;
-
-      input {
-        background: transparent;
-        border: 0px;
-        -webkit-appearance: none;
-        border-radius: 0px;
-        padding: 12px 5px 12px 15px;
-        color: $light_gray;
-        vertical-align: middle;
-        height: 37px;
-        caret-color: $cursor;
-
-        &:-webkit-autofill {
-          box-shadow: 0 0 0px 1000px $bg inset !important;
-          -webkit-text-fill-color: $cursor !important;
-        }
-      }
-    }
-
-    .el-form-item {
-      border: 1px solid rgba(255, 255, 255, 0.1);
-      background: rgba(0, 0, 0, 0.1);
-      border-radius: 5px;
-      color: #454545;
-    }
+  .input-icon {
+    height: 39px;
+    width: 14px;
+    margin-left: 2px;
   }
-
-  .el-form-item__content {
-    line-height: 28px;
-    position: relative;
-    /*font-size: 14px;*/
-    height: 37px;
+}
+.login-tip {
+  font-size: 13px;
+  text-align: center;
+  color: #bfbfbf;
+}
+.login-code {
+  display: flex;  justify-content: center;  align-items: center;
+  width: 33%;
+  height: 38px;
+  float: right;
+  img {
+    cursor: pointer;
+    vertical-align: middle;
   }
-</style>
-
-<style lang="scss" scoped>
-  $bg: #2d3a4b;
-  $dark_gray: #889aa4;
-  $light_gray: #eee;
-
-  .login-container {
-    position: fixed;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 10;
-    zoom: 1;
-    background-color: #fff;
-    background-repeat: no-repeat;
-    background-size: cover;
-    -webkit-background-size: cover;
-    -o-background-size: cover;
-    background-position: bottom;
-    /*background-image: url('../../assets/login.jpg');*/
-
-    .login-form {
-      position: relative;
-      max-width: 100%;
-      width: 320px;
-      margin: 0 auto;
-      top: 52%;
-      -webkit-transform: translateY(-50%);
-      -moz-transform: translateY(-50%);
-      -ms-transform: translateY(-50%);
-      -o-transform: translateY(-50%);
-      transform: translateY(-50%);
-    }
-
-    .tips {
-      font-size: 14px;
-      color: #fff;
-      margin-bottom: 10px;
-
-      span {
-        &:first-of-type {
-          margin-right: 16px;
-        }
-      }
-    }
-
-    .svg-container {
-      padding: 6px 5px 6px 15px;
-      color: $dark_gray;
-      vertical-align: middle;
-      width: 30px;
-      display: inline-block;
-    }
-
-    .show-pwd {
-      position: absolute;
-      right: 10px;
-      top: 7px;
-      font-size: 16px;
-      color: $dark_gray;
-      cursor: pointer;
-      user-select: none;
-    }
+}
+.el-login-footer {
+  height: 40px;
+  line-height: 40px;
+  position: fixed;
+  bottom: 0;
+  width: 100%;
+  text-align: center;
+  color: #fff;
+  font-family: Arial;
+  font-size: 12px;
+  letter-spacing: 1px;
+}
+.login-code-img {
+  height: 38px;
+  width: 105px;
+}
+.mail-box {
+  display: flex;  justify-content: center;  align-items: center;
+  width: 36%;
+  height: 38px;
+  float: right;
+  img {
+    cursor: pointer;
+    vertical-align: middle;
   }
+}
 </style>
 

+ 542 - 0
ui/src/views/sysManager/userManager/index.vue

@@ -0,0 +1,542 @@
+<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
+
+  <div class="app-container">
+    <div>
+      <el-card>
+        <div slot="header" class="clearfix">
+          <b><span>用户管理</span></b>
+        </div>
+
+        <div class="filter-container">
+          <el-button type="primary" size="small" style="round-clip: 10px" @click="insertEvent"
+                     :loading="loadButton">添加
+          </el-button>
+        </div>
+        <div style="padding-top: 10px">
+          <vxe-table
+            ref="xTable"
+            border
+            export-config
+            keep-source
+            auto-resize
+            resizable
+            :edit-rules="rules"
+            :loading="loading"
+            :data="tableData"
+            v-show="showTable"
+            show-overflow
+            highlight-current-row
+            align="center"
+          >
+            <!--:edit-config="{trigger: 'manual', mode: 'row',autoClear:false,showStatus: true,icon:'none'}"-->
+            <vxe-table-column title="用户基本信息" align="center">
+              <vxe-table-column
+                field="username"
+                title="用户名"
+                width="7%"
+                align="center"
+              />
+              <!--:edit-render="{name: '$input', attrs: {type: 'text'}}"-->
+<!--              <vxe-table-column-->
+<!--                field="password"-->
+<!--                title="密码"-->
+<!--                width="12%"-->
+<!--                align="center"-->
+<!--              />-->
+                            <vxe-table-column
+                              field="mailbox"
+                              title="邮箱"
+                              width="12%"
+                              align="center"
+                            />
+              <!--:edit-render="{name: '$input', attrs: {type: 'text'}}"-->
+              <vxe-table-column
+                field="name"
+                title="姓名"
+                width="7%"
+                align="center"
+              />
+              <!--:edit-render="{name: '$input', attrs: {type: 'text'}}"-->
+              <vxe-table-column
+                field="status"
+                title="状态"
+                width="8%"
+                align="center"
+                :formatter="formatStatus"
+              />
+              <!--:edit-render="{name: '$select', options: userStatus }"-->
+              <vxe-table-column title="操作" width="15%" align="center">
+                <template v-slot="{ row }">
+                  <!--<template v-if="$refs.xTable.isActiveByRow(row)">-->
+                  <!--<el-button-->
+                  <!--type="success"-->
+                  <!--style="padding: 3px 4px 3px 4px;margin: 2px;"-->
+                  <!--size="mini"-->
+                  <!--icon="el-icon-edit"-->
+                  <!--@click="editSave(row)"-->
+                  <!--&gt;保存-->
+                  <!--</el-button>-->
+                  <!--<el-button-->
+                  <!--class="cancel-btn"-->
+                  <!--icon="el-icon-refresh"-->
+                  <!--type="warning"-->
+                  <!--style="padding: 3px 4px 3px 4px;margin: 2px;"-->
+                  <!--size="mini"-->
+                  <!--@click="cancelRowEvent(row)"-->
+                  <!--&gt;取消-->
+                  <!--</el-button>-->
+                  <!--</template>-->
+                  <!--<template v-else>-->
+                  <el-button
+                    :loading="loadButton"
+                    type="primary"
+                    style="padding: 3px 4px 3px 4px;margin: 2px;"
+                    size="mini "
+                    icon="el-icon-edit"
+                    @click="editRowEvent(row)"
+                  >编辑
+                  </el-button>
+                  <el-button
+                    :loading="loadButton"
+                    type="danger"
+                    style="padding: 3px 4px 3px 4px;margin: 2px;"
+                    size="mini "
+                    icon="el-icon-delete"
+                    @click="deleteRowEvent(row)"
+                  >删除
+                  </el-button>
+                  <!--</template>-->
+                </template>
+              </vxe-table-column>
+            </vxe-table-column>
+          </vxe-table>
+          <vxe-modal
+            ref="xModal"
+            v-model="showEdit"
+            :title="selectRow ? '编辑&保存' : '新增&保存'"
+            width="800"
+            resize
+            destroy-on-close
+          >
+            <el-form
+              :model="formData"
+              :rules="rules"
+              ref="ruleForm"
+              label-width="100px"
+              title-align="center"
+              title-width="100"
+            >
+              <el-form-item label="用户名" prop="username" class="formItem" >
+                <el-input v-model="formData.username" placeholder="填写用户名" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="密码" prop="password" class="formItem" >
+                <el-input type="password" v-model="formData.password" placeholder="填写密码" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="姓名" prop="name" class="formItem" >
+                <el-input v-model="formData.name" placeholder="填写姓名" clearable></el-input>
+              </el-form-item>
+              <el-form-item label="状态" prop="status" class="formItem" >
+                <el-select v-model="formData.status" placeholder="请选择状态" size="medium">
+                  <el-option
+                    v-for="item in userStatus"
+                    :key="item.value"
+                    :value="item.value"
+                    :label="item.label"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item label="配置菜单" prop="menu" class="formItem" >
+                  <el-cascader
+                    v-model="formData.menu"
+                    :options="options"
+                    :props="props"
+                    collapse-tags
+                  />
+              </el-form-item>
+              <el-form-item align="center" span="24">
+                <el-button status="primary" @click="editSave">保存</el-button>
+                <el-button @click="cancelRowEvent">取消</el-button>
+              </el-form-item>
+            </el-form>
+          </vxe-modal>
+        </div>
+      </el-card>
+    </div>
+
+    <!-- 删除提示框 -->
+    <el-dialog :visible.sync="delVisible" title="提示" width="300px" center>
+      <div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="deleteCancel()">取 消</el-button>
+        <el-button type="primary" @click="deleteInfo()">确 定</el-button>
+      </span>
+    </el-dialog>
+
+  </div>
+
+</template>
+
+<script>
+
+  export default {
+    data() {
+      let strTopShiftReg = /(\~(?=\!)|\!(?=\@)|\@(?=\#)|\#(?=\$)|\$(?=\%)|\%(?=\^)|\^(?=\&)|\&(?=\*)|\*(?=\()|\((?=\))|\)(?=\_)|\_(?=\+)|\+(?=\~)){3}/
+      let strTopResverShiftReg = /(\+(?=\_)|\_(?=\))|\)(?=\()|\((?=\*)|\*(?=\&)|\&(?=\^)|\^(?=\%)|\%(?=\$)|\$(?=\#)|\#(?=\@)|\@(?=\!)|\!(?=\~)|\~(?=\+)){3}/
+      //字母连续规则
+      let strReg = /(a(?=b)|b(?=c)|c(?=d)|d(?=e)|e(?=f)|f(?=g)|g(?=h)|h(?=i)|i(?=j)|j(?=k)|k(?=l)|l(?=m)|m(?=n)|n(?=o)|o(?=p)|p(?=q)|q(?=r)|r(?=s)|s(?=t)|t(?=u)|u(?=v)|v(?=w)|w(?=x)|x(?=y)|y(?=z)|z(?=a)){3}[a-z]/i
+      let strResverReg = /(a(?=z)|z(?=y)|y(?=x)|x(?=w)|w(?=v)|v(?=u)|u(?=t)|t(?=s)|s(?=r)|r(?=q)|q(?=p)|p(?=o)|o(?=n)|n(?=m)|m(?=l)|l(?=k)|k(?=j)|j(?=i)|i(?=h)|h(?=g)|g(?=f)|f(?=e)|e(?=d)|d(?=c)|c(?=b)|b(?=a)){3}[a-z]/i
+      //数字连续规则
+      let numReg = /(\`(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)|9(?=0)|0(?=\-)|\-(?=\=)|\=(?=\`)){3}/
+      let numResverReg = /(\=(?=\-)|\-(?=0)|0(?=9)|9(?=8)|8(?=7)|7(?=6)|6(?=5)|5(?=4)|4(?=3)|3(?=2)|2(?=1)|1(?=\`)|\`(?=\=)){3}/
+      //键盘字母横向连续规则
+      let keyboardHorizontalReg = /(q(?=w)|w(?=e)|e(?=r)|r(?=t)|t(?=y)|y(?=u)|u(?=i)|i(?=o)|o(?=p)|p(?=q)|a(?=s)|s(?=d)|d(?=f)|f(?=g)|g(?=h)|h(?=j)|j(?=k)|k(?=l)|l(?=a)|z(?=x)|x(?=c)|c(?=v)|v(?=b)|b(?=n)|n(?=m)|m(?=z)){3}[a-z]/i
+      let keyboardHorizontalResverReg = /(p(?=o)|o(?=i)|i(?=u)|u(?=y)|y(?=t)|t(?=r)|r(?=e)|e(?=w)|w(?=q)|q(?=p)|l(?=k)|k(?=j)|j(?=h)|h(?=g)|g(?=f)|f(?=d)|d(?=s)|s(?=a)|a(?=l)|m(?=n)|n(?=b)|b(?=v)|v(?=c)|c(?=x)|x(?=z)|z(?=m)){3}[a-z]/i
+      //多个相同字母、数字规则
+      let sameReg = /([0-9a-zA-Z])\1{3}/
+      let keyboardSlopeArr = ["1qaz", "2wsx", "3edc", "4rfv", "5tgb", "6yhn", "7ujm", "8ik,", "9ol.", "0p;/", "/;p0", ".lo9", ",ki8", "mju7", "nhy6", "bgt5", "vfr4", "cde3", "xsw2", "zaq1", "4esz", "5rdx", "6tfc", "7ygv", "8uhb", "9ijn", "0okm", "-pl,", "=[;.", ".;[=", ",lp-", "mko0", "nji9", "bhu8", "vgy7", "cft6", "xdr5", "zse4"]
+      let keyboardSlopeShiftArr = ["!qaz", "@wsx", "#edc","$rfv", "%tgb", "^yhn", "&ujm", "*ik<", "(ol>", ")p:?", "?:p)", ">lo(", "<ki*", "mju&", "nhy^", "bgt%", "vfr$", "cde#", "xsw@", "zaq!", "$esz", "%rdx", "^tfc", "&ygv", "*uhb", "(ijn", ")okm", "_pl<", "+{:>", ">:{+", "<lp_", "mko)", "nji(", "bhu*", "vgy&", "cft^", "xdr%", "zse$"]
+      const checkName = ({rule, value, callback}) => {
+        var username = this.formData.username
+        var s6 = this.tableDatas
+        if (username == null || username === '') {
+          return Promise.reject(new Error('请填写用户名'))
+        }
+        for (let i = 0; i < s6.length; i++) {
+          if (this.id == '' || this.id == undefined) {
+            // 新增
+            if ((username == s6[i].username)) {
+              return Promise.reject(new Error('用户名不能重复'))
+            }
+          } else {
+            // 修改
+            if (this.id != s6[i].id) {
+              if ((username == s6[i].username)) {
+                return Promise.reject(new Error('用户名不能重复'))
+              }
+            }
+          }
+        }
+        return Promise.resolve()
+      }
+      const checkpassword = (rule, value, callback) => {
+        var password = this.formData.password
+        if (this.fileUploadNodeShowSysValue==0){
+          if (sameReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有连续4位相同的数字或字母'))
+          } else if (strResverReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位连续的字母'))
+          } else if (strReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位键盘横向方向连续的字符'))
+          } else if (numReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位键盘横向方向连续的字符'))
+          } else if (numResverReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位连续的数字'))
+          } else if (keyboardHorizontalReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位键盘横向方向连续的字母'))
+          } else if (keyboardHorizontalResverReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位键盘横向方向连续的字母'))
+          } else if (keyboardSlopeArr.some(v => password.toLowerCase().indexOf(v) > -1)) {
+            return Promise.reject(new Error('密码不能含有4位键盘斜向方向连续的字符'))
+          }else if (keyboardSlopeShiftArr.some(v => password.toLowerCase().indexOf(v) > -1)) {
+            return Promise.reject(new Error('密码不能含有4位键盘斜向方向连续的字符'))
+          }else if (strTopShiftReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位键盘横向方向连续的字符'))
+          }else if (strTopResverShiftReg.test(password)) {
+            return Promise.reject(new Error('密码不能含有4位键盘横向方向连续的字符'))
+          }
+        }
+        return Promise.resolve()
+      }
+      return {
+        fileUploadNodeShowSysValue: '1',
+        id: '',
+        tableDatas: [],
+        elStations: [],
+        elStation: '',
+        total: 0,
+        pageSize: 5,
+        currentPage: 1,
+        loadButton: false,
+        rowId: '',
+        showTable: true,
+        tableData: [],
+        delVisible: false,
+        loading: false,
+        // 是否为编辑
+        isEdit: false,
+        pvRotationMode: [],
+        userStatus: [{value: "0", label: "正常"}, {value: "2", label: "禁用"}],
+        // 表单验证规则
+        rules: {
+          username: [
+            {required: true, validator: checkName},
+            {message: '输入过长', max: 15}
+          ],
+          password: [
+            {required: true, message: '请填写密码', validator: checkpassword},
+            {
+              pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{7,10}/,
+              message: '由8位数字、字母大小写及特殊符号组成'
+            }
+          ],
+          name:[{required: true, message: '请填写姓名', trigger: 'blur'}],
+          status:[{required: true, message: '请选择状态', trigger: 'change'}]
+        },
+        formData:{},
+        showEdit:false,
+        selectRow:false,
+        props: { multiple: true },
+        options:[],
+        childrenOption:[]
+      }
+    },
+    created() {
+      // this.getParameterBySysKey()
+      this.getAll()
+      // this.getAllMenu()
+    },
+
+    methods: {
+      // getParameterBySysKey(){
+      //   this.$axios.get('/sysParameter/sysParameterKey/FILE_UPLOAD_NODE_SHOW').then(res => {
+      //     this.fileUploadNodeShowSysValue = res.data.sysValue
+      //   })
+      // },
+      getAll() {
+        this.loading = true
+        this.$axios.get('/sysUserController/getAll').then((res) => {
+          this.tableData = res.data
+          if (res.data == null) {
+            this.showTable = false
+          } else {
+            this.showTable = true
+          }
+          this.loading = false
+        }).catch((error) => {
+          this.$message.error('获取用户信息出错' + error)
+        })
+      },
+      // getAllMenu(){
+      //   this.$axios.get('/sysMenu').then(res => {
+      //     // console.log(res.data)
+      //     var data = res.data
+      //     var option = [{value:9001,label:'后台管理', disabled: true}]
+      //     var option9000=[{value:9000,label:'统计查询', disabled: true}]
+      //     for(var i=0;i<data.length;i++){
+      //       var children = []
+      //       if(data[i].children.length>0){
+      //         for (var j =0;j<data[i].children.length;j++) {
+      //           if(data[i].children[j].id ==1){
+      //             children.push({
+      //               id: data[i].children[j].id,
+      //               fId:58,
+      //               value:data[i].children[j].id,
+      //               label: data[i].children[j].title,
+      //               path: data[i].children[j].path,
+      //               itemPath: data[i].children[j].itemPath,
+      //               url: data[i].children[j].url
+      //             })
+      //             this.childrenOption.push({
+      //               id: data[i].children[j].id,
+      //               fId:58,
+      //               value:data[i].children[j].id,
+      //               label: data[i].children[j].title,
+      //               path: data[i].children[j].path,
+      //               itemPath: data[i].children[j].itemPath,
+      //               url: data[i].children[j].url
+      //             })
+      //           }else{
+      //             children.push({
+      //               id: data[i].children[j].id,
+      //               fId:data[i].id,
+      //               value:data[i].children[j].id,
+      //               label: data[i].children[j].title,
+      //               path: data[i].children[j].path,
+      //               itemPath: data[i].children[j].itemPath,
+      //               url: data[i].children[j].url
+      //             })
+      //             this.childrenOption.push({
+      //               id: data[i].children[j].id,
+      //               fId:data[i].id,
+      //               value:data[i].children[j].id,
+      //               label: data[i].children[j].title,
+      //               path: data[i].children[j].path,
+      //               itemPath: data[i].children[j].itemPath,
+      //               url: data[i].children[j].url
+      //             })
+      //           }
+      //
+      //         }
+      //       }
+      //       if(data[i].url == '9000'){
+      //         option9000.push({
+      //           id: data[i].id,
+      //           value:data[i].id,
+      //           label: data[i].title,
+      //           itemPath: data[i].itemPath,
+      //           url: data[i].url,
+      //           children:children
+      //         })
+      //       }else{
+      //         option.push({
+      //           id: data[i].id,
+      //           value:data[i].id,
+      //           label: data[i].title,
+      //           itemPath: data[i].itemPath,
+      //           url: data[i].url,
+      //           children:children
+      //         })
+      //       }
+      //     }
+      //     this.options = option.concat(option9000)
+      //     // console.log(this.childrenOption)
+      //   })
+      // },
+      insertEvent(row) {
+        this.id = ''
+        this.showTable = true
+        this.isEdit = false
+        // this.loadButton = true
+        this.showEdit = true
+        this.selectRow = false
+        this.formData={}
+        // this.$refs.xTable.insert().then(({row}) => this.$refs.xTable.setActiveRow(row))
+      },
+      editRowEvent(row) {
+        this.id = row.id
+        this.isEdit = true
+        // this.loadButton = true
+        this.showEdit = true
+        this.selectRow = true
+        this.formData = row
+        this.formData.menu = this.formatMenu(this.formData.menu)
+        // this.$refs.xTable.setActiveRow(row)
+      },
+      editSave() {
+        this.$refs["ruleForm"].validate(valid => {
+          if (valid) {
+            var menu = []
+            if(this.formData.menu != null){
+              for(var i=0;i<this.formData.menu.length;i++){
+                if(menu.find(value=>value == this.formData.menu[i][0])){
+                  menu.push(this.formData.menu[i][1])
+                }else{
+                  menu.push(this.formData.menu[i][0])
+                  menu.push(this.formData.menu[i][1])
+                }
+              }
+              // console.log(this.formData.menu)
+              this.formData.menu = menu.toString()
+            }else{
+              this.formData.menu = ''
+            }
+            if (this.isEdit) {
+              // 编辑保存
+              this.$axios.put('/sysUser/', this.formData).then(res => {
+                this.$message({
+                  message: '修改成功',
+                  type: 'success'
+                })
+                this.loadButton = false
+                this.getAll()
+              }).catch((error) => {
+                // this.$refs.xTable.setActiveRow(row)
+                this.$message.error('修改用户出错' + error)
+              })
+            } else {
+              // 新增保存
+              this.formData.stationCode = this.elStation
+              console.log(this.formData)
+              this.$axios.post('/sysUser/', this.formData).then(res => {
+                this.$message({
+                  message: '保存成功',
+                  type: 'success'
+                })
+                this.loadButton = false
+                this.getAll()
+              }).catch((error) => {
+                this.$message.error('修改用户出错' + error)
+              })
+            }
+            this.showEdit = false
+            this.selectRow = false
+          }
+        })
+
+      },
+      cancelRowEvent(row) {
+        // const xTable = this.$refs.xTable
+        this.$refs.xModal.close()
+        this.loadButton = false
+        // xTable.clearActived().then(() => {
+        //   // 还原行数据
+        //   if (this.isEdit) {
+        //     // 编辑
+        //     xTable.revertData(row)
+        //   } else {
+        //     // 新增
+        //     xTable.remove(row)
+        //   }
+        //   this.loadButton = false
+        // })
+      },
+      // 删除场站信息
+      deleteRowEvent(row) {
+        this.rowId = row.id
+        this.delVisible = true
+      },
+      deleteCancel() {
+        this.delVisible = false
+      },
+      deleteInfo() {
+        this.$axios.delete('/sysUser/' + this.rowId).then(res => {
+          this.$message({
+            message: '删除成功',
+            type: 'success'
+          })
+          this.delVisible = false
+          this.getAll()
+        }).catch((error) => {
+          this.$message.error('删除用户出错' + error)
+        })
+      },
+      formatStatus({ cellValue }){
+        const item = this.userStatus.find(item => item.value === cellValue)
+        return item ? item.label : ''
+      },
+      formatMenu(menu){
+        if(menu === null){
+          return null
+        }else{
+          var data = menu.split(",")
+          var menuArr = []
+          for (var i=0;i<data.length;i++){
+            for(var j=0;j<this.childrenOption.length;j++){
+              if(this.childrenOption[j].id == data[i]){
+                menuArr.push([this.childrenOption[j].fId,this.childrenOption[j].id])
+              }
+            }
+          }
+          return menuArr
+        }
+
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+  .my_table_insert .vxe-body--row.is--new {
+    background-color: #f1fdf1;
+  }
+  .formItem{
+    width: 45%;
+    display: inline-block;
+  }
+  /*/deep/*/
+  /*.vxe-input--inner{*/
+  /*  width: auto;*/
+  /*}*/
+
+</style>