实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript子应用+vue2微前端架构实现动态菜单与登录共享
导读:
在当今的前端开发中,微前端架构已经成为了一种流行的架构模式。本文将介绍如何结合Vue 2基座、Vue 3子应用、Vite构建工具和TypeScript语言,利用qiankun微前端框架实现动态菜单和登录共享功能的实战指南。
效果:

 
引言
当前项目架构,vue+ruoyi+elementUI作为基座,vue3+recoDesign+vite+ts作为子应用,基座只用于登录鉴权,动态菜单功能,尽量少在基座写其他业务,子应用分为业务子应用,系统子应用,其他业务子应用,结合npm私服组件库进行组件抽取,供各个子应用使用,基座登录后,将token及其其他子应用需要的参数通过props进行传递比如最直接的【按钮权限,token】。
技术栈介绍
- vue2全家桶+ruoyi脚手架进行基座改造。
- vue3全家桶+arcoDesign中台后台脚手架进行子应用改造。
vue2子应用改造,vue3子应用改造
http://t.csdnimg.cn/U7a3p,vue2+qiankun项目实战
http://t.csdnimg.cn/4UFDs,vue3+qiankun项目实战
- npm私服组件库的打包上传拉取使用:http://t.csdnimg.cn/5Tgax。
创建vue2基座
- 安装qiankunnpm i qiankun -S
- 设置需要将子应用的页面嵌入到主应用的某个div中(子应用在主应用上的渲染出口)<div id="subapp-container">
  
- 在主应用(基座)中注册子应用
  
 整体代码:
import Vue from 'vue'
import Cookies from 'js-cookie'
import Element from 'element-ui'
import './assets/styles/element-variables.scss'
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun';
import "ant-design-vue/dist/antd.less"
import 'default-passive-events'
import '@/assets/styles/index.scss' // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css
import App from './App'
import store from './store'
import router from './router'
import directive from './directive' // directive
import plugins from './plugins' // plugins
import { download } from '@/utils/request'
import './assets/icons' // icon
import './permission' // permission control
import { getDicts } from "@/api/system/dict/data";
import { getConfigKey } from "@/api/system/config";
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
// 分页组件
import Pagination from "@/components/Pagination";
// 自定义表格工具组件
import RightToolbar from "@/components/RightToolbar"
// 富文本组件
import Editor from "@/components/Editor"
// 文件上传组件
import FileUpload from "@/components/FileUpload"
// 图片上传组件
import ImageUpload from "@/components/ImageUpload"
// 图片预览组件
import ImagePreview from "@/components/ImagePreview"
// 字典标签组件
import DictTag from '@/components/DictTag'
// 头部标签组件
import VueMeta from 'vue-meta'
// 字典数据组件
import DictData from '@/components/DictData'
//局部使用antDesign-vue中的tree组件
import { Tree } from 'ant-design-vue';
import { Table } from 'ant-design-vue';
import { Icon } from 'ant-design-vue';
// import a from "hskCommApi"
Vue.config.productionTip = false;
// 全局方法挂载
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
Vue.prototype.parseTime = parseTime
Vue.prototype.resetForm = resetForm
Vue.prototype.addDateRange = addDateRange
Vue.prototype.selectDictLabel = selectDictLabel
Vue.prototype.selectDictLabels = selectDictLabels
Vue.prototype.download = download
Vue.prototype.handleTree = handleTree
// 全局组件挂载
Vue.component('DictTag', DictTag)
Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
Vue.component('Editor', Editor)
Vue.component('FileUpload', FileUpload)
Vue.component('ImageUpload', ImageUpload)
Vue.component('ImagePreview', ImagePreview)
Vue.component('ATree', Tree)
Vue.component('ATable', Table)
Vue.component('AIcon', Icon)
Vue.use(directive)
Vue.use(plugins)
Vue.use(VueMeta)
DictData.install()
Vue.use(Element, {
  size: Cookies.get('size') || 'medium' // set element-ui default size
})
// 1. 注册微应用 
registerMicroApps([
  {
    name: 'son',
    entry: process.env.VUE_APP_BUSINESS, // 子应用页面访问入口
    container: '#subapp-container', // 子应用渲染的出口
    activeRule: '/vision-web/business-module-vue2', // 路径匹配规则
    sandbox: {
      strictStyleIsolation: true, // 开启样式隔离
    },
    props: { sharedStore: store, baseName: '/vision-web/business-module-vue2' }
  },
  {
    name: 'son2',
    entry: process.env.VUE_APP_SYSTEM_URL, // 子应用页面访问入口
    container: '#subapp-container', // 子应用渲染的出口
    activeRule: '/vision-web/system-module-vue2', // 路径匹配规则
    sandbox: {
      strictStyleIsolation: true, // 开启样式隔离
    },
    props: { sharedStore: store, baseName: '/vision-web/system-module-vue2' }
  },
  {
    name: 'business-module-vue3',  // 微应用package.json的name字段
    entry: '//192.168.80.15:8010/business-module-vue3/', // 微应用访问地址,默认加载这个html页面并解析其中的js动态执行
    container: '#subapp-container', // 子应用渲染的出口
    // return location.pathname.includes('/vite-vue3-app2') 
    activeRule: '/vision-web/business-module-vue3',// 激活路径,微应用路由
    sandbox: {
     strictStyleIsolation: false, // 开启样式隔离
    },
    props: { sharedStore: store, baseName: '/vision-web/business-module-vue3' }
  },
])
// 判断subapp-container是否已加载,如果未加载就延迟
function ensureContainerAndStartMicroApps() {
  if (document.getElementById('subapp-container')) {
    // 容器存在,可以注册微应用并启动
    // registerMicroApps([...]); // 注册微应用的代码
    setDefaultMountApp('/'); // 默认打开的子应用
    start({
      sandbox: {
        // strictStyleIsolation: true,
        experimentalStyleIsolation: true
      }
    }); // 启动 qiankun
  } else {
    // 容器尚不存在,稍后重试
    setTimeout(ensureContainerAndStartMicroApps, 100); // 100毫秒后再次尝试
  }
}
// 确保 DOMContentLoaded 事件触发后再执行
document.addEventListener('DOMContentLoaded', ensureContainerAndStartMicroApps);
Vue.config.productionTip = false
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})
- 基座改造完成后,进行子应用的改造
在根目录下创建一个子应用,,子应用最好与在基座主应用main.js中配置的名称一致,这样可以直接使用package.json中的name作为output。
vue.config.js,devServer的端口改为与主应用配置的一致,且加上跨域headers和output配置。
配置子应用支持跨域
进行微应用打包成UMD库格式
设置vue.config.js中的publicPath,防止出现主应用引入子应用的时候出现样式,图片访问不到情况
 
