@chatbi-v/cli 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/.env +8 -0
- package/dist/app/.env.hbs +8 -0
- package/dist/app/README.md.hbs +16 -0
- package/dist/app/chatbi.config.ts.hbs +60 -0
- package/{templates/default/apps/main → dist/app}/index.html.hbs +3 -4
- package/dist/app/package.json.hbs +34 -0
- package/dist/app/src/App.tsx.hbs +92 -0
- package/dist/app/src/components/GlobalErrorBoundary.tsx.hbs +69 -0
- package/dist/app/src/components/GlobalSettingsModal.tsx.hbs +35 -0
- package/dist/app/src/components/LayoutSkeletons.tsx.hbs +79 -0
- package/dist/app/src/components/index.ts.hbs +2 -0
- package/dist/app/src/custom-antd.less.hbs +7 -0
- package/dist/app/src/features/settings/ConfigRenderStrategy.tsx.hbs +119 -0
- package/dist/app/src/features/settings/ExtensionSettings.tsx.hbs +52 -0
- package/dist/app/src/features/settings/PluginList.tsx.hbs +115 -0
- package/dist/app/src/features/settings/PluginSettings.tsx.hbs +123 -0
- package/dist/app/src/features/settings/SchemaSettingsRenderer.tsx.hbs +56 -0
- package/dist/app/src/hooks/useAppRoutes.ts.hbs +39 -0
- package/dist/app/src/hooks/usePluginLoader.ts.hbs +22 -0
- package/dist/app/src/hooks/usePluginSettings.ts.hbs +29 -0
- package/dist/app/src/hooks/useThemeSync.ts.hbs +108 -0
- package/dist/app/src/index.css.hbs +45 -0
- package/dist/app/src/layouts/BackgroundEffects.tsx.hbs +10 -0
- package/dist/app/src/layouts/MainContent.tsx.hbs +58 -0
- package/dist/app/src/layouts/SidebarNav.tsx.hbs +182 -0
- package/dist/app/src/main.tsx.hbs +43 -0
- package/dist/app/src/providers/AppProviders.tsx.hbs +36 -0
- package/dist/app/src/services/api/index.ts.hbs +37 -0
- package/dist/app/src/services/api/modules/auth.ts.hbs +18 -0
- package/dist/app/src/services/config-service.ts.hbs +48 -0
- package/dist/app/src/stores/storage-adapter.ts.hbs +29 -0
- package/dist/app/src/stores/useSessionStore.ts.hbs +22 -0
- package/dist/app/src/stores/useUIStore.ts.hbs +64 -0
- package/dist/app/tailwind.config.cjs.hbs +14 -0
- package/dist/app/tsconfig.json.hbs +26 -0
- package/dist/app/vite.config.ts.hbs +89 -0
- package/dist/index.js +5662 -4194
- package/{templates/default → dist/monorepo}/package.json.hbs +5 -0
- package/dist/monorepo/pnpm-workspace.yaml.hbs +10 -0
- package/dist/{default → monorepo}/tsconfig.json.hbs +3 -1
- package/{templates/default/plugins/demo-plugin → dist/plugin}/package.json.hbs +9 -3
- package/dist/plugin/src/index.tsx.hbs +90 -0
- package/dist/plugin/tsconfig.json.hbs +14 -0
- package/package.json +18 -6
- package/templates/app/.env.hbs +8 -0
- package/templates/app/README.md.hbs +16 -0
- package/templates/app/chatbi.config.ts.hbs +60 -0
- package/{dist/default/apps/main → templates/app}/index.html.hbs +3 -4
- package/templates/app/package.json.hbs +34 -0
- package/templates/app/src/App.tsx.hbs +92 -0
- package/templates/app/src/components/GlobalErrorBoundary.tsx.hbs +69 -0
- package/templates/app/src/components/GlobalSettingsModal.tsx.hbs +35 -0
- package/templates/app/src/components/LayoutSkeletons.tsx.hbs +79 -0
- package/templates/app/src/components/index.ts.hbs +2 -0
- package/templates/app/src/custom-antd.less.hbs +7 -0
- package/templates/app/src/features/settings/ConfigRenderStrategy.tsx.hbs +119 -0
- package/templates/app/src/features/settings/ExtensionSettings.tsx.hbs +52 -0
- package/templates/app/src/features/settings/PluginList.tsx.hbs +115 -0
- package/templates/app/src/features/settings/PluginSettings.tsx.hbs +123 -0
- package/templates/app/src/features/settings/SchemaSettingsRenderer.tsx.hbs +56 -0
- package/templates/app/src/hooks/useAppRoutes.ts.hbs +39 -0
- package/templates/app/src/hooks/usePluginLoader.ts.hbs +22 -0
- package/templates/app/src/hooks/usePluginSettings.ts.hbs +29 -0
- package/templates/app/src/hooks/useThemeSync.ts.hbs +108 -0
- package/templates/app/src/index.css.hbs +45 -0
- package/templates/app/src/layouts/BackgroundEffects.tsx.hbs +10 -0
- package/templates/app/src/layouts/MainContent.tsx.hbs +58 -0
- package/templates/app/src/layouts/SidebarNav.tsx.hbs +182 -0
- package/templates/app/src/main.tsx.hbs +43 -0
- package/templates/app/src/providers/AppProviders.tsx.hbs +36 -0
- package/templates/app/src/services/api/index.ts.hbs +37 -0
- package/templates/app/src/services/api/modules/auth.ts.hbs +18 -0
- package/templates/app/src/services/config-service.ts.hbs +48 -0
- package/templates/app/src/stores/storage-adapter.ts.hbs +29 -0
- package/templates/app/src/stores/useSessionStore.ts.hbs +22 -0
- package/templates/app/src/stores/useUIStore.ts.hbs +64 -0
- package/templates/app/tailwind.config.cjs.hbs +14 -0
- package/templates/app/tsconfig.json.hbs +26 -0
- package/templates/app/vite.config.ts.hbs +89 -0
- package/templates/monorepo/.gitignore.hbs +3 -0
- package/{dist/default → templates/monorepo}/package.json.hbs +5 -0
- package/templates/monorepo/pnpm-workspace.yaml.hbs +10 -0
- package/templates/{default → monorepo}/tsconfig.json.hbs +3 -1
- package/{dist/default/plugins/demo-plugin → templates/plugin}/package.json.hbs +9 -3
- package/templates/plugin/src/index.tsx.hbs +90 -0
- package/templates/plugin/tsconfig.json.hbs +14 -0
- package/dist/default/apps/main/package.json.hbs +0 -20
- package/dist/default/apps/main/src/App.tsx.hbs +0 -162
- package/dist/default/apps/main/src/components/NavIcon.tsx.hbs +0 -41
- package/dist/default/apps/main/src/hooks/usePluginLoader.ts.hbs +0 -25
- package/dist/default/apps/main/src/index.css.hbs +0 -8
- package/dist/default/apps/main/src/main.tsx.hbs +0 -13
- package/dist/default/apps/main/src/pages/Guide.tsx.hbs +0 -133
- package/dist/default/apps/main/tailwind.config.cjs.hbs +0 -17
- package/dist/default/apps/main/tsconfig.json.hbs +0 -10
- package/dist/default/apps/main/vite.config.ts.hbs +0 -16
- package/dist/default/plugins/demo-plugin/src/index.tsx.hbs +0 -44
- package/dist/default/plugins/demo-plugin/tsconfig.json.hbs +0 -10
- package/dist/default/pnpm-workspace.yaml.hbs +0 -3
- package/templates/default/apps/main/package.json.hbs +0 -20
- package/templates/default/apps/main/src/App.tsx.hbs +0 -162
- package/templates/default/apps/main/src/components/NavIcon.tsx.hbs +0 -41
- package/templates/default/apps/main/src/hooks/usePluginLoader.ts.hbs +0 -25
- package/templates/default/apps/main/src/index.css.hbs +0 -8
- package/templates/default/apps/main/src/main.tsx.hbs +0 -13
- package/templates/default/apps/main/src/pages/Guide.tsx.hbs +0 -133
- package/templates/default/apps/main/tailwind.config.cjs.hbs +0 -17
- package/templates/default/apps/main/tsconfig.json.hbs +0 -10
- package/templates/default/apps/main/vite.config.ts.hbs +0 -16
- package/templates/default/plugins/demo-plugin/src/index.tsx.hbs +0 -44
- package/templates/default/plugins/demo-plugin/tsconfig.json.hbs +0 -10
- package/templates/default/pnpm-workspace.yaml.hbs +0 -3
- /package/dist/{default/apps/main → app}/postcss.config.cjs.hbs +0 -0
- /package/{templates/default → dist/monorepo}/.gitignore.hbs +0 -0
- /package/dist/{default → monorepo}/README.md.hbs +0 -0
- /package/templates/{default/apps/main → app}/postcss.config.cjs.hbs +0 -0
- /package/templates/{default → monorepo}/README.md.hbs +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@import 'antd/dist/reset.css';
|
|
2
|
+
@import './custom-antd.less'; /* 注入变量 */
|
|
3
|
+
|
|
4
|
+
@tailwind base;
|
|
5
|
+
@tailwind components;
|
|
6
|
+
@tailwind utilities;
|
|
7
|
+
|
|
8
|
+
@layer base {
|
|
9
|
+
:root {
|
|
10
|
+
/* Default Dark Theme (Slate/Indigo) */
|
|
11
|
+
--color-background: 15 23 42; /* slate-900 #0f172a */
|
|
12
|
+
--color-surface: 30 41 59; /* slate-800 #1e293b */
|
|
13
|
+
--color-primary: 99 102 241; /* indigo-500 #6366f1 */
|
|
14
|
+
--color-secondary: 168 85 247; /* purple-500 #a855f7 */
|
|
15
|
+
--color-accent: 34 211 238; /* cyan-400 #22d3ee */
|
|
16
|
+
--color-text-main: 226 232 240; /* slate-200 */
|
|
17
|
+
--color-text-muted: 148 163 184; /* slate-400 */
|
|
18
|
+
--color-border: 51 65 85; /* slate-700 */
|
|
19
|
+
|
|
20
|
+
/* Layout Variables Default (Compact) */
|
|
21
|
+
--layout-sidebar-width: 240px;
|
|
22
|
+
--layout-content-padding: 16px;
|
|
23
|
+
--layout-gap: 8px;
|
|
24
|
+
--layout-font-scale: 1;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
[data-theme='light'] {
|
|
28
|
+
/* Light Theme */
|
|
29
|
+
--color-background: 248 250 252; /* slate-50 */
|
|
30
|
+
--color-surface: 255 255 255; /* white */
|
|
31
|
+
--color-primary: 79 70 229; /* indigo-600 */
|
|
32
|
+
--color-secondary: 147 51 234; /* purple-600 */
|
|
33
|
+
--color-accent: 6 182 212; /* cyan-500 */
|
|
34
|
+
--color-text-main: 15 23 42; /* slate-900 */
|
|
35
|
+
--color-text-muted: 100 116 139; /* slate-500 */
|
|
36
|
+
--color-border: 226 232 240; /* slate-200 */
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
body {
|
|
40
|
+
@apply bg-background text-text-main overflow-hidden antialiased;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@layer utilities {
|
|
45
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export const BackgroundEffects: React.FC = () => {
|
|
4
|
+
return (
|
|
5
|
+
<div className="fixed top-0 left-0 w-full h-full overflow-hidden pointer-events-none z-0">
|
|
6
|
+
<div className="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] bg-primary/10 rounded-full blur-[120px]" />
|
|
7
|
+
<div className="absolute bottom-[-20%] right-[-10%] w-[40%] h-[40%] bg-secondary/10 rounded-full blur-[100px]" />
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { PluginSlot, Slot, StatusBarItemSkeleton } from '@chatbi-v/core';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Navigate, Route, Routes } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
import { ContentSkeleton } from '../components/LayoutSkeletons';
|
|
6
|
+
import { useAppRoutes } from '../hooks/useAppRoutes';
|
|
7
|
+
|
|
8
|
+
interface MainContentProps {
|
|
9
|
+
pluginsLoaded: boolean;
|
|
10
|
+
sharedProps: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const MainContent: React.FC<MainContentProps> = React.memo(({
|
|
14
|
+
pluginsLoaded,
|
|
15
|
+
sharedProps,
|
|
16
|
+
}) => {
|
|
17
|
+
const { routes, defaultRoute } = useAppRoutes();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<main className="flex-1 flex flex-col relative z-10 overflow-hidden bg-white/50 dark:bg-black/20 backdrop-blur-sm">
|
|
21
|
+
{!pluginsLoaded ? (
|
|
22
|
+
<ContentSkeleton />
|
|
23
|
+
) : (
|
|
24
|
+
<>
|
|
25
|
+
<div className="flex-1 relative overflow-hidden min-h-0">
|
|
26
|
+
<Routes>
|
|
27
|
+
{routes.map((route) => (
|
|
28
|
+
<Route
|
|
29
|
+
key={route.path}
|
|
30
|
+
path={route.path}
|
|
31
|
+
element={<route.component {...sharedProps} />}
|
|
32
|
+
/>
|
|
33
|
+
))}
|
|
34
|
+
|
|
35
|
+
{/* 默认路由重定向 */}
|
|
36
|
+
{defaultRoute ? (
|
|
37
|
+
<Route path="/" element={<Navigate to={defaultRoute} replace />} />
|
|
38
|
+
) : (
|
|
39
|
+
<Route path="/" element={<div className="flex h-full items-center justify-center text-slate-500">请在设置中启用至少一个业务插件</div>} />
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
{/* 404 / Catch-all: 重定向到默认路由 */}
|
|
43
|
+
<Route path="*" element={
|
|
44
|
+
defaultRoute ? <Navigate to={defaultRoute} replace /> : <Navigate to="/" replace />
|
|
45
|
+
} />
|
|
46
|
+
</Routes>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
{/* Status Bar Slot */}
|
|
50
|
+
<div className="h-6 bg-slate-100 dark:bg-slate-900 border-t border-slate-200 dark:border-white/5 flex items-center px-2 text-xs text-slate-500 gap-4">
|
|
51
|
+
<div className="flex-1">Ready</div>
|
|
52
|
+
<PluginSlot slot={Slot.StatusBar} skeleton={<StatusBarItemSkeleton />} />
|
|
53
|
+
</div>
|
|
54
|
+
</>
|
|
55
|
+
)}
|
|
56
|
+
</main>
|
|
57
|
+
);
|
|
58
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MenuFoldOutlined,
|
|
3
|
+
MenuUnfoldOutlined,
|
|
4
|
+
SettingOutlined,
|
|
5
|
+
ThunderboltOutlined,
|
|
6
|
+
} from '@ant-design/icons';
|
|
7
|
+
import { AvatarSkeleton,type PluginExtension, pluginManager, PluginSlot, SidebarIconSkeleton, Slot } from '@chatbi-v/core';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { useNavigate } from 'react-router-dom';
|
|
10
|
+
|
|
11
|
+
import { chatbiConfig } from '../../chatbi.config';
|
|
12
|
+
import { NavSkeleton } from '../components/LayoutSkeletons';
|
|
13
|
+
import { useUIStore } from '../stores/useUIStore';
|
|
14
|
+
|
|
15
|
+
// 导航图标组件
|
|
16
|
+
export const NavIcon = React.memo(({
|
|
17
|
+
icon,
|
|
18
|
+
active,
|
|
19
|
+
onClick,
|
|
20
|
+
title,
|
|
21
|
+
label,
|
|
22
|
+
expanded,
|
|
23
|
+
}: {
|
|
24
|
+
icon: React.ReactNode;
|
|
25
|
+
active?: boolean;
|
|
26
|
+
onClick?: () => void;
|
|
27
|
+
title?: string;
|
|
28
|
+
label?: string;
|
|
29
|
+
expanded?: boolean;
|
|
30
|
+
}) => (
|
|
31
|
+
<div
|
|
32
|
+
onClick={onClick}
|
|
33
|
+
title={title}
|
|
34
|
+
className={`flex items-center cursor-pointer transition-all duration-300 relative group
|
|
35
|
+
${expanded ? 'w-full' : 'w-12'} px-3 h-11 rounded-xl
|
|
36
|
+
${
|
|
37
|
+
active
|
|
38
|
+
? 'bg-primary/15 text-primary font-semibold shadow-sm ring-1 ring-primary/20 dark:bg-primary/20'
|
|
39
|
+
: 'text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-white/10 hover:text-slate-900 dark:hover:text-white hover:scale-105 active:scale-95'
|
|
40
|
+
}`}
|
|
41
|
+
>
|
|
42
|
+
<div className="w-6 h-6 flex items-center justify-center shrink-0">
|
|
43
|
+
<div className={`text-xl transition-transform duration-300 ${!expanded && active ? 'scale-110' : ''}`}>{icon}</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div className={`overflow-hidden whitespace-nowrap transition-all duration-300 ease-in-out ${
|
|
47
|
+
expanded ? 'w-auto opacity-100 ml-3' : 'w-0 opacity-0 ml-0'
|
|
48
|
+
}`}>
|
|
49
|
+
<span className="text-sm block">
|
|
50
|
+
{label}
|
|
51
|
+
</span>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
</div>
|
|
55
|
+
));
|
|
56
|
+
|
|
57
|
+
interface SidebarNavProps {
|
|
58
|
+
activeTab: string;
|
|
59
|
+
pluginsLoaded: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const SidebarNav: React.FC<SidebarNavProps> = React.memo(({
|
|
63
|
+
activeTab,
|
|
64
|
+
pluginsLoaded,
|
|
65
|
+
}) => {
|
|
66
|
+
const navigate = useNavigate();
|
|
67
|
+
const {
|
|
68
|
+
collapsed, setCollapsed,
|
|
69
|
+
setIsSettingsOpen, isMaximized
|
|
70
|
+
} = useUIStore();
|
|
71
|
+
|
|
72
|
+
const renderSidebarBottomItem = React.useCallback(({ key, extension }: { key: string, extension: PluginExtension }) => {
|
|
73
|
+
const Component = extension.component;
|
|
74
|
+
return (
|
|
75
|
+
<Component key={key} collapsed={collapsed} />
|
|
76
|
+
);
|
|
77
|
+
}, [collapsed]);
|
|
78
|
+
|
|
79
|
+
const renderSidebarSystemItem = React.useCallback(({ key, extension }: { key: string, extension: PluginExtension }) => {
|
|
80
|
+
const Component = extension.component;
|
|
81
|
+
return (
|
|
82
|
+
<div key={key} className={`flex items-center gap-3 transition-all duration-300 relative group
|
|
83
|
+
${collapsed ? 'w-12 justify-center' : 'w-full px-4'} rounded-xl`}
|
|
84
|
+
>
|
|
85
|
+
{Component ? <Component collapsed={collapsed} /> : null}
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}, [collapsed]);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<nav className={`relative z-20 flex flex-col items-center py-4 gap-[var(--layout-gap)] transition-all duration-500 ease-[cubic-bezier(0.2,0,0,1)]
|
|
92
|
+
border-r border-slate-200/60 dark:border-white/10 backdrop-blur-xl bg-white/80 dark:bg-slate-900/80
|
|
93
|
+
${isMaximized ? 'w-0 overflow-hidden opacity-0 p-0 border-none' : (collapsed ? 'w-[72px]' : 'w-[var(--layout-sidebar-width)]')}
|
|
94
|
+
h-screen
|
|
95
|
+
`}>
|
|
96
|
+
{!pluginsLoaded ? (
|
|
97
|
+
<NavSkeleton />
|
|
98
|
+
) : (
|
|
99
|
+
<>
|
|
100
|
+
{/* 品牌标识 */}
|
|
101
|
+
<div className={`flex items-center gap-3 mb-6 transition-all duration-300 ${!collapsed ? 'w-full px-5' : 'justify-center'}`}>
|
|
102
|
+
<div className="w-12 h-12 rounded-2xl bg-gradient-to-tr from-primary to-secondary flex items-center justify-center shadow-lg shadow-primary/20 shrink-0 group cursor-pointer hover:scale-105 transition-transform duration-300">
|
|
103
|
+
<ThunderboltOutlined className="text-2xl text-white" />
|
|
104
|
+
</div>
|
|
105
|
+
{!collapsed && (
|
|
106
|
+
<div className="flex flex-col animate-in fade-in slide-in-from-left-4 duration-500">
|
|
107
|
+
<span className="font-bold text-xl leading-tight tracking-tight text-slate-800 dark:text-slate-100">{chatbiConfig.system.title}</span>
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
{/* 导航项 */}
|
|
113
|
+
<div className="flex-1 w-full flex flex-col gap-[var(--layout-gap)] overflow-y-auto no-scrollbar items-center transition-all duration-300 px-3 pt-2">
|
|
114
|
+
<PluginSlot
|
|
115
|
+
slot={Slot.Sidebar}
|
|
116
|
+
className="flex flex-col gap-[var(--layout-gap)] w-full items-center"
|
|
117
|
+
skeleton={
|
|
118
|
+
<div className="flex flex-col gap-[var(--layout-gap)] w-full items-center">
|
|
119
|
+
{[1, 2, 3].map(i => <SidebarIconSkeleton key={i} />)}
|
|
120
|
+
</div>
|
|
121
|
+
}
|
|
122
|
+
renderItem={({ key, extension }: { key: string, extension: PluginExtension }) => (
|
|
123
|
+
<NavIcon
|
|
124
|
+
key={key}
|
|
125
|
+
icon={extension.meta?.icon}
|
|
126
|
+
label={extension.meta?.label}
|
|
127
|
+
active={
|
|
128
|
+
// Support multi-level route matching
|
|
129
|
+
// e.g., activeTab='scene/10/tables' should match path='/scene'
|
|
130
|
+
// Remove leading slash for consistency
|
|
131
|
+
activeTab === (extension.meta?.path || extension.meta?.key)?.replace(/^\//, '') ||
|
|
132
|
+
activeTab.startsWith(((extension.meta?.path || extension.meta?.key)?.replace(/^\//, '') || '') + '/')
|
|
133
|
+
}
|
|
134
|
+
onClick={() => navigate(extension.meta?.path || '/')}
|
|
135
|
+
expanded={!collapsed}
|
|
136
|
+
/>
|
|
137
|
+
)}
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
{/* 底部操作区 */}
|
|
142
|
+
<div className="w-full flex flex-col gap-[var(--layout-gap)] mt-auto mb-2 items-center transition-all duration-300 px-3">
|
|
143
|
+
<PluginSlot
|
|
144
|
+
slot={Slot.SidebarBottom}
|
|
145
|
+
className="w-full flex flex-col gap-[var(--layout-gap)]"
|
|
146
|
+
skeleton={<SidebarIconSkeleton />}
|
|
147
|
+
renderItem={renderSidebarBottomItem}
|
|
148
|
+
/>
|
|
149
|
+
<NavIcon
|
|
150
|
+
icon={<SettingOutlined />}
|
|
151
|
+
onClick={() => setIsSettingsOpen(true)}
|
|
152
|
+
title="系统设置"
|
|
153
|
+
label="设置"
|
|
154
|
+
expanded={!collapsed}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<div className={`h-px bg-slate-200 dark:bg-white/10 my-2 transition-all duration-300 ${collapsed ? 'w-8' : 'w-full'}`} />
|
|
158
|
+
|
|
159
|
+
<PluginSlot
|
|
160
|
+
slot={Slot.SidebarSystem}
|
|
161
|
+
className={`w-full transition-all duration-300 ${collapsed ? 'flex justify-center' : ''}`}
|
|
162
|
+
skeleton={
|
|
163
|
+
<div className={`flex items-center gap-3 transition-all duration-300 ${collapsed ? 'w-12 justify-center' : 'w-full px-4'}`}>
|
|
164
|
+
<AvatarSkeleton />
|
|
165
|
+
</div>
|
|
166
|
+
}
|
|
167
|
+
renderItem={renderSidebarSystemItem}
|
|
168
|
+
/>
|
|
169
|
+
|
|
170
|
+
<NavIcon
|
|
171
|
+
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
172
|
+
onClick={() => setCollapsed(!collapsed)}
|
|
173
|
+
title={collapsed ? "展开侧边栏" : "折叠侧边栏"}
|
|
174
|
+
label={collapsed ? "展开" : "折叠"}
|
|
175
|
+
expanded={!collapsed}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</>
|
|
179
|
+
)}
|
|
180
|
+
</nav>
|
|
181
|
+
);
|
|
182
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import 'antd/dist/reset.css'
|
|
2
|
+
import './index.css'
|
|
3
|
+
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import ReactDOM from 'react-dom/client'
|
|
6
|
+
import { HashRouter } from 'react-router-dom'
|
|
7
|
+
|
|
8
|
+
import App from './App'
|
|
9
|
+
import { GlobalErrorBoundary } from './components'
|
|
10
|
+
import { api } from './services/api'
|
|
11
|
+
|
|
12
|
+
// 挂载全局 API 实例供插件使用
|
|
13
|
+
// 插件可以通过 window.chatbi.api 访问核心请求实例
|
|
14
|
+
declare global {
|
|
15
|
+
interface Window {
|
|
16
|
+
chatbi: {
|
|
17
|
+
api: typeof api;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
window.chatbi = {
|
|
23
|
+
api
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// API Mock 策略说明:
|
|
27
|
+
// Mock 逻辑已移至 services/api/index.ts 中统一处理
|
|
28
|
+
// 开启方式:
|
|
29
|
+
// 1. URL 参数: ?mock=true (优先级最高)
|
|
30
|
+
// 2. 环境变量: VITE_USE_MOCK=true (在 .env.development 中配置)
|
|
31
|
+
|
|
32
|
+
// 应用启动
|
|
33
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
34
|
+
<React.StrictMode>
|
|
35
|
+
{/* 全局错误边界,捕获渲染过程中的未处理异常 */}
|
|
36
|
+
<GlobalErrorBoundary>
|
|
37
|
+
{/* 路由模式:默认使用 HashRouter,如果需要 history 模式请改为 BrowserRouter */}
|
|
38
|
+
<HashRouter>
|
|
39
|
+
<App />
|
|
40
|
+
</HashRouter>
|
|
41
|
+
</GlobalErrorBoundary>
|
|
42
|
+
</React.StrictMode>,
|
|
43
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { XProvider } from '@ant-design/x';
|
|
2
|
+
import { ApiProvider, pluginManager, PluginProvider } from '@chatbi-v/core';
|
|
3
|
+
import { App as AntdApp, ConfigProvider } from 'antd';
|
|
4
|
+
import React, { ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
import { useThemeSync } from '../hooks/useThemeSync';
|
|
7
|
+
import { api } from '../services/api';
|
|
8
|
+
|
|
9
|
+
interface AppProvidersProps {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 应用全局 Providers
|
|
15
|
+
* @description 封装所有全局 Context Provider,包括 API, Plugin, Theme, Antd 等
|
|
16
|
+
* @note 使用 reduceRight 组合 Provider,避免嵌套地狱 (Provider Hell)
|
|
17
|
+
*/
|
|
18
|
+
export const AppProviders: React.FC<AppProvidersProps> = ({ children }) => {
|
|
19
|
+
const themeConfig = useThemeSync();
|
|
20
|
+
|
|
21
|
+
// 定义 Provider 列表,顺序从外到内
|
|
22
|
+
const providers: Array<[React.ComponentType<any>, Record<string, any>]> = [
|
|
23
|
+
// 核心服务
|
|
24
|
+
[ApiProvider, { api }],
|
|
25
|
+
[PluginProvider, { manager: pluginManager }],
|
|
26
|
+
// UI 主题配置
|
|
27
|
+
[ConfigProvider, { theme: themeConfig }],
|
|
28
|
+
[AntdApp, {}],
|
|
29
|
+
[XProvider, { theme: themeConfig }],
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// 使用 reduceRight 从内向外包裹 children
|
|
33
|
+
return providers.reduceRight((acc, [Provider, props]) => {
|
|
34
|
+
return <Provider {...props}>{acc}</Provider>;
|
|
35
|
+
}, children);
|
|
36
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ApiEngine, AxiosAdapter, createLogger, isMockMode } from '@chatbi-v/core';
|
|
2
|
+
import { MockAdapter } from '@chatbi-v/mocks';
|
|
3
|
+
|
|
4
|
+
const logger = createLogger('Api');
|
|
5
|
+
|
|
6
|
+
// 从环境变量获取配置
|
|
7
|
+
const baseURL = import.meta.env.VITE_API_BASE_URL || '/api';
|
|
8
|
+
const timeout = Number(import.meta.env.VITE_API_TIMEOUT) || 10000;
|
|
9
|
+
|
|
10
|
+
// 统一 Mock 模式决策
|
|
11
|
+
const useMock = isMockMode();
|
|
12
|
+
|
|
13
|
+
if (useMock) {
|
|
14
|
+
logger.info(`⚠️ Mock 模式已启用 (基于核心库 isMockMode 判断)`);
|
|
15
|
+
} else {
|
|
16
|
+
logger.debug(`正在以真实模式运行`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 创建适配器 (根据 Mock 模式切换)
|
|
20
|
+
const adapter = useMock
|
|
21
|
+
? new MockAdapter(100) // 100ms 延迟模拟
|
|
22
|
+
: new AxiosAdapter(baseURL, timeout);
|
|
23
|
+
|
|
24
|
+
// 创建 API 引擎实例
|
|
25
|
+
export const api = new ApiEngine(adapter);
|
|
26
|
+
|
|
27
|
+
// 自动加载 ./modules 下的所有配置文件
|
|
28
|
+
// 使用 Vite 的 import.meta.glob 功能
|
|
29
|
+
const modules = import.meta.glob('./modules/*.ts', { eager: true });
|
|
30
|
+
|
|
31
|
+
Object.entries(modules).forEach(([path, module]: [string, any]) => {
|
|
32
|
+
// 从路径中提取模块名 (e.g., './modules/auth.ts' -> 'auth')
|
|
33
|
+
const name = path.match(/\.\/modules\/(.*)\.ts$/)?.[1];
|
|
34
|
+
if (name && module.default) {
|
|
35
|
+
api.register({ [name]: module.default });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
login: {
|
|
3
|
+
url: '/auth/login',
|
|
4
|
+
method: 'POST',
|
|
5
|
+
desc: '用户登录',
|
|
6
|
+
mock: true,
|
|
7
|
+
mockData: {
|
|
8
|
+
code: 0,
|
|
9
|
+
message: 'success',
|
|
10
|
+
data: { token: 'mock-token-123', user: { id: 1, name: 'Admin' } },
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
logout: {
|
|
14
|
+
url: '/auth/logout',
|
|
15
|
+
method: 'POST',
|
|
16
|
+
desc: '用户登出',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { pluginManager, dateUtils } from '@chatbi-v/core';
|
|
2
|
+
|
|
3
|
+
export const exportSystemConfig = () => {
|
|
4
|
+
const exportData: Record<string, any> = {
|
|
5
|
+
pluginStates: {},
|
|
6
|
+
configs: {},
|
|
7
|
+
timestamp: dateUtils.dayjs().toISOString(),
|
|
8
|
+
environment: {
|
|
9
|
+
userAgent: navigator.userAgent,
|
|
10
|
+
language: navigator.language,
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// 1. Export Plugin States
|
|
15
|
+
const allPlugins = pluginManager.getPlugins();
|
|
16
|
+
allPlugins.forEach((p) => {
|
|
17
|
+
exportData.pluginStates[p.id] = pluginManager.getPluginState(p.id);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// 2. Export Configs (scan localStorage for plugin related keys)
|
|
21
|
+
// We look for keys starting with 'plugin:' and global config keys
|
|
22
|
+
const globalKeys = ['chatbi-theme', 'chatbi-layout'];
|
|
23
|
+
|
|
24
|
+
// Global configs
|
|
25
|
+
globalKeys.forEach(key => {
|
|
26
|
+
const val = localStorage.getItem(key);
|
|
27
|
+
if (val) exportData.configs[key] = val;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Plugin specific configs and System configs
|
|
31
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
32
|
+
const key = localStorage.key(i);
|
|
33
|
+
if (key && (key.startsWith('plugin:') || key.startsWith('system:'))) {
|
|
34
|
+
exportData.configs[key] = localStorage.getItem(key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Create and download file
|
|
39
|
+
const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
|
|
40
|
+
const url = URL.createObjectURL(blob);
|
|
41
|
+
const a = document.createElement('a');
|
|
42
|
+
a.href = url;
|
|
43
|
+
a.download = `chatbi-config-${dateUtils.formatDate()}.json`;
|
|
44
|
+
document.body.appendChild(a);
|
|
45
|
+
a.click();
|
|
46
|
+
document.body.removeChild(a);
|
|
47
|
+
URL.revokeObjectURL(url);
|
|
48
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { pluginManager } from '@chatbi-v/core';
|
|
2
|
+
import { StateStorage } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
export const createZustandStorage = (scope: 'system' | 'shared' = 'system'): StateStorage => {
|
|
5
|
+
return {
|
|
6
|
+
getItem: (name: string): string | null => {
|
|
7
|
+
// Lazy access to ensure pluginManager is ready (though it's a singleton)
|
|
8
|
+
const storageManager = pluginManager.getStorageManager();
|
|
9
|
+
const storage = scope === 'system'
|
|
10
|
+
? storageManager.getSystemStorage()
|
|
11
|
+
: storageManager.getSharedStorage();
|
|
12
|
+
return storage.getItem(name);
|
|
13
|
+
},
|
|
14
|
+
setItem: (name: string, value: string): void => {
|
|
15
|
+
const storageManager = pluginManager.getStorageManager();
|
|
16
|
+
const storage = scope === 'system'
|
|
17
|
+
? storageManager.getSystemStorage()
|
|
18
|
+
: storageManager.getSharedStorage();
|
|
19
|
+
storage.setItem(name, value);
|
|
20
|
+
},
|
|
21
|
+
removeItem: (name: string): void => {
|
|
22
|
+
const storageManager = pluginManager.getStorageManager();
|
|
23
|
+
const storage = scope === 'system'
|
|
24
|
+
? storageManager.getSystemStorage()
|
|
25
|
+
: storageManager.getSharedStorage();
|
|
26
|
+
storage.removeItem(name);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { createJSONStorage,persist } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
import { createZustandStorage } from './storage-adapter';
|
|
5
|
+
|
|
6
|
+
export interface SessionState {
|
|
7
|
+
activeSession: string | null;
|
|
8
|
+
setActiveSession: (id: string | null) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useSessionStore = create<SessionState>()(
|
|
12
|
+
persist(
|
|
13
|
+
(set) => ({
|
|
14
|
+
activeSession: null,
|
|
15
|
+
setActiveSession: (id) => set({ activeSession: id }),
|
|
16
|
+
}),
|
|
17
|
+
{
|
|
18
|
+
name: 'chatbi-session-storage',
|
|
19
|
+
storage: createJSONStorage(() => createZustandStorage('system')),
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { createJSONStorage,persist } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
import { createZustandStorage } from './storage-adapter';
|
|
5
|
+
|
|
6
|
+
export interface UIState {
|
|
7
|
+
collapsed: boolean;
|
|
8
|
+
toggleCollapsed: () => void;
|
|
9
|
+
setCollapsed: (v: boolean) => void;
|
|
10
|
+
|
|
11
|
+
isRightPanelOpen: boolean;
|
|
12
|
+
toggleRightPanel: () => void;
|
|
13
|
+
setIsRightPanelOpen: (v: boolean) => void;
|
|
14
|
+
|
|
15
|
+
isMaximized: boolean;
|
|
16
|
+
toggleMaximized: () => void;
|
|
17
|
+
setIsMaximized: (v: boolean) => void;
|
|
18
|
+
|
|
19
|
+
isSettingsOpen: boolean;
|
|
20
|
+
setIsSettingsOpen: (v: boolean) => void;
|
|
21
|
+
|
|
22
|
+
currentThemeId: string;
|
|
23
|
+
themeMode: 'dark' | 'light';
|
|
24
|
+
setThemeId: (id: string) => void;
|
|
25
|
+
setThemeMode: (mode: 'dark' | 'light') => void;
|
|
26
|
+
toggleThemeMode: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const useUIStore = create<UIState>()(
|
|
30
|
+
persist(
|
|
31
|
+
(set) => ({
|
|
32
|
+
collapsed: false,
|
|
33
|
+
toggleCollapsed: () => set((state) => ({ collapsed: !state.collapsed })),
|
|
34
|
+
setCollapsed: (v) => set({ collapsed: v }),
|
|
35
|
+
|
|
36
|
+
isRightPanelOpen: true,
|
|
37
|
+
toggleRightPanel: () => set((state) => ({ isRightPanelOpen: !state.isRightPanelOpen })),
|
|
38
|
+
setIsRightPanelOpen: (v) => set({ isRightPanelOpen: v }),
|
|
39
|
+
|
|
40
|
+
isMaximized: false,
|
|
41
|
+
toggleMaximized: () => set((state) => ({ isMaximized: !state.isMaximized })),
|
|
42
|
+
setIsMaximized: (v) => set({ isMaximized: v }),
|
|
43
|
+
|
|
44
|
+
isSettingsOpen: false,
|
|
45
|
+
setIsSettingsOpen: (v) => set({ isSettingsOpen: v }),
|
|
46
|
+
|
|
47
|
+
currentThemeId: 'default',
|
|
48
|
+
themeMode: 'dark',
|
|
49
|
+
setThemeId: (id) => set({ currentThemeId: id }),
|
|
50
|
+
setThemeMode: (mode) => set({ themeMode: mode }),
|
|
51
|
+
toggleThemeMode: () => set((state) => ({ themeMode: state.themeMode === 'light' ? 'dark' : 'light' })),
|
|
52
|
+
}),
|
|
53
|
+
{
|
|
54
|
+
name: 'chatbi-ui-storage',
|
|
55
|
+
storage: createJSONStorage(() => createZustandStorage('system')),
|
|
56
|
+
partialize: (state) => ({
|
|
57
|
+
currentThemeId: state.currentThemeId,
|
|
58
|
+
themeMode: state.themeMode,
|
|
59
|
+
collapsed: state.collapsed,
|
|
60
|
+
isRightPanelOpen: state.isRightPanelOpen
|
|
61
|
+
}),
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
presets: [
|
|
4
|
+
require('@chatbi-v/tailwind-config')
|
|
5
|
+
],
|
|
6
|
+
darkMode: 'class',
|
|
7
|
+
content: [
|
|
8
|
+
'./index.html',
|
|
9
|
+
'./src/**/*.{ts,tsx}',
|
|
10
|
+
'./.chatbi/core/src/**/*.{ts,tsx}',
|
|
11
|
+
'./.chatbi/plugin-*/**/*.{ts,tsx}'
|
|
12
|
+
],
|
|
13
|
+
plugins: []
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "{{tsconfigPath}}",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"isolatedModules": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"declaration": false,
|
|
15
|
+
"declarationMap": false,
|
|
16
|
+
"composite": false,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
"types": ["vite/client", "node"],
|
|
19
|
+
"baseUrl": ".",
|
|
20
|
+
"paths": {
|
|
21
|
+
"@/*": ["./src/*"]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"],
|
|
25
|
+
"references": []
|
|
26
|
+
}
|