Compare commits

...

23 Commits

Author SHA1 Message Date
41877a6e8b 【issues/9275】用户组件第二次点击取消时勾选值还是回显了 2026-01-20 12:02:54 +08:00
b7a3da89ca 同步钉钉部门报错 #9228 2026-01-09 14:39:15 +08:00
de4a8ce652 --author:scott--date:20260108--for:升级minidao版本至1.10.18 2026-01-08 16:20:28 +08:00
e533af285c 【issues/9217】当配置了pagination: true时,BasicTable组件自适应高度异常 2025-12-31 16:43:12 +08:00
23dc7b3f03 字段类型是int,生成的代码,字典未匹配上,默认加上转换 2025-12-26 10:54:38 +08:00
e57aef0708 使用useListPage的导出异常 #9209 2025-12-26 10:50:11 +08:00
42087c0bf8 国际化 2025-12-24 18:27:13 +08:00
606edcc82f 国际化 2025-12-24 18:06:13 +08:00
9082e986f1 国际化 2025-12-24 18:05:16 +08:00
40cd525bba Merge branch 'main' of https://github.com/jeecgboot/jeecg-boot 2025-12-17 18:38:32 +08:00
d6b6cf079e 支持更多AI模型 2025-12-17 18:38:04 +08:00
1b688e7cd2 添加JUnit平台启动器依赖用于测试 2025-12-16 14:09:54 +08:00
58915a6410 Merge pull request #8878 from WolfCat-ICE/patch-1
Update renderUtils.ts 修复字典渲染renderTag使用tag渲染没使用字典配置颜色的问题
2025-12-15 17:42:56 +08:00
b67096dc54 Merge pull request #9004 from SunJary/patch-1
fix#9002 解决字典注解查询出现异常之后,数据源不能恢复问题
2025-12-15 17:24:33 +08:00
67795493bd 支持加签注解 @SignatureCheck,针对获取租户信息接口加签
【严重安全漏洞】用户可加入任意租户 #9196
2025-12-15 17:07:22 +08:00
e1c8f00bf2 【严重安全漏洞】用户可加入任意租户 #9196
jeecgboot模式的租户未做申请加入租户和审批逻辑,所以这俩接口注释掉
2025-12-15 17:02:16 +08:00
17a81e89a5 “用于后端字典翻译”,同一枚举dictCode,keys传多个也只add第1个DictModel #9124 2025-12-15 15:04:17 +08:00
bcbf775756 列表选字段导出异常 #9173 2025-12-15 10:56:06 +08:00
462365890e 表单添加了按钮并设置排序,代码生成报错 #9190 2025-12-15 10:49:19 +08:00
b686f9fbd1 【严重安全漏洞】未授权用户可强制任意在线用户下线,存在DOS攻击风险 #9195-- 2025-12-15 09:25:12 +08:00
872f84d006 【issues/9169】切换页码时,pageChange事件加载了两次 2025-12-10 09:43:36 +08:00
adc191f03e fix#9002 解决字典注解查询出现异常之后,数据源不能恢复问题 2025-10-20 22:21:47 +08:00
f6f2ef6316 Update renderUtils.ts 修复字典渲染renderTag使用tag渲染没使用字典配置颜色的问题
在renderDict方法中增加颜色属性传递,支持标签颜色渲染
render.renderDict(text, 'bpm_status',true)
2025-09-19 18:07:21 +08:00
44 changed files with 468 additions and 228 deletions

View File

@ -106,6 +106,10 @@ JeecgBoot平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发
| ChatGTP | √ |
| Qwq | √ |
| 智库 | √ |
| claude | √ |
| vl模型 | √ |
| 千帆大模型 | √ |
| 通义千问 | √ |
| Ollama本地搭建大模型 | √ |
| 等等。。 | √ |

View File

@ -1,4 +1,4 @@
[中文](./README.md) | English
![JEECG](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/logov3.png "JeecgBoot低代码开发平台")

View File

@ -1,3 +1,4 @@
中文 | [English](./README.en-US.md)
JeecgBoot AI低代码平台
===============

View File

@ -1,4 +1,3 @@
JeecgBoot 低代码开发平台
===============

View File

@ -3,9 +3,7 @@ package org.jeecg.common.system.util;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.annotation.EnumDict;
import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
@ -13,6 +11,7 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Method;
import java.util.*;
@ -183,10 +182,10 @@ public class ResourceUtil {
for (DictModel dm : dictItemList) {
String value = dm.getValue();
if (keySet.contains(value)) {
List<DictModel> list = new ArrayList<>();
// 修复bug获取或创建该dictCode对应的list而不是每次都创建新的list
List<DictModel> list = map.computeIfAbsent(code, k -> new ArrayList<>());
list.add(new DictModel(value, dm.getText()));
map.put(code, list);
break;
//break;
}
}
}

View File

@ -0,0 +1,32 @@
package org.jeecg.config.sign.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 签名校验注解
* 用于方法级别的签名验证功能等同于yml中的jeecg.signUrls配置
* 参考DragSignatureAspect的设计思路使用AOP切面实现
*
* @author GitHub Copilot
* @since 2025-12-15
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SignatureCheck {
/**
* 是否启用签名校验
* @return true-启用(默认), false-禁用
*/
boolean enabled() default true;
/**
* 签名校验失败时的错误消息
* @return 错误消息
*/
String errorMessage() default "Sign签名校验失败";
}

View File

@ -0,0 +1,93 @@
package org.jeecg.config.sign.aspect;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jeecg.config.sign.annotation.SignatureCheck;
import org.jeecg.config.sign.interceptor.SignAuthInterceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
/**
* 基于AOP的签名验证切面
* 复用SignAuthInterceptor的成熟签名验证逻辑
*
* @author GitHub Copilot
* @since 2025-12-15
*/
@Aspect
@Slf4j
@Component("signatureCheckAspect")
public class SignatureCheckAspect {
/**
* 复用SignAuthInterceptor的签名验证逻辑
*/
private final SignAuthInterceptor signAuthInterceptor = new SignAuthInterceptor();
/**
* 验签切点:拦截所有标记了@SignatureCheck注解的方法
*/
@Pointcut("@annotation(org.jeecg.config.sign.annotation.SignatureCheck)")
private void signatureCheckPointCut() {
}
/**
* 开始验签
*/
@Before("signatureCheckPointCut()")
public void doSignatureValidation(JoinPoint point) throws Exception {
// 获取方法上的注解
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
SignatureCheck signatureCheck = method.getAnnotation(SignatureCheck.class);
log.info("AOP签名验证: {}.{}", method.getDeclaringClass().getSimpleName(), method.getName());
// 如果注解被禁用,直接返回
if (!signatureCheck.enabled()) {
log.info("签名验证已禁用,跳过");
return;
}
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
log.error("无法获取请求上下文");
throw new IllegalArgumentException("无法获取请求上下文");
}
HttpServletRequest request = attributes.getRequest();
log.info("X-SIGN: {}, X-TIMESTAMP: {}", request.getHeader("X-SIGN"), request.getHeader("X-TIMESTAMP"));
try {
// 直接调用SignAuthInterceptor的验证逻辑
signAuthInterceptor.validateSignature(request);
log.info("AOP签名验证通过");
} catch (IllegalArgumentException e) {
// 使用注解中配置的错误消息,或者保留原始错误消息
String errorMessage = signatureCheck.errorMessage();
log.error("AOP签名验证失败: {}", e.getMessage());
if ("Sign签名校验失败".equals(errorMessage)) {
// 如果是默认错误消息,使用原始的详细错误信息
throw e;
} else {
// 如果是自定义错误消息,使用自定义消息
throw new IllegalArgumentException(errorMessage, e);
}
} catch (Exception e) {
// 包装其他异常
String errorMessage = signatureCheck.errorMessage();
log.error("AOP签名验证异常: {}", e.getMessage());
throw new IllegalArgumentException(errorMessage, e);
}
}
}

