最近做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="px" | |
class="demo-ruleForm" | |
> | |
<el-row> | |
<el-col :span=""> | |
<el-form-item label="链路名称" prop="name"> | |
<el-input | |
v-model="ruleForm.name" | |
placeholder="请输入链路名称" | |
clearable | |
></el-input> | |
</el-form-item> | |
</el-col> | |
<el-col :span=""> | |
<el-form-item label="链路类型" prop="linkType"> | |
<el-select | |
v-model="ruleForm.linkType" | |
placeholder="请选择链路类型" | |
style="width:%" | |
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=""> | |
<el-form-item label="链路走向" prop="go"> | |
<el-row> | |
<el-col :span=""> | |
<el-select | |
v-model="ruleForm.srcNetwork" | |
placeholder="请选择" | |
style="width:%" | |
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=""> | |
<div style="text-align: center;width:%"> | |
<img | |
src="@/assets/toRight.png" | |
style="width:.75rem;height:0.75rem;margin:0 auto" | |
/> | |
</div> | |
</el-col> | |
<el-col :span=""> | |
<el-select | |
v-model="ruleForm.dstNetwork" | |
placeholder="请选择" | |
style="width:%" | |
: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=""> | |
<el-form-item label="物理位置" prop="physicalPosition"> | |
<el-input | |
v-model="ruleForm.physicalPosition" | |
placeholder="请输入链路物理位置" | |
clearable | |
></el-input> | |
</el-form-item> | |
</el-col> | |
<el-col :span=""> | |
<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=""> | |
<el-form-item label="行政区编码" prop="organCode"> | |
<el-input | |
v-model="ruleForm.organCode" | |
placeholder="请输入行政区编码,例:" | |
clearable | |
></el-input> | |
</el-form-item> | |
</el-col> | |
<el-col :span=""> | |
<el-form-item label="责任人" prop="dutyPerson"> | |
<el-input | |
v-model="ruleForm.dutyPerson" | |
placeholder="请输入链路责任人" | |
clearable | |
></el-input> | |
</el-form-item> | |
</el-col> | |
<el-col :span=""> | |
<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=""> | |
<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=""> | |
<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=""> | |
<el-form-item label="链路端口" prop="port"> | |
<el-input-number | |
placeholder="请输入链路端口" | |
type="text" | |
:min="" | |
:controls="false" | |
v-model.trim="ruleForm.port" | |
style="width:%" | |
></el-input-number> | |
</el-form-item> | |
</el-col> | |
</el-row> | |
<el-row> | |
<el-col :span=""> | |
<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=""> | |
<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="px"> | |
<el-row> | |
<el-col :span=""> | |
<el-form-item label="拓扑图" prop="fileName"> | |
<el-input | |
v-model="tuopuForm.fileName" | |
placeholder="请选择电脑中拓扑图文件" | |
clearable | |
disabled | |
> | |
<el-upload | |
accept=".pdf, .doc, .docx" | |
action="string" | |
:limit="" | |
: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-upload" | |
style="background:#ff;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="" :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:.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="" | |
> | |
<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:-.015rem" | |
>删除</el-button | |
> | |
</template> | |
</commonTable> | |
</div> | |
</div> | |
<div class="itemBox"> | |
<div class="headerTitle">链路注册</div> | |
<el-row type="flex" justify="end" style="margin:.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="" | |
> | |
<template slot-scope="scope" slot="status"> | |
<el-tag v-if="scope.rows.status ==">已注册</el-tag> | |
<el-tag type="success" v-if="scope.rows.status ==" | |
>未注册</el-tag | |
> | |
<el-tag type="danger" v-if="scope.rows.status ==" | |
>注册失败</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 == && | |
$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 ==) || | |
scope.rows.status == | |
" | |
@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:-.015rem" | |
>删除</el-button | |
> | |
</template> | |
</commonTable> | |
</div> | |
</div> | |
</div> | |
<!-- 添加和编辑设备弹窗 --> | |
<el-dialog | |
:title="textMap[dialogStatus]" | |
:visible.sync="dialogFormVisible" | |
width="px" | |
: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="px" | |
: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 > - || 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: | |
}, | |
{ | |
label: "厂商", | |
prop: "manufacturer", | |
type: "normal", | |
sortable: false | |
// width: | |
}, | |
{ | |
label: "型号", | |
prop: "model", | |
type: "normal", | |
sortable: false | |
// width: | |
}, | |
{ | |
label: "设备IP", | |
prop: "devIp", | |
type: "normal", | |
sortable: false | |
}, | |
{ | |
label: "子网掩码", | |
prop: "ipMask", | |
type: "normal", | |
sortable: false | |
// width: | |
}, | |
{ | |
label: "网关", | |
prop: "ipGaway", | |
type: "normal", | |
sortable: false | |
// width: | |
}, | |
{ | |
label: "安装时间", | |
prop: "installTime", | |
type: "normal", | |
sortable: false, | |
width: | |
}, | |
{ | |
label: "操作", | |
prop: "doSomething", | |
type: "slot", | |
sortable: false, | |
slotName: "doSomething", | |
width: | |
} | |
// { | |
// label: "任务详情", | |
// prop: "log_text", | |
// type: "slot", | |
// sortable: false, | |
// slotName: "log_text", | |
// width: | |
// } | |
], | |
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: | |
}, | |
{ | |
label: "注册状态", | |
prop: "status", | |
type: "slot", | |
slotName: "status", | |
sortable: false | |
}, | |
{ | |
label: "操作", | |
prop: "doSomething", | |
type: "slot", | |
sortable: false, | |
slotName: "doSomething", | |
width: | |
} | |
// { | |
// label: "任务详情", | |
// prop: "log_text", | |
// type: "slot", | |
// sortable: false, | |
// slotName: "log_text", | |
// width: | |
// } | |
], | |
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 ==) { | |
this.$notify({ | |
title: "成功", | |
message: "边界信息修改成功", | |
type: "success", | |
duration: | |
}); | |
} | |
}) | |
.catch(err => {}); | |
} | |
}); | |
}, | |
preview() { | |
console.log("预览", this.fileList, this.tuopuForm); | |
if (!this.fileList) { | |
this.$message.error( | |
"拓扑图文件为空不能预览,请先上传拓扑图文件后再预览", | |
); | |
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(); | |
},); | |
}, | |
//获取公共下拉 | |
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:, | |
page_size:, | |
ip: "" | |
}) | |
.then(res => { | |
// console.log("设备列表", res); | |
if (res.code ==) { | |
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"; | |
},); | |
}, | |
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 ==) { | |
this.fetchData(); | |
this.$notify({ | |
title: "成功", | |
message: "删除成功", | |
type: "success", | |
duration: | |
}); | |
} | |
}) | |
.catch(err => {}); | |
}); | |
// const index = this.list.indexOf(row) | |
}, | |
//注销 | |
handlerLogoff(row) { | |
api | |
.registerStatusSwitch({ | |
id: row.id, | |
status: row.status == ? 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 ==) { | |
this.fetchData(); | |
this.$notify({ | |
title: "成功", | |
message: "删除成功", | |
type: "success", | |
duration: | |
}); | |
} | |
}); | |
}); | |
}, | |
//链路添加 | |
addRegister() { | |
this.$refs["ruleForm"].validate(valid => { | |
if (valid) { | |
this.Register_dialogFormVisible = true; | |
setTimeout(() => { | |
// this.$refs["AddEdit"].dialogStatus = "create"; | |
// this.$refs.AddEdit.resetTransferDetail(); | |
// this.getCommonData(); | |
},); | |
} else { | |
this.$message.error("基本信息为空不能添加,请先补充基本信息",); | |
} | |
}); | |
}, | |
Register_handleClose() { | |
this.Register_dialogFormVisible = false; | |
}, | |
Register_submitForm() { | |
this.$refs["RegisterAdd"].submitForm(); | |
}, | |
//获取链路注册列表 | |
Register_fetchData() { | |
this.Register_tableLoading = true; | |
api | |
.registerList({ | |
page:, | |
page_size:, | |
ip: "" | |
}) | |
.then(res => { | |
console.log("注册列表", res); | |
if (res.code ==) { | |
this.Register_tableData = res.data.list; | |
this.Register_tableLoading = false; | |
} | |
}) | |
.catch(err => {}); | |
}, | |
handleExceed(files, fileList) { | |
if (files.length >) { | |
this.$message.warning( | |
`当前限制最多选择个文件上传,本次选择了${files.length}个文件` | |
); | |
} | |
}, | |
//文件上传前的钩子 | |
beforeAvatarUpload(file) { | |
console.log("文件上传前的钩子", file); | |
// 文件类型判断 | |
var testmsg = file.name.substring(file.name.lastIndexOf(".") +); | |
const extension = testmsg === "doc"; | |
const extension = testmsg === "pdf"; | |
const extension = testmsg === "docx"; | |
if (!extension && !extension && !extension3) { | |
this.$message({ | |
message: "上传的拓扑图文件只能是word、pdf格式,请重新上传!", | |
type: "error", | |
duration: | |
}); | |
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 ==) { | |
this.tuopuForm = res.data.data; | |
//拓扑预览 | |
this.tuopuPreview(); | |
} else { | |
console.log("上传失败"); | |
this.$message({ | |
message: "上传拓扑图文件失败,请稍后重试!", | |
type: "error", | |
duration: | |
}); | |
} | |
}) | |
.catch(err => { | |
console.log("上传失败", err); | |
this.$message({ | |
message: "上传拓扑图文件失败,请稍后重试!", | |
type: "error", | |
duration: | |
}); | |
}); | |
}, | |
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 ==) { | |
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:.5rem; | |
.headerTitle { | |
font-size:rem; | |
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( | |
"拓扑图文件为空不能预览,请先上传拓扑图文件后再预览", | |
); | |
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-", // 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 声明 | |
useBaseURL: 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="%" 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-", // 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 声明 | |
useBaseURL: false, // boolean:如果为true,图片、字体等会转为base 64 URL,否则使用URL.createObjectURL | |
useMathMLPolyfill: false, // boolean:包括用于 chrome、edge 等的 MathML polyfill。 | |
showChanges: false, // boolean:启用文档更改的实验性渲染(插入/删除) | |
debug: false // boolean:启用额外的日志记录 | |
}, | |
num:, | |
numPages:, | |
pdfUrl: "" | |
}; | |
}, | |
created() { | |
console.log("接收", this.$route.query); | |
this.resetTitle(); | |
this.getFile(); | |
}, | |
methods: { | |
goBack() { | |
this.$router.go(-); | |
}, | |
//获取文件流 | |
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 ==) { | |
this.previewData = res.data; | |
if ( | |
this.$route.query.fileName && | |
this.$route.query.fileName.indexOf(".docx") && | |
this.$route.query.fileName.indexOf(".pdf") == - | |
) { | |
//word--docx格式 | |
this.wordPreview(res.data); | |
} else if ( | |
this.$route.query.fileName && | |
this.$route.query.fileName.indexOf(".doc") && | |
this.$route.query.fileName.indexOf(".pdf") == - | |
) { | |
//word--doc格式 | |
this.wordPreview(res.data); | |
} else if ( | |
this.$route.query.fileName && | |
this.$route.query.fileName.indexOf(".pdf") | |
) { | |
this.pdfPreview(res.data); | |
} | |
} | |
}) | |
.catch(err => { | |
console.log("文件流获取失败", err); | |
this.$message.error("获取文件信息失败,请稍后重试",); | |
}); | |
}, | |
//重置标题 | |
resetTitle() { | |
let fileName = this.$route.query.fileName; | |
console.log("fileName", fileName); | |
if ( | |
fileName && | |
fileName.indexOf(".docx") && | |
fileName.indexOf(".pdf") == - | |
) { | |
//word--docx格式 | |
let wordDocxArr = fileName.split(".docx"); | |
console.log("wordDocxArr", wordDocxArr); | |
this.myTitle = wordDocxArr[]; | |
} else if ( | |
fileName && | |
fileName.indexOf(".doc") && | |
fileName.indexOf(".pdf") == - | |
) { | |
//word--doc格式 | |
let wordDocArr = fileName.split(".docx"); | |
console.log("wordDocArr", wordDocArr); | |
this.myTitle = wordDocArr[]; | |
} else if (fileName && fileName.indexOf(".pdf")) { | |
let pdfArr = fileName.split(".pdf"); | |
console.log("pdfArr", pdfArr); | |
this.myTitle = pdfArr[]; | |
} | |
}, | |
// word文档预览 | |
wordPreview(buffer) { | |
console.log("文档buffer", buffer); | |
let bodyContainer = document.getElementById("bodyContainer"); | |
renderAsync( | |
buffer, // Blob | ArrayBuffer | UintArray, 可以是 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:rem; | |
img { | |
cursor: pointer; | |
} | |
} | |
.titleBox { | |
text-align: center; | |
background: #fff; | |
color: #; | |
position: absolute; | |
top:; | |
left:%; | |
width: auto; | |
} | |
} | |
} | |
} | |
</style> |