diff --git a/web/src/components/modals/GroupDetail/Panel.tsx b/web/src/components/modals/GroupDetail/Panel.tsx index f66b7599..97a784c8 100644 --- a/web/src/components/modals/GroupDetail/Panel.tsx +++ b/web/src/components/modals/GroupDetail/Panel.tsx @@ -1,13 +1,17 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useRef } from 'react'; import { GroupPanelType, useGroupInfo, GroupPanel as GroupPanelInfo, + showToasts, } from 'tailchat-shared'; import { Tree } from 'antd'; import type { NodeDragEventParams } from 'rc-tree/lib/contextTypes'; import type { DataNode, EventDataNode } from 'antd/lib/tree'; import type { Key } from 'rc-tree/lib/interface'; +import type { AllowDrop } from 'rc-tree/lib/Tree'; +import _cloneDeep from 'lodash/cloneDeep'; +import _isNil from 'lodash/isNil'; /** * 将一维的面板列表构筑成立体的数组 @@ -31,12 +35,63 @@ function buildTreeDataWithGroupPanel( })); } -export const GroupPanel: React.FC<{ - groupId: string; -}> = React.memo((props) => { - const groupId = props.groupId; - const groupInfo = useGroupInfo(groupId); - const groupPanels = groupInfo?.panels ?? []; +/** + * 重新构建面板顺序 + */ +function rebuildGroupPanelOrder( + groupPanels: GroupPanelInfo[] +): GroupPanelInfo[] { + const originGroupPanels = _cloneDeep(groupPanels); + const newPanel = originGroupPanels.filter((panel) => _isNil(panel.parentId)); // 第一层 + const len = newPanel.length; + for (let i = len - 1; i >= 0; i--) { + const currentId = newPanel[i].id; + newPanel.splice( + i + 1, + 0, + ...originGroupPanels.filter((p) => p.parentId === currentId) + ); + } + + return newPanel; +} + +interface GroupPanelTree { + groupPanels: GroupPanelInfo[]; + onChange: (newGroupPanels: GroupPanelInfo[]) => void; +} +/** + * 仅同层级相互拉的树形结构 + */ +const GroupPanelTree: React.FC = React.memo((props) => { + const draggingNode = useRef(null); + const treeData: DataNode[] = useMemo( + () => buildTreeDataWithGroupPanel(props.groupPanels), + [props.groupPanels] + ); + + const handleDragStart = useCallback( + (info: NodeDragEventParams) => { + draggingNode.current = info.node; + }, + [] + ); + + const handleDragEnd = useCallback(() => { + draggingNode.current = null; + }, []); + + const handleAllowDrop = useCallback( + ({ dropNode, dropPosition }: Parameters[0]) => { + if (dropPosition === 0 && draggingNode.current?.isLeaf === false) { + // 不允许容器之间产生父子节点 + return false; + } + + return dropNode.isLeaf === draggingNode.current?.isLeaf; + }, + [] + ); const handleDrop = useCallback( ( @@ -47,25 +102,84 @@ export const GroupPanel: React.FC<{ dropToGap: boolean; } ) => { - // TODO - console.log(info); + const newGroupPanels = _cloneDeep(props.groupPanels); + const dropNodePos = newGroupPanels.findIndex( + (panel) => panel.id === info.node.key + ); + const dropGroupPanel = newGroupPanels[dropNodePos]; + if (dropNodePos === -1) { + showToasts('异常, 目标节点未找到', 'error'); + } + + const dragPanelPos = newGroupPanels.findIndex( + (panel) => panel.id === info.dragNode.key + ); + + if (draggingNode.current?.isLeaf === true && info.node.isLeaf === true) { + // 如果是面板且目标也是面板 + // 则更新它的父节点id + info.dragNodesKeys + // 获取所有的移动节点的位置 + .map((key) => newGroupPanels.findIndex((panel) => panel.id === key)) + // 过滤掉没找到的 + .filter((index) => index !== -1) + .forEach((pos) => { + newGroupPanels[pos].parentId = dropGroupPanel.parentId; + }); + } + + if (info.node.dragOverGapTop === true) { + // 移动到目标节点之前 + newGroupPanels.splice( + dropNodePos, + 0, + newGroupPanels.splice(dragPanelPos, 1)[0] + ); + } else if (info.node.dragOverGapBottom === true) { + // 移动到目标节点之后 + newGroupPanels.splice( + dragPanelPos, + 0, + newGroupPanels.splice(dropNodePos, 1)[0] + ); + } + + if (typeof props.onChange === 'function') { + props.onChange(rebuildGroupPanelOrder(newGroupPanels)); + } }, - [] + [props.groupPanels, props.onChange] ); - const treeData: DataNode[] = useMemo( - () => buildTreeDataWithGroupPanel(groupPanels), - [groupPanels] + return ( + ); +}); +GroupPanelTree.displayName = 'GroupPanelTree'; + +export const GroupPanel: React.FC<{ + groupId: string; +}> = React.memo((props) => { + const groupId = props.groupId; + const groupInfo = useGroupInfo(groupId); + const groupPanels = groupInfo?.panels ?? []; + + const handleChange = useCallback((newGroupPanels: GroupPanelInfo[]) => { + console.log('newGroupPanels', newGroupPanels); + }, []); return (
- +
); });