drew 5 anos atrás
pai
commit
49509ca5a3
26 arquivos alterados com 381 adições e 131 exclusões
  1. 23 8
      pom.xml
  2. 4 0
      src/main/java/com/izouma/awesomeAdmin/Application.java
  3. 17 0
      src/main/java/com/izouma/awesomeAdmin/config/CacheConfig.java
  4. 0 44
      src/main/java/com/izouma/awesomeAdmin/config/RedisConfig.java
  5. 1 6
      src/main/java/com/izouma/awesomeAdmin/domain/User.java
  6. 12 0
      src/main/java/com/izouma/awesomeAdmin/dto/Captcha.java
  7. 37 0
      src/main/java/com/izouma/awesomeAdmin/dto/UserRegister.java
  8. 8 0
      src/main/java/com/izouma/awesomeAdmin/repo/UserRepo.java
  9. 0 6
      src/main/java/com/izouma/awesomeAdmin/security/JwtTokenUtil.java
  10. 1 1
      src/main/java/com/izouma/awesomeAdmin/security/JwtUser.java
  11. 1 0
      src/main/java/com/izouma/awesomeAdmin/security/WebSecurityConfig.java
  12. 49 0
      src/main/java/com/izouma/awesomeAdmin/service/CaptchaService.java
  13. 2 2
      src/main/java/com/izouma/awesomeAdmin/service/GenCodeService.java
  14. 15 6
      src/main/java/com/izouma/awesomeAdmin/service/UserService.java
  15. 30 0
      src/main/java/com/izouma/awesomeAdmin/web/CaptchaController.java
  16. 2 2
      src/main/java/com/izouma/awesomeAdmin/web/SysConfigController.java
  17. 17 11
      src/main/java/com/izouma/awesomeAdmin/web/UserController.java
  18. 2 0
      src/main/resources/application.yaml
  19. 3 3
      src/main/resources/templates/ControllerTemplate.ftl
  20. 2 4
      src/main/resources/templates/ListViewTemplate.ftl
  21. 2 2
      src/main/resources/templates/RepoTemplate.ftl
  22. 6 6
      src/main/vue/src/mixins/pageableTable.js
  23. 134 2
      src/main/vue/src/views/Admin.vue
  24. 11 17
      src/main/vue/src/views/UserEdit.vue
  25. 2 10
      src/main/vue/src/views/UserList.vue
  26. 0 1
      src/test/java/com/izouma/awesomeAdmin/repo/UserRepoTest.java

+ 23 - 8
pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>2.1.8.RELEASE</version>
+        <version>2.3.2.RELEASE</version>
         <relativePath/> <!-- lookup parent from repository -->
     </parent>
     <groupId>com.izouma</groupId>
@@ -71,6 +71,21 @@
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
@@ -94,11 +109,6 @@
             <optional>true</optional>
         </dependency>
 
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-data-redis</artifactId>
-        </dependency>
-
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-pool2</artifactId>
@@ -116,7 +126,6 @@
             <version>1.3</version>
         </dependency>
 
-
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt</artifactId>
@@ -193,7 +202,7 @@
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>easyexcel</artifactId>
-            <version>2.2.3</version>
+            <version>2.2.6</version>
         </dependency>
 
         <dependency>
@@ -251,6 +260,12 @@
         </dependency>
         <!-- 钉钉 -->
 
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+            <version>1.6.2</version>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 4 - 0
src/main/java/com/izouma/awesomeAdmin/Application.java

@@ -2,12 +2,16 @@ package com.izouma.awesomeAdmin;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
 import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.scheduling.annotation.EnableScheduling;
 import springfox.documentation.swagger2.annotations.EnableSwagger2;
 
 @SpringBootApplication
 @EnableJpaAuditing
 @EnableSwagger2
