v0.9.7
parent
4b41763ef4
commit
fd10178367
|
|
@ -74,6 +74,18 @@
|
|||
transform: translateX(0) scale(1);
|
||||
}
|
||||
|
||||
.floating-toc.floating-toc-dismissed .floating-toc-tab {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.floating-toc.floating-toc-dismissed .floating-toc-panel {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateX(10px) scale(0.98);
|
||||
}
|
||||
|
||||
.floating-toc-header {
|
||||
min-height: 48px;
|
||||
padding: 0 16px;
|
||||
|
|
@ -167,6 +179,16 @@
|
|||
font-size: 13px;
|
||||
}
|
||||
|
||||
.floating-toc-drawer .ant-drawer-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.floating-toc-drawer .floating-toc-content {
|
||||
max-height: none;
|
||||
height: 100%;
|
||||
padding: 10px 8px 12px;
|
||||
}
|
||||
|
||||
body.dark .floating-toc-tab,
|
||||
body.dark .floating-toc-panel {
|
||||
box-shadow: 0 22px 48px rgba(0, 0, 0, 0.38);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
import { Anchor } from 'antd'
|
||||
import { useState } from 'react'
|
||||
import { Anchor, Drawer } from 'antd'
|
||||
import { FileTextOutlined, MenuOutlined } from '@ant-design/icons'
|
||||
import './FloatingToc.css'
|
||||
|
||||
export default function FloatingToc({
|
||||
items = [],
|
||||
getContainer,
|
||||
searchKeyword = '',
|
||||
renderTitle,
|
||||
className = '',
|
||||
}) {
|
||||
const anchorItems = items.map((item) => ({
|
||||
function buildAnchorItems(items, searchKeyword, renderTitle) {
|
||||
return items.map((item) => ({
|
||||
key: item.key,
|
||||
href: item.href,
|
||||
title: (
|
||||
|
|
@ -21,12 +16,74 @@ export default function FloatingToc({
|
|||
</div>
|
||||
),
|
||||
}))
|
||||
}
|
||||
|
||||
function TocContent({ items = [], getContainer, searchKeyword = '', renderTitle, onItemClick }) {
|
||||
const anchorItems = buildAnchorItems(items, searchKeyword, renderTitle)
|
||||
|
||||
return (
|
||||
<div className="floating-toc-content">
|
||||
{items.length > 0 ? (
|
||||
<Anchor
|
||||
affix={false}
|
||||
offsetTop={0}
|
||||
getContainer={getContainer}
|
||||
items={anchorItems}
|
||||
onClick={(e, link) => {
|
||||
window.setTimeout(() => onItemClick?.(link), 120)
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div className="floating-toc-empty">当前文档无标题</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function TocDrawer({
|
||||
open,
|
||||
onClose,
|
||||
items = [],
|
||||
getContainer,
|
||||
searchKeyword = '',
|
||||
renderTitle,
|
||||
}) {
|
||||
return (
|
||||
<Drawer
|
||||
title="文档索引"
|
||||
placement="right"
|
||||
onClose={onClose}
|
||||
open={open}
|
||||
width="82%"
|
||||
className="floating-toc-drawer"
|
||||
>
|
||||
<TocContent
|
||||
items={items}
|
||||
getContainer={getContainer}
|
||||
searchKeyword={searchKeyword}
|
||||
renderTitle={renderTitle}
|
||||
onItemClick={onClose}
|
||||
/>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
export default function FloatingToc({
|
||||
items = [],
|
||||
getContainer,
|
||||
searchKeyword = '',
|
||||
renderTitle,
|
||||
className = '',
|
||||
}) {
|
||||
const [dismissed, setDismissed] = useState(false)
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={`floating-toc ${className}`.trim()}
|
||||
className={`floating-toc ${dismissed ? 'floating-toc-dismissed' : ''} ${className}`.trim()}
|
||||
tabIndex={0}
|
||||
aria-label="文档索引"
|
||||
onMouseEnter={() => setDismissed(false)}
|
||||
onFocus={() => setDismissed(false)}
|
||||
>
|
||||
<div className="floating-toc-tab">
|
||||
<MenuOutlined />
|
||||
|
|
@ -37,18 +94,13 @@ export default function FloatingToc({
|
|||
<span>文档索引</span>
|
||||
{items.length > 0 && <span className="floating-toc-count">{items.length}</span>}
|
||||
</div>
|
||||
<div className="floating-toc-content">
|
||||
{items.length > 0 ? (
|
||||
<Anchor
|
||||
affix={false}
|
||||
offsetTop={0}
|
||||
getContainer={getContainer}
|
||||
items={anchorItems}
|
||||
/>
|
||||
) : (
|
||||
<div className="floating-toc-empty">当前文档无标题</div>
|
||||
)}
|
||||
</div>
|
||||
<TocContent
|
||||
items={items}
|
||||
getContainer={getContainer}
|
||||
searchKeyword={searchKeyword}
|
||||
renderTitle={renderTitle}
|
||||
onItemClick={() => setDismissed(true)}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,20 @@
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.pdf-toolbar-compact .ant-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
|
||||
.pdf-toolbar-compact .ant-btn:hover {
|
||||
background: var(--item-hover-bg);
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
.pdf-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useMemo, useRef, useEffect, useCallback } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Document, Page, pdfjs } from 'react-pdf'
|
||||
import { Button, Space, InputNumber, message, Spin } from 'antd'
|
||||
import { Button, Space, InputNumber, message, Spin, Tooltip } from 'antd'
|
||||
import {
|
||||
ZoomInOutlined,
|
||||
ZoomOutOutlined,
|
||||
|
|
@ -17,7 +17,7 @@ import './VirtualPDFViewer.css'
|
|||
// 配置 PDF.js worker
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf-worker/pdf.worker.min.mjs'
|
||||
|
||||
function VirtualPDFViewer({ url, filename, toolbarTarget }) {
|
||||
function VirtualPDFViewer({ url, filename, toolbarTarget, compactToolbar = false }) {
|
||||
const [numPages, setNumPages] = useState(null)
|
||||
const [scale, setScale] = useState(1.0)
|
||||
const [pdfOriginalSize, setPdfOriginalSize] = useState({ width: 595, height: 842 }) // 默认 A4
|
||||
|
|
@ -160,7 +160,31 @@ function VirtualPDFViewer({ url, filename, toolbarTarget }) {
|
|||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
const toolbar = (
|
||||
const toolbar = compactToolbar ? (
|
||||
<div className="pdf-toolbar pdf-toolbar-compact">
|
||||
<Space size={4}>
|
||||
<Tooltip title="回到顶部">
|
||||
<Button
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
onClick={scrollToTop}
|
||||
size="small"
|
||||
type="text"
|
||||
disabled={currentPage === 1}
|
||||
aria-label="回到顶部"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="下载PDF">
|
||||
<Button
|
||||
icon={<CloudDownloadOutlined />}
|
||||
onClick={handleDownload}
|
||||
size="small"
|
||||
type="text"
|
||||
aria-label="下载PDF"
|
||||
/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pdf-toolbar">
|
||||
<Space>
|
||||
<Button icon={<ZoomOutOutlined />} onClick={zoomOut} size="small">
|
||||
|
|
|
|||
|
|
@ -283,8 +283,7 @@
|
|||
background-color: var(--item-hover-bg);
|
||||
}
|
||||
|
||||
/* The fixed editor layout does not use Bytemd's built-in sidebar modes. */
|
||||
.bytemd-toolbar-right .bytemd-toolbar-icon:nth-child(1),
|
||||
/* Keep the TOC entry visible; hide the help sidebar button. */
|
||||
.bytemd-toolbar-right .bytemd-toolbar-icon:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { Layout, Modal, Input, Spin, Button, Space } from 'antd'
|
||||
import { CloseOutlined, LockOutlined, FileTextOutlined, FilePdfOutlined, VerticalAlignTopOutlined, CloudDownloadOutlined } from '@ant-design/icons'
|
||||
import { Layout, Modal, Input, Spin, Button, Space, Tooltip } from 'antd'
|
||||
import { CloseOutlined, LockOutlined, FileTextOutlined, FilePdfOutlined, VerticalAlignTopOutlined, CloudDownloadOutlined, MenuOutlined } from '@ant-design/icons'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import rehypeHighlight from 'rehype-highlight'
|
||||
|
|
@ -9,7 +9,7 @@ import rehypeSlug from 'rehype-slug'
|
|||
import 'highlight.js/styles/github.css'
|
||||
import GithubSlugger from 'github-slugger'
|
||||
import Toast from '@/components/Toast/Toast'
|
||||
import FloatingToc from '@/components/FloatingToc/FloatingToc'
|
||||
import FloatingToc, { TocDrawer } from '@/components/FloatingToc/FloatingToc'
|
||||
import VirtualPDFViewer from '@/components/PDFViewer/VirtualPDFViewer'
|
||||
import {
|
||||
getFileSharePublicInfo,
|
||||
|
|
@ -31,6 +31,7 @@ function FileSharePage() {
|
|||
const [loading, setLoading] = useState(true)
|
||||
const [isMobile, setIsMobile] = useState(false)
|
||||
const [tocItems, setTocItems] = useState([])
|
||||
const [tocDrawerVisible, setTocDrawerVisible] = useState(false)
|
||||
const [passwordModalVisible, setPasswordModalVisible] = useState(false)
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
|
|
@ -181,22 +182,54 @@ function FileSharePage() {
|
|||
<span className="preview-header-text">{headerLabel}</span>
|
||||
</h3>
|
||||
{contentInfo?.type === 'markdown' && (
|
||||
<Space className="preview-header-actions">
|
||||
<Button
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
onClick={scrollContentToTop}
|
||||
size="small"
|
||||
>
|
||||
回到顶部
|
||||
</Button>
|
||||
<Button
|
||||
icon={<CloudDownloadOutlined />}
|
||||
onClick={handleExportPDF}
|
||||
size="small"
|
||||
>
|
||||
下载PDF
|
||||
</Button>
|
||||
</Space>
|
||||
isMobile ? (
|
||||
<Space className="preview-header-actions preview-compact-actions" size={4}>
|
||||
<Tooltip title="回到顶部">
|
||||
<Button
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
onClick={scrollContentToTop}
|
||||
size="small"
|
||||
type="text"
|
||||
aria-label="回到顶部"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="下载PDF">
|
||||
<Button
|
||||
icon={<CloudDownloadOutlined />}
|
||||
onClick={handleExportPDF}
|
||||
size="small"
|
||||
type="text"
|
||||
aria-label="下载PDF"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="文档索引">
|
||||
<Button
|
||||
icon={<MenuOutlined />}
|
||||
onClick={() => setTocDrawerVisible(true)}
|
||||
size="small"
|
||||
type="text"
|
||||
aria-label="文档索引"
|
||||
/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
) : (
|
||||
<Space className="preview-header-actions">
|
||||
<Button
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
onClick={scrollContentToTop}
|
||||
size="small"
|
||||
>
|
||||
回到顶部
|
||||
</Button>
|
||||
<Button
|
||||
icon={<CloudDownloadOutlined />}
|
||||
onClick={handleExportPDF}
|
||||
size="small"
|
||||
>
|
||||
下载PDF
|
||||
</Button>
|
||||
</Space>
|
||||
)
|
||||
)}
|
||||
{contentInfo?.type === 'pdf' && <div className="preview-header-actions pdf-header-toolbar" ref={setPdfToolbarTarget} />}
|
||||
</div>
|
||||
|
|
@ -213,6 +246,7 @@ function FileSharePage() {
|
|||
url={contentInfo.document_url}
|
||||
filename={contentInfo.filename}
|
||||
toolbarTarget={pdfToolbarTarget}
|
||||
compactToolbar={isMobile}
|
||||
/>
|
||||
) : (
|
||||
<div className="markdown-body">
|
||||
|
|
@ -239,6 +273,13 @@ function FileSharePage() {
|
|||
</Layout>
|
||||
</div>
|
||||
|
||||
<TocDrawer
|
||||
open={tocDrawerVisible}
|
||||
onClose={() => setTocDrawerVisible(false)}
|
||||
items={tocItems}
|
||||
getContainer={() => contentRef.current}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title={<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}><LockOutlined /><span>访问验证</span></div>}
|
||||
open={passwordModalVisible}
|
||||
|
|
|
|||
|
|
@ -170,6 +170,29 @@
|
|||
gap: 16px;
|
||||
}
|
||||
|
||||
.preview-header-leading-actions {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.preview-header-leading-actions .ant-btn,
|
||||
.preview-compact-actions .ant-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
|
||||
.preview-header-leading-actions .ant-btn:hover,
|
||||
.preview-compact-actions .ant-btn:hover {
|
||||
background: var(--item-hover-bg);
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
.preview-header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -365,31 +388,23 @@
|
|||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* 移动端菜单按钮 */
|
||||
.mobile-menu-btn {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.mobile-close-btn {
|
||||
position: fixed;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
z-index: 1000;
|
||||
background: var(--header-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 999px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* 移动端响应式样式 */
|
||||
@media (max-width: 768px) {
|
||||
.preview-content-header {
|
||||
padding: 12px 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.file-share-content-header {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.preview-content-wrapper {
|
||||
padding: 16px;
|
||||
padding-top: 60px; /* 为移动端菜单按钮留出空间 */
|
||||
}
|
||||
|
||||
.preview-content-wrapper.pdf-mode {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
|
|
@ -444,6 +459,10 @@
|
|||
padding: 12px;
|
||||
}
|
||||
|
||||
.preview-content-wrapper.pdf-mode {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.markdown-body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState, useEffect, useRef, useMemo } from 'react'
|
||||
import { useParams, useSearchParams, useNavigate } from 'react-router-dom'
|
||||
import { Layout, Menu, Spin, Button, Modal, Input, Drawer, Empty, Tooltip, Space } from 'antd'
|
||||
import { FileTextOutlined, FolderOutlined, FolderOpenOutlined, FilePdfOutlined, LockOutlined, MenuOutlined, CloseOutlined, VerticalAlignTopOutlined, CloudDownloadOutlined } from '@ant-design/icons'
|
||||
import { FileTextOutlined, FolderOutlined, FolderOpenOutlined, FilePdfOutlined, LockOutlined, MenuOutlined, CloseOutlined, VerticalAlignTopOutlined, CloudDownloadOutlined, UnorderedListOutlined } from '@ant-design/icons'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import rehypeHighlight from 'rehype-highlight'
|
||||
|
|
@ -11,7 +11,7 @@ import Mark from 'mark.js'
|
|||
import Highlighter from 'react-highlight-words'
|
||||
import GithubSlugger from 'github-slugger'
|
||||
import Toast from '@/components/Toast/Toast'
|
||||
import FloatingToc from '@/components/FloatingToc/FloatingToc'
|
||||
import FloatingToc, { TocDrawer } from '@/components/FloatingToc/FloatingToc'
|
||||
import VirtualPDFViewer from '@/components/PDFViewer/VirtualPDFViewer'
|
||||
import {
|
||||
getProjectSharePublicInfo,
|
||||
|
|
@ -54,6 +54,7 @@ function ProjectSharePage() {
|
|||
const [password, setPassword] = useState('')
|
||||
const [siderCollapsed, setSiderCollapsed] = useState(false)
|
||||
const [mobileDrawerVisible, setMobileDrawerVisible] = useState(false)
|
||||
const [tocDrawerVisible, setTocDrawerVisible] = useState(false)
|
||||
const [isMobile, setIsMobile] = useState(false)
|
||||
const [pdfUrl, setPdfUrl] = useState('')
|
||||
const [pdfFilename, setPdfFilename] = useState('')
|
||||
|
|
@ -418,20 +419,6 @@ function ProjectSharePage() {
|
|||
<Layout className="preview-layout">
|
||||
{isMobile ? (
|
||||
<>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<CloseOutlined />}
|
||||
className="mobile-close-btn"
|
||||
onClick={handleClose}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<MenuOutlined />}
|
||||
className="mobile-menu-btn"
|
||||
onClick={() => setMobileDrawerVisible(true)}
|
||||
>
|
||||
目录索引
|
||||
</Button>
|
||||
<Drawer
|
||||
title={projectInfo?.name || '项目分享'}
|
||||
placement="left"
|
||||
|
|
@ -457,7 +444,10 @@ function ProjectSharePage() {
|
|||
openKeys={openKeys}
|
||||
onOpenChange={setOpenKeys}
|
||||
items={menuItems}
|
||||
onClick={({ key }) => openSharedFile(key)}
|
||||
onClick={({ key }) => {
|
||||
openSharedFile(key)
|
||||
setMobileDrawerVisible(false)
|
||||
}}
|
||||
className="preview-menu"
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -512,27 +502,81 @@ function ProjectSharePage() {
|
|||
<Layout className="preview-content-layout">
|
||||
<Content className="preview-content" ref={contentRef}>
|
||||
<div className="preview-content-header">
|
||||
{isMobile && (
|
||||
<div className="preview-header-leading-actions">
|
||||
<Tooltip title="关闭">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<CloseOutlined />}
|
||||
onClick={handleClose}
|
||||
size="small"
|
||||
aria-label="关闭项目分享"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="目录索引">
|
||||
<Button
|
||||
type="text"
|
||||
icon={<UnorderedListOutlined />}
|
||||
onClick={() => setMobileDrawerVisible(true)}
|
||||
size="small"
|
||||
aria-label="目录索引"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
<h3 className="preview-header-title">
|
||||
<HeaderIcon className="preview-header-icon" style={isHeaderPdf ? { color: '#f5222d' } : undefined} />
|
||||
<span className="preview-header-text">{headerLabel}</span>
|
||||
</h3>
|
||||
{viewMode === 'markdown' && (
|
||||
<Space className="preview-header-actions">
|
||||
<Button
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
onClick={scrollContentToTop}
|
||||
size="small"
|
||||
>
|
||||
回到顶部
|
||||
</Button>
|
||||
<Button
|
||||
icon={<CloudDownloadOutlined />}
|
||||
onClick={handleExportPDF}
|
||||
size="small"
|
||||
>
|
||||
下载PDF
|
||||
</Button>
|
||||
</Space>
|
||||
isMobile ? (
|
||||
<Space className="preview-header-actions preview-compact-actions" size={4}>
|
||||
<Tooltip title="回到顶部">
|
||||
<Button
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
onClick={scrollContentToTop}
|
||||
size="small"
|
||||
type="text"
|
||||
aria-label="回到顶部"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="下载PDF">
|
||||
<Button
|
||||
icon={<CloudDownloadOutlined />}
|
||||
onClick={handleExportPDF}
|
||||
size="small"
|
||||
type="text"
|
||||
aria-label="下载PDF"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="文档索引">
|
||||
<Button
|
||||
icon={<MenuOutlined />}
|
||||
onClick={() => setTocDrawerVisible(true)}
|
||||
size="small"
|
||||
type="text"
|
||||
aria-label="文档索引"
|
||||
/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
) : (
|
||||
<Space className="preview-header-actions">
|
||||
<Button
|
||||
icon={<VerticalAlignTopOutlined />}
|
||||
onClick={scrollContentToTop}
|
||||
size="small"
|
||||
>
|
||||
回到顶部
|
||||
</Button>
|
||||
<Button
|
||||
icon={<CloudDownloadOutlined />}
|
||||
onClick={handleExportPDF}
|
||||
size="small"
|
||||
>
|
||||
下载PDF
|
||||
</Button>
|
||||
</Space>
|
||||
)
|
||||
)}
|
||||
{viewMode === 'pdf' && <div className="preview-header-actions pdf-header-toolbar" ref={setPdfToolbarTarget} />}
|
||||
</div>
|
||||
|
|
@ -544,7 +588,7 @@ function ProjectSharePage() {
|
|||
</Spin>
|
||||
</div>
|
||||
) : viewMode === 'pdf' ? (
|
||||
<VirtualPDFViewer url={pdfUrl} filename={pdfFilename} toolbarTarget={pdfToolbarTarget} />
|
||||
<VirtualPDFViewer url={pdfUrl} filename={pdfFilename} toolbarTarget={pdfToolbarTarget} compactToolbar={isMobile} />
|
||||
) : (
|
||||
<div className="markdown-body" onClick={(e) => {
|
||||
if (e.defaultPrevented) return
|
||||
|
|
@ -578,6 +622,15 @@ function ProjectSharePage() {
|
|||
</Layout>
|
||||
</Layout>
|
||||
|
||||
<TocDrawer
|
||||
open={tocDrawerVisible}
|
||||
onClose={() => setTocDrawerVisible(false)}
|
||||
items={tocItems}
|
||||
searchKeyword={searchKeyword}
|
||||
getContainer={() => contentRef.current}
|
||||
renderTitle={(item, keyword) => <HighlightText text={item.title} keyword={keyword} />}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title={<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}><LockOutlined /><span>访问验证</span></div>}
|
||||
open={passwordModalVisible}
|
||||
|
|
|
|||
Loading…
Reference in New Issue