1、结构调整

2、抽象工厂优化
This commit is contained in:
2026-03-31 19:35:21 +08:00
parent 87757b352c
commit ebdbdbeb41
667 changed files with 1240 additions and 50173 deletions

View File

@@ -1,23 +0,0 @@
package com.njcn.msgpush.module.infra;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 项目的启动类
* <p>
*
* @author hongawen
*/
@EnableFeignClients
@SpringBootApplication
public class InfraServerApplication {
public static void main(String[] args) {
SpringApplication.run(InfraServerApplication.class, args);
}
}

View File

@@ -1,31 +0,0 @@
package com.njcn.msgpush.module.infra.api.file;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.module.infra.api.file.dto.FileCreateReqDTO;
import com.njcn.msgpush.module.infra.service.file.FileService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class FileApiImpl implements FileApi {
@Resource
private FileService fileService;
@Override
public CommonResult<String> createFile(FileCreateReqDTO createReqDTO) {
return success(fileService.createFile(createReqDTO.getContent(), createReqDTO.getName(),
createReqDTO.getDirectory(), createReqDTO.getType()));
}
@Override
public CommonResult<String> presignGetUrl(String url, Integer expirationSeconds) {
return success(fileService.presignGetUrl(url, expirationSeconds));
}
}

View File

@@ -1,36 +0,0 @@
package com.njcn.msgpush.module.infra.api.websocket;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.websocket.core.sender.WebSocketMessageSender;
import com.njcn.msgpush.module.infra.api.websocket.dto.WebSocketSendReqDTO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import jakarta.annotation.Resource;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@RestController // 提供 RESTful API 接口,给 Feign 调用
@Validated
public class WebSocketSenderApiImpl implements WebSocketSenderApi {
@Resource
private WebSocketMessageSender webSocketMessageSender;
@Override
public CommonResult<Boolean> send(WebSocketSendReqDTO message) {
if (StrUtil.isNotEmpty(message.getSessionId())) {
webSocketMessageSender.send(message.getSessionId(),
message.getMessageType(), message.getMessageContent());
} else if (message.getUserType() != null && message.getUserId() != null) {
webSocketMessageSender.send(message.getUserType(), message.getUserId(),
message.getMessageType(), message.getMessageContent());
} else if (message.getUserType() != null) {
webSocketMessageSender.send(message.getUserType(),
message.getMessageType(), message.getMessageContent());
}
return success(true);
}
}

View File

@@ -1,160 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import com.njcn.msgpush.module.infra.convert.codegen.CodegenConvert;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.njcn.msgpush.module.infra.service.codegen.CodegenService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
import static com.njcn.msgpush.framework.security.core.util.SecurityFrameworkUtils.getLoginUserNickname;
import static com.njcn.msgpush.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
@Tag(name = "管理后台 - 代码生成器")
@RestController
@RequestMapping("/infra/codegen")
@Validated
public class CodegenController {
@Resource
private CodegenService codegenService;
@GetMapping("/db/table/list")
@Operation(summary = "获得数据库自带的表定义列表", description = "会过滤掉已经导入 Codegen 的表")
@Parameters({
@Parameter(name = "dataSourceConfigId", description = "数据源配置的编号", required = true, example = "1"),
@Parameter(name = "name", description = "表名,模糊匹配", example = "msgpush"),
@Parameter(name = "comment", description = "描述,模糊匹配", example = "灿能")
})
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<List<DatabaseTableRespVO>> getDatabaseTableList(
@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId,
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "comment", required = false) String comment) {
return success(codegenService.getDatabaseTableList(dataSourceConfigId, name, comment));
}
@GetMapping("/table/list")
@Operation(summary = "获得表定义列表")
@Parameter(name = "dataSourceConfigId", description = "数据源配置的编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<List<CodegenTableRespVO>> getCodegenTableList(@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId) {
List<CodegenTableDO> list = codegenService.getCodegenTableList(dataSourceConfigId);
return success(BeanUtils.toBean(list, CodegenTableRespVO.class));
}
@GetMapping("/table/page")
@Operation(summary = "获得表定义分页")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<PageResult<CodegenTableRespVO>> getCodegenTablePage(@Valid CodegenTablePageReqVO pageReqVO) {
PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, CodegenTableRespVO.class));
}
@GetMapping("/detail")
@Operation(summary = "获得表和字段的明细")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
public CommonResult<CodegenDetailRespVO> getCodegenDetail(@RequestParam("tableId") Long tableId) {
CodegenTableDO table = codegenService.getCodegenTable(tableId);
List<CodegenColumnDO> columns = codegenService.getCodegenColumnListByTableId(tableId);
// 拼装返回
return success(CodegenConvert.INSTANCE.convert(table, columns));
}
@Operation(summary = "基于数据库的表结构,创建代码生成器的表和字段定义")
@PostMapping("/create-list")
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
public CommonResult<List<Long>> createCodegenList(@Valid @RequestBody CodegenCreateListReqVO reqVO) {
return success(codegenService.createCodegenList(getLoginUserNickname(), reqVO));
}
@Operation(summary = "更新数据库的表和字段定义")
@PutMapping("/update")
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
public CommonResult<Boolean> updateCodegen(@Valid @RequestBody CodegenUpdateReqVO updateReqVO) {
codegenService.updateCodegen(updateReqVO);
return success(true);
}
@Operation(summary = "基于数据库的表结构,同步数据库的表和字段定义")
@PutMapping("/sync-from-db")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
public CommonResult<Boolean> syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
codegenService.syncCodegenFromDB(tableId);
return success(true);
}
@Operation(summary = "删除数据库的表和字段定义")
@DeleteMapping("/delete")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
public CommonResult<Boolean> deleteCodegen(@RequestParam("tableId") Long tableId) {
codegenService.deleteCodegen(tableId);
return success(true);
}
@Operation(summary = "批量删除数据库的表和字段定义")
@DeleteMapping("/delete-list")
@Parameter(name = "tableIds", description = "表编号列表", required = true)
@PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
public CommonResult<Boolean> deleteCodegenList(@RequestParam("tableIds") List<Long> tableIds) {
codegenService.deleteCodegenList(tableIds);
return success(true);
}
@Operation(summary = "预览生成代码")
@GetMapping("/preview")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:preview')")
public CommonResult<List<CodegenPreviewRespVO>> previewCodegen(@RequestParam("tableId") Long tableId) {
Map<String, String> codes = codegenService.generationCodes(tableId);
return success(CodegenConvert.INSTANCE.convert(codes));
}
@Operation(summary = "下载生成代码")
@GetMapping("/download")
@Parameter(name = "tableId", description = "表编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:codegen:download')")
public void downloadCodegen(@RequestParam("tableId") Long tableId,
HttpServletResponse response) throws IOException {
// 生成代码
Map<String, String> codes = codegenService.generationCodes(tableId);
// 构建 zip 包
String[] paths = codes.keySet().toArray(new String[0]);
ByteArrayInputStream[] ins = codes.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipUtil.zip(outputStream, paths, ins);
// 输出
writeAttachment(response, "codegen.zip", outputStream.toByteArray());
}
}

View File

@@ -1,21 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 基于数据库的表结构,创建代码生成器的表和字段定义 Request VO")
@Data
public class CodegenCreateListReqVO {
@Schema(description = "数据源配置的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "数据源配置的编号不能为空")
private Long dataSourceConfigId;
@Schema(description = "表名数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3]")
@NotNull(message = "表名数组不能为空")
private List<String> tableNames;
}

View File

@@ -1,20 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 代码生成表和字段的明细 Response VO")
@Data
public class CodegenDetailRespVO {
@Schema(description = "表定义")
private CodegenTableRespVO table;
@Schema(description = "字段定义")
private List<CodegenColumnRespVO> columns;
}

View File

@@ -1,16 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 代码生成预览 Response VO注意每个文件都是一个该对象")
@Data
public class CodegenPreviewRespVO {
@Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "java/com.njcn.msgpush/adminserver/modules/system/controller/test/SysTestDemoController.java")
private String filePath;
@Schema(description = "代码", requiredMode = Schema.RequiredMode.REQUIRED, example = "Hello World")
private String code;
}

View File

@@ -1,24 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTableSaveReqVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 代码生成表和字段的修改 Request VO")
@Data
public class CodegenUpdateReqVO {
@Valid // 校验内嵌的字段
@NotNull(message = "表定义不能为空")
private CodegenTableSaveReqVO table;
@Valid // 校验内嵌的字段
@NotNull(message = "字段定义不能为空")
private List<CodegenColumnSaveReqVO> columns;
}

View File

