使用中台 Admin.Core 實現了一個Razor模板的通用代碼生成器

来源:https://www.cnblogs.com/morang/p/18294868/zhontai_admin_core_module_dev_common
-Advertisement-
Play Games

前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...


前言

前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載

準備

首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖

image

前面完成了基礎的模板組,模板管理,項目,模型,欄位管理,都是由 Admin.Core 框架的代碼生成器完成,感興趣的可參考前篇使用,文末也會給出倉庫地址,有問題歡迎交流

image

本文主要分享項目代碼的生成,先放出效果圖

  • 項目生成管理,支持多模板組

image

  • 模板生成預覽,預覽頁可以直接編輯模板

image

  • 模板生成,將生成壓縮包並下載

image

實現

需要實現上面效果的代碼生成器,基礎的增刪改查都可以藉由代碼生成器生成,也寫過幾篇了這裡就不再贅述,我們只需要關註核心部分:如何生成?如何預覽?如何下載?

根據模板生成對應項目的代碼文件

當我們有了一個模板(模板內容),也有了對應的配置項(項目&項目模型&項目欄位),中間肯定是需要一個模板引擎來將模板根據配置項解析出來的。

市面上有很多模板引擎,比如 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 文檔

有時間的話至少過一遍再開始,磨刀不誤砍柴工

生命周期的一定花時間看看:官方文檔

頁面中獲取路由信息與頁面跳轉

以項目生成頁面調整到預覽頁面為例

image

  • 項目生成頁和預覽頁引入定義
//引入路由
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: () => []
})

在預覽左側菜單中,我們可以看到一個標記的小圖標,用來直接編輯模板

image

這個彈窗其實是直接引用了編輯模板的組件

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

基於前面代碼生成器生成的功能模塊上,周末花了兩天完善項目生成,終於算是搞定

後面還需要逐步完善生成器,歡迎點個贊,留個言,交流指點一二

相關文檔

未經許可,禁止轉載!!!
作者:易墨
Github:yimogit
純靜態工具站點:metools


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文主要介紹 HSQLDB 的基本使用,文中所使用到的軟體版本:Java 11.0.22、HSQLDB 2.7.2。 1、進程內模式 直接使用 JDBC 連接資料庫即可,如果資料庫不存在會自動創建。 1.1、file 資料庫 @Test public void inProcessFile() thr ...
  • 前言 Serilog是 .NET 上的一個原生結構化高性能日誌庫,這個庫能實現一些比內置庫更高度的定製。日誌持久化是其中一個非常重要的功能,生產環境通常很難掛接調試器或者某些bug的觸發條件很奇怪。為了在脫離調試環境的情況下儘可能保留更多線索來輔助解決生產問題,持久化的日誌就顯得很重要了。目前Ser ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他們有一個崩潰的dump讓我幫忙看下怎麼回事,確實有太多的人在網上找各種故障分析最後聯繫到了我,還好我一直都是免費分析,不收取任何費用,造福社區。 話不多說,既然有 dump 來了,那就上 windbg 說話吧。 二:WinDbg 分析 1. 為什麼 ...
  • 早兩天寫了一篇S3簡單上傳文件的小工具,知乎上看到了一個問題問如何實現顯示MINIO上傳進度,因此拓展一下這個小工具能夠在上傳大文件時顯示進度。 ...
  • 提高錄製視頻的質量,使腳本內容資料、筆記本電腦屏幕、手機錄製三者有機會整合在一起,主要解決的問題和特點:(1)在手機錄製視頻過程,不會因為眼睛看腳本內容導致眼神漂浮,眼睛與手機攝像頭保持對焦狀態。(2)隨意調整文字腳本區域大小、手機與電腦屏幕倚靠區域大小、引用資料區域大小。(3)載入錄製視頻文字腳本... ...
  • 前言 .NET 官方有一個用來管理國際化資源的擴展包Microsoft.Extensions.Localization,ASP.NET Core也用這個來實現國際化功能。但是這個包的翻譯數據是使用resx資源文件來管理的,這就意味著無法動態管理。雖然官方有在文檔中提供了一些第三方管理方案,但是都不太 ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
