日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

简单快速导出word文档

發布時間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 简单快速导出word文档 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近,我寫公司項目word導出功能,應該只有2小時的工作量,卻被硬生生的拉長2天,項目上線到業務正常運行也被拉長到2個星期。

?

為什么如此浪費時間呢?

??? 1)公司的項目比較老,采用硬編碼模式,意味著word改一個字就要發布一次代碼。發布檢驗就浪時間了。

??? 2)由于硬編碼,采用的是<html>這種格式,手寫代碼比較廢時,而且編寫表格時會遇到單元格字數變多被撐大,表格變形的情況。表格長度需要人工計算。這類意想不到的問題。

??? 3)公司測試庫數據不全,測試庫數據無法全面覆蓋線上環境。這又拉長了檢驗時間。

??? 4)項目分支被正在開發的分支合并了,一下子被拉長了4天。

?

這簡單功能浪費太多時間了,我在網上搜了一下word導出的方案:

??? 第一種:硬編碼,就是公司的方案,問題太多了不用考慮。

??? 第二種:通過Sql查詢數據,存入字典,再通過第三方組件替換word的文字。這種方案,簡單容操作,sql查詢可以換成存儲過程,也存在缺點,1)存儲過程要寫提很細,邏輯算法都寫在存儲過程,存儲過程可能變得很復雜。2)不支持表格內插入多條數據。

??? 第三種:通過Sql查詢數據,使用Razor模板引擎生成word。這種方案解決了存儲過程復雜問題,但Razor模板內使用<html>這種格式,所以寫模板時很麻煩。

??? 第四種:通過Sql查詢數據,存入字典,再通過第三方組件替換word的域。這種方案與第二種方案類似,對我個人來說,我不喜歡修改域。

?

但是,我想要一個簡單、容易控制、表格內能插入多條數據、可商用的方案。

??? 簡單:類似第二種方案,數據存入字典,循環替換word的文字,存儲過程可以寫得簡單。

??? 容易控制:模板不能使用<html>這種格式,最好能用office直接控制表格文字大小、顏色。

??? 表格內能插入多條數據:我寫的組件內必須有索引。

??? 可商用:拒絕商用組件。

?

經過幾天琢磨,我找到可行的方案:存儲過程+模板+算法可控

依賴組件:

??? DocumentFormat.OpenXml,微軟官方開源組件,支持docx文件,MIT協議。

??? ToolGood.Algorithm,本人的Excel計算引擎組件,MIT協議,可簡化存儲過程。

?

核心代碼:

??? ReplaceTemplate 替換Word文字

??? ReplaceTable 替換Word表格并支持插入

?

ReplaceTemplate 替換Word文字

