page.tsx 32 KB

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