2
头图

The previous article mainly explained the addition and generation of visual custom nodes. This article mainly introduces the editing function of custom nodes and realizes the editing of various attributes on custom nodes.

Flowchart Edit Panel

The process node component on the right side of the page is FlowchartFormPanel , which is the same as the component of the custom node list, so we remove FlowchartFormPanel and replace it with the original JsonSchemaForm , to update node attributes.

Add custom properties

We first add some custom attributes when creating a node, and then click on the node to display the attributes in the edit box and edit it. attribute is the custom attribute we added.

 // config-dnd-panel.tsx(见上一篇说明)
export const nodeDataService: NsNodeCollapsePanel.INodeDataService =
  async () => {
    // 这里可以通过接口获取节点列表
    const resData = [
      {
        id: 1,
        renderKey: 'CustomNode',
        label: '开始',
        attrs: {
          attribute: {
            type: 'aaa',
            tag: [1, 2],
          },
          style: {
            width: 280,
            height: 40,
          },
          canvansStyle: {
            width: 120,
            height: 40,
          },
        },
      },
      {
        id: 2,
        renderKey: 'CustomConnecto',
        label: '审核节点',
        attrs: {
          attribute: {
            type: 'bbb',
            tag: [2],
          },
          style: {
            width: 80,
            height: 80,
          },
          canvansStyle: {
            width: 80,
            height: 80,
          },
        },
      },
    ];
    return [
      {
        id: 'NODE',
        header: '节点',
        children: nodeList(resData),
      },
    ];
  };

custom edit

FlowchartFormPanel上一章的组件---aadd5ac17a2052a5186293e0cb6488f6---删除,新建一个文件夹CustomFlowchartFormPanel cae753bb49e90fa448968ce6b5f0436a--- ,新建index.tsxNodeComponent/index.tsxEdgeComponent/index.tsx , CanvasService , these are our main views, Node node edit form, Edge connection edit form, Canvas view edit form.

 // index.tsx
import type {
  NsNodeCmd,
  NsGraph,
  NsEdgeCmd,
  NsJsonSchemaForm,
} from '@antv/xflow';
import {
  JsonSchemaForm,
  XFlowNodeCommands,
  XFlowEdgeCommands,
} from '@antv/xflow';
import { FC } from 'react';
import { CanvasService, canvasSchema } from './CanvasService';
import NodeComponent from './NodeComponent';
import EdgeComponent from './EdgeComponent';

namespace NsJsonForm {
  export const getCustomRenderComponent: NsJsonSchemaForm.ICustomRender = (
    targetType,
    targetData,
    _modelService,
    commandService,
  ) => {
    const updateNode = (node: NsGraph.INodeConfig) => {
      return commandService.executeCommand<NsNodeCmd.UpdateNode.IArgs>(
        XFlowNodeCommands.UPDATE_NODE.id,
        { nodeConfig: node },
      );
    };
    const updateEdge = (edge: NsGraph.IEdgeConfig) => {
      return commandService.executeCommand<NsEdgeCmd.UpdateEdge.IArgs>(
        XFlowEdgeCommands.UPDATE_EDGE.id,
        { edgeConfig: edge, options: {} },
      );
    };
    if (targetType === 'node') {
      return () => (
        <NodeComponent updateNode={updateNode} targetData={targetData} />
      );
    }
    if (targetType === 'edge') {
      return () => (
        <EdgeComponent updateEdge={updateEdge} targetData={targetData} />
      );
    }
    if (targetType === 'canvas') {
      return () => <CanvasService />;
    }

    return null;
  };
  export const formSchemaService: NsJsonSchemaForm.IFormSchemaService = async (
    args,
  ) => {
    return canvasSchema();
  };
}

const CustomFlowchartFormPanel: FC = () => {
  return (
    <JsonSchemaForm
      targetType={['canvas', 'node', 'edge']}
      formSchemaService={NsJsonForm.formSchemaService}
      getCustomRenderComponent={NsJsonForm.getCustomRenderComponent}
      position={{ top: 40, bottom: 0, right: 0, width: 400 }}
    />
  );
};

export default CustomFlowchartFormPanel;

We are in getCustomRenderComponent to determine whether the selected node, connection or main view returns the corresponding custom edit control. It should be noted that the original xflow provides a way to configure the json form, but we This is a completely custom edit box, so the built-in controlMapService is not used. Students who are interested in using the built-in custom form rendering can check the official documentation and try it out. Note that formSchemaService still have to return Click, otherwise it will be in the loading state.

Node editing

The node editing component receives two values, updateNode is the function to update the node, targetData is the node attribute of the clicked node, when we click the save button, the traversal form is attribute Update the corresponding value, Header is a simple public header, nothing to say.

 // NodeComponent/index.tsx