@@ -1,69 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo.column;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 代码生成字段定义 Response VO")
@Data
public class CodegenColumnRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long tableId;
@Schema(description = "字段名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_age")
private String columnName;
@Schema(description = "字段类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "int(11)")
private String dataType;
@Schema(description = "字段描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "年龄")
private String columnComment;
@Schema(description = "是否允许为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean nullable;
@Schema(description = "是否主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean primaryKey;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer ordinalPosition;
@Schema(description = "Java 属性类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "userAge")
private String javaType;
@Schema(description = "Java 属性名", requiredMode = Schema.RequiredMode.REQUIRED, example = "Integer")
private String javaField;
@Schema(description = "字典类型", example = "sys_gender")
private String dictType;
@Schema(description = "数据示例", example = "1024")
private String example;
@Schema(description = "是否为 Create 创建操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean createOperation;
@Schema(description = "是否为 Update 更新操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean updateOperation;
@Schema(description = "是否为 List 查询操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean listOperation;
@Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
private String listOperationCondition;
@Schema(description = "是否为 List 查询操作的返回字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean listOperationResult;
@Schema(description = "显示类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "input")
private String htmlType;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -1,81 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo.column;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 代码生成字段定义创建/修改 Request VO")
@Data
public class CodegenColumnSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "表编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "表编号不能为空")
private Long tableId;
@Schema(description = "字段名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_age")
@NotNull(message = "字段名不能为空")
private String columnName;
@Schema(description = "字段类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "int(11)")
@NotNull(message = "字段类型不能为空")
private String dataType;
@Schema(description = "字段描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "年龄")
@NotNull(message = "字段描述不能为空")
private String columnComment;
@Schema(description = "是否允许为空", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否允许为空不能为空")
private Boolean nullable;
@Schema(description = "是否主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否主键不能为空")
private Boolean primaryKey;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "排序不能为空")
private Integer ordinalPosition;
@Schema(description = "Java 属性类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "userAge")
@NotNull(message = "Java 属性类型不能为空")
private String javaType;
@Schema(description = "Java 属性名", requiredMode = Schema.RequiredMode.REQUIRED, example = "Integer")
@NotNull(message = "Java 属性名不能为空")
private String javaField;
@Schema(description = "字典类型", example = "sys_gender")
private String dictType;
@Schema(description = "数据示例", example = "1024")
private String example;
@Schema(description = "是否为 Create 创建操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否为 Create 创建操作的字段不能为空")
private Boolean createOperation;
@Schema(description = "是否为 Update 更新操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否为 Update 更新操作的字段不能为空")
private Boolean updateOperation;
@Schema(description = "是否为 List 查询操作的字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否为 List 查询操作的字段不能为空")
private Boolean listOperation;
@Schema(description = "List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "LIKE")
@NotNull(message = "List 查询操作的条件类型不能为空")
private String listOperationCondition;
@Schema(description = "是否为 List 查询操作的返回字段", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否为 List 查询操作的返回字段不能为空")
private Boolean listOperationResult;
@Schema(description = "显示类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "input")
@NotNull(message = "显示类型不能为空")
private String htmlType;
}

View File

@@ -1,29 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.njcn.msgpush.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 表定义分页 Request VO")
@Data
public class CodegenTablePageReqVO extends PageParam {
@Schema(description = "表名称,模糊匹配", example = "msgpush")
private String tableName;
@Schema(description = "表描述,模糊匹配", example = "灿能")
private String tableComment;
@Schema(description = "实体,模糊匹配", example = "Msgpush")
private String className;
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -1,72 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 代码生成表定义 Response VO")
@Data
public class CodegenTableRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer scene;
@Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
private String tableName;
@Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
private String tableComment;
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "模块名", requiredMode = Schema.RequiredMode.REQUIRED, example = "system")
private String moduleName;
@Schema(description = "业务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "codegen")
private String businessName;
@Schema(description = "类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CodegenTable")
private String className;
@Schema(description = "类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "代码生成器的表定义")
private String classComment;
@Schema(description = "作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能源码")
private String author;
@Schema(description = "模板类型,参见 CodegenTemplateTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer templateType;
@Schema(description = "前端类型,参见 CodegenFrontTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer frontType;
@Schema(description = "父菜单编号", example = "1024")
private Long parentMenuId;
@Schema(description = "主表的编号", example = "2048")
private Long masterTableId;
@Schema(description = "子表关联主表的字段编号", example = "4096")
private Long subJoinColumnId;
@Schema(description = "主表与子表是否一对多", example = "4096")
private Boolean subJoinMany;
@Schema(description = "树表的父字段编号", example = "8192")
private Long treeParentColumnId;
@Schema(description = "树表的名字字段编号", example = "16384")
private Long treeNameColumnId;
@Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer dataSourceConfigId;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime updateTime;
}

View File

@@ -1,100 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table;
import cn.hutool.core.util.ObjectUtil;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenSceneEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 代码生成表定义创建/修改 Response VO")
@Data
public class CodegenTableSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "生成场景,参见 CodegenSceneEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "导入类型不能为空")
private Integer scene;
@Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "msgpush")
@NotNull(message = "表名称不能为空")
private String tableName;
@Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能")
@NotNull(message = "表描述不能为空")
private String tableComment;
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "模块名", requiredMode = Schema.RequiredMode.REQUIRED, example = "system")
@NotNull(message = "模块名不能为空")
private String moduleName;
@Schema(description = "业务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "codegen")
@NotNull(message = "业务名不能为空")
private String businessName;
@Schema(description = "类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "CodegenTable")
@NotNull(message = "类名称不能为空")
private String className;
@Schema(description = "类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "代码生成器的表定义")
@NotNull(message = "类描述不能为空")
private String classComment;
@Schema(description = "作者", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能源码")
@NotNull(message = "作者不能为空")
private String author;
@Schema(description = "模板类型,参见 CodegenTemplateTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模板类型不能为空")
private Integer templateType;
@Schema(description = "前端类型,参见 CodegenFrontTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
@NotNull(message = "前端类型不能为空")
private Integer frontType;
@Schema(description = "父菜单编号", example = "1024")
private Long parentMenuId;
@Schema(description = "主表的编号", example = "2048")
private Long masterTableId;
@Schema(description = "子表关联主表的字段编号", example = "4096")
private Long subJoinColumnId;
@Schema(description = "主表与子表是否一对多", example = "4096")
private Boolean subJoinMany;
@Schema(description = "树表的父字段编号", example = "8192")
private Long treeParentColumnId;
@Schema(description = "树表的名字字段编号", example = "16384")
private Long treeNameColumnId;
@AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
@JsonIgnore
public boolean isParentMenuIdValid() {
// 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
|| getParentMenuId() != null;
}
@AssertTrue(message = "关联的父表信息不全")
@JsonIgnore
public boolean isSubValid() {
return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)
|| (ObjectUtil.isAllNotEmpty(masterTableId, subJoinColumnId, subJoinMany));
}
@AssertTrue(message = "关联的树表信息不全")
@JsonIgnore
public boolean isTreeValid() {
return ObjectUtil.notEqual(templateType, CodegenTemplateTypeEnum.TREE)
|| (ObjectUtil.isAllNotEmpty(treeParentColumnId, treeNameColumnId));
}
}

View File

@@ -1,16 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 数据库的表定义 Response VO")
@Data
public class DatabaseTableRespVO {
@Schema(description = "表名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yuanma")
private String name;
@Schema(description = "表描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "灿能源码")
private String comment;
}

View File

@@ -1,81 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.db;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.infra.controller.admin.db.vo.DataSourceConfigRespVO;
import com.njcn.msgpush.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;
import com.njcn.msgpush.module.infra.dal.dataobject.db.DataSourceConfigDO;
import com.njcn.msgpush.module.infra.service.db.DataSourceConfigService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.njcn.msgpush.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 数据源配置")
@RestController
@RequestMapping("/infra/data-source-config")
@Validated
public class DataSourceConfigController {
@Resource
private DataSourceConfigService dataSourceConfigService;
@PostMapping("/create")
@Operation(summary = "创建数据源配置")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:create')")
public CommonResult<Long> createDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO createReqVO) {
return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新数据源配置")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
public CommonResult<Boolean> updateDataSourceConfig(@Valid @RequestBody DataSourceConfigSaveReqVO updateReqVO) {
dataSourceConfigService.updateDataSourceConfig(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除数据源配置")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam("id") Long id) {
dataSourceConfigService.deleteDataSourceConfig(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除数据源配置")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
public CommonResult<Boolean> deleteDataSourceConfigList(@RequestParam("ids") List<Long> ids) {
dataSourceConfigService.deleteDataSourceConfigList(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得数据源配置")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam("id") Long id) {
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(id);
return success(BeanUtils.toBean(config, DataSourceConfigRespVO.class));
}
@GetMapping("/list")
@Operation(summary = "获得数据源配置列表")
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
return success(BeanUtils.toBean(list, DataSourceConfigRespVO.class));
}
}

View File

@@ -1,27 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 数据源配置 Response VO")
@Data
public class DataSourceConfigRespVO {
@Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
private String name;
@Schema(description = "数据源连接", requiredMode = Schema.RequiredMode.REQUIRED, example = "jdbc:mysql://192.168.1.22:13306/msgpush")
private String url;
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "root")
private String username;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -1,31 +0,0 @@
package com.njcn.msgpush.module.infra.controller.admin.db.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - 数据源配置创建/修改 Request VO")
@Data
public class DataSourceConfigSaveReqVO {
@Schema(description = "主键编号", example = "1024")
private Long id;
@Schema(description = "数据源名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "test")
@NotNull(message = "数据源名称不能为空")
private String name;
@Schema(description = "数据源连接", requiredMode = Schema.RequiredMode.REQUIRED, example = "jdbc:mysql://192.168.1.22:13306/msgpush")
@NotNull(message = "数据源连接不能为空")
private String url;
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "root")
@NotNull(message = "用户名不能为空")
private String username;
@Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456")
@NotNull(message = "密码不能为空")
private String password;
}

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package com.njcn.msgpush.module.infra.controller.app;

View File

@@ -1,6 +0,0 @@
/**
* 提供 RESTful API 给前端:
* 1. admin 包:提供给管理后台 msgpush-ui-admin 前端项目
* 2. app 包:提供给用户 APP msgpush-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
*/
package com.njcn.msgpush.module.infra.controller;

View File

@@ -1,68 +0,0 @@
package com.njcn.msgpush.module.infra.convert.codegen;
import com.njcn.msgpush.framework.common.util.collection.CollectionUtils;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import org.apache.ibatis.type.JdbcType;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
@Mapper
public interface CodegenConvert {
CodegenConvert INSTANCE = Mappers.getMapper(CodegenConvert.class);
// ========== TableInfo 相关 ==========
@Mappings({
@Mapping(source = "name", target = "tableName"),
@Mapping(source = "comment", target = "tableComment"),
})
CodegenTableDO convert(TableInfo bean);
List<CodegenColumnDO> convertList(List<TableField> list);
@Mappings({
@Mapping(source = "name", target = "columnName"),
@Mapping(source = "metaInfo.jdbcType", target = "dataType", qualifiedByName = "getDataType"),
@Mapping(source = "comment", target = "columnComment"),
@Mapping(source = "metaInfo.nullable", target = "nullable"),
@Mapping(source = "keyFlag", target = "primaryKey"),
@Mapping(source = "columnType.type", target = "javaType"),
@Mapping(source = "propertyName", target = "javaField"),
})
CodegenColumnDO convert(TableField bean);
@Named("getDataType")
default String getDataType(JdbcType jdbcType) {
return jdbcType.name();
}
// ========== 其它 ==========
default CodegenDetailRespVO convert(CodegenTableDO table, List<CodegenColumnDO> columns) {
CodegenDetailRespVO respVO = new CodegenDetailRespVO();
respVO.setTable(BeanUtils.toBean(table, CodegenTableRespVO.class));
respVO.setColumns(BeanUtils.toBean(columns, CodegenColumnRespVO.class));
return respVO;
}
default List<CodegenPreviewRespVO> convert(Map<String, String> codes) {
return CollectionUtils.convertList(codes.entrySet(),
entry -> new CodegenPreviewRespVO().setFilePath(entry.getKey()).setCode(entry.getValue()));
}
}

View File

@@ -1,6 +0,0 @@
/**
* 提供 POJO 类的实体转换
*
* 目前使用 MapStruct 框架
*/
package com.njcn.msgpush.module.infra.convert;

View File

@@ -1,133 +0,0 @@
package com.njcn.msgpush.module.infra.dal.dataobject.codegen;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenColumnListConditionEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import lombok.Data;
/**
* 代码生成 column 字段定义
*
* @author hongawen
*/
@TableName(value = "infra_codegen_column", autoResultMap = true)
@KeySequence("infra_codegen_column_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
public class CodegenColumnDO extends BaseDO {
/**
* ID 编号
*/
@TableId
private Long id;
/**
* 表编号
* <p>
* 关联 {@link CodegenTableDO#getId()}
*/
private Long tableId;
// ========== 表相关字段 ==========
/**
* 字段名
*
* 关联 {@link TableField#getName()}
*/
private String columnName;
/**
* 数据库字段类型
*
* 关联 {@link TableField.MetaInfo#getJdbcType()}
*/
private String dataType;
/**
* 字段描述
*
* 关联 {@link TableField#getComment()}
*/
private String columnComment;
/**
* 是否允许为空
*
* 关联 {@link TableField.MetaInfo#isNullable()}
*/
private Boolean nullable;
/**
* 是否主键
*
* 关联 {@link TableField#isKeyFlag()}
*/
private Boolean primaryKey;
/**
* 排序
*/
private Integer ordinalPosition;
// ========== Java 相关字段 ==========
/**
* Java 属性类型
*
* 例如说 String、Boolean 等等
*
* 关联 {@link TableField#getColumnType()}
*/
private String javaType;
/**
* Java 属性名
*
* 关联 {@link TableField#getPropertyName()}
*/
private String javaField;
/**
* 字典类型
* <p>
* 关联 DictTypeDO 的 type 属性
*/
private String dictType;
/**
* 数据示例,主要用于生成 Swagger 注解的 example 字段
*/
private String example;
// ========== CRUD 相关字段 ==========
/**
* 是否为 Create 创建操作的字段
*/
private Boolean createOperation;
/**
* 是否为 Update 更新操作的字段
*/
private Boolean updateOperation;
/**
* 是否为 List 查询操作的字段
*/
private Boolean listOperation;
/**
* List 查询操作的条件类型
* <p>
* 枚举 {@link CodegenColumnListConditionEnum}
*/
private String listOperationCondition;
/**
* 是否为 List 查询操作的返回字段
*/
private Boolean listOperationResult;
// ========== UI 相关字段 ==========
/**
* 显示类型
* <p>
* 枚举 {@link CodegenColumnHtmlTypeEnum}
*/
private String htmlType;
}

View File

@@ -1,155 +0,0 @@
package com.njcn.msgpush.module.infra.dal.dataobject.codegen;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.msgpush.module.infra.dal.dataobject.db.DataSourceConfigDO;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenFrontTypeEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenSceneEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import lombok.Data;
/**
* 代码生成 table 表定义
*
* @author hongawen
*/
@TableName(value = "infra_codegen_table", autoResultMap = true)
@KeySequence("infra_codegen_table_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
public class CodegenTableDO extends BaseDO {
/**
* ID 编号
*/
@TableId
private Long id;
/**
* 数据源编号
*
* 关联 {@link DataSourceConfigDO#getId()}
*/
private Long dataSourceConfigId;
/**
* 生成场景
*
* 枚举 {@link CodegenSceneEnum}
*/
private Integer scene;
// ========== 表相关字段 ==========
/**
* 表名称
*
* 关联 {@link TableInfo#getName()}
*/
private String tableName;
/**
* 表描述
*
* 关联 {@link TableInfo#getComment()}
*/
private String tableComment;
/**
* 备注
*/
private String remark;
// ========== 类相关字段 ==========
/**
* 模块名,即一级目录
*
* 例如说system、infra、tool 等等
*/
private String moduleName;
/**
* 业务名,即二级目录
*
* 例如说user、permission、dict 等等
*/
private String businessName;
/**
* 类名称(首字母大写)
*
* 例如说SysUser、SysMenu、SysDictData 等等
*/
private String className;
/**
* 类描述
*/
private String classComment;
/**
* 作者
*/
private String author;
// ========== 生成相关字段 ==========
/**
* 模板类型
*
* 枚举 {@link CodegenTemplateTypeEnum}
*/
private Integer templateType;
/**
* 代码生成的前端类型
*
* 枚举 {@link CodegenFrontTypeEnum}
*/
private Integer frontType;
// ========== 菜单相关字段 ==========
/**
* 父菜单编号
*
* 关联 MenuDO 的 id 属性
*/
private Long parentMenuId;
// ========== 主子表相关字段 ==========
/**
* 主表的编号
*
* 关联 {@link CodegenTableDO#getId()}
*/
private Long masterTableId;
/**
* 【自己】子表关联主表的字段编号
*
* 关联 {@link CodegenColumnDO#getId()}
*/
private Long subJoinColumnId;
/**
* 主表与子表是否一对多
*
* true一对多
* false一对一
*/
private Boolean subJoinMany;
// ========== 树表相关字段 ==========
/**
* 树表的父字段编号
*
* 关联 {@link CodegenColumnDO#getId()}
*/
private Long treeParentColumnId;
/**
* 树表的名字字段编号
*
* 名字的用途新增或修改时select 框展示的字段
*
* 关联 {@link CodegenColumnDO#getId()}
*/
private Long treeNameColumnId;
}

View File

@@ -1,49 +0,0 @@
package com.njcn.msgpush.module.infra.dal.dataobject.db;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.msgpush.framework.mybatis.core.type.EncryptTypeHandler;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* 数据源配置
*
* @author hongawen
*/
@TableName(value = "infra_data_source_config", autoResultMap = true)
@KeySequence("infra_data_source_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
public class DataSourceConfigDO extends BaseDO {
/**
* 主键编号 - Master 数据源
*/
public static final Long ID_MASTER = 0L;
/**
* 主键编号
*/
private Long id;
/**
* 连接名
*/
private String name;
/**
* 数据源连接
*/
private String url;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
@TableField(typeHandler = EncryptTypeHandler.class)
private String password;
}

View File

@@ -1,29 +0,0 @@
package com.njcn.msgpush.module.infra.dal.mysql.codegen;
import com.njcn.msgpush.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.msgpush.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface CodegenColumnMapper extends BaseMapperX<CodegenColumnDO> {
default List<CodegenColumnDO> selectListByTableId(Long tableId) {
return selectList(new LambdaQueryWrapperX<CodegenColumnDO>()
.eq(CodegenColumnDO::getTableId, tableId)
.orderByAsc(CodegenColumnDO::getOrdinalPosition));
}
default void deleteListByTableId(Long tableId) {
delete(CodegenColumnDO::getTableId, tableId);
}
default void deleteListByTableId(Collection<Long> tableIds) {
delete(new LambdaQueryWrapperX<CodegenColumnDO>()
.in(CodegenColumnDO::getTableId, tableIds));
}
}

View File

@@ -1,39 +0,0 @@
package com.njcn.msgpush.module.infra.dal.mysql.codegen;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.msgpush.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenTableDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface CodegenTableMapper extends BaseMapperX<CodegenTableDO> {
default CodegenTableDO selectByTableNameAndDataSourceConfigId(String tableName, Long dataSourceConfigId) {
return selectOne(CodegenTableDO::getTableName, tableName,
CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
default PageResult<CodegenTableDO> selectPage(CodegenTablePageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX<CodegenTableDO>()
.likeIfPresent(CodegenTableDO::getTableName, pageReqVO.getTableName())
.likeIfPresent(CodegenTableDO::getTableComment, pageReqVO.getTableComment())
.likeIfPresent(CodegenTableDO::getClassName, pageReqVO.getClassName())
.betweenIfPresent(CodegenTableDO::getCreateTime, pageReqVO.getCreateTime())
.orderByDesc(CodegenTableDO::getUpdateTime)
);
}
default List<CodegenTableDO> selectListByDataSourceConfigId(Long dataSourceConfigId) {
return selectList(CodegenTableDO::getDataSourceConfigId, dataSourceConfigId);
}
default List<CodegenTableDO> selectListByTemplateTypeAndMasterTableId(Integer templateType, Long masterTableId) {
return selectList(CodegenTableDO::getTemplateType, templateType,
CodegenTableDO::getMasterTableId, masterTableId);
}
}

View File

@@ -1,14 +0,0 @@
package com.njcn.msgpush.module.infra.dal.mysql.db;
import com.njcn.msgpush.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.msgpush.module.infra.dal.dataobject.db.DataSourceConfigDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 数据源配置 Mapper
*
* @author hongawen
*/
@Mapper
public interface DataSourceConfigMapper extends BaseMapperX<DataSourceConfigDO> {
}

View File

@@ -1,9 +0,0 @@
package com.njcn.msgpush.module.infra.framework.codegen.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(CodegenProperties.class)
public class CodegenConfiguration {
}

View File

@@ -1,58 +0,0 @@
package com.njcn.msgpush.module.infra.framework.codegen.config;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenFrontTypeEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenVOTypeEnum;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Collection;
@ConfigurationProperties(prefix = "msgpush.codegen")
@Validated
@Data
public class CodegenProperties {
/**
* 生成的 Java 代码的基础包
*/
@NotNull(message = "Java 代码的基础包不能为空")
private String basePackage;
/**
* 数据库名数组
*/
@NotEmpty(message = "数据库不能为空")
private Collection<String> dbSchemas;
/**
* 代码生成的前端类型(默认)
*
* 枚举 {@link CodegenFrontTypeEnum#getType()}
*/
@NotNull(message = "代码生成的前端类型不能为空")
private Integer frontType;
/**
* 代码生成的 VO 类型
*
* 枚举 {@link CodegenVOTypeEnum#getType()}
*/
@NotNull(message = "代码生成的 VO 类型不能为空")
private Integer voType;
/**
* 是否生成批量删除接口
*/
@NotNull(message = "是否生成批量删除接口不能为空")
private Boolean deleteBatchEnable;
/**
* 是否生成单元测试
*/
@NotNull(message = "是否生成单元测试不能为空")
private Boolean unitTestEnable;
}

View File

@@ -1,4 +0,0 @@
/**
* 代码生成器
*/
package com.njcn.msgpush.module.infra.framework.codegen;

View File

@@ -1,55 +0,0 @@
package com.njcn.msgpush.module.infra.framework.file.core.enums;
import cn.hutool.core.util.ArrayUtil;
import com.njcn.msgpush.module.infra.framework.file.core.client.FileClient;
import com.njcn.msgpush.module.infra.framework.file.core.client.FileClientConfig;
import com.njcn.msgpush.module.infra.framework.file.core.client.db.DBFileClient;
import com.njcn.msgpush.module.infra.framework.file.core.client.db.DBFileClientConfig;
import com.njcn.msgpush.module.infra.framework.file.core.client.ftp.FtpFileClient;
import com.njcn.msgpush.module.infra.framework.file.core.client.ftp.FtpFileClientConfig;
import com.njcn.msgpush.module.infra.framework.file.core.client.local.LocalFileClient;
import com.njcn.msgpush.module.infra.framework.file.core.client.local.LocalFileClientConfig;
import com.njcn.msgpush.module.infra.framework.file.core.client.s3.S3FileClient;
import com.njcn.msgpush.module.infra.framework.file.core.client.s3.S3FileClientConfig;
import com.njcn.msgpush.module.infra.framework.file.core.client.sftp.SftpFileClient;
import com.njcn.msgpush.module.infra.framework.file.core.client.sftp.SftpFileClientConfig;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 文件存储器枚举
*
* @author hongawen
*/
@AllArgsConstructor
@Getter
public enum FileStorageEnum {
DB(1, DBFileClientConfig.class, DBFileClient.class),
LOCAL(10, LocalFileClientConfig.class, LocalFileClient.class),
FTP(11, FtpFileClientConfig.class, FtpFileClient.class),
SFTP(12, SftpFileClientConfig.class, SftpFileClient.class),
S3(20, S3FileClientConfig.class, S3FileClient.class),
;
/**
* 存储器
*/
private final Integer storage;
/**
* 配置类
*/
private final Class<? extends FileClientConfig> configClass;
/**
* 客户端类
*/
private final Class<? extends FileClient> clientClass;
public static FileStorageEnum getByStorage(Integer storage) {
return ArrayUtil.firstMatch(o -> o.getStorage().equals(storage), values());
}
}

View File

@@ -1,6 +0,0 @@
/**
* 属于 infra 模块的 framework 封装
*
* @author hongawen
*/
package com.njcn.msgpush.module.infra.framework;

View File

@@ -1,9 +0,0 @@
package com.njcn.msgpush.module.infra.framework.rpc.config;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration(value = "infraRpcConfiguration", proxyBeanMethods = false)
@EnableFeignClients()
public class RpcConfiguration {
}

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package com.njcn.msgpush.module.infra.framework.rpc;

View File

@@ -1,43 +0,0 @@
package com.njcn.msgpush.module.infra.framework.security.config;
import com.njcn.msgpush.framework.security.config.AuthorizeRequestsCustomizer;
import com.njcn.msgpush.module.infra.enums.ApiConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
/**
* Infra 模块的 Security 配置
*/
@Configuration(proxyBeanMethods = false, value = "infraSecurityConfiguration")
public class SecurityConfiguration {
@Bean("infraAuthorizeRequestsCustomizer")
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
return new AuthorizeRequestsCustomizer() {
@Override
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
// Swagger 接口文档
registry.requestMatchers("/v3/api-docs/**").permitAll()
.requestMatchers("/webjars/**").permitAll()
.requestMatchers("/swagger-ui").permitAll()
.requestMatchers("/swagger-ui/**").permitAll();
// Spring Boot Actuator 的安全配置
registry.requestMatchers("/actuator").permitAll()
.requestMatchers("/actuator/**").permitAll();
// Druid 监控
registry.requestMatchers("/druid/**").permitAll();
// 文件读取
registry.requestMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll();
// TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案
// RPC 服务的安全配置
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
}
};
}
}

View File

@@ -1,4 +0,0 @@
/**
* 占位
*/
package com.njcn.msgpush.module.infra.framework.security.core;

View File

@@ -1,4 +0,0 @@
/**
* 消息队列的消费者
*/
package com.njcn.msgpush.module.infra.mq.consumer;

View File

@@ -1,4 +0,0 @@
/**
* 消息队列的消息
*/
package com.njcn.msgpush.module.infra.mq.message;

View File

@@ -1,4 +0,0 @@
/**
* 消息队列的生产者
*/
package com.njcn.msgpush.module.infra.mq.producer;

View File

@@ -1,9 +0,0 @@
/**
* infra 模块,主要提供两块能力:
* 1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
* 2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
*
* 1. Controller URL以 /infra/ 开头,避免和其它 Module 冲突
* 2. DataObject 表名:以 infra_ 开头,方便在数据库中区分
*/
package com.njcn.msgpush.module.infra;

View File

@@ -1,108 +0,0 @@
package com.njcn.msgpush.module.infra.service.codegen;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenTableDO;
import java.util.List;
import java.util.Map;
/**
* 代码生成 Service 接口
*
* @author hongawen
*/
public interface CodegenService {
/**
* 基于数据库的表结构,创建代码生成器的表定义
*
* @param author 作者
* @param reqVO 表信息
* @return 创建的表定义的编号数组
*/
List<Long> createCodegenList(String author, CodegenCreateListReqVO reqVO);
/**
* 更新数据库的表和字段定义
*
* @param updateReqVO 更新信息
*/
void updateCodegen(CodegenUpdateReqVO updateReqVO);
/**
* 基于数据库的表结构,同步数据库的表和字段定义
*
* @param tableId 表编号
*/
void syncCodegenFromDB(Long tableId);
/**
* 删除数据库的表和字段定义
*
* @param tableId 数据编号
*/
void deleteCodegen(Long tableId);
/**
* 批量删除数据库的表和字段定义
*
* @param tableIds 数据编号列表
*/
void deleteCodegenList(List<Long> tableIds);
/**
* 获得表定义列表
*
* @param dataSourceConfigId 数据源配置的编号
* @return 表定义列表
*/
List<CodegenTableDO> getCodegenTableList(Long dataSourceConfigId);
/**
* 获得表定义分页
*
* @param pageReqVO 分页条件
* @return 表定义分页
*/
PageResult<CodegenTableDO> getCodegenTablePage(CodegenTablePageReqVO pageReqVO);
/**
* 获得表定义
*
* @param id 表编号
* @return 表定义
*/
CodegenTableDO getCodegenTable(Long id);
/**
* 获得指定表的字段定义数组
*
* @param tableId 表编号
* @return 字段定义数组
*/
List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId);
/**
* 执行指定表的代码生成
*
* @param tableId 表编号
* @return 生成结果。key 为文件路径value 为对应的代码内容
*/
Map<String, String> generationCodes(Long tableId);
/**
* 获得数据库自带的表定义列表
*
* @param dataSourceConfigId 数据源的配置编号
* @param name 表名称
* @param comment 表描述
* @return 表定义列表
*/
List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment);
}

View File

@@ -1,310 +0,0 @@
package com.njcn.msgpush.module.infra.service.codegen;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
import com.njcn.msgpush.module.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.njcn.msgpush.module.infra.dal.dataobject.db.DataSourceConfigDO;
import com.njcn.msgpush.module.infra.dal.mysql.codegen.CodegenColumnMapper;
import com.njcn.msgpush.module.infra.dal.mysql.codegen.CodegenTableMapper;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenSceneEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.njcn.msgpush.module.infra.framework.codegen.config.CodegenProperties;
import com.njcn.msgpush.module.infra.service.codegen.inner.CodegenBuilder;
import com.njcn.msgpush.module.infra.service.codegen.inner.CodegenEngine;
import com.njcn.msgpush.module.infra.service.db.DataSourceConfigService;
import com.njcn.msgpush.module.infra.service.db.DatabaseTableService;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.google.common.annotations.VisibleForTesting;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static com.njcn.msgpush.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.njcn.msgpush.framework.common.util.collection.CollectionUtils.convertMap;
import static com.njcn.msgpush.framework.common.util.collection.CollectionUtils.convertSet;
import static com.njcn.msgpush.module.infra.enums.ErrorCodeConstants.*;
/**
* 代码生成 Service 实现类
*
* @author hongawen
*/
@Service
public class CodegenServiceImpl implements CodegenService {
@Resource
private DatabaseTableService databaseTableService;
@Resource
private DataSourceConfigService dataSourceConfigService;
@Resource
private CodegenTableMapper codegenTableMapper;
@Resource
private CodegenColumnMapper codegenColumnMapper;
@Resource
private CodegenBuilder codegenBuilder;
@Resource
private CodegenEngine codegenEngine;
@Resource
private CodegenProperties codegenProperties;
@Override
@Transactional(rollbackFor = Exception.class)
public List<Long> createCodegenList(String author, CodegenCreateListReqVO reqVO) {
List<Long> ids = new ArrayList<>(reqVO.getTableNames().size());
// 遍历添加。虽然效率会低一点,但是没必要做成完全批量,因为不会这么大量
reqVO.getTableNames().forEach(tableName -> ids.add(createCodegen(author, reqVO.getDataSourceConfigId(), tableName)));
return ids;
}
private Long createCodegen(String author, Long dataSourceConfigId, String tableName) {
// 从数据库中,获得数据库表结构
TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, tableName);
// 导入
return createCodegen0(author, dataSourceConfigId, tableInfo);
}
private Long createCodegen0(String author, Long dataSourceConfigId, TableInfo tableInfo) {
// 校验导入的表和字段非空
validateTableInfo(tableInfo);
// 校验是否已经存在
if (codegenTableMapper.selectByTableNameAndDataSourceConfigId(tableInfo.getName(),
dataSourceConfigId) != null) {
throw exception(CODEGEN_TABLE_EXISTS);
}
// 构建 CodegenTableDO 对象,插入到 DB 中
CodegenTableDO table = codegenBuilder.buildTable(tableInfo);
table.setDataSourceConfigId(dataSourceConfigId);
table.setScene(CodegenSceneEnum.ADMIN.getScene()); // 默认配置下,使用管理后台的模板
table.setFrontType(codegenProperties.getFrontType());
table.setAuthor(author);
codegenTableMapper.insert(table);
// 构建 CodegenColumnDO 数组,插入到 DB 中
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(table.getId(), tableInfo.getFields());
// 如果没有主键,则使用第一个字段作为主键
if (!tableInfo.isHavePrimaryKey()) {
columns.get(0).setPrimaryKey(true);
}
codegenColumnMapper.insertBatch(columns);
return table.getId();
}
@VisibleForTesting
void validateTableInfo(TableInfo tableInfo) {
if (tableInfo == null) {
throw exception(CODEGEN_IMPORT_TABLE_NULL);
}
if (StrUtil.isEmpty(tableInfo.getComment())) {
throw exception(CODEGEN_TABLE_INFO_TABLE_COMMENT_IS_NULL);
}
if (CollUtil.isEmpty(tableInfo.getFields())) {
throw exception(CODEGEN_IMPORT_COLUMNS_NULL);
}
tableInfo.getFields().forEach(field -> {
if (StrUtil.isEmpty(field.getComment())) {
throw exception(CODEGEN_TABLE_INFO_COLUMN_COMMENT_IS_NULL, field.getName());
}
});
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCodegen(CodegenUpdateReqVO updateReqVO) {
// 校验是否已经存在
if (codegenTableMapper.selectById(updateReqVO.getTable().getId()) == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 校验主表字段存在
if (Objects.equals(updateReqVO.getTable().getTemplateType(), CodegenTemplateTypeEnum.SUB.getType())) {
if (codegenTableMapper.selectById(updateReqVO.getTable().getMasterTableId()) == null) {
throw exception(CODEGEN_MASTER_TABLE_NOT_EXISTS, updateReqVO.getTable().getMasterTableId());
}
if (CollUtil.findOne(updateReqVO.getColumns(), // 关联主表的字段不存在
column -> column.getId().equals(updateReqVO.getTable().getSubJoinColumnId())) == null) {
throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, updateReqVO.getTable().getSubJoinColumnId());
}
}
// 更新 table 表定义
CodegenTableDO updateTableObj = BeanUtils.toBean(updateReqVO.getTable(), CodegenTableDO.class);
codegenTableMapper.updateById(updateTableObj);
// 更新 column 字段定义
List<CodegenColumnDO> updateColumnObjs = BeanUtils.toBean(updateReqVO.getColumns(), CodegenColumnDO.class);
updateColumnObjs.forEach(updateColumnObj -> codegenColumnMapper.updateById(updateColumnObj));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void syncCodegenFromDB(Long tableId) {
// 校验是否已经存在
CodegenTableDO table = codegenTableMapper.selectById(tableId);
if (table == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 从数据库中,获得数据库表结构
TableInfo tableInfo = databaseTableService.getTable(table.getDataSourceConfigId(), table.getTableName());
// 执行同步
syncCodegen0(tableId, tableInfo);
}
private void syncCodegen0(Long tableId, TableInfo tableInfo) {
// 1. 校验导入的表和字段非空
validateTableInfo(tableInfo);
List<TableField> tableFields = tableInfo.getFields();
// 2. 构建 CodegenColumnDO 数组,只同步新增的字段
List<CodegenColumnDO> codegenColumns = codegenColumnMapper.selectListByTableId(tableId);
Set<String> codegenColumnNames = convertSet(codegenColumns, CodegenColumnDO::getColumnName);
// 3.1 计算需要【修改】的字段,插入时重新插入,删除时将原来的删除
Map<String, CodegenColumnDO> codegenColumnDOMap = convertMap(codegenColumns, CodegenColumnDO::getColumnName);
BiPredicate<TableField, CodegenColumnDO> primaryKeyPredicate =
(tableField, codegenColumn) -> tableField.getMetaInfo().getJdbcType().name().equals(codegenColumn.getDataType())
&& tableField.getMetaInfo().isNullable() == codegenColumn.getNullable()
&& tableField.isKeyFlag() == codegenColumn.getPrimaryKey()
&& tableField.getComment().equals(codegenColumn.getColumnComment());
Set<String> modifyFieldNames = IntStream.range(0, tableFields.size()).mapToObj(index -> {
TableField tableField = tableFields.get(index);
String columnName = tableField.getColumnName();
CodegenColumnDO codegenColumn = codegenColumnDOMap.get(columnName);
if (codegenColumn == null) {
return null;
}
if (!primaryKeyPredicate.test(tableField, codegenColumn) || codegenColumn.getOrdinalPosition() != index) {
return columnName;
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toSet());
// 3.2 计算需要【删除】的字段
Set<String> tableFieldNames = convertSet(tableFields, TableField::getName);
Set<Long> deleteColumnIds = codegenColumns.stream()
.filter(column -> (!tableFieldNames.contains(column.getColumnName())) || modifyFieldNames.contains(column.getColumnName()))
.map(CodegenColumnDO::getId).collect(Collectors.toSet());
// 移除已经存在的字段
tableFields.removeIf(column -> codegenColumnNames.contains(column.getColumnName()) && (!modifyFieldNames.contains(column.getColumnName())));
if (CollUtil.isEmpty(tableFields) && CollUtil.isEmpty(deleteColumnIds)) {
throw exception(CODEGEN_SYNC_NONE_CHANGE);
}
// 4.1 插入新增的字段
List<CodegenColumnDO> columns = codegenBuilder.buildColumns(tableId, tableFields);
codegenColumnMapper.insertBatch(columns);
// 4.2 删除不存在的字段
if (CollUtil.isNotEmpty(deleteColumnIds)) {
codegenColumnMapper.deleteByIds(deleteColumnIds);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteCodegen(Long tableId) {
// 校验是否已经存在
if (codegenTableMapper.selectById(tableId) == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
// 删除 table 表定义
codegenTableMapper.deleteById(tableId);
// 删除 column 字段定义
codegenColumnMapper.deleteListByTableId(tableId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteCodegenList(List<Long> tableIds) {
// 批量删除 table 表定义
codegenTableMapper.deleteByIds(tableIds);
// 批量删除 column 字段定义
codegenColumnMapper.deleteListByTableId(tableIds);
}
@Override
public List<CodegenTableDO> getCodegenTableList(Long dataSourceConfigId) {
return codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId);
}
@Override
public PageResult<CodegenTableDO> getCodegenTablePage(CodegenTablePageReqVO pageReqVO) {
return codegenTableMapper.selectPage(pageReqVO);
}
@Override
public CodegenTableDO getCodegenTable(Long id) {
return codegenTableMapper.selectById(id);
}
@Override
public List<CodegenColumnDO> getCodegenColumnListByTableId(Long tableId) {
return codegenColumnMapper.selectListByTableId(tableId);
}
@Override
public Map<String, String> generationCodes(Long tableId) {
// 校验是否已经存在
CodegenTableDO table = codegenTableMapper.selectById(tableId);
if (table == null) {
throw exception(CODEGEN_TABLE_NOT_EXISTS);
}
List<CodegenColumnDO> columns = codegenColumnMapper.selectListByTableId(tableId);
if (CollUtil.isEmpty(columns)) {
throw exception(CODEGEN_COLUMN_NOT_EXISTS);
}
// 如果是主子表,则加载对应的子表信息
List<CodegenTableDO> subTables = null;
List<List<CodegenColumnDO>> subColumnsList = null;
if (CodegenTemplateTypeEnum.isMaster(table.getTemplateType())) {
// 校验子表存在
subTables = codegenTableMapper.selectListByTemplateTypeAndMasterTableId(
CodegenTemplateTypeEnum.SUB.getType(), tableId);
if (CollUtil.isEmpty(subTables)) {
throw exception(CODEGEN_MASTER_GENERATION_FAIL_NO_SUB_TABLE);
}
// 校验子表的关联字段存在
subColumnsList = new ArrayList<>();
for (CodegenTableDO subTable : subTables) {
List<CodegenColumnDO> subColumns = codegenColumnMapper.selectListByTableId(subTable.getId());
if (CollUtil.findOne(subColumns, column -> column.getId().equals(subTable.getSubJoinColumnId())) == null) {
throw exception(CODEGEN_SUB_COLUMN_NOT_EXISTS, subTable.getId());
}
subColumnsList.add(subColumns);
}
}
// 获取数据源对应的数据库类型
DataSourceConfigDO dataSourceConfig = dataSourceConfigService.getDataSourceConfig(table.getDataSourceConfigId());
DbType dbType = JdbcUtils.getDbType(dataSourceConfig.getUrl());
// 执行生成
return codegenEngine.execute(dbType, table, columns, subTables, subColumnsList);
}
@Override
public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
// 移除在 Codegen 中,已经存在的
Set<String> existsTables = convertSet(
codegenTableMapper.selectListByDataSourceConfigId(dataSourceConfigId), CodegenTableDO::getTableName);
tables.removeIf(table -> existsTables.contains(table.getName()));
return BeanUtils.toBean(tables, DatabaseTableRespVO.class);
}
}

View File

@@ -1,216 +0,0 @@
package com.njcn.msgpush.module.infra.service.codegen.inner;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.msgpush.module.infra.convert.codegen.CodegenConvert;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenColumnListConditionEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.*;
import static cn.hutool.core.text.CharSequenceUtil.*;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.hutool.core.util.RandomUtil.randomInt;
/**
* 代码生成器的 Builder负责
* 1. 将数据库的表 {@link TableInfo} 定义,构建成 {@link CodegenTableDO}
* 2. 将数据库的列 {@link TableField} 构定义,建成 {@link CodegenColumnDO}
*/
@Component
public class CodegenBuilder {
/**
* 字段名与 {@link CodegenColumnListConditionEnum} 的默认映射
* 注意,字段的匹配以后缀的方式
*/
private static final Map<String, CodegenColumnListConditionEnum> COLUMN_LIST_OPERATION_CONDITION_MAPPINGS =
MapUtil.<String, CodegenColumnListConditionEnum>builder()
.put("name", CodegenColumnListConditionEnum.LIKE)
.put("time", CodegenColumnListConditionEnum.BETWEEN)
.put("date", CodegenColumnListConditionEnum.BETWEEN)
.build();
/**
* 字段名与 {@link CodegenColumnHtmlTypeEnum} 的默认映射
* 注意,字段的匹配以后缀的方式
*/
private static final Map<String, CodegenColumnHtmlTypeEnum> COLUMN_HTML_TYPE_MAPPINGS =
MapUtil.<String, CodegenColumnHtmlTypeEnum>builder()
.put("status", CodegenColumnHtmlTypeEnum.RADIO)
.put("sex", CodegenColumnHtmlTypeEnum.RADIO)
.put("type", CodegenColumnHtmlTypeEnum.SELECT)
.put("image", CodegenColumnHtmlTypeEnum.IMAGE_UPLOAD)
.put("file", CodegenColumnHtmlTypeEnum.FILE_UPLOAD)
.put("content", CodegenColumnHtmlTypeEnum.EDITOR)
.put("description", CodegenColumnHtmlTypeEnum.EDITOR)
.put("demo", CodegenColumnHtmlTypeEnum.EDITOR)
.put("time", CodegenColumnHtmlTypeEnum.DATETIME)
.put("date", CodegenColumnHtmlTypeEnum.DATETIME)
.build();
/**
* {@link com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO} 的字段
*/
public static final Set<String> BASE_DO_FIELDS = new HashSet<>();
/**
* 新增操作,不需要传递的字段
*/
private static final Set<String> CREATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
/**
* 修改操作,不需要传递的字段
*/
private static final Set<String> UPDATE_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet();
/**
* 列表操作的条件,不需要传递的字段
*/
private static final Set<String> LIST_OPERATION_EXCLUDE_COLUMN = Sets.newHashSet("id");
/**
* 列表操作的结果,不需要返回的字段
*/
private static final Set<String> LIST_OPERATION_RESULT_EXCLUDE_COLUMN = Sets.newHashSet();
static {
Arrays.stream(ReflectUtil.getFields(BaseDO.class)).forEach(field -> BASE_DO_FIELDS.add(field.getName()));
// 处理 OPERATION 相关的字段
CREATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
UPDATE_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
LIST_OPERATION_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
LIST_OPERATION_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是可能需要传递的
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.addAll(BASE_DO_FIELDS);
LIST_OPERATION_RESULT_EXCLUDE_COLUMN.remove("createTime"); // 创建时间,还是需要返回的
}
public CodegenTableDO buildTable(TableInfo tableInfo) {
CodegenTableDO table = CodegenConvert.INSTANCE.convert(tableInfo);
initTableDefault(table);
return table;
}
/**
* 初始化 Table 表的默认字段
*
* @param table 表定义
*/
private void initTableDefault(CodegenTableDO table) {
// 以 system_dept 举例子。moduleName 为 system、businessName 为 dept、className 为 Dept
// 如果希望以 System 前缀,则可以手动在【代码生成 - 修改生成配置 - 基本信息】,将实体类名称改为 SystemDept 即可
String tableName = table.getTableName().toLowerCase();
// 第一步_ 前缀的前面,作为 module 名字第二步moduleName 必须小写;
table.setModuleName(subBefore(tableName, '_', false).toLowerCase());
// 第一步,第一个 _ 前缀的后面,作为 module 名字; 第二步,可能存在多个 _ 的情况,转换成驼峰; 第三步businessName 必须小写;
table.setBusinessName(toCamelCase(subAfter(tableName, '_', false)).toLowerCase());
// 驼峰 + 首字母大写;第一步,第一个 _ 前缀的后面,作为 class 名字;第二步,驼峰命名
table.setClassName(upperFirst(toCamelCase(subAfter(tableName, '_', false))));
// 去除结尾的表,作为类描述
table.setClassComment(StrUtil.removeSuffixIgnoreCase(table.getTableComment(), ""));
table.setTemplateType(CodegenTemplateTypeEnum.ONE.getType());
}
public List<CodegenColumnDO> buildColumns(Long tableId, List<TableField> tableFields) {
List<CodegenColumnDO> columns = CodegenConvert.INSTANCE.convertList(tableFields);
int index = 1;
for (CodegenColumnDO column : columns) {
column.setTableId(tableId);
column.setOrdinalPosition(index++);
// 特殊处理Byte => Integer
if (Byte.class.getSimpleName().equals(column.getJavaType())) {
column.setJavaType(Integer.class.getSimpleName());
}
// 初始化 Column 列的默认字段
processColumnOperation(column); // 处理 CRUD 相关的字段的默认值
processColumnUI(column); // 处理 UI 相关的字段的默认值
processColumnExample(column); // 处理字段的 swagger example 示例
}
return columns;
}
private void processColumnOperation(CodegenColumnDO column) {
// 处理 createOperation 字段
column.setCreateOperation(!CREATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !column.getPrimaryKey()); // 对于主键,创建时无需传递
// 处理 updateOperation 字段
column.setUpdateOperation(!UPDATE_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
|| column.getPrimaryKey()); // 对于主键,更新时需要传递
// 处理 listOperation 字段
column.setListOperation(!LIST_OPERATION_EXCLUDE_COLUMN.contains(column.getJavaField())
&& !column.getPrimaryKey()); // 对于主键,列表过滤不需要传递
// 处理 listOperationCondition 字段
COLUMN_LIST_OPERATION_CONDITION_MAPPINGS.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setListOperationCondition(entry.getValue().getCondition()));
if (column.getListOperationCondition() == null) {
column.setListOperationCondition(CodegenColumnListConditionEnum.EQ.getCondition());
}
// 处理 listOperationResult 字段
column.setListOperationResult(!LIST_OPERATION_RESULT_EXCLUDE_COLUMN.contains(column.getJavaField()));
}
private void processColumnUI(CodegenColumnDO column) {
// 基于后缀进行匹配
COLUMN_HTML_TYPE_MAPPINGS.entrySet().stream()
.filter(entry -> StrUtil.endWithIgnoreCase(column.getJavaField(), entry.getKey()))
.findFirst().ifPresent(entry -> column.setHtmlType(entry.getValue().getType()));
// 如果是 Boolean 类型时,设置为 radio 类型.
if (Boolean.class.getSimpleName().equals(column.getJavaType())) {
column.setHtmlType(CodegenColumnHtmlTypeEnum.RADIO.getType());
}
// 如果是 LocalDateTime 类型,则设置为 datetime 类型
if (LocalDateTime.class.getSimpleName().equals(column.getJavaType())) {
column.setHtmlType(CodegenColumnHtmlTypeEnum.DATETIME.getType());
}
// 兜底,设置默认为 input 类型
if (column.getHtmlType() == null) {
column.setHtmlType(CodegenColumnHtmlTypeEnum.INPUT.getType());
}
}
/**
* 处理字段的 swagger example 示例
*
* @param column 字段
*/
private void processColumnExample(CodegenColumnDO column) {
// id、price、count 等可能是整数的后缀
if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), "id", "price", "count")) {
column.setExample(String.valueOf(randomInt(1, Short.MAX_VALUE)));
return;
}
// name
if (StrUtil.endWithIgnoreCase(column.getJavaField(), "name")) {
column.setExample(randomEle(new String[]{"张三", "李四", "王五", "赵六", "芋艿"}));
return;
}
// status
if (StrUtil.endWithAnyIgnoreCase(column.getJavaField(), "status", "type")) {
column.setExample(randomEle(new String[]{"1", "2"}));
return;
}
// url
if (StrUtil.endWithIgnoreCase(column.getColumnName(), "url")) {
column.setExample("https://www.iocoder.cn");
return;
}
// reason
if (StrUtil.endWithIgnoreCase(column.getColumnName(), "reason")) {
column.setExample(randomEle(new String[]{"不喜欢", "不对", "不好", "不香"}));
return;
}
// description、memo、remark
if (StrUtil.endWithAnyIgnoreCase(column.getColumnName(), "description", "memo", "remark")) {
column.setExample(randomEle(new String[]{"你猜", "随便", "你说的对"}));
}
}
}

View File

@@ -1,679 +0,0 @@
package com.njcn.msgpush.module.infra.service.codegen.inner;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.engine.velocity.VelocityEngine;
import cn.hutool.system.SystemUtil;
import com.njcn.msgpush.framework.apilog.core.annotation.ApiAccessLog;
import com.njcn.msgpush.framework.apilog.core.enums.OperateTypeEnum;
import com.njcn.msgpush.framework.common.exception.util.ServiceExceptionUtil;
import com.njcn.msgpush.framework.common.pojo.CommonResult;
import com.njcn.msgpush.framework.common.pojo.PageParam;
import com.njcn.msgpush.framework.common.pojo.PageResult;
import com.njcn.msgpush.framework.common.util.collection.CollectionUtils;
import com.njcn.msgpush.framework.common.util.date.DateUtils;
import com.njcn.msgpush.framework.common.util.date.LocalDateTimeUtils;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.common.util.object.ObjectUtils;
import com.njcn.msgpush.framework.common.util.string.StrUtils;
import com.njcn.msgpush.framework.excel.core.annotations.DictFormat;
import com.njcn.msgpush.framework.excel.core.convert.DictConvert;
import com.njcn.msgpush.framework.excel.core.util.ExcelUtils;
import com.njcn.msgpush.framework.mybatis.core.dataobject.BaseDO;
import com.njcn.msgpush.framework.mybatis.core.mapper.BaseMapperX;
import com.njcn.msgpush.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenColumnDO;
import com.njcn.msgpush.module.infra.dal.dataobject.codegen.CodegenTableDO;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenFrontTypeEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenSceneEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenTemplateTypeEnum;
import com.njcn.msgpush.module.infra.enums.codegen.CodegenVOTypeEnum;
import com.njcn.msgpush.module.infra.framework.codegen.config.CodegenProperties;
import com.baomidou.mybatisplus.annotation.DbType;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Maps;
import com.google.common.collect.Table;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.Setter;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import java.util.*;
import static cn.hutool.core.map.MapUtil.getStr;
import static cn.hutool.core.text.CharSequenceUtil.*;
/**
* 代码生成的引擎,用于具体生成代码
* 目前基于 {@link org.apache.velocity.app.Velocity} 模板引擎实现
*
* 考虑到 Java 模板引擎的框架非常多Freemarker、Velocity、Thymeleaf 等等,所以我们采用 hutool 封装的 {@link cn.hutool.extra.template.Template} 抽象
*
* @author hongawen
*/
@Component
public class CodegenEngine {
/**
* 后端的模板配置
*
* key模板在 resources 的地址
* value生成的路径
*/
private static final Map<String, String> SERVER_TEMPLATES = MapUtil.<String, String>builder(new LinkedHashMap<>()) // 有序
// Java module-biz(server) Main
.put(javaTemplatePath("controller/vo/pageReqVO"), javaModuleImplVOFilePath("PageReqVO"))
.put(javaTemplatePath("controller/vo/listReqVO"), javaModuleImplVOFilePath("ListReqVO"))
.put(javaTemplatePath("controller/vo/respVO"), javaModuleImplVOFilePath("RespVO"))
.put(javaTemplatePath("controller/vo/saveReqVO"), javaModuleImplVOFilePath("SaveReqVO"))
.put(javaTemplatePath("controller/controller"), javaModuleImplControllerFilePath())
.put(javaTemplatePath("dal/do"),
javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${table.className}DO"))
.put(javaTemplatePath("dal/do_sub"), // 特殊:主子表专属逻辑
javaModuleImplMainFilePath("dal/dataobject/${table.businessName}/${subTable.className}DO"))
.put(javaTemplatePath("dal/mapper"),
javaModuleImplMainFilePath("dal/mysql/${table.businessName}/${table.className}Mapper"))
.put(javaTemplatePath("dal/mapper_sub"), // 特殊:主子表专属逻辑
javaModuleImplMainFilePath("dal/mysql/${table.businessName}/${subTable.className}Mapper"))
.put(javaTemplatePath("dal/mapper.xml"), mapperXmlFilePath())
.put(javaTemplatePath("service/serviceImpl"),
javaModuleImplMainFilePath("service/${table.businessName}/${table.className}ServiceImpl"))
.put(javaTemplatePath("service/service"),
javaModuleImplMainFilePath("service/${table.businessName}/${table.className}Service"))
// Java module-biz(server) Test
.put(javaTemplatePath("test/serviceTest"),
javaModuleImplTestFilePath("service/${table.businessName}/${table.className}ServiceImplTest"))
// Java module-api Main
.put(javaTemplatePath("enums/errorcode"), javaModuleApiMainFilePath("enums/ErrorCodeConstants_手动操作"))
// SQL
.put("codegen/sql/sql.vm", "sql/sql.sql")
.put("codegen/sql/h2.vm", "sql/h2.sql")
.build();
/**
* 前端的配置模版
*
* key1UI 模版的类型 {@link CodegenFrontTypeEnum#getType()}
* key2模板在 resources 的地址
* value生成的路径
*/
private static final Table<Integer, String, String> FRONT_TEMPLATES = ImmutableTable.<Integer, String, String>builder()
// VUE2_ELEMENT_UI
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("views/index.vue"),
vueFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("api/api.js"),
vueFilePath("api/${table.moduleName}/${table.businessName}/index.js"))
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("views/form.vue"),
vueFilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("views/components/form_sub_erp.vue"), // 特殊:主子表专属逻辑
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("views/components/list_sub_inner.vue"), // 特殊:主子表专属逻辑
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
.put(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType(), vueTemplatePath("views/components/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vueFilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
// VUE3_ELEMENT_PLUS
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/index.vue"),
vue3FilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/form.vue"),
vue3FilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_normal.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/form_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}Form.vue"))
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/list_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("views/components/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3FilePath("views/${table.moduleName}/${table.businessName}/components/${subSimpleClassName}List.vue"))
.put(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType(), vue3TemplatePath("api/api.ts"),
vue3FilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
.put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("api/api.ts"),
vue3UniappFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
.put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/index.vue"),
vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("components/search-form.vue"),
vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/components/search-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/form/index.vue"),
vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/form/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_ADMIN_UNIAPP_WOT.getType(), vue3AdminUniappTemplatePath("views/detail/index.vue"),
vue3UniappFilePath("pages-${table.moduleName}/${table.businessName}/detail/index.vue"))
// VUE3_VBEN2_ANTD_SCHEMA
.put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath("views/data.ts"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/${classNameVar}.data.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath("views/index.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath("views/form.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/${simpleClassName}Modal.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN2_ANTD_SCHEMA.getType(), vue3VbenTemplatePath("api/api.ts"),
vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
// VUE3_VBEN5_ANTD_SCHEMA
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/data.ts"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/index.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/form.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("api/api.ts"),
vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_SCHEMA.getType(), vue3Vben5AntdSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
// VUE3_VBEN5_ANTD
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/index.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/form.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("api/api.ts"),
vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_ANTD_GENERAL.getType(), vue3Vben5AntdGeneralTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
// VUE3_VBEN5_EP_SCHEMA
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/data.ts"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/data.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/index.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/form.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("api/api.ts"),
vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_SCHEMA.getType(), vue3Vben5EpSchemaTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
// VUE3_VBEN5_EP
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/index.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/index.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/form.vue"),
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("api/api.ts"),
vue3VbenFilePath("api/${table.moduleName}/${table.businessName}/index.ts"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/form_sub_normal.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/form_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/form_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-form.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/list_sub_inner.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
.put(CodegenFrontTypeEnum.VUE3_VBEN5_EP_GENERAL.getType(), vue3Vben5EpGeneralTemplatePath("views/modules/list_sub_erp.vue"), // 特殊:主子表专属逻辑
vue3VbenFilePath("views/${table.moduleName}/${table.businessName}/modules/${subSimpleClassName_strikeCase}-list.vue"))
.build();
@Resource
private CodegenProperties codegenProperties;
/**
* 是否使用 jakarta 包,用于解决 Spring Boot 2.X 和 3.X 的兼容性问题
*
* true - 使用 jakarta.validation.constraints.*
* false - 使用 javax.validation.constraints.*
*/
@Setter // 允许设置的原因,是因为单测需要手动改变
private Boolean jakartaEnable;
/**
* 是否为 msgpush-cloud 项目,用于解决 Boot 和 Cloud 的 api 模块兼容性问题
*
* true - 需要有 msgpush-module-xxx-api 模块
* false - 不需要有,使用 api、enum 包即可
*/
@Setter
private Boolean cloudEnable;
/**
* 模板引擎,由 hutool 实现
*/
private final TemplateEngine templateEngine;
/**
* 全局通用变量映射
*/
private final Map<String, Object> globalBindingMap = new HashMap<>();
public CodegenEngine() {
// 初始化 TemplateEngine 属性
TemplateConfig config = new TemplateConfig();
config.setResourceMode(TemplateConfig.ResourceMode.CLASSPATH);
this.templateEngine = new VelocityEngine(config);
// 设置 javaxEnable按照是否使用 JDK17 来判断
this.jakartaEnable = SystemUtil.getJavaInfo().isJavaVersionAtLeast(1700) // 17.00 * 100
&& ClassUtils.isPresent("jakarta.annotation.Resource", ClassUtils.getDefaultClassLoader());
// 设置 cloudEnable按照是否使用 Spring Cloud 来判断
this.cloudEnable = ClassUtils.isPresent("com.njcn.msgpush.module.infra.framework.rpc.config.RpcConfiguration",
ClassUtils.getDefaultClassLoader());
}
@PostConstruct
@VisibleForTesting
void initGlobalBindingMap() {
// 全局配置
globalBindingMap.put("basePackage", codegenProperties.getBasePackage());
globalBindingMap.put("baseFrameworkPackage", codegenProperties.getBasePackage()
+ '.' + "framework"); // 用于后续获取测试类的 package 地址
globalBindingMap.put("jakartaPackage", jakartaEnable ? "jakarta" : "javax");
globalBindingMap.put("voType", codegenProperties.getVoType());
globalBindingMap.put("deleteBatchEnable", codegenProperties.getDeleteBatchEnable());
// 全局 Java Bean
globalBindingMap.put("CommonResultClassName", CommonResult.class.getName());
globalBindingMap.put("PageResultClassName", PageResult.class.getName());
// VO 类,独有字段
globalBindingMap.put("PageParamClassName", PageParam.class.getName());
globalBindingMap.put("DictFormatClassName", DictFormat.class.getName());
// DO 类,独有字段
globalBindingMap.put("BaseDOClassName", BaseDO.class.getName());
globalBindingMap.put("baseDOFields", CodegenBuilder.BASE_DO_FIELDS);
globalBindingMap.put("QueryWrapperClassName", LambdaQueryWrapperX.class.getName());
globalBindingMap.put("BaseMapperClassName", BaseMapperX.class.getName());
// Util 工具类
globalBindingMap.put("ServiceExceptionUtilClassName", ServiceExceptionUtil.class.getName());
globalBindingMap.put("DateUtilsClassName", DateUtils.class.getName());
globalBindingMap.put("ExcelUtilsClassName", ExcelUtils.class.getName());
globalBindingMap.put("LocalDateTimeUtilsClassName", LocalDateTimeUtils.class.getName());
globalBindingMap.put("ObjectUtilsClassName", ObjectUtils.class.getName());
globalBindingMap.put("DictConvertClassName", DictConvert.class.getName());
globalBindingMap.put("ApiAccessLogClassName", ApiAccessLog.class.getName());
globalBindingMap.put("OperateTypeEnumClassName", OperateTypeEnum.class.getName());
globalBindingMap.put("BeanUtils", BeanUtils.class.getName());
globalBindingMap.put("CollectionUtilsClassName", CollectionUtils.class.getName());
}
/**
* 生成代码
*
* @param dbType 数据库类型
* @param table 表定义
* @param columns table 的字段定义数组
* @param subTables 子表数组,当且仅当主子表时使用
* @param subColumnsList subTables 的字段定义数组
* @return 生成的代码key 是路径value 是对应代码
*/
public Map<String, String> execute(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,
List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {
// 1.1 初始化 bindMap 上下文
Map<String, Object> bindingMap = initBindingMap(dbType, table, columns, subTables, subColumnsList);
// 1.2 获得模版
Map<String, String> templates = getTemplates(table.getFrontType());
// 2. 执行生成
Map<String, String> result = Maps.newLinkedHashMapWithExpectedSize(templates.size()); // 有序
templates.forEach((vmPath, filePath) -> {
// 2.1 特殊:主子表专属逻辑
if (isSubTemplate(vmPath)) {
generateSubCode(table, subTables, result, vmPath, filePath, bindingMap);
return;
// 2.2 特殊:树表专属逻辑
} else if (isPageReqVOTemplate(vmPath)) {
// 减少多余的类生成,例如说 PageVO.java 类
if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
return;
}
} else if (isListReqVOTemplate(vmPath)) {
// 减少多余的类生成,例如说 ListVO.java 类
if (!CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
return;
}
}
// 2.3 默认生成
generateCode(result, vmPath, filePath, bindingMap);
});
return result;
}
private void generateCode(Map<String, String> result, String vmPath,
String filePath, Map<String, Object> bindingMap) {
filePath = formatFilePath(filePath, bindingMap);
String content = templateEngine.getTemplate(vmPath).render(bindingMap);
// 格式化代码
content = prettyCode(content, vmPath);
result.put(filePath, content);
}
private void generateSubCode(CodegenTableDO table, List<CodegenTableDO> subTables,
Map<String, String> result, String vmPath,
String filePath, Map<String, Object> bindingMap) {
// 没有子表,所以不生成
if (CollUtil.isEmpty(subTables)) {
return;
}
// 主子表的模式匹配。目的:过滤掉个性化的模版
if (vmPath.contains("_normal")
&& ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_NORMAL.getType())) {
return;
}
if (vmPath.contains("_erp")
&& ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_ERP.getType())) {
return;
}
if (vmPath.contains("_inner")
&& ObjectUtil.notEqual(table.getTemplateType(), CodegenTemplateTypeEnum.MASTER_INNER.getType())) {
return;
}
// 逐个生成
for (int i = 0; i < subTables.size(); i++) {
bindingMap.put("subIndex", i);
generateCode(result, vmPath, filePath, bindingMap);
}
bindingMap.remove("subIndex");
}
/**
* 格式化生成后的代码
*
* 因为尽量让 vm 模版简单,所以统一的处理都在这个方法。
* 如果不处理Vue 的 Pretty 格式校验可能会报错
*
* @param content 格式化前的代码
* @param vmPath 模板路径
* @return 格式化后的代码
*/
private String prettyCode(String content, String vmPath) {
// Vue 界面:去除字段后面多余的 , 逗号,解决前端的 Pretty 代码格式检查的报错(需要排除 vben5、vue3_admin_uniapp
if (!StrUtil.containsAny(vmPath, "vben5", "vue3_admin_uniapp")) {
content = content.replaceAll(",\n}", "\n}").replaceAll(",\n }", "\n }");
}
// Vue 界面:去除多的 dateFormatter只有一个的情况下说明没使用到
if (StrUtil.count(content, "dateFormatter") == 1) {
content = StrUtils.removeLineContains(content, "dateFormatter");
}
// Vue2 界面:修正 $refs
if (StrUtil.count(content, "this.refs") >= 1) {
content = content.replace("this.refs", "this.$refs");
}
// Vue 界面:去除多的 dict 相关,只有一个的情况下,说明没使用到
if (StrUtil.count(content, "getIntDictOptions") == 1) {
content = content.replace("getIntDictOptions, ", "");
}
if (StrUtil.count(content, "getStrDictOptions") == 1) {
content = content.replace("getStrDictOptions, ", "");
}
if (StrUtil.count(content, "getBoolDictOptions") == 1) {
content = content.replace("getBoolDictOptions, ", "");
}
if (StrUtil.count(content, "DICT_TYPE.") == 0) {
content = StrUtils.removeLineContains(content, "DICT_TYPE");
}
return content;
}
private Map<String, Object> initBindingMap(DbType dbType, CodegenTableDO table, List<CodegenColumnDO> columns,
List<CodegenTableDO> subTables, List<List<CodegenColumnDO>> subColumnsList) {
// 创建 bindingMap
Map<String, Object> bindingMap = new HashMap<>(globalBindingMap);
bindingMap.put("dbType", dbType);
bindingMap.put("table", table);
bindingMap.put("columns", columns);
bindingMap.put("primaryColumn", CollectionUtils.findFirst(columns, CodegenColumnDO::getPrimaryKey)); // 主键字段
bindingMap.put("sceneEnum", CodegenSceneEnum.valueOf(table.getScene()));
// className 相关
// 去掉指定前缀,将 TestDictType 转换成 DictType. 因为在 create 等方法后,不需要带上 Test 前缀
String className = table.getClassName();
String simpleClassName = equalsAnyIgnoreCase(table.getClassName(), table.getModuleName()) ? table.getClassName()
: removePrefix(table.getClassName(), upperFirst(table.getModuleName()));
String classNameVar = lowerFirst(simpleClassName);
bindingMap.put("simpleClassName", simpleClassName);
bindingMap.put("simpleClassName_underlineCase", toUnderlineCase(simpleClassName)); // 将 DictType 转换成 dict_type
bindingMap.put("classNameVar", classNameVar); // 将 DictType 转换成 dictType用于变量
// 将 DictType 转换成 dict-type
String simpleClassNameStrikeCase = toSymbolCase(simpleClassName, '-');
bindingMap.put("simpleClassName_strikeCase", simpleClassNameStrikeCase);
// permission 前缀
bindingMap.put("permissionPrefix", table.getModuleName() + ":" + simpleClassNameStrikeCase);
// 特殊:树表专属逻辑
if (CodegenTemplateTypeEnum.isTree(table.getTemplateType())) {
CodegenColumnDO treeParentColumn = CollUtil.findOne(columns,
column -> Objects.equals(column.getId(), table.getTreeParentColumnId()));
bindingMap.put("treeParentColumn", treeParentColumn);
bindingMap.put("treeParentColumn_javaField_underlineCase", toUnderlineCase(treeParentColumn.getJavaField()));
CodegenColumnDO treeNameColumn = CollUtil.findOne(columns,
column -> Objects.equals(column.getId(), table.getTreeNameColumnId()));
bindingMap.put("treeNameColumn", treeNameColumn);
bindingMap.put("treeNameColumn_javaField_underlineCase", toUnderlineCase(treeNameColumn.getJavaField()));
}
// 特殊:主子表专属逻辑
if (CollUtil.isNotEmpty(subTables)) {
// 创建 bindingMap
bindingMap.put("subTables", subTables);
bindingMap.put("subColumnsList", subColumnsList);
List<CodegenColumnDO> subPrimaryColumns = new ArrayList<>();
List<CodegenColumnDO> subJoinColumns = new ArrayList<>();
List<String> subJoinColumnStrikeCases = new ArrayList<>();
List<String> subSimpleClassNames = new ArrayList<>();
List<String> subClassNameVars = new ArrayList<>();
List<String> simpleClassNameUnderlineCases = new ArrayList<>();
List<String> subSimpleClassNameStrikeCases = new ArrayList<>();
for (int i = 0; i < subTables.size(); i++) {
CodegenTableDO subTable = subTables.get(i);
List<CodegenColumnDO> subColumns = subColumnsList.get(i);
subPrimaryColumns.add(CollectionUtils.findFirst(subColumns, CodegenColumnDO::getPrimaryKey)); //
CodegenColumnDO subColumn = CollectionUtils.findFirst(subColumns, // 关联的字段
column -> Objects.equals(column.getId(), subTable.getSubJoinColumnId()));
subJoinColumns.add(subColumn);
subJoinColumnStrikeCases.add(toSymbolCase(subColumn.getJavaField(), '-')); // 将 DictType 转换成 dict-type
// className 相关
String subSimpleClassName = removePrefix(subTable.getClassName(), upperFirst(subTable.getModuleName()));
subSimpleClassNames.add(subSimpleClassName);
simpleClassNameUnderlineCases.add(toUnderlineCase(subSimpleClassName)); // 将 DictType 转换成 dict_type
subClassNameVars.add(lowerFirst(subSimpleClassName)); // 将 DictType 转换成 dictType用于变量
subSimpleClassNameStrikeCases.add(toSymbolCase(subSimpleClassName, '-')); // 将 DictType 转换成 dict-type
}
bindingMap.put("subPrimaryColumns", subPrimaryColumns);
bindingMap.put("subJoinColumns", subJoinColumns);
bindingMap.put("subJoinColumn_strikeCases", subJoinColumnStrikeCases);
bindingMap.put("subSimpleClassNames", subSimpleClassNames);
bindingMap.put("simpleClassNameUnderlineCases", simpleClassNameUnderlineCases);
bindingMap.put("subClassNameVars", subClassNameVars);
bindingMap.put("subSimpleClassName_strikeCases", subSimpleClassNameStrikeCases);
}
// 多个 vm 公用的 VO 变量
if (ObjectUtil.equal(codegenProperties.getVoType(), CodegenVOTypeEnum.VO.getType())) {
String prefixClass = CodegenSceneEnum.valueOf(table.getScene()).getPrefixClass();
bindingMap.put("saveReqVOClass", prefixClass + className + "SaveReqVO");
bindingMap.put("updateReqVOClass", prefixClass + className + "SaveReqVO");
bindingMap.put("respVOClass", prefixClass + className + "RespVO");
bindingMap.put("saveReqVOVar", "createReqVO");
bindingMap.put("updateReqVOVar", "updateReqVO");
} else if (ObjectUtil.equal(codegenProperties.getVoType(), CodegenVOTypeEnum.DO.getType())) {
bindingMap.put("saveReqVOClass", className + "DO");
bindingMap.put("updateReqVOClass", className + "DO");
bindingMap.put("respVOClass", className + "DO");
bindingMap.put("saveReqVOVar", classNameVar);
bindingMap.put("updateReqVOVar", classNameVar);
}
return bindingMap;
}
private Map<String, String> getTemplates(Integer frontType) {
Map<String, String> templates = new LinkedHashMap<>();
templates.putAll(SERVER_TEMPLATES);
templates.putAll(FRONT_TEMPLATES.row(frontType));
// 如果是 Boot 项目,则不使用 api/server 模块
if (Boolean.FALSE.equals(cloudEnable)) {
SERVER_TEMPLATES.forEach((templatePath, filePath) -> {
filePath = StrUtil.replace(filePath, "/msgpush-module-${table.moduleName}-api", "");
filePath = StrUtil.replace(filePath, "/msgpush-module-${table.moduleName}-server", "");
templates.put(templatePath, filePath);
});
}
// 如果禁用单元测试,则移除对应的模版
if (Boolean.FALSE.equals(codegenProperties.getUnitTestEnable())) {
templates.remove(javaTemplatePath("test/serviceTest"));
templates.remove("codegen/sql/h2.vm");
}
// 如果禁用 VO 类型,则移除对应的模版
if (ObjectUtil.notEqual(codegenProperties.getVoType(), CodegenVOTypeEnum.VO.getType())) {
templates.remove(javaTemplatePath("controller/vo/respVO"));
templates.remove(javaTemplatePath("controller/vo/saveReqVO"));
}
return templates;
}
@SuppressWarnings("unchecked")
private String formatFilePath(String filePath, Map<String, Object> bindingMap) {
filePath = StrUtil.replace(filePath, "${basePackage}",
getStr(bindingMap, "basePackage").replaceAll("\\.", "/"));
filePath = StrUtil.replace(filePath, "${classNameVar}",
getStr(bindingMap, "classNameVar"));
filePath = StrUtil.replace(filePath, "${simpleClassName}",
getStr(bindingMap, "simpleClassName"));
// sceneEnum 包含的字段
CodegenSceneEnum sceneEnum = (CodegenSceneEnum) bindingMap.get("sceneEnum");
filePath = StrUtil.replace(filePath, "${sceneEnum.prefixClass}", sceneEnum.getPrefixClass());
filePath = StrUtil.replace(filePath, "${sceneEnum.basePackage}", sceneEnum.getBasePackage());
// table 包含的字段
CodegenTableDO table = (CodegenTableDO) bindingMap.get("table");
filePath = StrUtil.replace(filePath, "${table.moduleName}", table.getModuleName());
filePath = StrUtil.replace(filePath, "${table.businessName}", table.getBusinessName());
filePath = StrUtil.replace(filePath, "${table.className}", table.getClassName());
// 特殊:主子表专属逻辑
Integer subIndex = (Integer) bindingMap.get("subIndex");
if (subIndex != null) {
CodegenTableDO subTable = ((List<CodegenTableDO>) bindingMap.get("subTables")).get(subIndex);
filePath = StrUtil.replace(filePath, "${subTable.moduleName}", subTable.getModuleName());
filePath = StrUtil.replace(filePath, "${subTable.businessName}", subTable.getBusinessName());
filePath = StrUtil.replace(filePath, "${subTable.className}", subTable.getClassName());
filePath = StrUtil.replace(filePath, "${subSimpleClassName}",
((List<String>) bindingMap.get("subSimpleClassNames")).get(subIndex));
filePath = StrUtil.replace(filePath, "${subSimpleClassName_strikeCase}",
((List<String>) bindingMap.get("subSimpleClassName_strikeCases")).get(subIndex));
}
return filePath;
}
private static String javaTemplatePath(String path) {
return "codegen/java/" + path + ".vm";
}
private static String javaModuleImplVOFilePath(String path) {
return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
"vo/${sceneEnum.prefixClass}${table.className}" + path, "server", "main");
}
private static String javaModuleImplControllerFilePath() {
return javaModuleFilePath("controller/${sceneEnum.basePackage}/${table.businessName}/" +
"${sceneEnum.prefixClass}${table.className}Controller", "server", "main");
}
private static String javaModuleImplMainFilePath(String path) {
return javaModuleFilePath(path, "server", "main");
}
private static String javaModuleApiMainFilePath(String path) {
return javaModuleFilePath(path, "api", "main");
}
private static String javaModuleImplTestFilePath(String path) {
return javaModuleFilePath(path, "server", "test");
}
private static String javaModuleFilePath(String path, String module, String src) {
return "msgpush-module-${table.moduleName}/" + // 顶级模块
"msgpush-module-${table.moduleName}-" + module + "/" + // 子模块
"src/" + src + "/java/${basePackage}/module/${table.moduleName}/" + path + ".java";
}
private static String mapperXmlFilePath() {
return "msgpush-module-${table.moduleName}/" + // 顶级模块
"msgpush-module-${table.moduleName}-server/" + // 子模块
"src/main/resources/mapper/${table.businessName}/${table.className}Mapper.xml";
}
private static String vueTemplatePath(String path) {
return "codegen/vue/" + path + ".vm";
}
private static String vueFilePath(String path) {
return "msgpush-ui-${sceneEnum.basePackage}-vue2/" + // 顶级目录
"src/" + path;
}
private static String vue3TemplatePath(String path) {
return "codegen/vue3/" + path + ".vm";
}
private static String vue3FilePath(String path) {
return "msgpush-ui-${sceneEnum.basePackage}-vue3/" + // 顶级目录
"src/" + path;
}
private static String vue3AdminUniappTemplatePath(String path) {
return "codegen/vue3_admin_uniapp/" + path + ".vm";
}
private static String vue3UniappFilePath(String path) {
return "msgpush-ui-${sceneEnum.basePackage}-uniapp/" + // 顶级目录
"src/" + path;
}
private static String vue3VbenFilePath(String path) {
return "msgpush-ui-${sceneEnum.basePackage}-vben/" + // 顶级目录
"src/" + path;
}
private static String vue3VbenTemplatePath(String path) {
return "codegen/vue3_vben/" + 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 String vue3Vben5EpSchemaTemplatePath(String path) {
return "codegen/vue3_vben5_ele/schema/" + path + ".vm";
}
private static String vue3Vben5EpGeneralTemplatePath(String path) {
return "codegen/vue3_vben5_ele/general/" + path + ".vm";
}
private static boolean isSubTemplate(String path) {
return path.contains("_sub");
}
private static boolean isPageReqVOTemplate(String path) {
return path.contains("pageReqVO");
}
private static boolean isListReqVOTemplate(String path) {
return path.contains("listReqVO");
}
}

View File

@@ -1,60 +0,0 @@
package com.njcn.msgpush.module.infra.service.db;
import com.njcn.msgpush.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;
import com.njcn.msgpush.module.infra.dal.dataobject.db.DataSourceConfigDO;
import jakarta.validation.Valid;
import java.util.List;
/**
* 数据源配置 Service 接口
*
* @author hongawen
*/
public interface DataSourceConfigService {
/**
* 创建数据源配置
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createDataSourceConfig(@Valid DataSourceConfigSaveReqVO createReqVO);
/**
* 更新数据源配置
*
* @param updateReqVO 更新信息
*/
void updateDataSourceConfig(@Valid DataSourceConfigSaveReqVO updateReqVO);
/**
* 删除数据源配置
*
* @param id 编号
*/
void deleteDataSourceConfig(Long id);
/**
* 批量删除数据源配置
*
* @param ids 编号列表
*/
void deleteDataSourceConfigList(List<Long> ids);
/**
* 获得数据源配置
*
* @param id 编号
* @return 数据源配置
*/
DataSourceConfigDO getDataSourceConfig(Long id);
/**
* 获得数据源配置列表
*
* @return 数据源配置列表
*/
List<DataSourceConfigDO> getDataSourceConfigList();
}

View File

@@ -1,111 +0,0 @@
package com.njcn.msgpush.module.infra.service.db;
import com.njcn.msgpush.framework.common.util.object.BeanUtils;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import com.njcn.msgpush.module.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;
import com.njcn.msgpush.module.infra.dal.dataobject.db.DataSourceConfigDO;
import com.njcn.msgpush.module.infra.dal.mysql.db.DataSourceConfigMapper;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import java.util.Objects;
import static com.njcn.msgpush.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.njcn.msgpush.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_EXISTS;
import static com.njcn.msgpush.module.infra.enums.ErrorCodeConstants.DATA_SOURCE_CONFIG_NOT_OK;
/**
* 数据源配置 Service 实现类
*
* @author hongawen
*/
@Service
@Validated
public class DataSourceConfigServiceImpl implements DataSourceConfigService {
@Resource
private DataSourceConfigMapper dataSourceConfigMapper;
@Resource
private DynamicDataSourceProperties dynamicDataSourceProperties;
@Override
public Long createDataSourceConfig(DataSourceConfigSaveReqVO createReqVO) {
DataSourceConfigDO config = BeanUtils.toBean(createReqVO, DataSourceConfigDO.class);
validateConnectionOK(config);
// 插入
dataSourceConfigMapper.insert(config);
// 返回
return config.getId();
}
@Override
public void updateDataSourceConfig(DataSourceConfigSaveReqVO updateReqVO) {
// 校验存在
validateDataSourceConfigExists(updateReqVO.getId());
DataSourceConfigDO updateObj = BeanUtils.toBean(updateReqVO, DataSourceConfigDO.class);
validateConnectionOK(updateObj);
// 更新
dataSourceConfigMapper.updateById(updateObj);
}
@Override
public void deleteDataSourceConfig(Long id) {
// 校验存在
validateDataSourceConfigExists(id);
// 删除
dataSourceConfigMapper.deleteById(id);
}
@Override
public void deleteDataSourceConfigList(List<Long> ids) {
dataSourceConfigMapper.deleteByIds(ids);
}
private void validateDataSourceConfigExists(Long id) {
if (dataSourceConfigMapper.selectById(id) == null) {
throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS);
}
}
@Override
public DataSourceConfigDO getDataSourceConfig(Long id) {
// 如果 id 为 0默认为 master 的数据源
if (Objects.equals(id, DataSourceConfigDO.ID_MASTER)) {
return buildMasterDataSourceConfig();
}
// 从 DB 中读取
return dataSourceConfigMapper.selectById(id);
}
@Override
public List<DataSourceConfigDO> getDataSourceConfigList() {
List<DataSourceConfigDO> result = dataSourceConfigMapper.selectList();
// 补充 master 数据源
result.add(0, buildMasterDataSourceConfig());
return result;
}
private void validateConnectionOK(DataSourceConfigDO config) {
boolean success = JdbcUtils.isConnectionOK(config.getUrl(), config.getUsername(), config.getPassword());
if (!success) {
throw exception(DATA_SOURCE_CONFIG_NOT_OK);
}
}
private DataSourceConfigDO buildMasterDataSourceConfig() {
String primary = dynamicDataSourceProperties.getPrimary();
DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary);
return new DataSourceConfigDO().setId(DataSourceConfigDO.ID_MASTER).setName(primary)
.setUrl(dataSourceProperty.getUrl())
.setUsername(dataSourceProperty.getUsername())
.setPassword(dataSourceProperty.getPassword());
}
}

View File

@@ -1,33 +0,0 @@
package com.njcn.msgpush.module.infra.service.db;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import java.util.List;
/**
* 数据库表 Service
*
* @author hongawen
*/
public interface DatabaseTableService {
/**
* 获得表列表,基于表名称 + 表描述进行模糊匹配
*
* @param dataSourceConfigId 数据源配置的编号
* @param nameLike 表名称,模糊匹配
* @param commentLike 表描述,模糊匹配
* @return 表列表
*/
List<TableInfo> getTableList(Long dataSourceConfigId, String nameLike, String commentLike);
/**
* 获得指定表名
*
* @param dataSourceConfigId 数据源配置的编号
* @param tableName 表名称
* @return 表
*/
TableInfo getTable(Long dataSourceConfigId, String tableName);
}

View File

@@ -1,77 +0,0 @@
package com.njcn.msgpush.module.infra.service.db;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.njcn.msgpush.framework.mybatis.core.util.JdbcUtils;
import com.njcn.msgpush.module.infra.dal.dataobject.db.DataSourceConfigDO;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.query.SQLQuery;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* 数据库表 Service 实现类
*
* @author hongawen
*/
@Service
public class DatabaseTableServiceImpl implements DatabaseTableService {
@Resource
private DataSourceConfigService dataSourceConfigService;
@Override
public List<TableInfo> getTableList(Long dataSourceConfigId, String nameLike, String commentLike) {
List<TableInfo> tables = getTableList0(dataSourceConfigId, null);
return tables.stream().filter(tableInfo -> (StrUtil.isEmpty(nameLike) || tableInfo.getName().contains(nameLike))
&& (StrUtil.isEmpty(commentLike) || tableInfo.getComment().contains(commentLike)))
.collect(Collectors.toList());
}
@Override
public TableInfo getTable(Long dataSourceConfigId, String name) {
return CollUtil.getFirst(getTableList0(dataSourceConfigId, name));
}
private List<TableInfo> getTableList0(Long dataSourceConfigId, String name) {
// 获得数据源配置
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(dataSourceConfigId);
Assert.notNull(config, "数据源({}) 不存在!", dataSourceConfigId);
// 使用 MyBatis Plus Generator 解析表结构
DataSourceConfig.Builder dataSourceConfigBuilder = new DataSourceConfig.Builder(config.getUrl(), config.getUsername(),
config.getPassword());
if (JdbcUtils.isSQLServer(config.getUrl())) { // 特殊SQLServer jdbc 非标准,参见 https://github.com/baomidou/mybatis-plus/issues/5419
dataSourceConfigBuilder.databaseQueryClass(SQLQuery.class);
}
StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder().enableSkipView(); // 忽略视图,业务上一般用不到
if (StrUtil.isNotEmpty(name)) {
strategyConfig.addInclude(name);
} else {
// 移除工作流和定时任务前缀的表名
strategyConfig.addExclude("ACT_[\\S\\s]+|QRTZ_[\\S\\s]+|FLW_[\\S\\s]+");
// 移除 ORACLE 相关的系统表
strategyConfig.addExclude("IMPDP_[\\S\\s]+|ALL_[\\S\\s]+|HS_[\\S\\\\s]+");
strategyConfig.addExclude("[\\S\\s]+\\$[\\S\\s]+|[\\S\\s]+\\$"); // 表里不能有 $,一般有都是系统的表
}
GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build(); // 只使用 LocalDateTime 类型,不使用 LocalDate
ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfigBuilder.build(), strategyConfig.build(),
null, globalConfig, null);
// 按照名字排序
List<TableInfo> tables = builder.getTableInfoList();
tables.sort(Comparator.comparing(TableInfo::getName));
return tables;
}
}

View File

@@ -1,89 +0,0 @@
--- #################### 注册中心 + 配置中心相关配置 ####################
spring:
cloud:
nacos:
server-addr: 192.168.1.103:18848 # Nacos 服务器地址
username: # Nacos 账号
password: # Nacos 密码
discovery: # 【配置中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
config: # 【注册中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
autoconfigure:
exclude:
datasource:
druid: # Druid 【监控】相关的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: # 控制台管理用户名和密码
login-password:
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 100
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic: # 多数据源配置
druid: # Druid 【连接池】相关的全局配置
initial-size: 5 # 初始连接数
min-idle: 10 # 最小连接池数量
max-active: 20 # 最大连接池数量
max-wait: 60000 # 配置获取连接等待超时的时间单位毫秒1 分钟)
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测检测需要关闭的空闲连接单位毫秒1 分钟)
min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间单位毫秒10 分钟)
max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间单位毫秒30 分钟)
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true # 是否开启 PreparedStatement 缓存
max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量
primary: master
datasource:
master:
url: jdbc:mysql://192.168.1.22:13306/msgpush?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
username: root
password: njcnpqs
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口
database: 1 # 数据库索引
# password: njcnpqs # 密码,建议生产环境开启
--- #################### 监控相关配置 ####################
# Actuator 监控端点的配置项
management:
endpoints:
web:
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
--- #################### 灿能相关配置 ####################
# 灿能配置项,设置当前项目所有自定义的配置
msgpush:
demo: true # 开启演示模式

View File

@@ -1,103 +0,0 @@
--- #################### 注册中心 + 配置中心相关配置 ####################
spring:
cloud:
nacos:
server-addr: 192.168.1.103:18848 # Nacos 服务器地址
username: # Nacos 账号
password: # Nacos 密码
discovery: # 【配置中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
config: # 【注册中心】配置项
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
autoconfigure:
exclude:
datasource:
druid: # Druid 【监控】相关的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: # 控制台管理用户名和密码
login-password:
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 100
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic: # 多数据源配置
druid: # Druid 【连接池】相关的全局配置
initial-size: 1 # 初始连接数
min-idle: 1 # 最小连接池数量
max-active: 20 # 最大连接池数量
max-wait: 60000 # 配置获取连接等待超时的时间单位毫秒1 分钟)
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测检测需要关闭的空闲连接单位毫秒1 分钟)
min-evictable-idle-time-millis: 600000 # 配置一个连接在池中最小生存的时间单位毫秒10 分钟)
max-evictable-idle-time-millis: 1800000 # 配置一个连接在池中最大生存的时间单位毫秒30 分钟)
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true # 是否开启 PreparedStatement 缓存
max-pool-prepared-statement-per-connection-size: 20 # 每个连接缓存的 PreparedStatement 数量
primary: master
datasource:
master:
url: jdbc:mysql://192.168.1.22:13306/msgpush?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例
username: root
password: njcnpqs
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
data:
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口
database: 0 # 数据库索引
# password: njcnpqs # 密码,建议生产环境开启
--- #################### 监控相关配置 ####################
# Actuator 监控端点的配置项
management:
endpoints:
web:
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
# 日志文件配置
logging:
level:
# 配置自己写的 MyBatis Mapper 打印日志
com.njcn.msgpush.module.infra.dal.mysql: debug
com.njcn.msgpush.module.infra.dal.mysql.logger.ApiErrorLogMapper: INFO # 配置 ApiErrorLogMapper 的日志级别为 info避免和 GlobalExceptionHandler 重复打印
com.njcn.msgpush.module.infra.dal.mysql.file.FileConfigMapper: INFO # 配置 FileConfigMapper 的日志级别为 info
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿先禁用Spring Boot 3.X 存在部分错误的 WARN 提示
--- #################### 灿能相关配置 ####################
# 灿能配置项,设置当前项目所有自定义的配置
msgpush:
env: # 多环境的配置项
tag: ${HOSTNAME}
security:
mock-enable: true
access-log: # 访问日志的配置项
enable: true

View File

@@ -1,111 +0,0 @@
spring:
application:
name: infra-server
profiles:
active: local
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
config:
import:
- optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置
# Servlet 配置
servlet:
# 文件上传相关配置项
multipart:
max-file-size: 16MB # 单个文件大小
max-request-size: 32MB # 设置总上传的文件大小
# Jackson 配置项
jackson:
serialization:
write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳
write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
fail-on-empty-beans: false # 允许序列化无属性的 Bean
# Cache 配置项
cache:
type: REDIS
redis:
time-to-live: 1h # 设置过期时间为 1 小时
data:
redis:
repositories:
enabled: false # 项目未使用到 Spring Data Redis 的 Repository所以直接禁用保证启动速度
server:
port: 48082
logging:
file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
--- #################### 接口文档配置 ####################
springdoc:
api-docs:
enabled: true # 1. 是否开启 Swagger 接文档的元数据
path: /v3/api-docs
swagger-ui:
enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面
path: /swagger-ui
default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
knife4j:
enable: true
setting:
language: zh_cn
# MyBatis Plus 的配置项
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
# id-type: AUTO # 自增 ID适合 MySQL 等直接自增的数据库
# id-type: INPUT # 用户输入 ID适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
# id-type: ASSIGN_ID # 分配 ID默认使用雪花算法。注意Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
banner: false # 关闭控制台的 Banner 打印
type-aliases-package: ${msgpush.info.base-package}.dal.dataobject
encryptor:
password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
mybatis-plus-join:
banner: false # 关闭控制台的 Banner 打印
# VO 转换(数据翻译)相关
easy-trans:
is-enable-global: false # 【默认禁用,对性能确认压力大】启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口
msgpush:
info:
version: 1.0.0
base-package: com.njcn.msgpush.module.infra
web:
admin-ui:
url: http://localhost # Admin 管理后台 UI 的地址
xss:
enable: false
exclude-urls: # 如下两个 url仅仅是为了演示去掉配置也没关系
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
websocket:
enable: true # websocket的开关
path: /infra/ws # 路径
sender-type: local
swagger:
title: 管理后台
description: 提供管理员管理的所有功能
version: ${msgpush.info.version}
codegen:
base-package: com.njcn.msgpush
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
front-type: 20 # 前端模版的类型,参见 CodegenFrontTypeEnum 枚举类
vo-type: 10 # VO 的类型,参见 CodegenVOTypeEnum 枚举类
delete-batch-enable: true # 是否生成批量删除接口
unit-test-enable: false # 是否生成单元测试
debug: false

View File

@@ -1,271 +0,0 @@
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName};
import org.springframework.web.bind.annotation.*;
import ${jakartaPackage}.annotation.Resource;
import org.springframework.validation.annotation.Validated;
#if ($sceneEnum.scene == 1)import org.springframework.security.access.prepost.PreAuthorize;#end
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import ${jakartaPackage}.validation.constraints.*;
import ${jakartaPackage}.validation.*;
import ${jakartaPackage}.servlet.http.*;
import java.util.*;
import java.io.IOException;
import ${PageParamClassName};
import ${PageResultClassName};
import ${CommonResultClassName};
import ${BeanUtils};
import static ${CommonResultClassName}.success;
import ${ExcelUtilsClassName};
import ${ApiAccessLogClassName};
import static ${OperateTypeEnumClassName}.*;
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
#end
import ${basePackage}.module.${table.moduleName}.service.${table.businessName}.${table.className}Service;
@Tag(name = "${sceneEnum.name} - ${table.classComment}")
@RestController
##二级的 businessName 暂时不算在 HTTP 路径上,可以根据需要写
@RequestMapping("/${table.moduleName}/${simpleClassName_strikeCase}")
@Validated
public class ${sceneEnum.prefixClass}${table.className}Controller {
@Resource
private ${table.className}Service ${classNameVar}Service;
@PostMapping("/create")
@Operation(summary = "创建${table.classComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')")
#end
public CommonResult<${primaryColumn.javaType}> create${simpleClassName}(@Valid @RequestBody ${saveReqVOClass} ${saveReqVOVar}) {
return success(${classNameVar}Service.create${simpleClassName}(${saveReqVOVar}));
}
@PutMapping("/update")
@Operation(summary = "更新${table.classComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')")
#end
public CommonResult<Boolean> update${simpleClassName}(@Valid @RequestBody ${updateReqVOClass} ${updateReqVOVar}) {
${classNameVar}Service.update${simpleClassName}(${updateReqVOVar});
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除${table.classComment}")
@Parameter(name = "id", description = "编号", required = true)
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")
#end
public CommonResult<Boolean> delete${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) {
${classNameVar}Service.delete${simpleClassName}(id);
return success(true);
}
#if ( $table.templateType != 2 && $deleteBatchEnable)
@DeleteMapping("/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除${table.classComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")
#end
public CommonResult<Boolean> delete${simpleClassName}List(@RequestParam("ids") List<${primaryColumn.javaType}> ids) {
${classNameVar}Service.delete${simpleClassName}ListByIds(ids);
return success(true);
}
#end
@GetMapping("/get")
@Operation(summary = "获得${table.classComment}")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
#end
public CommonResult<${respVOClass}> get${simpleClassName}(@RequestParam("id") ${primaryColumn.javaType} id) {
${table.className}DO ${classNameVar} = ${classNameVar}Service.get${simpleClassName}(id);
#if ($voType == 10)
return success(BeanUtils.toBean(${classNameVar}, ${respVOClass}.class));
#else
return success(${classNameVar});
#end
}
#if ( $table.templateType != 2 )
@GetMapping("/page")
@Operation(summary = "获得${table.classComment}分页")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
#end
public CommonResult<PageResult<${respVOClass}>> get${simpleClassName}Page(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {
PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO);
#if ($voType == 10)
return success(BeanUtils.toBean(pageResult, ${respVOClass}.class));
#else
return success(pageResult);
#end
}
## 特殊:树表专属逻辑(树不需要分页接口)
#else
@GetMapping("/list")
@Operation(summary = "获得${table.classComment}列表")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
#end
public CommonResult<List<${respVOClass}>> get${simpleClassName}List(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);
#if ($voType == 10)
return success(BeanUtils.toBean(list, ${respVOClass}.class));
#else
return success(list);
#end
}
#end
@GetMapping("/export-excel")
@Operation(summary = "导出${table.classComment} Excel")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:export')")
#end
@ApiAccessLog(operateType = EXPORT)
#if ( $table.templateType != 2 )
public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}Page(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${respVOClass}.class,
BeanUtils.toBean(list, ${respVOClass}.class));
}
## 特殊:树表专属逻辑(树不需要分页接口)
#else
public void export${simpleClassName}Excel(@Valid ${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO,
HttpServletResponse response) throws IOException {
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(listReqVO);
// 导出 Excel
ExcelUtils.write(response, "${table.classComment}.xls", "数据", ${table.className}RespVO.class,
BeanUtils.toBean(list, ${table.className}RespVO.class));
}
#end
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
#set ($subClassNameVar = $subClassNameVars.get($index))
// ==================== 子表($subTable.classComment ====================
## 情况一MASTER_ERP 时,需要分查询页子表
#if ( $table.templateType == 11 )
@GetMapping("/${subSimpleClassName_strikeCase}/page")
@Operation(summary = "获得${subTable.classComment}分页")
@Parameter(name = "${subJoinColumn.javaField}", description = "${subJoinColumn.columnComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
#end
public CommonResult<PageResult<${subTable.className}DO>> get${subSimpleClassName}Page(PageParam pageReqVO,
@RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return success(${classNameVar}Service.get${subSimpleClassName}Page(pageReqVO, ${subJoinColumn.javaField}));
}
## 情况二:非 MASTER_ERP 时,需要列表查询子表
#else
#if ( $subTable.subJoinMany )
@GetMapping("/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}")
@Operation(summary = "获得${subTable.classComment}列表")
@Parameter(name = "${subJoinColumn.javaField}", description = "${subJoinColumn.columnComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
#end
public CommonResult<List<${subTable.className}DO>> get${subSimpleClassName}ListBy${SubJoinColumnName}(@RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return success(${classNameVar}Service.get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}));
}
#else
@GetMapping("/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}")
@Operation(summary = "获得${subTable.classComment}")
@Parameter(name = "${subJoinColumn.javaField}", description = "${subJoinColumn.columnComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
#end
public CommonResult<${subTable.className}DO> get${subSimpleClassName}By${SubJoinColumnName}(@RequestParam("${subJoinColumn.javaField}") ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return success(${classNameVar}Service.get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}));
}
#end
#end
## 特殊MASTER_ERP 时,支持单个的新增、修改、删除操作
#if ( $table.templateType == 11 )
@PostMapping("/${subSimpleClassName_strikeCase}/create")
@Operation(summary = "创建${subTable.classComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:create')")
#end
public CommonResult<${subPrimaryColumn.javaType}> create${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) {
return success(${classNameVar}Service.create${subSimpleClassName}(${subClassNameVar}));
}
@PutMapping("/${subSimpleClassName_strikeCase}/update")
@Operation(summary = "更新${subTable.classComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:update')")
#end
public CommonResult<Boolean> update${subSimpleClassName}(@Valid @RequestBody ${subTable.className}DO ${subClassNameVar}) {
${classNameVar}Service.update${subSimpleClassName}(${subClassNameVar});
return success(true);
}
@DeleteMapping("/${subSimpleClassName_strikeCase}/delete")
@Parameter(name = "id", description = "编号", required = true)
@Operation(summary = "删除${subTable.classComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")
#end
public CommonResult<Boolean> delete${subSimpleClassName}(@RequestParam("id") ${subPrimaryColumn.javaType} id) {
${classNameVar}Service.delete${subSimpleClassName}(id);
return success(true);
}
#if ($deleteBatchEnable)
@DeleteMapping("/${subSimpleClassName_strikeCase}/delete-list")
@Parameter(name = "ids", description = "编号", required = true)
@Operation(summary = "批量删除${subTable.classComment}")
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:delete')")
#end
public CommonResult<Boolean> delete${subSimpleClassName}List(@RequestParam("ids") List<${subPrimaryColumn.javaType}> ids) {
${classNameVar}Service.delete${subSimpleClassName}ListByIds(ids);
return success(true);
}
#end
@GetMapping("/${subSimpleClassName_strikeCase}/get")
@Operation(summary = "获得${subTable.classComment}")
@Parameter(name = "id", description = "编号", required = true)
#if ($sceneEnum.scene == 1)
@PreAuthorize("@ss.hasPermission('${permissionPrefix}:query')")
#end
public CommonResult<${subTable.className}DO> get${subSimpleClassName}(@RequestParam("id") ${subPrimaryColumn.javaType} id) {
return success(${classNameVar}Service.get${subSimpleClassName}(id));
}
#end
#end
}

View File

@@ -1,45 +0,0 @@
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import ${PageParamClassName};
#foreach ($column in $columns)
#if (${column.javaType} == "BigDecimal")
import java.math.BigDecimal;
#break
#end
#end
## 处理 LocalDateTime 字段的引入
#foreach ($column in $columns)
#if (${column.listOperation} && ${column.javaType} == "LocalDateTime")
import java.time.LocalDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
#break
#end
#end
## 字段模板
#macro(columnTpl $prefix $prefixStr)
@Schema(description = "${prefixStr}${column.columnComment}"#if ("$!column.example" != ""), example = "${column.example}"#end)
private ${column.javaType}#if ("$!prefix" != "") ${prefix}${JavaField}#else ${column.javaField}#end;
#end
@Schema(description = "${sceneEnum.name} - ${table.classComment}列表 Request VO")
@Data
public class ${sceneEnum.prefixClass}${table.className}ListReqVO {
#foreach ($column in $columns)
#if (${column.listOperation})##查询操作
#if (${column.listOperationCondition} == "BETWEEN")## 情况一Between 的时候
@Schema(description = "${column.columnComment}"#if ("$!column.example" != ""), example = "${column.example}"#end)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private ${column.javaType}[] ${column.javaField};
#else##情况二,非 Between 的时间
#columnTpl('', '')
#end
#end
#end
}

View File

@@ -1,45 +0,0 @@
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import ${PageParamClassName};
#foreach ($column in $columns)
#if (${column.javaType} == "BigDecimal")
import java.math.BigDecimal;
#break
#end
#end
## 处理 LocalDateTime 字段的引入
#foreach ($column in $columns)
#if (${column.listOperationCondition} && ${column.javaType} == "LocalDateTime")
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
#break
#end
#end
## 字段模板
#macro(columnTpl $prefix $prefixStr)
@Schema(description = "${prefixStr}${column.columnComment}"#if ("$!column.example" != ""), example = "${column.example}"#end)
private ${column.javaType}#if ("$!prefix" != "") ${prefix}${JavaField}#else ${column.javaField}#end;
#end
@Schema(description = "${sceneEnum.name} - ${table.classComment}分页 Request VO")
@Data
public class ${sceneEnum.prefixClass}${table.className}PageReqVO extends PageParam {
#foreach ($column in $columns)
#if (${column.listOperation})##查询操作
#if (${column.listOperationCondition} == "BETWEEN")## 情况一Between 的时候
@Schema(description = "${column.columnComment}"#if ("$!column.example" != ""), example = "${column.example}"#end)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private ${column.javaType}[] ${column.javaField};
#else##情况二,非 Between 的时间
#columnTpl('', '')
#end
#end
#end
}

View File

@@ -1,53 +0,0 @@
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
## 处理 BigDecimal 字段的引入
#foreach ($column in $columns)
#if (${column.javaType} == "BigDecimal")
import java.math.BigDecimal;
#break
#end
#end
## 处理 LocalDateTime 字段的引入
#foreach ($column in $columns)
#if (${column.listOperationResult} && ${column.javaType} == "LocalDateTime")
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
#break
#end
#end
## 处理 Excel 导出
import cn.idev.excel.annotation.*;
#foreach ($column in $columns)
#if ("$!column.dictType" != "")## 有设置数据字典
import ${DictFormatClassName};
import ${DictConvertClassName};
#break
#end
#end
@Schema(description = "${sceneEnum.name} - ${table.classComment} Response VO")
@Data
@ExcelIgnoreUnannotated
public class ${sceneEnum.prefixClass}${table.className}RespVO {
## 逐个处理字段
#foreach ($column in $columns)
#if (${column.listOperationResult})
## 1. 处理 Swagger 注解
@Schema(description = "${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != ""), example = "${column.example}"#end)
## 2. 处理 Excel 导出
#if ("$!column.dictType" != "")##处理枚举值
@ExcelProperty(value = "${column.columnComment}", converter = DictConvert.class)
@DictFormat("${column.dictType}") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
#else
@ExcelProperty("${column.columnComment}")
#end
## 3. 处理字段定义
private ${column.javaType} ${column.javaField};
#end
#end
}

View File

@@ -1,64 +0,0 @@
package ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import ${jakartaPackage}.validation.constraints.*;
## 处理 BigDecimal 字段的引入
#foreach ($column in $columns)
#if (${column.javaType} == "BigDecimal")
import java.math.BigDecimal;
#break
#end
#end
## 处理 LocalDateTime 字段的引入
#foreach ($column in $columns)
#if ((${column.createOperation} || ${column.updateOperation}) && ${column.javaType} == "LocalDateTime")
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
#break
#end
#end
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
#end
@Schema(description = "${sceneEnum.name} - ${table.classComment}新增/修改 Request VO")
@Data
public class ${sceneEnum.prefixClass}${table.className}SaveReqVO {
## 逐个处理字段
#foreach ($column in $columns)
#if (${column.createOperation} || ${column.updateOperation})
## 1. 处理 Swagger 注解
@Schema(description = "${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != ""), example = "${column.example}"#end)
## 2. 处理 Validator 参数校验
#if (!${column.nullable} && !${column.primaryKey})
#if (${column.javaType} == 'String')
@NotEmpty(message = "${column.columnComment}不能为空")
#else
@NotNull(message = "${column.columnComment}不能为空")
#end
#end
## 3. 处理字段定义
private ${column.javaType} ${column.javaField};
#end
#end
## 特殊:主子表专属逻辑(非 ERP 模式)
#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#if ( $subTable.subJoinMany)
@Schema(description = "${subTable.classComment}列表")
private List<${subTable.className}DO> ${subClassNameVars.get($index)}s;
#else
@Schema(description = "${subTable.classComment}")
private ${subTable.className}DO ${subClassNameVars.get($index)};
#end
#end
#end
}

View File

@@ -1,103 +0,0 @@
package ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName};
import lombok.*;
import java.util.*;
#foreach ($column in $columns)
#if (${column.javaType} == "BigDecimal")
import java.math.BigDecimal;
#end
#if (${column.javaType} == "LocalDateTime")
import java.time.LocalDateTime;
#end
#end
import com.baomidou.mybatisplus.annotation.*;
import ${BaseDOClassName};
## 处理 Excel 导出 + Schema 注解(仅 DO 模式)
#if ($voType == 20)
import io.swagger.v3.oas.annotations.media.Schema;
import cn.idev.excel.annotation.*;
#foreach ($column in $columns)
#if ("$!column.dictType" != "")## 有设置数据字典
import ${DictFormatClassName};
import ${DictConvertClassName};
#break
#end
#end
#end
/**
* ${table.classComment} DO
*
* @author ${table.author}
*/
@TableName("${table.tableName.toLowerCase()}")
@KeySequence("${table.tableName.toLowerCase()}_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
## 处理 Excel 导出 + Schema 注解(仅 DO 模式)
#if ($voType == 20)
@Schema(description = "${sceneEnum.name} - ${table.classComment} Response VO")
@ExcelIgnoreUnannotated
#end
public class ${table.className}DO extends BaseDO {
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
public static final Long ${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT = 0L;
#end
#foreach ($column in $columns)
#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段
/**
* ${column.columnComment}
#if ("$!column.dictType" != "")##处理枚举值
*
* 枚举 {@link TODO ${column.dictType} 对应的类}
#end
*/
#if (${column.primaryKey})##处理主键
@TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end
#end
#if ($voType == 20)
## 1. 处理 Swagger 注解
@Schema(description = "${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != ""), example = "${column.example}"#end)
## 2. 处理 Excel 导出
#if ("$!column.dictType" != "")##处理枚举值
@ExcelProperty(value = "${column.columnComment}", converter = DictConvert.class)
@DictFormat("${column.dictType}") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
#else
@ExcelProperty("${column.columnComment}")
#end
#end
## 3. 处理字段定义
private ${column.javaType} ${column.javaField};
#end
#end
## 特殊:主子表专属逻辑(非 ERP 模式)
#if ( $voType == 20 && $subTables && $subTables.size() > 0 && $table.templateType != 11 )
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#if ( $subTable.subJoinMany)
/**
* ${subTable.classComment}列表
*/
@Schema(description = "${subTable.classComment}列表")
@TableField(exist = false)
private List<${subTable.className}DO> ${subClassNameVars.get($index)}s;
#else
/**
* ${subTable.classComment}
*/
@Schema(description = "${subTable.classComment}")
@TableField(exist = false)
private ${subTable.className}DO ${subClassNameVars.get($index)};
#end
#end
#end
}

View File

@@ -1,69 +0,0 @@
#set ($subTable = $subTables.get($subIndex))##当前表
#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
package ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName};
import lombok.*;
import java.util.*;
#foreach ($column in $subColumns)
#if (${column.javaType} == "BigDecimal")
import java.math.BigDecimal;
#end
#if (${column.javaType} == "LocalDateTime")
import java.time.LocalDateTime;
#end
#end
import com.baomidou.mybatisplus.annotation.*;
import ${BaseDOClassName};
## 处理 Schema 注解(仅 DO 模式)
#if ($voType == 20)
import io.swagger.v3.oas.annotations.media.Schema;
#foreach ($column in $columns)
#if ("$!column.dictType" != "")## 有设置数据字典
import ${DictFormatClassName};
import ${DictConvertClassName};
#break
#end
#end
#end
/**
* ${subTable.classComment} DO
*
* @author ${subTable.author}
*/
@TableName("${subTable.tableName.toLowerCase()}")
@KeySequence("${subTable.tableName.toLowerCase()}_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
## 处理 Schema 注解(仅 DO 模式)
#if ($voType == 20)
@Schema(description = "${sceneEnum.name} - ${table.classComment} Response VO")
#end
public class ${subTable.className}DO extends BaseDO {
#foreach ($column in $subColumns)
#if (!${baseDOFields.contains(${column.javaField})})##排除 BaseDO 的字段
/**
* ${column.columnComment}
#if ("$!column.dictType" != "")##处理枚举值
*
* 枚举 {@link TODO ${column.dictType} 对应的类}
#end
*/
#if (${column.primaryKey})##处理主键
@TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end
#end
#if ($voType == 20)
## 1. 处理 Swagger 注解
@Schema(description = "${column.columnComment}"#if (!${column.nullable}), requiredMode = Schema.RequiredMode.REQUIRED#end#if ("$!column.example" != ""), example = "${column.example}"#end)
#end
## 2. 处理字段定义
private ${column.javaType} ${column.javaField};
#end
#end
}

View File

@@ -1,82 +0,0 @@
package ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName};
import java.util.*;
import ${PageResultClassName};
import ${QueryWrapperClassName};
import ${BaseMapperClassName};
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
import org.apache.ibatis.annotations.Mapper;
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
## 字段模板
#macro(listCondition)
#foreach ($column in $columns)
#if (${column.listOperation})
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
#if (${column.listOperationCondition} == "=")##情况一,= 的时候
.eqIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#if (${column.listOperationCondition} == "!=")##情况二,!= 的时候
.neIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#if (${column.listOperationCondition} == ">")##情况三,> 的时候
.gtIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#if (${column.listOperationCondition} == ">=")##情况四,>= 的时候
.geIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#if (${column.listOperationCondition} == "<")##情况五,< 的时候
.ltIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#if (${column.listOperationCondition} == "<=")##情况五,<= 的时候
.leIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#if (${column.listOperationCondition} == "LIKE")##情况七Like 的时候
.likeIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#if (${column.listOperationCondition} == "BETWEEN")##情况八Between 的时候
.betweenIfPresent(${table.className}DO::get${JavaField}, reqVO.get${JavaField}())
#end
#end
#end
#end
/**
* ${table.classComment} Mapper
*
* @author ${table.author}
*/
@Mapper
public interface ${table.className}Mapper extends BaseMapperX<${table.className}DO> {
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
default PageResult<${table.className}DO> selectPage(${sceneEnum.prefixClass}${table.className}PageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<${table.className}DO>()
#listCondition()
.orderByDesc(${table.className}DO::getId));## 大多数情况下id 倒序
}
#else
default List<${table.className}DO> selectList(${sceneEnum.prefixClass}${table.className}ListReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<${table.className}DO>()
#listCondition()
.orderByDesc(${table.className}DO::getId));## 大多数情况下id 倒序
}
#end
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
default ${table.className}DO selectBy${TreeParentJavaField}And${TreeNameJavaField}(Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) {
return selectOne(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField}, ${table.className}DO::get${TreeNameJavaField}, ${treeNameColumn.javaField});
}
default Long selectCountBy${TreeParentJavaField}(${treeParentColumn.javaType} ${treeParentColumn.javaField}) {
return selectCount(${table.className}DO::get${TreeParentJavaField}, ${treeParentColumn.javaField});
}
#end
}

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@@ -1,63 +0,0 @@
#set ($subTable = $subTables.get($subIndex))##当前表
#set ($subColumns = $subJoinColumnsList.get($subIndex))##当前字段数组
#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
package ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName};
import java.util.*;
import ${PageResultClassName};
import ${PageParamClassName};
import ${QueryWrapperClassName};
import ${BaseMapperClassName};
import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
import org.apache.ibatis.annotations.Mapper;
/**
* ${subTable.classComment} Mapper
*
* @author ${subTable.author}
*/
@Mapper
public interface ${subTable.className}Mapper extends BaseMapperX<${subTable.className}DO> {
## 情况一MASTER_ERP 时,需要分查询页子表
#if ( $table.templateType == 11 )
default PageResult<${subTable.className}DO> selectPage(PageParam reqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return selectPage(reqVO, new LambdaQueryWrapperX<${subTable.className}DO>()
.eq(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField})
.orderByDesc(${subTable.className}DO::getId));## 大多数情况下id 倒序
}
## 主表与子表是一对一时
#if (!$subTable.subJoinMany)
default ${subTable.className}DO selectBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return selectOne(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});
}
#end
## 情况二:非 MASTER_ERP 时,需要列表查询子表
#else
#if ( $subTable.subJoinMany)
default List<${subTable.className}DO> selectListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return selectList(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});
}
#else
default ${subTable.className}DO selectBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return selectOne(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});
}
#end
#end
default int deleteBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return delete(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField});
}
#if ( $table.templateType != 2 && $deleteBatchEnable)
default int deleteBy${SubJoinColumnName}s(List<${subJoinColumn.javaType}> ${subJoinColumn.javaField}s) {
return deleteBatch(${subTable.className}DO::get${SubJoinColumnName}, ${subJoinColumn.javaField}s);
}
#end
}

View File

@@ -1,22 +0,0 @@
// TODO 待办:请将下面的错误码复制到 msgpush-module-${table.moduleName} 模块的 ErrorCodeConstants 类中。注意请给“TODO 补充编号”设置一个错误码编号!!!
// ========== ${table.classComment} TODO 补充编号 ==========
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, "${table.classComment}不存在");
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子${table.classComment},无法删除");
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级${table.classComment}不存在");
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父${table.classComment}");
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该${treeNameColumn.columnComment}的${table.classComment}");
ErrorCode ${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子${table.className}为父${table.className}");
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 )## 特殊ERP 情况
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index))
ErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS = new ErrorCode(TODO 补充编号, "${subTable.classComment}不存在");
#if ( !$subTable.subJoinMany )
ErrorCode ${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS = new ErrorCode(TODO 补充编号, "${subTable.classComment}已存在");
#end
#end
#end

View File

@@ -1,165 +0,0 @@
package ${basePackage}.module.${table.moduleName}.service.${table.businessName};
import java.util.*;
import ${jakartaPackage}.validation.*;
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
#end
import ${PageResultClassName};
import ${PageParamClassName};
/**
* ${table.classComment} Service 接口
*
* @author ${table.author}
*/
public interface ${table.className}Service {
/**
* 创建${table.classComment}
*
* @param ${saveReqVOVar} 创建信息
* @return 编号
*/
${primaryColumn.javaType} create${simpleClassName}(@Valid ${saveReqVOClass} ${saveReqVOVar});
/**
* 更新${table.classComment}
*
* @param ${updateReqVOVar} 更新信息
*/
void update${simpleClassName}(@Valid ${updateReqVOClass} ${updateReqVOVar});
/**
* 删除${table.classComment}
*
* @param id 编号
*/
void delete${simpleClassName}(${primaryColumn.javaType} id);
#if ( $table.templateType != 2 && $deleteBatchEnable)
/**
* 批量删除${table.classComment}
*
* @param ids 编号
*/
void delete${simpleClassName}ListByIds(List<${primaryColumn.javaType}> ids);
#end
/**
* 获得${table.classComment}
*
* @param id 编号
* @return ${table.classComment}
*/
${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id);
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
/**
* 获得${table.classComment}分页
*
* @param pageReqVO 分页查询
* @return ${table.classComment}分页
*/
PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO);
#else
/**
* 获得${table.classComment}列表
*
* @param listReqVO 查询条件
* @return ${table.classComment}列表
*/
List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO);
#end
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#set ($subClassNameVar = $subClassNameVars.get($index))
// ==================== 子表($subTable.classComment ====================
## 情况一MASTER_ERP 时,需要分查询页子表
#if ( $table.templateType == 11 )
/**
* 获得${subTable.classComment}分页
*
* @param pageReqVO 分页查询
* @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}
* @return ${subTable.classComment}分页
*/
PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField});
## 情况二:非 MASTER_ERP 时,需要列表查询子表
#else
#if ( $subTable.subJoinMany )
/**
* 获得${subTable.classComment}列表
*
* @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}
* @return ${subTable.classComment}列表
*/
List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField});
#else
/**
* 获得${subTable.classComment}
*
* @param ${subJoinColumn.javaField} ${subJoinColumn.columnComment}
* @return ${subTable.classComment}
*/
${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField});
#end
#end
## 特殊MASTER_ERP 时,支持单个的新增、修改、删除操作
#if ( $table.templateType == 11 )
/**
* 创建${subTable.classComment}
*
* @param ${subClassNameVar} 创建信息
* @return 编号
*/
${subPrimaryColumn.javaType} create${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar});
/**
* 更新${subTable.classComment}
*
* @param ${subClassNameVar} 更新信息
*/
void update${subSimpleClassName}(@Valid ${subTable.className}DO ${subClassNameVar});
/**
* 删除${subTable.classComment}
*
* @param id 编号
*/
void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id);
#if ($deleteBatchEnable)
/**
* 批量删除${subTable.classComment}
*
* @param ids 编号
*/
void delete${subSimpleClassName}ListByIds(List<${subPrimaryColumn.javaType}> ids);
#end
/**
* 获得${subTable.classComment}
*
* @param id 编号
* @return ${subTable.classComment}
*/
${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id);
#end
#end
}

View File

@@ -1,419 +0,0 @@
package ${basePackage}.module.${table.moduleName}.service.${table.businessName};
import cn.hutool.core.collection.CollUtil;
import org.springframework.stereotype.Service;
import ${jakartaPackage}.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
import ${basePackage}.module.${subTable.moduleName}.dal.dataobject.${subTable.businessName}.${subTable.className}DO;
#end
import ${PageResultClassName};
import ${PageParamClassName};
import ${BeanUtils};
import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper;
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
import ${basePackage}.module.${subTable.moduleName}.dal.mysql.${subTable.businessName}.${subTable.className}Mapper;
#end
import static ${ServiceExceptionUtilClassName}.exception;
import static ${CollectionUtilsClassName}.convertList;
import static ${CollectionUtilsClassName}.diffList;
import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
/**
* ${table.classComment} Service 实现类
*
* @author ${table.author}
*/
@Service
@Validated
public class ${table.className}ServiceImpl implements ${table.className}Service {
@Resource
private ${table.className}Mapper ${classNameVar}Mapper;
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
@Resource
private ${subTable.className}Mapper ${subClassNameVars.get($index)}Mapper;
#end
@Override
## 特殊:主子表专属逻辑(非 ERP 模式)
#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
@Transactional(rollbackFor = Exception.class)
#end
public ${primaryColumn.javaType} create${simpleClassName}(${saveReqVOClass} ${saveReqVOVar}) {
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
// 校验${treeParentColumn.columnComment}的有效性
validateParent${simpleClassName}(null, ${saveReqVOVar}.get${TreeParentJavaField}());
// 校验${treeNameColumn.columnComment}的唯一性
validate${simpleClassName}${TreeNameJavaField}Unique(null, ${saveReqVOVar}.get${TreeParentJavaField}(), ${saveReqVOVar}.get${TreeNameJavaField}());
#end
// 插入
#if ($voType == 10)
${table.className}DO ${classNameVar} = BeanUtils.toBean(createReqVO, ${table.className}DO.class);
${classNameVar}Mapper.insert(${classNameVar});
#else
${saveReqVOVar}.clean() // 清理掉创建、更新时间等相关属性值
${classNameVar}Mapper.insert(${saveReqVOVar});
#end
## 特殊:主子表专属逻辑(非 ERP 模式)
#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
// 插入子表
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#if ( $subTable.subJoinMany)
create${subSimpleClassName}List(${classNameVar}.getId(), ${saveReqVOVar}.get${subSimpleClassNames.get($index)}s());
#else
create${subSimpleClassName}(${classNameVar}.getId(), ${saveReqVOVar}.get${subSimpleClassNames.get($index)}());
#end
#end
#end
// 返回
return ${classNameVar}.getId();
}
@Override
## 特殊:主子表专属逻辑(非 ERP 模式)
#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11 )
@Transactional(rollbackFor = Exception.class)
#end
public void update${simpleClassName}(${updateReqVOClass} ${updateReqVOVar}) {
// 校验存在
validate${simpleClassName}Exists(${updateReqVOVar}.getId());
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
// 校验${treeParentColumn.columnComment}的有效性
validateParent${simpleClassName}(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${TreeParentJavaField}());
// 校验${treeNameColumn.columnComment}的唯一性
validate${simpleClassName}${TreeNameJavaField}Unique(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${TreeParentJavaField}(), ${updateReqVOVar}.get${TreeNameJavaField}());
#end
// 更新
#if ($voType == 10)
${table.className}DO updateObj = BeanUtils.toBean(updateReqVO, ${table.className}DO.class);
${classNameVar}Mapper.updateById(updateObj);
#else
${updateReqVOVar}.clean() // 清理掉创建、更新时间等相关属性值
${classNameVar}Mapper.updateById(${updateReqVOVar});
#end
## 特殊:主子表专属逻辑(非 ERP 模式)
#if ( $subTables && $subTables.size() > 0 && $table.templateType != 11)
// 更新子表
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#if ( $subTable.subJoinMany)
update${subSimpleClassName}List(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${subSimpleClassNames.get($index)}s());
#else
update${subSimpleClassName}(${updateReqVOVar}.getId(), ${updateReqVOVar}.get${subSimpleClassNames.get($index)}());
#end
#end
#end
}
@Override
## 特殊:主子表专属逻辑
#if ( $subTables && $subTables.size() > 0)
@Transactional(rollbackFor = Exception.class)
#end
public void delete${simpleClassName}(${primaryColumn.javaType} id) {
// 校验存在
validate${simpleClassName}Exists(id);
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
#set ($ParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
// 校验是否有子${table.classComment}
if (${classNameVar}Mapper.selectCountBy${ParentJavaField}(id) > 0) {
throw exception(${simpleClassName_underlineCase.toUpperCase()}_EXITS_CHILDREN);
}
#end
// 删除
${classNameVar}Mapper.deleteById(id);
## 特殊:主子表专属逻辑
#if ( $subTables && $subTables.size() > 0)
// 删除子表
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
delete${subSimpleClassName}By${SubJoinColumnName}(id);
#end
#end
}
#if ( $table.templateType != 2 && $deleteBatchEnable)
@Override
## 特殊:主子表专属逻辑
#if ( $subTables && $subTables.size() > 0)
@Transactional(rollbackFor = Exception.class)
#end
public void delete${simpleClassName}ListByIds(List<${primaryColumn.javaType}> ids) {
// 删除
${classNameVar}Mapper.deleteByIds(ids);
## 特殊:主子表专属逻辑
#if ( $subTables && $subTables.size() > 0)
// 删除子表
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
delete${subSimpleClassName}By${SubJoinColumnName}s(ids);
#end
#end
}
#end
private void validate${simpleClassName}Exists(${primaryColumn.javaType} id) {
if (${classNameVar}Mapper.selectById(id) == null) {
throw exception(${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
}
}
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
#set ($TreeParentJavaField = $treeParentColumn.javaField.substring(0,1).toUpperCase() + ${treeParentColumn.javaField.substring(1)})##首字母大写
#set ($TreeNameJavaField = $treeNameColumn.javaField.substring(0,1).toUpperCase() + ${treeNameColumn.javaField.substring(1)})##首字母大写
private void validateParent${simpleClassName}(Long id, Long ${treeParentColumn.javaField}) {
if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) {
return;
}
// 1. 不能设置自己为父${table.classComment}
if (Objects.equals(id, ${treeParentColumn.javaField})) {
throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_ERROR);
}
// 2. 父${table.classComment}不存在
${simpleClassName}DO parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField});
if (parent${simpleClassName} == null) {
throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_NOT_EXITS);
}
// 3. 递归校验父${table.classComment},如果父${table.classComment}是自己的子${table.classComment},则报错,避免形成环路
if (id == null) { // id 为空,说明新增,不需要考虑环路
return;
}
for (int i = 0; i < Short.MAX_VALUE; i++) {
// 3.1 校验环路
${treeParentColumn.javaField} = parent${simpleClassName}.get${TreeParentJavaField}();
if (Objects.equals(id, ${treeParentColumn.javaField})) {
throw exception(${simpleClassName_underlineCase.toUpperCase()}_PARENT_IS_CHILD);
}
// 3.2 继续递归下一级父${table.classComment}
if (${treeParentColumn.javaField} == null || ${simpleClassName}DO.${treeParentColumn_javaField_underlineCase.toUpperCase()}_ROOT.equals(${treeParentColumn.javaField})) {
break;
}
parent${simpleClassName} = ${classNameVar}Mapper.selectById(${treeParentColumn.javaField});
if (parent${simpleClassName} == null) {
break;
}
}
}
private void validate${simpleClassName}${TreeNameJavaField}Unique(Long id, Long ${treeParentColumn.javaField}, String ${treeNameColumn.javaField}) {
${simpleClassName}DO ${classNameVar} = ${classNameVar}Mapper.selectBy${TreeParentJavaField}And${TreeNameJavaField}(${treeParentColumn.javaField}, ${treeNameColumn.javaField});
if (${classNameVar} == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的${table.classComment}
if (id == null) {
throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE);
}
if (!Objects.equals(${classNameVar}.getId(), id)) {
throw exception(${simpleClassName_underlineCase.toUpperCase()}_${treeNameColumn_javaField_underlineCase.toUpperCase()}_DUPLICATE);
}
}
#end
@Override
public ${table.className}DO get${simpleClassName}(${primaryColumn.javaType} id) {
return ${classNameVar}Mapper.selectById(id);
}
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
@Override
public PageResult<${table.className}DO> get${simpleClassName}Page(${sceneEnum.prefixClass}${table.className}PageReqVO pageReqVO) {
return ${classNameVar}Mapper.selectPage(pageReqVO);
}
#else
@Override
public List<${table.className}DO> get${simpleClassName}List(${sceneEnum.prefixClass}${table.className}ListReqVO listReqVO) {
return ${classNameVar}Mapper.selectList(listReqVO);
}
#end
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($simpleClassNameUnderlineCase = $simpleClassNameUnderlineCases.get($index))
#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#set ($subClassNameVar = $subClassNameVars.get($index))
// ==================== 子表($subTable.classComment ====================
## 情况一MASTER_ERP 时,需要分查询页子表
#if ( $table.templateType == 11 )
@Override
public PageResult<${subTable.className}DO> get${subSimpleClassName}Page(PageParam pageReqVO, ${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return ${subClassNameVars.get($index)}Mapper.selectPage(pageReqVO, ${subJoinColumn.javaField});
}
## 情况二:非 MASTER_ERP 时,需要列表查询子表
#else
#if ( $subTable.subJoinMany )
@Override
public List<${subTable.className}DO> get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return ${subClassNameVars.get($index)}Mapper.selectListBy${SubJoinColumnName}(${subJoinColumn.javaField});
}
#else
@Override
public ${subTable.className}DO get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaType} ${subJoinColumn.javaField}) {
return ${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subJoinColumn.javaField});
}
#end
#end
## 情况一MASTER_ERP 时,支持单个的新增、修改、删除操作
#if ( $table.templateType == 11 )
@Override
public ${subPrimaryColumn.javaType} create${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) {
## 特殊:一对一时,需要保证只有一条,不能重复插入
#if ( !$subTable.subJoinMany)
// 校验是否已经存在
if (${subClassNameVars.get($index)}Mapper.selectBy${SubJoinColumnName}(${subClassNameVar}.get${SubJoinColumnName}()) != null) {
throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_EXISTS);
}
// 插入
#end
${subClassNameVar}.clean(); // 清理掉创建、更新时间等相关属性值
${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar});
return ${subClassNameVar}.getId();
}
@Override
public void update${subSimpleClassName}(${subTable.className}DO ${subClassNameVar}) {
// 校验存在
validate${subSimpleClassName}Exists(${subClassNameVar}.getId());
// 更新
${subClassNameVar}.clean(); // 解决更新情况下updateTime 不更新
${subClassNameVars.get($index)}Mapper.updateById(${subClassNameVar});
}
@Override
public void delete${subSimpleClassName}(${subPrimaryColumn.javaType} id) {
// 删除
${subClassNameVars.get($index)}Mapper.deleteById(id);
}
#if ($deleteBatchEnable)
@Override
public void delete${subSimpleClassName}ListByIds(List<${subPrimaryColumn.javaType}> ids) {
// 删除
${subClassNameVars.get($index)}Mapper.deleteByIds(ids);
}
#end
@Override
public ${subTable.className}DO get${subSimpleClassName}(${subPrimaryColumn.javaType} id) {
return ${subClassNameVars.get($index)}Mapper.selectById(id);
}
private void validate${subSimpleClassName}Exists(${subPrimaryColumn.javaType} id) {
if (${subClassNameVar}Mapper.selectById(id) == null) {
throw exception(${simpleClassNameUnderlineCase.toUpperCase()}_NOT_EXISTS);
}
}
## 情况二:非 MASTER_ERP 时,支持批量的新增、修改操作
#else
#if ( $subTable.subJoinMany)
private void create${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) {
list.forEach(o -> o.set${SubJoinColumnName}(${subJoinColumn.javaField}).clean());
${subClassNameVars.get($index)}Mapper.insertBatch(list);
}
private void update${subSimpleClassName}List(${primaryColumn.javaType} ${subJoinColumn.javaField}, List<${subTable.className}DO> list) {
list.forEach(o -> o.set${SubJoinColumnName}(${subJoinColumn.javaField}).clean());
List<${subTable.className}DO> oldList = ${subClassNameVar}Mapper.selectListBy${SubJoinColumnName}(${subJoinColumn.javaField});
List<List<${subTable.className}DO>> diffList = diffList(oldList, list, (oldVal, newVal) -> {
boolean same = ObjectUtil.equal(oldVal.getId(), newVal.getId());
if (same) {
newVal.setId(oldVal.getId()).clean(); // 解决更新情况下updateTime 不更新
}
return same;
});
// 第二步,批量添加、修改、删除
if (CollUtil.isNotEmpty(diffList.get(0))) {
${subClassNameVar}Mapper.insertBatch(diffList.get(0));
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
${subClassNameVar}Mapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
${subClassNameVar}Mapper.deleteByIds(convertList(diffList.get(2), ${subTable.className}DO::getId));
}
}
#else
private void create${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {
if (${subClassNameVar} == null) {
return;
}
${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField});
${subClassNameVars.get($index)}Mapper.insert(${subClassNameVar});
}
private void update${subSimpleClassName}(${primaryColumn.javaType} ${subJoinColumn.javaField}, ${subTable.className}DO ${subClassNameVar}) {
if (${subClassNameVar} == null) {
return;
}
${subClassNameVar}.set$SubJoinColumnName(${subJoinColumn.javaField}).clean();// 解决更新情况下updateTime 不更新
${subClassNameVars.get($index)}Mapper.insertOrUpdate(${subClassNameVar});
}
#end
#end
private void delete${subSimpleClassName}By${SubJoinColumnName}(${primaryColumn.javaType} ${subJoinColumn.javaField}) {
${subClassNameVars.get($index)}Mapper.deleteBy${SubJoinColumnName}(${subJoinColumn.javaField});
}
#if ( $table.templateType != 2 && $deleteBatchEnable)
private void delete${subSimpleClassName}By${SubJoinColumnName}s(List<${primaryColumn.javaType}> ${subJoinColumn.javaField}s) {
${subClassNameVars.get($index)}Mapper.deleteBy${SubJoinColumnName}s(${subJoinColumn.javaField}s);
}
#end
#end
}

