上一篇文章次要对可视化的页面进行展现及革新,本篇次要对自定义节点进行阐明。
流程图节点组件
上一篇页面左侧的流程节点组件为 FlowchartNodePanel
,这个组件是基于NodeCollapsePanel
封装的,提供了一些内置的罕用节点,还能够增加一些自定义节点,然而毛病在于内置的节点无奈增加咱们自定义的属性,所以只能作于画图展现,并无流程节点的实际意义。
所以咱们这里去除掉 FlowchartNodePanel
,应用扩展性更强的(原始的)NodeCollapsePanel
进行替换,然而 NodeCollapsePanel
很多办法须要咱们自行添加,本文将一步一步进行阐明。
流程节点要留神的有两点,一个是左侧的节点列表,这个列表提供了可拖拽的节点在主视图进行增加,二是主视图展现的节点(反显及拖拽展现),这两个节点实际上都是应用流程节点组件提供的。
自定义节点
首先咱们把上一章的组件 FlowchartNodePanel
删除,新建一个文件夹 CustomNodeCollapsePanel
,新建index.tsx
和config-dnd-panel.tsx
,前者是咱们的自定义左侧组件,后者是自定义节点的各个办法
// index.tsx
import {NodeCollapsePanel} from '@antv/xflow';
import {FC} from 'react';
import * as panelConfig from './config-dnd-panel';
const CustomNodeCollapsePanel: FC = () => {
return (
<NodeCollapsePanel
footer={<div> Foorter </div>}
onNodeDrop={panelConfig.onNodeDrop}
searchService={panelConfig.searchService}
nodeDataService={panelConfig.nodeDataService}
position={{top: 40, bottom: 0, left: 0, width: 290}}
/>
);
};
export default CustomNodeCollapsePanel;
searchService
为搜寻节点,onNodeDrop
为左侧节点列表拖拽在主视图上的回调,咱们用它在主视图上创立节点,nodeDataService
为返回咱们节点列表数组。
咱们接下来对几个办法进行一一增加阐明:
渲染节点列表
// config-dnd-panel.tsx
import {
NsNodeCollapsePanel,
NsNodeCmd,
uuidv4,
XFlowNodeCommands,
IFlowchartGraphProps,
} from '@antv/xflow';
import Nodes from './Nodes';
const renderNode = (
props: {
data: NsNodeCollapsePanel.IPanelNode;
isNodePanel: boolean;
},
style: {width: number; height: number},
) => {const Node = Nodes[props.data.renderKey!];
return Node ? <Node {...props} style={style} /> : null;
};
const nodeList = (arr: any[]) => {const newArr = arr.map((s) => {
const attr = s.attr;
return {popoverContent: () => <div>{s.label}</div>,
renderKey: s.renderKey || 'CustomNode',
renderComponent: (props: any) => renderNode(props, attr.style),
label: s.label,
id: s.id,
attr: attr,
...attr.canvansStyle,
};
});
return newArr;
};
export const nodeDataService: NsNodeCollapsePanel.INodeDataService =
async () => {
// 这里能够通过接口获取节点列表
const resData = [
{
id: 1,
renderKey: 'CustomNode',
label: '开始',
attr: {
style: {
width: 280,
height: 40,
},
canvansStyle: {
width: 120,
height: 40,
},
},
},
{
id: 2,
renderKey: 'CustomConnecto',
label: '审核节点',
attr: {
style: {
width: 80,
height: 80,
},
canvansStyle: {
width: 80,
height: 80,
},
},
},
];
return [
{
id: 'NODE',
header: '节点',
children: nodeList(resData),
},
];
};
咱们把接口返回的节点属性转换为面板 Dnd 节点所须要的属性构造,我这里申明了两个宽高,次要是一个用于 canvas 主视图展现的节点宽高,一个为节点列表中组件的宽高。
咱们的节点渲染实际上是通过 renderComponent
返回的组件进行渲染的,renderKey
的作用为咱们视图上已有节点进行反显时,能够依据 renderKey
来渲染节点列表中对应的节点。
自定义节点
新建 CustomNodeCollapsePanel/Nodes/index.ts
,咱们这里应用批量导出,Nodes 外面建设每个节点的文件夹,应用index.ts
进行批量导出,先装置解析 require.context
的包yarn add @types/webpack-env -D
// index.ts
const files = require.context('.', true, /index\.tsx$/);
const modules: {[key: string]: any } = {};
function isPromise(obj: Promise<any> | null | undefined) {
return (
obj !== null &&
obj !== undefined &&
typeof obj.then === 'function' &&
typeof obj.catch === 'function'
);
}
files.keys().forEach(async (key) => {const pathArr = key.replace(/(\.\/|\.tsx)/g, '').split('/');
pathArr.pop();
const moduleName = pathArr.join('/').replace(/\/\w{1}/g, function (val) {return val.substring(1, 2).toUpperCase();});
const module = isPromise(files(key)) ? await files(key) : files(key);
modules[moduleName] = module.default;
});
export default modules;
require.context
是 webpack 提供的检索 api,这里多了个 Promise 的判断,如果 umi
开启了 mfsu
,拿到的 module 会是一个按需的异步导出,故判断解决,这个index.ts
的作用是遍历 Nodes 中的所有目录中的index.tsx
,而后将其组装成一个对象返回,列如:
aaa/bbb/index.tsx
ccc/index.tsx
{
aaaBbb: module
ccc: module
}
接下来咱们创立一下咱们的自定义节点 CustomNode
,CustomConnecto
同理就略过了,新建Nodes/CustomNode/index.tsx
:
import type {FC} from 'react';
import type {NsNodeCollapsePanel} from '@antv/xflow';
import styles from './index.less';
interface CustomNodeProps {
data: NsNodeCollapsePanel.IPanelNode;
isNodePanel: boolean;
style: React.CSSProperties;
}
const CustomNode: FC<CustomNodeProps> = ({data, style, isNodePanel}) => {
return (
<div
style={style}
className={`${styles.customNode} ${isNodePanel ? 'isNodePanel' : ''}`}
>
{data.label}
</div>
);
};
export default CustomNode;
index.less:
.customNode {
width: 100%;
height: 100%;
border: 1px solid rgb(162, 177, 195);
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
&:global(.isNodePanel) {border-color: red;}
}
咱们这里建设一个简略的节点,承受传过来的节点属性以及款式,能够用 isNodePanel
这个值来辨别是节点列表渲染还是主视图反显渲染,为 true 时为前者,否则为后者,所以能够用这个值进行不同的返回,比方组件列表是一个圆形,拖拽到主视图能够变成一个方形,当然咱们这里只是简略的把边框色彩批改一下,可自行批改款式。须要留神的是节点的款式宽高不能写死,因为咱们主画布上的图形是能够拖动扭转宽高的,所以咱们承受了传入 style 的宽高为初始宽高。
反显节点
在进行 canvans 视图中的节点渲染时,咱们应用 setNodeRender
设置当 renderKey
为CustomNode
时,应用咱们的 CustomNode
组件进行渲染。
// config-dnd-panel.tsx
export const useGraphConfig: IFlowchartGraphProps['useConfig'] = (config) => {Object.keys(Nodes).map((key) => {config.setNodeRender(key, (props) => {return renderNode({ data: props.data, isNodePanel: false}, props.size);
});
});
};
// Xflow/index.tsx
import {useGraphConfig} from './CustomNodeCollapsePanel/config-dnd-panel';
<FlowchartCanvas
useConfig={useGraphConfig}
position={{top: 40, left: 0, right: 0, bottom: 0}}
>
...
</FlowchartCanvas>
拖拽生成节点
咱们进行左侧组件列表拖拽到主视图时会触发onNodeDrop
,咱们在这个事件里进行节点的增加:
// CustomNodeCollapsePanel/index.tsx
import {
NsNodeCollapsePanel,
NsNodeCmd,
uuidv4,
XFlowNodeCommands,
IFlowchartGraphProps,
} from '@antv/xflow';
import getPorts from './ports';
export const onNodeDrop: NsNodeCollapsePanel.IOnNodeDrop = async (
nodeConfig,
commandService,
) => {
const args: NsNodeCmd.AddNode.IArgs = {nodeConfig: { ...nodeConfig, id: uuidv4(), ports: getPorts()},
};
commandService.executeCommand<NsNodeCmd.AddNode.IArgs>(
XFlowNodeCommands.ADD_NODE.id,
args,
);
};
当然,增加了节点还不够,节点须要连线的,所以咱们新建一个ports.ts
,用来生成连线所需的连贯桩:
// CustomNodeCollapsePanel/ports.ts
import {uuidv4} from '@antv/xflow';
const getAnchorStyle = (position: string) => {
return {position: { name: position},
attrs: {
circle: {
r: 4,
magnet: true,
stroke: '#31d0c6',
strokeWidth: 2,
fill: '#fff',
style: {visibility: 'hidden',},
},
},
zIndex: 10,
};
};
const getPorts = (position = ['top', 'right', 'bottom', 'left']) => {
return {items: position.map((name) => {return { group: name, id: uuidv4() };
}),
groups: {top: getAnchorStyle('top'),
right: getAnchorStyle('right'),
bottom: getAnchorStyle('bottom'),
left: getAnchorStyle('left'),
},
};
};
export default getPorts;
搜寻节点
最初咱们增加一下搜寻节点性能:
// CustomNodeCollapsePanel/index.tsx
export const searchService: NsNodeCollapsePanel.ISearchService = async (nodes: NsNodeCollapsePanel.IPanelNode[] = [],
keyword: string,
) => {const list = nodes.filter((node) => node?.label?.includes(keyword));
return list;
};
自定义节点反显测试
咱们增加几个自定义节点,连接起来,在点击保留时把数据存在 localStorage
,而后在onLoad
时赋值,看看是否失常反显咱们的自定义节点:
// Xflow/config-toolbar.ts
找到 saveGraphDataService:saveGraphDataService: (meta, graphData) => {console.log(graphData);
localStorage.setItem('graphData', JSON.stringify(graphData));
return null;
},
// Xflow/index.tsx
const onLoad: IAppLoad = async (app) => {graphRef.current = await app.getGraphInstance();
const graphData: NsGraph.IGraphData = JSON.parse(localStorage.getItem('graphData')!,
) || {nodes: [], edges: []};
await app.executeCommand<NsGraphCmd.GraphRender.IArgs>(
XFlowGraphCommands.GRAPH_RENDER.id,
{graphData: graphData,},
);
// 居中
await app.executeCommand<NsGraphCmd.GraphZoom.IArgs>(
XFlowGraphCommands.GRAPH_ZOOM.id,
{factor: 'real',},
);
graphBind();};
好了,这样咱们的自定义节点就根本实现了,实现了左侧列表节点及款式,主视图节点及款式,增加连贯桩,连线,主视图反显等性能,然而这时原先右侧的节点编辑性能有些生效了,且当初的编辑性能不能满足咱们的须要,接下来下一篇将介绍自定义节点的编辑性能,编辑自定义节点上的各种属性,纵情期待。
本文地址:链接
本文 github 地址:链接