Commit 2450732e authored by 武广's avatar 武广

feat: 添加自定义tree

parent aeac15f4
import React, { useState, useImperativeHandle, useRef, forwardRef, useEffect } from 'react';
import { Checkbox } from 'antd';
import { CaretRightOutlined, CaretDownOutlined } from '@ant-design/icons';
import styles from './index.less';
const CheckboxGroup = Checkbox.Group;
const CustomTree = forwardRef((props, ref) => {
const [checked, setChecked] = useState(false);
const [checkExpand, setCheckExpand] = useState(false);
const refNode = useRef(null);
const onChange = e => {
console.log('props.treeData :>> ', props.treeData);
props.onChange(
{
key: props.treeData.key,
children: props.children,
},
!checked,
);
setChecked(!checked);
};
const onClose = () => {
setCheckExpand(false);
};
useImperativeHandle(ref, () => ({
onClose,
}));
// useEffect(() => {
// // console.log('props.hideChildren :>> ', props.hideChildren);
// // setCheckExpand(false);
// console.log('props.treeData :>> ', props.treeData);
// }, [])
return (
<div className={styles['ccheck-box']} ref={refNode}>
{props.children && (
<div className={styles['ccheck-box--expand']} onClick={() => setCheckExpand(!checkExpand)}>
{checkExpand ? <CaretDownOutlined /> : <CaretRightOutlined />}
</div>
)}
<Checkbox
onChange={onChange}
indeterminate={
!props.checked && props.treeData.value ? props.treeData.value.length > 0 : false
}
checked={props.checked}
>
{props.label}
{`${props.checked}`}
</Checkbox>
{checkExpand && <div className={styles['ccheck-box--childrens']}>{props.children}</div>}
</div>
);
});
export default CustomTree;
/* eslint-disable no-unused-expressions */
import React, { useState, useLayoutEffect, useEffect, useRef } from 'react';
import { Row, Col } from 'antd';
import styles from './index.less';
import CTreeNode from './CTreeNode';
import { deepClone } from '@/utils/utils';
const CustomTree = props => {
const [hideChildren, setHideChildern] = useState(false);
const refNode = useRef();
const [treeData, setTreeData] = useState([]);
// const updateTreeData = (list, obj, isChecked) => {
// let checked = false;
// list.forEach(node => {
// if (node.key === obj.key) {
// checked = true;
// if (obj.children) {
// node.value = isChecked ? obj.children.map(item => item.key) : [];
// } else {
// node.value = isChecked ? [obj.key] : []
// }
// } else if (node.children) {
// const res = updateTreeData(node.children, obj, isChecked)
// if (res) {
// node.value ? node.value.push(obj.key) : (node.value = [obj.key]);
// checked = true;
// }
// }
// });
// return checked;
// }
const updateChildren = (list, isChecked) => {
list.forEach(item => {
item.indeterminate = isChecked;
item.checked = isChecked;
if (item.children) {
item.value = isChecked ? item.children.map(c => c.key) : [];
updateChildren(item.children, isChecked);
} else {
item.value = isChecked ? [item.key] : [];
}
});
};
const updateTreeData = (list, obj, isChecked) => {
const json = {
checked: false,
key: obj.key,
};
let checked = false;
list.forEach(node => {
if (node.key === obj.key) {
json.checked = isChecked;
checked = isChecked;
if (node.children) {
node.value = isChecked ? node.children.map(item => item.key) : [];
updateChildren(node.children, isChecked);
} else {
node.value = isChecked ? [obj.key] : [];
node.checked = isChecked;
}
} else if (node.children) {
json.checked = updateTreeData(node.children, obj, isChecked);
// console.log('json.key :>> ', json.key);
// if (json.checked) {
// node.value.push(json.key);
// json.key = node.key;
// } else {
// const index = node.value.indexOf(json.key);
// index > -1 && node.value.splice(index, 1);
// }
}
json.key = node.key;
});
return checked;
};
// const changeChecked = (list, obj, isChecked) => {
// list.forEach(node => {
// if (node.key === obj.key) {
// node.checked = isChecked;
// } else if (node.children) {
// changeChecked(node.children, obj, isChecked);
// }
// });
// }
const onChange = (obj, isChecked) => {
// console.log('obj1 :>> ', obj);
const datas = [...treeData];
// changeChecked(datas, obj, isChecked)
updateTreeData(datas, obj, isChecked);
console.log('datas :>> ', datas);
setTreeData(datas);
};
const getChecked = (parent, item) => {
const res =
parent.value &&
(parent.value.indexOf(item.key) > -1 ||
(item.value && item.value[0] === item.key) ||
(parent.children && parent.value.length === parent.children.length));
if (item.key === '0-0') {
// console.log('item.value && item.value[0] === item.key :>> ', item.value && item.value[0] === item.key);
// console.log('parent1, item2 :>> ', res, parent, item);
}
// res === undefined && console.log('res :>> ', parent, item);
return res;
};
const renderTreeNodes = (data, parent) =>
data &&
data.map(item => {
if (item.children) {
return (
<CTreeNode
ref={refNode}
label={item.title}
onChange={onChange}
checked={getChecked(parent, item)}
key={item.key}
treeData={item}
>
{renderTreeNodes(item.children, item)}
</CTreeNode>
);
}
return (
<CTreeNode
ref={refNode}
label={item.title}
onChange={onChange}
checked={getChecked(parent, item)}
key={item.key}
treeData={item}
/>
);
});
const handleMouseUp = e => {
const isCur = e.path.some(item => item.id === 'my-custom-tree-box');
if (isCur) {
console.log('20001112 :>> ', 20001112);
} else {
console.log('3333 :>> ', 3333);
refNode.current.onClose();
}
};
useEffect(() => {
window.addEventListener('mouseup', handleMouseUp, false);
return () => {
window.removeEventListener('mouseup', handleMouseUp, false);
};
}, []);
const deepCloneArr = (arr = []) => {
// console.log('arr :>> ', arr);
arr.forEach(item => {
item.value = [];
if (item.children) {
deepCloneArr(item.children);
}
});
};
useEffect(() => {
const arr = deepClone(props.treeData || []);
deepCloneArr(arr);
setTreeData(arr);
}, [props.treeData]);
return (
<div className={styles['tree-box']} id="my-custom-tree-box">
<Row justify="space-between">
{treeData.length &&
treeData.map(item => (
<Col span={7}>
<CTreeNode
ref={refNode}
label={item.title}
onChange={onChange}
key={item.key}
checked={getChecked(item, item)}
treeData={item}
>
{item.children && renderTreeNodes(item.children, item)}
</CTreeNode>
</Col>
))}
</Row>
</div>
);
};
CustomTree.CTreeNode = CTreeNode;
export default CustomTree;
.tree-box {
position: relative;
}
.ccheck-box {
position: relative;
display: flex;
box-sizing: border-box;
width: max-content;
padding-right: 0;
padding-left: 22px;
&--expand {
position: absolute;
left: 0;
z-index: 1;
cursor: pointer;
}
&--childrens {
position: absolute;
left: 100%;
z-index: 2;
display: flex;
flex-direction: column;
box-sizing: border-box;
width: max-content;
padding-left: 4px;
background-color: #fff;
box-shadow: 0 0 3px #ccc;
:global(.ant-checkbox-wrapper) {
margin-left: 0;
}
}
}
import React, { useRef, useState } from 'react';
import { Row, Col, Tree } from 'antd';
import { treeDataList } from '../data';
import CustomTree from '@/components/CustomTree';
const { TreeNode } = Tree;
const { CTreeNode } = CustomTree;
const CheckboxList = props => {
const [expandedKeys, setExpandedKeys] = useState([]);
const [treeData, setTreeData] = useState(treeDataList);
const refTreeNode = useRef();
const renderTreeNodes = data =>
data.map(item => {
if (item.children) {
return (
<CTreeNode ref={refTreeNode} label={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</CTreeNode>
);
}
return <CTreeNode ref={refTreeNode} key={item.key} label={item.title} {...item} />;
});
const updateTreeData = (list, key, children) =>
list.map(node => {
if (node.key === key) {
return {
...node,
children,
};
}
if (node.children) {
return {
...node,
children: updateTreeData(node.children, key, children),
};
}
return node;
});
const onExpand = expandedKeysValue => {
console.log('onExpand', expandedKeysValue);
setExpandedKeys(expandedKeysValue);
};
const onLoadData = ({ key, children }) =>
new Promise(resolve => {
if (children) {
resolve();
return;
}
setTimeout(() => {
setTreeData(origin =>
updateTreeData(origin, key, [
{ title: 'Child Node', key: `${key}-0` },
{ title: 'Child Node', key: `${key}-1` },
]),
);
resolve();
}, 1000);
});
return (
// <CustomTree>
// <Row justify="space-between">
// {treeData && treeData.length && treeData.map(item =>
// <Col span={7}>
// <CTreeNode treeData={item} ref={refTreeNode} label={item.title}>
// {item.children && renderTreeNodes(item.children)}
// </CTreeNode>
// </Col>,
// )}
// </Row>
// </CustomTree>
<CustomTree treeData={treeData} />
);
};
export default CheckboxList;
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Modal, Input, Select, Cascader, Tag, notification } from 'antd';
import { Modal, Input, Select, Cascader, Tag, notification, Tree, Col, Row } from 'antd';
import React, { useState, useEffect } from 'react';
import { el } from 'date-fns/locale';
import { areaList, getAddTemplate, forbiddenAddress } from '../services';
import { treeDataList } from '../data';
import CheckboxList from './CheckboxList';
const { Option } = Select;
const { TreeNode } = Tree;
const AddAreaModal = props => {
const {
......@@ -16,6 +19,9 @@ const AddAreaModal = props => {
const [addList, setAddList] = useState([]);
const [selectedList, setSelectedList] = useState([]);
const [selected, setSelect] = useState([]);
const [expandedKeys, setExpandedKeys] = useState([]);
const [treeData, setTreeData] = useState(treeDataList);
const formItemLayout = {
labelCol: {
span: 6,
......@@ -24,54 +30,19 @@ const AddAreaModal = props => {
span: 16,
},
};
const getAreaList = async () => {
if (!addList.length) {
const loadProvice = async () => {
const data = await areaList();
const newData = [];
data.map(item =>
newData.push({
label: item.addrName,
value: item.addrId,
addressId: item.addrId,
addressLevel: item.addrLevel,
isLeaf: item.addrLevel === 4,
}),
);
setAddList(newData);
if (data && data.length) {
const arr = data.map(item => ({
title: item.addrName,
key: item.addrId,
level: item.addrLevel,
children: [],
}));
setTreeData(arr);
}
};
const onChange = (value, selectedOptions) => {
setSelect([...selectedOptions]);
};
// 加载
const loadData = async selectedOptions => {
const targetOption = selectedOptions.slice(-1)[0];
targetOption.loading = true;
const data = await areaList({ parentId: targetOption.addressId });
if (!data.length) {
targetOption.loading = false;
} else {
const newData = [];
data.map(itemData =>
newData.push({
label: itemData.addrName,
value: itemData.addrId,
addressId: itemData.addrId,
addressLevel: itemData.addrLevel,
isLeaf: false,
}),
);
targetOption.children = newData;
targetOption.loading = false;
setAddList([...addList]);
}
};
// 限制区域删除
const preventDefault = val => {
const newArr = selectedList.filter(item => item !== val);
setSelectedList([...newArr]);
};
const onCancel = () => {
setSelectedList([]);
resetFields();
......@@ -128,34 +99,70 @@ const AddAreaModal = props => {
return reslutData;
};
const onPopupVisibleChange = labels => {
if (!labels && selected?.length) {
const select = selected.slice(-1)[0];
const arr = selected.map(x => x.label);
select.addressName = arr.join('/');
const reslutData = getChilds(select);
// 用中文字符排序
const resultArray = reslutData.sort((param1, param2) =>
param1.addressName.localeCompare(param2.addressName, 'zh'),
const renderTreeNodes = data =>
data.map(item => {
if (item.children) {
return (
<TreeNode title={item.name} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
setSelectedList([...resultArray]);
setSelect([]);
}
return <TreeNode key={item.key} {...item} />;
});
const updateTreeData = (list, key, children) =>
list.map(node => {
if (node.key === key) {
return {
...node,
children,
};
}
if (node.children) {
return {
...node,
children: updateTreeData(node.children, key, children),
};
}
return node;
});
const onExpand = expandedKeysValue => {
console.log('onExpand', expandedKeysValue);
setExpandedKeys(expandedKeysValue);
};
const onLoadData = ({ key, children }) =>
new Promise(resolve => {
if (children) {
resolve();
return;
}
setTimeout(() => {
setTreeData(origin =>
updateTreeData(origin, key, [
{ title: 'Child Node', key: `${key}-0` },
{ title: 'Child Node', key: `${key}-1` },
]),
);
resolve();
}, 1000);
});
useEffect(() => {
getAreaList();
// getAreaList();
if (props.templateData) {
setSelectedList(props.templateData.list);
}
}, [props.templateData]);
useEffect(() => {
// console.log(selectedList)
}, [selectedList]);
// useEffect(() => {
// props.visible && loadProvice();
// }, [props.visible])
return (
<Modal
title={props.templateData.status ? '编辑限制区域配送模板' : '添加限制区域配送模板'}
maskClosable={false}
visible={visible}
width="500px"
width="1000px"
onCancel={() => onCancel()}
onOk={() => handleOk()}
>
......@@ -166,23 +173,7 @@ const AddAreaModal = props => {
initialValue: templateData.templateName,
})(<Input placeholder="请填写模板名称" maxLength={20} />)}
</Form.Item>
<Form.Item label="限制配送区域">
{getFieldDecorator('name')(
<Cascader
options={addList}
loadData={loadData}
onChange={(val, label) => onChange(val, label)}
allowClear={false}
changeOnSelect
onDropdownVisibleChange={onPopupVisibleChange}
/>,
)}
{selectedList?.map((selItem, selIndex) => (
<Tag closable key={selItem.addressId} onClose={() => preventDefault(selItem)}>
{selItem.addressName}
</Tag>
))}
</Form.Item>
<Form.Item label="限制配送区域">{getFieldDecorator('name')(<CheckboxList />)}</Form.Item>
</Form>
</Modal>
);
......
......@@ -59,3 +59,208 @@ export function columns(res) {
},
];
}
export const treeDataList = [
{
title: '0-0',
key: '0-0',
level: 1,
children: [
{
title: '0-0-0',
key: '0-0-0',
level: 2,
children: [
{ title: '0-0-0-0', key: '0-0-0-0', level: 3 },
{ title: '0-0-0-1', key: '0-0-0-1', level: 3 },
{ title: '0-0-0-2', key: '0-0-0-2', level: 3 },
],
},
// {
// title: '0-0-1',
// key: '0-0-1',
// level: 2,
// children: [
// { title: '0-0-1-0', key: '0-0-1-0', level: 3 },
// { title: '0-0-1-1', key: '0-0-1-1', level: 3 },
// { title: '0-0-1-2', key: '0-0-1-2', level: 3 },
// ],
// },
// {
// title: '0-0-2',
// key: '0-0-2',
// level: 2,
// },
],
},
// {
// title: '0-1',
// key: '0-1',
// level: 1,
// children: [
// { title: '0-1-0-0', key: '0-1-0-0', level: 2 },
// { title: '0-1-0-1', key: '0-1-0-1', level: 2 },
// { title: '0-1-0-2', key: '0-1-0-2', level: 2 },
// ],
// },
// {
// title: '0-2',
// key: '0-2',
// level: 1,
// children: [
// {
// title: '0-2-0',
// key: '0-2-0',
// level: 2,
// children: [
// { title: '0-2-0-0', key: '0-2-0-0', level: 3 },
// { title: '0-2-0-1', key: '0-2-0-1', level: 3 },
// { title: '0-2-0-2', key: '0-2-0-2', level: 3 },
// ],
// },
// {
// title: '0-2-1',
// key: '0-2-1',
// level: 2,
// children: [
// { title: '0-2-1-0', key: '0-2-1-0', level: 3 },
// { title: '0-2-1-1', key: '0-2-1-1', level: 3 },
// { title: '0-2-1-2', key: '0-2-1-2', level: 3 },
// ],
// },
// {
// title: '0-2-2',
// key: '0-2-2',
// level: 2,
// },
// ],
// },
// {
// title: '0-2',
// key: '0-2',
// level: 1,
// children: [
// {
// title: '0-2-0',
// key: '0-2-0',
// level: 2,
// children: [
// { title: '0-2-0-0', key: '0-2-0-0', level: 3 },
// { title: '0-2-0-1', key: '0-2-0-1', level: 3 },
// { title: '0-2-0-2', key: '0-2-0-2', level: 3 },
// ],
// },
// {
// title: '0-2-1',
// key: '0-2-1',
// level: 2,
// children: [
// { title: '0-2-1-0', key: '0-2-1-0', level: 3 },
// { title: '0-2-1-1', key: '0-2-1-1', level: 3 },
// { title: '0-2-1-2', key: '0-2-1-2', level: 3 },
// ],
// },
// {
// title: '0-2-2',
// key: '0-2-2',
// level: 2,
// },
// ],
// },
// {
// title: '0-2',
// key: '0-2',
// level: 1,
// children: [
// {
// title: '0-2-0',
// key: '0-2-0',
// level: 2,
// children: [
// { title: '0-2-0-0', key: '0-2-0-0', level: 3 },
// { title: '0-2-0-1', key: '0-2-0-1', level: 3 },
// { title: '0-2-0-2', key: '0-2-0-2', level: 3 },
// ],
// },
// {
// title: '0-2-1',
// key: '0-2-1',
// level: 2,
// children: [
// { title: '0-2-1-0', key: '0-2-1-0', level: 3 },
// { title: '0-2-1-1', key: '0-2-1-1', level: 3 },
// { title: '0-2-1-2', key: '0-2-1-2', level: 3 },
// ],
// },
// {
// title: '0-2-2',
// key: '0-2-2',
// level: 2,
// },
// ],
// },
// {
// title: '0-2',
// key: '0-2',
// level: 1,
// children: [
// {
// title: '0-2-0',
// key: '0-2-0',
// level: 2,
// children: [
// { title: '0-2-0-0', key: '0-2-0-0', level: 3 },
// { title: '0-2-0-1', key: '0-2-0-1', level: 3 },
// { title: '0-2-0-2', key: '0-2-0-2', level: 3 },
// ],
// },
// {
// title: '0-2-1',
// key: '0-2-1',
// level: 2,
// children: [
// { title: '0-2-1-0', key: '0-2-1-0', level: 3 },
// { title: '0-2-1-1', key: '0-2-1-1', level: 3 },
// { title: '0-2-1-2', key: '0-2-1-2', level: 3 },
// ],
// },
// {
// title: '0-2-2',
// key: '0-2-2',
// level: 2,
// },
// ],
// },
// {
// title: '0-2',
// key: '0-2',
// level: 1,
// children: [
// {
// title: '0-2-0',
// key: '0-2-0',
// level: 2,
// children: [
// { title: '0-2-0-0', key: '0-2-0-0', level: 3 },
// { title: '0-2-0-1', key: '0-2-0-1', level: 3 },
// { title: '0-2-0-2', key: '0-2-0-2', level: 3 },
// ],
// },
// {
// title: '0-2-1',
// key: '0-2-1',
// level: 2,
// children: [
// { title: '0-2-1-0', key: '0-2-1-0', level: 3 },
// { title: '0-2-1-1', key: '0-2-1-1', level: 3 },
// { title: '0-2-1-2', key: '0-2-1-2', level: 3 },
// ],
// },
// {
// title: '0-2-2',
// key: '0-2-2',
// level: 2,
// },
// ],
// },
];
......@@ -11,7 +11,7 @@ import { DISTRIBUTION_AREA } from '@/../config/permission.config';
const TableList = props => {
const { permissions } = props;
const canEditable = permissions[DISTRIBUTION_AREA.EDITABLE];
const [visible, setVisible] = useState(false);
const [visible, setVisible] = useState(true);
const [templateData, settemplateData] = useState('');
const actionRef = useRef();
const reload = () => {
......
......@@ -56,3 +56,39 @@ export function toThousands(data, num) {
export const formatTime = (time, crm = 'YYYY-MM-DD HH:mm:ss') => time.format(crm);
export const resetTime = (time, crm = 'YYYY-MM-DD HH:mm:ss') => moment(time, crm);
// 返回传递给他的任意对象的类
export function isClass(o) {
if (o === null) return 'Null';
if (o === undefined) return 'Undefined';
return Object.prototype.toString.call(o).slice(8, -1);
}
// 深拷贝
export function deepClone(obj) {
let result;
const oClass = isClass(obj);
// 确定result的类型
if (oClass === 'Object') {
result = {};
} else if (oClass === 'Array') {
result = [];
} else {
return obj;
}
// eslint-disable-next-line no-restricted-syntax
for (const key in obj) {
if ({}.hasOwnProperty.call(obj, key)) {
const copy = obj[key];
if (isClass(copy) === 'Object') {
result[key] = deepClone(copy); // 递归调用
} else if (isClass(copy) === 'Array') {
result[key] = deepClone(copy);
} else {
result[key] = obj[key];
}
}
}
return result;
}
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