diff --git a/pom.xml b/pom.xml index 9082f0b0eb..9d772bd543 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ yudao-module-system yudao-module-infra - yudao-module-bpm + diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java index d236d1817f..47a53a5957 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.string; import cn.hutool.core.text.StrPool; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; +import org.aspectj.lang.JoinPoint; import java.util.Arrays; import java.util.Collection; @@ -77,4 +78,30 @@ public class StrUtils { .collect(Collectors.joining("\n")); } + /** + * 拼接方法的参数 + * + * 特殊:排除一些无法序列化的参数,如 ServletRequest、ServletResponse、MultipartFile + * + * @param joinPoint 连接点 + * @return 拼接后的参数 + */ + public static String joinMethodArgs(JoinPoint joinPoint) { + Object[] args = joinPoint.getArgs(); + if (ArrayUtil.isEmpty(args)) { + return ""; + } + return ArrayUtil.join(args, ",", item -> { + if (item == null) { + return ""; + } + // 讨论可见:https://t.zsxq.com/XUJVk、https://t.zsxq.com/MnKcL + String clazzName = item.getClass().getName(); + if (StrUtil.startWithAny(clazzName, "javax.servlet", "jakarta.servlet", "org.springframework.web")) { + return ""; + } + return item; + }); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index a804cce871..c142e3dbb4 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.tenant.config; import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect; import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor; import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect; @@ -19,11 +20,13 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import cn.iocoder.yudao.module.system.api.tenant.TenantApi; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import jakarta.annotation.Resource; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.cache.BatchStrategies; @@ -32,14 +35,24 @@ import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.util.pattern.PathPattern; +import java.util.Map; import java.util.Objects; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + @AutoConfiguration @ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户 @EnableConfigurationProperties(TenantProperties.class) public class YudaoTenantAutoConfiguration { + @Resource + private ApplicationContext applicationContext; + @Bean public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) { return new TenantFrameworkServiceImpl(tenantApi); @@ -67,13 +80,41 @@ public class YudaoTenantAutoConfiguration { // ========== WEB ========== @Bean - public FilterRegistrationBean tenantContextWebFilter() { + public FilterRegistrationBean tenantContextWebFilter(TenantProperties tenantProperties) { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new TenantContextWebFilter()); registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER); + addIgnoreUrls(tenantProperties); return registrationBean; } + /** + * 如果 Controller 接口上,有 {@link TenantIgnore} 注解,那么添加到忽略的 URL 中 + * + * @param tenantProperties 租户配置 + */ + private void addIgnoreUrls(TenantProperties tenantProperties) { + // 获得接口对应的 HandlerMethod 集合 + RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) + applicationContext.getBean("requestMappingHandlerMapping"); + Map handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods(); + // 获得有 @TenantIgnore 注解的接口 + for (Map.Entry entry : handlerMethodMap.entrySet()) { + HandlerMethod handlerMethod = entry.getValue(); + if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class)) { + continue; + } + // 添加到忽略的 URL 中 + if (entry.getKey().getPatternsCondition() != null) { + tenantProperties.getIgnoreUrls().addAll(entry.getKey().getPatternsCondition().getPatterns()); + } + if (entry.getKey().getPathPatternsCondition() != null) { + tenantProperties.getIgnoreUrls().addAll( + convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString)); + } + } + } + // ========== Security ========== @Bean diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java index 7b5e145e4c..402184895b 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/DefaultIdempotentKeyResolver.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl; -import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent; import cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver; import org.aspectj.lang.JoinPoint; @@ -18,7 +18,7 @@ public class DefaultIdempotentKeyResolver implements IdempotentKeyResolver { @Override public String resolver(JoinPoint joinPoint, Idempotent idempotent) { String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); + String argsStr = StrUtils.joinMethodArgs(joinPoint); return SecureUtil.md5(methodName + argsStr); } diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java index 2fa91ff976..dd89617058 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/keyresolver/impl/UserIdempotentKeyResolver.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl; -import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent; import cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; @@ -19,7 +19,7 @@ public class UserIdempotentKeyResolver implements IdempotentKeyResolver { @Override public String resolver(JoinPoint joinPoint, Idempotent idempotent) { String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); + String argsStr = StrUtils.joinMethodArgs(joinPoint); Long userId = WebFrameworkUtils.getLoginUserId(); Integer userType = WebFrameworkUtils.getLoginUserType(); return SecureUtil.md5(methodName + argsStr + userId + userType); diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java index 8d6253caa7..4ed68ddb36 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ClientIpRateLimiterKeyResolver.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl; -import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter; import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; import org.aspectj.lang.JoinPoint; @@ -19,7 +19,7 @@ public class ClientIpRateLimiterKeyResolver implements RateLimiterKeyResolver { @Override public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); + String argsStr = StrUtils.joinMethodArgs(joinPoint); String clientIp = ServletUtils.getClientIP(); return SecureUtil.md5(methodName + argsStr + clientIp); } diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java index 236ea45cb6..9ae2ff3f1d 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/DefaultRateLimiterKeyResolver.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl; -import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter; import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; import org.aspectj.lang.JoinPoint; @@ -18,7 +18,7 @@ public class DefaultRateLimiterKeyResolver implements RateLimiterKeyResolver { @Override public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); + String argsStr = StrUtils.joinMethodArgs(joinPoint); return SecureUtil.md5(methodName + argsStr); } diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java index 300a4d2f1e..653790dff7 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/ServerNodeRateLimiterKeyResolver.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl; -import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.system.SystemUtil; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter; import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; import org.aspectj.lang.JoinPoint; @@ -19,7 +19,7 @@ public class ServerNodeRateLimiterKeyResolver implements RateLimiterKeyResolver @Override public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); + String argsStr = StrUtils.joinMethodArgs(joinPoint); String serverNode = String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID()); return SecureUtil.md5(methodName + argsStr + serverNode); } diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java index a8d1c3a98c..eccabf19bc 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/keyresolver/impl/UserRateLimiterKeyResolver.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl; -import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter; import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; @@ -19,7 +19,7 @@ public class UserRateLimiterKeyResolver implements RateLimiterKeyResolver { @Override public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { String methodName = joinPoint.getSignature().toString(); - String argsStr = StrUtil.join(",", joinPoint.getArgs()); + String argsStr = StrUtils.joinMethodArgs(joinPoint); Long userId = WebFrameworkUtils.getLoginUserId(); Integer userType = WebFrameworkUtils.getLoginUserType(); return SecureUtil.md5(methodName + argsStr + userId + userType); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java index 3f72767c0b..75a6d92a71 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java @@ -6,6 +6,7 @@ import cn.hutool.core.util.URLUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.*; import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO; import cn.iocoder.yudao.module.infra.service.file.FileService; @@ -77,6 +78,7 @@ public class FileController { @GetMapping("/{configId}/get/**") @PermitAll + @TenantIgnore @Operation(summary = "下载文件") @Parameter(name = "configId", description = "配置编号", required = true) public void getFileContent(HttpServletRequest request, diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java index cb645f42b5..6e4b0f62bc 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java @@ -164,8 +164,6 @@ public class CodegenEngine { .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑 vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue")) // VUE3_VBEN5_ANTD - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/data.ts"), - vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts")) .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"), vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"), diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm index 18dd72bd13..d3342f1fc8 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm @@ -1,4 +1,5 @@ import type { PageParam, PageResult } from '@vben/request'; +import type { Dayjs } from 'dayjs'; import { requestClient } from '#/api/request'; #set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}") @@ -16,7 +17,7 @@ export namespace ${simpleClassName}Api { #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal") ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment} #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime") - ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment} + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment} #else ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment} #end @@ -32,7 +33,7 @@ export namespace ${simpleClassName}Api { #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal") ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment} #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime") - ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: Date; // ${column.columnComment} + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment} #else ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment} #end diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/data.ts.vm deleted file mode 100644 index 9f356f6848..0000000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/data.ts.vm +++ /dev/null @@ -1,349 +0,0 @@ -import type { VxeTableGridOptions } from '@vben/plugins/vxe-table'; -import type { VbenFormSchema } from '#/adapter/form'; -import type { OnActionClickFn } from '#/adapter/vxe-table'; -import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; - -import { z } from '#/adapter/form'; -#if(${table.templateType} == 2)## 树表需要导入这些 -import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; -import { handleTree } from '#/utils/tree'; -#end -import { DICT_TYPE, getDictOptions } from '#/utils/dict'; -import { getRangePickerDefaultProps } from '#/utils/date'; -import { useAccess } from '@vben/access'; - -const { hasAccessByCodes } = useAccess(); - -/** 列表的搜索表单 */ -export function useGridFormSchema(): VbenFormSchema[] { - return [ -#foreach($column in $columns) -#if ($column.listOperation) - #set ($dictType = $column.dictType) - #set ($javaType = $column.javaType) - #set ($javaField = $column.javaField) - #set ($comment = $column.columnComment) - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") - #set ($dictMethod = "number") - #elseif ($javaType == "String") - #set ($dictMethod = "string") - #elseif ($javaType == "Boolean") - #set ($dictMethod = "boolean") - #end - { - fieldName: '${javaField}', - label: '${comment}', - #if ($column.htmlType == "input" || $column.htmlType == "textarea" || $column.htmlType == "editor") - component: 'Input', - componentProps: { - allowClear: true, - placeholder: '请输入${comment}', - }, - #elseif ($column.htmlType == "select" || $column.htmlType == "radio") - component: 'Select', - componentProps: { - allowClear: true, - #if ("" != $dictType)## 设置了 dictType 数据字典的情况 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else## 未设置 dictType 数据字典的情况 - options: [], - #end - placeholder: '请选择${comment}', - }, - #elseif($column.htmlType == "datetime") - component: 'RangePicker', - componentProps: { - ...getRangePickerDefaultProps(), - allowClear: true, - }, - #end - }, -#end -#end - ]; -} - -/** 列表的字段 */ -export function useGridColumns( - onActionClick?: OnActionClickFn<${simpleClassName}Api.${simpleClassName}>, -): VxeTableGridOptions<${simpleClassName}Api.${simpleClassName}>['columns'] { - return [ -#if ($table.templateType == 12) ## 内嵌情况 - { type: 'expand', width: 80, slots: { content: 'expand_content' } }, -#end -#foreach($column in $columns) -#if ($column.listOperationResult) - #set ($dictType = $column.dictType) - #set ($javaField = $column.javaField) - #set ($comment = $column.columnComment) - { - field: '${javaField}', - title: '${comment}', - minWidth: 120, - #if ($column.javaType == "LocalDateTime")## 时间类型 - formatter: 'formatDateTime', - #elseif("" != $dictType)## 数据字典 - cellRender: { - name: 'CellDict', - props: { type: DICT_TYPE.$dictType.toUpperCase() }, - }, - #end - #if (${table.templateType} == 2 && $column.id == $treeNameColumn.id)## 树表特有:标记树节点列 - treeNode: true, - #end - }, -#end -#end - { - field: 'operation', - title: '操作', - minWidth: 200, - align: 'center', - fixed: 'right', - headerAlign: 'center', - showOverflow: false, - cellRender: { - attrs: { - nameField: '${columns[0].javaField}', - nameTitle: '${table.classComment}', - onClick: onActionClick, - }, - name: 'CellOperation', - options: [ -#if (${table.templateType} == 2)## 树表特有操作 - { - code: 'append', - text: '新增下级', - show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:create']), - }, -#end - { - code: 'edit', - show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']), - }, - { - code: 'delete', - show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']), -#if (${table.templateType} == 2)## 树表禁止删除带有子节点的数据 - disabled: (row: ${simpleClassName}Api.${simpleClassName}) => { - return !!(row.children && row.children.length > 0); - }, -#end - }, - ], - }, - }, - ]; -} - -## 标准模式和内嵌模式时,主子关系一对一则生成表单schema,一对多则生成列表schema(内嵌模式时表单schema也要生成)。erp 模式时都生成 -## 特殊:主子表专属逻辑 -#foreach ($subTable in $subTables) - #set ($index = $foreach.count - 1) - #set ($subColumns = $subColumnsList.get($index))##当前字段数组 - #set ($subSimpleClassName = $subSimpleClassNames.get($index)) - #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段 - #set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index)) -// ==================== 子表($subTable.classComment) ==================== - -#if ($table.templateType == 11) ## erp 情况 -/** 列表的搜索表单 */ -export function use${subSimpleClassName}GridFormSchema(): VbenFormSchema[] { - return [ - #foreach($column in $subColumns) - #if ($column.listOperation) - #set ($dictType = $column.dictType) - #set ($javaType = $column.javaType) - #set ($javaField = $column.javaField) - #set ($comment = $column.columnComment) - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") - #set ($dictMethod = "number") - #elseif ($javaType == "String") - #set ($dictMethod = "string") - #elseif ($javaType == "Boolean") - #set ($dictMethod = "boolean") - #end - { - fieldName: '${javaField}', - label: '${comment}', - #if ($column.htmlType == "input" || $column.htmlType == "textarea" || $column.htmlType == "editor") - component: 'Input', - componentProps: { - allowClear: true, - placeholder: '请输入${comment}', - }, - #elseif ($column.htmlType == "select" || $column.htmlType == "radio") - component: 'Select', - componentProps: { - allowClear: true, - #if ("" != $dictType)## 设置了 dictType 数据字典的情况 - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - #else## 未设置 dictType 数据字典的情况 - options: [], - #end - placeholder: '请选择${comment}', - }, - #elseif($column.htmlType == "datetime") - component: 'RangePicker', - componentProps: { - ...getRangePickerDefaultProps(), - allowClear: true, - }, - #end - }, - #end - #end - ]; -} - -/** 列表的字段 */ -export function use${subSimpleClassName}GridColumns( - onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>, -): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] { - return [ - #foreach($column in $subColumns) - #if ($column.listOperationResult) - #set ($dictType = $column.dictType) - #set ($javaField = $column.javaField) - #set ($comment = $column.columnComment) - { - field: '${javaField}', - title: '${comment}', - minWidth: 120, - #if ($column.javaType == "LocalDateTime")## 时间类型 - formatter: 'formatDateTime', - #elseif("" != $dictType)## 数据字典 - cellRender: { - name: 'CellDict', - props: { type: DICT_TYPE.$dictType.toUpperCase() }, - }, - #end - }, - #end - #end - { - field: 'operation', - title: '操作', - minWidth: 200, - align: 'center', - fixed: 'right', - headerAlign: 'center', - showOverflow: false, - cellRender: { - attrs: { - nameField: '${columns[0].javaField}', - nameTitle: '${subTable.classComment}', - onClick: onActionClick, - }, - name: 'CellOperation', - options: [ - { - code: 'edit', - show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:update']), - }, - { - code: 'delete', - show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']), - }, - ], - }, - }, - ]; -} - -#else - #if ($subTable.subJoinMany) ## 一对多 - /** 新增/修改列表的字段 */ - export function use${subSimpleClassName}GridEditColumns( - onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>, - ): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] { - return [ - #foreach($column in $subColumns) - #if ($column.createOperation || $column.updateOperation) - #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 - #set ($dictType = $column.dictType) - #set ($javaField = $column.javaField) - #set ($comment = $column.columnComment) - #if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short") - #set ($dictMethod = "number") - #elseif ($javaType == "String") - #set ($dictMethod = "string") - #elseif ($javaType == "Boolean") - #set ($dictMethod = "boolean") - #end - { - field: '${javaField}', - title: '${comment}', - minWidth: 120, - slots: { default: '${javaField}' }, - #if ($column.htmlType == "select" || $column.htmlType == "checkbox" || $column.htmlType == "radio") - #if ("" != $dictType)## 有数据字典 - params: { - options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), - }, - #else - params: { - options: [], - }, - #end - #end - }, - #end - #end - #end - { - field: 'operation', - title: '操作', - minWidth: 60, - align: 'center', - fixed: 'right', - headerAlign: 'center', - showOverflow: false, - cellRender: { - attrs: { - nameField: '${columns[0].javaField}', - nameTitle: '${table.classComment}', - onClick: onActionClick, - }, - name: 'CellOperation', - options: [ - { - code: 'delete', - show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:delete']), - }, - ], - }, - }, - ]; - } - #end - #if ($table.templateType == 12) ## 内嵌情况 - /** 列表的字段 */ - export function use${subSimpleClassName}GridColumns(): VxeTableGridOptions<${simpleClassName}Api.${subSimpleClassName}>['columns'] { - return [ - #foreach($column in $subColumns) - #if ($column.listOperationResult) - #set ($dictType = $column.dictType) - #set ($javaField = $column.javaField) - #set ($comment = $column.columnComment) - { - field: '${javaField}', - title: '${comment}', - minWidth: 120, - #if ($column.javaType == "LocalDateTime")## 时间类型 - formatter: 'formatDateTime', - #elseif("" != $dictType)## 数据字典 - cellRender: { - name: 'CellDict', - props: { type: DICT_TYPE.$dictType.toUpperCase() }, - }, - #end - }, - #end - #end - ]; - } - - #end -#end -#end \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm index 281df24799..187f66c144 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm @@ -5,11 +5,11 @@ import type { Rule } from 'ant-design-vue/es/form'; import { useVbenModal } from '@vben/common-ui'; import { Tinymce as RichTextarea } from '#/components/tinymce'; import { ImageUpload, FileUpload } from "#/components/upload"; -import { message, Tabs, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker } from 'ant-design-vue'; +import { message, Tabs, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker, TreeSelect } from 'ant-design-vue'; import { DICT_TYPE, getDictOptions } from '#/utils/dict'; #if($table.templateType == 2)## 树表需要导入这些 import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; -import { handleTree } from '#/utils/tree'; +import { handleTree } from '@vben/utils' #end ## 特殊:主子表专属逻辑 #if ( $table.templateType == 10 || $table.templateType == 12 ) @@ -25,9 +25,8 @@ import { $t } from '#/locales'; import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; const emit = defineEmits(['success']); + const formRef = ref(); -const labelCol = { span: 5 }; -const wrapperCol = { span: 13 }; const formData = ref>({ #foreach ($column in $columns) #if ($column.createOperation || $column.updateOperation) @@ -91,10 +90,12 @@ const resetForm = () => { /** 获得${table.classComment}树 */ const get${simpleClassName}Tree = async () => { ${classNameVar}Tree.value = [] - const data = await get${simpleClassName}List() - const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] } - root.children = handleTree(data, 'id', '${treeParentColumn.javaField}') - ${classNameVar}Tree.value.push(root) + const data = await get${simpleClassName}List({}); + data.unshift({ + id: 0, + name: '顶级${table.classComment}', + }); + ${classNameVar}Tree.value = handleTree(data); } #end @@ -111,10 +112,11 @@ const [Modal, modalApi] = useVbenModal({ #if ($subTable.subJoinMany) ## 一对多 ## TODO 列表值校验? #else - const ${subClassNameVar}Valid = await ${subClassNameVar}FormRef.value?.validate(); - if (!${subClassNameVar}Valid) { - subTabsName.value = '${subClassNameVar}'; - return; + try { + await ${subClassNameVar}FormRef.value?.validate() + } catch (e) { + subTabsName.value = '${subClassNameVar}' + return } #end #end @@ -133,7 +135,7 @@ const [Modal, modalApi] = useVbenModal({ #if ($subTable.subJoinMany) data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData(); #else - data.${subClassNameVar} = await ${subClassNameVar}FormRef.value?.getValues(); + data.${subClassNameVar} = ${subClassNameVar}FormRef.value?.getValues(); #end #end #end @@ -156,7 +158,6 @@ const [Modal, modalApi] = useVbenModal({ resetForm() return; } - // 加载数据 let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>(); if (!data) { @@ -185,8 +186,8 @@ const [Modal, modalApi] = useVbenModal({ ref="formRef" :model="formData" :rules="rules" - :label-col="labelCol" - :wrapper-col="wrapperCol" + :label-col="{ span: 5 }" + :wrapper-col="{ span: 18 }" > #foreach($column in $columns) #if ($column.createOperation || $column.updateOperation) @@ -207,9 +208,17 @@ const [Modal, modalApi] = useVbenModal({ v-model:value="formData.${javaField}" :treeData="${classNameVar}Tree" #if ($treeNameColumn.javaField == "name") - :fieldNames="defaultProps" + :fieldNames="{ + label: 'name', + value: 'id', + children: 'children', + }" #else - :fieldNames="{...defaultProps, label: '$treeNameColumn.javaField'}" + :fieldNames="{ + label: '$treeNameColumn.javaField', + value: 'id', + children: 'children', + }" #end checkable treeDefaultExpandAll @@ -239,9 +248,10 @@ const [Modal, modalApi] = useVbenModal({ + > + {{ dict.label }} + #else##没数据字典 #end @@ -254,9 +264,10 @@ const [Modal, modalApi] = useVbenModal({ + > + {{ dict.label }} + #else##没数据字典 #end diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm index aaaad6b1d8..c7f4822686 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm @@ -1,8 +1,9 @@ + #elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField) + #else #end @@ -305,6 +385,16 @@ onMounted(() => { #end +#if ( $table.templateType != 2 )
{ @change="getList" />
+#end #if ($table.templateType == 11) ## erp情况 diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm index e114b7daea..77b9d11dd2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm @@ -4,49 +4,54 @@ #set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))