最近有一个需求,需要做一个pdf预览,然后禁止用户打印和调起控制台,页面有一个倒计时,时间到了就不允许用户进行查看了,然后简单做了两个demo版本,一个是读取本地的pdf文件,一个是接口调用然后展示pdf,并在后期做了一些优化
- 先看效果图(读取本地效果)
- 进入页面以后,读取本地文件,再渲染到页面,倒计时9秒后加上一层蒙版,然后禁止用户进行页面操作
<template>
<div
class="pdf"
@mousemove="moveEvent"
@click="moveEvent"
@mousedown="mouseDown"
>
<pdf
:src="blobURL"
:page="currentPage"
@num-pages="pageCount = $event"
@page-loaded="currentPage = $event"
class="pdf-set"
></pdf>
<div class="span-clas">
<span>{{ currentPage }} / {{ pageCount }}</span>
<div class="btn-list">
<div @click="change1" class="btn-info">上一页</div>
<div @click="change2" class="btn-info">下一页</div>
<div class="btn-info" v-show="show" @click="showInfo">继续查看</div>
</div>
剩余查看时间:{{ count }}
</div>
<div class="btn" v-if="show"></div>
</div>
</template>
<script>
import pdf from "vue-pdf-signature"; //引入组件
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js";
export default {
name: "PDF",
created() {
this.name = "文案";
this.init();
this.prohibit();
},
data() {
return {
againTimer: false, // 是否从新计时
show: false,
blobURL: "",
name: "", //此处在本地需要把pdf文件放入static文件夹下面,线上可以放入别的可访问的文件夹即可
currentPage: 1,
pageCount: 1,
seconds: 10,
count: "",
timer: null,
keyCodeList: [
"Escape",
"Meta",
"F12",
"F6",
"c",
"i",
"j",
"p",
"s",
"u",
"v",
],
url: "",
};
},
components: {
pdf,
},
mounted() {
this.inquire();
this.change();
this.Time(10);
},
methods: {
// 读取本地下的pdf并解析
inquire() {
let xhr = new XMLHttpRequest();
// let okStatus = document.location.protocol === "file:" ? 0 : 200;
xhr.open("GET", "base64(1).txt", false); // public文件夹下的绝对路径
xhr.overrideMimeType("text/html;charset=utf-8");
xhr.send(null);
this.url = xhr.responseText;
// console.log(xhr.responseText, okStatus); // xhr.responseText为文本中的内容
},
changeBaseInfo(code) {
var raw = window.atob(code);
let rawLength = raw.length;
//转换成pdf.js能直接解析的Uint8Array类型
let uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: "application/pdf" }); //转成pdf类型
},
change() {
let url = this.changeBaseInfo(this.url);
this.blobURL = window.URL.createObjectURL(url);
// 兼容中文水印字符不展示的问题
this.blobURL = pdf.createLoadingTask({
url: this.blobURL,
CMapReaderFactory,
});
},
// 天 时 分 秒 格式化函数
countDown(seconds) {
let d = parseInt(seconds / (24 * 60 * 60));
d = d < 10 ? "0" + d : d;
let h = parseInt((seconds / (60 * 60)) % 24);
h = h < 10 ? "0" + h : h;
let m = parseInt((seconds / 60) % 60);
m = m < 10 ? "0" + m : m;
let s = parseInt(seconds % 60);
s = s < 10 ? "0" + s : s;
this.count = d + "天" + h + "时" + m + "分" + s + "秒";
},
//定时器没过1秒参数减1
Time(time) {
this.timer = setInterval(() => {
if (time > 0) {
time -= 1;
this.countDown(time);
this.againTimer = false;
} else {
this.show = true;
this.againTimer = true;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
},
showInfo() {
this.show = false;
this.Time(10);
},
moveEvent(e) {
console.log(e);
// if (this.againTimer) this.show = false;
},
mouseDown() {
// if (this.againTimer) {
// this.show = false;
// this.Time(10);
// }
},
prohibit() {
// 禁用鼠标右击、F12
document.oncontextmenu = function () {
return false;
};
document.onkeydown = function (e) {
if (e) {
this.show = false;
}
if (e.ctrlKey && this.keyCodeList.include(e.code)) {
return false;
}
};
},
init() {
this.src += this.name;
},
change1() {
if (this.currentPage > 1) {
this.currentPage--;
}
},
change2() {
if (this.currentPage < this.pageCount) {
this.currentPage++;
}
},
},
};
</script>
<style scoped lang="scss">
.pdf .pdf-set {
display: inline-block;
text-align: center;
width: 850px;
height: 500px;
// border: 1px solid;
margin: 0 auto;
overflow: auto;
}
.btn {
width: 850px;
height: 500px;
position: fixed;
top: 8px;
left: 26%;
background-color: rgba(243, 243, 243, 0.8);
backdrop-filter: brightness(200%) saturate(130%) blur(8px);
}
.span-clas {
margin-top: 20px;
.btn-list {
margin: 0 auto;
margin-top: 20px;
margin-bottom: 20px;
width: 300px;
display: flex;
align-items: center;
justify-content: space-between;
.btn-info {
border: 1px solid;
width: 75px;
height: 25px;
line-height: 25px;
border-radius: 5px;
font-size: 14px;
cursor: pointer;
}
}
}
</style>
- 调用接口效果
- 不再读取本地,通过接口拉起数据,然后解析
base64
,在渲染到页面,去除了翻页按钮,采用滚动方式展示页面,并且遮罩层变成了整个页面遮罩,点击遮罩按钮再重新拉起计时,新增放大缩小效果,分页展示,缩略图以及点击滚动到当前页的效果 - 先看效果
<template>
<div class="pdf" v-loading.fullscreen.lock="fullscreenLoading">
<template v-if="hasReport">
<div class="pdf-container">
<div class="top">
<div class="top-container">
<div class="page">
<span>{{ chooseIndex + 1 }} / {{ pageCount }}</span>
</div>
<div class="segmentation">|</div>
<div class="scale-container">
<div class="lessen" @click="minus">-</div>
<div class="num">{{ scale }}%</div>
<div class="add" @click="plus">+</div>
</div>
</div>
<div class="span-clas">
<div v-if="!show" class="remaining">
剩余查看报告时间:{{ count }}
</div>
</div>
</div>
<div class="contianer">
<div class="pdf-wrapper">
<div class="pdf-scroll" :style="{ width: scale + '%' }">
<div
v-for="(item, index) in numPages"
:key="item"
:id="`choose${index}`"
>
<pdf
:src="blobURL"
:page="item"
@num-pages="pageCount = $event"
/>
</div>
</div>
</div>
<div class="right-list">
<!-- 缩略图 -->
<div class="thumbnail-wrapper">
<div
:class="
chooseIndex === index ? 'thumbnail-active' : 'thumbnail'
"
v-for="(item, index) in numPages"
:key="index"
@click="clickPdf(index)"
>
<pdf :src="blobURL" :page="item" />
<div class="text">{{ index + 1 }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 遮罩层 -->
<div class="mask" v-if="show">
<el-button v-show="show" round @click="showInfo" class="mask-btn"
>继续查看征信报告</el-button
>
</div>
</template>
<template v-else>
<div class="nothing"></div>
<div class="nothing-text">当前用户暂无征信报告~</div>
</template>
</div>
</template>
<script>
import pdf from 'vue-pdf-signature';//引入组件
// eslint-disable-next-line import/extensions
import CMapReaderFactory from 'vue-pdf/src/CMapReaderFactory.js';
export default {
name: 'PDF',
created() {
this.prohibit();
},
data() {
return {
hasReport: null, // 是否有报告
scale: 80,
scaleAdd: 10,
againTimer: false, // 是否从新计时
show: false,
fullscreenLoading: true,
blobURL: '',
currentPage: 1,
pageCount: 1,
seconds: 10,
count: '',
timer: null,
keyCodeList: [
'Escape',
'Meta',
'F12',
'F6',
'c',
'i',
'j',
'p',
's',
'u',
'v',
],
numPages: '',
chooseIndex: 0,
};
},
components: {
pdf,
},
mounted() {
this.inquire();
},
methods: {
async inquire() {
this.fullscreenLoading = true
const res = await this.$http.get(`/thirdData/renhang/${this.$route.query.identityCard}`)
if (res.code === 0 && res.data) {
this.fullscreenLoading = false
this.hasReport = true
const dataUrl = `data:application/pdf;base64,${res.data}`
this.blobURL = pdf.createLoadingTask({ url: dataUrl, CMapReaderFactory });
this.blobURL.promise.then((val) => {
this.numPages = val.numPages;
});
this.Time(1800);
} else {
this.hasReport = false
this.fullscreenLoading = false
}
},
// 天 时 分 秒 格式化函数
countDown(seconds) {
// eslint-disable-next-line radix
let d = parseInt(seconds / (24 * 60 * 60));
d = d < 10 ? `0${d}` : d;
// eslint-disable-next-line radix
let h = parseInt((seconds / (60 * 60)) % 24);
h = h < 10 ? `0${h}` : h;
// eslint-disable-next-line radix
let m = parseInt((seconds / 60) % 60);
m = m < 10 ? `0${m}` : m;
// eslint-disable-next-line radix
let s = parseInt(seconds % 60);
s = s < 10 ? `0${s}` : s;
this.count = `${d}天${h}时${m}分${s}秒`;
},
//定时器没过1秒参数减1
Time(time) {
this.timer = setInterval(() => {
if (time > 0) {
// eslint-disable-next-line no-param-reassign
time -= 1;
this.countDown(time);
this.againTimer = false;
} else {
this.show = true;
this.againTimer = true;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
},
showInfo() {
this.show = false;
this.Time(1800);
},
// 禁用鼠标右击、F12
prohibit() {
// eslint-disable-next-line func-names
document.oncontextmenu = function () {
return false;
};
// eslint-disable-next-line consistent-return, func-names
document.onkeydown = function (e) {
if (e) {
this.show = false;
}
if (e.ctrlKey && this.keyCodeList.include(e.code)) {
return false;
}
};
},
clickPdf(index) {
if (this.chooseIndex === index) return;
document
.querySelector(`#choose${index}`)
.scrollIntoView({ block: 'start', behavior: 'smooth' });
this.chooseIndex = index
},
plus() {
let { scale } = this;
scale += this.scaleAdd;
scale = scale > 100 ? 100 : scale;
this.scale = scale;
},
minus() {
let { scale } = this;
scale -= this.scaleAdd;
scale = scale < 50 ? 50 : scale;
this.scale = scale;
},
},
};
</script>
<style scoped lang="scss">
.nothing {
width: 125px;
height: 105px;
margin: 0 auto;
margin-top: 250px;
background: url('~@/assets/images/nothing.svg');
background-size: 100% 100%;
}
.nothing-text {
color: #8d8d8d;
margin-top: 20px;
text-align: center;
}
.remaining {
font-size: 14px;
float: right;
color: #303133;
}
.pdf-container {
width: 100%;
height: 100%;
.top {
width: 100%;
height: 60px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
.top-container {
height: 40px;
width: 200px;
margin-left: 38%;
display: flex;
align-items: center;
.segmentation {
font-size: 16px;
margin-left: 20px;
margin-right: 20px;
color: #8d8d8d;
}
.scale-container {
display: flex;
align-items: center;
.add {
cursor: pointer;
font-size: 22px;
color: #8d8d8d;
}
.num {
width: 50px;
height: 20px;
font-size: 14px;
text-align: center;
line-height: 20px;
margin-right: 10px;
background: #eaeaea;
color: #8d8d8d;
}
.lessen {
cursor: pointer;
font-size: 22px;
color: #8d8d8d;
margin-right: 10px;
}
}
.page {
font-size: 16px;
color: #8d8d8d;
}
}
}
.contianer {
display: flex;
justify-content: space-between;
height: 720px;
.pdf-wrapper {
width: 100%;
height: 720px;
margin-top: 10px;
.pdf-scroll {
width: 60%;
margin: 0 auto;
height: 720px;
overflow: auto;
}
}
.right-list {
width: 270px;
height: 100%;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.thumbnail-wrapper {
width: 140px;
height: 98%;
margin: 0 auto;
margin-top: 10px;
overflow: auto;
.thumbnail {
cursor: pointer;
margin-bottom: 20px;
height: 200px;
background-size: 100%;
border: 5px solid transparent;
.text {
text-align: center;
font-size: 14px;
color: #8d8d8d;
}
}
.thumbnail-active {
cursor: pointer;
margin-bottom: 20px;
height: 200px;
background-size: 100%;
border: 5px solid #c1c1c1;
.text {
text-align: center;
font-size: 14px;
color: #8d8d8d;
}
}
}
}
}
}
::-webkit-scrollbar {
width: 0;
}
.mask {
width: 100%;
height: 100%;
position: fixed;
top: 60px;
left: 0%;
background-color: rgba(243, 243, 243, 0.8);
backdrop-filter: brightness(200%) saturate(130%) blur(8px);
display: flex;
align-items: center;
justify-content: center;
.mask-btn {
margin: 0 auto;
}
}
.span-clas {
color: #8d8d8d;
margin-left: 34%;
.btn-list {
margin: 0 auto;
margin-top: 20px;
margin-bottom: 20px;
width: 250px;
display: flex;
align-items: center;
justify-content: space-between;
}
}
</style>
直接复制代码然后改下接口就可以用啦~