diff --git a/web/client/src/jsonRender/catalog.js b/web/client/src/jsonRender/catalog.js index 7691e24..ccc8ff0 100644 --- a/web/client/src/jsonRender/catalog.js +++ b/web/client/src/jsonRender/catalog.js @@ -242,6 +242,16 @@ const catalog = defineCatalog(schema, { }), description: "日期选择器,支持 $bindState 双向绑定到 value", }, + DateTimePicker: { + props: z.object({ + value: z.string().nullish(), + placeholder: z.string().nullish(), + label: z.string().nullish(), + format: z.string().nullish(), + disabled: z.boolean().nullish(), + }), + description: "日期时间选择器,支持 $bindState 双向绑定到 value", + }, StructuredEditor: { props: z.object({ value: z.array(z.record(z.unknown())).nullish(), diff --git a/web/client/src/jsonRender/registry.jsx b/web/client/src/jsonRender/registry.jsx index 0d70750..f934c36 100644 --- a/web/client/src/jsonRender/registry.jsx +++ b/web/client/src/jsonRender/registry.jsx @@ -26,6 +26,7 @@ const SIZE_ALIASES = { lg: "large", default: "middle", }; +const DEFAULT_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss"; const EMPTY_MULTI_VALUE = []; //[validateStatePath][校验状态路径并阻止原型链字段写入] @@ -718,6 +719,25 @@ const { registry, handlers, executeAction } = defineRegistry(catalog, { return renderFormControl(props.label, control); }, + DateTimePicker: ({ props, bindings }) => { + const [value, setValue] = useEditableProp( + props.value, + bindings?.value, + undefined + ); + const control = ( + setValue(dateString)} + placeholder={props.placeholder} + format={props.format || DEFAULT_DATETIME_FORMAT} + disabled={props.disabled} + showTime + /> + ); + return renderFormControl(props.label, control); + }, + StructuredEditor: ({ props, bindings }) => { const [value, setValue] = useEditableProp( props.value, diff --git a/web/client/src/jsonRender/render-ui.md b/web/client/src/jsonRender/render-ui.md index d7fa43c..4980679 100644 --- a/web/client/src/jsonRender/render-ui.md +++ b/web/client/src/jsonRender/render-ui.md @@ -103,6 +103,7 @@ | **RadioGroup** | `value` | `options[{label,value}]`, `optionType` | | **NumberInput** | `value` | `min`, `max`, `step`, `precision`, `prefix`, `suffix` | | **DatePicker** | `value` | `format`, `picker` (date/week/month/quarter/year) | +| **DateTimePicker** | `value` | `format`(默认 `YYYY-MM-DD HH:mm:ss`) | | **Slider** | `value` | `min`, `max`, `step`, `range` | #### 4.4 操作类 diff --git a/web/client/src/sections/superAgent/container/index.jsx b/web/client/src/sections/superAgent/container/index.jsx index 568c4d5..4914ac0 100644 --- a/web/client/src/sections/superAgent/container/index.jsx +++ b/web/client/src/sections/superAgent/container/index.jsx @@ -902,6 +902,14 @@ const SuperAgent = () => { (taskStatus === "suspended" || runtimeSessionStatus === "suspended"); const hasPersistedRunningThread = hasPersistedActiveThread && !hasSuspendedRuntimeStatus && !isFailedTask; + const hasTerminalStreamRunPhase = [ + "completed", + "failed", + "cancelled", + "canceled", + "aborted", + "stopped", + ].includes(stream.runPhase); const isTerminalRuntimeSession = [ "failed", "completed", @@ -914,6 +922,7 @@ const SuperAgent = () => { runtimeTaskType === "artifact_build" || (taskStage === TASK_STAGE.CONTENT && runtimeTaskTotal > 0); const isBodyGenerationRuntimeActive = + !hasTerminalStreamRunPhase && runtimeBodyTaskShape && [ "queued", @@ -990,6 +999,7 @@ const SuperAgent = () => { ["running", "awaiting_interrupt", "resuming"].includes(stream.runPhase) && (!isTerminalTask || stream.isLocalRunActive); const isStreamRunning = + !hasTerminalStreamRunPhase && !isFailedTask && (hasPersistedRunningThread || isBodyGenerationRuntimeActive || diff --git a/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js b/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js index bc2d188..c22eb20 100644 --- a/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js +++ b/web/client/src/sections/superAgent/hooks/useSuperAgentStream.js @@ -1172,16 +1172,22 @@ const useSuperAgentStream = ({ "cancelled", "canceled", ].includes(runPhase); + const isPersistedExecutionActiveForUi = + isPersistedExecutionActive && !isTerminalRunPhase; const effectiveRunPhase = isPersistedSuspended ? "idle" : shouldResetStaleWaitingPhase ? "running" - : isPersistedExecutionActive && (runPhase === "idle" || isTerminalRunPhase) + : isPersistedExecutionActive && runPhase === "idle" ? "running" : runPhase; - const effectiveIsLocalRunActive = isPersistedSuspended ? false : isLocalRunActive; - const effectiveIsBusinessRunning = isPersistedSuspended ? false : isBusinessRunning; + const isPersistedThreadActiveForUi = + isPersistedThreadActive && !isTerminalRunPhase; + const effectiveIsLocalRunActive = + isPersistedSuspended || isTerminalRunPhase ? false : isLocalRunActive; + const effectiveIsBusinessRunning = + isPersistedSuspended || isTerminalRunPhase ? false : isBusinessRunning; const visibleInterrupts = useMemo(() => { if (!isStreamProjectionReady && !isInterruptProjectionReady) return []; @@ -1230,13 +1236,15 @@ const useSuperAgentStream = ({ effectiveRunPhase ); const effectiveIsLoading = - isPersistedSuspended - ? isRespondingInterrupt - : stream.isLoading || - isRespondingInterrupt || - isBusinessRunning || - isRunPhaseActive || - isPersistedExecutionActive; + isTerminalRunPhase + ? false + : isPersistedSuspended + ? isRespondingInterrupt + : stream.isLoading || + isRespondingInterrupt || + isBusinessRunning || + isRunPhaseActive || + isPersistedExecutionActiveForUi; return { messages: streamMessages, @@ -1250,7 +1258,7 @@ const useSuperAgentStream = ({ isLoading: effectiveIsLoading, isLocalRunActive: effectiveIsLocalRunActive, runPhase: effectiveRunPhase, - isPersistedThreadActive, + isPersistedThreadActive: isPersistedThreadActiveForUi, currentInterruptId: hydratedState && pendingHydratedInterrupts.length === 0 &&