Commit 71e27d87 authored by tr's avatar tr

1、Excel导出封装

parent e0ad8dea
......@@ -13,6 +13,8 @@ using NPOI.SS.Util;
using RtfPipe;
//
using System.Data.SqlClient;
using System.Windows.Forms;
using System.Diagnostics;
namespace FunnyPick
{
......@@ -22,10 +24,7 @@ namespace FunnyPick
public class ExcelExportHelper
{
#region 私有变量
/// <summary>
/// 模板路径
/// </summary>
string _mouldFilePath = "";
/// <summary>
/// Excel文件对象
/// </summary>
......@@ -34,6 +33,10 @@ namespace FunnyPick
/// 全局数据集
/// </summary>
DataSet _ds = null;
/// <summary>
/// 模板路径
/// </summary>
string _mouldFilePath = "";
#endregion
......@@ -41,31 +44,24 @@ namespace FunnyPick
/// <summary>
/// 导出文件(绝对)路径
/// </summary>
public string ExportFileName { get; } = string.Empty;
public object FileService { get; private set; }
public string ExportFileName { get; set; } = string.Empty;
/// <summary>
/// 自定义表名列表
/// <para>如果设置该列表,则只取列表中的表数据,否则取所有数据</para>
/// </summary>
public List<string> CustomTableList = null;
/// <summary>
/// 构造函数
/// <para>配合UnifyExport()使用</para>
/// </summary>
/// <param name="excelMouldName">模板名称</param>
/// <param name="xmlName">(数据源)xml配置文件</param>
/// <param name="keyID">单据主键</param>
public ExcelExportHelper(string excelMouldName, string xmlName, int keyID)
{
//赋值全局数据源
this._ds = this.GetExportExcelDataSource(xmlName, keyID);
//模板临时文件及Excel对象初始化
this._mouldFilePath = Path.Combine(Utils.StartupPath, "Template", excelMouldName);
this.ExportFileName = Path.Combine(Utils.StartupPath, "temp", $"Excel导出临时文件-{DateTime.Now.ToString("yyyMMdd-hhmmss")}.xls");
this._Workbook = this.GetWorkbook(this._mouldFilePath);
}
public ExcelExportHelper() { }
/// <summary>
/// 导出(单sheet页)
/// </summary>
/// <returns></returns>
public bool Export()
private bool Export()
{
try
{
......@@ -91,7 +87,7 @@ namespace FunnyPick
/// 导出(多sheet页)
/// </summary>
/// <returns></returns>
public bool ExportSheets(string tableName, string groupCol)
private bool ExportSheets(string tableName, string groupCol)
{
try
{
......@@ -134,7 +130,7 @@ namespace FunnyPick
/// <param name="groupCol"></param>
/// <param name="Reclassify"></param>
/// <returns></returns>
public bool ExportSOP(string tableName, string groupCol, bool landscape = false)
private bool ExportSOP(string tableName, string groupCol)
{
try
{
......@@ -162,14 +158,66 @@ namespace FunnyPick
}
}
//删除模板文件
this._Workbook.RemoveSheetAt(1);
this._Workbook.RemoveSheetAt(1);
//横向打印
for (int i = 0; i < this._Workbook.NumberOfSheets; i++)
this._Workbook.RemoveSheetAt(1); //删除:工艺单汇总-模板
this._Workbook.RemoveSheetAt(1); //删除:工艺明细-模板
//保存Excel文件
this.SaveExcel(this.ExportFileName);
}
catch (Exception ex)
{
Utils.ShowMessage($"导出失败:{ex.Message}");
return false;
}
finally
{
GC.Collect();
}
return true;
}
/// <summary>
/// 导出(款式SOP维护-工艺单)
/// </summary>
/// <param name="tableName"></param>
/// <param name="groupCol"></param>
/// <param name="Reclassify"></param>
/// <returns></returns>
private bool ExportSOP1(string tableName, string groupCol, string numCol)
{
try
{
//获取分组
var groupList = Utils.GetColumnList(this._ds.Tables[tableName], groupCol);
//按照分组创建新的sheet页, 并复制sheet0的模板格式
var sheet0 = this._Workbook?.GetSheetAt(0) as HSSFSheet; //封页
var sheet1 = this._Workbook?.GetSheetAt(1) as HSSFSheet; //工艺单汇总-模板
var sheet2 = this._Workbook?.GetSheetAt(2) as HSSFSheet; //工艺明细-模板
DataTable dt = this._ds.Tables[tableName];
this.FillSheet(sheet0);
for (int i = 0; i < groupList.Count; i++)
{
var sheet_i = this._Workbook.GetSheetAt(i) as HSSFSheet;
sheet_i.PrintSetup.Landscape = landscape;
var sheetNew = this._Workbook.CreateSheet($"{groupList[i]}流程") as HSSFSheet;
this.CopySheet(sheet1, sheetNew);
//填充汇总数据
dt.DefaultView.RowFilter = $"{groupCol} = '{groupList[i]}'";
this.FillSheet(sheetNew);
//根据页码渲染明细
var SheetNoList = GetSheetNoFromDefaultView(dt.DefaultView, numCol);
SheetNoList.Sort(); //排序
foreach (var n in SheetNoList)
{
dt.DefaultView.RowFilter = $"{groupCol} = '{groupList[i]}' and ISNULL({numCol},'')='{n}'";
if (dt.DefaultView.Count == 0)
continue;
var IEName = dt.DefaultView[0]["IEName"].ToString();
var sheetNewIE = this._Workbook.CreateSheet($"{groupList[i]}{n}-{IEName.Replace(@"/", "_")}") as HSSFSheet;
this.CopySheet(sheet2, sheetNewIE);
this.FillSheet(sheetNewIE);
}
}
//删除模板文件
this._Workbook.RemoveSheetAt(1); //删除:工艺单汇总-模板
this._Workbook.RemoveSheetAt(1); //删除:工艺明细-模板
//保存Excel文件
this.SaveExcel(this.ExportFileName);
}
......@@ -185,6 +233,28 @@ namespace FunnyPick
return true;
}
/// <summary>
/// 获取页号集合
/// </summary>
/// <param name="defaultView"></param>
/// <returns></returns>
private List<string> GetSheetNoFromDefaultView(DataView defaultView, string numCol)
{
var result = new List<string>();
if (Utils.IsContainsColumn(defaultView, numCol))
{
foreach (DataRowView r in defaultView)
{
var n = r[numCol].ToString();
if (result.Contains(n))
continue;
//
result.Add(n);
}
}
return result;
}
/// <summary>
/// 复制Sheet页
/// </summary>
......@@ -214,6 +284,8 @@ namespace FunnyPick
{
to.AddMergedRegion(from.GetMergedRegion(i));
}
//复制打印设置
this.CopyPrintSetup(from, to);
}
/// <summary>
/// 复制行(内容、列宽、类型与格式)
......@@ -239,17 +311,54 @@ namespace FunnyPick
targetCell.SetCellType(sourceCell.CellType); //复制单元格类型
}
}
/// <summary>
/// 复制打印设置
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
public void CopyPrintSetup(HSSFSheet from, HSSFSheet to)
{
//打印设置复制
IPrintSetup s_printSetup = from.PrintSetup;
IPrintSetup t_printSetup = to.PrintSetup;
t_printSetup.Copies = s_printSetup.Copies;
t_printSetup.Draft = s_printSetup.Draft;
t_printSetup.FitHeight = s_printSetup.FitHeight;
t_printSetup.FitWidth = s_printSetup.FitWidth;
t_printSetup.FooterMargin = s_printSetup.FooterMargin;
t_printSetup.HeaderMargin = s_printSetup.HeaderMargin;
t_printSetup.HResolution = s_printSetup.HResolution;
t_printSetup.Landscape = s_printSetup.Landscape;
t_printSetup.LeftToRight = s_printSetup.LeftToRight;
t_printSetup.NoColor = s_printSetup.NoColor;
t_printSetup.NoOrientation = s_printSetup.NoOrientation;
t_printSetup.Notes = s_printSetup.Notes;
t_printSetup.PageStart = s_printSetup.PageStart;
t_printSetup.PaperSize = s_printSetup.PaperSize;
t_printSetup.Scale = s_printSetup.Scale;
t_printSetup.UsePage = s_printSetup.UsePage;
t_printSetup.ValidSettings = s_printSetup.ValidSettings;
t_printSetup.VResolution = s_printSetup.VResolution;
//边距复制
to.SetMargin(MarginType.TopMargin, from.GetMargin(MarginType.TopMargin));
to.SetMargin(MarginType.RightMargin, from.GetMargin(MarginType.RightMargin));
to.SetMargin(MarginType.BottomMargin, from.GetMargin(MarginType.BottomMargin));
to.SetMargin(MarginType.LeftMargin, from.GetMargin(MarginType.LeftMargin));
}
/// <summary>
/// 填充Sheet模板
/// </summary>
/// <param name="sheet"></param>
/// <param name="rowIndex"></param>
private void FillSheet(HSSFSheet sheet)
{
/*模板导出规则:
* 1、每行只能是一种类型,要么全部是基本信息,要么全是明细(如果混合,则按第一个绑定信息类型界定此行类型)
* 基本信息绑定格式:[表名.字段名]
* 明细信息绑定格式:(表名.字段名)
* 合并信息绑定格式:{表名.字段名}
* 二维码绑定格式:<表名.字段名>
* 2、图片必须以Image或Picture结尾,区分大小写
*/
......@@ -270,8 +379,10 @@ namespace FunnyPick
string bind = cell.StringCellValue;
bool IsBaseInfo = bind.StartsWith("[") && bind.EndsWith("]");
bool IsDetailInfo = bind.StartsWith("(") && bind.EndsWith(")");
string[] strArray = bind.Trim(new char[] { '[', ']', '(', ')' }).Split('.'); //去除括号,并分割表名与列名
if ((IsBaseInfo || IsDetailInfo) && strArray.Length == 2)
bool IsMerge = bind.StartsWith("{") && bind.EndsWith("}");
bool IsQRCode = bind.StartsWith("<") && bind.EndsWith(">");
string[] strArray = bind.Trim(new char[] { '[', ']', '(', ')', '{', '}', '<', '>' }).Split('.'); //去除括号,并分割表名与列名
if ((IsBaseInfo || IsDetailInfo || IsMerge || IsQRCode) && strArray.Length == 2)
{
bindList.Add(new BindInfo
{
......@@ -279,16 +390,20 @@ namespace FunnyPick
ColumnName = strArray[1],
CellColumn = col,
IsDetailInfo = IsDetailInfo,
IsMerge = IsMerge,
IsQRCode = IsQRCode,
});
}
}
//填充数据
int scale = 1; //不管是否绑定,行都要加1,所以这里默认为1
if (bindList.Count > 0)
if (bindList.Count > 0 && this._ds.Tables[bindList[0].TableName] != null)
{
EmptyCounts = 0; //命中数据行时,清空计数
int ViewCount = this._ds.Tables[bindList[0].TableName].DefaultView.Count; //数据总行数
if (bindList[0].IsDetailInfo && ViewCount > 1) //明细,并且多余1行时,采用插行渲染
bool IsDetailInfo = bindList[0].IsDetailInfo; //同行的第1个绑定,决定该行类型
string tableName = bindList[0].TableName;
int ViewCount = this._ds.Tables.Contains(tableName) ? this._ds.Tables[tableName].DefaultView.Count : 0; //数据总行数(表不存在数据源中,当0处理)
if (IsDetailInfo && ViewCount > 1) //明细,并且多余1行时,采用插行渲染
{
int InsertRowCount = ViewCount - 1; //本来就有一行,所以:插入行数 = 数据行数 - 1
this.MyInsertRow(sheet, currentRow, InsertRowCount, (HSSFRow)sheet.GetRow(currentRow - 1)); //从当前行开始,复制当前行上一行的行格式
......@@ -296,7 +411,6 @@ namespace FunnyPick
{
foreach (BindInfo bind in bindList)
{
//这里减1,是为了消除scale默认值
ICell cell = sheet?.GetRow(currentRow + i).GetCell(bind.CellColumn);
this.LoadData(cell, bind, RowIndex: i);
}
......@@ -304,14 +418,16 @@ namespace FunnyPick
//赋值标量:插入行数大于1时,标量 = 插入行数
if (InsertRowCount > scale)
scale = InsertRowCount;
}
else
{
foreach (BindInfo bind in bindList)
{
ICell cell = sheet?.GetRow(currentRow).GetCell(bind.CellColumn);
this.LoadData(cell, bind);
if (bind.IsMerge)
this.LoadDataMerge(cell, bind);
else
this.LoadData(cell, bind);
}
}
}
......@@ -327,12 +443,19 @@ namespace FunnyPick
}
/// <summary>
/// 填充Sheet模板:单行渲染
/// </summary>
/// <param name="sheet"></param>
/// <param name="rowIndex"></param>
private void FillSheet(HSSFSheet sheet, int rowIndex)
{
/*模板导出规则:
* 1、每行只能是一种类型,要么全部是基本信息,要么全是明细(如果混合,则按第一个绑定信息类型界定此行类型)
* 基本信息绑定格式:[表名.字段名]
* 明细信息绑定格式:(表名.字段名)
* 合并信息绑定格式:{表名.字段名}
* 二维码绑定格式:<表名.字段名>
* 2、图片必须以Image或Picture结尾,区分大小写
*/
......@@ -353,8 +476,10 @@ namespace FunnyPick
string bind = cell.StringCellValue;
bool IsBaseInfo = bind.StartsWith("[") && bind.EndsWith("]");
bool IsDetailInfo = bind.StartsWith("(") && bind.EndsWith(")");
string[] strArray = bind.Trim(new char[] { '[', ']', '(', ')' }).Split('.'); //去除括号,并分割表名与列名
if ((IsBaseInfo || IsDetailInfo) && strArray.Length == 2)
bool IsMerge = bind.StartsWith("{") && bind.EndsWith("}");
bool IsQRCode = bind.StartsWith("<") && bind.EndsWith(">");
string[] strArray = bind.Trim(new char[] { '[', ']', '(', ')', '{', '}', '<', '>' }).Split('.'); //去除括号,并分割表名与列名
if ((IsBaseInfo || IsDetailInfo || IsMerge || IsQRCode) && strArray.Length == 2)
{
bindList.Add(new BindInfo
{
......@@ -362,6 +487,8 @@ namespace FunnyPick
ColumnName = strArray[1],
CellColumn = col,
IsDetailInfo = IsDetailInfo,
IsMerge = IsMerge,
IsQRCode = IsQRCode,
});
}
}
......@@ -404,11 +531,11 @@ namespace FunnyPick
bool IsImage = ColumnName.EndsWith("Image") || ColumnName.EndsWith("Picture"); //图片列规范:必须以Image或Picture结尾,区分大小写
if (this._ds.Tables.Contains(TableName) && this._ds.Tables[TableName].DefaultView.Count > 0 && this._ds.Tables[TableName].Columns.Contains(ColumnName))
{
cell.SetCellValue(""); //先清除单元格信息
var dr = this._ds.Tables[TableName].DefaultView[RowIndex];
if (IsImage)
{
cell.SetCellValue(""); //先清除单元格信息
#region 加载图片
try
......@@ -417,20 +544,35 @@ namespace FunnyPick
if (dr[ColumnName] != DBNull.Value && dr[ColumnName].GetType().Name.ToLower().Equals("byte[]"))
{
byte[] bytes = dr[ColumnName] as byte[];
if (bytes.Length == 0)
return;
bool IsSegment = bytes.Length.Equals(32); //是否进行图片分离
var img = IsSegment ? Utils.FetchImageSingle(bytes) : bytes;
int CellRow = cell.RowIndex;
int CellCol = cell.ColumnIndex;
var range = cell.IsMergedCell ? this.GetMergedRange(cell) : new CellRangeAddress(CellRow, CellRow, CellCol, CellCol);
var range = this.GetMergedRange(cell);
this.LoadImage(cell, img, range);
}
}
catch (Exception ex)
{
throw new Exception($"加载图片出错:{ex.Message}");
//Utils.Write($"加载图片出错:{ex.Message}");
}
#endregion
}
else if (bind.IsQRCode)
{
#region 二维码处理
var str = dr[ColumnName].ToString();
if (string.IsNullOrEmpty(str))
return;
var bytes = Utils.GenQRCode(dr[ColumnName].ToString());
if (bytes == null || bytes.Length == 0)
return;
var range = this.GetMergedRange(cell);
this.LoadImage(cell, bytes, range);
#endregion
}
else
{
cell.SetCellValue(dr[ColumnName].ToString());
......@@ -457,7 +599,7 @@ namespace FunnyPick
//取出图片宽高
var ms = new MemoryStream(bytes);
Bitmap bmp = bmp = new Bitmap(ms);
Bitmap bmp = new Bitmap(ms);
var height = bmp.Height;
var width = bmp.Width;
// 图片位置空间的高度
......@@ -465,7 +607,7 @@ namespace FunnyPick
for (int i = 1; i <= range.LastRow - range.FirstRow + 1; i++)
AreaHeight += sheet.GetRow(i).Height;
//根据宽高比计算出宽度
int AreaWidth = width * AreaHeight / height;
int AreaWidth = AreaHeight * width / height;
//获取图片索引
int picIndex = sheet.Workbook.AddPicture(bytes, PictureType.JPEG);
//创建图片锚点
......@@ -483,22 +625,155 @@ namespace FunnyPick
};
//重新绘制图片
IDrawing patriarch = null;
try
IDrawing patriarch = this.GetSheetDrawingObject(sheet);
var pict = (HSSFPicture)patriarch.CreatePicture(anchor, picIndex);
bmp.Dispose();
ms.Close();
}
#region 多行合并渲染
/// <summary>
/// 加载单元格数据
/// </summary>
/// <param name="cell"></param>
/// <param name="bind"></param>
/// <param name="RowIndex"></param>
private void LoadDataMerge(ICell cell, BindInfo bind)
{
if (cell == null)
return;
string TableName = bind.TableName; //表名
string ColumnName = bind.ColumnName; //列名
bool IsImage = ColumnName.EndsWith("Image") || ColumnName.EndsWith("Picture"); //图片列规范:必须以Image或Picture结尾,区分大小写
if (this._ds.Tables.Contains(TableName) && this._ds.Tables[TableName].DefaultView.Count > 0 && this._ds.Tables[TableName].Columns.Contains(ColumnName))
{
// 第一次取不到居然会有异常抛出...
patriarch = sheet.DrawingPatriarch;
if (IsImage)
{
cell.SetCellValue(""); //先清除单元格信息
#region 加载图片
try
{
//获取单元格范围
int CellRow = cell.RowIndex;
int CellCol = cell.ColumnIndex;
var range = cell.IsMergedCell ? this.GetMergedRange(cell) : new CellRangeAddress(CellRow, CellRow, CellCol, CellCol);
//循环当前视图,填充图片
int index = 0;
foreach (DataRowView r in this._ds.Tables[TableName].DefaultView)
{
//图片列非空,并且是byte[]类型才处理
if (r[ColumnName] != DBNull.Value && r[ColumnName].GetType().Name.ToLower().Equals("byte[]"))
{
byte[] bytes = r[ColumnName] as byte[];
if (bytes.Length == 0)
continue;
bool IsSegment = bytes.Length.Equals(32); //是否进行图片分离
var img = IsSegment ? Utils.FetchImageSingle(bytes) : bytes;
//加载图片
this.LoadImageMerge(cell, img, range, index);
index++;
}
}
}
catch (Exception ex)
{
//Utils.Write($"加载图片出错:{ex.Message}");
}
#endregion
}
else
{
var mergeStrList = new List<string>();
foreach (DataRowView r in this._ds.Tables[TableName].DefaultView)
{
mergeStrList.Add(r[ColumnName].ToString().Trim());
}
cell.SetCellValue(string.Join("\r\n", mergeStrList));
}
}
catch
else
{
patriarch = sheet.CreateDrawingPatriarch();
//不存在的列名或表名,直接清空
cell.SetCellValue("");
}
}
/// <summary>
/// 绘制图片
/// </summary>
/// <param name="cell"></param>
/// <param name="bytes"></param>
/// <param name="range"></param>
private void LoadImageMerge(ICell cell, byte[] bytes, CellRangeAddress range, int index)
{
if (bytes.Length == 0)
return;
//获取单元格对应的Sheet页
HSSFSheet sheet = cell.Sheet as HSSFSheet;
int Columnlndex = range.FirstColumn + index;
//取出图片宽高
var ms = new MemoryStream(bytes);
Bitmap bmp = bmp = new Bitmap(ms);
var height = bmp.Height;
var width = bmp.Width;
// 图片位置空间的高度
short AreaHeight = 0;
for (int i = 0; i <= range.LastRow - range.FirstRow; i++)
AreaHeight += sheet.GetRow(i).Height;
//根据列宽填充(在一个合并区域里,每个图片占一列)
int AreaWidth = sheet.GetColumnWidth(Columnlndex);
//获取图片索引
int picIndex = sheet.Workbook.AddPicture(bytes, PictureType.JPEG);
//创建图片锚点
var anchor = new HSSFClientAnchor()
{
Col1 = Columnlndex, //起始列
Col2 = Columnlndex, //结束列
Row1 = range.FirstRow, //起始行
Row2 = range.LastRow, //结束行
//说明:此处的x和y可看做相对于左上边框的偏移位置;左上(dx1,dy1)和右下(dx2,dy2)分别可看做矩形框的对角顶点,图片在此矩形内绘制
Dx1 = 0,
Dy1 = 0, //起始单元格中的x,y坐标
Dx2 = AreaWidth,
Dy2 = AreaHeight //结束单元格中的x, y坐标
};
//重新绘制图片
IDrawing patriarch = this.GetSheetDrawingObject(sheet);
var pict = (HSSFPicture)patriarch.CreatePicture(anchor, picIndex);
bmp.Dispose();
ms.Close();
}
#endregion
/// <summary>
/// 获取绘图对象
/// </summary>
/// <param name="sheet"></param>
/// <returns></returns>
private IDrawing GetSheetDrawingObject(HSSFSheet sheet)
{
IDrawing result = null;
try
{
// 第一次取不到居然会有异常抛出...
result = sheet.DrawingPatriarch;
}
catch
{
result = sheet.CreateDrawingPatriarch();
}
return result;
}
/// <summary>
/// 获取合并列区域
/// </summary>
......@@ -509,21 +784,83 @@ namespace FunnyPick
{
CellRangeAddress result = null;
//获取单元格对应的Sheet页
HSSFSheet sheet = cell.Sheet as HSSFSheet;
for (int i = 0; i < sheet.NumMergedRegions; i++)
if (cell.IsMergedCell)
{
CellRangeAddress range = sheet.GetMergedRegion(i);
if ((cell.RowIndex >= range.FirstRow && cell.RowIndex <= range.LastRow)
&& (cell.ColumnIndex >= range.FirstColumn && cell.ColumnIndex <= range.LastColumn))
HSSFSheet sheet = cell.Sheet as HSSFSheet;
for (int i = 0; i < sheet.NumMergedRegions; i++)
{
result = range;
break;
CellRangeAddress range = sheet.GetMergedRegion(i);
if ((cell.RowIndex >= range.FirstRow && cell.RowIndex <= range.LastRow)
&& (cell.ColumnIndex >= range.FirstColumn && cell.ColumnIndex <= range.LastColumn))
{
result = range;
break;
}
}
}
else
{
int row = cell.RowIndex;
int col = cell.ColumnIndex;
result = new CellRangeAddress(row, row, col, col);
}
return result;
}
/// <summary>
/// 批量插入复制行
/// </summary>
/// <param name="sheet">Excel页</param>
/// <param name="RowStart">起始行</param>
/// <param name="RowCount">(插入)总行数</param>
/// <param name="sourceRow">(复制)源</param>
private void MyInsertRow(HSSFSheet sheet, int RowStart, int RowCount, HSSFRow sourceRow)
{
//插入行数为0时,直接返回
if (RowCount <= 0)
return;
#region 批量移动行
sheet.ShiftRows(
RowStart, //--开始行
sheet.LastRowNum, //--结束行
RowCount, //--插入行数
true, //是否复制行高
false, //是否重置行高
true //是否移动批注
);
#endregion
#region 创建行,并复制rowSource行的格式
//首行特殊处理,并且后续各行行高要与首行一致
HSSFRow firstTargetRow = sheet.GetRow(RowStart) as HSSFRow;
//复制单元格样式(除了第一行,第一行的行已经存在,这里处理未创建的行)
for (int i = RowStart; i < RowStart + RowCount; i++)
{
HSSFRow targetRow = null;
HSSFCell sourceCell = null;
HSSFCell targetCell = null;
targetRow = i == RowStart ? firstTargetRow : sheet.CreateRow(i) as HSSFRow;
targetRow.Height = firstTargetRow.Height; //复制与首行保持一致
for (int m = sourceRow.FirstCellNum; m < sourceRow.LastCellNum; m++)
{
sourceCell = sourceRow.GetCell(m) as HSSFCell;
if (sourceCell == null) continue;
targetCell = targetRow.CreateCell(m) as HSSFCell;
targetCell.CellStyle = sourceCell.CellStyle;
targetCell.SetCellType(sourceCell.CellType);
//合并列处理
if (sourceCell.IsMergedCell)
{
var range = this.GetMergedRange(sourceCell);
//这里的行跨度都是1,所以主要处理列
var newRange = new CellRangeAddress(i, i, range.FirstColumn, range.LastColumn);
sheet.AddMergedRegion(newRange);
}
}
}
            #endregion
        }
