using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using Microsoft.AspNetCore.Hosting; using OpenAuth.Repository.Domain; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OpenAuth.App.Common { public class MiWordHelper { #region public string GenerateSeizureDocumentWord(MiSeizureDocument mr, IWebHostEnvironment env) { string templatePath = Path.Combine(env.ContentRootPath, "Templates", "扣押财物决定书.docx"); if (!File.Exists(templatePath)) throw new FileNotFoundException("模板文件不存在", templatePath); string fileName = $"扣押财物决定书_{mr.Id}.docx"; string uploadDir = Path.Combine(env.ContentRootPath, "upload"); if (!Directory.Exists(uploadDir)) Directory.CreateDirectory(uploadDir); string outputPath = Path.Combine(uploadDir, fileName); File.Copy(templatePath, outputPath, true); using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(outputPath, true)) { Body body = wordDoc.MainDocumentPart.Document.Body; ReplacePlaceholders(body, mr); wordDoc.MainDocumentPart.Document.Save(); } return $"/upload/{fileName}"; } private void ReplacePlaceholders(Body body, MiSeizureDocument mr) { // 准备替换值 string party = mr.Party ?? ""; string clueLocation = mr.ClueLocation ?? ""; string violationType = mr.ViolationType ?? ""; string year = mr.Year ?? DateTime.Now.Year.ToString(); string serialNumber = mr.SerialNumber.ToString("D3"); int month = mr.SeizureDate?.Month ?? 0; int day = mr.SeizureDate?.Day ?? 0; string monthStr = month > 0 ? month.ToString() : ""; string dayStr = day > 0 ? day.ToString() : ""; var paragraphs = body.Elements().ToList(); for (int i = 0; i < paragraphs.Count; i++) { Paragraph para = paragraphs[i]; string paraText = para.InnerText; // 处理物品列表占位符 {{Items}} if (paraText.Contains("{{Items}}")) { ParagraphProperties paraProps = para.ParagraphProperties?.CloneNode(true) as ParagraphProperties; var runs = para.Elements().ToList(); RunProperties indexRunProps = null; // 序号部分的样式 RunProperties contentRunProps = null; // 内容部分的样式 // 找到包含 "{{Items}}" 的 Run 作为内容 Run Run contentRun = runs.FirstOrDefault(r => r.InnerText.Contains("{{Items}}")); if (contentRun?.RunProperties != null) contentRunProps = contentRun.RunProperties.CloneNode(true) as RunProperties; // 取第一个 Run 作为序号 Run(假设序号在最前面) Run indexRun = runs.FirstOrDefault(); if (indexRun != null && indexRun != contentRun && indexRun.RunProperties != null) indexRunProps = indexRun.RunProperties.CloneNode(true) as RunProperties; para.Remove(); InsertItemsParagraphs(body, i, mr.Items, paraProps, indexRunProps, contentRunProps); ReplacePlaceholders(body, mr); // 重新开始循环 return; } // 处理普通占位符替换 foreach (var run in para.Elements()) { Text text = run.Elements().FirstOrDefault(); if (text == null) continue; string original = text.Text; string replaced = original .Replace("{{Party}}", party) .Replace("{{ClueLocation}}", clueLocation) .Replace("{{Year}}", year) .Replace("{{Month}}", monthStr) .Replace("{{Day}}", dayStr) .Replace("{{SerialNumber}}", serialNumber) .Replace("{{ViolationType}}", violationType); // 方括号形式 if (original != replaced) text.Text = replaced; } } } private void InsertItemsParagraphs(Body body, int insertIndex, string itemsStr, ParagraphProperties templateParaProps, RunProperties indexRunProps, RunProperties contentRunProps) { string[] items = itemsStr.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < items.Length; i++) { string indexText = $"{i + 1}、 "; string rawContent = items[i].Trim(); int leadingSpaces = 6; int trailingSpaces = 30 - leadingSpaces - rawContent.Length; if (trailingSpaces < 0) trailingSpaces = 0; string contentText = new string(' ', leadingSpaces) + rawContent + new string(' ', trailingSpaces); Paragraph newPara = new Paragraph(); if (templateParaProps != null) newPara.ParagraphProperties = (ParagraphProperties)templateParaProps.CloneNode(true); // 序号 Run(移除可能的下划线) Run indexRun = new Run(); if (indexRunProps != null) { indexRun.RunProperties = (RunProperties)indexRunProps.CloneNode(true); if (indexRun.RunProperties.Underline != null) indexRun.RunProperties.Underline.Remove(); } // 关键:设置 Space 为 Preserve 以保留空格 indexRun.AppendChild(new Text(indexText) { Space = SpaceProcessingModeValues.Preserve }); newPara.AppendChild(indexRun); // 内容 Run(强制黑色下划线) Run contentRun = new Run(); if (contentRunProps != null) { contentRun.RunProperties = (RunProperties)contentRunProps.CloneNode(true); if (contentRun.RunProperties.Color == null) contentRun.RunProperties.Color = new Color() { Val = "000000" }; else contentRun.RunProperties.Color.Val = "000000"; if (contentRun.RunProperties.Underline == null) contentRun.RunProperties.Underline = new Underline() { Val = UnderlineValues.Single }; } else { contentRun.RunProperties = new RunProperties(); contentRun.RunProperties.Underline = new Underline() { Val = UnderlineValues.Single }; contentRun.RunProperties.Color = new Color() { Val = "000000" }; } // 关键:设置 Space 为 Preserve 以保留前后空格 contentRun.AppendChild(new Text(contentText) { Space = SpaceProcessingModeValues.Preserve }); newPara.AppendChild(contentRun); body.InsertAt(newPara, insertIndex + i); } } #endregion #region 解除扣押 public string GenerateReleaseDocumentWord(MiReleaseDocument rd, IWebHostEnvironment env) { string templatePath = Path.Combine(env.ContentRootPath, "Templates", "解除扣押决定书.docx"); if (!File.Exists(templatePath)) throw new FileNotFoundException("模板文件不存在", templatePath); string fileName = $"解除扣押决定书_{rd.Id}.docx"; string uploadDir = Path.Combine(env.ContentRootPath, "upload"); if (!Directory.Exists(uploadDir)) Directory.CreateDirectory(uploadDir); string outputPath = Path.Combine(uploadDir, fileName); File.Copy(templatePath, outputPath, true); using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(outputPath, true)) { Body body = wordDoc.MainDocumentPart.Document.Body; ReplacePlaceholdersForRelease(body, rd); wordDoc.MainDocumentPart.Document.Save(); } return $"/upload/{fileName}"; } private void ReplacePlaceholdersForRelease(Body body, MiReleaseDocument rd) { // 准备替换值 string party = rd.Party ?? ""; string clueLocation = rd.ClueLocation ?? ""; string violationType = rd.ViolationType ?? ""; string year = rd.Year ?? DateTime.Now.Year.ToString(); string serialNumber = rd.SerialNumber.ToString("D3"); // 解除扣押决定日期(落款) int rYear = rd.ReleaseDate?.Year ?? 0; int rMonth = rd.ReleaseDate?.Month ?? 0; int rDay = rd.ReleaseDate?.Day ?? 0; string rYearStr = rYear > 0 ? rYear.ToString() : ""; string rMonthStr = rMonth > 0 ? rMonth.ToString() : ""; string rDayStr = rDay > 0 ? rDay.ToString() : ""; // 领取截止日期 int pYear = rd.PickupDeadline?.Year ?? 0; int pMonth = rd.PickupDeadline?.Month ?? 0; int pDay = rd.PickupDeadline?.Day ?? 0; string pYearStr = pYear > 0 ? pYear.ToString() : ""; string pMonthStr = pMonth > 0 ? pMonth.ToString() : ""; string pDayStr = pDay > 0 ? pDay.ToString() : ""; // 扣押财物日期(来自实体类 SeizureData) int fYear = rd.SeizureData?.Year ?? 0; int fMonth = rd.SeizureData?.Month ?? 0; int fDay = rd.SeizureData?.Day ?? 0; string fYearStr = fYear > 0 ? fYear.ToString() : ""; string fMonthStr = fMonth > 0 ? fMonth.ToString() : ""; string fDayStr = fDay > 0 ? fDay.ToString() : ""; string parkingLot = rd.ParkingLot ?? ""; var paragraphs = body.Elements().ToList(); for (int i = 0; i < paragraphs.Count; i++) { Paragraph para = paragraphs[i]; string paraText = para.InnerText; // 处理物品列表占位符 {{Items}} if (paraText.Contains("{{Items}}")) { ParagraphProperties paraProps = para.ParagraphProperties?.CloneNode(true) as ParagraphProperties; var runs = para.Elements().ToList(); RunProperties indexRunProps = null; RunProperties contentRunProps = null; // 找到包含 "{{Items}}" 的 Run 作为内容 Run Run contentRun = runs.FirstOrDefault(r => r.InnerText.Contains("{{Items}}")); if (contentRun?.RunProperties != null) contentRunProps = contentRun.RunProperties.CloneNode(true) as RunProperties; // 取第一个 Run 作为序号 Run(假设序号在最前面) Run indexRun = runs.FirstOrDefault(); if (indexRun != null && indexRun != contentRun && indexRun.RunProperties != null) indexRunProps = indexRun.RunProperties.CloneNode(true) as RunProperties; para.Remove(); InsertItemsParagraphs(body, i, rd.Items, paraProps, indexRunProps, contentRunProps); ReplacePlaceholdersForRelease(body, rd); // 重新开始循环 return; } // 处理普通占位符替换 foreach (var run in para.Elements()) { Text text = run.Elements().FirstOrDefault(); if (text == null) continue; string original = text.Text; string replaced = original .Replace("{{Year}}", year) // 文书编号年份 .Replace("{{SerialNumber}}", serialNumber) .Replace("{{Paty}}", party) // 注意模板拼写为 Paty .Replace("{{ClueLocation}}", clueLocation) .Replace("{{ViolationType}}", violationType) .Replace("{{FYear}}", fYearStr) .Replace("{{FMonth}}", fMonthStr) .Replace("{{FDay}}", fDayStr) .Replace("{{PYear}}", pYearStr) .Replace("{{Pmonth}}", pMonthStr) // 注意大小写 .Replace("{{Pday}}", pDayStr) .Replace("{{ParkingLot}}", parkingLot) .Replace("{{Month}}", rMonthStr) // 落款月份 .Replace("{{Day}}", rDayStr); // 落款日 if (original != replaced) text.Text = replaced; } } } //private void InsertItemsParagraphs(Body body, int insertIndex, string itemsStr, // ParagraphProperties templateParaProps, // RunProperties indexRunProps, // RunProperties contentRunProps) //{ // string[] items = itemsStr.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); // for (int i = 0; i < items.Length; i++) // { // string indexText = $"{i + 1}、 "; // string rawContent = items[i].Trim(); // int leadingSpaces = 6; // int trailingSpaces = 30 - leadingSpaces - rawContent.Length; // if (trailingSpaces < 0) trailingSpaces = 0; // string contentText = new string(' ', leadingSpaces) + rawContent + new string(' ', trailingSpaces); // Paragraph newPara = new Paragraph(); // if (templateParaProps != null) // newPara.ParagraphProperties = (ParagraphProperties)templateParaProps.CloneNode(true); // // 序号 Run(移除可能的下划线) // Run indexRun = new Run(); // if (indexRunProps != null) // { // indexRun.RunProperties = (RunProperties)indexRunProps.CloneNode(true); // if (indexRun.RunProperties.Underline != null) // indexRun.RunProperties.Underline.Remove(); // } // // 关键:设置 Space 为 Preserve 以保留空格 // indexRun.AppendChild(new Text(indexText) { Space = SpaceProcessingModeValues.Preserve }); // newPara.AppendChild(indexRun); // // 内容 Run(强制黑色下划线) // Run contentRun = new Run(); // if (contentRunProps != null) // { // contentRun.RunProperties = (RunProperties)contentRunProps.CloneNode(true); // if (contentRun.RunProperties.Color == null) // contentRun.RunProperties.Color = new Color() { Val = "000000" }; // else // contentRun.RunProperties.Color.Val = "000000"; // if (contentRun.RunProperties.Underline == null) // contentRun.RunProperties.Underline = new Underline() { Val = UnderlineValues.Single }; // } // else // { // contentRun.RunProperties = new RunProperties(); // contentRun.RunProperties.Underline = new Underline() { Val = UnderlineValues.Single }; // contentRun.RunProperties.Color = new Color() { Val = "000000" }; // } // // 关键:设置 Space 为 Preserve 以保留前后空格 // contentRun.AppendChild(new Text(contentText) { Space = SpaceProcessingModeValues.Preserve }); // newPara.AppendChild(contentRun); // body.InsertAt(newPara, insertIndex + i); // } //} #endregion } }