feat: plugin system

This commit is contained in:
杨梦洁
2024-11-14 19:09:46 +08:00
parent 9ba1537f18
commit a1dcdd49fb
56 changed files with 785 additions and 1987 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"cSpell.words": [
"Antd",
"apinto",
"Apipark",
"logsettings",
"resourcesettings"
]
}
-17
View File
@@ -1,17 +0,0 @@
# 部署
## 代码同步
packages目录下,部分子项目为企业版独有,不要同步到开源版:
packages/businessEntry, packages/openApi, packages/systemRunning, README.pro.md
## 安装依赖
建议使用pnpm
`npm install -g pnpm`
使用pnpm安装依赖
`pnpm install`
## 编译
### 开源版本
`pnpm run build`
### 企业版本
`pnpm run build:pro`
-2
View File
@@ -9,11 +9,9 @@
"scripts": {
"test": "jest",
"build": "set NODE_OPTIONS=--max-old-space-size=4096 && lerna run build --scope=core --stream --verbose ",
"build:pro": "set NODE_OPTIONS=--max-old-space-size=4096 && lerna run build --scope=business-entry --stream --verbose ",
"serve": "lerna run preview --parallel",
"serve:remotes": "lerna run serve --scope=remote --parallel",
"dev": "lerna run dev --scope=core --stream",
"dev:pro": "lerna run dev --scope=business-entry --stream",
"stop": "kill-port --port 5000",
"scan": "i18next-scanner --config i18next-scanner.config.js"
},
-5
View File
@@ -1,5 +0,0 @@
// .env.pro
VITE_APP_MODE=pro
VITE_APP_TITLE=My Production App
VITE_API_BASE_URL=https://api.production.example.com
@@ -1,18 +0,0 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs','public','code-snippet','ace-editor'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
@@ -1,25 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
public/tinymce
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
-11
View File
@@ -1,11 +0,0 @@
# `businessEntry`
> TODO: description
## Usage
```
const businessEntry = require('businessEntry');
// TODO: DEMONSTRATE API
```
@@ -1,7 +0,0 @@
'use strict';
const businessEntry = require('..');
const assert = require('assert').strict;
assert.strictEqual(businessEntry(), 'Hello from businessEntry');
console.info('businessEntry tests passed');
@@ -1,15 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/frontend/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>2APIPark</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script src="/frontend/iconpark_eolink.js"></script>
<script src="/frontend/iconpark_apinto.js"></script>
</body>
</html>
@@ -1,17 +0,0 @@
{
"name": "business-entry",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": " vite --port 5000 --strictPort",
"build": "vite build ",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview --port 5000 --strictPort",
"serve": "vite preview --port 5000 --strictPort"
},
"dependencies": {
},
"devDependencies": {
}
}
@@ -1,9 +0,0 @@
export default {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {}
},
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

