style: format codes
This commit is contained in:
1 parent
c3ea18e307
commit
00e79d641e
14 files changed
+2152
-2186
No files matched your search
+267
-273
@@ -1,297 +1,291 @@
|
||||
import React from 'react';
|
||||
import { Card, Divider, Form, Input, AutoComplete, Select, Switch, InputNumber, notification, message } from 'antd';
|
||||
import { INode } from '@antv/g6/lib/interface/item';
|
||||
import { BehaviorNodeModel, BehaviorNodeTypeModel, ArgsDefType } from '../../common/BehaviorTreeModel';
|
||||
import Settings from '../../main-process/Settings';
|
||||
import { FormInstance } from 'antd/lib/form';
|
||||
import Markdown from 'react-markdown';
|
||||
import React from "react";
|
||||
import {
|
||||
Card,
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
AutoComplete,
|
||||
Select,
|
||||
Switch,
|
||||
InputNumber,
|
||||
notification,
|
||||
message,
|
||||
} from "antd";
|
||||
import { INode } from "@antv/g6/lib/interface/item";
|
||||
import {
|
||||
BehaviorNodeModel,
|
||||
BehaviorNodeTypeModel,
|
||||
ArgsDefType,
|
||||
} from "../../common/BehaviorTreeModel";
|
||||
import Settings from "../../main-process/Settings";
|
||||
import { FormInstance } from "antd/lib/form";
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
const { Item } = Form;
|
||||
|
||||
interface NodePanelProps {
|
||||
model: BehaviorNodeModel;
|
||||
settings: Settings;
|
||||
updateNode: (id: string, forceUpdate:boolean) => void;
|
||||
model: BehaviorNodeModel;
|
||||
settings: Settings;
|
||||
updateNode: (id: string, forceUpdate: boolean) => void;
|
||||
}
|
||||
|
||||
interface NodePanelState {
|
||||
|
||||
}
|
||||
interface NodePanelState {}
|
||||
|
||||
export default class NodePanel extends React.Component<NodePanelProps> {
|
||||
formRef = React.createRef<FormInstance>();
|
||||
formRef = React.createRef<FormInstance>();
|
||||
|
||||
componentDidUpdate() {
|
||||
this.formRef.current.resetFields();
|
||||
this.formRef.current.setFieldsValue(this.getInitialValues());
|
||||
}
|
||||
|
||||
getInitialValues() {
|
||||
const { model } = this.props;
|
||||
const initialValues: any = {
|
||||
name: model.name,
|
||||
desc: model.desc,
|
||||
debug: model.debug,
|
||||
customArgs: model.args ? JSON.stringify(model.args, null, " ") : '',
|
||||
};
|
||||
if (model.args) {
|
||||
for (let k in model.args) {
|
||||
initialValues[`args.${k}`] = model.args[k];
|
||||
}
|
||||
}
|
||||
if (model.input) {
|
||||
model.input.forEach((v, i) => {
|
||||
initialValues[`input.${i}`] = v;
|
||||
})
|
||||
}
|
||||
if (model.output) {
|
||||
model.output.forEach((v, i) => {
|
||||
initialValues[`output.${i}`] = v;
|
||||
})
|
||||
}
|
||||
return initialValues;
|
||||
}
|
||||
|
||||
onFinish = (values: any) => {
|
||||
console.log('Success:', values);
|
||||
const { updateNode, model, settings } = this.props;
|
||||
const conf = settings.getNodeConf(values.name)
|
||||
if (!conf) {
|
||||
notification.warn({ message: `节点${values.name}未定义` });
|
||||
return;
|
||||
}
|
||||
var forceUpdate = false;
|
||||
if (model.name != values.name) {
|
||||
model.name = values.name;
|
||||
forceUpdate = true;
|
||||
}
|
||||
model.desc = values.desc;
|
||||
|
||||
var args: any = {};
|
||||
if (values.customArgs) {
|
||||
try {
|
||||
args = JSON.parse(values.customArgs);
|
||||
} catch (e) {
|
||||
message.warn(`您输入的自定义参数不符合json格式${values.customArgs}`);
|
||||
return;
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.formRef.current.resetFields();
|
||||
this.formRef.current.setFieldsValue(this.getInitialValues());
|
||||
}
|
||||
|
||||
if (conf.args) {
|
||||
conf.args.forEach(e => {
|
||||
const k = 'args.' + e.name;
|
||||
if (e.type.indexOf('number') > 0) {
|
||||
args[e.name] = Number(values[k]);
|
||||
getInitialValues() {
|
||||
const { model } = this.props;
|
||||
const initialValues: any = {
|
||||
name: model.name,
|
||||
desc: model.desc,
|
||||
debug: model.debug,
|
||||
customArgs: model.args ? JSON.stringify(model.args, null, " ") : "",
|
||||
};
|
||||
if (model.args) {
|
||||
for (let k in model.args) {
|
||||
initialValues[`args.${k}`] = model.args[k];
|
||||
}
|
||||
}
|
||||
if (model.input) {
|
||||
model.input.forEach((v, i) => {
|
||||
initialValues[`input.${i}`] = v;
|
||||
});
|
||||
}
|
||||
if (model.output) {
|
||||
model.output.forEach((v, i) => {
|
||||
initialValues[`output.${i}`] = v;
|
||||
});
|
||||
}
|
||||
return initialValues;
|
||||
}
|
||||
|
||||
onFinish = (values: any) => {
|
||||
console.log("Success:", values);
|
||||
const { updateNode, model, settings } = this.props;
|
||||
const conf = settings.getNodeConf(values.name);
|
||||
if (!conf) {
|
||||
notification.warn({ message: `节点${values.name}未定义` });
|
||||
return;
|
||||
}
|
||||
var forceUpdate = false;
|
||||
if (model.name != values.name) {
|
||||
model.name = values.name;
|
||||
forceUpdate = true;
|
||||
}
|
||||
model.desc = values.desc;
|
||||
|
||||
var args: any = {};
|
||||
if (values.customArgs) {
|
||||
try {
|
||||
args = JSON.parse(values.customArgs);
|
||||
} catch (e) {
|
||||
message.warn(`您输入的自定义参数不符合json格式${values.customArgs}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.args) {
|
||||
conf.args.forEach((e) => {
|
||||
const k = "args." + e.name;
|
||||
if (e.type.indexOf("number") > 0) {
|
||||
args[e.name] = Number(values[k]);
|
||||
} else {
|
||||
args[e.name] = values[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
model.args = args;
|
||||
this.formRef.current.setFieldsValue({
|
||||
customArgs: model.args ? JSON.stringify(model.args, null, " ") : "",
|
||||
});
|
||||
|
||||
if (conf.input) {
|
||||
model.input = [];
|
||||
conf.input.forEach((e, i) => {
|
||||
model.input.push(values["input." + i] || "");
|
||||
});
|
||||
} else {
|
||||
args[e.name] = values[k];
|
||||
model.input = null;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
model.args = args;
|
||||
this.formRef.current.setFieldsValue({ customArgs: model.args ? JSON.stringify(model.args, null, " ") : '' });
|
||||
if (conf.output) {
|
||||
model.output = [];
|
||||
conf.output.forEach((e, i) => {
|
||||
model.output.push(values["output." + i] || "");
|
||||
});
|
||||
} else {
|
||||
model.output = null;
|
||||
}
|
||||
|
||||
if (conf.input) {
|
||||
model.input = [];
|
||||
conf.input.forEach((e, i) => {
|
||||
model.input.push(values['input.' + i] || '');
|
||||
})
|
||||
} else {
|
||||
model.input = null;
|
||||
}
|
||||
|
||||
if (conf.output) {
|
||||
model.output = [];
|
||||
conf.output.forEach((e, i) => {
|
||||
model.output.push(values['output.' + i] || '');
|
||||
})
|
||||
} else {
|
||||
model.output = null;
|
||||
}
|
||||
|
||||
if (forceUpdate) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
updateNode(model.id.toString(), forceUpdate);
|
||||
};
|
||||
|
||||
onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
console.log("handleSubmit");
|
||||
this.formRef.current.submit();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { model, settings } = this.props;
|
||||
const conf = settings.getNodeConf(model.name);
|
||||
const title = conf.desc;
|
||||
|
||||
const options: any = [];
|
||||
settings.nodeConfig.map((e) => {
|
||||
options.push({ label: `${e.name}(${e.desc})`, value: e.name });
|
||||
})
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 18 },
|
||||
if (forceUpdate) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
updateNode(model.id.toString(), forceUpdate);
|
||||
};
|
||||
return (
|
||||
<Card title={title} style={{ height: window.screen.height - 100, overflow: 'auto' }}>
|
||||
<Form
|
||||
{...layout}
|
||||
name="basic"
|
||||
onFinish={this.onFinish}
|
||||
initialValues={this.getInitialValues()}
|
||||
ref={this.formRef}
|
||||
>
|
||||
<Item
|
||||
label="节点id"
|
||||
>
|
||||
<Input value={model.id} disabled={true} />
|
||||
</Item>
|
||||
<Item
|
||||
label="节点名称"
|
||||
name="name"
|
||||
>
|
||||
<AutoComplete
|
||||
options={options}
|
||||
onBlur={this.handleSubmit}
|
||||
filterOption={(inputValue: string, option: any) => {
|
||||
return option.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item
|
||||
label="节点说明"
|
||||
name="desc"
|
||||
>
|
||||
<Input onBlur={this.handleSubmit} />
|
||||
</Item>
|
||||
<Item
|
||||
label="调试开关"
|
||||
name="debug"
|
||||
valuePropName='checked'
|
||||
>
|
||||
<Switch onChange={this.handleSubmit} />
|
||||
</Item>
|
||||
<Markdown source={conf.doc} />
|
||||
{this.renderInputs(conf)}
|
||||
{this.renderArgs(conf)}
|
||||
{this.renderOutputs(conf)}
|
||||
</Form>
|
||||
</Card >
|
||||
)
|
||||
}
|
||||
|
||||
renderArgs(conf: BehaviorNodeTypeModel) {
|
||||
if (!conf || !conf.args || conf.args.length == 0) {
|
||||
return null;
|
||||
onFinishFailed = (errorInfo: any) => {
|
||||
console.log("Failed:", errorInfo);
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
console.log("handleSubmit");
|
||||
this.formRef.current.submit();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { model, settings } = this.props;
|
||||
const conf = settings.getNodeConf(model.name);
|
||||
const title = conf.desc;
|
||||
|
||||
const options: any = [];
|
||||
settings.nodeConfig.map((e) => {
|
||||
options.push({ label: `${e.name}(${e.desc})`, value: e.name });
|
||||
});
|
||||
|
||||
const layout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 18 },
|
||||
};
|
||||
return (
|
||||
<Card title={title} style={{ height: window.screen.height - 100, overflow: "auto" }}>
|
||||
<Form
|
||||
{...layout}
|
||||
name="basic"
|
||||
onFinish={this.onFinish}
|
||||
initialValues={this.getInitialValues()}
|
||||
ref={this.formRef}
|
||||
>
|
||||
<Item label="节点id">
|
||||
<Input value={model.id} disabled={true} />
|
||||
</Item>
|
||||
<Item label="节点名称" name="name">
|
||||
<AutoComplete
|
||||
options={options}
|
||||
onBlur={this.handleSubmit}
|
||||
filterOption={(inputValue: string, option: any) => {
|
||||
return (
|
||||
option.label.toUpperCase().indexOf(inputValue.toUpperCase()) !==
|
||||
-1
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item label="节点说明" name="desc">
|
||||
<Input onBlur={this.handleSubmit} />
|
||||
</Item>
|
||||
<Item label="调试开关" name="debug" valuePropName="checked">
|
||||
<Switch onChange={this.handleSubmit} />
|
||||
</Item>
|
||||
<Markdown source={conf.doc} />
|
||||
{this.renderInputs(conf)}
|
||||
{this.renderArgs(conf)}
|
||||
{this.renderOutputs(conf)}
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// 普通参数
|
||||
const normalArgs = (e: ArgsDefType) => {
|
||||
const required = e.type.indexOf("?") == -1;
|
||||
if (e.type.indexOf("string") >= 0) {
|
||||
return <Input onBlur={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("int") >= 0) {
|
||||
return <InputNumber style={{ width: '100%' }} onBlur={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("boolean") >= 0) {
|
||||
return <Switch onChange={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("lua") >= 0) {
|
||||
return <Input onBlur={this.handleSubmit} placeholder={'公式'} />;
|
||||
}
|
||||
}
|
||||
|
||||
// 自定义参数
|
||||
const customArgs = () => {
|
||||
return (
|
||||
<Item
|
||||
name='customArgs'
|
||||
label='自定义'
|
||||
key='customArgs'
|
||||
>
|
||||
<Input.TextArea onBlur={this.handleSubmit} style={{ minHeight: 100 }} />
|
||||
</Item>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Divider orientation="left"><h3>常量参数</h3></Divider>
|
||||
{conf && conf.args && conf.args.map((e, i: number) => {
|
||||
const required = e.type.indexOf("?") == -1;
|
||||
return (
|
||||
<Item
|
||||
name={`args.${e.name}`}
|
||||
label={e.desc}
|
||||
key={`args.${e.name}`}
|
||||
valuePropName={e.type.indexOf("boolean") >= 0 ? 'checked' : undefined}
|
||||
rules={[
|
||||
{ required, message: `${e.desc}(${e.name})为必填字段` }
|
||||
]}
|
||||
>
|
||||
{normalArgs(e)}
|
||||
</Item>
|
||||
)
|
||||
})}
|
||||
{customArgs()}
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
renderInputs(conf: BehaviorNodeTypeModel) {
|
||||
if (!conf.input || !conf.input || conf.input.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Divider orientation="left"><h3>输入变量</h3></Divider>
|
||||
{
|
||||
conf.input.map((e, i) => {
|
||||
return (
|
||||
<Item
|
||||
label={e}
|
||||
name={`input.${i}`}
|
||||
key={`input.${i}`}
|
||||
>
|
||||
<Input onBlur={this.handleSubmit} />
|
||||
</Item>
|
||||
)
|
||||
})
|
||||
renderArgs(conf: BehaviorNodeTypeModel) {
|
||||
if (!conf || !conf.args || conf.args.length == 0) {
|
||||
return null;
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderOutputs(conf: BehaviorNodeTypeModel) {
|
||||
if (!conf.output || !conf.output || conf.output.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Divider orientation="left"><h3>输出变量</h3></Divider>
|
||||
{
|
||||
conf.output.map((e, i) => {
|
||||
// 普通参数
|
||||
const normalArgs = (e: ArgsDefType) => {
|
||||
const required = e.type.indexOf("?") == -1;
|
||||
if (e.type.indexOf("string") >= 0) {
|
||||
return <Input onBlur={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("int") >= 0) {
|
||||
return <InputNumber style={{ width: "100%" }} onBlur={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("boolean") >= 0) {
|
||||
return <Switch onChange={this.handleSubmit} />;
|
||||
} else if (e.type.indexOf("lua") >= 0) {
|
||||
return <Input onBlur={this.handleSubmit} placeholder={"公式"} />;
|
||||
}
|
||||
};
|
||||
|
||||
// 自定义参数
|
||||
const customArgs = () => {
|
||||
return (
|
||||
<Item
|
||||
label={e}
|
||||
name={`output.${i}`}
|
||||
key={`output.${i}`}
|
||||
>
|
||||
<Input onBlur={this.handleSubmit} />
|
||||
</Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<Item name="customArgs" label="自定义" key="customArgs">
|
||||
<Input.TextArea onBlur={this.handleSubmit} style={{ minHeight: 100 }} />
|
||||
</Item>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Divider orientation="left">
|
||||
<h3>常量参数</h3>
|
||||
</Divider>
|
||||
{conf &&
|
||||
conf.args &&
|
||||
conf.args.map((e, i: number) => {
|
||||
const required = e.type.indexOf("?") == -1;
|
||||
return (
|
||||
<Item
|
||||
name={`args.${e.name}`}
|
||||
label={e.desc}
|
||||
key={`args.${e.name}`}
|
||||
valuePropName={
|
||||
e.type.indexOf("boolean") >= 0 ? "checked" : undefined
|
||||
}
|
||||
rules={[{ required, message: `${e.desc}(${e.name})为必填字段` }]}
|
||||
>
|
||||
{normalArgs(e)}
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
{customArgs()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderInputs(conf: BehaviorNodeTypeModel) {
|
||||
if (!conf.input || !conf.input || conf.input.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Divider orientation="left">
|
||||
<h3>输入变量</h3>
|
||||
</Divider>
|
||||
{conf.input.map((e, i) => {
|
||||
return (
|
||||
<Item label={e} name={`input.${i}`} key={`input.${i}`}>
|
||||
<Input onBlur={this.handleSubmit} />
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOutputs(conf: BehaviorNodeTypeModel) {
|
||||
if (!conf.output || !conf.output || conf.output.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Divider orientation="left">
|
||||
<h3>输出变量</h3>
|
||||
</Divider>
|
||||
{conf.output.map((e, i) => {
|
||||
return (
|
||||
<Item label={e} name={`output.${i}`} key={`output.${i}`}>
|
||||
<Input onBlur={this.handleSubmit} />
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Card } from 'antd';
|
||||
import { INode } from '@antv/g6/lib/interface/item';
|
||||
import { BehaviorTreeModel } from '../../common/BehaviorTreeModel';
|
||||
|
||||
interface TreePanelProps {
|
||||
model?: BehaviorTreeModel;
|
||||
}
|
||||
|
||||
interface TreePanelState {
|
||||
}
|
||||
|
||||
export default class TreePanel extends React.Component<TreePanelProps, TreePanelState> {
|
||||
state: TreePanelState = {};
|
||||
|
||||
componentDidMount() {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { model } = this.props;
|
||||
if(!model) {
|
||||
return (<div/>)
|
||||
}
|
||||
return (
|
||||
<Card title="概况" style={{ height: "100%" }}>
|
||||
行为树:{model.name}
|
||||
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
import React from "react";
|
||||
import { Card } from "antd";
|
||||
import { INode } from "@antv/g6/lib/interface/item";
|
||||
import { BehaviorTreeModel } from "../../common/BehaviorTreeModel";
|
||||
|
||||
interface TreePanelProps {
|
||||
model?: BehaviorTreeModel;
|
||||
}
|
||||
|
||||
interface TreePanelState {}
|
||||
|
||||
export default class TreePanel extends React.Component<TreePanelProps, TreePanelState> {
|
||||
state: TreePanelState = {};
|
||||
|
||||
componentDidMount() {}
|
||||
|
||||
render() {
|
||||
const { model } = this.props;
|
||||
if (!model) {
|
||||
return <div />;
|
||||
}
|
||||
return (
|
||||
<Card title="概况" style={{ height: "100%" }}>
|
||||
行为树:{model.name}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
+414
-412
@@ -1,412 +1,414 @@
|
||||
import * as React from 'react';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { Row, Col, message } from 'antd';
|
||||
import NodePanel from './NodePanel';
|
||||
import G6, { TreeGraph } from '@antv/g6';
|
||||
import { G6GraphEvent } from '@antv/g6/lib/interface/behavior';
|
||||
import * as Utils from '../../common/Utils';
|
||||
import { BehaviorTreeModel, GraphNodeModel, BehaviorNodeModel } from '../../common/BehaviorTreeModel';
|
||||
import TreePanel from './TreePanel';
|
||||
import Settings from '../../main-process/Settings';
|
||||
|
||||
import './Editor.css';
|
||||
import { clipboard } from 'electron';
|
||||
|
||||
export interface EditorProps {
|
||||
filepath: string;
|
||||
}
|
||||
|
||||
interface EditorState {
|
||||
curNodeId?: string;
|
||||
treeModel?: BehaviorTreeModel;
|
||||
settings?: Settings;
|
||||
}
|
||||
|
||||
export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
private ref: React.RefObject<any>;
|
||||
state: EditorState = {};
|
||||
|
||||
private graph: TreeGraph;
|
||||
private dragSrcId: string;
|
||||
private dragDstId: string;
|
||||
private autoId: number;
|
||||
|
||||
constructor(props: EditorProps) {
|
||||
super(props);
|
||||
this.ref = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const graph = new TreeGraph({
|
||||
container: this.ref.current,
|
||||
width: window.screen.width * 0.66,
|
||||
height: window.screen.height,
|
||||
animate: false,
|
||||
// fitCenter: true,
|
||||
modes: {
|
||||
default: [
|
||||
'drag-canvas',
|
||||
'zoom-canvas',
|
||||
'click-select',
|
||||
'hover',
|
||||
{
|
||||
type: 'collapse-expand',
|
||||
trigger: 'dblclick',
|
||||
onChange: (item, collapsed) => {
|
||||
this.onSelectNode(item.getID());
|
||||
const data = item.getModel();
|
||||
data.collapsed = collapsed;
|
||||
graph.setItemState(item, 'collapsed', data.collapsed as boolean);
|
||||
const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse;
|
||||
const marker = item.get('group').find((ele: any) => ele.get('name') === 'collapse-icon');
|
||||
marker.attr('symbol', icon);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
defaultEdge: {
|
||||
type: 'cubic-horizontal',
|
||||
style: {
|
||||
stroke: '#A3B1BF'
|
||||
}
|
||||
},
|
||||
defaultNode: {
|
||||
type: "TreeNode",
|
||||
},
|
||||
layout: {
|
||||
type: 'compactBox',
|
||||
direction: 'LR',
|
||||
getHGap: () => 50,
|
||||
getWidth: (d: GraphNodeModel) => {
|
||||
return 150;
|
||||
},
|
||||
getHeight: (d: GraphNodeModel) => {
|
||||
if (d.size) {
|
||||
return d.size[1];
|
||||
} else {
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
graph.on('node:mouseenter', (e: G6GraphEvent) => {
|
||||
const { item } = e;
|
||||
if (item.hasState('selected')) {
|
||||
return;
|
||||
}
|
||||
graph.setItemState(item, 'hover', true);
|
||||
});
|
||||
|
||||
graph.on('node:mouseleave', (e: G6GraphEvent) => {
|
||||
const { item } = e;
|
||||
if (item.hasState('selected')) {
|
||||
return;
|
||||
}
|
||||
graph.setItemState(item, 'hover', false);
|
||||
});
|
||||
|
||||
graph.on('nodeselectchange', (e: G6GraphEvent) => {
|
||||
if (e.target) {
|
||||
this.onSelectNode(e.target.getID());
|
||||
} else {
|
||||
this.onSelectNode(null);
|
||||
}
|
||||
});
|
||||
|
||||
const clearDragDstState = () => {
|
||||
if (this.dragDstId) {
|
||||
graph.setItemState(this.dragDstId, 'dragRight', false);
|
||||
graph.setItemState(this.dragDstId, 'dragDown', false);
|
||||
graph.setItemState(this.dragDstId, 'dragUp', false);
|
||||
this.dragDstId = null;
|
||||
}
|
||||
}
|
||||
|
||||
const clearDragSrcState = () => {
|
||||
if (this.dragSrcId) {
|
||||
graph.setItemState(this.dragSrcId, 'dragSrc', false);
|
||||
this.dragSrcId = null;
|
||||
}
|
||||
}
|
||||
|
||||
graph.on('node:dragstart', (e: G6GraphEvent) => {
|
||||
this.dragSrcId = e.item.getID();
|
||||
graph.setItemState(this.dragSrcId, 'dragSrc', true);
|
||||
});
|
||||
graph.on('node:dragend', (e: G6GraphEvent) => {
|
||||
if (this.dragSrcId) {
|
||||
graph.setItemState(this.dragSrcId, 'dragSrc', false);
|
||||
this.dragSrcId = null;
|
||||
}
|
||||
});
|
||||
|
||||
graph.on('node:dragover', (e: G6GraphEvent) => {
|
||||
const dstNodeId = e.item.getID();
|
||||
if (dstNodeId == this.dragSrcId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dragDstId) {
|
||||
graph.setItemState(this.dragDstId, 'dragRight', false);
|
||||
graph.setItemState(this.dragDstId, 'dragDown', false);
|
||||
graph.setItemState(this.dragDstId, 'dragUp', false);
|
||||
}
|
||||
|
||||
const box = e.item.getBBox();
|
||||
if (e.x > box.minX + box.width * 0.6) {
|
||||
graph.setItemState(dstNodeId, 'dragRight', true);
|
||||
} else if (e.y > box.minY + box.height * 0.5) {
|
||||
graph.setItemState(dstNodeId, 'dragDown', true);
|
||||
} else {
|
||||
graph.setItemState(dstNodeId, 'dragUp', true);
|
||||
}
|
||||
this.dragDstId = dstNodeId;
|
||||
});
|
||||
|
||||
graph.on('node:dragleave', (e: G6GraphEvent) => {
|
||||
clearDragDstState();
|
||||
})
|
||||
|
||||
graph.on('node:drop', (e: G6GraphEvent) => {
|
||||
const srcNodeId = this.dragSrcId;
|
||||
const dstNode = e.item;
|
||||
|
||||
var dragDir;
|
||||
if (dstNode.hasState('dragRight')) {
|
||||
dragDir = 'dragRight';
|
||||
} else if (dstNode.hasState('dragDown')) {
|
||||
dragDir = 'dragDown';
|
||||
} else if (dstNode.hasState('dragUp')) {
|
||||
dragDir = 'dragUp';
|
||||
}
|
||||
|
||||
clearDragSrcState();
|
||||
clearDragDstState();
|
||||
|
||||
if (!srcNodeId) {
|
||||
console.log("no drag src");
|
||||
return;
|
||||
}
|
||||
|
||||
if (srcNodeId == dstNode.getID()) {
|
||||
console.log("drop same node");
|
||||
return;
|
||||
}
|
||||
|
||||
const rootData = graph.findDataById('1');
|
||||
const srcData = graph.findDataById(srcNodeId);
|
||||
const srcParent = Utils.findParent(rootData, srcNodeId);
|
||||
const dstData = graph.findDataById(dstNode.getID());
|
||||
const dstParent = Utils.findParent(rootData, dstNode.getID());
|
||||
if (!srcParent) {
|
||||
console.log("no parent!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utils.findFromAllChildren(srcData, dstData.id)) {
|
||||
// 不能将父节点拖到自已的子孙节点
|
||||
console.log("cannot move to child");
|
||||
return;
|
||||
}
|
||||
|
||||
const removeSrc = () => {
|
||||
srcParent.children = srcParent.children.filter(e => e.id != srcData.id);
|
||||
}
|
||||
console.log("dstNode", dstNode);
|
||||
if (dragDir == 'dragRight') {
|
||||
removeSrc();
|
||||
if (!dstData.children) {
|
||||
dstData.children = [];
|
||||
}
|
||||
dstData.children.push(srcData);
|
||||
} else if (dragDir == 'dragUp') {
|
||||
if (!dstParent) {
|
||||
return;
|
||||
}
|
||||
removeSrc();
|
||||
const idx = dstParent.children.findIndex(e => e.id == dstData.id);
|
||||
dstParent.children.splice(idx, 0, srcData);
|
||||
} else if (dragDir == 'dragDown') {
|
||||
if (!dstParent) {
|
||||
return;
|
||||
}
|
||||
removeSrc();
|
||||
const idx = dstParent.children.findIndex(e => e.id == dstData.id);
|
||||
dstParent.children.splice(idx + 1, 0, srcData);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log("cur data", graph.findDataById('1'));
|
||||
this.changeWithoutAnim();
|
||||
});
|
||||
|
||||
const settings = Utils.getRemoteSettings();
|
||||
|
||||
const str = fs.readFileSync(this.props.filepath, 'utf8');
|
||||
let tree: BehaviorTreeModel = JSON.parse(str);
|
||||
const data = Utils.createTreeData(tree.root, settings);
|
||||
this.autoId = Utils.refreshNodeId(data);
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph.fitCenter();
|
||||
graph.set('animate', true);
|
||||
|
||||
this.graph = graph;
|
||||
|
||||
this.setState({ treeModel: tree, settings });
|
||||
}
|
||||
|
||||
onSelectNode(curNodeId: string | null) {
|
||||
const graph = this.graph;
|
||||
|
||||
if (this.state.curNodeId) {
|
||||
graph.setItemState(this.state.curNodeId, 'selected', false);
|
||||
}
|
||||
|
||||
this.setState({ curNodeId });
|
||||
if (this.state.curNodeId) {
|
||||
graph.setItemState(this.state.curNodeId, 'selected', true);
|
||||
}
|
||||
}
|
||||
|
||||
createNode(name: string) {
|
||||
console.log("editor create node", name);
|
||||
const { curNodeId, settings } = this.state;
|
||||
if (!curNodeId) {
|
||||
message.warn("未选中节点");
|
||||
return;
|
||||
}
|
||||
const curNodeData = this.graph.findDataById(curNodeId);
|
||||
const newNodeData: BehaviorNodeModel = {
|
||||
id: this.autoId++,
|
||||
name: name,
|
||||
}
|
||||
if (!curNodeData.children) {
|
||||
curNodeData.children = [];
|
||||
}
|
||||
curNodeData.children.push(Utils.createTreeData(newNodeData, settings));
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
|
||||
deleteNode() {
|
||||
console.log("editor delete node");
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(curNodeId == '1') {
|
||||
message.warn("根节点不能删除!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.onSelectNode(null);
|
||||
const rootData = this.graph.findDataById('1');
|
||||
const parentData = Utils.findParent(rootData, curNodeId);
|
||||
parentData.children = parentData.children.filter(e => e.id != curNodeId);
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
|
||||
updateNode(id: string) {
|
||||
this.graph.changeData();
|
||||
this.graph.layout();
|
||||
}
|
||||
|
||||
changeWithoutAnim() {
|
||||
this.graph.set('animate', false);
|
||||
this.graph.changeData();
|
||||
this.graph.layout();
|
||||
this.graph.set('animate', true);
|
||||
}
|
||||
|
||||
save() {
|
||||
const { filepath } = this.props;
|
||||
const root = Utils.createFileData(this.graph.findDataById('1') as GraphNodeModel);
|
||||
fs.writeFileSync(filepath, JSON.stringify({
|
||||
name: path.basename(filepath).slice(0, -5),
|
||||
root,
|
||||
desc: root.desc,
|
||||
}));
|
||||
}
|
||||
|
||||
copyNode() {
|
||||
console.log("editor copy node");
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
return;
|
||||
}
|
||||
const data = this.graph.findDataById(curNodeId);
|
||||
clipboard.writeText(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
pasteNode() {
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
message.warn("未选中节点");
|
||||
return;
|
||||
}
|
||||
const curNodeData = this.graph.findDataById(curNodeId);
|
||||
try {
|
||||
const str = clipboard.readText();
|
||||
if(!str || str == '') {
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(str);
|
||||
if(!curNodeData.children) {
|
||||
curNodeData.children = [];
|
||||
}
|
||||
curNodeData.children.push(data);
|
||||
this.autoId = Utils.refreshNodeId(this.graph.findDataById('1') as GraphNodeModel);
|
||||
this.changeWithoutAnim();
|
||||
} catch (error) {
|
||||
message.error("粘贴数据有误");
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { curNodeId, treeModel, settings } = this.state;
|
||||
var curNode: any;
|
||||
if (curNodeId) {
|
||||
curNode = this.graph.findDataById(curNodeId);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="editor">
|
||||
<Row className="editorBd">
|
||||
<Col
|
||||
span={18}
|
||||
className="editorContent"
|
||||
ref={this.ref}
|
||||
/>
|
||||
<Col span={6} className="editorSidebar">
|
||||
{
|
||||
curNode ?
|
||||
<NodePanel
|
||||
model={curNode}
|
||||
settings={settings}
|
||||
updateNode={(id, forceUpdate) => {
|
||||
if (forceUpdate) {
|
||||
const data: any = this.graph.findDataById(id);
|
||||
data.conf = settings.getNodeConf(data.name);
|
||||
data.size = Utils.calcTreeNodeSize(data);
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
const item = this.graph.findById(id);
|
||||
item.draw();
|
||||
}}
|
||||
/>
|
||||
:
|
||||
<TreePanel
|
||||
model={treeModel}
|
||||
/>
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
import * as React from "react";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { Row, Col, message } from "antd";
|
||||
import NodePanel from "./NodePanel";
|
||||
import G6, { TreeGraph } from "@antv/g6";
|
||||
import { G6GraphEvent } from "@antv/g6/lib/interface/behavior";
|
||||
import * as Utils from "../../common/Utils";
|
||||
import {
|
||||
BehaviorTreeModel,
|
||||
GraphNodeModel,
|
||||
BehaviorNodeModel,
|
||||
} from "../../common/BehaviorTreeModel";
|
||||
import TreePanel from "./TreePanel";
|
||||
import Settings from "../../main-process/Settings";
|
||||
|
||||
import "./Editor.css";
|
||||
import { clipboard } from "electron";
|
||||
|
||||
export interface EditorProps {
|
||||
filepath: string;
|
||||
}
|
||||
|
||||
interface EditorState {
|
||||
curNodeId?: string;
|
||||
treeModel?: BehaviorTreeModel;
|
||||
settings?: Settings;
|
||||
}
|
||||
|
||||
export default class Editor extends React.Component<EditorProps, EditorState> {
|
||||
private ref: React.RefObject<any>;
|
||||
state: EditorState = {};
|
||||
|
||||
private graph: TreeGraph;
|
||||
private dragSrcId: string;
|
||||
private dragDstId: string;
|
||||
private autoId: number;
|
||||
|
||||
constructor(props: EditorProps) {
|
||||
super(props);
|
||||
this.ref = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const graph = new TreeGraph({
|
||||
container: this.ref.current,
|
||||
width: window.screen.width * 0.66,
|
||||
height: window.screen.height,
|
||||
animate: false,
|
||||
// fitCenter: true,
|
||||
modes: {
|
||||
default: [
|
||||
"drag-canvas",
|
||||
"zoom-canvas",
|
||||
"click-select",
|
||||
"hover",
|
||||
{
|
||||
type: "collapse-expand",
|
||||
trigger: "dblclick",
|
||||
onChange: (item, collapsed) => {
|
||||
this.onSelectNode(item.getID());
|
||||
const data = item.getModel();
|
||||
data.collapsed = collapsed;
|
||||
graph.setItemState(item, "collapsed", data.collapsed as boolean);
|
||||
const icon = data.collapsed ? G6.Marker.expand : G6.Marker.collapse;
|
||||
const marker = item
|
||||
.get("group")
|
||||
.find((ele: any) => ele.get("name") === "collapse-icon");
|
||||
marker.attr("symbol", icon);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultEdge: {
|
||||
type: "cubic-horizontal",
|
||||
style: {
|
||||
stroke: "#A3B1BF",
|
||||
},
|
||||
},
|
||||
defaultNode: {
|
||||
type: "TreeNode",
|
||||
},
|
||||
layout: {
|
||||
type: "compactBox",
|
||||
direction: "LR",
|
||||
getHGap: () => 50,
|
||||
getWidth: (d: GraphNodeModel) => {
|
||||
return 150;
|
||||
},
|
||||
getHeight: (d: GraphNodeModel) => {
|
||||
if (d.size) {
|
||||
return d.size[1];
|
||||
} else {
|
||||
return 50;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
graph.on("node:mouseenter", (e: G6GraphEvent) => {
|
||||
const { item } = e;
|
||||
if (item.hasState("selected")) {
|
||||
return;
|
||||
}
|
||||
graph.setItemState(item, "hover", true);
|
||||
});
|
||||
|
||||
graph.on("node:mouseleave", (e: G6GraphEvent) => {
|
||||
const { item } = e;
|
||||
if (item.hasState("selected")) {
|
||||
return;
|
||||
}
|
||||
graph.setItemState(item, "hover", false);
|
||||
});
|
||||
|
||||
graph.on("nodeselectchange", (e: G6GraphEvent) => {
|
||||
if (e.target) {
|
||||
this.onSelectNode(e.target.getID());
|
||||
} else {
|
||||
this.onSelectNode(null);
|
||||
}
|
||||
});
|
||||
|
||||
const clearDragDstState = () => {
|
||||
if (this.dragDstId) {
|
||||
graph.setItemState(this.dragDstId, "dragRight", false);
|
||||
graph.setItemState(this.dragDstId, "dragDown", false);
|
||||
graph.setItemState(this.dragDstId, "dragUp", false);
|
||||
this.dragDstId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const clearDragSrcState = () => {
|
||||
if (this.dragSrcId) {
|
||||
graph.setItemState(this.dragSrcId, "dragSrc", false);
|
||||
this.dragSrcId = null;
|
||||
}
|
||||
};
|
||||
|
||||
graph.on("node:dragstart", (e: G6GraphEvent) => {
|
||||
this.dragSrcId = e.item.getID();
|
||||
graph.setItemState(this.dragSrcId, "dragSrc", true);
|
||||
});
|
||||
graph.on("node:dragend", (e: G6GraphEvent) => {
|
||||
if (this.dragSrcId) {
|
||||
graph.setItemState(this.dragSrcId, "dragSrc", false);
|
||||
this.dragSrcId = null;
|
||||
}
|
||||
});
|
||||
|
||||
graph.on("node:dragover", (e: G6GraphEvent) => {
|
||||
const dstNodeId = e.item.getID();
|
||||
if (dstNodeId == this.dragSrcId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.dragDstId) {
|
||||
graph.setItemState(this.dragDstId, "dragRight", false);
|
||||
graph.setItemState(this.dragDstId, "dragDown", false);
|
||||
graph.setItemState(this.dragDstId, "dragUp", false);
|
||||
}
|
||||
|
||||
const box = e.item.getBBox();
|
||||
if (e.x > box.minX + box.width * 0.6) {
|
||||
graph.setItemState(dstNodeId, "dragRight", true);
|
||||
} else if (e.y > box.minY + box.height * 0.5) {
|
||||
graph.setItemState(dstNodeId, "dragDown", true);
|
||||
} else {
|
||||
graph.setItemState(dstNodeId, "dragUp", true);
|
||||
}
|
||||
this.dragDstId = dstNodeId;
|
||||
});
|
||||
|
||||
graph.on("node:dragleave", (e: G6GraphEvent) => {
|
||||
clearDragDstState();
|
||||
});
|
||||
|
||||
graph.on("node:drop", (e: G6GraphEvent) => {
|
||||
const srcNodeId = this.dragSrcId;
|
||||
const dstNode = e.item;
|
||||
|
||||
var dragDir;
|
||||
if (dstNode.hasState("dragRight")) {
|
||||
dragDir = "dragRight";
|
||||
} else if (dstNode.hasState("dragDown")) {
|
||||
dragDir = "dragDown";
|
||||
} else if (dstNode.hasState("dragUp")) {
|
||||
dragDir = "dragUp";
|
||||
}
|
||||
|
||||
clearDragSrcState();
|
||||
clearDragDstState();
|
||||
|
||||
if (!srcNodeId) {
|
||||
console.log("no drag src");
|
||||
return;
|
||||
}
|
||||
|
||||
if (srcNodeId == dstNode.getID()) {
|
||||
console.log("drop same node");
|
||||
return;
|
||||
}
|
||||
|
||||
const rootData = graph.findDataById("1");
|
||||
const srcData = graph.findDataById(srcNodeId);
|
||||
const srcParent = Utils.findParent(rootData, srcNodeId);
|
||||
const dstData = graph.findDataById(dstNode.getID());
|
||||
const dstParent = Utils.findParent(rootData, dstNode.getID());
|
||||
if (!srcParent) {
|
||||
console.log("no parent!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Utils.findFromAllChildren(srcData, dstData.id)) {
|
||||
// 不能将父节点拖到自已的子孙节点
|
||||
console.log("cannot move to child");
|
||||
return;
|
||||
}
|
||||
|
||||
const removeSrc = () => {
|
||||
srcParent.children = srcParent.children.filter((e) => e.id != srcData.id);
|
||||
};
|
||||
console.log("dstNode", dstNode);
|
||||
if (dragDir == "dragRight") {
|
||||
removeSrc();
|
||||
if (!dstData.children) {
|
||||
dstData.children = [];
|
||||
}
|
||||
dstData.children.push(srcData);
|
||||
} else if (dragDir == "dragUp") {
|
||||
if (!dstParent) {
|
||||
return;
|
||||
}
|
||||
removeSrc();
|
||||
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
|
||||
dstParent.children.splice(idx, 0, srcData);
|
||||
} else if (dragDir == "dragDown") {
|
||||
if (!dstParent) {
|
||||
return;
|
||||
}
|
||||
removeSrc();
|
||||
const idx = dstParent.children.findIndex((e) => e.id == dstData.id);
|
||||
dstParent.children.splice(idx + 1, 0, srcData);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log("cur data", graph.findDataById('1'));
|
||||
this.changeWithoutAnim();
|
||||
});
|
||||
|
||||
const settings = Utils.getRemoteSettings();
|
||||
|
||||
const str = fs.readFileSync(this.props.filepath, "utf8");
|
||||
let tree: BehaviorTreeModel = JSON.parse(str);
|
||||
const data = Utils.createTreeData(tree.root, settings);
|
||||
this.autoId = Utils.refreshNodeId(data);
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph.fitCenter();
|
||||
graph.set("animate", true);
|
||||
|
||||
this.graph = graph;
|
||||
|
||||
this.setState({ treeModel: tree, settings });
|
||||
}
|
||||
|
||||
onSelectNode(curNodeId: string | null) {
|
||||
const graph = this.graph;
|
||||
|
||||
if (this.state.curNodeId) {
|
||||
graph.setItemState(this.state.curNodeId, "selected", false);
|
||||
}
|
||||
|
||||
this.setState({ curNodeId });
|
||||
if (this.state.curNodeId) {
|
||||
graph.setItemState(this.state.curNodeId, "selected", true);
|
||||
}
|
||||
}
|
||||
|
||||
createNode(name: string) {
|
||||
console.log("editor create node", name);
|
||||
const { curNodeId, settings } = this.state;
|
||||
if (!curNodeId) {
|
||||
message.warn("未选中节点");
|
||||
return;
|
||||
}
|
||||
const curNodeData = this.graph.findDataById(curNodeId);
|
||||
const newNodeData: BehaviorNodeModel = {
|
||||
id: this.autoId++,
|
||||
name: name,
|
||||
};
|
||||
if (!curNodeData.children) {
|
||||
curNodeData.children = [];
|
||||
}
|
||||
curNodeData.children.push(Utils.createTreeData(newNodeData, settings));
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
|
||||
deleteNode() {
|
||||
console.log("editor delete node");
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (curNodeId == "1") {
|
||||
message.warn("根节点不能删除!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.onSelectNode(null);
|
||||
const rootData = this.graph.findDataById("1");
|
||||
const parentData = Utils.findParent(rootData, curNodeId);
|
||||
parentData.children = parentData.children.filter((e) => e.id != curNodeId);
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
|
||||
updateNode(id: string) {
|
||||
this.graph.changeData();
|
||||
this.graph.layout();
|
||||
}
|
||||
|
||||
changeWithoutAnim() {
|
||||
this.graph.set("animate", false);
|
||||
this.graph.changeData();
|
||||
this.graph.layout();
|
||||
this.graph.set("animate", true);
|
||||
}
|
||||
|
||||
save() {
|
||||
const { filepath } = this.props;
|
||||
const root = Utils.createFileData(this.graph.findDataById("1") as GraphNodeModel);
|
||||
fs.writeFileSync(
|
||||
filepath,
|
||||
JSON.stringify({
|
||||
name: path.basename(filepath).slice(0, -5),
|
||||
root,
|
||||
desc: root.desc,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
copyNode() {
|
||||
console.log("editor copy node");
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
return;
|
||||
}
|
||||
const data = this.graph.findDataById(curNodeId);
|
||||
clipboard.writeText(JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
pasteNode() {
|
||||
const { curNodeId } = this.state;
|
||||
if (!curNodeId) {
|
||||
message.warn("未选中节点");
|
||||
return;
|
||||
}
|
||||
const curNodeData = this.graph.findDataById(curNodeId);
|
||||
try {
|
||||
const str = clipboard.readText();
|
||||
if (!str || str == "") {
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(str);
|
||||
if (!curNodeData.children) {
|
||||
curNodeData.children = [];
|
||||
}
|
||||
curNodeData.children.push(data);
|
||||
this.autoId = Utils.refreshNodeId(this.graph.findDataById("1") as GraphNodeModel);
|
||||
this.changeWithoutAnim();
|
||||
} catch (error) {
|
||||
message.error("粘贴数据有误");
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { curNodeId, treeModel, settings } = this.state;
|
||||
var curNode: any;
|
||||
if (curNodeId) {
|
||||
curNode = this.graph.findDataById(curNodeId);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="editor">
|
||||
<Row className="editorBd">
|
||||
<Col span={18} className="editorContent" ref={this.ref} />
|
||||
<Col span={6} className="editorSidebar">
|
||||
{curNode ? (
|
||||
<NodePanel
|
||||
model={curNode}
|
||||
settings={settings}
|
||||
updateNode={(id, forceUpdate) => {
|
||||
if (forceUpdate) {
|
||||
const data: any = this.graph.findDataById(id);
|
||||
data.conf = settings.getNodeConf(data.name);
|
||||
data.size = Utils.calcTreeNodeSize(data);
|
||||
this.changeWithoutAnim();
|
||||
}
|
||||
const item = this.graph.findById(id);
|
||||
item.draw();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<TreePanel model={treeModel} />
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user