diff --git a/packages/web-proxy/src/credentials.ts b/packages/web-proxy/src/credentials.ts index d36efb007..03348e091 100644 --- a/packages/web-proxy/src/credentials.ts +++ b/packages/web-proxy/src/credentials.ts @@ -356,7 +356,7 @@ export function buildAutofillScript(selectors, credentials) { successSelector: selectors.success, interactiveSelector: selectors.interactive }; - return `(() => { + return `(async () => { ${selectorLookupScript} ${submitElementScript} let payload = ${JSON.stringify(payload)}; @@ -412,9 +412,17 @@ if (successObserver) { } // The main process decides whether a visible verification area must precede submission. if (payload.interactiveSelector) return true; -internalAction = true; try { - return submitElement(submit); + // Input handlers may schedule a framework render that replaces or enables the + // submit button. Let that update finish, then resolve the current DOM node. + await new Promise(resolve => setTimeout(resolve, 0)); + const currentSubmit = findElement(payload.submitSelector); + if (!(currentSubmit instanceof HTMLElement) || !currentSubmit.isConnected || currentSubmit.disabled) { + throw new Error("登录提交按钮不可用,请检查提交元素配置或页面是否需要验证"); + } + internalAction = true; + if (!submitElement(currentSubmit)) throw new Error("登录表单校验未通过,请检查必填项或验证码配置"); + return true; } finally { internalAction = false; if (!payload.successSelector && !payload.interactiveSelector) cleanup(); diff --git a/ui/composables/useAiPanel.test.ts b/ui/composables/useAiPanel.test.ts index e78c3232b..f76a7cc47 100644 --- a/ui/composables/useAiPanel.test.ts +++ b/ui/composables/useAiPanel.test.ts @@ -54,8 +54,9 @@ describe("AI overlay panel", () => { afterAll(() => vi.unstubAllGlobals()); - it("remembers visibility independently for each workspace tab", () => { + it("defaults to closed on desktop and remembers visibility independently for each workspace tab", () => { const panel = useAiPanel(); + expect(useLocalStorage).toHaveBeenCalledWith("jumpserver-client:ai-panel-open", false, { writeDefaults: false }); panel.setOpen(false); expect(panel.open.value).toBe(false); diff --git a/ui/composables/useAiPanel.ts b/ui/composables/useAiPanel.ts index 13e2907c3..cfc085901 100644 --- a/ui/composables/useAiPanel.ts +++ b/ui/composables/useAiPanel.ts @@ -16,7 +16,7 @@ interface UnifiedAiPanelContext { } const usePanelPreferences = createGlobalState(() => { - const preferredOpen = useLocalStorage("jumpserver-client:ai-panel-open", true, { writeDefaults: false }); + const preferredOpen = useLocalStorage("jumpserver-client:ai-panel-open", false, { writeDefaults: false }); const storedWidth = useLocalStorage("jumpserver-client:ai-panel-width", AI_PANEL_DEFAULT_WIDTH, { writeDefaults: false });