Commit d1f80bf8 authored by 张烨's avatar 张烨

feat: 树形选择器组件

parent 808a9293
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 : ''
}
>
{obj.text}
</span>
);
obj.value = obj.userID || obj.roleID || obj.stationID || obj.id;
arr.push(obj);
});
setDefaultList(arr);
}, [searchWord]);
// const all = children.every(u => checkAllChildrenChecked(u));
if (isCurrentGroup) {
// setIndeterminate(!all && children.some(u => checkSomeChildrenChecked(u)));
} else {
// 初始进来报告自己的值
getValueCallback(isChecked ? [id] : [], itemid);
setIndeterminate(false);
}
}, []);
// 监听父级是否勾选,如果父级勾选 则需要吧自己设置勾选状态并回传值
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] : [], itemid);
}
});
// 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}
>
{text}
{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>
......
......@@ -67,18 +67,35 @@ const ListCard = props => {
}}
/>
) : (
dataList &&
dataList.length > 0 &&
dataList.map((item, index) => (
<ListCardItem
{...item}
itemid={index}
key={`item${index}key`}
getValueCallback={getValueCallback}
searchWord={searchWord}
{...props}
/>
))
<>
{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
.filter(d => d.type === 'widgetGroup')
.map((item, index) => (
<ListCardItem
{...item}
itemid={index}
key={`item${index}key`}
getValueCallback={getValueCallback}
searchWord={searchWord}
{...props}
/>
))}
</>
)}
{true && !loading && dataList && (
<Button
......
.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;
}
\ No newline at end of file
}
.boldLabel{
font-size: 14px;
font-weight: bold;
background-color: #fff;
}
......@@ -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);
......
......@@ -51,6 +51,7 @@ const BasicLayout = props => {
// footerRender={() => defaultFooterDom}
rightContentRender={() => <RightContent />}
title="熊猫运维平台"
fixedHeader
route={
{
// routes: config.routes,
......
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