diff --git a/frontend/src/App.css b/frontend/src/App.css index 87f1da4..7e55f13 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -211,6 +211,11 @@ gap: 16px; } +/* 虚拟环境目录通常是一条完整路径,给它更多宽度;解释器名很短 */ +.venv-grid { + grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.9fr); +} + .form-select { width: 100%; } @@ -889,6 +894,10 @@ grid-template-columns: 1fr; } + .venv-grid { + grid-template-columns: 1fr; + } + .cron-run-grid { grid-template-columns: 1fr; } diff --git a/frontend/src/components/TaskEditorModal.tsx b/frontend/src/components/TaskEditorModal.tsx index 67a8d88..900ec86 100644 --- a/frontend/src/components/TaskEditorModal.tsx +++ b/frontend/src/components/TaskEditorModal.tsx @@ -15,7 +15,7 @@ import { Textarea, TextButton, } from '@stash/ui' -import { useEffect, useState, type ReactNode } from 'react' +import { useEffect, useRef, useState, type ReactNode } from 'react' import { Controller, type FieldPath, @@ -87,9 +87,13 @@ export function TaskEditorModal({ const [submitError, setSubmitError] = useState(null) const [cronPreviewCount, setCronPreviewCount] = useState(5) const [pickerOpen, setPickerOpen] = useState(false) + // 记录「自动填入」的虚拟环境目录:用户手动改过就不再覆盖; + // 工作目录变化时只有仍等于该值的字段会被清掉,交给新目录重新探测。 + const autoVenvRef = useRef('') const { register, control, + getValues, handleSubmit, reset, watch, @@ -181,8 +185,49 @@ export function TaskEditorModal({ setCronPreviewCount(5) setSubmitError(null) setPickerOpen(false) + autoVenvRef.current = '' }, [open, reset, task]) + // 换工作目录时清掉上一轮自动填入的值(手动填写的值不动)。 + useEffect(() => { + if (!open) { + return + } + if (autoVenvRef.current && getValues('script.venv').trim() === autoVenvRef.current) { + setValue('script.venv', '', { shouldDirty: false }) + } + autoVenvRef.current = '' + }, [debouncedWorkingDir, getValues, open, setValue]) + + // 探测到虚拟环境就自动填进「虚拟环境目录」,但用户自己改过就保留用户的值。 + useEffect(() => { + if (!open) { + return + } + const probe = venvProbe.data + if (!probe || probe.status !== 'linked' || !probe.venv_path) { + return + } + const current = getValues('script.venv').trim() + // 防抖中的输入可能还没落到查询键上:只有「当前输入」与「本次探测用的输入」 + // 一致时才回填,否则工作目录刚变化时可能把上一轮探测结果又填回去。 + if (debouncedVenv.trim() !== current) { + return + } + if (current && current !== autoVenvRef.current) { + return + } + if (current !== '' && current === autoVenvRef.current) { + return + } + const next = venvFieldValue(debouncedWorkingDir, probe.venv_path) + autoVenvRef.current = next + setValue('script.venv', next, { + shouldDirty: true, + shouldValidate: true, + }) + }, [debouncedVenv, debouncedWorkingDir, getValues, open, setValue, venvProbe.data]) + // 打开后把焦点给第一个可编辑字段:新建时是任务标识,编辑时 ID 已锁定,跳名称。 // 原生 dialog 默认聚焦关闭按钮,不符合编辑表单的节奏。 useEffect(() => { @@ -442,14 +487,20 @@ export function TaskEditorModal({ - - -
+
+ + + - - -
+ +