import React, { useState } from "react"; import { useEffect } from "react"; import { message, Modal, Tree } from "antd"; import type { DataNode, TreeProps } from "antd/es/tree"; import type { Key } from "antd/lib/table/interface"; import { DeleteOutlined, SaveOutlined } from "@ant-design/icons"; import { FileAddOutlined, LinkOutlined } from "@ant-design/icons"; import { Button, Divider, Space } from "antd"; import { useIntl } from "react-intl"; import EditableTreeNode from "./EditableTreeNode"; import { randomString } from "../../utils"; export interface TreeNodeData { key: string; id: string; title: string | React.ReactNode; title_text?: string; icon?: React.ReactNode; children: TreeNodeData[]; status?: number; deletedAt?: string | null; level: number; } export type ListNodeData = { key: string; title: string | React.ReactNode; title_text?: string; level: number; status?: number; children?: number; deletedAt?: string | null; }; let tocActivePath: TreeNodeData[] = []; function tocGetTreeData(articles: ListNodeData[], active = "") { const treeData = []; const treeParents = []; const rootNode: TreeNodeData = { key: randomString(), id: "0", title: "root", title_text: "root", level: 0, children: [], }; treeData.push(rootNode); let lastInsNode: TreeNodeData = rootNode; let iCurrLevel = 0; const keys: string[] = []; for (let index = 0; index < articles.length; index++) { const element = articles[index]; const newNode: TreeNodeData = { key: randomString(), id: element.key, title: element.title, title_text: element.title_text, children: [], icon: keys.includes(element.key) ? : undefined, status: element.status, level: element.level, deletedAt: element.deletedAt, }; if (!keys.includes(element.key)) { keys.push(element.key); } /* if (active == element.article) { newNode["extraClasses"] = "active"; } */ if (newNode.level > iCurrLevel) { //新的层级比较大,为上一个的子目录 treeParents.push(lastInsNode); lastInsNode.children.push(newNode); } else if (newNode.level === iCurrLevel) { //目录层级相同,为平级 treeParents[treeParents.length - 1].children.push(newNode); } else { // 小于 挂在上一个层级 while (treeParents.length > 1) { treeParents.pop(); if (treeParents[treeParents.length - 1].level < newNode.level) { break; } } treeParents[treeParents.length - 1].children.push(newNode); } lastInsNode = newNode; iCurrLevel = newNode.level; if (active === element.key) { tocActivePath = []; for (let index = 1; index < treeParents.length; index++) { tocActivePath.push(treeParents[index]); } } } return treeData[0].children; } function treeToList(treeNode: TreeNodeData[]): ListNodeData[] { let iTocTreeCurrLevel = 1; const arrTocTree: ListNodeData[] = []; for (const iterator of treeNode) { getTreeNodeData(iterator); } function getTreeNodeData(node: TreeNodeData) { let children = 0; if (typeof node.children != "undefined") { children = node.children.length; } arrTocTree.push({ key: node.id, title: node.title, title_text: node.title_text, level: iTocTreeCurrLevel, children: children, deletedAt: node.deletedAt, }); if (children > 0) { iTocTreeCurrLevel++; for (const iterator of node.children) { getTreeNodeData(iterator); } iTocTreeCurrLevel--; } } return arrTocTree; } interface IWidget { treeData: ListNodeData[]; addFileButton?: React.ReactNode; addOnArticle?: TreeNodeData; updatedNode?: TreeNodeData; onChange?: Function; onSelect?: Function; onSave?: Function; onAddFile?: Function; onAppend?: Function; onTitleClick?: Function; } const EditableTreeWidget = ({ treeData, addFileButton, addOnArticle, updatedNode, onChange, onSelect, onSave, onAppend, onTitleClick, }: IWidget) => { const intl = useIntl(); const [checkKeys, setCheckKeys] = useState([]); const [checkNodes, setCheckNodes] = useState([]); const [gData, setGData] = useState([]); const [listTreeData, setListTreeData] = useState(); const [keys, setKeys] = useState(""); useEffect(() => { if (typeof onChange !== "undefined") { onChange(listTreeData); } }, [listTreeData]); useEffect(() => { //找到节点并更新 if (typeof updatedNode === "undefined") { return; } const update = (_node: TreeNodeData[]) => { _node.forEach((value, index, array) => { if (value.id === updatedNode.id) { array[index].title = updatedNode.title; array[index].title_text = updatedNode.title_text; console.log("key found"); return; } else { update(array[index].children); } return; }); }; const newTree = [...gData]; update(newTree); setGData(newTree); const list = treeToList(newTree); setListTreeData(list); }, [updatedNode]); const appendNode = (key: string, node: TreeNodeData) => { console.log("key", key); const append = (_node: TreeNodeData[]) => { _node.forEach((value, index, array) => { if (value.key === key) { array[index].children.push(node); console.log("key found"); return; } else { append(array[index].children); } return; }); }; const newTree = [...gData]; append(newTree); setGData(newTree); const list = treeToList(newTree); setListTreeData(list); }; useEffect(() => { if (typeof addOnArticle === "undefined") { return; } console.log("add ", addOnArticle); const newTreeData = [...gData, addOnArticle]; setGData(newTreeData); const list = treeToList(newTreeData); setListTreeData(list); }, [addOnArticle]); useEffect(() => { const data = tocGetTreeData(treeData); console.log("tree data", data); setGData(data); }, [treeData]); const onCheck: TreeProps["onCheck"] = (checkedKeys, info) => { console.log("onCheck", checkedKeys, info); setCheckKeys(checkedKeys as string[]); setCheckNodes(info.checkedNodes as TreeNodeData[]); }; const onDragEnter: TreeProps["onDragEnter"] = (info) => { console.log(info); // expandedKeys 需要受控时设置 // setExpandedKeys(info.expandedKeys) }; const onDrop: TreeProps["onDrop"] = (info) => { console.log(info); const dropKey = info.node.key; const dragKey = info.dragNode.key; const dropPos = info.node.pos.split("-"); const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); const loop = ( data: DataNode[], key: React.Key, callback: (node: DataNode, i: number, data: DataNode[]) => void ) => { for (let i = 0; i < data.length; i++) { if (data[i].key === key) { return callback(data[i], i, data); } if (data[i].children) { loop(data[i].children!, key, callback); } } }; const data = [...gData]; // Find dragObject let dragObj: DataNode; loop(data, dragKey, (item, index, arr) => { arr.splice(index, 1); dragObj = item; }); if (!info.dropToGap) { // Drop on the content loop(data, dropKey, (item) => { item.children = item.children || []; // where to insert 示例添加到头部,可以是随意位置 item.children.unshift(dragObj); }); } else if ( ((info.node as any).props.children || []).length > 0 && // Has children (info.node as any).props.expanded && // Is expanded dropPosition === 1 // On the bottom gap ) { loop(data, dropKey, (item) => { item.children = item.children || []; // where to insert 示例添加到头部,可以是随意位置 item.children.unshift(dragObj); // in previous version, we use item.children.push(dragObj) to insert the // item to the tail of the children }); } else { let ar: DataNode[] = []; let i: number; loop(data, dropKey, (_item, index, arr) => { ar = arr; i = index; }); if (dropPosition === -1) { ar.splice(i!, 0, dragObj!); } else { ar.splice(i! + 1, 0, dragObj!); } } setGData(data); const list = treeToList(data); setListTreeData(list); }; return ( <> {addFileButton} { if (selectedKeys.length > 0) { setKeys(selectedKeys[0]); } else { setKeys(""); } if (typeof onSelect !== "undefined") { onSelect(selectedKeys); } }} treeData={gData} titleRender={(node: TreeNodeData) => { return ( { if (typeof onAppend !== "undefined") { const newNode = await onAppend(node); console.log("newNode", newNode); if (newNode) { appendNode(node.key, newNode); return true; } else { message.error("添加失败"); return false; } } else { return false; } }} onTitleClick={(e: React.MouseEvent) => { if (typeof onTitleClick !== "undefined") { onTitleClick(e, node); } }} /> ); }} /> ); }; export default EditableTreeWidget;