Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
J
jinfa-admin
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
shenshaokai
jinfa-admin
Commits
d0245af6
Commit
d0245af6
authored
Sep 08, 2020
by
XieZhiXiong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
搬运
parent
ab756a42
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
220 additions
and
133 deletions
+220
-133
index.tsx
src/components/TabTree/index.tsx
+220
-133
No files found.
src/components/TabTree/index.tsx
View file @
d0245af6
...
...
@@ -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
()
const
actionRef
=
useRef
<
any
>
(
null
)
;
actionRef
.
current
=
actionRef
.
current
||
action
||
createTreeActions
()
;
return
actionRef
.
current
}
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
>
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
)
}
}
/>
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
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
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
>
}
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
}
/>
}
{
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
(()
=>
{
result
.
then
(()
=>
{
// 若promise 是resolve状态, 说明确认离开了当前页面
setSelectKey
(
selectKey
===
node
.
key
?
''
:
node
.
key
)
setExpandkeys
(
expandkeys
.
includes
(
node
.
key
)
?
findItemAndDelete
(
expandkeys
,
node
.
key
)
:
[...
expandkeys
,
node
.
key
])
}).
catch
(()
=>
{
setSelectKey
(
selectKey
===
node
.
key
?
''
:
node
.
key
);
setExpandkeys
(
expandkeys
.
includes
(
node
.
key
)
?
findItemAndDelete
(
expandkeys
,
node
.
key
)
:
[...
expandkeys
,
node
.
key
],
);
})
return
false
.
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
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment