page.tsx 30 KB

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