/// <summary>
/// 获取Excel文件对象
......@@ -559,6 +896,7 @@ namespace FunnyPick
fs.Close();
}
}
/// <summary>
/// 设置单元格的值
/// </summary>
......@@ -605,65 +943,12 @@ namespace FunnyPick
/// <param name="rowend"></param>
/// <param name="colstart"></param>
/// <param name="colend"></param>
public void SetMergedCell(HSSFSheet sheet, int rowstart, int rowend, int colstart, int colend)
private void SetMergedCell(HSSFSheet sheet, int rowstart, int rowend, int colstart, int colend)
{
CellRangeAddress range = new CellRangeAddress(rowstart, rowend, colstart, colend);
sheet.AddMergedRegion(range);
}
/// <summary>
/// 批量插入复制行
/// </summary>
/// <param name="sheet">Excel页</param>
/// <param name="RowStart">起始行</param>
/// <param name="RowCount">(插入)总行数</param>
/// <param name="sourceRow">(复制)源</param>
private void MyInsertRow(HSSFSheet sheet, int RowStart, int RowCount, HSSFRow sourceRow)
{
//插入行数为0时,直接返回
if (RowCount <= 0)
return;
#region 批量移动行
sheet.ShiftRows(
RowStart, //--开始行
sheet.LastRowNum, //--结束行
RowCount, //--插入行数
true, //是否复制行高
false, //是否重置行高
true //是否移动批注
);
#endregion
#region 创建行,并复制rowSource行的格式
//首行特殊处理,并且后续各行行高要与首行一致
HSSFRow firstTargetRow = sheet.GetRow(RowStart) as HSSFRow;
//复制单元格样式(除了第一行,第一行的行已经存在,这里处理未创建的行)
for (int i = RowStart; i < RowStart + RowCount; i++)
{
HSSFRow targetRow = null;
HSSFCell sourceCell = null;
HSSFCell targetCell = null;
targetRow = i == RowStart ? firstTargetRow : sheet.CreateRow(i) as HSSFRow;
targetRow.Height = firstTargetRow.Height; //复制与首行保持一致
for (int m = sourceRow.FirstCellNum; m < sourceRow.LastCellNum; m++)
{
sourceCell = sourceRow.GetCell(m) as HSSFCell;
if (sourceCell == null) continue;
targetCell = targetRow.CreateCell(m) as HSSFCell;
targetCell.CellStyle = sourceCell.CellStyle;
targetCell.SetCellType(sourceCell.CellType);
//合并列处理
if (sourceCell.IsMergedCell)
{
var range = this.GetMergedRange(sourceCell);
//这里的行跨度都是1,所以主要处理列
var newRange = new CellRangeAddress(i, i, range.FirstColumn, range.LastColumn);
sheet.AddMergedRegion(newRange);
}
}
}
            #endregion
        }
