fix(ui): localize hardcoded strings
This commit is contained in:
parent
8e04889745
commit
ed1db7db6b
|
|
@ -66,8 +66,8 @@ const installedJres = computed(() => {
|
|||
const progressText = computed(() => {
|
||||
const p = progress.value;
|
||||
if (!p) return "";
|
||||
if (p.step === "jre-extract") return "解压 JRE...";
|
||||
const label = p.step === "jre" ? "下载 JRE" : "下载驱动";
|
||||
if (p.step === "jre-extract") return t("driverStore.progressJreExtract");
|
||||
const label = p.step === "jre" ? t("driverStore.progressDownloadJre") : t("driverStore.progressDownloadDriver");
|
||||
if (!p.total) return `${label}...`;
|
||||
const pct = Math.round(((p.downloaded ?? 0) / p.total) * 100);
|
||||
const dl = formatSize(p.downloaded ?? 0);
|
||||
|
|
@ -86,11 +86,11 @@ const usageSummary = computed(() => {
|
|||
const usage = driverStoreUsage.value;
|
||||
if (!usage) return [];
|
||||
return [
|
||||
{ key: "total", label: "总计", bytes: usage.total_bytes },
|
||||
{ key: "jre", label: "托管 JRE", bytes: usage.jre_bytes },
|
||||
{ key: "agent", label: "内置驱动 Agent", bytes: usage.agent_driver_bytes },
|
||||
{ key: "jdbc-plugin", label: "JDBC 插件", bytes: usage.jdbc_plugin_bytes },
|
||||
{ key: "jdbc-driver", label: "JDBC 驱动 JAR", bytes: usage.jdbc_driver_bytes },
|
||||
{ key: "total", label: t("driverStore.usageTotalLabel"), bytes: usage.total_bytes },
|
||||
{ key: "jre", label: t("driverStore.usageManagedJre"), bytes: usage.jre_bytes },
|
||||
{ key: "agent", label: t("driverStore.usageAgentDrivers"), bytes: usage.agent_driver_bytes },
|
||||
{ key: "jdbc-plugin", label: t("driverStore.usageJdbcPlugin"), bytes: usage.jdbc_plugin_bytes },
|
||||
{ key: "jdbc-driver", label: t("driverStore.usageJdbcDriverJars"), bytes: usage.jdbc_driver_bytes },
|
||||
];
|
||||
});
|
||||
const jreUsageByKey = computed(() => {
|
||||
|
|
@ -178,9 +178,9 @@ async function saveJavaRuntimeConfig() {
|
|||
});
|
||||
javaRuntimeConfig.value = config;
|
||||
customJavaPath.value = config.custom_java_path ?? "";
|
||||
toast("Java 运行时设置已保存");
|
||||
toast(t("driverStore.javaRuntimeSaved"));
|
||||
} catch (e: any) {
|
||||
toast(`Java 运行时设置失败: ${e}`);
|
||||
toast(t("driverStore.javaRuntimeSaveFailed", { error: e }));
|
||||
} finally {
|
||||
savingJavaRuntime.value = false;
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ async function chooseCustomJavaPath() {
|
|||
if (isWeb) return;
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
const selected = await open({
|
||||
title: "选择 Java 可执行文件",
|
||||
title: t("driverStore.chooseJavaExecutable"),
|
||||
multiple: false,
|
||||
});
|
||||
if (typeof selected === "string") {
|
||||
|
|
@ -214,9 +214,9 @@ async function runDriverInstall(dbType: string) {
|
|||
try {
|
||||
await api.installAgent(dbType);
|
||||
await refreshAgents();
|
||||
toast(`${label} 驱动安装成功`);
|
||||
toast(t("driverStore.driverInstallSuccess", { label }));
|
||||
} catch (e: any) {
|
||||
toast(`${label} 驱动安装失败: ${e}`);
|
||||
toast(t("driverStore.driverInstallFailed", { label, error: e }));
|
||||
} finally {
|
||||
installing.value = null;
|
||||
progress.value = null;
|
||||
|
|
@ -241,9 +241,9 @@ async function upgradeAll() {
|
|||
try {
|
||||
const count = await api.upgradeAllAgents();
|
||||
await refreshAgents();
|
||||
toast(`${count} 个驱动升级完成`);
|
||||
toast(t("driverStore.upgradeAllSuccess", { count }));
|
||||
} catch (e: any) {
|
||||
toast(`批量升级失败: ${e}`);
|
||||
toast(t("driverStore.upgradeAllFailed", { error: e }));
|
||||
} finally {
|
||||
upgradingAll.value = false;
|
||||
upgradingCurrent.value = "";
|
||||
|
|
@ -258,9 +258,9 @@ async function uninstallDriver(dbType: string) {
|
|||
try {
|
||||
await api.uninstallAgent(dbType);
|
||||
await refreshAgents();
|
||||
toast(`${label} 驱动已卸载`);
|
||||
toast(t("driverStore.driverUninstallSuccess", { label }));
|
||||
} catch (e: any) {
|
||||
toast(`${label} 驱动卸载失败: ${e}`);
|
||||
toast(t("driverStore.driverUninstallFailed", { label, error: e }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ async function importOfflineZip() {
|
|||
} else {
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
const path = await open({
|
||||
title: "选择离线驱动包",
|
||||
title: t("driverStore.chooseOfflineDriverPackage"),
|
||||
multiple: false,
|
||||
filters: [{ name: "ZIP", extensions: ["zip"] }],
|
||||
});
|
||||
|
|
@ -324,9 +324,9 @@ async function importOfflineZip() {
|
|||
try {
|
||||
const count = await api.importAgentsFromZip(selected);
|
||||
await refreshAgents();
|
||||
toast(`离线导入完成,已安装 ${count} 个驱动`);
|
||||
toast(t("driverStore.offlineImportSuccess", { count }));
|
||||
} catch (e: any) {
|
||||
toast(`离线导入失败: ${e}`);
|
||||
toast(t("driverStore.offlineImportFailed", { error: e }));
|
||||
} finally {
|
||||
importingZip.value = false;
|
||||
progress.value = null;
|
||||
|
|
@ -341,15 +341,15 @@ async function importDriverJar(dbType: string) {
|
|||
try {
|
||||
await api.importAgentJar(dbType, file);
|
||||
await refreshAgents();
|
||||
toast(`${label} 驱动导入成功`);
|
||||
toast(t("driverStore.driverImportSuccess", { label }));
|
||||
} catch (e: any) {
|
||||
toast(`${label} 驱动导入失败: ${e}`);
|
||||
toast(t("driverStore.driverImportFailed", { label, error: e }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
const selected = await open({
|
||||
title: "选择驱动 JAR 文件",
|
||||
title: t("driverStore.chooseDriverJar"),
|
||||
multiple: false,
|
||||
filters: [{ name: "JAR", extensions: ["jar"] }],
|
||||
});
|
||||
|
|
@ -357,9 +357,9 @@ async function importDriverJar(dbType: string) {
|
|||
try {
|
||||
await api.importAgentJar(dbType, selected);
|
||||
await refreshAgents();
|
||||
toast(`${label} 驱动导入成功`);
|
||||
toast(t("driverStore.driverImportSuccess", { label }));
|
||||
} catch (e: any) {
|
||||
toast(`${label} 驱动导入失败: ${e}`);
|
||||
toast(t("driverStore.driverImportFailed", { label, error: e }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -369,9 +369,9 @@ async function reinstallJre(jreKey: string) {
|
|||
try {
|
||||
await api.reinstallJre(jreKey);
|
||||
await refreshAgents();
|
||||
toast(`JRE ${jreKey} 重新安装成功`);
|
||||
toast(t("driverStore.jreReinstallSuccess", { jre: jreKey }));
|
||||
} catch (e: any) {
|
||||
toast(`JRE ${jreKey} 重新安装失败: ${e}`);
|
||||
toast(t("driverStore.jreReinstallFailed", { jre: jreKey, error: e }));
|
||||
} finally {
|
||||
reinstallingJre.value = null;
|
||||
progress.value = null;
|
||||
|
|
@ -382,7 +382,7 @@ async function uninstallJre(jreKey: string) {
|
|||
try {
|
||||
await api.uninstallJre(jreKey);
|
||||
await refreshAgents();
|
||||
toast(`JRE ${jreKey} 已卸载`);
|
||||
toast(t("driverStore.jreUninstallSuccess", { jre: jreKey }));
|
||||
} catch (e: any) {
|
||||
toast(String(e));
|
||||
}
|
||||
|
|
@ -466,7 +466,7 @@ async function installJdbcPluginLocal() {
|
|||
} else {
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
const result = await open({
|
||||
title: "选择 JDBC 插件 zip 文件",
|
||||
title: t("driverStore.chooseJdbcPluginZip"),
|
||||
multiple: false,
|
||||
filters: [{ name: "ZIP", extensions: ["zip"] }],
|
||||
});
|
||||
|
|
@ -597,9 +597,13 @@ onUnmounted(() => {
|
|||
<Tabs default-value="agent">
|
||||
<div class="mb-5 rounded-xl border bg-muted/20 p-4">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="text-sm font-medium">空间占用明细</div>
|
||||
<div class="text-sm font-medium">{{ t("driverStore.usageTitle") }}</div>
|
||||
<div class="text-xs text-muted-foreground">
|
||||
{{ usageSummary.length ? `总计 ${formatBytes(usageSummary[0].bytes)}` : "统计中..." }}
|
||||
{{
|
||||
usageSummary.length
|
||||
? t("driverStore.usageTotal", { size: formatBytes(usageSummary[0].bytes) })
|
||||
: t("driverStore.calculating")
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="usageSummary.length" class="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-5">
|
||||
|
|
@ -617,11 +621,11 @@ onUnmounted(() => {
|
|||
<div class="flex items-center justify-between">
|
||||
<TabsList class="w-fit">
|
||||
<TabsTrigger value="agent" class="gap-1.5 relative">
|
||||
内置驱动
|
||||
{{ t("driverStore.agentDrivers") }}
|
||||
<span v-if="agentTabUpdateCount > 0" class="inline-block h-2 w-2 rounded-full bg-red-500" />
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="jdbc" class="gap-1.5 relative">
|
||||
JDBC 驱动
|
||||
{{ t("driverStore.jdbcDrivers") }}
|
||||
<span v-if="jdbcTabUpdateCount > 0" class="inline-block h-2 w-2 rounded-full bg-red-500" />
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -634,7 +638,7 @@ onUnmounted(() => {
|
|||
@click="importOfflineZip"
|
||||
>
|
||||
<FileUp class="h-3.5 w-3.5" />
|
||||
{{ importingZip ? "导入中..." : "导入离线包" }}
|
||||
{{ importingZip ? t("driverStore.importing") : t("driverStore.importOfflinePackage") }}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
|
@ -644,7 +648,7 @@ onUnmounted(() => {
|
|||
@click="forceRefresh"
|
||||
>
|
||||
<RefreshCw class="h-3.5 w-3.5" :class="{ 'animate-spin': refreshing }" />
|
||||
刷新
|
||||
{{ t("driverStore.refresh") }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -655,15 +659,15 @@ onUnmounted(() => {
|
|||
<div class="rounded-xl border bg-muted/20 p-4 space-y-3">
|
||||
<div class="flex flex-wrap items-end gap-3">
|
||||
<div class="min-w-[220px] flex-1 space-y-1.5">
|
||||
<Label>Java 运行时</Label>
|
||||
<Label>{{ t("driverStore.javaRuntime") }}</Label>
|
||||
<Select :model-value="javaRuntimeConfig.mode" @update:model-value="setJavaRuntimeMode">
|
||||
<SelectTrigger class="h-8 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="managed">DBX 托管 JRE</SelectItem>
|
||||
<SelectItem value="system">系统 java</SelectItem>
|
||||
<SelectItem value="custom">自定义路径</SelectItem>
|
||||
<SelectItem value="managed">{{ t("driverStore.javaRuntimeManaged") }}</SelectItem>
|
||||
<SelectItem value="system">{{ t("driverStore.javaRuntimeSystem") }}</SelectItem>
|
||||
<SelectItem value="custom">{{ t("driverStore.javaRuntimeCustom") }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
|
@ -672,23 +676,23 @@ onUnmounted(() => {
|
|||
:disabled="savingJavaRuntime || (javaRuntimeConfig.mode === 'custom' && !customJavaPath.trim())"
|
||||
@click="saveJavaRuntimeConfig"
|
||||
>
|
||||
{{ savingJavaRuntime ? "保存中..." : "保存" }}
|
||||
{{ savingJavaRuntime ? t("driverStore.saving") : t("settings.save") }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-if="javaRuntimeConfig.mode === 'custom'" class="flex items-center gap-2">
|
||||
<Input
|
||||
v-model="customJavaPath"
|
||||
class="h-8 flex-1 text-xs"
|
||||
placeholder="/path/to/java 或 /path/to/jdk"
|
||||
:placeholder="t('driverStore.customJavaPathPlaceholder')"
|
||||
@keydown.enter.prevent="saveJavaRuntimeConfig"
|
||||
/>
|
||||
<Button variant="outline" class="h-8 shrink-0 rounded-full text-xs" @click="chooseCustomJavaPath">
|
||||
<FolderOpen class="h-3.5 w-3.5" />
|
||||
选择
|
||||
{{ t("driverStore.choose") }}
|
||||
</Button>
|
||||
</div>
|
||||
<p v-else-if="javaRuntimeConfig.mode === 'system'" class="text-xs text-muted-foreground">
|
||||
使用当前环境 PATH 中的 java。
|
||||
{{ t("driverStore.systemJavaHint") }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -696,7 +700,7 @@ onUnmounted(() => {
|
|||
<div v-if="installedJres.length > 0" class="rounded-xl border bg-muted/20 p-4 space-y-2.5">
|
||||
<div v-for="jre in installedJres" :key="jre.key" class="flex items-center justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-medium">JRE {{ jre.key }} 运行时</div>
|
||||
<div class="text-sm font-medium">{{ t("driverStore.jreRuntimeTitle", { jre: jre.key }) }}</div>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-3">
|
||||
<span
|
||||
|
|
@ -706,11 +710,11 @@ onUnmounted(() => {
|
|||
{{ jreUsageLabel(jre.key) }}
|
||||
</span>
|
||||
<Check v-if="jre.installed" class="h-4 w-4 text-green-600" />
|
||||
<span v-else class="text-xs text-muted-foreground">未安装</span>
|
||||
<span v-else class="text-xs text-muted-foreground">{{ t("driverStore.notInstalled") }}</span>
|
||||
<DriverInstallProgressCircle
|
||||
v-if="reinstallingJre === jre.key"
|
||||
:percent="progressNumber"
|
||||
:title="progressTitle(jre.installed ? '重装中' : '安装中')"
|
||||
:title="progressTitle(jre.installed ? t('driverStore.reinstalling') : t('driverStore.installing'))"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="!jre.installed"
|
||||
|
|
@ -722,7 +726,7 @@ onUnmounted(() => {
|
|||
@click="reinstallJre(jre.key)"
|
||||
>
|
||||
<Download class="h-3.5 w-3.5 mr-1" />
|
||||
安装
|
||||
{{ t("driverStore.install") }}
|
||||
</Button>
|
||||
<Button
|
||||
v-else-if="jre.installed"
|
||||
|
|
@ -734,7 +738,7 @@ onUnmounted(() => {
|
|||
@click="reinstallJre(jre.key)"
|
||||
>
|
||||
<RotateCcw class="h-3.5 w-3.5 mr-1" />
|
||||
重新安装
|
||||
{{ t("driverStore.reinstall") }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="jre.installed"
|
||||
|
|
@ -745,21 +749,25 @@ onUnmounted(() => {
|
|||
:disabled="reinstallingJre !== null || installing !== null"
|
||||
@click="uninstallJre(jre.key)"
|
||||
>
|
||||
卸载
|
||||
{{ t("driverStore.uninstall") }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="rounded-xl border bg-muted/20 p-4">
|
||||
<div class="text-sm font-medium">JRE 运行时</div>
|
||||
<p class="text-xs text-muted-foreground mt-0.5">首次安装驱动时自动下载</p>
|
||||
<div class="text-sm font-medium">{{ t("driverStore.jreRuntime") }}</div>
|
||||
<p class="text-xs text-muted-foreground mt-0.5">{{ t("driverStore.jreRuntimeAutoDownloadHint") }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Driver List -->
|
||||
<div v-if="drivers.length === 0" class="py-12 text-center text-sm text-muted-foreground">加载中...</div>
|
||||
<div v-if="drivers.length === 0" class="py-12 text-center text-sm text-muted-foreground">
|
||||
{{ t("common.loading") }}
|
||||
</div>
|
||||
<div v-else class="rounded-md border divide-y">
|
||||
<div v-if="updatableCount > 0" class="flex items-center justify-between px-4 py-2 bg-muted/30">
|
||||
<span class="text-xs text-muted-foreground">{{ updatableCount }} 个驱动可更新</span>
|
||||
<span class="text-xs text-muted-foreground">{{
|
||||
t("driverStore.driversUpdatable", { count: updatableCount })
|
||||
}}</span>
|
||||
<Button
|
||||
size="sm"
|
||||
class="h-7 rounded-full text-xs"
|
||||
|
|
@ -768,7 +776,11 @@ onUnmounted(() => {
|
|||
>
|
||||
<Loader2 v-if="upgradingAll" class="h-3 w-3 animate-spin mr-1" />
|
||||
<Download v-else class="h-3 w-3 mr-1" />
|
||||
{{ upgradingAll ? `升级中 (${upgradingIndex}/${upgradingTotal})` : "全部升级" }}
|
||||
{{
|
||||
upgradingAll
|
||||
? t("driverStore.upgradingProgress", { current: upgradingIndex, total: upgradingTotal })
|
||||
: t("driverStore.upgradeAll")
|
||||
}}
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -822,12 +834,12 @@ onUnmounted(() => {
|
|||
@click="removeQueuedDriverInstall(driver.db_type)"
|
||||
>
|
||||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
排队中
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle
|
||||
v-else-if="!driver.installed && isDriverProgressActive(driver.db_type)"
|
||||
:percent="progressNumber"
|
||||
:title="progressTitle('安装中')"
|
||||
:title="progressTitle(t('driverStore.installing'))"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="!driver.installed"
|
||||
|
|
@ -837,7 +849,7 @@ onUnmounted(() => {
|
|||
@click="installDriver(driver.db_type)"
|
||||
>
|
||||
<Download class="h-3 w-3 mr-1" />
|
||||
安装
|
||||
{{ t("driverStore.install") }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="
|
||||
|
|
@ -846,7 +858,7 @@ onUnmounted(() => {
|
|||
size="sm"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 rounded-full text-xs text-muted-foreground"
|
||||
title="导入本地 JAR"
|
||||
:title="t('driverStore.importLocalJar')"
|
||||
:disabled="upgradingAll || installing !== null"
|
||||
@click="importDriverJar(driver.db_type)"
|
||||
>
|
||||
|
|
@ -866,12 +878,12 @@ onUnmounted(() => {
|
|||
@click="removeQueuedDriverInstall(driver.db_type)"
|
||||
>
|
||||
<Clock3 class="h-3 w-3 mr-1" />
|
||||
排队中
|
||||
{{ t("driverStore.queued") }}
|
||||
</Button>
|
||||
<DriverInstallProgressCircle
|
||||
v-else-if="driver.update_available && isDriverProgressActive(driver.db_type)"
|
||||
:percent="progressNumber"
|
||||
:title="progressTitle('更新中')"
|
||||
:title="progressTitle(t('driverStore.updating'))"
|
||||
/>
|
||||
<Button
|
||||
v-else-if="driver.update_available"
|
||||
|
|
@ -881,7 +893,7 @@ onUnmounted(() => {
|
|||
:disabled="upgradingAll"
|
||||
@click="installDriver(driver.db_type)"
|
||||
>
|
||||
更新
|
||||
{{ t("driverStore.update") }}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
|
@ -890,7 +902,7 @@ onUnmounted(() => {
|
|||
:disabled="installing !== null || upgradingAll || isDriverQueued(driver.db_type)"
|
||||
@click="uninstallDriver(driver.db_type)"
|
||||
>
|
||||
卸载
|
||||
{{ t("driverStore.uninstall") }}
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
|
|
@ -967,7 +979,7 @@ onUnmounted(() => {
|
|||
@click="installJdbcPluginLocal"
|
||||
>
|
||||
<FolderOpen class="h-3.5 w-3.5 mr-1" />
|
||||
本地安装
|
||||
{{ t("driverStore.localInstall") }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1901,7 +1901,7 @@ function openExternalUrl(url: string) {
|
|||
</div>
|
||||
|
||||
<div v-if="form.db_type === 'oracle'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">连接方式</Label>
|
||||
<Label class="text-right text-xs">{{ t("connection.mode") }}</Label>
|
||||
<div
|
||||
class="col-span-3 grid h-8 grid-cols-2 overflow-hidden rounded-md border border-input bg-muted/30 p-0.5"
|
||||
>
|
||||
|
|
@ -1916,7 +1916,7 @@ function openExternalUrl(url: string) {
|
|||
:aria-pressed="form.oracle_connection_type !== 'sid'"
|
||||
@click="form.oracle_connection_type = 'service_name'"
|
||||
>
|
||||
服务名
|
||||
{{ t("connection.serviceNameOnly") }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -1937,16 +1937,17 @@ function openExternalUrl(url: string) {
|
|||
<div v-if="shouldShowAgentDriverInstallHint" class="grid grid-cols-4 items-center gap-4">
|
||||
<span />
|
||||
<p class="col-span-3 text-xs text-muted-foreground">
|
||||
需要在顶部导航栏「<a
|
||||
{{ t("connection.driverInstallHintPrefix")
|
||||
}}<a
|
||||
class="underline cursor-pointer text-primary hover:text-primary/80"
|
||||
@click="emit('openDriverStore')"
|
||||
>驱动管理</a
|
||||
>」中安装对应的驱动才能连接。
|
||||
>{{ t("toolbar.driverManager") }}</a
|
||||
>{{ t("connection.driverInstallHintSuffix") }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="form.db_type === 'oracle'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">版本</Label>
|
||||
<Label class="text-right text-xs">{{ t("connection.version") }}</Label>
|
||||
<Select
|
||||
:model-value="selectedType === 'oracle-10g' ? 'oracle-10g' : 'oracle'"
|
||||
@update:model-value="(val) => applyProfile(String(val), true)"
|
||||
|
|
|
|||
|
|
@ -1504,7 +1504,7 @@ watch(
|
|||
id="webdav-password"
|
||||
v-model="webdavPassword"
|
||||
type="password"
|
||||
:placeholder="webdavHasSavedPassword ? '••••••••' : '输入密码'"
|
||||
:placeholder="webdavHasSavedPassword ? '••••••••' : t('settings.syncPasswordPlaceholder')"
|
||||
:disabled="webdavHasSavedPassword"
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
|
|
@ -1513,7 +1513,7 @@ watch(
|
|||
variant="ghost"
|
||||
size="icon-xs"
|
||||
class="absolute right-1 top-1/2 -translate-y-1/2"
|
||||
title="清除已保存的密码"
|
||||
:title="t('settings.syncClearSavedPassword')"
|
||||
@click="
|
||||
webdavRememberPassword = false;
|
||||
forgetWebdavSavedPassword(currentWebDavAccountConfig());
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ const dataTabsMenuContainerClass = computed(() =>
|
|||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<div
|
||||
class="group flex items-center gap-1 px-2 text-xs cursor-pointer transition-colors whitespace-nowrap"
|
||||
class="group flex items-center gap-1 px-2 text-xs cursor-pointer transition-colors whitespace-nowrap select-none"
|
||||
:class="
|
||||
settingsStore.editorSettings.appLayout === 'classic'
|
||||
? [
|
||||
|
|
@ -289,11 +289,11 @@ const dataTabsMenuContainerClass = computed(() =>
|
|||
<span class="shrink-0 text-amber-600 dark:text-amber-400">
|
||||
<Package class="h-3.5 w-3.5" />
|
||||
</span>
|
||||
<span class="min-w-0 truncate flex-1">驱动管理</span>
|
||||
<span class="min-w-0 truncate flex-1">{{ t("toolbar.driverManager") }}</span>
|
||||
<span
|
||||
v-if="(agentDriverUpdateCount ?? 0) > 0"
|
||||
class="inline-flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-medium leading-none text-white"
|
||||
aria-label="可更新驱动数量"
|
||||
:aria-label="t('toolbar.updatableDriverCount')"
|
||||
>
|
||||
{{ (agentDriverUpdateCount ?? 0) > 99 ? "99+" : agentDriverUpdateCount }}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -154,11 +154,11 @@ function onToolbarDblClick(e: MouseEvent) {
|
|||
@click="emit('open-driver-store')"
|
||||
>
|
||||
<Package class="h-3.5 w-3.5" />
|
||||
驱动管理
|
||||
{{ t("toolbar.driverManager") }}
|
||||
<span
|
||||
v-if="agentDriverUpdateCount > 0"
|
||||
class="ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-red-500 px-1 text-[10px] font-medium leading-none text-white"
|
||||
aria-label="可更新驱动数量"
|
||||
:aria-label="t('toolbar.updatableDriverCount')"
|
||||
>
|
||||
{{ agentDriverUpdateCount > 99 ? "99+" : agentDriverUpdateCount }}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -94,9 +94,9 @@ watch(
|
|||
@click="handleReleaseNotesClick"
|
||||
/>
|
||||
<p v-if="!isDesktop && updateInfo?.update_available" class="text-xs text-muted-foreground">
|
||||
Docker 用户请运行
|
||||
{{ t("updates.dockerUsersRun") }}
|
||||
<code class="bg-muted px-1 py-0.5 rounded text-[11px]">docker compose pull && docker compose up -d</code>
|
||||
更新
|
||||
{{ t("updates.toUpdate") }}
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
|
|
|
|||
|
|
@ -1806,6 +1806,7 @@ const showDropInside = computed(
|
|||
);
|
||||
const isDragging = computed(() => dragState.active && dragState.draggedId === props.node.id);
|
||||
const TABLE_REFERENCE_DRAG_THRESHOLD = 5;
|
||||
const TABLE_REFERENCE_DRAGGING_CLASS = "dbx-table-reference-dragging";
|
||||
const canDragTableReference = computed(
|
||||
() =>
|
||||
!props.dragDisabled &&
|
||||
|
|
@ -1837,16 +1838,16 @@ function tableReferenceDragPayload(): QueryEditorTableReferencePayload | null {
|
|||
function startTableReferenceDrag(payload: QueryEditorTableReferencePayload) {
|
||||
draggingTableReferencePayload = payload;
|
||||
setActiveTableReferencePayload(payload);
|
||||
document.getSelection()?.removeAllRanges();
|
||||
document.body.style.cursor = "copy";
|
||||
document.body.style.userSelect = "none";
|
||||
}
|
||||
|
||||
function finishTableReferenceDrag() {
|
||||
clearActiveTableReferencePayload(draggingTableReferencePayload);
|
||||
pendingTableReferenceDrag = null;
|
||||
draggingTableReferencePayload = null;
|
||||
document.body.classList.remove(TABLE_REFERENCE_DRAGGING_CLASS);
|
||||
document.body.style.cursor = "";
|
||||
document.body.style.userSelect = "";
|
||||
document.removeEventListener("mousemove", onTableReferenceMouseMove, true);
|
||||
document.removeEventListener("mouseup", onTableReferenceMouseUp, true);
|
||||
}
|
||||
|
|
@ -1861,6 +1862,7 @@ function onTableReferenceMouseMove(event: MouseEvent) {
|
|||
}
|
||||
if (draggingTableReferencePayload) {
|
||||
event.preventDefault();
|
||||
document.getSelection()?.removeAllRanges();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1886,6 +1888,9 @@ function startTableReferenceMouseDrag(event: MouseEvent) {
|
|||
if (event.button !== 0) return;
|
||||
const payload = tableReferenceDragPayload();
|
||||
if (!payload) return;
|
||||
event.preventDefault();
|
||||
document.getSelection()?.removeAllRanges();
|
||||
document.body.classList.add(TABLE_REFERENCE_DRAGGING_CLASS);
|
||||
pendingTableReferenceDrag = { payload, startX: event.clientX, startY: event.clientY };
|
||||
document.addEventListener("mousemove", onTableReferenceMouseMove, true);
|
||||
document.addEventListener("mouseup", onTableReferenceMouseUp, true);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||
data-slot="tabs-trigger"
|
||||
:class="
|
||||
cn(
|
||||
'gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg:not([class*=size-])]:size-4 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||
'gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg:not([class*=size-])]:size-4 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 relative inline-flex h-[calc(100%-1px)] flex-1 select-none items-center justify-center whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
||||
'group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent',
|
||||
'data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground',
|
||||
'after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export { default as TabsList } from "./TabsList.vue";
|
|||
export { default as TabsTrigger } from "./TabsTrigger.vue";
|
||||
|
||||
export const tabsListVariants = cva(
|
||||
"rounded-lg p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none group/tabs-list inline-flex w-fit items-center justify-center text-muted-foreground group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col",
|
||||
"rounded-lg p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none group/tabs-list inline-flex w-fit select-none items-center justify-center text-muted-foreground group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ export default {
|
|||
sqlSaved: "SQL saved",
|
||||
sqlOpenFailed: "Failed to open file: {message}",
|
||||
sqlSaveFailed: "Failed to save file: {message}",
|
||||
driverManager: "Driver Manager",
|
||||
updatableDriverCount: "Updatable driver count",
|
||||
},
|
||||
updates: {
|
||||
title: "Updates",
|
||||
|
|
@ -59,6 +61,8 @@ export default {
|
|||
restartFailed: "Failed to restart app: {error}",
|
||||
exitAndUpdate: "Exit & Update",
|
||||
reopenHint: "The app will relaunch to finish updating",
|
||||
dockerUsersRun: "Docker users should run",
|
||||
toUpdate: "to update",
|
||||
},
|
||||
sidebar: {
|
||||
connections: "CONNECTIONS",
|
||||
|
|
@ -115,6 +119,10 @@ export default {
|
|||
authMechanism: "Auth Mechanism",
|
||||
authMechanismDefault: "Default",
|
||||
serviceName: "Service/SID",
|
||||
serviceNameOnly: "Service Name",
|
||||
version: "Version",
|
||||
driverInstallHintPrefix: "Install the required driver from ",
|
||||
driverInstallHintSuffix: " in the top toolbar before connecting.",
|
||||
driverName: "Driver Name",
|
||||
driverNamePlaceholder: "Vendor or environment name",
|
||||
urlParams: "URL Params",
|
||||
|
|
@ -259,6 +267,8 @@ export default {
|
|||
editor: {
|
||||
pressToExecute: "Press {mod}+Enter to execute",
|
||||
pressToSaveSql: "Press {mod}+S to save SQL",
|
||||
queryTimeoutError: "Query timed out ({seconds}s). Check whether the database connection is healthy.",
|
||||
connectionMayBeLost: "The connection may have been lost. Refresh data and try again.",
|
||||
showResultsPane: "Show results",
|
||||
hideResultsPane: "Hide results",
|
||||
noDatabase: "No database selected",
|
||||
|
|
@ -1463,6 +1473,8 @@ export default {
|
|||
syncEndpoint: "WebDAV URL",
|
||||
syncUsername: "Username",
|
||||
syncPassword: "Password",
|
||||
syncPasswordPlaceholder: "Enter password",
|
||||
syncClearSavedPassword: "Clear saved password",
|
||||
syncRememberWebDavPassword: "Remember WebDAV app password",
|
||||
syncSavedPassword: "(saved)",
|
||||
syncRememberWebDavPasswordDescription:
|
||||
|
|
@ -1546,6 +1558,68 @@ export default {
|
|||
openSource: "Open-source repository",
|
||||
officialDocs: "Official docs",
|
||||
},
|
||||
driverStore: {
|
||||
progressJreExtract: "Extracting JRE...",
|
||||
progressDownloadJre: "Downloading JRE",
|
||||
progressDownloadDriver: "Downloading driver",
|
||||
usageTitle: "Storage usage",
|
||||
usageTotal: "Total {size}",
|
||||
usageTotalLabel: "Total",
|
||||
calculating: "Calculating...",
|
||||
usageManagedJre: "Managed JRE",
|
||||
usageAgentDrivers: "Built-in driver agents",
|
||||
usageJdbcPlugin: "JDBC plugin",
|
||||
usageJdbcDriverJars: "JDBC driver JARs",
|
||||
javaRuntimeSaved: "Java runtime settings saved",
|
||||
javaRuntimeSaveFailed: "Failed to save Java runtime settings: {error}",
|
||||
chooseJavaExecutable: "Choose Java executable",
|
||||
driverInstallSuccess: "{label} driver installed",
|
||||
driverInstallFailed: "Failed to install {label} driver: {error}",
|
||||
upgradeAllSuccess: "{count} driver(s) upgraded",
|
||||
upgradeAllFailed: "Batch upgrade failed: {error}",
|
||||
driverUninstallSuccess: "{label} driver uninstalled",
|
||||
driverUninstallFailed: "Failed to uninstall {label} driver: {error}",
|
||||
chooseOfflineDriverPackage: "Choose offline driver package",
|
||||
offlineImportSuccess: "Offline import complete. Installed {count} driver(s).",
|
||||
offlineImportFailed: "Offline import failed: {error}",
|
||||
driverImportSuccess: "{label} driver imported",
|
||||
driverImportFailed: "Failed to import {label} driver: {error}",
|
||||
chooseDriverJar: "Choose driver JAR file",
|
||||
jreReinstallSuccess: "JRE {jre} reinstalled",
|
||||
jreReinstallFailed: "Failed to reinstall JRE {jre}: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} uninstalled",
|
||||
chooseJdbcPluginZip: "Choose JDBC plugin zip file",
|
||||
agentDrivers: "Built-in Drivers",
|
||||
jdbcDrivers: "JDBC Drivers",
|
||||
importing: "Importing...",
|
||||
importOfflinePackage: "Import offline package",
|
||||
refresh: "Refresh",
|
||||
javaRuntime: "Java Runtime",
|
||||
javaRuntimeManaged: "DBX managed JRE",
|
||||
javaRuntimeSystem: "System java",
|
||||
javaRuntimeCustom: "Custom path",
|
||||
saving: "Saving...",
|
||||
customJavaPathPlaceholder: "/path/to/java or /path/to/jdk",
|
||||
choose: "Choose",
|
||||
systemJavaHint: "Use java from the current PATH.",
|
||||
jreRuntimeTitle: "JRE {jre} Runtime",
|
||||
notInstalled: "Not installed",
|
||||
reinstalling: "Reinstalling",
|
||||
installing: "Installing",
|
||||
install: "Install",
|
||||
reinstall: "Reinstall",
|
||||
uninstall: "Uninstall",
|
||||
jreRuntime: "JRE Runtime",
|
||||
jreRuntimeAutoDownloadHint: "Downloaded automatically when installing a driver for the first time",
|
||||
driversUpdatable: "{count} driver(s) can be updated",
|
||||
upgradingProgress: "Upgrading ({current}/{total})",
|
||||
upgradeAll: "Upgrade all",
|
||||
queued: "Queued",
|
||||
importLocalJar: "Import local JAR",
|
||||
updating: "Updating",
|
||||
update: "Update",
|
||||
localInstall: "Local install",
|
||||
},
|
||||
databaseExport: {
|
||||
title: "Export Database",
|
||||
includeStructure: "Table structure (DDL)",
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ export default {
|
|||
sqlSaved: "SQL guardado",
|
||||
sqlOpenFailed: "Error al abrir el archivo: {message}",
|
||||
sqlSaveFailed: "Error al guardar el archivo: {message}",
|
||||
driverManager: "Administrador de drivers",
|
||||
updatableDriverCount: "Cantidad de drivers actualizables",
|
||||
},
|
||||
updates: {
|
||||
title: "Actualizaciones",
|
||||
|
|
@ -59,6 +61,8 @@ export default {
|
|||
restartFailed: "Error al reiniciar la aplicación: {error}",
|
||||
exitAndUpdate: "Salir y actualizar",
|
||||
reopenHint: "La aplicación se reiniciará para completar la actualización",
|
||||
dockerUsersRun: "Los usuarios de Docker deben ejecutar",
|
||||
toUpdate: "para actualizar",
|
||||
},
|
||||
sidebar: {
|
||||
connections: "CONEXIONES",
|
||||
|
|
@ -108,7 +112,16 @@ export default {
|
|||
database: "Base de datos",
|
||||
databasePlaceholder: "Opcional",
|
||||
databasePlaceholderWithDefault: "Opcional, por defecto {database}",
|
||||
defaultDatabase: "BD predeterminada",
|
||||
authDatabase: "BD de autenticación",
|
||||
authDatabasePlaceholder: "Opcional, normalmente admin",
|
||||
authMechanism: "Mecanismo de autenticación",
|
||||
authMechanismDefault: "Predeterminado",
|
||||
serviceName: "Servicio/SID",
|
||||
serviceNameOnly: "Nombre de servicio",
|
||||
version: "Versión",
|
||||
driverInstallHintPrefix: "Instala el driver requerido desde ",
|
||||
driverInstallHintSuffix: " en la barra superior antes de conectar.",
|
||||
driverName: "Nombre del driver",
|
||||
driverNamePlaceholder: "Nombre del proveedor o entorno",
|
||||
urlParams: "Parámetros de URL",
|
||||
|
|
@ -252,6 +265,8 @@ export default {
|
|||
editor: {
|
||||
pressToExecute: "Presiona {mod}+Enter para ejecutar",
|
||||
pressToSaveSql: "Presiona {mod}+S para guardar el SQL",
|
||||
queryTimeoutError: "La consulta agotó el tiempo ({seconds}s). Comprueba la conexión a la base de datos.",
|
||||
connectionMayBeLost: "Es posible que la conexión se haya perdido. Actualiza los datos e inténtalo de nuevo.",
|
||||
showResultsPane: "Mostrar resultados",
|
||||
hideResultsPane: "Ocultar resultados",
|
||||
noDatabase: "Sin base de datos seleccionada",
|
||||
|
|
@ -1352,6 +1367,8 @@ export default {
|
|||
syncEndpoint: "URL WebDAV",
|
||||
syncUsername: "Usuario",
|
||||
syncPassword: "Contraseña",
|
||||
syncPasswordPlaceholder: "Introduce la contraseña",
|
||||
syncClearSavedPassword: "Borrar contraseña guardada",
|
||||
syncRememberWebDavPassword: "Recordar contraseña de app WebDAV",
|
||||
syncSavedPassword: "(guardada)",
|
||||
syncRememberWebDavPasswordDescription:
|
||||
|
|
@ -1434,6 +1451,68 @@ export default {
|
|||
openSource: "Repositorio de código abierto",
|
||||
officialDocs: "Documentación oficial",
|
||||
},
|
||||
driverStore: {
|
||||
progressJreExtract: "Extrayendo JRE...",
|
||||
progressDownloadJre: "Descargando JRE",
|
||||
progressDownloadDriver: "Descargando driver",
|
||||
usageTitle: "Uso de almacenamiento",
|
||||
usageTotal: "Total {size}",
|
||||
usageTotalLabel: "Total",
|
||||
calculating: "Calculando...",
|
||||
usageManagedJre: "JRE administrado",
|
||||
usageAgentDrivers: "Agentes de driver integrados",
|
||||
usageJdbcPlugin: "Plugin JDBC",
|
||||
usageJdbcDriverJars: "JARs de driver JDBC",
|
||||
javaRuntimeSaved: "Ajustes de Java guardados",
|
||||
javaRuntimeSaveFailed: "Error al guardar los ajustes de Java: {error}",
|
||||
chooseJavaExecutable: "Seleccionar ejecutable Java",
|
||||
driverInstallSuccess: "Driver {label} instalado",
|
||||
driverInstallFailed: "Error al instalar el driver {label}: {error}",
|
||||
upgradeAllSuccess: "{count} driver(s) actualizados",
|
||||
upgradeAllFailed: "Error al actualizar en lote: {error}",
|
||||
driverUninstallSuccess: "Driver {label} desinstalado",
|
||||
driverUninstallFailed: "Error al desinstalar el driver {label}: {error}",
|
||||
chooseOfflineDriverPackage: "Seleccionar paquete offline de drivers",
|
||||
offlineImportSuccess: "Importación offline completada. Se instalaron {count} driver(s).",
|
||||
offlineImportFailed: "Error en la importación offline: {error}",
|
||||
driverImportSuccess: "Driver {label} importado",
|
||||
driverImportFailed: "Error al importar el driver {label}: {error}",
|
||||
chooseDriverJar: "Seleccionar archivo JAR del driver",
|
||||
jreReinstallSuccess: "JRE {jre} reinstalado",
|
||||
jreReinstallFailed: "Error al reinstalar JRE {jre}: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} desinstalado",
|
||||
chooseJdbcPluginZip: "Seleccionar zip del plugin JDBC",
|
||||
agentDrivers: "Drivers integrados",
|
||||
jdbcDrivers: "Drivers JDBC",
|
||||
importing: "Importando...",
|
||||
importOfflinePackage: "Importar paquete offline",
|
||||
refresh: "Actualizar",
|
||||
javaRuntime: "Runtime Java",
|
||||
javaRuntimeManaged: "JRE administrado por DBX",
|
||||
javaRuntimeSystem: "java del sistema",
|
||||
javaRuntimeCustom: "Ruta personalizada",
|
||||
saving: "Guardando...",
|
||||
customJavaPathPlaceholder: "/ruta/a/java o /ruta/a/jdk",
|
||||
choose: "Seleccionar",
|
||||
systemJavaHint: "Usa java del PATH actual.",
|
||||
jreRuntimeTitle: "Runtime JRE {jre}",
|
||||
notInstalled: "No instalado",
|
||||
reinstalling: "Reinstalando",
|
||||
installing: "Instalando",
|
||||
install: "Instalar",
|
||||
reinstall: "Reinstalar",
|
||||
uninstall: "Desinstalar",
|
||||
jreRuntime: "Runtime JRE",
|
||||
jreRuntimeAutoDownloadHint: "Se descarga automáticamente al instalar un driver por primera vez",
|
||||
driversUpdatable: "{count} driver(s) pueden actualizarse",
|
||||
upgradingProgress: "Actualizando ({current}/{total})",
|
||||
upgradeAll: "Actualizar todo",
|
||||
queued: "En cola",
|
||||
importLocalJar: "Importar JAR local",
|
||||
updating: "Actualizando",
|
||||
update: "Actualizar",
|
||||
localInstall: "Instalación local",
|
||||
},
|
||||
databaseExport: {
|
||||
title: "Exportar base de datos",
|
||||
includeStructure: "Estructura de tablas (DDL)",
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ export default {
|
|||
sqlSaved: "SQL 已保存",
|
||||
sqlOpenFailed: "打开文件失败:{message}",
|
||||
sqlSaveFailed: "保存文件失败:{message}",
|
||||
driverManager: "驱动管理",
|
||||
updatableDriverCount: "可更新驱动数量",
|
||||
},
|
||||
updates: {
|
||||
title: "更新",
|
||||
|
|
@ -58,6 +60,8 @@ export default {
|
|||
restartFailed: "重启应用失败:{error}",
|
||||
exitAndUpdate: "退出并更新",
|
||||
reopenHint: "应用将自动重启以完成更新",
|
||||
dockerUsersRun: "Docker 用户请运行",
|
||||
toUpdate: "更新",
|
||||
},
|
||||
sidebar: {
|
||||
connections: "连接",
|
||||
|
|
@ -114,6 +118,10 @@ export default {
|
|||
authMechanism: "认证机制",
|
||||
authMechanismDefault: "默认",
|
||||
serviceName: "服务名/SID",
|
||||
serviceNameOnly: "服务名",
|
||||
version: "版本",
|
||||
driverInstallHintPrefix: "需要在顶部导航栏「",
|
||||
driverInstallHintSuffix: "」中安装对应的驱动才能连接。",
|
||||
driverName: "驱动名称",
|
||||
driverNamePlaceholder: "厂商或环境名称",
|
||||
urlParams: "URL 参数",
|
||||
|
|
@ -255,6 +263,8 @@ export default {
|
|||
editor: {
|
||||
pressToExecute: "按 {mod}+Enter 执行查询",
|
||||
pressToSaveSql: "按 {mod}+S 保存 SQL",
|
||||
queryTimeoutError: "查询超时 ({seconds}s),请检查数据库连接是否正常",
|
||||
connectionMayBeLost: "连接可能已断开,请刷新数据重试",
|
||||
showResultsPane: "显示结果",
|
||||
hideResultsPane: "收起结果",
|
||||
noDatabase: "未选择数据库",
|
||||
|
|
@ -1431,6 +1441,8 @@ export default {
|
|||
syncEndpoint: "WebDAV 地址",
|
||||
syncUsername: "用户名",
|
||||
syncPassword: "密码",
|
||||
syncPasswordPlaceholder: "输入密码",
|
||||
syncClearSavedPassword: "清除已保存的密码",
|
||||
syncRememberWebDavPassword: "记住 WebDAV 应用密码",
|
||||
syncSavedPassword: "(已保存)",
|
||||
syncRememberWebDavPasswordDescription: "密码会加密保存在本机,不会同步到 WebDAV,也不会替代同步密码。",
|
||||
|
|
@ -1510,6 +1522,68 @@ export default {
|
|||
openSource: "开源仓库",
|
||||
officialDocs: "官方文档",
|
||||
},
|
||||
driverStore: {
|
||||
progressJreExtract: "解压 JRE...",
|
||||
progressDownloadJre: "下载 JRE",
|
||||
progressDownloadDriver: "下载驱动",
|
||||
usageTitle: "空间占用明细",
|
||||
usageTotal: "总计 {size}",
|
||||
usageTotalLabel: "总计",
|
||||
calculating: "统计中...",
|
||||
usageManagedJre: "托管 JRE",
|
||||
usageAgentDrivers: "内置驱动 Agent",
|
||||
usageJdbcPlugin: "JDBC 插件",
|
||||
usageJdbcDriverJars: "JDBC 驱动 JAR",
|
||||
javaRuntimeSaved: "Java 运行时设置已保存",
|
||||
javaRuntimeSaveFailed: "Java 运行时设置失败: {error}",
|
||||
chooseJavaExecutable: "选择 Java 可执行文件",
|
||||
driverInstallSuccess: "{label} 驱动安装成功",
|
||||
driverInstallFailed: "{label} 驱动安装失败: {error}",
|
||||
upgradeAllSuccess: "{count} 个驱动升级完成",
|
||||
upgradeAllFailed: "批量升级失败: {error}",
|
||||
driverUninstallSuccess: "{label} 驱动已卸载",
|
||||
driverUninstallFailed: "{label} 驱动卸载失败: {error}",
|
||||
chooseOfflineDriverPackage: "选择离线驱动包",
|
||||
offlineImportSuccess: "离线导入完成,已安装 {count} 个驱动",
|
||||
offlineImportFailed: "离线导入失败: {error}",
|
||||
driverImportSuccess: "{label} 驱动导入成功",
|
||||
driverImportFailed: "{label} 驱动导入失败: {error}",
|
||||
chooseDriverJar: "选择驱动 JAR 文件",
|
||||
jreReinstallSuccess: "JRE {jre} 重新安装成功",
|
||||
jreReinstallFailed: "JRE {jre} 重新安装失败: {error}",
|
||||
jreUninstallSuccess: "JRE {jre} 已卸载",
|
||||
chooseJdbcPluginZip: "选择 JDBC 插件 zip 文件",
|
||||
agentDrivers: "内置驱动",
|
||||
jdbcDrivers: "JDBC 驱动",
|
||||
importing: "导入中...",
|
||||
importOfflinePackage: "导入离线包",
|
||||
refresh: "刷新",
|
||||
javaRuntime: "Java 运行时",
|
||||
javaRuntimeManaged: "DBX 托管 JRE",
|
||||
javaRuntimeSystem: "系统 java",
|
||||
javaRuntimeCustom: "自定义路径",
|
||||
saving: "保存中...",
|
||||
customJavaPathPlaceholder: "/path/to/java 或 /path/to/jdk",
|
||||
choose: "选择",
|
||||
systemJavaHint: "使用当前环境 PATH 中的 java。",
|
||||
jreRuntimeTitle: "JRE {jre} 运行时",
|
||||
notInstalled: "未安装",
|
||||
reinstalling: "重装中",
|
||||
installing: "安装中",
|
||||
install: "安装",
|
||||
reinstall: "重新安装",
|
||||
uninstall: "卸载",
|
||||
jreRuntime: "JRE 运行时",
|
||||
jreRuntimeAutoDownloadHint: "首次安装驱动时自动下载",
|
||||
driversUpdatable: "{count} 个驱动可更新",
|
||||
upgradingProgress: "升级中 ({current}/{total})",
|
||||
upgradeAll: "全部升级",
|
||||
queued: "排队中",
|
||||
importLocalJar: "导入本地 JAR",
|
||||
updating: "更新中",
|
||||
update: "更新",
|
||||
localInstall: "本地安装",
|
||||
},
|
||||
databaseExport: {
|
||||
title: "导出数据库",
|
||||
includeStructure: "表结构 (DDL)",
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ export function buildEditorFontThemeRules(
|
|||
fontSize: `var(${EDITOR_FONT_SIZE_CSS_VAR}, ${defaults?.size ?? 13}px)`,
|
||||
fontFamily: `var(${EDITOR_FONT_FAMILY_CSS_VAR}, ${defaults?.family ?? "monospace"})`,
|
||||
position: "relative",
|
||||
userSelect: "none",
|
||||
},
|
||||
".cm-gutters:after": {
|
||||
background: "rgba(148, 163, 184, 0.38)",
|
||||
|
|
@ -143,6 +144,7 @@ export function buildEditorFontThemeRules(
|
|||
},
|
||||
".cm-lineNumbers .cm-gutterElement": {
|
||||
paddingRight: "16px",
|
||||
userSelect: "none",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -390,41 +390,41 @@ const WINDOW_FUNCTIONS = new Set([
|
|||
function getFunctionDescriptions(t?: SqlCompletionTranslations): Map<string, string> {
|
||||
const d = t?.functionDescriptions ?? {};
|
||||
return new Map<string, string>([
|
||||
["COUNT", d.COUNT || "返回行数"],
|
||||
["SUM", d.SUM || "返回数值列的总和"],
|
||||
["AVG", d.AVG || "返回数值列的平均值"],
|
||||
["MIN", d.MIN || "返回最小值"],
|
||||
["MAX", d.MAX || "返回最大值"],
|
||||
["GROUP_CONCAT", d.GROUP_CONCAT || "将分组中的值连接为字符串"],
|
||||
["STRING_AGG", d.STRING_AGG || "将分组中的字符串连接起来"],
|
||||
["CONCAT", d.CONCAT || "连接多个字符串"],
|
||||
["CONCAT_WS", d.CONCAT_WS || "使用分隔符连接多个字符串"],
|
||||
["SUBSTRING", d.SUBSTRING || "提取子字符串"],
|
||||
["REPLACE", d.REPLACE || "替换字符串中的内容"],
|
||||
["TRIM", d.TRIM || "去除首尾空格"],
|
||||
["UPPER", d.UPPER || "转换为大写"],
|
||||
["LOWER", d.LOWER || "转换为小写"],
|
||||
["LENGTH", d.LENGTH || "返回字符串长度"],
|
||||
["REGEXP_REPLACE", d.REGEXP_REPLACE || "使用正则表达式替换"],
|
||||
["DATE_FORMAT", d.DATE_FORMAT || "按指定格式格式化日期"],
|
||||
["DATEDIFF", d.DATEDIFF || "计算两个日期的差值"],
|
||||
["DATE_ADD", d.DATE_ADD || "对日期进行加法运算"],
|
||||
["DATE_SUB", d.DATE_SUB || "对日期进行减法运算"],
|
||||
["EXTRACT", d.EXTRACT || "提取日期中的指定部分"],
|
||||
["NOW", d.NOW || "返回当前日期时间"],
|
||||
["ROUND", d.ROUND || "四舍五入到指定小数位"],
|
||||
["FLOOR", d.FLOOR || "向下取整"],
|
||||
["CEIL", d.CEIL || "向上取整"],
|
||||
["ABS", d.ABS || "返回绝对值"],
|
||||
["MOD", d.MOD || "返回除法余数"],
|
||||
["COALESCE", d.COALESCE || "返回第一个非 NULL 的参数"],
|
||||
["IFNULL", d.IFNULL || "如果为 NULL 则返回替代值"],
|
||||
["NULLIF", d.NULLIF || "如果相等则返回 NULL"],
|
||||
["CAST", d.CAST || "将表达式转换为指定类型"],
|
||||
["JSON_EXTRACT", d.JSON_EXTRACT || "从 JSON 中提取值"],
|
||||
["JSON_VALUE", d.JSON_VALUE || "从 JSON 中提取标量值"],
|
||||
["JSON_OBJECT", d.JSON_OBJECT || "创建 JSON 对象"],
|
||||
["JSON_ARRAY", d.JSON_ARRAY || "创建 JSON 数组"],
|
||||
["COUNT", d.COUNT || "Returns the number of rows"],
|
||||
["SUM", d.SUM || "Returns the sum of a numeric column"],
|
||||
["AVG", d.AVG || "Returns the average of a numeric column"],
|
||||
["MIN", d.MIN || "Returns the minimum value"],
|
||||
["MAX", d.MAX || "Returns the maximum value"],
|
||||
["GROUP_CONCAT", d.GROUP_CONCAT || "Concatenates group values into a string"],
|
||||
["STRING_AGG", d.STRING_AGG || "Concatenates strings in a group"],
|
||||
["CONCAT", d.CONCAT || "Concatenates multiple strings"],
|
||||
["CONCAT_WS", d.CONCAT_WS || "Concatenates strings with a separator"],
|
||||
["SUBSTRING", d.SUBSTRING || "Extracts a substring"],
|
||||
["REPLACE", d.REPLACE || "Replaces content in a string"],
|
||||
["TRIM", d.TRIM || "Removes leading and trailing spaces"],
|
||||
["UPPER", d.UPPER || "Converts to uppercase"],
|
||||
["LOWER", d.LOWER || "Converts to lowercase"],
|
||||
["LENGTH", d.LENGTH || "Returns string length"],
|
||||
["REGEXP_REPLACE", d.REGEXP_REPLACE || "Replaces using a regular expression"],
|
||||
["DATE_FORMAT", d.DATE_FORMAT || "Formats a date with a pattern"],
|
||||
["DATEDIFF", d.DATEDIFF || "Calculates the difference between two dates"],
|
||||
["DATE_ADD", d.DATE_ADD || "Adds to a date"],
|
||||
["DATE_SUB", d.DATE_SUB || "Subtracts from a date"],
|
||||
["EXTRACT", d.EXTRACT || "Extracts a part from a date"],
|
||||
["NOW", d.NOW || "Returns the current date and time"],
|
||||
["ROUND", d.ROUND || "Rounds to the specified precision"],
|
||||
["FLOOR", d.FLOOR || "Rounds down"],
|
||||
["CEIL", d.CEIL || "Rounds up"],
|
||||
["ABS", d.ABS || "Returns the absolute value"],
|
||||
["MOD", d.MOD || "Returns the remainder"],
|
||||
["COALESCE", d.COALESCE || "Returns the first non-NULL argument"],
|
||||
["IFNULL", d.IFNULL || "Returns an alternate value when NULL"],
|
||||
["NULLIF", d.NULLIF || "Returns NULL when values are equal"],
|
||||
["CAST", d.CAST || "Converts an expression to a specified type"],
|
||||
["JSON_EXTRACT", d.JSON_EXTRACT || "Extracts a value from JSON"],
|
||||
["JSON_VALUE", d.JSON_VALUE || "Extracts a scalar value from JSON"],
|
||||
["JSON_OBJECT", d.JSON_OBJECT || "Creates a JSON object"],
|
||||
["JSON_ARRAY", d.JSON_ARRAY || "Creates a JSON array"],
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -1527,7 +1527,7 @@ function buildStarExpansionItem(
|
|||
return {
|
||||
label: "* → columns",
|
||||
type: "snippet" as const,
|
||||
detail: `${(t?.starExpansionColumns ?? "{count} 列").replace("{count}", String(allColumns.length))}: ${expansion.length > 60 ? expansion.slice(0, 57) + "..." : expansion}`,
|
||||
detail: `${(t?.starExpansionColumns ?? "{count} columns").replace("{count}", String(allColumns.length))}: ${expansion.length > 60 ? expansion.slice(0, 57) + "..." : expansion}`,
|
||||
apply: expansion,
|
||||
boost: 1900,
|
||||
};
|
||||
|
|
@ -1580,19 +1580,19 @@ function buildComparisonValueItems(
|
|||
items.push({
|
||||
label: "NULL",
|
||||
type: "keyword" as const,
|
||||
detail: t?.nullValue ?? "空值",
|
||||
detail: t?.nullValue ?? "NULL value",
|
||||
boost: 1300,
|
||||
});
|
||||
items.push({
|
||||
label: "IS NULL",
|
||||
type: "keyword" as const,
|
||||
detail: t?.isNull ?? "判断是否为 NULL",
|
||||
detail: t?.isNull ?? "Checks whether the value is NULL",
|
||||
boost: 1250,
|
||||
});
|
||||
items.push({
|
||||
label: "IS NOT NULL",
|
||||
type: "keyword" as const,
|
||||
detail: t?.isNotNull ?? "判断是否不为 NULL",
|
||||
detail: t?.isNotNull ?? "Checks whether the value is not NULL",
|
||||
boost: 1200,
|
||||
});
|
||||
|
||||
|
|
@ -1607,7 +1607,7 @@ function buildComparisonValueItems(
|
|||
items.push({
|
||||
label: "''",
|
||||
type: "snippet" as const,
|
||||
detail: t?.stringLiteral ?? "字符串字面量",
|
||||
detail: t?.stringLiteral ?? "String literal",
|
||||
apply: "'${value}'",
|
||||
boost: 1800,
|
||||
});
|
||||
|
|
@ -1630,7 +1630,7 @@ function buildComparisonValueItems(
|
|||
items.push({
|
||||
label: "0",
|
||||
type: "snippet" as const,
|
||||
detail: t?.numericLiteral ?? "数值字面量",
|
||||
detail: t?.numericLiteral ?? "Numeric literal",
|
||||
apply: "${1:value}",
|
||||
boost: 1750,
|
||||
});
|
||||
|
|
@ -1640,8 +1640,8 @@ function buildComparisonValueItems(
|
|||
// Boolean-ish: tinyint or bit
|
||||
if (dt === "bit" || dt === "boolean" || dt === "bool") {
|
||||
items.push(
|
||||
{ label: "TRUE", type: "keyword" as const, detail: t?.booleanValue ?? "布尔值", boost: 1700 },
|
||||
{ label: "FALSE", type: "keyword" as const, detail: t?.booleanValue ?? "布尔值", boost: 1650 },
|
||||
{ label: "TRUE", type: "keyword" as const, detail: t?.booleanValue ?? "Boolean value", boost: 1700 },
|
||||
{ label: "FALSE", type: "keyword" as const, detail: t?.booleanValue ?? "Boolean value", boost: 1650 },
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -709,7 +709,7 @@ export const useQueryStore = defineStore("query", () => {
|
|||
timeoutSecs: queryTimeoutSecs,
|
||||
};
|
||||
const frontendTimeoutSecs = Math.max(queryTimeoutSecs * 2, 60);
|
||||
const timeoutError = new Error(`查询超时 (${frontendTimeoutSecs}s),请检查数据库连接是否正常`);
|
||||
const timeoutError = new Error(t("editor.queryTimeoutError", { seconds: frontendTimeoutSecs }));
|
||||
const results = await Promise.race([
|
||||
api.executeMulti(tab.connectionId, tab.database, sqlToExecute, tab.schema, executionId, executionOptions),
|
||||
new Promise<never>((_, reject) => setTimeout(() => reject(timeoutError), frontendTimeoutSecs * 1000)),
|
||||
|
|
@ -924,11 +924,11 @@ export const useQueryStore = defineStore("query", () => {
|
|||
function notifyConnectionMayBeLost() {
|
||||
const stuck = tabs.value.filter((t) => t.isExecuting);
|
||||
if (stuck.length > 0) {
|
||||
stuck.forEach((t) => {
|
||||
t.isExecuting = false;
|
||||
t.isCancelling = false;
|
||||
t.executionId = undefined;
|
||||
t.result = toErrorResult(new Error("连接可能已断开,请刷新数据重试"));
|
||||
stuck.forEach((tab) => {
|
||||
tab.isExecuting = false;
|
||||
tab.isCancelling = false;
|
||||
tab.executionId = undefined;
|
||||
tab.result = toErrorResult(new Error(t("editor.connectionMayBeLost")));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,6 +147,12 @@ html.disable-transitions *::after {
|
|||
animation-duration: 0s !important;
|
||||
}
|
||||
|
||||
body.dbx-table-reference-dragging,
|
||||
body.dbx-table-reference-dragging * {
|
||||
user-select: none !important;
|
||||
-webkit-user-select: none !important;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
|
|
|
|||
|
|
@ -354,7 +354,6 @@ test("suggests DATE_FORMAT as parameter snippet", () => {
|
|||
|
||||
const snippet = items.find((item) => item.type === "function" && item.label === "DATE_FORMAT");
|
||||
assert.ok(snippet);
|
||||
assert.equal(snippet.detail, "按指定格式格式化日期");
|
||||
assert.equal(snippet.apply, "DATE_FORMAT(${date}, ${format})");
|
||||
});
|
||||
|
||||
|
|
@ -872,18 +871,6 @@ test("prefix matches still rank above fuzzy matches", () => {
|
|||
assert.equal(items[0]?.label, "name");
|
||||
});
|
||||
|
||||
// --- Function inline docs ---
|
||||
|
||||
test("shows function description in detail", () => {
|
||||
const items = buildSqlCompletionItems("select cou", "select cou".length, {
|
||||
tables,
|
||||
columnsByTable,
|
||||
});
|
||||
const countItem = items.find((item) => item.label === "COUNT");
|
||||
assert.ok(countItem);
|
||||
assert.equal(countItem.detail, "返回行数");
|
||||
});
|
||||
|
||||
// --- Type-aware comparison hints ---
|
||||
|
||||
test("suggests NULL and IS NULL after comparison operator", () => {
|
||||
|
|
@ -918,16 +905,6 @@ test("shows SELECT * column expansion", () => {
|
|||
assert.ok(starItem, "should show column expansion for *");
|
||||
});
|
||||
|
||||
test("star expansion item includes column count in detail", () => {
|
||||
const sql = "select *";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
tables,
|
||||
columnsByTable,
|
||||
});
|
||||
const starItem = items.find((item) => item.label === "* → columns");
|
||||
assert.ok(starItem?.detail?.includes("列"), "detail should mention column count");
|
||||
});
|
||||
|
||||
// --- History-based ranking ---
|
||||
|
||||
test("recordCompletionSelection boosts future ranking", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue