blob: 43030d9536070005f41b43707db14a1d321fe472 [file] [log] [blame]
86133aaa3f5d2025-04-20 21:33:29 +08001
2import React, { useState, useRef, useEffect } from 'react';
3import { useIntl, FormattedMessage, useAccess, history } from '@umijs/max';
4import { Dropdown, FormInstance, Space } from 'antd';
5import { Button, message, Modal } from 'antd';
6import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
7import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, DownOutlined, EditOutlined } from '@ant-design/icons';
8import { getJobList, removeJob, addJob, updateJob, exportJob, runJob } from '@/services/monitor/job';
9import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
10import UpdateForm from './edit';
11import DetailForm from './detail';
12import DictTag from '@/components/DictTag';
13
14/**
15 * 定时任务调度 List Page
16 *
17 * @author whiteshader
18 * @date 2023-02-07
19 */
20
21/**
22 * 添加节点
23 *
24 * @param fields
25 */
26const handleAdd = async (fields: API.Monitor.Job) => {
27 const hide = message.loading('正在添加');
28 try {
29 const resp = await addJob({ ...fields });
30 hide();
31 if (resp.code === 200) {
32 message.success('添加成功');
33 } else {
34 message.error(resp.msg);
35 }
36 return true;
37 } catch (error) {
38 hide();
39 message.error('添加失败请重试!');
40 return false;
41 }
42};
43
44/**
45 * 更新节点
46 *
47 * @param fields
48 */
49const handleUpdate = async (fields: API.Monitor.Job) => {
50 const hide = message.loading('正在更新');
51 try {
52 const resp = await updateJob(fields);
53 hide();
54 if (resp.code === 200) {
55 message.success('更新成功');
56 } else {
57 message.error(resp.msg);
58 }
59 return true;
60 } catch (error) {
61 hide();
62 message.error('配置失败请重试!');
63 return false;
64 }
65};
66
67/**
68 * 删除节点
69 *
70 * @param selectedRows
71 */
72const handleRemove = async (selectedRows: API.Monitor.Job[]) => {
73 const hide = message.loading('正在删除');
74 if (!selectedRows) return true;
75 try {
76 const resp = await removeJob(selectedRows.map((row) => row.jobId).join(','));
77 hide();
78 if (resp.code === 200) {
79 message.success('删除成功,即将刷新');
80 } else {
81 message.error(resp.msg);
82 }
83 return true;
84 } catch (error) {
85 hide();
86 message.error('删除失败,请重试');
87 return false;
88 }
89};
90
91const handleRemoveOne = async (selectedRow: API.Monitor.Job) => {
92 const hide = message.loading('正在删除');
93 if (!selectedRow) return true;
94 try {
95 const params = [selectedRow.jobId];
96 const resp = await removeJob(params.join(','));
97 hide();
98 if (resp.code === 200) {
99 message.success('删除成功,即将刷新');
100 } else {
101 message.error(resp.msg);
102 }
103 return true;
104 } catch (error) {
105 hide();
106 message.error('删除失败,请重试');
107 return false;
108 }
109};
110
111/**
112 * 导出数据
113 *
114 */
115const handleExport = async () => {
116 const hide = message.loading('正在导出');
117 try {
118 await exportJob();
119 hide();
120 message.success('导出成功');
121 return true;
122 } catch (error) {
123 hide();
124 message.error('导出失败,请重试');
125 return false;
126 }
127};
128
129
130const JobTableList: React.FC = () => {
131 const formTableRef = useRef<FormInstance>();
132
133 const [modalVisible, setModalVisible] = useState<boolean>(false);
134 const [detailModalVisible, setDetailModalVisible] = useState<boolean>(false);
135
136 const actionRef = useRef<ActionType>();
137 const [currentRow, setCurrentRow] = useState<API.Monitor.Job>();
138 const [selectedRows, setSelectedRows] = useState<API.Monitor.Job[]>([]);
139
140 const [jobGroupOptions, setJobGroupOptions] = useState<any>([]);
141 const [statusOptions, setStatusOptions] = useState<any>([]);
142
143 const access = useAccess();
144
145 /** 国际化配置 */
146 const intl = useIntl();
147
148 useEffect(() => {
149 getDictSelectOption('sys_job_group').then((data) => {
150 setJobGroupOptions(data);
151 });
152 getDictValueEnum('sys_normal_disable').then((data) => {
153 setStatusOptions(data);
154 });
155 }, []);
156
157 const columns: ProColumns<API.Monitor.Job>[] = [
158 {
159 title: <FormattedMessage id="monitor.job.job_id" defaultMessage="任务编号" />,
160 dataIndex: 'jobId',
161 valueType: 'text',
162 hideInSearch: true,
163 },
164 {
165 title: <FormattedMessage id="monitor.job.job_name" defaultMessage="任务名称" />,
166 dataIndex: 'jobName',
167 valueType: 'text',
168 render: (dom, record) => {
169 return (
170 <a
171 onClick={() => {
172 setDetailModalVisible(true);
173 setCurrentRow(record);
174 }}
175 >
176 {dom}
177 </a>
178 );
179 },
180 },
181 {
182 title: <FormattedMessage id="monitor.job.job_group" defaultMessage="任务组名" />,
183 dataIndex: 'jobGroup',
184 valueType: 'text',
185 valueEnum: jobGroupOptions,
186 render: (_, record) => {
187 return (<DictTag options={jobGroupOptions} value={record.jobGroup} />);
188 },
189 },
190 {
191 title: <FormattedMessage id="monitor.job.invoke_target" defaultMessage="调用目标字符串" />,
192 dataIndex: 'invokeTarget',
193 valueType: 'textarea',
194 },
195 {
196 title: <FormattedMessage id="monitor.job.cron_expression" defaultMessage="cron执行表达式" />,
197 dataIndex: 'cronExpression',
198 valueType: 'text',
199 },
200 {
201 title: <FormattedMessage id="monitor.job.status" defaultMessage="状态" />,
202 dataIndex: 'status',
203 valueType: 'select',
204 valueEnum: statusOptions,
205 render: (_, record) => {
206 return (<DictTag enums={statusOptions} value={record.status} />);
207 },
208 },
209 {
210 title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
211 dataIndex: 'option',
212 width: '220px',
213 valueType: 'option',
214 render: (_, record) => [
215 <Button
216 type="link"
217 size="small"
218 key="edit"
219 icon = <EditOutlined />
220 hidden={!access.hasPerms('monitor:job:edit')}
221 onClick={() => {
222 setModalVisible(true);
223 setCurrentRow(record);
224 }}
225 >
226 编辑
227 </Button>,
228 <Button
229 type="link"
230 size="small"
231 danger
232 key="batchRemove"
233 icon = <DeleteOutlined />
234 hidden={!access.hasPerms('monitor:job:remove')}
235 onClick={async () => {
236 Modal.confirm({
237 title: '删除',
238 content: '确定删除该项吗?',
239 okText: '确认',
240 cancelText: '取消',
241 onOk: async () => {
242 const success = await handleRemoveOne(record);
243 if (success) {
244 if (actionRef.current) {
245 actionRef.current.reload();
246 }
247 }
248 },
249 });
250 }}
251 >
252 删除
253 </Button>,
254 <Dropdown
255 key="more"
256 menu={{
257 items: [
258 {
259 label: '执行一次',
260 key: 'runOnce',
261 },
262 {
263 label: '详细',
264 key: 'detail',
265 },
266 {
267 label: '历史',
268 key: 'log',
269 },
270 ],
271 onClick: ({ key }) => {
272 if (key === 'runOnce') {
273 Modal.confirm({
274 title: '警告',
275 content: '确认要立即执行一次?',
276 okText: '确认',
277 cancelText: '取消',
278 onOk: async () => {
279 const success = await runJob(record.jobId, record.jobGroup);
280 if (success) {
281 message.success('执行成功');
282 }
283 },
284 });
285 }
286 else if (key === 'detail') {
287 setDetailModalVisible(true);
288 setCurrentRow(record);
289 }
290 else if( key === 'log') {
291 history.push(`/monitor/job-log/index/${record.jobId}`);
292 }
293 }
294 }}
295 >
296 <a className="ant-dropdown-link" onClick={(e) => e.preventDefault()}>
297 <Space>
298 <DownOutlined />
299 更多
300 </Space>
301 </a>
302 </Dropdown>,
303 ],
304 },
305 ];
306
307 return (
308 <PageContainer>
309 <div style={{ width: '100%', float: 'right' }}>
310 <ProTable<API.Monitor.Job>
311 headerTitle={intl.formatMessage({
312 id: 'pages.searchTable.title',
313 defaultMessage: '信息',
314 })}
315 actionRef={actionRef}
316 formRef={formTableRef}
317 rowKey="jobId"
318 key="jobList"
319 search={{
320 labelWidth: 120,
321 }}
322 toolBarRender={() => [
323 <Button
324 type="primary"
325 key="add"
326 hidden={!access.hasPerms('monitor:job:add')}
327 onClick={async () => {
328 setCurrentRow(undefined);
329 setModalVisible(true);
330 }}
331 >
332 <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
333 </Button>,
334 <Button
335 type="primary"
336 key="remove"
337 danger
338 hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:job:remove')}
339 onClick={async () => {
340 Modal.confirm({
341 title: '是否确认删除所选数据项?',
342 icon: <ExclamationCircleOutlined />,
343 content: '请谨慎操作',
344 async onOk() {
345 const success = await handleRemove(selectedRows);
346 if (success) {
347 setSelectedRows([]);
348 actionRef.current?.reloadAndRest?.();
349 }
350 },
351 onCancel() { },
352 });
353 }}
354 >
355 <DeleteOutlined />
356 <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
357 </Button>,
358 <Button
359 type="primary"
360 key="export"
361 hidden={!access.hasPerms('monitor:job:export')}
362 onClick={async () => {
363 handleExport();
364 }}
365 >
366 <PlusOutlined />
367 <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
368 </Button>,
369 ]}
370 request={(params) =>
371 getJobList({ ...params } as API.Monitor.JobListParams).then((res) => {
372 const result = {
373 data: res.rows,
374 total: res.total,
375 success: true,
376 };
377 return result;
378 })
379 }
380 columns={columns}
381 rowSelection={{
382 onChange: (_, selectedRows) => {
383 setSelectedRows(selectedRows);
384 },
385 }}
386 />
387 </div>
388 {selectedRows?.length > 0 && (
389 <FooterToolbar
390 extra={
391 <div>
392 <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
393 <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
394 <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
395 </div>
396 }
397 >
398 <Button
399 key="remove"
400 danger
401 hidden={!access.hasPerms('monitor:job:del')}
402 onClick={async () => {
403 Modal.confirm({
404 title: '删除',
405 content: '确定删除该项吗?',
406 okText: '确认',
407 cancelText: '取消',
408 onOk: async () => {
409 const success = await handleRemove(selectedRows);
410 if (success) {
411 setSelectedRows([]);
412 actionRef.current?.reloadAndRest?.();
413 }
414 },
415 });
416 }}
417 >
418 <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
419 </Button>
420 </FooterToolbar>
421 )}
422 <UpdateForm
423 onSubmit={async (values) => {
424 let success = false;
425 if (values.jobId) {
426 success = await handleUpdate({ ...values } as API.Monitor.Job);
427 } else {
428 success = await handleAdd({ ...values } as API.Monitor.Job);
429 }
430 if (success) {
431 setModalVisible(false);
432 setCurrentRow(undefined);
433 if (actionRef.current) {
434 actionRef.current.reload();
435 }
436 }
437 }}
438 onCancel={() => {
439 setModalVisible(false);
440 setCurrentRow(undefined);
441 }}
442 open={modalVisible}
443 values={currentRow || {}}
444 jobGroupOptions={jobGroupOptions||{}}
445 statusOptions={statusOptions}
446 />
447 <DetailForm
448 onCancel={() => {
449 setDetailModalVisible(false);
450 setCurrentRow(undefined);
451 }}
452 open={detailModalVisible}
453 values={currentRow || {}}
454 statusOptions={statusOptions}
455 />
456 </PageContainer>
457 );
458};
459
460export default JobTableList;