实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享

实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript子应用+vue2微前端架构实现动态菜单与登录共享

导读:

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

效果:

qiankun基座实现动态菜单
实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享

引言

当前项目架构,vue+ruoyi+elementUI作为基座,vue3+recoDesign+vite+ts作为子应用,基座只用于登录鉴权,动态菜单功能,尽量少在基座写其他业务,子应用分为业务子应用,系统子应用,其他业务子应用,结合npm私服组件库进行组件抽取,供各个子应用使用,基座登录后,将token及其其他子应用需要的参数通过props进行传递比如最直接的【按钮权限,token】。

技术栈介绍

  1. vue2全家桶+ruoyi脚手架进行基座改造。
  2. vue3全家桶+arcoDesign中台后台脚手架进行子应用改造。

vue2子应用改造,vue3子应用改造
http://t.csdnimg.cn/U7a3p,vue2+qiankun项目实战
http://t.csdnimg.cn/4UFDs,vue3+qiankun项目实战

  1. npm私服组件库的打包上传拉取使用:http://t.csdnimg.cn/5Tgax。

创建vue2基座

  1. 安装qiankun
    npm i qiankun -S
    
  2. 设置需要将子应用的页面嵌入到主应用的某个div中(子应用在主应用上的渲染出口)<div id="subapp-container">
    实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享
  3. 在主应用(基座)中注册子应用
    实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享
    整体代码:
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)
})

  1. 基座改造完成后,进行子应用的改造

在根目录下创建一个子应用,,子应用最好与在基座主应用main.js中配置的名称一致,这样可以直接使用package.json中的name作为output。
vue.config.js,devServer的端口改为与主应用配置的一致,且加上跨域headers和output配置。
实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享

配置子应用支持跨域
实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享
进行微应用打包成UMD库格式
实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享

设置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改为只暴露routesnew 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
        }
      }
    ]
  },

效果:其实配置最难的地方就是路由的配置。
实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享
主应用登陆后通过qiankun自带的propstoken传递给子应用,子应用在qiankunmount生命周期中设置token进行响应的判断设置。我当前主子应用用的都是ruoyi脚手架搭建的,我直接将store传递个子应用使用,不需要做太多的操作即可。实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享
在这里插入图片描述
配置动态路由:我当前是ruoyi脚手架搭建的后台项目,配置动态路由在permission.js中设置,和后端约定好后,调用接口,通过后端返回进行路由设置。
实战指南:qiankun前端架构实现动态菜单与登录共享
动态菜单全部代码,目前是写死的,后期根据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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/653701.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【IC】partial good

假设单core良率80%&#xff0c;core pass 数量分布呈二项分布。 16个core全pass的概率为&#xff1a; 有n个core pass的概率为&#xff1a; 分布如下&#xff1a; 当np>5且nq>5时&#xff0c;二项分布近似服从正态分布

Python魔法之旅-魔法方法(01)

目录 一、概述 1、定义 2、作用 二、主要应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类…

Postman入门 - 环境变量和全局变量

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、发送请求 二、设置并引用环境变量 比如&#xff1a;我建的这个生产环境 使用环境有两个方式&…

怎么做图片海报二维码?扫码查看图片内容

现在很多的宣传推广海报会放入二维码中&#xff0c;然后将二维码分享给用户后&#xff0c;通过扫码的方式来查看图片内容&#xff0c;从而获取自己需要的信息&#xff0c;经常在活动宣传、商品推广、旅游攻略等场景下使用。二维码可以提供更加便捷的内容获取方式&#xff0c;让…

Vivado打开之前项目仿真过的波形文件

第一步&#xff1a;顶部菜单 点击&#xff1a;Open Static Simulation 然后在弹出的窗口找到.sim结尾的文件夹&#xff0c;在里面找到wdb结尾的文件&#xff0c;点击ok 第二步&#xff1a;依次点击下方红圈 找到wcfg结尾的文件&#xff0c;点击ok即可

echart图表legend每列固定宽度

修改前&#xff1a; 修改后&#xff1a; 关键代码&#xff1a; 设置一个背景并使之透明&#xff0c;否则宽度不生效&#xff0c;配合formatter使用 formatter: {a|{name}},rich:{a: {width: 48,fontSize: 12,backgroundColor: "rgba(11, 39, 52, 0)" // 关键代码&a…

多张图片上传、图片回显、url路径转成File文件

1. 实现 背景&#xff1a;在表单中使用element-plus实现多张图片上传(限制最多10张)&#xff0c;因为还要与其他参数一起上传&#xff0c;所以使用formData格式。 编辑表单回显时得到的是图片路径数组&#xff0c;上传的格式是File&#xff0c;所以要进行一次转换。 <tem…

Pytorch环境配置2.0.1+ Cuda11.7

查找cuda、cudnn、Pytorch(GPU)及cuda和NVIDIA显卡驱动对应关系 查询可支持的最高cuda版本 nvidia-smi查看支持的cuda的版本 CUDA版本对应表 我的显卡驱动是Driver Version&#xff1a;535.40.&#xff0c;那么左边对应的CUDA都可以兼容 右上角为CUDA 版本&#xff0c;可以看…

gitLab 使用tortoiseGit 克隆新项目 一直提示tortoiseGitPlink输入密码 输完也不生效

问题描述&#xff1a;准备用TortoiseGit拉取gitlab上一个新项目代码&#xff0c;出现tortoiseGitPlink提示让输入密码&#xff0c;输入后又弹出&#xff0c;反复几次&#xff0c;无法down下来代码。 解决方案&#xff1a; 1.找到PuTTYgen工具&#xff0c;打开 2. 点击load 按钮…

基于 Milvus Cloud + LlamaIndex 实现初级 RAG

初级 RAG 初级 RAG 的定义 初级 RAG 研究范式代表了最早的方法论,在 ChatGPT 广泛采用后不久就取得了重要地位。初级 RAG 遵循传统的流程,包括索引创建(Indexing)、检索(Retrieval)和生成(Generation),常常被描绘成一个“检索—读取”框架,其工作流包括三个关键步…

CSS学习笔记:Less

什么是Less&#xff1f; Less是一个CSS预处理器&#xff0c; Less文件后缀是.less 扩充了CSS 语言&#xff0c;使CSS具备一定的逻辑性、计算能力 可以通俗地理解&#xff1a;Less是一种更好用的CSS 注释 运算 嵌套 Less嵌套的作用&#xff1a;快速生成后代选择器 变量 问…

开源远程协助:分享屏幕,隔空协助!

&#x1f5a5;️ 星控远程协助系统 &#x1f5b1;️ 一个使用Java GUI技术实现的远程控制软件&#xff0c;你现在就可以远程查看和控制你的伙伴的桌面&#xff0c;接受星星的指引吧&#xff01; 支持系统&#xff1a;Windows / Mac / Linux &#x1f31f; 功能导览 &#x1f…

解密Prompt系列15. LLM Agent之数据库应用设计:DIN C3 SQL-Palm BIRD

上一章我们主要讲搜索引擎和LLM的应用设计&#xff0c;这一章我们来唠唠大模型和DB数据库之间的交互方案。有很多数据平台已经接入&#xff0c;可以先去玩玩再来看下面的实现方案&#xff0c;推荐 [sql translate]&#xff1a;简单&#xff0c;文本到SQL&#xff0c;SQL到文本…

AI架构设计7:TGI

这个专栏主要关注围绕着AI运用于实际的业务场景所需的系统架构设计。整体基于云原生技术&#xff0c;结合开源领域的LLMOps或者MLOps技术&#xff0c;充分运用低代码构建高性能、高效率和敏捷响应的AI中台。该专栏需要具备一定的计算机基础。 若在某个环节出现卡点&#xff0c;…

(2023|EMNLP,RWKV,Transformer,RNN,AFT,时间依赖 Softmax,线性复杂度)

RWKV: Reinventing RNNs for the Transformer Era 公众号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 背景 2.1 循环神经网络 (RNN) 2.2 Transformer 和 AFT 3. RWKV 3.1 架构 …

零拷贝(Zero Copy)

目录 零拷贝&#xff08;Zero Copy&#xff09; 1.什么是Zero Copy? 2.物理内存和虚拟内存 3.内核空间和用户空间 4.Linux的I/O读写方式 4.1 I/O中断原理 4.2 DMA传输原理 5.传统I/O方式 5.1传统读操作 5.2传统写操作 6.零拷贝 6.1.用户态直接IO 6.2.mmapwrite …

The First项目报告:解读去中心化衍生品交易所AVEO

2023 年12月8日凌晨&#xff0c;Solana 生态 MEV 基础设施开发商 Jito Labs 开放了 JTO 空投申领窗口&#xff0c;JTO 的价格在开盘短暂震荡后迅速攀高&#xff0c;一度触及 4.94 美元。 JTO 是加密社区这两日关注的热门标的&#xff0c;而在这场讨论中&#xff0c;除 Solana …

unity接入live2d

在bilibili上找到一个教程&#xff0c;首先注意一点&#xff0c;你直接导入那个sdk&#xff0c;并且打开示例&#xff0c;显示的模型是有问题的&#xff0c;你需要调整模型上脚本的一个枚举值&#xff0c;调整它的渲染顺序是front z to我看教程时候&#xff0c;很多老师都没有提…

python max_min标准化

python max_min标准化 max_min标准化sklearn实现max_min标准化手动实现max_min标准化 max_min标准化 Max-Min标准化&#xff08;也称为归一化或Min-Max Scaling&#xff09;是一种将数据缩放到特定范围&#xff08;通常是0到1&#xff09;的标准化方法。这种方法通过线性变换将…

【软考】下篇 第14章 云原生架构设计与理论实践

目录 一、云原生架构定义二、云原生架构原则三、云原生架构主要架构模式3.1 服务化架构模式3.2 Mesh化架构模式3.3 Serverless模式3.4 存储计算分离模式3.5 分布式事务模式4.6 可观测架构3.7 事件驱动架构 四、云原生架构反模式五、云原生架构技术5.1 容器技术容器编排K8S 5.2 …