vue项目如何实现前端预览word与pdf格式文件
作者:百思不得小李
最近做vue项目遇到一个需求,就是前端实现上传word或pdf文件后,后端返回文件对应的文件流,前端需要在页面上展示出来。word预览简单一些,pdf预览我试过pdfjs,vue-pdf总是报各种奇奇怪怪的bug,但最终总算解决了问题,先看一下页面最终呈现效果吧:
页面上传pdf文件效果如下:
页面预览pdf文件效果如下:
页面上传word文件效果如下:
页面预览word文件效果如下:
这里先从上传组件页面说起,上传页面组件完整代码如下,按钮里面v-show=“$checkPermission([‘Register_tuotpUpload’])“都是给这个按钮设置了按钮权限的,我们只需要关注上传那一部分的代码即可,我们用了el-upload组件实现的手动上传,由于需求要求只能上传word和pdf,所以能看到属性设置的有 accept=”.pdf, .doc, .docx”,然后不展示上传成功的文件的列表设置的属性有:show-file-list=“false”,而handleExceed回调函数和limit都是为了限制只能上传一个文件,上传前的回调钩子函数beforeAvatarUpload里进行了文件类型判断与提醒,手动上传是通过UploadFile里进行完成的,需要注意的是由于docx-preview这个插件只能预览后缀为docx的word文件,如果是doc后缀格式的word文件一定要让后端强制将上传doc格式的文件改为docx格式,目前对于doc格式的word文件实现网页在线预览我只想到了docx-preview这个插件和这个解决办法:
<template> <div class="app-container"> <div class="cardWhite"> <div class="itemBox"> <div class="headerTitle">基本信息</div> <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="120px" class="demo-ruleForm" > <el-row> <el-col :span="12"> <el-form-item label="链路名称" prop="name"> <el-input v-model="ruleForm.name" placeholder="请输入链路名称" clearable ></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="链路类型" prop="linkType"> <el-select v-model="ruleForm.linkType" placeholder="请选择链路类型" style="width:100%" clearable > <el-option v-for="item in linkTypeList" :key="item.val" :label="item.key" :value="item.val" ></el-option> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="链路走向" prop="go"> <el-row> <el-col :span="10"> <el-select v-model="ruleForm.srcNetwork" placeholder="请选择" style="width:100%" clearable @clear="clearSrc" @change="srcChange" > <el-option v-for="item in scrList" :key="item.val" :label="item.key" :value="item.val" ></el-option> </el-select> </el-col> <el-col :span="4"> <div style="text-align: center;width:100%"> <img src="@/assets/toRight.png" style="width:3.75rem;height:0.75rem;margin:0 auto" /> </div> </el-col> <el-col :span="10"> <el-select v-model="ruleForm.dstNetwork" placeholder="请选择" style="width:100%" :clearable="false" @clear="clearDst" @change="dstChange" > <el-option v-for="item in dstList" :key="item.val" :label="item.key" :value="item.val" ></el-option> </el-select> </el-col> </el-row> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="物理位置" prop="physicalPosition"> <el-input v-model="ruleForm.physicalPosition" placeholder="请输入链路物理位置" clearable ></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="所属机构" prop="orangeName"> <el-input v-model="ruleForm.orangeName" placeholder="例:xx市公安局" clearable ></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="行政区编码" prop="organCode"> <el-input v-model="ruleForm.organCode" placeholder="请输入行政区编码,例:027" clearable ></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="责任人" prop="dutyPerson"> <el-input v-model="ruleForm.dutyPerson" placeholder="请输入链路责任人" clearable ></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="责任人电话" prop="dutyTel"> <el-input v-model="ruleForm.dutyTel" placeholder="请输入链路责任人电话" clearable ></el-input> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="公安网邮箱" prop="dutyEmail"> <el-input v-model="ruleForm.dutyEmail" placeholder="请输入负责人公安网邮箱" clearable ></el-input> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="链路IP地址" prop="ip"> <el-input v-model="ruleForm.ip" placeholder="请输入链路IP地址" clearable ></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="链路端口" prop="port"> <el-input-number placeholder="请输入链路端口" type="text" :min="0" :controls="false" v-model.trim="ruleForm.port" style="width:100%" ></el-input-number> </el-form-item> </el-col> </el-row> <el-row> <el-col :span="12"> <el-form-item label="管理页面" prop="webUrl"> <el-input v-model="ruleForm.webUrl" placeholder="请输入链路管理页面" clearable ></el-input> </el-form-item> </el-col> </el-row> <el-row> <el-col :push="2"> <el-button class="filter-item" type="primary" icon="el-icon-plus" @click="saveForm" v-show="$checkPermission(['Register_boundarySave'])" > 保存 </el-button> </el-col> </el-row> </el-form> </div> <div class="itemBox"> <div class="headerTitle">链路拓扑图</div> <el-form :model="tuopuForm" ref="tuopuForm" label-width="120px"> <el-row> <el-col :span="12"> <el-form-item label="拓扑图" prop="fileName"> <el-input v-model="tuopuForm.fileName" placeholder="请选择电脑中拓扑图文件" clearable disabled > <el-upload accept=".pdf, .doc, .docx" action="string" :limit="1" :on-exceed="handleExceed" :before-upload="beforeAvatarUpload" :http-request="UploadFile" slot="append" :show-file-list="false" > <el-button type="primary" v-show="$checkPermission(['Register_tuotpUpload'])" icon="el-icon-upload2" style="background:#1890ff;color:#fff;border-top-left-radius:0;border-bottom-left-radius:0" >上传</el-button > </el-upload> </el-input> </el-form-item> </el-col> <el-col :span="3" :push="1"> <el-button class="filter-item" type="primary" icon="el-icon-view" @click="preview" v-show="$checkPermission(['Register_tuotpPreview'])" > 预览 </el-button> </el-col> </el-row> </el-form> </div> <div class="itemBox"> <div class="headerTitle">设备信息列表</div> <el-row type="flex" justify="end" style="margin:0.5rem 0;"> <el-button class="filter-item" type="primary" icon="el-icon-plus" @click="addEquipment" v-show="$checkPermission(['Register_equipmentAdd'])" > 添加 </el-button> </el-row> <div> <commonTable :tableHead="tableHead" :tableData="tableData" :dataFiter="true" :selectionFlag="false" :dropdownList="[]" :resizable="true" :tableLoading="tableLoading" :showListD="showListD" :toolBoxFlag="false" @sortChange="() => {}" @selection-change="() => {}" @selectAction="() => {}" @addData="() => {}" :actionFlag="false" :actionList="[]" :freeElfFlag="false" :xuhaoFlag="true" :freeWidth="480" > <template slot-scope="scope" slot="doSomething" fixed="right" align="left" > <el-button icon="el-icon-edit" type="primary" @click="handlerUpdate(scope.rows)" v-show="$checkPermission(['Register_equipmentEdit'])" >编辑</el-button > <el-button icon="el-icon-delete" type="danger" @click="handlerDelete(scope.rows)" v-show="$checkPermission(['Register_equipmentDelete'])" style="margin-left:-0.015rem" >删除</el-button > </template> </commonTable> </div> </div> <div class="itemBox"> <div class="headerTitle">链路注册</div> <el-row type="flex" justify="end" style="margin:0.5rem 0;"> <el-button class="filter-item" type="primary" icon="el-icon-plus" @click="addRegister" v-show="$checkPermission(['Register_registerAdd'])" > 添加 </el-button> </el-row> <div> <commonTable :tableHead="Register_tableHead" :tableData="Register_tableData" :dataFiter="true" :selectionFlag="false" :dropdownList="[]" :resizable="true" :tableLoading="Register_tableLoading" :showListD="Register_showListD" :toolBoxFlag="false" @sortChange="() => {}" @selection-change="() => {}" @selectAction="() => {}" @addData="() => {}" :actionFlag="false" :actionList="[]" :freeElfFlag="false" :xuhaoFlag="true" :freeWidth="480" > <template slot-scope="scope" slot="status"> <el-tag v-if="scope.rows.status == 1">已注册</el-tag> <el-tag type="success" v-if="scope.rows.status == 0" >未注册</el-tag > <el-tag type="danger" v-if="scope.rows.status == 2" >注册失败</el-tag > </template> <template slot-scope="scope" slot="doSomething" fixed="right" align="left" > <el-button icon="el-icon-edit" type="primary" v-if=" scope.rows.status == 1 && $checkPermission(['Register_registerOff']) " @click="handlerLogoff(scope.rows)" >注销</el-button > <el-button icon="el-icon-edit" type="primary" v-if=" ($checkPermission(['Register_registerGo']) && scope.rows.status == 0) || scope.rows.status == 2 " @click="handlerLogoff(scope.rows)" >注册</el-button > <el-button icon="el-icon-delete" type="danger" v-if="$checkPermission(['Register_registerDelete'])" @click="Register_handlerDelete(scope.rows)" style="margin-left:-0.015rem" >删除</el-button > </template> </commonTable> </div> </div> </div> <!-- 添加和编辑设备弹窗 --> <el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible" width="800px" :before-close="handleClose" :close-on-click-modal="false" > <add-edit @refresh="fetchData" @closeDialog="dialogFormVisible = false" class="AddEdit" ref="AddEdit" :devTypeList="EquipmentList" v-if="dialogFormVisible" /> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false"> 取消 </el-button> <el-button type="primary" @click="submitForm()"> 确定 </el-button> </div> </el-dialog> <!-- 链路注册弹窗 --> <el-dialog title="链路注册" :visible.sync="Register_dialogFormVisible" width="800px" :before-close="Register_handleClose" :close-on-click-modal="false" > <register-add @reg_refresh="Register_fetchData" @reg_closeDialog="Register_dialogFormVisible = false" ref="RegisterAdd" v-if="Register_dialogFormVisible" /> <div slot="footer" class="dialog-footer"> <el-button @click="Register_dialogFormVisible = false"> 取消 </el-button> <el-button type="primary" @click="Register_submitForm()"> 确定 </el-button> </div> </el-dialog> </div> </template> <script> import commonTable from "@/components/common-table"; import Pagination from "@/components/Pagination"; import AddEdit from "./EquipmentAddEdit.vue"; import RegisterAdd from "./RegisterAdd.vue"; import * as api from "@/api/datax-register.js"; import axios from "axios"; import { getToken } from "@/utils/auth"; export default { components: { Pagination, commonTable, AddEdit, RegisterAdd }, data() { const validateGo = (rule, value, callback) => { // if (!value) { // callback("请输入平台IP地址"); // } else { // let index = value.indexOf("/"); // let findHengT = value.indexOf("-"); // let findHengP = value.indexOf("——"); // if (index > -1 || findHengT > -1 || findHengP > -1) { // callback("请输入正确格式的平台IP地址"); // } else { // validator.IpArea(rule, value, callback); // } // } if (!this.ruleForm.srcNetwork || !this.ruleForm.dstNetwork) { callback("请选择链路走向"); } else { callback(); } }; return { ruleForm: { name: "", linkType: "", srcNetwork: "", dstNetwork: "", physicalPosition: "", orangeName: "", organCode: "", dutyPerson: "", dutyTel: "", dutyEmail: "", ip: "", port: undefined, webUrl: "" }, rules: { name: [{ required: true, message: "请输入链路名称", trigger: "blur" }], linkType: [ { required: true, message: "请选择链路类型", trigger: "blur" } ], go: [ { required: true, message: "请选择链路走向", trigger: "blur", validator: validateGo } ], physicalPosition: [ { required: false, message: "请输入链路物理位置", trigger: "blur" } ], orangeName: [ { required: true, message: "请输入所属机构", trigger: "blur" } ], organCode: [ { required: true, message: "请输入行政区编码", trigger: "blur" } ], dutyPerson: [ { required: true, message: "请输入链路责任人", trigger: "blur" } ], dutyTel: [ { required: true, message: "请输入链路责任人电话", trigger: "blur" } ], dutyEmail: [ { required: false, message: "请输入负责人公安网邮箱", trigger: "blur" } ], ip: [{ required: false, message: "请输入链路IP地址", trigger: "blur" }], port: [{ required: true, message: "请输入链路端口", trigger: "blur" }], webUrl: [ { required: false, message: "请输入链路管理页面", trigger: "blur" } ] }, linkTypeList: [], scrList: [], dstList: [], tuopuForm: { fileName: "", fileUrl: "" }, tableHead: [ { label: "设备名称", prop: "name", type: "normal", sortable: false }, { label: "设备类型", prop: "devType", type: "normal", sortable: false // width: 150 }, { label: "厂商", prop: "manufacturer", type: "normal", sortable: false // width: 150 }, { label: "型号", prop: "model", type: "normal", sortable: false // width: 150 }, { label: "设备IP", prop: "devIp", type: "normal", sortable: false }, { label: "子网掩码", prop: "ipMask", type: "normal", sortable: false // width: 150 }, { label: "网关", prop: "ipGaway", type: "normal", sortable: false // width: 150 }, { label: "安装时间", prop: "installTime", type: "normal", sortable: false, width: 180 }, { label: "操作", prop: "doSomething", type: "slot", sortable: false, slotName: "doSomething", width: 220 } // { // label: "任务详情", // prop: "log_text", // type: "slot", // sortable: false, // slotName: "log_text", // width: 100 // } ], showListD: [ "name", "devType", "manufacturer", "model", "devIp", "ipMask", "ipGaway", "installTime", "doSomething" // "log_text" ], dialogStatus: "", dialogFormVisible: false, textMap: { update: "编辑设备", Edit: "编辑设备", edit: "编辑设备", create: "添加设备" }, tableData: [], tableLoading: false, Register_tableHead: [ { label: "平台名称", prop: "name", type: "normal", sortable: false }, { label: "平台IP地址", prop: "ip", type: "normal", sortable: false }, { label: "平台端口", prop: "port", type: "normal", sortable: false }, { label: "平台唯一标识", prop: "uniquePlatformCode", type: "normal", sortable: false }, { label: "注册时间", prop: "lastTime", type: "normal", sortable: false, width: 180 }, { label: "注册状态", prop: "status", type: "slot", slotName: "status", sortable: false }, { label: "操作", prop: "doSomething", type: "slot", sortable: false, slotName: "doSomething", width: 220 } // { // label: "任务详情", // prop: "log_text", // type: "slot", // sortable: false, // slotName: "log_text", // width: 100 // } ], Register_tableData: [], Register_tableLoading: false, Register_showListD: [ "name", "ip", "port", "uniquePlatformCode", "lastTime", "status", "doSomething" // "log_text" ], Register_dialogFormVisible: false, fileList: null, //拓扑图文件列表 tuotpData: null, EquipmentList: [], originalList: [] }; }, created() { this.getNews(); }, methods: { getNews() { //获取边界信息 this.getBoundaryDetail(); //获取拓扑图 this.getTuotp(); //获取设备列表 this.fetchData(); //获取链路注册列表 this.Register_fetchData(); //获取公共下拉 this.getSelect(); }, saveForm() { this.$refs["ruleForm"].validate(valid => { if (valid) { let params = { ...this.ruleForm }; console.log("修改入参", params); //修改 api .boundaryEdit(params) .then(res => { console.log("修改", res); if (res.code == 200) { this.$notify({ title: "成功", message: "边界信息修改成功", type: "success", duration: 2000 }); } }) .catch(err => {}); } }); }, preview() { console.log("预览", this.fileList, this.tuopuForm); if (!this.fileList) { this.$message.error( "拓扑图文件为空不能预览,请先上传拓扑图文件后再预览", 6000 ); return false; } this.$router.push({ path: "TuotpPreview", query: { fileName: this.tuopuForm.fileName, fileUrl: this.tuopuForm.fileUrl } }); }, addEquipment() { this.dialogStatus = "create"; this.dialogFormVisible = true; setTimeout(() => { this.$refs["AddEdit"].dialogStatus = "create"; // this.$refs.AddEdit.resetTransferDetail(); // this.getCommonData(); }, 1); }, //获取公共下拉 getSelect() { api .getLinkEquimentSelect({ clsName: "link_direction" }) .then(res => { console.log("链路走向下拉", res); this.scrList = res.data; this.dstList = res.data; this.originalList = res.data; }) .catch(err => {}); api .getLinkEquimentSelect({ clsName: "link_type" }) .then(res => { console.log("链路类型下拉", res); this.linkTypeList = res.data; }) .catch(err => {}); api .getLinkEquimentSelect({ clsName: "dev_type" }) .then(res => { console.log("设备类型下拉", res); this.EquipmentList = res.data; }) .catch(err => {}); }, //获取边界信息 getBoundaryDetail() { api .boundaryDetail() .then(res => { // console.log("获取边界信息", res); this.ruleForm = res.data; this.ruleForm.port = res.data.port ? res.data.port : undefined; }) .catch(err => {}); }, //获取拓扑图 getTuotp() { api .boundaryTuopoDetail() .then(res => { // console.log("获取拓扑图成功", res); this.tuopuForm = res.data; this.tuopuPreview(); }) .catch(err => { // console.log("获取拓扑图失败", err); }); }, //获取设备列表 fetchData() { this.tableLoading = true; api .equipmentList({ page: 1, page_size: 99999, ip: "" }) .then(res => { // console.log("设备列表", res); if (res.code == 200) { this.tableData = res.data.list; this.tableLoading = false; } }) .catch(err => {}); }, //编辑 handlerUpdate(row) { // console.log("点了修改", row); this.dialogStatus = "Edit"; this.dialogFormVisible = true; setTimeout(() => { this.$refs["AddEdit"].setData(row); this.$refs["AddEdit"].dialogStatus = "Edit"; }, 1); }, handleClose() { this.dialogFormVisible = false; }, submitForm() { this.$refs["AddEdit"].submitForm(); }, //删除 handlerDelete(row) { this.$confirm("确定删除吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(() => { api .equipmentDelete(row) .then(response => { if (response.code == 200) { this.fetchData(); this.$notify({ title: "成功", message: "删除成功", type: "success", duration: 2000 }); } }) .catch(err => {}); }); // const index = this.list.indexOf(row) }, //注销 handlerLogoff(row) { api .registerStatusSwitch({ id: row.id, status: row.status == 1 ? 2 : 1 }) .then(res => { this.Register_fetchData(); }) .catch(err => {}); }, //链路删除 Register_handlerDelete(row) { this.$confirm("确定删除吗?", "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(() => { let id = row.id; api.VideoTaskDelete(id).then(response => { if (response.code == 200) { this.fetchData(); this.$notify({ title: "成功", message: "删除成功", type: "success", duration: 2000 }); } }); }); }, //链路添加 addRegister() { this.$refs["ruleForm"].validate(valid => { if (valid) { this.Register_dialogFormVisible = true; setTimeout(() => { // this.$refs["AddEdit"].dialogStatus = "create"; // this.$refs.AddEdit.resetTransferDetail(); // this.getCommonData(); }, 1); } else { this.$message.error("基本信息为空不能添加,请先补充基本信息", 6000); } }); }, Register_handleClose() { this.Register_dialogFormVisible = false; }, Register_submitForm() { this.$refs["RegisterAdd"].submitForm(); }, //获取链路注册列表 Register_fetchData() { this.Register_tableLoading = true; api .registerList({ page: 1, page_size: 99999, ip: "" }) .then(res => { console.log("注册列表", res); if (res.code == 200) { this.Register_tableData = res.data.list; this.Register_tableLoading = false; } }) .catch(err => {}); }, handleExceed(files, fileList) { if (files.length > 1) { this.$message.warning( `当前限制最多选择1个文件上传,本次选择了${files.length}个文件` ); } }, //文件上传前的钩子 beforeAvatarUpload(file) { console.log("文件上传前的钩子", file); // 文件类型判断 var testmsg = file.name.substring(file.name.lastIndexOf(".") + 1); const extension = testmsg === "doc"; const extension2 = testmsg === "pdf"; const extension3 = testmsg === "docx"; if (!extension && !extension2 && !extension3) { this.$message({ message: "上传的拓扑图文件只能是word、pdf格式,请重新上传!", type: "error", duration: 6000 }); this.fileList = null; return false; } else { this.fileList = file; } }, //自定义上传 UploadFile() { // 参数拼接 let fileData = new FormData(); fileData.append("file", this.fileList); // 调用接口 axios({ url: `${window.g.API_URL}/api/cascade/topo/upload`, method: "post", data: fileData, headers: { "Content-Type": "multipart/form-data", Authorization: getToken() } }) .then(res => { console.log("上传成功", res); if (res.data.code == 200) { this.tuopuForm = res.data.data; //拓扑预览 this.tuopuPreview(); } else { console.log("上传失败1"); this.$message({ message: "上传拓扑图文件失败,请稍后重试!", type: "error", duration: 6000 }); } }) .catch(err => { console.log("上传失败2", err); this.$message({ message: "上传拓扑图文件失败,请稍后重试!", type: "error", duration: 6000 }); }); }, tuopuPreview() { axios({ url: `${window.g.API_URL}/api/cascade/topo/preview`, method: "get", params: { fileUrl: this.tuopuForm.fileUrl }, headers: { Authorization: getToken() }, responseType: "arraybuffer" }) .then(res => { console.log("拓扑预览获取成功", res); if (res.status == 200) { this.fileList = res.data; } }) .catch(err => { console.log("拓扑预览失败失败", err); }); }, srcChange(val) { console.log("srcChange", val); if (val == this.ruleForm.dstNetwork) { this.ruleForm.dstNetwork = ""; } this.dstList = this.originalList.filter(k => { return k.val != val; }); }, clearSrc() { this.scrList = JSON.parse(JSON.stringify(this.originalList)); }, dstChange(val) { console.log("dstChange", val); if (val == this.ruleForm.srcNetwork) { this.ruleForm.srcNetwork = ""; } this.scrList = this.originalList.filter(k => { return k.val != val; }); }, clearDst() { this.dstList = JSON.parse(JSON.stringify(this.originalList)); } } }; </script> <style lang="scss" scoped> .app-container { .cardWhite { .itemBox { margin-bottom: 1.5rem; .headerTitle { font-size: 1rem; font-weight: bold; } } } } ::v-deep .el-input-number .el-input__inner { text-align: left; } </style>
上传功能实现之后,我们再看预览功能。我们需求是点击预览按钮之后跳到一个新页面进行预览,我先是对文件是否为空是否没上传进行了一个判断拦截,由于预览页面组件与当前页面组件既非父子关系,又非爷孙关系,八竿子打不着的关系,我们就不能通过绑定和sessionStorage来进行流文件的传递,只能在预览页面再发一次请求获取文件流,我们可以把参数通过路由带上,预览按钮对应的方法代码如下:
preview() { console.log("预览", this.fileList, this.tuopuForm); if (!this.fileList) { this.$message.error( "拓扑图文件为空不能预览,请先上传拓扑图文件后再预览", 6000 ); return false; } this.$router.push({ path: "TuotpPreview", query: { fileName: this.tuopuForm.fileName, fileUrl: this.tuopuForm.fileUrl } }); }
再来看预览页面,也就是TuotpPreview.vue组件。word预览比较简单,先通过命令cnpm i docx-preview --save安装插件docx-preview,需要注意的是这个插件只能预览后缀为docx的word文件,如果是doc后缀格式的word文件一定要让后端强制将上传doc格式的文件改为docx格式,然后当前页面组件就直接import { defaultOptions, renderAsync } from “docx-preview”;引入,最后在data里进行docxOptions配置,然后在页面上 要显示的div上绑定一个 id="bodyContainer"的,因为下面要通过document.getElementById来获取dom并操作dom,最后调用renderAsync方法即可。
cnpm i docx-preview --save //安装word预览插件docx-preview import { defaultOptions, renderAsync } from "docx-preview"; //引入docx-preview插件对应的方法 docxOptions: { className: "kaimo-docx-666", // string:默认和文档样式类的类名/前缀 inWrapper: true, // boolean:启用围绕文档内容的包装器渲染 ignoreWidth: false, // boolean:禁用页面的渲染宽度 ignoreHeight: false, // boolean:禁止渲染页面高度 ignoreFonts: false, // boolean:禁用字体渲染 breakPages: true, // boolean:在分页符上启用分页 ignoreLastRenderedPageBreak: true, // boolean:在 lastRenderedPageBreak 元素上禁用分页 experimental: false, // boolean:启用实验功能(制表符停止计算) trimXmlDeclaration: true, // boolean:如果为true,解析前会从 xml 文档中移除 xml 声明 useBase64URL: false, // boolean:如果为true,图片、字体等会转为base 64 URL,否则使用URL.createObjectURL useMathMLPolyfill: false, // boolean:包括用于 chrome、edge 等的 MathML polyfill。 showChanges: false, // boolean:启用文档更改的实验性渲染(插入/删除) debug: false // boolean:启用额外的日志记录 },
pdf文件的预览是通过获取到blob文件流之后,直接通过window.open打开新窗口,通过浏览器就能预览pdf,还有一种就是我现在的这种需求,要在页面上预览pdf,那就需要通过iframe,然后把blob流对应的src地址赋值回显即可。完整代码如下:
<template> <div> <!-- 拓扑图预览 --> <div class="app-container"> <div class="cardWhite"> <div class="topArea"> <div class="backBox"> <img src="@/assets/goBack.png" @click="goBack" alt="" /> </div> <div class="titleBox"> {{ myTitle }} </div> </div> <div class="previewBox"> <div id="bodyContainer"> <iframe :src="pdfUrl" width="100%" height="750px" /> </div> </div> </div> </div> </div> </template> <script> import axios from "axios"; import { getToken } from "@/utils/auth"; import { defaultOptions, renderAsync } from "docx-preview"; export default { data() { return { myTitle: "", previewData: null, docxOptions: { className: "kaimo-docx-666", // string:默认和文档样式类的类名/前缀 inWrapper: true, // boolean:启用围绕文档内容的包装器渲染 ignoreWidth: false, // boolean:禁用页面的渲染宽度 ignoreHeight: false, // boolean:禁止渲染页面高度 ignoreFonts: false, // boolean:禁用字体渲染 breakPages: true, // boolean:在分页符上启用分页 ignoreLastRenderedPageBreak: true, // boolean:在 lastRenderedPageBreak 元素上禁用分页 experimental: false, // boolean:启用实验功能(制表符停止计算) trimXmlDeclaration: true, // boolean:如果为true,解析前会从 xml 文档中移除 xml 声明 useBase64URL: false, // boolean:如果为true,图片、字体等会转为base 64 URL,否则使用URL.createObjectURL useMathMLPolyfill: false, // boolean:包括用于 chrome、edge 等的 MathML polyfill。 showChanges: false, // boolean:启用文档更改的实验性渲染(插入/删除) debug: false // boolean:启用额外的日志记录 }, num: 1, numPages: 0, pdfUrl: "" }; }, created() { console.log("接收", this.$route.query); this.resetTitle(); this.getFile(); }, methods: { goBack() { this.$router.go(-1); }, //获取文件流 getFile() { axios({ url: `${window.g.API_URL}/api/cascade/topo/preview`, method: "get", params: { fileUrl: this.$route.query.fileUrl }, headers: { Authorization: getToken() }, responseType: "arraybuffer" }) .then(res => { console.log("文件流获取成功", res); if (res.status == 200) { this.previewData = res.data; if ( this.$route.query.fileName && this.$route.query.fileName.indexOf(".docx") && this.$route.query.fileName.indexOf(".pdf") == -1 ) { //word--docx格式 this.wordPreview(res.data); } else if ( this.$route.query.fileName && this.$route.query.fileName.indexOf(".doc") && this.$route.query.fileName.indexOf(".pdf") == -1 ) { //word--doc格式 this.wordPreview(res.data); } else if ( this.$route.query.fileName && this.$route.query.fileName.indexOf(".pdf") ) { //pdf this.pdfPreview(res.data); } } }) .catch(err => { console.log("文件流获取失败", err); this.$message.error("获取文件信息失败,请稍后重试", 6000); }); }, //重置标题 resetTitle() { let fileName = this.$route.query.fileName; console.log("fileName", fileName); if ( fileName && fileName.indexOf(".docx") && fileName.indexOf(".pdf") == -1 ) { //word--docx格式 let wordDocxArr = fileName.split(".docx"); console.log("wordDocxArr", wordDocxArr); this.myTitle = wordDocxArr[0]; } else if ( fileName && fileName.indexOf(".doc") && fileName.indexOf(".pdf") == -1 ) { //word--doc格式 let wordDocArr = fileName.split(".docx"); console.log("wordDocArr", wordDocArr); this.myTitle = wordDocArr[0]; } else if (fileName && fileName.indexOf(".pdf")) { //pdf let pdfArr = fileName.split(".pdf"); console.log("pdfArr", pdfArr); this.myTitle = pdfArr[0]; } }, // word文档预览 wordPreview(buffer) { console.log("文档buffer", buffer); let bodyContainer = document.getElementById("bodyContainer"); renderAsync( buffer, // Blob | ArrayBuffer | Uint8Array, 可以是 JSZip.loadAsync 支持的任何类型 bodyContainer, // HTMLElement 渲染文档内容的元素, null, // HTMLElement, 用于呈现文档样式、数字、字体的元素。如果为 null,则将使用 bodyContainer。 this.docxOptions // 配置 ) .then(res => { console.log("res---->", res); }) .catch(err => { console.log("err---->", err); }); }, //pdf预览 pdfPreview(data) { // data是一个ArrayBuffer格式,也是一个buffer流的数据 console.log("pdf流", data); const binaryData = []; binaryData.push(data); //获取blob链接 let pdfUrl = window.URL.createObjectURL( new Blob(binaryData, { type: "application/pdf" }) ); console.log("pdfUrl", pdfUrl); // window.open(pdfUrl); 这种方式是直接打开新浏览器窗口预览 this.pdfUrl = pdfUrl; } } }; </script> <style lang="scss" scoped> .app-container { .cardWhite { display: flex; flex-direction: column; .topArea { display: flex; position: relative; .backBox { margin-bottom: 1rem; img { cursor: pointer; } } .titleBox { text-align: center; background: #fff; color: #000000; position: absolute; top: 0; left: 50%; width: auto; } } } } </style>
总结
到此这篇关于vue项目如何实现前端预览word与pdf格式文件的文章就介绍到这了,更多相关vue前端预览word与pdf格式文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!