Vue2 doc、excel、pdf、ppt、txt、图片等在线预览
- 安装
- 使用
-
- 目录结构
- 直接上代码
-
- src\components\FileView\doc\index.vue
- src\components\FileView\excel\index.vue
- src\components\FileView\img\index.vue
- src\components\FileView\pdf\index.vue
- src\components\FileView\ppt\index.vue
- src\components\FileView\txt\index.vue
- src\components\FileView\index.vue
- src\components\FileView\video\index.vue
- 应用实例
安装
powershell
npm install --save @vue-office/docx @vue-office/excel @vue-office/pdf vue-demi@0.14.6
vue-demi@0.14.6; 其中 @0.14.6 为版本号,可以不加,默认下载最新版。
如果是 vue2.6 版本或以下还需要额外安装 @vue/composition-api
powershell
npm install @vue/composition-api
使用
目录结构
src\components
直接上代码
src\components\FileView\doc\index.vue
javascript
<template>
<div>
<vue-office-docx
:src="docx"
style="height: 75vh"
@rendered="rendered"
@error="errorHandler"
/>
</div>
</template>
<script>
//引入VueOfficeDocx组件
import VueOfficeDocx from "@vue-office/docx";
//引入相关样式
import "@vue-office/docx/lib/index.css";
export default {
components: {
VueOfficeDocx,
},
props: {
docx: {
type: String,
default:
"http://qncdn.qkongtao.cn/lib/teamadmin/files/Hadoop2.7.1%E4%BC%AA%E5%88%86%E5%B8%83%E5%BC%8F%E9%9B%86%E7%BE%A4%E5%AE%89%E8%A3%85%E6%96%87%E6%A1%A3.docx", //设置文档网络地址,可以是相对地址
},
},
data() {
return {};
},
methods: {
rendered() {
console.log("渲染完成");
},
errorHandler() {
console.log("渲染失败");
},
},
};
</script>
src\components\FileView\excel\index.vue
javascript
<template>
<vue-office-excel
:src="excel"
:options="options"
@rendered="renderedHandler"
@error="errorHandler"
style="height: 75vh"
/>
</template>
<script>
//引入VueOfficeExcel组件
import VueOfficeExcel from "@vue-office/excel";
//引入相关样式
import "@vue-office/excel/lib/index.css";
export default {
components: {
VueOfficeExcel,
},
props: {
excel: {
type: String,
default:
"http://qncdn.qkongtao.cn/lib/teamadmin/files/2021%E5%B1%8A%E5%85%A8%E5%9B%BD%E5%90%84%E5%9C%B0%E6%B4%BE%E9%81%A3%E5%9C%B0%E5%9D%80.xlsx", //设置文档地址
},
},
data() {
return {
options: {
xls: true, //预览xlsx文件设置为false 预览xls文件设置为true
minColLength: 0,
minRowLength: 0,
widthOffset: 10,
heightOffset: 10,
},
};
},
methods: {
renderedHandler() {
console.log("渲染完成");
},
errorHandler() {
console.log("渲染失败");
},
},
};
</script>
src\components\FileView\img\index.vue
javascript
<template>
<div style="display: flex; justify-content: center">
<el-image :src="imgUrl" :preview-src-list="[imgUrl]"> fit="contain"></el-image>
</div>
</template>
<script>
export default {
props: {
imgUrl: {
type: String,
default: "",
},
},
components: {},
data() {
return {};
},
methods: {
closeImage() {},
},
};
</script>
src\components\FileView\pdf\index.vue
javascript
<template>
<vue-office-pdf :src="pdf" style="height: 75vh" @rendered="rendered" />
</template>
<script>
//引入VueOfficePdf组件
import VueOfficePdf from "@vue-office/pdf";
export default {
components: {
VueOfficePdf,
},
props: {
pdf: {
type: String,
default:
"http://qncdn.qkongtao.cn/lib/teamadmin/files/%E6%B7%B1%E5%9C%B3-xx-Java%E9%AB%98%E7%BA%A7.pdf",
},
},
data() {
return {};
},
methods: {
rendered() {
console.log("渲染完成");
},
},
};
</script>
src\components\FileView\ppt\index.vue
PPT预览使用微软提供的方法进行文件预览
javascript
<template>
<iframe
id="iframe1"
width="100%"
height="700px"
frameborder="0"
border="0"
marginwidth="0"
marginheight="0"
scrolling="no"
allowtransparency="no"
:src="'https://view.officeapps.live.com/op/embed.aspx?src=' + fileUrl"
></iframe>
</template>
<script>
export default {
props: {
fileUrl: {
type: String,
default: "",
},
},
components: {},
data() {
return {};
},
methods: {},
};
</script>
src\components\FileView\txt\index.vue
javascript
<template>
<div>
<pre class="pre-txt">{{ txtContent }}</pre>
</div>
</template>
<script>
import axios from "axios";
export default {
components: {},
props: {
fileUrl: {
type: String,
default: "https://example.com/your-txt-file.txt",
},
},
data() {
return {
txtContent: "",
};
},
mounted() {
this.fetchTxtFile();
},
methods: {
fetchTxtFile() {
axios
.get(this.fileUrl)
.then((response) => {
this.txtContent = response.data;
})
.catch((error) => {
console.error("获取txt文件失败:", error);
});
},
},
};
</script>
<style scoped>
.pre-txt {
font-size: 12px;
padding: 0;
width: 100%;
max-height: 70vh;
min-height: 70vh;
margin: 0;
background: #f0f0f0;
line-height: 20px; /* 行距 */
overflow: auto; /* 超出宽度出现滑动条 */
overflow-y: auto; /* 作用是隐藏IE的右侧滑动条 */
}
</style>
src\components\FileView\index.vue
javascript
<template>
<el-dialog
title="文件预览"
:visible.sync="open"
width="50vw"
:before-close="handleClose"
:close-on-click-modal="true"
>
<DOC :docx="url" v-if="componentToUse === 'DOC'"></DOC>
<PDF :pdf="url" v-if="componentToUse === 'PDF'"></PDF>
<EXCEL :excel="url" v-if="componentToUse === 'EXCEL'"></EXCEL>
<TXT :fileUrl="url" v-if="componentToUse === 'TXT'"></TXT>
<IMG :imgUrl="url" v-if="componentToUse === 'IMG'"></IMG>
<PPT :fileUrl="url" v-if="componentToUse === 'PPT'"></PPT>
<!-- <span slot="footer" class="dialog-footer">
<el-button @click="handleClose" size="small">关 闭</el-button>
</span> -->
<div v-if="componentToUse === 'WZ'">不支持的文件类型</div>
</el-dialog>
</template>
<script>
import DOC from "./doc/index.vue";
import PDF from "./pdf/index.vue";
import EXCEL from "./excel/index.vue";
import TXT from "./txt/index.vue";
import IMG from "./img/index.vue";
import PPT from "./ppt/index.vue";
export default {
components: {
DOC,
PDF,
EXCEL,
TXT,
IMG,
PPT,
},
props: {
open: {
type: Boolean,
default: false,
},
url: {
type: String,
default:
"http://qncdn.qkongtao.cn/lib/teamadmin/files/Hadoop2.7.1%E4%BC%AA%E5%88%86%E5%B8%83%E5%BC%8F%E9%9B%86%E7%BE%A4%E5%AE%89%E8%A3%85%E6%96%87%E6%A1%A3.docx", //设置文档网络地址,可以是相对地址
},
},
data() {
return {
componentToUse: null, // 用于存储要使用的组件
};
},
watch: {
url() {
// 当 url 改变时,重新判断文件类型并设置组件
this.determineComponentType();
},
},
created() {
// 在组件创建时判断文件类型并设置组件
this.determineComponentType();
},
methods: {
rendered() {
console.log("渲染完成");
},
handleClose() {
this.$emit("update:open", false);
},
determineComponentType() {
const fileExtension = this.url.split(".").pop().toLowerCase();
switch (fileExtension) {
case "docx":
this.componentToUse = "DOC";
break;
case "pdf":
this.componentToUse = "PDF";
break;
case "xlsx":
case "xls":
this.componentToUse = "EXCEL";
break;
case "txt":
case "html":
case "vue":
case "js":
case "json":
this.componentToUse = "TXT";
break;
case "png":
case "jpg":
case "jpeg":
this.componentToUse = "IMG";
break;
case "ppt":
case "pptx":
this.componentToUse = "PPT";
break;
default:
console.error("不支持的文件类型");
this.componentToUse = "WZ";
}
},
},
};
</script>
src\components\FileView\video\index.vue
javascript
<template>
<div
class="m-video"
:class="{ 'u-video-hover': !hidden }"
:style="`width: ${veoWidth}; height: ${veoHeight};`"
>
<video
ref="veoRef"
class="u-video"
:style="`object-fit: ${zoom};`"
:src="src"
:poster="veoPoster"
:autoplay="autoplay"
:controls="!originPlay && controls"
:loop="loop"
:muted="autoplay || muted"
:preload="preload"
crossorigin="anonymous"
@loadeddata="poster ? () => false : getPoster()"
@pause="showPlay ? onPause() : () => false"
@playing="showPlay ? onPlaying() : () => false"
@click.prevent.once="onPlay"
v-bind="$attrs"
>
您的浏览器不支持video标签。
</video>
<svg
v-show="originPlay || showPlay"
class="u-play"
:class="{ hidden: hidden }"
:style="`width: ${playWidth}px; height: ${playWidth}px;`"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M4.75 6.75C4.75 5.64543 5.64543 4.75 6.75 4.75H17.25C18.3546 4.75 19.25 5.64543 19.25 6.75V17.25C19.25 18.3546 18.3546 19.25 17.25 19.25H6.75C5.64543 19.25 4.75 18.3546 4.75 17.25V6.75Z"
></path>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M15.25 12L9.75 8.75V15.25L15.25 12Z"
></path>
</svg>
</div>
</template>
<script>
export default {
name: "Video",
props: {
src: {
// 视频文件url,必传,支持网络地址 https 和相对地址 require('@/assets/files/Bao.mp4')
type: String,
required: true,
default: "",
},
poster: {
// 视频封面url,支持网络地址 https 和相对地址 require('@/assets/images/Bao.jpg')
type: String,
default: "",
},
second: {
// 在未设置封面时,自动截取视频第 second 秒对应帧作为视频封面
type: Number,
default: 0.5,
},
width: {
// 视频播放器宽度,单位 px
type: [String, Number],
default: 800,
},
height: {
// 视频播放器高度,单位 px
type: [String, Number],
default: 450,
},
/*
参考 MDN 自动播放指南:https://developer.mozilla.org/zh-CN/docs/Web/Media/Autoplay_guide
Autoplay 功能
据新政策,媒体内容将在满足以下至少一个的条件下自动播放:
1.音频被静音或其音量设置为 0
2.用户和网页已有交互行为(包括点击、触摸、按下某个键等等)
3.网站已被列入白名单;如果浏览器确定用户经常与媒体互动,这可能会自动发生,也可能通过首选项或其他用户界面功能手动发生
4.自动播放策略应用到<iframe>或者其文档上
autoplay:由于目前在最新版的Chrome浏览器(以及所有以Chromium为内核的浏览器)中,
已不再允许自动播放音频和视频。就算你为video或audio标签设置了autoplay属性也一样不能自动播放!
解决方法:设置视频 autoplay 时,视频必须设置为静音 muted: true 即可实现自动播放,
然后用户可以使用控制栏开启声音,类似某宝商品自动播放的宣传视频逻辑
*/
autoplay: {
// 视频就绪后是否马上播放,优先级高于 preload
type: Boolean,
default: false,
},
controls: {
// 是否向用户显示控件,比如进度条,全屏等
type: Boolean,
default: true,
},
loop: {
// 视频播放完成后,是否循环播放
type: Boolean,
default: false,
},
muted: {
// 是否静音
type: Boolean,
default: false,
},
preload: {
// 是否在页面加载后载入视频,如果设置了autoplay属性,则preload将被忽略;
type: String,
default: "metadata", // auto:一旦页面加载,则开始加载视频; metadata:当页面加载后仅加载视频的元数据 none:页面加载后不应加载视频
},
showPlay: {
// 播放暂停时是否显示播放器中间的暂停图标
type: Boolean,
default: true,
},
playWidth: {
// 中间播放暂停按钮的边长
type: Number,
default: 96,
},
zoom: {
// video的poster默认图片和视频内容缩放规则
type: String,
default: "contain", // none:(默认)保存原有内容,不进行缩放; fill:不保持原有比例,内容拉伸填充整个内容容器; contain:保存原有比例,内容以包含方式缩放; cover:保存原有比例,内容以覆盖方式缩放
},
},
data() {
return {
veoPoster: this.poster,
originPlay: true,
hidden: false,
};
},
computed: {
veoWidth() {
return '100%';
},
veoHeight() {
if (typeof this.height === "number") {
return this.height + "px";
}
return this.height;
},
},
mounted() {
if (this.autoplay) {
this.hidden = true;
this.originPlay = false;
}
/*
自定义设置播放速度,经测试:
在vue2中需设置:this.$refs.veoRef.playbackRate = 2
在vue3中需设置:veo.value.defaultPlaybackRate = 2
*/
// this.$refs.veoRef.playbackRate = 2
},
methods: {
/*
loadedmetadata 事件在元数据(metadata)被加载完成后触发
loadeddata 事件在媒体当前播放位置的视频帧(通常是第一帧)加载完成后触发
若在移动/平板设备的浏览器设置中开启了流量节省(data-saver)模式,该事件则不会被触发。
preload 为 none 时不会触发
*/
getPoster() {
// 在未设置封面时,自动获取视频0.5s对应帧作为视频封面
// 由于不少视频第一帧为黑屏,故设置视频开始播放时间为0.5s,即取该时刻帧作为封面图
this.$refs.veoRef.currentTime = this.second;
// 创建canvas元素
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// canvas画图
canvas.width = this.$refs.veoRef.videoWidth;
canvas.height = this.$refs.veoRef.videoHeight;
ctx.drawImage(this.$refs.veoRef, 0, 0, canvas.width, canvas.height);
// 把canvas转成base64编码格式
this.veoPoster = canvas.toDataURL("image/png");
},
onPlay() {
if (this.originPlay) {
this.$refs.veoRef.currentTime = 0;
this.originPlay = false;
}
if (this.autoplay) {
this.$refs.veoRef.pause();
} else {
this.hidden = true;
this.$refs.veoRef.play();
}
},
onPause() {
this.hidden = false;
},
onPlaying() {
this.hidden = true;
},
},
};
</script>
<style scoped>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.m-video {
display: inline-block;
position: relative;
background: #000;
cursor: pointer;
}
.u-play {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
fill: none;
color: #fff;
pointer-events: none;
opacity: 0.7;
transition: opacity 0.3s;
}
.hidden {
opacity: 0;
}
.u-video {
display: inline-block;
width: 100%;
height: 100%;
vertical-align: bottom;
}
.u-video-hover {
}
</style>
应用实例
建议在main.js
中增加全局组件引用
javascript
import FileView from '@/components/FileView'
Vue.component('FileView',FileView)
如果不使用全局挂载,那么请局部注册
我的使用场景
javascript
<FileView :open.sync="fileViewOpen" :url="fileUrl" v-if="fileViewOpen" />