Jelajahi Sumber

在下载功能中添加了文档预览和强制下载选项,更新了相关日志记录。修改了前端页面以支持文档预览功能,并添加了样式和交互逻辑。

xbx 1 bulan lalu
induk
melakukan
13a965faf2
2 mengubah file dengan 161 tambahan dan 10 penghapusan
  1. 47 5
      app.py
  2. 114 5
      upload.html

+ 47 - 5
app.py

@@ -141,7 +141,7 @@ def process_file():
 @app.route('/download/<filename>')
 def download_file(filename):
     """
-    提供处理后文档的下载
+    提供处理后文档的下载或预览
     
     参数:
         filename: 要下载的文件名
@@ -149,7 +149,34 @@ def download_file(filename):
     返回:
         处理后的文档文件
     """
-    logger.info(f"请求下载文件: {filename}")
+    logger.info(f"请求下载/预览文件: {filename}")
+    return serve_file(filename, False)
+
+@app.route('/download-attachment/<filename>')
+def download_file_attachment(filename):
+    """
+    提供处理后文档的强制下载
+    
+    参数:
+        filename: 要下载的文件名
+        
+    返回:
+        处理后的文档文件(作为附件)
+    """
+    logger.info(f"请求强制下载文件: {filename}")
+    return serve_file(filename, True)
+
+def serve_file(filename, as_attachment):
+    """
+    通用文件服务函数
+    
+    参数:
+        filename: 要提供的文件名
+        as_attachment: 是否作为附件下载
+        
+    返回:
+        文件响应
+    """
     try:
         # 获取当天的日期文件夹
         today_folder = get_date_folder(app.config['OUTPUT_FOLDER'])
@@ -157,12 +184,22 @@ def download_file(filename):
         # 首先尝试从当天的文件夹中查找
         if os.path.exists(os.path.join(today_folder, filename)):
             logger.info(f"文件在当天文件夹中找到: {os.path.join(today_folder, filename)}")
-            return send_from_directory(today_folder, filename)
+            return send_from_directory(
+                today_folder, 
+                filename,
+                mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+                as_attachment=as_attachment
+            )
         
         # 如果当天文件夹中没有,则尝试在输出根目录查找(兼容旧文件)
         if os.path.exists(os.path.join(app.config['OUTPUT_FOLDER'], filename)):
             logger.info(f"文件在输出根目录中找到: {os.path.join(app.config['OUTPUT_FOLDER'], filename)}")
-            return send_from_directory(app.config['OUTPUT_FOLDER'], filename)
+            return send_from_directory(
+                app.config['OUTPUT_FOLDER'], 
+                filename,
+                mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+                as_attachment=as_attachment
+            )
         
         # 如果还是没找到,尝试在其他日期文件夹中查找
         for item in os.listdir(app.config['OUTPUT_FOLDER']):
@@ -171,7 +208,12 @@ def download_file(filename):
                 file_path = os.path.join(item_path, filename)
                 if os.path.exists(file_path):
                     logger.info(f"文件在日期文件夹中找到: {file_path}")
-                    return send_from_directory(item_path, filename)
+                    return send_from_directory(
+                        item_path, 
+                        filename,
+                        mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+                        as_attachment=as_attachment
+                    )
         
         # 如果所有位置都没找到,返回404错误
         logger.warning(f"找不到请求的文件: {filename}")

+ 114 - 5
upload.html

@@ -5,6 +5,8 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Word模板替换</title>
     <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
+    <!-- 添加docx-preview库 -->
+    <script src="https://unpkg.com/docx-preview@0.1.18/dist/docx-preview.js"></script>
     <style>
       /* 全局样式 */
       body {
@@ -150,6 +152,49 @@
       .remove-field-btn:hover {
         background-color: #c9302c;
       }
+
+      /* 预览按钮样式 */
+      .preview-btn {
+        display: inline-block;
+        margin-top: 15px;
+        margin-left: 10px;
+        padding: 8px 15px;
+        background-color: #f0ad4e;
+        color: white;
+        text-decoration: none;
+        border-radius: 4px;
+        cursor: pointer;
+      }
+      .preview-btn:hover {
+        background-color: #ec971f;
+      }
+
+      /* 预览容器样式 */
+      #previewContainer {
+        margin-top: 20px;
+        padding: 20px;
+        border: 1px solid #ddd;
+        border-radius: 4px;
+        display: none;
+        background-color: white;
+        max-height: 500px;
+        overflow-y: auto;
+      }
+      
+      /* 文档操作区域样式 */
+      .doc-actions {
+        display: flex;
+        justify-content: center;
+        gap: 10px;
+      }
+      
+      /* 加载中样式 */
+      .loading {
+        text-align: center;
+        padding: 20px;
+        font-style: italic;
+        color: #666;
+      }
     </style>
   </head>
   <body>