import { FC, useEffect, useState } from 'react';
import { Input, Form, Spin, Button, Select } from 'antd';
import { NsJsonSchemaForm } from '@antv/xflow';
import { set } from 'lodash';
import Header from '../Header';
import styles from './index.less';

const { Option } = Select;

interface NodeComponentProps {
  updateNode: any;
  targetData: NsJsonSchemaForm.TargetData;
}

const NodeComponent: FC<NodeComponentProps> = (props) => {
  const { updateNode, targetData } = props;
  const [loading, setLoading] = useState(true);
  const [form] = Form.useForm();

  useEffect(() => {
    form.setFieldsValue({
      label: targetData?.label,
      ...targetData?.attrs?.attribute,
    });
    setLoading(false);
  }, [targetData]);

  const onFinish = (values: any) => {
    const data: any = {
      ...targetData,
    };
    Object.keys(values).forEach((key: any) => {
      if (key === 'label') {
        set(data, key, values[key]);
      } else {
        set(data.attrs.attribute, key, values[key]);
      }
    });
    updateNode(data);
  };

  return (
    <div className={styles.nodeComponent}>
      <Header title="节点编辑" />
      <Spin spinning={loading}>
        <Form form={form} onFinish={onFinish}>
          <Form.Item name="label" label="节点名">
            <Input />
          </Form.Item>
          <Form.Item name="type" label="类型">
            <Input />
          </Form.Item>
          <Form.Item name="tag" label="类型">
            <Select mode="multiple">
              <Option key={1}>1</Option>
              <Option key={2}>2</Option>
            </Select>
          </Form.Item>
          <Button type="primary" htmlType="submit">
            保存
          </Button>
        </Form>
      </Spin>
    </div>
  );
};

export default NodeComponent;

节点编辑

Online editing

The connection editing is similar to the node editing, so I will not explain too much. It should be noted that the connection attrs does not initially have attribute , so we made a judgment to add a Empty attribute property.

 // EdgeComponent/index.tsx
import { NsJsonSchemaForm } from '@antv/xflow';
import { Input, Button, Form, Spin } from 'antd';
import { set } from 'lodash';
import { FC, useEffect, useState } from 'react';
import Header from '../Header';
import styles from './index.less';
interface EdgeComponentProps {
  updateEdge: any;
  targetData: NsJsonSchemaForm.TargetData;
}

const EdgeComponent: FC<EdgeComponentProps> = (props) => {
  const { updateEdge, targetData } = props;
  const [loading, setLoading] = useState(true);
  const [form] = Form.useForm();

  useEffect(() => {
    form.setFieldsValue({
      ...targetData?.attrs?.attribute,
    });
    setLoading(false);
  }, [targetData]);

  const onFinish = (values: any) => {
    const data: any = {
      ...targetData,
    };
    Object.keys(values).forEach((key: any) => {
      if (!data.attrs?.attribute) {
        set(data.attrs, 'attribute', {});
      }
      set(data.attrs.attribute, key, values[key]);
    });
    updateEdge(data);
  };

  return (
    <div className={styles.edgeComponent}>
      <Header title="连线编辑" />
      <Spin spinning={loading}>
        <Form form={form} onFinish={onFinish}>
          <Form.Item name="key" label="key">
            <Input />
          </Form.Item>
          <Form.Item name="value" label="value">
            <Input />
          </Form.Item>
          <Button type="primary" htmlType="submit">
            保存
          </Button>
        </Form>
      </Spin>
    </div>
  );
};

export default EdgeComponent;

连线编辑

Canvas display

The canvas will not be edited here, we will write some text directly

 // CanvasService.tsx
import Header from './Header';

const CanvasService = () => {
  return (
    <div style={{ padding: '15px' }}>
      <Header title="主画布" />
    </div>
  );
};

const canvasSchema = () => {
  return {
    tabs: [
      {
        /** Tab的title */
        name: '画布配置',
        groups: [],
      },
    ],
  };
};

export { CanvasService, canvasSchema };

Try to modify the attributes of the node and the connection line, save and then save the entire process (in the previous issue, we stored the data in localStorage), refresh the page, click the node and connection line again, and see if the modified version can be successfully displayed.

Ok, so our custom node editing is basically completed, and the custom attribute editing and inverse display of nodes and connections are realized. A basic process visualization function is basically completed, but the current editing functions include Some shortcomings, such as custom attributes need to determine their type, what control do we need to use to render the editor, add attributes, we need to edit the control code to add the corresponding form, can not add any attributes, so what can solve this problem, let We can add attributes at will, and we can render the form we want without changing the code. This is the content of our next optimization: use formliy to generate a custom form to edit node attributes, look forward to it .

Address of this article: link
This article github address: link
github demo address: link


陌路凡歌
7.9k 声望258 粉丝

人笨,多听,多写,多看书