1.前言
我平时写代码习惯使用Mybatis-Plus,但是若依只带了Mybatis的代码生成器。为了提高开发效率,在原有代码基础上,构造了个自己用的Mybatis-Plus生成器。本文会先介绍若依前后端分离版集成Mybatis-Plus,然后在构造自己的Mybatis-Plus代码生成器。为了不浪费大家时间,我每次都会在文章前面说明收费问题。这篇文章所有资源是免费的,并且文章中会附带所有用到的代码。
我已经发布文章了,感觉如果一步步设置还是有点麻烦。如果不想麻烦,可以直接下载我2-5步替换的内容《若依前后端分离版集成Mybatis-Plus代码生成器》,可以看下我的文章《从零开始学若依框架-下载和环境初始化》配置好若依项目,启动成功后,直接从第6步使用代码生成器看。
2.下载代码
2.点击“克隆/下载”按钮。
3.点击“下载 Zip”按钮。
4.等待下载完成后,解压到一个文件夹中。
3.基础设置
用IDEA打开刚才的若依前后端分离版项目,至于新的若依项目怎么配置,可以看下我的文章《从零开始学若依框架-下载和环境初始化》,保证代码能够成功运行。
4.集成Mybatis-Plus
1.打开ruoyi-common模块下的pom.xml文件,添加mybatis-plus的依赖。
<!-- mybatis-plus 增强CRUD -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
2.点击“加载maven改变”按钮,等待依赖下载完成。
3.打开ruoyi-admin模块下的application.yml文件,将原来的mybatis配置删除(可以先将代码注释掉,按照下面步骤增加所有配置,并能成功运行后,再删除),改为mybatis-plus配置。
# MyBatis Plus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
4.将ruoyi-framework模块下的MyBatisConfig.java中配置注入注解注释去掉(按照下面步骤增加所有配置,并能成功运行后,可以将这个文件删除)。
5.在MyBatisConfig.java文件同一包下,创建MybatisPlusConfig类,并在此类中添加以下代码。
package com.ruoyi.framework.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Mybatis Plus 配置
*
* @author ruoyi
*/
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig
{
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
// 阻断插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
*/
public PaginationInnerInterceptor paginationInnerInterceptor()
{
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型为mysql
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(-1L);
return paginationInnerInterceptor;
}
/**
* 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor()
{
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
*/
public BlockAttackInnerInterceptor blockAttackInnerInterceptor()
{
return new BlockAttackInnerInterceptor();
}
}
6.重新启动后端程序,成功启动,表示配置成功。
7.打开ruoyi-admin模块下的application.yml文件,删除第3步中需要删除的mybatis配置。
8.找到ruoyi-framework/src/main/java/com/ruoyi/framework/config包,删除第4步中需要删除的MyBatisConfig.java文件。
9.再次重新启动后端程序,保证第7、第8步删除的部分,不影响程序运行。程序再次运行成功,表示集成Mybatis-Plus部分完成。
5.实现Mybatis-Plus代码生成器
1.打开ruoyi-common模块下的pom.xml,添加以下与Mybatis-Plus代码生成相关的所有依赖。
<!-- Hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis-plus代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--其他mybatis-plus生成器依赖-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.33</version>
</dependency>
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.17.0.RELEASE</version>
</dependency>
2.点击“加载maven改变”按钮,等待依赖下载完成。
3.在ruoyi-admin模块下的resources文件夹下,创建mybatis-plus-generator.yml文件,并在此文件中添加以下代码,可根据注释按照自己实际需求进行设置。
# 代码生成
generator:
# 作者
author: py
# 模板路径,需要放在resources路径下,会分别从generator/js,generator/plus,generator/vue中读取js、java和vue模板
generatorPath: generator
# 文件存在是否覆盖
fileOverride: false
# java代码保存路径
javaPath: ruoyi-admin/src/main/java
# 默认生成包路径,优先使用此配置。如果此配置为空,读取gen_table中的数据(package_name),不拼接业务名
# 若依自带的生成器保存位置就是packageName,因为若依每次生成代码,前端页面可以手动修改保存
# 但是为了方便,会自动拼接业务名(business_name)。若是想和若依框架统一,可以修改下Generator
packageName: com.ruoyi.data
# 自动去除表前缀,默认是false
autoRemovePre: false
# 表前缀,当去除表前缀为true时生效
tablePrefix: ruoyi_
# 前端代码保存路径
uiPath: ruoyi-ui/src
# js代码保存路径(相对于uiPath的路径),会自动拼接(uiPath + "/" + jsPath)
jsPath: py/api
# 页面代码路径(相对于uiPath的路径),会自动拼接(uiPath + "/" + viewPath)
viewPath: views/py
4.打开ruoyi-admin模块下的application.yml文件,在spring配置中导入mybatis-plus-generator.yml文件,使其生效。
# 引入其他配置文件
config:
import: mybatis-plus-generator.yml
5.在ruoyi-admin模块下的com.ruoyi包下建一个config包,并在此包下创建MybatisPlusGenConfig类,并在此类中添加以下代码。
package com.ruoyi.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "generator")
public class MybatisPlusGenConfig {
// 作者
private String author;
// 模板路径
private String generatorPath;
// 文件存在是否覆盖
private boolean fileOverride;
// java代码保存路径
private String javaPath;
// 默认生成包路径
private String packageName;
// 自动去除表前缀
private boolean autoRemovePre;
// 表前缀
private String tablePrefix;
// 前端代码保存路径
private String uiPath;
// js代码保存路径
private String jsPath;
// 页面代码路径
private String viewPath;
}
6.在ruoyi-admin模块下的com.ruoyi包下建一个domain包,并在此包下添加BasePojo类,并在此类中添加以下代码。
package com.ruoyi.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import java.util.Date;
@Data
public class BasePojo {
//创建者id
@TableField("create_by")
private String createBy;
//创建时间
@TableField("create_time")
private Date createTime;
//更新者id
@TableField("update_by")
private String updateBy;
//更新时间
@TableField("update_time")
private Date updateTime;
//是否可用,2代表可用,0代表删除
@TableField("del_flag")
@TableLogic(value = "0", delval = "2")
private String delFlag;
}
7.在ruoyi-admin模块下的com.ruoyi包下建一个util包,并在此包下添加MybatisPlusGenerator类,并在此类中添加以下代码。
package com.ruoyi.util;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.config.MybatisPlusGenConfig;
import com.ruoyi.domain.BasePojo;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.service.GenTableServiceImpl;
import com.ruoyi.generator.util.VelocityInitializer;
import com.ruoyi.generator.util.VelocityUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileOutputStream;
import java.io.StringWriter;
import java.util.HashMap;
@Component("mybatisPlusGenerator" )
public class MybatisPlusGenerator {
@Value("${spring.datasource.druid.master.url}" )
private String mysqlUrl;
@Value("${spring.datasource.druid.master.username}" )
private String mysqlUsername;
@Value("${spring.datasource.druid.master.password}" )
private String mysqlPassword;
@Autowired
private MybatisPlusGenConfig mybatisPlusGenConfig;
/**
* 生成到本地
*
* @param tableName 表名
* @param table 生成表单
*/
public String generatorToLocal(String tableName, GenTable table) {
// 业务名
String businessName = table.getBusinessName();
// 模板保存路径
String templatePath = mybatisPlusGenConfig.getGeneratorPath();
String plusTemplatePath = templatePath + "/plus/";
String packageName = mybatisPlusGenConfig.getPackageName();
if (StrUtil.isBlank(packageName)) {
packageName = table.getPackageName();
} else {
packageName = packageName + "." + businessName;
}
String outputDir = System.getProperty("user.dir" ) + "/" + mybatisPlusGenConfig.getJavaPath();
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(outputDir) // 设置输出目录
.setAuthor(mybatisPlusGenConfig.getAuthor()) // 设置作者
.setServiceName("%sService" ) // 设置Service接口名后缀
.setIdType(IdType.ASSIGN_UUID) // 设置主键生成策略
.setDateType(DateType.ONLY_DATE) // 设置时间类型为Date
.setFileOverride(mybatisPlusGenConfig.isFileOverride()) // 是否覆盖已有文件
.setOpen(false); //生成成功后不自动打开文件夹
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL) // 设置数据库类型
.setUrl(mysqlUrl) // 数据库连接URL
.setUsername(mysqlUsername) // 数据库用户名
.setPassword(mysqlPassword) // 数据库密码
.setDriverName("com.mysql.cj.jdbc.Driver" ); // 数据库驱动类名
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude(tableName) // 指定需要生成代码的表名
.setNaming(NamingStrategy.underline_to_camel) // 设置表名转类名策略
.setColumnNaming(NamingStrategy.underline_to_camel) // 设置列名转属性名策略
.setEntityLombokModel(true) // 设置实体类使用Lombok模型
.setRestControllerStyle(true) // 设置Controller使用REST风格
.setSuperEntityClass(BasePojo.class) // 设置继承父类
.setSuperEntityColumns("create_by", "create_time", "update_by", "update_time", "del_flag" ) // 设置公共字段
.setEntityTableFieldAnnotationEnable(true); //生成@TableField注解
if (mybatisPlusGenConfig.isAutoRemovePre()) {
strategyConfig.setTablePrefix(new String[]{mybatisPlusGenConfig.getTablePrefix()}); // 设置过滤表名前缀
}
// 包配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent(packageName) // 设置父包名
.setMapper("mapper" ) // 设置Mapper接口所在的子包名
.setEntity("domain" ) // 设置实体类所在的子包名
.setController("controller" ) // 设置Controller所在的子包名
.setService("service" ); // 设置Service所在的子包名
// .setXml("mapper"); // 设置Mapper XML文件所在的子包名
// 模板配置
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null) // 不生成XML文件
.setController(plusTemplatePath + "controller.java.vm" ) // 设置Controller类模板路径
.setEntity(plusTemplatePath + "entity.java.vm" ) // 设置实体类模板路径
.setMapper(plusTemplatePath + "mapper.java.vm" ) // 设置Mapper接口模板路径
.setService(plusTemplatePath + "service.java.vm" ) // 设置Service接口模板路径
.setServiceImpl(plusTemplatePath + "serviceImpl.java.vm" ); // 设置ServiceImpl类模板路径
//注入配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// 自定义 Map 对象,可以在模板中通过 cfg.xxx 引用
this.setMap(new HashMap<String, Object>() {{
put("moduleName", table.getModuleName()); // 模块名(英文)
put("businessName", businessName); // 业务名(英文)
put("functionName", table.getFunctionName()); // 功能名(中文)
put("javaType", "String" ); // 表主键java类型
}});
}
};
// 整合配置
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setGlobalConfig(globalConfig)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setPackageInfo(packageConfig)
.setTemplate(templateConfig)
.setCfg(cfg);
// 执行生成
autoGenerator.execute();
generatorUI(businessName, table);
String packageSrc = StrUtil.replace(packageName, ".", "/" );
String savePath = StrUtil.replace(outputDir + "/" + packageSrc, "\\", "/");
return savePath;
}
/**
* 生成前端vue和js文件
*
* @param name 业务名
* @param table 表信息
*/
public void generatorUI(String name, GenTable table) {
String templatePath = mybatisPlusGenConfig.getGeneratorPath();
boolean fileOverride = mybatisPlusGenConfig.isFileOverride();
// 设置主键列信息
new GenTableServiceImpl().setPkColumn(table);
VelocityInitializer.initVelocity();
VelocityContext context = VelocityUtils.prepareContext(table);
String uiPath = System.getProperty("user.dir" ) + "/" + mybatisPlusGenConfig.getUiPath();
// 保存js文件
String jsTemplate = templatePath + "/js/api.js.vm";
String jsPath = uiPath + "/" + mybatisPlusGenConfig.getJsPath() + "/" + name;
String jsFileName = name + ".js";
saveUIFile(jsPath, jsFileName, context, jsTemplate, fileOverride);
// 保存view文件
String viewTemplate = templatePath + "/vue/index.vue.vm";
String viewPath = uiPath + "/" + mybatisPlusGenConfig.getViewPath() + "/" + name;
String viewFileName = "index.vue";
saveUIFile(viewPath, viewFileName, context, viewTemplate, fileOverride);
}
/**
* 保存前端文件
* @param directoryPath 路径
* @param fileName 文件名
* @param context 内容
* @param template 模板
* @param fileOverride 是否覆盖
*/
public void saveUIFile(String directoryPath, String fileName, VelocityContext context, String template, boolean fileOverride) {
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
tpl.merge(context, sw);
FileUtil.mkdir(directoryPath);
File file = new File(directoryPath + "/" + fileName);
if (fileOverride || !file.exists()) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write(sw.toString().getBytes());
fos.flush();
fos.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
8.在ruoyi-admin模块下的com.ruoyi包下建一个util包,并在此包下添加QueryWrapperUtil类(此工具能够动态创建查询条件,能够提高开发效率),并在此类中添加以下代码。
package com.ruoyi.util;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.domain.BasePojo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
public class QueryWrapperUtil {
public static <T extends BasePojo> QueryWrapper<T> getWrapper(T entity) {
QueryWrapper<T> wrapper = new QueryWrapper<>();
Class<? extends BasePojo> entityClass = entity.getClass();
Field[] declaredFields = entityClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
String name = declaredField.getName();
if (!name.equals("serialVersionUID")) {
String getName = StrUtil.upperFirstAndAddPre(name, "get");
Method method = null;
try {
method = entityClass.getMethod(getName);
Object obj = method.invoke(entity);
String column = StrUtil.toSymbolCase(name, '_');
wrapper.eq(ObjectUtil.isNotEmpty(obj), column, obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return wrapper;
}
public static <T extends BasePojo> QueryWrapper<T> getWrapper(T entity, Map<String, String> map) {
QueryWrapper<T> wrapper = new QueryWrapper<>();
Class<? extends BasePojo> entityClass = entity.getClass();
Field[] declaredFields = entityClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
String name = declaredField.getName();
if (!name.equals("serialVersionUID")) {
String getName = StrUtil.upperFirstAndAddPre(name, "get");
Method method = null;
try {
method = entityClass.getMethod(getName);
Object obj = method.invoke(entity);
String column = StrUtil.toSymbolCase(name, '_');
String mybatisValue = map.get(name);
if (StrUtil.isNotEmpty(mybatisValue)) {
switch (mybatisValue) {
case "isNull":
wrapper.isNull(column);
break;
case "isNotNull":
wrapper.isNotNull(column);
break;
case "eq":
wrapper.eq(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "ne":
wrapper.ne(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "like":
wrapper.like(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "notLike":
wrapper.notLike(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "likeRight":
wrapper.likeRight(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "likeLeft":
wrapper.likeLeft(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "lt":
wrapper.lt(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "le":
wrapper.le(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "gt":
wrapper.gt(ObjectUtil.isNotEmpty(obj), column, obj);
break;
case "ge":
wrapper.ge(ObjectUtil.isNotEmpty(obj), column, obj);
break;
default:
break;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return wrapper;
}
}
9.在ruoyi-admin模块下的com.ruoyi包下建一个controller包,并在此包下添加PyController类,并在此类中添加以下代码。
package com.ruoyi.controller;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.mapper.GenTableMapper;
import com.ruoyi.util.MybatisPlusGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/py")
public class PyController extends BaseController {
@Autowired
private GenTableMapper genTableMapper;
@Autowired
private MybatisPlusGenerator mybatisPlusGenerator;
/**
* 生成Mybatis-plus代码
* @param tableName
* @return
*/
@PreAuthorize("@ss.hasPermi('tool:gen:code')")
@Log(title = "代码生成", businessType = BusinessType.GENCODE)
@GetMapping("/tool/gen/genCode/{tableName}")
public AjaxResult genCode(@PathVariable("tableName") String tableName) {
GenTable table = genTableMapper.selectGenTableByName(tableName);
String path = mybatisPlusGenerator.generatorToLocal(tableName, table);
return success(path);
}
}
10.在ruoyi-admin模块下的resources文件夹下建一个generator文件夹,在generator文件夹下再建js、plus和vue三个文件夹。但是在IDEA无法在generator下直接建三个文件夹,需要找到generator文件夹的在本地的位置,然后再建这三个文件夹。
11.在js文件夹中,创建api.js.vm文件,并在此文件中添加以下代码。
import request from '@/utils/request';
let ${businessName}Url = "/py/${businessName}";
// 分页查询${functionName}列表
export function list${BusinessName}(query) {
return request({
url: ${businessName}Url + '/list',
method: 'get',
params: query
})
}
// 不分页查询${functionName}列表
export function get${BusinessName}List(query) {
return request({
url: ${businessName}Url + '/getList',
method: 'get',
params: query
})
}
// 查询${functionName}详细
export function get${BusinessName}(${pkColumn.javaField}) {
return request({
url: ${businessName}Url + '/' +${pkColumn.javaField},
method: 'get'
})
}
// 新增${functionName}
export function add${BusinessName}(data) {
return request({
url: ${businessName}Url,
method: 'post',
data: data
})
}
// 修改${functionName}
export function update${BusinessName}(data) {
return request({
url: ${businessName}Url,
method: 'put',
data: data
})
}
// 删除${functionName}
export function del${BusinessName}(${pkColumn.javaField}s) {
return request({
url: ${businessName}Url + '/' + ${pkColumn.javaField}s,
method: 'delete'
})
}
12.在plus文件夹中,创建controller.java.vm、entity.java.vm、mapper.java.vm、service.java.vm和serviceImpl.java.vm五个文件,分别在文件中添加以下代码。
controller.java.vm文件代码如下:
package ${package.Controller};
import java.util.Arrays;
import java.util.List;
import java.util.Date;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.util.QueryWrapperUtil;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import ${package.Entity}.${table.entityName};
import ${package.Service}.${table.serviceName};
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
/**
* @author ${author}
* @date ${date}
* @description ${cfg.functionName}对应的controller层
*/
@RestController
@RequestMapping("/py/${cfg.businessName}")
public class ${table.controllerName} extends BaseController {
@Autowired
private ${table.serviceName} ${table.entityPath}Service;
/**
* 分页查询${cfg.functionName}列表
*/
@GetMapping("/list")
@PreAuthorize("@ss.hasPermi('py:#if(${cfg.moduleName})${cfg.moduleName}:#end${cfg.businessName}:list')")
public TableDataInfo list(${table.entityName} ${table.entityPath}) {
startPage();
Wrapper<${table.entityName}> wrapper = QueryWrapperUtil.getWrapper(${table.entityPath});
List<${table.entityName}> list = ${table.entityPath}Service.list(wrapper);
return getDataTable(list);
}
/**
* 查询${cfg.functionName}列表
*/
@GetMapping("/getList")
@PreAuthorize("@ss.hasPermi('py:#if(${cfg.moduleName})${cfg.moduleName}:#end${cfg.businessName}:list')")
public AjaxResult getList(${table.entityName} ${table.entityPath}) {
Wrapper<${table.entityName}> wrapper = QueryWrapperUtil.getWrapper(${table.entityPath});
List<${table.entityName}> list = ${table.entityPath}Service.list(wrapper);
return success(list);
}
/**
* 导出${cfg.functionName}列表
*/
@PostMapping("/export")
@PreAuthorize("@ss.hasPermi('py:#if(${cfg.moduleName})${cfg.moduleName}:#end${cfg.businessName}:export')")
@Log(title = "${cfg.functionName}", businessType = BusinessType.EXPORT)
public void export(HttpServletResponse response, ${table.entityName} ${table.entityPath}) {
Wrapper<${table.entityName}> wrapper = QueryWrapperUtil.getWrapper(${table.entityPath});
List<${table.entityName}> list = ${table.entityPath}Service.list(wrapper);
ExcelUtil<${table.entityName}> util = new ExcelUtil<${table.entityName}>(${table.entityName}.class);
util.exportExcel(response, list, "${cfg.functionName}数据");
}
/**
* 获取${cfg.functionName}详细信息
*/
@PreAuthorize("@ss.hasPermi('py:#if(${cfg.moduleName})${cfg.moduleName}:#end${cfg.businessName}:query')")
@GetMapping(value = "/{${cfg.businessName}Id}")
public AjaxResult getInfo(@PathVariable ${cfg.javaType} ${cfg.businessName}Id) {
return success(${table.entityPath}Service.getById(${cfg.businessName}Id));
}
/**
* 新增${cfg.functionName}
*/
@PreAuthorize("@ss.hasPermi('py:#if(${cfg.moduleName})${cfg.moduleName}:#end${cfg.businessName}:add')")
@Log(title = "${cfg.functionName}", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ${table.entityName} ${table.entityPath}) {
${table.entityPath}.setCreateBy(getUsername());
${table.entityPath}.setCreateTime(new Date());
return toAjax(${table.entityPath}Service.save(${table.entityPath}));
}
/**
* 修改${cfg.functionName}
*/
@PreAuthorize("@ss.hasPermi('py:#if(${cfg.moduleName})${cfg.moduleName}:#end${cfg.businessName}:edit')")
@Log(title = "${cfg.functionName}", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody ${table.entityName} ${table.entityPath}) {
${table.entityPath}.setUpdateBy(getUsername());
${table.entityPath}.setUpdateTime(new Date());
return toAjax(${table.entityPath}Service.updateById(${table.entityPath}));
}
/**
* 删除${cfg.functionName}
*/
@PreAuthorize("@ss.hasPermi('py:#if(${cfg.moduleName})${cfg.moduleName}:#end${cfg.businessName}:remove')")
@Log(title = "${cfg.functionName}", businessType = BusinessType.DELETE)
@DeleteMapping("/{${cfg.businessName}Ids}")
public AjaxResult remove(@PathVariable ${cfg.javaType}[] ${cfg.businessName}Ids)
{
return toAjax(${table.entityPath}Service.removeByIds(Arrays.asList(${cfg.businessName}Ids)));
}
}
entity.java.vm文件代码如下:
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
#if(${chainModel})
import lombok.experimental.Accessors;
#end
#end
/**
* @author ${author}
* @date ${date}
* @description ${cfg.functionName}的基础类,对应数据库${table.name}表中的数据
*/
#if(${entityLombokModel})
@Data
#if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
#else
@EqualsAndHashCode(callSuper = false)
#end
#if(${chainModel})
@Accessors(chain = true)
#end
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value="${entity}对象", description="$!{table.comment}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end
#if(${entitySerialVersionUID})
private static final long serialVersionUID = 1L;
#end
## ---------- BEGIN 字段循环遍历 ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
#if(${swagger2})
@ApiModelProperty(value = "${field.comment}")
#else
// ${field.comment}
#end
#end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
## 普通字段
#elseif(${field.fill})
## ----- 存在字段填充设置 -----
#if(${field.convert})
@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.annotationColumnName}")
#end
## 乐观锁注解
#if(${versionFieldName}==${field.name})
@Version
#end
## 逻辑删除注解
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
private ${field.propertyType} ${field.propertyName};
#end
## ---------- END 字段循环遍历 ----------
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
#if(${chainModel})
public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#else
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
#end
this.${field.propertyName} = ${field.propertyName};
#if(${chainModel})
return this;
#end
}
#end
## --foreach end---
#end
## --end of #if(!${entityLombokModel})--
#if(${entityColumnConstant})
#foreach($field in ${table.fields})
public static final String ${field.name.toUpperCase()} = "${field.name}";
#end
#end
#if(${activeRecord})
@Override
protected Serializable pkVal() {
#if(${keyPropertyName})
return this.${keyPropertyName};
#else
return null;
#end
}
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}
mapper.java.vm文件代码如下:
package ${package.Mapper};
import ${package.Entity}.${entity};
import ${superMapperClassPackage};
/**
* @author ${author}
* @date ${date}
* @description ${cfg.functionName}对应的mapper
*/
#if(${kotlin})
interface ${table.mapperName} : ${superMapperClass}<${entity}>
#else
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
}
#end
service.java.vm文件代码如下:
package ${package.Service};
import ${package.Entity}.${entity};
import ${superServiceClassPackage};
/**
* @author ${author}
* @date ${date}
* @description ${cfg.functionName}对应的Service层接口
*/
#if(${kotlin})
interface ${table.serviceName} : ${superServiceClass}<${entity}>
#else
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
}
#end
serviceImpl.java.vm文件代码如下:
package ${package.ServiceImpl};
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;
/**
* @author ${author}
* @date ${date}
* @description ${cfg.functionName}对应的Service层实现类
*/
@Service("${table.entityPath}Service")
#if(${kotlin})
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {
}
#else
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} {
}
#end
13.在vue文件夹中,创建index.vue.vm文件,并在此文件中添加以下代码。
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.htmlType == "input")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-input
v-model="queryParams.${column.javaField}"
placeholder="请输入${comment}"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option
v-for="dict in dict.type.${dictType}"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
<el-form-item label="${comment}" prop="${column.javaField}">
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
<el-form-item label="${comment}" prop="${column.javaField}">
<el-date-picker clearable
v-model="queryParams.${column.javaField}"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
<el-form-item label="${comment}">
<el-date-picker
v-model="daterange${AttrName}"
style="width: 240px"
value-format="yyyy-MM-dd"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
#end
#end
#end
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['py:${moduleName}:${businessName}:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['py:${moduleName}:${businessName}:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['py:${moduleName}:${businessName}:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['py:${moduleName}:${businessName}:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
#foreach($column in $columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk)
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#elseif($column.list && $column.htmlType == "datetime")
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "imageUpload")
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
</template>
</el-table-column>
#elseif($column.list && "" != $column.dictType)
<el-table-column label="${comment}" align="center" prop="${javaField}">
<template slot-scope="scope">
#if($column.htmlType == "checkbox")
<dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
#else
<dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField}"/>
#end
</template>
</el-table-column>
#elseif($column.list && "" != $javaField)
<el-table-column label="${comment}" align="center" prop="${javaField}" />
#end
#end
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['py:${moduleName}:${businessName}:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['py:${moduleName}:${businessName}:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改${functionName}对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
#foreach($column in $columns)
#set($field=$column.javaField)
#if($column.insert && !$column.pk)
#if(($column.usableColumn) || (!$column.superColumn))
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#set($dictType=$column.dictType)
#if($column.htmlType == "input")
<el-form-item label="${comment}" prop="${field}">
<el-input v-model="form.${field}" placeholder="请输入${comment}" />
</el-form-item>
#elseif($column.htmlType == "imageUpload")
<el-form-item label="${comment}" prop="${field}">
<image-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "fileUpload")
<el-form-item label="${comment}" prop="${field}">
<file-upload v-model="form.${field}"/>
</el-form-item>
#elseif($column.htmlType == "editor")
<el-form-item label="${comment}">
<editor v-model="form.${field}" :min-height="192"/>
</el-form-item>
#elseif($column.htmlType == "select" && "" != $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-select v-model="form.${field}" placeholder="请选择${comment}">
<el-option
v-for="dict in dict.type.${dictType}"
:key="dict.value"
:label="dict.label"
#if($column.javaType == "Integer" || $column.javaType == "Long")
:value="parseInt(dict.value)"
#else
:value="dict.value"
#end
></el-option>
</el-select>
</el-form-item>
#elseif($column.htmlType == "select" && $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-select v-model="form.${field}" placeholder="请选择${comment}">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
#elseif($column.htmlType == "checkbox" && "" != $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-checkbox-group v-model="form.${field}">
<el-checkbox
v-for="dict in dict.type.${dictType}"
:key="dict.value"
:label="dict.value">
{{dict.label}}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "checkbox" && $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-checkbox-group v-model="form.${field}">
<el-checkbox>请选择字典生成</el-checkbox>
</el-checkbox-group>
</el-form-item>
#elseif($column.htmlType == "radio" && "" != $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-radio-group v-model="form.${field}">
<el-radio
v-for="dict in dict.type.${dictType}"
:key="dict.value"
#if($column.javaType == "Integer" || $column.javaType == "Long")
:label="parseInt(dict.value)"
#else
:label="dict.value"
#end
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "radio" && $dictType)
<el-form-item label="${comment}" prop="${field}">
<el-radio-group v-model="form.${field}">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
#elseif($column.htmlType == "datetime")
<el-form-item label="${comment}" prop="${field}">
<el-date-picker clearable
v-model="form.${field}"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择${comment}">
</el-date-picker>
</el-form-item>
#elseif($column.htmlType == "textarea")
<el-form-item label="${comment}" prop="${field}">
<el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
</el-form-item>
#end
#end
#end
#end
#if($table.sub)
<el-divider content-position="center">${subTable.functionName}信息</el-divider>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd${subClassName}">添加</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" icon="el-icon-delete" size="mini" @click="handleDelete${subClassName}">删除</el-button>
</el-col>
</el-row>
<el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}">
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="序号" align="center" prop="index" width="50"/>
#foreach($column in $subTable.columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && $column.htmlType == "input")
<el-table-column label="$comment" prop="${javaField}" width="150">
<template slot-scope="scope">
<el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
</template>
</el-table-column>
#elseif($column.list && $column.htmlType == "datetime")
<el-table-column label="$comment" prop="${javaField}" width="240">
<template slot-scope="scope">
<el-date-picker clearable v-model="scope.row.$javaField" type="date" value-format="yyyy-MM-dd" placeholder="请选择$comment" />
</template>
</el-table-column>
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
<el-table-column label="$comment" prop="${javaField}" width="150">
<template slot-scope="scope">
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
<el-option
v-for="dict in dict.type.$column.dictType"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</template>
</el-table-column>
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
<el-table-column label="$comment" prop="${javaField}" width="150">
<template slot-scope="scope">
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
<el-option label="请选择字典生成" value="" />
</el-select>
</template>
</el-table-column>
#end
#end
</el-table>
#end
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/py/api/${businessName}/${businessName}";
export default {
name: "${BusinessName}",
#if(${dicts} != '')
dicts: [${dicts}],
#end
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
#if($table.sub)
// 子表选中数据
checked${subClassName}: [],
#end
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// ${functionName}表格数据
${businessName}List: [],
#if($table.sub)
// ${subTable.functionName}表格数据
${subclassName}List: [],
#end
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
// $comment时间范围
daterange${AttrName}: [],
#end
#end
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
#foreach ($column in $columns)
#if($column.query)
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
},
// 表单参数
form: {},
// 表单校验
rules: {
#foreach ($column in $columns)
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
$column.javaField: [
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
]#if($foreach.count != $columns.size()),#end
#end
#end
}
};
},
created() {
this.getList();
},
methods: {
/** 查询${functionName}列表 */
getList() {
this.loading = true;
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
this.queryParams.params = {};
#break
#end
#end
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
}
#end
#end
list${BusinessName}(this.queryParams).then(response => {
this.${businessName}List = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: undefined#if($foreach.count != $columns.size()),#end
#end
#end
};
#if($table.sub)
this.${subclassName}List = [];
#end
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
this.daterange${AttrName} = [];
#end
#end
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.${pkColumn.javaField});
this.single = selection.length!==1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const ${pkColumn.javaField} = row.${pkColumn.javaField} || this.ids
get${BusinessName}(${pkColumn.javaField}).then(response => {
this.form = response.data;
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.split(",");
#end
#end
#if($table.sub)
this.${subclassName}List = response.data.${subclassName}List;
#end
this.open = true;
this.title = "修改";
});
},
/** 提交按钮 */
submitForm() {
this.#[[$]]#refs["form"].validate(valid => {
if (valid) {
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.join(",");
#end
#end
#if($table.sub)
this.form.${subclassName}List = this.${subclassName}List;
#end
if (this.form.${pkColumn.javaField} != null) {
update${BusinessName}(this.form).then(response => {
this.#[[$modal]]#.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
add${BusinessName}(this.form).then(response => {
this.#[[$modal]]#.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids;
this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?').then(function() {
return del${BusinessName}(${pkColumn.javaField}s);
}).then(() => {
this.getList();
this.#[[$modal]]#.msgSuccess("删除成功");
}).catch(() => {});
},
#if($table.sub)
/** ${subTable.functionName}序号 */
row${subClassName}Index({ row, rowIndex }) {
row.index = rowIndex + 1;
},
/** ${subTable.functionName}添加按钮操作 */
handleAdd${subClassName}() {
let obj = {};
#foreach($column in $subTable.columns)
#if($column.pk || $column.javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
obj.$column.javaField = "";
#end
#end
this.${subclassName}List.push(obj);
},
/** ${subTable.functionName}删除按钮操作 */
handleDelete${subClassName}() {
if (this.checked${subClassName}.length == 0) {
this.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据");
} else {
const ${subclassName}List = this.${subclassName}List;
const checked${subClassName} = this.checked${subClassName};
this.${subclassName}List = ${subclassName}List.filter(function(item) {
return checked${subClassName}.indexOf(item.index) == -1
});
}
},
/** 复选框选中数据 */
handle${subClassName}SelectionChange(selection) {
this.checked${subClassName} = selection.map(item => item.index)
},
#end
/** 导出按钮操作 */
handleExport() {
this.download('${moduleName}/${businessName}/export', {
...this.queryParams
}, `${functionName}_#[[${new Date().getTime()}]]#.xlsx`);
}
}
};
</script>
14.重新启动后端程序。如果启动过程中,提示如下,请点击“Enable”。程序启动成功,表示后端配置完成。
15.在前端(ruoyi-ui)src/api文件夹下创建文件夹py,并且在py文件夹创建py.js文件,在此文件中添加以下代码,并保存文件。
import request from "@/utils/request";
// 生成代码(自定义路径)
export function genPlusCode(tableName) {
return request({
url: 'py/tool/gen/genCode/' + tableName,
method: 'get'
})
}
16.打开前端(ruoyi-ui)views/gen文件夹下的genInfoForm.vue文件,找到代码生成单选位置,添加“MybatisPlus代码生成器”选项,,并保存。
<el-radio v-model="info.genType" label="2">MybatisPlus代码生成器</el-radio>
17.打开前端(ruoyi-ui)views/gen文件夹下的index.vue文件,导入genPlusCode方法。
import { genPlusCode } from "@/api/py/py";
找到handleGenTable方法,将生成调用代码由原来的代码改为改为如下代码,并保存。
if(row.genType === "1") {
genCode(row.tableName).then(response => {
this.$modal.msgSuccess("成功生成到自定义路径:" + row.genPath);
});
} else if (row.genType === "0") {
this.$download.zip("/tool/gen/batchGenCode?tables=" + tableNames, "ruoyi.zip");
} else if (row.genType === "2") {
genPlusCode(row.tableName).then(response => {
this.$modal.msgSuccess("java保存路径:" + response.msg);
});
}
6.使用代码生成器
1.运行前后端程序,登录若依管理系统的管理员账号(默认账号为admin,密码为admin123)。
2.打开菜单“系统工具”下面的“代码生成”菜单。
3.点击“新增”按钮,创建新表。
4.复制下面sql语句,点击“确定”按钮(如果不是最新版若依框架,没有此功能,直接在数据库中执行此sql语句,不形象后续功能测试)。
CREATE TABLE `py_course` (
`id` varchar(32) NOT NULL COMMENT '主键ID',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`name` varchar(128) DEFAULT NULL COMMENT '课程名称',
`teacher_name` varchar(128) DEFAULT NULL COMMENT '老师姓名',
`course_class` varchar(128) DEFAULT NULL COMMENT '课程班级',
`introduce` varchar(500) DEFAULT NULL COMMENT '课程介绍',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='课程信息';
5.点击“编辑”按钮。
6.点击“生成信息”模块。
7.填写基本信息(具体配置信息情况,后面会有说明),并选择“Mybatis-Plus代码生成器”,点击“提交”按钮。
8.点击“生成代码”按钮。
9.后端代码生成情况如下(controller中QueryWrapperUtil工具类具体使用方式后面会有介绍)。
10.打开菜单“系统管理”下面的“菜单管理”菜单。
11.点击“新增”按钮,按照如下方式填写内容,点击“确定”按钮。
12.重新启动前后程序,保证新增代码已经运行(否则会报错404错误)。
13.点击“课程管理”菜单。
14.测试各功能没问题。因为前面设置字段信息,导致出现了id和删除符号等信息,后面会说明字段信息怎么设置,但是代码生成器功能没有问题。
7.配置信息说明
1.后端配置文件mybatis-plus-generator.yml配置说明,会优先使用此配置文件中的配置。
author:作者,只能从这里设置。
generatorPath:模板保存路径,设置在了此配置文件同目录下的generator文件夹,一般不用修改。
fileOverride:是否覆盖文件,默认是false,代表如果文件存在就不会生成新文件。设置为true时,代表不管文件是否存在,都会生成新文件。
javaPath:生成java的路径,因为我一般生成在ruoyi-admin模块下,所以这样配置。如果生成在其他模块,只需要用模块名称替换ruoyi-admin即可,后面部分不用替换。
packageName:生成代码的包名。与javaPath结合使用,javaPath选择模块,packageName选择包。
autoRemovePre:是否自动去表前缀,默认是false,代表去除。如果设置为true,代表除去。
tablePrefix:需要去除的表前缀。autoRemovePre设置为true时生效。
uiPath:前端保存js和vue代码的前缀,一般不用修改。如果修改了若依前端模块名称,用新名称替换ruoyi-ui即可。
jsPath:前端保存js路径,会与uiPath进行拼接。
viewPath:前段保存view路径,会与uiPath进行拼接。
2.前端字段信息模块配置,用于控制查询字段和新增或修改字段(再次说明,Mybatis-Plus生成器不会读取基本信息模块配置,系统默认的生成器会读取)。字段信息不会全部使用,Mybatis-Plus生成器只会读取和前端代码生成相关的信息,后端信息具体配置信息,通过MybatisPlusGenerator类进行设置,对于Mybatis-Plus生成器真正有用的信息只有“字段描述”和“插入”以后的字段(不包含“查询方式”,查询方式怎么配置,后面说明)。
一般简单配置一下即可,复杂功能都是自己再设计代码。
字段名称:前端显示的列名称。
插入、编辑、列表和查询:分别在增、改、列表、查是否显示字段。
必填:修改或增加此项是否必须填写。
显示类型:修改或者填写时,字段类型。一般使用自动配置的即可。
字典类型:只有某个字段使用字典时,设置对应的字典。
3.前端生成信息模块配置,只有后端没有的配置,前后设置的配置才有用。对于生成包路径,优先使用后端mybatis-plus-generator.yml的packageName,当packageName为空时,才使用此路径。生成信息模块的生成模板、前端类型和上级菜单这三个配置项,对于Mybatis-Plus代码生成器没有作用。
生成包路径:同后端mybatis-plus-generator.yml的packageName作用,,当packageName为空时,才使用此路径。一般不要使用此配置,使用后端配置。
生成模块名:模块名称,一般填写英文。例如:system
生成业务名:业务名称,一般填写英文。例如:user
生成功能名:功能名称,一般填写中文。例如:课程信息
生成代码方式:代码通过何种方式生成。zip压缩包和自定义路径是系统自带的代码生成器这里不介绍。MybatisPlus代码生成器:会根据填写配置信息,直接在后端和前端文件夹中生成文件,重启系统后,可以直接使用。
8.开发说明
1.我一般喜欢将表主键设置为varchar(32),所以主键我设置为自动生成uuid。如果有特殊需要,可以在domain.java.vm文件中进行设置。
2.对于表单查询时,默认会根据传来的对象值进行匹配,对不为空的对象属性进行“eq”查询。
如果对象中的字段,有特殊匹配查询,可以创建map传入。我已经根据Mybatis-Plus写了很多常用的字段,匹配方式和Mybatis-Plus条件构造器一致,具体匹配规则可以查Mybatis-Plus条件构造器。如果需要修改匹配规则,可以修改QueryWrapperUtil类。
下面以课程名称和老师名称进行“like”为例进行操作。
注意:
1.QueryWrapperUtil.getWrapper只传对象时,会自动判断属性值是否为空,将不为空的属性匹配“eq”。
2.对于特殊需求的查询条件,需要创建个map保存属性名称(和domain对象的java属性名称一致)和查询方式(QueryWrapperUtil类中匹配值一致),然后向QueryWrapperUtil.getWrapper方法传入对象和map。对于传入map的情况,要把所有匹配情况写清楚,哪怕是“eq”匹配也要表明。当然,也可根据需要调整QueryWrapperUtil匹配方式。
9.总结
这个代码生成器还是有很多不足的,希望大家能够理解。如果大家条件允许的话,希望大家能够打赏一下。不打赏也没有关系,可以关注我,我会不定时发一些与java或若依框架相关的内容。目前,已经发布了《以若依Flowable工作流版本(RuoYi-Vue-Flowable)为基础,进行二次开发》、《以kkFileView为基础,可嵌入项目的前后端分离版(springboot+vue)文件预览系统结合若依框架前后端分离版的使用》、《若依前后端分离版包名修改器》等内容。大家可以根据情况获取资源,最后感谢大家的支持。