【Vue】使用iframe解决多应用整合问题(微前端)

一、需求背景

有老系统需要重构,新做的系统需要做一个大一统的整合,类似一个分类栏目

在菜单位置罗列出有什么子系统应用,点击对应的应用菜单,展示区跳转到相应的子系统应用中

我用Excel简单描述了下系统的页面效果:

 

二、技术方案

第一种,使用iframe实现,html提供了iframe标签实现页面内访问其他页面的功能

第二种,使用微前端框架,微前端的实现原理很多,iframe或则组件,又或者远程调用....

  详细见:https://www.jianshu.com/p/0aaf0daaaeee

 

基于现实情况的考量:非专业前端开发,项目成员没有前端大神,微前端一无所知,项目初版开发周期短

我们选择的方案是使用iframe实现

 

三、落地实现

主应用web + 基础应用web 共用 基础应用server的接口

基础应用相当于认证中心,其他业务server只需要认证中心的鉴权即可

所有web使用同一个令牌做前端权限控制(同理后端)

 

iframe标签内的应用和主应用共享浏览器数据,需要保持在同一个域之内

 

1、主应用web改造

主应用本身不需要server,  都是对接基础应用的,所以新增api接口

解决web登录,跳转,退出的接口问题

 

请求基础应用的令牌携带方式和一些其他参数传递的处理也调整:

 

 vuex的用户actions的方法也要调整

这里定义如何存储用户信息,和退出时需要处理的步骤逻辑

 

因为我的token存储方式和基础应用的存储方式并不一致,同一个token以两种方式存储

在退出登录的时候,一定要同步把基础应用的token删除,那这里是把那边的逻辑代码搬过来了

(见上面logout方法调用 removeJnpfToken)

 

iframe跳转实现

首先要有侧边应用菜单的选择:

<el-aside class="sub-sys-menu">
    <img :src="appLogo" class="app-logo">
    <el-menu
        mode="vertical"
        class="sub-sys-menu-list"
        :default-active="getDefaultActiveMenu"
        @select="whenMenuItemSelect"
    >
        <el-menu-item
            v-for="(m, idx) in sysMenuList"
            :key="`menu${idx}`"
            :index="m.key"
        >
            <template slot="title">
                <div class="sub-sys-menu-item">
                    <svg-icon :icon-class="m.icon" class="sub-sys-menu-icon" />
                    <span class="sub-sys-menu-title">{{ m.name }}</span>
                </div>
            </template>
        </el-menu-item>
    </el-menu>
</el-aside>

 

默认激活的应用,和应用选中后的事件处理

    getDefaultActiveMenu() {
      if (!this.sysMenuList || this.sysMenuList.length === 0) return ''
      return this.sysMenuList[0].key
    },
    async whenMenuItemSelect(key, keyPath) {
      await this.isLoginCheck()
      const app = this.appList.find(x => x.symbol === key)
      const menu = this.sysMenuList.find(x => x.key === key)
      this.currentAppUrl = `${app.url}?Authorization=${encodeURIComponent(this.currentToken)}&time=${new Date().getTime()}`
      this.currentAppTitle = menu.name
    },

 

因为点击时不是路由跳转,所以手动追加是否在线判断:

async isLoginCheck() {
      try {
        await this.$store.dispatch('UserGetInfo')
      } catch (error) {
        await this.$store.dispatch('UserResetToken')
        this.$router.push({ path: '/login' })
      }
    }

 

iframe标签去除所有边框,外边距,百分百填充容器元素

 <el-main class="frame-container">
        <iframe
          frameborder="0"
          border="0"
          marginwidth="0"
          marginheight="0"
          width="100%"
          height="100%"
          :src="currentAppUrl"
        />
      </el-main>

  

数据定义部分:

  data() {
    return {
      color: 'red',
      appLogo: require('@/assets/sidelogo-light.png'),
      sysMenuList: [
        { name: '经营场所', icon: 'jxxcs', key: 'APP-1001' },
        { name: '非经营场所', icon: 'fjyxcs', key: 'APP-1002' },
        { name: '等保测评', icon: 'dbcp', key: 'APP-1003' },
        { name: 'APP管理', icon: 'app', key: 'APP-1004' },
        { name: '系统管理', icon: 'sz', key: 'APP-1005' }
      ],
      appList: [
        { symbol: 'APP-1001', url: 'https://www.cnblogs.com/mindzone/p/17964919' },
        { symbol: 'APP-1002', url: 'https://www.bilibili.com/' },
        { symbol: 'APP-1003', url: 'http://192.168.200.45/cp-mng-web/home' },
        { symbol: 'APP-1004', url: 'https://www.cnblogs.com/mindzone' },
        { symbol: 'APP-1005', url: 'http://192.168.200.45:3000/home' }
        // { symbol: 'APP-1005', url: 'http://172.17.29.7:3000/home' }
      ],
      currentAppUrl: '',
      currentAppTitle: '',
      currentUserName: '',
      currentToken: '',
      changeFlag: false
    }
  },
  computed: {
    ...mapGetters(['userInfo', 'token'])
  },

  

 页面挂载时,同步当前用户信息:

  mounted() {
    this.currentUserName = this.userInfo.userName
    this.currentToken = `${this.token}`
  },

 

2、子应用免登录访问问题:

因为进入子应用不应该再次输入密码登录访问,在进入主应用的时候已经通过了认证中心的处理

这里以基础应用为例(本身也是子应用)进行改造调整:

 在路由权限逻辑中追加一个免登录的跳转,判断路由路径 + 请求参数是否携带令牌,如果符号则设置令牌,继续跳转

 

posted @ 2024-02-09 11:06  emdzz  阅读(164)  评论(0编辑  收藏  举报