diff --git a/frontend/src/components/ConnectionModal.edit-password.test.tsx b/frontend/src/components/ConnectionModal.edit-password.test.tsx
index e683429..2e6b86c 100644
--- a/frontend/src/components/ConnectionModal.edit-password.test.tsx
+++ b/frontend/src/components/ConnectionModal.edit-password.test.tsx
@@ -3,7 +3,8 @@ import { readFileSync } from 'node:fs';
const connectionModalSource = readFileSync(new URL('./ConnectionModal.tsx', import.meta.url), 'utf8');
const redisSectionsSource = readFileSync(new URL('./ConnectionModalRedisSections.tsx', import.meta.url), 'utf8');
-const source = `${connectionModalSource}\n${redisSectionsSource}`;
+const mongoSectionsSource = readFileSync(new URL('./ConnectionModalMongoSections.tsx', import.meta.url), 'utf8');
+const source = `${connectionModalSource}\n${redisSectionsSource}\n${mongoSectionsSource}`;
describe('ConnectionModal edit password behavior', () => {
it('keeps the prefilled primary password masked by default', () => {
@@ -51,3 +52,15 @@ describe('ConnectionModal Redis Sentinel configuration', () => {
expect(source).toContain('form.setFieldValue("port", 6379)');
});
});
+
+describe('ConnectionModal MongoDB configuration', () => {
+ it('keeps replica, SRV, and read preference fields in the split Mongo sections', () => {
+ expect(source).toContain('ConnectionModalMongoSections');
+ expect(source).toContain('name="mongoSrv"');
+ expect(source).toContain('SRV 与 SSH 隧道同时启用');
+ expect(source).toContain('name="mongoReplicaPassword"');
+ expect(source).toContain('clearKey: "mongoReplicaPassword"');
+ expect(source).toContain('自动发现成员');
+ expect(source).toContain('fieldName: "mongoReadPreference"');
+ });
+});
diff --git a/frontend/src/components/ConnectionModal.tsx b/frontend/src/components/ConnectionModal.tsx
index c39891d..f923bc4 100644
--- a/frontend/src/components/ConnectionModal.tsx
+++ b/frontend/src/components/ConnectionModal.tsx
@@ -97,6 +97,7 @@ import {
TestJVMConnection,
} from "../../wailsjs/go/app/App";
import { ConnectionConfig, MongoMemberInfo, SavedConnection } from "../types";
+import ConnectionModalMongoSections from "./ConnectionModalMongoSections";
import ConnectionModalRedisSections from "./ConnectionModalRedisSections";
const { Text } = Typography;
@@ -5335,308 +5336,24 @@ const ConnectionModal: React.FC<{
),
})}
- {dbType === "mongodb" &&
- renderConfigSectionCard({
- sectionKey: "connectionMode",
- icon: ,
- children: renderChoiceCards({
- fieldName: "mongoTopology",
- value: String(mongoTopology),
- options: [
- {
- value: "single",
- label: "单机模式",
- description: "只连接一个 MongoDB 节点。",
- },
- {
- value: "replica",
- label: "副本集 / 多节点",
- description: "配置副本集名称和多个候选节点。",
- },
- ],
- }),
- })}
-
- {dbType === "mongodb" &&
- renderConfigSectionCard({
- sectionKey: "mongoDiscovery",
- icon: ,
- children: (
- <>
-
-
-
-
- {[
- {
- value: false,
- label: "标准地址",
- description: "使用 host:port 直连或副本集节点列表。",
- },
- {
- value: true,
- label: "SRV 地址",
- description:
- "使用 mongodb+srv,由 DNS 发现目标节点。",
- },
- ].map((option) => {
- const active = mongoSrv === option.value;
- return (
-
- );
- })}
-
- {mongoSrv && useSSH && (
-
- )}
- >
- ),
- })}
-
- {dbType === "mongodb" &&
- mongoTopology === "replica" &&
- renderConfigSectionCard({
- sectionKey: "replica",
- icon: ,
- children: (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {renderStoredSecretControls({
- fieldName: "mongoReplicaPassword",
- clearKey: "mongoReplicaPassword",
- hasStoredSecret: initialValues?.hasMongoReplicaPassword,
- clearLabel: "清除已保存副本集密码",
- description:
- "当前已保存副本集密码。留空表示继续沿用,输入新值表示替换。",
- })}
-
-
-
- {mongoMembers.length > 0 && (
- record.host}
- pagination={false}
- dataSource={mongoMembers}
- style={{ marginBottom: 12 }}
- columns={[
- { title: "Host", dataIndex: "host", width: "48%" },
- {
- title: "角色",
- dataIndex: "role",
- width: "32%",
- render: (
- value: string,
- record: MongoMemberInfo,
- ) => (
-
- {value || "UNKNOWN"}
-
- ),
- },
- {
- title: "健康",
- dataIndex: "healthy",
- width: "20%",
- render: (value: boolean) => (
-
- {value ? "正常" : "异常"}
-
- ),
- },
- ]}
- />
- )}
- >
- ),
- })}
-
- {dbType === "mongodb" &&
- renderConfigSectionCard({
- sectionKey: "mongoPolicy",
- icon: ,
- children: (
-
-
-
-
-
- 读偏好 (readPreference)
- {renderChoiceCards({
- fieldName: "mongoReadPreference",
- value: String(mongoReadPreference),
- minWidth: 130,
- options: [
- {
- value: "primary",
- label: "primary",
- description: "只读主节点。",
- },
- {
- value: "primaryPreferred",
- label: "primaryPreferred",
- description: "主节点优先。",
- },
- {
- value: "secondary",
- label: "secondary",
- description: "只读从节点。",
- },
- {
- value: "secondaryPreferred",
- label: "secondaryPreferred",
- description: "从节点优先。",
- },
- {
- value: "nearest",
- label: "nearest",
- description: "选择最近节点。",
- },
- ],
- })}
-
-
- ),
- })}
+ {dbType === "mongodb" && (
+
+ )}
{isRedis && (
void;
+}) => React.ReactNode;
+
+type RenderConfigSectionCard = (params: {
+ sectionKey: ConnectionConfigSectionKey;
+ icon: React.ReactNode;
+ children: React.ReactNode;
+ badge?: React.ReactNode;
+}) => React.ReactNode;
+
+type RenderStoredSecretControls = (params: {
+ fieldName: string;
+ clearKey: "mongoReplicaPassword";
+ hasStoredSecret?: boolean;
+ clearLabel: string;
+ description: string;
+}) => React.ReactNode;
+
+interface ConnectionModalMongoSectionsProps {
+ mongoTopology: string;
+ mongoSrv: boolean;
+ useSSH: boolean;
+ darkMode: boolean;
+ modalMutedTextStyle: React.CSSProperties;
+ mongoReadPreference: string;
+ mongoMembers: MongoMemberInfo[];
+ discoveringMembers: boolean;
+ initialValues?: SavedConnection | null;
+ renderChoiceCards: RenderChoiceCards;
+ renderConfigSectionCard: RenderConfigSectionCard;
+ renderStoredSecretControls: RenderStoredSecretControls;
+ setChoiceFieldValue: (fieldName: string, value: string | boolean) => void;
+ handleDiscoverMongoMembers: () => void;
+}
+
+const ConnectionModalMongoSections: React.FC = ({
+ mongoTopology,
+ mongoSrv,
+ useSSH,
+ darkMode,
+ modalMutedTextStyle,
+ mongoReadPreference,
+ mongoMembers,
+ discoveringMembers,
+ initialValues,
+ renderChoiceCards,
+ renderConfigSectionCard,
+ renderStoredSecretControls,
+ setChoiceFieldValue,
+ handleDiscoverMongoMembers,
+}) => (
+ <>
+ {renderConfigSectionCard({
+ sectionKey: "connectionMode",
+ icon: ,
+ children: renderChoiceCards({
+ fieldName: "mongoTopology",
+ value: String(mongoTopology),
+ options: [
+ {
+ value: "single",
+ label: "单机模式",
+ description: "只连接一个 MongoDB 节点。",
+ },
+ {
+ value: "replica",
+ label: "副本集 / 多节点",
+ description: "配置副本集名称和多个候选节点。",
+ },
+ ],
+ }),
+ })}
+
+ {renderConfigSectionCard({
+ sectionKey: "mongoDiscovery",
+ icon: ,
+ children: (
+ <>
+
+
+
+
+ {[
+ {
+ value: false,
+ label: "标准地址",
+ description: "使用 host:port 直连或副本集节点列表。",
+ },
+ {
+ value: true,
+ label: "SRV 地址",
+ description: "使用 mongodb+srv,由 DNS 发现目标节点。",
+ },
+ ].map((option) => {
+ const active = mongoSrv === option.value;
+ return (
+
+ );
+ })}
+
+ {mongoSrv && useSSH && (
+
+ )}
+ >
+ ),
+ })}
+
+ {mongoTopology === "replica" &&
+ renderConfigSectionCard({
+ sectionKey: "replica",
+ icon: ,
+ children: (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {renderStoredSecretControls({
+ fieldName: "mongoReplicaPassword",
+ clearKey: "mongoReplicaPassword",
+ hasStoredSecret: initialValues?.hasMongoReplicaPassword,
+ clearLabel: "清除已保存副本集密码",
+ description:
+ "当前已保存副本集密码。留空表示继续沿用,输入新值表示替换。",
+ })}
+
+
+
+ {mongoMembers.length > 0 && (
+ record.host}
+ pagination={false}
+ dataSource={mongoMembers}
+ style={{ marginBottom: 12 }}
+ columns={[
+ { title: "Host", dataIndex: "host", width: "48%" },
+ {
+ title: "角色",
+ dataIndex: "role",
+ width: "32%",
+ render: (value: string, record: MongoMemberInfo) => (
+
+ {value || "UNKNOWN"}
+
+ ),
+ },
+ {
+ title: "健康",
+ dataIndex: "healthy",
+ width: "20%",
+ render: (value: boolean) => (
+
+ {value ? "正常" : "异常"}
+
+ ),
+ },
+ ]}
+ />
+ )}
+ >
+ ),
+ })}
+
+ {renderConfigSectionCard({
+ sectionKey: "mongoPolicy",
+ icon: ,
+ children: (
+
+
+
+
+
+ 读偏好 (readPreference)
+ {renderChoiceCards({
+ fieldName: "mongoReadPreference",
+ value: String(mongoReadPreference),
+ minWidth: 130,
+ options: [
+ {
+ value: "primary",
+ label: "primary",
+ description: "只读主节点。",
+ },
+ {
+ value: "primaryPreferred",
+ label: "primaryPreferred",
+ description: "主节点优先。",
+ },
+ {
+ value: "secondary",
+ label: "secondary",
+ description: "只读从节点。",
+ },
+ {
+ value: "secondaryPreferred",
+ label: "secondaryPreferred",
+ description: "从节点优先。",
+ },
+ {
+ value: "nearest",
+ label: "nearest",
+ description: "选择最近节点。",
+ },
+ ],
+ })}
+
+
+ ),
+ })}
+ >
+);
+
+export default ConnectionModalMongoSections;