Browse Source

fix bug of RedisTemplate

master
刘力 3 years ago
parent
commit
42fa5e4366
  1. 198
      common/src/main/java/com/storeroom/config/RedisConfig.java
  2. 109
      common/src/main/java/com/storeroom/config/SwaggerConfig.java
  3. 2
      common/src/main/java/com/storeroom/utils/RsaUtils.java
  4. 3
      system/src/main/java/com/storeroom/modules/security/controller/AuthorizationController.java
  5. 52
      system/src/main/java/com/storeroom/modules/security/controller/OnlineController.java
  6. 14
      system/src/main/java/com/storeroom/modules/system/domain/User.java
  7. 117
      system/src/main/resources/application-prod.yml
  8. 3
      system/src/main/resources/application.yml
  9. BIN
      system/src/main/resources/ip2region/ip2region.db
  10. 48
      system/src/main/resources/template/email/email.ftl
  11. 69
      system/src/main/resources/template/email/taskAlarm.ftl
  12. 73
      system/src/main/resources/template/generator/admin/Controller.ftl
  13. 40
      system/src/main/resources/template/generator/admin/Dto.ftl
  14. 71
      system/src/main/resources/template/generator/admin/Entity.ftl
  15. 18
      system/src/main/resources/template/generator/admin/Mapper.ftl
  16. 67
      system/src/main/resources/template/generator/admin/QueryCriteria.ftl
  17. 26
      system/src/main/resources/template/generator/admin/Repository.ftl
  18. 69
      system/src/main/resources/template/generator/admin/Service.ftl
  19. 143
      system/src/main/resources/template/generator/admin/ServiceImpl.ftl
  20. 27
      system/src/main/resources/template/generator/front/api.ftl
  21. 175
      system/src/main/resources/template/generator/front/index.ftl

198
common/src/main/java/com/storeroom/config/RedisConfig.java

@ -0,0 +1,198 @@
package com.storeroom.config;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.storeroom.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Configuration
@EnableCaching
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig extends CachingConfigurerSupport {
/**
* 设置 redis 数据默认过期时间默认2小时
* 设置@cacheable 序列化方式
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration(){
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.
SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(6));
return configuration;
}
@SuppressWarnings("all")
@Bean(name = "redisTemplate")
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//序列化
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 全局开启AutoType这里方便开发使用全局的方式
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式小范围指定白名单
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
/**
* 自定义缓存key生成策略默认将使用该策略
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
Map<String,Object> container = new HashMap<>(3);
Class<?> targetClassClass = target.getClass();
// 类地址
container.put("class",targetClassClass.toGenericString());
// 方法名称
container.put("methodName",method.getName());
// 包名称
container.put("package",targetClassClass.getPackage());
// 参数列表
for (int i = 0; i < params.length; i++) {
container.put(String.valueOf(i),params[i]);
}
// 转为JSON字符串
String jsonString = JSON.toJSONString(container);
// 做SHA256 Hash计算得到一个SHA256摘要作为Key
return DigestUtils.sha256Hex(jsonString);
};
}
@Bean
@Override
public CacheErrorHandler errorHandler() {
// 异常处理当Redis发生异常时打印日志但是程序正常走
log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
}
@Override
public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
}
@Override
public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
}
@Override
public void handleCacheClearError(RuntimeException e, Cache cache) {
log.error("Redis occur handleCacheClearError:", e);
}
};
}
}
/**
* Value 序列化
*
* @author /
* @param <T>
*/
class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
private final Class<T> clazz;
FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
}
@Override
public T deserialize(byte[] bytes) {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, StandardCharsets.UTF_8);
return JSON.parseObject(str, clazz);
}
}
/**
* 重写序列化器
*
* @author /
*/
class StringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
StringRedisSerializer() {
this(StandardCharsets.UTF_8);
}
private StringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}
@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
@Override
public byte[] serialize(Object object) {
String string = JSON.toJSONString(object);
if (StringUtils.isBlank(string)) {
return null;
}
string = string.replace("\"", "");
return string.getBytes(charset);
}
}

109
common/src/main/java/com/storeroom/config/SwaggerConfig.java

@ -0,0 +1,109 @@
package com.storeroom.config;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Predicates;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.data.domain.Pageable;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.AlternateTypeRule;
import springfox.documentation.schema.AlternateTypeRuleConvention;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Value("${jwt.header}")
private String tokenHeader;
@Value("${jwt.token-start-with}")
private String tokenStartWith;
@Value("${swagger.enabled}")
private Boolean enabled;
@Bean
@SuppressWarnings("all")
public Docket createRestApi() {
ParameterBuilder ticketPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
ticketPar.name(tokenHeader).description("token")
.modelRef(new ModelRef("string"))
.parameterType("header")
.defaultValue(tokenStartWith + " ")
.required(true)
.build();
pars.add(ticketPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.enable(enabled)
.apiInfo(apiInfo())
.select()
.paths(Predicates.not(PathSelectors.regex("/error.*")))
.build()
.globalOperationParameters(pars);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.description("阅行客后台管理系统")
.title("YXK-ADMIN 接口文档")
.version("2.4")
.build();
}
}
/**
* 将Pageable转换展示在swagger中
*/
@Configuration
class SwaggerDataConfig {
@Bean
public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) {
return new AlternateTypeRuleConvention() {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public List<AlternateTypeRule> rules() {
return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class)));
}
};
}
@ApiModel
@Data
private static class Page {
@ApiModelProperty("页码 (0..N)")
private Integer page;
@ApiModelProperty("每页显示的数目")
private Integer size;
@ApiModelProperty("以下列格式排序标准:property[,asc | desc]。 默认排序顺序为升序。 支持多种排序条件:如:id,asc")
private List<String> sort;
}
}

2
common/src/main/java/com/storeroom/utils/RsaUtils.java

