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

This commit is contained in:
zhangdaiscott
2022-02-24 15:13:05 +08:00
parent 8c143f35f8
commit f8c7ddd223
304 changed files with 40313 additions and 230872 deletions

View File

@ -11,6 +11,6 @@ WORKDIR /jeecg-boot
EXPOSE 8080
ADD ./src/main/resources/jeecg ./config/jeecg
ADD ./target/jeecg-boot-module-system-3.0.jar ./
ADD ./target/jeecg-boot-module-system-3.1.0.jar ./
CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-3.0.jar
CMD sleep 60;java -Djava.security.egd=file:/dev/./urandom -jar jeecg-boot-module-system-3.1.0.jar

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>3.0</version>
<version>3.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -38,7 +38,7 @@
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>jeewx-api</artifactId>
<version>1.4.6</version>
<version>1.4.7</version>
<exclusions>
<exclusion>
<artifactId>commons-beanutils</artifactId>
@ -54,7 +54,7 @@
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
<version>1.4.0</version>
<version>1.4.2</version>
</dependency>
@ -62,7 +62,7 @@
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-module-demo</artifactId>
<version>3.0</version>
<version>3.1.0</version>
</dependency>
</dependencies>

View File

@ -1,12 +1,19 @@
package org.jeecg;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.Context;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.boot.SpringApplication;
//import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import java.net.InetAddress;
@ -17,6 +24,7 @@ import java.net.UnknownHostException;
*/
@Slf4j
@SpringBootApplication
@EnableAutoConfiguration(exclude={MongoAutoConfiguration.class})
public class JeecgSystemApplication extends SpringBootServletInitializer {
@Override

View File

@ -1,6 +1,6 @@
package org.jeecg.config.jimureport;
import org.jeecg.common.constant.DataBaseConstant;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.SysUserCacheInfo;
@ -20,6 +20,9 @@ import java.util.Map;
* * 1.自定义获取登录token
* * 2.自定义获取登录用户
*/
@Slf4j
@Component
public class JimuReportTokenService implements JmReportTokenServiceI {
@Autowired
@ -45,10 +48,16 @@ public class JimuReportTokenService implements JmReportTokenServiceI {
@Override
public Map<String, Object> getUserInfo(String token) {
Map<String, Object> map = new HashMap<String, Object>();
String username = JwtUtil.getUsername(token);
//此处通过token只能拿到一个信息 用户账号 后面的就是根据账号获取其他信息 查询数据或是走redis 用户根据自身业务可自定义
SysUserCacheInfo userInfo = sysBaseAPI.getCacheUser(username);
Map<String, Object> map = new HashMap<String, Object>();
SysUserCacheInfo userInfo = null;
try {
userInfo = sysBaseAPI.getCacheUser(username);
} catch (Exception e) {
log.error("获取用户信息异常:"+ e.getMessage());
return map;
}
//设置账号名
map.put(SYS_USER_CODE, userInfo.getSysUserCode());
//设置部门编码

View File

@ -1,46 +1,46 @@
package org.jeecg.modules.ngalain.aop;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;;
// 暂时注释掉,提高系统性能
//@Aspect //定义一个切面
//@Configuration
public class LogRecordAspect {
private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);
// 定义切点Pointcut
@Pointcut("execution(public * org.jeecg.modules.*.*.*Controller.*(..))")
public void excudeService() {
}
@Around("excudeService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
String url = request.getRequestURL().toString();
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
logger.info("请求开始, 各个参数, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);
// result的值就是被拦截方法的返回值
Object result = pjp.proceed();
logger.info("请求结束controller的返回值是 " + result);
return result;
}
}
//package org.jeecg.modules.ngalain.aop;
//
//import javax.servlet.http.HttpServletRequest;
//
//import org.aspectj.lang.ProceedingJoinPoint;
//import org.aspectj.lang.annotation.Around;
//import org.aspectj.lang.annotation.Aspect;
//import org.aspectj.lang.annotation.Pointcut;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.web.context.request.RequestAttributes;
//import org.springframework.web.context.request.RequestContextHolder;
//import org.springframework.web.context.request.ServletRequestAttributes;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;;
//
//
//// 暂时注释掉,提高系统性能
////@Aspect //定义一个切面
////@Configuration
//public class LogRecordAspect {
//private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);
//
// // 定义切点Pointcut
// @Pointcut("execution(public * org.jeecg.modules.*.*.*Controller.*(..))")
// public void excudeService() {
// }
//
// @Around("excudeService()")
// public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
// RequestAttributes ra = RequestContextHolder.getRequestAttributes();
// ServletRequestAttributes sra = (ServletRequestAttributes) ra;
// HttpServletRequest request = sra.getRequest();
//
// String url = request.getRequestURL().toString();
// String method = request.getMethod();
// String uri = request.getRequestURI();
// String queryString = request.getQueryString();
// logger.info("请求开始, 各个参数, url: {}, method: {}, uri: {}, params: {}", url, method, uri, queryString);
//
// // result的值就是被拦截方法的返回值
// Object result = pjp.proceed();
//
// logger.info("请求结束controller的返回值是 " + result);
// return result;
// }
//}

View File

@ -1,86 +1,86 @@
package org.jeecg.modules.ngalain.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.ngalain.service.NgAlainService;
import org.jeecg.modules.system.service.ISysDictService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("/sys/ng-alain")
public class NgAlainController {
@Autowired
private NgAlainService ngAlainService;
@Autowired
private ISysDictService sysDictService;
@RequestMapping(value = "/getAppData")
@ResponseBody
public JSONObject getAppData(HttpServletRequest request) throws Exception {
String token=request.getHeader("X-Access-Token");
JSONObject j = new JSONObject();
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
JSONObject userObjcet = new JSONObject();
userObjcet.put("name", user.getUsername());
userObjcet.put("avatar", user.getAvatar());
userObjcet.put("email", user.getEmail());
userObjcet.put("token", token);
j.put("user", userObjcet);
j.put("menu",ngAlainService.getMenu(user.getUsername()));
JSONObject app = new JSONObject();
app.put("name", "jeecg-boot-angular");
app.put("description", "jeecg+ng-alain整合版本");
j.put("app", app);
return j;
}
@RequestMapping(value = "/getDictItems/{dictCode}", method = RequestMethod.GET)
public Object getDictItems(@PathVariable String dictCode) {
log.info(" dictCode : "+ dictCode);
Result<List<DictModel>> result = new Result<List<DictModel>>();
List<DictModel> ls = null;
try {
ls = sysDictService.queryDictItemsByCode(dictCode);
result.setSuccess(true);
result.setResult(ls);
} catch (Exception e) {
log.error(e.getMessage(),e);
result.error500("操作失败");
return result;
}
List<JSONObject> dictlist=new ArrayList<>();
for (DictModel l : ls) {
JSONObject dict=new JSONObject();
try {
dict.put("value",Integer.parseInt(l.getValue()));
} catch (NumberFormatException e) {
dict.put("value",l.getValue());
}
dict.put("label",l.getText());
dictlist.add(dict);
}
return dictlist;
}
@RequestMapping(value = "/getDictItemsByTable/{table}/{key}/{value}", method = RequestMethod.GET)
public Object getDictItemsByTable(@PathVariable String table,@PathVariable String key,@PathVariable String value) {
return this.ngAlainService.getDictByTable(table,key,value);
}
}
//package org.jeecg.modules.ngalain.controller;
//
//import java.util.ArrayList;
//import java.util.List;
//import java.util.Map;
//
//import javax.servlet.http.HttpServletRequest;
//
//import org.apache.shiro.SecurityUtils;
//import org.jeecg.common.api.vo.Result;
//import org.jeecg.common.system.vo.DictModel;
//import org.jeecg.common.system.vo.LoginUser;
//import org.jeecg.modules.ngalain.service.NgAlainService;
//import org.jeecg.modules.system.service.ISysDictService;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.web.bind.annotation.PathVariable;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RequestMethod;
//import org.springframework.web.bind.annotation.ResponseBody;
//import org.springframework.web.bind.annotation.RestController;
//
//import com.alibaba.fastjson.JSONObject;
//
//import lombok.extern.slf4j.Slf4j;
//
//@Slf4j
//@RestController
//@RequestMapping("/sys/ng-alain")
//public class NgAlainController {
// @Autowired
// private NgAlainService ngAlainService;
// @Autowired
// private ISysDictService sysDictService;
//
// @RequestMapping(value = "/getAppData")
// @ResponseBody
// public JSONObject getAppData(HttpServletRequest request) throws Exception {
// String token=request.getHeader("X-Access-Token");
// JSONObject j = new JSONObject();
// LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// JSONObject userObjcet = new JSONObject();
// userObjcet.put("name", user.getUsername());
// userObjcet.put("avatar", user.getAvatar());
// userObjcet.put("email", user.getEmail());
// userObjcet.put("token", token);
// j.put("user", userObjcet);
// j.put("menu",ngAlainService.getMenu(user.getUsername()));
// JSONObject app = new JSONObject();
// app.put("name", "jeecg-boot-angular");
// app.put("description", "jeecg+ng-alain整合版本");
// j.put("app", app);
// return j;
// }
//
// @RequestMapping(value = "/getDictItems/{dictCode}", method = RequestMethod.GET)
// public Object getDictItems(@PathVariable String dictCode) {
// log.info(" dictCode : "+ dictCode);
// Result<List<DictModel>> result = new Result<List<DictModel>>();
// List<DictModel> ls = null;
// try {
// ls = sysDictService.queryDictItemsByCode(dictCode);
// result.setSuccess(true);
// result.setResult(ls);
// } catch (Exception e) {
// log.error(e.getMessage(),e);
// result.error500("操作失败");
// return result;
// }
// List<JSONObject> dictlist=new ArrayList<>();
// for (DictModel l : ls) {
// JSONObject dict=new JSONObject();
// try {
// dict.put("value",Integer.parseInt(l.getValue()));
// } catch (NumberFormatException e) {
// dict.put("value",l.getValue());
// }
// dict.put("label",l.getText());
// dictlist.add(dict);
// }
// return dictlist;
// }
// @RequestMapping(value = "/getDictItemsByTable/{table}/{key}/{value}", method = RequestMethod.GET)
// public Object getDictItemsByTable(@PathVariable String table,@PathVariable String key,@PathVariable String value) {
// return this.ngAlainService.getDictByTable(table,key,value);
// }
//}

View File

@ -6,10 +6,12 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.ImportExcelUtil;
import org.jeecg.modules.quartz.entity.QuartzJob;
import org.jeecg.modules.quartz.service.IQuartzJobService;
@ -145,14 +147,14 @@ public class QuartzJobController {
*/
//@RequiresRoles("admin")
@GetMapping(value = "/pause")
@ApiOperation(value = "停定时任务")
@ApiOperation(value = "定时任务")
public Result<Object> pauseJob(@RequestParam(name = "id") String id) {
QuartzJob job = quartzJobService.getById(id);
if (job == null) {
return Result.error("定时任务不存在!");
}
quartzJobService.pause(job);
return Result.ok("停定时任务成功");
return Result.ok("定时任务成功");
}
/**
@ -163,7 +165,7 @@ public class QuartzJobController {
*/
//@RequiresRoles("admin")
@GetMapping(value = "/resume")
@ApiOperation(value = "恢复定时任务")
@ApiOperation(value = "启动定时任务")
public Result<Object> resumeJob(@RequestParam(name = "id") String id) {
QuartzJob job = quartzJobService.getById(id);
if (job == null) {
@ -171,7 +173,7 @@ public class QuartzJobController {
}
quartzJobService.resumeJob(job);
//scheduler.resumeJob(JobKey.jobKey(job.getJobClassName().trim()));
return Result.ok("恢复定时任务成功");
return Result.ok("启动定时任务成功");
}
/**
@ -202,8 +204,12 @@ public class QuartzJobController {
// 导出文件名称
mv.addObject(NormalExcelConstants.FILE_NAME, "定时任务列表");
mv.addObject(NormalExcelConstants.CLASS, QuartzJob.class);
mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("定时任务列表数据", "导出人:Jeecg", "导出信息"));
mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
//获取当前登录用户
//update-begin---author:wangshuai ---date:20211227 for[JTC-116]导出人写死了------------
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("定时任务列表数据", "导出人:"+user.getRealname(), "导出信息"));
//update-end---author:wangshuai ---date:20211227 for[JTC-116]导出人写死了------------
mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
return mv;
}

View File

@ -17,7 +17,7 @@ public class SampleJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info(" Job Execution key"+jobExecutionContext.getJobDetail().getKey());
log.info(String.format(" Jeecg-Boot 普通定时任务 SampleJob ! 时间:" + DateUtils.getTimestamp()));
}
}

View File

@ -47,6 +47,15 @@ public class DuplicateCheckController {
//SQL注入校验只限制非法串改数据库
final String[] sqlInjCheck = {duplicateCheckVo.getTableName(),duplicateCheckVo.getFieldName()};
SqlInjectionUtil.filterContent(sqlInjCheck);
// update-begin-author:taoyan date:20211227 for: JTC-25 【online报表】oracle 操作问题 录入弹框啥都不填直接保存 ①编码不是应该提示必填么?②报错也应该是具体文字提示,不是后台错误日志
if(StringUtils.isEmpty(duplicateCheckVo.getFieldVal())){
Result rs = new Result();
rs.setCode(500);
rs.setSuccess(true);
rs.setMessage("数据为空,不作处理!");
return rs;
}
// update-end-author:taoyan date:20211227 for: JTC-25 【online报表】oracle 操作问题 录入弹框啥都不填直接保存 ①编码不是应该提示必填么?②报错也应该是具体文字提示,不是后台错误日志
if (StringUtils.isNotBlank(duplicateCheckVo.getDataId())) {
// [2].编辑页面校验
num = sysDictMapper.duplicateCheckCountSql(duplicateCheckVo);

View File

@ -4,6 +4,7 @@ import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@ -58,8 +59,6 @@ public class LoginController {
@Resource
private BaseCommonService baseCommonService;
private static final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";
@ApiOperation("登录接口")
@RequestMapping(value = "/login", method = RequestMethod.POST)
public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel){
@ -82,6 +81,7 @@ public class LoginController {
Object checkCode = redisUtil.get(realKey);
//当进入登录页时,有一定几率出现验证码错误 #1714
if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
log.warn("验证码错误key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode);
result.error500("验证码错误");
return result;
}
@ -402,7 +402,10 @@ public class LoginController {
// update-begin--Author:sunjianlei Date:20210802 for获取用户租户信息
String tenantIds = sysUser.getRelTenantIds();
if (oConvertUtils.isNotEmpty(tenantIds)) {
List<String> tenantIdList = Arrays.asList(tenantIds.split(","));
List<Integer> tenantIdList = new ArrayList<>();
for(String id: tenantIds.split(",")){
tenantIdList.add(Integer.valueOf(id));
}
// 该方法仅查询有效的租户如果返回0个就说明所有的租户均无效。
List<SysTenant> tenantList = sysTenantService.queryEffectiveTenant(tenantIdList);
if (tenantList.size() == 0) {
@ -450,10 +453,17 @@ public class LoginController {
public Result<String> randomImage(HttpServletResponse response,@PathVariable String key){
Result<String> res = new Result<String>();
try {
//生成验证码
final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";
String code = RandomUtil.randomString(BASE_CHECK_CODES,4);
//存到redis中
String lowerCaseCode = code.toLowerCase();
String realKey = MD5Util.MD5Encode(lowerCaseCode+key, "utf-8");
log.info("获取验证码Redis checkCode = {}key = {}", code, key);
redisUtil.set(realKey, lowerCaseCode, 60);
//返回前端
String base64 = RandImageUtil.generate(code);
res.setSuccess(true);
res.setResult(base64);
@ -495,13 +505,16 @@ public class LoginController {
if(oConvertUtils.isEmpty(orgCode)) {
//如果当前用户无选择部门 查看部门关联信息
List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
//update-begin-author:taoyan date:20220117 for: JTC-1068【app】新建用户没有设置部门及角色点击登录提示暂未归属部一直在登录页面 使用手机号登录 可正常
if (departs == null || departs.size() == 0) {
result.error500("用户暂未归属部门,不可登录!");
return result;
/*result.error500("用户暂未归属部门,不可登录!");
return result;*/
}else{
orgCode = departs.get(0).getOrgCode();
sysUser.setOrgCode(orgCode);
this.sysUserService.updateUserDepart(username, orgCode);
}
orgCode = departs.get(0).getOrgCode();
sysUser.setOrgCode(orgCode);
this.sysUserService.updateUserDepart(username, orgCode);
//update-end-author:taoyan date:20220117 for: JTC-1068【app】新建用户没有设置部门及角色点击登录提示暂未归属部一直在登录页面 使用手机号登录 可正常
}
JSONObject obj = new JSONObject();
//用户登录信息
@ -542,5 +555,58 @@ public class LoginController {
}
return Result.ok();
}
/**
* 登录二维码
*/
@ApiOperation(value = "登录二维码", notes = "登录二维码")
@GetMapping("/getLoginQrcode")
public Result<?> getLoginQrcode() {
String qrcodeId = CommonConstant.LOGIN_QRCODE_PRE+IdWorker.getIdStr();
//定义二维码参数
Map params = new HashMap(5);
params.put("qrcodeId", qrcodeId);
//存放二维码唯一标识30秒有效
redisUtil.set(CommonConstant.LOGIN_QRCODE + qrcodeId, qrcodeId, 30);
return Result.OK(params);
}
/**
* 扫码二维码
*/
@ApiOperation(value = "扫码登录二维码", notes = "扫码登录二维码")
@PostMapping("/scanLoginQrcode")
public Result<?> scanLoginQrcode(@RequestParam String qrcodeId, @RequestParam String token) {
Object check = redisUtil.get(CommonConstant.LOGIN_QRCODE + qrcodeId);
if (oConvertUtils.isNotEmpty(check)) {
//存放token给前台读取
redisUtil.set(CommonConstant.LOGIN_QRCODE_TOKEN+qrcodeId, token, 60);
} else {
return Result.error("二维码已过期,请刷新后重试");
}
return Result.OK("扫码成功");
}
/**
* 获取用户扫码后保存的token
*/
@ApiOperation(value = "获取用户扫码后保存的token", notes = "获取用户扫码后保存的token")
@GetMapping("/getQrcodeToken")
public Result getQrcodeToken(@RequestParam String qrcodeId) {
Object token = redisUtil.get(CommonConstant.LOGIN_QRCODE_TOKEN + qrcodeId);
Map result = new HashMap();
Object qrcodeIdExpire = redisUtil.get(CommonConstant.LOGIN_QRCODE + qrcodeId);
if (oConvertUtils.isEmpty(qrcodeIdExpire)) {
//二维码过期通知前台刷新
result.put("token", "-2");
return Result.OK(result);
}
if (oConvertUtils.isNotEmpty(token)) {
result.put("success", true);
result.put("token", token);
} else {
result.put("token", "-1");
}
return Result.OK(result);
}
}

View File

@ -1,19 +0,0 @@
package org.jeecg.modules.system.controller;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.system.entity.SysUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* vue3前端临时接口
*/
@RestController
@RequestMapping("/")
@Slf4j
public class MockVue3Controller {
}

View File

@ -14,6 +14,7 @@ import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.CommonSendStatus;
import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.RedisUtil;
@ -93,18 +94,21 @@ public class SysAnnouncementController {
HttpServletRequest req) {
Result<IPage<SysAnnouncement>> result = new Result<IPage<SysAnnouncement>>();
sysAnnouncement.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
QueryWrapper<SysAnnouncement> queryWrapper = new QueryWrapper<SysAnnouncement>(sysAnnouncement);
QueryWrapper<SysAnnouncement> queryWrapper = QueryGenerator.initQueryWrapper(sysAnnouncement, req.getParameterMap());
Page<SysAnnouncement> page = new Page<SysAnnouncement>(pageNo,pageSize);
//update-begin-author:lvdandan date:20211229 for: sqlserver mssql-jdbc 8.2.2.jre8版本下系统公告列表查询报错 查询SQL中生成了两个create_time DESC故注释此段代码
//排序逻辑 处理
String column = req.getParameter("column");
String order = req.getParameter("order");
if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
if("asc".equals(order)) {
queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
}else {
queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
}
}
// String column = req.getParameter("column");
// String order = req.getParameter("order");
// if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
// if("asc".equals(order)) {
// queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
// }else {
// queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
// }
// }
//update-end-author:lvdandan date:20211229 for: sqlserver mssql-jdbc 8.2.2.jre8版本下系统公告列表查询报错 查询SQL中生成了两个create_time DESC故注释此段代码
IPage<SysAnnouncement> pageList = sysAnnouncementService.page(page, queryWrapper);
result.setSuccess(true);
result.setResult(pageList);
@ -337,11 +341,13 @@ public class SysAnnouncementController {
query.eq(SysAnnouncementSend::getUserId,userId);
SysAnnouncementSend one = sysAnnouncementSendService.getOne(query);
if(null==one){
SysAnnouncementSend announcementSend = new SysAnnouncementSend();
announcementSend.setAnntId(announcements.get(i).getId());
announcementSend.setUserId(userId);
announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
sysAnnouncementSendService.save(announcementSend);
log.info("listByUser接口新增了SysAnnouncementSendpageSize{}"+pageSize);
SysAnnouncementSend announcementSend = new SysAnnouncementSend();
announcementSend.setAnntId(announcements.get(i).getId());
announcementSend.setUserId(userId);
announcementSend.setReadFlag(CommonConstant.NO_READ_FLAG);
sysAnnouncementSendService.save(announcementSend);
log.info("announcementSend.toString()",announcementSend.toString());
}
//update-end--Author:wangshuai Date:20200803 for 通知公告消息重复LOWCOD-759------------
}
@ -373,7 +379,7 @@ public class SysAnnouncementController {
LambdaQueryWrapper<SysAnnouncement> queryWrapper = new LambdaQueryWrapper<SysAnnouncement>(sysAnnouncement);
//Step.2 AutoPoi 导出Excel
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
queryWrapper.eq(SysAnnouncement::getDelFlag,CommonConstant.DEL_FLAG_0);
queryWrapper.eq(SysAnnouncement::getDelFlag,CommonConstant.DEL_FLAG_0.toString());
List<SysAnnouncement> pageList = sysAnnouncementService.list(queryWrapper);
//导出文件名称
mv.addObject(NormalExcelConstants.FILE_NAME, "系统通告列表");

View File

@ -10,6 +10,7 @@ import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.WebsocketConst;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.message.websocket.WebSocket;
import org.jeecg.modules.system.entity.SysAnnouncementSend;
@ -69,6 +70,11 @@ public class SysAnnouncementSendController {
//排序逻辑 处理
String column = req.getParameter("column");
String order = req.getParameter("order");
//issues/3331 SQL injection vulnerability
SqlInjectionUtil.filterContent(column);
SqlInjectionUtil.filterContent(order);
if(oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
if("asc".equals(order)) {
queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));

View File

@ -9,9 +9,11 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.ImportExcelUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.system.entity.SysCategory;
import org.jeecg.modules.system.model.TreeSelectModel;
@ -65,10 +67,16 @@ public class SysCategoryController {
Result<IPage<SysCategory>> result = new Result<IPage<SysCategory>>();
//--author:os_chengtgen---date:20190804 -----for: 分类字典页面显示错误,issues:377--------start
//QueryWrapper<SysCategory> queryWrapper = QueryGenerator.initQueryWrapper(sysCategory, req.getParameterMap());
QueryWrapper<SysCategory> queryWrapper = new QueryWrapper<SysCategory>();
queryWrapper.eq("pid", sysCategory.getPid());
//--author:os_chengtgen---date:20190804 -----for: 分类字典页面显示错误,issues:377--------end
//--author:liusq---date:20211119 -----for: 【vue3】分类字典页面查询条件配置--------start
QueryWrapper<SysCategory> queryWrapper = QueryGenerator.initQueryWrapper(sysCategory, req.getParameterMap());
String name = sysCategory.getName();
String code = sysCategory.getCode();
//QueryWrapper<SysCategory> queryWrapper = new QueryWrapper<SysCategory>();
if(StringUtils.isBlank(name)&&StringUtils.isBlank(code)){
queryWrapper.eq("pid", sysCategory.getPid());
}
//--author:liusq---date:20211119 -----for: 分类字典页面查询条件配置--------end
//--author:os_chengtgen---date:20190804 -----for:【vue3】 分类字典页面显示错误,issues:377--------end
Page<SysCategory> page = new Page<SysCategory>(pageNo, pageSize);
IPage<SysCategory> pageList = sysCategoryService.page(page, queryWrapper);
@ -215,10 +223,13 @@ public class SysCategoryController {
* @return
*/
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) throws IOException{
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
// 错误信息
List<String> errorMessage = new ArrayList<>();
int successLines = 0, errorLines = 0;
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
MultipartFile file = entity.getValue();// 获取上传文件对象
ImportParams params = new ImportParams();
params.setTitleRows(2);
@ -229,7 +240,8 @@ public class SysCategoryController {
//按照编码长度排序
Collections.sort(listSysCategorys);
log.info("排序后的list====>",listSysCategorys);
for (SysCategory sysCategoryExcel : listSysCategorys) {
for (int i = 0; i < listSysCategorys.size(); i++) {
SysCategory sysCategoryExcel = listSysCategorys.get(i);
String code = sysCategoryExcel.getCode();
if(code.length()>3){
String pCode = sysCategoryExcel.getCode().substring(0,code.length()-3);
@ -242,12 +254,25 @@ public class SysCategoryController {
}else{
sysCategoryExcel.setPid("0");
}
sysCategoryService.save(sysCategoryExcel);
try {
sysCategoryService.save(sysCategoryExcel);
successLines++;
} catch (Exception e) {
errorLines++;
String message = e.getMessage().toLowerCase();
int lineNumber = i + 1;
// 通过索引名判断出错信息
if (message.contains(CommonConstant.SQL_INDEX_UNIQ_CATEGORY_CODE)) {
errorMessage.add("" + lineNumber + " 行:分类编码已经存在,忽略导入。");
} else {
errorMessage.add("" + lineNumber + " 行:未知错误,忽略导入");
log.error(e.getMessage(), e);
}
}
}
return Result.ok("文件导入成功!数据行数:" + listSysCategorys.size());
} catch (Exception e) {
log.error(e.getMessage(), e);
return Result.error("文件导入失败:"+e.getMessage());
errorMessage.add("发生异常:" + e.getMessage());
log.error(e.getMessage(), e);
} finally {
try {
file.getInputStream().close();
@ -256,7 +281,7 @@ public class SysCategoryController {
}
}
}
return Result.error("文件导入失败!");
return ImportExcelUtil.imporReturnRes(errorLines,successLines,errorMessage);
}

View File

@ -112,7 +112,7 @@ public class SysCheckRuleController extends JeecgController<SysCheckRule, ISysCh
*/
@AutoLog(value = "编码校验规则-编辑")
@ApiOperation(value = "编码校验规则-编辑", notes = "编码校验规则-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result edit(@RequestBody SysCheckRule sysCheckRule) {
sysCheckRuleService.updateById(sysCheckRule);
return Result.ok("编辑成功!");

View File

@ -14,6 +14,7 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
@ -57,6 +58,7 @@ public class SysDataSourceController extends JeecgController<SysDataSource, ISys
*/
@AutoLog(value = "多数据源管理-分页列表查询")
@ApiOperation(value = "多数据源管理-分页列表查询", notes = "多数据源管理-分页列表查询")
//@RequiresRoles("admin")
@GetMapping(value = "/list")
public Result<?> queryPageList(
SysDataSource sysDataSource,

View File

@ -198,7 +198,7 @@ public class SysDepartController {
* @return
*/
//@RequiresRoles({"admin"})
@RequestMapping(value = "/edit", method = RequestMethod.PUT)
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
@CacheEvict(value= {CacheConstant.SYS_DEPARTS_CACHE,CacheConstant.SYS_DEPART_IDS_CACHE}, allEntries=true)
public Result<SysDepart> edit(@RequestBody SysDepart sysDepart, HttpServletRequest request) {
String username = JwtUtil.getUserNameByToken(request);
@ -322,7 +322,7 @@ public class SysDepartController {
if(oConvertUtils.isNotEmpty(user.getUserIdentity()) && user.getUserIdentity().equals( CommonConstant.USER_IDENTITY_2 )){
departIds = user.getDepartIds();
}
List<SysDepartTreeModel> treeList = this.sysDepartService.searhBy(keyWord,myDeptSearch,departIds);
List<SysDepartTreeModel> treeList = this.sysDepartService.searchByKeyWord(keyWord,myDeptSearch,departIds);
if (treeList == null || treeList.size() == 0) {
result.setSuccess(false);
result.setMessage("未查询匹配数据!");
@ -363,6 +363,8 @@ public class SysDepartController {
/**
* 通过excel导入数据
* 部门导入方案1: 通过机构编码来计算出部门的父级ID,维护上下级关系;
* 部门导入方案2: 你也可以改造下程序,机构编码直接导入,先不设置父ID;全部导入后,写一个sql,补下父ID;
*
* @param request
* @param response
@ -418,6 +420,11 @@ public class SysDepartController {
sysDepart.setOrgType(sysDepart.getOrgCode().length()/codeLength+"");
//update-end---author:liusq Date:20210223 for批量导入部门以后不能追加下一级部门 #2245------------
sysDepart.setDelFlag(CommonConstant.DEL_FLAG_0.toString());
//update-begin---author:wangshuai ---date:20220105 for[JTC-363]部门导入 机构类别没有时导入失败,赋默认值------------
if(oConvertUtils.isEmpty(sysDepart.getOrgCategory())){
sysDepart.setOrgCategory("1");
}
//update-end---author:wangshuai ---date:20220105 for[JTC-363]部门导入 机构类别没有时导入失败,赋默认值------------
ImportExcelUtil.importDateSaveOne(sysDepart, ISysDepartService.class, errorMessageList, num, CommonConstant.SQL_INDEX_UNIQ_DEPART_ORG_CODE);
num++;
}

View File

@ -97,7 +97,7 @@ public class SysDepartPermissionController extends JeecgController<SysDepartPerm
* @return
*/
@ApiOperation(value="部门权限表-编辑", notes="部门权限表-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody SysDepartPermission sysDepartPermission) {
sysDepartPermissionService.updateById(sysDepartPermission);
return Result.ok("编辑成功!");

View File

@ -114,7 +114,7 @@ public class SysDepartRoleController extends JeecgController<SysDepartRole, ISys
*/
//@RequiresRoles({"admin"})
@ApiOperation(value="部门角色-编辑", notes="部门角色-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody SysDepartRole sysDepartRole) {
sysDepartRoleService.updateById(sysDepartRole);
return Result.ok("编辑成功!");

View File

@ -496,7 +496,7 @@ public class SysDictController {
try {
//导入Excel格式校验看匹配的字段文本概率
Boolean t = ExcelImportCheckUtil.check(file.getInputStream(), SysDictPage.class, params);
if(!t){
if(t!=null && !t){
throw new RuntimeException("导入Excel校验失败 ");
}
List<SysDictPage> list = ExcelImportUtil.importExcel(file.getInputStream(), SysDictPage.class, params);
@ -511,11 +511,22 @@ public class SysDictController {
Integer integer = sysDictService.saveMain(po, list.get(i).getSysDictItemList());
if(integer>0){
successLines++;
}else{
//update-begin---author:wangshuai ---date:20220211 for[JTC-1168]如果字典项值为空,则字典项忽略导入------------
}else if(integer == -1){
errorLines++;
errorMessage.add("字典名称:" + po.getDictName() + ",对应字典列表的字典项值不能为空,忽略导入。");
}else{
//update-end---author:wangshuai ---date:20220211 for[JTC-1168]如果字典项值为空,则字典项忽略导入------------
errorLines++;
int lineNumber = i + 1;
errorMessage.add("" + lineNumber + " 行:字典编码已经存在,忽略导入。");
}
//update-begin---author:wangshuai ---date:20220209 for[JTC-1168]字典编号不能为空------------
if(oConvertUtils.isEmpty(po.getDictCode())){
errorMessage.add("" + lineNumber + " 行:字典编码不能为空,忽略导入。");
}else{
errorMessage.add("" + lineNumber + " 行:字典编码已经存在,忽略导入。");
}
//update-end---author:wangshuai ---date:20220209 for[JTC-1168]字典编号不能为空------------
}
} catch (Exception e) {
errorLines++;
int lineNumber = i + 1;

View File

@ -93,7 +93,7 @@ public class SysFillRuleController extends JeecgController<SysFillRule, ISysFill
*/
@AutoLog(value = "填值规则-编辑")
@ApiOperation(value = "填值规则-编辑", notes = "填值规则-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody SysFillRule sysFillRule) {
sysFillRuleService.updateById(sysFillRule);
return Result.ok("编辑成功!");

View File

@ -59,7 +59,7 @@ public class SysPermissionController {
/**
* 加载数据节点
*
*
* @return
*/
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ -200,7 +200,7 @@ public class SysPermissionController {
/**
* 查询用户拥有的菜单权限和按钮权限
*
*
* @return
*/
@RequestMapping(value = "/getUserPermissionByToken", method = RequestMethod.GET)
@ -261,32 +261,49 @@ public class SysPermissionController {
}
/**
* 【vue3专用】查询用户拥有的按钮/表单访问权限
* @return
* 【vue3专用】获取
* 1、查询用户拥有的按钮/表单访问权限
* 2、所有权限 (菜单权限配置)
* 3、系统安全模式 (开启则online报表的数据源必填)
*/
@RequestMapping(value = "/getPermCode", method = RequestMethod.GET)
public Result<?> getPermCode() {
Result<List<String>> result = new Result<List<String>>();
try {
//直接获取当前用户
// 直接获取当前用户
LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//获取当前用户的权限集合
if (oConvertUtils.isEmpty(loginUser)) {
return Result.error("请登录系统!");
}
// 获取当前用户的权限集合
List<SysPermission> metaList = sysPermissionService.queryByUser(loginUser.getUsername());
// 按钮权限(用户拥有的权限集合)
List<String> codeList = metaList.stream()
.filter((permission) -> CommonConstant.MENU_TYPE_2.equals(permission.getMenuType()) && CommonConstant.STATUS_1.equals(permission.getStatus()))
.collect(ArrayList::new, (list, permission) -> list.add(permission.getPerms()), ArrayList::addAll);
//
JSONArray authArray = new JSONArray();
this.getAuthJsonArray(authArray, metaList);
// 查询所有的权限
LambdaQueryWrapper<SysPermission> query = new LambdaQueryWrapper<>();
query.eq(SysPermission::getDelFlag, CommonConstant.DEL_FLAG_0);
query.eq(SysPermission::getMenuType, CommonConstant.MENU_TYPE_2);
List<SysPermission> allAuthList = sysPermissionService.list(query);
JSONArray allAuthArray = new JSONArray();
this.getAllAuthJsonArray(allAuthArray, allAuthList);
JSONObject result = new JSONObject();
// 所拥有的权限编码
result.put("codeList", codeList);
//按钮权限(用户拥有的权限集合)
List<String> authList = metaList.stream()
.filter((permission) -> permission.getMenuType().equals(CommonConstant.MENU_TYPE_2)
&& CommonConstant.STATUS_1.equals(permission.getStatus())
)
.collect(() -> new ArrayList<String>(),
(list, permission) -> list.add(permission.getPerms()),
(list1, list2) -> list1.addAll(list2)
);
result.setResult(authList);
result.put("auth", authArray);
//全部权限配置集合(按钮权限,访问权限)
result.put("allAuth", allAuthArray);
// 系统安全模式
result.put("sysSafeMode", jeeccgBaseConfig.getSafeMode());
return Result.OK(result);
} catch (Exception e) {
result.error500("查询失败:" + e.getMessage());
log.error(e.getMessage(), e);
return Result.error("查询失败:" + e.getMessage());
}
return result;
}
/**
@ -374,7 +391,7 @@ public class SysPermissionController {
/**
* 获取全部的权限树
*
*
* @return
*/
@RequestMapping(value = "/queryTreeList", method = RequestMethod.GET)
@ -720,7 +737,7 @@ public class SysPermissionController {
/**
* 判断是否外网URL 例如: http://localhost:8080/jeecg-boot/swagger-ui.html#/ 支持特殊格式: {{
* window._CONFIG['domianURL'] }}/druid/ {{ JS代码片段 }}前台解析会自动执行JS代码片段
*
*
* @return
*/
private boolean isWWWHttpUrl(String url) {
@ -733,7 +750,7 @@ public class SysPermissionController {
/**
* 通过URL生成路由name去掉URL前缀斜杠替换内容中的斜杠/’为- 举例: URL = /isystem/role RouteName =
* isystem-role
*
*
* @return
*/
private String urlToRouteName(String url) {
@ -753,7 +770,7 @@ public class SysPermissionController {
/**
* 根据菜单id来获取其对应的权限数据
*
*
* @param sysPermissionDataRule
* @return
*/
@ -768,7 +785,7 @@ public class SysPermissionController {
/**
* 添加菜单权限数据
*
*
* @param sysPermissionDataRule
* @return
*/
@ -803,7 +820,7 @@ public class SysPermissionController {
/**
* 删除菜单权限数据
*
*
* @param id
* @return
*/
@ -823,7 +840,7 @@ public class SysPermissionController {
/**
* 查询菜单权限数据
*
*
* @param sysPermissionDataRule
* @return
*/

View File

@ -8,10 +8,12 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.ImportExcelUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.quartz.service.IQuartzJobService;
@ -208,10 +210,11 @@ public class SysPositionController {
//Step.2 AutoPoi 导出Excel
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
List<SysPosition> pageList = sysPositionService.list(queryWrapper);
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//导出文件名称
mv.addObject(NormalExcelConstants.FILE_NAME, "职务表列表");
mv.addObject(NormalExcelConstants.CLASS, SysPosition.class);
mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("职务表列表数据", "导出人:Jeecg", "导出信息"));
mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("职务表列表数据", "导出人:"+user.getRealname(),"导出信息"));
mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
return mv;
}

View File

@ -6,9 +6,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.PermissionData;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.system.entity.SysTenant;
import org.jeecg.modules.system.service.ISysTenantService;
@ -16,9 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.*;
/**
* 租户配置信息
@ -132,11 +132,11 @@ public class SysTenantController {
}else {
String[] ls = ids.split(",");
// 过滤掉已被引用的租户
List<String> idList = new ArrayList<>();
List<Integer> idList = new ArrayList<>();
for (String id : ls) {
int userCount = sysTenantService.countUserLinkTenant(id);
if (userCount == 0) {
idList.add(id);
idList.add(Integer.parseInt(id));
}
}
if (idList.size() > 0) {
@ -190,4 +190,32 @@ public class SysTenantController {
result.setResult(ls);
return result;
}
/**
* 查询当前用户的所有有效租户 【当前用于vue3版本】
* @return
*/
@RequestMapping(value = "/getCurrentUserTenant", method = RequestMethod.GET)
public Result<Map<String,Object>> getCurrentUserTenant() {
Result<Map<String,Object>> result = new Result<Map<String,Object>>();
try {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String tenantIds = sysUser.getRelTenantIds();
Map<String,Object> map = new HashMap<String,Object>();
if (oConvertUtils.isNotEmpty(tenantIds)) {
List<Integer> tenantIdList = new ArrayList<>();
for(String id: tenantIds.split(",")){
tenantIdList.add(Integer.valueOf(id));
}
// 该方法仅查询有效的租户如果返回0个就说明所有的租户均无效。
List<SysTenant> tenantList = sysTenantService.queryEffectiveTenant(tenantIdList);
map.put("list", tenantList);
}
result.setSuccess(true);
result.setResult(map);
}catch(Exception e) {
log.error(e.getMessage(), e);
result.error500("查询失败!");
}
return result;
}
}

View File

@ -108,12 +108,38 @@ public class SysUserController {
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,HttpServletRequest req) {
Result<IPage<SysUser>> result = new Result<IPage<SysUser>>();
QueryWrapper<SysUser> queryWrapper = QueryGenerator.initQueryWrapper(user, req.getParameterMap());
//TODO 外部模拟登陆临时账号,列表不显示
//update-begin-Author:wangshuai--Date:20211119--for:【vue3】通过部门id查询用户通过code查询id
//部门ID
String departId = req.getParameter("departId");
if(oConvertUtils.isNotEmpty(departId)){
LambdaQueryWrapper<SysUserDepart> query = new LambdaQueryWrapper<>();
query.eq(SysUserDepart::getDepId,departId);
List<SysUserDepart> list = sysUserDepartService.list(query);
List<String> userIds = list.stream().map(SysUserDepart::getUserId).collect(Collectors.toList());
queryWrapper.in("id",userIds);
}
//用户ID
String code = req.getParameter("code");
if(oConvertUtils.isNotEmpty(code)){
queryWrapper.in("id",Arrays.asList(code.split(",")));
pageSize = code.split(",").length;
}
//update-end-Author:wangshuai--Date:20211119--for:【vue3】通过部门id查询用户通过code查询id
//update-begin-author:taoyan--date:20220104--for: JTC-372 【用户冻结问题】 online授权、用户组件选择用户都能看到被冻结的用户
String status = req.getParameter("status");
if(oConvertUtils.isNotEmpty(status)){
queryWrapper.eq("status", Integer.parseInt(status));
}
//update-end-author:taoyan--date:20220104--for: JTC-372 【用户冻结问题】 online授权、用户组件选择用户都能看到被冻结的用户
//TODO 外部模拟登陆临时账号,列表不显示
queryWrapper.ne("username","_reserve_user_external");
Page<SysUser> page = new Page<SysUser>(pageNo, pageSize);
IPage<SysUser> pageList = sysUserService.page(page, queryWrapper);
//批量查询用户的所属部门
//批量查询用户的所属部门
//step.1 先拿到全部的 useids
//step.2 通过 useids一次性查询用户的所属部门名字
List<String> userIds = pageList.getRecords().stream().map(SysUser::getId).collect(Collectors.toList());
@ -492,6 +518,8 @@ public class SysUserController {
errorMessage.add("" + lineNumber + " 行:手机号已经存在,忽略导入。");
} else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER_EMAIL)) {
errorMessage.add("" + lineNumber + " 行:电子邮件已经存在,忽略导入。");
} else if (message.contains(CommonConstant.SQL_INDEX_UNIQ_SYS_USER)) {
errorMessage.add("" + lineNumber + " 行:违反表唯一性约束。");
} else {
errorMessage.add("" + lineNumber + " 行:未知错误,忽略导入");
log.error(e.getMessage(), e);
@ -1187,7 +1215,7 @@ public class SysUserController {
* @param jsonObject
* @return
*/
@RequestMapping(value = "/appEdit", method = RequestMethod.PUT)
@RequestMapping(value = "/appEdit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<SysUser> appEdit(HttpServletRequest request,@RequestBody JSONObject jsonObject) {
Result<SysUser> result = new Result<SysUser>();
try {
@ -1305,7 +1333,9 @@ public class SysUserController {
* @return
*/
@GetMapping("/appQueryUser")
public Result<List<SysUser>> appQueryUser(@RequestParam(name = "keyword", required = false) String keyword) {
public Result<List<SysUser>> appQueryUser(@RequestParam(name = "keyword", required = false) String keyword,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
Result<List<SysUser>> result = new Result<List<SysUser>>();
LambdaQueryWrapper<SysUser> queryWrapper =new LambdaQueryWrapper<SysUser>();
//TODO 外部模拟登陆临时账号,列表不显示
@ -1313,18 +1343,19 @@ public class SysUserController {
if(StringUtils.isNotBlank(keyword)){
queryWrapper.and(i -> i.like(SysUser::getUsername, keyword).or().like(SysUser::getRealname, keyword));
}
List<SysUser> list = sysUserService.list(queryWrapper);
Page<SysUser> page = new Page<>(pageNo, pageSize);
IPage<SysUser> pageList = this.sysUserService.page(page, queryWrapper);
//批量查询用户的所属部门
//step.1 先拿到全部的 useids
//step.2 通过 useids一次性查询用户的所属部门名字
List<String> userIds = list.stream().map(SysUser::getId).collect(Collectors.toList());
List<String> userIds = pageList.getRecords().stream().map(SysUser::getId).collect(Collectors.toList());
if(userIds!=null && userIds.size()>0){
Map<String,String> useDepNames = sysUserService.getDepNamesByUserIds(userIds);
list.forEach(item->{
pageList.getRecords().forEach(item->{
item.setOrgCodeTxt(useDepNames.get(item.getId()));
});
}
result.setResult(list);
result.setResult(pageList.getRecords());
return result;
}
@ -1374,6 +1405,9 @@ public class SysUserController {
@GetMapping("/getMultiUser")
public List<SysUser> getMultiUser(SysUser sysUser){
QueryWrapper<SysUser> queryWrapper = QueryGenerator.initQueryWrapper(sysUser, null);
//update-begin---author:wangshuai ---date:20220104 for[JTC-297]已冻结用户仍可设置为代理人------------
queryWrapper.eq("status",Integer.parseInt(CommonConstant.STATUS_1));
//update-end---author:wangshuai ---date:20220104 for[JTC-297]已冻结用户仍可设置为代理人------------
List<SysUser> ls = this.sysUserService.list(queryWrapper);
for(SysUser user: ls){
user.setPassword(null);

View File

@ -1,8 +1,7 @@
package org.jeecg.modules.system.controller;
import java.util.*;
import javax.annotation.Resource;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
@ -21,9 +20,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @Description: 在线用户
@ -63,8 +64,20 @@ public class SysUserOnlineController {
online.setToken(token);
//TODO 改成一次性查询
LoginUser loginUser = sysBaseAPI.getUserByName(JwtUtil.getUsername(token));
BeanUtils.copyProperties(loginUser, online);
onlineList.add(online);
if (loginUser != null) {
//update-begin---author:wangshuai ---date:20220104 for[JTC-382]在线用户查询无效------------
//验证用户名是否与传过来的用户名相同
boolean isMatchUsername=true;
//判断用户名是否为空并且当前循环的用户不包含传过来的用户名那么就设成false
if(oConvertUtils.isNotEmpty(username) && !loginUser.getUsername().contains(username)){
isMatchUsername = false;
}
if(isMatchUsername){
BeanUtils.copyProperties(loginUser, online);
onlineList.add(online);
}
//update-end---author:wangshuai ---date:20220104 for[JTC-382]在线用户查询无效------------
}
}
}
Collections.reverse(onlineList);

View File

@ -91,8 +91,12 @@ public class ThirdAppController {
@GetMapping("/sync/wechatEnterprise/depart/toApp")
public Result syncWechatEnterpriseDepartToApp(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isWechatEnterpriseEnabled()) {
boolean flag = wechatEnterpriseService.syncLocalDepartmentToThirdApp(ids);
return flag ? Result.OK("同步成功", null) : Result.error("同步失败");
SyncInfoVo syncInfo = wechatEnterpriseService.syncLocalDepartmentToThirdApp(ids);
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", null);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("企业微信同步功能已禁用");
}
@ -125,8 +129,12 @@ public class ThirdAppController {
@GetMapping("/sync/dingtalk/depart/toApp")
public Result syncDingtalkDepartToApp(@RequestParam(value = "ids", required = false) String ids) {
if (thirdAppConfig.isDingtalkEnabled()) {
boolean flag = dingtalkService.syncLocalDepartmentToThirdApp(ids);
return flag ? Result.OK("同步成功", null) : Result.error("同步失败");
SyncInfoVo syncInfo = dingtalkService.syncLocalDepartmentToThirdApp(ids);
if (syncInfo.getFailInfo().size() == 0) {
return Result.OK("同步成功", null);
} else {
return Result.error("同步失败", syncInfo);
}
}
return Result.error("钉钉同步功能已禁用");
}

View File

@ -45,7 +45,7 @@ public class SysDepart implements Serializable {
/**描述*/
@Excel(name="描述",width=15)
private String description;
/**机构类别 1公司2组织机构2岗位*/
/**机构类别 1=公司2=组织机构,3=岗位*/
@Excel(name="机构类别",width=15,dicCode="org_category")
private String orgCategory;
/**机构类型*/

View File

@ -54,7 +54,6 @@ public class SysPosition {
/**
* 公司id
*/
@Excel(name = "公司id", width = 15)
@ApiModelProperty(value = "公司id")
private java.lang.String companyId;
/**
@ -84,7 +83,6 @@ public class SysPosition {
/**
* 组织机构编码
*/
@Excel(name = "组织机构编码", width = 15)
@ApiModelProperty(value = "组织机构编码")
private java.lang.String sysOrgCode;
}

View File

@ -3,12 +3,14 @@ package org.jeecg.modules.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
/**
* @Description: 第三方登录账号表
@ -25,7 +27,7 @@ public class SysThirdAccount {
/**编号*/
@TableId(type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "编号")
@ApiModelProperty(value = "编号")
private java.lang.String id;
/**第三方登录id*/
@Excel(name = "第三方登录id", width = 15)
@ -59,4 +61,20 @@ public class SysThirdAccount {
@Excel(name = "第三方用户账号", width = 15)
@ApiModelProperty(value = "第三方用户账号")
private java.lang.String thirdUserId;
/**创建人*/
@Excel(name = "创建人", width = 15)
private java.lang.String createBy;
/**创建日期*/
@Excel(name = "创建日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private java.util.Date createTime;
/**修改人*/
@Excel(name = "修改人", width = 15)
private java.lang.String updateBy;
/**修改日期*/
@Excel(name = "修改日期", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private java.util.Date updateTime;
}

View File

@ -193,4 +193,15 @@ public interface SysDictMapper extends BaseMapper<SysDict> {
*/
@Deprecated
List<DictModel> queryAllTableDictItems(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("filterSql") String filterSql);
/**
* 查询字典表的数据
* @param table 表名
* @param text 显示字段名
* @param code 存储字段名
* @param filterSql 条件sql
* @param codeValues 存储字段值 作为查询条件in
* @return
*/
List<DictModel> queryTableDictByKeysAndFilterSql(@Param("table") String table, @Param("text") String text, @Param("code") String code, @Param("filterSql") String filterSql, @Param("codeValues") List<String> codeValues);
}

View File

@ -134,19 +134,24 @@
${pidField} as parentId
from ${table}
where
<choose>
<when test="pid != null and pid != ''">
${pidField} = #{pid}
</when>
<otherwise>
(${pidField} = '' OR ${pidField} IS NULL)
</otherwise>
</choose>
<!-- udapte-begin-author:sunjianlei date:20220110 for: 【JTC-597】自定义树查询条件查不出数据 -->
<if test="query == null">
<choose>
<when test="pid != null and pid != ''">
${pidField} = #{pid}
</when>
<otherwise>
(${pidField} = '' OR ${pidField} IS NULL)
</otherwise>
</choose>
</if>
<if test="query!= null">
1 = 1
<foreach collection="query.entrySet()" item="value" index="key" >
and ${key} = #{value}
and ${key} LIKE #{value}
</foreach>
</if>
<!-- udapte-end-author:sunjianlei date:20220110 for: 【JTC-597】自定义树查询条件查不出数据 -->
</select>
@ -179,5 +184,18 @@
</select>
<!-- 查询字典表的数据 支持设置过滤条件、设置存储值作为in查询条件 -->
<select id="queryTableDictByKeysAndFilterSql" parameterType="String" resultType="org.jeecg.common.system.vo.DictModel">
select ${text} as "text", ${code} as "value" from ${table} where ${code} IN (
<foreach item="key" collection="codeValues" separator=",">
#{key}
</foreach>
)
<if test="filterSql != null and filterSql != ''">
and ${filterSql}
</if>
</select>
</mapper>

View File

@ -14,7 +14,9 @@
join sys_depart c on b.dep_id = c.id
where a.del_flag = 0 and c.org_code like '${orgCode}%'
<if test="realname!=null and realname!=''">
and a.realname like '%${realname}%'
<!-- update by sunjianlei 20220119【#3348】SQL injection exists in /sys/user/queryUserByDepId -->
<bind name="bindRealname" value="'%'+realname+'%'"/>
and a.realname like #{bindRealname}
</if>
</select>
@ -23,12 +25,14 @@
select a.*, c.depart_name as org_code_txt from sys_user a
join sys_user_depart b on b.user_id = a.id
join sys_depart c on b.dep_id = c.id
where a.del_flag = 0 and c.org_code like '${orgCode}%'
where a.del_flag = 0 and a.status = 1 and c.org_code like '${orgCode}%'
<if test="username!=null and username!=''">
and a.username like '%${username}%'
<bind name="bindUsername" value="'%'+username+'%'"/>
and a.username like #{bindUsername}
</if>
<if test="realname!=null and realname!=''">
and a.realname like '%${realname}%'
<bind name="bindRealname" value="'%'+realname+'%'"/>
and a.realname like #{bindRealname}
</if>
</select>
</mapper>

View File

@ -1,15 +1,12 @@
package org.jeecg.modules.system.model;
import java.io.Serializable;
import org.springframework.format.annotation.DateTimeFormat;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
/**
* @Description: 用户通告阅读标记表

View File

@ -2,6 +2,7 @@ package org.jeecg.modules.system.rule;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.handler.IFillRuleHandler;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.YouBianCodeUtil;
@ -16,12 +17,14 @@ import java.util.List;
* @Date 2019/12/9 11:32
* @Description: 分类字典编码生成规则
*/
@Slf4j
public class CategoryCodeRule implements IFillRuleHandler {
public static final String ROOT_PID_VALUE = "0";
@Override
public Object execute(JSONObject params, JSONObject formData) {
log.info("系统自定义编码规则[category_code_rule]params{} formData {}", params, formData);
String categoryPid = ROOT_PID_VALUE;
String categoryCode = null;

View File

@ -67,7 +67,7 @@ public interface ISysDepartService extends IService<SysDepart>{
* @param keyWord
* @return
*/
List<SysDepartTreeModel> searhBy(String keyWord,String myDeptSearch,String departIds);
List<SysDepartTreeModel> searchByKeyWord(String keyWord,String myDeptSearch,String departIds);
/**
* 根据部门id删除并删除其可能存在的子级部门

View File

@ -14,7 +14,7 @@ public interface ISysTenantService extends IService<SysTenant> {
* @param idList
* @return
*/
List<SysTenant> queryEffectiveTenant(Collection<String> idList);
List<SysTenant> queryEffectiveTenant(Collection<Integer> idList);
/**
* 返回某个租户被多少个用户引用了

View File

@ -21,7 +21,7 @@ public interface IThirdAppService {
*
* @return 成功返回true
*/
boolean syncLocalDepartmentToThirdApp(String ids);
SyncInfoVo syncLocalDepartmentToThirdApp(String ids);
/**
* 将第三方App部门同步到本地<br>

View File

@ -146,7 +146,9 @@ public class SysBaseApiImpl implements ISysBaseAPI {
//通过自定义URL匹配规则 获取菜单(实现通过菜单配置数据权限规则,实际上针对获取数据接口进行数据规则控制)
String userMatchUrl = UrlMatchEnum.getMatchResultByUrl(requestPath);
LambdaQueryWrapper<SysPermission> queryQserMatch = new LambdaQueryWrapper<SysPermission>();
queryQserMatch.eq(SysPermission::getMenuType, 1);
// update-begin-author:taoyan date:20211027 for: online菜单如果配置成一级菜单 权限查询不到 取消menuType = 1
//queryQserMatch.eq(SysPermission::getMenuType, 1);
// update-end-author:taoyan date:20211027 for: online菜单如果配置成一级菜单 权限查询不到 取消menuType = 1
queryQserMatch.eq(SysPermission::getDelFlag, 0);
queryQserMatch.eq(SysPermission::getUrl, userMatchUrl);
if(oConvertUtils.isNotEmpty(userMatchUrl)){

View File

@ -69,7 +69,7 @@ public class SysDepartPermissionServiceImpl extends ServiceImpl<SysDepartPermiss
@Override
public List<SysPermissionDataRule> getPermRuleListByDeptIdAndPermId(String departId, String permissionId) {
SysDepartPermission departPermission = this.getOne(new QueryWrapper<SysDepartPermission>().lambda().eq(SysDepartPermission::getDepartId, departId).eq(SysDepartPermission::getPermissionId, permissionId));
if(departPermission != null){
if(departPermission != null && oConvertUtils.isNotEmpty(departPermission.getDataRuleIds())){
LambdaQueryWrapper<SysPermissionDataRule> query = new LambdaQueryWrapper<SysPermissionDataRule>();
query.in(SysPermissionDataRule::getId, Arrays.asList(departPermission.getDataRuleIds().split(",")));
query.orderByDesc(SysPermissionDataRule::getCreateTime);

View File

@ -300,7 +300,7 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
* </p>
*/
@Override
public List<SysDepartTreeModel> searhBy(String keyWord,String myDeptSearch,String departIds) {
public List<SysDepartTreeModel> searchByKeyWord(String keyWord,String myDeptSearch,String departIds) {
LambdaQueryWrapper<SysDepart> query = new LambdaQueryWrapper<SysDepart>();
List<SysDepartTreeModel> newList = new ArrayList<>();
//myDeptSearch不为空时为我的部门搜索只搜索所负责部门
@ -311,9 +311,15 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
}
//根据部门id获取所负责部门
String[] codeArr = this.getMyDeptParentOrgCode(departIds);
for(int i=0;i<codeArr.length;i++){
query.or().likeRight(SysDepart::getOrgCode,codeArr[i]);
//update-begin-author:taoyan date:20220104 for:/issues/3311 当用户属于两个部门的时候,且这两个部门没有上下级关系,我的部门-部门名称查询条件模糊搜索失效!
if (codeArr != null && codeArr.length > 0) {
query.nested(i -> {
for (String s : codeArr) {
i.or().likeRight(SysDepart::getOrgCode, s);
}
});
}
//update-end-author:taoyan date:20220104 for:/issues/3311 当用户属于两个部门的时候,且这两个部门没有上下级关系,我的部门-部门名称查询条件模糊搜索失效!
query.eq(SysDepart::getDelFlag, CommonConstant.DEL_FLAG_0.toString());
}
query.like(SysDepart::getDepartName, keyWord);
@ -499,7 +505,7 @@ public class SysDepartServiceImpl extends ServiceImpl<SysDepartMapper, SysDepart
}
};
LambdaQueryWrapper<SysDepart> lqw=new LambdaQueryWrapper();
lqw.eq(true,SysDepart::getDelFlag,CommonConstant.DEL_FLAG_0);
lqw.eq(true,SysDepart::getDelFlag,CommonConstant.DEL_FLAG_0.toString());
lqw.func(square);
lqw.orderByDesc(SysDepart::getDepartOrder);
List<SysDepart> list = list(lqw);

View File

@ -163,7 +163,15 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
@Override
public List<DictModel> queryTableDictTextByKeys(String table, String text, String code, List<String> keys) {
return sysDictMapper.queryTableDictTextByKeys(table, text, code, keys);
//update-begin-author:taoyan date:20220113 for: @dict注解支持 dicttable 设置where条件
String filterSql = null;
if(table.toLowerCase().indexOf("where")>0){
String[] arr = table.split(" (?i)where ");
table = arr[0];
filterSql = arr[1];
}
return sysDictMapper.queryTableDictByKeysAndFilterSql(table, text, code, filterSql, keys);
//update-end-author:taoyan date:20220113 for: @dict注解支持 dicttable 设置where条件
}
@Override
@ -225,6 +233,11 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
insert = sysDictMapper.insert(sysDict);
if (sysDictItemList != null) {
for (SysDictItem entity : sysDictItemList) {
//update-begin---author:wangshuai ---date:20220211 for[JTC-1168]如果字典项值为空,则字典项忽略导入------------
if(oConvertUtils.isEmpty(entity.getItemValue())){
return -1;
}
//update-end---author:wangshuai ---date:20220211 for[JTC-1168]如果字典项值为空,则字典项忽略导入------------
entity.setDictId(sysDict.getId());
entity.setStatus(1);
sysDictItemMapper.insert(entity);
@ -255,7 +268,7 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
public List<DictModel> queryLittleTableDictItems(String table, String text, String code, String condition, String keyword, int pageSize) {
Page<DictModel> page = new Page<DictModel>(1, pageSize);
page.setSearchCount(false);
String filterSql = getFilterSql(text, code, condition, keyword);
String filterSql = getFilterSql(table, text, code, condition, keyword);
IPage<DictModel> pageList = baseMapper.queryTableDictWithFilter(page, table, text, code, filterSql);
return pageList.getRecords();
}
@ -268,12 +281,19 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
* @param keyword
* @return
*/
private String getFilterSql(String text, String code, String condition, String keyword){
private String getFilterSql(String table, String text, String code, String condition, String keyword){
String keywordSql = null, filterSql = "", sql_where = " where ";
// update-begin-author:sunjianlei date:20220112 for: 【JTC-631】判断如果 table 携带了 where 条件,那么就使用 and 查询,防止报错
if (table.toLowerCase().contains(" where ")) {
sql_where = " and ";
}
// update-end-author:sunjianlei date:20220112 for: 【JTC-631】判断如果 table 携带了 where 条件,那么就使用 and 查询,防止报错
if(oConvertUtils.isNotEmpty(keyword)){
// 判断是否是多选
if (keyword.contains(",")) {
String inKeywords = "\"" + keyword.replaceAll(",", "\",\"") + "\"";
//update-begin--author:scott--date:20220105--forJTC-529【表单设计器】 编辑页面报错in参数采用双引号导致 ----
String inKeywords = "'" + String.join("','", keyword.split(",")) + "'";
//update-end--author:scott--date:20220105--forJTC-529【表单设计器】 编辑页面报错in参数采用双引号导致----
keywordSql = "(" + text + " in (" + inKeywords + ") or " + code + " in (" + inKeywords + "))";
} else {
keywordSql = "("+text + " like '%"+keyword+"%' or "+ code + " like '%"+keyword+"%')";
@ -290,14 +310,20 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
}
@Override
public List<DictModel> queryAllTableDictItems(String table, String text, String code, String condition, String keyword) {
String filterSql = getFilterSql(text, code, condition, keyword);
String filterSql = getFilterSql(table, text, code, condition, keyword);
List<DictModel> ls = baseMapper.queryAllTableDictItems(table, text, code, filterSql);
return ls;
}
@Override
public List<TreeSelectModel> queryTreeList(Map<String, String> query,String table, String text, String code, String pidField,String pid,String hasChildField) {
return baseMapper.queryTreeList(query,table, text, code, pidField, pid,hasChildField);
List<TreeSelectModel> result = baseMapper.queryTreeList(query, table, text, code, pidField, pid, hasChildField);
// udapte-begin-author:sunjianlei date:20220110 for: 【JTC-597】如果 query 有值,就不允许展开子节点
if (query != null) {
result.forEach(r -> r.setLeaf(true));
}
return result;
// udapte-end-author:sunjianlei date:20220110 for: 【JTC-597】如果 query 有值,就不允许展开子节点
}
@Override

View File

@ -24,10 +24,10 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
ISysUserService userService;
@Override
public List<SysTenant> queryEffectiveTenant(Collection<String> idList) {
public List<SysTenant> queryEffectiveTenant(Collection<Integer> idList) {
LambdaQueryWrapper<SysTenant> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(SysTenant::getId, idList);
queryWrapper.eq(SysTenant::getStatus, CommonConstant.STATUS_1);
queryWrapper.eq(SysTenant::getStatus, Integer.valueOf(CommonConstant.STATUS_1));
//此处查询忽略时间条件
return super.list(queryWrapper);
}
@ -50,7 +50,7 @@ public class SysTenantServiceImpl extends ServiceImpl<SysTenantMapper, SysTenant
if (userCount > 0) {
throw new JeecgBootException("该租户已被引用,无法删除!");
}
return super.removeById(id);
return super.removeById(Integer.parseInt(id));
}
}

View File

@ -2,6 +2,7 @@ package org.jeecg.modules.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.PasswordUtil;
@ -30,6 +31,7 @@ import java.util.List;
* @Version: V1.0
*/
@Service
@Slf4j
public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMapper, SysThirdAccount> implements ISysThirdAccountService {
@Autowired
@ -116,6 +118,7 @@ public class SysThirdAccountServiceImpl extends ServiceImpl<SysThirdAccountMappe
@Override
public SysThirdAccount getOneBySysUserId(String sysUserId, String thirdType) {
LambdaQueryWrapper<SysThirdAccount> queryWrapper = new LambdaQueryWrapper<>();
log.info("getSysUserId: {} ,getThirdType: {}",sysUserId,thirdType);
queryWrapper.eq(SysThirdAccount::getSysUserId, sysUserId);
queryWrapper.eq(SysThirdAccount::getThirdType, thirdType);
return super.getOne(queryWrapper);

View File

@ -1,15 +1,10 @@
package org.jeecg.modules.system.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysUser;
@ -22,8 +17,11 @@ import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* <P>
@ -128,6 +126,9 @@ public class SysUserDepartServiceImpl extends ServiceImpl<SysUserDepartMapper, S
Page<SysUser> page = new Page<SysUser>(pageNo, pageSize);
if(oConvertUtils.isEmpty(departId)){
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<>();
//update-begin---author:wangshuai ---date:20220104 for[JTC-297]已冻结用户仍可设置为代理人------------
query.eq(SysUser::getStatus,Integer.parseInt(CommonConstant.STATUS_1));
//update-end---author:wangshuai ---date:20220104 for[JTC-297]已冻结用户仍可设置为代理人------------
if(oConvertUtils.isNotEmpty(username)){
query.like(SysUser::getUsername, username);
}

View File

@ -399,6 +399,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
}
@Override
@CacheEvict(value={CacheConstant.SYS_USERS_CACHE}, allEntries=true)
public boolean revertLogicDeleted(List<String> userIds, SysUser updateEntity) {
String ids = String.format("'%s'", String.join("','", userIds));
return userMapper.revertLogicDeleted(ids, updateEntity) > 0;

View File

@ -85,18 +85,31 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
return null;
}
// update2022-1-21updateBysunjianlei; for 【JTC-704】【钉钉】部门同步成功实际没成后台提示ip白名单
@Override
public boolean syncLocalDepartmentToThirdApp(String ids) {
public SyncInfoVo syncLocalDepartmentToThirdApp(String ids) {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
return false;
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
// 获取【钉钉】所有的部门
List<Department> departments = JdtDepartmentAPI.listAll(accessToken);
List<Response<Department>> departments = JdtDepartmentAPI.listAllResponse(accessToken);
// 删除钉钉有但本地没有的部门(以本地部门数据为主)(钉钉不能创建同名部门,只能先删除)
List<SysDepart> sysDepartList = sysDepartService.list();
for1:
for (Department department : departments) {
for (Response<Department> departmentRes : departments) {
// 判断部门是否查询成功
if (!departmentRes.isSuccess()) {
syncInfo.addFailInfo(departmentRes.getErrmsg());
// 88 是 ip 不在白名单的错误码,如果遇到此错误码,后面的操作都可以不用进行了,因为肯定都是失败的
if (new Integer(88).equals(departmentRes.getErrcode())) {
return syncInfo;
}
continue;
}
Department department = departmentRes.getResult();
for (SysDepart depart : sysDepartList) {
// id相同代表已存在不删除
String sourceIdentifier = department.getSource_identifier();
@ -124,24 +137,34 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
Department parent = new Department();
parent.setDept_id(1);
// 递归同步部门
departments = JdtDepartmentAPI.listAll(accessToken);
this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken);
return true;
departments = JdtDepartmentAPI.listAllResponse(accessToken);
this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken, syncInfo);
return syncInfo;
}
// 递归同步部门到本地
public void syncDepartmentRecursion(List<SysDepartTreeModel> sysDepartsTree, List<Department> departments, Department parent, String accessToken) {
public void syncDepartmentRecursion(List<SysDepartTreeModel> sysDepartsTree, List<Response<Department>> departments, Department parent, String accessToken, SyncInfoVo syncInfo) {
if (sysDepartsTree != null && sysDepartsTree.size() != 0) {
for1:
for (SysDepartTreeModel depart : sysDepartsTree) {
for (Department department : departments) {
for (Response<Department> departmentRes : departments) {
// 判断部门是否查询成功
if (!departmentRes.isSuccess()) {
syncInfo.addFailInfo(departmentRes.getErrmsg());
continue;
}
Department department = departmentRes.getResult();
// id相同代表已存在执行修改操作
String sourceIdentifier = department.getSource_identifier();
if (sourceIdentifier != null && sourceIdentifier.equals(depart.getId())) {
this.sysDepartToDtDepartment(depart, department, parent.getDept_id());
JdtDepartmentAPI.update(department, accessToken);
// 紧接着同步子级
this.syncDepartmentRecursion(depart.getChildren(), departments, department, accessToken);
Response<JSONObject> response = JdtDepartmentAPI.update(department, accessToken);
if (response.isSuccess()) {
// 紧接着同步子级
this.syncDepartmentRecursion(depart.getChildren(), departments, department, accessToken, syncInfo);
}
// 收集错误信息
this.syncDepartCollectErrInfo(response, depart, syncInfo);
// 跳出外部循环
continue for1;
}
@ -154,10 +177,10 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
Department newParent = new Department();
newParent.setDept_id(response.getResult());
// 紧接着同步子级
this.syncDepartmentRecursion(depart.getChildren(), departments, newParent, accessToken);
this.syncDepartmentRecursion(depart.getChildren(), departments, newParent, accessToken, syncInfo);
}
// 收集错误信息
// this.syncUserCollectErrInfo(errCode, sysUser, errInfo);
this.syncDepartCollectErrInfo(response, depart, syncInfo);
}
}
}
@ -209,6 +232,11 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
SysDepart newSysDepart = this.dtDepartmentToSysDepart(departmentTree, null);
if (sysParentId != null) {
newSysDepart.setParentId(sysParentId);
// 2 = 组织机构
newSysDepart.setOrgCategory("2");
} else {
// 1 = 公司
newSysDepart.setOrgCategory("1");
}
try {
sysDepartService.saveDepartData(newSysDepart, username);
@ -246,6 +274,20 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
return false;
}
/**
* 【同步部门】收集同步过程中的错误信息
*/
private boolean syncDepartCollectErrInfo(Response<?> response, SysDepartTreeModel depart, SyncInfoVo syncInfo) {
if (!response.isSuccess()) {
String str = String.format("部门 %s(%s) 同步失败!错误码:%s——%s", depart.getDepartName(), depart.getOrgCode(), response.getErrcode(), response.getErrmsg());
syncInfo.addFailInfo(str);
return false;
} else {
String str = String.format("部门户 %s(%s) 同步成功!", depart.getDepartName(), depart.getOrgCode());
syncInfo.addSuccessInfo(str);
return true;
}
}
@Override
public SyncInfoVo syncLocalUserToThirdApp(String ids) {
@ -279,7 +321,7 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
/*
* 判断是否同步过的逻辑:
* 1. 查询 sys_third_account第三方账号表是否有数据如果有代表已同步
* 2. 本地表里没有就先用手机号判断不通过再用username判断。
* 2. 本地表里没有就先用手机号判断不通过再用username(用户账号)判断。
*/
SysThirdAccount sysThirdAccount = sysThirdAccountService.getOneBySysUserId(sysUser.getId(), THIRD_TYPE);
if (sysThirdAccount != null && oConvertUtils.isNotEmpty(sysThirdAccount.getThirdUserId())) {
@ -528,6 +570,12 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
// update-begin--Author:liusq Date:20210713 for钉钉同步到本地的人员没有状态导致同步之后无法登录 #I3ZC2L
sysUser.setStatus(1);
// update-end--Author:liusq Date:20210713 for钉钉同步到本地的人员没有状态导致同步之后无法登录 #I3ZC2L
// 设置工号如果工号为空则使用username
if (oConvertUtils.isEmpty(dtUser.getJob_number())) {
sysUser.setWorkNo(dtUser.getUserid());
} else {
sysUser.setWorkNo(dtUser.getJob_number());
}
return this.dtUserToSysUser(dtUser, sysUser);
}

View File

@ -96,15 +96,18 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
}
@Override
public boolean syncLocalDepartmentToThirdApp(String ids) {
public SyncInfoVo syncLocalDepartmentToThirdApp(String ids) {
SyncInfoVo syncInfo = new SyncInfoVo();
String accessToken = this.getAccessToken();
if (accessToken == null) {
return false;
syncInfo.addFailInfo("accessToken获取失败");
return syncInfo;
}
// 获取企业微信所有的部门
List<Department> departments = JwDepartmentAPI.getAllDepartment(accessToken);
if (departments == null) {
return false;
syncInfo.addFailInfo("获取企业微信所有部门失败!");
return syncInfo;
}
// 删除企业微信有但本地没有的部门(以本地部门数据为主)(以为企业微信不能创建同名部门,所以只能先删除)
List<JwDepartmentTreeVo> departmentTreeList = JwDepartmentTreeVo.listToTree(departments);
@ -117,7 +120,7 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
// 递归同步部门
departments = JwDepartmentAPI.getAllDepartment(accessToken);
this.syncDepartmentRecursion(sysDepartsTree, departments, parent, accessToken);
return true;
return syncInfo;
}
// 递归删除部门以及子部门,由于企业微信不允许删除带有成员和子部门的部门,所以需要递归删除下子部门,然后把部门成员移动端根部门下
@ -250,6 +253,11 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
SysDepart newSysDepart = this.qwDepartmentToSysDepart(departmentTree, null);
if (sysParentId != null) {
newSysDepart.setParentId(sysParentId);
// 2 = 组织机构
newSysDepart.setOrgCategory("2");
} else {
// 1 = 公司
newSysDepart.setOrgCategory("1");
}
try {
sysDepartService.saveDepartData(newSysDepart, username);
@ -604,6 +612,10 @@ public class ThirdAppWechatEnterpriseServiceImpl implements IThirdAppService {
BeanUtils.copyProperties(oldSysUser, sysUser);
sysUser.setRealname(qwUser.getName());
sysUser.setPost(qwUser.getPosition());
// 设置工号,由于企业微信没有工号的概念,所以只能用 userId 代替
if (oConvertUtils.isEmpty(sysUser.getWorkNo())) {
sysUser.setWorkNo(qwUser.getUserid());
}
try {
sysUser.setSex(Integer.parseInt(qwUser.getGender()));
} catch (NumberFormatException ignored) {

View File

@ -197,7 +197,7 @@ jeecg :
# ElasticSearch 设置
elasticsearch:
cluster-name: jeecg-ES
cluster-nodes: 81.70.47.128:9200
cluster-nodes: 127.0.0.1:9200
check-enabled: true
# 表单设计器配置
desform:

View File

@ -9,6 +9,6 @@ ${AnsiColor.BRIGHT_BLUE}
${AnsiColor.BRIGHT_GREEN}
Jeecg Boot Version: 3.0
Jeecg Boot Version: 3.1.0
Spring Boot Version: ${spring-boot.version}${spring-boot.formatted-version}
${AnsiColor.BLACK}

View File

@ -115,9 +115,9 @@
<#return "{type:'${po.classType}',value:'${po.fieldName}',text:'${po.filedComment}'}">
<#else>
<#if po.classType=="sel_search" || po.classType=="list_multi">
<#return "{type:'${po.classType}',value:'${po.fieldName}',text:'${po.filedComment}',dictTable:'${superQuery_dictTable}', dictText:'${superQuery_dictText}', dictCode:'${po.dictField}'}">
<#return "{type:'${po.classType}',value:'${po.fieldName}',text:'${po.filedComment}',dictTable:\"${superQuery_dictTable}\", dictText:'${superQuery_dictText}', dictCode:'${po.dictField}'}">
<#elseif po.dictTable?? && po.dictTable!="" && po.classType!="sel_tree" && po.classType!="cat_tree" && po.classType!="link_down">
<#return "{type:'${po.fieldDbType}',value:'${po.fieldName}',text:'${po.filedComment}',dictCode:'${po.dictTable},${po.dictText},${po.dictField}'}">
<#return "{type:'${po.fieldDbType}',value:'${po.fieldName}',text:'${po.filedComment}',dictCode:\"${po.dictTable},${po.dictText},${po.dictField}\"}">
<#elseif po.dictField?? && po.classType!="sel_tree" && po.classType!="cat_tree" && po.classType!="link_down">
<#return "{type:'${po.fieldDbType}',value:'${po.fieldName}',text:'${po.filedComment}',dictCode:'${po.dictField}'}">
<#elseif po.fieldDbType=="Text">

View File

@ -65,10 +65,10 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
* @param req
* @return
*/
@AutoLog(value = "${tableVo.ftlDescription}-分页列表查询")
//@AutoLog(value = "${tableVo.ftlDescription}-分页列表查询")
@ApiOperation(value="${tableVo.ftlDescription}-分页列表查询", notes="${tableVo.ftlDescription}-分页列表查询")
@GetMapping(value = "/list")
public Result<?> queryPageList(${entityName} ${entityName?uncap_first},
public Result<IPage<${entityName}>> queryPageList(${entityName} ${entityName?uncap_first},
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
@ -87,7 +87,7 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
@AutoLog(value = "${tableVo.ftlDescription}-添加")
@ApiOperation(value="${tableVo.ftlDescription}-添加", notes="${tableVo.ftlDescription}-添加")
@PostMapping(value = "/add")
public Result<?> add(@RequestBody ${entityName} ${entityName?uncap_first}) {
public Result<String> add(@RequestBody ${entityName} ${entityName?uncap_first}) {
<#if bpm_flag>
${entityName?uncap_first}.setBpmStatus("1");
</#if>
@ -103,8 +103,8 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
*/
@AutoLog(value = "${tableVo.ftlDescription}-编辑")
@ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
@PutMapping(value = "/edit")
public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
${entityName?uncap_first}Service.updateById(${entityName?uncap_first});
return Result.OK("编辑成功!");
}
@ -118,7 +118,7 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
@AutoLog(value = "${tableVo.ftlDescription}-通过id删除")
@ApiOperation(value="${tableVo.ftlDescription}-通过id删除", notes="${tableVo.ftlDescription}-通过id删除")
@DeleteMapping(value = "/delete")
public Result<?> delete(@RequestParam(name="id",required=true) String id) {
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
${entityName?uncap_first}Service.removeById(id);
return Result.OK("删除成功!");
}
@ -132,7 +132,7 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
@AutoLog(value = "${tableVo.ftlDescription}-批量删除")
@ApiOperation(value="${tableVo.ftlDescription}-批量删除", notes="${tableVo.ftlDescription}-批量删除")
@DeleteMapping(value = "/deleteBatch")
public Result<?> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.${entityName?uncap_first}Service.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
@ -143,13 +143,13 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
* @param id
* @return
*/
@AutoLog(value = "${tableVo.ftlDescription}-通过id查询")
//@AutoLog(value = "${tableVo.ftlDescription}-通过id查询")
@ApiOperation(value="${tableVo.ftlDescription}-通过id查询", notes="${tableVo.ftlDescription}-通过id查询")
@GetMapping(value = "/queryById")
public Result<?> queryById(@RequestParam(name="id",required=true) String id) {
public Result<${entityName}> queryById(@RequestParam(name="id",required=true) String id) {
${entityName} ${entityName?uncap_first} = ${entityName?uncap_first}Service.getById(id);
if(${entityName?uncap_first}==null) {
return Result.error("未找到对应数据");
return Result.error("未找到对应数据",null);
}
return Result.OK(${entityName?uncap_first});
}

View File

@ -108,8 +108,8 @@
</#if>
</#list>
<#if bpm_flag>
<a-col v-if="showFlowSubmitButton" :span="24" style="text-align: center">
<a-button @click="submitForm">提 交</a-button>
<a-col v-if="showFlowSubmitButton" :span="24" style="width: 100%;text-align: center;">
<a-button icon="check" style="width: 126px" type="primary" @click="submitForm">提 交</a-button>
</a-col>
</#if>
</a-row>

View File

@ -0,0 +1,218 @@
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
<#assign bpm_flag=false>
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
<#assign list_need_category=true>
</#if>
<#if po.classType=='pca'>
<#assign list_need_pca=true>
</#if>
</#list>
<#-- 结束循环 -->
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="checkedKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
</div>
</template>
<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
import {ref, computed, unref} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import {useModal} from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage'
import ${entityName}Modal from './components/${entityName}Modal.vue'
import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
<#if list_need_category>
import { loadCategoryData } from '/@/api/common/api'
import { getAuthCache, setAuthCache } from '/@/utils/auth';
import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
</#if>
const checkedKeys = ref<Array<string | number>>([]);
//注册model
const [registerModal, {openModal}] = useModal();
//注册table数据
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '${tableVo.ftlDescription}',
api: list,
columns,
canResize:false,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter:true,
showAdvancedButton:true,
fieldMapToTime: [
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.classType=='date'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
<#elseif po.classType=='datetime'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
</#if>
</#if>
</#if>
</#list>
],
},
actionColumn: {
width: 120,
},
},
exportConfig: {
name:"${tableVo.ftlDescription}",
url: getExportUrl,
},
importConfig: {
url: getImportUrl
},
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: checkedKeys.value}, reload);
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
}
}
]
}
<#if list_need_category>
/**
* 初始化字典配置
*/
function initDictConfig(){
<#list columns as po>
<#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
<#if po.classType=='cat_tree' && list_need_category==true>
loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
if (res) {
let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
if(!allDictDate['${po.dictField?default("")}']){
Object.assign(allDictDate,{'${po.dictField?default("")}':res})
}
setAuthCache(DB_DICT_DATA_KEY,allDictDate)
}
})
</#if>
</#if>
</#list>
}
initDictConfig();
</#if>
</script>
<style scoped>
</style>

View File

@ -0,0 +1,61 @@
import {defHttp} from '/@/utils/http/axios';
import {Modal} from 'ant-design-vue';
enum Api {
list = '/${entityPackage}/${entityName?uncap_first}/list',
save='/${entityPackage}/${entityName?uncap_first}/add',
edit='/${entityPackage}/${entityName?uncap_first}/edit',
deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
* 删除单个
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}

View File

@ -0,0 +1,354 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [
<#list columns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
//查询数据
export const searchFormSchema: FormSchema[] = [
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isQuery=='Y'>
<#assign query_flag=true>
<#assign query_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictField}">
</#if>
<#if po.queryMode=='single'>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${po.dictTable},${po.dictText},${po.dictField}"
},
<#elseif po.classType=='sel_user'>
component: 'JSelectUserByDept',
<#elseif po.classType=='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:"${po.dictField}"
</#if>
},
<#elseif po.classType=='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType=='list_multi'>
component: 'JMultiSelectTag',//暂无该组件
componentProps:{
dictCode:"query_field_dictCode?default("")"
},
<#elseif po.classType=='cat_tree'>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}",//back和事件未添加暂时有问题
},
<#elseif po.classType=='date'>
component: 'DatePicker',
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.classType=='pca'>
component: 'JAreaLinkage',
<#elseif po.classType=='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:"${po.dictField}",
multi:${po.extendParams.popupMulti?c},
}
},
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
component: 'JDictSelectTag',
componentProps:{
<#if po.dictTable?default("")?trim?length gt 1>
dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
<#elseif po.dictField?default("")?trim?length gt 1>
dictCode:"${po.dictField}"
</#if>
},
<#else>
component: 'Input',
</#if>
colProps: {span: 6},
},
<#else>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='date'>
component: 'RangePicker',
<#elseif po.classType=='datetime'>
component: 'RangePicker',
componentProps: {
showTime:true
},
<#else>
component: 'Input', //TODO 范围查询
</#if>
colProps: {span: 6},
},
</#if>
</#if>
</#list>
<#-- 结束循环 -->
];
//表单数据
export const formSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list columns as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];

View File

@ -0,0 +1,58 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<BasicForm @register="registerForm"/>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {formSchema} from '../${entityName?uncap_first}.data';
import {saveOrUpdate} from '../${entityName?uncap_first}.api';
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -119,7 +119,7 @@ public class ${entityName}Controller {
*/
@AutoLog(value = "${tableVo.ftlDescription}-编辑")
@ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
${entityName} ${entityName?uncap_first} = new ${entityName}();
BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});

View File

@ -28,13 +28,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :span="${form_span}" >
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='datetime'>
@ -140,7 +135,7 @@
</#list>
</a-tabs>
<#if bpm_flag>
<a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button @click="handleOk">提 交</a-button></a-row>
<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>
</#if>
</a-spin>
</template>
@ -171,20 +166,12 @@
return {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
model:{
<#include "/common/init/initValue.ftl">
},

View File

@ -25,13 +25,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :span="${form_span}">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
<#elseif po.classType =='datetime'>
@ -117,20 +112,12 @@
},
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
<#include "/common/validatorRulesTemplate/sub.ftl">
confirmLoading: false,
}

View File

@ -158,7 +158,7 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
*/
@AutoLog(value = "${tableVo.ftlDescription}-编辑")
@ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
${entityName?uncap_first}Service.update${entityName}(${entityName?uncap_first});
return Result.OK("编辑成功!");

View File

@ -0,0 +1,272 @@
<template>
<div class="p-4">
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection" :expandedRowKeys="expandedRowKeys" @expand="handleExpand" @fetch-success="onFetchSuccess">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleCreate" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="ant-design:down-outlined"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)"/>
</template>
</BasicTable>
<!--字典弹窗-->
<${entityName}Modal @register="registerModal" @success="handleSuccess"/>
</div>
</template>
<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
//ts语法
import {ref, computed, unref, toRaw, nextTick} from 'vue';
import {BasicTable, useTable, TableAction} from '/src/components/Table';
import {useModal} from '/src/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage'
import ${entityName}Modal from './components/${entityName}Modal.vue';
import {columns} from './${entityName}.data';
import {list, delete${entityName}, batchDelete${entityName}, getExportUrl,getImportUrl, getChildList,getChildListBatch} from './${entityName}.api';
const expandedRowKeys = ref([]);
//字典model
const [registerModal, {openModal}] = useModal();
//注册table数据
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '${tableVo.ftlDescription}',
columns,
canResize:false,
actionColumn: {
width: 120,
},
},
exportConfig: {
name:"${tableVo.ftlDescription}",
url: getExportUrl,
},
importConfig: {
url: getImportUrl,
successimportSuccess
},
})
const [registerTable, {reload, collapseAll, updateTableDataRecord, findTableDataRecord,getDataSource},{ rowSelection, selectedRowKeys }] = tableContext
/**
* 新增事件
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑事件
*/
async function handleEdit(record) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 详情
*/
async function handleDetail(record) {
openModal(true, {
record,
isUpdate: true,
hideFooter: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await delete${entityName}({id: record.id}, importSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
const ids = selectedRowKeys.value.filter(item => !item.includes('loading'))
await batchDelete${entityName}({ids: ids}, importSuccess);
}
/**
* 导入
*/
function importSuccess() {
reload() && (expandedRowKeys.value = []);
}
/**
* 添加下级
*/
function handleAddSub(record) {
openModal(true, {
record,
isUpdate: false,
});
}
/**
* 成功回调
*/
async function handleSuccess({isUpdate, values, expandedArr}) {
if (isUpdate) {
//编辑回调
updateTableDataRecord(values.id, values);
} else {
if(!values['pid']){
//新增根节点
reload();
}else{
//新增子集
expandedRowKeys.value = [];
for (let key of unref(expandedArr)) {
await expandTreeNode(key)
}
}
}
}
/**
* 接口请求成功后回调
*/
function onFetchSuccess(result) {
getDataByResult(result.items)&&loadDataByExpandedRows();
}
/**
* 根据已展开的行查询数据(用于保存后刷新时异步加载子级的数据)
*/
async function loadDataByExpandedRows() {
if (unref(expandedRowKeys).length > 0) {
const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',')});
if (res.success && res.result.records.length>0) {
//已展开的数据批量子节点
let records = res.result.records
const listMap = new Map();
for (let item of records) {
let pid = item['pid'];
if (unref(expandedRowKeys).includes(pid)) {
let mapList = listMap.get(pid);
if (mapList == null) {
mapList = [];
}
mapList.push(item);
listMap.set(pid, mapList);
}
}
let childrenMap = listMap;
let fn = (list) => {
if(list) {
list.forEach(data => {
if (unref(expandedRowKeys).includes(data.id)) {
data.children = getDataByResult(childrenMap.get(data.id))
fn(data.children)
}
})
}
};
fn(getDataSource())
}
}
}
/**
* 处理数据集
*/
function getDataByResult(result){
if(result && result.length>0){
return result.map(item=>{
//判断是否标记了带有子节点
if(item["hasChild"]=='1'){
let loadChild = { id: item.id+'_loadChild', name: 'loading...', isLoading: true }
item.children = [loadChild]
}
return item
})
}
}
/**
*树节点展开合并
* */
async function handleExpand(expanded, record) {
// 判断是否是展开状态,展开状态(expanded)并且存在子集(children)并且未加载过(isLoading)的就去查询子节点数据
if (expanded) {
expandedRowKeys.value.push(record.id)
if (record.children.length > 0 && !!record.children[0].isLoading) {
let result = await getChildList({pid: record.id});
result=result.records?result.records:result;
if (result && result.length > 0) {
record.children = getDataByResult(result);
} else {
record.children = null
record.hasChild = '0'
}
}
} else {
let keyIndex = expandedRowKeys.value.indexOf(record.id)
if (keyIndex >= 0) {
expandedRowKeys.value.splice(keyIndex, 1);
}
}
}
/**
*操作表格后处理树节点展开合并
* */
async function expandTreeNode(key) {
let record = findTableDataRecord(key)
expandedRowKeys.value.push(key);
let result = await getChildList({pid: key});
if (result && result.length > 0) {
record.children = getDataByResult(result);
} else {
record.children = null
record.hasChild = '0'
}
updateTableDataRecord(key, record);
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '确定删除吗?',
confirm: handleDelete.bind(null, record),
},
},
{
label: '添加下级',
onClick: handleAddSub.bind(null, {pid: record.id}),
}
]
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,82 @@
import {defHttp} from "/@/utils/http/axios";
import {Modal} from 'ant-design-vue';
enum Api {
list = '/${entityPackage}/${entityName?uncap_first}/rootList',
save='/${entityPackage}/${entityName?uncap_first}/add',
edit='/${entityPackage}/${entityName?uncap_first}/edit',
delete${entityName} = '/sys/${entityName?uncap_first}/delete',
deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
loadTreeData = '/${entityPackage}/${entityName?uncap_first}/loadTreeRoot',
getChildList = '/${entityPackage}/${entityName?uncap_first}/childList',
getChildListBatch = '/${entityPackage}/${entityName?uncap_first}/getChildListBatch',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
* @param params
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
* 删除
*/
export const delete${entityName} = (params,handleSuccess) => {
return defHttp.delete({url: Api.delete${entityName}, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
* 批量删除
* @param params
*/
export const batchDelete${entityName} = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdateDict = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}
/**
* 查询全部树形节点数据
* @param params
*/
export const loadTreeData = (params) =>
defHttp.get({url: Api.loadTreeData,params});
/**
* 查询子节点数据
* @param params
*/
export const getChildList = (params) =>
defHttp.get({url: Api.getChildList, params});
/**
* 批量查询子节点数据
* @param params
*/
export const getChildListBatch = (params) =>
defHttp.get({url: Api.getChildListBatch, params},{isTransformResponse:false});

View File

@ -0,0 +1,354 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [
<#list columns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
//查询数据
export const searchFormSchema: FormSchema[] = [
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isQuery=='Y'>
<#assign query_flag=true>
<#assign query_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictField}">
</#if>
<#if po.queryMode=='single'>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${po.dictTable},${po.dictText},${po.dictField}"
},
<#elseif po.classType=='sel_user'>
component: 'JSelectUserByDept',
<#elseif po.classType=='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:"${po.dictField}"
</#if>
},
<#elseif po.classType=='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType=='list_multi'>
component: 'JMultiSelectTag',//暂无该组件
componentProps:{
dictCode:"query_field_dictCode?default("")"
},
<#elseif po.classType=='cat_tree'>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}",//back和事件未添加暂时有问题
},
<#elseif po.classType=='date'>
component: 'DatePicker',
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.classType=='pca'>
component: 'JAreaLinkage',
<#elseif po.classType=='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:"${po.dictField}",
multi:${po.extendParams.popupMulti?c},
}
},
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
component: 'JDictSelectTag',
componentProps:{
<#if po.dictTable?default("")?trim?length gt 1>
dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
<#elseif po.dictField?default("")?trim?length gt 1>
dictCode:"${po.dictField}"
</#if>
},
<#else>
component: 'Input',
</#if>
colProps: {span: 6},
},
<#else>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='date'>
component: 'RangePicker',
<#elseif po.classType=='datetime'>
component: 'RangePicker',
componentProps: {
showTime:true
},
<#else>
component: 'Input', //TODO 范围查询
</#if>
colProps: {span: 6},
},
</#if>
</#if>
</#list>
<#-- 结束循环 -->
];
//表单数据
export const formSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list columns as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];

View File

@ -0,0 +1,87 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
<BasicForm @register="registerForm"/>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref} from 'vue';
import {BasicModal, useModalInner} from '/src/components/Modal';
import {BasicForm, useForm} from '/src/components/Form';
import {formSchema} from '../${entityName?uncap_first}.data';
import {loadTreeData, saveOrUpdateDict} from '../${entityName?uncap_first}.api';
// 获取emit
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const expandedRowKeys = ref([]);
const treeData = ref([]);
//表单配置
const [registerForm, {resetFields, setFieldsValue, validate, updateSchema}] = useForm({
schemas: formSchema,
showActionButtonGroup: false,
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 },
},
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await resetFields();
expandedRowKeys.value = [];
setModalProps({confirmLoading: false, minHeight: 80});
isUpdate.value = !!data?.isUpdate;
if (data?.record) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
//父级节点树信息
treeData.value = await loadTreeData({'async': false,'pcode':''});
updateSchema({
field: 'pid',
componentProps: {treeData},
});
});
//设置标题
const getTitle = computed(() => (!unref(isUpdate) ? '新增字典' : '编辑字典'));
/**
* 根据pid获取展开的节点
* @param pid
* @param arr
*/
function getExpandKeysByPid(pid,arr){
if(pid && arr && arr.length>0){
for(let i=0;i<arr.length;i++){
if(arr[i].key==pid && unref(expandedRowKeys).indexOf(pid)<0){
expandedRowKeys.value.push(arr[i].key);
getExpandKeysByPid(arr[i]['parentId'],unref(treeData))
}else{
getExpandKeysByPid(pid,arr[i].children)
}
}
}
}
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdateDict(values, isUpdate.value);
//关闭弹窗
closeModal();
//展开的节点信息
await getExpandKeysByPid(values['pid'],unref(treeData))
//刷新列表(isUpdate:是否编辑;values:表单信息;expandedArr:展开的节点信息)
emit('success', {isUpdate: unref(isUpdate), values:{...values},expandedArr: unref(expandedRowKeys).reverse()});
} finally {
setModalProps({confirmLoading: false});
}
}
</script>

View File

@ -103,7 +103,7 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
*/
@AutoLog(value = "${tableVo.ftlDescription}-编辑")
@ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
${entityName?uncap_first}Service.updateById(${entityName?uncap_first});
return Result.OK("编辑成功!");
@ -194,7 +194,7 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
*/
@AutoLog(value = "${sub.ftlDescription}-编辑")
@ApiOperation(value="${sub.ftlDescription}-编辑", notes="${sub.ftlDescription}-编辑")
@PutMapping(value = "/edit${sub.entityName}")
@RequestMapping(value = "/edit${sub.entityName}", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit${sub.entityName}(@RequestBody ${sub.entityName} ${sub.entityName?uncap_first}) {
${sub.entityName?uncap_first}Service.updateById(${sub.entityName?uncap_first});
return Result.OK("编辑成功!");

View File

@ -371,7 +371,7 @@
this.$set(this.dictOptions, '${po.fieldName}', res.result)
}
})
<#elseif po.classType=='sel_search' || po.classType=='list_multi' || po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#elseif po.classType=='radio' || po.classType=='checkbox'>
<#assign list_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1>
<#assign list_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">

View File

@ -15,8 +15,6 @@
@change="handleImportExcel">
<a-button type="primary" icon="import">导入</a-button>
</a-upload>
<!-- 高级查询区域 -->
<j-super-query :fieldList="superFieldList" ref="superQueryModal" @handleSuperQuery="handleSuperQuery"></j-super-query>
<a-dropdown v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay">
<a-menu-item key="1" @click="batchDel"><a-icon type="delete"/>删除</a-menu-item>
@ -192,12 +190,10 @@
</#if>
</#if>
</#list>
},
superFieldList:[],
}
}
},
created() {
this.getSuperFieldList();
},
computed: {
importExcelUrl(){
@ -209,15 +205,8 @@
this.dataSource=[]
this.selectedRowKeys=[]
this.ipagination.current = 1
},
getSuperFieldList(){
<#include "/common/utils.ftl">
let fieldList=[];
<#list columns as po>
fieldList.push(${superQueryFieldList(po)})
</#list>
this.superFieldList = fieldList
}
}
}
</script>

View File

@ -0,0 +1,236 @@
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
<#assign bpm_flag=false>
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
<#assign list_need_category=true>
</#if>
<#if po.classType=='pca'>
<#assign list_need_pca=true>
</#if>
</#list>
<#-- 结束循环 -->
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
<!--子表表格tab-->
<a-tabs defaultActiveKey="1">
<#assign sub_seq=1>
<#list subTables as sub>
<a-tab-pane tab="${sub.ftlDescription}" key="${sub_seq}" <#if sub_seq gt 1>forceRender</#if>>
<${sub.entityName}List/>
</a-tab-pane>
<#assign sub_seq=sub_seq+1>
</#list>
</a-tabs>
<!-- 表单区域 -->
<${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
</div>
</template>
<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
import {ref, computed, unref,provide} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'
import {useModal} from '/@/components/Modal';
import ${entityName}Modal from './components/${entityName}Modal.vue'
<#list subTables as sub>
import ${sub.entityName}List from './${sub.entityName}List.vue'
</#list>
import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
<#if list_need_category>
import { loadCategoryData } from '/@/api/common/api'
import { getAuthCache, setAuthCache } from '/@/utils/auth';
import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
</#if>
//注册model
const [registerModal, {openModal}] = useModal();
//注册table数据
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '${tableVo.ftlDescription}',
api: list,
columns,
canResize:false,
rowSelection: {type: 'radio'},
formConfig: {
schemas: searchFormSchema,
fieldMapToTime: [
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.classType=='date'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
<#elseif po.classType=='datetime'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
</#if>
</#if>
</#if>
</#list>
],
},
actionColumn: {
width: 120,
},
pagination:{
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20'],
}
},
exportConfig: {
name:"${tableVo.ftlDescription}",
url: getExportUrl,
},
importConfig: {
url: getImportUrl
},
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
const mainId = computed(() => (unref(selectedRowKeys).length > 0 ? unref(selectedRowKeys)[0] : ''));
//下发 mainId,子组件接收
provide('mainId', mainId);
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: selectedRowKeys.value}, reload);
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
}
}
]
}
<#if list_need_category>
/**
* 初始化字典配置
*/
function initDictConfig(){
<#list columns as po>
<#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
<#if po.classType=='cat_tree' && list_need_category==true>
loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
if (res) {
let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
if(!allDictDate['${po.dictField?default("")}']){
Object.assign(allDictDate,{'${po.dictField?default("")}':res})
}
setAuthCache(DB_DICT_DATA_KEY,allDictDate)
}
})
</#if>
</#if>
</#list>
}
initDictConfig();
</#if>
</script>
<style scoped>
</style>

View File

@ -0,0 +1,113 @@
import {defHttp} from '/@/utils/http/axios';
import {Modal} from 'ant-design-vue';
enum Api {
list = '/${entityPackage}/${entityName?uncap_first}/list',
save='/${entityPackage}/${entityName?uncap_first}/add',
edit='/${entityPackage}/${entityName?uncap_first}/edit',
deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
<#list subTables as sub><#rt/>
${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/list${sub.entityName}ByMainId',
${sub.entityName?uncap_first}Save='/${entityPackage}/${entityName?uncap_first}/add${sub.entityName}',
${sub.entityName?uncap_first}Edit='/${entityPackage}/${entityName?uncap_first}/edit${sub.entityName}',
${sub.entityName?uncap_first}Delete = '/${entityPackage}/${entityName?uncap_first}/delete${sub.entityName}',
${sub.entityName?uncap_first}DeleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch${sub.entityName}',
${sub.entityName?uncap_first}ExportXlsUrl = '/${entityPackage}/${entityName?uncap_first}/export${sub.entityName}',
${sub.entityName?uncap_first}ImportUrl = '/${entityPackage}/${entityName?uncap_first}/import${sub.entityName}',
</#list>
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
* 删除单个
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}
<#list subTables as sub><#rt/>
/**
* 列表接口
* @param params
*/
export const ${sub.entityName?uncap_first}List = (params) =>
defHttp.get({url: Api.${sub.entityName?uncap_first}List, params});
/**
* 删除单个
*/
export const ${sub.entityName?uncap_first}Delete = (params,handleSuccess) => {
return defHttp.delete({url: Api.${sub.entityName?uncap_first}Delete, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
* 批量删除
* @param params
*/
export const ${sub.entityName?uncap_first}DeleteBatch = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.${sub.entityName?uncap_first}DeleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
* 保存或者更新
* @param params
*/
export const ${sub.entityName?uncap_first}SaveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.${sub.entityName?uncap_first}Edit : Api.${sub.entityName?uncap_first}Save;
return defHttp.post({url: url, params});
}
</#list>

View File

@ -0,0 +1,601 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [
<#list columns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
//查询数据
export const searchFormSchema: FormSchema[] = [
<#-- 开始循环 -->
<#list columns as po>
<#if po.isQuery=='Y'>
<#assign query_flag=true>
<#assign query_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictField}">
</#if>
<#if po.queryMode=='single'>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${po.dictTable},${po.dictText},${po.dictField}"
},
<#elseif po.classType=='sel_user'>
component: 'JSelectUserByDept',
<#elseif po.classType=='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:"${po.dictField}"
</#if>
},
<#elseif po.classType=='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType=='list_multi'>
component: 'JMultiSelectTag',//暂无该组件
componentProps:{
dictCode:"query_field_dictCode?default("")"
},
<#elseif po.classType=='cat_tree'>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}",//back和事件未添加暂时有问题
},
<#elseif po.classType=='date'>
component: 'DatePicker',
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.classType=='pca'>
component: 'JAreaLinkage',
<#elseif po.classType=='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:"${po.dictField}",
multi:${po.extendParams.popupMulti?c},
}
},
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
component: 'JDictSelectTag',
componentProps:{
<#if po.dictTable?default("")?trim?length gt 1>
dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
<#elseif po.dictField?default("")?trim?length gt 1>
dictCode:"${po.dictField}"
</#if>
},
<#else>
component: 'Input',
</#if>
colProps: {span: 6},
},
<#else>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='date'>
component: 'RangePicker',
<#elseif po.classType=='datetime'>
component: 'RangePicker',
componentProps: {
showTime:true
},
<#else>
component: 'Input', //TODO 范围查询
</#if>
colProps: {span: 6},
},
</#if>
</#if>
</#list>
<#-- 结束循环 -->
];
//表单数据
export const formSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list columns as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
<#list subTables as sub>
//子表列表数据
export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
<#list sub.originalColumns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
//子表表单数据
export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list sub.originalColumns as po><#rt/>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
</#list>

View File

@ -0,0 +1,157 @@
<#list subTables as sub>
#segment#${sub.entityName}List.vue
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection" :searchInfo="searchInfo">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleCreate" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)"/>
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
<${sub.entityName}Modal @register="registerModal" @success="handleSuccess"/>
</div>
</template>
<script lang="ts" setup>
import {ref, computed, unref,inject,watch} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'
import {useModal} from '/@/components/Modal';
import ${sub.entityName}Modal from './components/${sub.entityName}Modal.vue'
import {${sub.entityName?uncap_first}Columns} from './${entityName}.data';
import {${sub.entityName?uncap_first}List, ${sub.entityName?uncap_first}Delete, ${sub.entityName?uncap_first}DeleteBatch} from './${entityName}.api';
import {isEmpty} from "/@/utils/is";
import {useMessage} from '/@/hooks/web/useMessage';
//接收主表id
const mainId = inject('mainId') || '';
//提示弹窗
const $message = useMessage()
//弹窗model
const [registerModal, {openModal}] = useModal();
const searchInfo = {};
// 列表页面公共参数、方法
const {prefixCls, tableContext} = useListPage({
tableProps: {
api: ${sub.entityName?uncap_first}List,
columns: ${sub.entityName?uncap_first}Columns,
canResize: false,
useSearchForm: false,
actionColumn: {
width: 180,
},
pagination:{
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20'],
}
},
});
//注册table数据
const [registerTable, {reload}, {rowSelection, selectedRowKeys}] = tableContext;
watch(mainId, () => {
<#list sub.foreignKeys as key>
searchInfo['${key?uncap_first}'] = unref(mainId);
</#list>
reload();
}
);
/**
* 新增事件
*/
function handleCreate() {
if (isEmpty(unref(mainId))) {
$message.createMessage.warning('请选择一个主表信息')
return;
}
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
async function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await ${sub.entityName?uncap_first}Delete({id: record.id}, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await ${sub.entityName?uncap_first}DeleteBatch({ids: selectedRowKeys.value}, () => {
reload()
})
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
}
]
}
</script>
</#list>

View File

@ -0,0 +1,58 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<BasicForm @register="registerForm"/>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {formSchema} from '../${entityName?uncap_first}.data';
import {saveOrUpdate} from '../${entityName?uncap_first}.api';
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,66 @@
<#include "/common/utils.ftl">
<#list subTables as sub>
#segment#${sub.entityName}Modal.vue
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<BasicForm @register="registerForm"/>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref,inject} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
import {${sub.entityName?uncap_first}SaveOrUpdate} from '../${entityName?uncap_first}.api';
//接收主表id
const mainId = inject('mainId');
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
labelWidth: 150,
schemas: ${sub.entityName?uncap_first}FormSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({confirmLoading: true});
if (unref(mainId)) {
<#list sub.foreignKeys as key>
values['${key?uncap_first}'] = unref(mainId);
</#list>
}
//提交表单
await ${sub.entityName?uncap_first}SaveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>
</#list>

View File

@ -110,7 +110,7 @@ public class ${entityName}Controller {
*/
@AutoLog(value = "${tableVo.ftlDescription}-编辑")
@ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
${entityName} ${entityName?uncap_first} = new ${entityName}();
BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});

View File

@ -266,9 +266,7 @@
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical"/>
<a-dropdown>
<a class="ant-dropdown-link">
<span>更多 <a-icon type="down"/></span>
</a>
<a class="ant-dropdown-link">更多 <a-icon type="down" /></a>
<a-menu slot="overlay">
<a-menu-item>
<a-popconfirm title="确定删除吗?" @confirm="handleDelete(record.id)">

View File

@ -21,13 +21,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :xs="24" :sm="12">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='datetime'>
@ -131,7 +126,7 @@
</#list>
</a-tabs>
<#if bpm_flag>
<a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button @click="handleOk">提 交</a-button></a-row>
<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>
</#if>
</a-spin>
</template>
@ -161,20 +156,12 @@
return {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
model:{
<#include "/common/init/initValue.ftl">
},

View File

@ -15,13 +15,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :xs="24" :sm="12">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
<#elseif po.classType =='datetime'>
@ -90,20 +85,12 @@
},
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
<#include "/common/validatorRulesTemplate/sub.ftl">
confirmLoading: false,
}

View File

@ -0,0 +1,244 @@
<#-- ** 引入全局工具方法 ** -->
<#--<#include "/common/utils.ftl">-->
<#include "../../../../../../common/utils.ftl">
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
<#assign bpm_flag=false>
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
<#assign list_need_category=true>
</#if>
<#if po.classType=='pca'>
<#assign list_need_pca=true>
</#if>
</#list>
<#-- 结束循环 -->
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection" :expandedRowKeys="expandedRowKeys" @expand="handleExpand">
<!-- 内嵌table区域 begin -->
<template #expandedRowRender="{record}">
<a-tabs tabPosition="top">
<#list subTables as sub>
<a-tab-pane tab="${sub.ftlDescription}" key="${sub.entityName?uncap_first}" forceRender>
<${sub.entityName?uncap_first}SubTable :id="expandedRowKeys[0]"/>
</a-tab-pane>
</#list>
</a-tabs>
</template>
<!-- 内嵌table区域 end -->
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
</div>
</template>
<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
import {ref, computed, unref} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'
import {useModal} from '/@/components/Modal';
import ${entityName}Modal from './components/${entityName}Modal.vue'
<#list subTables as sub>
import ${sub.entityName}SubTable from './subTables/${sub.entityName}SubTable.vue'
</#list>
import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
<#if list_need_category>
import { loadCategoryData } from '/@/api/common/api'
import { getAuthCache, setAuthCache } from '/@/utils/auth';
import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
</#if>
// 展开key
const expandedRowKeys = ref<any[]>([]);
//注册model
const [registerModal, {openModal}] = useModal();
//注册table数据
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '${tableVo.ftlDescription}',
api: list,
columns,
canResize:false,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter:true,
showAdvancedButton:true,
fieldMapToTime: [
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.classType=='date'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
<#elseif po.classType=='datetime'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
</#if>
</#if>
</#if>
</#list>
],
},
actionColumn: {
width: 120,
},
},
exportConfig: {
name:"${tableVo.ftlDescription}",
url: getExportUrl,
},
importConfig: {
url: getImportUrl
},
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
/**
* 展开事件
* */
function handleExpand(expanded, record){
expandedRowKeys.value=[];
if (expanded === true) {
expandedRowKeys.value.push(record.id)
}
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: selectedRowKeys.value}, reload);
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
}
}
]
}
<#if list_need_category>
/**
* 初始化字典配置
*/
function initDictConfig(){
<#list columns as po>
<#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
<#if po.classType=='cat_tree' && list_need_category==true>
loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
if (res) {
let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
if(!allDictDate['${po.dictField?default("")}']){
Object.assign(allDictDate,{'${po.dictField?default("")}':res})
}
setAuthCache(DB_DICT_DATA_KEY,allDictDate)
}
})
</#if>
</#if>
</#list>
}
initDictConfig();
</#if>
</script>
<style scoped>
</style>

