import { useState, useEffect, useRef } from 'react'
import { useParams } from 'react-router-dom'
import { Layout, Menu, Spin, FloatButton, Button, Modal, Input, message, Drawer, Anchor } from 'antd'
import { VerticalAlignTopOutlined, MenuOutlined, MenuFoldOutlined, MenuUnfoldOutlined, FileTextOutlined, FolderOutlined, FilePdfOutlined, LockOutlined } from '@ant-design/icons'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import rehypeRaw from 'rehype-raw'
import rehypeSlug from 'rehype-slug'
import rehypeHighlight from 'rehype-highlight'
import 'highlight.js/styles/github.css'
import { getPreviewInfo, getPreviewTree, getPreviewFile, verifyAccessPassword, getPreviewDocumentUrl } from '@/api/share'
import VirtualPDFViewer from '@/components/PDFViewer/VirtualPDFViewer'
import './PreviewPage.css'
const { Sider, Content } = Layout
function PreviewPage() {
const { projectId } = useParams()
const [projectInfo, setProjectInfo] = useState(null)
const [fileTree, setFileTree] = useState([])
const [selectedFile, setSelectedFile] = useState('')
const [markdownContent, setMarkdownContent] = useState('')
const [loading, setLoading] = useState(false)
const [openKeys, setOpenKeys] = useState([])
const [tocCollapsed, setTocCollapsed] = useState(false)
const [tocItems, setTocItems] = useState([])
const [passwordModalVisible, setPasswordModalVisible] = useState(false)
const [password, setPassword] = useState('')
const [accessPassword, setAccessPassword] = useState(null) // 已验证的密码
const [siderCollapsed, setSiderCollapsed] = useState(false)
const [mobileDrawerVisible, setMobileDrawerVisible] = useState(false)
const [isMobile, setIsMobile] = useState(false)
const [pdfViewerVisible, setPdfViewerVisible] = useState(false)
const [pdfUrl, setPdfUrl] = useState('')
const [pdfFilename, setPdfFilename] = useState('')
const [viewMode, setViewMode] = useState('markdown') // 'markdown' or 'pdf'
const contentRef = useRef(null)
// 检测是否为移动设备
useEffect(() => {
const checkMobile = () => {
setIsMobile(window.innerWidth < 768)
}
checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])
useEffect(() => {
loadProjectInfo()
}, [projectId])
// 加载项目基本信息
const loadProjectInfo = async () => {
try {
const res = await getPreviewInfo(projectId)
const info = res.data
setProjectInfo(info)
if (info.has_password) {
// 需要密码验证
setPasswordModalVisible(true)
} else {
// 无需密码,直接加载文档树
loadFileTree()
}
} catch (error) {
console.error('Load project info error:', error)
message.error('项目不存在或已被删除')
}
}
// 验证密码
const handleVerifyPassword = async () => {
if (!password.trim()) {
message.warning('请输入访问密码')
return
}
try {
await verifyAccessPassword(projectId, password)
setAccessPassword(password)
setPasswordModalVisible(false)
loadFileTree(password)
message.success('验证成功')
} catch (error) {
message.error('访问密码错误')
}
}
// 加载文件树
const loadFileTree = async (pwd = null) => {
try {
const res = await getPreviewTree(projectId, pwd || accessPassword)
const tree = res.data || []
setFileTree(tree)
// 默认打开 README.md
const readmeNode = findReadme(tree)
if (readmeNode) {
setSelectedFile(readmeNode.key)
loadMarkdown(readmeNode.key, pwd || accessPassword)
}
} catch (error) {
console.error('Load file tree error:', error)
if (error.response?.status === 403) {
message.error('访问密码错误或已过期')
setPasswordModalVisible(true)
}
}
}
// 查找根目录的 README.md
const findReadme = (nodes) => {
for (const node of nodes) {
if (node.title === 'README.md' && node.isLeaf) {
return node
}
}
return null
}
// 转换文件树为菜单项
const convertTreeToMenuItems = (nodes) => {
return nodes.map((node) => {
if (!node.isLeaf) {
return {
key: node.key,
label: node.title,
icon:
{projectInfo.description}
)}该项目需要访问密码,请输入密码后继续浏览。