mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-28 16:09:17 +00:00
feat(connection): add compatible driver profiles
This commit is contained in:
+16
-5
@@ -78,7 +78,7 @@ const activeTabContext = computed(() => {
|
||||
|
||||
const items = [
|
||||
connection.name,
|
||||
connection.db_type.toUpperCase(),
|
||||
connectionDriverLabel(connection),
|
||||
];
|
||||
|
||||
if (tab.tableMeta?.tableName) {
|
||||
@@ -100,7 +100,7 @@ const activeConnectionValue = computed(() => activeConnection.value?.id || "");
|
||||
const connectionStats = computed(() => {
|
||||
const total = connectionStore.connections.length;
|
||||
const connected = connectionStore.connectedIds.size;
|
||||
const types = new Set(connectionStore.connections.map((connection) => connection.db_type)).size;
|
||||
const types = new Set(connectionStore.connections.map((connection) => connection.driver_profile || connection.db_type)).size;
|
||||
return { total, connected, types };
|
||||
});
|
||||
|
||||
@@ -110,6 +110,14 @@ function connectionDisplayName(connectionId: string): string {
|
||||
return connectionStore.getConfig(connectionId)?.name || connectionId;
|
||||
}
|
||||
|
||||
function connectionDriverLabel(connection?: ConnectionConfig): string {
|
||||
return connection?.driver_label || connection?.db_type.toUpperCase() || "";
|
||||
}
|
||||
|
||||
function connectionIconType(connection?: ConnectionConfig): string {
|
||||
return connection?.driver_profile || connection?.db_type || "postgres";
|
||||
}
|
||||
|
||||
function connectionColor(connectionId: string): string {
|
||||
return connectionStore.getConfig(connectionId)?.color || "";
|
||||
}
|
||||
@@ -424,6 +432,9 @@ async function setupFileDrop() {
|
||||
id: crypto.randomUUID(),
|
||||
name,
|
||||
db_type: dbType,
|
||||
driver_profile: dbType,
|
||||
driver_label: dbType === "duckdb" ? "DuckDB" : "SQLite",
|
||||
url_params: "",
|
||||
host: path,
|
||||
port: 0,
|
||||
username: "",
|
||||
@@ -599,7 +610,7 @@ async function setupFileDrop() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<span class="text-muted-foreground/50">/</span>
|
||||
<span class="shrink-0">{{ activeConnection?.db_type.toUpperCase() }}</span>
|
||||
<span class="shrink-0">{{ connectionDriverLabel(activeConnection) }}</span>
|
||||
<span class="text-muted-foreground/50">/</span>
|
||||
<Select
|
||||
:model-value="activeDatabaseValue"
|
||||
@@ -748,12 +759,12 @@ async function setupFileDrop() {
|
||||
class="flex w-full items-center gap-3 px-4 py-3 text-left hover:bg-muted/40"
|
||||
@click="openConnectionQuery(connection.id)"
|
||||
>
|
||||
<DatabaseIcon :db-type="connection.db_type" class="h-4 w-4" />
|
||||
<DatabaseIcon :db-type="connectionIconType(connection)" class="h-4 w-4" />
|
||||
<span v-if="connection.color" class="h-5 w-1 rounded-full shrink-0" :style="{ backgroundColor: connection.color }" />
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="truncate text-sm font-medium">{{ connection.name }}</div>
|
||||
<div class="truncate text-xs text-muted-foreground">
|
||||
{{ connection.db_type.toUpperCase() }} · {{ connection.host || connection.database || 'local' }}{{ connection.port ? ':' + connection.port : '' }}
|
||||
{{ connectionDriverLabel(connection) }} · {{ connection.host || connection.database || 'local' }}{{ connection.port ? ':' + connection.port : '' }}
|
||||
</div>
|
||||
</div>
|
||||
<FilePlus2 class="h-4 w-4 text-muted-foreground" />
|
||||
|
||||
@@ -31,6 +31,9 @@ const editingId = ref<string | null>(null);
|
||||
const defaultForm = (): Omit<ConnectionConfig, "id"> => ({
|
||||
name: "",
|
||||
db_type: "mysql",
|
||||
driver_profile: "mysql",
|
||||
driver_label: "MySQL",
|
||||
url_params: "",
|
||||
host: "127.0.0.1",
|
||||
port: 3306,
|
||||
username: "root",
|
||||
@@ -48,6 +51,7 @@ const defaultForm = (): Omit<ConnectionConfig, "id"> => ({
|
||||
|
||||
const form = ref(defaultForm());
|
||||
const selectedType = ref("mysql");
|
||||
const customDriverName = ref("");
|
||||
|
||||
const colorOptions = [
|
||||
{ value: "", class: "bg-transparent border-dashed", labelKey: "connection.colorNone" },
|
||||
@@ -59,12 +63,69 @@ const colorOptions = [
|
||||
{ value: "#a855f7", class: "bg-purple-500", labelKey: "connection.colorPurple" },
|
||||
];
|
||||
|
||||
const driverProfiles: Record<string, { type: DatabaseType; port: number; user: string; label: string; icon: string; urlParams?: string }> = {
|
||||
mysql: { type: "mysql", port: 3306, user: "root", label: "MySQL", icon: "mysql", urlParams: "" },
|
||||
postgres: { type: "postgres", port: 5432, user: "postgres", label: "PostgreSQL", icon: "postgres", urlParams: "" },
|
||||
redis: { type: "redis", port: 6379, user: "", label: "Redis", icon: "redis" },
|
||||
sqlite: { type: "sqlite", port: 0, user: "", label: "SQLite", icon: "sqlite" },
|
||||
duckdb: { type: "duckdb", port: 0, user: "", label: "DuckDB", icon: "duckdb" },
|
||||
mongodb: { type: "mongodb", port: 27017, user: "", label: "MongoDB", icon: "mongodb" },
|
||||
clickhouse: { type: "clickhouse", port: 8123, user: "default", label: "ClickHouse", icon: "clickhouse" },
|
||||
sqlserver: { type: "sqlserver", port: 1433, user: "sa", label: "SQL Server", icon: "sqlserver" },
|
||||
oracle: { type: "oracle", port: 1521, user: "system", label: "Oracle", icon: "oracle" },
|
||||
mariadb: { type: "mysql", port: 3306, user: "root", label: "MariaDB", icon: "mariadb" },
|
||||
tidb: { type: "mysql", port: 4000, user: "root", label: "TiDB", icon: "tidb" },
|
||||
oceanbase: { type: "mysql", port: 2881, user: "root", label: "OceanBase", icon: "oceanbase" },
|
||||
goldendb: { type: "mysql", port: 3306, user: "root", label: "GoldenDB", icon: "goldendb" },
|
||||
opengauss: { type: "postgres", port: 5432, user: "gaussdb", label: "openGauss", icon: "opengauss", urlParams: "sslmode=disable" },
|
||||
gaussdb: { type: "postgres", port: 5432, user: "gaussdb", label: "GaussDB", icon: "gaussdb" },
|
||||
kingbase: { type: "postgres", port: 54321, user: "system", label: "KingBase", icon: "kingbase" },
|
||||
vastbase: { type: "postgres", port: 5432, user: "vastbase", label: "Vastbase", icon: "vastbase" },
|
||||
custom_mysql: { type: "mysql", port: 3306, user: "root", label: "Custom MySQL-compatible", icon: "mysql", urlParams: "" },
|
||||
custom_postgres: { type: "postgres", port: 5432, user: "postgres", label: "Custom PostgreSQL-compatible", icon: "postgres", urlParams: "" },
|
||||
};
|
||||
|
||||
function profileForConfig(config: ConnectionConfig) {
|
||||
if (config.driver_profile && driverProfiles[config.driver_profile]) return config.driver_profile;
|
||||
return config.db_type;
|
||||
}
|
||||
|
||||
function selectedProfile() {
|
||||
return driverProfiles[selectedType.value] ?? driverProfiles.mysql;
|
||||
}
|
||||
|
||||
function isCustomCompatibleProfile() {
|
||||
return selectedType.value === "custom_mysql" || selectedType.value === "custom_postgres";
|
||||
}
|
||||
|
||||
function applyProfile(val: string, preserveConnectionFields = false) {
|
||||
const profile = driverProfiles[val];
|
||||
if (!profile) return;
|
||||
|
||||
selectedType.value = val;
|
||||
form.value.db_type = profile.type;
|
||||
form.value.driver_profile = val;
|
||||
form.value.driver_label = isCustomCompatibleProfile()
|
||||
? (customDriverName.value.trim() || profile.label)
|
||||
: profile.label;
|
||||
|
||||
if (!preserveConnectionFields) {
|
||||
form.value.port = profile.port;
|
||||
form.value.username = profile.user;
|
||||
form.value.url_params = profile.urlParams || "";
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.editConfig, (config) => {
|
||||
if (config) {
|
||||
const profile = profileForConfig(config);
|
||||
editingId.value = config.id;
|
||||
form.value = {
|
||||
name: config.name,
|
||||
db_type: config.db_type,
|
||||
driver_profile: profile,
|
||||
driver_label: config.driver_label || driverProfiles[profile]?.label || config.db_type,
|
||||
url_params: config.url_params || "",
|
||||
host: config.host,
|
||||
port: config.port,
|
||||
username: config.username,
|
||||
@@ -79,11 +140,13 @@ watch(() => props.editConfig, (config) => {
|
||||
ssh_key_path: config.ssh_key_path || "",
|
||||
ssl: config.ssl || false,
|
||||
};
|
||||
selectedType.value = config.db_type;
|
||||
selectedType.value = profile;
|
||||
customDriverName.value = isCustomCompatibleProfile() ? (config.driver_label || "") : "";
|
||||
} else {
|
||||
editingId.value = null;
|
||||
form.value = defaultForm();
|
||||
selectedType.value = "mysql";
|
||||
customDriverName.value = "";
|
||||
}
|
||||
testResult.value = null;
|
||||
});
|
||||
@@ -92,42 +155,17 @@ const isEditing = ref(false);
|
||||
watch(() => editingId.value, (v) => { isEditing.value = !!v; });
|
||||
|
||||
function onDbTypeChange(val: string) {
|
||||
const aliasMap: Record<string, { type: DatabaseType; port: number; user: string }> = {
|
||||
mysql: { type: "mysql", port: 3306, user: "root" },
|
||||
postgres: { type: "postgres", port: 5432, user: "postgres" },
|
||||
redis: { type: "redis", port: 6379, user: "" },
|
||||
sqlite: { type: "sqlite", port: 0, user: "" },
|
||||
duckdb: { type: "duckdb", port: 0, user: "" },
|
||||
mongodb: { type: "mongodb", port: 27017, user: "" },
|
||||
clickhouse: { type: "clickhouse", port: 8123, user: "default" },
|
||||
sqlserver: { type: "sqlserver", port: 1433, user: "sa" },
|
||||
oracle: { type: "oracle", port: 1521, user: "system" },
|
||||
tidb: { type: "mysql", port: 4000, user: "root" },
|
||||
oceanbase: { type: "mysql", port: 2881, user: "root" },
|
||||
goldendb: { type: "mysql", port: 3306, user: "root" },
|
||||
mariadb: { type: "mysql", port: 3306, user: "root" },
|
||||
opengauss: { type: "postgres", port: 5432, user: "gaussdb" },
|
||||
gaussdb: { type: "postgres", port: 5432, user: "gaussdb" },
|
||||
kingbase: { type: "postgres", port: 54321, user: "system" },
|
||||
vastbase: { type: "postgres", port: 5432, user: "vastbase" },
|
||||
};
|
||||
const alias = aliasMap[val];
|
||||
if (alias) {
|
||||
selectedType.value = val;
|
||||
form.value.db_type = alias.type;
|
||||
if (!editingId.value) {
|
||||
form.value.port = alias.port;
|
||||
form.value.username = alias.user;
|
||||
}
|
||||
}
|
||||
customDriverName.value = "";
|
||||
applyProfile(val, !!editingId.value);
|
||||
}
|
||||
|
||||
const iconTypeMap: Record<string, string> = {
|
||||
mysql: "mysql", postgres: "postgres", sqlite: "sqlite", redis: "redis",
|
||||
mongodb: "mongodb", duckdb: "duckdb", clickhouse: "clickhouse", sqlserver: "sqlserver",
|
||||
oracle: "oracle",
|
||||
mariadb: "mariadb", tidb: "tidb", oceanbase: "mysql", goldendb: "mysql",
|
||||
opengauss: "postgres", gaussdb: "postgres", kingbase: "postgres", vastbase: "postgres",
|
||||
mariadb: "mariadb", tidb: "tidb", oceanbase: "oceanbase", goldendb: "goldendb",
|
||||
opengauss: "opengauss", gaussdb: "gaussdb", kingbase: "kingbase", vastbase: "vastbase",
|
||||
custom_mysql: "mysql", custom_postgres: "postgres",
|
||||
};
|
||||
|
||||
const dbOptions = [
|
||||
@@ -147,6 +185,7 @@ const mysqlCompat = [
|
||||
{ value: "tidb", label: "TiDB" },
|
||||
{ value: "oceanbase", label: "OceanBase" },
|
||||
{ value: "goldendb", label: "GoldenDB" },
|
||||
{ value: "custom_mysql", label: "Custom MySQL-compatible" },
|
||||
];
|
||||
|
||||
const pgCompat = [
|
||||
@@ -154,8 +193,15 @@ const pgCompat = [
|
||||
{ value: "gaussdb", label: "GaussDB" },
|
||||
{ value: "kingbase", label: "KingBase" },
|
||||
{ value: "vastbase", label: "Vastbase" },
|
||||
{ value: "custom_postgres", label: "Custom PostgreSQL-compatible" },
|
||||
];
|
||||
|
||||
watch(customDriverName, (value) => {
|
||||
if (isCustomCompatibleProfile()) {
|
||||
form.value.driver_label = value.trim() || selectedProfile().label;
|
||||
}
|
||||
});
|
||||
|
||||
async function testConnection() {
|
||||
isTesting.value = true;
|
||||
testResult.value = null;
|
||||
@@ -257,6 +303,11 @@ watch([() => editingId.value, () => open.value], () => {
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div v-if="isCustomCompatibleProfile()" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right">{{ t('connection.driverName') }}</Label>
|
||||
<Input v-model="customDriverName" class="col-span-3" :placeholder="t('connection.driverNamePlaceholder')" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right">{{ t('connection.color') }}</Label>
|
||||
<div class="col-span-3 flex items-center gap-1.5">
|
||||
@@ -326,6 +377,11 @@ watch([() => editingId.value, () => open.value], () => {
|
||||
<Label class="text-right">{{ t('connection.database') }}</Label>
|
||||
<Input v-model="form.database" class="col-span-3" :placeholder="t('connection.databasePlaceholder')" />
|
||||
</div>
|
||||
|
||||
<div v-if="form.db_type === 'mysql' || form.db_type === 'postgres'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right">{{ t('connection.urlParams') }}</Label>
|
||||
<Input v-model="form.url_params" class="col-span-3" :placeholder="form.db_type === 'postgres' ? 'sslmode=disable' : 'charset=utf8mb4'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- SSH Tunnel (not for SQLite) -->
|
||||
|
||||
@@ -223,6 +223,11 @@ const isConnected = computed(() =>
|
||||
props.node.type === "connection" && !!props.node.connectionId && connectionStore.connectedIds.has(props.node.connectionId)
|
||||
);
|
||||
|
||||
function connectionIconType(connectionId?: string) {
|
||||
const config = connectionId ? connectionStore.getConfig(connectionId) : undefined;
|
||||
return config?.driver_profile || config?.db_type || "postgres";
|
||||
}
|
||||
|
||||
const connectionColor = computed(() => {
|
||||
const connectionId = props.node.connectionId;
|
||||
return connectionId ? connectionStore.getConfig(connectionId)?.color || "" : "";
|
||||
@@ -272,7 +277,7 @@ async function showMore() {
|
||||
<ChevronRight v-else class="w-3.5 h-3.5 shrink-0 text-muted-foreground" />
|
||||
</template>
|
||||
<span v-else class="w-3.5 h-3.5 shrink-0" />
|
||||
<DatabaseIcon v-if="node.type === 'connection'" :db-type="connectionStore.getConfig(node.connectionId || '')?.db_type || 'postgres'" class="w-3.5 h-3.5 shrink-0" />
|
||||
<DatabaseIcon v-if="node.type === 'connection'" :db-type="connectionIconType(node.connectionId)" class="w-3.5 h-3.5 shrink-0" />
|
||||
<component v-else :is="getIconInfo(node)?.icon || Database" class="w-3.5 h-3.5 shrink-0" :class="getIconInfo(node)?.colorClass" />
|
||||
<span v-if="node.type === 'connection' && connectionColor" class="h-3 w-1.5 rounded-full shrink-0" :style="{ backgroundColor: connectionColor }" />
|
||||
<span class="truncate">{{ isGroupLabel(node) ? t(node.label) : node.label }}</span>
|
||||
|
||||
@@ -27,6 +27,9 @@ export default {
|
||||
password: "Password",
|
||||
database: "Database",
|
||||
databasePlaceholder: "Optional",
|
||||
driverName: "Driver Name",
|
||||
driverNamePlaceholder: "Vendor or environment name",
|
||||
urlParams: "URL Params",
|
||||
test: "Test",
|
||||
testing: "Testing...",
|
||||
saveAndConnect: "Save & Connect",
|
||||
|
||||
@@ -27,6 +27,9 @@ export default {
|
||||
password: "密码",
|
||||
database: "数据库",
|
||||
databasePlaceholder: "可选",
|
||||
driverName: "驱动名称",
|
||||
driverNamePlaceholder: "厂商或环境名称",
|
||||
urlParams: "URL 参数",
|
||||
test: "测试",
|
||||
testing: "测试中...",
|
||||
saveAndConnect: "保存并连接",
|
||||
|
||||
@@ -22,8 +22,28 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
return connections.value.find((c) => c.id === connectionId);
|
||||
}
|
||||
|
||||
function normalizeConnection(config: ConnectionConfig): ConnectionConfig {
|
||||
const labelMap: Record<string, string> = {
|
||||
mysql: "MySQL",
|
||||
postgres: "PostgreSQL",
|
||||
sqlite: "SQLite",
|
||||
redis: "Redis",
|
||||
duckdb: "DuckDB",
|
||||
clickhouse: "ClickHouse",
|
||||
sqlserver: "SQL Server",
|
||||
mongodb: "MongoDB",
|
||||
oracle: "Oracle",
|
||||
};
|
||||
return {
|
||||
...config,
|
||||
driver_profile: config.driver_profile || config.db_type,
|
||||
driver_label: config.driver_label || labelMap[config.driver_profile || config.db_type] || config.db_type,
|
||||
url_params: config.url_params || "",
|
||||
};
|
||||
}
|
||||
|
||||
function addConnection(config: ConnectionConfig) {
|
||||
connections.value.push(config);
|
||||
connections.value.push(normalizeConnection(config));
|
||||
persistConnections();
|
||||
}
|
||||
|
||||
@@ -37,6 +57,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
|
||||
function updateConnection(config: ConnectionConfig) {
|
||||
config = normalizeConnection(config);
|
||||
const idx = connections.value.findIndex((c) => c.id === config.id);
|
||||
if (idx >= 0) connections.value[idx] = config;
|
||||
const node = findNode(treeNodes.value, config.id);
|
||||
@@ -50,6 +71,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
}
|
||||
|
||||
async function connect(config: ConnectionConfig) {
|
||||
config = normalizeConnection(config);
|
||||
const id = await api.connectDb(config);
|
||||
activeConnectionId.value = id;
|
||||
connectedIds.value.add(id);
|
||||
@@ -439,12 +461,13 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
for (const config of imported) {
|
||||
if (!connections.value.find((c) => c.id === config.id)) {
|
||||
config.id = crypto.randomUUID();
|
||||
addConnection(config);
|
||||
const normalized = normalizeConnection(config);
|
||||
addConnection(normalized);
|
||||
treeNodes.value.push({
|
||||
id: config.id,
|
||||
label: config.name,
|
||||
id: normalized.id,
|
||||
label: normalized.name,
|
||||
type: "connection" as const,
|
||||
connectionId: config.id,
|
||||
connectionId: normalized.id,
|
||||
isExpanded: false,
|
||||
children: [],
|
||||
});
|
||||
@@ -454,7 +477,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
|
||||
async function initFromDisk() {
|
||||
const saved = await api.loadConnections();
|
||||
connections.value = saved;
|
||||
connections.value = saved.map(normalizeConnection);
|
||||
treeNodes.value = saved.map((config) => ({
|
||||
id: config.id,
|
||||
label: config.name,
|
||||
|
||||
@@ -4,6 +4,9 @@ export interface ConnectionConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
db_type: DatabaseType;
|
||||
driver_profile?: string;
|
||||
driver_label?: string;
|
||||
url_params?: string;
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
|
||||
Reference in New Issue
Block a user