refactor: rewrite SettingsPage as lightweight container with hooks and components
Extracted cron utilities to utils/schedule.js, all state management (15+ useState) to useSettingsForm hook, and 5 config sections + 2 dialogs + 1 status badge into focused components. SettingsPage reduced from ~700 lines to ~130 line container. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
// frontend/src/components/settings/ScheduleSection.jsx
|
||||
import { CalendarClock, Clock, PlayCircle } from 'lucide-react';
|
||||
import {
|
||||
SCHEDULE_TYPE, DEFAULT_CRON, DEFAULT_TIME,
|
||||
getScheduleSummary, buildDailyCron, buildWeeklyCron
|
||||
} from '../../utils/schedule';
|
||||
|
||||
export default function ScheduleSection({ schedule = {}, onUpdate }) {
|
||||
const enabled = schedule.enabled !== false;
|
||||
const type = schedule.type || SCHEDULE_TYPE.DAILY;
|
||||
const time = schedule.time || DEFAULT_TIME;
|
||||
const dayOfWeek = schedule.dayOfWeek || '1';
|
||||
const cron = schedule.cron || DEFAULT_CRON;
|
||||
|
||||
const setEnabled = (val) => onUpdate('schedule', { ...schedule, enabled: val });
|
||||
const setType = (val) => {
|
||||
if (val === SCHEDULE_TYPE.DAILY) {
|
||||
onUpdate('schedule', { ...schedule, type: val, cron: buildDailyCron(time) });
|
||||
} else if (val === SCHEDULE_TYPE.WEEKLY) {
|
||||
onUpdate('schedule', { ...schedule, type: val, dayOfWeek, cron: buildWeeklyCron(dayOfWeek, time) });
|
||||
} else {
|
||||
onUpdate('schedule', { ...schedule, type: val, cron: schedule.cron || DEFAULT_CRON });
|
||||
}
|
||||
};
|
||||
const setTime = (val) => {
|
||||
const newCron = type === SCHEDULE_TYPE.WEEKLY
|
||||
? buildWeeklyCron(dayOfWeek, val) : buildDailyCron(val);
|
||||
onUpdate('schedule', { ...schedule, type, time: val, cron: newCron });
|
||||
};
|
||||
const setDay = (val) => {
|
||||
onUpdate('schedule', { ...schedule, type, dayOfWeek: val, cron: buildWeeklyCron(val, time) });
|
||||
};
|
||||
const setCron = (val) => {
|
||||
onUpdate('schedule', { ...schedule, type: SCHEDULE_TYPE.CRON, cron: val });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-slate-900 border border-slate-800 rounded-xl p-6 shadow-lg">
|
||||
<div className="flex justify-between items-center mb-5 border-b border-slate-800 pb-3">
|
||||
<h3 className="text-base font-semibold text-white flex items-center">
|
||||
<CalendarClock className="w-5 h-5 mr-2 text-amber-400" />
|
||||
自动化定时任务
|
||||
</h3>
|
||||
<label className="flex items-center cursor-pointer">
|
||||
<div className="relative">
|
||||
<input type="checkbox" className="sr-only" checked={enabled} onChange={(e) => setEnabled(e.target.checked)} />
|
||||
<div className={`block w-10 h-6 rounded-full transition-colors ${enabled ? 'bg-amber-500' : 'bg-slate-700'}`}></div>
|
||||
<div className={`dot absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform ${enabled ? 'transform translate-x-4' : ''}`}></div>
|
||||
</div>
|
||||
<span className="ml-3 text-sm font-medium text-slate-300">{enabled ? '已启用自动入库' : '已暂停自动入库'}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className={`flex flex-col gap-4 transition-opacity ${enabled ? 'opacity-100' : 'opacity-40 pointer-events-none'}`}>
|
||||
<div className="flex flex-wrap gap-4 items-end">
|
||||
<div className="w-44 shrink-0">
|
||||
<label className="flex items-center text-xs font-medium text-slate-400 mb-1.5">执行频率</label>
|
||||
<select className="w-full px-3 py-2 rounded bg-slate-950 border border-slate-700 text-slate-200 text-sm focus:ring-amber-500 focus:border-amber-500 focus:outline-none"
|
||||
value={type} onChange={(e) => setType(e.target.value)}>
|
||||
<option value={SCHEDULE_TYPE.DAILY}>每天执行</option>
|
||||
<option value={SCHEDULE_TYPE.WEEKLY}>每周执行</option>
|
||||
<option value={SCHEDULE_TYPE.CRON}>专家模式 (Cron)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{type === SCHEDULE_TYPE.WEEKLY && (
|
||||
<div className="w-32 shrink-0">
|
||||
<label className="flex items-center text-xs font-medium text-slate-400 mb-1.5">星期</label>
|
||||
<select className="w-full px-3 py-2 rounded bg-slate-950 border border-slate-700 text-slate-200 text-sm focus:ring-amber-500 focus:border-amber-500 focus:outline-none"
|
||||
value={dayOfWeek} onChange={(e) => setDay(e.target.value)}>
|
||||
<option value="1">星期一</option><option value="2">星期二</option>
|
||||
<option value="3">星期三</option><option value="4">星期四</option>
|
||||
<option value="5">星期五</option><option value="6">星期六</option>
|
||||
<option value="0">星期日</option>
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type !== SCHEDULE_TYPE.CRON && (
|
||||
<div className="w-32 shrink-0">
|
||||
<label className="flex items-center text-xs font-medium text-slate-400 mb-1.5">具体时间</label>
|
||||
<input type="time" className="w-full px-3 py-2 rounded bg-slate-950 border border-slate-700 text-slate-200 text-sm font-mono focus:ring-amber-500 focus:border-amber-500 focus:outline-none"
|
||||
style={{ colorScheme: 'dark' }} value={time} onChange={(e) => setTime(e.target.value)} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type === SCHEDULE_TYPE.CRON && (
|
||||
<div className="flex-1 min-w-[200px]">
|
||||
<label className="flex items-center text-xs font-medium text-slate-400 mb-1.5">Cron 表达式</label>
|
||||
<input type="text" className="w-full px-3 py-2 rounded bg-slate-950 border border-slate-700 text-amber-400 text-sm font-mono focus:ring-amber-500 focus:border-amber-500 focus:outline-none"
|
||||
value={cron} onChange={(e) => setCron(e.target.value)} placeholder="例如: 0 2 * * *" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-4 rounded-xl border border-slate-800/90 bg-[#0b1328] px-4 py-3.5">
|
||||
<div className="flex min-w-0 items-center text-sm">
|
||||
<Clock className="mr-2 h-4 w-4 shrink-0 text-amber-400" />
|
||||
<span className="mr-2 shrink-0 text-slate-200">解析结果:</span>
|
||||
<span className="truncate font-medium text-amber-400">{getScheduleSummary(schedule)}</span>
|
||||
</div>
|
||||
<button className="flex shrink-0 items-center justify-center rounded-lg border border-slate-600 bg-slate-700/70 px-4 py-2 text-sm font-medium text-slate-100 shadow-inner shadow-slate-950/30 transition hover:bg-slate-600/80">
|
||||
<PlayCircle className="mr-1.5 h-4 w-4" /> 立即运行测试
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user