需求:动态路由要嵌入外链页面,需要传token,需要懒加载,需要url能处理一下不要太暴露
写的比较粗糙,肯定有优化空间,抛砖引玉,抛砖引玉
1、在生成动态路由过程中顺便生成iframe的列表:
// 当遍历到iframe节点时
if(oMenu.path.includes('http')){
let hasFlag = false // 是否已含有该项
// 从localStorage中取iframeList,取不到则取空数组
let iframeList = localStorage.getItem('iframeList') ? JSON.parse(localStorage.getItem('iframeList')) : []
// 处理相关信息
let obj = {
name: oMenu.name,
label: oMenu.path.split('/').length===7?'page-' + oMenu.path.split('/')[6]:oMenu.path,
id: oMenu.id,
path: oMenu.path
}
for(let i=0;i<iframeList.length;i++){
if(iframeList[i].id === obj.id){
// 检测id相同时覆盖信息
iframeList[i] = obj
hasFlag = true
}
}
// 不存在相同id项则push
!hasFlag && iframeList.push(obj)
localStorage.setItem('iframeList',JSON.stringify(iframeList))
}
生成的数据大概这样子:
[
{label: "page-cost-supplement-list", name:"xxx",path:"http://xxx/xx/xx/xx/cost-supplement-list"},
...
]
再顺便添加一个iframe专用的getPath方法,后面要用
getIframePath(params){
const { page } = params
let result = page || '/'
if (page.includes('page-')) {
result = `/out/page?${objToform(params)}`
}
return result
},
2、在固定router里头添加iframe相关路由
{
path: '/out', // 可以改,与后面代码对应
component: Layout,
redirect: '/out',
children: [{
path: ":page", // 可以改,与后面代码对应
name: 'iframe',
component: () =>
import ( /* webpackChunkName: "page" */ '@/components/iframe/main'),
props: true
}]
},
3、处理左边菜单栏点击跳转方法
open(item) {
if (this.screen <= 1) this.$store.commit("SET_COLLAPSE");
this.$router.$avueRouter.group = item.group;
// 处理iframe情况
if(/^http/.test(item.path)){
this.$router.push({
path: this.$router.$avueRouter.getIframePath({
// 本来是用src的,但是src太明显了,遮掩一下,后续也因此要做对应处理
page: this.changePath(item)
}),
query: item.query,
}).catch(() => {});
}else{
this.$router.push({
path: this.$router.$avueRouter.getPath({
name: item[this.labelKey],
src: item[this.pathKey]
}),
query: item.query
}).catch(() => {});
}
}
4、处理路由导航守卫中页面跳转时修改taglist的方法
let value = to.query.src || to.fullPath
const label = to.query.name || to.name
if (meta.isTab !== false && !validatenull(value) && !validatenull(label)) {
// iframe情况,因为上面src变成page了要做对应处理
if( to.query.page && to.query.page.includes('page-') ){
value = to.query.page
const arr = JSON.parse(localStorage.getItem('iframeList'))
store.commit('ADD_TAG', {
// 做对应转换使多标签tab页能正常显示对应名称
label: arr.filter(item => item.label === value)[0].name,
value: value,
params: to.params,
query: to.query,
group: router.$avueRouter.group || [],
})
}else{
store.commit('ADD_TAG', {
label: label,
value: value,
params: to.params,
query: to.query,
group: router.$avueRouter.group || [],
})
}
}
next()
5、接下来要处理iframe组件(这里就是借鉴的思路糅合进来了)
<template>
<div>
<basic-container>
<keep-alive>
<router-view></router-view>
</keep-alive>
<component
v-for="item in hasOpenComponentsArr"
:key="item.name"
:is="item.name"
v-show="item.label === getLabel($route.query.page)"
></component>
</basic-container>
</div>
</template>
export default {
data () {
return {
componentsArr: [],
clientHeight: '',
}
},
created () {
let componentsArr = this.getComponentsArr()
componentsArr.forEach(item => {
// 这里是重点,卡了我好久
// Vue.component(item.name,{template:`<iframe src='${this.getSrc(this.$route.query.page)}' class="iframe"></iframe>`})
// 才发现上面写的有问题!!!!应该下面这样写
let str = item.path +'?token='+ token
Vue.component(item.name,{template:`<iframe src='${str}' class="iframe"></iframe>`})
})
this.componentsArr = componentsArr
this.isOpenIframePage();
},
props: ['routerPath'],
watch: {
$route: function () {
this.isOpenIframePage();
},
},
computed: {
// 实现懒加载,只渲染已经打开过(hasOpen:true)的iframe页
hasOpenComponentsArr() {
return this.componentsArr.filter(item => item.hasOpen);
}
},
methods: {
getLabel(val){
const arr = JSON.parse(localStorage.getItem('iframeList'))
return arr.filter(item => item.label === val)[0].name
},
getSrc(val){
// 要附带的token就在这里添加进去
let token = JSON.parse(localStorage.getItem("pig-access_token")).content
const arr = JSON.parse(localStorage.getItem('iframeList'))
return arr.filter(item => item.label === val)[0].path + '?token=' + token
},
// 根据当前路由设置hasOpen
isOpenIframePage() {
const target = this.componentsArr.find(item => {
return item.label === this.getLabel(this.$route.query.page)
});
if (target && !target.hasOpen) {
target.hasOpen = true;
}
},
// 遍历路由的所有页面,把含有iframeComponent标识的收集起来
getComponentsArr() {
const iframeArr = JSON.parse(localStorage.getItem('iframeList'))
console.log(iframeArr)
return iframeArr.map((item) => {
return {
label: item.name,
// 这个name就是上面注册组件的名字,由于我这里附带信息没有好用的所以这样做
// 注意组件注册的名字规定
name: 'a'+item.id,
path: item.path,
hasOpen: false, // 是否打开过,默认false
}
})
},
}
}
</script>
由于使用了Vue.component注册组件不要忘了在vue.config.js里做修改
module.exports = {
runtimeCompiler: true, // 这一行就够了
}
6、多页签tab的点击方法也要做对应修改
openTag(item) {
let tag;
if (item.name) {
tag = this.findTag(item.name).tag;
// 如果标签地址和目前地址不同才跳转,否则会报错
if(item.name != this.$route.path){
// 处理iframe
if(item.name.includes('page-')){
// 其实也没啥,主要就是把src换成page
this.$router.push({
path: this.$router.$avueRouter.getIframePath({
page: tag.value
}),
})
}else{
this.$router.push({
path: this.$router.$avueRouter.getPath({
name: tag.label,
src: tag.value
}),
query: tag.query
})
}
}
}
},
这样就完成了 大概效果像这样
第一次分享经验,比较粗糙,还望多包涵