From 10dedd87e62e7ffa4d36a47ff680708331a590cd Mon Sep 17 00:00:00 2001 From: Junyi Date: Sat, 16 Apr 2022 23:49:50 +0800 Subject: [PATCH] feat(plugin-workflow): add all crud nodes for workflow (#293) --- .../src/collection-manager/hooks/index.ts | 2 +- .../hooks/useCollectionDataSource.ts | 17 +++ .../schema-component/antd/filter/Filter.tsx | 4 +- .../antd/record-picker/InputRecordPicker.tsx | 2 +- packages/client/src/workflow/calculators.tsx | 101 +++++++++++++++ packages/client/src/workflow/nodes/create.tsx | 115 ++---------------- .../client/src/workflow/nodes/destroy.tsx | 31 +++++ packages/client/src/workflow/nodes/index.tsx | 51 ++++---- packages/client/src/workflow/nodes/query.tsx | 95 ++------------- packages/client/src/workflow/nodes/update.tsx | 32 +++++ .../client/src/workflow/schemas/collection.ts | 64 ++++++++++ .../client/src/workflow/triggers/model.tsx | 49 ++------ 12 files changed, 309 insertions(+), 254 deletions(-) create mode 100644 packages/client/src/collection-manager/hooks/useCollectionDataSource.ts create mode 100644 packages/client/src/workflow/nodes/destroy.tsx create mode 100644 packages/client/src/workflow/nodes/update.tsx create mode 100644 packages/client/src/workflow/schemas/collection.ts diff --git a/packages/client/src/collection-manager/hooks/index.ts b/packages/client/src/collection-manager/hooks/index.ts index a02c5a955..6acbb07c3 100644 --- a/packages/client/src/collection-manager/hooks/index.ts +++ b/packages/client/src/collection-manager/hooks/index.ts @@ -1,4 +1,4 @@ export * from './useCollection'; export * from './useCollectionField'; export * from './useCollectionManager'; - +export * from './useCollectionDataSource'; diff --git a/packages/client/src/collection-manager/hooks/useCollectionDataSource.ts b/packages/client/src/collection-manager/hooks/useCollectionDataSource.ts new file mode 100644 index 000000000..7fad2cfb7 --- /dev/null +++ b/packages/client/src/collection-manager/hooks/useCollectionDataSource.ts @@ -0,0 +1,17 @@ +import { action } from '@formily/reactive'; + +import { useCollectionManager } from "."; +import { useCompile } from "../../schema-component"; + +export function useCollectionDataSource() { + return (field: any) => { + const compile = useCompile(); + const { collections = [] } = useCollectionManager(); + action.bound((data: any) => { + field.dataSource = data.map(item => ({ + label: compile(item.title), + value: item.name + })); + })(collections); + } +} diff --git a/packages/client/src/schema-component/antd/filter/Filter.tsx b/packages/client/src/schema-component/antd/filter/Filter.tsx index 17f629e90..62fff2267 100644 --- a/packages/client/src/schema-component/antd/filter/Filter.tsx +++ b/packages/client/src/schema-component/antd/filter/Filter.tsx @@ -16,7 +16,7 @@ const useDef = (options) => { export const Filter: any = observer((props: any) => { const { useDataSource = useDef } = props; - const { options, dynamicComponent } = useProps(props); + const { options, dynamicComponent, className } = useProps(props); const field = useField(); const fieldSchema = useFieldSchema(); useDataSource({ @@ -25,7 +25,7 @@ export const Filter: any = observer((props: any) => { }, }); return ( -
+
diff --git a/packages/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx b/packages/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx index 57f9b8b80..4f01cf241 100644 --- a/packages/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx +++ b/packages/client/src/schema-component/antd/record-picker/InputRecordPicker.tsx @@ -97,7 +97,7 @@ export const InputRecordPicker: React.FC = (props) => { open={false} /> - + diff --git a/packages/client/src/workflow/calculators.tsx b/packages/client/src/workflow/calculators.tsx index 7b459d6ec..348cd8cd4 100644 --- a/packages/client/src/workflow/calculators.tsx +++ b/packages/client/src/workflow/calculators.tsx @@ -1,10 +1,13 @@ import React from "react"; +import { observer } from "@formily/react"; +import { FormItem } from "@formily/antd"; import { Cascader, DatePicker, Input, InputNumber, Select } from "antd"; import { css } from "@emotion/css"; import { instructions, useNodeContext } from "./nodes"; import { useFlowContext } from "./WorkflowCanvas"; import { triggers } from "./triggers"; +import { SchemaComponent, useCompile } from ".."; function NullRender() { return null; @@ -325,3 +328,101 @@ export function Calculation({ calculator, operands = [], onChange }) { ); } + +export function VariableComponent({ value, onChange, renderSchemaComponent }) { + const VTypes = { ...VariableTypes, + constant: { + title: '常量', + value: 'constant', + options: undefined + } + }; + + const operand = typeof value === 'string' + ? parseStringValue(value, VTypes) + : { type: 'constant', value }; + + return ( + + { + if (next.type !== operand.type && next.type === 'constant') { + onChange(null); + } else { + const { stringify } = VTypes[next.type]; + onChange(stringify(next)); + } + }} + > + {operand.type === 'constant' ? renderSchemaComponent() : null} + + + ); +} + +// NOTE: observer for watching useProps +export const CollectionFieldset = observer(({ value, onChange, useProps }: any) => { + const { fields } = useProps(); + const compile = useCompile(); + + const VTypes = { ...VariableTypes, + constant: { + title: '常量', + value: 'constant', + options: undefined + } + }; + + return ( +
+ {fields.length + ? fields.map(field => { + const operand = typeof value[field.name] === 'string' + ? parseStringValue(value[field.name], VTypes) + : { type: 'constant', value: value[field.name] }; + + return ( + + + { + 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' + ? + : null + } + + + + ); + }) + :

请先选择数据表

+ } +
+ ); +}); diff --git a/packages/client/src/workflow/nodes/create.tsx b/packages/client/src/workflow/nodes/create.tsx index 1b609266e..4dd961b45 100644 --- a/packages/client/src/workflow/nodes/create.tsx +++ b/packages/client/src/workflow/nodes/create.tsx @@ -1,30 +1,17 @@ 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 { useCollectionDataSource, useCollectionManager, useCompile } from '../..'; import { useFlowContext } from '../WorkflowCanvas'; -import { Operand, parseStringValue, VariableTypes, VariableTypesContext, BaseTypeSet } from '../calculators'; -import { FormItem } from '@formily/antd'; +import { collection, values } from '../schemas/collection'; +import { BaseTypeSet, CollectionFieldset } from '../calculators'; export default { - title: '新增', + title: '新增数据', type: 'create', group: 'model', fieldset: { - collection: { - type: 'string', - title: '数据表', - name: 'collection', - required: true, - 'x-reactions': ['{{useCollectionDataSource()}}'], - 'x-decorator': 'FormItem', - 'x-component': 'Select', - }, + collection, // multiple: { // type: 'boolean', // title: '多条数据', @@ -38,21 +25,10 @@ export default { params: { type: 'object', name: 'params', - title: '数据', + 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) }; - } - } - } + values } } }, @@ -60,82 +36,13 @@ export default { }, 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); - } - } + useCollectionDataSource }, 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 ( -
- {fields.map(field => { - const operand = typeof value[field.name] === 'string' - ? parseStringValue(value[field.name], VTypes) - : { type: 'constant', value: value[field.name] }; - - return ( - - - { - 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' - ? - : null - } - - - - ); - })} -
- ); - }) + CollectionFieldset }, getter({ type, options, onChange }) { + const compile = useCompile(); const { collections = [] } = useCollectionManager(); const { nodes } = useFlowContext(); const { config } = nodes.find(n => n.id == options.nodeId); @@ -148,7 +55,7 @@ export default { {collection.fields .filter(field => BaseTypeSet.has(field.uiSchema.type)) .map(field => ( - {t(field.uiSchema.title)} + {compile(field.uiSchema.title)} ))} ); diff --git a/packages/client/src/workflow/nodes/destroy.tsx b/packages/client/src/workflow/nodes/destroy.tsx new file mode 100644 index 000000000..5ad6ae4b6 --- /dev/null +++ b/packages/client/src/workflow/nodes/destroy.tsx @@ -0,0 +1,31 @@ +import { useCollectionDataSource } from '../..'; + +import { VariableComponent } from '../calculators'; +import { collection, filter } from '../schemas/collection'; + +export default { + title: '删除数据', + type: 'destroy', + group: 'model', + fieldset: { + collection, + params: { + type: 'object', + name: 'params', + title: '', + 'x-decorator': 'FormItem', + properties: { + filter + } + } + }, + view: { + + }, + scope: { + useCollectionDataSource + }, + components: { + VariableComponent + } +}; diff --git a/packages/client/src/workflow/nodes/index.tsx b/packages/client/src/workflow/nodes/index.tsx index 96a65715b..eed834c00 100644 --- a/packages/client/src/workflow/nodes/index.tsx +++ b/packages/client/src/workflow/nodes/index.tsx @@ -13,11 +13,38 @@ import { nodeClass, nodeCardClass, nodeHeaderClass, nodeTitleClass, nodeBlockCla import query from './query'; import create from './create'; +import update from './update'; +import destroy from './destroy'; import condition from './condition'; import parallel from './parallel'; import calculation from './calculation'; + +export interface Instruction { + title: string; + type: string; + group: string; + options?: { label: string; value: any; key: string }[]; + fieldset: { [key: string]: ISchema }; + view?: ISchema; + scope?: { [key: string]: any }; + components?: { [key: string]: any }; + render?(props): React.ReactElement; + endding?: boolean; + getter?(node: any): React.ReactElement; +}; + +export const instructions = new Registry(); + +instructions.register('query', query); +instructions.register('create', create); +instructions.register('update', update); +instructions.register('destroy', destroy); +instructions.register('condition', condition); +instructions.register('parallel', parallel); +instructions.register('calculation', calculation); + function useUpdateConfigAction() { const form = useForm(); const api = useAPIClient(); @@ -41,30 +68,6 @@ function useUpdateConfigAction() { }; }; - - -export interface Instruction { - title: string; - type: string; - group: string; - options?: { label: string; value: any; key: string }[]; - fieldset: { [key: string]: ISchema }; - view?: ISchema; - scope?: { [key: string]: any }; - components?: { [key: string]: any }; - render?(props): React.ReactElement; - endding?: boolean; - getter?(node: any): React.ReactElement; -}; - -export const instructions = new Registry(); - -instructions.register('query', query); -instructions.register('create', create); -instructions.register('condition', condition); -instructions.register('parallel', parallel); -instructions.register('calculation', calculation); - const NodeContext = React.createContext(null); export function useNodeContext() { diff --git a/packages/client/src/workflow/nodes/query.tsx b/packages/client/src/workflow/nodes/query.tsx index 027cdd9dd..5e5f12b8d 100644 --- a/packages/client/src/workflow/nodes/query.tsx +++ b/packages/client/src/workflow/nodes/query.tsx @@ -1,29 +1,18 @@ import React from 'react'; -import { useForm } from '@formily/react'; -import { action } from '@formily/reactive'; import { Select } from 'antd'; -import { t } from 'i18next'; -import { css } from '@emotion/css'; -import { useCollectionManager } from '../..'; -import { useCollectionFilterOptions } from '../../collection-manager/action-hooks'; +import { useCollectionDataSource, useCollectionManager, useCompile } from '../..'; + import { useFlowContext } from '../WorkflowCanvas'; -import { Operand, parseStringValue, VariableTypes, VariableTypesContext, BaseTypeSet } from '../calculators'; +import { BaseTypeSet, VariableComponent } from '../calculators'; +import { collection, filter } from '../schemas/collection'; export default { - title: '查询', + title: '查询数据', type: 'query', group: 'model', fieldset: { - collection: { - type: 'string', - title: '数据表', - name: 'collection', - required: true, - 'x-reactions': ['{{useCollectionDataSource()}}'], - 'x-decorator': 'FormItem', - 'x-component': 'Select', - }, + collection, multiple: { type: 'boolean', title: '多条数据', @@ -37,31 +26,10 @@ export default { params: { type: 'object', name: 'params', - title: '查询参数', + title: '', 'x-decorator': 'FormItem', properties: { - filter: { - type: 'object', - title: '筛选条件', - name: 'filter', - 'x-decorator': 'div', - 'x-decorator-props': { - className: css` - .ant-select{ - width: auto; - } - ` - }, - 'x-component': 'Filter', - 'x-component-props': { - useProps() { - const { values } = useForm(); - const options = useCollectionFilterOptions(values.collection); - return { options }; - }, - dynamicComponent: 'VariableComponent' - } - } + filter } } }, @@ -69,52 +37,13 @@ export default { }, 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); - } - } + useCollectionDataSource }, components: { - VariableComponent({ value, onChange, renderSchemaComponent }) { - const VTypes = { ...VariableTypes, - constant: { - title: '常量', - value: 'constant', - options: undefined - } - }; - - const operand = typeof value === 'string' - ? parseStringValue(value, VTypes) - : { type: 'constant', value }; - - return ( - - { - if (next.type !== operand.type && next.type === 'constant') { - onChange(null); - } else { - const { stringify } = VTypes[next.type]; - onChange(stringify(next)); - } - }} - > - {operand.type === 'constant' ? renderSchemaComponent() : null} - - - ); - } + VariableComponent }, getter({ type, options, onChange }) { + const compile = useCompile(); const { collections = [] } = useCollectionManager(); const { nodes } = useFlowContext(); const { config } = nodes.find(n => n.id == options.nodeId); @@ -127,7 +56,7 @@ export default { {collection.fields .filter(field => BaseTypeSet.has(field.uiSchema.type)) .map(field => ( - {t(field.uiSchema.title)} + {compile(field.uiSchema.title)} ))} ); diff --git a/packages/client/src/workflow/nodes/update.tsx b/packages/client/src/workflow/nodes/update.tsx new file mode 100644 index 000000000..95f1fceaf --- /dev/null +++ b/packages/client/src/workflow/nodes/update.tsx @@ -0,0 +1,32 @@ +import { useCollectionDataSource } from '../..'; +import { CollectionFieldset, VariableComponent } from '../calculators'; +import { collection, filter, values } from '../schemas/collection'; + +export default { + title: '更新数据', + type: 'update', + group: 'model', + fieldset: { + collection, + params: { + type: 'object', + name: 'params', + title: '', + 'x-decorator': 'FormItem', + properties: { + filter, + values + } + } + }, + view: { + + }, + scope: { + useCollectionDataSource + }, + components: { + VariableComponent, + CollectionFieldset + } +}; diff --git a/packages/client/src/workflow/schemas/collection.ts b/packages/client/src/workflow/schemas/collection.ts new file mode 100644 index 000000000..de4602b42 --- /dev/null +++ b/packages/client/src/workflow/schemas/collection.ts @@ -0,0 +1,64 @@ +import { css } from "@emotion/css"; +import { useForm } from "@formily/react"; +import { useCollectionFilterOptions } from "../../collection-manager/action-hooks"; + +export const collection = { + type: 'string', + title: '数据表', + name: 'collection', + required: true, + 'x-reactions': ['{{useCollectionDataSource()}}'], + 'x-decorator': 'FormItem', + 'x-component': 'Select', +}; + +export const values = { + type: 'object', + title: '数据内容', + name: 'values', + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelAlign: 'left', + className: css` + flex-direction: column; + ` + }, + 'x-component': 'CollectionFieldset', + 'x-component-props': { + useProps() { + const { values: form } = useForm(); + const fields = useCollectionFilterOptions(form.collection); + return { fields }; + } + } +}; + +export const filter = { + type: 'object', + title: '筛选条件', + name: 'filter', + 'x-decorator': 'FormItem', + 'x-decorator-props': { + labelAlign: 'left', + className: css` + flex-direction: column; + ` + }, + 'x-component': 'Filter', + 'x-component-props': { + useProps() { + const { values } = useForm(); + const options = useCollectionFilterOptions(values.collection); + return { + options, + className: css` + position: relative; + width: 100%; + padding: .5em 1em; + border: 1px dashed #ddd; + ` + }; + }, + dynamicComponent: 'VariableComponent' + } +}; diff --git a/packages/client/src/workflow/triggers/model.tsx b/packages/client/src/workflow/triggers/model.tsx index dae142814..d02da3aec 100644 --- a/packages/client/src/workflow/triggers/model.tsx +++ b/packages/client/src/workflow/triggers/model.tsx @@ -1,27 +1,18 @@ import React from 'react'; -import { action } from '@formily/reactive'; -import { t } from 'i18next'; import { Select } from 'antd'; -import { useCollectionManager } from '../../collection-manager'; +import { useCollectionDataSource, useCollectionManager } from '../../collection-manager'; +import { useCompile } from '../../schema-component'; + import { useFlowContext } from '../WorkflowCanvas'; import { BaseTypeSet } from '../calculators'; -import { useForm } from '@formily/react'; -import { useCollectionFilterOptions } from '../../collection-manager/action-hooks'; +import { collection, filter } from '../schemas/collection'; export default { title: '数据表事件', type: 'model', fieldset: { - collection: { - type: 'string', - title: '数据表', - name: 'collection', - required: true, - 'x-reactions': ['{{useAsyncDataSource()}}'], - 'x-decorator': 'FormItem', - 'x-component': 'Select', - }, + collection, mode: { type: 'number', title: '触发时机', @@ -38,35 +29,15 @@ export default { } }, filter: { - type: 'object', - title: '满足条件', - description: 'not supported by server yet', - name: 'filter', - 'x-decorator': 'FormItem', - 'x-component': 'Filter', - 'x-component-props': { - useProps() { - const { values } = useForm(); - const options = useCollectionFilterOptions(values.collection); - return { options }; - } - } + ...filter, + title: '满足条件' } }, scope: { - useAsyncDataSource() { - return (field: any) => { - const { collections = [] } = useCollectionManager(); - action.bound((data: any) => { - field.dataSource = data.map(item => ({ - label: t(item.title), - value: item.name - })); - })(collections); - } - } + useCollectionDataSource }, getter({ type, options, onChange }) { + const compile = useCompile(); const { collections = [] } = useCollectionManager(); const { workflow } = useFlowContext(); const collection = collections.find(item => item.name === workflow.config.collection) ?? { fields: [] }; @@ -82,7 +53,7 @@ export default { {collection.fields .filter(field => BaseTypeSet.has(field?.uiSchema?.type)) .map(field => ( - {t(field.uiSchema.title)} + {compile(field.uiSchema.title)} ))} );