View File

@ -1,12 +1,10 @@
package org.jeecg.config.sign.interceptor;
import java.io.PrintWriter;
import java.util.SortedMap;
import com.alibaba.fastjson.JSON;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
@ -16,9 +14,8 @@ import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import java.io.PrintWriter;
import java.util.SortedMap;
/**
* 签名拦截器
@ -33,63 +30,94 @@ public class SignAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("Sign Interceptor request URI = " + request.getRequestURI());
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
//获取全部参数(包括URL和body上的)
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
//对参数进行签名验证
String headerSign = request.getHeader(CommonConstant.X_SIGN);
String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);
log.info("签名拦截器 Interceptor request URI = " + request.getRequestURI());
if(oConvertUtils.isEmpty(xTimestamp)){
Result<?> result = Result.error("Sign签名校验失败时间戳为空");
log.error("Sign 签名校验失败Header xTimestamp 为空");
//校验失败返回前端
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
out.print(JSON.toJSON(result));
return false;
}
//客户端时间
Long clientTimestamp = Long.parseLong(xTimestamp);
int length = 14;
int length1000 = 1000;
//1.校验签名时间兼容X_TIMESTAMP的新老格式
if (xTimestamp.length() == length) {
//a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子20220308152143)
if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) {
log.error("签名验证失败:X-TIMESTAMP已过期注意系统时间和服务器时间是否有误差");
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期");
}
} else {
//b. X_TIMESTAMP格式是 时间戳 (例子1646552406000)
if ((System.currentTimeMillis() - clientTimestamp) > (MAX_EXPIRE * length1000)) {
log.error("签名验证失败:X-TIMESTAMP已过期注意系统时间和服务器时间是否有误差");
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期");
}
}
//2.校验签名
boolean isSigned = SignUtil.verifySign(allParams,headerSign);
if (isSigned) {
log.debug("Sign 签名通过Header Sign : {}",headerSign);
try {
// 调用验证逻辑
validateSignature(request);
return true;
} else {
log.debug("sign allParams: {}", allParams);
log.error("request URI = " + request.getRequestURI());
log.error("Sign 签名校验失败Header Sign : {}",headerSign);
//校验失败返回前端
} catch (IllegalArgumentException e) {
// 验证失败,返回错误响应
log.error("Sign 签名校验失败!{}", e.getMessage());
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
Result<?> result = Result.error("Sign签名校验失败");
Result<?> result = Result.error(e.getMessage());
out.print(JSON.toJSON(result));
return false;
}
}
/**
* 签名验证核心逻辑
* 提取出来供AOP切面复用
* @param request HTTP请求
* @throws IllegalArgumentException 验证失败时抛出异常
*/
public void validateSignature(HttpServletRequest request) throws IllegalArgumentException {
try {
log.debug("开始签名验证: {} {}", request.getMethod(), request.getRequestURI());
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
//获取全部参数(包括URL和body上的)
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
log.debug("提取参数: {}", allParams);
//对参数进行签名验证
String headerSign = request.getHeader(CommonConstant.X_SIGN);
String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);
if(oConvertUtils.isEmpty(xTimestamp)){
log.error("Sign签名校验失败时间戳为空");
throw new IllegalArgumentException("Sign签名校验失败请求参数不完整");
}
//客户端时间
Long clientTimestamp = Long.parseLong(xTimestamp);
int length = 14;
int length1000 = 1000;
//1.校验签名时间兼容X_TIMESTAMP的新老格式
if (xTimestamp.length() == length) {
//a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子20220308152143)
long currentTimestamp = DateUtils.getCurrentTimestamp();
long timeDiff = currentTimestamp - clientTimestamp;
log.debug("时间戳验证(yyyyMMddHHmmss): 时间差{}秒", timeDiff);
if (timeDiff > MAX_EXPIRE) {
log.error("时间戳已过期: {}秒 > {}秒", timeDiff, MAX_EXPIRE);
throw new IllegalArgumentException("签名验证失败,请求时效性验证失败!");
}
} else {
//b. X_TIMESTAMP格式是 时间戳 (例子1646552406000)
long currentTime = System.currentTimeMillis();
long timeDiff = currentTime - clientTimestamp;
long maxExpireMs = MAX_EXPIRE * length1000;
log.debug("时间戳验证(Unix): 时间差{}ms", timeDiff);
if (timeDiff > maxExpireMs) {
log.error("时间戳已过期: {}ms > {}ms", timeDiff, maxExpireMs);
throw new IllegalArgumentException("签名验证失败,请求时效性验证失败!");
}
}
//2.校验签名
boolean isSigned = SignUtil.verifySign(allParams,headerSign);
if (isSigned) {
log.debug("签名验证通过");
} else {
log.error("签名验证失败, 参数: {}", allParams);
throw new IllegalArgumentException("Sign签名校验失败");
}
} catch (IllegalArgumentException e) {
// 重新抛出签名验证异常
throw e;
} catch (Exception e) {
// 包装其他异常如IOException
log.error("签名验证异常: {}", e.getMessage());
throw new IllegalArgumentException("Sign签名校验失败" + e.getMessage());
}
}
}

