ofd文件
ofd文件处理预览
文章内容所涉及的源码地址,求朋友们给个star啊
::: tip
个人网站 ([https://nexuslin.github.io/](https://nexuslin.github.io/))
文章内容所涉及的源码地址,求朋友们给个star啊
同路之人,幸得君顾,盼得君之一赞!
与君同行,愿得青眼相加!
你的star
如春风化雨,润物无声;
如山间清泉,滋润心田;
如长河落日,映照初心;
亦如暗夜明灯,照亮前路;
是吾辈前行之明灯,亦是我坚持的动力!
愿君前程似锦,代码如诗,人生如画!
【GIthub地址】([https://github.com/lintaibai/TG](https://gitee.com/lintaibai/TG))
【Gitee地址】([https://gitee.com/lintaibai/TG](https://gitee.com/lintaibai/TG))
:::
OFD文件是什么
本质上就是一种国产的压缩文件
2016年成为国家标准(GB/T 33190-2016)
// 国家标准网站
https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=3AF6682D939116B6F5EED53D01A9DB5D
OFD(Open Fixed-layout Document)开放式版式文件
中国自主研发的一种电子文档格式
中国国家标准版的电子文件格式,类似于 PDF
具有以下特点:
- 基于XML和ZIP技术
- 支持数字签名
- 支持版式固定
- 支持国产密码算法
- 支持长期保存格式
OFD 文件结构
OFD 文件本质上是一个 ZIP 压缩包,包含以下结构
document.ofd
├── OFD.xml // 文档根文件
├── Doc_0/
│ ├── Doc_0/ // 文档目录
│ │ ├── Document.xml
│ │ └── Pages/
│ │ └── Page_0/
│ │ ├── Page.xml
│ │ └── Res/ // 资源目录
│ │ ├── image/
│ │ └── font/
└── Public.xml // 公共资源
相关库的认识
想要解决ofd文件的预览,我们必须认识相关的两个依赖库
jszip
JSZipUtils
这两个库存的作用和认识
jszip的认识使用
认识
JSZip 是一个用于创建、读取和编辑 .zip 文件的JavaScript库。它可以在浏览器和Node.js环境中使用。
主要功能:
- 创建新的 ZIP 文件
- 读取现有的 ZIP 文件
- 向 ZIP 文件中添加或删除文件
- 生成 ZIP 文件并下载
使用示例
// 创建一个新的 ZIP 文件
var zip = new JSZip();
// 添加一个文本文件
zip.file("hello.txt", "Hello World\n");
// 添加一个文件夹和其中的文件
zip.folder("images").file("smile.gif", "base64数据", {base64: true});
// 生成 ZIP 文件
zip.generateAsync({type:"blob"})
.then(function(content) {
// 在浏览器中下载
saveAs(content, "example.zip");
});
常见应用场景:
- 在前端打包多个文件供用户下载
- 动态生成包含多个文件的压缩包
- 处理上传的 ZIP 文件内容
JSZipUtils 的认识使用
认识
JSZipUtils 是 JSZip 的一个辅助工具库,主要用于处理二进制数据,特别是在获取远程文件时非常有用。
主要功能:
- 获取二进制数据
- 处理跨域请求
- 提供便捷的文件获取方法
基本使用示例:
// 获取远程二进制数据
JSZipUtils.getBinaryContent("path/to/file.zip", function(err, data) {
if(err) {
throw err;
}
// 使用获取到的数据创建 JSZip 对象
JSZip.loadAsync(data)
.then(function(zip) {
// 处理 zip 文件内容
return zip.file("hello.txt").async("string");
})
.then(function(text) {
console.log(text);
});
});
常见应用场景:
- 下载并处理远程 ZIP 文件
- 获取二进制文件内容
- 处理跨域的二进制数据请求
为什么 JSZipUtils 被废弃
JSZipUtils 被废弃的主要原因有:
- 现代浏览器原生支持更好:fetch API 已经成为现代浏览器的标准,提供了更强大和灵活的功能
- 维护问题:JSZipUtils 已经很久没有更新,可能存在安全漏洞
- 功能冗余:fetch API 可以完全覆盖 JSZipUtils 的功能,而且提供更多特性
使用 fetch API 替代的示例
原来的 JSZipUtils 写法:
JSZipUtils.getBinaryContent("path/to/file.zip", function(err, data) {
if(err) {
throw err;
}
JSZip.loadAsync(data)
.then(function(zip) {
// 处理 zip 文件
});
});
使用 fetch API 的现代写法:
fetch("path/to/file.zip")
.then(response => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.arrayBuffer();
})
.then(data => {
return JSZip.loadAsync(data);
})
.then(zip => {
// 处理 zip 文件
})
.catch(error => {
console.error("Error:", error);
});
fetch API 的优势
- 更好的错误处理:
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.arrayBuffer();
})
- 支持更多数据类型:
// 获取不同类型的数据
fetch(url)
.then(response => response.arrayBuffer()) // 二进制数据
.then(response => response.blob()) // Blob 对象
.then(response => response.text()) // 文本数据
.then(response => response.json()) // JSON 数据
- 支持请求配置:
fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Bearer token',
'Content-Type': 'application/octet-stream'
},
mode: 'cors',
credentials: 'include'
})
- 支持异步/等待语法:
async function loadZip(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.arrayBuffer();
const zip = await JSZip.loadAsync(data);
return zip;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
完整的迁移示例
假设我们要下载一个 ZIP 文件,解压后获取其中的文件内容:
// 使用 async/await 的现代写法
async function processZipFile(url) {
try {
// 1. 获取文件
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 2. 转换为二进制数据
const arrayBuffer = await response.arrayBuffer();
// 3. 加载到 JSZip
const zip = await JSZip.loadAsync(arrayBuffer);
// 4. 处理文件内容
const files = Object.keys(zip.files);
for (const filename of files) {
if (!zip.files[filename].dir) {
const content = await zip.file(filename).async('string');
console.log(`${filename}: ${content}`);
}
}
return zip;
} catch (error) {
console.error('Error processing zip file:', error);
throw error;
}
}
// 使用示例
processZipFile('path/to/file.zip')
.then(zip => {
console.log('Zip file processed successfully');
})
.catch(error => {
console.error('Failed to process zip file:', error);
});
兼容性考虑
如果需要支持较老的浏览器,可以添加 polyfill:
// 添加 fetch polyfill(如果需要)
import 'whatwg-fetch';
// 或者使用条件加载
if (!window.fetch) {
// 加载 fetch polyfill
}
总结
迁移到 fetch API 的好处:
- 更现代的 API 设计
- 更好的错误处理机制
- 更灵活的数据处理能力
- 更好的性能和浏览器支持
- 更少的依赖,减少包大小
- 更好的 TypeScript 支持
建议在新的项目中直接使用 fetch API,在现有项目中逐步将 JSZipUtils 的调用替换为 fetch API 的实现。
JSZipUtils和jszip的配合使用
通常在实际应用中,这两个库会配合使用:
// 下载远程文件并创建新的 ZIP 包
JSZipUtils.getBinaryContent("path/to/image.jpg", function(err, data) {
if(err) throw err;
var zip = new JSZip();
zip.file("image.jpg", data, {binary: true});
zip.generateAsync({type:"blob"})
.then(function(content) {
saveAs(content, "new.zip");
});
});
注意事项
- 在浏览器中使用时,如果处理大文件,要注意内存使用情况
- 处理远程文件时要注意跨域问题
- JSZipUtils 已经被标记为废弃,推荐使用原生的 fetch API 替代
- 现代项目中,也可以考虑使用更新的替代方案,如 JSZip 的最新版本配合 fetch API
这两个库在前端处理文件压缩和解压缩任务时非常实用,特别是在需要动态处理文件内容的场景中。
vue3之中预览ofd.js文件
接下来我们就简单实现一下ofd.js文件的预览,我们的想法是在vue3之中替代掉老旧的JSZipUtils库,当然,和之前一样我们还是以实现功能然后进行优化为主
因为ofd文件的本质上是一种压缩包,所以我们需要先解压 OFD 文件,然后解析其中的内容。
先来看看标准的ofd文件是什么样子的
{
"name": "OFD.xml",
"dir": false,
"date": "2020-08-22T16:21:20.000Z",
"comment": null,
"unixPermissions": null,
"dosPermissions": 32,
"_data": {
"compressedSize": 446,
"uncompressedSize": 1269,
"crc32": -2125441896,
"compression": {
"magic": "\b\u0000"
},
"compressedContent": {
"0": 157,
"1": 84,
"2": 209,
"3": 110,
xxx
}
},
"_dataBinary": true,
"options": {
"compression": null,
"compressionOptions": null
},
"unsafeOriginalName": "OFD.xml"
}
方式1-使用easyofd的方式预览
推荐的预览方式
安装相关依赖
pnpm i jszip x2js jb2 opentype.js easyofd
预览文件
<script setup>
import EasyOFD from "easyofd";
import { onMounted } from 'vue'
onMounted(() => {
let yourElement=document.getElementById("1111111");
let ofd=new EasyOFD('myofdID', yourElement);
})
</script>
<template>
<div id="1111111"> </div>
</template>
<style >
.OfdButton{
padding: 10px 20px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
margin-right: 10px;
}
</style>
方式2-使用ofd的方式预览
相关源码地址
https://github.com/DLTech21/ofd.js
安装依赖
这里我们先安装必须的依赖,后面剔除
pnpm install jszip
pnpm install jszip-utils(后面剔除)
pnpm i @lapo/asn1js
pnpm i js-sha1
pnpm i ofd-xml-parser js-md5 jsrsasign jsrsasign-util sm-crypto
安装成功以后我们首要的就是将ofd之前的版本兼容到vue3
这里需要我们main.TS之中设置一下全局
// 设置全局变量
if (typeof window !== 'undefined') {
window.global = window;
}
文件之中使用
<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" @click="uploadFile">
<div class="upload-icon">打开OFD</div>
<font-awesome-icon icon="cloud-upload-alt"/>
<input type="file" ref="fileRef" class="hidden" accept=".ofd" @change="fileChanged">
</div>
<div class="upload-icon" @click="uploadPdfFile">
<div class="upload-icon">PDF2OFD</div>
<font-awesome-icon icon="cloud-upload-alt"/>
<input type="file" ref="pdfFileRef" class="hidden" accept=".pdf" @change="pdfFileChanged">
</div>
<div style="display: flex;align-items: center" v-if="ofdObj">
<div class="upload-icon" style="margin-left: 10px" @click="downPdf" v-if="ofdBase64">
下载PDF
<font-awesome-icon icon="download"/>
</div>
<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 id="leftMenu" class="left-section">
<div class="text-icon" @click="demo(1)">
<p>电子发票</p>
</div>
<div class="text-icon" @click="demo(2)">
<p>电子公文</p>
</div>
<div class="text-icon" @click="demo(3)">
<p>骑缝章</p>
</div>
<div class="text-icon" @click="demo(4)">
<p>多页文档</p>
</div>
</div>
<div class="main-section" id="content" ref="contentDivRef" @mousewheel="scrool"></div>
</el-main>
<div class="SealContainer" id="sealInfoDiv" hidden="hidden" ref="sealInfoDivRef">
<div class="SealContainer mask" @click="closeSealInfoDialog"></div>
<div class="SealContainer-layout">
<div class="SealContainer-content">
<p class="content-title">签章信息</p>
<div class="subcontent">
<span class="title">签章人</span>
<span class="value" id="spSigner">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">签章提供者</span>
<span class="value" id="spProvider">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">原文摘要值</span>
<span class="value" id="spHashedValue" @click="showMore('原文摘要值', 'spHashedValue')" style="cursor: pointer">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">签名值</span>
<span class="value" id="spSignedValue" @click="showMore('签名值', 'spSignedValue')" style="cursor: pointer">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">签名算法</span>
<span class="value" id="spSignMethod">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">版本号</span>
<span class="value" id="spVersion">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">验签结果</span>
<span class="value" id="VerifyRet">[无效的签章结构]</span>
</div>
<p class="content-title">印章信息</p>
<div class="subcontent">
<span class="title">印章标识</span>
<span class="value" id="spSealID">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">印章名称</span>
<span class="value" id="spSealName">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">印章类型</span>
<span class="value" id="spSealType">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">有效时间</span>
<span class="value" id="spSealAuthTime">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">制章日期</span>
<span class="value" id="spSealMakeTime">[无效的签章结构]</span>
</div>
<div class="subcontent">
<span class="title">印章版本</span>
<span class="value" id="spSealVersion">[无效的签章结构]</span>
</div>
</div>
<input style="position:absolute;right:1%;top:1%;" type="button" name="" id="" value="X" @click="closeSealInfoDialog()"/>
</div>
</div>
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible">
<span style="text-align: left">{{dialogValue}}</span>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
</div>
</el-dialog>
</el-container>
</template>
<script setup>
import { ref, onMounted, reactive } from 'vue'
import { parseOfdDocument, renderOfd, renderOfdByScale, digestCheck, getPageScale, setPageScale } from "@/utils/ofd/ofd.js"
import JSZipUtils from "jszip-utils"
import { ElMessage, ElMessageBox } from 'element-plus'
// 响应式数据
const fileRef = ref(null)
const pdfFileRef = ref(null)
const contentDivRef = ref(null)
const sealInfoDivRef = ref(null)
const state = reactive({
pdfFile: null,
ofdBase64: null,
loading: false,
pageIndex: 1,
pageCount: 0,
scale: 0,
dialogTitle: null,
dialogValue: null,
dialogVisible: false,
ofdObj: null,
screenWidth: document.body.clientWidth
})
// 方法
const uploadFile = () => {
state.pdfFile = null
fileRef.value.click()
}
const fileChanged = (e) => {
const file = e.target.files[0]
const ext = file.name.replace(/.+\./, "")
if (["ofd"].indexOf(ext) === -1) {
ElMessageBox.alert('仅支持ofd类型', 'error', {
confirmButtonText: '确定',
callback: action => {
ElMessage({
type: 'info',
message: `action: ${action}`
})
}
})
return
}
if (file.size > 100 * 1024 * 1024) {
ElMessageBox.alert('文件大小需 < 100M', 'error', {
confirmButtonText: '确定',
callback: action => {
ElMessage({
type: 'info',
message: `action: ${action}`
})
}
})
return
}
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = (e) => {
state.ofdBase64 = e.target.result.split(',')[1]
}
getOfdDocumentObj(file, state.screenWidth)
fileRef.value.value = null
}
const uploadPdfFile = () => {
state.pdfFile = null
pdfFileRef.value.click()
}
const pdfFileChanged = (e) => {
const file = e.target.files[0]
const ext = file.name.replace(/.+\./, "")
if (["pdf"].indexOf(ext) === -1) {
ElMessageBox.alert('仅支持pdf类型', 'error', {
confirmButtonText: '确定',
callback: action => {
ElMessage({
type: 'info',
message: `action: ${action}`
})
}
})
return
}
if (file.size > 100 * 1024 * 1024) {
ElMessageBox.alert('文件大小需 < 100M', 'error', {
confirmButtonText: '确定',
callback: action => {
ElMessage({
type: 'info',
message: `action: ${action}`
})
}
})
return
}
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = (e) => {
const pdfBase64 = e.target.result.split(',')[1]
downOfd(pdfBase64)
}
pdfFileRef.value.value = null
}
const downOfd = (pdfBase64) => {
state.loading = true
axios({
method: "post",
url: "https://51shouzu.xyz/api/ofd/convertOfd",
data: {
pdfBase64,
}
}).then(response => {
state.loading = false
const binary = atob(response.data.data.replace(/\s/g, ''))
const len = binary.length
const buffer = new ArrayBuffer(len)
const view = new Uint8Array(buffer)
for (let i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i)
}
const blob = new Blob([view], null)
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', 'ofd.ofd')
document.body.appendChild(link)
link.click()
}).catch(error => {
console.log(error, "error")
ElMessageBox.alert('PDF打开失败', error, {
confirmButtonText: '确定',
callback: action => {
ElMessage({
type: 'info',
message: `action: ${action}`
})
}
})
})
}
const downPdf = () => {
state.loading = true
axios({
method: "post",
url: "https://51shouzu.xyz/api/ofd/convertPdf",
data: {
ofdBase64: state.ofdBase64
}
}).then(response => {
state.loading = false
const binary = atob(response.data.data.replace(/\s/g, ''))
const len = binary.length
const buffer = new ArrayBuffer(len)
const view = new Uint8Array(buffer)
for (let i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i)
}
const blob = new Blob([view], { type: "application/pdf" })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', 'ofd.pdf')
document.body.appendChild(link)
link.click()
}).catch(error => {
console.log(error, "error")
ElMessageBox.alert('OFD打开失败', error, {
confirmButtonText: '确定',
callback: action => {
ElMessage({
type: 'info',
message: `action: ${action}`
})
}
})
})
}
const plus = () => {
setPageScale(++state.scale)
const divs = renderOfdByScale(state.ofdObj)
displayOfdDiv(divs)
}
const minus = () => {
setPageScale(--state.scale)
const divs = renderOfdByScale(state.ofdObj)
displayOfdDiv(divs)
}
const prePage = () => {
const contentDiv = document.getElementById('content')
const ele = contentDiv.children.item(state.pageIndex - 2)
ele?.scrollIntoView(true)
if (ele) state.pageIndex = state.pageIndex - 1
}
const firstPage = () => {
const contentDiv = document.getElementById('content')
const ele = contentDiv.firstElementChild
ele?.scrollIntoView(true)
if (ele) state.pageIndex = 1
}
const nextPage = () => {
const contentDiv = document.getElementById('content')
const ele = contentDiv.children.item(state.pageIndex)
ele?.scrollIntoView(true)
if (ele) ++state.pageIndex
}
const lastPage = () => {
const contentDiv = document.getElementById('content')
const ele = contentDiv.lastElementChild
ele?.scrollIntoView(true)
if (ele) state.pageIndex = contentDiv.childElementCount
}
const demo = (value) => {
let ofdFile = null
switch (value) {
case 1:
ofdFile = '999.ofd'
break
case 2:
ofdFile = 'n.ofd'
break
case 3:
ofdFile = 'h.ofd'
break
case 4:
ofdFile = '2.ofd'
break
}
JSZipUtils.getBinaryContent(ofdFile, (err, data) => {
if (err) {
console.log("JSZipUtils===1");
console.log(err)
} else {
const base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(data)))
console.log("JSZipUtils===2");
state.ofdBase64 = base64String
}
})
getOfdDocumentObj(ofdFile, state.screenWidth)
}
const getOfdDocumentObj = (file, screenWidth) => {
const t = new Date().getTime()
state.loading = true
parseOfdDocument({
ofd: file,
success(res) {
console.log(res)
const t1 = new Date().getTime()
console.log('解析ofd', t1 - t)
state.ofdObj = res[0]
state.pageCount = res[0].pages.length
const divs = renderOfd(screenWidth, res[0])
const t2 = new Date().getTime()
console.log('xml转svg', t2 - t1)
displayOfdDiv(divs)
const t3 = new Date().getTime()
console.log('svg渲染到页面', t3 - t2)
state.loading = false
},
fail(error) {
console.log(error)
state.loading = false
ElMessageBox.alert('OFD打开失败', error, {
confirmButtonText: '确定',
callback: action => {
ElMessage({
type: 'info',
message: `action: ${action}`
})
}
})
}
})
}
const displayOfdDiv = (divs) => {
state.scale = getPageScale()
const contentDiv = document.getElementById('content')
contentDiv.innerHTML = ''
for (const div of divs) {
contentDiv.appendChild(div)
}
for (const ele of document.getElementsByName('seal_img_div')) {
addEventOnSealDiv(ele, JSON.parse(ele.dataset.sesSignature), JSON.parse(ele.dataset.signedInfo))
}
}
const addEventOnSealDiv = (div, SES_Signature, signedInfo) => {
try {
global.HashRet = null
global.VerifyRet = signedInfo.VerifyRet
div.addEventListener("click", () => {
document.getElementById('sealInfoDiv').hidden = false
document.getElementById('sealInfoDiv').setAttribute('style', 'display:flex;align-items: center;justify-content: center;')
if (SES_Signature.realVersion < 4) {
document.getElementById('spSigner').innerText = SES_Signature.toSign.cert['commonName']
document.getElementById('spProvider').innerText = signedInfo.Provider['@_ProviderName']
document.getElementById('spHashedValue').innerText = SES_Signature.toSign.dataHash.replace(/\n/g, '')
document.getElementById('spSignedValue').innerText = SES_Signature.signature.replace(/\n/g, '')
document.getElementById('spSignMethod').innerText = SES_Signature.toSign.signatureAlgorithm.replace(/\n/g, '')
document.getElementById('spSealID').innerText = SES_Signature.toSign.eseal.esealInfo.esID
document.getElementById('spSealName').innerText = SES_Signature.toSign.eseal.esealInfo.property.name
document.getElementById('spSealType').innerText = SES_Signature.toSign.eseal.esealInfo.property.type
document.getElementById('spSealAuthTime').innerText = "从 " + SES_Signature.toSign.eseal.esealInfo.property.validStart + " 到 " + SES_Signature.toSign.eseal.esealInfo.property.validEnd
document.getElementById('spSealMakeTime').innerText = SES_Signature.toSign.eseal.esealInfo.property.createDate
document.getElementById('spSealVersion').innerText = SES_Signature.toSign.eseal.esealInfo.header.version
} else {
document.getElementById('spSigner').innerText = SES_Signature.cert['commonName']
document.getElementById('spProvider').innerText = signedInfo.Provider['@_ProviderName']
document.getElementById('spHashedValue').innerText = SES_Signature.toSign.dataHash.replace(/\n/g, '')
document.getElementById('spSignedValue').innerText = SES_Signature.signature.replace(/\n/g, '')
document.getElementById('spSignMethod').innerText = SES_Signature.signatureAlgID.replace(/\n/g, '')
document.getElementById('spSealID').innerText = SES_Signature.toSign.eseal.esealInfo.esID
document.getElementById('spSealName').innerText = SES_Signature.toSign.eseal.esealInfo.property.name
document.getElementById('spSealType').innerText = SES_Signature.toSign.eseal.esealInfo.property.type
document.getElementById('spSealAuthTime').innerText = "从 " + SES_Signature.toSign.eseal.esealInfo.property.validStart + " 到 " + SES_Signature.toSign.eseal.esealInfo.property.validEnd
document.getElementById('spSealMakeTime').innerText = SES_Signature.toSign.eseal.esealInfo.property.createDate
document.getElementById('spSealVersion').innerText = SES_Signature.toSign.eseal.esealInfo.header.version
}
document.getElementById('spVersion').innerText = SES_Signature.toSign.version
document.getElementById('VerifyRet').innerText = "文件摘要值后台验证中,请稍等... " + (global.VerifyRet ? "签名值验证成功" : "签名值验证失败")
if (global.HashRet == null || global.HashRet == undefined || Object.keys(global.HashRet).length <= 0) {
setTimeout(() => {
const signRetStr = global.VerifyRet ? "签名值验证成功" : "签名值验证失败"
global.HashRet = digestCheck(global.toBeChecked.get(signedInfo.signatureID))
const hashRetStr = global.HashRet ? "文件摘要值验证成功" : "文件摘要值验证失败"
document.getElementById('VerifyRet').innerText = hashRetStr + " " + signRetStr
}, 1000)
}
})
} catch (e) {
console.log(e)
}
if (!global.VerifyRet) {
div.setAttribute('class', 'gray')
}
}
const closeSealInfoDialog = () => {
sealInfoDivRef.value.setAttribute('style', 'display: none')
document.getElementById('spSigner').innerText = "[无效的签章结构]"
document.getElementById('spProvider').innerText = "[无效的签章结构]"
document.getElementById('spHashedValue').innerText = "[无效的签章结构]"
document.getElementById('spSignedValue').innerText = "[无效的签章结构]"
document.getElementById('spSignMethod').innerText = "[无效的签章结构]"
document.getElementById('spSealID').innerText = "[无效的签章结构]"
document.getElementById('spSealName').innerText = "[无效的签章结构]"
document.getElementById('spSealType').innerText = "[无效的签章结构]"
document.getElementById('spSealAuthTime').innerText = "[无效的签章结构]"
document.getElementById('spSealMakeTime').innerText = "[无效的签章结构]"
document.getElementById('spSealVersion').innerText = "[无效的签章结构]"
document.getElementById('spVersion').innerText = "[无效的签章结构]"
document.getElementById('VerifyRet').innerText = "[无效的签章结构]"
}
const showMore = (title, id) => {
state.dialogVisible = true
state.dialogValue = document.getElementById(id).innerText
state.dialogTitle = title
}
const scrool = () => {
const scrolled = contentDivRef.value.firstElementChild?.getBoundingClientRect()?.top - 60
let top = 0
let index = 0
for (let i = 0; i < contentDivRef.value.childElementCount; i++) {
top += (Math.abs(contentDivRef.value.children.item(i)?.style.height.replace('px', '')) + Math.abs(contentDivRef.value.children.item(i)?.style.marginBottom.replace('px', '')))
if (Math.abs(scrolled) < top) {
index = i
break
}
}
state.pageIndex = index + 1
}
// 生命周期钩子
onMounted(() => {
state.screenWidth = document.body.clientWidth - document.getElementById('leftMenu').getBoundingClientRect().width
contentDivRef.value.addEventListener('scroll', scrool)
window.onresize = () => {
state.screenWidth = (document.body.clientWidth - 88)
const divs = renderOfd(state.screenWidth, state.ofdObj)
displayOfdDiv(divs)
}
})
</script>
<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>
方式3-ofd.js文件预览以及使用(废弃)
查看了一下npm上已经需要付费了,所以我果断放弃了
// npm地址
https://www.npmjs.com/package/ofd.js
// 安装
pnpm install ofd.js