# -*- coding: utf-8 -*- """甲方报表 FillDetailSheet 重写:DOM 组装(上万行节点树+OuterXml 双份字符串→32位OOM) 改为字符串模板替换法:提取模板行字符串→逐行换号/替换单元格→拼接输出。零 DOM、单份字符串。""" import io import re P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\xiangmu\甲方报表.cs" s = io.open(P, encoding="utf-8-sig").read() start_anchor = " private void FillDetailSheet(Dictionary entries, string sheetKey," end_anchor = " entries[sheetKey] = System.Text.Encoding.UTF8.GetBytes(doc.OuterXml);\n }" i0 = s.find(start_anchor) assert i0 > 0, "起点未找到" i1 = s.find(end_anchor, i0) assert i1 > 0, "终点未找到" i1 += len(end_anchor) NEW = ''' // ============ 明细表:字符串模板替换法(零 DOM——上万像素级 DOM+OuterXml 双份字符串会 OOM) ============ /// 行字符串:更新行号与全部单元格引用(r="列行号")。 private static string 行换号(string rowXml, int rowNum) { rowXml = System.Text.RegularExpressions.Regex.Replace(rowXml, "行字符串:设置某列单元格内容(innerXml=单元格内部 XML;tAttr=类型属性,null=移除 t)。 private static string 行设单元格(string rowXml, string col, int rowNum, string innerXml, string tAttr) { string r = col + rowNum; var m = System.Text.RegularExpressions.Regex.Match(rowXml, "]*?)(?:/>|>.*?)"); if (!m.Success) return rowXml; string attrs = m.Groups[1].Value; attrs = System.Text.RegularExpressions.Regex.Replace(attrs, "\\\\s*t=\\"[^\\"]*\\"", ""); if (!string.IsNullOrEmpty(tAttr)) attrs += " t=\\"" + tAttr + "\\""; return rowXml.Substring(0, m.Index) + "" + innerXml + "" + rowXml.Substring(m.Index + m.Length); } private static string 行设文本(string rowXml, string col, int rowNum, string text) { return 行设单元格(rowXml, col, rowNum, "" + XmlEscape(text) + "", "str"); } private static string 行设数值(string rowXml, string col, int rowNum, decimal v) { return 行设单元格(rowXml, col, rowNum, "" + v.ToString(System.Globalization.CultureInfo.InvariantCulture) + "", null); } private static string 行设公式(string rowXml, string col, int rowNum, string formula) { return 行设单元格(rowXml, col, rowNum, "" + XmlEscape(formula) + "", null); } private void FillDetailSheet(Dictionary entries, string sheetKey, List tuhaoList, List cabRowInfos, System.Text.StringBuilder dbgLog, bool useFormula) { string xml = System.Text.Encoding.UTF8.GetString(entries[sheetKey]); // 提取全部行块,定位模板行 11~16 与头/尾(零 DOM) var 行匹配 = System.Text.RegularExpressions.Regex.Matches( xml, "]*?(?:/>|>.*?)"); if (行匹配.Count == 0) return; string[] tpl = new string[6]; int bodyStart = -1, bodyEnd = -1; foreach (System.Text.RegularExpressions.Match m in 行匹配) { int rn = int.Parse(m.Groups[1].Value); if (rn >= 11 && rn <= 16) { tpl[rn - 11] = m.Value; if (bodyStart < 0) bodyStart = m.Index; bodyEnd = m.Index + m.Length; } } string tplPaperRow = tpl[0], tplCabRow = tpl[1], tplHeaderRow = tpl[2], tplElemRow = tpl[3], tplSingleRow = tpl[4], tplGrandRow = tpl[5]; if (tplCabRow == null || tplElemRow == null) return; string 头 = xml.Substring(0, bodyStart); // 行1~10 string 尾 = xml.Substring(bodyEnd); // 行17(签名)及之后 var sb = new System.Text.StringBuilder(); int rowNum = 11; decimal grandQty = 0, grandTotal = 0; int tuhaoIdx = 0; foreach (var tuhao in tuhaoList) { string tuhaoName = !string.IsNullOrEmpty(tuhao.TuMing) ? tuhao.TuMing : tuhao.Tuhao; // 图名行 if (tplPaperRow != null) { string row = 行换号(tplPaperRow, rowNum); row = 行设文本(row, "A", rowNum, tuhaoName); sb.Append(row); rowNum++; } // 每个箱号: 柜号行 + 表头 + 元件行*N + 单台合计 + 总计 int cabOrder = 0; foreach (var cab in tuhao.Cabinets) { cabOrder++; int cabRowNum = rowNum; // 柜号行 { string row = 行换号(tplCabRow, rowNum); row = 行设文本(row, "A", rowNum, (tuhaoIdx + 1) + "-" + cabOrder); row = 行设文本(row, "B", rowNum, "柜号:" + cab.CabinetNo); row = 行设文本(row, "D", rowNum, cab.Model); // ★ G列"名称:"= 箱名(进线柜/PT柜等);库里没有时退回图名(与旧版行为一致) string 名称值 = !string.IsNullOrEmpty(cab.BoxName) ? cab.BoxName : tuhaoName; row = 行设文本(row, "G", rowNum, 名称值); row = 行设文本(row, "I", rowNum, cab.SpecName); sb.Append(row); rowNum++; } // 表头行 if (tplHeaderRow != null) { sb.Append(行换号(tplHeaderRow, rowNum)); rowNum++; } // 元件行(G列用 =E*F 公式) int elemStartRow = rowNum; if (cab.Elements.Count == 0) { sb.Append(行换号(tplElemRow, rowNum)); rowNum++; } else { int elemOrder = 0; foreach (var elem in cab.Elements) { elemOrder++; string row = 行换号(tplElemRow, rowNum); row = 行设文本(row, "A", rowNum, elemOrder.ToString()); row = 行设文本(row, "B", rowNum, elem.Name); row = 行设文本(row, "C", rowNum, elem.ShowSpecName); row = 行设文本(row, "D", rowNum, elem.Unit); row = 行设数值(row, "E", rowNum, SafeDec(elem.Count)); row = 行设数值(row, "F", rowNum, elem.OfferUnitPrice); // G列: 公式 =E{行}*F{行} 或直接值 if (useFormula) row = 行设公式(row, "G", rowNum, "E" + rowNum + "*F" + rowNum); else row = 行设数值(row, "G", rowNum, SafeDec(elem.Count) * elem.OfferUnitPrice); row = 行设文本(row, "H", rowNum, elem.FactName); row = 行设文本(row, "I", rowNum, elem.Remark); sb.Append(row); rowNum++; } } int elemEndRow = rowNum - 1; // 单台合计行(G列用 =ROUND(SUM(G{起}:G{止}),0) 公式) int singleRowNum = rowNum; if (tplSingleRow != null) { string row = 行换号(tplSingleRow, rowNum); row = 行设文本(row, "B", rowNum, "单台合计"); if (useFormula) row = 行设公式(row, "G", rowNum, "ROUND(SUM(G" + elemStartRow + ":G" + elemEndRow + "),0)"); else { decimal elemSum = 0; foreach (var elem in cab.Elements) elemSum += SafeDec(elem.Count) * elem.OfferUnitPrice; row = 行设数值(row, "G", rowNum, elemSum); } sb.Append(row); rowNum++; } // 总计行(G列用 =G{单台合计}*E{总计} 公式) int grandRowNum = rowNum; if (tplGrandRow != null) { string row = 行换号(tplGrandRow, rowNum); row = 行设文本(row, "B", rowNum, "总计"); row = 行设文本(row, "D", rowNum, "台"); row = 行设数值(row, "E", rowNum, SafeDec(cab.Count)); if (useFormula) row = 行设公式(row, "G", rowNum, "G" + singleRowNum + "*E" + rowNum); else { decimal elemSum = 0; foreach (var elem in cab.Elements) elemSum += SafeDec(elem.Count) * elem.OfferUnitPrice; row = 行设数值(row, "G", rowNum, elemSum * SafeDec(cab.Count)); } sb.Append(row); rowNum++; } // 记录行号供汇总表引用 cabRowInfos.Add(new CabinetRowInfo { CabRow = cabRowNum, SingleTotalRow = singleRowNum, GrandRow = grandRowNum }); grandQty += SafeDec(cab.Count); grandTotal += cab.OfferTotalPrice; } tuhaoIdx++; } entries[sheetKey] = System.Text.Encoding.UTF8.GetBytes(头 + sb.ToString() + 尾); }''' s = s[:i0] + NEW + s[i1:] io.open(P, "w", encoding="utf-8-sig").write(s) print("FillDetailSheet 已重写为字符串模板法")