新增src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
	  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
	}
src/router/index.js改为只暴露routes, new Router改到 main.js中声明,并改造main.js,并引入src下创建的public-path.js,改写render,添加生命周期函数,最终结果如下⬇,当前是子应用的时候根据主应用传递过来的baseName进行子应用路由的base及其mode的改造。
import './public-path';
import Vue from 'vue'
import Cookies from 'js-cookie'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';
import './assets/styles/element-variables.scss'
// import 'ant-design-vue/dist/antd.css';
import "ant-design-vue/dist/antd.less"
import '@/assets/styles/index.scss' // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css
import App from './App'
import store from './store'
import router, {constantRoutes} from './router'
import directive from './directive' // directive
import plugins from './plugins' // plugins
//引入hsk组件
import hskui from "hsk-ui"
import "hsk-ui/styles/hskui.css"
//引入hsk方法
import { hskMsgbox } from 'hsk-ui/commonUtils'
import { download } from '@/utils/request'
import './assets/icons' // icon
import './permission' // permission control
import { getDicts } from "@/api/system/dict/data";
import { getConfigKey } from "@/api/system/config";
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
// 分页组件
import Pagination from "@/components/Pagination";
// Vue.prototype.hskMsgbox = hskui.hskMsgbox.hskMsgbox
// 自定义表格工具组件
import RightToolbar from "@/components/RightToolbar"
// 富文本组件
import Editor from "@/components/Editor"
// 文件上传组件
import FileUpload from "@/components/FileUpload"
// 图片上传组件
import ImageUpload from "@/components/ImageUpload"
// 图片预览组件
import ImagePreview from "@/components/ImagePreview"
// 字典标签组件
import DictTag from '@/components/DictTag'
// 头部标签组件
import VueMeta from 'vue-meta'
// 字典数据组件
import DictData from '@/components/DictData'
// import action from '../src/action'
//局部使用antDesign-vue中的tree组件
import { Tree } from 'ant-design-vue';
import { Table } from 'ant-design-vue';
import { Icon } from 'ant-design-vue';
import Router from "vue-router";
// 全局方法挂载
Vue.prototype.hskMsgbox = hskMsgbox
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
Vue.prototype.parseTime = parseTime
Vue.prototype.resetForm = resetForm
Vue.prototype.addDateRange = addDateRange
Vue.prototype.selectDictLabel = selectDictLabel
Vue.prototype.selectDictLabels = selectDictLabels
Vue.prototype.download = download
Vue.prototype.handleTree = handleTree
// 全局组件挂载
Vue.component('DictTag', DictTag)
Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
Vue.component('Editor', Editor)
Vue.component('FileUpload', FileUpload)
Vue.component('ImageUpload', ImageUpload)
Vue.component('ImagePreview', ImagePreview)
Vue.component('ATree', Tree)
Vue.component('ATable', Table)
Vue.component('AIcon', Icon)
Vue.use(directive)
Vue.use(plugins)
Vue.use(VueMeta)
DictData.install()
/**
 * If you don't want to use mock-server
 * you want to use MockJs for mock api
 * you can execute: mockXHR()
 *
 * Currently MockJs will be used in the production environment,
 * please remove it before going online! ! !
 */