View File

@ -21,6 +21,7 @@ import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.config.sign.annotation.SignatureCheck;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.service.ISysTenantPackService;
@ -260,6 +261,7 @@ public class SysTenantController {
* @param id
* @return
*/
@SignatureCheck
@RequestMapping(value = "/queryById", method = RequestMethod.GET)
public Result<SysTenant> queryById(@RequestParam(name="id",required=true) String id) {
Result<SysTenant> result = new Result<SysTenant>();
@ -507,26 +509,26 @@ public class SysTenantController {
return result;
}
/**
* 加入租户通过门牌号【低代码应用专用接口】
* @param sysTenant
*/
@PostMapping("/joinTenantByHouseNumber")
public Result<Integer> joinTenantByHouseNumber(@RequestBody SysTenant sysTenant){
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
Integer tenantId = sysTenantService.joinTenantByHouseNumber(sysTenant, sysUser.getId());
Result<Integer> result = new Result<>();
if(tenantId != 0){
result.setMessage("申请加入组织成功");
result.setSuccess(true);
result.setResult(tenantId);
return result;
}else{
result.setMessage("该门牌号不存在");
result.setSuccess(false);
return result;
}
}
// /**
// * 加入租户通过门牌号【低代码应用专用接口】
// * @param sysTenant
// */
// @PostMapping("/joinTenantByHouseNumber")
// public Result<Integer> joinTenantByHouseNumber(@RequestBody SysTenant sysTenant){
// LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// Integer tenantId = sysTenantService.joinTenantByHouseNumber(sysTenant, sysUser.getId());
// Result<Integer> result = new Result<>();
// if(tenantId != 0){
// result.setMessage("申请加入组织成功");
// result.setSuccess(true);
// result.setResult(tenantId);
// return result;
// }else{
// result.setMessage("该门牌号不存在");
// result.setSuccess(false);
// return result;
// }
// }
/**
* 分页获取租户用户数据(vue3用户租户页面)【低代码应用专用接口】
@ -713,6 +715,7 @@ public class SysTenantController {
* @return
*/
@PostMapping("/invitationUser")
@RequiresPermissions("system:tenant:invitation:user")
public Result<String> invitationUser(@RequestParam(name="phone") String phone,
@RequestParam(name="departId",defaultValue = "") String departId){
return sysTenantService.invitationUser(phone,departId);
@ -911,43 +914,43 @@ public class SysTenantController {
return Result.ok(pageList);
}
/**
* 同意或拒绝加入租户
*/
@PutMapping("/agreeOrRefuseJoinTenant")
public Result<String> agreeOrRefuseJoinTenant(@RequestParam("tenantId") Integer tenantId,
@RequestParam("status") String status){
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId();
SysTenant tenant = sysTenantService.getById(tenantId);
if(null == tenant){
return Result.error("不存在该组织");
}
SysUserTenant sysUserTenant = relationService.getUserTenantByTenantId(userId, tenantId);
if (null == sysUserTenant) {
return Result.error("该用户不存在该组织中,无权修改");
}
String content = "";
SysUser user = new SysUser();
user.setUsername(sysUserTenant.getCreateBy());
String realname = oConvertUtils.getString(sysUser.getRealname(),sysUser.getUsername());
//成功加入
if(CommonConstant.USER_TENANT_NORMAL.equals(status)){
//修改租户状态
relationService.agreeJoinTenant(userId,tenantId);
content = content + realname + "已同意您发送的加入 " + tenant.getName() + " 的邀请";
sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content);
return Result.OK("您已同意该组织的邀请");
}else if(CommonConstant.USER_TENANT_REFUSE.equals(status)){
//直接删除关系表即可
relationService.refuseJoinTenant(userId,tenantId);
content = content + realname + "拒绝了您发送的加入 " + tenant.getName() + " 的邀请";
sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content);
return Result.OK("您已成功拒绝该组织的邀请");
}
return Result.error("类型不匹配,禁止修改数据");
}
// /**
// * 同意或拒绝加入租户
// */
// @PutMapping("/agreeOrRefuseJoinTenant")
// public Result<String> agreeOrRefuseJoinTenant(@RequestParam("tenantId") Integer tenantId,
// @RequestParam("status") String status){
// //是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
// LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// String userId = sysUser.getId();
// SysTenant tenant = sysTenantService.getById(tenantId);
// if(null == tenant){
// return Result.error("不存在该组织");
// }
// SysUserTenant sysUserTenant = relationService.getUserTenantByTenantId(userId, tenantId);
// if (null == sysUserTenant) {
// return Result.error("该用户不存在该组织中,无权修改");
// }
// String content = "";
// SysUser user = new SysUser();
// user.setUsername(sysUserTenant.getCreateBy());
// String realname = oConvertUtils.getString(sysUser.getRealname(),sysUser.getUsername());
// //成功加入
// if(CommonConstant.USER_TENANT_NORMAL.equals(status)){
// //修改租户状态
// relationService.agreeJoinTenant(userId,tenantId);
// content = content + realname + "已同意您发送的加入 " + tenant.getName() + " 的邀请";
// sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content);
// return Result.OK("您已同意该组织的邀请");
// }else if(CommonConstant.USER_TENANT_REFUSE.equals(status)){
// //直接删除关系表即可
// relationService.refuseJoinTenant(userId,tenantId);
// content = content + realname + "拒绝了您发送的加入 " + tenant.getName() + " 的邀请";
// sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content);
// return Result.OK("您已成功拒绝该组织的邀请");
// }
// return Result.error("类型不匹配,禁止修改数据");
// }
/**
* 目前只给敲敲云租户下删除用户使用

View File

@ -1,9 +1,11 @@
package org.jeecg.modules.system.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
@ -20,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -48,6 +49,7 @@ public class SysUserOnlineController {
@Resource
private BaseCommonService baseCommonService;
@RequiresPermissions("system:online:list")
@RequestMapping(value = "/list", method = RequestMethod.GET)
public Result<Page<SysUserOnlineVO>> list(@RequestParam(name="username", required=false) String username,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,@RequestParam(name="pageSize", defaultValue="10") Integer pageSize) {
@ -100,6 +102,7 @@ public class SysUserOnlineController {
/**
* 强退用户
*/
@RequiresPermissions("system:online:forceLogout")
@RequestMapping(value = "/forceLogout",method = RequestMethod.POST)
public Result<Object> forceLogout(@RequestBody SysUserOnlineVO online) {
//用户退出逻辑

View File

@ -371,11 +371,19 @@ public class SysDictServiceImpl extends ServiceImpl<SysDictMapper, SysDict> impl
if (isCustomDataSource) {
DynamicDataSourceContextHolder.push(dataSource);
}
List<DictModel> restData = sysDictMapper.queryTableDictByKeysAndFilterSql(table, text, code, filterSql, codeValues);
// 清理自定义的数据源
if (isCustomDataSource) {
DynamicDataSourceContextHolder.clear();
//update-begin---author:jarysun ---date:20251020 for[issues/#9002]解决表字典查询出现异常之后,数据源不能恢复问题------------
List<DictModel> restData = null;
try {
restData = sysDictMapper.queryTableDictByKeysAndFilterSql(table, text, code, filterSql, codeValues);
} finally {
// 清理自定义的数据源
if (isCustomDataSource) {
DynamicDataSourceContextHolder.clear();
}
}
//update-end---author:jarysun ---date:20251020 for[issues/#9002]解决表字典查询出现异常之后,数据源不能恢复问题------------
return restData;
}

View File

@ -229,7 +229,7 @@ public class ThirdAppDingtalkServiceImpl implements IThirdAppService {
LambdaQueryWrapper<SysDepart> queryWrapper = new LambdaQueryWrapper<>();
// 根据 source_identifier 字段查询
// 代码逻辑说明: 【issues/6017】钉钉同步部门时没有最顶层的部门名同步用户时用户没有部门信息---
queryWrapper.and(item -> item.eq(SysDepart::getId, departmentTree.getSource_identifier()).or().eq(SysDepart::getDingIdentifier,departmentTree.getDept_id()));
queryWrapper.and(item -> item.eq(SysDepart::getId, departmentTree.getSource_identifier()).or().eq(SysDepart::getDingIdentifier,oConvertUtils.getString(departmentTree.getDept_id())));
SysDepart sysDepart = sysDepartService.getOne(queryWrapper);
if (sysDepart != null) {
// 执行更新操作

View File

@ -1,4 +1,4 @@
<template>
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
@ -33,7 +33,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -303,7 +303,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -339,7 +339,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -378,7 +378,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -355,13 +355,19 @@ export const formSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>

View File

@ -108,7 +108,7 @@
};
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<template>
<div class="p-2">
<#assign query_field_no=0>
@ -110,7 +110,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -368,7 +368,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -404,7 +404,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -484,7 +484,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -78,7 +78,7 @@
visible.value = false;
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<#assign pidFieldName = "">
<#assign hasChildrenField = "">
<#assign bpm_flag=false>
@ -38,7 +38,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -447,7 +447,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -483,7 +483,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -544,7 +544,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -381,13 +381,19 @@ export const formSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<#assign pidFieldName = "">
<#assign hasChildrenField = "">
<#list originalColumns as po>
@ -170,7 +170,7 @@
};
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<template>
<div class="p-2">
<#assign pidFieldName = "">
@ -120,7 +120,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -518,7 +518,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -559,7 +559,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -597,7 +597,7 @@
}
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<template>
<template>
<div class="p-2 cgformErpList">
<#assign list_need_category=false>
<#assign list_need_pca=false>
@ -33,7 +33,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -350,7 +350,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -385,7 +385,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -444,7 +444,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -350,13 +350,19 @@ export const formSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
@ -686,7 +692,10 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#elseif po.classType=='list' || po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>

View File

@ -107,7 +107,7 @@
}
};
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -114,7 +114,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -390,7 +390,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
},
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -427,7 +427,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -465,7 +465,7 @@
}
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -80,7 +80,7 @@
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<#-- ** 引入全局工具方法 ** -->
<#-- ** 引入全局工具方法 ** -->
<#include "/common/utils.ftl">
<template>
<div>
@ -45,7 +45,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -355,7 +355,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -391,7 +391,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -450,7 +450,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -351,13 +351,19 @@ export const formSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
@ -672,13 +678,19 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<template>
<#assign buttonList=[]>
<#if tableVo.extendParams?? && tableVo.extendParams.cgButtonList??>
@ -253,7 +253,7 @@
</#list>
};
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<template>
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
@ -32,7 +32,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -330,7 +330,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -366,7 +366,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -425,7 +425,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -351,13 +351,19 @@ export const formSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
@ -599,13 +605,19 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<template>
<#assign buttonList=[]>
<#if tableVo.extendParams?? && tableVo.extendParams.cgButtonList??>
@ -253,7 +253,7 @@
</#list>
};
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<template>
<div class="p-2">
<#assign query_field_no=0>
@ -113,7 +113,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -364,7 +364,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -401,7 +401,7 @@
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -441,7 +441,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<template>
<#assign buttonList=[]>
<#if tableVo.extendParams?? && tableVo.extendParams.cgButtonList??>
@ -59,7 +59,7 @@
closeModal();
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');
@ -75,7 +75,7 @@
handleSubmit,
submitSuccess,
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
handle${btn.buttonCode?cap_first},
</#if>