@ -85,6 +85,8 @@ public class RsaUtils {
System.out.println("***************** 私钥加密公钥解密结束 *****************");
}
/**
* 公钥解密
*

3
system/src/main/java/com/storeroom/modules/security/controller/AuthorizationController.java

@ -16,13 +16,11 @@ import com.storeroom.modules.security.service.OnlineUserService;
import com.storeroom.modules.security.service.dto.AuthUserDto;
import com.storeroom.modules.security.service.dto.JwtUserDto;
import com.storeroom.utils.*;
import com.storeroom.utils.enums.DataStatusEnum;
import com.wf.captcha.base.Captcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@ -113,6 +111,7 @@ public class AuthorizationController {
put("img", captcha.toBase64());
put("uuid", uuid);
}};
//return ApiResponse.success(imgResult);
return ApiResponse.success(imgResult);
}

52
system/src/main/java/com/storeroom/modules/security/controller/OnlineController.java

@ -0,0 +1,52 @@
package com.storeroom.modules.security.controller;
import com.storeroom.modules.security.service.OnlineUserService;
import com.storeroom.utils.EncryptUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth/online")
@Api(tags = "系统:在线用户管理")
public class OnlineController {
private final OnlineUserService onlineUserService;
@ApiOperation("查询在线用户")
@GetMapping
//@PreAuthorize("@el.check()")
public ResponseEntity<Object> query(String filter, Pageable pageable){
return new ResponseEntity<>(onlineUserService.getAll(filter, pageable), HttpStatus.OK);
}
@ApiOperation("导出数据")
@GetMapping(value = "/download")
//@PreAuthorize("@el.check()")
public void download(HttpServletResponse response, String filter) throws IOException {
onlineUserService.download(onlineUserService.getAll(filter), response);
}
@ApiOperation("踢出用户")
@DeleteMapping
//@PreAuthorize("@el.check()")
public ResponseEntity<Object> delete(@RequestBody Set<String> keys) throws Exception {
for (String key : keys) {
// 解密Key
key = EncryptUtils.desDecrypt(key);
onlineUserService.kickOut(key);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

14
system/src/main/java/com/storeroom/modules/system/domain/User.java

@ -84,23 +84,21 @@ public class User extends BaseEntity implements Serializable {
@ApiModelProperty(value = "是否为admin账号", hidden = true)
private Boolean isAdmin = false;
@Column(name = "pwd_reset_time")
@ApiModelProperty(value = "最后修改密码的时间", hidden = true)
private Date pwdResetTime;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) &&
Objects.equals(username, user.username);
return Objects.equals(id, user.id) && Objects.equals(roles, user.roles) && Objects.equals(jobs, user.jobs) && Objects.equals(dept, user.dept) && Objects.equals(username, user.username) && Objects.equals(nickName, user.nickName) && Objects.equals(email, user.email) && Objects.equals(phone, user.phone) && Objects.equals(gender, user.gender) && Objects.equals(avatarName, user.avatarName) && Objects.equals(avatarPath, user.avatarPath) && Objects.equals(password, user.password) && Objects.equals(enabled, user.enabled) && Objects.equals(isAdmin, user.isAdmin) && Objects.equals(pwdResetTime, user.pwdResetTime);
}
@Override
public int hashCode() {
return Objects.hash(id, username);

117
system/src/main/resources/application-prod.yml

@ -0,0 +1,117 @@
#配置数据源
spring:
datasource:
druid:
db-type: com.alibaba.druid.pool.DruidDataSource
driverClassName: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
url: jdbc:log4jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:yxkadmin}?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
username: ${DB_USER:root}
password: ${DB_PWD:ftzn83560792}
# 初始连接数
initial-size: 5
# 最小连接数
min-idle: 10
# 最大连接数
max-active: 20
# 获取连接超时时间
max-wait: 5000
# 连接有效性检测时间
time-between-eviction-runs-millis: 60000
# 连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
# 连接在池中最大生存的时间
max-evictable-idle-time-millis: 900000
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 检测连接是否有效
validation-query: select 1
# 配置监控统计
webStatFilter:
enabled: true
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: false
login-username: admin
login-password: 123456
filter:
stat:
enabled: true
# 记录慢SQL
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
# 登录相关配置
login:
# 登录缓存
cache-enable: true
# 是否限制单用户登录
single-login: false
# 验证码
login-code:
# 验证码类型配置 查看 LoginProperties 类
code-type: arithmetic
# 登录图形验证码有效时间/分钟
expiration: 2
# 验证码高度
width: 111
# 验证码宽度
heigth: 36
# 内容长度
length: 2
# 字体名称,为空则使用默认字体,如遇到线上乱码,设置其他字体即可
font-name:
# 字体大小
font-size: 25
#jwt
jwt:
header: Authorization
# 令牌前缀
token-start-with: Bearer
# 必须使用最少88位的Base64对该令牌进行编码
base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
# 令牌过期时间 此处单位/毫秒 ,默认2小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
token-validity-in-seconds: 7200000
# 在线用户key
online-key: online-token-
# 验证码
code-key: code-key-
# token 续期检查时间范围(默认30分钟,单位默认毫秒),在token即将过期的一段时间内用户操作了,则给用户的token续期
detect: 1800000
# 续期时间范围,默认 1小时,这里单位毫秒
renew: 3600000
# IP 本地解析
ip:
local-parsing: false
#是否允许生成代码,生产环境设置为false
generator:
enabled: false
#是否开启 swagger-ui
swagger:
enabled: true
# 文件存储路径
file:
mac:
path: ~/file/
avatar: ~/avatar/
linux:
path: /home/www/yxkadmin/file/
avatar: /home/www/yxkadmin/avatar/
windows:
path: D:\yxk-storetoom\file\
avatar: D:\yxk-storetoom\avatar\
# 文件大小 /M
maxSize: 100
avatarMaxSize: 5

3
system/src/main/resources/application.yml

@ -60,8 +60,7 @@ ip:
#密码加密传输 此处为私钥
rsa:
private_key: MIICWwIBAAKBgQDyhGeZ23kNfDysndtEMHQgX+3YcKPNny2YFtlfvYTpHmVClbaiZK+g2VetX/mhkiSQtyp5OwSy+dm6WVX7QOvUO7iPNs2Hl+33d6rzzMhROs46rCTBpukgdaT0N6SC8NWkBF9ogoDzhEGNKIV0AGHRPWmL75ghflUPD2VflmAnywIDAQABAoGAPL47MMdPD7ihfd7gD7lPLNi6Oy8jaBpJkkGO2rMeekFZvY7AOvabIt+tXUifvv9a10B5i/njWGzKQymjJpaBOp+JswuBKEq8ZFqeyQJWsrj1zJC+HaCImP1frvgdtWIu1tldVE44X7jIQSFAd26JJsPslMntj/iE7VBkzQRYhjkCQQD/3+zu4OJP5XdSq4Y+HzIJ7u2JS8u/SxRQYQh1E6/yydgyWE96cze4jZQQTvxBRsOrXvBlVJdT+QmcV0YnlyMtAkEA8qLOBevOgH5qq1nLRMYU6zb0k5OiAaiI+Oq1mZn0kjcisyFZopAs2FH07DVlh+tzbGdzt4Q3PjLSEqhVFU4x1wJARljoKRzG27R4w8/IjpfBCB4aTF78W1Fm+lpTGu0YuKVpvR2ubDn1HdY+2OT+UWwFK75kVVeWa03SqJsN/KB+2QJAKD9XO2Y1F91gZlH7xMmyuJ2iDkTD79B8AAY232bJSeO5bstOagfOWIenv/LPh69Hsyip6jwVScz2ScAAdQtGewJACeSyLWjPnQ4ZgWrJyz6dyPgGKprDzQ00UMlvH3F2KJ9Ll99yfBah++GQXPfzjhD2ouWAQ+GaiS/RdSNDX1RoFg==
private_key: MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
# 内存用户缓存配置
user-cache:

BIN
system/src/main/resources/ip2region/ip2region.db

48
system/src/main/resources/template/email/email.ftl

@ -0,0 +1,48 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
@page {
margin: 0;
}
</style>
</head>
<body style="margin: 0px;
padding: 0px;
font: 100% SimSun, Microsoft YaHei, Times New Roman, Verdana, Arial, Helvetica, sans-serif;
color: #000;">
<div style="height: auto;
width: 820px;
min-width: 820px;
margin: 0 auto;
margin-top: 20px;
border: 1px solid #eee;">
<div style="padding: 10px;padding-bottom: 0px;">
<p style="margin-bottom: 10px;padding-bottom: 0px;">尊敬的用户,您好:</p>
<p style="text-indent: 2em; margin-bottom: 10px;">您正在申请邮箱验证,您的验证码为:</p>
<p style="text-align: center;
font-family: Times New Roman;
font-size: 22px;
color: #C60024;
padding: 20px 0px;
margin-bottom: 10px;
font-weight: bold;
background: #ebebeb;">${code}</p>
<div class="foot-hr hr" style="margin: 0 auto;
z-index: 111;
width: 800px;
margin-top: 30px;
border-top: 1px solid #DA251D;">
</div>
<div style="text-align: center;
font-size: 12px;
padding: 20px 0px;
font-family: Microsoft YaHei;">
Copyright &copy;${.now?string("yyyy")} <a hover="color: #DA251D;" style="color: #999;" href="" target="_blank">yxk-ADMIN</a> 阅行客后台管理系统.
</div>
</div>
</div>
</body>
</html>

69
system/src/main/resources/template/email/taskAlarm.ftl

@ -0,0 +1,69 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
@page {
margin: 0;
}
</style>
</head>
<body style="margin: 0px;
padding: 0px;
font: 100% SimSun, Microsoft YaHei, Times New Roman, Verdana, Arial, Helvetica, sans-serif;
color: #000;">
<div style="height: auto;
margin: 0 auto;
margin-top: 20px;
padding: 20px;
border: 1px solid #eee;">
<div>
<p style="margin-bottom: 10px;">任务信息:</p>
<table style="border-collapse: collapse;">
<tr>
<th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">任务名称</th>
<th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">Bean名称</th>
<th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">执行方法</th>
<th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">参数内容</th>
<th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">Cron表达式</th>
<th style="padding: .65em;background: #666;border: 1px solid #777;color: #fff;">描述内容</th>
</tr>
<tr>
<td style="padding: .65em;border: 1px solid#777;">${task.jobName}</td>
<td style="padding: .65em;border: 1px solid#777;">${task.beanName}</td>
<td style="padding: .65em;border: 1px solid#777;">${task.methodName}</td>
<td style="padding: .65em;border: 1px solid#777;">${(task.params)!""}</td>
<td style="padding: .65em;border: 1px solid#777;">${task.cronExpression}</td>
<td style="padding: .65em;border: 1px solid#777;">${(task.description)!""}</td>
</tr>
</table>
</div>
<div>
<p style="margin-bottom: 10px;">异常信息:</p>
<pre style="position: relative;
padding: 15px;
line-height: 20px;
border-left: 5px solid #ddd;
color: #333;
font-family: Courier New, serif;
font-size: 12px">
${msg}
</pre>
</div>
<div class="foot-hr hr" style="margin: 0 auto;
z-index: 111;
width: 800px;
margin-top: 30px;
border-top: 1px solid #DA251D;">
</div>
<div style="text-align: center;
font-size: 12px;
padding: 20px 0px;
font-family: Microsoft YaHei;">
Copyright &copy;${.now?string("yyyy")} <a hover="color: #DA251D;" style="color: #999;" href="#" target="_blank">YXK-ADMIN</a> 阅行客后台管理系统.
</div>
</div>
</div>
</body>
</html>

73
system/src/main/resources/template/generator/admin/Controller.ftl

@ -0,0 +1,73 @@
package ${package}.rest;
import com.yxkadmin.annotation.Log;
import ${package}.domain.${className};
import ${package}.service.${className}Service;
import ${package}.service.dto.${className}QueryCriteria;
import org.springframework.data.domain.Pageable;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import io.swagger.annotations.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
/**
* @website https://yxk-admin.vip
* @author ${author}
* @date ${date}
**/
@RestController
@RequiredArgsConstructor
@Api(tags = "${apiAlias}管理")
@RequestMapping("/api/${changeClassName}")
public class ${className}Controller {
private final ${className}Service ${changeClassName}Service;
@Log("导出数据")
@ApiOperation("导出数据")
@GetMapping(value = "/download")
@PreAuthorize("@el.check('${changeClassName}:list')")
public void download(HttpServletResponse response, ${className}QueryCriteria criteria) throws IOException {
${changeClassName}Service.download(${changeClassName}Service.queryAll(criteria), response);
}
@GetMapping
@Log("查询${apiAlias}")
@ApiOperation("查询${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:list')")
public ResponseEntity<Object> query(${className}QueryCriteria criteria, Pageable pageable){
return new ResponseEntity<>(${changeClassName}Service.queryAll(criteria,pageable),HttpStatus.OK);
}
@PostMapping
@Log("新增${apiAlias}")
@ApiOperation("新增${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:add')")
public ResponseEntity<Object> create(@Validated @RequestBody ${className} resources){
return new ResponseEntity<>(${changeClassName}Service.create(resources),HttpStatus.CREATED);
}
@PutMapping
@Log("修改${apiAlias}")
@ApiOperation("修改${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:edit')")
public ResponseEntity<Object> update(@Validated @RequestBody ${className} resources){
${changeClassName}Service.update(resources);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@Log("删除${apiAlias}")
@ApiOperation("删除${apiAlias}")
@PreAuthorize("@el.check('${changeClassName}:del')")
@DeleteMapping
public ResponseEntity<Object> delete(@RequestBody ${pkColumnType}[] ids) {
${changeClassName}Service.deleteAll(ids);
return new ResponseEntity<>(HttpStatus.OK);
}
}

40
system/src/main/resources/template/generator/admin/Dto.ftl

@ -0,0 +1,40 @@
package ${package}.service.dto;
import lombok.Data;
<#if hasTimestamp>
import java.sql.Timestamp;
</#if>
<#if hasBigDecimal>
import java.math.BigDecimal;
</#if>
import java.io.Serializable;
<#if !auto && pkColumnType = 'Long'>
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
</#if>
/**
* @website https://yxk-admin.vip
* @description /
* @author ${author}
* @date ${date}
**/
@Data
public class ${className}Dto implements Serializable {
<#if columns??>
<#list columns as column>
<#if column.remark != ''>
/** ${column.remark} */
</#if>
<#if column.columnKey = 'PRI'>
<#if !auto && pkColumnType = 'Long'>
/** 防止精度丢失 */
@JsonSerialize(using= ToStringSerializer.class)
</#if>
</#if>
private ${column.columnType} ${column.changeColumnName};
</#list>
</#if>
}

71
system/src/main/resources/template/generator/admin/Entity.ftl

@ -0,0 +1,71 @@
package ${package}.domain;
import lombok.Data;
import cn.hutool.core.bean.BeanUtil;
import io.swagger.annotations.ApiModelProperty;
import cn.hutool.core.bean.copier.CopyOptions;
import javax.persistence.*;
<#if isNotNullColumns??>
import javax.validation.constraints.*;
</#if>
<#if hasDateAnnotation>
import javax.persistence.Entity;
import javax.persistence.Table;
import org.hibernate.annotations.*;
</#if>
<#if hasTimestamp>
import java.sql.Timestamp;
</#if>
<#if hasBigDecimal>
import java.math.BigDecimal;
</#if>
import java.io.Serializable;
/**
* @website https://yxk-admin
* @description /
* @author ${author}
* @date ${date}
**/
@Entity
@Data
@Table(name="${tableName}")
public class ${className} implements Serializable {
<#if columns??>
<#list columns as column>
<#if column.columnKey = 'PRI'>
@Id
<#if auto>
@GeneratedValue(strategy = GenerationType.IDENTITY)
</#if>
</#if>
@Column(name = "${column.columnName}"<#if column.columnKey = 'UNI'>,unique = true</#if><#if column.istNotNull && column.columnKey != 'PRI'>,nullable = false</#if>)
<#if column.istNotNull && column.columnKey != 'PRI'>
<#if column.columnType = 'String'>
@NotBlank
<#else>
@NotNull
</#if>
</#if>
<#if (column.dateAnnotation)??>
<#if column.dateAnnotation = 'CreationTimestamp'>
@CreationTimestamp
<#else>
@UpdateTimestamp
</#if>
</#if>
<#if column.remark != ''>
@ApiModelProperty(value = "${column.remark}")
<#else>
@ApiModelProperty(value = "${column.changeColumnName}")
</#if>
private ${column.columnType} ${column.changeColumnName};
</#list>
</#if>
public void copy(${className} source){
BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
}
}

18
system/src/main/resources/template/generator/admin/Mapper.ftl

@ -0,0 +1,18 @@
package ${package}.service.mapstruct;
import com.yxkadmin.base.BaseMapper;
import ${package}.domain.${className};
import ${package}.service.dto.${className}Dto;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
/**
* @website https://yxk-admin.vip
* @author ${author}
* @date ${date}
**/
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface ${className}Mapper extends BaseMapper<${className}Dto, ${className}> {
}

67
system/src/main/resources/template/generator/admin/QueryCriteria.ftl

@ -0,0 +1,67 @@
package ${package}.service.dto;
import lombok.Data;
<#if queryHasTimestamp>
import java.sql.Timestamp;
</#if>
<#if queryHasBigDecimal>
import java.math.BigDecimal;
</#if>
<#if betweens??>
import java.util.List;
</#if>
<#if queryColumns??>
import me.zhengjie.annotation.Query;
</#if>
/**
* @website https://el-admin.vip
* @author ${author}
* @date ${date}
**/
@Data
public class ${className}QueryCriteria{
<#if queryColumns??>
<#list queryColumns as column>
<#if column.queryType = '='>
/** 精确 */
@Query
private ${column.columnType} ${column.changeColumnName};
</#if>
<#if column.queryType = 'Like'>
/** 模糊 */
@Query(type = Query.Type.INNER_LIKE)
private ${column.columnType} ${column.changeColumnName};
</#if>
<#if column.queryType = '!='>
/** 不等于 */
@Query(type = Query.Type.NOT_EQUAL)
private ${column.columnType} ${column.changeColumnName};
</#if>
<#if column.queryType = 'NotNull'>
/** 不为空 */
@Query(type = Query.Type.NOT_NULL)
private ${column.columnType} ${column.changeColumnName};
</#if>
<#if column.queryType = '>='>
/** 大于等于 */
@Query(type = Query.Type.GREATER_THAN)
private ${column.columnType} ${column.changeColumnName};
</#if>
<#if column.queryType = '<='>
/** 小于等于 */
@Query(type = Query.Type.LESS_THAN)
private ${column.columnType} ${column.changeColumnName};
</#if>
</#list>
</#if>
<#if betweens??>
<#list betweens as column>
/** BETWEEN */
@Query(type = Query.Type.BETWEEN)
private List<${column.columnType}> ${column.changeColumnName};
</#list>
</#if>
}

26
system/src/main/resources/template/generator/admin/Repository.ftl

@ -0,0 +1,26 @@
package ${package}.repository;
import ${package}.domain.${className};
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* @website https://yxk-admin.vip
* @author ${author}
* @date ${date}
**/
public interface ${className}Repository extends JpaRepository<${className}, ${pkColumnType}>, JpaSpecificationExecutor<${className}> {
<#if columns??>
<#list columns as column>
<#if column.columnKey = 'UNI'>
/**
* 根据 ${column.capitalColumnName} 查询
* @param ${column.columnName} /
* @return /
*/
${className} findBy${column.capitalColumnName}(${column.columnType} ${column.columnName});
</#if>
</#list>
</#if>
}

69
system/src/main/resources/template/generator/admin/Service.ftl

@ -0,0 +1,69 @@
package ${package}.service;
import ${package}.domain.${className};
import ${package}.service.dto.${className}Dto;
import ${package}.service.dto.${className}QueryCriteria;
import org.springframework.data.domain.Pageable;
import java.util.Map;
import java.util.List;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
/**
* @website https://yxk-admin.vip
* @description 服务接口
* @author ${author}
* @date ${date}
**/
public interface ${className}Service {
/**
* 查询数据分页
* @param criteria 条件
* @param pageable 分页参数
* @return Map<String,Object>
*/
Map<String,Object> queryAll(${className}QueryCriteria criteria, Pageable pageable);
/**
* 查询所有数据不分页
* @param criteria 条件参数
* @return List<${className}Dto>
*/
List<${className}Dto> queryAll(${className}QueryCriteria criteria);
/**
* 根据ID查询
* @param ${pkChangeColName} ID
* @return ${className}Dto
*/
${className}Dto findById(${pkColumnType} ${pkChangeColName});
/**
* 创建
* @param resources /
* @return ${className}Dto
*/
${className}Dto create(${className} resources);
/**
* 编辑
* @param resources /
*/
void update(${className} resources);
/**
* 多选删除
* @param ids /
*/
void deleteAll(${pkColumnType}[] ids);
/**
* 导出数据
* @param all 待导出的数据
* @param response /
* @throws IOException /
*/
void download(List<${className}Dto> all, HttpServletResponse response) throws IOException;
}

143
system/src/main/resources/template/generator/admin/ServiceImpl.ftl

@ -0,0 +1,143 @@
package ${package}.service.impl;
import ${package}.domain.${className};
<#if columns??>
<#list columns as column>
<#if column.columnKey = 'UNI'>
<#if column_index = 1>
import me.zhengjie.exception.EntityExistException;
</#if>
</#if>
</#list>
</#if>
import com.yxkadmin.utils.ValidationUtil;
import com.yxkadmin.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import ${package}.repository.${className}Repository;
import ${package}.service.${className}Service;
import ${package}.service.dto.${className}Dto;
import ${package}.service.dto.${className}QueryCriteria;
import ${package}.service.mapstruct.${className}Mapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
<#if !auto && pkColumnType = 'Long'>
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
</#if>
<#if !auto && pkColumnType = 'String'>
import cn.hutool.core.util.IdUtil;
</#if>
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import com.yxkadmin.utils.PageUtil;
import com.yxkadmin.utils.QueryHelp;
import java.util.List;
import java.util.Map;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.LinkedHashMap;
/**
* @website https://yxk-admin.vip
* @description 服务实现
* @author ${author}
* @date ${date}
**/
@Service
@RequiredArgsConstructor
public class ${className}ServiceImpl implements ${className}Service {
private final ${className}Repository ${changeClassName}Repository;
private final ${className}Mapper ${changeClassName}Mapper;
@Override
public Map<String,Object> queryAll(${className}QueryCriteria criteria, Pageable pageable){
Page<${className}> page = ${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
return PageUtil.toPage(page.map(${changeClassName}Mapper::toDto));
}
@Override
public List<${className}Dto> queryAll(${className}QueryCriteria criteria){
return ${changeClassName}Mapper.toDto(${changeClassName}Repository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
}
@Override
@Transactional
public ${className}Dto findById(${pkColumnType} ${pkChangeColName}) {
${className} ${changeClassName} = ${changeClassName}Repository.findById(${pkChangeColName}).orElseGet(${className}::new);
ValidationUtil.isNull(${changeClassName}.get${pkCapitalColName}(),"${className}","${pkChangeColName}",${pkChangeColName});
return ${changeClassName}Mapper.toDto(${changeClassName});
}
@Override
@Transactional(rollbackFor = Exception.class)
public ${className}Dto create(${className} resources) {
<#if !auto && pkColumnType = 'Long'>
Snowflake snowflake = IdUtil.createSnowflake(1, 1);
resources.set${pkCapitalColName}(snowflake.nextId());
</#if>
<#if !auto && pkColumnType = 'String'>
resources.set${pkCapitalColName}(IdUtil.simpleUUID());
</#if>
<#if columns??>
<#list columns as column>
<#if column.columnKey = 'UNI'>
if(${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}()) != null){
throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}());
}
</#if>
</#list>
</#if>
return ${changeClassName}Mapper.toDto(${changeClassName}Repository.save(resources));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(${className} resources) {
${className} ${changeClassName} = ${changeClassName}Repository.findById(resources.get${pkCapitalColName}()).orElseGet(${className}::new);
ValidationUtil.isNull( ${changeClassName}.get${pkCapitalColName}(),"${className}","id",resources.get${pkCapitalColName}());
<#if columns??>
<#list columns as column>
<#if column.columnKey = 'UNI'>
<#if column_index = 1>
${className} ${changeClassName}1 = null;
</#if>
${changeClassName}1 = ${changeClassName}Repository.findBy${column.capitalColumnName}(resources.get${column.capitalColumnName}());
if(${changeClassName}1 != null && !${changeClassName}1.get${pkCapitalColName}().equals(${changeClassName}.get${pkCapitalColName}())){
throw new EntityExistException(${className}.class,"${column.columnName}",resources.get${column.capitalColumnName}());
}
</#if>
</#list>
</#if>
${changeClassName}.copy(resources);
${changeClassName}Repository.save(${changeClassName});
}
@Override
public void deleteAll(${pkColumnType}[] ids) {
for (${pkColumnType} ${pkChangeColName} : ids) {
${changeClassName}Repository.deleteById(${pkChangeColName});
}
}
@Override
public void download(List<${className}Dto> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();
for (${className}Dto ${changeClassName} : all) {
Map<String,Object> map = new LinkedHashMap<>();
<#list columns as column>
<#if column.columnKey != 'PRI'>
<#if column.remark != ''>
map.put("${column.remark}", ${changeClassName}.get${column.capitalColumnName}());
<#else>
map.put(" ${column.changeColumnName}", ${changeClassName}.get${column.capitalColumnName}());
</#if>
</#if>
</#list>
list.add(map);
}
FileUtil.downloadExcel(list, response);
}
}

27
system/src/main/resources/template/generator/front/api.ftl

@ -0,0 +1,27 @@
import request from '@/utils/request'
export function add(data) {
return request({
url: 'api/${changeClassName}',
method: 'post',
data
})
}
export function del(ids) {
return request({
url: 'api/${changeClassName}/',
method: 'delete',
data: ids
})
}
export function edit(data) {
return request({
url: 'api/${changeClassName}',
method: 'put',
data
})
}
export default { add, edit, del }

175
system/src/main/resources/template/generator/front/index.ftl

@ -0,0 +1,175 @@
<#--noinspection ALL-->
<template>
<div class="app-container">
<!--工具栏-->
<div class="head-container">
<#if hasQuery>
<div v-if="crud.props.searchToggle">
<!-- 搜索 -->
<#if queryColumns??>
<#list queryColumns as column>
<#if column.queryType != 'BetWeen'>
<label class="el-form-item-label"><#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if></label>
<el-input v-model="query.${column.changeColumnName}" clearable placeholder="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>" style="width: 185px;" class="filter-item" @keyup.enter.native="crud.toQuery" />
</#if>
</#list>
</#if>
<#if betweens??>
<#list betweens as column>
<#if column.queryType = 'BetWeen'>
<date-range-picker
v-model="query.${column.changeColumnName}"
start-placeholder="${column.changeColumnName}Start"
end-placeholder="${column.changeColumnName}Start"
class="date-item"
/>
</#if>
</#list>
</#if>
<rrOperation :crud="crud" />
</div>
</#if>
<!--如果想在工具栏加入更多按钮,可以使用插槽方式, slot = 'left' or 'right'-->
<crudOperation :permission="permission" />
<!--表单组件-->
<el-dialog :close-on-click-modal="false" :before-close="crud.cancelCU" :visible.sync="crud.status.cu > 0" :title="crud.status.title" width="500px">
<el-form ref="form" :model="form" <#if isNotNullColumns??>:rules="rules"</#if> size="small" label-width="80px">
<#if columns??>
<#list columns as column>
<#if column.formShow>
<el-form-item label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>"<#if column.istNotNull> prop="${column.changeColumnName}"</#if>>
<#if column.formType = 'Input'>
<el-input v-model="form.${column.changeColumnName}" style="width: 370px;" />
<#elseif column.formType = 'Textarea'>
<el-input v-model="form.${column.changeColumnName}" :rows="3" type="textarea" style="width: 370px;" />
<#elseif column.formType = 'Radio'>
<#if (column.dictName)?? && (column.dictName)!="">
<el-radio v-model="form.${column.changeColumnName}" v-for="item in dict.${column.dictName}" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
<#else>
未设置字典,请手动设置 Radio
</#if>
<#elseif column.formType = 'Select'>
<#if (column.dictName)?? && (column.dictName)!="">
<el-select v-model="form.${column.changeColumnName}" filterable placeholder="请选择">
<el-option
v-for="item in dict.${column.dictName}"
:key="item.id"
:label="item.label"
:value="item.value" />
</el-select>
<#else>
未设置字典,请手动设置 Select
</#if>
<#else>
<el-date-picker v-model="form.${column.changeColumnName}" type="datetime" style="width: 370px;" />
</#if>
</el-form-item>
</#if>
</#list>
</#if>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="crud.cancelCU">取消</el-button>
<el-button :loading="crud.cu === 2" type="primary" @click="crud.submitCU">确认</el-button>
</div>
</el-dialog>
<!--表格渲染-->
<el-table ref="table" v-loading="crud.loading" :data="crud.data" size="small" style="width: 100%;" @selection-change="crud.selectionChangeHandler">
<el-table-column type="selection" width="55" />
<#if columns??>
<#list columns as column>
<#if column.columnShow>
<#if (column.dictName)?? && (column.dictName)!="">
<el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>">
<template slot-scope="scope">
{{ dict.label.${column.dictName}[scope.row.${column.changeColumnName}] }}
</template>
</el-table-column>
<#elseif column.columnType != 'Timestamp'>
<el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>" />
<#else>
<el-table-column prop="${column.changeColumnName}" label="<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.${column.changeColumnName}) }}</span>
</template>
</el-table-column>
</#if>
</#if>
</#list>
</#if>
<el-table-column v-permission="['admin','${changeClassName}:edit','${changeClassName}:del']" label="操作" width="150px" align="center">
<template slot-scope="scope">
<udOperation
:data="scope.row"
:permission="permission"
/>
</template>
</el-table-column>
</el-table>
<!--分页组件-->
<pagination />
</div>
</div>
</template>
<script>
import crud${className} from '@/api/${changeClassName}'
import CRUD, { presenter, header, form, crud } from '@crud/crud'
import rrOperation from '@crud/RR.operation'
import crudOperation from '@crud/CRUD.operation'
import udOperation from '@crud/UD.operation'
import pagination from '@crud/Pagination'
const defaultForm = { <#if columns??><#list columns as column>${column.changeColumnName}: null<#if column_has_next>, </#if></#list></#if> }
export default {
name: '${className}',
components: { pagination, crudOperation, rrOperation, udOperation },
mixins: [presenter(), header(), form(defaultForm), crud()],
<#if hasDict>
dicts: [<#if hasDict??><#list dicts as dict>'${dict}'<#if dict_has_next>, </#if></#list></#if>],
</#if>
cruds() {
return CRUD({ title: '${apiAlias}', url: 'api/${changeClassName}', idField: '${pkChangeColName}', sort: '${pkChangeColName},desc', crudMethod: { ...crud${className} }})
},
data() {
return {
permission: {
add: ['admin', '${changeClassName}:add'],
edit: ['admin', '${changeClassName}:edit'],
del: ['admin', '${changeClassName}:del']
},
rules: {
<#if isNotNullColumns??>
<#list isNotNullColumns as column>
<#if column.istNotNull>
${column.changeColumnName}: [
{ required: true, message: '<#if column.remark != ''>${column.remark}</#if>不能为空', trigger: 'blur' }
]<#if column_has_next>,</#if>
</#if>
</#list>
</#if>
}<#if hasQuery>,
queryTypeOptions: [
<#if queryColumns??>
<#list queryColumns as column>
<#if column.queryType != 'BetWeen'>
{ key: '${column.changeColumnName}', display_name: '<#if column.remark != ''>${column.remark}<#else>${column.changeColumnName}</#if>' }<#if column_has_next>,</#if>
</#if>
</#list>
</#if>
]
</#if>
}
},
methods: {
// 钩子:在获取表格数据之前执行,false 则代表不获取数据
[CRUD.HOOK.beforeRefresh]() {
return true
}
}
}
</script>
<style scoped>
</style>
Loading…
Cancel
Save