修复bug
parent
b3f5fb00b7
commit
fef17e59e4
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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}>
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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="关闭">
|
||||||
|
|
|
||||||
|
|
@ -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 || '查看者'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue