PdfView.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <div class="home_wrap">
  3. <div class="pdf_down">
  4. <div class="pdf_set_left" @click="scaleD">➕</div>
  5. <div class="pdf_set_middle" @click="scaleX">➖</div>
  6. <!-- <div class="pdf-pre" @click="prePage">上一页</div> -->
  7. <!-- <div class="pdf-next" @click="nextPage">下一页</div> -->
  8. </div>
  9. <div :style="{ width: pdf_div_width, margin: '0 auto' }">
  10. <!-- <canvas id="the_canvas"></canvas> -->
  11. <canvas
  12. v-for="page in pdf_pages"
  13. :id="'the_canvas' + page"
  14. :key="page"
  15. ></canvas>
  16. </div>
  17. </div>
  18. </template>
  19. <script setup>
  20. import { ref, onMounted } from "vue"
  21. import * as PDF from "pdfjs-dist"
  22. // 引入 Vite 识别的 worker URL(处理哈希路径)
  23. import localWorkerUrl from "pdfjs-dist/build/pdf.worker.mjs?url"
  24. let workerBlobUrl = null
  25. const initWorker = async () => {
  26. if (workerBlobUrl) return
  27. const pdfjsVersion = PDF.version
  28. const urls = [
  29. // 1. 优先使用 CDN,规避所有本地服务器配置问题
  30. `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjsVersion}/build/pdf.worker.min.mjs`,
  31. `https://unpkg.com/pdfjs-dist@${pdfjsVersion}/build/pdf.worker.min.mjs`,
  32. // 2. 本地备选:Vite 处理后的路径
  33. localWorkerUrl,
  34. // 3. 库模式下的路径
  35. new URL("./pdf.worker.js", import.meta.url).toString(),
  36. ]
  37. let lastError = null
  38. for (const url of urls) {
  39. try {
  40. const response = await fetch(url)
  41. if (!response.ok) continue
  42. const arrayBuffer = await response.arrayBuffer()
  43. // 强制指定为 ESM 模块
  44. const blob = new Blob([arrayBuffer], { type: "text/javascript" })
  45. workerBlobUrl = URL.createObjectURL(blob)
  46. PDF.GlobalWorkerOptions.workerSrc = workerBlobUrl
  47. console.log("PDF Worker loaded successfully from:", url)
  48. return
  49. } catch (e) {
  50. lastError = e
  51. continue
  52. }
  53. }
  54. console.error("All PDF worker paths failed", lastError)
  55. // 最后保底:直接尝试 localWorkerUrl
  56. PDF.GlobalWorkerOptions.workerSrc = localWorkerUrl
  57. }
  58. const props = defineProps({
  59. data: Array,
  60. })
  61. const pdf_scale = ref(1.0)
  62. const pdf_pages = ref([])
  63. const pdf_div_width = ref("")
  64. const currentPage = ref(1)
  65. let pdfDoc = null
  66. const scaleD = () => {
  67. let max = 0
  68. if (window.screen.width > 1440) {
  69. max = 1.4
  70. } else {
  71. max = 1.2
  72. }
  73. if (pdf_scale.value >= max) {
  74. return
  75. }
  76. pdf_scale.value = pdf_scale.value + 0.1
  77. loadFile()
  78. }
  79. const scaleX = () => {
  80. let min = 1.0
  81. if (pdf_scale.value <= min) {
  82. return
  83. }
  84. pdf_scale.value = pdf_scale.value - 0.1
  85. loadFile()
  86. }
  87. const loadFile = async () => {
  88. await initWorker()
  89. pdfDoc = await PDF.getDocument(props.data).promise
  90. pdf_pages.value = pdfDoc.numPages
  91. renderPage()
  92. }
  93. const renderPage = async (num = 1) => {
  94. currentPage.value = num
  95. const page = await pdfDoc.getPage(num)
  96. const canvas = document.getElementById("the_canvas" + num)
  97. const ctx = canvas.getContext("2d")
  98. const dpr = window.devicePixelRatio || 1
  99. const bsr =
  100. ctx.webkitBackingStorePixelRatio ||
  101. ctx.mozBackingStorePixelRatio ||
  102. ctx.msBackingStorePixelRatio ||
  103. ctx.oBackingStorePixelRatio ||
  104. ctx.backingStorePixelRatio ||
  105. 1
  106. const ratio = dpr / bsr
  107. const viewport = page.getViewport({ scale: pdf_scale.value })
  108. canvas.width = viewport.width * ratio
  109. canvas.height = viewport.height * ratio
  110. canvas.style.width = viewport.width + "px"
  111. pdf_div_width.value = viewport.width + "px"
  112. canvas.style.height = viewport.height + "px"
  113. ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
  114. const renderContext = {
  115. canvasContext: ctx,
  116. viewport: viewport,
  117. }
  118. page.render(renderContext)
  119. if (pdf_pages.value > num) {
  120. setTimeout(() => {
  121. return renderPage(num + 1)
  122. })
  123. }
  124. }
  125. const nextPage = () => {
  126. if (pdf_pages.value > currentPage.value) {
  127. renderPage(currentPage.value + 1)
  128. }
  129. }
  130. const prePage = () => {
  131. if (currentPage.value > 1) {
  132. renderPage(currentPage.value - 1)
  133. }
  134. }
  135. onMounted(() => {
  136. loadFile()
  137. })
  138. </script>
  139. <style scoped>
  140. .home_wrap {
  141. width: 100%;
  142. height: 100%;
  143. }
  144. .home_wrap .pdf_down {
  145. position: fixed;
  146. display: flex;
  147. z-index: 20;
  148. right: 26px;
  149. bottom: 7%;
  150. cursor: pointer;
  151. }
  152. .home_wrap .pdf-pre {
  153. position: fixed;
  154. display: flex;
  155. z-index: 20;
  156. right: 160px;
  157. bottom: 9%;
  158. cursor: pointer;
  159. }
  160. .home_wrap .pdf-next {
  161. position: fixed;
  162. display: flex;
  163. z-index: 20;
  164. right: 100px;
  165. bottom: 9%;
  166. }
  167. .home_wrap .pdf_down .pdf_set_left {
  168. width: 30px;
  169. height: 40px;
  170. color: #408fff;
  171. font-size: 15px;
  172. padding-top: 25px;
  173. text-align: center;
  174. margin-right: 5px;
  175. cursor: pointer;
  176. }
  177. .home_wrap .pdf_down .pdf_set_middle {
  178. width: 30px;
  179. height: 40px;
  180. color: #408fff;
  181. font-size: 15px;
  182. padding-top: 25px;
  183. text-align: center;
  184. margin-right: 5px;
  185. cursor: pointer;
  186. }
  187. </style>