[feat]:[FL-179][优化文档查看页面布局和样式]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -66,33 +66,36 @@ export default function DocsViewPage() {
|
||||
});
|
||||
|
||||
const convertToMenuItems = useCallback((chapters: DocumentChapterTreeItem[]): MenuItem[] => {
|
||||
return chapters
|
||||
.filter((chapter) => {
|
||||
const hasPublishedDocs = chapter.documents?.some((doc) => doc.status === "published");
|
||||
const hasPublishedChildren = chapter.children?.some((child) =>
|
||||
child.documents?.some((doc) => doc.status === "published")
|
||||
);
|
||||
return hasPublishedDocs || hasPublishedChildren;
|
||||
})
|
||||
.map((chapter) => {
|
||||
const hasChildren = chapter.children && chapter.children.length > 0;
|
||||
const publishedDocs = chapter.documents?.filter((doc) => doc.status === "published") || [];
|
||||
const convert = (chapters: DocumentChapterTreeItem[]): MenuItem[] => {
|
||||
return chapters
|
||||
.filter((chapter) => {
|
||||
const hasPublishedDocs = chapter.documents?.some((doc) => doc.status === "published");
|
||||
const hasPublishedChildren = chapter.children?.some((child) =>
|
||||
child.documents?.some((doc) => doc.status === "published")
|
||||
);
|
||||
return hasPublishedDocs || hasPublishedChildren;
|
||||
})
|
||||
.map((chapter) => {
|
||||
const hasChildren = chapter.children && chapter.children.length > 0;
|
||||
const publishedDocs = chapter.documents?.filter((doc) => doc.status === "published") || [];
|
||||
|
||||
const docItems: MenuItem[] = publishedDocs.map((doc) => ({
|
||||
key: `doc-${doc.id}`,
|
||||
icon: <FileTextOutlined />,
|
||||
label: doc.title,
|
||||
}));
|
||||
const docItems: MenuItem[] = publishedDocs.map((doc) => ({
|
||||
key: `doc-${doc.id}`,
|
||||
icon: <FileTextOutlined />,
|
||||
label: doc.title,
|
||||
}));
|
||||
|
||||
const childItems = hasChildren ? convertToMenuItems(chapter.children) : [];
|
||||
const childItems = hasChildren ? convert(chapter.children) : [];
|
||||
|
||||
return {
|
||||
key: `chapter-${chapter.id}`,
|
||||
icon: <FolderOutlined />,
|
||||
label: chapter.name,
|
||||
children: [...docItems, ...childItems],
|
||||
};
|
||||
});
|
||||
return {
|
||||
key: `chapter-${chapter.id}`,
|
||||
icon: <FolderOutlined />,
|
||||
label: chapter.name,
|
||||
children: [...docItems, ...childItems],
|
||||
};
|
||||
});
|
||||
};
|
||||
return convert(chapters);
|
||||
}, []);
|
||||
|
||||
const handleMenuClick: MenuProps["onClick"] = useCallback((e) => {
|
||||
@@ -144,10 +147,14 @@ export default function DocsViewPage() {
|
||||
const menuContent = (
|
||||
<>
|
||||
<div className="admin-docs-view-sider-header">
|
||||
{!siderCollapsed && <Title level={4} style={{ margin: 0 }}>操作文档</Title>}
|
||||
{!siderCollapsed && (
|
||||
<Title level={4} style={{ margin: 0, fontSize: '16px', fontWeight: 600 }}>
|
||||
操作文档
|
||||
</Title>
|
||||
)}
|
||||
</div>
|
||||
{treeLoading ? (
|
||||
<div style={{ padding: "24px", textAlign: "center" }}>
|
||||
<div className="admin-docs-view-sider-loading">
|
||||
<Spin />
|
||||
</div>
|
||||
) : treeData && treeData.length > 0 ? (
|
||||
@@ -162,7 +169,7 @@ export default function DocsViewPage() {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ padding: "24px" }}>
|
||||
<div className="admin-docs-view-sider-empty">
|
||||
<Empty description="暂无文档" image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
</div>
|
||||
)}
|
||||
@@ -204,31 +211,82 @@ export default function DocsViewPage() {
|
||||
|
||||
<div className="admin-docs-view-content">
|
||||
{documentLoading ? (
|
||||
<div className="flex items-center justify-center" style={{ minHeight: 300 }}>
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<Spin size="large" />
|
||||
<Typography.Text type="secondary">加载文档中...</Typography.Text>
|
||||
</div>
|
||||
<div className="admin-docs-view-loading">
|
||||
<Spin size="large" />
|
||||
<Typography.Text type="secondary">加载文档中...</Typography.Text>
|
||||
</div>
|
||||
) : selectedDocument ? (
|
||||
<AntCard className="admin-docs-view-document-card">
|
||||
<Title level={2}>{selectedDocument.title}</Title>
|
||||
<AntCard className="admin-docs-view-document-card" bordered={false}>
|
||||
<div className="admin-docs-view-document-header">
|
||||
<Title level={2} style={{ marginBottom: 8 }}>
|
||||
{selectedDocument.title}
|
||||
</Title>
|
||||
</div>
|
||||
<div className="admin-docs-view-markdown-content">
|
||||
<ReactMarkdown
|
||||
components={{
|
||||
h1: ({ children }) => <Title level={2}>{children}</Title>,
|
||||
h2: ({ children }) => <Title level={3}>{children}</Title>,
|
||||
h3: ({ children }) => <Title level={4}>{children}</Title>,
|
||||
h4: ({ children }) => <Title level={5}>{children}</Title>,
|
||||
p: ({ children }) => <Paragraph>{children}</Paragraph>,
|
||||
h1: ({ children }) => (
|
||||
<Title level={2} style={{ marginTop: 32, marginBottom: 16 }}>
|
||||
{children}
|
||||
</Title>
|
||||
),
|
||||
h2: ({ children }) => (
|
||||
<Title level={3} style={{ marginTop: 28, marginBottom: 14 }}>
|
||||
{children}
|
||||
</Title>
|
||||
),
|
||||
h3: ({ children }) => (
|
||||
<Title level={4} style={{ marginTop: 24, marginBottom: 12 }}>
|
||||
{children}
|
||||
</Title>
|
||||
),
|
||||
h4: ({ children }) => (
|
||||
<Title level={5} style={{ marginTop: 20, marginBottom: 10 }}>
|
||||
{children}
|
||||
</Title>
|
||||
),
|
||||
p: ({ children }) => (
|
||||
<Paragraph style={{ marginBottom: 16 }}>
|
||||
{children}
|
||||
</Paragraph>
|
||||
),
|
||||
ul: ({ children }) => (
|
||||
<ul style={{ marginBottom: 16, paddingLeft: 24 }}>
|
||||
{children}
|
||||
</ul>
|
||||
),
|
||||
ol: ({ children }) => (
|
||||
<ol style={{ marginBottom: 16, paddingLeft: 24 }}>
|
||||
{children}
|
||||
</ol>
|
||||
),
|
||||
li: ({ children }) => (
|
||||
<li style={{ marginBottom: 8 }}>
|
||||
{children}
|
||||
</li>
|
||||
),
|
||||
blockquote: ({ children }) => (
|
||||
<blockquote className="admin-docs-view-blockquote">
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
code: ({ children, className }) => {
|
||||
const isBlock = className?.includes("language-");
|
||||
return isBlock ? (
|
||||
<pre><code>{children}</code></pre>
|
||||
<pre className="admin-docs-view-code-block">
|
||||
<code>{children}</code>
|
||||
</pre>
|
||||
) : (
|
||||
<code>{children}</code>
|
||||
<code className="admin-docs-view-inline-code">{children}</code>
|
||||
);
|
||||
},
|
||||
table: ({ children }) => (
|
||||
<div className="admin-docs-view-table-wrapper">
|
||||
<table className="admin-docs-view-table">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{selectedDocument.content}
|
||||
@@ -236,7 +294,7 @@ export default function DocsViewPage() {
|
||||
</div>
|
||||
</AntCard>
|
||||
) : (
|
||||
<div className="flex items-center justify-center" style={{ minHeight: 300 }}>
|
||||
<div className="admin-docs-view-empty">
|
||||
<Empty
|
||||
description="请从左侧目录选择要查看的文档"
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
|
||||
+186
-40
@@ -1451,14 +1451,18 @@ body {
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-sider {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 280px;
|
||||
border-right: 1px solid var(--ant-color-border-secondary);
|
||||
background: var(--fquiz-theme-bg-container);
|
||||
overflow-y: auto;
|
||||
background: var(--fquiz-theme-bg-elevated);
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.admin-docs-view-sider.collapsed {
|
||||
@@ -1466,14 +1470,31 @@ body {
|
||||
}
|
||||
|
||||
.admin-docs-view-sider-header {
|
||||
padding: 16px;
|
||||
padding: 20px 16px;
|
||||
border-bottom: 1px solid var(--ant-color-border-secondary);
|
||||
background: color-mix(in srgb, var(--fquiz-theme-primary) 4%, transparent);
|
||||
background: color-mix(in srgb, var(--fquiz-theme-primary) 5%, transparent);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-sider-menu {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-sider-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40px 24px;
|
||||
}
|
||||
|
||||
.admin-docs-view-sider-empty {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40px 24px;
|
||||
}
|
||||
|
||||
.admin-docs-view-sider-footer {
|
||||
@@ -1481,70 +1502,176 @@ body {
|
||||
justify-content: center;
|
||||
border-top: 1px solid var(--ant-color-border-secondary);
|
||||
padding: 12px;
|
||||
background: var(--fquiz-theme-bg-elevated);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-content {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 24px;
|
||||
background: var(--fquiz-theme-bg-container);
|
||||
padding: 32px;
|
||||
background: var(--fquiz-theme-bg-layout);
|
||||
}
|
||||
|
||||
.admin-docs-view-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.admin-docs-view-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.admin-docs-view-document-card {
|
||||
border-color: color-mix(in srgb, var(--fquiz-theme-primary) 20%, var(--ant-color-border-secondary));
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
color-mix(in srgb, var(--fquiz-theme-bg-container) 98%, var(--fquiz-theme-primary) 2%) 0%,
|
||||
var(--fquiz-theme-bg-container) 100%
|
||||
);
|
||||
box-shadow: 0 4px 12px color-mix(in srgb, var(--fquiz-theme-text-primary) 6%, transparent);
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
border: none;
|
||||
border-radius: var(--ant-border-radius-lg);
|
||||
background: var(--fquiz-theme-bg-container);
|
||||
box-shadow: 0 2px 8px color-mix(in srgb, var(--fquiz-theme-text-primary) 4%, transparent);
|
||||
}
|
||||
|
||||
.admin-docs-view-document-card > .ant-card-head {
|
||||
border-bottom-color: color-mix(in srgb, var(--fquiz-theme-primary) 15%, var(--ant-color-border-secondary));
|
||||
background: color-mix(in srgb, var(--fquiz-theme-primary) 4%, transparent);
|
||||
.admin-docs-view-document-header {
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 2px solid var(--ant-color-border-secondary);
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content {
|
||||
margin-top: 24px;
|
||||
line-height: 1.8;
|
||||
line-height: 1.75;
|
||||
font-size: 15px;
|
||||
color: var(--ant-color-text);
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content pre {
|
||||
.admin-docs-view-markdown-content > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content strong {
|
||||
font-weight: 600;
|
||||
color: var(--ant-color-text);
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content a {
|
||||
color: var(--ant-color-primary);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content a:hover {
|
||||
color: var(--ant-color-primary-hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: var(--ant-border-radius);
|
||||
margin: 20px 0;
|
||||
box-shadow: 0 2px 8px color-mix(in srgb, var(--fquiz-theme-text-primary) 8%, transparent);
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content hr {
|
||||
border: none;
|
||||
border-top: 1px solid var(--ant-color-border-secondary);
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-blockquote {
|
||||
margin: 20px 0;
|
||||
padding: 12px 20px;
|
||||
border-left: 4px solid var(--ant-color-primary);
|
||||
background: color-mix(in srgb, var(--ant-color-primary) 6%, transparent);
|
||||
border-radius: 0 var(--ant-border-radius) var(--ant-border-radius) 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-blockquote p {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.admin-docs-view-code-block {
|
||||
background: var(--ant-color-bg-layout);
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
padding: 16px 20px;
|
||||
border-radius: var(--ant-border-radius);
|
||||
overflow-x: auto;
|
||||
margin: 20px 0;
|
||||
border: 1px solid var(--ant-color-border-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content code {
|
||||
background: var(--ant-color-bg-layout);
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content pre code {
|
||||
.admin-docs-view-code-block code {
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
font-size: 14px;
|
||||
color: var(--ant-color-text);
|
||||
}
|
||||
|
||||
.admin-docs-view-inline-code {
|
||||
background: color-mix(in srgb, var(--ant-color-primary) 10%, transparent);
|
||||
color: var(--ant-color-primary);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.admin-docs-view-table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.admin-docs-view-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid var(--ant-color-border-secondary);
|
||||
border-radius: var(--ant-border-radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.admin-docs-view-table th,
|
||||
.admin-docs-view-table td {
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--ant-color-border-secondary);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.admin-docs-view-table th {
|
||||
background: var(--fquiz-theme-table-header-bg);
|
||||
font-weight: 600;
|
||||
color: var(--ant-color-text);
|
||||
}
|
||||
|
||||
.admin-docs-view-table tr:hover {
|
||||
background: color-mix(in srgb, var(--ant-color-primary) 4%, transparent);
|
||||
}
|
||||
|
||||
/* 暗色主题适配 */
|
||||
:root[data-fquiz-theme="dark"] .admin-docs-view-document-card {
|
||||
border-color: color-mix(in srgb, var(--fquiz-theme-primary) 30%, var(--ant-color-border-secondary)) !important;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
color-mix(in srgb, var(--ant-color-bg-container) 94%, var(--fquiz-theme-primary) 6%) 0%,
|
||||
var(--ant-color-bg-container) 100%
|
||||
) !important;
|
||||
box-shadow: 0 6px 16px color-mix(in srgb, black 35%, transparent) !important;
|
||||
background: var(--ant-color-bg-container);
|
||||
box-shadow: 0 2px 8px color-mix(in srgb, black 30%, transparent);
|
||||
}
|
||||
|
||||
:root[data-fquiz-theme="dark"] .admin-docs-view-document-card > .ant-card-head {
|
||||
border-bottom-color: color-mix(in srgb, var(--fquiz-theme-primary) 22%, var(--ant-color-border-secondary)) !important;
|
||||
background: color-mix(in srgb, var(--fquiz-theme-primary) 8%, transparent) !important;
|
||||
:root[data-fquiz-theme="dark"] .admin-docs-view-code-block {
|
||||
background: color-mix(in srgb, var(--ant-color-bg-container) 70%, black);
|
||||
border-color: color-mix(in srgb, var(--ant-color-border-secondary) 60%, transparent);
|
||||
}
|
||||
|
||||
:root[data-fquiz-theme="dark"] .admin-docs-view-table th {
|
||||
background: color-mix(in srgb, var(--ant-color-bg-container) 85%, black);
|
||||
}
|
||||
|
||||
/* 移动端适配 */
|
||||
@@ -1552,6 +1679,25 @@ body {
|
||||
.admin-docs-view-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.admin-docs-view-document-card {
|
||||
border-radius: var(--ant-border-radius);
|
||||
}
|
||||
|
||||
.admin-docs-view-markdown-content {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.admin-docs-view-code-block {
|
||||
padding: 12px 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.admin-docs-view-table th,
|
||||
.admin-docs-view-table td {
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 表格边框优化 - 仅外边框,上下圆角对齐 */
|
||||
|
||||
Reference in New Issue
Block a user