简易树形选择组件——初始版全部代码
ArrowTree
interface StoreValue {
data: any;
setArrowTreeData: (data: any) => void
}
const defaultData = {
children:[
{
"title":"组织1",
"key": "0-1",
"children":[
{
"title":"部门1",
"key":"0-1-1",
"children": [
{
title: '2018年9月10号校招啦啦啦',
key : '0-1-1-0',
checked: false,
children: [],
"parentKey": "0-1-1"
}
],
"collapsed": true,
"checked": false,
"parentKey": '0-1'
},
{
"title":"部门1.5",
"key":"0-1-2",
"children": [
{
title: '2018年9月10号校招啦啦啦',
key : '0-1-2-0',
checked: false,
children: [],
"parentKey": "0-1-1"
}
],
"collapsed": true,
"checked": false,
"parentKey": '0-1'
},
],
"collapsed": true,
"checked": true,
"parentKey": 'root'
},
{
"title":"组织2",
"key": "0-2",
"children":[
{
"title":"部门2",
"key":"0-2-1",
"children": [
{
title: '2018年和外和发发和发挥',
key : '0-2-1-0',
checked: false,
children: [],
"parentKey": "0-2-1"
}
],
"collapsed": true,
"checked": false,
"parentKey": '0-2'
},
],
"collapsed": true,
"checked": true,
"parentKey": 'root'
}
],
key: "root"
}
const Store = React.createContext({} as StoreValue)
const ArrowTree = (props:any):JSX.Element => {
const [data,setData] = useState(defaultData)
const setArrowTreeData = (newData: any) => {
setData(newData)
}
const TreeArr = data.children.map((item: any,index: number) => {
return (
<div className="arrow-tree" key={item.key}>
<TreeNode paddingLeft={35} positionLeft={20} keyArr={[item.key]} {...item}/>
</div>
)
})
return (
<div>
<Store.Provider value={{data,setArrowTreeData}}>
{TreeArr}
</Store.Provider>
</div>
)
}
TreeNode
// 递归处理子组件的checked
const DealRecursionChild = (dealData: any,checked: boolean) => {
if(dealData.children && dealData.children.length!==0) {
dealData.children.forEach((item:any) => {
item.checked = checked
if(item.children && item.children.length!==0) {
DealRecursionChild(item,checked)
}
})
}
}
// 处理父组件的checked
const DealParent = (dealParentArr: any[],checked: boolean) => {
// 遍历处理其父节点选中不选中的问题
for(let i=dealParentArr.length-2;i>=0;i--) {
let childCount = 0
dealParentArr[i].children.forEach((item: any) => {
if(item.checked === true) {
childCount ++
}
})
if(childCount === dealParentArr[i].children.length) {
dealParentArr[i].checked = true
}else {
dealParentArr[i].checked = false
}
}
}
const TreeNode = (props: any) => {
const {data,setArrowTreeData} = useContext(Store)
const {paddingLeft,positionLeft,collapsed,children,keyArr,title,checked} = props
const handleCollapsed = () => {
// 操作原始的data
const newData = JSON.parse(JSON.stringify(data))
let dealData = newData
keyArr.forEach((key: string) => {
dealData = dealData.children.find((item: any) => item.key === key)
})
dealData.collapsed = !dealData.collapsed
setArrowTreeData(newData)
}
const handleChecked = (e: React.ChangeEvent) => {
// 操作原始的data
e.stopPropagation()
const newData = {...data}
let dealData = newData
// 记录其父节点的问题
let dealParentArr: any[] = []
keyArr.forEach((key: string) => {
dealData = dealData.children.find((item: any) => item.key === key)
dealParentArr.push(dealData)
})
// 不受控 如果受控的话
let checked = !dealData.checked
dealData.checked = checked
// 遍历处理其子节点选中不选中的问题
DealRecursionChild(dealData,checked)
// 遍历处理其父节点选中不选中的问题
DealParent(dealParentArr,checked)
setArrowTreeData(newData)
}
const mapTreeData = (children: any[],paddingLeft: number) => {
const newChildren = children.map(item => {
const newKeyArr = [...keyArr,item.key]
return <TreeNode paddingLeft={paddingLeft+15} positionLeft={paddingLeft} keyArr={newKeyArr} {...item}/>
})
return newChildren
}
return (
<>
<div className="show" onClick={handleCollapsed} style={{paddingLeft: paddingLeft+'px'}}>
<div>
{ children.length!==0 ?(<strong className={`${collapsed?'':'arrow-down' } arrow-icon`} style={{left: positionLeft-17+'px'}}>
<Icon/>
</strong>):null}
<div className='checkbox' style={{left:positionLeft+'px'}}>
<input type="checkbox" onChange={handleChecked} checked={checked}/>
</div>
<div>{title}</div>
</div>
</div>
<div className="children">
{
(!collapsed && Array.isArray(children) && children.length !==0) ?
mapTreeData(children,paddingLeft) : null
}
</div>
</>
)
}
export default ArrowTree
index.css
.arrow-tree {
width: 240px;
line-height: 30px;
}
.arrow-tree .show {
cursor: pointer;
}
.arrow-tree .show {
position: relative;
width: 100%;
box-sizing: border-box;
color: #111;
font-size: 14px;
}
.arrow-tree .show .checkbox {
position: absolute;
left: 20px;
top: 1px;
}
.arrow-tree .show .checkbox+div {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.arrow-tree .show .arrow-icon {
position: absolute;
}
.arrow-tree .show .arrow-down {
transform: rotate(90deg);
}
.arrow-tree .show:hover {
background-color: skyblue;
}
.arrow-tree .children {
box-sizing: border-box;
}
Icon
import React from 'react'
const Icon = ():JSX.Element => {
return (
<svg width="12px" height="12px" viewBox="0 0 18 18" color="#83898F"><g fill="none" fillRule="evenodd"><path d="M0 0h18v18H0z"></path><path d="M5 2.944c0-1.032.297-1.467 2.014 0l5.57 5.13a1.24 1.24 0 0 1 0 1.855l-5.57 5.13c-1.697 1.594-2.014.864-2.014 0V2.943z" fill="#83898F"></path></g></svg>
)
}
export default Icon