Vue.use(Element, {
  size: Cookies.get('size') || 'medium' // set element-ui default size
})
Vue.use(hskui)
let instance = null
Cookies.set("client_id","admin")
async function render(props={}){
  const { container } = props;
  instance = new Vue({
      router,
      store,
      render: h => h(App),
      beforeCreate(){
        if (window.__POWERED_BY_QIANKUN__) {
          store.state.user = props.sharedStore.state.user
        }
      }
    }).$mount(
      container
      ?
      container.querySelector('#app')  //渲染到主应用的入口
      :'#app' //独立运行的时候
    )
}
// 在被qiankun引用时 修改运行时的 `publicPath`
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
//如果独立运行的时候,判断是否是独立运行
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}
/**
 * 子应用建议使用qiankun的规则来接入不需要安装任何依赖,
 * 只需要再三个入口到二u三个必须的钩子函数给qiankun主应用使用
 * 钩子函数必须返回promise(启动的时候调用)
 */
export async function bootstrap() {
  // console.log('[vue] vue app bootstraped');
}
// 从生命周期 mount 中获取通信方法,props默认会有onGlobalStateChange和setGlobalState两个api
export async function mount(props) {
  // console.log('乾坤子应用容器加载完成,开始渲染 child',props)
  if (window.__POWERED_BY_QIANKUN__) {
    if(router.options.base !== props.baseName){
      const { container } = props;
      // 获取容器元素,用于后续操作或设置环境变量等
      let rootRoute = new Router({
        mode: 'history', // 去掉url中的#
        base: props.baseName,
        scrollBehavior: () => ({y: 0}),
        routes: constantRoutes
      })
      instance =  new Vue({
        router:rootRoute,
        store,
        render: h => h(App),
        beforeCreate(){
          if (window.__POWERED_BY_QIANKUN__) {
            store.state.user = props.sharedStore.state.user
          }
        }
      }).$mount(
        container
          ?
          container.querySelector('#app')  //渲染到主应用的入口
          :'#app' //独立运行的时候
      )
    } else {
      render(props);
    }
  } else {
    render(props);
  }
}
/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props) {
}
export async function unmount() {
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
}
export default instance;
上面完成进行基座的路由配置,使其主应用能够通过路由访问子应用。
{
    path: '/system-module-vue2/system',
    component: Layout,
    hidden: false,
    redirect: 'noredirect',
    meta: {
      title: "系统设置",
      noCache: false,
      link: null,
      icon: "system"
    },
    children: [
      {
        path: 'user',
        name: 'user',
        meta: { title: '用户管理', icon: 'user', "link": null }
      }, {
        name: "role",
        path: "role",
        meta: {
          "title": "角色管理",
          "icon": 'tree',
          "noCache": false,
          "link": null
        }
      }, {
        name: "codeManagement",
        path: "codeManagement",
        meta: {
          "title": "编码管理",
          "icon": 'tree',
          "noCache": false,
          "link": null
        }
      },
      {
        name: "log",
        path: "log",
        meta: {
          title: "操作日志",
          icon: 'log',
          link: null
        }
      },
      {
        name: "dictionaryMiddle",
        path: "dictionaryMiddle",
        meta: {
          title: "数据字典管理",
          icon: 'component',
          link: null
        }
      }, {
        path: 'configInformation',
        hidden: true,
        name: 'configInformation',
        meta: { title: '配置信息', icon: '', noCache: true }
      }, {
        name: "templateMiddle",
        path: "templateMiddle",
        meta: {
          title: "消息模板管理",
          icon: 'message',
          link: null
        }
      }, {
        name: "serverLog",
        path: "serverLog",
        meta: {
          title: "服务调用日志",
          icon: 'log',
          link: null
        }
      }, {
        name: "serverLogDetail",
        path: "serverLogDetail",
        hidden: true,
        meta: {
          title: "服务使用情况",
          icon: 'log',
          link: null
        }
      }
    ]
  },
效果:其实配置最难的地方就是路由的配置。
 
 主应用登陆后通过qiankun自带的props将token传递给子应用,子应用在qiankun的mount生命周期中设置token进行响应的判断设置。我当前主子应用用的都是ruoyi脚手架搭建的,我直接将store传递个子应用使用,不需要做太多的操作即可。
 
 配置动态路由:我当前是ruoyi脚手架搭建的后台项目,配置动态路由在permission.js中设置,和后端约定好后,调用接口,通过后端返回进行路由设置。
 
 动态菜单全部代码,目前是写死的,后期根据getRouters()方法向后端发送请求进行动态配置,注意:路由基本上前端进行配置,不然很容易出现404报错现象。
 下面是permission.js文件代码,其中当前后端还未有接口,目前先使用adminId代表不同系统显示不同路由
import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink'
import store from '../../store'
const permission = {
  state: {
    routes: [],
    addRoutes: [],
    defaultRoutes: [],
    topbarRouters: [],
    // sidebarRouters: []
    sidebarRouters: [],
    permissions: [],
  },
  mutations: {
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions
    },
    SET_ROUTES: (state, routes) => {
      state.addRoutes = routes
      state.routes = constantRoutes.concat(routes)
    },
    SET_DEFAULT_ROUTES: (state, routes) => {
      state.defaultRoutes = constantRoutes.concat(routes)
    },
    SET_TOPBAR_ROUTES: (state, routes) => {
      state.topbarRouters = routes
    },
    SET_SIDEBAR_ROUTERS: (state, routes) => {
      state.sidebarRouters = routes
    },
  },
  actions: {
    // 生成路由
    GenerateRoutes({ commit }) {
      return new Promise(resolve => {
        //   // 向后端请求路由数据
        // getRouters().then(res => {
        const res1 = {
          "msg": "操作成功",
          "code": 200,
          "data": [
            {
              "path": "/business-module-vue2/equipment",
              "redirect": "noRedirect",
              "component": "Layout",
              "meta": {
                "title": "检测设备管理",
                "noCache": false,
                "icon": 'zhgl',
                "link": null
              },
              "children": [
                {
                  "name": "equipment",
                  "path": "/business-module-vue2/equipment/equipment",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "检测设备管理",
                    "icon": 'yygl',
                    "noCache": false,
                    "link": null
                  }
                },
              ]
            }
          ]
        }
        const res2 = {
          "msg": "操作成功",
          "code": 200,
          "data": [
            {
              "path": "/business-module-vue2",
              "redirect": "noRedirect",
              "component": "Layout",
              "meta": {
                "title": "业务系统",
                "noCache": false,
                "icon": 'zhgl',
                "link": null
              },
              "children": [
                {
                  "name": "zhanghao",
                  "path": "/business-module-vue2/zhanghao",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "账号管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "tenant",
                  "path": "/business-module-vue2/tenant",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "企业管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "shenhe",
                  "path": "/business-module-vue2/shenhe",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "认证审核",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
              ]
            },
            {
              "path": "/business-module-vue2/productListA",
              "redirect": "noRedirect",
              "component": "Layout",
              "meta": {
                "title": "资源中心",
                "noCache": false,
                "icon": 'zhgl',
                "link": null
              },
              "children": [
                {
                  "name": "productList",
                  "path": "/business-module-vue2/productList",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "产品列表",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "resourceList",
                  "path": "/business-module-vue2/resourceList",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "资源列表",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "viewProductDetail",
                  "path": "/business-module-vue2/viewProductDetail",
                  "hidden": true,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "产品详情",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "productDetail",
                  "path": "/business-module-vue2/productDetail",
                  "hidden": true,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "产品详情",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
              ]
            },
            {
              "path": "/system-module-vue2/system",
              "redirect": "noRedirect",
              "component": "Layout",
              "meta": {
                "title": "系统设置",
                "noCache": false,
                "icon": 'zhgl',
                "link": null
              },
              "children": [
                {
                  "name": "user",
                  "path": "/system-module-vue2/system/user",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "用户管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "role",
                  "path": "/system-module-vue2/system/role",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "角色管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "codeManagement",
                  "path": "/system-module-vue2/system/codeManagement",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "编码管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "log",
                  "path": "/system-module-vue2/system/log",
                  "hidden": false, 
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "操作日志",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },{
                  "name": "dictionaryMiddle",
                  "path": "/system-module-vue2/system/dictionaryMiddle",
                  "hidden": false, 
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "数据字典管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },{
                  "name": "configInformation",
                  "path": "/system-module-vue2/system/configInformation",
                  "hidden": false, 
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "配置信息",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },{
                  "name": "templateMiddle",
                  "path": "/system-module-vue2/system/templateMiddle",
                  "hidden": false, 
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "消息模板管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
                {
                  "name": "serverLog",
                  "path": "/system-module-vue2/system/serverLog",
                  "hidden": false, 
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "服务调用日志",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },{
                  "name": "serverLogDetail",
                  "path": "/system-module-vue2/system/serverLogDetail",
                  "hidden": false, 
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "服务使用情况",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
              ]
            },{
              "path": "/business-module-vue2/gatewayAdministration",
              "redirect": "noRedirect",
              "component": "Layout",
              "meta": {
                "title": "物联网中心",
                "noCache": false,
                "icon": 'zhgl',
                "link": null
              },
              "children": [
                {
                  "name": "gatewayAdministration",
                  "path": "/business-module-vue2/gatewayAdministration",
                  "hidden": false,
                  "redirect": "noRedirect",
                  "meta": {
                    "title": "网关管理",
                    "icon": 'zhgl',
                    "noCache": false,
                    "link": null
                  }
                },
              ]
            },
          ]
        }
        console.log("store",store.getters.adminID)
        let res = {}
        if(localStorage.getItem('adminId') === '1'){
           res = res2
        }else{
           res = res1
        }
        //遍历菜单树,将菜单树下的所有按钮权限拿到,并使用v-permissions方法比对是否有按钮权限
        // commit('SET_PERMISSIONS', getAllPermissions(res.data,[])) 
        const sdata = JSON.parse(JSON.stringify(res.data))
        const rdata = JSON.parse(JSON.stringify(res.data))
        const sidebarRoutes = filterAsyncRouter(sdata)
        const rewriteRoutes = filterAsyncRouter(rdata, false, true)
        const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
        rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
        router.addRoutes(asyncRoutes);
        commit('SET_ROUTES', rewriteRoutes)
        commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
        commit('SET_DEFAULT_ROUTES', sidebarRoutes)
        commit('SET_TOPBAR_ROUTES', sidebarRoutes)
        resolve(rewriteRoutes)
        // })
      })
    }
  }
}
function getAllPermissions(tree, result) {
  //遍历树  获取id数组
  for (let i = 0; i < tree.length; i++) {
    if (tree[i].meta.permission !== null) {
      result.push(...tree[i].meta.permission)
    }
    if (typeof (tree[i].children) !== "undefined" && tree[i].children !== null && tree[i].children.length > 0) {
      getAllPermissions(tree[i].children, result);
    }
  }
  return result;
}
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
  return asyncRouterMap.filter(route => {
    if (type && route.children) {
      route.children = filterChildren(route.children)
    }
    if (route.component) {
      // Layout ParentView 组件特殊处理
      if (route.component === 'Layout') {
        route.component = Layout
      } else if (route.component === 'ParentView') {
        route.component = ParentView
      } else if (route.component === 'InnerLink') {
        route.component = InnerLink
      } else {
        route.component = loadView(route.component)
      }
    }
    if (route.children != null && route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children, route, type)
    } else {
      delete route['children']
      delete route['redirect']
    }
    return true
  })
}
function filterChildren(childrenMap, lastRouter = false) {
  var children = []
  childrenMap.forEach((el, index) => {
    if (el.children && el.children.length) {
      if (el.component === 'ParentView' && !lastRouter) {
        console.log("~~~~~~~~~",c)
        el.children.forEach(c => {
          c.path = el.path + '/' + c.path
          if (c.children && c.children.length) {
            children = children.concat(filterChildren(c.children, c))
            return
          }
          children.push(c)
        })
        return
      }
    }
    if (lastRouter) {
      el.path = lastRouter.path + '/' + el.path
    }
    children = children.concat(el)
  })
  return children
}
// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
  const res = []
  routes.forEach(route => {
    if (route.permissions) {
      if (auth.hasPermiOr(route.permissions)) {
        res.push(route)
      }
    } else if (route.roles) {
      if (auth.hasRoleOr(route.roles)) {
        res.push(route)
      }
    }
  })
  return res
}
export const loadView = (view) => {
  if (process.env.NODE_ENV === 'development') {
    return (resolve) => require([`@/views/${view}`], resolve)
  } else {
    // 使用 import 实现生产环境的路由懒加载
    return () => import(`@/views/${view}`)
  }
}
export default permission






















