预览PDF,下载Word

4 阅读1分钟

🎯 目标

  1. 前端打开新标签页预览 PDF(来自后端生成的 PDF blob)。

  2. 该标签页中提供一个“下载 Word 文件”的按钮。

  3. 点击按钮后,调用你自己定义的方法(比如调接口获取 Word 文件 blob 并下载)。

用 Vue 3 Composition API 封装整套,并贴完整版本

✅ 功能说明

  1. 后端返回 PDF Blob,新标签页预览

  2. 子页面中有一个按钮 点击可触发主页面下载 Word 文件

  3. 主页面通过 Axios 调接口获取 Word Blob 并下载

🧩 第一步:封装组合式函数 usePdfPreviewWithWordDownload.ts

// composables/usePdfPreviewWithWordDownload.ts
import { onMounted, onBeforeUnmount } from "vue";

export function usePdfPreviewWithWordDownload(
  downloadWordFile: () => Promise<Blob>
) {
  const openPdfPreview = (pdfBlob: Blob) => {
    const blobUrl = URL.createObjectURL(pdfBlob);

    const html = `
      <html>
        <head>
          <title>PDF预览</title>
          <style>
            body { margin: 0; }
            #downloadBtn {
              position: fixed;
              top: 16px;
              right: 16px;
              z-index: 1000;
              background: #409EFF;
              color: white;
              border: none;
              padding: 8px 14px;
              font-size: 14px;
              cursor: pointer;
              border-radius: 4px;
            }
          </style>
        </head>
        <body>
          <button id="downloadBtn">下载 Word</button>
          <embed src="${blobUrl}" type="application/pdf" width="100%" height="100%"/>
          <script>
            window.addEventListener('DOMContentLoaded', () => {
              const btn = document.getElementById('downloadBtn');
              btn.addEventListener('click', () => {
                window.opener.postMessage({ type: 'DOWNLOAD_WORD' }, '*');
              });
            });
          </script>
        </body>
      </html>
    `;

    const previewWindow = window.open();
    if (previewWindow) {
      previewWindow.document.write(html);
      previewWindow.document.close();
    } else {
      console.warn("浏览器拦截了新标签页");
    }
  };

  // 监听子页面请求下载 Word
  const messageHandler = async (event: MessageEvent) => {
    if (event.data?.type === "DOWNLOAD_WORD") {
      try {
        const wordBlob = await downloadWordFile();
        const url = URL.createObjectURL(wordBlob);
        const a = document.createElement("a");
        a.href = url;
        a.download = "通知.docx";
        a.click();
        URL.revokeObjectURL(url);
      } catch (err) {
        console.error("下载 Word 文件失败", err);
      }
    }
  };

  onMounted(() => {
    window.addEventListener("message", messageHandler);
  });

  onBeforeUnmount(() => {
    window.removeEventListener("message", messageHandler);
  });

  return {
    openPdfPreview,
  };
}

✅ 第二步:组件中使用

<!-- components/PdfPreview.vue -->
<template>
  <el-button type="primary" @click="handlePreview">预览 PDF 并下载 Word</el-button>
</template>

<script setup lang="ts">
import { usePdfPreviewWithWordDownload } from '@/composables/usePdfPreviewWithWordDownload';
import axios from 'axios';

// 你自己的 API 封装(可替换)
const fetchPdfBlob = async (): Promise<Blob> => {
  const res = await axios.post('/api/pdf', {}, { responseType: 'blob' });
  return new Blob([res.data], { type: 'application/pdf' });
};

const fetchWordBlob = async (): Promise<Blob> => {
  const res = await axios.post('/api/word', {}, { responseType: 'blob' });
  return new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
};

const { openPdfPreview } = usePdfPreviewWithWordDownload(fetchWordBlob);

const handlePreview = async () => {
  const pdfBlob = await fetchPdfBlob();
  openPdfPreview(pdfBlob);
};
</script>

✅ 效果展示

  1. 点击按钮 ➜ 打开新标签页 ➜ 显示 PDF 内容

  2. 右上角按钮 ➜ 触发主页面下载 Word 文件(无需暴露 Word 地址)