使用 freemarker 制作代码生成器

时间:2022-07-28
本文章向大家介绍使用 freemarker 制作代码生成器,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在开发的过程中发现有大量的重复代码,比如 ControllerService 以及 Mapper 和实体类,后两者都可以使用 Mybatis 自动生成,前两者只能自己写。

本文介绍使用 freemarker 制作代码生成器,可以自动生成 ControllerServiceDtoVue 代码。

首先解释一下为什么使用 freemarker

  • 他是一个模板语言,模板的特点就是有很多不变的,还有一小部分变的;
  • 观察我们的 controller 层代码,大部分是一样的,变的只是实体类的名字,所以动态的数据可以使用占位符;
  • freemarker 提供了包括占位符在内的多种处理方式,所以我们选择它。

当然也可以使用 thymeleaf,两者大同小异,都可以实现。

搭建环境

pom

<!-- 模板引擎freemarker -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.29</version>
</dependency>
<!-- 读xml -->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.1</version>
</dependency>

我们先将用到 freemarker 的地方单独拿出来,封装成一个 工具类

package com.lsu.generator.util;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;

/**
 * 代码生成器工具类
 *
 * @Author wang suo
 * @Date 2020/10/12 0012 18:30
 * @Version 1.0
 */
public class FreemarkerUtil {

    private static String ftlPath = "generator\src\main\java\com\lsu\generator\ftl\";
    private static Template temp;

    public static void initConfig(String ftlName) throws IOException {
        Configuration conf = new Configuration(Configuration.VERSION_2_3_29);
        conf.setDirectoryForTemplateLoading(new File(ftlPath));
        conf.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_29));
        temp = conf.getTemplate(ftlName + ".ftl");
    }

    public static void generator(Map<String, Object> map, String fileName) throws IOException, TemplateException {
        FileWriter fw = new FileWriter(fileName);
        BufferedWriter bw = new BufferedWriter(fw);
        temp.process(map, bw);
        bw.flush();
        fw.close();
    }
}

然后我们就可以制作模板了,新建 controller.ftl

package com.lsu.${module}.controller.admin;

import com.lsu.server.dto.${Domain}Dto;
import com.lsu.server.dto.PageDto;
import com.lsu.server.dto.ResponseDto;
import com.lsu.server.service.${Domain}Service;
import com.lsu.server.util.ValidatorUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**
 * 测试
 *
 * @Author wang suo
 * @Date 2020/10/9 0009 18:21
 * @Version 1.0
 */
@RestController
@RequestMapping("/admin/${domain}")
public class ${Domain}Controller {

    private static final Logger LOG = LoggerFactory.getLogger(${Domain}Controller.class);
    public static final String BUSINESS_NAME = "${tableNameCn}";

    @Resource
    private ${Domain}Service ${domain}Service;

    @PostMapping("/list")
    public ResponseDto<PageDto> getAll(@RequestBody PageDto<${Domain}Dto> pageDto) {
        ResponseDto<PageDto> responseDto = new ResponseDto<>();
        ${domain}Service.getAll(pageDto);
        responseDto.setContent(pageDto);
        return responseDto;
    }

    @PostMapping("/save")
    public ResponseDto<${Domain}Dto> save(@RequestBody ${Domain}Dto ${domain}Dto) {

        // 保存校验
        <#list fieldList as field>
            <#if !field.nullAble>
        ValidatorUtil.require(${domain}Dto.get${field.nameBigHump}(), "${field.nameCn}");
            </#if>
            <#if (field.length > 0)>
        ValidatorUtil.length(${domain}Dto.get${field.nameBigHump}(), "${field.nameCn}", 1, ${field.length});
            </#if>
        </#list>

        ResponseDto<${Domain}Dto> responseDto = new ResponseDto<>();
        ${domain}Service.save(${domain}Dto);
        responseDto.setContent(${domain}Dto);
        return responseDto;
    }

    @DeleteMapping("/delete/{id}")
    public ResponseDto delete(@PathVariable String id) {
        ResponseDto responseDto = new ResponseDto();
        ${domain}Service.delete(id);
        return responseDto;
    }
}

