Skip to content

Commit

Permalink
refactor: 适配 3.0 前端代码生成模板,代码预览及生成
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles7c committed Apr 19, 2024
1 parent be7e806 commit 3dbe72f
Show file tree
Hide file tree
Showing 19 changed files with 452 additions and 646 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package top.charles7c.continew.admin.generator.config.properties;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.map.MapUtil;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
Expand Down Expand Up @@ -71,5 +72,15 @@ public static class TemplateConfig {
* 排除字段
*/
private String[] excludeFields;

/**
* 扩展名
*/
private String extension = FileNameUtil.EXT_JAVA;

/**
* 是否为后端模板
*/
private boolean backend = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
Expand Down Expand Up @@ -227,35 +226,30 @@ public List<GeneratePreviewResp> preview(String tableName) {
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
genConfigMap.put("apiModuleName", apiModuleName);
genConfigMap.put("apiName", StrUtil.lowerFirst(genConfig.getClassNamePrefix()));
// 渲染后端代码
// 渲染代码
String classNamePrefix = genConfig.getClassNamePrefix();
Map<String, TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
for (Map.Entry<String, TemplateConfig> templateConfigEntry : templateConfigMap.entrySet()) {
this.pretreatment(genConfigMap, fieldConfigList, templateConfigEntry);
String className = classNamePrefix + StrUtil.nullToEmpty(templateConfigEntry.getKey());
genConfigMap.put("className", className);
TemplateConfig templateConfig = templateConfigEntry.getValue();
String content = TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap);
boolean isBackend = templateConfig.isBackend();
String extension = templateConfig.getExtension();
GeneratePreviewResp generatePreview = new GeneratePreviewResp();
generatePreview.setFileName(className + FileNameUtil.EXT_JAVA);
generatePreview.setContent(content);
generatePreview.setBackend(true);
generatePreview.setBackend(isBackend);
generatePreviewList.add(generatePreview);
if (isBackend) {
generatePreview.setFileName(className + extension);
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
} else {
generatePreview.setFileName(".vue".equals(extension) && "index".equals(templateConfigEntry.getKey())
? "index.vue"
: this.getFrontendFileName(classNamePrefix, className, extension));
genConfigMap.put("fieldConfigs", fieldConfigList);
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
}
}
// 渲染前端代码
// api 代码
genConfigMap.put("fieldConfigs", fieldConfigList);
String apiContent = TemplateUtils.render("api.ftl", genConfigMap);
GeneratePreviewResp apiGeneratePreview = new GeneratePreviewResp();
apiGeneratePreview.setFileName(classNamePrefix.toLowerCase() + ".ts");
apiGeneratePreview.setContent(apiContent);
generatePreviewList.add(apiGeneratePreview);
// view 代码
String viewContent = TemplateUtils.render("index.ftl", genConfigMap);
GeneratePreviewResp viewGeneratePreview = new GeneratePreviewResp();
viewGeneratePreview.setFileName("index.vue");
viewGeneratePreview.setContent(viewContent);
generatePreviewList.add(viewGeneratePreview);
return generatePreviewList;
}

Expand All @@ -265,45 +259,12 @@ public void generate(List<String> tableNames, HttpServletRequest request, HttpSe
String tempDir = SystemUtil.getUserInfo().getTempDir();
// 删除旧代码
FileUtil.del(tempDir + projectProperties.getAppName());

tableNames.forEach(tableName -> {
// 初始化配置及数据
List<GeneratePreviewResp> generatePreviewList = this.preview(tableName);
GenConfigDO genConfig = genConfigMapper.selectById(tableName);
// 生成后端代码
Map<Boolean, List<GeneratePreviewResp>> generatePreviewListMap = generatePreviewList.stream()
.collect(Collectors.groupingBy(GeneratePreviewResp::isBackend));
this.generateBackendCode(generatePreviewListMap.get(true), genConfig);
// 生成前端代码
List<GeneratePreviewResp> frontendGeneratePreviewList = generatePreviewListMap.get(false);
String packageName = genConfig.getPackageName();
String moduleName = StrUtil.subSuf(packageName, StrUtil
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);

// 例如:continew-admin-ui/src
String frontendBasicPackagePath = tempDir + String.join(File.separator, projectProperties
.getAppName(), projectProperties.getAppName() + "-ui", "src");
// 1、生成 api 代码
GeneratePreviewResp apiGeneratePreview = frontendGeneratePreviewList.get(0);
// 例如:continew-admin-ui/src/src/api/system
String apiPath = String.join(File.separator, frontendBasicPackagePath, "api", moduleName);
// 例如:continew-admin-ui/src/api/system/user.ts
File apiFile = new File(apiPath, apiGeneratePreview.getFileName());
if (!apiFile.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
FileUtil.writeUtf8String(apiGeneratePreview.getContent(), apiFile);
}
// 2、生成 view 代码
GeneratePreviewResp viewGeneratePreview = frontendGeneratePreviewList.get(1);
// 例如:continew-admin-ui/src/views/system
String vuePath = String.join(File.separator, frontendBasicPackagePath, "views", moduleName, StrUtil
.lowerFirst(genConfig.getClassNamePrefix()));
// 例如:continew-admin-ui/src/views/system/user/index.vue
File vueFile = new File(vuePath, viewGeneratePreview.getFileName());
if (!vueFile.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
FileUtil.writeUtf8String(viewGeneratePreview.getContent(), vueFile);
}
// 生成代码
this.generateCode(generatePreviewList, genConfigMapper.selectById(tableName));
});

// 打包下载
File tempDirFile = new File(tempDir, projectProperties.getAppName());
String zipFilePath = tempDirFile.getPath() + jodd.io.ZipUtil.ZIP_EXT;
Expand All @@ -316,27 +277,46 @@ public void generate(List<String> tableNames, HttpServletRequest request, HttpSe
}

/**
* 生成后端代码
* 生成代码
*
* @param generatePreviewList 生成预览列表
* @param genConfig 生成配置
*/
private void generateBackendCode(List<GeneratePreviewResp> generatePreviewList, GenConfigDO genConfig) {
private void generateCode(List<GeneratePreviewResp> generatePreviewList, GenConfigDO genConfig) {
// 获取前后端基础路径
String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig);
String frontendBasicPackagePath = SystemUtil.getUserInfo().getTempDir() + String
.join(File.separator, projectProperties.getAppName(), projectProperties.getAppName() + "-ui");
String packageName = genConfig.getPackageName();
String moduleName = StrUtil.subSuf(packageName, StrUtil
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
// 生成代码
Map<String, TemplateConfig> templateConfigMap = generatorProperties.getTemplateConfigs();
for (GeneratePreviewResp generatePreview : generatePreviewList) {
// 获取对应模板配置
TemplateConfig templateConfig = templateConfigMap.get(generatePreview.getFileName()
.replace(genConfig.getClassNamePrefix(), StringConstants.EMPTY)
.replace(FileNameUtil.EXT_JAVA, StringConstants.EMPTY));
// 例如:continew-admin/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl
String packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
.replace(StringConstants.DOT, File.separator));
// 例如:continew-admin/continew-admin-system/src/main/java/top/charles7c/continew/admin/system/service/impl/XxxServiceImpl.java
File classFile = new File(packagePath, generatePreview.getFileName());
TemplateConfig templateConfig = templateConfigMap.getOrDefault(StrUtil.subBefore(generatePreview
.getFileName(), StringConstants.DOT, true)
.replace(genConfig.getClassNamePrefix(), StringConstants.EMPTY), templateConfigMap.get("api"));
String packagePath;
if (generatePreview.isBackend()) {
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl
packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
.replace(StringConstants.DOT, File.separator));
} else {
// 例如:continew-admin/continew-admin-ui/src/views/system
packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName()
.replace(StringConstants.SLASH, File.separator), moduleName);
// 例如:continew-admin/continew-admin-ui/src/views/system/user
packagePath = ".vue".equals(templateConfig.getExtension())
? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
: packagePath;
}
// 后端:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl/XxxServiceImpl.java
// 前端:continew-admin/continew-admin-ui/src/views/system/user/index.vue
File file = new File(packagePath, generatePreview.getFileName());
// 如果已经存在,且不允许覆盖,则跳过
if (!classFile.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
FileUtil.writeUtf8String(generatePreview.getContent(), classFile);
if (!file.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
FileUtil.writeUtf8String(generatePreview.getContent(), file);
}
}
}
Expand All @@ -348,13 +328,25 @@ private void generateBackendCode(List<GeneratePreviewResp> generatePreviewList,
* @return 后端包路径
*/
private String buildBackendBasicPackagePath(GenConfigDO genConfig) {
// 例如:continew-admin/continew-admin-system/src/main/java/top/charles7c/continew/admin/system
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system
return SystemUtil.getUserInfo().getTempDir() + String.join(File.separator, projectProperties
.getAppName(), projectProperties.getAppName(), genConfig.getModuleName(), "src", "main", "java", genConfig
.getPackageName()
.replace(StringConstants.DOT, File.separator));
}

/**
* 获取前端文件名
*
* @param classNamePrefix 类名前缀
* @param className 类名
* @param extension 扩展名
* @return 前端文件名
*/
private String getFrontendFileName(String classNamePrefix, String className, String extension) {
return (".ts".equals(extension) ? StrUtil.lowerFirst(classNamePrefix) : className) + extension;
}

/**
* 预处理生成配置
*
Expand Down
57 changes: 0 additions & 57 deletions continew-admin-generator/src/main/resources/templates/api.ftl

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<template>
<a-modal
v-model:visible="visible"
:title="title"
:mask-closable="false"
:esc-to-close="false"
:modal-style="{ maxWidth: '520px' }"
width="90%"
@before-ok="save"
@close="reset"
>
<GiForm ref="formRef" v-model="form" :options="options" :columns="columns" />
</a-modal>
</template>

<script setup lang="ts">
import { get${classNamePrefix}, add${classNamePrefix}, update${classNamePrefix} } from '@/apis'
import { Message } from '@arco-design/web-vue'
import { GiForm, type Columns } from '@/components/GiForm'
import { useForm } from '@/hooks'
const dataId = ref('')
const isUpdate = computed(() => !!dataId.value)
const title = computed(() => (isUpdate.value ? '修改${businessName}' : '新增${businessName}'))
const formRef = ref<InstanceType<typeof GiForm>>()
const options: Options = {
form: {},
col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
btns: { hide: true }
}
const columns: Columns = [
<#list fieldConfigs as fieldConfig>
<#if fieldConfig.showInForm>
{
label: '${fieldConfig.comment}',
field: '${fieldConfig.fieldName}',
<#if fieldConfig.formType = 'TEXT'>
type: 'input',
<#elseif fieldConfig.formType = 'TEXT_AREA'>
type: 'textarea',
</#if>
<#if fieldConfig.isRequired>
rules: [{ required: true, message: '请输入${fieldConfig.comment}' }]
</#if>
},
</#if>
</#list>
]
const { form, resetForm } = useForm<({
// todo 待补充
})
// 重置
const reset = () => {
formRef.value?.formRef?.resetFields()
resetForm()
}
const visible = ref(false)
// 新增
const onAdd = () => {
reset()
dataId.value = ''
visible.value = true
}
// 修改
const onUpdate = async (id: string) => {
reset()
dataId.value = id
const res = await get${classNamePrefix}(id)
Object.assign(form, res.data)
visible.value = true
}
// 保存
const save = async () => {
try {
const isInvalid = await formRef.value?.formRef?.validate()
if (isInvalid) return false
if (isUpdate.value) {
await update${classNamePrefix}(form, dataId.value)
Message.success('修改成功')
} else {
await add${classNamePrefix}(form)
Message.success('新增成功')
}
emit('save-success')
return true
} catch (error) {
return false
}
}
const emit = defineEmits<{
(e: 'save-success'): void
}>()
defineExpose({ onAdd, onUpdate })
</script>
Loading

0 comments on commit 3dbe72f

Please sign in to comment.