View File

@ -1,4 +1,4 @@
<template>
<template>
<div>
<#assign list_need_category=false>
<#assign list_need_pca=false>
@ -32,7 +32,7 @@
<a-button type="primary" v-auth="'${entityPackage}:${tableName}:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'${entityPackage}:${tableName}:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'button'>
<a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if>
@ -329,7 +329,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -365,7 +365,7 @@
auth: '${entityPackage}:${tableName}:delete'
}
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle == 'link'>
,{
label: '${btn.buttonName}',
@ -424,7 +424,7 @@
</#if>
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -351,13 +351,19 @@ export const formSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>
@ -612,13 +618,19 @@ export const ${sub.entityName?uncap_first}FormSchema: FormSchema[] = [
<#elseif po.classType=='list'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}"
dictCode:"${form_field_dictCode}",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#elseif po.classType=='radio'>
component: 'JDictSelectTag',
componentProps:{
dictCode:"${form_field_dictCode}",
type: "radio"
type: "radio",
<#if po.fieldDbType=='int'>
stringToNumber: true
</#if>
},
<#-- update-begin---author:chenrui ---date:20231228 for[QQYUN-7583] Vue3风格表单页面多选控件渲染成了下拉多选---------- -->
<#elseif po.classType=='list_multi'>

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl">
<#include "/common/utils.ftl">
<template>
<#assign buttonList=[]>
<#if tableVo.extendParams?? && tableVo.extendParams.cgButtonList??>
@ -286,7 +286,7 @@
</#list>
};
<#if buttonList?size gt 0>
<#list buttonList?filter(it -> it.orderNum?? && it.orderNum != null)?sort_by("orderNum") as btn>
<#list buttonList as btn>
<#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -0,0 +1,3 @@
-- author:scott---date:20251212--for:在线用户接口权限配置
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1999406402585542657', '1594930803956920321', '在线用户列表接口', NULL, NULL, 0, NULL, NULL, 2, 'system:online:list', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-12-12 17:10:08', NULL, NULL, 0, 0, '1', 0);
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1999406500300242946', '1594930803956920321', '强制用户退出接口', NULL, NULL, 0, NULL, NULL, 2, 'system:online:forceLogout', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-12-12 17:10:32', NULL, NULL, 0, 0, '1', 0);