@@ -355,10 +400,17 @@
 
       <!-- 下载区域 -->
       <div id="downloadSection" style="display: none; margin-top: 20px">
-        <p>文档已成功生成,点击下面的链接下载:</p>
-        <a id="downloadLink" href="#" class="download-link" download
-          >下载文档</a
-        >
+        <p>文档已成功生成:</p>
+        <div class="doc-actions">
+          <a id="downloadLink" href="#" class="download-link" download>下载文档</a>
+          <button id="previewBtn" class="preview-btn">预览文档</button>
+        </div>
+      </div>
+      
+      <!-- 文档预览区域 -->
+      <div id="previewContainer">
+        <div id="previewLoading" class="loading">正在加载预览,请稍候...</div>
+        <div id="previewContent"></div>
       </div>
     </div>
 
@@ -369,6 +421,13 @@
       const downloadSection = document.getElementById("downloadSection");
       const downloadLink = document.getElementById("downloadLink");
       const fieldsContainer = document.getElementById("fieldsContainer");
+      const previewBtn = document.getElementById("previewBtn");
+      const previewContainer = document.getElementById("previewContainer");
+      const previewLoading = document.getElementById("previewLoading");
+      const previewContent = document.getElementById("previewContent");
+      
+      // 当前文件名
+      let currentFileName = null;
       
       // 获取当前页面的主机名和端口,用于API请求
       const apiBaseUrl = `${window.location.protocol}//${window.location.hostname}:5000`;
@@ -402,6 +461,9 @@
         generateBtn.textContent = "处理中...";
         statusMessage.className = "status";
         statusMessage.style.display = "none";
+        
+        // 隐藏预览容器
+        previewContainer.style.display = "none";
 
         // 发送请求到Flask后端
         axios
@@ -417,8 +479,11 @@
             statusMessage.textContent = `成功: ${response.data.message}`;
             console.log("成功:", response.data);
 
+            // 保存当前文件名
+            currentFileName = response.data.output_filename;
+            
             // 更新下载链接
-            downloadLink.href = `${apiBaseUrl}/download/${response.data.output_filename}`;
+            downloadLink.href = `${apiBaseUrl}/download-attachment/${currentFileName}`;
 
             // 显示下载部分
             downloadSection.style.display = "block";
@@ -443,6 +508,50 @@
             generateBtn.textContent = "生成文档";
           });
       });
+      
+      // 预览文档
+      previewBtn.addEventListener("click", function(e) {
+        e.preventDefault();
+        
+        if (!currentFileName) {
+          alert("没有可预览的文档");
+          return;
+        }
+        
+        // 显示预览容器和加载状态
+        previewContainer.style.display = "block";
+        previewLoading.style.display = "block";
+        previewContent.innerHTML = "";
+        
+        // 获取文档二进制数据
+        axios.get(`${apiBaseUrl}/download/${currentFileName}`, {
+          responseType: 'arraybuffer'
+        })
+        .then(response => {
+          // 使用docx-preview渲染文档
+          window.docx.renderAsync(response.data, previewContent, null, {
+            className: 'docx-viewer',
+            inWrapper: true
+          })
+          .then(() => {
+            // 预览加载完成,隐藏加载状态
+            previewLoading.style.display = "none";
+            
+            // 滚动到预览区域
+            previewContainer.scrollIntoView({ behavior: 'smooth' });
+          })
+          .catch(error => {
+            console.error('预览渲染错误:', error);
+            previewContent.innerHTML = `<div class="error">预览加载失败: ${error.message}</div>`;
+            previewLoading.style.display = "none";
+          });
+        })
+        .catch(error => {
+          console.error('文档下载错误:', error);
+          previewContent.innerHTML = `<div class="error">文档下载失败</div>`;
+          previewLoading.style.display = "none";
+        });
+      });
     </script>
   </body>
 </html>