21 changed files with 1312 additions and 12 deletions
-
198common/src/main/java/com/storeroom/config/RedisConfig.java
-
109common/src/main/java/com/storeroom/config/SwaggerConfig.java
-
2common/src/main/java/com/storeroom/utils/RsaUtils.java
-
3system/src/main/java/com/storeroom/modules/security/controller/AuthorizationController.java
-
52system/src/main/java/com/storeroom/modules/security/controller/OnlineController.java
-
14system/src/main/java/com/storeroom/modules/system/domain/User.java
-
117system/src/main/resources/application-prod.yml
-
3system/src/main/resources/application.yml
-
BINsystem/src/main/resources/ip2region/ip2region.db
-
48system/src/main/resources/template/email/email.ftl
-
69system/src/main/resources/template/email/taskAlarm.ftl
-
73system/src/main/resources/template/generator/admin/Controller.ftl
-
40system/src/main/resources/template/generator/admin/Dto.ftl
-
71system/src/main/resources/template/generator/admin/Entity.ftl
-
18system/src/main/resources/template/generator/admin/Mapper.ftl
-
67system/src/main/resources/template/generator/admin/QueryCriteria.ftl
-
26system/src/main/resources/template/generator/admin/Repository.ftl
-
69system/src/main/resources/template/generator/admin/Service.ftl
-
143system/src/main/resources/template/generator/admin/ServiceImpl.ftl
-
27system/src/main/resources/template/generator/front/api.ftl
-
175system/src/main/resources/template/generator/front/index.ftl
@ -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); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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 |
@ -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 ©${.now?string("yyyy")} <a hover="color: #DA251D;" style="color: #999;" href="" target="_blank">yxk-ADMIN</a> 阅行客后台管理系统. |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -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 ©${.now?string("yyyy")} <a hover="color: #DA251D;" style="color: #999;" href="#" target="_blank">YXK-ADMIN</a> 阅行客后台管理系统. |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -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); |
|||
} |
|||
} |
@ -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> |
|||
} |
@ -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)); |
|||
} |
|||
} |
@ -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}> { |
|||
|
|||
} |
@ -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> |
|||
} |
@ -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> |
|||
} |
@ -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; |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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 } |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue