jettask 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl

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.
Files changed (93) hide show
  1. jettask/monitor/run_backlog_collector.py +96 -0
  2. jettask/monitor/stream_backlog_monitor.py +362 -0
  3. jettask/pg_consumer/pg_consumer_v2.py +403 -0
  4. jettask/pg_consumer/sql_utils.py +182 -0
  5. jettask/scheduler/__init__.py +17 -0
  6. jettask/scheduler/add_execution_count.sql +11 -0
  7. jettask/scheduler/add_priority_field.sql +26 -0
  8. jettask/scheduler/add_scheduler_id.sql +25 -0
  9. jettask/scheduler/add_scheduler_id_index.sql +10 -0
  10. jettask/scheduler/loader.py +249 -0
  11. jettask/scheduler/make_scheduler_id_required.sql +28 -0
  12. jettask/scheduler/manager.py +696 -0
  13. jettask/scheduler/migrate_interval_seconds.sql +9 -0
  14. jettask/scheduler/models.py +200 -0
  15. jettask/scheduler/multi_namespace_scheduler.py +294 -0
  16. jettask/scheduler/performance_optimization.sql +45 -0
  17. jettask/scheduler/run_scheduler.py +186 -0
  18. jettask/scheduler/scheduler.py +715 -0
  19. jettask/scheduler/schema.sql +84 -0
  20. jettask/scheduler/unified_manager.py +450 -0
  21. jettask/scheduler/unified_scheduler_manager.py +280 -0
  22. jettask/webui/backend/api/__init__.py +3 -0
  23. jettask/webui/backend/api/v1/__init__.py +17 -0
  24. jettask/webui/backend/api/v1/monitoring.py +431 -0
  25. jettask/webui/backend/api/v1/namespaces.py +504 -0
  26. jettask/webui/backend/api/v1/queues.py +342 -0
  27. jettask/webui/backend/api/v1/tasks.py +367 -0
  28. jettask/webui/backend/core/__init__.py +3 -0
  29. jettask/webui/backend/core/cache.py +221 -0
  30. jettask/webui/backend/core/database.py +200 -0
  31. jettask/webui/backend/core/exceptions.py +102 -0
  32. jettask/webui/backend/models/__init__.py +3 -0
  33. jettask/webui/backend/models/requests.py +236 -0
  34. jettask/webui/backend/models/responses.py +230 -0
  35. jettask/webui/backend/services/__init__.py +3 -0
  36. jettask/webui/frontend/index.html +13 -0
  37. jettask/webui/models/__init__.py +3 -0
  38. jettask/webui/models/namespace.py +63 -0
  39. jettask/webui/sql/batch_upsert_functions.sql +178 -0
  40. jettask/webui/sql/init_database.sql +640 -0
  41. {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/METADATA +80 -10
  42. {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/RECORD +46 -53
  43. jettask/webui/frontend/package-lock.json +0 -4833
  44. jettask/webui/frontend/package.json +0 -30
  45. jettask/webui/frontend/src/App.css +0 -109
  46. jettask/webui/frontend/src/App.jsx +0 -66
  47. jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
  48. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
  49. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
  50. jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
  51. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
  52. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
  53. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
  54. jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
  55. jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
  56. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
  57. jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
  58. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
  59. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
  60. jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
  61. jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
  62. jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
  63. jettask/webui/frontend/src/components/layout/Header.css +0 -106
  64. jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
  65. jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
  66. jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
  67. jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
  68. jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
  69. jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
  70. jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
  71. jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
  72. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
  73. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
  74. jettask/webui/frontend/src/index.css +0 -114
  75. jettask/webui/frontend/src/main.jsx +0 -20
  76. jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
  77. jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
  78. jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
  79. jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
  80. jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
  81. jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
  82. jettask/webui/frontend/src/pages/Queues.jsx +0 -12
  83. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -809
  84. jettask/webui/frontend/src/pages/Settings.jsx +0 -800
  85. jettask/webui/frontend/src/pages/Workers.jsx +0 -12
  86. jettask/webui/frontend/src/services/api.js +0 -114
  87. jettask/webui/frontend/src/services/queueTrend.js +0 -152
  88. jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
  89. jettask/webui/frontend/src/utils/userPreferences.js +0 -154
  90. {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/WHEEL +0 -0
  91. {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/entry_points.txt +0 -0
  92. {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/licenses/LICENSE +0 -0
  93. {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/top_level.txt +0 -0
@@ -1,111 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Card, Empty, Tabs } from 'antd';
3
- import { Column } from '@ant-design/plots';
4
- import QueueTrendChart from './QueueTrendChart';
5
- import { fetchQueueTrend } from '../../services/queueTrend';
6
-
7
- const { TabPane } = Tabs;
8
-
9
- const QueueChart = ({
10
- data,
11
- loading,
12
- queues = [],
13
- timeRange = '1h',
14
- customTimeRange = null,
15
- mode = 'distribution' // 'distribution' | 'trend'
16
- }) => {
17
- const [trendData, setTrendData] = useState([]);
18
- const [activeTab, setActiveTab] = useState('trend');
19
-
20
- // 获取趋势数据
21
- useEffect(() => {
22
- if (queues && queues.length > 0 && (mode === 'trend' || activeTab === 'trend')) {
23
- // 获取队列名称列表
24
- const queueNames = queues.map(q => q.name || q);
25
-
26
- // 从API获取趋势数据
27
- const loadTrendData = async () => {
28
- try {
29
- const data = await fetchQueueTrend(timeRange, queueNames, customTimeRange);
30
- setTrendData(data);
31
- } catch (error) {
32
- console.error('Failed to load trend data:', error);
33
- // 如果失败,使用空数据
34
- setTrendData([]);
35
- }
36
- };
37
-
38
- loadTrendData();
39
- }
40
- }, [queues, timeRange, customTimeRange, mode, activeTab]);
41
-
42
- // 柱状图配置
43
- const columnConfig = {
44
- data: data || [],
45
- xField: 'name',
46
- yField: 'value',
47
- label: {
48
- position: 'middle',
49
- style: {
50
- fill: '#FFFFFF',
51
- opacity: 0.6,
52
- },
53
- },
54
- xAxis: {
55
- label: {
56
- autoHide: true,
57
- autoRotate: false,
58
- },
59
- },
60
- meta: {
61
- name: { alias: '队列' },
62
- value: { alias: '任务数' },
63
- },
64
- };
65
-
66
- // 如果指定为趋势模式,直接返回趋势图
67
- if (mode === 'trend') {
68
- return (
69
- <QueueTrendChart
70
- data={trendData}
71
- loading={loading}
72
- timeRange={timeRange}
73
- />
74
- );
75
- }
76
-
77
- // 如果没有指定模式,显示标签页切换
78
- if (!mode || mode === 'both') {
79
- return (
80
- <Tabs activeKey={activeTab} onChange={setActiveTab}>
81
- <TabPane tab="处理趋势" key="trend">
82
- <QueueTrendChart
83
- data={trendData}
84
- loading={loading}
85
- timeRange={timeRange}
86
- />
87
- </TabPane>
88
- <TabPane tab="队列分布" key="distribution">
89
- {data && data.length > 0 ? (
90
- <Column {...columnConfig} />
91
- ) : (
92
- <Empty description="暂无数据" />
93
- )}
94
- </TabPane>
95
- </Tabs>
96
- );
97
- }
98
-
99
- // 默认显示分布图
100
- return (
101
- <Card title="队列任务分布" loading={loading}>
102
- {data && data.length > 0 ? (
103
- <Column {...columnConfig} />
104
- ) : (
105
- <Empty description="暂无数据" />
106
- )}
107
- </Card>
108
- );
109
- };
110
-
111
- export default QueueChart;
@@ -1,115 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import { Line } from '@ant-design/plots';
3
- import { Empty, Spin } from 'antd';
4
- import dayjs from 'dayjs';
5
-
6
- const QueueTrendChart = ({ data = [], loading = false }) => {
7
- const [chartData, setChartData] = useState([]);
8
-
9
- useEffect(() => {
10
- if (data && data.length > 0) {
11
- // 处理数据,转换为Date对象并按时间排序
12
- const processedData = data.map(item => ({
13
- ...item,
14
- date: new Date(item.time), // 使用date字段,与示例保持一致
15
- value: item.value || 0,
16
- queue: item.queue
17
- }));
18
-
19
- // 按时间排序确保连线正确
20
- processedData.sort((a, b) => a.date.getTime() - b.date.getTime());
21
-
22
- setChartData(processedData);
23
- }
24
- }, [data]);
25
-
26
- // 图表配置 - 参考Ant Design Charts示例
27
- const config = {
28
- data: chartData,
29
- xField: (d) => d.date, // 返回Date对象,让图表库自动处理
30
- yField: 'value',
31
- seriesField: 'queue', // 如果有多个队列,按队列分组
32
-
33
- // 连接空值配置
34
- connectNulls: {
35
- connect: true,
36
- connectStroke: '#ccc',
37
- },
38
-
39
- // X轴会自动优化显示,不需要手动设置formatter
40
- xAxis: {
41
- type: 'time',
42
- nice: true,
43
- },
44
-
45
- // Y轴配置
46
- yAxis: {
47
- label: {
48
- formatter: (v) => {
49
- // 格式化数字显示
50
- if (v >= 1000000) {
51
- return `${(v / 1000000).toFixed(1)}M`;
52
- } else if (v >= 1000) {
53
- return `${(v / 1000).toFixed(1)}K`;
54
- }
55
- return String(v);
56
- }
57
- }
58
- },
59
-
60
- // 悬浮提示配置
61
- tooltip: {
62
- showTitle: true,
63
- title: (title) => {
64
- // 格式化时间显示
65
- return dayjs(title).format('YYYY-MM-DD HH:mm:ss');
66
- },
67
- formatter: (datum) => {
68
- return {
69
- name: datum.queue || '任务数',
70
- value: datum.value.toLocaleString(), // 添加千分位分隔符
71
- };
72
- },
73
- },
74
-
75
- // 平滑曲线
76
- smooth: true,
77
-
78
- // 动画配置
79
- animation: {
80
- appear: {
81
- animation: 'path-in',
82
- duration: 1000,
83
- },
84
- },
85
-
86
- // 响应式配置
87
- autoFit: true,
88
- height: 300,
89
-
90
- // 主题颜色
91
- color: ['#5B8FF9', '#61DDAA', '#65789B', '#F6BD16', '#7262FD'],
92
- };
93
-
94
- // 空数据处理
95
- if (!loading && (!chartData || chartData.length === 0)) {
96
- return (
97
- <div style={{ height: 300, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
98
- <Empty description="暂无数据" />
99
- </div>
100
- );
101
- }
102
-
103
- // 加载状态
104
- if (loading) {
105
- return (
106
- <div style={{ height: 300, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
107
- <Spin size="large" tip="加载中..." />
108
- </div>
109
- );
110
- }
111
-
112
- return <Line {...config} />;
113
- };
114
-
115
- export default QueueTrendChart;
@@ -1,40 +0,0 @@
1
- import React from 'react';
2
- import { Card, Empty } from 'antd';
3
- import { Pie } from '@ant-design/plots';
4
-
5
- const WorkerChart = ({ data, loading }) => {
6
- const chartData = data || [
7
- { type: '在线', value: 0 },
8
- { type: '离线', value: 0 },
9
- ];
10
-
11
- const config = {
12
- appendPadding: 10,
13
- data: chartData,
14
- angleField: 'value',
15
- colorField: 'type',
16
- radius: 0.8,
17
- label: {
18
- type: 'inner',
19
- offset: '-30%',
20
- content: ({ percent }) => `${(percent * 100).toFixed(0)}%`,
21
- style: {
22
- fontSize: 14,
23
- textAlign: 'center',
24
- },
25
- },
26
- interactions: [{ type: 'element-active' }],
27
- };
28
-
29
- return (
30
- <Card title="Worker 状态分布" loading={loading}>
31
- {chartData.some(item => item.value > 0) ? (
32
- <Pie {...config} />
33
- ) : (
34
- <Empty description="暂无数据" />
35
- )}
36
- </Card>
37
- );
38
- };
39
-
40
- export default WorkerChart;
@@ -1,18 +0,0 @@
1
- import React from 'react';
2
- import { Card, Statistic } from 'antd';
3
-
4
- const StatsCard = ({ title, value, icon, prefix, suffix, valueStyle }) => {
5
- return (
6
- <Card>
7
- <Statistic
8
- title={title}
9
- value={value}
10
- prefix={icon}
11
- suffix={suffix}
12
- valueStyle={valueStyle}
13
- />
14
- </Card>
15
- );
16
- };
17
-
18
- export default StatsCard;
@@ -1,95 +0,0 @@
1
- /* 整体布局 */
2
- .app-layout {
3
- height: 100vh;
4
- overflow: hidden;
5
- }
6
-
7
- /* 主布局区域 */
8
- .main-layout {
9
- transition: margin-left 0.2s;
10
- display: flex;
11
- flex-direction: column;
12
- height: 100vh;
13
- }
14
-
15
- /* 顶部导航栏 */
16
- .app-header {
17
- background: #2a2d31;
18
- padding: 0;
19
- height: 48px;
20
- line-height: 48px;
21
- display: flex;
22
- justify-content: space-between;
23
- align-items: center;
24
- border-bottom: 1px solid #f0f2f5;
25
- z-index: 10;
26
- position: sticky;
27
- top: 0;
28
- }
29
-
30
- .header-left {
31
- flex: 1;
32
- display: flex;
33
- align-items: center;
34
- height: 100%;
35
- overflow: hidden;
36
- }
37
-
38
- .header-right {
39
- display: flex;
40
- align-items: center;
41
- height: 100%;
42
- gap: 16px;
43
- }
44
-
45
- /* 内容区域 */
46
- .app-content {
47
- flex: 1;
48
- overflow: auto;
49
- background: #f0f2f5;
50
- padding: 0;
51
- }
52
-
53
- /* 页面内容容器 */
54
- .page-container {
55
- height: 100%;
56
- background: #fff;
57
- }
58
-
59
- /* 滚动条样式 */
60
- .app-content::-webkit-scrollbar {
61
- width: 8px;
62
- height: 8px;
63
- }
64
-
65
- .app-content::-webkit-scrollbar-track {
66
- background: #f0f0f0;
67
- }
68
-
69
- .app-content::-webkit-scrollbar-thumb {
70
- background: #bfbfbf;
71
- border-radius: 4px;
72
- }
73
-
74
- .app-content::-webkit-scrollbar-thumb:hover {
75
- background: #8c8c8c;
76
- }
77
-
78
- /* 响应式调整 */
79
- @media (max-width: 768px) {
80
- .main-layout {
81
- margin-left: 0 !important;
82
- }
83
-
84
- .app-sider {
85
- position: fixed;
86
- z-index: 1000;
87
- height: 100vh;
88
- left: -200px;
89
- transition: left 0.2s;
90
- }
91
-
92
- .app-sider.ant-layout-sider-collapsed {
93
- left: 0;
94
- }
95
- }
@@ -1,49 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { Layout } from 'antd';
3
- import { Outlet } from 'react-router-dom';
4
- import SideMenu from './SideMenu';
5
- import TabsNav from './TabsNav';
6
- import UserInfo from './UserInfo';
7
- import NamespaceSelector from '../NamespaceSelector';
8
- import { useNamespace } from '../../contexts/NamespaceContext';
9
- import './AppLayout.css';
10
-
11
- const { Header, Content } = Layout;
12
-
13
- const AppLayout = () => {
14
- const { currentNamespace, setCurrentNamespace } = useNamespace();
15
-
16
- console.log('🔧 AppLayout渲染,currentNamespace:', currentNamespace);
17
- console.log('🔧 AppLayout渲染,setCurrentNamespace:', typeof setCurrentNamespace);
18
-
19
- return (
20
- <Layout className="app-layout">
21
- {/* 左侧菜单 */}
22
- <SideMenu />
23
-
24
- {/* 右侧主体 */}
25
- <Layout className="main-layout" style={{ marginLeft: 64 }}>
26
- {/* 顶部导航栏 */}
27
- <Header className="app-header">
28
- <div className="header-left">
29
- <TabsNav />
30
- </div>
31
- <div className="header-right">
32
- <NamespaceSelector
33
- value={currentNamespace}
34
- onChange={setCurrentNamespace}
35
- />
36
- <UserInfo />
37
- </div>
38
- </Header>
39
-
40
- {/* 内容区域 */}
41
- <Content className="app-content">
42
- <Outlet />
43
- </Content>
44
- </Layout>
45
- </Layout>
46
- );
47
- };
48
-
49
- export default AppLayout;
@@ -1,106 +0,0 @@
1
- .app-header {
2
- background: rgba(20, 20, 20, 0.95) !important;
3
- border-bottom: 1px solid rgba(255, 255, 255, 0.08);
4
- padding: 0;
5
- position: relative;
6
- height: 48px !important;
7
- line-height: 48px !important;
8
- }
9
-
10
- .header-container {
11
- max-width: 1440px;
12
- margin: 0 auto;
13
- padding: 0 24px;
14
- display: flex;
15
- align-items: center;
16
- justify-content: space-between;
17
- height: 48px;
18
- }
19
-
20
- .header-left {
21
- display: flex;
22
- align-items: center;
23
- }
24
-
25
- .app-logo {
26
- display: flex;
27
- align-items: center;
28
- gap: 8px;
29
- color: white;
30
- font-size: 16px;
31
- font-weight: 700;
32
- cursor: pointer;
33
- }
34
-
35
- .logo-icon {
36
- font-size: 18px;
37
- color: var(--primary-color);
38
- }
39
-
40
- .logo-text {
41
- background: linear-gradient(45deg, #1890ff, #52c41a);
42
- -webkit-background-clip: text;
43
- -webkit-text-fill-color: transparent;
44
- background-clip: text;
45
- }
46
-
47
- .header-center {
48
- flex: 1;
49
- display: flex;
50
- justify-content: center;
51
- }
52
-
53
- .header-menu {
54
- background: transparent !important;
55
- border: none !important;
56
- line-height: 48px !important;
57
- }
58
-
59
- .header-menu .ant-menu-item {
60
- color: rgba(255, 255, 255, 0.65) !important;
61
- height: 48px !important;
62
- line-height: 48px !important;
63
- }
64
-
65
- .header-menu .ant-menu-item:hover {
66
- color: white !important;
67
- }
68
-
69
- .header-menu .ant-menu-item-selected {
70
- color: var(--primary-color) !important;
71
- }
72
-
73
- .header-right {
74
- display: flex;
75
- align-items: center;
76
- }
77
-
78
- .refresh-btn {
79
- color: rgba(255, 255, 255, 0.65) !important;
80
- }
81
-
82
- .refresh-btn:hover {
83
- color: white !important;
84
- }
85
-
86
- /* 命名空间选择器样式 */
87
- .header-right {
88
- display: flex;
89
- align-items: center;
90
- gap: 16px;
91
- }
92
-
93
- /* 脉动动画 */
94
- @keyframes pulse {
95
- 0% {
96
- opacity: 1;
97
- }
98
- 50% {
99
- opacity: 0.5;
100
- }
101
- 100% {
102
- opacity: 1;
103
- }
104
- }
105
-
106
- /* 响应式设计 */
@@ -1,106 +0,0 @@
1
- import React from 'react'
2
- import { useNavigate, useLocation } from 'react-router-dom'
3
- import { Layout, Menu } from 'antd'
4
- import {
5
- DashboardOutlined,
6
- AppstoreOutlined,
7
- TeamOutlined,
8
- RocketOutlined,
9
- LoadingOutlined,
10
- ClockCircleOutlined,
11
- AlertOutlined,
12
- CloudServerOutlined
13
- } from '@ant-design/icons'
14
- import { useLoading } from '../../contexts/LoadingContext'
15
- import { useNamespace } from '../../contexts/NamespaceContext'
16
- import NamespaceSelector from '../NamespaceSelector'
17
- import './Header.css'
18
-
19
- const { Header: AntHeader } = Layout
20
-
21
- const Header = () => {
22
- const navigate = useNavigate()
23
- const location = useLocation()
24
- const { isLoading } = useLoading()
25
- const { currentNamespace, setCurrentNamespace } = useNamespace()
26
-
27
- const menuItems = [
28
- {
29
- key: '/dashboard',
30
- icon: <DashboardOutlined />,
31
- label: '概览',
32
- },
33
- {
34
- key: '/queues',
35
- icon: <AppstoreOutlined />,
36
- label: '队列',
37
- },
38
- // {
39
- // key: '/workers',
40
- // icon: <TeamOutlined />,
41
- // label: 'Workers',
42
- // },
43
- {
44
- key: '/scheduled-tasks',
45
- icon: <ClockCircleOutlined />,
46
- label: '定时任务',
47
- },
48
- {
49
- key: '/alerts',
50
- icon: <AlertOutlined />,
51
- label: '监控告警',
52
- },
53
- ]
54
-
55
- const handleMenuClick = ({ key }) => {
56
- navigate(key)
57
- }
58
-
59
- return (
60
- <AntHeader className="app-header">
61
- <div className="header-container">
62
- <div className="header-left">
63
- <div className="app-logo">
64
- {isLoading ? (
65
- <LoadingOutlined
66
- className="logo-icon"
67
- style={{
68
- fontSize: 18,
69
- color: '#1890ff'
70
- }}
71
- spin
72
- />
73
- ) : (
74
- <RocketOutlined className="logo-icon" />
75
- )}
76
- <span className="logo-text">JetTask Monitor</span>
77
- </div>
78
- </div>
79
-
80
- <div className="header-center">
81
- <Menu
82
- mode="horizontal"
83
- selectedKeys={[location.pathname]}
84
- items={menuItems}
85
- onClick={handleMenuClick}
86
- className="header-menu"
87
- />
88
- </div>
89
-
90
- <div className="header-right">
91
- <NamespaceSelector
92
- value={currentNamespace}
93
- onChange={(namespace) => {
94
- console.log('🔧 Header收到命名空间切换:', namespace);
95
- setCurrentNamespace(namespace);
96
- }}
97
- style={{ marginLeft: 'auto' }}
98
- />
99
- </div>
100
-
101
- </div>
102
- </AntHeader>
103
- )
104
- }
105
-
106
- export default Header