From a74cfa990f4f8196bf305045f5454436b20910ce Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 3 Aug 2026 19:25:30 +0000 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20Teamserver=20=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E4=B8=AD=E5=BF=83=E4=B8=8E=E5=AE=89=E5=85=A8=E8=AF=8A?= =?UTF-8?q?=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: multica-agent --- src/App.tsx | 8 +- src/features/connections/components.test.tsx | 23 ++++ src/features/connections/components.tsx | 109 +++++++++++++++++++ src/features/connections/index.ts | 3 + src/features/connections/mockAdapter.ts | 29 +++++ src/features/connections/model.test.ts | 41 +++++++ src/features/connections/model.ts | 99 +++++++++++++++++ src/styles.css | 40 ++++++- src/vite-env.d.ts | 1 + 9 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 src/features/connections/components.test.tsx create mode 100644 src/features/connections/components.tsx create mode 100644 src/features/connections/index.ts create mode 100644 src/features/connections/mockAdapter.ts create mode 100644 src/features/connections/model.test.ts create mode 100644 src/features/connections/model.ts create mode 100644 src/vite-env.d.ts diff --git a/src/App.tsx b/src/App.tsx index ec50883..ebaf560 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,5 @@ import { useReducer, useState } from "react"; +import { ConnectionCenter, MemoryMockConnectionAdapter } from "./features/connections"; import { SessionQueryIntent, SessionTable } from "./SessionTable"; import { demoSessions } from "./protocol/fixtures"; import { initializeSessionTableStates, updateSessionTableState } from "./sessionTableState"; @@ -12,11 +13,13 @@ import { const fixtureNow = new Date("2026-08-03T19:12:02Z"); const sessions = demoSessions.map((session) => toSessionRow(session, fixtureNow)); +const connectionAdapter = new MemoryMockConnectionAdapter(); export function App() { const [workspace, dispatch] = useReducer(workspaceReducer, undefined, createInitialWorkspaceState); const [tableStates, setTableStates] = useState(() => initializeSessionTableStates(workspace.serverOrder)); const [lastQueryIntent, setLastQueryIntent] = useState>({}); + const [view, setView] = useState<"sessions" | "connections">("sessions"); const server = selectActiveWorkspace(workspace); const activeTab = selectActiveTab(server); const tableState = { ...tableStates[server.id], filter: server.filter }; @@ -54,9 +57,10 @@ export function App() {

仅内存状态 · 退出后清除

@@ -81,6 +85,7 @@ export function App() { ◆ LOCKED{server.name} / {activeTab.context.project} / {activeTab.context.session} / {activeTab.title}
+ {view === "connections" ? : <>

{activeTab.title}

{activeTab.context.project} 中的 248 个 Session · 4 个实时更新

@@ -90,6 +95,7 @@ export function App() { setTableStates((current) => updateSessionTableState(current, server.id, state)); }} onQueryIntent={(intent) => setLastQueryIntent((current) => ({ ...current, [server.id]: intent }))} /> {lastQueryIntent[server.id] ? `查询意图:${lastQueryIntent[server.id]?.type}` : "等待服务端查询意图"} + }
● {server.connection}身份 operator@example延迟 38ms协议 v1.0事件流 ✓ 已同步内存工作区
diff --git a/src/features/connections/components.test.tsx b/src/features/connections/components.test.tsx new file mode 100644 index 0000000..f2c494a --- /dev/null +++ b/src/features/connections/components.test.tsx @@ -0,0 +1,23 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { ConnectionCenter, ConnectionEditor } from "./components"; +import { MemoryMockConnectionAdapter } from "./mockAdapter"; + +describe("连接中心组件", () => { + it("表单拒绝无效配置,秘密字段使用 password 类型", () => { + render( undefined} onCancel={() => undefined} />); + expect(screen.getByLabelText("认证秘密")).toHaveAttribute("type", "password"); + fireEvent.click(screen.getByRole("button", { name: "保存到内存" })); + expect(screen.getByRole("alert")).toHaveTextContent("配置错误"); + }); + + it("通过 mock adapter 完成创建并明确未建立网络连接", async () => { + render(); + fireEvent.click(await screen.findByRole("button", { name: "+ 新建连接" })); + fireEvent.change(screen.getByLabelText("名称"), { target: { value: "Atlas" } }); + fireEvent.change(screen.getByLabelText("地址"), { target: { value: "https://atlas.example" } }); + fireEvent.click(screen.getByRole("button", { name: "保存到内存" })); + await waitFor(() => expect(screen.getByText("Atlas")).toBeInTheDocument()); + expect(screen.getByText(/尚未建立网络连接/)).toBeInTheDocument(); + }); +}); diff --git a/src/features/connections/components.tsx b/src/features/connections/components.tsx new file mode 100644 index 0000000..b121979 --- /dev/null +++ b/src/features/connections/components.tsx @@ -0,0 +1,109 @@ +import { ChangeEvent, ReactNode, useEffect, useMemo, useState } from "react"; +import { ConnectionAdapter } from "./mockAdapter"; +import { blankConnection, ConnectionConfig, ConnectionRecord, connectionStates, DiagnosticCategory, DiagnosticResult, maskedSecret, parseImportPreview, toSafeExport, validateConnection } from "./model"; + +const field = (config: ConnectionConfig, onChange: (next: ConnectionConfig) => void) => + (key: K, value: ConnectionConfig[K]) => onChange({ ...config, [key]: value }); + +function Section({ title, children }: { title: string; children: ReactNode }) { + return
{title}{children}
; +} + +export function TlsSettings({ config, onChange, developmentMode }: { config: ConnectionConfig; onChange: (next: ConnectionConfig) => void; developmentMode: boolean }) { + const set = (value: Partial) => onChange({ ...config, tls: { ...config.tls, ...value } }); + return
+ +