public class WordTemplate : AlgorithmEngine{private readonly static Regex _tempEngine = new Regex("^###([^::]*)[::](.*)$");// 定義臨時變量private readonly static Regex _tempMatch = new Regex("(#[^#]+#)");// private readonly static Regex _simplifyMatch = new Regex(@"(\{[^\{\}]*\})");//簡化文本 只讀取字段private void ReplaceTemplate(Body body){var tempMatches = new List<string>();List<Paragraph> deleteParagraph = new List<Paragraph>();foreach (var paragraph in body.Descendants<Paragraph>()) {var text = paragraph.InnerText.Trim();var m = _tempEngine.Match(text);if (m.Success) {var name = m.Groups[1].Value.Trim();var engine = m.Groups[2].Value.Trim();var value = this.TryEvaluate(engine, "");this.AddParameter(name, value);deleteParagraph.Add(paragraph);continue;}var m2 = _tempMatch.Match(text);if (m2.Success) {tempMatches.Add(m2.Groups[1].Value);continue;}var m3 = _simplifyMatch.Match(text);if (m3.Success) {tempMatches.Add(m3.Groups[1].Value);continue;}}foreach (var paragraph in deleteParagraph) {paragraph.Remove();}Regex nameReg = new Regex(string.Join("|", listNames));foreach (var m in tempMatches) {string value;if (m.StartsWith("#")) {var eval = m.Trim('#');……value = this.TryEvaluate(eval, "");} else {value = this.TryEvaluate(m.Replace("{", "[").Replace("}", "]"), "");}foreach (var paragraph in body.Descendants<Paragraph>()) {ReplaceText(paragraph, m, value);}}} // 代碼來源 https://stackoverflow.com/questions/19094388/openxml-replace-text-in-all-documentprivate void ReplaceText(Paragraph paragraph, string find, string replaceWith){…. } }

ReplaceTable 替換Word表格并支持插入

private readonly static Regex _rowMatch = new Regex(@"({{(.*?)}})");//private int _idx;private List<string> listNames = new List<string>();private void ReplaceTable(Body body){foreach (Table table in body.Descendants<Table>()) {foreach (TableRow row in table.Descendants<TableRow>()) {bool isRowData = false;foreach (var paragraph in row.Descendants<Paragraph>()) {var text = paragraph.InnerText.Trim();if (_rowMatch.IsMatch(text)) {isRowData = true;break;}}if (isRowData) {// 防止 list[i].Id 寫成 [list][[i]].Id 這種繁雜的方式Regex nameReg = new Regex(string.Join("|", listNames));Dictionary<string, string> tempMatches = new Dictionary<string, string>();foreach (Paragraph ph in row.Descendants<Paragraph>()) {var m2 = _rowMatch.Match(ph.InnerText.Trim());if (m2.Success) {var txt = m2.Groups[1].Value;var eval = txt.Substring(2, txt.Length - 4).Trim();eval = nameReg.Replace(eval, new MatchEvaluator((k) => {return "[" + k.Value + "]";}));tempMatches[txt] = eval;}}TableRow tpl = row.CloneNode(true) as TableRow;TableRow lastRow = row;TableRow opRow = row;var startIndex = UseExcelIndex ? 1 : 0;_idx = startIndex;while (true) {if (_idx > startIndex) { opRow = tpl.CloneNode(true) as TableRow; }bool isMatch = true;foreach (var m in tempMatches) {string value = this.TryEvaluate(m.Value, null);if (value == null) {isMatch = false;break;}foreach (var ph in opRow.Descendants<Paragraph>()) {ReplaceText(ph, m.Key, value);}}if (isMatch==false) {//當數據為空時,清空數據if (_idx == startIndex) {foreach (var ph in opRow.Descendants<Paragraph>()) {ph.RemoveAllChildren();}}break;}if (_idx > startIndex) { table.InsertAfter(opRow, lastRow); }lastRow = opRow;_idx++;}}}}}

案例上手:

后臺代碼:

// 獲取數據var helper = SqlHelperFactory.OpenSqliteFile("test.db");.......var dt = helper.ExecuteDataTable("select * from Introduction");var tableTests = helper.Select<TableTest>("select * from TableTest");ToolGood.OutputWord.WordTemplate openXmlTemplate = new ToolGood.OutputWord.WordTemplate();// 加載數據openXmlTemplate.SetData(dt);openXmlTemplate.SetListData("list", JsonConvert.SerializeObject(tableTests));// 生成模板 一openXmlTemplate.BuildTemplate("test.docx", "openxml_2.docx");// 生成模板 二var bs = openXmlTemplate.BuildTemplate("test.docx");File.WriteAllBytes("openxml_1.docx", bs);

Word模板:

?

Word生成后:

?后記:

WordTemplate 類,主要實現了三個功能:

??? 1、自定義替換word中的文字標簽,當標簽不存在,則設置為空字符串;

??? 2、可以有word中定義公式,替換所對應的值;

??? 3、在表格插入多行數據,當數據為0時清空單元格。

通過上面三個功能,WordTemplate 類將代碼中的word生成方法分離出來。

系統后臺需要配置 存儲過程與word模板信息,就可以將word生成與系統更新完成分離開了。

系統后臺可以配置公式,則公式修改不需要更新word模板。

注:一般業務人員是看得懂四則運算的,部分財務人員更是了解Excel公式,可以減少開發協助時間。

?

完整代碼:https://github.com/toolgood/ToolGood.OutputWord

該組件已上傳到Nuget:Install-Package ToolGood.OutputWord

Excel公式參考:https://github.com/toolgood/ToolGood.Algorithm

總結

以上是生活随笔為你收集整理的简单快速导出word文档的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。