From c3aa006e03eeb0817833cde93ed963c893478792 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 05 五月 2026 12:55:33 +0800
Subject: [PATCH] # Agent数据分析V3.0.1.7

---
 src/main/webapp/views/ai/data_analysis.html |  120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/src/main/webapp/views/ai/data_analysis.html b/src/main/webapp/views/ai/data_analysis.html
index 25dd85a..0449d35 100644
--- a/src/main/webapp/views/ai/data_analysis.html
+++ b/src/main/webapp/views/ai/data_analysis.html
@@ -110,7 +110,6 @@
       margin-top: 2px;
     }
     .report-summary {
-      margin-top: 12px;
       padding: 14px;
       border-radius: 12px;
       border: 1px solid #e4ebf2;
@@ -121,16 +120,27 @@
       font-size: 15px;
       color: #223046;
     }
-    .report-summary pre {
-      white-space: pre-wrap;
-      word-break: break-word;
-      font-size: 13px;
-      line-height: 1.6;
+    .markdown-body {
+      font-size: 15px;
+      line-height: 1.8;
       color: #333;
-      margin: 0;
-      max-height: 500px;
+      max-height: 600px;
       overflow-y: auto;
     }
+    .markdown-body h1 { font-size: 22px; margin: 20px 0 10px; border-bottom: 1px solid #eee; padding-bottom: 6px; }
+    .markdown-body h2 { font-size: 19px; margin: 18px 0 8px; color: #223046; }
+    .markdown-body h3 { font-size: 17px; margin: 14px 0 6px; }
+    .markdown-body p { margin: 8px 0; }
+    .markdown-body ul, .markdown-body ol { padding-left: 24px; margin: 8px 0; }
+    .markdown-body li { margin: 4px 0; }
+    .markdown-body code { background: #f0f2f5; padding: 2px 6px; border-radius: 3px; font-size: 14px; }
+    .markdown-body pre { background: #282c34; color: #abb2bf; padding: 14px; border-radius: 6px; overflow-x: auto; margin: 10px 0; }
+    .markdown-body pre code { background: none; color: inherit; padding: 0; font-size: 14px; }
+    .markdown-body table { border-collapse: collapse; margin: 10px 0; width: 100%; }
+    .markdown-body th, .markdown-body td { border: 1px solid #ddd; padding: 8px 12px; text-align: left; font-size: 14px; }
+    .markdown-body th { background: #f5f7fa; font-weight: 600; }
+    .markdown-body blockquote { border-left: 3px solid #409eff; padding-left: 12px; color: #666; margin: 10px 0; }
+    .markdown-body strong { color: #223046; }
   </style>
 </head>
 <body>
@@ -275,12 +285,15 @@
             {{ periodLabel(selectedReport.periodType) }} 路 {{ formatTime(selectedReport.createTime) }}
           </div>
         </div>
-        <el-button size="mini" @click="selectedReport=null">鍏抽棴</el-button>
+        <div>
+          <el-button size="mini" type="primary" icon="el-icon-download" :loading="pdfLoading" @click="downloadPdf">涓嬭浇 PDF</el-button>
+          <el-button size="mini" @click="selectedReport=null">鍏抽棴</el-button>
+        </div>
       </div>
       <div class="panel-body">
-        <div class="report-summary">
+        <div class="report-summary" id="reportContent">
           <h3>鍒嗘瀽鎶ュ憡</h3>
-          <pre>{{ selectedReport.summary || '鏆傛棤鎶ュ憡鍐呭' }}</pre>
+          <div class="markdown-body" v-html="renderedSummary"></div>
         </div>
       </div>
     </div>
@@ -290,6 +303,9 @@
 <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
 <script type="text/javascript" src="../../static/vue/element/element.js"></script>
 <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
+<script type="text/javascript" src="../../static/js/marked.min.js"></script>
+<script type="text/javascript" src="../../static/lib/pdf/html2canvas.min.js"></script>
+<script type="text/javascript" src="../../static/lib/pdf/jspdf.umd.min.js"></script>
 <script>
   new Vue({
     el: '#app',
@@ -300,6 +316,7 @@
         enabled: false,
         config: {},
         enabledLoading: false,
+        pdfLoading: false,
         triggerLoading: false,
         triggerPeriod: '',
         reportsLoading: false,
@@ -314,6 +331,15 @@
         if (cron === '0 0 2 * * ?') return '姣忓ぉ鍑屾櫒 2:00';
         if (cron === '0 30 0 * * ?') return '姣忓ぉ 0:30';
         return cron;
+      },
+      renderedSummary: function() {
+        var md = this.selectedReport && this.selectedReport.summary;
+        if (!md) return '<p style="color:#999;">鏆傛棤鎶ュ憡鍐呭</p>';
+        try {
+          return marked.parse(md);
+        } catch (e) {
+          return '<pre>' + md.replace(/</g, '&lt;') + '</pre>';
+        }
       }
     },
     mounted: function() {
@@ -443,6 +469,78 @@
         var pad = function(n) { return n < 10 ? '0' + n : n; };
         return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
           + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
+      },
+      downloadPdf: function() {
+        var self = this;
+        var el = document.getElementById('reportContent');
+        if (!el) return;
+        self.pdfLoading = true;
+
+        // 涓存椂绉婚櫎婊氬姩闄愬埗锛岃 html2canvas 鎹曡幏瀹屾暣鍐呭
+        var origMaxHeight = el.style.maxHeight;
+        var origOverflow = el.style.maxHeight;
+        var scrollEl = el.querySelector('.markdown-body');
+        if (scrollEl) {
+          scrollEl.style.maxHeight = 'none';
+          scrollEl.style.overflow = 'visible';
+        }
+
+        html2canvas(el, {
+          scale: 2,
+          useCORS: true,
+          backgroundColor: '#ffffff',
+          width: el.scrollWidth,
+          height: el.scrollHeight,
+          windowWidth: el.scrollWidth,
+          windowHeight: el.scrollHeight
+        }).then(function(canvas) {
+          // 鎭㈠婊氬姩闄愬埗
+          if (scrollEl) {
+            scrollEl.style.maxHeight = origMaxHeight || '';
+            scrollEl.style.overflow = origOverflow || '';
+          }
+
+          var pdf = new jspdf.jsPDF('p', 'mm', 'a4');
+          var pdfWidth = pdf.internal.pageSize.getWidth();
+          var pdfHeight = pdf.internal.pageSize.getHeight();
+          var margin = 15;
+          var contentWidth = pdfWidth - margin * 2;
+
+          // 鎸� A4 椤甸潰楂樺害鍒嗛〉
+          var pageContentHeight = pdfHeight - margin * 2;
+          var imgRatio = canvas.width / contentWidth;
+          var totalImgHeightPx = canvas.height;
+          var pageImgHeightPx = pageContentHeight * imgRatio;
+          var srcY = 0;
+          var page = 0;
+
+          while (srcY < totalImgHeightPx) {
+            if (page > 0) pdf.addPage();
+            var sliceH = Math.min(pageImgHeightPx, totalImgHeightPx - srcY);
+            var sliceCanvas = document.createElement('canvas');
+            sliceCanvas.width = canvas.width;
+            sliceCanvas.height = sliceH;
+            var ctx = sliceCanvas.getContext('2d');
+            ctx.fillStyle = '#ffffff';
+            ctx.fillRect(0, 0, sliceCanvas.width, sliceCanvas.height);
+            ctx.drawImage(canvas, 0, srcY, canvas.width, sliceH, 0, 0, canvas.width, sliceH);
+            var sliceImgH = sliceH / imgRatio;
+            pdf.addImage(sliceCanvas.toDataURL('image/png'), 'PNG', margin, margin, contentWidth, sliceImgH);
+            srcY += sliceH;
+            page++;
+          }
+
+          var fileName = 'WCS鏁版嵁鍒嗘瀽鎶ュ憡_' + (self.selectedReport.periodType || '') + '_' + self.formatTime(self.selectedReport.createTime).replace(/[: ]/g, '-') + '.pdf';
+          pdf.save(fileName);
+          self.pdfLoading = false;
+        }).catch(function() {
+          if (scrollEl) {
+            scrollEl.style.maxHeight = origMaxHeight || '';
+            scrollEl.style.overflow = origOverflow || '';
+          }
+          self.pdfLoading = false;
+          self.$message.error('PDF 鐢熸垚澶辫触');
+        });
       }
     }
   });

--
Gitblit v1.9.1