PdfViewer.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <template>
  2. <div class="yaxq-bottom-jcxx yawd">
  3. <div class="page-tool">
  4. <div class="page-tool-item" @click="zoomOut">缩小</div>
  5. <div class="page-tool-item" @click="lastPage">上一页</div>
  6. <div class="page-tool-item">{{ state.pageNum }}/{{ state.numPages }}</div>
  7. <div class="page-tool-item" @click="nextPage">下一页</div>
  8. <div class="page-tool-item" @click="zoomIn">放大</div>
  9. </div>
  10. <div class="pdf-preview"
  11. v-loading="pdfLoading"
  12. @mousedown="startDrag"
  13. @mousemove="doDrag"
  14. @mouseup="endDrag"
  15. @mouseleave="endDrag">
  16. <div class="pdf-container" :style="containerStyle">
  17. <vue-pdf-embed
  18. v-if="show"
  19. :source="state.source"
  20. class="vue-pdf-embed"
  21. :style="{ transform: `scale(${state.scale})` }"
  22. :page="state.pageNum"
  23. @loaded="handleDocument" />
  24. </div>
  25. </div>
  26. </div>
  27. </template>
  28. <script setup lang="ts">
  29. import { ref, reactive, computed, onMounted } from 'vue';
  30. import VuePdfEmbed from "vue-pdf-embed";
  31. const props = defineProps({
  32. url: String
  33. });
  34. // PDF状态
  35. const state = reactive({
  36. source: '' as string | undefined,
  37. pageNum: 1,
  38. scale: 1,
  39. numPages: 0,
  40. });
  41. // 拖拽状态
  42. const dragState = reactive({
  43. isDragging: false,
  44. startX: 0,
  45. startY: 0,
  46. translateX: 0,
  47. translateY: 0,
  48. lastTranslateX: 0,
  49. lastTranslateY: 0
  50. });
  51. // 加载状态
  52. const pdfLoading = ref(false);
  53. const show = ref(false);
  54. // 容器样式计算
  55. const containerStyle = computed(() => ({
  56. transform: `translate(${dragState.translateX}px, ${dragState.translateY}px)`,
  57. cursor: dragState.isDragging ? 'grabbing' : 'grab'
  58. }));
  59. // 拖拽处理
  60. const startDrag = (e: MouseEvent) => {
  61. dragState.isDragging = true;
  62. dragState.startX = e.clientX - dragState.translateX;
  63. dragState.startY = e.clientY - dragState.translateY;
  64. };
  65. const doDrag = (e: MouseEvent) => {
  66. if (!dragState.isDragging) return;
  67. dragState.translateX = e.clientX - dragState.startX;
  68. dragState.translateY = e.clientY - dragState.startY;
  69. };
  70. const endDrag = () => {
  71. if (!dragState.isDragging) return;
  72. dragState.isDragging = false;
  73. dragState.lastTranslateX = dragState.translateX;
  74. dragState.lastTranslateY = dragState.translateY;
  75. };
  76. // 缩放功能
  77. const zoomOut = () => {
  78. state.scale = Math.max(0.5, state.scale - 0.1);
  79. resetPosition();
  80. };
  81. const zoomIn = () => {
  82. state.scale = Math.min(3, state.scale + 0.1);
  83. resetPosition();
  84. };
  85. // 重置位置
  86. const resetPosition = () => {
  87. dragState.translateX = 0;
  88. dragState.translateY = 0;
  89. dragState.lastTranslateX = 0;
  90. dragState.lastTranslateY = 0;
  91. };
  92. // 翻页功能
  93. const lastPage = () => {
  94. if (state.pageNum > 1) {
  95. state.pageNum -= 1;
  96. resetPosition();
  97. }
  98. };
  99. const nextPage = () => {
  100. if (state.pageNum < state.numPages) {
  101. state.pageNum += 1;
  102. resetPosition();
  103. }
  104. };
  105. // PDF加载完成
  106. const handleDocument = (pdf: any) => {
  107. if (pdf.numPages) {
  108. pdfLoading.value = false;
  109. state.numPages = pdf.numPages;
  110. }
  111. };
  112. // 初始化
  113. onMounted(() => {
  114. if (props.url) {
  115. state.source = props.url;
  116. pdfLoading.value = true;
  117. show.value = true;
  118. }
  119. });
  120. </script>
  121. <style scoped lang="scss">
  122. .yawd {
  123. display: flex;
  124. justify-content: center;
  125. position: relative;
  126. //height: 100%;
  127. .page-tool {
  128. position: fixed;
  129. bottom: 20px;
  130. left: 50%;
  131. transform: translateX(-50%);
  132. padding: 5px 15px;
  133. display: flex;
  134. align-items: center;
  135. background: #424242;
  136. color: white;
  137. border-radius: 5px;
  138. z-index: 999;
  139. cursor: pointer;
  140. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  141. &-item {
  142. font-size: 14px;
  143. padding: 5px 15px;
  144. transition: background 0.3s;
  145. border-radius: 3px;
  146. &:hover {
  147. background: rgba(255, 255, 255, 0.1);
  148. }
  149. &:not(:last-child) {
  150. border-right: 1px solid rgba(255, 255, 255, 0.2);
  151. }
  152. }
  153. }
  154. .pdf-preview {
  155. width: 100%;
  156. height: calc(100% - 60px);
  157. overflow: hidden;
  158. position: relative;
  159. background: #f0f0f0;
  160. //.pdf-container {
  161. // transition: transform 0.3s ease;
  162. // will-change: transform;
  163. // padding: 20px;
  164. //}
  165. .vue-pdf-embed {
  166. transform-origin: 0 0;
  167. user-select: none;
  168. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  169. background: white !important;
  170. }
  171. }
  172. }
  173. </style>