View File

@@ -1,167 +0,0 @@
package ${basePackage}.module.${table.moduleName}.service.${table.businessName};
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import ${jakartaPackage}.annotation.Resource;
import ${baseFrameworkPackage}.test.core.ut.BaseDbUnitTest;
import ${basePackage}.module.${table.moduleName}.controller.${sceneEnum.basePackage}.${table.businessName}.vo.*;
import ${basePackage}.module.${table.moduleName}.dal.dataobject.${table.businessName}.${table.className}DO;
import ${basePackage}.module.${table.moduleName}.dal.mysql.${table.businessName}.${table.className}Mapper;
import ${PageResultClassName};
import ${jakartaPackage}.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*;
import static ${baseFrameworkPackage}.test.core.util.AssertUtils.*;
import static ${baseFrameworkPackage}.test.core.util.RandomUtils.*;
import static ${LocalDateTimeUtilsClassName}.*;
import static ${ObjectUtilsClassName}.*;
import static ${DateUtilsClassName}.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
## 字段模板
#macro(getPageCondition $VO)
// mock 数据
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class, o -> { // 等会查询到
#foreach ($column in $columns)
#if (${column.listOperation})
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
o.set$JavaField(null);
#end
#end
});
${classNameVar}Mapper.insert(db${simpleClassName});
#foreach ($column in $columns)
#if (${column.listOperation})
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
// 测试 ${column.javaField} 不匹配
${classNameVar}Mapper.insert(cloneIgnoreId(db${simpleClassName}, o -> o.set$JavaField(null)));
#end
#end
// 准备参数
${sceneEnum.prefixClass}${table.className}${VO} reqVO = new ${sceneEnum.prefixClass}${table.className}${VO}();
#foreach ($column in $columns)
#if (${column.listOperation})
#set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写
#if (${column.listOperationCondition} == "BETWEEN")## BETWEEN 的情况
reqVO.set${JavaField}(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
#else
reqVO.set$JavaField(null);
#end
#end
#end
#end
/**
* {@link ${table.className}ServiceImpl} 的单元测试类
*
* @author ${table.author}
*/
@Import(${table.className}ServiceImpl.class)
public class ${table.className}ServiceImplTest extends BaseDbUnitTest {
@Resource
private ${table.className}ServiceImpl ${classNameVar}Service;
@Resource
private ${table.className}Mapper ${classNameVar}Mapper;
@Test
public void testCreate${simpleClassName}_success() {
// 准备参数
${sceneEnum.prefixClass}${table.className}SaveReqVO createReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class).setId(null);
// 调用
${primaryColumn.javaType} ${classNameVar}Id = ${classNameVar}Service.create${simpleClassName}(createReqVO);
// 断言
assertNotNull(${classNameVar}Id);
// 校验记录的属性是否正确
${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(${classNameVar}Id);
assertPojoEquals(createReqVO, ${classNameVar}, "id");
}
@Test
public void testUpdate${simpleClassName}_success() {
// mock 数据
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);
${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据
// 准备参数
${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class, o -> {
o.setId(db${simpleClassName}.getId()); // 设置更新的 ID
});
// 调用
${classNameVar}Service.update${simpleClassName}(updateReqVO);
// 校验是否更新正确
${table.className}DO ${classNameVar} = ${classNameVar}Mapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, ${classNameVar});
}
@Test
public void testUpdate${simpleClassName}_notExists() {
// 准备参数
${sceneEnum.prefixClass}${table.className}SaveReqVO updateReqVO = randomPojo(${sceneEnum.prefixClass}${table.className}SaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> ${classNameVar}Service.update${simpleClassName}(updateReqVO), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
}
@Test
public void testDelete${simpleClassName}_success() {
// mock 数据
${table.className}DO db${simpleClassName} = randomPojo(${table.className}DO.class);
${classNameVar}Mapper.insert(db${simpleClassName});// @Sql: 先插入出一条存在的数据
// 准备参数
${primaryColumn.javaType} id = db${simpleClassName}.getId();
// 调用
${classNameVar}Service.delete${simpleClassName}(id);
// 校验数据不存在了
assertNull(${classNameVar}Mapper.selectById(id));
}
@Test
public void testDelete${simpleClassName}_notExists() {
// 准备参数
${primaryColumn.javaType} id = random${primaryColumn.javaType}Id();
// 调用, 并断言异常
assertServiceException(() -> ${classNameVar}Service.delete${simpleClassName}(id), ${simpleClassName_underlineCase.toUpperCase()}_NOT_EXISTS);
}
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGet${simpleClassName}Page() {
#getPageCondition("PageReqVO")
// 调用
PageResult<${table.className}DO> pageResult = ${classNameVar}Service.get${simpleClassName}Page(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(db${simpleClassName}, pageResult.getList().get(0));
}
#else
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGet${simpleClassName}List() {
#getPageCondition("ListReqVO")
// 调用
List<${table.className}DO> list = ${classNameVar}Service.get${simpleClassName}List(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(db${simpleClassName}, list.get(0));
}
#end
}

View File

@@ -1,35 +0,0 @@
-- 将该建表 SQL 语句,添加到 msgpush-module-${table.moduleName}-biz 模块的 test/resources/sql/create_tables.sql 文件里
CREATE TABLE IF NOT EXISTS "${table.tableName.toLowerCase()}" (
#foreach ($column in $columns)
#if (${column.javaType} == 'Long')
#set ($dataType='bigint')
#elseif (${column.javaType} == 'Integer')
#set ($dataType='int')
#elseif (${column.javaType} == 'Boolean')
#set ($dataType='bit')
#elseif (${column.javaType} == 'Date')
#set ($dataType='datetime')
#else
#set ($dataType='varchar')
#end
#if (${column.primaryKey})##处理主键
"${column.javaField}"#if (${column.javaType} == 'String') ${dataType} NOT NULL#else ${dataType} NOT NULL GENERATED BY DEFAULT AS IDENTITY#end,
#else
#if (${column.columnName} == 'create_time')
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
#elseif (${column.columnName} == 'update_time')
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
#elseif (${column.columnName} == 'creator' || ${column.columnName} == 'updater')
"${column.columnName}" ${dataType} DEFAULT '',
#elseif (${column.columnName} == 'deleted')
"deleted" bit NOT NULL DEFAULT FALSE,
#else
"${column.columnName.toLowerCase()}" ${dataType}#if (${column.nullable} == false) NOT NULL#end,
#end
#end
#end
PRIMARY KEY ("${primaryColumn.columnName.toLowerCase()}")
) COMMENT '${table.tableComment}';
-- 将该删表 SQL 语句,添加到 msgpush-module-${table.moduleName}-biz 模块的 test/resources/sql/clean.sql 文件里
DELETE FROM "${table.tableName}";

View File

@@ -1,181 +0,0 @@
## 通用变量定义
#set ($functionNames = ['查询', '创建', '更新', '删除', '导出'])
#set ($functionOps = ['query', 'create', 'update', 'delete', 'export'])
##
## 宏定义:生成按钮 SQL通用部分
#macro(insertButtonSql $parentIdVar)
#foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, ${parentIdVar},
'', '', '', 0
);
#end
#end
##
## ======================= MySQL / OceanBase =======================
#if ($dbType.name() == 'MYSQL' || $dbType.name() == 'OCEAN_BASE')
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
);
-- 按钮父菜单ID
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
#foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
'', '', '', 0
);
#end
##
## ======================= Oracle / 达梦 DM =======================
#elseif ($dbType.name() == 'ORACLE' || $dbType.name() == 'DM')
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
);
-- 按钮父菜单ID
-- 说明Oracle/达梦 使用序列获取上一个插入的 ID
DECLARE
v_parent_id NUMBER;
BEGIN
SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;
-- 按钮 SQL
#foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,
'', '', '', 0
);
#end
END;
/
##
## ======================= PostgreSQL / 人大金仓 KingbaseES =======================
#elseif ($dbType.name() == 'POSTGRE_SQL' || $dbType.name() == 'KINGBASE_ES')
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
);
-- 按钮父菜单ID
-- 说明PostgreSQL/KingbaseES 使用 lastval() 获取上一个插入的序列值
DO $$
DECLARE
v_parent_id BIGINT;
BEGIN
v_parent_id := lastval();
-- 按钮 SQL
#foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, v_parent_id,
'', '', '', 0
);
#end
END $$;
##
## ======================= SQL Server =======================
#elseif ($dbType.name() == 'SQL_SERVER' || $dbType.name() == 'SQL_SERVER2005')
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
);
-- 按钮父菜单ID
-- 说明SQL Server 使用 SCOPE_IDENTITY() 获取上一个插入的自增 ID
DECLARE @parentId BIGINT;
SET @parentId = SCOPE_IDENTITY();
-- 按钮 SQL
#foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
'', '', '', 0
);
#end
##
## ======================= 不支持的数据库类型 =======================
#else
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-- 注意:当前数据库类型 ${dbType.name()} 暂不支持自动生成菜单 SQL
-- 请参考以下 MySQL 语法,手动修改为您的数据库对应的语法
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-- 菜单 SQL
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status, component_name
)
VALUES (
'${table.classComment}管理', '', 2, 0, ${table.parentMenuId},
'${simpleClassName_strikeCase}', '', '${table.moduleName}/${table.businessName}/index', 0, '${table.className}'
);
-- 按钮父菜单ID
-- TODO: 请根据您的数据库类型,修改获取上一个插入 ID 的方式
-- MySQL: SELECT @parentId := LAST_INSERT_ID();
-- Oracle: SELECT system_menu_seq.CURRVAL INTO v_parent_id FROM DUAL;
-- PostgreSQL: SELECT lastval() INTO v_parent_id;
-- SQL Server: SET @parentId = SCOPE_IDENTITY();
SELECT @parentId := LAST_INSERT_ID();
-- 按钮 SQL
#foreach ($functionName in $functionNames)
#set ($index = $foreach.count - 1)
INSERT INTO system_menu(
name, permission, type, sort, parent_id,
path, icon, component, status
)
VALUES (
'${table.classComment}${functionName}', '${permissionPrefix}:${functionOps.get($index)}', 3, $foreach.count, @parentId,
'', '', '', 0
);
#end
#end

