From f141d64eb2bd755a44564157d3c5254506230d11 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 6 Feb 2025 16:05:56 +0800 Subject: [PATCH 001/134] =?UTF-8?q?=E3=80=90=E4=BE=9D=E8=B5=96=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E3=80=91AWS=20SDK=20for=20Java=201.x=20to=202.x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 16 +- .../yudao-module-infra-biz/pom.xml | 13 +- .../file/core/client/s3/S3FileClient.java | 174 ++++++++++++------ 3 files changed, 133 insertions(+), 70 deletions(-) diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 4ecaa83e29..9787ede53c 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -68,7 +68,7 @@ 2.17.0 1.27.1 - 1.12.777 + 2.30.14 2.0.5 1.8.1 4.6.0 @@ -91,6 +91,13 @@ pom import + + software.amazon.awssdk + bom + ${awssdk.version} + pom + import + @@ -538,13 +545,6 @@ ${jsoup.version} - - - com.amazonaws - aws-java-sdk-s3 - ${aws-java-sdk-s3.version} - - com.xingyuv spring-boot-starter-justauth diff --git a/yudao-module-infra/yudao-module-infra-biz/pom.xml b/yudao-module-infra/yudao-module-infra-biz/pom.xml index 9786c000dd..3c40314889 100644 --- a/yudao-module-infra/yudao-module-infra-biz/pom.xml +++ b/yudao-module-infra/yudao-module-infra-biz/pom.xml @@ -115,9 +115,18 @@ com.jcraft jsch + - com.amazonaws - aws-java-sdk-s3 + software.amazon.awssdk + s3 + + + software.amazon.awssdk + auth + + + software.amazon.awssdk + regions diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java index 5c76e1a7c8..16a49bba4e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java @@ -4,65 +4,68 @@ import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; -import com.amazonaws.HttpMethod; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.S3Object; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3Configuration; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; -import java.io.ByteArrayInputStream; -import java.util.Date; -import java.util.concurrent.TimeUnit; +import java.net.URI; +import java.time.Duration; /** * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 - *

- * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库 * * @author 芋道源码 */ public class S3FileClient extends AbstractFileClient { - private AmazonS3Client client; + private S3Client client; public S3FileClient(Long id, S3FileClientConfig config) { super(id, config); } - @Override - protected void doInit() { - // 补全 domain - if (StrUtil.isEmpty(config.getDomain())) { - config.setDomain(buildDomain()); - } - // 初始化客户端 - client = (AmazonS3Client)AmazonS3ClientBuilder.standard() - .withCredentials(buildCredentials()) - .withEndpointConfiguration(buildEndpointConfiguration()) + /** + * 动态创建 S3Presigner + * + * @param endpoint 节点地址 + * @param accessKey 访问 Key + * @param secretKey 访问 Secret + * @return S3Presigner + */ + private static S3Presigner createPresigner(String endpoint, String accessKey, String secretKey) { + return S3Presigner.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey))) + .endpointOverride(URI.create(endpoint)) .build(); } /** - * 基于 config 秘钥,构建 S3 客户端的认证信息 + * 生成动态的预签名上传 URL * - * @return S3 客户端的认证信息 + * @param bucket 存储 Bucket + * @param path 相对路径 + * @param duration 过期时间 + * @param endpoint 节点地址 + * @param accessKey 访问 Key + * @param secretKey 访问 Secret + * @return 生成的上传 URL */ - private AWSStaticCredentialsProvider buildCredentials() { - return new AWSStaticCredentialsProvider( - new BasicAWSCredentials(config.getAccessKey(), config.getAccessSecret())); - } - - /** - * 构建 S3 客户端的 Endpoint 配置,包括 region、endpoint - * - * @return S3 客户端的 EndpointConfiguration 配置 - */ - private AwsClientBuilder.EndpointConfiguration buildEndpointConfiguration() { - return new AwsClientBuilder.EndpointConfiguration(config.getEndpoint(), - null); // 无需设置 region + public static String getPresignedUrl(String bucket, String path, Duration duration, + String endpoint, String accessKey, String secretKey) { + try (S3Presigner presigner = createPresigner(endpoint, accessKey, secretKey)) { + return presigner.presignPutObject(PutObjectPresignRequest.builder() + .signatureDuration(duration) + .putObjectRequest(b -> b.bucket(bucket).key(path)) + .build()).url().toString(); + } } /** @@ -80,39 +83,90 @@ public class S3FileClient extends AbstractFileClient { } @Override - public String upload(byte[] content, String path, String type) throws Exception { - // 元数据,主要用于设置文件类型 - ObjectMetadata objectMetadata = new ObjectMetadata(); - objectMetadata.setContentType(type); - objectMetadata.setContentLength(content.length); // 如果不设置,会有 “ No content length specified for stream data” 警告日志 - // 执行上传 - client.putObject(config.getBucket(), - path, // 相对路径 - new ByteArrayInputStream(content), // 文件内容 - objectMetadata); + protected void doInit() { + // 补全 domain + if (StrUtil.isEmpty(config.getDomain())) { + config.setDomain(buildDomain()); + } + // 初始化 S3 客户端 + client = S3Client.builder() + .credentialsProvider(buildCredentials()) + .region(Region.of(config.getEndpoint())) // 这里随便填,SDK 需要 + .endpointOverride(URI.create(buildEndpoint())) + .serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build()) // Path-style 访问 + .build(); + } + /** + * 基于 config 秘钥,构建 S3 客户端的认证信息 + * + * @return S3 客户端的认证信息 + */ + private StaticCredentialsProvider buildCredentials() { + return StaticCredentialsProvider.create( + AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret())); + } + + /** + * 节点地址补全协议头 + * + * @return 节点地址 + */ + private String buildEndpoint() { + // 如果已经是 http 或者 https,则不进行拼接 + if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { + return config.getEndpoint(); + } + return StrUtil.format("https://{}", config.getEndpoint()); + } + + @Override + public String upload(byte[] content, String path, String type) { + // 构造 PutObjectRequest + PutObjectRequest putRequest = PutObjectRequest.builder() + .bucket(config.getBucket()) + .key(path) + .contentType(type) + .contentLength((long) content.length) + .build(); + + // 上传文件 + client.putObject(putRequest, RequestBody.fromBytes(content)); // 拼接返回路径 return config.getDomain() + "/" + path; } @Override - public void delete(String path) throws Exception { - client.deleteObject(config.getBucket(), path); + public void delete(String path) { + DeleteObjectRequest deleteRequest = DeleteObjectRequest.builder() + .bucket(config.getBucket()) + .key(path) + .build(); + client.deleteObject(deleteRequest); } @Override - public byte[] getContent(String path) throws Exception { - S3Object tempS3Object = client.getObject(config.getBucket(), path); - return IoUtil.readBytes(tempS3Object.getObjectContent()); + public byte[] getContent(String path) { + GetObjectRequest getRequest = GetObjectRequest.builder() + .bucket(config.getBucket()) + .key(path) + .build(); + + return IoUtil.readBytes(client.getObject(getRequest)); } @Override - public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception { - // 设定过期时间为 10 分钟。取值范围:1 秒 ~ 7 天 - Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10)); - // 生成上传 URL - String uploadUrl = String.valueOf(client.generatePresignedUrl(config.getBucket(), path, expiration , HttpMethod.PUT)); - return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path); + public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) { + String presignedUrl = getPresignedUrl( + config.getBucket(), + path, + Duration.ofMinutes(10), + config.getEndpoint(), + config.getAccessKey(), + config.getAccessSecret() + ); + + return new FilePresignedUrlRespDTO(presignedUrl, config.getDomain() + "/" + path); } } From cf40cce552590995096e1b081cce34fe7e23421d Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 7 Feb 2025 18:10:03 +0800 Subject: [PATCH 002/134] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91S3FileClient?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../file/core/client/s3/S3FileClient.java | 153 ++++++++---------- 1 file changed, 68 insertions(+), 85 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java index 16a49bba4e..57a3561565 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java @@ -9,7 +9,6 @@ import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; @@ -32,56 +31,6 @@ public class S3FileClient extends AbstractFileClient { super(id, config); } - /** - * 动态创建 S3Presigner - * - * @param endpoint 节点地址 - * @param accessKey 访问 Key - * @param secretKey 访问 Secret - * @return S3Presigner - */ - private static S3Presigner createPresigner(String endpoint, String accessKey, String secretKey) { - return S3Presigner.builder() - .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey))) - .endpointOverride(URI.create(endpoint)) - .build(); - } - - /** - * 生成动态的预签名上传 URL - * - * @param bucket 存储 Bucket - * @param path 相对路径 - * @param duration 过期时间 - * @param endpoint 节点地址 - * @param accessKey 访问 Key - * @param secretKey 访问 Secret - * @return 生成的上传 URL - */ - public static String getPresignedUrl(String bucket, String path, Duration duration, - String endpoint, String accessKey, String secretKey) { - try (S3Presigner presigner = createPresigner(endpoint, accessKey, secretKey)) { - return presigner.presignPutObject(PutObjectPresignRequest.builder() - .signatureDuration(duration) - .putObjectRequest(b -> b.bucket(bucket).key(path)) - .build()).url().toString(); - } - } - - /** - * 基于 bucket + endpoint 构建访问的 Domain 地址 - * - * @return Domain 地址 - */ - private String buildDomain() { - // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO - if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { - return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket()); - } - // 阿里云、腾讯云、华为云都适合。七牛云比较特殊,必须有自定义域名 - return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint()); - } - @Override protected void doInit() { // 补全 domain @@ -91,35 +40,12 @@ public class S3FileClient extends AbstractFileClient { // 初始化 S3 客户端 client = S3Client.builder() .credentialsProvider(buildCredentials()) - .region(Region.of(config.getEndpoint())) // 这里随便填,SDK 需要 + .region(Region.of("us-east-1")) // 必须填,但填什么都行,常见的值有 "us-east-1",不填会报错 .endpointOverride(URI.create(buildEndpoint())) - .serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build()) // Path-style 访问 + //.serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build()) // Path-style 访问 .build(); } - /** - * 基于 config 秘钥,构建 S3 客户端的认证信息 - * - * @return S3 客户端的认证信息 - */ - private StaticCredentialsProvider buildCredentials() { - return StaticCredentialsProvider.create( - AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret())); - } - - /** - * 节点地址补全协议头 - * - * @return 节点地址 - */ - private String buildEndpoint() { - // 如果已经是 http 或者 https,则不进行拼接 - if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { - return config.getEndpoint(); - } - return StrUtil.format("https://{}", config.getEndpoint()); - } - @Override public String upload(byte[] content, String path, String type) { // 构造 PutObjectRequest @@ -157,16 +83,73 @@ public class S3FileClient extends AbstractFileClient { @Override public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) { - String presignedUrl = getPresignedUrl( - config.getBucket(), - path, - Duration.ofMinutes(10), - config.getEndpoint(), - config.getAccessKey(), - config.getAccessSecret() - ); + return new FilePresignedUrlRespDTO(getPresignedUrl(path, Duration.ofMinutes(10)), config.getDomain() + "/" + path); + } - return new FilePresignedUrlRespDTO(presignedUrl, config.getDomain() + "/" + path); + /** + * 动态创建 S3Presigner + * + * @return S3Presigner + */ + private S3Presigner createPresigner() { + return S3Presigner.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()))) + .region(Region.of("us-east-1")) // 必须填,但填什么都行,常见的值有 "us-east-1",不填会报错 + .endpointOverride(URI.create(buildEndpoint())) + .build(); + } + + /** + * 生成动态的预签名上传 URL + * + * @param path 相对路径 + * @param duration 过期时间 + * @return 生成的上传 URL + */ + public String getPresignedUrl(String path, Duration duration) { + try (S3Presigner presigner = createPresigner()) { + return presigner.presignPutObject(PutObjectPresignRequest.builder() + .signatureDuration(duration) + .putObjectRequest(b -> b.bucket(config.getBucket()).key(path)) + .build()).url().toString(); + } + } + + /** + * 基于 bucket + endpoint 构建访问的 Domain 地址 + * + * @return Domain 地址 + */ + private String buildDomain() { + // 如果已经是 http 或者 https,则不进行拼接.主要适配 MinIO + if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { + return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket()); + } + // 阿里云、腾讯云、华为云都适合。七牛云比较特殊,必须有自定义域名 + return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint()); + } + + /** + * 基于 config 秘钥,构建 S3 客户端的认证信息 + * + * @return S3 客户端的认证信息 + */ + private StaticCredentialsProvider buildCredentials() { + return StaticCredentialsProvider.create( + AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret())); + } + + /** + * 节点地址补全协议头 + * + * @return 节点地址 + */ + private String buildEndpoint() { + // 如果已经是 http 或者 https,则不进行拼接 + if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) { + return config.getEndpoint(); + } + return StrUtil.format("https://{}", config.getEndpoint()); } } From 09aa6b25675e03dddb70afa2730d275ba07ad967 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 2 Apr 2025 11:35:36 +0800 Subject: [PATCH 003/134] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=E5=95=86=E5=9F=8E=EF=BC=9A=E4=BC=98=E6=83=A0?= =?UTF-8?q?=E5=88=B8=E5=9C=A8=E9=A2=86=E5=8F=96=E7=B1=BB=E5=9E=8B=E4=B8=BA?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E5=8F=91=E6=94=BE=E5=92=8C=E6=96=B0=E4=BA=BA?= =?UTF-8?q?=E5=88=B8=E6=97=B6=E6=97=A0=E6=B3=95=E5=8F=91=E9=80=81=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../promotion/service/coupon/CouponServiceImpl.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java index 7a0ae512f6..22741e4023 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java @@ -263,13 +263,17 @@ public class CouponServiceImpl implements CouponService { if (CollUtil.isEmpty(userIds)) { throw exception(COUPON_TEMPLATE_USER_ALREADY_TAKE); } - // 校验模板 if (couponTemplate == null) { throw exception(COUPON_TEMPLATE_NOT_EXISTS); } + // 校验领取方式 + if (ObjUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) { + throw exception(COUPON_TEMPLATE_CANNOT_TAKE); + } // 校验剩余数量 - if (ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TIME_LIMIT_COUNT_MAX) // 非不限制 + if (ObjUtil.equal(CouponTakeTypeEnum.USER.getType(), couponTemplate.getTakeType()) // 直接领取 + && ObjUtil.notEqual(couponTemplate.getTakeLimitCount(), CouponTemplateDO.TIME_LIMIT_COUNT_MAX) // 非不限制 && couponTemplate.getTakeCount() + userIds.size() > couponTemplate.getTotalCount()) { throw exception(COUPON_TEMPLATE_NOT_ENOUGH); } @@ -279,10 +283,7 @@ public class CouponServiceImpl implements CouponService { throw exception(COUPON_TEMPLATE_EXPIRED); } } - // 校验领取方式 - if (ObjectUtil.notEqual(couponTemplate.getTakeType(), takeType.getType())) { - throw exception(COUPON_TEMPLATE_CANNOT_TAKE); - } + } /** From d5c1a2ff9f356dfe7dde4d15af21441dedd83072 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 2 Apr 2025 17:48:10 +0800 Subject: [PATCH 004/134] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=E5=95=86=E5=9F=8E=EF=BC=9A=E8=AE=A2=E5=8D=95?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=94=B6=E5=9B=9E=E4=BC=98=E6=83=A0=E5=88=B8?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E5=AF=BC=E8=87=B4=E7=AE=A1=E7=90=86=E5=91=98?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E9=80=80=E6=AC=BE=E4=B8=8D=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/promotion/service/coupon/CouponServiceImpl.java | 3 ++- .../module/trade/service/aftersale/AfterSaleServiceImpl.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java index 22741e4023..b8759436bd 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java @@ -22,6 +22,7 @@ import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTemplateValidityType import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -176,7 +177,7 @@ public class CouponServiceImpl implements CouponService { * @param couponId 模版编号 * @param userId 用户编号 */ - @Transactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) // 每次调用开启一个新的事务 public void invalidateCoupon(Long couponId, Long userId) { // 1.1 校验优惠券 CouponDO coupon = couponMapper.selectByIdAndUserId(couponId, userId); diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java index bcac6b5b6a..a01e350424 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java @@ -36,6 +36,7 @@ import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -44,7 +45,6 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.validation.annotation.Validated; -import jakarta.annotation.Resource; import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -386,7 +386,7 @@ public class AfterSaleServiceImpl implements AfterSaleService { public void afterCommit() { // 创建退款单 PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties) - .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));; + .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName())); Long payRefundId = payRefundApi.createRefund(createReqDTO); // 更新售后单的退款单号 tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId)); From dda2b56bbffdabff9fc19bdceae6b045a308290e Mon Sep 17 00:00:00 2001 From: Ren Date: Sat, 12 Apr 2025 23:52:24 +0800 Subject: [PATCH 005/134] =?UTF-8?q?feat=EF=BC=9AAI=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=96=B0=E5=A2=9EToolContext?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/AiChatMessageServiceImpl.java | 16 +++++-- .../model/tool/UserIdQueryToolFunction.java | 43 +++++++++++++++++++ .../framework/ai/core/pojo/AiToolContext.java | 37 ++++++++++++++++ .../yudao/framework/ai/core/util/AiUtils.java | 19 ++++---- 4 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserIdQueryToolFunction.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/pojo/AiToolContext.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index f310ba69fd..36dcaa999c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -4,10 +4,12 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; +import cn.iocoder.yudao.framework.ai.core.pojo.AiToolContext; import cn.iocoder.yudao.framework.ai.core.util.AiUtils; 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.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; @@ -115,7 +117,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { knowledgeSegments); // 3.2 创建 chat 需要的 Prompt - Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); + Prompt prompt = buildPrompt(chatModel, conversation, historyMessages, knowledgeSegments, model, sendReqVO); ChatResponse chatResponse = chatModel.call(prompt); // 3.3 更新响应内容 @@ -164,7 +166,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { knowledgeSegments); // 4.2 构建 Prompt,并进行调用 - Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); + Prompt prompt = buildPrompt(chatModel, conversation, historyMessages, knowledgeSegments, model, sendReqVO); Flux streamResponse = chatModel.stream(prompt); // 4.3 流式返回 @@ -220,7 +222,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { return knowledgeSegments; } - private Prompt buildPrompt(AiChatConversationDO conversation, List messages, + private Prompt buildPrompt(StreamingChatModel chatModel, AiChatConversationDO conversation, List messages, List knowledgeSegments, AiModelDO model, AiChatMessageSendReqVO sendReqVO) { List chatMessages = new ArrayList<>(); @@ -247,16 +249,22 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { // 2.1 查询 tool 工具 Set toolNames = null; + Map toolContext = Map.of(); if (conversation.getRoleId() != null) { AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId()); if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) { toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName); + // 2.1.1 构建 Function Calling 的上下文参数 + toolContext = Map.of( + AiToolContext.CONTEXT_KEY, new AiToolContext().setChatModel(chatModel).setUserId(SecurityFrameworkUtils.getLoginUserId()) + .setRoleId(conversation.getRoleId()) + .setConversationId(conversation.getId())); } } // 2.2 构建 ChatOptions 对象 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), - conversation.getTemperature(), conversation.getMaxTokens(), toolNames); + conversation.getTemperature(), conversation.getMaxTokens(), toolNames, toolContext); return new Prompt(chatMessages, chatOptions); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserIdQueryToolFunction.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserIdQueryToolFunction.java new file mode 100644 index 0000000000..380fdba414 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserIdQueryToolFunction.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.ai.service.model.tool; + +import cn.iocoder.yudao.framework.ai.core.pojo.AiToolContext; +import com.fasterxml.jackson.annotation.JsonClassDescription; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.ai.chat.model.ToolContext; +import org.springframework.stereotype.Component; + +import java.util.function.BiFunction; + +/** + * 工具:用户ID查询(上下文参数Demo) + * + * @author Ren + */ +@Component("userid_query") +public class UserIdQueryToolFunction + implements BiFunction { + + @Data + @JsonClassDescription("用户ID查询") + public static class Request { } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Response { + /** + * 用户ID + */ + private Long UserId; + + } + @Override + public UserIdQueryToolFunction.Response apply(UserIdQueryToolFunction.Request request, ToolContext toolContext) { + // 获取当前登录用户 + AiToolContext context = (AiToolContext) toolContext.getContext().get(AiToolContext.CONTEXT_KEY); + + return new Response(context.getUserId()); + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/pojo/AiToolContext.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/pojo/AiToolContext.java new file mode 100644 index 0000000000..a0aa72f466 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/pojo/AiToolContext.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.framework.ai.core.pojo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.StreamingChatModel; + +/** + * 工具上下文参数 DTO,让AI工具可以处理当前用户的相关信息 + * + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AiToolContext { + public static final String CONTEXT_KEY = "AI_TOOL_CONTEXT"; + /** + * 用户ID + */ + private Long userId; + + /** + * 聊天模型 + */ + private StreamingChatModel chatModel; + + /** + * 关联的聊天角色Id + */ + private Long roleId; + + /** + * 会话Id + */ + private Long conversationId; +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index 09370c6363..47577356be 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -15,6 +15,7 @@ import org.springframework.ai.qianfan.QianFanChatOptions; import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; import java.util.Collections; +import java.util.Map; import java.util.Set; /** @@ -25,28 +26,28 @@ import java.util.Set; public class AiUtils { public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { - return buildChatOptions(platform, model, temperature, maxTokens, null); + return buildChatOptions(platform, model, temperature, maxTokens, null, Map.of()); } public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens, - Set toolNames) { + Set toolNames, Map toolContext) { toolNames = ObjUtil.defaultIfNull(toolNames, Collections.emptySet()); // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) - .withFunctions(toolNames).build(); + .withFunctions(toolNames).withToolContext(toolContext).build(); case YI_YAN: return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case ZHI_PU: return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .functions(toolNames).build(); + .functions(toolNames).toolContext(toolContext).build(); case MINI_MAX: return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .functions(toolNames).build(); + .functions(toolNames).toolContext(toolContext).build(); case MOONSHOT: return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .functions(toolNames).build(); + .functions(toolNames).toolContext(toolContext).build(); case OPENAI: case DEEP_SEEK: // 复用 OpenAI 客户端 case DOU_BAO: // 复用 OpenAI 客户端 @@ -55,14 +56,14 @@ public class AiUtils { case SILICON_FLOW: // 复用 OpenAI 客户端 case BAI_CHUAN: // 复用 OpenAI 客户端 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .toolNames(toolNames).build(); + .toolNames(toolNames).toolContext(toolContext).build(); case AZURE_OPENAI: // TODO 芋艿:貌似没 model 字段???! return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) - .toolNames(toolNames).build(); + .toolNames(toolNames).toolContext(toolContext).build(); case OLLAMA: return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) - .toolNames(toolNames).build(); + .toolNames(toolNames).toolContext(toolContext).build(); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } From 24aea3e30a28f9f93e33db3f743208aeafb96975 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 16 Apr 2025 15:26:01 +0800 Subject: [PATCH 006/134] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91INFRA:=20vue3=5Fvben5=5Fantd=20schema=20?= =?UTF-8?q?=E5=8D=95=E8=A1=A8=E5=92=8C=E6=A0=91=E8=A1=A8=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=A8=A1=E7=89=88=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vue3_vben_next/schema/views/data.ts.vm | 6 ++-- .../vue3_vben_next/schema/views/form.vue.vm | 32 +++---------------- .../vue3_vben_next/schema/views/index.vue.vm | 28 +++++++++------- 3 files changed, 24 insertions(+), 42 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm index 349bf378a5..6877e59c24 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm @@ -9,6 +9,7 @@ import { get${simpleClassName}List } from '#/api/${table.moduleName}/${simpleCla 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(); @@ -196,6 +197,7 @@ export function useGridFormSchema(): VbenFormSchema[] { #elseif($column.htmlType == "datetime") component: 'RangePicker', componentProps: { + ...getRangePickerDefaultProps(), allowClear: true, }, #end @@ -237,7 +239,7 @@ export function useGridColumns( field: 'operation', title: '操作', minWidth: 200, - align: 'right', + align: 'center', fixed: 'right', headerAlign: 'center', showOverflow: false, @@ -251,7 +253,7 @@ export function useGridColumns( options: [ #if (${table.templateType} == 2)## 树表特有操作 { - code: 'add_child', + code: 'append', text: '新增下级', show: hasAccessByCodes(['${table.moduleName}:${simpleClassName_strikeCase}:create']), }, diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm index 9a357427ad..6d25b58a82 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm @@ -64,49 +64,25 @@ const [Modal, modalApi] = useVbenModal({ if (!isOpen) { return; } + // 加载数据 -#if (${table.templateType} == 2)## 树表处理传入的父ID let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>(); -#else## 标准表直接获取 - const data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>(); -#end if (!data) { return; } -#if (${table.templateType} == 2)## 树表特有:处理新增下级的情况 - // 处理新增下级的情况 - if (!data.id && data.${treeParentColumn.javaField}) { - parentId.value = data.${treeParentColumn.javaField}; - formData.value = { ${treeParentColumn.javaField}: parentId.value } as ${simpleClassName}Api.${simpleClassName}; - await formApi.setValues(formData.value); - return; - } -#end - if (data.id) { // 编辑 modalApi.lock(); try { -#if (${table.templateType} == 2)## 树表获取数据后重新赋值 data = await get${simpleClassName}(data.id); - formData.value = data; -#else## 标准表设置表单数据 - formData.value = await get${simpleClassName}(data.id as number); -#end - await formApi.setValues(formData.value); } finally { modalApi.lock(false); } - } else { - // 新增 -#if (${table.templateType} == 2)## 树表特有:设置顶级ID - formData.value = { ${treeParentColumn.javaField}: 0 } as ${simpleClassName}Api.${simpleClassName}; -#else## 标准表:设置空值 - formData.value = data; -#end - await formApi.setValues(formData.value || {}); } + // 设置到 values + formData.value = data; + await formApi.setValues(formData.value); }, }); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm index 198e1d4c4d..43c2a3730f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm @@ -7,7 +7,7 @@ import { Button, message } from 'ant-design-vue'; import { Download, Plus } from '@vben/icons'; import Form from './modules/form.vue'; -import { ref } from 'vue'; +import { ref, h } from 'vue'; import { $t } from '#/locales'; import { useVbenVxeGrid } from '#/adapter/vxe-table'; #if (${table.templateType} == 2)## 树表接口 @@ -56,7 +56,7 @@ function onEdit(row: ${simpleClassName}Api.${simpleClassName}) { #if (${table.templateType} == 2)## 树表特有:新增下级 /** 新增下级${table.classComment} */ -function onAddChild(row: ${simpleClassName}Api.${simpleClassName}) { +function onAppend(row: ${simpleClassName}Api.${simpleClassName}) { formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open(); } #end @@ -86,20 +86,20 @@ function onActionClick({ row, }: OnActionClickParams<${simpleClassName}Api.${simpleClassName}>) { switch (code) { - case 'edit': { - onEdit(row); + #if (${table.templateType} == 2)## 树表特有:新增下级 + case 'append': { + onAppend(row); break; } + #end case 'delete': { onDelete(row); break; } -#if (${table.templateType} == 2)## 树表特有:新增下级 - case 'add_child': { - onAddChild(row); + case 'edit': { + onEdit(row); break; } -#end } } @@ -167,12 +167,16 @@ const [Grid, gridApi] = useVbenVxeGrid({ {{ isExpanded ? '收缩' : '展开' }} #end - - From e704f4c7553e30b2c26fb9b6068936fba916c7c2 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 16 Apr 2025 15:52:20 +0800 Subject: [PATCH 007/134] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90:=20vue3?= =?UTF-8?q?=5Fvben5=5Fantd=20=E6=A8=A1=E7=89=88=E7=9B=AE=E5=BD=95=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/codegen/inner/CodegenEngine.java | 16 ++++++++++------ .../general}/index.vue.vm | 0 .../schema/api/api.ts.vm | 0 .../schema/views/data.ts.vm | 0 .../schema/views/form.vue.vm | 0 .../schema/views/index.vue.vm | 0 6 files changed, 10 insertions(+), 6 deletions(-) rename yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/{vue3_vben_next/ant_design_vue => vue3_vben5_antd/general}/index.vue.vm (100%) rename yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/{vue3_vben_next => vue3_vben5_antd}/schema/api/api.ts.vm (100%) rename yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/{vue3_vben_next => vue3_vben5_antd}/schema/views/data.ts.vm (100%) rename yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/{vue3_vben_next => vue3_vben5_antd}/schema/views/form.vue.vm (100%) rename yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/{vue3_vben_next => vue3_vben5_antd}/schema/views/index.vue.vm (100%) 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 65cf236b58..3787f616b1 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 @@ -146,13 +146,13 @@ public class CodegenEngine { vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) // VUE3_VBEN5_ANTD_SCHEMA // TODO @puhui999:目录改成 vue3_vben5_antd;然后里面有 schema(目前我们在写的)和 general(你微信里提的,原生的,感觉也要搞!) - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/data.ts"), + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/data.ts"), vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts")) - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/index.vue"), + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/index.vue"), vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/form.vue"), + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/form.vue"), vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) - .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("api/api.ts"), + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"), vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) // 主子表模板配置 - Vue3 vben5 schema 模版 //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/master_slave_data.ts"), @@ -515,8 +515,12 @@ public class CodegenEngine { return "codegen/vue3_vben/" + path + ".vm"; } - private static String vue3VbenNextSchemaTemplatePath(String path) { - return "codegen/vue3_vben_next/schema/" + path + ".vm"; + private static String vue3Vben5AntdSchemaTemplatePath(String path) { + return "codegen/vue3_vben5_antd/schema/" + path + ".vm"; + } + + private static String vue3Vben5AntdGeneralTemplatePath(String path) { + return "codegen/vue3_vben5_antd/general/" + path + ".vm"; } private static boolean isSubTemplate(String path) { diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/ant_design_vue/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/index.vue.vm similarity index 100% rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/ant_design_vue/index.vue.vm rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/index.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm similarity index 100% rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/api/api.ts.vm rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm similarity index 100% rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/data.ts.vm rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm similarity index 100% rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/form.vue.vm rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm similarity index 100% rename from yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben_next/schema/views/index.vue.vm rename to yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm From d67ca0562116c5a2eae88f2cffed2f4a47386313 Mon Sep 17 00:00:00 2001 From: aho <124668342@qq.com> Date: Wed, 16 Apr 2025 17:26:08 +0800 Subject: [PATCH 008/134] =?UTF-8?q?=E3=80=90fix=E3=80=91bpm=20=E5=9B=9E?= =?UTF-8?q?=E9=80=80=E6=93=8D=E4=BD=9C=E5=8F=82=E6=95=B0=E5=8F=96=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index efdb41951c..e59144cb3a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -874,13 +874,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { List returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); List returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId); + List runExecutionIds = new ArrayList<>(); // 2. 给当前要被退回的 task 数组,设置退回意见 taskList.forEach(task -> { // 需要排除掉,不需要设置退回意见的任务 if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { return; } - + runExecutionIds.add(task.getExecutionId()); // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 // 2.1.1 添加评论 @@ -899,7 +900,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 4. 执行驳回 // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因: // 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944 - List runExecutionIds = convertList(taskList, Task::getExecutionId); runtimeService.createChangeActivityStateBuilder() .processInstanceId(currentTask.getProcessInstanceId()) .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) From 3923189887da1cf4bb8a44b7d95bee00544c818f Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 16 Apr 2025 18:47:47 +0800 Subject: [PATCH 009/134] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90:=20vue3?= =?UTF-8?q?=5Fvben5=5Fantd=20schema=20=E4=B8=BB=E5=AD=90=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/codegen/inner/CodegenEngine.java | 19 +- .../vue3_vben5_antd/schema/api/api.ts.vm | 32 ++ .../vue3_vben5_antd/schema/views/data.ts.vm | 164 ++++++++ .../vue3_vben5_antd/schema/views/form.vue.vm | 62 ++- .../schema/views/modules/form_sub_erp.vue.vm | 204 ++++++++++ .../views/modules/form_sub_inner.vue.vm | 2 + .../views/modules/form_sub_normal.vue.vm | 360 ++++++++++++++++++ .../schema/views/modules/list_sub_erp.vue.vm | 184 +++++++++ .../views/modules/list_sub_inner.vue.vm | 4 + 9 files changed, 1021 insertions(+), 10 deletions(-) create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm create mode 100644 yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm 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 3787f616b1..3dececdf43 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 @@ -154,15 +154,16 @@ public class CodegenEngine { vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"), vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts")) - // 主子表模板配置 - Vue3 vben5 schema 模版 - //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/master_slave_data.ts"), - // vue3FilePath("views/${table.moduleName}/${table.businessName}/data.ts")) - //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/master_slave_index.vue"), - // vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue")) - //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/modules/master_slave_form.vue"), - // vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue")) - //.put(CodegenFrontTypeEnum.VUE3_VBEN_NEXT_SCHEMA.getType(), vue3VbenNextSchemaTemplatePath("views/modules/sub_table.vue"), - // vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/sub_table.vue")) + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}Form.vue")) + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}Form.vue")) + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}Form.vue")) + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}List.vue")) + .put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑 + vue3FilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName}List.vue")) .build(); @Resource diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm index b1a24af09c..843f2fd2f6 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm @@ -19,8 +19,40 @@ export namespace ${simpleClassName}Api { #end #if ( $table.templateType == 2 ) children?: ${simpleClassName}[]; +#end +## 特殊:主子表专属逻辑 +#if ( $table.templateType == 10 || $table.templateType == 12 ) + #foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subSimpleClassName = $subSimpleClassNames.get($index)) + #if ( $subTable.subJoinMany ) + ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[] + #else + ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName} + #end + #end #end } +## 特殊:主子表专属逻辑 +#foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subSimpleClassName = $subSimpleClassNames.get($index)) + #set ($subColumns = $subColumnsList.get($index))##当前字段数组 + /** ${subTable.classComment}信息 */ + export interface ${subSimpleClassName} { + #foreach ($column in $subColumns) + #if ($column.createOperation || $column.updateOperation) + #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} + #else + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment} + #end + #end + #end + } +#end } #if ( $table.templateType != 2 ) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm index 6877e59c24..cda2e5c244 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm @@ -276,3 +276,167 @@ export function useGridColumns( }, ]; } + +// TODO @puhui999: 标准模式和内嵌模式时,主子关系一对一则生成表单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 字段 +// ==================== 子表($subTable.classComment) ==================== +/** 新增/修改的表单 */ +export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + #foreach($column in $subColumns) + #if ($column.createOperation || $column.updateOperation) + #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段,这里排除 + #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 + #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 + #else + { + fieldName: '${javaField}', + label: '${comment}', + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键 + rules: 'required', + #end + #if ($column.htmlType == "input") + component: 'Input', + componentProps: { + placeholder: '请输入${comment}', + }, + #elseif($column.htmlType == "imageUpload")## 图片上传 + component: 'FileUpload', + componentProps: { + fileType: 'image', + maxCount: 1, + }, + #elseif($column.htmlType == "fileUpload")## 文件上传 + component: 'FileUpload', + componentProps: { + fileType: 'file', + maxCount: 1, + }, + #elseif($column.htmlType == "editor")## 文本编辑器 + component: 'Editor', + #elseif($column.htmlType == "select")## 下拉框 + component: 'Select', + componentProps: { + #if ("" != $dictType)## 有数据字典 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), + #else##没数据字典 + options: [], + #end + placeholder: '请选择${comment}', + class: 'w-full', + }, + #elseif($column.htmlType == "checkbox")## 多选框 + component: 'Checkbox', + componentProps: { + #if ("" != $dictType)## 有数据字典 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), + #else##没数据字典 + options: [], + #end + }, + #elseif($column.htmlType == "radio")## 单选框 + component: 'RadioGroup', + componentProps: { + #if ("" != $dictType)## 有数据字典 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), + #else##没数据字典 + options: [], + #end + buttonStyle: 'solid', + optionType: 'button', + }, + #elseif($column.htmlType == "datetime")## 时间框 + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + }, + #elseif($column.htmlType == "textarea")## 文本域 + component: 'Textarea', + componentProps: { + placeholder: '请输入${comment}', + }, + #elseif($column.htmlType == "inputNumber")## 数字输入框 + component: 'InputNumber', + componentProps: { + min: 0, + class: 'w-full', + controlsPosition: 'right', + placeholder: '请输入${comment}', + }, + #end + }, + #end + #end + #end + #end + ]; +} +/** 列表的字段 */ +export function use${subSimpleClassName}GridColumns( + onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>, +): VxeTableGridOptions<${subSimpleClassName}Api.${subSimpleClassName}>['columns'] { + return [ + #foreach($column in $subColumns) + #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 + #set ($dictType = $column.dictType) + #set ($javaField = $column.javaField) + #set ($comment = $column.columnComment) + { + field: '${javaField}', + title: '${comment}', + minWidth: 120, + slots: { default: '${javaField}' }, + }, + #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 \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm index 6d25b58a82..427dbfb446 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm @@ -2,7 +2,13 @@ import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; import { useVbenModal } from '@vben/common-ui'; -import { message } from 'ant-design-vue'; +import { message, Tabs } from 'ant-design-vue'; +## 特殊:主子表专属逻辑 +#if ( $table.templateType == 10 || $table.templateType == 12 ) + #foreach ($subSimpleClassName in $subSimpleClassNames) + import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue' + #end +#end import { computed, ref } from 'vue'; import { $t } from '#/locales'; @@ -32,6 +38,18 @@ const getTitle = computed(() => { }); #end +## 特殊:主子表专属逻辑 +#if ( $table.templateType == 10 || $table.templateType == 12 ) + #if ( $subTables && $subTables.size() > 0 ) + + /** 子表的表单 */ + const subTabsName = ref('$subClassNameVars.get(0)') + #foreach ($subClassNameVar in $subClassNameVars) + const ${subClassNameVar}FormRef = ref() + #end + #end +#end + const [Form, formApi] = useVbenForm({ layout: 'horizontal', schema: useFormSchema(), @@ -44,9 +62,36 @@ const [Modal, modalApi] = useVbenModal({ if (!valid) { return; } + ## 特殊:主子表专属逻辑 + #if ( $table.templateType == 10 || $table.templateType == 12 ) + #if ( $subTables && $subTables.size() > 0 ) + // 校验子表单 + #foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subClassNameVar = $subClassNameVars.get($index)) + try { + await ${subClassNameVar}FormRef.value.validate() + } catch (e) { + subTabsName.value = '${subClassNameVar}' + return + } + #end + #end + #end modalApi.lock(); // 提交表单 const data = (await formApi.getValues()) as ${simpleClassName}Api.${simpleClassName}; + ## 特殊:主子表专属逻辑 + #if ( $table.templateType == 10 || $table.templateType == 12 ) + #if ( $subTables && $subTables.size() > 0 ) + // 拼接子表的数据 + #foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subClassNameVar = $subClassNameVars.get($index)) + data.${subClassNameVar}#if ( $subTable.subJoinMany)s#end = ${subClassNameVar}FormRef.value.getData() + #end + #end + #end try { await (formData.value?.id ? update${simpleClassName}(data) : create${simpleClassName}(data)); // 关闭并提示 @@ -90,5 +135,20 @@ const [Modal, modalApi] = useVbenModal({ diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm new file mode 100644 index 0000000000..81cd9775eb --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm @@ -0,0 +1,204 @@ +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 + + \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm new file mode 100644 index 0000000000..d8542c3d5c --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm @@ -0,0 +1,2 @@ +## 主表的 normal 和 inner 使用相同的 form 表单 +#parse("codegen/vue3/views/components/form_sub_normal.vue.vm") \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm new file mode 100644 index 0000000000..3fa1effb2d --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm @@ -0,0 +1,360 @@ +#set ($subTable = $subTables.get($subIndex))##当前表 +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写 + + \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm new file mode 100644 index 0000000000..3f0710e01c --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm @@ -0,0 +1,184 @@ +#set ($subTable = $subTables.get($subIndex))##当前表 +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 +#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写 + + \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm new file mode 100644 index 0000000000..3fe648892a --- /dev/null +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm @@ -0,0 +1,4 @@ +## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点: +## 1)inner 使用 list 不分页,erp 使用 page 分页 +## 2)erp 支持单个子表的新增、修改、删除,inner 不支持 +#parse("codegen/vue3/views/components/list_sub_erp.vue.vm") \ No newline at end of file From 1f7f06549fbafed9407f2769e3a443f1549d6c40 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 16 Apr 2025 22:29:32 +0800 Subject: [PATCH 010/134] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90:=20vue3?= =?UTF-8?q?=5Fvben5=5Fantd=20schema=20=E4=B8=BB=E5=AD=90=E8=A1=A8=20api=20?= =?UTF-8?q?=E5=92=8C=20data.ts=20=E6=A8=A1=E6=9D=BF=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vue3_vben5_antd/schema/api/api.ts.vm | 12 +- .../vue3_vben5_antd/schema/views/data.ts.vm | 194 +++++++++++++++++- 2 files changed, 196 insertions(+), 10 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm index 843f2fd2f6..1565ab9cd7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm @@ -108,31 +108,31 @@ export function export${simpleClassName}(params: any) { #if ( $table.templateType == 11 ) /** 获得${subTable.classComment}分页 */ export function get${subSimpleClassName}Page(params: PageParam) { - return requestClient.get>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params }); + return requestClient.get>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params }); } ## 情况二:非 MASTER_ERP 时,需要列表查询子表 #else #if ( $subTable.subJoinMany ) /** 获得${subTable.classComment}列表 */ export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) { - return requestClient.get<${simpleClassName}Api.${simpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); + return requestClient.get<${simpleClassName}Api.${subSimpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); } #else /** 获得${subTable.classComment} */ export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) { - return requestClient.get<${simpleClassName}Api.${simpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); + return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); } #end #end ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作 #if ( $table.templateType == 11 ) /** 新增${subTable.classComment} */ -export function create${subSimpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) { +export function create${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) { return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data); } /** 修改${subTable.classComment} */ -export function update${subSimpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) { +export function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) { return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data); } @@ -143,7 +143,7 @@ export function delete${subSimpleClassName}(id: number) { /** 获得${subTable.classComment} */ export function get${subSimpleClassName}(id: number) { - return requestClient.get<${simpleClassName}Api.${simpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`); + return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`); } #end #end diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm index cda2e5c244..c68fc9e7cc 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm @@ -277,7 +277,7 @@ export function useGridColumns( ]; } -// TODO @puhui999: 标准模式和内嵌模式时,主子关系一对一则生成表单schema,一对多则生成列表schema(内嵌模式时表单schema也要生成)。erp 模式时都生成 +## 标准模式和内嵌模式时,主子关系一对一则生成表单schema,一对多则生成列表schema(内嵌模式时表单schema也要生成)。erp 模式时都生成 ## 特殊:主子表专属逻辑 #foreach ($subTable in $subTables) #set ($index = $foreach.count - 1) @@ -285,6 +285,7 @@ export function useGridColumns( #set ($subSimpleClassName = $subSimpleClassNames.get($index)) #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段 // ==================== 子表($subTable.classComment) ==================== +#if ($table.templateType == 11) ## erp 情况 /** 新增/修改的表单 */ export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { return [ @@ -402,7 +403,7 @@ export function use${subSimpleClassName}GridColumns( ): VxeTableGridOptions<${subSimpleClassName}Api.${subSimpleClassName}>['columns'] { return [ #foreach($column in $subColumns) - #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 + #if ($column.listOperationResult) #set ($dictType = $column.dictType) #set ($javaField = $column.javaField) #set ($comment = $column.columnComment) @@ -410,14 +411,24 @@ export function use${subSimpleClassName}GridColumns( field: '${javaField}', title: '${comment}', minWidth: 120, - slots: { default: '${javaField}' }, + #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: 60, + minWidth: 200, align: 'center', fixed: 'right', headerAlign: 'center', @@ -430,13 +441,188 @@ export function use${subSimpleClassName}GridColumns( }, 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 }, ], }, }, ]; } +#else + #if ($subTable.subJoinMany) ## 一对多 + /** 列表的字段 */ + export function use${subSimpleClassName}GridColumns( + onActionClick?: OnActionClickFn<${simpleClassName}Api.${subSimpleClassName}>, + ): VxeTableGridOptions<${subSimpleClassName}Api.${subSimpleClassName}>['columns'] { + return [ + #foreach($column in $subColumns) + #if (!$column.primaryKey && $column.listOperationResult && $column.id != $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 + #set ($dictType = $column.dictType) + #set ($javaField = $column.javaField) + #set ($comment = $column.columnComment) + { + field: '${javaField}', + title: '${comment}', + minWidth: 120, + slots: { default: '${javaField}' }, + }, + #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']), + }, + ], + }, + }, + ]; + } + #else + /** 新增/修改的表单 */ + export function use${subSimpleClassName}FormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + #foreach($column in $subColumns) + #if ($column.createOperation || $column.updateOperation) + #if (!$column.primaryKey && ($table.templateType != 2 || ($table.templateType == 2 && $column.id != $treeParentColumn.id)))## 树表中已经添加了父ID字段,这里排除 + #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 + #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 + #else + { + fieldName: '${javaField}', + label: '${comment}', + #if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键 + rules: 'required', + #end + #if ($column.htmlType == "input") + component: 'Input', + componentProps: { + placeholder: '请输入${comment}', + }, + #elseif($column.htmlType == "imageUpload")## 图片上传 + component: 'FileUpload', + componentProps: { + fileType: 'image', + maxCount: 1, + }, + #elseif($column.htmlType == "fileUpload")## 文件上传 + component: 'FileUpload', + componentProps: { + fileType: 'file', + maxCount: 1, + }, + #elseif($column.htmlType == "editor")## 文本编辑器 + component: 'Editor', + #elseif($column.htmlType == "select")## 下拉框 + component: 'Select', + componentProps: { + #if ("" != $dictType)## 有数据字典 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), + #else##没数据字典 + options: [], + #end + placeholder: '请选择${comment}', + class: 'w-full', + }, + #elseif($column.htmlType == "checkbox")## 多选框 + component: 'Checkbox', + componentProps: { + #if ("" != $dictType)## 有数据字典 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), + #else##没数据字典 + options: [], + #end + }, + #elseif($column.htmlType == "radio")## 单选框 + component: 'RadioGroup', + componentProps: { + #if ("" != $dictType)## 有数据字典 + options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'), + #else##没数据字典 + options: [], + #end + buttonStyle: 'solid', + optionType: 'button', + }, + #elseif($column.htmlType == "datetime")## 时间框 + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + }, + #elseif($column.htmlType == "textarea")## 文本域 + component: 'Textarea', + componentProps: { + placeholder: '请输入${comment}', + }, + #elseif($column.htmlType == "inputNumber")## 数字输入框 + component: 'InputNumber', + componentProps: { + min: 0, + class: 'w-full', + controlsPosition: 'right', + placeholder: '请输入${comment}', + }, + #end + }, + #end + #end + #end + #end + ]; + } + #end +#end #end \ No newline at end of file From da248cd12642c7af0f22777750288aa538568e7e Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 16 Apr 2025 23:53:33 +0800 Subject: [PATCH 011/134] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90:=20vue3?= =?UTF-8?q?=5Fvben5=5Fantd=20schema=20=E4=B8=BB=E5=AD=90=E8=A1=A8=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E4=BC=98=E5=8C=96=EF=BC=88=E6=A0=87=E5=87=86=E5=92=8C?= =?UTF-8?q?=E5=86=85=E5=B5=8C=E6=A8=A1=E5=BC=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vue3_vben5_antd/schema/views/data.ts.vm | 18 +++ .../vue3_vben5_antd/schema/views/form.vue.vm | 128 +++++++++++++++--- 2 files changed, 130 insertions(+), 16 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm index c68fc9e7cc..12f6de370b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm @@ -478,11 +478,29 @@ export function use${subSimpleClassName}GridColumns( #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 diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm index 427dbfb446..88156fb95e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm @@ -2,13 +2,7 @@ import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; import { useVbenModal } from '@vben/common-ui'; -import { message, Tabs } from 'ant-design-vue'; -## 特殊:主子表专属逻辑 -#if ( $table.templateType == 10 || $table.templateType == 12 ) - #foreach ($subSimpleClassName in $subSimpleClassNames) - import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue' - #end -#end +import { message, Tabs, Checkbox, Input, Textarea, Select,RadioGroup,CheckboxGroup, DatePicker } from 'ant-design-vue'; import { computed, ref } from 'vue'; import { $t } from '#/locales'; @@ -16,6 +10,20 @@ import { useVbenForm } from '#/adapter/form'; import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${simpleClassName_strikeCase}'; import { useFormSchema } from '../data'; +#if ( $table.templateType == 10 || $table.templateType == 12 ) +#if ( $subTables && $subTables.size() > 0 ) +#foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subSimpleClassName = $subSimpleClassNames.get($index)) + #if ($subTable.subJoinMany) ## 一对多 +import { useVbenVxeGrid } from '#/adapter/vxe-table'; +import { use${subSimpleClassName}GridColumns } from '../data'; + #else +import { use${subSimpleClassName}FormSchema } from '../data'; + #end +#end +#end +#end const emit = defineEmits(['success']); const formData = ref<${simpleClassName}Api.${simpleClassName}>(); @@ -40,15 +48,44 @@ const getTitle = computed(() => { ## 特殊:主子表专属逻辑 #if ( $table.templateType == 10 || $table.templateType == 12 ) - #if ( $subTables && $subTables.size() > 0 ) - - /** 子表的表单 */ - const subTabsName = ref('$subClassNameVars.get(0)') - #foreach ($subClassNameVar in $subClassNameVars) - const ${subClassNameVar}FormRef = ref() - #end +#if ( $subTables && $subTables.size() > 0 ) +/** 子表的表单 */ +const subTabsName = ref('$subClassNameVars.get(0)') +## 特殊:主子表专属逻辑 +#foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subSimpleClassName = $subSimpleClassNames.get($index)) + #set ($subClassNameVar = $subClassNameVars.get($index)) + #if ($subTable.subJoinMany) ## 一对多 +/** {$subTable.classComment}表格配置 */ +const [${subSimpleClassName}Grid, {$subClassNameVar}GridApi] = useVbenVxeGrid({ + gridOptions: { + columns: use${subSimpleClassName}GridColumns(), + border: true, + showOverflow: true, + autoResize: true, + keepSource: true, + rowConfig: { + keyField: 'id', + }, + pagerConfig: { + enabled: false, + }, + toolbarConfig: { + enabled: false, + }, + }, +}); + #else +const [${subSimpleClassName}Form, {$subClassNameVar}FormApi] = useVbenForm({ + layout: 'horizontal', + schema: useFormSchema(), + showDefaultActions: false +}); #end #end +#end +#end const [Form, formApi] = useVbenForm({ layout: 'horizontal', @@ -56,6 +93,7 @@ const [Form, formApi] = useVbenForm({ showDefaultActions: false }); +// TODO @puhui999: 处理完成主子表标准模式和内嵌模式下的表单提交 const [Modal, modalApi] = useVbenModal({ async onConfirm() { const { valid } = await formApi.validate(); @@ -143,9 +181,67 @@ const [Modal, modalApi] = useVbenModal({ #set ($index = $foreach.count - 1) #set ($subClassNameVar = $subClassNameVars.get($index)) #set ($subSimpleClassName = $subSimpleClassNames.get($index)) - #set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index)) + #set ($subColumns = $subColumnsList.get($index))##当前字段数组 + #set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段 - <${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" /> + #if ($subTable.subJoinMany) ## 一对多 + <${subSimpleClassName}Grid class="mx-4"> + #foreach($column in $subColumns) + #if ($column.createOperation || $column.updateOperation) + #set ($javaField = $column.javaField) + #if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写 + #elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里 + + #elseif($column.htmlType == "imageUpload")## 图片上传 + + #elseif($column.htmlType == "fileUpload")## 文件上传 + + #elseif($column.htmlType == "editor")## 文本编辑器 +