Skip to content

Commit 517887c

Browse files
committed
重构HTML、PDF导出等逻辑,并修改IExporterByTemplate
添加收据导出的单元测试示例
1 parent 6ffb7ca commit 517887c

File tree

20 files changed

+563
-89
lines changed

20 files changed

+563
-89
lines changed
File renamed without changes.

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
![](./res/导出Pdf.png "导出Pdf")
3232
- 导入支持重复验证;
3333
![](./res/重复错误.png "重复错误.png")
34+
- 支持单个数据模板导出,常用于导出收据、凭据等业务
3435

3536
### 相关官方Nuget包
3637

@@ -75,6 +76,14 @@
7576

7677
### 更新历史
7778

79+
#### 2019.10.12
80+
- 【重构】重构HTML、PDF导出等逻辑,并修改IExporterByTemplate为:
81+
- Task<string> ExportListByTemplate<T>(IList<T> dataItems, string htmlTemplate = null) where T : class;
82+
- Task<string> ExportByTemplate<T>(T data, string htmlTemplate = null) where T : class;
83+
- 【示例】添加收据导出的单元测试示例
84+
85+
86+
7887
#### 2019.9.28
7988
- 【导出】修改默认的导出HTML、Word、Pdf模板
8089
- 【导入】添加截断行的单元测试,以测试中间空格和结尾空格
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Text;
6+
7+
namespace Magicodes.ExporterAndImporter.Core.Extension
8+
{
9+
/// <summary>
10+
///
11+
/// </summary>
12+
public static class ReadManifestExtensions
13+
{
14+
/// <summary>
15+
/// 读取嵌入式资源
16+
/// </summary>
17+
/// <param name="assembly"></param>
18+
/// <param name="embeddedFileName"></param>
19+
/// <returns></returns>
20+
public static string ReadManifestString(this Assembly assembly, string embeddedFileName)
21+
{
22+
var resourceName = assembly.GetManifestResourceNames().First(s =>
23+
s.EndsWith(embeddedFileName, StringComparison.CurrentCultureIgnoreCase));
24+
25+
using (var stream = assembly.GetManifestResourceStream(resourceName))
26+
{
27+
if (stream == null)
28+
{
29+
throw new InvalidOperationException($"无法加载嵌入式资源,请确认路径是否正确:{embeddedFileName}。");
30+
}
31+
32+
using (var reader = new StreamReader(stream, Encoding.UTF8))
33+
{
34+
return reader.ReadToEnd();
35+
}
36+
}
37+
}
38+
}
39+
}

src/Magicodes.ExporterAndImporter.Core/Extension/TypeExtensions.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,13 @@ public static IEnumerable<Type> GetTypesWith<TAttribute>(this Assembly assembly,
142142
where TAttribute : Attribute
143143
{
144144
var attrType = typeof(TAttribute);
145-
foreach (var type in assembly.GetTypes())
146-
if (type.GetCustomAttributes(attrType, true).Length > 0)
147-
yield return type;
145+
foreach (var type in assembly.GetTypes().Where(type => type.GetCustomAttributes(attrType, true).Length > 0))
146+
yield return type;
148147
}
149148

150149
/// <summary>
151150
/// 获取枚举定义列表
152151
/// </summary>
153-
/// <typeparam name="TAttribute">枚举类型</typeparam>
154152
/// <returns>返回枚举列表元组(名称、值、描述)</returns>
155153
public static List<Tuple<string, int, string>> GetEnumDefinitionList(this Type type)
156154
{
@@ -193,10 +191,9 @@ public static IDictionary<string, int> GetEnumDisplayNames(this Type type)
193191
IDictionary<string, int> displayNames = new Dictionary<string, int>();
194192
foreach (var name in names)
195193
{
196-
var displayAttribute = type.GetField(name)
194+
if (type.GetField(name)
197195
.GetCustomAttributes(typeof(DisplayAttribute), false)
198-
.SingleOrDefault() as DisplayAttribute;
199-
if (displayAttribute != null)
196+
.SingleOrDefault() is DisplayAttribute displayAttribute)
200197
{
201198
var value = (int) Enum.Parse(type, name);
202199
displayNames.Add(displayAttribute.Name, value);
@@ -220,6 +217,11 @@ public static Dictionary<string, List<Tuple<string, int, string>>> GetClassEnumD
220217
}
221218

222219

220+
/// <summary>
221+
///
222+
/// </summary>
223+
/// <param name="type"></param>
224+
/// <returns></returns>
223225
public static string GetCSharpTypeName(this Type type)
224226
{
225227
var sb = new StringBuilder();

src/Magicodes.ExporterAndImporter.Core/IExporterByTemplate.cs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
using System.Collections.Generic;
1818
using System.Threading.Tasks;
19+
using Magicodes.ExporterAndImporter.Core.Models;
1920

2021
namespace Magicodes.ExporterAndImporter.Core
2122
{
@@ -24,12 +25,43 @@ namespace Magicodes.ExporterAndImporter.Core
2425
public interface IExporterByTemplate
2526
{
2627
/// <summary>
27-
/// 根据模板导出
28+
/// 根据模板导出列表
2829
/// </summary>
2930
/// <typeparam name="T"></typeparam>
3031
/// <param name="dataItems"></param>
3132
/// <param name="htmlTemplate">Html模板内容</param>
3233
/// <returns></returns>
33-
Task<string> ExportByTemplate<T>(IList<T> dataItems, string htmlTemplate = null) where T : class;
34+
Task<string> ExportListByTemplate<T>(IList<T> dataItems, string htmlTemplate = null) where T : class;
35+
36+
/// <summary>
37+
/// 根据模板导出
38+
/// </summary>
39+
/// <typeparam name="T"></typeparam>
40+
/// <param name="data"></param>
41+
/// <param name="htmlTemplate">Html模板内容</param>
42+
/// <returns></returns>
43+
Task<string> ExportByTemplate<T>(T data, string htmlTemplate = null) where T : class;
44+
45+
/// <summary>
46+
/// 根据模板导出列表
47+
/// </summary>
48+
/// <typeparam name="T"></typeparam>
49+
/// <param name="fileName"></param>
50+
/// <param name="dataItems"></param>
51+
/// <param name="htmlTemplate"></param>
52+
/// <returns></returns>
53+
Task<TemplateFileInfo> ExportListByTemplate<T>(string fileName, IList<T> dataItems,
54+
string htmlTemplate = null) where T : class;
55+
56+
/// <summary>
57+
/// 根据模板导出
58+
/// </summary>
59+
/// <typeparam name="T"></typeparam>
60+
/// <param name="fileName"></param>
61+
/// <param name="data"></param>
62+
/// <param name="htmlTemplate"></param>
63+
/// <returns></returns>
64+
Task<TemplateFileInfo> ExportByTemplate<T>(string fileName, T data,
65+
string htmlTemplate) where T : class;
3466
}
3567
}

src/Magicodes.ExporterAndImporter.Core/Magicodes.ExporterAndImporter.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Import Project="..\..\common.props"></Import>
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
5-
<Version>1.3.1</Version>
5+
<Version>1.3.2</Version>
66
<PackageId>Magicodes.IE.Core</PackageId>
77
<Authors>雪雁</Authors>
88
<Company>湖南心莱信息科技有限公司</Company>

src/Magicodes.ExporterAndImporter.Core/Models/ExportDocumentInfo.cs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ namespace Magicodes.ExporterAndImporter.Core.Models
2525
/// <summary>
2626
/// </summary>
2727
/// <typeparam name="TData"></typeparam>
28-
public class ExportDocumentInfo<TData> where TData : class
28+
public class ExportDocumentInfoOfListData<TData> where TData : class
2929
{
3030
/// <summary>
3131
/// </summary>
32-
public ExportDocumentInfo(IList<TData> datas)
32+
public ExportDocumentInfoOfListData(IList<TData> datas)
3333
{
3434
Headers = new List<ExporterHeaderAttribute>();
3535
Datas = datas;
@@ -70,4 +70,46 @@ public DataTable ToDataTable()
7070
return Datas.ToDataTable();
7171
}
7272
}
73+
74+
/// <summary>
75+
/// </summary>
76+
/// <typeparam name="TData"></typeparam>
77+
public class ExportDocumentInfo<TData> where TData : class
78+
{
79+
/// <summary>
80+
/// </summary>
81+
public ExportDocumentInfo(TData data)
82+
{
83+
Headers = new List<ExporterHeaderAttribute>();
84+
Data = data;
85+
Title = typeof(TData).GetAttribute<ExporterAttribute>()?.Name ?? typeof(TData).Name;
86+
87+
foreach (var propertyInfo in typeof(TData).GetProperties())
88+
{
89+
var exporterHeader = propertyInfo.PropertyType.GetAttribute<ExporterHeaderAttribute>() ??
90+
new ExporterHeaderAttribute
91+
{
92+
DisplayName = propertyInfo.GetDisplayName() ?? propertyInfo.Name
93+
};
94+
Headers.Add(exporterHeader);
95+
}
96+
}
97+
98+
99+
/// <summary>
100+
/// 文档标题
101+
/// </summary>
102+
public string Title { get; set; }
103+
104+
/// <summary>
105+
/// 头部信息
106+
/// </summary>
107+
public IList<ExporterHeaderAttribute> Headers { get; set; }
108+
109+
/// <summary>
110+
/// 数据
111+
/// </summary>
112+
public TData Data { get; set; }
113+
}
114+
73115
}

src/Magicodes.ExporterAndImporter.Html/HtmlExporter.cs

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@
1414
//
1515
// ======================================================================
1616

17-
using System;
18-
using System.Collections.Generic;
19-
using System.IO;
20-
using System.Linq;
21-
using System.Reflection;
22-
using System.Threading.Tasks;
2317
using Magicodes.ExporterAndImporter.Core;
18+
using Magicodes.ExporterAndImporter.Core.Extension;
2419
using Magicodes.ExporterAndImporter.Core.Models;
2520
using RazorEngine;
2621
using RazorEngine.Templating;
22+
using System.Collections.Generic;
23+
using System.IO;
24+
using System.Threading.Tasks;
2725
using Encoding = System.Text.Encoding;
2826

2927
namespace Magicodes.ExporterAndImporter.Html
@@ -40,16 +38,44 @@ public class HtmlExporter : IExporterByTemplate
4038
/// <param name="dataItems"></param>
4139
/// <param name="htmlTemplate">Html模板内容</param>
4240
/// <returns></returns>
43-
public async Task<string> ExportByTemplate<T>(IList<T> dataItems, string htmlTemplate = null) where T : class
41+
public Task<string> ExportListByTemplate<T>(IList<T> dataItems, string htmlTemplate = null) where T : class
42+
{
43+
var result = RunCompileTpl(new ExportDocumentInfoOfListData<T>(dataItems), htmlTemplate);
44+
return Task.FromResult(result);
45+
}
46+
47+
/// <summary>
48+
/// 根据模板导出
49+
/// </summary>
50+
/// <typeparam name="T"></typeparam>
51+
/// <param name="data"></param>
52+
/// <param name="htmlTemplate">Html模板内容</param>
53+
/// <returns></returns>
54+
public Task<string> ExportByTemplate<T>(T data, string htmlTemplate) where T : class
4455
{
45-
var htmlTpl = string.IsNullOrWhiteSpace(htmlTemplate)
46-
? ReadManifestData<HtmlExporter>("default.cshtml")
56+
var result = RunCompileTpl(new ExportDocumentInfo<T>(data), htmlTemplate);
57+
return Task.FromResult(result);
58+
}
59+
60+
/// <summary>
61+
/// 获取HTML模板
62+
/// </summary>
63+
/// <param name="htmlTemplate"></param>
64+
/// <returns></returns>
65+
protected string GetHtmlTemplate(string htmlTemplate = null) => string.IsNullOrWhiteSpace(htmlTemplate)
66+
? typeof(HtmlExporter).Assembly.ReadManifestString("default.cshtml")
4767
: htmlTemplate;
4868

49-
var exportDocumentInfo = new ExportDocumentInfo<T>(dataItems);
50-
var result =
51-
Engine.Razor.RunCompile(htmlTpl, htmlTpl.GetHashCode().ToString(), null, exportDocumentInfo);
52-
return result;
69+
/// <summary>
70+
/// 编译和运行模板
71+
/// </summary>
72+
/// <param name="model"></param>
73+
/// <param name="htmlTemplate"></param>
74+
/// <returns></returns>
75+
protected string RunCompileTpl(object model, string htmlTemplate = null)
76+
{
77+
var htmlTpl = GetHtmlTemplate(htmlTemplate);
78+
return Engine.Razor.RunCompile(htmlTpl, htmlTpl.GetHashCode().ToString(), null, model);
5379
}
5480

5581
/// <summary>
@@ -59,29 +85,24 @@ public async Task<string> ExportByTemplate<T>(IList<T> dataItems, string htmlTem
5985
/// <param name="dataItems"></param>
6086
/// <param name="htmlTemplate"></param>
6187
/// <returns></returns>
62-
public async Task<TemplateFileInfo> ExportByTemplate<T>(string fileName, IList<T> dataItems,
88+
public async Task<TemplateFileInfo> ExportListByTemplate<T>(string fileName, IList<T> dataItems,
6389
string htmlTemplate = null) where T : class
6490
{
6591
var file = new TemplateFileInfo(fileName, "text/html");
66-
var result = await ExportByTemplate(dataItems, htmlTemplate);
92+
93+
var result = await ExportListByTemplate(dataItems, htmlTemplate);
6794
File.WriteAllText(fileName, result, Encoding.UTF8);
6895
return file;
6996
}
7097

71-
public static string ReadManifestData<TSource>(string embeddedFileName) where TSource : class
98+
public async Task<TemplateFileInfo> ExportByTemplate<T>(string fileName, T data,
99+
string htmlTemplate) where T : class
72100
{
73-
var assembly = typeof(TSource).GetTypeInfo().Assembly;
74-
var resourceName = assembly.GetManifestResourceNames().First(s =>
75-
s.EndsWith(embeddedFileName, StringComparison.CurrentCultureIgnoreCase));
101+
var file = new TemplateFileInfo(fileName, "text/html");
102+
var result = await ExportByTemplate(data, htmlTemplate);
76103

77-
using (var stream = assembly.GetManifestResourceStream(resourceName))
78-
{
79-
if (stream == null) throw new InvalidOperationException("Could not load manifest resource stream.");
80-
using (var reader = new StreamReader(stream, Encoding.UTF8))
81-
{
82-
return reader.ReadToEnd();
83-
}
84-
}
104+
File.WriteAllText(fileName, result, Encoding.UTF8);
105+
return file;
85106
}
86107
}
87108
}

src/Magicodes.ExporterAndImporter.Html/Magicodes.ExporterAndImporter.Html.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<PackageId>Magicodes.IE.Html</PackageId>
6-
<Version>1.0.1</Version>
6+
<Version>1.0.2</Version>
77
<Authors>雪雁</Authors>
88
<Company>湖南心莱信息科技有限公司</Company>
99
<Product>麦扣</Product>

src/Magicodes.ExporterAndImporter.Pdf/Magicodes.ExporterAndImporter.Pdf.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<PackageId>Magicodes.IE.Pdf</PackageId>
6-
<Version>1.0.1</Version>
6+
<Version>1.0.2</Version>
77
<Authors>雪雁</Authors>
88
<Company>湖南心莱信息科技有限公司</Company>
99
<Product>麦扣</Product>

0 commit comments

Comments
 (0)