style(frontend): 重排任务编辑器布局
- 未来运行时间改为全宽三列面板,次数选择器移到面板头部,去掉内部滚动条 - Cron / 超时、时区 / 任务类型两两成行,消除原右列的大片空白 - 校验改为提交时触发,避免 blur 插错误行导致下方控件位移、下拉框点击丢失
This commit is contained in:
+39
-10
@@ -211,10 +211,6 @@
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.form-grid-single {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-select {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -236,15 +232,13 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cron-preview-box {
|
||||
.cron-preview-panel {
|
||||
display: flex;
|
||||
max-height: 160px;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow-y: auto;
|
||||
gap: 10px;
|
||||
border: 1px solid var(--su-border);
|
||||
border-radius: var(--su-radius-sm);
|
||||
padding: 10px 12px;
|
||||
padding: 12px 14px;
|
||||
background-color: var(--su-bg-subtle);
|
||||
}
|
||||
|
||||
@@ -252,15 +246,42 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cron-preview-title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.cron-preview-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cron-preview-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.cron-count-select {
|
||||
min-width: 96px;
|
||||
}
|
||||
|
||||
/* 未来运行时间按三列铺开:默认 5 条一眼看完,不需要内部滚动条 */
|
||||
.cron-run-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 6px 12px;
|
||||
}
|
||||
|
||||
.cron-run-item {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--su-text-secondary);
|
||||
@@ -270,18 +291,22 @@
|
||||
|
||||
.cron-run-index {
|
||||
width: 28px;
|
||||
flex: none;
|
||||
color: var(--su-text-muted);
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.cron-run-time {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--su-border);
|
||||
border-radius: var(--su-radius-xs);
|
||||
padding: 2px 8px;
|
||||
background-color: var(--su-bg-card);
|
||||
color: var(--su-text-main);
|
||||
font-weight: 500;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cron-preview-error {
|
||||
@@ -854,6 +879,10 @@
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.cron-run-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.argument-row,
|
||||
.key-value-row {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Notice,
|
||||
PasswordInput,
|
||||
Select,
|
||||
Spinner,
|
||||
Text,
|
||||
Textarea,
|
||||
TextButton,
|
||||
@@ -97,7 +98,10 @@ export function TaskEditorModal({
|
||||
} = useForm<TaskFormValues>({
|
||||
resolver: zodResolver(taskFormSchema),
|
||||
defaultValues: createEmptyTaskFormValues(),
|
||||
mode: 'onBlur',
|
||||
// 长表单在 blur 时插错误行会让下面所有控件瞬间位移,刚点下去的按钮
|
||||
// 可能在 mouseup 前被挪走(下拉框会「点了没反应」)。改为提交时校验,
|
||||
// 提交后仍按 onChange 实时修正。
|
||||
mode: 'onSubmit',
|
||||
})
|
||||
const actionType = watch('actionType')
|
||||
const cronValue = watch('cron')
|
||||
@@ -288,42 +292,6 @@ export function TaskEditorModal({
|
||||
placeholder="0 0 2 * * *"
|
||||
aria-invalid={Boolean(errors.cron)}
|
||||
/>
|
||||
<div className="cron-preview-box" aria-live="polite">
|
||||
<div className="cron-preview-head">
|
||||
<Text size="xs" tone="muted">
|
||||
预览未来运行时间
|
||||
</Text>
|
||||
<Select
|
||||
className="cron-count-select"
|
||||
options={CRON_PREVIEW_COUNTS.map((count) => ({
|
||||
value: String(count),
|
||||
label: `${count} 次`,
|
||||
}))}
|
||||
value={String(cronPreviewCount)}
|
||||
onChange={(value) => setCronPreviewCount(Number(value))}
|
||||
/>
|
||||
</div>
|
||||
{cronPreview.isFetching ? (
|
||||
<Text size="sm" tone="sub">
|
||||
正在计算...
|
||||
</Text>
|
||||
) : cronPreview.isError ? (
|
||||
<Text size="sm" className="cron-preview-error">
|
||||
{cronPreview.error.message}
|
||||
</Text>
|
||||
) : cronPreview.data?.runs.length ? (
|
||||
cronPreview.data.runs.map((run, index) => (
|
||||
<div className="cron-run-item" key={run}>
|
||||
<span className="cron-run-index">#{index + 1}</span>
|
||||
<span className="cron-run-time">{run}</span>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<Text size="sm" tone="sub">
|
||||
等待输入有效 Cron 表达式...
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
@@ -348,7 +316,64 @@ export function TaskEditorModal({
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<div className="form-grid form-grid-single">
|
||||
<section
|
||||
className="cron-preview-panel"
|
||||
aria-label="未来运行时间"
|
||||
aria-live="polite"
|
||||
>
|
||||
<div className="cron-preview-head">
|
||||
<div className="cron-preview-title">
|
||||
<Text strong size="sm">
|
||||
未来运行时间
|
||||
</Text>
|
||||
<Text size="xs" tone="muted">
|
||||
按当前 Cron 与时区实时计算
|
||||
</Text>
|
||||
</div>
|
||||
<div className="cron-preview-actions">
|
||||
<Text size="xs" tone="muted">
|
||||
预览
|
||||
</Text>
|
||||
<Select
|
||||
className="cron-count-select"
|
||||
options={CRON_PREVIEW_COUNTS.map((count) => ({
|
||||
value: String(count),
|
||||
label: `${count} 次`,
|
||||
}))}
|
||||
value={String(cronPreviewCount)}
|
||||
onChange={(value) => setCronPreviewCount(Number(value))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{cronPreview.isFetching ? (
|
||||
<div className="cron-preview-status">
|
||||
<Spinner size="sm" label="正在计算" />
|
||||
<Text size="sm" tone="sub">
|
||||
正在计算...
|
||||
</Text>
|
||||
</div>
|
||||
) : cronPreview.isError ? (
|
||||
<Text size="sm" className="cron-preview-error">
|
||||
{cronPreview.error.message}
|
||||
</Text>
|
||||
) : cronPreview.data?.runs.length ? (
|
||||
<div className="cron-run-grid">
|
||||
{cronPreview.data.runs.map((run, index) => (
|
||||
<div className="cron-run-item" key={run}>
|
||||
<span className="cron-run-index">#{index + 1}</span>
|
||||
<span className="cron-run-time">{run}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Text size="sm" tone="sub">
|
||||
等待输入有效 Cron 表达式...
|
||||
</Text>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<div className="form-grid">
|
||||
<FormField
|
||||
label="IANA 时区"
|
||||
htmlFor="task-timezone"
|
||||
|
||||
Reference in New Issue
Block a user