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

news2024/11/20 1:39:54

实战指南: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.coloradmin.cn/o/1709894.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为机考入门python3--(32)牛客32-密码截取

分类&#xff1a;最长对称子串、动态规划 知识点&#xff1a; 生成二维数组 dp [[0] * n for _ in range(n)] 求最大值 max(value1, value2) 动态规划的步骤 a. 定义问题 长度为n下最长的对称子串的长度 b. 确定状态 dp[i][j]表示字符串从索引i到j的子串是否为对称…

2024.5.28晚训题解

提前预告&#xff0c;市赛初中组会考算法题&#xff0c;应该会有两道模板题 比如DFS BFS 二分 简单动态规划&#xff0c;虽然我们没学多久&#xff0c;但是模板题你还是要会写的 A题 编辑距离 动态规划 注意多组输入 #include<iostream> using namespace std; int dp[1…

unity3D获取某天的0点和23点59分59秒

系列文章目录 unity工具 文章目录 系列文章目录unity工具 &#x1f449;一、前言&#x1f449;二、获取某一天的0点和23点59分59秒1-1.代码如下1-2.调用方法如下1-2-1.获取当天的时间1-2-2.获取某一天的时间 &#x1f449;三、当月第一天0时0分0秒&#x1f449;四、当月最后一…

SHELL编程(三)网络基础命令 Makefile

目标 一、网络基础及相关命令&#xff08;一&#xff09;网络相关命令&#xff08;二&#xff09;重启网络服务 二、Makefile&#xff08;一&#xff09;标签式语法&#xff08;二&#xff09;目标:依赖 式语法1. 格式2. 编译流程&#xff1a;预处理 编译 汇编 链接3. 目标和伪…

TiDB-从0到1-体系结构

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务 一、TiDB体系结构图 TiDB基础的体系架构中有4大组件 TiDB Server&#xff1a;用于处理客户端的请求PD&#xff1a;体系的大脑&#xff0c;存储元数据信息TiKV&#xff1a;存储数据TiFlash…

Stable Diffusion 模型演进:LDM、SD 1.0, 1.5, 2.0、SDXL、SDXL-Turbo 等

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

Vue3+Ant design 实现Select下拉框一键全选/清空

最近在做后台管理系统项目的时候&#xff0c;产品增加了一个让人非常苦恼的需求&#xff0c;让在Select选择器中添加一键全选和清空的功能&#xff0c;刚开始听到的时候真是很懵&#xff0c;他又不让在外部增加按钮&#xff0c;其实如果说在外部增加按钮实现全选或者清空的话&a…

触摸屏是输入设备还是输出设备?

从功能上讲&#xff0c;触摸屏理应属于输入设备&#xff0c;之所以有很多用户会误会它是输出设备&#xff0c;是因为将其与“触摸显示屏”搞混了&#xff0c;以手机屏幕为例&#xff0c;它并不是单层屏幕&#xff0c;而是有多个不同功能和作用组成的集成屏&#xff0c;这类带有…

ubuntu-24.04系统静态Mac和IP配置

操作系统版本&#xff08;桌面版&#xff09;&#xff1a;ubuntu-24.04-desktop-amd64.iso 原因说明&#xff1a;因网络的IP地址和Mac是预分配的&#xff0c;所以ubuntu系统需要修改网卡的mac地址和IP才能访问&#xff0c;网络查了半天资料都没成功&#xff0c;后再界面提示&a…

【Python】 Python中的“命名元组”:简单而强大的数据结构

基本原理 在Python中&#xff0c;namedtuple是tuple的一个子类&#xff0c;它允许我们为元组的每个位置指定一个名字。这种数据结构非常适合用于需要固定字段和值的场景&#xff0c;例如数据库查询的结果或配置文件中的设置。 namedtuple提供了一种方便的方式来访问元组中的元…

力扣2028. 找出缺失的观测数据

