代码基于SpringBoot3、Vue3、highlight实现自定义代码生成功能
SpringBoot3.x、MySQL8、MyBatisPlus3.5.x、velocity2.x、SpringSecurity6.x、Vue3、TypeScript、highlight
demo所需要的依赖及其对应版本号
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.molu</groupId>
<artifactId>mgzyf-api</artifactId>
<version>2.4.1</version>
<description>Java 17 + SpringBoot3 + SpringSecurity6 </description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version> <!-- lookup parent from repository -->
<relativePath/>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hutool.version>5.8.15</hutool.version>
<mysql.version>8.0.28</mysql.version>
<druid.version>1.2.16</druid.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<mapstruct.version>1.5.3.Final</mapstruct.version>
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
</properties>
<dependencies>
<!-- velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--编译测试环境,不打包在lib-->
<scope>provided</scope>
</dependency>
<!-- 允许使用Lombok的Java Bean类中使用MapStruct注解 (Lombok 1.18.20+) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>${lombok-mapstruct-binding.version}</version>
<scope>provided</scope>
</dependency>
<!-- hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- web支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- security安全支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 阿里druid工具包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.40</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 参数验证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 自定义配置类支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这里是最基础的MySQL的配置信息
application
server:
port: 8989
spring:
jackson:
## 默认序列化时间格式
date-format: yyyy-MM-dd HH:mm:ss
## 默认序列化时区
time-zone: GMT+8
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
druid:
master:
url: jdbc:mysql://127.0.0.1:3306/test01?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true
username: xxx
password: 123
driver-class-name: com.mysql.cj.jdbc.Driver
initial-size: 15
min-idle: 15
max-active: 200
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: ""
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: false
connection-properties: false
# 配置生成代码的数据库
cn:
molu:
generate:
database: test01
这里是代码生成器的源码目录结构,下面会逐一展示这3个类
这里是根据我的项目需要,自定义的代码生成器模板,下面会以
Service
为例解释
提供数据到Vue页面的API接口,主要功能是将模板解析为代码返回给前台
import cn.molu.system.common.result.Result;
import cn.molu.system.generate.mapper.ColumnDetailMapper;
import cn.molu.system.generate.vo.ColumnDetailVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author 陌路
* @apiNote 数据库表细信息
* @date 2023/12/31 13:16
* @tool Created by IntelliJ IDEA
*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/gen")
public class ColumnDetailController {
@Value("${cn.molu.generate.database}")
private String database; // 这里是数据库的名字,我的数据库是 test01
private final ColumnDetailMapper columnDetailMapper;
/**
* 获取所有表信息数据
*
* @return List<ColumnDetailVo>
*/
@GetMapping("/getAllTables")
@Cacheable(value = "gen:allTableDetails", key = "#root.methodName")
public Result<List<ColumnDetailVo>> getAllTables() {
List<ColumnDetailVo> columnDetailVoList = columnDetailMapper.getColumnDetailMapVo(database);
return Result.success(columnDetailVoList);
}
/**
* 获取所有表中字段信息数据
*
* @param tableName 表名
* @return List<ColumnDetailVo>
*/
@GetMapping("/getTableInfo/{tableName}")
public Result<String> getTableInfo(@PathVariable String tableName,
@RequestParam(value = "delPrefix", required = false) String delPrefix,
@RequestParam(value = "packageName", required = false) String packageName,
@RequestParam(value = "type") String type
) {
List<ColumnDetailVo> columnDetailVoList = columnDetailMapper.getColumnDetailMapVoByTableName(database, tableName);
try (StringWriter writer = new StringWriter()) {
// 初始化Velocity引擎
Properties p = new Properties();
// 加载classpath目录下的vm文件
p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
// 定义字符集
p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
// 初始化Velocity引擎,指定配置Properties
Velocity.init(p);
// 获取模板
Template template = Velocity.getTemplate("vm/" + type + ".vm", "UTF-8");
ColumnDetailVo detailVo = new ColumnDetailVo();
Map<String, Object> map = detailVo.listToMap(columnDetailVoList, delPrefix); // 去除前缀
map.put("packageName", packageName); // 文件所在包
// 创建Velocity上下文
VelocityContext context = new VelocityContext();
// 设置模板中的变量
context.put("map", map);
// 将模板与上下文数据进行合并
template.merge(context, writer);
// 输出结果
String writerString = writer.toString();
return Result.success(writerString);
} catch (IOException | ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
log.error("报错了:" + e.getMessage(), e);
}
return Result.failed();
}
}
查询数据库,获取表字段信息,以便后面生成代码使用
import cn.molu.system.generate.vo.ColumnDetailVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author 陌路
* @apiNote 数据库表细信息
* @date 2023/12/31 13:14
* @tool Created by IntelliJ IDEA
*/
@Mapper
public interface ColumnDetailMapper {
/**
* 获取所有表信息数据
*
* @return List<ColumnDetailVo>
*/
public List<ColumnDetailVo> getColumnDetailMapVo(@Param("database") String database);
/**
* 获取所有表中字段信息数据
*
* @param tableName 表名
* @return List<ColumnDetailVo>
*/
public List<ColumnDetailVo> getColumnDetailMapVoByTableName(@Param("database") String database, @Param("tableName") String tableName);
}
根据application中指定的数据库名,获取数据库中所有的表名以及表中的字段名
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.molu.system.generate.mapper.ColumnDetailMapper">
<resultMap type="cn.molu.system.generate.vo.ColumnDetailVo" id="ColumnDetailMap">
<result property="tableName" column="table_name" jdbcType="VARCHAR"/>
<result property="tableComment" column="table_comment" jdbcType="VARCHAR"/>
<result property="engine" column="engine" jdbcType="VARCHAR"/>
<result property="tableCollation" column="table_collation" jdbcType="VARCHAR"/>
<result property="tableRows" column="table_rows" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="VARCHAR"/>
</resultMap>
<!-- 获取所有表信息数据 -->
<select id="getColumnDetailMapVo" resultMap="ColumnDetailMap">
select table_name, <!-- 表名 -->
table_comment, <!-- 表名注释 -->
engine, <!-- 表数据引擎 -->
table_collation, <!-- 表字符集 -->
table_rows, <!-- 表中数据条数 -->
create_time <!-- 表创建时间 -->
from information_schema.tables
where upper(table_schema) = upper(#{database})
</select>
<!-- 获取所有表中字段信息数据 -->
<select id="getColumnDetailMapVoByTableName" parameterType="string" resultMap="ColumnDetailMap">
SELECT TABLE_SCHEMA, <!-- 数据库名 -->
TABLE_NAME, <!-- 表名 -->
(select b.table_comment
from information_schema.tables b
where upper(table_schema) = upper(#{database})
and upper(table_name) = upper(#{tableName})
limit 1) as TABLE_COMMENT, <!-- 表名注释 -->
COLUMN_NAME, <!-- 字段名 -->
COLUMN_DEFAULT, <!-- 默认值 -->
IS_NULLABLE, <!-- 是否可为空:YES、NO -->
DATA_TYPE, <!-- 数据类型:int、varchar... -->
COLUMN_TYPE, <!-- 字段类型:int、varchar(30)... -->
COLUMN_KEY, <!-- 是否主键:PRI -->
EXTRA, <!-- 是否自增:auto_increment(自增) -->
COLUMN_COMMENT <!-- 字段注释 -->
FROM information_schema.columns a
WHERE upper(table_schema) = upper(#{database})
AND upper(table_name) = upper(#{tableName})
</select>
</mapper>
对应表中字段和字段信息的实体类
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serial;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author 陌路
* @apiNote 数据库表细信息
* @date 2023/12/31 13:21
* @tool Created by IntelliJ IDEA
*/
@Data
@ToString
@NoArgsConstructor
@EqualsAndHashCode
@Accessors(chain = true)
@JsonInclude(value = JsonInclude.Include.NON_NULL, content = JsonInclude.Include.NON_EMPTY)
public class ColumnDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 9196390045041955368L;
// ====================查询所有表信息====================
/**
* 表名
*/
private String tableName;
/**
* 表名注释
*/
private String tableComment;
/**
* 表数据引擎
*/
private String engine;
/**
* 表字符集
*/
private String tableCollation;
/**
* 表中数据条数
*/
private String tableRows;
/**
* 表创建时间
*/
@JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
@DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
private String createTime;
// ====================查询指定表中的字段信息====================
/**
* 数据库名
*/
private String tableSchema;
/**
* 字段名
*/
private String columnName;
/**
* 默认值
*/
private String columnDefault;
/**
* 是否可为空:YES、NO
*/
private String isNullable;
/**
* 数据类型:int、varchar...
*/
private String dataType;
/**
* 字段类型:int、varchar(30)...
*/
private String columnType;
/**
* 是否主键:PRI
*/
private String columnKey;
/**
* 是否自增:auto_increment(自增)
*/
private String extra;
/**
* 字段注释
*/
private String columnComment;
/**
* 是否主键
*
* @param columnKey 字段键
* @return 是否主键
*/
public boolean isPrimaryKey(String columnKey) {
return StrUtil.equalsIgnoreCase(columnKey, "PRI");
}
/**
* 获取主键字段名
*
* @param list 字段列表
* @return 主键字段名
*/
public String getPrimaryKey(List<ColumnDetailVo> list) {
return Optional.ofNullable(list)
.orElseGet(ArrayList::new)
.stream()
.filter(item -> isPrimaryKey(item.columnKey))
.findFirst()
.orElseGet(ColumnDetailVo::new).columnName;
}
/**
* 列表转Map
*
* @param list 列表
* @param replacePrefix 替换前缀
* @return Map
*/
public Map<String, Object> listToMap(List<ColumnDetailVo> list, String replacePrefix) {
if (Objects.isNull(list) || list.isEmpty()) return null;
Map<String, Object> map = new HashMap<>(2);
ColumnDetailVo detailVo = list.get(0);
String voTableName = detailVo.getTableName().toLowerCase();
String subTableName = voTableName.replace(replacePrefix, "");
String className = toUpperFirst(subTableName);
map.put("tableName", voTableName); // 表名
map.put("tableComment", detailVo.getTableComment()); // 表名注释
map.put("className", StrUtil.upperFirst(className)); // Java类名
map.put("subClassName", className); // Java类名
map.put("path", voTableName.replace("_", "/")); // 生成路径
map.put("prem", voTableName.replace("_", ":")); // 权限标识
map.put("currTime", DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN)); // 日期时间
AtomicReference<String> pk = new AtomicReference<>();
AtomicReference<String> pkType = new AtomicReference<>();
AtomicReference<String> getterPk = new AtomicReference<>();
AtomicReference<String> pkColumn = new AtomicReference<>();
List<Map<String, String>> fields = new ArrayList<>(2);
List<Map<String, String>> otherColumn = new ArrayList<>(2);
list.forEach(vo -> {
Map<String, String> field = new HashMap<>(2);
field.put("columnName", vo.getColumnName()); // 字段名
field.put("javaField", toUpperFirst(vo.getColumnName())); // 字段对应的Java字段
field.put("columnComment", vo.getColumnComment()); // 字段注释
String javaType = getJavaType(vo.getDataType());
field.put("javaType", javaType); // java字段类型
field.put("jdbcType", vo.getDataType().toUpperCase());
field.put("jdbcTypeXml", getJdbcTypeXml(vo.getDataType().toLowerCase()));
field.put("type", getType(vo.getDataType()));
field.put("getter", StrUtil.upperFirst(toUpperFirst(vo.getColumnName())));
if (isPrimaryKey(vo.getColumnKey())) {
if (StrUtil.equalsIgnoreCase(javaType, "Integer")) {
javaType = "Long";
}
field.put("javaType", javaType); // java字段类型
field.put("pkColumn", vo.getColumnName()); // 主键
pk.set(toUpperFirst(vo.getColumnName()));
pkColumn.set(vo.getColumnName());
pkType.set(javaType);
getterPk.set(StrUtil.upperFirst(toUpperFirst(vo.getColumnName())));
} else {
otherColumn.add(field);
}
fields.add(field);
});
map.put("columns", fields);
map.put("otherColumn", otherColumn);
map.put("pk", pk);
map.put("pkColumn", pkColumn);
map.put("pkType", pkType);
map.put("getterPk", getterPk);
return map;
}
/**
* @title 获取字段类型
* @author 陌路
* @date 2023/12/31 21:01
*/
private String getJdbcTypeXml(String dataType) {
return switch (dataType) {
case "int", "tinyint" -> "INTEGER";
case "char" -> "CHAR";
case "mediumint" -> "MEDIUMINT";
case "bigint" -> "BIGINT";
case "float" -> "FLOAT";
case "double" -> "DOUBLE";
case "bit" -> "BIT";
case "datetime", "date", "time", "timestamp" -> "TIMESTAMP";
default -> "VARCHAR";
};
}
/**
* @title 获取字段类型
* @author 陌路
* @date 2023/12/31 21:01
*/
private String getType(String dataType) {
String javaType = getJavaType(dataType);
String type = "java.lang." + dataType;
if (StrUtil.equals(javaType, "Date")) {
type = "java.util.Date";
}
return type;
}
/**
* @title 获取字段类型
* @author 陌路
* @date 2023/12/31 21:01
*/
private String getJavaType(String dataType) {
return switch (dataType) {
case "bigint" -> "Long";
case "datetime", "date", "time", "timestamp" -> "Date";
case "decimal", "double" -> "Double";
case "float" -> "Float";
case "int", "tinyint", "integer" -> "Integer";
default -> "String";
};
}
/**
* 首字母转大写
*
* @param field 字段
* @return 首字母大写
*/
public String toUpperFirst(String field) {
// 表名转驼峰命名
StringBuilder string = new StringBuilder();
if (StrUtil.isNotEmpty(field) && field.contains("_")) {
for (String str : field.split("_")) {
string.append(StrUtil.upperFirst(str));
}
} else {
string = new StringBuilder(StrUtil.upperFirst(field));
}
return StrUtil.lowerFirst(string.toString());
}
}
自定义的模板,通过模板生成所需要的代码
package $!{map.packageName}.service;
import $!{map.packageName}.entity.$!{map.className};
import com.baomidou.mybatisplus.core.metadata.IPage;
import $!{map.packageName}.params.$!{map.className}Params;
import java.lang.*;
/**
* @apiNote $!{map.tableComment}($!{map.className})表服务接口
* @author 陌路
* @date $!{map.currTime}
* @tool Created by IntelliJ IDEA
*/
public interface $!{map.className}Service /* extends IService<$!{map.className}> */{
/**
* 通过ID查询单条数据
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.pk} 主键
* @return $!{map.subClassName} 实例对象
*/
$!{map.className} queryById($!{map.pkType} $!{map.pk});
/**
* 分页查询
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.subClassName}Params 筛选条件
* @return IPage<$!{map.className}> 查询结果
*/
IPage<$!{map.className}> queryByPage($!{map.className}Params $!{map.subClassName}Params);
/**
* 新增数据
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.subClassName} 实例对象
* @return $!{map.subClassName} 实例对象
*/
$!{map.className} insert($!{map.className} $!{map.subClassName}));
/**
* 修改数据
*
* @author 陌路
* @date $!{map.currTime}
* @param $!{map.subClassName} 实例对象
* @return $!{map.subClassName} 实例对象
*/
$!{map.className} update($!{map.className} $!{map.subClassName}));
/**
* 通过主键作废(逻辑删除)
*
* @author 陌路
* @date $!{map.currTime}
* @param ids 主键
* @return boolean 是否成功
*/
boolean deleteByIds(String ids);
/**
* 通过主键删除(物理删除)
*
* @author 陌路
* @date $!{map.currTime}
* @param ids 主键
* @return boolean 是否成功
*/
boolean removeByIds(String ids);
}
后台生成代码返回给vue页面,vue渲染,并使用语法高亮展示完整代码
<script setup lang="ts">
defineOptions({
name: "Code",
inheritAttrs: false,
});
import { ColumnDetailQuery, ColumnDetailVo } from "@/api/generate/types";
import { getAllTables, getTableInfo } from "@/api/generate";
import { ref, onMounted, reactive } from "vue";
import "highlight.js/styles/atom-one-light.css";
import "highlight.js/lib/common";
import hljs from "highlight.js";
const queryParams = reactive<ColumnDetailQuery>({});
const queryFormRef = ref(ElForm);
const dialog = reactive<DialogOption>({
visible: false,
title: "",
});
// 选择表格行的数据ID
const loading = ref(false); // 加载状态
const columnDetailVoList = ref<ColumnDetailVo[]>([]); // 记录列表数据
const dataType = ref(0); // 数据类型
const params = ref({
tableName: "", // 表名
delPrefix: "", // 删除前缀
packageName: "cn.molu.system", // 所在包
type: "java/entity.java", // 数据类型
});
const result = ref({});
const codeCache = new Map();
const tableName = ref("");
const preview = ref({
activeName: "entity",
data: [],
title: "",
open: false,
code: null,
vmName: "entity.java",
});
// 这里可以从后台获取,我这里为了直观的展示就写死了这几种
const tabPaneData = reactive<any[]>([
{
label: "entity.java",
name: "entity",
path: "java/entity.java",
},
{
label: "controller.java",
name: "controller",
path: "java/controller.java",
},
{
label: "service.java",
name: "service",
path: "java/service.java",
},
{
label: "serviceImpl.java",
name: "scopeImpl",
path: "java/serviceImpl.java",
},
{
label: "mapper.java",
name: "mapper",
path: "java/mapper.java",
},
{
label: "mapperXml.xml",
name: "mapperXml",
path: "xml/mapper.xml",
},
{
label: "params.java",
name: "params",
path: "java/params.java",
},
{
label: "api.ts",
name: "api",
path: "vue3/api.ts",
},
{
label: "page.vue",
name: "page",
path: "vue3/page.vue",
},
{
label: "types.ts",
name: "types",
path: "vue3/types.ts",
},
{
label: "sql.sql",
name: "sql",
path: "sql/sql.sql",
},
]);
/**
* 查询按钮点击事件
*/
const handleQuery = () => {
getAllTables()
.then(({ data }) => {
columnDetailVoList.value = data;
})
.finally(() => {
loading.value = false;
});
};
/**
* 重置按钮点击事件
*/
const resetQuery = () => {
queryFormRef.value.resetFields();
handleQuery();
};
/** 关闭弹窗 */
const closeDialog = () => {
dialog.visible = false;
};
/**
* 显示弹框
*/
const openDialog = (row: ColumnDetailVo) => {
dialog.visible = true;
dialog.title = "代码预览";
console.log(row);
params.value.tableName = row.tableName || "";
if (!params.value.delPrefix && row.tableName?.includes("_")) {
params.value.delPrefix = row.tableName?.split("_")[0] + "_";
}
tableName.value = row.tableName || "";
generateCode();
};
/**
* 选择列表
*/
const changeSelect = (value: string) => {
console.log(value);
params.value.tableName = value;
if (!params.value.delPrefix && value?.includes("_")) {
params.value.delPrefix = value?.split("_")[0] + "_";
}
tableName.value = value || "";
generateCode();
};
/**
* 生成代码
*/
const generateCode = () => {
let obj = codeCache.get(tableName.value) || {};
let activeName = preview.value.activeName;
if (obj && obj[activeName]) {
let code = obj[activeName] || "";
result.value = code;
return;
}
getTableInfo(tableName.value, params.value).then((response) => {
const data = response.data;
preview.value.code = data;
const vmName = preview.value.vmName || "";
let language = vmName.substring(vmName.indexOf(".") + 1, vmName.length).toLowerCase();
language = ["vue", "html", "xml"].includes(language) ? "html" : language;
language = language === 'js' ? "javascript": language;
result.value = hljs.highlight(data || "", {
language: language,
ignoreIllegals: true,
}).value;
obj[activeName] = result.value || "";
codeCache.set(tableName.value, obj);
return result.value || " ";
});
};
/**
* tab切换事件
*/
const tabChange = (tabPaneName: any) => {
console.log(tabPaneName);
preview.value.activeName = tabPaneName;
const tabPane = tabPaneData.find((item) => item.name == tabPaneName);
console.log(tabPane);
preview.value.vmName = tabPane.label;
params.value.type = tabPane.path;
generateCode();
};
/**
* 复制代码
*/
const clipboardSuccess = () => {
ElMessage.success("复制成功");
};
/**
* 选择页码,分页查询
*/
const updateCurrentPage = (page?: number) => {
queryParams.page = page;
handleQuery();
};
/**
* 页面加载完成后执行
*/
onMounted(() => {
handleQuery();
});
</script>
<template>
<div class="app-container">
<div class="search-container">
<el-form ref="queryFormRef" :model="params" :inline="true">
<!-- 表名 -->
<el-form-item label="表名" prop="tableName">
<el-input v-model="params.tableName" placeholder="表名" disabled />
</el-form-item>
<!-- 删除前缀 -->
<el-form-item label="删除前缀" prop="delPrefix" v-if="false">
<el-input
v-model="params.delPrefix"
placeholder="删除前缀"
clearable
/>
</el-form-item>
<!-- 所在包 -->
<el-form-item label="所在包" prop="packageName" v-if="false">
<el-input
v-model="params.packageName"
placeholder="所在包"
clearable
/>
</el-form-item>
<el-form-item>
<el-button class="filter-item" type="primary" @click="handleQuery">
<i-ep-search />
搜索
</el-button>
<el-button @click="resetQuery"> <i-ep-refresh />重置 </el-button>
</el-form-item>
</el-form>
</div>
<el-card shadow="never" class="table-container">
<el-scrollbar style="height: 400px">
<el-table
v-loading="loading"
:data="columnDetailVoList"
row-key="id"
default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<!-- 复选款 -->
<el-table-column type="selection" width="55" align="center" />
<!-- 序号 -->
<el-table-column
type="index"
width="55"
align="center"
label="序号"
/>
<!-- 数据库名 -->
<el-table-column
label="数据库名"
align="center"
prop="tableSchema"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.tableSchema }}
</template>
</el-table-column>
<!-- 表名 -->
<el-table-column label="表名" align="center" prop="tableName">
<template #default="scope">
{{ scope.row.tableName }}
</template>
</el-table-column>
<!-- 表注释 -->
<el-table-column
label="表注释"
align="center"
prop="tableComment"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.tableComment }}
</template>
</el-table-column>
<!-- 表数据引擎 -->
<el-table-column
label="表引擎"
align="center"
prop="engine"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.engine }}
</template>
</el-table-column>
<!-- 表字符集 -->
<el-table-column
label="字符集"
align="center"
prop="tableCollation"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.tableCollation }}
</template>
</el-table-column>
<!-- 表中数据条数 -->
<el-table-column
label="数据量"
align="center"
prop="tableRows"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.tableRows }}
</template>
</el-table-column>
<!-- 表创建时间 -->
<el-table-column
label="创建时间"
align="center"
prop="createTime"
v-if="dataType == 0"
>
<template #default="scope">
{{ scope.row.createTime }}
</template>
</el-table-column>
<!-- 字段名 -->
<el-table-column
label="字段名"
align="center"
prop="columnName"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnName }}
</template>
</el-table-column>
<!-- 默认值 -->
<el-table-column
label="默认值"
align="center"
prop="columnDefault"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnDefault }}
</template>
</el-table-column>
<!-- 是否可为空:YES、NO -->
<el-table-column
label="可为空"
align="center"
prop="isNullable"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.isNullable }}
</template>
</el-table-column>
<!-- 数据类型:int、varchar... -->
<el-table-column
label="数据类型"
align="center"
prop="dataType"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.dataType }}
</template>
</el-table-column>
<!-- 字段类型:int、varchar(30)... -->
<el-table-column
label="字段类型"
align="center"
prop="columnType"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnType }}
</template>
</el-table-column>
<!-- 是否主键:PRI -->
<el-table-column
label="是否主键"
align="center"
prop="columnKey"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnKey }}
</template>
</el-table-column>
<!-- 是否自增:auto_increment(自增) -->
<el-table-column
label="是否自增"
align="center"
prop="extra"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.extra }}
</template>
</el-table-column>
<!-- 字段注释 -->
<el-table-column
label="字段注释"
align="center"
prop="columnComment"
v-if="dataType == 1"
>
<template #default="scope">
{{ scope.row.columnComment }}
</template>
</el-table-column>
<!-- 操作 -->
<el-table-column
label="操作"
fixed="right"
align="left"
width="200"
v-if="dataType == 0"
>
<template #default="scope">
<el-button
type="primary"
link
size="small"
@click.stop="openDialog(scope.row)"
>
<i-ep-view />预览
</el-button>
</template>
</el-table-column>
</el-table>
</el-scrollbar>
<div
style="
margin: 15px auto;
display: flex;
align-items: center;
justify-content: center;
"
>
<!-- 这里是分页插件 -->
<!-- <el-pagination
background
layout="prev, pager, next"
:total="queryParams.total"
:default-page-size="10"
v-model:page-size="queryParams.size"
v-model:current-page="queryParams.page"
@update:current-page="updateCurrentPage"
/> -->
</div>
</el-card>
<!-- 预览弹窗 -->
<el-dialog
v-model="dialog.visible"
:title="dialog.title"
destroy-on-close
append-to-body
align-center
lock-scroll
width="80%"
@close="closeDialog"
top="3vh"
>
<el-form ref="queryFormRef" :model="params" :inline="true">
<!-- 表名 -->
<el-form-item label="表名" prop="tableName">
<el-select
v-model="params.tableName"
placeholder="表名"
@change="changeSelect"
>
<el-option
v-for="(item, i) in columnDetailVoList"
:key="i"
:label="item.tableName"
:value="item.tableName"
/>
</el-select>
</el-form-item>
<!-- 删除前缀 -->
<el-form-item label="删除前缀" prop="delPrefix">
<el-input v-model="params.delPrefix" placeholder="删除前缀" />
</el-form-item>
<!-- 所在包 -->
<el-form-item label="所在包" prop="packageName">
<el-input v-model="params.packageName" placeholder="所在包" />
</el-form-item>
<el-form-item>
<el-button class="filter-item" type="primary" @click="generateCode">
生成
</el-button>
</el-form-item>
</el-form>
<el-tabs
v-model="preview.activeName"
class="demo-tabs"
@tab-change="tabChange"
>
<el-scrollbar max-height="420px">
<el-tab-pane
v-for="(item, key) in tabPaneData"
:label="item.label"
:name="item.name"
:key="key"
>
<!-- <el-link :underline="false"
icon="el-icon-document-copy"
v-clipboard:copy="preview.code"
v-clipboard:success="clipboardSuccess"
style="float:right">复制</el-link> -->
<pre class="hljs-container">
<code class="hljs" v-html="result"></code>
</pre>
</el-tab-pane>
</el-scrollbar>
</el-tabs>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
/* 语法高亮 */
.hljs-container {
position: relative;
display: block;
// width: 600px;
padding: 30px 5px 2px;
overflow-x: hidden;
line-height: 20px;
text-align: left;
background: #21252b;
box-shadow: 0 10px 30px 0 rgb(0 0 0 / 40%);
}
/** 3个点 */
.hljs-container::before {
position: absolute;
top: 10px;
left: 15px;
width: 12px;
height: 12px;
overflow: visible;
font-weight: 700;
font-size: 16px;
line-height: 12px;
white-space: nowrap;
text-indent: 75px;
background-color: #fc625d;
border-radius: 16px;
box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
content: attr(codetype);
}
/** 滚动条 */
:deep(.hljs) {
overflow-x: auto;
color: #4887b8;
background-color: #21252b;
// background-color: #dddddd;
}
:deep(.hljs::-webkit-scrollbar) {
width: 12px !important;
height: 12px !important;
}
:deep(.hljs::-webkit-scrollbar-thumb) {
height: 30px !important;
background: #d1d8e6;
background-clip: content-box;
border: 2px solid transparent;
border-radius: 19px;
opacity: 0.8;
}
:deep(.hljs::-webkit-scrollbar-thumb:hover) {
background: #a5b3cf;
background-clip: content-box;
border: 2px solid transparent;
}
:deep(.hljs::-webkit-scrollbar-track-piece) {
width: 30px;
height: 30px;
background: #333;
}
::-webkit-scrollbar-button {
display: none;
}
</style>
1、选择需要生成代码的表
2、根据个人需求是否去除表前缀
3、生成的代码所在的包路径
点击生成即可生成代码,并可以直接复制使用
生成的菜单SQL语句,包含权限代码
可以生成vue3的语法支持的前端页面
可以生成我们需要的mapper.xml文件
里面包含对数据库的增删改查的语句
这里可以生成对Service的代码实现类
生成对外提供的接口层,包含所需要的权限,接口问道的doc解释等
以上就是使用SpringBoot3、JDK17、Vue3、SpringSecurity6等实现的代码生成器了,其他页面就不一一展示了
优点:
可扩展性高,可以根据自己不同的需求调整代码生成器以及生成逻辑
可自定义生成内容,支持范围广
可个自定义性化代码,按照自己喜欢的风格来自定义生成器