+@EnableCaching
+@EnableScheduling
 public class Application {
 
     public static void main(String[] args) {

+ 17 - 0
src/main/java/com/izouma/awesomeAdmin/config/CacheConfig.java

@@ -0,0 +1,17 @@
+package com.izouma.awesomeAdmin.config;
+
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.support.SimpleCacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@EnableCaching
+@Configuration
+public class CacheConfig {
+    @Bean
+    public CacheManager cacheManager() {
+        SimpleCacheManager manager = new SimpleCacheManager();
+        return manager;
+    }
+}

+ 0 - 44
src/main/java/com/izouma/awesomeAdmin/config/RedisConfig.java

@@ -1,44 +0,0 @@
-package com.izouma.awesomeAdmin.config;
-
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-import com.fasterxml.jackson.annotation.PropertyAccessor;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
-import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
-import org.springframework.data.redis.serializer.StringRedisSerializer;
-
-@Configuration
-@AutoConfigureAfter(RedisAutoConfiguration.class)
-@EnableRedisRepositories
-public class RedisConfig {
-
-    @Bean
-    RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
-
-        RedisTemplate<String, Object> template = new RedisTemplate<>();
-        template.setConnectionFactory(redisConnectionFactory);
-
-        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
-        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class);
-
-        ObjectMapper mapper = new ObjectMapper();
-        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
-        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
-        serializer.setObjectMapper(mapper);
-
-        template.setValueSerializer(serializer);
-        //使用StringRedisSerializer来序列化和反序列化redis的key值
-        template.setKeySerializer(new StringRedisSerializer());
-        template.setHashKeySerializer(new StringRedisSerializer());
-        template.setHashValueSerializer(serializer);
-        template.afterPropertiesSet();
-        return template;
-    }
-
-}

+ 1 - 6
src/main/java/com/izouma/awesomeAdmin/domain/User.java

@@ -11,15 +11,12 @@ import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import org.hibernate.annotations.BatchSize;
-import org.hibernate.annotations.Where;
 
 import javax.persistence.*;
 import javax.validation.constraints.Pattern;
 import javax.validation.constraints.Size;
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 @Data
@@ -27,7 +24,6 @@ import java.util.Set;
 @AllArgsConstructor
 @NoArgsConstructor
 @Builder
-@Where(clause = "enabled = 1")
 @ApiModel(value = "用户", description = "用户")
 public class User extends BaseEntity implements Serializable {
 
@@ -45,8 +41,7 @@ public class User extends BaseEntity implements Serializable {
     @JsonIgnore
     private String password;
 
-    @Column(nullable = false)
-    private Boolean enabled = true;
+    private boolean del = false;
 
     @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH})
     @JoinTable(

+ 12 - 0
src/main/java/com/izouma/awesomeAdmin/dto/Captcha.java

@@ -0,0 +1,12 @@
+package com.izouma.awesomeAdmin.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class Captcha {
+    private String key;
+    private String image;
+}

+ 37 - 0
src/main/java/com/izouma/awesomeAdmin/dto/UserRegister.java

@@ -0,0 +1,37 @@
+package com.izouma.awesomeAdmin.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.izouma.awesomeAdmin.security.Authority;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}, ignoreUnknown = true)
+public class UserRegister {
+
+    private String username;
+
+    private String nickname;
+
+    private String avatar;
+
+    private String password;
+
+    private Set<Authority> authorities = new HashSet<>();
+
+    private String sex;
+
+    private String phone;
+
+    private String email;
+}

+ 8 - 0
src/main/java/com/izouma/awesomeAdmin/repo/UserRepo.java

@@ -4,10 +4,18 @@ import com.izouma.awesomeAdmin.domain.User;
 import com.izouma.awesomeAdmin.security.Authority;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
 
+import javax.transaction.Transactional;
 import java.util.List;
 
 public interface UserRepo extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
+    @Transactional
+    @Modifying
+    @Query("update User u set u.del = true where u.id = ?1")
+    void softDelete(Long id);
+
     User findByUsername(String username);
 
     List<User> findAllByAuthoritiesContains(Authority authority);

+ 0 - 6
src/main/java/com/izouma/awesomeAdmin/security/JwtTokenUtil.java

@@ -6,12 +6,6 @@ import io.jsonwebtoken.Clock;
 import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.impl.DefaultClock;
-import lombok.AllArgsConstructor;
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.cache.Cache;
-import org.springframework.cache.CacheManager;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.stereotype.Component;
 

+ 1 - 1
src/main/java/com/izouma/awesomeAdmin/security/JwtUser.java

