VUE2
1 先安装 npm i ofd.js
<template>
<div>
<div>
<div><el-button @click="downClick">下载按钮</el-button></div>
<div><el-button @click="getOfdManager">接口按钮</el-button></div>
<div>打开OFD</div>
<input
type="file"
ref="file"
class="hidden"
accept=".ofd"
@change="fileChanged"
/>
</div>
<div id="content">
<div class="seal_img_div"></div>
</div>
</div>
</template>
<script>
import { parseOfdDocument, renderOfd } from "ofd.js";
import { getZzxxByYWH } from "@/api/zq/index";
export default {
name: "HelloWorld",
data() {
return {
file: null,
ofdBase64: null,
};
},
created() {},
mounted() {},
methods: {
fileChanged() {
this.file = this.$refs.file.files[0];
console.log(this.file);
let ext = this.file.name.replace(/.+\./, "");
if (["ofd"].indexOf(ext) === -1) {
this.$alert("error", "仅支持ofd类型", {
confirmButtonText: "确定",
callback: (action) => {
this.$message({
type: "info",
message: `action: ${action}`,
});
},
});
return;
}
if (this.file.size > 100 * 1024 * 1024) {
this.$alert("error", "文件大小需 < 100M", {
confirmButtonText: "确定",
callback: (action) => {
this.$message({
type: "info",
message: `action: ${action}`,
});
},
});
return;
}
let that = this;
//转换器
let reader = new FileReader();
reader.readAsDataURL(this.file);
reader.onload = function (e) {
that.ofdBase64 = e.target.result.split(",")[1];
};
this.handleChange(this.file);
this.$refs.file.value = null;
},
handleChange(file) {
let that = this;
parseOfdDocument({
ofd: file,
success(res) {
//输出ofd每页的div
let screenWidth = 1000;
const divs = renderOfd(screenWidth, res[0]);
let contentDiv = document.getElementById("content");
contentDiv.innerHTML = "";
for (const div of divs) {
contentDiv.appendChild(div);
}
for (let ele of document.getElementsByName("seal_img_div")) {
this.addEventOnSealDiv(
ele,
JSON.parse(ele.dataset.sesSignature),
JSON.parse(ele.dataset.signedInfo)
);
}
},
fail(error) {
console.log(error);
},
});
},
getOfdManager() {
let postdata = {
slbh: "",
};
getZzxxByYWH(postdata)
.then((response) => {
//读取接口数据
if (response.code == "1") {
this.ofdBase64 = response.data.base64;
var binary_string = window.atob(this.ofdBase64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
let ofdfile = bytes.buffer;
console.log(ofdfile);
this.handleChange(ofdfile);
this.$refs.file.value = null;
} else {
this.$message.error(response.message);
}
})
.catch((err) => {
this.$message.error(err);
return;
});
},
downClick(){
//let imgData = "data:image/jpg;base64," + ret;
//data是带有"data:image/jpg;base64,"的内容的值
//window.location.href = process.env.VUE_APP_BASE_API + '/profile/pdfTempPath/' + this.businessId + 'N.ofd'
this.downloadFile("123.ofd", this.ofdBase64);
},
downloadFile(fileName, content) {
let aLink = document.createElement('a');
let blob = this.base64ToBlob(content); //new Blob([content]);
let evt = document.createEvent("HTMLEvents");
evt.initEvent("click", true, true);//initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
aLink.download = fileName;
aLink.href = URL.createObjectURL(blob);
// aLink.dispatchEvent(evt);
aLink.click()
},
base64ToBlob(code) {
// let parts = code.split(';base64,');
// let contentType = parts[0].split(':')[1];
let contentType=code;
let raw = window.atob(code);
let rawLength = raw.length;
let uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType});
},
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>
VUE3
需求接口是base64的OFD文件 前端展示 啊....弄了一天多 才调出来 下面记录一下
1 我分别用了 ofdview, cafe-ofd, ofd.js 各种插件 都去官网下了demo 只有ofd.js 好使,其他的没研究明白 一直报错
我一开始用的vue2 但是ofd.js一直无法引用进去 从网上也查不到什么好的方法 后来换成vue3 就好了
这两个文件里面的js 是要导入的 vue2 就直接报错了
2 接口是base64的 file是无法直接读取的 然后我就开始base64、 blob 、file 、arraybuffer 直接无限循环转换 浪费了好多时间
先转了 blob 不行
let base64EncodedData = res.data;(返回的base64)
const byteCharacters = atob(base64EncodedData);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const fileBlob = new Blob([byteArray], { type: 'application/octet-stream' });
console.log(fileBlob)
然后是二进制也不行
// 将base64编码解码成二进制数据
const byteCharacters = atob(res.data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const uInt8Array = new Uint8Array(byteNumbers);
console.log(byteArray)
然后是file 也不行!!! 但是本地直接读取的是file 放在接口里面 就不行了 这块就以为是转错了 对照了半天 发现确实是不行
/const byteCharacters = atob(base64EncodedData);
const byteNumbers = new Array(byteCharacters.length);
const byteArray = new Uint8Array(byteNumbers);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const fileBlob = new File([byteArray], "ofd.ofd",{ type: 'application/ofd' });
console.log(fileBlob)
最后接口里面支持的格式是ArrayBuffer 这样可以!!!!
var binary_string = window.atob(base64EncodedData);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
let file= bytes.buffer;
console.log(file)
直接从官网下demo: https://github.com/DLTech21/ofd.js
参考文章:https://blog.csdn.net/zhangyoude123/article/details/127998841?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-4-127998841-blog-127545928.235%5Ev32%5Epc_relevant_increate_t0_download_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-4-127998841-blog-127545928.235%5Ev32%5Epc_relevant_increate_t0_download_v2&utm_relevant_index=7
最终代码:
<template>
<el-container style="width:100vw; height: 100vh;">
<el-header style="background:#F5F5F5;display: flex; height: 40px; border: 1px solid #e8e8e8; align-items: center;">
<div class="upload-icon">
<form><input type="text" size="30" /></form>
</div>
<div class="upload-icon" @click="getManager">
<div class="upload-icon">查询</div>
<font-awesome-icon icon="search"/>
</div>
<div class="upload-icon" @click="uploadFile">
<div class="upload-icon">打开本地OFD</div>
<font-awesome-icon icon="cloud-upload-alt"/>
<input type="file" ref="file" class="hidden" accept=".ofd"
@change="fileChanged">
</div>
<div style="display: flex;align-items: center" v-if="ofdObj">
<div class="scale-icon" style="margin-left: 10px" @click="plus">
<font-awesome-icon icon="search-plus"/>
</div>
<div class="scale-icon" @click="minus">
<font-awesome-icon icon="search-minus" />
</div>
<div class="scale-icon">
<font-awesome-icon icon="step-backward" @click="firstPage"/>
</div>
<div class="scale-icon" style="font-size: 18px" @click="prePage">
<font-awesome-icon icon="caret-left"/>
</div>
<div class="scale-icon">
{{pageIndex}}/{{pageCount}}
</div>
<div class="scale-icon" style="font-size: 18px" @click="nextPage">
<font-awesome-icon icon="caret-right"/>
</div>
<div class="scale-icon" @click="lastPage">
<font-awesome-icon icon="step-forward"/>
</div>
</div>
</el-header>
<el-main style="height: auto;background: #808080;;padding: 0" v-loading="loading">
<div class="main-section"
id="content" ref="contentDiv" @mousewheel="scrool">
</div>
</el-main>
</el-container>
</template>
<script>
import {parseOfdDocument, renderOfd, renderOfdByScale, digestCheck, getPageScale, setPageScale} from "@/utils/ofd/ofd";
export default {
name: 'HelloWorld',
data() {
return {
ofdBase64: null,
pageIndex: 1,
pageCount: 0,
scale: 0,
title: null,
value: null,
ofdObj: null,
screenWidth: document.body.clientWidth,
loading:false,
}
},
created() {
this.getManager();
},
mounted() {
let that = this;
this.$refs.contentDiv.addEventListener('scroll', this.scrool);
window.onresize = () => {
return (() => {
that.screenWidth = (document.body.clientWidth - 88);
const divs = renderOfd(that.screenWidth, that.ofdObj);
that.displayOfdDiv(divs);
})()
}
},
methods: {
scrool() {
let scrolled = this.$refs.contentDiv.firstElementChild?.getBoundingClientRect()?.top - 60;
let top = 0
let index = 0;
for (let i=0;i<this.$refs.contentDiv.childElementCount; i ++) {
top += (Math.abs(this.$refs.contentDiv.children.item(i)?.style.height.replace('px','')) + Math.abs(this.$refs.contentDiv.children.item(i)?.style.marginBottom.replace('px','')));
if (Math.abs(scrolled) < top) {
index = i;
break;
}
}
this.pageIndex = index+1;
},
//变大
plus() {
setPageScale(++this.scale);
const divs = renderOfdByScale(this.ofdObj);
this.displayOfdDiv(divs);
},
//变小
minus() {
setPageScale(--this.scale);
const divs = renderOfdByScale(this.ofdObj);
this.displayOfdDiv(divs);
},
//上一页
prePage() {
let contentDiv = document.getElementById('content');
let ele = contentDiv.children.item(this.pageIndex-2);
ele?.scrollIntoView(true);
ele?this.pageIndex=this.pageIndex-1:'';
},
//第一页
firstPage() {
let contentDiv = document.getElementById('content');
let ele = contentDiv.firstElementChild;
ele?.scrollIntoView(true);
ele?this.pageIndex=1:'';
},
//下一页
nextPage() {
let contentDiv = document.getElementById('content');
let ele = contentDiv.children.item(this.pageIndex);
ele?.scrollIntoView(true);
ele?++this.pageIndex:'';
},
//最后一页
lastPage() {
let contentDiv = document.getElementById('content');
let ele = contentDiv.lastElementChild;
ele?.scrollIntoView(true);
ele?this.pageIndex=contentDiv.childElementCount:'';
},
//打开ofd
uploadFile() {
this.file = null;
this.$refs.file.click();
},
fileChanged() {
this.file = this.$refs.file.files[0];
let ext = this.file.name.replace(/.+\./, "");
if (["ofd"].indexOf(ext) === -1) {
this.$alert('error', '仅支持ofd类型', {
confirmButtonText: '确定',
callback: action => {
this.$message({
type: 'info',
message: `action: ${ action }`
});
}
});
return;
}
if (this.file.size > 100 * 1024 * 1024) {
this.$alert('error', '文件大小需 < 100M', {
confirmButtonText: '确定',
callback: action => {
this.$message({
type: 'info',
message: `action: ${ action }`
});
}
});
return;
}
let that = this;
let reader = new FileReader();
reader.readAsDataURL(this.file);
reader.onload = function (e) {
that.ofdBase64 = e.target.result.split(',')[1];
}
this.getOfdDocumentObj(this.file, this.screenWidth);
this.$refs.file.value = null;
},
//加载ofd格式数据
getOfdDocumentObj(file, screenWidth) {
let that = this;
let t = new Date().getTime();
parseOfdDocument({
ofd: file,
success(res) {
let t1 = new Date().getTime();
//console.log('解析ofd',t1 - t);
that.ofdObj = res[0];
that.pageCount = res[0].pages.length;
const divs = renderOfd(screenWidth, res[0]);
let t2 = new Date().getTime();
//console.log('xml转svg', t2 - t1)
that.displayOfdDiv(divs);
let t3 = new Date().getTime();
//console.log('svg渲染到页面', t3 - t2);
},
fail(error) {
console.log('OFD打开失败', error)
that.$alert('OFD打开失败', error, {
confirmButtonText: '确定',
callback: action => {
this.$message({
type: 'info',
message: `action: ${ action }`
});
}
});
}
});
},
//辅助功能
displayOfdDiv(divs) {
this.scale = getPageScale();
let contentDiv = document.getElementById('content');
contentDiv.innerHTML = '';
for (const div of divs) {
contentDiv.appendChild(div)
}
},
//读取接口数据
getManager(){
this.loading=true;
this.$axios.requests
.get("/api/map/queryBuilding/ofd")
.then((res) => {
//读取接口数据
//console.log(res.data)
let base64EncodedData = res.data;
var binary_string = window.atob(base64EncodedData);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
this.file= bytes.buffer;
this.getOfdDocumentObj(this.file, this.screenWidth);
this.$refs.file.value = null;
this.loading=false;
})
.catch((err) => {
this.$toast.fail("系统错误:" + err);
this.loading=false;
});
},
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.upload-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
height: 28px;
padding-left: 10px;
padding-right: 10px;
background-color: rgb(59, 95, 232);
border-radius: 1px;
border-color: #5867dd;
font-weight: 500;
font-size: 12px;
color: white;
margin: 1px;
}
.scale-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
width: 33px;
height: 28px;
background-color: #F5F5F5;;
border-radius: 1px;
font-weight: 500;
font-size: 12px;
color: #333333;
text-align: center;
padding: 2px;
}
.scale-icon :active {
color: rgb(59, 95, 232);
}
.scale-icon :hover {
color: rgb(59, 95, 232);
}
.text-icon {
display: flex;
cursor: pointer;
justify-content: center;
align-items: center;
height: 28px;
width: 90%;
background-color: rgb(59, 95, 232);
border-radius: 1px;
border-color: #5867dd;
font-weight: 500;
font-size: 10px;
color: white;
margin-top: 20px;
}
.hidden {
display: none !important;
}
.SealContainer {
z-index: 99999;
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
}
.SealContainer .mask {
background: #000000;
opacity: 0.3;
}
.content-title {
font-size: 16px;
text-align: center;
border-bottom: 1px solid rgb(59, 95, 232);
color: rgb(59, 95, 232);
margin-top: 10px;
}
.SealContainer-content {
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
background: white;
display: flex;
flex-direction: column;
padding: 10px;
align-items: center;
}
.SealContainer-layout {
position: relative;
width: 60%;
height: 80vh;
overflow-y: auto;
background: white;
z-index: 100;
display: flex;
flex-direction: column;
padding: 10px;
align-items: center;
}
.subcontent {
width: 80%;
display: flex;
flex-direction: column;
text-align: left;
margin-bottom: 10px;
font-family: simsun;
}
.subcontent .title {
font-weight: 600;
}
.subcontent .value {
font-weight: 400;
-webkit-line-clamp: 1;
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
}
.left-section {
position: fixed;
width: 88px;
height: 100%;
background:#F5F5F5;
border: 1px solid #e8e8e8;
align-items: center;
display: flex;
flex-direction: column
}
.main-section {
padding-top: 20px;
margin-left:88px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #808080;
overflow: hidden
}
@media (max-width: 767px) {
.SealContainer-layout {
position: relative;
width: 90%;
height: 90vh;
overflow-y: auto;
background: white;
z-index: 100;
display: flex;
flex-direction: column;
padding: 10px;
align-items: center;
}
.subcontent {
width: 95%;
display: flex;
flex-direction: column;
text-align: left;
margin-bottom: 10px;
font-family: simsun;
}
.left-section {
position: fixed;
width: 0px;
height: 100%;
background:#F5F5F5;
border: 1px solid #e8e8e8;
align-items: center;
display: none;
flex-direction: column;
}
.main-section {
padding-top: 20px;
margin-left:0px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #808080;
overflow: hidden
}
}
</style>