refactor: 重构为多模块
This commit is contained in:
41
youlai-codegen/pom.xml
Normal file
41
youlai-codegen/pom.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>youlai-boot</artifactId>
|
||||
<version>4.3.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>youlai-codegen</artifactId>
|
||||
<description>代码生成器模块 - 可视化配置的前后端代码生成</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>youlai-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>youlai-framework</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Velocity 模板引擎 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId>
|
||||
<version>${velocity.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis-Plus 代码生成器 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId>
|
||||
<version>${mybatis-plus-generator.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.youlai.boot.codegen.config;
|
||||
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 代码生成配置属性
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.11.0
|
||||
*/
|
||||
@Component
|
||||
@EnableConfigurationProperties(CodegenProperties.class)
|
||||
@ConfigurationProperties(prefix = "codegen")
|
||||
@Data
|
||||
public class CodegenProperties {
|
||||
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
private DefaultConfig defaultConfig ;
|
||||
|
||||
/**
|
||||
* 模板配置
|
||||
*/
|
||||
private Map<String, TemplateConfig> templateConfigs = MapUtil.newHashMap(true);
|
||||
|
||||
/**
|
||||
* 后端应用名
|
||||
*/
|
||||
private String backendAppName;
|
||||
|
||||
/**
|
||||
* 前端应用名
|
||||
*/
|
||||
private String frontendAppName;
|
||||
|
||||
/**
|
||||
* 下载文件名
|
||||
*/
|
||||
private String downloadFileName;
|
||||
|
||||
/**
|
||||
* 排除数据表
|
||||
*/
|
||||
private List<String> excludeTables;
|
||||
|
||||
/**
|
||||
* 模板配置
|
||||
*/
|
||||
@Data
|
||||
public static class TemplateConfig {
|
||||
|
||||
/**
|
||||
* 模板路径 (e.g. /templates/codegen/controller.java.vm)
|
||||
*/
|
||||
private String templatePath;
|
||||
|
||||
/**
|
||||
* 子包名 (e.g. controller/service/mapper/model)
|
||||
*/
|
||||
private String subpackageName;
|
||||
|
||||
/**
|
||||
* 文件扩展名,如 .java
|
||||
*/
|
||||
private String extension = FileNameUtil.EXT_JAVA;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*/
|
||||
@Data
|
||||
public static class DefaultConfig {
|
||||
|
||||
/**
|
||||
* 作者 (e.g. Ray)
|
||||
*/
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* 默认模块名(e.g. system)
|
||||
*/
|
||||
private String moduleName;
|
||||
|
||||
/**
|
||||
* 基础包名 (e.g. com.youlai.boot)
|
||||
*/
|
||||
private String packageName = "com.youlai.boot";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.youlai.boot.codegen.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.common.result.PageResult;
|
||||
import com.youlai.boot.common.result.Result;
|
||||
import com.youlai.boot.codegen.config.CodegenProperties;
|
||||
import com.youlai.boot.common.enums.ActionTypeEnum;
|
||||
import com.youlai.boot.common.enums.LogModuleEnum;
|
||||
import com.youlai.boot.codegen.service.CodegenService;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.CodegenPreviewVO;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
import com.youlai.boot.common.annotation.Log;
|
||||
import com.youlai.boot.codegen.service.GenTableService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成器控制层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Tag(name = "11.代码生成")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/codegen")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class CodegenController {
|
||||
|
||||
private final CodegenService codegenService;
|
||||
private final GenTableService genTableService;
|
||||
private final CodegenProperties codegenProperties;
|
||||
|
||||
@Operation(summary = "获取数据表分页列表")
|
||||
@GetMapping("/table")
|
||||
public PageResult<TablePageVO> getTablePage(
|
||||
TableQuery queryParams
|
||||
) {
|
||||
Page<TablePageVO> result = codegenService.getTablePage(queryParams);
|
||||
return PageResult.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取代码生成配置")
|
||||
@GetMapping("/{tableName}/config")
|
||||
public Result<GenConfigForm> getGenTableFormData(
|
||||
@Parameter(description = "表名", example = "sys_user") @PathVariable String tableName
|
||||
) {
|
||||
GenConfigForm formData = genTableService.getGenTableFormData(tableName);
|
||||
return Result.success(formData);
|
||||
}
|
||||
|
||||
@Operation(summary = "保存代码生成配置")
|
||||
@PostMapping("/{tableName}/config")
|
||||
@Log(module = LogModuleEnum.CODEGEN, value = ActionTypeEnum.UPDATE)
|
||||
public Result<?> saveGenConfig(@RequestBody GenConfigForm formData) {
|
||||
genTableService.saveGenConfig(formData);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "删除代码生成配置")
|
||||
@DeleteMapping("/{tableName}/config")
|
||||
public Result<?> deleteGenConfig(
|
||||
@Parameter(description = "表名", example = "sys_user") @PathVariable String tableName
|
||||
) {
|
||||
genTableService.deleteGenConfig(tableName);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "获取预览生成代码")
|
||||
@GetMapping("/{tableName}/preview")
|
||||
public Result<List<CodegenPreviewVO>> getTablePreviewData(@PathVariable String tableName,
|
||||
@RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType,
|
||||
@RequestParam(value = "type", required = false, defaultValue = "ts") String type) {
|
||||
List<CodegenPreviewVO> list = codegenService.getCodegenPreviewData(tableName, pageType, type);
|
||||
return Result.success(list);
|
||||
}
|
||||
|
||||
@Operation(summary = "下载代码")
|
||||
@GetMapping("/{tableName}/download")
|
||||
@Log(module = LogModuleEnum.CODEGEN, value = ActionTypeEnum.DOWNLOAD)
|
||||
public void downloadZip(HttpServletResponse response, @PathVariable String tableName,
|
||||
@RequestParam(value = "pageType", required = false, defaultValue = "classic") String pageType,
|
||||
@RequestParam(value = "type", required = false, defaultValue = "ts") String type) {
|
||||
String[] tableNames = tableName.split(",");
|
||||
byte[] data = codegenService.downloadCode(tableNames, pageType, type);
|
||||
|
||||
response.reset();
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(codegenProperties.getDownloadFileName(), StandardCharsets.UTF_8));
|
||||
response.setContentType("application/octet-stream; charset=UTF-8");
|
||||
|
||||
try (ServletOutputStream outputStream = response.getOutputStream()) {
|
||||
outputStream.write(data);
|
||||
outputStream.flush();
|
||||
} catch (IOException e) {
|
||||
log.error("Error while writing the zip file1 to response", e);
|
||||
throw new RuntimeException("Failed to write the zip file1 to response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.youlai.boot.codegen.converter;
|
||||
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成配置转换器
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface CodegenConverter {
|
||||
|
||||
@Mapping(source = "genTable.tableName", target = "tableName")
|
||||
@Mapping(source = "genTable.businessName", target = "businessName")
|
||||
@Mapping(source = "genTable.moduleName", target = "moduleName")
|
||||
@Mapping(source = "genTable.packageName", target = "packageName")
|
||||
@Mapping(source = "genTable.entityName", target = "entityName")
|
||||
@Mapping(source = "genTable.author", target = "author")
|
||||
@Mapping(source = "genTable.pageType", target = "pageType")
|
||||
@Mapping(source = "genTable.removeTablePrefix", target = "removeTablePrefix")
|
||||
@Mapping(source = "fieldConfigs", target = "fieldConfigs")
|
||||
GenConfigForm toGenConfigForm(GenTable genTable, List<GenTableColumn> fieldConfigs);
|
||||
|
||||
List<GenConfigForm.FieldConfig> toGenTableColumnForm(List<GenTableColumn> fieldConfigs);
|
||||
|
||||
GenConfigForm.FieldConfig toGenTableColumnForm(GenTableColumn genTableColumn);
|
||||
|
||||
GenTable toGenTable(GenConfigForm formData);
|
||||
|
||||
List<GenTableColumn> toGenTableColumn(List<GenConfigForm.FieldConfig> fieldConfigs);
|
||||
|
||||
GenTableColumn toGenTableColumn(GenConfigForm.FieldConfig fieldConfig);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.youlai.boot.codegen.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.youlai.boot.common.base.IBaseEnum;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 表单类型枚举
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum FormTypeEnum implements IBaseEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 输入框
|
||||
*/
|
||||
INPUT(1, "输入框"),
|
||||
|
||||
/**
|
||||
* 下拉框
|
||||
*/
|
||||
SELECT(2, "下拉框"),
|
||||
|
||||
/**
|
||||
* 单选框
|
||||
*/
|
||||
RADIO(3, "单选框"),
|
||||
|
||||
/**
|
||||
* 复选框
|
||||
*/
|
||||
CHECK_BOX(4, "复选框"),
|
||||
|
||||
/**
|
||||
* 数字输入框
|
||||
*/
|
||||
INPUT_NUMBER(5, "数字输入框"),
|
||||
|
||||
/**
|
||||
* 开关
|
||||
*/
|
||||
SWITCH(6, "开关"),
|
||||
|
||||
/**
|
||||
* 文本域
|
||||
*/
|
||||
TEXT_AREA(7, "文本域"),
|
||||
|
||||
/**
|
||||
* 日期时间框
|
||||
*/
|
||||
DATE(8, "日期框"),
|
||||
|
||||
/**
|
||||
* 日期框
|
||||
*/
|
||||
DATE_TIME(9, "日期时间框"),
|
||||
|
||||
/**
|
||||
* 隐藏域
|
||||
*/
|
||||
HIDDEN(10, "隐藏域");
|
||||
|
||||
|
||||
// Mybatis-Plus 提供注解表示插入数据库时插入该值
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final Integer value;
|
||||
|
||||
// @JsonValue // 表示对枚举序列化时返回此字段
|
||||
private final String label;
|
||||
|
||||
|
||||
@JsonCreator
|
||||
public static FormTypeEnum fromValue(Integer value) {
|
||||
for (FormTypeEnum type : FormTypeEnum.values()) {
|
||||
if (type.getValue().equals(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No enum constant with value " + value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.youlai.boot.codegen.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表单类型枚举
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Getter
|
||||
public enum JavaTypeEnum {
|
||||
|
||||
VARCHAR("varchar", "String", "string"),
|
||||
CHAR("char", "String", "string"),
|
||||
BLOB("blob", "byte[]", "Uint8Array"),
|
||||
TEXT("text", "String", "string"),
|
||||
JSON("json", "String", "any"),
|
||||
INTEGER("int", "Integer", "number"),
|
||||
TINYINT("tinyint", "Integer", "number"),
|
||||
SMALLINT("smallint", "Integer", "number"),
|
||||
MEDIUMINT("mediumint", "Integer", "number"),
|
||||
BIGINT("bigint", "Long", "number"),
|
||||
FLOAT("float", "Float", "number"),
|
||||
DOUBLE("double", "Double", "number"),
|
||||
DECIMAL("decimal", "BigDecimal", "number"),
|
||||
DATE("date", "LocalDate", "string"),
|
||||
DATETIME("datetime", "LocalDateTime", "string"),
|
||||
TIMESTAMP("timestamp", "LocalDateTime", "string"),
|
||||
BOOLEAN("boolean", "Boolean", "boolean"),
|
||||
BIT("bit", "Boolean", "boolean");
|
||||
|
||||
// 数据库类型
|
||||
private final String dbType;
|
||||
// Java类型
|
||||
private final String javaType;
|
||||
// TypeScript类型
|
||||
private final String tsType;
|
||||
|
||||
// 数据库类型和Java类型的映射
|
||||
private static final Map<String, JavaTypeEnum> typeMap = new HashMap<>();
|
||||
|
||||
// 初始化映射关系
|
||||
static {
|
||||
for (JavaTypeEnum javaTypeEnum : JavaTypeEnum.values()) {
|
||||
typeMap.put(javaTypeEnum.getDbType(), javaTypeEnum);
|
||||
}
|
||||
}
|
||||
|
||||
JavaTypeEnum(String dbType, String javaType, String tsType) {
|
||||
this.dbType = dbType;
|
||||
this.javaType = javaType;
|
||||
this.tsType = tsType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数据库类型获取对应的Java类型
|
||||
*
|
||||
* @param columnType 列类型
|
||||
* @return 对应的Java类型
|
||||
*/
|
||||
public static String getJavaTypeByColumnType(String columnType) {
|
||||
String normalized = normalizeColumnType(columnType);
|
||||
JavaTypeEnum javaTypeEnum = typeMap.get(normalized);
|
||||
if (javaTypeEnum != null) {
|
||||
return javaTypeEnum.getJavaType();
|
||||
}
|
||||
return "String";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Java类型获取对应的TypeScript类型
|
||||
*
|
||||
* @param javaType Java类型
|
||||
* @return 对应的TypeScript类型
|
||||
*/
|
||||
public static String getTsTypeByJavaType(String javaType) {
|
||||
if (javaType == null) {
|
||||
return "any";
|
||||
}
|
||||
for (JavaTypeEnum javaTypeEnum : JavaTypeEnum.values()) {
|
||||
if (javaTypeEnum.getJavaType().equals(javaType)) {
|
||||
return javaTypeEnum.getTsType();
|
||||
}
|
||||
}
|
||||
return "any";
|
||||
}
|
||||
|
||||
private static String normalizeColumnType(String columnType) {
|
||||
if (columnType == null) {
|
||||
return "";
|
||||
}
|
||||
// Handle values like: varchar(255), bigint unsigned, INT
|
||||
String normalized = columnType.trim().toLowerCase();
|
||||
int parenIndex = normalized.indexOf('(');
|
||||
if (parenIndex > -1) {
|
||||
normalized = normalized.substring(0, parenIndex);
|
||||
}
|
||||
// Remove modifiers
|
||||
normalized = normalized.replace("unsigned", "").replace("zerofill", "").trim();
|
||||
// Collapse repeated spaces
|
||||
normalized = normalized.replaceAll("\\s+", " ");
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.youlai.boot.codegen.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.EnumValue;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.youlai.boot.common.base.IBaseEnum;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 查询类型枚举
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum QueryTypeEnum implements IBaseEnum<Integer> {
|
||||
|
||||
/** 等于 */
|
||||
EQ(1, "="),
|
||||
|
||||
/** 模糊匹配 */
|
||||
LIKE(2, "LIKE '%s%'"),
|
||||
|
||||
/** 包含 */
|
||||
IN(3, "IN"),
|
||||
|
||||
/** 范围 */
|
||||
BETWEEN(4, "BETWEEN"),
|
||||
|
||||
/** 大于 */
|
||||
GT(5, ">"),
|
||||
|
||||
/** 大于等于 */
|
||||
GE(6, ">="),
|
||||
|
||||
/** 小于 */
|
||||
LT(7, "<"),
|
||||
|
||||
/** 小于等于 */
|
||||
LE(8, "<="),
|
||||
|
||||
/** 不等于 */
|
||||
NE(9, "!="),
|
||||
|
||||
/** 左模糊匹配 */
|
||||
LIKE_LEFT(10, "LIKE '%s'"),
|
||||
|
||||
/** 右模糊匹配 */
|
||||
LIKE_RIGHT(11, "LIKE 's%'");
|
||||
|
||||
|
||||
// 存储在数据库中的枚举属性值
|
||||
@EnumValue
|
||||
@JsonValue
|
||||
private final Integer value;
|
||||
|
||||
// 序列化成 JSON 时的属性值
|
||||
private final String label;
|
||||
|
||||
|
||||
@JsonCreator
|
||||
public static QueryTypeEnum fromValue(Integer value) {
|
||||
for (QueryTypeEnum type : QueryTypeEnum.values()) {
|
||||
if (type.getValue().equals(value)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No enum constant with value " + value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.youlai.boot.codegen.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.codegen.model.vo.ColumnMetaVO;
|
||||
import com.youlai.boot.codegen.model.vo.TableMetaVO;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 数据库映射层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.9.0
|
||||
*/
|
||||
@Mapper
|
||||
public interface DatabaseMapper extends BaseMapper {
|
||||
|
||||
/**
|
||||
* 获取表分页列表
|
||||
*
|
||||
* @param page
|
||||
* @param queryParams
|
||||
* @return
|
||||
*/
|
||||
Page<TablePageVO> getTablePage(Page<TablePageVO> page, TableQuery queryParams);
|
||||
|
||||
/**
|
||||
* 获取表字段列表
|
||||
*
|
||||
* @param tableName
|
||||
* @return
|
||||
*/
|
||||
List<ColumnMetaVO> getTableColumns(String tableName);
|
||||
|
||||
TableMetaVO getTableMetadata(String tableName);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.youlai.boot.codegen.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 代码生成表字段配置访问层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Mapper
|
||||
public interface GenTableColumnMapper extends BaseMapper<GenTableColumn> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.youlai.boot.codegen.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 代码生成表配置访问层
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Mapper
|
||||
public interface GenTableMapper extends BaseMapper<GenTable> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.youlai.boot.codegen.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
|
||||
import com.youlai.boot.common.base.BaseEntity;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 代码生成表配置
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@TableName(value = "gen_table")
|
||||
@Getter
|
||||
@Setter
|
||||
public class GenTable extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 包名
|
||||
*/
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 模块名
|
||||
*/
|
||||
private String moduleName;
|
||||
|
||||
/**
|
||||
* 实体类名
|
||||
*/
|
||||
private String entityName;
|
||||
|
||||
/**
|
||||
* 业务名
|
||||
*/
|
||||
private String businessName;
|
||||
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
private Long parentMenuId;
|
||||
|
||||
/**
|
||||
* 作者
|
||||
*/
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* 页面类型 classic|curd
|
||||
*/
|
||||
private String pageType;
|
||||
|
||||
/**
|
||||
* 要移除的表前缀,如: sys_
|
||||
*/
|
||||
private String removeTablePrefix;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.youlai.boot.codegen.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.youlai.boot.common.base.BaseEntity;
|
||||
import com.youlai.boot.codegen.enums.FormTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.QueryTypeEnum;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 代码生成表字段配置实体
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@TableName(value = "gen_table_column")
|
||||
@Getter
|
||||
@Setter
|
||||
public class GenTableColumn extends BaseEntity {
|
||||
|
||||
|
||||
/**
|
||||
* 关联的表配置ID
|
||||
*/
|
||||
private Long tableId;
|
||||
|
||||
/**
|
||||
* 列名
|
||||
*/
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 列类型
|
||||
*/
|
||||
private String columnType;
|
||||
|
||||
/**
|
||||
* 字段长度
|
||||
*/
|
||||
private Long maxLength;
|
||||
|
||||
/**
|
||||
* 字段名称
|
||||
*/
|
||||
private String fieldName;
|
||||
|
||||
/**
|
||||
* 字段排序
|
||||
*/
|
||||
private Integer fieldSort;
|
||||
|
||||
/**
|
||||
* 字段类型
|
||||
*/
|
||||
private String fieldType;
|
||||
|
||||
/**
|
||||
* 字段描述
|
||||
*/
|
||||
private String fieldComment;
|
||||
|
||||
/**
|
||||
* 表单类型
|
||||
*/
|
||||
private FormTypeEnum formType;
|
||||
|
||||
/**
|
||||
* 查询方式
|
||||
*/
|
||||
private QueryTypeEnum queryType;
|
||||
|
||||
/**
|
||||
* 是否在列表显示
|
||||
*/
|
||||
private Integer isShowInList;
|
||||
|
||||
/**
|
||||
* 是否在表单显示
|
||||
*/
|
||||
private Integer isShowInForm;
|
||||
|
||||
/**
|
||||
* 是否在查询条件显示
|
||||
*/
|
||||
private Integer isShowInQuery;
|
||||
|
||||
/**
|
||||
* 是否必填
|
||||
*/
|
||||
private Integer isRequired;
|
||||
|
||||
/**
|
||||
* TypeScript类型
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
@JsonIgnore
|
||||
private String tsType;
|
||||
|
||||
/**
|
||||
* 字典类型
|
||||
*/
|
||||
private String dictType;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.youlai.boot.codegen.model.form;
|
||||
|
||||
import com.youlai.boot.codegen.enums.FormTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.QueryTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成配置表单
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Schema(description = "代码生成配置表单")
|
||||
@Data
|
||||
public class GenConfigForm {
|
||||
|
||||
@Schema(description = "主键",example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "表名",example = "sys_user")
|
||||
private String tableName;
|
||||
|
||||
@Schema(description = "业务名",example = "用户")
|
||||
private String businessName;
|
||||
|
||||
@Schema(description = "模块名",example = "system")
|
||||
private String moduleName;
|
||||
|
||||
@Schema(description = "包名",example = "com.youlai")
|
||||
private String packageName;
|
||||
|
||||
@Schema(description = "实体名",example = "User")
|
||||
private String entityName;
|
||||
|
||||
@Schema(description = "作者",example = "youlaitech")
|
||||
private String author;
|
||||
|
||||
@Schema(description = "上级菜单ID",example = "1")
|
||||
private Long parentMenuId;
|
||||
|
||||
@Schema(description = "字段配置列表")
|
||||
private List<FieldConfig> fieldConfigs;
|
||||
|
||||
@Schema(description = "后端应用名")
|
||||
private String backendAppName;
|
||||
|
||||
@Schema(description = "前端应用名")
|
||||
private String frontendAppName;
|
||||
|
||||
@Schema(description = "页面类型 classic|curd", example = "classic")
|
||||
private String pageType;
|
||||
|
||||
@Schema(description = "要移除的表前缀,如: sys_", example = "sys_")
|
||||
private String removeTablePrefix;
|
||||
|
||||
@Schema(description = "字段配置")
|
||||
@Data
|
||||
public static class FieldConfig {
|
||||
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "列名")
|
||||
private String columnName;
|
||||
|
||||
@Schema(description = "列类型")
|
||||
private String columnType;
|
||||
|
||||
@Schema(description = "字段名")
|
||||
private String fieldName;
|
||||
|
||||
@Schema(description = "字段排序")
|
||||
private Integer fieldSort;
|
||||
|
||||
@Schema(description = "字段类型")
|
||||
private String fieldType;
|
||||
|
||||
@Schema(description = "字段描述")
|
||||
private String fieldComment;
|
||||
|
||||
@Schema(description = "是否在列表显示")
|
||||
private Integer isShowInList;
|
||||
|
||||
@Schema(description = "是否在表单显示")
|
||||
private Integer isShowInForm;
|
||||
|
||||
@Schema(description = "是否在查询条件显示")
|
||||
private Integer isShowInQuery;
|
||||
|
||||
@Schema(description = "是否必填")
|
||||
private Integer isRequired;
|
||||
|
||||
@Schema(description = "最大长度")
|
||||
private Integer maxLength;
|
||||
|
||||
@Schema(description = "表单类型")
|
||||
private FormTypeEnum formType;
|
||||
|
||||
@Schema(description = "查询类型")
|
||||
private QueryTypeEnum queryType;
|
||||
|
||||
@Schema(description = "字典类型")
|
||||
private String dictType;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.youlai.boot.codegen.model.query;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.youlai.boot.common.base.BaseQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据表分页查询对象
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Schema(description = "数据表分页查询对象")
|
||||
@Getter
|
||||
@Setter
|
||||
public class TablePageQuery extends BaseQuery {
|
||||
|
||||
@Schema(description="关键字(表名)")
|
||||
private String keywords;
|
||||
|
||||
/**
|
||||
* 排除的表名
|
||||
*/
|
||||
@JsonIgnore
|
||||
private List<String> excludeTables;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.youlai.boot.codegen.model.query;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.youlai.boot.common.base.BaseQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据表查询对象
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Schema(description = "数据表查询对象")
|
||||
@Getter
|
||||
@Setter
|
||||
public class TableQuery extends BaseQuery {
|
||||
|
||||
@Schema(description="关键字(表名)")
|
||||
private String keywords;
|
||||
|
||||
/**
|
||||
* 排除的表名
|
||||
*/
|
||||
@JsonIgnore
|
||||
private List<String> excludeTables;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "代码生成代码预览Vo")
|
||||
@Data
|
||||
public class CodegenPreviewVO {
|
||||
|
||||
@Schema(description = "生成文件路径")
|
||||
private String path;
|
||||
|
||||
@Schema(description = "生成文件名称",example = "SysUser.java" )
|
||||
private String fileName;
|
||||
|
||||
@Schema(description = "生成文件内容")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "文件范围(frontend/backend)")
|
||||
private String scope;
|
||||
|
||||
@Schema(description = "文件语言(扩展名)")
|
||||
private String language;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "数据表字段元数据")
|
||||
@Data
|
||||
public class ColumnMetaVO {
|
||||
|
||||
private String columnName;
|
||||
|
||||
private String dataType;
|
||||
|
||||
private String columnComment;
|
||||
|
||||
private Long characterMaximumLength;
|
||||
|
||||
private Integer isPrimaryKey;
|
||||
|
||||
private String isNullable;
|
||||
|
||||
private String characterSetName;
|
||||
|
||||
private String collationName;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 数据表元数据
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Data
|
||||
public class TableMetaVO {
|
||||
|
||||
private String tableName;
|
||||
|
||||
private String tableComment;
|
||||
|
||||
private String tableCollation;
|
||||
|
||||
private String engine;
|
||||
|
||||
private String charset;
|
||||
|
||||
private String createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.youlai.boot.codegen.model.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Schema(description = "表视图对象")
|
||||
@Data
|
||||
public class TablePageVO {
|
||||
|
||||
@Schema(description = "表名称", example = "sys_user")
|
||||
private String tableName;
|
||||
|
||||
@Schema(description = "表描述",example = "用户表")
|
||||
private String tableComment;
|
||||
|
||||
@Schema(description = "表排序规则",example = "utf8mb4_general_ci")
|
||||
private String tableCollation;
|
||||
|
||||
@Schema(description = "存储引擎",example = "InnoDB")
|
||||
private String engine;
|
||||
|
||||
@Schema(description = "字符集",example = "utf8mb4")
|
||||
private String charset;
|
||||
|
||||
@Schema(description = "创建时间",example = "2023-08-08 08:08:08")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description="是否已配置")
|
||||
private Integer isConfigured;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.youlai.boot.codegen.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.CodegenPreviewVO;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 代码生成配置接口
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public interface CodegenService {
|
||||
|
||||
/**
|
||||
* 获取数据表分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @return
|
||||
*/
|
||||
Page<TablePageVO> getTablePage(TableQuery queryParams);
|
||||
|
||||
/**
|
||||
* 获取预览生成代码
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return
|
||||
*/
|
||||
List<CodegenPreviewVO> getCodegenPreviewData(String tableName, String pageType, String type);
|
||||
|
||||
/**
|
||||
* 下载代码
|
||||
* @param tableNames 表名
|
||||
* @return
|
||||
*/
|
||||
byte[] downloadCode(String[] tableNames, String pageType, String type);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.youlai.boot.codegen.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
|
||||
/**
|
||||
* 代码生成配置接口
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public interface GenTableColumnService extends IService<GenTableColumn> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.youlai.boot.codegen.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
|
||||
/**
|
||||
* 代码生成配置接口
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
public interface GenTableService extends IService<GenTable> {
|
||||
|
||||
/**
|
||||
* 获取代码生成配置
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return
|
||||
*/
|
||||
GenConfigForm getGenTableFormData(String tableName);
|
||||
|
||||
/**
|
||||
* 保存代码生成配置
|
||||
*
|
||||
* @param formData 表单数据
|
||||
* @return
|
||||
*/
|
||||
void saveGenConfig(GenConfigForm formData);
|
||||
|
||||
/**
|
||||
* 删除代码生成配置
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return
|
||||
*/
|
||||
void deleteGenConfig(String tableName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
package com.youlai.boot.codegen.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.template.Template;
|
||||
import cn.hutool.extra.template.TemplateConfig;
|
||||
import cn.hutool.extra.template.TemplateEngine;
|
||||
import cn.hutool.extra.template.TemplateUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.youlai.boot.codegen.enums.JavaTypeEnum;
|
||||
import com.youlai.boot.codegen.config.CodegenProperties;
|
||||
import com.youlai.boot.codegen.service.GenTableService;
|
||||
import com.youlai.boot.codegen.service.GenTableColumnService;
|
||||
import com.youlai.boot.codegen.service.CodegenService;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.codegen.mapper.DatabaseMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.model.query.TableQuery;
|
||||
import com.youlai.boot.codegen.model.vo.CodegenPreviewVO;
|
||||
import com.youlai.boot.codegen.model.vo.TablePageVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* 代码生成服务实现类。
|
||||
*
|
||||
* <p>
|
||||
* 根据代码生成配置({@link CodegenProperties})与表/字段元数据,渲染模板并提供预览与下载能力。
|
||||
* </p>
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class CodegenServiceImpl implements CodegenService {
|
||||
|
||||
private final DatabaseMapper databaseMapper;
|
||||
private final CodegenProperties codegenProperties;
|
||||
private final GenTableService genTableService;
|
||||
private final GenTableColumnService genTableColumnService;
|
||||
|
||||
/**
|
||||
* 数据表分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @return 分页结果
|
||||
*/
|
||||
public Page<TablePageVO> getTablePage(TableQuery queryParams) {
|
||||
Page<TablePageVO> page = new Page<>(queryParams.getPageNum(), queryParams.getPageSize());
|
||||
// 设置排除的表
|
||||
List<String> excludeTables = codegenProperties.getExcludeTables();
|
||||
queryParams.setExcludeTables(excludeTables);
|
||||
|
||||
return databaseMapper.getTablePage(page, queryParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析前端模板路径
|
||||
*
|
||||
* @param templateName 模板标识
|
||||
* @param templateConfig 模板配置
|
||||
* @param frontendType 前端类型
|
||||
* @return 模板路径
|
||||
*/
|
||||
private String resolveFrontendTemplatePath(String templateName,
|
||||
CodegenProperties.TemplateConfig templateConfig,
|
||||
String frontendType) {
|
||||
if (!"js".equals(frontendType)) {
|
||||
return templateConfig.getTemplatePath();
|
||||
}
|
||||
if ("API".equals(templateName)) {
|
||||
return "codegen/frontend/js/api.js.vm";
|
||||
}
|
||||
if ("VIEW".equals(templateName)) {
|
||||
return "codegen/frontend/js/index.js.vue.vm";
|
||||
}
|
||||
return templateConfig.getTemplatePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析前端文件后缀
|
||||
*
|
||||
* @param templateName 模板标识
|
||||
* @param templateConfig 模板配置
|
||||
* @param frontendType 前端类型
|
||||
* @return 文件后缀
|
||||
*/
|
||||
private String resolveFrontendExtension(String templateName,
|
||||
CodegenProperties.TemplateConfig templateConfig,
|
||||
String frontendType) {
|
||||
if (!"js".equals(frontendType)) {
|
||||
return templateConfig.getExtension();
|
||||
}
|
||||
if ("API".equals(templateName) || "API_TYPES".equals(templateName)) {
|
||||
return ".js";
|
||||
}
|
||||
return templateConfig.getExtension();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取预览生成代码
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return 预览数据
|
||||
*/
|
||||
@Override
|
||||
public List<CodegenPreviewVO> getCodegenPreviewData(String tableName, String pageType, String type) {
|
||||
|
||||
List<CodegenPreviewVO> list = new ArrayList<>();
|
||||
|
||||
GenTable genTable = genTableService.getOne(new LambdaQueryWrapper<GenTable>()
|
||||
.eq(GenTable::getTableName, tableName)
|
||||
);
|
||||
if (genTable == null) {
|
||||
throw new BusinessException("未找到表生成配置");
|
||||
}
|
||||
|
||||
List<GenTableColumn> fieldConfigs = genTableColumnService.list(new LambdaQueryWrapper<GenTableColumn>()
|
||||
.eq(GenTableColumn::getTableId, genTable.getId())
|
||||
.orderByAsc(GenTableColumn::getFieldSort)
|
||||
|
||||
);
|
||||
if (CollectionUtil.isEmpty(fieldConfigs)) {
|
||||
throw new BusinessException("未找到字段生成配置");
|
||||
}
|
||||
|
||||
// 遍历模板配置
|
||||
Map<String, CodegenProperties.TemplateConfig> templateConfigs = codegenProperties.getTemplateConfigs();
|
||||
String frontendType = StrUtil.blankToDefault(type, "ts").toLowerCase();
|
||||
for (Map.Entry<String, CodegenProperties.TemplateConfig> templateConfigEntry : templateConfigs.entrySet()) {
|
||||
CodegenPreviewVO previewVo = new CodegenPreviewVO();
|
||||
|
||||
CodegenProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
|
||||
|
||||
String templateName = templateConfigEntry.getKey();
|
||||
if ("js".equals(frontendType) && "API_TYPES".equals(templateName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String effectiveTemplatePath = resolveFrontendTemplatePath(templateName, templateConfig, frontendType);
|
||||
String extension = resolveFrontendExtension(templateName, templateConfig, frontendType);
|
||||
|
||||
/* 1. 生成文件名 UserController */
|
||||
// User Role Menu Dept
|
||||
String entityName = genTable.getEntityName();
|
||||
// Controller Service Mapper Entity
|
||||
// .java .ts .vue
|
||||
|
||||
// 文件名 UserController.java
|
||||
String fileName = getFileName(entityName, templateName, extension);
|
||||
previewVo.setFileName(fileName);
|
||||
previewVo.setScope(resolveScope(templateName));
|
||||
previewVo.setLanguage(resolveLanguage(fileName));
|
||||
|
||||
/* 2. 生成文件路径 */
|
||||
// 包名:com.youlai.boot
|
||||
String packageName = genTable.getPackageName();
|
||||
// 模块名:system
|
||||
String moduleName = genTable.getModuleName();
|
||||
// 子包名:controller
|
||||
String subpackageName = templateConfig.getSubpackageName();
|
||||
// 组合成文件路径:src/main/java/com/youlai/boot/system/controller
|
||||
String filePath = getFilePath(templateName, moduleName, packageName, subpackageName, entityName);
|
||||
previewVo.setPath(filePath);
|
||||
|
||||
/* 3. 生成文件内容 */
|
||||
// 将模板文件中的变量替换为具体的值 生成代码内容
|
||||
// 优先使用保存的 ui,没有则使用请求参数
|
||||
String finalType = StrUtil.blankToDefault(genTable.getPageType(), pageType);
|
||||
String content = getCodeContent(
|
||||
effectiveTemplatePath,
|
||||
templateConfig.getSubpackageName(),
|
||||
genTable,
|
||||
fieldConfigs,
|
||||
finalType
|
||||
);
|
||||
previewVo.setContent(content);
|
||||
|
||||
list.add(previewVo);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private String resolveScope(String templateName) {
|
||||
return switch (templateName) {
|
||||
case "API", "API_TYPES", "VIEW" -> "frontend";
|
||||
default -> "backend";
|
||||
};
|
||||
}
|
||||
|
||||
private String resolveLanguage(String fileName) {
|
||||
return FileNameUtil.extName(fileName).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件名。
|
||||
*
|
||||
* <p>部分模板需要使用约定的命名规则(例如前端 API 文件)。</p>
|
||||
*
|
||||
* @param entityName 实体名(例如 User)
|
||||
* @param templateName 模板名(例如 Entity、Controller、API)
|
||||
* @param extension 文件后缀(例如 .java、.ts)
|
||||
* @return 文件名
|
||||
*/
|
||||
private String getFileName(String entityName, String templateName, String extension) {
|
||||
if ("Entity".equals(templateName)) {
|
||||
return entityName + extension;
|
||||
} else if ("MapperXml".equals(templateName)) {
|
||||
return entityName + "Mapper" + extension;
|
||||
} else if ("API".equals(templateName)) {
|
||||
return "index" + extension;
|
||||
} else if ("API_TYPES".equals(templateName)) {
|
||||
return "types" + extension;
|
||||
} else if ("VIEW".equals(templateName)) {
|
||||
return "index.vue";
|
||||
}
|
||||
return entityName + templateName + extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件路径。
|
||||
*
|
||||
* @param templateName 模板名
|
||||
* @param moduleName 模块名(例如 system)
|
||||
* @param packageName 包名(例如 com.youlai.boot)
|
||||
* @param subPackageName 子包名(例如 controller、service.impl、api、views)
|
||||
* @param entityName 实体名(例如 User)
|
||||
* @return 生成文件路径
|
||||
*/
|
||||
private String getFilePath(String templateName, String moduleName, String packageName, String subPackageName, String entityName) {
|
||||
String path;
|
||||
if ("MapperXml".equals(templateName)) {
|
||||
path = (codegenProperties.getBackendAppName()
|
||||
+ File.separator
|
||||
+ "src" + File.separator + "main" + File.separator + "resources"
|
||||
+ File.separator + subPackageName
|
||||
+ File.separator + moduleName
|
||||
);
|
||||
} else if ("API".equals(templateName)) {
|
||||
path = (codegenProperties.getFrontendAppName()
|
||||
+ File.separator + "src"
|
||||
+ File.separator + "api"
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + StrUtil.toSymbolCase(entityName, '-')
|
||||
);
|
||||
} else if ("API_TYPES".equals(templateName)) {
|
||||
path = (codegenProperties.getFrontendAppName()
|
||||
+ File.separator + "src"
|
||||
+ File.separator + "api"
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + StrUtil.toSymbolCase(entityName, '-')
|
||||
);
|
||||
} else if ("VIEW".equals(templateName)) {
|
||||
// path = "src/views/system/user";
|
||||
path = (codegenProperties.getFrontendAppName()
|
||||
+ File.separator + "src"
|
||||
+ File.separator + subPackageName
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + StrUtil.toSymbolCase(entityName, '-')
|
||||
);
|
||||
} else {
|
||||
path = (codegenProperties.getBackendAppName()
|
||||
+ File.separator
|
||||
+ "src" + File.separator + "main" + File.separator + "java"
|
||||
+ File.separator + packageName
|
||||
+ File.separator + moduleName
|
||||
+ File.separator + subPackageName
|
||||
);
|
||||
}
|
||||
|
||||
// subPackageName = model.entity => model/entity
|
||||
path = path.replace(".", File.separator);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模板,生成代码内容。
|
||||
*
|
||||
* @param templateConfig 模板配置
|
||||
* @param genTable 表生成配置
|
||||
* @param fieldConfigs 字段配置
|
||||
* @param pageType 前端页面类型
|
||||
* @return 渲染后的代码内容
|
||||
*/
|
||||
private String getCodeContent(String templatePath,
|
||||
String subpackageName,
|
||||
GenTable genTable,
|
||||
List<GenTableColumn> fieldConfigs,
|
||||
String pageType) {
|
||||
|
||||
Map<String, Object> bindMap = new HashMap<>();
|
||||
|
||||
String entityName = genTable.getEntityName();
|
||||
|
||||
bindMap.put("packageName", genTable.getPackageName());
|
||||
bindMap.put("moduleName", genTable.getModuleName());
|
||||
bindMap.put("subpackageName", subpackageName);
|
||||
bindMap.put("date", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm"));
|
||||
bindMap.put("entityName", entityName);
|
||||
bindMap.put("tableName", genTable.getTableName());
|
||||
bindMap.put("author", genTable.getAuthor());
|
||||
String entityLowerCamel = StrUtil.lowerFirst(entityName);
|
||||
String entityKebab = StrUtil.toSymbolCase(entityName, '-');
|
||||
String entityUpperSnake = StrUtil.toSymbolCase(entityName, '_').toUpperCase();
|
||||
bindMap.put("entityLowerCamel", entityLowerCamel);
|
||||
bindMap.put("entityKebab", entityKebab);
|
||||
bindMap.put("entityUpperSnake", entityUpperSnake);
|
||||
bindMap.put("businessName", genTable.getBusinessName());
|
||||
bindMap.put("entityComment", genTable.getBusinessName());
|
||||
bindMap.put("fieldConfigs", fieldConfigs);
|
||||
|
||||
boolean hasLocalDateTime = false;
|
||||
boolean hasBigDecimal = false;
|
||||
boolean hasRequiredField = false;
|
||||
|
||||
for (GenTableColumn fieldConfig : fieldConfigs) {
|
||||
|
||||
if (StrUtil.isBlank(fieldConfig.getFieldType())) {
|
||||
fieldConfig.setFieldType(JavaTypeEnum.getJavaTypeByColumnType(fieldConfig.getColumnType()));
|
||||
}
|
||||
|
||||
if ("LocalDateTime".equals(fieldConfig.getFieldType()) || "LocalDate".equals(fieldConfig.getFieldType())) {
|
||||
hasLocalDateTime = true;
|
||||
}
|
||||
if ("BigDecimal".equals(fieldConfig.getFieldType())) {
|
||||
hasBigDecimal = true;
|
||||
}
|
||||
if (ObjectUtil.equals(fieldConfig.getIsRequired(), 1)) {
|
||||
hasRequiredField = true;
|
||||
}
|
||||
fieldConfig.setTsType(JavaTypeEnum.getTsTypeByJavaType(fieldConfig.getFieldType()));
|
||||
}
|
||||
|
||||
bindMap.put("hasLocalDateTime", hasLocalDateTime);
|
||||
bindMap.put("hasBigDecimal", hasBigDecimal);
|
||||
bindMap.put("hasRequiredField", hasRequiredField);
|
||||
|
||||
TemplateEngine templateEngine = TemplateUtil.createEngine(new TemplateConfig("templates", TemplateConfig.ResourceMode.CLASSPATH));
|
||||
// 根据 ui 选择不同的前端页面模板:默认 index.vue.vm;封装版使用 index.curd.vue.vm
|
||||
String path = templatePath;
|
||||
if ("curd".equalsIgnoreCase(pageType)) {
|
||||
if (path.endsWith("index.js.vue.vm")) {
|
||||
path = path.replace("index.js.vue.vm", "index.curd.js.vue.vm");
|
||||
} else if (path.endsWith("index.vue.vm")) {
|
||||
path = path.replace("index.vue.vm", "index.curd.vue.vm");
|
||||
}
|
||||
}
|
||||
Template template = templateEngine.getTemplate(path);
|
||||
|
||||
return template.render(bindMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载代码。
|
||||
*
|
||||
* @param tableNames 表名数组,支持多张表
|
||||
* @param ui 页面类型
|
||||
* @return zip 压缩文件字节数组
|
||||
*/
|
||||
@Override
|
||||
public byte[] downloadCode(String[] tableNames, String ui, String type) {
|
||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zip = new ZipOutputStream(outputStream)) {
|
||||
|
||||
// 遍历每个表名,生成对应的代码并压缩到 zip 文件中
|
||||
for (String tableName : tableNames) {
|
||||
generateAndZipCode(tableName, zip, ui, type);
|
||||
}
|
||||
// 确保所有压缩数据写入输出流,避免数据残留在内存缓冲区引发的数据不完整
|
||||
zip.finish();
|
||||
return outputStream.toByteArray();
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Error while generating zip for code download", e);
|
||||
throw new RuntimeException("Failed to generate code zip file1", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表名生成代码并压缩到 zip 文件中。
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param zip 压缩文件输出流
|
||||
* @param ui 页面类型
|
||||
*/
|
||||
private void generateAndZipCode(String tableName, ZipOutputStream zip, String ui, String type) {
|
||||
List<CodegenPreviewVO> codePreviewList = getCodegenPreviewData(tableName, ui, type);
|
||||
|
||||
for (CodegenPreviewVO codePreview : codePreviewList) {
|
||||
String fileName = codePreview.getFileName();
|
||||
String content = codePreview.getContent();
|
||||
String path = codePreview.getPath();
|
||||
|
||||
try {
|
||||
// 创建压缩条目
|
||||
ZipEntry zipEntry = new ZipEntry(path + File.separator + fileName);
|
||||
zip.putNextEntry(zipEntry);
|
||||
|
||||
// 写入文件内容
|
||||
zip.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// 关闭当前压缩条目
|
||||
zip.closeEntry();
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Error while adding file1 {} to zip", fileName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.youlai.boot.codegen.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.boot.codegen.mapper.GenTableColumnMapper;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.service.GenTableColumnService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 代码生成字段配置服务实现类
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GenTableColumnServiceImpl extends ServiceImpl<GenTableColumnMapper, GenTableColumn> implements GenTableColumnService {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
package com.youlai.boot.codegen.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.boot.common.enums.EnvEnum;
|
||||
import com.youlai.boot.codegen.enums.FormTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.JavaTypeEnum;
|
||||
import com.youlai.boot.codegen.enums.QueryTypeEnum;
|
||||
import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.codegen.config.CodegenProperties;
|
||||
import com.youlai.boot.codegen.converter.CodegenConverter;
|
||||
import com.youlai.boot.codegen.mapper.DatabaseMapper;
|
||||
import com.youlai.boot.codegen.mapper.GenTableMapper;
|
||||
import com.youlai.boot.codegen.model.vo.ColumnMetaVO;
|
||||
import com.youlai.boot.codegen.model.vo.TableMetaVO;
|
||||
import com.youlai.boot.codegen.model.entity.GenTable;
|
||||
import com.youlai.boot.codegen.model.entity.GenTableColumn;
|
||||
import com.youlai.boot.codegen.model.form.GenConfigForm;
|
||||
import com.youlai.boot.codegen.service.GenTableService;
|
||||
import com.youlai.boot.codegen.service.GenTableColumnService;
|
||||
import com.youlai.boot.common.event.CodegenMenuEvent;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据库服务实现类
|
||||
*
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> implements GenTableService {
|
||||
|
||||
private final DatabaseMapper databaseMapper;
|
||||
private final CodegenProperties codegenProperties;
|
||||
private final GenTableColumnService genTableColumnService;
|
||||
private final CodegenConverter codegenConverter;
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String springProfilesActive;
|
||||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
/**
|
||||
* 获取代码生成配置
|
||||
*
|
||||
* @param tableName 表名 eg: sys_user
|
||||
* @return 代码生成配置
|
||||
*/
|
||||
@Override
|
||||
public GenConfigForm getGenTableFormData(String tableName) {
|
||||
// 查询表生成配置
|
||||
GenTable genTable = this.getOne(
|
||||
new LambdaQueryWrapper<>(GenTable.class)
|
||||
.eq(GenTable::getTableName, tableName)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
|
||||
// 是否有代码生成配置
|
||||
boolean hasGenTable = genTable != null;
|
||||
|
||||
// 如果没有代码生成配置,则根据表的元数据生成默认配置
|
||||
if (genTable == null) {
|
||||
TableMetaVO tableMetadata = databaseMapper.getTableMetadata(tableName);
|
||||
Assert.isTrue(tableMetadata != null, "未找到表元数据");
|
||||
|
||||
genTable = new GenTable();
|
||||
genTable.setTableName(tableName);
|
||||
|
||||
// 表注释作为业务名称,去掉表字 例如:用户表 -> 用户
|
||||
String tableComment = tableMetadata.getTableComment();
|
||||
if (StrUtil.isNotBlank(tableComment)) {
|
||||
genTable.setBusinessName(tableComment.replace("表", "").trim());
|
||||
}
|
||||
// 根据表名生成实体类名,支持去除前缀 例如:sys_user -> SysUser
|
||||
String removePrefix = genTable.getRemoveTablePrefix();
|
||||
String processedTable = tableName;
|
||||
if (StrUtil.isNotBlank(removePrefix) && StrUtil.startWith(tableName, removePrefix)) {
|
||||
processedTable = StrUtil.removePrefix(tableName, removePrefix);
|
||||
}
|
||||
genTable.setEntityName(StrUtil.toCamelCase(StrUtil.upperFirst(StrUtil.toCamelCase(processedTable))));
|
||||
|
||||
genTable.setPackageName(codegenProperties.getDefaultConfig().getPackageName());
|
||||
genTable.setModuleName(codegenProperties.getDefaultConfig().getModuleName()); // 默认模块名
|
||||
genTable.setAuthor(codegenProperties.getDefaultConfig().getAuthor());
|
||||
}
|
||||
|
||||
// 根据表的列 + 已经存在的字段生成配置 得到 组合后的字段生成配置
|
||||
List<GenTableColumn> genTableColumns = new ArrayList<>();
|
||||
|
||||
// 获取表的列
|
||||
List<ColumnMetaVO> tableColumns = databaseMapper.getTableColumns(tableName);
|
||||
if (CollectionUtil.isNotEmpty(tableColumns)) {
|
||||
// 查询字段生成配置
|
||||
List<GenTableColumn> fieldConfigList = genTableColumnService.list(
|
||||
new LambdaQueryWrapper<GenTableColumn>()
|
||||
.eq(GenTableColumn::getTableId, genTable.getId())
|
||||
.orderByAsc(GenTableColumn::getFieldSort)
|
||||
);
|
||||
Integer maxSort = fieldConfigList.stream()
|
||||
.map(GenTableColumn::getFieldSort)
|
||||
.filter(Objects::nonNull) // 过滤掉空值
|
||||
.max(Integer::compareTo)
|
||||
.orElse(0);
|
||||
for (ColumnMetaVO tableColumn : tableColumns) {
|
||||
// 根据列名获取字段生成配置
|
||||
String columnName = tableColumn.getColumnName();
|
||||
GenTableColumn fieldConfig = fieldConfigList.stream()
|
||||
.filter(item -> StrUtil.equals(item.getColumnName(), columnName))
|
||||
.findFirst()
|
||||
.orElseGet(() -> createDefaultFieldConfig(tableColumn));
|
||||
if (fieldConfig.getFieldSort() == null) {
|
||||
fieldConfig.setFieldSort(++maxSort);
|
||||
}
|
||||
// 根据列类型设置字段类型
|
||||
String fieldType = fieldConfig.getFieldType();
|
||||
if (StrUtil.isBlank(fieldType)) {
|
||||
String javaType = JavaTypeEnum.getJavaTypeByColumnType(fieldConfig.getColumnType());
|
||||
fieldConfig.setFieldType(javaType);
|
||||
}
|
||||
// 如果没有代码生成配置,则默认展示在列表和表单
|
||||
if (!hasGenTable) {
|
||||
fieldConfig.setIsShowInList(1);
|
||||
fieldConfig.setIsShowInForm(1);
|
||||
}
|
||||
genTableColumns.add(fieldConfig);
|
||||
}
|
||||
}
|
||||
// 对 genTableColumns 按照 fieldSort 排序
|
||||
genTableColumns = genTableColumns.stream().sorted(Comparator.comparing(GenTableColumn::getFieldSort)).toList();
|
||||
GenConfigForm genConfigForm = codegenConverter.toGenConfigForm(genTable, genTableColumns);
|
||||
|
||||
genConfigForm.setFrontendAppName(codegenProperties.getFrontendAppName());
|
||||
genConfigForm.setBackendAppName(codegenProperties.getBackendAppName());
|
||||
return genConfigForm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建默认字段配置
|
||||
*
|
||||
* @param columnMetaVO 表字段元数据
|
||||
* @return
|
||||
*/
|
||||
private GenTableColumn createDefaultFieldConfig(ColumnMetaVO columnMetaVO) {
|
||||
GenTableColumn fieldConfig = new GenTableColumn();
|
||||
fieldConfig.setColumnName(columnMetaVO.getColumnName());
|
||||
fieldConfig.setColumnType(columnMetaVO.getDataType());
|
||||
fieldConfig.setFieldComment(columnMetaVO.getColumnComment());
|
||||
fieldConfig.setFieldName(StrUtil.toCamelCase(columnMetaVO.getColumnName()));
|
||||
fieldConfig.setIsRequired("YES".equals(columnMetaVO.getIsNullable()) ? 0 : 1);
|
||||
|
||||
String columnType = StrUtil.blankToDefault(fieldConfig.getColumnType(), "").toLowerCase();
|
||||
if ("date".equals(columnType)) {
|
||||
fieldConfig.setFormType(FormTypeEnum.DATE);
|
||||
} else if ("datetime".equals(columnType) || "timestamp".equals(columnType)) {
|
||||
fieldConfig.setFormType(FormTypeEnum.DATE_TIME);
|
||||
} else {
|
||||
fieldConfig.setFormType(FormTypeEnum.INPUT);
|
||||
}
|
||||
|
||||
fieldConfig.setQueryType(QueryTypeEnum.EQ);
|
||||
fieldConfig.setMaxLength(columnMetaVO.getCharacterMaximumLength());
|
||||
return fieldConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存代码生成配置
|
||||
*
|
||||
* @param formData 代码生成配置表单
|
||||
*/
|
||||
@Override
|
||||
public void saveGenConfig(GenConfigForm formData) {
|
||||
GenTable genTable = codegenConverter.toGenTable(formData);
|
||||
this.saveOrUpdate(genTable);
|
||||
|
||||
// 如果选择上级菜单且当前环境不是生产环境,发布菜单创建事件
|
||||
Long parentMenuId = formData.getParentMenuId();
|
||||
if (parentMenuId != null && !EnvEnum.PROD.getValue().equals(springProfilesActive)) {
|
||||
eventPublisher.publishEvent(new CodegenMenuEvent(this, parentMenuId,
|
||||
genTable.getBusinessName(), genTable.getModuleName(), genTable.getPackageName()));
|
||||
}
|
||||
|
||||
List<GenTableColumn> genTableColumns = codegenConverter.toGenTableColumn(formData.getFieldConfigs());
|
||||
|
||||
if (CollectionUtil.isEmpty(genTableColumns)) {
|
||||
throw new BusinessException("字段配置不能为空");
|
||||
}
|
||||
genTableColumns.forEach(genTableColumn -> {
|
||||
genTableColumn.setTableId(genTable.getId());
|
||||
});
|
||||
genTableColumnService.saveOrUpdateBatch(genTableColumns);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除代码生成配置
|
||||
*
|
||||
* @param tableName 表名
|
||||
*/
|
||||
@Override
|
||||
public void deleteGenConfig(String tableName) {
|
||||
GenTable genTable = this.getOne(new LambdaQueryWrapper<GenTable>()
|
||||
.eq(GenTable::getTableName, tableName));
|
||||
|
||||
boolean result = this.remove(new LambdaQueryWrapper<GenTable>()
|
||||
.eq(GenTable::getTableName, tableName)
|
||||
);
|
||||
if (result) {
|
||||
genTableColumnService.remove(new LambdaQueryWrapper<GenTableColumn>()
|
||||
.eq(GenTableColumn::getTableId, genTable.getId())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
61
youlai-codegen/src/main/resources/codegen.yml
Normal file
61
youlai-codegen/src/main/resources/codegen.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
# 代码生成器配置
|
||||
codegen:
|
||||
# 下载代码文件名称
|
||||
downloadFileName: youlai-admin-code.zip
|
||||
# 后端项目名称
|
||||
backendAppName: youlai-boot
|
||||
# 前端项目名称
|
||||
frontendAppName: vue3-element-admin
|
||||
# 默认配置
|
||||
defaultConfig:
|
||||
author: youlaitech
|
||||
moduleName: system
|
||||
# 排除数据表
|
||||
excludeTables:
|
||||
- gen_table
|
||||
- gen_table_column
|
||||
## 模板配置
|
||||
templateConfigs:
|
||||
API:
|
||||
templatePath: codegen/frontend/ts/api.ts.vm
|
||||
subpackageName: api
|
||||
extension: .ts
|
||||
API_TYPES:
|
||||
templatePath: codegen/frontend/ts/api-types.ts.vm
|
||||
subpackageName: types
|
||||
extension: .ts
|
||||
VIEW:
|
||||
templatePath: codegen/frontend/ts/index.vue.vm
|
||||
subpackageName: views
|
||||
extension: .vue
|
||||
Controller:
|
||||
templatePath: codegen/backend/controller.java.vm
|
||||
subpackageName: controller
|
||||
Service:
|
||||
templatePath: codegen/backend/service.java.vm
|
||||
subpackageName: service
|
||||
ServiceImpl:
|
||||
templatePath: codegen/backend/serviceImpl.java.vm
|
||||
subpackageName: service.impl
|
||||
Mapper:
|
||||
templatePath: codegen/backend/mapper.java.vm
|
||||
subpackageName: mapper
|
||||
MapperXml:
|
||||
templatePath: codegen/backend/mapper.xml.vm
|
||||
subpackageName: mapper
|
||||
extension: .xml
|
||||
Converter:
|
||||
templatePath: codegen/backend/converter.java.vm
|
||||
subpackageName: converter
|
||||
Query:
|
||||
templatePath: codegen/backend/query.java.vm
|
||||
subpackageName: model.query
|
||||
Form:
|
||||
templatePath: codegen/backend/form.java.vm
|
||||
subpackageName: model.form
|
||||
Vo:
|
||||
templatePath: codegen/backend/vo.java.vm
|
||||
subpackageName: model.vo
|
||||
Entity:
|
||||
templatePath: codegen/backend/entity.java.vm
|
||||
subpackageName: model.entity
|
||||
@@ -0,0 +1,71 @@
|
||||
<?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">
|
||||
<!--suppress ALL -->
|
||||
<mapper namespace="com.youlai.boot.codegen.mapper.DatabaseMapper">
|
||||
|
||||
<!-- 查询数据库表分页 -->
|
||||
<select id="getTablePage" resultType="com.youlai.boot.codegen.model.vo.TablePageVO">
|
||||
SELECT
|
||||
t1.TABLE_NAME ,
|
||||
t1.TABLE_COMMENT ,
|
||||
t1.TABLE_COLLATION,
|
||||
t1.ENGINE,
|
||||
t1.CREATE_TIME,
|
||||
CASE WHEN t2.id IS NOT NULL THEN 1 ELSE 0 END AS isConfigured
|
||||
FROM
|
||||
information_schema.tables t1
|
||||
LEFT JOIN gen_table t2 on t1.TABLE_NAME = t2.table_name
|
||||
WHERE
|
||||
t1.TABLE_SCHEMA = (SELECT DATABASE())
|
||||
AND t1.table_type = 'BASE TABLE'
|
||||
<if test="queryParams.keywords != null and queryParams.keywords.trim() neq ''">
|
||||
AND t1.TABLE_NAME LIKE CONCAT('%',#{queryParams.keywords},'%')
|
||||
</if>
|
||||
<!-- 排除的表 -->
|
||||
<if test="queryParams.excludeTables != null and queryParams.excludeTables.size() > 0">
|
||||
AND t1.TABLE_NAME NOT IN
|
||||
<foreach collection="queryParams.excludeTables" item="excludeTable" open="(" close=")" separator=",">
|
||||
#{excludeTable}
|
||||
</foreach>
|
||||
</if>
|
||||
ORDER BY
|
||||
CREATE_TIME DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询数据库表元数据 -->
|
||||
<select id="getTableMetadata" resultType="com.youlai.boot.codegen.model.vo.TableMetaVO">
|
||||
SELECT
|
||||
TABLE_NAME ,
|
||||
TABLE_COMMENT ,
|
||||
TABLE_COLLATION,
|
||||
ENGINE,
|
||||
CREATE_TIME
|
||||
FROM
|
||||
information_schema.tables
|
||||
WHERE
|
||||
TABLE_SCHEMA = (SELECT DATABASE())
|
||||
AND TABLE_NAME = #{tableName}
|
||||
</select>
|
||||
|
||||
<!-- 获取数据库表字段元数据 -->
|
||||
<select id="getTableColumns" resultType="com.youlai.boot.codegen.model.vo.ColumnMetaVO">
|
||||
SELECT
|
||||
COLUMN_NAME,
|
||||
DATA_TYPE,
|
||||
COLUMN_COMMENT,
|
||||
CASE COLUMN_KEY WHEN 'PRI' THEN 1 ELSE 0 END AS isPrimaryKey,
|
||||
IS_NULLABLE,
|
||||
CHARACTER_MAXIMUM_LENGTH,
|
||||
CHARACTER_SET_NAME,
|
||||
COLLATION_NAME
|
||||
FROM
|
||||
information_schema.columns
|
||||
WHERE
|
||||
TABLE_SCHEMA = (SELECT DATABASE())
|
||||
AND TABLE_NAME = #{tableName}
|
||||
ORDER BY ORDINAL_POSITION ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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="com.youlai.boot.platform.codegen.mapper.GenConfigMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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="com.youlai.boot.platform.codegen.mapper.GenTableColumnMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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="com.youlai.boot.platform.codegen.mapper.GenTableColumnMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?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="com.youlai.boot.platform.codegen.mapper.GenTableMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,81 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import ${packageName}.${moduleName}.service.${entityName}Service;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ${packageName}.${moduleName}.model.form.${entityName}Form;
|
||||
import ${packageName}.${moduleName}.model.query.${entityName}Query;
|
||||
import ${packageName}.${moduleName}.model.vo.${entityName}VO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import ${packageName}.common.result.PageResult;
|
||||
import ${packageName}.common.result.Result;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* $!{businessName}前端控制层
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Tag(name = "${businessName}接口")
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/${entityKebab}")
|
||||
@RequiredArgsConstructor
|
||||
public class ${entityName}Controller {
|
||||
|
||||
private final ${entityName}Service ${entityLowerCamel}Service;
|
||||
|
||||
@Operation(summary = "$!{businessName}分页列表")
|
||||
@GetMapping
|
||||
@PreAuthorize("@ss.hasPerm('${moduleName}:${entityKebab}:list')")
|
||||
public PageResult<${entityName}VO> get${entityName}Page(${entityName}Query queryParams ) {
|
||||
IPage<${entityName}VO> result = ${entityLowerCamel}Service.get${entityName}Page(queryParams);
|
||||
return PageResult.success(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "新增${businessName}")
|
||||
@PostMapping
|
||||
@PreAuthorize("@ss.hasPerm('${moduleName}:${entityKebab}:create')")
|
||||
public Result<Void> save${entityName}(@RequestBody @Valid ${entityName}Form formData ) {
|
||||
boolean result = ${entityLowerCamel}Service.save${entityName}(formData);
|
||||
return Result.judge(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取${businessName}表单数据")
|
||||
@GetMapping("/{id}/form")
|
||||
@PreAuthorize("@ss.hasPerm('${moduleName}:${entityKebab}:update')")
|
||||
public Result<${entityName}Form> get${entityName}Form(
|
||||
@Parameter(description = "$!{businessName}ID") @PathVariable Long id
|
||||
) {
|
||||
${entityName}Form formData = ${entityLowerCamel}Service.get${entityName}FormData(id);
|
||||
return Result.success(formData);
|
||||
}
|
||||
|
||||
@Operation(summary = "修改${businessName}")
|
||||
@PutMapping(value = "/{id}")
|
||||
@PreAuthorize("@ss.hasPerm('${moduleName}:${entityKebab}:update')")
|
||||
public Result<Void> update${entityName}(
|
||||
@Parameter(description = "$!{businessName}ID") @PathVariable Long id,
|
||||
@RequestBody @Validated ${entityName}Form formData
|
||||
) {
|
||||
boolean result = ${entityLowerCamel}Service.update${entityName}(id, formData);
|
||||
return Result.judge(result);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除${businessName}")
|
||||
@DeleteMapping("/{ids}")
|
||||
@PreAuthorize("@ss.hasPerm('${moduleName}:${entityKebab}:delete')")
|
||||
public Result<Void> delete${entityName}s(
|
||||
@Parameter(description = "$!{businessName}ID,多个以英文逗号(,)分割") @PathVariable String ids
|
||||
) {
|
||||
boolean result = ${entityLowerCamel}Service.delete${entityName}s(ids);
|
||||
return Result.judge(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import ${packageName}.${moduleName}.model.entity.${entityName};
|
||||
import ${packageName}.${moduleName}.model.form.${entityName}Form;
|
||||
|
||||
/**
|
||||
* $!{businessName}对象转换器
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface ${entityName}Converter{
|
||||
|
||||
${entityName}Form toForm(${entityName} entity);
|
||||
|
||||
${entityName} toEntity(${entityName}Form formData);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
#if(${hasLocalDateTime})
|
||||
import java.time.LocalDateTime;
|
||||
#end
|
||||
#if(${hasBigDecimal})
|
||||
import java.math.BigDecimal;
|
||||
#end
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import ${packageName}.common.base.BaseEntity;
|
||||
|
||||
/**
|
||||
* $!{businessName}实体对象
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@TableName("${tableName}")
|
||||
public class ${entityName} extends BaseEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if(!$fieldConfig.fieldName.equals("id") && !$fieldConfig.fieldName.equals("createTime") && !$fieldConfig.fieldName.equals("updateTime"))
|
||||
#if("$!fieldConfig.fieldComment" != "")
|
||||
/**
|
||||
* ${fieldConfig.fieldComment}
|
||||
*/
|
||||
#end
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
#end
|
||||
#end
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
#if(${hasLocalDateTime})
|
||||
import java.time.LocalDateTime;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
#end
|
||||
#if(${hasBigDecimal})
|
||||
import java.math.BigDecimal;
|
||||
#end
|
||||
#if(${hasRequiredField})
|
||||
import jakarta.validation.constraints.*;
|
||||
#end
|
||||
|
||||
/**
|
||||
* $!{businessName}表单对象
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "$!{businessName}表单对象")
|
||||
public class ${entityName}Form implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "${businessName}ID")
|
||||
private Long id;
|
||||
|
||||
## ---------- BEGIN 字段循环遍历 ----------
|
||||
#if($fieldConfigs)
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInForm)
|
||||
#if("$!fieldConfig.fieldComment" != "")
|
||||
@Schema(description = "${fieldConfig.fieldComment}")
|
||||
#end
|
||||
#if($fieldConfig.isRequired)
|
||||
#if($fieldConfig.fieldType == 'String')
|
||||
@NotBlank(message = "$fieldConfig.fieldComment不能为空")
|
||||
#else
|
||||
@NotNull(message = "$fieldConfig.fieldComment不能为空")
|
||||
#end
|
||||
#end
|
||||
#if($fieldConfig.maxLength)
|
||||
@Size(max=$fieldConfig.maxLength, message="$fieldConfig.fieldComment长度不能超过${fieldConfig.maxLength}个字符")
|
||||
#end
|
||||
#if($fieldConfig.fieldType == 'LocalDateTime')
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
#end
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import ${packageName}.${moduleName}.model.entity.${entityName};
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import ${packageName}.${moduleName}.model.query.${entityName}Query;
|
||||
import ${packageName}.${moduleName}.model.vo.${entityName}VO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* $!{businessName}Mapper接口
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Mapper
|
||||
public interface ${entityName}Mapper extends BaseMapper<${entityName}> {
|
||||
|
||||
/**
|
||||
* 获取${businessName}分页数据
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param queryParams 查询参数
|
||||
* @return {@link Page<${entityName}VO>} $!{businessName}分页列表
|
||||
*/
|
||||
Page<${entityName}VO> get${entityName}Page(Page<${entityName}VO> page, ${entityName}Query queryParams);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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="${packageName}.${moduleName}.mapper.${entityName}Mapper">
|
||||
|
||||
<!-- 获取${businessName}分页列表 -->
|
||||
<select id="get${entityName}Page" resultType="${packageName}.${moduleName}.model.vo.${entityName}VO">
|
||||
SELECT
|
||||
#if($fieldConfigs)
|
||||
#set ($fields = [])
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInList)
|
||||
#set ($discard = $fields.add(${fieldConfig.columnName}))
|
||||
#end
|
||||
#end
|
||||
#foreach($field in $fields)
|
||||
${field}#if($foreach.hasNext),#end
|
||||
#end
|
||||
#end
|
||||
FROM
|
||||
${tableName}
|
||||
<where>
|
||||
#if($fieldConfigs)
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInQuery)
|
||||
#if($fieldConfig.fieldType == "String")
|
||||
<if test="queryParams.${fieldConfig.fieldName} != null and queryParams.${fieldConfig.fieldName} != ''">
|
||||
#else
|
||||
<if test="queryParams.${fieldConfig.fieldName} != null">
|
||||
#end
|
||||
#set ($queryType = ${fieldConfig.queryType}.name())
|
||||
#if($queryType == "EQ")
|
||||
AND ${fieldConfig.columnName} = #{queryParams.${fieldConfig.fieldName}}
|
||||
#elseif($queryType == "LIKE")
|
||||
AND ${fieldConfig.columnName} LIKE CONCAT('%', #{queryParams.${fieldConfig.fieldName}}, '%')
|
||||
#elseif($queryType == "IN")
|
||||
<if test="queryParams.${fieldConfig.fieldName}.size() > 0">
|
||||
AND ${fieldConfig.columnName} IN <foreach collection="queryParams.${fieldConfig.fieldName}" item="item" open="(" close=")" separator=",">#{item}</foreach>
|
||||
</if>
|
||||
#elseif($queryType == "BETWEEN")
|
||||
#if($fieldConfig.formType.name() == "DATE" || $fieldConfig.formType.name() == "DATE_TIME")
|
||||
<if test="queryParams.${fieldConfig.fieldName}[0] != null and queryParams.${fieldConfig.fieldName}[0] != ''">
|
||||
<bind name="startDate" value="queryParams.${fieldConfig.fieldName}[0].length() == 10 ? queryParams.${fieldConfig.fieldName}[0] + ' 00:00:00' : queryParams.${fieldConfig.fieldName}[0]"/>
|
||||
AND ${fieldConfig.columnName} >= #{startDate}
|
||||
</if>
|
||||
<if test="queryParams.${fieldConfig.fieldName}[1] != null and queryParams.${fieldConfig.fieldName}[1] != ''">
|
||||
<bind name="endDate" value="queryParams.${fieldConfig.fieldName}[1].length() == 10 ? queryParams.${fieldConfig.fieldName}[1] + ' 23:59:59' : queryParams.${fieldConfig.fieldName}[1]"/>
|
||||
AND ${fieldConfig.columnName} <= #{endDate}
|
||||
</if>
|
||||
#else
|
||||
<if test="queryParams.${fieldConfig.fieldName}[0] != null and queryParams.${fieldConfig.fieldName}[0] != ''">
|
||||
AND ${fieldConfig.columnName} >= #{queryParams.${fieldConfig.fieldName}[0]}
|
||||
</if>
|
||||
<if test="queryParams.${fieldConfig.fieldName}[1] != null and queryParams.${fieldConfig.fieldName}[1] != ''">
|
||||
AND ${fieldConfig.columnName} <= #{queryParams.${fieldConfig.fieldName}[1]}
|
||||
</if>
|
||||
#end
|
||||
#elseif($queryType == "GT")
|
||||
AND ${fieldConfig.columnName} > #{queryParams.${fieldConfig.fieldName}}
|
||||
#elseif($queryType == "GE")
|
||||
AND ${fieldConfig.columnName} >= #{queryParams.${fieldConfig.fieldName}}
|
||||
#elseif($queryType == "LT")
|
||||
AND ${fieldConfig.columnName} < #{queryParams.${fieldConfig.fieldName}}
|
||||
#elseif($queryType == "LE")
|
||||
AND ${fieldConfig.columnName} <= #{queryParams.${fieldConfig.fieldName}}
|
||||
#elseif($queryType == "NE")
|
||||
AND ${fieldConfig.columnName} != #{queryParams.${fieldConfig.fieldName}}
|
||||
#elseif($queryType == "LIKE_LEFT")
|
||||
AND ${fieldConfig.columnName} LIKE CONCAT('%', #{queryParams.${fieldConfig.fieldName}})
|
||||
#elseif($queryType == "LIKE_RIGHT")
|
||||
AND ${fieldConfig.columnName} LIKE CONCAT(#{queryParams.${fieldConfig.fieldName}}, '%')
|
||||
#end
|
||||
</if>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,44 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import ${packageName}.common.base.BaseQuery;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.util.List;
|
||||
#if(${hasLocalDateTime})
|
||||
import java.time.LocalDateTime;
|
||||
#end
|
||||
#if(${hasBigDecimal})
|
||||
import java.math.BigDecimal;
|
||||
#end
|
||||
|
||||
/**
|
||||
* $!{businessName}查询对象
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Schema(description ="$!{businessName}查询对象")
|
||||
@Getter
|
||||
@Setter
|
||||
public class ${entityName}Query extends BaseQuery {
|
||||
|
||||
#if($fieldConfigs)
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInQuery)
|
||||
#if("$!fieldConfig.fieldComment" != "")
|
||||
@Schema(description = "${fieldConfig.fieldComment}")
|
||||
#end
|
||||
#if($fieldConfig.formType.name() == "DATE" || $fieldConfig.formType.name() == "DATE_TIME")
|
||||
#if($fieldConfig.queryType.name() == "BETWEEN")
|
||||
private List<String> ${fieldConfig.fieldName};
|
||||
#else
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
#end
|
||||
#else
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import ${packageName}.${moduleName}.model.entity.${entityName};
|
||||
import ${packageName}.${moduleName}.model.form.${entityName}Form;
|
||||
import ${packageName}.${moduleName}.model.query.${entityName}Query;
|
||||
import ${packageName}.${moduleName}.model.vo.${entityName}VO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* $!{businessName}服务类
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
public interface ${entityName}Service extends IService<${entityName}> {
|
||||
|
||||
/**
|
||||
*$!{businessName}分页列表
|
||||
*
|
||||
* @return {@link IPage<${entityName}VO>} $!{businessName}分页列表
|
||||
*/
|
||||
IPage<${entityName}VO> get${entityName}Page(${entityName}Query queryParams);
|
||||
|
||||
/**
|
||||
* 获取${businessName}表单数据
|
||||
*
|
||||
* @param id $!{businessName}ID
|
||||
* @return ${businessName}表单数据
|
||||
*/
|
||||
${entityName}Form get${entityName}FormData(Long id);
|
||||
|
||||
/**
|
||||
* 新增${businessName}
|
||||
*
|
||||
* @param formData $!{businessName}表单对象
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
boolean save${entityName}(${entityName}Form formData);
|
||||
|
||||
/**
|
||||
* 修改${businessName}
|
||||
*
|
||||
* @param id $!{businessName}ID
|
||||
* @param formData $!{businessName}表单对象
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
boolean update${entityName}(Long id, ${entityName}Form formData);
|
||||
|
||||
/**
|
||||
* 删除${businessName}
|
||||
*
|
||||
* @param ids $!{businessName}ID,多个以英文逗号(,)分割
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
boolean delete${entityName}s(String ids);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import ${packageName}.${moduleName}.mapper.${entityName}Mapper;
|
||||
import ${packageName}.${moduleName}.service.${entityName}Service;
|
||||
import ${packageName}.${moduleName}.model.entity.${entityName};
|
||||
import ${packageName}.${moduleName}.model.form.${entityName}Form;
|
||||
import ${packageName}.${moduleName}.model.query.${entityName}Query;
|
||||
import ${packageName}.${moduleName}.model.vo.${entityName}VO;
|
||||
import ${packageName}.${moduleName}.converter.${entityName}Converter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* $!{businessName}服务实现类
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ${entityName}ServiceImpl extends ServiceImpl<${entityName}Mapper, ${entityName}> implements ${entityName}Service {
|
||||
|
||||
private final ${entityName}Converter ${entityLowerCamel}Converter;
|
||||
|
||||
/**
|
||||
* 获取${businessName}分页列表
|
||||
*
|
||||
* @param queryParams 查询参数
|
||||
* @return {@link IPage<${entityName}VO>} $!{businessName}分页列表
|
||||
*/
|
||||
@Override
|
||||
public IPage<${entityName}VO> get${entityName}Page(${entityName}Query queryParams) {
|
||||
Page<${entityName}VO> pageResult = this.baseMapper.get${entityName}Page(
|
||||
new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
|
||||
queryParams
|
||||
);
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取${businessName}表单数据
|
||||
*
|
||||
* @param id $!{businessName}ID
|
||||
* @return ${businessName}表单数据
|
||||
*/
|
||||
@Override
|
||||
public ${entityName}Form get${entityName}FormData(Long id) {
|
||||
${entityName} entity = this.getById(id);
|
||||
return ${entityLowerCamel}Converter.toForm(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增${businessName}
|
||||
*
|
||||
* @param formData $!{businessName}表单对象
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public boolean save${entityName}(${entityName}Form formData) {
|
||||
${entityName} entity = ${entityLowerCamel}Converter.toEntity(formData);
|
||||
return this.save(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新${businessName}
|
||||
*
|
||||
* @param id $!{businessName}ID
|
||||
* @param formData $!{businessName}表单对象
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public boolean update${entityName}(Long id,${entityName}Form formData) {
|
||||
${entityName} entity = ${entityLowerCamel}Converter.toEntity(formData);
|
||||
entity.setId(id);
|
||||
return this.updateById(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除${businessName}
|
||||
*
|
||||
* @param ids $!{businessName}ID,多个以英文逗号(,)分割
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public boolean delete${entityName}s(String ids) {
|
||||
Assert.isTrue(StrUtil.isNotBlank(ids), "删除的${businessName}数据为空");
|
||||
// 逻辑删除
|
||||
List<Long> idList = Arrays.stream(ids.split(","))
|
||||
.map(Long::parseLong)
|
||||
.toList();
|
||||
return this.removeByIds(idList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package ${packageName}.${moduleName}.${subpackageName};
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
#if(${hasLocalDateTime})
|
||||
import java.time.LocalDateTime;
|
||||
#end
|
||||
#if(${hasBigDecimal})
|
||||
import java.math.BigDecimal;
|
||||
#end
|
||||
|
||||
/**
|
||||
* $!{businessName}视图对象
|
||||
*
|
||||
* @author ${author}
|
||||
* @since ${date}
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema( description = "$!{businessName}视图对象")
|
||||
public class ${entityName}VO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
#if($fieldConfigs)
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInList)
|
||||
#if("$!fieldConfig.fieldComment" != "")
|
||||
@Schema(description = "${fieldConfig.fieldComment}")
|
||||
#end
|
||||
private ${fieldConfig.fieldType} ${fieldConfig.fieldName};
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import request from "@/utils/request";
|
||||
|
||||
const ${entityUpperSnake}_BASE_URL = "/api/v1/${entityKebab}";
|
||||
|
||||
const ${entityName}API = {
|
||||
/** 获取${businessName}分页数据 */
|
||||
getPage(queryParams) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取${businessName}表单数据
|
||||
* @param {string} id ${businessName}ID
|
||||
*/
|
||||
getFormData(id) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}/${id}/form`,
|
||||
method: "get",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增${businessName}
|
||||
* @param {Object} data ${businessName}表单数据
|
||||
*/
|
||||
create(data) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}`,
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新${businessName}
|
||||
* @param {string} id ${businessName}ID
|
||||
* @param {Object} data ${businessName}表单数据
|
||||
*/
|
||||
update(id, data) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}/${id}`,
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量删除${businessName},多个以英文逗号分割
|
||||
* @param {string} ids ${businessName}ID字符串
|
||||
*/
|
||||
deleteByIds(ids) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default ${entityName}API;
|
||||
@@ -0,0 +1,369 @@
|
||||
<template>
|
||||
<div class="page-container h-full flex flex-1 flex-col">
|
||||
<!-- 搜索 -->
|
||||
<PageSearch
|
||||
ref="searchRef"
|
||||
:search-config="searchConfig"
|
||||
@query-click="handleQueryClick"
|
||||
@reset-click="handleResetClick"
|
||||
>
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1 && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<Dict v-model="scope.formData[scope.prop]" code="$fieldConfig.dictType" v-bind="scope.attrs" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageSearch>
|
||||
|
||||
<!-- 列表 -->
|
||||
<PageContent
|
||||
ref="contentRef"
|
||||
:content-config="contentConfig"
|
||||
@add-click="handleAddClick"
|
||||
@export-click="handleExportClick"
|
||||
@search-click="handleSearchClick"
|
||||
@toolbar-click="handleToolbarClick"
|
||||
@operate-click="handleOperateClick"
|
||||
@filter-change="handleFilterChange"
|
||||
>
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInList == 1 && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<DictLabel v-model="scope.row[scope.prop]" code="$fieldConfig.dictType" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageContent>
|
||||
|
||||
<!-- 新增 -->
|
||||
<PageModal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN" && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<Dict v-model="scope.formData[scope.prop]" code="$fieldConfig.dictType" v-bind="scope.attrs" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageModal>
|
||||
|
||||
<!-- 编辑 -->
|
||||
<PageModal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN" && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<Dict v-model="scope.formData[scope.prop]" code="$fieldConfig.dictType" v-bind="scope.attrs" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineOptions({ name: "$entityName" });
|
||||
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
import usePage from "@/components/CURD/usePage";
|
||||
|
||||
// 组合式 CRUD
|
||||
const {
|
||||
searchRef,
|
||||
contentRef,
|
||||
addModalRef,
|
||||
editModalRef,
|
||||
handleQueryClick,
|
||||
handleResetClick,
|
||||
handleAddClick,
|
||||
handleEditClick,
|
||||
handleSubmitClick,
|
||||
handleExportClick,
|
||||
handleSearchClick,
|
||||
handleFilterChange,
|
||||
} = usePage();
|
||||
|
||||
// 搜索配置
|
||||
const searchConfig = reactive({
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
formItems: [
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1)
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
{
|
||||
type: "custom",
|
||||
slotName: "$fieldConfig.fieldName",
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
clearable: true,
|
||||
style: { width: "200px" },
|
||||
},
|
||||
},
|
||||
#else
|
||||
{
|
||||
#if($fieldConfig.javaType == "String")
|
||||
type: "input",
|
||||
#elseif($fieldConfig.javaType == "Integer" || $fieldConfig.javaType == "Long")
|
||||
type: "input-number",
|
||||
#elseif($fieldConfig.javaType == "Date" || $fieldConfig.javaType == "LocalDateTime")
|
||||
type: "date-picker",
|
||||
#else
|
||||
type: "input",
|
||||
#end
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
#if($fieldConfig.javaType == "Date" || $fieldConfig.javaType == "LocalDateTime")
|
||||
type: "daterange",
|
||||
"range-separator": "~",
|
||||
"start-placeholder": "开始时间",
|
||||
"end-placeholder": "截止时间",
|
||||
"value-format": "YYYY-MM-DD",
|
||||
#end
|
||||
clearable: true,
|
||||
style: { width: "200px" },
|
||||
},
|
||||
},
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
],
|
||||
});
|
||||
|
||||
// 列表配置
|
||||
const contentConfig = reactive({
|
||||
// 权限前缀
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
table: {
|
||||
border: true,
|
||||
highlightCurrentRow: true,
|
||||
},
|
||||
// 主键
|
||||
pk: "id",
|
||||
// 列表查询接口
|
||||
indexAction: ${entityName}API.getPage,
|
||||
// 删除接口
|
||||
deleteAction: ${entityName}API.deleteByIds,
|
||||
// 数据解析函数
|
||||
parseData(res) {
|
||||
return {
|
||||
total: res?.total ?? 0,
|
||||
list: res?.list ?? [],
|
||||
};
|
||||
},
|
||||
// 分页配置
|
||||
pagination: {
|
||||
background: true,
|
||||
layout: "total, sizes, prev, pager, next, jumper",
|
||||
pageSize: 20,
|
||||
pageSizes: [10, 20, 30, 50],
|
||||
},
|
||||
// 工具栏配置
|
||||
toolbar: ["add", "delete"],
|
||||
defaultToolbar: ["refresh", "filter"],
|
||||
// 表格列配置
|
||||
cols: [
|
||||
{ type: "selection", width: 55, align: "center" },
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInList == 1)
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
{
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
templet: "custom",
|
||||
slotName: "$fieldConfig.fieldName"
|
||||
},
|
||||
#else
|
||||
{ label: "$fieldConfig.fieldComment", prop: "$fieldConfig.fieldName" },
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
{
|
||||
label: "操作",
|
||||
prop: "operation",
|
||||
width: 220,
|
||||
templet: "tool",
|
||||
operat: ["edit", "delete"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 新增配置
|
||||
const addModalConfig = reactive({
|
||||
// 权限前缀
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
// 主键
|
||||
pk: "id",
|
||||
// 弹窗配置
|
||||
dialog: {
|
||||
title: "新增$!{entityComment}",
|
||||
width: 800,
|
||||
draggable: true,
|
||||
},
|
||||
form: {
|
||||
labelWidth: 100,
|
||||
},
|
||||
// 表单项配置
|
||||
formItems: [
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
{
|
||||
type: "custom",
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
slotName: "$fieldConfig.fieldName",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "change" }],
|
||||
#end
|
||||
},
|
||||
#else
|
||||
{
|
||||
#if($fieldConfig.javaType == "String")
|
||||
#if($fieldConfig.fieldName.contains("password") || $fieldConfig.fieldName.contains("pwd"))
|
||||
type: "input",
|
||||
attrs: {
|
||||
type: "password",
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
showPassword: true
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#elseif($fieldConfig.fieldName.contains("email"))
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
maxlength: 50
|
||||
},
|
||||
rules: [
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" },
|
||||
#end
|
||||
{ pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/, message: "请输入正确的邮箱地址", trigger: "blur" }
|
||||
],
|
||||
#elseif($fieldConfig.fieldName.contains("mobile") || $fieldConfig.fieldName.contains("phone"))
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
maxlength: 11
|
||||
},
|
||||
rules: [
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" },
|
||||
#end
|
||||
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }
|
||||
],
|
||||
#else
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment"
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "Integer" || $fieldConfig.javaType == "Long")
|
||||
type: "input-number",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "BigDecimal" || $fieldConfig.javaType == "Double")
|
||||
type: "input-number",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
precision: 2,
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "Date" || $fieldConfig.javaType == "LocalDateTime")
|
||||
type: "date-picker",
|
||||
attrs: {
|
||||
type: "datetime",
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
format: "YYYY-MM-DD HH:mm:ss",
|
||||
"value-format": "YYYY-MM-DD HH:mm:ss",
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "change" }],
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "Boolean" || $fieldConfig.fieldName.contains("status"))
|
||||
type: "switch",
|
||||
attrs: {
|
||||
activeText: "启用",
|
||||
inactiveText: "禁用",
|
||||
activeValue: 1,
|
||||
inactiveValue: 0
|
||||
},
|
||||
initialValue: 1,
|
||||
#else
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment"
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#end
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
},
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
],
|
||||
// 提交函数
|
||||
formAction(data) {
|
||||
if (data.id) {
|
||||
// 编辑
|
||||
return ${entityName}API.update(data.id, data);
|
||||
} else {
|
||||
// 新增
|
||||
return ${entityName}API.create(data);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 编辑配置
|
||||
const editModalConfig = reactive({
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
component: "drawer",
|
||||
drawer: {
|
||||
title: "编辑$!{entityComment}",
|
||||
size: 500,
|
||||
},
|
||||
pk: "id",
|
||||
formAction(data) {
|
||||
return ${entityName}API.update(data.id, data);
|
||||
},
|
||||
formItems: addModalConfig.formItems, // 复用新增的表单项
|
||||
});
|
||||
|
||||
// 处理操作按钮点击
|
||||
const handleOperateClick = (data) => {
|
||||
if (data.name === "edit") {
|
||||
handleEditClick(data.row, async () => {
|
||||
return await ${entityName}API.getFormData(String(data.row.id));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 处理工具栏按钮点击(删除等)
|
||||
const handleToolbarClick = (name) => {
|
||||
console.log(name);
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,431 @@
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<el-card class="page-search" shadow="never">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="auto">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1)
|
||||
<el-form-item label="$fieldConfig.fieldComment" prop="$fieldConfig.fieldName">
|
||||
#if($fieldConfig.formType.name() == "INPUT")
|
||||
<el-input
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
clearable
|
||||
@keyup.enter="handleQuery()"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SELECT")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="queryParams.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-select v-model="queryParams.$fieldConfig.fieldName" placeholder="请选择$fieldConfig.fieldComment">
|
||||
<el-option :key="1" :value="1" label="选项一"/>
|
||||
<el-option :key="2" :value="2" label="选项二"/>
|
||||
</el-select>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "RADIO")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="queryParams.$fieldConfig.fieldName" type="radio" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-radio-group v-model="queryParams.$fieldConfig.fieldName">
|
||||
<el-radio :key="1" :value="1">选项一</el-radio>
|
||||
<el-radio :key="2" :value="2">选项二</el-radio>
|
||||
</el-radio-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "CHECK_BOX")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="queryParams.$fieldConfig.fieldName" type="checkbox" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-checkbox-group v-model="queryParams.$fieldConfig.fieldName">
|
||||
<el-checkbox :key="1" :label="1">选项一</el-checkbox>
|
||||
<el-checkbox :key="2" :label="2">选项二</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "INPUT_NUMBER")
|
||||
<el-input-number
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SWITCH")
|
||||
<el-switch
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "TEXT_AREA")
|
||||
<el-input type="textarea"
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE_TIME")
|
||||
<el-date-picker
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
#if($fieldConfig.queryType.name() == "BETWEEN")
|
||||
type="daterange"
|
||||
range-separator="~"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
#else
|
||||
type="datetime"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
#end
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE")
|
||||
<el-date-picker
|
||||
class="!w-[240px]"
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
#if($fieldConfig.queryType.name() == "BETWEEN")
|
||||
type="daterange"
|
||||
range-separator="~"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
#else
|
||||
type="date"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
#end
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
#end
|
||||
</el-form-item>
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="refresh" @click="handleResetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="page-content" shadow="never">
|
||||
<div class="page-toolbar">
|
||||
<div class="page-toolbar__left">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:create']"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleCreateClick()"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
:disabled="!hasSelection"
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
>删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="dataTableRef"
|
||||
v-loading="loading"
|
||||
:data="pageData"
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInList == 1)
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<el-table-column label="$fieldConfig.fieldComment" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<DictTag v-model="scope.row.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
#else
|
||||
<el-table-column
|
||||
key="$fieldConfig.fieldName"
|
||||
label="$fieldConfig.fieldComment"
|
||||
prop="$fieldConfig.fieldName"
|
||||
min-width="150"
|
||||
align="center"
|
||||
/>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<el-table-column fixed="right" label="操作" width="220">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:update']"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
icon="edit"
|
||||
@click="handleEditClick(String(scope.row.id))"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
icon="delete"
|
||||
@click="handleDelete(String(scope.row.id))"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="page-pagination">
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="fetchList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- $!{businessName}表单弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
@close="closeDialog"
|
||||
>
|
||||
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN")
|
||||
<el-form-item label="$fieldConfig.fieldComment" prop="$fieldConfig.fieldName">
|
||||
#if($fieldConfig.formType.name() == "INPUT")
|
||||
<el-input
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SELECT")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-select v-model="formData.$fieldConfig.fieldName" placeholder="请选择$fieldConfig.fieldComment">
|
||||
<el-option :value="0" label="选项一"/>
|
||||
<el-option :value="1" label="选项二"/>
|
||||
</el-select>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "RADIO")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" type="radio" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-radio-group v-model="formData.$fieldConfig.fieldName">
|
||||
<el-radio :value="0">选项一</el-radio>
|
||||
<el-radio :value="1">选项二</el-radio>
|
||||
</el-radio-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "CHECK_BOX")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" type="checkbox" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-checkbox-group v-model="formData.$fieldConfig.fieldName">
|
||||
<el-checkbox :value="0">选项一</el-checkbox>
|
||||
<el-checkbox :value="1">选项二</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "INPUT_NUMBER")
|
||||
<el-input-number
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SWITCH")
|
||||
<el-switch
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "TEXT_AREA")
|
||||
<el-input type="textarea"
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE_TIME")
|
||||
<el-date-picker
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
type="datetime"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE")
|
||||
<el-date-picker
|
||||
class="!w-[240px]"
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
type="date"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
#end
|
||||
</el-form-item>
|
||||
|
||||
#elseif($fieldConfig.formType.name() == "HIDDEN")
|
||||
<el-input type="hidden" v-model="formData.$fieldConfig.fieldName" />
|
||||
#end
|
||||
#end
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit()">确定</el-button>
|
||||
<el-button @click="closeDialog()">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useTableSelection } from "@/composables";
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
|
||||
defineOptions({
|
||||
name: "${entityName}",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const queryFormRef = ref();
|
||||
const dataFormRef = ref();
|
||||
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
// $!{businessName}表格数据
|
||||
const pageData = ref([]);
|
||||
|
||||
const dialog = reactive({
|
||||
title: "",
|
||||
visible: false,
|
||||
});
|
||||
|
||||
// $!{businessName}表单数据
|
||||
const formData = reactive({});
|
||||
|
||||
// $!{businessName}表单校验规则
|
||||
const rules = reactive({
|
||||
#if($fieldConfigs)
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInForm && $fieldConfig.isRequired)
|
||||
${fieldConfig.fieldName}: [{ required: true, message: "请输入${fieldConfig.fieldComment}", trigger: "blur" }],
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
});
|
||||
|
||||
const { selectedIds, hasSelection, handleSelectionChange } = useTableSelection();
|
||||
|
||||
async function fetchList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await ${entityName}API.getPage(queryParams);
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询$!{businessName} */
|
||||
function handleQuery() {
|
||||
queryParams.pageNum = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
/** 重置$!{businessName}查询 */
|
||||
function handleResetQuery() {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
function openDialog() {
|
||||
dialog.visible = true;
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
formData.id = undefined;
|
||||
}
|
||||
|
||||
async function handleCreateClick() {
|
||||
dialog.title = "新增$!{businessName}";
|
||||
openDialog();
|
||||
}
|
||||
|
||||
async function handleEditClick(id) {
|
||||
dialog.title = "修改$!{businessName}";
|
||||
${entityName}API.getFormData(id).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
openDialog();
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交$!{businessName}表单 */
|
||||
function handleSubmit() {
|
||||
dataFormRef.value?.validate((valid) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
const id = formData.id;
|
||||
if (id) {
|
||||
${entityName}API.update(id, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
${entityName}API.create(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除$!{businessName} */
|
||||
function handleDelete(id) {
|
||||
const ids = [id || selectedIds.value].join(",");
|
||||
if (!ids) {
|
||||
ElMessage.warning("请勾选删除项");
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(
|
||||
() => {
|
||||
loading.value = true;
|
||||
${entityName}API.deleteByIds(ids)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
},
|
||||
() => {
|
||||
ElMessage.info("已取消删除");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* ${entityName} $!{businessName}类型定义
|
||||
*/
|
||||
|
||||
import type { BaseQueryParams } from "@/api/common";
|
||||
|
||||
/** $!{businessName}查询参数 */
|
||||
export interface ${entityName}QueryParams extends BaseQueryParams {
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery)
|
||||
#if("$!fieldConfig.fieldComment" != "")
|
||||
/** ${fieldConfig.fieldComment} */
|
||||
#end
|
||||
#if($fieldConfig.formType.name() == "DATE" || $fieldConfig.formType.name() == "DATE_TIME")
|
||||
#if($fieldConfig.queryType.name() == "BETWEEN")
|
||||
${fieldConfig.fieldName}?: [string, string];
|
||||
#else
|
||||
${fieldConfig.fieldName}?: ${fieldConfig.tsType};
|
||||
#end
|
||||
#else
|
||||
${fieldConfig.fieldName}?: ${fieldConfig.tsType};
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
/** $!{businessName}表单对象 */
|
||||
export interface ${entityName}Form {
|
||||
id?: number;
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm)
|
||||
#if("$!fieldConfig.fieldComment" != "")
|
||||
/** ${fieldConfig.fieldComment} */
|
||||
#end
|
||||
${fieldConfig.fieldName}?: ${fieldConfig.tsType};
|
||||
#end
|
||||
#end
|
||||
}
|
||||
|
||||
/** $!{businessName}详情对象 */
|
||||
export type ${entityName}Detail = ${entityName}Item & ${entityName}Form;
|
||||
|
||||
/** $!{businessName}列表项 */
|
||||
export interface ${entityName}Item {
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInList)
|
||||
#if("$!fieldConfig.fieldComment" != "")
|
||||
/** ${fieldConfig.fieldComment} */
|
||||
#end
|
||||
${fieldConfig.fieldName}?: ${fieldConfig.tsType};
|
||||
#end
|
||||
#end
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import request from "@/utils/request";
|
||||
import type { ${entityName}Form, ${entityName}QueryParams, ${entityName}Item } from "./types";
|
||||
import type { PageResult } from "@/api/common";
|
||||
|
||||
const ${entityUpperSnake}_BASE_URL = "/api/v1/${entityKebab}";
|
||||
|
||||
const ${entityName}API = {
|
||||
/** 获取${businessName}分页数据 */
|
||||
getPage(queryParams?: ${entityName}QueryParams) {
|
||||
return request<any, PageResult<${entityName}Item>>({
|
||||
url: `${${entityUpperSnake}_BASE_URL}`,
|
||||
method: "get",
|
||||
params: queryParams,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取${businessName}表单数据
|
||||
*
|
||||
* @param id ${businessName}ID
|
||||
* @returns ${businessName}表单数据
|
||||
*/
|
||||
getFormData(id: string) {
|
||||
return request<any, ${entityName}Form>({
|
||||
url: `${${entityUpperSnake}_BASE_URL}/${id}/form`,
|
||||
method: "get",
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 添加${businessName}
|
||||
*
|
||||
* @param data ${businessName}表单数据
|
||||
*/
|
||||
create(data: ${entityName}Form) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}`,
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新${businessName}
|
||||
*
|
||||
* @param id ${businessName}ID
|
||||
* @param data ${businessName}表单数据
|
||||
*/
|
||||
update(id: string, data: ${entityName}Form) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}/${id}`,
|
||||
method: "put",
|
||||
data,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量删除${businessName},多个以英文逗号(,)分割
|
||||
*
|
||||
* @param ids ${businessName}ID字符串,多个以英文逗号(,)分割
|
||||
*/
|
||||
deleteByIds(ids: string) {
|
||||
return request({
|
||||
url: `${${entityUpperSnake}_BASE_URL}/${ids}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ${entityName}API;
|
||||
|
||||
// 重导出类型
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<div class="page-container h-full flex flex-1 flex-col">
|
||||
<!-- 搜索 -->
|
||||
<PageSearch
|
||||
ref="searchRef"
|
||||
:search-config="searchConfig"
|
||||
@query-click="handleQueryClick"
|
||||
@reset-click="handleResetClick"
|
||||
>
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1 && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<DictSelect v-model="scope.formData[scope.prop]" code="$fieldConfig.dictType" v-bind="scope.attrs" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageSearch>
|
||||
|
||||
<!-- 列表 -->
|
||||
<PageContent
|
||||
ref="contentRef"
|
||||
:content-config="contentConfig"
|
||||
@add-click="handleAddClick"
|
||||
@export-click="handleExportClick"
|
||||
@search-click="handleSearchClick"
|
||||
@toolbar-click="handleToolbarClick"
|
||||
@operate-click="handleOperateClick"
|
||||
@filter-change="handleFilterChange"
|
||||
>
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInList == 1 && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<DictTag v-model="scope.row[scope.prop]" code="$fieldConfig.dictType" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageContent>
|
||||
|
||||
<!-- 新增 -->
|
||||
<PageModal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN" && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<DictSelect v-model="scope.formData[scope.prop]" code="$fieldConfig.dictType" v-bind="scope.attrs" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageModal>
|
||||
|
||||
<!-- 编辑 -->
|
||||
<PageModal ref="editModalRef" :modal-config="editModalConfig" @submit-click="handleSubmitClick">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN" && $fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<template #$fieldConfig.fieldName="scope">
|
||||
<DictSelect v-model="scope.formData[scope.prop]" code="$fieldConfig.dictType" v-bind="scope.attrs" />
|
||||
</template>
|
||||
#end
|
||||
#end
|
||||
</PageModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: "$entityName" });
|
||||
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
import type { ${entityName}Form, ${entityName}QueryParams } from "@/api/${moduleName}/${entityKebab}";
|
||||
import type { IObject, IModalConfig, IContentConfig, ISearchConfig } from "@/components/CURD/types";
|
||||
import usePage from "@/components/CURD/usePage";
|
||||
|
||||
// 组合式 CRUD
|
||||
const {
|
||||
searchRef,
|
||||
contentRef,
|
||||
addModalRef,
|
||||
editModalRef,
|
||||
handleQueryClick,
|
||||
handleResetClick,
|
||||
handleAddClick,
|
||||
handleEditClick,
|
||||
handleSubmitClick,
|
||||
handleExportClick,
|
||||
handleSearchClick,
|
||||
handleFilterChange,
|
||||
} = usePage();
|
||||
|
||||
// 搜索配置
|
||||
const searchConfig: ISearchConfig = reactive({
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
formItems: [
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1)
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
{
|
||||
type: "custom",
|
||||
slotName: "$fieldConfig.fieldName",
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
clearable: true,
|
||||
style: { width: "200px" },
|
||||
},
|
||||
},
|
||||
#else
|
||||
{
|
||||
#if($fieldConfig.javaType == "String")
|
||||
type: "input",
|
||||
#elseif($fieldConfig.javaType == "Integer" || $fieldConfig.javaType == "Long")
|
||||
type: "input-number",
|
||||
#elseif($fieldConfig.javaType == "Date" || $fieldConfig.javaType == "LocalDateTime")
|
||||
type: "date-picker",
|
||||
#else
|
||||
type: "input",
|
||||
#end
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
#if($fieldConfig.javaType == "Date" || $fieldConfig.javaType == "LocalDateTime")
|
||||
type: "daterange",
|
||||
"range-separator": "~",
|
||||
"start-placeholder": "开始时间",
|
||||
"end-placeholder": "截止时间",
|
||||
"value-format": "YYYY-MM-DD",
|
||||
#end
|
||||
clearable: true,
|
||||
style: { width: "200px" },
|
||||
},
|
||||
},
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
],
|
||||
});
|
||||
|
||||
// 列表配置
|
||||
const contentConfig: IContentConfig<${entityName}QueryParams> = reactive({
|
||||
// 权限前缀
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
table: {
|
||||
border: true,
|
||||
highlightCurrentRow: true,
|
||||
},
|
||||
// 主键
|
||||
pk: "id",
|
||||
// 列表查询接口
|
||||
indexAction: ${entityName}API.getPage,
|
||||
// 删除接口
|
||||
deleteAction: ${entityName}API.deleteByIds,
|
||||
// 数据解析函数
|
||||
parseData(res: any) {
|
||||
return {
|
||||
total: res?.total ?? 0,
|
||||
list: res?.list ?? [],
|
||||
};
|
||||
},
|
||||
// 分页配置
|
||||
pagination: {
|
||||
background: true,
|
||||
layout: "total, sizes, prev, pager, next, jumper",
|
||||
pageSize: 20,
|
||||
pageSizes: [10, 20, 30, 50],
|
||||
},
|
||||
// 工具栏配置
|
||||
toolbar: ["add", "delete"],
|
||||
defaultToolbar: ["refresh", "filter"],
|
||||
// 表格列配置
|
||||
cols: [
|
||||
{ type: "selection", width: 55, align: "center" },
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInList == 1)
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
{
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
templet: "custom",
|
||||
slotName: "$fieldConfig.fieldName"
|
||||
},
|
||||
#else
|
||||
{ label: "$fieldConfig.fieldComment", prop: "$fieldConfig.fieldName" },
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
{
|
||||
label: "操作",
|
||||
prop: "operation",
|
||||
width: 220,
|
||||
templet: "tool",
|
||||
operat: ["edit", "delete"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 新增配置
|
||||
const addModalConfig: IModalConfig<${entityName}Form> = reactive({
|
||||
// 权限前缀
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
// 主键
|
||||
pk: "id",
|
||||
// 弹窗配置
|
||||
dialog: {
|
||||
title: "新增$!{entityComment}",
|
||||
width: 800,
|
||||
draggable: true,
|
||||
},
|
||||
form: {
|
||||
labelWidth: 100,
|
||||
},
|
||||
// 表单项配置
|
||||
formItems: [
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
{
|
||||
type: "custom",
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
slotName: "$fieldConfig.fieldName",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "change" }],
|
||||
#end
|
||||
},
|
||||
#else
|
||||
{
|
||||
#if($fieldConfig.javaType == "String")
|
||||
#if($fieldConfig.fieldName.contains("password") || $fieldConfig.fieldName.contains("pwd"))
|
||||
type: "input",
|
||||
attrs: {
|
||||
type: "password",
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
showPassword: true
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#elseif($fieldConfig.fieldName.contains("email"))
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
maxlength: 50
|
||||
},
|
||||
rules: [
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" },
|
||||
#end
|
||||
{ pattern: /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/, message: "请输入正确的邮箱地址", trigger: "blur" }
|
||||
],
|
||||
#elseif($fieldConfig.fieldName.contains("mobile") || $fieldConfig.fieldName.contains("phone"))
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
maxlength: 11
|
||||
},
|
||||
rules: [
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" },
|
||||
#end
|
||||
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }
|
||||
],
|
||||
#else
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment"
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "Integer" || $fieldConfig.javaType == "Long")
|
||||
type: "input-number",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "BigDecimal" || $fieldConfig.javaType == "Double")
|
||||
type: "input-number",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
precision: 2,
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "Date" || $fieldConfig.javaType == "LocalDateTime")
|
||||
type: "date-picker",
|
||||
attrs: {
|
||||
type: "datetime",
|
||||
placeholder: "$fieldConfig.fieldComment",
|
||||
format: "YYYY-MM-DD HH:mm:ss",
|
||||
"value-format": "YYYY-MM-DD HH:mm:ss",
|
||||
style: { width: "100%" }
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "change" }],
|
||||
#end
|
||||
#elseif($fieldConfig.javaType == "Boolean" || $fieldConfig.fieldName.contains("status"))
|
||||
type: "switch",
|
||||
attrs: {
|
||||
activeText: "启用",
|
||||
inactiveText: "禁用",
|
||||
activeValue: 1,
|
||||
inactiveValue: 0
|
||||
},
|
||||
initialValue: 1,
|
||||
#else
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "$fieldConfig.fieldComment"
|
||||
},
|
||||
#if($fieldConfig.isRequired == 1)
|
||||
rules: [{ required: true, message: "$fieldConfig.fieldComment不能为空", trigger: "blur" }],
|
||||
#end
|
||||
#end
|
||||
label: "$fieldConfig.fieldComment",
|
||||
prop: "$fieldConfig.fieldName",
|
||||
},
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
],
|
||||
// 提交函数
|
||||
formAction: (data: ${entityName}Form) => {
|
||||
if (data.id) {
|
||||
// 编辑
|
||||
return ${entityName}API.update(data.id as string, data);
|
||||
} else {
|
||||
// 新增
|
||||
return ${entityName}API.create(data);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 编辑配置
|
||||
const editModalConfig: IModalConfig<${entityName}Form> = reactive({
|
||||
permPrefix: "${moduleName}:${entityKebab}",
|
||||
component: "drawer",
|
||||
drawer: {
|
||||
title: "编辑$!{entityComment}",
|
||||
size: 500,
|
||||
},
|
||||
pk: "id",
|
||||
formAction(data: any) {
|
||||
return ${entityName}API.update(data.id as string, data);
|
||||
},
|
||||
formItems: addModalConfig.formItems, // 复用新增的表单项
|
||||
});
|
||||
|
||||
// 处理操作按钮点击
|
||||
const handleOperateClick = (data: IObject) => {
|
||||
if (data.name === "edit") {
|
||||
handleEditClick(data.row, async () => {
|
||||
return await ${entityName}API.getFormData(String(data.row.id));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 处理工具栏按钮点击(删除等)
|
||||
const handleToolbarClick = (name: string) => {
|
||||
console.log(name);
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,433 @@
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<el-card class="page-search" shadow="never">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="auto">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInQuery == 1)
|
||||
<el-form-item label="$fieldConfig.fieldComment" prop="$fieldConfig.fieldName">
|
||||
#if($fieldConfig.formType.name() == "INPUT")
|
||||
<el-input
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
clearable
|
||||
@keyup.enter="handleQuery()"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SELECT")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="queryParams.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-select v-model="queryParams.$fieldConfig.fieldName" placeholder="请选择$fieldConfig.fieldComment">
|
||||
<el-option :key="1" :value="1" label="选项一"/>
|
||||
<el-option :key="2" :value="2" label="选项二"/>
|
||||
</el-select>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "RADIO")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="queryParams.$fieldConfig.fieldName" type="radio" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-radio-group v-model="queryParams.$fieldConfig.fieldName">
|
||||
<el-radio :key="1" :value="1">选项一</el-radio>
|
||||
<el-radio :key="2" :value="2">选项二</el-radio>
|
||||
</el-radio-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "CHECK_BOX")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="queryParams.$fieldConfig.fieldName" type="checkbox" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-checkbox-group v-model="queryParams.$fieldConfig.fieldName">
|
||||
<el-checkbox :key="1" :label="1">选项一</el-checkbox>
|
||||
<el-checkbox :key="2" :label="2">选项二</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "INPUT_NUMBER")
|
||||
<el-input-number
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SWITCH")
|
||||
<el-switch
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "TEXT_AREA")
|
||||
<el-input type="textarea"
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE_TIME")
|
||||
<el-date-picker
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
#if($fieldConfig.queryType.name() == "BETWEEN")
|
||||
type="daterange"
|
||||
range-separator="~"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
#else
|
||||
type="datetime"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
#end
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE")
|
||||
<el-date-picker
|
||||
class="!w-[240px]"
|
||||
v-model="queryParams.$fieldConfig.fieldName"
|
||||
#if($fieldConfig.queryType.name() == "BETWEEN")
|
||||
type="daterange"
|
||||
range-separator="~"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
#else
|
||||
type="date"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
#end
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
#end
|
||||
</el-form-item>
|
||||
#end
|
||||
#end
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="refresh" @click="handleResetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card class="page-content" shadow="never">
|
||||
<div class="page-toolbar">
|
||||
<div class="page-toolbar__left">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:create']"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleCreateClick()"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
:disabled="!hasSelection"
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
>删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="dataTableRef"
|
||||
v-loading="loading"
|
||||
:data="pageData"
|
||||
border
|
||||
stripe
|
||||
highlight-current-row
|
||||
row-key="id"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInList == 1)
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<el-table-column label="$fieldConfig.fieldComment" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<DictTag v-model="scope.row.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
#else
|
||||
<el-table-column
|
||||
key="$fieldConfig.fieldName"
|
||||
label="$fieldConfig.fieldComment"
|
||||
prop="$fieldConfig.fieldName"
|
||||
min-width="150"
|
||||
align="center"
|
||||
/>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
<el-table-column fixed="right" label="操作" width="220">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:update']"
|
||||
type="primary"
|
||||
size="small"
|
||||
link
|
||||
icon="edit"
|
||||
@click="handleEditClick(String(scope.row.id))"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPerm="['${moduleName}:${entityKebab}:delete']"
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
icon="delete"
|
||||
@click="handleDelete(String(scope.row.id))"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="page-pagination">
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="fetchList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- $!{businessName}表单弹窗 -->
|
||||
<el-dialog
|
||||
v-model="dialog.visible"
|
||||
:title="dialog.title"
|
||||
width="500px"
|
||||
@close="closeDialog"
|
||||
>
|
||||
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
|
||||
#foreach($fieldConfig in $fieldConfigs)
|
||||
#if($fieldConfig.isShowInForm == 1 && $fieldConfig.formType.name() != "HIDDEN")
|
||||
<el-form-item label="$fieldConfig.fieldComment" prop="$fieldConfig.fieldName">
|
||||
#if($fieldConfig.formType.name() == "INPUT")
|
||||
<el-input
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SELECT")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-select v-model="formData.$fieldConfig.fieldName" placeholder="请选择$fieldConfig.fieldComment">
|
||||
<el-option :value="0" label="选项一"/>
|
||||
<el-option :value="1" label="选项二"/>
|
||||
</el-select>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "RADIO")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" type="radio" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-radio-group v-model="formData.$fieldConfig.fieldName">
|
||||
<el-radio :value="0">选项一</el-radio>
|
||||
<el-radio :value="1">选项二</el-radio>
|
||||
</el-radio-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "CHECK_BOX")
|
||||
#if($fieldConfig.dictType && $fieldConfig.dictType.trim() != "")
|
||||
<DictSelect v-model="formData.$fieldConfig.fieldName" type="checkbox" code="$fieldConfig.dictType" />
|
||||
#else
|
||||
<el-checkbox-group v-model="formData.$fieldConfig.fieldName">
|
||||
<el-checkbox :value="0">选项一</el-checkbox>
|
||||
<el-checkbox :value="1">选项二</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
#end
|
||||
#elseif($fieldConfig.formType.name() == "INPUT_NUMBER")
|
||||
<el-input-number
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "SWITCH")
|
||||
<el-switch
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "TEXT_AREA")
|
||||
<el-input type="textarea"
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE_TIME")
|
||||
<el-date-picker
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
type="datetime"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
#elseif($fieldConfig.formType.name() == "DATE")
|
||||
<el-date-picker
|
||||
class="!w-[240px]"
|
||||
v-model="formData.$fieldConfig.fieldName"
|
||||
type="date"
|
||||
placeholder="$fieldConfig.fieldComment"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
#end
|
||||
</el-form-item>
|
||||
|
||||
#elseif($fieldConfig.formType.name() == "HIDDEN")
|
||||
<el-input type="hidden" v-model="formData.$fieldConfig.fieldName" />
|
||||
#end
|
||||
#end
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="handleSubmit()">确定</el-button>
|
||||
<el-button @click="closeDialog()">取消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from "element-plus";
|
||||
import { useTableSelection } from "@/composables";
|
||||
import ${entityName}API from "@/api/${moduleName}/${entityKebab}";
|
||||
import type { ${entityName}Item, ${entityName}Form, ${entityName}QueryParams } from "@/api/${moduleName}/${entityKebab}";
|
||||
|
||||
defineOptions({
|
||||
name: "${entityName}",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
const queryFormRef = ref<FormInstance>();
|
||||
const dataFormRef = ref<FormInstance>();
|
||||
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
|
||||
const queryParams = reactive<${entityName}QueryParams>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
} as ${entityName}QueryParams);
|
||||
|
||||
// $!{businessName}表格数据
|
||||
const pageData = ref<${entityName}Item[]>([]);
|
||||
|
||||
// 弹窗
|
||||
const dialog = reactive({
|
||||
title: "",
|
||||
visible: false,
|
||||
});
|
||||
|
||||
// $!{businessName}表单数据
|
||||
const formData = reactive<${entityName}Form>({} as ${entityName}Form);
|
||||
|
||||
// $!{businessName}表单校验规则
|
||||
const rules: FormRules = {
|
||||
#if($fieldConfigs)
|
||||
#foreach($fieldConfig in ${fieldConfigs})
|
||||
#if($fieldConfig.isShowInForm && $fieldConfig.isRequired)
|
||||
${fieldConfig.fieldName}: [{ required: true, message: "请输入${fieldConfig.fieldComment}", trigger: "blur" }],
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
};
|
||||
|
||||
const { selectedIds, hasSelection, handleSelectionChange } = useTableSelection<${entityName}Item>();
|
||||
|
||||
async function fetchList(): Promise<void> {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data = await ${entityName}API.getPage(queryParams);
|
||||
pageData.value = data.list;
|
||||
total.value = data.total ?? 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询$!{businessName} */
|
||||
function handleQuery(): void {
|
||||
queryParams.pageNum = 1;
|
||||
fetchList();
|
||||
}
|
||||
|
||||
/** 重置$!{businessName}查询 */
|
||||
function handleResetQuery() {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
function openDialog(): void {
|
||||
dialog.visible = true;
|
||||
}
|
||||
|
||||
function closeDialog(): void {
|
||||
dialog.visible = false;
|
||||
resetForm();
|
||||
}
|
||||
|
||||
function resetForm(): void {
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
formData.id = undefined;
|
||||
}
|
||||
|
||||
async function handleCreateClick(): Promise<void> {
|
||||
dialog.title = "新增$!{businessName}";
|
||||
openDialog();
|
||||
}
|
||||
|
||||
async function handleEditClick(id: string): Promise<void> {
|
||||
dialog.title = "修改$!{businessName}";
|
||||
${entityName}API.getFormData(id).then((data) => {
|
||||
Object.assign(formData, data);
|
||||
openDialog();
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交$!{businessName}表单 */
|
||||
function handleSubmit() {
|
||||
dataFormRef.value?.validate((valid: any) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
const id = formData.id;
|
||||
if (id) {
|
||||
${entityName}API.update(id, formData)
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
${entityName}API.create(formData)
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
closeDialog();
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除$!{businessName} */
|
||||
function handleDelete(id?: string) {
|
||||
const ids = [id || selectedIds.value].join(",");
|
||||
if (!ids) {
|
||||
ElMessage.warning("请勾选删除项");
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(
|
||||
() => {
|
||||
loading.value = true;
|
||||
${entityName}API.deleteByIds(ids)
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
handleResetQuery();
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
},
|
||||
() => {
|
||||
ElMessage.info("已取消删除");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery();
|
||||
});
|
||||
</script>
|
||||
BIN
youlai-codegen/src/main/resources/templates/excel/用户导入模板.xlsx
Normal file
BIN
youlai-codegen/src/main/resources/templates/excel/用户导入模板.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user