init
This commit is contained in:
90
ruoyi-modules/ruoyi-gen/pom.xml
Normal file
90
ruoyi-modules/ruoyi-gen/pom.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?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">
|
||||
<parent>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-gen</artifactId>
|
||||
|
||||
<description>
|
||||
generator 代码生成
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 通用工具-->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 接口模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- web服务 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志记录 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- FreeMarker代码生成模板 -->
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- AnyLine Spring JDBC 环境 -->
|
||||
<dependency>
|
||||
<groupId>org.anyline</groupId>
|
||||
<artifactId>anyline-environment-spring-data-jdbc</artifactId>
|
||||
<version>${anyline.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- AnyLine MySQL 驱动 -->
|
||||
<dependency>
|
||||
<groupId>org.anyline</groupId>
|
||||
<artifactId>anyline-data-jdbc-mysql</artifactId>
|
||||
<version>${anyline.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- anyline支持100+种类型数据库 添加对应的jdbc依赖与anyline对应数据库依赖包即可 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.anyline</groupId>-->
|
||||
<!-- <artifactId>anyline-data-jdbc-oracle</artifactId>-->
|
||||
<!-- <version>${anyline.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.anyline</groupId>-->
|
||||
<!-- <artifactId>anyline-data-jdbc-postgresql</artifactId>-->
|
||||
<!-- <version>${anyline.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.anyline</groupId>-->
|
||||
<!-- <artifactId>anyline-data-jdbc-mssql</artifactId>-->
|
||||
<!-- <version>${anyline.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.dromara.gen.config;
|
||||
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.anyline.data.datasource.DataSourceMonitor;
|
||||
import org.anyline.data.runtime.DataRuntime;
|
||||
import org.anyline.util.ConfigTable;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* anyline 适配 动态数据源改造
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MyBatisDataSourceMonitor implements DataSourceMonitor {
|
||||
|
||||
/**
|
||||
* 初始化 anyline 与动态数据源联动所需的元数据解析策略。
|
||||
*/
|
||||
public MyBatisDataSourceMonitor() {
|
||||
// 调整执行模式为自定义
|
||||
ConfigTable.KEEP_ADAPTER = 2;
|
||||
// 禁用缓存
|
||||
ConfigTable.METADATA_CACHE_SCOPE = 0;
|
||||
}
|
||||
|
||||
private final Map<String, String> features = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 数据源特征 用来定准 adapter 包含数据库或JDBC协议关键字<br/>
|
||||
* 一般会通过 产品名_url 合成 如果返回null 上层方法会通过driver_产品名_url合成
|
||||
*
|
||||
* @param datasource 数据源
|
||||
* @return String 返回null由上层自动提取
|
||||
*/
|
||||
@Override
|
||||
public String feature(DataRuntime runtime, Object datasource) {
|
||||
String feature = null;
|
||||
if (datasource instanceof JdbcTemplate jdbc) {
|
||||
DataSource ds = jdbc.getDataSource();
|
||||
if (ds instanceof DynamicRoutingDataSource) {
|
||||
String key = DynamicDataSourceContextHolder.peek();
|
||||
feature = features.get(key);
|
||||
if (null == feature) {
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DataSourceUtils.getConnection(ds);
|
||||
DatabaseMetaData meta = con.getMetaData();
|
||||
String url = meta.getURL();
|
||||
feature = meta.getDatabaseProductName().toLowerCase().replace(" ", "") + "_" + url;
|
||||
features.put(key, feature);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
} finally {
|
||||
if (null != con && !DataSourceUtils.isConnectionTransactional(con, ds)) {
|
||||
DataSourceUtils.releaseConnection(con, ds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据源唯一标识 如果不实现则默认feature
|
||||
*
|
||||
* @param runtime 数据运行时上下文
|
||||
* @param datasource 数据源
|
||||
* @return String 返回null由上层自动提取
|
||||
*/
|
||||
@Override
|
||||
public String key(DataRuntime runtime, Object datasource) {
|
||||
if (datasource instanceof JdbcTemplate jdbc) {
|
||||
DataSource ds = jdbc.getDataSource();
|
||||
if (ds instanceof DynamicRoutingDataSource) {
|
||||
return DynamicDataSourceContextHolder.peek();
|
||||
}
|
||||
}
|
||||
return runtime.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* ConfigTable.KEEP_ADAPTER=2 : 根据当前接口判断是否保持同一个数据源绑定同一个adapter<br/>
|
||||
* DynamicRoutingDataSource类型的返回false,因为同一个DynamicRoutingDataSource可能对应多类数据库, 如果项目中只有一种数据库 应该直接返回true
|
||||
*
|
||||
* @param runtime 数据运行时上下文
|
||||
* @param datasource 数据源
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
public boolean keepAdapter(DataRuntime runtime, Object datasource) {
|
||||
if (datasource instanceof JdbcTemplate jdbc) {
|
||||
DataSource ds = jdbc.getDataSource();
|
||||
return !(ds instanceof DynamicRoutingDataSource);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.dromara.gen.config.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.common.core.factory.YmlPropertySourceFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 代码生成配置属性
|
||||
*
|
||||
* @author 秋辞未寒
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "gen")
|
||||
@PropertySource(value = "classpath:generator.yml", factory = YmlPropertySourceFactory.class)
|
||||
public class GenProperties {
|
||||
|
||||
/**
|
||||
* 作者
|
||||
*/
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* 生成包路径
|
||||
*/
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 自动去除表前缀,默认是false
|
||||
*/
|
||||
private boolean autoRemovePre = false;
|
||||
|
||||
/**
|
||||
* 表前缀(类名不会包含表前缀)
|
||||
*/
|
||||
private String tablePrefix;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
package org.dromara.gen.constant;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 代码生成通用常量
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public interface GenConstants {
|
||||
/**
|
||||
* 单表(增删改查)
|
||||
*/
|
||||
String TPL_CRUD = "crud";
|
||||
|
||||
/**
|
||||
* 树表(增删改查)
|
||||
*/
|
||||
String TPL_TREE = "tree";
|
||||
|
||||
/**
|
||||
* 树编码字段
|
||||
*/
|
||||
String TREE_CODE = "treeCode";
|
||||
|
||||
/**
|
||||
* 树父编码字段
|
||||
*/
|
||||
String TREE_PARENT_CODE = "treeParentCode";
|
||||
|
||||
/**
|
||||
* 树名称字段
|
||||
*/
|
||||
String TREE_NAME = "treeName";
|
||||
|
||||
/**
|
||||
* 上级菜单ID字段
|
||||
*/
|
||||
String PARENT_MENU_ID = "parentMenuId";
|
||||
|
||||
/**
|
||||
* 上级菜单名称字段
|
||||
*/
|
||||
String PARENT_MENU_NAME = "parentMenuName";
|
||||
|
||||
/**
|
||||
* 是否启用导出能力
|
||||
*/
|
||||
String ENABLE_EXPORT = "enableExport";
|
||||
|
||||
/**
|
||||
* 是否启用状态切换能力
|
||||
*/
|
||||
String ENABLE_STATUS = "enableStatus";
|
||||
|
||||
/**
|
||||
* 状态字段
|
||||
*/
|
||||
String STATUS_FIELD = "statusField";
|
||||
|
||||
/**
|
||||
* 是否启用组合唯一校验
|
||||
*/
|
||||
String ENABLE_UNIQUE = "enableUnique";
|
||||
|
||||
/**
|
||||
* 组合唯一字段
|
||||
*/
|
||||
String UNIQUE_FIELDS = "uniqueFields";
|
||||
|
||||
/**
|
||||
* 是否启用排序调整能力
|
||||
*/
|
||||
String ENABLE_SORT = "enableSort";
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
String SORT_FIELD = "sortField";
|
||||
|
||||
/**
|
||||
* 默认前端模板类型,对应模板根目录下的 vue 目录。
|
||||
*/
|
||||
String FRONTEND_TYPE_VUE = "vue";
|
||||
|
||||
/**
|
||||
* React 前端模板类型,对应模板根目录下的 react 目录。
|
||||
*/
|
||||
String FRONTEND_TYPE_REACT = "react";
|
||||
|
||||
/**
|
||||
* 树根节点值
|
||||
*/
|
||||
String TREE_ROOT_VALUE = "treeRootValue";
|
||||
|
||||
/**
|
||||
* 树祖级字段
|
||||
*/
|
||||
String TREE_ANCESTORS = "treeAncestors";
|
||||
|
||||
/**
|
||||
* 树排序字段
|
||||
*/
|
||||
String TREE_ORDER_FIELD = "treeOrderField";
|
||||
|
||||
/**
|
||||
* 数据库字符串类型
|
||||
*/
|
||||
String[] COLUMNTYPE_STR = {"char", "varchar", "enum", "set", "nchar", "nvarchar", "varchar2", "nvarchar2",
|
||||
"long varchar", "long nvarchar", "uniqueidentifier", "uuid", "json", "jsonb", "xml"};
|
||||
|
||||
/**
|
||||
* 数据库文本类型
|
||||
*/
|
||||
String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext", "binary", "varbinary", "blob",
|
||||
"ntext", "image", "bytea", "clob", "nclob", "long", "long raw", "raw"};
|
||||
|
||||
/**
|
||||
* 数据库时间类型
|
||||
*/
|
||||
String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp", "year", "interval",
|
||||
"smalldatetime", "datetime2", "datetimeoffset", "timestamptz", "timestamp with time zone",
|
||||
"timestamp without time zone", "time with time zone", "time without time zone"};
|
||||
|
||||
/**
|
||||
* 数据库数字类型
|
||||
*/
|
||||
String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "int2", "int4", "int8", "number", "integer",
|
||||
"bit", "boolean", "bool", "bigint", "float", "float4", "float8", "double", "decimal", "dec", "fixed",
|
||||
"numeric", "real", "double precision", "binary_float", "binary_double", "smallserial", "serial",
|
||||
"serial2", "serial4", "serial8", "bigserial", "money", "smallmoney"};
|
||||
|
||||
/**
|
||||
* BO对象 不需要添加字段
|
||||
*/
|
||||
String[] COLUMNNAME_NOT_ADD = {"del_flag", "version"};
|
||||
|
||||
/**
|
||||
* BO对象 不需要编辑字段
|
||||
*/
|
||||
String[] COLUMNNAME_NOT_EDIT = {"del_flag", "version"};
|
||||
|
||||
/**
|
||||
* VO对象 不需要返回字段
|
||||
*/
|
||||
String[] COLUMNNAME_NOT_LIST = {"create_dept", "create_by", "create_time", "del_flag", "update_by",
|
||||
"update_time", "version"};
|
||||
|
||||
/**
|
||||
* BO对象 不需要查询字段
|
||||
*/
|
||||
String[] COLUMNNAME_NOT_QUERY = {"id", "del_flag", "remark", "version"};
|
||||
|
||||
/**
|
||||
* Entity基类字段
|
||||
*/
|
||||
String[] BASE_ENTITY = {"createDept", "createBy", "createTime", "updateBy", "updateTime"};
|
||||
|
||||
/**
|
||||
* 文本框
|
||||
*/
|
||||
String HTML_INPUT = "input";
|
||||
|
||||
/**
|
||||
* 文本域
|
||||
*/
|
||||
String HTML_TEXTAREA = "textarea";
|
||||
|
||||
/**
|
||||
* 下拉框
|
||||
*/
|
||||
String HTML_SELECT = "select";
|
||||
|
||||
/**
|
||||
* 单选框
|
||||
*/
|
||||
String HTML_RADIO = "radio";
|
||||
|
||||
/**
|
||||
* 复选框
|
||||
*/
|
||||
String HTML_CHECKBOX = "checkbox";
|
||||
|
||||
/**
|
||||
* 日期控件
|
||||
*/
|
||||
String HTML_DATETIME = "datetime";
|
||||
|
||||
/**
|
||||
* 开关控件
|
||||
*/
|
||||
String HTML_SWITCH = "switch";
|
||||
|
||||
/**
|
||||
* 数字输入控件
|
||||
*/
|
||||
String HTML_INPUT_NUMBER = "inputNumber";
|
||||
|
||||
/**
|
||||
* 图片上传控件
|
||||
*/
|
||||
String HTML_IMAGE_UPLOAD = "imageUpload";
|
||||
|
||||
/**
|
||||
* 文件上传控件
|
||||
*/
|
||||
String HTML_FILE_UPLOAD = "fileUpload";
|
||||
|
||||
/**
|
||||
* 富文本控件
|
||||
*/
|
||||
String HTML_EDITOR = "editor";
|
||||
|
||||
/**
|
||||
* 字符串类型
|
||||
*/
|
||||
String TYPE_STRING = "String";
|
||||
|
||||
/**
|
||||
* 整型
|
||||
*/
|
||||
String TYPE_INTEGER = "Integer";
|
||||
|
||||
/**
|
||||
* 长整型
|
||||
*/
|
||||
String TYPE_LONG = "Long";
|
||||
|
||||
/**
|
||||
* 浮点型
|
||||
*/
|
||||
String TYPE_DOUBLE = "Double";
|
||||
|
||||
/**
|
||||
* 单精度浮点型
|
||||
*/
|
||||
String TYPE_FLOAT = "Float";
|
||||
|
||||
/**
|
||||
* 高精度计算类型
|
||||
*/
|
||||
String TYPE_BIGDECIMAL = "BigDecimal";
|
||||
|
||||
/**
|
||||
* 布尔类型
|
||||
*/
|
||||
String TYPE_BOOLEAN = "Boolean";
|
||||
|
||||
/**
|
||||
* 时间类型
|
||||
*/
|
||||
String TYPE_DATE = "LocalDateTime";
|
||||
|
||||
/**
|
||||
* 模糊查询
|
||||
*/
|
||||
String QUERY_LIKE = "LIKE";
|
||||
|
||||
/**
|
||||
* 相等查询
|
||||
*/
|
||||
String QUERY_EQ = "EQ";
|
||||
/**
|
||||
* 范围查询
|
||||
*/
|
||||
String QUERY_BETWEEN = "BETWEEN";
|
||||
|
||||
/**
|
||||
* 必填标识,对应前端表单规则中的必填字段配置。
|
||||
*/
|
||||
String REQUIRE = "1";
|
||||
|
||||
// 模板根路径与后缀
|
||||
String TEMPLATE_ROOT_PATH = "fm";
|
||||
String TEMPLATE_FILE_SUFFIX = ".ftl";
|
||||
String TEMPLATE_RESOURCE_PREFIX = "classpath*:";
|
||||
String TEMPLATE_PAGE_PATTERN_SUFFIX = ".*" + TEMPLATE_FILE_SUFFIX;
|
||||
// 后端源码模板
|
||||
String JAVA_TEMPLATE_ROOT_PATH = TEMPLATE_ROOT_PATH + "/java";
|
||||
String JAVA_DOMAIN_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/domain.java" + TEMPLATE_FILE_SUFFIX;
|
||||
String JAVA_VO_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/vo.java" + TEMPLATE_FILE_SUFFIX;
|
||||
String JAVA_BO_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/bo.java" + TEMPLATE_FILE_SUFFIX;
|
||||
String JAVA_MAPPER_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/mapper.java" + TEMPLATE_FILE_SUFFIX;
|
||||
String JAVA_SERVICE_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/service.java" + TEMPLATE_FILE_SUFFIX;
|
||||
String JAVA_SERVICE_IMPL_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/serviceImpl.java" + TEMPLATE_FILE_SUFFIX;
|
||||
String JAVA_CONTROLLER_TEMPLATE_PATH = JAVA_TEMPLATE_ROOT_PATH + "/controller.java" + TEMPLATE_FILE_SUFFIX;
|
||||
// MyBatis MapperXML 模板
|
||||
String XML_TEMPLATE_ROOT_PATH = TEMPLATE_ROOT_PATH + "/xml";
|
||||
String XML_MAPPER_TEMPLATE_PATH = XML_TEMPLATE_ROOT_PATH + "/mapper.xml" + TEMPLATE_FILE_SUFFIX;
|
||||
// 前端源码模板
|
||||
String FRONTEND_API_TEMPLATE_NAME = "api.ts" + TEMPLATE_FILE_SUFFIX;
|
||||
String FRONTEND_TYPES_TEMPLATE_NAME = "types.ts" + TEMPLATE_FILE_SUFFIX;
|
||||
String FRONTEND_INDEX_TEMPLATE_PREFIX = "index";
|
||||
String FRONTEND_INDEX_TREE_TEMPLATE_PREFIX = "index-tree";
|
||||
// 数据库SQL模板
|
||||
String SQL_TEMPLATE_ROOT_PATH = TEMPLATE_ROOT_PATH + "/sql";
|
||||
String SQL_ORACLE_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/oracle.sql" + TEMPLATE_FILE_SUFFIX;
|
||||
String SQL_POSTGRES_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/postgres.sql" + TEMPLATE_FILE_SUFFIX;
|
||||
String SQL_SQLSERVER_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/sqlserver.sql" + TEMPLATE_FILE_SUFFIX;
|
||||
String SQL_MYSQL_TEMPLATE_PATH = SQL_TEMPLATE_ROOT_PATH + "/mysql.sql" + TEMPLATE_FILE_SUFFIX;
|
||||
|
||||
/**
|
||||
* 固定模板路径集合,前端模板按 frontendType 动态解析。
|
||||
*/
|
||||
Set<String> TEMPLATE_PATHS = CollUtil.newHashSet(
|
||||
JAVA_DOMAIN_TEMPLATE_PATH
|
||||
, JAVA_VO_TEMPLATE_PATH
|
||||
, JAVA_BO_TEMPLATE_PATH
|
||||
, JAVA_MAPPER_TEMPLATE_PATH
|
||||
, JAVA_SERVICE_TEMPLATE_PATH
|
||||
, JAVA_SERVICE_IMPL_TEMPLATE_PATH
|
||||
, JAVA_CONTROLLER_TEMPLATE_PATH
|
||||
, XML_MAPPER_TEMPLATE_PATH
|
||||
, SQL_ORACLE_TEMPLATE_PATH
|
||||
, SQL_POSTGRES_TEMPLATE_PATH
|
||||
, SQL_SQLSERVER_TEMPLATE_PATH
|
||||
, SQL_MYSQL_TEMPLATE_PATH
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package org.dromara.gen.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import com.baomidou.lock.annotation.Lock4j;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.helper.DataBaseHelper;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.gen.domain.GenTable;
|
||||
import org.dromara.gen.domain.GenTableColumn;
|
||||
import org.dromara.gen.service.IGenTableService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 代码生成 操作处理
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/tool/gen")
|
||||
public class GenController extends BaseController {
|
||||
|
||||
private final IGenTableService genTableService;
|
||||
|
||||
/**
|
||||
* 分页查询代码生成业务列表。
|
||||
*
|
||||
* @param genTable 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 代码生成列表
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:list")
|
||||
@GetMapping("/list")
|
||||
public R<PageResult<GenTable>> genList(GenTable genTable, PageQuery pageQuery) {
|
||||
return R.ok(genTableService.selectPageGenTableList(genTable, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改代码生成业务
|
||||
*
|
||||
* @param tableId 表ID
|
||||
* @return 表与字段信息
|
||||
*/
|
||||
@RepeatSubmit()
|
||||
@SaCheckPermission("tool:gen:query")
|
||||
@GetMapping(value = "/{tableId}")
|
||||
public R<Map<String, Object>> getInfo(@PathVariable Long tableId) {
|
||||
GenTable table = genTableService.selectGenTableById(tableId);
|
||||
List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);
|
||||
Map<String, Object> map = new HashMap<>(2);
|
||||
map.put("info", table);
|
||||
map.put("rows", list);
|
||||
return R.ok(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询数据库表列表。
|
||||
*
|
||||
* @param genTable 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 数据库表列表
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:list")
|
||||
@GetMapping("/db/list")
|
||||
public R<PageResult<GenTable>> dataList(GenTable genTable, PageQuery pageQuery) {
|
||||
return R.ok(genTableService.selectPageDbTableList(genTable, pageQuery));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据表字段列表
|
||||
*
|
||||
* @param tableId 表ID
|
||||
* @return 字段列表
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:list")
|
||||
@GetMapping(value = "/column/{tableId}")
|
||||
public R<PageResult<GenTableColumn>> columnList(@PathVariable("tableId") Long tableId) {
|
||||
List<GenTableColumn> list = genTableService.selectGenTableColumnListByTableId(tableId);
|
||||
return R.ok(PageResult.build(list));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入表结构(保存)
|
||||
*
|
||||
* @param tables 表名串
|
||||
* @param dataName 数据源名称
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:import")
|
||||
@Log(title = "代码生成", businessType = BusinessType.IMPORT)
|
||||
@Lock4j(keys = {"#dataName"}, acquireTimeout = 10000)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/importTable")
|
||||
public R<Void> importTableSave(String tables, String dataName) {
|
||||
String[] tableNames = Convert.toStrArray(tables);
|
||||
// 查询表信息
|
||||
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames, dataName);
|
||||
genTableService.importGenTable(tableList, dataName);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存代码生成业务配置。
|
||||
*
|
||||
* @param genTable 业务配置
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:edit")
|
||||
@Log(title = "代码生成", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping
|
||||
public R<Void> editSave(@Validated @RequestBody GenTable genTable) {
|
||||
genTableService.validateEdit(genTable);
|
||||
genTableService.updateGenTable(genTable);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除代码生成
|
||||
*
|
||||
* @param tableIds 表ID串
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:remove")
|
||||
@Log(title = "代码生成", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{tableIds}")
|
||||
public R<Void> remove(@PathVariable Long[] tableIds) {
|
||||
genTableService.deleteGenTableByIds(tableIds);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览代码
|
||||
*
|
||||
* @param tableId 表ID
|
||||
* @return 模板路径与生成代码内容映射
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:preview")
|
||||
@GetMapping("/preview/{tableId}")
|
||||
public R<Map<String, String>> preview(@PathVariable("tableId") Long tableId) throws IOException {
|
||||
Map<String, String> dataMap = genTableService.previewCode(tableId);
|
||||
return R.ok(dataMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代码(下载方式)
|
||||
*
|
||||
* @param response HTTP 响应
|
||||
* @param tableId 表ID
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:code")
|
||||
@Log(title = "代码生成", businessType = BusinessType.GENCODE)
|
||||
@GetMapping("/download/{tableId}")
|
||||
public void download(HttpServletResponse response, @PathVariable("tableId") Long tableId) throws IOException {
|
||||
byte[] data = genTableService.downloadCode(tableId);
|
||||
genCode(response, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步数据库
|
||||
*
|
||||
* @param tableId 表ID
|
||||
* @return 操作结果
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:edit")
|
||||
@Log(title = "代码生成", businessType = BusinessType.UPDATE)
|
||||
@Lock4j(keys = {"#tableId"}, acquireTimeout = 5000)
|
||||
@GetMapping("/synchDb/{tableId}")
|
||||
public R<Void> synchDb(@PathVariable("tableId") Long tableId) {
|
||||
genTableService.synchDb(tableId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成代码
|
||||
*
|
||||
* @param response HTTP 响应
|
||||
* @param tableIdStr 表ID串
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:code")
|
||||
@Log(title = "代码生成", businessType = BusinessType.GENCODE)
|
||||
@GetMapping("/batchGenCode")
|
||||
public void batchGenCode(HttpServletResponse response, String tableIdStr) throws IOException {
|
||||
String[] tableIds = Convert.toStrArray(tableIdStr);
|
||||
byte[] data = genTableService.downloadCode(tableIds);
|
||||
genCode(response, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将生成结果写出为 zip 文件流。
|
||||
*
|
||||
* @param response HTTP 响应
|
||||
* @param data zip 二进制数据
|
||||
*/
|
||||
private void genCode(HttpServletResponse response, byte[] data) throws IOException {
|
||||
response.reset();
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\"");
|
||||
response.addHeader("Content-Length", "" + data.length);
|
||||
response.setContentType("application/octet-stream; charset=UTF-8");
|
||||
IoUtil.write(response.getOutputStream(), false, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前可用数据源名称列表。
|
||||
*
|
||||
* @return 数据源名称集合
|
||||
*/
|
||||
@SaCheckPermission("tool:gen:list")
|
||||
@GetMapping(value = "/getDataNames")
|
||||
public R<Object> getCurrentDataSourceNameList() {
|
||||
return R.ok(DataBaseHelper.getDataSourceNameList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package org.dromara.gen.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.gen.constant.GenConstants;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 业务表 gen_table
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gen_table")
|
||||
public class GenTable extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId(value = "table_id")
|
||||
private Long tableId;
|
||||
|
||||
/**
|
||||
* 数据源名称
|
||||
*/
|
||||
@NotBlank(message = "数据源名称不能为空")
|
||||
private String dataName;
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*/
|
||||
@NotBlank(message = "表名称不能为空")
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 表描述
|
||||
*/
|
||||
@NotBlank(message = "表描述不能为空")
|
||||
private String tableComment;
|
||||
|
||||
/**
|
||||
* 实体类名称(首字母大写)
|
||||
*/
|
||||
@NotBlank(message = "实体类名称不能为空")
|
||||
private String className;
|
||||
|
||||
/**
|
||||
* 使用的模板(crud单表操作 tree树表操作)
|
||||
*/
|
||||
private String tplCategory;
|
||||
|
||||
/**
|
||||
* 前端模板类型,对应 fm 下的模板目录
|
||||
*/
|
||||
private String frontendType;
|
||||
|
||||
/**
|
||||
* 生成包路径
|
||||
*/
|
||||
@NotBlank(message = "生成包路径不能为空")
|
||||
private String packageName;
|
||||
|
||||
/**
|
||||
* 生成模块名
|
||||
*/
|
||||
@NotBlank(message = "生成模块名不能为空")
|
||||
private String moduleName;
|
||||
|
||||
/**
|
||||
* 生成业务名
|
||||
*/
|
||||
@NotBlank(message = "生成业务名不能为空")
|
||||
private String businessName;
|
||||
|
||||
/**
|
||||
* 生成功能名
|
||||
*/
|
||||
@NotBlank(message = "生成功能名不能为空")
|
||||
private String functionName;
|
||||
|
||||
/**
|
||||
* 生成作者
|
||||
*/
|
||||
@NotBlank(message = "作者不能为空")
|
||||
private String functionAuthor;
|
||||
|
||||
/**
|
||||
* 主键信息
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private GenTableColumn pkColumn;
|
||||
|
||||
/**
|
||||
* 表列信息
|
||||
*/
|
||||
@Valid
|
||||
@TableField(exist = false)
|
||||
private List<GenTableColumn> columns;
|
||||
|
||||
/**
|
||||
* 其它生成选项
|
||||
*/
|
||||
private String options;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 树编码字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String treeCode;
|
||||
|
||||
/**
|
||||
* 树父编码字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String treeParentCode;
|
||||
|
||||
/**
|
||||
* 树名称字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String treeName;
|
||||
|
||||
/**
|
||||
* 菜单id列表
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<Long> menuIds;
|
||||
|
||||
/**
|
||||
* 上级菜单ID字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Long parentMenuId;
|
||||
|
||||
/**
|
||||
* 上级菜单名称字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String parentMenuName;
|
||||
|
||||
/**
|
||||
* 是否启用导出
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Boolean enableExport;
|
||||
|
||||
/**
|
||||
* 是否启用状态切换
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Boolean enableStatus;
|
||||
|
||||
/**
|
||||
* 状态字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String statusField;
|
||||
|
||||
/**
|
||||
* 是否启用组合唯一校验
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Boolean enableUnique;
|
||||
|
||||
/**
|
||||
* 组合唯一字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<String> uniqueFields;
|
||||
|
||||
/**
|
||||
* 是否启用排序调整
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Boolean enableSort;
|
||||
|
||||
/**
|
||||
* 排序字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String sortField;
|
||||
|
||||
/**
|
||||
* 树根节点值
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String treeRootValue;
|
||||
|
||||
/**
|
||||
* 树祖级字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String treeAncestorsField;
|
||||
|
||||
/**
|
||||
* 树排序字段
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String treeOrderField;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 判断当前业务表是否采用树表模板。
|
||||
*
|
||||
* @return 树表模板返回 {@code true}
|
||||
*/
|
||||
public boolean isTree() {
|
||||
return isTree(this.tplCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板分类判断是否为树表模板。
|
||||
*
|
||||
* @param tplCategory 模板分类
|
||||
* @return 树表模板返回 {@code true}
|
||||
*/
|
||||
public static boolean isTree(String tplCategory) {
|
||||
return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前业务表是否采用普通 CRUD 模板。
|
||||
*
|
||||
* @return 普通 CRUD 模板返回 {@code true}
|
||||
*/
|
||||
public boolean isCrud() {
|
||||
return isCrud(this.tplCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板分类判断是否为普通 CRUD 模板。
|
||||
*
|
||||
* @param tplCategory 模板分类
|
||||
* @return 普通 CRUD 模板返回 {@code true}
|
||||
*/
|
||||
public static boolean isCrud(String tplCategory) {
|
||||
return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定 Java 字段是否属于基类公共字段。
|
||||
*
|
||||
* @param javaField Java 字段名
|
||||
* @return 基类公共字段返回 {@code true}
|
||||
*/
|
||||
public boolean isSuperColumn(String javaField) {
|
||||
return isSuperColumn(this.tplCategory, javaField);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板分类与字段名判断是否属于基类公共字段。
|
||||
*
|
||||
* @param tplCategory 模板分类
|
||||
* @param javaField Java 字段名
|
||||
* @return 基类公共字段返回 {@code true}
|
||||
*/
|
||||
public static boolean isSuperColumn(String tplCategory, String javaField) {
|
||||
return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
package org.dromara.gen.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.gen.constant.GenConstants;
|
||||
|
||||
/**
|
||||
* 代码生成业务字段表 gen_table_column
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("gen_table_column")
|
||||
public class GenTableColumn extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId(value = "column_id")
|
||||
private Long columnId;
|
||||
|
||||
/**
|
||||
* 归属表编号
|
||||
*/
|
||||
private Long tableId;
|
||||
|
||||
/**
|
||||
* 列名称
|
||||
*/
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 列描述
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String columnComment;
|
||||
|
||||
/**
|
||||
* 列类型
|
||||
*/
|
||||
private String columnType;
|
||||
|
||||
/**
|
||||
* JAVA类型
|
||||
*/
|
||||
private String javaType;
|
||||
|
||||
/**
|
||||
* JAVA字段名
|
||||
*/
|
||||
@NotBlank(message = "Java属性不能为空")
|
||||
private String javaField;
|
||||
|
||||
/**
|
||||
* 是否主键(1是)
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String isPk;
|
||||
|
||||
/**
|
||||
* 是否自增(1是)
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String isIncrement;
|
||||
|
||||
/**
|
||||
* 是否必填(1是)
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String isRequired;
|
||||
|
||||
/**
|
||||
* 是否为插入字段(1是)
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String isInsert;
|
||||
|
||||
/**
|
||||
* 是否编辑字段(1是)
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String isEdit;
|
||||
|
||||
/**
|
||||
* 是否列表字段(1是)
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String isList;
|
||||
|
||||
/**
|
||||
* 是否查询字段(1是)
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String isQuery;
|
||||
|
||||
/**
|
||||
* 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围)
|
||||
*/
|
||||
private String queryType;
|
||||
|
||||
/**
|
||||
* 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件)
|
||||
*/
|
||||
private String htmlType;
|
||||
|
||||
/**
|
||||
* 字典类型
|
||||
*/
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
|
||||
private String dictType;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 获取首字母大写后的 Java 字段名。
|
||||
*
|
||||
* @return 首字母大写的 Java 字段名
|
||||
*/
|
||||
public String getCapJavaField() {
|
||||
return StringUtils.capitalize(javaField);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取适合界面展示的字段注释,去除括号内的枚举说明。
|
||||
*
|
||||
* @return 字段展示名称
|
||||
*/
|
||||
public String getColumnLabel() {
|
||||
int index = StringUtils.indexOf(this.columnComment, "(");
|
||||
if (index != StringUtils.INDEX_NOT_FOUND) {
|
||||
return StringUtils.substring(this.columnComment, 0, index);
|
||||
}
|
||||
return this.columnComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 TypeScript 字段类型。
|
||||
*
|
||||
* @return TypeScript 类型
|
||||
*/
|
||||
public String getTsType() {
|
||||
if (StringUtils.containsAny(this.javaField, "id", "Id")) {
|
||||
return "string | number";
|
||||
}
|
||||
if (StringUtils.equalsAny(this.javaType,
|
||||
GenConstants.TYPE_LONG, GenConstants.TYPE_INTEGER, GenConstants.TYPE_DOUBLE,
|
||||
GenConstants.TYPE_FLOAT, GenConstants.TYPE_BIGDECIMAL)) {
|
||||
return "number";
|
||||
}
|
||||
if (StringUtils.equals(this.javaType, GenConstants.TYPE_BOOLEAN)) {
|
||||
return "boolean";
|
||||
}
|
||||
return "string";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取开关启用值的前端字面量。
|
||||
*
|
||||
* @return 前端字面量
|
||||
*/
|
||||
public String getSwitchActiveValue() {
|
||||
if (StringUtils.equals(this.javaType, GenConstants.TYPE_BOOLEAN)) {
|
||||
return "true";
|
||||
}
|
||||
if (isNumberType()) {
|
||||
return "0";
|
||||
}
|
||||
return "'0'";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取开关停用值的前端字面量。
|
||||
*
|
||||
* @return 前端字面量
|
||||
*/
|
||||
public String getSwitchInactiveValue() {
|
||||
if (StringUtils.equals(this.javaType, GenConstants.TYPE_BOOLEAN)) {
|
||||
return "false";
|
||||
}
|
||||
if (isNumberType()) {
|
||||
return "1";
|
||||
}
|
||||
return "'1'";
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前字段是否为数值类型。
|
||||
*
|
||||
* @return 数值类型返回 {@code true}
|
||||
*/
|
||||
public boolean isNumberType() {
|
||||
return StringUtils.equalsAny(this.javaType,
|
||||
GenConstants.TYPE_INTEGER, GenConstants.TYPE_LONG, GenConstants.TYPE_DOUBLE,
|
||||
GenConstants.TYPE_FLOAT, GenConstants.TYPE_BIGDECIMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为日期范围查询字段。
|
||||
*
|
||||
* @return 日期范围查询字段返回 {@code true}
|
||||
*/
|
||||
public boolean isDateRangeQuery() {
|
||||
return isQuery()
|
||||
&& StringUtils.equals(this.htmlType, GenConstants.HTML_DATETIME)
|
||||
&& StringUtils.equals(this.queryType, GenConstants.QUERY_BETWEEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否为主键列。
|
||||
*
|
||||
* @return 主键列返回 {@code true}
|
||||
*/
|
||||
public boolean isPk() {
|
||||
return isPk(this.isPk);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标识判断是否为主键列。
|
||||
*
|
||||
* @param isPk 主键标识
|
||||
* @return 主键列返回 {@code true}
|
||||
*/
|
||||
public boolean isPk(String isPk) {
|
||||
return isPk != null && StringUtils.equals("1", isPk);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否为自增列。
|
||||
*
|
||||
* @return 自增列返回 {@code true}
|
||||
*/
|
||||
public boolean isIncrement() {
|
||||
return isIncrement(this.isIncrement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标识判断是否为自增列。
|
||||
*
|
||||
* @param isIncrement 自增标识
|
||||
* @return 自增列返回 {@code true}
|
||||
*/
|
||||
public boolean isIncrement(String isIncrement) {
|
||||
return isIncrement != null && StringUtils.equals("1", isIncrement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否必填。
|
||||
*
|
||||
* @return 必填返回 {@code true}
|
||||
*/
|
||||
public boolean isRequired() {
|
||||
return isRequired(this.isRequired);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标识判断当前列是否必填。
|
||||
*
|
||||
* @param isRequired 必填标识
|
||||
* @return 必填返回 {@code true}
|
||||
*/
|
||||
public boolean isRequired(String isRequired) {
|
||||
return isRequired != null && StringUtils.equals("1", isRequired);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否参与新增。
|
||||
*
|
||||
* @return 参与新增返回 {@code true}
|
||||
*/
|
||||
public boolean isInsert() {
|
||||
return isInsert(this.isInsert);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标识判断当前列是否参与新增。
|
||||
*
|
||||
* @param isInsert 新增标识
|
||||
* @return 参与新增返回 {@code true}
|
||||
*/
|
||||
public boolean isInsert(String isInsert) {
|
||||
return isInsert != null && StringUtils.equals("1", isInsert);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否参与编辑。
|
||||
*
|
||||
* @return 参与编辑返回 {@code true}
|
||||
*/
|
||||
public boolean isEdit() {
|
||||
return isEdit(this.isEdit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标识判断当前列是否参与编辑。
|
||||
*
|
||||
* @param isEdit 编辑标识
|
||||
* @return 参与编辑返回 {@code true}
|
||||
*/
|
||||
public boolean isEdit(String isEdit) {
|
||||
return isEdit != null && StringUtils.equals("1", isEdit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否参与列表展示。
|
||||
*
|
||||
* @return 参与列表展示返回 {@code true}
|
||||
*/
|
||||
public boolean isList() {
|
||||
return isList(this.isList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标识判断当前列是否参与列表展示。
|
||||
*
|
||||
* @param isList 列表展示标识
|
||||
* @return 参与列表展示返回 {@code true}
|
||||
*/
|
||||
public boolean isList(String isList) {
|
||||
return isList != null && StringUtils.equals("1", isList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否参与查询条件。
|
||||
*
|
||||
* @return 参与查询返回 {@code true}
|
||||
*/
|
||||
public boolean isQuery() {
|
||||
return isQuery(this.isQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标识判断当前列是否参与查询条件。
|
||||
*
|
||||
* @param isQuery 查询标识
|
||||
* @return 参与查询返回 {@code true}
|
||||
*/
|
||||
public boolean isQuery(String isQuery) {
|
||||
return isQuery != null && StringUtils.equals("1", isQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否为基类公共字段。
|
||||
*
|
||||
* @return 基类公共字段返回 {@code true}
|
||||
*/
|
||||
public boolean isSuperColumn() {
|
||||
return isSuperColumn(this.javaField);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段名判断是否为基类公共字段。
|
||||
*
|
||||
* @param javaField Java 字段名
|
||||
* @return 基类公共字段返回 {@code true}
|
||||
*/
|
||||
public static boolean isSuperColumn(String javaField) {
|
||||
return StringUtils.equalsAnyIgnoreCase(javaField,
|
||||
// BaseEntity
|
||||
"createBy", "createTime", "updateBy", "updateTime",
|
||||
// TreeEntity
|
||||
"parentName", "parentId");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否属于生成页面需要保留的白名单字段。
|
||||
*
|
||||
* @return 白名单字段返回 {@code true}
|
||||
*/
|
||||
public boolean isUsableColumn() {
|
||||
return isUsableColumn(javaField);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段名判断是否属于生成页面需要保留的白名单字段。
|
||||
*
|
||||
* @param javaField Java 字段名
|
||||
* @return 白名单字段返回 {@code true}
|
||||
*/
|
||||
public static boolean isUsableColumn(String javaField) {
|
||||
// isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单
|
||||
return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否需要显式声明 MP 字段映射。
|
||||
*
|
||||
* @return 字段名与下划线列名不一致时返回 {@code true}
|
||||
*/
|
||||
public boolean isNeedTableField() {
|
||||
if (StringUtils.isAnyBlank(this.columnName, this.javaField)) {
|
||||
return false;
|
||||
}
|
||||
return !StringUtils.equalsIgnoreCase(this.columnName, StringUtils.toUnderScoreCase(this.javaField));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前列是否属于字典控件列。
|
||||
*
|
||||
* @return 仅当已配置字典类型且显示类型支持字典时返回 {@code true}
|
||||
*/
|
||||
public boolean isDictColumn() {
|
||||
return StringUtils.isNotBlank(this.dictType) && StringUtils.equalsAny(this.htmlType,
|
||||
GenConstants.HTML_SELECT,
|
||||
GenConstants.HTML_RADIO,
|
||||
GenConstants.HTML_CHECKBOX,
|
||||
GenConstants.HTML_SWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从字段注释中解析字典读转换表达式。
|
||||
*
|
||||
* @return 形如 `0=男,1=女` 的转换表达式,无法解析时返回原始注释
|
||||
*/
|
||||
public String readConverterExp() {
|
||||
String remarks = StringUtils.substringBetween(this.columnComment, "(", ")");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (StringUtils.isNotEmpty(remarks)) {
|
||||
for (String value : remarks.split(" ")) {
|
||||
if (StringUtils.isNotEmpty(value)) {
|
||||
Object startStr = value.subSequence(0, 1);
|
||||
String endStr = value.substring(1);
|
||||
sb.append(StringUtils.EMPTY).append(startStr).append("=").append(endStr).append(StringUtils.SEPARATOR);
|
||||
}
|
||||
}
|
||||
return sb.deleteCharAt(sb.length() - 1).toString();
|
||||
} else {
|
||||
return this.columnComment;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.dromara.gen.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.gen.domain.GenTableColumn;
|
||||
|
||||
/**
|
||||
* 业务字段 数据层
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@InterceptorIgnore(dataPermission = "true", tenantLine = "true")
|
||||
public interface GenTableColumnMapper extends BaseMapperPlus<GenTableColumn, GenTableColumn> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.dromara.gen.mapper;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.gen.domain.GenTable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 业务 数据层
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@InterceptorIgnore(dataPermission = "true", tenantLine = "true")
|
||||
public interface GenTableMapper extends BaseMapperPlus<GenTable, GenTable> {
|
||||
|
||||
/**
|
||||
* 查询指定数据源下的所有表名列表
|
||||
*
|
||||
* @param dataName 数据源名称,用于选择不同的数据源
|
||||
* @return 当前数据库中的表名列表
|
||||
* @DS("") 使用默认数据源执行查询操作
|
||||
*/
|
||||
@DS("")
|
||||
List<String> selectTableNameList(String dataName);
|
||||
}
|
||||
@@ -0,0 +1,654 @@
|
||||
package org.dromara.gen.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.anyline.metadata.Column;
|
||||
import org.anyline.metadata.Table;
|
||||
import org.anyline.proxy.ServiceProxy;
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StreamUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.utils.IdGeneratorUtil;
|
||||
import org.dromara.gen.constant.GenConstants;
|
||||
import org.dromara.gen.domain.GenTable;
|
||||
import org.dromara.gen.domain.GenTableColumn;
|
||||
import org.dromara.gen.mapper.GenTableColumnMapper;
|
||||
import org.dromara.gen.mapper.GenTableMapper;
|
||||
import org.dromara.gen.util.GenUtils;
|
||||
import org.dromara.gen.util.TemplateEngineUtils;
|
||||
import org.dromara.gen.util.template.PathNamedTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* 业务 服务层实现
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GenTableServiceImpl implements IGenTableService {
|
||||
|
||||
private final GenTableMapper tableMapper;
|
||||
private final GenTableColumnMapper genTableColumnMapper;
|
||||
|
||||
private static final String[] TABLE_IGNORE = new String[]{"sai_", "sj_", "flow_", "gen_"};
|
||||
|
||||
/**
|
||||
* 查询业务字段列表
|
||||
*
|
||||
* @param tableId 业务字段编号
|
||||
* @return 业务字段集合
|
||||
*/
|
||||
@Override
|
||||
public List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId) {
|
||||
return genTableColumnMapper.lambda()
|
||||
.eq(GenTableColumn::getTableId, tableId)
|
||||
.orderByAsc(GenTableColumn::getSort)
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询业务信息
|
||||
*
|
||||
* @param id 业务ID
|
||||
* @return 业务信息
|
||||
*/
|
||||
@Override
|
||||
public GenTable selectGenTableById(Long id) {
|
||||
GenTable genTable = getGenTable(id);
|
||||
setTableFromOptions(genTable);
|
||||
return genTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询已导入的代码生成业务表。
|
||||
*
|
||||
* @param genTable 业务表筛选条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 业务表分页结果
|
||||
*/
|
||||
@Override
|
||||
public PageResult<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery) {
|
||||
Page<GenTable> page = tableMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable));
|
||||
return PageResult.build(page.getRecords(), page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造代码生成业务表查询条件。
|
||||
*
|
||||
* @param genTable 业务表筛选条件
|
||||
* @return 包含数据源、表名、表注释和时间区间的查询包装器
|
||||
*/
|
||||
private QueryWrapper<GenTable> buildGenTableQueryWrapper(GenTable genTable) {
|
||||
Map<String, Object> params = genTable.getParams();
|
||||
QueryWrapper<GenTable> wrapper = Wrappers.query();
|
||||
wrapper
|
||||
.eq(StringUtils.isNotEmpty(genTable.getDataName()), "data_name", genTable.getDataName())
|
||||
.like(StringUtils.isNotBlank(genTable.getTableName()), "lower(table_name)", StringUtils.lowerCase(genTable.getTableName()))
|
||||
.like(StringUtils.isNotBlank(genTable.getTableComment()), "lower(table_comment)", StringUtils.lowerCase(genTable.getTableComment()))
|
||||
.between(params.get("beginTime") != null && params.get("endTime") != null,
|
||||
"create_time", params.get("beginTime"), params.get("endTime"))
|
||||
.orderByDesc("update_time");
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据库列表
|
||||
*
|
||||
* @param genTable 包含查询条件的GenTable对象
|
||||
* @param pageQuery 包含分页信息的PageQuery对象
|
||||
* @return 包含分页结果的TableDataInfo对象
|
||||
*/
|
||||
@DS("#genTable.dataName")
|
||||
@Override
|
||||
public PageResult<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery) {
|
||||
// 获取查询条件
|
||||
String tableName = genTable.getTableName();
|
||||
String tableComment = genTable.getTableComment();
|
||||
|
||||
LinkedHashMap<String, Table<?>> tablesMap = ServiceProxy.metadata().tables();
|
||||
if (CollUtil.isEmpty(tablesMap)) {
|
||||
return PageResult.build();
|
||||
}
|
||||
List<String> tableNames = tableMapper.selectTableNameList(genTable.getDataName());
|
||||
String[] tableArrays;
|
||||
if (CollUtil.isNotEmpty(tableNames)) {
|
||||
tableArrays = tableNames.toArray(new String[0]);
|
||||
} else {
|
||||
tableArrays = new String[0];
|
||||
}
|
||||
// 过滤并转换表格数据
|
||||
List<GenTable> tables = tablesMap.values().stream()
|
||||
.filter(x -> !StringUtils.startWithAnyIgnoreCase(x.getName(), TABLE_IGNORE))
|
||||
.filter(x -> {
|
||||
if (CollUtil.isEmpty(tableNames)) {
|
||||
return true;
|
||||
}
|
||||
return !StringUtils.equalsAnyIgnoreCase(x.getName(), tableArrays);
|
||||
})
|
||||
.filter(x -> {
|
||||
boolean nameMatches = true;
|
||||
boolean commentMatches = true;
|
||||
// 进行表名称的模糊查询
|
||||
if (StringUtils.isNotBlank(tableName)) {
|
||||
nameMatches = StringUtils.containsIgnoreCase(x.getName(), tableName);
|
||||
}
|
||||
// 进行表描述的模糊查询
|
||||
if (StringUtils.isNotBlank(tableComment)) {
|
||||
commentMatches = StringUtils.containsIgnoreCase(x.getComment(), tableComment);
|
||||
}
|
||||
// 同时匹配名称和描述
|
||||
return nameMatches && commentMatches;
|
||||
})
|
||||
.map(x -> {
|
||||
GenTable gen = new GenTable();
|
||||
gen.setTableName(x.getName());
|
||||
gen.setTableComment(x.getComment());
|
||||
// postgresql的表元数据没有创建时间这个东西(好奇葩) 只能new Date代替
|
||||
Date createDate = ObjectUtil.defaultIfNull(x.getCreateTime(), new Date());
|
||||
gen.setCreateTime(LocalDateTimeUtil.of(createDate));
|
||||
gen.setUpdateTime(x.getUpdateTime() != null ? LocalDateTimeUtil.of(x.getUpdateTime()) : null);
|
||||
return gen;
|
||||
}).sorted(Comparator.comparing(GenTable::getCreateTime).reversed())
|
||||
.toList();
|
||||
// 根据原始数据列表和分页参数,构建表格分页数据对象(用于假分页)
|
||||
if (CollUtil.isEmpty(tables)) {
|
||||
return PageResult.build();
|
||||
}
|
||||
Page<Object> page = pageQuery.build();
|
||||
List<GenTable> pageList = CollUtil.page((int) page.getCurrent() - 1, (int) page.getSize(), tables);
|
||||
return PageResult.build(pageList, tables.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询据库列表
|
||||
*
|
||||
* @param tableNames 表名称组
|
||||
* @param dataName 数据源名称
|
||||
* @return 数据库表集合
|
||||
*/
|
||||
@DS("#dataName")
|
||||
@Override
|
||||
public List<GenTable> selectDbTableListByNames(String[] tableNames, String dataName) {
|
||||
Set<String> tableNameSet = new HashSet<>(List.of(tableNames));
|
||||
LinkedHashMap<String, Table<?>> tablesMap = ServiceProxy.metadata().tables();
|
||||
|
||||
if (CollUtil.isEmpty(tablesMap)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Table<?>> tableList = tablesMap.values().stream()
|
||||
.filter(x -> !StringUtils.startWithAnyIgnoreCase(x.getName(), TABLE_IGNORE))
|
||||
.filter(x -> tableNameSet.contains(x.getName())).toList();
|
||||
|
||||
if (CollUtil.isEmpty(tableList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return tableList.stream().map(x -> {
|
||||
GenTable gen = new GenTable();
|
||||
gen.setDataName(dataName);
|
||||
gen.setTableName(x.getName());
|
||||
gen.setTableComment(x.getComment());
|
||||
gen.setCreateTime(LocalDateTimeUtil.of(x.getCreateTime()));
|
||||
gen.setUpdateTime(LocalDateTimeUtil.of(x.getUpdateTime()));
|
||||
return gen;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改业务
|
||||
*
|
||||
* @param genTable 业务信息
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void updateGenTable(GenTable genTable) {
|
||||
normalizeColumnOptions(genTable.getColumns());
|
||||
String options = JsonUtils.toJsonString(genTable.getParams());
|
||||
genTable.setOptions(options);
|
||||
int row = tableMapper.updateById(genTable);
|
||||
if (row > 0) {
|
||||
genTableColumnMapper.updateBatchById(genTable.getColumns());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除业务对象
|
||||
*
|
||||
* @param tableIds 需要删除的数据ID
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void deleteGenTableByIds(Long[] tableIds) {
|
||||
List<Long> ids = Arrays.asList(tableIds);
|
||||
tableMapper.deleteByIds(ids);
|
||||
genTableColumnMapper.lambda()
|
||||
.in(GenTableColumn::getTableId, ids)
|
||||
.deleteCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入表结构
|
||||
*
|
||||
* @param tableList 导入表列表
|
||||
* @param dataName 数据源名称
|
||||
*/
|
||||
@DSTransactional
|
||||
@Override
|
||||
public void importGenTable(List<GenTable> tableList, String dataName) {
|
||||
try {
|
||||
for (GenTable table : tableList) {
|
||||
String tableName = table.getTableName();
|
||||
GenUtils.initTable(table);
|
||||
table.setDataName(dataName);
|
||||
int row = tableMapper.insert(table);
|
||||
if (row > 0) {
|
||||
// 保存列信息
|
||||
List<GenTableColumn> genTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(tableName, dataName);
|
||||
List<GenTableColumn> saveColumns = new ArrayList<>();
|
||||
for (GenTableColumn column : genTableColumns) {
|
||||
GenUtils.initColumnField(column, table);
|
||||
saveColumns.add(column);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(saveColumns)) {
|
||||
genTableColumnMapper.insertBatch(saveColumns);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("导入失败", e);
|
||||
throw new ServiceException("导入失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据表名称查询列信息
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @param dataName 数据源名称
|
||||
* @return 列信息
|
||||
*/
|
||||
@DS("#dataName")
|
||||
@Override
|
||||
public List<GenTableColumn> selectDbTableColumnsByName(String tableName, String dataName) {
|
||||
Table<?> table = ServiceProxy.metadata().table(tableName);
|
||||
if (ObjectUtil.isNull(table)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
LinkedHashMap<String, Column> columns = table.getColumns();
|
||||
List<GenTableColumn> tableColumns = new ArrayList<>();
|
||||
columns.forEach((columnName, column) -> {
|
||||
GenTableColumn tableColumn = new GenTableColumn();
|
||||
tableColumn.setIsPk(column.isPrimaryKey() ? "1" : "0");
|
||||
tableColumn.setColumnName(column.getName());
|
||||
tableColumn.setColumnComment(column.getComment());
|
||||
tableColumn.setColumnType(column.getOriginType().toLowerCase());
|
||||
tableColumn.setSort(column.getPosition());
|
||||
tableColumn.setIsRequired(column.isNullable() ? "0" : "1");
|
||||
tableColumn.setIsIncrement(column.isAutoIncrement() ? "1" : "0");
|
||||
tableColumns.add(tableColumn);
|
||||
});
|
||||
return tableColumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览代码
|
||||
*
|
||||
* @param tableId 表编号
|
||||
* @return 预览数据列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> previewCode(Long tableId) {
|
||||
Map<String, String> dataMap = new LinkedHashMap<>();
|
||||
RenderContext rc = buildRenderContext(tableId);
|
||||
for (PathNamedTemplate template : rc.templates()) {
|
||||
dataMap.put(template.getPathName(), template.render(rc.context()));
|
||||
}
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代码(下载方式)
|
||||
*
|
||||
* @param tableId 表名称
|
||||
* @return 数据
|
||||
*/
|
||||
@Override
|
||||
public byte[] downloadCode(Long tableId) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zip = new ZipOutputStream(outputStream);
|
||||
writeCodeToZip(tableId, zip);
|
||||
IoUtil.close(zip);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步数据库
|
||||
*
|
||||
* @param tableId 表名称
|
||||
*/
|
||||
@DSTransactional
|
||||
@Override
|
||||
public void synchDb(Long tableId) {
|
||||
GenTable table = getGenTable(tableId);
|
||||
List<GenTableColumn> tableColumns = table.getColumns();
|
||||
Map<String, GenTableColumn> tableColumnMap = StreamUtils.toIdentityMap(tableColumns, GenTableColumn::getColumnName);
|
||||
|
||||
List<GenTableColumn> dbTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(table.getTableName(), table.getDataName());
|
||||
if (CollUtil.isEmpty(dbTableColumns)) {
|
||||
throw new ServiceException("同步数据失败,原表结构不存在");
|
||||
}
|
||||
List<String> dbTableColumnNames = StreamUtils.toList(dbTableColumns, GenTableColumn::getColumnName);
|
||||
|
||||
List<GenTableColumn> saveColumns = new ArrayList<>();
|
||||
dbTableColumns.forEach(column -> {
|
||||
GenUtils.initColumnField(column, table);
|
||||
if (tableColumnMap.containsKey(column.getColumnName())) {
|
||||
GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName());
|
||||
column.setColumnId(prevColumn.getColumnId());
|
||||
if (column.isList()) {
|
||||
// 如果是列表,继续保留查询方式/字典类型选项
|
||||
column.setDictType(prevColumn.getDictType());
|
||||
column.setQueryType(prevColumn.getQueryType());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk()
|
||||
&& (column.isInsert() || column.isEdit())
|
||||
&& ((column.isUsableColumn()) || (!column.isSuperColumn()))) {
|
||||
// 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项
|
||||
column.setIsRequired(prevColumn.getIsRequired());
|
||||
column.setHtmlType(prevColumn.getHtmlType());
|
||||
}
|
||||
}
|
||||
saveColumns.add(column);
|
||||
});
|
||||
if (CollUtil.isNotEmpty(saveColumns)) {
|
||||
normalizeColumnOptions(saveColumns);
|
||||
genTableColumnMapper.insertOrUpdateBatch(saveColumns);
|
||||
}
|
||||
List<GenTableColumn> delColumns = StreamUtils.filter(tableColumns, column -> !dbTableColumnNames.contains(column.getColumnName()));
|
||||
if (CollUtil.isNotEmpty(delColumns)) {
|
||||
List<Long> ids = StreamUtils.toList(delColumns, GenTableColumn::getColumnId);
|
||||
if (CollUtil.isNotEmpty(ids)) {
|
||||
genTableColumnMapper.deleteByIds(ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量生成代码(下载方式)
|
||||
*
|
||||
* @param tableIds 表ID数组
|
||||
* @return 数据
|
||||
*/
|
||||
@Override
|
||||
public byte[] downloadCode(String[] tableIds) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
ZipOutputStream zip = new ZipOutputStream(outputStream);
|
||||
for (String tableId : tableIds) {
|
||||
writeCodeToZip(Long.parseLong(tableId), zip);
|
||||
}
|
||||
IoUtil.close(zip);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询表信息并生成代码
|
||||
*
|
||||
* @param tableId 业务表主键
|
||||
* @param zip 代码压缩输出流
|
||||
*/
|
||||
private void writeCodeToZip(Long tableId, ZipOutputStream zip) {
|
||||
RenderContext rc = buildRenderContext(tableId);
|
||||
GenTable table = rc.table();
|
||||
for (PathNamedTemplate template : rc.templates()) {
|
||||
String pathName = template.getPathName();
|
||||
try {
|
||||
String render = template.render(rc.context());
|
||||
zip.putNextEntry(new ZipEntry(TemplateEngineUtils.getFileName(pathName, table)));
|
||||
IoUtil.write(zip, StandardCharsets.UTF_8, false, render);
|
||||
zip.flush();
|
||||
zip.closeEntry();
|
||||
} catch (IOException e) {
|
||||
log.error("渲染模板失败,表名:{}", table.getTableName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建代码渲染上下文(含表信息、菜单ID、主键列、模板列表)
|
||||
*
|
||||
* @param tableId 业务表主键
|
||||
* @return 渲染上下文
|
||||
*/
|
||||
private RenderContext buildRenderContext(Long tableId) {
|
||||
GenTable table = getGenTable(tableId);
|
||||
List<Long> menuIds = new ArrayList<>();
|
||||
for (int i = 0; i < 6; i++) {
|
||||
menuIds.add(IdGeneratorUtil.nextLongId());
|
||||
}
|
||||
table.setMenuIds(menuIds);
|
||||
setPkColumn(table);
|
||||
Dict context = TemplateEngineUtils.buildContext(table);
|
||||
List<PathNamedTemplate> templates = TemplateEngineUtils.getTemplateList(table.getTplCategory(), table.getDataName(), table.getFrontendType());
|
||||
return new RenderContext(table, context, templates);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板渲染上下文。
|
||||
*
|
||||
* @param table 生成表信息
|
||||
* @param context 模板上下文
|
||||
* @param templates 待渲染模板
|
||||
*/
|
||||
private record RenderContext(GenTable table, Dict context, List<PathNamedTemplate> templates) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改保存参数校验
|
||||
*
|
||||
* @param genTable 业务信息
|
||||
*/
|
||||
@Override
|
||||
public void validateEdit(GenTable genTable) {
|
||||
validateOptionColumns(genTable);
|
||||
if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) {
|
||||
String options = JsonUtils.toJsonString(genTable.getParams());
|
||||
Dict paramsObj = JsonUtils.parseMap(options);
|
||||
if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_CODE))) {
|
||||
throw new ServiceException("树编码字段不能为空");
|
||||
} else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_PARENT_CODE))) {
|
||||
throw new ServiceException("树父编码字段不能为空");
|
||||
} else if (StringUtils.isEmpty(paramsObj.getStr(GenConstants.TREE_NAME))) {
|
||||
throw new ServiceException("树名称字段不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验生成选项中配置的字段是否存在。
|
||||
*
|
||||
* @param genTable 业务表信息
|
||||
*/
|
||||
private void validateOptionColumns(GenTable genTable) {
|
||||
Map<String, Object> params = genTable.getParams();
|
||||
if (CollUtil.isEmpty(params) || CollUtil.isEmpty(genTable.getColumns())) {
|
||||
return;
|
||||
}
|
||||
Set<String> validFields = new HashSet<>();
|
||||
genTable.getColumns().forEach(column -> {
|
||||
validFields.add(column.getColumnName());
|
||||
validFields.add(column.getJavaField());
|
||||
});
|
||||
validateOptionField(validFields, params.get(GenConstants.STATUS_FIELD), "状态字段");
|
||||
validateOptionField(validFields, params.get(GenConstants.SORT_FIELD), "排序字段");
|
||||
validateOptionField(validFields, params.get(GenConstants.TREE_ANCESTORS), "树祖级字段");
|
||||
validateOptionField(validFields, params.get(GenConstants.TREE_ORDER_FIELD), "树排序字段");
|
||||
Object uniqueFields = params.get(GenConstants.UNIQUE_FIELDS);
|
||||
if (uniqueFields instanceof Collection<?> collection) {
|
||||
for (Object field : collection) {
|
||||
validateOptionField(validFields, field, "组合唯一字段");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验单个选项字段。
|
||||
*
|
||||
* @param validFields 有效字段集合
|
||||
* @param field 待校验字段
|
||||
* @param label 字段显示名称
|
||||
*/
|
||||
private void validateOptionField(Set<String> validFields, Object field, String label) {
|
||||
if (ObjectUtil.isNull(field)) {
|
||||
return;
|
||||
}
|
||||
String fieldValue = Convert.toStr(field);
|
||||
if (StringUtils.isBlank(fieldValue)) {
|
||||
return;
|
||||
}
|
||||
if (!validFields.contains(fieldValue)) {
|
||||
throw new ServiceException(label + "不存在,请刷新字段后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化字段扩展配置。
|
||||
*
|
||||
* @param columns 表字段列表
|
||||
*/
|
||||
private void normalizeColumnOptions(List<GenTableColumn> columns) {
|
||||
if (CollUtil.isEmpty(columns)) {
|
||||
return;
|
||||
}
|
||||
for (GenTableColumn column : columns) {
|
||||
if (!column.isDictColumn()) {
|
||||
column.setDictType(StringUtils.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询业务表并补齐其列信息。
|
||||
*
|
||||
* @param tableId 业务表主键
|
||||
* @return 包含字段集合的业务表实体
|
||||
*/
|
||||
private GenTable getGenTable(Long tableId) {
|
||||
GenTable table = tableMapper.selectById(tableId);
|
||||
if (ObjectUtil.isNull(table)) {
|
||||
throw new ServiceException("业务表不存在");
|
||||
}
|
||||
fillTableColumns(Collections.singletonList(table));
|
||||
return table;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量填充业务表对应的字段列表。
|
||||
*
|
||||
* @param tables 业务表集合
|
||||
* @return 已填充字段信息的业务表集合
|
||||
*/
|
||||
private List<GenTable> fillTableColumns(List<GenTable> tables) {
|
||||
if (CollUtil.isEmpty(tables)) {
|
||||
return tables;
|
||||
}
|
||||
List<Long> tableIds = StreamUtils.toList(tables, GenTable::getTableId);
|
||||
List<GenTableColumn> columns = genTableColumnMapper.lambda()
|
||||
.in(GenTableColumn::getTableId, tableIds)
|
||||
.orderByAsc(GenTableColumn::getTableId)
|
||||
.orderByAsc(GenTableColumn::getSort)
|
||||
.list();
|
||||
Map<Long, List<GenTableColumn>> columnMap = StreamUtils.groupByKey(columns, GenTableColumn::getTableId);
|
||||
tables.forEach(table -> table.setColumns(columnMap.getOrDefault(table.getTableId(), new ArrayList<>())));
|
||||
return tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主键列信息
|
||||
*
|
||||
* @param table 业务表信息
|
||||
*/
|
||||
public void setPkColumn(GenTable table) {
|
||||
if (CollUtil.isEmpty(table.getColumns())) {
|
||||
throw new ServiceException("表【" + table.getTableName() + "】字段为空,请检查表结构");
|
||||
}
|
||||
for (GenTableColumn column : table.getColumns()) {
|
||||
if (column.isPk()) {
|
||||
table.setPkColumn(column);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ObjectUtil.isNull(table.getPkColumn())) {
|
||||
table.setPkColumn(table.getColumns().getFirst());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置代码生成其他选项值
|
||||
*
|
||||
* @param genTable 设置后的生成对象
|
||||
*/
|
||||
public void setTableFromOptions(GenTable genTable) {
|
||||
Dict paramsObj = JsonUtils.parseMap(genTable.getOptions());
|
||||
if (ObjectUtil.isNull(paramsObj)) {
|
||||
paramsObj = new Dict();
|
||||
}
|
||||
String treeCode = paramsObj.getStr(GenConstants.TREE_CODE);
|
||||
String treeParentCode = paramsObj.getStr(GenConstants.TREE_PARENT_CODE);
|
||||
String treeName = paramsObj.getStr(GenConstants.TREE_NAME);
|
||||
Long parentMenuId = Convert.toLong(TemplateEngineUtils.getParentMenuId(paramsObj));
|
||||
String parentMenuName = paramsObj.getStr(GenConstants.PARENT_MENU_NAME);
|
||||
Boolean enableExport = Convert.toBool(paramsObj.get(GenConstants.ENABLE_EXPORT), true);
|
||||
Boolean enableStatus = Convert.toBool(paramsObj.get(GenConstants.ENABLE_STATUS), false);
|
||||
String statusField = paramsObj.getStr(GenConstants.STATUS_FIELD);
|
||||
Boolean enableUnique = Convert.toBool(paramsObj.get(GenConstants.ENABLE_UNIQUE), false);
|
||||
List<String> uniqueFields = Convert.toList(String.class, paramsObj.get(GenConstants.UNIQUE_FIELDS));
|
||||
Boolean enableSort = Convert.toBool(paramsObj.get(GenConstants.ENABLE_SORT), false);
|
||||
String sortField = paramsObj.getStr(GenConstants.SORT_FIELD);
|
||||
String treeRootValue = paramsObj.getStr(GenConstants.TREE_ROOT_VALUE);
|
||||
String treeAncestorsField = paramsObj.getStr(GenConstants.TREE_ANCESTORS);
|
||||
String treeOrderField = paramsObj.getStr(GenConstants.TREE_ORDER_FIELD);
|
||||
|
||||
genTable.setTreeCode(treeCode);
|
||||
genTable.setTreeParentCode(treeParentCode);
|
||||
genTable.setTreeName(treeName);
|
||||
genTable.setParentMenuId(parentMenuId);
|
||||
genTable.setParentMenuName(parentMenuName);
|
||||
genTable.setEnableExport(enableExport);
|
||||
genTable.setEnableStatus(enableStatus);
|
||||
genTable.setStatusField(statusField);
|
||||
genTable.setEnableUnique(enableUnique);
|
||||
genTable.setUniqueFields(uniqueFields);
|
||||
genTable.setEnableSort(enableSort);
|
||||
genTable.setSortField(sortField);
|
||||
genTable.setTreeRootValue(treeRootValue);
|
||||
genTable.setTreeAncestorsField(treeAncestorsField);
|
||||
genTable.setTreeOrderField(treeOrderField);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package org.dromara.gen.service;
|
||||
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.gen.domain.GenTable;
|
||||
import org.dromara.gen.domain.GenTableColumn;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 业务 服务层
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface IGenTableService {
|
||||
|
||||
/**
|
||||
* 查询业务字段列表
|
||||
*
|
||||
* @param tableId 业务字段编号
|
||||
* @return 业务字段集合
|
||||
*/
|
||||
List<GenTableColumn> selectGenTableColumnListByTableId(Long tableId);
|
||||
|
||||
/**
|
||||
* 查询业务列表
|
||||
*
|
||||
* @param genTable 业务信息
|
||||
* @param pageQuery 分页参数
|
||||
* @return 业务分页集合
|
||||
*/
|
||||
PageResult<GenTable> selectPageGenTableList(GenTable genTable, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询据库列表
|
||||
*
|
||||
* @param genTable 业务信息
|
||||
* @param pageQuery 分页参数
|
||||
* @return 数据库表分页集合
|
||||
*/
|
||||
PageResult<GenTable> selectPageDbTableList(GenTable genTable, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询据库列表
|
||||
*
|
||||
* @param tableNames 表名称组
|
||||
* @param dataName 数据源名称
|
||||
* @return 数据库表集合
|
||||
*/
|
||||
List<GenTable> selectDbTableListByNames(String[] tableNames, String dataName);
|
||||
|
||||
/**
|
||||
* 查询业务信息
|
||||
*
|
||||
* @param id 业务ID
|
||||
* @return 业务信息
|
||||
*/
|
||||
GenTable selectGenTableById(Long id);
|
||||
|
||||
/**
|
||||
* 修改业务
|
||||
*
|
||||
* @param genTable 业务信息
|
||||
*/
|
||||
void updateGenTable(GenTable genTable);
|
||||
|
||||
/**
|
||||
* 删除业务信息
|
||||
*
|
||||
* @param tableIds 需要删除的表数据ID
|
||||
*/
|
||||
void deleteGenTableByIds(Long[] tableIds);
|
||||
|
||||
/**
|
||||
* 导入表结构
|
||||
*
|
||||
* @param tableList 导入表列表
|
||||
* @param dataName 数据源名称
|
||||
*/
|
||||
void importGenTable(List<GenTable> tableList, String dataName);
|
||||
|
||||
/**
|
||||
* 根据表名称查询列信息
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @param dataName 数据源名称
|
||||
* @return 列信息
|
||||
*/
|
||||
List<GenTableColumn> selectDbTableColumnsByName(String tableName, String dataName);
|
||||
|
||||
/**
|
||||
* 预览代码
|
||||
*
|
||||
* @param tableId 表编号
|
||||
* @return 预览数据列表
|
||||
*/
|
||||
Map<String, String> previewCode(Long tableId);
|
||||
|
||||
/**
|
||||
* 生成代码(下载方式)
|
||||
*
|
||||
* @param tableId 表名称
|
||||
* @return 数据
|
||||
*/
|
||||
byte[] downloadCode(Long tableId);
|
||||
|
||||
/**
|
||||
* 同步数据库
|
||||
*
|
||||
* @param tableId 表名称
|
||||
*/
|
||||
void synchDb(Long tableId);
|
||||
|
||||
/**
|
||||
* 批量生成代码(下载方式)
|
||||
*
|
||||
* @param tableIds 表ID数组
|
||||
* @return 数据
|
||||
*/
|
||||
byte[] downloadCode(String[] tableIds);
|
||||
|
||||
/**
|
||||
* 修改保存参数校验
|
||||
*
|
||||
* @param genTable 业务信息
|
||||
*/
|
||||
void validateEdit(GenTable genTable);
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
package org.dromara.gen.util;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.RegExUtils;
|
||||
import org.dromara.common.core.utils.SpringUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.gen.config.properties.GenProperties;
|
||||
import org.dromara.gen.constant.GenConstants;
|
||||
import org.dromara.gen.domain.GenTable;
|
||||
import org.dromara.gen.domain.GenTableColumn;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 代码生成器 工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class GenUtils {
|
||||
|
||||
private final static GenProperties PROPERTIES = SpringUtils.getBean(GenProperties.class);
|
||||
|
||||
/**
|
||||
* 初始化表信息
|
||||
*
|
||||
* @param genTable 待初始化的业务表对象
|
||||
*/
|
||||
public static void initTable(GenTable genTable) {
|
||||
genTable.setClassName(convertClassName(genTable.getTableName()));
|
||||
genTable.setPackageName(PROPERTIES.getPackageName());
|
||||
genTable.setModuleName(getModuleName(PROPERTIES.getPackageName()));
|
||||
genTable.setBusinessName(getBusinessName(genTable.getTableName()));
|
||||
genTable.setFunctionName(replaceText(genTable.getTableComment()));
|
||||
genTable.setFunctionAuthor(PROPERTIES.getAuthor());
|
||||
genTable.setFrontendType(GenConstants.FRONTEND_TYPE_VUE);
|
||||
genTable.setCreateTime(null);
|
||||
genTable.setUpdateTime(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化列属性字段
|
||||
*
|
||||
* @param column 待初始化的列对象
|
||||
* @param table 所属业务表对象
|
||||
*/
|
||||
public static void initColumnField(GenTableColumn column, GenTable table) {
|
||||
String dataType = normalizeDbType(getDbType(column.getColumnType()));
|
||||
// 统一转小写 避免有些数据库默认大写问题 如果需要特别书写方式 请在实体类增加注解标注别名
|
||||
String columnName = column.getColumnName().toLowerCase();
|
||||
Integer columnLength = getColumnLength(column.getColumnType());
|
||||
Integer columnScale = getColumnScale(column.getColumnType());
|
||||
column.setTableId(table.getTableId());
|
||||
column.setCreateTime(null);
|
||||
column.setUpdateTime(null);
|
||||
// 设置java字段名
|
||||
column.setJavaField(StringUtils.toCamelCase(columnName));
|
||||
// 设置默认类型
|
||||
column.setJavaType(GenConstants.TYPE_STRING);
|
||||
// 设置默认HTML类型
|
||||
column.setHtmlType(GenConstants.HTML_INPUT);
|
||||
column.setQueryType(GenConstants.QUERY_EQ);
|
||||
|
||||
if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) {
|
||||
// 字符串长度超过500设置为文本域
|
||||
String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
|
||||
if (isBooleanColumn(dataType, columnLength, columnScale, columnName)) {
|
||||
column.setJavaType(GenConstants.TYPE_BOOLEAN);
|
||||
htmlType = GenConstants.HTML_SWITCH;
|
||||
}
|
||||
column.setHtmlType(htmlType);
|
||||
} else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) {
|
||||
column.setJavaType(GenConstants.TYPE_DATE);
|
||||
column.setHtmlType(GenConstants.HTML_DATETIME);
|
||||
} else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) {
|
||||
column.setJavaType(resolveNumberJavaType(dataType, columnLength, columnScale, columnName));
|
||||
column.setHtmlType(GenConstants.TYPE_BOOLEAN.equals(column.getJavaType()) ? GenConstants.HTML_SWITCH : GenConstants.HTML_INPUT_NUMBER);
|
||||
}
|
||||
|
||||
// BO对象 默认插入勾选
|
||||
if (!arraysContains(GenConstants.COLUMNNAME_NOT_ADD, columnName) && !column.isPk()) {
|
||||
column.setIsInsert(GenConstants.REQUIRE);
|
||||
}
|
||||
// BO对象 默认编辑勾选
|
||||
if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName)) {
|
||||
column.setIsEdit(GenConstants.REQUIRE);
|
||||
}
|
||||
// VO对象 默认返回勾选
|
||||
if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName)) {
|
||||
column.setIsList(GenConstants.REQUIRE);
|
||||
}
|
||||
// BO对象 默认查询勾选
|
||||
if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) {
|
||||
column.setIsQuery(GenConstants.REQUIRE);
|
||||
}
|
||||
|
||||
// 查询字段类型
|
||||
if (StringUtils.endsWithIgnoreCase(columnName, "name")) {
|
||||
column.setQueryType(GenConstants.QUERY_LIKE);
|
||||
}
|
||||
if (GenConstants.HTML_DATETIME.equals(column.getHtmlType()) && column.isQuery()) {
|
||||
column.setQueryType(GenConstants.QUERY_BETWEEN);
|
||||
}
|
||||
// 状态字段设置单选框
|
||||
if (isSwitchColumn(columnName) || GenConstants.TYPE_BOOLEAN.equals(column.getJavaType())) {
|
||||
column.setHtmlType(GenConstants.HTML_SWITCH);
|
||||
}
|
||||
// 状态字段设置单选框/开关
|
||||
if (StringUtils.endsWithIgnoreCase(columnName, "status")) {
|
||||
column.setHtmlType(GenConstants.TYPE_BOOLEAN.equals(column.getJavaType()) ? GenConstants.HTML_SWITCH : GenConstants.HTML_RADIO);
|
||||
}
|
||||
// 类型&性别字段设置下拉框
|
||||
else if (StringUtils.endsWithIgnoreCase(columnName, "type")
|
||||
|| StringUtils.endsWithIgnoreCase(columnName, "sex")) {
|
||||
column.setHtmlType(GenConstants.HTML_SELECT);
|
||||
}
|
||||
// 排序字段设置数字输入控件
|
||||
else if (isSortColumn(columnName)) {
|
||||
column.setHtmlType(GenConstants.HTML_INPUT_NUMBER);
|
||||
}
|
||||
// 图片字段设置图片上传控件
|
||||
else if (StringUtils.endsWithAny(columnName, "image", "avatar", "logo", "picture")) {
|
||||
column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD);
|
||||
}
|
||||
// 文件字段设置文件上传控件
|
||||
else if (StringUtils.endsWithAny(columnName, "file", "attachment")) {
|
||||
column.setHtmlType(GenConstants.HTML_FILE_UPLOAD);
|
||||
}
|
||||
// 备注描述类字段设置文本域
|
||||
else if (StringUtils.endsWithAny(columnName, "remark", "description", "desc", "note")) {
|
||||
column.setHtmlType(GenConstants.HTML_TEXTAREA);
|
||||
}
|
||||
// 内容字段设置富文本控件
|
||||
else if (StringUtils.endsWithAny(columnName, "content", "html", "body")) {
|
||||
column.setHtmlType(GenConstants.HTML_EDITOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析数值类型对应的 Java 类型。
|
||||
*
|
||||
* @param dataType 数据库字段类型
|
||||
* @param columnLength 字段长度
|
||||
* @param columnScale 小数位数
|
||||
* @param columnName 字段名称
|
||||
* @return Java 类型
|
||||
*/
|
||||
private static String resolveNumberJavaType(String dataType, Integer columnLength, Integer columnScale, String columnName) {
|
||||
if (isBooleanColumn(dataType, columnLength, columnScale, columnName)) {
|
||||
return GenConstants.TYPE_BOOLEAN;
|
||||
}
|
||||
if (arraysContains(new String[]{"decimal", "dec", "fixed", "numeric", "money", "smallmoney"}, dataType)) {
|
||||
return GenConstants.TYPE_BIGDECIMAL;
|
||||
}
|
||||
if (arraysContains(new String[]{"float4", "real", "binary_float"}, dataType)) {
|
||||
return GenConstants.TYPE_FLOAT;
|
||||
}
|
||||
if (arraysContains(new String[]{"float", "float8", "double", "double precision", "binary_double"}, dataType)) {
|
||||
return GenConstants.TYPE_DOUBLE;
|
||||
}
|
||||
if (arraysContains(new String[]{"bigint", "int8", "bigserial", "serial8"}, dataType)) {
|
||||
return GenConstants.TYPE_LONG;
|
||||
}
|
||||
if (arraysContains(new String[]{"tinyint", "smallint", "mediumint", "int", "int2", "int4", "integer",
|
||||
"smallserial", "serial", "serial2", "serial4"}, dataType)) {
|
||||
return GenConstants.TYPE_INTEGER;
|
||||
}
|
||||
if (StringUtils.equals(dataType, "number")) {
|
||||
if (columnScale > 0) {
|
||||
return GenConstants.TYPE_BIGDECIMAL;
|
||||
}
|
||||
return resolveIntegerJavaType(columnLength);
|
||||
}
|
||||
return GenConstants.TYPE_LONG;
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化数据库字段类型,去除 MySQL 等数据库的类型修饰。
|
||||
*
|
||||
* @param dataType 数据库字段类型
|
||||
* @return 规范化后的数据库字段类型
|
||||
*/
|
||||
private static String normalizeDbType(String dataType) {
|
||||
return StringUtils.trim(dataType)
|
||||
.toLowerCase()
|
||||
.replace(" unsigned", "")
|
||||
.replace(" zerofill", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据整数字段长度解析 Java 类型。
|
||||
*
|
||||
* @param columnLength 字段长度
|
||||
* @return Java 类型
|
||||
*/
|
||||
private static String resolveIntegerJavaType(Integer columnLength) {
|
||||
if (columnLength > 0 && columnLength <= 9) {
|
||||
return GenConstants.TYPE_INTEGER;
|
||||
}
|
||||
return GenConstants.TYPE_LONG;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字段是否适合按布尔类型生成。
|
||||
*
|
||||
* @param dataType 数据库字段类型
|
||||
* @param columnLength 字段长度
|
||||
* @param columnScale 小数位数
|
||||
* @param columnName 字段名称
|
||||
* @return 是否布尔字段
|
||||
*/
|
||||
private static boolean isBooleanColumn(String dataType, Integer columnLength, Integer columnScale, String columnName) {
|
||||
if (columnScale > 0) {
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.equalsAny(dataType, "bit", "boolean", "bool")) {
|
||||
return true;
|
||||
}
|
||||
if (StringUtils.equalsAny(dataType, "tinyint", "number", "numeric", "decimal", "char", "nchar")
|
||||
&& columnLength == 1 && isSwitchColumn(columnName)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字段名称是否为开关类字段。
|
||||
*
|
||||
* @param columnName 字段名称
|
||||
* @return 是否开关类字段
|
||||
*/
|
||||
private static boolean isSwitchColumn(String columnName) {
|
||||
return StringUtils.endsWithAny(columnName, "status", "flag", "enabled", "disabled", "available", "visible")
|
||||
|| columnName.startsWith("is_")
|
||||
|| columnName.startsWith("has_")
|
||||
|| columnName.startsWith("enable_")
|
||||
|| columnName.startsWith("disable_");
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字段名称是否为排序字段。
|
||||
*
|
||||
* @param columnName 字段名称
|
||||
* @return 是否排序字段
|
||||
*/
|
||||
private static boolean isSortColumn(String columnName) {
|
||||
return StringUtils.endsWithAny(columnName, "sort", "order_num", "order", "rank", "seq", "sequence");
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验数组是否包含指定值
|
||||
*
|
||||
* @param arr 数组
|
||||
* @param targetValue 值
|
||||
* @return 是否包含
|
||||
*/
|
||||
public static boolean arraysContains(String[] arr, String targetValue) {
|
||||
return Arrays.asList(arr).contains(targetValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块名
|
||||
*
|
||||
* @param packageName 包名
|
||||
* @return 模块名
|
||||
*/
|
||||
public static String getModuleName(String packageName) {
|
||||
int lastIndex = packageName.lastIndexOf(".");
|
||||
int nameLength = packageName.length();
|
||||
return StringUtils.substring(packageName, lastIndex + 1, nameLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取业务名
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return 业务名
|
||||
*/
|
||||
public static String getBusinessName(String tableName) {
|
||||
int firstIndex = tableName.indexOf("_");
|
||||
int nameLength = tableName.length();
|
||||
String businessName = StringUtils.substring(tableName, firstIndex + 1, nameLength);
|
||||
businessName = StringUtils.toCamelCase(businessName);
|
||||
return businessName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表名转换成Java类名
|
||||
*
|
||||
* @param tableName 表名称
|
||||
* @return 类名
|
||||
*/
|
||||
public static String convertClassName(String tableName) {
|
||||
boolean autoRemovePre = PROPERTIES.isAutoRemovePre();
|
||||
String tablePrefix = PROPERTIES.getTablePrefix();
|
||||
if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) {
|
||||
String[] searchList = StringUtils.split(tablePrefix, StringUtils.SEPARATOR);
|
||||
tableName = replaceFirst(tableName, searchList);
|
||||
}
|
||||
return StringUtils.convertToCamelCase(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量替换前缀
|
||||
*
|
||||
* @param replacementm 替换值
|
||||
* @param searchList 替换列表
|
||||
* @return 去除命中前缀后的字符串
|
||||
*/
|
||||
public static String replaceFirst(String replacementm, String[] searchList) {
|
||||
String text = replacementm;
|
||||
for (String searchString : searchList) {
|
||||
if (replacementm.startsWith(searchString)) {
|
||||
text = StringUtils.removeStart(replacementm, searchString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关键字替换
|
||||
*
|
||||
* @param text 需要被替换的名字
|
||||
* @return 替换后的名字
|
||||
*/
|
||||
public static String replaceText(String text) {
|
||||
return RegExUtils.replaceAll(text, "(?:表|若依)", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库类型字段
|
||||
*
|
||||
* @param columnType 列类型
|
||||
* @return 截取后的列类型
|
||||
*/
|
||||
public static String getDbType(String columnType) {
|
||||
if (StringUtils.indexOf(columnType, "(") > 0) {
|
||||
return StringUtils.substringBefore(columnType, "(");
|
||||
} else {
|
||||
return columnType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段长度
|
||||
*
|
||||
* @param columnType 列类型
|
||||
* @return 字段长度,未声明长度时返回 0
|
||||
*/
|
||||
public static Integer getColumnLength(String columnType) {
|
||||
if (StringUtils.indexOf(columnType, "(") > 0) {
|
||||
String length = StringUtils.substringBetween(columnType, "(", ")").trim();
|
||||
// 处理 decimal(10,2) 这类带精度的类型,只取长度部分
|
||||
if (length.contains(",")) {
|
||||
length = StringUtils.substringBefore(length, ",").trim();
|
||||
}
|
||||
return Integer.valueOf(length);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字段精度
|
||||
*
|
||||
* @param columnType 列类型
|
||||
* @return 字段精度,未声明精度时返回 0
|
||||
*/
|
||||
public static Integer getColumnScale(String columnType) {
|
||||
if (StringUtils.indexOf(columnType, "(") > 0) {
|
||||
String length = StringUtils.substringBetween(columnType, "(", ")").trim();
|
||||
if (length.contains(",")) {
|
||||
return Integer.valueOf(StringUtils.substringAfter(length, ",").trim());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,763 @@
|
||||
package org.dromara.gen.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Dict;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.extra.template.TemplateConfig;
|
||||
import cn.hutool.extra.template.TemplateEngine;
|
||||
import cn.hutool.extra.template.TemplateUtil;
|
||||
import cn.hutool.extra.template.engine.freemarker.FreemarkerEngine;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.DateUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.json.utils.JsonUtils;
|
||||
import org.dromara.common.mybatis.enums.DataBaseType;
|
||||
import org.dromara.common.mybatis.helper.DataBaseHelper;
|
||||
import org.dromara.gen.constant.GenConstants;
|
||||
import org.dromara.gen.domain.GenTable;
|
||||
import org.dromara.gen.domain.GenTableColumn;
|
||||
import org.dromara.gen.util.template.PathNamedTemplate;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 模板引擎工具
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class TemplateEngineUtils {
|
||||
|
||||
/**
|
||||
* 项目空间路径
|
||||
*/
|
||||
private static final String PROJECT_PATH = "main/java";
|
||||
|
||||
/**
|
||||
* mybatis空间路径
|
||||
*/
|
||||
private static final String MYBATIS_PATH = "main/resources/mapper";
|
||||
|
||||
/**
|
||||
* 默认上级菜单,系统工具
|
||||
*/
|
||||
private static final String DEFAULT_PARENT_MENU_ID = "1761400000000000003";
|
||||
|
||||
// 模板引擎
|
||||
private static final TemplateEngine TEMPLATE_ENGINE;
|
||||
private static final Map<String, PathNamedTemplate> TEMPLATE_MAPPER;
|
||||
private static final ResourcePatternResolver RESOURCE_PATTERN_RESOLVER = new PathMatchingResourcePatternResolver();
|
||||
|
||||
static {
|
||||
// 模板引擎初始化
|
||||
TemplateConfig templateConfig = new TemplateConfig(StandardCharsets.UTF_8, StringUtils.EMPTY, TemplateConfig.ResourceMode.CLASSPATH);
|
||||
templateConfig.setCustomEngine(FreemarkerEngine.class);
|
||||
TEMPLATE_ENGINE = TemplateUtil.createEngine(templateConfig);
|
||||
// 禁用数字千分位格式化 避免 Long 类型的 ID 渲染是被加上逗号
|
||||
if (TEMPLATE_ENGINE instanceof FreemarkerEngine freemarkerEngine) {
|
||||
freemarkerEngine.getConfiguration().setNumberFormat("computer");
|
||||
}
|
||||
TEMPLATE_MAPPER = PathNamedTemplate.form(TEMPLATE_ENGINE, GenConstants.TEMPLATE_PATHS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建模板上下文
|
||||
*
|
||||
* @param contextInit 模板上下文初始化函数
|
||||
* @return 模板上下文
|
||||
*/
|
||||
public static Dict buildContext(Consumer<Dict> contextInit) {
|
||||
Dict context = new Dict();
|
||||
contextInit.accept(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建模板上下文
|
||||
*
|
||||
* @param genTable 代码生成业务表对象
|
||||
* @return 模板上下文
|
||||
*/
|
||||
public static Dict buildContext(GenTable genTable) {
|
||||
// 构建上下文
|
||||
Dict context = new Dict();
|
||||
String moduleName = genTable.getModuleName();
|
||||
String businessName = genTable.getBusinessName();
|
||||
String packageName = genTable.getPackageName();
|
||||
String tplCategory = genTable.getTplCategory();
|
||||
String functionName = genTable.getFunctionName();
|
||||
|
||||
context.put("tplCategory", genTable.getTplCategory());
|
||||
context.put("frontendType", resolveFrontendType(genTable.getFrontendType()));
|
||||
context.put("tableName", genTable.getTableName());
|
||||
context.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
|
||||
context.put("ClassName", genTable.getClassName());
|
||||
context.put("className", StringUtils.uncapitalize(genTable.getClassName()));
|
||||
context.put("moduleName", moduleName);
|
||||
context.put("BusinessName", StringUtils.capitalize(businessName));
|
||||
context.put("businessName", businessName);
|
||||
context.put("basePackage", getPackagePrefix(packageName));
|
||||
context.put("packageName", packageName);
|
||||
context.put("author", genTable.getFunctionAuthor());
|
||||
context.put("datetime", DateUtils.now());
|
||||
context.put("pkColumn", genTable.getPkColumn());
|
||||
String dicts = getDicts(genTable);
|
||||
context.put("importList", getImportList(genTable));
|
||||
context.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
|
||||
context.put("columns", genTable.getColumns());
|
||||
context.put("table", genTable);
|
||||
context.put("dicts", dicts);
|
||||
context.put("dictsNoSymbol", StringUtils.replace(dicts, "'", StringUtils.EMPTY));
|
||||
setColumnFeatureContext(context, genTable, dicts);
|
||||
// 向模板上下文写入菜单相关变量
|
||||
String options = genTable.getOptions();
|
||||
Dict paramsObj = JsonUtils.parseMap(options);
|
||||
if (ObjectUtil.isNull(paramsObj)) {
|
||||
paramsObj = new Dict();
|
||||
}
|
||||
String parentMenuId = getParentMenuId(paramsObj);
|
||||
context.put("parentMenuId", parentMenuId);
|
||||
boolean enableExport = getBooleanOption(paramsObj, GenConstants.ENABLE_EXPORT, true);
|
||||
boolean enableStatus = getBooleanOption(paramsObj, GenConstants.ENABLE_STATUS, false);
|
||||
boolean enableUnique = getBooleanOption(paramsObj, GenConstants.ENABLE_UNIQUE, false);
|
||||
boolean enableSort = getBooleanOption(paramsObj, GenConstants.ENABLE_SORT, false);
|
||||
GenTableColumn statusColumn = getColumn(genTable, paramsObj.getStr(GenConstants.STATUS_FIELD));
|
||||
GenTableColumn sortColumn = getColumn(genTable, paramsObj.getStr(GenConstants.SORT_FIELD));
|
||||
List<GenTableColumn> uniqueColumns = getColumns(genTable, paramsObj.get(GenConstants.UNIQUE_FIELDS));
|
||||
context.put("enableExport", enableExport);
|
||||
context.put("enableStatus", enableStatus && ObjectUtil.isNotNull(statusColumn));
|
||||
context.put("statusColumn", statusColumn);
|
||||
context.put("statusField", ObjectUtil.isNotNull(statusColumn) ? statusColumn.getJavaField() : StringUtils.EMPTY);
|
||||
context.put("enableUnique", enableUnique && CollUtil.isNotEmpty(uniqueColumns));
|
||||
context.put("uniqueColumns", uniqueColumns);
|
||||
context.put("enableSort", enableSort && ObjectUtil.isNotNull(sortColumn));
|
||||
context.put("sortColumn", sortColumn);
|
||||
context.put("sortField", ObjectUtil.isNotNull(sortColumn) ? sortColumn.getJavaField() : StringUtils.EMPTY);
|
||||
context.put("statusActiveValue", ObjectUtil.isNotNull(statusColumn) ? statusColumn.getSwitchActiveValue() : StringUtils.EMPTY);
|
||||
context.put("statusInactiveValue", ObjectUtil.isNotNull(statusColumn) ? statusColumn.getSwitchInactiveValue() : StringUtils.EMPTY);
|
||||
|
||||
// 向树形模板上下文写入树字段相关变量
|
||||
if (GenConstants.TPL_TREE.equals(tplCategory)) {
|
||||
setTreeContext(context, genTable, paramsObj);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入模板中常用的列派生开关,避免在模板内反复扫描和处理。
|
||||
*
|
||||
* @param context 模板上下文
|
||||
* @param genTable 代码生成业务表对象
|
||||
* @param dicts 字典类型表达式
|
||||
*/
|
||||
private static void setColumnFeatureContext(Dict context, GenTable genTable, String dicts) {
|
||||
boolean hasBetween = false;
|
||||
boolean needImagePreview = false;
|
||||
boolean needImageUpload = false;
|
||||
boolean needFileUpload = false;
|
||||
boolean needEditor = false;
|
||||
boolean needCheckbox = false;
|
||||
boolean needSelect = false;
|
||||
boolean needTextArea = false;
|
||||
boolean needDigit = false;
|
||||
boolean needDateField = false;
|
||||
boolean needSwitchField = false;
|
||||
boolean needParseTime = false;
|
||||
String firstTreeListField = StringUtils.EMPTY;
|
||||
for (GenTableColumn column : genTable.getColumns()) {
|
||||
boolean writable = column.isInsert() || column.isEdit();
|
||||
hasBetween = hasBetween || column.isQuery() && StringUtils.equals(column.getQueryType(), GenConstants.QUERY_BETWEEN);
|
||||
needImagePreview = needImagePreview || column.isList() && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_IMAGE_UPLOAD);
|
||||
needImageUpload = needImageUpload || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_IMAGE_UPLOAD);
|
||||
needFileUpload = needFileUpload || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_FILE_UPLOAD);
|
||||
needEditor = needEditor || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_EDITOR);
|
||||
needCheckbox = needCheckbox || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_CHECKBOX);
|
||||
needSelect = needSelect || (writable || column.isQuery()) && StringUtils.equalsAny(column.getHtmlType(),
|
||||
GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_SWITCH);
|
||||
needTextArea = needTextArea || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_TEXTAREA);
|
||||
needDigit = needDigit || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_INPUT_NUMBER);
|
||||
needDateField = needDateField || writable && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_DATETIME);
|
||||
needSwitchField = needSwitchField || (writable || column.isList()) && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_SWITCH);
|
||||
needParseTime = needParseTime || column.isList() && StringUtils.equals(column.getHtmlType(), GenConstants.HTML_DATETIME);
|
||||
if (StringUtils.isBlank(firstTreeListField) && column.isList()) {
|
||||
firstTreeListField = column.getJavaField();
|
||||
}
|
||||
}
|
||||
boolean needDateRange = hasDateRangeQuery(genTable);
|
||||
context.put("hasBetween", hasBetween);
|
||||
context.put("needAddDateRange", needDateRange);
|
||||
context.put("needDateRange", needDateRange);
|
||||
context.put("needDict", StringUtils.isNotBlank(dicts));
|
||||
context.put("needImagePreview", needImagePreview);
|
||||
context.put("needImageUpload", needImageUpload);
|
||||
context.put("needFileUpload", needFileUpload);
|
||||
context.put("needEditor", needEditor);
|
||||
context.put("needCheckbox", needCheckbox);
|
||||
context.put("needSelect", needSelect);
|
||||
context.put("needTextArea", needTextArea);
|
||||
context.put("needDigit", needDigit);
|
||||
context.put("needDateField", needDateField);
|
||||
context.put("needSwitchField", needSwitchField);
|
||||
context.put("needParseTime", needParseTime);
|
||||
context.put("firstTreeListField", firstTreeListField);
|
||||
}
|
||||
|
||||
private static boolean hasDateRangeQuery(GenTable genTable) {
|
||||
for (GenTableColumn column : genTable.getColumns()) {
|
||||
if (column.isDateRangeQuery()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向树形模板上下文写入树字段相关变量。
|
||||
*
|
||||
* @param context 模板上下文
|
||||
* @param genTable 代码生成业务表对象
|
||||
* @param paramsObj 已解析的 options 参数(避免重复解析)
|
||||
*/
|
||||
public static void setTreeContext(Dict context, GenTable genTable, Dict paramsObj) {
|
||||
String treeCode = getTreeCode(paramsObj);
|
||||
String treeParentCode = getTreeParentCode(paramsObj);
|
||||
String treeName = getTreeName(paramsObj);
|
||||
GenTableColumn treeParentColumn = getColumn(genTable, paramsObj.getStr(GenConstants.TREE_PARENT_CODE));
|
||||
GenTableColumn treeAncestorsColumn = getColumn(genTable, paramsObj.getStr(GenConstants.TREE_ANCESTORS));
|
||||
GenTableColumn treeOrderColumn = getColumn(genTable, paramsObj.getStr(GenConstants.TREE_ORDER_FIELD));
|
||||
String treeRootValue = getTreeRootValue(paramsObj, treeParentColumn);
|
||||
|
||||
context.put("treeCode", treeCode);
|
||||
context.put("treeParentCode", treeParentCode);
|
||||
context.put("treeName", treeName);
|
||||
context.put("treeParentColumn", treeParentColumn);
|
||||
context.put("treeAncestorsField", ObjectUtil.isNotNull(treeAncestorsColumn) ? treeAncestorsColumn.getJavaField() : StringUtils.EMPTY);
|
||||
context.put("treeOrderField", ObjectUtil.isNotNull(treeOrderColumn) ? treeOrderColumn.getJavaField() : StringUtils.EMPTY);
|
||||
context.put("treeOrderColumn", treeOrderColumn);
|
||||
context.put("treeRootValue", treeRootValue);
|
||||
context.put("treeRootValueJavaLiteral", getJavaLiteral(treeParentColumn, treeRootValue));
|
||||
context.put("treeRootValueTsLiteral", getTsLiteral(treeParentColumn, treeRootValue));
|
||||
context.put("treeParentCap", StringUtils.capitalize(treeParentCode));
|
||||
context.put("treeAncestorsCap", StringUtils.capitalize(ObjectUtil.isNotNull(treeAncestorsColumn) ? treeAncestorsColumn.getJavaField() : StringUtils.EMPTY));
|
||||
context.put("treeOrderCap", StringUtils.capitalize(ObjectUtil.isNotNull(treeOrderColumn) ? treeOrderColumn.getJavaField() : StringUtils.EMPTY));
|
||||
String expandTreeName = paramsObj.getStr(GenConstants.TREE_NAME);
|
||||
int expandColumn = 0;
|
||||
for (GenTableColumn column : genTable.getColumns()) {
|
||||
if (column.isList()) {
|
||||
expandColumn++;
|
||||
if (column.getColumnName().equals(expandTreeName)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
context.put("expandColumn", expandColumn);
|
||||
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
|
||||
context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE));
|
||||
}
|
||||
if (paramsObj.containsKey(GenConstants.TREE_NAME)) {
|
||||
context.put("tree_name", paramsObj.get(GenConstants.TREE_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板信息
|
||||
*
|
||||
* @return 模板列表
|
||||
*/
|
||||
public static List<PathNamedTemplate> getTemplateList(String tplCategory, String dsName) {
|
||||
return getTemplateList(tplCategory, dsName, GenConstants.FRONTEND_TYPE_VUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板信息
|
||||
*
|
||||
* @param tplCategory 前端页面模板分类
|
||||
* @param dsName 数据源名称
|
||||
* @param frontendType 前端模板类型
|
||||
* @return 模板列表
|
||||
*/
|
||||
public static List<PathNamedTemplate> getTemplateList(String tplCategory, String dsName, String frontendType) {
|
||||
List<PathNamedTemplate> templates = new ArrayList<>();
|
||||
// 后端源码模板
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.JAVA_DOMAIN_TEMPLATE_PATH));
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.JAVA_VO_TEMPLATE_PATH));
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.JAVA_BO_TEMPLATE_PATH));
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.JAVA_MAPPER_TEMPLATE_PATH));
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.JAVA_SERVICE_TEMPLATE_PATH));
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.JAVA_SERVICE_IMPL_TEMPLATE_PATH));
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.JAVA_CONTROLLER_TEMPLATE_PATH));
|
||||
// MyBatis MapperXML 模板
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.XML_MAPPER_TEMPLATE_PATH));
|
||||
// 前端 API 与类型模板
|
||||
templates.add(getTemplate(getFrontendApiTemplatePath(frontendType)));
|
||||
templates.add(getTemplate(getFrontendTypesTemplatePath(frontendType)));
|
||||
// 数据库模板
|
||||
DataBaseType dataBaseType = DataBaseHelper.getDataBaseType(dsName);
|
||||
if (dataBaseType.isOracle()) {
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.SQL_ORACLE_TEMPLATE_PATH));
|
||||
} else if (dataBaseType.isPostgreSql()) {
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.SQL_POSTGRES_TEMPLATE_PATH));
|
||||
} else if (dataBaseType.isSqlServer()) {
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.SQL_SQLSERVER_TEMPLATE_PATH));
|
||||
} else {
|
||||
// 默认使用MySQL模板
|
||||
templates.add(TEMPLATE_MAPPER.get(GenConstants.SQL_MYSQL_TEMPLATE_PATH));
|
||||
}
|
||||
// 前端页面模板
|
||||
if (GenConstants.TPL_CRUD.equals(tplCategory)) {
|
||||
templates.add(getTemplate(getFrontendIndexTemplatePath(frontendType)));
|
||||
} else if (GenConstants.TPL_TREE.equals(tplCategory)) {
|
||||
templates.add(getTemplate(getFrontendIndexTreeTemplatePath(frontendType)));
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模板,支持按前端模板类型动态加载模板根目录下的模板。
|
||||
*
|
||||
* @param templatePath 模板路径
|
||||
* @return 带路径名的模板
|
||||
*/
|
||||
private static PathNamedTemplate getTemplate(String templatePath) {
|
||||
PathNamedTemplate template = TEMPLATE_MAPPER.get(templatePath);
|
||||
if (ObjectUtil.isNotNull(template)) {
|
||||
return template;
|
||||
}
|
||||
return PathNamedTemplate.form(TEMPLATE_ENGINE, templatePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名
|
||||
*
|
||||
* @param template 模板路径
|
||||
* @param genTable 代码生成业务表对象
|
||||
* @return 模板对应的目标文件相对路径
|
||||
*/
|
||||
public static String getFileName(String template, GenTable genTable) {
|
||||
// 文件名称
|
||||
String fileName = "";
|
||||
// 包路径
|
||||
String packageName = genTable.getPackageName();
|
||||
// 模块名
|
||||
String moduleName = genTable.getModuleName();
|
||||
// 大写类名
|
||||
String className = genTable.getClassName();
|
||||
// 业务名称
|
||||
String businessName = genTable.getBusinessName();
|
||||
|
||||
String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
|
||||
String mybatisPath = MYBATIS_PATH + "/" + moduleName;
|
||||
String frontendPath = getFrontendPath(genTable.getFrontendType());
|
||||
String frontendPagePath = getFrontendPagePath(genTable.getFrontendType());
|
||||
// templatePath
|
||||
// genFilePathFormat
|
||||
if (template.contains("domain.java.")) {
|
||||
fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
|
||||
} else if (template.contains("vo.java.")) {
|
||||
fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className);
|
||||
} else if (template.contains("bo.java.")) {
|
||||
fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className);
|
||||
} else if (template.contains("mapper.java.")) {
|
||||
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
|
||||
} else if (template.contains("service.java.")) {
|
||||
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
|
||||
} else if (template.contains("serviceImpl.java.")) {
|
||||
fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
|
||||
} else if (template.contains("controller.java.")) {
|
||||
fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
|
||||
} else if (template.contains("mapper.xml.")) {
|
||||
fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
|
||||
} else if (template.contains("sql.")) {
|
||||
fileName = businessName + "Menu.sql";
|
||||
} else if (template.contains("api.ts.")) {
|
||||
fileName = StringUtils.format("{}/api/{}/{}/index.ts", frontendPath, moduleName, businessName);
|
||||
} else if (template.contains("types.ts.")) {
|
||||
fileName = StringUtils.format("{}/api/{}/{}/types.ts", frontendPath, moduleName, businessName);
|
||||
} else if (isFrontendPageTemplate(template, GenConstants.FRONTEND_INDEX_TEMPLATE_PREFIX)) {
|
||||
fileName = StringUtils.format("{}/{}/{}/{}/index.{}", frontendPath, frontendPagePath, moduleName, businessName,
|
||||
getFrontendPageExtension(template, GenConstants.FRONTEND_INDEX_TEMPLATE_PREFIX));
|
||||
} else if (isFrontendPageTemplate(template, GenConstants.FRONTEND_INDEX_TREE_TEMPLATE_PREFIX)) {
|
||||
fileName = StringUtils.format("{}/{}/{}/{}/index.{}", frontendPath, frontendPagePath, moduleName, businessName,
|
||||
getFrontendPageExtension(template, GenConstants.FRONTEND_INDEX_TREE_TEMPLATE_PREFIX));
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前端输出目录。
|
||||
*
|
||||
* @param frontendType 前端模板类型
|
||||
* @return 前端输出目录
|
||||
*/
|
||||
private static String getFrontendPath(String frontendType) {
|
||||
return resolveFrontendType(frontendType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前端页面输出目录。
|
||||
*
|
||||
* @param frontendType 前端模板类型
|
||||
* @return 页面输出目录
|
||||
*/
|
||||
private static String getFrontendPagePath(String frontendType) {
|
||||
return GenConstants.FRONTEND_TYPE_REACT.equals(resolveFrontendType(frontendType)) ? "pages" : "views";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前端页面文件扩展名。
|
||||
*
|
||||
* @param template 模板路径
|
||||
* @param templatePrefix 模板文件名前缀
|
||||
* @return 文件扩展名
|
||||
*/
|
||||
private static String getFrontendPageExtension(String template, String templatePrefix) {
|
||||
String fileName = getTemplateFileName(template);
|
||||
int suffixIndex = fileName.lastIndexOf('.');
|
||||
String withoutTemplateSuffix = suffixIndex >= 0 ? fileName.substring(0, suffixIndex) : fileName;
|
||||
return withoutTemplateSuffix.substring((templatePrefix + ".").length());
|
||||
}
|
||||
|
||||
private static String getFrontendApiTemplatePath(String frontendType) {
|
||||
return getFrontendTemplatePath(frontendType, GenConstants.FRONTEND_API_TEMPLATE_NAME);
|
||||
}
|
||||
|
||||
private static String getFrontendTypesTemplatePath(String frontendType) {
|
||||
return getFrontendTemplatePath(frontendType, GenConstants.FRONTEND_TYPES_TEMPLATE_NAME);
|
||||
}
|
||||
|
||||
private static String getFrontendIndexTemplatePath(String frontendType) {
|
||||
return getFrontendPageTemplatePath(frontendType, GenConstants.FRONTEND_INDEX_TEMPLATE_PREFIX);
|
||||
}
|
||||
|
||||
private static String getFrontendIndexTreeTemplatePath(String frontendType) {
|
||||
return getFrontendPageTemplatePath(frontendType, GenConstants.FRONTEND_INDEX_TREE_TEMPLATE_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析前端模板类型。
|
||||
*
|
||||
* @param frontendType 前端模板类型
|
||||
* @return 已规范化的模板目录名
|
||||
*/
|
||||
private static String resolveFrontendType(String frontendType) {
|
||||
String type = StringUtils.blankToDefault(frontendType, GenConstants.FRONTEND_TYPE_VUE);
|
||||
if (!type.matches("[A-Za-z0-9_-]+")) {
|
||||
throw new ServiceException("前端模板类型仅支持字母、数字、下划线和中划线");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private static String getFrontendTemplatePath(String frontendType, String templateName) {
|
||||
return StringUtils.format("{}/{}/{}", GenConstants.TEMPLATE_ROOT_PATH, resolveFrontendType(frontendType), templateName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取前端页面模板路径,页面输出扩展名由模板文件名决定。
|
||||
*
|
||||
* @param frontendType 前端模板类型
|
||||
* @param templatePrefix 页面模板文件名前缀
|
||||
* @return 页面模板路径
|
||||
*/
|
||||
private static String getFrontendPageTemplatePath(String frontendType, String templatePrefix) {
|
||||
String type = resolveFrontendType(frontendType);
|
||||
String templatePathPattern = getFrontendPageTemplatePathPattern(type, templatePrefix);
|
||||
String resourcePattern = GenConstants.TEMPLATE_RESOURCE_PREFIX + templatePathPattern;
|
||||
try {
|
||||
Resource[] resources = RESOURCE_PATTERN_RESOLVER.getResources(resourcePattern);
|
||||
if (resources.length == 0) {
|
||||
throw new ServiceException(StringUtils.format("未找到前端模板: {}", templatePathPattern));
|
||||
}
|
||||
return Arrays.stream(resources)
|
||||
.map(Resource::getFilename)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.sorted()
|
||||
.findFirst()
|
||||
.map(fileName -> StringUtils.format("{}/{}/{}", GenConstants.TEMPLATE_ROOT_PATH, type, fileName))
|
||||
.orElseThrow(() -> new ServiceException(StringUtils.format("未找到前端模板: {}", templatePathPattern)));
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException(StringUtils.format("读取前端模板失败: {}", templatePathPattern), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFrontendPageTemplatePathPattern(String frontendType, String templatePrefix) {
|
||||
return StringUtils.format("{}/{}/{}{}", GenConstants.TEMPLATE_ROOT_PATH, frontendType,
|
||||
templatePrefix, GenConstants.TEMPLATE_PAGE_PATTERN_SUFFIX);
|
||||
}
|
||||
|
||||
private static boolean isFrontendPageTemplate(String template, String templatePrefix) {
|
||||
String fileName = getTemplateFileName(template);
|
||||
return fileName.startsWith(templatePrefix + ".") && fileName.endsWith(GenConstants.TEMPLATE_FILE_SUFFIX);
|
||||
}
|
||||
|
||||
private static String getTemplateFileName(String template) {
|
||||
int index = Math.max(template.lastIndexOf('/'), template.lastIndexOf('\\'));
|
||||
return index >= 0 ? template.substring(index + 1) : template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取包前缀
|
||||
*
|
||||
* @param packageName 包名称
|
||||
* @return 包前缀名称
|
||||
*/
|
||||
public static String getPackagePrefix(String packageName) {
|
||||
int lastIndex = packageName.lastIndexOf(".");
|
||||
return StringUtils.substring(packageName, 0, lastIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据列类型获取导入包
|
||||
*
|
||||
* @param genTable 业务表对象
|
||||
* @return 返回需要导入的包列表
|
||||
*/
|
||||
public static HashSet<String> getImportList(GenTable genTable) {
|
||||
List<GenTableColumn> columns = genTable.getColumns();
|
||||
HashSet<String> importList = new HashSet<>();
|
||||
for (GenTableColumn column : columns) {
|
||||
if (needImportColumn(column) && GenConstants.TYPE_DATE.equals(column.getJavaType())) {
|
||||
importList.add("java.time.LocalDateTime");
|
||||
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
|
||||
} else if (needImportColumn(column) && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
|
||||
importList.add("java.math.BigDecimal");
|
||||
}
|
||||
}
|
||||
return importList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断列是否需要生成类型导入。
|
||||
*
|
||||
* @param column 列对象
|
||||
* @return 需要生成类型导入返回 {@code true}
|
||||
*/
|
||||
private static boolean needImportColumn(GenTableColumn column) {
|
||||
return !column.isSuperColumn() || column.isQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据列类型获取字典组
|
||||
*
|
||||
* @param genTable 业务表对象
|
||||
* @return 返回字典组
|
||||
*/
|
||||
public static String getDicts(GenTable genTable) {
|
||||
List<GenTableColumn> columns = genTable.getColumns();
|
||||
Set<String> dicts = new HashSet<>();
|
||||
addDicts(dicts, columns);
|
||||
return StringUtils.join(dicts, ", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加字典列表
|
||||
*
|
||||
* @param dicts 字典列表
|
||||
* @param columns 列集合
|
||||
*/
|
||||
public static void addDicts(Set<String> dicts, List<GenTableColumn> columns) {
|
||||
for (GenTableColumn column : columns) {
|
||||
if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny(
|
||||
column.getHtmlType(),
|
||||
GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX, GenConstants.HTML_SWITCH)) {
|
||||
dicts.add("'" + column.getDictType() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限前缀
|
||||
*
|
||||
* @param moduleName 模块名称
|
||||
* @param businessName 业务名称
|
||||
* @return 返回权限前缀
|
||||
*/
|
||||
public static String getPermissionPrefix(String moduleName, String businessName) {
|
||||
return StringUtils.format("{}:{}", moduleName, businessName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上级菜单ID字段
|
||||
*
|
||||
* @param paramsObj 生成其他选项
|
||||
* @return 上级菜单ID字段
|
||||
*/
|
||||
public static String getParentMenuId(Dict paramsObj) {
|
||||
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID)
|
||||
&& StringUtils.isNotEmpty(paramsObj.getStr(GenConstants.PARENT_MENU_ID))) {
|
||||
return paramsObj.getStr(GenConstants.PARENT_MENU_ID);
|
||||
}
|
||||
return DEFAULT_PARENT_MENU_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树编码
|
||||
*
|
||||
* @param paramsObj 生成其他选项
|
||||
* @return 树编码
|
||||
*/
|
||||
public static String getTreeCode(Map<String, Object> paramsObj) {
|
||||
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_CODE)) {
|
||||
return StringUtils.toCamelCase(Convert.toStr(paramsObj.get(GenConstants.TREE_CODE)));
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树父编码
|
||||
*
|
||||
* @param paramsObj 生成其他选项
|
||||
* @return 树父编码
|
||||
*/
|
||||
public static String getTreeParentCode(Dict paramsObj) {
|
||||
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
|
||||
return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_PARENT_CODE));
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树名称
|
||||
*
|
||||
* @param paramsObj 生成其他选项
|
||||
* @return 树名称
|
||||
*/
|
||||
public static String getTreeName(Dict paramsObj) {
|
||||
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_NAME)) {
|
||||
return StringUtils.toCamelCase(paramsObj.getStr(GenConstants.TREE_NAME));
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树根节点值。
|
||||
*
|
||||
* @param paramsObj 其他选项
|
||||
* @param treeParentColumn 父节点字段
|
||||
* @return 树根节点值
|
||||
*/
|
||||
public static String getTreeRootValue(Dict paramsObj, GenTableColumn treeParentColumn) {
|
||||
String defaultValue = "0";
|
||||
if (ObjectUtil.isNotNull(treeParentColumn) && StringUtils.equals(treeParentColumn.getJavaType(), GenConstants.TYPE_STRING)) {
|
||||
defaultValue = "0";
|
||||
}
|
||||
if (CollUtil.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.TREE_ROOT_VALUE)) {
|
||||
return StringUtils.blankToDefault(paramsObj.getStr(GenConstants.TREE_ROOT_VALUE), defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取布尔类型生成选项。
|
||||
*
|
||||
* @param paramsObj 生成选项
|
||||
* @param key 选项键
|
||||
* @param defaultValue 默认值
|
||||
* @return 选项值
|
||||
*/
|
||||
private static boolean getBooleanOption(Dict paramsObj, String key, boolean defaultValue) {
|
||||
if (CollUtil.isEmpty(paramsObj) || !paramsObj.containsKey(key)) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Convert.toBool(paramsObj.get(key), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段名查找业务表字段。
|
||||
*
|
||||
* @param genTable 业务表
|
||||
* @param field 字段名称或 Java 属性名
|
||||
* @return 业务表字段
|
||||
*/
|
||||
private static GenTableColumn getColumn(GenTable genTable, String field) {
|
||||
if (StringUtils.isBlank(field) || CollUtil.isEmpty(genTable.getColumns())) {
|
||||
return null;
|
||||
}
|
||||
for (GenTableColumn column : genTable.getColumns()) {
|
||||
if (StringUtils.equalsAny(field, column.getColumnName(), column.getJavaField())) {
|
||||
return column;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段配置查找业务表字段列表。
|
||||
*
|
||||
* @param genTable 业务表
|
||||
* @param fieldValues 字段配置值
|
||||
* @return 业务表字段列表
|
||||
*/
|
||||
private static List<GenTableColumn> getColumns(GenTable genTable, Object fieldValues) {
|
||||
List<String> fields = new ArrayList<>();
|
||||
if (fieldValues instanceof Collection<?> collection) {
|
||||
collection.stream().map(Convert::toStr).forEach(fields::add);
|
||||
} else if (ObjectUtil.isNotNull(fieldValues)) {
|
||||
fields.addAll(StringUtils.str2List(Convert.toStr(fieldValues), StringUtils.SEPARATOR, true, true));
|
||||
}
|
||||
List<GenTableColumn> columns = new ArrayList<>();
|
||||
for (String field : fields) {
|
||||
GenTableColumn column = getColumn(genTable, field);
|
||||
if (ObjectUtil.isNotNull(column)) {
|
||||
columns.add(column);
|
||||
}
|
||||
}
|
||||
return columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 Java 字面量。
|
||||
*
|
||||
* @param column 业务表字段
|
||||
* @param value 字段值
|
||||
* @return Java 字面量
|
||||
*/
|
||||
private static String getJavaLiteral(GenTableColumn column, String value) {
|
||||
if (ObjectUtil.isNull(column) || StringUtils.isBlank(value)) {
|
||||
return "null";
|
||||
}
|
||||
if (StringUtils.equals(column.getJavaType(), GenConstants.TYPE_LONG)) {
|
||||
return value + "L";
|
||||
}
|
||||
if (StringUtils.equals(column.getJavaType(), GenConstants.TYPE_FLOAT)) {
|
||||
return value + "F";
|
||||
}
|
||||
if (StringUtils.equals(column.getJavaType(), GenConstants.TYPE_DOUBLE)) {
|
||||
return value + "D";
|
||||
}
|
||||
if (StringUtils.equals(column.getJavaType(), GenConstants.TYPE_BIGDECIMAL)) {
|
||||
return "new java.math.BigDecimal(\"" + value + "\")";
|
||||
}
|
||||
if (StringUtils.equals(column.getJavaType(), GenConstants.TYPE_INTEGER)) {
|
||||
return value;
|
||||
}
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 TypeScript 字面量。
|
||||
*
|
||||
* @param column 业务表字段
|
||||
* @param value 字段值
|
||||
* @return TypeScript 字面量
|
||||
*/
|
||||
private static String getTsLiteral(GenTableColumn column, String value) {
|
||||
if (ObjectUtil.isNull(column) || StringUtils.isBlank(value)) {
|
||||
return "undefined";
|
||||
}
|
||||
if (StringUtils.equalsAny(column.getJavaType(), GenConstants.TYPE_LONG, GenConstants.TYPE_INTEGER,
|
||||
GenConstants.TYPE_DOUBLE, GenConstants.TYPE_FLOAT, GenConstants.TYPE_BIGDECIMAL)) {
|
||||
return value;
|
||||
}
|
||||
return "'" + value + "'";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package org.dromara.gen.util.template;
|
||||
|
||||
import cn.hutool.extra.template.Template;
|
||||
import cn.hutool.extra.template.TemplateEngine;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 基于路径命名的模板委托实现
|
||||
*
|
||||
* @author 秋辞未寒
|
||||
*/
|
||||
public class PathNamedTemplate implements Template {
|
||||
|
||||
@Getter
|
||||
private final String pathName;
|
||||
|
||||
private final Template delegate;
|
||||
|
||||
/**
|
||||
* 创建基于路径命名的模板。
|
||||
*
|
||||
* @param pathName 路径名称
|
||||
* @param delegate 委托模板
|
||||
*/
|
||||
private PathNamedTemplate(String pathName, Template delegate) {
|
||||
this.pathName = pathName;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模板到字符输出器。
|
||||
*
|
||||
* @param bindingMap 模板绑定数据
|
||||
* @param writer 字符输出器
|
||||
*/
|
||||
@Override
|
||||
public void render(Map<?, ?> bindingMap, Writer writer) {
|
||||
delegate.render(bindingMap, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模板到输出流。
|
||||
*
|
||||
* @param bindingMap 模板绑定数据
|
||||
* @param out 输出流
|
||||
*/
|
||||
@Override
|
||||
public void render(Map<?, ?> bindingMap, OutputStream out) {
|
||||
delegate.render(bindingMap, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模板到文件。
|
||||
*
|
||||
* @param bindingMap 模板绑定数据
|
||||
* @param file 输出文件
|
||||
*/
|
||||
@Override
|
||||
public void render(Map<?, ?> bindingMap, File file) {
|
||||
delegate.render(bindingMap, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模板为字符串。
|
||||
*
|
||||
* @param bindingMap 模板绑定数据
|
||||
* @return 渲染结果
|
||||
*/
|
||||
@Override
|
||||
public String render(Map<?, ?> bindingMap) {
|
||||
return delegate.render(bindingMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建带路径名称的模板。
|
||||
*
|
||||
* @param pathName 路径名称
|
||||
* @param delegate 模板对象
|
||||
* @return 带路径名称的模板
|
||||
*/
|
||||
public static PathNamedTemplate form(String pathName, Template delegate) {
|
||||
return new PathNamedTemplate(pathName, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板引擎和路径名称构建模板。
|
||||
*
|
||||
* @param templateEngine 模板引擎
|
||||
* @param pathName 路径名称
|
||||
* @return 带路径名称的模板
|
||||
*/
|
||||
public static PathNamedTemplate form(TemplateEngine templateEngine, String pathName) {
|
||||
return new PathNamedTemplate(pathName, templateEngine.getTemplate(pathName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板引擎和路径名称集合构建模板映射。
|
||||
*
|
||||
* @param templateEngine 模板引擎
|
||||
* @param pathNames 路径名称集合
|
||||
* @return 模板映射
|
||||
*/
|
||||
public static Map<String, PathNamedTemplate> form(TemplateEngine templateEngine, Set<String> pathNames) {
|
||||
Map<String, PathNamedTemplate> result = new HashMap<>();
|
||||
for (String pathName : pathNames) {
|
||||
PathNamedTemplate pathNamedTemplate = form(templateEngine, pathName);
|
||||
result.put(pathName, pathNamedTemplate);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package ${packageName}.domain.bo;
|
||||
|
||||
import ${packageName}.domain.${ClassName};
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
<#if hasBetween>
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
</#if>
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.*;
|
||||
<#list importList as import>
|
||||
import ${import};
|
||||
</#list>
|
||||
|
||||
/**
|
||||
* ${functionName}业务对象 ${tableName}
|
||||
*
|
||||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@AutoMapper(target = ${ClassName}.class, reverseConvertGenerate = false)
|
||||
public class ${ClassName}Bo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
<#list columns as column>
|
||||
<#if column.query || (!table.isSuperColumn(column.javaField) && (column.insert || column.edit))>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
<#if column.insert && column.edit>
|
||||
<#assign Group = "AddGroup.class, EditGroup.class">
|
||||
<#elseif column.insert>
|
||||
<#assign Group = "AddGroup.class">
|
||||
<#elseif column.edit>
|
||||
<#assign Group = "EditGroup.class">
|
||||
</#if>
|
||||
<#if column.required && !table.isSuperColumn(column.javaField) && (column.insert || column.edit)>
|
||||
<#if column.javaType == 'String'>
|
||||
@NotBlank(message = "${column.columnComment}不能为空", groups = { ${Group} })
|
||||
<#else>
|
||||
@NotNull(message = "${column.columnComment}不能为空", groups = { ${Group} })
|
||||
</#if>
|
||||
</#if>
|
||||
private ${column.javaType} ${column.javaField};
|
||||
|
||||
</#if>
|
||||
</#list>
|
||||
<#if hasBetween>
|
||||
/**
|
||||
* 查询参数
|
||||
*/
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
</#if>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package ${packageName}.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
<#if enableExport>
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
</#if>
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.redis.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
<#if enableExport>
|
||||
import org.dromara.common.excel.utils.ExcelBuilder;
|
||||
</#if>
|
||||
import ${packageName}.domain.vo.${ClassName}Vo;
|
||||
import ${packageName}.domain.bo.${ClassName}Bo;
|
||||
import ${packageName}.service.I${ClassName}Service;
|
||||
<#if table.crud>
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
<#elseif table.tree>
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* ${functionName}
|
||||
*
|
||||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/${moduleName}/${businessName}")
|
||||
public class ${ClassName}Controller extends BaseController {
|
||||
|
||||
private final I${ClassName}Service ${className}Service;
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:list")
|
||||
@GetMapping("/list")
|
||||
<#if table.crud>
|
||||
public R<PageResult<${ClassName}Vo>> list(${ClassName}Bo bo, PageQuery pageQuery) {
|
||||
return R.ok(${className}Service.queryPageList(bo, pageQuery));
|
||||
}
|
||||
<#elseif table.tree>
|
||||
public R<List<${ClassName}Vo>> list(${ClassName}Bo bo) {
|
||||
List<${ClassName}Vo> list = ${className}Service.queryList(bo);
|
||||
return R.ok(list);
|
||||
}
|
||||
</#if>
|
||||
|
||||
<#if enableExport>
|
||||
/**
|
||||
* 导出${functionName}列表
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:export")
|
||||
@Log(title = "${functionName}", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(${ClassName}Bo bo, HttpServletResponse response) {
|
||||
List<${ClassName}Vo> list = ${className}Service.queryList(bo);
|
||||
ExcelBuilder.of(list, ${ClassName}Vo.class).sheetName("${functionName}").toResponse(response);
|
||||
}
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 获取${functionName}详细信息
|
||||
*
|
||||
* @param ${pkColumn.javaField} 主键
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:query")
|
||||
@GetMapping("/{${pkColumn.javaField}}")
|
||||
public R<${ClassName}Vo> getInfo(@NotNull(message = "主键不能为空")
|
||||
@PathVariable ${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return R.ok(${className}Service.queryById(${pkColumn.javaField}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:add")
|
||||
@Log(title = "${functionName}", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {
|
||||
<#if enableUnique>
|
||||
if (!${className}Service.checkUnique(bo)) {
|
||||
return R.fail("新增${functionName}失败,组合唯一字段已存在");
|
||||
}
|
||||
</#if>
|
||||
return toAjax(${className}Service.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:edit")
|
||||
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
|
||||
<#if enableUnique>
|
||||
if (!${className}Service.checkUnique(bo)) {
|
||||
return R.fail("修改${functionName}失败,组合唯一字段已存在");
|
||||
}
|
||||
</#if>
|
||||
return toAjax(${className}Service.updateByBo(bo));
|
||||
}
|
||||
|
||||
<#if enableStatus>
|
||||
/**
|
||||
* 修改${functionName}状态
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:edit")
|
||||
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeStatus")
|
||||
public R<Void> changeStatus(@RequestBody ${ClassName}Bo bo) {
|
||||
return toAjax(${className}Service.updateStatus(bo.get${pkColumn.capJavaField}(), bo.get${statusColumn.capJavaField}()));
|
||||
}
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/**
|
||||
* 调整${functionName}排序
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:edit")
|
||||
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/updateSort")
|
||||
public R<Void> updateSort(@RequestBody ${ClassName}Bo bo) {
|
||||
return toAjax(${className}Service.updateSort(bo.get${pkColumn.capJavaField}(), bo.get${sortColumn.capJavaField}()));
|
||||
}
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 删除${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField}s 主键串
|
||||
*/
|
||||
@SaCheckPermission("${permissionPrefix}:remove")
|
||||
@Log(title = "${functionName}", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{${pkColumn.javaField}s}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
|
||||
return toAjax(${className}Service.deleteWithValidByIds(List.of(${pkColumn.javaField}s), true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package ${packageName}.domain;
|
||||
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
<#list importList as import>
|
||||
import ${import};
|
||||
</#list>
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* ${functionName}对象 ${tableName}
|
||||
*
|
||||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("${tableName}")
|
||||
public class ${ClassName} extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
<#list columns as column>
|
||||
<#if !table.isSuperColumn(column.javaField)>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
<#if column.javaField=='delFlag'>
|
||||
@TableLogic
|
||||
</#if>
|
||||
<#if column.javaField=='version'>
|
||||
@Version
|
||||
</#if>
|
||||
<#if column.pk>
|
||||
@TableId(value = "${column.columnName}")
|
||||
<#elseif column.needTableField>
|
||||
@TableField(value = "${column.columnName}")
|
||||
</#if>
|
||||
private ${column.javaType} ${column.javaField};
|
||||
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ${packageName}.mapper;
|
||||
|
||||
import ${packageName}.domain.${ClassName};
|
||||
import ${packageName}.domain.vo.${ClassName}Vo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* ${functionName}Mapper接口
|
||||
*
|
||||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
public interface ${ClassName}Mapper extends BaseMapperPlus<${ClassName}, ${ClassName}Vo> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package ${packageName}.service;
|
||||
|
||||
import ${packageName}.domain.vo.${ClassName}Vo;
|
||||
import ${packageName}.domain.bo.${ClassName}Bo;
|
||||
<#if table.crud>
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
</#if>
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ${functionName}Service接口
|
||||
*
|
||||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
public interface I${ClassName}Service {
|
||||
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} 主键
|
||||
* @return ${functionName}
|
||||
*/
|
||||
${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField});
|
||||
|
||||
<#if table.crud>
|
||||
/**
|
||||
* 分页查询${functionName}列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return ${functionName}分页列表
|
||||
*/
|
||||
PageResult<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery);
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 查询符合条件的${functionName}列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return ${functionName}列表
|
||||
*/
|
||||
List<${ClassName}Vo> queryList(${ClassName}Bo bo);
|
||||
|
||||
<#if enableUnique>
|
||||
/**
|
||||
* 校验${functionName}是否满足组合唯一约束
|
||||
*
|
||||
* @param bo ${functionName}
|
||||
* @return 是否唯一
|
||||
*/
|
||||
boolean checkUnique(${ClassName}Bo bo);
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param bo ${functionName}
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(${ClassName}Bo bo);
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param bo ${functionName}
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(${ClassName}Bo bo);
|
||||
|
||||
<#if enableStatus>
|
||||
/**
|
||||
* 修改${functionName}状态
|
||||
*
|
||||
* @param ${pkColumn.javaField} 主键
|
||||
* @param status 状态值
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateStatus(${pkColumn.javaType} ${pkColumn.javaField}, ${statusColumn.javaType} status);
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/**
|
||||
* 调整${functionName}排序
|
||||
*
|
||||
* @param ${pkColumn.javaField} 主键
|
||||
* @param sortValue 排序值
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateSort(${pkColumn.javaType} ${pkColumn.javaField}, ${sortColumn.javaType} sortValue);
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 校验并批量删除${functionName}信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package ${packageName}.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
<#if table.crud>
|
||||
import org.dromara.common.core.domain.PageResult;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
</#if>
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
<#if enableUnique>
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
</#if>
|
||||
import org.dromara.common.mybatis.core.query.QueryBuilder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ${packageName}.domain.bo.${ClassName}Bo;
|
||||
import ${packageName}.domain.vo.${ClassName}Vo;
|
||||
import ${packageName}.domain.${ClassName};
|
||||
import ${packageName}.mapper.${ClassName}Mapper;
|
||||
import ${packageName}.service.I${ClassName}Service;
|
||||
<#if table.tree>
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
</#if>
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* ${functionName}Service业务层处理
|
||||
*
|
||||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ${ClassName}ServiceImpl implements I${ClassName}Service {
|
||||
|
||||
private final ${ClassName}Mapper ${className}Mapper;
|
||||
|
||||
/**
|
||||
* 查询${functionName}
|
||||
*
|
||||
* @param ${pkColumn.javaField} 主键
|
||||
* @return ${functionName}
|
||||
*/
|
||||
@Override
|
||||
public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}) {
|
||||
return ${className}Mapper.selectVoById(${pkColumn.javaField});
|
||||
}
|
||||
|
||||
<#if table.crud>
|
||||
/**
|
||||
* 分页查询${functionName}列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return ${functionName}分页列表
|
||||
*/
|
||||
@Override
|
||||
public PageResult<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
|
||||
Page<${ClassName}Vo> result = ${className}Mapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return PageResult.build(result.getRecords(), result.getTotal());
|
||||
}
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 查询符合条件的${functionName}列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return ${functionName}列表
|
||||
*/
|
||||
@Override
|
||||
public List<${ClassName}Vo> queryList(${ClassName}Bo bo) {
|
||||
LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo);
|
||||
return ${className}Mapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
<#if enableUnique>
|
||||
/**
|
||||
* 校验${functionName}是否满足组合唯一约束
|
||||
*
|
||||
* @param bo ${functionName}
|
||||
* @return 是否唯一
|
||||
*/
|
||||
@Override
|
||||
public boolean checkUnique(${ClassName}Bo bo) {
|
||||
boolean hasUniqueValue = true;
|
||||
<#list uniqueColumns as column>
|
||||
<#if column.javaType == 'String'>
|
||||
hasUniqueValue = hasUniqueValue && StringUtils.isNotBlank(bo.get${column.capJavaField}());
|
||||
<#else>
|
||||
hasUniqueValue = hasUniqueValue && bo.get${column.capJavaField}() != null;
|
||||
</#if>
|
||||
</#list>
|
||||
if (!hasUniqueValue) {
|
||||
return true;
|
||||
}
|
||||
LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery();
|
||||
<#list uniqueColumns as column>
|
||||
lqw.eq(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}());
|
||||
</#list>
|
||||
lqw.ne(bo.get${pkColumn.capJavaField}() != null, ${ClassName}::get${pkColumn.capJavaField}, bo.get${pkColumn.capJavaField}());
|
||||
return !${className}Mapper.exists(lqw);
|
||||
}
|
||||
</#if>
|
||||
|
||||
private LambdaQueryWrapper<${ClassName}> buildQueryWrapper(${ClassName}Bo bo) {
|
||||
<#if hasBetween>
|
||||
Map<String, Object> params = bo.getParams();
|
||||
</#if>
|
||||
return QueryBuilder.lambda(${ClassName}.class)
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#assign queryType = column.queryType>
|
||||
<#assign javaType = column.javaType>
|
||||
<#assign AttrName = column.capJavaField>
|
||||
<#assign mpMethod = column.queryType?lower_case>
|
||||
<#if queryType != 'BETWEEN'>
|
||||
<#if javaType == 'String'>
|
||||
<#assign condition = 'StringUtils.isNotBlank(bo.get'+AttrName+'())'>
|
||||
<#if queryType == 'LIKE'>
|
||||
.likeIfText(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
<#elseif queryType == 'EQ'>
|
||||
.eqIfText(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
<#elseif queryType == 'NE'>
|
||||
.neIfText(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
<#else>
|
||||
.${mpMethod}(${condition}, ${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
</#if>
|
||||
<#else>
|
||||
<#assign condition = 'bo.get'+AttrName+'() != null'>
|
||||
<#if queryType == 'EQ'>
|
||||
.eqIfPresent(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
<#elseif queryType == 'NE'>
|
||||
.neIfPresent(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
<#elseif queryType == 'GT'>
|
||||
.gtIfPresent(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
<#elseif queryType == 'LT'>
|
||||
.ltIfPresent(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
<#else>
|
||||
.${mpMethod}(${condition}, ${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}())
|
||||
</#if>
|
||||
</#if>
|
||||
<#else>
|
||||
.betweenParams(${ClassName}::get${column.capJavaField}, params, "begin${column.capJavaField}", "end${column.capJavaField}")
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<#if table.tree && "" != treeAncestorsField>
|
||||
.orderByAsc(${ClassName}::get${treeAncestorsCap})
|
||||
</#if>
|
||||
<#if table.tree && "" != treeParentCode>
|
||||
.orderByAsc(${ClassName}::get${treeParentCap})
|
||||
</#if>
|
||||
<#if table.tree && "" != treeOrderField>
|
||||
.orderByAsc(${ClassName}::get${treeOrderCap})
|
||||
<#elseif enableSort>
|
||||
.orderByAsc(${ClassName}::get${sortColumn.capJavaField})
|
||||
</#if>
|
||||
.orderByAsc(${ClassName}::get${pkColumn.capJavaField})
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*
|
||||
* @param bo ${functionName}
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(${ClassName}Bo bo) {
|
||||
${ClassName} add = MapstructUtils.convert(bo, ${ClassName}.class);
|
||||
<#if table.tree>
|
||||
fillTreeMetaBeforeSave(add, false);
|
||||
</#if>
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = ${className}Mapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.set${pkColumn.capJavaField}(add.get${pkColumn.capJavaField}());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*
|
||||
* @param bo ${functionName}
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(${ClassName}Bo bo) {
|
||||
${ClassName} update = MapstructUtils.convert(bo, ${ClassName}.class);
|
||||
<#if table.tree>
|
||||
fillTreeMetaBeforeSave(update, true);
|
||||
</#if>
|
||||
validEntityBeforeSave(update);
|
||||
return ${className}Mapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
<#if enableStatus>
|
||||
/**
|
||||
* 修改${functionName}状态
|
||||
*
|
||||
* @param ${pkColumn.javaField} 主键
|
||||
* @param status 状态值
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateStatus(${pkColumn.javaType} ${pkColumn.javaField}, ${statusColumn.javaType} status) {
|
||||
return ${className}Mapper.lambda()
|
||||
.set(${ClassName}::get${statusColumn.capJavaField}, status)
|
||||
.eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField})
|
||||
.update();
|
||||
}
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/**
|
||||
* 调整${functionName}排序
|
||||
*
|
||||
* @param ${pkColumn.javaField} 主键
|
||||
* @param sortValue 排序值
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateSort(${pkColumn.javaType} ${pkColumn.javaField}, ${sortColumn.javaType} sortValue) {
|
||||
return ${className}Mapper.lambda()
|
||||
.set(${ClassName}::get${sortColumn.capJavaField}, sortValue)
|
||||
.eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField})
|
||||
.update();
|
||||
}
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(${ClassName} entity) {
|
||||
// 可在此扩展通用业务校验
|
||||
}
|
||||
|
||||
<#if table.tree>
|
||||
private void fillTreeMetaBeforeSave(${ClassName} entity, boolean updateMode) {
|
||||
<#if "" != treeParentCode>
|
||||
if (entity.get${treeParentCap}() == null) {
|
||||
entity.set${treeParentCap}(${treeRootValueJavaLiteral});
|
||||
}
|
||||
if (ObjectUtil.equal(entity.get${pkColumn.capJavaField}(), entity.get${treeParentCap}())) {
|
||||
throw new ServiceException("${functionName}父节点不能选择自身");
|
||||
}
|
||||
<#if "" != treeAncestorsField>
|
||||
${ClassName} parent = null;
|
||||
if (!ObjectUtil.equal(entity.get${treeParentCap}(), ${treeRootValueJavaLiteral})) {
|
||||
parent = ${className}Mapper.selectById(entity.get${treeParentCap}());
|
||||
if (ObjectUtil.isNull(parent)) {
|
||||
throw new ServiceException("${functionName}父节点不存在");
|
||||
}
|
||||
}
|
||||
if (updateMode && entity.get${pkColumn.capJavaField}() != null && ObjectUtil.isNotNull(parent)
|
||||
&& containsAncestor(parent.get${treeAncestorsCap}(), entity.get${pkColumn.capJavaField}())) {
|
||||
throw new ServiceException("不能选择当前节点或其子节点作为父节点");
|
||||
}
|
||||
String newAncestors = resolveAncestors(entity.get${treeParentCap}(), parent);
|
||||
if (updateMode && entity.get${pkColumn.capJavaField}() != null) {
|
||||
${ClassName} oldEntity = ${className}Mapper.selectById(entity.get${pkColumn.capJavaField}());
|
||||
if (ObjectUtil.isNull(oldEntity)) {
|
||||
throw new ServiceException("${functionName}不存在,无法修改");
|
||||
}
|
||||
String oldAncestors = oldEntity.get${treeAncestorsCap}();
|
||||
entity.set${treeAncestorsCap}(newAncestors);
|
||||
if (!StringUtils.equals(oldAncestors, newAncestors)) {
|
||||
updateChildrenAncestors(entity.get${pkColumn.capJavaField}(), newAncestors, oldAncestors);
|
||||
}
|
||||
} else {
|
||||
entity.set${treeAncestorsCap}(newAncestors);
|
||||
}
|
||||
</#if>
|
||||
</#if>
|
||||
}
|
||||
<#if "" != treeAncestorsField>
|
||||
|
||||
private String resolveAncestors(${treeParentColumn.javaType} parentId, ${ClassName} parent) {
|
||||
if (ObjectUtil.equal(parentId, ${treeRootValueJavaLiteral})) {
|
||||
return "${treeRootValue}";
|
||||
}
|
||||
String parentAncestors = parent.get${treeAncestorsCap}();
|
||||
if (StringUtils.isBlank(parentAncestors)) {
|
||||
return String.valueOf(parentId);
|
||||
}
|
||||
return parentAncestors + StringUtils.SEPARATOR + parentId;
|
||||
}
|
||||
|
||||
private void updateChildrenAncestors(${pkColumn.javaType} currentId, String newAncestors, String oldAncestors) {
|
||||
List<${ClassName}> children = ${className}Mapper.lambda()
|
||||
.select(${ClassName}::get${pkColumn.capJavaField}, ${ClassName}::get${treeAncestorsCap})
|
||||
.findInSet(currentId, ${ClassName}::get${treeAncestorsCap})
|
||||
.list();
|
||||
List<${ClassName}> updateList = new ArrayList<>();
|
||||
for (${ClassName} child : children) {
|
||||
String ancestors = child.get${treeAncestorsCap}();
|
||||
if (StringUtils.isBlank(ancestors)) {
|
||||
continue;
|
||||
}
|
||||
${ClassName} update = new ${ClassName}();
|
||||
update.set${pkColumn.capJavaField}(child.get${pkColumn.capJavaField}());
|
||||
update.set${treeAncestorsCap}(StringUtils.replaceOnce(ancestors, oldAncestors, newAncestors));
|
||||
updateList.add(update);
|
||||
}
|
||||
if (!updateList.isEmpty()) {
|
||||
${className}Mapper.updateBatchById(updateList);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsAncestor(String ancestors, ${pkColumn.javaType} nodeId) {
|
||||
for (String item : StringUtils.splitList(ancestors)) {
|
||||
if (StringUtils.equals(item, String.valueOf(nodeId))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
</#if>
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 校验并批量删除${functionName}信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<${pkColumn.javaType}> ids, Boolean isValid) {
|
||||
if (isValid) {
|
||||
// 可在此扩展删除前业务校验
|
||||
}
|
||||
return ${className}Mapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package ${packageName}.domain.vo;
|
||||
|
||||
<#list importList as import>
|
||||
import ${import};
|
||||
</#list>
|
||||
import ${packageName}.domain.${ClassName};
|
||||
import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated;
|
||||
import org.apache.fesod.sheet.annotation.ExcelProperty;
|
||||
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||
import org.dromara.common.translation.annotation.Translation;
|
||||
import org.dromara.common.translation.constant.TransConstant;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* ${functionName}视图对象 ${tableName}
|
||||
*
|
||||
* @author ${author}
|
||||
* @date ${datetime}
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = ${ClassName}.class)
|
||||
public class ${ClassName}Vo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
<#list columns as column>
|
||||
<#if column.list>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
<#assign parentheseIndex = column.columnComment?index_of("(")>
|
||||
<#if column.dictType?has_content>
|
||||
@ExcelProperty(value = "${column.columnLabel}", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "${column.dictType}")
|
||||
<#elseif parentheseIndex != -1>
|
||||
@ExcelProperty(value = "${column.columnLabel}", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "${column.readConverterExp()}")
|
||||
<#else>
|
||||
@ExcelProperty(value = "${column.columnLabel}")
|
||||
</#if>
|
||||
private ${column.javaType} ${column.javaField};
|
||||
|
||||
<#if column.htmlType == "imageUpload">
|
||||
/**
|
||||
* ${column.columnComment}Url
|
||||
*/
|
||||
@Translation(type = TransConstant.OSS_ID_TO_URL, mapper = "${column.javaField}")
|
||||
private String ${column.javaField}Url;
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import type { <#if !table.tree>PageResult, </#if> R } from '@/api/types';
|
||||
import request from '@/api/request';
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from './types';
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
*/
|
||||
export function list${BusinessName}(query?: ${BusinessName}Query) {
|
||||
return request<R<<#if table.tree>${BusinessName}VO[]<#else> PageResult<${BusinessName}VO></#if>>>({
|
||||
url: '/${moduleName}/${businessName}/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询${functionName}详细
|
||||
*/
|
||||
export function get${BusinessName}(${pkColumn.javaField}: string | number) {
|
||||
return request<R<${BusinessName}VO>>({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
*/
|
||||
export function add${BusinessName}(data: ${BusinessName}Form) {
|
||||
return request<R>({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
*/
|
||||
export function update${BusinessName}(data: ${BusinessName}Form) {
|
||||
return request<R>({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
<#if enableStatus>
|
||||
/**
|
||||
* 修改${functionName}状态
|
||||
*/
|
||||
export function change${BusinessName}Status(
|
||||
${pkColumn.javaField}: string | number,
|
||||
${statusField}: <#if statusColumn.javaType == 'Boolean'>boolean<#elseif statusColumn.javaType == 'Integer' || statusColumn.javaType == 'Long'>number<#else> string</#if>
|
||||
) {
|
||||
return request<R>({
|
||||
url: '/${moduleName}/${businessName}/changeStatus',
|
||||
method: 'put',
|
||||
data: {
|
||||
${pkColumn.javaField},
|
||||
${statusField}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</#if>
|
||||
<#if enableSort>
|
||||
/**
|
||||
* 调整${functionName}排序
|
||||
*/
|
||||
export function update${BusinessName}Sort(
|
||||
${pkColumn.javaField}: string | number,
|
||||
${sortField}: <#if sortColumn.javaType == 'LocalDateTime' || sortColumn.javaType == 'String'>string<#else> number</#if>
|
||||
) {
|
||||
return request<R>({
|
||||
url: '/${moduleName}/${businessName}/updateSort',
|
||||
method: 'put',
|
||||
data: {
|
||||
${pkColumn.javaField},
|
||||
${sortField}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</#if>
|
||||
/**
|
||||
* 删除${functionName}
|
||||
*/
|
||||
export function del${BusinessName}(${pkColumn.javaField}: string | number | Array<string | number>) {
|
||||
return request<R>({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'delete'
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
import { DeleteOutlined<#if enableExport>, DownloadOutlined</#if>, EditOutlined, PlusOutlined, SortAscendingOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
<#if needCheckbox>
|
||||
ProFormCheckbox,
|
||||
</#if>
|
||||
<#if needDateField>
|
||||
ProFormDateTimePicker,
|
||||
</#if>
|
||||
<#if needDigit>
|
||||
ProFormDigit,
|
||||
</#if>
|
||||
<#if needSelect>
|
||||
ProFormSelect,
|
||||
</#if>
|
||||
<#if needTextArea>
|
||||
ProFormTextArea,
|
||||
</#if>
|
||||
ProFormText,
|
||||
ProFormTreeSelect,
|
||||
ProTable,
|
||||
type ActionType,
|
||||
type ProColumns
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message<#if enableStatus || needSwitchField>, Switch</#if><#if enableSort>, InputNumber</#if> } from 'antd';
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
<#if needDict>
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
</#if>
|
||||
<#if needFileUpload>
|
||||
import FileUpload from '@/components/common/FileUpload';
|
||||
</#if>
|
||||
<#if needImagePreview>
|
||||
import ImagePreview from '@/components/common/ImagePreview';
|
||||
</#if>
|
||||
<#if needImageUpload>
|
||||
import ImageUpload from '@/components/common/ImageUpload';
|
||||
</#if>
|
||||
<#if needEditor>
|
||||
import RichTextEditor from '@/components/common/RichTextEditor';
|
||||
</#if>
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
</#if>
|
||||
<#if enableExport>
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
</#if>
|
||||
import { useTableScroll } from '@/hooks/useTableScroll';
|
||||
import { useTreeTableExpand } from '@/hooks/useTreeTableExpand';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
<#if needDict>
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
</#if>
|
||||
<#if enableStatus>
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
</#if>
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { filterTree<#if needDateField>, formatDateTimeFields</#if>, handleTree<#if needDateField>, toDayjsFields</#if> } from '@/utils/ruoyi';
|
||||
|
||||
const default${BusinessName}Form: ${BusinessName}Form = {
|
||||
${treeParentCode}: ${treeRootValueTsLiteral},
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk && column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
|
||||
interface ${BusinessName}SelectNode {
|
||||
title: string;
|
||||
value: string | number;
|
||||
children?: ${BusinessName}SelectNode[];
|
||||
}
|
||||
|
||||
function toTreeSelectData(nodes: ${BusinessName}VO[]): ${BusinessName}SelectNode[] {
|
||||
return nodes.map(node => ({
|
||||
title: String(node.${treeName} || ''),
|
||||
value: node.${treeCode},
|
||||
children: node.children ? toTreeSelectData(node.children) : undefined
|
||||
}));
|
||||
}
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
|
||||
</#if>
|
||||
export default function ${BusinessName}Page() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const { tableScroll } = useTableScroll(1000);
|
||||
const [form] = Form.useForm<${BusinessName}Form>();
|
||||
const userInfo = useUserStore(state => state.userInfo);
|
||||
<#if needDict>
|
||||
const dicts = useDict(${dicts});
|
||||
</#if>
|
||||
const [treeOptions, setTreeOptions] = useState<${BusinessName}VO[]>([]);
|
||||
const [tableRows, setTableRows] = useState<${BusinessName}VO[]>([]);
|
||||
const { expandAll, expandedRowKeys, onExpandedRowsChange, syncExpandedRows, toggleExpandAll } =
|
||||
useTreeTableExpand<${BusinessName}VO>(row => row.${treeCode});
|
||||
const [modalOpen, { setTrue: openModal, setFalse: closeModal }] = useBoolean(false);
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
<#if enableExport>
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
</#if>
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['${permissionPrefix}:add']);
|
||||
const canEdit = hasPermi(userInfo, ['${permissionPrefix}:edit']);
|
||||
const canRemove = hasPermi(userInfo, ['${permissionPrefix}:remove']);
|
||||
<#if enableExport>
|
||||
const canExport = hasPermi(userInfo, ['${permissionPrefix}:export']);
|
||||
</#if>
|
||||
const treeSelectData = useMemo(
|
||||
() => [{ title: '顶级节点', value: ${treeRootValueTsLiteral}, children: toTreeSelectData(treeOptions) }],
|
||||
[treeOptions]
|
||||
);
|
||||
|
||||
const loadTreeOptions = async (excludeId?: string | number) => {
|
||||
const res = await list${BusinessName}();
|
||||
const rows = handleTree<${BusinessName}VO>(res.data || [], '${treeCode}', '${treeParentCode}');
|
||||
setTreeOptions(excludeId ? filterTree(rows, node => node.${treeCode} !== excludeId) : rows);
|
||||
};
|
||||
|
||||
const openAdd = async (row?: ${BusinessName}VO) => {
|
||||
await loadTreeOptions();
|
||||
form.resetFields();
|
||||
form.setFieldsValue({ ...default${BusinessName}Form, ${treeParentCode}: row?.${treeCode} || treeRootValueTsLiteral });
|
||||
setModalTitle('添加${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const openEdit = async (row: ${BusinessName}VO) => {
|
||||
await loadTreeOptions(row.${treeCode});
|
||||
const res = await get${BusinessName}(row.${pkColumn.javaField});
|
||||
const data = <#if needDateField>toDayjsFields({ ...res.data }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...res.data }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (typeof data.${column.javaField} === 'string') {
|
||||
data.${column.javaField} = data.${column.javaField}.split(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
form.resetFields();
|
||||
form.setFieldsValue(data);
|
||||
setModalTitle('修改${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const submitForm = async (values: ${BusinessName}Form) => {
|
||||
const submitValues = <#if needDateField>formatDateTimeFields({ ...values }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...values }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (Array.isArray(submitValues.${column.javaField})) {
|
||||
submitValues.${column.javaField} = submitValues.${column.javaField}.join(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
submitValues.${pkColumn.javaField} ? await update${BusinessName}(submitValues) : await add${BusinessName}(submitValues);
|
||||
message.success('操作成功');
|
||||
form.resetFields();
|
||||
actionRef.current?.reload();
|
||||
return true;
|
||||
};
|
||||
|
||||
const remove = async (row: ${BusinessName}VO) => {
|
||||
await del${BusinessName}(row.${pkColumn.javaField});
|
||||
message.success('删除成功');
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
const handleStatusChange = async (row: ${BusinessName}VO, checked: boolean) => {
|
||||
const previousStatus = row.${statusField};
|
||||
const status = checked ? ${statusField}ActiveValue : ${statusField}InactiveValue;
|
||||
const text = checked ? '启用' : '停用';
|
||||
try {
|
||||
await confirmAction(`确认要"${r'${text}'}"吗?`);
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, status);
|
||||
message.success(`${r'${text}'}成功`);
|
||||
actionRef.current?.reload();
|
||||
} catch {
|
||||
row.${statusField} = previousStatus;
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
};
|
||||
|
||||
</#if>
|
||||
<#if enableSort>
|
||||
const handleSortChange = async (row: ${BusinessName}VO, value?: number | null) => {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, value || 0);
|
||||
message.success('排序更新成功');
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
</#if>
|
||||
const columns: ProColumns<${BusinessName}VO>[] = [
|
||||
<#list columns as column>
|
||||
<#if column.list && !column.pk>
|
||||
<#if enableStatus && statusField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
<#if column.dictType?has_content>
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
</#if>
|
||||
render: (_, row) => (
|
||||
<Switch
|
||||
checked={row.${column.javaField} === ${statusField}ActiveValue}
|
||||
disabled={!canEdit}
|
||||
onChange={checked => handleStatusChange(row, checked)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
search: false,
|
||||
width: 130,
|
||||
render: (_, row) => <InputNumber min={0} value={row.${column.javaField} as number} onChange={value => handleSortChange(row, value)} />
|
||||
},
|
||||
<#elseif column.htmlType == "datetime">
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}', valueType: 'dateTime', search: <#if column.query>true<#else> false</#if>, width: 170 },
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}Url',
|
||||
search: false,
|
||||
width: 100,
|
||||
render: (_, row) => <ImagePreview src={row.${column.javaField}Url || String(row.${column.javaField} || '')} width={50} height={50} />
|
||||
},
|
||||
<#elseif column.dictColumn>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
render: (_, row) => <DictTag options={dicts.${column.dictType}} value={row.${column.javaField}} />
|
||||
},
|
||||
<#elseif column.htmlType == "switch">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
render: (_, row) => <Switch checked={row.${column.javaField} === ${column.switchActiveValue}} disabled />
|
||||
},
|
||||
<#else>
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}'<#if !column.query>, search: false</#if> },
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
{
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
width: 130,
|
||||
fixed: 'right',
|
||||
render: (_, row) => (
|
||||
<RowActions
|
||||
actions={[
|
||||
canEdit && { key: 'edit', label: '修改', icon: <EditOutlined />, onClick: () => openEdit(row) },
|
||||
canAdd && { key: 'add', label: '新增', icon: <PlusOutlined />, onClick: () => openAdd(row) },
|
||||
canRemove && {
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
confirm: `是否确认删除${functionName}编号为"${r'${row.'}${pkColumn.javaField}${r'}'}"的数据项?`,
|
||||
onClick: () => remove(row)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer title="${functionName}">
|
||||
<ProTable<${BusinessName}VO, ${BusinessName}Query>
|
||||
actionRef={actionRef}
|
||||
rowKey="${treeCode}"
|
||||
columns={columns}
|
||||
scroll={tableScroll}
|
||||
pagination={false}
|
||||
search={{ labelWidth: 90 }}
|
||||
expandable={{
|
||||
expandedRowKeys,
|
||||
onExpandedRowsChange
|
||||
}}
|
||||
request={async params => {
|
||||
const res = await list${BusinessName}(params);
|
||||
const rows = handleTree<${BusinessName}VO>(res.data || [], '${treeCode}', '${treeParentCode}');
|
||||
setTableRows(rows);
|
||||
syncExpandedRows(rows, expandAll);
|
||||
<#if enableExport>
|
||||
updateExportParams(params);
|
||||
</#if>
|
||||
return { data: rows, total: rows.length, success: true };
|
||||
}}
|
||||
toolbar={{ title: '${functionName}列表' }}
|
||||
toolBarRender={() => [
|
||||
canAdd && (
|
||||
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={() => openAdd()}>
|
||||
新增
|
||||
</Button>
|
||||
),
|
||||
<Button
|
||||
key="expand"
|
||||
icon={<SortAscendingOutlined />}
|
||||
onClick={() => toggleExpandAll(tableRows)}
|
||||
>
|
||||
展开/折叠
|
||||
</Button><#if enableExport>,
|
||||
canExport && (
|
||||
<Button
|
||||
key="export"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => exportFile('/${moduleName}/${businessName}/export', () => `${businessName}_${r'${Date.now()}'}.xlsx`)}
|
||||
>
|
||||
导出
|
||||
</Button>
|
||||
)</#if>
|
||||
]}
|
||||
/>
|
||||
|
||||
<ModalForm<${BusinessName}Form>
|
||||
title={modalTitle}
|
||||
open={modalOpen}
|
||||
width={560}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={default${BusinessName}Form}
|
||||
modalProps={{ destroyOnHidden: true, onCancel: closeModal }}
|
||||
onOpenChange={open => !open && closeModal()}
|
||||
onFinish={submitForm}
|
||||
>
|
||||
<ProFormText name="${pkColumn.javaField}" hidden />
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if column.javaField == treeParentCode>
|
||||
<ProFormTreeSelect
|
||||
name="${column.javaField}"
|
||||
label="${column.columnLabel}"
|
||||
<#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>
|
||||
fieldProps={{
|
||||
allowClear: true,
|
||||
treeDefaultExpandAll: true,
|
||||
treeData: treeSelectData,
|
||||
placeholder: '请选择${column.columnLabel}'
|
||||
}}
|
||||
/>
|
||||
<#elseif column.htmlType == "input">
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<ProFormTextArea name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<ProFormDigit name="${column.javaField}" label="${column.columnLabel}" min={0} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch") && column.dictType?has_content>
|
||||
<ProFormSelect name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<ProFormCheckbox.Group name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "switch">
|
||||
<Form.Item
|
||||
name="${column.javaField}"
|
||||
label="${column.columnLabel}"
|
||||
valuePropName="checked"
|
||||
getValueProps={value => ({ checked: value === ${column.switchActiveValue} })}
|
||||
normalize={checked => (checked ? ${column.switchActiveValue} : ${column.switchInactiveValue})}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<ProFormDateTimePicker name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<ImageUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<FileUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<RichTextEditor />
|
||||
</Form.Item>
|
||||
<#else>
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</ModalForm>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
import { DeleteOutlined<#if enableExport>, DownloadOutlined</#if>, EditOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
<#if needCheckbox>
|
||||
ProFormCheckbox,
|
||||
</#if>
|
||||
<#if needDateField>
|
||||
ProFormDateTimePicker,
|
||||
</#if>
|
||||
<#if needDigit>
|
||||
ProFormDigit,
|
||||
</#if>
|
||||
<#if needSelect>
|
||||
ProFormSelect,
|
||||
</#if>
|
||||
<#if needTextArea>
|
||||
ProFormTextArea,
|
||||
</#if>
|
||||
ProFormText,
|
||||
ProTable,
|
||||
type ActionType,
|
||||
type ProColumns
|
||||
} from '@ant-design/pro-components';
|
||||
import { useBoolean } from 'ahooks';
|
||||
import { Button, Form, message, Popconfirm<#if enableStatus || needSwitchField>, Switch</#if><#if enableSort>, InputNumber</#if> } from 'antd';
|
||||
import { useRef, useState } from 'react';
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
<#if needDict>
|
||||
import DictTag from '@/components/common/DictTag';
|
||||
</#if>
|
||||
<#if needFileUpload>
|
||||
import FileUpload from '@/components/common/FileUpload';
|
||||
</#if>
|
||||
<#if needImagePreview>
|
||||
import ImagePreview from '@/components/common/ImagePreview';
|
||||
</#if>
|
||||
<#if needImageUpload>
|
||||
import ImageUpload from '@/components/common/ImageUpload';
|
||||
</#if>
|
||||
<#if needEditor>
|
||||
import RichTextEditor from '@/components/common/RichTextEditor';
|
||||
</#if>
|
||||
import RowActions from '@/components/common/RowActions';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/hooks/useDict';
|
||||
</#if>
|
||||
<#if needDateRange>
|
||||
import { useDateRangeQuery } from '@/hooks/useDateRangeQuery';
|
||||
</#if>
|
||||
<#if enableExport>
|
||||
import { useTableExport } from '@/hooks/useTableExport';
|
||||
</#if>
|
||||
import { useTableScroll } from '@/hooks/useTableScroll';
|
||||
import { useTableSelection } from '@/hooks/useTableSelection';
|
||||
import { useUserStore } from '@/stores/userStore';
|
||||
<#if needDict>
|
||||
import { dictOptions } from '@/utils/dict';
|
||||
</#if>
|
||||
<#if enableStatus>
|
||||
import { confirmAction } from '@/utils/modal';
|
||||
</#if>
|
||||
import { hasPermi } from '@/utils/permission';
|
||||
import { <#if needDateField>formatDateTimeFields, toDayjsFields, </#if> toPageQuery, toTableData } from '@/utils/ruoyi';
|
||||
|
||||
const default${BusinessName}Form: ${BusinessName}Form = {
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk && column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
|
||||
</#if>
|
||||
export default function ${BusinessName}Page() {
|
||||
const actionRef = useRef<ActionType | undefined>(undefined);
|
||||
const { tableScroll } = useTableScroll(1000);
|
||||
const [form] = Form.useForm<${BusinessName}Form>();
|
||||
const userInfo = useUserStore(state => state.userInfo);
|
||||
<#if needDict>
|
||||
const dicts = useDict(${dicts});
|
||||
</#if>
|
||||
const { ids, selectedOne, handleSelectionChange, clearSelection } = useTableSelection<${BusinessName}VO>(
|
||||
row => row.${pkColumn.javaField}
|
||||
);
|
||||
const [modalOpen, { setTrue: openModal, setFalse: closeModal }] = useBoolean(false);
|
||||
const [modalTitle, setModalTitle] = useState('');
|
||||
<#if enableExport>
|
||||
const { updateExportParams, exportFile } = useTableExport();
|
||||
</#if>
|
||||
<#list columns as column>
|
||||
<#if column.query && column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
const { applyDateRange: apply${column.capJavaField}DateRange } = useDateRangeQuery('${column.capJavaField}');
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const canAdd = hasPermi(userInfo, ['${permissionPrefix}:add']);
|
||||
const canEdit = hasPermi(userInfo, ['${permissionPrefix}:edit']);
|
||||
const canRemove = hasPermi(userInfo, ['${permissionPrefix}:remove']);
|
||||
<#if enableExport>
|
||||
const canExport = hasPermi(userInfo, ['${permissionPrefix}:export']);
|
||||
</#if>
|
||||
|
||||
const openAdd = () => {
|
||||
form.resetFields();
|
||||
form.setFieldsValue(default${BusinessName}Form);
|
||||
setModalTitle('添加${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const openEdit = async (row?: ${BusinessName}VO) => {
|
||||
const target = row || selectedOne;
|
||||
if (!target?.${pkColumn.javaField}) return;
|
||||
const res = await get${BusinessName}(target.${pkColumn.javaField});
|
||||
const data = <#if needDateField>toDayjsFields({ ...res.data }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...res.data }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (typeof data.${column.javaField} === 'string') {
|
||||
data.${column.javaField} = data.${column.javaField}.split(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
form.resetFields();
|
||||
form.setFieldsValue(data);
|
||||
setModalTitle('修改${functionName}');
|
||||
openModal();
|
||||
};
|
||||
|
||||
const submitForm = async (values: ${BusinessName}Form) => {
|
||||
const submitValues = <#if needDateField>formatDateTimeFields({ ...values }, [
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "datetime">
|
||||
'${column.javaField}',
|
||||
</#if>
|
||||
</#list>
|
||||
])<#else>{ ...values }</#if>;
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && column.htmlType == "checkbox">
|
||||
if (Array.isArray(submitValues.${column.javaField})) {
|
||||
submitValues.${column.javaField} = submitValues.${column.javaField}.join(',');
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
submitValues.${pkColumn.javaField} ? await update${BusinessName}(submitValues) : await add${BusinessName}(submitValues);
|
||||
message.success('操作成功');
|
||||
form.resetFields();
|
||||
actionRef.current?.reload();
|
||||
return true;
|
||||
};
|
||||
|
||||
const remove = async (row?: ${BusinessName}VO) => {
|
||||
await del${BusinessName}(row?.${pkColumn.javaField} || ids);
|
||||
message.success('删除成功');
|
||||
clearSelection();
|
||||
actionRef.current?.reloadAndRest?.();
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
const handleStatusChange = async (row: ${BusinessName}VO, checked: boolean) => {
|
||||
const previousStatus = row.${statusField};
|
||||
const status = checked ? ${statusField}ActiveValue : ${statusField}InactiveValue;
|
||||
const text = checked ? '启用' : '停用';
|
||||
try {
|
||||
await confirmAction(`确认要"${r'${text}'}"吗?`);
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, status);
|
||||
message.success(`${r'${text}'}成功`);
|
||||
actionRef.current?.reload();
|
||||
} catch {
|
||||
row.${statusField} = previousStatus;
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
};
|
||||
|
||||
</#if>
|
||||
<#if enableSort>
|
||||
const handleSortChange = async (row: ${BusinessName}VO, value?: number | null) => {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, value || 0);
|
||||
message.success('排序更新成功');
|
||||
actionRef.current?.reload();
|
||||
};
|
||||
|
||||
</#if>
|
||||
const columns: ProColumns<${BusinessName}VO>[] = [
|
||||
<#list columns as column>
|
||||
<#if column.list>
|
||||
<#if enableStatus && statusField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
<#if column.dictType?has_content>
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
</#if>
|
||||
render: (_, row) => (
|
||||
<Switch
|
||||
checked={row.${column.javaField} === ${statusField}ActiveValue}
|
||||
disabled={!canEdit}
|
||||
onChange={checked => handleStatusChange(row, checked)}
|
||||
/>
|
||||
)
|
||||
},
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
search: false,
|
||||
width: 130,
|
||||
render: (_, row) => <InputNumber min={0} value={row.${column.javaField} as number} onChange={value => handleSortChange(row, value)} />
|
||||
},
|
||||
<#elseif column.htmlType == "datetime">
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}', valueType: 'dateTime', search: <#if column.query && column.queryType != "BETWEEN">true<#else> false</#if>, width: 170 },
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}Url',
|
||||
search: false,
|
||||
width: 100,
|
||||
render: (_, row) => <ImagePreview src={row.${column.javaField}Url || String(row.${column.javaField} || '')} width={50} height={50} />
|
||||
},
|
||||
<#elseif column.dictColumn>
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
fieldProps: { options: dictOptions(dicts.${column.dictType}) },
|
||||
render: (_, row) => <DictTag options={dicts.${column.dictType}} value={row.${column.javaField}} />
|
||||
},
|
||||
<#elseif column.htmlType == "switch">
|
||||
{
|
||||
title: '${column.columnLabel}',
|
||||
dataIndex: '${column.javaField}',
|
||||
valueType: 'select',
|
||||
width: 100,
|
||||
render: (_, row) => <Switch checked={row.${column.javaField} === ${column.switchActiveValue}} disabled />
|
||||
},
|
||||
<#else>
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}'<#if !column.query>, search: false</#if> },
|
||||
</#if>
|
||||
</#if>
|
||||
<#if column.query && column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
{ title: '${column.columnLabel}', dataIndex: '${column.javaField}Range', valueType: 'dateTimeRange', hideInTable: true },
|
||||
</#if>
|
||||
</#list>
|
||||
{
|
||||
title: '操作',
|
||||
valueType: 'option',
|
||||
width: 92,
|
||||
fixed: 'right',
|
||||
render: (_, row) => (
|
||||
<RowActions
|
||||
actions={[
|
||||
canEdit && { key: 'edit', label: '修改', icon: <EditOutlined />, onClick: () => openEdit(row) },
|
||||
canRemove && {
|
||||
key: 'delete',
|
||||
label: '删除',
|
||||
icon: <DeleteOutlined />,
|
||||
danger: true,
|
||||
confirm: `是否确认删除${functionName}编号为"${r'${row.'}${pkColumn.javaField}${r'}'}"的数据项?`,
|
||||
onClick: () => remove(row)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContainer title="${functionName}">
|
||||
<ProTable<${BusinessName}VO, ${BusinessName}Query<#if needDateRange> & Record<string, unknown></#if>>
|
||||
actionRef={actionRef}
|
||||
rowKey="${pkColumn.javaField}"
|
||||
columns={columns}
|
||||
scroll={tableScroll}
|
||||
search={{ labelWidth: 90 }}
|
||||
pagination={{ defaultPageSize: 10, showSizeChanger: true }}
|
||||
rowSelection={{ selectedRowKeys: ids, onChange: handleSelectionChange }}
|
||||
request={async params => {
|
||||
<#if needDateRange>
|
||||
let query = toPageQuery(params);
|
||||
<#list columns as column>
|
||||
<#if column.query && column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
query = apply${column.capJavaField}DateRange(query, params.${column.javaField}Range as [string, string] | undefined);
|
||||
delete query.${column.javaField}Range;
|
||||
</#if>
|
||||
</#list>
|
||||
<#else>
|
||||
const query = toPageQuery(params);
|
||||
</#if>
|
||||
<#if enableExport>
|
||||
updateExportParams(query);
|
||||
</#if>
|
||||
const res = await list${BusinessName}(query);
|
||||
return toTableData(res);
|
||||
}}
|
||||
toolbar={{ title: '${functionName}列表' }}
|
||||
toolBarRender={() => [
|
||||
canAdd && (
|
||||
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={openAdd}>
|
||||
新增
|
||||
</Button>
|
||||
),
|
||||
canEdit && (
|
||||
<Button key="edit" disabled={!selectedOne} icon={<EditOutlined />} onClick={() => openEdit()}>
|
||||
修改
|
||||
</Button>
|
||||
),
|
||||
canRemove && (
|
||||
<Popconfirm key="delete" title={`是否确认删除${functionName}编号为"${r'${ids}'}"的数据项?`} onConfirm={() => remove()}>
|
||||
<Button danger disabled={!ids.length} icon={<DeleteOutlined />}>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)<#if enableExport>,
|
||||
canExport && (
|
||||
<Button
|
||||
key="export"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => exportFile('/${moduleName}/${businessName}/export', () => `${businessName}_${r'${Date.now()}'}.xlsx`)}
|
||||
>
|
||||
导出
|
||||
</Button>
|
||||
)</#if>
|
||||
]}
|
||||
/>
|
||||
|
||||
<ModalForm<${BusinessName}Form>
|
||||
title={modalTitle}
|
||||
open={modalOpen}
|
||||
width={560}
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={default${BusinessName}Form}
|
||||
modalProps={{ destroyOnHidden: true, onCancel: closeModal }}
|
||||
onOpenChange={open => !open && closeModal()}
|
||||
onFinish={submitForm}
|
||||
>
|
||||
<ProFormText name="${pkColumn.javaField}" hidden />
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if column.htmlType == "input">
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<ProFormTextArea name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<ProFormDigit name="${column.javaField}" label="${column.columnLabel}" min={0} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch") && column.dictType?has_content>
|
||||
<ProFormSelect name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<ProFormCheckbox.Group name="${column.javaField}" label="${column.columnLabel}" options={dictOptions(dicts.${column.dictType})} <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "switch">
|
||||
<Form.Item
|
||||
name="${column.javaField}"
|
||||
label="${column.columnLabel}"
|
||||
valuePropName="checked"
|
||||
getValueProps={value => ({ checked: value === ${column.switchActiveValue} })}
|
||||
normalize={checked => (checked ? ${column.switchActiveValue} : ${column.switchInactiveValue})}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<ProFormDateTimePicker name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<ImageUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<FileUpload />
|
||||
</Form.Item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<Form.Item name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if>>
|
||||
<RichTextEditor />
|
||||
</Form.Item>
|
||||
<#else>
|
||||
<ProFormText name="${column.javaField}" label="${column.columnLabel}" <#if column.required>rules={[{ required: true, message: '${column.columnLabel}不能为空' }]}</#if> />
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</ModalForm>
|
||||
</PageContainer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { BaseEntity<#if !table.tree>, PageQuery</#if> } from '@/api/types';
|
||||
|
||||
export interface ${BusinessName}VO {
|
||||
<#list columns as column>
|
||||
<#if column.list || column.pk>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}: ${column.tsType};
|
||||
<#if column.htmlType == "imageUpload">
|
||||
/**
|
||||
* ${column.columnComment}Url
|
||||
*/
|
||||
${column.javaField}Url?: string;
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<#if table.tree>
|
||||
/**
|
||||
* 子对象
|
||||
*/
|
||||
children?: ${BusinessName}VO[];
|
||||
</#if>
|
||||
}
|
||||
|
||||
export interface ${BusinessName}Form extends BaseEntity {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit || column.pk>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
<#if column.htmlType == "checkbox">
|
||||
${column.javaField}?: string | string[];
|
||||
<#else>
|
||||
${column.javaField}?: ${column.tsType};
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
|
||||
export interface ${BusinessName}Query<#if !table.tree> extends PageQuery</#if> {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}?: ${column.tsType};
|
||||
</#if>
|
||||
</#list>
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: Record<string, unknown>;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
-- 菜单 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[0]}, '${functionName}', ${parentMenuId}, 1, '${businessName}', '${moduleName}/${businessName}/index', 'N', 'Y', 'C', '0', '0', '${permissionPrefix}:list', '#', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '${functionName}菜单');
|
||||
|
||||
-- 按钮 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, 1, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:query', '#', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, 2, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:add', '#', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, 3, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:edit', '#', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, 4, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:remove', '#', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '');
|
||||
<#if enableExport>
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, 5, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:export', '#', 1761000000000000103, 1761100000000000001, sysdate(), null, null, '');
|
||||
</#if>
|
||||
@@ -0,0 +1,20 @@
|
||||
-- 菜单 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[0]}, '${functionName}', ${parentMenuId}, 1, '${businessName}', '${moduleName}/${businessName}/index', 'N', 'Y', 'C', '0', '0', '${permissionPrefix}:list', '#', 1761000000000000103, 1761100000000000001, sysdate, null, null, '${functionName}菜单');
|
||||
|
||||
-- 按钮 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, 1, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:query', '#', 1761000000000000103, 1761100000000000001, sysdate, null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, 2, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:add', '#', 1761000000000000103, 1761100000000000001, sysdate, null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, 3, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:edit', '#', 1761000000000000103, 1761100000000000001, sysdate, null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, 4, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:remove', '#', 1761000000000000103, 1761100000000000001, sysdate, null, null, '');
|
||||
<#if enableExport>
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, 5, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:export', '#', 1761000000000000103, 1761100000000000001, sysdate, null, null, '');
|
||||
</#if>
|
||||
@@ -0,0 +1,20 @@
|
||||
-- 菜单 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[0]}, '${functionName}', ${parentMenuId}, 1, '${businessName}', '${moduleName}/${businessName}/index', 'N', 'Y', 'C', '0', '0', '${permissionPrefix}:list', '#', 1761000000000000103, 1761100000000000001, now(), null, null, '${functionName}菜单');
|
||||
|
||||
-- 按钮 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, 1, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:query', '#', 1761000000000000103, 1761100000000000001, now(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, 2, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:add', '#', 1761000000000000103, 1761100000000000001, now(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, 3, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:edit', '#', 1761000000000000103, 1761100000000000001, now(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, 4, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:remove', '#', 1761000000000000103, 1761100000000000001, now(), null, null, '');
|
||||
<#if enableExport>
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, 5, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:export', '#', 1761000000000000103, 1761100000000000001, now(), null, null, '');
|
||||
</#if>
|
||||
@@ -0,0 +1,20 @@
|
||||
-- 菜单 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[0]}, '${functionName}', ${parentMenuId}, 1, '${businessName}', '${moduleName}/${businessName}/index', 'N', 'Y', 'C', '0', '0', '${permissionPrefix}:list', '#', 1761000000000000103, 1761100000000000001, getdate(), null, null, '${functionName}菜单');
|
||||
|
||||
-- 按钮 SQL
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[1]}, '${functionName}查询', ${table.menuIds[0]}, 1, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:query', '#', 1761000000000000103, 1761100000000000001, getdate(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[2]}, '${functionName}新增', ${table.menuIds[0]}, 2, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:add', '#', 1761000000000000103, 1761100000000000001, getdate(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[3]}, '${functionName}修改', ${table.menuIds[0]}, 3, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:edit', '#', 1761000000000000103, 1761100000000000001, getdate(), null, null, '');
|
||||
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[4]}, '${functionName}删除', ${table.menuIds[0]}, 4, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:remove', '#', 1761000000000000103, 1761100000000000001, getdate(), null, null, '');
|
||||
<#if enableExport>
|
||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||
values(${table.menuIds[5]}, '${functionName}导出', ${table.menuIds[0]}, 5, '#', '', 'N', 'Y', 'F', '0', '0', '${permissionPrefix}:export', '#', 1761000000000000103, 1761100000000000001, getdate(), null, null, '');
|
||||
</#if>
|
||||
101
ruoyi-modules/ruoyi-gen/src/main/resources/fm/vue/api.ts.ftl
Normal file
101
ruoyi-modules/ruoyi-gen/src/main/resources/fm/vue/api.ts.ftl
Normal file
@@ -0,0 +1,101 @@
|
||||
import type { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
<#if !table.tree>
|
||||
import type { PageResult } from '@/api/types';
|
||||
</#if>
|
||||
import type { AxiosPromise } from '@/utils/api-types';
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 查询${functionName}列表
|
||||
* @param query
|
||||
* @returns {*}
|
||||
*/
|
||||
export const list${BusinessName} = (query?: ${BusinessName}Query): AxiosPromise<<#if table.tree>${BusinessName}VO[]<#else>PageResult<${BusinessName}VO></#if>> => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询${functionName}详细
|
||||
* @param ${pkColumn.javaField}
|
||||
*/
|
||||
export const get${BusinessName} = (${pkColumn.javaField}: string | number): AxiosPromise<${BusinessName}VO> => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增${functionName}
|
||||
* @param data
|
||||
*/
|
||||
export const add${BusinessName} = (data: ${BusinessName}Form) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改${functionName}
|
||||
* @param data
|
||||
*/
|
||||
export const update${BusinessName} = (data: ${BusinessName}Form) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
/**
|
||||
* 修改${functionName}状态
|
||||
* @param ${pkColumn.javaField}
|
||||
* @param status
|
||||
*/
|
||||
export const change${BusinessName}Status = (${pkColumn.javaField}: string | number, status: <#if statusColumn.javaType == 'Boolean'>boolean<#elseif statusColumn.javaType == 'String'>string<#else> number</#if>) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/changeStatus',
|
||||
method: 'put',
|
||||
data: {
|
||||
${pkColumn.javaField},
|
||||
${statusField}: status
|
||||
}
|
||||
});
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/**
|
||||
* 调整${functionName}排序
|
||||
* @param ${pkColumn.javaField}
|
||||
* @param sortValue
|
||||
*/
|
||||
export const update${BusinessName}Sort = (${pkColumn.javaField}: string | number, sortValue: <#if sortColumn.javaType == 'String' || sortColumn.javaType == 'LocalDateTime'>string<#else> number</#if>) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/updateSort',
|
||||
method: 'put',
|
||||
data: {
|
||||
${pkColumn.javaField},
|
||||
${sortField}: sortValue
|
||||
}
|
||||
});
|
||||
};
|
||||
</#if>
|
||||
|
||||
/**
|
||||
* 删除${functionName}
|
||||
* @param ${pkColumn.javaField}
|
||||
*/
|
||||
export const del${BusinessName} = (${pkColumn.javaField}: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,691 @@
|
||||
<template>
|
||||
<div class="p-2 page-shell ${moduleName}-${businessName}-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "input" || column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="queryParams.${column.javaField}" placeholder="请输入${column.columnLabel}" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="queryParams.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<#if column.javaType == "Boolean">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
<el-option label="开启" :value="0" />
|
||||
<el-option label="关闭" :value="1" />
|
||||
<#else>
|
||||
<el-option label="开启" value="0" />
|
||||
<el-option label="关闭" value="1" />
|
||||
</#if>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable>
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType != "BETWEEN">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.${column.javaField}"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
<el-form-item label="${column.columnLabel}" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange${column.capJavaField}"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>${functionName}列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
||||
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
|
||||
<#if enableExport>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['${moduleName}:${businessName}:export']">导出</el-button>
|
||||
</#if>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-table
|
||||
ref="${businessName}TableRef"
|
||||
v-loading="loading"
|
||||
class="data-table"
|
||||
:data="${businessName}List"
|
||||
row-key="${treeCode}"
|
||||
border
|
||||
:default-expand-all="isExpandAll"
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
>
|
||||
<#assign firstTreeListField = "">
|
||||
<#list columns as tempColumn>
|
||||
<#if !tempColumn.pk && tempColumn.list && "" != tempColumn.javaField && firstTreeListField == "">
|
||||
<#assign firstTreeListField = tempColumn.javaField>
|
||||
</#if>
|
||||
</#list>
|
||||
<#list columns as column>
|
||||
<#if column.pk>
|
||||
<#elseif enableStatus && statusField == column.javaField>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
:active-value="${statusField}ActiveValue"
|
||||
:inactive-value="${statusField}InactiveValue"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" width="160">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="160">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<#if column.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${column.javaField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "switch">
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" width="120">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="120">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "datetime">
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" width="180">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="180">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.${column.javaField}, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "imageUpload">
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}Url" width="100">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}Url" width="100">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<image-preview :src="scope.row.${column.javaField}Url" :width="50" :height="50"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.dictColumn>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
</#if>
|
||||
<template #default="scope">
|
||||
<#if column.htmlType == "checkbox">
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField} ? scope.row.${column.javaField}.split(',') : []"/>
|
||||
<#else>
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField}"/>
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && "" != column.javaField>
|
||||
<#if column.javaField == firstTreeListField>
|
||||
<el-table-column label="${column.columnLabel}" prop="${column.javaField}" />
|
||||
<#else>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" />
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<#if enableStatus && !statusColumn.list>
|
||||
<el-table-column label="${statusColumn.columnComment}" align="center" prop="${statusField}">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${statusField}"
|
||||
:active-value="${statusField}ActiveValue"
|
||||
:inactive-value="${statusField}InactiveValue"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<#if enableSort && !sortColumn.list>
|
||||
<el-table-column label="${sortColumn.columnComment}" align="center" prop="${sortField}" width="160">
|
||||
<template #default="scope">
|
||||
<#if sortColumn.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${sortField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${sortColumn.columnComment}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${sortField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="新增" placement="top">
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<!-- 添加或修改${functionName}对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="${businessName}FormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if "" != treeParentCode && column.javaField == treeParentCode>
|
||||
<el-form-item label="${column.columnLabel}" prop="${treeParentCode}">
|
||||
<el-tree-select
|
||||
v-model="form.${treeParentCode}"
|
||||
:data="${businessName}Options"
|
||||
:props="{ value: '${treeCode}', label: '${treeName}', children: 'children' } as any"
|
||||
value-key="${treeCode}"
|
||||
placeholder="请选择${column.columnLabel}"
|
||||
check-strictly
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "input">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" placeholder="请输入${column.columnLabel}" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="form.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<image-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<file-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<el-form-item label="${column.columnLabel}">
|
||||
<editor v-model="form.${column.javaField}" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.value">
|
||||
{{dict.label}}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox>请选择字典生成</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
>{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio value="1">请选择字典生成</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-switch
|
||||
v-model="form.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="form.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="${BusinessName}" lang="ts">
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
import { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import { useLoading } from '@/hooks/async/useLoading';
|
||||
import { useFormDialog } from '@/hooks/dialog/useFormDialog';
|
||||
<#if needAddDateRange>
|
||||
import { useDateRangeQuery } from '@/hooks/form/useDateRangeQuery';
|
||||
</#if>
|
||||
import { useSearchReset } from '@/hooks/form/useSearchReset';
|
||||
import { useSearchToggle } from '@/hooks/form/useSearchToggle';
|
||||
import { useTreeTableExpand } from '@/hooks/tree/useTreeTableExpand';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/utils/dict';
|
||||
</#if>
|
||||
import modal from '@/plugins/modal';
|
||||
import { handleTree<#if needParseTime>, parseTime</#if> } from '@/utils/ruoyi';
|
||||
<#if enableExport>
|
||||
import { download as requestDownload } from '@/utils/request';
|
||||
</#if>
|
||||
|
||||
<#if needDict>
|
||||
const { ${dictsNoSymbol} } = toRefs<any>(useDict(${dicts}));
|
||||
</#if>
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
</#if>
|
||||
|
||||
type ${BusinessName}Option = {
|
||||
${treeCode}: <#if treeParentColumn.javaType == 'String'>string<#else> number</#if>;
|
||||
${treeName}: string;
|
||||
children?: ${BusinessName}Option[];
|
||||
};
|
||||
|
||||
const ${businessName}List = ref<${BusinessName}VO[]>([]);
|
||||
const ${businessName}Options = ref<${BusinessName}Option[]>([]);
|
||||
const all${BusinessName}Options = ref<${BusinessName}Option[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const { showSearch } = useSearchToggle();
|
||||
const { loading, setLoading, withLoading } = useLoading();
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const ${businessName}FormRef = ref<ElFormInstance>();
|
||||
const ${businessName}TableRef = ref<ElTableInstance>();
|
||||
const { isExpandAll, handleToggleExpandAll } = useTreeTableExpand<${BusinessName}VO>({
|
||||
tableRef: ${businessName}TableRef,
|
||||
data: ${businessName}List
|
||||
});
|
||||
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
const {
|
||||
dateRange: dateRange${column.capJavaField},
|
||||
applyDateRange: apply${column.capJavaField}DateRange,
|
||||
resetDateRange: reset${column.capJavaField}DateRange
|
||||
} = useDateRangeQuery('${column.capJavaField}');
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const initFormData: ${BusinessName}Form = {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
<#else>
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
|
||||
const data = reactive<PageData<${BusinessName}Form, ${BusinessName}Query>>({
|
||||
form: {...initFormData},
|
||||
queryParams: {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType != "datetime" || column.queryType != "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
params: {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.required>
|
||||
${column.javaField}: [
|
||||
{ required: true, message: "${column.columnLabel}不能为空", trigger: <#if column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch" || column.htmlType == "inputNumber">"change"<#else>"blur"</#if> }
|
||||
],
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { dialog, resetForm: reset, openDialog, showDialog, closeDialog } = useFormDialog({
|
||||
form,
|
||||
formRef: ${businessName}FormRef,
|
||||
initialFormData: initFormData
|
||||
});
|
||||
|
||||
/** 查询${functionName}列表 */
|
||||
const getList = async () => {
|
||||
await withLoading(async () => {
|
||||
<#if needAddDateRange>
|
||||
let params = queryParams.value;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
params = apply${column.capJavaField}DateRange(params);
|
||||
</#if>
|
||||
</#list>
|
||||
const res = await list${BusinessName}(params);
|
||||
<#else>
|
||||
const res = await list${BusinessName}(queryParams.value);
|
||||
</#if>
|
||||
const data = handleTree<${BusinessName}VO>(res.data, '${treeCode}', '${treeParentCode}');
|
||||
if (data) {
|
||||
${businessName}List.value = data;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 查询${functionName}下拉树结构 */
|
||||
const getTreeselect = async (excludeId?: string | number) => {
|
||||
const res = await list${BusinessName}();
|
||||
const data: ${BusinessName}Option = { ${treeCode}: ${treeRootValueTsLiteral}, ${treeName}: '顶级节点', children: [] };
|
||||
data.children = handleTree<${BusinessName}Option>(res.data, '${treeCode}', '${treeParentCode}');
|
||||
all${BusinessName}Options.value = [data];
|
||||
${businessName}Options.value = excludeId != null ? filterTreeOptions(all${BusinessName}Options.value, excludeId) : all${BusinessName}Options.value;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
const { resetQuery } = useSearchReset({
|
||||
queryFormRef,
|
||||
queryParams,
|
||||
resetExtras: () => {
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
reset${column.capJavaField}DateRange();
|
||||
</#if>
|
||||
</#list>
|
||||
},
|
||||
afterReset: () => {
|
||||
handleQuery();
|
||||
}
|
||||
});
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = (row?: Partial<${BusinessName}VO>) => {
|
||||
openDialog('添加${functionName}');
|
||||
getTreeselect();
|
||||
if (row != null && row.${treeCode}) {
|
||||
form.value.${treeParentCode} = row.${treeCode};
|
||||
} else {
|
||||
form.value.${treeParentCode} = ${treeRootValueTsLiteral};
|
||||
}
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row: Partial<${BusinessName}VO>) => {
|
||||
reset();
|
||||
await getTreeselect(row.${treeCode});
|
||||
if (row != null) {
|
||||
form.value.${treeParentCode} = row.${treeParentCode};
|
||||
}
|
||||
const res = await get${BusinessName}(row.${pkColumn.javaField});
|
||||
Object.assign(form.value, res.data);
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.split(",");
|
||||
</#if>
|
||||
</#list>
|
||||
showDialog('修改${functionName}');
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
${businessName}FormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.join(",");
|
||||
</#if>
|
||||
</#list>
|
||||
if (form.value.${pkColumn.javaField}) {
|
||||
await update${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await add${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
modal.msgSuccess('操作成功');
|
||||
closeDialog();
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row: Partial<${BusinessName}VO>) => {
|
||||
await modal.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?');
|
||||
setLoading(true);
|
||||
await del${BusinessName}(row.${pkColumn.javaField}).finally(() => setLoading(false));
|
||||
await getList();
|
||||
modal.msgSuccess('删除成功');
|
||||
};
|
||||
|
||||
const filterTreeOptions = (options: ${BusinessName}Option[], excludeId: string | number): ${BusinessName}Option[] => {
|
||||
return options
|
||||
.filter(item => item.${treeCode} !== excludeId)
|
||||
.map(item => ({
|
||||
...item,
|
||||
children: item.children ? filterTreeOptions(item.children, excludeId) : []
|
||||
}));
|
||||
};
|
||||
|
||||
<#if enableStatus>
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
const text = row.${statusField} === ${statusField}ActiveValue ? '启用' : '停用';
|
||||
try {
|
||||
await modal.confirm('确认要"' + text + '"吗?');
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, row.${statusField});
|
||||
modal.msgSuccess(text + '成功');
|
||||
} catch (err) {
|
||||
row.${statusField} = row.${statusField} === ${statusField}ActiveValue ? ${statusField}InactiveValue : ${statusField}ActiveValue;
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/** 排序调整 */
|
||||
const handleSortChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
try {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, row.${sortField});
|
||||
modal.msgSuccess('排序更新成功');
|
||||
} catch (err) {
|
||||
await getList();
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableExport>
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
requestDownload(
|
||||
'${moduleName}/${businessName}/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`${businessName}_${r'${new Date().getTime()}'}.xlsx`
|
||||
);
|
||||
};
|
||||
</#if>
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
615
ruoyi-modules/ruoyi-gen/src/main/resources/fm/vue/index.vue.ftl
Normal file
615
ruoyi-modules/ruoyi-gen/src/main/resources/fm/vue/index.vue.ftl
Normal file
@@ -0,0 +1,615 @@
|
||||
<template>
|
||||
<div class="p-2 page-shell ${moduleName}-${businessName}-page">
|
||||
<div class="search-wrap">
|
||||
<el-card shadow="hover" class="search-panel" :class="{ 'is-collapsed': !showSearch }">
|
||||
<template #header>
|
||||
<div class="panel-heading search-panel-toggle" @click.stop="showSearch = !showSearch">
|
||||
<div><h3>筛选条件</h3></div>
|
||||
</div>
|
||||
</template>
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" class="query-form">
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "input" || column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="queryParams.${column.javaField}" placeholder="请输入${column.columnLabel}" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="queryParams.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<el-option v-for="dict in ${column.dictType}" :key="dict.value" :label="dict.label" :value="dict.value"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<#if column.javaType == "Boolean">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
<el-option label="开启" :value="0" />
|
||||
<el-option label="关闭" :value="1" />
|
||||
<#else>
|
||||
<el-option label="开启" value="0" />
|
||||
<el-option label="关闭" value="1" />
|
||||
</#if>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif (column.htmlType == "select" || column.htmlType == "radio") && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${column.columnLabel}" clearable >
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType != "BETWEEN">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="queryParams.${column.javaField}"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择${column.columnLabel}"
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
<el-form-item label="${column.columnLabel}" style="width: 308px">
|
||||
<el-date-picker
|
||||
v-model="dateRange${column.capJavaField}"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<el-card shadow="hover" class="table-panel">
|
||||
<template #header>
|
||||
<div class="toolbar-shell">
|
||||
<div class="table-heading">
|
||||
<h3>${functionName}列表</h3>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||
<#if enableExport>
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['${moduleName}:${businessName}:export']">导出</el-button>
|
||||
</#if>
|
||||
<right-toolbar v-model:show-search="showSearch" :search="false" @query-table="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table v-loading="loading" border class="data-table" :data="${businessName}List" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<#list columns as column>
|
||||
<#if column.pk && column.list>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" />
|
||||
<#elseif enableStatus && statusField == column.javaField>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
:active-value="${statusField}ActiveValue"
|
||||
:inactive-value="${statusField}InactiveValue"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif enableSort && sortField == column.javaField>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="160">
|
||||
<template #default="scope">
|
||||
<#if column.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${column.columnLabel}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${column.javaField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "switch">
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="120">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "datetime">
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.${column.javaField}, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.htmlType == "imageUpload">
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}Url" width="100">
|
||||
<template #default="scope">
|
||||
<image-preview :src="scope.row.${column.javaField}Url" :width="50" :height="50"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && column.dictColumn>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}">
|
||||
<template #default="scope">
|
||||
<#if column.htmlType == "checkbox">
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField} ? scope.row.${column.javaField}.split(',') : []"/>
|
||||
<#else>
|
||||
<dict-tag :options="${column.dictType}" :value="scope.row.${column.javaField}"/>
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<#elseif column.list && "" != column.javaField>
|
||||
<el-table-column label="${column.columnLabel}" align="center" prop="${column.javaField}" />
|
||||
</#if>
|
||||
</#list>
|
||||
<#if enableStatus && !statusColumn.list>
|
||||
<el-table-column label="${statusColumn.columnComment}" align="center" prop="${statusField}">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.${statusField}"
|
||||
<#if statusColumn.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<#if enableSort && !sortColumn.list>
|
||||
<el-table-column label="${sortColumn.columnComment}" align="center" prop="${sortField}" width="160">
|
||||
<template #default="scope">
|
||||
<#if sortColumn.javaType == "LocalDateTime">
|
||||
<el-date-picker
|
||||
v-model="scope.row.${sortField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="选择${sortColumn.columnComment}"
|
||||
@change="handleSortChange(scope.row)"
|
||||
/>
|
||||
<#else>
|
||||
<el-input-number v-model="scope.row.${sortField}" controls-position="right" :min="0" @change="handleSortChange(scope.row)" />
|
||||
</#if>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</#if>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
<!-- 添加或修改${functionName}对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="${businessName}FormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<#list columns as column>
|
||||
<#if (column.insert || column.edit) && !column.pk>
|
||||
<#if column.htmlType == "input">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" placeholder="请输入${column.columnLabel}" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "inputNumber">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input-number v-model="form.${column.javaField}" controls-position="right" />
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "imageUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<image-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "fileUpload">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<file-upload v-model="form.${column.javaField}"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "editor">
|
||||
<el-form-item label="${column.columnLabel}">
|
||||
<editor v-model="form.${column.javaField}" :min-height="192"/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "select" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-select v-model="form.${column.javaField}" placeholder="请选择${column.columnLabel}">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
:label="dict.value">
|
||||
{{dict.label}}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "checkbox" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-checkbox-group v-model="form.${column.javaField}">
|
||||
<el-checkbox>请选择字典生成</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && column.dictType?has_content>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio
|
||||
v-for="dict in ${column.dictType}"
|
||||
:key="dict.value"
|
||||
<#if column.javaType == "Integer" || column.javaType == "Long">
|
||||
:value="parseInt(dict.value)"
|
||||
<#else>
|
||||
:value="dict.value"
|
||||
</#if>
|
||||
>{{dict.label}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "radio" && !(column.dictType?has_content)>
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-radio-group v-model="form.${column.javaField}">
|
||||
<el-radio value="1">请选择字典生成</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "switch">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-switch
|
||||
v-model="form.${column.javaField}"
|
||||
<#if column.javaType == "Boolean">
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
<#elseif column.javaType == "Integer" || column.javaType == "Long">
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
<#else>
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
</#if>
|
||||
/>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "datetime">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-date-picker clearable
|
||||
v-model="form.${column.javaField}"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择${column.columnLabel}">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<#elseif column.htmlType == "textarea">
|
||||
<el-form-item label="${column.columnLabel}" prop="${column.javaField}">
|
||||
<el-input v-model="form.${column.javaField}" type="textarea" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="${BusinessName}" lang="ts">
|
||||
import {
|
||||
add${BusinessName},
|
||||
<#if enableStatus>
|
||||
change${BusinessName}Status,
|
||||
</#if>
|
||||
del${BusinessName},
|
||||
get${BusinessName},
|
||||
list${BusinessName},
|
||||
<#if enableSort>
|
||||
update${BusinessName}Sort,
|
||||
</#if>
|
||||
update${BusinessName}
|
||||
} from '@/api/${moduleName}/${businessName}';
|
||||
import { ${BusinessName}Form, ${BusinessName}Query, ${BusinessName}VO } from '@/api/${moduleName}/${businessName}/types';
|
||||
import { useLoading } from '@/hooks/async/useLoading';
|
||||
import { useFormDialog } from '@/hooks/dialog/useFormDialog';
|
||||
<#if needAddDateRange>
|
||||
import { useDateRangeQuery } from '@/hooks/form/useDateRangeQuery';
|
||||
</#if>
|
||||
import { useSearchReset } from '@/hooks/form/useSearchReset';
|
||||
import { useSearchToggle } from '@/hooks/form/useSearchToggle';
|
||||
import { useTableSelection } from '@/hooks/table/useTableSelection';
|
||||
<#if needDict>
|
||||
import { useDict } from '@/utils/dict';
|
||||
</#if>
|
||||
<#if needParseTime>
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
</#if>
|
||||
import modal from '@/plugins/modal';
|
||||
<#if enableExport>
|
||||
import { download as requestDownload } from '@/utils/request';
|
||||
</#if>
|
||||
|
||||
<#if needDict>
|
||||
const { ${dictsNoSymbol} } = toRefs<any>(useDict(${dicts}));
|
||||
</#if>
|
||||
|
||||
<#if enableStatus>
|
||||
const ${statusField}ActiveValue = <#if statusColumn.javaType == "Boolean">true<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">0<#else>'0'</#if>;
|
||||
const ${statusField}InactiveValue = <#if statusColumn.javaType == "Boolean">false<#elseif statusColumn.javaType == "Integer" || statusColumn.javaType == "Long">1<#else>'1'</#if>;
|
||||
</#if>
|
||||
|
||||
const ${businessName}List = ref<${BusinessName}VO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const { loading, withLoading } = useLoading(true);
|
||||
const { showSearch } = useSearchToggle();
|
||||
const total = ref(0);
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
const {
|
||||
dateRange: dateRange${column.capJavaField},
|
||||
applyDateRange: apply${column.capJavaField}DateRange,
|
||||
resetDateRange: reset${column.capJavaField}DateRange
|
||||
} = useDateRangeQuery('${column.capJavaField}');
|
||||
</#if>
|
||||
</#list>
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const ${businessName}FormRef = ref<ElFormInstance>();
|
||||
|
||||
const initFormData: ${BusinessName}Form = {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.htmlType == "checkbox">
|
||||
${column.javaField}: [],
|
||||
<#else>
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
const data = reactive<PageData<${BusinessName}Form, ${BusinessName}Query>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType != "datetime" || column.queryType != "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
params: {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN">
|
||||
${column.javaField}: undefined,
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
<#if column.required>
|
||||
${column.javaField}: [
|
||||
{ required: true, message: "${column.columnLabel}不能为空", trigger: <#if column.htmlType == "select" || column.htmlType == "radio" || column.htmlType == "switch" || column.htmlType == "inputNumber">"change"<#else>"blur"</#if> }
|
||||
],
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
const { ids, single, multiple, handleSelectionChange } = useTableSelection<${BusinessName}VO>(item => item.${pkColumn.javaField});
|
||||
const { dialog, resetForm: reset, openDialog, showDialog, closeDialog } = useFormDialog({
|
||||
form,
|
||||
formRef: ${businessName}FormRef,
|
||||
initialFormData: initFormData
|
||||
});
|
||||
|
||||
/** 查询${functionName}列表 */
|
||||
const getList = async () => {
|
||||
await withLoading(async () => {
|
||||
<#if needAddDateRange>
|
||||
let params = queryParams.value;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
params = apply${column.capJavaField}DateRange(params);
|
||||
</#if>
|
||||
</#list>
|
||||
const res = await list${BusinessName}(params);
|
||||
<#else>
|
||||
const res = await list${BusinessName}(queryParams.value);
|
||||
</#if>
|
||||
${businessName}List.value = res.data?.rows;
|
||||
total.value = res.data?.total;
|
||||
});
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const { resetQuery } = useSearchReset({
|
||||
queryFormRef,
|
||||
queryParams,
|
||||
pageNumKey: 'pageNum',
|
||||
pageSizeKey: 'pageSize',
|
||||
initialPageSize: 10,
|
||||
resetExtras: () => {
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "datetime" && column.queryType == "BETWEEN" && column.query>
|
||||
reset${column.capJavaField}DateRange();
|
||||
</#if>
|
||||
</#list>
|
||||
},
|
||||
afterReset: () => {
|
||||
handleQuery();
|
||||
}
|
||||
});
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
openDialog('添加${functionName}');
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: Partial<${BusinessName}VO>) => {
|
||||
reset();
|
||||
const _${pkColumn.javaField} = row?.${pkColumn.javaField} || ids.value[0];
|
||||
const res = await get${BusinessName}(_${pkColumn.javaField});
|
||||
Object.assign(form.value, res.data);
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.split(",");
|
||||
</#if>
|
||||
</#list>
|
||||
showDialog('修改${functionName}');
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
${businessName}FormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
buttonLoading.value = true;
|
||||
<#list columns as column>
|
||||
<#if column.htmlType == "checkbox">
|
||||
form.value.${column.javaField} = form.value.${column.javaField}.join(",");
|
||||
</#if>
|
||||
</#list>
|
||||
if (form.value.${pkColumn.javaField}) {
|
||||
await update${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
} else {
|
||||
await add${BusinessName}(form.value).finally(() => (buttonLoading.value = false));
|
||||
}
|
||||
modal.msgSuccess('操作成功');
|
||||
closeDialog();
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: Partial<${BusinessName}VO>) => {
|
||||
const _${pkColumn.javaField}s = row?.${pkColumn.javaField} || ids.value;
|
||||
await modal.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?');
|
||||
await del${BusinessName}(_${pkColumn.javaField}s);
|
||||
modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
<#if enableExport>
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
requestDownload(
|
||||
'${moduleName}/${businessName}/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`${businessName}_${r'${new Date().getTime()}'}.xlsx`
|
||||
);
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableStatus>
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
const text = row.${statusField} === ${statusField}ActiveValue ? '启用' : '停用';
|
||||
try {
|
||||
await modal.confirm('确认要"' + text + '"吗?');
|
||||
await change${BusinessName}Status(row.${pkColumn.javaField}, row.${statusField});
|
||||
modal.msgSuccess(text + '成功');
|
||||
} catch (err) {
|
||||
row.${statusField} = row.${statusField} === ${statusField}ActiveValue ? ${statusField}InactiveValue : ${statusField}ActiveValue;
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
<#if enableSort>
|
||||
/** 排序调整 */
|
||||
const handleSortChange = async (row: Partial<${BusinessName}VO>) => {
|
||||
try {
|
||||
await update${BusinessName}Sort(row.${pkColumn.javaField}, row.${sortField});
|
||||
modal.msgSuccess('排序更新成功');
|
||||
} catch (err) {
|
||||
await getList();
|
||||
}
|
||||
};
|
||||
</#if>
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
export interface ${BusinessName}VO {
|
||||
<#list columns as column>
|
||||
<#if column.list>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}: ${column.tsType};
|
||||
<#if column.htmlType == "imageUpload">
|
||||
/**
|
||||
* ${column.columnComment}Url
|
||||
*/
|
||||
${column.javaField}Url: string;
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
<#if table.tree>
|
||||
/**
|
||||
* 子对象
|
||||
*/
|
||||
children: ${BusinessName}VO[];
|
||||
</#if>
|
||||
}
|
||||
|
||||
export interface ${BusinessName}Form extends BaseEntity {
|
||||
<#list columns as column>
|
||||
<#if column.insert || column.edit>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}?: ${column.tsType};
|
||||
</#if>
|
||||
</#list>
|
||||
}
|
||||
|
||||
export interface ${BusinessName}Query<#if !table.tree> extends PageQuery</#if> {
|
||||
<#list columns as column>
|
||||
<#if column.query>
|
||||
/**
|
||||
* ${column.columnComment}
|
||||
*/
|
||||
${column.javaField}?: ${column.tsType};
|
||||
</#if>
|
||||
</#list>
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?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}.mapper.${ClassName}Mapper">
|
||||
</mapper>
|
||||
10
ruoyi-modules/ruoyi-gen/src/main/resources/generator.yml
Normal file
10
ruoyi-modules/ruoyi-gen/src/main/resources/generator.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
# 代码生成
|
||||
gen:
|
||||
# 作者
|
||||
author: Lion Li
|
||||
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
|
||||
packageName: org.dromara.system
|
||||
# 自动去除表前缀,默认是false
|
||||
autoRemovePre: false
|
||||
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
|
||||
tablePrefix: sys_
|
||||
@@ -0,0 +1,10 @@
|
||||
<?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="org.dromara.gen.mapper.GenTableColumnMapper">
|
||||
|
||||
<resultMap type="org.dromara.gen.domain.GenTableColumn" id="GenTableColumnResult">
|
||||
</resultMap>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?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="org.dromara.gen.mapper.GenTableMapper">
|
||||
<select id="selectTableNameList" resultType="java.lang.String">
|
||||
select table_name from gen_table where data_name = #{dataName,jdbcType=VARCHAR}
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -0,0 +1,3 @@
|
||||
java包使用 `.` 分割 resource 目录使用 `/` 分割
|
||||
<br>
|
||||
此文件目的 防止文件夹粘连找不到 `xml` 文件
|
||||
Reference in New Issue
Block a user