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


    起因

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

    常见的使用场景比如租赁协议生成,邮件内容模板生成等等,不要傻傻的hard-code像‘#name#’这样的标记了。

    优势就是可自定义模板,灵活匹配可获取到对象的任何字段,解除开发侧的包袱

    开源框架

    wangEditor 简单的富文本编辑器,基本功能够用,使用方便
    RazorLight.NetCore3 基于Razor模板动态生成内容
    html2openxml 一个把html转换为Xml的组件,依赖于DocumentFormat.OpenXml

    富文本编辑器

    wangEditor已经更新到V3了,功能简洁高效,配置简单,爽的飞起,简单配置后就用起来。

    注意一点就是,想要用razor生成,模板的内容要尽量保持干净,不要混入html代码之外的内容。如果直接把word文档粘贴到editor中,界面上看起来是完好的,但其实会混进来很多xml的东西,像这样的成千上万行:
    <w:LsdException Locked="false" Priority="99" SemiHidden="false" Name="Colorful Grid Accent 6" ></w:LsdException>

    然后导致razor模板运行失败。解决方法如下:

    用wangEditor的pastTextHandle,在文本内容被帖进去之前,把影响razor的字符处理掉。   var E = window.wangEditor;

        var editor2 = new E('#demo');
        editor2.customConfig.pasteTextHandle = function (content) {
            return setEditor(content);
        }
    
    
    //设置wangeditor格式,去掉word的xml内容
    function setEditor(content) {
        // content 即粘贴过来的内容(html 或 纯文本),可进行自定义处理然后返回
        if (content == '' && !content) return '';
        var str = content;
        str = str.replace(/<xml>[sS]*?</xml>/ig, '');
        str = str.replace(/<style>[sS]*?</style>/ig, '');
        str = str.replace(/</?[^>]*>/g, '');
        str = str.replace(/[ | ]*
    /g, '
    ');
        str = str.replace(/ /ig, '');
        console.log('****', content);
        console.log('****', str);
        return str;
    }

    实际效果是这样的。里面的@() 就是常见的mvc里的razor语法

    模板引擎

    之前用过很多次了,只不过之前是.net framework下的RazorEngine,这次找了个.net core下的RazorLight.NetCore3.

    原理上很简单,比如我有一个模板 “我今天买了一本书,书名叫《@(Model.BookName)》,花了@(Model.Price)元钱”,然后我又拿到这么个对象

    var order = new Order();
    order.BookName="Lucky Day";
    order.Price = 100;
    

     我想根据模板生成实际内容就是:“我今天买了一本书,书名叫《Lucky Day》,花了100元钱”,只需要几行代码,就能拿到想要的结果

    var engine = new RazorLightEngineBuilder()
                   .UseEmbeddedResourcesProject(typeof(SysConfigAppService))
                   .UseMemoryCachingProvider()
                   .Build();
    
                    template = "我今天买了一本书,书名叫《@(Model.BookName)》,花了@(Model.Price)元钱"; 
                    string result = await engine.CompileRenderStringAsync("RazorId", template, order);
                    Logger.Info($"razor result: {result}");
    
                    return result;

    遇到的坑:如果对象属性值含有中文,会被编码成字符,解决办法是在模板最前面加上“@{DisableEncoding = true; }”就可以了

    template = "@{DisableEncoding = true; }" + template; 
    

      

    Html转Word

    最后一个需求是导出文件并下载,html导出成word,必须依赖openxml,搜遍全网找到这个html2openxml 

    支持.Net Core (netstandard2.1) 以及 .Net Framework 4.8

    这里把代码先贴一下,env是用来获取程序根目录的,因为我需要在Linux上跑,这种方式比较稳妥。过程是这样,生成一个随机的文件名,并放在根目录/ExportFile/文件夹下,导出word并写入文件后,返回文件路径。

    这里我采用的是服务端生成文件,把地址返回客户端再下载的方式,当然你也可以写文件流到客户端,根据业务需要自行选择。

     public static string ExportToWord(string html, IWebHostEnvironment env)
            {
                string file = SnowHelper.Instance.NextId() + ".docx";
                string fileDir = env.WebRootPath + "/ExportFile/";
                string filename = fileDir + file;
    
                if (!Directory.Exists(fileDir)) Directory.CreateDirectory(fileDir);
    
                if (File.Exists(filename)) File.Delete(filename);
    
                using (MemoryStream generatedDocument = new MemoryStream())
                {
                    using (WordprocessingDocument package = WordprocessingDocument.Create(generatedDocument, WordprocessingDocumentType.Document))
                    {
                        MainDocumentPart mainPart = package.MainDocumentPart;
                        if (mainPart == null)
                        {
                            mainPart = package.AddMainDocumentPart();
                            new Document(new Body()).Save(mainPart);
                        }
    
                        HtmlConverter converter = new HtmlConverter(mainPart);
                        converter.ParseHtml(html);
    
                        mainPart.Document.Save();
                    }
    
                    File.WriteAllBytes(filename, generatedDocument.ToArray());
                }
    
                return "/ExportFile/" + file;
                //System.Diagnostics.Process.Start(filename);
            }
    

      然后在appService层组织一下返回数据,把下载文件名和文件路径返回给前端。这里下载文件名是和实际文件名不一样的。

      [HttpPost]
            public async Task<FileOutDto> ExportProtocal(FileInputDto inputDto)
            {
                var order = await this.GetAsync(new EntityDto<long>(inputDto.Id));
                string path = FileHelper.ExportToWord(inputDto.Content, _environment);
                
                var result = new FileOutDto()
                {
                    FileName = $"租赁协议-{order.RentUser.Name}-{order.House.RoomNumber}.docx",
                    FilePath = path
                };
    
                return result;
            }
    

      最后到前端,加一个按钮并绑定事件

        function exportFile() {
            abp.ui.setBusy(_$form);
            var d = {
                Content: editor2.txt.html(),
                Id: $("#orderId").val()
        };
            _orderService.exportProtocal(d).done(function (res) {
                console.log(res);
    
                abp.notify.info(l('SuccessfullyExported'));
    
                var url = res.filePath;
                var link = document.createElement('a');
                // 设置导出的文件名
                link.download = res.fileName;
                link.href = url;
                // 点击获取文件
                link.click();
            }).always(function () {
                abp.ui.clearBusy(_$form);
            });
    
    
        }
    

    导出的word文件格式会和html有些许差别,微调下html就能导出想要的效果了  

    至此一个可自定义内容的模板生成功能就做好了。

  • 相关阅读:
    天大复试机试练习_003
    C++随手记--字符串转数字
    C++标准库STL 之 我觉得应该有的方法——split
    apt-get 详解&&配置阿里源
    Nginx 图文详解
    MySQL数据库管理常用命令小结
    oracle数据库备份
    SqlServer数据库备份还原步骤
    mysql数据备份与恢复
    Tomcat架构
  • 原文地址:https://www.cnblogs.com/rockcode777/p/13525081.html
Copyright © 2020-2023  润新知