原需求:生成的word版、PDF版合同中多行产品最后一列备注内容太多,不同产品有时备注又一样,这样每一列产品都有一个内容很长的备注,导致整个合同的排版不美观,占空间也比较大,如图1。因此用户提出:将不同产品行相邻的备注内容一样的进行合并,效果如图2。
图1
图2(word版)
图3(PDF版)
1.word版合同
原来方法:直接在后台绑定repeater控件(控件放在前台一个div中),点击导出按钮直接将该div转换成字符数组写入HTTP响应输出流,但无法实现图2效果。
解决思路:因为数据是动态绑定的,所以在创建表格某一行时还不知道需要合并几行,而table又不支持向上合并。因此一个可行的方法就是先将合并的行数定义为特殊字符,在将整个表格遍历结束之后再将特殊字符替换掉。
实现方法:将数据绑定改为字符串拼接,在第一次循环时设置rowspan,在第二次之后开始判断备注是否一样,若一样该行就结束,否则需要再加带rowspan的一列。并且在每次循环时记录相同的行数,每次出现不一样的行时就将上次的行数拼接,这样循环结束之后就得到一个类似于 “,3,1” 的字符串,表示表格的前三行备注一样。最后将该字符串转换成数组arr_str,以数组长度进行遍历拼接成的html(带有特殊字符),将特殊字段替换成相应的数组值。代码如下:
//绑定合同产品
protected void BindProduct(int id, int uid)
{
DataTable dt = bllProduct.AgreementProListByAgridUid(id, uid);
string strHtml = "";
string DeliveryDate = "", HomeFee = "", Installation = "", PNote = "";
int count = 1, index = 1;
string strs = "";
int[] array = new int[dt.Rows.Count];
for (int i = 0; i < dt.Rows.Count; i++)
{
strHtml += "<tr>";
strHtml += "<td style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000;\">" + dt.Rows[i]["ProductName"] + "</td>";
strHtml += "<td style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000;\">" + dt.Rows[i]["Brand"] + "</td>";
strHtml += "<td style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000;\">" + dt.Rows[i]["Model"] + "</td>";
strHtml += "<td style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000;\">" + dt.Rows[i]["SellingPrice"] + "</td>";
strHtml += "<td style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000;\">" + dt.Rows[i]["Number"] + "(" + dt.Rows[i]["Unit"] + ")</td>";
strHtml += "<td style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000;\">" + Convert.ToSingle(dt.Rows[i]["SellingPrice"]) * Convert.ToInt32(dt.Rows[i]["Number"]) + "</td>";
if (i == 0)
{
strHtml += "<td rowspan=\"@_0\" style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000; border-right: 1px solid #000;\">" + (dt.Rows[i]["DeliveryDate"] != DBNull.Value ? "货期:" + dt.Rows[i]["DeliveryDate"] + "<br/>" : "") + (dt.Rows[i]["HomeFee"] != DBNull.Value ? "运费:" + (dt.Rows[i]["HomeFee"].ToString() == "1" ? "送货上门,不含任何搬卸及上楼费用" : dt.Rows[i]["HomeFee"].ToString() == "2" ? "物流点自提" : dt.Rows[i]["HomeFee"].ToString() == "3" ? "不含运费" : "送货上门,含卸车上楼费用") + "<br/>" : "") + (dt.Rows[i]["Installation"] != DBNull.Value ? "安调说明:" + (dt.Rows[i]["Installation"].ToString() == "1" ? "上门指导安装调试" : dt.Rows[i]["Installation"].ToString() == "2" ? "出厂前已调试无需安装" : "出厂前已调试,可电话指导安装") + "<br/>" : "") + (dt.Rows[i]["PNote"] != DBNull.Value ? dt.Rows[i]["PNote"] : "") + "</td>";
}
if (i > 0)
{
DeliveryDate = dt.Rows[i - 1]["DeliveryDate"].ToString();
HomeFee = dt.Rows[i - 1]["HomeFee"].ToString();
Installation = dt.Rows[i - 1]["Installation"].ToString();
PNote = dt.Rows[i - 1]["PNote"].ToString();
}
if (DeliveryDate == dt.Rows[i]["DeliveryDate"].ToString() && HomeFee == dt.Rows[i]["HomeFee"].ToString() && Installation == dt.Rows[i]["Installation"].ToString() && PNote == dt.Rows[i]["PNote"].ToString())
{
count++;
}
else if(i > 0)
{
strs += "," + count;
count = 1;
strHtml += "<td rowspan=\"@_" + index + "\" style=\"border-left: 1px solid #000; border-top: 1px solid #000; border-bottom: 1px solid #000; border-right: 1px solid #000;\">" + (dt.Rows[i]["DeliveryDate"] != DBNull.Value ? "货期:" + dt.Rows[i]["DeliveryDate"] + "<br/>" : "") + (dt.Rows[i]["HomeFee"] != DBNull.Value ? "运费:" + (dt.Rows[i]["HomeFee"].ToString() == "1" ? "送货上门,不含任何搬卸及上楼费用" : dt.Rows[i]["HomeFee"].ToString() == "2" ? "物流点自提" : dt.Rows[i]["HomeFee"].ToString() == "3" ? "不含运费" : "送货上门,含卸车上楼费用") + "<br/>" : "") + (dt.Rows[i]["Installation"] != DBNull.Value ? "安调说明:" + (dt.Rows[i]["Installation"].ToString() == "1" ? "上门指导安装调试" : dt.Rows[i]["Installation"].ToString() == "2" ? "出厂前已调试无需安装" : "出厂前已调试,可电话指导安装") + "<br/>" : "") + (dt.Rows[i]["PNote"] != DBNull.Value ? dt.Rows[i]["PNote"] : "") + "</td>";
index++;
}
if (i == dt.Rows.Count - 1)
{
strs += "," + count;
}
strHtml += "</tr>";
}
strs = strs.Length > 0 ? strs.Substring(1) : "";
string[] arr_str = strs.Split(',');
for (int i = 0; i < arr_str.Length; i++)
{
if (strHtml.Contains("@_" + i))
{
strHtml = strHtml.Replace("@_" + i, arr_str[i].ToString());
}
}
this.tbody.InnerHtml = strHtml;
}
2.PDF版合同
难点:与html不同的是,由于生成PDF合同是用组件iTextSharp实现,但这个组件却没有Rowspan方法,只有一个Colspan。
解决思路:转换思维,要实现最后一列的Rowspan的效果,只需要将前面几行几列作为一个table嵌套到外面的table中。
实现方法:需要先确定每次要合并多少行,即html中提到的数组arr_str。以数组的长度作为遍历条件,每次遍历需要创建一次table,在这层循环中再嵌套一个循环,内层循环以外层循环的数组值作为遍历条件,即有几个行相同的就遍历几次,代表外层这个表格有几行。具体代码如下:
PdfPTable t2 = new PdfPTable(7);
float[] widths2 = new float[] { 2f, 1f, 1f, 1f, 1f, 1f, 2f };
t2.SpacingBefore = 10f;
t2.SpacingAfter = 10f;
t2.SetWidths(widths2);
t2.AddCell(new PdfPCell(new Paragraph("产品名称", font10)));
t2.AddCell(new PdfPCell(new Phrase("品牌", font10)));
t2.AddCell(new PdfPCell(new Phrase("规格/型号", font10)));
t2.AddCell(new PdfPCell(new Phrase("单价(元)", font10)));
t2.AddCell(new PdfPCell(new Phrase("数量", font10)));//括号后单位动态获取
t2.AddCell(new PdfPCell(new Phrase("总额(元)", font10)));
t2.AddCell(new PdfPCell(new Phrase("备注", font10)));
DataTable dtP = bllProduct.AgreementProListByAgridUid(model.id, model.UId);
string strs = "";
string DeliveryDate = "", HomeFee = "", Installation = "", PNote = "";
int count = 1;
for (int k = 0; k < dtP.Rows.Count; k++)
{
if (k > 0)
{
DeliveryDate = dtP.Rows[k - 1]["DeliveryDate"].ToString();
HomeFee = dtP.Rows[k - 1]["HomeFee"].ToString();
Installation = dtP.Rows[k - 1]["Installation"].ToString();
PNote = dtP.Rows[k - 1]["PNote"].ToString();
}
if (DeliveryDate == dtP.Rows[k]["DeliveryDate"].ToString() && HomeFee == dtP.Rows[k]["HomeFee"].ToString() && Installation == dtP.Rows[k]["Installation"].ToString() && PNote == dtP.Rows[k]["PNote"].ToString())
{
count++;
}
else if (k > 0)
{
strs += "," + count;
count = 1;
}
if (k == dtP.Rows.Count - 1)
{
strs += "," + count;
}
}
strs = strs.Length > 0 ? strs.Substring(1) : "";
string[] arr_str = strs.Split(',');
totalAmount = 0;
int m = 0;
for (int j = 0; j < arr_str.Length; j++)
{
PdfPTable pro_dt = new PdfPTable(6);
float[] pro_width = new float[] { 2f, 1f, 1f, 1f, 1f, 1f };
pro_dt.SetWidths(pro_width);
float minHeight = 50f;
if (int.Parse(arr_str[j]) > 2)
{
minHeight = 30f;
}
for (int n = 0; n < int.Parse(arr_str[j]); n++)
{
var ProductName = new PdfPCell(new Phrase(dtP.Rows[m]["ProductName"].ToString(), font10));
ProductName.VerticalAlignment = Element.ALIGN_MIDDLE;
ProductName.MinimumHeight = minHeight;
pro_dt.AddCell(ProductName);
var Brand = new PdfPCell(new Phrase(dtP.Rows[m]["Brand"].ToString(), font10));
Brand.VerticalAlignment = Element.ALIGN_MIDDLE;
Brand.MinimumHeight = minHeight;
pro_dt.AddCell(Brand);
var Model = new PdfPCell(new Phrase(dtP.Rows[m]["Model"].ToString(), font10));
Model.VerticalAlignment = Element.ALIGN_MIDDLE;
Model.MinimumHeight = minHeight;
pro_dt.AddCell(Model);
var SellingPrice = new PdfPCell(new Phrase(dtP.Rows[m]["SellingPrice"].ToString(), font10));
SellingPrice.VerticalAlignment = Element.ALIGN_MIDDLE;
SellingPrice.MinimumHeight = minHeight;
pro_dt.AddCell(SellingPrice);
var Number_Unit = new PdfPCell(new Phrase(dtP.Rows[m]["Number"].ToString() + "(" + dtP.Rows[m]["Unit"].ToString() + ")", font10));
Number_Unit.VerticalAlignment = Element.ALIGN_MIDDLE;
Number_Unit.MinimumHeight = minHeight;
pro_dt.AddCell(Number_Unit);
float totalPer = Convert.ToSingle(dtP.Rows[m]["SellingPrice"]) * Convert.ToSingle(dtP.Rows[m]["Number"]);
totalAmount += totalPer;
var cell_total = new PdfPCell(new Phrase(totalPer.ToString(), font10));
cell_total.VerticalAlignment = Element.ALIGN_MIDDLE;
cell_total.MinimumHeight = minHeight;
pro_dt.AddCell(cell_total);
m++;
}
PdfPCell pro_cell = new PdfPCell(pro_dt);
pro_cell.Colspan = 6;
pro_cell.Padding = 0f;
t2.AddCell(pro_cell);
#region
PdfPCell remarkcell = new PdfPCell();
remarkcell.AddElement(new Paragraph((dtP.Rows[m-1]["DeliveryDate"] != DBNull.Value ? "货期:" + dtP.Rows[m-1]["DeliveryDate"] : ""), font10));
string homefee = "";
if (dtP.Rows[m-1]["HomeFee"] != DBNull.Value)
{
switch (dtP.Rows[m-1]["HomeFee"].ToString())
{
case "1":
homefee = "送货上门,不含任何搬卸及上楼费用";
break;
case "2":
homefee = "物流点自提";
break;
case "3":
homefee = "不含运费";
break;
case "4":
homefee = "送货上门,含卸车上楼费用";
break;
}
}
remarkcell.AddElement(new Paragraph(dtP.Rows[m-1]["HomeFee"] != DBNull.Value ? "运费:" + homefee : "", font10));
string installation = "";
if (dtP.Rows[m-1]["Installation"] != DBNull.Value)
{
switch (dtP.Rows[m-1]["Installation"].ToString())
{
case "1":
installation = "上门指导安装调试";
break;
case "2":
installation = "出厂前已调试无需安装";
break;
case "3":
installation = "出厂前已调试,可电话指导安装";
break;
}
}
remarkcell.AddElement(new Paragraph(dtP.Rows[m-1]["Installation"] != DBNull.Value ? "安调说明:" + installation : "", font10));
remarkcell.AddElement(new Paragraph(dtP.Rows[m-1]["PNote"] != null ? dtP.Rows[m-1]["PNote"].ToString() : "", font10));
remarkcell.VerticalAlignment = Element.ALIGN_MIDDLE;
t2.AddCell(remarkcell);
#endregion
}