修复bug

main
mula.liu 2026-08-31 18:13:18 +08:00
parent b3f5fb00b7
commit fef17e59e4
5 changed files with 116 additions and 31 deletions

View File

@ -28,6 +28,33 @@
height: 100%; height: 100%;
} }
/* 侧栏收起时:左侧悬浮展开按钮 */
.docs-sider-expand-btn {
position: fixed;
left: 12px;
top: 50%;
transform: translateY(-50%);
z-index: 30;
width: 32px;
height: 48px;
border-radius: 8px;
border: 1px solid var(--border-color);
background: var(--header-bg);
color: var(--text-color-secondary);
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
transition: color 0.2s ease, border-color 0.2s ease;
}
.docs-sider-expand-btn:hover {
color: var(--link-color);
border-color: var(--link-color);
}
.docs-sider-header { .docs-sider-header {
padding: 12px 10px 12px; padding: 12px 10px 12px;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);

View File

@ -1,7 +1,7 @@
import { useState, useEffect, useRef, useMemo } from 'react' import { useState, useEffect, useRef, useMemo } from 'react'
import { useParams, useNavigate, useSearchParams } from 'react-router-dom' import { useParams, useNavigate, useSearchParams } from 'react-router-dom'
import { Layout, Spin, Button, Tooltip, Modal, Input, Space, Dropdown, Empty, Switch, Select, TreeSelect, Radio, Alert } from 'antd' import { Layout, Spin, Button, Tooltip, Modal, Input, Space, Dropdown, Empty, Switch, Select, TreeSelect, Radio, Alert } from 'antd'
import { ShareAltOutlined, FileTextOutlined, FolderOutlined, FolderOpenOutlined, FilePdfOutlined, CopyOutlined, CloudDownloadOutlined, CloudUploadOutlined, ArrowLeftOutlined, ReloadOutlined, VerticalAlignTopOutlined } from '@ant-design/icons' import { ShareAltOutlined, FileTextOutlined, FolderOutlined, FolderOpenOutlined, FilePdfOutlined, CopyOutlined, CloudDownloadOutlined, CloudUploadOutlined, ArrowLeftOutlined, ReloadOutlined, VerticalAlignTopOutlined, MenuUnfoldOutlined } from '@ant-design/icons'
import ReactMarkdown from 'react-markdown' import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm' import remarkGfm from 'remark-gfm'
import rehypeRaw from 'rehype-raw' import rehypeRaw from 'rehype-raw'
@ -61,6 +61,7 @@ function DocumentPage() {
const [pdfUrl, setPdfUrl] = useState('') const [pdfUrl, setPdfUrl] = useState('')
const [pdfFilename, setPdfFilename] = useState('') const [pdfFilename, setPdfFilename] = useState('')
const [viewMode, setViewMode] = useState('markdown') const [viewMode, setViewMode] = useState('markdown')
const [siderCollapsed, setSiderCollapsed] = useState(false)
const [gitRepos, setGitRepos] = useState([]) const [gitRepos, setGitRepos] = useState([])
// Git // Git
const [gitSyncModalVisible, setGitSyncModalVisible] = useState(false) const [gitSyncModalVisible, setGitSyncModalVisible] = useState(false)
@ -131,6 +132,7 @@ function DocumentPage() {
setMarkdownContent('') setMarkdownContent('')
setTocItems([]) setTocItems([])
setViewMode('folder') setViewMode('folder')
setSiderCollapsed(true)
expandParentFolders(`${folderPath}/placeholder`) expandParentFolders(`${folderPath}/placeholder`)
// //
@ -149,6 +151,7 @@ function DocumentPage() {
setMarkdownContent('') setMarkdownContent('')
setTocItems([]) setTocItems([])
setViewMode('folder') setViewMode('folder')
setSiderCollapsed(true)
setOpenKeys([]) setOpenKeys([])
if (syncUrl) { if (syncUrl) {
@ -1259,7 +1262,7 @@ function DocumentPage() {
<div className="project-docs-page"> <div className="project-docs-page">
<Layout className="docs-layout"> <Layout className="docs-layout">
{/* 左侧目录 */} {/* 左侧目录 */}
<Sider width={280} className="docs-sider" theme="light"> <Sider width={280} className="docs-sider" theme="light" collapsed={siderCollapsed} collapsedWidth={0}>
<div className="docs-sider-header"> <div className="docs-sider-header">
<div className="docs-sider-title-row"> <div className="docs-sider-title-row">
<button <button
@ -1339,6 +1342,19 @@ function DocumentPage() {
/> />
</Sider> </Sider>
{/* 侧栏收起时:悬浮展开按钮 */}
{siderCollapsed && (
<button
type="button"
className="docs-sider-expand-btn"
onClick={() => setSiderCollapsed(false)}
aria-label="展开目录"
title="展开目录"
>
<MenuUnfoldOutlined />
</button>
)}
{/* 右侧内容区 */} {/* 右侧内容区 */}
<Layout className="docs-content-layout"> <Layout className="docs-content-layout">
<Content className="docs-content" ref={contentRef}> <Content className="docs-content" ref={contentRef}>

View File

@ -37,6 +37,32 @@ function FileSharePage() {
const contentRef = useRef(null) const contentRef = useRef(null)
const largeMarkdownRef = useRef(null) const largeMarkdownRef = useRef(null)
const [headerVisible, setHeaderVisible] = useState(false) const [headerVisible, setHeaderVisible] = useState(false)
const headerHoveringRef = useRef(false)
const headerHideTimerRef = useRef(null)
const clearHeaderHideTimer = () => {
if (headerHideTimerRef.current) {
clearTimeout(headerHideTimerRef.current)
headerHideTimerRef.current = null
}
}
// Header
const scheduleHeaderHide = () => {
clearHeaderHideTimer()
if (headerHoveringRef.current) return
headerHideTimerRef.current = setTimeout(() => setHeaderVisible(false), 1200)
}
// Header
const handleHeaderMouseEnter = () => {
headerHoveringRef.current = true
clearHeaderHideTimer()
}
// Header
const handleHeaderMouseLeave = () => {
headerHoveringRef.current = false
const root = contentRef.current
const target = root ? findScrollableEl(root) : null
if (target && target.scrollTop > 8) scheduleHeaderHide()
}
const [pdfToolbarTarget, setPdfToolbarTarget] = useState(null) const [pdfToolbarTarget, setPdfToolbarTarget] = useState(null)
const [shareInfo, setShareInfo] = useState(null) const [shareInfo, setShareInfo] = useState(null)
const [contentInfo, setContentInfo] = useState(null) const [contentInfo, setContentInfo] = useState(null)
@ -60,30 +86,22 @@ function FileSharePage() {
return () => window.removeEventListener('resize', checkMobile) return () => window.removeEventListener('resize', checkMobile)
}, []) }, [])
// Header PDF/ // Header
useEffect(() => { useEffect(() => {
const root = contentRef.current const root = contentRef.current
if (!root) return if (!root) return
const hideTimer = { current: null }
const clearHideTimer = () => {
if (hideTimer.current) {
clearTimeout(hideTimer.current)
hideTimer.current = null
}
}
const sync = (scrollEl = null) => { const sync = (scrollEl = null) => {
const target = scrollEl || findScrollableEl(root) const target = scrollEl || findScrollableEl(root)
if (!target) { if (!target) {
clearHideTimer() clearHeaderHideTimer()
setHeaderVisible(true) setHeaderVisible(true)
return return
} }
if (target.scrollTop > 8) { if (target.scrollTop > 8) {
setHeaderVisible(true) setHeaderVisible(true)
clearHideTimer() scheduleHeaderHide()
hideTimer.current = setTimeout(() => setHeaderVisible(false), 1200)
} else { } else {
clearHideTimer() clearHeaderHideTimer()
setHeaderVisible(false) setHeaderVisible(false)
} }
} }
@ -92,7 +110,7 @@ function FileSharePage() {
sync() sync()
return () => { return () => {
root.removeEventListener('scroll', onScroll, true) root.removeEventListener('scroll', onScroll, true)
clearHideTimer() clearHeaderHideTimer()
} }
}, [contentInfo?.type, isLargeMarkdown, loading]) }, [contentInfo?.type, isLargeMarkdown, loading])
@ -222,7 +240,10 @@ function FileSharePage() {
<div className="file-share-shell"> <div className="file-share-shell">
<Layout className="file-share-content-layout"> <Layout className="file-share-content-layout">
<Content className="file-share-content" ref={contentRef}> <Content className="file-share-content" ref={contentRef}>
<div className={`preview-content-header file-share-content-header${headerVisible ? '' : ' preview-header-hidden'}`}> <div
onMouseEnter={handleHeaderMouseEnter}
onMouseLeave={handleHeaderMouseLeave}
className={`preview-content-header file-share-content-header${headerVisible ? '' : ' preview-header-hidden'}`}>
<button <button
type="button" type="button"
className="project-back-button" className="project-back-button"

View File

@ -77,6 +77,32 @@ function ProjectSharePage() {
const contentRef = useRef(null) const contentRef = useRef(null)
const viewerRef = useRef(null) const viewerRef = useRef(null)
const [headerVisible, setHeaderVisible] = useState(false) const [headerVisible, setHeaderVisible] = useState(false)
const headerHoveringRef = useRef(false)
const headerHideTimerRef = useRef(null)
const clearHeaderHideTimer = () => {
if (headerHideTimerRef.current) {
clearTimeout(headerHideTimerRef.current)
headerHideTimerRef.current = null
}
}
// Header
const scheduleHeaderHide = () => {
clearHeaderHideTimer()
if (headerHoveringRef.current) return
headerHideTimerRef.current = setTimeout(() => setHeaderVisible(false), 1200)
}
// Header
const handleHeaderMouseEnter = () => {
headerHoveringRef.current = true
clearHeaderHideTimer()
}
// Header
const handleHeaderMouseLeave = () => {
headerHoveringRef.current = false
const root = contentRef.current
const target = root ? findScrollableEl(root) : null
if (target && target.scrollTop > 8) scheduleHeaderHide()
}
const largeMarkdownRef = useRef(null) const largeMarkdownRef = useRef(null)
const [pdfToolbarTarget, setPdfToolbarTarget] = useState(null) const [pdfToolbarTarget, setPdfToolbarTarget] = useState(null)
const isLargeMarkdown = isLargeMarkdownContent(markdownContent) const isLargeMarkdown = isLargeMarkdownContent(markdownContent)
@ -96,30 +122,22 @@ function ProjectSharePage() {
return () => window.removeEventListener('resize', checkMobile) return () => window.removeEventListener('resize', checkMobile)
}, []) }, [])
// Header PDF/ // Header
useEffect(() => { useEffect(() => {
const root = contentRef.current const root = contentRef.current
if (!root) return if (!root) return
const hideTimer = { current: null }
const clearHideTimer = () => {
if (hideTimer.current) {
clearTimeout(hideTimer.current)
hideTimer.current = null
}
}
const sync = (scrollEl = null) => { const sync = (scrollEl = null) => {
const target = scrollEl || findScrollableEl(root) const target = scrollEl || findScrollableEl(root)
if (!target) { if (!target) {
clearHideTimer() clearHeaderHideTimer()
setHeaderVisible(true) setHeaderVisible(true)
return return
} }
if (target.scrollTop > 8) { if (target.scrollTop > 8) {
setHeaderVisible(true) setHeaderVisible(true)
clearHideTimer() scheduleHeaderHide()
hideTimer.current = setTimeout(() => setHeaderVisible(false), 1200)
} else { } else {
clearHideTimer() clearHeaderHideTimer()
setHeaderVisible(false) setHeaderVisible(false)
} }
} }
@ -128,7 +146,7 @@ function ProjectSharePage() {
sync() sync()
return () => { return () => {
root.removeEventListener('scroll', onScroll, true) root.removeEventListener('scroll', onScroll, true)
clearHideTimer() clearHeaderHideTimer()
} }
}, [viewMode, isLargeMarkdown, loading]) }, [viewMode, isLargeMarkdown, loading])
@ -711,7 +729,10 @@ function ProjectSharePage() {
<Layout className="preview-content-layout"> <Layout className="preview-content-layout">
<Content className="preview-content" ref={contentRef}> <Content className="preview-content" ref={contentRef}>
<div className={`preview-content-header${headerVisible ? '' : ' preview-header-hidden'}`}> <div
onMouseEnter={handleHeaderMouseEnter}
onMouseLeave={handleHeaderMouseLeave}
className={`preview-content-header${headerVisible ? '' : ' preview-header-hidden'}`}>
{isMobile && ( {isMobile && (
<div className="preview-header-leading-actions"> <div className="preview-header-leading-actions">
<Tooltip title="关闭"> <Tooltip title="关闭">

View File

@ -46,7 +46,7 @@ function ProjectList({ type = 'my' }) {
// -> // ->
const projectRoleLabel = (role) => { const projectRoleLabel = (role) => {
const roleMap = { admin: '管理', editor: '编辑者', viewer: '查看者' } const roleMap = { admin: '管理', editor: '编辑者', viewer: '查看者' }
return roleMap[role] || role || '查看者' return roleMap[role] || role || '查看者'
} }