diff --git a/SGGL/BLL/Common/FastReport.cs b/SGGL/BLL/Common/FastReport.cs index 9ecf8a8b..e3667678 100644 --- a/SGGL/BLL/Common/FastReport.cs +++ b/SGGL/BLL/Common/FastReport.cs @@ -1,12 +1,19 @@ -using FastReport; +using FastReport; +using FastReport.Export.Pdf; +using System; using System.Collections.Generic; using System.Data; +using System.IO; +using System.Text; +using System.Timers; using System.Web; namespace BLL { public static class FastReportService { + private static Timer tempReportCleanupTimer; + /// /// 重置数据 /// @@ -32,6 +39,159 @@ namespace BLL } + /// + /// 启动临时报表清理定时器,定期删除超过 1 小时的 PDF。 + /// + public static void StartTempReportCleanupMonitor() + { + if (tempReportCleanupTimer != null) + { + tempReportCleanupTimer.Stop(); + tempReportCleanupTimer.Dispose(); + tempReportCleanupTimer = null; + } + + CleanExpiredTempReports(); + tempReportCleanupTimer = new Timer + { + AutoReset = true, + Interval = 1000 * 60 * 30 + }; + tempReportCleanupTimer.Elapsed += TempReportCleanupTimer_Elapsed; + tempReportCleanupTimer.Start(); + } + + private static void TempReportCleanupTimer_Elapsed(object sender, ElapsedEventArgs e) + { + CleanExpiredTempReports(); + } + + /// + /// 删除 File/Fastreport/Temp 下创建时间超过 1 小时的 PDF 临时文件。 + /// + public static void CleanExpiredTempReports() + { + try + { + string tempDirectory = Path.Combine(Funs.RootPath, @"File\Fastreport\Temp\"); + if (!Directory.Exists(tempDirectory)) + { + return; + } + + DateTime expiredTime = DateTime.Now.AddHours(-1); + foreach (string file in Directory.GetFiles(tempDirectory, "*.pdf", SearchOption.TopDirectoryOnly)) + { + FileInfo fileInfo = new FileInfo(file); + if (fileInfo.CreationTime < expiredTime) + { + fileInfo.Attributes = FileAttributes.Normal; + fileInfo.Delete(); + } + } + } + catch (Exception ex) + { + ErrLogInfo.WriteLog(ex, "FastReport临时报表清理", "FastReportService.CleanExpiredTempReports"); + } + } + + /// + /// 导出报表 PDF 到临时目录,并返回可访问的相对 URL。 + /// + /// 报表模板路径,支持绝对路径或相对 Funs.RootPath 的路径。 + public static string ExportReport(string reportPath) + { + List dataTables = (List)HttpContext.Current.Session["ReportDataTables"]; + Dictionary parameterValues = (Dictionary)HttpContext.Current.Session["ReportParameterValues"]; + return ExportReport(reportPath, dataTables, parameterValues); + } + + /// + /// 导出报表 PDF 到临时目录,并返回可访问的相对 URL。 + /// + /// 报表模板路径,支持绝对路径或相对 Funs.RootPath 的路径。 + /// 报表数据源集合。 + /// 报表参数集合。 + public static string ExportReport(string reportPath, List dataTables, Dictionary parameterValues) + { + string fullReportPath = GetReportFullPath(reportPath); + if (string.IsNullOrEmpty(fullReportPath) || !File.Exists(fullReportPath)) + { + throw new FileNotFoundException("打印模板不存在!", reportPath); + } + + string relativeDirectory = @"File\Fastreport\Temp\"; + string tempDirectory = Path.Combine(Funs.RootPath, relativeDirectory); + if (!Directory.Exists(tempDirectory)) + { + Directory.CreateDirectory(tempDirectory); + } + + string fileName = "report_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString("N") + ".pdf"; + string filePath = Path.Combine(tempDirectory, fileName); + using (Report report = BuildPreparedReport(fullReportPath, dataTables, parameterValues)) + using (PDFExport pdfExport = new PDFExport()) + { + // Web 打印先导出 PDF,前端用 iframe 装载后调用浏览器打印。 + pdfExport.PrintScaling = false; + pdfExport.ShowPrintDialog = true; + report.Export(pdfExport, filePath); + } + + return "~/" + relativeDirectory.Replace("\\", "/") + fileName; + } + + private static Report BuildPreparedReport(string reportPath, List dataTables, Dictionary parameterValues) + { + FastReport.Utils.Config.WebMode = true; + Report report = new Report(); + report.Load(reportPath); + if (report.Dictionary.Connections.Count > 0) + { + var reportConnection = report.Dictionary.Connections[0]; + if (reportConnection.ConnectionString != Funs.ConnString) + { + // 打印时只替换本次报表连接,避免把运行环境连接串写回模板文件。 + reportConnection.ConnectionString = Funs.ConnString; + } + } + + if (dataTables != null && dataTables.Count > 0) + { + for (int i = 0; i < dataTables.Count; i++) + { + report.RegisterData(dataTables[i], dataTables[i].TableName); + } + } + + if (parameterValues != null && parameterValues.Count > 0) + { + foreach (KeyValuePair kvp in parameterValues) + { + report.SetParameterValue(kvp.Key, kvp.Value); + } + } + + report.Prepare(); + return report; + } + + private static string GetReportFullPath(string reportPath) + { + if (string.IsNullOrEmpty(reportPath)) + { + return reportPath; + } + + if (Path.IsPathRooted(reportPath)) + { + return reportPath; + } + + return Path.Combine(Funs.RootPath, reportPath.TrimStart('\\', '/')); + } + /// /// 合并报表后打印 /// diff --git a/SGGL/FineUIPro.Web/CLGL/InPlanMaster.aspx.cs b/SGGL/FineUIPro.Web/CLGL/InPlanMaster.aspx.cs index 3b0232d4..d0f6a212 100644 --- a/SGGL/FineUIPro.Web/CLGL/InPlanMaster.aspx.cs +++ b/SGGL/FineUIPro.Web/CLGL/InPlanMaster.aspx.cs @@ -1,4 +1,5 @@ using BLL; +using FastReport; using Model; using System; using System.Collections.Generic; @@ -433,8 +434,7 @@ namespace FineUIPro.Web.CLGL { BindDetailGrid(Grid1.SelectedRowID); } - - private void Print(string Id) + private void Print(string Id) { BLL.FastReportService.ResetData(); if (string.IsNullOrEmpty(Id)) diff --git a/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx b/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx index 19c3dd18..e2c7d0bf 100644 --- a/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx +++ b/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx @@ -254,6 +254,54 @@ function reloadGrid() { __doPostBack(null, 'reloadGrid'); } + function printByHiddenFrame(url) { + var frameId = 'fastreport-print-frame-' + new Date().getTime(); + var frame = document.createElement('iframe'); + frame.id = frameId; + frame.name = frameId; + frame.style.position = 'fixed'; + frame.style.left = '-10000px'; + frame.style.top = '-10000px'; + frame.style.width = '1px'; + frame.style.height = '1px'; + frame.style.border = '0'; + frame.style.opacity = '0'; + frame.onload = function () { + setTimeout(function () { + try { + frame.contentWindow.onafterprint = cleanupPrintFrame; + frame.contentWindow.focus(); + frame.contentWindow.print(); + } catch (e) { + window.print(); + } + }, 500); + }; + frame.src = url; + document.body.appendChild(frame); + var cleaned = false; + function cleanupPrintFrame() { + if (cleaned) { + return; + } + cleaned = true; + if (frame && frame.parentNode) { + frame.parentNode.removeChild(frame); + } + window.removeEventListener('afterprint', cleanupPrintFrame); + } + window.addEventListener('afterprint', cleanupPrintFrame); + setTimeout(cleanupPrintFrame, 300000); + } + window.addEventListener('message', function (event) { + if (!event.data || event.data.type !== 'fastreport-print-finished') { + return; + } + var frame = document.getElementById(event.data.frameId); + if (frame && frame.parentNode) { + frame.parentNode.removeChild(frame); + } + }); diff --git a/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx.cs b/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx.cs index a8f8f4ce..c35657c3 100644 --- a/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx.cs +++ b/SGGL/FineUIPro.Web/CLGL/InputMaster.aspx.cs @@ -368,7 +368,7 @@ namespace FineUIPro.Web.CLGL { ShowNotify("请选择要打印的项", MessageBoxIcon.Warning); return; - } + } BLL.FastReportService.ResetData(); var query = new Tw_InputDetailBarCodeOutput @@ -394,7 +394,9 @@ namespace FineUIPro.Web.CLGL string initTemplatePath = "File\\Fastreport\\材料入库条码.frx"; if (File.Exists(rootPath + initTemplatePath)) { - PageContext.RegisterStartupScript(Window2.GetShowReference(String.Format("~/Controls/Fastreport.aspx?ReportPath={0}", rootPath + initTemplatePath))); + //PageContext.RegisterStartupScript(Window2.GetShowReference(String.Format("~/Controls/Fastreport.aspx?ReportPath={0}", rootPath + initTemplatePath))); + string printUrl = ResolveUrl(BLL.FastReportService.ExportReport(rootPath + initTemplatePath)); + PageContext.RegisterStartupScript(String.Format("printByHiddenFrame('{0}');", printUrl)); } } diff --git a/SGGL/FineUIPro.Web/Controls/Fastreport.aspx.cs b/SGGL/FineUIPro.Web/Controls/Fastreport.aspx.cs index 26e751dc..9165301e 100644 --- a/SGGL/FineUIPro.Web/Controls/Fastreport.aspx.cs +++ b/SGGL/FineUIPro.Web/Controls/Fastreport.aspx.cs @@ -1,4 +1,5 @@ using BLL; +using FastReport.Web; using System; using System.Collections.Generic; using System.Data; @@ -61,7 +62,8 @@ namespace FineUIPro.Web.Controls } } WebReport1.ReportFile = ReportPath; - WebReport1.Prepare(); + WebReport1.PdfShowPrintDialog = false; + WebReport1.Prepare(); // WebReport1.ExportPdf(); } private void WebReport1_StartReport(object sender, EventArgs e) diff --git a/SGGL/FineUIPro.Web/File/Excel/DataOut/管道数据表导出模板.xlsx b/SGGL/FineUIPro.Web/File/Excel/DataOut/管道数据表导出模板.xlsx new file mode 100644 index 00000000..6662b4ad Binary files /dev/null and b/SGGL/FineUIPro.Web/File/Excel/DataOut/管道数据表导出模板.xlsx differ diff --git a/SGGL/FineUIPro.Web/FineUIPro.Web.csproj b/SGGL/FineUIPro.Web/FineUIPro.Web.csproj index 8adc138b..662b9dd6 100644 --- a/SGGL/FineUIPro.Web/FineUIPro.Web.csproj +++ b/SGGL/FineUIPro.Web/FineUIPro.Web.csproj @@ -17266,4 +17266,4 @@ --> - + \ No newline at end of file diff --git a/SGGL/FineUIPro.Web/Global.asax.cs b/SGGL/FineUIPro.Web/Global.asax.cs index 73682871..79486a86 100644 --- a/SGGL/FineUIPro.Web/Global.asax.cs +++ b/SGGL/FineUIPro.Web/Global.asax.cs @@ -66,6 +66,7 @@ BLL.MonitorService.StartMonitorEve(); //BLL.YunMouService.StartMonitor(); //BLL.MonitorService.StartPersonQuarterCheck(); + BLL.FastReportService.StartTempReportCleanupMonitor(); QuartzServices.Init(); } catch (Exception ex) diff --git a/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx b/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx index 6a31d251..26d7173d 100644 --- a/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx +++ b/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx @@ -76,6 +76,9 @@ + + diff --git a/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.cs b/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.cs index b05fdf1d..fd0373ad 100644 --- a/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.cs +++ b/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.cs @@ -406,6 +406,179 @@ namespace FineUIPro.Web.HJGL.InfoQuery // return sb.ToString(); // } + + /// + /// 管道数据表导出按钮 + /// + protected void btnPipelineDataOut_Click(object sender, EventArgs e) + { + var pipelines = GetCurrentPipelineList(); + if (pipelines.Count == 0) + { + ShowNotify("没有可导出的管线数据!", MessageBoxIcon.Warning); + return; + } + + string templatePath = Funs.RootPath + @"File\Excel\DataOut\管道数据表导出模板.xlsx"; + if (!File.Exists(templatePath)) + { + ShowNotify("导出模板不存在!", MessageBoxIcon.Warning); + return; + } + + string tempPath = Funs.RootPath + @"File\Excel\Temp\管道数据表.xlsx"; + tempPath = tempPath.Replace(".xlsx", string.Format("{0:yyyy-MM-dd-HH-mm-ss}", DateTime.Now) + ".xlsx"); + Directory.CreateDirectory(Path.GetDirectoryName(tempPath)); + + var pipelineIds = pipelines.Select(x => x.PipelineId).Where(x => !string.IsNullOrEmpty(x)).ToList(); + var hotPipelineIds = Funs.DB.HJGL_WeldJoint + .Where(x => x.ProjectId == this.CurrUser.LoginProjectId && x.PipelineId != null && pipelineIds.Contains(x.PipelineId) && x.IsHotProess == true) + .Select(x => x.PipelineId) + .Distinct() + .ToList(); + var hotPipelineIdSet = new HashSet(hotPipelineIds); + + var pipelineData = pipelines.Select((x, index) => new + { + No = index + 1, + PipelineCode = SafeText(x.PipelineCode), + PipingClassCode = SafeText(x.PipingClassCode), + MediumName = SafeText(x.MediumName), + PressurePipingClassCode = SafeText(x.PressurePipingClassCode), + DesignPress = SafeText(x.DesignPress), + DesignTemperature = SafeText(x.DesignTemperature), + OperatePressure = "/", + OperateTemperature = "/", + PWHT = hotPipelineIdSet.Contains(x.PipelineId) ? "PWHT" : "/", + DetectionRateCode = SafeText(x.DetectionRateCode), + InsulationType = "/", + Tracing = "/", + TestType = "/", + TestMethod = SafeText(x.TestMediumCode), + TestPressure = SafeText(x.TestPressure) + }).ToList(); + + var weldJoints = Funs.DB.View_HJGL_WeldJoint + .Where(x => x.ProjectId == this.CurrUser.LoginProjectId && x.PipelineId != null && pipelineIds.Contains(x.PipelineId)) + .OrderBy(x => x.PipelineCode) + .ThenBy(x => x.WeldJointCode) + .ToList(); + var weldJointIds = weldJoints.Select(x => x.WeldJointId).Where(x => !string.IsNullOrEmpty(x)).ToList(); + var pointJointIds = Funs.DB.HJGL_Batch_PointBatchItem + .Where(x => x.WeldJointId != null && weldJointIds.Contains(x.WeldJointId) && x.PointState != null) + .Select(x => x.WeldJointId) + .Distinct() + .ToList(); + var pointJointIdSet = new HashSet(pointJointIds); + + var weldJointData = weldJoints.Select((x, index) => new + { + No = index + 1, + SingleNumber = SafeText(x.SingleNumber), + WeldJointCode = SafeText(x.WeldJointCode), + Dia = x.Dia, + Thickness = x.Thickness, + MaterialCode = SafeText(x.MaterialCode), + WeldingDate = SafeText(x.WeldingDate), + WelderName = JoinTexts(x.BackingWelderName, x.CoverWelderName), + CertificateNo = "/", + WelderCode = JoinTexts(x.BackingWelderCode, x.CoverWelderCode), + WelderExamDate = "/", + TestJointDate = "/", + RootWeldingData = FormatWeldingData(x.WeldingMethodCode, x.WeldingRodCode, x.WeldingWireCode), + RemainingWeldingData = FormatWeldingData(x.WeldingMethodCode, x.WeldingRodCode, x.WeldingWireCode), + // P列和V列按“管线需要热处理”判断,不按单个焊口判断。 + PWHT = hotPipelineIdSet.Contains(x.PipelineId) ? "PWHT" : "/", + HotProcessAccept = hotPipelineIdSet.Contains(x.PipelineId) ? "ACC." : "/", + // AB列按点口记录判断;AC列按管道等级1级判断,否则保持模板要求的“/”。 + PointAccept = IsPointed(x.IsPoint, x.WeldJointId, pointJointIdSet) ? "ACC." : "/", + PipingClassAccept = IsFirstLevelPipingClass(x.PipingClassCode) ? "ACC." : "/" + }).ToList(); + + var value = new Dictionary + { + ["PipelineData"] = pipelineData, + ["WeldJointData"] = weldJointData + }; + + MiniExcel.SaveAsByTemplate(tempPath, templatePath, value); + DownTempFile(tempPath, "管道数据表.xlsx"); + } + + /// + /// 按当前页面筛选条件重新获取导出管线,避免使用跨用户共享的静态缓存。 + /// + private List GetCurrentPipelineList() + { + Model.View_HJGL_Pipeline model = new Model.View_HJGL_Pipeline(); + model.ProjectId = this.CurrUser.LoginProjectId; + model.UnitWorkId = this.tvControlItem.SelectedNodeID; + model.PipelineCode = this.txtPipelineCode.Text.Trim(); + model.IsFinished = null; + if (drpIsFinish.SelectedValue == "1") + { + model.IsFinished = true; + } + if (drpIsFinish.SelectedValue == "0") + { + model.IsFinished = false; + } + return BLL.PipelineService.GetView_HJGL_Pipelines(model); + } + + private void DownTempFile(string path, string fileName) + { + FileInfo info = new FileInfo(path); + long fileSize = info.Length; + System.Web.HttpContext.Current.Response.Clear(); + System.Web.HttpContext.Current.Response.ContentType = "application/x-zip-compressed"; + System.Web.HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" + System.Web.HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8)); + System.Web.HttpContext.Current.Response.AddHeader("Content-Length", fileSize.ToString()); + System.Web.HttpContext.Current.Response.TransmitFile(path, 0, fileSize); + System.Web.HttpContext.Current.Response.Flush(); + System.Web.HttpContext.Current.Response.Close(); + File.Delete(path); + } + + private static string SafeText(object value) + { + if (value == null) + { + return "/"; + } + string text = value.ToString(); + return string.IsNullOrWhiteSpace(text) ? "/" : text.Trim(); + } + + private static string JoinTexts(params string[] values) + { + var texts = values + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => x.Trim()) + .Distinct() + .ToList(); + return texts.Count == 0 ? "/" : string.Join("/", texts); + } + + private static string FormatWeldingData(params string[] values) + { + return JoinTexts(values); + } + + private static bool IsPointed(string isPoint, string weldJointId, HashSet pointJointIdSet) + { + return pointJointIdSet.Contains(weldJointId) || isPoint == "1" || isPoint == "是" || isPoint == "True"; + } + + private static bool IsFirstLevelPipingClass(string pipingClassCode) + { + if (string.IsNullOrWhiteSpace(pipingClassCode)) + { + return false; + } + string code = pipingClassCode.Trim(); + return code == "1" || code == "1级" || code == "一级" || code == "Ⅰ级" || code == "Ⅰ"; + } #endregion protected string ConvertDetectionType(object detectionType) @@ -572,4 +745,4 @@ namespace FineUIPro.Web.HJGL.InfoQuery //ctlAuditFlow.BindData(); } } -} \ No newline at end of file +} diff --git a/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.designer.cs b/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.designer.cs index 93d00524..c499d441 100644 --- a/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.designer.cs +++ b/SGGL/FineUIPro.Web/HJGL/InfoQuery/PipelineQuery.aspx.designer.cs @@ -182,6 +182,15 @@ namespace FineUIPro.Web.HJGL.InfoQuery { /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 /// protected global::FineUIPro.Button btnOut; + + /// + /// btnPipelineDataOut 控件。 + /// + /// + /// 自动生成的字段。 + /// 若要进行修改,请将字段声明从设计器文件移到代码隐藏文件。 + /// + protected global::FineUIPro.Button btnPipelineDataOut; /// /// Grid1 控件。 diff --git a/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheck.aspx.designer.cs b/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheck.aspx.designer.cs index 1f0181e6..9d31f91d 100644 --- a/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheck.aspx.designer.cs +++ b/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheck.aspx.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // <自动生成> // 此代码由工具生成。 // diff --git a/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheckEdit.aspx.designer.cs b/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheckEdit.aspx.designer.cs index 940ccf53..d337f239 100644 --- a/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheckEdit.aspx.designer.cs +++ b/SGGL/FineUIPro.Web/HJGL/PreWeld/CuttingCheckEdit.aspx.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // <自动生成> // 此代码由工具生成。 // diff --git a/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheck.aspx.designer.cs b/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheck.aspx.designer.cs index 36fe2914..6ed60852 100644 --- a/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheck.aspx.designer.cs +++ b/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheck.aspx.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // <自动生成> // 此代码由工具生成。 // diff --git a/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheckEdit.aspx.designer.cs b/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheckEdit.aspx.designer.cs index 61e38245..988ac02b 100644 --- a/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheckEdit.aspx.designer.cs +++ b/SGGL/FineUIPro.Web/HJGL/PreWeld/FitupCheckEdit.aspx.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // <自动生成> // 此代码由工具生成。 //