jettask 0.2.15__py3-none-any.whl → 0.2.17__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.
- jettask/__init__.py +14 -35
- jettask/{webui/__main__.py → __main__.py} +4 -4
- jettask/api/__init__.py +103 -0
- jettask/api/v1/__init__.py +29 -0
- jettask/api/v1/alerts.py +226 -0
- jettask/api/v1/analytics.py +323 -0
- jettask/api/v1/namespaces.py +134 -0
- jettask/api/v1/overview.py +136 -0
- jettask/api/v1/queues.py +530 -0
- jettask/api/v1/scheduled.py +420 -0
- jettask/api/v1/settings.py +44 -0
- jettask/{webui/api.py → api.py} +4 -46
- jettask/{webui/backend → backend}/main.py +21 -109
- jettask/{webui/backend → backend}/main_unified.py +1 -1
- jettask/{webui/backend → backend}/namespace_api_old.py +3 -30
- jettask/{webui/backend → backend}/namespace_data_access.py +2 -1
- jettask/{webui/backend → backend}/unified_api_router.py +14 -74
- jettask/{core/cli.py → cli.py} +106 -26
- jettask/config/nacos_config.py +386 -0
- jettask/core/app.py +8 -100
- jettask/core/db_manager.py +515 -0
- jettask/core/event_pool.py +5 -2
- jettask/core/unified_manager_base.py +59 -14
- jettask/{webui/db_init.py → db_init.py} +1 -1
- jettask/executors/asyncio.py +2 -2
- jettask/{webui/integrated_gradio_app.py → integrated_gradio_app.py} +1 -1
- jettask/{webui/multi_namespace_consumer.py → multi_namespace_consumer.py} +5 -2
- jettask/{webui/pg_consumer.py → pg_consumer.py} +137 -69
- jettask/{webui/run.py → run.py} +1 -1
- jettask/{webui/run_webui.py → run_webui.py} +4 -4
- jettask/scheduler/manager.py +6 -0
- jettask/scheduler/multi_namespace_scheduler.py +2 -2
- jettask/scheduler/unified_manager.py +5 -5
- jettask/scheduler/unified_scheduler_manager.py +20 -12
- jettask/schemas/__init__.py +166 -0
- jettask/schemas/alert.py +99 -0
- jettask/schemas/backlog.py +122 -0
- jettask/schemas/common.py +139 -0
- jettask/schemas/monitoring.py +181 -0
- jettask/schemas/namespace.py +168 -0
- jettask/schemas/queue.py +83 -0
- jettask/schemas/scheduled_task.py +128 -0
- jettask/schemas/task.py +70 -0
- jettask/services/__init__.py +24 -0
- jettask/services/alert_service.py +454 -0
- jettask/services/analytics_service.py +46 -0
- jettask/services/overview_service.py +978 -0
- jettask/services/queue_service.py +711 -0
- jettask/services/redis_monitor_service.py +151 -0
- jettask/services/scheduled_task_service.py +207 -0
- jettask/services/settings_service.py +758 -0
- jettask/services/task_service.py +157 -0
- jettask/{webui/task_center.py → task_center.py} +30 -8
- jettask/{webui/task_center_client.py → task_center_client.py} +1 -1
- jettask/{webui/config.py → webui_config.py} +6 -1
- jettask/webui_exceptions.py +67 -0
- jettask/webui_sql/verify_database.sql +72 -0
- {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/METADATA +2 -1
- jettask-0.2.17.dist-info/RECORD +150 -0
- {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/entry_points.txt +1 -1
- jettask/webui/backend/data_api.py +0 -3294
- jettask/webui/backend/namespace_api.py +0 -295
- jettask/webui/backend/queue_backlog_api.py +0 -727
- jettask/webui/backend/redis_monitor_api.py +0 -476
- jettask/webui/frontend/index.html +0 -13
- jettask/webui/frontend/package.json +0 -30
- jettask/webui/frontend/src/App.css +0 -109
- jettask/webui/frontend/src/App.jsx +0 -66
- jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
- jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
- jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
- jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
- jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
- jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
- jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
- jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
- jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
- jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
- jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
- jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
- jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
- jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
- jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
- jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
- jettask/webui/frontend/src/components/layout/Header.css +0 -106
- jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
- jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
- jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
- jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
- jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
- jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
- jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
- jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
- jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
- jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
- jettask/webui/frontend/src/index.css +0 -114
- jettask/webui/frontend/src/main.jsx +0 -22
- jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
- jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
- jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
- jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
- jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
- jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
- jettask/webui/frontend/src/pages/Queues.jsx +0 -12
- jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -810
- jettask/webui/frontend/src/pages/Settings.jsx +0 -801
- jettask/webui/frontend/src/pages/Workers.jsx +0 -12
- jettask/webui/frontend/src/services/api.js +0 -159
- jettask/webui/frontend/src/services/queueTrend.js +0 -166
- jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
- jettask/webui/frontend/src/utils/userPreferences.js +0 -154
- jettask/webui/frontend/vite.config.js +0 -26
- jettask/webui/sql/init_database.sql +0 -640
- jettask-0.2.15.dist-info/RECORD +0 -172
- /jettask/{webui/backend → backend}/__init__.py +0 -0
- /jettask/{webui/backend → backend}/api/__init__.py +0 -0
- /jettask/{webui/backend → backend}/api/v1/__init__.py +0 -0
- /jettask/{webui/backend → backend}/api/v1/monitoring.py +0 -0
- /jettask/{webui/backend → backend}/api/v1/namespaces.py +0 -0
- /jettask/{webui/backend → backend}/api/v1/queues.py +0 -0
- /jettask/{webui/backend → backend}/api/v1/tasks.py +0 -0
- /jettask/{webui/backend → backend}/config.py +0 -0
- /jettask/{webui/backend → backend}/core/__init__.py +0 -0
- /jettask/{webui/backend → backend}/core/cache.py +0 -0
- /jettask/{webui/backend → backend}/core/database.py +0 -0
- /jettask/{webui/backend → backend}/core/exceptions.py +0 -0
- /jettask/{webui/backend → backend}/data_access.py +0 -0
- /jettask/{webui/backend → backend}/dependencies.py +0 -0
- /jettask/{webui/backend → backend}/init_meta_db.py +0 -0
- /jettask/{webui/backend → backend}/main_v2.py +0 -0
- /jettask/{webui/backend → backend}/models/__init__.py +0 -0
- /jettask/{webui/backend → backend}/models/requests.py +0 -0
- /jettask/{webui/backend → backend}/models/responses.py +0 -0
- /jettask/{webui/backend → backend}/queue_stats_v2.py +0 -0
- /jettask/{webui/backend → backend}/services/__init__.py +0 -0
- /jettask/{webui/backend → backend}/start.py +0 -0
- /jettask/{webui/cleanup_deprecated_tables.sql → cleanup_deprecated_tables.sql} +0 -0
- /jettask/{webui/gradio_app.py → gradio_app.py} +0 -0
- /jettask/{webui/__init__.py → main.py} +0 -0
- /jettask/{webui/models.py → models.py} +0 -0
- /jettask/{webui/run_monitor.py → run_monitor.py} +0 -0
- /jettask/{webui/schema.sql → schema.sql} +0 -0
- /jettask/{webui/unified_consumer_manager.py → unified_consumer_manager.py} +0 -0
- /jettask/{webui/models → webui_models}/__init__.py +0 -0
- /jettask/{webui/models → webui_models}/namespace.py +0 -0
- /jettask/{webui/sql → webui_sql}/batch_upsert_functions.sql +0 -0
- {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/WHEEL +0 -0
- {jettask-0.2.15.dist-info → jettask-0.2.17.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.15.dist-info → jettask-0.2.17.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
|