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.
- jettask/monitor/run_backlog_collector.py +96 -0
- jettask/monitor/stream_backlog_monitor.py +362 -0
- jettask/pg_consumer/pg_consumer_v2.py +403 -0
- jettask/pg_consumer/sql_utils.py +182 -0
- jettask/scheduler/__init__.py +17 -0
- jettask/scheduler/add_execution_count.sql +11 -0
- jettask/scheduler/add_priority_field.sql +26 -0
- jettask/scheduler/add_scheduler_id.sql +25 -0
- jettask/scheduler/add_scheduler_id_index.sql +10 -0
- jettask/scheduler/loader.py +249 -0
- jettask/scheduler/make_scheduler_id_required.sql +28 -0
- jettask/scheduler/manager.py +696 -0
- jettask/scheduler/migrate_interval_seconds.sql +9 -0
- jettask/scheduler/models.py +200 -0
- jettask/scheduler/multi_namespace_scheduler.py +294 -0
- jettask/scheduler/performance_optimization.sql +45 -0
- jettask/scheduler/run_scheduler.py +186 -0
- jettask/scheduler/scheduler.py +715 -0
- jettask/scheduler/schema.sql +84 -0
- jettask/scheduler/unified_manager.py +450 -0
- jettask/scheduler/unified_scheduler_manager.py +280 -0
- jettask/webui/backend/api/__init__.py +3 -0
- jettask/webui/backend/api/v1/__init__.py +17 -0
- jettask/webui/backend/api/v1/monitoring.py +431 -0
- jettask/webui/backend/api/v1/namespaces.py +504 -0
- jettask/webui/backend/api/v1/queues.py +342 -0
- jettask/webui/backend/api/v1/tasks.py +367 -0
- jettask/webui/backend/core/__init__.py +3 -0
- jettask/webui/backend/core/cache.py +221 -0
- jettask/webui/backend/core/database.py +200 -0
- jettask/webui/backend/core/exceptions.py +102 -0
- jettask/webui/backend/models/__init__.py +3 -0
- jettask/webui/backend/models/requests.py +236 -0
- jettask/webui/backend/models/responses.py +230 -0
- jettask/webui/backend/services/__init__.py +3 -0
- jettask/webui/frontend/index.html +13 -0
- jettask/webui/models/__init__.py +3 -0
- jettask/webui/models/namespace.py +63 -0
- jettask/webui/sql/batch_upsert_functions.sql +178 -0
- jettask/webui/sql/init_database.sql +640 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/METADATA +80 -10
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/RECORD +46 -53
- jettask/webui/frontend/package-lock.json +0 -4833
- 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 -20
- 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 -809
- jettask/webui/frontend/src/pages/Settings.jsx +0 -800
- jettask/webui/frontend/src/pages/Workers.jsx +0 -12
- jettask/webui/frontend/src/services/api.js +0 -114
- jettask/webui/frontend/src/services/queueTrend.js +0 -152
- jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
- jettask/webui/frontend/src/utils/userPreferences.js +0 -154
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/WHEEL +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.7.dist-info}/top_level.txt +0 -0
@@ -1,114 +0,0 @@
|
|
1
|
-
import axios from 'axios';
|
2
|
-
|
3
|
-
// 智能获取 API 基础 URL
|
4
|
-
const getApiBaseUrl = () => {
|
5
|
-
// 在开发环境中使用 localhost
|
6
|
-
if (typeof window !== 'undefined') {
|
7
|
-
const hostname = window.location.hostname;
|
8
|
-
const protocol = window.location.protocol;
|
9
|
-
|
10
|
-
// 如果是生产环境,使用相同的域名
|
11
|
-
if (hostname !== 'localhost' && hostname !== '127.0.0.1') {
|
12
|
-
return `${protocol}//${hostname}:8001/api`;
|
13
|
-
}
|
14
|
-
}
|
15
|
-
|
16
|
-
// 默认开发环境
|
17
|
-
return 'http://localhost:8001/api';
|
18
|
-
};
|
19
|
-
|
20
|
-
// 智能获取 WebSocket URL
|
21
|
-
const getWsBaseUrl = () => {
|
22
|
-
if (typeof window !== 'undefined') {
|
23
|
-
const hostname = window.location.hostname;
|
24
|
-
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
25
|
-
|
26
|
-
// 如果是生产环境,使用相同的域名
|
27
|
-
if (hostname !== 'localhost' && hostname !== '127.0.0.1') {
|
28
|
-
return `${wsProtocol}//${hostname}:8001/ws`;
|
29
|
-
}
|
30
|
-
}
|
31
|
-
|
32
|
-
// 默认开发环境
|
33
|
-
return 'ws://localhost:8001/ws';
|
34
|
-
};
|
35
|
-
|
36
|
-
const API_BASE_URL = getApiBaseUrl();
|
37
|
-
const WS_BASE_URL = getWsBaseUrl();
|
38
|
-
|
39
|
-
// 创建 axios 实例
|
40
|
-
const api = axios.create({
|
41
|
-
baseURL: API_BASE_URL,
|
42
|
-
timeout: 10000,
|
43
|
-
});
|
44
|
-
|
45
|
-
// 获取全局统计信息
|
46
|
-
export const fetchGlobalStats = async () => {
|
47
|
-
try {
|
48
|
-
const response = await api.get('/stats');
|
49
|
-
return response.data.data;
|
50
|
-
} catch (error) {
|
51
|
-
console.error('Failed to fetch global stats:', error);
|
52
|
-
throw error;
|
53
|
-
}
|
54
|
-
};
|
55
|
-
|
56
|
-
// 获取队列列表
|
57
|
-
export const fetchQueues = async () => {
|
58
|
-
try {
|
59
|
-
const response = await api.get('/queues');
|
60
|
-
return response.data;
|
61
|
-
} catch (error) {
|
62
|
-
console.error('Failed to fetch queues:', error);
|
63
|
-
throw error;
|
64
|
-
}
|
65
|
-
};
|
66
|
-
|
67
|
-
// 获取队列时间线数据
|
68
|
-
export const fetchQueueTimeline = async (params) => {
|
69
|
-
try {
|
70
|
-
// 从参数中提取命名空间,如果没有则使用 default
|
71
|
-
const namespace = params.namespace || 'default';
|
72
|
-
const response = await api.post(`/queue-timeline/${namespace}`, params);
|
73
|
-
// 处理数据,确保不包含填充的空值
|
74
|
-
const data = response.data.data || response.data || [];
|
75
|
-
return {
|
76
|
-
...response.data,
|
77
|
-
data: data.filter(item => item.value > 0) // 只返回有实际值的数据点
|
78
|
-
};
|
79
|
-
} catch (error) {
|
80
|
-
console.error('Failed to fetch queue timeline:', error);
|
81
|
-
throw error;
|
82
|
-
}
|
83
|
-
};
|
84
|
-
|
85
|
-
// WebSocket 连接
|
86
|
-
export const connectWebSocket = (onMessage, onError) => {
|
87
|
-
const ws = new WebSocket(`${WS_BASE_URL}/monitor`);
|
88
|
-
|
89
|
-
ws.onopen = () => {
|
90
|
-
console.log('WebSocket connected');
|
91
|
-
};
|
92
|
-
|
93
|
-
ws.onmessage = (event) => {
|
94
|
-
try {
|
95
|
-
const data = JSON.parse(event.data);
|
96
|
-
onMessage(data);
|
97
|
-
} catch (error) {
|
98
|
-
console.error('Failed to parse WebSocket message:', error);
|
99
|
-
}
|
100
|
-
};
|
101
|
-
|
102
|
-
ws.onerror = (error) => {
|
103
|
-
console.error('WebSocket error:', error);
|
104
|
-
if (onError) onError(error);
|
105
|
-
};
|
106
|
-
|
107
|
-
ws.onclose = () => {
|
108
|
-
console.log('WebSocket disconnected');
|
109
|
-
};
|
110
|
-
|
111
|
-
return ws;
|
112
|
-
};
|
113
|
-
|
114
|
-
export default api;
|
@@ -1,152 +0,0 @@
|
|
1
|
-
import axios from 'axios';
|
2
|
-
|
3
|
-
// 在 React 应用中,环境变量需要以 REACT_APP_ 开头
|
4
|
-
// 使用 window.location 作为后备方案
|
5
|
-
const getApiBaseUrl = () => {
|
6
|
-
// 尝试从环境变量获取(Create React App 会在构建时注入)
|
7
|
-
if (typeof window !== 'undefined' && window.REACT_APP_API_URL) {
|
8
|
-
return window.REACT_APP_API_URL;
|
9
|
-
}
|
10
|
-
|
11
|
-
// 如果是生产环境,使用相同的域名和端口
|
12
|
-
if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {
|
13
|
-
return `${window.location.protocol}//${window.location.hostname}:8001`;
|
14
|
-
}
|
15
|
-
|
16
|
-
// 默认使用 localhost
|
17
|
-
return 'http://localhost:8001';
|
18
|
-
};
|
19
|
-
|
20
|
-
const API_BASE_URL = getApiBaseUrl();
|
21
|
-
|
22
|
-
/**
|
23
|
-
* 获取队列趋势数据
|
24
|
-
* @param {string} timeRange - 时间范围 (15m, 30m, 1h, 3h, 6h, 12h, 24h, 3d, 7d)
|
25
|
-
* @param {Array} queues - 队列名称列表
|
26
|
-
* @param {Object} customTimeRange - 自定义时间范围 {start: Date, end: Date}
|
27
|
-
* @returns {Promise<Array>} 趋势数据
|
28
|
-
*/
|
29
|
-
export const fetchQueueTrend = async (timeRange = '1h', queues = [], customTimeRange = null) => {
|
30
|
-
try {
|
31
|
-
const params = {
|
32
|
-
range: timeRange,
|
33
|
-
queues: queues.join(',')
|
34
|
-
};
|
35
|
-
|
36
|
-
// 如果有自定义时间范围
|
37
|
-
if (customTimeRange) {
|
38
|
-
params.start = customTimeRange[0].valueOf();
|
39
|
-
params.end = customTimeRange[1].valueOf();
|
40
|
-
}
|
41
|
-
|
42
|
-
const response = await axios.get(`${API_BASE_URL}/api/queue-trend`, { params });
|
43
|
-
|
44
|
-
// 处理响应数据 - 后端现在直接返回时间线数据
|
45
|
-
const data = response.data.data || response.data || [];
|
46
|
-
|
47
|
-
// 转换数据格式,保持时间字段为ISO字符串,让组件转换为Date对象
|
48
|
-
return data.map(item => ({
|
49
|
-
time: item.time || item.timestamp, // 保持原始时间字符串
|
50
|
-
value: item.value || item.count || 0,
|
51
|
-
queue: item.queue || item.queue_name
|
52
|
-
}));
|
53
|
-
} catch (error) {
|
54
|
-
console.error('Failed to fetch queue trend:', error);
|
55
|
-
|
56
|
-
// 如果API失败,返回模拟数据
|
57
|
-
return generateMockTrendData(timeRange, queues);
|
58
|
-
}
|
59
|
-
};
|
60
|
-
|
61
|
-
/**
|
62
|
-
* 生成模拟趋势数据(用于开发和测试)
|
63
|
-
*/
|
64
|
-
const generateMockTrendData = (timeRange, queues) => {
|
65
|
-
const now = Date.now();
|
66
|
-
const points = getDataPoints(timeRange);
|
67
|
-
const interval = getIntervalByRange(timeRange);
|
68
|
-
|
69
|
-
const data = [];
|
70
|
-
|
71
|
-
// 为每个队列生成数据
|
72
|
-
queues.slice(0, 3).forEach(queue => {
|
73
|
-
// 生成基础值和趋势
|
74
|
-
const baseValue = Math.floor(Math.random() * 50) + 20;
|
75
|
-
const trend = Math.random() > 0.5 ? 1 : -1;
|
76
|
-
|
77
|
-
for (let i = 0; i < points; i++) {
|
78
|
-
// 添加一些随机波动
|
79
|
-
const variation = Math.sin(i / 5) * 10 + Math.random() * 10 - 5;
|
80
|
-
const value = Math.max(0, baseValue + trend * i * 0.5 + variation);
|
81
|
-
|
82
|
-
data.push({
|
83
|
-
time: now - (points - i - 1) * interval,
|
84
|
-
value: Math.floor(value),
|
85
|
-
queue: queue.name || queue,
|
86
|
-
originalTime: new Date(now - (points - i - 1) * interval).toISOString()
|
87
|
-
});
|
88
|
-
}
|
89
|
-
});
|
90
|
-
|
91
|
-
return data;
|
92
|
-
};
|
93
|
-
|
94
|
-
/**
|
95
|
-
* 根据时间范围获取数据点数量
|
96
|
-
*/
|
97
|
-
const getDataPoints = (range) => {
|
98
|
-
const points = {
|
99
|
-
'15m': 15, // 每分钟一个点
|
100
|
-
'30m': 30, // 每分钟一个点
|
101
|
-
'1h': 30, // 每2分钟一个点
|
102
|
-
'3h': 36, // 每5分钟一个点
|
103
|
-
'6h': 36, // 每10分钟一个点
|
104
|
-
'12h': 36, // 每20分钟一个点
|
105
|
-
'24h': 48, // 每30分钟一个点
|
106
|
-
'3d': 36, // 每2小时一个点
|
107
|
-
'7d': 42, // 每4小时一个点
|
108
|
-
};
|
109
|
-
return points[range] || 30;
|
110
|
-
};
|
111
|
-
|
112
|
-
/**
|
113
|
-
* 根据时间范围获取数据间隔(毫秒)
|
114
|
-
*/
|
115
|
-
const getIntervalByRange = (range) => {
|
116
|
-
const intervals = {
|
117
|
-
'15m': 60 * 1000, // 1分钟
|
118
|
-
'30m': 60 * 1000, // 1分钟
|
119
|
-
'1h': 2 * 60 * 1000, // 2分钟
|
120
|
-
'3h': 5 * 60 * 1000, // 5分钟
|
121
|
-
'6h': 10 * 60 * 1000, // 10分钟
|
122
|
-
'12h': 20 * 60 * 1000, // 20分钟
|
123
|
-
'24h': 30 * 60 * 1000, // 30分钟
|
124
|
-
'3d': 2 * 60 * 60 * 1000, // 2小时
|
125
|
-
'7d': 4 * 60 * 60 * 1000, // 4小时
|
126
|
-
};
|
127
|
-
return intervals[range] || 60 * 1000;
|
128
|
-
};
|
129
|
-
|
130
|
-
/**
|
131
|
-
* 获取队列统计信息
|
132
|
-
*/
|
133
|
-
export const fetchQueueStats = async (queueName, timeRange = '1h') => {
|
134
|
-
try {
|
135
|
-
const response = await axios.get(`${API_BASE_URL}/api/queue/${queueName}/stats`, {
|
136
|
-
params: { range: timeRange }
|
137
|
-
});
|
138
|
-
|
139
|
-
return response.data;
|
140
|
-
} catch (error) {
|
141
|
-
console.error('Failed to fetch queue stats:', error);
|
142
|
-
return {
|
143
|
-
total: 0,
|
144
|
-
pending: 0,
|
145
|
-
running: 0,
|
146
|
-
completed: 0,
|
147
|
-
failed: 0,
|
148
|
-
avgProcessTime: 0,
|
149
|
-
throughput: 0
|
150
|
-
};
|
151
|
-
}
|
152
|
-
};
|
@@ -1,22 +0,0 @@
|
|
1
|
-
// 在开发环境中临时屏蔽特定的控制台警告
|
2
|
-
// 注意:这只是为了减少开发时的控制台噪音,不影响实际功能
|
3
|
-
|
4
|
-
if (process.env.NODE_ENV === 'development') {
|
5
|
-
const originalWarning = console.warn;
|
6
|
-
console.warn = (...args) => {
|
7
|
-
const warningMessage = args[0]?.toString() || '';
|
8
|
-
|
9
|
-
// 屏蔽 findDOMNode 相关警告(来自第三方库)
|
10
|
-
if (warningMessage.includes('findDOMNode')) {
|
11
|
-
return;
|
12
|
-
}
|
13
|
-
|
14
|
-
// 屏蔽 React Router v7 迁移提示
|
15
|
-
if (warningMessage.includes('React Router Future Flag Warning')) {
|
16
|
-
return;
|
17
|
-
}
|
18
|
-
|
19
|
-
// 其他警告正常显示
|
20
|
-
originalWarning.apply(console, args);
|
21
|
-
};
|
22
|
-
}
|
@@ -1,154 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* 用户偏好设置管理工具
|
3
|
-
*/
|
4
|
-
|
5
|
-
const STORAGE_KEY = 'jettask_user_preferences';
|
6
|
-
|
7
|
-
/**
|
8
|
-
* 获取用户偏好设置
|
9
|
-
* @returns {Object} 用户偏好设置对象
|
10
|
-
*/
|
11
|
-
export const getUserPreferences = () => {
|
12
|
-
try {
|
13
|
-
const stored = localStorage.getItem(STORAGE_KEY);
|
14
|
-
return stored ? JSON.parse(stored) : {};
|
15
|
-
} catch (error) {
|
16
|
-
console.error('Failed to load user preferences:', error);
|
17
|
-
return {};
|
18
|
-
}
|
19
|
-
};
|
20
|
-
|
21
|
-
/**
|
22
|
-
* 保存用户偏好设置
|
23
|
-
* @param {Object} preferences - 偏好设置对象
|
24
|
-
*/
|
25
|
-
export const saveUserPreferences = (preferences) => {
|
26
|
-
try {
|
27
|
-
const current = getUserPreferences();
|
28
|
-
console.log('[saveUserPreferences] 当前值:', current);
|
29
|
-
console.log('[saveUserPreferences] 要更新的值:', preferences);
|
30
|
-
const updated = { ...current, ...preferences };
|
31
|
-
console.log('[saveUserPreferences] 合并后的值:', updated);
|
32
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
33
|
-
console.log('[saveUserPreferences] 已保存到 localStorage');
|
34
|
-
} catch (error) {
|
35
|
-
console.error('Failed to save user preferences:', error);
|
36
|
-
}
|
37
|
-
};
|
38
|
-
|
39
|
-
/**
|
40
|
-
* 获取特定的偏好设置
|
41
|
-
* @param {string} key - 设置项的键
|
42
|
-
* @param {*} defaultValue - 默认值
|
43
|
-
* @returns {*} 设置值
|
44
|
-
*/
|
45
|
-
export const getPreference = (key, defaultValue = null) => {
|
46
|
-
const preferences = getUserPreferences();
|
47
|
-
return preferences[key] !== undefined ? preferences[key] : defaultValue;
|
48
|
-
};
|
49
|
-
|
50
|
-
/**
|
51
|
-
* 保存特定的偏好设置
|
52
|
-
* @param {string} key - 设置项的键
|
53
|
-
* @param {*} value - 设置值
|
54
|
-
*/
|
55
|
-
export const setPreference = (key, value) => {
|
56
|
-
console.log('[setPreference] 保存偏好设置 - key:', key, 'value:', value);
|
57
|
-
saveUserPreferences({ [key]: value });
|
58
|
-
// 立即验证保存
|
59
|
-
const saved = getUserPreferences();
|
60
|
-
console.log('[setPreference] 保存后立即验证 - saved[key]:', saved[key]);
|
61
|
-
};
|
62
|
-
|
63
|
-
/**
|
64
|
-
* 清除所有偏好设置
|
65
|
-
*/
|
66
|
-
export const clearPreferences = () => {
|
67
|
-
try {
|
68
|
-
localStorage.removeItem(STORAGE_KEY);
|
69
|
-
} catch (error) {
|
70
|
-
console.error('Failed to clear user preferences:', error);
|
71
|
-
}
|
72
|
-
};
|
73
|
-
|
74
|
-
// 偏好设置的键
|
75
|
-
export const PREFERENCE_KEYS = {
|
76
|
-
QUEUE_MONITOR_TIME_RANGE: 'queueMonitor.timeRange',
|
77
|
-
QUEUE_MONITOR_SELECTED_QUEUES: 'queueMonitor.selectedQueues',
|
78
|
-
QUEUE_MONITOR_CUSTOM_TIME_RANGE: 'queueMonitor.customTimeRange',
|
79
|
-
QUEUE_DETAILS_PAGE_SIZE: 'queueDetails.pageSize',
|
80
|
-
};
|
81
|
-
|
82
|
-
/**
|
83
|
-
* 获取队列特定的筛选条件
|
84
|
-
* @param {string} queueName - 队列名称
|
85
|
-
* @returns {Object} 队列的筛选设置
|
86
|
-
*/
|
87
|
-
export const getQueueFilters = (queueName) => {
|
88
|
-
const key = `queue.${queueName}.filters`;
|
89
|
-
const preferences = getUserPreferences();
|
90
|
-
const queueSettings = preferences[key] || {};
|
91
|
-
|
92
|
-
// 返回默认结构
|
93
|
-
return {
|
94
|
-
filters: queueSettings.filters || [],
|
95
|
-
timeRange: queueSettings.timeRange || '1h',
|
96
|
-
customTimeRange: queueSettings.customTimeRange || null
|
97
|
-
};
|
98
|
-
};
|
99
|
-
|
100
|
-
/**
|
101
|
-
* 保存队列特定的筛选条件
|
102
|
-
* @param {string} queueName - 队列名称
|
103
|
-
* @param {Object} settings - 筛选设置
|
104
|
-
*/
|
105
|
-
export const saveQueueFilters = (queueName, settings) => {
|
106
|
-
const key = `queue.${queueName}.filters`;
|
107
|
-
const currentPrefs = getUserPreferences();
|
108
|
-
|
109
|
-
// 限制存储的队列数量,防止localStorage过大
|
110
|
-
const queueKeys = Object.keys(currentPrefs).filter(k => k.startsWith('queue.'));
|
111
|
-
if (queueKeys.length > 50) {
|
112
|
-
// 删除最旧的队列设置(简单策略:删除第一个)
|
113
|
-
delete currentPrefs[queueKeys[0]];
|
114
|
-
}
|
115
|
-
|
116
|
-
// 保存新的设置
|
117
|
-
currentPrefs[key] = {
|
118
|
-
filters: settings.filters || [],
|
119
|
-
timeRange: settings.timeRange || '1h',
|
120
|
-
customTimeRange: settings.customTimeRange || null,
|
121
|
-
lastUpdated: new Date().toISOString()
|
122
|
-
};
|
123
|
-
|
124
|
-
try {
|
125
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(currentPrefs));
|
126
|
-
} catch (error) {
|
127
|
-
console.error('Failed to save queue filters:', error);
|
128
|
-
// 如果存储失败(可能是容量问题),清理旧数据
|
129
|
-
if (error.name === 'QuotaExceededError') {
|
130
|
-
// 清理最旧的一半队列设置
|
131
|
-
const halfLength = Math.floor(queueKeys.length / 2);
|
132
|
-
for (let i = 0; i < halfLength; i++) {
|
133
|
-
delete currentPrefs[queueKeys[i]];
|
134
|
-
}
|
135
|
-
// 重试保存
|
136
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(currentPrefs));
|
137
|
-
}
|
138
|
-
}
|
139
|
-
};
|
140
|
-
|
141
|
-
/**
|
142
|
-
* 清除特定队列的筛选设置
|
143
|
-
* @param {string} queueName - 队列名称
|
144
|
-
*/
|
145
|
-
export const clearQueueFilters = (queueName) => {
|
146
|
-
const key = `queue.${queueName}.filters`;
|
147
|
-
const currentPrefs = getUserPreferences();
|
148
|
-
delete currentPrefs[key];
|
149
|
-
try {
|
150
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(currentPrefs));
|
151
|
-
} catch (error) {
|
152
|
-
console.error('Failed to clear queue filters:', error);
|
153
|
-
}
|
154
|
-
};
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|