🐛 fix(tool-center): 移除内嵌工具重复标题头

- 工具中心详情页统一承载入口标题和说明
- 数据同步、表结构比对和数据比对嵌入时隐藏内部 hero
- 数据目录、快捷键、连接包和安全更新嵌入时隐藏内部 Modal 标题
- 补充工具中心全 pane 标题归属回归断言
This commit is contained in:
Syngnat
2026-06-25 18:42:53 +08:00
parent bdb60a656a
commit 8dde4c3c6d
8 changed files with 138 additions and 73 deletions

View File

@@ -64,9 +64,10 @@ export default function ConnectionPackagePasswordModal({
<Modal
open={open}
embedded={embedded}
title={(
title={embedded ? null : (
<span style={{ minWidth: 0 }}>{title}</span>
)}
closable={embedded ? false : undefined}
onCancel={onCancel}
destroyOnHidden={false}
maskClosable={false}

View File

@@ -109,4 +109,10 @@ describe("DataSyncModal i18n", () => {
/tr\(\s*(['"])data_sync\.compare_entry\.preview\.sql\.data_help\1/,
);
});
it('hides the modal hero when embedded in the tool center', () => {
expect(source).toContain('{!embedded && (');
expect(source).toContain('<div style={heroPanelStyle}>');
expect(source).toMatch(/embedded\s*\?\s*\(\s*dataSyncContent\s*\)/);
});
});

View File

@@ -1388,71 +1388,73 @@ const DataSyncModal: React.FC<{
const dataSyncContent = (
<div style={modalWorkspaceStyle}>
<div style={{ flex: "0 0 auto" }}>
<div style={heroPanelStyle}>
<div
style={{
display: "flex",
justifyContent: "space-between",
gap: 12,
alignItems: "flex-start",
flexWrap: "wrap",
}}
>
<div style={{ minWidth: 0 }}>
<div
style={{
fontSize: 18,
fontWeight: 700,
color: darkMode ? "#f8fafc" : "#0f172a",
}}
>
{isMigrationWorkflow
? tr("data_sync.title.migration")
: isCompareEntry
? entryPresentation.heroTitle
: tr("data_sync.title.sync")}
{!embedded && (
<div style={heroPanelStyle}>
<div
style={{
display: "flex",
justifyContent: "space-between",
gap: 12,
alignItems: "flex-start",
flexWrap: "wrap",
}}
>
<div style={{ minWidth: 0 }}>
<div
style={{
fontSize: 18,
fontWeight: 700,
color: darkMode ? "#f8fafc" : "#0f172a",
}}
>
{isMigrationWorkflow
? tr("data_sync.title.migration")
: isCompareEntry
? entryPresentation.heroTitle
: tr("data_sync.title.sync")}
</div>
<div
style={{
marginTop: 6,
fontSize: 13,
lineHeight: 1.7,
color: darkMode
? "rgba(255,255,255,0.62)"
: "rgba(15,23,42,0.62)",
}}
>
{isMigrationWorkflow
? tr("data_sync.title.migration_description")
: isCompareEntry
? entryPresentation.heroDescription
: tr("data_sync.title.sync_description")}
</div>
</div>
<div
style={{
marginTop: 6,
fontSize: 13,
lineHeight: 1.7,
color: darkMode
? "rgba(255,255,255,0.62)"
: "rgba(15,23,42,0.62)",
}}
>
{isMigrationWorkflow
? tr("data_sync.title.migration_description")
: isCompareEntry
? entryPresentation.heroDescription
: tr("data_sync.title.sync_description")}
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
<span style={badgeStyle}>
{isMigrationWorkflow ? <RocketOutlined /> : <SwapOutlined />}{" "}
{isMigrationWorkflow
? tr("data_sync.badge.migration_mode")
: isCompareEntry
? entryPresentation.badgeText
: tr("data_sync.badge.sync_mode")}
</span>
<span style={badgeStyle}>
<DatabaseOutlined />{" "}
{sourceConnId
? tr("data_sync.badge.source_selected")
: tr("data_sync.badge.source_pending")}
</span>
<span style={badgeStyle}>
<TableOutlined />{" "}
{tr("data_sync.badge.table_count", {
count: selectedTables.length || 0,
})}
</span>
</div>
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
<span style={badgeStyle}>
{isMigrationWorkflow ? <RocketOutlined /> : <SwapOutlined />}{" "}
{isMigrationWorkflow
? tr("data_sync.badge.migration_mode")
: isCompareEntry
? entryPresentation.badgeText
: tr("data_sync.badge.sync_mode")}
</span>
<span style={badgeStyle}>
<DatabaseOutlined />{" "}
{sourceConnId
? tr("data_sync.badge.source_selected")
: tr("data_sync.badge.source_pending")}
</span>
<span style={badgeStyle}>
<TableOutlined />{" "}
{tr("data_sync.badge.table_count", {
count: selectedTables.length || 0,
})}
</span>
</div>
</div>
</div>
)}
<Steps current={currentStep} style={{ marginBottom: 24 }}>
<Step title={tr("data_sync.step.configure")} />
<Step title={tr("data_sync.step.select_tables")} />

View File

@@ -134,6 +134,11 @@ describe('SecurityUpdateSettingsModal i18n source guards', () => {
expect(source).not.toContain('当前项需要进一步处理后才能完成安全更新。');
});
it('lets the tool center provide the title when embedded', () => {
expect(source).toContain('title={embedded ? null : (');
expect(source).toContain('closable={embedded ? false : undefined}');
});
it('localizes settings chrome while preserving raw issue details, backup path and error text', async () => {
const modalText = await renderSettingsModalText();
expect(modalText).toContain('Security Update');

View File

@@ -130,7 +130,7 @@ const SecurityUpdateSettingsModal = ({
return (
<Modal
rootClassName={SECURITY_UPDATE_MODAL_CLASS}
title={(
title={embedded ? null : (
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, minWidth: 0 }}>
<div
style={{
@@ -159,6 +159,7 @@ const SecurityUpdateSettingsModal = ({
)}
open={open}
embedded={embedded}
closable={embedded ? false : undefined}
onCancel={onClose}
footer={[
showRetry ? (