View File

@@ -1,160 +0,0 @@
import request from '@/utils/request'
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
// 创建${table.classComment}
export function create${simpleClassName}(data) {
return request({
url: '${baseURL}/create',
method: 'post',
data: data
})
}
// 更新${table.classComment}
export function update${simpleClassName}(data) {
return request({
url: '${baseURL}/update',
method: 'put',
data: data
})
}
// 删除${table.classComment}
export function delete${simpleClassName}(id) {
return request({
url: '${baseURL}/delete?id=' + id,
method: 'delete'
})
}
#if ( $table.templateType != 2 && $deleteBatchEnable)
/** 批量删除${table.classComment} */
export function delete${simpleClassName}List(ids) {
return request({
url: `${baseURL}/delete-list?ids=${ids.join(',')}`,
method: 'delete'
})
}
#end
// 获得${table.classComment}
export function get${simpleClassName}(id) {
return request({
url: '${baseURL}/get?id=' + id,
method: 'get'
})
}
#if ( $table.templateType != 2 )
// 获得${table.classComment}分页
export function get${simpleClassName}Page(params) {
return request({
url: '${baseURL}/page',
method: 'get',
params
})
}
#else
// 获得${table.classComment}列表
export function get${simpleClassName}List(params) {
return request({
url: '${baseURL}/list',
method: 'get',
params
})
}
#end
// 导出${table.classComment} Excel
export function export${simpleClassName}Excel(params) {
return request({
url: '${baseURL}/export-excel',
method: 'get',
params,
responseType: 'blob'
})
}
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
#set ($subClassNameVar = $subClassNameVars.get($index))
// ==================== 子表($subTable.classComment ====================
## 情况一MASTER_ERP 时,需要分查询页子表
#if ($table.templateType == 11)
// 获得${subTable.classComment}分页
export function get${subSimpleClassName}Page(params) {
return request({
url: '${baseURL}/${subSimpleClassName_strikeCase}/page',
method: 'get',
params
})
}
## 情况二:非 MASTER_ERP 时,需要列表查询子表
#else
#if ($subTable.subJoinMany)
// 获得${subTable.classComment}列表
export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}) {
return request({
url: '${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=' + ${subJoinColumn.javaField},
method: 'get'
})
}
#else
// 获得${subTable.classComment}
export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}) {
return request({
url: '${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=' + ${subJoinColumn.javaField},
method: 'get'
})
}
#end
#end
## 特殊MASTER_ERP 时,支持单个的新增、修改、删除操作
#if ($table.templateType == 11)
// 新增${subTable.classComment}
export function create${subSimpleClassName}(data) {
return request({
url: '${baseURL}/${subSimpleClassName_strikeCase}/create',
method: 'post',
data
})
}
// 修改${subTable.classComment}
export function update${subSimpleClassName}(data) {
return request({
url: '${baseURL}/${subSimpleClassName_strikeCase}/update',
method: 'post',
data
})
}
// 删除${subTable.classComment}
export function delete${subSimpleClassName}(id) {
return request({
url: '${baseURL}/${subSimpleClassName_strikeCase}/delete?id=' + id,
method: 'delete'
})
}
#if ($deleteBatchEnable)
/** 批量删除${subTable.classComment} */
export function delete${subSimpleClassName}List(ids) {
return request({
url: `${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`,
method: 'delete'
})
}
#end
// 获得${subTable.classComment}
export function get${subSimpleClassName}(id) {
return request({
url: '${baseURL}/${subSimpleClassName_strikeCase}/get?id=' + id,
method: 'get'
})
}
#end
#end

