Compare commits

...

21 Commits

Author SHA1 Message Date
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
26087172df 更新文档说明 2025-12-04 14:57:41 +08:00
281c3ff3c8 措辞优化 2025-12-04 13:55:53 +08:00
38d44c2487 修改一个参数,就实现默认的四个系统主题快速切换 2025-12-03 19:55:13 +08:00
8c88f8adf5 【issues/9098】tabs标签页关闭异常 2025-12-03 19:06:21 +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
42 changed files with 456 additions and 386 deletions

View File

@ -3,9 +3,6 @@ AIGC应用平台介绍
一个全栈式 AI 开发平台,旨在帮助开发者快速构建和部署个性化的 AI 应用。 一个全栈式 AI 开发平台,旨在帮助开发者快速构建和部署个性化的 AI 应用。
> JDK说明AI流程编排引擎暂时不支持jdk21所以目前只能使用jdk8或者jdk17启动项目。
JeecgBoot平台的AIGC功能模块是一套类似`Dify``AIGC应用开发平台`+`知识库问答`是一款基于LLM大语言模型AI应用平台和 RAG 的知识库问答系统。 JeecgBoot平台的AIGC功能模块是一套类似`Dify``AIGC应用开发平台`+`知识库问答`是一款基于LLM大语言模型AI应用平台和 RAG 的知识库问答系统。
其直观的界面结合了 AI 流程编排、RAG 管道、知识库管理、模型管理、对接向量库、实时运行可观察等让您可以快速从原型到生产拥有AI服务能力。 其直观的界面结合了 AI 流程编排、RAG 管道、知识库管理、模型管理、对接向量库、实时运行可观察等让您可以快速从原型到生产拥有AI服务能力。
@ -109,6 +106,10 @@ JeecgBoot平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发
| ChatGTP | √ | | ChatGTP | √ |
| Qwq | √ | | Qwq | √ |
| 智库 | √ | | 智库 | √ |
| claude | √ |
| vl模型 | √ |
| 千帆大模型 | √ |
| 通义千问 | √ |
| Ollama本地搭建大模型 | √ | | Ollama本地搭建大模型 | √ |
| 等等。。 | √ | | 等等。。 | √ |

View File

