前言

前面使用 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 吧

基于前面代码生成器生成的功能模块上,周末花了两天完善项目生成,终于算是搞定

后面还需要逐步完善生成器,欢迎点个赞,留个言,交流指点一二

相关文档

使用中台 Admin.Core 实现了一个Razor模板的通用代码生成器的更多相关文章

  1. ASP.NET Core 快速入门(Razor Pages + Entity Framework Core)

    引子 自从 2009 年开始在博客园写文章,这是目前我写的最长的一篇文章了. 前前后后,我总共花了 5 天的时间,每天超过 3 小时不间断写作和代码调试.总共有 8 篇文章,每篇 5~6 个小结,总截 ...

  2. Asp.net core 2.0.1 Razor 的使用学习笔记(一)

    环境:vs2017 版本:15.5.6 一.新建项目 1.文件>新建>项目>Visual c#>.NET Core>ASP.NET Core Web应用程序(“.NET ...

  3. Asp.net core 2.0.1 Razor 的使用学习笔记(五)

    按说这里应该写关于Role角色类的笔记,但是我还没时间实验这块,所以等以后我搞定了再来分享.现在先写其他部分. Asp.net core 2.0.1 Razor 的使用学习笔记——建立模型 按照微软官 ...

  4. 独立使用Asp.net Core 的razor模板 (一):Razor引擎的一些细节

    由于最近需要写一些界面稍微好看点的Winform程序,如果用原生控件,,想要达到好看的程度,需要花费比较大的功夫,因为之前使用过CefSharp,因此发觉如果是使用CEF+Html的方式,界面可以相对 ...

  5. Orchard Core 增加了一个API模块,要怎么调用

    如下,我在Orchard Core框架中添加了一个API的模块,并且定义了对应的权限才可以调用,那么我们现在考虑的就是要怎么去调用它. 首先,我们用Fiddler查看下我们正常的登录的http报文,直 ...

  6. 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 ...

  7. 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 ...

  8. 基于ABP做一个简单的系统——实战篇:4.基于富文本编辑器,Razor模板引擎生成内容并导出Word 填坑记录

    起因 需求是这样的,有一种协议需要生成,协议的模板是可配置的,在生成过程中,模板中的内容可以根据约定的标记进行替换(就像mvc的razor模板一样).生成后的内容还需要导出成word或pdf. 常见的 ...

  9. Uwl.Admin.Core开源框架(三) 使用RabbitMQ

    Uwl.Admin.Core中使用RabbitMQ消息队列: 本文负责讲解RabbitMQ的使用 Uwl.Admin.Core使用的技术有: *.Async和Await 异步编程 *.Reposito ...

  10. Uwl.Admin.Core开源框架(二) 使用QuartzNet

    Uwl.Admin.Core中使用QuartzNet定时任务模块: 本文负责讲解RabbitMQ的使用 Uwl.Admin.Core使用的技术有: *.Async和Await 异步编程 *.Repos ...

随机推荐

  1. Vue-Plugin-HiPrint

    Vue-Plugin-HiPrint 是一个Vue.js的插件,旨在提供一个简单而强大的打印解决方案.通过 Vue-Plugin-HiPrint,您可以轻松地在Vue.js应用程序中实现高度定制的打印 ...

  2. pageoffice 6 实现数据区域填充(插入文本、图片、word、excel等)

    在实际的Word文档开发中,经常需要自动填充数据到Word模板中,以生成动态的Word文档. 例如: 1.我们可以根据数据库表中已保存的个人信息,设计好一个简历模板docx文件,然后通过代码将这些个人 ...

  3. 解决”将公司Linux服务器上的脚本导出到windows上打开串行的“问题

    目录 一.前期准备 二.回车转换 一.前期准备 1.在linux服务器上写一个简单的脚本. [root@node5 ~]# vim linux脚本.sh [root@node5 ~]# cat lin ...

  4. 基于webapi的websocket聊天室(番外二)

    我比较好奇的是webapi服务器怎么处理http请求和websocket请求.有了上一篇番外的研究,这里就可以试着自己写个非常简易的webapi服务器来接收这两种请求. 效果 http请求 消息打印 ...

  5. Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案

    原文首发链接:Swoole 实践篇之结合 WebRTC 实现音视频实时通信方案 大家好,我是码农先森. 引言 这次实现音视频实时通信的方案是基于 WebRTC 技术的,它是一种点对点的通信技术,通过浏 ...

  6. CSS——阴影

    <!DOCTYPE html> <html> <head> <style> p.one { text-shadow: 3px 5px 5px #FF00 ...

  7. kubernetes的三种探针startupprobe,ReadinessProbe,LivenessProbe记录

    kubernetes的三种探针 startupprobe: k8s1.16版本后新加的探测方式,用于判断容器内应用程序是否已经启动,如果配置了startuprobe,就会先禁用其他的探测,直到它成功为 ...

  8. (八十八)c#Winform自定义控件-转子

    官网 http://www.hzhcontrols.com/ 前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kw ...

  9. if语句嵌套

       // if语句的嵌套         // 在if语句的{}中,执行程序中,再次有if语句         /*         if(){             if(){          ...

  10. 使用itextPDF实现PDF电子公章工具类

    使用itextPDF实现PDF电子公章工具类 一.制作公章 在线网站:印章生成器 - Kalvin在线工具 (kalvinbg.cn) 然后对公章进行下载保存 盖章图片: 二.生成数字签名 2.1: ...