View File

@@ -1,205 +0,0 @@
#set ($subTable = $subTables.get($subIndex))##当前表
#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")## 图片上传
#set ($hasImageUploadColumn = true)
<el-form-item label="${comment}" prop="${javaField}">
<ImageUpload v-model="formData.${javaField}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")## 文件上传
#set ($hasFileUploadColumn = true)
<el-form-item label="${comment}" prop="${javaField}">
<FileUpload v-model="formData.${javaField}"/>
</el-form-item>
#elseif($column.htmlType == "editor")## 文本编辑器
#set ($hasEditorColumn = true)
<el-form-item label="${comment}" prop="${javaField}">
<editor v-model="formData.${javaField}" :min-height="192"/>
</el-form-item>
#elseif($column.htmlType == "select")## 下拉框
<el-form-item label="${comment}" prop="${javaField}">
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox")## 多选框
<el-form-item label="${comment}" prop="${javaField}">
<el-checkbox-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
#else##没数据字典
<el-checkbox>请选择字典生成</el-checkbox>
#end
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio")## 单选框
<el-form-item label="${comment}" prop="${javaField}">
<el-radio-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
#else:label="dict.value"#end>{{dict.label}}</el-radio>
#else##没数据字典
<el-radio label="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")## 时间框
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker clearable v-model="formData.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
</el-form-item>
#elseif($column.htmlType == "textarea")## 文本框
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入内容" />
</el-form-item>
#end
#end
#end
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';
#if ($hasImageUploadColumn)
import ImageUpload from '@/components/ImageUpload';
#end
#if ($hasFileUploadColumn)
import FileUpload from '@/components/FileUpload';
#end
#if ($hasEditorColumn)
import Editor from '@/components/Editor';
#end
export default {
name: "${subSimpleClassName}Form",
components: {
#if ($hasImageUploadColumn)
ImageUpload,
#end
#if ($hasFileUploadColumn)
FileUpload,
#end
#if ($hasEditorColumn)
Editor,
#end
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
},
// 表单校验
formRules: {
#foreach ($column in $subColumns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: "${comment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],
#end
#end
},
};
},
methods: {
/** 打开弹窗 */
async open(id, ${subJoinColumn.javaField}) {
this.dialogVisible = true;
this.reset();
this.formData.${subJoinColumn.javaField} = ${subJoinColumn.javaField};
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
const res = await ${simpleClassName}Api.get${subSimpleClassName}(id);
this.formData = res.data;
this.dialogTitle = "修改${subTable.classComment}";
} finally {
this.formLoading = false;
}
}
this.dialogTitle = "新增${subTable.classComment}";
},
/** 提交按钮 */
async submitForm() {
await this.#[[$]]#refs["formRef"].validate();
this.formLoading = true;
try {
const data = this.formData;
// 修改的提交
if (data.${primaryColumn.javaField}) {
await ${simpleClassName}Api.update${subSimpleClassName}(data);
this.#[[$modal]]#.msgSuccess("修改成功");
this.dialogVisible = false;
this.#[[$]]#emit('success');
return;
}
// 添加的提交
await ${simpleClassName}Api.create${subSimpleClassName}(data);
this.#[[$modal]]#.msgSuccess("新增成功");
this.dialogVisible = false;
this.#[[$]]#emit('success');
}finally {
this.formLoading = false;
}
},
/** 表单重置 */
reset() {
this.formData = {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
};
this.resetForm("formRef");
},
}
};
</script>

View File

@@ -1,2 +0,0 @@
## 主表的 normal 和 inner 使用相同的 form 表单
#parse("codegen/vue/views/components/form_sub_normal.vue.vm")

View File

@@ -1,347 +0,0 @@
#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)})##首字母大写
<template>
<div class="app-container">
#if ( $subTable.subJoinMany )## 情况一一对多table + form
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<el-table-column label="${comment}" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-input v-model="row.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "imageUpload")## 图片上传
#set ($hasImageUploadColumn = true)
<el-table-column label="${comment}" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<ImageUpload v-model="row.${javaField}"/>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "fileUpload")## 文件上传
#set ($hasFileUploadColumn = true)
<el-table-column label="${comment}" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<FileUpload v-model="row.${javaField}"/>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "editor")## 文本编辑器
#set ($hasEditorColumn = true)
<el-table-column label="${comment}" min-width="400">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<Editor v-model="row.${javaField}" :min-height="192"/>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "select")## 下拉框
<el-table-column label="${comment}" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-select v-model="row.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "checkbox")## 多选框
<el-table-column label="${comment}" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-checkbox-group v-model="row.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
#else##没数据字典
<el-checkbox>请选择字典生成</el-checkbox>
#end
</el-checkbox-group>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "radio")## 单选框
<el-table-column label="${comment}" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-radio-group v-model="row.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
#else:label="dict.value"#end>{{dict.label}}</el-radio>
#else##没数据字典
<el-radio label="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "datetime")## 时间框
<el-table-column label="${comment}" min-width="150">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-date-picker clearable v-model="row.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "textarea")## 文本框
<el-table-column label="${comment}" min-width="200">
<template v-slot="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-input v-model="row.${javaField}" type="textarea" placeholder="请输入${comment}" />
</el-form-item>
</template>
</el-table-column>
#end
#end
#end
<el-table-column align="center" fixed="right" label="操作" width="60">
<template v-slot="{ $index }">
<el-link @click="handleDelete($index)">—</el-link>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加${subTable.classComment}</el-button>
</el-row>
#else## 情况二一对一form
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.htmlType == "input" && !$column.primaryKey)
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")## 图片上传
#set ($hasImageUploadColumn = true)
<el-form-item label="${comment}">
<ImageUpload v-model="formData.${javaField}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")## 文件上传
#set ($hasFileUploadColumn = true)
<el-form-item label="${comment}">
<FileUpload v-model="formData.${javaField}"/>
</el-form-item>
#elseif($column.htmlType == "editor")## 文本编辑器
#set ($hasEditorColumn = true)
<el-form-item label="${comment}">
<Editor v-model="formData.${javaField}" :min-height="192"/>
</el-form-item>
#elseif($column.htmlType == "select")## 下拉框
<el-form-item label="${comment}" prop="${javaField}">
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox")## 多选框
<el-form-item label="${comment}" prop="${javaField}">
<el-checkbox-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
#else##没数据字典
<el-checkbox>请选择字典生成</el-checkbox>
#end
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio")## 单选框
<el-form-item label="${comment}" prop="${javaField}">
<el-radio-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
#else:label="dict.value"#end>{{dict.label}}</el-radio>
#else##没数据字典
<el-radio label="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")## 时间框
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker clearable v-model="formData.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
</el-form-item>
#elseif($column.htmlType == "textarea")## 文本框
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
</el-form-item>
#end
#end
#end
</el-form>
#end
</div>
</template>
<script>
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';
#if ($hasImageUploadColumn)
import ImageUpload from '@/components/ImageUpload';
#end
#if ($hasFileUploadColumn)
import FileUpload from '@/components/FileUpload';
#end
#if ($hasEditorColumn)
import Editor from '@/components/Editor';
#end
export default {
name: "${subSimpleClassName}Form",
components: {
#if ($hasImageUploadColumn)
ImageUpload,
#end
#if ($hasFileUploadColumn)
FileUpload,
#end
#if ($hasEditorColumn)
Editor,
#end
},
props:[
'${subJoinColumn.javaField}'
],// ${subJoinColumn.columnComment}(主表的关联字段)
data() {
return {
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: [],
// 表单校验
formRules: {
#foreach ($column in $subColumns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: "${comment}不能为空", trigger: #if($column.htmlType == "select")"change"#else"blur"#end }],
#end
#end
},
};
},
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
${subJoinColumn.javaField}:{
handler(val) {
// 1. 重置表单
#if ( $subTable.subJoinMany )
this.formData = []
#else
this.formData = {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
}
#end
// 2. val 非空,则加载数据
if (!val) {
return;
}
try {
this.formLoading = true;
// 这里还是需要获取一下 this 的不然取不到 formData
const that = this;
#if ( $subTable.subJoinMany )
${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(val).then(function (res){
that.formData = res.data;
})
#else
${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val).then(function (res){
const data = res.data;
if (!data) {
return
}
that.formData = data;
})
#end
} finally {
this.formLoading = false;
}
},
immediate: true
}
},
methods: {
#if ( $subTable.subJoinMany )
/** 新增按钮操作 */
handleAdd() {
const row = {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
}
row.${subJoinColumn.javaField} = this.${subJoinColumn.javaField};
this.formData.push(row);
},
/** 删除按钮操作 */
handleDelete(index) {
this.formData.splice(index, 1);
},
#end
/** 表单校验 */
validate(){
return this.#[[$]]#refs["formRef"].validate();
},
/** 表单值 */
getData(){
return this.formData;
}
}
};
</script>

View File

@@ -1,210 +0,0 @@
#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)})##首字母大写
<template>
<div class="app-container">
#if ($table.templateType == 11)
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
</el-col>
#if ($deleteBatchEnable)
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['${permissionPrefix}:delete']"
>
批量删除
</el-button>
</el-col>
#end
</el-row>
#end
## 列表
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
#if ($table.templateType == 11 && $deleteBatchEnable)
@selection-change="handleRowCheckboxChange"
#end
>
#if ($table.templateType == 11 && $deleteBatchEnable)
<el-table-column type="selection" width="55" />
#end
#foreach($column in $subColumns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment=$column.columnComment)
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.javaType == "LocalDateTime")## 时间类型
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.${javaField}) }}</span>
</template>
</el-table-column>
#elseif($column.dictType && "" != $column.dictType)## 数据字典
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />
</template>
</el-table-column>
#else
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.${primaryColumn.javaField})"
v-hasPermi="['${permissionPrefix}:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['${permissionPrefix}:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
#if ($table.templateType == 11)
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<${subSimpleClassName}Form ref="formRef" @success="getList" />
#end
</div>
</template>
<script>
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';
#if ($table.templateType == 11)
import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue';
#end
export default {
name: "${subSimpleClassName}List",
#if ($table.templateType == 11)
components: {
${subSimpleClassName}Form
},
#end
props:[
'${subJoinColumn.javaField}'
],// ${subJoinColumn.columnComment}(主表的关联字段)
data() {
return {
// 遮罩层
loading: true,
// 列表的数据
list: [],
#if ($table.templateType == 11)
#if ($deleteBatchEnable)
checkedIds: [],
#end
// 列表的总页数
total: 0,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
${subJoinColumn.javaField}: undefined
}
#end
};
},
#if ($table.templateType != 11)
created() {
this.getList();
},
#end
watch:{/** 监听主表的关联字段的变化,加载对应的子表数据 */
${subJoinColumn.javaField}:{
handler(val) {
this.queryParams.${subJoinColumn.javaField} = val;
if (val){
this.handleQuery();
}
},
immediate: true
}
},
methods: {
/** 查询列表 */
async getList() {
try {
this.loading = true;
#if ($table.templateType == 11)
const res = await ${simpleClassName}Api.get${subSimpleClassName}Page(this.queryParams);
this.list = res.data.list;
this.total = res.data.total;
#else
#if ( $subTable.subJoinMany )
const res = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(this.${subJoinColumn.javaField});
this.list = res.data;
#else
const res = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(this.${subJoinColumn.javaField});
const data = res.data;
if (!data) {
return;
}
this.list.push(data);
#end
#end
} finally {
this.loading = false;
}
},
#if ($table.templateType == 11 && $deleteBatchEnable)
/** 批量删除${table.classComment} */
async handleDeleteBatch() {
await this.#[[$modal]]#.confirm('是否确认删除?')
try {
await ${simpleClassName}Api.delete${subSimpleClassName}List(this.checkedIds);
this.checkedIds = [];
await this.getList();
this.#[[$modal]]#.msgSuccess("删除成功");
} catch {}
},
handleRowCheckboxChange(records) {
this.checkedIds = records.map((item) => item.id);
},
#end
#if ($table.templateType == 11)
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 添加/修改操作 */
openForm(id) {
if (!this.${subJoinColumn.javaField}) {
this.#[[$modal]]#.msgError('请选择一个${table.classComment}');
return;
}
this.#[[$]]#refs["formRef"].open(id, this.${subJoinColumn.javaField});
},
/** 删除按钮操作 */
async handleDelete(row) {
const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
await this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为"' + ${primaryColumn.javaField} + '"的数据项?');
try {
await ${simpleClassName}Api.delete${subSimpleClassName}(${primaryColumn.javaField});
await this.getList();
this.#[[$modal]]#.msgSuccess("删除成功");
} catch {}
},
#end
}
};
</script>

View File

@@ -1,4 +0,0 @@
## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点:
## 1inner 使用 list 不分页erp 使用 page 分页
## 2erp 支持单个子表的新增、修改、删除inner 不支持
#parse("codegen/vue/views/components/list_sub_erp.vue.vm")

View File

@@ -1,320 +0,0 @@
<template>
<div class="app-container">
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag append-to-body>
<el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">
#foreach($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
<el-form-item label="${comment}" prop="${javaField}">
<TreeSelect
v-model="formData.${javaField}"
:options="${classNameVar}Tree"
:normalizer="normalizer"
placeholder="请选择${comment}"
/>
</el-form-item>
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")## 图片上传
#set ($hasImageUploadColumn = true)
<el-form-item label="${comment}">
<ImageUpload v-model="formData.${javaField}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")## 文件上传
#set ($hasFileUploadColumn = true)
<el-form-item label="${comment}">
<FileUpload v-model="formData.${javaField}"/>
</el-form-item>
#elseif($column.htmlType == "editor")## 文本编辑器
#set ($hasEditorColumn = true)
<el-form-item label="${comment}">
<Editor v-model="formData.${javaField}" :min-height="192"/>
</el-form-item>
#elseif($column.htmlType == "select")## 下拉框
<el-form-item label="${comment}" prop="${javaField}">
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" :label="dict.label" #if ($column.javaType == "Integer" || $column.javaType == "Long"):value="parseInt(dict.value)"#else:value="dict.value"#end />
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox")## 多选框
<el-form-item label="${comment}" prop="${javaField}">
<el-checkbox-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"#else:label="dict.value"#end>{{dict.label}}</el-checkbox>
#else##没数据字典
<el-checkbox>请选择字典生成</el-checkbox>
#end
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio")## 单选框
<el-form-item label="${comment}" prop="${javaField}">
<el-radio-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" #if($column.javaType == "Integer" || $column.javaType == "Long"):label="parseInt(dict.value)"
#else:label="dict.value"#end>{{dict.label}}</el-radio>
#else##没数据字典
<el-radio label="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")## 时间框
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker clearable v-model="formData.${javaField}" type="date" value-format="timestamp" placeholder="选择${comment}" />
</el-form-item>
#elseif($column.htmlType == "textarea")## 文本框
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入内容" />
</el-form-item>
#end
#end
#end
</el-form>
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
<${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" />
</el-tab-pane>
#end
</el-tabs>
#end
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';
#if ($hasImageUploadColumn)
import ImageUpload from '@/components/ImageUpload';
#end
#if ($hasFileUploadColumn)
import FileUpload from '@/components/FileUpload';
#end
#if ($hasEditorColumn)
import Editor from '@/components/Editor';
#end
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
import TreeSelect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'
#end
#end
export default {
name: "${simpleClassName}Form",
components: {
#if ($hasImageUploadColumn)
ImageUpload,
#end
#if ($hasFileUploadColumn)
FileUpload,
#end
#if ($hasEditorColumn)
Editor,
#end
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
TreeSelect,
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
${subSimpleClassName}Form,
#end
#end
},
data() {
return {
// 弹出层标题
dialogTitle: "",
// 是否显示弹出层
dialogVisible: false,
// 表单的加载中1修改时的数据加载2提交的按钮禁用
formLoading: false,
// 表单参数
formData: {
#foreach ($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
},
// 表单校验
formRules: {
#foreach ($column in $columns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
#end
#end
},
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
${classNameVar}Tree: [], // 树形结构
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
#if ( $subTables && $subTables.size() > 0 )
/** 子表的表单 */
subTabsName: '$subClassNameVars.get(0)'
#end
#end
};
},
methods: {
/** 打开弹窗 */
async open(id) {
this.dialogVisible = true;
this.reset();
// 修改时,设置数据
if (id) {
this.formLoading = true;
try {
const res = await ${simpleClassName}Api.get${simpleClassName}(id);
this.formData = res.data;
this.title = "修改${table.classComment}";
} finally {
this.formLoading = false;
}
}
this.title = "新增${table.classComment}";
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
await this.get${simpleClassName}Tree();
#end
},
/** 提交按钮 */
async submitForm() {
// 校验主表
await this.$refs["formRef"].validate();
## 特殊:主子表专属逻辑
#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 {
## 代码生成后会替换为正确的 refs
await this.refs['${subClassNameVar}FormRef'].validate();
} catch (e) {
this.subTabsName = '${subClassNameVar}';
return;
}
#end
#end
#end
this.formLoading = true;
try {
const data = this.formData;
## 特殊:主子表专属逻辑
#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 = this.refs['${subClassNameVar}FormRef'].getData();
#end
#end
#end
// 修改的提交
if (data.${primaryColumn.javaField}) {
await ${simpleClassName}Api.update${simpleClassName}(data);
this.#[[$modal]]#.msgSuccess("修改成功");
this.dialogVisible = false;
this.#[[$]]#emit('success');
return;
}
// 添加的提交
await ${simpleClassName}Api.create${simpleClassName}(data);
this.#[[$modal]]#.msgSuccess("新增成功");
this.dialogVisible = false;
this.#[[$]]#emit('success');
} finally {
this.formLoading = false;
}
},
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
/** 获得${table.classComment}树 */
async get${simpleClassName}Tree() {
this.${classNameVar}Tree = [];
const res = await ${simpleClassName}Api.get${simpleClassName}List();
const root = { id: 0, name: '顶级${table.classComment}', children: [] };
root.children = this.handleTree(res.data, 'id', '${treeParentColumn.javaField}')
this.${classNameVar}Tree.push(root)
},
#end
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
/** 转换${table.classComment}数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
#if ($treeNameColumn.javaField == "name")
return {
id: node.id,
label: node.name,
children: node.children
};
#else
return {
id: node.id,
label: node['$treeNameColumn.javaField'],
children: node.children
};
#end
},
#end
/** 表单重置 */
reset() {
this.formData = {
#foreach ($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
};
this.resetForm("formRef");
}
}
};
</script>

View File

@@ -1,386 +0,0 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
#foreach($column in $columns)
#if ($column.listOperation)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment=$column.columnComment)
#if ($column.htmlType == "input")
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="queryParams.${javaField}" placeholder="请输入${comment}" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
#elseif ($column.htmlType == "select" || $column.htmlType == "radio")
<el-form-item label="${comment}" prop="${javaField}">
<el-select v-model="queryParams.${javaField}" placeholder="请选择${comment}" clearable size="small">
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value" :label="dict.label" :value="dict.value"/>
#else## 未设置 dictType 数据字典的情况
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime")
#if ($column.listOperationCondition != "BETWEEN")## 非范围
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker clearable v-model="queryParams.${javaField}" type="date" value-format="yyyy-MM-dd" placeholder="选择${comment}" />
</el-form-item>
#else## 范围
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker v-model="queryParams.${javaField}" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
#end
#end
#end
#end
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)"
v-hasPermi="['${permissionPrefix}:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['${permissionPrefix}:export']">导出</el-button>
</el-col>
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">
展开/折叠
</el-button>
</el-col>
#end
#if ($table.templateType != 2 && $deleteBatchEnable)
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['${permissionPrefix}:delete']"
>
批量删除
</el-button>
</el-col>
#end
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:highlight-current-row="true"
:show-overflow-tooltip="true"
@current-change="handleCurrentChange"
#if ($deleteBatchEnable)
@selection-change="handleRowCheckboxChange"
#end
>
## 特殊:树表专属逻辑
#elseif ( $table.templateType == 2 )
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
v-if="refreshTable"
row-key="id"
:default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
>
#else
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
#if ($deleteBatchEnable)
@selection-change="handleRowCheckboxChange"
#end
>
#end
#if ($table.templateType != 2 && $deleteBatchEnable)
<el-table-column type="selection" width="55" />
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
<!-- 子表的列表 -->
<el-table-column type="expand">
<template #default="scope">
<el-tabs value="$subClassNameVars.get(0)">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
<${subSimpleClassName}List :${subJoinColumn_strikeCase}="scope.row.id" />
</el-tab-pane>
#end
</el-tabs>
</template>
</el-table-column>
#end
#foreach($column in $columns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment=$column.columnComment)
#if ($column.javaType == "LocalDateTime")## 时间类型
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.${javaField}) }}</span>
</template>
</el-table-column>
#elseif("" != $column.dictType)## 数据字典
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />
</template>
</el-table-column>
#else
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.${primaryColumn.javaField})"
v-hasPermi="['${permissionPrefix}:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['${permissionPrefix}:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
## 特殊:树表专属逻辑(树不需要分页)
#if ( $table.templateType != 2 )
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
#end
<!-- 对话框(添加 / 修改) -->
<${simpleClassName}Form ref="formRef" @success="getList" />
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
<!-- 子表的列表 -->
<el-tabs v-model="subTabsName">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
<${subSimpleClassName}List v-if="currentRow.id" :${subJoinColumn_strikeCase}="currentRow.id" />
</el-tab-pane>
#end
</el-tabs>
#end
</div>
</template>
<script>
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${table.businessName}';
import ${simpleClassName}Form from './${simpleClassName}Form.vue';
#if ($hasImageUploadColumn)
import ImageUpload from '@/components/ImageUpload';
#end
#if ($hasFileUploadColumn)
import FileUpload from '@/components/FileUpload';
#end
#if ($hasEditorColumn)
import Editor from '@/components/Editor';
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType != 10 )
#if ( $subTables && $subTables.size() > 0 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue';
#end
#end
#end
export default {
name: "${simpleClassName}",
components: {
${simpleClassName}Form,
## 特殊:主子表专属逻辑
#if ( $table.templateType != 10 )
#if ( $subTables && $subTables.size() > 0 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
${subSimpleClassName}List,
#end
#end
#end
#if ($hasImageUploadColumn)
ImageUpload,
#end
#if ($hasFileUploadColumn)
FileUpload,
#end
#if ($hasEditorColumn)
Editor,
#end
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
// 总条数
total: 0,
#end
// ${table.classComment}列表
list: [],
// 是否展开,默认全部展开
isExpandAll: true,
// 重新渲染表格状态
refreshTable: true,
// 选中行
currentRow: {},
checkedIds: [],
// 查询参数
queryParams: {
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
pageNo: 1,
pageSize: 10,
#end
#foreach ($column in $columns)
#if ($column.listOperation)
#if ($column.listOperationCondition != 'BETWEEN')
$column.javaField: null,
#end
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
$column.javaField: [],
#end
#end
#end
},
## 特殊:主子表专属逻辑-erp
#if ( $table.templateType == 11)
#if ( $subTables && $subTables.size() > 0 )
/** 子表的列表 */
subTabsName: '$subClassNameVars.get(0)'
#end
#end
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
async getList() {
try {
this.loading = true;
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType == 2 )
const res = await ${simpleClassName}Api.get${simpleClassName}List(this.queryParams);
this.list = this.handleTree(res.data, 'id', '${treeParentColumn.javaField}');
#else
const res = await ${simpleClassName}Api.get${simpleClassName}Page(this.queryParams);
this.list = res.data.list;
this.total = res.data.total;
#end
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 添加/修改操作 */
openForm(id) {
this.#[[$]]#refs["formRef"].open(id);
},
/** 删除按钮操作 */
async handleDelete(row) {
const ${primaryColumn.javaField} = row.${primaryColumn.javaField};
await this.#[[$modal]]#.confirm('是否确认删除${table.classComment}编号为"' + ${primaryColumn.javaField} + '"的数据项?')
try {
await ${simpleClassName}Api.delete${simpleClassName}(${primaryColumn.javaField});
await this.getList();
this.#[[$modal]]#.msgSuccess("删除成功");
} catch {}
},
#if ($table.templateType != 2 && $deleteBatchEnable)
/** 批量删除${table.classComment} */
async handleDeleteBatch() {
await this.#[[$modal]]#.confirm('是否确认删除?')
try {
await ${simpleClassName}Api.delete${simpleClassName}List(this.checkedIds);
this.checkedIds = [];
await this.getList();
this.#[[$modal]]#.msgSuccess("删除成功");
} catch {}
},
handleRowCheckboxChange(records) {
this.checkedIds = records.map((item) => item.id);
},
#end
/** 导出按钮操作 */
async handleExport() {
await this.#[[$modal]]#.confirm('是否确认导出所有${table.classComment}数据项?');
try {
this.exportLoading = true;
const data = await ${simpleClassName}Api.export${simpleClassName}Excel(this.queryParams);
this.#[[$]]#download.excel(data, '${table.classComment}.xls');
} catch {
} finally {
this.exportLoading = false;
}
},
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 )
/** 选中行操作 */
handleCurrentChange(row) {
this.currentRow = row;
#if ( $subTables && $subTables.size() > 0 )
/** 子表的列表 */
this.subTabsName = '$subClassNameVars.get(0)';
#end
},
#end
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false
this.isExpandAll = !this.isExpandAll
this.$nextTick(function () {
this.refreshTable = true
})
}
#end
}
};
</script>

View File

@@ -1,166 +0,0 @@
import request from '@/config/axios'
import type { Dayjs } from 'dayjs';
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
## 特殊:主子表专属逻辑
#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: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
#end
#end
}
#end
/** ${table.classComment}信息 */
export interface ${simpleClassName} {
#foreach ($column in $columns)
#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: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
#end
#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
}
// ${table.classComment} API
export const ${simpleClassName}Api = {
#if ( $table.templateType != 2 )
// 查询${table.classComment}分页
get${simpleClassName}Page: async (params: any) => {
return await request.get({ url: `${baseURL}/page`, params })
},
#else
// 查询${table.classComment}列表
get${simpleClassName}List: async (params) => {
return await request.get({ url: `${baseURL}/list`, params })
},
#end
// 查询${table.classComment}详情
get${simpleClassName}: async (id: number) => {
return await request.get({ url: `${baseURL}/get?id=` + id })
},
// 新增${table.classComment}
create${simpleClassName}: async (data: ${simpleClassName}) => {
return await request.post({ url: `${baseURL}/create`, data })
},
// 修改${table.classComment}
update${simpleClassName}: async (data: ${simpleClassName}) => {
return await request.put({ url: `${baseURL}/update`, data })
},
// 删除${table.classComment}
delete${simpleClassName}: async (id: number) => {
return await request.delete({ url: `${baseURL}/delete?id=` + id })
},
#if ( $table.templateType != 2 && $deleteBatchEnable)
/** 批量删除${table.classComment} */
delete${simpleClassName}List: async (ids: number[]) => {
return await request.delete({ url: `${baseURL}/delete-list?ids=${ids.join(',')}` })
},
#end
// 导出${table.classComment} Excel
export${simpleClassName}: async (params) => {
return await request.download({ url: `${baseURL}/export-excel`, params })
},
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
#set ($subClassNameVar = $subClassNameVars.get($index))
// ==================== 子表($subTable.classComment ====================
## 情况一MASTER_ERP 时,需要分查询页子表
#if ( $table.templateType == 11 )
// 获得${subTable.classComment}分页
get${subSimpleClassName}Page: async (params) => {
return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/page`, params })
},
## 情况二:非 MASTER_ERP 时,需要列表查询子表
#else
#if ( $subTable.subJoinMany )
// 获得${subTable.classComment}列表
get${subSimpleClassName}ListBy${SubJoinColumnName}: async (${subJoinColumn.javaField}) => {
return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} })
},
#else
// 获得${subTable.classComment}
get${subSimpleClassName}By${SubJoinColumnName}: async (${subJoinColumn.javaField}) => {
return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=` + ${subJoinColumn.javaField} })
},
#end
#end
## 特殊MASTER_ERP 时,支持单个的新增、修改、删除操作
#if ( $table.templateType == 11 )
// 新增${subTable.classComment}
create${subSimpleClassName}: async (data: ${subSimpleClassName}) => {
return await request.post({ url: `${baseURL}/${subSimpleClassName_strikeCase}/create`, data })
},
// 修改${subTable.classComment}
update${subSimpleClassName}: async (data: ${subSimpleClassName}) => {
return await request.put({ url: `${baseURL}/${subSimpleClassName_strikeCase}/update`, data })
},
// 删除${subTable.classComment}
delete${subSimpleClassName}: async (id: number) => {
return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete?id=` + id })
},
#if ($deleteBatchEnable)
/** 批量删除${subTable.classComment} */
delete${subSimpleClassName}List: async (ids: number[]) => {
return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}` })
},
#end
// 获得${subTable.classComment}
get${subSimpleClassName}: async (id: number) => {
return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get?id=` + id })
},
#end
#end
}

View File

@@ -1,204 +0,0 @@
#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
#set ($dictMethod = "getIntDictOptions")
#elseif ($javaType == "String")
#set ($dictMethod = "getStrDictOptions")
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")## 图片上传
<el-form-item label="${comment}" prop="${javaField}">
<UploadImg v-model="formData.${javaField}" />
</el-form-item>
#elseif($column.htmlType == "fileUpload")## 文件上传
<el-form-item label="${comment}" prop="${javaField}">
<UploadFile v-model="formData.${javaField}" />
</el-form-item>
#elseif($column.htmlType == "editor")## 文本编辑器
<el-form-item label="${comment}" prop="${javaField}">
<Editor v-model="formData.${javaField}" height="150px" />
</el-form-item>
#elseif($column.htmlType == "select")## 下拉框
<el-form-item label="${comment}" prop="${javaField}">
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox")## 多选框
<el-form-item label="${comment}" prop="${javaField}">
<el-checkbox-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-checkbox label="请选择字典生成" />
#end
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio")## 单选框
<el-form-item label="${comment}" prop="${javaField}">
<el-radio-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
#else##没数据字典
<el-radio value="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")## 时间框
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker
v-model="formData.${javaField}"
type="date"
value-format="x"
placeholder="选择${comment}"
/>
</el-form-item>
#elseif($column.htmlType == "textarea")## 文本框
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
</el-form-item>
#end
#end
#end
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { ${simpleClassName}Api, ${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref('') // 表单的类型create - 新增update - 修改
const formData = ref({
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
})
const formRules = reactive({
#foreach ($column in $subColumns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
#end
#end
})
const formRef = ref() // 表单 Ref
/** 打开弹窗 */
const open = async (type: string, id?: number, ${subJoinColumn.javaField}?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
formData.value.${subJoinColumn.javaField} = ${subJoinColumn.javaField} as any
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ${simpleClassName}Api.get${subSimpleClassName}(id)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ${subSimpleClassName}
if (formType.value === 'create') {
await ${simpleClassName}Api.create${subSimpleClassName}(data)
message.success(t('common.createSuccess'))
} else {
await ${simpleClassName}Api.update${subSimpleClassName}(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
}
formRef.value?.resetFields()
}
</script>

View File

@@ -1,2 +0,0 @@
## 主表的 normal 和 inner 使用相同的 form 表单
#parse("codegen/vue3/views/components/form_sub_normal.vue.vm")

View File

@@ -1,360 +0,0 @@
#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)})##首字母大写
<template>
#if ( $subTable.subJoinMany )## 情况一一对多table + form
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
v-loading="formLoading"
label-width="0px"
:inline-message="true"
>
<el-table :data="formData" class="-mt-10px">
<el-table-column label="序号" type="index" width="100" />
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
#set ($dictMethod = "getIntDictOptions")
#elseif ($javaType == "String")
#set ($dictMethod = "getStrDictOptions")
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<el-table-column label="${comment}" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-input v-model="row.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "imageUpload")## 图片上传
<el-table-column label="${comment}" min-width="200">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<UploadImg v-model="row.${javaField}" />
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "fileUpload")## 文件上传
<el-table-column label="${comment}" min-width="200">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<UploadFile v-model="row.${javaField}" />
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "editor")## 文本编辑器
<el-table-column label="${comment}" min-width="400">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<Editor v-model="row.${javaField}" height="150px" />
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "select")## 下拉框
<el-table-column label="${comment}" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-select v-model="row.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "checkbox")## 多选框
<el-table-column label="${comment}" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-checkbox-group v-model="row.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-checkbox label="请选择字典生成" />
#end
</el-checkbox-group>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "radio")## 单选框
<el-table-column label="${comment}" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-radio-group v-model="row.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
#else##没数据字典
<el-radio value="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "datetime")## 时间框
<el-table-column label="${comment}" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-date-picker
v-model="row.${javaField}"
type="date"
value-format="x"
placeholder="选择${comment}"
/>
</el-form-item>
</template>
</el-table-column>
#elseif($column.htmlType == "textarea")## 文本框
<el-table-column label="${comment}" min-width="200">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.${javaField}`" :rules="formRules.${javaField}" class="mb-0px!">
<el-input v-model="row.${javaField}" type="textarea" placeholder="请输入${comment}" />
</el-form-item>
</template>
</el-table-column>
#end
#end
#end
<el-table-column align="center" fixed="right" label="操作" width="60">
<template #default="{ $index }">
<el-button @click="handleDelete($index)" link>—</el-button>
</template>
</el-table-column>
</el-table>
</el-form>
<el-row justify="center" class="mt-3">
<el-button @click="handleAdd" round>+ 添加${subTable.classComment}</el-button>
</el-row>
#else## 情况二一对一form
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
#set ($dictMethod = "getIntDictOptions")
#elseif ($javaType == "String")
#set ($dictMethod = "getStrDictOptions")
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")## 图片上传
<el-form-item label="${comment}" prop="${javaField}">
<UploadImg v-model="formData.${javaField}" />
</el-form-item>
#elseif($column.htmlType == "fileUpload")## 文件上传
<el-form-item label="${comment}" prop="${javaField}">
<UploadFile v-model="formData.${javaField}" />
</el-form-item>
#elseif($column.htmlType == "editor")## 文本编辑器
<el-form-item label="${comment}" prop="${javaField}">
<Editor v-model="formData.${javaField}" height="150px" />
</el-form-item>
#elseif($column.htmlType == "select")## 下拉框
<el-form-item label="${comment}" prop="${javaField}">
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox")## 多选框
<el-form-item label="${comment}" prop="${javaField}">
<el-checkbox-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-checkbox label="请选择字典生成" />
#end
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio")## 单选框
<el-form-item label="${comment}" prop="${javaField}">
<el-radio-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
#else##没数据字典
<el-radio value="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")## 时间框
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker
v-model="formData.${javaField}"
type="date"
value-format="x"
placeholder="选择${comment}"
/>
</el-form-item>
#elseif($column.htmlType == "textarea")## 文本框
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
</el-form-item>
#end
#end
#end
</el-form>
#end
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { ${simpleClassName}Api } from '@/api/${table.moduleName}/${table.businessName}'
const props = defineProps<{
${subJoinColumn.javaField}: number // ${subJoinColumn.columnComment}(主表的关联字段)
}>()
const formLoading = ref(false) // 表单的加载中
const formData = ref<any#if ( $subTable.subJoinMany )[]#end>(#if ( $subTable.subJoinMany )[]#else{}#end)
const formRules = reactive({
#foreach ($column in $subColumns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
#end
#end
})
const formRef = ref() // 表单 Ref
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.${subJoinColumn.javaField},
async (val) => {
// 1. 重置表单
#if ( $subTable.subJoinMany )
formData.value = []
#else
formData.value = {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
}
#end
// 2. val 非空,则加载数据
if (!val) {
return;
}
try {
formLoading.value = true
#if ( $subTable.subJoinMany )
formData.value = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(val)
#else
const data = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(val)
if (!data) {
return
}
formData.value = data
#end
} finally {
formLoading.value = false
}
},
{ immediate: true }
)
#if ( $subTable.subJoinMany )
/** 新增按钮操作 */
const handleAdd = () => {
const row = {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
}
row.${subJoinColumn.javaField} = props.${subJoinColumn.javaField} as any
formData.value.push(row)
}
/** 删除按钮操作 */
const handleDelete = (index) => {
formData.value.splice(index, 1)
}
#end
/** 表单校验 */
const validate = () => {
return formRef.value.validate()
}
/** 表单值 */
const getData = () => {
return formData.value
}
defineExpose({ validate, getData })
</script>

View File