View File

@ -0,0 +1,80 @@
import {defHttp} from '/@/utils/http/axios';
import {Modal} from 'ant-design-vue';
enum Api {
list = '/${entityPackage}/${entityName?uncap_first}/list',
save='/${entityPackage}/${entityName?uncap_first}/add',
edit='/${entityPackage}/${entityName?uncap_first}/edit',
deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
<#list subTables as sub><#rt/>
${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
</#list>
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
<#list subTables as sub><#rt/>
/**
* 子表单查询接口
* @param params
*/
export const query${sub.entityName} = Api.${sub.entityName?uncap_first}List
</#list>
/**
* 列表接口
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
* 删除单个
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}
<#list subTables as sub><#rt/>
/**
* 子表列表接口
* @param params
*/
export const ${sub.entityName?uncap_first}List = (params) =>
defHttp.get({url: Api.${sub.entityName?uncap_first}List, params},{isTransformResponse:false});
</#list>

View File

@ -0,0 +1,763 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
//列表数据
export const columns: BasicColumn[] = [
<#list columns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
//查询数据
export const searchFormSchema: FormSchema[] = [
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isQuery=='Y'>
<#assign query_flag=true>
<#assign query_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictField}">
</#if>
<#if po.queryMode=='single'>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${po.dictTable},${po.dictText},${po.dictField}"
},
<#elseif po.classType=='sel_user'>
component: 'JSelectUserByDept',
<#elseif po.classType=='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:"${po.dictField}"
</#if>
},
<#elseif po.classType=='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType=='list_multi'>
component: 'JMultiSelectTag',//暂无该组件
componentProps:{
dictCode:"query_field_dictCode?default("")"
},
<#elseif po.classType=='cat_tree'>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}",//back和事件未添加暂时有问题
},
<#elseif po.classType=='date'>
component: 'DatePicker',
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.classType=='pca'>
component: 'JAreaLinkage',
<#elseif po.classType=='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:"${po.dictField}",
multi:${po.extendParams.popupMulti?c},
}
},
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
component: 'JDictSelectTag',
componentProps:{
<#if po.dictTable?default("")?trim?length gt 1>
dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
<#elseif po.dictField?default("")?trim?length gt 1>
dictCode:"${po.dictField}"
</#if>
},
<#else>
component: 'Input',
</#if>
colProps: {span: 6},
},
<#else>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='date'>
component: 'RangePicker',
<#elseif po.classType=='datetime'>
component: 'RangePicker',
componentProps: {
showTime:true
},
<#else>
component: 'Input', //TODO 范围查询
</#if>
colProps: {span: 6},
},
</#if>
</#if>
</#list>
<#-- 结束循环 -->
];
//表单数据
export const formSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list columns as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
//子表单数据
<#list subTables as sub>
//子表列表数据
export const ${sub.entityName?uncap_first}Columns: BasicColumn[] = [
<#list sub.originalColumns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
<#if sub.foreignRelationType =='1'>
export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list sub.colums as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
</#if>
</#list>
//子表表格配置
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
export const ${sub.entityName?uncap_first}JVxeColumns: JVxeColumn[] = [
<#assign popupBackFields = "">
<#-- 循环子表的列 开始 -->
<#list sub.colums as col><#rt/>
<#if col.isShow =='Y'>
<#if col.filedComment !='外键' >
{
title: '${col.filedComment}',
key: '${autoStringSuffixForModel(col)}',
<#if col.classType =='date'>
type: JVxeTypes.date,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='datetime'>
type: JVxeTypes.datetime,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='textarea'>
type: JVxeTypes.textarea,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif "int,decimal,double,"?contains(col.classType)>
type: JVxeTypes.inputNumber,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='list' || col.classType =='radio'>
type: JVxeTypes.select,
options:[],
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='list_multi' || col.classType =='checkbox'>
type: JVxeTypes.selectMultiple,
options:[],
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='sel_search'>
type: JVxeTypes.selectSearch,
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='image'>
type: JVxeTypes.image,
token:true,
responseName:"message",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#if col.uploadnum??>
number: ${col.uploadnum},
</#if>
<#elseif col.classType =='file'>
type: JVxeTypes.file,
token:true,
responseName:"message",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#if col.uploadnum??>
number: ${col.uploadnum},
</#if>
<#elseif col.classType =='switch'>
type: JVxeTypes.checkbox,
<#if col.dictField == 'is_open'>
customValue: ['Y', 'N'],
<#else>
customValue: ${col.dictField},
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='popup'>
<#if popupBackFields?length gt 0>
<#assign popupBackFields = "${popupBackFields}"+","+"${col.dictText}">
<#else>
<#assign popupBackFields = "${col.dictText}">
</#if>
type: JVxeTypes.popup,
popupCode:"${col.dictTable}",
field:"${col.dictField}",
orgFields:"${col.dictField}",
destFields:"${Format.underlineToHump(col.dictText)}",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#else>
type: JVxeTypes.input,
<#if col.readonly=='Y'>
disabled:true,
</#if>
</#if>
<#if col.classType =='list_multi' || col.classType =='checkbox'>
width:"250px",
<#else>
width:"200px",
</#if>
<#if col.classType =='file'>
placeholder: '请选择文件',
<#else>
placeholder: '请输入${'$'}{title}',
</#if>
<#if col.defaultVal??>
<#if col.fieldDbType=="BigDecimal" || col.fieldDbType=="double" || col.fieldDbType=="int">
defaultValue:${col.defaultVal},
<#else>
defaultValue:"${col.defaultVal}",
</#if>
<#else>
defaultValue:'',
</#if>
<#-- 子表的校验 -->
<#assign subFieldValidType = col.fieldValidType!''>
<#-- 非空校验 -->
<#if col.nullable == 'N' || subFieldValidType == '*'>
validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
<#-- 其他情况下,只要有值就被认为是正则校验 -->
<#elseif subFieldValidType?length gt 0>
<#assign subMessage = '格式不正确'>
<#if subFieldValidType == 'only' >
<#assign subMessage = '不能重复'>
</#if>
validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
</#if>
},
</#if>
</#if>
</#list>
<#-- 循环子表的列 结束 -->
]
</#if>
</#list>

View File

@ -0,0 +1,179 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<BasicForm @register="registerForm" ref="formRef"/>
<!-- 子表单区域 -->
<a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
<a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
<${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
</a-tab-pane>
<#else>
<a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
<JVxeTable
keep-source
resizable
:ref="refKeys[${sub_index}]"
:loading="${sub.entityName?uncap_first}Table.loading"
:columns="${sub.entityName?uncap_first}Table.columns"
:dataSource="${sub.entityName?uncap_first}Table.dataSource"
:maxHeight="300"
:rowNumber="true"
:rowSelection="true"
:toolbar="true"
/>
</a-tab-pane>
</#if>
</#list>
</a-tabs>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref,reactive} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import { JVxeTable } from '/@/components/jeecg/JVxeTable'
import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'
<#list subTables as sub>
<#if sub.foreignRelationType =='1'>
import ${sub.entityName}Form from './${sub.entityName}Form.vue'
</#if>
</#list>
import {formSchema<#list subTables as sub><#if sub.foreignRelationType =='0'>,${sub.entityName?uncap_first}JVxeColumns</#if></#list>} from '../${entityName?uncap_first}.data';
import {saveOrUpdate<#list subTables as sub>,query${sub.entityName}</#list>} from '../${entityName?uncap_first}.api';
import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
<#assign hasOne2Many = false>
<#assign hasOne2One = false>
const activeKey = ref('${subTables[0].entityName?uncap_first}');
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
<#assign hasOne2Many = true>
const ${sub.entityName?uncap_first} = ref();
</#if>
<#if sub.foreignRelationType =='1'>
<#assign hasOne2One = true>
const ${sub.entityName?uncap_first}Form = ref();
</#if>
</#list>
const tableRefs = {<#list subTables as sub><#if sub.foreignRelationType =='0'>${sub.entityName?uncap_first}, <#assign hasOne2Many = true></#if></#list>};
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
const ${sub.entityName?uncap_first}Table = reactive({
loading: false,
dataSource: [],
columns:${sub.entityName?uncap_first}JVxeColumns
})
</#if>
</#list>
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await reset();
setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.initFormData(query${sub.entityName},data?.record?.id)
</#if>
</#list>
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='0'>
requestSubTableData(query${sub.entityName}, {id:data?.record?.id}, ${sub.entityName?uncap_first}Table)
</#if>
</#list>
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//方法配置
const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys<#if hasOne2One==true>,validateSubForm</#if>);
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
async function reset(){
await resetFields();
activeKey.value = ref('${subTables[0].entityName?uncap_first}');
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
${sub.entityName?uncap_first}Table.dataSource = [];
</#if>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.resetFields();
</#if>
</#list>
}
function classifyIntoFormData(allValues) {
let main = Object.assign({}, allValues.formValue)
return {
...main, // 展开
<#assign subManyIndex = 0>
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='0'>
${sub.entityName?uncap_first}List: allValues.tablesValue[${subManyIndex}].tableData,
<#assign subManyIndex = subManyIndex+1>
<#else>
${sub.entityName?uncap_first}List: ${sub.entityName?uncap_first}Form.value.getFormData(),
</#if>
</#list>
}
}
<#if hasOne2One==true>
//校验所有一对一子表表单
function validateSubForm(allValues){
return new Promise((resolve,reject)=>{
Promise.all([
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.validateForm(${sub_index}),
</#if>
</#list>
]).then(() => {
resolve(allValues)
}).catch(e => {
if (e.error === VALIDATE_FAILED) {
// 如果有未通过表单验证的子表就自动跳转到它所在的tab
activeKey.value = e.index == null ? unref(activeKey) : refKeys.value[e.index]
} else {
console.error(e)
}
})
})
}
</#if>
//表单提交事件
async function requestAddOrEdit(values) {
try {
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,64 @@
<#list subTables as sub>
<#if sub.foreignRelationType=='1'>
#segment#${sub.entityName}Form.vue
<template>
<BasicForm @register="registerForm"/>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import {BasicForm, useForm} from '/@/components/Form/index';
import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
import {defHttp} from '/@/utils/http/axios';
import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
export default defineComponent({
name:"${sub.entityName}Form",
components: {BasicForm},
emits:['register'],
setup(_,{emit}) {
const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
labelWidth: 150,
schemas: ${sub.entityName?uncap_first}FormSchema,
showActionButtonGroup: false,
});
/**
*初始化加载数据
*/
function initFormData(url,id){
if(id){
defHttp.get({url,params:{id}},{isTransformResponse:false}).then(res=>{
res.success && setFieldsValue({...res.result.records[0]});
})
}
}
/**
*获取表单数据
*/
function getFormData(){
return [getFieldsValue()];
}
/**
*表单校验
*/
function validateForm(index){
return new Promise((resolve, reject) => {
// 验证子表表单
validate().then(()=>{
return resolve()
}).catch(()=> {
return reject({ error: VALIDATE_FAILED ,index})
})
})
}
return {
registerForm,
resetFields,
initFormData,
getFormData,
validateForm
}
}
})
</script>
</#if>
</#list>

View File

@ -0,0 +1,69 @@
<#--noinspection JSDuplicatedDeclaration-->
<#list subTables as sub>
#segment#${sub.entityName}SubTable.vue
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
<#assign bpm_flag=false>
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
<#assign list_need_category=true>
</#if>
<#if po.classType=='pca'>
<#assign list_need_pca=true>
</#if>
</#list>
<#-- 结束循环 -->
<!--引用表格-->
<BasicTable bordered size="middle" :loading="loading" rowKey="id" :canResize="false" :columns="${sub.entityName?uncap_first}Columns" :dataSource="dataSource" :pagination="false">
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
</div>
</template>
<script lang="ts" setup>
import {ref,watchEffect} from 'vue';
import {BasicTable} from '/@/components/Table';
import {${sub.entityName?uncap_first}Columns} from '../${entityName}.data';
import {${sub.entityName?uncap_first}List} from '../${entityName}.api';
const props = defineProps({
id: {
type: String,
default: '',
},
})
const loading = ref(false);
const dataSource = ref([]);
watchEffect(() => {
props.id && loadData(props.id);
});
function loadData(id) {
dataSource.value = []
loading.value = true
${sub.entityName?uncap_first}List({id}).then((res) => {
if (res.success) {
dataSource.value = res.result.records
}
}).finally(() => {
loading.value = false
})
}
</script>
</#list>

View File

@ -28,13 +28,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :span="${form_span}" >
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='datetime'>
@ -140,7 +135,7 @@
</#list>
</a-tabs>
<#if bpm_flag>
<a-row v-if="showFlowSubmitButton" style="text-align: center;width: 100%;margin-top: 16px;"><a-button @click="handleOk">提 交</a-button></a-row>
<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>
</#if>
</a-spin>
</template>
@ -174,20 +169,12 @@
return {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
model:{
<#include "/common/init/initValue.ftl">
},

View File

@ -25,13 +25,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :span="${form_span}">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
<#elseif po.classType =='datetime'>
@ -117,20 +112,12 @@
},
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
<#include "/common/validatorRulesTemplate/sub.ftl">
confirmLoading: false,
}

View File

@ -0,0 +1,218 @@
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
<#assign bpm_flag=false>
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
<#assign list_need_category=true>
</#if>
<#if po.classType=='pca'>
<#assign list_need_pca=true>
</#if>
</#list>
<#-- 结束循环 -->
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="checkedKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
</div>
</template>
<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
import {ref, computed, unref} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'
import {useModal} from '/@/components/Modal';
import ${entityName}Modal from './components/${entityName}Modal.vue'
import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
<#if list_need_category>
import { loadCategoryData } from '/@/api/common/api'
import { getAuthCache, setAuthCache } from '/@/utils/auth';
import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
</#if>
const checkedKeys = ref<Array<string | number>>([]);
//注册model
const [registerModal, {openModal}] = useModal();
//注册table数据
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '${tableVo.ftlDescription}',
api: list,
columns,
canResize:false,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter:true,
showAdvancedButton:true,
fieldMapToTime: [
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.classType=='date'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
<#elseif po.classType=='datetime'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
</#if>
</#if>
</#if>
</#list>
],
},
actionColumn: {
width: 120,
},
},
exportConfig: {
name:"${tableVo.ftlDescription}",
url: getExportUrl,
},
importConfig: {
url: getImportUrl
},
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: checkedKeys.value}, reload);
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
}
}
]
}
<#if list_need_category>
/**
* 初始化字典配置
*/
function initDictConfig(){
<#list columns as po>
<#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
<#if po.classType=='cat_tree' && list_need_category==true>
loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
if (res) {
let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
if(!allDictDate['${po.dictField?default("")}']){
Object.assign(allDictDate,{'${po.dictField?default("")}':res})
}
setAuthCache(DB_DICT_DATA_KEY,allDictDate)
}
})
</#if>
</#if>
</#list>
}
initDictConfig();
</#if>
</script>
<style scoped>
</style>

