jettask 0.2.15__py3-none-any.whl → 0.2.16__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 (148) hide show
  1. jettask/__init__.py +14 -35
  2. jettask/{webui/__main__.py → __main__.py} +4 -4
  3. jettask/api/__init__.py +103 -0
  4. jettask/api/v1/__init__.py +29 -0
  5. jettask/api/v1/alerts.py +226 -0
  6. jettask/api/v1/analytics.py +323 -0
  7. jettask/api/v1/namespaces.py +134 -0
  8. jettask/api/v1/overview.py +136 -0
  9. jettask/api/v1/queues.py +530 -0
  10. jettask/api/v1/scheduled.py +420 -0
  11. jettask/api/v1/settings.py +44 -0
  12. jettask/{webui/api.py → api.py} +4 -46
  13. jettask/{webui/backend → backend}/main.py +21 -109
  14. jettask/{webui/backend → backend}/main_unified.py +1 -1
  15. jettask/{webui/backend → backend}/namespace_api_old.py +3 -30
  16. jettask/{webui/backend → backend}/namespace_data_access.py +2 -1
  17. jettask/{webui/backend → backend}/unified_api_router.py +14 -74
  18. jettask/{core/cli.py → cli.py} +106 -26
  19. jettask/config/nacos_config.py +386 -0
  20. jettask/core/app.py +8 -100
  21. jettask/core/db_manager.py +515 -0
  22. jettask/core/event_pool.py +5 -2
  23. jettask/core/unified_manager_base.py +47 -14
  24. jettask/{webui/db_init.py → db_init.py} +1 -1
  25. jettask/executors/asyncio.py +2 -2
  26. jettask/{webui/integrated_gradio_app.py → integrated_gradio_app.py} +1 -1
  27. jettask/{webui/multi_namespace_consumer.py → multi_namespace_consumer.py} +5 -2
  28. jettask/{webui/pg_consumer.py → pg_consumer.py} +137 -69
  29. jettask/{webui/run.py → run.py} +1 -1
  30. jettask/{webui/run_webui.py → run_webui.py} +4 -4
  31. jettask/scheduler/multi_namespace_scheduler.py +2 -2
  32. jettask/scheduler/unified_manager.py +5 -5
  33. jettask/scheduler/unified_scheduler_manager.py +1 -1
  34. jettask/schemas/__init__.py +166 -0
  35. jettask/schemas/alert.py +99 -0
  36. jettask/schemas/backlog.py +122 -0
  37. jettask/schemas/common.py +139 -0
  38. jettask/schemas/monitoring.py +181 -0
  39. jettask/schemas/namespace.py +168 -0
  40. jettask/schemas/queue.py +83 -0
  41. jettask/schemas/scheduled_task.py +128 -0
  42. jettask/schemas/task.py +70 -0
  43. jettask/services/__init__.py +24 -0
  44. jettask/services/alert_service.py +454 -0
  45. jettask/services/analytics_service.py +46 -0
  46. jettask/services/overview_service.py +978 -0
  47. jettask/services/queue_service.py +711 -0
  48. jettask/services/redis_monitor_service.py +151 -0
  49. jettask/services/scheduled_task_service.py +207 -0
  50. jettask/services/settings_service.py +758 -0
  51. jettask/services/task_service.py +157 -0
  52. jettask/{webui/task_center.py → task_center.py} +30 -8
  53. jettask/{webui/task_center_client.py → task_center_client.py} +1 -1
  54. jettask/{webui/config.py → webui_config.py} +6 -1
  55. jettask/webui_exceptions.py +67 -0
  56. jettask/webui_sql/verify_database.sql +72 -0
  57. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/METADATA +2 -1
  58. jettask-0.2.16.dist-info/RECORD +150 -0
  59. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/entry_points.txt +1 -1
  60. jettask/webui/backend/data_api.py +0 -3294
  61. jettask/webui/backend/namespace_api.py +0 -295
  62. jettask/webui/backend/queue_backlog_api.py +0 -727
  63. jettask/webui/backend/redis_monitor_api.py +0 -476
  64. jettask/webui/frontend/index.html +0 -13
  65. jettask/webui/frontend/package.json +0 -30
  66. jettask/webui/frontend/src/App.css +0 -109
  67. jettask/webui/frontend/src/App.jsx +0 -66
  68. jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
  69. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
  70. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
  71. jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
  72. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
  73. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
  74. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
  75. jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
  76. jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
  77. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
  78. jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
  79. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
  80. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
  81. jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
  82. jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
  83. jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
  84. jettask/webui/frontend/src/components/layout/Header.css +0 -106
  85. jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
  86. jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
  87. jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
  88. jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
  89. jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
  90. jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
  91. jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
  92. jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
  93. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
  94. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
  95. jettask/webui/frontend/src/index.css +0 -114
  96. jettask/webui/frontend/src/main.jsx +0 -22
  97. jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
  98. jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
  99. jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
  100. jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
  101. jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
  102. jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
  103. jettask/webui/frontend/src/pages/Queues.jsx +0 -12
  104. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -810
  105. jettask/webui/frontend/src/pages/Settings.jsx +0 -801
  106. jettask/webui/frontend/src/pages/Workers.jsx +0 -12
  107. jettask/webui/frontend/src/services/api.js +0 -159
  108. jettask/webui/frontend/src/services/queueTrend.js +0 -166
  109. jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
  110. jettask/webui/frontend/src/utils/userPreferences.js +0 -154
  111. jettask/webui/frontend/vite.config.js +0 -26
  112. jettask/webui/sql/init_database.sql +0 -640
  113. jettask-0.2.15.dist-info/RECORD +0 -172
  114. /jettask/{webui/backend → backend}/__init__.py +0 -0
  115. /jettask/{webui/backend → backend}/api/__init__.py +0 -0
  116. /jettask/{webui/backend → backend}/api/v1/__init__.py +0 -0
  117. /jettask/{webui/backend → backend}/api/v1/monitoring.py +0 -0
  118. /jettask/{webui/backend → backend}/api/v1/namespaces.py +0 -0
  119. /jettask/{webui/backend → backend}/api/v1/queues.py +0 -0
  120. /jettask/{webui/backend → backend}/api/v1/tasks.py +0 -0
  121. /jettask/{webui/backend → backend}/config.py +0 -0
  122. /jettask/{webui/backend → backend}/core/__init__.py +0 -0
  123. /jettask/{webui/backend → backend}/core/cache.py +0 -0
  124. /jettask/{webui/backend → backend}/core/database.py +0 -0
  125. /jettask/{webui/backend → backend}/core/exceptions.py +0 -0
  126. /jettask/{webui/backend → backend}/data_access.py +0 -0
  127. /jettask/{webui/backend → backend}/dependencies.py +0 -0
  128. /jettask/{webui/backend → backend}/init_meta_db.py +0 -0
  129. /jettask/{webui/backend → backend}/main_v2.py +0 -0
  130. /jettask/{webui/backend → backend}/models/__init__.py +0 -0
  131. /jettask/{webui/backend → backend}/models/requests.py +0 -0
  132. /jettask/{webui/backend → backend}/models/responses.py +0 -0
  133. /jettask/{webui/backend → backend}/queue_stats_v2.py +0 -0
  134. /jettask/{webui/backend → backend}/services/__init__.py +0 -0
  135. /jettask/{webui/backend → backend}/start.py +0 -0
  136. /jettask/{webui/cleanup_deprecated_tables.sql → cleanup_deprecated_tables.sql} +0 -0
  137. /jettask/{webui/gradio_app.py → gradio_app.py} +0 -0
  138. /jettask/{webui/__init__.py → main.py} +0 -0
  139. /jettask/{webui/models.py → models.py} +0 -0
  140. /jettask/{webui/run_monitor.py → run_monitor.py} +0 -0
  141. /jettask/{webui/schema.sql → schema.sql} +0 -0
  142. /jettask/{webui/unified_consumer_manager.py → unified_consumer_manager.py} +0 -0
  143. /jettask/{webui/models → webui_models}/__init__.py +0 -0
  144. /jettask/{webui/models → webui_models}/namespace.py +0 -0
  145. /jettask/{webui/sql → webui_sql}/batch_upsert_functions.sql +0 -0
  146. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/WHEEL +0 -0
  147. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/licenses/LICENSE +0 -0
  148. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/top_level.txt +0 -0
@@ -1,166 +0,0 @@
1
- /**
2
- * 命名空间选择器组件
3
- * 放置在页面顶部,用于切换不同的命名空间
4
- */
5
- import React, { useState, useEffect } from 'react';
6
- import { Select, Space, Tag, Tooltip, message, Button, Dropdown, Modal } from 'antd';
7
- import { DatabaseOutlined, CloudServerOutlined, SettingOutlined, PlusOutlined, EditOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
8
- import { useNavigate } from 'react-router-dom';
9
- import { useNamespace } from '../contexts/NamespaceContext';
10
-
11
- const { Option } = Select;
12
-
13
- const NamespaceSelector = ({ value, onChange, style }) => {
14
- const [namespaces, setNamespaces] = useState([]);
15
- const [loading, setLoading] = useState(false);
16
- const navigate = useNavigate();
17
- const { refreshTrigger } = useNamespace(); // 获取刷新触发器
18
-
19
- // 获取命名空间列表
20
- const fetchNamespaces = async () => {
21
- setLoading(true);
22
- try {
23
- const response = await fetch('/api/data/namespaces');
24
- if (response.ok) {
25
- const data = await response.json();
26
- setNamespaces(data);
27
-
28
- // 如果没有选中的命名空间,默认选中第一个
29
- if (!value && data.length > 0) {
30
- const firstNamespace = data[0].name;
31
- if (onChange) {
32
- onChange(firstNamespace);
33
- }
34
- } else if (data.length === 0) {
35
- // 如果没有任何命名空间,弹出提示并引导到管理页面
36
- showNoNamespaceModal();
37
- }
38
- } else {
39
- message.error('获取命名空间列表失败');
40
- }
41
- } catch (error) {
42
- console.error('获取命名空间失败:', error);
43
- message.error('连接任务中心失败');
44
- } finally {
45
- setLoading(false);
46
- }
47
- };
48
-
49
- // 组件挂载时获取命名空间列表,以及当refreshTrigger变化时刷新
50
- useEffect(() => {
51
- fetchNamespaces();
52
- }, [refreshTrigger]); // 监听刷新触发器而不是value
53
-
54
- // 处理命名空间切换
55
- const handleNamespaceChange = (namespaceName) => {
56
- console.log('🔧 NamespaceSelector切换命名空间:', namespaceName);
57
- console.log('🔧 当前props.value:', value);
58
- console.log('🔧 onChange函数存在:', !!onChange);
59
-
60
- if (onChange) {
61
- onChange(namespaceName);
62
- console.log('🔧 已调用onChange函数');
63
- }
64
- message.success(`已切换到命名空间: ${namespaceName}`);
65
- };
66
-
67
- // 显示无命名空间提示弹窗
68
- const showNoNamespaceModal = () => {
69
- Modal.confirm({
70
- title: '暂无可用命名空间',
71
- icon: <ExclamationCircleOutlined />,
72
- content: '当前系统中没有配置任何命名空间,请先创建一个命名空间。',
73
- okText: '去管理命名空间',
74
- cancelText: '取消',
75
- onOk: () => {
76
- navigate('/settings');
77
- },
78
- });
79
- };
80
-
81
- // 获取命名空间标签颜色
82
- const getNamespaceTagColor = (name) => {
83
- if (name === 'default') return 'blue';
84
- if (name.includes('test')) return 'orange';
85
- if (name.includes('prod')) return 'green';
86
- if (name.includes('dev')) return 'purple';
87
- return 'default';
88
- };
89
-
90
-
91
- return (
92
- <Space style={{ ...style }}>
93
- <span style={{ color: 'rgba(255, 255, 255, 0.65)', fontSize: '14px' }}>
94
- <DatabaseOutlined style={{ marginRight: '6px' }} />
95
- 命名空间:
96
- </span>
97
-
98
- <Select
99
- value={value}
100
- onChange={handleNamespaceChange}
101
- loading={loading}
102
- style={{ minWidth: 200 }}
103
- placeholder="请选择命名空间"
104
- showSearch
105
- optionFilterProp="children"
106
- filterOption={(input, option) =>
107
- option.children.props.children[1]?.toLowerCase().includes(input.toLowerCase())
108
- }
109
- dropdownRender={(menu) => (
110
- <>
111
- {menu}
112
- <div
113
- style={{
114
- padding: '8px 12px',
115
- textAlign: 'center',
116
- borderTop: '1px solid #f0f0f0',
117
- cursor: 'pointer'
118
- }}
119
- onClick={(e) => {
120
- e.stopPropagation();
121
- e.preventDefault();
122
- console.log('🔧 点击管理命名空间区域');
123
- navigate('/settings');
124
- }}
125
- >
126
- <SettingOutlined style={{ marginRight: 4 }} />
127
- 管理命名空间
128
- </div>
129
- </>
130
- )}
131
- >
132
- {namespaces.map(ns => (
133
- <Option key={ns.name} value={ns.name}>
134
- <Space>
135
- <Tag color={getNamespaceTagColor(ns.name)} style={{ margin: 0 }}>
136
- {ns.name}
137
- </Tag>
138
- {ns.description && (
139
- <Tooltip title={ns.description}>
140
- <span style={{ color: '#999', fontSize: '12px' }}>
141
- {ns.description.length > 20
142
- ? `${ns.description.substring(0, 20)}...`
143
- : ns.description}
144
- </span>
145
- </Tooltip>
146
- )}
147
- </Space>
148
- </Option>
149
- ))}
150
- </Select>
151
-
152
- {value && (
153
- <Tooltip title={`当前命名空间: ${value}`}>
154
- <CloudServerOutlined
155
- style={{
156
- color: '#52c41a',
157
- fontSize: '16px'
158
- }}
159
- />
160
- </Tooltip>
161
- )}
162
- </Space>
163
- );
164
- };
165
-
166
- export default NamespaceSelector;
@@ -1,298 +0,0 @@
1
- import { useState, useEffect, useCallback } from 'react';
2
- import { Card, Select, Spin, Empty, Space, Button, message } from 'antd';
3
- import { Line } from '@ant-design/plots';
4
- import { ReloadOutlined, ExpandOutlined } from '@ant-design/icons';
5
- import { useNamespace } from '../contexts/NamespaceContext';
6
- import dayjs from 'dayjs';
7
- import axios from 'axios';
8
-
9
- const { Option } = Select;
10
-
11
- // 时间范围选项
12
- const TIME_RANGES = {
13
- '15m': { label: '15分钟', minutes: 15 },
14
- '30m': { label: '30分钟', minutes: 30 },
15
- '1h': { label: '1小时', minutes: 60 },
16
- '3h': { label: '3小时', minutes: 180 },
17
- '6h': { label: '6小时', minutes: 360 },
18
- '12h': { label: '12小时', minutes: 720 },
19
- '24h': { label: '24小时', minutes: 1440 },
20
- };
21
-
22
- function QueueBacklogChart({
23
- height = 300,
24
- showTitle = true,
25
- defaultTimeRange = '1h',
26
- autoRefresh = true,
27
- refreshInterval = 60000, // 默认60秒刷新一次
28
- onExpand = null
29
- }) {
30
- const { currentNamespace } = useNamespace();
31
- const [loading, setLoading] = useState(false);
32
- const [timeRange, setTimeRange] = useState(defaultTimeRange);
33
- const [selectedQueues, setSelectedQueues] = useState([]);
34
- const [availableQueues, setAvailableQueues] = useState([]);
35
- const [chartData, setChartData] = useState([]);
36
- const [lastUpdateTime, setLastUpdateTime] = useState(null);
37
-
38
- // 获取可用队列列表
39
- const fetchAvailableQueues = useCallback(async () => {
40
- try {
41
- const namespace = currentNamespace || 'default';
42
- const response = await axios.get(`/api/queues/${namespace}`);
43
- if (response.data.success) {
44
- const queues = response.data.data.map(q => q.name);
45
- setAvailableQueues(queues);
46
-
47
- // 默认选择前5个队列
48
- if (selectedQueues.length === 0 && queues.length > 0) {
49
- setSelectedQueues(queues.slice(0, 5));
50
- }
51
- }
52
- } catch (error) {
53
- console.error('Failed to fetch queues:', error);
54
- }
55
- }, [currentNamespace, selectedQueues.length]);
56
-
57
- // 获取队列积压数据
58
- const fetchBacklogData = useCallback(async () => {
59
- if (selectedQueues.length === 0) return;
60
-
61
- setLoading(true);
62
- try {
63
- const namespace = currentNamespace || 'default';
64
- const endTime = dayjs();
65
- const startTime = endTime.subtract(TIME_RANGES[timeRange].minutes, 'minute');
66
-
67
- const params = {
68
- namespace,
69
- queues: selectedQueues,
70
- time_range: timeRange,
71
- start_time: startTime.toISOString(),
72
- end_time: endTime.toISOString(),
73
- filters: [] // 不需要额外筛选
74
- };
75
-
76
- // 使用队列流量接口获取积压数据
77
- const response = await axios.post(`/api/data/queue-flow-rates/${namespace}`, params);
78
-
79
- if (response.data.success) {
80
- const { data } = response.data;
81
-
82
- // 转换数据格式,只保留pending数据作为积压量
83
- const backlogData = data
84
- .filter(item => item.metric === 'pending')
85
- .map(item => ({
86
- time: dayjs(item.time).format('HH:mm'),
87
- timestamp: item.time,
88
- queue: item.queue,
89
- value: item.value || 0,
90
- type: '积压量'
91
- }));
92
-
93
- setChartData(backlogData);
94
- setLastUpdateTime(dayjs());
95
- }
96
- } catch (error) {
97
- message.error('获取队列积压数据失败');
98
- console.error('Failed to fetch backlog data:', error);
99
- } finally {
100
- setLoading(false);
101
- }
102
- }, [currentNamespace, selectedQueues, timeRange]);
103
-
104
- // 初始化
105
- useEffect(() => {
106
- fetchAvailableQueues();
107
- }, [fetchAvailableQueues]);
108
-
109
- // 当选中队列变化时,获取数据
110
- useEffect(() => {
111
- if (selectedQueues.length > 0) {
112
- fetchBacklogData();
113
- }
114
- }, [selectedQueues, fetchBacklogData]);
115
-
116
- // 自动刷新
117
- useEffect(() => {
118
- if (!autoRefresh || selectedQueues.length === 0) return;
119
-
120
- const timer = setInterval(() => {
121
- fetchBacklogData();
122
- }, refreshInterval);
123
-
124
- return () => clearInterval(timer);
125
- }, [autoRefresh, refreshInterval, fetchBacklogData, selectedQueues.length]);
126
-
127
- // 图表配置
128
- const config = {
129
- data: chartData,
130
- xField: 'time',
131
- yField: 'value',
132
- seriesField: 'queue',
133
- height: height,
134
- smooth: true,
135
- animation: {
136
- appear: {
137
- animation: 'wave-in',
138
- duration: 1000,
139
- },
140
- },
141
- xAxis: {
142
- title: {
143
- text: '时间',
144
- style: { fontSize: 12 },
145
- },
146
- label: {
147
- autoRotate: true,
148
- autoHide: true,
149
- },
150
- },
151
- yAxis: {
152
- title: {
153
- text: '积压任务数',
154
- style: { fontSize: 12 },
155
- },
156
- min: 0,
157
- nice: true,
158
- },
159
- legend: {
160
- position: 'top-right',
161
- itemSpacing: 10,
162
- },
163
- tooltip: {
164
- shared: true,
165
- showCrosshairs: true,
166
- formatter: (datum) => {
167
- return {
168
- name: datum.queue,
169
- value: `${datum.value.toLocaleString()} 个任务`,
170
- };
171
- },
172
- },
173
- theme: {
174
- colors10: [
175
- '#5B8FF9',
176
- '#5AD8A6',
177
- '#5D7092',
178
- '#F6BD16',
179
- '#E8684A',
180
- '#6DC8EC',
181
- '#9270CA',
182
- '#FF9D4D',
183
- '#269A99',
184
- '#FF99C3',
185
- ],
186
- },
187
- // 添加告警线(可选)
188
- annotations: [
189
- {
190
- type: 'line',
191
- start: ['min', 1000],
192
- end: ['max', 1000],
193
- style: {
194
- stroke: '#ff9800',
195
- lineDash: [4, 4],
196
- lineWidth: 1,
197
- },
198
- text: {
199
- content: '警告线 (1000)',
200
- position: 'end',
201
- style: {
202
- textAlign: 'end',
203
- fontSize: 10,
204
- fill: '#ff9800',
205
- },
206
- offsetY: -5,
207
- },
208
- },
209
- {
210
- type: 'line',
211
- start: ['min', 5000],
212
- end: ['max', 5000],
213
- style: {
214
- stroke: '#f44336',
215
- lineDash: [4, 4],
216
- lineWidth: 1,
217
- },
218
- text: {
219
- content: '危险线 (5000)',
220
- position: 'end',
221
- style: {
222
- textAlign: 'end',
223
- fontSize: 10,
224
- fill: '#f44336',
225
- },
226
- offsetY: -5,
227
- },
228
- },
229
- ],
230
- };
231
-
232
- return (
233
- <Card
234
- title={showTitle ? "队列积压趋势" : null}
235
- size="small"
236
- extra={
237
- <Space>
238
- {lastUpdateTime && (
239
- <span style={{ fontSize: 12, color: '#999' }}>
240
- 更新于 {lastUpdateTime.format('HH:mm:ss')}
241
- </span>
242
- )}
243
- <Select
244
- value={timeRange}
245
- onChange={setTimeRange}
246
- style={{ width: 100 }}
247
- size="small"
248
- >
249
- {Object.entries(TIME_RANGES).map(([key, { label }]) => (
250
- <Option key={key} value={key}>{label}</Option>
251
- ))}
252
- </Select>
253
- <Select
254
- mode="multiple"
255
- placeholder="选择队列"
256
- value={selectedQueues}
257
- onChange={setSelectedQueues}
258
- style={{ minWidth: 200, maxWidth: 400 }}
259
- size="small"
260
- maxTagCount={2}
261
- maxTagTextLength={10}
262
- >
263
- {availableQueues.map(queue => (
264
- <Option key={queue} value={queue}>{queue}</Option>
265
- ))}
266
- </Select>
267
- <Button
268
- icon={<ReloadOutlined />}
269
- size="small"
270
- onClick={fetchBacklogData}
271
- loading={loading}
272
- />
273
- {onExpand && (
274
- <Button
275
- icon={<ExpandOutlined />}
276
- size="small"
277
- onClick={onExpand}
278
- title="展开详情"
279
- />
280
- )}
281
- </Space>
282
- }
283
- >
284
- <Spin spinning={loading}>
285
- {chartData.length > 0 ? (
286
- <Line {...config} />
287
- ) : (
288
- <Empty
289
- description={selectedQueues.length === 0 ? "请选择要监控的队列" : "暂无数据"}
290
- style={{ height: height, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' }}
291
- />
292
- )}
293
- </Spin>
294
- </Card>
295
- );
296
- }
297
-
298
- export default QueueBacklogChart;