feat(plugin): 集成 WASM 插件系统到主服务并修复链路问题
- 新增 erp-plugin crate:插件管理、WASM 运行时、动态表、数据 CRUD - 新增前端插件管理页面(PluginAdmin/PluginCRUDPage)和 API 层 - 新增插件数据迁移(plugins/plugin_entities/plugin_event_subscriptions) - 新增权限补充迁移(为已有租户补充 plugin.admin/plugin.list 权限) - 修复 PluginAdmin 页面 InstallOutlined 图标不存在的崩溃问题 - 修复 settings 唯一索引迁移顺序错误(先去重再建索引) - 更新 wiki 和 CLAUDE.md 反映插件系统集成状态 - 新增 dev.ps1 一键启动脚本
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useCallback, memo } from 'react';
|
||||
import { useCallback, memo, useEffect } from 'react';
|
||||
import { Layout, Avatar, Space, Dropdown, Tooltip, theme } from 'antd';
|
||||
import {
|
||||
HomeOutlined,
|
||||
@@ -14,10 +14,12 @@ import {
|
||||
SearchOutlined,
|
||||
BulbOutlined,
|
||||
BulbFilled,
|
||||
AppstoreOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useAppStore } from '../stores/app';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import { usePluginStore } from '../stores/plugin';
|
||||
import NotificationPanel from '../components/NotificationPanel';
|
||||
|
||||
const { Header, Sider, Content, Footer } = Layout;
|
||||
@@ -42,6 +44,7 @@ const bizMenuItems: MenuItem[] = [
|
||||
|
||||
const sysMenuItems: MenuItem[] = [
|
||||
{ key: '/settings', icon: <SettingOutlined />, label: '系统设置' },
|
||||
{ key: '/plugins/admin', icon: <AppstoreOutlined />, label: '插件管理' },
|
||||
];
|
||||
|
||||
const routeTitleMap: Record<string, string> = {
|
||||
@@ -52,6 +55,7 @@ const routeTitleMap: Record<string, string> = {
|
||||
'/workflow': '工作流',
|
||||
'/messages': '消息中心',
|
||||
'/settings': '系统设置',
|
||||
'/plugins/admin': '插件管理',
|
||||
};
|
||||
|
||||
// 侧边栏菜单项 - 提取为独立组件避免重复渲染
|
||||
@@ -82,11 +86,17 @@ const SidebarMenuItem = memo(function SidebarMenuItem({
|
||||
export default function MainLayout({ children }: { children: React.ReactNode }) {
|
||||
const { sidebarCollapsed, toggleSidebar, theme: themeMode, setTheme } = useAppStore();
|
||||
const { user, logout } = useAuthStore();
|
||||
const { pluginMenuItems, fetchPlugins } = usePluginStore();
|
||||
theme.useToken();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const currentPath = location.pathname || '/';
|
||||
|
||||
// 加载插件菜单
|
||||
useEffect(() => {
|
||||
fetchPlugins(1, 'running');
|
||||
}, [fetchPlugins]);
|
||||
|
||||
const handleLogout = useCallback(async () => {
|
||||
await logout();
|
||||
navigate('/login');
|
||||
@@ -159,6 +169,28 @@ export default function MainLayout({ children }: { children: React.ReactNode })
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 菜单组:插件 */}
|
||||
{pluginMenuItems.length > 0 && (
|
||||
<>
|
||||
{!sidebarCollapsed && <div className="erp-sidebar-group">插件</div>}
|
||||
<div className="erp-sidebar-menu">
|
||||
{pluginMenuItems.map((item) => (
|
||||
<SidebarMenuItem
|
||||
key={item.key}
|
||||
item={{
|
||||
key: item.key,
|
||||
icon: <AppstoreOutlined />,
|
||||
label: item.label,
|
||||
}}
|
||||
isActive={currentPath === item.key}
|
||||
collapsed={sidebarCollapsed}
|
||||
onClick={() => navigate(item.key)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 菜单组:系统 */}
|
||||
{!sidebarCollapsed && <div className="erp-sidebar-group">系统</div>}
|
||||
<div className="erp-sidebar-menu">
|
||||
@@ -187,7 +219,9 @@ export default function MainLayout({ children }: { children: React.ReactNode })
|
||||
{sidebarCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||
</div>
|
||||
<span className={`erp-header-title ${isDark ? 'erp-text-dark' : 'erp-text-light'}`}>
|
||||
{routeTitleMap[currentPath] || '页面'}
|
||||
{routeTitleMap[currentPath] ||
|
||||
pluginMenuItems.find((p) => p.key === currentPath)?.label ||
|
||||
'页面'}
|
||||
</span>
|
||||
</Space>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user