From 3652db8fa36fe1029b0e18ebbb29c00518a92343 Mon Sep 17 00:00:00 2001 From: runstone Date: Tue, 12 May 2026 10:21:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(connection):=20=E4=B8=BA=E4=B8=8D=E5=90=8C?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E7=B1=BB=E5=9E=8B=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=90=84=E8=87=AA=E5=AF=B9=E5=BA=94=E7=9A=84=20connection=20UR?= =?UTF-8?q?L=20placeholder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 修改内容 - 新增 connectionUrlPlaceholder 纯函数于 connectionPresentation.ts,根据 db_type 返回对应 URL 格式 - ConnectionDialog.vue 中引用该函数替换原有统一的 i18n placeholder - 新增 20 个单元测试覆盖所有数据库类型及边界场景 --- .../connection/ConnectionDialog.vue | 5 +- src/lib/connectionPresentation.ts | 49 +++++++++++++++++- tests/connectionUrlPlaceholder.test.ts | 51 +++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/connectionUrlPlaceholder.test.ts diff --git a/src/components/connection/ConnectionDialog.vue b/src/components/connection/ConnectionDialog.vue index dcad24c26d..10c1727b90 100644 --- a/src/components/connection/ConnectionDialog.vue +++ b/src/components/connection/ConnectionDialog.vue @@ -16,6 +16,7 @@ import DatabaseIcon from "@/components/icons/DatabaseIcon.vue"; import * as api from "@/lib/api"; import { isTauriRuntime } from "@/lib/tauriRuntime"; import { applyParsedConnectionUrl, parseConnectionUrl } from "@/lib/connectionUrl"; +import { connectionUrlPlaceholder as getUrlPlaceholder } from "@/lib/connectionPresentation"; import { ArrowLeft, ChevronRight, Copy, ExternalLink, FolderOpen, Grid3X3, Link2, List, Search } from "lucide-vue-next"; type DbOption = { value: string; label: string }; @@ -419,6 +420,8 @@ const filteredDbCategories = computed(() => { const hasDbPickerResults = computed(() => filteredDbCategories.value.some((category) => category.options.length > 0)); const selectedDbIcon = computed(() => iconTypeMap[selectedType.value] || selectedProfile().icon || selectedType.value); const isJdbcConnection = computed(() => form.value.db_type === "jdbc"); + +const connectionUrlPlaceholder = computed(() => getUrlPlaceholder(form.value.db_type)); const canUseSsh = computed(() => form.value.db_type !== "sqlite" && form.value.db_type !== "jdbc"); const canUseProxy = computed( () => form.value.db_type !== "sqlite" && form.value.db_type !== "duckdb" && form.value.db_type !== "jdbc", @@ -817,7 +820,7 @@ function openExternalUrl(url: string) { diff --git a/src/lib/connectionPresentation.ts b/src/lib/connectionPresentation.ts index e16db1168b..b578f2babc 100644 --- a/src/lib/connectionPresentation.ts +++ b/src/lib/connectionPresentation.ts @@ -1,4 +1,4 @@ -import type { ConnectionConfig } from "@/types/database"; +import type { ConnectionConfig, DatabaseType } from "@/types/database"; type ConnectionPresentationConfig = Pick< ConnectionConfig, @@ -24,6 +24,53 @@ export function connectionEndpointLabel(connection?: ConnectionPresentationConfi return connection.host || connection.database || ""; } +export function connectionUrlPlaceholder(dbType: DatabaseType): string { + switch (dbType) { + case "mysql": + case "doris": + case "starrocks": + return "mysql://user:password@host:port/database"; + + case "postgres": + case "gaussdb": + case "redshift": + return "postgresql://user:password@host:port/database"; + + case "redis": + return "redis://:password@host:port/0"; + + case "sqlite": + return "sqlite:///absolute/path/to/database.db"; + + case "duckdb": + return "duckdb:///absolute/path/to/database.duckdb"; + + case "mongodb": + return "mongodb://user:password@host:port/database"; + + case "clickhouse": + return "clickhouse://user:password@host:port/database"; + + case "sqlserver": + return "mssql://user:password@host:port/database"; + + case "oracle": + return "oracle://user:password@host:port/service_name"; + + case "elasticsearch": + return "http://user:password@host:port"; + + case "dameng": + return "dm://user:password@host:port"; + + case "jdbc": + return "jdbc:mysql://host:3306/database"; + + default: + return "postgresql://user:password@host:port/database"; + } +} + export function connectionOptionSubtitle(connection?: ConnectionPresentationConfig): string { return [connectionDriverLabel(connection), connectionEndpointLabel(connection)].filter(Boolean).join(" · "); } diff --git a/tests/connectionUrlPlaceholder.test.ts b/tests/connectionUrlPlaceholder.test.ts new file mode 100644 index 0000000000..61942f2fb5 --- /dev/null +++ b/tests/connectionUrlPlaceholder.test.ts @@ -0,0 +1,51 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { connectionUrlPlaceholder } from "../src/lib/connectionPresentation.ts"; + +const expected: Record = { + mysql: "mysql://user:password@host:port/database", + doris: "mysql://user:password@host:port/database", + starrocks: "mysql://user:password@host:port/database", + postgres: "postgresql://user:password@host:port/database", + gaussdb: "postgresql://user:password@host:port/database", + redshift: "postgresql://user:password@host:port/database", + redis: "redis://:password@host:port/0", + sqlite: "sqlite:///absolute/path/to/database.db", + duckdb: "duckdb:///absolute/path/to/database.duckdb", + mongodb: "mongodb://user:password@host:port/database", + clickhouse: "clickhouse://user:password@host:port/database", + sqlserver: "mssql://user:password@host:port/database", + oracle: "oracle://user:password@host:port/service_name", + elasticsearch: "http://user:password@host:port", + dameng: "dm://user:password@host:port", + jdbc: "jdbc:mysql://host:3306/database", +}; + +for (const [dbType, placeholder] of Object.entries(expected)) { + test(`returns correct placeholder for ${dbType}`, () => { + assert.equal(connectionUrlPlaceholder(dbType as any), placeholder); + }); +} + +test("falls back to postgresql placeholder for unknown type", () => { + assert.equal(connectionUrlPlaceholder("unknown" as any), "postgresql://user:password@host:port/database"); +}); + +test("mysql-family types share the same placeholder", () => { + const mysql = connectionUrlPlaceholder("mysql"); + assert.equal(connectionUrlPlaceholder("doris"), mysql); + assert.equal(connectionUrlPlaceholder("starrocks"), mysql); +}); + +test("postgres-family types share the same placeholder", () => { + const postgres = connectionUrlPlaceholder("postgres"); + assert.equal(connectionUrlPlaceholder("gaussdb"), postgres); + assert.equal(connectionUrlPlaceholder("redshift"), postgres); +}); + +test("each placeholder starts with the expected scheme", () => { + for (const [dbType, placeholder] of Object.entries(expected)) { + const scheme = placeholder.split("://")[0]; + assert.ok(scheme.length > 0, `${dbType} placeholder should have a scheme`); + } +});