运行结果:
环境以及依赖
没用ts
antd使用了两个版本进行测试 5.4.0, 4.21.0
校验cron合法性工具: cron-parser, 直接安装就行, 我使用的版本是4.9.0,注意这个校验工具不支持校验带有年的cron表达式
cron生成器: qnn-react-cron, 版本:0.8.6 这个版本是没问题的, 我尝试最新版本是不好使的, 这个可能和antd版本有关
代码:
写的不是很好多多包涵
import React, { useState, useRef, useEffect } from 'react';
import { Form, Input, Button, Modal, Dropdown, Space } from "antd";
import cronParser from 'cron-parser';
import Cron from "qnn-react-cron";
import moment from "moment";
export default function CronComponent() {
const [modalConfig, setModalConfig] = useState({ visible: false, cronTextArray: [], disabled: true });
const [update, setUpdate] = useState(false);
const [form] = Form.useForm();
const [cronValue, setCronValue] = useState("");
const onParseFunction = useRef({
getValue: () => { },
onParse: () => { },
});
const triggerValidation = async () => {
try {
// 仅校验 'username' 字段
await form.validateFields(['cronString']);
} catch (errorInfo) {
// eslint-disable-next-line
console.log('校验失败:', errorInfo);
}
};
const handleOk = () => {
const { getValue } = onParseFunction.current || {};
let cronString = "";
try {
// 因为用来检测正则格式的cronPaeser不能验证带有 年 的cron表达式, 所以让我剪掉了最后一个字符串
cronString = getValue().slice(0, -1);
}
catch {
cronString = "";
}
form.setFieldValue("cronString", cronString);
triggerValidation();
};
const onOpenChange = () => {
const { onParse } = onParseFunction.current || {};
const currentCronString = form.getFieldValue("cronString");
try {
onParse(currentCronString);
setCronValue(currentCronString);
setModalConfig((modalConfig) => {
return { ...modalConfig, disabled: false };
});
}
catch {
setCronValue("* * * * * ?");
}
};
// 使用 cron-parser 进行 cron 表达式的校验,以及生成五次运行结果
const validateCronExpression = (rule, value) => {
// 如果值为空,则不进行校验(因为 required 规则会处理)
if (!value) {
return Promise.reject(new Error("cron表达式为必填项"));
}
try {
// 尝试解析表达式
const cronEntriesObj = cronParser.parseExpression(value);
const cronTextArray = [
moment(cronEntriesObj.next().toString()).format("YYYY-MM-DD HH:mm:ss"),
moment(cronEntriesObj.next().toString()).format("YYYY-MM-DD HH:mm:ss"),
moment(cronEntriesObj.next().toString()).format("YYYY-MM-DD HH:mm:ss"),
moment(cronEntriesObj.next().toString()).format("YYYY-MM-DD HH:mm:ss"),
moment(cronEntriesObj.next().toString()).format("YYYY-MM-DD HH:mm:ss")
];
setModalConfig((modalConfig) => {
return { ...modalConfig, cronTextArray, disabled: false };
});
// 如果没有抛出错误,表达式是有效的
return Promise.resolve();
} catch (e) {
// 如果解析时抛出错误,表达式是无效的
setModalConfig((modalConfig) => {
return { ...modalConfig, disabled: true };
});
return Promise.reject(new Error("无效的 Cron 表达式"));
}
};
const validateEvent = () => {
validateCronExpression("_", form.getFieldValue("cronString")).then(() => {
setModalConfig((modalConfig) => { return { ...modalConfig, visible: true, disabled: false }; });
}).catch(() => {
setModalConfig((modalConfig) => { return { ...modalConfig, visible: true, disabled: true }; });
});
};
useEffect(() => {
onOpenChange();
}, []);
return (
<>
<Form form={form}>
<Form.Item label="Cron表达式">
<div style={{ display: "flex", gap: "12px" }}>
<Dropdown
menu={{}}
onOpenChange={onOpenChange}
dropdownRender={menu => (
<div style={{ width: 800 }}>
<Cron
value={cronValue}
key={update}
onOk={(e) => {
console.log('e: ', e);
setCronValue(e);
form.setFieldValue("cronString", e);
}}
panesShow={{
second: true,
minute: true,
hour: true,
day: true,
month: true,
week: true,
year: false,
}}
getCronFns={(e) => {
onParseFunction.current = e;
}}
footer={
<Space>
<Button
type="default"
onClick={() => {
setUpdate(!update);
setCronValue("* * * * * ?");
form.setFieldValue("cronString", "* * * * * ?");
triggerValidation();
}}
>
重置
</Button>
<Button type="primary" htmlType="submit" onClick={handleOk}>
生成
</Button>
</Space>
}
/>
</div>
)}
>
<Form.Item
name="cronString"
rules={[{ required: true, validator: validateCronExpression }]}
style={{ width: "800px" }}
>
<Input placeholder="请输入cron表达式" onChange={onOpenChange} />
</Form.Item>
</Dropdown>
<Button type="primary" disabled={modalConfig.disabled} onClick={validateEvent}>运行结果</Button>
</div>
</Form.Item>
</Form>
<Modal
title="最近5次运行时间"
visible={modalConfig.visible}
onOk={() => setModalConfig((modalConfig) => { return { ...modalConfig, visible: false }; })}
onCancel={() => setModalConfig((modalConfig) => { return { ...modalConfig, visible: false }; })}
cancelText="关闭"
>
<div style={{ minHeight: 80 }}>
<ul style={{ fontSize: "16px" }}>
{Array.isArray(modalConfig.cronTextArray) ? modalConfig.cronTextArray.map((item, index) => {
return <li>第{index + 1}次运行: {item}</li>;
}) : null}
</ul>
</div>
</Modal>
</>
);
}