使用中台 Admin.Core 实现了一个Razor模板的通用代码生成器
前言
前面使用 Admin.Core 的代码生成器生成了通用代码生成器的基础模块 分组,模板,项目,项目模型,项目字段的基础功能,本篇继续完善,实现最核心的模板生成功能,并提供生成预览及代码文件压缩下载
准备
首先清楚几个模块的关系,如何使用,简单画一个流程图
前面完成了基础的模板组,模板管理,项目,模型,字段管理,都是由 Admin.Core 框架的代码生成器完成,感兴趣的可参考前篇使用,文末也会给出仓库地址,有问题欢迎交流
本文主要分享项目代码的生成,先放出效果图
- 项目生成管理,支持多模板组
- 模板生成预览,预览页可以直接编辑模板
- 模板生成,将生成压缩包并下载
实现
需要实现上面效果的代码生成器,基础的增删改查都可以借由代码生成器生成,也写过几篇了这里就不再赘述,我们只需要关注核心部分:如何生成?如何预览?如何下载?
根据模板生成对应项目的代码文件
当我们有了一个模板(模板内容),也有了对应的配置项(项目&项目模型&项目字段),中间肯定是需要一个模板引擎来将模板根据配置项解析出来的。
市面上有很多模板引擎,比如 ejs,art 等,但既然是搞C#的,那不妨试试看 Razor (之前的Admin.Core代码生成器也是基于razor模板引擎,也可以换其他的或者支持多种模板引擎),C#的语法写起来还是挺舒服的,并且其实还可以新建一个.net core 的项目添加页面后可以复制模板和模型到页面为自己的模板增加智能提示
模板引擎的使用
项目中引用包:RazorEngine.NetCore
- 原版只支持 framework,大佬打包的 netcore 版本
<ItemGroup>
<PackageReference Include="RazorEngine.NetCore" Version="3.1.0" />
</ItemGroup>
- 使用方式:指定模型,内容,模板名称即可
var code="模板内容";
var key="模板名";
//模型名称
var model=new DevProjectRazorRenderModel();
RazorEngine.Engine.Razor.RunCompile(new LoadedTemplateSource(code), key, model.GetType(), model);
模板内容的写法
- 我这里定义了固定模型,所以需要先什么模型的变量,这里指定了 gen
- 模板语法文档
@{
var gen = Model as ZhonTai.Module.Dev.DevProjectRazorRenderModel;
}
- 这里定义了一个公共的项目模型,后续增加项目模型字段的信息都无需更搞代码,生成即可,另外也可以再属性模型中添加字典等属性,即可灵魂的再模板中使用动态配置了
//模型渲染
var gen = new DevProjectRazorRenderModel()
{
Project = Mapper.Map<DevProjectGetOutput>(project),
Model = Mapper.Map<DevProjectModelGetOutput>(model),
Fields = Mapper.Map<List<DevProjectModelFieldGetOutput>>(modelFields),
};
模板内容的生成
- 这里分为了两部分,一个是文件路径,一个是文件内容
- 因为要生成的路径可能也会包含一些模块或者模型的信息,所以可以将模板路径也使用模板生成,这里直接拼接一个模板即可
var pathCodeText = @"
@{
var gen = Model as ZhonTai.Module.Dev.DevProjectRazorRenderModel;
}
" + outPath;
//转换路径
var outPath = RazorCompile(gen, $"{project.Code}_{model.Code}_{tpl.Name}_Path.tpl", pathCodeText).Trim();
文末附完整代码
内容文件的下载
- 因为是多模板组多模板,所以每次生成项目代码都基本是多个文件,一个个下载很明显不合理,所以可以将所有代码内容文件打包成压缩包进行下载,下面是核心压缩代码,无需引用包,暂时只在Windows中测试使用
- 完整代码如下,做了一些文件和文件夹的判断处理,可自行封装
- 通过GenerateAsync获取到文件信息后写入文件,再将目录进行打包返回即可
/// <summary>
/// 下载
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult> DownAsync(DevProjectGenGenerateInput input)
{
var path = Path.Combine(AppContext.BaseDirectory, "DownCodes", DateTime.Now.ToString("yyyyMMddHHmmss"));
var zipFileName = $"源码{DateTime.Now.ToString("yyyyMMddHHmmss")}.zip";
var zipPath = Path.Combine(AppContext.BaseDirectory, "DownCodes", zipFileName);
try
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
//获取内容信息
var codes = await GenerateAsync(input);
foreach (var code in codes)
{
var codePath = Path.Combine(path, code.Path);
var directory = Path.GetDirectoryName(codePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
if (!File.Exists(codePath))
{
using (var fs = File.Open(codePath, FileMode.Create, FileAccess.ReadWrite))
{
await fs.WriteAsync(Encoding.UTF8.GetBytes(code.Content));
}
}
}
ZipFile.CreateFromDirectory(path, zipPath);
var bytes = await File.ReadAllBytesAsync(zipPath);
return new FileContentResult(bytes, "application/zip")
{
FileDownloadName = zipFileName
};
}
finally
{
if (Directory.Exists(path))
{
Directory.Delete(path, true);
}
if (File.Exists(zipPath))
{
File.Delete(zipPath);
}
}
}
前端对应需要支持文件下载,可以修改为生成对应下载URL,用GET请求直接打开新窗口进行下载
项目中前端页面的重点
整个框架主要使用者肯定是.net开发,如果没有写过 vue 项目,写起来的时候可能会有一些吃力,但因为现在有了代码生成器,大部分代码都可以生成,也可以做参考,所以这里只做一些关键点的说明
框架页面菜单的添加说明:具体可参考前文进行创建
- 系统管理-添加视图->指定 vue 页面文件的路径,可再权限菜单中复用这个视图生成不同的路由地址
- 接口管理-同步接口->将后端服务映射为权限点,对应后端功能权限
- 权限管理-添加权限->添加菜单,功能点
- 用户-角色->通过角色分配角色权限,用户管理角色
Vue 文档
有时间的话至少过一遍再开始,磨刀不误砍柴工
生命周期的一定花时间看看:官方文档
页面中获取路由信息与页面跳转
以项目生成页面调整到预览页面为例
- 项目生成页和预览页引入定义
//引入路由
import { useRoute, useRouter } from 'vue-router'
//路由信息
const route = useRoute()
//路由跳转
const router = useRouter()
- 跳转到预览页
router.push({
path: '/dev/dev-project-gen/preview', query: {
projectId: row.projectId,
groupIds: row.groupIds_Values
}
})
- 预览页获取生成列表传递的参数
//从路由中获取query参数
onMounted(() => {
state.filter.projectId = route.query.projectId
state.filter.groupIds = route.query.groupIds
})
左侧树/列表右侧预览实现
将左侧的列表列封装为一个组件,右侧如果简单可封装,也可以直接写到预览页面
这里参考框架中用户列表做的
<my-layout class="my-layout">
<pane size="30" min-size="20" max-size="35">
<div class="my-flex-column w100 h100">
<group-template-menu :groupIds="state.filter.groupIds" :projectId="state.filter.projectId"
@node-click="onNodeClick" select-first-node></group-template-menu>
</div>
</pane>
<pane size="70" v-loading="state.loading">
<div class="my-flex-column w100 h100">
内容预览部分
</div>
</pane>
</my-layout>
当然,封装了组件记得引入用到的组件
const GroupTemplateMenu = defineAsyncComponent(() => import('./components/dev-group-template-menu.vue'))
const MyLayout = defineAsyncComponent(() => import('/@/components/my-layout/index.vue'))
组件中获取传递的参数示例
interface Props {
modelValue: number[] | null | undefined
selectFirstNode: boolean,
projectId: number,
groupIds: number[]
}
const props = withDefaults(defineProps<Props>(), {
modelValue: () => [],
selectFirstNode: false,
projectId: 0,
groupIds: () => []
})
在预览左侧菜单中,我们可以看到一个标记的小图标,用来直接编辑模板
这个弹窗其实是直接引用了编辑模板的组件
<template>
...
<dev-template-form ref="devTemplateFormRef" :title="'编辑模板'"></dev-template-form>
...
</template>
<script>
...
// 引入组件
const DevTemplateForm = defineAsyncComponent(() => import('../../dev-template/components/dev-template-form.vue'))
//使用
const devTemplateFormRef = ref()
const editTemplate = (node, data) => {
devTemplateFormRef.value.open({
id: data.id
})
}
...
</script>
defineExpose({
open,
})
如上可以看到我们使用和组件同名的 const devTemplateFormRef = ref() 即可获取到组件引用
另外调用的方法可以查看 dev-template-form.vue 是开放了方法的
defineExpose({
open,
})
下载压缩包文件
首先需要后端返回文件流,然后调用对应接口的时候指定format格式为blob,并创建下载连接点击即可
const genCode = async (row: DevProjectGenGetOutput) => {
new DevProjectGenApi().down({ projectId: row.projectId, groupIds: row.groupIds_Values?.map(s => Number(s)) }, {
loading: false,
showErrorMessage: false,
format: 'blob'
})
.then((res) => {
const a = document.createElement('a');
a.href = URL.createObjectURL(res as Blob);
a.download = '源码.zip';
a.click();
});
}
后语
本文所有代码皆在 yimogit/Emo.Dev 仓库中可以找到,觉得有用的来个 Star 吧
基于前面代码生成器生成的功能模块上,周末花了两天完善项目生成,终于算是搞定
后面还需要逐步完善生成器,欢迎点个赞,留个言,交流指点一二
相关文档
- Emo.Dev 本文代码仓库,一个通用代码生成器
- 代码生成器系列文章
- abp-vnext-pro-suite 一个abp的生成器,功能设计上和下载都是参考借鉴了这个项目
- Admin.Core 项目所用到的框架
- RazorEngine.NetCore 模板引擎
- Razor 模板引擎语法
使用中台 Admin.Core 实现了一个Razor模板的通用代码生成器的更多相关文章
- ASP.NET Core 快速入门(Razor Pages + Entity Framework Core)
引子 自从 2009 年开始在博客园写文章,这是目前我写的最长的一篇文章了. 前前后后,我总共花了 5 天的时间,每天超过 3 小时不间断写作和代码调试.总共有 8 篇文章,每篇 5~6 个小结,总截 ...
- Asp.net core 2.0.1 Razor 的使用学习笔记(一)
环境:vs2017 版本:15.5.6 一.新建项目 1.文件>新建>项目>Visual c#>.NET Core>ASP.NET Core Web应用程序(“.NET ...
- Asp.net core 2.0.1 Razor 的使用学习笔记(五)
按说这里应该写关于Role角色类的笔记,但是我还没时间实验这块,所以等以后我搞定了再来分享.现在先写其他部分. Asp.net core 2.0.1 Razor 的使用学习笔记——建立模型 按照微软官 ...
- 独立使用Asp.net Core 的razor模板 (一):Razor引擎的一些细节
由于最近需要写一些界面稍微好看点的Winform程序,如果用原生控件,,想要达到好看的程度,需要花费比较大的功夫,因为之前使用过CefSharp,因此发觉如果是使用CEF+Html的方式,界面可以相对 ...
- Orchard Core 增加了一个API模块,要怎么调用
如下,我在Orchard Core框架中添加了一个API的模块,并且定义了对应的权限才可以调用,那么我们现在考虑的就是要怎么去调用它. 首先,我们用Fiddler查看下我们正常的登录的http报文,直 ...
- C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序
C#中缓存的使用 缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可: <%@ Outp ...
- ASP.NET Core:创建一个Core项目
ylbtech-ASP.NET Core:创建一个Core项目 1.返回顶部 1. 2. 3. 4. 5. 2.返回顶部 1.新建Razor页面 2. 3. 4.Abc 4.1.Abc ...
- 基于ABP做一个简单的系统——实战篇:4.基于富文本编辑器,Razor模板引擎生成内容并导出Word 填坑记录
起因 需求是这样的,有一种协议需要生成,协议的模板是可配置的,在生成过程中,模板中的内容可以根据约定的标记进行替换(就像mvc的razor模板一样).生成后的内容还需要导出成word或pdf. 常见的 ...
- Uwl.Admin.Core开源框架(三) 使用RabbitMQ
Uwl.Admin.Core中使用RabbitMQ消息队列: 本文负责讲解RabbitMQ的使用 Uwl.Admin.Core使用的技术有: *.Async和Await 异步编程 *.Reposito ...
- Uwl.Admin.Core开源框架(二) 使用QuartzNet
Uwl.Admin.Core中使用QuartzNet定时任务模块: 本文负责讲解RabbitMQ的使用 Uwl.Admin.Core使用的技术有: *.Async和Await 异步编程 *.Repos ...
随机推荐
- 【Azure Developer】如何通过Azure Portal快速获取到对应操作的API并转换为Python代码
问题描述 对于Azure资源进行配置操作,门户上可以正常操作.但是想通过Python代码实现,这样可以批量处理.那么在没有SDK的情况下,是否有快速办法呢? 问题解答 当然可以,Azure Porta ...
- kubernetes 之 Rolling Update 滚动升级
滚动升级 1.错误的yml文件 [machangwei@mcwk8s-master ~]$ cat mcwHttpd.yml apiVersion: apps/v1 kind: Deployment ...
- 一款基于C#开发的通讯调试工具(支持Modbus RTU、MQTT调试)
前言 今天大姚给大家分享一款基于C#.WPF.Prism.MaterialDesign.HandyControl开发的通讯调试工具(支持Modbus RTU.MQTT调试,界面色彩丰富):Wu.Com ...
- npm install current-device js 端判断程序运行的设备
https://github.com/matthewhudson/current-device CURRENT-DEVICE This module makes it easy to w ...
- win11如何调解屏幕亮度【win10刚刚升级win11】?
打开电脑后鼠标右键,点击个性化 点击系统 点击屏幕亮度 滑动按钮,调解屏幕亮度即可
- CSS---鼠标悬浮时逐渐变大样式
.tuijian_2:hover{ transform: scale(1.3); transition: all 1s; }
- .net c# 文件分片/断点续传之下载--客户端
断点续传客户端实现主要参考了以下文章: https://blog.csdn.net/binyao02123202/article/details/76599949 客户端实现续传的主要是一下几点 1. ...
- 7.12考试总结(NOIP模拟12)[简单的区间·简单的玄学·简单的填数]
即使想放弃,也没法放弃最想要的东西,这就是人 前言 这次应该是和 SDFZ 一起打的第一场比赛吧. 然而我还是 FW 一个... 这次考试也有不少遗憾,主要的问题是码力不足,不敢去直面正解,思考程度不 ...
- Linux进程间通信-FIFO(命名管道)
本系列文章主要是学习记录Linux下进程间通信的方式. 常用的进程间通信方式:管道.FIFO.消息队列.信号量以及共享存储. 参考文档:<UNIX环境高级编程(第三版)> 参考视频:Lin ...
- SMB3.0多通道叠加双网卡提速
SMB3.0多通道叠加双网卡提速 (双网卡.多网卡,NAS,局域网共享速度) WIN8及以上是默认开启的.(WIN10.WIN11 默认开启) 只需要同规格的网卡,比如你一张是1Gbps的,另一张网卡 ...