最近在写的一个分布式调度系统,后端同学需要让我传入cron表达式,给调度接口传参。我去了学习了解了cron表达式的用法,发现有3个通用的表达式刚好符合我们的需求:
0 11 20 * * ?
上面是每天20:11的cron表达式
0 14 20 * * WED,THU
上面是 每周星期三,星期四20:14的cron表达式
0 15 20 3,7 * ?
上面是 每月的3,7号20:15的cron表达式
这三个表达式刚好符合我们的需求,并且每个符号表达的意思也很直观。那么,话不多说,直接开写!
react
moment
semi-design(组件库)
typescript
utils下创建cron.ts,对组件所用到的数据进行统一的管理:
export const dayOfTheWeekData = [
{ key: 'MON', label: '星期一' },
{ key: 'TUE', label: '星期二' },
{ key: 'WED', label: '星期三' },
{ key: 'THU', label: '星期四' },
{ key: 'FRI', label: '星期五' },
{ key: 'SAT', label: '星期六' },
{ key: 'SUN', label: '星期天' }
];
export const dayOfTheWeekOption = [
{ key: '1', label: '星期一' },
{ key: '2', label: '星期二' },
{ key: '3', label: '星期三' },
{ key: '4', label: '星期四' },
{ key: '5', label: '星期五' },
{ key: '6', label: '星期六' },
{ key: '7', label: '星期天' }
];
export const monthOption = [
{ key: '1', label: '一月' },
{ key: '2', label: '二月' },
{ key: '3', label: '三月' },
{ key: '4', label: '四月' },
{ key: '5', label: '五月' },
{ key: '6', label: '六月' },
{ key: '7', label: '七月' },
{ key: '8', label: '八月' },
{ key: '9', label: '九月' },
{ key: '10', label: '十月' },
{ key: '11', label: '十一月' },
{ key: '12', label: '十二月' }
];
//获取dayOfTheMonthOption的每月对象
function getDayOfTheMonthOption() {
const days = [];
for (let i = 1; i < 32; i += 1) {
days.push({ key: i.toString(), label: i.toString().concat('号') });
}
return days;
}
export const dayOfTheMonthOption = getDayOfTheMonthOption();
到了组件的具体实现,个人感觉我写的注释挺全的,就单挑几个核心重点讲下:
//时间选择函数
const handleTimeChange = (time: moment.Moment | null) => {
setSelectTime(time);
if (!time) return;
const currentCron = expression ? expression.split(' ') : [];
const [seconds, , , dayOfMonth, month1, dayOfWeek] = currentCron;
const minutes = moment(time).minutes().toString(); //获取分钟
const hours = moment(time).hours().toString(); //获取小时
let result = null;
if (!Number.isNaN(Number(hours)) && !Number.isNaN(Number(minutes))) {
const minutesAndHour = seconds
.concat(space)
.concat(minutes)
.concat(space)
.concat(hours)
.concat(space);
if (defaultTimeType === 'everyDay') result = minutesAndHour.concat('* * ?');
if (defaultTimeType !== 'everyDay')
result = minutesAndHour
.concat(dayOfMonth)
.concat(space)
.concat(month1)
.concat(space)
.concat(dayOfWeek);
}
if (result) onChange?.(result);
setExpression(result);
};
const minutesAndHour = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space);
result = minutesAndHour.concat(‘* * ?’);
result = minutesAndHour.concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(dayOfWeek);
setSelectedValue(data);
const selectValues = data.join(',');
const currentCron = expression ? expression.split(' ') : [];
const [seconds, minutes, hours, dayOfMonth, month1, dayOfWeek] = currentCron;
let result = '';
if (defaultTimeType === 'everyWeek') {
result = seconds
.concat(space)
.concat(minutes)
.concat(space)
.concat(hours)
.concat(space)
.concat(dayOfMonth)
.concat(space)
.concat(month1)
.concat(space)
.concat(selectValues);
}
if (defaultTimeType === 'everyMonth') {
result = seconds
.concat(space)
.concat(minutes)
.concat(space)
.concat(hours)
.concat(space)
.concat(data.length ? selectValues : '*')
.concat(space)
.concat(month1)
.concat(space)
.concat(dayOfWeek);
}
if (selectTime) onChange?.(result);
setExpression(result);
result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(dayOfMonth).concat(space).concat(month1).concat(space).concat(selectValues);
result = seconds.concat(space).concat(minutes).concat(space).concat(hours).concat(space).concat(data.length ? selectValues : '*').concat(space).concat(month1).concat(space).concat(dayOfWeek);
import { ConfigProvider, TimePicker } from '@douyinfe/semi-ui';
import { Fragment, useState } from 'react';
import { Select } from '@douyinfe/semi-ui';
import moment from 'moment';
//引入数据
import { dayOfTheMonthOption, dayOfTheWeekData } from '@/utils/cron';
const { Option } = Select;
const format = 'HH:mm';
const defaultCron = '0 * * * * ?';
const space = ' '; //空格
//类型选择
const timeTypes = [
{ key: 'everyDay', label: '每天' },
{ key: 'everyWeek', label: '每周' },
{ key: 'everyMonth', label: '每月' }
];
interface Props {
onChange?: (cron?: string) => void;
}
const CronInput: React.FC = ({ onChange }) => {
const [defaultTimeType, setDefaultTimeType] = useState(timeTypes[0].key); //选择类型
const [selectedValue, setSelectedValue] = useState<[]>([]); //日期,多选数组
const [selectTime, setSelectTime] = useState(null); //时间
const [expression, setExpression] = useState(defaultCron); //bzd
//类型选择函数
const handleTimeTypeChange = (selectValue: string) => {
setDefaultTimeType(selectValue);
setSelectTime(null);
setSelectedValue([]);
setExpression(defaultCron);
};
//时间选择函数
const handleTimeChange = (time: moment.Moment | null) => {
setSelectTime(time);
if (!time) return;
const currentCron = expression ? expression.split(' ') : [];
const [seconds, , , dayOfMonth, month1, dayOfWeek] = currentCron;
const minutes = moment(time).minutes().toString(); //获取分钟
const hours = moment(time).hours().toString(); //获取小时
let result = null;
if (!Number.isNaN(Number(hours)) && !Number.isNaN(Number(minutes))) {
const minutesAndHour = seconds
.concat(space)
.concat(minutes)
.concat(space)
.concat(hours)
.concat(space);
if (defaultTimeType === 'everyDay') result = minutesAndHour.concat('* * ?');
if (defaultTimeType !== 'everyDay')
result = minutesAndHour
.concat(dayOfMonth)
.concat(space)
.concat(month1)
.concat(space)
.concat(dayOfWeek);
}
if (result) onChange?.(result);
setExpression(result);
};
const handleSelectChange = (data: []) => {
setSelectedValue(data);
const selectValues = data.join(',');
const currentCron = expression ? expression.split(' ') : [];
const [seconds, minutes, hours, dayOfMonth, month1, dayOfWeek] = currentCron;
let result = '';
if (defaultTimeType === 'everyWeek') {
result = seconds
.concat(space)
.concat(minutes)
.concat(space)
.concat(hours)
.concat(space)
.concat(dayOfMonth)
.concat(space)
.concat(month1)
.concat(space)
.concat(selectValues);
}
if (defaultTimeType === 'everyMonth') {
result = seconds
.concat(space)
.concat(minutes)
.concat(space)
.concat(hours)
.concat(space)
.concat(data.length ? selectValues : '*')
.concat(space)
.concat(month1)
.concat(space)
.concat(dayOfWeek);
}
if (selectTime) onChange?.(result);
setExpression(result);
};
const RenderSelect = ({
placeholder,
data = []
}: {
placeholder: string;
data: { key: string; label: string }[];
}) => {
return (
handleTimeChange(val)}
/>
);
};
return (
<>
{defaultTimeType === 'everyDay' && (
handleTimeChange(val)}
/>
)}
{defaultTimeType === 'everyWeek' && (
)}
{defaultTimeType === 'everyMonth' && (
)}
>
);
};
export default CronInput;
使用方法很简单,接收onChange传来的cron表达式即可:
const App: FC = (props) => {
const { datas = [] } = props;
let [value, setValue] = useState();
return (
setValue(cron)} />
{value}
);
};