diff --git a/designs/groovy-script/DESiGN.md b/designs/groovy-script/DESiGN.md new file mode 100644 index 00000000..90d6ae44 --- /dev/null +++ b/designs/groovy-script/DESiGN.md @@ -0,0 +1,43 @@ +# Groovy Script 脚本设计规范 + +## 脚本的实例写法如下 +``` +def run(request){ + return "Hello, ${request.name}!" +} +``` + +## 脚本规范 +request对象,根据脚本的不同,起传递的request对象也不同。 +return语句,return语句根据脚本的不同,起返回的对象也不同。 + +## 开发规范 +为了让脚本可以更好的呈现和使用,脚本的配置分为两种模式,一种是可视化配置模式,一种是代码配置模式。 + +* 代码配置模式 + 代码配置模式的脚本中,将会通过注释的方式添加一行@CUSTOM_SCRIPT,来标识这是一个自定义脚本,这样在编辑器中就会以代码的形式展示出来。 +``` +// @CUSTOM_SCRIPT +def run(request){ + return "Hello, ${request.name}!" +} +``` + +* 可视化配置模式 + 可视化配置模式的脚本中,没有@CUSTOM_SCRIPT的注释标识,这样在编辑器中就会以可视化的形式展示出来。 +``` +def run(request){ + return "Hello, ${request.name}!" +} +``` + +## 脚本展示标题 +为了让脚本在在展示时可以更好的展示脚本的作用,所以在脚本中支持通过@SCRIPT_TITLE的注释来标识脚本的展示标题,这样在编辑器中就会以这个标题来展示脚本。 + +``` +// @SCRIPT_TITLE 这是一个示例脚本 +def run(request){ + return "Hello, ${request.name}!" +} +``` +上述的脚本在编辑器中就会以“这是一个示例脚本”来展示,而不是以代码的方式来展示。 \ No newline at end of file diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ConditionScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ConditionScript.java index c8c506c8..41803f31 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ConditionScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ConditionScript.java @@ -9,7 +9,12 @@ @AllArgsConstructor public class ConditionScript { - public static final String SCRIPT_DEFAULT = "def run(request){return true}"; + public static final String SCRIPT_DEFAULT = """ + // @SCRIPT_TITLE 默认条件(允许执行) + def run(request){ + return true; + } + """; @Getter private final String script; diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ErrorTriggerScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ErrorTriggerScript.java index a114c81c..0879d2fb 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ErrorTriggerScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/ErrorTriggerScript.java @@ -13,9 +13,19 @@ public class ErrorTriggerScript { - public static final String SCRIPT_NODE_DEFAULT = "def run(request){ return $bind.createErrorThrow(request.getStartNode()); }"; - - public static final String SCRIPT_OPERATOR_DEFAULT = "def run(request){ return $bind.createErrorThrow(request.getCreatedOperator()); }"; + public static final String SCRIPT_NODE_DEFAULT = """ + // @SCRIPT_TITLE 回退至开始节点 + def run(request){ + return $bind.createErrorThrow(request.getStartNode()); + } + """; + + public static final String SCRIPT_OPERATOR_DEFAULT = """ + // @SCRIPT_TITLE 指定用户到流程发起者 + def run(request){ + return $bind.createErrorThrow(request.getCreatedOperator()); + } + """; @Getter private final String script; diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/NodeTitleScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/NodeTitleScript.java index d62808d9..a51df84d 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/NodeTitleScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/NodeTitleScript.java @@ -12,7 +12,12 @@ @AllArgsConstructor public class NodeTitleScript { - public static final String SCRIPT_DEFAULT = "def run(request){return '你有一条待办'}"; + public static final String SCRIPT_DEFAULT = """ + // @SCRIPT_TITLE 你有一条待办 + def run(request){ + return '你有一条待办' + } + """; @Getter private final String script; diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorLoadScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorLoadScript.java index 5202ce28..848800c4 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorLoadScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorLoadScript.java @@ -15,7 +15,12 @@ @AllArgsConstructor public class OperatorLoadScript { - public static final String SCRIPT_CREATOR = "def run(request){return [request.getCreatedOperator()]}"; + public static final String SCRIPT_CREATOR = """ + // @SCRIPT_TITLE 流程创建者 + def run(request){ + return [request.getCreatedOperator()] + } + """; @Getter private final String script; diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorMatchScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorMatchScript.java index 97bdf693..94b569a0 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorMatchScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/OperatorMatchScript.java @@ -11,7 +11,12 @@ @AllArgsConstructor public class OperatorMatchScript { - public static final String SCRIPT_ANY = "def run(operator){return true}"; + public static final String SCRIPT_ANY = """ + // @SCRIPT_TITLE 任意用户 + def run(request){ + return true + } + """; @Getter private final String script; diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/RouterNodeScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/RouterNodeScript.java index ccf031ab..485d140d 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/RouterNodeScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/RouterNodeScript.java @@ -6,13 +6,13 @@ import lombok.Getter; /** - * 异常触发脚本 + * 路由触发脚本 */ @AllArgsConstructor public class RouterNodeScript { - public static final String SCRIPT_NODE_DEFAULT = """ + // @SCRIPT_TITLE 发起节点 def run(request){ return request.getStartNode().getId(); } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/SubProcessScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/SubProcessScript.java index fa5a43d6..c840ca7f 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/SubProcessScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/SubProcessScript.java @@ -13,7 +13,12 @@ @AllArgsConstructor public class SubProcessScript { - public static final String SCRIPT_DEFAULT = "def run(session){ return session.toCreateRequest() }"; + public static final String SCRIPT_DEFAULT = """ + // @SCRIPT_TITLE 创建当前流程 + def run(request){ + return request.toCreateRequest() + } + """; @Getter private final String script; diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/TriggerScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/TriggerScript.java index f9c663b1..e35af8ce 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/TriggerScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/node/TriggerScript.java @@ -11,7 +11,12 @@ @AllArgsConstructor public class TriggerScript { - public static final String SCRIPT_DEFAULT = "def run(session){ print('hello trigger node.') }"; + public static final String SCRIPT_DEFAULT = """ + // @SCRIPT_TITLE 示例触发节点(打印触发日志) + def run(request){ + print('hello trigger node.'); + } + """; @Getter private final String script; diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/BaseGroovyRequest.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/BaseGroovyRequest.java index f40a5c2c..d62b0eae 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/BaseGroovyRequest.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/BaseGroovyRequest.java @@ -22,7 +22,7 @@ public abstract class BaseGroovyRequest { /** * 当前操作人ID */ - protected Integer operatorId; + protected long operatorId; /** * 是否流程管理员 diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/OperatorLoadGroovyRequest.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/OperatorLoadGroovyRequest.java index 9cab1540..d37ae1ee 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/OperatorLoadGroovyRequest.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/request/OperatorLoadGroovyRequest.java @@ -19,6 +19,11 @@ public class OperatorLoadGroovyRequest extends BaseGroovyRequest { */ private IFlowOperator createdOperator; + /** + * 当前操作人(上一节点审批人) + */ + private IFlowOperator currentOperator; + /** * 从FlowSession构建OperatorLoadGroovyRequest * @param session 流程会话(不能为null) @@ -29,5 +34,6 @@ public OperatorLoadGroovyRequest(FlowSession session) { if (session.getWorkflow() != null && session.getWorkflow().getCreatedOperator() != null) { this.createdOperator = session.getWorkflow().getCreatedOperator(); } + this.currentOperator = session.getCurrentOperator(); } } diff --git a/frontend/apps/app-pc/package.json b/frontend/apps/app-pc/package.json index 952cb743..4fa619db 100644 --- a/frontend/apps/app-pc/package.json +++ b/frontend/apps/app-pc/package.json @@ -13,7 +13,6 @@ "@flow-engine/flow-types": "workspace:*", "@flow-engine/flow-pc-design": "workspace:*", "@flow-engine/flow-pc-ui": "workspace:*", - "@flow-engine/flow-pc-form": "workspace:*", "@flow-engine/flow-pc-approval":"workspace:*", "antd": "^6.2.1", "dayjs": "^1.11.19", diff --git a/frontend/apps/app-pc/src/config/plugin-view.tsx b/frontend/apps/app-pc/src/config/plugin-view.tsx deleted file mode 100644 index cda74835..00000000 --- a/frontend/apps/app-pc/src/config/plugin-view.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import {ViewBindPlugin} from "@flow-engine/flow-types"; -import {FlowFormView} from "@flow-engine/flow-pc-form"; - -ViewBindPlugin.getInstance().register('default',FlowFormView); \ No newline at end of file diff --git a/frontend/apps/app-pc/src/index.tsx b/frontend/apps/app-pc/src/index.tsx index 67dd06b9..b3a719ca 100644 --- a/frontend/apps/app-pc/src/index.tsx +++ b/frontend/apps/app-pc/src/index.tsx @@ -7,7 +7,6 @@ import zhCN from 'antd/locale/zh_CN'; import dayjs from 'dayjs'; import 'dayjs/locale/zh-cn'; import "./index.css"; -import "./config/plugin-view"; dayjs.locale('zh'); diff --git a/frontend/packages/flow-pc/flow-pc-approval/package.json b/frontend/packages/flow-pc/flow-pc-approval/package.json index 37dafce5..c9c3edd3 100644 --- a/frontend/packages/flow-pc/flow-pc-approval/package.json +++ b/frontend/packages/flow-pc/flow-pc-approval/package.json @@ -28,6 +28,7 @@ "@flow-engine/flow-types": "workspace:*", "@flow-engine/flow-pc-design": "workspace:*", "@flow-engine/flow-pc-ui": "workspace:*", + "@flow-engine/flow-pc-form": "workspace:*", "@reduxjs/toolkit": "^2.11.2", "antd": "^6.2.1", "immer": "^11.1.3", diff --git a/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/form-view-component.tsx b/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/form-view-component.tsx index e39fa230..277bfc74 100644 --- a/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/form-view-component.tsx +++ b/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/form-view-component.tsx @@ -2,6 +2,7 @@ import React from "react"; import {useApprovalContext} from "@/components/flow-approval/hooks/use-approval-context"; import {ViewBindPlugin} from "@flow-engine/flow-types"; import { Form } from "antd"; +import {FlowFormView} from "@flow-engine/flow-pc-form"; interface FormViewComponentProps{ onValuesChange?:(values:any)=>void; @@ -9,7 +10,7 @@ interface FormViewComponentProps{ export const FormViewComponent: React.FC = (props) => { const {state, context} = useApprovalContext(); - const ViewComponent = ViewBindPlugin.getInstance().get(state.flow?.view || 'default'); + const ViewComponent = ViewBindPlugin.getInstance().get(state.flow?.view || 'default') || FlowFormView ; const formMeta = state.flow?.form; diff --git a/frontend/packages/flow-pc/flow-pc-design/package.json b/frontend/packages/flow-pc/flow-pc-design/package.json index 2c444415..f833ae02 100644 --- a/frontend/packages/flow-pc/flow-pc-design/package.json +++ b/frontend/packages/flow-pc/flow-pc-design/package.json @@ -23,9 +23,11 @@ }, "dependencies": { "@ant-design/icons": "~6.1.0", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/theme-one-dark": "^6.1.3", "@flow-engine/flow-core": "workspace:*", - "@flow-engine/flow-types": "workspace:*", "@flow-engine/flow-pc-ui": "workspace:*", + "@flow-engine/flow-types": "workspace:*", "@flowgram.ai/export-plugin": "1.0.7", "@flowgram.ai/fixed-layout-editor": "1.0.7", "@flowgram.ai/fixed-semi-materials": "1.0.7", diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/branch-adder/index.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/branch-adder/index.tsx index 79f1a70d..731244ee 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/branch-adder/index.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/branch-adder/index.tsx @@ -1,7 +1,6 @@ import {type FlowNodeEntity, useClientContext} from '@flowgram.ai/fixed-layout-editor'; import React, {useCallback, useContext} from "react"; import {NodeRenderContext} from "@/components/design-editor/context"; -import {NodePanel} from "@/components/design-editor/node-components/panel"; import {NodeHeader} from "@/components/design-editor/node-components/header"; import {Button} from "antd"; import {nodeFormPanelFactory} from "@/components/design-editor/components/sidebar"; @@ -54,13 +53,13 @@ export const BranchAdderRender: React.FC = (props) => { const canAddBranch = playground.config.readonlyOrDisabled; return ( - +
- +
); }; diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/context/index.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/context/index.ts index b0637c0b..c8556f7a 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/context/index.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/context/index.ts @@ -1,2 +1,3 @@ export { NodeRenderContext } from './node-render-context'; -export { IsSidebarContext } from './sidebar-context'; \ No newline at end of file +export { IsSidebarContext } from './sidebar-context'; +export { NodeFormContext } from './node-form-context'; \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/context/node-form-context.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/context/node-form-context.ts new file mode 100644 index 00000000..6a94e526 --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/context/node-form-context.ts @@ -0,0 +1,6 @@ +import React from 'react'; +import {FlowNodeJSON} from "@/components/design-editor/typings"; +import {FormRenderProps,} from '@flowgram.ai/fixed-layout-editor'; + +export const NodeFormContext = React.createContext>({} as any); + diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/hooks/use-node-form.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/hooks/use-node-form.ts new file mode 100644 index 00000000..64dd5a41 --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/hooks/use-node-form.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react'; +import { NodeFormContext } from '../context'; + +export function useNodeFormContext() { + return useContext(NodeFormContext); +} diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/condition/index.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/condition/index.tsx index 8a0833f3..ba8763e3 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/condition/index.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/condition/index.tsx @@ -1,6 +1,8 @@ -import { Field, FieldRenderProps } from "@flowgram.ai/fixed-layout-editor"; -import { Form,Input } from "antd"; +import {Field, FieldRenderProps} from "@flowgram.ai/fixed-layout-editor"; +import {Button, Form, Space} from "antd"; import React from "react"; +import {GroovyScriptPreview} from "@/components/script/components/groovy-script-preview"; +import {EditOutlined} from "@ant-design/icons"; /** * 条件配置 @@ -25,9 +27,20 @@ export const ConditionScript = ()=>{ ) => ( - <> - - + + + + + )} /> diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/current-operator/index.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/current-operator/index.tsx new file mode 100644 index 00000000..8122ad46 --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/current-operator/index.tsx @@ -0,0 +1,13 @@ +import React from "react"; +import {useNodeFormContext} from "@/components/design-editor/hooks/use-node-form"; +import {GroovyScriptConvertorUtil} from "@/components/script/utils/convertor"; + +export const CurrentNodeOperator = () => { + const data = useNodeFormContext(); + const script = data.form.getValueIn('OperatorLoadStrategy.script'); + return ( + + {GroovyScriptConvertorUtil.getScriptTitle(script)} + + ) +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/panel/index.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/panel/index.tsx index 2774e578..dcfde693 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/panel/index.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/panel/index.tsx @@ -1,16 +1,28 @@ import React, {useCallback, useContext, useMemo, useState} from "react"; import {Flex, theme} from "antd"; import {CloseCircleOutlined} from "@ant-design/icons"; -import {NodeRenderContext} from "@/components/design-editor/context"; -import {FlowNodeRegistry} from "@/components/design-editor/typings"; -import {useClientContext} from "@flowgram.ai/fixed-layout-editor"; +import {NodeFormContext, NodeRenderContext} from "@/components/design-editor/context"; +import {FlowNodeJSON, FlowNodeRegistry} from "@/components/design-editor/typings"; +import {FormRenderProps, useClientContext} from "@flowgram.ai/fixed-layout-editor"; import {useIsSidebar} from "@/components/design-editor/hooks"; interface NodePanelProps { children?: React.ReactNode; + data: FormRenderProps; } export const NodePanel: React.FC = (props) => { + return ( + + <$NodePanel + {...props} + /> + + ) +} + + +export const $NodePanel: React.FC = (props) => { const [isHovered, setIsHovered] = useState(false); const {node, deleteNode} = useContext(NodeRenderContext); const clientContext = useClientContext(); @@ -48,7 +60,7 @@ export const NodePanel: React.FC = (props) => { deleteNode(); }, [deleteNode]); - if(isSidebar || canDeleteNode){ + if (isSidebar || canDeleteNode) { return (
void; - /** 是否只读 */ - readonly?: boolean; -} - -/** - * 高级脚本编辑器组件 - * 支持自由编辑 Groovy 脚本 - */ -export const ScriptEditor:React.FC = (props: ScriptEditorProps)=> { - const {script, onChange, readonly = false, variables} = props; - - const handleChange = (value: string) => { - if (!readonly) { - onChange(value); - } - }; - - return ( -