feat(组件): 添加PdfPreview组件并集成到StudyDetailPage
将PDF预览功能从StudyDetailPage中抽离到独立的PdfPreview组件,提高代码复用性和可维护性
Showing
3 changed files
with
98 additions
and
64 deletions
| ... | @@ -20,6 +20,7 @@ declare module 'vue' { | ... | @@ -20,6 +20,7 @@ declare module 'vue' { |
| 20 | GradientHeader: typeof import('./components/ui/GradientHeader.vue')['default'] | 20 | GradientHeader: typeof import('./components/ui/GradientHeader.vue')['default'] |
| 21 | LiveStreamCard: typeof import('./components/ui/LiveStreamCard.vue')['default'] | 21 | LiveStreamCard: typeof import('./components/ui/LiveStreamCard.vue')['default'] |
| 22 | MenuItem: typeof import('./components/ui/MenuItem.vue')['default'] | 22 | MenuItem: typeof import('./components/ui/MenuItem.vue')['default'] |
| 23 | + PdfPreview: typeof import('./components/ui/PdfPreview.vue')['default'] | ||
| 23 | PdfViewer: typeof import('./components/ui/PdfViewer.vue')['default'] | 24 | PdfViewer: typeof import('./components/ui/PdfViewer.vue')['default'] |
| 24 | ReviewPopup: typeof import('./components/courses/ReviewPopup.vue')['default'] | 25 | ReviewPopup: typeof import('./components/courses/ReviewPopup.vue')['default'] |
| 25 | RouterLink: typeof import('vue-router')['RouterLink'] | 26 | RouterLink: typeof import('vue-router')['RouterLink'] | ... | ... |
src/components/ui/PdfPreview.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2024-01-17 | ||
| 3 | + * @Description: PDF预览组件 | ||
| 4 | +--> | ||
| 5 | +<template> | ||
| 6 | + <van-popup v-if="show" :show="show" @update:show="emit('update:show', $event)" position="right" :style="{ height: '100%', width: '100%' }"> | ||
| 7 | + <div id="pdf-container"></div> | ||
| 8 | + <van-button class="close-btn" type="default" icon="cross" round @click="emit('update:show', false)" /> | ||
| 9 | + </van-popup> | ||
| 10 | +</template> | ||
| 11 | + | ||
| 12 | +<script setup> | ||
| 13 | +import { ref, nextTick, onMounted } from 'vue'; | ||
| 14 | +import { initPdfView, configPdfApiOptions } from "@sunsetglow/vue-pdf-viewer"; | ||
| 15 | +import "@sunsetglow/vue-pdf-viewer/dist/style.css"; | ||
| 16 | + | ||
| 17 | +const props = defineProps({ | ||
| 18 | + show: { | ||
| 19 | + type: Boolean, | ||
| 20 | + default: false | ||
| 21 | + }, | ||
| 22 | + url: { | ||
| 23 | + type: String, | ||
| 24 | + default: '' | ||
| 25 | + } | ||
| 26 | +}); | ||
| 27 | + | ||
| 28 | +const emit = defineEmits(['update:show']); | ||
| 29 | + | ||
| 30 | +// PDF Worker路径 | ||
| 31 | +const pdfPath = new URL("@sunsetglow/vue-pdf-viewer/dist/libs/pdf.worker.min.js", import.meta.url).href; | ||
| 32 | + | ||
| 33 | +// 监听show属性变化 | ||
| 34 | +watch(() => props.show, (newVal) => { | ||
| 35 | + if (newVal && props.url) { | ||
| 36 | + initPdfViewer(); | ||
| 37 | + } | ||
| 38 | +}); | ||
| 39 | + | ||
| 40 | +// 初始化PDF预览 | ||
| 41 | +// 查看文档 https://www.npmjs.com/package/@sunsetglow/vue-pdf-viewer | ||
| 42 | +const initPdfViewer = () => { | ||
| 43 | + nextTick(() => { | ||
| 44 | + initPdfView(document.querySelector("#pdf-container"), { | ||
| 45 | + loadFileUrl: props.url, | ||
| 46 | + pdfPath: pdfPath, | ||
| 47 | + loading: (load, fileInfo) => { | ||
| 48 | + if (!load) { | ||
| 49 | + configPdfApiOptions.onSearch("", false); | ||
| 50 | + } | ||
| 51 | + }, | ||
| 52 | + pdfOption: { | ||
| 53 | + search: false, | ||
| 54 | + scale: true, | ||
| 55 | + pdfImageView: false, | ||
| 56 | + page: true, | ||
| 57 | + navShow: true, | ||
| 58 | + navigationShow: false, | ||
| 59 | + pdfViewResize: true, | ||
| 60 | + toolShow: true, | ||
| 61 | + download: true, | ||
| 62 | + clearScale: 1.5, | ||
| 63 | + fileName: "preview.pdf", | ||
| 64 | + lang: "en", | ||
| 65 | + print: true, | ||
| 66 | + watermarkOptions: undefined | ||
| 67 | + } | ||
| 68 | + }); | ||
| 69 | + }); | ||
| 70 | +}; | ||
| 71 | +</script> | ||
| 72 | + | ||
| 73 | +<style scoped> | ||
| 74 | +#pdf-container { | ||
| 75 | + width: 100%; | ||
| 76 | + padding: 0px; | ||
| 77 | + height: 100%; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +.close-btn { | ||
| 81 | + position: fixed; | ||
| 82 | + right: 20px; | ||
| 83 | + bottom: 20px; | ||
| 84 | + z-index: 100; | ||
| 85 | + width: 40px; | ||
| 86 | + height: 40px; | ||
| 87 | + padding: 0; | ||
| 88 | + border-radius: 50%; | ||
| 89 | + background: rgba(0, 0, 0, 0.6); | ||
| 90 | + color: #fff; | ||
| 91 | +} | ||
| 92 | +</style> |
| ... | @@ -225,9 +225,7 @@ | ... | @@ -225,9 +225,7 @@ |
| 225 | </van-popup> | 225 | </van-popup> |
| 226 | 226 | ||
| 227 | <!-- PDF预览 --> | 227 | <!-- PDF预览 --> |
| 228 | - <van-popup v-model:show="pdfShow" position="right" closeable :style="{ height: '100%', width: '100%' }"> | 228 | + <PdfPreview v-model:show="pdfShow" :url="pdfUrl" /> |
| 229 | - <div id="pdf-container"></div> | ||
| 230 | - </van-popup> | ||
| 231 | </div> | 229 | </div> |
| 232 | </template> | 230 | </template> |
| 233 | 231 | ||
| ... | @@ -240,8 +238,7 @@ import AudioPlayer from '@/components/ui/AudioPlayer.vue'; | ... | @@ -240,8 +238,7 @@ import AudioPlayer from '@/components/ui/AudioPlayer.vue'; |
| 240 | import dayjs from 'dayjs'; | 238 | import dayjs from 'dayjs'; |
| 241 | import { formatDate } from '@/utils/tools' | 239 | import { formatDate } from '@/utils/tools' |
| 242 | 240 | ||
| 243 | -import { initPdfView, configPdfApiOptions, configOption } from "@sunsetglow/vue-pdf-viewer"; | 241 | +import PdfPreview from '@/components/ui/PdfPreview.vue'; |
| 244 | -import "@sunsetglow/vue-pdf-viewer/dist/style.css"; | ||
| 245 | 242 | ||
| 246 | // 导入接口 | 243 | // 导入接口 |
| 247 | import { getScheduleCourseAPI, getGroupCommentListAPI, addGroupCommentAPI, addGroupCommentLikeAPI, delGroupCommentLikeAPI, getCourseDetailAPI } from '@/api/course'; | 244 | import { getScheduleCourseAPI, getGroupCommentListAPI, addGroupCommentAPI, addGroupCommentLikeAPI, delGroupCommentLikeAPI, getCourseDetailAPI } from '@/api/course'; |
| ... | @@ -358,69 +355,13 @@ const handleLessonClick = async (lesson) => { | ... | @@ -358,69 +355,13 @@ const handleLessonClick = async (lesson) => { |
| 358 | } | 355 | } |
| 359 | }; | 356 | }; |
| 360 | 357 | ||
| 361 | -const loading = ref(false); | ||
| 362 | -const pdfPath = new URL("@sunsetglow/vue-pdf-viewer/dist/libs/pdf.worker.min.js", import.meta.url).href; | ||
| 363 | const pdfShow = ref(false); | 358 | const pdfShow = ref(false); |
| 359 | +const pdfUrl = ref(''); | ||
| 364 | 360 | ||
| 365 | const showPdf = (url) => { | 361 | const showPdf = (url) => { |
| 362 | + pdfUrl.value = url; | ||
| 366 | pdfShow.value = true; | 363 | pdfShow.value = true; |
| 367 | - // 初始化pdf | 364 | +}; |
| 368 | - setTimeout(() => { | ||
| 369 | - nextTick(() => { | ||
| 370 | - loading.value = true; | ||
| 371 | - initPdfView(document.querySelector("#pdf-container"), { | ||
| 372 | - loadFileUrl: url, //文件路径 | ||
| 373 | - pdfPath: pdfPath, // pdf.js 里需要指定的文件路径 | ||
| 374 | - loading: (load, fileInfo) => { | ||
| 375 | - loading.value = load; | ||
| 376 | - console.log(`pdf 文件总数:${fileInfo.totalPage}`); | ||
| 377 | - //加载完成会返回 false | ||
| 378 | - configPdfApiOptions.onSearch("", false); | ||
| 379 | - }, | ||
| 380 | - pdfOption: { | ||
| 381 | - // search: true, // 搜索 开启搜索必须开启textLayer 为true | ||
| 382 | - scale: true, //缩放 | ||
| 383 | - pdfImageView: false, //pdf 是否可以单片点击预览 | ||
| 384 | - page: true, //分页查看 | ||
| 385 | - navShow: true, //左侧导航 | ||
| 386 | - navigationShow: false, // 左侧导航是否开启 | ||
| 387 | - pdfViewResize: true, // 是否开启resize 函数 确保pdf 根据可视窗口缩放大小 | ||
| 388 | - toolShow: true, // 是否开启顶部导航 | ||
| 389 | - download: true, //下载 | ||
| 390 | - clearScale: 1.5, // 清晰度 默认1.5 感觉不清晰调大 ,当然清晰度越高pdf生成性能有影响 | ||
| 391 | - fileName: "preview.pdf", // pdf 下载文件名称 | ||
| 392 | - lang: "en", //字典语言 | ||
| 393 | - print: true, //打印功能 | ||
| 394 | - customPdfOption: { | ||
| 395 | - // customPdfOption是 pdfjs getDocument 函数中一些配置参数 具体可参考 https://mozilla.github.io/pdf.js/api/draft/module-pdfjsLib.html#~DocumentInitParameters | ||
| 396 | - cMapPacked: true, //指定 CMap 是否是二进制打包的 | ||
| 397 | - cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/", //预定义 Adobe CMaps 所在的 URL。可解决字体加载错误 | ||
| 398 | - }, | ||
| 399 | - textLayer: true, //文本是否可复制 , 文本复制和点击查看大图冲突建议把 pdfImageView 改为false | ||
| 400 | - pageOption: { | ||
| 401 | - current: 1, //当前页码 | ||
| 402 | - }, | ||
| 403 | - renderTotalPage: 5, //是否渲染指定页面总数,-1 则默认渲染文件总数,如果传5 则渲染前五页 | ||
| 404 | - // 不传默认是 0.5 | ||
| 405 | - visibleWindowPageRatio: 0.5, //当前pdf页面在可视窗口多少比例触发分页 传入0.5 就是 (pdf下一页滚动到容器高度一半的时候 更新当前页码) | ||
| 406 | - containerWidthScale: 0.97, //pdf 文件占父元素容器width的比例 默认是0.8 | ||
| 407 | - pdfItemBackgroundColor: "#fff", //pdf 加载时背景颜色 默认#ebebeb | ||
| 408 | - watermarkOptions: { | ||
| 409 | - //水印功能 | ||
| 410 | - columns: 3, //列数量 | ||
| 411 | - rows: 4, // 行数量 | ||
| 412 | - color: "#2f7a54", //字体颜色 | ||
| 413 | - rotation: 25, //旋转角度 | ||
| 414 | - fontSize: 40, //字体大小 | ||
| 415 | - opacity: 0.4, //调整透明度 | ||
| 416 | - // watermarkTextList: ["第一行", "第二行", "第三行"], //水印文字和 watermarkLink 冲突,只能展示一个水印内容 | ||
| 417 | - // watermarkLink: "https://xxx.png", //水印可以支持公司logo(图片路径) | ||
| 418 | - }, // 不展示水印传 undefined即可 | ||
| 419 | - }, | ||
| 420 | - }) | ||
| 421 | - }); | ||
| 422 | - }, 1000); | ||
| 423 | -} | ||
| 424 | 365 | ||
| 425 | onMounted(async () => { | 366 | onMounted(async () => { |
| 426 | // 延迟设置topWrapper和bottomWrapper的高度 | 367 | // 延迟设置topWrapper和bottomWrapper的高度 | ... | ... |
-
Please register or login to post a comment