hookehuyr

feat(组件): 添加PdfPreview组件并集成到StudyDetailPage

将PDF预览功能从StudyDetailPage中抽离到独立的PdfPreview组件,提高代码复用性和可维护性
...@@ -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']
......
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/", //预定义 Adob​​e 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的高度
......