vue2用高版本pdfjs,對PDF文件本地預覽解決方案

  • 使用Element UI和PDF.js實現(xiàn)無服務器上傳的PDF預覽功能


    image.png

    image.png

    image.png

使用的組件

  • npm install element-ui --save
  • vue

如果不自定義,就不用 npm install --save pdfjs-dist

用viewer.html 下載壓縮包,在public下解壓,頁面中使用就可以了


  
<template>
  <div id="app">
    <div class="container">
      <header>
        <h1>PDF文件本地預覽解決方案</h1>
        <p>使用Element UI和PDF.js實現(xiàn)無服務器上傳的PDF預覽功能</p>
      </header>

      <div class="preview-container">
        <div class="card">
          <h2>PDF文件上傳</h2>
          <el-upload
            class="upload-area"
            drag
            action="#"
            :auto-upload="false"
            :on-change="handleFileChange"
            :show-file-list="false"
            accept="application/pdf"
          >
            <i class="el-icon-upload upload-icon"></i>
            <div class="el-upload__text">
              將PDF文件拖到此處,或<em>點擊上傳</em>
            </div>
            <div class="el-upload__tip" slot="tip">
              僅支持PDF文件,且不超過10MB
            </div>
          </el-upload>

          <div v-if="file" class="status-bar">
            <div class="file-info">
              <i class="el-icon-document file-icon"></i>
              <span>{{ file.name }} ({{ formatFileSize(file.size) }})</span>
            </div>
            <el-button type="success" plain @click="previewPDF"
              >預覽文件</el-button
            >
          </div>
        </div>

        <div class="card">
          <h2>PDF預覽</h2>
          <div v-if="previewUrl" class="preview-frame">
            <iframe
              :src="previewUrl"
              width="100%"
              height="100%"
              frameborder="0"
            ></iframe>
          </div>
          <div v-else class="preview-frame">
            <div
              style="
                display: flex;
                justify-content: center;
                align-items: center;
                height: 100%;
              "
            >
              <div style="text-align: center">
                <i
                  class="el-icon-document"
                  style="font-size: 60px; color: #dcdfe6; margin-bottom: 20px"
                ></i>
                <p style="color: #909399; font-size: 18px">等待上傳PDF文件</p>
              </div>
            </div>
          </div>
        </div>
      </div>
