最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue中的ofd/pdf預(yù)覽實現(xiàn)過程

 更新時間:2025年10月25日 09:05:29   作者:Qredsun  
這篇文章主要介紹了vue中的ofd/pdf預(yù)覽實現(xiàn)過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

背景

實現(xiàn)預(yù)覽ofd/pdf超鏈接功能

業(yè)務(wù)實現(xiàn)

1.pdf的預(yù)覽

實現(xiàn)方式:

1.直接使用 <iframe :src="${url}#navpanes=0&toolbar=0" /> 實現(xiàn)pdf的預(yù)覽。

  • navpanes=0 隱藏側(cè)邊欄
  • toolbar=0 隱藏頂部工具欄

2.使用pdf.js,代碼先行:

<template>
  <a-tabs
    v-if="props.urls.length > 0"
    :default-active-key="activateTab"
    type="card"
    class="pdf-tabs"
    @change="tabChangeHandler"
  >
    <a-tab-pane v-for="url in props.urls" :key="url" :tab="fileName(url)">
      <div class="pdf-container">
        <canvas
          v-if="url.endsWith('.pdf')"
          class="canvas"
          :ref="(el) => (canvasRefs[url] = el)"
        ></canvas>
        <a-button class="mb-2" type="link" @click="handleDownload(url)">
          {{ fileName(url) }}
        </a-button>
      </div>
    </a-tab-pane>
  </a-tabs>
</template>

<script lang="ts" setup>
import { ref, watch, nextTick } from 'vue'
import * as pdfjsLib from 'pdfjs-dist'
import { debounce } from 'lodash-es'
import { saveAs } from 'file-saver'
import EasyOFD from 'easyofd'

interface Props {
  urls?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  urls: () => [],
})
const url = ref<string>('')
const activateTab = ref<string>('')
const canvasRefs = ref<Record<string, HTMLCanvasElement | null>>({})

// 文件類型判斷
const ext = ref<string>('pdf')
const isOfd = ref<boolean>(false)
const isPdf = ref<boolean>(false)

//  設(shè)置 PDF.js worker 路徑(推薦方式)
pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.mjs'

//  從 URL 中提取文件名
function fileName(url: string): string {
  try {
    const decodeURL = decodeURIComponent(url).split('/')
    const lastSegment = decodeURL[decodeURL.length - 1]

    const firstIndex = lastSegment.indexOf('-')
    const lastIndex = lastSegment.lastIndexOf('-')

    if (firstIndex === -1 || lastIndex === -1 || lastIndex <= firstIndex) {
      return lastSegment.split('.')[0] // fallback 文件名
    }

    const name = lastSegment.substring(firstIndex + 1, lastIndex)
    const ext = name.split('.').pop()

    if (['pdf', 'ofd'].includes(ext ?? '')) {
      return name.substring(0, name.lastIndexOf('.'))
    }

    return name
  } catch {
    return 'unknown'
  }
}

// 獲取文件類型
const getFileType = (url: string) => {
  const decodeURL = decodeURIComponent(url)
  ext.value = decodeURL.endsWith('.pdf') ? 'pdf' : 'ofd'
  isPdf.value = ext.value === 'pdf'
  isOfd.value = ext.value === 'ofd'
  isPdf.value ? loadAndRenderPdf(url) : loadAndRenderOfd(url)
}
//  下載文件
const handleDownload = debounce((url: string) => {
  saveAs(url, `${fileName(url)}.${ext.value}`)
}, 300)

//  加載并渲染 PDF
async function loadAndRenderPdf(pdfUrl: string) {
  try {
    const canvas = canvasRefs.value[pdfUrl]
    if (!canvas) return

    const loadingTask = pdfjsLib.getDocument(pdfUrl)
    const pdf = await loadingTask.promise
    const page = await pdf.getPage(1)

    const viewport = page.getViewport({ scale: 1.3 })

    canvas.height = viewport.height
    canvas.width = viewport.width

    const context = canvas.getContext('2d')
    if (!context) return

    const renderContext = {
      canvasContext: context,
      viewport,
    }

    await page.render(renderContext).promise
  } catch (error) {
    console.error('PDF 渲染失敗:', error)
  }
}

//  標(biāo)簽頁切換時加載 PDF
async function tabChangeHandler(key: string) {
  url.value = key
  activateTab.value = fileName(key)

  await nextTick() // 等待 DOM 更新
  if (key.endsWith('.pdf')) {
    await loadAndRenderPdf(key)
  }
}

//  頁面初始化時自動加載第一個 PDF
watch(
  () => props.urls,
  async (newUrls) => {
    if (newUrls && newUrls.length > 0) {
      console.log('newUrls:', newUrls)
      url.value = newUrls[0]
      activateTab.value = fileName(newUrls[0])
      await nextTick()
      getFileType(newUrls[0])
    }
  },
  { immediate: true },
)
</script>

<style lang="less" scoped>
.canvas {
  border: 1px solid #000;
  width: 100%; // 響應(yīng)式寬度
  border: 1px solid #000;
}
.pdf-container {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 12px;
  max-width: 100%; // 限制最大寬度
  max-height: 400px;
  overflow: auto;
}
</style>

說一下重點:

問題一: 通過命令pnpm install pdf.js安裝后,通常出現(xiàn)引用問題;Cannot resolve pdf.worker.entry。代碼中使用的版本"pdfjs-dist": "^5.2.133"

import * as pdfjsLib from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';

pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;

解決方案:

ps: 上面的代碼中包含了文件的下載功能,需要安裝 "file-saver": "^2.0.5",