View File

@ -0,0 +1,72 @@
import {defHttp} from '/@/utils/http/axios';
import {Modal} from 'ant-design-vue';
enum Api {
list = '/${entityPackage}/${entityName?uncap_first}/list',
save='/${entityPackage}/${entityName?uncap_first}/add',
edit='/${entityPackage}/${entityName?uncap_first}/edit',
deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
<#list subTables as sub><#rt/>
${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
</#list>
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
<#list subTables as sub><#rt/>
/**
* 查询子表数据
* @param params
*/
export const ${sub.entityName?uncap_first}List = Api.${sub.entityName?uncap_first}List;
</#list>
/**
* 列表接口
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
* 删除单个
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}

View File

@ -0,0 +1,700 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
//列表数据
export const columns: BasicColumn[] = [
<#list columns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
//查询数据
export const searchFormSchema: FormSchema[] = [
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isQuery=='Y'>
<#assign query_flag=true>
<#assign query_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictField}">
</#if>
<#if po.queryMode=='single'>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${po.dictTable},${po.dictText},${po.dictField}"
},
<#elseif po.classType=='sel_user'>
component: 'JSelectUserByDept',
<#elseif po.classType=='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:"${po.dictField}"
</#if>
},
<#elseif po.classType=='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType=='list_multi'>
component: 'JMultiSelectTag',//暂无该组件
componentProps:{
dictCode:"query_field_dictCode?default("")"
},
<#elseif po.classType=='cat_tree'>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}",//back和事件未添加暂时有问题
},
<#elseif po.classType=='date'>
component: 'DatePicker',
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.classType=='pca'>
component: 'JAreaLinkage',
<#elseif po.classType=='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:"${po.dictField}",
multi:${po.extendParams.popupMulti?c},
}
},
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
component: 'JDictSelectTag',
componentProps:{
<#if po.dictTable?default("")?trim?length gt 1>
dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
<#elseif po.dictField?default("")?trim?length gt 1>
dictCode:"${po.dictField}"
</#if>
},
<#else>
component: 'Input',
</#if>
colProps: {span: 6},
},
<#else>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='date'>
component: 'RangePicker',
<#elseif po.classType=='datetime'>
component: 'RangePicker',
componentProps: {
showTime:true
},
<#else>
component: 'Input', //TODO 范围查询
</#if>
colProps: {span: 6},
},
</#if>
</#if>
</#list>
<#-- 结束循环 -->
];
//表单数据
export const formSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list columns as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
//子表单数据
<#list subTables as sub>
<#if sub.foreignRelationType =='1'>
export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list sub.colums as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
</#if>
</#list>
//子表表格配置
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
<#assign popupBackFields = "">
<#-- 循环子表的列 开始 -->
<#list sub.colums as col><#rt/>
<#if col.isShow =='Y'>
<#if col.filedComment !='外键' >
{
title: '${col.filedComment}',
key: '${autoStringSuffixForModel(col)}',
<#if col.classType =='date'>
type: JVxeTypes.date,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='datetime'>
type: JVxeTypes.datetime,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='textarea'>
type: JVxeTypes.textarea,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif "int,decimal,double,"?contains(col.classType)>
type: JVxeTypes.inputNumber,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='list' || col.classType =='radio'>
type: JVxeTypes.select,
options:[],
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='list_multi' || col.classType =='checkbox'>
type: JVxeTypes.selectMultiple,
options:[],
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='sel_search'>
type: JVxeTypes.selectSearch,
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='image'>
type: JVxeTypes.image,
token:true,
responseName:"message",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#if col.uploadnum??>
number: ${col.uploadnum},
</#if>
<#elseif col.classType =='file'>
type: JVxeTypes.file,
token:true,
responseName:"message",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#if col.uploadnum??>
number: ${col.uploadnum},
</#if>
<#elseif col.classType =='switch'>
type: JVxeTypes.checkbox,
<#if col.dictField == 'is_open'>
customValue: ['Y', 'N'],
<#else>
customValue: ${col.dictField},
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='popup'>
<#if popupBackFields?length gt 0>
<#assign popupBackFields = "${popupBackFields}"+","+"${col.dictText}">
<#else>
<#assign popupBackFields = "${col.dictText}">
</#if>
type: JVxeTypes.popup,
popupCode:"${col.dictTable}",
field:"${col.dictField}",
orgFields:"${col.dictField}",
destFields:"${Format.underlineToHump(col.dictText)}",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#else>
type: JVxeTypes.input,
<#if col.readonly=='Y'>
disabled:true,
</#if>
</#if>
<#if col.classType =='list_multi' || col.classType =='checkbox'>
width:"250px",
<#else>
width:"200px",
</#if>
<#if col.classType =='file'>
placeholder: '请选择文件',
<#else>
placeholder: '请输入${'$'}{title}',
</#if>
<#if col.defaultVal??>
<#if col.fieldDbType=="BigDecimal" || col.fieldDbType=="double" || col.fieldDbType=="int">
defaultValue:${col.defaultVal},
<#else>
defaultValue:"${col.defaultVal}",
</#if>
<#else>
defaultValue:'',
</#if>
<#-- 子表的校验 -->
<#assign subFieldValidType = col.fieldValidType!''>
<#-- 非空校验 -->
<#if col.nullable == 'N' || subFieldValidType == '*'>
validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
<#-- 其他情况下,只要有值就被认为是正则校验 -->
<#elseif subFieldValidType?length gt 0>
<#assign subMessage = '格式不正确'>
<#if subFieldValidType == 'only' >
<#assign subMessage = '不能重复'>
</#if>
validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
</#if>
},
</#if>
</#if>
</#list>
<#-- 循环子表的列 结束 -->
]
</#if>
</#list>

View File

@ -0,0 +1,179 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<BasicForm @register="registerForm" ref="formRef"/>
<!-- 子表单区域 -->
<a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
<a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
<${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
</a-tab-pane>
<#else>
<a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index}]" :forceRender="true">
<JVxeTable
keep-source
resizable
:ref="refKeys[${sub_index}]"
:loading="${sub.entityName?uncap_first}Table.loading"
:columns="${sub.entityName?uncap_first}Table.columns"
:dataSource="${sub.entityName?uncap_first}Table.dataSource"
:maxHeight="300"
:rowNumber="true"
:rowSelection="true"
:toolbar="true"
/>
</a-tab-pane>
</#if>
</#list>
</a-tabs>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref,reactive} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import { JVxeTable } from '/@/components/jeecg/JVxeTable'
import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'
<#list subTables as sub>
<#if sub.foreignRelationType =='1'>
import ${sub.entityName}Form from './${sub.entityName}Form.vue'
</#if>
</#list>
import {formSchema<#list subTables as sub><#if sub.foreignRelationType =='0'>,${sub.entityName?uncap_first}Columns</#if></#list>} from '../${entityName?uncap_first}.data';
import {saveOrUpdate<#list subTables as sub>,${sub.entityName?uncap_first}List</#list>} from '../${entityName?uncap_first}.api';
import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
const refKeys = ref([<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
<#assign hasOne2Many = false>
<#assign hasOne2One = false>
const activeKey = ref('${subTables[0].entityName?uncap_first}');
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
<#assign hasOne2Many = true>
const ${sub.entityName?uncap_first} = ref();
</#if>
<#if sub.foreignRelationType =='1'>
<#assign hasOne2One = true>
const ${sub.entityName?uncap_first}Form = ref();
</#if>
</#list>
const tableRefs = {<#list subTables as sub><#if sub.foreignRelationType =='0'>${sub.entityName?uncap_first}, <#assign hasOne2Many = true></#if></#list>};
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
const ${sub.entityName?uncap_first}Table = reactive({
loading: false,
dataSource: [],
columns:${sub.entityName?uncap_first}Columns
})
</#if>
</#list>
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await reset();
setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.initFormData(${sub.entityName?uncap_first}List,data?.record?.id)
</#if>
</#list>
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='0'>
requestSubTableData(${sub.entityName?uncap_first}List, {id:data?.record?.id}, ${sub.entityName?uncap_first}Table)
</#if>
</#list>
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//方法配置
const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys<#if hasOne2One==true>,validateSubForm</#if>);
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
async function reset(){
await resetFields();
activeKey.value = ref('${subTables[0].entityName?uncap_first}');
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
${sub.entityName?uncap_first}Table.dataSource = [];
</#if>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.resetFields();
</#if>
</#list>
}
function classifyIntoFormData(allValues) {
let main = Object.assign({}, allValues.formValue)
return {
...main, // 展开
<#assign subManyIndex = 0>
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='0'>
${sub.entityName?uncap_first}List: allValues.tablesValue[${subManyIndex}].tableData,
<#assign subManyIndex = subManyIndex+1>
<#else>
${sub.entityName?uncap_first}List: ${sub.entityName?uncap_first}Form.value.getFormData(),
</#if>
</#list>
}
}
<#if hasOne2One==true>
//校验所有一对一子表表单
function validateSubForm(allValues){
return new Promise((resolve,reject)=>{
Promise.all([
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.validateForm(${sub_index}),
</#if>
</#list>
]).then(() => {
resolve(allValues)
}).catch(e => {
if (e.error === VALIDATE_FAILED) {
// 如果有未通过表单验证的子表就自动跳转到它所在的tab
activeKey.value = e.index == null ? unref(activeKey) : refKeys.value[e.index]
} else {
console.error(e)
}
})
})
}
</#if>
//表单提交事件
async function requestAddOrEdit(values) {
try {
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,64 @@
<#list subTables as sub>
<#if sub.foreignRelationType=='1'>
#segment#${sub.entityName}Form.vue
<template>
<BasicForm @register="registerForm"/>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import {BasicForm, useForm} from '/@/components/Form/index';
import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
import {defHttp} from '/@/utils/http/axios';
import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
export default defineComponent({
name:"${sub.entityName}Form",
components: {BasicForm},
emits:['register'],
setup(_,{emit}) {
const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
labelWidth: 150,
schemas: ${sub.entityName?uncap_first}FormSchema,
showActionButtonGroup: false,
});
/**
*初始化加载数据
*/
function initFormData(url,id){
if(id){
defHttp.get({url,params:{id}},{isTransformResponse:false}).then(res=>{
res.success && setFieldsValue({...res.result[0]});
})
}
}
/**
*获取表单数据
*/
function getFormData(){
return [getFieldsValue()];
}
/**
*表单校验
*/
function validateForm(index){
return new Promise((resolve, reject) => {
// 验证子表表单
validate().then(()=>{
return resolve()
}).catch(()=> {
return reject({ error: VALIDATE_FAILED ,index})
})
})
}
return {
registerForm,
resetFields,
initFormData,
getFormData,
validateForm
}
}
})
</script>
</#if>
</#list>

View File

@ -110,7 +110,7 @@ public class ${entityName}Controller {
*/
@AutoLog(value = "${tableVo.ftlDescription}-编辑")
@ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody ${entityName}Page ${entityName?uncap_first}Page) {
${entityName} ${entityName?uncap_first} = new ${entityName}();
BeanUtils.copyProperties(${entityName?uncap_first}Page, ${entityName?uncap_first});

View File

@ -8,7 +8,7 @@
<a-tabs v-model="activeKey" @change="handleChangeTabs">
<!--主表区域 -->
<a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true">
<a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true" :class="'jeecg-tabs-top'" :animated="false">
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-row>
<#list columns as po>
@ -19,13 +19,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :xs="24" :sm="12">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%" <#if po.readonly=='Y'>disabled</#if>/>
<#elseif po.classType =='datetime'>
@ -160,20 +155,12 @@ export default {
return {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
// 新增时子表默认添加几行空数据
addDefaultRowNum: 1,
model:{
@ -516,4 +503,8 @@ export default {
</script>
<style scoped>
/** tab panel 中有下拉框/日期 这类带下拉效果的,需要加此样式 */
/deep/ .jeecg-tabs-top {
overflow: visible;
}
</style>

View File

@ -25,13 +25,8 @@
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
<#if po.classType =='textarea'>
<a-col :span="24">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol2" :wrapperCol="wrapperCol2" prop="${autoStringSuffixForModel(po)}">
<#else>
<a-col :span="${form_span}">
<a-form-model-item label="${po.filedComment}" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="${autoStringSuffixForModel(po)}">
</#if>
<#if po.classType =='date'>
<j-date placeholder="请选择${po.filedComment}" v-model="model.${po.fieldName}" style="width: 100%"/>
<#elseif po.classType =='datetime'>
@ -118,20 +113,12 @@
},
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
labelCol2: {
xs: { span: 24 },
sm: { span: 3 },
},
wrapperCol2: {
xs: { span: 24 },
sm: { span: 20 },
},
<#include "/common/validatorRulesTemplate/sub.ftl">
confirmLoading: false,
}

View File

@ -0,0 +1,218 @@
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
<#assign bpm_flag=false>
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.classType=='cat_tree' && po.dictText?default("")?trim?length == 0>
<#assign list_need_category=true>
</#if>
<#if po.classType=='pca'>
<#assign list_need_pca=true>
</#if>
</#list>
<#-- 结束循环 -->
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="checkedKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button>批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template #htmlSlot="{text}">
<div v-html="text"></div>
</template>
<template #fileSlot="{text}">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template>
</BasicTable>
<!-- 表单区域 -->
<${entityName}Modal @register="registerModal" @success="handleSuccess"></${entityName}Modal>
</div>
</template>
<script lang="ts" name="${entityPackage}-${entityName?uncap_first}" setup>
import {ref, computed, unref} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'
import {useModal} from '/@/components/Modal';
import ${entityName}Modal from './components/${entityName}Modal.vue'
import {columns, searchFormSchema} from './${entityName?uncap_first}.data';
import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './${entityName?uncap_first}.api';
<#if list_need_category>
import { loadCategoryData } from '/@/api/common/api'
import { getAuthCache, setAuthCache } from '/@/utils/auth';
import { DB_DICT_DATA_KEY } from '/@/enums/cacheEnum';
</#if>
const checkedKeys = ref<Array<string | number>>([]);
//注册model
const [registerModal, {openModal}] = useModal();
//注册table数据
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '${tableVo.ftlDescription}',
api: list,
columns,
canResize:false,
formConfig: {
labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter:true,
showAdvancedButton:true,
fieldMapToTime: [
<#list columns as po>
<#if po.isQuery=='Y'>
<#if po.queryMode!='single'>
<#if po.classType=='date'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD'],
<#elseif po.classType=='datetime'>
['${po.fieldName}', ['${po.fieldName}_begin', '${po.fieldName}_end'], 'YYYY-MM-DD HH:mm:ss'],
</#if>
</#if>
</#if>
</#list>
],
},
actionColumn: {
width: 120,
},
},
exportConfig: {
name:"${tableVo.ftlDescription}",
url: getExportUrl,
},
importConfig: {
url: getImportUrl
},
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, reload);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: checkedKeys.value}, reload);
}
/**
* 成功回调
*/
function handleSuccess() {
reload();
}
/**
* 操作栏
*/
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
}
}
]
}
<#if list_need_category>
/**
* 初始化字典配置
*/
function initDictConfig(){
<#list columns as po>
<#if (po.isQuery=='Y' || po.isShowList=='Y') && po.classType!='popup'>
<#if po.classType=='cat_tree' && list_need_category==true>
loadCategoryData({code:'${po.dictField?default("")}'}).then((res) => {
if (res) {
let allDictDate = getAuthCache(DB_DICT_DATA_KEY);
if(!allDictDate['${po.dictField?default("")}']){
Object.assign(allDictDate,{'${po.dictField?default("")}':res})
}
setAuthCache(DB_DICT_DATA_KEY,allDictDate)
}
})
</#if>
</#if>
</#list>
}
initDictConfig();
</#if>
</script>
<style scoped>
</style>

