You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

447 lines
14 KiB
JavaScript

3 months ago
import React, {PureComponent} from 'react';
import {Input, message, Tree, Modal} from 'antd';
import {DeleteOutlined, EditOutlined, FileOutlined, PlusCircleOutlined, ExclamationCircleOutlined} from '@ant-design/icons';
import {contextMenu, Item, Menu} from 'react-contexify';
import 'react-contexify/dist/ReactContexify.min.css';
import {getNodeByKeyAndCallbackProcess} from '@/components/_utils/algorithmTools';
import {connect} from '@umijs/max';
const {Search} = Input;
@connect(({globaldata, loading}) => ({
globaldata,
loading: loading.models.globaldata,
}))
class AsyncTree extends PureComponent {
state = {
expandedKeys: ['000000000000'],
autoExpandParent: true,
selectedKeys: [],
treeData: [],
selectedTreeByRightClick: "",
searchValue: '',
dataList: [],
display: "block",
};
constructor(props) {
super(props);
this.state.treeData = [
{title: props.rootNodeName, id: '000000000000', key: '000000000000', value: '000000000000', levelcode: "000000000000"},
]
}
componentDidMount() {
const {dispatch, getTreeMethod, reQueryParent} = this.props;
if(!reQueryParent) {
return
}
dispatch({
type: 'globaldata/' + getTreeMethod,
payload: {
parentid: "#"
},
callback: (res) => {
console.log(res)
if (res.success == true && res?.list.length > 0) {
const temList = res.list?.map((item) => {
return {
key: item.id,
...item
}
})
this.setState({
treeData: temList
})
}
}
});
}
//展开收起
onExpand = expandedKeys => {
this.setState({
expandedKeys,
autoExpandParent: false,
});
};
// 选中的树节点
onSelect = (selectedKeys, {node}) => {
const {checkedTreeChild} = this.props;
this.setState({selectedKeys});
if(checkedTreeChild) {
checkedTreeChild(node);
}
};
addMenuTreeNode = () => {
const {handleModalVisible} = this.props;
handleModalVisible(true, this.state.selectedTreeByRightClick, this.reLoadCurrentTreeNode);
};
updateMenuTreeNode = () => {
const {handleUpdateModalVisible} = this.props;
handleUpdateModalVisible(true, this.state.selectedTreeByRightClick, this.reLoadCurrentTreeNodeUpdate);
};
viewMenuTreeNode = () => {
const {handleViewModalVisible} = this.props;
handleViewModalVisible(true, this.state.selectedTreeByRightClick);
};
deleteMenuTreeNode = () => {
const {handleDeleteRecord} = this.props;
Modal.confirm({
title: '你确定要删除吗?',
icon: <ExclamationCircleOutlined />,
content: '删除该节点后其子节点也会删除!这并不会删除该节点及其子节点的的关联数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
handleDeleteRecord(this.state.selectedTreeByRightClick, this.reLoadCurrentTreeNodeDelete)
},
});
};
// 响应右键点击
rightClickTreeNode = ({event, node}) => {
event.preventDefault();
contextMenu.show({
id: 'menu_id',
event: event,
props: {
foo: 'bar'
}
});
if (node.key != '000000000000') {
this.setState({
selectedTreeByRightClick: node,
display: "block",
});
} else {
this.setState({
selectedTreeByRightClick: node,
display: "none",
});
}
}
// 右键菜单
MyAwesomeMenu = () => (
<Menu id='menu_id' theme="light" style={{zIndex: 1000}}>
<Item onClick={this.addMenuTreeNode}>
<PlusCircleOutlined style={{fontSize: 16, paddingTop: 3}}/>
<span style={{paddingLeft: 10}}>添加</span>
</Item>
<Item onClick={this.updateMenuTreeNode} style={{display: this.state.display}}>
<EditOutlined style={{fontSize: 16, paddingTop: 3}}/>
<span style={{paddingLeft: 10}}>修改</span>
</Item>
<Item onClick={this.viewMenuTreeNode} style={{display: this.state.display}}>
<FileOutlined style={{fontSize: 16, paddingTop: 3}}/>
<span style={{paddingLeft: 10}}>查看</span>
</Item>
<Item onClick={this.deleteMenuTreeNode} style={{display: this.state.display}}>
<DeleteOutlined style={{fontSize: 16, paddingTop: 3}}/>
<span style={{paddingLeft: 10}}>删除</span>
</Item>
</Menu>
);
// 组装树数据
updateTreeData(list, key, children) {
return list.map(node => {
if (node.key === key) {
return {
...node,
children,
};
} else if (node.children) {
return {
...node,
children: this.updateTreeData(node.children, key, children),
};
}
return node;
});
}
/**
* 异步加载树数据
* @param key
* @param children
* @returns {Promise<unknown>}
*/
onLoadData = ({key, children}) => {
const {dispatch, getTreeMethod} = this.props;
const params = {
parentid: key
};
return new Promise((resolve) => {
// if (children) {
// resolve();
// return;
// }
dispatch({
type: 'globaldata/' + getTreeMethod,
payload: params,
callback: (res) => {
if (res.success == true) {
const temList = res.list?.map((item) => {
return {
key: item.id,
...item
}
})
this.setState({
treeData: this.updateTreeData(this.state.treeData, key, temList)
});
}
resolve();
}
});
});
};
reLoadCurrentTreeNode = (treeNode, dropKey) => {
const data = [...this.state.treeData];
treeNode.key = treeNode.id;
getNodeByKeyAndCallbackProcess(data, dropKey, (item) => {
item.children = item.children || [];
// where to insert 示例添加到尾部,可以是随意位置
item.children.push(treeNode);
});
this.setState({
treeData: data,
});
};
reLoadCurrentTreeNodeUpdate = (treeNode, currentTreeNodeKey, parentTreeNodeKey) => {
const data = [...this.state.treeData];
getNodeByKeyAndCallbackProcess(data, parentTreeNodeKey, (item) => {
item.children = item.children || {};
item.children = item.children.map((item, key) => item.key == currentTreeNodeKey ? {...item, ...treeNode} : item)
// item.children.push(treeNode);
});
this.setState({
treeData: data,
});
}
reLoadCurrentTreeNodeDelete = (currentTreeNodeKey) => {
const data = [...this.state.treeData];
getNodeByKeyAndCallbackProcess(data, currentTreeNodeKey, (item, index, arr) => {
arr.splice(index, 1);
});
this.setState({
treeData: data,
});
}
// 搜索事件
onChange = e => {
const {treeData, dataList} = this.state;
const {value} = e.target;
const expandedKeys = dataList.map(item => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.title, treeData);
}
return null;
}).filter((item, i, self) => item && self.indexOf(item) === i);
this.setState({
expandedKeys,
searchValue: value,
autoExpandParent: true,
});
};
getParentKey = (title, tree) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some(item => item.title === title)) {
parentKey = node.key;
} else if (this.getParentKey(title, node.children)) {
parentKey = this.getParentKey(title, node.children);
}
}
}
return parentKey;
};
generateList = data => {
if (undefined == data) {
return false;
}
for (let i = 0; i < data.length; i++) {
const node = data[i];
const {key, title} = node;
this.state.dataList.push({key, title: title});
if (node.children) {
this.generateList(node.children);
}
}
};
onDrop = async info => {
const dropKey = info.node.key;
const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split('-');
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
const source_parent_id = info.dragNode.parentid;
const source_levelcode = info.dragNode.levelcode;
let new_levelcode = "";
let dragKey_parent_id = "";
let sequences = "";
let sequencesArray = [];
const data = [...this.state.treeData];
// Find dragObject
let dragObj;
getNodeByKeyAndCallbackProcess(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
let ar;
// Drop on the content 移动到未展开的节点里
getNodeByKeyAndCallbackProcess(data, dropKey, (item, index) => {
new_levelcode = item.levelcode + "/" + dragKey;
dragKey_parent_id = item.key;
ar = item.children || [];
dragObj.levelcode = new_levelcode;
dragObj.parentid = dragKey_parent_id;
item.children = item.children || [];
sequences = item.children.length;
// where to insert 示例添加到尾部,可以是随意位置 push
item.children.unshift(dragObj);
});
ar.forEach((item, index) => {
let arrs = {
id: item.key,
sequences: index
}
sequencesArray.push(arrs);
})
} else if (
(info.node.props.children || []).length > 0 && // Has children
info.node.props.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
getNodeByKeyAndCallbackProcess(data, dropKey, (item) => {
new_levelcode = item.levelcode + "/" + dragKey;
dragKey_parent_id = item.key
dragObj.levelcode = new_levelcode;
dragObj.parentid = dragKey_parent_id;
item.children = item.children || [];
item.children.unshift(dragObj);
});
} else {
let ar;
let i;
getNodeByKeyAndCallbackProcess(data, dropKey, (item, index, arr) => {
new_levelcode = item.levelcode.replace(item.key, "") + dragKey;
dragKey_parent_id = item.parentid
dragObj.levelcode = new_levelcode;
dragObj.parentid = dragKey_parent_id;
ar = arr;
i = index;
});
if (dropPosition === -1) {
sequences = i;
ar.splice(i, 0, dragObj);
} else {
sequences = i + 1;
ar.splice(i + 1, 0, dragObj);
}
ar.forEach((item, index, arr) => {
if(sequences <= index) {
let arrs = {
id: item.key,
sequences: index
}
sequencesArray.push(arrs);
}
})
}
const {dispatch, updateTreeNodeByDragUrl} = this.props;
await dispatch({
type: 'globaldata/' + updateTreeNodeByDragUrl,
payload: {
source_id: dragKey,
source_parentid: source_parent_id,
source_levelcode: source_levelcode,
target_id: dragKey_parent_id,
levelcode: new_levelcode,
sequences: sequences,
sequencesArray: sequencesArray
},
callback: async (res) => {
if(res.success == true) {
message.success('修改成功');
await this.setState({
treeData: data,
});
}
}
});
}
render() {
const { height, modify } = this.props;
const {autoExpandParent, expandedKeys, selectedKeys, treeData} = this.state;
this.generateList(treeData);
return (
<div style={{float: 'left', width: '30%'}}>
<Search style={{marginBottom: 8, width: '100%'}} placeholder={'请输入'}
onChange={this.onChange}/>
<Tree
loadData={this.onLoadData}
onExpand={this.onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onSelect={this.onSelect}
selectedKeys={selectedKeys}
onRightClick={this.rightClickTreeNode}
draggable={modify}
onDrop={this.onDrop}
treeData={treeData}
height={height || 700}
>
</Tree>
{modify ? this.MyAwesomeMenu() : null}
</div>
);
}
}
export default AsyncTree;