一周排行
    -Advertisement-
    Play Games
  • 前言 微服務架構已經成為搭建高效、可擴展系統的關鍵技術之一,然而,現有許多微服務框架往往過於複雜,使得我們普通開發者難以快速上手並體驗到微服務帶了的便利。為瞭解決這一問題,於是作者精心打造了一款最接地氣的 .NET 微服務框架,幫助我們輕鬆構建和管理微服務應用。 本框架不僅支持 Consul 服務註 ...
  • 先看一下效果吧: 如果不會寫動畫或者懶得寫動畫,就直接交給Blend來做吧; 其實Blend操作起來很簡單,有點類似於在操作PS,我們只需要設置關鍵幀,滑鼠點來點去就可以了,Blend會自動幫我們生成我們想要的動畫效果. 第一步:要創建一個空的WPF項目 第二步:右鍵我們的項目,在最下方有一個,在B ...
  • Prism:框架介紹與安裝 什麼是Prism? Prism是一個用於在 WPF、Xamarin Form、Uno 平臺和 WinUI 中構建鬆散耦合、可維護和可測試的 XAML 應用程式框架 Github https://github.com/PrismLibrary/Prism NuGet htt ...
  • 在WPF中,屏幕上的所有內容,都是通過畫筆(Brush)畫上去的。如按鈕的背景色,邊框,文本框的前景和形狀填充。藉助畫筆,可以繪製頁面上的所有UI對象。不同畫筆具有不同類型的輸出( 如:某些畫筆使用純色繪製區域,其他畫筆使用漸變、圖案、圖像或繪圖)。 ...
  • 前言 嗨,大家好!推薦一個基於 .NET 8 的高併發微服務電商系統,涵蓋了商品、訂單、會員、服務、財務等50多種實用功能。 項目不僅使用了 .NET 8 的最新特性,還集成了AutoFac、DotLiquid、HangFire、Nlog、Jwt、LayUIAdmin、SqlSugar、MySQL、 ...
  • 本文主要介紹攝像頭(相機)如何採集數據,用於類似攝像頭本地顯示軟體,以及流媒體數據傳輸場景如傳屏、視訊會議等。 攝像頭採集有多種方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),網上一些文章以及 ...
  • 前言 Seal-Report 是一款.NET 開源報表工具,擁有 1.4K Star。它提供了一個完整的框架,使用 C# 編寫,最新的版本採用的是 .NET 8.0 。 它能夠高效地從各種資料庫或 NoSQL 數據源生成日常報表,並支持執行複雜的報表任務。 其簡單易用的安裝過程和直觀的設計界面,我們 ...
  • 背景需求: 系統需要對接到XXX官方的API,但因此官方對接以及管理都十分嚴格。而本人部門的系統中包含諸多子系統,系統間為了穩定,程式間多數固定Token+特殊驗證進行調用,且後期還要提供給其他兄弟部門系統共同調用。 原則上:每套系統都必須單獨接入到官方,但官方的接入複雜,還要官方指定機構認證的證書 ...
  • 本文介紹下電腦設備關機的情況下如何通過網路喚醒設備,之前電源S狀態 電腦Power電源狀態- 唐宋元明清2188 - 博客園 (cnblogs.com) 有介紹過遠程喚醒設備,後面這倆天瞭解多了點所以單獨加個隨筆 設備關機的情況下,使用網路喚醒的前提條件: 1. 被喚醒設備需要支持這WakeOnL ...
  • 前言 大家好,推薦一個.NET 8.0 為核心,結合前端 Vue 框架,實現了前後端完全分離的設計理念。它不僅提供了強大的基礎功能支持,如許可權管理、代碼生成器等,還通過採用主流技術和最佳實踐,顯著降低了開發難度,加快了項目交付速度。 如果你需要一個高效的開發解決方案,本框架能幫助大家輕鬆應對挑戰,實 ...