@@ -67,7 +67,7 @@ public class JwtUser implements UserDetails {
 
     @Override
     public boolean isEnabled() {
-        return user.getEnabled();
+        return !user.isDel();
     }
 
     @JsonIgnore

+ 1 - 0
src/main/java/com/izouma/awesomeAdmin/security/WebSecurityConfig.java

@@ -66,6 +66,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers("/files/**").permitAll()
                 .antMatchers("/static/**").permitAll()
                 .antMatchers("/auth/**").permitAll()
+                .antMatchers("/captcha/**").permitAll()
                 .antMatchers("/admin/**").permitAll()
                 .antMatchers("/systemVariable/all").permitAll()
                 .antMatchers("/**/excel").permitAll()

+ 49 - 0
src/main/java/com/izouma/awesomeAdmin/service/CaptchaService.java

@@ -0,0 +1,49 @@
+package com.izouma.awesomeAdmin.service;
+
+import com.izouma.awesomeAdmin.dto.Captcha;
+import com.wf.captcha.SpecCaptcha;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.ehcache.UserManagedCache;
+import org.ehcache.config.builders.ExpiryPolicyBuilder;
+import org.ehcache.config.builders.UserManagedCacheBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.awt.*;
+import java.io.IOException;
+import java.time.Duration;
+import java.util.Objects;
+import java.util.UUID;
+
+@Service
+@AllArgsConstructor
+public class CaptchaService {
+    private final UserManagedCache<String, String> captchaCache =
+            UserManagedCacheBuilder.newUserManagedCacheBuilder(String.class, String.class)
+                    .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
+                    .build(true);
+
+    public Captcha gen() throws IOException, FontFormatException {
+        String key = UUID.randomUUID().toString();
+        SpecCaptcha specCaptcha = new SpecCaptcha(90 * 2, 32 * 2, 5);
+        specCaptcha.setFont(com.wf.captcha.base.Captcha.FONT_7, 24 * 2);
+        String code = specCaptcha.text().toLowerCase();
+        String image = specCaptcha.toBase64();
+        captchaCache.put(key, code);
+        return new Captcha(key, image);
+    }
+
+    public boolean verify(String key, String code) {
+        if (StringUtils.isBlank(key) || StringUtils.isBlank(code)) {
+            return false;
+        }
+        code = code.toLowerCase();
+        boolean verify = false;
+        String trueCode = captchaCache.get(key);
+        if (StringUtils.isNotBlank(trueCode) && trueCode.equals(code)) {
+            verify = true;
+        }
+        return verify;
+    }
+}

+ 2 - 2
src/main/java/com/izouma/awesomeAdmin/service/GenCodeService.java

@@ -170,8 +170,8 @@ public class GenCodeService {
 
     private boolean canSoftDelete(GenCode model) {
         try {
-            Field field = Class.forName(model.getTablePackage()).getDeclaredField("enabled");
-            if (field != null && field.getType().equals(Boolean.class)) {
+            Field field = Class.forName(model.getTablePackage()).getDeclaredField("del");
+            if (field.getType().equals(Boolean.class) || field.getType().equals(boolean.class)) {
                 return true;
             }
         } catch (NoSuchFieldException | ClassNotFoundException ignored) {

+ 15 - 6
src/main/java/com/izouma/awesomeAdmin/service/UserService.java

@@ -6,6 +6,7 @@ import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
 import com.izouma.awesomeAdmin.config.Constants;
 import com.izouma.awesomeAdmin.domain.User;
 import com.izouma.awesomeAdmin.dto.PageQuery;
+import com.izouma.awesomeAdmin.dto.UserRegister;
 import com.izouma.awesomeAdmin.exception.BusinessException;
 import com.izouma.awesomeAdmin.repo.UserRepo;
 import com.izouma.awesomeAdmin.security.Authority;
@@ -22,6 +23,7 @@ import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
 import me.chanjar.weixin.mp.bean.result.WxMpUser;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.data.domain.Page;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
@@ -39,14 +41,17 @@ public class UserService {
     private SmsService     smsService;
     private StorageService storageService;
     private JwtTokenUtil   jwtTokenUtil;
+    private CaptchaService captchaService;
 
     public Page<User> all(PageQuery pageQuery) {
         return userRepo.findAll(JpaUtils.toSpecification(pageQuery, User.class), JpaUtils.toPageRequest(pageQuery));
     }
 
-    public User create(User user) {
-        if (StringUtils.isNotBlank(user.getPassword())) {
-            user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
+    public User create(UserRegister userRegister) {
+        User user = new User();
+        BeanUtils.copyProperties(userRegister, user);
+        if (StringUtils.isNotBlank(userRegister.getPassword())) {
+            user.setPassword(new BCryptPasswordEncoder().encode(userRegister.getPassword()));
         }
         return userRepo.save(user);
     }
@@ -70,7 +75,6 @@ public class UserService {
                     .city(wxMpUser.getCity())
                     .openId(wxMpUser.getOpenId())
                     .language(wxMpUser.getLanguage())
-                    .enabled(true)
                     .authorities(Collections.singleton(Authority.builder().name("ROLE_USER").build()))
                     .build();
             userRepo.save(user);
@@ -92,7 +96,6 @@ public class UserService {
                     .nickname("用户" + RandomStringUtils.randomAlphabetic(6))
                     .openId(openId)
                     .avatar(Constants.DEFAULT_AVATAR)
-                    .enabled(true)
                     .authorities(Collections.singleton(Authority.builder().name("ROLE_USER").build()))
                     .build();
             userInfo = userRepo.save(userInfo);
@@ -136,7 +139,6 @@ public class UserService {
                     .country(wxUserInfo.getCountry())
                     .province(wxUserInfo.getProvince())
                     .city(wxUserInfo.getCity())
-                    .enabled(true)
                     .authorities(Collections.singleton(Authority.builder().name("ROLE_USER").build()))
                     .build();
             user = userRepo.save(user);
@@ -160,4 +162,11 @@ public class UserService {
         user = userRepo.save(user);
         return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
     }
+
+    public String setPassword(Long userId, String key, String code, String password) {
+        if (!captchaService.verify(key, code)) {
+            throw new BusinessException("验证码错误");
+        }
+        return setPassword(userId, password);
+    }
 }

+ 30 - 0
src/main/java/com/izouma/awesomeAdmin/web/CaptchaController.java

@@ -0,0 +1,30 @@
+package com.izouma.awesomeAdmin.web;
+
+import com.izouma.awesomeAdmin.dto.Captcha;
+import com.izouma.awesomeAdmin.service.CaptchaService;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.awt.*;
+import java.io.IOException;
+
+@RestController
+@RequestMapping("/captcha")
+@AllArgsConstructor
+public class CaptchaController {
+
+    private final CaptchaService captchaService;
+
+    @GetMapping("/get")
+    public Captcha get() throws IOException, FontFormatException {
+        return captchaService.gen();
+    }
+
+    @GetMapping("/verify")
+    public boolean verify(@RequestParam String key, @RequestParam String code) {
+        return captchaService.verify(key, code);
+    }
+}

+ 2 - 2
src/main/java/com/izouma/awesomeAdmin/web/SysConfigController.java

@@ -31,8 +31,8 @@ public class SysConfigController extends BaseController {
     }
 
 
-    @GetMapping("/all")
-    public Page<SysConfig> all(PageQuery pageQuery) {
+    @PostMapping("/all")
+    public Page<SysConfig> all(@RequestBody PageQuery pageQuery) {
         return sysConfigService.all(pageQuery);
     }
 

+ 17 - 11
src/main/java/com/izouma/awesomeAdmin/web/UserController.java

@@ -2,6 +2,7 @@ package com.izouma.awesomeAdmin.web;
 
 import com.izouma.awesomeAdmin.domain.User;
 import com.izouma.awesomeAdmin.dto.PageQuery;
+import com.izouma.awesomeAdmin.dto.UserRegister;
 import com.izouma.awesomeAdmin.enums.AuthorityName;
 import com.izouma.awesomeAdmin.exception.BusinessException;
 import com.izouma.awesomeAdmin.repo.UserRepo;
@@ -35,11 +36,10 @@ public class UserController extends BaseController {
     @PostMapping("/register")
     public User register(@RequestParam String username,
                          @RequestParam String password) {
-        User user = User.builder()
+        UserRegister user = UserRegister.builder()
                 .username(username)
                 .nickname(username)
                 .password(new BCryptPasswordEncoder().encode(password))
-                .enabled(true)
                 .authorities(Collections.singleton(Authority.get(AuthorityName.ROLE_USER)))
                 .build();
         return userService.create(user);
@@ -47,18 +47,13 @@ public class UserController extends BaseController {
 
     @PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/create")
-    public User create(@RequestBody User user) {
-        return userService.create(user);
+    public User create(@RequestBody UserRegister userRegister) {
+        return userService.create(userRegister);
     }
 
     @PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/save")
     public User save(@RequestBody User user) {
-        if (user.getId() != null) {
-            User orig = userRepo.findById(user.getId()).orElseThrow(new BusinessException("无记录"));
-            ObjUtils.merge(orig, user);
-            return userRepo.save(orig);
-        }
         return userRepo.save(user);
     }
 
@@ -76,8 +71,8 @@ public class UserController extends BaseController {
     }
 
     @PreAuthorize("hasRole('ADMIN')")
-    @GetMapping("/all")
-    public Page<User> all(PageQuery pageQuery) {
+    @PostMapping("/all")
+    public Page<User> all(@RequestBody PageQuery pageQuery) {
         return userService.all(pageQuery);
     }
 
@@ -87,6 +82,12 @@ public class UserController extends BaseController {
         return userRepo.findById(id).orElseThrow(new BusinessException("无记录"));
     }
 
+    @PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        userRepo.softDelete(id);
+    }
+
     @GetMapping("/excel")
     @ResponseBody
     public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
@@ -110,6 +111,11 @@ public class UserController extends BaseController {
         return userService.setPassword(userId, password);
     }
 
+    @PostMapping("/changePassword")
+    public String changePassword(@RequestParam String password, @RequestParam String key, @RequestParam String code) {
+        return userService.setPassword(SecurityUtils.getAuthenticatedUser().getId(), key, code, password);
+    }
+
     @PreAuthorize("hasRole('ADMIN')")
     @GetMapping("/getToken/{userId}")
     public String getToken(@PathVariable Long userId) {

+ 2 - 0
src/main/resources/application.yaml

@@ -46,6 +46,8 @@ spring:
     freemarker:
         settings:
             number_format: 0
+    cache:
+        type: caffeine
 jwt:
     secret: XvAD0kboD76Dpebm
     header: Authorization

+ 3 - 3
src/main/resources/templates/ControllerTemplate.ftl

@@ -44,8 +44,8 @@ public class ${model.className}Controller extends BaseController {
 
 
     //@PreAuthorize("hasRole('ADMIN')")
-    @GetMapping("/all")
-    public Page<${model.className}> all(PageQuery pageQuery) {
+    @PostMapping("/all")
+    public Page<${model.className}> all(@RequestBody PageQuery pageQuery) {
         return ${model.className?uncap_first}Service.all(pageQuery);
     }
 
@@ -57,7 +57,7 @@ public class ${model.className}Controller extends BaseController {
     @PostMapping("/del/{id}")
     public void del(@PathVariable Long id) {
 <#if softDelete == true>
-        ${model.className?uncap_first}Repo.disable(id);
+        ${model.className?uncap_first}Repo.softDelete(id);
 <#else>
         ${model.className?uncap_first}Repo.deleteById(id);
 </#if>

+ 2 - 4
src/main/resources/templates/ListViewTemplate.ftl

@@ -127,9 +127,7 @@
                 </#if>
             </#list>
             beforeGetData() {
-                if (this.search) {
-                    return { search: this.search };
-                }
+                return { search: this.search };
             },
             toggleMultipleMode(multipleMode) {
                 this.multipleMode = multipleMode;
@@ -141,7 +139,7 @@
                 this.$router.push({
                     path: "/${model.className?uncap_first}Edit",
                     query: {
-                    ...this.$route.query
+                        ...this.$route.query
                     }
                 });
             },

+ 2 - 2
src/main/resources/templates/RepoTemplate.ftl

@@ -12,9 +12,9 @@ import javax.transaction.Transactional;
 
 public interface ${model.className}Repo extends JpaRepository<${model.className}, Long>, JpaSpecificationExecutor<${model.className}> {
 <#if softDelete == true>
-    @Query("update ${model.className} t set t.enabled = false where t.id = ?1")
+    @Query("update ${model.className} t set t.del = true where t.id = ?1")
     @Modifying
     @Transactional
-    void disable(Long id);
+    void softDelete(Long id);
 </#if>
 }

+ 6 - 6
src/main/vue/src/mixins/pageableTable.js

@@ -30,15 +30,15 @@ export default {
     },
     methods: {
         getData() {
-            let urlQuery = { ...this.$route.query };
-            delete urlQuery.sort;
-            delete urlQuery.page;
-            delete urlQuery.size;
+            let query = { del: false, ...this.$route.query };
+            delete query.sort;
+            delete query.page;
+            delete query.size;
             let data = {
                 page: this.page - 1,
                 size: this.pageSize,
                 sort: this.sortStr,
-                query: urlQuery
+                query: query
             };
             if (this.beforeGetData) {
                 let mergeData = this.beforeGetData();
@@ -47,7 +47,7 @@ export default {
                 }
             }
             this.$http
-                .get(this.url, data)
+                .post(this.url, data, { body: 'json' })
                 .then(res => {
                     this.tableData = res.content;
                     this.totalPages = res.totalPages;

+ 134 - 2
src/main/vue/src/views/Admin.vue

@@ -45,9 +45,10 @@
                     </div>
                 </el-tooltip>
 
-                <el-dropdown @command="onCommand" style="margin-left: 20px;">
+                <el-dropdown @command="onCommand" style="margin-left: 20px;" trigger="click">
                     <img :src="userInfo ? userInfo.avatar || '' : ''" class="avatar" />
                     <el-dropdown-menu slot="dropdown">
+                        <el-dropdown-item command="pwd" style="word-break:keep-all">修改密码 </el-dropdown-item>
                         <el-dropdown-item command="logout">退出登录 </el-dropdown-item>
                     </el-dropdown-menu>
                 </el-dropdown>
@@ -56,6 +57,31 @@
                 <router-view></router-view>
             </el-main>
         </el-container>
+        <el-dialog
+            :visible.sync="showPwdDialog"
+            title="修改密码"
+            width="500px"
+            top="30vh"
+            :close-on-click-modal="false"
+            custom-class="change-pwd-dialog"
+        >
+            <el-form :model="pwdForm" label-width="100px" label-position="right" ref="pwdForm" :rules="pwdRules">
+                <el-form-item label="输入新密码" prop="password">
+                    <el-input v-model="pwdForm.password" type="password" placeholder="输入新密码"></el-input>
+                </el-form-item>
+                <el-form-item label="确认新密码" prop="repeat">
+                    <el-input v-model="pwdForm.repeat" type="password" placeholder="确认新密码"></el-input>
+                </el-form-item>
+                <el-form-item label="验证码" prop="code">
+                    <el-input v-model="pwdForm.code" placeholder="请输入验证码" style="width:150px"></el-input>
+                    <img :src="captcha" class="captcha-image" @click="refreshCaptcha" />
+                </el-form-item>
+            </el-form>
+            <span slot="footer">
+                <el-button @click="showPwdDialog = false">取 消</el-button>
+                <el-button type="primary" @click="savePwd" :loading="pwdLoading">确认修改</el-button>
+            </span>
+        </el-dialog>
     </el-container>
 </template>
 
@@ -82,7 +108,55 @@ export default {
             activeMenu: '',
             menuPath: [],
             collapse: localStorage.getItem('menu-collapse') == 'true',
-            isFullscreen: false
+            isFullscreen: false,
+            showPwdDialog: false,
+            pwdForm: {
+                password: '',
+                repeat: '',
+                code: '',
+                key: ''
+            },
+            captcha: '',
+            pwdRules: {
+                password: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
+                repeat: [
+                    { required: true, message: '请确认新密码', trigger: 'blur' },
+                    {
+                        validator: (rule, value, callback) => {
+                            if (value && value == this.pwdForm.password) {
+                                callback();
+                            } else {
+                                callback(new Error('两次密码输入不一致'));
+                            }
+                        },
+                        trigger: 'blur'
+                    }
+                ],
+                code: [
+                    { required: true, message: '请输入验证码', trigger: 'blur' },
+                    {
+                        validator: (rule, value, callback) => {
+                            this.$http
+                                .get('/captcha/verify', {
+                                    key: this.pwdForm.key,
+                                    code: value
+                                })
+                                .then(res => {
+                                    if (res === true) {
+                                        callback();
+                                    } else {
+                                        callback(new Error('验证码错误'));
+                                    }
+                                })
+                                .catch(e => {
+                                    callback(new Error('验证码错误'));
+                                });
+                        },
+                        trigger: 'blur'
+                    }
+                ]
+            },
+            pwdLoading: false
         };
     },
     computed: {
@@ -140,7 +214,54 @@ export default {
                 localStorage.removeItem('token');
                 this.$store.commit('updateUserInfo', null);
                 this.$router.replace('/login');
+            } else if (command === 'pwd') {
+                this.onShowPwdDialog();
             }
+        },
+        onShowPwdDialog() {
+            this.$refs.pwdForm && this.$refs.pwdForm.resetFields();
+            this.$http.get('/captcha/get').then(res => {
+                this.captcha = res.image;
+                this.pwdForm = {
+                    password: '',
+                    repeat: '',
+                    code: '',
+                    key: res.key
+                };
+                this.showPwdDialog = true;
+            });
+        },
+        savePwd() {
+            this.pwdLoading = true;
+            this.$refs.pwdForm.validate(valid => {
+                if (valid) {
+                    this.$http
+                        .post('/user/setPassword', {
+                            password: this.pwdForm.password
+                        })
+                        .then(res => {
+                            console.log(res);
+                            this.pwdLoading = false;
+                            this.showPwdDialog = false;
+                            localStorage.removeItem('token');
+                            this.$store.commit('updateUserInfo', null);
+                            this.$router.replace('/login');
+                            this.$message.success('修改成功,请重新登录');
+                        })
+                        .catch(e => {
+                            this.pwdLoading = false;
+                            this.$message.error(e.error || '修改失败');
+                        });
+                } else {
+                    this.pwdLoading = false;
+                }
+            });
+        },
+        refreshCaptcha() {
+            this.$http.get('/captcha/get').then(res => {
+                this.captcha = res.image;
+                this.pwdForm.key = res.key;
+            });
         }
     },
     watch: {
@@ -173,6 +294,17 @@ export default {
         }
     }
 }
+.change-pwd-dialog {
+    .captcha-image {
+        height: 32px;
+        vertical-align: middle;
+        margin-left: 10px;
+        cursor: pointer;
+    }
+    .el-dialog__body {
+        padding-bottom: 0;
+    }
+}
 </style>
 <style lang="less" scoped>
 #app {

+ 11 - 17
src/main/vue/src/views/UserEdit.vue

@@ -113,7 +113,7 @@ export default {
         },
         submit() {
             this.$http
-                .post('/user/save', this.formData, { body: 'json' })
+                .post(this.formData.id ? '/user/save' : '/user/create', this.formData, { body: 'json' })
                 .then(res => {
                     this.$message.success('成功');
                     this.formData = res;
@@ -131,23 +131,17 @@ export default {
         del() {
             this.$confirm('确认删除吗?', '提示', { type: 'warning' })
                 .then(() => {
-                    this.$http
-                        .post({
-                            url: '/userInfo/del',
-                            data: {
-                                id: this.formData.id
-                            }
-                        })
-                        .then(res => {
-                            if (res.success) {
-                                this.$message.success('成功');
-                                this.$router.go(-1);
-                            } else {
-                                this.$message.warning('失败');
-                            }
-                        });
+                    return this.$http.post(`/user/del/${this.formData.id}`);
                 })
-                .catch(() => {});
+                .then(res => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if ('cancel' !== e) {
+                        this.$message.error(e.error || '删除失败');
+                    }
+                });
         },
         resetPassword() {
             this.$prompt('请输入新密码', '重置密码', { inputType: 'password' })

+ 2 - 10
src/main/vue/src/views/UserList.vue

@@ -24,16 +24,8 @@
             cell-class-name="table-cell"
         >
             <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
-            <el-table-column prop="id" label="ID" width="100">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
-            <el-table-column prop="username" label="用户名" min-width="300">
-                <template slot="header" slot-scope="{ column }">
-                    <sortable-header :column="column" :current-sort="sort" @changeSort="changeSort"> </sortable-header>
-                </template>
-            </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="username" label="用户名" min-width="300"> </el-table-column>
             <el-table-column prop="nickname" label="昵称" min-width="300"> </el-table-column>
             <el-table-column label="头像" min-width="300">
                 <template slot-scope="{ row }">

+ 0 - 1
src/test/java/com/izouma/awesomeAdmin/repo/UserRepoTest.java

@@ -23,7 +23,6 @@ public class UserRepoTest {
                 .username("testuser")
                 .password("123")
                 .avatar("")
-                .enabled(true)
                 .build();
         userRepo.save(user);
     }