import * as Vue from 'vue' // 路由 import router from './router' // vuex // import store from './store' import getStore from '@/store/piniaToVuex' const store = getStore() // utils import { checkIsHref } from '@/utils' import { getToken, getItToken, getQuery, setToken, removeCookie } from '@/utils/auth' // getToken from cookie // api import { autoLogin } from '@/api/login' // 全局配置 import config from '@/config' // 组件 import Layout from '@/views/layout/Layout.vue' import { FormRender } from '@gushen/gushen-form-render' // 乾坤微前端 import { action, microApps } from '@/qiankun' declare const window: any const systemCode = config.systemCode const whiteList = config.whiteList let remoteRouters = [] const VUE_SUBPATH = window.VUE_SUBPATH ?? '/' // 不需要清空menuId的页面 const noRemoveMenuId = ['third-view'] // 浏览器回车刷新,清空菜单数据,然后请求一份最新的一份数据。 localStorage.removeItem('menuList') // step-1 获取qiankun子应用activeRule规则 const activeRule = microApps .map((app) => app.activeRule) .reduce((acc, rules) => acc.concat(rules), []) router.beforeEach(async (to, _from, next) => { // 获取iframe嵌套谷神第三方token fromThreeSystemToken() // 先清空menuId。不清的话有问题, 例如首页,menuId还是取上个页面的menuId // 主要是登录也需要清空的数据 clearCookieData(to) if (!noRemoveMenuId.includes(to.name?.toString())) { sessionStorage.setItem('menuId', '') } window.sessionStorage.removeItem('requestAllData') // 对空路由进行跳转重定向 if (to.fullPath === '/') next({ path: '/home', replace: true }) window.sessionStorage.setItem('pageAllData', JSON.stringify({ [window.location.pathname]: {} })) let token = getToken() || getItToken() // 从url上获取对应值 const otherSysToGushenToken = getQuery('otherSysToGushenToken') // 判断url上是否有token需要存在谷神中 if (otherSysToGushenToken) { token = otherSysToGushenToken setToken(otherSysToGushenToken, otherSysToGushenToken) } // 获取系统配置 const { systemSetting } = store.state.app // 有token的非白名单页面 if (!!token && whiteList.indexOf(to.path) === -1) { const { userInfo, pwdInfo } = store.state.user // 如果Vuex中没有用户信息重新获取 if (!userInfo) { await store.dispatch('GetUserInfo') } // 如果Vuex中没有系统配置重新获取 if (!systemSetting) { store.dispatch('SetSystemSetting') } // 如果Vuex中没有租户密码策略提示信息 if (!pwdInfo) { await store.dispatch('GetPwdInfo') } // 请求菜单。 await fetchMenus() let menuId = to.query.parentMenuId || '' let menuCode = '' let routeInfo: object = {} const checkItemIn = function (routes, path) { routes.forEach((route) => { // getRealDynamicFormPath 获取真实的动态表单路由 if (path === getRealDynamicFormPath(route) || route.path === path) { menuId = route.meta.menuId || (route.query ? route.query.parentMenuId : '') menuCode = route.meta.menuCode routeInfo = route } else if (route.children) { checkItemIn(route.children, path) } }) } checkItemIn(store.state.permission.routers, to.path) routeInfo = menuId ? routeInfo : to try { sessionStorage['routeInfo'] = JSON.stringify(routeInfo) } catch (err) { const newObject = { ...routeInfo, matched: null, } sessionStorage['routeInfo'] = JSON.stringify(newObject) console.error(err) } if (menuId) { sessionStorage['menuId'] = menuId await store.dispatch('setActiveMenuId', { menuId }) await store.dispatch('setAuthBTN', { menuId, menuCode }) await store.dispatch('getCollectStatus', { menuId, query: to.query }) action.setGlobalState({ authButtonList: JSON.stringify(store.state.app.btnAuth), }) } else { if (!sessionStorage.getItem('menuId') && !noRemoveMenuId.includes(to?.name?.toString())) { await store.dispatch('setActiveMenuId', { menuId: null }) } } if (to.path === '/login') { next({ // path: to.query.redirect, query: { listData: to.query.listData || null, routerFromName: to.query.routerFromName || null, }, }) } // 判断是否获取完remoteRouters路由信息 else if (!remoteRouters.length && to.path !== '/portal') { const menuList = JSON.parse(localStorage.getItem('menuList')) remoteRouters = formatRoutes(menuList || []) // step-3 添加子应用通配路由 activeRule.forEach((item) => { const name = item.replace('/', '') router.addRoute('root', { name, path: `/${name}:pathMatch(.*)*`, component: Vue.defineAsyncComponent(() => import('@/views/layout/MiddleView.vue')), }) }) store.commit('SET_ROUTERS', { remoteRouters }) // 在获取完角标key后请求角标数量 store.dispatch('getMenuCornersNumData') // fix 如果菜单列表为空,直接进入,防止一直刷新请求接口 if (!menuList?.length) { next() } else { next({ ...to, replace: true }) } } else { next() } } else if (whiteList.indexOf(to.path) !== -1) { // 如果Vuex中没有系统配置重新获取 if (!systemSetting) { store.dispatch('SetSystemSetting') } //清空用户信息和用户密码规则信息 store.commit('SET_USER_INFO', undefined) store.commit('SET_PWD_INFO', undefined) /* has no token*/ // 在免登录白名单,直接进入 next() } else { // 如果没有token又不在登录白名单中的话 清除用户信息和水印 localStorage.removeItem('watermarkData') localStorage.removeItem('userInfo') const res: any = await autoLogin(systemCode) // sso登录处理 if (res.portalType === 'sso') { localStorage.setItem('urlLogout', res.urlLogout) localStorage.setItem('urlLogin', res.urlLogin) localStorage.setItem('portalType', res.portalType) localStorage.setItem('portalCode', res.portalCode) // 重定向到sso登录页面 window.location.href = res.urlLogin return } else { window.location.href = `${VUE_SUBPATH}login` } } }) // 请求菜单信息 async function fetchMenus() { // fix 判断下是否请求了,请求过了就不再请求 if (!remoteRouters.length) { const { data } = await autoLogin(systemCode) // TODO 暂时排除不需要显示的菜单 // const excludeUrl = ['dynamic-report', 'rule-engine'] const excludeUrl = ['rule-engine'] const menus = data.menus.filter((menu) => !excludeUrl.includes(menu.menuUrl)) localStorage.setItem('menuList', JSON.stringify(menus)) // 保存默认菜单信息 window.sessionStorage.setItem('defaultMenu', JSON.stringify(data.defaultMenuUrl)) } } /** * 多级菜单递归算法 * @param {*} data 菜单数据 * @param {*} first 是否为第一个路由 * @param {*} parentPath 父级路径 */ function formatRoutes(data, first = true, parentPath = '') { return data.map((item) => { //是否有子路由 const hasChild = !!item.children && item.children.length !== 0 // 是否为 http 链接 const isHttpUrl = checkIsHref(item.menuUrl) // 路由名称 const name = isHttpUrl ? item.menuCode : item.menuUrl // path const path = generatePath(item, isHttpUrl, parentPath) let component = null if (!hasChild) { // originalAddr 为固定加载路径 const componentPath = item.originalAddr || path component = Vue.defineAsyncComponent(() => import(`@/views${componentPath}/index.vue`)) } else { component = Vue.defineAsyncComponent(() => import(`@/views/layout/MiddleView.vue`)) } // if (first && hasChild) { // component = Vue.defineAsyncComponent(() => import(`@/views/layout/Layout.vue`)) // } let route = { name, path, meta: generateMetaInfo(item), component, menuName: item.menuName || '', menuEnName: item.menuEnName || item.menuName || '', children: (() => { if (hasChild) return formatRoutes(item.children, false, path) })(), } // 只有一级菜单 if (first && !hasChild) { route = handleFirstRoute(item, route) } // 如果menuUrl是完整http地址则通过iframe打开 if (isHttpUrl) { route = handleHttpRoute(item, route) router.addRoute('root', route) } // 如果是动态表单需要渲染固定组件 route = handleDynamicFormRoute(item, route) // step-2 匹配子应用路由 const app = activeRule.some((path) => route.path.startsWith(path)) if (!app) { route.meta['isBootstrap'] = true // 只有一级菜单 if (!hasChild) { router.addRoute('root', route) } } return route }) } // 生成 meta 信息 function generateMetaInfo(item) { // 存储角标的key if (item.key) { const arrayList = [...store.state.app.cornerKeys, item.key] store.commit('SET_CORNERKEYS', [...new Set(arrayList)]) } return { icon: item.menuIconurl || 'example', noCache: !item.isCache, title: item.menuName ? item.menuName.replace('[PC]', '') : '', enTitle: item.menuEnName || item.menuName, params: item.parameter, //这些菜单都是主要的路由菜单,都可以从菜单列表进入。 isMainRouteMenu: true, menuCode: item.menuCode, menuId: item.menuId, isFetch: item.isFetch, fetchPath: item.fetchPath, subscriptKey: item.subscriptKey || null, subscriptStatus: item.subscriptStatus || null, key: item.key || null, userManualUrl: item.userManualUrl, } } // 请求菜单角标数据 // 处理 path 路径 function generatePath(item, isHttpUrl, parentPath) { let path = `${parentPath}/${isHttpUrl ? item.menuCode : item.menuUrl}` // 菜单URL设置中/开头的,忽略父级URL if (path && path.indexOf('//') > -1 && !item.originalAddr) { const pathArr = path.split('//') path = '/' + pathArr[1] } return path } // 处理一级菜单路由 function handleFirstRoute(item, route) { return { ...route, icon: item.menuIconurl, noChild: true, } } // 处理 http 链接路由 function handleHttpRoute(item, route) { return { ...route, path: `/third-view/${item.menuId}`, name: 'third-view', meta: { ...route.meta, iframe: true, isBootstrap: true, url: item.menuUrl, menuId: item.menuId, }, component: Vue.defineAsyncComponent(() => import('@/views/third-view.vue')), } } // 处理 动态表单 路由 function handleDynamicFormRoute(item, route) { const params = item.parameter && item.parameter if (params && params.length > 10 && (params.indexOf('{') > -1 || params.indexOf('}') > -1)) { try { const paramJson = JSON.parse(params) // 如果是动态表单需要渲染固定组件 if (paramJson && paramJson.menu_type === 'DYNAMIC_FORM') { route.meta.isBootstrap = true return { ...route, path: `${route.path}/:formId`, // component: () => import('@/views/dynamic-form/index.vue') component: { ...FormRender }, } } else { return route } } catch (e) { console.log(new Error(e)) } } else { return route } } // 获取真实的动态表单路由,增加一个动态表单浏览器刷新,按钮权限获取失败 daxiong function getRealDynamicFormPath(route) { let isDynamicForm = '' if (route.path.includes(':formId')) { const params = (route.meta.params && JSON.parse(route.meta.params)) || {} const path = JSON.parse(JSON.stringify(route.path)).replace(':formId', '') isDynamicForm = path + params['diy_code'] } return isDynamicForm } // 获取第三方系统的token function fromThreeSystemToken() { try { const arr = window.location.search.split('?')[1] if (!arr) return const obj: any = changeUrl(arr) if (obj.token) { setToken(obj.token, obj.token) } } catch {} function changeUrl(arr) { const parr = arr.split('&') const obj = {} for (const i of parr) { const arr = i.split('=') obj[arr[0]] = arr[1] } return obj } } // 需要清除的数据 function clearCookieData(to) { if (to.path === '/login') { removeCookie('VUE_GS_CHATGPT') } }