import React,{ useRef, useEffect, useState, useMemo }from"react";importG6,{ Graph, TreeGraph }from"@antv/g6";import{ IG6GraphEvent, TreeGraphData }from"@antv/g6/lib/types";import{ IEdge, INode }from"@antv/g6/lib/interface/item";let addedCount =0;// Register a custom behavior: add a node when user click the blank part of canvasG6.registerBehavior("click-add-node",{// Set the events and the corresponding responsing function for this behaviorgetEvents(){// The event is canvas:click, the responsing function is onClickreturn{"canvas:click":"onClick",};},// Click eventonClick(ev: IG6GraphEvent){const self =this;const graph = self.graph as TreeGraph;// Add a new nodeconst{ x, y }= graph.getPointByClient(ev.clientX, ev.clientY);
graph.addItem("node",{
x: x,
y: y,
id:`node-${addedCount}`,// Generate the unique id});
addedCount++;},});G6.registerBehavior("click-add-child",{// Set the events and the corresponding responsing function for this behaviorgetEvents(){// The event is canvas:click, the responsing function is onClickreturn{"node:click":"onClick",};},// Click eventonClick(ev: IG6GraphEvent){const self =this;const graph = self.graph as TreeGraph;const item = ev.item;if(item){const model = item.getModel()as TreeGraphData;if(!model.children){
model.children =[];}// Add a new nodeconst{ x, y }= graph.getPointByClient(ev.clientX, ev.clientY);
model.children.push({
x: x,
y: y,
id:`node-${addedCount}`,// Generate the unique id});
graph.updateChild(model, model.id);
addedCount++;}},});G6.registerBehavior("del-node-and-edge",{// Set the events and the corresponding responsing function for this behaviorgetEvents(){return{"node:click":"onClick",// The event is canvas:click, the responsing function is onClick"edge:click":"onEdgeClick",// The event is edge:click, the responsing function is onEdgeClick};},// The responsing function for node:click defined in getEventsonClick(ev: IG6GraphEvent){const self =this;const node = ev.item;const graph = self.graph as TreeGraph;// const itemController = graph.get("itemController");// itemController.removeItem(node);//graph.removeItem(node as INode);
graph.removeChild(node!.getID());},onEdgeClick(ev: IG6GraphEvent){const self =this;const currentEdge = ev.item;const graph = self.graph as TreeGraph;
graph.removeItem(currentEdge as IEdge);},});// Initial dataconst data ={
id:"Modeling Methods",
children:[{
id:"Classification",
children:[{ id:"Logistic regression"},{ id:"Linear discriminant analysis"},{ id:"Rules"},{ id:"Decision trees"},{ id:"Naive Bayes"},{ id:"K nearest neighbor"},{ id:"Probabilistic neural network"},{ id:"Support vector machine"},],},{
id:"Consensus",
children:[{
id:"Models diversity",
children:[{ id:"Different initializations"},{ id:"Different parameter choices"},{ id:"Different architectures"},{ id:"Different modeling methods"},{ id:"Different training sets"},{ id:"Different feature sets"},],},{
id:"Methods",
children:[{ id:"Classifier selection"},{ id:"Classifier fusion"},],},{
id:"Common",
children:[{ id:"Bagging"},{ id:"Boosting"},{ id:"AdaBoost"},],},],},{
id:"Regression",
children:[{ id:"Multiple linear regression"},{ id:"Partial least squares"},{ id:"Multi-layer feedforward neural network"},{ id:"General regression neural network"},{ id:"Support vector regression"},],},],};let graph: Graph;functionApp(){const ref = useRef<HTMLDivElement>(null);const[state, setState]=useState("H");const[show, setShow]=useState(false);const[changeItem, setChangeItem]= useState<INode>();const[label, setLabel]=useState("");const[fill, setfill]=useState("");const[fontSize, setFontSize]=useState(0);useMemo(()=>{G6.registerBehavior("click-change-style",{// Set the events and the corresponding responsing function for this behaviorgetEvents(){return{"node:click":"onClick",// The event is canvas:click, the responsing function is onClick};},// The responsing function for node:click defined in getEventsonClick(ev: IG6GraphEvent){const item = ev.item;const model = item!.getModel();const value = model.label;const f = model.style?.fill;const labelcfg = model.labelCfg;if(typeof value ==="string"){//将节点的值赋给input并绑上onchange给它setLabel(value);if(f){setfill(f);}else{setfill("");}if(labelcfg && labelcfg.style && labelcfg.style.fontSize){setFontSize(labelcfg.style.fontSize);}else{const nlabelcfg ={
style:{
fontSize:14,},};
model.labelCfg = nlabelcfg;setFontSize(14);}setChangeItem(item as INode);//要调用更新,最小是item item才有update}},});},[]);useEffect(()=>{
graph =newG6.TreeGraph({
container: ref.current!,
width:800,
height:800,// The sets of behavior modes
modes:{// Defualt modedefault:["drag-node","drag-canvas","zoom-canvas","collapse-expand",],// Adding node mode
addNode:["click-add-node","click-select"],// Adding edge mode
del:["del-node-and-edge"],
addChild:["click-add-child","click-select"],
changeStyle:["click-change-style"],},// The node styles in different states
nodeStateStyles:{// The node styles in selected state
selected:{
stroke:"#666",
lineWidth:2,
fill:"steelblue",},},
layout:{type:"mindmap",
direction: state,// H / V / LR / RL / TB / BT
getHeight:()=>{return16;},
getWidth:()=>{return16;},
getVGap:()=>{return10;},
getHGap:()=>{return50;},},
defaultEdge:{type:"cubic-horizontal",
style:{
stroke:"#A3B1BF",},},});
graph.node(function(node){return{
label: node.id,};});
graph.data(data);
graph.render();
graph.fitView();return()=> graph.destroy();},[state]);consthandleChange=(e: React.ChangeEvent<HTMLInputElement>)=>{setLabel(e.target.value);//修改state后改变节点Labelif(changeItem){//其他样式控制同理这么操作const model = changeItem.getModel();
model.label = e.target.value;
changeItem.update(model);}};consthandleColor=(e: React.ChangeEvent<HTMLInputElement>)=>{setfill(e.target.value);//修改state后改变节点Labelif(changeItem){//其他样式控制同理这么操作const model = changeItem.getModel();if(model.style){
model.style.fill = e.target.value;
changeItem.update(model);}}};consthandleFontSize=(e: React.ChangeEvent<HTMLInputElement>)=>{const num =parseFloat(e.target.value);setFontSize(num);//修改state后改变节点Labelif(changeItem){//其他样式控制同理这么操作const model = changeItem.getModel();if(model.labelCfg){if(model.labelCfg.style){
model.labelCfg.style.fontSize = num;}else{
model.labelCfg.style ={
fontSize: num,};}
changeItem.update(model);}}};return(<div><button
onClick={()=>{
graph.setMode("addNode");setShow(false);}}>
添加节点
</button><button
onClick={()=>{
graph.setMode("addChild");setShow(false);}}>
添加孩子
</button><button
onClick={()=>{
graph.setMode("default");setShow(false);}}>
默认模式
</button><button
onClick={()=>{
graph.setMode("del");setShow(false);}}>
删除模式
</button><span>请选择布局模式:</span><select
value={state}
onChange={(e)=>{setState(e.target.value);setShow(false);}}><option value="H">H</option><option value="V">V</option><option value="LR">LR</option><option value="RL">RL</option><option value="TB">TB</option><option value="BT">BT</option></select><button
onClick={()=>{setShow(true);
graph.setMode("changeStyle");}}>
修改样式
</button><div style={{ display: show ?"block":"none"}}><div>
修改label
<input value={label} onChange={handleChange}></input></div><div>
修改颜色
<input value={fill} onChange={handleColor}></input></div><div>
修改字体
<input value={fontSize} onChange={handleFontSize}></input></div></div><div ref={ref}></div></div>);}exportdefault App;