@ -1,126 +0,0 @@
JeecgBoot低代码平台(商业版介绍)
===============
项目介绍
-----------------------------------
<h3 align="center">企业级AI低代码平台</h3>
JeecgBoot是一款集成AI应用的基于BPM流程的低代码平台旨在帮助企业快速实现低代码开发和构建个性化AI应用支持MCP和插件实现聊天式业务操作如 “一句话创建用户”)!
前后端分离架构Ant Design&Vue3SpringBootSpringCloud AlibabaMybatis-plusShiro。强大的代码生成器让前后端代码一键生成无需写任何代码 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE 帮助Java项目解决80%的重复工作让开发更多关注业务提高效率、节省成本同时又不失灵活性低代码能力Online表单、表单设计、流程设计、Online报表、大屏/仪表盘设计、报表设计; AI应用平台功能AI知识库问答、AI模型管理、AI流程编排、AI聊天等支持含ChatGPT、DeepSeek、Ollama等多种AI大模型
JeecgBoot 提供了一系列 `低代码能力`,实现`真正的零代码`在线开发Online表单开发、Online报表、复杂报表设计、打印设计、在线图表设计、仪表盘设计、大屏设计、移动图表能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔
`AI赋能低代码:` 目前提供了AI应用、AI模型管理、AI流程编排、AI对话助手AI建表、AI写文章、AI知识库问答、AI字段建议等功能;支持各种AI大模型ChatGPT、DeepSeek、Ollama、智普、千问等.
`JEECG宗旨是:` 简单功能由OnlineCoding配置实现做到`零代码开发`复杂功能由代码生成器生成进行手工Merge 实现`低代码开发`,既保证了`智能`又兼顾`灵活`;实现了低代码开发的同时又支持灵活编码,解决了当前低代码产品普遍不灵活的弊端!
`JEECG业务流程:` 采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计松耦合、并支持任务节点灵活配置既保证了公司流程的保密性又减少了开发人员的工作量。
#### JeecgBoot商业版与同类产品区别
-----------------------------------
- 灵活性jeecgboot基于开源技术栈设计初考虑到可插拔性和集成灵活性确保平台的智能性与灵活性避免因平台过于庞大而导致的扩展困难。
- 流程管理:支持一个表单挂接多个流程,同时一个流程可以连接多个表单,增强了流程的灵活性和复杂性管理。
- 符合中国国情的流程针对中国市场的特定需求jeecgboot能够实现各种符合中国国情的业务流程。
- 强大的表单设计器jeecgboot的表单设计器与敲敲云共享具备高质量和智能化的特点能够满足零代码应用的需求业内同类产品中不多见。
- 报表功能:自主研发的报表工具,拥有独立知识产权,功能上比业内老牌产品如帆软更智能,操作简便。
- BI产品整合提供大屏、仪表盘、门户等功能完美解决这些需求并支持移动面板的设计与渲染。
- 自主研发的模块jeecgboot的所有模块均为自主研发具有独立的知识产权。
- 颗粒度和功能细致在功能细致度和颗粒度上jeecgboot远超同类产品尤其在零代码能力方面表现突出。
- 零代码应用管理最新版支持与敲敲云的零代码应用管理能力的集成使得jeecgboot既具备低代码又具备零代码的应用能力业内独一无二。
- 强大的代码生成器作为开源代码生成器的先锋jeecgboot在代码生成的智能化和在线低代码与代码生成的结合方面优势明显。
- 精细化权限管理提供行级和列级的数据权限控制满足企业在ERP和OA领域对权限管理的严格需求。
- 多平台支持的APP目前采用uniapp3实现支持小程序、H5、App及鸿蒙、鸿蒙Next、Electron桌面应用等多种终端。
> 综上所述jeecgboot不仅在功能上具备丰富性和灵活性还在技术架构、权限管理和用户体验等方面展现出明显的优势是一个综合性能强大的低代码平台。
商业版演示
-----------------------------------
JeecgBoot vs 敲敲云
> - JeecgBoot是低代码产品拥有系列低代码能力比如流程设计、表单设计、大屏设计代码生成器适合半开发模式开发+低代码结合),也可以集成零代码应用管理模块.
> - 敲敲云是零代码产品完全不写代码通过配置搭建业务系统其在jeecgboot基础上研发而成删除了online、代码生成、OA等需要编码功能只保留应用管理功能和聊天、日程、文件三个OA组件.
- JeecgBoot低代码 https://boot3.jeecg.com
- 敲敲云零代码https://app.qiaoqiaoyun.com
- APP演示(多端): http://jeecg.com/appIndex
### 流程视频介绍
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/flow_video.png)](https://www.bilibili.com/video/BV1Nk4y1o7Qc)
### 商业版功能简述
> 详细的功能介绍,[请联系官方](https://jeecg.com/vip)
```
│─更多商业功能
│ ├─流程设计器
│ ├─简流设计器(类钉钉版)
│ ├─门户设计NEW
│ ├─表单设计器
│ ├─大屏设计器
│ └─我的任务
│ └─历史流程
│ └─历史流程
│ └─流程实例管理
│ └─流程监听管理
│ └─流程表达式
│ └─我发起的流程
│ └─我的抄送
│ └─流程委派、抄送、跳转
│ └─OA办公组件
│ └─零代码应用管理(无需编码,在线搭建应用系统)
│ ├─积木报表企业版含jimureport、jimubi
│ ├─AI流程设计器源码
│ ├─Online全模块功能和源码
│ ├─AI写文章CMS
│ ├─AI表单字段建议表单设计器
│ ├─OA办公协同组件
│ ├─在线聊天功能
│ ├─设计表单移动适配
│ ├─设计表单支持外部填报
│ ├─设计表单AI字段建议
│ ├─设计表单视图功能(支持多种类型含日历、表格、看板、甘特图)
│ └─。。。
```
##### 流程设计
![](https://oscimg.oschina.net/oscnet/up-981ce174e4fbb48c8a2ce4ccfd7372e2994.png)
![](https://oscimg.oschina.net/oscnet/up-1dc0d052149ec675f3e4fad632b82b48add.png)
![](https://oscimg.oschina.net/oscnet/up-de31bc2f9d9b8332c554b0954cc73d79593.png)
![输入图片说明](https://static.oschina.net/uploads/img/201907/05165142_yyQ7.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题")
##### 表单设计器
![](https://oscimg.oschina.net/oscnet/up-5f8cb657615714b02190b355e59f60c5937.png)
![](https://oscimg.oschina.net/oscnet/up-d9659b2f324e33218476ec98c9b400e6508.png)
![](https://oscimg.oschina.net/oscnet/up-4868615395272d3206dbb960ade02dbc291.png)

View File

@ -1,4 +1,4 @@
[中文](./README.md) | English
![JEECG](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/logov3.png "JeecgBoot低代码开发平台") ![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低代码平台 JeecgBoot AI低代码平台
=============== ===============
@ -232,20 +233,6 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
开源版与企业版区别?
-----------------------------------
- JeecgBoot开源版采用 [Apache-2.0 license](LICENSE) 协议附加补充条款:允许商用使用,不会造成侵权行为,允许基于本平台软件开展业务系统开发(但在任何情况下,您不得使用本软件开发可能被认为与本软件竞争的软件).
- 商业版与开源版主要区别在于商业版提供了技术支持 和 更多的企业级功能(例如Online图表、流程监控、流程设计、流程审批、表单设计器、表单视图、积木报表企业版、OA办公、商业APP、零代码应用、Online模块源码等功能). [更多商业功能介绍,点击查看](README-Enterprise.md)
- JeecgBoot未来发展方向是零代码平台的建设也就是团队的另外一款产品 [敲敲云零代码](https://www.qiaoqiaoyun.com) 无需编码即可通过拖拽快速搭建企业级应用与JeecgBoot低代码平台形成互补满足从简单业务到复杂系统的全场景开发需求目前已经开源[欢迎下载](https://qiaoqiaoyun.com/downloadCode)
### Jeecg Boot 产品功能蓝图 ### Jeecg Boot 产品功能蓝图
![功能蓝图](https://jeecgos.oss-cn-beijing.aliyuncs.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题") ![功能蓝图](https://jeecgos.oss-cn-beijing.aliyuncs.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题")

View File

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

View File

@ -3,9 +3,7 @@ package org.jeecg.common.system.util;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.annotation.EnumDict; import org.jeecg.common.system.annotation.EnumDict;
import org.jeecg.common.system.vo.DictModel; import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils; import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver; 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.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@ -183,10 +182,10 @@ public class ResourceUtil {
for (DictModel dm : dictItemList) { for (DictModel dm : dictItemList) {
String value = dm.getValue(); String value = dm.getValue();
if (keySet.contains(value)) { 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())); 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; package org.jeecg.config.sign.interceptor;
import java.io.PrintWriter; import com.alibaba.fastjson.JSON;
import java.util.SortedMap;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result; import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils; 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.jeecg.config.sign.util.SignUtil;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
import com.alibaba.fastjson.JSON; import java.io.PrintWriter;
import java.util.SortedMap;
import lombok.extern.slf4j.Slf4j;
/** /**
* 签名拦截器 * 签名拦截器
@ -33,23 +30,46 @@ public class SignAuthInterceptor implements HandlerInterceptor {
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("Sign Interceptor request URI = " + request.getRequestURI()); log.info("签名拦截器 Interceptor request URI = " + request.getRequestURI());
try {
// 调用验证逻辑
validateSignature(request);
return true;
} 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(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); HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
//获取全部参数(包括URL和body上的) //获取全部参数(包括URL和body上的)
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper); SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
log.debug("提取参数: {}", allParams);
//对参数进行签名验证 //对参数进行签名验证
String headerSign = request.getHeader(CommonConstant.X_SIGN); String headerSign = request.getHeader(CommonConstant.X_SIGN);
String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP); String xTimestamp = request.getHeader(CommonConstant.X_TIMESTAMP);
if(oConvertUtils.isEmpty(xTimestamp)){ if(oConvertUtils.isEmpty(xTimestamp)){
Result<?> result = Result.error("Sign签名校验失败时间戳为空"); log.error("Sign签名校验失败时间戳为空");
log.error("Sign 签名校验失败Header xTimestamp 为空"); throw new IllegalArgumentException("Sign签名校验失败,请求参数不完整!");
//校验失败返回前端
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
out.print(JSON.toJSON(result));
return false;
} }
//客户端时间 //客户端时间
@ -60,15 +80,24 @@ public class SignAuthInterceptor implements HandlerInterceptor {
//1.校验签名时间兼容X_TIMESTAMP的新老格式 //1.校验签名时间兼容X_TIMESTAMP的新老格式
if (xTimestamp.length() == length) { if (xTimestamp.length() == length) {
//a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子20220308152143) //a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子20220308152143)
if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) { long currentTimestamp = DateUtils.getCurrentTimestamp();
log.error("签名验证失败:X-TIMESTAMP已过期注意系统时间和服务器时间是否有误差"); long timeDiff = currentTimestamp - clientTimestamp;
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期"); log.debug("时间戳验证(yyyyMMddHHmmss): 时间差{}秒", timeDiff);
if (timeDiff > MAX_EXPIRE) {
log.error("时间戳已过期: {}秒 > {}秒", timeDiff, MAX_EXPIRE);
throw new IllegalArgumentException("签名验证失败,请求时效性验证失败!");
} }
} else { } else {
//b. X_TIMESTAMP格式是 时间戳 (例子1646552406000) //b. X_TIMESTAMP格式是 时间戳 (例子1646552406000)
if ((System.currentTimeMillis() - clientTimestamp) > (MAX_EXPIRE * length1000)) { long currentTime = System.currentTimeMillis();
log.error("签名验证失败:X-TIMESTAMP已过期注意系统时间和服务器时间是否有误差"); long timeDiff = currentTime - clientTimestamp;
throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期"); long maxExpireMs = MAX_EXPIRE * length1000;
log.debug("时间戳验证(Unix): 时间差{}ms", timeDiff);
if (timeDiff > maxExpireMs) {
log.error("时间戳已过期: {}ms > {}ms", timeDiff, maxExpireMs);
throw new IllegalArgumentException("签名验证失败,请求时效性验证失败!");
} }
} }
@ -76,19 +105,18 @@ public class SignAuthInterceptor implements HandlerInterceptor {
boolean isSigned = SignUtil.verifySign(allParams,headerSign); boolean isSigned = SignUtil.verifySign(allParams,headerSign);
if (isSigned) { if (isSigned) {
log.debug("Sign 签名通过Header Sign : {}",headerSign); log.debug("签名验证通过");
return true;
} else { } else {
log.debug("sign allParams: {}", allParams); log.error("签名验证失败, 参数: {}", allParams);
log.error("request URI = " + request.getRequestURI()); throw new IllegalArgumentException("Sign签名校验失败");
log.error("Sign 签名校验失败Header Sign : {}",headerSign); }
//校验失败返回前端 } catch (IllegalArgumentException e) {
response.setCharacterEncoding("UTF-8"); // 重新抛出签名验证异常
response.setContentType("application/json; charset=utf-8"); throw e;
PrintWriter out = response.getWriter(); } catch (Exception e) {
Result<?> result = Result.error("Sign签名校验失败"); // 包装其他异常如IOException
out.print(JSON.toJSON(result)); log.error("签名验证异常: {}", e.getMessage());
return false; 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.TokenUtils;
import org.jeecg.common.util.oConvertUtils; import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig; import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.config.sign.annotation.SignatureCheck;
import org.jeecg.modules.base.service.BaseCommonService; import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.system.entity.*; import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.service.ISysTenantPackService; import org.jeecg.modules.system.service.ISysTenantPackService;
@ -260,6 +261,7 @@ public class SysTenantController {
* @param id * @param id
* @return * @return
*/ */
@SignatureCheck
@RequestMapping(value = "/queryById", method = RequestMethod.GET) @RequestMapping(value = "/queryById", method = RequestMethod.GET)
public Result<SysTenant> queryById(@RequestParam(name="id",required=true) String id) { public Result<SysTenant> queryById(@RequestParam(name="id",required=true) String id) {
Result<SysTenant> result = new Result<SysTenant>(); Result<SysTenant> result = new Result<SysTenant>();
@ -507,26 +509,26 @@ public class SysTenantController {
return result; return result;
} }
/** // /**
* 加入租户通过门牌号【低代码应用专用接口】 // * 加入租户通过门牌号【低代码应用专用接口】
* @param sysTenant // * @param sysTenant
*/ // */
@PostMapping("/joinTenantByHouseNumber") // @PostMapping("/joinTenantByHouseNumber")
public Result<Integer> joinTenantByHouseNumber(@RequestBody SysTenant sysTenant){ // public Result<Integer> joinTenantByHouseNumber(@RequestBody SysTenant sysTenant){
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); // LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
Integer tenantId = sysTenantService.joinTenantByHouseNumber(sysTenant, sysUser.getId()); // Integer tenantId = sysTenantService.joinTenantByHouseNumber(sysTenant, sysUser.getId());
Result<Integer> result = new Result<>(); // Result<Integer> result = new Result<>();
if(tenantId != 0){ // if(tenantId != 0){
result.setMessage("申请加入组织成功"); // result.setMessage("申请加入组织成功");
result.setSuccess(true); // result.setSuccess(true);
result.setResult(tenantId); // result.setResult(tenantId);
return result; // return result;
}else{ // }else{
result.setMessage("该门牌号不存在"); // result.setMessage("该门牌号不存在");
result.setSuccess(false); // result.setSuccess(false);
return result; // return result;
} // }
} // }
/** /**
* 分页获取租户用户数据(vue3用户租户页面)【低代码应用专用接口】 * 分页获取租户用户数据(vue3用户租户页面)【低代码应用专用接口】
@ -713,6 +715,7 @@ public class SysTenantController {
* @return * @return
*/ */
@PostMapping("/invitationUser") @PostMapping("/invitationUser")
@RequiresPermissions("system:tenant:invitation:user")
public Result<String> invitationUser(@RequestParam(name="phone") String phone, public Result<String> invitationUser(@RequestParam(name="phone") String phone,
@RequestParam(name="departId",defaultValue = "") String departId){ @RequestParam(name="departId",defaultValue = "") String departId){
return sysTenantService.invitationUser(phone,departId); return sysTenantService.invitationUser(phone,departId);
@ -911,43 +914,43 @@ public class SysTenantController {
return Result.ok(pageList); return Result.ok(pageList);
} }
/** // /**
* 同意或拒绝加入租户 // * 同意或拒绝加入租户
*/ // */
@PutMapping("/agreeOrRefuseJoinTenant") // @PutMapping("/agreeOrRefuseJoinTenant")
public Result<String> agreeOrRefuseJoinTenant(@RequestParam("tenantId") Integer tenantId, // public Result<String> agreeOrRefuseJoinTenant(@RequestParam("tenantId") Integer tenantId,
@RequestParam("status") String status){ // @RequestParam("status") String status){
//是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】 // //是否开启系统管理模块的多租户数据隔离【SAAS多租户模式】
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); // LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String userId = sysUser.getId(); // String userId = sysUser.getId();
SysTenant tenant = sysTenantService.getById(tenantId); // SysTenant tenant = sysTenantService.getById(tenantId);
if(null == tenant){ // if(null == tenant){
return Result.error("不存在该组织"); // return Result.error("不存在该组织");
} // }
SysUserTenant sysUserTenant = relationService.getUserTenantByTenantId(userId, tenantId); // SysUserTenant sysUserTenant = relationService.getUserTenantByTenantId(userId, tenantId);
if (null == sysUserTenant) { // if (null == sysUserTenant) {
return Result.error("该用户不存在该组织中,无权修改"); // return Result.error("该用户不存在该组织中,无权修改");
} // }
String content = ""; // String content = "";
SysUser user = new SysUser(); // SysUser user = new SysUser();
user.setUsername(sysUserTenant.getCreateBy()); // user.setUsername(sysUserTenant.getCreateBy());
String realname = oConvertUtils.getString(sysUser.getRealname(),sysUser.getUsername()); // String realname = oConvertUtils.getString(sysUser.getRealname(),sysUser.getUsername());
//成功加入 // //成功加入
if(CommonConstant.USER_TENANT_NORMAL.equals(status)){ // if(CommonConstant.USER_TENANT_NORMAL.equals(status)){
//修改租户状态 // //修改租户状态
relationService.agreeJoinTenant(userId,tenantId); // relationService.agreeJoinTenant(userId,tenantId);
content = content + realname + "已同意您发送的加入 " + tenant.getName() + " 的邀请"; // content = content + realname + "已同意您发送的加入 " + tenant.getName() + " 的邀请";
sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content); // sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content);
return Result.OK("您已同意该组织的邀请"); // return Result.OK("您已同意该组织的邀请");
}else if(CommonConstant.USER_TENANT_REFUSE.equals(status)){ // }else if(CommonConstant.USER_TENANT_REFUSE.equals(status)){
//直接删除关系表即可 // //直接删除关系表即可
relationService.refuseJoinTenant(userId,tenantId); // relationService.refuseJoinTenant(userId,tenantId);
content = content + realname + "拒绝了您发送的加入 " + tenant.getName() + " 的邀请"; // content = content + realname + "拒绝了您发送的加入 " + tenant.getName() + " 的邀请";
sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content); // sysTenantService.sendMsgForAgreeAndRefuseJoin(user, content);
return Result.OK("您已成功拒绝该组织的邀请"); // return Result.OK("您已成功拒绝该组织的邀请");
} // }
return Result.error("类型不匹配,禁止修改数据"); // return Result.error("类型不匹配,禁止修改数据");
} // }
/** /**
* 目前只给敲敲云租户下删除用户使用 * 目前只给敲敲云租户下删除用户使用

View File

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

View File

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

View File

@ -1,4 +1,4 @@
<template> <template>
<div> <div>
<#assign list_need_category=false> <#assign list_need_category=false>
<#assign list_need_pca=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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -303,7 +303,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -339,7 +339,7 @@
auth: '${entityPackage}:${tableName}:delete' auth: '${entityPackage}:${tableName}:delete'
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -378,7 +378,7 @@
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

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

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl"> <#include "/common/utils.ftl">
<template> <template>
<div class="p-2"> <div class="p-2">
<#assign query_field_no=0> <#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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -368,7 +368,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -404,7 +404,7 @@
auth: '${entityPackage}:${tableName}:delete' auth: '${entityPackage}:${tableName}:delete'
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -484,7 +484,7 @@
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

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

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl"> <#include "/common/utils.ftl">
<#assign pidFieldName = ""> <#assign pidFieldName = "">
<#assign hasChildrenField = ""> <#assign hasChildrenField = "">
<#assign bpm_flag=false> <#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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -447,7 +447,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -483,7 +483,7 @@
auth: '${entityPackage}:${tableName}:delete' auth: '${entityPackage}:${tableName}:delete'
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -544,7 +544,7 @@
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

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

View File

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

View File

@ -1,4 +1,4 @@
<template> <template>
<div class="p-2 cgformErpList"> <div class="p-2 cgformErpList">
<#assign list_need_category=false> <#assign list_need_category=false>
<#assign list_need_pca=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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -350,7 +350,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -385,7 +385,7 @@
auth: '${entityPackage}:${tableName}:delete' auth: '${entityPackage}:${tableName}:delete'
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -444,7 +444,7 @@
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

@ -107,7 +107,7 @@
} }
}; };
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='form'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); 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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="ant-design:${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -390,7 +390,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
}, },
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -427,7 +427,7 @@
auth: '${entityPackage}:${tableName}:delete' auth: '${entityPackage}:${tableName}:delete'
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -465,7 +465,7 @@
} }
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
<template> <template>
<div> <div>
<#assign list_need_category=false> <#assign list_need_category=false>
<#assign list_need_pca=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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -330,7 +330,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -366,7 +366,7 @@
auth: '${entityPackage}:${tableName}:delete' auth: '${entityPackage}:${tableName}:delete'
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -425,7 +425,7 @@
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

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

View File

@ -1,4 +1,4 @@
<#include "/common/utils.ftl"> <#include "/common/utils.ftl">
<template> <template>
<div class="p-2"> <div class="p-2">
<#assign query_field_no=0> <#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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -364,7 +364,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -401,7 +401,7 @@
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -441,7 +441,7 @@
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

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

View File

@ -1,4 +1,4 @@
<template> <template>
<div> <div>
<#assign list_need_category=false> <#assign list_need_category=false>
<#assign list_need_pca=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> <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> <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> <#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'> <#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> <a-button type="primary" @click="handle${btn.buttonCode?cap_first}" <#if btn.buttonIcon??> preIcon="${btn.buttonIcon}" </#if>>${btn.buttonName}</a-button>
</#if> </#if>
@ -329,7 +329,7 @@
ifShow: !!record.bpmStatus && record.bpmStatus !== '1', ifShow: !!record.bpmStatus && record.bpmStatus !== '1',
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -365,7 +365,7 @@
auth: '${entityPackage}:${tableName}:delete' auth: '${entityPackage}:${tableName}:delete'
} }
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle == 'link'>
,{ ,{
label: '${btn.buttonName}', label: '${btn.buttonName}',
@ -424,7 +424,7 @@
</#if> </#if>
<#if buttonList?size gt 0> <#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'> <#if btn.buttonStyle=='button'>
function handle${btn.buttonCode?cap_first}(){ function handle${btn.buttonCode?cap_first}(){
createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!'); createMessage.info('点击了${btn.buttonName}按钮,对应的业务逻辑需自行实现!');

View File

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

@ -132,6 +132,11 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<!-- Lombok --> <!-- Lombok -->
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>

View File

@ -38,7 +38,10 @@ export function usePagination(props: JVxeTableProps, methods: JVxeTableMethods)
function handleShowSizeChange(current, pageSize) { function handleShowSizeChange(current, pageSize) {
innerPagination.pageSize = 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

@ -1,14 +1,14 @@
/** /**
* @description: menu type * @description: 默认四种菜单主题
*/ */
export enum MenuTypeEnum { export enum MenuTypeEnum {
// left menu // 左侧边菜单导航风格
SIDEBAR = 'sidebar', SIDEBAR = 'sidebar',
// 顶部栏导航风格
MIX_SIDEBAR = 'mix-sidebar',
// mixin menu
MIX = 'mix', MIX = 'mix',
// top menu // 侧边折叠导航风格
MIX_SIDEBAR = 'mix-sidebar',
//顶部混合导航风格
TOP_MENU = 'top-menu', TOP_MENU = 'top-menu',
} }

View File

@ -132,8 +132,8 @@ export function useListPage(options: ListPageOptions) {
for (const column of columns) { for (const column of columns) {
if(!column.defaultHidden){ if(!column.defaultHidden){
let dataIndex = column?.dataIndex; let dataIndex = column?.dataIndex;
if(column?.dataIndex!.toString().indexOf('_dictText')){ if(column?.dataIndex?.toString()?.indexOf('_dictText') !== -1){
dataIndex = column?.dataIndex!.toString().replace('_dictText','') dataIndex = column?.dataIndex?.toString().replace('_dictText','')
} }
exportFields.push(dataIndex); exportFields.push(dataIndex);
} else { } else {

View File

@ -9,8 +9,7 @@ import { changeTheme } from '/@/logics/theme';
import { updateDarkTheme } from '/@/logics/theme/dark'; import { updateDarkTheme } from '/@/logics/theme/dark';
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/designSetting'; import { getConfigByMenuType } from '../../../utils/getConfigByMenuType';
import { getThemeColorByMenuType } from '/@/utils/getThemeColorByMenuType';
import { isObject } from '/@/utils/is'; import { isObject } from '/@/utils/is';
import { ThemeEnum } from '/@/enums/appEnum'; import { ThemeEnum } from '/@/enums/appEnum';
import { APP__THEME__COLOR } from '/@/enums/cacheEnum'; import { APP__THEME__COLOR } from '/@/enums/cacheEnum';
@ -27,10 +26,10 @@ export function layoutHandler(event: HandlerEnum, value: any) {
const appStore = useAppStore(); const appStore = useAppStore();
const darkMode = appStore.getDarkMode === ThemeEnum.DARK; const darkMode = appStore.getDarkMode === ThemeEnum.DARK;
// 根据菜单类型动态获取主题色 // 根据菜单类型动态获取主题色
const dynamicThemeColor = getThemeColorByMenuType(value.type); const {themeColor: dynamicThemeColor, headerBgColor, sideBgColor } = getConfigByMenuType(value.type);
if (isHTopMenu) { if (isHTopMenu) {
baseHandler(event, value); baseHandler(event, value);
baseHandler(HandlerEnum.HEADER_THEME, HEADER_PRESET_BG_COLOR_LIST[4]); baseHandler(HandlerEnum.HEADER_THEME, headerBgColor);
baseHandler(HandlerEnum.CHANGE_THEME_COLOR, dynamicThemeColor); baseHandler(HandlerEnum.CHANGE_THEME_COLOR, dynamicThemeColor);
if (darkMode) { if (darkMode) {
updateHeaderBgColor(); updateHeaderBgColor();
@ -39,8 +38,8 @@ export function layoutHandler(event: HandlerEnum, value: any) {
baseHandler(HandlerEnum.TABS_THEME, tabsThemeOptions[1].value); baseHandler(HandlerEnum.TABS_THEME, tabsThemeOptions[1].value);
} else if (isMixMenu) { } else if (isMixMenu) {
baseHandler(event, value); baseHandler(event, value);
baseHandler(HandlerEnum.HEADER_THEME, HEADER_PRESET_BG_COLOR_LIST[2]); baseHandler(HandlerEnum.HEADER_THEME, headerBgColor);
baseHandler(HandlerEnum.MENU_THEME, SIDE_BAR_BG_COLOR_LIST[3]); baseHandler(HandlerEnum.MENU_THEME, sideBgColor);
if (darkMode) { if (darkMode) {
updateHeaderBgColor(); updateHeaderBgColor();
updateSidebarBgColor(); updateSidebarBgColor();
@ -51,8 +50,8 @@ export function layoutHandler(event: HandlerEnum, value: any) {
} else if (isMixSidebarMenu) { } else if (isMixSidebarMenu) {
baseHandler(event, value); baseHandler(event, value);
baseHandler(HandlerEnum.CHANGE_THEME_COLOR, dynamicThemeColor); baseHandler(HandlerEnum.CHANGE_THEME_COLOR, dynamicThemeColor);
baseHandler(HandlerEnum.HEADER_THEME, HEADER_PRESET_BG_COLOR_LIST[0]); baseHandler(HandlerEnum.HEADER_THEME, headerBgColor);
baseHandler(HandlerEnum.MENU_THEME, SIDE_BAR_BG_COLOR_LIST[0]); baseHandler(HandlerEnum.MENU_THEME, sideBgColor);
if (darkMode) { if (darkMode) {
updateHeaderBgColor(); updateHeaderBgColor();
updateSidebarBgColor(); updateSidebarBgColor();
@ -60,8 +59,8 @@ export function layoutHandler(event: HandlerEnum, value: any) {
baseHandler(HandlerEnum.TABS_THEME, tabsThemeOptions[1].value); baseHandler(HandlerEnum.TABS_THEME, tabsThemeOptions[1].value);
} else { } else {
baseHandler(event, value); baseHandler(event, value);
baseHandler(HandlerEnum.HEADER_THEME, HEADER_PRESET_BG_COLOR_LIST[4]); baseHandler(HandlerEnum.HEADER_THEME, headerBgColor);
baseHandler(HandlerEnum.MENU_THEME, SIDE_BAR_BG_COLOR_LIST[7]); baseHandler(HandlerEnum.MENU_THEME, sideBgColor);
if (darkMode) { if (darkMode) {
updateHeaderBgColor(); updateHeaderBgColor();
updateSidebarBgColor(); updateSidebarBgColor();

View File

@ -50,7 +50,9 @@ export function useTabDropdown(tabContentProps: TabContentProps, getIsTabs: Comp
} else { } else {
// 【TV360X-1039】当只有首页和另一个tab页时关闭左侧禁用 // 【TV360X-1039】当只有首页和另一个tab页时关闭左侧禁用
const validTabList = tabStore.getTabList.filter((item) => !item?.meta?.affix); const validTabList = tabStore.getTabList.filter((item) => !item?.meta?.affix);
return validTabList[0].path === state.current?.path; // update-begin--author:liaozhiyang---date:20251128---for【issues/9098】tabs标签页关闭异常
return validTabList[0]?.path === state.current?.path;
// update-end--author:liaozhiyang---date:20251128---for【issues/9098】tabs标签页关闭异常
} }
}; };
// Close other // Close other

View File

@ -10,11 +10,14 @@ import {
SessionTimeoutProcessingEnum, SessionTimeoutProcessingEnum,
TabsThemeEnum, TabsThemeEnum,
} from '/@/enums/appEnum'; } from '/@/enums/appEnum';
import { SIDE_BAR_BG_COLOR_LIST, HEADER_PRESET_BG_COLOR_LIST } from './designSetting';
import { darkMode } from '/@/settings/designSetting'; import { darkMode } from '/@/settings/designSetting';
import { getThemeColorByMenuType } from '/@/utils/getThemeColorByMenuType'; import { getConfigByMenuType } from '../utils/getConfigByMenuType';
// 修改此属性,实现默认的四个系统主题快速切换
const menuType = MenuTypeEnum.SIDEBAR; const menuType = MenuTypeEnum.SIDEBAR;
// update-begin--author:liaozhiyang---date:20251201---for【QQYUN-14176】修改一个配置就能切换默认四个主题不需要额外修改颜色等
const { themeColor, headerBgColor, sideBgColor, split, mode } = getConfigByMenuType(menuType);
// update-end--author:liaozhiyang---date:20251201---for【QQYUN-14176】修改一个配置就能切换默认四个主题不需要额外修改颜色等
// ! 改动后需要清空浏览器缓存 // ! 改动后需要清空浏览器缓存
const setting: ProjectConfig = { const setting: ProjectConfig = {
// 是否显示SettingButton // 是否显示SettingButton
@ -43,7 +46,7 @@ const setting: ProjectConfig = {
// SessionTimeoutProcessingEnum.PAGE_COVERAGE: 生成登录弹窗,覆盖当前页面 // SessionTimeoutProcessingEnum.PAGE_COVERAGE: 生成登录弹窗,覆盖当前页面
sessionTimeoutProcessing: SessionTimeoutProcessingEnum.ROUTE_JUMP, sessionTimeoutProcessing: SessionTimeoutProcessingEnum.ROUTE_JUMP,
// 项目主题色 - 根据导航栏模式确定主题色动态设置 // 项目主题色 - 根据导航栏模式确定主题色动态设置
themeColor: getThemeColorByMenuType(menuType), themeColor: themeColor,
// 项目主题模式 // 项目主题模式
themeMode: darkMode, themeMode: darkMode,
@ -71,7 +74,7 @@ const setting: ProjectConfig = {
// 头部配置 // 头部配置
headerSetting: { headerSetting: {
// 背景色 // 背景色
bgColor: HEADER_PRESET_BG_COLOR_LIST[4], bgColor: headerBgColor,
// 固定头部 // 固定头部
fixed: true, fixed: true,
// 是否显示顶部 // 是否显示顶部
@ -93,7 +96,7 @@ const setting: ProjectConfig = {
// 菜单配置 // 菜单配置
menuSetting: { menuSetting: {
// 背景色 // 背景色
bgColor: SIDE_BAR_BG_COLOR_LIST[0], bgColor: sideBgColor,
// 是否固定住左侧菜单 // 是否固定住左侧菜单
fixed: true, fixed: true,
// 菜单折叠 // 菜单折叠
@ -110,15 +113,15 @@ const setting: ProjectConfig = {
// 菜单宽度 // 菜单宽度
menuWidth: 210, menuWidth: 210,
// 菜单模式 // 菜单模式
mode: MenuModeEnum.INLINE, mode,
// 菜单类型 // 菜单类型
type: MenuTypeEnum.SIDEBAR, type: menuType,
// 菜单主题 // 菜单主题
theme: ThemeEnum.DARK, theme: ThemeEnum.LIGHT,
// 左侧导航栏文字颜色调整区分彩色和暗黑 (不对应配置) // 左侧导航栏文字颜色调整区分彩色和暗黑 (不对应配置)
isThemeBright: false, isThemeBright: false,
// 分割菜单 // 分割菜单
split: false, split,
// 顶部菜单布局 // 顶部菜单布局
topMenuAlign: 'center', topMenuAlign: 'center',
// 折叠触发器的位置 // 折叠触发器的位置

View File

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

View File

@ -0,0 +1,49 @@
import { MenuTypeEnum, MenuModeEnum } from '/@/enums/menuEnum';
import { APP_PRESET_COLOR_LIST, HEADER_PRESET_BG_COLOR_LIST, SIDE_BAR_BG_COLOR_LIST } from '/@/settings/designSetting';
/**
* 根据菜单类型和模式获取对应的主题色
* @param menuType 菜单类型
*/
export function getConfigByMenuType(menuType: MenuTypeEnum): {
themeColor: string;
headerBgColor: string;
sideBgColor: string;
split: boolean;
mode: MenuModeEnum;
} {
let themeColor;
let headerBgColor;
let sideBgColor;
let split = false;
let mode: MenuModeEnum = MenuModeEnum.INLINE;
if (menuType === MenuTypeEnum.TOP_MENU) {
// 顶部栏导航
themeColor = APP_PRESET_COLOR_LIST[1];
headerBgColor = HEADER_PRESET_BG_COLOR_LIST[4];
mode = MenuModeEnum.HORIZONTAL;
} else if (menuType === MenuTypeEnum.MIX) {
// 顶部混合菜单模式
themeColor = APP_PRESET_COLOR_LIST[2];
headerBgColor = HEADER_PRESET_BG_COLOR_LIST[2];
sideBgColor = SIDE_BAR_BG_COLOR_LIST[3];
split = true;
} else if (menuType === MenuTypeEnum.MIX_SIDEBAR) {
// 侧边折叠导航模式
themeColor = APP_PRESET_COLOR_LIST[1];
headerBgColor = HEADER_PRESET_BG_COLOR_LIST[0];
sideBgColor = SIDE_BAR_BG_COLOR_LIST[0];
} else if (menuType === MenuTypeEnum.SIDEBAR) {
// 侧边栏导航
themeColor = APP_PRESET_COLOR_LIST[1];
headerBgColor = HEADER_PRESET_BG_COLOR_LIST[4];
sideBgColor = SIDE_BAR_BG_COLOR_LIST[7];
}
return {
themeColor,
headerBgColor,
sideBgColor,
split,
mode,
};
}

View File

@ -1,23 +0,0 @@
import { MenuTypeEnum } from '/@/enums/menuEnum';
import { APP_PRESET_COLOR_LIST } from '/@/settings/designSetting';
/**
* 根据菜单类型和模式获取对应的主题色
* @param menuType 菜单类型
*/
export function getThemeColorByMenuType(menuType: MenuTypeEnum): string {
if (menuType === MenuTypeEnum.TOP_MENU) {
// 顶部栏导航
return APP_PRESET_COLOR_LIST[1];
} else if (menuType === MenuTypeEnum.MIX) {
// 顶部混合菜单使用青绿色主题
return APP_PRESET_COLOR_LIST[2];
} else if (menuType === MenuTypeEnum.MIX_SIDEBAR) {
// 侧边折叠导航模式
return APP_PRESET_COLOR_LIST[1];
} else if (menuType === MenuTypeEnum.SIDEBAR) {
// 侧边栏导航
return APP_PRESET_COLOR_LIST[1];
}
return APP_PRESET_COLOR_LIST[1];
}