这个问题的出现,其实是chrome浏览器安全机制导致,不允许在本地直接运行,它会当作跨域访问。
既然我们是打算做pdf的预览功能,那么我们就可以用这样的一个思路:
1、跨域问题既然是因为浏览器安全机制导致,那么我们就绕过他,使用iframe,来请求加载主应用的文件,主应用项目,预览pdf:
<template>
<div>
<pdf v-for="item in numPages" :key="item" :src="pdfSrc" :page="item" />
</div>
</template>
<script>
import pdf from 'vue-pdf';
import CMapReaderFactory from 'vue-pdf/src/CMapReaderFactory.js';
import {ospdetail} from '@/api/comment.js';
export default {
name: 'ispdf',
components: {
pdf
},
data() {
return {
numPages: 0,
pdfSrc: ''
};
},
created() {
window.addEventListener('message', event => {
console.log('event',event);
if(event.data.type == 'docxPreview'){
this.getTitlePdfurl(event.data.data)
}
});
},
methods: {
getTitlePdfurl(urls) {
this.pdfSrc = pdf.createLoadingTask({ url: urls, CMapReaderFactory }); //解决中文乱码问题
this.pdfSrc.promise.then(pdf => {
this.numPages = pdf.numPages;
});
}
}
};
</script>
那我们要怎么在子应用中找到这个主应用的文件呢,路由呗,在主应用的路由文件中注册它,那么主应用的准备就做好了
{
path: '/preview',
component: resolve => require(['@/views/preview/ispdf.vue'], resolve),
hidden: true
},
2、然后在子组件中就是先办法找到主应用pdf文件,并加以利用
通过DpfSlot来条件判断,当前是在子应用本地(preview ),还是在主应用打开的,
通过乾坤框架的window.POWERED_BY_QIANKUN,来区分,true是主应用,在子应用则是undefined
<preview v-if="DpfSlot" ref="previews"></preview>
<iframe
v-else
:src="`/preview?id=${iframeId}`"
class="preview-iframe"
style="width: 100%;height: 99%;"
ref="previewIframe"
></iframe>
<script>
components: {//子应用本地dpf文件
preview: window.__POWERED_BY_QIANKUN__ ?'':() =>import('./component/ispdf.vue')
},
computed: {
DpfSlot() {
if( window.__POWERED_BY_QIANKUN__) {
return false
}else {
return true
}
}
},
</script>
3、那么关联子应用代码和主应用代码的方式如下:
在子应用中,通过iframe的src去找到主应用注册的文件路由
<iframe
v-else
:src="`/preview?id=${iframeId}`"
class="preview-iframe"
style="width: 100%;height: 99%;"
ref="previewIframe"
></iframe>
此方法为下拉框change方法,用于切换pdf,后面会有截图片段
得到isurl,用iframe的this.$refs.previewIframe.contentWindow.postMessage,将pdf文件流传递出去。
handleSelect(rowid) {
var fileData
this.xzydbg.forEach((item,i)=> {
if(item.id === rowid) {
fileData = item.data
}
})
var isurl = `data:application/pdf;base64,`+fileData
if( window.__POWERED_BY_QIANKUN__ ) {
this.$refs.previewIframe.contentWindow.postMessage(
{
type: "docxPreview",
data: isurl,
},
"*"
);
}else {
this.$refs.previews.getTitlePdfurl(isurl)
}
},
流是发出去了,那主应用是怎么接受的呢,主应用封装中有这样的代码:
通过注册一个message监听便可
created() {
window.addEventListener('message', event => {
console.log('event',event);
if(event.data.type == 'docxPreview'){
this.getTitlePdfurl(event.data.data)
}
});
},
这样就是一个完成的思路了。配个简图:
下面是实际开发时遇到的问题以及解决办法:
在初次打开阅读报告弹窗时,出现了加载顺序问题,本来是把数据传递写在接口内的,可是发现等接口跑完,发送,主应用未能监听到消息,退而求其次,选择用路由的方式把渲染的pdf对象的id给主应用(iframeId ),
子应用打开弹窗
handleOpenAddDialog(row) {//进入阅读报告
this.addDialog.title = '阅读报告';
this.iframeId = row.id;
this.addDialog.visible = true;
//如果是主应用代开,那么就这个接口就只服务于下拉选内容,但是在子应用本地,也依然起到了完整的效果
ospdetail({dataId:row.id}).then(res=> {
if(res.code===200) {
this.xzydbg = res.data
this.addDialog.formData.paramId = this.xzydbg[0].id
var isurl = `data:application/pdf;base64,`+this.xzydbg[0].data
if( !window.__POWERED_BY_QIANKUN__ ) {this.$refs.previews.getTitlePdfurl(isurl)}
}
})
// if( window.__POWERED_BY_QIANKUN__ ) {
// this.$refs.previewIframe.contentWindow.postMessage(
// {
// type: "docxPreview",
// dataId:row.id
// },
// "*"
// );
// }
},
主应用文件里就变成了这样
主应用项目可以兼容所有子应用接口,所以调接口解决初次打开弹窗的预览问题,
后续下拉切换pdf,依然选择用消息监听的方式
created() {
console.log(this.$route.query.id);
ospdetail({dataId:this.$route.query.id}).then(res=> {
if(res.code===200) {
var isurl = `data:application/pdf;base64,`+res.data[0].data
this.getTitlePdfurl(isurl)
}
})
window.addEventListener('message', event => {
console.log('event',event);
if(event.data.type == 'docxPreview'){
this.getTitlePdfurl(event.data.data)
}
});
},
methods: {
getTitlePdfurl(urls) {
this.$set(this,'pdfSrc',pdf.createLoadingTask({ url: urls, CMapReaderFactory }))
// this.pdfSrc = pdf.createLoadingTask({ url: urls, CMapReaderFactory }); //解决中文乱码问题
this.pdfSrc.promise.then(pdf => {
this.numPages = pdf.numPages;
});
}
}