From 97f062773b91ead197093790594539d7110b8098 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Fri, 12 Jun 2026 02:05:10 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(connection):=20?= =?UTF-8?q?=E6=8B=86=E5=88=86=20Redis=20=E8=BF=9E=E6=8E=A5=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抽离 Redis 单机、集群、哨兵配置区到独立组件 - 保留 Redis 密码、Sentinel 密钥和 DB 范围设置行为 - 同步更新连接弹窗源码级回归检查 --- .../ConnectionModal.edit-password.test.tsx | 4 +- frontend/src/components/ConnectionModal.tsx | 190 +------------- .../ConnectionModalRedisSections.tsx | 242 ++++++++++++++++++ 3 files changed, 259 insertions(+), 177 deletions(-) create mode 100644 frontend/src/components/ConnectionModalRedisSections.tsx diff --git a/frontend/src/components/ConnectionModal.edit-password.test.tsx b/frontend/src/components/ConnectionModal.edit-password.test.tsx index 5bf1187..e683429 100644 --- a/frontend/src/components/ConnectionModal.edit-password.test.tsx +++ b/frontend/src/components/ConnectionModal.edit-password.test.tsx @@ -1,7 +1,9 @@ import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; -const source = readFileSync(new URL('./ConnectionModal.tsx', import.meta.url), 'utf8'); +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}`; describe('ConnectionModal edit password behavior', () => { it('keeps the prefilled primary password masked by default', () => { diff --git a/frontend/src/components/ConnectionModal.tsx b/frontend/src/components/ConnectionModal.tsx index 9b84791..c39891d 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 ConnectionModalRedisSections from "./ConnectionModalRedisSections"; const { Text } = Typography; type EditableJVMMode = (typeof JVM_EDITABLE_MODES)[number]; @@ -5637,182 +5638,19 @@ const ConnectionModal: React.FC<{ ), })} - {isRedis && - renderConfigSectionCard({ - sectionKey: "connectionMode", - icon: , - children: ( - <> - {renderChoiceCards({ - fieldName: "redisTopology", - value: String(redisTopology), - options: [ - { - value: "single", - label: "单机模式", - description: "只连接一个 Redis 节点。", - }, - { - value: "cluster", - label: "集群模式", - description: "Redis Cluster,配置多个种子节点。", - }, - { - value: "sentinel", - label: "哨兵模式", - description: "通过 Sentinel 发现主节点,适合主从高可用。", - }, - ], - })} - {(redisTopology === "cluster" || - redisTopology === "sentinel") && ( - <> - - - - )} - - )} - - ), - })} - - {isRedis && - renderConfigSectionCard({ - sectionKey: "credentials", - icon: , - children: ( - <> - - - - {redisTopology === "sentinel" && ( - <> -
- - - - - - -
- {renderStoredSecretControls({ - fieldName: "redisSentinelPassword", - clearKey: "redisSentinelPassword", - hasStoredSecret: - initialValues?.hasRedisSentinelPassword, - clearLabel: "清除已保存 Sentinel 密码", - description: - "当前已保存 Sentinel 密码。留空表示继续沿用,输入新值表示替换。", - })} - - )} - - ), - })} - - {isRedis && - renderConfigSectionCard({ - sectionKey: "databaseScope", - icon: , - children: ( - - - - ), - })} + {isRedis && ( + + )} {!isFileDb && !isRedis && diff --git a/frontend/src/components/ConnectionModalRedisSections.tsx b/frontend/src/components/ConnectionModalRedisSections.tsx new file mode 100644 index 0000000..a60c2df --- /dev/null +++ b/frontend/src/components/ConnectionModalRedisSections.tsx @@ -0,0 +1,242 @@ +import React from "react"; +import { Form, Input, Select } from "antd"; +import { + ClusterOutlined, + DatabaseOutlined, + SafetyCertificateOutlined, +} from "@ant-design/icons"; + +import type { SavedConnection } from "../types"; +import { + getStoredSecretPlaceholder, + type ConnectionConfigSectionKey, +} from "../utils/connectionModalPresentation"; +import { noAutoCapInputProps } from "../utils/inputAutoCap"; + +type ChoiceCardOption = { + value: string; + label: string; + description?: string; +}; + +type RenderChoiceCards = (params: { + fieldName: string; + value: string; + options: ChoiceCardOption[]; + minWidth?: number; + onSelect?: (value: string) => 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: "redisSentinelPassword"; + hasStoredSecret?: boolean; + clearLabel: string; + description: string; +}) => React.ReactNode; + +interface ConnectionModalRedisSectionsProps { + redisTopology: string; + redisDbList: number[]; + initialValues?: SavedConnection | null; + primaryPasswordVisible: boolean; + setPrimaryPasswordVisible: (visible: boolean) => void; + renderChoiceCards: RenderChoiceCards; + renderConfigSectionCard: RenderConfigSectionCard; + renderStoredSecretControls: RenderStoredSecretControls; + createUriAwareRequiredRule: ( + messageText: string, + validateValue?: (value: unknown) => boolean, + ) => any; +} + +const ConnectionModalRedisSections: React.FC = ({ + redisTopology, + redisDbList, + initialValues, + primaryPasswordVisible, + setPrimaryPasswordVisible, + renderChoiceCards, + renderConfigSectionCard, + renderStoredSecretControls, + createUriAwareRequiredRule, +}) => ( + <> + {renderConfigSectionCard({ + sectionKey: "connectionMode", + icon: , + children: ( + <> + {renderChoiceCards({ + fieldName: "redisTopology", + value: String(redisTopology), + options: [ + { + value: "single", + label: "单机模式", + description: "只连接一个 Redis 节点。", + }, + { + value: "cluster", + label: "集群模式", + description: "Redis Cluster,配置多个种子节点。", + }, + { + value: "sentinel", + label: "哨兵模式", + description: "通过 Sentinel 发现主节点,适合主从高可用。", + }, + ], + })} + {(redisTopology === "cluster" || redisTopology === "sentinel") && ( + <> + + + + )} + + )} + + ), + })} + + {renderConfigSectionCard({ + sectionKey: "credentials", + icon: , + children: ( + <> + + + + {redisTopology === "sentinel" && ( + <> +
+ + + + + + +
+ {renderStoredSecretControls({ + fieldName: "redisSentinelPassword", + clearKey: "redisSentinelPassword", + hasStoredSecret: initialValues?.hasRedisSentinelPassword, + clearLabel: "清除已保存 Sentinel 密码", + description: + "当前已保存 Sentinel 密码。留空表示继续沿用,输入新值表示替换。", + })} + + )} + + ), + })} + + {renderConfigSectionCard({ + sectionKey: "databaseScope", + icon: , + children: ( + + + + ), + })} + +); + +export default ConnectionModalRedisSections;