jettask 0.2.1__py3-none-any.whl → 0.2.4__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/constants.py +213 -0
- jettask/core/app.py +525 -205
- jettask/core/cli.py +193 -185
- jettask/core/consumer_manager.py +126 -34
- jettask/core/context.py +3 -0
- jettask/core/enums.py +137 -0
- jettask/core/event_pool.py +501 -168
- jettask/core/message.py +147 -0
- jettask/core/offline_worker_recovery.py +181 -114
- jettask/core/task.py +10 -174
- jettask/core/task_batch.py +153 -0
- jettask/core/unified_manager_base.py +243 -0
- jettask/core/worker_scanner.py +54 -54
- jettask/executors/asyncio.py +184 -64
- jettask/webui/backend/config.py +51 -0
- jettask/webui/backend/data_access.py +2083 -92
- jettask/webui/backend/data_api.py +3294 -0
- jettask/webui/backend/dependencies.py +261 -0
- jettask/webui/backend/init_meta_db.py +158 -0
- jettask/webui/backend/main.py +1358 -69
- jettask/webui/backend/main_unified.py +78 -0
- jettask/webui/backend/main_v2.py +394 -0
- jettask/webui/backend/namespace_api.py +295 -0
- jettask/webui/backend/namespace_api_old.py +294 -0
- jettask/webui/backend/namespace_data_access.py +611 -0
- jettask/webui/backend/queue_backlog_api.py +727 -0
- jettask/webui/backend/queue_stats_v2.py +521 -0
- jettask/webui/backend/redis_monitor_api.py +476 -0
- jettask/webui/backend/unified_api_router.py +1601 -0
- jettask/webui/db_init.py +204 -32
- jettask/webui/frontend/package-lock.json +492 -1
- jettask/webui/frontend/package.json +4 -1
- jettask/webui/frontend/src/App.css +105 -7
- jettask/webui/frontend/src/App.jsx +49 -20
- jettask/webui/frontend/src/components/NamespaceSelector.jsx +166 -0
- jettask/webui/frontend/src/components/QueueBacklogChart.jsx +298 -0
- jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +638 -0
- jettask/webui/frontend/src/components/QueueDetailsTable.css +65 -0
- jettask/webui/frontend/src/components/QueueDetailsTable.jsx +487 -0
- jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +465 -0
- jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +423 -0
- jettask/webui/frontend/src/components/TaskFilter.jsx +425 -0
- jettask/webui/frontend/src/components/TimeRangeSelector.css +21 -0
- jettask/webui/frontend/src/components/TimeRangeSelector.jsx +160 -0
- jettask/webui/frontend/src/components/layout/AppLayout.css +95 -0
- jettask/webui/frontend/src/components/layout/AppLayout.jsx +49 -0
- jettask/webui/frontend/src/components/layout/Header.css +34 -10
- jettask/webui/frontend/src/components/layout/Header.jsx +31 -23
- jettask/webui/frontend/src/components/layout/SideMenu.css +137 -0
- jettask/webui/frontend/src/components/layout/SideMenu.jsx +209 -0
- jettask/webui/frontend/src/components/layout/TabsNav.css +244 -0
- jettask/webui/frontend/src/components/layout/TabsNav.jsx +206 -0
- jettask/webui/frontend/src/components/layout/UserInfo.css +197 -0
- jettask/webui/frontend/src/components/layout/UserInfo.jsx +197 -0
- jettask/webui/frontend/src/contexts/NamespaceContext.jsx +72 -0
- jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +245 -0
- jettask/webui/frontend/src/main.jsx +1 -0
- jettask/webui/frontend/src/pages/Alerts.jsx +684 -0
- jettask/webui/frontend/src/pages/Dashboard.jsx +1330 -0
- jettask/webui/frontend/src/pages/QueueDetail.jsx +1109 -10
- jettask/webui/frontend/src/pages/QueueMonitor.jsx +236 -115
- jettask/webui/frontend/src/pages/Queues.jsx +5 -1
- jettask/webui/frontend/src/pages/ScheduledTasks.jsx +809 -0
- jettask/webui/frontend/src/pages/Settings.jsx +800 -0
- jettask/webui/frontend/src/services/api.js +7 -5
- jettask/webui/frontend/src/utils/suppressWarnings.js +22 -0
- jettask/webui/frontend/src/utils/userPreferences.js +154 -0
- jettask/webui/multi_namespace_consumer.py +543 -0
- jettask/webui/pg_consumer.py +983 -246
- jettask/webui/static/dist/assets/index-7129cfe1.css +1 -0
- jettask/webui/static/dist/assets/index-8d1935cc.js +774 -0
- jettask/webui/static/dist/index.html +2 -2
- jettask/webui/task_center.py +216 -0
- jettask/webui/task_center_client.py +150 -0
- jettask/webui/unified_consumer_manager.py +193 -0
- {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/METADATA +1 -1
- jettask-0.2.4.dist-info/RECORD +134 -0
- jettask/webui/pg_consumer_slow.py +0 -1099
- jettask/webui/pg_consumer_test.py +0 -678
- jettask/webui/static/dist/assets/index-823408e8.css +0 -1
- jettask/webui/static/dist/assets/index-9968b0b8.js +0 -543
- jettask/webui/test_pg_consumer_recovery.py +0 -547
- jettask/webui/test_recovery_simple.py +0 -492
- jettask/webui/test_self_recovery.py +0 -467
- jettask-0.2.1.dist-info/RECORD +0 -91
- {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/WHEEL +0 -0
- {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/top_level.txt +0 -0
@@ -1,57 +1,97 @@
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react';
|
2
|
-
import { Card, Select,
|
2
|
+
import { Card, Select, Space, Spin, message, Button, Tooltip, Empty } from 'antd';
|
3
3
|
import { Line } from '@ant-design/plots';
|
4
4
|
import { G2 } from "@ant-design/plots";
|
5
5
|
|
6
6
|
|
7
7
|
|
8
|
-
import { ReloadOutlined } from '@ant-design/icons';
|
8
|
+
import { ReloadOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
9
9
|
import { useLoading } from '../contexts/LoadingContext';
|
10
|
+
import { useNamespace } from '../contexts/NamespaceContext';
|
11
|
+
import QueueDetailsTableV2 from '../components/QueueDetailsTableV2';
|
12
|
+
import TimeRangeSelector from '../components/TimeRangeSelector';
|
13
|
+
import { getUserPreferences, setPreference, PREFERENCE_KEYS } from '../utils/userPreferences';
|
10
14
|
|
11
15
|
import dayjs from 'dayjs';
|
12
16
|
import axios from 'axios';
|
13
17
|
|
14
|
-
const { RangePicker } = DatePicker;
|
15
18
|
const { ChartEvent } = G2;
|
16
19
|
|
17
|
-
// 时间范围选项
|
18
|
-
const TIME_RANGES = [
|
19
|
-
{ label: '最近15分钟', value: '15m' },
|
20
|
-
{ label: '最近30分钟', value: '30m' },
|
21
|
-
{ label: '最近1小时', value: '1h' },
|
22
|
-
{ label: '最近3小时', value: '3h' },
|
23
|
-
{ label: '最近6小时', value: '6h' },
|
24
|
-
{ label: '最近12小时', value: '12h' },
|
25
|
-
{ label: '最近24小时', value: '24h' },
|
26
|
-
{ label: '最近7天', value: '7d' },
|
27
|
-
{ label: '最近30天', value: '30d' },
|
28
|
-
];
|
29
|
-
|
30
20
|
function QueueMonitor() {
|
31
21
|
const { setLoading: setGlobalLoading } = useLoading();
|
22
|
+
const { currentNamespace } = useNamespace();
|
32
23
|
const [loading, setLoading] = useState(false);
|
33
24
|
const [queues, setQueues] = useState([]);
|
34
|
-
|
35
|
-
|
36
|
-
const
|
25
|
+
|
26
|
+
// 从本地存储恢复用户设置
|
27
|
+
const preferences = getUserPreferences();
|
28
|
+
const [selectedQueues, setSelectedQueues] = useState(
|
29
|
+
preferences[PREFERENCE_KEYS.QUEUE_MONITOR_SELECTED_QUEUES] || []
|
30
|
+
);
|
31
|
+
const savedTimeRange = preferences[PREFERENCE_KEYS.QUEUE_MONITOR_TIME_RANGE] || '15m';
|
32
|
+
const [timeRange, setTimeRange] = useState(savedTimeRange);
|
33
|
+
|
34
|
+
// 只有当保存的时间范围是 'custom' 时,才恢复自定义时间范围
|
35
|
+
const savedCustomRange = preferences[PREFERENCE_KEYS.QUEUE_MONITOR_CUSTOM_TIME_RANGE];
|
36
|
+
const [customTimeRange, setCustomTimeRange] = useState(
|
37
|
+
savedTimeRange === 'custom' && savedCustomRange
|
38
|
+
? [dayjs(savedCustomRange[0]), dayjs(savedCustomRange[1])]
|
39
|
+
: null
|
40
|
+
);
|
37
41
|
const [chartData, setChartData] = useState([]);
|
38
42
|
const [granularity, setGranularity] = useState('');
|
39
|
-
const [sliderValues, setSliderValues] = useState([0, 1]);
|
43
|
+
const [sliderValues, setSliderValues] = useState([0, 1]); // slider控件的值范围
|
40
44
|
|
41
45
|
// 用于防抖的 ref
|
42
46
|
const fetchTimeoutRef = useRef(null);
|
43
47
|
const isBrushingRef = useRef(false);
|
48
|
+
const queueDetailsRef = useRef(null);
|
49
|
+
|
50
|
+
// 使用 ref 追踪最新的 timeRange 和 customTimeRange
|
51
|
+
const timeRangeRef = useRef(timeRange);
|
52
|
+
const customTimeRangeRef = useRef(customTimeRange);
|
53
|
+
|
54
|
+
// 每次状态更新时同步更新 ref
|
55
|
+
useEffect(() => {
|
56
|
+
timeRangeRef.current = timeRange;
|
57
|
+
console.log('[QueueMonitor] timeRangeRef 更新为:', timeRange);
|
58
|
+
}, [timeRange]);
|
59
|
+
|
60
|
+
useEffect(() => {
|
61
|
+
customTimeRangeRef.current = customTimeRange;
|
62
|
+
}, [customTimeRange]);
|
44
63
|
|
45
64
|
// 获取队列列表
|
46
65
|
const fetchQueues = async () => {
|
66
|
+
if (!currentNamespace) {
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
|
47
70
|
try {
|
48
|
-
|
49
|
-
|
71
|
+
// 先调用 get_queues 获取指定命名空间的队列名称
|
72
|
+
const response = await axios.get(`/api/queues/${currentNamespace}`);
|
73
|
+
if (response.data && response.data.success) {
|
50
74
|
const queueList = response.data.data;
|
51
75
|
setQueues(queueList);
|
52
|
-
|
53
|
-
|
54
|
-
|
76
|
+
|
77
|
+
// 如果有保存的选择,恢复之前的选择;否则默认选择前10个
|
78
|
+
const savedQueues = preferences[PREFERENCE_KEYS.QUEUE_MONITOR_SELECTED_QUEUES];
|
79
|
+
if (savedQueues && savedQueues.length > 0) {
|
80
|
+
// 过滤掉不存在的队列
|
81
|
+
const validQueues = savedQueues.filter(q => queueList.includes(q));
|
82
|
+
if (validQueues.length > 0) {
|
83
|
+
setSelectedQueues(validQueues);
|
84
|
+
} else if (queueList.length > 0) {
|
85
|
+
// 如果保存的队列都不存在了,选择前10个
|
86
|
+
const defaultQueues = queueList.slice(0, 10);
|
87
|
+
setSelectedQueues(defaultQueues);
|
88
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_SELECTED_QUEUES, defaultQueues);
|
89
|
+
}
|
90
|
+
} else if (selectedQueues.length === 0 && queueList.length > 0) {
|
91
|
+
// 第一次使用,默认选择前10个
|
92
|
+
const defaultQueues = queueList.slice(0, 10);
|
93
|
+
setSelectedQueues(defaultQueues);
|
94
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_SELECTED_QUEUES, defaultQueues);
|
55
95
|
}
|
56
96
|
}
|
57
97
|
} catch (error) {
|
@@ -62,26 +102,31 @@ function QueueMonitor() {
|
|
62
102
|
|
63
103
|
// 获取队列趋势数据
|
64
104
|
const fetchQueueTimeline = async () => {
|
65
|
-
if (selectedQueues.length === 0) {
|
105
|
+
if (!currentNamespace || selectedQueues.length === 0) {
|
66
106
|
return;
|
67
107
|
}
|
68
108
|
|
69
109
|
setLoading(true);
|
70
110
|
setGlobalLoading(true, '加载数据中...');
|
71
111
|
try {
|
112
|
+
// 使用 ref 中的最新值
|
113
|
+
const currentTimeRange = timeRangeRef.current;
|
114
|
+
const currentCustomTimeRange = customTimeRangeRef.current;
|
115
|
+
|
72
116
|
const params = {
|
117
|
+
namespace: currentNamespace,
|
73
118
|
queues: selectedQueues,
|
74
|
-
time_range:
|
119
|
+
time_range: currentTimeRange,
|
75
120
|
};
|
76
121
|
|
77
122
|
// 如果有自定义时间范围
|
78
|
-
if (
|
79
|
-
params.start_time =
|
80
|
-
params.end_time =
|
123
|
+
if (currentCustomTimeRange) {
|
124
|
+
params.start_time = currentCustomTimeRange[0].toISOString();
|
125
|
+
params.end_time = currentCustomTimeRange[1].toISOString();
|
81
126
|
}
|
82
|
-
console.log('请求参数:', params);
|
127
|
+
console.log('[QueueMonitor.fetchQueueTimeline] 请求参数:', params, 'currentTimeRange:', currentTimeRange, 'timeRange状态值:', timeRange);
|
83
128
|
|
84
|
-
const response = await axios.post(
|
129
|
+
const response = await axios.post(`/api/data/queue-timeline/${currentNamespace}`, params);
|
85
130
|
const { data, granularity: dataGranularity } = response.data;
|
86
131
|
|
87
132
|
setChartData(data);
|
@@ -99,10 +144,12 @@ function QueueMonitor() {
|
|
99
144
|
}
|
100
145
|
};
|
101
146
|
|
102
|
-
//
|
147
|
+
// 初始化和命名空间改变时获取队列
|
103
148
|
useEffect(() => {
|
104
|
-
|
105
|
-
|
149
|
+
if (currentNamespace) {
|
150
|
+
fetchQueues();
|
151
|
+
}
|
152
|
+
}, [currentNamespace]);
|
106
153
|
|
107
154
|
// 当选择的队列或时间范围改变时,重新获取数据
|
108
155
|
useEffect(() => {
|
@@ -116,7 +163,7 @@ function QueueMonitor() {
|
|
116
163
|
|
117
164
|
// 如果是刷选触发的自定义时间范围,延迟一点获取数据
|
118
165
|
const delay = isBrushingRef.current ? 300 : 0;
|
119
|
-
|
166
|
+
|
120
167
|
fetchTimeoutRef.current = setTimeout(() => {
|
121
168
|
fetchQueueTimeline();
|
122
169
|
}, delay);
|
@@ -130,28 +177,16 @@ function QueueMonitor() {
|
|
130
177
|
};
|
131
178
|
}, [selectedQueues, timeRange, customTimeRange]);
|
132
179
|
|
133
|
-
// 处理预设时间范围变化
|
134
|
-
const handleTimeRangeChange = (value) => {
|
135
|
-
console.log('选择的时间范围:', value);
|
136
|
-
setTimeRange(value);
|
137
|
-
setCustomTimeRange(null);
|
138
|
-
};
|
139
|
-
|
140
|
-
// 处理自定义时间范围变化
|
141
|
-
const handleCustomTimeRangeChange = (dates) => {
|
142
|
-
if (dates && dates.length === 2) {
|
143
|
-
console.log('选择的自定义时间范围:', dates);
|
144
|
-
setCustomTimeRange(dates);
|
145
|
-
setTimeRange('custom');
|
146
|
-
} else {
|
147
|
-
setCustomTimeRange(null);
|
148
|
-
setTimeRange('15m');
|
149
|
-
}
|
150
|
-
};
|
151
180
|
|
152
181
|
// 手动刷新
|
153
182
|
const handleRefresh = () => {
|
183
|
+
console.log('[QueueMonitor] 刷新时的timeRange:', timeRange, 'customTimeRange:', customTimeRange);
|
184
|
+
console.log('[QueueMonitor] 刷新时的selectedQueues:', selectedQueues);
|
154
185
|
fetchQueueTimeline();
|
186
|
+
// 同时刷新队列详细信息表格
|
187
|
+
if (queueDetailsRef.current) {
|
188
|
+
queueDetailsRef.current.refresh();
|
189
|
+
}
|
155
190
|
};
|
156
191
|
|
157
192
|
|
@@ -183,10 +218,10 @@ function QueueMonitor() {
|
|
183
218
|
range: ['#5B8FF9', '#5AD8A6', '#5D7092', '#F6BD16', '#E8684A', '#6DC8EC', '#9270CA', '#FF9D4D', '#269A99', '#FF99C3']
|
184
219
|
}
|
185
220
|
},
|
186
|
-
point: {
|
187
|
-
|
188
|
-
|
189
|
-
},
|
221
|
+
// point: {
|
222
|
+
// size: 3,
|
223
|
+
// shape: 'circle',
|
224
|
+
// },
|
190
225
|
style: {
|
191
226
|
lineWidth: 2,
|
192
227
|
},
|
@@ -197,25 +232,25 @@ function QueueMonitor() {
|
|
197
232
|
formatter: (text) => {
|
198
233
|
// text 可能是时间戳或ISO字符串,统一处理
|
199
234
|
const date = dayjs(text);
|
200
|
-
|
235
|
+
|
201
236
|
// 根据后端返回的粒度决定显示格式
|
202
|
-
switch(granularity) {
|
237
|
+
switch (granularity) {
|
203
238
|
case 'second':
|
204
239
|
// 秒级:显示时分秒
|
205
240
|
return date.format('HH:mm:ss');
|
206
|
-
|
241
|
+
|
207
242
|
case 'minute':
|
208
243
|
// 分钟级:显示时分
|
209
244
|
return date.format('HH:mm');
|
210
|
-
|
245
|
+
|
211
246
|
case 'hour':
|
212
247
|
// 小时级:显示日期和小时
|
213
248
|
return date.format('MM-DD HH:00');
|
214
|
-
|
249
|
+
|
215
250
|
case 'day':
|
216
251
|
// 跨天:显示年月日
|
217
252
|
return date.format('YYYY-MM-DD');
|
218
|
-
|
253
|
+
|
219
254
|
default:
|
220
255
|
// 默认显示日期和小时
|
221
256
|
return date.format('MM-DD HH:mm');
|
@@ -234,21 +269,44 @@ function QueueMonitor() {
|
|
234
269
|
},
|
235
270
|
},
|
236
271
|
},
|
237
|
-
|
238
|
-
// autoFit: true,
|
272
|
+
autoFit: true,
|
239
273
|
interaction: {
|
240
274
|
brushXFilter: true // 启用横向筛选
|
241
275
|
},
|
242
|
-
|
243
|
-
slider: {
|
244
|
-
x: {
|
245
|
-
values: sliderValues,
|
246
|
-
},
|
247
|
-
},
|
248
276
|
connectNulls: {
|
249
277
|
connect: true,
|
250
278
|
connectStroke: '#aaa',
|
251
279
|
},
|
280
|
+
legend: {
|
281
|
+
position: 'top',
|
282
|
+
itemName: {
|
283
|
+
style: {
|
284
|
+
fontSize: 12,
|
285
|
+
},
|
286
|
+
},
|
287
|
+
},
|
288
|
+
// 配置 tooltip 时间格式,使用24小时制
|
289
|
+
tooltip: {
|
290
|
+
title: (title) => {
|
291
|
+
// 使用相同的时间格式化逻辑
|
292
|
+
const date = dayjs(title.time);
|
293
|
+
switch (granularity) {
|
294
|
+
case 'second':
|
295
|
+
return date.format('YYYY-MM-DD HH:mm:ss');
|
296
|
+
case 'minute':
|
297
|
+
return date.format('YYYY-MM-DD HH:mm:ss');
|
298
|
+
case 'hour':
|
299
|
+
return date.format('YYYY-MM-DD HH:mm');
|
300
|
+
case 'day':
|
301
|
+
return date.format('YYYY-MM-DD');
|
302
|
+
default:
|
303
|
+
return date.format('YYYY-MM-DD HH:mm');
|
304
|
+
}
|
305
|
+
}
|
306
|
+
},
|
307
|
+
style: {
|
308
|
+
lineWidth: 2,
|
309
|
+
},
|
252
310
|
// 监听brush事件,实现框选后自动请求数据
|
253
311
|
onReady: (plot) => {
|
254
312
|
console.log('图表已准备就绪', plot);
|
@@ -280,7 +338,13 @@ function QueueMonitor() {
|
|
280
338
|
// 更新UI状态,这会触发 useEffect 重新获取数据
|
281
339
|
setTimeRange('custom');
|
282
340
|
setCustomTimeRange([startTime, endTime]);
|
283
|
-
|
341
|
+
|
342
|
+
// 保存刷选的时间范围到本地存储
|
343
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_CUSTOM_TIME_RANGE,
|
344
|
+
[startTime.toISOString(), endTime.toISOString()]
|
345
|
+
);
|
346
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_TIME_RANGE, 'custom');
|
347
|
+
|
284
348
|
// 不需要手动更新 slider,fetchQueueTimeline 会处理
|
285
349
|
}
|
286
350
|
}
|
@@ -288,6 +352,26 @@ function QueueMonitor() {
|
|
288
352
|
},
|
289
353
|
};
|
290
354
|
|
355
|
+
// 如果没有选择命名空间,显示提示
|
356
|
+
if (!currentNamespace) {
|
357
|
+
return (
|
358
|
+
<Card style={{ minHeight: '400px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
359
|
+
<Empty
|
360
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
361
|
+
description={
|
362
|
+
<span>
|
363
|
+
请先在右上角选择一个命名空间
|
364
|
+
<br />
|
365
|
+
<span style={{ color: '#999', fontSize: '12px' }}>
|
366
|
+
选择命名空间后才能查看该空间的队列数据
|
367
|
+
</span>
|
368
|
+
</span>
|
369
|
+
}
|
370
|
+
/>
|
371
|
+
</Card>
|
372
|
+
);
|
373
|
+
}
|
374
|
+
|
291
375
|
return (
|
292
376
|
<Card>
|
293
377
|
{/* 控制面板 */}
|
@@ -299,7 +383,11 @@ function QueueMonitor() {
|
|
299
383
|
style={{ width: 400 }}
|
300
384
|
placeholder="请选择队列"
|
301
385
|
value={selectedQueues}
|
302
|
-
onChange={
|
386
|
+
onChange={(value) => {
|
387
|
+
setSelectedQueues(value);
|
388
|
+
// 保存到本地存储
|
389
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_SELECTED_QUEUES, value);
|
390
|
+
}}
|
303
391
|
options={queues.map(q => ({ label: q, value: q }))}
|
304
392
|
maxTagCount="responsive"
|
305
393
|
/>
|
@@ -307,21 +395,50 @@ function QueueMonitor() {
|
|
307
395
|
|
308
396
|
<div>
|
309
397
|
<span style={{ marginRight: '8px' }}>时间范围:</span>
|
310
|
-
<
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
398
|
+
<TimeRangeSelector
|
399
|
+
value={timeRange}
|
400
|
+
onChange={(value) => {
|
401
|
+
console.log('[QueueMonitor] TimeRangeSelector onChange - 新值:', value, '旧值:', timeRange);
|
402
|
+
setTimeRange(value);
|
403
|
+
if (value !== 'custom') {
|
404
|
+
setCustomTimeRange(null);
|
405
|
+
// 保存到本地存储
|
406
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_TIME_RANGE, value);
|
407
|
+
console.log('[QueueMonitor] 已保存timeRange到localStorage:', value);
|
408
|
+
// 清除自定义时间范围的记忆
|
409
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_CUSTOM_TIME_RANGE, null);
|
410
|
+
// 验证保存是否成功
|
411
|
+
setTimeout(() => {
|
412
|
+
const saved = getUserPreferences();
|
413
|
+
console.log('[QueueMonitor] 验证localStorage保存:', saved[PREFERENCE_KEYS.QUEUE_MONITOR_TIME_RANGE]);
|
414
|
+
}, 100);
|
415
|
+
}
|
416
|
+
}}
|
417
|
+
customValue={customTimeRange}
|
418
|
+
onCustomChange={(dates) => {
|
419
|
+
console.log('[QueueMonitor] onCustomChange 被调用, dates:', dates);
|
420
|
+
if (dates && dates.length === 2) {
|
421
|
+
setCustomTimeRange(dates);
|
422
|
+
setTimeRange('custom');
|
423
|
+
// 保存自定义时间范围到本地存储
|
424
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_CUSTOM_TIME_RANGE,
|
425
|
+
[dates[0].toISOString(), dates[1].toISOString()]
|
426
|
+
);
|
427
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_TIME_RANGE, 'custom');
|
428
|
+
} else if (dates === null) {
|
429
|
+
// dates 为 null 时,说明是选择了预设时间,不需要做任何操作
|
430
|
+
// TimeRangeSelector 的 onChange 已经处理了
|
431
|
+
console.log('[QueueMonitor] onCustomChange - dates为null,跳过处理(预设时间已在onChange中处理)');
|
432
|
+
setCustomTimeRange(null);
|
433
|
+
} else {
|
434
|
+
// 其他情况(可能是清空了选择)
|
435
|
+
setCustomTimeRange(null);
|
436
|
+
setTimeRange('15m');
|
437
|
+
// 清除自定义时间范围
|
438
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_CUSTOM_TIME_RANGE, null);
|
439
|
+
setPreference(PREFERENCE_KEYS.QUEUE_MONITOR_TIME_RANGE, '15m');
|
440
|
+
}
|
441
|
+
}}
|
325
442
|
/>
|
326
443
|
</div>
|
327
444
|
|
@@ -331,25 +448,29 @@ function QueueMonitor() {
|
|
331
448
|
disabled={loading}
|
332
449
|
type="primary"
|
333
450
|
>
|
334
|
-
{loading ? '
|
451
|
+
{loading ? '刷新' : '刷新'}
|
335
452
|
</Button>
|
336
|
-
</Space>
|
337
453
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
454
|
+
<Tooltip
|
455
|
+
placement="bottomRight"
|
456
|
+
title={
|
457
|
+
<div style={{ fontSize: '12px' }}>
|
458
|
+
<div style={{ marginBottom: '4px', fontWeight: 'bold' }}>操作提示:</div>
|
459
|
+
<ul style={{ margin: '0 0 0 16px', paddingLeft: '0' }}>
|
460
|
+
<li>鼠标按住左键横向拖动可刷选时间范围</li>
|
461
|
+
<li>刷选后自动获取该时段的详细数据</li>
|
462
|
+
<li>数据粒度根据时间范围自动调整</li>
|
463
|
+
<li>点击图表空白处取消刷选</li>
|
464
|
+
</ul>
|
465
|
+
</div>
|
347
466
|
}
|
348
|
-
|
349
|
-
|
467
|
+
>
|
468
|
+
<QuestionCircleOutlined style={{ fontSize: '16px', color: '#1890ff', cursor: 'help' }} />
|
469
|
+
</Tooltip>
|
470
|
+
</Space>
|
350
471
|
|
351
472
|
{/* 图表 */}
|
352
|
-
<div style={{ height: '
|
473
|
+
<div style={{ height: '240px', position: 'relative' }}>
|
353
474
|
{chartData.length > 0 ? (
|
354
475
|
<>
|
355
476
|
<Line {...chartConfig} />
|
@@ -387,18 +508,18 @@ function QueueMonitor() {
|
|
387
508
|
)}
|
388
509
|
</div>
|
389
510
|
|
390
|
-
{/*
|
391
|
-
<
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
511
|
+
{/* 分隔线 */}
|
512
|
+
{/* <Divider style={{ margin: '24px 0 16px' }} /> */}
|
513
|
+
|
514
|
+
{/* 队列详细信息表格 */}
|
515
|
+
<QueueDetailsTableV2
|
516
|
+
ref={queueDetailsRef}
|
517
|
+
autoRefresh={false}
|
518
|
+
refreshInterval={10000}
|
519
|
+
selectedQueues={selectedQueues}
|
520
|
+
timeRange={timeRange}
|
521
|
+
customTimeRange={customTimeRange}
|
522
|
+
/>
|
402
523
|
</Card>
|
403
524
|
);
|
404
525
|
}
|