题目&#xff1a; 现有一份 n m 次投掷单个 六面 骰子的观测数据&#xff0c;骰子的每个面从 1 到 6 编号。观测数据中缺失了 n 份&#xff0c;你手上只拿到剩余 m 次投掷的数据。幸好你有之前计算过的这 n m 次投掷数据的 平均值 。 给你一个长度为 m 的整数数组 rolls &a…

防止浏览器缓存了静态的配置等文件(例如外部的config.js 等文件)

防止浏览器缓存了静态的配置文件 前言1、在script引入的时候添加随机数1.1、引入js文件1.2、引入css文件2、通过html文件的<meta>设置防止缓存3、使用HTTP响应头:前言 在实际开发中浏览器的缓存问题一直是一个很让人头疼的问题,尤其是我们打包时候防止的静态配置文件c…

在 PhpStorm 中自定义代码片段

在 PhpStorm 中自定义代码片段的步骤如下: 打开 PhpStorm,进入 “File” > “Settings” > “Editor” > “Live Templates”。 在右侧面板中,点击 “” 号,选择 “Live Template”。 在弹出的窗口中: Abbreviation: 输入您想要自动补全的缩写,比如 “de”Template …

【强训笔记】day24

NO.1 思路&#xff1a;递归。 代码实现&#xff1a; class Solution { public:bool IsBalanced_Solution(TreeNode* pRoot) {return dfs(pRoot)!-1;}int dfs(TreeNode* root){if(rootnullptr) return 0;int leftdfs(root->left);if(left-1) return -1;int rightdfs(root-…

深度揭秘:蓝海创意云渲染农场的五大特色功能

在当今数字化时代&#xff0c;影视制作、效果图设计等领域对于高质量的渲染需求日益增长。在这个背景下&#xff0c;云渲染平台成为了行业中不可或缺的一部分&#xff0c;它为用户提供了高效、灵活的渲染解决方案。蓝海创意云渲染农场https://www.vsochina.com/cn/render蓝海创…

WWW24因果论文(1/8) | 利用强化学习(智能体)进行因果问答

【摘要】因果问题询问不同事件或现象之间的因果关系。它们对于各种用例都很重要&#xff0c;包括虚拟助手和搜索引擎。然而&#xff0c;许多当前的因果问答方法无法为其答案提供解释或证据。因此&#xff0c;在本文中&#xff0c;我们旨在使用因果关系图来回答因果问题&#xf…

昂科烧录器支持Infineon英飞凌的磁性位置传感器TLE4998S8D

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中Infineon英飞凌的磁性位置传感器TLE4998S8D已经被昂科的通用烧录平台AP8000所支持。 TLE4998S8D是一款磁性位置传感器&#xff0c;经过专门设计&#xff0c;满足高精度角度和位…

实施阶段(2024年5月)

【项目活动1】斐波拉契数列第n项的值&#xff1f; 数学思想&#xff1a;第一项和第二项的值都为1&#xff0c;从第三项开始值为前两项的和。 方法一&#xff1a;迭代 迭代变量&#xff1a;f1和f2 迭代表达式&#xff1a;f1,f2f2,f1f2 计数器&#xff1a;i 迭代表达式运算…

webpack打包配置项

webpack打包配置项 在config.js 中 module.exports {publicPath: process.env.NODE_ENV production ? / : /, //静态资源目录outputDir: dist, //打包名称assetsDir: static,//静态资源&#xff0c;目录devServer: {port: port,open: false,overlay: {warnings: false,erro…

解决Plugin ‘maven-clean-plugin:3.1.0‘ not found的问题

1. 问题描述 当导入别人的Maven项目时&#xff0c;可能会出现Plugin maven-clean-plugin:3.1.0 not found的错误信息。 2. 解决方案 2.1 方案一 检查自己的Maven仓库地址是否正确&#xff0c;一般引入其他人的项目时&#xff0c;Maven仓库的目录以及配置都会是别人的&#xff…