-158
View File
@@ -1,158 +0,0 @@
import '@core/App.css'
import { ConfigProvider } from 'antd';
import RenderRoutes from '@businessEntry/components/aoplatform/RenderRoutes';
import {BreadcrumbProvider} from "@common/contexts/BreadcrumbContext.tsx";
import { StyleProvider } from '@ant-design/cssinjs';
import zhCN from 'antd/locale/zh_CN';
import useInitializeMonaco from "@common/hooks/useInitializeMonaco";
import ThemeSwitcher from '@common/components/aoplatform/ThemeSwitcher'
const antdComponentThemeToken = {
token: {
// Seed Token,影响范围大
colorPrimary: '#3D46F2',
colorLink:'#3D46F2',
colorBorder:'#ededed',
colorText:'#333',
borderRadius: 4,
// 派生变量,影响范围小
colorBgContainer: '#fff',
colorPrimaryBg:'#EBEEF2',
colorTextQuaternary:'#BBB',
colorTextTertiary:'#999'
},
components:{
// 派生变量,影响范围小
Input:{
activeShadow:'none'
},
Select:{
activeShadow:'none'
},
Checkbox:{
activeShadow:'none'
},
Cascader:{
activeShadow:'none',
optionSelectedBg:'#EBEEF2',
optionHoverBg:'#EBEEF2'
},
Layout: {
bodyBg: '#17163E',
headerBg: 'transparent',
headerColor: '#333',
headerPadding: '10 20px',
lightSiderBg: 'transparent',
siderBg: 'transparent',
},
Breadcrumb:{
itemColor:'#666',
linkColor:'#666',
lastItemColor:'#333',
},
Table:{
headerBorderRadius:0,
headerSplitColor:'#ededed',
borderColor:'#ededed',
cellPaddingBlockMD:'10px',
cellPaddingInlineMD:'12px',
cellPaddingBlockSM:'8px',
cellPaddingInlineSM:'12px',
headerFilterHoverBg:'#EBEEF2',
headerSortActiveBg:'#F7F8FA',
headerSortHoverBg:'#F7F8FA',
fixedHeaderSortActiveBg:'#F7F8FA',
headerBg:'#F7F8FA',
rowHoverBg:'#EBEEF2'
},
Segmented:{
itemColor:'#333',
itemSelectedColor:'#333',
trackBg:'#f7f8fa',
trackPadding:0,
// itemHoverColor:'#EBEEF2',
itemActiveBg:'#EBEEF2',
itemHoverBg:'#EBEEF2',
itemSelectedBg:'#EBEEF2',
},
Tree:{
// titleHeight:30,
// fontSize:12,
directoryNodeSelectedBg:'#EBEEF2',
directoryNodeSelectedColor:'#333',
nodeSelectedBg:'#EBEEF2',
nodeHoverBg:'#EBEEF2'
},
Collapse:{
headerBg:'#f7f8fa',
headerPadding:"12px",
contentPadding:"0 10px 12px 10px"
},
Button:{
// paddingInline:8,
dangerShadow:'none',
defaultShadow:'none',
primaryShadow:'none'
},
Tabs:{
cardBg:'#EBEEF2',
cardHeight:42,
horizontalItemGutter:8,
horizontalItemPaddingSM:'12px 8px 8px 8px',
horizontalItemPadding:'12px 8px 8px 8px',
},
Menu:{
// itemBg:'#F7F8FA',
// subMenuItemBg:'#F7F8FA',
// itemMarginBlock:0,
// activeBarBorderWidth:0,
// itemSelectedColor:'#333',
// itemSelectedBg:'#EBEEF2',
// itemHoverBg:'#EBEEF2'
// itemHeight:'72px',
// darkItemBg:'transparent',
// itemBg:'transparent',
// itemSelectedBg:'transparent',
// darkItemSelectedBg:'transparent',
// subMenuItemBg:'transparent',
// itemActiveBg:'transparent',
// darkSubMenuItemBg:'transparent',
// activeBarHeight:'2px',
// activeBarBorderWidth:2
},
List:{
itemPadding:'8px 0'
},
Form:{
itemMarginBottom:10,
},
Alert:{
defaultPadding:'12px 16px'
},
Tag:{
defaultBg:"#f7f8fa"
},
}
}
function App() {
useInitializeMonaco()
return (
<StyleProvider hashPriority={"high"}>
<ConfigProvider
locale={zhCN}
wave={{disabled:true}}
theme={antdComponentThemeToken}>
<ThemeSwitcher />
<BreadcrumbProvider>
<RenderRoutes />
</BreadcrumbProvider>
</ConfigProvider>
</StyleProvider>
);
}
export default App
@@ -1,568 +0,0 @@
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import BasicLayout from '@common/components/aoplatform/BasicLayout';
import {createElement, ReactElement,ReactNode,Suspense, useEffect, useState} from 'react';
import {App, Skeleton} from "antd";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import {FC} from 'react';import React from 'react';
import usePluginLoader from '@common/hooks/pluginLoader.ts';
export type RouteConfig = {
path:string
component?:ReactElement
children?:(RouteConfig|false)[]
key:string
provider?:FC<{ children: ReactNode; }>
lazy?:unknown
}
export type RouterParams = {
teamId:string
apiId:string
serviceId:string
clusterId:string;
memberGroupId:string
userGroupId:string
pluginName:string
moduleId:string
accessType:'project'|'team'|'service'
categoryId:string
tagId:string
dashboardType:string
dashboardDetailId:string
topologyId:string
appId:string
roleType:string
roleId:string
}
// const PUBLIC_ROUTES:RouteConfig[] = [
// {
// path:'/',
// component:<Login/>,
// key: 'root',
// },
// {
// path:'/login',
// component:<Login/>,
// key: 'login'
// },
// {
// path:'/',
// component:<ProtectedRoute/>,
// key: 'layout',
// children:[
// {
// path:'guide/*',
// component:<Guide />,
// key:'guide'
// },
// {
// path:'team',
// component:<Outlet/>,
// key: 'team',
// provider: TeamProvider,
// children:[
// {
// path:'',
// key: 'teamList',
// component: <Navigate to="list" />
// },
// {
// path:'list',
// key: 'teamList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamList.tsx'))
// },
// {
// path:'inside/:teamId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsidePage.tsx')),
// key: 'teamInside',
// children:[
// {
// path:'member',
// key: 'teamMember',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsideMember.tsx')),
// },
// {
// path:'setting',
// key: 'teamSetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamConfig.tsx')),
// },
// ]
// }
// ]
// },
// {
// path:'service',
// component:<SystemOutlet />,
// key: 'restService',
// provider: SystemProvider,
// children:[
// {
// path:'',
// key:'restServiceList',
// component:<Navigate to="list" />
// },
// {
// path:'list',
// key: 'restServiceList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
// },
// {
// path:'list/:teamId',
// key: 'restServiceList3',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
// },
// {
// path:':teamId',
// component:<Outlet/>,
// key: 'restServiceInside',
// children:[
// {
// path:'inside/:serviceId',
// key: 'restServiceInside2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsidePage.tsx')),
// children:[
// {
// path:'api',
// key: 'restServiceInsideApi',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideApiDocument.tsx')),
// },
// {
// path:'route/create',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
// },
// {
// path:'route/:routeId',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
// },
// {
// path:'route',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterList')),
// },
// {
// path:'upstream',
// key: 'restServiceInsideUpstream',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/upstream/SystemInsideUpstreamContent.tsx')),
// },
// {
// path:'document',
// key: 'restServiceInsideDocument',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideDocument.tsx')),
// },
// {
// path:'subscriber',
// key: 'restServiceInsideSubscriber',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideSubscriber.tsx')),
// children:[
// ]
// },
// {
// path:'approval',
// key: 'restServiceInsideApproval',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApproval.tsx')),
// children:[
// {
// path:'',
// key: 'restServiceInsideApprovalList',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
// },
// {
// path:'*',
// key: 'restServiceInsideApprovalList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
// }
// ]
// },
// {
// path:'topology',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemTopology.tsx')),
// key: 'systemTopology',
// children:[
// ]
// },
// {
// path:'publish',
// key: 'systemPublish',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublish.tsx')),
// children:[
// {
// path:'',
// key: 'systemPublishList',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
// },
// {
// path:'*',
// key: 'systemPublishList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
// }
// ]
// },
// {
// path:'setting',
// key: 'systemConfig',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
// children:[
// ]
// },
// ]
// },
// {
// path:'aiInside/:serviceId',
// component:<AiServiceOutlet />,
// provider: AiServiceProvider,
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsidePage.tsx')),
// children:[
// {
// path:'api',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideApiDocument')),
// },
// {
// path:'route/create',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
// },
// {
// path:'route/:routeId',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
// },
// {
// path:'route',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterList')),
// },
// {
// path:'document',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideDocument.tsx')),
// },
// {
// path:'subscriber',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideSubscriber.tsx')),
// children:[
// ]
// },
// {
// path:'approval',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApproval')),
// children:[
// {
// path:'',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
// },
// {
// path:'*',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
// }
// ]
// },
// {
// path:'publish',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublish')),
// children:[
// {
// path:'',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
// },
// {
// path:'*',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
// }
// ]
// },
// {
// path:'setting',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
// children:[
// ]
// },
// ]
// }
// ]
// }
// ]
// },
// {
// path:'datasourcing',
// key: 'dataSourcing',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideDashboardSetting.tsx')),
// },
// {
// path:'cluster',
// key: 'cluster',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCluster.tsx')),
// },
// {
// path:'aisetting',
// key: 'aiSetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiSetting/AiSettingList.tsx')),
// },
// {
// path:'cert',
// key: 'cert',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCert.tsx')),
// },
// {
// path:'serviceHub',
// component:<Outlet />,
// key:'serviceHub',
// children:[
// {
// path:'',
// key: 'serviceHubList',
// component: <Navigate to="list" />
// },
// {
// path:'list',
// key:'serviceHubList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubList.tsx')),
// },
// {
// path:'detail/:serviceId',
// key:'serviceHubDetail',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubDetail.tsx')),
// }]
// },
// {
// path:'commonsetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/common/CommonPage.tsx')),
// key:uuidv4(),
// },
// {
// path:'consumer',
// component:<Outlet />,
// provider:TenantManagementProvider,
// key:'tenantManagement',
// children:[
// {
// path:'',
// key:'tenantManagementList',
// component:<Navigate to="list" />
// },
// {
// path:':teamId/inside/:appId',
// key:'tenantManagementInside',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsidePage.tsx')),
// children:[
// {
// path:'service',
// key:'tenantManagementInsideService',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideService.tsx')),
// },
// {
// path:'authorization',
// key:'tenantManagementInsideAuthorization',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideAuth.tsx')),
// },
// {
// path:'setting',
// key:'tenantManagementSetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementAppSetting.tsx')),
// },
// ]
// },
// {
// path:'list',
// key:'serviceHubManagementList',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
// },
// {
// path:'list/:teamId',
// key:'serviceHubManagementList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
// },
// ]
// },
// {
// path:'member',
// key:'member',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberPage.tsx')),
// children:[
// {
// path:'',
// key:'memberList',
// component:<Navigate to="list" />
// },
// {
// path:'list',
// key:'memberList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
// },
// {
// path:'list/:memberGroupId',
// key:'memberList3',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
// }
// ]
// },
// {
// path:'role',
// key:'role',
// component:<Outlet />,
// children:[
// {
// path: '',
// key: 'roleList',
// component: <Navigate to="list" />
// },
// {
// path:'list',
// key:'roleList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleList.tsx')),
// },{
// path:':roleType/config/:roleId',
// key:'roleConfig',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
// },{
// path:':roleType/config',
// key:'roleConfig2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
// }
// ]
// },
// {
// path:'analytics',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/Dashboard.tsx')),
// key:'analytics',
// children:[
// {
// path:'total',
// key:'analytics2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardTotal.tsx')),
// },
// ]
// },
// {
// path:'template/:moduleId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
// key:'intelligentPlugin'
// },
// {
// path:'logsettings/*',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/logsettings/LogSettings.tsx')),
// key: 'logSettings',
// children:[{
// path:'template/:moduleId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
// key:'logSettings2'
// }]
// },
// APP_MODE ==='pro' && {
// path:'resourcesettings/*',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/resourcesettings/ResourceSettings.tsx')),
// key: 'resourceSettings',
// children:[{
// path:'template/:moduleId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
// key:'resourceSettings2'
// }]
// },
// {
// path:'userProfile/*',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/UserProfile.tsx')),
// key:'userProfile',
// children:[{
// path:'changepsw',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/ChangePsw.tsx')),
// key:'changePsw'
// }]
// },
// APP_MODE === 'pro' &&{
// path:'openapi',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@openApi/pages/OpenApiList.tsx')),
// key:'openApi',
// },
// {
// path:'systemrunning',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@systemRunning/pages/SystemRunning.tsx')),
// key:'systemRunning'
// },
// ]
// }
// ]
const RenderRoutes = ()=> {
const { loadPlugins } = usePluginLoader()
const [routeConfig , setRouteConfig] = useState<RouteConfig[]>([])
useEffect(()=>{
loadPlugins().then((res)=>{
console.log('newRouteConfig',res)
setRouteConfig(res as RouteConfig[])
})
},[])
return (
<App className="h-full" message={{ maxCount: 1 }}>
<Router>
<Routes>
{generateRoutes(routeConfig)}
</Routes>
</Router>
</App>
)
}
const generateRoutes = (routerConfig: RouteConfig[]) => {
return routerConfig?.map((route: RouteConfig) => {
let routeElement;
if (route.lazy) {
const LazyComponent = route.lazy as React.ExoticComponent<unknown>;
routeElement = (
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active /></div>}>
{route.provider ? (
createElement(route.provider, {}, <LazyComponent />)
) : (
<LazyComponent />
)}
</Suspense>
);
} else {
routeElement = route.provider ? (
createElement(route.provider, {}, route.component)
) : (
route.component
);
}
return (
<Route
key={route.key}
path={route.path}
element={routeElement}
>
{route.children && generateRoutes(route.children as RouteConfig[])}
</Route>
);
}
)
}
// 保护的路由组件
export function ProtectedRoute() {
const {state} = useGlobalContext()
return state.isAuthenticated? <BasicLayout project="core" /> : <Navigate to="/login" />;
}
export default RenderRoutes
@@ -1,31 +0,0 @@
import { ReactElement,ReactNode,FC } from 'react';
export type RouteConfig = {
path:string
component?:ReactElement
children?:(RouteConfig|false)[]
key:string
provider?:FC<{ children: ReactNode; }>
lazy?:unknown
}
export type RouterParams = {
teamId:string
apiId:string
serviceId:string
clusterId:string;
memberGroupId:string
userGroupId:string
pluginName:string
moduleId:string
accessType:'project'|'team'|'service'
categoryId:string
tagId:string
dashboardType:string
dashboardDetailId:string
topologyId:string
appId:string
roleType:string
roleId:string
routeId:string
}
@@ -1,27 +0,0 @@
import {StrictMode} from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import '@core/index.css'
import {GlobalProvider} from "@common/contexts/GlobalStateContext.tsx";
async function initializeApp() {
try {
// 初始化行为
// await fetchInitialConfig(); // 示例:获取初始配置
// 异步操作完成后,渲染React应用
ReactDOM.createRoot(document.getElementById('root')!).render(
<StrictMode>
<GlobalProvider>
<App />
</GlobalProvider>
</StrictMode>,
);
} catch (error) {
console.error('Initialization failed:', error);
// 处理初始化失败的情况,比如渲染一个错误界面
}
}
// 执行初始化
initializeApp();
@@ -1,209 +0,0 @@
import { RouteConfig } from '@businessEntry/components/aoplatform/RenderRoutes';
import { routerMap } from '@businessEntry/consts/const';
import { isFunction } from 'lodash-es'
// @ts-expect-error module cannot find
import { __federation_method_setRemote,__federation_method_getRemote,__federation_method_unwrapDefault } from 'virtual:__federation__';
interface RemoteModuleConfig{
type:string
remoteEntry:string
exposedModule:string
remoteName:string
}
export async function loadRemoteModule(remoteModuleConfig:RemoteModuleConfig){
__federation_method_setRemote(remoteModuleConfig.remoteName, {
url: () => Promise.resolve(remoteModuleConfig.remoteEntry),
format: 'esm',
from: 'vite',
});
return await __federation_method_getRemote(
remoteModuleConfig.remoteName,
`./${remoteModuleConfig.exposedModule}`
)
}
export function generateRemoteModuleTemplate(
pluginName: string,
exposedModule: string,
pluginPath: string
):RemoteModuleConfig {
return {
type: 'module',
remoteEntry: pluginPath,
exposedModule: `./${exposedModule}`,
remoteName: pluginName
}
}
/** 校验子应用导出的 生命周期 对象是否正确 */
export function validateExportLifecycle(exports: unknown) {
const { bootstrap, mount, unmount } = exports ?? {}
return isFunction(bootstrap) && isFunction(mount) && isFunction(unmount)
}
export const DEFAULT_LOCAL_PLUGIN_PATH = '/plugin-frontend/'
interface PluginRouterConfig {
path:string;
type:string;
expose?:string
}
interface PluginConfig {
name: string;
router: Array<PluginRouterConfig>;
path?: string;
driver:string
}
export interface CoreObj {
routerConfig: RouteConfig[];
setExecuteList: (param:unknown[])=>void;
pluginLoader: PluginLoaderService;
pluginLifecycleGuard: PluginLifecycleGuard;
pluginProvider: PluginProviderService;
builtInPluginLoader: (name: string) => any;
}
const defaultBuiltInPlugin:Array<{path:string, pathMatch?:'full' | 'prefix', componentName?:string, type?:string}> = [
{ path: '/', pathMatch: 'full', componentName: 'login' },
{ path: '/login', componentName: 'login' },
{ path: '/', componentName: 'basicLayout' },
// { path: '**', componentName: 'redirectPage' }
]
export const ApiparkPluginDriver:{[key:string]:{[key:string]:(coreObj?:CoreObj, pluginConfig?:PluginConfig)=>(CoreObj|undefined)}} = {
builtIn: {
// apinto主项目驱动,在core中自动调,不根据插件配置表
default: (coreObj?:CoreObj) => {
if(!coreObj) return coreObj
const url = new URL(window.location.href)
const navHidden = url.searchParams.get('nav_hidden') || sessionStorage.getItem('nav_hidden')
if (navHidden === 'true') sessionStorage.setItem('nav_hidden', navHidden)
const routes = defaultBuiltInPlugin.filter(plugin =>navHidden === 'true' && plugin.componentName === 'basicLayout' ? routerMap.get('navHidden')!.component : routerMap.get(plugin.componentName || plugin.path) ).map(plugin => (
{
path: plugin.path,
component: navHidden === 'true' && plugin.componentName === 'basicLayout' ? routerMap.get('navHidden')!.component : routerMap.get(plugin.componentName || plugin.path)?.component,
children: [],
key:plugin.componentName,
data: {
type: plugin.type || plugin.componentName || plugin.path
},
pathMatch: plugin.pathMatch || 'prefix'
})
)
console.log(routes)
coreObj.routerConfig.push(...routes)
return coreObj
},
component: (coreObj?:CoreObj, pluginConfig?:PluginConfig) => {
if(!coreObj || !pluginConfig) return coreObj
for (const pluginRouter of pluginConfig.router) {
const routerToChanged:RouteConfig[] = pluginRouter.type === 'root' ? coreObj.routerConfig : coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
routerMap.get(pluginConfig.name) && routerToChanged.unshift({...routerMap.get(pluginConfig.name)!, key:pluginConfig.name, path:pluginRouter.path})
}
return coreObj
},
module: (coreObj?:CoreObj, pluginConfig?:PluginConfig) => {
if(!coreObj || !pluginConfig) return coreObj
for (const pluginRouter of pluginConfig.router) {
const routerToChanged:RouteConfig[] = pluginRouter.type === 'root' ? coreObj.routerConfig : coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
routerMap.get(pluginRouter.name) && routerToChanged.unshift({...routerMap.get(pluginRouter.name)!, key:pluginRouter.name, path:pluginRouter.path})
}
return coreObj
},
httpApi: () => {
return undefined
}
},
remote: {
normal: (coreObj?:CoreObj, pluginConfig?:PluginConfig) => {
if(!coreObj || !pluginConfig) return coreObj
const routerToChanged:RouteConfig[] = coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
const remoteRouter:RouteConfig[] = routerToChanged.find((item:RouteConfig) => item?.data?.['type'] === 'remotePlugin') as RouteConfig[]
if (!remoteRouter) {
routerMap.get('remote') && routerToChanged.unshift({
...routerMap.get('remote')!, key:'remote', path:'remote',type:'remotePlugin',children:[{
path:':moduleName',
component: routerMap.get('remote')!.component}
]
})
}
return coreObj
}
},
intelligent: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
normal: (coreObj?:CoreObj, pluginConfig?:PluginConfig) => {
if(!coreObj || !pluginConfig) return coreObj
if(['logsettings','resourcesettings'].indexOf(pluginConfig.name) !== -1){
const routerToChanged:RouteConfig[] = coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
const remoteRouter:RouteConfig[] = routerToChanged.find((item:RouteConfig) => item?.data?.['key'] === pluginConfig.name) as RouteConfig[]
if(!remoteRouter){
routerMap.get(pluginConfig.name) && routerToChanged.unshift({...routerMap.get(pluginConfig.name)!, key:pluginConfig.name, path:pluginConfig.path})
}
return
}
const remoteRouter = coreObj.routerConfig.find((item:RouteConfig) => item?.data?.['type'] === 'intelligentPlugin')
if (!remoteRouter) {
// coreObj.pluginProvider.setRouterConfig(false, {
// path: 'template',
// loadChildren: coreObj.builtInPluginLoader('intelligent'),
// data: {
// type: 'intelligentPlugin'
// }
// }, coreObj.routerConfig)
}
return coreObj
}
},
local: {
router: (coreObj?:CoreObj, pluginConfig?:PluginConfig) => {
if(!coreObj || !pluginConfig) return coreObj
for (const pluginRouter of pluginConfig.router) {
if (pluginRouter.type === 'sub') {
continue
}
updateRouterConfigWithPlugin(coreObj, pluginRouter, pluginConfig)
}
return coreObj
},
preload: (coreObj?:CoreObj, pluginConfig?:PluginConfig) => {
if(!coreObj || !pluginConfig) return coreObj
coreObj.setExecuteList(prev=>[...prev,{ ...pluginConfig, expose: 'Bootstrap', bootstrap: 'BootstrapModule.bootstrap' }])
for (const pluginRouter of pluginConfig.router) {
updateRouterConfigWithPlugin(coreObj, pluginRouter, pluginConfig)
}
return coreObj
}
// extender: (coreObj?:CoreObj, pluginConfig?:PluginConfig) => {}
}
}
function updateRouterConfigWithPlugin (coreObj: CoreObj, pluginRouter: PluginRouterConfig, pluginConfig: PluginConfig) {
if (!pluginRouter.expose) {
throw new Error('pluginRouter.expose is required')
} else {
for (const pluginRouter of pluginConfig.router) {
const routerToChanged:RouteConfig[] = pluginRouter.type === 'root' ? coreObj.routerConfig : coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
routerMap.get(pluginConfig.name) && routerToChanged.unshift({...routerMap.get(pluginConfig.name)!, key:pluginConfig.name, path:pluginRouter.path})
}
// coreObj.pluginProvider.setRouterConfig(pluginRouter.type === 'root', {
// path: pluginRouter.path,
// loadChildren: () => coreObj.pluginLoader.loadModule(
// pluginRouter.path,
// pluginConfig.name,
// pluginRouter.expose!,
// pluginConfig.path || `${DEFAULT_LOCAL_PLUGIN_PATH}${pluginConfig.name}/ApiparkPluginDriver.js`
// ),
// canActivate: [coreObj.pluginLifecycleGuard],
// canActivateChild: [coreObj.pluginLifecycleGuard],
// canDeactivate: [coreObj.pluginLifecycleGuard],
// canLoad: [coreObj.pluginLifecycleGuard]
// }, coreObj.routerConfig)
}
}
-1
View File
@@ -1 +0,0 @@
/// <reference types="vite/client" />
@@ -1,17 +0,0 @@
// start-vite.js// start-vite.js
import { exec } from 'child_process';
const viteProcess = exec('pnpm run build');
viteProcess.stdout.on('data', (data) => {
console.log(data.toString());
});
viteProcess.stderr.on('data', (data) => {
console.error(data.toString());
});
viteProcess.on('close', (code) => {
console.log(`Vite process exited with code ${code}`);
});
@@ -1,33 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noFallthroughCasesInSwitch": true,
"paths": {
"@core/*": ["../core/src/*"],
"@common/*": ["../common/src/*"],
"@market/*": ["../market/src/*"],
"@dashboard/*": ["../dashboard/src/*"],
"@openApi/*": ["../openApi/src/*"],
"@systemRunning/*": ["../systemRunning/src/*"],
"@businessEntry/*": ["./src/*"],
},
},
"include": ["src", "public/iconpark_eolink.js", "public/iconpark_apinto.js", "../common/src/component/aoplatform/EditableTableWithModal.tsx", "../common/src/components/aoplatform/TreeWithMore.tsx", "../common/src/components/aoplatform/DatePicker.tsx", "../common/src/components/aoplatform/TimeRangeSelector.tsx", "../common/src/components/aoplatform/TimePicker.tsx", "../common/src/components/aoplatform/MemberTransfer.tsx", "../common/src/components/aoplatform/Navigation.tsx", "../common/src/components/aoplatform/PageList.tsx", "../common/src/components/aoplatform/ErrorBoundary.tsx", "../common/src/components/aoplatform/ScrollableSection.tsx", "../common/src/utils/postcat.tsx", "../common/src/utils/curl.ts", "../common/src/components/aoplatform/ResetPsw.tsx", "../common/src/components/aoplatform/SubscribeApprovalModalContent.tsx", "src/components/aoplatform/RenderRoutes.tsx", "../common/src/components/aoplatform/PublishApprovalModalContent.tsx", "../common/src/components/aoplatform/InsidePage.tsx", "../common/src/const/type.ts", "../common/src/components/aoplatform/intelligent-plugin", "../common/src/const/domain", "../common/src/hooks/pluginLoader.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
@@ -1,10 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
@@ -1,82 +0,0 @@
import { defineConfig, Plugin } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
cacheDir: './node_modules/.vite',
build:{
outDir:'../../dist',
sourcemap: false,
chunkSizeWarningLimit: 50000,
rollupOptions: {
output: {
chunkFileNames: 'assets/eo-[name]-[hash].js',
},
},
},
css: {
postcss: {
plugins: [
tailwindcss(path.resolve(__dirname, '../common/tailwind.config.js')),
autoprefixer,
],
},
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
},
modules:{
localsConvention:"camelCase",
generateScopedName:"[local]_[hash:base64:2]"
}
},
plugins: [react(),
dynamicImportVars({
include:["src"],
exclude:[],
warnOnError:false
}),
federation({
name:"container",
remotes:{
remoteApp: 'http://localhost:5001/assets/remoteEntry.js' // 远程项目的URL
},
shared:[
"react",
"react-dom",
]
})
],
resolve: {
alias: [
{ find: /^~/, replacement: '' },
{ find: '@common', replacement: path.resolve(__dirname, '../common/src') },
{ find: '@market', replacement: path.resolve(__dirname, '../market/src') },
{ find: '@core', replacement: path.resolve(__dirname, '../core/src') },
{ find: '@dashboard', replacement: path.resolve(__dirname, '../dashboard/src') },
{ find: '@openApi', replacement: path.resolve(__dirname, '../openApi/src') },
{ find: '@systemRunning', replacement: path.resolve(__dirname, '../systemRunning/src') },
{ find: '@businessEntry', replacement: path.resolve(__dirname, './src') },
]
},
server: {
proxy: {
'/api/v1': {
// target: 'http://uat.apikit.com:11204/mockApi/aoplatform/',
target: 'http://172.18.166.219:8288/',
changeOrigin: true,
},
'/api2/v1': {
// target: 'http://uat.apikit.com:11204/mockApi/aoplatform/',
target: 'http://172.18.166.219:8288/',
changeOrigin: true,
}
}
},
logLevel:'info'
})
@@ -19,6 +19,7 @@ import { Icon } from '@iconify/react';
import { $t } from '@common/locales';
import { ProConfigProvider, ProLayout } from '@ant-design/pro-components';
import LanguageSetting from './LanguageSetting';
import { usePluginSlotHub } from '@common/contexts/PluginSlotHubContext';
const APP_MODE = import.meta.env.VITE_APP_MODE;
export type MenuItem = Required<MenuProps>['items'][number];
@@ -43,6 +44,7 @@ const themeToken = {
const [pathname, setPathname] = useState(currentUrl);
const mainPage = project === 'core' ?'/service/list':'/serviceHub/list'
const [menuItems, setMenuItems] = useState<MenuProps['items']>();
const pluginSlotHub = usePluginSlotHub()
useEffect(()=>{
const newMenu = transformMenuData(menuList)
@@ -145,6 +147,16 @@ const themeToken = {
].filter(Boolean), [userInfo]);
const actionRender =useMemo( ()=>{
return [
<LanguageSetting />,
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost onClick={()=>{window.open('https://docs.apipark.com','_blank')}}>
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-help" width="14" height="14"/>{$t('文档')}</span>
</Button> ,
...((pluginSlotHub.getSlot('basicLayoutAfterBtns') as unknown[] )||[] )
]
},[pluginSlotHub.getSlot('basicLayoutAfterBtns') ])
return(
<div
@@ -195,12 +207,7 @@ const themeToken = {
actionsRender={(props) => {
if (props.isMobile) return [];
if (typeof window === 'undefined') return [];
return [
<LanguageSetting />,
<Button className=" text-[#ffffffb3] hover:text-[#fff] border-none" type="default" ghost onClick={()=>{window.open('https://docs.apipark.com','_blank')}}>
<span className='flex items-center gap-[8px]'> <Icon icon="ic:baseline-help" width="14" height="14"/>{$t('文档')}</span>
</Button>
];
return actionRender;
}}
headerTitleRender={() => (
<div className="w-[192px] flex items-center">
@@ -0,0 +1,22 @@
import React, { useEffect, useState } from 'react';
import { Result, Skeleton } from 'antd';
const NotFound: React.FC = () => {
const [showPage, setShowPage] = useState<boolean>(false)
useEffect(()=>{
setTimeout(()=>setShowPage(true), 1000)
},[])
return (
<div className={`h-full w-full flex flex-1 align-middle ${showPage ? 'items-center' : ''}`}>
{ showPage ? <Result
className='w-full'
status="404"
title="404"
subTitle="Sorry, the page you visited does not exist."
/> : <Skeleton active /> }
</div>
)}
export default NotFound;
@@ -0,0 +1,80 @@
import { set } from 'lodash-es';
import { ExoticComponent, JSXElementConstructor, ReactElement, useEffect, useState } from 'react';
import { useBlocker, useLocation, useNavigate } from 'react-router-dom';
import { JSX } from 'react/jsx-runtime';
const withRouteGuard = (WrappedComponent: ExoticComponent<any> | JSXElementConstructor<any>, {
canActivate,
canLoad ,
canDeactivate,
deactivated,
pathPrefix
}: { pathPrefix?:string, canActivate?: () => Promise<boolean>; canLoad?: () => Promise<boolean>; canDeactivate?: () => Promise<boolean>; deactivated?: () => Promise<void>; } = {}) => {
return function RouteGuard(props: JSX.IntrinsicAttributes) {
const [isActivated, setIsActivated] = useState<boolean>(false);
const location = useLocation();
// check canActivate
const startLifecycle = async ()=>{
if(canActivate){
const activateRes = await canActivate();
setIsActivated(activateRes);
}else{
setIsActivated(true);
}
}
// check canDeactivate
const handleBeforeUnload =async (event: { preventDefault: () => void; returnValue: string; }) => {
const deactivateRes = canDeactivate? await canDeactivate():true;
if (!deactivateRes) {
event.preventDefault();
event.returnValue = '';
}
};
// 激活组件时的检查
useEffect(() => {
startLifecycle();
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
deactivated?.();
};
}, []);
const blocker = useBlocker((tx) => {
const currentPath = location.pathname;
const targetPath = tx.nextLocation.pathname;
if (pathPrefix && currentPath.startsWith(pathPrefix) && !targetPath.startsWith(pathPrefix) && canDeactivate) {
canDeactivate().then((res) => {
if(res){
return false;
}else{
return true;
}
})
} else {
return false
}
});
const checkCanLoad = async()=>{
const loadRes = await canLoad!();
!loadRes && setIsActivated(false);
}
useEffect(() => {
if (isActivated && canLoad) {
checkCanLoad()
}
}, [isActivated]);
return isActivated ? <WrappedComponent {...props}/> : null;
};
}
export default withRouteGuard;
@@ -58,7 +58,7 @@ import {
UPDATE_DATASETS_EVENT_EMITTER,
UPDATE_HISTORY_EVENT_EMITTER,
} from './constants'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
export type PromptEditorProps = {
instanceId?: string
@@ -30,7 +30,7 @@ import { $splitNodeContainingQuery } from '../../utils'
import { useOptions } from './hooks'
import type { PickerBlockMenuOption } from './menu'
// import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
type ComponentPickerProps = {
triggerString: string
@@ -14,7 +14,7 @@ import { DELETE_CONTEXT_BLOCK_COMMAND } from './index'
// PortalToFollowElemContent,
// PortalToFollowElemTrigger,
// } from '@/app/components/base/portal-to-follow-elem'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
import { $t } from '@common/locales'
type ContextBlockComponentProps = {
@@ -14,7 +14,7 @@ import { DELETE_HISTORY_BLOCK_COMMAND } from './index'
// PortalToFollowElemContent,
// PortalToFollowElemTrigger,
// } from '@/app/components/base/portal-to-follow-elem'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
import { $t } from '@common/locales'
type HistoryBlockComponentProps = {
@@ -3,7 +3,7 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
import { textToEditorState } from '../utils'
import { CustomTextNode } from './custom-text/node'
import { CLEAR_HIDE_MENU_TIMEOUT } from './workflow-variable-block'
import { useEventEmitterContextContext } from '@common/contexts/event-emitter'
import { useEventEmitterContextContext } from '@common/contexts/EventEmitterContext'
export const PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER = 'PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER'
export const PROMPT_EDITOR_INSERT_QUICKLY = 'PROMPT_EDITOR_INSERT_QUICKLY'
+9 -1
View File
@@ -1,3 +1,11 @@
import { ProtectedRoute } from '@core/components/aoplatform/RenderRoutes'
import { AiServiceProvider } from '@core/contexts/AiServiceContext'
import { SystemProvider } from '@core/contexts/SystemContext'
import { TeamProvider } from '@core/contexts/TeamContext'
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext'
import { lazy } from 'react'
import { Outlet, Navigate } from 'react-router-dom'
export type BasicResponse<T> = {
code:number
data:T
@@ -72,4 +80,4 @@ export const routerKeyMap = new Map<string, string[]|string>([
export const DATA_SHOW_TYPE_OPTIONS = [
{label:'列表', value:'list'},
{label:'块', value:'block'},
]
]
+87 -2
View File
@@ -1,5 +1,8 @@
import { FC, ReactElement, ReactNode } from "react"
import { PERMISSION_DEFINITION } from "./permissions"
import { MatchPositionEnum, MatchTypeEnum } from "@core/const/system/const"
import usePluginLoader from "@common/hooks/pluginLoader"
import { useGlobalContext } from "@common/contexts/GlobalStateContext"
export type UserInfoType = {
username: string
@@ -8,7 +11,6 @@ export type UserInfoType = {
phone: string
avatar: string
type:string
}
export type UserProfileProps = {
@@ -100,4 +102,87 @@ export type SimpleMemberItem = {
email:string
department:string
avatar:string
}
}
export type RouteConfig = {
path:string
pathPrefix?:string
component?:ReactElement
children?:(RouteConfig|false)[]
key:string
provider?:FC<{ children: ReactNode; }>
lazy?:unknown
data?:Record<string, string>
lifecycle?:{
canActivate?:()=>Promise<boolean>
canLoad?:()=>Promise<boolean>
canDeactivate?:()=>Promise<boolean>
deactivated?:()=>Promise<boolean>
}
}
export type RouterParams = {
teamId:string
apiId:string
serviceId:string
clusterId:string;
memberGroupId:string
userGroupId:string
pluginName:string
moduleId:string
accessType:'project'|'team'|'service'
categoryId:string
tagId:string
dashboardType:string
dashboardDetailId:string
topologyId:string
appId:string
roleType:string
roleId:string
routeId:string
}
export type PluginRouterConfig = {
name:string
path:string;
type:string;
expose?:string
}
export type CoreObj = {
routerConfig: RouteConfig[];
setExecuteList: (param:unknown[])=>void;
pluginLoader: {
loadModule: (path: string, name: string, expose: string, pluginPath: string) => Promise<any>;
};
pluginProvider: ReturnType<typeof useGlobalContext>
// pluginLifecycleGuard: PluginLifecycleGuard;
builtInPluginLoader: (name: string) => any;
}
export type PluginConfigType = {
name: string;
router: Array<PluginRouterConfig>;
path?: string;
driver:string
}
export type ApiparkPluginDriverType = {
[key:string]:{[key:string]:(coreObj?:CoreObj, pluginConfig?:PluginConfigType)=>(CoreObj|undefined)}
}
export type RouterMapConfig = {
type: 'component' | 'module',
component: ReactElement,
provider?: FC,
lazy?: FC
key?: string
children?: RouteConfig[]
data?:Record<string, string>
pathMatch?:string
}
@@ -1,4 +1,4 @@
import {createContext, Dispatch, FC, ReactNode, useContext, useReducer, useState} from "react";
import {createContext, Dispatch, FC, ReactNode, useContext, useEffect, useReducer, useState} from "react";
import { useFetch } from "@common/hooks/http";
import { App } from "antd";
import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from "@common/const/const";
@@ -6,6 +6,13 @@ import { checkAccess } from "@common/utils/permission";
import { PERMISSION_DEFINITION } from "@common/const/permissions";
import { $t } from "@common/locales";
import { MenuItem } from "@common/utils/navigation";
import { usePluginEventHub } from "./PluginEventHubContext";
import { ErrorBoundary } from "@ant-design/pro-components";
import NotFound from "@common/components/aoplatform/NotFound";
import { RouteConfig } from "@common/const/type";
import { ProtectedRoute } from "@core/components/aoplatform/RenderRoutes";
import Login from "@core/pages/Login";
import { useLocaleContext } from "./LocaleContext";
interface GlobalState {
isAuthenticated: boolean;
@@ -31,59 +38,6 @@ export type GlobalAction =
| { type: 'UPDATE_MAIN_PAGE'; mainPage: string }
| { type: 'UPDATE_LANGUAGE'; language: string }
class EventEmitter {
// 用来存放注册的事件与回调
_events:any
constructor () {
this._events = {}
}
on (eventName:string, callback:Function) {
// 由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列
const callbacks = this._events[eventName] || []
callbacks.push(callback)
this._events[eventName] = callbacks
}
// 此处需要处理,emit时需要按顺序执行监听的函数,每个函数都会返回是否中止的参数,如果中止则不执行后续的函数
// emit传入eventName 和 event, 返回 event
emit (eventName:string, event:any) {
return new Promise((resolve) => {
const callbacks = this._events[eventName] || []
for (const cb of callbacks) {
const cbRes = cb(event.data)
if (cbRes.continue === false) {
resolve(cbRes)
break
} else {
event = cbRes
}
}
resolve(event.data)
})
}
// 取消订阅
off (eventName:string, callback:Function) {
const callbacks = this._events[eventName] || []
const newCallbacks = callbacks.filter((fn:any) => fn !== callback && fn.initialCallback !== callback /* 用于once的取消订阅 */)
this._events[eventName] = newCallbacks
}
// 单次订阅,后台插件可以自行决定取消对事件的订阅
once (eventName:string, callback:Function) {
// 由于需要在回调函数执行后,取消订阅当前事件,所以需要对传入的回调函数做一层包装,然后绑定包装后的函数
const one = (...args:any) => {
callback(...args)
this.off(eventName, one)
}
// 由于:我们订阅事件的时候,修改了原回调函数的引用,所以,用户触发 off 的时候不能找到对应的回调函数
// 所以,我们需要在当前函数与用户传入的回调函数做一个绑定,我们通过自定义属性来实现
one.initialCallback = callback
this.on(eventName, one)
}
}
const mockData = [
{
@@ -119,7 +73,14 @@ class EventEmitter {
"path": "/team",
"icon": "ic:baseline-people-alt",
"access": "all"
}
},
// {
// "name": "路由组件",
// "key": "router",
// "path": "/router1",
// "icon": "ic:baseline-people-alt",
// "access": "all"
// }
]
},
{
@@ -251,16 +212,13 @@ export const GlobalContext = createContext<{
checkPermission:(access:keyof typeof PERMISSION_DEFINITION[0] | Array<keyof typeof PERMISSION_DEFINITION[0]>)=>boolean
teamDataFlushed:boolean
accessInit:boolean
// 插件系统
pluginEventHub:EventEmitter
pluginSlotHubService:{
addSlot:(name:string, content:unknown)=>void
addSlotArr:(name:string, content:unknown[])=>void
removeSlot:(name:string)=>void
getSlot:(name:string)=>unknown
}
aiConfigFlushed:boolean
setAiConfigFlushed:(flush:boolean)=>void
routeConfig: RouteConfig[];
setRouterConfig: (isRoot: boolean, config: RouteConfig) => void;
addRouteConfig: (parentRoute: RouteConfig, config: RouteConfig) => void;
fetchData: ReturnType<typeof useFetch>['fetchData'];
$t: typeof $t;
} | undefined>(undefined);
const globalReducer = (state: GlobalState, action: GlobalAction): GlobalState => {
@@ -311,10 +269,18 @@ const globalReducer = (state: GlobalState, action: GlobalAction): GlobalState =>
}
};
export const DefaultRouteConfig = [
{ path: '/', pathMatch: 'full', component: <Login /> ,key:'root',},
{ path: '/login', component: <Login /> ,key:'login'},
{ path: '/', pathMatch:'prefix',component:<ProtectedRoute /> ,key:'basciLayout',children:[
{ path: '*', component: <ErrorBoundary><NotFound/></ErrorBoundary>, key: 'errorBoundary' }
]}
]
// Create a context provider component
export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
const {fetchData} = useFetch()
const { message } = App.useApp()
const { setLocale } = useLocaleContext();
const [state, dispatch] = useReducer(globalReducer, {
isAuthenticated: true, //mock用
userData: null,
@@ -330,10 +296,43 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
const [accessInit, setAccessInit] = useState<boolean>(false)
const [aiConfigFlushed, setAiConfigFlushed] = useState<boolean>(false)
let getGlobalAccessPromise: Promise<BasicResponse<{ access:string[] }>> | null = null
const [pluginEventHub] = useState<EventEmitter>(new EventEmitter())
const [pluginSlotHub] = useState<Map<string,unknown>>(new Map())
const [menuList, setMenuList] = useState<MenuItem[]>(mockData);
const [routeConfig, setRouteConfigState] = useState<RouteConfig[]>(DefaultRouteConfig)
useEffect(() => {
setLocale(state.language);
}, [state.language, setLocale]);
const { fetchData } = useFetch();
const setRouterConfig = (isRoot: boolean, config: RouteConfig) => {
setRouteConfigState(prevConfig => {
if (isRoot) {
return [config,...prevConfig];
} else {
const rootRoute = prevConfig.find(route => route.path === '/' && route?.pathMatch === 'prefix') ;
if (rootRoute ) {
rootRoute.children = rootRoute.children ? [config, ...rootRoute.children] : [config];
}
return [...prevConfig];
}
});
};
const addRouteConfig = (parentRoute: RouteConfig, config: RouteConfig) => {
const addConfigToParent = (routes: RouteConfig[]): RouteConfig[] => {
return routes.map(route => {
if (route.key === parentRoute.key) {
route.children = route.children ? [...route.children, config] : [config];
} else if (route.children) {
route.children = addConfigToParent(route.children);
}
return route;
});
};
setRouteConfigState(prevConfig => addConfigToParent(prevConfig));
};
const getGlobalAccessData = ()=>{
if(getGlobalAccessPromise){
@@ -404,23 +403,26 @@ export const GlobalProvider: FC<{children:ReactNode}> = ({ children }) => {
return revs
}
const pluginSlotHubService = {
addSlot:(name:string, content:any) => {pluginSlotHub.set(name, content)},
addSlotArr : (name:string, content:any[]) => {pluginSlotHub.get(name) ? pluginSlotHub.set(name, (pluginSlotHub.get(name) as Array<unknown>).push(content)) : pluginSlotHub.set(name, content)},
removeSlot:(name:string) => {pluginSlotHub.delete(name)},
getSlot:(name:string) => {pluginSlotHub.get(name)}
}
return (
<GlobalContext.Provider value={
{ state, dispatch,accessData,pluginAccessDictionary,
{ state, dispatch,
accessData,
pluginAccessDictionary,
getGlobalAccessData,
getPluginAccessDictionary,
getTeamAccessData,teamDataFlushed,getMenuList,menuList,
cleanTeamAccessData,
resetAccess ,checkPermission,accessInit,
aiConfigFlushed, setAiConfigFlushed,pluginEventHub,pluginSlotHubService}}>
resetAccess ,checkPermission,
accessInit,
aiConfigFlushed,
setAiConfigFlushed,
routeConfig,
setRouterConfig,
addRouteConfig,
fetchData,
$t:$t,}}>
{children}
</GlobalContext.Provider>
);
@@ -0,0 +1,44 @@
import React, { createContext, useContext, useState, useEffect, useMemo } from 'react';
import { ConfigProviderProps } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import enUS from 'antd/locale/en_US';
import zhTW from 'antd/locale/zh_TW';
import jaJP from 'antd/locale/ja_JP';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import 'dayjs/locale/zh-tw';
import 'dayjs/locale/ja';
type Locale = ConfigProviderProps['locale'];
const languageMap: Record<string, Locale> = {
'zh-CN': zhCN,
'en-US': enUS,
'zh-TW': zhTW,
'ja-JP': jaJP,
};
const LocaleContext = createContext<{
locale: Locale;
setLocale: (locale: string) => void;
}>({
locale: zhCN,
setLocale: () => {},
});
export const LocaleProvider: React.FC = ({ children }) => {
const [locale, setLocaleState] = useState<Locale>(zhCN);
const setLocale = (language: string) => {
dayjs.locale(language);
setLocaleState(languageMap[language]);
};
return (
<LocaleContext.Provider value={{ locale, setLocale }}>
{children}
</LocaleContext.Provider>
);
};
export const useLocaleContext = () => useContext(LocaleContext);
@@ -0,0 +1,76 @@
import { createContext, FC, ReactNode, useContext, useState } from "react";
class EventEmitter {
// 用来存放注册的事件与回调
_events:any
constructor () {
this._events = {}
}
on (eventName:string, callback:Function) {
// 由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列
const callbacks = this._events[eventName] || []
callbacks.push(callback)
this._events[eventName] = callbacks
}
// 此处需要处理,emit时需要按顺序执行监听的函数,每个函数都会返回是否中止的参数,如果中止则不执行后续的函数
// emit传入eventName 和 event, 返回 event
emit (eventName:string, event:any) {
return new Promise((resolve) => {
const callbacks = this._events[eventName] || []
for (const cb of callbacks) {
const cbRes = cb(event.data)
if (cbRes.continue === false) {
resolve(cbRes)
break
} else {
event = cbRes
}
}
resolve(event.data)
})
}
// 取消订阅
off (eventName:string, callback:Function) {
const callbacks = this._events[eventName] || []
const newCallbacks = callbacks.filter((fn:any) => fn !== callback && fn.initialCallback !== callback /* 用于once的取消订阅 */)
this._events[eventName] = newCallbacks
}
// 单次订阅,后台插件可以自行决定取消对事件的订阅
once (eventName:string, callback:Function) {
// 由于需要在回调函数执行后,取消订阅当前事件,所以需要对传入的回调函数做一层包装,然后绑定包装后的函数
const one = (...args:any) => {
callback(...args)
this.off(eventName, one)
}
// 由于:我们订阅事件的时候,修改了原回调函数的引用,所以,用户触发 off 的时候不能找到对应的回调函数
// 所以,我们需要在当前函数与用户传入的回调函数做一个绑定,我们通过自定义属性来实现
one.initialCallback = callback
this.on(eventName, one)
}
}
export const PluginEventHubContext = createContext<EventEmitter | undefined>(undefined);
export const PluginEventHubProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [pluginEventHub] = useState<EventEmitter>(new EventEmitter());
return (
<PluginEventHubContext.Provider value={pluginEventHub}>
{children}
</PluginEventHubContext.Provider>
);
};
export const usePluginEventHub = () => {
const context = useContext(PluginEventHubContext);
if (!context) {
throw new Error('usePluginEventHub must be used within a PluginEventHubProvider');
}
return context;
};
@@ -0,0 +1,36 @@
import { createContext, FC, ReactNode, useContext, useState } from "react";
export const PluginSlotHubContext = createContext<{
addSlot: (name: string, content: unknown) => void;
addSlotArr: (name: string, content: unknown[]) => void;
removeSlot: (name: string) => void;
getSlot: (name: string) => unknown;
} | undefined>(undefined);
export const PluginSlotHubProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [pluginSlotHub] = useState<Map<string, unknown>>(new Map());
const pluginSlotHubService = {
addSlot: (name: string, content: any) => {
pluginSlotHub.set(name, pluginSlotHub.get(name) ? [...(pluginSlotHub.get(name) as Array<unknown>), content] : [content] ); },
addSlotArr: (name: string, content: any[]) => { pluginSlotHub.get(name) ? pluginSlotHub.set(name, (pluginSlotHub.get(name) as Array<unknown>).push(content)) : pluginSlotHub.set(name, content); },
removeSlot: (name: string) => { pluginSlotHub.delete(name); },
getSlot: (name: string) => {
return pluginSlotHub.get(name) ; }
};
return (
<PluginSlotHubContext.Provider value={pluginSlotHubService}>
{children}
</PluginSlotHubContext.Provider>
);
};
export const usePluginSlotHub = () => {
const context = useContext(PluginSlotHubContext);
if (!context) {
throw new Error('usePluginSlotHub must be used within a PluginSlotHubProvider');
}
return context;
};
+10 -3
View File
@@ -1,4 +1,6 @@
import { STATUS_CODE } from "@common/const/const";
import { BasicResponse, STATUS_CODE } from "@common/const/const";
import { usePluginEventHub } from "@common/contexts/PluginEventHubContext";
import EventEmitter from "events";
const urlWhiteList = [/api.example.com\/users/, /api.example2.com\/products/]; // 正则白名单
@@ -137,7 +139,10 @@ type EoRequest = RequestInit & {eoParams?:{[k:string]:unknown},eoTransformKeys?:
type EoHeaders = Headers | {[k:string]:string}
export function useFetch(){
export function useFetch() {
// plugin cannot use usePluginEventHub directly, so we need to pass it as a parameter
const pluginEventHub = usePluginEventHub()
function fetchData<T>(url:string, options: EoRequest ) {
// 合并传入的headers与默认headers
const headers = { ...(options.body ? {}:DEFAULT_HEADERS), ...options.headers };
@@ -184,9 +189,11 @@ export function useFetch(){
// 如果响应体为JSON且指定了转换键,则转换响应数据
if ( isJsonHttp(response.headers)) {
const data = await response.json();
return shouldTransformKeys ? keysToCamel(data,options.eoTransformKeys as string[]) as T:data
const newData = await pluginEventHub.emit('httpResponse', {data,continue:true}) as Response;
return shouldTransformKeys ? keysToCamel(newData,options.eoTransformKeys as string[]) as T:data
}
return response;
})
.catch(error => {
@@ -1,10 +1,12 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { useGlobalContext } from "@common/contexts/GlobalStateContext";
import { App } from "antd";
import { ApiparkPluginDriver as apipark, CoreObj, generateRemoteModuleTemplate, loadRemoteModule, validateExportLifecycle } from "@businessEntry/utils/plugin";
import { DEFAULT_LOCAL_PLUGIN_PATH, generateRemoteModuleTemplate, loadRemoteModule, validateExportLifecycle } from "@common/utils/plugin.tsx";
import { useFetch } from "@common/hooks/http";
import { RouteConfig } from "@businessEntry/components/aoplatform/RenderRoutes";
import { routerMap } from "@businessEntry/consts/const";
import { PluginConfigType, RouteConfig } from "@common/const/type.ts";
import { ApiparkPluginDriverType ,RouterMapConfig} from "@common/const/type";
import { usePluginEventHub } from "@common/contexts/PluginEventHubContext";
import { usePluginSlotHub } from "@common/contexts/PluginSlotHubContext";
import { App } from "antd";
const mockData = {
buildAt:'2024-09-13T03:51:25Z',
@@ -182,7 +184,7 @@ const mockData = {
type:'normal'
}
]
}
},
// {
// "driver": "apipark.remote.normal",
// "name": "remote",
@@ -230,12 +232,12 @@ const mockData = {
// ]
// },
// {
// "driver": "apipark.local.router",
// "name": "monitor",
// "driver": "apipark.local.preload",
// "name": "remote",
// "router": [
// {
// "expose": "AppModule",
// "path": "monitor",
// "expose": "App",
// "path": "router1/*",
// "type": "normal"
// }
// ]
@@ -256,17 +258,34 @@ const mockData = {
version:'6438d5aa'
}
const usePluginLoader = () => {
export type ExecutePluginType = PluginConfigType & {
expose:string,
bootstrap:string
}
const usePluginLoader = (apipark:ApiparkPluginDriverType,routerMap:Map<string, RouterMapConfig>) => {
const [modules, setModules] = useState(new Map());
const [executeList, setExecuteList] = useState([]);
const [executeList, setExecuteList] = useState<ExecutePluginType[]>([]);
const [baseHref, setBaseHref] = useState('');
const [redirectUrl, setRedirectUrl] = useState('');
const [pendingTasks, setPendingTasks] = useState(0);
const {fetchData} = useFetch();
const pluginProvider = useGlobalContext();
const {state, dispatch,getMenuList,pluginEventHub,pluginSlotHubService:pluginSlotHub} = pluginProvider
const { finalRouterConfig, setFinalRouterConfig} = useState<RouteConfig[]>([])
const {message:messageService, modal:modalService} = App.useApp();
const pluginEventHub = usePluginEventHub();
const pluginSlotHub = usePluginSlotHub();
const { getMenuList,dispatch} = pluginProvider
const { modal,message } = App.useApp()
const [startLoadExecutePlugin, setStartLoadExecutePlugin] = useState<boolean>(false)
const messageService = message;
const modalService = modal;
let startInstallPlugin = false
useEffect(()=>{
if (startLoadExecutePlugin && pendingTasks === 0 && executeList.length > 0) {
loadExecutedPlugin();
}
},[pendingTasks, executeList])
const getModule = (routerPrefix:string, specific = false) => {
if (routerPrefix.startsWith('/')) {
routerPrefix = routerPrefix.substring(1);
@@ -289,26 +308,34 @@ const usePluginLoader = () => {
const loadModule = async (routerPrefix: string, pluginName: any, exposedModule: string , pluginPath: any) => {
if (!modules.get(routerPrefix)) {
try {
const Module = await loadRemoteModule(generateRemoteModuleTemplate(pluginName, exposedModule, pluginPath));
setModules(prevModules => new Map(prevModules).set(routerPrefix, Module));
const loadedModule = await loadRemoteModule(generateRemoteModuleTemplate(pluginName, exposedModule, pluginPath));
const Module = loadedModule.default ?? loadedModule
let ModuleBootstrap;
try {
ModuleBootstrap = await loadRemoteModule(generateRemoteModuleTemplate(pluginName, 'Bootstrap', pluginPath));
} catch (error) {
console.warn('Bootstrap module not found:', error);
}
setModules(prevModules => new Map(prevModules).set(routerPrefix,Module[exposedModule] ));
if (!validateExportLifecycle(Module)) {
console.error('需要导出插件生命周期函数');
return;
}
await Module.bootstrap?.({
pluginProvider,
pluginEventHub: pluginEventHub,
pluginEventHub,
pluginSlotHub
});
return Module[exposedModule];
return Module;
} catch (error) {
console.error('导入插件失败:', error);
}
}
return getModule(routerPrefix, true)[exposedModule];
return getModule(routerPrefix, true);
};
const loadExecutedPlugin = async () => {
setStartLoadExecutePlugin(true)
for (const plugin of executeList) {
try {
const Module = await loadRemoteModule(generateRemoteModuleTemplate(plugin.name, plugin?.expose || 'Bootstrap', plugin.path || `${DEFAULT_LOCAL_PLUGIN_PATH}${plugin.name}/apipark.js`));
@@ -321,10 +348,8 @@ const usePluginLoader = () => {
pluginSlotHub,
pluginProvider,
platformProvider:null,
closeModal,
messageService,
modalService,
apiService:fetchData
});
}
} catch (error) {
@@ -333,24 +358,22 @@ const usePluginLoader = () => {
}
};
// TODO 暂未找到关闭弹窗的全局方法
const closeModal = () => {
};
const loadPlugins = () => {
return new Promise((resolve) => {
const routerConfig: RouteConfig[] = [];
apipark['builtIn'].default({ routerConfig } as CoreObj);
installPlugin(routerConfig).then(async (res)=>{
if(startInstallPlugin) {
return resolve(true)
}
startInstallPlugin = true
installPlugin().then(async (res)=>{
// reset route after loading executed plugins
// TODO 需要测试次式executeList是否已经更新,如果没有更新的话要修改executeList 更新的写法
await loadExecutedPlugin();
resolve(res)
return resolve(res)
})
});
};
const installPlugin = (routerConfig: any[]) => {
const installPlugin = () => {
return new Promise((resolve, reject) => {
// fetchData('system/plugins',{method:'GET'}).then((resp) => {
// if (resp.code === 0){
@@ -374,12 +397,18 @@ const usePluginLoader = () => {
continue;
}
const driver = driverName.split('.').reduce((driverMethod: { [x: string]: any; }, driverName: string | number) => driverMethod[driverName], driverMethod);
driver({ routerConfig, setExecuteList, pluginLoader, pluginProvider, pluginLifecycleGuard, builtInPluginLoader }, plugin);
if(driverName.split('.')[2] === 'preload'){
setPendingTasks(prev => prev + 1);
}
;(driver as Function )?.({ setExecuteList:(callback:ExecutePluginType[]) => {
setExecuteList(callback);
setPendingTasks(prev => prev - 1);
}, pluginLoader, pluginProvider, pluginLifecycleGuard, builtInPluginLoader }, plugin);
} catch (err) {
console.warn('安装插件出错:', err);
}
}
resolve(routerConfig);
resolve(true);
// } else {
// messageService.error(resp.msg || '获取插件配置列表失败,请重试!');
// reject(new Error(resp.msg || '获取插件配置列表失败'));
@@ -391,7 +420,7 @@ const usePluginLoader = () => {
const loadBuiltInModule = (pluginName: any) => {
try {
const { module } = routerMap.get(pluginName);
const { module } = routerMap.get(pluginName)!;
return module;
} catch (err) {
console.warn(`安装内置插件[${pluginName}]出错:`, err);
@@ -402,7 +431,6 @@ const usePluginLoader = () => {
loadPlugins,
loadModule,
loadExecutedPlugin,
closeModal,
setBaseHref,
getModule
};
@@ -0,0 +1,154 @@
import { CoreObj, PluginConfigType, PluginRouterConfig, RouteConfig, RouterMapConfig } from '@common/const/type';
import { isFunction } from 'lodash-es'
// @ts-expect-error module cannot find
import { __federation_method_setRemote,__federation_method_getRemote,__federation_method_unwrapDefault } from 'virtual:__federation__';
import { ApiparkPluginDriverType } from '@common/const/type';
import React from 'react';
interface RemoteModuleConfig{
type:string
remoteEntry:string
exposedModule:string
remoteName:string
}
export async function loadRemoteModule(remoteModuleConfig:RemoteModuleConfig){
__federation_method_setRemote(remoteModuleConfig.remoteName, {
url: () => Promise.resolve(remoteModuleConfig.remoteEntry),
format: 'esm',
from: 'vite',
});
return await __federation_method_getRemote(
remoteModuleConfig.remoteName,
`./${remoteModuleConfig.exposedModule}`
)
}
export function generateRemoteModuleTemplate(
pluginName: string,
exposedModule: string,
pluginPath: string
):RemoteModuleConfig {
return {
type: 'module',
remoteEntry: pluginPath,
exposedModule,
remoteName: pluginName
}
}
/** check the lifecycle method of plugin */
export function validateExportLifecycle(exports: unknown) {
const { bootstrap, mount, unmount } = exports ?? {}
return isFunction(bootstrap) && isFunction(mount) && isFunction(unmount)
}
export const DEFAULT_LOCAL_PLUGIN_PATH = '/plugin-frontend/'
export const ApiparkPluginDriver = (routerMap: Map<string, RouterMapConfig>): ApiparkPluginDriverType => { return {
builtIn: {
component: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
for (const pluginRouter of pluginConfig.router) {
routerMap.get(pluginConfig.name) && coreObj.pluginProvider.setRouterConfig(pluginRouter.type === 'root' , {
...routerMap.get(pluginConfig.name)!,
key:pluginConfig.name,
path:pluginRouter.path})
}
return coreObj
}
},
remote: {
normal: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
const routerToChanged:RouteConfig[] = coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
const remoteRouter:RouteConfig[] = routerToChanged.find((item:RouteConfig) => item?.data?.['type'] === 'remotePlugin') as RouteConfig[]
if (!remoteRouter) {
routerMap.get('remote') && coreObj.pluginProvider.setRouterConfig(false,{
...routerMap.get('remote')!, key:'remote', path:'remote',type:'remotePlugin',children:[{
path:':moduleName',
component: routerMap.get('remote')!.component}
]
})
}
return coreObj
}
},
intelligent: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
normal: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
if(['logsettings','resourcesettings'].indexOf(pluginConfig.name) !== -1){
const routerToChanged:RouteConfig[] = coreObj.routerConfig.find((router: RouteConfig) => router.path === '/' && router?.pathMatch !== 'full')!.children as RouteConfig[]
const remoteRouter:RouteConfig[] = routerToChanged.find((item:RouteConfig) => item?.data?.['key'] === pluginConfig.name) as RouteConfig[]
if(!remoteRouter){
routerMap.get(pluginConfig.name) && routerToChanged.unshift({...routerMap.get(pluginConfig.name)!, key:pluginConfig.name, path:pluginConfig.path})
}
return
}
const remoteRouter = coreObj.routerConfig.find((item:RouteConfig) => item?.data?.['type'] === 'intelligentPlugin')
if (!remoteRouter) {
// coreObj.pluginProvider.setRouterConfig(false, {
// path: 'template',
// loadChildren: coreObj.builtInPluginLoader('intelligent'),
// data: {
// type: 'intelligentPlugin'
// }
// }, coreObj.routerConfig)
}
return coreObj
}
},
local: {
router: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
for (const pluginRouter of pluginConfig.router) {
if (pluginRouter.type === 'sub') {
continue
}
updateRouterConfigWithPlugin(coreObj, pluginRouter, pluginConfig)
}
return coreObj
},
preload: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {
if(!coreObj || !pluginConfig) return coreObj
coreObj.setExecuteList(prev=>[...prev,{ ...pluginConfig, expose: 'Bootstrap', bootstrap: 'BootstrapModule.bootstrap' }])
for (const pluginRouter of pluginConfig.router) {
updateRouterConfigWithPlugin(coreObj, pluginRouter, pluginConfig)
}
return coreObj
}
// extender: (coreObj?:CoreObj, pluginConfig?:PluginConfigType) => {}
}
}
}
async function updateRouterConfigWithPlugin (coreObj: CoreObj, pluginRouter: PluginRouterConfig, pluginConfig: PluginConfigType) {
if (!pluginRouter.expose) {
throw new Error('pluginRouter.expose is required')
} else {
for (const pluginRouter of pluginConfig.router) {
const loadedModule = await coreObj.pluginLoader.loadModule(
pluginRouter.path,
pluginConfig.name,
pluginRouter.expose!,
pluginConfig.path || `${DEFAULT_LOCAL_PLUGIN_PATH}${pluginConfig.name}/apipark.js`
)
const loadedModulePage = loadedModule[pluginRouter.expose!]
const LazyComponent = React.lazy(() => Promise.resolve({ default: loadedModulePage?.default || loadedModulePage }));
const newRouter: RouteConfig = {
path: pluginRouter.path,
key: pluginConfig.name,
lazy: () => Promise.resolve({ default: (props: any) => <LazyComponent {...props} /> }),
pathPrefix: pluginRouter.path.endsWith('/*') ? pluginRouter.path.slice(0, -2) : pluginRouter.path,
lifecycle:{
canActivate: loadedModule?.beforeMount,
canLoad: loadedModule?.mount,
canDeactivate: loadedModule?.beforeUnmount,
deactivated: loadedModule?.unmount
}
};
coreObj.pluginProvider.setRouterConfig(pluginRouter.type === 'root' , newRouter)
}
}
}
+28 -40
View File
@@ -1,30 +1,16 @@
import './App.css'
import { ConfigProvider, ConfigProviderProps, Radio, RadioChangeEvent } from 'antd';
import { ConfigProvider, App as AppAntd } from 'antd';
import RenderRoutes from '@core/components/aoplatform/RenderRoutes';
import {BreadcrumbProvider} from "@common/contexts/BreadcrumbContext.tsx";
import { StyleProvider } from '@ant-design/cssinjs';
import zhCN from 'antd/locale/zh_CN';
import enUS from 'antd/locale/en_US';
import zhTW from 'antd/locale/zh_TW';
import jaJP from 'antd/locale/ja_JP';
import useInitializeMonaco from "@common/hooks/useInitializeMonaco";
import { useEffect, useMemo, useState } from 'react';
import 'dayjs/locale/zh-cn';
import 'dayjs/locale/zh-tw';
import 'dayjs/locale/ja';
import dayjs from 'dayjs';
import { useGlobalContext } from '@common/contexts/GlobalStateContext';
import { useMemo } from 'react';
import { GlobalProvider } from '@common/contexts/GlobalStateContext';
import { $t } from '@common/locales';
type Locale = ConfigProviderProps['locale'];
const languageMap: Record<string, Locale> = {
'zh-CN':zhCN,
'en-US':enUS,
'zh-TW':zhTW,
'ja-JP':jaJP
}
import { PluginEventHubProvider } from '@common/contexts/PluginEventHubContext';
import { PluginSlotHubProvider } from '@common/contexts/PluginSlotHubContext';
import { useLocaleContext } from '@common/contexts/LocaleContext';
import { StyleProvider } from '@ant-design/cssinjs';
const antdComponentThemeToken = {
@@ -150,33 +136,35 @@ const antdComponentThemeToken = {
function App() {
const [locale, setLocal] = useState<Locale>();
const { locale } = useLocaleContext();
useInitializeMonaco()
const { state} = useGlobalContext()
useEffect(() => {
dayjs.locale(state.language);
setLocal(languageMap[state.language]);
},[state.language])
const validateMessages = useMemo(()=>({
required: $t('必填项'),
email:$t('不是有效邮箱地址')}
),[state.language])
),[locale])
return (
<StyleProvider hashPriority={"high"}>
<ConfigProvider
locale={locale}
wave={{disabled:true}}
theme={antdComponentThemeToken}
form={{validateMessages }}>
<BreadcrumbProvider>
<RenderRoutes />
</BreadcrumbProvider>
</ConfigProvider>
</StyleProvider>
<StyleProvider hashPriority={"high"}>
<ConfigProvider
locale={locale}
wave={{disabled:true}}
theme={antdComponentThemeToken}
form={{validateMessages }}>
<AppAntd className="h-full" message={{ maxCount: 1 }}>
<PluginEventHubProvider>
<PluginSlotHubProvider>
<GlobalProvider>
<BreadcrumbProvider>
<RenderRoutes />
</BreadcrumbProvider>
</GlobalProvider>
</PluginSlotHubProvider>
</PluginEventHubProvider>
</AppAntd>
</ConfigProvider>
</StyleProvider>
);
}
@@ -1,532 +1,90 @@
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { Navigate, RouterProvider, createBrowserRouter, RouteObject } from 'react-router-dom';
import BasicLayout from '@common/components/aoplatform/BasicLayout';
import {createElement,Suspense, useEffect, useState} from 'react';
import {App, Skeleton} from "antd";
import {Skeleton, Spin} from "antd";
import {useGlobalContext} from "@common/contexts/GlobalStateContext.tsx";
import usePluginLoader from '@common/hooks/pluginLoader.ts';
import { RouteConfig } from '@common/const/type.ts';
// // const PUBLIC_ROUTES:RouteConfig[] = [
// {
// path:'/',
// component:<Login/>,
// key: 'root',
// },
// {
// path:'/login',
// component:<Login/>,
// key: 'login'
// },
// {
// path:'/',
// component:<ProtectedRoute/>,
// key: 'layout',
// children:[
// {
// path:'guide/*',
// component:<Guide />,
// key:'guide'
// },
// {
// path:'team',
// component:<Outlet/>,
// key: 'team',
// provider: TeamProvider,
// children:[
// {
// path:'',
// key: 'teamList',
// component: <Navigate to="list" />
// },
// {
// path:'list',
// key: 'teamList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamList.tsx'))
// },
// {
// path:'inside/:teamId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsidePage.tsx')),
// key: 'teamInside',
// children:[
// {
// path:'member',
// key: 'teamMember',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamInsideMember.tsx')),
// },
// {
// path:'setting',
// key: 'teamSetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/team/TeamConfig.tsx')),
// },
// ]
// }
// ]
// },
// {
// path:'service',
// key: uuidv4(),
// component:<SystemOutlet />,
// key: 'restService',
// provider: SystemProvider,
// children:[
// {
// path:'',
// key:'restServiceList',
// component:<Navigate to="list" />
// },
// {
// path:'list',
// key: 'restServiceList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
// },
// {
// path:'list/:teamId',
// key: 'restServiceList3',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemList.tsx')),
// },
// {
// path:':teamId',
// component:<Outlet/>,
// key: 'restServiceInside',
// children:[
// {
// path:'inside/:serviceId',
// key: 'restServiceInside2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsidePage.tsx')),
// children:[
// {
// path:'api',
// key: 'restServiceInsideApi',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideApiDocument.tsx')),
// },
// {
// path:'route/create',
// key: 'restServiceInsideRouteCreate',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
// },
// {
// path:'route/:routeId',
// key: 'restServiceInsideRouteEdit',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
// },
// {
// path:'route',
// key: 'restServiceInsideRoute',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterList')),
// },
// {
// path:'upstream',
// key: 'restServiceInsideUpstream',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/upstream/SystemInsideUpstreamContent.tsx')),
// },
// {
// path:'document',
// key: 'restServiceInsideDocument',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideDocument.tsx')),
// },
// {
// path:'subscriber',
// key: 'restServiceInsideSubscriber',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemInsideSubscriber.tsx')),
// children:[
// ]
// },
// {
// path:'approval',
// key: 'restServiceInsideApproval',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApproval.tsx')),
// children:[
// {
// path:'',
// key: 'restServiceInsideApprovalList',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
// },
// {
// path:'*',
// key: 'restServiceInsideApprovalList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/approval/SystemInsideApprovalList.tsx')),
// }
// ]
// },
// {
// path:'topology',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemTopology.tsx')),
// key: 'systemTopology',
// children:[
// ]
// },
// {
// path:'publish',
// key: 'systemPublish',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublish.tsx')),
// children:[
// {
// path:'',
// key: 'systemPublishList',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
// },
// {
// path:'*',
// key: 'systemPublishList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/publish/SystemInsidePublishList.tsx')),
// }
// ]
// },
// {
// path:'setting',
// key: 'systemConfig',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
// children:[
// ]
// },
// ]
// },
// {
// path:'aiInside/:serviceId',
// component:<AiServiceOutlet />,
// provider: AiServiceProvider,
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsidePage.tsx')),
// children:[
// {
// path:'api',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideApiDocument')),
// },
// {
// path:'route/create',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
// },
// {
// path:'route/:routeId',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterCreate')),
// },
// {
// path:'route',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/api/AiServiceInsideRouterList')),
// },
// {
// path:'document',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideDocument.tsx')),
// },
// {
// path:'subscriber',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/AiServiceInsideSubscriber.tsx')),
// children:[
// ]
// },
// {
// path:'approval',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApproval')),
// children:[
// {
// path:'',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
// },
// {
// path:'*',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/approval/AiServiceInsideApprovalList')),
// }
// ]
// },
// {
// path:'publish',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublish')),
// children:[
// {
// path:'',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
// },
// {
// path:'*',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiService/publish/AiServiceInsidePublishList')),
// }
// ]
// },
// {
// path:'setting',
// key: uuidv4(),
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/SystemConfig.tsx')),
// children:[
// ]
// },
// ]
// }
// ]
// }
// ]
// },
// {
// path:'datasourcing',
// key: 'dataSourcing',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideDashboardSetting.tsx')),
// },
// {
// path:'cluster',
// key: 'cluster',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCluster.tsx')),
// },
// {
// path:'aisetting',
// key: 'aiSetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/aiSetting/AiSettingList.tsx')),
// },
// {
// path:'cert',
// key: 'cert',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/partitions/PartitionInsideCert.tsx')),
// },
// {
// path:'serviceHub',
// component:<Outlet />,
// key:'serviceHub',
// children:[
// {
// path:'',
// key: 'serviceHubList',
// component: <Navigate to="list" />
// },
// {
// path:'list',
// key:'serviceHubList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubList.tsx')),
// },
// {
// path:'detail/:serviceId',
// key:'serviceHubDetail',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/ServiceHubDetail.tsx')),
// }]
// },
// {
// path:'commonsetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/common/CommonPage.tsx')),
// key:uuidv4(),
// },
// {
// path:'consumer',
// component:<Outlet />,
// provider:TenantManagementProvider,
// key:'tenantManagement',
// children:[
// {
// path:'',
// key:'tenantManagementList',
// component:<Navigate to="list" />
// },
// {
// path:':teamId/inside/:appId',
// key:'tenantManagementInside',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsidePage.tsx')),
// children:[
// {
// path:'service',
// key:'tenantManagementInsideService',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideService.tsx')),
// },
// {
// path:'authorization',
// key:'tenantManagementInsideAuthorization',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementInsideAuth.tsx')),
// },
// {
// path:'setting',
// key:'tenantManagementSetting',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ManagementAppSetting.tsx')),
// },
// ]
// },
// {
// path:'list',
// key:'serviceHubManagementList',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
// },
// {
// path:'list/:teamId',
// key:'serviceHubManagementList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@market/pages/serviceHub/management/ServiceHubManagement.tsx')),
// },
// ]
// },
// {
// path:'member',
// key:'member',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberPage.tsx')),
// children:[
// {
// path:'',
// key:'memberList',
// component:<Navigate to="list" />
// },
// {
// path:'list',
// key:'memberList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
// },
// {
// path:'list/:memberGroupId',
// key:'memberList3',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/member/MemberList.tsx')),
// }
// ]
// },
// {
// path:'role',
// key:'role',
// component:<Outlet />,
// children:[
// {
// path: '',
// key: 'roleList',
// component: <Navigate to="list" />
// },
// {
// path:'list',
// key:'roleList2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleList.tsx')),
// },{
// path:':roleType/config/:roleId',
// key:'roleConfig',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
// },{
// path:':roleType/config',
// key:'roleConfig2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/role/RoleConfig.tsx')),
// }
// ]
// },
// {
// path:'analytics',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/Dashboard.tsx')),
// key:'analytics',
// children:[
// {
// path:'total',
// key:'analytics2',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@dashboard/pages/DashboardTotal.tsx')),
// },
// ]
// },
// {
// path:'template/:moduleId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
// key:'intelligentPlugin'
// },
// {
// path:'logsettings/*',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/logsettings/LogSettings.tsx')),
// key: 'logSettings',
// children:[{
// path:'template/:moduleId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
// key:'logSettings2'
// }]
// },
// APP_MODE ==='pro' && {
// path:'resourcesettings/*',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/resourcesettings/ResourceSettings.tsx')),
// key: 'resourceSettings',
// children:[{
// path:'template/:moduleId',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@common/components/aoplatform/intelligent-plugin/IntelligentPluginList.tsx')),
// key:'resourceSettings2'
// }]
// },
// {
// path:'userProfile/*',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/UserProfile.tsx')),
// key:'userProfile',
// children:[{
// path:'changepsw',
// lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/userProfile/ChangePsw.tsx')),
// key:'changePsw'
// }]
// }
// ]
// },
// // ]
import { ApiparkPluginDriver } from '@common/utils/plugin.tsx';
import { routerMap } from '@core/const/const';
import withRouteGuard from "@common/components/aoplatform/WithRouteGuard.tsx";
import ErrorBoundary from "@common/components/aoplatform/ErrorBoundary";
import React from 'react';
import { LoadingOutlined } from '@ant-design/icons';
const RenderRoutes = ()=> {
const { loadPlugins } = usePluginLoader()
const [routeConfig , setRouteConfig] = useState<RouteConfig[]>([])
const { loadPlugins,loadExecutedPlugin } = usePluginLoader(ApiparkPluginDriver(routerMap), routerMap)
const { routeConfig } = useGlobalContext();
const [router, setRouter] = useState<unknown>(null);
useEffect(()=>{
loadPlugins().then((res)=>{
console.log('newRouteConfig',res)
setRouteConfig(res as RouteConfig[])
loadPlugins().then(()=>{
loadExecutedPlugin()
})
},[])
useEffect(() => {
if (routeConfig && routeConfig.length > 0) {
const routerInstance = createBrowserRouter(generateRoutes(routeConfig));
setRouter(routerInstance);
}
}, [routeConfig]);
if (!router) {
return <Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={true} className='w-full h-full flex items-center justify-center'></Spin>;
}
return (
<App className="h-full" message={{ maxCount: 1 }}>
<Router>
<Routes>
{ generateRoutes(routeConfig)}
</Routes>
</Router>
</App>
)
<RouterProvider router={router} />
);
}
const generateRoutes = (routerConfig: RouteConfig[]) => {
const generateRoutes = (routerConfig: RouteConfig[]):RouteObject[] => {
return routerConfig?.map((route: RouteConfig) => {
let routeElement;
if (route.lazy) {
const LazyComponent = route.lazy as React.ExoticComponent<unknown>;
let LazyComponent;
if (typeof route.lazy === 'function') {
const result = route.lazy();
if (result instanceof Promise) {
LazyComponent = React.lazy(() => result.then(module => ({ default: module.default || module })));
} else {
LazyComponent = result;
}
} else {
LazyComponent = route.lazy;
}
const GuardedComponent = withRouteGuard(LazyComponent, {pathPrefix:`/${route.pathPrefix ?? route.path}`, ...route.lifecycle});
routeElement = (
<Suspense fallback={ <div className=''><Skeleton className='m-btnbase w-calc-100vw-minus-padding-r' active /></div>}>
{route.provider ? (
createElement(route.provider, {}, <LazyComponent />)
createElement(route.provider, {}, <GuardedComponent />)
) : (
<LazyComponent />
<GuardedComponent />
)}
</Suspense>
);
} else {
routeElement = route.provider ? (
createElement(route.provider, {}, route.component)
createElement(route.provider, {}, route.component)
) : (
route.component
);
}
return (
<Route
key={route.key}
path={route.path}
element={routeElement}
>
{route.children && generateRoutes(route.children as RouteConfig[])}
</Route>
{
path: route.path,
element: <ErrorBoundary>{routeElement}</ErrorBoundary> ,
children: route.children ? generateRoutes(route.children as RouteConfig[]) : undefined,}
);
}
)
}
// 保护的路由组件
function ProtectedRoute() {
export function ProtectedRoute() {
const {state} = useGlobalContext()
return state.isAuthenticated? <BasicLayout project="core" /> : <Navigate to="/login" />;
}
@@ -1,18 +1,19 @@
import { RouterMapConfig } from '@common/const/type';
import { ProtectedRoute } from '@core/components/aoplatform/RenderRoutes';
import { AiServiceProvider } from '@core/contexts/AiServiceContext';
import { SystemProvider } from '@core/contexts/SystemContext';
import { TeamProvider } from '@core/contexts/TeamContext';
import AiServiceOutlet from '@core/pages/aiService/AiServiceOutlet';
import Guide from '@core/pages/guide/Guide';
import Login from '@core/pages/Login';
import SystemOutlet from '@core/pages/system/SystemOutlet';
import { TenantManagementProvider } from '@market/contexts/TenantManagementContext';
import { lazy } from 'react';
import { Outlet, Navigate } from 'react-router-dom';
import {ProtectedRoute} from '@businessEntry/components/aoplatform/RenderRoutes';
import Guide from '@core/pages/guide/Guide';
import { AiServiceProvider } from '@core/contexts/AiServiceContext';
import AiServiceOutlet from '@core/pages/aiService/AiServiceOutlet';
import React from 'react';
// 内置插件与对应组件/模块
export const routerMap:Map<string, unknown> = new Map([
export const routerMap:Map<string, RouterMapConfig> = new Map([
['basicLayout', { type: 'component', component: <ProtectedRoute />}],
['navHidden', { type: 'component', component: <ProtectedRoute /> }],
['login', { type: 'component', component: <Login /> }],
@@ -104,7 +105,7 @@ import React from 'react';
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterCreate')),
},
{
path:'router',
path:'route',
key: 'restServiceInsideRoute',
lazy:lazy(() => import(/* webpackChunkName: "[request]" */ '@core/pages/system/api/SystemInsideRouterList')),
},
@@ -1,23 +0,0 @@
import {FC, createContext, useContext, useState, ReactNode } from 'react';
import { PartitionConfigFieldType } from '../const/partitions/types.ts';
interface PartitionContextProps {
partitionInfo:PartitionConfigFieldType|undefined
setPartitionInfo:React.Dispatch<React.SetStateAction<PartitionConfigFieldType|undefined>>;
}
const PartitionContext = createContext<PartitionContextProps | undefined>(undefined);
export const usePartitionContext = () => {
const context = useContext(PartitionContext);
if (!context) {
throw new Error('useArray must be used within a ArrayProvider');
}
return context;
};
export const PartitionProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [partitionInfo, setPartitionInfo] = useState<PartitionConfigFieldType>()
return <PartitionContext.Provider value={{ partitionInfo, setPartitionInfo }}>{children}</PartitionContext.Provider>;
};
@@ -1,23 +0,0 @@
import { createContext, useContext, useState, ReactNode, FC } from 'react';
import { MyServiceFieldType } from '../const/system/type.ts';
interface SystemMyServiceContextProps {
serviceInfo:MyServiceFieldType|undefined
setServiceInfo:React.Dispatch<React.SetStateAction<MyServiceFieldType|undefined>>;
}
const SystemMyServiceContext = createContext<SystemMyServiceContextProps | undefined>(undefined);
export const useSystemMyServiceContext = () => {
const context = useContext(SystemMyServiceContext);
if (!context) {
throw new Error('useArray must be used within a ArrayProvider');
}
return context;
};
export const SystemMyServiceProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [serviceInfo, setServiceInfo] = useState<MyServiceFieldType>()
return <SystemMyServiceContext.Provider value={{ serviceInfo, setServiceInfo }}>{children}</SystemMyServiceContext.Provider>;
};
-4
View File
@@ -3,8 +3,6 @@ import {StrictMode} from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import {GlobalProvider} from "@common/contexts/GlobalStateContext.tsx";
async function initializeApp() {
try {
// 初始化行为
@@ -13,9 +11,7 @@ async function initializeApp() {
// 异步操作完成后,渲染React消费者
ReactDOM.createRoot(document.getElementById('root')!).render(
<StrictMode>
<GlobalProvider>
<App />
</GlobalProvider>
</StrictMode>,
);
} catch (error) {
+1 -1
View File
@@ -206,7 +206,7 @@ const Login:FC = ()=> {
<section className="flex flex-col items-center mt-[46px] text-SECOND_TEXT">
<p className="leading-[28px]">
{$t('Version (0)-(1)',[state.version,state.updateDate])}, {$t(state.powered)}
{$t('Version (0)-(1)',[state?.version,state?.updateDate])}, {$t(state?.powered || '-')}
</p>
<LanguageSetting mode="light"/>
</section>
@@ -42,7 +42,6 @@ const AiServiceInsidePage:FC = ()=> {
const getApiDefine = ()=>{
console.log('@@@@@@@')
setApiPrefix('')
setPrefixForce(false)
fetchData<BasicResponse<{ prefix:string, force:boolean }>>('service/router/define',{method:'GET',eoParams:{service:serviceId,team:teamId}}).then(response=>{
@@ -79,7 +79,7 @@ const SystemInsideUpstreamContent= forwardRef<SystemInsideUpstreamContentHandle>
if(code === STATUS_CODE.SUCCESS){
setTimeout(()=>{
form.setFieldsValue({...DEFAULT_FORM_VALUE,...data.upstream})
setFormShowHost(data.upstream.passHost === 'rewrite')
setFormShowHost(data.upstream?.passHost === 'rewrite')
},0)
}else{
message.error(msg || $t(RESPONSE_TIPS.error))
@@ -31,7 +31,6 @@ const TeamList:FC = ()=>{
const [curTeam, setCurTeam] = useState<TeamConfigFieldType>({} as TeamConfigFieldType)
const [modalVisible, setModalVisible] = useState<boolean>(false)
const [modalType, setModalType] = useState<'add'|'edit'>('add')
const getTeamList = ()=>{
if(!accessInit){
getGlobalAccessData()?.then?.(()=>{getTeamList()})
+12
View File
@@ -5,6 +5,7 @@ import path from 'path'
import dynamicImportVars from '@rollup/plugin-dynamic-import-vars';
import tailwindcss from 'tailwindcss';
import autoprefixer from 'autoprefixer';
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
cacheDir: './node_modules/.vite',
@@ -42,6 +43,17 @@ export default defineConfig({
exclude:[],
warnOnError:false
}),
federation({
name:"container",
remotes:{
remoteApp: 'http://localhost:5001/assets/remoteEntry.js' // 远程项目的URL
},
shared:[
"react",
"react-dom",
]
})
],
resolve: {
alias: [
@@ -78,7 +78,6 @@ export const ManagementAuthorityConfig = forwardRef<ManagementAuthorityConfigHan
useEffect(() => {
//console.log(data)
if(type === 'edit' && data){
form.setFieldsValue({...data,expireTime:data.expireTime === 0 ? '' : dayjs(data.expireTime * 1000)})
forceUpdate({})
@@ -110,7 +110,7 @@ export default function ServiceHubManagement() {
}))
);
if (!teamId && data.teams?.[0]?.id) {
navigateTo(data.teams[0].id);
navigateTo(`/consumer/list/${data.teams[0].id}`);
}
} else {
message.error(msg || $t(RESPONSE_TIPS.error));