Commit d0245af6 authored by XieZhiXiong's avatar XieZhiXiong

搬运

parent ab756a42
......@@ -6,142 +6,200 @@ import deepClone from 'clone'
import { TreeProps } from 'antd/lib/tree'
import { PlusOutlined, DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons'
import cx from 'classnames'
import { EventDataNode } from 'rc-tree/lib/interface'
import { useSelections } from '@umijs/hooks'
export interface TabTreeActions {
selected: ReactText[],
getExpandedKeys: () => ReactText[],
getSelectKey: () => ReactText,
getSelectKeys: () => ReactText[],
setExpandedKeys: (keys: ReactText[]) => void,
setSelectKey: (key: ReactText) => void
setSelectKeys: (keys: ReactText[]) => void
selected: ReactText[];
getExpandedKeys: () => ReactText[];
getSelectKey: () => ReactText;
getSelectKeys: () => ReactText[];
setExpandedKeys: (keys: ReactText[]) => void;
setSelectKey: (key: ReactText) => void;
setSelectKeys: (keys: ReactText[]) => void;
getParentPath: (id: ReactText) => string
getParent: (id) => any
}
export interface toolsRenderProps {
addNode?(node),
addChildNode?(node),
deleteNode?(node)
addNode?(node);
addChildNode?(node);
deleteNode?(node);
}
export interface TabTreeProps extends TreeProps {
treeData: any[],
fetchData(params?): Promise<any>,
actions?: TabTreeActions,
title?: React.ReactNode,
treeData: any[];
fetchData?(params?): Promise<any>;
actions?: TabTreeActions;
title?: React.ReactNode;
showSave?; // 是否显示保存按钮
// 若传入该字段, 则会作为tree识别的节点, 默认是`key`, 传入后原有的key值将无效
customKey?: string | number,
customTitle?: string | number,
handleSelect?: (key: ReactText, node: EventDataNode) => void | Promise<any>,
toolsRender?: toolsRenderProps,
getMenuSelectData?(): Promise<any>
customKey?: string | number;
customTitle?: string | number;
handleSelect?: (key: ReactText, node: any) => void | Promise<any>;
handleSubmit?();
toolsRender?: toolsRenderProps;
getMenuSelectData?(): Promise<any>;
}
export interface InnermostTreeNodeProps {
}
export interface InnermostTreeNodeProps {}
export interface RenderIconsProps {
node: any,
nowKey: any,
toolsRender?: toolsRenderProps
node: any;
nowKey: any;
toolsRender?: toolsRenderProps;
}
// 注意该方法只能在hooks中使用, 如果在函数外使用 会导致页面一直处于loading, 函数外可以直接用createTreeActions
export const useTreeActions = (action?): TabTreeActions => {
const actionRef = useRef<any>(null)
actionRef.current = actionRef.current || action || createTreeActions()
return actionRef.current
}
const actionRef = useRef<any>(null);
actionRef.current = actionRef.current || action || createTreeActions();
return actionRef.current;
};
export const createTreeActions = () => {
const actions: TabTreeActions = {
selected: [],
getExpandedKeys(){ return [] },
getSelectKey(){ return '' },
getSelectKeys(){ return [] },
setSelectKey(){},
setSelectKeys(){},
setExpandedKeys(){},
getExpandedKeys() {
return [];
},
getSelectKey() {
return '';
},
getSelectKeys() {
return [];
},
setSelectKey() {},
setSelectKeys() {},
setExpandedKeys() {},
getParentPath(id){return ''},
getParent(id){console.log('not init');return null}
}
return actions
}
getParent(id){return null}
};
return actions;
};
const InnermostTreeNode: React.FC<InnermostTreeNodeProps> = (props) => {
return <span style={{display: 'flex', alignItems: 'center'}}>
<span className='tree-node-circle'></span>
<span>{props.children}</span>
</span>
}
const InnermostTreeNode: React.FC<InnermostTreeNodeProps> = props => {
return (
<span style={{ display: 'flex', alignItems: 'center' }}>
<span className="tree-node-circle"></span>
<span>{props.children}</span>
</span>
);
};
const RenderIcons: React.FC<RenderIconsProps> = (props) => {
const {
toolsRender
} = props
const RenderIcons: React.FC<RenderIconsProps> = props => {
const { toolsRender } = props;
// @todo 去掉点击active时, 保持icon显示
// return <Space className={cx('god-tabtree-icons', props.nowKey === props.node.key ? 'show' : 'hide')}>
return <Space className={cx('god-tabtree-icons')}>
<Tooltip title='新增节点'>
<PlusCircleOutlined onClick={(e) => {
e.stopPropagation()
toolsRender && toolsRender.addNode && toolsRender.addNode(props.node)
}}/>
</Tooltip>
<Tooltip title='新增子节点'>
<PlusCircleOutlined onClick={(e) => {
e.stopPropagation()
toolsRender && toolsRender.addChildNode && toolsRender.addChildNode(props.node)
}}/>
</Tooltip>
<Tooltip title='删除当前节点'>
<DeleteOutlined onClick={(e) => {
e.stopPropagation()
toolsRender && toolsRender.deleteNode && toolsRender.deleteNode(props.node)
}}/>
</Tooltip>
</Space>
}
return (
<Space className={cx('god-tabtree-icons')}>
<Tooltip title="新增节点">
<PlusCircleOutlined
onClick={e => {
e.stopPropagation();
toolsRender &&
toolsRender.addNode &&
toolsRender.addNode(props.node);
}}
/>
</Tooltip>
<Tooltip title="新增子节点">
<PlusCircleOutlined
onClick={e => {
e.stopPropagation();
toolsRender &&
toolsRender.addChildNode &&
toolsRender.addChildNode(props.node);
}}
/>
</Tooltip>
<Tooltip title="删除当前节点">
<DeleteOutlined
onClick={e => {
e.stopPropagation();
toolsRender &&
toolsRender.deleteNode &&
toolsRender.deleteNode(props.node);
}}
/>
</Tooltip>
</Space>
);
};
// 将无children的叶子节点中的title 转化为带有样式的title, 由于每次render 都需要重新deepClone深拷贝,可以优化
// 在多选模式下无需转化
function transformSingleTitle(data, nowKey, checkable, disabled, toolsRender, customKey?, customTitle?) {
function transformSingleTitle(
data,
nowKey,
checkable,
disabled,
toolsRender,
customKey?,
customTitle?,
) {
if (Array.isArray(data) && data.length > 0) {
for (let item = 0; item < data.length; item++) {
// 指定默认key
if (customKey) {
data[item]._key = data[item].key
data[item].key = data[item][customKey]
data[item]._key = data[item].key;
data[item].key = data[item][customKey];
}
if (data[item].children) {
transformSingleTitle(data[item].children, nowKey, checkable, disabled, toolsRender, customKey, customTitle)
transformSingleTitle(
data[item].children,
nowKey,
checkable,
disabled,
toolsRender,
customKey,
customTitle,
);
}
data[item]._title = data[item]._title || data[item].title
data[item].title = <span className='god-tabtree-title' style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between'}}>
{ (checkable || data[item].children) ? data[item].title : <InnermostTreeNode>{data[item].title}</InnermostTreeNode> }
<div>
{ toolsRender && <RenderIcons node={data[item]} nowKey={nowKey} toolsRender={toolsRender}/> }
</div>
</span>
data[item]._title = data[item]._title || data[item].title;
data[item].title = (
<span
className="god-tabtree-title"
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
{checkable ||
(data[item].children && data[item].children.length !== 0) ? (
data[item].title
) : (
<InnermostTreeNode>{data[item].title}</InnermostTreeNode>
)}
<div>
{toolsRender && (
<RenderIcons
node={data[item]}
nowKey={nowKey}
toolsRender={toolsRender}
/>
)}
</div>
</span>
);
// 使选中样式受控
data[item].className= cx('god-tabtree-select', Number(nowKey) === Number(data[item].key) ? 'show' : 'hide')
data[item].className = cx(
'god-tabtree-select',
String(nowKey) === String(data[item].key) ? 'show' : 'hide',
);
if (disabled) {
data[item].disableCheckbox = disabled
data[item].disableCheckbox = disabled;
}
if (customTitle) {
data[item]._title = data[item].title
data[item].title = data[item][customTitle]
data[item]._title = data[item].title;
data[item].title = data[item][customTitle];
}
}
}
return data;
}
const TabTree:React.FC<TabTreeProps> = (props) => {
const TabTree: React.FC<TabTreeProps> = props => {
const {
title,
treeData,
......@@ -151,54 +209,69 @@ const TabTree:React.FC<TabTreeProps> = (props) => {
customTitle,
toolsRender,
disabled,
getMenuSelectData
} = props
showSave,
getMenuSelectData,
handleSubmit,
} = props;
const selfActions = useTreeActions(actions)
// 需展开的key
const [expandkeys, setExpandkeys] = useState<ReactText[]>([])
const [expandkeys, setExpandkeys] = useState<ReactText[]>([]);
// 当前选中的node
const [selectKey, setSelectKey] = useState<string | number>('')
const data = transformSingleTitle(deepClone(treeData), selectKey, checkable, disabled, toolsRender, customKey, customTitle)
const [selectKey, setSelectKey] = useState<string | number>('');
// 重写选择方法, 只有在开启多选的时候才会启用
const checkedKeys = findTreeKeys(treeData, 'id')
const { selected, select,setSelected, unSelect, allSelected, unSelectAll, selectAll } = useSelections(
checkedKeys,
[]
const data = transformSingleTitle(
deepClone(treeData),
selectKey,
checkable,
disabled,
toolsRender,
customKey,
customTitle,
);
// 重写选择方法, 只有在开启多选的时候才会启用
const checkedKeys = findTreeKeys(treeData, customKey);
const {
selected,
select,
setSelected,
unSelect,
allSelected,
unSelectAll,
selectAll,
} = useSelections(checkedKeys, []);
useEffect(() => {
if (getMenuSelectData) {
getMenuSelectData().then(res => {
const { ids } = res.data
setSelected(ids)
})
const { ids } = res.data;
setSelected(ids);
});
}
}, [])
}, []);
const toggleSelectAll = () => {
if (allSelected) {
unSelectAll()
unSelectAll();
} else {
selectAll()
selectAll();
}
}
};
if (selfActions) {
selfActions.getExpandedKeys = () => expandkeys
selfActions.getSelectKey = () => selectKey
selfActions.getSelectKeys = () => selected
selfActions.selected = selected
selfActions.getExpandedKeys = () => expandkeys;
selfActions.getSelectKey = () => selectKey;
selfActions.getSelectKeys = () => selected;
selfActions.selected = selected;
selfActions.setSelectKeys = (keys: ReactText[]) => {
setSelected(keys)
}
setSelected(keys);
};
selfActions.setExpandedKeys = (keys: ReactText[]) => {
setExpandkeys(keys)
}
setExpandkeys(keys);
};
selfActions.setSelectKey = (key: ReactText) => {
setSelectKey(key)
}
setSelectKey(key);
};
selfActions.getParentPath = (id: ReactText) => {
return getParentTreeTitles(treeData, id)
}
......@@ -211,7 +284,6 @@ const TabTree:React.FC<TabTreeProps> = (props) => {
return parentInfo || {id: 0}
}
}
const batchSelect = (items: any) => {
if (items.checked) {
// 更改为严格模式
......@@ -219,58 +291,74 @@ const TabTree:React.FC<TabTreeProps> = (props) => {
} else {
items.forEach(v => select(v))
}
}
};
return (
<div>
{title &&
<div className='god-tabtree-header'>
{title && (
<div className="god-tabtree-header">
<div>{title}</div>
{checkable && <Button onClick={toggleSelectAll} disabled={disabled} type='link'>{ allSelected ? '取消全选' : '全选'}</Button>}
{checkable && (
<Button onClick={toggleSelectAll} disabled={disabled} type="link">
{allSelected ? '取消全选' : '全选'}
</Button>
)}
{showSave && checkable && (
<Button onClick={handleSubmit} disabled={disabled} type="link">
保存
</Button>
)}
</div>
}
)}
<Tree
className="god-tabtree"
treeData={data}
blockNode
checkable={checkable}
checkStrictly
checkedKeys={selected}
expandedKeys={expandkeys}
onCheck={(keys, nodes) => {
const { node, checked, checkedNodes } = nodes
checked ? batchSelect(keys as any) : setSelected(checkedNodes.map(v => v.key))
const { node, checked, checkedNodes } = nodes;
checked
? batchSelect(keys as any)
: setSelected(checkedNodes.map(v => v.key));
}}
onSelect={(keys, e) => {
// 控制点击node时可以展开
const { node, selected } = e
const { node, selected } = e;
// 用户自定义的选择后触发事件
if (props.handleSelect) {
const result = props.handleSelect(node.key, node)
const result = props.handleSelect(node.key, node);
// 存在返回值则不执行选中事件, 一般用于切换node时,不希望离开当前页面
if (result) {
result.then(() => {
// 若promise 是resolve状态, 说明确认离开了当前页面
setSelectKey(selectKey === node.key ? '' : node.key)
setExpandkeys(expandkeys.includes(node.key) ? findItemAndDelete(expandkeys, node.key) : [...expandkeys, node.key])
}).catch(() => {
})
return false
result
.then(() => {
// 若promise 是resolve状态, 说明确认离开了当前页面
setSelectKey(selectKey === node.key ? '' : node.key);
setExpandkeys(
expandkeys.includes(node.key)
? findItemAndDelete(expandkeys, node.key)
: [...expandkeys, node.key],
);
})
.catch(() => {});
return false;
}
}
// 如果重复点击 需要取消选中
setSelectKey(selectKey === node.key ? '' : node.key)
setExpandkeys(expandkeys.includes(node.key) ? findItemAndDelete(expandkeys, node.key) : [...expandkeys, node.key])
setSelectKey(selectKey === node.key ? '' : node.key);
setExpandkeys(
expandkeys.includes(node.key)
? findItemAndDelete(expandkeys, node.key)
: [...expandkeys, node.key],
);
}}
>
</Tree>
></Tree>
</div>
)
}
);
};
TabTree.defaultProps = {}
TabTree.defaultProps = {};
export default TabTree
\ No newline at end of file
export default TabTree;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment