乐趣区

关于前端:xflow流程可视化节点编辑关联表单模板

后面两篇咱们介绍了阿里的表单设计器及利用,本篇咱们将回归咱们的流程设计,将表单模板和流程可视化的编辑性能关联起来,批改表单模板就能批改节点或连线的属性编辑,达到真正的动静属性编辑成果。

通用表单渲染

咱们把之前的表单模板的预览革新一下,提供一个 formily 渲染模板生成的函数。新建playground/temp-schemaField.tsx:

import React from 'react';
import {createSchemaField} from '@formily/react';
import {
  Form,
  FormItem,
  DatePicker,
  Checkbox,
  Cascader,
  Editable,
  Input,
  NumberPicker,
  Switch,
  Password,
  PreviewText,
  Radio,
  Reset,
  Select,
  Space,
  Submit,
  TimePicker,
  Transfer,
  TreeSelect,
  Upload,
  FormGrid,
  FormLayout,
  FormTab,
  FormCollapse,
  ArrayTable,
  ArrayCards,
} from '@formily/antd';
import {Card, Slider, Rate} from 'antd';

const Text: React.FC<{
  value?: string;
  content?: string;
  mode?: 'normal' | 'h1' | 'h2' | 'h3' | 'p';
}> = ({value, mode, content, ...props}) => {
  const tagName = mode === 'normal' || !mode ? 'div' : mode;
  return React.createElement(tagName, props, value || content);
};

const TempSchemaField = (scope?: any) => {
  const SchemaField = createSchemaField({
    components: {
      Space,
      FormGrid,
      FormLayout,
      FormTab,
      FormCollapse,
      ArrayTable,
      ArrayCards,
      FormItem,
      DatePicker,
      Checkbox,
      Cascader,
      Editable,
      Input,
      Text,
      NumberPicker,
      Switch,
      Password,
      PreviewText,
      Radio,
      Reset,
      Select,
      Submit,
      TimePicker,
      Transfer,
      TreeSelect,
      Upload,
      Card,
      Slider,
      Rate,
    },
    scope,
  });
  return SchemaField;
};

export default TempSchemaField;

原来的预览 playground/Preview.tsx 批改为:

import {FC, Ref, useEffect, useImperativeHandle, useState} from 'react';
import {Modal} from 'antd';
import {request} from 'umi';
import {Form, Submit} from '@formily/antd';
import tempSchemaField from '@/pages/playground/temp-schemaField';
import {LgetItem} from '@/utils/storage';
import {createForm} from '@formily/core';

interface PreviewProps {previewRef: Ref<{ setVisible: (flag: boolean) => void }>;
  modalConfig: {[key: string]: any };
}

const Preview: FC<PreviewProps> = ({previewRef, modalConfig}) => {const [visible, setVisible] = useState(false);
  const [params, setParams] = useState<{[key: string]: any }>({});
  const normalForm = createForm({});
  const SchemaField = tempSchemaField({$fetch: request,});

  useImperativeHandle(previewRef, () => ({setVisible,}));
  useEffect(() => {if (modalConfig && visible) {const playgroundList = LgetItem('playgroundList') || [];
      const data = playgroundList.find((s: { id: string}) => s.id === modalConfig.id,
      );
      setParams(data?.params || {});
    }
    request('/api/users', {
      params: {id: 1,},
    }).then((res) => {console.log(res);
    });
  }, [modalConfig, visible]);

  const handleCancel = () => {setVisible(false);
  };
  return (
    <Modal
      title="模板预览"
      visible={visible}
      onCancel={handleCancel}
      footer={null}
    >
      <Form form={normalForm} onAutoSubmit={console.log} {...params.form}>
        <SchemaField schema={params.schema} />
        <Submit block> 保留 </Submit>
      </Form>
    </Modal>
  );
};

export default Preview;

节点编辑革新

import {FC, useEffect, useState} from 'react';
import {Button, Empty, message} from 'antd';
import {NsJsonSchemaForm, useXFlowApp} from '@antv/xflow';
import {Form, Submit} from '@formily/antd';
import {createForm} from '@formily/core';
import {set} from 'lodash';
import {request} from 'umi';
import tempSchemaField from '@/pages/playground/temp-schemaField';
import Header from '../Header';
import styles from './index.less';
import {LgetItem} from '@/utils/storage';

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

const NodeComponent: FC<NodeComponentProps> = (props) => {const [formilySchema, setFormilySchema] = useState<{[key: string]: any }>({},
  );
  const {updateNode, targetData, readOnly = false} = props;
  const SchemaField = tempSchemaField({$fetch: request,});

  const xflowApp = useXFlowApp();
  const form = createForm({
    values: {
      label: targetData?.label,
      ...targetData?.attrs?.attribute,
    },
    readOnly,
  });

  const onFinish = async (values: any) => {const grap = await xflowApp.getGraphInstance();
    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);
    message.success('暂存胜利');
    // 失去焦点
    grap.resetSelection();};

  const delBtn = async () => {const grap = await xflowApp.getGraphInstance();
    grap.removeNode(targetData!.id);
  };

  useEffect(() => {
    // 这里用接口获取节点 id 关联的表单模板,我简写了
    if (targetData?.renderKey) {const playgroundList = LgetItem('playgroundList') || [];
      setFormilySchema(playgroundList[0]?.params ?? {});
    }
  }, [targetData]);

  const {form: formProps, schema} = formilySchema;
  return (<div className={styles.nodeComponent}>
      <Header title="节点编辑" />
      <div className="formBox">
        {schema && Object.keys(schema)?.length ? (<Form {...formProps} form={form} onAutoSubmit={onFinish}>
            <SchemaField schema={schema} />
            {readOnly ? null : <Submit block> 保留 </Submit>}
          </Form>
        ) : (
          <Empty
            image={Empty.PRESENTED_IMAGE_SIMPLE}
            description="无表单模板"
          />
        )}
      </div>
      {readOnly ? null : (
        <div className="delBtn">
          <Button size="large" block danger onClick={delBtn}>
            删除节点
          </Button>
        </div>
      )}
    </div>
  );
};

export default NodeComponent;

// index.less

.nodeComponent {
  height: 100%;
  padding: 0 16px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  :global {
    .formBox {
      flex: 1;
      overflow: auto;
      padding-bottom: 16px;
      box-sizing: border-box;
    }
    .delBtn {
      flex: 0 0 40px;
      height: 40px;
      margin-bottom: 16px;
    }
  }
}

连线编辑革新

import {FC, useEffect, useState} from 'react';
import {Empty, Button} from 'antd';
import {useXFlowApp, NsJsonSchemaForm} from '@antv/xflow';
import {Form, Submit} from '@formily/antd';
import {createForm} from '@formily/core';
import {set} from 'lodash';
import {request} from 'umi';
import tempSchemaField from '@/pages/playground/temp-schemaField';
import Header from '../Header';
import styles from './index.less';
import {LgetItem} from '@/utils/storage';

interface EdgeComponentProps {
  updateEdge: any;
  targetData: NsJsonSchemaForm.TargetData;
  readOnly?: boolean;
}

const EdgeComponent: FC<EdgeComponentProps> = (props) => {const [formilySchema, setFormilySchema] = useState<{[key: string]: any }>({},
  );
  const {updateEdge, targetData, readOnly = false} = props;
  const SchemaField = tempSchemaField({$fetch: request,});

  const xflowApp = useXFlowApp();

  const form = createForm({values: targetData!.attrs ?? {},
    readOnly,
  });

  const onFinish = async (values: any) => {const grap = await xflowApp.getGraphInstance();
    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);
    grap.resetSelection();};

  const delBtn = async () => {const grap = await xflowApp.getGraphInstance();
    grap.removeEdge(targetData!.id);
  };

  useEffect(() => {
    // 这里用接口获取节点 id 关联的表单模板,我简写了
    const playgroundList = LgetItem('playgroundList') || [];
    setFormilySchema(playgroundList[0]?.params ?? {});
  }, []);

  const {form: formProps, schema} = formilySchema;

  return (<div className={styles.edgeComponent}>
      <Header title="连线编辑" />
      <div className="formBox">
        {schema && Object.keys(schema)?.length ? (<Form {...formProps} form={form} onAutoSubmit={onFinish}>
            <SchemaField schema={schema} />
            {readOnly ? null : <Submit block> 保留 </Submit>}
          </Form>
        ) : (
          <Empty
            image={Empty.PRESENTED_IMAGE_SIMPLE}
            description="无表单模板"
          />
        )}
      </div>
      {readOnly ? null : (
        <div className="delBtn">
          <Button size="large" block danger onClick={delBtn}>
            删除连线
          </Button>
        </div>
      )}
    </div>
  );
};

export default EdgeComponent;

去表单模板建设一个模板,并配置一下,回到流程图点击节点编辑一下,看是否与表单模板预览的展现一样,批改模板的表单配置,流程图的会同步批改:

好了,动静表单与自定义属性的增加相结合就实现了,一个次要性能较为齐全的流程可视化设计就曾经实现了,然而还有一些细节上的货色须要打磨一下,接下来几篇为细节的优化补充阐明,敬请期待。

本文地址:https://xuxin123.com/web/xflow-contact-formily
本文 github 地址:链接
github demo 地址:链接

退出移动版