// app/components/FileTree/FileTree.tsx 'use client' import { useState, useEffect } from 'react' import { Tree, Input, Button, Dropdown, Modal, Form, message } from 'antd' import type { DataNode, TreeProps } from 'antd/es/tree' import { Key } from 'react' import { FileOutlined, FolderOutlined, PlusOutlined, FolderOpenOutlined, LoadingOutlined, } from '@ant-design/icons' import { useRouter } from 'next/navigation' import { FileService } from '@/app/lib/services/file' interface FileNode { id: string name: string type: string children?: FileNode[] parentKey?: string } interface FileModalProps { type: 'new-doc' | 'new-folder' | 'rename' title: string open: boolean initialValue?: string onOk: (value: string) => void onCancel: () => void loading?: boolean } // 文件操作模态框组件 const FileModal: React.FC = ({ type, title, open, initialValue = '', onOk, onCancel, loading, }) => { const [form] = Form.useForm() useEffect(() => { if (open) { form.setFieldsValue({ name: initialValue }) } }, [open, initialValue, form]) const handleOk = async () => { try { const values = await form.validateFields() onOk(values.name) } catch (error) { // 表单验证错误 } } return (
|]+$/, message: '名称不能包含特殊字符 \\ / : * ? " < > |' } ]} >
) } export default function FileTree() { const router = useRouter() const [treeData, setTreeData] = useState([]) const [loading, setLoading] = useState(false) const [searchValue, setSearchValue] = useState('') const [selectedKeys, setSelectedKeys] = useState([]) const [expandedKeys, setExpandedKeys] = useState([]) // 模态框状态 const [modal, setModal] = useState<{ type: 'new-doc' | 'new-folder' | 'rename' open: boolean title: string parentId?: string targetId?: string initialValue?: string }>({ type: 'new-doc', open: false, title: '', }) // 加载文件树数据 const loadFileTree = async () => { try { setLoading(true) const files = await FileService.getFileTree() const formattedData = formatFileTree(files) setTreeData(formattedData) } catch (error) { message.error('加载文件失败') } finally { setLoading(false) } } useEffect(() => { loadFileTree() }, []) // 格式化文件树数据 const formatFileTree = (files: FileNode[]): DataNode[] => { return files.map(file => ({ key: file.id, title: file.name, isLeaf: file.type === 'file', children: file.children ? formatFileTree(file.children) : undefined })) } // 处理新建文档 const handleNewDocument = (parentId: string) => { setModal({ type: 'new-doc', open: true, title: '新建文档', parentId, }) } // 处理新建文件夹 const handleNewFolder = (parentId: string) => { setModal({ type: 'new-folder', open: true, title: '新建文件夹', parentId, }) } // 处理重命名 const handleRename = (targetId: string, initialValue: string) => { setModal({ type: 'rename', open: true, title: '重命名', targetId, initialValue, }) } // 处理删除 const handleDelete = async (id: string) => { Modal.confirm({ title: '确认删除', content: '删除后无法恢复,是否继续?', okText: '确认', cancelText: '取消', onOk: async () => { try { await FileService.deleteFile(id) message.success('删除成功') loadFileTree() } catch (error) { message.error('删除失败'+error) } } }) } // 处理模态框确认 const handleModalOk = async (name: string) => { try { setLoading(true) if (modal.type === 'rename' && modal.targetId) { await FileService.updateFile({ id: modal.targetId, name, }) message.success('重命名成功') } else if (modal.parentId) { await FileService.createFile({ name, type: modal.type === 'new-folder' ? 'folder' : 'file', parentId: modal.parentId, }) message.success(modal.type === 'new-folder' ? '文件夹创建成功' : '文档创建成功') } loadFileTree() setModal({ ...modal, open: false }) } catch (error) { message.error('操作失败' + error) } finally { setLoading(false) } } // 处理模态框取消 const handleModalCancel = () => { setModal({ ...modal, open: false }) } // 获取右键菜单 const getContextMenu = (node: DataNode) => ({ items: [ { key: 'new-doc', label: '新建文档', icon: , onClick: () => handleNewDocument(node.key as string), }, { key: 'new-folder', label: '新建文件夹', icon: , onClick: () => handleNewFolder(node.key as string), }, { type: 'divider', }, { key: 'rename', label: '重命名', onClick: () => handleRename(node.key as string, node.title as string), }, { key: 'delete', label: '删除', danger: true, onClick: () => handleDelete(node.key as string), }, ], }) // 处理树节点选择 const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => { setSelectedKeys(selectedKeys as string[]) const key = selectedKeys[0] if (info.node.isLeaf) { router.push(`/document/${key}`) } } // 处理树节点展开/收起 const onExpand = (expandedKeys: Key[]) => { setExpandedKeys(expandedKeys) } return (
setSearchValue(e.target.value)} className="flex-1" /> , onClick: () => handleNewDocument('root'), }, { key: 'new-folder', label: '新建文件夹', icon: , onClick: () => handleNewFolder('root'), }, ], }} >
{loading ? (
) : ( node.isLeaf ? : node.expanded ? : } titleRender={(nodeData) => ( {nodeData.title} )} /> )}
) }