ExportExcelService.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. using NPOI.HSSF.UserModel;
  2. using NPOI.OpenXmlFormats.Dml.Chart;
  3. using NPOI.SS.UserModel;
  4. using NPOI.SS.UserModel.Charts;
  5. using NPOI.SS.Util;
  6. using NPOI.XSSF.UserModel;
  7. namespace YBEE.EQM.Application;
  8. /// <summary>
  9. /// EXCEL导出服务
  10. /// </summary>
  11. public class ExportExcelService : IExportExcelService, ISingleton
  12. {
  13. /// <summary>
  14. /// 导出 Excel 文件
  15. /// </summary>
  16. /// <typeparam name="T"></typeparam>
  17. /// <param name="input"></param>
  18. /// <returns></returns>
  19. public byte[] ExportExcel<T>(ExportExcelDto<T> input)
  20. {
  21. IWorkbook wb;
  22. if (input.IsXlsx)
  23. {
  24. wb = new XSSFWorkbook();
  25. }
  26. else
  27. {
  28. wb = new HSSFWorkbook();
  29. }
  30. ISheet sheet = wb.CreateSheet(input.SheetName);
  31. sheet.DisplayGridlines = false;
  32. input.SetSheet?.Invoke(sheet);
  33. var cellStyle = GetCellStyle(wb, input.FontSize, input.TitleFontSize);
  34. // 行索引号
  35. int rowNum = 0;
  36. int freezeRowCount = 0;
  37. #region 标题
  38. if (!(string.IsNullOrEmpty(input.Title?.Trim()) || string.IsNullOrWhiteSpace(input.Title?.Trim())))
  39. {
  40. IRow titleRow = sheet.CreateRow(rowNum++);
  41. titleRow.Height = input.TitleHeight ?? ExportExcelCellStyle.DefaultTitleHeight;
  42. AddCell(input.Title.Trim(), titleRow, 0, cellStyle.TitleStyle, sheet);
  43. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, input.Columns.Count - 1));
  44. freezeRowCount++;
  45. }
  46. #endregion
  47. #region 摘要
  48. if (input.BuildSummary == null)
  49. {
  50. string summary = "";
  51. if (input.IncludeExportTime)
  52. {
  53. summary = $"导出时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss} ";
  54. };
  55. if (!(string.IsNullOrEmpty(input.Summary) || string.IsNullOrWhiteSpace(input.Summary)))
  56. {
  57. summary = $"{summary}{input.Summary}";
  58. }
  59. if (summary != "")
  60. {
  61. IRow summaryRow = sheet.CreateRow(rowNum++);
  62. if (!input.NotSetRowHeight)
  63. {
  64. summaryRow.Height = ExportExcelCellStyle.DefaultRowHeight;
  65. }
  66. AddCell(summary, summaryRow, 0, cellStyle.SummaryStyle, sheet);
  67. sheet.AddMergedRegion(new CellRangeAddress(rowNum - 1, rowNum - 1, 0, input.Columns.Count - 1));
  68. freezeRowCount++;
  69. }
  70. }
  71. else
  72. {
  73. int rc = input.BuildSummary(sheet, cellStyle, rowNum);
  74. freezeRowCount += rc;
  75. rowNum += rc;
  76. }
  77. #endregion
  78. #region 列头
  79. IRow headerRow = sheet.CreateRow(rowNum++);
  80. if (!input.NotSetRowHeight)
  81. {
  82. headerRow.Height = input.HeaderHeight;
  83. }
  84. for (int i = 0; i < input.Columns.Count; i++)
  85. {
  86. var col = input.Columns[i];
  87. AddCell(col.Name, headerRow, i, cellStyle.ColumnHeaderStyle, sheet, col.Width);
  88. }
  89. sheet.CreateFreezePane(0, ++freezeRowCount);
  90. #endregion
  91. #region 数据
  92. foreach (var item in input.Items)
  93. {
  94. IRow row = sheet.CreateRow(rowNum++);
  95. if (!input.NotSetRowHeight)
  96. {
  97. row.Height = input.RowHeight ?? ExportExcelCellStyle.DefaultRowHeight;
  98. }
  99. for (int i = 0; i < input.Columns.Count; i++)
  100. {
  101. var col = input.Columns[i];
  102. ICellStyle cstyle = col.WrapText ? cellStyle.CenterWrapCellStyle : cellStyle.CenterCellStyle;
  103. switch (col.Align)
  104. {
  105. case ExportExcelCellAlign.LEFT:
  106. cstyle = col.WrapText ? cellStyle.LeftWrapCellStyle : cellStyle.LeftCellStyle;
  107. break;
  108. case ExportExcelCellAlign.RIGHT:
  109. cstyle = col.WrapText ? cellStyle.RightWrapCellStyle : cellStyle.RightCellStyle;
  110. break;
  111. }
  112. AddCell(col.GetCellValue(item), row, i, cstyle, sheet, col.Width);
  113. }
  114. }
  115. #endregion
  116. MemoryStream ms = new();
  117. wb.Write(ms, false);
  118. ms.Flush();
  119. return ms.ToArray();
  120. }
  121. #region EXCEL公共方法
  122. /// <summary>
  123. /// 获取单元格表头和单元格样式
  124. /// </summary>
  125. /// <param name="wb"></param>
  126. /// <param name="fontSize">正文字体大小</param>
  127. /// <param name="titleFontSize">标题字体大小</param>
  128. /// <returns></returns>
  129. public ExportExcelCellStyle GetCellStyle(IWorkbook wb, double fontSize = 10, double titleFontSize = 16)
  130. {
  131. IDataFormat dataformat = wb.CreateDataFormat();
  132. ExportExcelCellStyle cellStyle = new()
  133. {
  134. DataFormat = dataformat,
  135. TitleStyle = wb.CreateCellStyle(),
  136. SummaryStyle = wb.CreateCellStyle(),
  137. ColumnHeaderStyle = wb.CreateCellStyle(),
  138. ColumnFillHeaderStyle = wb.CreateCellStyle(),
  139. CenterCellStyle = wb.CreateCellStyle(),
  140. LeftCellStyle = wb.CreateCellStyle(),
  141. RightCellStyle = wb.CreateCellStyle(),
  142. CenterWrapCellStyle = wb.CreateCellStyle(),
  143. LeftWrapCellStyle = wb.CreateCellStyle(),
  144. RightWrapCellStyle = wb.CreateCellStyle(),
  145. FillCellStyle = wb.CreateCellStyle(),
  146. PercentCellStyleP2 = wb.CreateCellStyle(),
  147. NumberCellStyleP2 = wb.CreateCellStyle(),
  148. NumberCellStyleP4 = wb.CreateCellStyle(),
  149. };
  150. // 标题样式
  151. cellStyle.TitleStyle.WrapText = true;
  152. cellStyle.TitleStyle.Alignment = HorizontalAlignment.Center;
  153. cellStyle.TitleStyle.VerticalAlignment = VerticalAlignment.Center;
  154. IFont titleFont = wb.CreateFont();
  155. titleFont.IsBold = true;
  156. titleFont.FontName = cellStyle.TitleFontName;
  157. titleFont.FontHeightInPoints = titleFontSize;
  158. cellStyle.TitleStyle.SetFont(titleFont);
  159. // 摘要样式
  160. cellStyle.SummaryStyle.Alignment = HorizontalAlignment.Left;
  161. cellStyle.SummaryStyle.VerticalAlignment = VerticalAlignment.Center;
  162. IFont summaryFont = wb.CreateFont();
  163. summaryFont.FontName = cellStyle.FontName;
  164. summaryFont.FontHeightInPoints = fontSize;
  165. cellStyle.SummaryStyle.SetFont(summaryFont);
  166. // 列头样式
  167. cellStyle.ColumnHeaderStyle.Alignment = HorizontalAlignment.Center;
  168. cellStyle.ColumnHeaderStyle.VerticalAlignment = VerticalAlignment.Center;
  169. cellStyle.ColumnHeaderStyle.BorderTop = BorderStyle.Thin;
  170. cellStyle.ColumnHeaderStyle.BorderLeft = BorderStyle.Thin;
  171. cellStyle.ColumnHeaderStyle.BorderRight = BorderStyle.Thin;
  172. cellStyle.ColumnHeaderStyle.BorderBottom = BorderStyle.Thin;
  173. cellStyle.ColumnHeaderStyle.WrapText = true;
  174. IFont headerFont = wb.CreateFont();
  175. headerFont.IsBold = true;
  176. headerFont.FontName = cellStyle.TitleFontName;
  177. headerFont.FontHeightInPoints = fontSize;
  178. cellStyle.ColumnHeaderStyle.SetFont(headerFont);
  179. // 带背景列头样式
  180. cellStyle.ColumnFillHeaderStyle.CloneStyleFrom(cellStyle.ColumnHeaderStyle);
  181. if (wb.SpreadsheetVersion.DefaultExtension == "xls")
  182. {
  183. HSSFPalette palette = ((HSSFWorkbook)wb).GetCustomPalette();
  184. palette.SetColorAtIndex(48, 237, 237, 237);
  185. cellStyle.ColumnFillHeaderStyle.FillForegroundColor = palette.FindColor(237, 237, 237).Indexed;
  186. }
  187. else
  188. {
  189. cellStyle.ColumnFillHeaderStyle.FillForegroundColor = 0;
  190. cellStyle.ColumnFillHeaderStyle.FillPattern = FillPattern.SolidForeground;
  191. ((XSSFColor)cellStyle.ColumnFillHeaderStyle.FillForegroundColorColor).SetRgb(new byte[] { 237, 237, 237 });
  192. }
  193. #region 内容样式
  194. // 居中样式
  195. cellStyle.CenterCellStyle.Alignment = HorizontalAlignment.Center;
  196. cellStyle.CenterCellStyle.VerticalAlignment = VerticalAlignment.Center;
  197. cellStyle.CenterCellStyle.BorderTop = BorderStyle.Thin;
  198. cellStyle.CenterCellStyle.BorderLeft = BorderStyle.Thin;
  199. cellStyle.CenterCellStyle.BorderRight = BorderStyle.Thin;
  200. cellStyle.CenterCellStyle.BorderBottom = BorderStyle.Thin;
  201. IFont cellFont = wb.CreateFont();
  202. cellFont.FontName = cellStyle.FontName;
  203. cellFont.FontHeightInPoints = fontSize;
  204. cellStyle.CenterCellStyle.SetFont(cellFont);
  205. // 居左样式
  206. cellStyle.LeftCellStyle.CloneStyleFrom(cellStyle.CenterCellStyle);
  207. cellStyle.LeftCellStyle.Alignment = HorizontalAlignment.Left;
  208. // 居右样式
  209. cellStyle.RightCellStyle.CloneStyleFrom(cellStyle.CenterCellStyle);
  210. cellStyle.RightCellStyle.Alignment = HorizontalAlignment.Right;
  211. #endregion
  212. #region 自动换行
  213. // 居中样式(自动换行)
  214. cellStyle.CenterWrapCellStyle.CloneStyleFrom(cellStyle.CenterCellStyle);
  215. cellStyle.CenterWrapCellStyle.WrapText = true;
  216. // 居左样式(自动换行)
  217. cellStyle.LeftWrapCellStyle.CloneStyleFrom(cellStyle.LeftCellStyle);
  218. cellStyle.LeftWrapCellStyle.WrapText = true;
  219. // 居右样式(自动换行)
  220. cellStyle.RightWrapCellStyle.CloneStyleFrom(cellStyle.RightCellStyle);
  221. cellStyle.RightWrapCellStyle.WrapText = true;
  222. #endregion
  223. // 背景样式
  224. cellStyle.FillCellStyle.CloneStyleFrom(cellStyle.CenterCellStyle);
  225. if (wb.SpreadsheetVersion.DefaultExtension == "xls")
  226. {
  227. HSSFPalette palette = ((HSSFWorkbook)wb).GetCustomPalette();
  228. palette.SetColorAtIndex(48, 245, 245, 245);
  229. cellStyle.FillCellStyle.FillForegroundColor = palette.FindColor(245, 245, 245).Indexed;
  230. }
  231. else
  232. {
  233. cellStyle.FillCellStyle.FillForegroundColor = 0;
  234. cellStyle.FillCellStyle.FillPattern = FillPattern.SolidForeground;
  235. ((XSSFColor)cellStyle.FillCellStyle.FillForegroundColorColor).SetRgb(new byte[] { 245, 245, 245 });
  236. }
  237. cellStyle.PercentCellStyleP2.CloneStyleFrom(cellStyle.CenterCellStyle);
  238. cellStyle.PercentCellStyleP2.DataFormat = HSSFDataFormat.GetBuiltinFormat("0.00%");
  239. cellStyle.NumberCellStyleP2.CloneStyleFrom(cellStyle.CenterCellStyle);
  240. cellStyle.NumberCellStyleP2.DataFormat = dataformat.GetFormat("0.00");
  241. cellStyle.NumberCellStyleP4.CloneStyleFrom(cellStyle.CenterCellStyle);
  242. cellStyle.NumberCellStyleP4.DataFormat = dataformat.GetFormat("0.0000");
  243. return cellStyle;
  244. }
  245. /// <summary>
  246. /// 添加单元格
  247. /// </summary>
  248. /// <param name="value"></param>
  249. /// <param name="row"></param>
  250. /// <param name="columnIndex"></param>
  251. /// <param name="cellStyle"></param>
  252. /// <param name="sheet"></param>
  253. /// <param name="width"></param>
  254. /// <param name="zeroToBlank">0转为空白</param>
  255. /// <param name="cellType"></param>
  256. public ICell AddCell(object value, IRow row, int columnIndex, ICellStyle cellStyle, ISheet sheet = null, int? width = null, bool? zeroToBlank = false, CellType? cellType = null)
  257. {
  258. bool isSetBlank = false;
  259. if (zeroToBlank == true)
  260. {
  261. if (double.TryParse(value.ToString(), out double v))
  262. {
  263. isSetBlank = v == 0;
  264. }
  265. }
  266. ICell cell = row.CreateCell(columnIndex);
  267. cell.CellStyle = cellStyle;
  268. if (value != null && isSetBlank == false)
  269. {
  270. var valueTypeName = value.GetType().Name;
  271. if (cellType.HasValue)
  272. {
  273. cell.SetCellType(cellType.Value);
  274. }
  275. if (cellType.HasValue && cellType == CellType.Formula)
  276. {
  277. cell.CellFormula = value.ToString();
  278. }
  279. else if (valueTypeName.Contains("Int") || valueTypeName == "Double" || valueTypeName == "Decimal")
  280. {
  281. cell.SetCellValue(Convert.ToDouble(value));
  282. }
  283. else if (valueTypeName == "String")
  284. {
  285. cell.SetCellValue(value.ToString());
  286. }
  287. else if (valueTypeName == "Boolean")
  288. {
  289. cell.SetCellValue((bool)value);
  290. }
  291. }
  292. if (sheet != null && width.HasValue)
  293. {
  294. sheet.SetColumnWidth(columnIndex, width.Value * 256);
  295. }
  296. return cell;
  297. }
  298. #endregion
  299. #region 图表
  300. /// <summary>
  301. /// 导出柱状图
  302. /// </summary>
  303. /// <param name="sheet"></param>
  304. /// <param name="drawing"></param>
  305. /// <param name="anchor"></param>
  306. /// <param name="startDataRow"></param>
  307. /// <param name="endDataRow"></param>
  308. /// <param name="columnIndex"></param>
  309. /// <param name="title"></param>
  310. /// <param name="serieTitle"></param>
  311. /// <param name="catalogTitle"></param>
  312. /// <param name="valueTile"></param>
  313. public void CreateBarChart(ISheet sheet, IDrawing drawing, IClientAnchor anchor, int startDataRow, int endDataRow, int columnIndex, string title = null, string serieTitle = null, string catalogTitle = null, string valueTile = null)
  314. {
  315. XSSFChart chart = (XSSFChart)drawing.CreateChart(anchor);
  316. if (!string.IsNullOrEmpty(title))
  317. {
  318. chart.SetTitle(title);
  319. chart.GetCTChart().title.tx.rich.p[0].pPr = new NPOI.OpenXmlFormats.Dml.CT_TextParagraphProperties
  320. {
  321. defRPr = new NPOI.OpenXmlFormats.Dml.CT_TextCharacterProperties() { sz = 1400 }
  322. };
  323. }
  324. IBarChartData<string, double> barChartData = chart.ChartDataFactory.CreateBarChartData<string, double>();
  325. IChartLegend legend = chart.GetOrCreateLegend();
  326. legend.Position = LegendPosition.TopRight;
  327. legend.IsOverlay = true;
  328. IChartAxis bottomAxis = chart.ChartAxisFactory.CreateCategoryAxis(AxisPosition.Bottom);
  329. bottomAxis.MajorTickMark = AxisTickMark.None;
  330. IValueAxis leftAxis = chart.ChartAxisFactory.CreateValueAxis(AxisPosition.Left);
  331. leftAxis.Crosses = AxisCrosses.AutoZero;
  332. leftAxis.SetCrossBetween(AxisCrossBetween.Between);
  333. IChartDataSource<string> categoryAxis = DataSources.FromStringCellRange(sheet, new CellRangeAddress(startDataRow, endDataRow, 0, 0));
  334. IChartDataSource<double> valueAxis = DataSources.FromNumericCellRange(sheet, new CellRangeAddress(startDataRow, endDataRow, columnIndex, columnIndex));
  335. var serie = barChartData.AddSeries(categoryAxis, valueAxis);
  336. if (!string.IsNullOrEmpty(serieTitle))
  337. {
  338. serie.SetTitle(serieTitle);
  339. }
  340. chart.Plot(barChartData, bottomAxis, leftAxis);
  341. var plotArea = chart.GetCTChart().plotArea;
  342. plotArea.catAx[0].txPr = new CT_TextBody();
  343. plotArea.catAx[0].txPr.AddNewP().pPr = new NPOI.OpenXmlFormats.Dml.CT_TextParagraphProperties()
  344. {
  345. defRPr = new NPOI.OpenXmlFormats.Dml.CT_TextCharacterProperties() { sz = 900 }
  346. };
  347. plotArea.catAx[0].majorTickMark = new CT_TickMark() { val = ST_TickMark.@out };
  348. plotArea.valAx[0].txPr = new CT_TextBody();
  349. plotArea.valAx[0].txPr.AddNewP().pPr = new NPOI.OpenXmlFormats.Dml.CT_TextParagraphProperties()
  350. {
  351. defRPr = new NPOI.OpenXmlFormats.Dml.CT_TextCharacterProperties() { sz = 900 }
  352. };
  353. plotArea.valAx[0].majorTickMark = new CT_TickMark() { val = ST_TickMark.@out };
  354. var barChart = plotArea.barChart.First();
  355. barChart.barDir = new CT_BarDir { val = ST_BarDir.col };
  356. if (!string.IsNullOrEmpty(catalogTitle))
  357. {
  358. var aTitle = new CT_Title
  359. {
  360. tx = new CT_Tx()
  361. };
  362. aTitle.tx.rich = new CT_TextBody();
  363. aTitle.tx.rich.AddNewP().AddNewR().t = catalogTitle;
  364. plotArea.valAx[0].title = aTitle;
  365. }
  366. if (!string.IsNullOrEmpty(valueTile))
  367. {
  368. var aTitle = new CT_Title
  369. {
  370. tx = new CT_Tx()
  371. };
  372. aTitle.tx.rich = new CT_TextBody();
  373. aTitle.tx.rich.AddNewP().AddNewR().t = valueTile;
  374. plotArea.catAx[0].title = aTitle;
  375. }
  376. }
  377. #endregion
  378. }