使用中台 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 MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...