feat: 修改前端显示

This commit is contained in:
2026-10-18 22:52:18 +08:00
parent 8ce02af13a
commit 00a608c149
6 changed files with 235 additions and 82 deletions
+125 -47
View File
@@ -13,6 +13,7 @@ import {
mapServerFieldToFormField,
taskFormSchema,
taskToFormValues,
type KeyValueFormValue,
type TaskFormValues,
} from '../lib/taskForm'
import { useFocusTrap } from '../lib/useFocusTrap'
@@ -53,6 +54,7 @@ export function TaskEditorModal({
const actionType = watch('actionType')
const cronValue = watch('cron')
const timezoneValue = watch('timezone')
const scriptPreview = watch('script')
const debouncedCron = useDebouncedValue(cronValue, 300)
const debouncedTimezone = useDebouncedValue(timezoneValue, 300)
const cronPreview = useQuery({
@@ -169,7 +171,12 @@ export function TaskEditorModal({
</button>
</header>
<form className="modal-body" onSubmit={submit} noValidate>
<form
id="task-editor-form"
className="modal-body"
onSubmit={submit}
noValidate
>
{submitError ? (
<div className="inline-alert inline-alert-error" role="alert">
{submitError}
@@ -177,7 +184,10 @@ export function TaskEditorModal({
) : null}
<div className="form-grid">
<Field label="任务标识" error={errors.id?.message}>
<Field
label="任务标识 (Task ID)"
error={errors.id?.message}
>
<input
{...register('id')}
className="field-input mono"
@@ -187,7 +197,7 @@ export function TaskEditorModal({
aria-invalid={Boolean(errors.id)}
/>
</Field>
<Field label="任务名称" error={errors.name?.message}>
<Field label="名称" error={errors.name?.message}>
<input
{...register('name')}
className="field-input"
@@ -251,7 +261,7 @@ export function TaskEditorModal({
</span>
)}
</div>
<Field label="超时时间(秒)" error={errors.timeout_secs?.message}>
<Field label="超时时间 (秒)" error={errors.timeout_secs?.message}>
<input
{...register('timeout_secs', { valueAsNumber: true })}
type="number"
@@ -266,7 +276,7 @@ export function TaskEditorModal({
</Field>
</div>
<div className="form-grid">
<div className="form-grid form-grid-single">
<Field label="IANA 时区" error={errors.timezone?.message}>
<input
{...register('timezone')}
@@ -283,26 +293,19 @@ export function TaskEditorModal({
<option value="America/New_York" />
</datalist>
</Field>
<Field label="任务类型">
<Field label="任务类型 (Action Type)">
<select {...register('actionType')} className="field-input">
<option value="script">执行脚本</option>
<option value="http">HTTP 请求</option>
<option value="process">二进制进程</option>
<option value="script">
执行脚本 (Python / Shell / Node / Lua)
</option>
<option value="http">发起 HTTP 请求</option>
<option value="process">管理常驻/独立二进制</option>
</select>
</Field>
</div>
<label className="checkbox-field">
<input type="checkbox" {...register('enabled')} />
<span>
<strong>立即激活此任务调度</strong>
<small>保存后立即注册到 Cron 调度器</small>
</span>
</label>
{actionType === 'script' ? (
<fieldset className="action-panel">
<legend>脚本配置</legend>
<div className="dynamic-fields">
<div className="form-grid">
<Field
label="工作目录"
@@ -344,7 +347,9 @@ export function TaskEditorModal({
<button
type="button"
className="button button-secondary button-small"
onClick={() => scriptArgs.append({ value: '' })}
onClick={() =>
scriptArgs.append({ key: '', value: '' })
}
>
+ 添加参数
</button>
@@ -352,10 +357,15 @@ export function TaskEditorModal({
<div className="collection-list">
{scriptArgs.fields.map((field, index) => (
<div className="argument-row" key={field.id}>
<input
{...register(`script.args.${index}.key`)}
className="field-input mono"
placeholder="参数名 (如 --mode 或 -v)"
/>
<input
{...register(`script.args.${index}.value`)}
className="field-input mono"
placeholder="例如:--mode"
placeholder="参数值 (可选,无值可留空)"
/>
<button
type="button"
@@ -385,12 +395,38 @@ export function TaskEditorModal({
onAdd={() => scriptEnv.append({ key: '', value: '' })}
onRemove={scriptEnv.remove}
/>
</fieldset>
<div className="cli-preview">
<div className="cli-preview-header">
<span>调用预览 (Runtime Preview)</span>
<span>Windows 原生环境</span>
</div>
<div className="cli-preview-body">
{scriptPreview.env
.filter((item) => item.key.trim())
.map((item, index) => (
<div
className="cli-line-env"
key={`${item.key}-${index}`}
>
set {item.key.trim()}=
{isSensitiveName(item.key)
? '[REDACTED]'
: item.value || '""'}
</div>
))}
<div className="cli-line-cmd">
{scriptPreview.interpreter.trim() || 'python.exe'}{' '}
{scriptPreview.script.trim() || 'main.py'}
{formatArgumentPreview(scriptPreview.args)}
</div>
</div>
</div>
</div>
) : null}
{actionType === 'http' ? (
<fieldset className="action-panel">
<legend>HTTP 配置</legend>
<div className="dynamic-fields">
<div className="form-grid">
<Field label="请求 URL" error={errors.http?.url?.message}>
<input
@@ -436,12 +472,11 @@ export function TaskEditorModal({
onAdd={() => httpHeaders.append({ key: '', value: '' })}
onRemove={httpHeaders.remove}
/>
</fieldset>
</div>
) : null}
{actionType === 'process' ? (
<fieldset className="action-panel">
<legend>进程配置</legend>
<div className="dynamic-fields">
<div className="form-grid">
<Field
label="可执行程序"
@@ -469,7 +504,9 @@ export function TaskEditorModal({
<button
type="button"
className="button button-secondary button-small"
onClick={() => processArgs.append({ value: '' })}
onClick={() =>
processArgs.append({ key: '', value: '' })
}
>
+ 添加参数
</button>
@@ -477,10 +514,15 @@ export function TaskEditorModal({
<div className="collection-list">
{processArgs.fields.map((field, index) => (
<div className="argument-row" key={field.id}>
<input
{...register(`process.args.${index}.key`)}
className="field-input mono"
placeholder="参数名 (如 --port)"
/>
<input
{...register(`process.args.${index}.value`)}
className="field-input mono"
placeholder="例如:--port"
placeholder="参数值 (可选)"
/>
<button
type="button"
@@ -509,32 +551,68 @@ export function TaskEditorModal({
onAdd={() => processEnv.append({ key: '', value: '' })}
onRemove={processEnv.remove}
/>
</fieldset>
</div>
) : null}
<footer className="form-actions">
<button
type="button"
className="button button-secondary"
onClick={onClose}
disabled={saving}
>
取消
</button>
<button
type="submit"
className="button button-primary"
disabled={saving}
>
{saving ? '保存中...' : task ? '保存修改' : '保存并热刷新'}
</button>
</footer>
<label className="checkbox-field">
<input type="checkbox" {...register('enabled')} />
<span>
<strong>立即激活此任务调度</strong>
<small>保存后立即注册到 Cron 调度器</small>
</span>
</label>
</form>
<footer className="modal-footer">
<button
type="button"
className="button button-secondary"
onClick={onClose}
disabled={saving}
>
取消
</button>
<button
type="submit"
form="task-editor-form"
className="button button-primary"
disabled={saving}
>
{saving ? '保存中...' : task ? '保存修改' : '保存并热刷新'}
</button>
</footer>
</section>
</div>
)
}
function formatArgumentPreview(argumentsList: KeyValueFormValue[]): string {
const values = argumentsList.flatMap(({ key, value }) => {
const argument = key.trim()
const argumentValue = value.trim()
return [
argument,
argumentValue.includes(' ') ? `"${argumentValue}"` : argumentValue,
].filter(Boolean)
})
return values.length ? ` ${values.join(' ')}` : ''
}
function isSensitiveName(name: string): boolean {
const normalized = name.toUpperCase()
return [
'SECRET',
'TOKEN',
'PASSWORD',
'PASS',
'API_KEY',
'PRIVATE_KEY',
'CREDENTIAL',
'AUTHORIZATION',
'AUTH',
].some((marker) => normalized.includes(marker))
}
interface FieldProps {
label: string
error?: string