View File

@ -57,7 +57,7 @@
<!-- 积木报表-->
<jimureport-spring-boot-starter.version>2.2.0</jimureport-spring-boot-starter.version>
<jimubi-spring-boot-starter.version>2.2.0</jimubi-spring-boot-starter.version>
<minidao.version>1.10.16</minidao.version>
<minidao.version>1.10.18</minidao.version>
<autopoi-web.version>2.0.2</autopoi-web.version>
<!-- 持久层 -->
@ -132,6 +132,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
@ -475,17 +480,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.6.7.Final</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- AutoPoi Excel工具类-->
<dependency>
<groupId>org.jeecgframework</groupId>

View File

@ -60,7 +60,7 @@
import { useSelectBiz } from '/@/components/Form/src/jeecg/hooks/useSelectBiz';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { selectProps } from '/@/components/Form/src/jeecg/props/props';
import { isArray, cloneDeep } from 'lodash-es';
export default defineComponent({
name: 'UserSelectModal',
components: {
@ -115,8 +115,14 @@
}
// 代码逻辑说明: VUEN-1112 一对多 用户选择 未显示选择条数,及清空
setTimeout(() => {
// update-begin--author:liaozhiyang---date:20260120--for【issues/9275】用户组件第二次点击取消时勾选值还是回显了
let selectedData = selectValues['value'];
if (isArray(selectedData)) {
selectedData = cloneDeep(selectedData);
}
// update-end--author:liaozhiyang---date:20260120--for【issues/9275】用户组件第二次点击取消时勾选值还是回显了
if (tableRef.value) {
tableRef.value.setSelectedRowKeys(selectValues['value'] || []);
tableRef.value.setSelectedRowKeys(selectedData || []);
}
}, 800);
});

