一.知识点
(1).我们可以通过postMessage(发送方)和onmessage(接收方)这两个HTML5的方法, 来解决跨页面通信问题,或者通过iframe嵌套的不同页面之间的通信
a.父页面代码如下
<div v-if="src" class="iframe"><iframeref="iframe"id="iframe"width="100%"height="600"loading="lazy":src="src"frameborder="0"scrolling="no"marginheight="0"marginwidth="0"@load="loaded"></iframe>
</div>
a1.父页面向子页面发送信息(两种方法)
第一种
const iframe = document.getElementId('iframe')//id
//第一个参数是发送的消息,无格式要求;第二个参数是域名限制,当不限制域名时填写*
// 后面的 * 号就是处理跨域问题的,任何域名都不会出现跨域问题
// 传递的参数可以是数组,对象,字符串等
iframe.contentWindow.postMessage('你需要传的数据', "*"); //数据比如({},'*'),('123','*')
// 也可以指定传送域名地址,这个域名不会出现跨域问题,写父页面(接收)域名地址
iframe.contentWindow.postMessage("需要传递的参数", 'http://0.0.0.0:8080')
第二种
this.$refs.iframe.contentWindow.postMessage(workDetailsData.workflowList,'*')
a2.子页面接收父页面收到的信息
* 函数防抖* @param fn* @param interval* @returns {Function}* @constructor*/
export const Debounce = (fn, t) => {let delay = t || 500let timerreturn function () {let args = argumentsif (timer) {clearTimeout(timer)}timer = setTimeout(() => {timer = nullfn.apply(this, args)}, delay)}
}import { Debounce } from '@/utils/public.js'
mounted() {window.onmessage = this.handleMesg// addEventListener触发多次问题// window.addEventListener('message', this.handleMesg)}
},//防抖处理
handleMesg: Debounce(function (e) {//这里做接收数据的操作if(e){console.log(e.data)
}}),
a.3子页面(iframe加载的页面)向父页面传递消息
window.parent.postMessage({string: '我是iframe里面的数据'}, "*");
a.4父页面接收子页面传递的消息
window.onmessage = function(event){console.log(event.data.string) //我是iframe里面的数据
}
a.5 父页面接收子页面传递的消息(第二种)
//监听单个事件
window.addEventListener('message', function (msg) {console.log(msg.data.string)
})
二.项目实践
1.效果图
编辑
编辑
编辑
逻辑如下
1.首先点击审批通过按钮,调用方法
<el-buttonv-if="hasApproved"type="primary"size="small"@click="submit('Approved')">审批通过
</el-button>
2.方法如下
// 审批submit(data) {this.operateStatus = data //传过来的状态储存起来if (this.suspended) { //默认是false 请求详情接口时会根据后端返回这个字段判断他是否挂起this.$message.error('该任务已挂起,无法进行操作!')return}if (data === '撤回') {this.confirmSubmit()return}let label = data === 'Approved' ? '审批通过' : '审批拒绝'this.assignee_schema_model.comment =data === 'Approved' ? '批准' : '拒绝'this.dialogTitle = `确认是否${label}`this.msgContent = `确认${label}吗?`this.visible = true //打开弹窗},
弹窗如下
3.点击确定调用接口方法,调用接口时进行一些判断操作如下
confirmSubmit() {let data = this.operateStatusthis.isLoading = trueif (data === 'Approved' || data == 'Rejected') {const params = { //审批流接口需要的参数variables: [{name: 'approveResult',value: data === 'Approved' ? 'Approved' : 'Rejected', //判断是同意还是拒绝},{name: 'comment',value: this.assignee_schema_model.comment,},{name: 'isWithdraw',value: '0',},],action: 'complete',comment: this.assignee_schema_model.comment,currentTaskId: this.$route.query.id,}if (!this.assignee_schema_model.comment) { //判断是否写了批注 不能为空this.$message.error('请输入批注!')this.isLoading = falsereturn}//重点 这里假如你是需要在自己的页面,比如商品详情页面,即iframe的页面自己调用自己中心的后端接口,不通过公共的审批接口时,需要进入这里,this.isPostMsg这个数据需要自己判断是否需要进入if (this.isPostMsg && data == 'Approved') {// 审批动作在iframe页面中完成this.$refs.iframe.contentWindow.postMessage( //向iframe 页面传递信息{status: data,value: this.assignee_schema_model.comment,params: params,},'*')this.visible = falsethis.isLoading = falsewindow.addEventListener('message', this.handleMesg) //接收iframe页面传递过来的信息return //不往下执行 即不调用公共审批接口}if (!this.src) { //判断配置console.log('审批流配置问题,formKey为空')this.$message.error('流程配置问题,/activiti/task接口返回的formKey为空')this.visible = falsethis.isLoading = falsereturn}if (!this.isOpenIframe(this.src)) {//没有被审批流程的菜单权限this.$message.error(this.authMenuMsg)this.visible = falsethis.isLoading = falsereturn}taskAction(params) //调用公共审批接口 (包含了同意和拒绝) 根据定义的参数判断.then((res) => {if (res.failed === true) {this.$message.error(res.message || '操作失败,请联系管理员')this.isLoading = false} else {this.$message.success('操作成功')this.visible = falsethis.isLoading = falsethis.$store.dispatch( //关闭当前页面路由'tabsBar/delVisitedRoute',this.$route.fullPath)this.$router.push(`/tcl/tof/message/workflow/wait-list`) //跳转到待办列表页面}}).catch((error) => {this.$message.error(error)this.isLoading = false})} else {// 撤回backAction(this.$route.query.id) //撤回流程公共 接口.then((res) => {if (res.failed === true) {this.$message.error(res.message || '操作失败,请联系管理员')this.isLoading = false} else {this.$message.success('操作成功')this.$store.dispatch('tabsBar/delVisitedRoute',this.$route.fullPath)// this.$router.go(-1)this.visible = falsethis.isLoading = falsethis.$router.push(`/tcl/tof/message/workflow/wait-list`)}}).catch((error) => {this.$message.error(error)this.isLoading = false})}},
4.父页面的handleMesg方法(重点交互)
//防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。
//防抖处理 高频调用handleMesg: Debounce(function (e) {if (e.data.type == 'getDataDetail') {//子页面想要获取待办详情接口数据 会主动发送一个事件过来//如判断type是哪个页面过来的//这里向子页面发送数据回去this.$refs.iframe.contentWindow.postMessage(workDetailsData.workflowList, //待办详情接口返回的数据'*')}//这里可以自定义提示信息的操作//iframe子页面会发送一个方法过来 如第五点if (e.source) {if (e.data.status == 'Approved') {//补充知识//getElementsByClassName() 方法返回文档中所有指定类名的元素集合,作为 NodeList 对象。//NodeList 对象代表一个有顺序的节点列表。NodeList 对象 我们可通过节点列表中的节点索引号来访问列表中的节点(索引号由0开始)。//提示: 你可以使用 NodeList 对象的 length 属性来确定指定类名的元素个数,并循环各个元素来获取你需要的那个元素。let doms = document.getElementsByClassName('el-message')[0]if (doms == undefined) {this.$message.success('审批通过')}this.visible = falsethis.$store.dispatch('tabsBar/delVisitedRoute',this.$route.fullPath)if (this.$route.query.type === 'workflow')this.$router.push(`/tcl/tof/order/sales/workflow`)else this.$router.push(`/tcl/tof/message/workflow/wait-list`)} else if (e.data.status == 'Rejected') {let doms = document.getElementsByClassName('el-message')[0]if (doms == undefined) {this.$message.success('审批拒绝')}this.visible = falseif (this.$route.query.type === 'workflow')this.$router.push(`/tcl/tof/order/sales/workflow`)else this.$router.push(`/tcl/tof/message/workflow/wait-list`)} } else if (e.data.status == 'innerError') {let msgObj = e.datathis.$message({type: msgObj.msgType,message: msgObj.msg,})}}}),
5.子页面方法
that.postMsgToFrame('缺少附件', 'warning')
postMsgToFrame(msg, errType) {if (this.isPorcess) {window.parent.postMessage({type: 'custAddMsg',status: 'innerError',msgType: errType,msg: msg,},'*')} else {this.$message({type: errType,message: msg,})}},
子页面一开始要获取审批流待办详情接口数据来进行一些判断如下
mounted() {// console.log('workDetailsData', workDetailsData)//首先向父页面主动发送事件if (this.$route.query.type == 'delay') {window.parent.postMessage({type: 'getDataDetail',},'*')//父页面发送事件过来,这里接收事件和数据,进行对应的操作,如判断该字段在哪个节点是否需要显示window.addEventListener('message', (e) => {if (e.data) {let showNode = []if (this.options.nodeStatus && this.options.nodeStatus.length > 0) {this.options.nodeStatus.forEach((item) => {showNode.push(item.value)})console.log('这里的数据看看', showNode)if (showNode.indexOf(e.data.name) != -1) {obsData.isFlagShowCol = true}}}})}},
配置平台配置流程
iframe页面判断审批同意接口是否调用自己中心的后端接口
computed: {isPorcess: {get() {let ll = this.$route.queryif (ll.custPostMsgFlg && ll.custPostMsgFlg.indexOf('true') > -1) {return true} else {return false}},},
}
进入页面在mounted判断,需要节点都挂载完毕,然后在方法methods中调用审批方法,是由父页面点击审批通过发送事件过来进行调用
mounted() {if (this.isPorcess) {window.addEventListener('message', this.handleMesg)}
}
methods: {// 点击审批async handleMesg(e) {console.log('e', e)if (e.data.status === 'Approved') { //判断是否同意// this.$refs.Address.$refs.tabs.validate()this.examineApprove()}},async examineApprove() {//先进行表单必填校验操作 通过才调用接口let flag = await this.$refs.Address.validateForm()this.$refs['commentForm'].validate((isPass) => {this.schema_model.comment = this.commentForm.comment //批注自定义this.$refs['filterForm'].validate((valid) => {//校验if (valid && flag && isPass) {const loading = this.$baseLoading({target: 'document.body.tree',})subsidyPriceProcess(this.schema_model).then((res) => {if (res.success == false) {loading.close()this.$message.error('审批不通过,接口报错')} else {loading.close()//iframe 向父组件传值window.parent.postMessage({type: 'commodityApprove',status: 'Approved',},'*')}})} else {this.$message({type: 'error',message: '表单校验不通过!',})return false}})})},
}