若依前后端分离版创建Mybatis-Plus代码生成器

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)文件预览系统结合若依框架前后端分离版的使用》、《若依前后端分离版包名修改器》等内容。大家可以根据情况获取资源,最后感谢大家的支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飘逸飘逸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>