多平台统一管理软件接口,如何实现多平台统一管理软件接口
757
2023-03-07
Vue集成Iframe页面的方法示例
1. 项目需求
我们切换为vue框架是后面的事情,之前还有一些功能页面是用jsp页面写的,而我们的管理系统需要既支持Vue的url,又要支持这些发布之后的jsp页面
还有一个就是切换tab回来的时候之前输入的东西还要存在
系统页面截图
2. 实现思路
针对这个问题,我们最开始的实现思路是写了一个iframe的通用组件,然后把不同的http的页面的url传递进来进行切换,但是这样不满足第二条,我们发现只要切换了vue的路由,然后再切回http的页面,iframe中的src属性的页面就会从新刷新,没有办法保留住东西,于是就有了下面的实现思路
我们在vue的router-view同级别添加了一个iframeTemp组件,其实就是一个elementUI的tab组件,然后把tab组件的头的样式隐藏在我们菜单栏的下面
.position {
position: relative
}
.router-out-content {
position: static;
}
import { mapState } from 'vuex'
import iframeTemp from '@/containers/main/IframeTemplate.vue'
export default {
data() {
return {}
},
components: {
iframeTemp
},
computed: {
...mapState([
'showIframe'
])
}
}
/*
* IframeTemplate.vue组件的内部
**/
v-for="(item, index) in iframeTabData" :key="item.tag" :label="item.name" :name="item.tag" >
v-for="(item, index) in iframeTabData"
:key="item.tag"
:label="item.name"
:name="item.tag"
>
#fwIframe {
/*测试位置的时候显示这段--开始*/
/*width: 100%;*/
/*height: 100%;*/
/*background-color: red;*/
/*display: block !important;*/
/*测试位置的时候显示这段--结束*/
position: absolute;
left: 0;
right: 0;
top: 45px;
bottom: 0;
z-index: 5000 !important;
.el-tab-pane {
height: 100%;
width: 100%;
iframe {
/*height: auto;*/
min-height: 600px;
/*height: calc(100% - 45px);*/
width: 100%;
}
}
.full {
position: relative;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
}
// selectTabCode=>iframeSelectTab
// tabsList=>iframeTabData
import {mapState} from 'vuex'
import * as mainConst from '@/store/mainConst.js'
export default{
data(){
return {
// tabsList: [],
// selectTabCode: ''
}
},
computed: {
...mapState([
'iframeTabData',
'iframeSelectTab',
'navTabData',
'systemName'
])
},
mounted(){
const _this = this
// 1、监听添加iframe中tab的广播
this.$root.bus.$on('addIframeTab', function (item) {
// _this.tabsList.push(item)
// _this.selectTabCode = item.tag
_this.$store.commit(mainConst.M_IFRAME_PUSH_TAB, item)
_this.$store.commit(mainConst.M_IFRAME_CHANGE_SELECTCODE, item.tag)
})
// 2、监听切换iframe中tab的广播
this.$root.bus.$on('changeIframeTab', function (tag) {
_this.$store.commit(mainConst.M_IFRAME_CHANGE_SELECTCODE, tag)
})
// 3、监听删除iframe中tab的广播
this.$root.bus.$on('deleteIframeTab', function (obj) {
// 1、删除iframe中的指定tab页面
_this.$store.commit(mainConst.M_IFRAME_DELETE_TAB, obj)
// _this.tabsList = _this.tabsList.filter(tab => tab.tag !== obj.tag)
// 2、如果删除的一级tab不是当前激活的一级tab,TabsTemeplate中的一级tab删除事件已经在vuex中删除了,不需要做路由跳转操作了
let index = obj.index
for (let i = 0; i < _this.navTabData.length; i++) {
if (_this.navTabData[i].active) {
return
}
}
// 3、如果删除的一级tab是当前激活的一级tab,
const con = _this.navTabData[index - 1] || _this.navTabData[index]
let url = `/${_this.systemName}`
if (con) {
// 还有其他的一级tab,就赋值其他的一级tab的url,探后跳转
url = con.url
con.active = true
// 如果还有其他一级的tab,那么还要判断跳转的页面是不是iframe
if (url.toLowerCase().indexOf("/iframe") == 0) {
// 如果是iframe页面,显示iframe,广播iframe的切换tab切换事件,路由进行跳转
_this.$store.commit(mainConst.M_SHOW_IFRAME)
_this.$root.bus.$emit("changeIframeTab", url.slice(8))
} else {
// 如果不是iframe页面,隐藏iframe,路由进行跳转
_this.$store.commit(mainConst.M_HIDE_IFRAME)
// _this.$store.commit(mainConst.M_UPDATE_NAVTABDATA, {navIndex: index})
}
}
else {
// 没有其他的一级tab,直接隐藏iframe,跳首页
_this.$store.commit(mainConst.M_HIDE_IFRAME)
}
_this.$router.push(url)
})
http://}
}
之后的ifram组件的显示隐藏和tab切换,都是通用vuex和bus事件广播实现的
/*
* mainConst.js
**/
/*****************************getter常量****************************************/
export const G_GET_NAVTABDATA = 'G_GET_NAVTABDATA'
/*****************************mutations常量*************************************/
// 一级tab处理
export const M_PUSH_NAVTABDATA = 'M_PUSH_NAVTABDATA'
export const M_DELETE_NAVTABDATA = 'M_DELETE_NAVTABDATA'
export const M_UPDATE_NAVTABDATA = 'M_UPDATE_NAVTABDATA'
// iframe切换处理
export const M_SHOW_IFRAME = 'M_SHOW_IFRAME'
export const M_HIDE_IFRAME = 'M_HIDE_IFRAME'
// iframe添加,删除,选择处理
export const M_IFRAME_PUSH_TAB='M_IFRAME_PUSH_TAB'
export const M_IFRAME_DELETE_TAB='M_IFRAME_DELETE_TAB'
export const M_IFRAME_CHANGE_SELECTCODE='M_IFRAME_CHANGE_SELECTCODE'
// 设置全局系统变量
export const M_SET_SYSTEMNAME = 'M_SET_SYSTEMNAME'
/*****************************actions常量***************************************/
// export const A_REQUEST_DATA = 'A_REQUEST_DATA'
/*
* mainModule.js
**/
import * as mainConst from './mainConst.js'
export default {
state: {
// 一级Tab导航数据集合
navTabData: [],
// 进入的主系统前缀
systemName:'',
// 控制路由同级的Iframe的显示隐藏
showIframe: false,
// iframe页面中的选中页签的code值
iframeSelectTab:'',
// iframe页面的tab数据集合
iframeTabData:[]
},
getters: {
[mainConst.G_GET_NAVTABDATA](state, getters){
return state.navTabData
}
},
mutations: {
// 一级tab处理
[mainConst.M_UPDATE_NAVTABDATA](state, payload){
const index = payload.navIndex
state.navTabData.forEach((item)=> {
item.active = false
})
// 当你利用索引直接设置一个项时是不能触发视图的从新渲染的,下面是老方法和解决办法
// state.navTabData[index].active=true
let newItem = Object.assign({}, state.navTabData[index], {active: true})
// console.log(newItem, 'store newItem')
state.navTabData.splice(index, 1, newItem)
},
[mainConst.M_PUSH_NAVTABDATA] (state, payload) {
state.navTabData.push(payload)
},
[mainConst.M_DELETE_NAVTABDATA] (state, payload) {
state.navTabData.splice(payload.navIndex, 1)
},
// Iframe显示隐藏切换处理
[mainConst.M_SHOW_IFRAME] (state, payload) {
state.showIframe = true
},
[mainConst.M_HIDE_IFRAME] (state, payload) {
state.showIframe = false
},
// Iframe添加,删除,选中处理
[mainConst.M_IFRAME_PUSH_TAB] (state, payload) {
state.iframeTabData.push(payload)
},
[mainConst.M_IFRAME_DELETE_TAB] (state, payload) {
state.iframeTabData = state.iframeTabData.filter(tab => tab.tag !== payload.tag)
},
[mainConst.M_IFRAME_CHANGE_SELECTCODE] (state, payload) {
state.iframeSelectTab=payload
},
// 设置全局system变量
[mainConst.M_SET_SYSTEMNAME] (state, payload) {
state.systemName=payload
}
},
actions: {
// actions的最终功能是修改state,但是它不直接修改state,而是调用mutations
// async [aboutConst.A_REQUEST_DATA]({dispatch,commit}) {
// commit(aboutMutations.REQUEST_LOADING)
// await service.getMovieListData('{"movieType":"in_theaters","pageIndex":2,"start":0,"count":10}')
// console.log(333333)
// await function(){setTimeout(function () {
// commit(aboutMutations.REQUEST_FAILD)
// },6000)}()
// console.log(66666)
// }
// actions的最终功能是修改state,但是它不直接修改state,而是调用mutations
// async [aboutConst.A_REQUEST_DATA]({dispatch,commit}) {
// commit(aboutMutations.REQUEST_LOADING)
// await service.getMovieListData('{"movieType":"in_theaters","pageIndex":2,"start":0,"count":10}')
// console.log(333333)
// await function(){setTimeout(function () {
// commit(aboutMutations.REQUEST_FAILD)
// },6000)}()
// console.log(66666)
// }
}
}
/*
* 三级菜单的点击处理
**/
v-for="(item,index) in funMenu.nextMenu"
@mouseover="clickByMenu($event,item,'nextMenu')"
@click="clickMenuJump(funMenu.nextMenu, item)"
>
{{item.resourceName }}
@click="clickByMenu($event,item,'lastMenu')">
.main-nav {
position: relative;
height: 42px;
line-height: 42px;
background: #eee;
border-bottom: 1px solid #ddd;
}
.main-nav a {
color: #303e51;
text-decoration: none;
}
.main-nav a:hover {
color: #438eb9;
}
.main-nav .main {
/*padding: 0 16px;*/
text-align: center;
border-right: 1px solid #ddd;
position: relative;
background: #eee;
width: 122px;
}
.main-nav .main.active, .main-nav .main:hover {
background: white;
}
.main-nav .more-menu {
position: fixed;
top: 84px;
left: 0;
max-height: 500px;
bottom: 124px;
z-index: 998;
background: #fff;
border: 1px solid #ddd;
border-left: none;
border-top: 0;
overflow: hidden;
box-shadow: 1px 1px 10px #ddd;
}
.main-nav .more-menu ul, .main-nav .more-menu .dl {
text-align: left;
overflow: auto;
}
.main-nav .more-menu a {
font-size: 14px;
color: #303e51;
text-decoration: none;
}
.main-nav .more-menu a:hover, .main-nav .more-menu a.active {
color: rgb(46, 167, 224);
}
.main-nav .more-menu .first-menu {
height: 100%;
border-right: 1px solid #ddd;
box-shadow: -1px 0px 5px #ddd inset;
/*width: 138px;*/
}
.main-nav .more-menu .first-menu li {
height: 36px;
line-height: 36px;
margin: 0 15px 0 6px;
min-width: 94px;
}
.main-nav .more-menu .first-menu a {
display: block;
background: url(../../asserts/images/home/main/icon_1.png) no-repeat 5px center;
width: 100%;
height: 100%;
border-bottom: 1px solid #dddddd;
padding-left: 20px;
box-sizing: border-box;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
text-indent: 5px;
}
.main-nav .more-menu .first-menu a.active, .main-nav .more-menu .first-menu a:hover {
background: url(../../asserts/images/home/main/icon_2.png) no-repeat 5px center rgb(46, 167, 224);
color: white;
border: 0;
}
.main-nav .more-menu .next-menu {
height: 100%;
border-right: 1px solid #ddd;
box-shadow: -1px 0px 5px #ddd inset;
/*width: 138px;*/
line-height: 14px;
}
.main-nav .more-menu .next-menu li:first-child {
margin-top: 10px;
}
.main-nav .more-menu .next-menu li {
margin-bottom: 16px;
margin-left: 16px;
}
.main-nav .more-menu .next-menu li a {
border-left: 2px solid transparent;
padding-left: 10px;
margin-right: 24px;
}
.main-nav .more-menu .next-menu li a:hover, .main-nav .more-menu .next-menu li a.active {
border-left: 2px solid rgb(46, 167, 224);
}
.main-nav .more-menu .last-menu {
height: 100%;
min-width: 288px;
line-height: 14px;
}
.main-nav .more-menu .last-menu .dt {
margin-left: 16px;
margin-top: 10px;
span {
color: #566678;
}
}
.main-nav .more-menu .last-menu .dd {
color: #7a8897;
margin-top: 16px;
margin-left: 4px;
> li {
margin-bottom: 16px;
a {
border-left: 2px solid transparent;
padding-left: 6px;
margin-right: 16px;
&:hover, &.active {
border-color: #2ea7e0;
}
}
}
}
/*.main-nav .more-menu .last-menu dd a:hover,.main-nav .more-menu .last-menu dd a.active{*/
/*border-left: 2px solid rgb(46,167,224);*/
/*}*/
.main-nav .main .fun {
width: 100%;
height: 100%;
display: block;
}
.main-nav .main .fun:before {
content: "";
width: 18px;
height: 18px;
background: url("../../asserts/images/home/main/icon-all.png");
background-position: -89px -7px;
display: inline-block;
margin-right: 10px;
margin-top: 2px;
vertical-align: text-top;
}
.main-nav .l-nav {
z-index: 2;
}
.main-nav .nav-index {
width: 90px;
text-align: center;
position: relative;
background: #eee;
}
.main-nav .nav-index:after {
content: "";
width: 8px;
height: 40px;
background: url(../../asserts/images/home/main/shadow-l.png);
position: absolute;
top: 2px;
left: 90px;
}
.main-nav .lt-tab {
position: absolute;
left: 0;
z-index: 2;
border-bottom: 1px solid #ddd;
}
/*tab--------*/
.main-nav .ct-tab {
position: absolutehttp://;
z-index: 1;
left: 213px;
width: 10000000px;
}
.main-nav .ct-tab .ct-ul {
}
.main-nav .ct-tab .ct-ul li {
position: relative;
float: left;
}
.main-nav .ct-tab .ct-ul li a {
height: 24px;
line-height: 24px;
margin: 9px 0;
min-width: 90px;
/*max-width: 190px;*/
border-right: 1px solid #ddd;
display: block;
text-align: center;
position: relative;
}
.main-nav .ct-tab .ct-ul li a i {
display: none;
}
.main-nav .ct-tab .ct-ul li a i {
display: none;
}
.main-nav .ct-tab .ct-ul li a .content {
display: block;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.main-nav .ct-tab .ct-ul li a:hover {
z-index: 1;
}
.main-nav .ct-tab .ct-ul li:first-child a:hover, .main-nav .ct-tab li:first-child a.active {
margin-left: 0;
margin-right: 0;
}
.main-nav .ct-tab .ct-ul li a:hover, .main-nav .ct-tab li a.active {
max-width: 250px;
display: block;
text-align: center;
position: relative;
border: 0;
margin: 0 -20px;
margin-top: 4px;
color: black;
padding: 0;
}
.main-nav .ct-tab .padding {
width: auto;
padding: 0 16px;
}
.main-nav .ct-tab .ct-ul li a:hover > i, .main-nav .ct-tab .ct-ul li a.active > i {
display: inline-block;
width: 34px;
height: 37px;
float: left;
}
.main-nav .ct-tab .ct-ul li a:hover .line-l {
background: url(../../asserts/images/home/main/line_left.png) no-repeat;
}
.main-nav .ct-tab .ct-ul li a:hover .line-r {
background: url(../../asserts/images/home/main/line_right.png) no-repeat;
}
.main-nav .ct-tab .ct-ul li a.active .line-l {
background: url(../../asserts/images/home/main/line_sel_left.png) no-repeat;
}
.main-nav .ct-tab .ct-ul li a.active .line-r {
background: url(../../asserts/images/home/main/line_sel_right.png) no-repeat;
}
.main-nav .ct-tab .ct-ul li a:hover .content, .main-nav .ct-tab li a.active .content {
border-top: 1px solid #ddd;
float: left;
line-height: 36px;
min-width: 60px;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
background: rgb(245, 245, 245);
}
.main-nav .ct-tab .ct-ul li a:hover .cha, .main-nav .ct-tab .ct-ul li a.active .cha {
background: rgb(245, 245, 245);
width: 20px;
height: 36px;
line-height: 36px;
border-top: 1px solid #ddd;
padding-left: 7px;
color: #303e51;
}
.main-nav .ct-tab .ct-ul li a.active .content, .main-nav .ct-tab .ct-ul li a.active .cha {
background: white;
}
.main-nav .ct-tab .ct-ul li a .cha {
color: #eee;
}
.main-nav .ct-tab .ct-ul li a .cha:hover {
color: black;
}
.main-nav .ct-tab .ct-ul li a.active {
z-index: 2;
}
import axios from 'axios'
import { mapState} from 'vuex'
import * as mainConst from '@/store/mainConst.js'
import config from '@/config/index.js'
import storage from '@/utils/storage.js'
export default{
data(){
return {
funMenu: {
// 一级菜单
firstMenu: [],
// 二级菜单
nextMenu: [],
// 三级菜单
lastMenu: [],
// 是否显示
menuIsShow: true
}
}
},
computed: mapState({
// 箭头函数可使代码更简练
funcMenuList: state => state.funcMenuList,
}),
methods: {
// 跳转首页
goHome(){
// 跳转首页就关闭iframe
this.$store.commit(mainConst.M_HIDE_IFRAME)
this.$router.push(`/${this.$store.state.systemName}`)
},
// ★★★★★调用方法获取三级菜单列表★★★★★
getMainMenu(){
var _this = this
if (this.funMenu.firstMenu.length) {
this.funMenu.menuIsShow = true
} else {
if (config.setting.funcMenu) {
_this.funMenu.firstMenu = storage.getItem('hivescm.userAuthor').menus.funcs
} else {
axios.get("data/menu_json.json")
.then(function (res) {
_this.funMenu.firstMenu = res.data.result.funcs
})
}
}
},
// 点击菜单展开下一级别列表事件
clickByMenu(e, menuItem, level){
let menuList = this.funMenu[level]
switch (level) {
case "firstMenu": {
this.funMenu.nextMenu = this.getFirstAndNextVal(menuList, menuItem)
this.funMenu.lastMenu = []
}
break
case "nextMenu": {
if (!menuItem.url.length) this.funMenu.lastMenu = this.getFirstAndNextVal(menuList, menuItem)
// menuItem.url.length ? this.clickMenuJump(menuList, menuItem) : this.funMenu.lastMenu = this.getFirstAndNextVal(menuList, menuItem)
}
break
case "lastMenu": {
this.clickMenuJump(menuList, menuItem)
}
break
}
},
// ★★★★★点击有url的菜单,跳转事件★★★★★
clickMenuJump(menuList, menuItem){
if (!menuItem.url.length) return
this.funMenu.menuIsShow = false
this.lastmenuChange(menuList, menuItem)
let iframeTabItem = {}
// 1、路由跳转和iframe的显示隐藏
if (menuItem.url.toLowerCase().indexOf("/") != 0 || menuItem.url.toLowerCase().indexOf("/iframe") == 0) {
// 判断如果是iframe的url,显示iframe
// 定义一个新的item对象,防止对象的引用
iframeTabItem = Object.assign({}, menuItem)
this.$store.commit(mainConst.M_SHOW_IFRAME)
// 待优化:应该有优化为手动赋值样式
// (1)、此处利用router-view的特性,让一级tab变颜色
// (2)、这个还是控制一级tab点击切换tab标签的重要因素
// 因为原始的iframe的url已经改变,所以要保存到一个新的变量里面,如果已经有了就不需要在放了
if (!menuItem.iframeUrl) {
menuItem.iframeUrl = menuItem.url
let userId = storage.getItem('hivescm.userAuthor').id
let token = storage.getItem('hivescm.userAuthor').token
iframeTabItem.url = `${menuItem.url}?userId=${userId}&token=${token}`
} else {
let userId = storage.getItem('hivescm.userAuthor').id
let token = storage.getItem('hivescm.userAuthor').token
iframeTabItem.url = `${menuItem.iframeUrl}?userId=${userId}&token=${token}`
console.log(iframeTabItem.url)
// iframeTabItem.url = menuItem.iframeUrl
}
menuItem.url = `/iframe/${menuItem.tag}`
this.$router.push(`/iframe/${menuItem.tag}`)
} else {
// 判断如果是spa的url,隐藏iframe
this.$store.commit(mainConst.M_HIDE_IFRAME)
menuItem.url=`${menuItem.url}?permissionId=${menuItem.permissionId}`
this.$router.push({path:menuItem.url,query:{permissionId:menuItem.permissionId}})
}
// 2、判断vuex中是否有重复的tab标签
let navTabData = this.$store.state.navTabData
for (let i = 0; i < navTabData.length; i++) {
if (navTabData[i].url == menuItem.url) {
// 已经有页签了,一级tab内容不重新渲染
// 切换一级tab页签的激活样式
this.$store.commit(mainConst.M_UPDATE_NAVTABDATA, {navIndex: i})
// 从新计算一级tab位置
this.$root.bus.$emit("clickLastMenu", menuItem)
if (menuItem.url.toLowerCase().indexOf("/iframe") == 0) {
// 如果已经iframe中的tab已经存在,那么触发iframe中的切换事件
// this.$store.commit(mainConst.M_SHOW_IFRAME)
this.$root.bus.$emit("changeIframeTab", menuItem.url.slice(8))
}
return
}
}
// 3、向vuex中添加一级tab
// 默认是否选中
menuItem.active = true
// 向一级tab中添加新的tab标签
this.$store.commit(mainConst.M_PUSH_NAVTABDATA, menuItem)
this.$store.commit(mainConst.M_UPDATE_NAVTABDATA, {navIndex: navTabData.length - 1})
// 向iframe中的tab添加页签
this.$root.bus.$emit("addIframeTab", iframeTabItem)
},
// 清空导航属性值,确保再次点击无选中样式及不匹配数据
lastmenuChange(menuList, menuItem){
this.funMenu.firstMenu.forEach(function (item) {
item.active = false
})
this.funMenu.nextMenu.forEach(function (item) {
item.active = false
})
this.funMenu.lastMenu.forEach(function (item) {
item.active = false
})
this.funMenu.nextMenu = []
this.funMenu.lastMenu = []
},
// 增加选中样式及赋值下级菜单
getFirstAndNextVal(menuList, menuItem){
var childFuncs = []
for (let i = 0; i < menuList.length; i++) {
if (menuList[i].permissionId == menuItem.permissionId) {
menuList[i].active = true
childFuncs = menuList[i].childFuncs || []
} else {
menuList[i].active = false
}
}
return childFuncs
}
}
}
还要添加一个没用的路由,因为我们的锚记还要发生变化
/*
* iframe/router/index.js
*/
const systemNamePrefix = "iframe_"
import MainContainer from '@/containers/MainContainer.vue'
import IframeComponent from '@Iframe/containers/IframeComponent.vue'
export default [
{
path: '/iframe',
component: MainContainer,
children: [
{path: ':tag', component: IframeComponent, meta: {requiresAuth: true, keepAlive: true}},
],
meta: {requiresAuth: true}
}
]
/*
* iframeComponent.vue,一个没用的vue文件,只是为了让浏览器中的锚记发生变化
*/
{{src}}
export default{
data(){
return {
isCache: true,
src: ''
}
},
created(){
},
mounted(){
// 1、这个页面存在的意义就是在iframe页面切换的时候,路由可以跳转过去用,没有实际大的作用,但是得有这个页面
// 2、iframe的Tab页面的z-index比这个页面的高
this.src=this.$route.params.tag
}
}
3. 思考点
虽然这样和iframe结合有点恶心,但是可以实现我们的思路
在这个功能的实现中我们用到了bus事件总线的广播和监听
其实这点我们是可以仔细思考的,因为大量的使用广播不可控,我们可以完全用vuex去实现,这点用了广播,确实偷懒了
广播并不是不推荐,而是要使用对场景,这点其实用广播确实不太好,不利于扩展,谁能猜出来会有哪些扩展?
大家不用关心具体的代码,如果你们遇到类似的问题,了解这个思路就可以了
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~