@@ -1,231 +0,0 @@
#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)})##首字母大写
<template>
<!-- 列表 -->
<ContentWrap>
#if ($table.templateType == 11)
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['${permissionPrefix}:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
#if ($deleteBatchEnable)
<el-button
type="danger"
plain
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['${permissionPrefix}:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
#end
#end
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
#if ($table.templateType == 11 && $deleteBatchEnable)
@selection-change="handleRowCheckboxChange"
#end
>
#if ($table.templateType == 11 && $deleteBatchEnable)
<el-table-column type="selection" width="55" />
#end
#foreach($column in $subColumns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment=$column.columnComment)
#if ( $column.id == $subJoinColumn.id) ## 特殊:忽略主子表的 join 字段,不用填写
#elseif ($column.javaType == "LocalDateTime")## 时间类型
<el-table-column
label="${comment}"
align="center"
prop="${javaField}"
:formatter="dateFormatter"
width="180px"
/>
#elseif($column.dictType && "" != $column.dictType)## 数据字典
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template #default="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />
</template>
</el-table-column>
#else
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
#if ($table.templateType == 11)
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['${permissionPrefix}:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['${permissionPrefix}:delete']"
>
删除
</el-button>
</template>
</el-table-column>
#end
</el-table>
#if ($table.templateType == 11)
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
#end
</ContentWrap>
#if ($table.templateType == 11)
<!-- 表单弹窗:添加/修改 -->
<${subSimpleClassName}Form ref="formRef" @success="getList" />
#end
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
#if ($deleteBatchEnable)
import { isEmpty } from '@/utils/is'
#end
import { ${simpleClassName}Api, ${subSimpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
#if ($table.templateType == 11)
import ${subSimpleClassName}Form from './${subSimpleClassName}Form.vue'
#end
#if ($table.templateType == 11)
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
#end
const props = defineProps<{
${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
}>()
const loading = ref(false) // 列表的加载中
const list = ref([]) // 列表的数据
#if ($table.templateType == 11)
const total = ref(0) // 列表的总页数
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
${subJoinColumn.javaField}: undefined as unknown
})
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.${subJoinColumn.javaField},
(val: number) => {
if (!val) {
list.value = [] // 清空列表
return
}
queryParams.${subJoinColumn.javaField} = val
handleQuery()
},
{ immediate: true, deep: true }
)
#end
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
#if ($table.templateType == 11)
const data = await ${simpleClassName}Api.get${subSimpleClassName}Page(queryParams)
list.value = data.list
total.value = data.total
#else
#if ( $subTable.subJoinMany )
list.value = await ${simpleClassName}Api.get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField})
#else
const data = await ${simpleClassName}Api.get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField})
if (!data) {
return
}
list.value.push(data)
#end
#end
} finally {
loading.value = false
}
}
#if ($table.templateType == 11)
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
if (!props.${subJoinColumn.javaField}) {
message.error('请选择一个${table.classComment}')
return
}
formRef.value.open(type, id, props.${subJoinColumn.javaField})
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ${simpleClassName}Api.delete${subSimpleClassName}(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
#if ($table.templateType == 11 && $deleteBatchEnable)
/** 批量删除${subTable.classComment} */
const handleDeleteBatch = async () => {
try {
// 删除的二次确认
await message.delConfirm()
await ${simpleClassName}Api.delete${subSimpleClassName}List(checkedIds.value);
checkedIds.value = [];
message.success(t('common.delSuccess'))
await getList();
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: ${subSimpleClassName}[]) => {
checkedIds.value = records.map((item) => item.id!);
}
#end
#end
#if ($table.templateType != 11)
/** 初始化 **/
onMounted(() => {
getList()
})
#end
</script>

View File

@@ -1,4 +0,0 @@
## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点:
## 1inner 使用 list 不分页erp 使用 page 分页
## 2erp 支持单个子表的新增、修改、删除inner 不支持
#parse("codegen/vue3/views/components/list_sub_erp.vue.vm")

View File

@@ -1,300 +0,0 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
#foreach($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
#set ($dictMethod = "getIntDictOptions")
#elseif ($javaType == "String")
#set ($dictMethod = "getStrDictOptions")
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
#if ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
<el-form-item label="${comment}" prop="${javaField}">
<el-tree-select
v-model="formData.${javaField}"
:data="${classNameVar}Tree"
#if ($treeNameColumn.javaField == "name")
:props="defaultProps"
#else
:props="{...defaultProps, label: '$treeNameColumn.javaField'}"
#end
check-strictly
default-expand-all
placeholder="请选择${comment}"
/>
</el-form-item>
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")## 图片上传
<el-form-item label="${comment}" prop="${javaField}">
<UploadImg v-model="formData.${javaField}" />
</el-form-item>
#elseif($column.htmlType == "fileUpload")## 文件上传
<el-form-item label="${comment}" prop="${javaField}">
<UploadFile v-model="formData.${javaField}" />
</el-form-item>
#elseif($column.htmlType == "editor")## 文本编辑器
<el-form-item label="${comment}" prop="${javaField}">
<Editor v-model="formData.${javaField}" height="150px" />
</el-form-item>
#elseif($column.htmlType == "select")## 下拉框
<el-form-item label="${comment}" prop="${javaField}">
<el-select v-model="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<el-option
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox")## 多选框
<el-form-item label="${comment}" prop="${javaField}">
<el-checkbox-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-checkbox
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else##没数据字典
<el-checkbox label="请选择字典生成" />
#end
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio")## 单选框
<el-form-item label="${comment}" prop="${javaField}">
<el-radio-group v-model="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<el-radio
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.value"
>
{{ dict.label }}
</el-radio>
#else##没数据字典
<el-radio value="1">请选择字典生成</el-radio>
#end
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")## 时间框
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker
v-model="formData.${javaField}"
type="date"
value-format="x"
placeholder="选择${comment}"
/>
</el-form-item>
#elseif($column.htmlType == "textarea")## 文本框
<el-form-item label="${comment}" prop="${javaField}">
<el-input v-model="formData.${javaField}" type="textarea" placeholder="请输入${comment}" />
</el-form-item>
#end
#end
#end
</el-form>
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
<!-- 子表的表单 -->
<el-tabs v-model="subTabsName">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
<${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData.id" />
</el-tab-pane>
#end
</el-tabs>
#end
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { ${simpleClassName}Api, ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
import { defaultProps, handleTree } from '@/utils/tree'
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
import ${subSimpleClassName}Form from './components/${subSimpleClassName}Form.vue'
#end
#end
/** ${table.classComment} 表单 */
defineOptions({ name: '${simpleClassName}Form' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref('') // 表单的类型create - 新增update - 修改
const formData = ref({
#foreach ($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
})
const formRules = reactive({
#foreach ($column in $columns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
#end
#end
})
const formRef = ref() // 表单 Ref
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
const ${classNameVar}Tree = ref() // 树形结构
#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 open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await ${simpleClassName}Api.get${simpleClassName}(id)
} finally {
formLoading.value = false
}
}
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
await get${simpleClassName}Tree()
#end
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
await formRef.value.validate()
## 特殊:主子表专属逻辑
#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
// 提交请求
formLoading.value = true
try {
const data = formData.value as unknown as ${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
if (formType.value === 'create') {
await ${simpleClassName}Api.create${simpleClassName}(data)
message.success(t('common.createSuccess'))
} else {
await ${simpleClassName}Api.update${simpleClassName}(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
#foreach ($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
}
formRef.value?.resetFields()
}
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
/** 获得${table.classComment}树 */
const get${simpleClassName}Tree = async () => {
${classNameVar}Tree.value = []
const data = await ${simpleClassName}Api.get${simpleClassName}List()
const root: Tree = { id: 0, name: '顶级${table.classComment}', children: [] }
root.children = handleTree(data, 'id', '${treeParentColumn.javaField}')
${classNameVar}Tree.value.push(root)
}
#end
</script>

View File

@@ -1,424 +0,0 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
#foreach($column in $columns)
#if ($column.listOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment = $column.columnComment)
#set ($dictMethod = "getDictOptions")## 计算使用哪个 dict 字典方法
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
#set ($dictMethod = "getIntDictOptions")
#elseif ($javaType == "String")
#set ($dictMethod = "getStrDictOptions")
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
#if ($column.htmlType == "input")
<el-form-item label="${comment}" prop="${javaField}">
<el-input
v-model="queryParams.${javaField}"
placeholder="请输入${comment}"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
#elseif ($column.htmlType == "select" || $column.htmlType == "radio")
<el-form-item label="${comment}" prop="${javaField}">
<el-select
v-model="queryParams.${javaField}"
placeholder="请选择${comment}"
clearable
class="!w-240px"
>
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
<el-option
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
#else## 未设置 dictType 数据字典的情况
<el-option label="请选择字典生成" value="" />
#end
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime")
#if ($column.listOperationCondition != "BETWEEN")## 非范围
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker
v-model="queryParams.${javaField}"
value-format="YYYY-MM-DD"
type="date"
placeholder="选择${comment}"
clearable
class="!w-240px"
/>
</el-form-item>
#else## 范围
<el-form-item label="${comment}" prop="${javaField}">
<el-date-picker
v-model="queryParams.${javaField}"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</el-form-item>
#end
#end
#end
#end
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['${permissionPrefix}:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['${permissionPrefix}:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
<el-button type="danger" plain @click="toggleExpandAll">
<Icon icon="ep:sort" class="mr-5px" /> 展开/折叠
</el-button>
#end
#if ($table.templateType != 2 && $deleteBatchEnable)
<el-button
type="danger"
plain
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-hasPermi="['${table.moduleName}:${simpleClassName_strikeCase}:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
#end
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
highlight-current-row
@current-change="handleCurrentChange"
#if ($deleteBatchEnable)
@selection-change="handleRowCheckboxChange"
#end
>
## 特殊:树表专属逻辑
#elseif ( $table.templateType == 2 )
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
:default-expand-all="isExpandAll"
v-if="refreshTable"
>
#else
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
#if ($deleteBatchEnable)
@selection-change="handleRowCheckboxChange"
#end
>
#end
#if ($table.templateType != 2 && $deleteBatchEnable)
<el-table-column type="selection" width="55" />
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
<!-- 子表的列表 -->
<el-table-column type="expand">
<template #default="scope">
<el-tabs model-value="$subClassNameVars.get(0)">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
<${subSimpleClassName}List :${subJoinColumn_strikeCase}="scope.row.id" />
</el-tab-pane>
#end
</el-tabs>
</template>
</el-table-column>
#end
#foreach($column in $columns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment=$column.columnComment)
#if ($column.javaType == "LocalDateTime")## 时间类型
<el-table-column
label="${comment}"
align="center"
prop="${javaField}"
:formatter="dateFormatter"
width="180px"
/>
#elseif($column.dictType && "" != $column.dictType)## 数据字典
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template #default="scope">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="scope.row.${column.javaField}" />
</template>
</el-table-column>
#else
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
#end
<el-table-column label="操作" align="center" min-width="120px">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['${permissionPrefix}:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['${permissionPrefix}:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗:添加/修改 -->
<${simpleClassName}Form ref="formRef" @success="getList" />
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 && $subTables && $subTables.size() > 0 )
<!-- 子表的列表 -->
<ContentWrap>
<el-tabs model-value="$subClassNameVars.get(0)">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<el-tab-pane label="${subTable.classComment}" name="$subClassNameVar">
<${subSimpleClassName}List :${subJoinColumn_strikeCase}="currentRow.id" />
</el-tab-pane>
#end
</el-tabs>
</ContentWrap>
#end
</template>
<script setup lang="ts">
import { getIntDictOptions, getStrDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { isEmpty } from '@/utils/is'
import { dateFormatter } from '@/utils/formatTime'
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
import { handleTree } from '@/utils/tree'
#end
import download from '@/utils/download'
import { ${simpleClassName}Api, ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
import ${simpleClassName}Form from './${simpleClassName}Form.vue'
## 特殊:主子表专属逻辑
#if ( $table.templateType != 10 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
import ${subSimpleClassName}List from './components/${subSimpleClassName}List.vue'
#end
#end
/** ${table.classComment} 列表 */
defineOptions({ name: '${table.className}' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<${simpleClassName}[]>([]) // 列表的数据
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
const total = ref(0) // 列表的总页数
#end
const queryParams = reactive({
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
pageNo: 1,
pageSize: 10,
#end
#foreach ($column in $columns)
#if ($column.listOperation)
#if ($column.listOperationCondition != 'BETWEEN')
$column.javaField: undefined,
#end
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
$column.javaField: [],
#end
#end
#end
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType == 2 )
const data = await ${simpleClassName}Api.get${simpleClassName}List(queryParams)
list.value = handleTree(data, 'id', '${treeParentColumn.javaField}')
#else
const data = await ${simpleClassName}Api.get${simpleClassName}Page(queryParams)
list.value = data.list
total.value = data.total
#end
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await ${simpleClassName}Api.delete${simpleClassName}(id)
message.success(t('common.delSuccess'))
#if ( $table.templateType == 11 )
currentRow.value = {}
#end
// 刷新列表
await getList()
} catch {}
}
#if ($table.templateType != 2 && $deleteBatchEnable)
/** 批量删除${table.classComment} */
const handleDeleteBatch = async () => {
try {
// 删除的二次确认
await message.delConfirm()
await ${simpleClassName}Api.delete${simpleClassName}List(checkedIds.value);
checkedIds.value = [];
message.success(t('common.delSuccess'))
await getList();
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: ${simpleClassName}[]) => {
checkedIds.value = records.map((item) => item.id!);
}
#end
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await ${simpleClassName}Api.export${simpleClassName}(queryParams)
download.excel(data, '${table.classComment}.xls')
} catch {
} finally {
exportLoading.value = false
}
}
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 )
/** 选中行操作 */
const currentRow = ref({}) // 选中行
const handleCurrentChange = (row) => {
currentRow.value = row
}
#end
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
/** 展开/折叠操作 */
const isExpandAll = ref(true) // 是否展开,默认全部展开
const refreshTable = ref(true) // 重新渲染表格状态
const toggleExpandAll = async () => {
refreshTable.value = false
isExpandAll.value = !isExpandAll.value
await nextTick()
refreshTable.value = true
}
#end
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@@ -1,297 +0,0 @@
<template>
<!-- 搜索框入口 -->
<view @click="visible = true">
<wd-search :placeholder="placeholder" hide-cancel disabled />
</view>
<!-- 搜索弹窗 -->
<wd-popup v-model="visible" position="top" @close="visible = false">
<view class="yd-search-form-container" :style="{ paddingTop: `#[[${]]#getNavbarHeight()#[[}]]#px` }">
#set ($hasDict = 0)
#foreach ($column in $columns)
#if ($hasDict == 0 && $column.listOperation && $column.dictType && "" != $column.dictType)
#set ($hasDict = 1)
#end
#end
#foreach($column in $columns)
#if ($column.listOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($listOperationCondition = $column.listOperationCondition)
#set ($comment = $column.columnComment)
#set ($dictMethod = "getDictOptions")
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
#set ($dictMethod = "getIntDictOptions")
#elseif ($javaType == "String")
#set ($dictMethod = "getStrDictOptions")
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
#if ($column.htmlType == "input")
<view class="yd-search-form-item">
<view class="yd-search-form-label">
${comment}
</view>
<wd-input
v-model="formData.${javaField}"
placeholder="请输入${comment}"
clearable
/>
</view>
#elseif ($column.htmlType == "datetime" && $listOperationCondition == "BETWEEN")
#set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})
<view class="yd-search-form-item">
<view class="yd-search-form-label">
${comment}
</view>
<view class="yd-search-form-date-range-container">
<view @click="visible${AttrName}[0] = true">
<view class="yd-search-form-date-range-picker">
{{ formatDate(formData.${javaField}?.[0]) || '开始日期' }}
</view>
</view>
-
<view @click="visible${AttrName}[1] = true">
<view class="yd-search-form-date-range-picker">
{{ formatDate(formData.${javaField}?.[1]) || '结束日期' }}
</view>
</view>
</view>
<wd-datetime-picker-view v-if="visible${AttrName}[0]" v-model="temp${AttrName}[0]" type="date" />
<view v-if="visible${AttrName}[0]" class="yd-search-form-date-range-actions">
<wd-button size="small" plain @click="visible${AttrName}[0] = false">
取消
</wd-button>
<wd-button size="small" type="primary" @click="handle${AttrName}0Confirm">
确定
</wd-button>
</view>
<wd-datetime-picker-view v-if="visible${AttrName}[1]" v-model="temp${AttrName}[1]" type="date" />
<view v-if="visible${AttrName}[1]" class="yd-search-form-date-range-actions">
<wd-button size="small" plain @click="visible${AttrName}[1] = false">
取消
</wd-button>
<wd-button size="small" type="primary" @click="handle${AttrName}1Confirm">
确定
</wd-button>
</view>
</view>
#elseif (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
<view class="yd-search-form-item">
<view class="yd-search-form-label">
${comment}
</view>
<wd-radio-group v-model="formData.${javaField}" shape="button">
<wd-radio :value="-1">
全部
</wd-radio>
<wd-radio
v-for="dict in $dictMethod(DICT_TYPE.$dictType.toUpperCase())"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</wd-radio>
</wd-radio-group>
</view>
#else
<view class="yd-search-form-item">
<view class="yd-search-form-label">
${comment}
</view>
<wd-input
v-model="formData.${javaField}"
placeholder="请输入${comment}"
clearable
/>
</view>
#end
#end
#end
<view class="yd-search-form-actions">
<wd-button class="flex-1" plain @click="handleReset">
重置
</wd-button>
<wd-button class="flex-1" type="primary" @click="handleSearch">
搜索
</wd-button>
</view>
</view>
</wd-popup>
</template>
<script lang="ts" setup>
#set ($hasDict = 0)
#set ($hasGetDictOptions = 0)
#set ($hasGetIntDictOptions = 0)
#set ($hasGetStrDictOptions = 0)
#set ($hasGetBoolDictOptions = 0)
#set ($hasDateTimeBetween = 0)
#foreach($column in $columns)
#if ($column.listOperation && $column.dictType && "" != $column.dictType)
#set ($hasDict = 1)
#if ($column.htmlType == "select" || $column.htmlType == "radio")
#if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")
#set ($hasGetIntDictOptions = 1)
#elseif ($column.javaType == "String")
#set ($hasGetStrDictOptions = 1)
#elseif ($column.javaType == "Boolean")
#set ($hasGetBoolDictOptions = 1)
#else
#set ($hasGetDictOptions = 1)
#end
#end
#end
#if ($hasDateTimeBetween == 0 && $column.listOperation && $column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
#set ($hasDateTimeBetween = 1)
#end
#end
import { computed, reactive, ref } from 'vue'
import { getNavbarHeight } from '@/utils'
#if ($hasDateTimeBetween == 1)
import { formatDate, formatDateRange } from '@/utils/date'
#end
#if ($hasDict == 1)
#set ($dictImportNames = "getDictLabel, ")
#if ($hasGetDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getDictOptions, ")
#end
#if ($hasGetIntDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getIntDictOptions, ")
#end
#if ($hasGetStrDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getStrDictOptions, ")
#end
#if ($hasGetBoolDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getBoolDictOptions, ")
#end
#set ($dictImportNames = $dictImportNames.trim())
#set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1))
import { $dictImportNames } from '@/hooks/useDict'
import { DICT_TYPE } from '@/utils/constants'
#end
const emit = defineEmits<{
search: [data: Record<string, any>]
reset: []
}>()
const visible = ref(false)
const formData = reactive({
#foreach($column in $columns)
#if ($column.listOperation)
#set ($javaType = $column.javaType.toLowerCase())
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
${column.javaField}: [undefined, undefined] as [number | undefined, number | undefined],
#elseif ($column.dictType && "" != $column.dictType)
#if(${javaType} == "string")
${column.javaField}: -1 as -1 | string, // -1 表示全部
#elseif(${javaType} == "boolean")
${column.javaField}: -1 as -1 | boolean, // -1 表示全部
#else
${column.javaField}: -1, // -1 表示全部
#end
#elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
${column.javaField}: undefined as number | undefined,
#elseif(${javaType} == "boolean")
${column.javaField}: undefined as boolean | undefined,
#else
${column.javaField}: undefined as string | undefined,
#end
#end
#end
})
/** 搜索条件 placeholder 拼接 */
const placeholder = computed(() => {
const conditions: string[] = []
#foreach($column in $columns)
#if ($column.listOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType.toLowerCase())
#set ($comment = $column.columnComment)
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
if (formData.${javaField}?.[0] && formData.${javaField}?.[1]) {
conditions.push(`${comment}:#[[${]]#formatDate(formData.${javaField}[0])#[[}]]#~#[[${]]#formatDate(formData.${javaField}[1])#[[}]]#`)
}
#elseif ($dictType && "" != $dictType)
if (formData.${javaField} !== -1) {
conditions.push(`${comment}:#[[${]]#getDictLabel(DICT_TYPE.${dictType.toUpperCase()}, formData.${javaField})#[[}]]#`)
}
#else
#if(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte" || ${javaType} == "boolean")
if (formData.${javaField} !== undefined) {
conditions.push(`${comment}:#[[${]]#formData.${javaField}#[[}]]#`)
}
#else
if (formData.${javaField}) {
conditions.push(`${comment}:#[[${]]#formData.${javaField}#[[}]]#`)
}
#end
#end
#end
#end
return conditions.length > 0 ? conditions.join(' | ') : '搜索${table.classComment}'
})
#if ($hasDateTimeBetween == 1)
// 时间范围选择器状态
#foreach($column in $columns)
#if ($column.listOperation && $column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
#set ($javaField = $column.javaField)
#set ($AttrName = $javaField.substring(0,1).toUpperCase() + ${javaField.substring(1)})
const visible${AttrName} = ref<[boolean, boolean]>([false, false])
const temp${AttrName} = ref<[number, number]>([Date.now(), Date.now()])
/** ${column.columnComment}[0]确认 */
function handle${AttrName}0Confirm() {
formData.${javaField} = [temp${AttrName}.value[0], formData.${javaField}?.[1]]
visible${AttrName}.value[0] = false
}
/** ${column.columnComment}[1]确认 */
function handle${AttrName}1Confirm() {
formData.${javaField} = [formData.${javaField}?.[0], temp${AttrName}.value[1]]
visible${AttrName}.value[1] = false
}
#end
#end
#end
/** 搜索 */
function handleSearch() {
visible.value = false
emit('search', {
...formData,
#foreach($column in $columns)
#if ($column.listOperation)
#if ($column.dictType && "" != $column.dictType)
${column.javaField}: formData.${column.javaField} === -1 ? undefined : formData.${column.javaField},
#elseif ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
${column.javaField}: formatDateRange(formData.${column.javaField}),
#end
#end
#end
})
}
/** 重置 */
function handleReset() {
#foreach($column in $columns)
#if ($column.listOperation)
#if ($column.htmlType == "datetime" && $column.listOperationCondition == "BETWEEN")
formData.${column.javaField} = [undefined, undefined]
#elseif ($column.dictType && "" != $column.dictType)
formData.${column.javaField} = -1
#else
formData.${column.javaField} = undefined
#end
#end
#end
visible.value = false
emit('reset')
}
</script>

View File

@@ -1,151 +0,0 @@
<template>
<view class="yd-page-container">
<!-- 顶部导航栏 -->
<wd-navbar
title="${table.classComment}详情"
left-arrow placeholder safe-area-inset-top fixed
@click-left="handleBack"
/>
<!-- 详情内容 -->
<view>
<wd-cell-group border>
#foreach($column in $columns)
#if ($column.primaryKey || $column.listOperationResult || $column.createOperation || $column.updateOperation)
#set ($javaField = $column.javaField)
#set ($comment = $column.columnComment)
#if ($column.dictType && "" != $column.dictType)
<wd-cell title="${comment}">
<dict-tag :type="DICT_TYPE.${column.dictType.toUpperCase()}" :value="formData?.${javaField}" />
</wd-cell>
#elseif ($column.javaType == "LocalDateTime")
<wd-cell title="${comment}" :value="formatDateTime(formData?.${javaField}) || '-'" />
#else
<wd-cell title="${comment}" :value="formData?.${javaField} ?? '-'" />
#end
#end
#end
</wd-cell-group>
</view>
<!-- 底部操作按钮 -->
<view class="yd-detail-footer">
<view class="yd-detail-footer-actions">
<wd-button
v-if="hasAccessByCodes(['${permissionPrefix}:update'])"
class="flex-1" type="warning" @click="handleEdit"
>
编辑
</wd-button>
<wd-button
v-if="hasAccessByCodes(['${permissionPrefix}:delete'])"
class="flex-1" type="error" :loading="deleting" @click="handleDelete"
>
删除
</wd-button>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
#set ($hasDict = 0)
#foreach($column in $columns)
#if ($hasDict == 0 && $column.dictType && "" != $column.dictType)
#set ($hasDict = 1)
#end
#end
#set ($hasDateTime = 0)
#foreach($column in $columns)
#if ($hasDateTime == 0 && $column.javaType == "LocalDateTime")
#set ($hasDateTime = 1)
#end
#end
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
import { onMounted, ref } from 'vue'
import { useToast } from 'wot-design-uni'
import { delete${simpleClassName}, get${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
import { useAccess } from '@/hooks/useAccess'
import { navigateBackPlus } from '@/utils'
#if ($hasDict == 1)
import { DICT_TYPE } from '@/utils/constants'
#end
#if ($hasDateTime == 1)
import { formatDateTime } from '@/utils/date'
#end
const props = defineProps<{
id?: number | any
}>()
definePage({
style: {
navigationBarTitleText: '',
navigationStyle: 'custom',
},
})
const { hasAccessByCodes } = useAccess()
const toast = useToast()
const formData = ref<${simpleClassName}>()
const deleting = ref(false)
/** 返回上一页 */
function handleBack() {
navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index')
}
/** 加载${table.classComment}详情 */
async function getDetail() {
if (!props.id) {
return
}
try {
toast.loading('加载中...')
formData.value = await get${simpleClassName}(props.id)
} finally {
toast.close()
}
}
/** 编辑${table.classComment} */
function handleEdit() {
uni.navigateTo({
url: `/pages-${table.moduleName}/${table.businessName}/form/index?id=#[[${]]#props.id#[[}]]#`,
})
}
/** 删除${table.classComment} */
function handleDelete() {
if (!props.id) {
return
}
uni.showModal({
title: '提示',
content: '确定要删除该${table.classComment}吗?',
success: async (res) => {
if (!res.confirm) {
return
}
deleting.value = true
try {
await delete${simpleClassName}(props.id)
toast.success('删除成功')
setTimeout(() => {
handleBack()
}, 500)
} finally {
deleting.value = false
}
},
})
}
/** 初始化 */
onMounted(() => {
getDetail()
})
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,253 +0,0 @@
<template>
<view class="yd-page-container">
<!-- 顶部导航栏 -->
<wd-navbar
:title="getTitle"
left-arrow placeholder safe-area-inset-top fixed
@click-left="handleBack"
/>
<!-- 表单区域 -->
<view>
<wd-form ref="formRef" :model="formData" :rules="formRules">
<wd-cell-group border>
#foreach($column in $columns)
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#set ($comment = $column.columnComment)
#set ($dictMethod = "getDictOptions")
#if ($javaType == "Integer" || $javaType == "Long" || $javaType == "Byte" || $javaType == "Short")
#set ($dictMethod = "getIntDictOptions")
#elseif ($javaType == "String")
#set ($dictMethod = "getStrDictOptions")
#elseif ($javaType == "Boolean")
#set ($dictMethod = "getBoolDictOptions")
#end
## 优先判断是否有字典,有字典则使用 radio-group
#if (($column.htmlType == "select" || $column.htmlType == "radio") && $dictType && "" != $dictType)
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
<wd-radio-group v-model="formData.${javaField}" shape="button">
<wd-radio
v-for="dict in $dictMethod(DICT_TYPE.${dictType.toUpperCase()})"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</wd-radio>
</wd-radio-group>
</wd-cell>
## 数字类型(无字典)
#elseif (${javaType.toLowerCase()} == "long" || ${javaType.toLowerCase()} == "integer" || ${javaType.toLowerCase()} == "short" || ${javaType.toLowerCase()} == "double" || ${javaType.toLowerCase()} == "bigdecimal" || ${javaType.toLowerCase()} == "byte")
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
<wd-input-number
v-model="formData.${javaField}"
:min="0"
/>
</wd-cell>
## 布尔类型
#elseif (${javaType.toLowerCase()} == "boolean")
<wd-cell title="${comment}" title-width="180rpx" prop="${javaField}" center>
<wd-switch v-model="formData.${javaField}" />
</wd-cell>
## 日期时间类型
#elseif (${javaType.toLowerCase()} == "date" || ${javaType.toLowerCase()} == "localdate" || ${javaType.toLowerCase()} == "localdatetime")
#set ($pickerType = "date")
#if (${javaType.toLowerCase()} == "localdatetime")
#set ($pickerType = "datetime")
#end
<wd-datetime-picker
v-model="formData.${javaField}"
type="${pickerType}"
label="${comment}"
label-width="180rpx"
prop="${javaField}"
/>
## 文本域
#elseif ($column.htmlType == "textarea")
<wd-textarea
v-model="formData.${javaField}"
label="${comment}"
label-width="180rpx"
placeholder="请输入${comment}"
:maxlength="200"
show-word-limit
clearable
/>
## 默认:文本输入
#else
<wd-input
v-model="formData.${javaField}"
label="${comment}"
label-width="180rpx"
prop="${javaField}"
clearable
placeholder="请输入${comment}"
/>
#end
#end
#end
</wd-cell-group>
</wd-form>
</view>
<!-- 底部保存按钮 -->
<view class="yd-detail-footer">
<wd-button
type="primary"
block
:loading="formLoading"
@click="handleSubmit"
>
保存
</wd-button>
</view>
</view>
</template>
<script lang="ts" setup>
#set ($primaryJavaType = $primaryColumn.javaType.toLowerCase())
#if(${primaryJavaType} == "long" || ${primaryJavaType} == "integer" || ${primaryJavaType} == "short" || ${primaryJavaType} == "double" || ${primaryJavaType} == "bigdecimal" || ${primaryJavaType} == "byte")
#set ($primaryTsType = "number")
#else
#set ($primaryTsType = "string")
#end
#set ($hasDict = 0)
#set ($hasGetDictOptions = 0)
#set ($hasGetIntDictOptions = 0)
#set ($hasGetStrDictOptions = 0)
#set ($hasGetBoolDictOptions = 0)
#foreach ($column in $columns)
#if (($column.createOperation || $column.updateOperation) && !$column.primaryKey
&& ($column.htmlType == "select" || $column.htmlType == "radio")
&& $column.dictType && "" != $column.dictType)
#set ($hasDict = 1)
#if ($column.javaType == "Integer" || $column.javaType == "Long" || $column.javaType == "Byte" || $column.javaType == "Short")
#set ($hasGetIntDictOptions = 1)
#elseif ($column.javaType == "String")
#set ($hasGetStrDictOptions = 1)
#elseif ($column.javaType == "Boolean")
#set ($hasGetBoolDictOptions = 1)
#else
#set ($hasGetDictOptions = 1)
#end
#end
#end
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
import { computed, onMounted, ref } from 'vue'
import { useToast } from 'wot-design-uni'
import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
#if ($hasDict == 1)
#set ($dictImportNames = "")
#if ($hasGetDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getDictOptions, ")
#end
#if ($hasGetIntDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getIntDictOptions, ")
#end
#if ($hasGetStrDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getStrDictOptions, ")
#end
#if ($hasGetBoolDictOptions == 1)
#set ($dictImportNames = "${dictImportNames}getBoolDictOptions, ")
#end
#set ($dictImportNames = $dictImportNames.trim())
#set ($dictImportNames = $dictImportNames.substring(0, $dictImportNames.length() - 1))
import { $dictImportNames } from '@/hooks/useDict'
#end
import { navigateBackPlus } from '@/utils'
#if ($hasDict == 1)
import { DICT_TYPE } from '@/utils/constants'
#end
const props = defineProps<{
id?: ${primaryTsType} | any
}>()
definePage({
style: {
navigationBarTitleText: '',
navigationStyle: 'custom',
},
})
const toast = useToast()
const getTitle = computed(() => props.id ? '编辑${table.classComment}' : '新增${table.classComment}')
const formLoading = ref(false)
const formData = ref<${simpleClassName}>({
#foreach($column in $columns)
#if (($column.createOperation || $column.updateOperation) || $column.primaryKey)
#set ($javaType = $column.javaType.toLowerCase())
#set ($javaFieldLower = $column.javaField.toLowerCase())
#set ($optional = $column.nullable || $column.primaryKey || $javaFieldLower == "createtime" || $javaFieldLower == "updatetime")
#if ($column.primaryKey)
${column.javaField}: undefined,
#elseif(${javaType} == "long" || ${javaType} == "integer" || ${javaType} == "short" || ${javaType} == "double" || ${javaType} == "bigdecimal" || ${javaType} == "byte")
${column.javaField}: 0,
#elseif(${javaType} == "boolean")
${column.javaField}: false,
#elseif(${javaType} == "date" || ${javaType} == "localdate" || ${javaType} == "localdatetime")
${column.javaField}: undefined,
#else
${column.javaField}: '',
#end
#end
#end
})
const formRules = {
#foreach($column in $columns)
#set ($javaFieldLower = $column.javaField.toLowerCase())
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !$column.primaryKey
&& $javaFieldLower != "createtime" && $javaFieldLower != "updatetime")
${column.javaField}: [{ required: true, message: '${column.columnComment}不能为空' }],
#end
#end
}
const formRef = ref()
/** 返回上一页 */
function handleBack() {
navigateBackPlus('/pages-${table.moduleName}/${table.businessName}/index')
}
/** 加载${table.classComment}详情 */
async function getDetail() {
if (!props.id) {
return
}
formData.value = await get${simpleClassName}(props.id)
}
/** 提交表单 */
async function handleSubmit() {
const { valid } = await formRef.value.validate()
if (!valid) {
return
}
formLoading.value = true
try {
if (props.id) {
await update${simpleClassName}(formData.value)
toast.success('修改成功')
} else {
await create${simpleClassName}(formData.value)
toast.success('新增成功')
}
setTimeout(() => {
handleBack()
}, 500)
} finally {
formLoading.value = false
}
}
/** 初始化 */
onMounted(() => {
getDetail()
})
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,211 +0,0 @@
<template>
<view class="yd-page-container">
<!-- 顶部导航栏 -->
<wd-navbar
title="${table.classComment}管理"
left-arrow placeholder safe-area-inset-top fixed
@click-left="handleBack"
/>
<!-- 搜索组件 -->
<SearchForm @search="handleQuery" @reset="handleReset" />
<!-- ${table.classComment}列表 -->
<view class="p-24rpx">
<view
v-for="item in list"
:key="item.${primaryColumn.javaField}"
class="mb-24rpx overflow-hidden rounded-12rpx bg-white shadow-sm"
@click="handleDetail(item)"
>
<view class="p-24rpx">
#set ($titleField = "")
#set ($statusField = "")
#set ($statusDictType = "")
#foreach($column in $columns)
#if ($titleField == "" && !$column.primaryKey && $column.listOperationResult)
#set ($titleField = $column.javaField)
#set ($titleComment = $column.columnComment)
#end
#if ($statusField == "" && $column.listOperationResult && $column.dictType && "" != $column.dictType)
#set ($statusField = $column.javaField)
#set ($statusDictType = $column.dictType)
#end
#end
#if ($titleField == "")
#set ($titleField = $primaryColumn.javaField)
#end
<view class="mb-16rpx flex items-center justify-between">
<view class="text-32rpx text-[#333] font-semibold">
{{ item.${titleField} }}
</view>
#if($statusField != "")
<dict-tag :type="DICT_TYPE.${statusDictType.toUpperCase()}" :value="item.${statusField}" />
#end
</view>
#foreach($column in $columns)
#if ($column.listOperationResult && !$column.primaryKey && $column.javaField != $titleField && $column.javaField != $statusField)
#set ($javaField = $column.javaField)
#set ($comment = $column.columnComment)
#set ($dictType = $column.dictType)
#set ($javaType = $column.javaType)
#if ($dictType && "" != $dictType)
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
<text class="mr-8rpx text-[#999]">${comment}</text>
<dict-tag :type="DICT_TYPE.${dictType.toUpperCase()}" :value="item.${javaField}" />
</view>
#elseif ($javaType == "LocalDateTime")
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
<text class="mr-8rpx text-[#999]">${comment}</text>
<text class="line-clamp-1">{{ formatDateTime(item.${javaField}) || '-' }}</text>
</view>
#else
<view class="mb-12rpx flex items-center text-28rpx text-[#666]">
<text class="mr-8rpx text-[#999]">${comment}</text>
<text class="line-clamp-1">{{ item.${javaField} }}</text>
</view>
#end
#end
#end
</view>
</view>
<!-- 加载更多 -->
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="py-100rpx text-center">
<wd-status-tip image="content" tip="暂无${table.classComment}数据" />
</view>
<wd-loadmore
v-if="list.length > 0"
:state="loadMoreState"
@reload="loadMore"
/>
</view>
<!-- 新增按钮 -->
<wd-fab
v-if="hasAccessByCodes(['${permissionPrefix}:create'])"
position="right-bottom"
type="primary"
:expandable="false"
@click="handleAdd"
/>
</view>
</template>
<script lang="ts" setup>
#set ($hasDict = 0)
#foreach($column in $columns)
#if ($hasDict == 0 && $column.listOperationResult && $column.dictType && "" != $column.dictType)
#set ($hasDict = 1)
#end
#end
#set ($hasDateTime = 0)
#foreach($column in $columns)
#if ($column.listOperationResult)
#if ($hasDateTime == 0 && $column.javaType == "LocalDateTime")
#set ($hasDateTime = 1)
#end
#end
#end
import type { ${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
import type { LoadMoreState } from '@/http/types'
import { onReachBottom } from '@dcloudio/uni-app'
import { onMounted, ref } from 'vue'
import { get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}'
import { useAccess } from '@/hooks/useAccess'
import { navigateBackPlus } from '@/utils'
#if ($hasDict == 1)
import { DICT_TYPE } from '@/utils/constants'
#end
#if ($hasDateTime == 1)
import { formatDateTime } from '@/utils/date'
#end
import SearchForm from './components/search-form.vue'
definePage({
style: {
navigationBarTitleText: '',
navigationStyle: 'custom',
},
})
const { hasAccessByCodes } = useAccess()
const total = ref(0)
const list = ref<${simpleClassName}[]>([])
const loadMoreState = ref<LoadMoreState>('loading')
const queryParams = ref({
pageNo: 1,
pageSize: 10,
})
/** 返回上一页 */
function handleBack() {
navigateBackPlus()
}
/** 查询${table.classComment}列表 */
async function getList() {
loadMoreState.value = 'loading'
try {
const data = await get${simpleClassName}Page(queryParams.value)
list.value = [...list.value, ...data.list]
total.value = data.total
loadMoreState.value = list.value.length >= total.value ? 'finished' : 'loading'
} catch {
queryParams.value.pageNo = queryParams.value.pageNo > 1 ? queryParams.value.pageNo - 1 : 1
loadMoreState.value = 'error'
}
}
/** 搜索按钮操作 */
function handleQuery(data?: Record<string, any>) {
queryParams.value = {
...data,
pageNo: 1,
pageSize: queryParams.value.pageSize,
}
list.value = []
getList()
}
/** 重置按钮操作 */
function handleReset() {
handleQuery()
}
/** 加载更多 */
function loadMore() {
if (loadMoreState.value === 'finished') {
return
}
queryParams.value.pageNo++
getList()
}
/** 新增${table.classComment} */
function handleAdd() {
uni.navigateTo({
url: '/pages-${table.moduleName}/${table.businessName}/form/index',
})
}
/** 查看详情 */
function handleDetail(item: ${simpleClassName}) {
uni.navigateTo({
url: `/pages-${table.moduleName}/${table.businessName}/detail/index?id=#[[${]]#item.${primaryColumn.javaField}#[[}]]#`,
})
}
/** 触底加载更多 */
onReachBottom(() => {
loadMore()
})
/** 初始化 */
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,32 +0,0 @@
import { defHttp } from '@/utils/http/axios'
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
// 查询${table.classComment}列表
export function get${simpleClassName}Page(params) {
return defHttp.get({ url: '${baseURL}/page', params })
}
// 查询${table.classComment}详情
export function get${simpleClassName}(id: number) {
return defHttp.get({ url: `${baseURL}/get?id=${id}` })
}
// 新增${table.classComment}
export function create${simpleClassName}(data) {
return defHttp.post({ url: '${baseURL}/create', data })
}
// 修改${table.classComment}
export function update${simpleClassName}(data) {
return defHttp.put({ url: '${baseURL}/update', data })
}
// 删除${table.classComment}
export function delete${simpleClassName}(id: number) {
return defHttp.delete({ url: `${baseURL}/delete?id=${id}` })
}
// 导出${table.classComment} Excel
export function export${simpleClassName}(params) {
return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls')
}

View File

@@ -1,260 +0,0 @@
import type {BasicColumn, FormSchema} from '@/components/Table'
import {useRender} from '@/components/Table'
import {DICT_TYPE, getDictOptions} from '@/utils/dict'
export const columns: BasicColumn[] = [
#foreach($column in $columns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment=$column.columnComment)
#if ($column.javaType == "LocalDateTime")## 时间类型
{
title: '${comment}',
dataIndex: '${javaField}',
width: 180,
customRender: ({ text }) => {
return useRender.renderDate(text)
},
},
#elseif("" != $column.dictType)## 数据字典
{
title: '${comment}',
dataIndex: '${javaField}',
width: 180,
customRender: ({ text }) => {
return useRender.renderDict(text, DICT_TYPE.$dictType.toUpperCase())
},
},
#else
{
title: '${comment}',
dataIndex: '${javaField}',
width: 160,
},
#end
#end
#end
]
export const searchFormSchema: FormSchema[] = [
#foreach($column in $columns)
#if ($column.listOperation)
#set ($dictType=$column.dictType)
#set ($javaType = $column.javaType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#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
{
label: '${comment}',
field: '${javaField}',
#if ($column.htmlType == "input")
component: 'Input',
#elseif ($column.htmlType == "select")
component: 'Select',
componentProps: {
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
#else## 未设置 dictType 数据字典的情况
options: [],
#end
},
#elseif ($column.htmlType == "radio")
component: 'RadioButtonGroup',
componentProps: {
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
#else## 未设置 dictType 数据字典的情况
options: [],
#end
},
#elseif($column.htmlType == "datetime")
component: 'RangePicker',
#end
colProps: { span: 8 },
},
#end
#end
]
export const createFormSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
#foreach($column in $columns)
#if ($column.createOperation)
#set ($dictType = $column.dictType)
#set ($javaType = $column.javaType)
#set ($javaField = $column.javaField)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#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.primaryKey)## 忽略主键,不用在表单里
{
label: '${comment}',
field: '${javaField}',
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
required: true,
#end
#if ($column.htmlType == "input")
component: 'Input',
#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
},
#elseif($column.htmlType == "checkbox")## 多选框
component: 'Checkbox',
componentProps: {
#if ("" != $dictType)## 有数据字典
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
#else##没数据字典
options:[],
#end
},
#elseif($column.htmlType == "radio")## 单选框
component: 'RadioButtonGroup',
componentProps: {
#if ("" != $dictType)## 有数据字典
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
#else##没数据字典
options:[],
#end
},
#elseif($column.htmlType == "datetime")## 时间框
component: 'DatePicker',
componentProps: {
showTime: true,
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'x',
},
#elseif($column.htmlType == "textarea")## 文本域
component: 'InputTextArea',
#end
},
#end
#end
#end
]
export const updateFormSchema: FormSchema[] = [
{
label: '编号',
field: 'id',
show: false,
component: 'Input',
},
#foreach($column in $columns)
#if ($column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaType = $column.javaType)
#set ($javaField = $column.javaField)
#set ($AttrName = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#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.primaryKey)## 忽略主键,不用在表单里
{
label: '${comment}',
field: '${javaField}',
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
required: true,
#end
#if ($column.htmlType == "input")
component: 'Input',
#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
},
#elseif($column.htmlType == "checkbox")## 多选框
component: 'Checkbox',
componentProps: {
#if ("" != $dictType)## 有数据字典
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
#else##没数据字典
options:[],
#end
},
#elseif($column.htmlType == "radio")## 单选框
component: 'RadioButtonGroup',
componentProps: {
#if ("" != $dictType)## 有数据字典
options: getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod'),
#else##没数据字典
options:[],
#end
},
#elseif($column.htmlType == "datetime")## 时间框
component: 'DatePicker',
componentProps: {
showTime: true,
format: 'YYYY-MM-DD HH:mm:ss',
valueFormat: 'x',
},
#elseif($column.htmlType == "textarea")## 文本域
component: 'InputTextArea',
#end
},
#end
#end
#end
]

View File

@@ -1,58 +0,0 @@
<script lang="ts" setup>
import { ref, unref } from 'vue'
import { createFormSchema, updateFormSchema } from './${classNameVar}.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { BasicForm, useForm } from '@/components/Form'
import { BasicModal, useModalInner } from '@/components/Modal'
import { create${simpleClassName}, get${simpleClassName}, update${simpleClassName} } from '@/api/${table.moduleName}/${table.businessName}'
defineOptions({ name: '${table.className}Modal' })
const emit = defineEmits(['success', 'register'])
const { t } = useI18n()
const { createMessage } = useMessage()
const isUpdate = ref(true)
const [registerForm, { setFieldsValue, resetFields, resetSchema, validate }] = useForm({
labelWidth: 120,
baseColProps: { span: 24 },
schemas: createFormSchema,
showActionButtonGroup: false,
actionColOptions: { span: 23 },
})
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
resetFields()
setModalProps({ confirmLoading: false })
isUpdate.value = !!data?.isUpdate
if (unref(isUpdate)) {
resetSchema(updateFormSchema)
const res = await get${simpleClassName}(data.record.id)
setFieldsValue({ ...res })
}
})
async function handleSubmit() {
try {
const values = await validate()
setModalProps({ confirmLoading: true })
if (unref(isUpdate))
await update${simpleClassName}(values)
else
await create${simpleClassName}(values)
closeModal()
emit('success')
createMessage.success(t('common.saveSuccessText'))
} finally {
setModalProps({ confirmLoading: false })
}
}
</script>
<template>
<BasicModal v-bind="$attrs" :title="isUpdate ? t('action.edit') : t('action.create')" @register="registerModal" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>

View File

@@ -1,92 +0,0 @@
<script lang="ts" setup>
import ${simpleClassName}Modal from './${simpleClassName}Modal.vue'
import { columns, searchFormSchema } from './${classNameVar}.data'
import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage'
import { useModal } from '@/components/Modal'
import { IconEnum } from '@/enums/appEnum'
import { BasicTable, TableAction, useTable } from '@/components/Table'
import { delete${simpleClassName}, export${simpleClassName}, get${simpleClassName}Page } from '@/api/${table.moduleName}/${table.businessName}'
defineOptions({ name: '${table.className}' })
const { t } = useI18n()
const { createConfirm, createMessage } = useMessage()
const [registerModal, { openModal }] = useModal()
const [registerTable, { getForm, reload }] = useTable({
title: '${table.classComment}列表',
api: get${simpleClassName}Page,
columns,
formConfig: { labelWidth: 120, schemas: searchFormSchema },
useSearchForm: true,
showTableSetting: true,
actionColumn: {
width: 140,
title: t('common.action'),
dataIndex: 'action',
fixed: 'right',
},
})
function handleCreate() {
openModal(true, { isUpdate: false })
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true })
}
async function handleExport() {
createConfirm({
title: t('common.exportTitle'),
iconType: 'warning',
content: t('common.exportMessage'),
async onOk() {
await export${simpleClassName}(getForm().getFieldsValue())
createMessage.success(t('common.exportSuccessText'))
},
})
}
async function handleDelete(record: Recordable) {
await delete${simpleClassName}(record.id)
createMessage.success(t('common.delSuccessText'))
reload()
}
</script>
<template>
<div>
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" v-auth="['${permissionPrefix}:create']" :preIcon="IconEnum.ADD" @click="handleCreate">
{{ t('action.create') }}
</a-button>
<a-button v-auth="['${permissionPrefix}:export']" :preIcon="IconEnum.EXPORT" @click="handleExport">
{{ t('action.export') }}
</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{ icon: IconEnum.EDIT, label: t('action.edit'), auth: '${permissionPrefix}:update', onClick: handleEdit.bind(null, record) },
{
icon: IconEnum.DELETE,
danger: true,
label: t('action.delete'),
auth: '${permissionPrefix}:delete',
popConfirm: {
title: t('common.delMessage'),
placement: 'left',
confirm: handleDelete.bind(null, record),
},
},
]"
/>
</template>
</template>
</BasicTable>
<${simpleClassName}Modal @register="registerModal" @success="reload()" />
</div>
</template>

View File

@@ -1,167 +0,0 @@
import type { PageParam, PageResult } from '@vben/request';
import type { Dayjs } from 'dayjs';
import { requestClient } from '#/api/request';
#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
export namespace ${simpleClassName}Api {
## 特殊:主子表专属逻辑
#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: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
#end
#end
}
#end
/** ${table.classComment}信息 */
export interface ${simpleClassName} {
#foreach ($column in $columns)
#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: string | Dayjs; // ${column.columnComment}
#else
${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment}
#end
#end
#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
}
}
#if ( $table.templateType != 2 )
/** 查询${table.classComment}分页 */
export function get${simpleClassName}Page(params: PageParam) {
return requestClient.get<PageResult<${simpleClassName}Api.${simpleClassName}>>('${baseURL}/page', { params });
}
#else
/** 查询${table.classComment}列表 */
export function get${simpleClassName}List(params: any) {
return requestClient.get<${simpleClassName}Api.${simpleClassName}[]>('${baseURL}/list', { params });
}
#end
/** 查询${table.classComment}详情 */
export function get${simpleClassName}(id: number) {
return requestClient.get<${simpleClassName}Api.${simpleClassName}>(`${baseURL}/get?id=${id}`);
}
/** 新增${table.classComment} */
export function create${simpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) {
return requestClient.post('${baseURL}/create', data);
}
/** 修改${table.classComment} */
export function update${simpleClassName}(data: ${simpleClassName}Api.${simpleClassName}) {
return requestClient.put('${baseURL}/update', data);
}
/** 删除${table.classComment} */
export function delete${simpleClassName}(id: number) {
return requestClient.delete(`${baseURL}/delete?id=${id}`);
}
#if ( $table.templateType != 2 && $deleteBatchEnable)
/** 批量删除${table.classComment} */
export function delete${simpleClassName}List(ids: number[]) {
return requestClient.delete(`${baseURL}/delete-list?ids=${ids.join(',')}`)
}
#end
/** 导出${table.classComment} */
export function export${simpleClassName}(params: any) {
return requestClient.download('${baseURL}/export-excel', { params });
}
## 特殊:主子表专属逻辑
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subPrimaryColumn = $subPrimaryColumns.get($index))##当前 primary 字段
#set ($subJoinColumn = $subJoinColumns.get($index))##当前 join 字段
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
#set ($subClassNameVar = $subClassNameVars.get($index))
// ==================== 子表($subTable.classComment ====================
## 情况一MASTER_ERP 时,需要分查询页子表
#if ( $table.templateType == 11 )
/** 获得${subTable.classComment}分页 */
export function get${subSimpleClassName}Page(params: PageParam) {
return requestClient.get<PageResult<${simpleClassName}Api.${subSimpleClassName}>>(`${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.${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.${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.${subSimpleClassName}) {
return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data);
}
/** 修改${subTable.classComment} */
export function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) {
return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data);
}
/** 删除${subTable.classComment} */
export function delete${subSimpleClassName}(id: number) {
return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`);
}
#if ($deleteBatchEnable)
/** 批量删除${subTable.classComment} */
export function delete${subSimpleClassName}List(ids: number[]) {
return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`)
}
#end
/** 获得${subTable.classComment} */
export function get${subSimpleClassName}(id: number) {
return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`);
}
#end
#end

View File

@@ -1,325 +0,0 @@
<script lang="ts" setup>
import type { Rule } from 'ant-design-vue/es/form';
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
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, TreeSelect } from 'ant-design-vue';
#if($table.templateType == 2)## 树表需要导入这些
import { get${simpleClassName}List } from '#/api/${table.moduleName}/${table.businessName}';
import { handleTree } from '@vben/utils'
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'
#end
#end
import { $t } from '#/locales';
import { get${simpleClassName}, create${simpleClassName}, update${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
const emit = defineEmits(['success']);
const formRef = ref();
const formData = ref<Partial<${simpleClassName}Api.${simpleClassName}>>({
#foreach ($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
});
const rules: Record<string, Rule[]> = {
#foreach ($column in $columns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
#end
#end
};
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
const ${classNameVar}Tree = ref<any[]>([]) // 树形结构
#end
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['${table.classComment}'])
: $t('ui.actionTitle.create', ['${table.classComment}']);
});
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
#if ( $subTables && $subTables.size() > 0 )
/** 子表的表单 */
const subTabsName = ref('$subClassNameVars.get(0)')
#foreach ($subClassNameVar in $subClassNameVars)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
const ${subClassNameVar}FormRef = ref<InstanceType<typeof ${subSimpleClassName}Form>>()
#end
#end
#end
/** 重置表单 */
function resetForm() {
formData.value = {
#foreach ($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
};
formRef.value?.resetFields();
}
## 特殊:树表专属逻辑
#if ( $table.templateType == 2 )
/** 获得${table.classComment}树 */
async function get${simpleClassName}Tree() {
${classNameVar}Tree.value = []
const data = await get${simpleClassName}List({});
data.unshift({
id: 0,
name: '顶级${table.classComment}',
});
${classNameVar}Tree.value = handleTree(data);
}
#end
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
await formRef.value?.validate();
## 特殊:主子表专属逻辑
#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))
#if ($subTable.subJoinMany) ## 一对多
## TODO 列表值校验?
#else
try {
await ${subClassNameVar}FormRef.value?.validate()
} catch (e) {
subTabsName.value = '${subClassNameVar}'
return
}
#end
#end
#end
#end
modalApi.lock();
// 提交表单
const data = formData.value 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))
#if ($subTable.subJoinMany)
data.${subClassNameVar}s = ${subClassNameVar}FormRef.value?.getData();
#else
data.${subClassNameVar} = ${subClassNameVar}FormRef.value?.getValues();
#end
#end
#end
#end
try {
await (formData.value?.id ? update${simpleClassName}(data) : create${simpleClassName}(data));
// 关闭并提示
await modalApi.close();
emit('success');
message.success({
content: $t('ui.actionMessage.operationSuccess'),
});
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
resetForm()
return;
}
// 加载数据
let data = modalApi.getData<${simpleClassName}Api.${simpleClassName}>();
if (!data) {
return;
}
if (data.id) {
modalApi.lock();
try {
data = await get${simpleClassName}(data.id);
} finally {
modalApi.unlock();
}
}
formData.value = data;
#if ( $table.templateType == 2 )
// 加载树数据
await get${simpleClassName}Tree()
#end
},
});
</script>
<template>
<Modal :title="getTitle">
<Form
ref="formRef"
:model="formData"
:rules="rules"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 18 }"
>
#foreach($column in $columns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#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 ( $table.templateType == 2 && $column.id == $treeParentColumn.id )
<Form.Item label="${comment}" name="${javaField}">
<TreeSelect
v-model:value="formData.${javaField}"
:treeData="${classNameVar}Tree"
#if ($treeNameColumn.javaField == "name")
:fieldNames="{
label: 'name',
value: 'id',
children: 'children',
}"
#else
:fieldNames="{
label: '$treeNameColumn.javaField',
value: 'id',
children: 'children',
}"
#end
checkable
treeDefaultExpandAll
placeholder="请选择${comment}"
/>
</Form.Item>
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<Form.Item label="${comment}" name="${javaField}">
<Input v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
</Form.Item>
#elseif($column.htmlType == "imageUpload")## 图片上传
<Form.Item label="${comment}" name="${javaField}">
<ImageUpload v-model:value="formData.${javaField}" />
</Form.Item>
#elseif($column.htmlType == "fileUpload")## 文件上传
<Form.Item label="${comment}" name="${javaField}">
<FileUpload v-model:value="formData.${javaField}" />
</Form.Item>
#elseif($column.htmlType == "editor")## 文本编辑器
<Form.Item label="${comment}" name="${javaField}">
<RichTextarea v-model="formData.${javaField}" height="500px" />
</Form.Item>
#elseif($column.htmlType == "select")## 下拉框
<Form.Item label="${comment}" name="${javaField}">
<Select v-model:value="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<Select.Option
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Select.Option>
#else##没数据字典
<Select.Option label="请选择字典生成" value="" />
#end
</Select>
</Form.Item>
#elseif($column.htmlType == "checkbox")## 多选框
<Form.Item label="${comment}" name="${javaField}">
<CheckboxGroup v-model:value="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<Checkbox
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Checkbox>
#else##没数据字典
<Checkbox label="请选择字典生成" />
#end
</CheckboxGroup>
</Form.Item>
#elseif($column.htmlType == "radio")## 单选框
<Form.Item label="${comment}" name="${javaField}">
<RadioGroup v-model:value="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<Radio
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Radio>
#else##没数据字典
<Radio value="1">请选择字典生成</Radio>
#end
</RadioGroup>
</Form.Item>
#elseif($column.htmlType == "datetime")## 时间框
<Form.Item label="${comment}" name="${javaField}">
<DatePicker
v-model:value="formData.${javaField}"
valueFormat="x"
placeholder="选择${comment}"
/>
</Form.Item>
#elseif($column.htmlType == "textarea")## 文本框
<Form.Item label="${comment}" name="${javaField}">
<Textarea v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
</Form.Item>
#end
#end
#end
</Form>
## 特殊:主子表专属逻辑
#if ( $table.templateType == 10 || $table.templateType == 12 )
<!-- 子表的表单 -->
<Tabs v-model:active-key="subTabsName">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<Tabs.TabPane key="$subClassNameVar" tab="${subTable.classComment}" force-render>
<${subSimpleClassName}Form ref="${subClassNameVar}FormRef" :${subJoinColumn_strikeCase}="formData?.id" />
</Tabs.TabPane>
#end
</Tabs>
#end
</Modal>
</template>

