feat(plugin-workflow): add create node component (#292)
This commit is contained in:
		
							parent
							
								
									5039896b16
								
							
						
					
					
						commit
						e002777b63
					
				@ -65,7 +65,7 @@ export function parseStringValue(value: string, Types) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    type,
 | 
					    type,
 | 
				
			||||||
    options: paths.length ? (Types || VariableTypes)[type].parse(paths) : {}
 | 
					    options: paths.length ? (Types || VariableTypes)[type]?.parse(paths) : {}
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -252,7 +252,7 @@ export function Operand({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const { type } = operand;
 | 
					  const { type } = operand;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { component, appendTypeValue } = Types[type];
 | 
					  const { component, appendTypeValue } = Types[type] || {};
 | 
				
			||||||
  const VariableComponent = typeof component === 'function' ? component(operand) : NullRender;
 | 
					  const VariableComponent = typeof component === 'function' ? component(operand) : NullRender;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										156
									
								
								packages/client/src/workflow/nodes/create.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								packages/client/src/workflow/nodes/create.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,156 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { observer, useForm } from '@formily/react';
 | 
				
			||||||
 | 
					import { action } from '@formily/reactive';
 | 
				
			||||||
 | 
					import { Select } from 'antd';
 | 
				
			||||||
 | 
					import { t } from 'i18next';
 | 
				
			||||||
 | 
					import { css } from '@emotion/css';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { SchemaComponent, useCollectionManager } from '../..';
 | 
				
			||||||
 | 
					import { useCollectionFilterOptions } from '../../collection-manager/action-hooks';
 | 
				
			||||||
 | 
					import { useFlowContext } from '../WorkflowCanvas';
 | 
				
			||||||
 | 
					import { Operand, parseStringValue, VariableTypes, VariableTypesContext, BaseTypeSet } from '../calculators';
 | 
				
			||||||
 | 
					import { FormItem } from '@formily/antd';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  title: '新增',
 | 
				
			||||||
 | 
					  type: 'create',
 | 
				
			||||||
 | 
					  group: 'model',
 | 
				
			||||||
 | 
					  fieldset: {
 | 
				
			||||||
 | 
					    collection: {
 | 
				
			||||||
 | 
					      type: 'string',
 | 
				
			||||||
 | 
					      title: '数据表',
 | 
				
			||||||
 | 
					      name: 'collection',
 | 
				
			||||||
 | 
					      required: true,
 | 
				
			||||||
 | 
					      'x-reactions': ['{{useCollectionDataSource()}}'],
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      'x-component': 'Select',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // multiple: {
 | 
				
			||||||
 | 
					    //   type: 'boolean',
 | 
				
			||||||
 | 
					    //   title: '多条数据',
 | 
				
			||||||
 | 
					    //   name: 'multiple',
 | 
				
			||||||
 | 
					    //   'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					    //   'x-component': 'Checkbox',
 | 
				
			||||||
 | 
					    //   'x-component-props': {
 | 
				
			||||||
 | 
					    //     disabled: true
 | 
				
			||||||
 | 
					    //   }
 | 
				
			||||||
 | 
					    // },
 | 
				
			||||||
 | 
					    params: {
 | 
				
			||||||
 | 
					      type: 'object',
 | 
				
			||||||
 | 
					      name: 'params',
 | 
				
			||||||
 | 
					      title: '数据',
 | 
				
			||||||
 | 
					      'x-decorator': 'FormItem',
 | 
				
			||||||
 | 
					      properties: {
 | 
				
			||||||
 | 
					        values: {
 | 
				
			||||||
 | 
					          type: 'object',
 | 
				
			||||||
 | 
					          title: '',
 | 
				
			||||||
 | 
					          name: 'values',
 | 
				
			||||||
 | 
					          'x-component': 'DynamicFieldset',
 | 
				
			||||||
 | 
					          'x-component-props': {
 | 
				
			||||||
 | 
					            useProps() {
 | 
				
			||||||
 | 
					              const { values } = useForm();
 | 
				
			||||||
 | 
					              return { fields: useCollectionFilterOptions(values.collection) };
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  view: {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  scope: {
 | 
				
			||||||
 | 
					    useCollectionDataSource() {
 | 
				
			||||||
 | 
					      return (field: any) => {
 | 
				
			||||||
 | 
					        const { collections = [] } = useCollectionManager();
 | 
				
			||||||
 | 
					        action.bound((data: any) => {
 | 
				
			||||||
 | 
					          field.dataSource = data.map(item => ({
 | 
				
			||||||
 | 
					            label: t(item.title),
 | 
				
			||||||
 | 
					            value: item.name
 | 
				
			||||||
 | 
					          }));
 | 
				
			||||||
 | 
					        })(collections);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  components: {
 | 
				
			||||||
 | 
					    // NOTE: observer for watching useProps
 | 
				
			||||||
 | 
					    DynamicFieldset: observer(({ value, onChange, useProps }: any) => {
 | 
				
			||||||
 | 
					      const { fields } = useProps();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const VTypes = { ...VariableTypes,
 | 
				
			||||||
 | 
					        constant: {
 | 
				
			||||||
 | 
					          title: '常量',
 | 
				
			||||||
 | 
					          value: 'constant',
 | 
				
			||||||
 | 
					          options: undefined
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return (
 | 
				
			||||||
 | 
					        <fieldset className={css`
 | 
				
			||||||
 | 
					          margin-top: .5em;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          .ant-formily-item{
 | 
				
			||||||
 | 
					            .ant-formily-item-label{
 | 
				
			||||||
 | 
					              line-height: 32px;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .ant-select,
 | 
				
			||||||
 | 
					            .ant-cascader-picker,
 | 
				
			||||||
 | 
					            .ant-picker,
 | 
				
			||||||
 | 
					            .ant-input-number,
 | 
				
			||||||
 | 
					            .ant-input-affix-wrapper{
 | 
				
			||||||
 | 
					              width: auto;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        `}>
 | 
				
			||||||
 | 
					          {fields.map(field => {
 | 
				
			||||||
 | 
					            const operand = typeof value[field.name] === 'string'
 | 
				
			||||||
 | 
					              ? parseStringValue(value[field.name], VTypes)
 | 
				
			||||||
 | 
					              : { type: 'constant', value: value[field.name] };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return (
 | 
				
			||||||
 | 
					              <FormItem label={field.title}>
 | 
				
			||||||
 | 
					                <VariableTypesContext.Provider value={VTypes}>
 | 
				
			||||||
 | 
					                  <Operand
 | 
				
			||||||
 | 
					                    value={operand}
 | 
				
			||||||
 | 
					                    onChange={(next) => {
 | 
				
			||||||
 | 
					                      if (next.type !== operand.type && next.type === 'constant') {
 | 
				
			||||||
 | 
					                        onChange({ ...value, [field.name]: null });
 | 
				
			||||||
 | 
					                      } else {
 | 
				
			||||||
 | 
					                        const { stringify } = VTypes[next.type];
 | 
				
			||||||
 | 
					                        onChange({ ...value, [field.name]: stringify(next) });
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  >
 | 
				
			||||||
 | 
					                    {operand.type === 'constant'
 | 
				
			||||||
 | 
					                      ? <SchemaComponent schema={field.schema} />
 | 
				
			||||||
 | 
					                      : null
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                  </Operand>
 | 
				
			||||||
 | 
					                </VariableTypesContext.Provider>
 | 
				
			||||||
 | 
					              </FormItem>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          })}
 | 
				
			||||||
 | 
					        </fieldset>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  getter({ type, options, onChange }) {
 | 
				
			||||||
 | 
					    const { collections = [] } = useCollectionManager();
 | 
				
			||||||
 | 
					    const { nodes } = useFlowContext();
 | 
				
			||||||
 | 
					    const { config } = nodes.find(n => n.id == options.nodeId);
 | 
				
			||||||
 | 
					    const collection = collections.find(item => item.name === config.collection) ?? { fields: [] };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <Select value={options.path} placeholder="选择字段" onChange={path => {
 | 
				
			||||||
 | 
					        onChange({ type, options: { ...options, path } });
 | 
				
			||||||
 | 
					      }}>
 | 
				
			||||||
 | 
					        {collection.fields
 | 
				
			||||||
 | 
					          .filter(field => BaseTypeSet.has(field.uiSchema.type))
 | 
				
			||||||
 | 
					          .map(field => (
 | 
				
			||||||
 | 
					          <Select.Option key={field.name} value={field.name}>{t(field.uiSchema.title)}</Select.Option>
 | 
				
			||||||
 | 
					        ))}
 | 
				
			||||||
 | 
					      </Select>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -12,6 +12,7 @@ import { AddButton, useFlowContext } from '../WorkflowCanvas';
 | 
				
			|||||||
import { nodeClass, nodeCardClass, nodeHeaderClass, nodeTitleClass, nodeBlockClass } from '../style';
 | 
					import { nodeClass, nodeCardClass, nodeHeaderClass, nodeTitleClass, nodeBlockClass } from '../style';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import query from './query';
 | 
					import query from './query';
 | 
				
			||||||
 | 
					import create from './create';
 | 
				
			||||||
import condition from './condition';
 | 
					import condition from './condition';
 | 
				
			||||||
import parallel from './parallel';
 | 
					import parallel from './parallel';
 | 
				
			||||||
import calculation from './calculation';
 | 
					import calculation from './calculation';
 | 
				
			||||||
@ -59,6 +60,7 @@ export interface Instruction {
 | 
				
			|||||||
export const instructions = new Registry<Instruction>();
 | 
					export const instructions = new Registry<Instruction>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
instructions.register('query', query);
 | 
					instructions.register('query', query);
 | 
				
			||||||
 | 
					instructions.register('create', create);
 | 
				
			||||||
instructions.register('condition', condition);
 | 
					instructions.register('condition', condition);
 | 
				
			||||||
instructions.register('parallel', parallel);
 | 
					instructions.register('parallel', parallel);
 | 
				
			||||||
instructions.register('calculation', calculation);
 | 
					instructions.register('calculation', calculation);
 | 
				
			||||||
@ -159,54 +161,58 @@ export function NodeDefaultView(props) {
 | 
				
			|||||||
              type: 'void',
 | 
					              type: 'void',
 | 
				
			||||||
              properties: {
 | 
					              properties: {
 | 
				
			||||||
                view: instruction.view,
 | 
					                view: instruction.view,
 | 
				
			||||||
                config: {
 | 
					                ...(Object.keys(instruction.fieldset).length
 | 
				
			||||||
                  type: 'void',
 | 
					                  ? {
 | 
				
			||||||
                  title: '配置节点',
 | 
					                    config: {
 | 
				
			||||||
                  'x-component': 'Action.Link',
 | 
					 | 
				
			||||||
                  'x-component-props': {
 | 
					 | 
				
			||||||
                    type: 'primary',
 | 
					 | 
				
			||||||
                  },
 | 
					 | 
				
			||||||
                  properties: {
 | 
					 | 
				
			||||||
                    drawer: {
 | 
					 | 
				
			||||||
                      type: 'void',
 | 
					                      type: 'void',
 | 
				
			||||||
                      title: '配置节点',
 | 
					                      title: '配置节点',
 | 
				
			||||||
                      'x-component': 'Action.Drawer',
 | 
					                      'x-component': 'Action.Link',
 | 
				
			||||||
                      'x-decorator': 'Form',
 | 
					                      'x-component-props': {
 | 
				
			||||||
                      'x-decorator-props': {
 | 
					                        type: 'primary',
 | 
				
			||||||
                        useValues(options) {
 | 
					 | 
				
			||||||
                          const node = useNodeContext();
 | 
					 | 
				
			||||||
                          return useRequest(() => {
 | 
					 | 
				
			||||||
                            return Promise.resolve({ data: node.config });
 | 
					 | 
				
			||||||
                          }, options);
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                      },
 | 
					                      },
 | 
				
			||||||
                      properties: {
 | 
					                      properties: {
 | 
				
			||||||
                        ...instruction.fieldset,
 | 
					                        drawer: {
 | 
				
			||||||
                        actions: {
 | 
					 | 
				
			||||||
                          type: 'void',
 | 
					                          type: 'void',
 | 
				
			||||||
                          'x-component': 'Action.Drawer.Footer',
 | 
					                          title: '配置节点',
 | 
				
			||||||
                          properties: {
 | 
					                          'x-component': 'Action.Drawer',
 | 
				
			||||||
                            cancel: {
 | 
					                          'x-decorator': 'Form',
 | 
				
			||||||
                              title: '{{t("Cancel")}}',
 | 
					                          'x-decorator-props': {
 | 
				
			||||||
                              'x-component': 'Action',
 | 
					                            useValues(options) {
 | 
				
			||||||
                              'x-component-props': {
 | 
					                              const node = useNodeContext();
 | 
				
			||||||
                                useAction: '{{ cm.useCancelAction }}',
 | 
					                              return useRequest(() => {
 | 
				
			||||||
                              },
 | 
					                                return Promise.resolve({ data: node.config });
 | 
				
			||||||
                            },
 | 
					                              }, options);
 | 
				
			||||||
                            submit: {
 | 
					                            }
 | 
				
			||||||
                              title: '{{t("Submit")}}',
 | 
					 | 
				
			||||||
                              'x-component': 'Action',
 | 
					 | 
				
			||||||
                              'x-component-props': {
 | 
					 | 
				
			||||||
                                type: 'primary',
 | 
					 | 
				
			||||||
                                useAction: useUpdateConfigAction,
 | 
					 | 
				
			||||||
                              },
 | 
					 | 
				
			||||||
                            },
 | 
					 | 
				
			||||||
                          },
 | 
					                          },
 | 
				
			||||||
                        } as ISchema
 | 
					                          properties: {
 | 
				
			||||||
 | 
					                            ...instruction.fieldset,
 | 
				
			||||||
 | 
					                            actions: {
 | 
				
			||||||
 | 
					                              type: 'void',
 | 
				
			||||||
 | 
					                              'x-component': 'Action.Drawer.Footer',
 | 
				
			||||||
 | 
					                              properties: {
 | 
				
			||||||
 | 
					                                cancel: {
 | 
				
			||||||
 | 
					                                  title: '{{t("Cancel")}}',
 | 
				
			||||||
 | 
					                                  'x-component': 'Action',
 | 
				
			||||||
 | 
					                                  'x-component-props': {
 | 
				
			||||||
 | 
					                                    useAction: '{{ cm.useCancelAction }}',
 | 
				
			||||||
 | 
					                                  },
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                                submit: {
 | 
				
			||||||
 | 
					                                  title: '{{t("Submit")}}',
 | 
				
			||||||
 | 
					                                  'x-component': 'Action',
 | 
				
			||||||
 | 
					                                  'x-component-props': {
 | 
				
			||||||
 | 
					                                    type: 'primary',
 | 
				
			||||||
 | 
					                                    useAction: useUpdateConfigAction,
 | 
				
			||||||
 | 
					                                  },
 | 
				
			||||||
 | 
					                                },
 | 
				
			||||||
 | 
					                              },
 | 
				
			||||||
 | 
					                            } as ISchema
 | 
				
			||||||
 | 
					                          }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                      }
 | 
					                      }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                : {})
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
          />
 | 
					          />
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ import { useFlowContext } from '../WorkflowCanvas';
 | 
				
			|||||||
import { Operand, parseStringValue, VariableTypes, VariableTypesContext, BaseTypeSet } from '../calculators';
 | 
					import { Operand, parseStringValue, VariableTypes, VariableTypesContext, BaseTypeSet } from '../calculators';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  title: '数据查询',
 | 
					  title: '查询',
 | 
				
			||||||
  type: 'query',
 | 
					  type: 'query',
 | 
				
			||||||
  group: 'model',
 | 
					  group: 'model',
 | 
				
			||||||
  fieldset: {
 | 
					  fieldset: {
 | 
				
			||||||
 | 
				
			|||||||
@ -72,11 +72,15 @@ export default {
 | 
				
			|||||||
    const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] };
 | 
					    const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <Select value={options?.path.replace(/^data\./, '')} placeholder="选择字段" onChange={path => {
 | 
					      <Select
 | 
				
			||||||
        onChange({ type, options: { ...options, path: `data.${path}` } });
 | 
					        placeholder="选择字段"
 | 
				
			||||||
      }}>
 | 
					        value={options?.path?.replace(/^data\./, '')}
 | 
				
			||||||
 | 
					        onChange={(path) => {
 | 
				
			||||||
 | 
					          onChange({ type, options: { ...options, path: `data.${path}` } });
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
        {collection.fields
 | 
					        {collection.fields
 | 
				
			||||||
          .filter(field => BaseTypeSet.has(field.uiSchema.type))
 | 
					          .filter(field => BaseTypeSet.has(field?.uiSchema?.type))
 | 
				
			||||||
          .map(field => (
 | 
					          .map(field => (
 | 
				
			||||||
          <Select.Option key={field.name} value={field.name}>{t(field.uiSchema.title)}</Select.Option>
 | 
					          <Select.Option key={field.name} value={field.name}>{t(field.uiSchema.title)}</Select.Option>
 | 
				
			||||||
        ))}
 | 
					        ))}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user