H5内嵌到APP-实现PDF浏览功能

news/2025/1/15 23:39:34/

1、下载插件

npm install vue-pdf-embed vue3-pdfjs

vue3-pdfjs:获取PDF文件总页数

2、页面引入并使用

<template><div class="vuePdfEmbed"><div class="wraper" v-if="flagCover"><div class="articleTitle">{{ reportDetail.title }}</div><div class="articleTagBox"><div>{{ reportDetail.createTime }}</div></div><div class="articleDetailCover"><el-image :src="reportDetail.cover" alt="" lazy class="articleDetailCoverImg" /></div><div class="articleIntroduce">{{ reportDetail.summary }}</div></div><VuePdfEmbed:source="state.source":style="scaleFun"class="vue-pdf-embed":page="state.pageNum"lazy/><div class="page-tool" v-if="!bottomVisible"><div class="page-tool-item" @click="lastPage">上一页</div><div class="page-tool-item">{{ state.pageNum }}/{{ state.numPages }}</div><div class="page-tool-item" @click="nextPage">下一页</div></div><!-- 购买提示 --><div class="bottomVisible" v-if="bottomVisible"><div class="lock-box"><img src="./img/lock.png" alt="" class="lock-box-img" /><div>购买后可继续阅读本文档</div></div><div class="buy-box"><div class="fixedBoxL"><div class="fixedBoxLT">购买全文</div><div class="fixedBoxLB">单篇需付费¥{{ apperMoney }}</div></div><div class="fixedBoxR" @click="goBuyReport">立即购买<el-icon><ArrowRightBold /></el-icon></div></div></div><!-- 弹框提示 --><el-dialog v-model="dialogVisible"><div class="dialog-box"><div class="dialog-text-box"><div class="dialog-title">购买后可继续阅读本文档</div><div class="dialog-info">单篇付费只需{{ apperMoney }}元</div></div><div class="dialog-text-box"><div class="dialog-btn" @click="goBuyReport">立即购买</div><div class="dialog-close" @click="handleClose">我再想想</div></div></div><div class="dialog-close-box" @click="handleClose"></div></el-dialog></div>
</template>
<script setup>
import {LaunchApp,detector,ua,copy,supportLink,isAndroid,isIos,inWeixin,inQQ,inWeibo,inBaidu,
} from "web-launch-app";
import { getReportDetailApi } from "@/http/api";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";
import { reactive, onMounted, ref, onUnmounted } from "vue";
import VuePdfEmbed from "vue-pdf-embed";
import { createLoadingTask } from "vue3-pdfjs"; // 获得总页数
import { ArrowRightBold } from "@element-plus/icons-vue";
const route = useRoute();
const pdfurl = ref("");
const state = reactive({source: pdfurl, //预览pdf文件地址pageNum: 1, //当前页面scale: 1, // 缩放比例numPages: 0, // 总页数
});
const scaleFun = reactive({transform: "scale(" + state.scale + ")",
});
// 获取上一页
function lastPage() {if (state.pageNum > 1) {state.pageNum--;}
}
// 获取下一页
function nextPage() {if (state.pageNum < state.numPages) {state.pageNum++;}
}
onMounted(() => {getReportDetail();
});
// 获取pdf文件
// let flag = ref(false); // 下载按钮的显隐
let flagCover = ref(false); // 封面显隐
let apperMoney = ref(); // 价格
let bottomVisible = ref(false);
const reportDetail = ref({});
function getReportDetail() {getReportDetailApi(route.query.id).then((res) => {if (res.code == 200) {document.title = res.data.title;if (res.data.isFree == 1 && res.data.isPurchase == 2) {// 付费 且 已解锁pdfurl.value = res.data.fileUrl;// flag.value = true;// 加载异步任务const loadingTask = createLoadingTask(state.source);// 载入pdf后获取页数loadingTask.promise.then((pdf) => {state.numPages = pdf.numPages;});} else if (res.data.isFree == 0) {// 免费pdfurl.value = res.data.fileUrl;// flag.value = true;// 加载异步任务const loadingTask = createLoadingTask(state.source);// 载入pdf后获取页数loadingTask.promise.then((pdf) => {state.numPages = pdf.numPages;});} else if (res.data.isFree == 1 && res.data.isPurchase == 1) {// 付费 且 未解锁// 展示封面图 且 立即购买reportDetail.value = res.data;// flag.value = false;if (route.query.type == "ios") {apperMoney.value = res.data.appearIosAmount;} else {// 安卓现价apperMoney.value = res.data.appearAmount;}bottomVisible.value = true;flagCover.value = true;timer = setInterval(() => {dialogVisible.value = true;}, 2000);}} else {ElMessage({message: res.msg,type: "error",plain: true,});}});
}// 弹框
const dialogVisible = ref(false);
let timer;
function handleClose() {dialogVisible.value = false;clearInterval(timer);
}
onUnmounted(() => {clearInterval(timer);
});// 立即购买
function goBuyReport() {if (isAndroid) {androidCscFinanceNews.clickBuyReport(Number(route.query.id));} else if (isIos) {window.webkit.messageHandlers.clickBuyReport.postMessage([Number(route.query.id)]);}
}
</script>
<style>
.vuePdfEmbed {flex: 1;display: flex;height: 100%;flex-direction: column;
}
.vuePdfEmbed {flex: 1;display: flex;height: 100%;flex-direction: column;
}
.vuePdfEmbed {.page-tool {position: fixed;bottom: 10px;left: 50%;transform: translateX(-50%);padding-left: 15px;padding-right: 15px;display: flex;align-items: center;background: rgb(66, 66, 66);color: white;border-radius: 19px;z-index: 100;cursor: pointer;width: 320px;align-items: center;margin: auto;justify-content: space-around;}.page-tool-item {padding: 8px 15px;padding-left: 10px;cursor: pointer;}
}.el-dialog {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);margin: 0;width: 270px;height: 270px;background-image: url("./img/dialog.png");background-size: contain;background-color: transparent;box-shadow: none;
}.el-dialog__headerbtn {display: none;
}.dialog-box {position: absolute;left: 50%;bottom: 0;transform: translateX(-50%);width: 100%;height: 60%;display: flex;justify-content: space-around;flex-direction: column;align-items: center;
}.dialog-text-box {display: flex;flex-direction: column;align-items: center;
}
.dialog-title {font-weight: 500;font-size: 20px;color: #8b2507;line-height: 28px;
}.dialog-info {font-weight: 400;font-size: 16px;color: #ad705e;line-height: 23px;
}.dialog-btn {width: 154px;height: 47px;background: linear-gradient(360deg, #ff330a 0%, #fc4707 100%);box-shadow: 0px 2px 0px 0px #ffa073, inset 1px 5px 9px 0px rgba(255, 255, 255, 0.67);font-weight: 500;font-size: 17px;color: #ffffff;line-height: 47px;text-align: center;border-radius: 80px;
}.dialog-close {font-weight: 400;font-size: 12px;color: rgba(0, 0, 0, 0.4);line-height: 40px;
}.dialog-close-box {position: absolute;left: 50%;bottom: -60px;transform: translateX(-50%);width: 36px;height: 36px;background-image: url("./img/close.png");background-size: cover;
}.lock-box {display: flex;justify-content: center;align-items: center;height: 178px;background: linear-gradient(180deg, rgba(253, 250, 243, 0.97) 0%, #fef2e6 100%);border-radius: 4px 4px 4px 4px;text-align: center;line-height: 178px;font-weight: 400;font-size: 14px;color: #784122;
}.lock-box-img {display: block;width: 13px;height: 16px;margin-right: 9px;
}.buy-box {display: flex;align-items: center;justify-content: space-between;width: 100%;height: 60px;background-image: url("./img/bottom.png");background-size: cover;margin-top: -12px;padding: 0 30px;
}.fixedBoxL {padding-top: 10px;
}
.fixedBoxLT {font-size: 14px;line-height: 20px;color: rgba(255, 255, 255, 0.9);font-weight: 400;
}.fixedBoxLB {font-size: 10px;line-height: 12px;color: rgba(255, 255, 255, 0.6);font-weight: 400;
}.fixedBoxR {display: flex;align-items: center;font-size: 16px;line-height: 20px;color: rgba(255, 255, 255, 0.9);font-weight: 500;
}.bottomVisible {position: fixed;width: 100%;bottom: 0;z-index: 2;
}
.articleTitle {font-size: 24px;color: rgba(0, 0, 0, 0.9);line-height: 32px;font-weight: 500;
}.articleTagBox {display: flex;margin-top: 12px;margin-bottom: 20px;font-size: 12px;line-height: 20px;color: rgba(106, 106, 106, 1);
}.articleDetailCover {margin-bottom: 12px;
}.el-image__inner {border-radius: 4px;
}.articleDetailCoverImg {display: block;width: 100%;border-radius: 4px;
}.articleIntroduce {font-weight: 400;font-size: 12px;color: #6b6b6b;line-height: 22px;background: #f6f6f6;border-radius: 4px 4px 4px 4px;padding: 12px;margin-bottom: 12px;
}.vue-pdf-embed__page canvas {width: 100% !important;
}@media screen and (min-width: 500px) {.bottomVisible {position: relative;}
}
</style>

重点代码:


http://www.ppmy.cn/news/1508796.html

相关文章

React 用户点击某个元素后只执行一次操作

React开发中经常会遇到需求&#xff1a;用户点击某个元素后只执行一次特定操作。比如&#xff0c;用户点击按钮后弹出提示框&#xff0c;但希望再次点击按钮不再触发提示框。针对这种需求&#xff0c;可以封装一个自定义Hooks来实现只允许点击一次的功能。 import {useCallbac…

Java面试篇(JVM相关专题)

文章目录 0. 前言1. 为什么要学 JVM2. 什么是 JVM3. JVM 的好处3.1 一次编写&#xff0c;到处运行3.2 自动内存管理&#xff08;基于垃圾回收机制&#xff09; 4. 要学习哪些 JVM 的哪些内容5. JVM 的组成5.1 程序计数器5.2 堆5.3 什么是虚拟机栈常见问题一&#xff1a;垃圾回收…

嵌入式day25

进程线程 多任务编程 1、进程 2、线程 进程&#xff08;process&#xff09; 进行中的程序 --- 正在运行中的程序 进程 --- 程序的一次执行的过程 进程 是程序的一个实例 进程是跑起来的程序 一个程序可以对应多个进程 程序 静态 硬盘 进程 动态 内存 为什么需要进程…

深度学习之注意力机制

深度学习中的注意力机制 1. 注意力机制的基本概念1.1 注意力机制概述1.2 空间注意力 vs 时间注意力1.2.1 空间注意力应用场景 1.2.2 时间注意力应用场景 1.3 自注意力和交互注意力的区别和应用场景1.3.1 自注意力优点应用场景 1.3.2 交互注意力优点应用场景 1.4 注意力机制的数…

数据库核心技术:存储与索引概览

文章目录 存储与索引技术概览存储结构索引技术 MySQL存储结构索引技术事务与锁优势 PostgreSQL存储结构索引技术事务与锁优势 Oracle存储结构索引技术事务与锁优势 SQL Server存储结构索引技术事务与锁优势 选型考量存储结构索引技术事务与锁的支持综合因素未来趋势 结语 数据库…

FFMPEG 工具方法

av_strerror int av_strerror ( int errnum, char * errbuf, size_t errbuf_size )ffmpeg获取与设置mp4文件旋转方向方法 设置与获取都是对AVStream的dict操作. 设置 for (i 0; i < ifmt_ctx_v->nb_streams; i) { //Create output AVStream according to input A…

工业智能网关在汽车制造企业的应用价值及功能-天拓四方

随着工业互联网的飞速发展&#xff0c;工业智能网关作为连接物理世界与数字世界的桥梁&#xff0c;正逐渐成为制造业数字化转型的核心组件。本文将以一家汽车制造企业的实际使用案例为蓝本&#xff0c;深入解析工业智能网关在实际应用中的价值、功能及其实操性。 一、背景与挑…

resource not found with Azure OpenAI service

题意&#xff1a;使用 Azure OpenAI 服务时&#xff0c;系统未能找到所请求的资源 问题背景&#xff1a; when I am using this demo code to use the Azure OpenAI service in Java 11: 当我在Java 11环境中使用这段示例代码来调用Azure OpenAI服务时&#xff1a; package …