將文件從node_modules/pdfjs-dist/build/pdf.worker.min.mjs移動至項目的public/pdf.worker.min.mjs,可以使用命令 cp node_modules/pdfjs-dist/build/pdf.worker.min.mjs public/pdf.worker.min.mjs

修改引用:

import * as pdfjsLib from 'pdfjs-dist';

pdfjsLib.GlobalWorkerOptions.workerSrc = '/pdf.worker.min.mjs'

2.ofd的預(yù)覽

實現(xiàn)方式:http://www.easyofd.cn/

安裝的依賴:

pnpm -i jszip x2js jb2 opentype.js easyofd

業(yè)務(wù)實現(xiàn):

<template>
  <div ref="containerRef" style="width: 100%; height: 800px;"></div>
</template>

<script setup>
import EasyOFD from "easyofd"
import { ref, onMounted } from 'vue'

const containerRef = ref(null)

onMounted(async () => {
  if (!containerRef.value) {
    console.error('OFD 容器不存在')
    return
  }

  const ofd = new EasyOFD('myOFD', containerRef.value)

  try {
    const response = await fetch('/files/sample.ofd')
    const blob = await response.blob()
    ofd.loadFromBlob(blob)
  } catch (e) {
    console.error('OFD 加載失敗:', e)
  }
})
</script>
	
<style lang="less" scoped>
// 隱藏右側(cè)的ppi模塊,減少空白
:deep(#myOFD-ppi) {
  display: none;
}
// 增加邊框
:deep(#myOFD-ofd-canvas) {
  border: 1px solid #000;
}
// 隱藏頂部按鈕
:deep(.OfdButton) {
  display: none !important;
}
</style>

官網(wǎng)效果:(easyOfd官網(wǎng)手冊)

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue.js動態(tài)添加、刪除選題的實例代碼

    Vue.js動態(tài)添加、刪除選題的實例代碼

    這篇文章主要介紹了Vue.js動態(tài)添加、刪除選題的實例代碼,非常不錯具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • vue的h5日歷組件實現(xiàn)詳解

    vue的h5日歷組件實現(xiàn)詳解

    這篇文章主要為大家詳細(xì)介紹了vue的h5日歷組件使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Vue項目實現(xiàn)i18n國際化多語言切換方案實踐

    Vue項目實現(xiàn)i18n國際化多語言切換方案實踐

    本文將圍繞Vue項目國際化,借助官方提供的Vue I18n插件,介紹,講解安裝配置、多語言切換、復(fù)雜場景應(yīng)用及優(yōu)化等,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2025-09-09
  • vue3+vite使用element-plus問題

    vue3+vite使用element-plus問題

    這篇文章主要介紹了vue3+vite使用element-plus問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • VUE識別訪問設(shè)備是pc端還是移動端的實現(xiàn)步驟

    VUE識別訪問設(shè)備是pc端還是移動端的實現(xiàn)步驟

    經(jīng)常在項目中會有支持pc與手機(jī)端需求,并且pc與手機(jī)端是兩個不一樣的頁面,這時就要求判斷設(shè)置,下面這篇文章主要給大家介紹了關(guān)于VUE識別訪問設(shè)備是pc端還是移動端的相關(guān)資料,需要的朋友可以參考下
    2023-05-05
  • vue穿梭框?qū)崿F(xiàn)上下移動

    vue穿梭框?qū)崿F(xiàn)上下移動

    這篇文章主要為大家詳細(xì)介紹了vue穿梭框?qū)崿F(xiàn)上下移動,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • vue如何從后臺獲取數(shù)據(jù)生成動態(tài)菜單列表

    vue如何從后臺獲取數(shù)據(jù)生成動態(tài)菜單列表

    這篇文章主要介紹了vue如何從后臺獲取數(shù)據(jù)生成動態(tài)菜單列表,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue-router的導(dǎo)航守衛(wèi)使用最新講解

    vue-router的導(dǎo)航守衛(wèi)使用最新講解

    這篇文章主要介紹了vue-router的導(dǎo)航守衛(wèi)使用講解,vue-router提供了許多編程式導(dǎo)航的API,其中最常見的導(dǎo)航API有很多種,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2022-12-12
  • vue中使用vue-router切換頁面時滾動條自動滾動到頂部的方法

    vue中使用vue-router切換頁面時滾動條自動滾動到頂部的方法

    這篇文章主要介紹了vue項目中在使用vue-router切換頁面的時候滾動條自動滾動到頂部的實現(xiàn)方法,一般使用Window scrollTo() 方法實現(xiàn),本文給大家簡單介紹了crollTop的使用,需要的朋友可以參考下
    2017-11-11
  • Vue 中v-model的完整用法及v-model的實現(xiàn)原理解析

    Vue 中v-model的完整用法及v-model的實現(xiàn)原理解析

    這篇文章詳細(xì)介紹了Vue.js中的v-model指令的使用,包括基本用法、原理、結(jié)合不同類型的表單元素(如radio、checkbox、select)以及使用修飾符(如lazy、number、trim)等,感興趣的朋友一起看看吧
    2025-02-02

最新評論

凤山县| 佛学| 林甸县| 阳江市| 乐业县| 溧水县| 白山市| 柘城县| 遵义县| 义马市| 松滋市| 泸州市| 兴文县| 巨鹿县| 景德镇市| 萝北县| 夏河县| 玛曲县| 积石山| 平定县| 樟树市| 台江县| 公主岭市| 寿宁县| 龙游县| 阳春市| 平乐县| 运城市| 徐汇区| 岢岚县| 永和县| 辰溪县| 宣城市| 肃宁县| 泸州市| 建瓯市| 东安县| 裕民县| 五莲县| 广安市| 独山县|