0.9.5
parent
9a431fc046
commit
1319310b5a
|
|
@ -1,10 +1,13 @@
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
|
# 增加上传文件大小限制(支持大文件上传)
|
||||||
|
client_max_body_size 100M;
|
||||||
|
|
||||||
# Gzip 压缩
|
# Gzip 压缩
|
||||||
gzip on;
|
gzip on;
|
||||||
gzip_vary on;
|
gzip_vary on;
|
||||||
|
|
@ -25,6 +28,9 @@ server {
|
||||||
proxy_send_timeout 60s;
|
proxy_send_timeout 60s;
|
||||||
proxy_read_timeout 60s;
|
proxy_read_timeout 60s;
|
||||||
|
|
||||||
|
# 增加上传文件大小限制
|
||||||
|
client_max_body_size 100M;
|
||||||
|
|
||||||
# WebSocket 支持
|
# WebSocket 支持
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
|
@ -35,6 +41,15 @@ server {
|
||||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# PDF.js worker 文件 - 必须返回正确的 MIME type
|
||||||
|
location /pdf-worker/ {
|
||||||
|
types {
|
||||||
|
application/javascript mjs;
|
||||||
|
}
|
||||||
|
add_header Content-Type "application/javascript" always;
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
# 前端路由支持
|
# 前端路由支持
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/* 覆盖MainLayout的content-wrapper padding */
|
/* 覆盖MainLayout的content-wrapper padding */
|
||||||
.document-editor-page {
|
.document-editor-page {
|
||||||
height: calc(100vh - 64px);
|
height: calc(90vh);
|
||||||
width: calc(100% + 32px);
|
/* width: calc(100% + 32px); */
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,6 +95,20 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
padding: 4px 8px;
|
||||||
|
margin: -4px -8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中的文件夹样式 */
|
||||||
|
.file-tree .folder-selected > .ant-menu-submenu-title {
|
||||||
|
background-color: #e6f7ff !important;
|
||||||
|
color: #1890ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-tree .folder-selected > .ant-menu-submenu-title:hover {
|
||||||
|
background-color: #bae7ff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-tree .ant-menu-item,
|
.file-tree .ant-menu-item,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useState, useEffect, useRef, useMemo } from 'react'
|
import { useState, useEffect, useRef, useMemo } from 'react'
|
||||||
import { useParams, useNavigate } from 'react-router-dom'
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
import { Layout, Menu, Button, message, Modal, Input, Space, Tooltip, Dropdown, Upload, Select } from 'antd'
|
import { Layout, Menu, Button, Modal, Input, Space, Tooltip, Dropdown, Upload, Select, Progress } from 'antd'
|
||||||
import {
|
import {
|
||||||
FileOutlined,
|
FileOutlined,
|
||||||
FolderOutlined,
|
FolderOutlined,
|
||||||
|
|
@ -36,6 +36,7 @@ import {
|
||||||
exportDirectory,
|
exportDirectory,
|
||||||
uploadDocument,
|
uploadDocument,
|
||||||
} from '@/api/file'
|
} from '@/api/file'
|
||||||
|
import Toast from '@/components/Toast/Toast'
|
||||||
import './DocumentEditor.css'
|
import './DocumentEditor.css'
|
||||||
|
|
||||||
const { Sider, Content } = Layout
|
const { Sider, Content } = Layout
|
||||||
|
|
@ -60,6 +61,11 @@ function DocumentEditor() {
|
||||||
const [dirOptions, setDirOptions] = useState([])
|
const [dirOptions, setDirOptions] = useState([])
|
||||||
const [editorHeight, setEditorHeight] = useState(600) // 设置初始高度为600px
|
const [editorHeight, setEditorHeight] = useState(600) // 设置初始高度为600px
|
||||||
const [openKeys, setOpenKeys] = useState([]) // Menu组件的展开项
|
const [openKeys, setOpenKeys] = useState([]) // Menu组件的展开项
|
||||||
|
const [uploadProgress, setUploadProgress] = useState(0) // 上传进度
|
||||||
|
const [uploading, setUploading] = useState(false) // 是否正在上传
|
||||||
|
const [selectedMenuKey, setSelectedMenuKey] = useState(null) // 当前选中的菜单项(文件或文件夹)
|
||||||
|
const uploadingRef = useRef(false) // 使用ref防止重复上传
|
||||||
|
const [isPdfSelected, setIsPdfSelected] = useState(false) // 是否选中了PDF文件
|
||||||
|
|
||||||
// 在组件挂载后立即计算正确的高度
|
// 在组件挂载后立即计算正确的高度
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -88,7 +94,7 @@ function DocumentEditor() {
|
||||||
const tree = data.tree || data || [] // 兼容新旧格式
|
const tree = data.tree || data || [] // 兼容新旧格式
|
||||||
setTreeData(tree)
|
setTreeData(tree)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('加载文件树失败')
|
Toast.error('加载失败', '加载文件树失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,7 +107,7 @@ function DocumentEditor() {
|
||||||
|
|
||||||
// 检查是否是PDF文件
|
// 检查是否是PDF文件
|
||||||
if (filePath.toLowerCase().endsWith('.pdf')) {
|
if (filePath.toLowerCase().endsWith('.pdf')) {
|
||||||
message.info('PDF文件请在浏览模式下查看')
|
Toast.info('提示', 'PDF文件请在浏览模式下查看')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +117,7 @@ function DocumentEditor() {
|
||||||
setSelectedFile(filePath)
|
setSelectedFile(filePath)
|
||||||
setFileContent(res.data.content)
|
setFileContent(res.data.content)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Load file error:', error)
|
// 错误已通过request interceptor处理
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
@ -139,22 +145,28 @@ function DocumentEditor() {
|
||||||
|
|
||||||
// 始终记录选中的节点(包括文件夹)
|
// 始终记录选中的节点(包括文件夹)
|
||||||
setSelectedNode(node)
|
setSelectedNode(node)
|
||||||
|
setSelectedMenuKey(key) // 高亮显示选中项
|
||||||
|
|
||||||
// 只处理文件(叶子节点)加载内容
|
// 只处理文件(叶子节点)加载内容
|
||||||
if (node.isLeaf) {
|
if (node.isLeaf) {
|
||||||
// 检查是否是PDF文件
|
// 检查是否是PDF文件
|
||||||
if (key.toLowerCase().endsWith('.pdf')) {
|
if (key.toLowerCase().endsWith('.pdf')) {
|
||||||
message.info('PDF文件请在浏览模式下查看')
|
setSelectedFile(key)
|
||||||
|
setIsPdfSelected(true)
|
||||||
|
setFileContent('')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重置PDF标记
|
||||||
|
setIsPdfSelected(false)
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await getFileContent(projectId, key)
|
const res = await getFileContent(projectId, key)
|
||||||
setSelectedFile(key)
|
setSelectedFile(key)
|
||||||
setFileContent(res.data.content)
|
setFileContent(res.data.content)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('加载文件失败')
|
Toast.error('加载失败', '加载文件失败')
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
@ -163,7 +175,7 @@ function DocumentEditor() {
|
||||||
|
|
||||||
const handleSaveFile = async () => {
|
const handleSaveFile = async () => {
|
||||||
if (!selectedFile) {
|
if (!selectedFile) {
|
||||||
message.warning('请先选择文件')
|
Toast.warning('提示', '请先选择文件')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -173,9 +185,9 @@ function DocumentEditor() {
|
||||||
path: selectedFile,
|
path: selectedFile,
|
||||||
content: fileContent,
|
content: fileContent,
|
||||||
})
|
})
|
||||||
message.success('保存成功')
|
Toast.success('成功', '保存成功')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Save file error:', error)
|
// 错误已通过request interceptor处理
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +223,7 @@ function DocumentEditor() {
|
||||||
|
|
||||||
const handleDelete = (path = selectedFile) => {
|
const handleDelete = (path = selectedFile) => {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
message.warning('请先选择文件')
|
Toast.warning('提示', '请先选择文件')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,7 +236,7 @@ function DocumentEditor() {
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
path: path,
|
path: path,
|
||||||
})
|
})
|
||||||
message.success('删除成功')
|
Toast.success('成功', '删除成功')
|
||||||
if (selectedFile === path) {
|
if (selectedFile === path) {
|
||||||
setSelectedFile(null)
|
setSelectedFile(null)
|
||||||
setFileContent('')
|
setFileContent('')
|
||||||
|
|
@ -232,7 +244,7 @@ function DocumentEditor() {
|
||||||
fetchTree()
|
fetchTree()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Delete error:', error)
|
console.error('Delete error:', error)
|
||||||
message.error('删除失败')
|
Toast.error('错误', '删除失败')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
@ -243,7 +255,7 @@ function DocumentEditor() {
|
||||||
|
|
||||||
// 保护README.md
|
// 保护README.md
|
||||||
if (fileName === 'README.md' && path.indexOf('/') === -1) {
|
if (fileName === 'README.md' && path.indexOf('/') === -1) {
|
||||||
message.error('根目录的README.md不允许重命名')
|
Toast.error('错误', '根目录的README.md不允许重命名')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,7 +267,7 @@ function DocumentEditor() {
|
||||||
|
|
||||||
const handleOperation = async () => {
|
const handleOperation = async () => {
|
||||||
if (!newName.trim()) {
|
if (!newName.trim()) {
|
||||||
message.warning('请输入名称')
|
Toast.warning('提示', '请输入名称')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,7 +313,7 @@ function DocumentEditor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
await operateFile(projectId, params)
|
await operateFile(projectId, params)
|
||||||
message.success('操作成功')
|
Toast.success('成功', '操作成功')
|
||||||
setModalVisible(false)
|
setModalVisible(false)
|
||||||
setNewName('')
|
setNewName('')
|
||||||
setRightClickNode(null)
|
setRightClickNode(null)
|
||||||
|
|
@ -314,7 +326,7 @@ function DocumentEditor() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Operation error:', error)
|
console.error('Operation error:', error)
|
||||||
message.error('操作失败')
|
Toast.error('错误', '操作失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -344,7 +356,7 @@ function DocumentEditor() {
|
||||||
content: operation === 'create_file' ? '# 新文件\n' : undefined,
|
content: operation === 'create_file' ? '# 新文件\n' : undefined,
|
||||||
}
|
}
|
||||||
await operateFile(projectId, params)
|
await operateFile(projectId, params)
|
||||||
message.success('操作成功')
|
Toast.success('成功', '操作成功')
|
||||||
setModalVisible(false)
|
setModalVisible(false)
|
||||||
setNewName('')
|
setNewName('')
|
||||||
setRightClickNode(null)
|
setRightClickNode(null)
|
||||||
|
|
@ -353,16 +365,26 @@ function DocumentEditor() {
|
||||||
|
|
||||||
// 上传文档(支持MD和PDF)
|
// 上传文档(支持MD和PDF)
|
||||||
const handleImportDocuments = async (info) => {
|
const handleImportDocuments = async (info) => {
|
||||||
|
// 防止重复触发
|
||||||
|
if (uploadingRef.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const { fileList } = info
|
const { fileList } = info
|
||||||
|
|
||||||
|
// 过滤出有效文件
|
||||||
const mdFiles = fileList.filter((f) => f.name.endsWith('.md'))
|
const mdFiles = fileList.filter((f) => f.name.endsWith('.md'))
|
||||||
const pdfFiles = fileList.filter((f) => f.name.toLowerCase().endsWith('.pdf'))
|
const pdfFiles = fileList.filter((f) => f.name.toLowerCase().endsWith('.pdf'))
|
||||||
const allFiles = [...mdFiles, ...pdfFiles]
|
const allFiles = [...mdFiles, ...pdfFiles]
|
||||||
|
|
||||||
if (allFiles.length === 0) {
|
if (allFiles.length === 0) {
|
||||||
message.warning('请选择.md或.pdf格式的文档')
|
Toast.warning('提示', '请选择.md或.pdf格式的文档')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置上传标记
|
||||||
|
uploadingRef.current = true
|
||||||
|
|
||||||
// 确定目标路径(如果选中了目录,上传到该目录)
|
// 确定目标路径(如果选中了目录,上传到该目录)
|
||||||
const targetPath = selectedNode && !selectedNode.isLeaf ? selectedNode.key : ''
|
const targetPath = selectedNode && !selectedNode.isLeaf ? selectedNode.key : ''
|
||||||
|
|
||||||
|
|
@ -394,6 +416,10 @@ function DocumentEditor() {
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await executeImport(mdFiles, pdfFiles, targetPath)
|
await executeImport(mdFiles, pdfFiles, targetPath)
|
||||||
},
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
// 取消时重置标记
|
||||||
|
uploadingRef.current = false
|
||||||
|
},
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -404,14 +430,19 @@ function DocumentEditor() {
|
||||||
|
|
||||||
// 执行导入操作
|
// 执行导入操作
|
||||||
const executeImport = async (mdFiles, pdfFiles, targetPath) => {
|
const executeImport = async (mdFiles, pdfFiles, targetPath) => {
|
||||||
|
setUploading(true)
|
||||||
|
setUploadProgress(0)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let successCount = 0
|
let successCount = 0
|
||||||
|
const totalFiles = mdFiles.length + pdfFiles.length
|
||||||
|
|
||||||
// 上传MD文件
|
// 上传MD文件
|
||||||
if (mdFiles.length > 0) {
|
if (mdFiles.length > 0) {
|
||||||
const files = mdFiles.map((f) => f.originFileObj)
|
const files = mdFiles.map((f) => f.originFileObj)
|
||||||
await importDocuments(projectId, files, targetPath)
|
await importDocuments(projectId, files, targetPath)
|
||||||
successCount += files.length
|
successCount += files.length
|
||||||
|
setUploadProgress(Math.round((successCount / totalFiles) * 100))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传PDF文件
|
// 上传PDF文件
|
||||||
|
|
@ -419,18 +450,22 @@ function DocumentEditor() {
|
||||||
for (const pdfFile of pdfFiles) {
|
for (const pdfFile of pdfFiles) {
|
||||||
await uploadDocument(projectId, pdfFile.originFileObj, targetPath)
|
await uploadDocument(projectId, pdfFile.originFileObj, targetPath)
|
||||||
successCount++
|
successCount++
|
||||||
|
setUploadProgress(Math.round((successCount / totalFiles) * 100))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message.success(`成功上传 ${successCount} 个文档`)
|
Toast.success('成功', `成功上传 ${successCount} 个文档`)
|
||||||
fetchTree()
|
fetchTree()
|
||||||
// 清除文件选择
|
// 清除文件选择
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
fileInputRef.current.value = ''
|
fileInputRef.current.value = ''
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Import error:', error)
|
Toast.error('错误', '上传失败')
|
||||||
message.error('上传失败')
|
} finally {
|
||||||
|
setUploading(false)
|
||||||
|
setUploadProgress(0)
|
||||||
|
uploadingRef.current = false // 重置上传标记
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -463,10 +498,10 @@ function DocumentEditor() {
|
||||||
document.body.removeChild(link)
|
document.body.removeChild(link)
|
||||||
window.URL.revokeObjectURL(url)
|
window.URL.revokeObjectURL(url)
|
||||||
|
|
||||||
message.success('导出成功')
|
Toast.success('成功', '导出成功')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Export error:', error)
|
console.error('Export error:', error)
|
||||||
message.error('导出失败')
|
Toast.error('错误', '导出失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,7 +551,7 @@ function DocumentEditor() {
|
||||||
path: rightClickNode,
|
path: rightClickNode,
|
||||||
new_path: newPath,
|
new_path: newPath,
|
||||||
})
|
})
|
||||||
message.success('移动成功')
|
Toast.success('成功', '移动成功')
|
||||||
setMoveModalVisible(false)
|
setMoveModalVisible(false)
|
||||||
setRightClickNode(null)
|
setRightClickNode(null)
|
||||||
fetchTree()
|
fetchTree()
|
||||||
|
|
@ -528,7 +563,7 @@ function DocumentEditor() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Move error:', error)
|
console.error('Move error:', error)
|
||||||
message.error('移动失败')
|
Toast.error('错误', '移动失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,7 +575,7 @@ function DocumentEditor() {
|
||||||
return res.data.url
|
return res.data.url
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Upload error:', error)
|
console.error('Upload error:', error)
|
||||||
message.error('图片上传失败')
|
Toast.error('错误', '图片上传失败')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -564,12 +599,10 @@ function DocumentEditor() {
|
||||||
input.onchange = async (e) => {
|
input.onchange = async (e) => {
|
||||||
const file = e.target.files?.[0]
|
const file = e.target.files?.[0]
|
||||||
if (file) {
|
if (file) {
|
||||||
message.loading('上传图片中...', 0)
|
|
||||||
const url = await handleImageUpload(file)
|
const url = await handleImageUpload(file)
|
||||||
message.destroy()
|
|
||||||
if (url) {
|
if (url) {
|
||||||
ctx.appendBlock(``)
|
ctx.appendBlock(``)
|
||||||
message.success('图片上传成功')
|
Toast.success('成功', '图片上传成功')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -601,15 +634,13 @@ function DocumentEditor() {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const file = item.getAsFile()
|
const file = item.getAsFile()
|
||||||
if (file) {
|
if (file) {
|
||||||
message.loading('上传图片中...', 0)
|
|
||||||
const url = await handleImageUpload(file)
|
const url = await handleImageUpload(file)
|
||||||
message.destroy()
|
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
// 插入markdown图片语法
|
// 插入markdown图片语法
|
||||||
const imageMarkdown = ``
|
const imageMarkdown = ``
|
||||||
setFileContent(prev => prev + '\n' + imageMarkdown)
|
setFileContent(prev => prev + '\n' + imageMarkdown)
|
||||||
message.success('图片上传成功')
|
Toast.success('成功', '图片上传成功')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
@ -626,14 +657,12 @@ function DocumentEditor() {
|
||||||
const file = files[i]
|
const file = files[i]
|
||||||
if (file.type.indexOf('image') !== -1) {
|
if (file.type.indexOf('image') !== -1) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
message.loading('上传图片中...', 0)
|
|
||||||
const url = await handleImageUpload(file)
|
const url = await handleImageUpload(file)
|
||||||
message.destroy()
|
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
const imageMarkdown = ``
|
const imageMarkdown = ``
|
||||||
setFileContent(prev => prev + '\n' + imageMarkdown)
|
setFileContent(prev => prev + '\n' + imageMarkdown)
|
||||||
message.success('图片上传成功')
|
Toast.success('成功', '图片上传成功')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -693,10 +722,10 @@ function DocumentEditor() {
|
||||||
const convertTreeToMenuItems = (nodes) => {
|
const convertTreeToMenuItems = (nodes) => {
|
||||||
return nodes.map((node) => {
|
return nodes.map((node) => {
|
||||||
// 使用Dropdown包裹label,实现右键菜单
|
// 使用Dropdown包裹label,实现右键菜单
|
||||||
// 使用 div 和 width: 100% 增加点击区域
|
const isSelected = selectedMenuKey === node.key
|
||||||
const labelContent = (
|
const labelContent = (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={{ items: getNodeMenuItems(node) }}
|
menu={{ items: getNodeMenuItems(node) }}
|
||||||
trigger={['contextMenu']}
|
trigger={['contextMenu']}
|
||||||
>
|
>
|
||||||
<div className="tree-node-wrapper">
|
<div className="tree-node-wrapper">
|
||||||
|
|
@ -706,12 +735,17 @@ function DocumentEditor() {
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!node.isLeaf) {
|
if (!node.isLeaf) {
|
||||||
// 目录
|
// 目录 - 通过className和style控制选中样式
|
||||||
return {
|
return {
|
||||||
key: node.key,
|
key: node.key,
|
||||||
label: labelContent,
|
label: labelContent,
|
||||||
icon: <FolderOutlined />,
|
icon: <FolderOutlined style={isSelected ? { color: '#1890ff' } : {}} />,
|
||||||
children: node.children ? convertTreeToMenuItems(node.children) : [],
|
children: node.children ? convertTreeToMenuItems(node.children) : [],
|
||||||
|
className: isSelected ? 'folder-selected' : '',
|
||||||
|
onTitleClick: () => {
|
||||||
|
setSelectedNode(node)
|
||||||
|
setSelectedMenuKey(node.key)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else if (node.title && node.title.endsWith('.md')) {
|
} else if (node.title && node.title.endsWith('.md')) {
|
||||||
// Markdown 文件
|
// Markdown 文件
|
||||||
|
|
@ -800,9 +834,16 @@ function DocumentEditor() {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* 上传进度条 */}
|
||||||
|
{uploading && (
|
||||||
|
<div style={{ padding: '12px 16px', borderBottom: '1px solid #f0f0f0' }}>
|
||||||
|
<div style={{ marginBottom: 4, fontSize: 12, color: '#666' }}>上传中...</div>
|
||||||
|
<Progress percent={uploadProgress} size="small" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Menu
|
<Menu
|
||||||
mode="inline"
|
mode="inline"
|
||||||
selectedKeys={[selectedFile]}
|
selectedKeys={selectedMenuKey ? [selectedMenuKey] : []}
|
||||||
openKeys={openKeys}
|
openKeys={openKeys}
|
||||||
onOpenChange={setOpenKeys}
|
onOpenChange={setOpenKeys}
|
||||||
items={menuItems}
|
items={menuItems}
|
||||||
|
|
@ -836,7 +877,11 @@ function DocumentEditor() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="editor-container">
|
<div className="editor-container">
|
||||||
{selectedFile ? (
|
{isPdfSelected ? (
|
||||||
|
<div className="empty-editor">
|
||||||
|
<p>PDF文件请在浏览模式下查看</p>
|
||||||
|
</div>
|
||||||
|
) : selectedFile ? (
|
||||||
<div
|
<div
|
||||||
className="bytemd-wrapper"
|
className="bytemd-wrapper"
|
||||||
onPaste={handlePaste}
|
onPaste={handlePaste}
|
||||||
|
|
@ -867,9 +912,11 @@ function DocumentEditor() {
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title={
|
title={
|
||||||
operationType === 'create_file' ? '创建文件' :
|
operationType === 'create_file'
|
||||||
operationType === 'create_dir' ? '创建文件夹' :
|
? `创建文件 (dir: /${creationParentPath || '/'})`
|
||||||
'重命名'
|
: operationType === 'create_dir'
|
||||||
|
? `创建文件夹 (dir: /${creationParentPath || '/'})`
|
||||||
|
: '重命名'
|
||||||
}
|
}
|
||||||
open={modalVisible}
|
open={modalVisible}
|
||||||
onOk={handleOperation}
|
onOk={handleOperation}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue