Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
CivManage
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
ReactWeb5
CivManage
Commits
d1f80bf8
Commit
d1f80bf8
authored
4 years ago
by
张烨
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 树形选择器组件
parent
808a9293
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
143 additions
and
85 deletions
+143
-85
ItemCard.jsx
src/components/CheckGroup/ItemCard.jsx
+98
-77
index.jsx
src/components/CheckGroup/index.jsx
+20
-3
itemCard.less
src/components/CheckGroup/itemCard.less
+23
-4
RightContent.js
src/components/GlobalHeader/RightContent.js
+1
-1
BasicLayout.js
src/layouts/BasicLayout.js
+1
-0
No files found.
src/components/CheckGroup/ItemCard.jsx
View file @
d1f80bf8
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
{
debounce
}
from
'lodash'
;
import
{
Checkbox
}
from
'antd'
;
import
classnames
from
'classnames'
;
import
useMergeValue
from
'use-merge-value'
;
import
styles
from
'./itemCard.less'
;
const
CheckGroup
=
Checkbox
.
Group
;
const
getId
=
item
=>
item
.
userID
||
item
.
roleID
||
item
.
stationID
||
item
.
id
;
const
isGroup
=
node
=>
[
'widgetGroup'
].
includes
(
node
?.
type
||
node
);
const
checkChildrenByCondition
=
(
item
,
fn
,
tag
=
'every'
)
=>
{
if
(
!
isGroup
(
item
))
{
return
fn
(
item
);
}
return
item
.
children
[
tag
](
t
=>
checkChildrenByCondition
(
t
,
fn
,
tag
));
};
const
ListCardItem
=
props
=>
{
const
{
getValueCallback
,
itemid
,
// userList,
Child
,
OUName
,
searchWord
,
allChecked
,
groupIndeterminate
,
children
,
text
,
type
,
isChecked
,
...
rest
}
=
props
;
// id
const
id
=
getId
(
rest
);
// 当前checkbox为分组时有用,是否部分选中
const
[
indeterminate
,
setIndeterminate
]
=
useState
(
false
);
// 用来收集子节点的值
const
[
childValues
,
setChildValues
]
=
useState
({});
const
[
allChecked
,
setAllChecked
]
=
useState
(
false
);
// 全选状态
const
[
checkList
,
setCheckList
]
=
useState
([]);
// 复选框列表选中的值
const
[
defaultList
,
setDefaultList
]
=
useState
([]);
// 默认的选项
const
[
submitArr
,
setSubmitArr
]
=
useState
([]);
// 提交的数组
// const submitArr = [];
// 是否是地图组件
const
isMapWedgt
=
type
===
'widgetUIPage'
;
// 当前组件是否是分组id
const
isCurrentGroup
=
isGroup
(
type
);
// 当前组件是否勾选
const
[
checked
,
setChecked
]
=
useState
(
isChecked
);
useEffect
(()
=>
{
let
arr
=
[]
;
children
.
map
((
item
,
index
)
=>
{
let
obj
=
{
...
item
}
;
obj
.
label
=
(
<
span
className=
{
searchWord
&&
obj
.
text
.
includes
(
searchWord
)
?
styles
.
isSearch
:
''
// const all = children.every(u => checkAllChildrenChecked(u))
;
if
(
isCurrentGroup
)
{
// setIndeterminate(!all && children.some(u => checkSomeChildrenChecked(u)))
;
}
else
{
// 初始进来报告自己的值
getValueCallback
(
isChecked
?
[
id
]
:
[],
itemid
);
setIndeterminate
(
false
);
}
>
{
obj
.
text
}
</
span
>
);
obj
.
value
=
obj
.
userID
||
obj
.
roleID
||
obj
.
stationID
||
obj
.
id
;
arr
.
push
(
obj
);
});
setDefaultList
(
arr
);
},
[
searchWord
]);
},
[]);
// 监听父级是否勾选,如果父级勾选 则需要吧自己设置勾选状态并回传值
useEffect
(()
=>
{
let
arr2
=
[];
children
.
map
((
item
,
index
)
=>
{
if
(
item
.
isChecked
)
{
arr2
.
push
(
item
.
userID
||
item
.
roleID
||
item
.
stationID
||
item
.
id
);
if
(
allChecked
!==
checked
&&
!
groupIndeterminate
)
{
setChecked
(
allChecked
);
if
(
!
isCurrentGroup
)
{
getValueCallback
(
allChecked
?
[
id
]
:
[],
item
id
);
}
});
// eslint-disable-next-line no-unused-expressions
getValueCallback
&&
getValueCallback
(
arr2
,
itemid
);
const
all
=
children
.
every
(
u
=>
u
.
isChecked
);
setIndeterminate
(
!
all
&&
children
.
some
(
u
=>
u
.
isChecked
));
setAllChecked
(
all
);
setCheckList
(
arr2
);
},
[
children
]);
const
handleAllChecked
=
e
=>
{
const
{
checked
}
=
e
.
target
;
setAllChecked
(
checked
);
let
arr
=
[];
if
(
checked
)
{
arr
=
defaultList
.
map
(
item
=>
item
.
value
);
}
setIndeterminate
(
false
);
setCheckList
(
arr
);
// eslint-disable-next-line no-unused-expressions
getValueCallback
&&
getValueCallback
(
arr
,
itemid
);
};
},
[
allChecked
,
groupIndeterminate
]);
// 勾选事件处理
const
handleChecked
=
e
=>
{
setCheckList
(
e
);
setAllChecked
(
e
.
length
===
defaultList
.
length
);
setIndeterminate
(
!!
e
.
length
&&
e
.
length
<
defaultList
.
length
);
if
(
Child
)
{
const
childValArr
=
Object
.
values
(
childValues
).
flat
();
getValueCallback
([...
e
,
childValArr
],
itemid
);
}
else
{
getValueCallback
(
e
,
itemid
);
const
{
checked
:
v
}
=
e
.
target
;
setChecked
(
v
);
setIndeterminate
(
false
);
if
(
!
isCurrentGroup
)
{
getValueCallback
(
v
?
[
id
]
:
[],
itemid
);
}
};
const
handleChildValueCallback
=
(
arr
,
childIndex
)
=>
{
// 进这里的都是分组,组长统计并处理自己的状态: 全选,部分选,零选
// 拿到所有勾选的子节点
childValues
[
childIndex
]
=
arr
;
const
childValArr
=
Object
.
values
(
childValues
).
flat
();
// 保存组员状态
setChildValues
({
...
childValues
});
getValueCallback
([...
checkList
,
childValArr
]);
// 递归执行的条件函数
const
condition
=
c
=>
childValArr
.
includes
(
getId
(
c
));
const
isAllchecked
=
checkChildrenByCondition
(
{
children
,
type
},
condition
,
);
const
isSomeChecked
=
checkChildrenByCondition
(
{
children
,
type
},
condition
,
'some'
,
);
setIndeterminate
(
!
isAllchecked
&&
isSomeChecked
);
setChecked
(
isAllchecked
);
// eslint-disable-next-line no-unused-expressions
getValueCallback
&&
getValueCallback
([...
childValArr
,
id
],
itemid
);
};
const
renderChild
=
()
=>
Child
&&
Child
.
map
((
c
,
i
)
=>
(
children
&&
children
.
map
((
c
,
i
)
=>
(
<
ListCardItem
{
...
c
}
itemid=
{
i
}
allChecked=
{
checked
}
groupIndeterminate=
{
indeterminate
}
key=
{
`item${i}key`
}
getValueCallback=
{
handleChildValueCallback
}
searchWord=
{
searchWord
}
/>
));
if
(
defaultList
.
length
===
0
)
{
return
null
;
}
return
(
<>
<
div
className=
{
`${styles.divBox}`
}
>
<
div
className=
{
classnames
({
[
styles
.
divBox
]:
isCurrentGroup
,
[
styles
.
divSingle
]:
!
isCurrentGroup
,
})
}
>
<
div
className=
{
styles
.
topCheckbox
}
>
<
Checkbox
indeterminate=
{
indeterminate
}
checked=
{
allChecked
}
onChange=
{
e
=>
{
handleAllChecked
(
e
);
checked=
{
checked
}
onChange=
{
handleChecked
}
>
{
isMapWedgt
?
(
<
div
style=
{
{
display
:
'inline-block'
}
}
ref=
{
r
=>
{
if
(
r
)
r
.
innerHTML
=
text
;
}
}
/>
)
:
(
<
span
className=
{
classnames
({
[
styles
.
boldLabel
]:
isCurrentGroup
,
})
}
>
{
text
}
</
span
>
)
}
</
Checkbox
>
</
div
>
<
div
style=
{
{
width
:
'100%'
}
}
className=
{
styles
.
checkdiv
}
>
{
defaultList
&&
defaultList
.
length
>
0
&&
(
<
CheckGroup
className=
{
styles
.
check
}
onChange=
{
e
=>
{
handleChecked
(
e
);
}
}
value=
{
checkList
}
options=
{
defaultList
}
/>
)
}
{
renderChild
()
}
</
div
>
</
div
>
...
...
This diff is collapsed.
Click to expand it.
src/components/CheckGroup/index.jsx
View file @
d1f80bf8
...
...
@@ -67,9 +67,25 @@ const ListCard = props => {
}
}
/>
)
:
(
dataList
&&
<>
{
dataList
.
some
(
d
=>
d
.
type
===
'widgetUIPage'
)
&&
(
<
ListCardItem
{
...
{
type
:
'
widgetGroup
',
searchWord
,
children
:
dataList
.
filter
(
d
=
>
d.type === 'widgetUIPage'),
text: '地图组件',
itemid: '9999',
}}
getValueCallback=
{
getValueCallback
}
searchWord=
{
searchWord
}
/
>
)
}
{
dataList
&&
dataList
.
length
>
0
&&
dataList
.
map
((
item
,
index
)
=>
(
dataList
.
filter
(
d
=>
d
.
type
===
'widgetGroup'
)
.
map
((
item
,
index
)
=>
(
<
ListCardItem
{
...
item
}
itemid=
{
index
}
...
...
@@ -78,7 +94,8 @@ const ListCard = props => {
searchWord=
{
searchWord
}
{
...
props
}
/>
))
))
}
</>
)
}
{
true
&&
!
loading
&&
dataList
&&
(
<
Button
...
...
This diff is collapsed.
Click to expand it.
src/components/CheckGroup/itemCard.less
View file @
d1f80bf8
.divBox {
display: flex;
// display: flex;
width: 100%;
flex-wrap: wrap;
border: 1px solid #b5b8c8;
margin-top: 20px;
min-height: 50px;
padding: 0 10px 10px 20px;
.ant-checkbox-wrapper{
background-color: #fff;
}
.topCheckbox{
height: 20px;
background-color: #fff;
margin: -10px 0 0 20px;
line-height: 20px;
}
.checkdiv {
display: flex;
margin-left: 20px;
justify-content: space-between;
flex-wrap: wrap;
// margin-left: 20px;
// justify-content: space-between;
}
}
.divSingle{
border: none;
margin-top: 20px;
flex:0 0 150px;
background: transparent;
}
.isSearch{
color: red;
background-color: yellow;
}
.boldLabel{
font-size: 14px;
font-weight: bold;
background-color: #fff;
}
This diff is collapsed.
Click to expand it.
src/components/GlobalHeader/RightContent.js
View file @
d1f80bf8
...
...
@@ -4,7 +4,6 @@ import { Breadcrumb } from 'antd';
import
check
from
'@/components/Authorized/CheckPermissions'
;
import
{
Link
}
from
'react-router-dom'
;
import
HeaderSearch
from
'../HeaderSearch'
;
import
NoticeIcon
from
'../NoticeIcon'
;
import
Avatar
from
'./AvatarDropdown'
;
import
styles
from
'./index.less'
;
...
...
@@ -15,6 +14,7 @@ const routesOptions = route => {
});
return
route
.
routes
.
map
(
r
=>
{
if
(
r
.
hideMenu
)
return
null
;
if
(
r
.
authority
)
{
if
(
r
.
routes
)
{
return
routesOptions
(
r
);
...
...
This diff is collapsed.
Click to expand it.
src/layouts/BasicLayout.js
View file @
d1f80bf8
...
...
@@ -51,6 +51,7 @@ const BasicLayout = props => {
// footerRender={() => defaultFooterDom}
rightContentRender
=
{()
=>
<
RightContent
/>
}
title
=
"熊猫运维平台"
fixedHeader
route
=
{
{
// routes: config.routes,
...
...
This diff is collapsed.
Click to expand it.
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