首页 > 其他分享 >使用中台 Admin.Core 实现了一个Razor模板的通用代码生成器

使用中台 Admin.Core 实现了一个Razor模板的通用代码生成器

时间:2024-07-11 09:08:38浏览次数:19  
标签:代码生成 const 预览 Razor Core 生成 var 模板

前言

前面使用 Admin.Core 的代码生成器生成了通用代码生成器的基础模块 分组,模板,项目,项目模型,项目字段的基础功能,本篇继续完善,实现最核心的模板生成功能,并提供生成预览及代码文件压缩下载

准备

首先清楚几个模块的关系,如何使用,简单画一个流程图

image.png
前面完成了基础的模板组,模板管理,项目,模型,字段管理,都是由 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 吧

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

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

相关文档

标签:代码生成,const,预览,Razor,Core,生成,var,模板
From: https://www.cnblogs.com/morang/p/18294868/zhontai_admin_core_module_dev_common

相关文章

  • CorelDRAW2024新版本来咯!你的设计神助手
    ......
  • asp.net core 静态文件授权访问
    需求:项目中的有些静态文件,比如图片,JS等不想被爬虫直接爬到,需要登陆后才能访问微软官方介绍页面ASP.NETCore中的静态文件|MicrosoftLearn 根据授权提供静态文件:将它们存储在wwwroot之外。调用UseAuthorization之后调用UseStaticFiles,以指定路径。设置回退授权策......
  • 在Ubuntu上调试ARMv7的core文件
    1在Ubuntu上调试ARMv7的core文件1.1预备uname-a:获得目前嵌入式系统Linux(none)3.8.11-xilinx#40SMPPREEMPTThuJan1217:02:11CST2023armv7lGNU/Linux安装armv7的交叉编译工具链:sudoapt-getudpatesudoapt-getinstallgcc-arm-linux-gnueabihfgd......
  • .Net Core WebApi项目 Development模式下报错 System.InvalidOperationException 解决
    .NetCoreWebApi项目Development模式下报错System.InvalidOperationException:“Cannotresolvescopedservice'Microsoft.Extensions.Options.IOptionsSnapshot`1[Infrastructure.OptionsSetting]'fromrootprovider.” 但是在Production模式下,运行正常因为未设置的......
  • mormot.core.threads--TSynThreadPool
    mormot.core.threads--TSynThreadPool{************面向服务器进程的线程池}TSynThreadPool=class;//前向声明TSynThreadPool类///定义了TSynThreadPool所使用的工作线程TSynThreadPoolWorkThread=class(TSynThread)protectedfOwner:TSynThreadPool;//......
  • 一款EF Core下高性能、轻量级针对分表分库读写分离的解决方案
    前言今天大姚给大家分享一款EFCore下高性能、轻量级针对分表分库读写分离的解决方案,开源(ApacheLicense)的EFCore拓展程序包:ShardingCore。ShardingCore项目介绍ShardingCore是一款开源、简单易用、高性能、普适性,针对EFCore生态下的分表分库的扩展解决方案,支持EFCore2+......
  • 解析coredump文件
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、coredump是什么?二、操作步骤总结前言编写程序的时候可能会遇到coredump问题,如下方法可以解析dump,简单定位报错问题内核版本:5.10提示:以下是本篇文章正文内容,下面案例可供参考一、......
  • ASP.Net Core解读launchSettings.json
    一、环境目标框架:.NETCore3.1ASP.NETCore是一个全新的Web开发平台,微软在它上面构建了MVC、SingalR、GRPC、Orleans这样广泛使用的Web框架,我们先以MVC框架为例介绍利用ASP.NETCore构建项目的常见名词。这篇文章介绍启动配置文件launchSettings.json ASP.NET Core MVC项目......
  • Core WebAPI 中 Program.cs和Startup.cs认识 (高频考点)
    1.Program.cs认识publicclassProgram{publicstaticvoidMain(string[]args){//Build方法构建出一个web应用程序,然后去运行CreateWebHostBuilder(args)//调用下面的方法,返回一个IWebHostBuilder对象.Build()//用......
  • .net core部署linux下载文件 文件为中文下载
    在项目中,需要下载模板,某个文件夹下有excel文件或者word等 文件名为中文的时候在linux服务器下载不了  如果直接写路由的话在windows下是可以直接访问的,但是core部署在linux上所以会有些不同第一种:写一个接口使用流下载[httpget("download")]publicasyncTask<FileResult......