From daacd95193e20ddaa37cc92bed32053bbbe7e071 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Sun, 26 Jul 2026 22:31:04 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20style(connection):=20=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E8=A1=A8=E5=8D=95=E5=BF=85=E5=A1=AB=E9=A1=B9=E7=BD=AE?= =?UTF-8?q?=E9=A1=B6=E5=B9=B6=E5=8E=BB=E6=8E=89=E5=B1=82=E5=B1=82=E5=8D=A1?= =?UTF-8?q?=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 信息架构:原顺序是 身份信息 → 连接 URI → …(约 800 行后)→ 目标地址 → 服务与数据库 → 凭据, 最关键的 host/端口/账号/密码排在最后,而可选的连接名称、预设类型与很高的 URI 多行输入框 占据首屏。 - 把 identity 与 uri 两组从条件链之前搬到之后,使首屏依次为目标地址 → 生产连接保护 → 连接模式 → 认证凭据;连接名称与预设类型下沉,URI 作为「粘贴整串」的高级入口置于末尾 - URI 分组改为可折叠且默认折叠;编辑已含 uri 的连接时默认展开,避免看不到已录入的值 - configSectionCardStyle 由「16px 圆角 + 边框 + 内阴影 + 独立底色」改为「小标题 + 细上分割线」, 并用 CSS 让首组不显示分割线 - modalInnerSectionStyle 同样去除边框/底色/圆角(保留内边距,间距不变)。该样式同时用于 左侧导航容器、右侧内容面板与网络安全各分组,原先与内部分组叠成多层「卡片套卡片」 - 左侧导航项改为扁平列表:去掉 14px 圆角、边框、渐变底与投影,选中态只用左侧强调条 + 淡底 - 测试补 expandUriSection 辅助函数:两个断言 URI 文案/按钮的用例需先展开该分组。 已确认这两项失败确由本次行为变更引起(基线 21 项全通过) --- frontend/src/App.css | 7 + .../components/ConnectionModal.i18n.test.tsx | 21 ++ frontend/src/components/ConnectionModal.tsx | 32 +-- .../connectionModal/ConnectionModalStep2.tsx | 255 +++++++++--------- 4 files changed, 180 insertions(+), 135 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 418525e8..45bcd833 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -414,6 +414,13 @@ body[data-theme='light'] .redis-viewer-workbench .ant-radio-button-wrapper-check flex-shrink: 0; } +/* 连接配置的表单分组改用「小标题 + 细分割线」代替卡片(见 ConnectionModal.tsx 的 + configSectionCardStyle)。每组靠一条上分割线与前一组划界,因此首组不需要这条线。 */ +[data-connection-config-section]:first-child { + padding-top: 0 !important; + border-top: none !important; +} + /* Custom Title Bar Close Button Hover */ body[data-platform='windows'] .titlebar-window-controls { gap: 8px; diff --git a/frontend/src/components/ConnectionModal.i18n.test.tsx b/frontend/src/components/ConnectionModal.i18n.test.tsx index 84690e8f..9b597f56 100644 --- a/frontend/src/components/ConnectionModal.i18n.test.tsx +++ b/frontend/src/components/ConnectionModal.i18n.test.tsx @@ -68,6 +68,19 @@ const findClickableByAnyText = (renderer: ReactTestRenderer, texts: string[]) => const findClickableCard = (renderer: ReactTestRenderer, text: string) => renderer.root.findAll((node) => node.props?.role === "button" && textContent(node).includes(text))[0]; +/** + * 展开「连接 URI」分组。 + * + * 该分组默认折叠:它的多行输入框很高,展开时会把 host/账号/密码这些必填项挤出首屏, + * 因此只有编辑已含 uri 的连接时才默认展开。断言 URI 相关文案或按钮的用例需先展开。 + */ +const expandUriSection = (renderer: ReactTestRenderer) => { + const toggle = renderer.root.findAll( + (node) => node.props?.["data-connection-config-section-toggle"] === "uri", + )[0]; + toggle?.props?.onClick?.({ stopPropagation: () => {} }); +}; + const flushConnectionTestTick = async () => { await new Promise((resolve) => setTimeout(resolve, 0)); await Promise.resolve(); @@ -899,6 +912,10 @@ describe("ConnectionModal i18n", () => { />, ); }); + // URI 分组默认折叠(见 expandUriSection 的说明),先展开再断言其占位示例。 + await act(async () => { + expandUriSection(renderer!); + }); expect(textContent(renderer!.toJSON())).toContain( "For example: postgres://user:pass@127.0.0.1:5432/db_name", ); @@ -988,6 +1005,10 @@ describe("ConnectionModal i18n", () => { findClickableCard(renderer!, "MySQL").props.onClick(); }); + await act(async () => { + expandUriSection(renderer!); + }); + await act(async () => { findButton(renderer!, "Generate URI").props.onClick(); }); diff --git a/frontend/src/components/ConnectionModal.tsx b/frontend/src/components/ConnectionModal.tsx index 359f2e38..6edef8b6 100644 --- a/frontend/src/components/ConnectionModal.tsx +++ b/frontend/src/components/ConnectionModal.tsx @@ -539,14 +539,19 @@ const ConnectionModal: React.FC<{ [overlayTheme], ); + // 弹窗内的区块容器去卡片化:原先是 14px 圆角 + 边框 + 独立底色, + // 而它同时用在左侧导航容器、右侧内容面板与网络安全的各个分组上, + // 再叠加内部分组各自的卡片,形成多层「卡片套卡片」,整页视觉噪声很高。 + // 现在只保留内边距,靠内部的小标题与分割线划分层次。 + // 保留原有内边距(间距不变,避免表单贴到容器边缘),只去掉边框、底色与圆角。 const modalInnerSectionStyle = useMemo( () => ({ padding: 14, - borderRadius: 14, - border: overlayTheme.sectionBorder, - background: overlayTheme.sectionBg, + borderRadius: 0, + border: "none", + background: "transparent", }), - [overlayTheme], + [], ); const modalMutedTextStyle = useMemo( @@ -759,18 +764,15 @@ const ConnectionModal: React.FC<{ ); + // 表单分组改用「小标题 + 细分割线」而不是卡片: + // 原先每个分组都是 16px 圆角 + 边框 + 内阴影 + 独立底色的卡片,一屏里叠七八个, + // 视觉噪声极高且每张卡各占 16px 内边距,把真正的字段挤得很散。 + // 去掉边框/圆角/底色/内阴影,只保留组间的一条上分割线来划分层次,字段密度明显提升。 const configSectionCardStyle = (): React.CSSProperties => ({ - padding: 16, - borderRadius: 16, - border: darkMode - ? "1px solid rgba(255,255,255,0.08)" - : "1px solid rgba(16,24,40,0.08)", - background: darkMode - ? "rgba(255,255,255,0.025)" - : "rgba(255,255,255,0.70)", - boxShadow: darkMode - ? "inset 0 1px 0 rgba(255,255,255,0.04)" - : "inset 0 1px 0 rgba(255,255,255,0.90)", + paddingTop: 18, + borderTop: darkMode + ? "1px solid rgba(255,255,255,0.07)" + : "1px solid rgba(16,24,40,0.07)", }); const renderConfigSectionCard = ({ diff --git a/frontend/src/components/connectionModal/ConnectionModalStep2.tsx b/frontend/src/components/connectionModal/ConnectionModalStep2.tsx index 5f8bdd90..2b725729 100644 --- a/frontend/src/components/connectionModal/ConnectionModalStep2.tsx +++ b/frontend/src/components/connectionModal/ConnectionModalStep2.tsx @@ -200,10 +200,21 @@ const ConnectionModalStep2: React.FC = (props) => { const [readOnlyProtectionExpanded, setReadOnlyProtectionExpanded] = React.useState(false); + // 连接 URI 属于「粘贴整串」的高级入口,默认折叠:它的多行输入框很高, + // 展开时会把 host/账号/密码这些必填项挤出首屏。 + // 已经填了 URI 的连接(编辑场景)默认展开,避免用户看不到自己已录入的值。 + const [uriSectionExpanded, setUriSectionExpanded] = React.useState( + () => String(initialValues?.uri || "").trim() !== "", + ); + React.useEffect(() => { setReadOnlyProtectionExpanded(false); }, [dbType]); + React.useEffect(() => { + setUriSectionExpanded(String(initialValues?.uri || "").trim() !== ""); + }, [dbType, initialValues?.uri]); + const renderStep2 = () => { const showConnectionReadOnlyField = supportsConnectionReadOnlyMode({ type: dbType, @@ -240,107 +251,6 @@ const ConnectionModalStep2: React.FC = (props) => {
- {renderConfigSectionCard({ - sectionKey: "identity", - icon: , - badge: ( - - {getConnectionConfigLayoutKindLabel(connectionConfigLayout.kind)} - - ), - children: ( - <> - - - - - - - - ), - })} - - {!isCustom && - !isJVM && - renderConfigSectionCard({ - sectionKey: "uri", - icon: , - children: ( - <> - - - - {supportsConnectionParams && ( - - - - )} - - - - - - {uriFeedback && ( - setUriFeedback(null)} - style={{ marginBottom: 16 }} - /> - )} - {renderStoredSecretControls({ - fieldName: "uri", - clearKey: "opaqueURI", - hasStoredSecret: initialValues?.hasOpaqueURI, - clearLabel: t("connection.modal.uri.stored.clear"), - description: t("connection.modal.uri.stored.description"), - })} - - ), - })} {isCustom ? ( <> @@ -2539,6 +2449,115 @@ const ConnectionModalStep2: React.FC = (props) => { })} )} + + {/* 必填项优先:目标地址、凭据、数据库范围由上方的条件分支渲染。 + 连接名称与预设类型属于可选的展示信息,连接 URI 属于「粘贴整串」的高级入口, + 两者都下沉到必填项之后,避免它们(尤其是 URI 的多行输入框)把 + host/账号/密码挤出首屏。 */} + {renderConfigSectionCard({ + sectionKey: "identity", + icon: , + badge: ( + + {getConnectionConfigLayoutKindLabel(connectionConfigLayout.kind)} + + ), + children: ( + <> + + + + + + + + ), + })} + + {!isCustom && + !isJVM && + renderConfigSectionCard({ + sectionKey: "uri", + icon: , + collapsible: true, + expanded: uriSectionExpanded, + onToggle: () => setUriSectionExpanded((expanded) => !expanded), + children: ( + <> + + + + {supportsConnectionParams && ( + + + + )} + + + + + + {uriFeedback && ( + setUriFeedback(null)} + style={{ marginBottom: 16 }} + /> + )} + {renderStoredSecretControls({ + fieldName: "uri", + clearKey: "opaqueURI", + hasStoredSecret: initialValues?.hasOpaqueURI, + clearLabel: t("connection.modal.uri.stored.clear"), + description: t("connection.modal.uri.stored.description"), + })} + + ), + })}
); @@ -3107,26 +3126,26 @@ const ConnectionModalStep2: React.FC = (props) => { key={item.key} type="button" onClick={() => setActiveConfigSection(item.key)} + /* 导航项改为扁平列表:原先每项都是 14px 圆角 + 边框 + 渐变底 + 投影的 + 独立卡片,三项叠在一个卡片容器里形成「卡片套卡片」,视觉噪声很高。 + 现在只用左侧强调条 + 淡底表示选中,去掉边框、圆角与投影。 */ style={{ textAlign: "left", - padding: "12px 12px 12px 14px", - borderRadius: 14, - border: `1px solid ${ + padding: "10px 10px 10px 12px", + borderRadius: 8, + border: "none", + borderLeft: `2px solid ${ active ? darkMode - ? "rgba(255,214,102,0.3)" - : "rgba(24,144,255,0.24)" - : darkMode - ? "rgba(255,255,255,0.045)" - : "rgba(16,24,40,0.055)" + ? "#ffd666" + : "#1677ff" + : "transparent" }`, background: active ? darkMode - ? "linear-gradient(180deg, rgba(255,214,102,0.12) 0%, rgba(255,214,102,0.06) 100%)" - : "linear-gradient(180deg, rgba(24,144,255,0.10) 0%, rgba(24,144,255,0.05) 100%)" - : darkMode - ? "rgba(255,255,255,0.02)" - : "rgba(255,255,255,0.7)", + ? "rgba(255,214,102,0.10)" + : "rgba(24,144,255,0.08)" + : "transparent", color: active ? darkMode ? "#f5f7ff" @@ -3135,12 +3154,8 @@ const ConnectionModalStep2: React.FC = (props) => { ? "rgba(255,255,255,0.76)" : "#3f4b5e", cursor: "pointer", - transition: "all 120ms ease", - boxShadow: active - ? darkMode - ? "0 10px 24px rgba(0,0,0,0.18)" - : "0 10px 22px rgba(24,144,255,0.08)" - : "none", + transition: "background 120ms ease, color 120ms ease", + boxShadow: "none", }} >