OFD 前端展示

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 就好了


图片.png

这两个文件里面的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

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>


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容