From 60c4c36ccf7b3293b0baa4aaa2094d5143ac28f4 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sun, 8 Mar 2026 00:06:07 +0800 Subject: [PATCH 1/7] add plugin desgin --- designs/view-plugin/DESIGN.md | 237 ++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 designs/view-plugin/DESIGN.md diff --git a/designs/view-plugin/DESIGN.md b/designs/view-plugin/DESIGN.md new file mode 100644 index 00000000..99841ce3 --- /dev/null +++ b/designs/view-plugin/DESIGN.md @@ -0,0 +1,237 @@ +# 界面拓展机制 + +支持流程组件对各类业务层面内容的界面拓展机制。 + +## 实现方式 + +``` +import {ViewBindPlugin} from "@flow-engine/flow-core"; + +// 界面视图 +const MyView:React.FC = (props)=>{ + return ( + <> + ) +} + +// 注册,关键信息为key 和 界面ComponentType,传递的属性根据不同的界面对应查看 +ViewBindPlugin.getInstance().register('MyView',MyView) +``` + +## 拓展界面 + +### 流程审核 + +* 表单渲染 +``` +export interface FormViewProps { + /** 表单操控对象 */ + form: FormInstance; + /** 表单数据更新事件 */ + onValuesChange?: (values: any) => void; + /** 表单元数据对象 */ + meta: FlowForm; + /** 是否预览模式 */ + review:boolean; +} + +``` +表单选择的key对应流程节点设置的view名称,流程引擎对default进行了模型的渲染支持。 + +* 流程操作 加签 + +``` +export const VIEW_KEY = 'AddAuditViewPlugin'; + +export interface AddAuditViewPlugin { + /** 返回用户 */ + onChange?: (value: string|string[]) => void; + /** 当前用户 */ + value?: string|string[]; +} +``` +* 流程操作 委派 +``` +export const VIEW_KEY = 'DelegateViewPlugin'; + +export interface DelegateViewPlugin { + /** 返回用户 */ + onChange?: (value: string|string[]) => void; + /** 当前用户 */ + value?: string|string[]; +} +``` + +* 流程操作 退回流程 +``` +export const VIEW_KEY = 'ReturnViewPlugin'; + +export interface ReturnViewPlugin { + /** 返回用户 */ + onChange?: (value: string|string[]) => void; + /** 当前用户 */ + value?: string|string[]; +} +``` + +* 流程操作 提交时的获取签名界面 +``` +import {FlowOperator} from "@flow-engine/flow-types"; + +export const VIEW_KEY = 'SignKeyViewPlugin'; + +export interface SignKeyViewPlugin { + /** 当前用户 */ + current: FlowOperator; + /** 返回签名 */ + onChange?: (value: string) => void; + /** 当前签名 */ + value?: string; +} +``` + +* 流程操作 转办操作 +``` +export const VIEW_KEY = 'TransferViewPlugin'; + +export interface TransferViewPlugin { + /** 返回用户 */ + onChange?: (value: string|string[]) => void; + /** 当前用户 */ + value?: string|string[]; +} +``` + +### 流程设计 + +* 流程条件控制界面 +``` +import {GroovyVariableMapping, ScriptType} from "@/components/script/typings"; + +export const VIEW_KEY = 'ConditionViewPlugin'; + +export interface ConditionViewPlugin { + /** 脚本类型 */ + type: ScriptType; + /** 当前脚本 */ + script: string; + /** 变量映射列表 */ + variables: GroovyVariableMapping[]; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` + +* 异常处理逻辑界面 +``` +import {GroovyVariableMapping, ScriptType} from "@/components/script/typings"; + +export const VIEW_KEY = 'ErrorTriggerViewPlugin'; + +export interface ErrorTriggerViewPlugin { + /** 脚本类型 */ + type: ScriptType; + /** 当前脚本 */ + script: string; + /** 变量映射列表 */ + variables: GroovyVariableMapping[]; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` + +* 自定义标题界面 +``` +import {GroovyVariableMapping, ScriptType} from "@/components/script/typings"; + +export const VIEW_KEY = 'NodeTitleViewPlugin'; + +export interface NodeTitleViewPlugin { + /** 脚本类型 */ + type: ScriptType; + /** 当前脚本 */ + script: string; + /** 变量映射列表 */ + variables: GroovyVariableMapping[]; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` + +* 设置发起人范围界面 + +``` +export const VIEW_KEY = 'OperatorCreateViewPlugin'; + +export interface OperatorCreateViewPlugin { + /** 当前脚本 */ + script: string; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` + +* 节点人员选择界面 + +``` +export const VIEW_KEY = 'OperatorLoadViewPlugin'; + +export interface OperatorLoadViewPlugin { + /** 当前脚本 */ + script: string; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` +* 路由配置界面 + +``` +import {GroovyVariableMapping, ScriptType} from "@/components/script/typings"; + +export const VIEW_KEY = 'RouterViewPlugin'; + +export interface RouterViewPlugin { + /** 脚本类型 */ + type: ScriptType; + /** 当前脚本 */ + script: string; + /** 变量映射列表 */ + variables: GroovyVariableMapping[]; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` +* 子流程配置界面 +``` +import {GroovyVariableMapping, ScriptType} from "@/components/script/typings"; + +export const VIEW_KEY = 'SubProcessViewPlugin'; + +export interface SubProcessViewPlugin { + /** 脚本类型 */ + type: ScriptType; + /** 当前脚本 */ + script: string; + /** 变量映射列表 */ + variables: GroovyVariableMapping[]; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` +* 触发流程界面 +``` +import {GroovyVariableMapping, ScriptType} from "@/components/script/typings"; + +export const VIEW_KEY = 'TriggerViewPlugin'; + +export interface TriggerViewPlugin { + /** 脚本类型 */ + type: ScriptType; + /** 当前脚本 */ + script: string; + /** 变量映射列表 */ + variables: GroovyVariableMapping[]; + /** 确认回调 */ + onChange: (script: string) => void; +} +``` \ No newline at end of file From 3c3708e16a603a1d29d2de81af146d6ef6a371f8 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sun, 8 Mar 2026 09:25:08 +0800 Subject: [PATCH 2/7] update action --- .../flow/action/actions/RejectAction.java | 2 +- .../flow/script/ScriptDefaultConstants.java | 21 ++++ .../script/action/RejectActionScript.java | 21 ++-- .../components/action/custom.tsx | 3 +- .../components/branch-adder/index.tsx | 2 +- .../components/node-icon/index.tsx | 19 +++- .../components/node-list/index.tsx | 2 +- .../action/{table.tsx => index.tsx} | 54 ++++----- .../node-components/action/manager.ts | 23 ++++ .../node-components/action/modal.tsx | 86 --------------- .../node-components/action/presenter.ts | 94 ++++++++++++++++ .../node-components/header/index.tsx | 2 +- .../node-components/taps/action.tsx | 2 +- .../src/components/design-panel/types.ts | 3 +- .../action/components/add-audit.tsx | 13 +++ .../components/action/components/custom.tsx | 104 ++++++++++++++++++ .../components/action/components/delegate.tsx | 12 ++ .../components/action/components/factory.tsx | 40 +++++++ .../components/action/components/reject.tsx | 12 ++ .../components/action/components/return.tsx | 12 ++ .../components/action/components/transfer.tsx | 12 ++ .../components}/action/icon.tsx | 0 .../script/components/action/index.tsx | 68 ++++++++++++ .../components}/action/script.tsx | 0 .../components}/action/style.tsx | 0 .../script/components/action/type.ts | 0 .../script/modal/action-config-modal.tsx | 26 +++++ .../plugins/view/error-trigger-view.tsx | 2 +- .../script/plugins/view/router-view.tsx | 2 +- .../script/services/action-custom.ts | 31 ++++++ .../index.ts => script/services/action.ts} | 13 ++- ...error-trigger.ts => node-error-trigger.ts} | 0 .../services/{router.ts => node-router.ts} | 0 .../src/components/script/typings/action.ts | 21 ++++ .../src/components/script/typings/index.ts | 58 +--------- .../src/components/script/typings/script.ts | 56 ++++++++++ .../flow-types/src/types/flow-approval.ts | 4 +- .../src/types/flow-design.ts} | 4 +- .../packages/flow-types/src/types/index.ts | 1 + 39 files changed, 616 insertions(+), 209 deletions(-) rename frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/{table.tsx => index.tsx} (64%) create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/manager.ts delete mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/modal.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/presenter.ts create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/delegate.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/factory.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/reject.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/return.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/transfer.tsx rename frontend/packages/flow-pc/flow-pc-design/src/components/{design-editor/node-components => script/components}/action/icon.tsx (100%) create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/index.tsx rename frontend/packages/flow-pc/flow-pc-design/src/components/{design-editor/node-components => script/components}/action/script.tsx (100%) rename frontend/packages/flow-pc/flow-pc-design/src/components/{design-editor/node-components => script/components}/action/style.tsx (100%) create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/type.ts create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/modal/action-config-modal.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-custom.ts rename frontend/packages/flow-pc/flow-pc-design/src/components/{design-editor/node-components/action/index.ts => script/services/action.ts} (86%) rename frontend/packages/flow-pc/flow-pc-design/src/components/script/services/{error-trigger.ts => node-error-trigger.ts} (100%) rename frontend/packages/flow-pc/flow-pc-design/src/components/script/services/{router.ts => node-router.ts} (100%) create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/script.ts rename frontend/packages/{flow-pc/flow-pc-design/src/components/design-editor/typings/node-type.ts => flow-types/src/types/flow-design.ts} (98%) diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java b/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java index 39d9dc6f..9441b241 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java @@ -35,7 +35,7 @@ public RejectAction() { this.enable = true; this.type = ActionType.REJECT.name(); this.display = new ActionDisplay(this.title); - this.script = RejectActionScript.startScript(); + this.script = RejectActionScript.defaultScript(); } @Override diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/ScriptDefaultConstants.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/ScriptDefaultConstants.java index c5dcdd88..f5a9930e 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/ScriptDefaultConstants.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/ScriptDefaultConstants.java @@ -5,6 +5,27 @@ */ public class ScriptDefaultConstants { + /** + * 默认自定义动作脚本 + */ + public static final String SCRIPT_DEFAULT_ACTION_CUSTOM = """ + // @SCRIPT_TITLE 默认条件 触发通过 + def run(request){ + return 'PASS'; + } + """; + + + /** + * 默认拒绝动作脚本 + */ + public static final String SCRIPT_DEFAULT_ACTION_REJECT = """ + // @SCRIPT_TITLE 返回开始节点 + def run(request){ + return request.getStartNode().getId(); + } + """; + /** * 默认条件脚本 */ diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/RejectActionScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/RejectActionScript.java index 599f0429..dc2d4421 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/RejectActionScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/RejectActionScript.java @@ -1,5 +1,6 @@ package com.codingapi.flow.script.action; +import com.codingapi.flow.script.ScriptDefaultConstants; import com.codingapi.flow.script.runtime.ScriptRuntimeContext; import com.codingapi.flow.session.FlowSession; import lombok.AllArgsConstructor; @@ -12,28 +13,22 @@ @AllArgsConstructor public class RejectActionScript { - public static final String SCRIPT_START = "def run(session){return new com.codingapi.flow.script.action.RejectActionScript.RejectResult(session.getStartNode().getId())}"; - public static final String SCRIPT_TERMINATE = "def run(session){return new com.codingapi.flow.script.action.RejectActionScript.RejectResult(\"TERMINATE\")}"; + public static final String TYPE_TERMINATE = "TERMINATE"; + @Getter private final String script; public RejectResult execute(FlowSession session) { - return ScriptRuntimeContext.getInstance().run(script, RejectResult.class, session); + String result = ScriptRuntimeContext.getInstance().run(script, String.class, session); + return new RejectResult(result); } /** * 退回至发起节点 */ - public static RejectActionScript startScript() { - return new RejectActionScript(SCRIPT_START); - } - - /** - * 终止流程 - */ - public static RejectActionScript terminateScript() { - return new RejectActionScript(SCRIPT_TERMINATE); + public static RejectActionScript defaultScript() { + return new RejectActionScript(ScriptDefaultConstants.SCRIPT_DEFAULT_ACTION_REJECT); } @@ -58,7 +53,7 @@ public boolean isTerminate() { } public RejectResult(String result) { - if (result.equals("TERMINATE")) { + if (result.equals(TYPE_TERMINATE)) { this.type = RejectType.TERMINATE; } else { this.type = RejectType.RETURN_NODE; diff --git a/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/action/custom.tsx b/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/action/custom.tsx index af397948..79defc8c 100644 --- a/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/action/custom.tsx +++ b/frontend/packages/flow-pc/flow-pc-approval/src/components/flow-approval/components/action/custom.tsx @@ -5,6 +5,7 @@ import {useApprovalContext} from "@/components/flow-approval/hooks/use-approval- import {GroovyScriptConvertorUtil} from "@flow-engine/flow-core"; import {ActionFactory} from "@/components/flow-approval/components/action/factory"; import {ActionButton} from "@/components/flow-approval/components/action-button"; +import {ActionType} from "@flow-engine/flow-types"; /** * 自定义 @@ -24,7 +25,7 @@ export const CustomAction: React.FC = (props) => { const ActionView = ActionFactory.getInstance().render({ ...props.action, - type: triggerType, + type: triggerType as ActionType, }); if (ActionView) { 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 b0038edd..f3422f17 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 @@ -6,7 +6,7 @@ import {Button} from "antd"; import {nodeFormPanelFactory} from "@/components/design-editor/components/sidebar"; import {usePanelManager} from "@flowgram.ai/panel-manager-plugin"; import {useDesignContext} from "@/components/design-panel/hooks/use-design-context"; -import {NodeType} from "@/components/design-editor/typings/node-type"; +import {NodeType} from "@flow-engine/flow-types"; interface BranchAdderPropsType { activated?: boolean; diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-icon/index.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-icon/index.tsx index c0c4c23d..ef653fc1 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-icon/index.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-icon/index.tsx @@ -1,9 +1,20 @@ import React from "react"; -import {NodeType} from "@/components/design-editor/typings/node-type"; -import {Button, Flex, Space,Input,theme} from "antd"; -import {ApiOutlined, AuditOutlined, BellOutlined, BranchesOutlined, ClockCircleOutlined, CloseOutlined, EditOutlined, +import {NodeType} from "@flow-engine/flow-types"; +import {theme} from "antd"; +import { + ApiOutlined, + AuditOutlined, + BellOutlined, + BranchesOutlined, + ClockCircleOutlined, + EditOutlined, MergeOutlined, - NodeExpandOutlined, PoweroffOutlined, PullRequestOutlined, ShareAltOutlined, UserOutlined} from "@ant-design/icons"; + NodeExpandOutlined, + PoweroffOutlined, + PullRequestOutlined, + ShareAltOutlined, + UserOutlined +} from "@ant-design/icons"; interface NodeIconProps { diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-list/index.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-list/index.tsx index 24f6ef86..bb530d00 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-list/index.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/components/node-list/index.tsx @@ -4,7 +4,7 @@ import {FlowNodeRegistry} from "@/components/design-editor/typings"; import styled from 'styled-components'; import {FlowNodeRegistries} from "@/components/design-editor/nodes"; import {NodeIcon} from "@/components/design-editor/components/node-icon"; -import {NodeType} from "@/components/design-editor/typings/node-type"; +import {NodeType} from "@flow-engine/flow-types"; import {useDesignContext} from "@/components/design-panel/hooks/use-design-context"; const NodesWrap = styled.div` diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/table.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/index.tsx similarity index 64% rename from frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/table.tsx rename to frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/index.tsx index 7fced750..41318879 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/table.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/index.tsx @@ -1,11 +1,11 @@ import React from "react"; -import {Button, Form, Space, Switch,Popconfirm} from "antd"; +import {Button, Form, Popconfirm, Space, Switch} from "antd"; import {Table} from "@flow-engine/flow-pc-ui"; -import {ActionManager} from "@/components/design-editor/node-components/action/index"; import {useNodeRenderContext} from "@/components/design-editor/hooks/use-node-render-context"; import {PlusOutlined} from "@ant-design/icons"; -import {ActionModal} from "@/components/design-editor/node-components/action/modal"; -import {GroovyScriptConvertorUtil} from "@flow-engine/flow-core"; +import {actionOptions} from "@flow-engine/flow-types"; +import {ActionConfigModal} from "@/components/script/modal/action-config-modal"; +import {FlowActionListPresenter} from "./presenter"; interface ActionTableProps { value: any[]; @@ -15,11 +15,11 @@ interface ActionTableProps { export const ActionTable: React.FC = (props) => { const {node} = useNodeRenderContext(); const actions = node.getNodeRegistry()?.meta.actions || []; - const actionManager = new ActionManager(props.value, props.onChange); - const datasource = actionManager.getDatasource(actions); + const presenter = new FlowActionListPresenter(props.value, props.onChange); + const datasource = presenter.getDatasource(actions); const [visible, setVisible] = React.useState(false); - const [customVisible, setCustomVisible] = React.useState(false); const [form] = Form.useForm(); + const columns = React.useCallback(() => { return [ { @@ -33,6 +33,15 @@ export const ActionTable: React.FC = (props) => { dataIndex: 'title', key: 'title', }, + { + title: '类型', + dataIndex: 'type', + key: 'type', + render:(value:string) => { + const type = actionOptions.find(item=>item.value === value); + return type?.label + } + }, { title: '启用', dataIndex: 'enable', @@ -43,7 +52,7 @@ export const ActionTable: React.FC = (props) => { size="small" value={record.enable} onChange={(value) => { - actionManager.enable(record.id, value); + presenter.enable(record.id, value); }} /> ) @@ -59,21 +68,7 @@ export const ActionTable: React.FC = (props) => { { - const custom = record.type==='CUSTOM'; - let trigger = {}; - if(custom){ - const meta = GroovyScriptConvertorUtil.getScriptMeta(record.script); - trigger = JSON.parse(meta); - } - const data = { - ...record, - ...record.display, - ...trigger, - title: record.title, - id: record.id, - } - form.setFieldsValue(data); - setCustomVisible(custom); + form.setFieldsValue(record); setVisible(true); }} > @@ -83,7 +78,7 @@ export const ActionTable: React.FC = (props) => { { - actionManager.delete(record.id); + presenter.delete(record.id); }} > @@ -107,10 +102,9 @@ export const ActionTable: React.FC = (props) => { icon={} onClick={() => { form.resetFields(); - setCustomVisible(true); setVisible(true); }} - >添加按钮 + >自定义按钮 ]} columns={columns()} dataSource={datasource} @@ -119,17 +113,15 @@ export const ActionTable: React.FC = (props) => { }} pagination={false} /> - - { setVisible(false); }} onFinish={(values) => { - actionManager.update(values); + presenter.update(values); }} /> diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/manager.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/manager.ts new file mode 100644 index 00000000..7cbed8aa --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/manager.ts @@ -0,0 +1,23 @@ +import {actionOptions, FlowAction} from "@flow-engine/flow-types"; + +export class FlowActionManager { + private readonly data: FlowAction[]; + + public constructor(data: FlowAction[]) { + this.data = data; + } + + public getCurrentNodeActionOptions() { + const actions = this.data.filter(item => item.type !== "CUSTOM"); + const options: {label: string; value: string}[] = []; + for (const action of actions) { + const type = action.type; + const option = actionOptions.find(item => item.value === type); + if (option) { + options.push(option); + } + } + return options; + } + +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/modal.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/modal.tsx deleted file mode 100644 index 1e0485d1..00000000 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/modal.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import {Col, Form, FormInstance, Input, Modal, Row} from "antd"; -import React from "react"; -import {CustomScriptView} from "./script"; -import {ActionStyle} from "./style"; -import {ActionIcon} from "@/components/design-editor/node-components/action/icon"; - -interface ActionModalProps { - open: boolean; - onCancel: () => void; - form: FormInstance; - onFinish: (values: any) => void; - custom: boolean; - options: any[]; -} - - -export const ActionModal: React.FC = (props) => { - const custom = props.custom; - - return ( - { - props.form.submit(); - }} - > -
{ - props.onFinish(values); - props.onCancel(); - }} - > - - - - - - - - - - - - - - - - - - - {custom && ( - - )} - - -
-
- ) -} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/presenter.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/presenter.ts new file mode 100644 index 00000000..9ac3c0a4 --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/presenter.ts @@ -0,0 +1,94 @@ +import {actionOptions, ActionType, FlowAction} from "@flow-engine/flow-types"; +import {nanoid} from "nanoid"; +import {IdUtils} from "@/utils"; +import {FlowActionManager} from "./manager"; + +export class FlowActionListPresenter { + private readonly data: FlowAction[]; + private readonly onChange: (data: FlowAction[]) => void; + private readonly manager:FlowActionManager; + + public constructor(data: FlowAction[], onChange: (data: FlowAction[]) => void) { + this.onChange = onChange; + this.data = data; + this.manager = new FlowActionManager(data); + } + + public getFlowActionManager(){ + return this.manager; + } + + public enable(id: any, value: boolean) { + const data = this.data.map(item => { + if (item.id === id) { + return { + ...item, + enable: value, + } + } + return item; + }); + this.onChange(data); + } + + + public delete(id: string) { + const data = this.data.filter(item => item.id !== id); + this.onChange(data); + } + + public update(action: any) { + const actionId = action.id; + + if (actionId) { + const data = this.data.map(item => { + if (item.id === actionId) { + return { + ...item, + title: action.title, + display: { + ...action + } + } + } + return item; + }); + this.onChange(data); + } else { + const custom = { + ...action, + type: 'CUSTOM', + enable: true, + id: IdUtils.generateId(), + } + this.onChange([...this.data, custom]); + } + + } + + + public getDatasource(actions: ActionType[]): any[] { + if (this.data) { + return this.data.map(item => { + const enable = item.enable; + return { + ...item, + enable: enable === undefined ? true : enable, + } + }); + } + const list = actions.map(type => { + const title = actionOptions.filter(value => value.value === type)[0]?.label || '未命名操作'; + return { + id: nanoid(), + enable: true, + title: title, + type: type, + } as FlowAction; + }); + this.onChange(list); + return list; + } + + +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/header/index.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/header/index.tsx index d078031f..0048b932 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/header/index.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/header/index.tsx @@ -6,7 +6,7 @@ import {usePanelManager} from "@flowgram.ai/panel-manager-plugin"; import {Field, FieldRenderProps} from "@flowgram.ai/fixed-layout-editor"; import {CloseOutlined, EditOutlined} from "@ant-design/icons"; import {NodeIcon} from "@/components/design-editor/components/node-icon"; -import {NodeType} from "@/components/design-editor/typings/node-type"; +import {NodeType} from "@flow-engine/flow-types"; import {FlowNodeRegistry} from "@/components/design-editor/typings"; import {useNodeRenderContext} from "@/components/design-editor/hooks/use-node-render-context"; diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/taps/action.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/taps/action.tsx index c1100c58..50a41e10 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/taps/action.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/taps/action.tsx @@ -1,6 +1,6 @@ import React from "react"; import {Field, FieldRenderProps} from "@flowgram.ai/fixed-layout-editor"; -import {ActionTable} from "@/components/design-editor/node-components/action/table"; +import {ActionTable} from "@/components/design-editor/node-components/action"; export const TabAction: React.FC = () => { diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts index d6470f4b..f7669b2b 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts @@ -1,6 +1,5 @@ // Tab布局类型 -import {NodeType} from "@/components/design-editor/typings/node-type"; -import {FlowForm} from "@flow-engine/flow-types"; +import {FlowForm,NodeType} from "@flow-engine/flow-types"; export type TabPanelType = 'base' | 'form' | 'flow' | 'setting'; diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx new file mode 100644 index 00000000..abf3d47a --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx @@ -0,0 +1,13 @@ +import React from "react"; +import {ActionFormProps} from "@/components/script/typings"; +import { Row } from "antd"; + + +export const AddAuditActionForm:React.FC = (props)=>{ + + return ( + + add audit + + ) +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx new file mode 100644 index 00000000..f3a91b93 --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx @@ -0,0 +1,104 @@ +import React from "react"; +import {ActionFormProps, ActionSelectOption} from "@/components/script/typings"; +import {Col, Form, Row, Select} from "antd"; +import {GroovyCodeEditor} from "@/components/groovy-code"; +import {ActionCustomScriptUtils} from "@/components/script/services/action-custom"; + +interface CustomScriptProps { + value?: string; + onChange?: (value: string) => void; + options:ActionSelectOption[]; +} + +const CustomScript: React.FC = (props) => { + + const trigger = React.useMemo(() => { + if (props.value) { + return ActionCustomScriptUtils.getTrigger(props.value); + } + return undefined; + }, [props.value]); + + const handleChangeNodeType = (value: string) => { + const script = props.value; + if (script) { + const groovy = ActionCustomScriptUtils.update(value, script); + props.onChange?.(groovy); + } + } + + return ( +
+
+ 触发动作: + + + + + + + + + + + + + + + + + + + + + {FormAction && ( + + )} + + ) +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/script.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/script.tsx similarity index 100% rename from frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/script.tsx rename to frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/script.tsx diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/style.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/style.tsx similarity index 100% rename from frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/style.tsx rename to frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/style.tsx diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/type.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/type.ts new file mode 100644 index 00000000..e69de29b diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/modal/action-config-modal.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/modal/action-config-modal.tsx new file mode 100644 index 00000000..fbc827ec --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/modal/action-config-modal.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import {ActionModalProps} from "@/components/script/typings"; +import {Modal} from "antd"; +import {ActionForm} from "@/components/script/components/action"; + + +export const ActionConfigModal: React.FC = (props) => { + return ( + + { + props.onFinish(values); + props.onCancel(); + }} + /> + + ) +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/error-trigger-view.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/error-trigger-view.tsx index 46a0848e..3f49673d 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/error-trigger-view.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/error-trigger-view.tsx @@ -7,7 +7,7 @@ import {ViewBindPlugin} from "@flow-engine/flow-core"; import {SCRIPT_DEFAULT_ERROR_TRIGGER} from "@/components/script/default-script"; import {useNodeRouterManager} from "@/components/design-panel/hooks/use-node-router-manager"; import {useNodeRenderContext} from "@/components/design-editor/hooks/use-node-render-context"; -import {ErrorTriggerScriptUtils} from "@/components/script/services/error-trigger"; +import {ErrorTriggerScriptUtils} from "@/components/script/services/node-error-trigger"; import {useScriptMetaData} from "@/components/script/hooks/use-script-meta-data"; /** diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/router-view.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/router-view.tsx index 477db956..3e53bcd0 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/router-view.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/router-view.tsx @@ -6,7 +6,7 @@ import {GroovyScriptConvertorUtil} from "@flow-engine/flow-core"; import {Button, Form, Select, Space} from "antd"; import {CodeOutlined, ReloadOutlined} from "@ant-design/icons"; import {useNodeRouterManager} from "@/components/design-panel/hooks/use-node-router-manager"; -import {RouterScriptUtils} from "@/components/script/services/router"; +import {RouterScriptUtils} from "@/components/script/services/node-router"; import {useScriptMetaData} from "@/components/script/hooks/use-script-meta-data"; /** diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-custom.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-custom.ts new file mode 100644 index 00000000..1ee09e3e --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-custom.ts @@ -0,0 +1,31 @@ +import {GroovyScriptConvertorUtil} from "@flow-engine/flow-core"; + +export class ActionCustomScriptUtils { + + public static update(trigger: string, script: string) { + let groovy; + if (script) { + const returnData = GroovyScriptConvertorUtil.getReturnScript(script).trim(); + groovy = script.replace(returnData, `'${trigger}'`); + groovy = GroovyScriptConvertorUtil.updateScriptMeta(groovy, `{"trigger":"${trigger}"}`); + } else { + groovy = `// @CUSTOM_SCRIPT 自定义脚本,返回的数据为动作类型 + // @SCRIPT_META {"trigger":"${trigger}"} + def run(request){ + return '${trigger}'; + } + ` + } + return GroovyScriptConvertorUtil.formatScript(groovy); + } + + + public static getTrigger(script:string){ + const meta = GroovyScriptConvertorUtil.getScriptMeta(script); + const data = JSON.parse(meta); + if(data){ + return data.trigger; + } + return undefined; + } +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/index.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action.ts similarity index 86% rename from frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/index.ts rename to frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action.ts index fec7d339..4908ab9f 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/node-components/action/index.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action.ts @@ -1,17 +1,18 @@ -import {actionOptions, ActionType} from "@/components/design-editor/typings/node-type"; +import {actionOptions, ActionType} from "@flow-engine/flow-types"; import {nanoid} from "nanoid"; import {IdUtils} from "@/utils"; +import {FlowAction} from "@flow-engine/flow-types"; export class ActionManager { - private readonly data: any[]; - private readonly onChange: (data: any[]) => void; + private readonly data: FlowAction[]; + private readonly onChange: (data: FlowAction[]) => void; - public constructor(data: any[], onChange: (data: any[]) => void) { + public constructor(data: FlowAction[], onChange: (data: FlowAction[]) => void) { this.onChange = onChange; this.data = data; } - public getActionOptions() { + public getCurrentNodeActionOptions() { const actions = this.data.filter(item => item.type !== "CUSTOM"); const options:any[] = []; for (const action of actions) { @@ -90,7 +91,7 @@ export class ActionManager { enable: true, title: title, type: type, - } + } as FlowAction; }); this.onChange(list); return list; diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/error-trigger.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/node-error-trigger.ts similarity index 100% rename from frontend/packages/flow-pc/flow-pc-design/src/components/script/services/error-trigger.ts rename to frontend/packages/flow-pc/flow-pc-design/src/components/script/services/node-error-trigger.ts diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/router.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/node-router.ts similarity index 100% rename from frontend/packages/flow-pc/flow-pc-design/src/components/script/services/router.ts rename to frontend/packages/flow-pc/flow-pc-design/src/components/script/services/node-router.ts diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts new file mode 100644 index 00000000..520d6be0 --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts @@ -0,0 +1,21 @@ +import {FormInstance} from "antd"; +import {FlowActionManager} from "@/components/design-editor/node-components/action/manager"; + +export interface ActionModalProps { + open: boolean; + onCancel: () => void; + form: FormInstance; + onFinish: (values: any) => void; + manager: FlowActionManager; +} + +export interface ActionFormProps { + manager: FlowActionManager; + form: FormInstance; + onFinish: (values: any) => void; +} + +export interface ActionSelectOption { + label: string; + value: string; +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/index.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/index.ts index f86d5ada..8fea6cde 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/index.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/index.ts @@ -1,56 +1,2 @@ -import {DataType} from "@flow-engine/flow-types"; - -/** - * Groovy脚本类型枚举 - */ -export enum ScriptType { - /** 标题脚本 */ - TITLE = 'TITLE', - /** 条件脚本 */ - CONDITION = 'CONDITION', - /** 人员加载脚本 */ - OPERATOR_LOAD = 'OPERATOR_LOAD', - /** 流程创建人脚本 */ - OPERATOR_CREATE = 'OPERATOR_LOAD', - /** 异常触发脚本 */ - ERROR_TRIGGER = 'ERROR_TRIGGER', - /** 触发节点脚本 */ - TRIGGER = 'TRIGGER', - /** 路由节点脚本 */ - ROUTER = 'ROUTER', - /** 子流程节点脚本 */ - SUB_PROCESS = 'SUB_PROCESS', -} - -/** - * Groovy变量映射接口 - * 用于前后端变量映射统一 - */ -export interface GroovyVariableMapping { - /** 中文显示名称:如"当前操作人" */ - label: string; - - /** 变量展示名:如"request.getOperatorName()" */ - value: string; - - /** 数据类型 */ - type:DataType; - - /** Groovy表达式:如"${当前操作人}" */ - expression: string; - - /** 分组标签:如"操作人相关" */ - tag: string; - - /** 排序序号 */ - order: number; -} - -/** 变量分组标签枚举 */ -export enum VariableTag { - OPERATOR = '操作人相关', - WORKFLOW = '流程相关', - FORM_FIELD = '表单字段', - WORK_CODE = '流程编号', -} - +export * from "./script"; +export * from "./action"; \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/script.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/script.ts new file mode 100644 index 00000000..f86d5ada --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/script.ts @@ -0,0 +1,56 @@ +import {DataType} from "@flow-engine/flow-types"; + +/** + * Groovy脚本类型枚举 + */ +export enum ScriptType { + /** 标题脚本 */ + TITLE = 'TITLE', + /** 条件脚本 */ + CONDITION = 'CONDITION', + /** 人员加载脚本 */ + OPERATOR_LOAD = 'OPERATOR_LOAD', + /** 流程创建人脚本 */ + OPERATOR_CREATE = 'OPERATOR_LOAD', + /** 异常触发脚本 */ + ERROR_TRIGGER = 'ERROR_TRIGGER', + /** 触发节点脚本 */ + TRIGGER = 'TRIGGER', + /** 路由节点脚本 */ + ROUTER = 'ROUTER', + /** 子流程节点脚本 */ + SUB_PROCESS = 'SUB_PROCESS', +} + +/** + * Groovy变量映射接口 + * 用于前后端变量映射统一 + */ +export interface GroovyVariableMapping { + /** 中文显示名称:如"当前操作人" */ + label: string; + + /** 变量展示名:如"request.getOperatorName()" */ + value: string; + + /** 数据类型 */ + type:DataType; + + /** Groovy表达式:如"${当前操作人}" */ + expression: string; + + /** 分组标签:如"操作人相关" */ + tag: string; + + /** 排序序号 */ + order: number; +} + +/** 变量分组标签枚举 */ +export enum VariableTag { + OPERATOR = '操作人相关', + WORKFLOW = '流程相关', + FORM_FIELD = '表单字段', + WORK_CODE = '流程编号', +} + diff --git a/frontend/packages/flow-types/src/types/flow-approval.ts b/frontend/packages/flow-types/src/types/flow-approval.ts index eed2384e..b51f6514 100644 --- a/frontend/packages/flow-types/src/types/flow-approval.ts +++ b/frontend/packages/flow-types/src/types/flow-approval.ts @@ -1,3 +1,5 @@ +import {ActionType} from "@/types/flow-design"; + /** * 数据类型 */ @@ -97,7 +99,7 @@ export interface FlowAction { // 按钮名称 title: string; // 动作类型 - type: string; + type: ActionType; // 展示样式 display: FlowActionDisplay; // 是否启用 diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/typings/node-type.ts b/frontend/packages/flow-types/src/types/flow-design.ts similarity index 98% rename from frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/typings/node-type.ts rename to frontend/packages/flow-types/src/types/flow-design.ts index 157bb0dc..c5ebb15f 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-editor/typings/node-type.ts +++ b/frontend/packages/flow-types/src/types/flow-design.ts @@ -2,7 +2,7 @@ * 节点类型 */ export type NodeType = - // 审批 +// 审批 "APPROVAL" | // 分支控制 "CONDITION" | @@ -37,7 +37,7 @@ export type NodeType = * 操作类型 */ export type ActionType = - // 保存 +// 保存 'SAVE'| // 通过,流程继续往下流转 'PASS'| diff --git a/frontend/packages/flow-types/src/types/index.ts b/frontend/packages/flow-types/src/types/index.ts index 022f1d55..7ab035bd 100644 --- a/frontend/packages/flow-types/src/types/index.ts +++ b/frontend/packages/flow-types/src/types/index.ts @@ -1,3 +1,4 @@ +export * from './flow-design'; export * from './form-view'; export * from './form-instance'; export * from './flow-approval'; From 54324c7786108ed30b126dbf12a16c4fa8ebc453 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sun, 8 Mar 2026 09:25:17 +0800 Subject: [PATCH 3/7] update action --- .../com/codingapi/flow/action/actions/CustomAction.java | 2 +- .../com/codingapi/flow/script/action/CustomScript.java | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/CustomAction.java b/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/CustomAction.java index c54d737e..33738fad 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/CustomAction.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/CustomAction.java @@ -36,7 +36,7 @@ public CustomAction() { this.enable = true; this.type = ActionType.CUSTOM.name(); this.display = new ActionDisplay(this.title); - this.script = CustomScript.defaultCustomScript(); + this.script = CustomScript.defaultScript(); } @Override diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/CustomScript.java b/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/CustomScript.java index 6fb06e5d..21c3a1cd 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/CustomScript.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/script/action/CustomScript.java @@ -1,5 +1,6 @@ package com.codingapi.flow.script.action; +import com.codingapi.flow.script.ScriptDefaultConstants; import com.codingapi.flow.script.runtime.ScriptRuntimeContext; import com.codingapi.flow.session.FlowSession; import lombok.AllArgsConstructor; @@ -11,8 +12,6 @@ @AllArgsConstructor public class CustomScript { - public static final String SCRIPT_DEFAULT = "def run(session){return 'PASS'}"; - @Getter private final String script; @@ -26,8 +25,8 @@ public String execute(FlowSession session) { /** * 默认节点脚本 */ - public static CustomScript defaultCustomScript() { - return new CustomScript(SCRIPT_DEFAULT); + public static CustomScript defaultScript() { + return new CustomScript(ScriptDefaultConstants.SCRIPT_DEFAULT_ACTION_CUSTOM); } } From 66ee50ac20b7ac3255a0263b2004835372d01c8c Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sun, 8 Mar 2026 10:28:08 +0800 Subject: [PATCH 4/7] add action plugin --- .../com/codingapi/flow/node/BaseFlowNode.java | 1 + .../src/components/design-panel/types.ts | 43 ++++---- .../action/components/add-audit.tsx | 34 +++++- .../components/action/components/custom.tsx | 80 +------------- .../components/action/components/delegate.tsx | 38 ++++++- .../components/action/components/factory.tsx | 2 - .../components/action/components/reject.tsx | 16 ++- .../components/action/components/return.tsx | 12 --- .../components/action/components/transfer.tsx | 39 ++++++- .../script/modal/action-config-modal.tsx | 3 + .../script/plugins/action-custom-view-type.ts | 12 +++ .../script/plugins/action-reject-view-type.ts | 8 ++ .../plugins/view/action-custom-view.tsx | 80 ++++++++++++++ .../plugins/view/action-reject-view.tsx | 37 +++++++ .../plugins/view/operator-load-view.tsx | 5 +- .../script/services/action-reject.ts | 46 ++++++++ .../src/components/script/services/action.ts | 101 ------------------ 17 files changed, 330 insertions(+), 227 deletions(-) delete mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/return.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/action-custom-view-type.ts create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/action-reject-view-type.ts create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/action-custom-view.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/action-reject-view.tsx create mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts delete mode 100644 frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action.ts diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java index 7837ae96..02808a7a 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java @@ -147,6 +147,7 @@ public Map toMap() { map.put("id", id); map.put("name", name); map.put("type", getType()); + map.put("display",this instanceof IDisplayNode); map.put("order", String.valueOf(order)); if (this.blocks != null && !this.blocks.isEmpty()) { map.put("blocks", blocks.stream().map(IFlowNode::toMap).toList()); diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts index f7669b2b..9497f09f 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/design-panel/types.ts @@ -1,5 +1,5 @@ // Tab布局类型 -import {FlowForm,NodeType} from "@flow-engine/flow-types"; +import {FlowForm,NodeType,FlowAction} from "@flow-engine/flow-types"; export type TabPanelType = 'base' | 'form' | 'flow' | 'setting'; @@ -7,60 +7,57 @@ export type TabPanelType = 'base' | 'form' | 'flow' | 'setting'; export const LayoutHeaderHeight = 50; - export interface DesignPanelProps { // 流程编码 id?:string + // 是否开启 open: boolean; + // 关闭 onClose?: () => void; } // 流程配置 export interface Workflow { + // 设计id id: string; + // 流程名称 title: string; + // 流程编码 code: string; + // 流程表单 form: FlowForm; // 流程创建人脚本 operatorCreateScript:string; + // 流程策略 strategies?:any[]; + // 流程节点 nodes?:FlowNode[]; - edges?:FlowEdge[]; -} - -// 节点关系 -export interface FlowEdge { - from:string; - to:string; } -// 动作展示 -export interface ActionDisplay{ - title:string; - style:any; - icon:string; -} - -// 节点动作 -export interface FlowAction{ - id:string; - type:string; - title:string; - display:ActionDisplay; -} // 流程节点 export interface FlowNode{ + // 节点id id:string; + // 节点名称 name:string; + // 节点类型 type:NodeType; + // 节点优先级 order:number; + // 节点动作 actions:FlowAction[]; + // 节点策略 strategies:any[]; + // 节点条件块 blocks?:FlowNode[]; + // 节点表达式 script?:string; + // 节点视图 view?:string; + // 流程展示节点 + display?:boolean; } // 全局状态 diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx index abf3d47a..e546abda 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/add-audit.tsx @@ -1,13 +1,43 @@ import React from "react"; import {ActionFormProps} from "@/components/script/typings"; -import { Row } from "antd"; +import {Col, Form, Row} from "antd"; +import {OperatorLoadPluginView} from "@/components/script/plugins/view/operator-load-view"; +interface AddAuditInputProps{ + value?:string; + onChange?:(value:string) => void; +} + +const AddAuditInput:React.FC = (props)=>{ + + const script = props.value || ''; + + const handleChange = (value:string)=>{ + props.onChange && props.onChange(value); + } + + return ( + + ) +} + export const AddAuditActionForm:React.FC = (props)=>{ return ( - add audit + + + + + ) } \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx index f3a91b93..4a923357 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/custom.tsx @@ -1,79 +1,7 @@ import React from "react"; -import {ActionFormProps, ActionSelectOption} from "@/components/script/typings"; -import {Col, Form, Row, Select} from "antd"; -import {GroovyCodeEditor} from "@/components/groovy-code"; -import {ActionCustomScriptUtils} from "@/components/script/services/action-custom"; - -interface CustomScriptProps { - value?: string; - onChange?: (value: string) => void; - options:ActionSelectOption[]; -} - -const CustomScript: React.FC = (props) => { - - const trigger = React.useMemo(() => { - if (props.value) { - return ActionCustomScriptUtils.getTrigger(props.value); - } - return undefined; - }, [props.value]); - - const handleChangeNodeType = (value: string) => { - const script = props.value; - if (script) { - const groovy = ActionCustomScriptUtils.update(value, script); - props.onChange?.(groovy); - } - } - - return ( -
-
- 触发动作: - +
+ + +
+ ) +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/action-reject-view.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/action-reject-view.tsx new file mode 100644 index 00000000..05b98fbb --- /dev/null +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/action-reject-view.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import {ConditionRejectViewPlugin, VIEW_KEY} from "@/components/script/plugins/action-reject-view-type"; +import {ViewBindPlugin} from "@flow-engine/flow-core"; +import {useDesignContext} from "@/components/design-panel/hooks/use-design-context"; +import {RejectNodeManager} from "@/components/script/services/action-reject"; + + +const useActionRejectManager = () => { + const {state} = useDesignContext(); + const nodes = state.workflow.nodes || []; + return new RejectNodeManager(nodes); +} + + +export const ConditionRejectView: React.FC = (props) => { + + const rejectNodeManager = useActionRejectManager(); + const ConditionRejectViewComponent = ViewBindPlugin.getInstance().get(VIEW_KEY); + + if (ConditionRejectViewComponent) { + return ( + + ); + } + + return ( +
+ reject + {rejectNodeManager.getSize()} +
+ ) +} \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/operator-load-view.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/operator-load-view.tsx index fee53125..d03c6c90 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/operator-load-view.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/plugins/view/operator-load-view.tsx @@ -23,11 +23,12 @@ export const OperatorLoadPluginView: React.FC = (props) initialValues={{ ...data }} + layout="vertical" > { + handleChange(option); + }} + placeholder={"请选择拒绝退回到的节点"} + /> + ) } \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts index 323ecbfa..4214d97b 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts @@ -1,45 +1,69 @@ import {GroovyScriptConvertorUtil} from "@flow-engine/flow-core"; -import {FlowNode} from "@/components/design-panel/types"; +import {ActionSelectOption} from "@/components/script/typings"; +import {NodeManger} from "@/components/design-panel/manager/node"; +export class ActionRejectService { -export class RejectNodeManager { + private readonly nodeManger: NodeManger; - private readonly nodes:FlowNode[]; + public constructor(nodeManger: NodeManger) { + this.nodeManger = nodeManger; + } - constructor(nodes: FlowNode[]) { - this.nodes = nodes; + public getOptions(nodeId: string) { + const options: ActionSelectOption[] = []; + const backNodes = this.nodeManger.getBackNodes(nodeId); + for (const node of backNodes) { + options.push({ + label: node.name, + value: node.id, + }) + } + options.push({ + label: '终止流程', + value: 'TERMINATE', + danger: true, + }) + return options; + } + + public getValue(script?: string) { + const type = ActionRejectScriptUtils.getType(script); + const node = this.nodeManger.getNodeByType(type); + if (node) { + return node.id; + } + return type; } - public getSize(){ - return this.nodes.length; + public getScript(option: ActionSelectOption) { + return ActionRejectScriptUtils.update(option); } + } export class ActionRejectScriptUtils { - public static update(type: string, script: string) { - let groovy; - if (script) { - const returnData = GroovyScriptConvertorUtil.getReturnScript(script).trim(); - groovy = script.replace(returnData, `'${type}'`); - groovy = GroovyScriptConvertorUtil.updateScriptMeta(groovy, `{"type":"${type}"}`); - } else { - groovy = `// @CUSTOM_SCRIPT 自定义脚本,返回的数据为动作类型 - // @SCRIPT_META {"type":"${type}"} + public static update(option: ActionSelectOption) { + const groovy = `// @CUSTOM_SCRIPT 跳转到 ${option.label} + // @SCRIPT_META {"type":"${option.value}"} def run(request){ - return '${type}'; + return '${option.value}'; } ` - } return GroovyScriptConvertorUtil.formatScript(groovy); } - public static getType(script:string){ - const meta = GroovyScriptConvertorUtil.getScriptMeta(script); - const data = JSON.parse(meta); - if(data){ - return data.type; + public static getType(script?: string) { + if (script) { + const meta = GroovyScriptConvertorUtil.getScriptMeta(script); + if (meta) { + const data = JSON.parse(meta); + if (data) { + return data.type; + } + } } return undefined; } diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts index 520d6be0..59d0b469 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts @@ -2,6 +2,7 @@ import {FormInstance} from "antd"; import {FlowActionManager} from "@/components/design-editor/node-components/action/manager"; export interface ActionModalProps { + nodeId:string; open: boolean; onCancel: () => void; form: FormInstance; @@ -10,6 +11,7 @@ export interface ActionModalProps { } export interface ActionFormProps { + nodeId:string; manager: FlowActionManager; form: FormInstance; onFinish: (values: any) => void; @@ -18,4 +20,5 @@ export interface ActionFormProps { export interface ActionSelectOption { label: string; value: string; + danger?:boolean; } \ No newline at end of file From bec0199f4b713419136004775050b6c7c8f5d34b Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sun, 8 Mar 2026 11:29:41 +0800 Subject: [PATCH 6/7] update DESIGN.md --- designs/view-plugin/DESIGN.md | 46 ++++++++++++++++++- .../components/action/components/reject.tsx | 1 + .../{action-reject.ts => action-reject.tsx} | 1 - .../src/components/script/typings/action.ts | 3 +- 4 files changed, 47 insertions(+), 4 deletions(-) rename frontend/packages/flow-pc/flow-pc-design/src/components/script/services/{action-reject.ts => action-reject.tsx} (98%) diff --git a/designs/view-plugin/DESIGN.md b/designs/view-plugin/DESIGN.md index 99841ce3..28c3ea48 100644 --- a/designs/view-plugin/DESIGN.md +++ b/designs/view-plugin/DESIGN.md @@ -102,7 +102,7 @@ export interface TransferViewPlugin { } ``` -### 流程设计 +### 流程设计-节点配置 * 流程条件控制界面 ``` @@ -234,4 +234,48 @@ export interface TriggerViewPlugin { /** 确认回调 */ onChange: (script: string) => void; } +``` + +### 流程设计-动作配置 + +* 自定义按钮触发脚本界面 +``` +import {ActionSelectOption} from "@/components/script/typings"; + +export const VIEW_KEY = 'ConditionCustomViewPlugin'; + +export interface ConditionCustomViewPlugin { + // 当前的脚本 + value?: string; + // 脚本更改回掉 + onChange?: (value: string) => void; + // 可选择的动作范围 + options:ActionSelectOption[]; +} +``` + +* 拒绝动作界面 +``` +export const VIEW_KEY = 'ConditionRejectViewPlugin'; + +export interface ConditionRejectViewPlugin { + // 当前节点id + nodeId:string; + // 当前的脚本 + value?: string; + // 脚本更改回掉 + onChange?: (value: string) => void; +} +``` + +* 委派/转办/加签/界面 与(节点人员选择界面一致) +``` +export const VIEW_KEY = 'OperatorLoadViewPlugin'; + +export interface OperatorLoadViewPlugin { + /** 当前脚本 */ + script: string; + /** 确认回调 */ + onChange: (script: string) => void; +} ``` \ No newline at end of file diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/reject.tsx b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/reject.tsx index fffaf4b3..29c1938a 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/reject.tsx +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/components/action/components/reject.tsx @@ -12,6 +12,7 @@ export const RejectActionForm:React.FC = (props)=>{ diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.tsx similarity index 98% rename from frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts rename to frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.tsx index 4214d97b..0d7b98df 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/services/action-reject.tsx @@ -22,7 +22,6 @@ export class ActionRejectService { options.push({ label: '终止流程', value: 'TERMINATE', - danger: true, }) return options; } diff --git a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts index 59d0b469..759608ae 100644 --- a/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts +++ b/frontend/packages/flow-pc/flow-pc-design/src/components/script/typings/action.ts @@ -18,7 +18,6 @@ export interface ActionFormProps { } export interface ActionSelectOption { - label: string; + label: any; value: string; - danger?:boolean; } \ No newline at end of file From af6af525029db2e63bedb478fafacebd3b205da5 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Sun, 8 Mar 2026 11:41:54 +0800 Subject: [PATCH 7/7] update README.md --- README.md | 2 +- frontend/packages/flow-core/README.md | 108 ++++++-- .../flow-pc/flow-pc-approval/README.md | 253 +++++++++++++++++- .../packages/flow-pc/flow-pc-design/README.md | 210 +++++++++++---- .../packages/flow-pc/flow-pc-form/README.md | 161 ++++++++++- .../packages/flow-pc/flow-pc-ui/README.md | 125 ++++++++- frontend/packages/flow-types/README.md | 53 +++- 7 files changed, 783 insertions(+), 129 deletions(-) diff --git a/README.md b/README.md index b06b4727..7eeba77b 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ pnpm run dev:app-mobile ### 八层架构 1. **流程层** (Workflow Layer) - 流程定义层 -2. **节点层** (Node Layer) - 节点层(12种节点类型) +2. **节点层** (Node Layer) - 节点层(15种节点类型) 3. **动作层** (Action Layer) - 动作层(8种动作类型) 4. **记录层** (Record Layer) - 记录层 5. **会话层** (Session Layer) - 会话层 diff --git a/frontend/packages/flow-core/README.md b/frontend/packages/flow-core/README.md index 6e456844..d9e70d30 100644 --- a/frontend/packages/flow-core/README.md +++ b/frontend/packages/flow-core/README.md @@ -1,19 +1,20 @@ # @flow-engine/flow-core -Flow Engine 前端核心库,提供与后端 API 交互的基础功能。 +Flow Engine 前端核心框架库,提供 HTTP 客户端、Hooks、Presenter 等基础能力(不包含 UI 组件)。 ## 简介 -`flow-core` 是 Flow Engine 的核心前端库,包含: +`flow-core` 是 Flow Engine 的核心框架库,提供与 UI 无关的基础能力: -- HTTP 客户端封装 (基于 axios) -- API 服务接口定义 -- 通用类型和工具函数 +- HTTP 客户端封装(基于 axios) +- React Hooks 工具 +- Presenter 模式实现 +- 通用工具函数 +- 全局状态管理 -### 核心依赖 +### 依赖关系 -- `axios` - HTTP 客户端 -- `react` + `react-dom` - React 框架 +- **依赖**: 无 ## Setup @@ -39,25 +40,92 @@ pnpm run dev ## 核心功能 -### API 服务 +### HTTP 客户端 -提供与 Flow Engine 后端交互的 API 接口: +基于 axios 封装的 HTTP 客户端,提供: -- 流程定义 API -- 流程实例 API -- 待办/已办 API -- 流程操作 API +- 请求拦截器/响应拦截器 +- 统一的错误处理 +- 类型安全的请求/响应 +- 请求取消支持 -### HTTP 客户端 +```typescript +import { httpClient } from '@flow-engine/flow-core'; -基于 axios 封装的 HTTP 客户端,支持: +// GET 请求 +const workflow = await httpClient.get('/api/workflows/1'); -- 请求拦截 -- 响应拦截 -- 错误处理 -- 类型安全的请求/响应 +// POST 请求 +const result = await httpClient.post('/api/workflows', workflowData); +``` + +### React Hooks + +提供常用的业务 Hooks: + +- `useWorkflow` - 工作流相关操作 +- `useFlowRecord` - 流程记录相关操作 +- `useApproval` - 审批相关操作 +- 自定义 Hooks 工具 + +### Presenter 模式 + +实现业务逻辑与 UI 分离的 Presenter 模式: + +```typescript +import { Presenter } from '@flow-engine/flow-core'; + +class WorkflowPresenter extends Presenter { + async loadWorkflow(id: string): Promise { + // 加载工作流 + } + + async saveWorkflow(workflow: Workflow): Promise { + // 保存工作流 + } +} +``` + +### 工具函数 + +提供通用工具函数: + +- 日期格式化 +- 字符串处理 +- 对象深拷贝 +- 类型判断 + +## 模块结构 + +``` +flow-core/ +├── src/ +│ ├── http/ # HTTP 客户端 +│ ├── hooks/ # React Hooks +│ ├── presenter/ # Presenter 基类 +│ ├── utils/ # 工具函数 +│ └── types/ # 基础类型定义 +└── README.md +``` + +## 使用示例 + +```typescript +import { httpClient, useWorkflow, WorkflowPresenter } from '@flow-engine/flow-core'; + +// 使用 HTTP 客户端 +const workflows = await httpClient.get('/api/workflows'); + +// 使用 Hooks +const { data, loading, error } = useWorkflow('wf-001'); + +// 使用 Presenter +const presenter = new WorkflowPresenter(); +await presenter.loadWorkflow('wf-001'); +``` ## Learn more - [Rslib documentation](https://lib.rsbuild.io/) - Rslib 特性和 API - [Flow Engine Docs](https://github.com/codingapi/flow-engine) - 完整文档 +- [CLAUDE.md](../../CLAUDE.md) - 开发指南 diff --git a/frontend/packages/flow-pc/flow-pc-approval/README.md b/frontend/packages/flow-pc/flow-pc-approval/README.md index 84d85101..f3f11922 100644 --- a/frontend/packages/flow-pc/flow-pc-approval/README.md +++ b/frontend/packages/flow-pc/flow-pc-approval/README.md @@ -1,36 +1,261 @@ -# Rsbuild project +# @flow-engine/flow-pc-approval + +Flow Engine PC 端审批组件库,提供待办/已办/审批处理等功能。 + +## 简介 + +`flow-pc-approval` 是 Flow Engine PC 端的审批页面组件库,提供: + +- 待办列表(我的待办) +- 已办列表(我的已办) +- 我发起的(我的申请) +- 审批处理(审批操作) +- 流程详情查看 +- 审批记录展示 + +### 依赖关系 + +- **依赖**: `@flow-engine/flow-pc-design`, `@flow-engine/flow-pc-ui` ## Setup -Install the dependencies: +安装依赖: ```bash pnpm install ``` -## Get started +## 开发 -Start the dev server, and the app will be available at [http://localhost:3000](http://localhost:3000). +构建组件库: ```bash -pnpm run dev +pnpm run build ``` -Build the app for production: +监听模式构建: ```bash -pnpm run build +pnpm run dev ``` -Preview the production build locally: +## 核心功能 -```bash -pnpm run preview +### 待办列表 + +我的待办任务列表: + +- 任务筛选(按状态、类型、时间等) +- 任务搜索 +- 批量操作 +- 分页加载 + +```typescript +import { TodoList } from '@flow-engine/flow-pc-approval'; + + ``` -## Learn more +### 已办列表 + +我的已办任务列表: + +- 历史记录查询 +- 筛选和搜索 +- 查看审批详情 + +```typescript +import { DoneList } from '@flow-engine/flow-pc-approval'; + + +``` + +### 我发起的 + +我发起的流程列表: + +- 流程状态查看 +- 流程跟踪 +- 撤回/取消操作 + +```typescript +import { MyRequestsList } from '@flow-engine/flow-pc-approval'; + + +``` + +### 审批处理 + +审批操作组件: + +- 表单展示(只读/编辑) +- 审批意见填写 +- 审批操作(通过/拒绝/保存/转交/退回/委托) +- 附件上传 + +```typescript +import { ApprovalForm } from '@flow-engine/flow-pc-approval'; + + +``` -To learn more about Rsbuild, check out the following resources: +### 流程详情 + +流程详情查看: + +- 流程基本信息 +- 节点状态展示 +- 审批记录时间轴 +- 表单数据展示 + +```typescript +import { FlowDetail } from '@flow-engine/flow-pc-approval'; + + +``` + +### 审批记录 + +审批记录展示: + +- 时间轴展示 +- 审批人信息 +- 审批意见 +- 审批时间 +- 操作记录 + +```typescript +import { ApprovalTimeline } from '@flow-engine/flow-pc-approval'; + + +``` + +## 模块结构 + +``` +flow-pc-approval/ +├── src/ +│ ├── todo/ # 待办列表 +│ ├── done/ # 已办列表 +│ ├── my-requests/ # 我发起的 +│ ├── approval-form/ # 审批表单 +│ ├── flow-detail/ # 流程详情 +│ ├── timeline/ # 审批记录时间轴 +│ ├── components/ # 公共组件 +│ └── index.ts # 统一导出 +└── README.md +``` + +## 使用示例 + +### 待办页面 + +```typescript +import { TodoList, ApprovalModal } from '@flow-engine/flow-pc-approval'; + +const TodoPage = () => { + const [selectedTask, setSelectedTask] = useState(null); + const [showApprovalModal, setShowApprovalModal] = useState(false); + + const handleTaskClick = async (task) => { + setSelectedTask(task); + setShowApprovalModal(true); + }; + + return ( + <> + + {showApprovalModal && ( + setShowApprovalModal(false)} + onApproved={handleRefresh} + /> + )} + + ); +}; +``` + +### 审批操作 + +```typescript +import { ApprovalForm } from '@flow-engine/flow-pc-approval'; + +const MyApprovalForm = () => { + const handleSubmit = async (action, opinion) => { + await approvalApi.submit({ + taskId, + action, + opinion, + formData + }); + }; + + return ( + handleSubmit('PASS', opinion)} + onReject={(opinion) => handleSubmit('REJECT', opinion)} + onSave={(data) => handleSubmit('SAVE', data)} + onTransfer={(userId) => handleSubmit('TRANSFER', { userId })} + onReturn={(nodeId) => handleSubmit('RETURN', { nodeId })} + onDelegate={(userId) => handleSubmit('DELEGATE', { userId })} + /> + ); +}; +``` + +## 审批动作 + +支持以下审批动作: + +- `PASS` - 通过 +- `REJECT` - 拒绝 +- `SAVE` - 保存 +- `ADD_AUDIT` - 加签 +- `DELEGATE` - 委托 +- `RETURN` - 退回 +- `TRANSFER` - 转交 +- `CUSTOM` - 自定义动作 + +## Learn more -- [Rsbuild documentation](https://rsbuild.rs) - explore Rsbuild features and APIs. -- [Rsbuild GitHub repository](https://github.com/web-infra-dev/rsbuild) - your feedback and contributions are welcome! +- [Rslib documentation](https://lib.rsbuild.io/) - Rslib 特性和 API +- [Flow Engine Docs](https://github.com/codingapi/flow-engine) - 完整文档 +- [CLAUDE.md](../../../CLAUDE.md) - 开发指南 diff --git a/frontend/packages/flow-pc/flow-pc-design/README.md b/frontend/packages/flow-pc/flow-pc-design/README.md index e159353f..618ec17c 100644 --- a/frontend/packages/flow-pc/flow-pc-design/README.md +++ b/frontend/packages/flow-pc/flow-pc-design/README.md @@ -1,21 +1,21 @@ -# @flow-engine/flow-design +# @flow-engine/flow-pc-design -Flow Engine 流程设计器组件库,基于 @flowgram.ai fixed-layout-editor 构建。 +Flow Engine PC 端流程设计器组件库,提供节点配置、属性面板、脚本配置等功能。 ## 简介 -`flow-design` 是 Flow Engine 的核心前端组件库,提供可视化的流程设计能力。 +`flow-pc-design` 是 Flow Engine PC 端的流程设计器组件库,提供: -### 核心依赖 +- 流程设计器(可视化流程设计) +- 节点配置面板(15 种节点类型配置) +- 属性面板(节点属性编辑) +- 脚本配置器(Groovy 脚本编辑) +- 变量选择器 +- 策略配置组件 -- `@flowgram.ai/fixed-layout-editor` - 固定布局编辑器核心 -- `@flowgram.ai/fixed-semi-materials` - Semi Design 组件物料 -- `@flowgram.ai/form-materials` - 表单组件物料 -- `@flowgram.ai/panel-manager-plugin` - 面板管理插件 -- `@flowgram.ai/minimap-plugin` - 小地图插件 -- `@flowgram.ai/export-plugin` - 导出插件 -- `antd` - Ant Design 组件库 -- `@reduxjs/toolkit` + `react-redux` - 状态管理 +### 依赖关系 + +- **依赖**: `@flow-engine/flow-core`, `@flow-engine/flow-types`, `@flow-engine/flow-pc-ui` ## Setup @@ -39,76 +39,176 @@ pnpm run build pnpm run dev ``` -运行测试: +## 核心功能 -```bash -pnpm run test -``` +### 流程设计器 -## 核心功能 +可视化流程设计器,支持: + +- 节点拖拽添加 +- 节点连线 +- 节点配置 +- 流程预览 +- 流程校验 +- 流程导入/导出 -### 流程设计面板 +```typescript +import { WorkflowDesigner } from '@flow-engine/flow-pc-design'; + + +``` -`pages/design-panel/` 目录包含流程设计的核心组件: +### 节点配置 -- `types.ts` - TypeScript 类型定义 - - `Workflow` - 工作流定义 - - `FlowNode` - 节点定义 - - `FlowForm` - 表单定义 - - `FlowNode.blocks?: FlowNode[]` - 子节点(层次化结构) +支持 15 种节点类型的配置: -### 节点类型 +**基础节点 (9 种)**: +- `StartNode` - 开始节点 +- `EndNode` - 结束节点 +- `ApprovalNode` - 审批节点 +- `HandleNode` - 办理节点 +- `NotifyNode` - 通知节点 +- `RouterNode` - 路由节点 +- `SubProcessNode` - 子流程节点 +- `DelayNode` - 延迟节点 +- `TriggerNode` - 触发节点 -支持 15 种节点类型: +**块节点 (3 种)**: +- `ConditionNode` - 条件节点 +- `ParallelNode` - 并行节点 +- `InclusiveNode` - 包容节点 -**基础节点 (9种)**: StartNode, EndNode, ApprovalNode, HandleNode, NotifyNode, RouterNode, SubProcessNode, DelayNode, TriggerNode +**分支节点 (3 种)**: +- `ConditionBranchNode` - 条件分支 +- `ParallelBranchNode` - 并行分支 +- `InclusiveBranchNode` - 包容分支 -**块节点 (3种)**: ConditionNode, ParallelNode, InclusiveNode(包含子节点 blocks) +### 属性面板 -**分支节点 (3种)**: ConditionBranchNode, ParallelBranchNode, InclusiveBranchNode +节点属性编辑: -### 数据结构 +- 基本信息配置 +- 审批人配置 +- 表单权限配置 +- 通知配置 +- 超时策略配置 -#### 层次化节点结构 (Blocks) +### 脚本配置器 -使用 `blocks` 属性实现节点间的层次关系: +Groovy 脚本编辑支持: + +- 语法高亮 +- 代码补全 +- 语法检查 +- 变量提示 +- TypeScript/Groovy 转换 ```typescript -interface FlowNode { - id: string; - name: string; - type: NodeType; - blocks?: FlowNode[]; // 子节点列表 - // ... 其他属性 -} +import { ScriptEditor } from '@flow-engine/flow-pc-design'; + + ``` -#### 节点配置 +### 变量选择器 + +流程变量选择: + +- 表单字段变量 +- 流程实例变量 +- 系统内置变量 +- 自定义变量 ```typescript -interface FlowNode { - strategies?: NodeStrategy[]; // 节点策略 - actions?: FlowAction[]; // 节点动作 - // ... 其他属性 -} +import { VariablePicker } from '@flow-engine/flow-pc-design'; + + +``` + +### 策略配置 + +节点策略配置: + +- 多人审批策略(会签/或签) +- 超时策略 +- 通知策略 +- 权限策略 + +## 模块结构 + +``` +flow-pc-design/ +├── src/ +│ ├── designer/ # 流程设计器 +│ ├── node-config/ # 节点配置组件 +│ ├── property-panel/ # 属性面板 +│ ├── script/ # 脚本编辑器 +│ ├── variable/ # 变量选择器 +│ ├── strategy/ # 策略配置 +│ ├── components/ # 公共组件 +│ └── index.ts # 统一导出 +└── README.md ``` -## 开发指南 +## 使用示例 -### 添加新节点 +### 流程设计器 -1. 在 `types.ts` 中定义节点类型 -2. 创建对应的节点配置组件 -3. 注册到设计面板 +```typescript +import { WorkflowDesigner } from '@flow-engine/flow-pc-design'; +import type { Workflow } from '@flow-engine/flow-types'; + +const MyWorkflowDesigner = () => { + const [workflow, setWorkflow] = useState({ ... }); + + return ( + + ); +}; +``` -### 添加新策略 +### 脚本编辑器 -1. 扩展 `NodeStrategy` 类型 -2. 创建策略配置 UI 组件 -3. 集成到节点配置面板 +```typescript +import { ScriptEditor } from '@flow-engine/flow-pc-design'; +import type { ScriptType } from '@flow-engine/flow-pc-design'; + +const MyScriptEditor = () => { + const [script, setScript] = useState(''); + + return ( + { + setScript(prev => prev + variable); + }} + /> + ); +}; +``` ## Learn more - [Rslib documentation](https://lib.rsbuild.io/) - Rslib 特性和 API - [Flow Engine Docs](https://github.com/codingapi/flow-engine) - 完整文档 -- [CLAUDE.md](../../CLAUDE.md) - 开发指南 +- [CLAUDE.md](../../../CLAUDE.md) - 开发指南 diff --git a/frontend/packages/flow-pc/flow-pc-form/README.md b/frontend/packages/flow-pc/flow-pc-form/README.md index 84d85101..cf676f18 100644 --- a/frontend/packages/flow-pc/flow-pc-form/README.md +++ b/frontend/packages/flow-pc/flow-pc-form/README.md @@ -1,36 +1,169 @@ -# Rsbuild project +# @flow-engine/flow-pc-form + +Flow Engine PC 端表单组件库,提供表单设计器、表单渲染等功能。 + +## 简介 + +`flow-pc-form` 是 Flow Engine PC 端的表单相关组件库,提供: + +- 表单设计器(可视化表单设计) +- 表单渲染器(动态表单渲染) +- 表单字段组件 +- 表单验证规则 +- 表单权限控制 + +### 依赖关系 + +- **依赖**: `@flow-engine/flow-core`, `@flow-engine/flow-types` ## Setup -Install the dependencies: +安装依赖: ```bash pnpm install ``` -## Get started +## 开发 -Start the dev server, and the app will be available at [http://localhost:3000](http://localhost:3000). +构建组件库: ```bash -pnpm run dev +pnpm run build ``` -Build the app for production: +监听模式构建: ```bash -pnpm run build +pnpm run dev ``` -Preview the production build locally: +## 核心功能 -```bash -pnpm run preview +### 表单设计器 + +可视化表单设计器,支持: + +- 拖拽添加字段 +- 字段属性配置 +- 字段排序 +- 表单预览 +- 表单 schema 导出 + +```typescript +import { FormDesigner } from '@flow-engine/flow-pc-form'; + + ``` -## Learn more +### 表单渲染器 + +根据表单 schema 动态渲染表单: + +```typescript +import { FormRenderer } from '@flow-engine/flow-pc-form'; + + +``` + +### 表单字段 + +支持多种字段类型: + +- 单行文本 +- 多行文本 +- 数字 +- 日期 +- 下拉选择 +- 单选/多选 +- 人员选择 +- 部门选择 +- 附件上传 -To learn more about Rsbuild, check out the following resources: +### 表单验证 + +内置验证规则: + +- 必填验证 +- 长度限制 +- 格式验证(邮箱、手机号等) +- 自定义验证规则 + +### 表单权限 + +字段级别权限控制: + +- 可编辑 +- 只读 +- 隐藏 +- 必填 + +## 模块结构 + +``` +flow-pc-form/ +├── src/ +│ ├── designer/ # 表单设计器 +│ ├── renderer/ # 表单渲染器 +│ ├── fields/ # 表单字段组件 +│ ├── validator/ # 表单验证 +│ ├── schema/ # 表单 schema 定义 +│ └── index.ts # 统一导出 +└── README.md +``` + +## 使用示例 + +### 表单设计器 + +```typescript +import { FormDesigner } from '@flow-engine/flow-pc-form'; +import type { FlowForm } from '@flow-engine/flow-types'; + +const MyFormDesigner = () => { + const [schema, setSchema] = useState({ ... }); + + return ( + + ); +}; +``` + +### 表单渲染器 + +```typescript +import { FormRenderer } from '@flow-engine/flow-pc-form'; +import type { FlowForm, FormFieldPermission } from '@flow-engine/flow-types'; + +const MyFormRenderer = () => { + const [values, setValues] = useState({}); + + return ( + + ); +}; +``` + +## Learn more -- [Rsbuild documentation](https://rsbuild.rs) - explore Rsbuild features and APIs. -- [Rsbuild GitHub repository](https://github.com/web-infra-dev/rsbuild) - your feedback and contributions are welcome! +- [Rslib documentation](https://lib.rsbuild.io/) - Rslib 特性和 API +- [Flow Engine Docs](https://github.com/codingapi/flow-engine) - 完整文档 +- [CLAUDE.md](../../../CLAUDE.md) - 开发指南 diff --git a/frontend/packages/flow-pc/flow-pc-ui/README.md b/frontend/packages/flow-pc/flow-pc-ui/README.md index 84d85101..24c8f9b7 100644 --- a/frontend/packages/flow-pc/flow-pc-ui/README.md +++ b/frontend/packages/flow-pc/flow-pc-ui/README.md @@ -1,36 +1,133 @@ -# Rsbuild project +# @flow-engine/flow-pc-ui + +Flow Engine PC 端基础 UI 组件库,提供原子化 UI 组件。 + +## 简介 + +`flow-pc-ui` 是 Flow Engine PC 端的基础 UI 组件库,提供原子化的 UI 组件: + +- 基础输入组件(按钮、输入框、选择器等) +- 数据展示组件(表格、列表、卡片等) +- 反馈组件(弹窗、消息提示、加载等) +- 布局组件(容器、分割线、间距等) + +基于 Ant Design 组件库构建,提供统一的设计风格。 + +### 依赖关系 + +- **依赖**: 无 ## Setup -Install the dependencies: +安装依赖: ```bash pnpm install ``` -## Get started +## 开发 -Start the dev server, and the app will be available at [http://localhost:3000](http://localhost:3000). +构建组件库: ```bash -pnpm run dev +pnpm run build ``` -Build the app for production: +监听模式构建: ```bash -pnpm run build +pnpm run dev ``` -Preview the production build locally: +## 核心组件 + +### 基础输入组件 + +- `Button` - 按钮 +- `Input` - 输入框 +- `Select` - 选择器 +- `Checkbox` - 复选框 +- `Radio` - 单选框 +- `Switch` - 开关 +- `DatePicker` - 日期选择器 + +### 数据展示组件 + +- `Table` - 表格 +- `List` - 列表 +- `Card` - 卡片 +- `Descriptions` - 描述列表 +- `Timeline` - 时间轴 + +### 反馈组件 + +- `Modal` - 对话框 +- `Message` - 全局提示 +- `Notification` - 通知提醒框 +- `Spin` - 加载状态 +- `Progress` - 进度条 + +### 布局组件 + +- `Container` - 容器 +- `Divider` - 分割线 +- `Space` - 间距 +- `Grid` - 栅格布局 + +## 模块结构 -```bash -pnpm run preview +``` +flow-pc-ui/ +├── src/ +│ ├── button/ # 按钮组件 +│ ├── input/ # 输入框组件 +│ ├── select/ # 选择器组件 +│ ├── table/ # 表格组件 +│ ├── modal/ # 对话框组件 +│ └── index.ts # 统一导出 +└── README.md ``` -## Learn more +## 使用示例 + +```typescript +import { Button, Input, Modal } from '@flow-engine/flow-pc-ui'; + +// 使用按钮组件 + -To learn more about Rsbuild, check out the following resources: +// 使用输入框组件 + + +// 使用对话框组件 +Modal.confirm({ + title: '确认提交', + content: '确定要提交该流程吗?', + onOk: handleSubmit +}); +``` + +## 主题定制 + +支持通过 CSS 变量进行主题定制: + +```css +:root { + --primary-color: #1890ff; + --success-color: #52c41a; + --warning-color: #faad14; + --error-color: #f5222d; +} +``` + +## Learn more -- [Rsbuild documentation](https://rsbuild.rs) - explore Rsbuild features and APIs. -- [Rsbuild GitHub repository](https://github.com/web-infra-dev/rsbuild) - your feedback and contributions are welcome! +- [Rslib documentation](https://lib.rsbuild.io/) - Rslib 特性和 API +- [Flow Engine Docs](https://github.com/codingapi/flow-engine) - 完整文档 +- [CLAUDE.md](../../../CLAUDE.md) - 开发指南 diff --git a/frontend/packages/flow-types/README.md b/frontend/packages/flow-types/README.md index 4385d9cd..5766247e 100644 --- a/frontend/packages/flow-types/README.md +++ b/frontend/packages/flow-types/README.md @@ -1,14 +1,14 @@ # @flow-engine/flow-types -Flow Engine 前端类型定义库,提供整个项目的 TypeScript 类型系统。 +Flow Engine 前端 TypeScript 类型定义库,提供流程实例、表单、审批等业务类型定义。 ## 简介 -`flow-types` 是 Flow Engine 的类型定义库,为整个前端项目提供统一的类型系统。 +`flow-types` 是 Flow Engine 的类型定义库,为整个前端项目提供统一的业务类型系统。 -### 核心依赖 +### 依赖关系 -- `@flow-engine/flow-core` - 核心库类型引用 +- **依赖**: `@flow-engine/flow-core` ## Setup @@ -43,19 +43,19 @@ pnpm run dev - `FlowNode` - 节点基础类型 - `NodeType` - 节点类型枚举 -- 节点具体类型: `StartNode`, `EndNode`, `ApprovalNode`, `HandleNode`, 等 +- 节点具体类型:`StartNode`, `EndNode`, `ApprovalNode`, `HandleNode`, `NotifyNode`, `RouterNode`, `SubProcessNode`, `DelayNode`, `TriggerNode`, `ConditionNode`, `ParallelNode`, `InclusiveNode` 等 ### 策略类型 - `NodeStrategy` - 节点策略基础类型 - `WorkflowStrategy` - 工作流策略类型 -- 策略具体类型: `MultiOperatorAuditStrategy`, `TimeoutStrategy`, 等 +- 策略具体类型:`MultiOperatorAuditStrategy`, `TimeoutStrategy` 等 ### 动作类型 - `FlowAction` - 动作基础类型 - `ActionType` - 动作类型枚举 -- 动作具体类型: `PassAction`, `RejectAction`, `SaveAction`, 等 +- 动作具体类型:`PassAction`, `RejectAction`, `SaveAction`, `AddAuditAction`, `DelegateAction`, `ReturnAction`, `TransferAction`, `CustomAction` ### 表单类型 @@ -69,6 +69,11 @@ pnpm run dev - `FlowState` - 流程状态枚举 - `RecordState` - 记录状态枚举 +### 审批类型 + +- `ApprovalTask` - 审批任务 +- `ApprovalResult` - 审批结果 + ## 类型系统特点 ### 层次化节点结构 @@ -80,7 +85,7 @@ interface FlowNode { id: string; name: string; type: NodeType; - blocks?: FlowNode[]; // 子节点列表(块节点) + blocks?: FlowNode[]; // 子节点列表(块节点包含子节点) strategies?: NodeStrategy[]; actions?: FlowAction[]; } @@ -88,9 +93,35 @@ interface FlowNode { ### 节点分类 -- **基础节点** (9种): START, END, APPROVAL, HANDLE, NOTIFY, ROUTER, SUB_PROCESS, DELAY, TRIGGER -- **块节点** (3种): CONDITION, PARALLEL, INCLUSIVE(包含子节点) -- **分支节点** (3种): CONDITION_BRANCH, PARALLEL_BRANCH, INCLUSIVE_BRANCH +- **基础节点** (9 种): START, END, APPROVAL, HANDLE, NOTIFY, ROUTER, SUB_PROCESS, DELAY, TRIGGER +- **块节点** (3 种): CONDITION, PARALLEL, INCLUSIVE(包含子节点) +- **分支节点** (3 种): CONDITION_BRANCH, PARALLEL_BRANCH, INCLUSIVE_BRANCH + +## 使用示例 + +```typescript +import type { Workflow, FlowNode, NodeType } from '@flow-engine/flow-types'; + +// 定义一个工作流 +const workflow: Workflow = { + id: 'wf-001', + name: '请假审批流程', + nodes: [ + { + id: 'start-1', + name: '开始', + type: 'START' as NodeType + }, + { + id: 'approval-1', + name: '经理审批', + type: 'APPROVAL' as NodeType, + strategies: [...], + actions: [...] + } + ] +}; +``` ## Learn more