vue項(xiàng)目中常見的三種文件類型在線預(yù)覽實(shí)現(xiàn)(pdf/word/excel表格)
前言
之前做PDF預(yù)覽一直用的pdf.js,這次沒有太多附加需求比較簡(jiǎn)單簡(jiǎn),所以決定用vue-pdf這個(gè)組件,雖然說它沒有原生那樣強(qiáng)大,但已經(jīng)滿足常用的需求了,重要是這個(gè)組件使用簡(jiǎn)單。順便也把word文件和excel表格預(yù)覽也整理了下,接下來我們直接進(jìn)入正題!
一、預(yù)覽word文件
1.安裝 npm 依賴
npm i docx-preview@0.1.4 npm i jszip
2.預(yù)覽在線地址文件
<template>
<div class="home">
<div ref="file"></div>
</div>
</template>
<script>
import axios from 'axios'
const docx = require('docx-preview');
window.JSZip = require('jszip')
export default {
mounted(){
axios({
method: 'get',
responseType: 'blob', // 設(shè)置響應(yīng)文件格式
url: '/docx',
}).then(({data}) => {
docx.renderAsync(data,this.$refs.file) // 渲染到頁面預(yù)覽
})
}
}
</script>
3.預(yù)覽本地文件
<template>
<div class="my-component" ref="preview">
<input type="file" @change="preview" ref="file">
</div>
</template>
<script>
const docx = require('docx-preview');
window.JSZip = require('jszip')
export default {
methods:{
preview(e){
docx.renderAsync(this.$refs.file.files[0],this.$refs.preview) // 渲染到頁面預(yù)覽
}
}
};
</script>
<style lang="less" scoped>
.my-component{
width: 100%;
height: 90vh;
border: 1px solid #000;
}
</style>
二、預(yù)覽excel表格
1.安裝依賴
npm i xlsx
2.預(yù)覽在線表格
<template>
<div class="home">
<div v-html="tableau"></div>
</div>
</template>
<script>
import axios from 'axios'
import XLSX from "xlsx";
export default {
data(){
return {
tableau: null,
}
},
mounted(){
axios.get('/xlsx',{
responseType: "arraybuffer", // 設(shè)置響應(yīng)體類型為arraybuffer
}).then(({data})=> {
let workbook = XLSX.read(new Uint8Array(data), {type:"array"}); // 解析數(shù)據(jù)
var worksheet = workbook.Sheets[workbook.SheetNames[0]]; // workbook.SheetNames 下存的是該文件每個(gè)工作表名字,這里取出第一個(gè)工作表
this.tableau = XLSX.utils.sheet_to_html(worksheet); // 渲染
})
}
}
</script>
三、pdf預(yù)覽
1.安裝依賴vue-pdf
npm install --save vue-pdf
2.在需要的頁面注冊(cè)
<script>
import PDF from 'vue-pdf'
export default {
components:{
PDF,
},
data(){
return {
}
}
</script>
3.使用
一般預(yù)覽pdf時(shí)都是很多頁的,為了提升性能可以做個(gè)分頁。page: 當(dāng)前顯示的頁數(shù),比如第一頁page=1;rotate : 旋轉(zhuǎn)角度,比如0就是不旋轉(zhuǎn),+90,-90 就是水平旋轉(zhuǎn)。progress :當(dāng)前頁面的加載進(jìn)度,范圍是0-1 ,等于1的時(shí)候代表當(dāng)前頁已經(jīng)完全加載完成了。page-loaded :頁面加載成功的回調(diào)函數(shù),不咋能用到。num-pages :總頁數(shù);error :加載錯(cuò)誤的回調(diào)link-clicked:?jiǎn)螜C(jī)pdf內(nèi)的鏈接會(huì)觸發(fā)。print 這個(gè)是打印函數(shù)。(注意:谷歌瀏覽器預(yù)覽時(shí)會(huì)出現(xiàn)亂碼,這個(gè)問題是因?yàn)槟鉷df中使用了自定義字體導(dǎo)致的。若要解決須替換node_modules/vue-pdf/src/pdfjsWrapper.js,替換的pdfjsWrapper.js我放在文章最后)
<!-- 預(yù)覽PDF -->
<el-dialog v-dialogDrag :visible.sync="previewDialog">
<template>
<div>
<div class="tools">
<el-button :theme="'default'" type="submit" :title="'上一頁'" @click.stop="prePage" class="mr10"> 上一頁</el-button>
<el-button :theme="'default'" type="submit" :title="'下一頁'" @click.stop="nextPage" class="mr10"> 下一頁</el-button>
<div class="page">{{pageNum}}/{{pageTotalNum}} </div>
<el-button :theme="'default'" type="submit" :title="'順時(shí)針旋轉(zhuǎn)'" @click.stop="clock" class="mr10"> 順時(shí)針旋轉(zhuǎn)</el-button>
<el-button :theme="'default'" type="submit" :title="'逆時(shí)針旋轉(zhuǎn)'" @click.stop="counterClock" class="mr10"> 逆時(shí)針旋轉(zhuǎn)</el-button>
<el-button :theme="'default'" type="submit" :title="'打印'" @click.stop="pdfPrintAll" class="mr10"> 打印</el-button>
</div>
<pdf ref="pdf" :src="url" :page="pageNum" :rotate="pageRotate" @progress="loadedRatio = $event" @page-loaded="pageLoaded($event)"
@num-pages="pageTotalNum=$event" @error="pdfError($event)" @link-clicked="page = $event"></pdf>
</div>
</template>
</el-dialog>
<script>
import PDF from 'vue-pdf'
export default {
components:{
PDF,
},
data(){
return {
previewDialog:false,
url: "http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf",
pageNum: 1,
pageTotalNum: 1,
pageRotate: 0,
// 加載進(jìn)度
loadedRatio: 0,
curPageNum: 0,
},
methods:{
/**
* 預(yù)覽PDF
*/
previewPDF(row,index) {
this.previewDialog = true;
console.log("", row,index);
},
// 上一頁函數(shù),
prePage() {
var page = this.pageNum
page = page > 1 ? page - 1 : this.pageTotalNum
this.pageNum = page
},
// 下一頁函數(shù)
nextPage() {
var page = this.pageNum
page = page < this.pageTotalNum ? page + 1 : 1
this.pageNum = page
},
// 頁面順時(shí)針翻轉(zhuǎn)90度。
clock() {
this.pageRotate += 90
},
// 頁面逆時(shí)針翻轉(zhuǎn)90度。
counterClock() {
this.pageRotate -= 90
},
// 頁面加載回調(diào)函數(shù),其中e為當(dāng)前頁數(shù)
pageLoaded(e) {
this.curPageNum = e
},
// 錯(cuò)誤時(shí)回調(diào)函數(shù)。
pdfError(error) {
console.error(error)
},
// 打印全部
pdfPrintAll() {
/**
* 打印界面字符亂碼是因?yàn)槟鉷df中使用了自定義字體導(dǎo)致的,谷歌瀏覽器打印的時(shí)候預(yù)覽界面真的變成了真·方塊字 ,解決方案如下:
* 用文章最后的pdfjsWrapper.js在替換掉node_modules/vue-pdf/src/pdfjsWrapper.js
*/
console.log("打印");
this.$refs.pdf.print()
},
},
}
</script>
4.加載本地pdf文件
如果需要加載本地pdf文件(不需要?jiǎng)t不用配置),需要我們做一下本地配置flie-loader才行,否則webpack無法編譯pdf類型的文件,配置方法也很簡(jiǎn)單,在項(xiàng)目根目錄找到vue.config.js文件(若沒有則在根目錄下新建一個(gè)vue.config.js)。之后再url:require("…/assets/xxx.pdf")就沒有任何問題了,注意,vue-pdf src接收的是string對(duì)象,如果直接傳url這里報(bào)錯(cuò)了,需要傳url.default一下。
先安裝file-loader
npm install --save file-loader
然后在vue.config.js中加入以下內(nèi)容:
module.exports = {
chainWebpack: config => {
const fileRule = config.module.rule('file')
fileRule.uses.clear()
fileRule
.test(/\.pdf|ico$/)
.use('file-loader')
.loader('file-loader')
.options({
limit: 10000,
})
},
publicPath: './'
}
5.解決pdf使用自定義字體預(yù)覽和打印亂碼問題:pdfjsWrapper.js
import { PDFLinkService } from 'pdfjs-dist/es5/web/pdf_viewer';
var pendingOperation = Promise.resolve();
export default function(PDFJS) {
function isPDFDocumentLoadingTask(obj) {
return typeof(obj) === 'object' && obj !== null && obj.__PDFDocumentLoadingTask === true;
// or: return obj.constructor.name === 'PDFDocumentLoadingTask';
}
function createLoadingTask(src, options) {
var source;
if ( typeof(src) === 'string' )
source = { url: src };
else if ( src instanceof Uint8Array )
source = { data: src };
else if ( typeof(src) === 'object' && src !== null )
source = Object.assign({}, src);
else
throw new TypeError('invalid src type');
// source.verbosity = PDFJS.VerbosityLevel.INFOS;
// source.pdfBug = true;
// source.stopAtErrors = true;
if ( options && options.withCredentials )
source.withCredentials = options.withCredentials;
var loadingTask = PDFJS.getDocument(source);
loadingTask.__PDFDocumentLoadingTask = true; // since PDFDocumentLoadingTask is not public
if ( options && options.onPassword )
loadingTask.onPassword = options.onPassword;
if ( options && options.onProgress )
loadingTask.onProgress = options.onProgress;
return loadingTask;
}
function PDFJSWrapper(canvasElt, annotationLayerElt, emitEvent) {
var pdfDoc = null;
var pdfPage = null;
var pdfRender = null;
var canceling = false;
canvasElt.getContext('2d').save();
function clearCanvas() {
canvasElt.getContext('2d').clearRect(0, 0, canvasElt.width, canvasElt.height);
}
function clearAnnotations() {
while ( annotationLayerElt.firstChild )
annotationLayerElt.removeChild(annotationLayerElt.firstChild);
}
this.destroy = function() {
if ( pdfDoc === null )
return;
// Aborts all network requests and destroys worker.
pendingOperation = pdfDoc.destroy();
pdfDoc = null;
}
this.getResolutionScale = function() {
return canvasElt.offsetWidth / canvasElt.width;
}
this.printPage = function(dpi, pageNumberOnly) {
if ( pdfPage === null )
return;
// 1in == 72pt
// 1in == 96px
var PRINT_RESOLUTION = dpi === undefined ? 150 : dpi;
var PRINT_UNITS = PRINT_RESOLUTION / 72.0;
var CSS_UNITS = 96.0 / 72.0;
var printContainerElement = document.createElement('div');
printContainerElement.setAttribute('id', 'print-container')
function removePrintContainer() {
printContainerElement.parentNode.removeChild(printContainerElement);
}
new Promise(function(resolve, reject) {
printContainerElement.frameBorder = '0';
printContainerElement.scrolling = 'no';
printContainerElement.width = '0px;'
printContainerElement.height = '0px;'
printContainerElement.style.cssText = 'position: absolute; top: 0; left: 0';
window.document.body.appendChild(printContainerElement);
resolve(window)
})
.then(function(win) {
win.document.title = '';
return pdfDoc.getPage(1)
.then(function(page) {
var viewport = page.getViewport({ scale: 1 });
printContainerElement.appendChild(win.document.createElement('style')).textContent =
'@supports ((size:A4) and (size:1pt 1pt)) {' +
'@page { margin: 1pt; size: ' + ((viewport.width * PRINT_UNITS) / CSS_UNITS) + 'pt ' + ((viewport.height * PRINT_UNITS) / CSS_UNITS) + 'pt; }' +
'}' +
'#print-canvas { display: none }' +
'@media print {' +
'body { margin: 0 }' +
'#print-canvas { page-break-before: avoid; page-break-after: always; page-break-inside: avoid; display: block }' +
'body > *:not(#print-container) { display: none; }' +
'}'+
'@media screen {' +
'body { margin: 0 }' +
'}'
return win;
})
})
.then(function(win) {
var allPages = [];
for ( var pageNumber = 1; pageNumber <= pdfDoc.numPages; ++pageNumber ) {
if ( pageNumberOnly !== undefined && pageNumberOnly.indexOf(pageNumber) === -1 )
continue;
allPages.push(
pdfDoc.getPage(pageNumber)
.then(function(page) {
var viewport = page.getViewport({ scale: 1 });
var printCanvasElt = printContainerElement.appendChild(win.document.createElement('canvas'));
printCanvasElt.setAttribute('id', 'print-canvas')
printCanvasElt.width = (viewport.width * PRINT_UNITS);
printCanvasElt.height = (viewport.height * PRINT_UNITS);
return page.render({
canvasContext: printCanvasElt.getContext('2d'),
transform: [ // Additional transform, applied just before viewport transform.
PRINT_UNITS, 0, 0,
PRINT_UNITS, 0, 0
],
viewport: viewport,
intent: 'print'
}).promise;
})
);
}
Promise.all(allPages)
.then(function() {
win.focus(); // Required for IE
if (win.document.queryCommandSupported('print')) {
win.document.execCommand('print', false, null);
} else {
win.print();
}
removePrintContainer();
})
.catch(function(err) {
removePrintContainer();
emitEvent('error', err);
})
})
}
this.renderPage = function(rotate) {
if ( pdfRender !== null ) {
if ( canceling )
return;
canceling = true;
pdfRender.cancel().catch(function(err) {
emitEvent('error', err);
});
return;
}
if ( pdfPage === null )
return;
var pageRotate = (pdfPage.rotate === undefined ? 0 : pdfPage.rotate) + (rotate === undefined ? 0 : rotate);
var scale = canvasElt.offsetWidth / pdfPage.getViewport({ scale: 1 }).width * (window.devicePixelRatio || 1);
var viewport = pdfPage.getViewport({ scale: scale, rotation:pageRotate });
emitEvent('page-size', viewport.width, viewport.height, scale);
canvasElt.width = viewport.width;
canvasElt.height = viewport.height;
pdfRender = pdfPage.render({
canvasContext: canvasElt.getContext('2d'),
viewport: viewport
});
annotationLayerElt.style.visibility = 'hidden';
clearAnnotations();
var viewer = {
scrollPageIntoView: function(params) {
emitEvent('link-clicked', params.pageNumber)
},
};
var linkService = new PDFLinkService();
linkService.setDocument(pdfDoc);
linkService.setViewer(viewer);
pendingOperation = pendingOperation.then(function() {
var getAnnotationsOperation =
pdfPage.getAnnotations({ intent: 'display' })
.then(function(annotations) {
PDFJS.AnnotationLayer.render({
viewport: viewport.clone({ dontFlip: true }),
div: annotationLayerElt,
annotations: annotations,
page: pdfPage,
linkService: linkService,
renderInteractiveForms: false
});
});
var pdfRenderOperation =
pdfRender.promise
.then(function() {
annotationLayerElt.style.visibility = '';
canceling = false;
pdfRender = null;
})
.catch(function(err) {
pdfRender = null;
if ( err instanceof PDFJS.RenderingCancelledException ) {
canceling = false;
this.renderPage(rotate);
return;
}
emitEvent('error', err);
}.bind(this))
return Promise.all([getAnnotationsOperation, pdfRenderOperation]);
}.bind(this));
}
this.forEachPage = function(pageCallback) {
var numPages = pdfDoc.numPages;
(function next(pageNum) {
pdfDoc.getPage(pageNum)
.then(pageCallback)
.then(function() {
if ( ++pageNum <= numPages )
next(pageNum);
})
})(1);
}
this.loadPage = function(pageNumber, rotate) {
pdfPage = null;
if ( pdfDoc === null )
return;
pendingOperation = pendingOperation.then(function() {
return pdfDoc.getPage(pageNumber);
})
.then(function(page) {
pdfPage = page;
this.renderPage(rotate);
emitEvent('page-loaded', page.pageNumber);
}.bind(this))
.catch(function(err) {
clearCanvas();
clearAnnotations();
emitEvent('error', err);
});
}
this.loadDocument = function(src) {
pdfDoc = null;
pdfPage = null;
emitEvent('num-pages', undefined);
if ( !src ) {
canvasElt.removeAttribute('width');
canvasElt.removeAttribute('height');
clearAnnotations();
return;
}
// wait for pending operation ends
pendingOperation = pendingOperation.then(function() {
var loadingTask;
if ( isPDFDocumentLoadingTask(src) ) {
if ( src.destroyed ) {
emitEvent('error', new Error('loadingTask has been destroyed'));
return
}
loadingTask = src;
} else {
loadingTask = createLoadingTask(src, {
onPassword: function(updatePassword, reason) {
var reasonStr;
switch (reason) {
case PDFJS.PasswordResponses.NEED_PASSWORD:
reasonStr = 'NEED_PASSWORD';
break;
case PDFJS.PasswordResponses.INCORRECT_PASSWORD:
reasonStr = 'INCORRECT_PASSWORD';
break;
}
emitEvent('password', updatePassword, reasonStr);
},
onProgress: function(status) {
var ratio = status.loaded / status.total;
emitEvent('progress', Math.min(ratio, 1));
}
});
}
return loadingTask.promise;
})
.then(function(pdf) {
pdfDoc = pdf;
emitEvent('num-pages', pdf.numPages);
emitEvent('loaded');
})
.catch(function(err) {
clearCanvas();
clearAnnotations();
emitEvent('error', err);
})
}
annotationLayerElt.style.transformOrigin = '0 0';
}
return {
createLoadingTask: createLoadingTask,
PDFJSWrapper: PDFJSWrapper,
}
}總結(jié)
到此這篇關(guān)于vue項(xiàng)目中常見的三種文件類型在線預(yù)覽實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)vue文件在線預(yù)覽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue基于html2canvas和jspdf?生成pdf?、解決jspdf中文亂碼問題的方法詳解
這篇文章主要介紹了vue基于html2canvas和jspdf?生成pdf?、解決jspdf中文亂碼問題的方法,結(jié)合實(shí)例形式詳細(xì)描述了中文亂碼問題的原因、解決方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2023-06-06
解決vue的過渡動(dòng)畫無法正常實(shí)現(xiàn)問題
今天小編就為大家分享一篇解決vue的過渡動(dòng)畫無法正常實(shí)現(xiàn)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-10-10
解決vue項(xiàng)目nginx部署到非根目錄下刷新空白的問題
今天小編就為大家分享一篇解決vue項(xiàng)目nginx部署到非根目錄下刷新空白的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09
vue/cli3.0腳手架部署到nginx時(shí)頁面空白的問題及解決
這篇文章主要介紹了vue/cli3.0腳手架部署到nginx時(shí)頁面空白的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
安裝nvm?node版本管理器的操作方法(vue2.x遷移vue3.x)
這篇文章主要介紹了安裝nvm?node版本管理器(vue2.x遷移vue3.x)的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
Element?Plus組件Form表單Table表格二次封裝的完整過程
一般在后臺(tái)管理系統(tǒng)的開發(fā)中,都會(huì)遇到很多table,但每一次都去引入el-table就會(huì)導(dǎo)致代碼十分冗余,所以基于組件做一下二次封裝成自己需要的組件就十分nice,下面這篇文章主要給大家介紹了關(guān)于Element?Plus組件Form表單Table表格二次封裝的相關(guān)資料,需要的朋友可以參考下2022-09-09
vue中生成條形碼(jsbarcode)和二維碼(qrcodejs2)的簡(jiǎn)單示例
在vue項(xiàng)目中難免遇到有要生成條形碼或者二維碼的功能需求,下面這篇文章主要給大家介紹了關(guān)于vue中生成條形碼(jsbarcode)和二維碼(qrcodejs2)的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12