其中 forif${field.nameCn},都是模板的语法:

// 保存校验
<#list fieldList as field>
    <#if !field.nullAble>
ValidatorUtil.require(${domain}Dto.get${field.nameBigHump}(), "${field.nameCn}");
    </#if>
    <#if (field.length > 0)>
ValidatorUtil.length(${domain}Dto.get${field.nameBigHump}(), "${field.nameCn}", 1, ${field.length});
    </#if>
</#list>

如上例中的 fieldList 就是我们事先定义好的变量:

private static String MODULE = "business";
private static String toControllerPath = MODULE + "\src\main\java\com\lsu\" + MODULE + "\controller\admin\";

public static void main(String[] args) throws Exception {
 
    Map<String, Object> map = new HashMap<>(3);
    map.put("Domain", "Section");
    map.put("domain", "section");
    map.put("fieldList", "属性列表");
    
    // 生成 controller
    FreemarkerUtil.initConfig("controller");
    FreemarkerUtil.generator(map, toControllerPath + bigDoMain + "Controller.java");
}

上例只是演示流程,具体的还需要补充,如 属性列表,就需要连接数据库获取,这样才能获取到数据库的属性,然后转化为 Java 对应的类型。

生成数据传输对象

数据传输对象即 Dto:Data Transaction Object

他是介于实体类与 Service 中的一种对象,由于实体类通常与数据库中的字段相关联,所以一般确定之后不容易变化,但是如果今后我们的业务有一个需求需要添加字段,我们就可以使用位于中间的 Dto 对象来做协调,所以 Dto 就是一个媒介作为中间层,它具有自由可变性。

为了能确定 Dto 的属性列表,我们需要从数据库读取到属性,所以要连接数据库,我们写一个工具类:

package com.lsu.generator.util;

import com.lsu.generator.enums.EnumGenerator;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据库字段工具类
 *
 * @Author wang suo
 * @Date 2020/10/12 0012 22:16
 * @Version 1.0
 */
public class DbUtil {

    /**
     * 正则表达式规则: 匹配: _字母
     */
    private static final Pattern PATTERN = Pattern.compile("_(\w)");

