|
|
|
|
@ -20,6 +20,7 @@ interface CronPickerProps {
|
|
|
|
|
onChange?: (value: string) => void;
|
|
|
|
|
style?: React.CSSProperties;
|
|
|
|
|
className?: string;
|
|
|
|
|
onUpdateData: (data) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const PRESETS = [
|
|
|
|
|
@ -42,7 +43,7 @@ const dayMap = {
|
|
|
|
|
'SUN': '周日'
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, className }) => {
|
|
|
|
|
const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, className, onUpdateData }) => {
|
|
|
|
|
const [mode, setMode] = useState<'visual' | 'expression'>('visual');
|
|
|
|
|
const [cron, setCron] = useState<string>(value || '0 0 0 * * ?');
|
|
|
|
|
const [error, setError] = useState<string>('');
|
|
|
|
|
@ -56,6 +57,8 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
|
|
|
|
|
const [specificDay, setSpecificDay] = useState<number>(1);
|
|
|
|
|
const [selectedWeekdays, setSelectedWeekdays] = useState<string[]>(['MON']);
|
|
|
|
|
const [selectedMonths, setSelectedMonths] = useState<string[]>(['1']);
|
|
|
|
|
const [monthlyType, setMonthlyType] = useState<'every' | 'specific'>('every');
|
|
|
|
|
const [specificMonths, setSpecificMonths] = useState<string[]>(['1']);
|
|
|
|
|
|
|
|
|
|
// 初始化
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
@ -108,7 +111,11 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
|
|
|
|
|
dow = selectedWeekdays.length > 0 ? selectedWeekdays.join(',') : '*';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const mon = selectedMonths.length < 12 ? selectedMonths.join(',') : '*';
|
|
|
|
|
const mon = monthlyType === 'every'
|
|
|
|
|
? '*'
|
|
|
|
|
: specificMonths.length > 0
|
|
|
|
|
? specificMonths.join(',')
|
|
|
|
|
: '*';
|
|
|
|
|
const newCron = `${sec} ${min} ${hour} ${dom} ${mon} ${dow}`;
|
|
|
|
|
return newCron;
|
|
|
|
|
// validateAndSet(newCron);
|
|
|
|
|
@ -119,7 +126,14 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
|
|
|
|
|
return generateCron();
|
|
|
|
|
}
|
|
|
|
|
return cron;
|
|
|
|
|
}, [mode, minuteType, minutesInterval, hourType, hourlyAt, dailyType, specificDay, selectedWeekdays, selectedMonths, cron]);
|
|
|
|
|
}, [mode, minuteType, minutesInterval, hourType, hourlyAt, dailyType, specificDay, selectedWeekdays, selectedMonths, monthlyType, specificMonths, cron]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
onUpdateData({
|
|
|
|
|
customDef: { intervalSeconds: currentCron },
|
|
|
|
|
type: 'CYCLE'
|
|
|
|
|
});
|
|
|
|
|
}, [currentCron]);
|
|
|
|
|
|
|
|
|
|
// 解析Cron表达式并更新图形化配置的状态
|
|
|
|
|
const parseCronExpression = (cronExpression: string) => {
|
|
|
|
|
@ -189,11 +203,18 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解析月份部分
|
|
|
|
|
if (month !== '*' && month.includes(',')) {
|
|
|
|
|
if (month === '*') {
|
|
|
|
|
setMonthlyType('every');
|
|
|
|
|
}
|
|
|
|
|
else if (month.includes(',')) {
|
|
|
|
|
setMonthlyType('specific');
|
|
|
|
|
setSelectedMonths(month.split(','));
|
|
|
|
|
setSpecificMonths(month.split(','));
|
|
|
|
|
}
|
|
|
|
|
else if (month !== '*') {
|
|
|
|
|
setMonthlyType('specific');
|
|
|
|
|
setSelectedMonths([month]);
|
|
|
|
|
setSpecificMonths([month]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
@ -225,7 +246,6 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
|
|
|
|
|
|
|
|
|
|
const getNextTime = (): string => {
|
|
|
|
|
try {
|
|
|
|
|
console.log('currentCron:', currentCron);
|
|
|
|
|
const interval = CronExpressionParser.parse(currentCron);
|
|
|
|
|
const next = interval.next();
|
|
|
|
|
return next?.toDate ? next.toDate().toLocaleString() : '无法计算';
|
|
|
|
|
@ -379,6 +399,43 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
|
|
|
|
|
</Form.Item>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Form.Item label="月份" className={styles.formItem}>
|
|
|
|
|
<Radio.Group
|
|
|
|
|
value={monthlyType}
|
|
|
|
|
onChange={(value) => setMonthlyType(value as 'every' | 'specific')}
|
|
|
|
|
style={{ display: 'flex' }}
|
|
|
|
|
>
|
|
|
|
|
<Space size="large">
|
|
|
|
|
<Radio value="every">每月</Radio>
|
|
|
|
|
<Radio value="specific">指定月份</Radio>
|
|
|
|
|
</Space>
|
|
|
|
|
</Radio.Group>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
|
|
{monthlyType === 'specific' && (
|
|
|
|
|
<Form.Item label="指定月份" className={styles.formItem}>
|
|
|
|
|
<div className={styles.inlineGroup}>
|
|
|
|
|
{Array.from({ length: 12 }, (_, i) => i + 1).map(month => (
|
|
|
|
|
<Button
|
|
|
|
|
key={month}
|
|
|
|
|
type={specificMonths.includes(String(month)) ? 'primary' : 'secondary'}
|
|
|
|
|
size="small"
|
|
|
|
|
className={styles.weekButton}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setSpecificMonths(prev =>
|
|
|
|
|
prev.includes(String(month))
|
|
|
|
|
? prev.filter(m => m !== String(month))
|
|
|
|
|
: [...prev, String(month)]
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{month}月
|
|
|
|
|
</Button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Space wrap size="small" className={styles.presetsContainer}>
|
|
|
|
|
{PRESETS.map(p => (
|
|
|
|
|
<Button key={p.value} type="text" size="small" onClick={() => updateCron(p.value)}>
|
|
|
|
|
|