<!-- 
      <div class="instructions">
        <h3>技術(shù)實現(xiàn)說明</h3>
        <ul>
          <li>使用Element UI的Upload組件實現(xiàn)文件選擇功能</li>
          <li>通過URL.createObjectURL生成本地Blob URL實現(xiàn)預覽</li>
          <li>采用PDF.js作為PDF渲染引擎,無需服務器支持</li>
          <li>完全本地處理,避免網(wǎng)絡(luò)請求和代理錯誤</li>
          <li>支持文件驗證(類型、大?。┖湾e誤處理</li>
        </ul>
      </div> -->

      <!-- <div class="features">
        <div class="feature-card">
          <i class="el-icon-lock"></i>
          <h3>安全可靠</h3>
          <p>文件僅在瀏覽器中處理,不會上傳到任何服務器</p>
        </div>
        <div class="feature-card">
          <i class="el-icon-thunderbolt"></i>
          <h3>快速預覽</h3>
          <p>無需等待上傳過程,即時預覽PDF文件</p>
        </div>
        <div class="feature-card">
          <i class="el-icon-cpu"></i>
          <h3>零服務器依賴</h3>
          <p>完全在瀏覽器端實現(xiàn),無需后端支持</p>
        </div>
      </div> -->

      <footer>
        <p>PDF本地預覽解決方案 | 基于Vue.js和Element UI | 無服務器上傳</p>
      </footer>
    </div>
  </div>
</template>
  <script>
export default {
  components: {

  },
  data() {
    return {
      file: null,
      previewUrl: null,
      previousBlobUrl: null,
    };
  },
  methods: {
    handleFileChange(file) {
      // 驗證文件類型
      if (file.raw.type !== "application/pdf") {
        this.$message.error("請上傳PDF格式的文件");
        return;
      }

      // 驗證文件大小 (10MB)
      if (file.raw.size > 20 * 1024 * 1024) {
        this.$message.error("文件大小不能超過10MB");
        return;
      }

      // 清理之前的文件引用
      this.revokePreviousBlob();

      this.file = file.raw;
      this.$message.success("文件已選擇,點擊預覽按鈕查看");
    },

    previewPDF() {
      if (!this.file) {
        this.$message.warning("請先選擇PDF文件");
        return;
      }

      this.revokePreviousBlob();

      try {
        // 生成Blob URL
        const blobUrl = URL.createObjectURL(this.file);
        this.previousBlobUrl = blobUrl;

        // 創(chuàng)建PDF.js預覽URL
        const viewerPath = "/pdfjs2/web/viewer.html";
        const encodedUrl = encodeURIComponent(blobUrl);
        this.previewUrl = `${viewerPath}?file=${encodedUrl}`;

        this.$message.success("PDF預覽已加載");
      } catch (error) {
        console.error("預覽失敗:", error);
        this.$message.error("預覽失敗: " + error.message);
      }
    },

    revokePreviousBlob() {
      if (this.previousBlobUrl) {
        URL.revokeObjectURL(this.previousBlobUrl);
        this.previousBlobUrl = null;
      }
    },

    formatFileSize(bytes) {
      if (bytes === 0) return "0 Bytes";
      const k = 1024;
      const sizes = ["Bytes", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    },
  },
  beforeDestroy() {
    this.revokePreviousBlob();
  },
};
</script>



<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Helvetica Neue", Arial, sans-serif;
}
body {
  background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
  min-height: 100vh;
  padding: 20px;
}
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 30px;
  background-color: white;
  border-radius: 15px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
header {
  text-align: center;
  margin-bottom: 40px;
}
header h1 {
  color: #2c3e50;
  font-size: 2.5rem;
  margin-bottom: 10px;
}
header p {
  color: #7f8c8d;
  font-size: 1.1rem;
}
.card {
  background: white;
  border-radius: 12px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
  padding: 25px;
  margin-bottom: 30px;
  transition: transform 0.3s ease;
}
.card:hover {
  transform: translateY(-5px);
}
.card h2 {
  color: #3498db;
  margin-bottom: 20px;
  padding-bottom: 10px;
  border-bottom: 2px solid #f0f5ff;
}
.preview-container {
  display: flex;
  flex-direction: column;
  gap: 30px;
}
.upload-area {
  border: 2px dashed #dcdfe6;
  border-radius: 10px;
  padding: 40px 20px;
  text-align: center;
  background-color: #fafafa;
  cursor: pointer;
  transition: all 0.3s;
}
.upload-area:hover {
  border-color: #409eff;
  background-color: #f0f7ff;
}
.upload-icon {
  font-size: 50px;
  color: #409eff;
  margin-bottom: 15px;
}
.preview-frame {
  height: 70vh;
  width: 100%;
  border: 1px solid #ebeef5;
  border-radius: 8px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  background-color: #f8f9fa;
}
.instructions {
  background-color: #f8f9fa;
  border-left: 4px solid #3498db;
  padding: 15px;
  margin-top: 20px;
  border-radius: 0 8px 8px 0;
}
.instructions h3 {
  color: #2c3e50;
  margin-bottom: 10px;
}
.instructions ul {
  padding-left: 20px;
  color: #555;
}
.instructions li {
  margin-bottom: 8px;
  line-height: 1.5;
}
footer {
  text-align: center;
  margin-top: 40px;
  color: #7f8c8d;
  font-size: 0.9rem;
}
.status-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #eaf6ff;
  padding: 10px 15px;
  border-radius: 6px;
  margin-top: 15px;
  font-size: 14px;
}
.file-info {
  display: flex;
  align-items: center;
  gap: 10px;
}
.file-icon {
  color: #e74c3c;
  font-size: 20px;
}
.features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 20px;
  margin-top: 30px;
}
.feature-card {
  background: #f8f9fa;
  border-radius: 10px;
  padding: 20px;
  text-align: center;
  border: 1px solid #eaeaea;
}
.feature-card i {
  font-size: 36px;
  color: #3498db;
  margin-bottom: 15px;
}
.feature-card h3 {
  margin-bottom: 10px;
  color: #2c3e50;
}