#region 数据源处理
......@@ -672,7 +957,7 @@ namespace FunnyPick
/// </summary>
/// <param name="xmlName"></param>
/// <returns></returns>
public DataSet GetExportExcelDataSource(string xmlName, int keyID)
public DataSet GetDataFromXml(string xmlName, int keyID)
{
DataSet result = new DataSet();
......@@ -682,30 +967,34 @@ namespace FunnyPick
{
xmlName += ".xml";
}
var xmlPath = $@"{Utils.XmlPath}\{xmlName}";
var xmlPath = $@"{Utils.ConfigFilePath}\{xmlName}";
if (!File.Exists(xmlPath))
return result;
XDocument xml = XDocument.Load(xmlPath);
var tables = xml.Root.Element("ExcelExport").Elements("Table");
using (MySqlCommand cmd = new MySqlCommand())
using (SqlCommand cmd = new SqlCommand())
{
foreach (XElement e in tables)
{
//Name为必须字段,其他都为非必须字段
string Name = e.Attribute("Name").Value;
//做过滤,部分多数据源,可提高查询速度
if (CustomTableList != null && CustomTableList.Count > 0 && !CustomTableList.Contains(Name))
continue;
//
string IsConvert = e.Attribute("IsConvert")?.Value;
string NameCol = e.Attribute("NameCol")?.Value;
string ValueCol = e.Attribute("ValueCol")?.Value;
string SumValueCol = e.Attribute("SumValueCol")?.Value;
string IsRtf = e.Attribute("IsRtf")?.Value;
string sql = e.Value;
DataTable dt = cmd.Execute(sql, CommandType.Text, new System.Data.SqlClient.SqlParameter("ID", keyID));
dt = this.ConvertTable(dt, Name, IsConvert, NameCol, ValueCol, SumValueCol, IsRtf);
if (!result.Tables.Contains(Name))
{
result.Tables.Add(dt);
}
//DataTable dt = cmd.e(sql, CommandType.Text, new System.Data.SqlClient.SqlParameter("ID", keyID)).Tables[0];
//dt = this.ConvertTable(dt, Name, IsConvert, NameCol, ValueCol, SumValueCol, IsRtf);
//if (!result.Tables.Contains(Name))
//{
// result.Tables.Add(dt);
//}
}
}
......@@ -755,7 +1044,7 @@ namespace FunnyPick
drNew[col] = val;
if (IsNeedSum)
{
sum += Utils.UConvert.ToDecimal(val, 2);
sum += decimal.Round(Convert.ToDecimal(val), 2);
}
}
if (IsNeedSum)
......@@ -800,17 +1089,216 @@ namespace FunnyPick
}
#endregion
#region 统一导出接口
}
/// <summary>
/// 统一导出接口(SOP定制)
/// </summary>
/// <param name="lnk">标签控件</param>
/// <param name="xml">xml配置文件</param>
/// <param name="keyID">主键ID</param>
/// <param name="m">导出模型</param>
/// <param name="exportPath">导出路径</param>
public void UnifyExportSOP(string xml, int keyID, ExportModel m, string mName)
{
if (m == null)
{
Utils.ThrowErrorMessage("导出Excel必须设置导出模型!");
return;
}
//全局变量初始化
this._ds = this.GetDataFromXml(xml, keyID);
this._mouldFilePath = $@"{Utils.ExcelTemplateFilePath}\{m.UniquePrefix}{mName}";
this._Workbook = this.GetWorkbook(this._mouldFilePath);
this.ExportFileName = this.GetExportFileFullName();
//如未显式指定导出模型,则从配置文件读取
bool IsSuccess = this.ExportSOP1(m.GroupTableName, m.GroupColumnName, m.ReclassifyColumnName);
if (IsSuccess)
{
Process.Start(this.ExportFileName);
}
}
/// <summary>
/// 统一导出接口
/// </summary>
/// <param name="lnk">标签控件</param>
/// <param name="xml">xml配置文件</param>
/// <param name="keyID">主键ID</param>
/// <param name="m">导出模型</param>
/// <param name="exportPath">导出路径</param>
public void UnifyExport(LinkLabel lnk, string xml, int keyID, ExportModel m = null, string exportPath = "")
{
//赋值全局数据源
this._ds = this.GetDataFromXml(xml, keyID);
//模板导出路径
this.ExportFileName = this.GetExportFileFullName(exportPath);
//如未显式指定导出模型,则从配置文件读取
if (m == null)
m = Utils.GetExportModel(xml);
//显示上下文菜单
this.ShowContextMenu(lnk, m);
}
/// <summary>
/// 统一导出接口
/// </summary>
/// <param name="lnk">标签控件</param>
/// <param name="ds">数据源</param>
/// <param name="m">导出模型</param>
public void UnifyExport(LinkLabel lnk, DataSet ds, ExportModel m)
{
if (ds == null || m == null)
{
Utils.ThrowErrorMessage("导出Excel必须赋值数据源和导出模型!");
return;
}
//赋值全局数据源
this._ds = ds;
//模板导出路径
this.ExportFileName = this.GetExportFileFullName();
//显示上下文菜单
this.ShowContextMenu(lnk, m);
}
/// <summary>
/// 获取导出文件全名
/// </summary>
/// <param name="exportPath"></param>
/// <returns></returns>
private string GetExportFileFullName(string exportPath = "")
{
string path = "";
string tmpName = $"Excel导出临时文件-{DateTime.Now.ToString("yyyMMdd-hhmmss")}.xls";
if (string.IsNullOrEmpty(exportPath) || !Directory.Exists(exportPath))
{
path = Path.Combine(Utils.StartupPath, "temp", tmpName);
}
else
{
path = Path.Combine(exportPath, tmpName);
}
return path;
}
/// <summary>
/// 显示上下文菜单
/// </summary>
/// <param name="lnk"></param>
/// <param name="UniquePrefix"></param>
private void ShowContextMenu(LinkLabel lnk, ExportModel m)
{
//根据模板目录创建下拉菜单项(显示时去掉统一前缀,简洁显示)
DirectoryInfo directory = new DirectoryInfo(Utils.ExcelTemplateFilePath);
var items = directory.GetFiles().Where(f => f.Name.IndexOf(m.UniquePrefix) == 0)
.Select(p => new MenuItem(p.Name.Replace(m.UniquePrefix, ""), new EventHandler(MenuItem_Click)) { Tag = m })
.ToArray();
if (items.Length == 0)
{
Utils.ShowMessage("请先配置模板信息!");
return;
}
//创建菜单并显示
ContextMenu cm = new ContextMenu();
cm.MenuItems.AddRange(items);
cm.Show(lnk, new Point(0, lnk.Height));
}
/// <summary>
/// 模板项点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MenuItem_Click(object sender, EventArgs e)
{
var item = sender as MenuItem;
if (string.IsNullOrEmpty(item.Text))
return;
try
{
Utils.ShowWaitDialog("正在导出Excel文件, 请稍候!", "Exporting excel file, please wait!");
//打开模板
var m = item.Tag as ExportModel;
this._mouldFilePath = $@"{Utils.ExcelTemplateFilePath}\{m.UniquePrefix}{item.Text}";
this._Workbook = this.GetWorkbook(this._mouldFilePath);
//根据设置导出
bool IsSuccess = false;
if (!string.IsNullOrEmpty(m.GroupTableName.Trim()) && !string.IsNullOrEmpty(m.GroupColumnName.Trim()))
{
IsSuccess = this.ExportSheets(m.GroupTableName, m.GroupColumnName);
}
else
{
IsSuccess = this.Export();
}
//成功时,打开文件
if (IsSuccess)
{
Process.Start(this.ExportFileName);
}
}
finally
{
Utils.CloseWaitDialog();
}
}
#endregion
}
/// <summary>
/// 绑定信息
/// </summary>
class BindInfo
{
/// <summary>
/// 表名
/// </summary>
public string TableName { get; set; }
/// <summary>
/// 列名
/// </summary>
public string ColumnName { get; set; }
/// <summary>
/// 单元格的列序号
/// </summary>
public int CellColumn { get; set; }
/// <summary>
/// 是否明细信息
/// </summary>
public bool IsDetailInfo { get; set; }
/// <summary>
/// 是否合并
/// </summary>
public bool IsMerge { get; set; }
/// <summary>
/// 是否二维码
/// </summary>
public bool IsQRCode { get; set; }
}
/// <summary>
/// Excel导出模型
/// </summary>
public class ExportModel
{
/// <summary>
/// 模板唯一前缀(通过该前缀,初始化上下文菜单选项)
/// </summary>
public string UniquePrefix { get; set; }
/// <summary>
/// 分组的表名(不需要分组是给空)
/// </summary>
public string GroupTableName { get; set; }
/// <summary>
/// 分组的列名(不需要分组是给空)
/// </summary>
public string GroupColumnName { get; set; }
/// <summary>
/// 二级分类列名(用于多行合并到一个Sheet页)
/// </summary>
public string ReclassifyColumnName { get; set; }
}
}
......@@ -11,4 +11,10 @@
<PackageReference Include="System.Data.SqlClient" Version="4.8.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.Windows.Forms.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
......@@ -15,6 +15,10 @@ namespace FunnyPick
public static string XmlPath => $@"{AppContext.BaseDirectory}\xml";
public static string ExcelTemplateFilePath => AppContext.BaseDirectory;
public static object ConfigFilePath { get; internal set; }
public static class UConvert
{
public static bool ToBool(object o)
......@@ -131,5 +135,35 @@ namespace FunnyPick
{
throw new NotImplementedException();
}
internal static byte[] GenQRCode(string v)
{
throw new NotImplementedException();
}
internal static void CloseWaitDialog()
{
throw new NotImplementedException();
}
internal static ExportModel GetExportModel(string xml)
{
throw new NotImplementedException();
}
internal static bool IsContainsColumn(DataView defaultView, string numCol)
{
throw new NotImplementedException();
}
internal static void ShowWaitDialog(string v1, string v2)
{
throw new NotImplementedException();
}
internal static void ThrowErrorMessage(string v)
{
throw new NotImplementedException();
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment