From 3c555e9fc0b29ee5810cadf8290facebcd7704f1 Mon Sep 17 00:00:00 2001 From: Junyi Date: Mon, 30 Jun 2025 11:45:39 +0800 Subject: [PATCH] refactor(plugin-workflow): add log for node testing (#7129) --- .../src/client/nodes/calculation.tsx | 1 + .../src/client/nodes/condition.tsx | 1 + .../src/client/nodes/index.tsx | 234 +++++++++++------- .../instructions/CalculationInstruction.ts | 16 ++ .../instructions/ConditionInstruction.ts | 18 +- 5 files changed, 177 insertions(+), 93 deletions(-) diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx index 28c9fd676e..cb4d7e2f48 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/calculation.tsx @@ -100,4 +100,5 @@ export default class extends Instruction { resultTitle: lang('Calculation result'), }; } + testable = true; } diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx index c0b39d58a8..259f93896b 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/condition.tsx @@ -194,4 +194,5 @@ export default class extends Instruction { ); } + testable = true; } diff --git a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx index 5f1d3ddf89..98a5c0f133 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx +++ b/packages/plugins/@nocobase/plugin-workflow/src/client/nodes/index.tsx @@ -7,11 +7,11 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { CloseOutlined, DeleteOutlined } from '@ant-design/icons'; +import { CaretRightOutlined, CloseOutlined, DeleteOutlined } from '@ant-design/icons'; import { createForm, Field } from '@formily/core'; import { toJS } from '@formily/reactive'; import { ISchema, observer, useField, useForm } from '@formily/react'; -import { Alert, App, Button, Dropdown, Empty, Input, Space, Tag, Tooltip, message } from 'antd'; +import { Alert, App, Button, Collapse, Dropdown, Empty, Input, Space, Tag, Tooltip, message } from 'antd'; import { cloneDeep, get, set } from 'lodash'; import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -26,7 +26,6 @@ import { cx, useAPIClient, useActionContext, - useCancelAction, useCompile, usePlugin, useResourceActionContext, @@ -330,6 +329,7 @@ const useRunAction = () => { async run() { const template = parse(node.config); const config = template(toJS(values.config)); + const logField = query('log').take() as Field; const resultField = query('result').take() as Field; resultField.setValue(null); resultField.setFeedback({}); @@ -352,6 +352,7 @@ const useRunAction = () => { messages: data.status > 0 ? [lang('Resolved')] : [lang('Failed')], }); resultField.setValue(data.result); + logField.setValue(data.log || ''); } catch (err) { resultField.setFeedback({ type: 'error', @@ -359,7 +360,6 @@ const useRunAction = () => { }); } field.data.loading = false; - ctx.setFormValueChanged(false); }, }; }; @@ -397,113 +397,163 @@ function TestFormFieldset({ value, onChange }) { ); } +function LogCollapse({ value }) { + return value ? ( + + ), + }, + ]} + className={css` + .ant-collapse-item > .ant-collapse-header { + padding: 0; + } + .ant-collapse-content > .ant-collapse-content-box { + padding: 0; + } + `} + /> + ) : null; +} + +function useCancelAction() { + const form = useForm(); + const ctx = useActionContext(); + return { + async run() { + const resultField = form.query('result').take() as Field; + resultField.setFeedback(); + form.setValues({ result: null, log: null }); + form.clearFormGraph('*'); + form.reset(); + ctx.setVisible(false); + }, + }; +} + function TestButton() { const node = useNodeContext(); const { values } = useForm(); + const [visible, setVisible] = useState(false); const template = parse(values); const keys = template.parameters.map((item) => item.key); const form = useMemo(() => createForm(), []); + const onOpen = useCallback(() => { + setVisible(true); + }, []); + const setModalVisible = useCallback( + (v: boolean) => { + if (v) { + setVisible(true); + return; + } + const resultField = form.query('result').take() as Field; + resultField?.setFeedback(); + form.setValues({ result: null, log: null }); + form.clearFormGraph('*'); + form.reset(); + setVisible(false); + }, + [form], + ); + return ( - + + + }} + /> + ); diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/CalculationInstruction.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/CalculationInstruction.ts index 6512cc4a3b..9b1afcea4c 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/CalculationInstruction.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/CalculationInstruction.ts @@ -39,6 +39,22 @@ export class CalculationInstruction extends Instruction { }; } } + + async test({ engine = 'math.js', expression = '' }) { + const evaluator = evaluators.get(engine); + try { + const result = evaluator && expression ? evaluator(expression) : null; + return { + result, + status: JOB_STATUS.RESOLVED, + }; + } catch (e) { + return { + result: e.toString(), + status: JOB_STATUS.ERROR, + }; + } + } } export default CalculationInstruction; diff --git a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts index f9eaecd846..ce4df84bc8 100644 --- a/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts +++ b/packages/plugins/@nocobase/plugin-workflow/src/server/instructions/ConditionInstruction.ts @@ -7,7 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { evaluators } from '@nocobase/evaluators'; +import { Evaluator, evaluators } from '@nocobase/evaluators'; import { Instruction } from '.'; import type Processor from '../Processor'; import { JOB_STATUS } from '../constants'; @@ -80,6 +80,22 @@ export class ConditionInstruction extends Instruction { // pass control to upper scope by ending current scope return processor.exit(branchJob.status); } + + async test({ engine, calculation, expression = '' }) { + const evaluator = evaluators.get(engine); + try { + const result = evaluator ? evaluator(expression) : logicCalculate(calculation); + return { + result, + status: JOB_STATUS.RESOLVED, + }; + } catch (e) { + return { + result: e.toString(), + status: JOB_STATUS.ERROR, + }; + } + } } export default ConditionInstruction;