refactor(plugin-workflow): change canvas card and adjust styles (#1529)
* refactor(plugin-workflow): optimize workflow canvas * feat(plugin-workflow): allow click on node card to open config and adjust styles * fix(plugin-workflow): fix collection trigger linkages
This commit is contained in:
parent
4d44e31b31
commit
37998d03ad
@ -123,6 +123,8 @@ ReadPretty.JSON = (props) => {
|
|||||||
<pre
|
<pre
|
||||||
className={cx(prefixCls, props.className, css`
|
className={cx(prefixCls, props.className, css`
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 90%;
|
||||||
`)}
|
`)}
|
||||||
style={props.style}
|
style={props.style}
|
||||||
>
|
>
|
||||||
|
@ -20,7 +20,7 @@ interface AddButtonProps {
|
|||||||
export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
|
export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { workflow, onNodeAdded } = useFlowContext() ?? {};
|
const { workflow, refresh } = useFlowContext() ?? {};
|
||||||
if (!workflow) {
|
if (!workflow) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -30,8 +30,9 @@ export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
|
|||||||
const type = keyPath.pop();
|
const type = keyPath.pop();
|
||||||
const config = {};
|
const config = {};
|
||||||
const [optionKey] = keyPath;
|
const [optionKey] = keyPath;
|
||||||
|
const instruction = instructions.get(type);
|
||||||
if (optionKey) {
|
if (optionKey) {
|
||||||
const { value } = instructions.get(type)?.options?.find(item => item.key === optionKey) ?? {};
|
const { value } = instruction.options?.find(item => item.key === optionKey) ?? {};
|
||||||
Object.assign(config, value);
|
Object.assign(config, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,47 +41,46 @@ export function AddButton({ upstream, branchIndex = null }: AddButtonProps) {
|
|||||||
type,
|
type,
|
||||||
upstreamId: upstream?.id ?? null,
|
upstreamId: upstream?.id ?? null,
|
||||||
branchIndex,
|
branchIndex,
|
||||||
|
title: compile(instruction.title),
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onNodeAdded(node);
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
const groups = [
|
|
||||||
{ value: 'control', name: `{{t("Control", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
{ value: 'collection', name: `{{t("Collection operations", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
{ value: 'manual', name: `{{t("Manual", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
{ value: 'extended', name: `{{t("Extended types", { ns: "${NAMESPACE}" })}}` },
|
|
||||||
];
|
|
||||||
const instructionList = (Array.from(instructions.getValues()) as Instruction[]);
|
const instructionList = (Array.from(instructions.getValues()) as Instruction[]);
|
||||||
|
|
||||||
|
const groups = [
|
||||||
|
{ key: 'control', label: `{{t("Control", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
{ key: 'collection', label: `{{t("Collection operations", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
{ key: 'manual', label: `{{t("Manual", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
{ key: 'extended', label: `{{t("Extended types", { ns: "${NAMESPACE}" })}}` },
|
||||||
|
]
|
||||||
|
.filter(group => instructionList.filter(item => item.group === group.key).length)
|
||||||
|
.map(group => {
|
||||||
|
const groupInstructions = instructionList.filter(item => item.group === group.key);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...group,
|
||||||
|
type: 'group',
|
||||||
|
children: groupInstructions.map(item => ({
|
||||||
|
key: item.type,
|
||||||
|
label: item.title,
|
||||||
|
type: item.options ? 'subMenu' : null,
|
||||||
|
children: item.options ? item.options.map(option => ({
|
||||||
|
key: option.key,
|
||||||
|
label: option.label,
|
||||||
|
})) : null
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(addButtonClass)}>
|
<div className={cx(addButtonClass)}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
trigger={['click']}
|
trigger={['click']}
|
||||||
overlay={
|
overlay={<Menu onClick={ev => onCreate(ev)} items={compile(groups)} />}
|
||||||
<Menu onClick={ev => onCreate(ev)}>
|
|
||||||
{groups.map(group => {
|
|
||||||
const groupInstructions = instructionList.filter(item => item.group === group.value);
|
|
||||||
return groupInstructions.length ? (
|
|
||||||
<Menu.ItemGroup key={group.value} title={compile(group.name)}>
|
|
||||||
{groupInstructions.map(item => item.options
|
|
||||||
? (
|
|
||||||
<Menu.SubMenu key={item.type} title={compile(item.title)}>
|
|
||||||
{item.options.map(option => (
|
|
||||||
<Menu.Item key={option.key}>{compile(option.label)}</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu.SubMenu>
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<Menu.Item key={item.type}>{compile(item.title)}</Menu.Item>
|
|
||||||
))}
|
|
||||||
</Menu.ItemGroup>
|
|
||||||
) : null;
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
disabled={workflow.executed}
|
disabled={workflow.executed}
|
||||||
>
|
>
|
||||||
<Button shape="circle" icon={<PlusOutlined />} />
|
<Button shape="circle" icon={<PlusOutlined />} />
|
||||||
|
@ -117,8 +117,7 @@ export function WorkflowCanvas() {
|
|||||||
<FlowContext.Provider value={{
|
<FlowContext.Provider value={{
|
||||||
workflow,
|
workflow,
|
||||||
nodes,
|
nodes,
|
||||||
onNodeAdded: refresh,
|
refresh,
|
||||||
onNodeRemoved: refresh
|
|
||||||
}}>
|
}}>
|
||||||
<div className="workflow-toolbar">
|
<div className="workflow-toolbar">
|
||||||
<header>
|
<header>
|
||||||
|
@ -33,8 +33,8 @@ export default observer(({ value, disabled, onChange }: any) => {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const { getCollection, getCollectionFields } = useCollectionManager();
|
const { getCollection, getCollectionFields } = useCollectionManager();
|
||||||
const { values: data } = useForm();
|
const { values: config } = useForm();
|
||||||
const collectionName = data?.config?.collection;
|
const collectionName = config?.collection;
|
||||||
const fields = getCollectionFields(collectionName)
|
const fields = getCollectionFields(collectionName)
|
||||||
.filter(field => (
|
.filter(field => (
|
||||||
!field.hidden
|
!field.hidden
|
||||||
|
@ -17,10 +17,9 @@ export default {
|
|||||||
type: 'calculation',
|
type: 'calculation',
|
||||||
group: 'control',
|
group: 'control',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.engine': {
|
engine: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: `{{t("Calculation engine", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Calculation engine", { ns: "${NAMESPACE}" })}}`,
|
||||||
name: 'config.engine',
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'RadioWithTooltip',
|
'x-component': 'RadioWithTooltip',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
@ -29,10 +28,9 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
default: 'math.js'
|
default: 'math.js'
|
||||||
},
|
},
|
||||||
'config.expression': {
|
expression: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: `{{t("Calculation expression", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Calculation expression", { ns: "${NAMESPACE}" })}}`,
|
||||||
name: 'config.expression',
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Variable.TextArea',
|
'x-component': 'Variable.TextArea',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
@ -40,7 +38,7 @@ export default {
|
|||||||
},
|
},
|
||||||
['x-validator'](value, rules, { form }) {
|
['x-validator'](value, rules, { form }) {
|
||||||
const { values } = form;
|
const { values } = form;
|
||||||
const { evaluate } = evaluators.get(values.config.engine) as Evaluator;
|
const { evaluate } = evaluators.get(values.engine) as Evaluator;
|
||||||
const exp = value.trim().replace(/{{([^{}]+)}}/g, '1');
|
const exp = value.trim().replace(/{{([^{}]+)}}/g, '1');
|
||||||
try {
|
try {
|
||||||
evaluate(exp);
|
evaluate(exp);
|
||||||
@ -50,7 +48,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'x-reactions': {
|
'x-reactions': {
|
||||||
dependencies: ['config.engine'],
|
dependencies: ['engine'],
|
||||||
fulfill: {
|
fulfill: {
|
||||||
schema: {
|
schema: {
|
||||||
description: '{{renderReference($deps[0])}}',
|
description: '{{renderReference($deps[0])}}',
|
||||||
|
@ -304,9 +304,8 @@ export default {
|
|||||||
type: 'condition',
|
type: 'condition',
|
||||||
group: 'control',
|
group: 'control',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.rejectOnFalse': {
|
rejectOnFalse: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
name: 'config.rejectOnFalse',
|
|
||||||
title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Radio.Group',
|
'x-component': 'Radio.Group',
|
||||||
@ -324,10 +323,9 @@ export default {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'config.engine': {
|
engine: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: `{{t("Calculation engine", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Calculation engine", { ns: "${NAMESPACE}" })}}`,
|
||||||
name: 'config.engine',
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'RadioWithTooltip',
|
'x-component': 'RadioWithTooltip',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
@ -339,14 +337,13 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
default: 'basic',
|
default: 'basic',
|
||||||
},
|
},
|
||||||
'config.calculation': {
|
calculation: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'config.calculation',
|
|
||||||
title: `{{t("Condition", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Condition", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'CalculationConfig',
|
'x-component': 'CalculationConfig',
|
||||||
'x-reactions': {
|
'x-reactions': {
|
||||||
dependencies: ['config.engine'],
|
dependencies: ['engine'],
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{$deps[0] === "basic"}}'
|
visible: '{{$deps[0] === "basic"}}'
|
||||||
@ -355,10 +352,9 @@ export default {
|
|||||||
},
|
},
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
'config.expression': {
|
expression: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: `{{t("Condition expression", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Condition expression", { ns: "${NAMESPACE}" })}}`,
|
||||||
name: 'config.expression',
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Variable.TextArea',
|
'x-component': 'Variable.TextArea',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
@ -366,7 +362,7 @@ export default {
|
|||||||
},
|
},
|
||||||
['x-validator'](value, rules, { form }) {
|
['x-validator'](value, rules, { form }) {
|
||||||
const { values } = form;
|
const { values } = form;
|
||||||
const { evaluate } = evaluators.get(values.config.engine);
|
const { evaluate } = evaluators.get(values.engine);
|
||||||
const exp = value.trim().replace(/{{([^{}]+)}}/g, '1');
|
const exp = value.trim().replace(/{{([^{}]+)}}/g, '1');
|
||||||
try {
|
try {
|
||||||
evaluate(exp);
|
evaluate(exp);
|
||||||
@ -376,7 +372,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'x-reactions': {
|
'x-reactions': {
|
||||||
dependencies: ['config.engine'],
|
dependencies: ['engine'],
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{$deps[0] !== "basic"}}'
|
visible: '{{$deps[0] !== "basic"}}'
|
||||||
|
@ -14,10 +14,7 @@ export default {
|
|||||||
type: 'create',
|
type: 'create',
|
||||||
group: 'collection',
|
group: 'collection',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.collection': {
|
collection,
|
||||||
...collection,
|
|
||||||
name: 'config.collection'
|
|
||||||
},
|
|
||||||
// multiple: {
|
// multiple: {
|
||||||
// type: 'boolean',
|
// type: 'boolean',
|
||||||
// title: '多条数据',
|
// title: '多条数据',
|
||||||
@ -28,7 +25,7 @@ export default {
|
|||||||
// disabled: true
|
// disabled: true
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
'config.params.values': values
|
'params.values': values
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
|
|
||||||
|
@ -7,18 +7,16 @@ export default {
|
|||||||
type: 'delay',
|
type: 'delay',
|
||||||
group: 'control',
|
group: 'control',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.duration': {
|
duration: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
name: 'config.duration',
|
|
||||||
title: `{{t("Duration", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Duration", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Duration',
|
'x-component': 'Duration',
|
||||||
default: 60000,
|
default: 60000,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
'config.endStatus': {
|
endStatus: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
name: 'config.endStatus',
|
|
||||||
title: `{{t("End Status", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("End Status", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
|
@ -8,10 +8,9 @@ export default {
|
|||||||
type: 'destroy',
|
type: 'destroy',
|
||||||
group: 'collection',
|
group: 'collection',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.collection': collection,
|
collection,
|
||||||
'config.params': {
|
params: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
name: 'config.params',
|
|
||||||
title: '',
|
title: '',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useState, useContext } from 'react';
|
||||||
import {
|
import {
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { ISchema, useForm } from '@formily/react';
|
import { ISchema, useForm } from '@formily/react';
|
||||||
import { Button, message, Modal, Tag, Alert } from 'antd';
|
import { Button, message, Modal, Tag, Alert, Input } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import parse from 'json-templates';
|
import parse from 'json-templates';
|
||||||
|
|
||||||
import { Registry } from '@nocobase/utils/client';
|
import { Registry } from '@nocobase/utils/client';
|
||||||
import { SchemaComponent, SchemaInitializerItemOptions, useActionContext, useAPIClient, useCompile, useRequest, useResourceActionContext } from '@nocobase/client';
|
import { ActionContext, SchemaComponent, SchemaInitializerItemOptions, useActionContext, useAPIClient, useCompile, useRequest, useResourceActionContext } from '@nocobase/client';
|
||||||
|
|
||||||
import { nodeBlockClass, nodeCardClass, nodeClass, nodeHeaderClass, nodeMetaClass, nodeTitleClass } from '../style';
|
import { nodeBlockClass, nodeCardClass, nodeClass, nodeMetaClass, nodeTitleClass } from '../style';
|
||||||
import { AddButton } from '../AddButton';
|
import { AddButton } from '../AddButton';
|
||||||
import { useFlowContext } from '../FlowContext';
|
import { useFlowContext } from '../FlowContext';
|
||||||
|
|
||||||
@ -76,17 +76,11 @@ function useUpdateAction() {
|
|||||||
message.error(lang('Node in executed workflow cannot be modified'));
|
message.error(lang('Node in executed workflow cannot be modified'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: how to do validation separately for each field? especially disabled for dynamic fields?
|
await form.submit();
|
||||||
try {
|
|
||||||
await form.submit();
|
|
||||||
} catch (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await api.resource('flow_nodes', data.id).update?.({
|
await api.resource('flow_nodes', data.id).update?.({
|
||||||
filterByTk: data.id,
|
filterByTk: data.id,
|
||||||
values: {
|
values: {
|
||||||
title: form.values.title,
|
config: form.values
|
||||||
config: form.values.config
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ctx.setVisible(false);
|
ctx.setVisible(false);
|
||||||
@ -153,7 +147,7 @@ export function Node({ data }) {
|
|||||||
export function RemoveButton() {
|
export function RemoveButton() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { workflow, nodes, onNodeRemoved } = useFlowContext() ?? {};
|
const { workflow, nodes, refresh } = useFlowContext() ?? {};
|
||||||
const current = useNodeContext();
|
const current = useNodeContext();
|
||||||
if (!workflow) {
|
if (!workflow) {
|
||||||
return null;
|
return null;
|
||||||
@ -162,10 +156,10 @@ export function RemoveButton() {
|
|||||||
|
|
||||||
async function onRemove() {
|
async function onRemove() {
|
||||||
async function onOk() {
|
async function onOk() {
|
||||||
const { data: { data: node } } = await resource.destroy?.({
|
await resource.destroy?.({
|
||||||
filterByTk: current.id
|
filterByTk: current.id
|
||||||
});
|
});
|
||||||
onNodeRemoved(node);
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
const usingNodes = nodes.filter(node => {
|
const usingNodes = nodes.filter(node => {
|
||||||
@ -225,6 +219,7 @@ export function JobButton() {
|
|||||||
className={cx('workflow-node-job-button', css`
|
className={cx('workflow-node-job-button', css`
|
||||||
border: 2px solid #d9d9d9;
|
border: 2px solid #d9d9d9;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
cursor: not-allowed;
|
||||||
`)}
|
`)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -312,131 +307,161 @@ export function JobButton() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function NodeDefaultView(props) {
|
export function NodeDefaultView(props) {
|
||||||
const compile = useCompile();
|
|
||||||
const { workflow } = useFlowContext() ?? {};
|
|
||||||
if (!workflow) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data, children } = props;
|
const { data, children } = props;
|
||||||
|
const compile = useCompile();
|
||||||
|
const api = useAPIClient();
|
||||||
|
const { workflow, refresh } = useFlowContext() ?? {};
|
||||||
|
|
||||||
const instruction = instructions.get(data.type);
|
const instruction = instructions.get(data.type);
|
||||||
const detailText = workflow.executed ? '{{t("View")}}' : '{{t("Configure")}}';
|
const detailText = workflow.executed ? '{{t("View")}}' : '{{t("Configure")}}';
|
||||||
|
const typeTitle = compile(instruction.title);
|
||||||
|
|
||||||
|
const [editingTitle, setEditingTitle] = useState<string>(data.title ?? typeTitle);
|
||||||
|
const [editingConfig, setEditingConfig] = useState(false);
|
||||||
|
|
||||||
|
async function onChangeTitle(next) {
|
||||||
|
const title = next || typeTitle;
|
||||||
|
setEditingTitle(title);
|
||||||
|
if (title === data.title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await api.resource('flow_nodes').update?.({
|
||||||
|
filterByTk: data.id,
|
||||||
|
values: {
|
||||||
|
title
|
||||||
|
}
|
||||||
|
});
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpenDrawer(ev) {
|
||||||
|
if (ev.target === ev.currentTarget) {
|
||||||
|
setEditingConfig(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const whiteSet = new Set(['workflow-node-meta', 'workflow-node-config-button', 'ant-input-disabled']);
|
||||||
|
for (let el = ev.target; el && el !== ev.currentTarget; el = el.parentNode) {
|
||||||
|
if ((Array.from(el.classList) as string[]).some((name: string) => whiteSet.has(name))) {
|
||||||
|
setEditingConfig(true);
|
||||||
|
ev.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(nodeClass, `workflow-node-type-${data.type}`)}>
|
<div className={cx(nodeClass, `workflow-node-type-${data.type}`)}>
|
||||||
<div className={cx(nodeCardClass)}>
|
<div className={cx(nodeCardClass, { configuring: editingConfig })} onClick={onOpenDrawer}>
|
||||||
<div className={cx(nodeHeaderClass)}>
|
<div className={cx(nodeMetaClass, 'workflow-node-meta')}>
|
||||||
<div className={cx(nodeMetaClass)}>
|
<Tag>{typeTitle}</Tag>
|
||||||
<Tag>{compile(instruction.title)}</Tag>
|
<span className="workflow-node-id">{data.id}</span>
|
||||||
</div>
|
|
||||||
<h4 className={cx(nodeTitleClass)}>
|
|
||||||
<strong>{data.title}</strong>
|
|
||||||
<span className="workflow-node-id">#{data.id}</span>
|
|
||||||
</h4>
|
|
||||||
<RemoveButton />
|
|
||||||
<JobButton />
|
|
||||||
</div>
|
</div>
|
||||||
<SchemaComponent
|
<div>
|
||||||
scope={instruction.scope}
|
<Input.TextArea
|
||||||
components={instruction.components}
|
disabled={workflow.executed}
|
||||||
schema={{
|
value={editingTitle}
|
||||||
type: 'void',
|
onChange={(ev) => setEditingTitle(ev.target.value)}
|
||||||
properties: {
|
onBlur={(ev) => onChangeTitle(ev.target.value)}
|
||||||
...(instruction.view ? { view: instruction.view } : {}),
|
autoSize
|
||||||
config: {
|
/>
|
||||||
type: 'void',
|
</div>
|
||||||
title: detailText,
|
<RemoveButton />
|
||||||
'x-component': 'Action.Link',
|
<JobButton />
|
||||||
'x-component-props': {
|
<ActionContext.Provider value={{ visible: editingConfig, setVisible: setEditingConfig }}>
|
||||||
type: 'primary',
|
<SchemaComponent
|
||||||
|
scope={instruction.scope}
|
||||||
|
components={instruction.components}
|
||||||
|
schema={{
|
||||||
|
type: 'void',
|
||||||
|
properties: {
|
||||||
|
...(instruction.view ? { view: instruction.view } : {}),
|
||||||
|
config: {
|
||||||
|
type: 'void',
|
||||||
|
'x-content': detailText,
|
||||||
|
'x-component': Button,
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'link',
|
||||||
|
className: 'workflow-node-config-button'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
properties: {
|
[`${instruction.type}_${data.id}`]: {
|
||||||
[`${instruction.type}_${data.id}`]: {
|
type: 'void',
|
||||||
type: 'void',
|
title: instruction.title,
|
||||||
title: instruction.title,
|
'x-component': 'Action.Drawer',
|
||||||
'x-component': 'Action.Drawer',
|
'x-decorator': 'Form',
|
||||||
'x-decorator': 'Form',
|
'x-decorator-props': {
|
||||||
'x-decorator-props': {
|
disabled: workflow.executed,
|
||||||
disabled: workflow.executed,
|
useValues(options) {
|
||||||
useValues(options) {
|
const { config } = useNodeContext();
|
||||||
const d = useNodeContext();
|
return useRequest(() => {
|
||||||
return useRequest(() => {
|
return Promise.resolve({ data: config });
|
||||||
return Promise.resolve({ data: d });
|
}, options);
|
||||||
}, options);
|
}
|
||||||
}
|
},
|
||||||
},
|
properties: {
|
||||||
properties: {
|
...(workflow.executed ? {
|
||||||
...(workflow.executed ? {
|
alert: {
|
||||||
alert: {
|
|
||||||
'x-component': Alert,
|
|
||||||
'x-component-props': {
|
|
||||||
type: 'warning',
|
|
||||||
showIcon: true,
|
|
||||||
message: `{{t("Node in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
|
|
||||||
className: css`
|
|
||||||
width: 100%;
|
|
||||||
font-size: 85%;
|
|
||||||
margin-bottom: 2em;
|
|
||||||
`
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} : {}),
|
|
||||||
title: {
|
|
||||||
type: 'string',
|
|
||||||
name: 'title',
|
|
||||||
title: '{{t("Name")}}',
|
|
||||||
'x-decorator': 'FormItem',
|
|
||||||
'x-component': 'Input',
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'config',
|
'x-component': Alert,
|
||||||
'x-component': 'fieldset',
|
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
|
type: 'warning',
|
||||||
|
showIcon: true,
|
||||||
|
message: `{{t("Node in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
|
||||||
className: css`
|
className: css`
|
||||||
.ant-input,
|
width: 100%;
|
||||||
.ant-select,
|
font-size: 85%;
|
||||||
.ant-cascader-picker,
|
margin-bottom: 2em;
|
||||||
.ant-picker,
|
|
||||||
.ant-input-number,
|
|
||||||
.ant-input-affix-wrapper{
|
|
||||||
width: auto;
|
|
||||||
min-width: 6em;
|
|
||||||
}
|
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
properties: instruction.fieldset
|
}
|
||||||
|
} : {}),
|
||||||
|
fieldset: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'fieldset',
|
||||||
|
'x-component-props': {
|
||||||
|
className: css`
|
||||||
|
.ant-input,
|
||||||
|
.ant-select,
|
||||||
|
.ant-cascader-picker,
|
||||||
|
.ant-picker,
|
||||||
|
.ant-input-number,
|
||||||
|
.ant-input-affix-wrapper{
|
||||||
|
width: auto;
|
||||||
|
min-width: 6em;
|
||||||
|
}
|
||||||
|
`
|
||||||
},
|
},
|
||||||
actions: workflow.executed
|
properties: instruction.fieldset
|
||||||
? null
|
},
|
||||||
: {
|
actions: workflow.executed
|
||||||
type: 'void',
|
? null
|
||||||
'x-component': 'Action.Drawer.Footer',
|
: {
|
||||||
properties: {
|
type: 'void',
|
||||||
cancel: {
|
'x-component': 'Action.Drawer.Footer',
|
||||||
title: '{{t("Cancel")}}',
|
properties: {
|
||||||
'x-component': 'Action',
|
cancel: {
|
||||||
'x-component-props': {
|
title: '{{t("Cancel")}}',
|
||||||
useAction: '{{ cm.useCancelAction }}',
|
'x-component': 'Action',
|
||||||
},
|
'x-component-props': {
|
||||||
},
|
useAction: '{{ cm.useCancelAction }}',
|
||||||
submit: {
|
|
||||||
title: '{{t("Submit")}}',
|
|
||||||
'x-component': 'Action',
|
|
||||||
'x-component-props': {
|
|
||||||
type: 'primary',
|
|
||||||
useAction: useUpdateAction,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
submit: {
|
||||||
|
title: '{{t("Submit")}}',
|
||||||
|
'x-component': 'Action',
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'primary',
|
||||||
|
useAction: useUpdateAction,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} as ISchema
|
}
|
||||||
}
|
} as ISchema
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</ActionContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -34,9 +34,8 @@ export default {
|
|||||||
type: 'manual',
|
type: 'manual',
|
||||||
group: 'manual',
|
group: 'manual',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.assignees': {
|
assignees: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'config.assignees',
|
|
||||||
title: `{{t("Assignees", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Assignees", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'AssigneesSelect',
|
'x-component': 'AssigneesSelect',
|
||||||
@ -53,15 +52,14 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
'config.mode': {
|
mode: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
name: 'config.mode',
|
|
||||||
title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'ModeConfig',
|
'x-component': 'ModeConfig',
|
||||||
default: 1,
|
default: 1,
|
||||||
'x-reactions': {
|
'x-reactions': {
|
||||||
dependencies: ['config.assignees'],
|
dependencies: ['assignees'],
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{$deps[0].length > 1}}',
|
visible: '{{$deps[0].length > 1}}',
|
||||||
@ -69,7 +67,7 @@ export default {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'config.schema': {
|
schema: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
title: `{{t("User interface", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("User interface", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { css, cx } from "@emotion/css";
|
import { css, cx } from "@emotion/css";
|
||||||
import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Tooltip } from "antd";
|
import { Button, Tooltip } from "antd";
|
||||||
|
|
||||||
import { NodeDefaultView } from ".";
|
import { NodeDefaultView } from ".";
|
||||||
@ -17,9 +17,8 @@ export default {
|
|||||||
type: 'parallel',
|
type: 'parallel',
|
||||||
group: 'control',
|
group: 'control',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.mode': {
|
mode: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'config.mode',
|
|
||||||
title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'RadioWithTooltip',
|
'x-component': 'RadioWithTooltip',
|
||||||
@ -50,7 +49,7 @@ export default {
|
|||||||
},
|
},
|
||||||
render(data) {
|
render(data) {
|
||||||
const { id, config: { mode } } = data;
|
const { id, config: { mode } } = data;
|
||||||
const { nodes } = useFlowContext();
|
const { workflow, nodes } = useFlowContext();
|
||||||
const branches = nodes.reduce((result, node) => {
|
const branches = nodes.reduce((result, node) => {
|
||||||
if (node.upstreamId === id && node.branchIndex != null) {
|
if (node.upstreamId === id && node.branchIndex != null) {
|
||||||
return result.concat(node);
|
return result.concat(node);
|
||||||
@ -90,6 +89,7 @@ export default {
|
|||||||
shape="circle"
|
shape="circle"
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={() => setBranchCount(branchCount - 1)}
|
onClick={() => setBranchCount(branchCount - 1)}
|
||||||
|
disabled={workflow.executed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -104,7 +104,9 @@ export default {
|
|||||||
height: 2em;
|
height: 2em;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<Tooltip title={lang('Add branch')}>
|
<Tooltip title={lang('Add branch')} className={css`
|
||||||
|
visibility: ${workflow.executed ? 'hidden' : 'visible'}
|
||||||
|
`}>
|
||||||
<Button
|
<Button
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
className={css`
|
className={css`
|
||||||
@ -117,6 +119,7 @@ export default {
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
onClick={() => setBranchCount(branchCount + 1)}
|
onClick={() => setBranchCount(branchCount + 1)}
|
||||||
|
disabled={workflow.executed}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,7 @@ export default {
|
|||||||
type: 'query',
|
type: 'query',
|
||||||
group: 'collection',
|
group: 'collection',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.collection': collection,
|
collection,
|
||||||
// 'config.multiple': {
|
// 'config.multiple': {
|
||||||
// type: 'boolean',
|
// type: 'boolean',
|
||||||
// title: `{{t("Multiple records", { ns: "${NAMESPACE}" })}}`,
|
// title: `{{t("Multiple records", { ns: "${NAMESPACE}" })}}`,
|
||||||
@ -25,9 +25,8 @@ export default {
|
|||||||
// disabled: true
|
// disabled: true
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
'config.params': {
|
params: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
name: 'config.params',
|
|
||||||
title: '',
|
title: '',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -11,9 +11,8 @@ export default {
|
|||||||
type: 'request',
|
type: 'request',
|
||||||
group: 'extended',
|
group: 'extended',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.method': {
|
method: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'config.method',
|
|
||||||
required: true,
|
required: true,
|
||||||
title: `{{t("HTTP method", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("HTTP method", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
@ -31,9 +30,8 @@ export default {
|
|||||||
],
|
],
|
||||||
default: 'POST'
|
default: 'POST'
|
||||||
},
|
},
|
||||||
'config.url': {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'config.url',
|
|
||||||
required: true,
|
required: true,
|
||||||
title: `{{t("URL", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("URL", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
@ -52,9 +50,8 @@ export default {
|
|||||||
placeholder: 'https://www.nocobase.com',
|
placeholder: 'https://www.nocobase.com',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'config.headers': {
|
headers: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'config.headers',
|
|
||||||
'x-component': 'ArrayItems',
|
'x-component': 'ArrayItems',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
title: `{{t("Headers", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Headers", { ns: "${NAMESPACE}" })}}`,
|
||||||
@ -99,9 +96,8 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'config.params': {
|
params: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'config.params',
|
|
||||||
'x-component': 'ArrayItems',
|
'x-component': 'ArrayItems',
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
title: `{{t("Parameters", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Parameters", { ns: "${NAMESPACE}" })}}`,
|
||||||
@ -145,9 +141,8 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'config.data': {
|
data: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'config.data',
|
|
||||||
title: `{{t("Body", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Body", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-decorator-props': {},
|
'x-decorator-props': {},
|
||||||
@ -159,15 +154,14 @@ export default {
|
|||||||
},
|
},
|
||||||
placeholder: `{{t("Input request data", { ns: "${NAMESPACE}" })}}`,
|
placeholder: `{{t("Input request data", { ns: "${NAMESPACE}" })}}`,
|
||||||
className: css`
|
className: css`
|
||||||
font-size: 85%;
|
font-size: 90%;
|
||||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
`
|
`
|
||||||
},
|
},
|
||||||
description: `{{t("Only support standard JSON data", { ns: "${NAMESPACE}" })}}`,
|
description: `{{t("Only support standard JSON data", { ns: "${NAMESPACE}" })}}`,
|
||||||
},
|
},
|
||||||
'config.timeout': {
|
timeout: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
name: 'config.timeout',
|
|
||||||
title: `{{t("Timeout config", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Timeout config", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-decorator-props': {},
|
'x-decorator-props': {},
|
||||||
@ -179,9 +173,8 @@ export default {
|
|||||||
defaultValue: 5000,
|
defaultValue: 5000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'config.ignoreFail': {
|
ignoreFail: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
name: 'config.ignoreFail',
|
|
||||||
title: `{{t("Ignore fail request and continue workflow", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Ignore fail request and continue workflow", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Checkbox',
|
'x-component': 'Checkbox',
|
||||||
|
@ -12,12 +12,19 @@ export default {
|
|||||||
type: 'update',
|
type: 'update',
|
||||||
group: 'collection',
|
group: 'collection',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.collection': collection,
|
collection,
|
||||||
'config.params.filter': {
|
params: {
|
||||||
...filter,
|
type: 'object',
|
||||||
title: `{{t("Only update records matching conditions", { ns: "${NAMESPACE}" })}}`,
|
title: '',
|
||||||
},
|
'x-decorator': 'FormItem',
|
||||||
'config.params.values': values
|
properties: {
|
||||||
|
filter: {
|
||||||
|
...filter,
|
||||||
|
title: `{{t("Only update records matching conditions", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
},
|
||||||
|
values
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import { NAMESPACE } from "../locale";
|
|||||||
export const collection = {
|
export const collection = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: '{{t("Collection")}}',
|
title: '{{t("Collection")}}',
|
||||||
name: 'config.collection',
|
|
||||||
required: true,
|
required: true,
|
||||||
'x-reactions': ['{{useCollectionDataSource()}}'],
|
'x-reactions': ['{{useCollectionDataSource()}}'],
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
@ -19,7 +18,6 @@ export const collection = {
|
|||||||
export const values = {
|
export const values = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
title: '{{t("Fields values")}}',
|
title: '{{t("Fields values")}}',
|
||||||
name: 'config.params.values',
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
labelAlign: 'left',
|
labelAlign: 'left',
|
||||||
@ -34,7 +32,6 @@ export const values = {
|
|||||||
export const filter = {
|
export const filter = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
title: '{{t("Filter")}}',
|
title: '{{t("Filter")}}',
|
||||||
name: 'config.params.filter',
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-decorator-props': {
|
'x-decorator-props': {
|
||||||
labelAlign: 'left',
|
labelAlign: 'left',
|
||||||
@ -46,7 +43,7 @@ export const filter = {
|
|||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
useProps() {
|
useProps() {
|
||||||
const { values } = useForm();
|
const { values } = useForm();
|
||||||
const options = useCollectionFilterOptions(values.config?.collection);
|
const options = useCollectionFilterOptions(values?.collection);
|
||||||
return {
|
return {
|
||||||
options,
|
options,
|
||||||
className: css`
|
className: css`
|
||||||
|
@ -157,19 +157,27 @@ export const nodeClass = css`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const nodeCardClass = css`
|
export const nodeCardClass = css`
|
||||||
|
position: relative;
|
||||||
width: 20em;
|
width: 20em;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
box-shadow: 0 .25em .5em rgba(0, 0, 0, .1);
|
box-shadow: 0 .25em .5em rgba(0, 0, 0, .1);
|
||||||
|
border-radius: .5em;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: box-shadow .3s ease;
|
||||||
|
|
||||||
|
&.configuring{
|
||||||
|
box-shadow: 0 .25em 1em rgba(0, 100, 200, .25);
|
||||||
|
}
|
||||||
|
|
||||||
.workflow-node-remove-button,
|
.workflow-node-remove-button,
|
||||||
.workflow-node-job-button{
|
.workflow-node-job-button{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -.5em;
|
|
||||||
top: -.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-node-remove-button{
|
.workflow-node-remove-button{
|
||||||
|
right: .5em;
|
||||||
|
top: .5em;
|
||||||
color: #999;
|
color: #999;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity .3s ease;
|
transition: opacity .3s ease;
|
||||||
@ -185,8 +193,8 @@ export const nodeCardClass = css`
|
|||||||
|
|
||||||
.workflow-node-job-button{
|
.workflow-node-job-button{
|
||||||
display: flex;
|
display: flex;
|
||||||
top: 0;
|
top: 1em;
|
||||||
right: 0;
|
right: 1em;
|
||||||
width: 1.25rem;
|
width: 1.25rem;
|
||||||
height: 1.25rem;
|
height: 1.25rem;
|
||||||
min-width: 1.25rem;
|
min-width: 1.25rem;
|
||||||
@ -200,7 +208,32 @@ export const nodeCardClass = css`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-input{
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:not(:focus){
|
||||||
|
transition: background-color .3s ease, border-color .3s ease;
|
||||||
|
border-color: #f7f7f7;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
|
||||||
|
&:not(:disabled):hover{
|
||||||
|
border-color: #eee;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled:hover{
|
||||||
|
border-color: #f7f7f7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-node-config-button{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover{
|
&:hover{
|
||||||
|
box-shadow: 0 .25em .5em rgba(0, 0, 0, .25);
|
||||||
|
|
||||||
.workflow-node-remove-button{
|
.workflow-node-remove-button{
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@ -213,13 +246,20 @@ export const nodeHeaderClass = css`
|
|||||||
|
|
||||||
export const nodeMetaClass = css`
|
export const nodeMetaClass = css`
|
||||||
margin-bottom: .5em;
|
margin-bottom: .5em;
|
||||||
|
|
||||||
|
.workflow-node-id{
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
&:before{
|
||||||
|
content: "#"
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const nodeTitleClass = css`
|
export const nodeTitleClass = css`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
|
||||||
.workflow-node-id{
|
.workflow-node-id{
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
@ -234,4 +274,10 @@ export const nodeSubtreeClass = css`
|
|||||||
export const addButtonClass = css`
|
export const addButtonClass = css`
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 2em 0;
|
padding: 2em 0;
|
||||||
|
|
||||||
|
> .ant-btn{
|
||||||
|
&:disabled{
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import { onFieldValueChange } from '@formily/core';
|
import { observer, useForm } from '@formily/react';
|
||||||
import { observer, useForm, useFormEffects } from '@formily/react';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SchemaInitializerItemOptions,
|
SchemaInitializerItemOptions,
|
||||||
@ -16,26 +15,17 @@ import { CollectionBlockInitializer } from '../components/CollectionBlockInitial
|
|||||||
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
|
import { CollectionFieldInitializers } from '../components/CollectionFieldInitializers';
|
||||||
import { NAMESPACE, useWorkflowTranslation } from '../locale';
|
import { NAMESPACE, useWorkflowTranslation } from '../locale';
|
||||||
|
|
||||||
const FieldsSelect = observer((props) => {
|
const FieldsSelect = observer((props: any) => {
|
||||||
|
const { filter = () => true, ...others } = props;
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { getCollectionFields } = useCollectionManager();
|
const { getCollectionFields } = useCollectionManager();
|
||||||
const { values, clearFormGraph, setValuesIn } = useForm();
|
const { values } = useForm();
|
||||||
const fields = getCollectionFields(values?.config?.collection);
|
const fields = getCollectionFields(values?.collection);
|
||||||
useFormEffects(() => {
|
|
||||||
onFieldValueChange('config.collection', (field) => {
|
|
||||||
clearFormGraph('config.changed');
|
|
||||||
setValuesIn('config.condition', null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select {...props}>
|
<Select {...others}>
|
||||||
{fields
|
{fields
|
||||||
.filter(field => (
|
.filter(filter)
|
||||||
!field.hidden
|
|
||||||
&& (field.uiSchema ? !field.uiSchema['x-read-pretty'] : true)
|
|
||||||
&& !['linkTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(field.type)
|
|
||||||
))
|
|
||||||
.map(field => (
|
.map(field => (
|
||||||
<Select.Option key={field.name} value={field.name}>{compile(field.uiSchema?.title)}</Select.Option>
|
<Select.Option key={field.name} value={field.name}>{compile(field.uiSchema?.title)}</Select.Option>
|
||||||
))}
|
))}
|
||||||
@ -61,40 +51,33 @@ export default {
|
|||||||
title: `{{t("Collection event", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Collection event", { ns: "${NAMESPACE}" })}}`,
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
'config.collection': {
|
collection: {
|
||||||
...collection,
|
...collection,
|
||||||
['x-reactions']: [
|
['x-reactions']: [
|
||||||
...collection['x-reactions'],
|
...collection['x-reactions'],
|
||||||
{
|
{
|
||||||
target: 'config.mode',
|
target: 'changed',
|
||||||
|
effects: ['onFieldValueChange'],
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
value: []
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: 'config.changed',
|
target: 'condition',
|
||||||
|
effects: ['onFieldValueChange'],
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
value: null
|
||||||
},
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
target: 'config.condition',
|
|
||||||
fulfill: {
|
|
||||||
state: {
|
|
||||||
visible: '{{!!$self.value}}',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'config.mode': {
|
mode: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
title: `{{t("Trigger on", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Trigger on", { ns: "${NAMESPACE}" })}}`,
|
||||||
name: 'config.mode',
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Select',
|
'x-component': 'Select',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
@ -104,7 +87,15 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
'x-reactions': [
|
'x-reactions': [
|
||||||
{
|
{
|
||||||
target: 'config.changed',
|
dependencies: ['collection'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
visible: '{{!!$deps[0]}}',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: 'changed',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
disabled: `{{!($self.value & ${COLLECTION_TRIGGER_MODE.UPDATED})}}`,
|
disabled: `{{!($self.value & ${COLLECTION_TRIGGER_MODE.UPDATED})}}`,
|
||||||
@ -113,23 +104,69 @@ export default {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'config.changed': {
|
changed: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
name: 'changed',
|
|
||||||
title: `{{t("Changed fields", { ns: "${NAMESPACE}" })}}`,
|
title: `{{t("Changed fields", { ns: "${NAMESPACE}" })}}`,
|
||||||
description: `{{t("Triggered only if one of the selected fields changes. If unselected, it means that it will be triggered when any field changes. When record is added or deleted, any field is considered to have been changed.", { ns: "${NAMESPACE}" })}}`,
|
description: `{{t("Triggered only if one of the selected fields changes. If unselected, it means that it will be triggered when any field changes. When record is added or deleted, any field is considered to have been changed.", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'FieldsSelect',
|
'x-component': 'FieldsSelect',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
placeholder: '{{t("Select Field")}}'
|
placeholder: '{{t("Select Field")}}',
|
||||||
}
|
filter(field) {
|
||||||
|
return (
|
||||||
|
!field.hidden
|
||||||
|
&& (field.uiSchema ? !field.uiSchema['x-read-pretty'] : true)
|
||||||
|
&& !['linkTo', 'hasOne', 'hasMany', 'belongsToMany'].includes(field.type)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'x-reactions': [
|
||||||
|
{
|
||||||
|
dependencies: ['collection'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
visible: '{{!!$deps[0]}}',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
},
|
},
|
||||||
'config.condition': {
|
condition: {
|
||||||
...filter,
|
...filter,
|
||||||
name: 'config.condition',
|
title: `{{t("Only triggers when match conditions", { ns: "${NAMESPACE}" })}}`,
|
||||||
title: `{{t("Only triggers when match conditions", { ns: "${NAMESPACE}" })}}`
|
'x-reactions': [
|
||||||
}
|
{
|
||||||
|
dependencies: ['collection'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
visible: '{{!!$deps[0]}}',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// appends: {
|
||||||
|
// type: 'array',
|
||||||
|
// title: `{{t("Prefetch fields", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
// description: `{{t("Triggered only if one of the selected fields changes. If unselected, it means that it will be triggered when any field changes. When record is added or deleted, any field is considered to have been changed.", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
// 'x-decorator': 'FormItem',
|
||||||
|
// 'x-component': 'FieldsSelect',
|
||||||
|
// 'x-component-props': {
|
||||||
|
// mode: 'multiple',
|
||||||
|
// placeholder: '{{t("Select Field")}}'
|
||||||
|
// },
|
||||||
|
// 'x-reactions': [
|
||||||
|
// {
|
||||||
|
// dependencies: ['collection'],
|
||||||
|
// fulfill: {
|
||||||
|
// state: {
|
||||||
|
// visible: '{{!!$deps[0]}}',
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
scope: {
|
scope: {
|
||||||
useCollectionDataSource
|
useCollectionDataSource
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
import { css, cx } from "@emotion/css";
|
import { css, cx } from "@emotion/css";
|
||||||
import { ISchema, useForm } from "@formily/react";
|
import { ISchema, useForm } from "@formily/react";
|
||||||
import { Registry } from "@nocobase/utils/client";
|
import { Registry } from "@nocobase/utils/client";
|
||||||
import { message, Tag, Alert } from "antd";
|
import { message, Tag, Alert, Button, Input } from "antd";
|
||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { InfoOutlined } from '@ant-design/icons';
|
import { InfoOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
import { SchemaComponent, SchemaInitializerItemOptions, useActionContext, useAPIClient, useCompile, useRequest, useResourceActionContext } from '@nocobase/client';
|
import { ActionContext, SchemaComponent, SchemaInitializerItemOptions, useActionContext, useAPIClient, useCompile, useRequest, useResourceActionContext } from '@nocobase/client';
|
||||||
|
|
||||||
import { nodeCardClass, nodeHeaderClass, nodeMetaClass, nodeTitleClass } from "../style";
|
import { nodeCardClass, nodeMetaClass, nodeTitleClass } from "../style";
|
||||||
import { useFlowContext } from "../FlowContext";
|
import { useFlowContext } from "../FlowContext";
|
||||||
import collection from './collection';
|
import collection from './collection';
|
||||||
import schedule from "./schedule/";
|
import schedule from "./schedule/";
|
||||||
@ -16,7 +16,6 @@ import { lang, NAMESPACE } from "../locale";
|
|||||||
|
|
||||||
|
|
||||||
function useUpdateConfigAction() {
|
function useUpdateConfigAction() {
|
||||||
const { t } = useTranslation();
|
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
const api = useAPIClient();
|
const api = useAPIClient();
|
||||||
const { workflow } = useFlowContext() ?? {};
|
const { workflow } = useFlowContext() ?? {};
|
||||||
@ -25,13 +24,15 @@ function useUpdateConfigAction() {
|
|||||||
return {
|
return {
|
||||||
async run() {
|
async run() {
|
||||||
if (workflow.executed) {
|
if (workflow.executed) {
|
||||||
message.error(t('Trigger in executed workflow cannot be modified'));
|
message.error(lang('Trigger in executed workflow cannot be modified'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await form.submit();
|
await form.submit();
|
||||||
await api.resource('workflows').update?.({
|
await api.resource('workflows').update?.({
|
||||||
filterByTk: workflow.id,
|
filterByTk: workflow.id,
|
||||||
values: form.values
|
values: {
|
||||||
|
config: form.values
|
||||||
|
}
|
||||||
});
|
});
|
||||||
ctx.setVisible(false);
|
ctx.setVisible(false);
|
||||||
refresh();
|
refresh();
|
||||||
@ -127,108 +128,151 @@ function TriggerExecution() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TriggerConfig = () => {
|
export const TriggerConfig = () => {
|
||||||
const { t } = useTranslation();
|
const api = useAPIClient();
|
||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { workflow } = useFlowContext();
|
const { workflow, refresh } = useFlowContext();
|
||||||
if (!workflow || !workflow.type) {
|
if (!workflow || !workflow.type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { type, config, executed } = workflow;
|
const { title, type, config, executed } = workflow;
|
||||||
const { title, fieldset, scope, components } = triggers.get(type);
|
const { title: typeTitle, fieldset, scope, components } = triggers.get(type);
|
||||||
const detailText = executed ? '{{t("View")}}' : '{{t("Configure")}}';
|
const detailText = executed ? '{{t("View")}}' : '{{t("Configure")}}';
|
||||||
const titleText = `${lang('Trigger')}: ${compile(title)}`;
|
const titleText = `${lang('Trigger')}: ${compile(typeTitle)}`;
|
||||||
|
|
||||||
|
const [editingTitle, setEditingTitle] = useState<string>(title ?? typeTitle);
|
||||||
|
const [editingConfig, setEditingConfig] = useState(false);
|
||||||
|
|
||||||
|
async function onChangeTitle(next) {
|
||||||
|
const t = next || typeTitle;
|
||||||
|
setEditingTitle(t);
|
||||||
|
if (t === title) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await api.resource('workflows').update?.({
|
||||||
|
filterByTk: workflow.id,
|
||||||
|
values: {
|
||||||
|
title: t
|
||||||
|
}
|
||||||
|
});
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpenDrawer(ev) {
|
||||||
|
if (ev.target === ev.currentTarget) {
|
||||||
|
setEditingConfig(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const whiteSet = new Set(['workflow-node-meta', 'workflow-node-config-button', 'ant-input-disabled']);
|
||||||
|
for (let el = ev.target; el && el !== ev.currentTarget; el = el.parentNode) {
|
||||||
|
if (Array.from(el.classList).some((name: string) => whiteSet.has(name))) {
|
||||||
|
setEditingConfig(true);
|
||||||
|
ev.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx(nodeCardClass)}>
|
<div className={cx(nodeCardClass)} onClick={onOpenDrawer}>
|
||||||
<div className={cx(nodeHeaderClass)}>
|
<div className={cx(nodeMetaClass, 'workflow-node-meta')}>
|
||||||
<div className={cx(nodeMetaClass)}>
|
<Tag color="gold">{titleText}</Tag>
|
||||||
<Tag color="gold">{lang('Trigger')}</Tag>
|
|
||||||
</div>
|
|
||||||
<h4>{compile(title)}</h4>
|
|
||||||
<TriggerExecution />
|
|
||||||
</div>
|
</div>
|
||||||
<SchemaComponent
|
<div>
|
||||||
schema={{
|
<Input.TextArea
|
||||||
type: 'void',
|
value={editingTitle}
|
||||||
title: detailText,
|
onChange={(ev) => setEditingTitle(ev.target.value)}
|
||||||
'x-component': 'Action.Link',
|
onBlur={(ev) => onChangeTitle(ev.target.value)}
|
||||||
name: 'drawer',
|
autoSize
|
||||||
properties: {
|
/>
|
||||||
drawer: {
|
</div>
|
||||||
type: 'void',
|
<TriggerExecution />
|
||||||
title: titleText,
|
<ActionContext.Provider value={{ visible: editingConfig, setVisible: setEditingConfig }}>
|
||||||
'x-component': 'Action.Drawer',
|
<SchemaComponent
|
||||||
'x-decorator': 'Form',
|
schema={{
|
||||||
'x-decorator-props': {
|
type: 'void',
|
||||||
disabled: workflow.executed,
|
properties: {
|
||||||
useValues(options) {
|
config: {
|
||||||
return useRequest(() => Promise.resolve({
|
type: 'void',
|
||||||
data: { config },
|
'x-content': detailText,
|
||||||
}), options);
|
'x-component': Button,
|
||||||
|
'x-component-props': {
|
||||||
|
type: 'link',
|
||||||
|
className: 'workflow-node-config-button'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
properties: {
|
drawer: {
|
||||||
config: {
|
type: 'void',
|
||||||
type: 'void',
|
title: titleText,
|
||||||
name: 'config',
|
'x-component': 'Action.Drawer',
|
||||||
'x-component': 'fieldset',
|
'x-decorator': 'Form',
|
||||||
'x-component-props': {
|
'x-decorator-props': {
|
||||||
className: css`
|
disabled: workflow.executed,
|
||||||
.ant-select{
|
useValues(options) {
|
||||||
width: auto;
|
return useRequest(() => Promise.resolve({
|
||||||
min-width: 6em;
|
data: config,
|
||||||
}
|
}), options);
|
||||||
`
|
|
||||||
},
|
},
|
||||||
properties: {
|
|
||||||
...(executed ? {
|
|
||||||
alert: {
|
|
||||||
'x-component': Alert,
|
|
||||||
'x-component-props': {
|
|
||||||
type: 'warning',
|
|
||||||
showIcon: true,
|
|
||||||
message: `{{t("Trigger in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
|
|
||||||
className: css`
|
|
||||||
width: 100%;
|
|
||||||
font-size: 85%;
|
|
||||||
margin-bottom: 2em;
|
|
||||||
`
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} : {}),
|
|
||||||
...fieldset
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
actions: executed
|
properties: {
|
||||||
? null
|
...(executed ? {
|
||||||
: {
|
alert: {
|
||||||
type: 'void',
|
'x-component': Alert,
|
||||||
'x-component': 'Action.Drawer.Footer',
|
|
||||||
properties: {
|
|
||||||
cancel: {
|
|
||||||
title: '{{t("Cancel")}}',
|
|
||||||
'x-component': 'Action',
|
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
useAction: '{{ cm.useCancelAction }}',
|
type: 'warning',
|
||||||
|
showIcon: true,
|
||||||
|
message: `{{t("Trigger in executed workflow cannot be modified", { ns: "${NAMESPACE}" })}}`,
|
||||||
|
className: css`
|
||||||
|
width: 100%;
|
||||||
|
font-size: 85%;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
`
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
} : {}),
|
||||||
|
fieldset: {
|
||||||
|
type: 'void',
|
||||||
|
'x-component': 'fieldset',
|
||||||
|
'x-component-props': {
|
||||||
|
className: css`
|
||||||
|
.ant-select{
|
||||||
|
width: auto;
|
||||||
|
min-width: 6em;
|
||||||
|
}
|
||||||
|
`
|
||||||
},
|
},
|
||||||
submit: {
|
properties: fieldset
|
||||||
title: '{{t("Submit")}}',
|
},
|
||||||
'x-component': 'Action',
|
actions: executed
|
||||||
'x-component-props': {
|
? null
|
||||||
type: 'primary',
|
: {
|
||||||
useAction: useUpdateConfigAction
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
scope={scope}
|
||||||
scope={scope}
|
components={components}
|
||||||
components={components}
|
/>
|
||||||
/>
|
</ActionContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ export const DateFieldsSelect: React.FC<any> = observer((props) => {
|
|||||||
const compile = useCompile();
|
const compile = useCompile();
|
||||||
const { getCollectionFields } = useCollectionManager();
|
const { getCollectionFields } = useCollectionManager();
|
||||||
const { values } = useForm();
|
const { values } = useForm();
|
||||||
const fields = getCollectionFields(values?.config?.collection);
|
const fields = getCollectionFields(values?.collection);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
|
@ -33,7 +33,7 @@ const ModeFieldsets = {
|
|||||||
'x-component': 'RepeatField',
|
'x-component': 'RepeatField',
|
||||||
'x-reactions': [
|
'x-reactions': [
|
||||||
{
|
{
|
||||||
target: 'config.endsOn',
|
target: 'endsOn',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
visible: '{{!!$self.value}}',
|
||||||
@ -41,7 +41,7 @@ const ModeFieldsets = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: 'config.limit',
|
target: 'limit',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
visible: '{{!!$self.value}}',
|
||||||
@ -79,7 +79,7 @@ const ModeFieldsets = {
|
|||||||
...collection['x-reactions'],
|
...collection['x-reactions'],
|
||||||
{
|
{
|
||||||
// only full path works
|
// only full path works
|
||||||
target: 'config.startsOn',
|
target: 'startsOn',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
visible: '{{!!$self.value}}',
|
||||||
@ -95,7 +95,7 @@ const ModeFieldsets = {
|
|||||||
'x-component': 'OnField',
|
'x-component': 'OnField',
|
||||||
'x-reactions': [
|
'x-reactions': [
|
||||||
{
|
{
|
||||||
target: 'config.repeat',
|
target: 'repeat',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
visible: '{{!!$self.value}}',
|
||||||
@ -113,7 +113,7 @@ const ModeFieldsets = {
|
|||||||
'x-component': 'RepeatField',
|
'x-component': 'RepeatField',
|
||||||
'x-reactions': [
|
'x-reactions': [
|
||||||
{
|
{
|
||||||
target: 'config.endsOn',
|
target: 'endsOn',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
visible: '{{!!$self.value}}',
|
||||||
@ -121,7 +121,7 @@ const ModeFieldsets = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: 'config.limit',
|
target: 'limit',
|
||||||
fulfill: {
|
fulfill: {
|
||||||
state: {
|
state: {
|
||||||
visible: '{{!!$self.value}}',
|
visible: '{{!!$self.value}}',
|
||||||
@ -157,16 +157,15 @@ const scheduleModeOptions = [
|
|||||||
|
|
||||||
export const ScheduleConfig = () => {
|
export const ScheduleConfig = () => {
|
||||||
const { values = {}, clearFormGraph } = useForm();
|
const { values = {}, clearFormGraph } = useForm();
|
||||||
const { config = {} } = values;
|
const [mode, setMode] = useState(values.mode);
|
||||||
const [mode, setMode] = useState(config.mode);
|
|
||||||
useFormEffects(() => {
|
useFormEffects(() => {
|
||||||
onFieldValueChange('config.mode', (field) => {
|
onFieldValueChange('mode', (field) => {
|
||||||
setMode(field.value);
|
setMode(field.value);
|
||||||
clearFormGraph('config.collection');
|
clearFormGraph('collection');
|
||||||
clearFormGraph('config.startsOn');
|
clearFormGraph('startsOn');
|
||||||
clearFormGraph('config.repeat');
|
clearFormGraph('repeat');
|
||||||
clearFormGraph('config.endsOn');
|
clearFormGraph('endsOn');
|
||||||
clearFormGraph('config.limit');
|
clearFormGraph('limit');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -182,7 +181,8 @@ export const ScheduleConfig = () => {
|
|||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
options: scheduleModeOptions
|
options: scheduleModeOptions
|
||||||
},
|
},
|
||||||
required: true
|
required: true,
|
||||||
|
default: SCHEDULE_MODE.STATIC
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
|
@ -14,8 +14,7 @@ export default {
|
|||||||
type: 'schedule',
|
type: 'schedule',
|
||||||
fieldset: {
|
fieldset: {
|
||||||
config: {
|
config: {
|
||||||
type: 'object',
|
type: 'void',
|
||||||
name: 'config',
|
|
||||||
'x-component': 'ScheduleConfig',
|
'x-component': 'ScheduleConfig',
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user