/* 淺色主題覆蓋樣式 */
::v-ddep :root {
  --primary-color: #f0f0f0;
  --secondary-color: #ffffff;
  --text-color: #333333;
  --border-color: #e0e0e0;
  --hover-color: #e6f7ff;
}

/* PDF.js v1.10.100 淺色主題樣式 */

/* 基礎(chǔ)樣式 */
/* body {
  background-color: #f5f7fa !important;
  color: #333 !important;
  font-family: Arial, sans-serif !important;
} */
::v-deep #toolbarSideba{
    background-color: #ffffff !important;
  border-bottom: 1px solid #e1e4e8 !important;
}
::v-deep #sidebarContainer{
    background-color: #ffffff !important;
  border-bottom: 1px solid #e1e4e8 !important;
}

/* 工具欄 */
#toolbar {
  background-color: #ffffff !important;
  border-bottom: 1px solid #e1e4e8 !important;
}

.toolbarButton {
  color: #586069 !important;
  background-color: transparent !important;
  border: none !important;
}

.toolbarButton:hover {
  background-color: #f1f5f9 !important;
  color: #2d3748 !important;
  border-radius: 4px !important;
}

.toolbarButton[disabled] {
  color: #d1d5da !important;
}

/* 縮放控制 */
#scaleSelectContainer .dropdown {
  background-color: #ffffff !important;
  border: 1px solid #e1e4e8 !important;
  color: #333 !important;
}

/* 側(cè)邊欄 */
#sidebar {
  background-color: #ffffff !important;
  border-right: 1px solid #e1e4e8 !important;
}

#sidebarContent {
  background-color: #ffffff !important;
}

/* 縮略圖視圖 */
#thumbs {
  background-color: #ffffff !important;
}

.thumb {
  border: 1px solid transparent !important;
}

.thumb.selected {
  border-color: #4299e1 !important;
  background-color: #ebf8ff !important;
}

/* 大綱視圖 */
#outline {
  background-color: #ffffff !important;
}

.outlineItem {
  color: #333 !important;
}

.outlineItem:hover {
  background-color: #f1f5f9 !important;
}

.outlineItem.active {
  color: #2b6cb0 !important;
  background-color: #ebf8ff !important;
}

/* 文檔容器 */
#viewer {
  background-color: #e9ecef !important;
}

.page {
  background-color: #ffffff !important;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06) !important;
  margin: 10px auto !important;
}

.page:hover {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
}

/* 搜索面板 */
#findbar {
  background-color: #ffffff !important;
  border-top: 1px solid #e1e4e8 !important;
}

#findInput {
  background-color: #f9fafb !important;
  border: 1px solid #e1e4e8 !important;
  color: #333 !important;
}

.findbarButton {
  color: #586069 !important;
  background-color: transparent !important;
}

.findbarButton:hover {
  background-color: #f1f5f9 !important;
}

/* 上下文菜單 */
.contextMenu {
  background-color: #ffffff !important;
  border: 1px solid #e1e4e8 !important;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1) !important;
}

.contextMenuItem {
  color: #333 !important;
}

.contextMenuItem:hover {
  background-color: #f1f5f9 !important;
  color: #2d3748 !important;
}

/* 加載指示器 */
#loadingBox {
  background-color: rgba(255, 255, 255, 0.9) !important;
}

#loadingIndicator {
  color: #4a5568 !important;
}

/* 錯誤消息 */
#errorMessage {
  background-color: #fff5f5 !important;
  color: #742a2a !important;
  border: 1px solid #ffe3e3 !important;
}

/* 打印對話框 */
.printDialog {
  background-color: #ffffff !important;
  border: 1px solid #e1e4e8 !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;
}

.printDialogTitle {
  color: #333 !important;
  border-bottom: 1px solid #e1e4e8 !important;
}

.printDialogButton {
  background-color: #f5f5f5 !important;
  color: #333 !important;
  border: 1px solid #ddd !important;
}

.printDialogButton:hover {
  background-color: #e9e9e9 !important;
}

/* 文本層樣式 */
.textLayer {
  color: #1a202c !important;
}

/* 鏈接樣式 */
a {
  color: #2b6cb0 !important;
}

a:hover {
  color: #1a365d !important;
}
    
</style>

1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容