View File

@ -0,0 +1,72 @@
import {defHttp} from '/@/utils/http/axios';
import {Modal} from 'ant-design-vue';
enum Api {
list = '/${entityPackage}/${entityName?uncap_first}/list',
save='/${entityPackage}/${entityName?uncap_first}/add',
edit='/${entityPackage}/${entityName?uncap_first}/edit',
deleteOne = '/${entityPackage}/${entityName?uncap_first}/delete',
deleteBatch = '/${entityPackage}/${entityName?uncap_first}/deleteBatch',
importExcel = '/${entityPackage}/${entityName?uncap_first}/importExcel',
exportXls = '/${entityPackage}/${entityName?uncap_first}/exportXls',
<#list subTables as sub><#rt/>
${sub.entityName?uncap_first}List = '/${entityPackage}/${entityName?uncap_first}/query${sub.entityName}ByMainId',
</#list>
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
<#list subTables as sub><#rt/>
/**
* 查询子表数据
* @param params
*/
export const ${sub.entityName?uncap_first}List = Api.${sub.entityName?uncap_first}List;
</#list>
/**
* 列表接口
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
* 删除单个
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
Modal.confirm({
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
* 保存或者更新
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}

View File

@ -0,0 +1,700 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import {JVxeTypes,JVxeColumn} from '/@/components/jeecg/JVxeTable/types'
//列表数据
export const columns: BasicColumn[] = [
<#list columns as po>
<#if po.isShowList =='Y' && po.fieldName !='id'>
{
title: '${po.filedComment}',
align:"center",
<#if po.sort=='Y'>
sorter: true,
</#if>
<#if po.classType=='date'>
dataIndex: '${po.fieldName}',
customRender:({text}) =>{
return !text?"":(text.length>10?text.substr(0,10):text)
},
<#elseif po.fieldDbType=='Blob'>
dataIndex: '${po.fieldName}String'
<#elseif po.classType=='umeditor'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'htmlSlot' },
<#elseif po.classType=='pca'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'pcaSlot' },//TODO 未翻译
<#elseif po.classType=='file'>
dataIndex: '${po.fieldName}',
slots: { customRender: 'fileSlot' },
<#elseif po.classType=='image'>
dataIndex: '${po.fieldName}',
customRender:render.renderAvatar,
<#elseif po.classType=='switch'>
dataIndex: '${po.fieldName}',
<#assign switch_extend_arr=['Y','N']>
<#if po.dictField?default("")?contains("[")>
<#assign switch_extend_arr=po.dictField?eval>
</#if>
<#list switch_extend_arr as a>
<#if a_index == 0>
<#assign switch_extend_arr1=a>
<#else>
<#assign switch_extend_arr2=a>
</#if>
</#list>
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'${switch_extend_arr1}'},{text:'否',value:'${switch_extend_arr2}'}])
},
<#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'>
dataIndex: '${po.fieldName}_dictText'
<#elseif po.classType=='cat_tree'>
dataIndex: '${po.fieldName}',
<#if po.dictText?default("")?trim?length == 0>
customRender:({text}) => {
return render.renderCategoryTree(text,'${po.dictField?default("")}')
},
<#else>
customRender: (text, record) => (text ? record['${po.dictText}'] : '')
</#if>
<#else>
dataIndex: '${po.fieldName}'
</#if>
},
</#if>
</#list>
];
//查询数据
export const searchFormSchema: FormSchema[] = [
<#-- 开始循环 -->
<#list columns as po>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isQuery=='Y'>
<#assign query_flag=true>
<#assign query_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign query_field_dictCode="${po.dictField}">
</#if>
<#if po.queryMode=='single'>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${po.dictTable},${po.dictText},${po.dictField}"
},
<#elseif po.classType=='sel_user'>
component: 'JSelectUserByDept',
<#elseif po.classType=='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:"${po.dictField}"
</#if>
},
<#elseif po.classType=='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType=='list_multi'>
component: 'JMultiSelectTag',//暂无该组件
componentProps:{
dictCode:"query_field_dictCode?default("")"
},
<#elseif po.classType=='cat_tree'>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}",//back和事件未添加暂时有问题
},
<#elseif po.classType=='date'>
component: 'DatePicker',
<#elseif po.classType=='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.classType=='pca'>
component: 'JAreaLinkage',
<#elseif po.classType=='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:"${po.dictField}",
multi:${po.extendParams.popupMulti?c},
}
},
<#elseif po.classType=='list' || po.classType=='radio' || po.classType=='checkbox'>
<#-- ---------------------------下拉或是单选 判断数据字典是表字典还是普通字典------------------------------- -->
component: 'JDictSelectTag',
componentProps:{
<#if po.dictTable?default("")?trim?length gt 1>
dictCode:"${po.dictTable},${po.dictText},${po.dictField}"
<#elseif po.dictField?default("")?trim?length gt 1>
dictCode:"${po.dictField}"
</#if>
},
<#else>
component: 'Input',
</#if>
colProps: {span: 6},
},
<#else>
{
label: "${po.filedComment}",
field: "${po.fieldName}",
<#if po.classType=='date'>
component: 'RangePicker',
<#elseif po.classType=='datetime'>
component: 'RangePicker',
componentProps: {
showTime:true
},
<#else>
component: 'Input', //TODO 范围查询
</#if>
colProps: {span: 6},
},
</#if>
</#if>
</#list>
<#-- 结束循环 -->
];
//表单数据
export const formSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list columns as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
//子表单数据
<#list subTables as sub>
<#if sub.foreignRelationType =='1'>
export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#assign form_cat_tree = false>
<#assign form_cat_back = "">
<#assign bpm_flag=false>
<#list sub.colums as po><#rt/>
<#if po.fieldDbName=='bpm_status'>
<#assign bpm_flag=true>
</#if>
<#if po.isShow =='Y'>
<#assign form_field_dictCode="">
<#if po.dictTable?default("")?trim?length gt 1 && po.dictText?default("")?trim?length gt 1 && po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictTable},${po.dictText},${po.dictField}">
<#elseif po.dictField?default("")?trim?length gt 1>
<#assign form_field_dictCode="${po.dictField}">
</#if>
{
label: '${po.filedComment}',
field: '${po.fieldName}',
<#if po.classType =='date'>
component: 'DatePicker',
<#elseif po.fieldType =='datetime'>
component: 'DatePicker',
componentProps: {
showTime:true
},
<#elseif po.fieldType =='time'>
component: 'TimePicker',
<#elseif po.classType =='popup'>
component: 'JPopup',
componentProps: ({ formActionType }) => {
const {setFieldsValue} = formActionType;
return{
setFieldsValue:setFieldsValue,
code:"${po.dictTable}",
fieldConfig:${po.dictField},
multi:${po.extendParams.popupMulti?c},
}
}
<#elseif po.classType =='sel_depart'>
component: 'JSelectDept',
<#elseif po.classType =='switch'>
component: 'JSwitch',
componentProps:{
<#if po.dictField != 'is_open'>
options:${po.dictField}
</#if>
}
<#elseif po.classType =='pca'>
component: 'JAreaLinkage',
<#elseif po.classType =='markdown'>
component: 'JMarkdownEditor',//注意string转换问题
<#elseif po.classType =='password'>
component: 'InputPassword',
<#elseif po.classType =='sel_user'>
component: 'JSelectUserByDept',
componentProps:{
labelKey:'realname',
}
<#elseif po.classType =='textarea'>
component: 'InputTextArea',//TODO 注意string转换问题
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='list_multi' || po.classType=='checkbox'>
component: 'JMultiSelectTag',//TODO 暂无该组件
componentProps:{
dictCode:"${form_field_dictCode}"
}
<#elseif po.classType=='sel_search'>
component: 'JSearchSelect',
componentProps:{
dict:"${form_field_dictCode}"
}
<#elseif po.classType=='cat_tree'>
<#assign form_cat_tree = true>
component: 'JCategorySelect',
componentProps:{
pcode:"${po.dictField?default("")}", //TODO back和事件未添加暂时有问题
}
<#if po.dictText?default("")?trim?length gt 1>
<#assign form_cat_back = "${po.dictText}">
</#if>
<#elseif po.fieldDbType=='int' || po.fieldDbType=='double' || po.fieldDbType=='BigDecimal'>
component: 'InputNumber',
<#elseif po.classType=='file'>
component: 'JUpload',
componentProps:{
<#if po.uploadnum??>
maxCount:${po.uploadnum}
</#if>
}
<#elseif po.classType=='image'>
component: 'JImageUpload',
componentProps:{
<#if po.uploadnum??>
fileMax:${po.uploadnum}
</#if>
}
<#elseif po.classType=='umeditor'>
component: 'JCodeEditor', //TODO String后缀暂未添加
<#elseif po.classType == 'sel_tree'>
component: 'JTreeSelect',
componentProps:{
<#if po.dictText??>
<#if po.dictText?split(',')[2]?? && po.dictText?split(',')[0]??>
dict:"${po.dictTable},${po.dictText?split(',')[2]},${po.dictText?split(',')[0]}",
<#elseif po.dictText?split(',')[1]??>
pidField:"${po.dictText?split(',')[1]}",
<#elseif po.dictText?split(',')[3]??>
hasChildField:"${po.dictText?split(',')[3]}",
</#if>
</#if>
pidValue:"${po.dictField}",
}
<#else>
component: 'Input',
</#if>
<#include "/common/utils.ftl">
<#if po.isShow == 'Y' && poHasCheck(po)>
dynamicRules: ({model,schema}) => {
<#if po.fieldName != 'id'>
<#assign fieldValidType = po.fieldValidType!''>
return [
<#-- 非空校验 -->
<#if po.nullable == 'N' || fieldValidType == '*'>
{ required: true, message: '请输入${po.filedComment}!'},
<#elseif fieldValidType!=''>
{ required: false},
</#if>
<#-- 唯一校验 -->
<#if fieldValidType == 'only'>
{...rules.duplicateCheckRule(<#if sub?default("")?trim?length gt 1>'${sub.tableName}'<#else>'${tableName}'</#if>, '${po.fieldDbName}',model,schema,true)[0]},
<#-- 6到16位数字 -->
<#elseif fieldValidType == 'n6-16'>
{ pattern: /^\d{6,16}$/, message: '请输入6到16位数字!'},
<#-- 6到16位任意字符 -->
<#elseif fieldValidType == '*6-16'>
{ pattern: /^.{6,16}$/, message: '请输入6到16位任意字符!'},
<#-- 6到18位字符串 -->
<#elseif fieldValidType == 's6-18'>
{ pattern: /^.{6,18}$/, message: '请输入6到18位任意字符!'},
<#-- 网址 -->
<#elseif fieldValidType == 'url'>
{ pattern: /^((ht|f)tps?):\/\/[\w\-]+(\.[\w\-]+)+([\w\-.,@?^=%&:\/~+#]*[\w\-@?^=%&\/~+#])?$/, message: '请输入正确的网址!'},
<#-- 电子邮件 -->
<#elseif fieldValidType == 'e'>
{ pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/, message: '请输入正确的电子邮件!'},
<#-- 手机号码 -->
<#elseif fieldValidType == 'm'>
{ pattern: /^1[3456789]\d{9}$/, message: '请输入正确的手机号码!'},
<#-- 邮政编码 -->
<#elseif fieldValidType == 'p'>
{ pattern: /^[1-9]\d{5}$/, message: '请输入正确的邮政编码!'},
<#-- 字母 -->
<#elseif fieldValidType == 's'>
{ pattern: /^[A-Z|a-z]+$/, message: '请输入字母!'},
<#-- 数字 -->
<#elseif fieldValidType == 'n'>
{ pattern: /^-?\d+\.?\d*$/, message: '请输入数字!'},
<#-- 整数 -->
<#elseif fieldValidType == 'z'>
{ pattern: /^-?\d+$/, message: '请输入整数!'},
<#-- 金额 -->
<#elseif fieldValidType == 'money'>
{ pattern: /^(([1-9][0-9]*)|([0]\.\d{0,2}|[1-9][0-9]*\.\d{0,2}))$/, message: '请输入正确的金额!'},
<#-- 正则校验 -->
<#elseif fieldValidType != '' && fieldValidType != '*'>
{ pattern: '${fieldValidType}', message: '不符合校验规则!'},
<#-- 无校验 -->
<#else>
<#t>
</#if>
];
</#if>
},
</#if>
<#if po.readonly=='Y'>
dynamicDisabled:true
</#if>
},
</#if>
</#list>
];
</#if>
</#list>
//子表表格配置
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
export const ${sub.entityName?uncap_first}Columns: JVxeColumn[] = [
<#assign popupBackFields = "">
<#-- 循环子表的列 开始 -->
<#list sub.colums as col><#rt/>
<#if col.isShow =='Y'>
<#if col.filedComment !='外键' >
{
title: '${col.filedComment}',
key: '${autoStringSuffixForModel(col)}',
<#if col.classType =='date'>
type: JVxeTypes.date,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='datetime'>
type: JVxeTypes.datetime,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='textarea'>
type: JVxeTypes.textarea,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif "int,decimal,double,"?contains(col.classType)>
type: JVxeTypes.inputNumber,
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='list' || col.classType =='radio'>
type: JVxeTypes.select,
options:[],
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='list_multi' || col.classType =='checkbox'>
type: JVxeTypes.selectMultiple,
options:[],
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='sel_search'>
type: JVxeTypes.selectSearch,
<#if col.dictTable?default("")?trim?length gt 1>
dictCode:"${col.dictTable},${col.dictText},${col.dictField}",
<#else>
dictCode:"${col.dictField}",
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='image'>
type: JVxeTypes.image,
token:true,
responseName:"message",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#if col.uploadnum??>
number: ${col.uploadnum},
</#if>
<#elseif col.classType =='file'>
type: JVxeTypes.file,
token:true,
responseName:"message",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#if col.uploadnum??>
number: ${col.uploadnum},
</#if>
<#elseif col.classType =='switch'>
type: JVxeTypes.checkbox,
<#if col.dictField == 'is_open'>
customValue: ['Y', 'N'],
<#else>
customValue: ${col.dictField},
</#if>
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#elseif col.classType =='popup'>
<#if popupBackFields?length gt 0>
<#assign popupBackFields = "${popupBackFields}"+","+"${col.dictText}">
<#else>
<#assign popupBackFields = "${col.dictText}">
</#if>
type: JVxeTypes.popup,
popupCode:"${col.dictTable}",
field:"${col.dictField}",
orgFields:"${col.dictField}",
destFields:"${Format.underlineToHump(col.dictText)}",
<#if col.readonly=='Y'>
disabled:true,
</#if>
<#else>
type: JVxeTypes.input,
<#if col.readonly=='Y'>
disabled:true,
</#if>
</#if>
<#if col.classType =='list_multi' || col.classType =='checkbox'>
width:"250px",
<#else>
width:"200px",
</#if>
<#if col.classType =='file'>
placeholder: '请选择文件',
<#else>
placeholder: '请输入${'$'}{title}',
</#if>
<#if col.defaultVal??>
<#if col.fieldDbType=="BigDecimal" || col.fieldDbType=="double" || col.fieldDbType=="int">
defaultValue:${col.defaultVal},
<#else>
defaultValue:"${col.defaultVal}",
</#if>
<#else>
defaultValue:'',
</#if>
<#-- 子表的校验 -->
<#assign subFieldValidType = col.fieldValidType!''>
<#-- 非空校验 -->
<#if col.nullable == 'N' || subFieldValidType == '*'>
validateRules: [{ required: true, message: '${'$'}{title}不能为空' }],
<#-- 其他情况下,只要有值就被认为是正则校验 -->
<#elseif subFieldValidType?length gt 0>
<#assign subMessage = '格式不正确'>
<#if subFieldValidType == 'only' >
<#assign subMessage = '不能重复'>
</#if>
validateRules: [{ pattern: "${subFieldValidType}", message: "${'$'}{title}${subMessage}" }],
</#if>
},
</#if>
</#if>
</#list>
<#-- 循环子表的列 结束 -->
]
</#if>
</#list>

View File

@ -0,0 +1,183 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" @ok="handleSubmit">
<!-- 子表单区域 -->
<a-tabs v-model:activeKey="activeKey" @change="handleChangeTabs">
<!--主表区域 -->
<a-tab-pane tab="${tableVo.ftlDescription}" :key="refKeys[0]" :forceRender="true">
<BasicForm @register="registerForm" ref="formRef"/>
</a-tab-pane>
<!--子表单区域 -->
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
<a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index+1}]" :forceRender="true">
<${sub.entityName}Form ref="${sub.entityName?uncap_first}Form"></${sub.entityName}Form>
</a-tab-pane>
<#else>
<a-tab-pane tab="${sub.ftlDescription}" :key="refKeys[${sub_index+1}]" :forceRender="true">
<JVxeTable
keep-source
resizable
:ref="refKeys[${sub_index+1}]"
:loading="${sub.entityName?uncap_first}Table.loading"
:columns="${sub.entityName?uncap_first}Table.columns"
:dataSource="${sub.entityName?uncap_first}Table.dataSource"
:maxHeight="300"
:rowNumber="true"
:rowSelection="true"
:toolbar="true"
/>
</a-tab-pane>
</#if>
</#list>
</a-tabs>
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref,reactive} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import { JVxeTable } from '/@/components/jeecg/JVxeTable'
import { useJvxeMethod } from '/@/hooks/system/useJvxeMethods.ts'
<#list subTables as sub>
<#if sub.foreignRelationType =='1'>
import ${sub.entityName}Form from './${sub.entityName}Form.vue'
</#if>
</#list>
import {formSchema<#list subTables as sub><#if sub.foreignRelationType =='0'>,${sub.entityName?uncap_first}Columns</#if></#list>} from '../${entityName?uncap_first}.data';
import {saveOrUpdate<#list subTables as sub>,${sub.entityName?uncap_first}List</#list>} from '../${entityName?uncap_first}.api';
import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
const refKeys = ref(['${tableVo.entityName?uncap_first}',<#list subTables as sub>'${sub.entityName?uncap_first}', </#list>]);
<#assign hasOne2Many = false>
<#assign hasOne2One = false>
const activeKey = ref('${tableVo.entityName?uncap_first}');
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
<#assign hasOne2Many = true>
const ${sub.entityName?uncap_first} = ref();
</#if>
<#if sub.foreignRelationType =='1'>
<#assign hasOne2One = true>
const ${sub.entityName?uncap_first}Form = ref();
</#if>
</#list>
const tableRefs = {<#list subTables as sub><#if sub.foreignRelationType =='0'>${sub.entityName?uncap_first}, <#assign hasOne2Many = true></#if></#list>};
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
const ${sub.entityName?uncap_first}Table = reactive({
loading: false,
dataSource: [],
columns:${sub.entityName?uncap_first}Columns
})
</#if>
</#list>
//表单配置
const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await reset();
setModalProps({confirmLoading: false,showCancelBtn:data?.showFooter,showOkBtn:data?.showFooter});
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.initFormData(${sub.entityName?uncap_first}List,data?.record?.id)
</#if>
</#list>
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='0'>
requestSubTableData(${sub.entityName?uncap_first}List, {id:data?.record?.id}, ${sub.entityName?uncap_first}Table)
</#if>
</#list>
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//方法配置
const [handleChangeTabs,handleSubmit,requestSubTableData,formRef] = useJvxeMethod(requestAddOrEdit,classifyIntoFormData,tableRefs,activeKey,refKeys<#if hasOne2One==true>,validateSubForm</#if>);
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
async function reset(){
await resetFields();
activeKey.value = ref('${tableVo.entityName?uncap_first}');
<#list subTables as sub>
<#if sub.foreignRelationType =='0'>
${sub.entityName?uncap_first}Table.dataSource = [];
</#if>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.resetFields();
</#if>
</#list>
}
function classifyIntoFormData(allValues) {
let main = Object.assign({}, allValues.formValue)
return {
...main, // 展开
<#assign subManyIndex = 0>
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='0'>
${sub.entityName?uncap_first}List: allValues.tablesValue[${subManyIndex}].tableData,
<#assign subManyIndex = subManyIndex+1>
<#else>
${sub.entityName?uncap_first}List: ${sub.entityName?uncap_first}Form.value.getFormData(),
</#if>
</#list>
}
}
<#if hasOne2One==true>
//校验所有一对一子表表单
function validateSubForm(allValues){
return new Promise((resolve,reject)=>{
Promise.all([
<#list subTables as sub><#rt/>
<#if sub.foreignRelationType =='1'>
${sub.entityName?uncap_first}Form.value.validateForm(${sub_index+1}),
</#if>
</#list>
]).then(() => {
resolve(allValues)
}).catch(e => {
if (e.error === VALIDATE_FAILED) {
// 如果有未通过表单验证的子表就自动跳转到它所在的tab
activeKey.value = e.index == null ? unref(activeKey) : refKeys.value[e.index]
} else {
console.error(e)
}
})
})
}
</#if>
//表单提交事件
async function requestAddOrEdit(values) {
try {
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} finally {
setModalProps({confirmLoading: false});
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,64 @@
<#list subTables as sub>
<#if sub.foreignRelationType=='1'>
#segment#${sub.entityName}Form.vue
<template>
<BasicForm @register="registerForm"/>
</template>
<script lang="ts">
import {defineComponent} from 'vue';
import {BasicForm, useForm} from '/@/components/Form/index';
import {${sub.entityName?uncap_first}FormSchema} from '../${entityName?uncap_first}.data';
import {defHttp} from '/@/utils/http/axios';
import { VALIDATE_FAILED } from '/@/utils/common/vxeUtils'
export default defineComponent({
name:"${sub.entityName}Form",
components: {BasicForm},
emits:['register'],
setup(_,{emit}) {
const [registerForm, {resetFields, setFieldsValue,getFieldsValue,validate}] = useForm({
labelWidth: 150,
schemas: ${sub.entityName?uncap_first}FormSchema,
showActionButtonGroup: false,
});
/**
*初始化加载数据
*/
function initFormData(url,id){
if(id){
defHttp.get({url,params:{id}},{isTransformResponse:false}).then(res=>{
res.success && setFieldsValue({...res.result[0]});
})
}
}
/**
*获取表单数据
*/
function getFormData(){
return [getFieldsValue()];
}
/**
*表单校验
*/
function validateForm(index){
return new Promise((resolve, reject) => {
// 验证子表表单
validate().then(()=>{
return resolve()
}).catch(()=> {
return reject({ error: VALIDATE_FAILED ,index})
})
})
}
return {
registerForm,
resetFields,
initFormData,
getFormData,
validateForm
}
}
})
</script>
</#if>
</#list>

View File

@ -93,7 +93,7 @@ public class ${entityName}Controller extends JeecgController<${entityName}, I${e
*/
@AutoLog(value = "${tableVo.ftlDescription}-编辑")
@ApiOperation(value="${tableVo.ftlDescription}-编辑", notes="${tableVo.ftlDescription}-编辑")
@PutMapping(value = "/edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody ${entityName} ${entityName?uncap_first}) {
${entityName?uncap_first}Service.updateById(${entityName?uncap_first});
return Result.OK("编辑成功!");

Some files were not shown because too many files have changed in this diff Show More