page.tsx 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  1. "use client";
  2. import {fetchApi, fetchFile} from "@/app/_modules/func";
  3. import {
  4. CaretDownOutlined,
  5. CheckOutlined,
  6. ClockCircleOutlined,
  7. CloseOutlined,
  8. DeleteOutlined,
  9. ExclamationCircleFilled,
  10. EyeOutlined,
  11. PlayCircleOutlined,
  12. PlusOutlined,
  13. ReloadOutlined,
  14. ScheduleOutlined,
  15. } from "@ant-design/icons";
  16. import type {ActionType, ProColumns, ProFormInstance,} from "@ant-design/pro-components";
  17. import {
  18. ModalForm,
  19. PageContainer,
  20. ProDescriptions,
  21. ProForm,
  22. ProFormRadio,
  23. ProFormSelect,
  24. ProFormText,
  25. ProTable,
  26. } from "@ant-design/pro-components";
  27. import {Button, Dropdown, Input, Modal, Space, Switch,} from "antd";
  28. import {useRouter} from "next/navigation";
  29. import {faDownload, faPenToSquare, faToggleOff, faToggleOn,} from "@fortawesome/free-solid-svg-icons";
  30. import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
  31. import "./styles.scss";
  32. import {ReQuartzCron} from "@sbzen/re-cron";
  33. import {useRef, useState} from "react";
  34. import {MortnonCronLocalization} from "@/app/_modules/definies";
  35. //查询表格数据API
  36. const queryAPI = "/api/monitor/job/list";
  37. //新建数据API
  38. const newAPI = "/api/monitor/job";
  39. //修改数据API
  40. const modifyAPI = "/api/monitor/job";
  41. //查询详情数据API
  42. const queryDetailAPI = "/api/monitor/job";
  43. //删除API
  44. const deleteAPI = "/api/monitor/job";
  45. //导出API
  46. const exportAPI = "/api/monitor/job/export";
  47. //导出文件前缀名
  48. const exportFilePrefix = "job";
  49. //变更任务状态API
  50. const changeJobStatusAPI = "/api/monitor/job/changeStatus";
  51. //执行任务API
  52. const runAPI = "/api/monitor/job/run";
  53. export default function Job() {
  54. const { push } = useRouter();
  55. // 添加用于控制删除确认模态框的状态
  56. const [deleteModalVisible, setDeleteModalVisible] = useState(false);
  57. const [deleteJobId, setDeleteJobId] = useState<string | number | null>(null);
  58. // 添加用于控制切换状态确认模态框的状态
  59. const [statusModalVisible, setStatusModalVisible] = useState(false);
  60. const [statusChecked, setStatusChecked] = useState<boolean>(true);
  61. const [statusRecord, setStatusRecord] = useState<any>(null);
  62. // 添加用于控制执行任务确认模态框的状态
  63. const [runModalVisible, setRunModalVisible] = useState(false);
  64. const [runRecord, setRunRecord] = useState<any>(null);
  65. //表格列定义
  66. const columns: ProColumns[] = [
  67. {
  68. title: "任务编号",
  69. dataIndex: "jobId",
  70. search: false,
  71. },
  72. {
  73. title: "任务名称",
  74. fieldProps: {
  75. placeholder: "请输入任务名称",
  76. },
  77. dataIndex: "jobName",
  78. ellipsis: true,
  79. order: 3,
  80. },
  81. {
  82. title: "任务组名",
  83. dataIndex: "jobGroup",
  84. valueType: "select",
  85. valueEnum: {
  86. DEFAULT: {
  87. text: "默认",
  88. status: "DEFAULT",
  89. },
  90. SYSTEM: {
  91. text: "系统",
  92. status: "SYSTEM",
  93. },
  94. },
  95. sorter: true,
  96. order: 2,
  97. },
  98. {
  99. title: "调用目标字符串",
  100. dataIndex: "invokeTarget",
  101. search: false,
  102. },
  103. {
  104. title: "Cron执行表达式",
  105. dataIndex: "cronExpression",
  106. search: false,
  107. },
  108. {
  109. title: "状态",
  110. fieldProps: {
  111. placeholder: "请选择任务状态",
  112. },
  113. dataIndex: "status",
  114. valueType: "select",
  115. order: 2,
  116. valueEnum: {
  117. 0: {
  118. text: "正常",
  119. status: "0",
  120. },
  121. 1: {
  122. text: "停用",
  123. status: "1",
  124. },
  125. },
  126. render: (text, record) => {
  127. return (
  128. <Space>
  129. <Switch
  130. checkedChildren={<CheckOutlined />}
  131. unCheckedChildren={<CloseOutlined />}
  132. defaultChecked={record.status === "0"}
  133. checked={rowStatusMap[record.jobId]}
  134. onChange={(checked, event) => {
  135. showSwitchJobStatusModal(checked, record);
  136. }}
  137. />
  138. </Space>
  139. );
  140. },
  141. },
  142. {
  143. title: "操作",
  144. key: "option",
  145. search: false,
  146. render: (_, record) => [
  147. <Button
  148. key="modifyBtn"
  149. type="link"
  150. icon={<FontAwesomeIcon icon={faPenToSquare} />}
  151. onClick={() => onClickShowRowModifyModal(record)}
  152. >
  153. 修改
  154. </Button>,
  155. <Button
  156. key="deleteBtn"
  157. type="link"
  158. danger
  159. icon={<DeleteOutlined />}
  160. onClick={() => onClickDeleteRow(record)}
  161. >
  162. 删除
  163. </Button>,
  164. <Dropdown
  165. key="moreDrop"
  166. menu={{
  167. items: [
  168. {
  169. key: "1",
  170. label: (
  171. <a
  172. onClick={() => {
  173. showRunOnceModal(record);
  174. }}
  175. >
  176. 执行一次
  177. </a>
  178. ),
  179. icon: <PlayCircleOutlined />,
  180. },
  181. {
  182. key: "2",
  183. label: <a onClick={() => showRowModal(record)}>任务详情</a>,
  184. icon: <EyeOutlined />,
  185. },
  186. {
  187. key: "3",
  188. label: (
  189. <a
  190. onClick={() =>
  191. push(`/monitor/job-log/index/${record.jobId}`)
  192. }
  193. >
  194. 调度日志
  195. </a>
  196. ),
  197. icon: <ScheduleOutlined />,
  198. },
  199. ],
  200. }}
  201. >
  202. <a onClick={(e) => e.preventDefault()}>
  203. <Space>
  204. 更多
  205. <CaretDownOutlined />
  206. </Space>
  207. </a>
  208. </Dropdown>,
  209. ],
  210. },
  211. ];
  212. //0.查询表格数据
  213. const queryTableData = async (params: any, sorter: any, filter: any) => {
  214. const searchParams = {
  215. pageNum: params.current,
  216. ...params,
  217. };
  218. delete searchParams.current;
  219. const queryParams = new URLSearchParams(searchParams);
  220. Object.keys(sorter).forEach((key) => {
  221. queryParams.append("orderByColumn", key);
  222. if (sorter[key] === "ascend") {
  223. queryParams.append("isAsc", "ascending");
  224. } else {
  225. queryParams.append("isAsc", "descending");
  226. }
  227. });
  228. const body = await fetchApi(`${queryAPI}?${queryParams}`, push);
  229. if (body !== undefined) {
  230. body.rows.forEach((row: any) => {
  231. setRowStatusMap({ ...rowStatusMap, [row.userId]: row.status === "0" });
  232. });
  233. }
  234. return body;
  235. };
  236. //1.新建
  237. //新建对话框表单引用
  238. const addFormRef = useRef<ProFormInstance>(null);
  239. //确定新建数据
  240. const executeAddData = async (values: any) => {
  241. const body = await fetchApi(newAPI, push, {
  242. method: "POST",
  243. headers: {
  244. "Content-Type": "application/json",
  245. },
  246. body: JSON.stringify(values),
  247. });
  248. if (body != undefined) {
  249. if (body.code == 200) {
  250. App.useApp().message.success(body.msg);
  251. if (actionTableRef.current) {
  252. actionTableRef.current.reload();
  253. }
  254. return true;
  255. }
  256. App.useApp().message.error(body.msg);
  257. return false;
  258. }
  259. return false;
  260. };
  261. //2.修改
  262. //控制行的状态值的恢复
  263. const [rowStatusMap, setRowStatusMap] = useState<{ [key: number]: boolean }>(
  264. {}
  265. );
  266. //展示切换任务状态对话框
  267. const showSwitchJobStatusModal = (checked: boolean, record: any) => {
  268. setRowStatusMap({ ...rowStatusMap, [record.jobId]: checked });
  269. setStatusChecked(checked);
  270. setStatusRecord(record);
  271. setStatusModalVisible(true);
  272. };
  273. //确认变更任务状态
  274. const executeSwitchStatus = async (
  275. checked: boolean,
  276. jobId: string,
  277. erroCallback: () => void
  278. ) => {
  279. const modifyData = {
  280. jobId: jobId,
  281. status: checked ? "0" : "1",
  282. };
  283. const body = await fetchApi(changeJobStatusAPI, push, {
  284. method: "PUT",
  285. headers: {
  286. "Content-Type": "application/json",
  287. },
  288. body: JSON.stringify(modifyData),
  289. });
  290. if (body !== undefined) {
  291. if (body.code == 200) {
  292. App.useApp().message.success(body.msg);
  293. } else {
  294. App.useApp().message.error(body.msg);
  295. erroCallback();
  296. }
  297. }
  298. };
  299. //确定切换状态
  300. const confirmSwitchStatus = () => {
  301. if (!statusRecord) return;
  302. executeSwitchStatus(statusChecked, statusRecord.jobId, () => {
  303. setRowStatusMap({ ...rowStatusMap, [statusRecord.jobId]: !statusChecked });
  304. });
  305. setStatusModalVisible(false);
  306. setStatusRecord(null);
  307. };
  308. //取消切换状态
  309. const cancelSwitchStatus = () => {
  310. if (!statusRecord) return;
  311. setRowStatusMap({ ...rowStatusMap, [statusRecord.jobId]: !statusChecked });
  312. setStatusModalVisible(false);
  313. setStatusRecord(null);
  314. };
  315. //是否展示修改对话框
  316. const [isShowModifyDataModal, setIsShowModifyDataModal] = useState(false);
  317. //展示修改对话框
  318. const onClickShowRowModifyModal = (record?: any) => {
  319. queryRowData(record);
  320. setIsShowModifyDataModal(true);
  321. };
  322. //修改数据表单引用
  323. const modifyFormRef = useRef<ProFormInstance>(null);
  324. //操作当前数据的附加数据
  325. const [operatRowData, setOperateRowData] = useState<{
  326. [key: string]: any;
  327. }>({});
  328. //查询并加载待修改数据的详细信息
  329. const queryRowData = async (record?: any) => {
  330. const jobId = record !== undefined ? record.jobId : selectedRow.jobId;
  331. operatRowData["jobId"] = jobId;
  332. setOperateRowData(operatRowData);
  333. if (jobId !== undefined) {
  334. const body = await fetchApi(`${queryDetailAPI}/${jobId}`, push);
  335. if (body !== undefined) {
  336. if (body.code == 200) {
  337. modifyFormRef?.current?.setFieldsValue({
  338. //需要加载到修改表单中的数据
  339. jobName: body.data.jobName,
  340. jobGroup: body.data.jobGroup,
  341. invokeTarget: body.data.invokeTarget,
  342. cronExpression: body.data.cronExpression,
  343. status: body.data.status,
  344. misfirePolicy: body.data.misfirePolicy,
  345. concurrent: body.data.concurrent,
  346. });
  347. setCronValue(body.data.cronExpression);
  348. }
  349. }
  350. }
  351. };
  352. //确认修改数据
  353. const executeModifyData = async (values: any) => {
  354. values["jobId"] = operatRowData["jobId"];
  355. const body = await fetchApi(modifyAPI, push, {
  356. method: "PUT",
  357. headers: {
  358. "Content-Type": "application/json",
  359. },
  360. body: JSON.stringify(values),
  361. });
  362. if (body !== undefined) {
  363. if (body.code == 200) {
  364. App.useApp().message.success(body.msg);
  365. //刷新列表
  366. if (actionTableRef.current) {
  367. actionTableRef.current.reload();
  368. }
  369. setIsShowModifyDataModal(false);
  370. return true;
  371. }
  372. App.useApp().message.error(body.msg);
  373. return false;
  374. }
  375. };
  376. //3.删除
  377. //点击删除按钮,展示删除确认框
  378. const onClickDeleteRow = (record?: any) => {
  379. const jobId = record !== undefined ? record.jobId : selectedRowKeys.join(",");
  380. setDeleteJobId(jobId);
  381. setDeleteModalVisible(true);
  382. };
  383. //确定删除选中的数据
  384. const executeDeleteRow = async () => {
  385. if (deleteJobId === null) return;
  386. const body = await fetchApi(`${deleteAPI}/${deleteJobId}`, push, {
  387. method: "DELETE",
  388. });
  389. if (body !== undefined) {
  390. if (body.code == 200) {
  391. App.useApp().message.success("删除成功");
  392. //修改按钮变回不可点击
  393. setRowCanModify(false);
  394. //删除按钮变回不可点击
  395. setRowCanDelete(false);
  396. //选中行数据重置为空
  397. setSelectedRowKeys([]);
  398. //刷新列表
  399. if (actionTableRef.current) {
  400. actionTableRef.current.reload();
  401. }
  402. } else {
  403. App.useApp().message.error(body.msg);
  404. }
  405. }
  406. setDeleteModalVisible(false);
  407. setDeleteJobId(null);
  408. };
  409. //4.导出
  410. //导出表格数据
  411. const exportTable = async () => {
  412. if (searchTableFormRef.current) {
  413. const formData = new FormData();
  414. const data = {
  415. pageNum: page,
  416. pageSize: pageSize,
  417. ...searchTableFormRef.current.getFieldsValue(),
  418. };
  419. Object.keys(data).forEach((key) => {
  420. if (data[key] !== undefined) {
  421. formData.append(key, data[key]);
  422. }
  423. });
  424. await fetchFile(
  425. exportAPI,
  426. push,
  427. {
  428. method: "POST",
  429. body: formData,
  430. },
  431. `${exportFilePrefix}_${new Date().getTime()}.xlsx`
  432. );
  433. }
  434. };
  435. //5.选择行
  436. //选中行操作
  437. const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
  438. const [selectedRow, setSelectedRow] = useState(undefined as any);
  439. //修改按钮是否可用,选中行时才可用
  440. const [rowCanModify, setRowCanModify] = useState(false);
  441. //删除按钮是否可用,选中行时才可用
  442. const [rowCanDelete, setRowCanDelete] = useState(false);
  443. //ProTable rowSelection
  444. const rowSelection = {
  445. onChange: (newSelectedRowKeys: React.Key[], selectedRows: any[]) => {
  446. setSelectedRowKeys(newSelectedRowKeys);
  447. setRowCanDelete(newSelectedRowKeys && newSelectedRowKeys.length > 0);
  448. if (newSelectedRowKeys && newSelectedRowKeys.length == 1) {
  449. setSelectedRow(selectedRows[0]);
  450. setRowCanModify(true);
  451. } else {
  452. setRowCanModify(false);
  453. setSelectedRow(undefined);
  454. }
  455. },
  456. //复选框的额外禁用判断
  457. // getCheckboxProps: (record) => ({
  458. // disabled: record.userId == 1,
  459. // }),
  460. };
  461. //搜索栏显示状态
  462. const [showSearch, setShowSearch] = useState(true);
  463. //action对象引用
  464. const actionTableRef = useRef<ActionType>(null);
  465. //搜索表单对象引用
  466. const searchTableFormRef = useRef<ProFormInstance>(null!);
  467. //当前页数和每页条数
  468. const [page, setPage] = useState(1);
  469. const defaultPageSize = 10;
  470. const [pageSize, setPageSize] = useState(defaultPageSize);
  471. const pageChange = (page: number, pageSize: number) => {
  472. setPage(page);
  473. setPageSize(pageSize);
  474. };
  475. //是否展示Cron表达式生成框
  476. const [isCronShow, setIsCronShow] = useState(false);
  477. //Cron表达式值
  478. const [cronValue, setCronValue] = useState("");
  479. //当前是新建还是修改触发的Cron生成框
  480. const [isNew, setIsNew] = useState(true);
  481. //用于重置Cron生成框的key值
  482. const [modalKey, setModalKey] = useState(0);
  483. //展示Cron对话框,区分新建用还是修改用
  484. const showCronModal = (isNew: boolean) => {
  485. setIsNew(isNew);
  486. setIsCronShow(true);
  487. };
  488. //回写Cron数据
  489. const getCronData = () => {
  490. setIsCronShow(false);
  491. if (isNew) {
  492. addFormRef?.current?.setFieldsValue({
  493. cronExpression: cronValue,
  494. });
  495. } else {
  496. modifyFormRef?.current?.setFieldsValue({
  497. cronExpression: cronValue,
  498. });
  499. }
  500. //重置Cron数据
  501. setCronValue("");
  502. setModalKey((preKey) => preKey + 1);
  503. };
  504. //执行任务一次
  505. const showRunOnceModal = (record: any) => {
  506. setRunRecord(record);
  507. setRunModalVisible(true);
  508. };
  509. //执行任务
  510. const executeJob = async (record: any) => {
  511. const runData = {
  512. jobId: record.jobId,
  513. jobGroup: record.jobGroup,
  514. };
  515. const body = await fetchApi(runAPI, push, {
  516. method: "PUT",
  517. headers: {
  518. "Content-Type": "application/json",
  519. },
  520. body: JSON.stringify(runData),
  521. });
  522. if (body !== undefined) {
  523. if (body.code == 200) {
  524. App.useApp().message.success("执行成功");
  525. } else {
  526. App.useApp().message.error(body.msg);
  527. }
  528. }
  529. setRunModalVisible(false);
  530. setRunRecord(null);
  531. };
  532. //取消执行任务
  533. const cancelRunJob = () => {
  534. setRunModalVisible(false);
  535. setRunRecord(null);
  536. };
  537. //是否展示任务详情框
  538. const [isShowDetail, setIsShowDetail] = useState(false);
  539. //展示行详情框
  540. const showRowModal = async (record: any) => {
  541. const jobId = record.jobId;
  542. if (jobId !== undefined) {
  543. const body = await fetchApi(`${queryDetailAPI}/${jobId}`, push);
  544. setSelectedRow(body.data);
  545. }
  546. setIsShowDetail(true);
  547. };
  548. return (
  549. <PageContainer title={false}>
  550. <ProTable
  551. formRef={searchTableFormRef}
  552. rowKey="jobId"
  553. rowSelection={{
  554. selectedRowKeys,
  555. ...rowSelection,
  556. }}
  557. columns={columns}
  558. request={async (params: any, sorter: any, filter: any) => {
  559. // 表单搜索项会从 params 传入,传递给后端接口。
  560. const data = await queryTableData(params, sorter, filter);
  561. if (data !== undefined) {
  562. return Promise.resolve({
  563. data: data.rows,
  564. success: true,
  565. total: data.total,
  566. });
  567. }
  568. return Promise.resolve({
  569. data: [],
  570. success: true,
  571. });
  572. }}
  573. pagination={{
  574. defaultPageSize: defaultPageSize,
  575. showQuickJumper: true,
  576. showSizeChanger: true,
  577. onChange: pageChange,
  578. }}
  579. search={
  580. showSearch
  581. ? {
  582. defaultCollapsed: false,
  583. searchText: "搜索",
  584. }
  585. : false
  586. }
  587. dateFormatter="string"
  588. actionRef={actionTableRef}
  589. toolbar={{
  590. actions: [
  591. <ModalForm
  592. formRef={addFormRef}
  593. key="addmodal"
  594. layout="horizontal"
  595. title="添加任务"
  596. trigger={
  597. <Button icon={<PlusOutlined />} type="primary">
  598. 新建
  599. </Button>
  600. }
  601. autoFocusFirstInput
  602. modalProps={{
  603. destroyOnHidden: true,
  604. }}
  605. submitTimeout={2000}
  606. onFinish={executeAddData}
  607. >
  608. <ProForm.Group>
  609. <ProFormText
  610. width="md"
  611. name="jobName"
  612. label="任务名称"
  613. placeholder="请输入任务名称"
  614. rules={[{ required: true, message: "请输入任务名称" }]}
  615. />
  616. <ProFormSelect
  617. width="md"
  618. name="jobGroup"
  619. label="任务分组"
  620. valueEnum={{
  621. DEFAULT: {
  622. text: "默认",
  623. status: "DEFAULT",
  624. },
  625. SYSTEM: {
  626. text: "系统",
  627. status: "SYSTEM",
  628. },
  629. }}
  630. />
  631. </ProForm.Group>
  632. <ProForm.Group>
  633. <ProFormText
  634. width="lg"
  635. name="invokeTarget"
  636. label="调用方法"
  637. placeholder="请输入调用方法的字符串"
  638. tooltip="Bean调用示例:ryTask.ryParams('ry')
  639. Class调用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry')
  640. 参数说明:支持字符串,布尔类型,长整型,浮点型,整型"
  641. rules={[
  642. { required: true, message: "请输入调用方法的字符串" },
  643. ]}
  644. />
  645. </ProForm.Group>
  646. <ProForm.Group>
  647. <Space.Compact>
  648. <ProFormText
  649. width="lg"
  650. name="cronExpression"
  651. label="Cron表达式"
  652. placeholder="请输入Cron表达式"
  653. rules={[{ required: true, message: "请输入Cron表达式" }]}
  654. />
  655. <Button
  656. icon={<ClockCircleOutlined />}
  657. onClick={() => showCronModal(true)}
  658. >
  659. 生成表达式
  660. </Button>
  661. </Space.Compact>
  662. </ProForm.Group>
  663. <ProForm.Group>
  664. <ProFormRadio.Group
  665. width="md"
  666. name="misfirePolicy"
  667. label="执行策略"
  668. initialValue="1"
  669. options={[
  670. {
  671. label: "立即执行",
  672. value: "1",
  673. },
  674. {
  675. label: "执行一次",
  676. value: "2",
  677. },
  678. {
  679. label: "放弃执行",
  680. value: "3",
  681. },
  682. ]}
  683. />
  684. <ProFormRadio.Group
  685. name="concurrent"
  686. width="md"
  687. label="是否并发"
  688. initialValue="0"
  689. options={[
  690. {
  691. label: "允许",
  692. value: "0",
  693. },
  694. {
  695. label: "禁止",
  696. value: "1",
  697. },
  698. ]}
  699. />
  700. </ProForm.Group>
  701. </ModalForm>,
  702. <ModalForm
  703. key="modifymodal"
  704. title="修改岗位"
  705. layout="horizontal"
  706. formRef={modifyFormRef}
  707. trigger={
  708. <Button
  709. icon={<FontAwesomeIcon icon={faPenToSquare} />}
  710. disabled={!rowCanModify}
  711. onClick={() => onClickShowRowModifyModal()}
  712. >
  713. 修改
  714. </Button>
  715. }
  716. open={isShowModifyDataModal}
  717. autoFocusFirstInput
  718. modalProps={{
  719. destroyOnHidden: true,
  720. onCancel: () => {
  721. setIsShowModifyDataModal(false);
  722. },
  723. }}
  724. submitTimeout={2000}
  725. onFinish={executeModifyData}
  726. >
  727. <ProForm.Group>
  728. <ProFormText
  729. width="md"
  730. name="jobName"
  731. label="任务名称"
  732. placeholder="请输入任务名称"
  733. rules={[{ required: true, message: "请输入任务名称" }]}
  734. />
  735. <ProFormSelect
  736. width="md"
  737. name="jobGroup"
  738. label="任务分组"
  739. valueEnum={{
  740. DEFAULT: {
  741. text: "默认",
  742. status: "DEFAULT",
  743. },
  744. SYSTEM: {
  745. text: "系统",
  746. status: "SYSTEM",
  747. },
  748. }}
  749. />
  750. </ProForm.Group>
  751. <ProForm.Group>
  752. <ProFormText
  753. width="lg"
  754. name="invokeTarget"
  755. label="调用方法"
  756. placeholder="请输入调用方法的字符串"
  757. tooltip="Bean调用示例:ryTask.ryParams('ry')
  758. Class调用示例:com.ruoyi.quartz.task.RyTask.ryParams('ry')
  759. 参数说明:支持字符串,布尔类型,长整型,浮点型,整型"
  760. rules={[
  761. { required: true, message: "请输入调用方法的字符串" },
  762. ]}
  763. />
  764. </ProForm.Group>
  765. <ProForm.Group>
  766. <Space.Compact>
  767. <ProFormText
  768. width="lg"
  769. name="cronExpression"
  770. label="Cron表达式"
  771. placeholder="请输入Cron表达式"
  772. rules={[{ required: true, message: "请输入Cron表达式" }]}
  773. />
  774. <Button
  775. icon={<ClockCircleOutlined />}
  776. onClick={() => showCronModal(false)}
  777. >
  778. 生成表达式
  779. </Button>
  780. </Space.Compact>
  781. </ProForm.Group>
  782. <ProForm.Group>
  783. <ProFormRadio.Group
  784. name="status"
  785. width="md"
  786. label="状态"
  787. initialValue="0"
  788. options={[
  789. {
  790. label: "正常",
  791. value: "0",
  792. },
  793. {
  794. label: "暂停",
  795. value: "1",
  796. },
  797. ]}
  798. />
  799. </ProForm.Group>
  800. <ProForm.Group>
  801. <ProFormRadio.Group
  802. width="md"
  803. name="misfirePolicy"
  804. label="执行策略"
  805. initialValue="1"
  806. options={[
  807. {
  808. label: "立即执行",
  809. value: "1",
  810. },
  811. {
  812. label: "执行一次",
  813. value: "2",
  814. },
  815. {
  816. label: "放弃执行",
  817. value: "3",
  818. },
  819. ]}
  820. />
  821. <ProFormRadio.Group
  822. name="concurrent"
  823. width="md"
  824. label="是否并发"
  825. initialValue="0"
  826. options={[
  827. {
  828. label: "允许",
  829. value: "0",
  830. },
  831. {
  832. label: "禁止",
  833. value: "1",
  834. },
  835. ]}
  836. />
  837. </ProForm.Group>
  838. </ModalForm>,
  839. <Button
  840. key="danger"
  841. danger
  842. icon={<DeleteOutlined />}
  843. disabled={!rowCanDelete}
  844. onClick={() => onClickDeleteRow()}
  845. >
  846. 删除
  847. </Button>,
  848. <Button
  849. key="export"
  850. type="primary"
  851. icon={<FontAwesomeIcon icon={faDownload} />}
  852. onClick={exportTable}
  853. >
  854. 导出
  855. </Button>,
  856. ],
  857. settings: [
  858. {
  859. key: "switch",
  860. icon: showSearch ? (
  861. <FontAwesomeIcon icon={faToggleOn} />
  862. ) : (
  863. <FontAwesomeIcon icon={faToggleOff} />
  864. ),
  865. tooltip: showSearch ? "隐藏搜索栏" : "显示搜索栏",
  866. onClick: (key: string | undefined) => {
  867. setShowSearch(!showSearch);
  868. },
  869. },
  870. {
  871. key: "refresh",
  872. tooltip: "刷新",
  873. icon: <ReloadOutlined />,
  874. onClick: (key: string | undefined) => {
  875. if (actionTableRef.current) {
  876. actionTableRef.current.reload();
  877. }
  878. },
  879. },
  880. ],
  881. }}
  882. />
  883. <Modal
  884. title="Cron表达式生成器"
  885. key={modalKey}
  886. zIndex={10000}
  887. open={isCronShow}
  888. onOk={getCronData}
  889. onCancel={() => setIsCronShow(false)}
  890. // confirmLoading={confirmLoading}
  891. >
  892. <Input prefix={<ClockCircleOutlined />} value={cronValue} />
  893. <div style={{ maxHeight: 450, overflow: "auto" }}>
  894. <ReQuartzCron
  895. cssClassPrefix="cron-"
  896. localization={MortnonCronLocalization}
  897. value={cronValue}
  898. onChange={setCronValue}
  899. renderYearsFrom={new Date().getFullYear()}
  900. />
  901. </div>
  902. </Modal>
  903. {selectedRow !== undefined && (
  904. <Modal
  905. title="任务详情"
  906. footer={<Button onClick={() => setIsShowDetail(false)}>关闭</Button>}
  907. open={isShowDetail}
  908. onCancel={() => setIsShowDetail(false)}
  909. >
  910. <ProDescriptions column={2}>
  911. <ProDescriptions.Item label="任务编号">
  912. {selectedRow.jobId}
  913. </ProDescriptions.Item>
  914. <ProDescriptions.Item
  915. label="任务分组"
  916. valueEnum={{
  917. DEFAULT: {
  918. text: "默认",
  919. status: "DEFAULT",
  920. },
  921. SYSTEM: {
  922. text: "系统",
  923. status: "SYSTEM",
  924. },
  925. }}
  926. >
  927. {selectedRow.jobGroup}
  928. </ProDescriptions.Item>
  929. <ProDescriptions.Item label="任务名称">
  930. {selectedRow.jobName}
  931. </ProDescriptions.Item>
  932. <ProDescriptions.Item label="创建时间">
  933. {selectedRow.createTime}
  934. </ProDescriptions.Item>
  935. <ProDescriptions.Item label="Cron表达式">
  936. {selectedRow.cronExpression}
  937. </ProDescriptions.Item>
  938. <ProDescriptions.Item label="下次执行时间">
  939. {selectedRow.nextValidTime}
  940. </ProDescriptions.Item>
  941. </ProDescriptions>
  942. <ProDescriptions column={1}>
  943. <ProDescriptions.Item label="调用目标方法">
  944. {selectedRow.invokeTarget}
  945. </ProDescriptions.Item>
  946. </ProDescriptions>
  947. <ProDescriptions column={2}>
  948. <ProDescriptions.Item
  949. label="任务状态"
  950. valueEnum={{
  951. 0: {
  952. text: "正常",
  953. status: "0",
  954. },
  955. 1: {
  956. text: "暂停",
  957. status: "1",
  958. },
  959. }}
  960. >
  961. {selectedRow.status}
  962. </ProDescriptions.Item>
  963. <ProDescriptions.Item
  964. label="是否并发"
  965. valueEnum={{
  966. 0: {
  967. text: "允许",
  968. status: "0",
  969. },
  970. 1: {
  971. text: "禁止",
  972. status: "1",
  973. },
  974. }}
  975. >
  976. {selectedRow.concurrent}
  977. </ProDescriptions.Item>
  978. <ProDescriptions.Item
  979. label="执行策略"
  980. valueEnum={{
  981. 1: {
  982. text: "立即执行",
  983. status: "1",
  984. },
  985. 2: {
  986. text: "执行一次",
  987. status: "2",
  988. },
  989. 3: {
  990. text: "放弃执行",
  991. status: "3",
  992. },
  993. }}
  994. >
  995. {selectedRow.misfirePolicy}
  996. </ProDescriptions.Item>
  997. </ProDescriptions>
  998. </Modal>
  999. )}
  1000. {/* 删除确认模态框 */}
  1001. <Modal
  1002. title={
  1003. <div style={{ display: 'flex', alignItems: 'center' }}>
  1004. <ExclamationCircleFilled style={{ color: '#faad14', marginRight: 8 }} />
  1005. <span>系统提示</span>
  1006. </div>
  1007. }
  1008. open={deleteModalVisible}
  1009. onOk={executeDeleteRow}
  1010. onCancel={() => {
  1011. setDeleteModalVisible(false);
  1012. setDeleteJobId(null);
  1013. }}
  1014. okText="确认"
  1015. cancelText="取消"
  1016. >
  1017. <p>{`确定删除任务编号为“${deleteJobId}”的数据项?`}</p>
  1018. </Modal>
  1019. {/* 切换状态确认模态框 */}
  1020. <Modal
  1021. title={
  1022. <div style={{ display: 'flex', alignItems: 'center' }}>
  1023. <ExclamationCircleFilled style={{ color: '#faad14', marginRight: 8 }} />
  1024. <span>系统提示</span>
  1025. </div>
  1026. }
  1027. open={statusModalVisible}
  1028. onOk={confirmSwitchStatus}
  1029. onCancel={cancelSwitchStatus}
  1030. okText="确认"
  1031. cancelText="取消"
  1032. >
  1033. <p>{`确认要${statusChecked ? "启用" : "停用"}"${statusRecord?.jobName}"任务吗?`}</p>
  1034. </Modal>
  1035. {/* 执行任务确认模态框 */}
  1036. <Modal
  1037. title={
  1038. <div style={{ display: 'flex', alignItems: 'center' }}>
  1039. <ExclamationCircleFilled style={{ color: '#faad14', marginRight: 8 }} />
  1040. <span>系统提示</span>
  1041. </div>
  1042. }
  1043. open={runModalVisible}
  1044. onOk={() => executeJob(runRecord)}
  1045. onCancel={cancelRunJob}
  1046. okText="确认"
  1047. cancelText="取消"
  1048. >
  1049. <p>{`确定要立即执行一次任务“${runRecord?.jobName}”吗?`}</p>
  1050. </Modal>
  1051. </PageContainer>
  1052. );
  1053. }