feat(flow-editor): 更新周期编辑器以支持自定义Cron表达式

master
钟良源 6 days ago
parent 0360dd605f
commit 5bfc73751f

@ -44,8 +44,8 @@ const dayMap = {
}; };
const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, className, onUpdateData }) => { const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, className, onUpdateData }) => {
const [mode, setMode] = useState<'visual' | 'expression'>('visual'); const [mode, setMode] = useState<'visual' | 'expression'>('expression');
const [cron, setCron] = useState<string>(value || '0 0 0 * * ?'); const [cron, setCron] = useState<string>(value);
const [error, setError] = useState<string>(''); const [error, setError] = useState<string>('');
// 图形化配置的状态 // 图形化配置的状态
@ -62,7 +62,12 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
// 初始化 // 初始化
useEffect(() => { useEffect(() => {
if (value !== undefined && value !== cron) { console.log('CronPicker value changed:', value);
console.log('Current cron state:', cron);
// 当 value 有值且与当前 cron 不同时,更新 cron 状态
if (value && value !== cron) {
console.log('Updating cron to:', value);
setCron(value); setCron(value);
// 当外部value变化时解析并更新图形化配置的状态 // 当外部value变化时解析并更新图形化配置的状态
parseCronExpression(value); parseCronExpression(value);
@ -125,14 +130,31 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
if (mode === 'visual') { if (mode === 'visual') {
return generateCron(); return generateCron();
} }
return cron; // 如果 cron 为空,返回空字符串而不是默认值
return cron || '';
}, [mode, minuteType, minutesInterval, hourType, hourlyAt, dailyType, specificDay, selectedWeekdays, selectedMonths, monthlyType, specificMonths, cron]); }, [mode, minuteType, minutesInterval, hourType, hourlyAt, dailyType, specificDay, selectedWeekdays, selectedMonths, monthlyType, specificMonths, cron]);
// 使用 ref 来追踪是否是初始化阶段
const isInitializedRef = React.useRef(false);
useEffect(() => { useEffect(() => {
console.log('currentCron changed:', currentCron);
console.log('isInitializedRef.current:', isInitializedRef.current);
// 跳过初始化时的调用
if (!isInitializedRef.current) {
isInitializedRef.current = true;
return;
}
// 只有当 currentCron 有值时才更新
if (currentCron) {
console.log('Calling onUpdateData with:', currentCron);
onUpdateData({ onUpdateData({
customDef: { intervalSeconds: currentCron }, customDef: { intervalSeconds: currentCron },
type: 'CYCLE' type: 'CYCLE'
}); });
}
}, [currentCron]); }, [currentCron]);
// 解析Cron表达式并更新图形化配置的状态 // 解析Cron表达式并更新图形化配置的状态
@ -309,10 +331,26 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
return ( return (
<div style={style} className={`${styles.container} ${className || ''}`}> <div style={style} className={`${styles.container} ${className || ''}`}>
<Tabs onChange={handleTabChange} type="rounded" size="small" className={styles.tabs}> <Tabs onChange={handleTabChange} type="rounded" size="small" className={styles.tabs}>
<TabPane key="visual" title="图形化配置" />
<TabPane key="expression" title="Cron 表达式" /> <TabPane key="expression" title="Cron 表达式" />
<TabPane key="visual" title="图形化配置" />
</Tabs> </Tabs>
{mode === 'expression' && (
<div>
<label style={{ display: 'block', marginBottom: 6, fontSize: 14, color: '#333' }}>
Cron
</label>
<input
type="text"
value={currentCron}
onChange={handleExpressionChange}
placeholder="例如0 0 12 * * ?"
className={`${styles.expressionInput} ${error ? styles.error : ''}`}
/>
{error && <div className={styles.errorMessage}>{error}</div>}
</div>
)}
{mode === 'visual' && ( {mode === 'visual' && (
<div> <div>
<Form.Item label="分钟" className={styles.formItem}> <Form.Item label="分钟" className={styles.formItem}>
@ -446,22 +484,6 @@ const CronPicker: React.FC<CronPickerProps> = ({ value, onChange, style, classNa
</div> </div>
)} )}
{mode === 'expression' && (
<div>
<label style={{ display: 'block', marginBottom: 6, fontSize: 14, color: '#333' }}>
Cron
</label>
<input
type="text"
value={currentCron}
onChange={handleExpressionChange}
placeholder="0 0 12 * * ?"
className={`${styles.expressionInput} ${error ? styles.error : ''}`}
/>
{error && <div className={styles.errorMessage}>{error}</div>}
</div>
)}
<div className={styles.previewContainer}> <div className={styles.previewContainer}>
<Text type="secondary" style={{ fontSize: 13, display: 'block', marginBottom: 4 }}> <Text type="secondary" style={{ fontSize: 13, display: 'block', marginBottom: 4 }}>
{getHumanReadable()} {getHumanReadable()}

@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { NodeEditorProps } from '@/components/FlowEditor/nodeEditors'; import { NodeEditorProps } from '@/components/FlowEditor/nodeEditors';
import { Typography } from '@arco-design/web-react'; import { Typography } from '@arco-design/web-react';
import { IconUnorderedList } from '@arco-design/web-react/icon'; import { IconUnorderedList } from '@arco-design/web-react/icon';
@ -6,16 +6,22 @@ import ParamsTable from './ParamsTable';
import CronPicker from '@/components/CronPicker'; import CronPicker from '@/components/CronPicker';
const CycleEditor: React.FC<NodeEditorProps> = ({ nodeData, updateNodeData }) => { const CycleEditor: React.FC<NodeEditorProps> = ({ nodeData, updateNodeData }) => {
const [cron, setCron] = useState('0 0 9 * * ?'); // 从 nodeData 中获取初始值,如果没有则使用默认值
let initialCron = '';
try {
initialCron = JSON.parse(nodeData.component.customDef).intervalSeconds;
} catch (e) {
}
return ( return (
<> <>
<Typography.Title heading={5}><IconUnorderedList style={{ marginRight: 5 }} /></Typography.Title> <Typography.Title heading={5}><IconUnorderedList style={{ marginRight: 5 }} /></Typography.Title>
<CronPicker <CronPicker
value={cron} value={initialCron}
onChange={setCron}
onUpdateData={(data) => { onUpdateData={(data) => {
updateNodeData('component', { updateNodeData('component', {
...nodeData.component,
...data ...data
}); });
}} /> }} />

Loading…
Cancel
Save