jettask 0.2.5__py3-none-any.whl → 0.2.6__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.6.dist-info}/METADATA +11 -9
- {jettask-0.2.5.dist-info → jettask-0.2.6.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.6.dist-info}/WHEEL +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.5.dist-info → jettask-0.2.6.dist-info}/top_level.txt +0 -0
@@ -1,423 +0,0 @@
|
|
1
|
-
import React, { useState } from 'react';
|
2
|
-
import { Button, Popover, Form, Select, Input, Space, Tag, message } from 'antd';
|
3
|
-
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
|
4
|
-
|
5
|
-
const { Option } = Select;
|
6
|
-
|
7
|
-
// 定时任务可用的字段列表
|
8
|
-
const AVAILABLE_FIELDS = [
|
9
|
-
{ value: 'id', label: '任务ID', type: 'number' },
|
10
|
-
{ value: 'scheduler_id', label: '调度器ID', type: 'string' },
|
11
|
-
{ value: 'name', label: '任务名称', type: 'string' },
|
12
|
-
{ value: 'queue_name', label: '队列名称', type: 'string' },
|
13
|
-
{ value: 'schedule_type', label: '调度类型', type: 'enum', options: ['cron', 'interval', 'once'] },
|
14
|
-
{ value: 'is_active', label: '状态', type: 'boolean' },
|
15
|
-
{ value: 'description', label: '描述', type: 'string' },
|
16
|
-
{ value: 'last_run', label: '上次执行时间', type: 'datetime' },
|
17
|
-
{ value: 'next_run', label: '下次执行时间', type: 'datetime' },
|
18
|
-
{ value: 'created_at', label: '创建时间', type: 'datetime' },
|
19
|
-
{ value: 'task_data', label: '任务参数', type: 'json' },
|
20
|
-
{ value: 'tags', label: '标签', type: 'json' },
|
21
|
-
{ value: 'metadata', label: '元数据', type: 'json' },
|
22
|
-
];
|
23
|
-
|
24
|
-
// 操作符列表
|
25
|
-
const OPERATORS = {
|
26
|
-
string: [
|
27
|
-
{ value: 'eq', label: '等于' },
|
28
|
-
{ value: 'ne', label: '不等于' },
|
29
|
-
{ value: 'contains', label: '包含' },
|
30
|
-
{ value: 'starts_with', label: '开始于' },
|
31
|
-
{ value: 'ends_with', label: '结束于' },
|
32
|
-
{ value: 'is_null', label: '为空' },
|
33
|
-
{ value: 'is_not_null', label: '不为空' },
|
34
|
-
],
|
35
|
-
number: [
|
36
|
-
{ value: 'eq', label: '等于' },
|
37
|
-
{ value: 'ne', label: '不等于' },
|
38
|
-
{ value: 'gt', label: '大于' },
|
39
|
-
{ value: 'lt', label: '小于' },
|
40
|
-
{ value: 'gte', label: '大于等于' },
|
41
|
-
{ value: 'lte', label: '小于等于' },
|
42
|
-
{ value: 'is_null', label: '为空' },
|
43
|
-
{ value: 'is_not_null', label: '不为空' },
|
44
|
-
],
|
45
|
-
datetime: [
|
46
|
-
{ value: 'gt', label: '晚于' },
|
47
|
-
{ value: 'lt', label: '早于' },
|
48
|
-
{ value: 'gte', label: '不早于' },
|
49
|
-
{ value: 'lte', label: '不晚于' },
|
50
|
-
{ value: 'is_null', label: '为空' },
|
51
|
-
{ value: 'is_not_null', label: '不为空' },
|
52
|
-
],
|
53
|
-
enum: [
|
54
|
-
{ value: 'eq', label: '等于' },
|
55
|
-
{ value: 'ne', label: '不等于' },
|
56
|
-
{ value: 'in', label: '在列表中' },
|
57
|
-
{ value: 'not_in', label: '不在列表中' },
|
58
|
-
],
|
59
|
-
boolean: [
|
60
|
-
{ value: 'eq', label: '等于' },
|
61
|
-
],
|
62
|
-
json: [
|
63
|
-
{ value: 'contains', label: '包含文本' },
|
64
|
-
{ value: 'json_key_exists', label: '包含键名' },
|
65
|
-
{ value: 'json_path_value', label: 'JSON路径值' },
|
66
|
-
{ value: 'is_null', label: '为空' },
|
67
|
-
{ value: 'is_not_null', label: '不为空' },
|
68
|
-
],
|
69
|
-
};
|
70
|
-
|
71
|
-
function ScheduledTaskFilter({ filters, onFiltersChange }) {
|
72
|
-
const [visible, setVisible] = useState(false);
|
73
|
-
const [form] = Form.useForm();
|
74
|
-
const [selectedField, setSelectedField] = useState(null);
|
75
|
-
const [selectedOperator, setSelectedOperator] = useState(null);
|
76
|
-
const [disabledFilters, setDisabledFilters] = useState(new Set()); // 记录被禁用的筛选条件索引
|
77
|
-
|
78
|
-
// 获取当前字段的类型
|
79
|
-
const getFieldType = (fieldValue) => {
|
80
|
-
const field = AVAILABLE_FIELDS.find(f => f.value === fieldValue);
|
81
|
-
return field ? field.type : 'string';
|
82
|
-
};
|
83
|
-
|
84
|
-
// 获取当前字段的选项(用于枚举类型)
|
85
|
-
const getFieldOptions = (fieldValue) => {
|
86
|
-
const field = AVAILABLE_FIELDS.find(f => f.value === fieldValue);
|
87
|
-
return field && field.options ? field.options : [];
|
88
|
-
};
|
89
|
-
|
90
|
-
// 添加筛选条件
|
91
|
-
const handleAddFilter = () => {
|
92
|
-
form.validateFields().then(values => {
|
93
|
-
const newFilter = {
|
94
|
-
id: Date.now(), // 添加唯一ID
|
95
|
-
field: values.field,
|
96
|
-
operator: values.operator,
|
97
|
-
value: values.value,
|
98
|
-
enabled: true, // 默认启用
|
99
|
-
};
|
100
|
-
|
101
|
-
// 处理特殊操作符
|
102
|
-
if (values.operator === 'is_null' || values.operator === 'is_not_null') {
|
103
|
-
newFilter.value = null;
|
104
|
-
} else if (values.operator === 'in' || values.operator === 'not_in') {
|
105
|
-
// 如果是枚举类型的多选
|
106
|
-
if (getFieldType(values.field) === 'enum') {
|
107
|
-
newFilter.value = values.value; // 已经是数组
|
108
|
-
} else {
|
109
|
-
// 普通文本,用逗号分隔
|
110
|
-
newFilter.value = values.value.split(',').map(v => v.trim());
|
111
|
-
}
|
112
|
-
} else if (getFieldType(values.field) === 'boolean') {
|
113
|
-
// 布尔类型转换
|
114
|
-
newFilter.value = values.value === 'true';
|
115
|
-
}
|
116
|
-
|
117
|
-
const updatedFilters = [...(filters || []), newFilter];
|
118
|
-
onFiltersChange(updatedFilters);
|
119
|
-
|
120
|
-
// 重置表单
|
121
|
-
form.resetFields();
|
122
|
-
setSelectedField(null);
|
123
|
-
setSelectedOperator(null);
|
124
|
-
setVisible(false);
|
125
|
-
message.success('筛选条件已添加');
|
126
|
-
});
|
127
|
-
};
|
128
|
-
|
129
|
-
// 切换筛选条件的启用/禁用状态
|
130
|
-
const handleToggleFilter = (index) => {
|
131
|
-
const updatedFilters = [...filters];
|
132
|
-
updatedFilters[index] = {
|
133
|
-
...updatedFilters[index],
|
134
|
-
enabled: !updatedFilters[index].enabled
|
135
|
-
};
|
136
|
-
onFiltersChange(updatedFilters);
|
137
|
-
};
|
138
|
-
|
139
|
-
// 删除筛选条件
|
140
|
-
const handleRemoveFilter = (index) => {
|
141
|
-
const updatedFilters = filters.filter((_, i) => i !== index);
|
142
|
-
onFiltersChange(updatedFilters);
|
143
|
-
message.success('筛选条件已删除');
|
144
|
-
};
|
145
|
-
|
146
|
-
// 渲染筛选条件标签
|
147
|
-
const renderFilterTag = (filter, index) => {
|
148
|
-
const field = AVAILABLE_FIELDS.find(f => f.value === filter.field);
|
149
|
-
const fieldLabel = field ? field.label : filter.field;
|
150
|
-
|
151
|
-
const operator = OPERATORS[getFieldType(filter.field)]?.find(op => op.value === filter.operator);
|
152
|
-
const operatorLabel = operator ? operator.label : filter.operator;
|
153
|
-
|
154
|
-
let valueLabel = filter.value;
|
155
|
-
if (filter.operator === 'is_null' || filter.operator === 'is_not_null') {
|
156
|
-
valueLabel = '';
|
157
|
-
} else if (Array.isArray(filter.value)) {
|
158
|
-
valueLabel = filter.value.join(', ');
|
159
|
-
} else if (typeof filter.value === 'boolean') {
|
160
|
-
valueLabel = filter.value ? '是' : '否';
|
161
|
-
}
|
162
|
-
|
163
|
-
const tagLabel = `${fieldLabel} ${operatorLabel} ${valueLabel}`.trim();
|
164
|
-
const isDisabled = filter.enabled === false;
|
165
|
-
|
166
|
-
return (
|
167
|
-
<Tag
|
168
|
-
key={index}
|
169
|
-
closable
|
170
|
-
onClose={() => handleRemoveFilter(index)}
|
171
|
-
onClick={() => handleToggleFilter(index)}
|
172
|
-
style={{
|
173
|
-
margin: 0,
|
174
|
-
height: '24px',
|
175
|
-
lineHeight: '22px',
|
176
|
-
display: 'inline-flex',
|
177
|
-
alignItems: 'center',
|
178
|
-
cursor: 'pointer',
|
179
|
-
opacity: isDisabled ? 0.5 : 1,
|
180
|
-
textDecoration: isDisabled ? 'line-through' : 'none',
|
181
|
-
backgroundColor: isDisabled ? '#f5f5f5' : undefined,
|
182
|
-
borderStyle: isDisabled ? 'dashed' : 'solid',
|
183
|
-
}}
|
184
|
-
color={isDisabled ? 'default' : undefined}
|
185
|
-
>
|
186
|
-
{tagLabel}
|
187
|
-
</Tag>
|
188
|
-
);
|
189
|
-
};
|
190
|
-
|
191
|
-
// 渲染值输入组件
|
192
|
-
const renderValueInput = () => {
|
193
|
-
if (!selectedField) return null;
|
194
|
-
|
195
|
-
const fieldType = getFieldType(selectedField);
|
196
|
-
const needsValue = selectedOperator !== 'is_null' && selectedOperator !== 'is_not_null';
|
197
|
-
|
198
|
-
if (!needsValue) return null;
|
199
|
-
|
200
|
-
if (fieldType === 'enum') {
|
201
|
-
const options = getFieldOptions(selectedField);
|
202
|
-
if (selectedOperator === 'in' || selectedOperator === 'not_in') {
|
203
|
-
return (
|
204
|
-
<Form.Item
|
205
|
-
name="value"
|
206
|
-
label="值"
|
207
|
-
rules={[{ required: true, message: '请选择值' }]}
|
208
|
-
>
|
209
|
-
<Select mode="multiple" placeholder="选择多个值">
|
210
|
-
{options.map(opt => (
|
211
|
-
<Option key={opt} value={opt}>{opt}</Option>
|
212
|
-
))}
|
213
|
-
</Select>
|
214
|
-
</Form.Item>
|
215
|
-
);
|
216
|
-
} else {
|
217
|
-
return (
|
218
|
-
<Form.Item
|
219
|
-
name="value"
|
220
|
-
label="值"
|
221
|
-
rules={[{ required: true, message: '请选择值' }]}
|
222
|
-
>
|
223
|
-
<Select placeholder="选择值">
|
224
|
-
{options.map(opt => (
|
225
|
-
<Option key={opt} value={opt}>{opt}</Option>
|
226
|
-
))}
|
227
|
-
</Select>
|
228
|
-
</Form.Item>
|
229
|
-
);
|
230
|
-
}
|
231
|
-
} else if (fieldType === 'boolean') {
|
232
|
-
return (
|
233
|
-
<Form.Item
|
234
|
-
name="value"
|
235
|
-
label="值"
|
236
|
-
rules={[{ required: true, message: '请选择值' }]}
|
237
|
-
>
|
238
|
-
<Select placeholder="选择值">
|
239
|
-
<Option value="true">是</Option>
|
240
|
-
<Option value="false">否</Option>
|
241
|
-
</Select>
|
242
|
-
</Form.Item>
|
243
|
-
);
|
244
|
-
} else if (fieldType === 'number') {
|
245
|
-
return (
|
246
|
-
<Form.Item
|
247
|
-
name="value"
|
248
|
-
label="值"
|
249
|
-
rules={[{ required: true, message: '请输入值' }]}
|
250
|
-
>
|
251
|
-
<Input type="number" placeholder="输入数值" />
|
252
|
-
</Form.Item>
|
253
|
-
);
|
254
|
-
} else if (fieldType === 'datetime') {
|
255
|
-
return (
|
256
|
-
<Form.Item
|
257
|
-
name="value"
|
258
|
-
label="值"
|
259
|
-
rules={[{ required: true, message: '请输入值' }]}
|
260
|
-
>
|
261
|
-
<Input
|
262
|
-
placeholder="格式: YYYY-MM-DD HH:mm:ss"
|
263
|
-
title="请输入日期时间,格式: YYYY-MM-DD HH:mm:ss"
|
264
|
-
/>
|
265
|
-
</Form.Item>
|
266
|
-
);
|
267
|
-
} else if (fieldType === 'json') {
|
268
|
-
// JSON类型的输入
|
269
|
-
let label = '搜索内容';
|
270
|
-
let placeholder = '输入搜索内容';
|
271
|
-
let extra = '';
|
272
|
-
|
273
|
-
if (selectedOperator === 'json_key_exists') {
|
274
|
-
label = '键名';
|
275
|
-
placeholder = '输入要查找的键名';
|
276
|
-
extra = '检查JSON中是否存在指定的键';
|
277
|
-
} else if (selectedOperator === 'json_path_value') {
|
278
|
-
label = 'JSON路径和值';
|
279
|
-
placeholder = '路径=值,如:$.user_id=123 或 $.kwargs.message=test';
|
280
|
-
extra = '使用JSON路径语法,格式:路径=值。支持嵌套路径如 $.kwargs.user_id';
|
281
|
-
} else if (selectedOperator === 'contains') {
|
282
|
-
label = '搜索文本';
|
283
|
-
placeholder = '输入要搜索的文本内容';
|
284
|
-
extra = '在JSON数据中搜索包含此文本的任务';
|
285
|
-
}
|
286
|
-
|
287
|
-
return (
|
288
|
-
<Form.Item
|
289
|
-
name="value"
|
290
|
-
label={label}
|
291
|
-
rules={[{ required: true, message: `请输入${label}` }]}
|
292
|
-
extra={extra}
|
293
|
-
>
|
294
|
-
<Input.TextArea
|
295
|
-
placeholder={placeholder}
|
296
|
-
rows={2}
|
297
|
-
spellCheck={false}
|
298
|
-
/>
|
299
|
-
</Form.Item>
|
300
|
-
);
|
301
|
-
} else {
|
302
|
-
return (
|
303
|
-
<Form.Item
|
304
|
-
name="value"
|
305
|
-
label="值"
|
306
|
-
rules={[{ required: true, message: '请输入值' }]}
|
307
|
-
>
|
308
|
-
<Input placeholder={selectedOperator === 'in' || selectedOperator === 'not_in' ? '多个值用逗号分隔' : '输入值'} />
|
309
|
-
</Form.Item>
|
310
|
-
);
|
311
|
-
}
|
312
|
-
};
|
313
|
-
|
314
|
-
const filterContent = (
|
315
|
-
<div style={{ width: 350 }}>
|
316
|
-
<Form
|
317
|
-
form={form}
|
318
|
-
layout="vertical"
|
319
|
-
onFinish={handleAddFilter}
|
320
|
-
>
|
321
|
-
<Form.Item
|
322
|
-
name="field"
|
323
|
-
label="字段"
|
324
|
-
rules={[{ required: true, message: '请选择字段' }]}
|
325
|
-
>
|
326
|
-
<Select
|
327
|
-
placeholder="选择字段"
|
328
|
-
onChange={(value) => {
|
329
|
-
setSelectedField(value);
|
330
|
-
setSelectedOperator(null);
|
331
|
-
form.setFieldsValue({ operator: undefined, value: undefined });
|
332
|
-
}}
|
333
|
-
>
|
334
|
-
{AVAILABLE_FIELDS.map(field => (
|
335
|
-
<Option key={field.value} value={field.value}>
|
336
|
-
{field.label}
|
337
|
-
</Option>
|
338
|
-
))}
|
339
|
-
</Select>
|
340
|
-
</Form.Item>
|
341
|
-
|
342
|
-
{selectedField && (
|
343
|
-
<Form.Item
|
344
|
-
name="operator"
|
345
|
-
label="操作符"
|
346
|
-
rules={[{ required: true, message: '请选择操作符' }]}
|
347
|
-
>
|
348
|
-
<Select
|
349
|
-
placeholder="选择操作符"
|
350
|
-
onChange={(value) => {
|
351
|
-
setSelectedOperator(value);
|
352
|
-
form.setFieldsValue({ value: undefined });
|
353
|
-
}}
|
354
|
-
>
|
355
|
-
{OPERATORS[getFieldType(selectedField)]?.map(op => (
|
356
|
-
<Option key={op.value} value={op.value}>
|
357
|
-
{op.label}
|
358
|
-
</Option>
|
359
|
-
))}
|
360
|
-
</Select>
|
361
|
-
</Form.Item>
|
362
|
-
)}
|
363
|
-
|
364
|
-
{renderValueInput()}
|
365
|
-
|
366
|
-
<Form.Item>
|
367
|
-
<Space>
|
368
|
-
<Button type="primary" htmlType="submit">
|
369
|
-
添加筛选条件
|
370
|
-
</Button>
|
371
|
-
<Button onClick={() => {
|
372
|
-
setVisible(false);
|
373
|
-
form.resetFields();
|
374
|
-
setSelectedField(null);
|
375
|
-
setSelectedOperator(null);
|
376
|
-
}}>
|
377
|
-
取消
|
378
|
-
</Button>
|
379
|
-
</Space>
|
380
|
-
</Form.Item>
|
381
|
-
</Form>
|
382
|
-
</div>
|
383
|
-
);
|
384
|
-
|
385
|
-
return (
|
386
|
-
<div style={{
|
387
|
-
display: 'inline-flex',
|
388
|
-
alignItems: 'center',
|
389
|
-
flexWrap: 'wrap',
|
390
|
-
gap: '8px',
|
391
|
-
verticalAlign: 'middle'
|
392
|
-
}}>
|
393
|
-
{filters && filters.map((filter, index) => renderFilterTag(filter, index))}
|
394
|
-
|
395
|
-
<Popover
|
396
|
-
content={filterContent}
|
397
|
-
title="添加筛选条件"
|
398
|
-
trigger="click"
|
399
|
-
open={visible}
|
400
|
-
onOpenChange={setVisible}
|
401
|
-
placement="bottomLeft"
|
402
|
-
>
|
403
|
-
<Button
|
404
|
-
type="dashed"
|
405
|
-
icon={<PlusOutlined />}
|
406
|
-
size="small"
|
407
|
-
style={{
|
408
|
-
height: '24px',
|
409
|
-
padding: '0 8px',
|
410
|
-
display: 'inline-flex',
|
411
|
-
alignItems: 'center',
|
412
|
-
lineHeight: '22px',
|
413
|
-
verticalAlign: 'middle'
|
414
|
-
}}
|
415
|
-
>
|
416
|
-
Add Filter
|
417
|
-
</Button>
|
418
|
-
</Popover>
|
419
|
-
</div>
|
420
|
-
);
|
421
|
-
}
|
422
|
-
|
423
|
-
export default ScheduledTaskFilter;
|