Commit 805c4053c1e14cb2753d59ab6f8911b98f264073

Authored by zhangdaiscott
1 parent 789277c4

JeecgBoot 3.1.0 版本发布,基于代码生成器的企业级低代码平台

Showing 237 changed files with 11319 additions and 849 deletions
jeecg-boot/README.md
1 1 Jeecg-Boot 低代码开发平台
2 2 ===============
3 3  
4   -当前最新版本: 3.0(发布日期:2021-11-01)
  4 +当前最新版本: 3.1.0(发布日期:20220301)
5 5  
6 6  
7 7 ## 后端技术架构
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-base-api</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/common/bpm/api/IBpmBaseExtAPI.java deleted
1   -package org.jeecg.common.bpm.api;
2   -
3   -import org.jeecg.common.api.vo.Result;
4   -import org.jeecg.common.constant.ServiceNameConstants;
5   -import org.jeecg.common.online.api.factory.OnlineBaseExtAPIFallbackFactory;
6   -import org.springframework.cloud.openfeign.FeignClient;
7   -import org.springframework.stereotype.Component;
8   -import org.springframework.web.bind.annotation.PostMapping;
9   -import org.springframework.web.bind.annotation.RequestParam;
10   -
11   -/**
12   - * 流程接口
13   - *
14   - * @author scott
15   - */
16   -@Component
17   -@FeignClient(contextId = "bpmBaseRemoteApi", value = ServiceNameConstants.SYSTEM_SERVICE,
18   - fallbackFactory = OnlineBaseExtAPIFallbackFactory.class)
19   -public interface IBpmBaseExtAPI {
20   - /**
21   - * 23. 流程提交接口(online,自定义开发)
22   - *
23   - * @param flowCode
24   - * 流程业务关联 例如:joa_leave_01
25   - * @param id
26   - * 表单业务数据data id
27   - * @param formUrl
28   - * 流程审批时附件页面默认展示的PC端表单组件(地址)
29   - * @param formUrlMobile
30   - * 流程审批时附件页面默认展示的移动端表单组件(地址)
31   - * @param username
32   - * 流程发起人账号
33   - * @param jsonData
34   - * Json串,额外扩展的流程变量值 【非必填】
35   - * @return
36   - * @throws Exception
37   - */
38   - @PostMapping(value = "/act/process/extActProcess/startMutilProcess")
39   - Result<String> startMutilProcess(@RequestParam("flowCode") String flowCode, @RequestParam("id") String id,
40   - @RequestParam("formUrl") String formUrl, @RequestParam("formUrlMobile") String formUrlMobile,
41   - @RequestParam("username") String username, @RequestParam("jsonData") String jsonData) throws Exception;
42   -
43   - /**
44   - * 24. 流程提交接口(自定义表单设计器)
45   - *
46   - * @param flowCode
47   - * 流程业务关联 例如:joa_leave_01
48   - * @param id
49   - * 表单业务数据data id
50   - * @param formUrl
51   - * 流程审批时附件页面默认展示的PC端表单组件(地址)
52   - * @param formUrlMobile
53   - * 流程审批时附件页面默认展示的移动端表单组件(地址)
54   - * @param username
55   - * 流程发起人账号
56   - * @param jsonData
57   - * Json串,额外扩展的流程变量值 【非必填】
58   - * @return
59   - * @throws Exception
60   - */
61   - @PostMapping(value = "/act/process/extActProcess/startDesFormMutilProcess")
62   - Result<String> startDesFormMutilProcess(@RequestParam("flowCode") String flowCode, @RequestParam("id") String id,
63   - @RequestParam("formUrl") String formUrl, @RequestParam("formUrlMobile") String formUrlMobile,
64   - @RequestParam("username") String username, @RequestParam("jsonData") String jsonData) throws Exception;
65   -
66   - /**
67   - * 25. 保存流程草稿箱接口(自定义开发表单、online表单)
68   - *
69   - * @param flowCode
70   - * 流程业务关联 例如:joa_leave_01
71   - * @param id
72   - * 表单业务数据data id
73   - * @param formUrl
74   - * 流程审批时附件页面默认展示的PC端表单组件(地址) 【非必填】
75   - * @param formUrlMobile
76   - * 流程审批时附件页面默认展示的移动端表单组件(地址) 【非必填】
77   - * @param username
78   - * 流程发起人账号
79   - * @param jsonData
80   - * Json串,额外扩展的流程变量值 【非必填】
81   - * @return
82   - * @throws Exception
83   - */
84   - @PostMapping(value = "/act/process/extActProcess/saveMutilProcessDraft")
85   - Result<String> saveMutilProcessDraft(@RequestParam("flowCode") String flowCode, @RequestParam("id") String id,
86   - @RequestParam("formUrl") String formUrl, @RequestParam("formUrlMobile") String formUrlMobile,
87   - @RequestParam("username") String username, @RequestParam("jsonData") String jsonData) throws Exception;
88   -
89   -}
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/common/bpm/api/factory/BpmBaseExtAPIFallbackFactory.java deleted
1   -package org.jeecg.common.bpm.api.factory;
2   -
3   -import org.jeecg.common.bpm.api.IBpmBaseExtAPI;
4   -import org.jeecg.common.bpm.api.fallback.BpmBaseExtAPIFallback;
5   -import org.springframework.stereotype.Component;
6   -
7   -import feign.hystrix.FallbackFactory;
8   -
9   -@Component
10   -public class BpmBaseExtAPIFallbackFactory implements FallbackFactory<IBpmBaseExtAPI> {
11   -
12   - @Override
13   - public IBpmBaseExtAPI create(Throwable throwable) {
14   - BpmBaseExtAPIFallback fallback = new BpmBaseExtAPIFallback();
15   - fallback.setCause(throwable);
16   - return fallback;
17   - }
18   -}
19 0 \ No newline at end of file
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/common/bpm/api/fallback/BpmBaseExtAPIFallback.java deleted
1   -package org.jeecg.common.bpm.api.fallback;
2   -
3   -import java.util.List;
4   -import java.util.Map;
5   -
6   -import org.jeecg.common.api.vo.Result;
7   -import org.jeecg.common.bpm.api.IBpmBaseExtAPI;
8   -import org.jeecg.common.online.api.IOnlineBaseExtAPI;
9   -import org.jeecg.common.system.vo.DictModel;
10   -
11   -import com.alibaba.fastjson.JSONObject;
12   -
13   -import lombok.Setter;
14   -import lombok.extern.slf4j.Slf4j;
15   -
16   -/**
17   - * 进入fallback的方法 检查是否token未设置
18   - */
19   -@Slf4j
20   -public class BpmBaseExtAPIFallback implements IBpmBaseExtAPI {
21   -
22   - @Setter
23   - private Throwable cause;
24   -
25   - @Override
26   - public Result<String> startMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,
27   - String username, String jsonData) throws Exception {
28   - return null;
29   - }
30   -
31   - @Override
32   - public Result<String> startDesFormMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,
33   - String username, String jsonData) throws Exception {
34   - return null;
35   - }
36   -
37   - @Override
38   - public Result<String> saveMutilProcessDraft(String flowCode, String id, String formUrl, String formUrlMobile,
39   - String username, String jsonData) throws Exception {
40   - return null;
41   - }
42   -}
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-cloud-api/src/main/java/org/jeecg/common/online/api/IOnlineBaseExtAPI.java
... ... @@ -15,7 +15,8 @@ import java.util.Map;
15 15 * 【Online】Feign API接口
16 16 */
17 17 @Component
18   -@FeignClient(contextId = "onlineBaseRemoteApi", value = ServiceNameConstants.SYSTEM_ONLINE, fallbackFactory = OnlineBaseExtAPIFallbackFactory.class)
  18 +@FeignClient(contextId = "onlineBaseRemoteApi", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = OnlineBaseExtAPIFallbackFactory.class)
  19 +//@FeignClient(contextId = "onlineBaseRemoteApi", value = ServiceNameConstants.SYSTEM_ONLINE, fallbackFactory = OnlineBaseExtAPIFallbackFactory.class)
19 20 public interface IOnlineBaseExtAPI {
20 21  
21 22 /**
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-local-api/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-base-api</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/jeecg-system-local-api/src/main/java/org/jeecg/common/bpm/api/IBpmBaseExtAPI.java deleted
1   -package org.jeecg.common.bpm.api;
2   -
3   -import java.util.List;
4   -import java.util.Map;
5   -
6   -import org.jeecg.common.api.vo.Result;
7   -import org.jeecg.common.system.vo.DictModel;
8   -
9   -import com.alibaba.fastjson.JSONObject;
10   -
11   -/**
12   - * 流程接口
13   - *
14   - * @author scott
15   - */
16   -public interface IBpmBaseExtAPI {
17   - /**
18   - * 23. 流程提交接口(online,自定义开发)
19   - * @param flowCode 流程业务关联 例如:joa_leave_01
20   - * @param id 表单业务数据data id
21   - * @param formUrl 流程审批时附件页面默认展示的PC端表单组件(地址)
22   - * @param formUrlMobile 流程审批时附件页面默认展示的移动端表单组件(地址)
23   - * @param username 流程发起人账号
24   - * @param jsonData Json串,额外扩展的流程变量值 【非必填】
25   - * @return
26   - * @throws Exception
27   - */
28   - Result<String> startMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,String username, String jsonData) throws Exception;
29   -
30   - /**
31   - * 24. 流程提交接口(自定义表单设计器)
32   - * @param flowCode 流程业务关联 例如:joa_leave_01
33   - * @param id 表单业务数据data id
34   - * @param formUrl 流程审批时附件页面默认展示的PC端表单组件(地址)
35   - * @param formUrlMobile 流程审批时附件页面默认展示的移动端表单组件(地址)
36   - * @param username 流程发起人账号
37   - * @param jsonData Json串,额外扩展的流程变量值 【非必填】
38   - * @return
39   - * @throws Exception
40   - */
41   - Result<String> startDesFormMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,String username,String jsonData) throws Exception;
42   - /**
43   - * 25. 保存流程草稿箱接口(自定义开发表单、online表单)
44   - * @param flowCode 流程业务关联 例如:joa_leave_01
45   - * @param id 表单业务数据data id
46   - * @param formUrl 流程审批时附件页面默认展示的PC端表单组件(地址) 【非必填】
47   - * @param formUrlMobile 流程审批时附件页面默认展示的移动端表单组件(地址) 【非必填】
48   - * @param username 流程发起人账号
49   - * @param jsonData Json串,额外扩展的流程变量值 【非必填】
50   - * @return
51   - * @throws Exception
52   - */
53   - Result<String> saveMutilProcessDraft(String flowCode, String id, String formUrl, String formUrlMobile,String username,String jsonData) throws Exception;
54   -
55   -}
jeecg-boot/jeecg-boot-base/jeecg-boot-base-api/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-base</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/pom.xml
... ... @@ -4,7 +4,7 @@
4 4 <parent>
5 5 <groupId>org.jeecgframework.boot</groupId>
6 6 <artifactId>jeecg-boot-base</artifactId>
7   - <version>3.0</version>
  7 + <version>3.1.0</version>
8 8 </parent>
9 9 <modelVersion>4.0.0</modelVersion>
10 10  
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/api/vo/Result.java
... ... @@ -103,6 +103,7 @@ public class Result&lt;T&gt; implements Serializable {
103 103 return r;
104 104 }
105 105  
  106 + @Deprecated
106 107 public static<T> Result<T> OK(String msg) {
107 108 Result<T> r = new Result<T>();
108 109 r.setSuccess(true);
... ... @@ -157,6 +158,7 @@ public class Result&lt;T&gt; implements Serializable {
157 158 this.success = false;
158 159 return this;
159 160 }
  161 +
160 162 /**
161 163 * 无权限访问返回结果
162 164 */
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java
... ... @@ -88,7 +88,7 @@ public class AutoLogAspect {
88 88  
89 89  
90 90 //设置操作类型
91   - if (dto.getLogType() == CommonConstant.LOG_TYPE_2) {
  91 + if (CommonConstant.LOG_TYPE_2 == dto.getLogType()) {
92 92 dto.setOperateType(getOperateType(methodName, syslog.operateType()));
93 93 }
94 94  
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/DictAspect.java
... ... @@ -2,6 +2,7 @@ package org.jeecg.common.aspect;
2 2  
3 3 import com.alibaba.fastjson.JSON;
4 4 import com.alibaba.fastjson.JSONObject;
  5 +import com.alibaba.fastjson.parser.Feature;
5 6 import com.baomidou.mybatisplus.core.metadata.IPage;
6 7 import com.fasterxml.jackson.annotation.JsonFormat;
7 8 import com.fasterxml.jackson.core.JsonProcessingException;
... ... @@ -18,6 +19,7 @@ import org.jeecg.common.constant.CommonConstant;
18 19 import org.jeecg.common.system.vo.DictModel;
19 20 import org.jeecg.common.util.oConvertUtils;
20 21 import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.context.annotation.Lazy;
21 23 import org.springframework.data.redis.core.RedisTemplate;
22 24 import org.springframework.stereotype.Component;
23 25 import org.springframework.util.StringUtils;
... ... @@ -38,14 +40,14 @@ import java.util.stream.Collectors;
38 40 @Component
39 41 @Slf4j
40 42 public class DictAspect {
41   -
  43 + @Lazy
42 44 @Autowired
43 45 private CommonAPI commonAPI;
44 46 @Autowired
45 47 public RedisTemplate redisTemplate;
46 48  
47 49 // 定义切点Pointcut
48   - @Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..))")
  50 + @Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..)) || @annotation(org.jeecg.common.aspect.annotation.AutoDict)")
49 51 public void excudeService() {
50 52 }
51 53  
... ... @@ -103,7 +105,10 @@ public class DictAspect {
103 105 } catch (JsonProcessingException e) {
104 106 log.error("json解析失败"+e.getMessage(),e);
105 107 }
106   - JSONObject item = JSONObject.parseObject(json);
  108 + //update-begin--Author:scott -- Date:20211223 ----for:【issues/3303】restcontroller返回json数据后key顺序错乱 -----
  109 + JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
  110 + //update-end--Author:scott -- Date:20211223 ----for:【issues/3303】restcontroller返回json数据后key顺序错乱 -----
  111 +
107 112 //update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
108 113 //for (Field field : record.getClass().getDeclaredFields()) {
109 114 // 遍历所有字段,把字典Code取出来,放到 map 里
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/PermissionDataAspect.java
... ... @@ -8,6 +8,7 @@ import org.aspectj.lang.annotation.Pointcut;
8 8 import org.aspectj.lang.reflect.MethodSignature;
9 9 import org.jeecg.common.api.CommonAPI;
10 10 import org.jeecg.common.aspect.annotation.PermissionData;
  11 +import org.jeecg.common.constant.CommonConstant;
11 12 import org.jeecg.common.system.util.JeecgDataAutorUtils;
12 13 import org.jeecg.common.system.util.JwtUtil;
13 14 import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
... ... @@ -15,6 +16,7 @@ import org.jeecg.common.system.vo.SysUserCacheInfo;
15 16 import org.jeecg.common.util.SpringContextUtils;
16 17 import org.jeecg.common.util.oConvertUtils;
17 18 import org.springframework.beans.factory.annotation.Autowired;
  19 +import org.springframework.context.annotation.Lazy;
18 20 import org.springframework.stereotype.Component;
19 21  
20 22 import javax.servlet.http.HttpServletRequest;
... ... @@ -31,7 +33,7 @@ import java.util.List;
31 33 @Component
32 34 @Slf4j
33 35 public class PermissionDataAspect {
34   -
  36 + @Lazy
35 37 @Autowired
36 38 private CommonAPI commonAPI;
37 39  
... ... @@ -47,11 +49,21 @@ public class PermissionDataAspect {
47 49 Method method = signature.getMethod();
48 50 PermissionData pd = method.getAnnotation(PermissionData.class);
49 51 String component = pd.pageComponent();
50   -
51 52 String requestMethod = request.getMethod();
52 53 String requestPath = request.getRequestURI().substring(request.getContextPath().length());
53 54 requestPath = filterUrl(requestPath);
54   - log.debug("拦截请求 >> "+requestPath+";请求类型 >> "+requestMethod);
  55 + //update-begin-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
  56 + //先判断是否online报表请求
  57 + // TODO 参数顺序调整有隐患
  58 + if(requestPath.indexOf(UrlMatchEnum.CGREPORT_DATA.getMatch_url())>=0){
  59 + // 获取地址栏参数
  60 + String urlParamString = request.getParameter(CommonConstant.ONL_REP_URL_PARAM_STR);
  61 + if(oConvertUtils.isNotEmpty(urlParamString)){
  62 + requestPath+="?"+urlParamString;
  63 + }
  64 + }
  65 + //update-end-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
  66 + log.info("拦截请求 >> {} ; 请求类型 >> {} . ", requestPath, requestMethod);
55 67 String username = JwtUtil.getUserNameByToken(request);
56 68 //查询数据权限信息
57 69 //TODO 微服务情况下也得支持缓存机制
... ... @@ -86,6 +98,7 @@ public class PermissionDataAspect {
86 98 * @param request
87 99 * @return
88 100 */
  101 + @Deprecated
89 102 private String getJgAuthRequsetPath(HttpServletRequest request) {
90 103 String queryString = request.getQueryString();
91 104 String requestPath = request.getRequestURI();
... ... @@ -106,6 +119,7 @@ public class PermissionDataAspect {
106 119 return filterUrl(requestPath);
107 120 }
108 121  
  122 + @Deprecated
109 123 private boolean moHuContain(List<String> list,String key){
110 124 for(String str : list){
111 125 if(key.contains(str)){
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/UrlMatchEnum.java
... ... @@ -10,8 +10,8 @@ public enum UrlMatchEnum {
10 10 CGFORM_EXCEL_DATA("/online/cgform/api/exportXls/", "/online/cgformList/"),
11 11 CGFORM_TREE_DATA("/online/cgform/api/getTreeData/", "/online/cgformList/"),
12 12 CGREPORT_DATA("/online/cgreport/api/getColumnsAndData/", "/online/cgreport/"),
13   - CGREPORT_EXCEL_DATA("/online/cgreport/api/exportXls/", "/online/cgreport/");
14   -
  13 + CGREPORT_EXCEL_DATA("/online/cgreport/api/exportXls/", "/online/cgreport/"),
  14 + CGREPORT_EXCEL_DATA2("/online/cgreport/api/exportManySheetXls/", "/online/cgreport/");
15 15  
16 16 UrlMatchEnum(String url, String match_url) {
17 17 this.url = url;
... ... @@ -47,8 +47,10 @@ public enum UrlMatchEnum {
47 47 return null;
48 48 }
49 49  
50   -
51   -// public static void main(String[] args) {
  50 + public String getMatch_url() {
  51 + return match_url;
  52 + }
  53 + // public static void main(String[] args) {
52 54 // /**
53 55 // * 比如request真实请求URL: /online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a
54 56 // * 转换匹配路由URL后(对应配置的菜单路径):/online/cgformList/81fcf7d8922d45069b0d5ba983612d3a
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoDict.java 0 → 100644
  1 +package org.jeecg.common.aspect.annotation;
  2 +
  3 +import java.lang.annotation.*;
  4 +
  5 +/**
  6 + * 通过此注解声明的接口,自动实现字典翻译
  7 + *
  8 + * @Author scott
  9 + * @email jeecgos@163.com
  10 + * @Date 2022年01月05日
  11 + */
  12 +@Target(ElementType.METHOD)
  13 +@Retention(RetentionPolicy.RUNTIME)
  14 +@Documented
  15 +public @interface AutoDict {
  16 +
  17 + /**
  18 + * 暂时无用
  19 + * @return
  20 + */
  21 + String value() default "";
  22 +
  23 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/annotation/AutoLowApp.java 0 → 100644
  1 +package org.jeecg.common.aspect.annotation;
  2 +
  3 +import java.lang.annotation.*;
  4 +
  5 +import org.jeecg.common.constant.enums.LowAppAopEnum;
  6 +
  7 +/**
  8 + * 自动注入low_app_id
  9 + *
  10 + * @Author scott
  11 + * @email jeecgos@163.com
  12 + * @Date 2022年01月05日
  13 + */
  14 +@Target(ElementType.METHOD)
  15 +@Retention(RetentionPolicy.RUNTIME)
  16 +@Documented
  17 +public @interface AutoLowApp {
  18 +
  19 + /**
  20 + * 切面类型(add、delete、db_import等其他操作)
  21 + *
  22 + * @return
  23 + */
  24 + LowAppAopEnum action();
  25 +
  26 + /**
  27 + * 业务类型(cgform等)
  28 + *
  29 + * @return
  30 + */
  31 + String bizType();
  32 +
  33 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/CommonConstant.java
... ... @@ -77,7 +77,13 @@ public interface CommonConstant {
77 77 public static final String PREFIX_USER_TOKEN = "prefix_user_token_";
78 78 /** Token缓存时间:3600秒即一小时 */
79 79 public static final int TOKEN_EXPIRE_TIME = 3600;
80   -
  80 +
  81 + /** 登录二维码 */
  82 + public static final String LOGIN_QRCODE_PRE = "QRCODELOGIN:";
  83 + public static final String LOGIN_QRCODE = "LQ:";
  84 + /** 登录二维码token */
  85 + public static final String LOGIN_QRCODE_TOKEN = "LQT:";
  86 +
81 87  
82 88 /**
83 89 * 0:一级菜单
... ... @@ -91,7 +97,7 @@ public interface CommonConstant {
91 97 * 2:按钮权限
92 98 */
93 99 public static final Integer MENU_TYPE_2 = 2;
94   -
  100 +
95 101 /**通告对象类型(USER:指定用户,ALL:全体用户)*/
96 102 public static final String MSG_TYPE_UESR = "USER";
97 103 public static final String MSG_TYPE_ALL = "ALL";
... ... @@ -229,6 +235,9 @@ public interface CommonConstant {
229 235 public static final String SQL_INDEX_UNIQ_SYS_USER_WORK_NO = "uniq_sys_user_work_no";
230 236 /** sys_user 表 phone 唯一键索引 */
231 237 public static final String SQL_INDEX_UNIQ_SYS_USER_PHONE = "uniq_sys_user_phone";
  238 + /** 达梦数据库升提示。违反表[SYS_USER]唯一性约束 */
  239 + public static final String SQL_INDEX_UNIQ_SYS_USER = "唯一性约束";
  240 +
232 241 /** sys_user 表 email 唯一键索引 */
233 242 public static final String SQL_INDEX_UNIQ_SYS_USER_EMAIL = "uniq_sys_user_email";
234 243 /** sys_quartz_job 表 job_class_name 唯一键索引 */
... ... @@ -239,6 +248,8 @@ public interface CommonConstant {
239 248 public static final String SQL_INDEX_UNIQ_SYS_ROLE_CODE = "uniq_sys_role_role_code";
240 249 /** sys_depart 表 code 唯一键索引 */
241 250 public static final String SQL_INDEX_UNIQ_DEPART_ORG_CODE = "uniq_depart_org_code";
  251 + /** sys_category 表 code 唯一键索引 */
  252 + public static final String SQL_INDEX_UNIQ_CATEGORY_CODE = "idx_sc_code";
242 253 /**
243 254 * 在线聊天 是否为默认分组
244 255 */
... ... @@ -325,4 +336,7 @@ public interface CommonConstant {
325 336 /** 系统通告消息状态:2=已撤销 */
326 337 String ANNOUNCEMENT_SEND_STATUS_2 = "2";
327 338  
  339 + /**ONLINE 报表权限用 从request中获取地址栏后的参数*/
  340 + String ONL_REP_URL_PARAM_STR="onlRepUrlParamStr";
  341 +
328 342 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/ProvinceCityArea.java
... ... @@ -29,7 +29,7 @@ public class ProvinceCityArea {
29 29  
30 30 public String getCode(String text){
31 31 this.initAreaList();
32   - if(areaList!=null || areaList.size()>0){
  32 + if(areaList!=null && areaList.size()>0){
33 33 for(int i=areaList.size()-1;i>=0;i--){
34 34 if(text.indexOf(areaList.get(i).getText())>=0){
35 35 return areaList.get(i).getId();
... ... @@ -39,6 +39,73 @@ public class ProvinceCityArea {
39 39 return null;
40 40 }
41 41  
  42 + // update-begin-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省
  43 + /**
  44 + * 获取省市区code,精准匹配
  45 + * @param texts 文本数组,省,市,区
  46 + * @return 返回 省市区的code
  47 + */
  48 + public String[] getCode(String[] texts) {
  49 + if (texts == null || texts.length == 0) {
  50 + return null;
  51 + }
  52 + this.initAreaList();
  53 + if (areaList == null || areaList.size() == 0) {
  54 + return null;
  55 + }
  56 + String[] codes = new String[texts.length];
  57 + String code = null;
  58 + for (int i = 0; i < texts.length; i++) {
  59 + String text = texts[i];
  60 + Area area;
  61 + if (code == null) {
  62 + area = getAreaByText(text);
  63 + } else {
  64 + area = getAreaByPidAndText(code, text);
  65 + }
  66 + if (area != null) {
  67 + code = area.id;
  68 + codes[i] = code;
  69 + } else {
  70 + return null;
  71 + }
  72 + }
  73 + return codes;
  74 + }
  75 +
  76 + /**
  77 + * 根据text获取area
  78 + * @param text
  79 + * @return
  80 + */
  81 + public Area getAreaByText(String text) {
  82 + for (Area area : areaList) {
  83 + if (text.equals(area.getText())) {
  84 + return area;
  85 + }
  86 + }
  87 + return null;
  88 + }
  89 +
  90 + /**
  91 + * 通过pid获取 area 对象
  92 + * @param pCode 父级编码
  93 + * @param text
  94 + * @return
  95 + */
  96 + public Area getAreaByPidAndText(String pCode, String text) {
  97 + this.initAreaList();
  98 + if (this.areaList != null && this.areaList.size() > 0) {
  99 + for (Area area : this.areaList) {
  100 + if (area.getPid().equals(pCode) && area.getText().equals(text)) {
  101 + return area;
  102 + }
  103 + }
  104 + }
  105 + return null;
  106 + }
  107 + // update-end-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省
  108 +
42 109 public void getAreaByCode(String code,List<String> ls){
43 110 for(Area area: areaList){
44 111 if(area.getId().equals(code)){
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/LowAppAopEnum.java 0 → 100644
  1 +package org.jeecg.common.constant.enums;
  2 +
  3 +/**
  4 + * LowApp 切面注解枚举
  5 + * @date 2022-1-5
  6 + */
  7 +public enum LowAppAopEnum {
  8 +
  9 + /**
  10 + * 新增方法
  11 + */
  12 + ADD,
  13 + /**
  14 + * 删除方法(包含单个和批量删除)
  15 + */
  16 + DELETE,
  17 +
  18 + /**
  19 + * Online表单专用:数据库表转Online表单
  20 + */
  21 + CGFORM_DB_IMPORT
  22 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/RoleIndexConfigEnum.java
1 1 package org.jeecg.common.constant.enums;
2 2  
  3 +import org.jeecg.common.util.oConvertUtils;
  4 +
3 5 import java.util.List;
4 6  
5 7 /**
6 8 * 首页自定义
7 9 * 通过角色编码与首页组件路径配置
  10 + * 枚举的顺序有权限高低权重作用(也就是配置多个角色,在前面的角色首页,会优先生效)
8 11 */
9 12 public enum RoleIndexConfigEnum {
10   - /**
11   - * 管理员
12   - */
13   - ADMIN("admin1", "dashboard/Analysis2"),
14   - /**
15   - * 测试
16   - */
17   - TEST("test", "dashboard/Analysis"),
18   - /**
19   - * hr
20   - */
21   - HR("hr", "dashboard/Analysis1");
  13 +
  14 + ADMIN("admin", "dashboard/Analysis"),
  15 + //TEST("test", "dashboard/IndexChart"),
  16 + HR("hr", "dashboard/IndexBdc");
  17 + //DM("dm", "dashboard/IndexTask"),
22 18  
23 19 /**
24 20 * 角色编码
... ... @@ -44,7 +40,7 @@ public enum RoleIndexConfigEnum {
44 40 * @param roleCode 角色编码
45 41 * @return
46 42 */
47   - public static RoleIndexConfigEnum getEnumByCode(String roleCode) {
  43 + private static RoleIndexConfigEnum getEnumByCode(String roleCode) {
48 44 for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
49 45 if (e.roleCode.equals(roleCode)) {
50 46 return e;
... ... @@ -57,7 +53,7 @@ public enum RoleIndexConfigEnum {
57 53 * @param roleCode 角色编码
58 54 * @return
59 55 */
60   - public static String getIndexByCode(String roleCode) {
  56 + private static String getIndexByCode(String roleCode) {
61 57 for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
62 58 if (e.roleCode.equals(roleCode)) {
63 59 return e.componentUrl;
... ... @@ -67,11 +63,10 @@ public enum RoleIndexConfigEnum {
67 63 }
68 64  
69 65 public static String getIndexByRoles(List<String> roles) {
70   - for (String role : roles) {
71   - for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
72   - if (e.roleCode.equals(role)) {
73   - return e.componentUrl;
74   - }
  66 + String[] rolesArray = roles.toArray(new String[roles.size()]);
  67 + for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
  68 + if (oConvertUtils.isIn(e.roleCode,rolesArray)){
  69 + return e.componentUrl;
75 70 }
76 71 }
77 72 return null;
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/base/controller/JeecgController.java
... ... @@ -180,8 +180,15 @@ public class JeecgController&lt;T, S extends IService&lt;T&gt;&gt; {
180 180 //update-end-author:taoyan date:20190528 for:批量插入数据
181 181 return Result.ok("文件导入成功!数据行数:" + list.size());
182 182 } catch (Exception e) {
183   - log.error(e.getMessage(), e);
184   - return Result.error("文件导入失败:" + e.getMessage());
  183 + //update-begin-author:taoyan date:20211124 for: 导入数据重复增加提示
  184 + String msg = e.getMessage();
  185 + log.error(msg, e);
  186 + if(msg!=null && msg.indexOf("Duplicate entry")>=0){
  187 + return Result.error("文件导入失败:有重复数据!");
  188 + }else{
  189 + return Result.error("文件导入失败:" + e.getMessage());
  190 + }
  191 + //update-end-author:taoyan date:20211124 for: 导入数据重复增加提示
185 192 } finally {
186 193 try {
187 194 file.getInputStream().close();
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/query/QueryGenerator.java
... ... @@ -25,7 +25,6 @@ import org.jeecg.common.util.oConvertUtils;
25 25 import org.springframework.util.NumberUtils;
26 26  
27 27 import com.alibaba.fastjson.JSON;
28   -import com.baomidou.mybatisplus.annotation.DbType;
29 28 import com.baomidou.mybatisplus.annotation.TableField;
30 29 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
31 30  
... ... @@ -145,20 +144,23 @@ public class QueryGenerator {
145 144 //区间查询
146 145 doIntervalQuery(queryWrapper, parameterMap, type, name, column);
147 146 //判断单值 参数带不同标识字符串 走不同的查询
148   - //TODO 这种前后带逗号的支持分割后模糊查询需要否 使多选字段的查询生效
  147 + //TODO 这种前后带逗号的支持分割后模糊查询(多选字段查询生效) 示例:,1,3,
149 148 if (null != value && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) {
150 149 String multiLikeval = value.toString().replace(",,", COMMA);
151 150 String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA);
152 151 final String field = oConvertUtils.camelToUnderline(column);
153 152 if(vals.length>1) {
154 153 queryWrapper.and(j -> {
  154 + log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
155 155 j = j.like(field,vals[0]);
156 156 for (int k=1;k<vals.length;k++) {
157 157 j = j.or().like(field,vals[k]);
  158 + log.info("---查询过滤器,Query规则 .or()---field:{}, rule:{}, value:{}", field, "like", vals[k]);
158 159 }
159 160 //return j;
160 161 });
161 162 }else {
  163 + log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}", field, "like", vals[0]);
162 164 queryWrapper.and(j -> j.like(field,vals[0]));
163 165 }
164 166 }else {
... ... @@ -224,7 +226,7 @@ public class QueryGenerator {
224 226 if(parameterMap!=null&& parameterMap.containsKey(ORDER_TYPE)) {
225 227 order = parameterMap.get(ORDER_TYPE)[0];
226 228 }
227   - log.info("排序规则>>列:" + column + ",排序方式:" + order);
  229 + log.debug("排序规则>>列:" + column + ",排序方式:" + order);
228 230 if (oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
229 231 //字典字段,去掉字典翻译文本后缀
230 232 if(column.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
... ... @@ -270,10 +272,21 @@ public class QueryGenerator {
270 272 if (conditions == null || conditions.size() == 0) {
271 273 return;
272 274 }
273   - log.info("---高级查询参数-->" + conditions.toString());
  275 + // update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
  276 + List<QueryCondition> filterConditions = conditions.stream().filter(
  277 + rule -> oConvertUtils.isNotEmpty(rule.getField())
  278 + && oConvertUtils.isNotEmpty(rule.getRule())
  279 + && oConvertUtils.isNotEmpty(rule.getVal())
  280 + ).collect(Collectors.toList());
  281 + if (filterConditions.size() == 0) {
  282 + return;
  283 + }
  284 + // update-end-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
  285 + log.info("---高级查询参数-->" + filterConditions);
  286 +
274 287 queryWrapper.and(andWrapper -> {
275   - for (int i = 0; i < conditions.size(); i++) {
276   - QueryCondition rule = conditions.get(i);
  288 + for (int i = 0; i < filterConditions.size(); i++) {
  289 + QueryCondition rule = filterConditions.get(i);
277 290 if (oConvertUtils.isNotEmpty(rule.getField())
278 291 && oConvertUtils.isNotEmpty(rule.getRule())
279 292 && oConvertUtils.isNotEmpty(rule.getVal())) {
... ... @@ -324,7 +337,7 @@ public class QueryGenerator {
324 337 //update-end-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
325 338  
326 339 // 如果拼接方式是OR,就拼接OR
327   - if (MatchTypeEnum.OR == matchType && i < (conditions.size() - 1)) {
  340 + if (MatchTypeEnum.OR == matchType && i < (filterConditions.size() - 1)) {
328 341 andWrapper.or();
329 342 }
330 343 }
... ... @@ -457,15 +470,37 @@ public class QueryGenerator {
457 470  
458 471 private static void addQueryByRule(QueryWrapper<?> queryWrapper,String name,String type,String value,QueryRuleEnum rule) throws ParseException {
459 472 if(oConvertUtils.isNotEmpty(value)) {
460   - Object temp;
  473 + //update-begin--Author:sunjianlei Date:20220104 for:【JTC-409】修复逗号分割情况下没有转换类型,导致类型严格的数据库查询报错 -------------------
461 474 // 针对数字类型字段,多值查询
462   - if(value.indexOf(COMMA)!=-1){
463   - temp = value;
  475 + if(value.contains(COMMA)){
  476 + Object[] temp = Arrays.stream(value.split(COMMA)).map(v -> {
  477 + try {
  478 + return QueryGenerator.parseByType(v, type, rule);
  479 + } catch (ParseException e) {
  480 + e.printStackTrace();
  481 + return v;
  482 + }
  483 + }).toArray();
464 484 addEasyQuery(queryWrapper, name, rule, temp);
465 485 return;
466 486 }
  487 + Object temp = QueryGenerator.parseByType(value, type, rule);
  488 + addEasyQuery(queryWrapper, name, rule, temp);
  489 + //update-end--Author:sunjianlei Date:20220104 for:【JTC-409】修复逗号分割情况下没有转换类型,导致类型严格的数据库查询报错 -------------------
  490 + }
  491 + }
467 492  
468   - switch (type) {
  493 + /**
  494 + * 根据类型转换给定的值
  495 + * @param value
  496 + * @param type
  497 + * @param rule
  498 + * @return
  499 + * @throws ParseException
  500 + */
  501 + private static Object parseByType(String value, String type, QueryRuleEnum rule) throws ParseException {
  502 + Object temp;
  503 + switch (type) {
469 504 case "class java.lang.Integer":
470 505 temp = Integer.parseInt(value);
471 506 break;
... ... @@ -490,9 +525,8 @@ public class QueryGenerator {
490 525 default:
491 526 temp = value;
492 527 break;
493   - }
494   - addEasyQuery(queryWrapper, name, rule, temp);
495 528 }
  529 + return temp;
496 530 }
497 531  
498 532 /**
... ... @@ -527,12 +561,12 @@ public class QueryGenerator {
527 561 * @param rule 查询规则
528 562 * @param value 查询条件值
529 563 */
530   - private static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
  564 + public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
531 565 if (value == null || rule == null || oConvertUtils.isEmpty(value)) {
532 566 return;
533 567 }
534 568 name = oConvertUtils.camelToUnderline(name);
535   - log.info("--查询规则-->"+name+" "+rule.getValue()+" "+value);
  569 + log.info("---查询过滤器,Query规则---field:{}, rule:{}, value:{}",name,rule.getValue(),value);
536 570 switch (rule) {
537 571 case GT:
538 572 queryWrapper.gt(name, value);
... ... @@ -555,7 +589,7 @@ public class QueryGenerator {
555 589 break;
556 590 case IN:
557 591 if(value instanceof String) {
558   - queryWrapper.in(name, (Object[])value.toString().split(","));
  592 + queryWrapper.in(name, (Object[])value.toString().split(COMMA));
559 593 }else if(value instanceof String[]) {
560 594 queryWrapper.in(name, (Object[]) value);
561 595 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java
... ... @@ -34,8 +34,8 @@ import org.jeecg.common.util.oConvertUtils;
34 34 **/
35 35 public class JwtUtil {
36 36  
37   - // Token过期时间30分钟(用户登录过期时间是此时间的两倍,以token在reids缓存时间为准)
38   - public static final long EXPIRE_TIME = 30 * 60 * 1000;
  37 + // Token过期时间2小时(用户登录过期时间是此时间的两倍,以token在reids缓存时间为准)
  38 + public static final long EXPIRE_TIME = 2 * 60 * 60 * 1000;
39 39  
40 40 /**
41 41 *
... ... @@ -155,7 +155,6 @@ public class JwtUtil {
155 155 * @param user
156 156 * @return
157 157 */
158   - //TODO 急待改造 sckjkdsjsfjdk
159 158 public static String getUserSystemData(String key,SysUserCacheInfo user) {
160 159 if(user==null) {
161 160 user = JeecgDataAutorUtils.loadUserInfo();
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/DictModel.java
... ... @@ -39,5 +39,11 @@ public class DictModel implements Serializable{
39 39 public String getTitle() {
40 40 return this.text;
41 41 }
  42 + /**
  43 + * 特殊用途: vue3 Select组件
  44 + */
  45 + public String getLabel() {
  46 + return this.text;
  47 + }
42 48  
43 49 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java
... ... @@ -14,6 +14,7 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource;
14 14 import org.springframework.util.FileCopyUtils;
15 15 import org.springframework.web.multipart.MultipartFile;
16 16  
  17 +import javax.servlet.http.HttpServletRequest;
17 18 import javax.sql.DataSource;
18 19 import java.io.ByteArrayInputStream;
19 20 import java.io.File;
... ... @@ -282,4 +283,39 @@ public class CommonUtils {
282 283 return DB_TYPE;
283 284  
284 285 }
  286 + /**
  287 + * 获取服务器地址
  288 + *
  289 + * @param request
  290 + * @return
  291 + */
  292 + public static String getBaseUrl(HttpServletRequest request) {
  293 + //1.【兼容】兼容微服务下的 base path-------
  294 + String x_gateway_base_path = request.getHeader("X_GATEWAY_BASE_PATH");
  295 + if(oConvertUtils.isNotEmpty(x_gateway_base_path)){
  296 + log.info("x_gateway_base_path = "+ x_gateway_base_path);
  297 + return x_gateway_base_path;
  298 + }
  299 + //2.【兼容】SSL认证之后,request.getScheme()获取不到https的问题
  300 + // https://blog.csdn.net/weixin_34376986/article/details/89767950
  301 + String scheme = request.getHeader("X-Forwarded-Scheme");
  302 + if(oConvertUtils.isEmpty(scheme)){
  303 + scheme = request.getScheme();
  304 + }
  305 +
  306 + //3.常规操作
  307 + String serverName = request.getServerName();
  308 + int serverPort = request.getServerPort();
  309 + String contextPath = request.getContextPath();
  310 +
  311 + //返回 host domain
  312 + String baseDomainPath = null;
  313 + if(80 == serverPort){
  314 + baseDomainPath = scheme + "://" + serverName + contextPath ;
  315 + }else{
  316 + baseDomainPath = scheme + "://" + serverName + ":" + serverPort + contextPath ;
  317 + }
  318 + log.info("-----Common getBaseUrl----- : " + baseDomainPath);
  319 + return baseDomainPath;
  320 + }
285 321 }
286 322 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/MinioUtil.java
1 1 package org.jeecg.common.util;
2 2  
3 3 import io.minio.*;
  4 +import io.minio.http.Method;
4 5 import lombok.extern.slf4j.Slf4j;
5 6 import org.jeecg.common.util.filter.FileTypeFilter;
6 7 import org.jeecg.common.util.filter.StrAttackFilter;
... ... @@ -158,9 +159,11 @@ public class MinioUtil {
158 159 public static String getObjectURL(String bucketName, String objectName, Integer expires) {
159 160 initMinio(minioUrl, minioName,minioPass);
160 161 try{
  162 + //update-begin---author:liusq Date:20220121 for:获取文件外链报错提示method不能为空,导致文件下载和预览失败----
161 163 GetPresignedObjectUrlArgs objectArgs = GetPresignedObjectUrlArgs.builder().object(objectName)
162 164 .bucket(bucketName)
163   - .expiry(expires).build();
  165 + .expiry(expires).method(Method.GET).build();
  166 + //update-begin---author:liusq Date:20220121 for:获取文件外链报错提示method不能为空,导致文件下载和预览失败----
164 167 String url = minioClient.getPresignedObjectUrl(objectArgs);
165 168 return URLDecoder.decode(url,"UTF-8");
166 169 }catch (Exception e){
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java
... ... @@ -17,7 +17,7 @@ public class SqlInjectionUtil {
17 17 * (上线修改值 20200501,同步修改前端的盐值)
18 18 */
19 19 private final static String TABLE_DICT_SIGN_SALT = "20200501";
20   - private final static String xssStr = "'|and |exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+";
  20 + private final static String xssStr = "and |exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()";
21 21  
22 22 /*
23 23 * 针对表字典进行额外的sign签名校验(增加安全机制)
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/dynamic/db/DbTypeUtils.java
... ... @@ -25,7 +25,7 @@ public class DbTypeUtils {
25 25 dialectMap.put("postgresql", "org.hibernate.dialect.PostgreSQLDialect"); //1 --
26 26 dialectMap.put("sqlserver2005", "org.hibernate.dialect.SQLServer2005Dialect");
27 27 dialectMap.put("sqlserver", "org.hibernate.dialect.SQLServerDialect"); //1
28   - dialectMap.put("dm", "org.hibernate.dialect.OracleDialect");//达梦数据库 [国产] 1--
  28 + dialectMap.put("dm", "org.hibernate.dialect.DmDialect");//达梦数据库 [国产] 1--
29 29 dialectMap.put("xugu", "org.hibernate.dialect.HSQLDialect"); //虚谷数据库
30 30 dialectMap.put("kingbasees", "org.hibernate.dialect.PostgreSQLDialect"); //人大金仓 [国产] 1
31 31 dialectMap.put("phoenix", "org.hibernate.dialect.HSQLDialect"); // Phoenix HBase数据库
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oss/OssBootUtil.java
... ... @@ -258,6 +258,9 @@ public class OssBootUtil {
258 258 newBucket = bucket;
259 259 }
260 260 initOSS(endPoint, accessKeyId, accessKeySecret);
  261 + //update-begin---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件----
  262 + objectName = OssBootUtil.replacePrefix(objectName,bucket);
  263 + //update-end---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件----
261 264 OSSObject ossObject = ossClient.getObject(newBucket,objectName);
262 265 inputStream = new BufferedInputStream(ossObject.getObjectContent());
263 266 }catch (Exception e){
... ... @@ -266,14 +269,14 @@ public class OssBootUtil {
266 269 return inputStream;
267 270 }
268 271  
269   - /**
270   - * 获取文件流
271   - * @param objectName
272   - * @return
273   - */
274   - public static InputStream getOssFile(String objectName){
275   - return getOssFile(objectName,null);
276   - }
  272 + ///**
  273 + // * 获取文件流
  274 + // * @param objectName
  275 + // * @return
  276 + // */
  277 + //public static InputStream getOssFile(String objectName){
  278 + // return getOssFile(objectName,null);
  279 + //}
277 280  
278 281 /**
279 282 * 获取文件外链
... ... @@ -285,6 +288,9 @@ public class OssBootUtil {
285 288 public static String getObjectURL(String bucketName, String objectName, Date expires) {
286 289 initOSS(endPoint, accessKeyId, accessKeySecret);
287 290 try{
  291 + //update-begin---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件----
  292 + objectName = OssBootUtil.replacePrefix(objectName,bucketName);
  293 + //update-end---author:liusq Date:20220120 for:替换objectName前缀,防止key不一致导致获取不到文件----
288 294 if(ossClient.doesObjectExist(bucketName,objectName)){
289 295 URL url = ossClient.generatePresignedUrl(bucketName,objectName,expires);
290 296 return URLDecoder.decode(url.toString(),"UTF-8");
... ... @@ -334,5 +340,27 @@ public class OssBootUtil {
334 340 return FILE_URL;
335 341 }
336 342  
337   -
  343 + /**
  344 + * 替换前缀,防止key不一致导致获取不到文件
  345 + * @param objectName 文件上传路径 key
  346 + * @param customBucket 自定义桶
  347 + * @date 2022-01-20
  348 + * @author lsq
  349 + * @return
  350 + */
  351 + private static String replacePrefix(String objectName,String customBucket){
  352 + log.info("------replacePrefix---替换前---objectName:{}",objectName);
  353 + if(oConvertUtils.isNotEmpty(staticDomain)){
  354 + objectName= objectName.replace(staticDomain+"/","");
  355 + }else{
  356 + String newBucket = bucketName;
  357 + if(oConvertUtils.isNotEmpty(customBucket)){
  358 + newBucket = customBucket;
  359 + }
  360 + String path ="https://" + newBucket + "." + endPoint + "/";
  361 + objectName = objectName.replace(path,"");
  362 + }
  363 + log.info("------replacePrefix---替换后---objectName:{}",objectName);
  364 + return objectName;
  365 + }
338 366 }
339 367 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java
1 1 package org.jeecg.config;
2 2  
3   -import lombok.extern.slf4j.Slf4j;
  3 +import java.util.ArrayList;
  4 +import java.util.List;
  5 +
  6 +import javax.annotation.Resource;
  7 +
4 8 import org.jeecg.common.api.CommonAPI;
5 9 import org.jeecg.common.system.vo.DictModel;
6 10 import org.jeecg.common.util.oConvertUtils;
... ... @@ -8,9 +12,7 @@ import org.jeecgframework.dict.service.AutoPoiDictServiceI;
8 12 import org.springframework.context.annotation.Lazy;
9 13 import org.springframework.stereotype.Service;
10 14  
11   -import javax.annotation.Resource;
12   -import java.util.ArrayList;
13   -import java.util.List;
  15 +import lombok.extern.slf4j.Slf4j;
14 16  
15 17 /**
16 18 * 描述:AutoPoi Excel注解支持字典参数设置
... ... @@ -25,6 +27,9 @@ import java.util.List;
25 27 @Slf4j
26 28 @Service
27 29 public class AutoPoiDictConfig implements AutoPoiDictServiceI {
  30 + final static String EXCEL_SPLIT_TAG = "_";
  31 + final static String TEMP_EXCEL_SPLIT_TAG = "---";
  32 +
28 33 @Lazy
29 34 @Resource
30 35 private CommonAPI commonAPI;
... ... @@ -53,7 +58,14 @@ public class AutoPoiDictConfig implements AutoPoiDictServiceI {
53 58 }
54 59 for (DictModel t : dictList) {
55 60 if(t!=null){
56   - dictReplaces.add(t.getText() + "_" + t.getValue());
  61 + //update-begin---author:scott Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析---
  62 + if(t.getValue().contains(EXCEL_SPLIT_TAG)){
  63 + String val = t.getValue().replace(EXCEL_SPLIT_TAG,TEMP_EXCEL_SPLIT_TAG);
  64 + dictReplaces.add(t.getText() + EXCEL_SPLIT_TAG + val);
  65 + }else{
  66 + dictReplaces.add(t.getText() + EXCEL_SPLIT_TAG + t.getValue());
  67 + }
  68 + //update-end---author:20211220 Date:20211220 for:[issues/I4MBB3]@Excel dicText字段的值有下划线时,导入功能不能正确解析---
57 69 }
58 70 }
59 71 if (dictReplaces != null && dictReplaces.size() != 0) {
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeeccgBaseConfig.java
1 1 package org.jeecg.config;
2 2  
  3 +import org.jeecg.config.vo.Shiro;
3 4 import org.springframework.boot.context.properties.ConfigurationProperties;
4 5 import org.springframework.stereotype.Component;
5 6  
... ... @@ -14,6 +15,15 @@ public class JeeccgBaseConfig {
14 15 * 是否启用安全模式
15 16 */
16 17 private Boolean safeMode = false;
  18 + /**
  19 + * shiro拦截排除
  20 + */
  21 + private Shiro shiro;
  22 + /**
  23 + * 签名密钥串(字典等敏感接口)
  24 + * @TODO 降低使用成本加的默认值,实际以 yml配置 为准
  25 + */
  26 + private String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
17 27  
18 28 public Boolean getSafeMode() {
19 29 return safeMode;
... ... @@ -22,4 +32,20 @@ public class JeeccgBaseConfig {
22 32 public void setSafeMode(Boolean safeMode) {
23 33 this.safeMode = safeMode;
24 34 }
  35 +
  36 + public String getSignatureSecret() {
  37 + return signatureSecret;
  38 + }
  39 +
  40 + public void setSignatureSecret(String signatureSecret) {
  41 + this.signatureSecret = signatureSecret;
  42 + }
  43 +
  44 + public Shiro getShiro() {
  45 + return shiro;
  46 + }
  47 +
  48 + public void setShiro(Shiro shiro) {
  49 + this.shiro = shiro;
  50 + }
25 51 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/StaticConfig.java
... ... @@ -20,11 +20,11 @@ public class StaticConfig {
20 20 @Value(value = "${spring.mail.username}")
21 21 private String emailFrom;
22 22  
23   - /**
24   - * 签名密钥串
25   - */
26   - @Value(value = "${jeecg.signatureSecret}")
27   - private String signatureSecret;
  23 +// /**
  24 +// * 签名密钥串
  25 +// */
  26 +// @Value(value = "${jeecg.signatureSecret}")
  27 +// private String signatureSecret;
28 28  
29 29  
30 30 /*@Bean
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java
  1 +
1 2 package org.jeecg.config.shiro;
2 3  
3 4 import lombok.extern.slf4j.Slf4j;
... ... @@ -15,6 +16,7 @@ import org.crazycake.shiro.RedisClusterManager;
15 16 import org.crazycake.shiro.RedisManager;
16 17 import org.jeecg.common.constant.CommonConstant;
17 18 import org.jeecg.common.util.oConvertUtils;
  19 +import org.jeecg.config.JeeccgBaseConfig;
18 20 import org.jeecg.config.shiro.filters.CustomShiroFilterFactoryBean;
19 21 import org.jeecg.config.shiro.filters.JwtFilter;
20 22 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
... ... @@ -43,13 +45,12 @@ import java.util.*;
43 45 @Configuration
44 46 public class ShiroConfig {
45 47  
46   - @Value("${jeecg.shiro.excludeUrls}")
47   - private String excludeUrls;
48 48 @Resource
49 49 LettuceConnectionFactory lettuceConnectionFactory;
50 50 @Autowired
51 51 private Environment env;
52   -
  52 + @Autowired
  53 + JeeccgBaseConfig jeeccgBaseConfig;
53 54  
54 55 /**
55 56 * Filter Chain定义说明
... ... @@ -64,8 +65,9 @@ public class ShiroConfig {
64 65 shiroFilterFactoryBean.setSecurityManager(securityManager);
65 66 // 拦截器
66 67 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
67   - if(oConvertUtils.isNotEmpty(excludeUrls)){
68   - String[] permissionUrl = excludeUrls.split(",");
  68 + String shiroExcludeUrls = jeeccgBaseConfig.getShiro().getExcludeUrls();
  69 + if(oConvertUtils.isNotEmpty(shiroExcludeUrls)){
  70 + String[] permissionUrl = shiroExcludeUrls.split(",");
69 71 for(String url : permissionUrl){
70 72 filterChainDefinitionMap.put(url,"anon");
71 73 }
... ... @@ -89,6 +91,12 @@ public class ShiroConfig {
89 91 filterChainDefinitionMap.put("/sys/common/static/**", "anon");//图片预览 &下载文件不限制token
90 92 filterChainDefinitionMap.put("/sys/common/pdf/**", "anon");//pdf预览
91 93 filterChainDefinitionMap.put("/generic/**", "anon");//pdf预览需要文件
  94 +
  95 + filterChainDefinitionMap.put("/sys/getLoginQrcode/**", "anon"); //登录二维码
  96 + filterChainDefinitionMap.put("/sys/getQrcodeToken/**", "anon"); //监听扫码
  97 + filterChainDefinitionMap.put("/sys/checkAuth", "anon"); //授权接口排除
  98 +
  99 +
92 100 filterChainDefinitionMap.put("/", "anon");
93 101 filterChainDefinitionMap.put("/doc.html", "anon");
94 102 filterChainDefinitionMap.put("/**/*.js", "anon");
... ... @@ -130,6 +138,9 @@ public class ShiroConfig {
130 138 filterChainDefinitionMap.put("/newsWebsocket/**", "anon");//CMS模块
131 139 filterChainDefinitionMap.put("/vxeSocket/**", "anon");//JVxeTable无痕刷新示例
132 140  
  141 + //wps
  142 + filterChainDefinitionMap.put("/v1/**","anon");
  143 +
133 144 //性能监控 TODO 存在安全漏洞泄露TOEKN(durid连接池也有)
134 145 filterChainDefinitionMap.put("/actuator/**", "anon");
135 146  
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroRealm.java
... ... @@ -137,9 +137,12 @@ public class ShiroRealm extends AuthorizingRealm {
137 137 if(oConvertUtils.isNotEmpty(userTenantIds)){
138 138 String contextTenantId = TenantContext.getTenant();
139 139 if(oConvertUtils.isNotEmpty(contextTenantId) && !"0".equals(contextTenantId)){
140   - if(String.join(",",userTenantIds).indexOf(contextTenantId)<0){
  140 + //update-begin-author:taoyan date:20211227 for: /issues/I4O14W 用户租户信息变更判断漏洞
  141 + String[] arr = userTenantIds.split(",");
  142 + if(!oConvertUtils.isIn(contextTenantId, arr)){
141 143 throw new AuthenticationException("用户租户信息变更,请重新登陆!");
142 144 }
  145 + //update-end-author:taoyan date:20211227 for: /issues/I4O14W 用户租户信息变更判断漏洞
143 146 }
144 147 }
145 148 //update-end-author:taoyan date:20210609 for:校验用户的tenant_id和前端传过来的是否一致
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/sign/util/SignUtil.java
... ... @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j;
5 5 import org.jeecg.common.exception.JeecgBootException;
6 6 import org.jeecg.common.util.SpringContextUtils;
7 7 import org.jeecg.common.util.oConvertUtils;
8   -import org.jeecg.config.StaticConfig;
  8 +import org.jeecg.config.JeeccgBaseConfig;
9 9 import org.springframework.util.DigestUtils;
10 10 import org.springframework.util.StringUtils;
11 11  
... ... @@ -46,8 +46,9 @@ public class SignUtil {
46 46 params.remove("_t");
47 47 String paramsJsonStr = JSONObject.toJSONString(params);
48 48 log.info("Param paramsJsonStr : {}", paramsJsonStr);
49   - StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
50   - String signatureSecret = staticConfig.getSignatureSecret();
  49 + //设置签名秘钥
  50 + JeeccgBaseConfig jeeccgBaseConfig = SpringContextUtils.getBean(JeeccgBaseConfig.class);
  51 + String signatureSecret = jeeccgBaseConfig.getSignatureSecret();
51 52 if(oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains("${")){
52 53 throw new JeecgBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 !!");
53 54 }
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/vo/Shiro.java 0 → 100644
  1 +package org.jeecg.config.vo;
  2 +
  3 +/**
  4 + * @Description: TODO
  5 + * @author: scott
  6 + * @date: 2022年01月21日 14:23
  7 + */
  8 +public class Shiro {
  9 + private String excludeUrls = "";
  10 +
  11 + public String getExcludeUrls() {
  12 + return excludeUrls;
  13 + }
  14 +
  15 + public void setExcludeUrls(String excludeUrls) {
  16 + this.excludeUrls = excludeUrls;
  17 + }
  18 +}
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-tools/pom.xml
... ... @@ -4,7 +4,7 @@
4 4 <parent>
5 5 <groupId>org.jeecgframework.boot</groupId>
6 6 <artifactId>jeecg-boot-base</artifactId>
7   - <version>3.0</version>
  7 + <version>3.1.0</version>
8 8 </parent>
9 9 <modelVersion>4.0.0</modelVersion>
10 10 <description>公共模块</description>
... ...
jeecg-boot/jeecg-boot-base/jeecg-boot-base-tools/src/main/java/org/jeecg/common/constant/CacheConstant.java
... ... @@ -96,4 +96,8 @@ public interface CacheConstant {
96 96 * online图表
97 97 */
98 98 public static final String ONLINE_GRAPH = "sys:cache:online:graph";
  99 + /**
  100 + * 拖拽页面信息缓存
  101 + */
  102 + public static final String DRAG_PAGE_CACHE = "drag:cache:page";
99 103 }
... ...
jeecg-boot/jeecg-boot-base/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-parent</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ...
jeecg-boot/jeecg-boot-module-demo/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-parent</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ...
jeecg-boot/jeecg-boot-module-demo/src/main/java/org/jeecg/modules/demo/mock/MockController.java
... ... @@ -31,7 +31,7 @@ public class MockController {
31 31 * @return
32 32 */
33 33 @RequestMapping(value = "/json/{filename}", method = RequestMethod.GET)
34   - public String getJsonData(@PathVariable String filename) {
  34 + public String getJsonData(@PathVariable("filename") String filename) {
35 35 String jsonpath = "classpath:org/jeecg/modules/demo/mock/json/"+filename+".json";
36 36 return readJson(jsonpath);
37 37 }
... ...
jeecg-boot/jeecg-boot-module-demo/src/main/java/org/jeecg/modules/demo/online/OnlCgformDemoController.java 0 → 100644
  1 +package org.jeecg.modules.demo.online;
  2 +
  3 +import com.alibaba.fastjson.JSONArray;
  4 +import com.alibaba.fastjson.JSONObject;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.jeecg.common.api.vo.Result;
  7 +import org.jeecg.common.system.vo.DictModel;
  8 +import org.jeecg.common.util.oConvertUtils;
  9 +import org.springframework.web.bind.annotation.PostMapping;
  10 +import org.springframework.web.bind.annotation.RequestBody;
  11 +import org.springframework.web.bind.annotation.RequestMapping;
  12 +import org.springframework.web.bind.annotation.RestController;
  13 +
  14 +import java.util.ArrayList;
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * Online表单开发 demo 示例
  19 + *
  20 + * @author sunjianlei
  21 + * @date 2021-12-16
  22 + */
  23 +@Slf4j
  24 +@RestController("onlCgformDemoController")
  25 +@RequestMapping("/demo/online/cgform")
  26 +public class OnlCgformDemoController {
  27 +
  28 + /**
  29 + * Online表单 http 增强,list增强示例
  30 + * @param params
  31 + * @return
  32 + */
  33 + @PostMapping("/enhanceJavaListHttp")
  34 + public Result<?> enhanceJavaListHttp(@RequestBody JSONObject params) {
  35 + log.info(" --- params:" + params.toJSONString());
  36 + JSONArray dataList = params.getJSONArray("dataList");
  37 + List<DictModel> dict = virtualDictData();
  38 + for (int i = 0; i < dataList.size(); i++) {
  39 + JSONObject record = dataList.getJSONObject(i);
  40 + String province = record.getString("province");
  41 + if (province == null) {
  42 + continue;
  43 + }
  44 + String text = dict.stream()
  45 + .filter(p -> province.equals(p.getValue()))
  46 + .map(DictModel::getText)
  47 + .findAny()
  48 + .orElse(province);
  49 + record.put("province", text);
  50 + }
  51 + Result<?> res = Result.OK(dataList);
  52 + res.setCode(1);
  53 + return res;
  54 + }
  55 +
  56 + /**
  57 + * 模拟字典数据
  58 + *
  59 + * @return
  60 + */
  61 + private List<DictModel> virtualDictData() {
  62 + List<DictModel> dict = new ArrayList<>();
  63 + dict.add(new DictModel("bj", "北京"));
  64 + dict.add(new DictModel("sd", "山东"));
  65 + dict.add(new DictModel("ah", "安徽"));
  66 + return dict;
  67 + }
  68 +
  69 +
  70 + /**
  71 + * Online表单 http 增强,add、edit增强示例
  72 + * @param params
  73 + * @return
  74 + */
  75 + @PostMapping("/enhanceJavaHttp")
  76 + public Result<?> enhanceJavaHttp(@RequestBody JSONObject params) {
  77 + log.info(" --- params:" + params.toJSONString());
  78 + String tableName = params.getString("tableName");
  79 + JSONObject record = params.getJSONObject("record");
  80 + /*
  81 + * 业务场景一: 获取提交表单数据,进行其他业务关联操作
  82 + * (比如:根据入库单,同步更改库存)
  83 + */
  84 + log.info(" --- tableName:" + tableName);
  85 + log.info(" --- 行数据:" + record.toJSONString());
  86 + /*
  87 + * 业务场景二: 保存数据之前进行数据的校验
  88 + * 直接返回错误状态即可
  89 + */
  90 + String phone = record.getString("phone");
  91 + if (oConvertUtils.isEmpty(phone)) {
  92 + return Result.error("手机号不能为空!");
  93 + }
  94 + /*
  95 + * 业务场景三: 保存数据之对数据的处理
  96 + * 直接操作 record 即可
  97 + */
  98 + record.put("phone", "010-" + phone);
  99 +
  100 + /* 其他业务场景自行实现 */
  101 +
  102 + // 返回场景一: 不对 record 做任何修改的情况下,可以直接返回 code,
  103 + // 返回 0 = 丢弃当前数据
  104 + // 返回 1 = 新增当前数据
  105 + // 返回 2 = 修改当前数据 TODO(?)存疑
  106 +// return Result.OK(1);
  107 +
  108 + // 返回场景二: 需要对 record 做修改的情况下,需要返回一个JSONObject对象(或者Map也行)
  109 + JSONObject res = new JSONObject();
  110 + res.put("code", 1);
  111 + // 将 record 返回以进行修改
  112 + res.put("record", record);
  113 + // TODO 不要 code 的概念
  114 + return Result.OK(res);
  115 + }
  116 +
  117 +}
... ...
jeecg-boot/jeecg-boot-module-demo/src/main/java/org/jeecg/modules/demo/test/controller/JeecgDemoController.java
... ... @@ -62,6 +62,7 @@ public class JeecgDemoController extends JeecgController&lt;JeecgDemo, IJeecgDemoSe
62 62 public Result<?> list(JeecgDemo jeecgDemo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
63 63 HttpServletRequest req) {
64 64 QueryWrapper<JeecgDemo> queryWrapper = QueryGenerator.initQueryWrapper(jeecgDemo, req.getParameterMap());
  65 + queryWrapper.orderByDesc("create_time");
65 66 Page<JeecgDemo> page = new Page<JeecgDemo>(pageNo, pageSize);
66 67  
67 68 IPage<JeecgDemo> pageList = jeecgDemoService.page(page, queryWrapper);
... ... @@ -92,9 +93,9 @@ public class JeecgDemoController extends JeecgController&lt;JeecgDemo, IJeecgDemoSe
92 93 * @param jeecgDemo
93 94 * @return
94 95 */
95   - @PutMapping(value = "/edit")
96   - @ApiOperation(value = "编辑DEMO", notes = "编辑DEMO")
97 96 @AutoLog(value = "编辑DEMO", operateType = CommonConstant.OPERATE_TYPE_3)
  97 + @ApiOperation(value = "编辑DEMO", notes = "编辑DEMO")
  98 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
98 99 public Result<?> edit(@RequestBody JeecgDemo jeecgDemo) {
99 100 jeecgDemoService.updateById(jeecgDemo);
100 101 return Result.OK("更新成功!");
... ... @@ -316,5 +317,9 @@ public class JeecgDemoController extends JeecgController&lt;JeecgDemo, IJeecgDemoSe
316 317 return Result.OK("1");
317 318 }
318 319  
  320 + @GetMapping(value = "/hello")
  321 + public String hello(HttpServletRequest req) {
  322 + return "hello world!";
  323 + }
319 324  
320 325 }
... ...
jeecg-boot/jeecg-boot-module-demo/src/main/java/org/jeecg/modules/demo/test/controller/JeecgOrderTabMainController.java renamed to jeecg-boot/jeecg-boot-module-demo/src/main/java/org/jeecg/modules/demo/test/controller/JeecgOrderErpMainController.java
1 1 package org.jeecg.modules.demo.test.controller;
2 2  
3   -import java.util.Arrays;
4   -import java.util.List;
5   -
6   -import javax.servlet.http.HttpServletRequest;
7   -
  3 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  4 +import com.baomidou.mybatisplus.core.metadata.IPage;
  5 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6 +import lombok.extern.slf4j.Slf4j;
8 7 import org.jeecg.common.api.vo.Result;
9 8 import org.jeecg.common.system.query.QueryGenerator;
10 9 import org.jeecg.modules.demo.test.entity.JeecgOrderCustomer;
... ... @@ -16,20 +15,10 @@ import org.jeecg.modules.demo.test.service.IJeecgOrderTicketService;
16 15 import org.jeecg.modules.demo.test.vo.JeecgOrderMainPage;
17 16 import org.springframework.beans.BeanUtils;
18 17 import org.springframework.beans.factory.annotation.Autowired;
19   -import org.springframework.web.bind.annotation.DeleteMapping;
20   -import org.springframework.web.bind.annotation.GetMapping;
21   -import org.springframework.web.bind.annotation.PostMapping;
22   -import org.springframework.web.bind.annotation.PutMapping;
23   -import org.springframework.web.bind.annotation.RequestBody;
24   -import org.springframework.web.bind.annotation.RequestMapping;
25   -import org.springframework.web.bind.annotation.RequestParam;
26   -import org.springframework.web.bind.annotation.RestController;
  18 +import org.springframework.web.bind.annotation.*;
27 19  
28   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
29   -import com.baomidou.mybatisplus.core.metadata.IPage;
30   -import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
31   -
32   -import lombok.extern.slf4j.Slf4j;
  20 +import javax.servlet.http.HttpServletRequest;
  21 +import java.util.Arrays;
33 22  
34 23 /**
35 24 * @Description: 一对多示例(ERP TAB风格)
... ... @@ -40,7 +29,7 @@ import lombok.extern.slf4j.Slf4j;
40 29 @Slf4j
41 30 @RestController
42 31 @RequestMapping("/test/order")
43   -public class JeecgOrderTabMainController {
  32 +public class JeecgOrderErpMainController {
44 33  
45 34 @Autowired
46 35 private IJeecgOrderMainService jeecgOrderMainService;
... ... @@ -89,7 +78,7 @@ public class JeecgOrderTabMainController {
89 78 * @param jeecgOrderMainPage
90 79 * @return
91 80 */
92   - @PutMapping("/edit")
  81 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
93 82 public Result<?> edit(@RequestBody JeecgOrderMainPage jeecgOrderMainPage) {
94 83 JeecgOrderMain jeecgOrderMain = new JeecgOrderMain();
95 84 BeanUtils.copyProperties(jeecgOrderMainPage, jeecgOrderMain);
... ... @@ -186,7 +175,7 @@ public class JeecgOrderTabMainController {
186 175 * @param jeecgOrderCustomer
187 176 * @return
188 177 */
189   - @PutMapping("/editCustomer")
  178 + @RequestMapping(value = "/editCustomer", method = {RequestMethod.PUT,RequestMethod.POST})
190 179 public Result<?> editCustomer(@RequestBody JeecgOrderCustomer jeecgOrderCustomer) {
191 180 jeecgOrderCustomerService.updateById(jeecgOrderCustomer);
192 181 return Result.ok("添加成功!");
... ... @@ -234,7 +223,7 @@ public class JeecgOrderTabMainController {
234 223 * @param jeecgOrderTicket
235 224 * @return
236 225 */
237   - @PutMapping("/editTicket")
  226 + @RequestMapping(value = "/editTicket", method = {RequestMethod.PUT,RequestMethod.POST})
238 227 public Result<?> editTicket(@RequestBody JeecgOrderTicket jeecgOrderTicket) {
239 228 jeecgOrderTicketService.updateById(jeecgOrderTicket);
240 229 return Result.ok("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-demo/src/main/java/org/jeecg/modules/demo/test/controller/JeecgOrderMainController.java
... ... @@ -104,7 +104,7 @@ public class JeecgOrderMainController extends JeecgController&lt;JeecgOrderMain, IJ
104 104 * @param jeecgOrderMainPage
105 105 * @return
106 106 */
107   - @PutMapping(value = "/edit")
  107 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
108 108 public Result<?> eidt(@RequestBody JeecgOrderMainPage jeecgOrderMainPage) {
109 109 JeecgOrderMain jeecgOrderMain = new JeecgOrderMain();
110 110 BeanUtils.copyProperties(jeecgOrderMainPage, jeecgOrderMain);
... ...
jeecg-boot/jeecg-boot-module-system/Dockerfile
... ... @@ -11,6 +11,6 @@ WORKDIR /jeecg-boot
11 11 EXPOSE 8080
12 12  
13 13 ADD ./src/main/resources/jeecg ./config/jeecg
14   -ADD ./target/jeecg-boot-module-system-3.0.jar ./
  14 +ADD ./target/jeecg-boot-module-system-3.1.0.jar ./
15 15  
16   -CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-3.0.jar
17 16 \ No newline at end of file
  17 +CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-3.1.0.jar
18 18 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/pom.xml
... ... @@ -4,7 +4,7 @@
4 4 <parent>
5 5 <groupId>org.jeecgframework.boot</groupId>
6 6 <artifactId>jeecg-boot-parent</artifactId>
7   - <version>3.0</version>
  7 + <version>3.1.0</version>
8 8 </parent>
9 9 <modelVersion>4.0.0</modelVersion>
10 10  
... ... @@ -38,7 +38,7 @@
38 38 <dependency>
39 39 <groupId>org.jeecgframework</groupId>
40 40 <artifactId>jeewx-api</artifactId>
41   - <version>1.4.6</version>
  41 + <version>1.4.7</version>
42 42 <exclusions>
43 43 <exclusion>
44 44 <artifactId>commons-beanutils</artifactId>
... ... @@ -54,7 +54,7 @@
54 54 <dependency>
55 55 <groupId>org.jeecgframework.jimureport</groupId>
56 56 <artifactId>jimureport-spring-boot-starter</artifactId>
57   - <version>1.4.0</version>
  57 + <version>1.4.2</version>
58 58 </dependency>
59 59  
60 60  
... ... @@ -62,7 +62,7 @@
62 62 <dependency>
63 63 <groupId>org.jeecgframework.boot</groupId>
64 64 <artifactId>jeecg-boot-module-demo</artifactId>
65   - <version>3.0</version>
  65 + <version>3.1.0</version>
66 66 </dependency>
67 67  
68 68 </dependencies>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/JeecgSystemApplication.java
1 1 package org.jeecg;
2 2  
3 3 import lombok.extern.slf4j.Slf4j;
  4 +import org.apache.catalina.Context;
  5 +import org.apache.tomcat.util.scan.StandardJarScanner;
4 6 import org.jeecg.common.util.oConvertUtils;
5 7 import org.springframework.boot.SpringApplication;
  8 +//import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  9 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
6 10 import org.springframework.boot.autoconfigure.SpringBootApplication;
  11 +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
7 12 import org.springframework.boot.builder.SpringApplicationBuilder;
  13 +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
8 14 import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
9 15 import org.springframework.context.ConfigurableApplicationContext;
  16 +import org.springframework.context.annotation.Bean;
10 17 import org.springframework.core.env.Environment;
11 18  
12 19 import java.net.InetAddress;
... ... @@ -17,6 +24,7 @@ import java.net.UnknownHostException;
17 24 */
18 25 @Slf4j
19 26 @SpringBootApplication
  27 +@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
20 28 public class JeecgSystemApplication extends SpringBootServletInitializer {
21 29  
22 30 @Override
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/config/jimureport/JimuReportTokenService.java
1 1 package org.jeecg.config.jimureport;
2 2  
3   -import org.jeecg.common.constant.DataBaseConstant;
  3 +import lombok.extern.slf4j.Slf4j;
4 4 import org.jeecg.common.system.api.ISysBaseAPI;
5 5 import org.jeecg.common.system.util.JwtUtil;
6 6 import org.jeecg.common.system.vo.SysUserCacheInfo;
... ... @@ -20,6 +20,9 @@ import java.util.Map;
20 20 * * 1.自定义获取登录token
21 21 * * 2.自定义获取登录用户
22 22 */
  23 +
  24 +
  25 +@Slf4j
23 26 @Component
24 27 public class JimuReportTokenService implements JmReportTokenServiceI {
25 28 @Autowired
... ... @@ -45,10 +48,16 @@ public class JimuReportTokenService implements JmReportTokenServiceI {
45 48  
46 49 @Override
47 50 public Map<String, Object> getUserInfo(String token) {
  51 + Map<String, Object> map = new HashMap<String, Object>();
48 52 String username = JwtUtil.getUsername(token);
49 53 //此处通过token只能拿到一个信息 用户账号 后面的就是根据账号获取其他信息 查询数据或是走redis 用户根据自身业务可自定义
50   - SysUserCacheInfo userInfo = sysBaseAPI.getCacheUser(username);
51   - Map<String, Object> map = new HashMap<String, Object>();
  54 + SysUserCacheInfo userInfo = null;
  55 + try {
  56 + userInfo = sysBaseAPI.getCacheUser(username);
  57 + } catch (Exception e) {
  58 + log.error("获取用户信息异常:"+ e.getMessage());
  59 + return map;
  60 + }
52 61 //设置账号名
53 62 map.put(SYS_USER_CODE, userInfo.getSysUserCode());
54 63 //设置部门编码
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/ngalain/aop/LogRecordAspect.java
1   -package org.jeecg.modules.ngalain.aop;
2   -
3   -import javax.servlet.http.HttpServletRequest;
4   -
5   -import org.aspectj.lang.ProceedingJoinPoint;
6   -import org.aspectj.lang.annotation.Around;
7   -import org.aspectj.lang.annotation.Aspect;
8   -import org.aspectj.lang.annotation.Pointcut;
9   -import org.springframework.context.annotation.Configuration;
10   -import org.springframework.web.context.request.RequestAttributes;
11   -import org.springframework.web.context.request.RequestContextHolder;
12   -import org.springframework.web.context.request.ServletRequestAttributes;
13   -import org.slf4j.Logger;
14   -import org.slf4j.LoggerFactory;;
15   -
16   -
17   -// 暂时注释掉,提高系统性能
18   -//@Aspect //定义一个切面
19   -//@Configuration
20   -public class LogRecordAspect {
21   -private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);
22   -
23   - // 定义切点Pointcut
24   - @Pointcut("execution(public * org.jeecg.modules.*.*.*Controller.*(..))")
25   - public void excudeService() {
26   - }
27   -
28   - @Around("excudeService()")
29   - public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
30   - RequestAttributes ra = RequestContextHolder.getRequestAttributes();
31   - ServletRequestAttributes sra = (ServletRequestAttributes) ra;
32   - HttpServletRequest request = sra.getRequest();
33   -
34   - String url = request.getRequestURL().toString();
35   - String method = request.getMethod();
36   - String uri = request.getRequestURI();
37   - String queryString = request.getQueryString();
38   - logger.info("请求开始, 各个参数, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);
39   -
40   - // result的值就是被拦截方法的返回值
41   - Object result = pjp.proceed();
42   -
43   - logger.info("请求结束,controller的返回值是 " + result);
44   - return result;
45   - }
46   -}
47 1 \ No newline at end of file
  2 +//package org.jeecg.modules.ngalain.aop;
  3 +//
  4 +//import javax.servlet.http.HttpServletRequest;
  5 +//
  6 +//import org.aspectj.lang.ProceedingJoinPoint;
  7 +//import org.aspectj.lang.annotation.Around;
  8 +//import org.aspectj.lang.annotation.Aspect;
  9 +//import org.aspectj.lang.annotation.Pointcut;
  10 +//import org.springframework.context.annotation.Configuration;
  11 +//import org.springframework.web.context.request.RequestAttributes;
  12 +//import org.springframework.web.context.request.RequestContextHolder;
  13 +//import org.springframework.web.context.request.ServletRequestAttributes;
  14 +//import org.slf4j.Logger;
  15 +//import org.slf4j.LoggerFactory;;
  16 +//
  17 +//
  18 +//// 暂时注释掉,提高系统性能
  19 +////@Aspect //定义一个切面
  20 +////@Configuration
  21 +//public class LogRecordAspect {
  22 +//private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);
  23 +//
  24 +// // 定义切点Pointcut
  25 +// @Pointcut("execution(public * org.jeecg.modules.*.*.*Controller.*(..))")
  26 +// public void excudeService() {
  27 +// }
  28 +//
  29 +// @Around("excudeService()")
  30 +// public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
  31 +// RequestAttributes ra = RequestContextHolder.getRequestAttributes();
  32 +// ServletRequestAttributes sra = (ServletRequestAttributes) ra;
  33 +// HttpServletRequest request = sra.getRequest();
  34 +//
  35 +// String url = request.getRequestURL().toString();
  36 +// String method = request.getMethod();
  37 +// String uri = request.getRequestURI();
  38 +// String queryString = request.getQueryString();
  39 +// logger.info("请求开始, 各个参数, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);
  40 +//
  41 +// // result的值就是被拦截方法的返回值
  42 +// Object result = pjp.proceed();
  43 +//
  44 +// logger.info("请求结束,controller的返回值是 " + result);
  45 +// return result;
  46 +// }
  47 +//}
48 48 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/ngalain/controller/NgAlainController.java
1   -package org.jeecg.modules.ngalain.controller;
2   -
3   -import java.util.ArrayList;
4   -import java.util.List;
5   -import java.util.Map;
6   -
7   -import javax.servlet.http.HttpServletRequest;
8   -
9   -import org.apache.shiro.SecurityUtils;
10   -import org.jeecg.common.api.vo.Result;
11   -import org.jeecg.common.system.vo.DictModel;
12   -import org.jeecg.common.system.vo.LoginUser;
13   -import org.jeecg.modules.ngalain.service.NgAlainService;
14   -import org.jeecg.modules.system.service.ISysDictService;
15   -import org.springframework.beans.factory.annotation.Autowired;
16   -import org.springframework.web.bind.annotation.PathVariable;
17   -import org.springframework.web.bind.annotation.RequestMapping;
18   -import org.springframework.web.bind.annotation.RequestMethod;
19   -import org.springframework.web.bind.annotation.ResponseBody;
20   -import org.springframework.web.bind.annotation.RestController;
21   -
22   -import com.alibaba.fastjson.JSONObject;
23   -
24   -import lombok.extern.slf4j.Slf4j;
25   -
26   -@Slf4j
27   -@RestController
28   -@RequestMapping("/sys/ng-alain")
29   -public class NgAlainController {
30   - @Autowired
31   - private NgAlainService ngAlainService;
32   - @Autowired
33   - private ISysDictService sysDictService;
34   -
35   - @RequestMapping(value = "/getAppData")
36   - @ResponseBody
37   - public JSONObject getAppData(HttpServletRequest request) throws Exception {
38   - String token=request.getHeader("X-Access-Token");
39   - JSONObject j = new JSONObject();
40   - LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
41   - JSONObject userObjcet = new JSONObject();
42   - userObjcet.put("name", user.getUsername());
43   - userObjcet.put("avatar", user.getAvatar());
44   - userObjcet.put("email", user.getEmail());
45   - userObjcet.put("token", token);
46   - j.put("user", userObjcet);
47   - j.put("menu",ngAlainService.getMenu(user.getUsername()));
48   - JSONObject app = new JSONObject();
49   - app.put("name", "jeecg-boot-angular");
50   - app.put("description", "jeecg+ng-alain整合版本");
51   - j.put("app", app);
52   - return j;
53   - }
54   -
55   - @RequestMapping(value = "/getDictItems/{dictCode}", method = RequestMethod.GET)
56   - public Object getDictItems(@PathVariable String dictCode) {
57   - log.info(" dictCode : "+ dictCode);
58   - Result<List<DictModel>> result = new Result<List<DictModel>>();
59   - List<DictModel> ls = null;
60   - try {
61   - ls = sysDictService.queryDictItemsByCode(dictCode);
62   - result.setSuccess(true);
63   - result.setResult(ls);
64   - } catch (Exception e) {
65   - log.error(e.getMessage(),e);
66   - result.error500("操作失败");
67   - return result;
68   - }
69   - List<JSONObject> dictlist=new ArrayList<>();
70   - for (DictModel l : ls) {
71   - JSONObject dict=new JSONObject();
72   - try {
73   - dict.put("value",Integer.parseInt(l.getValue()));
74   - } catch (NumberFormatException e) {
75   - dict.put("value",l.getValue());
76   - }
77   - dict.put("label",l.getText());
78   - dictlist.add(dict);
79   - }
80   - return dictlist;
81   - }
82   - @RequestMapping(value = "/getDictItemsByTable/{table}/{key}/{value}", method = RequestMethod.GET)
83   - public Object getDictItemsByTable(@PathVariable String table,@PathVariable String key,@PathVariable String value) {
84   - return this.ngAlainService.getDictByTable(table,key,value);
85   - }
86   -}
  1 +//package org.jeecg.modules.ngalain.controller;
  2 +//
  3 +//import java.util.ArrayList;
  4 +//import java.util.List;
  5 +//import java.util.Map;
  6 +//
  7 +//import javax.servlet.http.HttpServletRequest;
  8 +//
  9 +//import org.apache.shiro.SecurityUtils;
  10 +//import org.jeecg.common.api.vo.Result;
  11 +//import org.jeecg.common.system.vo.DictModel;
  12 +//import org.jeecg.common.system.vo.LoginUser;
  13 +//import org.jeecg.modules.ngalain.service.NgAlainService;
  14 +//import org.jeecg.modules.system.service.ISysDictService;
  15 +//import org.springframework.beans.factory.annotation.Autowired;
  16 +//import org.springframework.web.bind.annotation.PathVariable;
  17 +//import org.springframework.web.bind.annotation.RequestMapping;
  18 +//import org.springframework.web.bind.annotation.RequestMethod;
  19 +//import org.springframework.web.bind.annotation.ResponseBody;
  20 +//import org.springframework.web.bind.annotation.RestController;
  21 +//
  22 +//import com.alibaba.fastjson.JSONObject;
  23 +//
  24 +//import lombok.extern.slf4j.Slf4j;
  25 +//
  26 +//@Slf4j
  27 +//@RestController
  28 +//@RequestMapping("/sys/ng-alain")
  29 +//public class NgAlainController {
  30 +// @Autowired
  31 +// private NgAlainService ngAlainService;
  32 +// @Autowired
  33 +// private ISysDictService sysDictService;
  34 +//
  35 +// @RequestMapping(value = "/getAppData")
  36 +// @ResponseBody
  37 +// public JSONObject getAppData(HttpServletRequest request) throws Exception {
  38 +// String token=request.getHeader("X-Access-Token");
  39 +// JSONObject j = new JSONObject();
  40 +// LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
  41 +// JSONObject userObjcet = new JSONObject();
  42 +// userObjcet.put("name", user.getUsername());
  43 +// userObjcet.put("avatar", user.getAvatar());
  44 +// userObjcet.put("email", user.getEmail());
  45 +// userObjcet.put("token", token);
  46 +// j.put("user", userObjcet);
  47 +// j.put("menu",ngAlainService.getMenu(user.getUsername()));
  48 +// JSONObject app = new JSONObject();
  49 +// app.put("name", "jeecg-boot-angular");
  50 +// app.put("description", "jeecg+ng-alain整合版本");
  51 +// j.put("app", app);
  52 +// return j;
  53 +// }
  54 +//
  55 +// @RequestMapping(value = "/getDictItems/{dictCode}", method = RequestMethod.GET)
  56 +// public Object getDictItems(@PathVariable String dictCode) {
  57 +// log.info(" dictCode : "+ dictCode);
  58 +// Result<List<DictModel>> result = new Result<List<DictModel>>();
  59 +// List<DictModel> ls = null;
  60 +// try {
  61 +// ls = sysDictService.queryDictItemsByCode(dictCode);
  62 +// result.setSuccess(true);
  63 +// result.setResult(ls);
  64 +// } catch (Exception e) {
  65 +// log.error(e.getMessage(),e);
  66 +// result.error500("操作失败");
  67 +// return result;
  68 +// }
  69 +// List<JSONObject> dictlist=new ArrayList<>();
  70 +// for (DictModel l : ls) {
  71 +// JSONObject dict=new JSONObject();
  72 +// try {
  73 +// dict.put("value",Integer.parseInt(l.getValue()));
  74 +// } catch (NumberFormatException e) {
  75 +// dict.put("value",l.getValue());
  76 +// }
  77 +// dict.put("label",l.getText());
  78 +// dictlist.add(dict);
  79 +// }
  80 +// return dictlist;
  81 +// }
  82 +// @RequestMapping(value = "/getDictItemsByTable/{table}/{key}/{value}", method = RequestMethod.GET)
  83 +// public Object getDictItemsByTable(@PathVariable String table,@PathVariable String key,@PathVariable String value) {
  84 +// return this.ngAlainService.getDictByTable(table,key,value);
  85 +// }
  86 +//}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/controller/QuartzJobController.java
... ... @@ -6,10 +6,12 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
6 6 import io.swagger.annotations.Api;
7 7 import io.swagger.annotations.ApiOperation;
8 8 import lombok.extern.slf4j.Slf4j;
  9 +import org.apache.shiro.SecurityUtils;
9 10 import org.apache.shiro.authz.annotation.RequiresRoles;
10 11 import org.jeecg.common.api.vo.Result;
11 12 import org.jeecg.common.constant.CommonConstant;
12 13 import org.jeecg.common.system.query.QueryGenerator;
  14 +import org.jeecg.common.system.vo.LoginUser;
13 15 import org.jeecg.common.util.ImportExcelUtil;
14 16 import org.jeecg.modules.quartz.entity.QuartzJob;
15 17 import org.jeecg.modules.quartz.service.IQuartzJobService;
... ... @@ -145,14 +147,14 @@ public class QuartzJobController {
145 147 */
146 148 //@RequiresRoles("admin")
147 149 @GetMapping(value = "/pause")
148   - @ApiOperation(value = "暂停定时任务")
  150 + @ApiOperation(value = "停止定时任务")
149 151 public Result<Object> pauseJob(@RequestParam(name = "id") String id) {
150 152 QuartzJob job = quartzJobService.getById(id);
151 153 if (job == null) {
152 154 return Result.error("定时任务不存在!");
153 155 }
154 156 quartzJobService.pause(job);
155   - return Result.ok("暂停定时任务成功");
  157 + return Result.ok("停止定时任务成功");
156 158 }
157 159  
158 160 /**
... ... @@ -163,7 +165,7 @@ public class QuartzJobController {
163 165 */
164 166 //@RequiresRoles("admin")
165 167 @GetMapping(value = "/resume")
166   - @ApiOperation(value = "恢复定时任务")
  168 + @ApiOperation(value = "启动定时任务")
167 169 public Result<Object> resumeJob(@RequestParam(name = "id") String id) {
168 170 QuartzJob job = quartzJobService.getById(id);
169 171 if (job == null) {
... ... @@ -171,7 +173,7 @@ public class QuartzJobController {
171 173 }
172 174 quartzJobService.resumeJob(job);
173 175 //scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim()));
174   - return Result.ok("恢复定时任务成功");
  176 + return Result.ok("启动定时任务成功");
175 177 }
176 178  
177 179 /**
... ... @@ -202,8 +204,12 @@ public class QuartzJobController {
202 204 // 导出文件名称
203 205 mv.addObject(NormalExcelConstants.FILE_NAME, "定时任务列表");
204 206 mv.addObject(NormalExcelConstants.CLASS, QuartzJob.class);
205   - mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("定时任务列表数据", "导出人:Jeecg", "导出信息"));
206   - mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
  207 + //获取当前登录用户
  208 + //update-begin---author:wangshuai ---date:20211227 for:[JTC-116]导出人写死了------------
  209 + LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
  210 + mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("定时任务列表数据", "导出人:"+user.getRealname(), "导出信息"));
  211 + //update-end---author:wangshuai ---date:20211227 for:[JTC-116]导出人写死了------------
  212 + mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
207 213 return mv;
208 214 }
209 215  
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/quartz/job/SampleJob.java
... ... @@ -17,7 +17,7 @@ public class SampleJob implements Job {
17 17  
18 18 @Override
19 19 public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
20   -
  20 + log.info(" Job Execution key:"+jobExecutionContext.getJobDetail().getKey());
21 21 log.info(String.format(" Jeecg-Boot 普通定时任务 SampleJob ! 时间:" + DateUtils.getTimestamp()));
22 22 }
23 23 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/DuplicateCheckController.java
... ... @@ -47,6 +47,15 @@ public class DuplicateCheckController {
47 47 //SQL注入校验(只限制非法串改数据库)
48 48 final String[] sqlInjCheck = {duplicateCheckVo.getTableName(),duplicateCheckVo.getFieldName()};
49 49 SqlInjectionUtil.filterContent(sqlInjCheck);
  50 + // update-begin-author:taoyan date:20211227 for: JTC-25 【online报表】oracle 操作问题 录入弹框啥都不填直接保存 ①编码不是应该提示必填么?②报错也应该是具体文字提示,不是后台错误日志
  51 + if(StringUtils.isEmpty(duplicateCheckVo.getFieldVal())){
  52 + Result rs = new Result();
  53 + rs.setCode(500);
  54 + rs.setSuccess(true);
  55 + rs.setMessage("数据为空,不作处理!");
  56 + return rs;
  57 + }
  58 + // update-end-author:taoyan date:20211227 for: JTC-25 【online报表】oracle 操作问题 录入弹框啥都不填直接保存 ①编码不是应该提示必填么?②报错也应该是具体文字提示,不是后台错误日志
50 59 if (StringUtils.isNotBlank(duplicateCheckVo.getDataId())) {
51 60 // [2].编辑页面校验
52 61 num = sysDictMapper.duplicateCheckCountSql(duplicateCheckVo);
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/LoginController.java
... ... @@ -4,6 +4,7 @@ import cn.hutool.core.util.RandomUtil;
4 4 import com.alibaba.fastjson.JSONObject;
5 5 import com.aliyuncs.exceptions.ClientException;
6 6 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  7 +import com.baomidou.mybatisplus.core.toolkit.IdWorker;
7 8 import io.swagger.annotations.Api;
8 9 import io.swagger.annotations.ApiOperation;
9 10 import lombok.extern.slf4j.Slf4j;
... ... @@ -58,8 +59,6 @@ public class LoginController {
58 59 @Resource
59 60 private BaseCommonService baseCommonService;
60 61  
61   - private static final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";
62   -
63 62 @ApiOperation("登录接口")
64 63 @RequestMapping(value = "/login", method = RequestMethod.POST)
65 64 public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel){
... ... @@ -82,6 +81,7 @@ public class LoginController {
82 81 Object checkCode = redisUtil.get(realKey);
83 82 //当进入登录页时,有一定几率出现验证码错误 #1714
84 83 if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
  84 + log.warn("验证码错误,key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode);
85 85 result.error500("验证码错误");
86 86 return result;
87 87 }
... ... @@ -402,7 +402,10 @@ public class LoginController {
402 402 // update-begin--Author:sunjianlei Date:20210802 for:获取用户租户信息
403 403 String tenantIds = sysUser.getRelTenantIds();
404 404 if (oConvertUtils.isNotEmpty(tenantIds)) {
405   - List<String> tenantIdList = Arrays.asList(tenantIds.split(","));
  405 + List<Integer> tenantIdList = new ArrayList<>();
  406 + for(String id: tenantIds.split(",")){
  407 + tenantIdList.add(Integer.valueOf(id));
  408 + }
406 409 // 该方法仅查询有效的租户,如果返回0个就说明所有的租户均无效。
407 410 List<SysTenant> tenantList = sysTenantService.queryEffectiveTenant(tenantIdList);
408 411 if (tenantList.size() == 0) {
... ... @@ -450,10 +453,17 @@ public class LoginController {
450 453 public Result<String> randomImage(HttpServletResponse response,@PathVariable String key){
451 454 Result<String> res = new Result<String>();
452 455 try {
  456 + //生成验证码
  457 + final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";
453 458 String code = RandomUtil.randomString(BASE_CHECK_CODES,4);
  459 +
  460 + //存到redis中
454 461 String lowerCaseCode = code.toLowerCase();
455 462 String realKey = MD5Util.MD5Encode(lowerCaseCode+key, "utf-8");
  463 + log.info("获取验证码,Redis checkCode = {},key = {}", code, key);
456 464 redisUtil.set(realKey, lowerCaseCode, 60);
  465 +
  466 + //返回前端
457 467 String base64 = RandImageUtil.generate(code);
458 468 res.setSuccess(true);
459 469 res.setResult(base64);
... ... @@ -495,13 +505,16 @@ public class LoginController {
495 505 if(oConvertUtils.isEmpty(orgCode)) {
496 506 //如果当前用户无选择部门 查看部门关联信息
497 507 List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
  508 + //update-begin-author:taoyan date:20220117 for: JTC-1068【app】新建用户,没有设置部门及角色,点击登录提示暂未归属部,一直在登录页面 使用手机号登录 可正常
498 509 if (departs == null || departs.size() == 0) {
499   - result.error500("用户暂未归属部门,不可登录!");
500   - return result;
  510 + /*result.error500("用户暂未归属部门,不可登录!");
  511 + return result;*/
  512 + }else{
  513 + orgCode = departs.get(0).getOrgCode();
  514 + sysUser.setOrgCode(orgCode);
  515 + this.sysUserService.updateUserDepart(username, orgCode);
501 516 }
502   - orgCode = departs.get(0).getOrgCode();
503   - sysUser.setOrgCode(orgCode);
504   - this.sysUserService.updateUserDepart(username, orgCode);
  517 + //update-end-author:taoyan date:20220117 for: JTC-1068【app】新建用户,没有设置部门及角色,点击登录提示暂未归属部,一直在登录页面 使用手机号登录 可正常
505 518 }
506 519 JSONObject obj = new JSONObject();
507 520 //用户登录信息
... ... @@ -542,5 +555,58 @@ public class LoginController {
542 555 }
543 556 return Result.ok();
544 557 }
  558 + /**
  559 + * 登录二维码
  560 + */
  561 + @ApiOperation(value = "登录二维码", notes = "登录二维码")
  562 + @GetMapping("/getLoginQrcode")
  563 + public Result<?> getLoginQrcode() {
  564 + String qrcodeId = CommonConstant.LOGIN_QRCODE_PRE+IdWorker.getIdStr();
  565 + //定义二维码参数
  566 + Map params = new HashMap(5);
  567 + params.put("qrcodeId", qrcodeId);
  568 + //存放二维码唯一标识30秒有效
  569 + redisUtil.set(CommonConstant.LOGIN_QRCODE + qrcodeId, qrcodeId, 30);
  570 + return Result.OK(params);
  571 + }
  572 + /**
  573 + * 扫码二维码
  574 + */
  575 + @ApiOperation(value = "扫码登录二维码", notes = "扫码登录二维码")
  576 + @PostMapping("/scanLoginQrcode")
  577 + public Result<?> scanLoginQrcode(@RequestParam String qrcodeId, @RequestParam String token) {
  578 + Object check = redisUtil.get(CommonConstant.LOGIN_QRCODE + qrcodeId);
  579 + if (oConvertUtils.isNotEmpty(check)) {
  580 + //存放token给前台读取
  581 + redisUtil.set(CommonConstant.LOGIN_QRCODE_TOKEN+qrcodeId, token, 60);
  582 + } else {
  583 + return Result.error("二维码已过期,请刷新后重试");
  584 + }
  585 + return Result.OK("扫码成功");
  586 + }
  587 +
  588 +
  589 + /**
  590 + * 获取用户扫码后保存的token
  591 + */
  592 + @ApiOperation(value = "获取用户扫码后保存的token", notes = "获取用户扫码后保存的token")
  593 + @GetMapping("/getQrcodeToken")
  594 + public Result getQrcodeToken(@RequestParam String qrcodeId) {
  595 + Object token = redisUtil.get(CommonConstant.LOGIN_QRCODE_TOKEN + qrcodeId);
  596 + Map result = new HashMap();
  597 + Object qrcodeIdExpire = redisUtil.get(CommonConstant.LOGIN_QRCODE + qrcodeId);
  598 + if (oConvertUtils.isEmpty(qrcodeIdExpire)) {
  599 + //二维码过期通知前台刷新
  600 + result.put("token", "-2");
  601 + return Result.OK(result);
  602 + }
  603 + if (oConvertUtils.isNotEmpty(token)) {
  604 + result.put("success", true);
  605 + result.put("token", token);
  606 + } else {
  607 + result.put("token", "-1");
  608 + }
  609 + return Result.OK(result);
  610 + }
545 611  
546 612 }
547 613 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/MockVue3Controller.java deleted
1   -package org.jeecg.modules.system.controller;
2   -
3   -import lombok.extern.slf4j.Slf4j;
4   -import org.jeecg.common.api.vo.Result;
5   -import org.jeecg.modules.system.entity.SysUser;
6   -import org.springframework.web.bind.annotation.GetMapping;
7   -import org.springframework.web.bind.annotation.RequestMapping;
8   -import org.springframework.web.bind.annotation.RestController;
9   -
10   -/**
11   - * vue3前端临时接口
12   - */
13   -@RestController
14   -@RequestMapping("/")
15   -@Slf4j
16   -public class MockVue3Controller {
17   -
18   -
19   -}
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysAnnouncementController.java
... ... @@ -14,6 +14,7 @@ import org.jeecg.common.constant.CommonConstant;
14 14 import org.jeecg.common.constant.CommonSendStatus;
15 15 import org.jeecg.common.constant.WebsocketConst;
16 16 import org.jeecg.common.system.api.ISysBaseAPI;
  17 +import org.jeecg.common.system.query.QueryGenerator;
17 18 import org.jeecg.common.system.util.JwtUtil;
18 19 import org.jeecg.common.system.vo.LoginUser;
19 20 import org.jeecg.common.util.RedisUtil;
... ... @@ -93,18 +94,21 @@ public class SysAnnouncementController {
93 94 HttpServletRequest req) {
94 95 Result<IPage<SysAnnouncement>> result = new Result<IPage<SysAnnouncement>>();
95 96 sysAnnouncement.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
96   - QueryWrapper<SysAnnouncement> queryWrapper = new QueryWrapper<SysAnnouncement>(sysAnnouncement);
  97 + QueryWrapper<SysAnnouncement> queryWrapper = QueryGenerator.initQueryWrapper(sysAnnouncement, req.getParameterMap());
97 98 Page<SysAnnouncement> page = new Page<SysAnnouncement>(pageNo,pageSize);
  99 +
  100 + //update-begin-author:lvdandan date:20211229 for: sqlserver mssql-jdbc 8.2.2.jre8版本下系统公告列表查询报错 查询SQL中生成了两个create_time DESC;故注释此段代码
98 101 //排序逻辑 处理
99   - String column = req.getParameter("column");
100   - String order = req.getParameter("order");
101   - if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
102   - if("asc".equals(order)) {
103   - queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
104   - }else {
105   - queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
106   - }
107   - }
  102 +// String column = req.getParameter("column");
  103 +// String order = req.getParameter("order");
  104 +// if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
  105 +// if("asc".equals(order)) {
  106 +// queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
  107 +// }else {
  108 +// queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
  109 +// }
  110 +// }
  111 + //update-end-author:lvdandan date:20211229 for: sqlserver mssql-jdbc 8.2.2.jre8版本下系统公告列表查询报错 查询SQL中生成了两个create_time DESC;故注释此段代码
108 112 IPage<SysAnnouncement> pageList = sysAnnouncementService.page(page, queryWrapper);
109 113 result.setSuccess(true);
110 114 result.setResult(pageList);
... ... @@ -337,11 +341,13 @@ public class SysAnnouncementController {
337 341 query.eq(SysAnnouncementSend::getUserId,userId);
338 342 SysAnnouncementSend one = sysAnnouncementSendService.getOne(query);
339 343 if(null==one){
340   - SysAnnouncementSend announcementSend = new SysAnnouncementSend();
341   - announcementSend.setAnntId(announcements.get(i).getId());
342   - announcementSend.setUserId(userId);
343   - announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
344   - sysAnnouncementSendService.save(announcementSend);
  344 + log.info("listByUser接口新增了SysAnnouncementSend:pageSize{}:"+pageSize);
  345 + SysAnnouncementSend announcementSend = new SysAnnouncementSend();
  346 + announcementSend.setAnntId(announcements.get(i).getId());
  347 + announcementSend.setUserId(userId);
  348 + announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
  349 + sysAnnouncementSendService.save(announcementSend);
  350 + log.info("announcementSend.toString()",announcementSend.toString());
345 351 }
346 352 //update-end--Author:wangshuai Date:20200803 for: 通知公告消息重复LOWCOD-759------------
347 353 }
... ... @@ -373,7 +379,7 @@ public class SysAnnouncementController {
373 379 LambdaQueryWrapper<SysAnnouncement> queryWrapper = new LambdaQueryWrapper<SysAnnouncement>(sysAnnouncement);
374 380 //Step.2 AutoPoi 导出Excel
375 381 ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
376   - queryWrapper.eq(SysAnnouncement::getDelFlag,CommonConstant.DEL_FLAG_0);
  382 + queryWrapper.eq(SysAnnouncement::getDelFlag,CommonConstant.DEL_FLAG_0.toString());
377 383 List<SysAnnouncement> pageList = sysAnnouncementService.list(queryWrapper);
378 384 //导出文件名称
379 385 mv.addObject(NormalExcelConstants.FILE_NAME, "系统通告列表");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysAnnouncementSendController.java
... ... @@ -10,6 +10,7 @@ import org.jeecg.common.api.vo.Result;
10 10 import org.jeecg.common.constant.CommonConstant;
11 11 import org.jeecg.common.constant.WebsocketConst;
12 12 import org.jeecg.common.system.vo.LoginUser;
  13 +import org.jeecg.common.util.SqlInjectionUtil;
13 14 import org.jeecg.common.util.oConvertUtils;
14 15 import org.jeecg.modules.message.websocket.WebSocket;
15 16 import org.jeecg.modules.system.entity.SysAnnouncementSend;
... ... @@ -69,6 +70,11 @@ public class SysAnnouncementSendController {
69 70 //排序逻辑 处理
70 71 String column = req.getParameter("column");
71 72 String order = req.getParameter("order");
  73 +
  74 + //issues/3331 SQL injection vulnerability
  75 + SqlInjectionUtil.filterContent(column);
  76 + SqlInjectionUtil.filterContent(order);
  77 +
72 78 if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
73 79 if("asc".equals(order)) {
74 80 queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysCategoryController.java
... ... @@ -9,9 +9,11 @@ import lombok.extern.slf4j.Slf4j;
9 9 import org.apache.commons.lang.StringUtils;
10 10 import org.apache.shiro.SecurityUtils;
11 11 import org.jeecg.common.api.vo.Result;
  12 +import org.jeecg.common.constant.CommonConstant;
12 13 import org.jeecg.common.system.query.QueryGenerator;
13 14 import org.jeecg.common.system.vo.DictModel;
14 15 import org.jeecg.common.system.vo.LoginUser;
  16 +import org.jeecg.common.util.ImportExcelUtil;
15 17 import org.jeecg.common.util.oConvertUtils;
16 18 import org.jeecg.modules.system.entity.SysCategory;
17 19 import org.jeecg.modules.system.model.TreeSelectModel;
... ... @@ -65,10 +67,16 @@ public class SysCategoryController {
65 67 Result<IPage<SysCategory>> result = new Result<IPage<SysCategory>>();
66 68  
67 69 //--author:os_chengtgen---date:20190804 -----for: 分类字典页面显示错误,issues:377--------start
68   - //QueryWrapper<SysCategory> queryWrapper = QueryGenerator.initQueryWrapper(sysCategory, req.getParameterMap());
69   - QueryWrapper<SysCategory> queryWrapper = new QueryWrapper<SysCategory>();
70   - queryWrapper.eq("pid", sysCategory.getPid());
71   - //--author:os_chengtgen---date:20190804 -----for: 分类字典页面显示错误,issues:377--------end
  70 + //--author:liusq---date:20211119 -----for: 【vue3】分类字典页面查询条件配置--------start
  71 + QueryWrapper<SysCategory> queryWrapper = QueryGenerator.initQueryWrapper(sysCategory, req.getParameterMap());
  72 + String name = sysCategory.getName();
  73 + String code = sysCategory.getCode();
  74 + //QueryWrapper<SysCategory> queryWrapper = new QueryWrapper<SysCategory>();
  75 + if(StringUtils.isBlank(name)&&StringUtils.isBlank(code)){
  76 + queryWrapper.eq("pid", sysCategory.getPid());
  77 + }
  78 + //--author:liusq---date:20211119 -----for: 分类字典页面查询条件配置--------end
  79 + //--author:os_chengtgen---date:20190804 -----for:【vue3】 分类字典页面显示错误,issues:377--------end
72 80  
73 81 Page<SysCategory> page = new Page<SysCategory>(pageNo, pageSize);
74 82 IPage<SysCategory> pageList = sysCategoryService.page(page, queryWrapper);
... ... @@ -215,10 +223,13 @@ public class SysCategoryController {
215 223 * @return
216 224 */
217 225 @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
218   - public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
  226 + public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) throws IOException{
219 227 MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
220 228 Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
221   - for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
  229 + // 错误信息
  230 + List<String> errorMessage = new ArrayList<>();
  231 + int successLines = 0, errorLines = 0;
  232 + for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
222 233 MultipartFile file = entity.getValue();// 获取上传文件对象
223 234 ImportParams params = new ImportParams();
224 235 params.setTitleRows(2);
... ... @@ -229,7 +240,8 @@ public class SysCategoryController {
229 240 //按照编码长度排序
230 241 Collections.sort(listSysCategorys);
231 242 log.info("排序后的list====>",listSysCategorys);
232   - for (SysCategory sysCategoryExcel : listSysCategorys) {
  243 + for (int i = 0; i < listSysCategorys.size(); i++) {
  244 + SysCategory sysCategoryExcel = listSysCategorys.get(i);
233 245 String code = sysCategoryExcel.getCode();
234 246 if(code.length()>3){
235 247 String pCode = sysCategoryExcel.getCode().substring(0,code.length()-3);
... ... @@ -242,12 +254,25 @@ public class SysCategoryController {
242 254 }else{
243 255 sysCategoryExcel.setPid("0");
244 256 }
245   - sysCategoryService.save(sysCategoryExcel);
  257 + try {
  258 + sysCategoryService.save(sysCategoryExcel);
  259 + successLines++;
  260 + } catch (Exception e) {
  261 + errorLines++;
  262 + String message = e.getMessage().toLowerCase();
  263 + int lineNumber = i + 1;
  264 + // 通过索引名判断出错信息
  265 + if (message.contains(CommonConstant.SQL_INDEX_UNIQ_CATEGORY_CODE)) {
  266 + errorMessage.add("第 " + lineNumber + " 行:分类编码已经存在,忽略导入。");
  267 + } else {
  268 + errorMessage.add("第 " + lineNumber + " 行:未知错误,忽略导入");
  269 + log.error(e.getMessage(), e);
  270 + }
  271 + }
246 272 }
247   - return Result.ok("文件导入成功!数据行数:" + listSysCategorys.size());
248 273 } catch (Exception e) {
249   - log.error(e.getMessage(), e);
250   - return Result.error("文件导入失败:"+e.getMessage());
  274 + errorMessage.add("发生异常:" + e.getMessage());
  275 + log.error(e.getMessage(), e);
251 276 } finally {
252 277 try {
253 278 file.getInputStream().close();
... ... @@ -256,7 +281,7 @@ public class SysCategoryController {
256 281 }
257 282 }
258 283 }
259   - return Result.error("文件导入失败!");
  284 + return ImportExcelUtil.imporReturnRes(errorLines,successLines,errorMessage);
260 285 }
261 286  
262 287  
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysCheckRuleController.java
... ... @@ -112,7 +112,7 @@ public class SysCheckRuleController extends JeecgController&lt;SysCheckRule, ISysCh
112 112 */
113 113 @AutoLog(value = "编码校验规则-编辑")
114 114 @ApiOperation(value = "编码校验规则-编辑", notes = "编码校验规则-编辑")
115   - @PutMapping(value = "/edit")
  115 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
116 116 public Result edit(@RequestBody SysCheckRule sysCheckRule) {
117 117 sysCheckRuleService.updateById(sysCheckRule);
118 118 return Result.ok("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDataSourceController.java
... ... @@ -14,6 +14,7 @@ import io.swagger.annotations.Api;
14 14 import io.swagger.annotations.ApiOperation;
15 15 import lombok.extern.slf4j.Slf4j;
16 16 import org.apache.commons.lang.StringUtils;
  17 +import org.apache.shiro.authz.annotation.RequiresRoles;
17 18 import org.jeecg.common.api.vo.Result;
18 19 import org.jeecg.common.aspect.annotation.AutoLog;
19 20 import org.jeecg.common.system.base.controller.JeecgController;
... ... @@ -57,6 +58,7 @@ public class SysDataSourceController extends JeecgController&lt;SysDataSource, ISys
57 58 */
58 59 @AutoLog(value = "多数据源管理-分页列表查询")
59 60 @ApiOperation(value = "多数据源管理-分页列表查询", notes = "多数据源管理-分页列表查询")
  61 + //@RequiresRoles("admin")
60 62 @GetMapping(value = "/list")
61 63 public Result<?> queryPageList(
62 64 SysDataSource sysDataSource,
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDepartController.java
... ... @@ -198,7 +198,7 @@ public class SysDepartController {
198 198 * @return
199 199 */
200 200 //@RequiresRoles({"admin"})
201   - @RequestMapping(value = "/edit", method = RequestMethod.PUT)
  201 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
202 202 @CacheEvict(value= {CacheConstant.SYS_DEPARTS_CACHE,CacheConstant.SYS_DEPART_IDS_CACHE}, allEntries=true)
203 203 public Result<SysDepart> edit(@RequestBody SysDepart sysDepart, HttpServletRequest request) {
204 204 String username = JwtUtil.getUserNameByToken(request);
... ... @@ -322,7 +322,7 @@ public class SysDepartController {
322 322 if(oConvertUtils.isNotEmpty(user.getUserIdentity()) && user.getUserIdentity().equals( CommonConstant.USER_IDENTITY_2 )){
323 323 departIds = user.getDepartIds();
324 324 }
325   - List<SysDepartTreeModel> treeList = this.sysDepartService.searhBy(keyWord,myDeptSearch,departIds);
  325 + List<SysDepartTreeModel> treeList = this.sysDepartService.searchByKeyWord(keyWord,myDeptSearch,departIds);
326 326 if (treeList == null || treeList.size() == 0) {
327 327 result.setSuccess(false);
328 328 result.setMessage("未查询匹配数据!");
... ... @@ -363,6 +363,8 @@ public class SysDepartController {
363 363  
364 364 /**
365 365 * 通过excel导入数据
  366 + * 部门导入方案1: 通过机构编码来计算出部门的父级ID,维护上下级关系;
  367 + * 部门导入方案2: 你也可以改造下程序,机构编码直接导入,先不设置父ID;全部导入后,写一个sql,补下父ID;
366 368 *
367 369 * @param request
368 370 * @param response
... ... @@ -418,6 +420,11 @@ public class SysDepartController {
418 420 sysDepart.setOrgType(sysDepart.getOrgCode().length()/codeLength+"");
419 421 //update-end---author:liusq Date:20210223 for:批量导入部门以后,不能追加下一级部门 #2245------------
420 422 sysDepart.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
  423 + //update-begin---author:wangshuai ---date:20220105 for:[JTC-363]部门导入 机构类别没有时导入失败,赋默认值------------
  424 + if(oConvertUtils.isEmpty(sysDepart.getOrgCategory())){
  425 + sysDepart.setOrgCategory("1");
  426 + }
  427 + //update-end---author:wangshuai ---date:20220105 for:[JTC-363]部门导入 机构类别没有时导入失败,赋默认值------------
421 428 ImportExcelUtil.importDateSaveOne(sysDepart, ISysDepartService.class, errorMessageList, num, CommonConstant.SQL_INDEX_UNIQ_DEPART_ORG_CODE);
422 429 num++;
423 430 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDepartPermissionController.java
... ... @@ -97,7 +97,7 @@ public class SysDepartPermissionController extends JeecgController&lt;SysDepartPerm
97 97 * @return
98 98 */
99 99 @ApiOperation(value="部门权限表-编辑", notes="部门权限表-编辑")
100   - @PutMapping(value = "/edit")
  100 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
101 101 public Result<?> edit(@RequestBody SysDepartPermission sysDepartPermission) {
102 102 sysDepartPermissionService.updateById(sysDepartPermission);
103 103 return Result.ok("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDepartRoleController.java
... ... @@ -114,7 +114,7 @@ public class SysDepartRoleController extends JeecgController&lt;SysDepartRole, ISys
114 114 */
115 115 //@RequiresRoles({"admin"})
116 116 @ApiOperation(value="部门角色-编辑", notes="部门角色-编辑")
117   - @PutMapping(value = "/edit")
  117 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
118 118 public Result<?> edit(@RequestBody SysDepartRole sysDepartRole) {
119 119 sysDepartRoleService.updateById(sysDepartRole);
120 120 return Result.ok("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDictController.java
... ... @@ -496,7 +496,7 @@ public class SysDictController {
496 496 try {
497 497 //导入Excel格式校验,看匹配的字段文本概率
498 498 Boolean t = ExcelImportCheckUtil.check(file.getInputStream(), SysDictPage.class, params);
499   - if(!t){
  499 + if(t!=null && !t){
500 500 throw new RuntimeException("导入Excel校验失败 !");
501 501 }
502 502 List<SysDictPage> list = ExcelImportUtil.importExcel(file.getInputStream(), SysDictPage.class, params);
... ... @@ -511,11 +511,22 @@ public class SysDictController {
511 511 Integer integer = sysDictService.saveMain(po, list.get(i).getSysDictItemList());
512 512 if(integer>0){
513 513 successLines++;
514   - }else{
  514 + //update-begin---author:wangshuai ---date:20220211 for:[JTC-1168]如果字典项值为空,则字典项忽略导入------------
  515 + }else if(integer == -1){
  516 + errorLines++;
  517 + errorMessage.add("字典名称:" + po.getDictName() + ",对应字典列表的字典项值不能为空,忽略导入。");
  518 + }else{
  519 + //update-end---author:wangshuai ---date:20220211 for:[JTC-1168]如果字典项值为空,则字典项忽略导入------------
515 520 errorLines++;
516 521 int lineNumber = i + 1;
517   - errorMessage.add("第 " + lineNumber + " 行:字典编码已经存在,忽略导入。");
518   - }
  522 + //update-begin---author:wangshuai ---date:20220209 for:[JTC-1168]字典编号不能为空------------
  523 + if(oConvertUtils.isEmpty(po.getDictCode())){
  524 + errorMessage.add("第 " + lineNumber + " 行:字典编码不能为空,忽略导入。");
  525 + }else{
  526 + errorMessage.add("第 " + lineNumber + " 行:字典编码已经存在,忽略导入。");
  527 + }
  528 + //update-end---author:wangshuai ---date:20220209 for:[JTC-1168]字典编号不能为空------------
  529 + }
519 530 } catch (Exception e) {
520 531 errorLines++;
521 532 int lineNumber = i + 1;
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysFillRuleController.java
... ... @@ -93,7 +93,7 @@ public class SysFillRuleController extends JeecgController&lt;SysFillRule, ISysFill
93 93 */
94 94 @AutoLog(value = "填值规则-编辑")
95 95 @ApiOperation(value = "填值规则-编辑", notes = "填值规则-编辑")
96   - @PutMapping(value = "/edit")
  96 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
97 97 public Result<?> edit(@RequestBody SysFillRule sysFillRule) {
98 98 sysFillRuleService.updateById(sysFillRule);
99 99 return Result.ok("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysPermissionController.java
... ... @@ -59,7 +59,7 @@ public class SysPermissionController {
59 59  
60 60 /**
61 61 * 加载数据节点
62   - *
  62 + *
63 63 * @return
64 64 */
65 65 @RequestMapping(value = "/list", method = RequestMethod.GET)
... ... @@ -200,7 +200,7 @@ public class SysPermissionController {
200 200  
201 201 /**
202 202 * 查询用户拥有的菜单权限和按钮权限
203   - *
  203 + *
204 204 * @return
205 205 */
206 206 @RequestMapping(value = "/getUserPermissionByToken", method = RequestMethod.GET)
... ... @@ -261,32 +261,49 @@ public class SysPermissionController {
261 261 }
262 262  
263 263 /**
264   - * 【vue3专用】查询用户拥有的按钮/表单访问权限
265   - * @return
  264 + * 【vue3专用】获取
  265 + * 1、查询用户拥有的按钮/表单访问权限
  266 + * 2、所有权限 (菜单权限配置)
  267 + * 3、系统安全模式 (开启则online报表的数据源必填)
266 268 */
267 269 @RequestMapping(value = "/getPermCode", method = RequestMethod.GET)
268 270 public Result<?> getPermCode() {
269   - Result<List<String>> result = new Result<List<String>>();
270 271 try {
271   - //直接获取当前用户
  272 + // 直接获取当前用户
272 273 LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
273   - //获取当前用户的权限集合
  274 + if (oConvertUtils.isEmpty(loginUser)) {
  275 + return Result.error("请登录系统!");
  276 + }
  277 + // 获取当前用户的权限集合
274 278 List<SysPermission> metaList = sysPermissionService.queryByUser(loginUser.getUsername());
  279 + // 按钮权限(用户拥有的权限集合)
  280 + List<String> codeList = metaList.stream()
  281 + .filter((permission) -> CommonConstant.MENU_TYPE_2.equals(permission.getMenuType()) && CommonConstant.STATUS_1.equals(permission.getStatus()))
  282 + .collect(ArrayList::new, (list, permission) -> list.add(permission.getPerms()), ArrayList::addAll);
  283 + //
  284 + JSONArray authArray = new JSONArray();
  285 + this.getAuthJsonArray(authArray, metaList);
  286 + // 查询所有的权限
  287 + LambdaQueryWrapper<SysPermission> query = new LambdaQueryWrapper<>();
  288 + query.eq(SysPermission::getDelFlag, CommonConstant.DEL_FLAG_0);
  289 + query.eq(SysPermission::getMenuType, CommonConstant.MENU_TYPE_2);
  290 + List<SysPermission> allAuthList = sysPermissionService.list(query);
  291 + JSONArray allAuthArray = new JSONArray();
  292 + this.getAllAuthJsonArray(allAuthArray, allAuthList);
  293 + JSONObject result = new JSONObject();
  294 + // 所拥有的权限编码
  295 + result.put("codeList", codeList);
275 296 //按钮权限(用户拥有的权限集合)
276   - List<String> authList = metaList.stream()
277   - .filter((permission) -> permission.getMenuType().equals(CommonConstant.MENU_TYPE_2)
278   - && CommonConstant.STATUS_1.equals(permission.getStatus())
279   - )
280   - .collect(() -> new ArrayList<String>(),
281   - (list, permission) -> list.add(permission.getPerms()),
282   - (list1, list2) -> list1.addAll(list2)
283   - );
284   - result.setResult(authList);
  297 + result.put("auth", authArray);
  298 + //全部权限配置集合(按钮权限,访问权限)
  299 + result.put("allAuth", allAuthArray);
  300 + // 系统安全模式
  301 + result.put("sysSafeMode", jeeccgBaseConfig.getSafeMode());
  302 + return Result.OK(result);
285 303 } catch (Exception e) {
286   - result.error500("查询失败:" + e.getMessage());
287 304 log.error(e.getMessage(), e);
  305 + return Result.error("查询失败:" + e.getMessage());
288 306 }
289   - return result;
290 307 }
291 308  
292 309 /**
... ... @@ -374,7 +391,7 @@ public class SysPermissionController {
374 391  
375 392 /**
376 393 * 获取全部的权限树
377   - *
  394 + *
378 395 * @return
379 396 */
380 397 @RequestMapping(value = "/queryTreeList", method = RequestMethod.GET)
... ... @@ -720,7 +737,7 @@ public class SysPermissionController {
720 737 /**
721 738 * 判断是否外网URL 例如: http://localhost:8080/jeecg-boot/swagger-ui.html#/ 支持特殊格式: {{
722 739 * window._CONFIG['domianURL'] }}/druid/ {{ JS代码片段 }},前台解析会自动执行JS代码片段
723   - *
  740 + *
724 741 * @return
725 742 */
726 743 private boolean isWWWHttpUrl(String url) {
... ... @@ -733,7 +750,7 @@ public class SysPermissionController {
733 750 /**
734 751 * 通过URL生成路由name(去掉URL前缀斜杠,替换内容中的斜杠‘/’为-) 举例: URL = /isystem/role RouteName =
735 752 * isystem-role
736   - *
  753 + *
737 754 * @return
738 755 */
739 756 private String urlToRouteName(String url) {
... ... @@ -753,7 +770,7 @@ public class SysPermissionController {
753 770  
754 771 /**
755 772 * 根据菜单id来获取其对应的权限数据
756   - *
  773 + *
757 774 * @param sysPermissionDataRule
758 775 * @return
759 776 */
... ... @@ -768,7 +785,7 @@ public class SysPermissionController {
768 785  
769 786 /**
770 787 * 添加菜单权限数据
771   - *
  788 + *
772 789 * @param sysPermissionDataRule
773 790 * @return
774 791 */
... ... @@ -803,7 +820,7 @@ public class SysPermissionController {
803 820  
804 821 /**
805 822 * 删除菜单权限数据
806   - *
  823 + *
807 824 * @param id
808 825 * @return
809 826 */
... ... @@ -823,7 +840,7 @@ public class SysPermissionController {
823 840  
824 841 /**
825 842 * 查询菜单权限数据
826   - *
  843 + *
827 844 * @param sysPermissionDataRule
828 845 * @return
829 846 */
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysPositionController.java
... ... @@ -8,10 +8,12 @@ import io.swagger.annotations.Api;
8 8 import io.swagger.annotations.ApiOperation;
9 9 import lombok.extern.slf4j.Slf4j;
10 10 import org.apache.poi.ss.formula.functions.T;
  11 +import org.apache.shiro.SecurityUtils;
11 12 import org.jeecg.common.api.vo.Result;
12 13 import org.jeecg.common.aspect.annotation.AutoLog;
13 14 import org.jeecg.common.constant.CommonConstant;
14 15 import org.jeecg.common.system.query.QueryGenerator;
  16 +import org.jeecg.common.system.vo.LoginUser;
15 17 import org.jeecg.common.util.ImportExcelUtil;
16 18 import org.jeecg.common.util.oConvertUtils;
17 19 import org.jeecg.modules.quartz.service.IQuartzJobService;
... ... @@ -208,10 +210,11 @@ public class SysPositionController {
208 210 //Step.2 AutoPoi 导出Excel
209 211 ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
210 212 List<SysPosition> pageList = sysPositionService.list(queryWrapper);
  213 + LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
211 214 //导出文件名称
212 215 mv.addObject(NormalExcelConstants.FILE_NAME, "职务表列表");
213 216 mv.addObject(NormalExcelConstants.CLASS, SysPosition.class);
214   - mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("职务表列表数据", "导出人:Jeecg", "导出信息"));
  217 + mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("职务表列表数据", "导出人:"+user.getRealname(),"导出信息"));
215 218 mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
216 219 return mv;
217 220 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysTenantController.java
... ... @@ -6,9 +6,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
6 6 import com.baomidou.mybatisplus.core.metadata.IPage;
7 7 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8 8 import lombok.extern.slf4j.Slf4j;
  9 +import org.apache.shiro.SecurityUtils;
9 10 import org.jeecg.common.api.vo.Result;
10 11 import org.jeecg.common.aspect.annotation.PermissionData;
11 12 import org.jeecg.common.system.query.QueryGenerator;
  13 +import org.jeecg.common.system.vo.LoginUser;
12 14 import org.jeecg.common.util.oConvertUtils;
13 15 import org.jeecg.modules.system.entity.SysTenant;
14 16 import org.jeecg.modules.system.service.ISysTenantService;
... ... @@ -16,9 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
16 18 import org.springframework.web.bind.annotation.*;
17 19  
18 20 import javax.servlet.http.HttpServletRequest;
19   -import java.util.ArrayList;
20   -import java.util.Date;
21   -import java.util.List;
  21 +import java.util.*;
22 22  
23 23 /**
24 24 * 租户配置信息
... ... @@ -132,11 +132,11 @@ public class SysTenantController {
132 132 }else {
133 133 String[] ls = ids.split(",");
134 134 // 过滤掉已被引用的租户
135   - List<String> idList = new ArrayList<>();
  135 + List<Integer> idList = new ArrayList<>();
136 136 for (String id : ls) {
137 137 int userCount = sysTenantService.countUserLinkTenant(id);
138 138 if (userCount == 0) {
139   - idList.add(id);
  139 + idList.add(Integer.parseInt(id));
140 140 }
141 141 }
142 142 if (idList.size() > 0) {
... ... @@ -190,4 +190,32 @@ public class SysTenantController {
190 190 result.setResult(ls);
191 191 return result;
192 192 }
  193 + /**
  194 + * 查询当前用户的所有有效租户 【当前用于vue3版本】
  195 + * @return
  196 + */
  197 + @RequestMapping(value = "/getCurrentUserTenant", method = RequestMethod.GET)
  198 + public Result<Map<String,Object>> getCurrentUserTenant() {
  199 + Result<Map<String,Object>> result = new Result<Map<String,Object>>();
  200 + try {
  201 + LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
  202 + String tenantIds = sysUser.getRelTenantIds();
  203 + Map<String,Object> map = new HashMap<String,Object>();
  204 + if (oConvertUtils.isNotEmpty(tenantIds)) {
  205 + List<Integer> tenantIdList = new ArrayList<>();
  206 + for(String id: tenantIds.split(",")){
  207 + tenantIdList.add(Integer.valueOf(id));
  208 + }
  209 + // 该方法仅查询有效的租户,如果返回0个就说明所有的租户均无效。
  210 + List<SysTenant> tenantList = sysTenantService.queryEffectiveTenant(tenantIdList);
  211 + map.put("list", tenantList);
  212 + }
  213 + result.setSuccess(true);
  214 + result.setResult(map);
  215 + }catch(Exception e) {
  216 + log.error(e.getMessage(), e);
  217 + result.error500("查询失败!");
  218 + }
  219 + return result;
  220 + }
193 221 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysUserController.java
... ... @@ -108,12 +108,38 @@ public class SysUserController {
108 108 @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,HttpServletRequest req) {
109 109 Result<IPage<SysUser>> result = new Result<IPage<SysUser>>();
110 110 QueryWrapper<SysUser> queryWrapper = QueryGenerator.initQueryWrapper(user, req.getParameterMap());
111   - //TODO 外部模拟登陆临时账号,列表不显示
  111 +
  112 + //update-begin-Author:wangshuai--Date:20211119--for:【vue3】通过部门id查询用户,通过code查询id
  113 + //部门ID
  114 + String departId = req.getParameter("departId");
  115 + if(oConvertUtils.isNotEmpty(departId)){
  116 + LambdaQueryWrapper<SysUserDepart> query = new LambdaQueryWrapper<>();
  117 + query.eq(SysUserDepart::getDepId,departId);
  118 + List<SysUserDepart> list = sysUserDepartService.list(query);
  119 + List<String> userIds = list.stream().map(SysUserDepart::getUserId).collect(Collectors.toList());
  120 + queryWrapper.in("id",userIds);
  121 + }
  122 + //用户ID
  123 + String code = req.getParameter("code");
  124 + if(oConvertUtils.isNotEmpty(code)){
  125 + queryWrapper.in("id",Arrays.asList(code.split(",")));
  126 + pageSize = code.split(",").length;
  127 + }
  128 + //update-end-Author:wangshuai--Date:20211119--for:【vue3】通过部门id查询用户,通过code查询id
  129 +
  130 + //update-begin-author:taoyan--date:20220104--for: JTC-372 【用户冻结问题】 online授权、用户组件,选择用户都能看到被冻结的用户
  131 + String status = req.getParameter("status");
  132 + if(oConvertUtils.isNotEmpty(status)){
  133 + queryWrapper.eq("status", Integer.parseInt(status));
  134 + }
  135 + //update-end-author:taoyan--date:20220104--for: JTC-372 【用户冻结问题】 online授权、用户组件,选择用户都能看到被冻结的用户
  136 +
  137 + //TODO 外部模拟登陆临时账号,列表不显示
112 138 queryWrapper.ne("username","_reserve_user_external");
113 139 Page<SysUser> page = new Page<SysUser>(pageNo, pageSize);
114 140 IPage<SysUser> pageList = sysUserService.page(page, queryWrapper);
115 141  
116   - //批量查询用户的所属部门
  142 + //批量查询用户的所属部门
117 143 //step.1 先拿到全部的 useids
118 144 //step.2 通过 useids,一次性查询用户的所属部门名字
119 145 List<String> userIds = pageList.getRecords().stream().map(SysUser::getId).collect(Collectors.toList());
... ... @@ -492,6 +518,8 @@ public class SysUserController {
492 518 errorMessage.add("第 " + lineNumber + " 行:手机号已经存在,忽略导入。");
493 519 } else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER_EMAIL)) {
494 520 errorMessage.add("第 " + lineNumber + " 行:电子邮件已经存在,忽略导入。");
  521 + } else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER)) {
  522 + errorMessage.add("第 " + lineNumber + " 行:违反表唯一性约束。");
495 523 } else {
496 524 errorMessage.add("第 " + lineNumber + " 行:未知错误,忽略导入");
497 525 log.error(e.getMessage(), e);
... ... @@ -1187,7 +1215,7 @@ public class SysUserController {
1187 1215 * @param jsonObject
1188 1216 * @return
1189 1217 */
1190   - @RequestMapping(value = "/appEdit", method = RequestMethod.PUT)
  1218 + @RequestMapping(value = "/appEdit", method = {RequestMethod.PUT,RequestMethod.POST})
1191 1219 public Result<SysUser> appEdit(HttpServletRequest request,@RequestBody JSONObject jsonObject) {
1192 1220 Result<SysUser> result = new Result<SysUser>();
1193 1221 try {
... ... @@ -1305,7 +1333,9 @@ public class SysUserController {
1305 1333 * @return
1306 1334 */
1307 1335 @GetMapping("/appQueryUser")
1308   - public Result<List<SysUser>> appQueryUser(@RequestParam(name = "keyword", required = false) String keyword) {
  1336 + public Result<List<SysUser>> appQueryUser(@RequestParam(name = "keyword", required = false) String keyword,
  1337 + @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
  1338 + @RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
1309 1339 Result<List<SysUser>> result = new Result<List<SysUser>>();
1310 1340 LambdaQueryWrapper<SysUser> queryWrapper =new LambdaQueryWrapper<SysUser>();
1311 1341 //TODO 外部模拟登陆临时账号,列表不显示
... ... @@ -1313,18 +1343,19 @@ public class SysUserController {
1313 1343 if(StringUtils.isNotBlank(keyword)){
1314 1344 queryWrapper.and(i -> i.like(SysUser::getUsername, keyword).or().like(SysUser::getRealname, keyword));
1315 1345 }
1316   - List<SysUser> list = sysUserService.list(queryWrapper);
  1346 + Page<SysUser> page = new Page<>(pageNo, pageSize);
  1347 + IPage<SysUser> pageList = this.sysUserService.page(page, queryWrapper);
1317 1348 //批量查询用户的所属部门
1318 1349 //step.1 先拿到全部的 useids
1319 1350 //step.2 通过 useids,一次性查询用户的所属部门名字
1320   - List<String> userIds = list.stream().map(SysUser::getId).collect(Collectors.toList());
  1351 + List<String> userIds = pageList.getRecords().stream().map(SysUser::getId).collect(Collectors.toList());
1321 1352 if(userIds!=null && userIds.size()>0){
1322 1353 Map<String,String> useDepNames = sysUserService.getDepNamesByUserIds(userIds);
1323   - list.forEach(item->{
  1354 + pageList.getRecords().forEach(item->{
1324 1355 item.setOrgCodeTxt(useDepNames.get(item.getId()));
1325 1356 });
1326 1357 }
1327   - result.setResult(list);
  1358 + result.setResult(pageList.getRecords());
1328 1359 return result;
1329 1360 }
1330 1361  
... ... @@ -1374,6 +1405,9 @@ public class SysUserController {
1374 1405 @GetMapping("/getMultiUser")
1375 1406 public List<SysUser> getMultiUser(SysUser sysUser){
1376 1407 QueryWrapper<SysUser> queryWrapper = QueryGenerator.initQueryWrapper(sysUser, null);
  1408 + //update-begin---author:wangshuai ---date:20220104 for:[JTC-297]已冻结用户仍可设置为代理人------------
  1409 + queryWrapper.eq("status",Integer.parseInt(CommonConstant.STATUS_1));
  1410 + //update-end---author:wangshuai ---date:20220104 for:[JTC-297]已冻结用户仍可设置为代理人------------
1377 1411 List<SysUser> ls = this.sysUserService.list(queryWrapper);
1378 1412 for(SysUser user: ls){
1379 1413 user.setPassword(null);
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysUserOnlineController.java
1 1 package org.jeecg.modules.system.controller;
2 2  
3   -import java.util.*;
4   -
5   -import javax.annotation.Resource;
  3 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4 +import lombok.extern.slf4j.Slf4j;
6 5 import org.apache.commons.lang.StringUtils;
7 6 import org.apache.shiro.SecurityUtils;
8 7 import org.jeecg.common.api.vo.Result;
... ... @@ -21,9 +20,11 @@ import org.springframework.beans.factory.annotation.Autowired;
21 20 import org.springframework.data.redis.core.RedisTemplate;
22 21 import org.springframework.web.bind.annotation.*;
23 22  
24   -import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
25   -
26   -import lombok.extern.slf4j.Slf4j;
  23 +import javax.annotation.Resource;
  24 +import java.util.ArrayList;
  25 +import java.util.Collection;
  26 +import java.util.Collections;
  27 +import java.util.List;
27 28  
28 29 /**
29 30 * @Description: 在线用户
... ... @@ -63,8 +64,20 @@ public class SysUserOnlineController {
63 64 online.setToken(token);
64 65 //TODO 改成一次性查询
65 66 LoginUser loginUser = sysBaseAPI.getUserByName(JwtUtil.getUsername(token));
66   - BeanUtils.copyProperties(loginUser, online);
67   - onlineList.add(online);
  67 + if (loginUser != null) {
  68 + //update-begin---author:wangshuai ---date:20220104 for:[JTC-382]在线用户查询无效------------
  69 + //验证用户名是否与传过来的用户名相同
  70 + boolean isMatchUsername=true;
  71 + //判断用户名是否为空,并且当前循环的用户不包含传过来的用户名,那么就设成false
  72 + if(oConvertUtils.isNotEmpty(username) && !loginUser.getUsername().contains(username)){
  73 + isMatchUsername = false;
  74 + }
  75 + if(isMatchUsername){
  76 + BeanUtils.copyProperties(loginUser, online);
  77 + onlineList.add(online);
  78 + }
  79 + //update-end---author:wangshuai ---date:20220104 for:[JTC-382]在线用户查询无效------------
  80 + }
68 81 }
69 82 }
70 83 Collections.reverse(onlineList);
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/ThirdAppController.java
... ... @@ -91,8 +91,12 @@ public class ThirdAppController {
91 91 @GetMapping("/sync/wechatEnterprise/depart/toApp")
92 92 public Result syncWechatEnterpriseDepartToApp(@RequestParam(value = "ids", required = false) String ids) {
93 93 if (thirdAppConfig.isWechatEnterpriseEnabled()) {
94   - boolean flag = wechatEnterpriseService.syncLocalDepartmentToThirdApp(ids);
95   - return flag ? Result.OK("同步成功", null) : Result.error("同步失败");
  94 + SyncInfoVo syncInfo = wechatEnterpriseService.syncLocalDepartmentToThirdApp(ids);
  95 + if (syncInfo.getFailInfo().size() == 0) {
  96 + return Result.OK("同步成功", null);
  97 + } else {
  98 + return Result.error("同步失败", syncInfo);
  99 + }
96 100 }
97 101 return Result.error("企业微信同步功能已禁用");
98 102 }
... ... @@ -125,8 +129,12 @@ public class ThirdAppController {
125 129 @GetMapping("/sync/dingtalk/depart/toApp")
126 130 public Result syncDingtalkDepartToApp(@RequestParam(value = "ids", required = false) String ids) {
127 131 if (thirdAppConfig.isDingtalkEnabled()) {
128   - boolean flag = dingtalkService.syncLocalDepartmentToThirdApp(ids);
129   - return flag ? Result.OK("同步成功", null) : Result.error("同步失败");
  132 + SyncInfoVo syncInfo = dingtalkService.syncLocalDepartmentToThirdApp(ids);
  133 + if (syncInfo.getFailInfo().size() == 0) {
  134 + return Result.OK("同步成功", null);
  135 + } else {
  136 + return Result.error("同步失败", syncInfo);
  137 + }
130 138 }
131 139 return Result.error("钉钉同步功能已禁用");
132 140 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/entity/SysDepart.java
... ... @@ -45,7 +45,7 @@ public class SysDepart implements Serializable {
45 45 /**描述*/
46 46 @Excel(name="描述",width=15)
47 47 private String description;
48   - /**机构类别 1公司,2组织机构,2岗位*/
  48 + /**机构类别 1=公司,2=组织机构,3=岗位*/
49 49 @Excel(name="机构类别",width=15,dicCode="org_category")
50 50 private String orgCategory;
51 51 /**机构类型*/
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/entity/SysPosition.java
... ... @@ -54,7 +54,6 @@ public class SysPosition {
54 54 /**
55 55 * 公司id
56 56 */
57   - @Excel(name = "公司id", width = 15)
58 57 @ApiModelProperty(value = "公司id")
59 58 private java.lang.String companyId;
60 59 /**
... ... @@ -84,7 +83,6 @@ public class SysPosition {
84 83 /**
85 84 * 组织机构编码
86 85 */
87   - @Excel(name = "组织机构编码", width = 15)
88 86 @ApiModelProperty(value = "组织机构编码")
89 87 private java.lang.String sysOrgCode;
90 88 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/entity/SysThirdAccount.java
... ... @@ -3,12 +3,14 @@ package org.jeecg.modules.system.entity;
3 3 import com.baomidou.mybatisplus.annotation.IdType;
4 4 import com.baomidou.mybatisplus.annotation.TableId;
5 5 import com.baomidou.mybatisplus.annotation.TableName;
  6 +import com.fasterxml.jackson.annotation.JsonFormat;
6 7 import io.swagger.annotations.ApiModel;
7 8 import io.swagger.annotations.ApiModelProperty;
8 9 import lombok.Data;
9 10 import lombok.EqualsAndHashCode;
10 11 import lombok.experimental.Accessors;
11 12 import org.jeecgframework.poi.excel.annotation.Excel;
  13 +import org.springframework.format.annotation.DateTimeFormat;
12 14  
13 15 /**
14 16 * @Description: 第三方登录账号表
... ... @@ -25,7 +27,7 @@ public class SysThirdAccount {
25 27  
26 28 /**编号*/
27 29 @TableId(type = IdType.ASSIGN_ID)
28   - @ApiModelProperty(value = "编号")
  30 + @ApiModelProperty(value = "编号")
29 31 private java.lang.String id;
30 32 /**第三方登录id*/
31 33 @Excel(name = "第三方登录id", width = 15)
... ... @@ -59,4 +61,20 @@ public class SysThirdAccount {
59 61 @Excel(name = "第三方用户账号", width = 15)
60 62 @ApiModelProperty(value = "第三方用户账号")
61 63 private java.lang.String thirdUserId;
  64 + /**创建人*/
  65 + @Excel(name = "创建人", width = 15)
  66 + private java.lang.String createBy;
  67 + /**创建日期*/
  68 + @Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
  69 + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
  70 + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
  71 + private java.util.Date createTime;
  72 + /**修改人*/
  73 + @Excel(name = "修改人", width = 15)
  74 + private java.lang.String updateBy;
  75 + /**修改日期*/
  76 + @Excel(name = "修改日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
  77 + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
  78 + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
  79 + private java.util.Date updateTime;
62 80 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/mapper/SysDictMapper.java
... ... @@ -193,4 +193,15 @@ public interface SysDictMapper extends BaseMapper&lt;SysDict&gt; {
193 193 */
194 194 @Deprecated
195 195 List<DictModel> queryAllTableDictItems(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("filterSql") String filterSql);
  196 +
  197 + /**
  198 + * 查询字典表的数据
  199 + * @param table 表名
  200 + * @param text 显示字段名
  201 + * @param code 存储字段名
  202 + * @param filterSql 条件sql
  203 + * @param codeValues 存储字段值 作为查询条件in
  204 + * @return
  205 + */
  206 + List<DictModel> queryTableDictByKeysAndFilterSql(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("filterSql") String filterSql, @Param("codeValues") List<String> codeValues);
196 207 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/mapper/xml/SysDictMapper.xml
... ... @@ -134,19 +134,24 @@
134 134 ${pidField} as parentId
135 135 from ${table}
136 136 where
137   - <choose>
138   - <when test="pid != null and pid != ''">
139   - ${pidField} = #{pid}
140   - </when>
141   - <otherwise>
142   - (${pidField} = '' OR ${pidField} IS NULL)
143   - </otherwise>
144   - </choose>
  137 + <!-- udapte-begin-author:sunjianlei date:20220110 for: 【JTC-597】自定义树查询条件查不出数据 -->
  138 + <if test="query == null">
  139 + <choose>
  140 + <when test="pid != null and pid != ''">
  141 + ${pidField} = #{pid}
  142 + </when>
  143 + <otherwise>
  144 + (${pidField} = '' OR ${pidField} IS NULL)
  145 + </otherwise>
  146 + </choose>
  147 + </if>
145 148 <if test="query!= null">
  149 + 1 = 1
146 150 <foreach collection="query.entrySet()" item="value" index="key" >
147   - and ${key} = #{value}
  151 + and ${key} LIKE #{value}
148 152 </foreach>
149 153 </if>
  154 + <!-- udapte-end-author:sunjianlei date:20220110 for: 【JTC-597】自定义树查询条件查不出数据 -->
150 155 </select>
151 156  
152 157  
... ... @@ -179,5 +184,18 @@
179 184 </select>
180 185  
181 186  
  187 + <!-- 查询字典表的数据 支持设置过滤条件、设置存储值作为in查询条件 -->
  188 + <select id="queryTableDictByKeysAndFilterSql" parameterType="String" resultType="org.jeecg.common.system.vo.DictModel">
  189 + select ${text} as "text", ${code} as "value" from ${table} where ${code} IN (
  190 + <foreach item="key" collection="codeValues" separator=",">
  191 + #{key}
  192 + </foreach>
  193 + )
  194 + <if test="filterSql != null and filterSql != ''">
  195 + and ${filterSql}
  196 + </if>
  197 + </select>
  198 +
  199 +
182 200  
183 201 </mapper>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/mapper/xml/SysUserDepartMapper.xml
... ... @@ -14,7 +14,9 @@
14 14 join sys_depart c on b.dep_id = c.id
15 15 where a.del_flag = 0 and c.org_code like '${orgCode}%'
16 16 <if test="realname!=null and realname!=''">
17   - and a.realname like '%${realname}%'
  17 + <!-- update by sunjianlei 20220119【#3348】SQL injection exists in /sys/user/queryUserByDepId; -->
  18 + <bind name="bindRealname" value="'%'+realname+'%'"/>
  19 + and a.realname like #{bindRealname}
18 20 </if>
19 21 </select>
20 22  
... ... @@ -23,12 +25,14 @@
23 25 select a.*, c.depart_name as org_code_txt from sys_user a
24 26 join sys_user_depart b on b.user_id = a.id
25 27 join sys_depart c on b.dep_id = c.id
26   - where a.del_flag = 0 and c.org_code like '${orgCode}%'
  28 + where a.del_flag = 0 and a.status = 1 and c.org_code like '${orgCode}%'
27 29 <if test="username!=null and username!=''">
28   - and a.username like '%${username}%'
  30 + <bind name="bindUsername" value="'%'+username+'%'"/>
  31 + and a.username like #{bindUsername}
29 32 </if>
30 33 <if test="realname!=null and realname!=''">
31   - and a.realname like '%${realname}%'
  34 + <bind name="bindRealname" value="'%'+realname+'%'"/>
  35 + and a.realname like #{bindRealname}
32 36 </if>
33 37 </select>
34 38 </mapper>
35 39 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/model/AnnouncementSendModel.java
1 1 package org.jeecg.modules.system.model;
2 2  
3   -import java.io.Serializable;
4   -
5   -import org.springframework.format.annotation.DateTimeFormat;
6   -
7 3 import com.baomidou.mybatisplus.annotation.IdType;
8 4 import com.baomidou.mybatisplus.annotation.TableId;
9   -import com.baomidou.mybatisplus.annotation.TableName;
10 5 import com.fasterxml.jackson.annotation.JsonFormat;
11   -
12 6 import lombok.Data;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +
  9 +import java.io.Serializable;
13 10  
14 11 /**
15 12 * @Description: 用户通告阅读标记表
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/rule/CategoryCodeRule.java
... ... @@ -2,6 +2,7 @@ package org.jeecg.modules.system.rule;
2 2  
3 3 import com.alibaba.fastjson.JSONObject;
4 4 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  5 +import lombok.extern.slf4j.Slf4j;
5 6 import org.jeecg.common.handler.IFillRuleHandler;
6 7 import org.jeecg.common.util.SpringContextUtils;
7 8 import org.jeecg.common.util.YouBianCodeUtil;
... ... @@ -16,12 +17,14 @@ import java.util.List;
16 17 * @Date 2019/12/9 11:32
17 18 * @Description: 分类字典编码生成规则
18 19 */
  20 +@Slf4j
19 21 public class CategoryCodeRule implements IFillRuleHandler {
20 22  
21 23 public static final String ROOT_PID_VALUE = "0";
22 24  
23 25 @Override
24 26 public Object execute(JSONObject params, JSONObject formData) {
  27 + log.info("系统自定义编码规则[category_code_rule],params:{} ,formData: {}", params, formData);
25 28  
26 29 String categoryPid = ROOT_PID_VALUE;
27 30 String categoryCode = null;
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/ISysDepartService.java
... ... @@ -67,7 +67,7 @@ public interface ISysDepartService extends IService&lt;SysDepart&gt;{
67 67 * @param keyWord
68 68 * @return
69 69 */
70   - List<SysDepartTreeModel> searhBy(String keyWord,String myDeptSearch,String departIds);
  70 + List<SysDepartTreeModel> searchByKeyWord(String keyWord,String myDeptSearch,String departIds);
71 71  
72 72 /**
73 73 * 根据部门id删除并删除其可能存在的子级部门
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/ISysTenantService.java
... ... @@ -14,7 +14,7 @@ public interface ISysTenantService extends IService&lt;SysTenant&gt; {
14 14 * @param idList
15 15 * @return
16 16 */
17   - List<SysTenant> queryEffectiveTenant(Collection<String> idList);
  17 + List<SysTenant> queryEffectiveTenant(Collection<Integer> idList);
18 18  
19 19 /**
20 20 * 返回某个租户被多少个用户引用了
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/IThirdAppService.java
... ... @@ -21,7 +21,7 @@ public interface IThirdAppService {
21 21 *
22 22 * @return 成功返回true
23 23 */
24   - boolean syncLocalDepartmentToThirdApp(String ids);
  24 + SyncInfoVo syncLocalDepartmentToThirdApp(String ids);
25 25  
26 26 /**
27 27 * 将第三方App部门同步到本地<br>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysBaseApiImpl.java
... ... @@ -146,7 +146,9 @@ public class SysBaseApiImpl implements ISysBaseAPI {
146 146 //通过自定义URL匹配规则 获取菜单(实现通过菜单配置数据权限规则,实际上针对获取数据接口进行数据规则控制)
147 147 String userMatchUrl = UrlMatchEnum.getMatchResultByUrl(requestPath);
148 148 LambdaQueryWrapper<SysPermission> queryQserMatch = new LambdaQueryWrapper<SysPermission>();
149   - queryQserMatch.eq(SysPermission::getMenuType, 1);
  149 + // update-begin-author:taoyan date:20211027 for: online菜单如果配置成一级菜单 权限查询不到 取消menuType = 1
  150 + //queryQserMatch.eq(SysPermission::getMenuType, 1);
  151 + // update-end-author:taoyan date:20211027 for: online菜单如果配置成一级菜单 权限查询不到 取消menuType = 1
150 152 queryQserMatch.eq(SysPermission::getDelFlag, 0);
151 153 queryQserMatch.eq(SysPermission::getUrl, userMatchUrl);
152 154 if(oConvertUtils.isNotEmpty(userMatchUrl)){
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysDepartPermissionServiceImpl.java
... ... @@ -69,7 +69,7 @@ public class SysDepartPermissionServiceImpl extends ServiceImpl&lt;SysDepartPermiss
69 69 @Override
70 70 public List<SysPermissionDataRule> getPermRuleListByDeptIdAndPermId(String departId, String permissionId) {
71 71 SysDepartPermission departPermission = this.getOne(new QueryWrapper<SysDepartPermission>().lambda().eq(SysDepartPermission::getDepartId, departId).eq(SysDepartPermission::getPermissionId, permissionId));
72   - if(departPermission != null){
  72 + if(departPermission != null && oConvertUtils.isNotEmpty(departPermission.getDataRuleIds())){
73 73 LambdaQueryWrapper<SysPermissionDataRule> query = new LambdaQueryWrapper<SysPermissionDataRule>();
74 74 query.in(SysPermissionDataRule::getId, Arrays.asList(departPermission.getDataRuleIds().split(",")));
75 75 query.orderByDesc(SysPermissionDataRule::getCreateTime);
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysDepartServiceImpl.java
... ... @@ -300,7 +300,7 @@ public class SysDepartServiceImpl extends ServiceImpl&lt;SysDepartMapper, SysDepart
300 300 * </p>
301 301 */
302 302 @Override
303   - public List<SysDepartTreeModel> searhBy(String keyWord,String myDeptSearch,String departIds) {
  303 + public List<SysDepartTreeModel> searchByKeyWord(String keyWord,String myDeptSearch,String departIds) {
304 304 LambdaQueryWrapper<SysDepart> query = new LambdaQueryWrapper<SysDepart>();
305 305 List<SysDepartTreeModel> newList = new ArrayList<>();
306 306 //myDeptSearch不为空时为我的部门搜索,只搜索所负责部门
... ... @@ -311,9 +311,15 @@ public class SysDepartServiceImpl extends ServiceImpl&lt;SysDepartMapper, SysDepart
311 311 }
312 312 //根据部门id获取所负责部门
313 313 String[] codeArr = this.getMyDeptParentOrgCode(departIds);
314   - for(int i=0;i<codeArr.length;i++){
315   - query.or().likeRight(SysDepart::getOrgCode,codeArr[i]);
  314 + //update-begin-author:taoyan date:20220104 for:/issues/3311 当用户属于两个部门的时候,且这两个部门没有上下级关系,我的部门-部门名称查询条件模糊搜索失效!
  315 + if (codeArr != null && codeArr.length > 0) {
  316 + query.nested(i -> {
  317 + for (String s : codeArr) {
  318 + i.or().likeRight(SysDepart::getOrgCode, s);
  319 + }
  320 + });
316 321 }
  322 + //update-end-author:taoyan date:20220104 for:/issues/3311 当用户属于两个部门的时候,且这两个部门没有上下级关系,我的部门-部门名称查询条件模糊搜索失效!
317 323 query.eq(SysDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString());
318 324 }
319 325 query.like(SysDepart::getDepartName, keyWord);
... ... @@ -499,7 +505,7 @@ public class SysDepartServiceImpl extends ServiceImpl&lt;SysDepartMapper, SysDepart
499 505 }
500 506 };
501 507 LambdaQueryWrapper<SysDepart> lqw=new LambdaQueryWrapper();
502   - lqw.eq(true,SysDepart::getDelFlag,CommonConstant.DEL_FLAG_0);
  508 + lqw.eq(true,SysDepart::getDelFlag,CommonConstant.DEL_FLAG_0.toString());
503 509 lqw.func(square);
504 510 lqw.orderByDesc(SysDepart::getDepartOrder);
505 511 List<SysDepart> list = list(lqw);
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysDictServiceImpl.java
... ... @@ -163,7 +163,15 @@ public class SysDictServiceImpl extends ServiceImpl&lt;SysDictMapper, SysDict&gt; impl
163 163  
164 164 @Override
165 165 public List<DictModel> queryTableDictTextByKeys(String table, String text, String code, List<String> keys) {
166   - return sysDictMapper.queryTableDictTextByKeys(table, text, code, keys);
  166 + //update-begin-author:taoyan date:20220113 for: @dict注解支持 dicttable 设置where条件
  167 + String filterSql = null;
  168 + if(table.toLowerCase().indexOf("where")>0){
  169 + String[] arr = table.split(" (?i)where ");
  170 + table = arr[0];
  171 + filterSql = arr[1];
  172 + }
  173 + return sysDictMapper.queryTableDictByKeysAndFilterSql(table, text, code, filterSql, keys);
  174 + //update-end-author:taoyan date:20220113 for: @dict注解支持 dicttable 设置where条件
167 175 }
168 176  
169 177 @Override
... ... @@ -225,6 +233,11 @@ public class SysDictServiceImpl extends ServiceImpl&lt;SysDictMapper, SysDict&gt; impl
225 233 insert = sysDictMapper.insert(sysDict);
226 234 if (sysDictItemList != null) {
227 235 for (SysDictItem entity : sysDictItemList) {
  236 + //update-begin---author:wangshuai ---date:20220211 for:[JTC-1168]如果字典项值为空,则字典项忽略导入------------
  237 + if(oConvertUtils.isEmpty(entity.getItemValue())){
  238 + return -1;
  239 + }
  240 + //update-end---author:wangshuai ---date:20220211 for:[JTC-1168]如果字典项值为空,则字典项忽略导入------------
228 241 entity.setDictId(sysDict.getId());
229 242 entity.setStatus(1);
230 243 sysDictItemMapper.insert(entity);
... ... @@ -255,7 +268,7 @@ public class SysDictServiceImpl extends ServiceImpl&lt;SysDictMapper, SysDict&gt; impl
255 268 public List<DictModel> queryLittleTableDictItems(String table, String text, String code, String condition, String keyword, int pageSize) {
256 269 Page<DictModel> page = new Page<DictModel>(1, pageSize);
257 270 page.setSearchCount(false);
258   - String filterSql = getFilterSql(text, code, condition, keyword);
  271 + String filterSql = getFilterSql(table, text, code, condition, keyword);
259 272 IPage<DictModel> pageList = baseMapper.queryTableDictWithFilter(page, table, text, code, filterSql);
260 273 return pageList.getRecords();
261 274 }
... ... @@ -268,12 +281,19 @@ public class SysDictServiceImpl extends ServiceImpl&lt;SysDictMapper, SysDict&gt; impl
268 281 * @param keyword
269 282 * @return
270 283 */
271   - private String getFilterSql(String text, String code, String condition, String keyword){
  284 + private String getFilterSql(String table, String text, String code, String condition, String keyword){
272 285 String keywordSql = null, filterSql = "", sql_where = " where ";
  286 + // update-begin-author:sunjianlei date:20220112 for: 【JTC-631】判断如果 table 携带了 where 条件,那么就使用 and 查询,防止报错
  287 + if (table.toLowerCase().contains(" where ")) {
  288 + sql_where = " and ";
  289 + }
  290 + // update-end-author:sunjianlei date:20220112 for: 【JTC-631】判断如果 table 携带了 where 条件,那么就使用 and 查询,防止报错
273 291 if(oConvertUtils.isNotEmpty(keyword)){
274 292 // 判断是否是多选
275 293 if (keyword.contains(",")) {
276   - String inKeywords = "\"" + keyword.replaceAll(",", "\",\"") + "\"";
  294 + //update-begin--author:scott--date:20220105--for:JTC-529【表单设计器】 编辑页面报错,in参数采用双引号导致 ----
  295 + String inKeywords = "'" + String.join("','", keyword.split(",")) + "'";
  296 + //update-end--author:scott--date:20220105--for:JTC-529【表单设计器】 编辑页面报错,in参数采用双引号导致----
277 297 keywordSql = "(" + text + " in (" + inKeywords + ") or " + code + " in (" + inKeywords + "))";
278 298 } else {
279 299 keywordSql = "("+text + " like '%"+keyword+"%' or "+ code + " like '%"+keyword+"%')";
... ... @@ -290,14 +310,20 @@ public class SysDictServiceImpl extends ServiceImpl&lt;SysDictMapper, SysDict&gt; impl
290 310 }
291 311 @Override
292 312 public List<DictModel> queryAllTableDictItems(String table, String text, String code, String condition, String keyword) {
293   - String filterSql = getFilterSql(text, code, condition, keyword);
  313 + String filterSql = getFilterSql(table, text, code, condition, keyword);
294 314 List<DictModel> ls = baseMapper.queryAllTableDictItems(table, text, code, filterSql);
295 315 return ls;
296 316 }
297 317  
298 318 @Override
299 319 public List<TreeSelectModel> queryTreeList(Map<String, String> query,String table, String text, String code, String pidField,String pid,String hasChildField) {
300   - return baseMapper.queryTreeList(query,table, text, code, pidField, pid,hasChildField);
  320 + List<TreeSelectModel> result = baseMapper.queryTreeList(query, table, text, code, pidField, pid, hasChildField);
  321 + // udapte-begin-author:sunjianlei date:20220110 for: 【JTC-597】如果 query 有值,就不允许展开子节点
  322 + if (query != null) {
  323 + result.forEach(r -> r.setLeaf(true));
  324 + }
  325 + return result;
  326 + // udapte-end-author:sunjianlei date:20220110 for: 【JTC-597】如果 query 有值,就不允许展开子节点
301 327 }
302 328  
303 329 @Override
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysTenantServiceImpl.java
... ... @@ -24,10 +24,10 @@ public class SysTenantServiceImpl extends ServiceImpl&lt;SysTenantMapper, SysTenant
24 24 ISysUserService userService;
25 25  
26 26 @Override
27   - public List<SysTenant> queryEffectiveTenant(Collection<String> idList) {
  27 + public List<SysTenant> queryEffectiveTenant(Collection<Integer> idList) {
28 28 LambdaQueryWrapper<SysTenant> queryWrapper = new LambdaQueryWrapper<>();
29 29 queryWrapper.in(SysTenant::getId, idList);
30   - queryWrapper.eq(SysTenant::getStatus, CommonConstant.STATUS_1);
  30 + queryWrapper.eq(SysTenant::getStatus, Integer.valueOf(CommonConstant.STATUS_1));
31 31 //此处查询忽略时间条件
32 32 return super.list(queryWrapper);
33 33 }
... ... @@ -50,7 +50,7 @@ public class SysTenantServiceImpl extends ServiceImpl&lt;SysTenantMapper, SysTenant
50 50 if (userCount > 0) {
51 51 throw new JeecgBootException("该租户已被引用,无法删除!");
52 52 }
53   - return super.removeById(id);
  53 + return super.removeById(Integer.parseInt(id));
54 54 }
55 55  
56 56 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysThirdAccountServiceImpl.java
... ... @@ -2,6 +2,7 @@ package org.jeecg.modules.system.service.impl;
2 2  
3 3 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 4 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  5 +import lombok.extern.slf4j.Slf4j;
5 6 import org.jeecg.common.constant.CommonConstant;
6 7 import org.jeecg.common.util.DateUtils;
7 8 import org.jeecg.common.util.PasswordUtil;
... ... @@ -30,6 +31,7 @@ import java.util.List;
30 31 * @Version: V1.0
31 32 */
32 33 @Service
  34 +@Slf4j
33 35 public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMapper, SysThirdAccount> implements ISysThirdAccountService {
34 36  
35 37 @Autowired
... ... @@ -116,6 +118,7 @@ public class SysThirdAccountServiceImpl extends ServiceImpl&lt;SysThirdAccountMappe
116 118 @Override
117 119 public SysThirdAccount getOneBySysUserId(String sysUserId, String thirdType) {
118 120 LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
  121 + log.info("getSysUserId: {} ,getThirdType: {}",sysUserId,thirdType);
119 122 queryWrapper.eq(SysThirdAccount::getSysUserId, sysUserId);
120 123 queryWrapper.eq(SysThirdAccount::getThirdType, thirdType);
121 124 return super.getOne(queryWrapper);
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysUserDepartServiceImpl.java
1 1 package org.jeecg.modules.system.service.impl;
2 2  
3   -import java.util.ArrayList;
4   -import java.util.HashMap;
5   -import java.util.List;
6   -import java.util.Map;
7   -import java.util.stream.Collectors;
8   -import java.util.stream.Collectors;
9   -
10   -import com.baomidou.mybatisplus.core.conditions.Wrapper;
  3 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
11 4 import com.baomidou.mybatisplus.core.metadata.IPage;
12 5 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  7 +import org.jeecg.common.constant.CommonConstant;
13 8 import org.jeecg.common.util.oConvertUtils;
14 9 import org.jeecg.modules.system.entity.SysDepart;
15 10 import org.jeecg.modules.system.entity.SysUser;
... ... @@ -22,8 +17,11 @@ import org.jeecg.modules.system.service.ISysUserService;
22 17 import org.springframework.beans.factory.annotation.Autowired;
23 18 import org.springframework.stereotype.Service;
24 19  
25   -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
26   -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  20 +import java.util.ArrayList;
  21 +import java.util.HashMap;
  22 +import java.util.List;
  23 +import java.util.Map;
  24 +import java.util.stream.Collectors;
27 25  
28 26 /**
29 27 * <P>
... ... @@ -128,6 +126,9 @@ public class SysUserDepartServiceImpl extends ServiceImpl&lt;SysUserDepartMapper, S
128 126 Page<SysUser> page = new Page<SysUser>(pageNo, pageSize);
129 127 if(oConvertUtils.isEmpty(departId)){
130 128 LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<>();
  129 + //update-begin---author:wangshuai ---date:20220104 for:[JTC-297]已冻结用户仍可设置为代理人------------
  130 + query.eq(SysUser::getStatus,Integer.parseInt(CommonConstant.STATUS_1));
  131 + //update-end---author:wangshuai ---date:20220104 for:[JTC-297]已冻结用户仍可设置为代理人------------
131 132 if(oConvertUtils.isNotEmpty(username)){
132 133 query.like(SysUser::getUsername, username);
133 134 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/SysUserServiceImpl.java
... ... @@ -399,6 +399,7 @@ public class SysUserServiceImpl extends ServiceImpl&lt;SysUserMapper, SysUser&gt; impl
399 399 }
400 400  
401 401 @Override
  402 + @CacheEvict(value={CacheConstant.SYS_USERS_CACHE}, allEntries=true)
402 403 public boolean revertLogicDeleted(List<String> userIds, SysUser updateEntity) {
403 404 String ids = String.format("'%s'", String.join("','", userIds));
404 405 return userMapper.revertLogicDeleted(ids, updateEntity) > 0;
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/ThirdAppDingtalkServiceImpl.java
... ... @@ -85,18 +85,31 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
85 85 return null;
86 86 }
87 87  
  88 + // update:2022-1-21,updateBy:sunjianlei; for 【JTC-704】【钉钉】部门同步成功,实际没成,后台提示ip白名单
88 89 @Override
89   - public boolean syncLocalDepartmentToThirdApp(String ids) {
  90 + public SyncInfoVo syncLocalDepartmentToThirdApp(String ids) {
  91 + SyncInfoVo syncInfo = new SyncInfoVo();
90 92 String accessToken = this.getAccessToken();
91 93 if (accessToken == null) {
92   - return false;
  94 + syncInfo.addFailInfo("accessToken获取失败!");
  95 + return syncInfo;
93 96 }
94 97 // 获取【钉钉】所有的部门
95   - List<Department> departments = JdtDepartmentAPI.listAll(accessToken);
  98 + List<Response<Department>> departments = JdtDepartmentAPI.listAllResponse(accessToken);
96 99 // 删除钉钉有但本地没有的部门(以本地部门数据为主)(钉钉不能创建同名部门,只能先删除)
97 100 List<SysDepart> sysDepartList = sysDepartService.list();
98 101 for1:
99   - for (Department department : departments) {
  102 + for (Response<Department> departmentRes : departments) {
  103 + // 判断部门是否查询成功
  104 + if (!departmentRes.isSuccess()) {
  105 + syncInfo.addFailInfo(departmentRes.getErrmsg());
  106 + // 88 是 ip 不在白名单的错误码,如果遇到此错误码,后面的操作都可以不用进行了,因为肯定都是失败的
  107 + if (new Integer(88).equals(departmentRes.getErrcode())) {
  108 + return syncInfo;
  109 + }
  110 + continue;
  111 + }
  112 + Department department = departmentRes.getResult();
100 113 for (SysDepart depart : sysDepartList) {
101 114 // id相同,代表已存在,不删除
102 115 String sourceIdentifier = department.getSource_identifier();
... ... @@ -124,24 +137,34 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
124 137 Department parent = new Department();
125 138 parent.setDept_id(1);
126 139 // 递归同步部门
127   - departments = JdtDepartmentAPI.listAll(accessToken);
128   - this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken);
129   - return true;
  140 + departments = JdtDepartmentAPI.listAllResponse(accessToken);
  141 + this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken, syncInfo);
  142 + return syncInfo;
130 143 }
131 144  
132 145 // 递归同步部门到本地
133   - public void syncDepartmentRecursion(List<SysDepartTreeModel> sysDepartsTree, List<Department> departments, Department parent, String accessToken) {
  146 + public void syncDepartmentRecursion(List<SysDepartTreeModel> sysDepartsTree, List<Response<Department>> departments, Department parent, String accessToken, SyncInfoVo syncInfo) {
134 147 if (sysDepartsTree != null && sysDepartsTree.size() != 0) {
135 148 for1:
136 149 for (SysDepartTreeModel depart : sysDepartsTree) {
137   - for (Department department : departments) {
  150 + for (Response<Department> departmentRes : departments) {
  151 + // 判断部门是否查询成功
  152 + if (!departmentRes.isSuccess()) {
  153 + syncInfo.addFailInfo(departmentRes.getErrmsg());
  154 + continue;
  155 + }
  156 + Department department = departmentRes.getResult();
138 157 // id相同,代表已存在,执行修改操作
139 158 String sourceIdentifier = department.getSource_identifier();
140 159 if (sourceIdentifier != null && sourceIdentifier.equals(depart.getId())) {
141 160 this.sysDepartToDtDepartment(depart, department, parent.getDept_id());
142   - JdtDepartmentAPI.update(department, accessToken);
143   - // 紧接着同步子级
144   - this.syncDepartmentRecursion(depart.getChildren(), departments, department, accessToken);
  161 + Response<JSONObject> response = JdtDepartmentAPI.update(department, accessToken);
  162 + if (response.isSuccess()) {
  163 + // 紧接着同步子级
  164 + this.syncDepartmentRecursion(depart.getChildren(), departments, department, accessToken, syncInfo);
  165 + }
  166 + // 收集错误信息
  167 + this.syncDepartCollectErrInfo(response, depart, syncInfo);
145 168 // 跳出外部循环
146 169 continue for1;
147 170 }
... ... @@ -154,10 +177,10 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
154 177 Department newParent = new Department();
155 178 newParent.setDept_id(response.getResult());
156 179 // 紧接着同步子级
157   - this.syncDepartmentRecursion(depart.getChildren(), departments, newParent, accessToken);
  180 + this.syncDepartmentRecursion(depart.getChildren(), departments, newParent, accessToken, syncInfo);
158 181 }
159 182 // 收集错误信息
160   -// this.syncUserCollectErrInfo(errCode, sysUser, errInfo);
  183 + this.syncDepartCollectErrInfo(response, depart, syncInfo);
161 184 }
162 185 }
163 186 }
... ... @@ -209,6 +232,11 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
209 232 SysDepart newSysDepart = this.dtDepartmentToSysDepart(departmentTree, null);
210 233 if (sysParentId != null) {
211 234 newSysDepart.setParentId(sysParentId);
  235 + // 2 = 组织机构
  236 + newSysDepart.setOrgCategory("2");
  237 + } else {
  238 + // 1 = 公司
  239 + newSysDepart.setOrgCategory("1");
212 240 }
213 241 try {
214 242 sysDepartService.saveDepartData(newSysDepart, username);
... ... @@ -246,6 +274,20 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
246 274 return false;
247 275 }
248 276  
  277 + /**
  278 + * 【同步部门】收集同步过程中的错误信息
  279 + */
  280 + private boolean syncDepartCollectErrInfo(Response<?> response, SysDepartTreeModel depart, SyncInfoVo syncInfo) {
  281 + if (!response.isSuccess()) {
  282 + String str = String.format("部门 %s(%s) 同步失败!错误码:%s——%s", depart.getDepartName(), depart.getOrgCode(), response.getErrcode(), response.getErrmsg());
  283 + syncInfo.addFailInfo(str);
  284 + return false;
  285 + } else {
  286 + String str = String.format("部门户 %s(%s) 同步成功!", depart.getDepartName(), depart.getOrgCode());
  287 + syncInfo.addSuccessInfo(str);
  288 + return true;
  289 + }
  290 + }
249 291  
250 292 @Override
251 293 public SyncInfoVo syncLocalUserToThirdApp(String ids) {
... ... @@ -279,7 +321,7 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
279 321 /*
280 322 * 判断是否同步过的逻辑:
281 323 * 1. 查询 sys_third_account(第三方账号表)是否有数据,如果有代表已同步
282   - * 2. 本地表里没有,就先用手机号判断,不通过再用username判断。
  324 + * 2. 本地表里没有,就先用手机号判断,不通过再用username(用户账号)判断。
283 325 */
284 326 SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneBySysUserId(sysUser.getId(), THIRD_TYPE);
285 327 if (sysThirdAccount != null && oConvertUtils.isNotEmpty(sysThirdAccount.getThirdUserId())) {
... ... @@ -528,6 +570,12 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
528 570 // update-begin--Author:liusq Date:20210713 for:钉钉同步到本地的人员没有状态,导致同步之后无法登录 #I3ZC2L
529 571 sysUser.setStatus(1);
530 572 // update-end--Author:liusq Date:20210713 for:钉钉同步到本地的人员没有状态,导致同步之后无法登录 #I3ZC2L
  573 + // 设置工号,如果工号为空,则使用username
  574 + if (oConvertUtils.isEmpty(dtUser.getJob_number())) {
  575 + sysUser.setWorkNo(dtUser.getUserid());
  576 + } else {
  577 + sysUser.setWorkNo(dtUser.getJob_number());
  578 + }
531 579 return this.dtUserToSysUser(dtUser, sysUser);
532 580 }
533 581  
... ...
jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/service/impl/ThirdAppWechatEnterpriseServiceImpl.java
... ... @@ -96,15 +96,18 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
96 96 }
97 97  
98 98 @Override
99   - public boolean syncLocalDepartmentToThirdApp(String ids) {
  99 + public SyncInfoVo syncLocalDepartmentToThirdApp(String ids) {
  100 + SyncInfoVo syncInfo = new SyncInfoVo();
100 101 String accessToken = this.getAccessToken();
101 102 if (accessToken == null) {
102   - return false;
  103 + syncInfo.addFailInfo("accessToken获取失败!");
  104 + return syncInfo;
103 105 }
104 106 // 获取企业微信所有的部门
105 107 List<Department> departments = JwDepartmentAPI.getAllDepartment(accessToken);
106 108 if (departments == null) {
107   - return false;
  109 + syncInfo.addFailInfo("获取企业微信所有部门失败!");
  110 + return syncInfo;
108 111 }
109 112 // 删除企业微信有但本地没有的部门(以本地部门数据为主)(以为企业微信不能创建同名部门,所以只能先删除)
110 113 List<JwDepartmentTreeVo> departmentTreeList = JwDepartmentTreeVo.listToTree(departments);
... ... @@ -117,7 +120,7 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
117 120 // 递归同步部门
118 121 departments = JwDepartmentAPI.getAllDepartment(accessToken);
119 122 this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken);
120   - return true;
  123 + return syncInfo;
121 124 }
122 125  
123 126 // 递归删除部门以及子部门,由于企业微信不允许删除带有成员和子部门的部门,所以需要递归删除下子部门,然后把部门成员移动端根部门下
... ... @@ -250,6 +253,11 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
250 253 SysDepart newSysDepart = this.qwDepartmentToSysDepart(departmentTree, null);
251 254 if (sysParentId != null) {
252 255 newSysDepart.setParentId(sysParentId);
  256 + // 2 = 组织机构
  257 + newSysDepart.setOrgCategory("2");
  258 + } else {
  259 + // 1 = 公司
  260 + newSysDepart.setOrgCategory("1");
253 261 }
254 262 try {
255 263 sysDepartService.saveDepartData(newSysDepart, username);
... ... @@ -604,6 +612,10 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
604 612 BeanUtils.copyProperties(oldSysUser, sysUser);
605 613 sysUser.setRealname(qwUser.getName());
606 614 sysUser.setPost(qwUser.getPosition());
  615 + // 设置工号,由于企业微信没有工号的概念,所以只能用 userId 代替
  616 + if (oConvertUtils.isEmpty(sysUser.getWorkNo())) {
  617 + sysUser.setWorkNo(qwUser.getUserid());
  618 + }
607 619 try {
608 620 sysUser.setSex(Integer.parseInt(qwUser.getGender()));
609 621 } catch (NumberFormatException ignored) {
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/application-prod.yml
... ... @@ -197,7 +197,7 @@ jeecg :
197 197 # ElasticSearch 设置
198 198 elasticsearch:
199 199 cluster-name: jeecg-ES
200   - cluster-nodes: 81.70.47.128:9200
  200 + cluster-nodes: 127.0.0.1:9200
201 201 check-enabled: true
202 202 # 表单设计器配置
203 203 desform:
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/banner.txt
... ... @@ -9,6 +9,6 @@ ${AnsiColor.BRIGHT_BLUE}
9 9  
10 10  
11 11 ${AnsiColor.BRIGHT_GREEN}
12   -Jeecg Boot Version: 3.0
  12 +Jeecg Boot Version: 3.1.0
13 13 Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
14 14 ${AnsiColor.BLACK}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/common/utils.ftl
... ... @@ -115,9 +115,9 @@
115 115 <#return "{type:'${po.classType}',value:'${po.fieldName}',text:'${po.filedComment}'}">
116 116 <#else>
117 117 <#if po.classType=="sel_search" || po.classType=="list_multi">
118   - <#return "{type:'${po.classType}',value:'${po.fieldName}',text:'${po.filedComment}',dictTable:'${superQuery_dictTable}', dictText:'${superQuery_dictText}', dictCode:'${po.dictField}'}">
  118 + <#return "{type:'${po.classType}',value:'${po.fieldName}',text:'${po.filedComment}',dictTable:\"${superQuery_dictTable}\", dictText:'${superQuery_dictText}', dictCode:'${po.dictField}'}">
119 119 <#elseif po.dictTable?? && po.dictTable!="" && po.classType!="sel_tree" && po.classType!="cat_tree" && po.classType!="link_down">
120   - <#return "{type:'${po.fieldDbType}',value:'${po.fieldName}',text:'${po.filedComment}',dictCode:'${po.dictTable},${po.dictText},${po.dictField}'}">
  120 + <#return "{type:'${po.fieldDbType}',value:'${po.fieldName}',text:'${po.filedComment}',dictCode:\"${po.dictTable},${po.dictText},${po.dictField}\"}">
121 121 <#elseif po.dictField?? && po.classType!="sel_tree" && po.classType!="cat_tree" && po.classType!="link_down">
122 122 <#return "{type:'${po.fieldDbType}',value:'${po.fieldName}',text:'${po.filedComment}',dictCode:'${po.dictField}'}">
123 123 <#elseif po.fieldDbType=="Text">
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -65,10 +65,10 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
65 65 * @param req
66 66 * @return
67 67 */
68   - @AutoLog(value = "${tableVo.ftlDescription}-分页列表查询")
  68 + //@AutoLog(value = "${tableVo.ftlDescription}-分页列表查询")
69 69 @ApiOperation(value="${tableVo.ftlDescription}-分页列表查询", notes="${tableVo.ftlDescription}-分页列表查询")
70 70 @GetMapping(value = "/list")
71   - public Result<?> queryPageList(${entityName} ${entityName?uncap_first},
  71 + public Result<IPage<${entityName}>> queryPageList(${entityName} ${entityName?uncap_first},
72 72 @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
73 73 @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
74 74 HttpServletRequest req) {
... ... @@ -87,7 +87,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
87 87 @AutoLog(value = "${tableVo.ftlDescription}-添加")
88 88 @ApiOperation(value="${tableVo.ftlDescription}-添加", notes="${tableVo.ftlDescription}-添加")
89 89 @PostMapping(value = "/add")
90   - public Result<?> add(@RequestBody ${entityName} ${entityName?uncap_first}) {
  90 + public Result<String> add(@RequestBody ${entityName} ${entityName?uncap_first}) {
91 91 <#if bpm_flag>
92 92 ${entityName?uncap_first}.setBpmStatus("1");
93 93 </#if>
... ... @@ -103,8 +103,8 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
103 103 */
104 104 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
105 105 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
106   - @PutMapping(value = "/edit")
107   - public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
  106 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
  107 + public Result<String> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
108 108 ${entityName?uncap_first}Service.updateById(${entityName?uncap_first});
109 109 return Result.OK("编辑成功!");
110 110 }
... ... @@ -118,7 +118,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
118 118 @AutoLog(value = "${tableVo.ftlDescription}-通过id删除")
119 119 @ApiOperation(value="${tableVo.ftlDescription}-通过id删除", notes="${tableVo.ftlDescription}-通过id删除")
120 120 @DeleteMapping(value = "/delete")
121   - public Result<?> delete(@RequestParam(name="id",required=true) String id) {
  121 + public Result<String> delete(@RequestParam(name="id",required=true) String id) {
122 122 ${entityName?uncap_first}Service.removeById(id);
123 123 return Result.OK("删除成功!");
124 124 }
... ... @@ -132,7 +132,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
132 132 @AutoLog(value = "${tableVo.ftlDescription}-批量删除")
133 133 @ApiOperation(value="${tableVo.ftlDescription}-批量删除", notes="${tableVo.ftlDescription}-批量删除")
134 134 @DeleteMapping(value = "/deleteBatch")
135   - public Result<?> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
  135 + public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
136 136 this.${entityName?uncap_first}Service.removeByIds(Arrays.asList(ids.split(",")));
137 137 return Result.OK("批量删除成功!");
138 138 }
... ... @@ -143,13 +143,13 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
143 143 * @param id
144 144 * @return
145 145 */
146   - @AutoLog(value = "${tableVo.ftlDescription}-通过id查询")
  146 + //@AutoLog(value = "${tableVo.ftlDescription}-通过id查询")
147 147 @ApiOperation(value="${tableVo.ftlDescription}-通过id查询", notes="${tableVo.ftlDescription}-通过id查询")
148 148 @GetMapping(value = "/queryById")
149   - public Result<?> queryById(@RequestParam(name="id",required=true) String id) {
  149 + public Result<${entityName}> queryById(@RequestParam(name="id",required=true) String id) {
150 150 ${entityName} ${entityName?uncap_first} = ${entityName?uncap_first}Service.getById(id);
151 151 if(${entityName?uncap_first}==null) {
152   - return Result.error("未找到对应数据");
  152 + return Result.error("未找到对应数据",null);
153 153 }
154 154 return Result.OK(${entityName?uncap_first});
155 155 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -108,8 +108,8 @@
108 108 </#if>
109 109 </#list>
110 110 <#if bpm_flag>
111   - <a-col v-if="showFlowSubmitButton" :span="24" style="text-align: center">
112   - <a-button @click="submitForm">提 交</a-button>
  111 + <a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
  112 + <a-button icon="check" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
113 113 </a-col>
114 114 </#if>
115 115 </a-row>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 +<#assign list_need_category=false>
  4 +<#assign list_need_pca=false>
  5 +<#assign bpm_flag=false>
  6 +
  7 +<#-- 开始循环 -->
  8 +<#list columns as po>
  9 +<#if po.fieldDbName=='bpm_status'>
  10 + <#assign bpm_flag=true>
  11 +</#if>
  12 +<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  13 +<#assign list_need_category=true>
  14 +</#if>
  15 +<#if po.classType=='pca'>
  16 +<#assign list_need_pca=true>
  17 +</#if>
  18 +</#list>
  19 +<#-- 结束循环 -->
  20 + <!--引用表格-->
  21 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  22 + <!--插槽:table标题-->
  23 + <template #tableTitle>
  24 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  25 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  26 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  27 + <a-dropdown v-if="checkedKeys.length > 0">
  28 + <template #overlay>
  29 + <a-menu>
  30 + <a-menu-item key="1" @click="batchHandleDelete">
  31 + <Icon icon="ant-design:delete-outlined"></Icon>
  32 + 删除
  33 + </a-menu-item>
  34 + </a-menu>
  35 + </template>
  36 + <a-button>批量操作
  37 + <Icon icon="mdi:chevron-down"></Icon>
  38 + </a-button>
  39 + </a-dropdown>
  40 + </template>
  41 + <!--操作栏-->
  42 + <template #action="{ record }">
  43 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  44 + </template>
  45 + <!--字段回显插槽-->
  46 + <template #htmlSlot="{text}">
  47 + <div v-html="text"></div>
  48 + </template>
  49 + <template #fileSlot="{text}">
  50 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  51 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  52 + </template>
  53 + </BasicTable>
  54 + <!-- 表单区域 -->
  55 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  56 + </div>
  57 +</template>
  58 +
  59 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  60 + import {ref, computed, unref} from 'vue';
  61 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  62 + import {useModal} from '/@/components/Modal';
  63 + import { useListPage } from '/@/hooks/system/useListPage'
  64 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  65 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  66 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  67 + <#if list_need_category>
  68 + import { loadCategoryData } from '/@/api/common/api'
  69 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  70 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  71 + </#if>
  72 + const checkedKeys = ref<Array<string | number>>([]);
  73 + //注册model
  74 + const [registerModal, {openModal}] = useModal();
  75 + //注册table数据
  76 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  77 + tableProps:{
  78 + title: '${tableVo.ftlDescription}',
  79 + api: list,
  80 + columns,
  81 + canResize:false,
  82 + formConfig: {
  83 + labelWidth: 120,
  84 + schemas: searchFormSchema,
  85 + autoSubmitOnEnter:true,
  86 + showAdvancedButton:true,
  87 + fieldMapToTime: [
  88 + <#list columns as po>
  89 + <#if po.isQuery=='Y'>
  90 + <#if po.queryMode!='single'>
  91 + <#if po.classType=='date'>
  92 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
  93 + <#elseif po.classType=='datetime'>
  94 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
  95 + </#if>
  96 + </#if>
  97 + </#if>
  98 + </#list>
  99 + ],
  100 + },
  101 + actionColumn: {
  102 + width: 120,
  103 + },
  104 + },
  105 + exportConfig: {
  106 + name:"${tableVo.ftlDescription}",
  107 + url: getExportUrl,
  108 + },
  109 + importConfig: {
  110 + url: getImportUrl
  111 + },
  112 + })
  113 +
  114 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  115 +
  116 + /**
  117 + * 新增事件
  118 + */
  119 + function handleAdd() {
  120 + openModal(true, {
  121 + isUpdate: false,
  122 + showFooter: true,
  123 + });
  124 + }
  125 + /**
  126 + * 编辑事件
  127 + */
  128 + function handleEdit(record: Recordable) {
  129 + openModal(true, {
  130 + record,
  131 + isUpdate: true,
  132 + showFooter: true,
  133 + });
  134 + }
  135 + /**
  136 + * 详情
  137 + */
  138 + function handleDetail(record: Recordable) {
  139 + openModal(true, {
  140 + record,
  141 + isUpdate: true,
  142 + showFooter: false,
  143 + });
  144 + }
  145 + /**
  146 + * 删除事件
  147 + */
  148 + async function handleDelete(record) {
  149 + await deleteOne({id: record.id}, reload);
  150 + }
  151 + /**
  152 + * 批量删除事件
  153 + */
  154 + async function batchHandleDelete() {
  155 + await batchDelete({ids: checkedKeys.value}, reload);
  156 + }
  157 + /**
  158 + * 成功回调
  159 + */
  160 + function handleSuccess() {
  161 + reload();
  162 + }
  163 + /**
  164 + * 操作栏
  165 + */
  166 + function getTableAction(record){
  167 + return [
  168 + {
  169 + label: '编辑',
  170 + onClick: handleEdit.bind(null, record),
  171 + }
  172 + ]
  173 + }
  174 + /**
  175 + * 下拉操作栏
  176 + */
  177 + function getDropDownAction(record){
  178 + return [
  179 + {
  180 + label: '详情',
  181 + onClick: handleDetail.bind(null, record),
  182 + }, {
  183 + label: '删除',
  184 + popConfirm: {
  185 + title: '是否确认删除',
  186 + confirm: handleDelete.bind(null, record),
  187 + }
  188 + }
  189 + ]
  190 + }
  191 + <#if list_need_category>
  192 + /**
  193 + * 初始化字典配置
  194 + */
  195 + function initDictConfig(){
  196 + <#list columns as po>
  197 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  198 + <#if po.classType=='cat_tree' && list_need_category==true>
  199 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  200 + if (res) {
  201 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  202 + if(!allDictDate['${po.dictField?default("")}']){
  203 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  204 + }
  205 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  206 + }
  207 + })
  208 + </#if>
  209 + </#if>
  210 + </#list>
  211 + }
  212 + initDictConfig();
  213 + </#if>
  214 +</script>
  215 +
  216 +<style scoped>
  217 +
  218 +</style>
0 219 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +}
  13 +/**
  14 + * 导出api
  15 + * @param params
  16 + */
  17 +export const getExportUrl = Api.exportXls;
  18 +/**
  19 + * 导入api
  20 + */
  21 +export const getImportUrl = Api.importExcel;
  22 +/**
  23 + * 列表接口
  24 + * @param params
  25 + */
  26 +export const list = (params) =>
  27 + defHttp.get({url: Api.list, params});
  28 +
  29 +/**
  30 + * 删除单个
  31 + */
  32 +export const deleteOne = (params,handleSuccess) => {
  33 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  34 + handleSuccess();
  35 + });
  36 +}
  37 +/**
  38 + * 批量删除
  39 + * @param params
  40 + */
  41 +export const batchDelete = (params, handleSuccess) => {
  42 + Modal.confirm({
  43 + title: '确认删除',
  44 + content: '是否删除选中数据',
  45 + okText: '确认',
  46 + cancelText: '取消',
  47 + onOk: () => {
  48 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  49 + handleSuccess();
  50 + });
  51 + }
  52 + });
  53 +}
  54 +/**
  55 + * 保存或者更新
  56 + * @param params
  57 + */
  58 +export const saveOrUpdate = (params, isUpdate) => {
  59 + let url = isUpdate ? Api.edit : Api.save;
  60 + return defHttp.post({url: url, params});
  61 +}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +//列表数据
  6 +export const columns: BasicColumn[] = [
  7 + <#list columns as po>
  8 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  9 + {
  10 + title: '${po.filedComment}',
  11 + align:"center",
  12 + <#if po.sort=='Y'>
  13 + sorter: true,
  14 + </#if>
  15 + <#if po.classType=='date'>
  16 + dataIndex: '${po.fieldName}',
  17 + customRender:({text}) =>{
  18 + return !text?"":(text.length>10?text.substr(0,10):text)
  19 + },
  20 + <#elseif po.fieldDbType=='Blob'>
  21 + dataIndex: '${po.fieldName}String'
  22 + <#elseif po.classType=='umeditor'>
  23 + dataIndex: '${po.fieldName}',
  24 + slots: { customRender: 'htmlSlot' },
  25 + <#elseif po.classType=='pca'>
  26 + dataIndex: '${po.fieldName}',
  27 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  28 + <#elseif po.classType=='file'>
  29 + dataIndex: '${po.fieldName}',
  30 + slots: { customRender: 'fileSlot' },
  31 + <#elseif po.classType=='image'>
  32 + dataIndex: '${po.fieldName}',
  33 + customRender:render.renderAvatar,
  34 + <#elseif po.classType=='switch'>
  35 + dataIndex: '${po.fieldName}',
  36 +<#assign switch_extend_arr=['Y','N']>
  37 +<#if po.dictField?default("")?contains("[")>
  38 +<#assign switch_extend_arr=po.dictField?eval>
  39 +</#if>
  40 +<#list switch_extend_arr as a>
  41 +<#if a_index == 0>
  42 +<#assign switch_extend_arr1=a>
  43 +<#else>
  44 +<#assign switch_extend_arr2=a>
  45 +</#if>
  46 +</#list>
  47 + customRender:({text}) => {
  48 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  49 + },
  50 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  51 + dataIndex: '${po.fieldName}_dictText'
  52 + <#elseif po.classType=='cat_tree'>
  53 + dataIndex: '${po.fieldName}',
  54 + <#if po.dictText?default("")?trim?length == 0>
  55 + customRender:({text}) => {
  56 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  57 + },
  58 + <#else>
  59 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  60 + </#if>
  61 + <#else>
  62 + dataIndex: '${po.fieldName}'
  63 + </#if>
  64 + },
  65 + </#if>
  66 + </#list>
  67 +];
  68 +//查询数据
  69 +export const searchFormSchema: FormSchema[] = [
  70 +<#-- 开始循环 -->
  71 +<#list columns as po>
  72 +<#if po.fieldDbName=='bpm_status'>
  73 + <#assign bpm_flag=true>
  74 +</#if>
  75 +<#if po.isQuery=='Y'>
  76 +<#assign query_flag=true>
  77 + <#assign query_field_dictCode="">
  78 + <#if po.dictTable?default("")?trim?length gt 1>
  79 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  80 + <#elseif po.dictField?default("")?trim?length gt 1>
  81 + <#assign query_field_dictCode="${po.dictField}">
  82 + </#if>
  83 +<#if po.queryMode=='single'>
  84 + {
  85 + label: "${po.filedComment}",
  86 + field: "${po.fieldName}",
  87 +<#if po.classType=='sel_search'>
  88 + component: 'JSearchSelect',
  89 + componentProps:{
  90 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  91 + },
  92 +<#elseif po.classType=='sel_user'>
  93 + component: 'JSelectUserByDept',
  94 +<#elseif po.classType=='switch'>
  95 + component: 'JSwitch',
  96 + componentProps:{
  97 + <#if po.dictField != 'is_open'>
  98 + options:"${po.dictField}"
  99 + </#if>
  100 + },
  101 + <#elseif po.classType=='sel_depart'>
  102 + component: 'JSelectDept',
  103 + <#elseif po.classType=='list_multi'>
  104 + component: 'JMultiSelectTag',//暂无该组件
  105 + componentProps:{
  106 + dictCode:"query_field_dictCode?default("")"
  107 + },
  108 + <#elseif po.classType=='cat_tree'>
  109 + component: 'JCategorySelect',
  110 + componentProps:{
  111 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  112 + },
  113 +<#elseif po.classType=='date'>
  114 + component: 'DatePicker',
  115 +<#elseif po.classType=='datetime'>
  116 + component: 'DatePicker',
  117 + componentProps: {
  118 + showTime:true
  119 + },
  120 +<#elseif po.classType=='pca'>
  121 + component: 'JAreaLinkage',
  122 +<#elseif po.classType=='popup'>
  123 + component: 'JPopup',
  124 + componentProps: ({ formActionType }) => {
  125 + const {setFieldsValue} = formActionType;
  126 + return{
  127 + setFieldsValue:setFieldsValue,
  128 + code:"${po.dictTable}",
  129 + fieldConfig:"${po.dictField}",
  130 + multi:${po.extendParams.popupMulti?c},
  131 + }
  132 + },
  133 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  134 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  135 + component: 'JDictSelectTag',
  136 + componentProps:{
  137 + <#if po.dictTable?default("")?trim?length gt 1>
  138 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
  139 + <#elseif po.dictField?default("")?trim?length gt 1>
  140 + dictCode:"${po.dictField}"
  141 + </#if>
  142 + },
  143 +<#else>
  144 + component: 'Input',
  145 +</#if>
  146 + colProps: {span: 6},
  147 + },
  148 +<#else>
  149 + {
  150 + label: "${po.filedComment}",
  151 + field: "${po.fieldName}",
  152 +<#if po.classType=='date'>
  153 + component: 'RangePicker',
  154 +<#elseif po.classType=='datetime'>
  155 + component: 'RangePicker',
  156 + componentProps: {
  157 + showTime:true
  158 + },
  159 +<#else>
  160 + component: 'Input', //TODO 范围查询
  161 +</#if>
  162 + colProps: {span: 6},
  163 + },
  164 +</#if>
  165 +</#if>
  166 +</#list>
  167 +<#-- 结束循环 -->
  168 +];
  169 +//表单数据
  170 +export const formSchema: FormSchema[] = [
  171 +<#assign form_cat_tree = false>
  172 +<#assign form_cat_back = "">
  173 +<#assign bpm_flag=false>
  174 +<#list columns as po><#rt/>
  175 +<#if po.fieldDbName=='bpm_status'>
  176 + <#assign bpm_flag=true>
  177 +</#if>
  178 +<#if po.isShow =='Y'>
  179 +<#assign form_field_dictCode="">
  180 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  181 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  182 + <#elseif po.dictField?default("")?trim?length gt 1>
  183 + <#assign form_field_dictCode="${po.dictField}">
  184 + </#if>
  185 + {
  186 + label: '${po.filedComment}',
  187 + field: '${po.fieldName}',
  188 + <#if po.classType =='date'>
  189 + component: 'DatePicker',
  190 + <#elseif po.fieldType =='datetime'>
  191 + component: 'DatePicker',
  192 + componentProps: {
  193 + showTime:true
  194 + },
  195 + <#elseif po.fieldType =='time'>
  196 + component: 'TimePicker',
  197 + <#elseif po.classType =='popup'>
  198 + component: 'JPopup',
  199 + componentProps: ({ formActionType }) => {
  200 + const {setFieldsValue} = formActionType;
  201 + return{
  202 + setFieldsValue:setFieldsValue,
  203 + code:"${po.dictTable}",
  204 + fieldConfig:${po.dictField},
  205 + multi:${po.extendParams.popupMulti?c},
  206 + }
  207 + }
  208 + <#elseif po.classType =='sel_depart'>
  209 + component: 'JSelectDept',
  210 + <#elseif po.classType =='switch'>
  211 + component: 'JSwitch',
  212 + componentProps:{
  213 + <#if po.dictField != 'is_open'>
  214 + options:${po.dictField}
  215 + </#if>
  216 + }
  217 + <#elseif po.classType =='pca'>
  218 + component: 'JAreaLinkage',
  219 + <#elseif po.classType =='markdown'>
  220 + component: 'JMarkdownEditor',//注意string转换问题
  221 + <#elseif po.classType =='password'>
  222 + component: 'InputPassword',
  223 + <#elseif po.classType =='sel_user'>
  224 + component: 'JSelectUserByDept',
  225 + componentProps:{
  226 + labelKey:'realname',
  227 + }
  228 + <#elseif po.classType =='textarea'>
  229 + component: 'InputTextArea',//TODO 注意string转换问题
  230 + <#elseif po.classType=='list' || po.classType=='radio'>
  231 + component: 'JDictSelectTag',
  232 + componentProps:{
  233 + dictCode:"${form_field_dictCode}"
  234 + }
  235 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  236 + component: 'JMultiSelectTag',//TODO 暂无该组件
  237 + componentProps:{
  238 + dictCode:"${form_field_dictCode}"
  239 + }
  240 + <#elseif po.classType=='sel_search'>
  241 + component: 'JSearchSelect',
  242 + componentProps:{
  243 + dict:"${form_field_dictCode}"
  244 + }
  245 +<#elseif po.classType=='cat_tree'>
  246 + <#assign form_cat_tree = true>
  247 + component: 'JCategorySelect',
  248 + componentProps:{
  249 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  250 + }
  251 + <#if po.dictText?default("")?trim?length gt 1>
  252 + <#assign form_cat_back = "${po.dictText}">
  253 + </#if>
  254 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  255 + component: 'InputNumber',
  256 + <#elseif po.classType=='file'>
  257 + component: 'JUpload',
  258 + componentProps:{
  259 + <#if po.uploadnum??>
  260 + maxCount:${po.uploadnum}
  261 + </#if>
  262 + }
  263 + <#elseif po.classType=='image'>
  264 + component: 'JImageUpload',
  265 + componentProps:{
  266 + <#if po.uploadnum??>
  267 + fileMax:${po.uploadnum}
  268 + </#if>
  269 + }
  270 + <#elseif po.classType=='umeditor'>
  271 + component: 'JCodeEditor', //TODO String后缀暂未添加
  272 + <#elseif po.classType == 'sel_tree'>
  273 + component: 'JTreeSelect',
  274 + componentProps:{
  275 + <#if po.dictText??>
  276 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  277 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  278 + <#elseif po.dictText?split(',')[1]??>
  279 + pidField:"${po.dictText?split(',')[1]}",
  280 + <#elseif po.dictText?split(',')[3]??>
  281 + hasChildField:"${po.dictText?split(',')[3]}",
  282 + </#if>
  283 + </#if>
  284 + pidValue:"${po.dictField}",
  285 + }
  286 + <#else>
  287 + component: 'Input',
  288 + </#if>
  289 + <#include "/common/utils.ftl">
  290 + <#if po.isShow == 'Y' && poHasCheck(po)>
  291 + dynamicRules: ({model,schema}) => {
  292 + <#if po.fieldName != 'id'>
  293 + <#assign fieldValidType = po.fieldValidType!''>
  294 + return [
  295 + <#-- 非空校验 -->
  296 + <#if po.nullable == 'N' || fieldValidType == '*'>
  297 + { required: true, message: '请输入${po.filedComment}!'},
  298 + <#elseif fieldValidType!=''>
  299 + { required: false},
  300 + </#if>
  301 + <#-- 唯一校验 -->
  302 + <#if fieldValidType == 'only'>
  303 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  304 + <#-- 6到16位数字 -->
  305 + <#elseif fieldValidType == 'n6-16'>
  306 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  307 + <#-- 6到16位任意字符 -->
  308 + <#elseif fieldValidType == '*6-16'>
  309 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  310 + <#-- 6到18位字符串 -->
  311 + <#elseif fieldValidType == 's6-18'>
  312 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  313 + <#-- 网址 -->
  314 + <#elseif fieldValidType == 'url'>
  315 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  316 + <#-- 电子邮件 -->
  317 + <#elseif fieldValidType == 'e'>
  318 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  319 + <#-- 手机号码 -->
  320 + <#elseif fieldValidType == 'm'>
  321 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  322 + <#-- 邮政编码 -->
  323 + <#elseif fieldValidType == 'p'>
  324 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  325 + <#-- 字母 -->
  326 + <#elseif fieldValidType == 's'>
  327 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  328 + <#-- 数字 -->
  329 + <#elseif fieldValidType == 'n'>
  330 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  331 + <#-- 整数 -->
  332 + <#elseif fieldValidType == 'z'>
  333 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  334 + <#-- 金额 -->
  335 + <#elseif fieldValidType == 'money'>
  336 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  337 + <#-- 正则校验 -->
  338 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  339 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  340 + <#-- 无校验 -->
  341 + <#else>
  342 + <#t>
  343 + </#if>
  344 + ];
  345 + </#if>
  346 + },
  347 + </#if>
  348 + <#if po.readonly=='Y'>
  349 + dynamicDisabled:true
  350 + </#if>
  351 + },
  352 +</#if>
  353 +</#list>
  354 +];
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicForm @register="registerForm"/>
  4 + </BasicModal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import {ref, computed, unref} from 'vue';
  9 + import {BasicModal, useModalInner} from '/@/components/Modal';
  10 + import {BasicForm, useForm} from '/@/components/Form/index';
  11 + import {formSchema} from '../${entityName?uncap_first}.data';
  12 + import {saveOrUpdate} from '../${entityName?uncap_first}.api';
  13 + // Emits声明
  14 + const emit = defineEmits(['register','success']);
  15 + const isUpdate = ref(true);
  16 + //表单配置
  17 + const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
  18 + labelWidth: 150,
  19 + schemas: formSchema,
  20 + showActionButtonGroup: false,
  21 + });
  22 + //表单赋值
  23 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  24 + //重置表单
  25 + await resetFields();
  26 + setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
  27 + isUpdate.value = !!data?.isUpdate;
  28 + if (unref(isUpdate)) {
  29 + //表单赋值
  30 + await setFieldsValue({
  31 + ...data.record,
  32 + });
  33 + }
  34 + // 隐藏底部时禁用整个表单
  35 + setProps({ disabled: !data?.showFooter })
  36 + });
  37 + //设置标题
  38 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  39 + //表单提交事件
  40 + async function handleSubmit(v) {
  41 + try {
  42 + let values = await validate();
  43 + setModalProps({confirmLoading: true});
  44 + //提交表单
  45 + await saveOrUpdate(values, isUpdate.value);
  46 + //关闭弹窗
  47 + closeModal();
  48 + //刷新列表
  49 + emit('success');
  50 + } finally {
  51 + setModalProps({confirmLoading: false});
  52 + }
  53 + }
  54 +</script>
  55 +
  56 +<style lang="less" scoped>
  57 +
  58 +</style>
0 59 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -119,7 +119,7 @@ public class ${entityName}Controller {
119 119 */
120 120 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
121 121 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
122   - @PutMapping(value = "/edit")
  122 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
123 123 public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
124 124 ${entityName} ${entityName?uncap_first} = new ${entityName}();
125 125 BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -28,13 +28,8 @@
28 28 <#elseif po.dictField?default("")?trim?length gt 1>
29 29 <#assign form_field_dictCode="${po.dictField}">
30 30 </#if>
31   - <#if po.classType =='textarea'>
32   - <a-col :span="24">
33   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
34   - <#else>
35 31 <a-col :span="${form_span}" >
36 32 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
37   - </#if>
38 33 <#if po.classType =='date'>
39 34 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
40 35 <#elseif po.classType =='datetime'>
... ... @@ -140,7 +135,7 @@
140 135 </#list>
141 136 </a-tabs>
142 137 <#if bpm_flag>
143   - <a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button @click="handleOk">提 交</a-button></a-row>
  138 + <a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button icon="check" style="width: 126px" type="primary" @click="handleOk">提 交</a-button></a-row>
144 139 </#if>
145 140 </a-spin>
146 141 </template>
... ... @@ -171,20 +166,12 @@
171 166 return {
172 167 labelCol: {
173 168 xs: { span: 24 },
174   - sm: { span: 6 },
  169 + sm: { span: 5 },
175 170 },
176 171 wrapperCol: {
177 172 xs: { span: 24 },
178 173 sm: { span: 16 },
179 174 },
180   - labelCol2: {
181   - xs: { span: 24 },
182   - sm: { span: 3 },
183   - },
184   - wrapperCol2: {
185   - xs: { span: 24 },
186   - sm: { span: 20 },
187   - },
188 175 model:{
189 176 <#include "/common/init/initValue.ftl">
190 177 },
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -25,13 +25,8 @@
25 25 <#elseif po.dictField?default("")?trim?length gt 1>
26 26 <#assign form_field_dictCode="${po.dictField}">
27 27 </#if>
28   - <#if po.classType =='textarea'>
29   - <a-col :span="24">
30   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
31   - <#else>
32 28 <a-col :span="${form_span}">
33 29 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
34   - </#if>
35 30 <#if po.classType =='date'>
36 31 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
37 32 <#elseif po.classType =='datetime'>
... ... @@ -117,20 +112,12 @@
117 112 },
118 113 labelCol: {
119 114 xs: { span: 24 },
120   - sm: { span: 6 },
  115 + sm: { span: 5 },
121 116 },
122 117 wrapperCol: {
123 118 xs: { span: 24 },
124 119 sm: { span: 16 },
125 120 },
126   - labelCol2: {
127   - xs: { span: 24 },
128   - sm: { span: 3 },
129   - },
130   - wrapperCol2: {
131   - xs: { span: 24 },
132   - sm: { span: 20 },
133   - },
134 121 <#include "/common/validatorRulesTemplate/sub.ftl">
135 122 confirmLoading: false,
136 123 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -158,7 +158,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
158 158 */
159 159 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
160 160 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
161   - @PutMapping(value = "/edit")
  161 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
162 162 public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
163 163 ${entityName?uncap_first}Service.update${entityName}(${entityName?uncap_first});
164 164 return Result.OK("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div class="p-4">
  3 + <!--引用表格-->
  4 + <BasicTable @register="registerTable" :rowSelection="rowSelection" :expandedRowKeys="expandedRowKeys" @expand="handleExpand" @fetch-success="onFetchSuccess">
  5 + <!--插槽:table标题-->
  6 + <template #tableTitle>
  7 + <a-button type="primary" @click="handleCreate" preIcon="ant-design:plus-outlined"> 新增</a-button>
  8 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  9 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  10 +
  11 + <a-dropdown v-if="selectedRowKeys.length > 0">
  12 + <template #overlay>
  13 + <a-menu>
  14 + <a-menu-item key="1" @click="batchHandleDelete">
  15 + <Icon icon="ant-design:delete-outlined"></Icon>
  16 + 删除
  17 + </a-menu-item>
  18 + </a-menu>
  19 + </template>
  20 + <a-button>批量操作
  21 + <Icon icon="ant-design:down-outlined"></Icon>
  22 + </a-button>
  23 + </a-dropdown>
  24 + </template>
  25 + <!--操作栏-->
  26 + <template #action="{ record }">
  27 + <TableAction :actions="getTableAction(record)"/>
  28 + </template>
  29 + </BasicTable>
  30 + <!--字典弹窗-->
  31 + <${entityName}Modal @register="registerModal" @success="handleSuccess"/>
  32 + </div>
  33 +</template>
  34 +
  35 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  36 + //ts语法
  37 + import {ref, computed, unref, toRaw, nextTick} from 'vue';
  38 + import {BasicTable, useTable, TableAction} from '/src/components/Table';
  39 + import {useModal} from '/src/components/Modal';
  40 + import { useListPage } from '/@/hooks/system/useListPage'
  41 + import ${entityName}Modal from './components/${entityName}Modal.vue';
  42 + import {columns} from './${entityName}.data';
  43 + import {list, delete${entityName}, batchDelete${entityName}, getExportUrl,getImportUrl, getChildList,getChildListBatch} from './${entityName}.api';
  44 +
  45 + const expandedRowKeys = ref([]);
  46 + //字典model
  47 + const [registerModal, {openModal}] = useModal();
  48 + //注册table数据
  49 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  50 + tableProps:{
  51 + title: '${tableVo.ftlDescription}',
  52 + columns,
  53 + canResize:false,
  54 + actionColumn: {
  55 + width: 120,
  56 + },
  57 + },
  58 + exportConfig: {
  59 + name:"${tableVo.ftlDescription}",
  60 + url: getExportUrl,
  61 + },
  62 + importConfig: {
  63 + url: getImportUrl,
  64 + success:importSuccess
  65 + },
  66 + })
  67 +
  68 + const [registerTable, {reload, collapseAll, updateTableDataRecord, findTableDataRecord,getDataSource},{ rowSelection, selectedRowKeys }] = tableContext
  69 +
  70 + /**
  71 + * 新增事件
  72 + */
  73 + function handleCreate() {
  74 + openModal(true, {
  75 + isUpdate: false,
  76 + });
  77 + }
  78 +
  79 + /**
  80 + * 编辑事件
  81 + */
  82 + async function handleEdit(record) {
  83 + openModal(true, {
  84 + record,
  85 + isUpdate: true,
  86 + });
  87 + }
  88 +
  89 + /**
  90 + * 详情
  91 + */
  92 + async function handleDetail(record) {
  93 + openModal(true, {
  94 + record,
  95 + isUpdate: true,
  96 + hideFooter: true,
  97 + });
  98 + }
  99 +
  100 + /**
  101 + * 删除事件
  102 + */
  103 + async function handleDelete(record) {
  104 + await delete${entityName}({id: record.id}, importSuccess);
  105 + }
  106 +
  107 + /**
  108 + * 批量删除事件
  109 + */
  110 + async function batchHandleDelete() {
  111 + const ids = selectedRowKeys.value.filter(item => !item.includes('loading'))
  112 + await batchDelete${entityName}({ids: ids}, importSuccess);
  113 + }
  114 + /**
  115 + * 导入
  116 + */
  117 + function importSuccess() {
  118 + reload() && (expandedRowKeys.value = []);
  119 + }
  120 + /**
  121 + * 添加下级
  122 + */
  123 + function handleAddSub(record) {
  124 + openModal(true, {
  125 + record,
  126 + isUpdate: false,
  127 + });
  128 + }
  129 + /**
  130 + * 成功回调
  131 + */
  132 + async function handleSuccess({isUpdate, values, expandedArr}) {
  133 + if (isUpdate) {
  134 + //编辑回调
  135 + updateTableDataRecord(values.id, values);
  136 + } else {
  137 + if(!values['pid']){
  138 + //新增根节点
  139 + reload();
  140 + }else{
  141 + //新增子集
  142 + expandedRowKeys.value = [];
  143 + for (let key of unref(expandedArr)) {
  144 + await expandTreeNode(key)
  145 + }
  146 + }
  147 + }
  148 + }
  149 +
  150 + /**
  151 + * 接口请求成功后回调
  152 + */
  153 + function onFetchSuccess(result) {
  154 + getDataByResult(result.items)&&loadDataByExpandedRows();
  155 + }
  156 + /**
  157 + * 根据已展开的行查询数据(用于保存后刷新时异步加载子级的数据)
  158 + */
  159 + async function loadDataByExpandedRows() {
  160 + if (unref(expandedRowKeys).length > 0) {
  161 + const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',')});
  162 + if (res.success && res.result.records.length>0) {
  163 + //已展开的数据批量子节点
  164 + let records = res.result.records
  165 + const listMap = new Map();
  166 + for (let item of records) {
  167 + let pid = item['pid'];
  168 + if (unref(expandedRowKeys).includes(pid)) {
  169 + let mapList = listMap.get(pid);
  170 + if (mapList == null) {
  171 + mapList = [];
  172 + }
  173 + mapList.push(item);
  174 + listMap.set(pid, mapList);
  175 + }
  176 + }
  177 + let childrenMap = listMap;
  178 + let fn = (list) => {
  179 + if(list) {
  180 + list.forEach(data => {
  181 + if (unref(expandedRowKeys).includes(data.id)) {
  182 + data.children = getDataByResult(childrenMap.get(data.id))
  183 + fn(data.children)
  184 + }
  185 + })
  186 + }
  187 + };
  188 + fn(getDataSource())
  189 + }
  190 + }
  191 + }
  192 + /**
  193 + * 处理数据集
  194 + */
  195 + function getDataByResult(result){
  196 + if(result && result.length>0){
  197 + return result.map(item=>{
  198 + //判断是否标记了带有子节点
  199 + if(item["hasChild"]=='1'){
  200 + let loadChild = { id: item.id+'_loadChild', name: 'loading...', isLoading: true }
  201 + item.children = [loadChild]
  202 + }
  203 + return item
  204 + })
  205 + }
  206 + }
  207 + /**
  208 + *树节点展开合并
  209 + * */
  210 + async function handleExpand(expanded, record) {
  211 + // 判断是否是展开状态,展开状态(expanded)并且存在子集(children)并且未加载过(isLoading)的就去查询子节点数据
  212 + if (expanded) {
  213 + expandedRowKeys.value.push(record.id)
  214 + if (record.children.length > 0 && !!record.children[0].isLoading) {
  215 + let result = await getChildList({pid: record.id});
  216 + result=result.records?result.records:result;
  217 + if (result && result.length > 0) {
  218 + record.children = getDataByResult(result);
  219 + } else {
  220 + record.children = null
  221 + record.hasChild = '0'
  222 + }
  223 + }
  224 + } else {
  225 + let keyIndex = expandedRowKeys.value.indexOf(record.id)
  226 + if (keyIndex >= 0) {
  227 + expandedRowKeys.value.splice(keyIndex, 1);
  228 + }
  229 + }
  230 + }
  231 + /**
  232 + *操作表格后处理树节点展开合并
  233 + * */
  234 + async function expandTreeNode(key) {
  235 + let record = findTableDataRecord(key)
  236 + expandedRowKeys.value.push(key);
  237 + let result = await getChildList({pid: key});
  238 + if (result && result.length > 0) {
  239 + record.children = getDataByResult(result);
  240 + } else {
  241 + record.children = null
  242 + record.hasChild = '0'
  243 + }
  244 + updateTableDataRecord(key, record);
  245 + }
  246 + /**
  247 + * 操作栏
  248 + */
  249 + function getTableAction(record) {
  250 + return [
  251 + {
  252 + label: '编辑',
  253 + onClick: handleEdit.bind(null, record),
  254 + },
  255 + {
  256 + label: '删除',
  257 + popConfirm: {
  258 + title: '确定删除吗?',
  259 + confirm: handleDelete.bind(null, record),
  260 + },
  261 + },
  262 + {
  263 + label: '添加下级',
  264 + onClick: handleAddSub.bind(null, {pid: record.id}),
  265 + }
  266 + ]
  267 + }
  268 +</script>
  269 +
  270 +<style scoped>
  271 +
  272 +</style>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from "/@/utils/http/axios";
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/rootList',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + delete${entityName} = '/sys/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 + loadTreeData = '/${entityPackage}/${entityName?uncap_first}/loadTreeRoot',
  13 + getChildList = '/${entityPackage}/${entityName?uncap_first}/childList',
  14 + getChildListBatch = '/${entityPackage}/${entityName?uncap_first}/getChildListBatch',
  15 +}
  16 +/**
  17 + * 导出api
  18 + * @param params
  19 + */
  20 +export const getExportUrl = Api.exportXls;
  21 +/**
  22 + * 导入api
  23 + * @param params
  24 + */
  25 +export const getImportUrl = Api.importExcel;
  26 +/**
  27 + * 列表接口
  28 + * @param params
  29 + */
  30 +export const list = (params) =>
  31 + defHttp.get({url: Api.list, params});
  32 +/**
  33 + * 删除
  34 + */
  35 +export const delete${entityName} = (params,handleSuccess) => {
  36 + return defHttp.delete({url: Api.delete${entityName}, params}, {joinParamsToUrl: true}).then(() => {
  37 + handleSuccess();
  38 + });
  39 +}
  40 +/**
  41 + * 批量删除
  42 + * @param params
  43 + */
  44 +export const batchDelete${entityName} = (params, handleSuccess) => {
  45 + Modal.confirm({
  46 + title: '确认删除',
  47 + content: '是否删除选中数据',
  48 + okText: '确认',
  49 + cancelText: '取消',
  50 + onOk: () => {
  51 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  52 + handleSuccess();
  53 + });
  54 + }
  55 + });
  56 +}
  57 +/**
  58 + * 保存或者更新
  59 + * @param params
  60 + */
  61 +export const saveOrUpdateDict = (params, isUpdate) => {
  62 + let url = isUpdate ? Api.edit : Api.save;
  63 + return defHttp.post({url: url, params});
  64 +}
  65 +/**
  66 + * 查询全部树形节点数据
  67 + * @param params
  68 + */
  69 +export const loadTreeData = (params) =>
  70 + defHttp.get({url: Api.loadTreeData,params});
  71 +/**
  72 + * 查询子节点数据
  73 + * @param params
  74 + */
  75 +export const getChildList = (params) =>
  76 + defHttp.get({url: Api.getChildList, params});
  77 +/**
  78 + * 批量查询子节点数据
  79 + * @param params
  80 + */
  81 +export const getChildListBatch = (params) =>
  82 + defHttp.get({url: Api.getChildListBatch, params},{isTransformResponse:false});
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +//列表数据
  6 +export const columns: BasicColumn[] = [
  7 + <#list columns as po>
  8 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  9 + {
  10 + title: '${po.filedComment}',
  11 + align:"center",
  12 + <#if po.sort=='Y'>
  13 + sorter: true,
  14 + </#if>
  15 + <#if po.classType=='date'>
  16 + dataIndex: '${po.fieldName}',
  17 + customRender:({text}) =>{
  18 + return !text?"":(text.length>10?text.substr(0,10):text)
  19 + },
  20 + <#elseif po.fieldDbType=='Blob'>
  21 + dataIndex: '${po.fieldName}String'
  22 + <#elseif po.classType=='umeditor'>
  23 + dataIndex: '${po.fieldName}',
  24 + slots: { customRender: 'htmlSlot' },
  25 + <#elseif po.classType=='pca'>
  26 + dataIndex: '${po.fieldName}',
  27 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  28 + <#elseif po.classType=='file'>
  29 + dataIndex: '${po.fieldName}',
  30 + slots: { customRender: 'fileSlot' },
  31 + <#elseif po.classType=='image'>
  32 + dataIndex: '${po.fieldName}',
  33 + customRender:render.renderAvatar,
  34 + <#elseif po.classType=='switch'>
  35 + dataIndex: '${po.fieldName}',
  36 +<#assign switch_extend_arr=['Y','N']>
  37 +<#if po.dictField?default("")?contains("[")>
  38 +<#assign switch_extend_arr=po.dictField?eval>
  39 +</#if>
  40 +<#list switch_extend_arr as a>
  41 +<#if a_index == 0>
  42 +<#assign switch_extend_arr1=a>
  43 +<#else>
  44 +<#assign switch_extend_arr2=a>
  45 +</#if>
  46 +</#list>
  47 + customRender:({text}) => {
  48 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  49 + },
  50 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  51 + dataIndex: '${po.fieldName}_dictText'
  52 + <#elseif po.classType=='cat_tree'>
  53 + dataIndex: '${po.fieldName}',
  54 + <#if po.dictText?default("")?trim?length == 0>
  55 + customRender:({text}) => {
  56 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  57 + },
  58 + <#else>
  59 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  60 + </#if>
  61 + <#else>
  62 + dataIndex: '${po.fieldName}'
  63 + </#if>
  64 + },
  65 + </#if>
  66 + </#list>
  67 +];
  68 +//查询数据
  69 +export const searchFormSchema: FormSchema[] = [
  70 +<#-- 开始循环 -->
  71 +<#list columns as po>
  72 +<#if po.fieldDbName=='bpm_status'>
  73 + <#assign bpm_flag=true>
  74 +</#if>
  75 +<#if po.isQuery=='Y'>
  76 +<#assign query_flag=true>
  77 + <#assign query_field_dictCode="">
  78 + <#if po.dictTable?default("")?trim?length gt 1>
  79 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  80 + <#elseif po.dictField?default("")?trim?length gt 1>
  81 + <#assign query_field_dictCode="${po.dictField}">
  82 + </#if>
  83 +<#if po.queryMode=='single'>
  84 + {
  85 + label: "${po.filedComment}",
  86 + field: "${po.fieldName}",
  87 +<#if po.classType=='sel_search'>
  88 + component: 'JSearchSelect',
  89 + componentProps:{
  90 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  91 + },
  92 +<#elseif po.classType=='sel_user'>
  93 + component: 'JSelectUserByDept',
  94 +<#elseif po.classType=='switch'>
  95 + component: 'JSwitch',
  96 + componentProps:{
  97 + <#if po.dictField != 'is_open'>
  98 + options:"${po.dictField}"
  99 + </#if>
  100 + },
  101 + <#elseif po.classType=='sel_depart'>
  102 + component: 'JSelectDept',
  103 + <#elseif po.classType=='list_multi'>
  104 + component: 'JMultiSelectTag',//暂无该组件
  105 + componentProps:{
  106 + dictCode:"query_field_dictCode?default("")"
  107 + },
  108 + <#elseif po.classType=='cat_tree'>
  109 + component: 'JCategorySelect',
  110 + componentProps:{
  111 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  112 + },
  113 +<#elseif po.classType=='date'>
  114 + component: 'DatePicker',
  115 +<#elseif po.classType=='datetime'>
  116 + component: 'DatePicker',
  117 + componentProps: {
  118 + showTime:true
  119 + },
  120 +<#elseif po.classType=='pca'>
  121 + component: 'JAreaLinkage',
  122 +<#elseif po.classType=='popup'>
  123 + component: 'JPopup',
  124 + componentProps: ({ formActionType }) => {
  125 + const {setFieldsValue} = formActionType;
  126 + return{
  127 + setFieldsValue:setFieldsValue,
  128 + code:"${po.dictTable}",
  129 + fieldConfig:"${po.dictField}",
  130 + multi:${po.extendParams.popupMulti?c},
  131 + }
  132 + },
  133 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  134 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  135 + component: 'JDictSelectTag',
  136 + componentProps:{
  137 + <#if po.dictTable?default("")?trim?length gt 1>
  138 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
  139 + <#elseif po.dictField?default("")?trim?length gt 1>
  140 + dictCode:"${po.dictField}"
  141 + </#if>
  142 + },
  143 +<#else>
  144 + component: 'Input',
  145 +</#if>
  146 + colProps: {span: 6},
  147 + },
  148 +<#else>
  149 + {
  150 + label: "${po.filedComment}",
  151 + field: "${po.fieldName}",
  152 +<#if po.classType=='date'>
  153 + component: 'RangePicker',
  154 +<#elseif po.classType=='datetime'>
  155 + component: 'RangePicker',
  156 + componentProps: {
  157 + showTime:true
  158 + },
  159 +<#else>
  160 + component: 'Input', //TODO 范围查询
  161 +</#if>
  162 + colProps: {span: 6},
  163 + },
  164 +</#if>
  165 +</#if>
  166 +</#list>
  167 +<#-- 结束循环 -->
  168 +];
  169 +//表单数据
  170 +export const formSchema: FormSchema[] = [
  171 +<#assign form_cat_tree = false>
  172 +<#assign form_cat_back = "">
  173 +<#assign bpm_flag=false>
  174 +<#list columns as po><#rt/>
  175 +<#if po.fieldDbName=='bpm_status'>
  176 + <#assign bpm_flag=true>
  177 +</#if>
  178 +<#if po.isShow =='Y'>
  179 +<#assign form_field_dictCode="">
  180 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  181 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  182 + <#elseif po.dictField?default("")?trim?length gt 1>
  183 + <#assign form_field_dictCode="${po.dictField}">
  184 + </#if>
  185 + {
  186 + label: '${po.filedComment}',
  187 + field: '${po.fieldName}',
  188 + <#if po.classType =='date'>
  189 + component: 'DatePicker',
  190 + <#elseif po.fieldType =='datetime'>
  191 + component: 'DatePicker',
  192 + componentProps: {
  193 + showTime:true
  194 + },
  195 + <#elseif po.fieldType =='time'>
  196 + component: 'TimePicker',
  197 + <#elseif po.classType =='popup'>
  198 + component: 'JPopup',
  199 + componentProps: ({ formActionType }) => {
  200 + const {setFieldsValue} = formActionType;
  201 + return{
  202 + setFieldsValue:setFieldsValue,
  203 + code:"${po.dictTable}",
  204 + fieldConfig:${po.dictField},
  205 + multi:${po.extendParams.popupMulti?c},
  206 + }
  207 + }
  208 + <#elseif po.classType =='sel_depart'>
  209 + component: 'JSelectDept',
  210 + <#elseif po.classType =='switch'>
  211 + component: 'JSwitch',
  212 + componentProps:{
  213 + <#if po.dictField != 'is_open'>
  214 + options:${po.dictField}
  215 + </#if>
  216 + }
  217 + <#elseif po.classType =='pca'>
  218 + component: 'JAreaLinkage',
  219 + <#elseif po.classType =='markdown'>
  220 + component: 'JMarkdownEditor',//注意string转换问题
  221 + <#elseif po.classType =='password'>
  222 + component: 'InputPassword',
  223 + <#elseif po.classType =='sel_user'>
  224 + component: 'JSelectUserByDept',
  225 + componentProps:{
  226 + labelKey:'realname',
  227 + }
  228 + <#elseif po.classType =='textarea'>
  229 + component: 'InputTextArea',//TODO 注意string转换问题
  230 + <#elseif po.classType=='list' || po.classType=='radio'>
  231 + component: 'JDictSelectTag',
  232 + componentProps:{
  233 + dictCode:"${form_field_dictCode}"
  234 + }
  235 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  236 + component: 'JMultiSelectTag',//TODO 暂无该组件
  237 + componentProps:{
  238 + dictCode:"${form_field_dictCode}"
  239 + }
  240 + <#elseif po.classType=='sel_search'>
  241 + component: 'JSearchSelect',
  242 + componentProps:{
  243 + dict:"${form_field_dictCode}"
  244 + }
  245 +<#elseif po.classType=='cat_tree'>
  246 + <#assign form_cat_tree = true>
  247 + component: 'JCategorySelect',
  248 + componentProps:{
  249 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  250 + }
  251 + <#if po.dictText?default("")?trim?length gt 1>
  252 + <#assign form_cat_back = "${po.dictText}">
  253 + </#if>
  254 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  255 + component: 'InputNumber',
  256 + <#elseif po.classType=='file'>
  257 + component: 'JUpload',
  258 + componentProps:{
  259 + <#if po.uploadnum??>
  260 + maxCount:${po.uploadnum}
  261 + </#if>
  262 + }
  263 + <#elseif po.classType=='image'>
  264 + component: 'JImageUpload',
  265 + componentProps:{
  266 + <#if po.uploadnum??>
  267 + fileMax:${po.uploadnum}
  268 + </#if>
  269 + }
  270 + <#elseif po.classType=='umeditor'>
  271 + component: 'JCodeEditor', //TODO String后缀暂未添加
  272 + <#elseif po.classType == 'sel_tree'>
  273 + component: 'JTreeSelect',
  274 + componentProps:{
  275 + <#if po.dictText??>
  276 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  277 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  278 + <#elseif po.dictText?split(',')[1]??>
  279 + pidField:"${po.dictText?split(',')[1]}",
  280 + <#elseif po.dictText?split(',')[3]??>
  281 + hasChildField:"${po.dictText?split(',')[3]}",
  282 + </#if>
  283 + </#if>
  284 + pidValue:"${po.dictField}",
  285 + }
  286 + <#else>
  287 + component: 'Input',
  288 + </#if>
  289 + <#include "/common/utils.ftl">
  290 + <#if po.isShow == 'Y' && poHasCheck(po)>
  291 + dynamicRules: ({model,schema}) => {
  292 + <#if po.fieldName != 'id'>
  293 + <#assign fieldValidType = po.fieldValidType!''>
  294 + return [
  295 + <#-- 非空校验 -->
  296 + <#if po.nullable == 'N' || fieldValidType == '*'>
  297 + { required: true, message: '请输入${po.filedComment}!'},
  298 + <#elseif fieldValidType!=''>
  299 + { required: false},
  300 + </#if>
  301 + <#-- 唯一校验 -->
  302 + <#if fieldValidType == 'only'>
  303 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  304 + <#-- 6到16位数字 -->
  305 + <#elseif fieldValidType == 'n6-16'>
  306 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  307 + <#-- 6到16位任意字符 -->
  308 + <#elseif fieldValidType == '*6-16'>
  309 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  310 + <#-- 6到18位字符串 -->
  311 + <#elseif fieldValidType == 's6-18'>
  312 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  313 + <#-- 网址 -->
  314 + <#elseif fieldValidType == 'url'>
  315 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  316 + <#-- 电子邮件 -->
  317 + <#elseif fieldValidType == 'e'>
  318 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  319 + <#-- 手机号码 -->
  320 + <#elseif fieldValidType == 'm'>
  321 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  322 + <#-- 邮政编码 -->
  323 + <#elseif fieldValidType == 'p'>
  324 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  325 + <#-- 字母 -->
  326 + <#elseif fieldValidType == 's'>
  327 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  328 + <#-- 数字 -->
  329 + <#elseif fieldValidType == 'n'>
  330 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  331 + <#-- 整数 -->
  332 + <#elseif fieldValidType == 'z'>
  333 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  334 + <#-- 金额 -->
  335 + <#elseif fieldValidType == 'money'>
  336 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  337 + <#-- 正则校验 -->
  338 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  339 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  340 + <#-- 无校验 -->
  341 + <#else>
  342 + <#t>
  343 + </#if>
  344 + ];
  345 + </#if>
  346 + },
  347 + </#if>
  348 + <#if po.readonly=='Y'>
  349 + dynamicDisabled:true
  350 + </#if>
  351 + },
  352 +</#if>
  353 +</#list>
  354 +];
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
  3 + <BasicForm @register="registerForm"/>
  4 + </BasicModal>
  5 +</template>
  6 +<script lang="ts" setup>
  7 + import {ref, computed, unref} from 'vue';
  8 + import {BasicModal, useModalInner} from '/src/components/Modal';
  9 + import {BasicForm, useForm} from '/src/components/Form';
  10 + import {formSchema} from '../${entityName?uncap_first}.data';
  11 + import {loadTreeData, saveOrUpdateDict} from '../${entityName?uncap_first}.api';
  12 + // 获取emit
  13 + const emit = defineEmits(['register', 'success']);
  14 + const isUpdate = ref(true);
  15 + const expandedRowKeys = ref([]);
  16 + const treeData = ref([]);
  17 + //表单配置
  18 + const [registerForm, {resetFields, setFieldsValue, validate, updateSchema}] = useForm({
  19 + schemas: formSchema,
  20 + showActionButtonGroup: false,
  21 + labelCol: {
  22 + xs: { span: 24 },
  23 + sm: { span: 4 },
  24 + },
  25 + wrapperCol: {
  26 + xs: { span: 24 },
  27 + sm: { span: 18 },
  28 + },
  29 + });
  30 + //表单赋值
  31 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  32 + //重置表单
  33 + await resetFields();
  34 + expandedRowKeys.value = [];
  35 + setModalProps({confirmLoading: false, minHeight: 80});
  36 + isUpdate.value = !!data?.isUpdate;
  37 + if (data?.record) {
  38 + //表单赋值
  39 + await setFieldsValue({
  40 + ...data.record,
  41 + });
  42 + }
  43 + //父级节点树信息
  44 + treeData.value = await loadTreeData({'async': false,'pcode':''});
  45 + updateSchema({
  46 + field: 'pid',
  47 + componentProps: {treeData},
  48 + });
  49 + });
  50 + //设置标题
  51 + const getTitle = computed(() => (!unref(isUpdate) ? '新增字典' : '编辑字典'));
  52 +
  53 + /**
  54 + * 根据pid获取展开的节点
  55 + * @param pid
  56 + * @param arr
  57 + */
  58 + function getExpandKeysByPid(pid,arr){
  59 + if(pid && arr && arr.length>0){
  60 + for(let i=0;i<arr.length;i++){
  61 + if(arr[i].key==pid && unref(expandedRowKeys).indexOf(pid)<0){
  62 + expandedRowKeys.value.push(arr[i].key);
  63 + getExpandKeysByPid(arr[i]['parentId'],unref(treeData))
  64 + }else{
  65 + getExpandKeysByPid(pid,arr[i].children)
  66 + }
  67 + }
  68 + }
  69 + }
  70 + //表单提交事件
  71 + async function handleSubmit() {
  72 + try {
  73 + let values = await validate();
  74 + setModalProps({confirmLoading: true});
  75 + //提交表单
  76 + await saveOrUpdateDict(values, isUpdate.value);
  77 + //关闭弹窗
  78 + closeModal();
  79 + //展开的节点信息
  80 + await getExpandKeysByPid(values['pid'],unref(treeData))
  81 + //刷新列表(isUpdate:是否编辑;values:表单信息;expandedArr:展开的节点信息)
  82 + emit('success', {isUpdate: unref(isUpdate), values:{...values},expandedArr: unref(expandedRowKeys).reverse()});
  83 + } finally {
  84 + setModalProps({confirmLoading: false});
  85 + }
  86 + }
  87 +</script>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -103,7 +103,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
103 103 */
104 104 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
105 105 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
106   - @PutMapping(value = "/edit")
  106 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
107 107 public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
108 108 ${entityName?uncap_first}Service.updateById(${entityName?uncap_first});
109 109 return Result.OK("编辑成功!");
... ... @@ -194,7 +194,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
194 194 */
195 195 @AutoLog(value = "${sub.ftlDescription}-编辑")
196 196 @ApiOperation(value="${sub.ftlDescription}-编辑", notes="${sub.ftlDescription}-编辑")
197   - @PutMapping(value = "/edit${sub.entityName}")
  197 + @RequestMapping(value = "/edit${sub.entityName}", method = {RequestMethod.PUT,RequestMethod.POST})
198 198 public Result<?> edit${sub.entityName}(@RequestBody ${sub.entityName} ${sub.entityName?uncap_first}) {
199 199 ${sub.entityName?uncap_first}Service.updateById(${sub.entityName?uncap_first});
200 200 return Result.OK("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}List.vuei
... ... @@ -371,7 +371,7 @@
371 371 this.$set(this.dictOptions, '${po.fieldName}', res.result)
372 372 }
373 373 })
374   - <#elseif po.classType=='sel_search' || po.classType=='list_multi' || po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  374 + <#elseif po.classType=='radio' || po.classType=='checkbox'>
375 375 <#assign list_field_dictCode="">
376 376 <#if po.dictTable?default("")?trim?length gt 1>
377 377 <#assign list_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue/[1-n]List.vuei
... ... @@ -15,8 +15,6 @@
15 15 @change="handleImportExcel">
16 16 <a-button type="primary" icon="import">导入</a-button>
17 17 </a-upload>
18   - <!-- 高级查询区域 -->
19   - <j-super-query :fieldList="superFieldList" ref="superQueryModal" @handleSuperQuery="handleSuperQuery"></j-super-query>
20 18 <a-dropdown v-if="selectedRowKeys.length > 0">
21 19 <a-menu slot="overlay">
22 20 <a-menu-item key="1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
... ... @@ -192,12 +190,10 @@
192 190 </#if>
193 191 </#if>
194 192 </#list>
195   - },
196   - superFieldList:[],
  193 + }
197 194 }
198 195 },
199 196 created() {
200   - this.getSuperFieldList();
201 197 },
202 198 computed: {
203 199 importExcelUrl(){
... ... @@ -209,15 +205,8 @@
209 205 this.dataSource=[]
210 206 this.selectedRowKeys=[]
211 207 this.ipagination.current = 1
212   - },
213   - getSuperFieldList(){
214   - <#include "/common/utils.ftl">
215   - let fieldList=[];
216   - <#list columns as po>
217   - fieldList.push(${superQueryFieldList(po)})
218   - </#list>
219   - this.superFieldList = fieldList
220 208 }
  209 +
221 210 }
222 211 }
223 212 </script>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 +<#assign list_need_category=false>
  4 +<#assign list_need_pca=false>
  5 +<#assign bpm_flag=false>
  6 +
  7 +<#-- 开始循环 -->
  8 +<#list columns as po>
  9 +<#if po.fieldDbName=='bpm_status'>
  10 + <#assign bpm_flag=true>
  11 +</#if>
  12 +<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  13 +<#assign list_need_category=true>
  14 +</#if>
  15 +<#if po.classType=='pca'>
  16 +<#assign list_need_pca=true>
  17 +</#if>
  18 +</#list>
  19 +<#-- 结束循环 -->
  20 + <!--引用表格-->
  21 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  22 + <!--插槽:table标题-->
  23 + <template #tableTitle>
  24 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  25 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  26 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  27 + <a-dropdown v-if="selectedRowKeys.length > 0">
  28 + <template #overlay>
  29 + <a-menu>
  30 + <a-menu-item key="1" @click="batchHandleDelete">
  31 + <Icon icon="ant-design:delete-outlined"></Icon>
  32 + 删除
  33 + </a-menu-item>
  34 + </a-menu>
  35 + </template>
  36 + <a-button>批量操作
  37 + <Icon icon="mdi:chevron-down"></Icon>
  38 + </a-button>
  39 + </a-dropdown>
  40 + </template>
  41 + <!--操作栏-->
  42 + <template #action="{ record }">
  43 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  44 + </template>
  45 + <!--字段回显插槽-->
  46 + <template #htmlSlot="{text}">
  47 + <div v-html="text"></div>
  48 + </template>
  49 + <template #fileSlot="{text}">
  50 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  51 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  52 + </template>
  53 + </BasicTable>
  54 + <!--子表表格tab-->
  55 + <a-tabs defaultActiveKey="1">
  56 + <#assign sub_seq=1>
  57 + <#list subTables as sub>
  58 + <a-tab-pane tab="${sub.ftlDescription}" key="${sub_seq}" <#if sub_seq gt 1>forceRender</#if>>
  59 + <${sub.entityName}List/>
  60 + </a-tab-pane>
  61 + <#assign sub_seq=sub_seq+1>
  62 + </#list>
  63 + </a-tabs>
  64 + <!-- 表单区域 -->
  65 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  66 + </div>
  67 +</template>
  68 +
  69 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  70 + import {ref, computed, unref,provide} from 'vue';
  71 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  72 + import { useListPage } from '/@/hooks/system/useListPage'
  73 + import {useModal} from '/@/components/Modal';
  74 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  75 +<#list subTables as sub>
  76 + import ${sub.entityName}List from './${sub.entityName}List.vue'
  77 +</#list>
  78 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  79 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  80 + <#if list_need_category>
  81 + import { loadCategoryData } from '/@/api/common/api'
  82 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  83 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  84 + </#if>
  85 + //注册model
  86 + const [registerModal, {openModal}] = useModal();
  87 + //注册table数据
  88 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  89 + tableProps:{
  90 + title: '${tableVo.ftlDescription}',
  91 + api: list,
  92 + columns,
  93 + canResize:false,
  94 + rowSelection: {type: 'radio'},
  95 + formConfig: {
  96 + schemas: searchFormSchema,
  97 + fieldMapToTime: [
  98 + <#list columns as po>
  99 + <#if po.isQuery=='Y'>
  100 + <#if po.queryMode!='single'>
  101 + <#if po.classType=='date'>
  102 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
  103 + <#elseif po.classType=='datetime'>
  104 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
  105 + </#if>
  106 + </#if>
  107 + </#if>
  108 + </#list>
  109 + ],
  110 + },
  111 + actionColumn: {
  112 + width: 120,
  113 + },
  114 + pagination:{
  115 + current: 1,
  116 + pageSize: 5,
  117 + pageSizeOptions: ['5', '10', '20'],
  118 + }
  119 + },
  120 + exportConfig: {
  121 + name:"${tableVo.ftlDescription}",
  122 + url: getExportUrl,
  123 + },
  124 + importConfig: {
  125 + url: getImportUrl
  126 + },
  127 + })
  128 +
  129 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  130 +
  131 + const mainId = computed(() => (unref(selectedRowKeys).length > 0 ? unref(selectedRowKeys)[0] : ''));
  132 + //下发 mainId,子组件接收
  133 + provide('mainId', mainId);
  134 + /**
  135 + * 新增事件
  136 + */
  137 + function handleAdd() {
  138 + openModal(true, {
  139 + isUpdate: false,
  140 + showFooter: true,
  141 + });
  142 + }
  143 + /**
  144 + * 编辑事件
  145 + */
  146 + function handleEdit(record: Recordable) {
  147 + openModal(true, {
  148 + record,
  149 + isUpdate: true,
  150 + showFooter: true,
  151 + });
  152 + }
  153 + /**
  154 + * 详情
  155 + */
  156 + function handleDetail(record: Recordable) {
  157 + openModal(true, {
  158 + record,
  159 + isUpdate: true,
  160 + showFooter: false,
  161 + });
  162 + }
  163 + /**
  164 + * 删除事件
  165 + */
  166 + async function handleDelete(record) {
  167 + await deleteOne({id: record.id}, reload);
  168 + }
  169 + /**
  170 + * 批量删除事件
  171 + */
  172 + async function batchHandleDelete() {
  173 + await batchDelete({ids: selectedRowKeys.value}, reload);
  174 + }
  175 + /**
  176 + * 成功回调
  177 + */
  178 + function handleSuccess() {
  179 + reload();
  180 + }
  181 + /**
  182 + * 操作栏
  183 + */
  184 + function getTableAction(record){
  185 + return [
  186 + {
  187 + label: '编辑',
  188 + onClick: handleEdit.bind(null, record),
  189 + }
  190 + ]
  191 + }
  192 + /**
  193 + * 下拉操作栏
  194 + */
  195 + function getDropDownAction(record){
  196 + return [
  197 + {
  198 + label: '详情',
  199 + onClick: handleDetail.bind(null, record),
  200 + }, {
  201 + label: '删除',
  202 + popConfirm: {
  203 + title: '是否确认删除',
  204 + confirm: handleDelete.bind(null, record),
  205 + }
  206 + }
  207 + ]
  208 + }
  209 + <#if list_need_category>
  210 + /**
  211 + * 初始化字典配置
  212 + */
  213 + function initDictConfig(){
  214 + <#list columns as po>
  215 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  216 + <#if po.classType=='cat_tree' && list_need_category==true>
  217 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  218 + if (res) {
  219 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  220 + if(!allDictDate['${po.dictField?default("")}']){
  221 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  222 + }
  223 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  224 + }
  225 + })
  226 + </#if>
  227 + </#if>
  228 + </#list>
  229 + }
  230 + initDictConfig();
  231 + </#if>
  232 +</script>
  233 +
  234 +<style scoped>
  235 +
  236 +</style>
0 237 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +<#list subTables as sub><#rt/>
  13 + ${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/list${sub.entityName}ByMainId',
  14 + ${sub.entityName?uncap_first}Save='/${entityPackage}/${entityName?uncap_first}/add${sub.entityName}',
  15 + ${sub.entityName?uncap_first}Edit='/${entityPackage}/${entityName?uncap_first}/edit${sub.entityName}',
  16 + ${sub.entityName?uncap_first}Delete = '/${entityPackage}/${entityName?uncap_first}/delete${sub.entityName}',
  17 + ${sub.entityName?uncap_first}DeleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch${sub.entityName}',
  18 + ${sub.entityName?uncap_first}ExportXlsUrl = '/${entityPackage}/${entityName?uncap_first}/export${sub.entityName}',
  19 + ${sub.entityName?uncap_first}ImportUrl = '/${entityPackage}/${entityName?uncap_first}/import${sub.entityName}',
  20 +</#list>
  21 +}
  22 +/**
  23 + * 导出api
  24 + * @param params
  25 + */
  26 +export const getExportUrl = Api.exportXls;
  27 +
  28 +/**
  29 + * 导入api
  30 + */
  31 +export const getImportUrl = Api.importExcel;
  32 +/**
  33 + * 列表接口
  34 + * @param params
  35 + */
  36 +export const list = (params) =>
  37 + defHttp.get({url: Api.list, params});
  38 +
  39 +/**
  40 + * 删除单个
  41 + */
  42 +export const deleteOne = (params,handleSuccess) => {
  43 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  44 + handleSuccess();
  45 + });
  46 +}
  47 +/**
  48 + * 批量删除
  49 + * @param params
  50 + */
  51 +export const batchDelete = (params, handleSuccess) => {
  52 + Modal.confirm({
  53 + title: '确认删除',
  54 + content: '是否删除选中数据',
  55 + okText: '确认',
  56 + cancelText: '取消',
  57 + onOk: () => {
  58 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  59 + handleSuccess();
  60 + });
  61 + }
  62 + });
  63 +}
  64 +/**
  65 + * 保存或者更新
  66 + * @param params
  67 + */
  68 +export const saveOrUpdate = (params, isUpdate) => {
  69 + let url = isUpdate ? Api.edit : Api.save;
  70 + return defHttp.post({url: url, params});
  71 +}
  72 +<#list subTables as sub><#rt/>
  73 +/**
  74 + * 列表接口
  75 + * @param params
  76 + */
  77 +export const ${sub.entityName?uncap_first}List = (params) =>
  78 + defHttp.get({url: Api.${sub.entityName?uncap_first}List, params});
  79 +
  80 +/**
  81 + * 删除单个
  82 + */
  83 +export const ${sub.entityName?uncap_first}Delete = (params,handleSuccess) => {
  84 + return defHttp.delete({url: Api.${sub.entityName?uncap_first}Delete, params}, {joinParamsToUrl: true}).then(() => {
  85 + handleSuccess();
  86 + });
  87 +}
  88 +/**
  89 + * 批量删除
  90 + * @param params
  91 + */
  92 +export const ${sub.entityName?uncap_first}DeleteBatch = (params, handleSuccess) => {
  93 + Modal.confirm({
  94 + title: '确认删除',
  95 + content: '是否删除选中数据',
  96 + okText: '确认',
  97 + cancelText: '取消',
  98 + onOk: () => {
  99 + return defHttp.delete({url: Api.${sub.entityName?uncap_first}DeleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  100 + handleSuccess();
  101 + });
  102 + }
  103 + });
  104 +}
  105 +/**
  106 + * 保存或者更新
  107 + * @param params
  108 + */
  109 +export const ${sub.entityName?uncap_first}SaveOrUpdate = (params, isUpdate) => {
  110 + let url = isUpdate ? Api.${sub.entityName?uncap_first}Edit : Api.${sub.entityName?uncap_first}Save;
  111 + return defHttp.post({url: url, params});
  112 +}
  113 +</#list>
0 114 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +//列表数据
  6 +export const columns: BasicColumn[] = [
  7 + <#list columns as po>
  8 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  9 + {
  10 + title: '${po.filedComment}',
  11 + align:"center",
  12 + <#if po.sort=='Y'>
  13 + sorter: true,
  14 + </#if>
  15 + <#if po.classType=='date'>
  16 + dataIndex: '${po.fieldName}',
  17 + customRender:({text}) =>{
  18 + return !text?"":(text.length>10?text.substr(0,10):text)
  19 + },
  20 + <#elseif po.fieldDbType=='Blob'>
  21 + dataIndex: '${po.fieldName}String'
  22 + <#elseif po.classType=='umeditor'>
  23 + dataIndex: '${po.fieldName}',
  24 + slots: { customRender: 'htmlSlot' },
  25 + <#elseif po.classType=='pca'>
  26 + dataIndex: '${po.fieldName}',
  27 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  28 + <#elseif po.classType=='file'>
  29 + dataIndex: '${po.fieldName}',
  30 + slots: { customRender: 'fileSlot' },
  31 + <#elseif po.classType=='image'>
  32 + dataIndex: '${po.fieldName}',
  33 + customRender:render.renderAvatar,
  34 + <#elseif po.classType=='switch'>
  35 + dataIndex: '${po.fieldName}',
  36 +<#assign switch_extend_arr=['Y','N']>
  37 +<#if po.dictField?default("")?contains("[")>
  38 +<#assign switch_extend_arr=po.dictField?eval>
  39 +</#if>
  40 +<#list switch_extend_arr as a>
  41 +<#if a_index == 0>
  42 +<#assign switch_extend_arr1=a>
  43 +<#else>
  44 +<#assign switch_extend_arr2=a>
  45 +</#if>
  46 +</#list>
  47 + customRender:({text}) => {
  48 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  49 + },
  50 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  51 + dataIndex: '${po.fieldName}_dictText'
  52 + <#elseif po.classType=='cat_tree'>
  53 + dataIndex: '${po.fieldName}',
  54 + <#if po.dictText?default("")?trim?length == 0>
  55 + customRender:({text}) => {
  56 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  57 + },
  58 + <#else>
  59 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  60 + </#if>
  61 + <#else>
  62 + dataIndex: '${po.fieldName}'
  63 + </#if>
  64 + },
  65 + </#if>
  66 + </#list>
  67 +];
  68 +//查询数据
  69 +export const searchFormSchema: FormSchema[] = [
  70 +<#-- 开始循环 -->
  71 +<#list columns as po>
  72 +<#if po.isQuery=='Y'>
  73 +<#assign query_flag=true>
  74 + <#assign query_field_dictCode="">
  75 + <#if po.dictTable?default("")?trim?length gt 1>
  76 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  77 + <#elseif po.dictField?default("")?trim?length gt 1>
  78 + <#assign query_field_dictCode="${po.dictField}">
  79 + </#if>
  80 +<#if po.queryMode=='single'>
  81 + {
  82 + label: "${po.filedComment}",
  83 + field: "${po.fieldName}",
  84 +<#if po.classType=='sel_search'>
  85 + component: 'JSearchSelect',
  86 + componentProps:{
  87 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  88 + },
  89 +<#elseif po.classType=='sel_user'>
  90 + component: 'JSelectUserByDept',
  91 +<#elseif po.classType=='switch'>
  92 + component: 'JSwitch',
  93 + componentProps:{
  94 + <#if po.dictField != 'is_open'>
  95 + options:"${po.dictField}"
  96 + </#if>
  97 + },
  98 + <#elseif po.classType=='sel_depart'>
  99 + component: 'JSelectDept',
  100 + <#elseif po.classType=='list_multi'>
  101 + component: 'JMultiSelectTag',//暂无该组件
  102 + componentProps:{
  103 + dictCode:"query_field_dictCode?default("")"
  104 + },
  105 + <#elseif po.classType=='cat_tree'>
  106 + component: 'JCategorySelect',
  107 + componentProps:{
  108 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  109 + },
  110 +<#elseif po.classType=='date'>
  111 + component: 'DatePicker',
  112 +<#elseif po.classType=='datetime'>
  113 + component: 'DatePicker',
  114 + componentProps: {
  115 + showTime:true
  116 + },
  117 +<#elseif po.classType=='pca'>
  118 + component: 'JAreaLinkage',
  119 +<#elseif po.classType=='popup'>
  120 + component: 'JPopup',
  121 + componentProps: ({ formActionType }) => {
  122 + const {setFieldsValue} = formActionType;
  123 + return{
  124 + setFieldsValue:setFieldsValue,
  125 + code:"${po.dictTable}",
  126 + fieldConfig:"${po.dictField}",
  127 + multi:${po.extendParams.popupMulti?c},
  128 + }
  129 + },
  130 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  131 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  132 + component: 'JDictSelectTag',
  133 + componentProps:{
  134 + <#if po.dictTable?default("")?trim?length gt 1>
  135 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
  136 + <#elseif po.dictField?default("")?trim?length gt 1>
  137 + dictCode:"${po.dictField}"
  138 + </#if>
  139 + },
  140 +<#else>
  141 + component: 'Input',
  142 +</#if>
  143 + colProps: {span: 6},
  144 + },
  145 +<#else>
  146 + {
  147 + label: "${po.filedComment}",
  148 + field: "${po.fieldName}",
  149 +<#if po.classType=='date'>
  150 + component: 'RangePicker',
  151 +<#elseif po.classType=='datetime'>
  152 + component: 'RangePicker',
  153 + componentProps: {
  154 + showTime:true
  155 + },
  156 +<#else>
  157 + component: 'Input', //TODO 范围查询
  158 +</#if>
  159 + colProps: {span: 6},
  160 + },
  161 +</#if>
  162 +</#if>
  163 +</#list>
  164 +<#-- 结束循环 -->
  165 +];
  166 +
  167 +//表单数据
  168 +export const formSchema: FormSchema[] = [
  169 +<#assign form_cat_tree = false>
  170 +<#assign form_cat_back = "">
  171 +<#assign bpm_flag=false>
  172 +<#list columns as po><#rt/>
  173 +<#if po.fieldDbName=='bpm_status'>
  174 + <#assign bpm_flag=true>
  175 +</#if>
  176 +<#if po.isShow =='Y'>
  177 +<#assign form_field_dictCode="">
  178 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  179 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  180 + <#elseif po.dictField?default("")?trim?length gt 1>
  181 + <#assign form_field_dictCode="${po.dictField}">
  182 + </#if>
  183 + {
  184 + label: '${po.filedComment}',
  185 + field: '${po.fieldName}',
  186 + <#if po.classType =='date'>
  187 + component: 'DatePicker',
  188 + <#elseif po.fieldType =='datetime'>
  189 + component: 'DatePicker',
  190 + componentProps: {
  191 + showTime:true
  192 + },
  193 + <#elseif po.fieldType =='time'>
  194 + component: 'TimePicker',
  195 + <#elseif po.classType =='popup'>
  196 + component: 'JPopup',
  197 + componentProps: ({ formActionType }) => {
  198 + const {setFieldsValue} = formActionType;
  199 + return{
  200 + setFieldsValue:setFieldsValue,
  201 + code:"${po.dictTable}",
  202 + fieldConfig:${po.dictField},
  203 + multi:${po.extendParams.popupMulti?c},
  204 + }
  205 + }
  206 + <#elseif po.classType =='sel_depart'>
  207 + component: 'JSelectDept',
  208 + <#elseif po.classType =='switch'>
  209 + component: 'JSwitch',
  210 + componentProps:{
  211 + <#if po.dictField != 'is_open'>
  212 + options:${po.dictField}
  213 + </#if>
  214 + }
  215 + <#elseif po.classType =='pca'>
  216 + component: 'JAreaLinkage',
  217 + <#elseif po.classType =='markdown'>
  218 + component: 'JMarkdownEditor',//注意string转换问题
  219 + <#elseif po.classType =='password'>
  220 + component: 'InputPassword',
  221 + <#elseif po.classType =='sel_user'>
  222 + component: 'JSelectUserByDept',
  223 + componentProps:{
  224 + labelKey:'realname',
  225 + }
  226 + <#elseif po.classType =='textarea'>
  227 + component: 'InputTextArea',//TODO 注意string转换问题
  228 + <#elseif po.classType=='list' || po.classType=='radio'>
  229 + component: 'JDictSelectTag',
  230 + componentProps:{
  231 + dictCode:"${form_field_dictCode}"
  232 + }
  233 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  234 + component: 'JMultiSelectTag',//TODO 暂无该组件
  235 + componentProps:{
  236 + dictCode:"${form_field_dictCode}"
  237 + }
  238 + <#elseif po.classType=='sel_search'>
  239 + component: 'JSearchSelect',
  240 + componentProps:{
  241 + dict:"${form_field_dictCode}"
  242 + }
  243 +<#elseif po.classType=='cat_tree'>
  244 + <#assign form_cat_tree = true>
  245 + component: 'JCategorySelect',
  246 + componentProps:{
  247 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  248 + }
  249 + <#if po.dictText?default("")?trim?length gt 1>
  250 + <#assign form_cat_back = "${po.dictText}">
  251 + </#if>
  252 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  253 + component: 'InputNumber',
  254 + <#elseif po.classType=='file'>
  255 + component: 'JUpload',
  256 + componentProps:{
  257 + <#if po.uploadnum??>
  258 + maxCount:${po.uploadnum}
  259 + </#if>
  260 + }
  261 + <#elseif po.classType=='image'>
  262 + component: 'JImageUpload',
  263 + componentProps:{
  264 + <#if po.uploadnum??>
  265 + fileMax:${po.uploadnum}
  266 + </#if>
  267 + }
  268 + <#elseif po.classType=='umeditor'>
  269 + component: 'JCodeEditor', //TODO String后缀暂未添加
  270 + <#elseif po.classType == 'sel_tree'>
  271 + component: 'JTreeSelect',
  272 + componentProps:{
  273 + <#if po.dictText??>
  274 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  275 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  276 + <#elseif po.dictText?split(',')[1]??>
  277 + pidField:"${po.dictText?split(',')[1]}",
  278 + <#elseif po.dictText?split(',')[3]??>
  279 + hasChildField:"${po.dictText?split(',')[3]}",
  280 + </#if>
  281 + </#if>
  282 + pidValue:"${po.dictField}",
  283 + }
  284 + <#else>
  285 + component: 'Input',
  286 + </#if>
  287 + <#include "/common/utils.ftl">
  288 + <#if po.isShow == 'Y' && poHasCheck(po)>
  289 + dynamicRules: ({model,schema}) => {
  290 + <#if po.fieldName != 'id'>
  291 + <#assign fieldValidType = po.fieldValidType!''>
  292 + return [
  293 + <#-- 非空校验 -->
  294 + <#if po.nullable == 'N' || fieldValidType == '*'>
  295 + { required: true, message: '请输入${po.filedComment}!'},
  296 + <#elseif fieldValidType!=''>
  297 + { required: false},
  298 + </#if>
  299 + <#-- 唯一校验 -->
  300 + <#if fieldValidType == 'only'>
  301 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  302 + <#-- 6到16位数字 -->
  303 + <#elseif fieldValidType == 'n6-16'>
  304 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  305 + <#-- 6到16位任意字符 -->
  306 + <#elseif fieldValidType == '*6-16'>
  307 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  308 + <#-- 6到18位字符串 -->
  309 + <#elseif fieldValidType == 's6-18'>
  310 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  311 + <#-- 网址 -->
  312 + <#elseif fieldValidType == 'url'>
  313 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  314 + <#-- 电子邮件 -->
  315 + <#elseif fieldValidType == 'e'>
  316 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  317 + <#-- 手机号码 -->
  318 + <#elseif fieldValidType == 'm'>
  319 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  320 + <#-- 邮政编码 -->
  321 + <#elseif fieldValidType == 'p'>
  322 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  323 + <#-- 字母 -->
  324 + <#elseif fieldValidType == 's'>
  325 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  326 + <#-- 数字 -->
  327 + <#elseif fieldValidType == 'n'>
  328 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  329 + <#-- 整数 -->
  330 + <#elseif fieldValidType == 'z'>
  331 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  332 + <#-- 金额 -->
  333 + <#elseif fieldValidType == 'money'>
  334 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  335 + <#-- 正则校验 -->
  336 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  337 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  338 + <#-- 无校验 -->
  339 + <#else>
  340 + <#t>
  341 + </#if>
  342 + ];
  343 + </#if>
  344 + },
  345 + </#if>
  346 + <#if po.readonly=='Y'>
  347 + dynamicDisabled:true
  348 + </#if>
  349 + },
  350 +</#if>
  351 +</#list>
  352 +];
  353 +
  354 +<#list subTables as sub>
  355 +//子表列表数据
  356 +export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
  357 + <#list sub.originalColumns as po>
  358 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  359 + {
  360 + title: '${po.filedComment}',
  361 + align:"center",
  362 + <#if po.sort=='Y'>
  363 + sorter: true,
  364 + </#if>
  365 + <#if po.classType=='date'>
  366 + dataIndex: '${po.fieldName}',
  367 + customRender:({text}) =>{
  368 + return !text?"":(text.length>10?text.substr(0,10):text)
  369 + },
  370 + <#elseif po.fieldDbType=='Blob'>
  371 + dataIndex: '${po.fieldName}String'
  372 + <#elseif po.classType=='umeditor'>
  373 + dataIndex: '${po.fieldName}',
  374 + slots: { customRender: 'htmlSlot' },
  375 + <#elseif po.classType=='pca'>
  376 + dataIndex: '${po.fieldName}',
  377 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  378 + <#elseif po.classType=='file'>
  379 + dataIndex: '${po.fieldName}',
  380 + slots: { customRender: 'fileSlot' },
  381 + <#elseif po.classType=='image'>
  382 + dataIndex: '${po.fieldName}',
  383 + customRender:render.renderAvatar,
  384 + <#elseif po.classType=='switch'>
  385 + dataIndex: '${po.fieldName}',
  386 +<#assign switch_extend_arr=['Y','N']>
  387 +<#if po.dictField?default("")?contains("[")>
  388 +<#assign switch_extend_arr=po.dictField?eval>
  389 +</#if>
  390 +<#list switch_extend_arr as a>
  391 +<#if a_index == 0>
  392 +<#assign switch_extend_arr1=a>
  393 +<#else>
  394 +<#assign switch_extend_arr2=a>
  395 +</#if>
  396 +</#list>
  397 + customRender:({text}) => {
  398 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  399 + },
  400 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  401 + dataIndex: '${po.fieldName}_dictText'
  402 + <#elseif po.classType=='cat_tree'>
  403 + dataIndex: '${po.fieldName}',
  404 + <#if po.dictText?default("")?trim?length == 0>
  405 + customRender:({text}) => {
  406 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  407 + },
  408 + <#else>
  409 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  410 + </#if>
  411 + <#else>
  412 + dataIndex: '${po.fieldName}'
  413 + </#if>
  414 + },
  415 + </#if>
  416 + </#list>
  417 +];
  418 +//子表表单数据
  419 +export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
  420 +<#assign form_cat_tree = false>
  421 +<#assign form_cat_back = "">
  422 +<#assign bpm_flag=false>
  423 +<#list sub.originalColumns as po><#rt/>
  424 +<#if po.isShow =='Y'>
  425 +<#assign form_field_dictCode="">
  426 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  427 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  428 + <#elseif po.dictField?default("")?trim?length gt 1>
  429 + <#assign form_field_dictCode="${po.dictField}">
  430 + </#if>
  431 + {
  432 + label: '${po.filedComment}',
  433 + field: '${po.fieldName}',
  434 + <#if po.classType =='date'>
  435 + component: 'DatePicker',
  436 + <#elseif po.fieldType =='datetime'>
  437 + component: 'DatePicker',
  438 + componentProps: {
  439 + showTime:true
  440 + },
  441 + <#elseif po.fieldType =='time'>
  442 + component: 'TimePicker',
  443 + <#elseif po.classType =='popup'>
  444 + component: 'JPopup',
  445 + componentProps: ({ formActionType }) => {
  446 + const {setFieldsValue} = formActionType;
  447 + return{
  448 + setFieldsValue:setFieldsValue,
  449 + code:"${po.dictTable}",
  450 + fieldConfig:${po.dictField},
  451 + multi:${po.extendParams.popupMulti?c},
  452 + }
  453 + }
  454 + <#elseif po.classType =='sel_depart'>
  455 + component: 'JSelectDept',
  456 + <#elseif po.classType =='switch'>
  457 + component: 'JSwitch',
  458 + componentProps:{
  459 + <#if po.dictField != 'is_open'>
  460 + options:${po.dictField}
  461 + </#if>
  462 + }
  463 + <#elseif po.classType =='pca'>
  464 + component: 'JAreaLinkage',
  465 + <#elseif po.classType =='markdown'>
  466 + component: 'JMarkdownEditor',//注意string转换问题
  467 + <#elseif po.classType =='password'>
  468 + component: 'InputPassword',
  469 + <#elseif po.classType =='sel_user'>
  470 + component: 'JSelectUserByDept',
  471 + componentProps:{
  472 + labelKey:'realname',
  473 + }
  474 + <#elseif po.classType =='textarea'>
  475 + component: 'InputTextArea',//TODO 注意string转换问题
  476 + <#elseif po.classType=='list' || po.classType=='radio'>
  477 + component: 'JDictSelectTag',
  478 + componentProps:{
  479 + dictCode:"${form_field_dictCode}"
  480 + }
  481 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  482 + component: 'JMultiSelectTag',//TODO 暂无该组件
  483 + componentProps:{
  484 + dictCode:"${form_field_dictCode}"
  485 + }
  486 + <#elseif po.classType=='sel_search'>
  487 + component: 'JSearchSelect',
  488 + componentProps:{
  489 + dict:"${form_field_dictCode}"
  490 + }
  491 +<#elseif po.classType=='cat_tree'>
  492 + <#assign form_cat_tree = true>
  493 + component: 'JCategorySelect',
  494 + componentProps:{
  495 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  496 + }
  497 + <#if po.dictText?default("")?trim?length gt 1>
  498 + <#assign form_cat_back = "${po.dictText}">
  499 + </#if>
  500 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  501 + component: 'InputNumber',
  502 + <#elseif po.classType=='file'>
  503 + component: 'JUpload',
  504 + componentProps:{
  505 + <#if po.uploadnum??>
  506 + maxCount:${po.uploadnum}
  507 + </#if>
  508 + }
  509 + <#elseif po.classType=='image'>
  510 + component: 'JImageUpload',
  511 + componentProps:{
  512 + <#if po.uploadnum??>
  513 + fileMax:${po.uploadnum}
  514 + </#if>
  515 + }
  516 + <#elseif po.classType=='umeditor'>
  517 + component: 'JCodeEditor', //TODO String后缀暂未添加
  518 + <#elseif po.classType == 'sel_tree'>
  519 + component: 'JTreeSelect',
  520 + componentProps:{
  521 + <#if po.dictText??>
  522 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  523 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  524 + <#elseif po.dictText?split(',')[1]??>
  525 + pidField:"${po.dictText?split(',')[1]}",
  526 + <#elseif po.dictText?split(',')[3]??>
  527 + hasChildField:"${po.dictText?split(',')[3]}",
  528 + </#if>
  529 + </#if>
  530 + pidValue:"${po.dictField}",
  531 + }
  532 + <#else>
  533 + component: 'Input',
  534 + </#if>
  535 + <#include "/common/utils.ftl">
  536 + <#if po.isShow == 'Y' && poHasCheck(po)>
  537 + dynamicRules: ({model,schema}) => {
  538 + <#if po.fieldName != 'id'>
  539 + <#assign fieldValidType = po.fieldValidType!''>
  540 + return [
  541 + <#-- 非空校验 -->
  542 + <#if po.nullable == 'N' || fieldValidType == '*'>
  543 + { required: true, message: '请输入${po.filedComment}!'},
  544 + <#elseif fieldValidType!=''>
  545 + { required: false},
  546 + </#if>
  547 + <#-- 唯一校验 -->
  548 + <#if fieldValidType == 'only'>
  549 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  550 + <#-- 6到16位数字 -->
  551 + <#elseif fieldValidType == 'n6-16'>
  552 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  553 + <#-- 6到16位任意字符 -->
  554 + <#elseif fieldValidType == '*6-16'>
  555 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  556 + <#-- 6到18位字符串 -->
  557 + <#elseif fieldValidType == 's6-18'>
  558 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  559 + <#-- 网址 -->
  560 + <#elseif fieldValidType == 'url'>
  561 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  562 + <#-- 电子邮件 -->
  563 + <#elseif fieldValidType == 'e'>
  564 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  565 + <#-- 手机号码 -->
  566 + <#elseif fieldValidType == 'm'>
  567 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  568 + <#-- 邮政编码 -->
  569 + <#elseif fieldValidType == 'p'>
  570 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  571 + <#-- 字母 -->
  572 + <#elseif fieldValidType == 's'>
  573 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  574 + <#-- 数字 -->
  575 + <#elseif fieldValidType == 'n'>
  576 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  577 + <#-- 整数 -->
  578 + <#elseif fieldValidType == 'z'>
  579 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  580 + <#-- 金额 -->
  581 + <#elseif fieldValidType == 'money'>
  582 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  583 + <#-- 正则校验 -->
  584 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  585 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  586 + <#-- 无校验 -->
  587 + <#else>
  588 + <#t>
  589 + </#if>
  590 + ];
  591 + </#if>
  592 + },
  593 + </#if>
  594 + <#if po.readonly=='Y'>
  595 + dynamicDisabled:true
  596 + </#if>
  597 + },
  598 +</#if>
  599 +</#list>
  600 +];
  601 +</#list>
0 602 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/[1-n]List.vuei 0 → 100644
  1 +<#list subTables as sub>
  2 +#segment#${sub.entityName}List.vue
  3 +<template>
  4 + <div>
  5 + <!--引用表格-->
  6 + <BasicTable @register="registerTable" :rowSelection="rowSelection" :searchInfo="searchInfo">
  7 + <!--插槽:table标题-->
  8 + <template #tableTitle>
  9 + <a-button type="primary" @click="handleCreate" preIcon="ant-design:plus-outlined"> 新增</a-button>
  10 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  11 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  12 + <a-dropdown v-if="selectedRowKeys.length > 0">
  13 + <template #overlay>
  14 + <a-menu>
  15 + <a-menu-item key="1" @click="batchHandleDelete">
  16 + <Icon icon="ant-design:delete-outlined"></Icon>
  17 + 删除
  18 + </a-menu-item>
  19 + </a-menu>
  20 + </template>
  21 + <a-button>批量操作
  22 + <Icon icon="mdi:chevron-down"></Icon>
  23 + </a-button>
  24 + </a-dropdown>
  25 + </template>
  26 + <!--操作栏-->
  27 + <template #action="{ record }">
  28 + <TableAction :actions="getTableAction(record)"/>
  29 + </template>
  30 + <!--字段回显插槽-->
  31 + <template #htmlSlot="{text}">
  32 + <div v-html="text"></div>
  33 + </template>
  34 + <template #fileSlot="{text}">
  35 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  36 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  37 + </template>
  38 + </BasicTable>
  39 +
  40 + <${sub.entityName}Modal @register="registerModal" @success="handleSuccess"/>
  41 + </div>
  42 +</template>
  43 +
  44 +<script lang="ts" setup>
  45 + import {ref, computed, unref,inject,watch} from 'vue';
  46 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  47 + import { useListPage } from '/@/hooks/system/useListPage'
  48 + import {useModal} from '/@/components/Modal';
  49 + import ${sub.entityName}Modal from './components/${sub.entityName}Modal.vue'
  50 + import {${sub.entityName?uncap_first}Columns} from './${entityName}.data';
  51 + import {${sub.entityName?uncap_first}List, ${sub.entityName?uncap_first}Delete, ${sub.entityName?uncap_first}DeleteBatch} from './${entityName}.api';
  52 + import {isEmpty} from "/@/utils/is";
  53 + import {useMessage} from '/@/hooks/web/useMessage';
  54 +
  55 + //接收主表id
  56 + const mainId = inject('mainId') || '';
  57 + //提示弹窗
  58 + const $message = useMessage()
  59 + //弹窗model
  60 + const [registerModal, {openModal}] = useModal();
  61 + const searchInfo = {};
  62 + // 列表页面公共参数、方法
  63 + const {prefixCls, tableContext} = useListPage({
  64 + tableProps: {
  65 + api: ${sub.entityName?uncap_first}List,
  66 + columns: ${sub.entityName?uncap_first}Columns,
  67 + canResize: false,
  68 + useSearchForm: false,
  69 + actionColumn: {
  70 + width: 180,
  71 + },
  72 + pagination:{
  73 + current: 1,
  74 + pageSize: 5,
  75 + pageSizeOptions: ['5', '10', '20'],
  76 + }
  77 + },
  78 + });
  79 +
  80 + //注册table数据
  81 + const [registerTable, {reload}, {rowSelection, selectedRowKeys}] = tableContext;
  82 +
  83 + watch(mainId, () => {
  84 + <#list sub.foreignKeys as key>
  85 + searchInfo['${key?uncap_first}'] = unref(mainId);
  86 + </#list>
  87 + reload();
  88 + }
  89 + );
  90 +
  91 + /**
  92 + * 新增事件
  93 + */
  94 + function handleCreate() {
  95 + if (isEmpty(unref(mainId))) {
  96 + $message.createMessage.warning('请选择一个主表信息')
  97 + return;
  98 + }
  99 + openModal(true, {
  100 + isUpdate: false,
  101 + showFooter: true,
  102 + });
  103 + }
  104 +
  105 + /**
  106 + * 编辑事件
  107 + */
  108 + async function handleEdit(record: Recordable) {
  109 + openModal(true, {
  110 + record,
  111 + isUpdate: true,
  112 + showFooter: true,
  113 + });
  114 + }
  115 +
  116 + /**
  117 + * 删除事件
  118 + */
  119 + async function handleDelete(record) {
  120 + await ${sub.entityName?uncap_first}Delete({id: record.id}, reload);
  121 + }
  122 +
  123 + /**
  124 + * 批量删除事件
  125 + */
  126 + async function batchHandleDelete() {
  127 + await ${sub.entityName?uncap_first}DeleteBatch({ids: selectedRowKeys.value}, () => {
  128 + reload()
  129 + })
  130 + }
  131 +
  132 + /**
  133 + * 成功回调
  134 + */
  135 + function handleSuccess() {
  136 + reload();
  137 + }
  138 +
  139 + /**
  140 + * 操作栏
  141 + */
  142 + function getTableAction(record) {
  143 + return [
  144 + {
  145 + label: '编辑',
  146 + onClick: handleEdit.bind(null, record),
  147 + }, {
  148 + label: '删除',
  149 + popConfirm: {
  150 + title: '是否确认删除',
  151 + confirm: handleDelete.bind(null, record),
  152 + },
  153 + }
  154 + ]
  155 + }
  156 +</script>
  157 +</#list>
0 158 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicForm @register="registerForm"/>
  4 + </BasicModal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import {ref, computed, unref} from 'vue';
  9 + import {BasicModal, useModalInner} from '/@/components/Modal';
  10 + import {BasicForm, useForm} from '/@/components/Form/index';
  11 + import {formSchema} from '../${entityName?uncap_first}.data';
  12 + import {saveOrUpdate} from '../${entityName?uncap_first}.api';
  13 + // Emits声明
  14 + const emit = defineEmits(['register','success']);
  15 + const isUpdate = ref(true);
  16 + //表单配置
  17 + const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
  18 + labelWidth: 150,
  19 + schemas: formSchema,
  20 + showActionButtonGroup: false,
  21 + });
  22 + //表单赋值
  23 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  24 + //重置表单
  25 + await resetFields();
  26 + setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
  27 + isUpdate.value = !!data?.isUpdate;
  28 + if (unref(isUpdate)) {
  29 + //表单赋值
  30 + await setFieldsValue({
  31 + ...data.record,
  32 + });
  33 + }
  34 + // 隐藏底部时禁用整个表单
  35 + setProps({ disabled: !data?.showFooter })
  36 + });
  37 + //设置标题
  38 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  39 + //表单提交事件
  40 + async function handleSubmit(v) {
  41 + try {
  42 + let values = await validate();
  43 + setModalProps({confirmLoading: true});
  44 + //提交表单
  45 + await saveOrUpdate(values, isUpdate.value);
  46 + //关闭弹窗
  47 + closeModal();
  48 + //刷新列表
  49 + emit('success');
  50 + } finally {
  51 + setModalProps({confirmLoading: false});
  52 + }
  53 + }
  54 +</script>
  55 +
  56 +<style lang="less" scoped>
  57 +
  58 +</style>
0 59 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Modal.vuei 0 → 100644
  1 +<#include "/common/utils.ftl">
  2 +<#list subTables as sub>
  3 +#segment#${sub.entityName}Modal.vue
  4 +<template>
  5 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  6 + <BasicForm @register="registerForm"/>
  7 + </BasicModal>
  8 +</template>
  9 +
  10 +<script lang="ts" setup>
  11 + import {ref, computed, unref,inject} from 'vue';
  12 + import {BasicModal, useModalInner} from '/@/components/Modal';
  13 + import {BasicForm, useForm} from '/@/components/Form/index';
  14 + import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
  15 + import {${sub.entityName?uncap_first}SaveOrUpdate} from '../${entityName?uncap_first}.api';
  16 +
  17 + //接收主表id
  18 + const mainId = inject('mainId');
  19 + // Emits声明
  20 + const emit = defineEmits(['register','success']);
  21 + const isUpdate = ref(true);
  22 + //表单配置
  23 + const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
  24 + labelWidth: 150,
  25 + schemas: ${sub.entityName?uncap_first}FormSchema,
  26 + showActionButtonGroup: false,
  27 + });
  28 + //表单赋值
  29 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  30 + //重置表单
  31 + await resetFields();
  32 + setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
  33 + isUpdate.value = !!data?.isUpdate;
  34 + if (unref(isUpdate)) {
  35 + //表单赋值
  36 + await setFieldsValue({
  37 + ...data.record,
  38 + });
  39 + }
  40 + // 隐藏底部时禁用整个表单
  41 + setProps({ disabled: !data?.showFooter })
  42 + });
  43 + //设置标题
  44 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  45 + //表单提交事件
  46 + async function handleSubmit(v) {
  47 + try {
  48 + let values = await validate();
  49 + setModalProps({confirmLoading: true});
  50 + if (unref(mainId)) {
  51 + <#list sub.foreignKeys as key>
  52 + values['${key?uncap_first}'] = unref(mainId);
  53 + </#list>
  54 + }
  55 + //提交表单
  56 + await ${sub.entityName?uncap_first}SaveOrUpdate(values, isUpdate.value);
  57 + //关闭弹窗
  58 + closeModal();
  59 + //刷新列表
  60 + emit('success');
  61 + } finally {
  62 + setModalProps({confirmLoading: false});
  63 + }
  64 + }
  65 +</script>
  66 +</#list>
0 67 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -110,7 +110,7 @@ public class ${entityName}Controller {
110 110 */
111 111 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
112 112 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
113   - @PutMapping(value = "/edit")
  113 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
114 114 public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
115 115 ${entityName} ${entityName?uncap_first} = new ${entityName}();
116 116 BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue/${entityName}List.vuei
... ... @@ -266,9 +266,7 @@
266 266 <a @click="handleEdit(record)">编辑</a>
267 267 <a-divider type="vertical"/>
268 268 <a-dropdown>
269   - <a class="ant-dropdown-link">
270   - <span>更多 <a-icon type="down"/></span>
271   - </a>
  269 + <a class="ant-dropdown-link">更多 <a-icon type="down" /></a>
272 270 <a-menu slot="overlay">
273 271 <a-menu-item>
274 272 <a-popconfirm title="确定删除吗?" @confirm="handleDelete(record.id)">
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -21,13 +21,8 @@
21 21 <#elseif po.dictField?default("")?trim?length gt 1>
22 22 <#assign form_field_dictCode="${po.dictField}">
23 23 </#if>
24   - <#if po.classType =='textarea'>
25   - <a-col :span="24">
26   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
27   - <#else>
28 24 <a-col :xs="24" :sm="12">
29 25 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
30   - </#if>
31 26 <#if po.classType =='date'>
32 27 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
33 28 <#elseif po.classType =='datetime'>
... ... @@ -131,7 +126,7 @@
131 126 </#list>
132 127 </a-tabs>
133 128 <#if bpm_flag>
134   - <a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button @click="handleOk">提 交</a-button></a-row>
  129 + <a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button icon="check" style="width: 126px" type="primary" @click="handleOk">提 交</a-button></a-row>
135 130 </#if>
136 131 </a-spin>
137 132 </template>
... ... @@ -161,20 +156,12 @@
161 156 return {
162 157 labelCol: {
163 158 xs: { span: 24 },
164   - sm: { span: 6 },
  159 + sm: { span: 5 },
165 160 },
166 161 wrapperCol: {
167 162 xs: { span: 24 },
168 163 sm: { span: 16 },
169 164 },
170   - labelCol2: {
171   - xs: { span: 24 },
172   - sm: { span: 3 },
173   - },
174   - wrapperCol2: {
175   - xs: { span: 24 },
176   - sm: { span: 20 },
177   - },
178 165 model:{
179 166 <#include "/common/init/initValue.ftl">
180 167 },
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -15,13 +15,8 @@
15 15 <#elseif po.dictField?default("")?trim?length gt 1>
16 16 <#assign form_field_dictCode="${po.dictField}">
17 17 </#if>
18   - <#if po.classType =='textarea'>
19   - <a-col :span="24">
20   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
21   - <#else>
22 18 <a-col :xs="24" :sm="12">
23 19 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
24   - </#if>
25 20 <#if po.classType =='date'>
26 21 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
27 22 <#elseif po.classType =='datetime'>
... ... @@ -90,20 +85,12 @@
90 85 },
91 86 labelCol: {
92 87 xs: { span: 24 },
93   - sm: { span: 6 },
  88 + sm: { span: 5 },
94 89 },
95 90 wrapperCol: {
96 91 xs: { span: 24 },
97 92 sm: { span: 16 },
98 93 },
99   - labelCol2: {
100   - xs: { span: 24 },
101   - sm: { span: 3 },
102   - },
103   - wrapperCol2: {
104   - xs: { span: 24 },
105   - sm: { span: 20 },
106   - },
107 94 <#include "/common/validatorRulesTemplate/sub.ftl">
108 95 confirmLoading: false,
109 96 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<#-- ** 引入全局工具方法 ** -->
  2 +<#--<#include "/common/utils.ftl">-->
  3 +<#include "../../../../../../common/utils.ftl">
  4 +<template>
  5 + <div>
  6 +<#assign list_need_category=false>
  7 +<#assign list_need_pca=false>
  8 +<#assign bpm_flag=false>
  9 +
  10 +<#-- 开始循环 -->
  11 +<#list columns as po>
  12 +<#if po.fieldDbName=='bpm_status'>
  13 + <#assign bpm_flag=true>
  14 +</#if>
  15 +<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  16 +<#assign list_need_category=true>
  17 +</#if>
  18 +<#if po.classType=='pca'>
  19 +<#assign list_need_pca=true>
  20 +</#if>
  21 +</#list>
  22 +<#-- 结束循环 -->
  23 + <!--引用表格-->
  24 + <BasicTable @register="registerTable" :rowSelection="rowSelection" :expandedRowKeys="expandedRowKeys" @expand="handleExpand">
  25 + <!-- 内嵌table区域 begin -->
  26 + <template #expandedRowRender="{record}">
  27 + <a-tabs tabPosition="top">
  28 + <#list subTables as sub>
  29 + <a-tab-pane tab="${sub.ftlDescription}" key="${sub.entityName?uncap_first}" forceRender>
  30 + <${sub.entityName?uncap_first}SubTable :id="expandedRowKeys[0]"/>
  31 + </a-tab-pane>
  32 + </#list>
  33 + </a-tabs>
  34 + </template>
  35 + <!-- 内嵌table区域 end -->
  36 + <!--插槽:table标题-->
  37 + <template #tableTitle>
  38 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  39 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  40 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  41 + <a-dropdown v-if="selectedRowKeys.length > 0">
  42 + <template #overlay>
  43 + <a-menu>
  44 + <a-menu-item key="1" @click="batchHandleDelete">
  45 + <Icon icon="ant-design:delete-outlined"></Icon>
  46 + 删除
  47 + </a-menu-item>
  48 + </a-menu>
  49 + </template>
  50 + <a-button>批量操作
  51 + <Icon icon="mdi:chevron-down"></Icon>
  52 + </a-button>
  53 + </a-dropdown>
  54 + </template>
  55 + <!--操作栏-->
  56 + <template #action="{ record }">
  57 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  58 + </template>
  59 + <!--字段回显插槽-->
  60 + <template #htmlSlot="{text}">
  61 + <div v-html="text"></div>
  62 + </template>
  63 + <template #fileSlot="{text}">
  64 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  65 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  66 + </template>
  67 + </BasicTable>
  68 + <!-- 表单区域 -->
  69 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  70 + </div>
  71 +</template>
  72 +
  73 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  74 + import {ref, computed, unref} from 'vue';
  75 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  76 + import { useListPage } from '/@/hooks/system/useListPage'
  77 + import {useModal} from '/@/components/Modal';
  78 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  79 + <#list subTables as sub>
  80 + import ${sub.entityName}SubTable from './subTables/${sub.entityName}SubTable.vue'
  81 + </#list>
  82 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  83 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  84 + <#if list_need_category>
  85 + import { loadCategoryData } from '/@/api/common/api'
  86 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  87 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  88 + </#if>
  89 + // 展开key
  90 + const expandedRowKeys = ref<any[]>([]);
  91 + //注册model
  92 + const [registerModal, {openModal}] = useModal();
  93 + //注册table数据
  94 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  95 + tableProps:{
  96 + title: '${tableVo.ftlDescription}',
  97 + api: list,
  98 + columns,
  99 + canResize:false,
  100 + formConfig: {
  101 + labelWidth: 120,
  102 + schemas: searchFormSchema,
  103 + autoSubmitOnEnter:true,
  104 + showAdvancedButton:true,
  105 + fieldMapToTime: [
  106 + <#list columns as po>
  107 + <#if po.isQuery=='Y'>
  108 + <#if po.queryMode!='single'>
  109 + <#if po.classType=='date'>
  110 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
  111 + <#elseif po.classType=='datetime'>
  112 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
  113 + </#if>
  114 + </#if>
  115 + </#if>
  116 + </#list>
  117 + ],
  118 + },
  119 + actionColumn: {
  120 + width: 120,
  121 + },
  122 + },
  123 + exportConfig: {
  124 + name:"${tableVo.ftlDescription}",
  125 + url: getExportUrl,
  126 + },
  127 + importConfig: {
  128 + url: getImportUrl
  129 + },
  130 + })
  131 +
  132 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  133 + /**
  134 + * 展开事件
  135 + * */
  136 + function handleExpand(expanded, record){
  137 + expandedRowKeys.value=[];
  138 + if (expanded === true) {
  139 + expandedRowKeys.value.push(record.id)
  140 + }
  141 + }
  142 + /**
  143 + * 新增事件
  144 + */
  145 + function handleAdd() {
  146 + openModal(true, {
  147 + isUpdate: false,
  148 + showFooter: true,
  149 + });
  150 + }
  151 + /**
  152 + * 编辑事件
  153 + */
  154 + function handleEdit(record: Recordable) {
  155 + openModal(true, {
  156 + record,
  157 + isUpdate: true,
  158 + showFooter: true,
  159 + });
  160 + }
  161 + /**
  162 + * 详情
  163 + */
  164 + function handleDetail(record: Recordable) {
  165 + openModal(true, {
  166 + record,
  167 + isUpdate: true,
  168 + showFooter: false,
  169 + });
  170 + }
  171 + /**
  172 + * 删除事件
  173 + */
  174 + async function handleDelete(record) {
  175 + await deleteOne({id: record.id}, reload);
  176 + }
  177 + /**
  178 + * 批量删除事件
  179 + */
  180 + async function batchHandleDelete() {
  181 + await batchDelete({ids: selectedRowKeys.value}, reload);
  182 + }
  183 + /**
  184 + * 成功回调
  185 + */
  186 + function handleSuccess() {
  187 + reload();
  188 + }
  189 + /**
  190 + * 操作栏
  191 + */
  192 + function getTableAction(record){
  193 + return [
  194 + {
  195 + label: '编辑',
  196 + onClick: handleEdit.bind(null, record),
  197 + }
  198 + ]
  199 + }
  200 + /**
  201 + * 下拉操作栏
  202 + */
  203 + function getDropDownAction(record){
  204 + return [
  205 + {
  206 + label: '详情',
  207 + onClick: handleDetail.bind(null, record),
  208 + }, {
  209 + label: '删除',
  210 + popConfirm: {
  211 + title: '是否确认删除',
  212 + confirm: handleDelete.bind(null, record),
  213 + }
  214 + }
  215 + ]
  216 + }
  217 + <#if list_need_category>
  218 + /**
  219 + * 初始化字典配置
  220 + */
  221 + function initDictConfig(){
  222 + <#list columns as po>
  223 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  224 + <#if po.classType=='cat_tree' && list_need_category==true>
  225 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  226 + if (res) {
  227 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  228 + if(!allDictDate['${po.dictField?default("")}']){
  229 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  230 + }
  231 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  232 + }
  233 + })
  234 + </#if>
  235 + </#if>
  236 + </#list>
  237 + }
  238 + initDictConfig();
  239 + </#if>
  240 +</script>
  241 +
  242 +<style scoped>
  243 +
  244 +</style>
0 245 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +<#list subTables as sub><#rt/>
  13 + ${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
  14 +</#list>
  15 +}
  16 +/**
  17 + * 导出api
  18 + * @param params
  19 + */
  20 +export const getExportUrl = Api.exportXls;
  21 +
  22 +/**
  23 + * 导入api
  24 + */
  25 +export const getImportUrl = Api.importExcel;
  26 +<#list subTables as sub><#rt/>
  27 +/**
  28 + * 子表单查询接口
  29 + * @param params
  30 + */
  31 +export const query${sub.entityName} = Api.${sub.entityName?uncap_first}List
  32 +</#list>
  33 +/**
  34 + * 列表接口
  35 + * @param params
  36 + */
  37 +export const list = (params) =>
  38 + defHttp.get({url: Api.list, params});
  39 +
  40 +/**
  41 + * 删除单个
  42 + */
  43 +export const deleteOne = (params,handleSuccess) => {
  44 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  45 + handleSuccess();
  46 + });
  47 +}
  48 +/**
  49 + * 批量删除
  50 + * @param params
  51 + */
  52 +export const batchDelete = (params, handleSuccess) => {
  53 + Modal.confirm({
  54 + title: '确认删除',
  55 + content: '是否删除选中数据',
  56 + okText: '确认',
  57 + cancelText: '取消',
  58 + onOk: () => {
  59 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  60 + handleSuccess();
  61 + });
  62 + }
  63 + });
  64 +}
  65 +/**
  66 + * 保存或者更新
  67 + * @param params
  68 + */
  69 +export const saveOrUpdate = (params, isUpdate) => {
  70 + let url = isUpdate ? Api.edit : Api.save;
  71 + return defHttp.post({url: url, params});
  72 +}
  73 +<#list subTables as sub><#rt/>
  74 +/**
  75 + * 子表列表接口
  76 + * @param params
  77 + */
  78 +export const ${sub.entityName?uncap_first}List = (params) =>
  79 + defHttp.get({url: Api.${sub.entityName?uncap_first}List, params},{isTransformResponse:false});
  80 +</#list>
0 81 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
  6 +//列表数据
  7 +export const columns: BasicColumn[] = [
  8 + <#list columns as po>
  9 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  10 + {
  11 + title: '${po.filedComment}',
  12 + align:"center",
  13 + <#if po.sort=='Y'>
  14 + sorter: true,
  15 + </#if>
  16 + <#if po.classType=='date'>
  17 + dataIndex: '${po.fieldName}',
  18 + customRender:({text}) =>{
  19 + return !text?"":(text.length>10?text.substr(0,10):text)
  20 + },
  21 + <#elseif po.fieldDbType=='Blob'>
  22 + dataIndex: '${po.fieldName}String'
  23 + <#elseif po.classType=='umeditor'>
  24 + dataIndex: '${po.fieldName}',
  25 + slots: { customRender: 'htmlSlot' },
  26 + <#elseif po.classType=='pca'>
  27 + dataIndex: '${po.fieldName}',
  28 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  29 + <#elseif po.classType=='file'>
  30 + dataIndex: '${po.fieldName}',
  31 + slots: { customRender: 'fileSlot' },
  32 + <#elseif po.classType=='image'>
  33 + dataIndex: '${po.fieldName}',
  34 + customRender:render.renderAvatar,
  35 + <#elseif po.classType=='switch'>
  36 + dataIndex: '${po.fieldName}',
  37 +<#assign switch_extend_arr=['Y','N']>
  38 +<#if po.dictField?default("")?contains("[")>
  39 +<#assign switch_extend_arr=po.dictField?eval>
  40 +</#if>
  41 +<#list switch_extend_arr as a>
  42 +<#if a_index == 0>
  43 +<#assign switch_extend_arr1=a>
  44 +<#else>
  45 +<#assign switch_extend_arr2=a>
  46 +</#if>
  47 +</#list>
  48 + customRender:({text}) => {
  49 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  50 + },
  51 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  52 + dataIndex: '${po.fieldName}_dictText'
  53 + <#elseif po.classType=='cat_tree'>
  54 + dataIndex: '${po.fieldName}',
  55 + <#if po.dictText?default("")?trim?length == 0>
  56 + customRender:({text}) => {
  57 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  58 + },
  59 + <#else>
  60 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  61 + </#if>
  62 + <#else>
  63 + dataIndex: '${po.fieldName}'
  64 + </#if>
  65 + },
  66 + </#if>
  67 + </#list>
  68 +];
  69 +//查询数据
  70 +export const searchFormSchema: FormSchema[] = [
  71 +<#-- 开始循环 -->
  72 +<#list columns as po>
  73 +<#if po.fieldDbName=='bpm_status'>
  74 + <#assign bpm_flag=true>
  75 +</#if>
  76 +<#if po.isQuery=='Y'>
  77 +<#assign query_flag=true>
  78 + <#assign query_field_dictCode="">
  79 + <#if po.dictTable?default("")?trim?length gt 1>
  80 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  81 + <#elseif po.dictField?default("")?trim?length gt 1>
  82 + <#assign query_field_dictCode="${po.dictField}">
  83 + </#if>
  84 +<#if po.queryMode=='single'>
  85 + {
  86 + label: "${po.filedComment}",
  87 + field: "${po.fieldName}",
  88 +<#if po.classType=='sel_search'>
  89 + component: 'JSearchSelect',
  90 + componentProps:{
  91 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  92 + },
  93 +<#elseif po.classType=='sel_user'>
  94 + component: 'JSelectUserByDept',
  95 +<#elseif po.classType=='switch'>
  96 + component: 'JSwitch',
  97 + componentProps:{
  98 + <#if po.dictField != 'is_open'>
  99 + options:"${po.dictField}"
  100 + </#if>
  101 + },
  102 + <#elseif po.classType=='sel_depart'>
  103 + component: 'JSelectDept',
  104 + <#elseif po.classType=='list_multi'>
  105 + component: 'JMultiSelectTag',//暂无该组件
  106 + componentProps:{
  107 + dictCode:"query_field_dictCode?default("")"
  108 + },
  109 + <#elseif po.classType=='cat_tree'>
  110 + component: 'JCategorySelect',
  111 + componentProps:{
  112 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  113 + },
  114 +<#elseif po.classType=='date'>
  115 + component: 'DatePicker',
  116 +<#elseif po.classType=='datetime'>
  117 + component: 'DatePicker',
  118 + componentProps: {
  119 + showTime:true
  120 + },
  121 +<#elseif po.classType=='pca'>
  122 + component: 'JAreaLinkage',
  123 +<#elseif po.classType=='popup'>
  124 + component: 'JPopup',
  125 + componentProps: ({ formActionType }) => {
  126 + const {setFieldsValue} = formActionType;
  127 + return{
  128 + setFieldsValue:setFieldsValue,
  129 + code:"${po.dictTable}",
  130 + fieldConfig:"${po.dictField}",
  131 + multi:${po.extendParams.popupMulti?c},
  132 + }
  133 + },
  134 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  135 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  136 + component: 'JDictSelectTag',
  137 + componentProps:{
  138 + <#if po.dictTable?default("")?trim?length gt 1>
  139 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
  140 + <#elseif po.dictField?default("")?trim?length gt 1>
  141 + dictCode:"${po.dictField}"
  142 + </#if>
  143 + },
  144 +<#else>
  145 + component: 'Input',
  146 +</#if>
  147 + colProps: {span: 6},
  148 + },
  149 +<#else>
  150 + {
  151 + label: "${po.filedComment}",
  152 + field: "${po.fieldName}",
  153 +<#if po.classType=='date'>
  154 + component: 'RangePicker',
  155 +<#elseif po.classType=='datetime'>
  156 + component: 'RangePicker',
  157 + componentProps: {
  158 + showTime:true
  159 + },
  160 +<#else>
  161 + component: 'Input', //TODO 范围查询
  162 +</#if>
  163 + colProps: {span: 6},
  164 + },
  165 +</#if>
  166 +</#if>
  167 +</#list>
  168 +<#-- 结束循环 -->
  169 +];
  170 +//表单数据
  171 +export const formSchema: FormSchema[] = [
  172 +<#assign form_cat_tree = false>
  173 +<#assign form_cat_back = "">
  174 +<#assign bpm_flag=false>
  175 +<#list columns as po><#rt/>
  176 +<#if po.fieldDbName=='bpm_status'>
  177 + <#assign bpm_flag=true>
  178 +</#if>
  179 +<#if po.isShow =='Y'>
  180 +<#assign form_field_dictCode="">
  181 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  182 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  183 + <#elseif po.dictField?default("")?trim?length gt 1>
  184 + <#assign form_field_dictCode="${po.dictField}">
  185 + </#if>
  186 + {
  187 + label: '${po.filedComment}',
  188 + field: '${po.fieldName}',
  189 + <#if po.classType =='date'>
  190 + component: 'DatePicker',
  191 + <#elseif po.fieldType =='datetime'>
  192 + component: 'DatePicker',
  193 + componentProps: {
  194 + showTime:true
  195 + },
  196 + <#elseif po.fieldType =='time'>
  197 + component: 'TimePicker',
  198 + <#elseif po.classType =='popup'>
  199 + component: 'JPopup',
  200 + componentProps: ({ formActionType }) => {
  201 + const {setFieldsValue} = formActionType;
  202 + return{
  203 + setFieldsValue:setFieldsValue,
  204 + code:"${po.dictTable}",
  205 + fieldConfig:${po.dictField},
  206 + multi:${po.extendParams.popupMulti?c},
  207 + }
  208 + }
  209 + <#elseif po.classType =='sel_depart'>
  210 + component: 'JSelectDept',
  211 + <#elseif po.classType =='switch'>
  212 + component: 'JSwitch',
  213 + componentProps:{
  214 + <#if po.dictField != 'is_open'>
  215 + options:${po.dictField}
  216 + </#if>
  217 + }
  218 + <#elseif po.classType =='pca'>
  219 + component: 'JAreaLinkage',
  220 + <#elseif po.classType =='markdown'>
  221 + component: 'JMarkdownEditor',//注意string转换问题
  222 + <#elseif po.classType =='password'>
  223 + component: 'InputPassword',
  224 + <#elseif po.classType =='sel_user'>
  225 + component: 'JSelectUserByDept',
  226 + componentProps:{
  227 + labelKey:'realname',
  228 + }
  229 + <#elseif po.classType =='textarea'>
  230 + component: 'InputTextArea',//TODO 注意string转换问题
  231 + <#elseif po.classType=='list' || po.classType=='radio'>
  232 + component: 'JDictSelectTag',
  233 + componentProps:{
  234 + dictCode:"${form_field_dictCode}"
  235 + }
  236 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  237 + component: 'JMultiSelectTag',//TODO 暂无该组件
  238 + componentProps:{
  239 + dictCode:"${form_field_dictCode}"
  240 + }
  241 + <#elseif po.classType=='sel_search'>
  242 + component: 'JSearchSelect',
  243 + componentProps:{
  244 + dict:"${form_field_dictCode}"
  245 + }
  246 +<#elseif po.classType=='cat_tree'>
  247 + <#assign form_cat_tree = true>
  248 + component: 'JCategorySelect',
  249 + componentProps:{
  250 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  251 + }
  252 + <#if po.dictText?default("")?trim?length gt 1>
  253 + <#assign form_cat_back = "${po.dictText}">
  254 + </#if>
  255 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  256 + component: 'InputNumber',
  257 + <#elseif po.classType=='file'>
  258 + component: 'JUpload',
  259 + componentProps:{
  260 + <#if po.uploadnum??>
  261 + maxCount:${po.uploadnum}
  262 + </#if>
  263 + }
  264 + <#elseif po.classType=='image'>
  265 + component: 'JImageUpload',
  266 + componentProps:{
  267 + <#if po.uploadnum??>
  268 + fileMax:${po.uploadnum}
  269 + </#if>
  270 + }
  271 + <#elseif po.classType=='umeditor'>
  272 + component: 'JCodeEditor', //TODO String后缀暂未添加
  273 + <#elseif po.classType == 'sel_tree'>
  274 + component: 'JTreeSelect',
  275 + componentProps:{
  276 + <#if po.dictText??>
  277 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  278 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  279 + <#elseif po.dictText?split(',')[1]??>
  280 + pidField:"${po.dictText?split(',')[1]}",
  281 + <#elseif po.dictText?split(',')[3]??>
  282 + hasChildField:"${po.dictText?split(',')[3]}",
  283 + </#if>
  284 + </#if>
  285 + pidValue:"${po.dictField}",
  286 + }
  287 + <#else>
  288 + component: 'Input',
  289 + </#if>
  290 + <#include "/common/utils.ftl">
  291 + <#if po.isShow == 'Y' && poHasCheck(po)>
  292 + dynamicRules: ({model,schema}) => {
  293 + <#if po.fieldName != 'id'>
  294 + <#assign fieldValidType = po.fieldValidType!''>
  295 + return [
  296 + <#-- 非空校验 -->
  297 + <#if po.nullable == 'N' || fieldValidType == '*'>
  298 + { required: true, message: '请输入${po.filedComment}!'},
  299 + <#elseif fieldValidType!=''>
  300 + { required: false},
  301 + </#if>
  302 + <#-- 唯一校验 -->
  303 + <#if fieldValidType == 'only'>
  304 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  305 + <#-- 6到16位数字 -->
  306 + <#elseif fieldValidType == 'n6-16'>
  307 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  308 + <#-- 6到16位任意字符 -->
  309 + <#elseif fieldValidType == '*6-16'>
  310 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  311 + <#-- 6到18位字符串 -->
  312 + <#elseif fieldValidType == 's6-18'>
  313 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  314 + <#-- 网址 -->
  315 + <#elseif fieldValidType == 'url'>
  316 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  317 + <#-- 电子邮件 -->
  318 + <#elseif fieldValidType == 'e'>
  319 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  320 + <#-- 手机号码 -->
  321 + <#elseif fieldValidType == 'm'>
  322 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  323 + <#-- 邮政编码 -->
  324 + <#elseif fieldValidType == 'p'>
  325 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  326 + <#-- 字母 -->
  327 + <#elseif fieldValidType == 's'>
  328 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  329 + <#-- 数字 -->
  330 + <#elseif fieldValidType == 'n'>
  331 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  332 + <#-- 整数 -->
  333 + <#elseif fieldValidType == 'z'>
  334 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  335 + <#-- 金额 -->
  336 + <#elseif fieldValidType == 'money'>
  337 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  338 + <#-- 正则校验 -->
  339 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  340 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  341 + <#-- 无校验 -->
  342 + <#else>
  343 + <#t>
  344 + </#if>
  345 + ];
  346 + </#if>
  347 + },
  348 + </#if>
  349 + <#if po.readonly=='Y'>
  350 + dynamicDisabled:true
  351 + </#if>
  352 + },
  353 +</#if>
  354 +</#list>
  355 +];
  356 +//子表单数据
  357 +<#list subTables as sub>
  358 +//子表列表数据
  359 +export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
  360 + <#list sub.originalColumns as po>
  361 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  362 + {
  363 + title: '${po.filedComment}',
  364 + align:"center",
  365 + <#if po.sort=='Y'>
  366 + sorter: true,
  367 + </#if>
  368 + <#if po.classType=='date'>
  369 + dataIndex: '${po.fieldName}',
  370 + customRender:({text}) =>{
  371 + return !text?"":(text.length>10?text.substr(0,10):text)
  372 + },
  373 + <#elseif po.fieldDbType=='Blob'>
  374 + dataIndex: '${po.fieldName}String'
  375 + <#elseif po.classType=='umeditor'>
  376 + dataIndex: '${po.fieldName}',
  377 + slots: { customRender: 'htmlSlot' },
  378 + <#elseif po.classType=='pca'>
  379 + dataIndex: '${po.fieldName}',
  380 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  381 + <#elseif po.classType=='file'>
  382 + dataIndex: '${po.fieldName}',
  383 + slots: { customRender: 'fileSlot' },
  384 + <#elseif po.classType=='image'>
  385 + dataIndex: '${po.fieldName}',
  386 + customRender:render.renderAvatar,
  387 + <#elseif po.classType=='switch'>
  388 + dataIndex: '${po.fieldName}',
  389 +<#assign switch_extend_arr=['Y','N']>
  390 +<#if po.dictField?default("")?contains("[")>
  391 +<#assign switch_extend_arr=po.dictField?eval>
  392 +</#if>
  393 +<#list switch_extend_arr as a>
  394 +<#if a_index == 0>
  395 +<#assign switch_extend_arr1=a>
  396 +<#else>
  397 +<#assign switch_extend_arr2=a>
  398 +</#if>
  399 +</#list>
  400 + customRender:({text}) => {
  401 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  402 + },
  403 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  404 + dataIndex: '${po.fieldName}_dictText'
  405 + <#elseif po.classType=='cat_tree'>
  406 + dataIndex: '${po.fieldName}',
  407 + <#if po.dictText?default("")?trim?length == 0>
  408 + customRender:({text}) => {
  409 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  410 + },
  411 + <#else>
  412 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  413 + </#if>
  414 + <#else>
  415 + dataIndex: '${po.fieldName}'
  416 + </#if>
  417 + },
  418 + </#if>
  419 + </#list>
  420 +];
  421 +<#if sub.foreignRelationType =='1'>
  422 +export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
  423 +<#assign form_cat_tree = false>
  424 +<#assign form_cat_back = "">
  425 +<#assign bpm_flag=false>
  426 +<#list sub.colums as po><#rt/>
  427 +<#if po.fieldDbName=='bpm_status'>
  428 + <#assign bpm_flag=true>
  429 +</#if>
  430 +<#if po.isShow =='Y'>
  431 +<#assign form_field_dictCode="">
  432 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  433 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  434 + <#elseif po.dictField?default("")?trim?length gt 1>
  435 + <#assign form_field_dictCode="${po.dictField}">
  436 + </#if>
  437 + {
  438 + label: '${po.filedComment}',
  439 + field: '${po.fieldName}',
  440 + <#if po.classType =='date'>
  441 + component: 'DatePicker',
  442 + <#elseif po.fieldType =='datetime'>
  443 + component: 'DatePicker',
  444 + componentProps: {
  445 + showTime:true
  446 + },
  447 + <#elseif po.fieldType =='time'>
  448 + component: 'TimePicker',
  449 + <#elseif po.classType =='popup'>
  450 + component: 'JPopup',
  451 + componentProps: ({ formActionType }) => {
  452 + const {setFieldsValue} = formActionType;
  453 + return{
  454 + setFieldsValue:setFieldsValue,
  455 + code:"${po.dictTable}",
  456 + fieldConfig:${po.dictField},
  457 + multi:${po.extendParams.popupMulti?c},
  458 + }
  459 + }
  460 + <#elseif po.classType =='sel_depart'>
  461 + component: 'JSelectDept',
  462 + <#elseif po.classType =='switch'>
  463 + component: 'JSwitch',
  464 + componentProps:{
  465 + <#if po.dictField != 'is_open'>
  466 + options:${po.dictField}
  467 + </#if>
  468 + }
  469 + <#elseif po.classType =='pca'>
  470 + component: 'JAreaLinkage',
  471 + <#elseif po.classType =='markdown'>
  472 + component: 'JMarkdownEditor',//注意string转换问题
  473 + <#elseif po.classType =='password'>
  474 + component: 'InputPassword',
  475 + <#elseif po.classType =='sel_user'>
  476 + component: 'JSelectUserByDept',
  477 + componentProps:{
  478 + labelKey:'realname',
  479 + }
  480 + <#elseif po.classType =='textarea'>
  481 + component: 'InputTextArea',//TODO 注意string转换问题
  482 + <#elseif po.classType=='list' || po.classType=='radio'>
  483 + component: 'JDictSelectTag',
  484 + componentProps:{
  485 + dictCode:"${form_field_dictCode}"
  486 + }
  487 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  488 + component: 'JMultiSelectTag',//TODO 暂无该组件
  489 + componentProps:{
  490 + dictCode:"${form_field_dictCode}"
  491 + }
  492 + <#elseif po.classType=='sel_search'>
  493 + component: 'JSearchSelect',
  494 + componentProps:{
  495 + dict:"${form_field_dictCode}"
  496 + }
  497 +<#elseif po.classType=='cat_tree'>
  498 + <#assign form_cat_tree = true>
  499 + component: 'JCategorySelect',
  500 + componentProps:{
  501 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  502 + }
  503 + <#if po.dictText?default("")?trim?length gt 1>
  504 + <#assign form_cat_back = "${po.dictText}">
  505 + </#if>
  506 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  507 + component: 'InputNumber',
  508 + <#elseif po.classType=='file'>
  509 + component: 'JUpload',
  510 + componentProps:{
  511 + <#if po.uploadnum??>
  512 + maxCount:${po.uploadnum}
  513 + </#if>
  514 + }
  515 + <#elseif po.classType=='image'>
  516 + component: 'JImageUpload',
  517 + componentProps:{
  518 + <#if po.uploadnum??>
  519 + fileMax:${po.uploadnum}
  520 + </#if>
  521 + }
  522 + <#elseif po.classType=='umeditor'>
  523 + component: 'JCodeEditor', //TODO String后缀暂未添加
  524 + <#elseif po.classType == 'sel_tree'>
  525 + component: 'JTreeSelect',
  526 + componentProps:{
  527 + <#if po.dictText??>
  528 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  529 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  530 + <#elseif po.dictText?split(',')[1]??>
  531 + pidField:"${po.dictText?split(',')[1]}",
  532 + <#elseif po.dictText?split(',')[3]??>
  533 + hasChildField:"${po.dictText?split(',')[3]}",
  534 + </#if>
  535 + </#if>
  536 + pidValue:"${po.dictField}",
  537 + }
  538 + <#else>
  539 + component: 'Input',
  540 + </#if>
  541 + <#include "/common/utils.ftl">
  542 + <#if po.isShow == 'Y' && poHasCheck(po)>
  543 + dynamicRules: ({model,schema}) => {
  544 + <#if po.fieldName != 'id'>
  545 + <#assign fieldValidType = po.fieldValidType!''>
  546 + return [
  547 + <#-- 非空校验 -->
  548 + <#if po.nullable == 'N' || fieldValidType == '*'>
  549 + { required: true, message: '请输入${po.filedComment}!'},
  550 + <#elseif fieldValidType!=''>
  551 + { required: false},
  552 + </#if>
  553 + <#-- 唯一校验 -->
  554 + <#if fieldValidType == 'only'>
  555 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  556 + <#-- 6到16位数字 -->
  557 + <#elseif fieldValidType == 'n6-16'>
  558 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  559 + <#-- 6到16位任意字符 -->
  560 + <#elseif fieldValidType == '*6-16'>
  561 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  562 + <#-- 6到18位字符串 -->
  563 + <#elseif fieldValidType == 's6-18'>
  564 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  565 + <#-- 网址 -->
  566 + <#elseif fieldValidType == 'url'>
  567 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  568 + <#-- 电子邮件 -->
  569 + <#elseif fieldValidType == 'e'>
  570 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  571 + <#-- 手机号码 -->
  572 + <#elseif fieldValidType == 'm'>
  573 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  574 + <#-- 邮政编码 -->
  575 + <#elseif fieldValidType == 'p'>
  576 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  577 + <#-- 字母 -->
  578 + <#elseif fieldValidType == 's'>
  579 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  580 + <#-- 数字 -->
  581 + <#elseif fieldValidType == 'n'>
  582 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  583 + <#-- 整数 -->
  584 + <#elseif fieldValidType == 'z'>
  585 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  586 + <#-- 金额 -->
  587 + <#elseif fieldValidType == 'money'>
  588 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  589 + <#-- 正则校验 -->
  590 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  591 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  592 + <#-- 无校验 -->
  593 + <#else>
  594 + <#t>
  595 + </#if>
  596 + ];
  597 + </#if>
  598 + },
  599 + </#if>
  600 + <#if po.readonly=='Y'>
  601 + dynamicDisabled:true
  602 + </#if>
  603 + },
  604 +</#if>
  605 +</#list>
  606 +];
  607 +</#if>
  608 +</#list>
  609 +//子表表格配置
  610 +<#list subTables as sub>
  611 +<#if sub.foreignRelationType =='0'>
  612 +export const ${sub.entityName?uncap_first}JVxeColumns: JVxeColumn[] = [
  613 +<#assign popupBackFields = "">
  614 +
  615 +<#-- 循环子表的列 开始 -->
  616 +<#list sub.colums as col><#rt/>
  617 +<#if col.isShow =='Y'>
  618 +<#if col.filedComment !='外键' >
  619 + {
  620 + title: '${col.filedComment}',
  621 + key: '${autoStringSuffixForModel(col)}',
  622 +<#if col.classType =='date'>
  623 + type: JVxeTypes.date,
  624 + <#if col.readonly=='Y'>
  625 + disabled:true,
  626 + </#if>
  627 +<#elseif col.classType =='datetime'>
  628 + type: JVxeTypes.datetime,
  629 + <#if col.readonly=='Y'>
  630 + disabled:true,
  631 + </#if>
  632 +<#elseif col.classType =='textarea'>
  633 + type: JVxeTypes.textarea,
  634 + <#if col.readonly=='Y'>
  635 + disabled:true,
  636 + </#if>
  637 +<#elseif "int,decimal,double,"?contains(col.classType)>
  638 + type: JVxeTypes.inputNumber,
  639 + <#if col.readonly=='Y'>
  640 + disabled:true,
  641 + </#if>
  642 +<#elseif col.classType =='list' || col.classType =='radio'>
  643 + type: JVxeTypes.select,
  644 + options:[],
  645 + <#if col.dictTable?default("")?trim?length gt 1>
  646 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  647 + <#else>
  648 + dictCode:"${col.dictField}",
  649 + </#if>
  650 + <#if col.readonly=='Y'>
  651 + disabled:true,
  652 + </#if>
  653 +<#elseif col.classType =='list_multi' || col.classType =='checkbox'>
  654 + type: JVxeTypes.selectMultiple,
  655 + options:[],
  656 + <#if col.dictTable?default("")?trim?length gt 1>
  657 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  658 + <#else>
  659 + dictCode:"${col.dictField}",
  660 + </#if>
  661 + <#if col.readonly=='Y'>
  662 + disabled:true,
  663 + </#if>
  664 +<#elseif col.classType =='sel_search'>
  665 + type: JVxeTypes.selectSearch,
  666 + <#if col.dictTable?default("")?trim?length gt 1>
  667 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  668 + <#else>
  669 + dictCode:"${col.dictField}",
  670 + </#if>
  671 + <#if col.readonly=='Y'>
  672 + disabled:true,
  673 + </#if>
  674 +<#elseif col.classType =='image'>
  675 + type: JVxeTypes.image,
  676 + token:true,
  677 + responseName:"message",
  678 + <#if col.readonly=='Y'>
  679 + disabled:true,
  680 + </#if>
  681 + <#if col.uploadnum??>
  682 + number: ${col.uploadnum},
  683 + </#if>
  684 +<#elseif col.classType =='file'>
  685 + type: JVxeTypes.file,
  686 + token:true,
  687 + responseName:"message",
  688 + <#if col.readonly=='Y'>
  689 + disabled:true,
  690 + </#if>
  691 + <#if col.uploadnum??>
  692 + number: ${col.uploadnum},
  693 + </#if>
  694 +<#elseif col.classType =='switch'>
  695 + type: JVxeTypes.checkbox,
  696 + <#if col.dictField == 'is_open'>
  697 + customValue: ['Y', 'N'],
  698 + <#else>
  699 + customValue: ${col.dictField},
  700 + </#if>
  701 + <#if col.readonly=='Y'>
  702 + disabled:true,
  703 + </#if>
  704 +<#elseif col.classType =='popup'>
  705 +<#if popupBackFields?length gt 0>
  706 + <#assign popupBackFields = "${popupBackFields}"+","+"${col.dictText}">
  707 +<#else>
  708 + <#assign popupBackFields = "${col.dictText}">
  709 +</#if>
  710 + type: JVxeTypes.popup,
  711 + popupCode:"${col.dictTable}",
  712 + field:"${col.dictField}",
  713 + orgFields:"${col.dictField}",
  714 + destFields:"${Format.underlineToHump(col.dictText)}",
  715 + <#if col.readonly=='Y'>
  716 + disabled:true,
  717 + </#if>
  718 +<#else>
  719 + type: JVxeTypes.input,
  720 + <#if col.readonly=='Y'>
  721 + disabled:true,
  722 + </#if>
  723 +</#if>
  724 +<#if col.classType =='list_multi' || col.classType =='checkbox'>
  725 + width:"250px",
  726 +<#else>
  727 + width:"200px",
  728 +</#if>
  729 +<#if col.classType =='file'>
  730 + placeholder: '请选择文件',
  731 +<#else>
  732 + placeholder: '请输入${'$'}{title}',
  733 +</#if>
  734 +<#if col.defaultVal??>
  735 +<#if col.fieldDbType=="BigDecimal" || col.fieldDbType=="double" || col.fieldDbType=="int">
  736 + defaultValue:${col.defaultVal},
  737 + <#else>
  738 + defaultValue:"${col.defaultVal}",
  739 +</#if>
  740 +<#else>
  741 + defaultValue:'',
  742 +</#if>
  743 +<#-- 子表的校验 -->
  744 +<#assign subFieldValidType = col.fieldValidType!''>
  745 +<#-- 非空校验 -->
  746 +<#if col.nullable == 'N' || subFieldValidType == '*'>
  747 + validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
  748 +<#-- 其他情况下,只要有值就被认为是正则校验 -->
  749 +<#elseif subFieldValidType?length gt 0>
  750 +<#assign subMessage = '格式不正确'>
  751 +<#if subFieldValidType == 'only' >
  752 + <#assign subMessage = '不能重复'>
  753 +</#if>
  754 + validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
  755 +</#if>
  756 + },
  757 +</#if>
  758 +</#if>
  759 +</#list>
  760 +<#-- 循环子表的列 结束 -->
  761 + ]
  762 +</#if>
  763 +</#list>
0 764 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicForm @register="registerForm" ref="formRef"/>
  4 + <!-- 子表单区域 -->
  5 + <a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
  6 +<#list subTables as sub><#rt/>
  7 + <#if sub.foreignRelationType =='1'>
  8 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
  9 + <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
  10 + </a-tab-pane>
  11 +
  12 + <#else>
  13 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
  14 + <JVxeTable
  15 + keep-source
  16 + resizable
  17 + :ref="refKeys[${sub_index}]"
  18 + :loading="${sub.entityName?uncap_first}Table.loading"
  19 + :columns="${sub.entityName?uncap_first}Table.columns"
  20 + :dataSource="${sub.entityName?uncap_first}Table.dataSource"
  21 + :maxHeight="300"
  22 + :rowNumber="true"
  23 + :rowSelection="true"
  24 + :toolbar="true"
  25 + />
  26 + </a-tab-pane>
  27 + </#if>
  28 +</#list>
  29 + </a-tabs>
  30 + </BasicModal>
  31 +</template>
  32 +
  33 +<script lang="ts" setup>
  34 + import {ref, computed, unref,reactive} from 'vue';
  35 + import {BasicModal, useModalInner} from '/@/components/Modal';
  36 + import {BasicForm, useForm} from '/@/components/Form/index';
  37 + import { JVxeTable } from '/@/components/jeecg/JVxeTable'
  38 + import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'
  39 + <#list subTables as sub>
  40 + <#if sub.foreignRelationType =='1'>
  41 + import ${sub.entityName}Form from './${sub.entityName}Form.vue'
  42 + </#if>
  43 + </#list>
  44 + import {formSchema<#list subTables as sub><#if sub.foreignRelationType =='0'>,${sub.entityName?uncap_first}JVxeColumns</#if></#list>} from '../${entityName?uncap_first}.data';
  45 + import {saveOrUpdate<#list subTables as sub>,query${sub.entityName}</#list>} from '../${entityName?uncap_first}.api';
  46 + import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
  47 + // Emits声明
  48 + const emit = defineEmits(['register','success']);
  49 + const isUpdate = ref(true);
  50 + const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
  51 + <#assign hasOne2Many = false>
  52 + <#assign hasOne2One = false>
  53 + const activeKey = ref('${subTables[0].entityName?uncap_first}');
  54 +<#list subTables as sub>
  55 +<#if sub.foreignRelationType =='0'>
  56 + <#assign hasOne2Many = true>
  57 + const ${sub.entityName?uncap_first} = ref();
  58 +</#if>
  59 +<#if sub.foreignRelationType =='1'>
  60 + <#assign hasOne2One = true>
  61 + const ${sub.entityName?uncap_first}Form = ref();
  62 +</#if>
  63 +</#list>
  64 + const tableRefs = {<#list subTables as sub><#if sub.foreignRelationType =='0'>${sub.entityName?uncap_first}, <#assign hasOne2Many = true></#if></#list>};
  65 + <#list subTables as sub>
  66 + <#if sub.foreignRelationType =='0'>
  67 + const ${sub.entityName?uncap_first}Table = reactive({
  68 + loading: false,
  69 + dataSource: [],
  70 + columns:${sub.entityName?uncap_first}JVxeColumns
  71 + })
  72 + </#if>
  73 + </#list>
  74 + //表单配置
  75 + const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
  76 + labelWidth: 150,
  77 + schemas: formSchema,
  78 + showActionButtonGroup: false,
  79 + });
  80 + //表单赋值
  81 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  82 + //重置表单
  83 + await reset();
  84 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  85 + isUpdate.value = !!data?.isUpdate;
  86 + if (unref(isUpdate)) {
  87 + //表单赋值
  88 + await setFieldsValue({
  89 + ...data.record,
  90 + });
  91 + <#list subTables as sub><#rt/>
  92 + <#if sub.foreignRelationType =='1'>
  93 + ${sub.entityName?uncap_first}Form.value.initFormData(query${sub.entityName},data?.record?.id)
  94 + </#if>
  95 + </#list>
  96 + <#list subTables as sub><#rt/>
  97 + <#if sub.foreignRelationType =='0'>
  98 + requestSubTableData(query${sub.entityName}, {id:data?.record?.id}, ${sub.entityName?uncap_first}Table)
  99 + </#if>
  100 + </#list>
  101 + }
  102 + // 隐藏底部时禁用整个表单
  103 + setProps({ disabled: !data?.showFooter })
  104 + });
  105 + //方法配置
  106 + const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys<#if hasOne2One==true>,validateSubForm</#if>);
  107 +
  108 + //设置标题
  109 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  110 +
  111 + async function reset(){
  112 + await resetFields();
  113 + activeKey.value = ref('${subTables[0].entityName?uncap_first}');
  114 + <#list subTables as sub>
  115 + <#if sub.foreignRelationType =='0'>
  116 + ${sub.entityName?uncap_first}Table.dataSource = [];
  117 + </#if>
  118 + <#if sub.foreignRelationType =='1'>
  119 + ${sub.entityName?uncap_first}Form.value.resetFields();
  120 + </#if>
  121 + </#list>
  122 + }
  123 + function classifyIntoFormData(allValues) {
  124 + let main = Object.assign({}, allValues.formValue)
  125 + return {
  126 + ...main, // 展开
  127 + <#assign subManyIndex = 0>
  128 + <#list subTables as sub><#rt/>
  129 + <#if sub.foreignRelationType =='0'>
  130 + ${sub.entityName?uncap_first}List: allValues.tablesValue[${subManyIndex}].tableData,
  131 + <#assign subManyIndex = subManyIndex+1>
  132 + <#else>
  133 + ${sub.entityName?uncap_first}List: ${sub.entityName?uncap_first}Form.value.getFormData(),
  134 + </#if>
  135 + </#list>
  136 + }
  137 + }
  138 + <#if hasOne2One==true>
  139 + //校验所有一对一子表表单
  140 + function validateSubForm(allValues){
  141 + return new Promise((resolve,reject)=>{
  142 + Promise.all([
  143 + <#list subTables as sub><#rt/>
  144 + <#if sub.foreignRelationType =='1'>
  145 + ${sub.entityName?uncap_first}Form.value.validateForm(${sub_index}),
  146 + </#if>
  147 + </#list>
  148 + ]).then(() => {
  149 + resolve(allValues)
  150 + }).catch(e => {
  151 + if (e.error === VALIDATE_FAILED) {
  152 + // 如果有未通过表单验证的子表,就自动跳转到它所在的tab
  153 + activeKey.value = e.index == null ? unref(activeKey) : refKeys.value[e.index]
  154 + } else {
  155 + console.error(e)
  156 + }
  157 + })
  158 + })
  159 + }
  160 + </#if>
  161 + //表单提交事件
  162 + async function requestAddOrEdit(values) {
  163 + try {
  164 + setModalProps({confirmLoading: true});
  165 + //提交表单
  166 + await saveOrUpdate(values, isUpdate.value);
  167 + //关闭弹窗
  168 + closeModal();
  169 + //刷新列表
  170 + emit('success');
  171 + } finally {
  172 + setModalProps({confirmLoading: false});
  173 + }
  174 + }
  175 +</script>
  176 +
  177 +<style lang="less" scoped>
  178 +
  179 +</style>
0 180 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Form.vuei 0 → 100644
  1 +<#list subTables as sub>
  2 +<#if sub.foreignRelationType=='1'>
  3 +#segment#${sub.entityName}Form.vue
  4 +<template>
  5 + <BasicForm @register="registerForm"/>
  6 +</template>
  7 +<script lang="ts">
  8 + import {defineComponent} from 'vue';
  9 + import {BasicForm, useForm} from '/@/components/Form/index';
  10 + import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
  11 + import {defHttp} from '/@/utils/http/axios';
  12 + import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
  13 +
  14 + export default defineComponent({
  15 + name:"${sub.entityName}Form",
  16 + components: {BasicForm},
  17 + emits:['register'],
  18 + setup(_,{emit}) {
  19 + const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
  20 + labelWidth: 150,
  21 + schemas: ${sub.entityName?uncap_first}FormSchema,
  22 + showActionButtonGroup: false,
  23 + });
  24 + /**
  25 + *初始化加载数据
  26 + */
  27 + function initFormData(url,id){
  28 + if(id){
  29 + defHttp.get({url,params:{id}},{isTransformResponse:false}).then(res=>{
  30 + res.success && setFieldsValue({...res.result.records[0]});
  31 + })
  32 + }
  33 + }
  34 + /**
  35 + *获取表单数据
  36 + */
  37 + function getFormData(){
  38 + return [getFieldsValue()];
  39 + }
  40 + /**
  41 + *表单校验
  42 + */
  43 + function validateForm(index){
  44 + return new Promise((resolve, reject) => {
  45 + // 验证子表表单
  46 + validate().then(()=>{
  47 + return resolve()
  48 + }).catch(()=> {
  49 + return reject({ error: VALIDATE_FAILED ,index})
  50 + })
  51 + })
  52 + }
  53 + return {
  54 + registerForm,
  55 + resetFields,
  56 + initFormData,
  57 + getFormData,
  58 + validateForm
  59 + }
  60 + }
  61 + })
  62 +</script>
  63 +</#if>
  64 +</#list>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vue3/subTables/[1-n]SubTable.vuei 0 → 100644
  1 +<#--noinspection JSDuplicatedDeclaration-->
  2 +<#list subTables as sub>
  3 +#segment#${sub.entityName}SubTable.vue
  4 +<template>
  5 + <div>
  6 + <#assign list_need_category=false>
  7 + <#assign list_need_pca=false>
  8 + <#assign bpm_flag=false>
  9 +
  10 + <#-- 开始循环 -->
  11 + <#list columns as po>
  12 + <#if po.fieldDbName=='bpm_status'>
  13 + <#assign bpm_flag=true>
  14 + </#if>
  15 + <#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  16 + <#assign list_need_category=true>
  17 + </#if>
  18 + <#if po.classType=='pca'>
  19 + <#assign list_need_pca=true>
  20 + </#if>
  21 + </#list>
  22 + <#-- 结束循环 -->
  23 + <!--引用表格-->
  24 + <BasicTable bordered size="middle" :loading="loading" rowKey="id" :canResize="false" :columns="${sub.entityName?uncap_first}Columns" :dataSource="dataSource" :pagination="false">
  25 + <!--字段回显插槽-->
  26 + <template #htmlSlot="{text}">
  27 + <div v-html="text"></div>
  28 + </template>
  29 + <template #fileSlot="{text}">
  30 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  31 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  32 + </template>
  33 + </BasicTable>
  34 + </div>
  35 +</template>
  36 +
  37 +<script lang="ts" setup>
  38 + import {ref,watchEffect} from 'vue';
  39 + import {BasicTable} from '/@/components/Table';
  40 + import {${sub.entityName?uncap_first}Columns} from '../${entityName}.data';
  41 + import {${sub.entityName?uncap_first}List} from '../${entityName}.api';
  42 +
  43 + const props = defineProps({
  44 + id: {
  45 + type: String,
  46 + default: '',
  47 + },
  48 + })
  49 +
  50 + const loading = ref(false);
  51 + const dataSource = ref([]);
  52 +
  53 + watchEffect(() => {
  54 + props.id && loadData(props.id);
  55 + });
  56 +
  57 + function loadData(id) {
  58 + dataSource.value = []
  59 + loading.value = true
  60 + ${sub.entityName?uncap_first}List({id}).then((res) => {
  61 + if (res.success) {
  62 + dataSource.value = res.result.records
  63 + }
  64 + }).finally(() => {
  65 + loading.value = false
  66 + })
  67 + }
  68 +</script>
  69 +</#list>
0 70 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -28,13 +28,8 @@
28 28 <#elseif po.dictField?default("")?trim?length gt 1>
29 29 <#assign form_field_dictCode="${po.dictField}">
30 30 </#if>
31   - <#if po.classType =='textarea'>
32   - <a-col :span="24">
33   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
34   - <#else>
35 31 <a-col :span="${form_span}" >
36 32 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
37   - </#if>
38 33 <#if po.classType =='date'>
39 34 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
40 35 <#elseif po.classType =='datetime'>
... ... @@ -140,7 +135,7 @@
140 135 </#list>
141 136 </a-tabs>
142 137 <#if bpm_flag>
143   - <a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button @click="handleOk">提 交</a-button></a-row>
  138 + <a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button icon="check" style="width: 126px" type="primary" @click="handleOk">提 交</a-button></a-row>
144 139 </#if>
145 140 </a-spin>
146 141 </template>
... ... @@ -174,20 +169,12 @@
174 169 return {
175 170 labelCol: {
176 171 xs: { span: 24 },
177   - sm: { span: 6 },
  172 + sm: { span: 5 },
178 173 },
179 174 wrapperCol: {
180 175 xs: { span: 24 },
181 176 sm: { span: 16 },
182 177 },
183   - labelCol2: {
184   - xs: { span: 24 },
185   - sm: { span: 3 },
186   - },
187   - wrapperCol2: {
188   - xs: { span: 24 },
189   - sm: { span: 20 },
190   - },
191 178 model:{
192 179 <#include "/common/init/initValue.ftl">
193 180 },
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -25,13 +25,8 @@
25 25 <#elseif po.dictField?default("")?trim?length gt 1>
26 26 <#assign form_field_dictCode="${po.dictField}">
27 27 </#if>
28   - <#if po.classType =='textarea'>
29   - <a-col :span="24">
30   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
31   - <#else>
32 28 <a-col :span="${form_span}">
33 29 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
34   - </#if>
35 30 <#if po.classType =='date'>
36 31 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
37 32 <#elseif po.classType =='datetime'>
... ... @@ -117,20 +112,12 @@
117 112 },
118 113 labelCol: {
119 114 xs: { span: 24 },
120   - sm: { span: 6 },
  115 + sm: { span: 5 },
121 116 },
122 117 wrapperCol: {
123 118 xs: { span: 24 },
124 119 sm: { span: 16 },
125 120 },
126   - labelCol2: {
127   - xs: { span: 24 },
128   - sm: { span: 3 },
129   - },
130   - wrapperCol2: {
131   - xs: { span: 24 },
132   - sm: { span: 20 },
133   - },
134 121 <#include "/common/validatorRulesTemplate/sub.ftl">
135 122 confirmLoading: false,
136 123 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 +<#assign list_need_category=false>
  4 +<#assign list_need_pca=false>
  5 +<#assign bpm_flag=false>
  6 +
  7 +<#-- 开始循环 -->
  8 +<#list columns as po>
  9 +<#if po.fieldDbName=='bpm_status'>
  10 + <#assign bpm_flag=true>
  11 +</#if>
  12 +<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  13 +<#assign list_need_category=true>
  14 +</#if>
  15 +<#if po.classType=='pca'>
  16 +<#assign list_need_pca=true>
  17 +</#if>
  18 +</#list>
  19 +<#-- 结束循环 -->
  20 + <!--引用表格-->
  21 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  22 + <!--插槽:table标题-->
  23 + <template #tableTitle>
  24 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  25 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  26 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  27 + <a-dropdown v-if="checkedKeys.length > 0">
  28 + <template #overlay>
  29 + <a-menu>
  30 + <a-menu-item key="1" @click="batchHandleDelete">
  31 + <Icon icon="ant-design:delete-outlined"></Icon>
  32 + 删除
  33 + </a-menu-item>
  34 + </a-menu>
  35 + </template>
  36 + <a-button>批量操作
  37 + <Icon icon="mdi:chevron-down"></Icon>
  38 + </a-button>
  39 + </a-dropdown>
  40 + </template>
  41 + <!--操作栏-->
  42 + <template #action="{ record }">
  43 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  44 + </template>
  45 + <!--字段回显插槽-->
  46 + <template #htmlSlot="{text}">
  47 + <div v-html="text"></div>
  48 + </template>
  49 + <template #fileSlot="{text}">
  50 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  51 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  52 + </template>
  53 + </BasicTable>
  54 + <!-- 表单区域 -->
  55 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  56 + </div>
  57 +</template>
  58 +
  59 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  60 + import {ref, computed, unref} from 'vue';
  61 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  62 + import { useListPage } from '/@/hooks/system/useListPage'
  63 + import {useModal} from '/@/components/Modal';
  64 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  65 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  66 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  67 + <#if list_need_category>
  68 + import { loadCategoryData } from '/@/api/common/api'
  69 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  70 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  71 + </#if>
  72 + const checkedKeys = ref<Array<string | number>>([]);
  73 + //注册model
  74 + const [registerModal, {openModal}] = useModal();
  75 + //注册table数据
  76 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  77 + tableProps:{
  78 + title: '${tableVo.ftlDescription}',
  79 + api: list,
  80 + columns,
  81 + canResize:false,
  82 + formConfig: {
  83 + labelWidth: 120,
  84 + schemas: searchFormSchema,
  85 + autoSubmitOnEnter:true,
  86 + showAdvancedButton:true,
  87 + fieldMapToTime: [
  88 + <#list columns as po>
  89 + <#if po.isQuery=='Y'>
  90 + <#if po.queryMode!='single'>
  91 + <#if po.classType=='date'>
  92 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
  93 + <#elseif po.classType=='datetime'>
  94 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
  95 + </#if>
  96 + </#if>
  97 + </#if>
  98 + </#list>
  99 + ],
  100 + },
  101 + actionColumn: {
  102 + width: 120,
  103 + },
  104 + },
  105 + exportConfig: {
  106 + name:"${tableVo.ftlDescription}",
  107 + url: getExportUrl,
  108 + },
  109 + importConfig: {
  110 + url: getImportUrl
  111 + },
  112 + })
  113 +
  114 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  115 +
  116 + /**
  117 + * 新增事件
  118 + */
  119 + function handleAdd() {
  120 + openModal(true, {
  121 + isUpdate: false,
  122 + showFooter: true,
  123 + });
  124 + }
  125 + /**
  126 + * 编辑事件
  127 + */
  128 + function handleEdit(record: Recordable) {
  129 + openModal(true, {
  130 + record,
  131 + isUpdate: true,
  132 + showFooter: true,
  133 + });
  134 + }
  135 + /**
  136 + * 详情
  137 + */
  138 + function handleDetail(record: Recordable) {
  139 + openModal(true, {
  140 + record,
  141 + isUpdate: true,
  142 + showFooter: false,
  143 + });
  144 + }
  145 + /**
  146 + * 删除事件
  147 + */
  148 + async function handleDelete(record) {
  149 + await deleteOne({id: record.id}, reload);
  150 + }
  151 + /**
  152 + * 批量删除事件
  153 + */
  154 + async function batchHandleDelete() {
  155 + await batchDelete({ids: checkedKeys.value}, reload);
  156 + }
  157 + /**
  158 + * 成功回调
  159 + */
  160 + function handleSuccess() {
  161 + reload();
  162 + }
  163 + /**
  164 + * 操作栏
  165 + */
  166 + function getTableAction(record){
  167 + return [
  168 + {
  169 + label: '编辑',
  170 + onClick: handleEdit.bind(null, record),
  171 + }
  172 + ]
  173 + }
  174 + /**
  175 + * 下拉操作栏
  176 + */
  177 + function getDropDownAction(record){
  178 + return [
  179 + {
  180 + label: '详情',
  181 + onClick: handleDetail.bind(null, record),
  182 + }, {
  183 + label: '删除',
  184 + popConfirm: {
  185 + title: '是否确认删除',
  186 + confirm: handleDelete.bind(null, record),
  187 + }
  188 + }
  189 + ]
  190 + }
  191 + <#if list_need_category>
  192 + /**
  193 + * 初始化字典配置
  194 + */
  195 + function initDictConfig(){
  196 + <#list columns as po>
  197 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  198 + <#if po.classType=='cat_tree' && list_need_category==true>
  199 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  200 + if (res) {
  201 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  202 + if(!allDictDate['${po.dictField?default("")}']){
  203 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  204 + }
  205 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  206 + }
  207 + })
  208 + </#if>
  209 + </#if>
  210 + </#list>
  211 + }
  212 + initDictConfig();
  213 + </#if>
  214 +</script>
  215 +
  216 +<style scoped>
  217 +
  218 +</style>
0 219 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +<#list subTables as sub><#rt/>
  13 + ${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
  14 +</#list>
  15 +}
  16 +/**
  17 + * 导出api
  18 + * @param params
  19 + */
  20 +export const getExportUrl = Api.exportXls;
  21 +
  22 +/**
  23 + * 导入api
  24 + */
  25 +export const getImportUrl = Api.importExcel;
  26 +<#list subTables as sub><#rt/>
  27 +/**
  28 + * 查询子表数据
  29 + * @param params
  30 + */
  31 +export const ${sub.entityName?uncap_first}List = Api.${sub.entityName?uncap_first}List;
  32 +</#list>
  33 +/**
  34 + * 列表接口
  35 + * @param params
  36 + */
  37 +export const list = (params) =>
  38 + defHttp.get({url: Api.list, params});
  39 +
  40 +/**
  41 + * 删除单个
  42 + */
  43 +export const deleteOne = (params,handleSuccess) => {
  44 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  45 + handleSuccess();
  46 + });
  47 +}
  48 +/**
  49 + * 批量删除
  50 + * @param params
  51 + */
  52 +export const batchDelete = (params, handleSuccess) => {
  53 + Modal.confirm({
  54 + title: '确认删除',
  55 + content: '是否删除选中数据',
  56 + okText: '确认',
  57 + cancelText: '取消',
  58 + onOk: () => {
  59 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  60 + handleSuccess();
  61 + });
  62 + }
  63 + });
  64 +}
  65 +/**
  66 + * 保存或者更新
  67 + * @param params
  68 + */
  69 +export const saveOrUpdate = (params, isUpdate) => {
  70 + let url = isUpdate ? Api.edit : Api.save;
  71 + return defHttp.post({url: url, params});
  72 +}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
  6 +//列表数据
  7 +export const columns: BasicColumn[] = [
  8 + <#list columns as po>
  9 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  10 + {
  11 + title: '${po.filedComment}',
  12 + align:"center",
  13 + <#if po.sort=='Y'>
  14 + sorter: true,
  15 + </#if>
  16 + <#if po.classType=='date'>
  17 + dataIndex: '${po.fieldName}',
  18 + customRender:({text}) =>{
  19 + return !text?"":(text.length>10?text.substr(0,10):text)
  20 + },
  21 + <#elseif po.fieldDbType=='Blob'>
  22 + dataIndex: '${po.fieldName}String'
  23 + <#elseif po.classType=='umeditor'>
  24 + dataIndex: '${po.fieldName}',
  25 + slots: { customRender: 'htmlSlot' },
  26 + <#elseif po.classType=='pca'>
  27 + dataIndex: '${po.fieldName}',
  28 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  29 + <#elseif po.classType=='file'>
  30 + dataIndex: '${po.fieldName}',
  31 + slots: { customRender: 'fileSlot' },
  32 + <#elseif po.classType=='image'>
  33 + dataIndex: '${po.fieldName}',
  34 + customRender:render.renderAvatar,
  35 + <#elseif po.classType=='switch'>
  36 + dataIndex: '${po.fieldName}',
  37 +<#assign switch_extend_arr=['Y','N']>
  38 +<#if po.dictField?default("")?contains("[")>
  39 +<#assign switch_extend_arr=po.dictField?eval>
  40 +</#if>
  41 +<#list switch_extend_arr as a>
  42 +<#if a_index == 0>
  43 +<#assign switch_extend_arr1=a>
  44 +<#else>
  45 +<#assign switch_extend_arr2=a>
  46 +</#if>
  47 +</#list>
  48 + customRender:({text}) => {
  49 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  50 + },
  51 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  52 + dataIndex: '${po.fieldName}_dictText'
  53 + <#elseif po.classType=='cat_tree'>
  54 + dataIndex: '${po.fieldName}',
  55 + <#if po.dictText?default("")?trim?length == 0>
  56 + customRender:({text}) => {
  57 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  58 + },
  59 + <#else>
  60 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  61 + </#if>
  62 + <#else>
  63 + dataIndex: '${po.fieldName}'
  64 + </#if>
  65 + },
  66 + </#if>
  67 + </#list>
  68 +];
  69 +//查询数据
  70 +export const searchFormSchema: FormSchema[] = [
  71 +<#-- 开始循环 -->
  72 +<#list columns as po>
  73 +<#if po.fieldDbName=='bpm_status'>
  74 + <#assign bpm_flag=true>
  75 +</#if>
  76 +<#if po.isQuery=='Y'>
  77 +<#assign query_flag=true>
  78 + <#assign query_field_dictCode="">
  79 + <#if po.dictTable?default("")?trim?length gt 1>
  80 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  81 + <#elseif po.dictField?default("")?trim?length gt 1>
  82 + <#assign query_field_dictCode="${po.dictField}">
  83 + </#if>
  84 +<#if po.queryMode=='single'>
  85 + {
  86 + label: "${po.filedComment}",
  87 + field: "${po.fieldName}",
  88 +<#if po.classType=='sel_search'>
  89 + component: 'JSearchSelect',
  90 + componentProps:{
  91 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  92 + },
  93 +<#elseif po.classType=='sel_user'>
  94 + component: 'JSelectUserByDept',
  95 +<#elseif po.classType=='switch'>
  96 + component: 'JSwitch',
  97 + componentProps:{
  98 + <#if po.dictField != 'is_open'>
  99 + options:"${po.dictField}"
  100 + </#if>
  101 + },
  102 + <#elseif po.classType=='sel_depart'>
  103 + component: 'JSelectDept',
  104 + <#elseif po.classType=='list_multi'>
  105 + component: 'JMultiSelectTag',//暂无该组件
  106 + componentProps:{
  107 + dictCode:"query_field_dictCode?default("")"
  108 + },
  109 + <#elseif po.classType=='cat_tree'>
  110 + component: 'JCategorySelect',
  111 + componentProps:{
  112 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  113 + },
  114 +<#elseif po.classType=='date'>
  115 + component: 'DatePicker',
  116 +<#elseif po.classType=='datetime'>
  117 + component: 'DatePicker',
  118 + componentProps: {
  119 + showTime:true
  120 + },
  121 +<#elseif po.classType=='pca'>
  122 + component: 'JAreaLinkage',
  123 +<#elseif po.classType=='popup'>
  124 + component: 'JPopup',
  125 + componentProps: ({ formActionType }) => {
  126 + const {setFieldsValue} = formActionType;
  127 + return{
  128 + setFieldsValue:setFieldsValue,
  129 + code:"${po.dictTable}",
  130 + fieldConfig:"${po.dictField}",
  131 + multi:${po.extendParams.popupMulti?c},
  132 + }
  133 + },
  134 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  135 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  136 + component: 'JDictSelectTag',
  137 + componentProps:{
  138 + <#if po.dictTable?default("")?trim?length gt 1>
  139 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
  140 + <#elseif po.dictField?default("")?trim?length gt 1>
  141 + dictCode:"${po.dictField}"
  142 + </#if>
  143 + },
  144 +<#else>
  145 + component: 'Input',
  146 +</#if>
  147 + colProps: {span: 6},
  148 + },
  149 +<#else>
  150 + {
  151 + label: "${po.filedComment}",
  152 + field: "${po.fieldName}",
  153 +<#if po.classType=='date'>
  154 + component: 'RangePicker',
  155 +<#elseif po.classType=='datetime'>
  156 + component: 'RangePicker',
  157 + componentProps: {
  158 + showTime:true
  159 + },
  160 +<#else>
  161 + component: 'Input', //TODO 范围查询
  162 +</#if>
  163 + colProps: {span: 6},
  164 + },
  165 +</#if>
  166 +</#if>
  167 +</#list>
  168 +<#-- 结束循环 -->
  169 +];
  170 +//表单数据
  171 +export const formSchema: FormSchema[] = [
  172 +<#assign form_cat_tree = false>
  173 +<#assign form_cat_back = "">
  174 +<#assign bpm_flag=false>
  175 +<#list columns as po><#rt/>
  176 +<#if po.fieldDbName=='bpm_status'>
  177 + <#assign bpm_flag=true>
  178 +</#if>
  179 +<#if po.isShow =='Y'>
  180 +<#assign form_field_dictCode="">
  181 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  182 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  183 + <#elseif po.dictField?default("")?trim?length gt 1>
  184 + <#assign form_field_dictCode="${po.dictField}">
  185 + </#if>
  186 + {
  187 + label: '${po.filedComment}',
  188 + field: '${po.fieldName}',
  189 + <#if po.classType =='date'>
  190 + component: 'DatePicker',
  191 + <#elseif po.fieldType =='datetime'>
  192 + component: 'DatePicker',
  193 + componentProps: {
  194 + showTime:true
  195 + },
  196 + <#elseif po.fieldType =='time'>
  197 + component: 'TimePicker',
  198 + <#elseif po.classType =='popup'>
  199 + component: 'JPopup',
  200 + componentProps: ({ formActionType }) => {
  201 + const {setFieldsValue} = formActionType;
  202 + return{
  203 + setFieldsValue:setFieldsValue,
  204 + code:"${po.dictTable}",
  205 + fieldConfig:${po.dictField},
  206 + multi:${po.extendParams.popupMulti?c},
  207 + }
  208 + }
  209 + <#elseif po.classType =='sel_depart'>
  210 + component: 'JSelectDept',
  211 + <#elseif po.classType =='switch'>
  212 + component: 'JSwitch',
  213 + componentProps:{
  214 + <#if po.dictField != 'is_open'>
  215 + options:${po.dictField}
  216 + </#if>
  217 + }
  218 + <#elseif po.classType =='pca'>
  219 + component: 'JAreaLinkage',
  220 + <#elseif po.classType =='markdown'>
  221 + component: 'JMarkdownEditor',//注意string转换问题
  222 + <#elseif po.classType =='password'>
  223 + component: 'InputPassword',
  224 + <#elseif po.classType =='sel_user'>
  225 + component: 'JSelectUserByDept',
  226 + componentProps:{
  227 + labelKey:'realname',
  228 + }
  229 + <#elseif po.classType =='textarea'>
  230 + component: 'InputTextArea',//TODO 注意string转换问题
  231 + <#elseif po.classType=='list' || po.classType=='radio'>
  232 + component: 'JDictSelectTag',
  233 + componentProps:{
  234 + dictCode:"${form_field_dictCode}"
  235 + }
  236 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  237 + component: 'JMultiSelectTag',//TODO 暂无该组件
  238 + componentProps:{
  239 + dictCode:"${form_field_dictCode}"
  240 + }
  241 + <#elseif po.classType=='sel_search'>
  242 + component: 'JSearchSelect',
  243 + componentProps:{
  244 + dict:"${form_field_dictCode}"
  245 + }
  246 +<#elseif po.classType=='cat_tree'>
  247 + <#assign form_cat_tree = true>
  248 + component: 'JCategorySelect',
  249 + componentProps:{
  250 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  251 + }
  252 + <#if po.dictText?default("")?trim?length gt 1>
  253 + <#assign form_cat_back = "${po.dictText}">
  254 + </#if>
  255 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  256 + component: 'InputNumber',
  257 + <#elseif po.classType=='file'>
  258 + component: 'JUpload',
  259 + componentProps:{
  260 + <#if po.uploadnum??>
  261 + maxCount:${po.uploadnum}
  262 + </#if>
  263 + }
  264 + <#elseif po.classType=='image'>
  265 + component: 'JImageUpload',
  266 + componentProps:{
  267 + <#if po.uploadnum??>
  268 + fileMax:${po.uploadnum}
  269 + </#if>
  270 + }
  271 + <#elseif po.classType=='umeditor'>
  272 + component: 'JCodeEditor', //TODO String后缀暂未添加
  273 + <#elseif po.classType == 'sel_tree'>
  274 + component: 'JTreeSelect',
  275 + componentProps:{
  276 + <#if po.dictText??>
  277 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  278 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  279 + <#elseif po.dictText?split(',')[1]??>
  280 + pidField:"${po.dictText?split(',')[1]}",
  281 + <#elseif po.dictText?split(',')[3]??>
  282 + hasChildField:"${po.dictText?split(',')[3]}",
  283 + </#if>
  284 + </#if>
  285 + pidValue:"${po.dictField}",
  286 + }
  287 + <#else>
  288 + component: 'Input',
  289 + </#if>
  290 + <#include "/common/utils.ftl">
  291 + <#if po.isShow == 'Y' && poHasCheck(po)>
  292 + dynamicRules: ({model,schema}) => {
  293 + <#if po.fieldName != 'id'>
  294 + <#assign fieldValidType = po.fieldValidType!''>
  295 + return [
  296 + <#-- 非空校验 -->
  297 + <#if po.nullable == 'N' || fieldValidType == '*'>
  298 + { required: true, message: '请输入${po.filedComment}!'},
  299 + <#elseif fieldValidType!=''>
  300 + { required: false},
  301 + </#if>
  302 + <#-- 唯一校验 -->
  303 + <#if fieldValidType == 'only'>
  304 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  305 + <#-- 6到16位数字 -->
  306 + <#elseif fieldValidType == 'n6-16'>
  307 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  308 + <#-- 6到16位任意字符 -->
  309 + <#elseif fieldValidType == '*6-16'>
  310 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  311 + <#-- 6到18位字符串 -->
  312 + <#elseif fieldValidType == 's6-18'>
  313 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  314 + <#-- 网址 -->
  315 + <#elseif fieldValidType == 'url'>
  316 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  317 + <#-- 电子邮件 -->
  318 + <#elseif fieldValidType == 'e'>
  319 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  320 + <#-- 手机号码 -->
  321 + <#elseif fieldValidType == 'm'>
  322 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  323 + <#-- 邮政编码 -->
  324 + <#elseif fieldValidType == 'p'>
  325 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  326 + <#-- 字母 -->
  327 + <#elseif fieldValidType == 's'>
  328 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  329 + <#-- 数字 -->
  330 + <#elseif fieldValidType == 'n'>
  331 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  332 + <#-- 整数 -->
  333 + <#elseif fieldValidType == 'z'>
  334 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  335 + <#-- 金额 -->
  336 + <#elseif fieldValidType == 'money'>
  337 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  338 + <#-- 正则校验 -->
  339 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  340 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  341 + <#-- 无校验 -->
  342 + <#else>
  343 + <#t>
  344 + </#if>
  345 + ];
  346 + </#if>
  347 + },
  348 + </#if>
  349 + <#if po.readonly=='Y'>
  350 + dynamicDisabled:true
  351 + </#if>
  352 + },
  353 +</#if>
  354 +</#list>
  355 +];
  356 +//子表单数据
  357 +<#list subTables as sub>
  358 +<#if sub.foreignRelationType =='1'>
  359 +export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
  360 +<#assign form_cat_tree = false>
  361 +<#assign form_cat_back = "">
  362 +<#assign bpm_flag=false>
  363 +<#list sub.colums as po><#rt/>
  364 +<#if po.fieldDbName=='bpm_status'>
  365 + <#assign bpm_flag=true>
  366 +</#if>
  367 +<#if po.isShow =='Y'>
  368 +<#assign form_field_dictCode="">
  369 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  370 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  371 + <#elseif po.dictField?default("")?trim?length gt 1>
  372 + <#assign form_field_dictCode="${po.dictField}">
  373 + </#if>
  374 + {
  375 + label: '${po.filedComment}',
  376 + field: '${po.fieldName}',
  377 + <#if po.classType =='date'>
  378 + component: 'DatePicker',
  379 + <#elseif po.fieldType =='datetime'>
  380 + component: 'DatePicker',
  381 + componentProps: {
  382 + showTime:true
  383 + },
  384 + <#elseif po.fieldType =='time'>
  385 + component: 'TimePicker',
  386 + <#elseif po.classType =='popup'>
  387 + component: 'JPopup',
  388 + componentProps: ({ formActionType }) => {
  389 + const {setFieldsValue} = formActionType;
  390 + return{
  391 + setFieldsValue:setFieldsValue,
  392 + code:"${po.dictTable}",
  393 + fieldConfig:${po.dictField},
  394 + multi:${po.extendParams.popupMulti?c},
  395 + }
  396 + }
  397 + <#elseif po.classType =='sel_depart'>
  398 + component: 'JSelectDept',
  399 + <#elseif po.classType =='switch'>
  400 + component: 'JSwitch',
  401 + componentProps:{
  402 + <#if po.dictField != 'is_open'>
  403 + options:${po.dictField}
  404 + </#if>
  405 + }
  406 + <#elseif po.classType =='pca'>
  407 + component: 'JAreaLinkage',
  408 + <#elseif po.classType =='markdown'>
  409 + component: 'JMarkdownEditor',//注意string转换问题
  410 + <#elseif po.classType =='password'>
  411 + component: 'InputPassword',
  412 + <#elseif po.classType =='sel_user'>
  413 + component: 'JSelectUserByDept',
  414 + componentProps:{
  415 + labelKey:'realname',
  416 + }
  417 + <#elseif po.classType =='textarea'>
  418 + component: 'InputTextArea',//TODO 注意string转换问题
  419 + <#elseif po.classType=='list' || po.classType=='radio'>
  420 + component: 'JDictSelectTag',
  421 + componentProps:{
  422 + dictCode:"${form_field_dictCode}"
  423 + }
  424 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  425 + component: 'JMultiSelectTag',//TODO 暂无该组件
  426 + componentProps:{
  427 + dictCode:"${form_field_dictCode}"
  428 + }
  429 + <#elseif po.classType=='sel_search'>
  430 + component: 'JSearchSelect',
  431 + componentProps:{
  432 + dict:"${form_field_dictCode}"
  433 + }
  434 +<#elseif po.classType=='cat_tree'>
  435 + <#assign form_cat_tree = true>
  436 + component: 'JCategorySelect',
  437 + componentProps:{
  438 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  439 + }
  440 + <#if po.dictText?default("")?trim?length gt 1>
  441 + <#assign form_cat_back = "${po.dictText}">
  442 + </#if>
  443 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  444 + component: 'InputNumber',
  445 + <#elseif po.classType=='file'>
  446 + component: 'JUpload',
  447 + componentProps:{
  448 + <#if po.uploadnum??>
  449 + maxCount:${po.uploadnum}
  450 + </#if>
  451 + }
  452 + <#elseif po.classType=='image'>
  453 + component: 'JImageUpload',
  454 + componentProps:{
  455 + <#if po.uploadnum??>
  456 + fileMax:${po.uploadnum}
  457 + </#if>
  458 + }
  459 + <#elseif po.classType=='umeditor'>
  460 + component: 'JCodeEditor', //TODO String后缀暂未添加
  461 + <#elseif po.classType == 'sel_tree'>
  462 + component: 'JTreeSelect',
  463 + componentProps:{
  464 + <#if po.dictText??>
  465 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  466 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  467 + <#elseif po.dictText?split(',')[1]??>
  468 + pidField:"${po.dictText?split(',')[1]}",
  469 + <#elseif po.dictText?split(',')[3]??>
  470 + hasChildField:"${po.dictText?split(',')[3]}",
  471 + </#if>
  472 + </#if>
  473 + pidValue:"${po.dictField}",
  474 + }
  475 + <#else>
  476 + component: 'Input',
  477 + </#if>
  478 + <#include "/common/utils.ftl">
  479 + <#if po.isShow == 'Y' && poHasCheck(po)>
  480 + dynamicRules: ({model,schema}) => {
  481 + <#if po.fieldName != 'id'>
  482 + <#assign fieldValidType = po.fieldValidType!''>
  483 + return [
  484 + <#-- 非空校验 -->
  485 + <#if po.nullable == 'N' || fieldValidType == '*'>
  486 + { required: true, message: '请输入${po.filedComment}!'},
  487 + <#elseif fieldValidType!=''>
  488 + { required: false},
  489 + </#if>
  490 + <#-- 唯一校验 -->
  491 + <#if fieldValidType == 'only'>
  492 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  493 + <#-- 6到16位数字 -->
  494 + <#elseif fieldValidType == 'n6-16'>
  495 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  496 + <#-- 6到16位任意字符 -->
  497 + <#elseif fieldValidType == '*6-16'>
  498 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  499 + <#-- 6到18位字符串 -->
  500 + <#elseif fieldValidType == 's6-18'>
  501 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  502 + <#-- 网址 -->
  503 + <#elseif fieldValidType == 'url'>
  504 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  505 + <#-- 电子邮件 -->
  506 + <#elseif fieldValidType == 'e'>
  507 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  508 + <#-- 手机号码 -->
  509 + <#elseif fieldValidType == 'm'>
  510 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  511 + <#-- 邮政编码 -->
  512 + <#elseif fieldValidType == 'p'>
  513 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  514 + <#-- 字母 -->
  515 + <#elseif fieldValidType == 's'>
  516 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  517 + <#-- 数字 -->
  518 + <#elseif fieldValidType == 'n'>
  519 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  520 + <#-- 整数 -->
  521 + <#elseif fieldValidType == 'z'>
  522 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  523 + <#-- 金额 -->
  524 + <#elseif fieldValidType == 'money'>
  525 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  526 + <#-- 正则校验 -->
  527 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  528 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  529 + <#-- 无校验 -->
  530 + <#else>
  531 + <#t>
  532 + </#if>
  533 + ];
  534 + </#if>
  535 + },
  536 + </#if>
  537 + <#if po.readonly=='Y'>
  538 + dynamicDisabled:true
  539 + </#if>
  540 + },
  541 +</#if>
  542 +</#list>
  543 +];
  544 +</#if>
  545 +</#list>
  546 +//子表表格配置
  547 +<#list subTables as sub>
  548 +<#if sub.foreignRelationType =='0'>
  549 +export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
  550 +<#assign popupBackFields = "">
  551 +
  552 +<#-- 循环子表的列 开始 -->
  553 +<#list sub.colums as col><#rt/>
  554 +<#if col.isShow =='Y'>
  555 +<#if col.filedComment !='外键' >
  556 + {
  557 + title: '${col.filedComment}',
  558 + key: '${autoStringSuffixForModel(col)}',
  559 +<#if col.classType =='date'>
  560 + type: JVxeTypes.date,
  561 + <#if col.readonly=='Y'>
  562 + disabled:true,
  563 + </#if>
  564 +<#elseif col.classType =='datetime'>
  565 + type: JVxeTypes.datetime,
  566 + <#if col.readonly=='Y'>
  567 + disabled:true,
  568 + </#if>
  569 +<#elseif col.classType =='textarea'>
  570 + type: JVxeTypes.textarea,
  571 + <#if col.readonly=='Y'>
  572 + disabled:true,
  573 + </#if>
  574 +<#elseif "int,decimal,double,"?contains(col.classType)>
  575 + type: JVxeTypes.inputNumber,
  576 + <#if col.readonly=='Y'>
  577 + disabled:true,
  578 + </#if>
  579 +<#elseif col.classType =='list' || col.classType =='radio'>
  580 + type: JVxeTypes.select,
  581 + options:[],
  582 + <#if col.dictTable?default("")?trim?length gt 1>
  583 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  584 + <#else>
  585 + dictCode:"${col.dictField}",
  586 + </#if>
  587 + <#if col.readonly=='Y'>
  588 + disabled:true,
  589 + </#if>
  590 +<#elseif col.classType =='list_multi' || col.classType =='checkbox'>
  591 + type: JVxeTypes.selectMultiple,
  592 + options:[],
  593 + <#if col.dictTable?default("")?trim?length gt 1>
  594 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  595 + <#else>
  596 + dictCode:"${col.dictField}",
  597 + </#if>
  598 + <#if col.readonly=='Y'>
  599 + disabled:true,
  600 + </#if>
  601 +<#elseif col.classType =='sel_search'>
  602 + type: JVxeTypes.selectSearch,
  603 + <#if col.dictTable?default("")?trim?length gt 1>
  604 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  605 + <#else>
  606 + dictCode:"${col.dictField}",
  607 + </#if>
  608 + <#if col.readonly=='Y'>
  609 + disabled:true,
  610 + </#if>
  611 +<#elseif col.classType =='image'>
  612 + type: JVxeTypes.image,
  613 + token:true,
  614 + responseName:"message",
  615 + <#if col.readonly=='Y'>
  616 + disabled:true,
  617 + </#if>
  618 + <#if col.uploadnum??>
  619 + number: ${col.uploadnum},
  620 + </#if>
  621 +<#elseif col.classType =='file'>
  622 + type: JVxeTypes.file,
  623 + token:true,
  624 + responseName:"message",
  625 + <#if col.readonly=='Y'>
  626 + disabled:true,
  627 + </#if>
  628 + <#if col.uploadnum??>
  629 + number: ${col.uploadnum},
  630 + </#if>
  631 +<#elseif col.classType =='switch'>
  632 + type: JVxeTypes.checkbox,
  633 + <#if col.dictField == 'is_open'>
  634 + customValue: ['Y', 'N'],
  635 + <#else>
  636 + customValue: ${col.dictField},
  637 + </#if>
  638 + <#if col.readonly=='Y'>
  639 + disabled:true,
  640 + </#if>
  641 +<#elseif col.classType =='popup'>
  642 +<#if popupBackFields?length gt 0>
  643 + <#assign popupBackFields = "${popupBackFields}"+","+"${col.dictText}">
  644 +<#else>
  645 + <#assign popupBackFields = "${col.dictText}">
  646 +</#if>
  647 + type: JVxeTypes.popup,
  648 + popupCode:"${col.dictTable}",
  649 + field:"${col.dictField}",
  650 + orgFields:"${col.dictField}",
  651 + destFields:"${Format.underlineToHump(col.dictText)}",
  652 + <#if col.readonly=='Y'>
  653 + disabled:true,
  654 + </#if>
  655 +<#else>
  656 + type: JVxeTypes.input,
  657 + <#if col.readonly=='Y'>
  658 + disabled:true,
  659 + </#if>
  660 +</#if>
  661 +<#if col.classType =='list_multi' || col.classType =='checkbox'>
  662 + width:"250px",
  663 +<#else>
  664 + width:"200px",
  665 +</#if>
  666 +<#if col.classType =='file'>
  667 + placeholder: '请选择文件',
  668 +<#else>
  669 + placeholder: '请输入${'$'}{title}',
  670 +</#if>
  671 +<#if col.defaultVal??>
  672 +<#if col.fieldDbType=="BigDecimal" || col.fieldDbType=="double" || col.fieldDbType=="int">
  673 + defaultValue:${col.defaultVal},
  674 + <#else>
  675 + defaultValue:"${col.defaultVal}",
  676 +</#if>
  677 +<#else>
  678 + defaultValue:'',
  679 +</#if>
  680 +<#-- 子表的校验 -->
  681 +<#assign subFieldValidType = col.fieldValidType!''>
  682 +<#-- 非空校验 -->
  683 +<#if col.nullable == 'N' || subFieldValidType == '*'>
  684 + validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
  685 +<#-- 其他情况下,只要有值就被认为是正则校验 -->
  686 +<#elseif subFieldValidType?length gt 0>
  687 +<#assign subMessage = '格式不正确'>
  688 +<#if subFieldValidType == 'only' >
  689 + <#assign subMessage = '不能重复'>
  690 +</#if>
  691 + validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
  692 +</#if>
  693 + },
  694 +</#if>
  695 +</#if>
  696 +</#list>
  697 +<#-- 循环子表的列 结束 -->
  698 + ]
  699 +</#if>
  700 +</#list>
0 701 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <BasicForm @register="registerForm" ref="formRef"/>
  4 + <!-- 子表单区域 -->
  5 + <a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
  6 +<#list subTables as sub><#rt/>
  7 + <#if sub.foreignRelationType =='1'>
  8 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
  9 + <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
  10 + </a-tab-pane>
  11 +
  12 + <#else>
  13 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
  14 + <JVxeTable
  15 + keep-source
  16 + resizable
  17 + :ref="refKeys[${sub_index}]"
  18 + :loading="${sub.entityName?uncap_first}Table.loading"
  19 + :columns="${sub.entityName?uncap_first}Table.columns"
  20 + :dataSource="${sub.entityName?uncap_first}Table.dataSource"
  21 + :maxHeight="300"
  22 + :rowNumber="true"
  23 + :rowSelection="true"
  24 + :toolbar="true"
  25 + />
  26 + </a-tab-pane>
  27 + </#if>
  28 +</#list>
  29 + </a-tabs>
  30 + </BasicModal>
  31 +</template>
  32 +
  33 +<script lang="ts" setup>
  34 + import {ref, computed, unref,reactive} from 'vue';
  35 + import {BasicModal, useModalInner} from '/@/components/Modal';
  36 + import {BasicForm, useForm} from '/@/components/Form/index';
  37 + import { JVxeTable } from '/@/components/jeecg/JVxeTable'
  38 + import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'
  39 + <#list subTables as sub>
  40 + <#if sub.foreignRelationType =='1'>
  41 + import ${sub.entityName}Form from './${sub.entityName}Form.vue'
  42 + </#if>
  43 + </#list>
  44 + import {formSchema<#list subTables as sub><#if sub.foreignRelationType =='0'>,${sub.entityName?uncap_first}Columns</#if></#list>} from '../${entityName?uncap_first}.data';
  45 + import {saveOrUpdate<#list subTables as sub>,${sub.entityName?uncap_first}List</#list>} from '../${entityName?uncap_first}.api';
  46 + import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
  47 + // Emits声明
  48 + const emit = defineEmits(['register','success']);
  49 + const isUpdate = ref(true);
  50 + const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
  51 + <#assign hasOne2Many = false>
  52 + <#assign hasOne2One = false>
  53 + const activeKey = ref('${subTables[0].entityName?uncap_first}');
  54 +<#list subTables as sub>
  55 +<#if sub.foreignRelationType =='0'>
  56 + <#assign hasOne2Many = true>
  57 + const ${sub.entityName?uncap_first} = ref();
  58 +</#if>
  59 +<#if sub.foreignRelationType =='1'>
  60 + <#assign hasOne2One = true>
  61 + const ${sub.entityName?uncap_first}Form = ref();
  62 +</#if>
  63 +</#list>
  64 + const tableRefs = {<#list subTables as sub><#if sub.foreignRelationType =='0'>${sub.entityName?uncap_first}, <#assign hasOne2Many = true></#if></#list>};
  65 + <#list subTables as sub>
  66 + <#if sub.foreignRelationType =='0'>
  67 + const ${sub.entityName?uncap_first}Table = reactive({
  68 + loading: false,
  69 + dataSource: [],
  70 + columns:${sub.entityName?uncap_first}Columns
  71 + })
  72 + </#if>
  73 + </#list>
  74 + //表单配置
  75 + const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
  76 + labelWidth: 150,
  77 + schemas: formSchema,
  78 + showActionButtonGroup: false,
  79 + });
  80 + //表单赋值
  81 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  82 + //重置表单
  83 + await reset();
  84 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  85 + isUpdate.value = !!data?.isUpdate;
  86 + if (unref(isUpdate)) {
  87 + //表单赋值
  88 + await setFieldsValue({
  89 + ...data.record,
  90 + });
  91 + <#list subTables as sub><#rt/>
  92 + <#if sub.foreignRelationType =='1'>
  93 + ${sub.entityName?uncap_first}Form.value.initFormData(${sub.entityName?uncap_first}List,data?.record?.id)
  94 + </#if>
  95 + </#list>
  96 + <#list subTables as sub><#rt/>
  97 + <#if sub.foreignRelationType =='0'>
  98 + requestSubTableData(${sub.entityName?uncap_first}List, {id:data?.record?.id}, ${sub.entityName?uncap_first}Table)
  99 + </#if>
  100 + </#list>
  101 + }
  102 + // 隐藏底部时禁用整个表单
  103 + setProps({ disabled: !data?.showFooter })
  104 + });
  105 + //方法配置
  106 + const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys<#if hasOne2One==true>,validateSubForm</#if>);
  107 +
  108 + //设置标题
  109 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  110 +
  111 + async function reset(){
  112 + await resetFields();
  113 + activeKey.value = ref('${subTables[0].entityName?uncap_first}');
  114 + <#list subTables as sub>
  115 + <#if sub.foreignRelationType =='0'>
  116 + ${sub.entityName?uncap_first}Table.dataSource = [];
  117 + </#if>
  118 + <#if sub.foreignRelationType =='1'>
  119 + ${sub.entityName?uncap_first}Form.value.resetFields();
  120 + </#if>
  121 + </#list>
  122 + }
  123 + function classifyIntoFormData(allValues) {
  124 + let main = Object.assign({}, allValues.formValue)
  125 + return {
  126 + ...main, // 展开
  127 + <#assign subManyIndex = 0>
  128 + <#list subTables as sub><#rt/>
  129 + <#if sub.foreignRelationType =='0'>
  130 + ${sub.entityName?uncap_first}List: allValues.tablesValue[${subManyIndex}].tableData,
  131 + <#assign subManyIndex = subManyIndex+1>
  132 + <#else>
  133 + ${sub.entityName?uncap_first}List: ${sub.entityName?uncap_first}Form.value.getFormData(),
  134 + </#if>
  135 + </#list>
  136 + }
  137 + }
  138 + <#if hasOne2One==true>
  139 + //校验所有一对一子表表单
  140 + function validateSubForm(allValues){
  141 + return new Promise((resolve,reject)=>{
  142 + Promise.all([
  143 + <#list subTables as sub><#rt/>
  144 + <#if sub.foreignRelationType =='1'>
  145 + ${sub.entityName?uncap_first}Form.value.validateForm(${sub_index}),
  146 + </#if>
  147 + </#list>
  148 + ]).then(() => {
  149 + resolve(allValues)
  150 + }).catch(e => {
  151 + if (e.error === VALIDATE_FAILED) {
  152 + // 如果有未通过表单验证的子表,就自动跳转到它所在的tab
  153 + activeKey.value = e.index == null ? unref(activeKey) : refKeys.value[e.index]
  154 + } else {
  155 + console.error(e)
  156 + }
  157 + })
  158 + })
  159 + }
  160 + </#if>
  161 + //表单提交事件
  162 + async function requestAddOrEdit(values) {
  163 + try {
  164 + setModalProps({confirmLoading: true});
  165 + //提交表单
  166 + await saveOrUpdate(values, isUpdate.value);
  167 + //关闭弹窗
  168 + closeModal();
  169 + //刷新列表
  170 + emit('success');
  171 + } finally {
  172 + setModalProps({confirmLoading: false});
  173 + }
  174 + }
  175 +</script>
  176 +
  177 +<style lang="less" scoped>
  178 +
  179 +</style>
0 180 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Form.vuei 0 → 100644
  1 +<#list subTables as sub>
  2 +<#if sub.foreignRelationType=='1'>
  3 +#segment#${sub.entityName}Form.vue
  4 +<template>
  5 + <BasicForm @register="registerForm"/>
  6 +</template>
  7 +<script lang="ts">
  8 + import {defineComponent} from 'vue';
  9 + import {BasicForm, useForm} from '/@/components/Form/index';
  10 + import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
  11 + import {defHttp} from '/@/utils/http/axios';
  12 + import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
  13 +
  14 + export default defineComponent({
  15 + name:"${sub.entityName}Form",
  16 + components: {BasicForm},
  17 + emits:['register'],
  18 + setup(_,{emit}) {
  19 + const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
  20 + labelWidth: 150,
  21 + schemas: ${sub.entityName?uncap_first}FormSchema,
  22 + showActionButtonGroup: false,
  23 + });
  24 + /**
  25 + *初始化加载数据
  26 + */
  27 + function initFormData(url,id){
  28 + if(id){
  29 + defHttp.get({url,params:{id}},{isTransformResponse:false}).then(res=>{
  30 + res.success && setFieldsValue({...res.result[0]});
  31 + })
  32 + }
  33 + }
  34 + /**
  35 + *获取表单数据
  36 + */
  37 + function getFormData(){
  38 + return [getFieldsValue()];
  39 + }
  40 + /**
  41 + *表单校验
  42 + */
  43 + function validateForm(index){
  44 + return new Promise((resolve, reject) => {
  45 + // 验证子表表单
  46 + validate().then(()=>{
  47 + return resolve()
  48 + }).catch(()=> {
  49 + return reject({ error: VALIDATE_FAILED ,index})
  50 + })
  51 + })
  52 + }
  53 + return {
  54 + registerForm,
  55 + resetFields,
  56 + initFormData,
  57 + getFormData,
  58 + validateForm
  59 + }
  60 + }
  61 + })
  62 +</script>
  63 +</#if>
  64 +</#list>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -110,7 +110,7 @@ public class ${entityName}Controller {
110 110 */
111 111 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
112 112 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
113   - @PutMapping(value = "/edit")
  113 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
114 114 public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
115 115 ${entityName} ${entityName?uncap_first} = new ${entityName}();
116 116 BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/${entityName}Form.vuei
... ... @@ -8,7 +8,7 @@
8 8  
9 9 <a-tabs v-model="activeKey" @change="handleChangeTabs">
10 10 <!--主表区域 -->
11   - <a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true">
  11 + <a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true" :class="'jeecg-tabs-top'" :animated="false">
12 12 <a-form-model ref="form" :model="model" :rules="validatorRules">
13 13 <a-row>
14 14 <#list columns as po>
... ... @@ -19,13 +19,8 @@
19 19 <#elseif po.dictField?default("")?trim?length gt 1>
20 20 <#assign form_field_dictCode="${po.dictField}">
21 21 </#if>
22   - <#if po.classType =='textarea'>
23   - <a-col :span="24">
24   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
25   - <#else>
26 22 <a-col :xs="24" :sm="12">
27 23 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
28   - </#if>
29 24 <#if po.classType =='date'>
30 25 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
31 26 <#elseif po.classType =='datetime'>
... ... @@ -160,20 +155,12 @@ export default {
160 155 return {
161 156 labelCol: {
162 157 xs: { span: 24 },
163   - sm: { span: 6 },
  158 + sm: { span: 5 },
164 159 },
165 160 wrapperCol: {
166 161 xs: { span: 24 },
167 162 sm: { span: 16 },
168 163 },
169   - labelCol2: {
170   - xs: { span: 24 },
171   - sm: { span: 3 },
172   - },
173   - wrapperCol2: {
174   - xs: { span: 24 },
175   - sm: { span: 20 },
176   - },
177 164 // 新增时子表默认添加几行空数据
178 165 addDefaultRowNum: 1,
179 166 model:{
... ... @@ -516,4 +503,8 @@ export default {
516 503 </script>
517 504  
518 505 <style scoped>
  506 + /** tab panel 中有下拉框/日期 这类带下拉效果的,需要加此样式 */
  507 + /deep/ .jeecg-tabs-top {
  508 + overflow: visible;
  509 + }
519 510 </style>
520 511 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue/modules/[1-n]Form.vuei
... ... @@ -25,13 +25,8 @@
25 25 <#elseif po.dictField?default("")?trim?length gt 1>
26 26 <#assign form_field_dictCode="${po.dictField}">
27 27 </#if>
28   - <#if po.classType =='textarea'>
29   - <a-col :span="24">
30   - <a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
31   - <#else>
32 28 <a-col :span="${form_span}">
33 29 <a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
34   - </#if>
35 30 <#if po.classType =='date'>
36 31 <j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
37 32 <#elseif po.classType =='datetime'>
... ... @@ -118,20 +113,12 @@
118 113 },
119 114 labelCol: {
120 115 xs: { span: 24 },
121   - sm: { span: 6 },
  116 + sm: { span: 5 },
122 117 },
123 118 wrapperCol: {
124 119 xs: { span: 24 },
125 120 sm: { span: 16 },
126 121 },
127   - labelCol2: {
128   - xs: { span: 24 },
129   - sm: { span: 3 },
130   - },
131   - wrapperCol2: {
132   - xs: { span: 24 },
133   - sm: { span: 20 },
134   - },
135 122 <#include "/common/validatorRulesTemplate/sub.ftl">
136 123 confirmLoading: false,
137 124 }
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 +<#assign list_need_category=false>
  4 +<#assign list_need_pca=false>
  5 +<#assign bpm_flag=false>
  6 +
  7 +<#-- 开始循环 -->
  8 +<#list columns as po>
  9 +<#if po.fieldDbName=='bpm_status'>
  10 + <#assign bpm_flag=true>
  11 +</#if>
  12 +<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
  13 +<#assign list_need_category=true>
  14 +</#if>
  15 +<#if po.classType=='pca'>
  16 +<#assign list_need_pca=true>
  17 +</#if>
  18 +</#list>
  19 +<#-- 结束循环 -->
  20 + <!--引用表格-->
  21 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  22 + <!--插槽:table标题-->
  23 + <template #tableTitle>
  24 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  25 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  26 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  27 + <a-dropdown v-if="checkedKeys.length > 0">
  28 + <template #overlay>
  29 + <a-menu>
  30 + <a-menu-item key="1" @click="batchHandleDelete">
  31 + <Icon icon="ant-design:delete-outlined"></Icon>
  32 + 删除
  33 + </a-menu-item>
  34 + </a-menu>
  35 + </template>
  36 + <a-button>批量操作
  37 + <Icon icon="mdi:chevron-down"></Icon>
  38 + </a-button>
  39 + </a-dropdown>
  40 + </template>
  41 + <!--操作栏-->
  42 + <template #action="{ record }">
  43 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  44 + </template>
  45 + <!--字段回显插槽-->
  46 + <template #htmlSlot="{text}">
  47 + <div v-html="text"></div>
  48 + </template>
  49 + <template #fileSlot="{text}">
  50 + <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
  51 + <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
  52 + </template>
  53 + </BasicTable>
  54 + <!-- 表单区域 -->
  55 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  56 + </div>
  57 +</template>
  58 +
  59 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  60 + import {ref, computed, unref} from 'vue';
  61 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  62 + import { useListPage } from '/@/hooks/system/useListPage'
  63 + import {useModal} from '/@/components/Modal';
  64 + import ${entityName}Modal from './components/${entityName}Modal.vue'
  65 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  66 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  67 + <#if list_need_category>
  68 + import { loadCategoryData } from '/@/api/common/api'
  69 + import { getAuthCache, setAuthCache } from '/@/utils/auth';
  70 + import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
  71 + </#if>
  72 + const checkedKeys = ref<Array<string | number>>([]);
  73 + //注册model
  74 + const [registerModal, {openModal}] = useModal();
  75 + //注册table数据
  76 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  77 + tableProps:{
  78 + title: '${tableVo.ftlDescription}',
  79 + api: list,
  80 + columns,
  81 + canResize:false,
  82 + formConfig: {
  83 + labelWidth: 120,
  84 + schemas: searchFormSchema,
  85 + autoSubmitOnEnter:true,
  86 + showAdvancedButton:true,
  87 + fieldMapToTime: [
  88 + <#list columns as po>
  89 + <#if po.isQuery=='Y'>
  90 + <#if po.queryMode!='single'>
  91 + <#if po.classType=='date'>
  92 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
  93 + <#elseif po.classType=='datetime'>
  94 + ['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
  95 + </#if>
  96 + </#if>
  97 + </#if>
  98 + </#list>
  99 + ],
  100 + },
  101 + actionColumn: {
  102 + width: 120,
  103 + },
  104 + },
  105 + exportConfig: {
  106 + name:"${tableVo.ftlDescription}",
  107 + url: getExportUrl,
  108 + },
  109 + importConfig: {
  110 + url: getImportUrl
  111 + },
  112 + })
  113 +
  114 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  115 +
  116 + /**
  117 + * 新增事件
  118 + */
  119 + function handleAdd() {
  120 + openModal(true, {
  121 + isUpdate: false,
  122 + showFooter: true,
  123 + });
  124 + }
  125 + /**
  126 + * 编辑事件
  127 + */
  128 + function handleEdit(record: Recordable) {
  129 + openModal(true, {
  130 + record,
  131 + isUpdate: true,
  132 + showFooter: true,
  133 + });
  134 + }
  135 + /**
  136 + * 详情
  137 + */
  138 + function handleDetail(record: Recordable) {
  139 + openModal(true, {
  140 + record,
  141 + isUpdate: true,
  142 + showFooter: false,
  143 + });
  144 + }
  145 + /**
  146 + * 删除事件
  147 + */
  148 + async function handleDelete(record) {
  149 + await deleteOne({id: record.id}, reload);
  150 + }
  151 + /**
  152 + * 批量删除事件
  153 + */
  154 + async function batchHandleDelete() {
  155 + await batchDelete({ids: checkedKeys.value}, reload);
  156 + }
  157 + /**
  158 + * 成功回调
  159 + */
  160 + function handleSuccess() {
  161 + reload();
  162 + }
  163 + /**
  164 + * 操作栏
  165 + */
  166 + function getTableAction(record){
  167 + return [
  168 + {
  169 + label: '编辑',
  170 + onClick: handleEdit.bind(null, record),
  171 + }
  172 + ]
  173 + }
  174 + /**
  175 + * 下拉操作栏
  176 + */
  177 + function getDropDownAction(record){
  178 + return [
  179 + {
  180 + label: '详情',
  181 + onClick: handleDetail.bind(null, record),
  182 + }, {
  183 + label: '删除',
  184 + popConfirm: {
  185 + title: '是否确认删除',
  186 + confirm: handleDelete.bind(null, record),
  187 + }
  188 + }
  189 + ]
  190 + }
  191 + <#if list_need_category>
  192 + /**
  193 + * 初始化字典配置
  194 + */
  195 + function initDictConfig(){
  196 + <#list columns as po>
  197 + <#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
  198 + <#if po.classType=='cat_tree' && list_need_category==true>
  199 + loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
  200 + if (res) {
  201 + let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
  202 + if(!allDictDate['${po.dictField?default("")}']){
  203 + Object.assign(allDictDate,{'${po.dictField?default("")}':res})
  204 + }
  205 + setAuthCache(DB_DICT_DATA_KEY,allDictDate)
  206 + }
  207 + })
  208 + </#if>
  209 + </#if>
  210 + </#list>
  211 + }
  212 + initDictConfig();
  213 + </#if>
  214 +</script>
  215 +
  216 +<style scoped>
  217 +
  218 +</style>
0 219 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +<#list subTables as sub><#rt/>
  13 + ${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
  14 +</#list>
  15 +}
  16 +/**
  17 + * 导出api
  18 + * @param params
  19 + */
  20 +export const getExportUrl = Api.exportXls;
  21 +
  22 +/**
  23 + * 导入api
  24 + */
  25 +export const getImportUrl = Api.importExcel;
  26 +<#list subTables as sub><#rt/>
  27 +/**
  28 + * 查询子表数据
  29 + * @param params
  30 + */
  31 +export const ${sub.entityName?uncap_first}List = Api.${sub.entityName?uncap_first}List;
  32 +</#list>
  33 +/**
  34 + * 列表接口
  35 + * @param params
  36 + */
  37 +export const list = (params) =>
  38 + defHttp.get({url: Api.list, params});
  39 +
  40 +/**
  41 + * 删除单个
  42 + */
  43 +export const deleteOne = (params,handleSuccess) => {
  44 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  45 + handleSuccess();
  46 + });
  47 +}
  48 +/**
  49 + * 批量删除
  50 + * @param params
  51 + */
  52 +export const batchDelete = (params, handleSuccess) => {
  53 + Modal.confirm({
  54 + title: '确认删除',
  55 + content: '是否删除选中数据',
  56 + okText: '确认',
  57 + cancelText: '取消',
  58 + onOk: () => {
  59 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  60 + handleSuccess();
  61 + });
  62 + }
  63 + });
  64 +}
  65 +/**
  66 + * 保存或者更新
  67 + * @param params
  68 + */
  69 +export const saveOrUpdate = (params, isUpdate) => {
  70 + let url = isUpdate ? Api.edit : Api.save;
  71 + return defHttp.post({url: url, params});
  72 +}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
  6 +//列表数据
  7 +export const columns: BasicColumn[] = [
  8 + <#list columns as po>
  9 + <#if po.isShowList =='Y' && po.fieldName !='id'>
  10 + {
  11 + title: '${po.filedComment}',
  12 + align:"center",
  13 + <#if po.sort=='Y'>
  14 + sorter: true,
  15 + </#if>
  16 + <#if po.classType=='date'>
  17 + dataIndex: '${po.fieldName}',
  18 + customRender:({text}) =>{
  19 + return !text?"":(text.length>10?text.substr(0,10):text)
  20 + },
  21 + <#elseif po.fieldDbType=='Blob'>
  22 + dataIndex: '${po.fieldName}String'
  23 + <#elseif po.classType=='umeditor'>
  24 + dataIndex: '${po.fieldName}',
  25 + slots: { customRender: 'htmlSlot' },
  26 + <#elseif po.classType=='pca'>
  27 + dataIndex: '${po.fieldName}',
  28 + slots: { customRender: 'pcaSlot' },//TODO 未翻译
  29 + <#elseif po.classType=='file'>
  30 + dataIndex: '${po.fieldName}',
  31 + slots: { customRender: 'fileSlot' },
  32 + <#elseif po.classType=='image'>
  33 + dataIndex: '${po.fieldName}',
  34 + customRender:render.renderAvatar,
  35 + <#elseif po.classType=='switch'>
  36 + dataIndex: '${po.fieldName}',
  37 +<#assign switch_extend_arr=['Y','N']>
  38 +<#if po.dictField?default("")?contains("[")>
  39 +<#assign switch_extend_arr=po.dictField?eval>
  40 +</#if>
  41 +<#list switch_extend_arr as a>
  42 +<#if a_index == 0>
  43 +<#assign switch_extend_arr1=a>
  44 +<#else>
  45 +<#assign switch_extend_arr2=a>
  46 +</#if>
  47 +</#list>
  48 + customRender:({text}) => {
  49 + return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
  50 + },
  51 + <#elseif po.classType == 'sel_tree' || po.classType=='list' || po.classType=='list_multi' || po.classType=='sel_search' || po.classType=='radio' || po.classType=='checkbox' || po.classType=='sel_depart' || po.classType=='sel_user'>
  52 + dataIndex: '${po.fieldName}_dictText'
  53 + <#elseif po.classType=='cat_tree'>
  54 + dataIndex: '${po.fieldName}',
  55 + <#if po.dictText?default("")?trim?length == 0>
  56 + customRender:({text}) => {
  57 + return render.renderCategoryTree(text,'${po.dictField?default("")}')
  58 + },
  59 + <#else>
  60 + customRender: (text, record) => (text ? record['${po.dictText}'] : '')
  61 + </#if>
  62 + <#else>
  63 + dataIndex: '${po.fieldName}'
  64 + </#if>
  65 + },
  66 + </#if>
  67 + </#list>
  68 +];
  69 +//查询数据
  70 +export const searchFormSchema: FormSchema[] = [
  71 +<#-- 开始循环 -->
  72 +<#list columns as po>
  73 +<#if po.fieldDbName=='bpm_status'>
  74 + <#assign bpm_flag=true>
  75 +</#if>
  76 +<#if po.isQuery=='Y'>
  77 +<#assign query_flag=true>
  78 + <#assign query_field_dictCode="">
  79 + <#if po.dictTable?default("")?trim?length gt 1>
  80 + <#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  81 + <#elseif po.dictField?default("")?trim?length gt 1>
  82 + <#assign query_field_dictCode="${po.dictField}">
  83 + </#if>
  84 +<#if po.queryMode=='single'>
  85 + {
  86 + label: "${po.filedComment}",
  87 + field: "${po.fieldName}",
  88 +<#if po.classType=='sel_search'>
  89 + component: 'JSearchSelect',
  90 + componentProps:{
  91 + dict:"${po.dictTable},${po.dictText},${po.dictField}"
  92 + },
  93 +<#elseif po.classType=='sel_user'>
  94 + component: 'JSelectUserByDept',
  95 +<#elseif po.classType=='switch'>
  96 + component: 'JSwitch',
  97 + componentProps:{
  98 + <#if po.dictField != 'is_open'>
  99 + options:"${po.dictField}"
  100 + </#if>
  101 + },
  102 + <#elseif po.classType=='sel_depart'>
  103 + component: 'JSelectDept',
  104 + <#elseif po.classType=='list_multi'>
  105 + component: 'JMultiSelectTag',//暂无该组件
  106 + componentProps:{
  107 + dictCode:"query_field_dictCode?default("")"
  108 + },
  109 + <#elseif po.classType=='cat_tree'>
  110 + component: 'JCategorySelect',
  111 + componentProps:{
  112 + pcode:"${po.dictField?default("")}",//back和事件未添加,暂时有问题
  113 + },
  114 +<#elseif po.classType=='date'>
  115 + component: 'DatePicker',
  116 +<#elseif po.classType=='datetime'>
  117 + component: 'DatePicker',
  118 + componentProps: {
  119 + showTime:true
  120 + },
  121 +<#elseif po.classType=='pca'>
  122 + component: 'JAreaLinkage',
  123 +<#elseif po.classType=='popup'>
  124 + component: 'JPopup',
  125 + componentProps: ({ formActionType }) => {
  126 + const {setFieldsValue} = formActionType;
  127 + return{
  128 + setFieldsValue:setFieldsValue,
  129 + code:"${po.dictTable}",
  130 + fieldConfig:"${po.dictField}",
  131 + multi:${po.extendParams.popupMulti?c},
  132 + }
  133 + },
  134 +<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
  135 +<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
  136 + component: 'JDictSelectTag',
  137 + componentProps:{
  138 + <#if po.dictTable?default("")?trim?length gt 1>
  139 + dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
  140 + <#elseif po.dictField?default("")?trim?length gt 1>
  141 + dictCode:"${po.dictField}"
  142 + </#if>
  143 + },
  144 +<#else>
  145 + component: 'Input',
  146 +</#if>
  147 + colProps: {span: 6},
  148 + },
  149 +<#else>
  150 + {
  151 + label: "${po.filedComment}",
  152 + field: "${po.fieldName}",
  153 +<#if po.classType=='date'>
  154 + component: 'RangePicker',
  155 +<#elseif po.classType=='datetime'>
  156 + component: 'RangePicker',
  157 + componentProps: {
  158 + showTime:true
  159 + },
  160 +<#else>
  161 + component: 'Input', //TODO 范围查询
  162 +</#if>
  163 + colProps: {span: 6},
  164 + },
  165 +</#if>
  166 +</#if>
  167 +</#list>
  168 +<#-- 结束循环 -->
  169 +];
  170 +//表单数据
  171 +export const formSchema: FormSchema[] = [
  172 +<#assign form_cat_tree = false>
  173 +<#assign form_cat_back = "">
  174 +<#assign bpm_flag=false>
  175 +<#list columns as po><#rt/>
  176 +<#if po.fieldDbName=='bpm_status'>
  177 + <#assign bpm_flag=true>
  178 +</#if>
  179 +<#if po.isShow =='Y'>
  180 +<#assign form_field_dictCode="">
  181 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  182 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  183 + <#elseif po.dictField?default("")?trim?length gt 1>
  184 + <#assign form_field_dictCode="${po.dictField}">
  185 + </#if>
  186 + {
  187 + label: '${po.filedComment}',
  188 + field: '${po.fieldName}',
  189 + <#if po.classType =='date'>
  190 + component: 'DatePicker',
  191 + <#elseif po.fieldType =='datetime'>
  192 + component: 'DatePicker',
  193 + componentProps: {
  194 + showTime:true
  195 + },
  196 + <#elseif po.fieldType =='time'>
  197 + component: 'TimePicker',
  198 + <#elseif po.classType =='popup'>
  199 + component: 'JPopup',
  200 + componentProps: ({ formActionType }) => {
  201 + const {setFieldsValue} = formActionType;
  202 + return{
  203 + setFieldsValue:setFieldsValue,
  204 + code:"${po.dictTable}",
  205 + fieldConfig:${po.dictField},
  206 + multi:${po.extendParams.popupMulti?c},
  207 + }
  208 + }
  209 + <#elseif po.classType =='sel_depart'>
  210 + component: 'JSelectDept',
  211 + <#elseif po.classType =='switch'>
  212 + component: 'JSwitch',
  213 + componentProps:{
  214 + <#if po.dictField != 'is_open'>
  215 + options:${po.dictField}
  216 + </#if>
  217 + }
  218 + <#elseif po.classType =='pca'>
  219 + component: 'JAreaLinkage',
  220 + <#elseif po.classType =='markdown'>
  221 + component: 'JMarkdownEditor',//注意string转换问题
  222 + <#elseif po.classType =='password'>
  223 + component: 'InputPassword',
  224 + <#elseif po.classType =='sel_user'>
  225 + component: 'JSelectUserByDept',
  226 + componentProps:{
  227 + labelKey:'realname',
  228 + }
  229 + <#elseif po.classType =='textarea'>
  230 + component: 'InputTextArea',//TODO 注意string转换问题
  231 + <#elseif po.classType=='list' || po.classType=='radio'>
  232 + component: 'JDictSelectTag',
  233 + componentProps:{
  234 + dictCode:"${form_field_dictCode}"
  235 + }
  236 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  237 + component: 'JMultiSelectTag',//TODO 暂无该组件
  238 + componentProps:{
  239 + dictCode:"${form_field_dictCode}"
  240 + }
  241 + <#elseif po.classType=='sel_search'>
  242 + component: 'JSearchSelect',
  243 + componentProps:{
  244 + dict:"${form_field_dictCode}"
  245 + }
  246 +<#elseif po.classType=='cat_tree'>
  247 + <#assign form_cat_tree = true>
  248 + component: 'JCategorySelect',
  249 + componentProps:{
  250 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  251 + }
  252 + <#if po.dictText?default("")?trim?length gt 1>
  253 + <#assign form_cat_back = "${po.dictText}">
  254 + </#if>
  255 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  256 + component: 'InputNumber',
  257 + <#elseif po.classType=='file'>
  258 + component: 'JUpload',
  259 + componentProps:{
  260 + <#if po.uploadnum??>
  261 + maxCount:${po.uploadnum}
  262 + </#if>
  263 + }
  264 + <#elseif po.classType=='image'>
  265 + component: 'JImageUpload',
  266 + componentProps:{
  267 + <#if po.uploadnum??>
  268 + fileMax:${po.uploadnum}
  269 + </#if>
  270 + }
  271 + <#elseif po.classType=='umeditor'>
  272 + component: 'JCodeEditor', //TODO String后缀暂未添加
  273 + <#elseif po.classType == 'sel_tree'>
  274 + component: 'JTreeSelect',
  275 + componentProps:{
  276 + <#if po.dictText??>
  277 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  278 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  279 + <#elseif po.dictText?split(',')[1]??>
  280 + pidField:"${po.dictText?split(',')[1]}",
  281 + <#elseif po.dictText?split(',')[3]??>
  282 + hasChildField:"${po.dictText?split(',')[3]}",
  283 + </#if>
  284 + </#if>
  285 + pidValue:"${po.dictField}",
  286 + }
  287 + <#else>
  288 + component: 'Input',
  289 + </#if>
  290 + <#include "/common/utils.ftl">
  291 + <#if po.isShow == 'Y' && poHasCheck(po)>
  292 + dynamicRules: ({model,schema}) => {
  293 + <#if po.fieldName != 'id'>
  294 + <#assign fieldValidType = po.fieldValidType!''>
  295 + return [
  296 + <#-- 非空校验 -->
  297 + <#if po.nullable == 'N' || fieldValidType == '*'>
  298 + { required: true, message: '请输入${po.filedComment}!'},
  299 + <#elseif fieldValidType!=''>
  300 + { required: false},
  301 + </#if>
  302 + <#-- 唯一校验 -->
  303 + <#if fieldValidType == 'only'>
  304 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  305 + <#-- 6到16位数字 -->
  306 + <#elseif fieldValidType == 'n6-16'>
  307 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  308 + <#-- 6到16位任意字符 -->
  309 + <#elseif fieldValidType == '*6-16'>
  310 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  311 + <#-- 6到18位字符串 -->
  312 + <#elseif fieldValidType == 's6-18'>
  313 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  314 + <#-- 网址 -->
  315 + <#elseif fieldValidType == 'url'>
  316 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  317 + <#-- 电子邮件 -->
  318 + <#elseif fieldValidType == 'e'>
  319 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  320 + <#-- 手机号码 -->
  321 + <#elseif fieldValidType == 'm'>
  322 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  323 + <#-- 邮政编码 -->
  324 + <#elseif fieldValidType == 'p'>
  325 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  326 + <#-- 字母 -->
  327 + <#elseif fieldValidType == 's'>
  328 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  329 + <#-- 数字 -->
  330 + <#elseif fieldValidType == 'n'>
  331 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  332 + <#-- 整数 -->
  333 + <#elseif fieldValidType == 'z'>
  334 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  335 + <#-- 金额 -->
  336 + <#elseif fieldValidType == 'money'>
  337 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  338 + <#-- 正则校验 -->
  339 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  340 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  341 + <#-- 无校验 -->
  342 + <#else>
  343 + <#t>
  344 + </#if>
  345 + ];
  346 + </#if>
  347 + },
  348 + </#if>
  349 + <#if po.readonly=='Y'>
  350 + dynamicDisabled:true
  351 + </#if>
  352 + },
  353 +</#if>
  354 +</#list>
  355 +];
  356 +//子表单数据
  357 +<#list subTables as sub>
  358 +<#if sub.foreignRelationType =='1'>
  359 +export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
  360 +<#assign form_cat_tree = false>
  361 +<#assign form_cat_back = "">
  362 +<#assign bpm_flag=false>
  363 +<#list sub.colums as po><#rt/>
  364 +<#if po.fieldDbName=='bpm_status'>
  365 + <#assign bpm_flag=true>
  366 +</#if>
  367 +<#if po.isShow =='Y'>
  368 +<#assign form_field_dictCode="">
  369 + <#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
  370 + <#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
  371 + <#elseif po.dictField?default("")?trim?length gt 1>
  372 + <#assign form_field_dictCode="${po.dictField}">
  373 + </#if>
  374 + {
  375 + label: '${po.filedComment}',
  376 + field: '${po.fieldName}',
  377 + <#if po.classType =='date'>
  378 + component: 'DatePicker',
  379 + <#elseif po.fieldType =='datetime'>
  380 + component: 'DatePicker',
  381 + componentProps: {
  382 + showTime:true
  383 + },
  384 + <#elseif po.fieldType =='time'>
  385 + component: 'TimePicker',
  386 + <#elseif po.classType =='popup'>
  387 + component: 'JPopup',
  388 + componentProps: ({ formActionType }) => {
  389 + const {setFieldsValue} = formActionType;
  390 + return{
  391 + setFieldsValue:setFieldsValue,
  392 + code:"${po.dictTable}",
  393 + fieldConfig:${po.dictField},
  394 + multi:${po.extendParams.popupMulti?c},
  395 + }
  396 + }
  397 + <#elseif po.classType =='sel_depart'>
  398 + component: 'JSelectDept',
  399 + <#elseif po.classType =='switch'>
  400 + component: 'JSwitch',
  401 + componentProps:{
  402 + <#if po.dictField != 'is_open'>
  403 + options:${po.dictField}
  404 + </#if>
  405 + }
  406 + <#elseif po.classType =='pca'>
  407 + component: 'JAreaLinkage',
  408 + <#elseif po.classType =='markdown'>
  409 + component: 'JMarkdownEditor',//注意string转换问题
  410 + <#elseif po.classType =='password'>
  411 + component: 'InputPassword',
  412 + <#elseif po.classType =='sel_user'>
  413 + component: 'JSelectUserByDept',
  414 + componentProps:{
  415 + labelKey:'realname',
  416 + }
  417 + <#elseif po.classType =='textarea'>
  418 + component: 'InputTextArea',//TODO 注意string转换问题
  419 + <#elseif po.classType=='list' || po.classType=='radio'>
  420 + component: 'JDictSelectTag',
  421 + componentProps:{
  422 + dictCode:"${form_field_dictCode}"
  423 + }
  424 + <#elseif po.classType=='list_multi' || po.classType=='checkbox'>
  425 + component: 'JMultiSelectTag',//TODO 暂无该组件
  426 + componentProps:{
  427 + dictCode:"${form_field_dictCode}"
  428 + }
  429 + <#elseif po.classType=='sel_search'>
  430 + component: 'JSearchSelect',
  431 + componentProps:{
  432 + dict:"${form_field_dictCode}"
  433 + }
  434 +<#elseif po.classType=='cat_tree'>
  435 + <#assign form_cat_tree = true>
  436 + component: 'JCategorySelect',
  437 + componentProps:{
  438 + pcode:"${po.dictField?default("")}", //TODO back和事件未添加,暂时有问题
  439 + }
  440 + <#if po.dictText?default("")?trim?length gt 1>
  441 + <#assign form_cat_back = "${po.dictText}">
  442 + </#if>
  443 + <#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
  444 + component: 'InputNumber',
  445 + <#elseif po.classType=='file'>
  446 + component: 'JUpload',
  447 + componentProps:{
  448 + <#if po.uploadnum??>
  449 + maxCount:${po.uploadnum}
  450 + </#if>
  451 + }
  452 + <#elseif po.classType=='image'>
  453 + component: 'JImageUpload',
  454 + componentProps:{
  455 + <#if po.uploadnum??>
  456 + fileMax:${po.uploadnum}
  457 + </#if>
  458 + }
  459 + <#elseif po.classType=='umeditor'>
  460 + component: 'JCodeEditor', //TODO String后缀暂未添加
  461 + <#elseif po.classType == 'sel_tree'>
  462 + component: 'JTreeSelect',
  463 + componentProps:{
  464 + <#if po.dictText??>
  465 + <#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
  466 + dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
  467 + <#elseif po.dictText?split(',')[1]??>
  468 + pidField:"${po.dictText?split(',')[1]}",
  469 + <#elseif po.dictText?split(',')[3]??>
  470 + hasChildField:"${po.dictText?split(',')[3]}",
  471 + </#if>
  472 + </#if>
  473 + pidValue:"${po.dictField}",
  474 + }
  475 + <#else>
  476 + component: 'Input',
  477 + </#if>
  478 + <#include "/common/utils.ftl">
  479 + <#if po.isShow == 'Y' && poHasCheck(po)>
  480 + dynamicRules: ({model,schema}) => {
  481 + <#if po.fieldName != 'id'>
  482 + <#assign fieldValidType = po.fieldValidType!''>
  483 + return [
  484 + <#-- 非空校验 -->
  485 + <#if po.nullable == 'N' || fieldValidType == '*'>
  486 + { required: true, message: '请输入${po.filedComment}!'},
  487 + <#elseif fieldValidType!=''>
  488 + { required: false},
  489 + </#if>
  490 + <#-- 唯一校验 -->
  491 + <#if fieldValidType == 'only'>
  492 + {...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
  493 + <#-- 6到16位数字 -->
  494 + <#elseif fieldValidType == 'n6-16'>
  495 + { pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
  496 + <#-- 6到16位任意字符 -->
  497 + <#elseif fieldValidType == '*6-16'>
  498 + { pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
  499 + <#-- 6到18位字符串 -->
  500 + <#elseif fieldValidType == 's6-18'>
  501 + { pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
  502 + <#-- 网址 -->
  503 + <#elseif fieldValidType == 'url'>
  504 + { pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
  505 + <#-- 电子邮件 -->
  506 + <#elseif fieldValidType == 'e'>
  507 + { pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
  508 + <#-- 手机号码 -->
  509 + <#elseif fieldValidType == 'm'>
  510 + { pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
  511 + <#-- 邮政编码 -->
  512 + <#elseif fieldValidType == 'p'>
  513 + { pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
  514 + <#-- 字母 -->
  515 + <#elseif fieldValidType == 's'>
  516 + { pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
  517 + <#-- 数字 -->
  518 + <#elseif fieldValidType == 'n'>
  519 + { pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
  520 + <#-- 整数 -->
  521 + <#elseif fieldValidType == 'z'>
  522 + { pattern: /^-?\d+$/, message: '请输入整数!'},
  523 + <#-- 金额 -->
  524 + <#elseif fieldValidType == 'money'>
  525 + { pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
  526 + <#-- 正则校验 -->
  527 + <#elseif fieldValidType != '' && fieldValidType != '*'>
  528 + { pattern: '${fieldValidType}', message: '不符合校验规则!'},
  529 + <#-- 无校验 -->
  530 + <#else>
  531 + <#t>
  532 + </#if>
  533 + ];
  534 + </#if>
  535 + },
  536 + </#if>
  537 + <#if po.readonly=='Y'>
  538 + dynamicDisabled:true
  539 + </#if>
  540 + },
  541 +</#if>
  542 +</#list>
  543 +];
  544 +</#if>
  545 +</#list>
  546 +//子表表格配置
  547 +<#list subTables as sub>
  548 +<#if sub.foreignRelationType =='0'>
  549 +export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
  550 +<#assign popupBackFields = "">
  551 +
  552 +<#-- 循环子表的列 开始 -->
  553 +<#list sub.colums as col><#rt/>
  554 +<#if col.isShow =='Y'>
  555 +<#if col.filedComment !='外键' >
  556 + {
  557 + title: '${col.filedComment}',
  558 + key: '${autoStringSuffixForModel(col)}',
  559 +<#if col.classType =='date'>
  560 + type: JVxeTypes.date,
  561 + <#if col.readonly=='Y'>
  562 + disabled:true,
  563 + </#if>
  564 +<#elseif col.classType =='datetime'>
  565 + type: JVxeTypes.datetime,
  566 + <#if col.readonly=='Y'>
  567 + disabled:true,
  568 + </#if>
  569 +<#elseif col.classType =='textarea'>
  570 + type: JVxeTypes.textarea,
  571 + <#if col.readonly=='Y'>
  572 + disabled:true,
  573 + </#if>
  574 +<#elseif "int,decimal,double,"?contains(col.classType)>
  575 + type: JVxeTypes.inputNumber,
  576 + <#if col.readonly=='Y'>
  577 + disabled:true,
  578 + </#if>
  579 +<#elseif col.classType =='list' || col.classType =='radio'>
  580 + type: JVxeTypes.select,
  581 + options:[],
  582 + <#if col.dictTable?default("")?trim?length gt 1>
  583 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  584 + <#else>
  585 + dictCode:"${col.dictField}",
  586 + </#if>
  587 + <#if col.readonly=='Y'>
  588 + disabled:true,
  589 + </#if>
  590 +<#elseif col.classType =='list_multi' || col.classType =='checkbox'>
  591 + type: JVxeTypes.selectMultiple,
  592 + options:[],
  593 + <#if col.dictTable?default("")?trim?length gt 1>
  594 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  595 + <#else>
  596 + dictCode:"${col.dictField}",
  597 + </#if>
  598 + <#if col.readonly=='Y'>
  599 + disabled:true,
  600 + </#if>
  601 +<#elseif col.classType =='sel_search'>
  602 + type: JVxeTypes.selectSearch,
  603 + <#if col.dictTable?default("")?trim?length gt 1>
  604 + dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
  605 + <#else>
  606 + dictCode:"${col.dictField}",
  607 + </#if>
  608 + <#if col.readonly=='Y'>
  609 + disabled:true,
  610 + </#if>
  611 +<#elseif col.classType =='image'>
  612 + type: JVxeTypes.image,
  613 + token:true,
  614 + responseName:"message",
  615 + <#if col.readonly=='Y'>
  616 + disabled:true,
  617 + </#if>
  618 + <#if col.uploadnum??>
  619 + number: ${col.uploadnum},
  620 + </#if>
  621 +<#elseif col.classType =='file'>
  622 + type: JVxeTypes.file,
  623 + token:true,
  624 + responseName:"message",
  625 + <#if col.readonly=='Y'>
  626 + disabled:true,
  627 + </#if>
  628 + <#if col.uploadnum??>
  629 + number: ${col.uploadnum},
  630 + </#if>
  631 +<#elseif col.classType =='switch'>
  632 + type: JVxeTypes.checkbox,
  633 + <#if col.dictField == 'is_open'>
  634 + customValue: ['Y', 'N'],
  635 + <#else>
  636 + customValue: ${col.dictField},
  637 + </#if>
  638 + <#if col.readonly=='Y'>
  639 + disabled:true,
  640 + </#if>
  641 +<#elseif col.classType =='popup'>
  642 +<#if popupBackFields?length gt 0>
  643 + <#assign popupBackFields = "${popupBackFields}"+","+"${col.dictText}">
  644 +<#else>
  645 + <#assign popupBackFields = "${col.dictText}">
  646 +</#if>
  647 + type: JVxeTypes.popup,
  648 + popupCode:"${col.dictTable}",
  649 + field:"${col.dictField}",
  650 + orgFields:"${col.dictField}",
  651 + destFields:"${Format.underlineToHump(col.dictText)}",
  652 + <#if col.readonly=='Y'>
  653 + disabled:true,
  654 + </#if>
  655 +<#else>
  656 + type: JVxeTypes.input,
  657 + <#if col.readonly=='Y'>
  658 + disabled:true,
  659 + </#if>
  660 +</#if>
  661 +<#if col.classType =='list_multi' || col.classType =='checkbox'>
  662 + width:"250px",
  663 +<#else>
  664 + width:"200px",
  665 +</#if>
  666 +<#if col.classType =='file'>
  667 + placeholder: '请选择文件',
  668 +<#else>
  669 + placeholder: '请输入${'$'}{title}',
  670 +</#if>
  671 +<#if col.defaultVal??>
  672 +<#if col.fieldDbType=="BigDecimal" || col.fieldDbType=="double" || col.fieldDbType=="int">
  673 + defaultValue:${col.defaultVal},
  674 + <#else>
  675 + defaultValue:"${col.defaultVal}",
  676 +</#if>
  677 +<#else>
  678 + defaultValue:'',
  679 +</#if>
  680 +<#-- 子表的校验 -->
  681 +<#assign subFieldValidType = col.fieldValidType!''>
  682 +<#-- 非空校验 -->
  683 +<#if col.nullable == 'N' || subFieldValidType == '*'>
  684 + validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
  685 +<#-- 其他情况下,只要有值就被认为是正则校验 -->
  686 +<#elseif subFieldValidType?length gt 0>
  687 +<#assign subMessage = '格式不正确'>
  688 +<#if subFieldValidType == 'only' >
  689 + <#assign subMessage = '不能重复'>
  690 +</#if>
  691 + validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
  692 +</#if>
  693 + },
  694 +</#if>
  695 +</#if>
  696 +</#list>
  697 +<#-- 循环子表的列 结束 -->
  698 + ]
  699 +</#if>
  700 +</#list>
0 701 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
  3 + <!-- 子表单区域 -->
  4 + <a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
  5 + <!--主表区域 -->
  6 + <a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true">
  7 + <BasicForm @register="registerForm" ref="formRef"/>
  8 + </a-tab-pane>
  9 + <!--子表单区域 -->
  10 +<#list subTables as sub><#rt/>
  11 + <#if sub.foreignRelationType =='1'>
  12 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index+1}]" :forceRender="true">
  13 + <${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
  14 + </a-tab-pane>
  15 +
  16 + <#else>
  17 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index+1}]" :forceRender="true">
  18 + <JVxeTable
  19 + keep-source
  20 + resizable
  21 + :ref="refKeys[${sub_index+1}]"
  22 + :loading="${sub.entityName?uncap_first}Table.loading"
  23 + :columns="${sub.entityName?uncap_first}Table.columns"
  24 + :dataSource="${sub.entityName?uncap_first}Table.dataSource"
  25 + :maxHeight="300"
  26 + :rowNumber="true"
  27 + :rowSelection="true"
  28 + :toolbar="true"
  29 + />
  30 + </a-tab-pane>
  31 + </#if>
  32 +</#list>
  33 + </a-tabs>
  34 + </BasicModal>
  35 +</template>
  36 +
  37 +<script lang="ts" setup>
  38 + import {ref, computed, unref,reactive} from 'vue';
  39 + import {BasicModal, useModalInner} from '/@/components/Modal';
  40 + import {BasicForm, useForm} from '/@/components/Form/index';
  41 + import { JVxeTable } from '/@/components/jeecg/JVxeTable'
  42 + import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'
  43 + <#list subTables as sub>
  44 + <#if sub.foreignRelationType =='1'>
  45 + import ${sub.entityName}Form from './${sub.entityName}Form.vue'
  46 + </#if>
  47 + </#list>
  48 + import {formSchema<#list subTables as sub><#if sub.foreignRelationType =='0'>,${sub.entityName?uncap_first}Columns</#if></#list>} from '../${entityName?uncap_first}.data';
  49 + import {saveOrUpdate<#list subTables as sub>,${sub.entityName?uncap_first}List</#list>} from '../${entityName?uncap_first}.api';
  50 + import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
  51 + // Emits声明
  52 + const emit = defineEmits(['register','success']);
  53 + const isUpdate = ref(true);
  54 + const refKeys = ref(['${tableVo.entityName?uncap_first}',<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
  55 + <#assign hasOne2Many = false>
  56 + <#assign hasOne2One = false>
  57 + const activeKey = ref('${tableVo.entityName?uncap_first}');
  58 +<#list subTables as sub>
  59 +<#if sub.foreignRelationType =='0'>
  60 + <#assign hasOne2Many = true>
  61 + const ${sub.entityName?uncap_first} = ref();
  62 +</#if>
  63 +<#if sub.foreignRelationType =='1'>
  64 + <#assign hasOne2One = true>
  65 + const ${sub.entityName?uncap_first}Form = ref();
  66 +</#if>
  67 +</#list>
  68 + const tableRefs = {<#list subTables as sub><#if sub.foreignRelationType =='0'>${sub.entityName?uncap_first}, <#assign hasOne2Many = true></#if></#list>};
  69 + <#list subTables as sub>
  70 + <#if sub.foreignRelationType =='0'>
  71 + const ${sub.entityName?uncap_first}Table = reactive({
  72 + loading: false,
  73 + dataSource: [],
  74 + columns:${sub.entityName?uncap_first}Columns
  75 + })
  76 + </#if>
  77 + </#list>
  78 + //表单配置
  79 + const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
  80 + labelWidth: 150,
  81 + schemas: formSchema,
  82 + showActionButtonGroup: false,
  83 + });
  84 + //表单赋值
  85 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  86 + //重置表单
  87 + await reset();
  88 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  89 + isUpdate.value = !!data?.isUpdate;
  90 + if (unref(isUpdate)) {
  91 + //表单赋值
  92 + await setFieldsValue({
  93 + ...data.record,
  94 + });
  95 + <#list subTables as sub><#rt/>
  96 + <#if sub.foreignRelationType =='1'>
  97 + ${sub.entityName?uncap_first}Form.value.initFormData(${sub.entityName?uncap_first}List,data?.record?.id)
  98 + </#if>
  99 + </#list>
  100 + <#list subTables as sub><#rt/>
  101 + <#if sub.foreignRelationType =='0'>
  102 + requestSubTableData(${sub.entityName?uncap_first}List, {id:data?.record?.id}, ${sub.entityName?uncap_first}Table)
  103 + </#if>
  104 + </#list>
  105 + }
  106 + // 隐藏底部时禁用整个表单
  107 + setProps({ disabled: !data?.showFooter })
  108 + });
  109 + //方法配置
  110 + const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys<#if hasOne2One==true>,validateSubForm</#if>);
  111 +
  112 + //设置标题
  113 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  114 +
  115 + async function reset(){
  116 + await resetFields();
  117 + activeKey.value = ref('${tableVo.entityName?uncap_first}');
  118 + <#list subTables as sub>
  119 + <#if sub.foreignRelationType =='0'>
  120 + ${sub.entityName?uncap_first}Table.dataSource = [];
  121 + </#if>
  122 + <#if sub.foreignRelationType =='1'>
  123 + ${sub.entityName?uncap_first}Form.value.resetFields();
  124 + </#if>
  125 + </#list>
  126 + }
  127 + function classifyIntoFormData(allValues) {
  128 + let main = Object.assign({}, allValues.formValue)
  129 + return {
  130 + ...main, // 展开
  131 + <#assign subManyIndex = 0>
  132 + <#list subTables as sub><#rt/>
  133 + <#if sub.foreignRelationType =='0'>
  134 + ${sub.entityName?uncap_first}List: allValues.tablesValue[${subManyIndex}].tableData,
  135 + <#assign subManyIndex = subManyIndex+1>
  136 + <#else>
  137 + ${sub.entityName?uncap_first}List: ${sub.entityName?uncap_first}Form.value.getFormData(),
  138 + </#if>
  139 + </#list>
  140 + }
  141 + }
  142 + <#if hasOne2One==true>
  143 + //校验所有一对一子表表单
  144 + function validateSubForm(allValues){
  145 + return new Promise((resolve,reject)=>{
  146 + Promise.all([
  147 + <#list subTables as sub><#rt/>
  148 + <#if sub.foreignRelationType =='1'>
  149 + ${sub.entityName?uncap_first}Form.value.validateForm(${sub_index+1}),
  150 + </#if>
  151 + </#list>
  152 + ]).then(() => {
  153 + resolve(allValues)
  154 + }).catch(e => {
  155 + if (e.error === VALIDATE_FAILED) {
  156 + // 如果有未通过表单验证的子表,就自动跳转到它所在的tab
  157 + activeKey.value = e.index == null ? unref(activeKey) : refKeys.value[e.index]
  158 + } else {
  159 + console.error(e)
  160 + }
  161 + })
  162 + })
  163 + }
  164 + </#if>
  165 + //表单提交事件
  166 + async function requestAddOrEdit(values) {
  167 + try {
  168 + setModalProps({confirmLoading: true});
  169 + //提交表单
  170 + await saveOrUpdate(values, isUpdate.value);
  171 + //关闭弹窗
  172 + closeModal();
  173 + //刷新列表
  174 + emit('success');
  175 + } finally {
  176 + setModalProps({confirmLoading: false});
  177 + }
  178 + }
  179 +</script>
  180 +
  181 +<style lang="less" scoped>
  182 +
  183 +</style>
0 184 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vue3/components/[1-n]Form.vuei 0 → 100644
  1 +<#list subTables as sub>
  2 +<#if sub.foreignRelationType=='1'>
  3 +#segment#${sub.entityName}Form.vue
  4 +<template>
  5 + <BasicForm @register="registerForm"/>
  6 +</template>
  7 +<script lang="ts">
  8 + import {defineComponent} from 'vue';
  9 + import {BasicForm, useForm} from '/@/components/Form/index';
  10 + import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
  11 + import {defHttp} from '/@/utils/http/axios';
  12 + import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
  13 +
  14 + export default defineComponent({
  15 + name:"${sub.entityName}Form",
  16 + components: {BasicForm},
  17 + emits:['register'],
  18 + setup(_,{emit}) {
  19 + const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
  20 + labelWidth: 150,
  21 + schemas: ${sub.entityName?uncap_first}FormSchema,
  22 + showActionButtonGroup: false,
  23 + });
  24 + /**
  25 + *初始化加载数据
  26 + */
  27 + function initFormData(url,id){
  28 + if(id){
  29 + defHttp.get({url,params:{id}},{isTransformResponse:false}).then(res=>{
  30 + res.success && setFieldsValue({...res.result[0]});
  31 + })
  32 + }
  33 + }
  34 + /**
  35 + *获取表单数据
  36 + */
  37 + function getFormData(){
  38 + return [getFieldsValue()];
  39 + }
  40 + /**
  41 + *表单校验
  42 + */
  43 + function validateForm(index){
  44 + return new Promise((resolve, reject) => {
  45 + // 验证子表表单
  46 + validate().then(()=>{
  47 + return resolve()
  48 + }).catch(()=> {
  49 + return reject({ error: VALIDATE_FAILED ,index})
  50 + })
  51 + })
  52 + }
  53 + return {
  54 + registerForm,
  55 + resetFields,
  56 + initFormData,
  57 + getFormData,
  58 + validateForm
  59 + }
  60 + }
  61 + })
  62 +</script>
  63 +</#if>
  64 +</#list>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -93,7 +93,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
93 93 */
94 94 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
95 95 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
96   - @PutMapping(value = "/edit")
  96 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
97 97 public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
98 98 ${entityName?uncap_first}Service.updateById(${entityName?uncap_first});
99 99 return Result.OK("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/vue-app/${entityName}Form.vuei 0 → 100644
  1 +<template>
  2 + <view>
  3 + <!--标题和返回-->
  4 + <cu-custom :bgColor="NavBarColor" isBack :backRouterName="backRouteName">
  5 + <block slot="backText">返回</block>
  6 + <block slot="content">${tableVo.ftlDescription}</block>
  7 + </cu-custom>
  8 + <!--表单区域-->
  9 + <view>
  10 + <form>
  11 + <#list columns as po><#rt/>
  12 + <#if po.fieldName !='id'><#rt/>
  13 + <#if po.fieldType =='date'>
  14 + <my-date label="${po.filedComment}:" fields="day" v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}"></my-date>
  15 + <#elseif po.fieldType =='datetime'>
  16 + <my-date label="${po.filedComment}:" v-model="model.${po.fieldName}" placeholder="请输入${po.filedComment}"></my-date>
  17 + <#else>
  18 + <view class="cu-form-group">
  19 + <view class="flex align-center">
  20 + <view class="title"><text space="ensp">${po.filedComment}:</text></view>
  21 + <input <#if "int,decimal,double,"?contains(po.fieldType)>type="number"</#if> placeholder="请输入${po.filedComment}" v-model="model.${po.fieldName}"/>
  22 + </view>
  23 + </view>
  24 + </#if>
  25 + </#if>
  26 + </#list>
  27 + <view class="padding">
  28 + <button class="cu-btn block bg-blue margin-tb-sm lg" @click="onSubmit">
  29 + <text v-if="loading" class="cuIcon-loading2 cuIconfont-spin"></text>提交
  30 + </button>
  31 + </view>
  32 + </form>
  33 + </view>
  34 + </view>
  35 +</template>
  36 +
  37 +<script>
  38 + import myDate from '@/components/my-componets/my-date.vue'
  39 +
  40 + export default {
  41 + name: "${entityName}Form",
  42 + components:{myDate},
  43 + props:{
  44 + formData:{
  45 + type:Object,
  46 + default:()=>{},
  47 + required:false
  48 + }
  49 + },
  50 + data(){
  51 + return {
  52 + CustomBar: this.CustomBar,
  53 + NavBarColor: this.NavBarColor,
  54 + loading:false,
  55 + model: {},
  56 + backRouteName:'index',
  57 + url: {
  58 + queryById: "/${entityPackage}/${entityName?uncap_first}/queryById",
  59 + add: "/${entityPackage}/${entityName?uncap_first}/add",
  60 + edit: "/${entityPackage}/${entityName?uncap_first}/edit",
  61 + },
  62 + }
  63 + },
  64 + created(){
  65 + this.initFormData();
  66 + },
  67 + methods:{
  68 + initFormData(){
  69 + if(this.formData){
  70 + let dataId = this.formData.dataId;
  71 + this.$http.get(this.url.queryById,{params:{id:dataId}}).then((res)=>{
  72 + if(res.data.success){
  73 + console.log("表单数据",res);
  74 + this.model = res.data.result;
  75 + }
  76 + })
  77 + }
  78 + },
  79 + onSubmit() {
  80 + let myForm = {...this.model};
  81 + this.loading = true;
  82 + let url = myForm.id?this.url.edit:this.url.add;
  83 + this.$http.post(url,myForm).then(res=>{
  84 + console.log("res",res)
  85 + this.loading = false
  86 + this.$Router.push({name:this.backRouteName})
  87 + }).catch(()=>{
  88 + this.loading = false
  89 + });
  90 + }
  91 + }
  92 + }
  93 +</script>
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/vue-app/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <view>
  3 + <!--标题和返回-->
  4 + <cu-custom :bgColor="NavBarColor" isBack>
  5 + <block slot="backText">返回</block>
  6 + <block slot="content">${tableVo.ftlDescription}</block>
  7 + </cu-custom>
  8 + <!--滚动加载列表-->
  9 + <mescroll-body ref="mescrollRef" bottom="88" @init="mescrollInit" :up="upOption" :down="downOption" @down="downCallback" @up="upCallback">
  10 + <view class="cu-list menu">
  11 + <view class="cu-item" v-for="(item,index) in list" :key="index" @click="goHome">
  12 + <view class="flex" style="width:100%">
  13 + <text class="text-lg" style="color: #000;">
  14 + {{ item.createBy}}
  15 + </text>
  16 + </view>
  17 + </view>
  18 + </view>
  19 + </mescroll-body>
  20 + </view>
  21 +</template>
  22 +
  23 +<script>
  24 + import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
  25 + import Mixin from "@/common/mixin/Mixin.js";
  26 +
  27 + export default {
  28 + name: '${tableVo.ftlDescription}',
  29 + mixins: [MescrollMixin,Mixin],
  30 + data() {
  31 + return {
  32 + CustomBar:this.CustomBar,
  33 + NavBarColor:this.NavBarColor,
  34 + url: "/${entityPackage}/${entityName?uncap_first}/list",
  35 + };
  36 + },
  37 + methods: {
  38 + goHome(){
  39 + this.$Router.push({name: "index"})
  40 + }
  41 + }
  42 + }
  43 +</script>
  44 +
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 + <!--引用表格-->
  4 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  5 + <!--插槽:table标题-->
  6 + <template #tableTitle>
  7 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  8 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  9 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  10 + <a-dropdown v-if="checkedKeys.length > 0">
  11 + <template #overlay>
  12 + <a-menu>
  13 + <a-menu-item key="1" @click="batchHandleDelete">
  14 + <Icon icon="ant-design:delete-outlined"></Icon>
  15 + 删除
  16 + </a-menu-item>
  17 + </a-menu>
  18 + </template>
  19 + <a-button>批量操作
  20 + <Icon icon="mdi:chevron-down"></Icon>
  21 + </a-button>
  22 + </a-dropdown>
  23 + </template>
  24 + <!--操作栏-->
  25 + <template #action="{ record }">
  26 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  27 + </template>
  28 + </BasicTable>
  29 +
  30 + <!-- 表单区域 -->
  31 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  32 + </div>
  33 +</template>
  34 +
  35 +<script lang="ts" setup>
  36 + import {ref, computed, unref} from 'vue';
  37 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  38 + import {useModal} from '/@/components/Modal';
  39 + import { useListPage } from '/@/hooks/system/useListPage'
  40 + import ${entityName}Modal from './modules/${entityName}Modal.vue'
  41 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  42 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  43 +
  44 + const checkedKeys = ref<Array<string | number>>([]);
  45 + //注册model
  46 + const [registerModal, {openModal}] = useModal();
  47 + //注册table数据
  48 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  49 + tableProps:{
  50 + title: '${tableVo.ftlDescription}',
  51 + api: list,
  52 + columns,
  53 + canResize:false,
  54 + formConfig: {
  55 + labelWidth: 120,
  56 + schemas: searchFormSchema,
  57 + autoSubmitOnEnter:true,
  58 + showAdvancedButton:true,
  59 + },
  60 + actionColumn: {
  61 + width: 120,
  62 + },
  63 + },
  64 + exportConfig: {
  65 + name:"${tableVo.ftlDescription}",
  66 + url: getExportUrl,
  67 + },
  68 + importConfig: {
  69 + url: getImportUrl
  70 + },
  71 + })
  72 +
  73 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  74 +
  75 + /**
  76 + * 新增事件
  77 + */
  78 + function handleAdd() {
  79 + openModal(true, {
  80 + isUpdate: false,
  81 + showFooter: true,
  82 + });
  83 + }
  84 + /**
  85 + * 编辑事件
  86 + */
  87 + function handleEdit(record: Recordable) {
  88 + openModal(true, {
  89 + record,
  90 + isUpdate: true,
  91 + showFooter: true,
  92 + });
  93 + }
  94 + /**
  95 + * 详情
  96 + */
  97 + function handleDetail(record: Recordable) {
  98 + openModal(true, {
  99 + record,
  100 + isUpdate: true,
  101 + showFooter: false,
  102 + });
  103 + }
  104 + /**
  105 + * 删除事件
  106 + */
  107 + async function handleDelete(record) {
  108 + await deleteOne({id: record.id}, reload);
  109 + }
  110 + /**
  111 + * 批量删除事件
  112 + */
  113 + async function batchHandleDelete() {
  114 + await batchDelete({ids: checkedKeys.value}, reload);
  115 + }
  116 + /**
  117 + * 成功回调
  118 + */
  119 + function handleSuccess({isUpdate, values}) {
  120 + reload();
  121 + }
  122 + /**
  123 + * 操作栏
  124 + */
  125 + function getTableAction(record){
  126 + return [
  127 + {
  128 + label: '编辑',
  129 + onClick: handleEdit.bind(null, record),
  130 + }
  131 + ]
  132 + }
  133 + /**
  134 + * 下拉操作栏
  135 + */
  136 + function getDropDownAction(record){
  137 + return [
  138 + {
  139 + label: '详情',
  140 + onClick: handleDetail.bind(null, record),
  141 + }, {
  142 + label: '删除',
  143 + popConfirm: {
  144 + title: '是否确认删除',
  145 + confirm: handleDelete.bind(null, record),
  146 + }
  147 + }
  148 + ]
  149 + }
  150 +</script>
  151 +<style scoped>
  152 +
  153 +</style>
0 154 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +}
  13 +/**
  14 + * 导出api
  15 + * @param params
  16 + */
  17 +export const getExportUrl = Api.exportXls;
  18 +/**
  19 + * 导入api
  20 + */
  21 +export const getImportUrl = Api.importExcel;
  22 +/**
  23 + * 列表接口
  24 + * @param params
  25 + */
  26 +export const list = (params) =>
  27 + defHttp.get({url: Api.list, params});
  28 +
  29 +/**
  30 + * 删除单个
  31 + */
  32 +export const deleteOne = (params,handleSuccess) => {
  33 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  34 + handleSuccess();
  35 + });
  36 +}
  37 +/**
  38 + * 批量删除
  39 + * @param params
  40 + */
  41 +export const batchDelete = (params, handleSuccess) => {
  42 + Modal.confirm({
  43 + title: '确认删除',
  44 + content: '是否删除选中数据',
  45 + okText: '确认',
  46 + cancelText: '取消',
  47 + onOk: () => {
  48 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  49 + handleSuccess();
  50 + });
  51 + }
  52 + });
  53 +}
  54 +/**
  55 + * 保存或者更新
  56 + * @param params
  57 + */
  58 +export const saveOrUpdate = (params, isUpdate) => {
  59 + let url = isUpdate ? Api.edit : Api.save;
  60 + return defHttp.post({url: url, params});
  61 +}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +
  6 +export const columns: BasicColumn[] = [
  7 + <#list columns as po>
  8 + <#if po.fieldName !='id'>
  9 + {
  10 + title: '${po.filedComment}',
  11 + dataIndex: '${po.fieldName}'
  12 + },
  13 + </#if>
  14 + </#list>
  15 +];
  16 +
  17 +export const searchFormSchema: FormSchema[] = [
  18 +<#list columns as po>
  19 +<#if po.fieldName !='id' && po_index<= tableVo.searchFieldNum>
  20 + {
  21 + label: '${po.filedComment}',
  22 + field: '${po.fieldName}',
  23 + <#if po.fieldType =='date'>
  24 + component: 'DatePicker'
  25 + <#elseif po.fieldType =='datetime'>
  26 + component: 'TimePicker'
  27 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  28 + component: 'InputNumber'
  29 + <#else>
  30 + component: 'Input'
  31 + </#if>
  32 + },
  33 +</#if>
  34 +</#list>
  35 +];
  36 +
  37 +export const formSchema: FormSchema[] = [
  38 +<#list columns as po><#rt/>
  39 + {
  40 + label: '${po.filedComment}',
  41 + field: '${po.fieldName}',
  42 + <#if po.fieldType =='date'>
  43 + component: 'DatePicker'
  44 + <#elseif po.fieldType =='datetime'>
  45 + component: 'TimePicker'
  46 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  47 + component: 'InputNumber'
  48 + <#else>
  49 + component: 'Input'
  50 + </#if>
  51 + <#if po.fieldName =='id'><#rt/>
  52 + show:false
  53 + </#if>
  54 + },
  55 +</#list>
  56 +];
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/vue3/modules/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="40%">
  3 + <BasicForm @register="registerForm"/>
  4 + </BasicModal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import {ref, computed, unref} from 'vue';
  9 + import {BasicModal, useModalInner} from '/@/components/Modal';
  10 + import {BasicForm, useForm} from '/@/components/Form/index';
  11 + import {formSchema} from '../${entityName?uncap_first}.data';
  12 + import {saveOrUpdate} from '../${entityName?uncap_first}.api';
  13 + // Emits声明
  14 + const emit = defineEmits(['register','success']);
  15 + const isUpdate = ref(true);
  16 + //表单配置
  17 + const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
  18 + labelWidth: 150,
  19 + schemas: formSchema,
  20 + showActionButtonGroup: false,
  21 + });
  22 + //表单赋值
  23 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  24 + //重置表单
  25 + await resetFields();
  26 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  27 + isUpdate.value = !!data?.isUpdate;
  28 + if (unref(isUpdate)) {
  29 + //表单赋值
  30 + await setFieldsValue({
  31 + ...data.record,
  32 + });
  33 + }
  34 + });
  35 + //设置标题
  36 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  37 + //表单提交事件
  38 + async function handleSubmit(v) {
  39 + try {
  40 + let values = await validate();
  41 + setModalProps({confirmLoading: true});
  42 + //提交表单
  43 + await saveOrUpdate(values, isUpdate.value);
  44 + //关闭弹窗
  45 + closeModal();
  46 + //刷新列表
  47 + emit('success');
  48 + } finally {
  49 + setModalProps({confirmLoading: false});
  50 + }
  51 + }
  52 +</script>
  53 +
  54 +<style lang="less" scoped>
  55 +
  56 +</style>
0 57 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/controller/${entityPackage}/${entityName}Controller.javai renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/controller/${entityPackage}/${entityName}Controller.javai
... ... @@ -97,7 +97,7 @@ public class ${entityName}Controller extends JeecgController&lt;${entityName}, I${e
97 97 */
98 98 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
99 99 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
100   - @PutMapping(value = "/edit")
  100 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
101 101 public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
102 102 ${entityName?uncap_first}Service.updateById(${entityName?uncap_first});
103 103 return Result.OK("编辑成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/entity/${entityPackage}/${entityName}.javai renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/entity/${entityPackage}/${entityName}.javai
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/mapper/${entityPackage}/${entityName}Mapper.javai renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/mapper/${entityPackage}/${entityName}Mapper.javai
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/mapper/${entityPackage}/xml/${entityName}Mapper.xml renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/mapper/${entityPackage}/xml/${entityName}Mapper.xml
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/service/${entityPackage}/I${entityName}Service.javai renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/service/${entityPackage}/I${entityName}Service.javai
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/service/${entityPackage}/impl/${entityName}ServiceImpl.javai renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/service/${entityPackage}/impl/${entityName}ServiceImpl.javai
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/vue/${entityPackage}/${entityName}List.vuei renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/vue/${entityPackage}/${entityName}List.vuei
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/vue/${entityPackage}/modules/${entityName}Modal.vuei renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/vue/${entityPackage}/modules/${entityName}Modal.vuei
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/vue/${entityPackage}/modules/${entityName}Modal__Style#Drawer.vuei renamed to jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/vue/${entityPackage}/modules/${entityName}Modal__Style#Drawer.vuei
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 + <!--引用表格-->
  4 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  5 + <!--插槽:table标题-->
  6 + <template #tableTitle>
  7 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  8 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  9 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  10 + <a-dropdown v-if="checkedKeys.length > 0">
  11 + <template #overlay>
  12 + <a-menu>
  13 + <a-menu-item key="1" @click="batchHandleDelete">
  14 + <Icon icon="ant-design:delete-outlined"></Icon>
  15 + 删除
  16 + </a-menu-item>
  17 + </a-menu>
  18 + </template>
  19 + <a-button>批量操作
  20 + <Icon icon="mdi:chevron-down"></Icon>
  21 + </a-button>
  22 + </a-dropdown>
  23 + </template>
  24 + <!--操作栏-->
  25 + <template #action="{ record }">
  26 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  27 + </template>
  28 + </BasicTable>
  29 +
  30 + <!-- 表单区域 -->
  31 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  32 + </div>
  33 +</template>
  34 +
  35 +<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
  36 + import {ref, computed, unref} from 'vue';
  37 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  38 + import {useModal} from '/@/components/Modal';
  39 + import { useListPage } from '/@/hooks/system/useListPage'
  40 + import ${entityName}Modal from './modules/${entityName}Modal.vue'
  41 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  42 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  43 +
  44 + const checkedKeys = ref<Array<string | number>>([]);
  45 + //注册model
  46 + const [registerModal, {openModal}] = useModal();
  47 + //注册table数据
  48 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  49 + tableProps:{
  50 + title: '${tableVo.ftlDescription}',
  51 + api: list,
  52 + columns,
  53 + canResize:false,
  54 + formConfig: {
  55 + labelWidth: 120,
  56 + schemas: searchFormSchema,
  57 + autoSubmitOnEnter:true,
  58 + showAdvancedButton:true,
  59 + },
  60 + actionColumn: {
  61 + width: 120,
  62 + },
  63 + },
  64 + exportConfig: {
  65 + name:"${tableVo.ftlDescription}",
  66 + url: getExportUrl,
  67 + },
  68 + importConfig: {
  69 + url: getImportUrl
  70 + },
  71 + })
  72 +
  73 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  74 +
  75 + /**
  76 + * 新增事件
  77 + */
  78 + function handleAdd() {
  79 + openModal(true, {
  80 + isUpdate: false,
  81 + showFooter: true,
  82 + });
  83 + }
  84 + /**
  85 + * 编辑事件
  86 + */
  87 + function handleEdit(record: Recordable) {
  88 + openModal(true, {
  89 + record,
  90 + isUpdate: true,
  91 + showFooter: true,
  92 + });
  93 + }
  94 + /**
  95 + * 详情
  96 + */
  97 + function handleDetail(record: Recordable) {
  98 + openModal(true, {
  99 + record,
  100 + isUpdate: true,
  101 + showFooter: false,
  102 + });
  103 + }
  104 + /**
  105 + * 删除事件
  106 + */
  107 + async function handleDelete(record) {
  108 + await deleteOne({id: record.id}, reload);
  109 + }
  110 + /**
  111 + * 批量删除事件
  112 + */
  113 + async function batchHandleDelete() {
  114 + await batchDelete({ids: checkedKeys.value}, reload);
  115 + }
  116 + /**
  117 + * 成功回调
  118 + */
  119 + function handleSuccess({isUpdate, values}) {
  120 + reload();
  121 + }
  122 + /**
  123 + * 操作栏
  124 + */
  125 + function getTableAction(record){
  126 + return [
  127 + {
  128 + label: '编辑',
  129 + onClick: handleEdit.bind(null, record),
  130 + }
  131 + ]
  132 + }
  133 + /**
  134 + * 下拉操作栏
  135 + */
  136 + function getDropDownAction(record){
  137 + return [
  138 + {
  139 + label: '详情',
  140 + onClick: handleDetail.bind(null, record),
  141 + }, {
  142 + label: '删除',
  143 + popConfirm: {
  144 + title: '是否确认删除',
  145 + confirm: handleDelete.bind(null, record),
  146 + }
  147 + }
  148 + ]
  149 + }
  150 +</script>
  151 +<style scoped>
  152 +
  153 +</style>
0 154 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +}
  13 +/**
  14 + * 导出api
  15 + * @param params
  16 + */
  17 +export const getExportUrl = Api.exportXls;
  18 +/**
  19 + * 导入api
  20 + */
  21 +export const getImportUrl = Api.importExcel;
  22 +/**
  23 + * 列表接口
  24 + * @param params
  25 + */
  26 +export const list = (params) =>
  27 + defHttp.get({url: Api.list, params});
  28 +
  29 +/**
  30 + * 删除单个
  31 + */
  32 +export const deleteOne = (params,handleSuccess) => {
  33 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  34 + handleSuccess();
  35 + });
  36 +}
  37 +/**
  38 + * 批量删除
  39 + * @param params
  40 + */
  41 +export const batchDelete = (params, handleSuccess) => {
  42 + Modal.confirm({
  43 + title: '确认删除',
  44 + content: '是否删除选中数据',
  45 + okText: '确认',
  46 + cancelText: '取消',
  47 + onOk: () => {
  48 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  49 + handleSuccess();
  50 + });
  51 + }
  52 + });
  53 +}
  54 +/**
  55 + * 保存或者更新
  56 + * @param params
  57 + */
  58 +export const saveOrUpdate = (params, isUpdate) => {
  59 + let url = isUpdate ? Api.edit : Api.save;
  60 + return defHttp.post({url: url, params});
  61 +}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +
  6 +export const columns: BasicColumn[] = [
  7 + <#list columns as po>
  8 + <#if po.fieldName !='id'>
  9 + {
  10 + title: '${po.filedComment}',
  11 + dataIndex: '${po.fieldName}'
  12 + },
  13 + </#if>
  14 + </#list>
  15 +];
  16 +
  17 +export const searchFormSchema: FormSchema[] = [
  18 +<#list columns as po>
  19 +<#if po.fieldName !='id' && po_index<= tableVo.searchFieldNum>
  20 + {
  21 + label: '${po.filedComment}',
  22 + field: '${po.fieldName}',
  23 + <#if po.fieldType =='date'>
  24 + component: 'DatePicker'
  25 + <#elseif po.fieldType =='datetime'>
  26 + component: 'TimePicker'
  27 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  28 + component: 'InputNumber'
  29 + <#else>
  30 + component: 'Input'
  31 + </#if>
  32 + },
  33 +</#if>
  34 +</#list>
  35 +];
  36 +
  37 +export const formSchema: FormSchema[] = [
  38 +<#list columns as po><#rt/>
  39 + {
  40 + label: '${po.filedComment}',
  41 + field: '${po.fieldName}',
  42 + <#if po.fieldType =='date'>
  43 + component: 'DatePicker'
  44 + <#elseif po.fieldType =='datetime'>
  45 + component: 'TimePicker'
  46 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  47 + component: 'InputNumber'
  48 + <#else>
  49 + component: 'Input'
  50 + </#if>
  51 + <#if po.fieldName =='id'><#rt/>
  52 + show:false
  53 + </#if>
  54 + },
  55 +</#list>
  56 +];
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/${entityPackage}/vue3/modules/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="40%">
  3 + <BasicForm @register="registerForm"/>
  4 + </BasicModal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import {ref, computed, unref} from 'vue';
  9 + import {BasicModal, useModalInner} from '/@/components/Modal';
  10 + import {BasicForm, useForm} from '/@/components/Form/index';
  11 + import {formSchema} from '../${entityName?uncap_first}.data';
  12 + import {saveOrUpdate} from '../${entityName?uncap_first}.api';
  13 + // Emits声明
  14 + const emit = defineEmits(['register','success']);
  15 + const isUpdate = ref(true);
  16 + //表单配置
  17 + const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
  18 + labelWidth: 150,
  19 + schemas: formSchema,
  20 + showActionButtonGroup: false,
  21 + });
  22 + //表单赋值
  23 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  24 + //重置表单
  25 + await resetFields();
  26 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  27 + isUpdate.value = !!data?.isUpdate;
  28 + if (unref(isUpdate)) {
  29 + //表单赋值
  30 + await setFieldsValue({
  31 + ...data.record,
  32 + });
  33 + }
  34 + });
  35 + //设置标题
  36 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  37 + //表单提交事件
  38 + async function handleSubmit(v) {
  39 + try {
  40 + let values = await validate();
  41 + setModalProps({confirmLoading: true});
  42 + //提交表单
  43 + await saveOrUpdate(values, isUpdate.value);
  44 + //关闭弹窗
  45 + closeModal();
  46 + //刷新列表
  47 + emit('success');
  48 + } finally {
  49 + setModalProps({confirmLoading: false});
  50 + }
  51 + }
  52 +</script>
  53 +
  54 +<style lang="less" scoped>
  55 +
  56 +</style>
0 57 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -109,7 +109,7 @@ public class ${entityName}Controller {
109 109 */
110 110 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
111 111 @ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
112   - @PutMapping(value = "/edit")
  112 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
113 113 public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
114 114 ${entityName} ${entityName?uncap_first} = new ${entityName}();
115 115 BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 + <!--引用表格-->
  4 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  5 + <!--插槽:table标题-->
  6 + <template #tableTitle>
  7 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  8 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  9 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  10 + <a-dropdown v-if="checkedKeys.length > 0">
  11 + <template #overlay>
  12 + <a-menu>
  13 + <a-menu-item key="1" @click="batchHandleDelete">
  14 + <Icon icon="ant-design:delete-outlined"></Icon>
  15 + 删除
  16 + </a-menu-item>
  17 + </a-menu>
  18 + </template>
  19 + <a-button>批量操作
  20 + <Icon icon="mdi:chevron-down"></Icon>
  21 + </a-button>
  22 + </a-dropdown>
  23 + </template>
  24 + <!--操作栏-->
  25 + <template #action="{ record }">
  26 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  27 + </template>
  28 + </BasicTable>
  29 + <!-- 表单区域 -->
  30 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  31 + </div>
  32 +</template>
  33 +
  34 +<script lang="ts" setup>
  35 + import {ref, computed, unref} from 'vue';
  36 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  37 + import { useListPage } from '/@/hooks/system/useListPage'
  38 + import {useModal} from '/@/components/Modal';
  39 + import ${entityName}Modal from './modules/${entityName}Modal.vue'
  40 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  41 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  42 + const checkedKeys = ref<Array<string | number>>([]);
  43 + //注册model
  44 + const [registerModal, {openModal}] = useModal();
  45 + //注册table数据
  46 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  47 + tableProps:{
  48 + title: '${tableVo.ftlDescription}',
  49 + api: list,
  50 + columns,
  51 + canResize:false,
  52 + formConfig: {
  53 + labelWidth: 120,
  54 + schemas: searchFormSchema,
  55 + autoSubmitOnEnter:true,
  56 + showAdvancedButton:true,
  57 + },
  58 + actionColumn: {
  59 + width: 120,
  60 + },
  61 + },
  62 + exportConfig: {
  63 + name:"${tableVo.ftlDescription}",
  64 + url: getExportUrl,
  65 + },
  66 + importConfig: {
  67 + url: getImportUrl
  68 + },
  69 + })
  70 +
  71 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  72 +
  73 + /**
  74 + * 新增事件
  75 + */
  76 + function handleAdd() {
  77 + openModal(true, {
  78 + isUpdate: false,
  79 + showFooter: true,
  80 + });
  81 + }
  82 + /**
  83 + * 编辑事件
  84 + */
  85 + function handleEdit(record: Recordable) {
  86 + openModal(true, {
  87 + record,
  88 + isUpdate: true,
  89 + showFooter: true,
  90 + });
  91 + }
  92 + /**
  93 + * 详情
  94 + */
  95 + function handleDetail(record: Recordable) {
  96 + openModal(true, {
  97 + record,
  98 + isUpdate: true,
  99 + showFooter: false,
  100 + });
  101 + }
  102 + /**
  103 + * 删除事件
  104 + */
  105 + async function handleDelete(record) {
  106 + await deleteOne({id: record.id}, reload);
  107 + }
  108 + /**
  109 + * 批量删除事件
  110 + */
  111 + async function batchHandleDelete() {
  112 + await batchDelete({ids: checkedKeys.value}, reload);
  113 + }
  114 + /**
  115 + * 成功回调
  116 + */
  117 + function handleSuccess() {
  118 + reload();
  119 + }
  120 + /**
  121 + * 操作栏
  122 + */
  123 + function getTableAction(record){
  124 + return [
  125 + {
  126 + label: '编辑',
  127 + onClick: handleEdit.bind(null, record),
  128 + }
  129 + ]
  130 + }
  131 + /**
  132 + * 下拉操作栏
  133 + */
  134 + function getDropDownAction(record){
  135 + return [
  136 + {
  137 + label: '详情',
  138 + onClick: handleDetail.bind(null, record),
  139 + }, {
  140 + label: '删除',
  141 + popConfirm: {
  142 + title: '是否确认删除',
  143 + confirm: handleDelete.bind(null, record),
  144 + }
  145 + }
  146 + ]
  147 + }
  148 +</script>
  149 +
  150 +<style scoped>
  151 +
  152 +</style>
0 153 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +<#list subTables as sub><#rt/>
  13 + ${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
  14 +</#list>
  15 +}
  16 +/**
  17 + * 导出api
  18 + * @param params
  19 + */
  20 +export const getExportUrl = Api.exportXls;
  21 +
  22 +/**
  23 + * 导入api
  24 + */
  25 +export const getImportUrl = Api.importExcel;
  26 +<#list subTables as sub><#rt/>
  27 +/**
  28 + * 查询子表数据
  29 + * @param params
  30 + */
  31 +export const ${sub.entityName?uncap_first}List = Api.${sub.entityName?uncap_first}List;
  32 +</#list>
  33 +/**
  34 + * 列表接口
  35 + * @param params
  36 + */
  37 +export const list = (params) =>
  38 + defHttp.get({url: Api.list, params});
  39 +
  40 +/**
  41 + * 删除单个
  42 + */
  43 +export const deleteOne = (params,handleSuccess) => {
  44 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  45 + handleSuccess();
  46 + });
  47 +}
  48 +/**
  49 + * 批量删除
  50 + * @param params
  51 + */
  52 +export const batchDelete = (params, handleSuccess) => {
  53 + Modal.confirm({
  54 + title: '确认删除',
  55 + content: '是否删除选中数据',
  56 + okText: '确认',
  57 + cancelText: '取消',
  58 + onOk: () => {
  59 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  60 + handleSuccess();
  61 + });
  62 + }
  63 + });
  64 +}
  65 +/**
  66 + * 保存或者更新
  67 + * @param params
  68 + */
  69 +export const saveOrUpdate = (params, isUpdate) => {
  70 + let url = isUpdate ? Api.edit : Api.save;
  71 + return defHttp.post({url: url, params});
  72 +}
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
  6 +//列表数据
  7 +export const columns: BasicColumn[] = [
  8 + <#list columns as po>
  9 + <#if po.fieldName !='id'>
  10 + {
  11 + title: '${po.filedComment}',
  12 + align:"center",
  13 + <#if po.classType=='date'>
  14 + dataIndex: '${po.fieldName}',
  15 + customRender:({text}) =>{
  16 + return !text?"":(text.length>10?text.substr(0,10):text)
  17 + },
  18 + <#else>
  19 + dataIndex: '${po.fieldName}'
  20 + </#if>
  21 + },
  22 + </#if>
  23 + </#list>
  24 +];
  25 +//查询数据
  26 +export const searchFormSchema: FormSchema[] = [
  27 +<#list columns as po>
  28 +<#if po.fieldName !='id' && po_index<= tableVo.searchFieldNum>
  29 + {
  30 + label: '${po.filedComment}',
  31 + field: '${po.fieldName}',
  32 + <#if po.fieldType =='date'>
  33 + component: 'DatePicker'
  34 + <#elseif po.fieldType =='datetime'>
  35 + component: 'TimePicker'
  36 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  37 + component: 'InputNumber'
  38 + <#else>
  39 + component: 'Input'
  40 + </#if>
  41 + },
  42 +</#if>
  43 +</#list>
  44 +];
  45 +export const formSchema: FormSchema[] = [
  46 +<#list columns as po><#rt/>
  47 + {
  48 + label: '${po.filedComment}',
  49 + field: '${po.fieldName}',
  50 + <#if po.fieldType =='date'>
  51 + component: 'DatePicker'
  52 + <#elseif po.fieldType =='datetime'>
  53 + component: 'TimePicker'
  54 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  55 + component: 'InputNumber'
  56 + <#else>
  57 + component: 'Input'
  58 + </#if>
  59 + <#if po.fieldName =='id'><#rt/>
  60 + show:false
  61 + </#if>
  62 + },
  63 +</#list>
  64 +];
  65 +//子表表格配置
  66 +<#list subTables as sub>
  67 +export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
  68 +<#-- 循环子表的列 开始 -->
  69 +<#list sub.colums as col><#rt/>
  70 +<#if col.filedComment !='外键' >
  71 + {
  72 + title: '${col.filedComment}',
  73 + key: '${col.fieldName}',
  74 +<#if col.classType =='date'>
  75 + type: JVxeTypes.date,
  76 +<#elseif col.classType =='datetime'>
  77 + type: JVxeTypes.datetime,
  78 +<#elseif "int,decimal,double,"?contains(col.classType)>
  79 + type: JVxeTypes.inputNumber,
  80 +<#else>
  81 + type: JVxeTypes.input,
  82 +</#if>
  83 + width:"200px",
  84 + placeholder: '请输入${'$'}{title}',
  85 + defaultValue: '',
  86 +<#-- 子表的校验 -->
  87 +<#if col.nullable =='N'>
  88 + validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
  89 +</#if>
  90 + },
  91 +</#if>
  92 +</#list>
  93 +<#-- 循环子表的列 结束 -->
  94 + ]
  95 +</#list>
0 96 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/vue3/modules/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="1000px">
  3 + <BasicForm @register="registerForm" ref="formRef"/>
  4 + <!-- 子表单区域 -->
  5 + <a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
  6 +<#list subTables as sub><#rt/>
  7 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
  8 + <JVxeTable
  9 + keep-source
  10 + resizable
  11 + :ref="refKeys[${sub_index}]"
  12 + :loading="${sub.entityName?uncap_first}Table.loading"
  13 + :columns="${sub.entityName?uncap_first}Table.columns"
  14 + :dataSource="${sub.entityName?uncap_first}Table.dataSource"
  15 + :maxHeight="300"
  16 + :rowNumber="true"
  17 + :rowSelection="true"
  18 + :toolbar="true"
  19 + />
  20 + </a-tab-pane>
  21 +</#list>
  22 + </a-tabs>
  23 + </BasicModal>
  24 +</template>
  25 +
  26 +<script lang="ts" setup>
  27 + import {ref, computed, unref,reactive} from 'vue';
  28 + import {BasicModal, useModalInner} from '/@/components/Modal';
  29 + import {BasicForm, useForm} from '/@/components/Form/index';
  30 + import { JVxeTable } from '/@/components/jeecg/JVxeTable'
  31 + import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'
  32 + import {formSchema<#list subTables as sub>,${sub.entityName?uncap_first}Columns</#list>} from '../${entityName?uncap_first}.data';
  33 + import {saveOrUpdate<#list subTables as sub>,${sub.entityName?uncap_first}List</#list>} from '../${entityName?uncap_first}.api';
  34 + import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
  35 + // Emits声明
  36 + const emit = defineEmits(['register','success']);
  37 + const isUpdate = ref(true);
  38 + const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
  39 + <#assign hasOne2Many = false>
  40 + const activeKey = ref('${subTables[0].entityName?uncap_first}');
  41 +<#list subTables as sub>
  42 + const ${sub.entityName?uncap_first} = ref();
  43 +</#list>
  44 + const tableRefs = {<#list subTables as sub>${sub.entityName?uncap_first},</#list>};
  45 + <#list subTables as sub>
  46 + const ${sub.entityName?uncap_first}Table = reactive({
  47 + loading: false,
  48 + dataSource: [],
  49 + columns:${sub.entityName?uncap_first}Columns
  50 + })
  51 + </#list>
  52 + //表单配置
  53 + const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
  54 + labelWidth: 150,
  55 + schemas: formSchema,
  56 + showActionButtonGroup: false,
  57 + });
  58 + //表单赋值
  59 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  60 + //重置表单
  61 + await reset();
  62 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  63 + isUpdate.value = !!data?.isUpdate;
  64 + if (unref(isUpdate)) {
  65 + //表单赋值
  66 + await setFieldsValue({
  67 + ...data.record,
  68 + });
  69 + <#list subTables as sub><#rt/>
  70 + requestSubTableData(${sub.entityName?uncap_first}List, {id:data?.record?.id}, ${sub.entityName?uncap_first}Table)
  71 + </#list>
  72 + }
  73 + // 隐藏底部时禁用整个表单
  74 + setProps({ disabled: !data?.showFooter })
  75 + });
  76 + //方法配置
  77 + const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys);
  78 +
  79 + //设置标题
  80 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  81 +
  82 + async function reset(){
  83 + await resetFields();
  84 + activeKey.value = ref('${subTables[0].entityName?uncap_first}');
  85 + <#list subTables as sub>
  86 + ${sub.entityName?uncap_first}Table.dataSource = [];
  87 + </#list>
  88 + }
  89 + function classifyIntoFormData(allValues) {
  90 + let main = Object.assign({}, allValues.formValue)
  91 + return {
  92 + ...main, // 展开
  93 + <#assign subManyIndex = 0>
  94 + <#list subTables as sub><#rt/>
  95 + ${sub.entityName?uncap_first}List: allValues.tablesValue[${subManyIndex}].tableData,
  96 + <#assign subManyIndex = subManyIndex+1>
  97 + </#list>
  98 + }
  99 + }
  100 +
  101 + //表单提交事件
  102 + async function requestAddOrEdit(values) {
  103 + try {
  104 + setModalProps({confirmLoading: true});
  105 + //提交表单
  106 + await saveOrUpdate(values, isUpdate.value);
  107 + //关闭弹窗
  108 + closeModal();
  109 + //刷新列表
  110 + emit('success');
  111 + } finally {
  112 + setModalProps({confirmLoading: false});
  113 + }
  114 + }
  115 +</script>
  116 +
  117 +<style lang="less" scoped>
  118 +
  119 +</style>
0 120 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
... ... @@ -108,7 +108,7 @@ public class ${entityName}Controller {
108 108 */
109 109 @AutoLog(value = "${tableVo.ftlDescription}-编辑")
110 110 @ApiOperation(value="${tableVo.ftlDescription}-", notes="${tableVo.ftlDescription}-编辑")
111   - @PutMapping(value = "/edit")
  111 + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
112 112 public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
113 113 ${entityName} ${entityName?uncap_first} = new ${entityName}();
114 114 BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});
... ... @@ -223,7 +223,7 @@ ${sub.entityName?uncap_first}.get${key}()!=null&lt;#rt/&gt;
223 223 */
224 224 @AutoLog(value = "${sub.ftlDescription}-编辑")
225 225 @ApiOperation(value="${sub.ftlDescription}-编辑", notes="${sub.ftlDescription}-编辑")
226   - @PutMapping("/edit${sub.entityName}")
  226 + @RequestMapping(value = "/edit${sub.entityName}", method = {RequestMethod.PUT,RequestMethod.POST})
227 227 public Result<?> edit${sub.entityName}(@RequestBody ${sub.entityName} ${sub.entityName?uncap_first}) {
228 228 ${sub.entityName?uncap_first}Service.updateById(${sub.entityName?uncap_first});
229 229 return Result.OK("编辑${sub.ftlDescription}成功!");
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vue3/${entityName}List.vuei 0 → 100644
  1 +<template>
  2 + <div>
  3 + <!--引用表格-->
  4 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  5 + <!--插槽:table标题-->
  6 + <template #tableTitle>
  7 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  8 + <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
  9 + <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
  10 + <a-dropdown v-if="checkedKeys.length > 0">
  11 + <template #overlay>
  12 + <a-menu>
  13 + <a-menu-item key="1" @click="batchHandleDelete">
  14 + <Icon icon="ant-design:delete-outlined"></Icon>
  15 + 删除
  16 + </a-menu-item>
  17 + </a-menu>
  18 + </template>
  19 + <a-button>批量操作
  20 + <Icon icon="mdi:chevron-down"></Icon>
  21 + </a-button>
  22 + </a-dropdown>
  23 + </template>
  24 + <!--操作栏-->
  25 + <template #action="{ record }">
  26 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
  27 + </template>
  28 + </BasicTable>
  29 + <a-tabs :defaultActiveKey="refKeys[0]" style="margin: 10px">
  30 + <#list subTables as sub><#rt/>
  31 + <a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
  32 + <${sub.entityName}List></${sub.entityName}List>
  33 + </a-tab-pane>
  34 + </#list>
  35 + </a-tabs>
  36 +
  37 + <!-- 表单区域 -->
  38 + <${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
  39 + </div>
  40 +</template>
  41 +
  42 +<script lang="ts" setup>
  43 + import {ref, computed, unref,provide} from 'vue';
  44 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  45 +<#list subTables as sub><#rt/>
  46 + import ${sub.entityName}List from './${sub.entityName}List.vue'
  47 +</#list>
  48 + import { useListPage } from '/@/hooks/system/useListPage'
  49 + import {useModal} from '/@/components/Modal';
  50 + import ${entityName}Modal from './modules/${entityName}Modal.vue'
  51 + import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
  52 + import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
  53 +
  54 + const checkedKeys = ref<Array<string | number>>([]);
  55 + const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
  56 + //注册model
  57 + const [registerModal, {openModal}] = useModal();
  58 + //注册table数据
  59 + const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
  60 + tableProps:{
  61 + title: '${tableVo.ftlDescription}',
  62 + api: list,
  63 + columns,
  64 + canResize: false,
  65 + rowSelection: {type: 'radio'},
  66 + formConfig: {
  67 + labelWidth: 120,
  68 + schemas: searchFormSchema,
  69 + autoSubmitOnEnter:true,
  70 + showAdvancedButton:true,
  71 + },
  72 + actionColumn: {
  73 + width: 120,
  74 + },
  75 + },
  76 + exportConfig: {
  77 + name:"${tableVo.ftlDescription}",
  78 + url: getExportUrl,
  79 + },
  80 + importConfig: {
  81 + url: getImportUrl
  82 + },
  83 + })
  84 +
  85 + const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
  86 +
  87 + const mainId = computed(() => (unref(selectedRowKeys).length > 0 ? unref(selectedRowKeys)[0] : ''));
  88 +
  89 + //下发 orderId,子组件接收
  90 + provide('mainId', mainId);
  91 +
  92 + /**
  93 + * 新增事件
  94 + */
  95 + function handleAdd() {
  96 + openModal(true, {
  97 + isUpdate: false,
  98 + showFooter: true,
  99 + });
  100 + }
  101 + /**
  102 + * 编辑事件
  103 + */
  104 + function handleEdit(record: Recordable) {
  105 + openModal(true, {
  106 + record,
  107 + isUpdate: true,
  108 + showFooter: true,
  109 + });
  110 + }
  111 + /**
  112 + * 详情
  113 + */
  114 + function handleDetail(record: Recordable) {
  115 + openModal(true, {
  116 + record,
  117 + isUpdate: true,
  118 + showFooter: false,
  119 + });
  120 + }
  121 + /**
  122 + * 删除事件
  123 + */
  124 + async function handleDelete(record) {
  125 + await deleteOne({id: record.id}, reload);
  126 + }
  127 + /**
  128 + * 批量删除事件
  129 + */
  130 + async function batchHandleDelete() {
  131 + await batchDelete({ids: checkedKeys.value}, reload);
  132 + }
  133 + /**
  134 + * 成功回调
  135 + */
  136 + function handleSuccess() {
  137 + reload();
  138 + }
  139 + /**
  140 + * 操作栏
  141 + */
  142 + function getTableAction(record){
  143 + return [
  144 + {
  145 + label: '编辑',
  146 + onClick: handleEdit.bind(null, record),
  147 + }
  148 + ]
  149 + }
  150 + /**
  151 + * 下拉操作栏
  152 + */
  153 + function getDropDownAction(record){
  154 + return [
  155 + {
  156 + label: '详情',
  157 + onClick: handleDetail.bind(null, record),
  158 + }, {
  159 + label: '删除',
  160 + popConfirm: {
  161 + title: '是否确认删除',
  162 + confirm: handleDelete.bind(null, record),
  163 + }
  164 + }
  165 + ]
  166 + }
  167 +</script>
  168 +
  169 +<style scoped>
  170 +
  171 +</style>
0 172 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__api.tsi 0 → 100644
  1 +import {defHttp} from '/@/utils/http/axios';
  2 +import {Modal} from 'ant-design-vue';
  3 +
  4 +enum Api {
  5 + list = '/${entityPackage}/${entityName?uncap_first}/list',
  6 + save='/${entityPackage}/${entityName?uncap_first}/add',
  7 + edit='/${entityPackage}/${entityName?uncap_first}/edit',
  8 + deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
  9 + deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
  10 + importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
  11 + exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
  12 +<#list subTables as sub><#rt/>
  13 + ${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/list${sub.entityName}ByMainId',
  14 + ${sub.entityName?uncap_first}Save='/${entityPackage}/${entityName?uncap_first}/add${sub.entityName}',
  15 + ${sub.entityName?uncap_first}Edit='/${entityPackage}/${entityName?uncap_first}/edit${sub.entityName}',
  16 + ${sub.entityName?uncap_first}Delete = '/${entityPackage}/${entityName?uncap_first}/delete${sub.entityName}',
  17 + ${sub.entityName?uncap_first}DeleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch${sub.entityName}',
  18 +</#list>
  19 +}
  20 +/**
  21 + * 导出api
  22 + * @param params
  23 + */
  24 +export const getExportUrl = Api.exportXls;
  25 +
  26 +/**
  27 + * 导入api
  28 + */
  29 +export const getImportUrl = Api.importExcel;
  30 +
  31 +/**
  32 + * 列表接口
  33 + * @param params
  34 + */
  35 +export const list = (params) =>
  36 + defHttp.get({url: Api.list, params});
  37 +
  38 +/**
  39 + * 删除单个
  40 + */
  41 +export const deleteOne = (params,handleSuccess) => {
  42 + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
  43 + handleSuccess();
  44 + });
  45 +}
  46 +/**
  47 + * 批量删除
  48 + * @param params
  49 + */
  50 +export const batchDelete = (params, handleSuccess) => {
  51 + Modal.confirm({
  52 + title: '确认删除',
  53 + content: '是否删除选中数据',
  54 + okText: '确认',
  55 + cancelText: '取消',
  56 + onOk: () => {
  57 + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  58 + handleSuccess();
  59 + });
  60 + }
  61 + });
  62 +}
  63 +/**
  64 + * 保存或者更新
  65 + * @param params
  66 + */
  67 +export const saveOrUpdate = (params, isUpdate) => {
  68 + let url = isUpdate ? Api.edit : Api.save;
  69 + return defHttp.post({url: url, params});
  70 +}
  71 +
  72 +<#list subTables as sub><#rt/>
  73 +/**
  74 + * 列表接口
  75 + * @param params
  76 + */
  77 +export const ${sub.entityName?uncap_first}List = (params) =>
  78 + defHttp.get({url: Api.${sub.entityName?uncap_first}List, params});
  79 +
  80 +/**
  81 + * 删除单个
  82 + */
  83 +export const ${sub.entityName?uncap_first}Delete = (params,handleSuccess) => {
  84 + return defHttp.delete({url: Api.${sub.entityName?uncap_first}Delete, params}, {joinParamsToUrl: true}).then(() => {
  85 + handleSuccess();
  86 + });
  87 +}
  88 +/**
  89 + * 批量删除
  90 + * @param params
  91 + */
  92 +export const ${sub.entityName?uncap_first}DeleteBatch = (params, handleSuccess) => {
  93 + Modal.confirm({
  94 + title: '确认删除',
  95 + content: '是否删除选中数据',
  96 + okText: '确认',
  97 + cancelText: '取消',
  98 + onOk: () => {
  99 + return defHttp.delete({url: Api. ${sub.entityName?uncap_first}DeleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
  100 + handleSuccess();
  101 + });
  102 + }
  103 + });
  104 +}
  105 +/**
  106 + * 保存或者更新
  107 + * @param params
  108 + */
  109 +export const ${sub.entityName?uncap_first}Save = (params, isUpdate) => {
  110 + let url = isUpdate ? Api.${sub.entityName?uncap_first}Edit : Api.${sub.entityName?uncap_first}Save;
  111 + return defHttp.post({url: url, params});
  112 +}
  113 +</#list>
0 114 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vue3/${entityName}__data.tsi 0 → 100644
  1 +import {BasicColumn} from '/@/components/Table';
  2 +import {FormSchema} from '/@/components/Table';
  3 +import { rules} from '/@/utils/helper/validator';
  4 +import { render } from '/@/utils/common/renderUtils';
  5 +//列表数据
  6 +export const columns: BasicColumn[] = [
  7 + <#list columns as po>
  8 + <#if po.fieldName !='id'>
  9 + {
  10 + title: '${po.filedComment}',
  11 + align:"center",
  12 + <#if po.classType=='date'>
  13 + dataIndex: '${po.fieldName}',
  14 + customRender:({text}) =>{
  15 + return !text?"":(text.length>10?text.substr(0,10):text)
  16 + },
  17 + <#else>
  18 + dataIndex: '${po.fieldName}'
  19 + </#if>
  20 + },
  21 + </#if>
  22 + </#list>
  23 +];
  24 +//查询数据
  25 +export const searchFormSchema: FormSchema[] = [
  26 +<#list columns as po>
  27 +<#if po.fieldName !='id' && po_index<= tableVo.searchFieldNum>
  28 + {
  29 + label: '${po.filedComment}',
  30 + field: '${po.fieldName}',
  31 + <#if po.fieldType =='date'>
  32 + component: 'DatePicker'
  33 + <#elseif po.fieldType =='datetime'>
  34 + component: 'TimePicker'
  35 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  36 + component: 'InputNumber'
  37 + <#else>
  38 + component: 'Input'
  39 + </#if>
  40 + },
  41 +</#if>
  42 +</#list>
  43 +];
  44 +
  45 +export const formSchema: FormSchema[] = [
  46 +<#list columns as po><#rt/>
  47 + {
  48 + label: '${po.filedComment}',
  49 + field: '${po.fieldName}',
  50 + <#if po.fieldType =='date'>
  51 + component: 'DatePicker'
  52 + <#elseif po.fieldType =='datetime'>
  53 + component: 'TimePicker'
  54 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  55 + component: 'InputNumber'
  56 + <#else>
  57 + component: 'Input'
  58 + </#if>
  59 + <#if po.fieldName =='id'><#rt/>
  60 + show:false
  61 + </#if>
  62 + },
  63 +</#list>
  64 +];
  65 +
  66 +//子表表格配置
  67 +<#list subTables as sub>
  68 +//列表数据
  69 +export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
  70 + <#list sub.colums as po><#rt/>
  71 + <#if po.fieldName !='id' && sub.foreignKeys[0]?uncap_first != po.fieldName>
  72 + {
  73 + title: '${po.filedComment}',
  74 + align:"center",
  75 + <#if po.classType=='date'>
  76 + dataIndex: '${po.fieldName}',
  77 + customRender:({text}) =>{
  78 + return !text?"":(text.length>10?text.substr(0,10):text)
  79 + },
  80 + <#else>
  81 + dataIndex: '${po.fieldName}'
  82 + </#if>
  83 + },
  84 + </#if>
  85 + </#list>
  86 +];
  87 +
  88 +export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
  89 +<#-- 循环子表的列 开始 -->
  90 +<#list sub.colums as po><#rt/>
  91 + {
  92 + label: '${po.filedComment}',
  93 + field: '${po.fieldName}',
  94 + <#if po.fieldType =='date'>
  95 + component: 'DatePicker'
  96 + <#elseif po.fieldType =='datetime'>
  97 + component: 'TimePicker'
  98 + <#elseif "int,decimal,double,"?contains(po.fieldType)>
  99 + component: 'InputNumber'
  100 + <#else>
  101 + component: 'Input'
  102 + </#if>
  103 + <#if po.fieldName =='id'><#rt/>
  104 + show:false
  105 + </#if>
  106 + },
  107 +</#list>
  108 +<#-- 循环子表的列 结束 -->
  109 + ]
  110 +</#list>
0 111 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vue3/[1-n]List.vuei 0 → 100644
  1 +<#list subTables as subTab>
  2 +#segment#${subTab.entityName}List.vue
  3 +<template>
  4 + <div>
  5 + <!--table区域-begin-->
  6 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  7 + <!--插槽:table标题-->
  8 + <template #tableTitle>
  9 + <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  10 + <a-dropdown v-if="selectedRowKeys.length > 0">
  11 + <template #overlay>
  12 + <a-menu>
  13 + <a-menu-item key="1" @click="batchHandleDelete">
  14 + <Icon icon="ant-design:delete-outlined"></Icon>
  15 + 删除
  16 + </a-menu-item>
  17 + </a-menu>
  18 + </template>
  19 + <a-button>批量操作
  20 + <Icon icon="mdi:chevron-down"></Icon>
  21 + </a-button>
  22 + </a-dropdown>
  23 + </template>
  24 + <!--操作栏-->
  25 + <template #action="{ record }">
  26 + <TableAction :actions="getTableAction(record)"/>
  27 + </template>
  28 + </BasicTable>
  29 + <!-- table区域-end -->
  30 +
  31 + <!-- 表单区域 -->
  32 + <${subTab.entityName}Modal @register="registerModal" @success="handleSuccess"/>
  33 + </div>
  34 +</template>
  35 +
  36 +<script lang="ts" setup>
  37 + import {ref, computed, unref, watch, inject} from 'vue';
  38 + import ${subTab.entityName}Modal from './modules/${subTab.entityName}Modal.vue'
  39 + import {BasicTable, useTable, TableAction} from '/@/components/Table';
  40 + import { useListPage } from '/@/hooks/system/useListPage'
  41 + import {useModal} from '/@/components/Modal';
  42 + import {isEmpty} from "/@/utils/is";
  43 + import {useMessage} from '/@/hooks/web/useMessage';
  44 + import {${subTab.entityName?uncap_first}Columns} from './${entityName?uncap_first}.data';
  45 + import {${subTab.entityName?uncap_first}List, ${subTab.entityName?uncap_first}Delete, ${subTab.entityName?uncap_first}DeleteBatch} from './${entityName?uncap_first}.api';
  46 + //提示弹窗
  47 + const $message = useMessage()
  48 + //接收主表id
  49 + const mainId = inject('mainId') || '';
  50 + //查询参数
  51 + const searchInfo = ref({});
  52 + //注册model
  53 + const [registerModal, {openModal}] = useModal();
  54 + // 列表页面公共参数、方法
  55 + const {prefixCls, tableContext} = useListPage({
  56 + tableProps: {
  57 + api: ${subTab.entityName?uncap_first}List,
  58 + columns: ${subTab.entityName?uncap_first}Columns,
  59 + canResize: false,
  60 + useSearchForm: false,
  61 + searchInfo,
  62 + actionColumn: {
  63 + width: 180,
  64 + }
  65 + },
  66 + });
  67 +
  68 + //注册table数据
  69 + const [registerTable, {reload}, {rowSelection, selectedRowKeys}] = tableContext;
  70 +
  71 + watch(mainId, () => {
  72 + searchInfo.value['${subTab.foreignKeys[0]?uncap_first}'] = unref(mainId);
  73 + reload();
  74 + }
  75 + );
  76 +
  77 + /**
  78 + * 新增事件
  79 + */
  80 + function handleAdd() {
  81 + if (isEmpty(unref(mainId))) {
  82 + $message.createMessage.warning('请选择一个主表信息')
  83 + return;
  84 + }
  85 + openModal(true, {
  86 + isUpdate: false,
  87 + showFooter: true,
  88 + });
  89 + }
  90 +
  91 + /**
  92 + * 编辑事件
  93 + */
  94 + async function handleEdit(record: Recordable) {
  95 + openModal(true, {
  96 + record,
  97 + isUpdate: true,
  98 + showFooter: true,
  99 + });
  100 + }
  101 +
  102 + /**
  103 + * 删除事件
  104 + */
  105 + async function handleDelete(record) {
  106 + await ${subTab.entityName?uncap_first}Delete({id: record.id}, reload);
  107 + }
  108 +
  109 + /**
  110 + * 批量删除事件
  111 + */
  112 + async function batchHandleDelete() {
  113 + await ${subTab.entityName?uncap_first}DeleteBatch({ids: selectedRowKeys.value}, () => {
  114 + selectedRowKeys.value = []
  115 + reload()
  116 + })
  117 + }
  118 +
  119 + /**
  120 + * 成功回调
  121 + */
  122 + function handleSuccess() {
  123 + reload();
  124 + }
  125 +
  126 + /**
  127 + * 操作栏
  128 + */
  129 + function getTableAction(record) {
  130 + return [
  131 + {
  132 + label: '编辑',
  133 + onClick: handleEdit.bind(null, record),
  134 + }, {
  135 + label: '删除',
  136 + popConfirm: {
  137 + title: '是否确认删除',
  138 + confirm: handleDelete.bind(null, record),
  139 + },
  140 + }
  141 + ]
  142 + }
  143 +</script>
  144 +<style scoped>
  145 +
  146 +</style>
  147 +</#list>
0 148 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vue3/modules/${entityName}Modal.vuei 0 → 100644
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="40%">
  3 + <BasicForm @register="registerForm"/>
  4 + </BasicModal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import {ref, computed, unref} from 'vue';
  9 + import {BasicModal, useModalInner} from '/@/components/Modal';
  10 + import {BasicForm, useForm} from '/@/components/Form/index';
  11 + import {formSchema} from '../${entityName?uncap_first}.data';
  12 + import {saveOrUpdate} from '../${entityName?uncap_first}.api';
  13 + // Emits声明
  14 + const emit = defineEmits(['register','success']);
  15 + const isUpdate = ref(true);
  16 + //表单配置
  17 + const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
  18 + labelWidth: 150,
  19 + schemas: formSchema,
  20 + showActionButtonGroup: false,
  21 + });
  22 + //表单赋值
  23 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  24 + //重置表单
  25 + await resetFields();
  26 + setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
  27 + isUpdate.value = !!data?.isUpdate;
  28 + if (unref(isUpdate)) {
  29 + //表单赋值
  30 + await setFieldsValue({
  31 + ...data.record,
  32 + });
  33 + }
  34 + });
  35 + //设置标题
  36 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  37 + //表单提交事件
  38 + async function handleSubmit(v) {
  39 + try {
  40 + let values = await validate();
  41 + setModalProps({confirmLoading: true});
  42 + //提交表单
  43 + await saveOrUpdate(values, isUpdate.value);
  44 + //关闭弹窗
  45 + closeModal();
  46 + //刷新列表
  47 + emit('success');
  48 + } finally {
  49 + setModalProps({confirmLoading: false});
  50 + }
  51 + }
  52 +</script>
  53 +
  54 +<style lang="less" scoped>
  55 +
  56 +</style>
0 57 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vue3/modules/[1-n]Modal.vuei 0 → 100644
  1 +<#list subTables as subTab>
  2 +#segment#${subTab.entityName}Modal.vue
  3 +<template>
  4 + <BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit" width="40%">
  5 + <BasicForm @register="registerForm"/>
  6 + </BasicModal>
  7 +</template>
  8 +
  9 +<script lang="ts" setup>
  10 + import {ref, computed, unref,inject} from 'vue';
  11 + import {BasicModal, useModalInner} from '/@/components/Modal';
  12 + import {BasicForm, useForm} from '/@/components/Form/index';
  13 + import {${subTab.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
  14 + import {${subTab.entityName?uncap_first}Save} from '../${entityName?uncap_first}.api';
  15 + // Emits声明
  16 + const emit = defineEmits(['register','success']);
  17 + //接收主表id
  18 + const mainId = inject('mainId');
  19 + const isUpdate = ref(true);
  20 + //表单配置
  21 + const [registerForm, {resetFields, setFieldsValue, validate}] = useForm({
  22 + labelWidth: 150,
  23 + schemas: ${subTab.entityName?uncap_first}FormSchema,
  24 + showActionButtonGroup: false,
  25 + });
  26 + //表单赋值
  27 + const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
  28 + //重置表单
  29 + await resetFields();
  30 + setModalProps({confirmLoading: false});
  31 + isUpdate.value = !!data?.isUpdate;
  32 + if (unref(isUpdate)) {
  33 + //表单赋值
  34 + await setFieldsValue({
  35 + ...data.record,
  36 + });
  37 + }
  38 + });
  39 + //设置标题
  40 + const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
  41 + //表单提交事件
  42 + async function handleSubmit(v) {
  43 + try {
  44 + let values = await validate();
  45 + setModalProps({confirmLoading: true});
  46 + if (unref(mainId)) {
  47 + values['${subTab.foreignKeys[0]?uncap_first}'] = unref(mainId);
  48 + }
  49 + //提交表单
  50 + await ${subTab.entityName?uncap_first}Save(values, isUpdate.value);
  51 + //关闭弹窗
  52 + closeModal();
  53 + //刷新列表
  54 + emit('success');
  55 + } finally {
  56 + setModalProps({confirmLoading: false});
  57 + }
  58 + }
  59 +</script>
  60 +
  61 +<style scoped>
  62 +
  63 +</style>
  64 +</#list>
0 65 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-module-system/src/main/resources/templates/announcement/showContent.ftl
... ... @@ -129,7 +129,7 @@
129 129 </div>
130 130 </div>
131 131 <div>
132   - ${data.msgContent}
  132 + ${data.msgContent!''}
133 133 </div>
134 134 </body>
135 135 </html>
136 136 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-starter</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11 <artifactId>jeecg-boot-starter-cloud</artifactId>
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-cloud/src/main/resources/bootstrap.yml
... ... @@ -9,9 +9,9 @@ spring:
9 9 nacos:
10 10 config:
11 11 # Nacos 认证用户
12   - username: nacos
  12 + username: @config.username@
13 13 # Nacos 认证密码
14   - password: nacos
  14 + password: @config.password@
15 15 # 命名空间 常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等
16 16 namespace: @config.namespace@
17 17 # 配置中心地址
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-job/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-starter</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11 <artifactId>jeecg-boot-starter-job</artifactId>
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-lock/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-starter</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11 <artifactId>jeecg-boot-starter-lock</artifactId>
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-lock/src/main/java/org/jeecg/boot/starter/lock/aspect/DistributedLockHandler.java
... ... @@ -94,9 +94,6 @@ public class DistributedLockHandler extends BaseAspect{
94 94 Object[] args = joinPoint.getArgs();
95 95  
96 96 LockModel lockModel = jLock.lockModel();
97   - if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {
98   - throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
99   - }
100 97 RLock rLock = null;
101 98 String keyConstant = jLock.keyConstant();
102 99 if (lockModel.equals(LockModel.AUTO)) {
... ... @@ -106,6 +103,9 @@ public class DistributedLockHandler extends BaseAspect{
106 103 lockModel = LockModel.REENTRANT;
107 104 }
108 105 }
  106 + if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {
  107 + throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");
  108 + }
109 109 switch (lockModel) {
110 110 case FAIR:
111 111 rLock = redissonClient.getFairLock(getValueBySpEL(keys[0], parameterNames, args, keyConstant).get(0));
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-rabbitmq/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-starter</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11 <artifactId>jeecg-boot-starter-rabbitmq</artifactId>
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-seata/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>jeecg-boot-starter</artifactId>
  7 + <groupId>org.jeecgframework.boot</groupId>
  8 + <version>3.1.0</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>jeecg-boot-starter-seata</artifactId>
  12 + <description>分布式事务</description>
  13 + <dependencies>
  14 + <!-- seata依赖 -->
  15 + <dependency>
  16 + <groupId>io.seata</groupId>
  17 + <artifactId>seata-spring-boot-starter</artifactId>
  18 + <version>1.4.2</version>
  19 + </dependency>
  20 + <dependency>
  21 + <groupId>com.alibaba.nacos</groupId>
  22 + <artifactId>nacos-client</artifactId>
  23 + <version>1.3.3</version>
  24 + </dependency>
  25 +
  26 + </dependencies>
  27 +</project>
0 28 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-shardingsphere/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>jeecg-boot-starter</artifactId>
  7 + <groupId>org.jeecgframework.boot</groupId>
  8 + <version>3.1.0</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>jeecg-boot-starter-shardingsphere</artifactId>
  12 + <description>分库分表</description>
  13 + <dependencies>
  14 + <!-- 动态数据源 -->
  15 + <dependency>
  16 + <groupId>com.baomidou</groupId>
  17 + <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  18 + <version>${dynamic-datasource-spring-boot-starter.version}</version>
  19 + </dependency>
  20 + <!-- 分库分表 -->
  21 + <dependency>
  22 + <groupId>org.apache.shardingsphere</groupId>
  23 + <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
  24 + <version>5.0.0</version>
  25 + </dependency>
  26 + </dependencies>
  27 +
  28 +</project>
0 29 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-shardingsphere/src/main/java/org/jeecg/boot/shardingsphere/config/DataSourceConfiguration.java 0 → 100644
  1 +package org.jeecg.boot.shardingsphere.config;
  2 +
  3 +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
  4 +import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
  5 +import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
  6 +import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
  7 +import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
  8 +import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
  9 +import org.springframework.boot.SpringBootConfiguration;
  10 +import org.springframework.boot.autoconfigure.AutoConfigureBefore;
  11 +import org.springframework.context.annotation.Bean;
  12 +import org.springframework.context.annotation.Configuration;
  13 +import org.springframework.context.annotation.Lazy;
  14 +import org.springframework.context.annotation.Primary;
  15 +
  16 +import javax.annotation.Resource;
  17 +import javax.sql.DataSource;
  18 +import java.util.Map;
  19 +
  20 +/**
  21 + * 分库分表数据源配置
  22 + * @author zyf
  23 + */
  24 +@Configuration
  25 +@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
  26 +public class DataSourceConfiguration {
  27 + /**
  28 + * 分表数据源名称
  29 + */
  30 + public static final String SHARDING_DATA_SOURCE_NAME = "sharding";
  31 + /**
  32 + * 动态数据源配置项
  33 + */
  34 + @Resource
  35 + private DynamicDataSourceProperties dynamicDataSourceProperties;
  36 +
  37 + @Lazy
  38 + @Resource
  39 + DataSource shardingDataSource;
  40 +
  41 + /**
  42 + * 将shardingDataSource放到了多数据源(dataSourceMap)中
  43 + * 注意有个版本的bug,3.1.1版本 不会进入loadDataSources 方法,这样就一直造成数据源注册失败
  44 + */
  45 + @Bean
  46 + public DynamicDataSourceProvider dynamicDataSourceProvider() {
  47 + Map<String, DataSourceProperty> datasourceMap = dynamicDataSourceProperties.getDatasource();
  48 + return new AbstractDataSourceProvider() {
  49 + @Override
  50 + public Map<String, DataSource> loadDataSources() {
  51 + Map<String, DataSource> dataSourceMap = createDataSourceMap(datasourceMap);
  52 + // 将 shardingjdbc 管理的数据源也交给动态数据源管理
  53 + dataSourceMap.put(SHARDING_DATA_SOURCE_NAME, shardingDataSource);
  54 + return dataSourceMap;
  55 + }
  56 + };
  57 + }
  58 +
  59 + /**
  60 + * 将动态数据源设置为首选的
  61 + * 当spring存在多个数据源时, 自动注入的是首选的对象
  62 + * 设置为主要的数据源之后,就可以支持shardingjdbc原生的配置方式了
  63 + *
  64 + * @return
  65 + */
  66 + @Primary
  67 + @Bean
  68 + public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
  69 + DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
  70 + dataSource.setPrimary(dynamicDataSourceProperties.getPrimary());
  71 + dataSource.setStrict(dynamicDataSourceProperties.getStrict());
  72 + dataSource.setStrategy(dynamicDataSourceProperties.getStrategy());
  73 + dataSource.setProvider(dynamicDataSourceProvider);
  74 + dataSource.setP6spy(dynamicDataSourceProperties.getP6spy());
  75 + dataSource.setSeata(dynamicDataSourceProperties.getSeata());
  76 + return dataSource;
  77 + }
  78 +}
... ...
jeecg-boot/jeecg-boot-starter/jeecg-boot-starter-shardingsphere/src/main/resources/application.yml 0 → 100644
  1 +spring:
  2 + profiles:
  3 + active: sharding
0 4 \ No newline at end of file
... ...
jeecg-boot/jeecg-boot-starter/pom.xml
... ... @@ -4,7 +4,7 @@
4 4 <parent>
5 5 <groupId>org.jeecgframework.boot</groupId>
6 6 <artifactId>jeecg-boot-parent</artifactId>
7   - <version>3.0</version>
  7 + <version>3.1.0</version>
8 8 </parent>
9 9 <modelVersion>4.0.0</modelVersion>
10 10 <artifactId>jeecg-boot-starter</artifactId>
... ... @@ -20,6 +20,8 @@
20 20 <module>jeecg-boot-starter-job</module>
21 21 <module>jeecg-boot-starter-lock</module>
22 22 <module>jeecg-boot-starter-rabbitmq</module>
  23 + <module>jeecg-boot-starter-shardingsphere</module>
  24 + <module>jeecg-boot-starter-seata</module>
23 25 </modules>
24 26 <dependencies>
25 27 <!--jeecg-tools-->
... ... @@ -38,4 +40,93 @@
38 40 <optional>true</optional>
39 41 </dependency>
40 42 </dependencies>
  43 +
  44 +
  45 + <build>
  46 + <plugins>
  47 + <plugin>
  48 + <artifactId>maven-compiler-plugin</artifactId>
  49 + <configuration>
  50 + <source>1.8</source>
  51 + <target>1.8</target>
  52 + <encoding>UTF-8</encoding>
  53 + </configuration>
  54 + </plugin>
  55 + <plugin>
  56 + <groupId>org.apache.maven.plugins</groupId>
  57 + <artifactId>maven-surefire-plugin</artifactId>
  58 + <configuration>
  59 + <testFailureIgnore>true</testFailureIgnore>
  60 + </configuration>
  61 + </plugin>
  62 + <!--生成Source jar文件-->
  63 + <plugin>
  64 + <groupId>org.apache.maven.plugins</groupId>
  65 + <artifactId>maven-source-plugin</artifactId>
  66 + <version>2.2.1</version>
  67 + <executions>
  68 + <execution>
  69 + <id>attach-sources</id>
  70 + <goals>
  71 + <goal>jar-no-fork</goal>
  72 + </goals>
  73 + </execution>
  74 + </executions>
  75 + </plugin>
  76 + <!--生成Javadoc,关闭doclint,避免注解检查不通过-->
  77 + <plugin>
  78 + <groupId>org.apache.maven.plugins</groupId>
  79 + <artifactId>maven-javadoc-plugin</artifactId>
  80 + <version>2.9.1</version>
  81 + <executions>
  82 + <execution>
  83 + <id>attach-javadocs</id>
  84 + <goals>
  85 + <goal>jar</goal>
  86 + </goals>
  87 + <configuration>
  88 + <additionalparam>-Xdoclint:none</additionalparam>
  89 + </configuration>
  90 + </execution>
  91 + </executions>
  92 + </plugin>
  93 + <!--Maven GPG插件用于使用以下配置对组件进行签名-->
  94 + <plugin>
  95 + <groupId>org.apache.maven.plugins</groupId>
  96 + <artifactId>maven-gpg-plugin</artifactId>
  97 + <version>1.6</version>
  98 + <executions>
  99 + <execution>
  100 + <id>sign-artifacts</id>
  101 + <phase>verify</phase>
  102 + <goals>
  103 + <goal>sign</goal>
  104 + </goals>
  105 + </execution>
  106 + </executions>
  107 + </plugin>
  108 + <!--Nexus Staging Maven插件是将组件部署到OSS并将其发布到Central Repository的推荐方法-->
  109 + <plugin>
  110 + <groupId>org.sonatype.plugins</groupId>
  111 + <artifactId>nexus-staging-maven-plugin</artifactId>
  112 + <version>1.6.7</version>
  113 + <extensions>true</extensions>
  114 + <configuration>
  115 + <serverId>oss</serverId>
  116 + <nexusUrl>https://oss.sonatype.org/</nexusUrl>
  117 + <autoReleaseAfterClose>true</autoReleaseAfterClose>
  118 + </configuration>
  119 + </plugin>
  120 + </plugins>
  121 + </build>
  122 + <distributionManagement>
  123 + <snapshotRepository>
  124 + <id>oss</id>
  125 + <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
  126 + </snapshotRepository>
  127 + <repository>
  128 + <id>oss</id>
  129 + <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
  130 + </repository>
  131 + </distributionManagement>
41 132 </project>
42 133 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-gateway/Dockerfile
... ... @@ -10,6 +10,6 @@ WORKDIR /jeecg-cloud-gateway
10 10  
11 11 EXPOSE 9999
12 12  
13   -ADD ./target/jeecg-cloud-gateway-3.0.jar ./
  13 +ADD ./target/jeecg-cloud-gateway-3.1.0.jar ./
14 14  
15   -CMD sleep 10;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-gateway-3.0.jar
16 15 \ No newline at end of file
  16 +CMD sleep 10;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-gateway-3.1.0.jar
17 17 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-gateway/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-cloud-module</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-gateway/src/main/resources/application.yml
... ... @@ -65,4 +65,4 @@ hystrix:
65 65 isolation:
66 66 strategy: SEMAPHORE
67 67 thread:
68   - timeoutInMilliseconds: 3000
69 68 \ No newline at end of file
  69 + timeoutInMilliseconds: 9000
70 70 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-monitor/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-cloud-module</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11 <artifactId>jeecg-cloud-monitor</artifactId>
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-nacos/Dockerfile
... ... @@ -10,6 +10,6 @@ WORKDIR /jeecg-cloud-nacos
10 10  
11 11 EXPOSE 8848
12 12  
13   -ADD ./target/jeecg-cloud-nacos-3.0.jar ./
  13 +ADD ./target/jeecg-cloud-nacos-3.1.0.jar ./
14 14  
15   -CMD sleep 5;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-nacos-3.0.jar
16 15 \ No newline at end of file
  16 +CMD sleep 5;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-nacos-3.1.0.jar
17 17 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-nacos/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-cloud-module</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <artifactId>jeecg-cloud-nacos</artifactId>
11 11 <name>jeecg-cloud-nacos</name>
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-sentinel/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-cloud-module</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <artifactId>jeecg-cloud-sentinel</artifactId>
11 11 <name>jeecg-cloud-sentinel</name>
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-system-start/Dockerfile
... ... @@ -10,6 +10,6 @@ WORKDIR /jeecg-cloud-system
10 10  
11 11 EXPOSE 7001
12 12  
13   -ADD ./target/jeecg-cloud-system-start-3.0.jar ./
  13 +ADD ./target/jeecg-cloud-system-start-3.1.0.jar ./
14 14  
15   -CMD sleep 10;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-system-start-3.0.jar
16 15 \ No newline at end of file
  16 +CMD sleep 10;java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-system-start-3.1.0.jar
17 17 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-system-start/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-cloud-module</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11 <artifactId>jeecg-cloud-system-start</artifactId>
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>jeecg-cloud-test</artifactId>
  7 + <groupId>org.jeecgframework.boot</groupId>
  8 + <version>3.1.0</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <description>分布式事务测试模块</description>
  12 + <artifactId>jeecg-cloud-test-seata</artifactId>
  13 +
  14 + <dependencies>
  15 + <dependency>
  16 + <groupId>org.jeecgframework.boot</groupId>
  17 + <artifactId>jeecg-boot-starter-seata</artifactId>
  18 + <version>3.1.0</version>
  19 + </dependency>
  20 + </dependencies>
  21 +
  22 +</project>
0 23 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/controller/SeataOrderController.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.controller;
  2 +
  3 +/**
  4 + * @Description: TODO
  5 + * @author: zyf
  6 + * @date: 2022/01/24
  7 + * @version: V1.0
  8 + */
  9 +import io.swagger.annotations.Api;
  10 +import io.swagger.annotations.ApiOperation;
  11 +import org.jeecg.modules.test.seata.dto.PlaceOrderRequest;
  12 +import org.jeecg.modules.test.seata.service.SeataOrderService;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.validation.annotation.Validated;
  15 +import org.springframework.web.bind.annotation.PostMapping;
  16 +import org.springframework.web.bind.annotation.RequestBody;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +
  20 +@RestController
  21 +@RequestMapping("/order")
  22 +@Api(tags = "seata测试")
  23 +public class SeataOrderController {
  24 +
  25 + @Autowired
  26 + private SeataOrderService orderService;
  27 +
  28 + /**
  29 + * 自由下单
  30 + */
  31 + @PostMapping("/placeOrder")
  32 + @ApiOperation(value = "自由下单", notes = "自由下单")
  33 + public String placeOrder(@Validated @RequestBody PlaceOrderRequest request) {
  34 + orderService.placeOrder(request);
  35 + return "下单成功";
  36 + }
  37 +
  38 + /**
  39 + * 测试商品库存不足-异常回滚
  40 + */
  41 + @PostMapping("/test1")
  42 + @ApiOperation(value = "测试商品库存不足", notes = "测试商品库存不足")
  43 + public String test1() {
  44 + //商品单价10元,库存20个,用户余额50元,模拟一次性购买22个。 期望异常回滚
  45 + orderService.placeOrder(new PlaceOrderRequest(1L, 1L, 22));
  46 + return "下单成功";
  47 + }
  48 +
  49 + /**
  50 + * 测试用户账户余额不足-异常回滚
  51 + */
  52 + @PostMapping("/test2")
  53 + @ApiOperation(value = "测试用户账户余额不足", notes = "测试用户账户余额不足")
  54 + public String test2() {
  55 + //商品单价10元,库存20个,用户余额50元,模拟一次性购买6个。 期望异常回滚
  56 + orderService.placeOrder(new PlaceOrderRequest(1L, 1L, 6));
  57 + return "下单成功";
  58 + }
  59 +}
0 60 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/dto/PlaceOrderRequest.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.dto;
  2 +import lombok.AllArgsConstructor;
  3 +import lombok.Builder;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +/**
  9 + * @Description: 订单请求对象
  10 + * @author: zyf
  11 + * @date: 2022/01/24
  12 + * @version: V1.0
  13 + */
  14 +@Data
  15 +@Builder
  16 +@AllArgsConstructor
  17 +@NoArgsConstructor
  18 +public class PlaceOrderRequest {
  19 +
  20 + @NotNull
  21 + private Long userId;
  22 +
  23 + @NotNull
  24 + private Long productId;
  25 +
  26 + @NotNull
  27 + private Integer amount;
  28 +}
0 29 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/dto/ReduceBalanceRequest.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.dto;
  2 +import lombok.AllArgsConstructor;
  3 +import lombok.Builder;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * @Description: 余额请求对象
  9 + * @author: zyf
  10 + * @date: 2022/01/24
  11 + * @version: V1.0
  12 + */
  13 +@Data
  14 +@Builder
  15 +@AllArgsConstructor
  16 +@NoArgsConstructor
  17 +public class ReduceBalanceRequest {
  18 +
  19 + private Long userId;
  20 + private Integer price;
  21 +}
0 22 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/dto/ReduceStockRequest.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.dto;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Builder;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +/**
  8 + * @Description: 库存请求对象
  9 + * @author: zyf
  10 + * @date: 2022/01/24
  11 + * @version: V1.0
  12 + */
  13 +@Data
  14 +@Builder
  15 +@AllArgsConstructor
  16 +@NoArgsConstructor
  17 +public class ReduceStockRequest {
  18 +
  19 + private Long productId;
  20 + private Integer amount;
  21 +}
0 22 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/entity/SeataAccount.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.entity;
  2 +
  3 +import com.baomidou.mybatisplus.annotation.IdType;
  4 +import com.baomidou.mybatisplus.annotation.TableId;
  5 +import com.baomidou.mybatisplus.annotation.TableName;
  6 +import lombok.Builder;
  7 +import lombok.Data;
  8 +
  9 +import java.util.Date;
  10 +
  11 +/**
  12 + * @Description: 账户
  13 + * @author: zyf
  14 + * @date: 2022/01/24
  15 + * @version: V1.0
  16 + */
  17 +@Data
  18 +@Builder
  19 +@TableName("account")
  20 +public class SeataAccount {
  21 + @TableId(type = IdType.AUTO)
  22 + private Long id;
  23 +
  24 + /**
  25 + * 余额
  26 + */
  27 + private Double balance;
  28 +
  29 + private Date lastUpdateTime;
  30 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/entity/SeataOrder.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.entity;
  2 +
  3 +import com.baomidou.mybatisplus.annotation.IdType;
  4 +import com.baomidou.mybatisplus.annotation.TableId;
  5 +import com.baomidou.mybatisplus.annotation.TableName;
  6 +import lombok.Builder;
  7 +import lombok.Data;
  8 +import org.jeecg.modules.test.seata.enums.OrderStatus;
  9 +
  10 +/**
  11 + * @Description: 订单
  12 + * @author: zyf
  13 + * @date: 2022/01/24
  14 + * @version: V1.0
  15 + */
  16 +@Builder
  17 +@Data
  18 +@TableName("p_order")
  19 +public class SeataOrder {
  20 +
  21 + @TableId(type = IdType.AUTO)
  22 + private Integer id;
  23 +
  24 + /**
  25 + * 用户ID
  26 + */
  27 + private Long userId;
  28 + /**
  29 + * 商品ID
  30 + */
  31 + private Long productId;
  32 + /**
  33 + * 订单状态
  34 + */
  35 + private OrderStatus status;
  36 + /**
  37 + * 数量
  38 + */
  39 + private Integer amount;
  40 + /**
  41 + * 总金额
  42 + */
  43 + private Double totalPrice;
  44 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/entity/SeataProduct.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.entity;
  2 +
  3 +import com.baomidou.mybatisplus.annotation.IdType;
  4 +import com.baomidou.mybatisplus.annotation.TableId;
  5 +import com.baomidou.mybatisplus.annotation.TableName;
  6 +import lombok.Builder;
  7 +import lombok.Data;
  8 +
  9 +import java.util.Date;
  10 +/**
  11 + * @Description: 产品
  12 + * @author: zyf
  13 + * @date: 2022/01/24
  14 + * @version: V1.0
  15 + */
  16 +@Data
  17 +@Builder
  18 +@TableName("product")
  19 +public class SeataProduct {
  20 +
  21 + @TableId(type = IdType.AUTO)
  22 + private Integer id;
  23 + /**
  24 + * 价格
  25 + */
  26 + private Double price;
  27 + /**
  28 + * 库存
  29 + */
  30 + private Integer stock;
  31 +
  32 + private Date lastUpdateTime;
  33 +}
0 34 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/enums/OrderStatus.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.enums;
  2 +
  3 +/**
  4 + * @Description: 订单状态
  5 + * @author: zyf
  6 + * @date: 2022/01/24
  7 + * @version: V1.0
  8 + */
  9 +public enum OrderStatus {
  10 + /**
  11 + * INIT
  12 + */
  13 + INIT,
  14 + /**
  15 + * SUCCESS
  16 + */
  17 + SUCCESS,
  18 + /**
  19 + * FAIL
  20 + */
  21 + FAIL
  22 +}
0 23 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/mapper/SeataAccountMapper.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.mapper;
  2 +
  3 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4 +import org.apache.ibatis.annotations.Mapper;
  5 +import org.jeecg.modules.test.seata.entity.SeataAccount;
  6 +
  7 +/**
  8 + * @Description: TODO
  9 + * @author: zyf
  10 + * @date: 2022/01/24
  11 + * @version: V1.0
  12 + */
  13 +@Mapper
  14 +public interface SeataAccountMapper extends BaseMapper<SeataAccount> {
  15 +
  16 +}
0 17 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/mapper/SeataOrderMapper.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.mapper;
  2 +
  3 +/**
  4 + * @Description: TODO
  5 + * @author: zyf
  6 + * @date: 2022/01/24
  7 + * @version: V1.0
  8 + */
  9 +
  10 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  11 +import org.apache.ibatis.annotations.Mapper;
  12 +import org.jeecg.modules.test.seata.entity.SeataOrder;
  13 +
  14 +@Mapper
  15 +public interface SeataOrderMapper extends BaseMapper<SeataOrder> {
  16 +
  17 +}
0 18 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/mapper/SeataProductMapper.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.mapper;
  2 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3 +import org.apache.ibatis.annotations.Mapper;
  4 +import org.jeecg.modules.test.seata.entity.SeataProduct;
  5 +
  6 +/**
  7 + * @Description: TODO
  8 + * @author: zyf
  9 + * @date: 2022/01/24
  10 + * @version: V1.0
  11 + */
  12 +@Mapper
  13 +public interface SeataProductMapper extends BaseMapper<SeataProduct> {
  14 +
  15 +}
0 16 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/service/SeataAccountService.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.service;
  2 +
  3 +/**
  4 + * @Description: 账户接口
  5 + * @author: zyf
  6 + * @date: 2022/01/24
  7 + * @version: V1.0
  8 + */
  9 +public interface SeataAccountService {
  10 + /**
  11 + * @param userId 用户 ID
  12 + * @param price 扣减金额
  13 + */
  14 + void reduceBalance(Long userId, Double price);
  15 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/service/SeataOrderService.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.service;
  2 +
  3 +import org.jeecg.modules.test.seata.dto.PlaceOrderRequest;
  4 +
  5 +/**
  6 + * @Description: 订单接口
  7 + * @author: zyf
  8 + * @date: 2022/01/24
  9 + * @version: V1.0
  10 + */
  11 +public interface SeataOrderService {
  12 + /**
  13 + * 下单
  14 + *
  15 + * @param placeOrderRequest 订单请求参数
  16 + */
  17 + void placeOrder(PlaceOrderRequest placeOrderRequest);
  18 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/service/SeataProductService.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.service;
  2 +
  3 +/**
  4 + * @Description: 产品接口
  5 + * @author: zyf
  6 + * @date: 2022/01/24
  7 + * @version: V1.0
  8 + */
  9 +public interface SeataProductService {
  10 + /**
  11 + * 扣减库存
  12 + *
  13 + * @param productId 商品 ID
  14 + * @param amount 扣减数量
  15 + * @return 商品总价
  16 + */
  17 + Double reduceStock(Long productId, Integer amount);
  18 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/service/impl/SeataAccountServiceImpl.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.service.impl;
  2 +
  3 +
  4 +import com.baomidou.dynamic.datasource.annotation.DS;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.jeecg.modules.test.seata.entity.SeataAccount;
  7 +import org.jeecg.modules.test.seata.mapper.SeataAccountMapper;
  8 +import org.jeecg.modules.test.seata.service.SeataAccountService;
  9 +import org.springframework.stereotype.Service;
  10 +import org.springframework.transaction.annotation.Propagation;
  11 +import org.springframework.transaction.annotation.Transactional;
  12 +import org.springframework.util.Assert;
  13 +
  14 +import javax.annotation.Resource;
  15 +
  16 +/**
  17 + * @Description: TODO
  18 + * @author: zyf
  19 + * @date: 2022/01/24
  20 + * @version: V1.0
  21 + */
  22 +@Slf4j
  23 +@Service
  24 +public class SeataAccountServiceImpl implements SeataAccountService {
  25 + @Resource
  26 + private SeataAccountMapper accountMapper;
  27 +
  28 + /**
  29 + * 事务传播特性设置为 REQUIRES_NEW 开启新的事务
  30 + */
  31 + @DS("account")
  32 + @Override
  33 + @Transactional(propagation = Propagation.REQUIRES_NEW)
  34 + public void reduceBalance(Long userId, Double price) {
  35 + log.info("=============ACCOUNT START=================");
  36 + SeataAccount account = accountMapper.selectById(userId);
  37 + Assert.notNull(account, "用户不存在");
  38 + Double balance = account.getBalance();
  39 + log.info("下单用户{}余额为 {},商品总价为{}", userId, balance, price);
  40 +
  41 + if (balance < price) {
  42 + log.warn("用户 {} 余额不足,当前余额:{}", userId, balance);
  43 + throw new RuntimeException("余额不足");
  44 + }
  45 + log.info("开始扣减用户 {} 余额", userId);
  46 + double currentBalance = account.getBalance() - price;
  47 + account.setBalance(currentBalance);
  48 + accountMapper.updateById(account);
  49 + log.info("扣减用户 {} 余额成功,扣减后用户账户余额为{}", userId, currentBalance);
  50 + log.info("=============ACCOUNT END=================");
  51 + }
  52 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/service/impl/SeataOrderServiceImpl.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.service.impl;
  2 +import com.baomidou.dynamic.datasource.annotation.DS;
  3 +
  4 +import io.seata.spring.annotation.GlobalTransactional;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.jeecg.modules.test.seata.dto.PlaceOrderRequest;
  7 +import org.jeecg.modules.test.seata.entity.SeataOrder;
  8 +import org.jeecg.modules.test.seata.enums.OrderStatus;
  9 +import org.jeecg.modules.test.seata.mapper.SeataOrderMapper;
  10 +import org.jeecg.modules.test.seata.service.SeataAccountService;
  11 +import org.jeecg.modules.test.seata.service.SeataOrderService;
  12 +import org.jeecg.modules.test.seata.service.SeataProductService;
  13 +import org.springframework.beans.factory.annotation.Autowired;
  14 +import org.springframework.stereotype.Service;
  15 +import org.springframework.transaction.annotation.Transactional;
  16 +
  17 +import javax.annotation.Resource;
  18 +/**
  19 + * @Description: 订单服务类
  20 + * @author: zyf
  21 + * @date: 2022/01/24
  22 + * @version: V1.0
  23 + */
  24 +@Slf4j
  25 +@Service
  26 +public class SeataOrderServiceImpl implements SeataOrderService {
  27 +
  28 + @Resource
  29 + private SeataOrderMapper orderMapper;
  30 + @Autowired
  31 + private SeataAccountService accountService;
  32 + @Autowired
  33 + private SeataProductService productService;
  34 +
  35 + @DS("order")
  36 + @Override
  37 + @Transactional
  38 + @GlobalTransactional
  39 + public void placeOrder(PlaceOrderRequest request) {
  40 + log.info("=============ORDER START=================");
  41 + Long userId = request.getUserId();
  42 + Long productId = request.getProductId();
  43 + Integer amount = request.getAmount();
  44 + log.info("收到下单请求,用户:{}, 商品:{},数量:{}", userId, productId, amount);
  45 +
  46 +
  47 + SeataOrder order = SeataOrder.builder()
  48 + .userId(userId)
  49 + .productId(productId)
  50 + .status(OrderStatus.INIT)
  51 + .amount(amount)
  52 + .build();
  53 +
  54 + orderMapper.insert(order);
  55 + log.info("订单一阶段生成,等待扣库存付款中");
  56 + // 扣减库存并计算总价
  57 + Double totalPrice = productService.reduceStock(productId, amount);
  58 + // 扣减余额
  59 + accountService.reduceBalance(userId, totalPrice);
  60 +
  61 + order.setStatus(OrderStatus.SUCCESS);
  62 + order.setTotalPrice(totalPrice);
  63 + orderMapper.updateById(order);
  64 + log.info("订单已成功下单");
  65 + log.info("=============ORDER END=================");
  66 + }
  67 +}
0 68 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/java/org/jeecg/modules/test/seata/service/impl/SeataProductServiceImpl.java 0 → 100644
  1 +package org.jeecg.modules.test.seata.service.impl;
  2 +
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +
  6 +import org.jeecg.modules.test.seata.entity.SeataProduct;
  7 +import org.jeecg.modules.test.seata.mapper.SeataProductMapper;
  8 +import org.jeecg.modules.test.seata.service.SeataProductService;
  9 +import org.springframework.stereotype.Service;
  10 +import org.springframework.transaction.annotation.Propagation;
  11 +import org.springframework.transaction.annotation.Transactional;
  12 +import org.springframework.util.Assert;
  13 +
  14 +import javax.annotation.Resource;
  15 +
  16 +/**
  17 + * @Description: 产品服务类
  18 + * @author: zyf
  19 + * @date: 2022/01/24
  20 + * @version: V1.0
  21 + */
  22 +@Slf4j
  23 +@Service
  24 +public class SeataProductServiceImpl implements SeataProductService {
  25 +
  26 + @Resource
  27 + private SeataProductMapper productMapper;
  28 +
  29 + /**
  30 + * 事务传播特性设置为 REQUIRES_NEW 开启新的事务
  31 + */
  32 + @DS("product")
  33 + @Transactional(propagation = Propagation.REQUIRES_NEW)
  34 + @Override
  35 + public Double reduceStock(Long productId, Integer amount) {
  36 + log.info("=============PRODUCT START=================");
  37 + // 检查库存
  38 + SeataProduct product = productMapper.selectById(productId);
  39 + Assert.notNull(product, "商品不存在");
  40 + Integer stock = product.getStock();
  41 + log.info("商品编号为 {} 的库存为{},订单商品数量为{}", productId, stock, amount);
  42 +
  43 + if (stock < amount) {
  44 + log.warn("商品编号为{} 库存不足,当前库存:{}", productId, stock);
  45 + throw new RuntimeException("库存不足");
  46 + }
  47 + log.info("开始扣减商品编号为 {} 库存,单价商品价格为{}", productId, product.getPrice());
  48 + // 扣减库存
  49 + int currentStock = stock - amount;
  50 + product.setStock(currentStock);
  51 + productMapper.updateById(product);
  52 + double totalPrice = product.getPrice() * amount;
  53 + log.info("扣减商品编号为 {} 库存成功,扣减后库存为{}, {} 件商品总价为 {} ", productId, currentStock, amount, totalPrice);
  54 + log.info("=============PRODUCT END=================");
  55 + return totalPrice;
  56 + }
  57 +}
0 58 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/resources/application-seata.yml 0 → 100644
  1 +seata:
  2 + enable-auto-data-source-proxy: false
  3 + service:
  4 + grouplist:
  5 + default: 127.0.0.1:8091
  6 + vgroup-mapping:
  7 + springboot-seata-group: default
  8 + # seata 事务组编号 用于TC集群名
  9 + tx-service-group: springboot-seata-group
  10 + transport:
  11 + heartbeat: false
  12 +spring:
  13 + datasource:
  14 + dynamic:
  15 + seata: true # 开启对 seata的支持
  16 + seata-mode: AT #支持XA及AT模式,默认AT
  17 + datasource:
  18 + # 设置 账号数据源配置
  19 + account:
  20 + driver-class-name: com.mysql.cj.jdbc.Driver
  21 + url: jdbc:mysql://127.0.0.1:3306/jeecg-account?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
  22 + username: root
  23 + password: root
  24 + schema: classpath:sql/schema-account.sql
  25 + # 设置 订单数据源配置
  26 + order:
  27 + driver-class-name: com.mysql.cj.jdbc.Driver
  28 + url: jdbc:mysql://127.0.0.1:3306/jeecg-order?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
  29 + username: root
  30 + password: root
  31 + schema: classpath:sql/schema-order.sql
  32 + # 设置商品 数据源配置
  33 + product:
  34 + driver-class-name: com.mysql.cj.jdbc.Driver
  35 + url: jdbc:mysql://127.0.0.1:3306/jeecg-product?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
  36 + username: root
  37 + password: root
  38 + schema: classpath:sql/schema-product.sql
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/resources/sql/schema-account.sql 0 → 100644
  1 +DROP TABLE IF EXISTS account;
  2 +CREATE TABLE account
  3 +(
  4 + id INT(11) NOT NULL AUTO_INCREMENT,
  5 + balance DOUBLE DEFAULT NULL,
  6 + last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  7 + PRIMARY KEY (id)
  8 +) ENGINE = InnoDB
  9 + AUTO_INCREMENT = 1
  10 + DEFAULT CHARSET = utf8;
  11 +
  12 +DROP TABLE IF EXISTS undo_log;
  13 +CREATE TABLE undo_log
  14 +(
  15 + id BIGINT(20) NOT NULL AUTO_INCREMENT,
  16 + branch_id BIGINT(20) NOT NULL,
  17 + xid VARCHAR(100) NOT NULL,
  18 + context VARCHAR(128) NOT NULL,
  19 + rollback_info LONGBLOB NOT NULL,
  20 + log_status INT(11) NOT NULL,
  21 + log_created DATETIME NOT NULL,
  22 + log_modified DATETIME NOT NULL,
  23 + PRIMARY KEY (id),
  24 + UNIQUE KEY ux_undo_log (xid, branch_id)
  25 +) ENGINE = InnoDB
  26 + AUTO_INCREMENT = 1
  27 + DEFAULT CHARSET = utf8;
  28 +INSERT INTO account (id, balance)
  29 +VALUES (1, 50);
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/resources/sql/schema-order.sql 0 → 100644
  1 +DROP TABLE IF EXISTS p_order;
  2 +CREATE TABLE p_order
  3 +(
  4 + id INT(11) NOT NULL AUTO_INCREMENT,
  5 + user_id INT(11) DEFAULT NULL,
  6 + product_id INT(11) DEFAULT NULL,
  7 + amount INT(11) DEFAULT NULL,
  8 + total_price DOUBLE DEFAULT NULL,
  9 + status VARCHAR(100) DEFAULT NULL,
  10 + add_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  11 + last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  12 + PRIMARY KEY (id)
  13 +) ENGINE = InnoDB
  14 + AUTO_INCREMENT = 1
  15 + DEFAULT CHARSET = utf8;
  16 +
  17 +DROP TABLE IF EXISTS undo_log;
  18 +CREATE TABLE undo_log
  19 +(
  20 + id BIGINT(20) NOT NULL AUTO_INCREMENT,
  21 + branch_id BIGINT(20) NOT NULL,
  22 + xid VARCHAR(100) NOT NULL,
  23 + context VARCHAR(128) NOT NULL,
  24 + rollback_info LONGBLOB NOT NULL,
  25 + log_status INT(11) NOT NULL,
  26 + log_created DATETIME NOT NULL,
  27 + log_modified DATETIME NOT NULL,
  28 + PRIMARY KEY (id),
  29 + UNIQUE KEY ux_undo_log (xid, branch_id)
  30 +) ENGINE = InnoDB
  31 + AUTO_INCREMENT = 1
  32 + DEFAULT CHARSET = utf8;
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/resources/sql/schema-product.sql 0 → 100644
  1 +DROP TABLE IF EXISTS product;
  2 +CREATE TABLE product
  3 +(
  4 + id INT(11) NOT NULL AUTO_INCREMENT,
  5 + price DOUBLE DEFAULT NULL,
  6 + stock INT(11) DEFAULT NULL,
  7 + last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  8 + PRIMARY KEY (id)
  9 +) ENGINE = InnoDB
  10 + AUTO_INCREMENT = 1
  11 + DEFAULT CHARSET = utf8;
  12 +
  13 +DROP TABLE IF EXISTS undo_log;
  14 +CREATE TABLE undo_log
  15 +(
  16 + id BIGINT(20) NOT NULL AUTO_INCREMENT,
  17 + branch_id BIGINT(20) NOT NULL,
  18 + xid VARCHAR(100) NOT NULL,
  19 + context VARCHAR(128) NOT NULL,
  20 + rollback_info LONGBLOB NOT NULL,
  21 + log_status INT(11) NOT NULL,
  22 + log_created DATETIME NOT NULL,
  23 + log_modified DATETIME NOT NULL,
  24 + PRIMARY KEY (id),
  25 + UNIQUE KEY ux_undo_log (xid, branch_id)
  26 +) ENGINE = InnoDB
  27 + AUTO_INCREMENT = 1
  28 + DEFAULT CHARSET = utf8;
  29 +
  30 +INSERT INTO product (id, price, stock)
  31 +VALUES (1, 10, 20);
0 32 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/resources/sql/seata.sql 0 → 100644
  1 +/*
  2 + Navicat Premium Data Transfer
  3 +
  4 + Source Server : localhost
  5 + Source Server Type : MariaDB
  6 + Source Server Version : 100316
  7 + Source Host : localhost:3300
  8 + Source Schema : seata
  9 +
  10 + Target Server Type : MariaDB
  11 + Target Server Version : 100316
  12 + File Encoding : 65001
  13 +
  14 + Date: 05/01/2022 20:25:07
  15 +*/
  16 +
  17 +SET NAMES utf8mb4;
  18 +SET FOREIGN_KEY_CHECKS = 0;
  19 +
  20 +-- ----------------------------
  21 +-- Table structure for branch_table
  22 +-- ----------------------------
  23 +DROP TABLE IF EXISTS `branch_table`;
  24 +CREATE TABLE `branch_table` (
  25 + `branch_id` bigint(20) NOT NULL,
  26 + `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  27 + `transaction_id` bigint(20) NULL DEFAULT NULL,
  28 + `resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  29 + `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  30 + `branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  31 + `status` tinyint(4) NULL DEFAULT NULL,
  32 + `client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  33 + `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  34 + `gmt_create` datetime(6) NULL DEFAULT NULL,
  35 + `gmt_modified` datetime(6) NULL DEFAULT NULL,
  36 + PRIMARY KEY (`branch_id`) USING BTREE,
  37 + INDEX `idx_xid`(`xid`) USING BTREE
  38 +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  39 +
  40 +-- ----------------------------
  41 +-- Table structure for global_table
  42 +-- ----------------------------
  43 +DROP TABLE IF EXISTS `global_table`;
  44 +CREATE TABLE `global_table` (
  45 + `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  46 + `transaction_id` bigint(20) NULL DEFAULT NULL,
  47 + `status` tinyint(4) NOT NULL,
  48 + `application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  49 + `transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  50 + `transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  51 + `timeout` int(11) NULL DEFAULT NULL,
  52 + `begin_time` bigint(20) NULL DEFAULT NULL,
  53 + `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  54 + `gmt_create` datetime(0) NULL DEFAULT NULL,
  55 + `gmt_modified` datetime(0) NULL DEFAULT NULL,
  56 + PRIMARY KEY (`xid`) USING BTREE,
  57 + INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
  58 + INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
  59 +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  60 +
  61 +-- ----------------------------
  62 +-- Table structure for lock_table
  63 +-- ----------------------------
  64 +DROP TABLE IF EXISTS `lock_table`;
  65 +CREATE TABLE `lock_table` (
  66 + `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  67 + `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  68 + `transaction_id` bigint(20) NULL DEFAULT NULL,
  69 + `branch_id` bigint(20) NOT NULL,
  70 + `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  71 + `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  72 + `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  73 + `gmt_create` datetime(0) NULL DEFAULT NULL,
  74 + `gmt_modified` datetime(0) NULL DEFAULT NULL,
  75 + PRIMARY KEY (`row_key`) USING BTREE,
  76 + INDEX `idx_branch_id`(`branch_id`) USING BTREE
  77 +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  78 +
  79 +SET FOREIGN_KEY_CHECKS = 1;
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-seata/src/main/resources/sql/undo_log.sql 0 → 100644
  1 +CREATE TABLE `undo_log` (
  2 + `id` bigint(20) NOT NULL AUTO_INCREMENT,
  3 + `branch_id` bigint(20) NOT NULL,
  4 + `xid` varchar(100) NOT NULL,
  5 + `context` varchar(128) NOT NULL,
  6 + `rollback_info` longblob NOT NULL,
  7 + `log_status` int(11) NOT NULL,
  8 + `log_created` datetime NOT NULL,
  9 + `log_modified` datetime NOT NULL,
  10 + PRIMARY KEY (`id`),
  11 + UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
  12 +) ENGINE=InnoDB AUTO_INCREMENT=137 DEFAULT CHARSET=utf8;
0 13 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/doc/db.sql 0 → 100644
  1 +CREATE TABLE `sys_log0` (
  2 + `id` varchar(32) NOT NULL,
  3 + `log_type` int(2) DEFAULT NULL COMMENT '日志类型(1登录日志,2操作日志)',
  4 + `log_content` varchar(1000) DEFAULT NULL COMMENT '日志内容',
  5 + `operate_type` int(2) DEFAULT NULL COMMENT '操作类型',
  6 + `userid` varchar(32) DEFAULT NULL COMMENT '操作用户账号',
  7 + `username` varchar(100) DEFAULT NULL COMMENT '操作用户名称',
  8 + `ip` varchar(100) DEFAULT NULL COMMENT 'IP',
  9 + `method` varchar(500) DEFAULT NULL COMMENT '请求java方法',
  10 + `request_url` varchar(255) DEFAULT NULL COMMENT '请求路径',
  11 + `request_param` longtext DEFAULT NULL COMMENT '请求参数',
  12 + `request_type` varchar(10) DEFAULT NULL COMMENT '请求类型',
  13 + `cost_time` bigint(20) DEFAULT NULL COMMENT '耗时',
  14 + `create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
  15 + `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  16 + `update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
  17 + `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  18 + PRIMARY KEY (`id`) USING BTREE,
  19 + KEY `index_table_userid` (`userid`) USING BTREE,
  20 + KEY `index_logt_ype` (`log_type`) USING BTREE,
  21 + KEY `index_operate_type` (`operate_type`) USING BTREE,
  22 + KEY `index_createtime` (`create_time`) USING BTREE
  23 +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统日志表';
  24 +CREATE TABLE `sys_log1` (
  25 + `id` varchar(32) NOT NULL,
  26 + `log_type` int(2) DEFAULT NULL COMMENT '日志类型(1登录日志,2操作日志)',
  27 + `log_content` varchar(1000) DEFAULT NULL COMMENT '日志内容',
  28 + `operate_type` int(2) DEFAULT NULL COMMENT '操作类型',
  29 + `userid` varchar(32) DEFAULT NULL COMMENT '操作用户账号',
  30 + `username` varchar(100) DEFAULT NULL COMMENT '操作用户名称',
  31 + `ip` varchar(100) DEFAULT NULL COMMENT 'IP',
  32 + `method` varchar(500) DEFAULT NULL COMMENT '请求java方法',
  33 + `request_url` varchar(255) DEFAULT NULL COMMENT '请求路径',
  34 + `request_param` longtext DEFAULT NULL COMMENT '请求参数',
  35 + `request_type` varchar(10) DEFAULT NULL COMMENT '请求类型',
  36 + `cost_time` bigint(20) DEFAULT NULL COMMENT '耗时',
  37 + `create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
  38 + `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  39 + `update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
  40 + `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  41 + PRIMARY KEY (`id`) USING BTREE,
  42 + KEY `index_table_userid` (`userid`) USING BTREE,
  43 + KEY `index_logt_ype` (`log_type`) USING BTREE,
  44 + KEY `index_operate_type` (`operate_type`) USING BTREE,
  45 + KEY `index_createtime` (`create_time`) USING BTREE
  46 +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统日志表';
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>jeecg-cloud-test</artifactId>
  7 + <groupId>org.jeecgframework.boot</groupId>
  8 + <version>3.1.0</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 +
  12 + <artifactId>jeecg-cloud-test-shardingsphere</artifactId>
  13 +
  14 + <dependencies>
  15 + <dependency>
  16 + <groupId>org.jeecgframework.boot</groupId>
  17 + <artifactId>jeecg-boot-starter-shardingsphere</artifactId>
  18 + <version>3.1.0</version>
  19 + </dependency>
  20 + </dependencies>
  21 +
  22 +</project>
0 23 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/java/org/jeecg/modules/test/sharding/algorithm/StandardModTableShardAlgorithm.java 0 → 100644
  1 +package org.jeecg.modules.test.sharding.algorithm;
  2 +
  3 +
  4 +import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
  5 +import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
  6 +import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
  7 +
  8 +import java.util.Collection;
  9 +import java.util.Properties;
  10 +
  11 +/**
  12 + * 用于处理使用单一键
  13 + * 根据分片字段的值和sharding-count进行取模运算
  14 + * SQL 语句中有>,>=, <=,<,=,IN 和 BETWEEN AND 操作符,都可以应用此分片策略。
  15 + *
  16 + * @author zyf
  17 + */
  18 +public class StandardModTableShardAlgorithm implements StandardShardingAlgorithm<Integer> {
  19 + private Properties props = new Properties();
  20 +
  21 +
  22 + /**
  23 + * 用于处理=和IN的分片
  24 + *
  25 + * @param collection 目标分片的集合(表名)
  26 + * @param preciseShardingValue 逻辑表相关信息
  27 + * @return
  28 + */
  29 + @Override
  30 + public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) {
  31 +
  32 + for (String name : collection) {
  33 + Integer value = preciseShardingValue.getValue();
  34 + //根据值进行取模,得到一个目标值
  35 + if (name.indexOf(value % 2+"") > -1) {
  36 + return name;
  37 + }
  38 + }
  39 + throw new UnsupportedOperationException();
  40 + }
  41 +
  42 + /**
  43 + * 用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理
  44 + *
  45 + * @param collection
  46 + * @param rangeShardingValue
  47 + * @return
  48 + */
  49 + @Override
  50 + public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Integer> rangeShardingValue) {
  51 +
  52 + return collection;
  53 + }
  54 +
  55 + /**
  56 + * 初始化对象的时候调用的方法
  57 + */
  58 + @Override
  59 + public void init() {
  60 + }
  61 +
  62 + /**
  63 + * 对应分片算法(sharding-algorithms)的类型
  64 + *
  65 + * @return
  66 + */
  67 + @Override
  68 + public String getType() {
  69 + return "STANDARD_MOD";
  70 + }
  71 +
  72 + @Override
  73 + public Properties getProps() {
  74 + return this.props;
  75 + }
  76 +
  77 + /**
  78 + * 获取分片相关属性
  79 + *
  80 + * @param properties
  81 + */
  82 + @Override
  83 + public void setProps(Properties properties) {
  84 + this.props = properties;
  85 + }
  86 +}
0 87 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/java/org/jeecg/modules/test/sharding/controller/JeecgShardingDemoController.java 0 → 100644
  1 +package org.jeecg.modules.test.sharding.controller;
  2 +
  3 +import org.jeecg.common.api.vo.Result;
  4 +import org.jeecg.common.aspect.annotation.AutoLog;
  5 +import org.jeecg.common.system.base.controller.JeecgController;
  6 +import org.jeecg.modules.test.sharding.entity.ShardingSysLog;
  7 +import org.jeecg.modules.test.sharding.service.IShardingSysLogService;
  8 +import org.springframework.beans.factory.annotation.Autowired;
  9 +import org.springframework.web.bind.annotation.PostMapping;
  10 +import org.springframework.web.bind.annotation.RequestMapping;
  11 +import org.springframework.web.bind.annotation.RestController;
  12 +
  13 +import io.swagger.annotations.Api;
  14 +import io.swagger.annotations.ApiOperation;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +
  17 +/**
  18 + * @Description: 分库分表测试
  19 + * @author: zyf
  20 + * @date: 2022/01/24
  21 + * @version: V1.0
  22 + */
  23 +@Slf4j
  24 +@Api(tags = "分库分表测试")
  25 +@RestController
  26 +@RequestMapping("/sharding/")
  27 +public class JeecgShardingDemoController extends JeecgController<ShardingSysLog, IShardingSysLogService> {
  28 + @Autowired
  29 + private IShardingSysLogService shardingSysLogService;
  30 +
  31 + /**
  32 + * 添加
  33 + * @return
  34 + */
  35 + @PostMapping(value = "/add")
  36 + @AutoLog(value = "分库分表添加")
  37 + @ApiOperation(value = "分库分表添加", notes = "分库分表添加")
  38 + public Result<?> add() {
  39 + for (int i = 0; i < 10; i++) {
  40 + ShardingSysLog shardingSysLog = new ShardingSysLog();
  41 + shardingSysLog.setLogContent("jeecg");
  42 + shardingSysLog.setLogType(i);
  43 + shardingSysLog.setOperateType(i);
  44 + shardingSysLogService.save(shardingSysLog);
  45 + }
  46 + return Result.OK();
  47 + }
  48 +
  49 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/java/org/jeecg/modules/test/sharding/entity/ShardingSysLog.java 0 → 100644
  1 +package org.jeecg.modules.test.sharding.entity;
  2 +
  3 +import com.baomidou.mybatisplus.annotation.IdType;
  4 +import com.baomidou.mybatisplus.annotation.TableId;
  5 +import com.baomidou.mybatisplus.annotation.TableName;
  6 +import com.fasterxml.jackson.annotation.JsonFormat;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +import lombok.experimental.Accessors;
  10 +import org.jeecg.common.aspect.annotation.Dict;
  11 +import org.springframework.format.annotation.DateTimeFormat;
  12 +
  13 +import java.io.Serializable;
  14 +import java.util.Date;
  15 +
  16 +/**
  17 + * <p>
  18 + * 系统日志表
  19 + * </p>
  20 + */
  21 +@Data
  22 +@TableName("sys_log")
  23 +public class ShardingSysLog implements Serializable {
  24 +
  25 + private static final long serialVersionUID = 1L;
  26 +
  27 + /**
  28 + * id
  29 + */
  30 + @TableId(type = IdType.ASSIGN_ID)
  31 + private String id;
  32 +
  33 + /**
  34 + * 创建人
  35 + */
  36 + private String createBy;
  37 +
  38 + /**
  39 + * 创建时间
  40 + */
  41 + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
  42 + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  43 + private Date createTime;
  44 +
  45 + /**
  46 + * 更新人
  47 + */
  48 + private String updateBy;
  49 +
  50 + /**
  51 + * 更新时间
  52 + */
  53 + private Date updateTime;
  54 +
  55 + /**
  56 + * 耗时
  57 + */
  58 + private Long costTime;
  59 +
  60 + /**
  61 + * IP
  62 + */
  63 + private String ip;
  64 +
  65 + /**
  66 + * 请求参数
  67 + */
  68 + private String requestParam;
  69 +
  70 + /**
  71 + * 请求类型
  72 + */
  73 + private String requestType;
  74 +
  75 + /**
  76 + * 请求路径
  77 + */
  78 + private String requestUrl;
  79 + /**
  80 + * 请求方法
  81 + */
  82 + private String method;
  83 +
  84 + /**
  85 + * 操作人用户名称
  86 + */
  87 + private String username;
  88 + /**
  89 + * 操作人用户账户
  90 + */
  91 + private String userid;
  92 + /**
  93 + * 操作详细日志
  94 + */
  95 + private String logContent;
  96 +
  97 + /**
  98 + * 日志类型(1登录日志,2操作日志)
  99 + */
  100 + @Dict(dicCode = "log_type")
  101 + private Integer logType;
  102 +
  103 + /**
  104 + * 操作类型(1查询,2添加,3修改,4删除,5导入,6导出)
  105 + */
  106 + @Dict(dicCode = "operate_type")
  107 + private Integer operateType;
  108 +
  109 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/java/org/jeecg/modules/test/sharding/mapper/ShardingSysLogMapper.java 0 → 100644
  1 +package org.jeecg.modules.test.sharding.mapper;
  2 +
  3 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4 +import org.jeecg.modules.test.sharding.entity.ShardingSysLog;
  5 +
  6 +
  7 +/**
  8 + * @Description: 系统日志表 Mapper 接口
  9 + * @author: zyf
  10 + * @date: 2022/01/24
  11 + * @version: V1.0
  12 + */
  13 +public interface ShardingSysLogMapper extends BaseMapper<ShardingSysLog> {
  14 +
  15 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/java/org/jeecg/modules/test/sharding/mapper/xml/ShardingSysLogMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="org.jeecg.modules.demo.sharding.mapper.ShardingSysLogMapper">
  4 +
  5 +</mapper>
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/java/org/jeecg/modules/test/sharding/service/IShardingSysLogService.java 0 → 100644
  1 +package org.jeecg.modules.test.sharding.service;
  2 +
  3 +import com.baomidou.mybatisplus.extension.service.IService;
  4 +import org.jeecg.modules.test.sharding.entity.ShardingSysLog;
  5 +
  6 +/**
  7 + * @Description: 系统日志表 服务类
  8 + * @author: zyf
  9 + * @date: 2022/01/24
  10 + * @version: V1.0
  11 + */
  12 +public interface IShardingSysLogService extends IService<ShardingSysLog> {
  13 +
  14 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/java/org/jeecg/modules/test/sharding/service/impl/ShardingSysLogServiceImpl.java 0 → 100644
  1 +package org.jeecg.modules.test.sharding.service.impl;
  2 +
  3 +import com.baomidou.dynamic.datasource.annotation.DS;
  4 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  5 +import org.jeecg.modules.test.sharding.entity.ShardingSysLog;
  6 +import org.jeecg.modules.test.sharding.mapper.ShardingSysLogMapper;
  7 +import org.jeecg.modules.test.sharding.service.IShardingSysLogService;
  8 +import org.springframework.stereotype.Service;
  9 +
  10 +/**
  11 + * <p>
  12 + * 系统日志表 服务实现类
  13 + * </p>
  14 + */
  15 +@Service
  16 +@DS("sharding")
  17 +public class ShardingSysLogServiceImpl extends ServiceImpl<ShardingSysLogMapper, ShardingSysLog> implements IShardingSysLogService {
  18 +
  19 +}
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/jeecg-cloud-test-shardingsphere/src/main/resources/application-sharding.yml 0 → 100644
  1 +spring:
  2 + shardingsphere:
  3 + props:
  4 + sql-show: true
  5 + datasource:
  6 + ds0:
  7 + driverClassName: com.mysql.cj.jdbc.Driver
  8 + url: jdbc:mysql://localhost:3306/jeecg-boot?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
  9 + username: root
  10 + type: com.alibaba.druid.pool.DruidDataSource
  11 + password: root
  12 + names: ds0
  13 + # 规则配置
  14 + rules:
  15 + sharding:
  16 + # 配置绑定表,每一行为一组
  17 + binding-tables: sys_log
  18 + # 分布式序列算法配置
  19 + key-generators:
  20 + snowflake:
  21 + type: SNOWFLAKE
  22 + props:
  23 + worker-id: 123
  24 + # 分片算法配置
  25 + sharding-algorithms:
  26 + table-classbased:
  27 + props:
  28 + strategy: standard
  29 + algorithmClassName: org.jeecg.modules.test.sharding.algorithm.StandardModTableShardAlgorithm
  30 + type: CLASS_BASED
  31 + tables:
  32 + # 逻辑表名称
  33 + sys_log:
  34 + #配置具体表的数据节点
  35 + actual-data-nodes: ds0.sys_log$->{0..1}
  36 + # 分表策略
  37 + table-strategy:
  38 + standard:
  39 + # 分片算法名称
  40 + sharding-algorithm-name: table-classbased
  41 + # 分片列名称
  42 + sharding-column: log_type
0 43 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-test/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>jeecg-cloud-module</artifactId>
  7 + <groupId>org.jeecgframework.boot</groupId>
  8 + <version>3.1.0</version>
  9 + </parent>
  10 +
  11 + <modelVersion>4.0.0</modelVersion>
  12 + <packaging>pom</packaging>
  13 + <artifactId>jeecg-cloud-test</artifactId>
  14 +
  15 + <dependencies>
  16 + <dependency>
  17 + <groupId>org.jeecgframework.boot</groupId>
  18 + <artifactId>jeecg-boot-base-core</artifactId>
  19 + </dependency>
  20 + </dependencies>
  21 +
  22 + <modules>
  23 + <module>jeecg-cloud-test-seata</module>
  24 + <module>jeecg-cloud-test-shardingsphere</module>
  25 + </modules>
  26 +</project>
0 27 \ No newline at end of file
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-xxljob/Dockerfile
... ... @@ -10,7 +10,7 @@ WORKDIR /jeecg-cloud-xxljob
10 10  
11 11 EXPOSE 9080
12 12  
13   -ADD ./target/jeecg-cloud-xxljob-3.0.jar ./
  13 +ADD ./target/jeecg-cloud-xxljob-3.1.0.jar ./
14 14  
15   -CMD java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-xxljob-3.0.jar
  15 +CMD java -Dfile.encoding=utf-8 -Djava.security.egd=file:/dev/./urandom -jar jeecg-cloud-xxljob-3.1.0.jar
16 16  
... ...
jeecg-boot/jeecg-cloud-module/jeecg-cloud-xxljob/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-cloud-module</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ...
jeecg-boot/jeecg-cloud-module/pom.xml
... ... @@ -5,7 +5,7 @@
5 5 <parent>
6 6 <artifactId>jeecg-boot-parent</artifactId>
7 7 <groupId>org.jeecgframework.boot</groupId>
8   - <version>3.0</version>
  8 + <version>3.1.0</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11 11  
... ... @@ -18,6 +18,7 @@
18 18 <module>jeecg-cloud-monitor</module>
19 19 <module>jeecg-cloud-system-start</module>
20 20 <module>jeecg-cloud-xxljob</module>
  21 + <module>jeecg-cloud-test</module>
21 22 </modules>
22 23  
23 24  
... ...
jeecg-boot/pom.xml
... ... @@ -2,7 +2,7 @@
2 2 <modelVersion>4.0.0</modelVersion>
3 3 <groupId>org.jeecgframework.boot</groupId>
4 4 <artifactId>jeecg-boot-parent</artifactId>
5   - <version>3.0</version>
  5 + <version>3.1.0</version>
6 6 <packaging>pom</packaging>
7 7  
8 8 <parent>
... ... @@ -13,7 +13,7 @@
13 13 </parent>
14 14  
15 15 <properties>
16   - <jeecgboot.version>3.0</jeecgboot.version>
  16 + <jeecgboot.version>3.1.0</jeecgboot.version>
17 17 <java.version>1.8</java.version>
18 18 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19 19 <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
... ... @@ -42,12 +42,12 @@
42 42 <java-jwt.version>3.11.0</java-jwt.version>
43 43 <shiro-redis.version>3.1.0</shiro-redis.version>
44 44 <codegenerate.version>1.3.7</codegenerate.version>
45   - <autopoi-web.version>1.3.6</autopoi-web.version>
  45 + <autopoi-web.version>1.3.9</autopoi-web.version>
46 46 <minio.version>8.0.3</minio.version>
47 47 <justauth-spring-boot-starter.version>1.3.4</justauth-spring-boot-starter.version>
48 48 <dom4j.version>1.6.1</dom4j.version>
49 49 <qiniu-java-sdk.version>7.4.0</qiniu-java-sdk.version>
50   - <!-- Log4j2惊爆0Day漏洞-->
  50 + <!-- Log4j2爆雷漏洞 -->
51 51 <log4j2.version>2.17.0</log4j2.version>
52 52 <logback.version>1.2.9</logback.version>
53 53 </properties>
... ... @@ -366,6 +366,10 @@
366 366 <config.server-addr>127.0.0.1:8848</config.server-addr>
367 367 <!--Nacos配置中心命名空间,用于支持多环境.这里必须使用ID,不能使用名称,默认为空-->
368 368 <config.namespace></config.namespace>
  369 + <!--Nacos用户名-->
  370 + <config.username>nacos</config.username>
  371 + <!--Nacos密码-->
  372 + <config.password>nacos</config.password>
369 373 <!--Nacos配置分组名称-->
370 374 <config.group>DEFAULT_GROUP</config.group>
371 375 <!--Nacos服务发现地址-->
... ... @@ -384,6 +388,10 @@
384 388 <config.server-addr>127.0.0.1:8848</config.server-addr>
385 389 <!--Nacos配置中心命名空间,用于支持多环境.这里必须使用ID,不能使用名称,默认为空-->
386 390 <config.namespace></config.namespace>
  391 + <!--Nacos用户名-->
  392 + <config.username>nacos</config.username>
  393 + <!--Nacos密码-->
  394 + <config.password>nacos</config.password>
387 395 <!--Nacos配置分组名称-->
388 396 <config.group>DEFAULT_GROUP</config.group>
389 397 <!--Nacos服务发现地址-->
... ... @@ -402,6 +410,10 @@
402 410 <config.server-addr>127.0.0.1:8848</config.server-addr>
403 411 <!--Nacos配置中心命名空间,用于支持多环境.这里必须使用ID,不能使用名称,默认为空-->
404 412 <config.namespace></config.namespace>
  413 + <!--Nacos用户名-->
  414 + <config.username>nacos</config.username>
  415 + <!--Nacos密码-->
  416 + <config.password>nacos</config.password>
405 417 <!--Nacos配置分组名称-->
406 418 <config.group>DEFAULT_GROUP</config.group>
407 419 <!--Nacos服务发现地址-->
... ...