    /**
     * 获取数据库连接
     *
     * @return 返回连接对象
     */
    private static Connection getConnection() {
        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/online_course?useUnicode=false&characterEncoding=UTF8&serverTimezone=UTC";
            String user = "root";
            String password = "root";
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 获取表的中文名称
     *
     * @param tableName 表名
     * @return 返回表的注释
     */
    public static String getTableComment(String tableName) throws SQLException {
        Connection conn = getConnection();
        Statement state = conn.createStatement();
        ResultSet res = state
                .executeQuery("select table_comment from information_schema.TABLES where TABLE_NAME = '"
                        + tableName + "';");
        String tableNameCh = "";
        while (res.next()) {
            tableNameCh = res.getString("table_comment");
        }
        res.close();
        state.close();
        conn.close();
        System.out.println("表名 = " + tableNameCh);
        return tableNameCh;
    }

    /**
     * 获取所有列信息
     *
     * @param tableName 表名
     * @return 返回属性的Field集合
     * @throws Exception 抛出异常
     */
    public static List<Field> getColumnByTableName(String tableName) throws Exception {
        List<Field> fieldList = new ArrayList<>();
        Connection conn = getConnection();
        Statement state = conn.createStatement();
        ResultSet res = state.executeQuery("show full columns from " + tableName);
        while (res.next()) {
            String columnName = res.getString("Field");
            String type = res.getString("Type");
            String comment = res.getString("Comment");
            String nullAble = res.getString("Null");
            Field field = new Field();
            field.setName(columnName);
            field.setNameHump(lineToHump(columnName));
            field.setNameBigHump(lineToBigHump(columnName));
            field.setComment(comment);
            field.setJavaType(sqlTypeToJavaType(type));
            field.setType(type);
            if (comment.contains("|")) {
                field.setNameCn(comment.substring(0, comment.indexOf("|")));
            } else {
                field.setNameCn(comment);
            }
            field.setNullAble("YES".equals(nullAble));
            if (type.toUpperCase().contains("VARCHAR")) {
                String lengthStr = type.substring(type.indexOf("(") + 1, type.length() - 1);
                field.setLength(Integer.valueOf(lengthStr));
            } else {
                field.setLength(0);
            }
            if (comment.contains("枚举")) {
                field.setEnums(true);

                // 以课程等级为例:从注释中的“枚举[CourseLevelEnum]”,得到COURSE_LEVEL
                int start = comment.indexOf("[");
                int end = comment.indexOf("]");
                String enums = comment.substring(start + 1, end);
                String enumsConst = EnumGenerator.toUnderline(enums);
                field.setEnumsConst(enumsConst);
            } else {
                field.setEnums(false);
            }
            fieldList.add(field);
        }
        res.close();
        state.close();
        conn.close();
        System.out.println("列信息 = " + fieldList);
        return fieldList;
    }

    /**
     * 下划线转小驼峰
     *
     * @param str 字符串
     * @return 小驼峰
     */
    private static String lineToHump(String str) {
        Matcher matcher = PATTERN.matcher(str.toLowerCase());
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 下划线转大驼峰
     *
     * @param str 字符串
     * @return 大驼峰
     */
    private static String lineToBigHump(String str) {
        String s = lineToHump(str);
        return s.substring(0, 1).toUpperCase() + s.substring(1);
    }

    /**
     * 数据库类型转为 Java 类型
     *
     * @param sqlType 数据库类型
     * @return 返回 Java 类型
     */
    private static String sqlTypeToJavaType(String sqlType) {
        final String varchar = "VARCHAR";
        final String chr = "CHAR";
        final String text = "TEXT";
        final String datetime = "DATETIME";
        final String integer = "INT";
        final String aLong = "LONG";
        final String decimal = "DECIMAL";
        if (sqlType.toUpperCase().contains(varchar)
                || sqlType.toUpperCase().contains(chr)
                || sqlType.toUpperCase().contains(text)) {
            return "String";
        } else if (sqlType.toUpperCase().contains(datetime)) {
            return "Date";
        } else if (sqlType.toUpperCase().contains(integer)) {
            return "Integer";
        } else if (sqlType.toUpperCase().contains(aLong)) {
            return "Long";
        } else if (sqlType.toUpperCase().contains(decimal)) {
            return "BigDecimal";
        } else {
            return "String";
        }
    }
}

这里涉及到两个 SQL:

# 根据表名获取表的注释信息
select table_comment
from information_schema.TABLES
where TABLE_NAME = 'chapter';

# 获取表的所有字段信息
show full columns from chapter;

为什么需要判断 | 符号呢?

因为我们的数据库定义的时候是这样的:

create table `section`
(
    `id`         char(8)     not null default '' comment 'id',
    `title`      varchar(50) not null comment '标题',
    `course_id`  char(8) comment '课程|course.id',
    `chapter_id` char(8) comment '大章|chapter.id',
    `video`      varchar(200) comment '视频',
    `time`       int comment '时长|单位秒',
    `charge`     char(1) comment '收费|C 收费;F 免费',
    `sort`       int comment '顺序',
    `created_at` datetime(3) comment '创建时间',
    `updated_at` datetime(3) comment '修改时间',
    primary key (`id`)
) engine = innodb
  default charset = utf8mb4 comment ='小节';

上述代码中的 Field 类如下:

package com.lsu.generator.util;

import lombok.Data;
import lombok.ToString;

/**
 * 属性
 *
 * @Author wang suo
 * @Date 2020/10/12 0012 22:03
 * @Version 1.0
 */
@Data
@ToString
public class Field {
    /**
     * 字段名;
     * 字段名小驼峰;
     * 字段名大驼峰;
     * 中文名;
     * 字段类型;
     * java类型;
     * 注释;
     * 是否可为空;
     * 字符串长度;
     * 是否是枚举;
     * 枚举常量 COURSE_LEVEL;
     */
    private String name;
    private String nameHump;
    private String nameBigHump;
    private String nameCn;
    private String type;
    private String javaType;
    private String comment;
    private Boolean nullAble;
    private Integer length;
    private Boolean enums;
    private String enumsConst;
}

最后我们的启动类就可以这样写:

package com.lsu.generator.server;

import com.lsu.generator.util.DbUtil;
import com.lsu.generator.util.Field;
import com.lsu.generator.util.FreemarkerUtil;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.*;

/**
 * 代码生成
 *
 * @Author wang suo
 * @Date 2020/10/12 0012 18:35
 * @Version 1.0
 */
public class ServerGenerator {

    private static String MODULE = "business";
    private static String toServicePath = "server\src\main\java\com\lsu\server\service\";
    private static String toDtoPath = "server\src\main\java\com\lsu\server\dto\";
    private static String toControllerPath = MODULE + "\src\main\java\com\lsu\" + MODULE + "\controller\admin\";
    private static String generatorPath = "server\src\main\resources\generator\generatorConfig.xml";

    public static void main(String[] args) throws Exception {
        String module = MODULE;

        /*
            读 generatorConfig.xml
        */
        File file = new File(generatorPath);
        SAXReader reader = new SAXReader();
        Document dom = reader.read(file);
        /*
        获取文件的根节点
         */
        Element rootElement = dom.getRootElement();
        Element contextElement = rootElement.element("context");
        /*
        获取第一个 table 节点
         */
        Element tableElement = contextElement.elementIterator("table").next();
        /*
        获得大驼峰类名
         */
        String bigDoMain = tableElement.attributeValue("domainObjectName");
        /*
        获取小驼峰类名
         */
        String domain = bigDoMain.substring(0, 1).toLowerCase() + bigDoMain.substring(1);
        /*
        获取表名
         */
        String tableName = tableElement.attributeValue("tableName");
        /*
        获取表的中文名: 备注名
         */
        String tableNameCn = DbUtil.getTableComment(tableName);
        /*
        将表名打印到控制台
         */
        System.out.println("表名 = " + tableName);
        System.out.println("Domain = " + bigDoMain);

        /*
        获取数据库表的属性列表
         */
        List<Field> fieldList = DbUtil.getColumnByTableName(tableName);

        /*
        获取要导入的Java包
         */
        Set<String> typeSet = getJavaTypes(fieldList);

        /*
        放到 Map 集合中供 freemarker 使用
         */
        Map<String, Object> map = new HashMap<>(10);
        map.put("Domain", bigDoMain);
        map.put("domain", domain);
        map.put("tableNameCn", tableNameCn);
        map.put("module", module);
        map.put("fieldList", fieldList);
        map.put("typeSet", typeSet);

        // 生成 dto
        FreemarkerUtil.initConfig("dto");
        FreemarkerUtil.generator(map, toDtoPath + bigDoMain + "Dto.java");

        // 生成 service
        FreemarkerUtil.initConfig("service");
        FreemarkerUtil.generator(map, toServicePath + bigDoMain + "Service.java");

        // 生成 controller
        FreemarkerUtil.initConfig("controller");
        FreemarkerUtil.generator(map, toControllerPath + bigDoMain + "Controller.java");
    }

    private static Set<String> getJavaTypes(List<Field> fieldList) {
        Set<String> set = new HashSet<>();
        for (Field field : fieldList) {
            set.add(field.getJavaType());
        }
        return set;
    }
}

其中的 generatorConfig.xmlmybatis-generator 的配置文件,这里使用 Dom4j 自动读取了从而获取表名,你也可以不获取,自己手动写。

generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="Mysql" targetRuntime="MyBatis3" defaultModelType="flat">

        <property name="autoDelimitKeywords" value="true"/>

        <!--    反引号:如果表名或字段名是 MySQL 的关键字就自动加上反引号    -->
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>

        <!-- 覆盖生成XML文件 -->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />

        <!-- 生成的实体类添加 toString() 方法 -->
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />

        <!-- 不生成注释: 因为注释是英文 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/online_course?serverTimezone=UTC"
                        userId="root"
                        password="root">
        </jdbcConnection>

        <!-- domain类的位置 -->
        <javaModelGenerator targetProject="srcmainjava"
                            targetPackage="com.lsu.server.domain"/>

        <!-- mapper xml的位置 -->
        <sqlMapGenerator targetProject="srcmainresources"
                         targetPackage="mapper"/>

        <!-- mapper类的位置 -->
        <javaClientGenerator targetProject="srcmainjava"
                             targetPackage="com.lsu.server.mapper"
                             type="XMLMAPPER" />

<!--        <table tableName="test" domainObjectName="Test"/>-->
<!--        <table tableName="chapter" domainObjectName="Chapter"/>-->
        <table tableName="section" domainObjectName="Section"/>
<!--        <table tableName="course" domainObjectName="Course"/>-->
<!--        <table tableName="course_content" domainObjectName="CourseContent"/>-->
<!--        <table tableName="course_content_file" domainObjectName="CourseContentFile"/>-->
<!--        <table tableName="teacher" domainObjectName="Teacher"/>-->
<!--        <table tableName="file" domainObjectName="File"/>-->
<!--        <table tableName="user" domainObjectName="User"/>-->
<!--        <table tableName="resource" domainObjectName="Resource"/>-->
<!--        <table tableName="role" domainObjectName="Role"/>-->
<!--        <table tableName="role_resource" domainObjectName="RoleResource"/>-->
<!--        <table tableName="role_user" domainObjectName="RoleUser"/>-->
<!--        <table tableName="member" domainObjectName="Member"/>-->
<!--        <table tableName="sms" domainObjectName="Sms"/>-->
<!--        <table tableName="member_course" domainObjectName="MemberCourse"/>-->
    </context>
<!--    mybatis-generator:generate -e 生成代码命令-->
</generatorConfiguration>

配合 mybatis-generator 使用就可以从持久层到控制层的代码一套打通了。

生成 Vue 代码

生成 vue 的模板文件:

<template>
  <div>
    <p>
      <button @click="add" class="btn btn-white btn-default btn-round">
        <i class="ace-icon fa fa-edit red2"></i>新增
      </button> &nbsp;
      <button @click="getAll(1)" class="btn btn-white btn-default btn-round">
        <i class="ace-icon fa fa-refresh red2"></i>刷新
      </button>
    </p>
    <table id="simple-table" class="table  table-bordered table-hover">

      <thead>
      <tr>
        <#list fieldList as field>
          <th>${field.nameCn}</th>
        </#list>
        <th>操作</th>
      </tr>
      </thead>
      <tbody>
      <tr v-for="${domain} in ${domain}s">
        <#list fieldList as field>
          <td>{{${domain}.${field.nameHump}}}</td>
        </#list>
        <td>
          <div class="hidden-sm hidden-xs btn-group">
            <button @click="edit(${domain})" class="btn btn-xs btn-info">
              <i class="ace-icon fa fa-pencil bigger-120"></i>
            </button>
            <button @click="del(${domain}.id)" class="btn btn-xs btn-danger">
              <i class="ace-icon fa fa-trash-o bigger-120"></i>
            </button>
          </div>
        </td>
      </tr>
      </tbody>
    </table>
    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
              aria-hidden="true">&times;</span></button>
            <h4 class="modal-title" id="myModalLabel">表单</h4>
          </div>
          <div class="modal-body">
            <form class="form-horizontal">
              <#list fieldList as field>
                <div class="form-group">
                  <label for="${field.nameHump}" class="col-sm-2 control-label">${field.nameCn}</label>
                  <div class="col-sm-10">
                    <input v-model="${domain}.${field.nameHump}" id="${field.nameHump}" class="form-control">
                  </div>
                </div>
              </#list>
            </form>
            <div class="modal-footer">
              <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
              <button @click="save" type="button" class="btn btn-primary">保存</button>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!--
        :list="getAll"

        list: 是子组件暴露出来的一个回调方法;
        getAll: 是父组件的 getAll 方法;
      -->
    <pagination ref="pagination" :list="getAll" :itemCount="8"/>
  </div>
</template>

<script>
  import Pagination from '../../components/pagination'

  export default {
    name: "${domain}",
    components: {
      Pagination,
    },
    data() {
      return {
        ${domain}s: [],
        ${domain}: {
          <#list fieldList as field>
            ${field.nameHump}: '',
          </#list>
        },
      }
    },
    created() {
    },
    mounted() {
      let _this = this;
      _this.$refs.pagination.size = 10;
      _this.getAll(1);
    },
    methods: {
      getAll(page) {
        let _this = this;
        Loading.show();
        _this.$ajax.post(process.env.VUE_APP_SERVER + '/${module}/admin/${domain}/list', {
          page: page,
          size: _this.$refs.pagination.size,
        }).then(response => {
          Loading.hide();
          let resp = response.data;
          _this.${domain}s = resp.content.list;
          // 渲染子组件
          _this.$refs.pagination.render(page, resp.content.total);
        })
      },
      add() {
        let _this = this;
        _this.${domain} = {};
        $("#myModal").modal("show");
      },
      save() {
        let _this = this;

        // 保存校验
        if (1 !== 1
        <#list fieldList as field>
          <#if !field.nullAble>
          || !Validator.require(_this.${domain}.${field.nameHump}, "${field.nameCn}")
          </#if>
          <#if (field.length > 0)>
          || !Validator.length(_this.${domain}.${field.nameHump}, "${field.nameCn}", 1, ${field.length})
          </#if>
        </#list>
        ) {return;}

        Loading.show();
        _this.$ajax.post(process.env.VUE_APP_SERVER + '/${module}/admin/${domain}/save',
          _this.${domain}).then(response => {
          Loading.hide();
          let resp = response.data;
          if (resp.success) {
            $("#myModal").modal("hide");
            _this.getAll(1);
            Toast.success("保存成功");
          }
        })
      },
      edit(${domain}) {
        let _this = this;
        // 双向绑定问题: 输入的时候表格也会更新数据: 使用 JQuery 的函数解决问题
        _this.${domain} = $.extend({}, ${domain});
        $("#myModal").modal("show");
      },
      del(id) {
        let _this = this;
        Confirm.show("该操作不可逆转", function () {
          Loading.show();
          _this.$ajax.delete(process.env.VUE_APP_SERVER + '/${module}/admin/${domain}/delete/' + id).then(response => {
            Loading.hide();
            let resp = response.data;
            if (resp.success) {
              _this.getAll(1);
              Toast.success("删除成功");
            }
          });
        });
      }
    }
  }
</script>

<style scoped>

</style>

启动类:

package com.lsu.generator.vue;

import com.lsu.generator.util.DbUtil;
import com.lsu.generator.util.Field;
import com.lsu.generator.util.FreemarkerUtil;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.*;

/**
 * Vue 代码生成
 *
 * @Author wang suo
 * @Date 2020/10/12 0012 18:35
 * @Version 1.0
 */
public class VueGenerator {

    private static String MODULE = "business";
    private static String toVuePath = "admin\src\views\admin\";
    private static String generatorPath = "server\src\main\resources\generator\generatorConfig.xml";

    public static void main(String[] args) throws Exception {
        /*
            读 generatorConfig.xml
        */
        File file = new File(generatorPath);
        SAXReader reader = new SAXReader();
        Document dom = reader.read(file);
        /*
        获取文件的根节点
         */
        Element rootElement = dom.getRootElement();
        Element contextElement = rootElement.element("context");
        /*
        获取第一个 table 节点
         */
        Element tableElement = contextElement.elementIterator("table").next();
        /*
        获得大驼峰类名
         */
        String bigDoMain = tableElement.attributeValue("domainObjectName");
        /*
        获取小驼峰类名
         */
        String domain = bigDoMain.substring(0, 1).toLowerCase() + bigDoMain.substring(1);
        /*
        获取表名
         */
        String tableName = tableElement.attributeValue("tableName");

        /*
        获取数据库表的属性列表
         */
        List<Field> fieldList = DbUtil.getColumnByTableName(tableName);

        /*
        放到 Map 集合中供 freemarker 使用
         */
        Map<String, Object> map = new HashMap<>(10);
        map.put("domain", domain);
        map.put("module", MODULE);
        map.put("fieldList", fieldList);

        // 生成 vue
        FreemarkerUtil.initConfig("vue");
        FreemarkerUtil.generator(map, toVuePath + domain + ".vue");
    }
}

生成之后设置一下 router.js 即可看到效果。