View File

@@ -1,483 +0,0 @@
<script lang="ts" setup>
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
import { ref, h, reactive, onMounted, nextTick } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { useTableToolbar, VbenVxeTableToolbar } from '@vben/plugins/vxe-table';
import { cloneDeep, downloadFileFromBlobPart, formatDateTime, isEmpty } from '@vben/utils';
import { Button, Card, message, Tabs, Pagination, Form, RangePicker, DatePicker, Select, Input } from 'ant-design-vue';
import ${simpleClassName}Form from './modules/form.vue';
import { Download, Plus, RefreshCw, Search, Trash2 } from '@vben/icons';
import { DictTag } from '#/components/dict-tag';
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
import { getRangePickerDefaultProps } from '#/utils/rangePickerProps';
## 特殊:主子表专属逻辑
#if ( $table.templateType == 11 || $table.templateType == 12 )
#foreach ($subSimpleClassName in $subSimpleClassNames)
#set ($index = $foreach.count - 1)
#set ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($index))
import ${subSimpleClassName}List from './modules/${subSimpleClassName_strikeCase}-list.vue'
#end
#end
import { $t } from '#/locales';
#if (${table.templateType} == 2)## 树表接口
import { handleTree } from '@vben/utils'
import { get${simpleClassName}List, delete${simpleClassName}, export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
#else## 标准表接口
import { get${simpleClassName}Page, delete${simpleClassName},#if ($deleteBatchEnable) delete${simpleClassName}List,#end export${simpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
#end
#if ($table.templateType == 12 || $table.templateType == 11) ## 内嵌和erp情况
/** 子表的列表 */
const subTabsName = ref('$subClassNameVars.get(0)')
#if ($table.templateType == 11)
const select${simpleClassName} = ref<${simpleClassName}Api.${simpleClassName}>();
async function onCellClick({ row }: { row: ${simpleClassName}Api.${simpleClassName} }) {
select${simpleClassName}.value = row
}
#end
#end
const loading = ref(true) // 列表的加载中
#if ( $table.templateType == 2 )
const list = ref<any[]>([]) // 树列表的数据
#else
const list = ref<${simpleClassName}Api.${simpleClassName}[]>([]) // 列表的数据
#end
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
const total = ref(0) // 列表的总页数
#end
const queryParams = reactive({
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType != 2 )
pageNo: 1,
pageSize: 10,
#end
#foreach ($column in $columns)
#if ($column.listOperation)
#if ($column.listOperationCondition != 'BETWEEN')
$column.javaField: undefined,
#end
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
$column.javaField: undefined,
#end
#end
#end
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
async function getList() {
loading.value = true
try {
const params = cloneDeep(queryParams) as any;
#foreach ($column in $columns)
#if ($column.listOperation)
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
}
#end
#end
#end
## 特殊:树表专属逻辑(树不需要分页接口)
#if ( $table.templateType == 2 )
list.value = await get${simpleClassName}List(params);
#else
const data = await get${simpleClassName}Page(params)
list.value = data.list
total.value = data.total
#end
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
function handleQuery() {
#if ( $table.templateType != 2 )
queryParams.pageNo = 1
#end
getList()
}
/** 重置按钮操作 */
function resetQuery() {
queryFormRef.value.resetFields()
handleQuery()
}
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: ${simpleClassName}Form,
destroyOnClose: true,
});
/** 创建${table.classComment} */
function handleCreate() {
formModalApi.setData(null).open();
}
/** 编辑${table.classComment} */
function handleEdit(row: ${simpleClassName}Api.${simpleClassName}) {
formModalApi.setData(row).open();
}
#if (${table.templateType} == 2)## 树表特有:新增下级
/** 新增下级${table.classComment} */
function handleAppend(row: ${simpleClassName}Api.${simpleClassName}) {
formModalApi.setData({ ${treeParentColumn.javaField}: row.id }).open();
}
#end
/** 删除${table.classComment} */
async function handleDelete(row: ${simpleClassName}Api.${simpleClassName}) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.id]),
duration: 0,
});
try {
await delete${simpleClassName}(row.id!);
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
await getList();
} finally {
hideLoading();
}
}
#if ($table.templateType != 2 && $deleteBatchEnable)
/** 批量删除${table.classComment} */
async function handleDeleteBatch() {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting'),
duration: 0,
});
try {
await delete${simpleClassName}List(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {
hideLoading();
}
}
const checkedIds = ref<number[]>([])
function handleRowCheckboxChange({
records,
}: {
records: ${simpleClassName}Api.${simpleClassName}[];
}) {
checkedIds.value = records.map((item) => item.id!);
}
#end
/** 导出表格 */
async function handleExport() {
try {
exportLoading.value = true;
const data = await export${simpleClassName}(queryParams);
downloadFileFromBlobPart({ fileName: '${table.classComment}.xls', source: data });
}finally {
exportLoading.value = false;
}
}
#if (${table.templateType} == 2)
/** 切换树形展开/收缩状态 */
const isExpanded = ref(true);
function handleExpand() {
isExpanded.value = !isExpanded.value;
tableRef.value?.setAllTreeExpand(isExpanded.value);
}
#end
/** 初始化 */
const { hiddenSearchBar, tableToolbarRef, tableRef } = useTableToolbar();
onMounted(() => {
getList();
});
</script>
<template>
<Page auto-content-height>
<FormModal @success="getList" />
<Card v-if="!hiddenSearchBar" class="mb-4">
<!-- 搜索工作栏 -->
<Form
:model="queryParams"
ref="queryFormRef"
layout="inline"
>
#foreach($column in $columns)
#if ($column.listOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#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.htmlType == "input")
<Form.Item label="${comment}" name="${javaField}">
<Input
v-model:value="queryParams.${javaField}"
placeholder="请输入${comment}"
allowClear
@pressEnter="handleQuery"
class="w-full"
/>
</Form.Item>
#elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox")
<Form.Item label="${comment}" name="${javaField}">
<Select
v-model:value="queryParams.${javaField}"
placeholder="请选择${comment}"
allowClear
class="w-full"
>
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
<Select.Option
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Select.Option>
#else## 未设置 dictType 数据字典的情况
<Select.Option label="请选择字典生成" value="" />
#end
</Select>
</Form.Item>
#elseif($column.htmlType == "datetime")
#if ($column.listOperationCondition != "BETWEEN")## 非范围
<Form.Item label="${comment}" name="${javaField}">
<DatePicker
v-model:value="queryParams.${javaField}"
valueFormat="YYYY-MM-DD"
placeholder="选择${comment}"
allowClear
class="w-full"
/>
</Form.Item>
#else## 范围
<Form.Item label="${comment}" name="${javaField}">
<RangePicker
v-model:value="queryParams.${javaField}"
v-bind="getRangePickerDefaultProps()"
class="w-full"
/>
</Form.Item>
#end
#end
#end
#end
<Form.Item>
<Button class="ml-2" @click="resetQuery"> 重置 </Button>
<Button class="ml-2" @click="handleQuery" type="primary">
搜索
</Button>
</Form.Item>
</Form>
</Card>
<!-- 列表 -->
<Card title="${table.classComment}">
<template #extra>
<VbenVxeTableToolbar
ref="tableToolbarRef"
v-model:hidden-search="hiddenSearchBar"
>
#if (${table.templateType} == 2)
<Button @click="handleExpand" class="mr-2">
{{ isExpanded ? '收缩' : '展开' }}
</Button>
#end
<Button
class="ml-2"
:icon="h(Plus)"
type="primary"
@click="handleCreate"
v-access:code="['${permissionPrefix}:create']"
>
{{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
</Button>
<Button
:icon="h(Download)"
type="primary"
class="ml-2"
:loading="exportLoading"
@click="handleExport"
v-access:code="['${permissionPrefix}:export']"
>
{{ $t('ui.actionTitle.export') }}
</Button>
#if ($table.templateType != 2 && $deleteBatchEnable)
<Button
:icon="h(Trash2)"
type="primary"
danger
class="ml-2"
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']"
>
批量删除
</Button>
#end
</VbenVxeTableToolbar>
</template>
<VxeTable
ref="tableRef"
:data="list"
#if ( $table.templateType == 2 )
:tree-config="{
parentField: '${treeParentColumn.javaField}',
rowField: 'id',
transform: true,
expandAll: true,
reserve: true,
}"
#end
#if ($table.templateType == 11) ## erp情况
@cell-click="onCellClick"
:row-config="{
keyField: 'id',
isHover: true,
isCurrent: true,
}"
#end
show-overflow
:loading="loading"
#if ($table.templateType != 2 && $deleteBatchEnable)
@checkboxAll="handleRowCheckboxChange"
@checkboxChange="handleRowCheckboxChange"
#end
>
#if ($table.templateType != 2 && $deleteBatchEnable)
<VxeColumn type="checkbox" width="40" />
#end
## 特殊:主子表专属逻辑
#if ( $table.templateType == 12 && $subTables && $subTables.size() > 0 )
<!-- 子表的列表 -->
<VxeColumn type="expand" width="60">
<template #content="{ row }">
<!-- 子表的表单 -->
<Tabs v-model:active-key="subTabsName" class="mx-8">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<Tabs.TabPane key="$subClassNameVar" tab="${subTable.classComment}" force-render>
<${subSimpleClassName}List :${subJoinColumn_strikeCase}="row?.id" />
</Tabs.TabPane>
#end
</Tabs>
</template>
</VxeColumn>
#end
#foreach($column in $columns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set ($comment=$column.columnComment)
#if ($column.javaType == "LocalDateTime")## 时间类型
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{row}">
{{formatDateTime(row.${javaField})}}
</template>
</VxeColumn>
#elseif($column.dictType && "" != $column.dictType)## 数据字典
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{row}">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
</template>
</VxeColumn>
#elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
<VxeColumn field="${javaField}" title="${comment}" align="center" tree-node/>
#else
<VxeColumn field="${javaField}" title="${comment}" align="center" />
#end
#end
#end
<VxeColumn field="operation" title="操作" align="center">
<template #default="{row}">
#if ( $table.templateType == 2 )
<Button
size="small"
type="link"
@click="handleAppend(row)"
v-access:code="['${permissionPrefix}:create']"
>
新增下级
</Button>
#end
<Button
size="small"
type="link"
@click="handleEdit(row)"
v-access:code="['${permissionPrefix}:update']"
>
{{ $t('ui.actionTitle.edit') }}
</Button>
<Button
size="small"
type="link"
danger
class="ml-2"
#if ( $table.templateType == 2 )
:disabled="!isEmpty(row?.children)"
#end
@click="handleDelete(row)"
v-access:code="['${permissionPrefix}:delete']"
>
{{ $t('ui.actionTitle.delete') }}
</Button>
</template>
</VxeColumn>
</VxeTable>
#if ( $table.templateType != 2 )
<!-- 分页 -->
<div class="mt-2 flex justify-end">
<Pagination
:total="total"
v-model:current="queryParams.pageNo"
v-model:page-size="queryParams.pageSize"
show-size-changer
@change="getList"
/>
</div>
#end
</Card>
#if ($table.templateType == 11) ## erp情况
<Card>
<!-- 子表的表单 -->
<Tabs v-model:active-key="subTabsName">
#foreach ($subTable in $subTables)
#set ($index = $foreach.count - 1)
#set ($subClassNameVar = $subClassNameVars.get($index))
#set ($subSimpleClassName = $subSimpleClassNames.get($index))
#set ($subJoinColumn_strikeCase = $subJoinColumn_strikeCases.get($index))
<Tabs.TabPane key="$subClassNameVar" tab="${subTable.classComment}" force-render>
<${subSimpleClassName}List :${subJoinColumn_strikeCase}="select${simpleClassName}?.id" />
</Tabs.TabPane>
#end
</Tabs>
</Card>
#end
</Page>
</template>

View File

@@ -1,213 +0,0 @@
#set ($subTable = $subTables.get($subIndex))##当前表
#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
<script lang="ts" setup>
import type { Rule } from 'ant-design-vue/es/form';
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
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, TreeSelect } from 'ant-design-vue';
import { $t } from '#/locales';
import { get${subSimpleClassName}, create${subSimpleClassName}, update${subSimpleClassName} } from '#/api/${table.moduleName}/${table.businessName}';
const emit = defineEmits(['success']);
const getTitle = computed(() => {
return formData.value?.id
? $t('ui.actionTitle.edit', ['${subTable.classComment}'])
: $t('ui.actionTitle.create', ['${subTable.classComment}']);
});
const formRef = ref();
const formData = ref<Partial<${simpleClassName}Api.${subSimpleClassName}>>({
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
});
const rules: Record<string, Rule[]> = {
#foreach ($column in $subColumns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
#end
#end
};
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
await formRef.value?.validate();
modalApi.lock();
// 提交表单
const data = formData.value as ${simpleClassName}Api.${subSimpleClassName};
try {
await (formData.value?.id ? update${subSimpleClassName}(data) : create${subSimpleClassName}(data));
// 关闭并提示
await modalApi.close();
emit('success');
message.success({
content: $t('ui.actionMessage.operationSuccess'),
});
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
resetForm()
return;
}
// 加载数据
let data = modalApi.getData<${simpleClassName}Api.${subSimpleClassName}>();
if (!data) {
return;
}
if (data.id) {
modalApi.lock();
try {
data = await get${subSimpleClassName}(data.id);
} finally {
modalApi.unlock();
}
}
// 设置到 values
formData.value = data;
},
});
/** 重置表单 */
function resetForm(){
formData.value = {
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
};
formRef.value?.resetFields();
}
</script>
<template>
<Modal :title="getTitle">
<Form
ref="formRef"
:model="formData"
:rules="rules"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 18 }"
>
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#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.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<Form.Item label="${comment}" name="${javaField}">
<Input v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
</Form.Item>
#elseif($column.htmlType == "imageUpload")## 图片上传
<Form.Item label="${comment}" name="${javaField}">
<ImageUpload v-model:value="formData.${javaField}" />
</Form.Item>
#elseif($column.htmlType == "fileUpload")## 文件上传
<Form.Item label="${comment}" name="${javaField}">
<FileUpload v-model:value="formData.${javaField}" />
</Form.Item>
#elseif($column.htmlType == "editor")## 文本编辑器
<Form.Item label="${comment}" name="${javaField}">
<RichTextarea v-model="formData.${javaField}" height="500px" />
</Form.Item>
#elseif($column.htmlType == "select")## 下拉框
<Form.Item label="${comment}" name="${javaField}">
<Select v-model:value="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<Select.Option
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Select.Option>
#else##没数据字典
<Select.Option label="请选择字典生成" value="" />
#end
</Select>
</Form.Item>
#elseif($column.htmlType == "checkbox")## 多选框
<Form.Item label="${comment}" name="${javaField}">
<CheckboxGroup v-model:value="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<Checkbox
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Checkbox>
#else##没数据字典
<Checkbox label="请选择字典生成" />
#end
</CheckboxGroup>
</Form.Item>
#elseif($column.htmlType == "radio")## 单选框
<Form.Item label="${comment}" name="${javaField}">
<RadioGroup v-model:value="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<Radio
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Radio>
#else##没数据字典
<Radio value="1">请选择字典生成</Radio>
#end
</RadioGroup>
</Form.Item>
#elseif($column.htmlType == "datetime")## 时间框
<Form.Item label="${comment}" name="${javaField}">
<DatePicker
v-model:value="formData.${javaField}"
valueFormat="x"
placeholder="选择${comment}"
/>
</Form.Item>
#elseif($column.htmlType == "textarea")## 文本框
<Form.Item label="${comment}" name="${javaField}">
<Textarea v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
</Form.Item>
#end
#end
#end
</Form>
</Modal>
</template>

View File

@@ -1,2 +0,0 @@
## 主表的 normal 和 inner 使用相同的 form 表单
#parse("codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm")

View File

@@ -1,347 +0,0 @@
#set ($subTable = $subTables.get($subIndex))##当前表
#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组
#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段
#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex))
#set ($subClassNameVar = $subClassNameVars.get($subIndex))
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
<script lang="ts" setup>
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
import { computed, ref, h, onMounted, watch, nextTick } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { message, Tabs, Form, Input, Textarea, Button, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, DatePicker } from 'ant-design-vue';
import { $t } from '#/locales';
#if ($subTable.subJoinMany) ## 一对多
import type { VxeTableInstance } from '#/adapter/vxe-table';
import { Plus } from "@vben/icons";
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
import { ImageUpload, FileUpload } from "#/components/upload";
import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
#else
import type { Rule } from 'ant-design-vue/es/form';
import { Tinymce as RichTextarea } from '#/components/tinymce';
import { ImageUpload, FileUpload } from "#/components/upload";
import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
#end
const props = defineProps<{
${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
}>()
#if ($subTable.subJoinMany) ## 一对多
const list = ref<${simpleClassName}Api.${subSimpleClassName}[]>([]) // 列表的数据
const tableRef = ref<VxeTableInstance>();
/** 添加${subTable.classComment} */
async function handleAdd() {
await tableRef.value?.insertAt({} as ${simpleClassName}Api.${subSimpleClassName}, -1);
}
/** 删除${subTable.classComment} */
async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
await tableRef.value?.remove(row);
}
/** 提供获取表格数据的方法供父组件调用 */
defineExpose({
getData: (): ${simpleClassName}Api.${subSimpleClassName}[] => {
const data = list.value as ${simpleClassName}Api.${subSimpleClassName}[];
const removeRecords = tableRef.value?.getRemoveRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
const insertRecords = tableRef.value?.getInsertRecords() as ${simpleClassName}Api.${subSimpleClassName}[];
return [
...data.filter(
(row) => !removeRecords.some((removed) => removed.id === row.id),
),
...insertRecords.map((row: any) => ({ ...row, id: undefined })),
];
},
});
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.${subJoinColumn.javaField},
async (val) => {
if (!val) {
return;
}
list.value = await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!);
},
{ immediate: true },
);
#else
const formRef = ref();
const formData = ref<Partial<${simpleClassName}Api.${subSimpleClassName}>>({
#foreach ($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#if ($column.htmlType == "checkbox")
$column.javaField: [],
#else
$column.javaField: undefined,
#end
#end
#end
});
const rules: Record<string, Rule[]> = {
#foreach ($column in $subColumns)
#if (($column.createOperation || $column.updateOperation) && !$column.nullable && !${column.primaryKey})## 创建或者更新操作 && 要求非空 && 非主键
#set($comment=$column.columnComment)
$column.javaField: [{ required: true, message: '${comment}不能为空', trigger: #if($column.htmlType == 'select')'change'#else'blur'#end }],
#end
#end
};
/** 暴露出表单校验方法和表单值获取方法 */
defineExpose({
validate: async () => await formRef.value?.validate(),
getValues: ()=> formData.value,
});
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.${subJoinColumn.javaField},
async (val) => {
if (!val) {
return;
}
await nextTick();
formData.value = await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!);
},
{ immediate: true },
);
#end
</script>
<template>
#if ($subTable.subJoinMany) ## 一对多
<VxeTable ref="tableRef" :data="list" show-overflow class="mx-4">
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($comment = $column.columnComment)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#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 字段,不用填写
#elseif ($column.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<Input v-model:value="row.${javaField}" />
</template>
</VxeColumn>
#elseif($column.htmlType == "imageUpload")## 图片上传
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<ImageUpload v-model:value="row.${javaField}" />
</template>
</VxeColumn>
#elseif($column.htmlType == "fileUpload")## 文件上传
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<FileUpload v-model:value="row.${javaField}" />
</template>
</VxeColumn>
#elseif($column.htmlType == "select")## 下拉框
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<Select v-model:value="row.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<Select.Option
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Select.Option>
#else##没数据字典
<Select.Option label="请选择字典生成" value="" />
#end
</Select>
</template>
</VxeColumn>
#elseif($column.htmlType == "checkbox")## 多选框
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<CheckboxGroup v-model:value="row.${javaField}">
#if ("" != $dictType)## 有数据字典
<Checkbox
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Checkbox>
#else##没数据字典
<Checkbox label="请选择字典生成" />
#end
</CheckboxGroup>
</template>
</VxeColumn>
#elseif($column.htmlType == "radio")## 单选框
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<RadioGroup v-model:value="row.${javaField}">
#if ("" != $dictType)## 有数据字典
<Radio
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Radio>
#else##没数据字典
<Radio value="1">请选择字典生成</Radio>
#end
</RadioGroup>
</template>
</VxeColumn>
#elseif($column.htmlType == "datetime")## 时间框
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<DatePicker
v-model:value="row.${javaField}"
:showTime="true"
format="YYYY-MM-DD HH:mm:ss"
valueFormat='x'
/>
</template>
</VxeColumn>
#elseif($column.htmlType == "textarea" || $column.htmlType == "editor")## 文本框
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{ row }">
<Textarea v-model:value="row.${javaField}" />
</template>
</VxeColumn>
#end
#end
#end
<VxeColumn field="operation" title="操作" align="center">
<template #default="{ row }">
<Button
size="small"
type="link"
danger
@click="handleDelete(row)"
v-access:code="['${permissionPrefix}:delete']"
>
{{ $t('ui.actionTitle.delete') }}
</Button>
</template>
</VxeColumn>
</VxeTable>
<div class="flex justify-center mt-4">
<Button :icon="h(Plus)" type="primary" ghost @click="handleAdd" v-access:code="['${permissionPrefix}:create']">
{{ $t('ui.actionTitle.create', ['${subTable.classComment}']) }}
</Button>
</div>
#else
<Form
ref="formRef"
class="mx-4"
:model="formData"
:rules="rules"
:label-col="{ span: 5 }"
:wrapper-col="{ span: 18 }"
>
#foreach($column in $subColumns)
#if ($column.createOperation || $column.updateOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#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.htmlType == "input" && !$column.primaryKey)## 忽略主键,不用在表单里
<Form.Item label="${comment}" name="${javaField}">
<Input v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
</Form.Item>
#elseif($column.htmlType == "imageUpload")## 图片上传
<Form.Item label="${comment}" name="${javaField}">
<ImageUpload v-model:value="formData.${javaField}" />
</Form.Item>
#elseif($column.htmlType == "fileUpload")## 文件上传
<Form.Item label="${comment}" name="${javaField}">
<FileUpload v-model:value="formData.${javaField}" />
</Form.Item>
#elseif($column.htmlType == "editor")## 文本编辑器
<Form.Item label="${comment}" name="${javaField}">
<RichTextarea v-model="formData.${javaField}" height="500px" />
</Form.Item>
#elseif($column.htmlType == "select")## 下拉框
<Form.Item label="${comment}" name="${javaField}">
<Select v-model:value="formData.${javaField}" placeholder="请选择${comment}">
#if ("" != $dictType)## 有数据字典
<Select.Option
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Select.Option>
#else##没数据字典
<Select.Option label="请选择字典生成" value="" />
#end
</Select>
</Form.Item>
#elseif($column.htmlType == "checkbox")## 多选框
<Form.Item label="${comment}" name="${javaField}">
<CheckboxGroup v-model:value="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<Checkbox
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Checkbox>
#else##没数据字典
<Checkbox label="请选择字典生成" />
#end
</CheckboxGroup>
</Form.Item>
#elseif($column.htmlType == "radio")## 单选框
<Form.Item label="${comment}" name="${javaField}">
<RadioGroup v-model:value="formData.${javaField}">
#if ("" != $dictType)## 有数据字典
<Radio
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Radio>
#else##没数据字典
<Radio value="1">请选择字典生成</Radio>
#end
</RadioGroup>
</Form.Item>
#elseif($column.htmlType == "datetime")## 时间框
<Form.Item label="${comment}" name="${javaField}">
<DatePicker
v-model:value="formData.${javaField}"
valueFormat="x"
placeholder="选择${comment}"
/>
</Form.Item>
#elseif($column.htmlType == "textarea")## 文本框
<Form.Item label="${comment}" name="${javaField}">
<Textarea v-model:value="formData.${javaField}" placeholder="请输入${comment}" />
</Form.Item>
#end
#end
#end
</Form>
#end
</template>

View File

@@ -1,424 +0,0 @@
#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 ($subSimpleClassName_strikeCase = $subSimpleClassName_strikeCases.get($subIndex))
#set ($SubJoinColumnName = $subJoinColumn.javaField.substring(0,1).toUpperCase() + ${subJoinColumn.javaField.substring(1)})##首字母大写
<script lang="ts" setup>
import type { ${simpleClassName}Api } from '#/api/${table.moduleName}/${table.businessName}';
import type { VxeTableInstance } from '#/adapter/vxe-table';
import { reactive, ref, h, nextTick, watch, onMounted } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { DictTag } from '#/components/dict-tag';
import { getRangePickerDefaultProps } from '#/utils/rangePickerProps';
import { VxeColumn, VxeTable } from '#/adapter/vxe-table';
import { formatDateTime } from '@vben/utils';
#if ($table.templateType == 11) ## erp
import { useVbenModal } from '@vben/common-ui';
import { useTableToolbar, VbenVxeTableToolbar } from '@vben/plugins/vxe-table';
import ${subSimpleClassName}Form from './${subSimpleClassName_strikeCase}-form.vue'
import { Tinymce as RichTextarea } from '#/components/tinymce';
import { ImageUpload, FileUpload } from "#/components/upload";
import { message, Button, Card, Tabs, Pagination, Form, Input, Textarea, Select, RadioGroup, Radio, CheckboxGroup, Checkbox, RangePicker, DatePicker, TreeSelect } from 'ant-design-vue';
import { Plus, Trash2 } from '@vben/icons';
import { $t } from '#/locales';
#end
#if ($table.templateType == 11) ## erp
import { delete${subSimpleClassName},#if ($deleteBatchEnable) delete${subSimpleClassName}List,#end get${subSimpleClassName}Page } from '#/api/${table.moduleName}/${table.businessName}';
import { isEmpty, cloneDeep } from '@vben/utils';
#else
#if ($subTable.subJoinMany) ## 一对多
import { get${subSimpleClassName}ListBy${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
#else
import { get${subSimpleClassName}By${SubJoinColumnName} } from '#/api/${table.moduleName}/${table.businessName}';
#end
#end
const props = defineProps<{
${subJoinColumn.javaField}?: number // ${subJoinColumn.columnComment}(主表的关联字段)
}>()
#if ($table.templateType == 11) ## erp
const [FormModal, formModalApi] = useVbenModal({
connectedComponent: ${subSimpleClassName}Form,
destroyOnClose: true,
});
/** 创建${subTable.classComment} */
function handleCreate() {
if (!props.${subJoinColumn.javaField}){
message.warning("请先选择一个${table.classComment}!")
return
}
formModalApi.setData({${subJoinColumn.javaField}: props.${subJoinColumn.javaField}}).open();
}
/** 编辑${subTable.classComment} */
function handleEdit(row: ${simpleClassName}Api.${subSimpleClassName}) {
formModalApi.setData(row).open();
}
/** 删除${subTable.classComment} */
async function handleDelete(row: ${simpleClassName}Api.${subSimpleClassName}) {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.id]),
duration: 0,
});
try {
await delete${subSimpleClassName}(row.id!);
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
await getList();
} finally {
hideLoading();
}
}
#if ($deleteBatchEnable)
/** 批量删除${subTable.classComment} */
async function handleDeleteBatch() {
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting'),
duration: 0,
});
try {
await delete${subSimpleClassName}List(checkedIds.value);
checkedIds.value = [];
message.success($t('ui.actionMessage.deleteSuccess'));
await getList();
} finally {
hideLoading();
}
}
const checkedIds = ref<number[]>([])
function handleRowCheckboxChange({
records,
}: {
records: ${simpleClassName}Api.${subSimpleClassName}[];
}) {
checkedIds.value = records.map((item) => item.id!);
}
#end
#end
const loading = ref(true) // 列表的加载中
const list = ref<${simpleClassName}Api.${subSimpleClassName}[]>([]) // 列表的数据
#if ($table.templateType == 11) ## erp
const total = ref(0) // 列表的总页数
#end
#if ($table.templateType == 11) ## erp
const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
#foreach ($column in $subColumns)
#if ($column.listOperation)
#if ($column.listOperationCondition != 'BETWEEN')
$column.javaField: undefined,
#end
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
$column.javaField: undefined,
#end
#end
#end
})
/** 搜索按钮操作 */
function handleQuery() {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
function resetQuery() {
queryFormRef.value.resetFields()
handleQuery()
}
#end
/** 查询列表 */
async function getList() {
loading.value = true
try {
if (!props.${subJoinColumn.javaField}){
return []
}
## 特殊:树表专属逻辑(树不需要分页接口)
#if ($table.templateType == 11) ## erp
const params = cloneDeep(queryParams) as any;
#foreach ($column in $columns)
#if ($column.listOperation)
#if ($column.htmlType == "datetime" || $column.listOperationCondition == "BETWEEN")
if (params.${column.javaField} && Array.isArray(params.${column.javaField})) {
params.${column.javaField} = (params.${column.javaField} as string[]).join(',');
}
#end
#end
#end
params.${subJoinColumn.javaField} = props.${subJoinColumn.javaField};
const data = await get${subSimpleClassName}Page(params)
list.value = data.list
total.value = data.total
#else
#if ($subTable.subJoinMany) ## 一对多
list.value = await get${subSimpleClassName}ListBy${SubJoinColumnName}(props.${subJoinColumn.javaField}!);
#else
list.value = [await get${subSimpleClassName}By${SubJoinColumnName}(props.${subJoinColumn.javaField}!)];
#end
#end
} finally {
loading.value = false
}
}
/** 监听主表的关联字段的变化,加载对应的子表数据 */
watch(
() => props.${subJoinColumn.javaField},
async (val) => {
if (!val) {
return;
}
await nextTick();
await getList()
},
{ immediate: true },
);
#if ($table.templateType == 11) ## erp
/** 初始化 */
const { hiddenSearchBar, tableToolbarRef, tableRef } = useTableToolbar();
onMounted(() => {
getList();
});
#end
</script>
<template>
#if ($table.templateType == 11) ## erp
<FormModal @success="getList" />
<div class="h-[600px]">
<Card v-if="!hiddenSearchBar" class="mb-4">
<!-- 搜索工作栏 -->
<Form
:model="queryParams"
ref="queryFormRef"
layout="inline"
>
#foreach($column in $subColumns)
#if ($column.listOperation)
#set ($dictType = $column.dictType)
#set ($javaField = $column.javaField)
#set ($javaType = $column.javaType)
#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.htmlType == "input")
<Form.Item label="${comment}" name="${javaField}">
<Input
v-model:value="queryParams.${javaField}"
placeholder="请输入${comment}"
allowClear
@pressEnter="handleQuery"
class="w-full"
/>
</Form.Item>
#elseif ($column.htmlType == "select" || $column.htmlType == "radio" || $column.htmlType == "checkbox")
<Form.Item label="${comment}" name="${javaField}">
<Select
v-model:value="queryParams.${javaField}"
placeholder="请选择${comment}"
allowClear
class="w-full"
>
#if ("" != $dictType)## 设置了 dictType 数据字典的情况
<Select.Option
v-for="dict in getDictOptions(DICT_TYPE.$dictType.toUpperCase(), '$dictMethod')"
:key="dict.value"
:value="dict.value"
>
{{ dict.label }}
</Select.Option>
#else## 未设置 dictType 数据字典的情况
<Select.Option label="请选择字典生成" value="" />
#end
</Select>
</Form.Item>
#elseif($column.htmlType == "datetime")
#if ($column.listOperationCondition != "BETWEEN")## 非范围
<Form.Item label="${comment}" name="${javaField}">
<DatePicker
v-model:value="queryParams.${javaField}"
valueFormat="YYYY-MM-DD"
placeholder="选择${comment}"
allowClear
class="w-full"
/>
</Form.Item>
#else## 范围
<Form.Item label="${comment}" name="${javaField}">
<RangePicker
v-model:value="queryParams.${javaField}"
v-bind="getRangePickerDefaultProps()"
class="w-full"
/>
</Form.Item>
#end
#end
#end
#end
<Form.Item>
<Button class="ml-2" @click="resetQuery"> 重置 </Button>
<Button class="ml-2" @click="handleQuery" type="primary">
搜索
</Button>
</Form.Item>
</Form>
</Card>
<!-- 列表 -->
<Card title="${table.classComment}">
<template #extra>
<VbenVxeTableToolbar
ref="tableToolbarRef"
v-model:hidden-search="hiddenSearchBar"
>
<Button
class="ml-2"
:icon="h(Plus)"
type="primary"
@click="handleCreate"
v-access:code="['${permissionPrefix}:create']"
>
{{ $t('ui.actionTitle.create', ['${table.classComment}']) }}
</Button>
#if ($deleteBatchEnable)
<Button
:icon="h(Trash2)"
type="primary"
danger
class="ml-2"
:disabled="isEmpty(checkedIds)"
@click="handleDeleteBatch"
v-access:code="['${table.moduleName}:${simpleClassName_strikeCase}:delete']"
>
批量删除
</Button>
#end
</VbenVxeTableToolbar>
</template>
<VxeTable
ref="tableRef"
:data="list"
show-overflow
:loading="loading"
#if ($deleteBatchEnable)
@checkboxAll="handleRowCheckboxChange"
@checkboxChange="handleRowCheckboxChange"
#end
>
#if ($deleteBatchEnable)
<VxeColumn type="checkbox" width="40" />
#end
#foreach($column in $subColumns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($comment=$column.columnComment)
#if ($column.javaType == "LocalDateTime")## 时间类型
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{row}">
{{formatDateTime(row.${javaField})}}
</template>
</VxeColumn>
#elseif($column.dictType && "" != $column.dictType)## 数据字典
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{row}">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
</template>
</VxeColumn>
#elseif ($table.templateType == 2 && $javaField == $treeNameColumn.javaField)
<VxeColumn field="${javaField}" title="${comment}" align="center" tree-node/>
#else
<VxeColumn field="${javaField}" title="${comment}" align="center" />
#end
#end
#end
<VxeColumn field="operation" title="操作" align="center">
<template #default="{row}">
<Button
size="small"
type="link"
@click="handleEdit(row)"
v-access:code="['${permissionPrefix}:update']"
>
{{ $t('ui.actionTitle.edit') }}
</Button>
<Button
size="small"
type="link"
danger
class="ml-2"
@click="handleDelete(row)"
v-access:code="['${permissionPrefix}:delete']"
>
{{ $t('ui.actionTitle.delete') }}
</Button>
</template>
</VxeColumn>
</VxeTable>
<!-- 分页 -->
<div class="mt-2 flex justify-end">
<Pagination
:total="total"
v-model:current="queryParams.pageNo"
v-model:page-size="queryParams.pageSize"
show-size-changer
@change="getList"
/>
</div>
</Card>
</div>
#else
<Card title="${subTable.classComment}列表">
<VxeTable
:data="list"
show-overflow
:loading="loading"
>
#foreach($column in $subColumns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
#set ($javaField = $column.javaField)
#set ($comment=$column.columnComment)
#if ($column.javaType == "LocalDateTime")## 时间类型
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{row}">
{{formatDateTime(row.${javaField})}}
</template>
</VxeColumn>
#elseif($column.dictType && "" != $column.dictType)## 数据字典
<VxeColumn field="${javaField}" title="${comment}" align="center">
<template #default="{row}">
<dict-tag :type="DICT_TYPE.$dictType.toUpperCase()" :value="row.${javaField}" />
</template>
</VxeColumn>
#else
<VxeColumn field="${javaField}" title="${comment}" align="center" />
#end
#end
#end
</VxeTable>
</Card>
#end
</template>

View File

@@ -1,4 +0,0 @@
## 子表的 erp 和 inner 使用相似的 list 列表,差异主要两点:
## 1inner 使用 list 不分页erp 使用 page 分页
## 2erp 支持单个子表的新增、修改、删除inner 不支持
#parse("codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm")

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