From 39ab7d970012226daac08980ff7323196d11cf29 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Sat, 25 Jul 2026 14:25:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(desktop):=20=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E5=9C=B0=E5=9D=80=E8=BF=90=E8=A1=8C=E6=97=B6=E5=8F=AF=E9=85=8D?= =?UTF-8?q?(=E7=99=BB=E5=BD=95=E9=A1=B5=E5=8F=AF=E5=A1=AB,=E4=B8=8D?= =?UTF-8?q?=E5=BF=85=E9=87=8D=E7=BC=96=E5=AE=A2=E6=88=B7=E7=AB=AF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此前 GATEWAY 只在构建期由 VITE_GATEWAY 写死,客户端连线上后端就得重新打包, 换一次环境编一次不现实。 改:优先级 localStorage > 构建期 VITE_GATEWAY > localhost:8080; 登录页加折叠的「服务器地址」输入(必须在登录前可改——地址不对连都连不上, 更谈不上登录);保存后自动重载窗口生效(模块级常量在加载时读定)。 顺带:地址统一去尾斜杠,否则拼出 //api/v1 这种路径。 Co-Authored-By: Claude Opus 5 --- sundynix-desktop/frontend/src/lib/api.ts | 39 ++++++++++++++-- sundynix-desktop/frontend/src/views/Login.tsx | 44 ++++++++++++++++++- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/sundynix-desktop/frontend/src/lib/api.ts b/sundynix-desktop/frontend/src/lib/api.ts index d8efab9..11d848a 100644 --- a/sundynix-desktop/frontend/src/lib/api.ts +++ b/sundynix-desktop/frontend/src/lib/api.ts @@ -1,9 +1,42 @@ // Gateway HTTP/SSE 客户端:提交 DSL 任务、订阅 Token 流、登记偏好记忆。 import type { TaskDsl } from "./dsl"; -// 开发期直连 Gateway;Wails 打包后可改为本地后端地址或经 Go 绑定。 -export const GATEWAY: string = - (import.meta.env.VITE_GATEWAY as string | undefined) ?? "http://localhost:8080"; +// 服务器地址:**运行时可配**(登录页可填),优先级 localStorage > 构建期 VITE_GATEWAY > 本地默认。 +// 做成运行时而非只在构建期写死,是因为同一个 App 要能在「本地全栈」和「线上服务器」之间随时切, +// 每换一次环境就重编一次客户端不现实。改完需重载窗口(模块级常量,40+ 处调用点靠它读同一份值)。 +const GATEWAY_KEY = "sdx_gateway"; + +function initialGateway(): string { + try { + const saved = localStorage.getItem(GATEWAY_KEY); + if (saved) return saved.replace(/\/+$/, ""); // 去掉尾斜杠,否则拼出 //api/v1 这种路径 + } catch { + /* 隐私模式忽略 */ + } + return (import.meta.env.VITE_GATEWAY as string | undefined) ?? "http://localhost:8080"; +} + +export const GATEWAY: string = initialGateway(); + +// setGatewayURL 保存服务器地址(空串=清除,回落构建期默认)。调用方保存后应重载窗口使其生效。 +export function setGatewayURL(url: string): void { + try { + const v = url.trim().replace(/\/+$/, ""); + if (v) localStorage.setItem(GATEWAY_KEY, v); + else localStorage.removeItem(GATEWAY_KEY); + } catch { + /* ignore */ + } +} + +// customGatewayURL 返回用户自定义的地址(没设过则空串),供设置界面回显。 +export function customGatewayURL(): string { + try { + return localStorage.getItem(GATEWAY_KEY) ?? ""; + } catch { + return ""; + } +} export interface Identity { userId: string; diff --git a/sundynix-desktop/frontend/src/views/Login.tsx b/sundynix-desktop/frontend/src/views/Login.tsx index bb77b5d..b123db2 100644 --- a/sundynix-desktop/frontend/src/views/Login.tsx +++ b/sundynix-desktop/frontend/src/views/Login.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { LogIn, UserPlus, Loader2 } from "lucide-react"; -import { authLogin, authRegister, type AuthUser } from "../lib/api"; +import { authLogin, authRegister, customGatewayURL, setGatewayURL, GATEWAY, type AuthUser } from "../lib/api"; import { Button, Input, Field } from "../ui"; // Login:未登录时的全屏鉴权门。登录/注册成功后回调 onAuthed 把用户交给 App。 @@ -66,7 +66,49 @@ export function Login({ onAuthed }: { onAuthed: (u: AuthUser) => void }) { {isRegister ? "去登录" : "去注册"} + + ); } + +// ServerField 服务器地址设置:默认折叠,展开后可指向线上后端或本地全栈。 +// 放在登录页是因为它必须在登录之前可改——地址不对就连不上、更谈不上登录。 +function ServerField() { + const [open, setOpen] = useState(false); + const [url, setUrl] = useState(customGatewayURL()); + const [saved, setSaved] = useState(false); + + const save = () => { + setGatewayURL(url); + setSaved(true); + // 模块级常量在加载时读定,改完必须重载窗口才生效——直接替用户重载,省得他困惑为何没变。 + setTimeout(() => window.location.reload(), 400); + }; + + return ( +
+ + {open && ( +
+ setUrl(e.target.value)} + placeholder="https://agent.sundynix.cn" + onKeyDown={(e) => e.key === "Enter" && save()} + /> +
+ + 留空 = 用默认 +
+
+ )} +
+ ); +}