View File

@ -106,7 +106,8 @@ export function useTableScroll(
const paddingHeight = 32;
// Pager height
let paginationHeight = 2;
if (!isBoolean(pagination)) {
// 【issues/9217】当配置了pagination: true时,BasicTable组件自适应高度异常
if (pagination !== false) {
paginationEl = tableEl.querySelector('.ant-pagination') as HTMLElement;
if (paginationEl) {
const offsetHeight = paginationEl.offsetHeight;

View File

@ -38,7 +38,10 @@ export function usePagination(props: JVxeTableProps, methods: JVxeTableMethods)
function handleShowSizeChange(current, pageSize) {
innerPagination.pageSize = pageSize;
methods.trigger('pageChange', { current, pageSize });
// -update-begin--author:liaozhiyang---date:20251209---for:【issues/9169】切换页码时pageChange事件加载了两次
// 因为 handleShowSizeChange先触发紧接着会触发 handleChange所以可以注释掉。
// methods.trigger('pageChange', { current, pageSize });
// -update-end--author:liaozhiyang---date:20251209---for:【issues/9169】切换页码时pageChange事件加载了两次
}
/** 渲染分页器 */

View File

@ -75,6 +75,10 @@ export function useListPage(options: ListPageOptions) {
if (options?.tableProps?.useSearchForm !== false) {
paramsForm = await getForm().validate();
console.log('paramsForm', paramsForm);
// 在这里把执行beforeFetch
if (options?.tableProps?.beforeFetch) {
paramsForm = await options?.tableProps?.beforeFetch(paramsForm);
}
}
} catch (e) {
console.warn(e);
@ -132,8 +136,8 @@ export function useListPage(options: ListPageOptions) {
for (const column of columns) {
if(!column.defaultHidden){
let dataIndex = column?.dataIndex;
if(column?.dataIndex!.toString().indexOf('_dictText')){
dataIndex = column?.dataIndex!.toString().replace('_dictText','')
if(column?.dataIndex?.toString()?.indexOf('_dictText') !== -1){
dataIndex = column?.dataIndex?.toString().replace('_dictText','')
}
exportFields.push(dataIndex);
} else {

View File

@ -45,15 +45,17 @@ const render = {
*/
renderDict: (v, code, renderTag = false) => {
let text = '';
let color = '';
let array = getDictItemsByCode(code) || [];
let obj = array.filter((item) => {
return item.value == v;
});
if (obj.length > 0) {
text = obj[0].text;
color = obj[0].color;
}
//【jeecgboot-vue3/issues/903】render.renderDict使用tag渲染报警告问题 #903
return isEmpty(text) || !renderTag ? h('span', text) : h(Tag, () => text);
return isEmpty(text) || !renderTag ? h('span', text) : h(Tag,{ color }, () => text);
},
/**
* 渲染图片