ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Superplane 组件自定义开发指南:基于 Mapper 注册表扩展工作流组件行为

Superplane 组件自定义开发指南:基于 Mapper 注册表扩展工作流组件行为 【免费下载链接】superplaneOpen source factory for one-shot engineering项目地址https://gitcode.com/gh_mirrors/su/superplane点击查看免费下载Superplane 的工作流画布由大量组件Component与触发器Trigger构成前端的组件映射Mapper体系通过一组注册表将这些组件的渲染逻辑、状态样式与自定义 UI 统一管理。本文基于 docs/contributing/component-customization.md 整理结合当前仓库源码完整讲解注册表体系的结构、六类注册类型、两个实战教程审批状态注册表、Wait 自定义字段渲染器以及为ComponentBase增加 Props 的全流程。读完本文你将能够为任意工作流组件添加自定义状态样式、自定义字段渲染器甚至从零创建一个全新的自定义组件并注册进画布。一、组件映射体系的目录结构该文档描述的核心目录在仓库中位于web_src/src/pages/app/mappers/注原文档写作时的目录名为web_src/src/pages/workflowv2/mappers/当前仓库已演进为app下文以实际路径为准。该目录集中了所有前端组件的映射实现web_src/src/pages/app/mappers/ ├── index.ts # 主注册表文件——所有注册都在这里发生 ├── types.ts # 所有自定义类型的 TypeScript 接口 ├── stateRegistry.ts # 默认状态注册表与回退状态逻辑 ├── default.ts # 默认触发器渲染器实现 ├── approval.ts # 审批组件自定义状态、数据构建器 ├── wait/ # Wait 组件自定义字段渲染器 │ ├── index.tsx │ └── expressionHelp.tsx ├── schedule.ts # 定时触发器自定义字段渲染器 ├── if.ts # If/条件组件映射器 ├── filter.ts # Filter 组件映射器 ├── timegate.ts # 时间门组件映射器 ├── noop.ts # 空操作组件映射器 ├── display.tsx # 展示组件映射器 ├── runner.tsx # Runner脚本执行组件映射器 ├── semaphore/ # Semaphore 应用专属映射器 └── github/、gitlab/、jira/、linear/ … # 各应用专属映射器目录各文件职责如下index.ts所有组件自定义的注册中枢任何新自定义都必须在此登记types.ts定义TriggerRenderer、ComponentBaseMapper、EventStateRegistry、CustomFieldRenderer等全部契约接口stateRegistry.ts提供大多数组件继承的默认状态逻辑defaultStateFunction与DEFAULT_STATE_REGISTRY各组件文件单个组件的具体实现与其特定自定义。二、六类注册表一览主注册表文件 index.ts 管理多类自定义注册。相比文档写作时的 6 类当前仓库已扩充为更细的 8 组注册对象文档中的 6 类核心类型全部保留注册表作用当前仓库中的注册对象组件基础映射器componentBaseMappers组件渲染逻辑与属性index.ts触发器渲染器triggerRenderers触发器展示与行为index.ts事件状态注册表eventStateRegistries组件状态逻辑与视觉样式index.ts自定义字段渲染器customFieldRenderers设置面板中附加 UIindex.ts应用专属映射器appMappers/appTriggerRenderers特定应用如semaphore.*、github.*组件index.ts应用专属状态/字段注册表应用组件的状态与自定义字段index.ts2.1 组件基础映射器Component Base Mappers以Recordstring, ComponentBaseMapper形式把组件类型名映射到各自的 mapper 实现。当前仓库中注册了noop、display、内存读写系列addMemory、readMemory等、if、loop、http、graphql、ssh、runner系列、timeGate、filter、forEach、wait、approval、merge、runApp、addRunError等。示例节选自 index.tsconst componentBaseMappers: Recordstring, ComponentBaseMapper { noop: noopMapper, display: displayMapper, if: ifMapper, loop: loopMapper, http: httpMapper, graphql: graphqlMapper, ssh: sshMapper, runner: runnerMapper, runnerJS: runnerMapper, timeGate: timeGateMapper, filter: filterMapper, forEach: forEachMapper, wait: waitMapper, approval: approvalMapper, merge: mergeMapper, runApp: runAppMapper, };2.2 触发器渲染器Trigger Renderers负责触发器节点在画布上的展示与交互。文档时期的注册仅github、schedule当前仓库已扩展为schedule、webhook、start、onBroadcast、onRun等const triggerRenderers: Recordstring, TriggerRenderer { schedule: scheduleTriggerRenderer, webhook: webhookTriggerRenderer, start: startTriggerRenderer, onBroadcast: onBroadcastTriggerRenderer, onRun: onRunTriggerRenderer, };TriggerRenderer接口types.ts要求实现三个方法getTriggerProps把后端节点/触发器元数据转换为TriggerProps、getRootEventValues根事件展示值、getTitleAndSubtitle标题与副标题并可选实现getEventState映射事件状态。未注册任何渲染器的触发器类型会自动回退到 default.ts 中的defaultTriggerRenderer。2.3 事件状态注册表Event State Registries状态注册表由stateMap状态到图标/颜色样式的映射与getState从执行信息推导状态的函数组成见 types.ts。当前注册的组件状态包括approval、http、graphql、ssh、runner系列、filter、forEach、if、loop、timeGate、wait、merge、runApp等完整列表见 index.ts。2.4 自定义字段渲染器Custom Field Renderers用于在组件设置面板中渲染额外 UI。文档时期注册schedule、wait两个当前仓库新增webhook并扩展出应用级appCustomFieldRenderersgrafana、newrelic、prometheus、dockerhub、incident、gcp、servicenowconst customFieldRenderers: Recordstring, CustomFieldRenderer { schedule: scheduleCustomFieldRenderer, wait: waitCustomFieldRenderer, webhook: webhookCustomFieldRenderer, };2.5 应用专属注册表App-specific Registries针对特定外部应用命名形如semaphore.something、github.something的组件与触发器采用双层Recordstring, Recordstring, ...结构按应用名分组。当前仓库中的应用覆盖规模远超文档时期包括cloudflare、cloudsmith、digitalocean、semaphore、github、gitlab、jira、linear、grafana、pagerduty、aws、azure、gcp、claude、openai、slack、sentry、datadog、prometheus等数十个完整清单见 index.ts。以semaphore为例其映射器集中在 semaphore/index.ts并在主注册表中导出const appMappers: Recordstring, Recordstring, ComponentBaseMapper { semaphore: semaphoreComponentMappers, github: githubComponentMappers, gitlab: gitlabComponentMappers, // ... };三、实战教程一为审批组件创建自定义状态注册表审批Approval组件是自定义状态逻辑的典范实现核心代码位于 approval.ts。3.1 定义自定义状态映射首先以DEFAULT_EVENT_STATE_MAP为基础扩展新状态。注意必须使用对象展开继承默认状态而不是整体替换export const APPROVAL_STATE_MAP: EventStateMap { ...DEFAULT_EVENT_STATE_MAP, // 继承默认状态 waiting: { icon: clock, textColor: text-gray-800, backgroundColor: bg-orange-100, badgeColor: bg-yellow-600, }, approved: { icon: circle-check, textColor: text-gray-800, backgroundColor: bg-green-100, badgeColor: bg-emerald-500, }, rejected: { icon: circle-x, textColor: text-gray-800, backgroundColor: bg-red-100, badgeColor: bg-red-400, }, };当前仓库的 approval.ts 在文档示例基础上进一步补充了errortriangle-alert图标、红色系与runningclock图标、琥珀色系两个状态值得参考。3.2 创建自定义状态函数状态函数接收一次节点执行ExecutionInfo并返回一个EventState。审批组件的逻辑是执行失败 →error取消 →cancelled取消中 →cancelling待审批 →waiting成功完成时依据执行元数据metadata.result区分approved/rejected默认按通过处理export const approvalStateFunction: StateFunction (execution: ExecutionInfo): EventState { // 错误状态——组件无法评估 if (execution.state STATE_FINISHED execution.result RESULT_FAILED) { return error; } // 等待状态——审批人尚未响应 if (execution.state STATE_PENDING || execution.state STATE_STARTED) { return waiting; } // 检查执行元数据中的审批决策 if (execution.state STATE_FINISHED execution.result RESULT_PASSED) { const metadata execution.metadata as Recordstring, any | undefined; if (metadata?.result approved) return approved; if (metadata?.result rejected) return rejected; return approved; // 默认视为成功 } return error; // 兜底 };当前仓库中的实现更为健壮approval.ts它先通过isApprovalExecutionError辅助函数检查resultReason如RESULT_REASON_ERROR来判定错误再依次处理RESULT_CANCELLED、STATE_CANCELLING等取消类状态最后才进入通过/拒绝判定。对比可知状态函数的演进方向是覆盖更多执行终态建议新组件实现时同样覆盖错误、取消、取消中、运行中与成功分支。3.3 组装状态注册表export const APPROVAL_STATE_REGISTRY: EventStateRegistry { stateMap: APPROVAL_STATE_MAP, getState: approvalStateFunction, };3.4 注册到主注册表在主注册表 index.ts 中登记const eventStateRegistries: Recordstring, EventStateRegistry { approval: APPROVAL_STATE_REGISTRY, // ... };四、实战教程二为 Wait 组件创建自定义字段渲染器Wait 组件在设置面板中展示模式相关的说明 UI实现位于 wait/index.tsx文档时期的wait.tsx已演进为目录结构含 expressionHelp.tsx 提供表达式环境与示例提示。4.1 实现 CustomFieldRendererexport const waitCustomFieldRenderer: CustomFieldRenderer { render: (node: NodeInfo) { const mode node.configuration?.mode as string; let content: string; let title: string; if (mode interval) { title Fixed Time Interval; content Component will wait for a fixed amount of time... Example expressions: {{ $.wait_time }} {{ $.wait_time 5 }}; } else if (mode countdown) { title Countdown to Date/Time; content Component will countdown until the provided date/time... Example expressions: {{ $.run_time }} {{ date($.date_string) }}; } else { title Wait Component; content Configure the wait mode to see more details.; } return ( div classNameborder-t-1 border-gray-200 pt-4 div classNamespace-y-3 div span classNametext-sm font-medium text-gray-700{title}:/span div classNametext-sm text-gray-800 mt-1 border-1 p-3 bg-gray-50 rounded-md font-mono whitespace-pre-line {content} /div /div /div /div ); }, };该渲染器根据node.configuration.modeinterval/countdown动态切换标题与内容向用户展示可在配置中使用的表达式语法如{{ $.wait_time }}、{{ date($.date_string) }}。自定义字段渲染器通常配合customFieldVisibility属性控制可见时机——例如 Wait 组件在主 mapper 中将其设为live-only表示仅在实时画布中展示wait/index.tsx。4.2 注册到主注册表const customFieldRenderers: Recordstring, CustomFieldRenderer { schedule: scheduleCustomFieldRenderer, wait: waitCustomFieldRenderer, webhook: webhookCustomFieldRenderer, };五、从零创建一个全新自定义组件以下完整流程对应文档中的mycomponent示例是新增组件时可直接套用的模板。5.1 创建组件文件在web_src/src/pages/app/mappers/下新建mycomponent.ts命名约定是文件名与组件类型名保持一致。必须从./types引入所需接口import { ComponentBaseMapper, ComponentBaseContext, ExecutionDetailsContext, EventStateRegistry, NodeInfo, SubtitleContext, CustomFieldRenderer, } from ./types; import { DEFAULT_EVENT_STATE_MAP } from /ui/componentBase/eventState; // 自定义状态映射可选 export const MY_COMPONENT_STATE_MAP { ...DEFAULT_EVENT_STATE_MAP, processing: { icon: loader, textColor: text-blue-800, backgroundColor: bg-blue-100, badgeColor: bg-blue-500, }, }; // 自定义状态函数可选 export const myComponentStateFunction (execution) { if (execution.metadata?.status processing) return processing; // ... 其他逻辑 return defaultStateFunction(execution); }; // 状态注册表可选 export const MY_COMPONENT_STATE_REGISTRY: EventStateRegistry { stateMap: MY_COMPONENT_STATE_MAP, getState: myComponentStateFunction, }; // 基础映射器必需 export const myComponentMapper: ComponentBaseMapper { props(context: ComponentBaseContext) { return { iconSlug: context.componentDefinition.icon || box, iconColor: text-blue-600, headerColor: bg-white, title: context.node.name || My Component, // ... 其他属性 }; }, subtitle(context: SubtitleContext) { return context.execution.metadata?.customMessage || Processing...; }, getExecutionDetails(context: ExecutionDetailsContext) { return context.execution.metadata || {}; }, }; // 自定义字段渲染器可选 export const myComponentCustomFieldRenderer: CustomFieldRenderer { render: (node: NodeInfo) { return ( div classNamep-4 pCustom configuration UI for {node.name}/p pre{JSON.stringify(node.configuration, null, 2)}/pre /div ); }, };ComponentBaseMapper接口types.ts要求实现三个成员props返回ComponentBaseProps驱动组件卡片渲染、subtitle组件副标题、getExecutionDetails执行详情面板数据。参考 approval.ts 可以看到更完整的实现props方法会读取context.lastExecutions[0]判断是否有历史执行、从context.node.metadata读取审批记录并交给getApprovalSpecs生成徽章规格getExecutionDetails则汇总开始时间、结束时间与逐条审批明细。5.2 注册到主注册表修改 index.ts在文件顶部与其他导入一起添加 import随后在对应注册表对象中登记import { myComponentMapper, MY_COMPONENT_STATE_REGISTRY, myComponentCustomFieldRenderer, } from ./mycomponent; // 加入注册表 const componentBaseMappers: Recordstring, ComponentBaseMapper { // ... 已有映射器 mycomponent: myComponentMapper, }; const eventStateRegistries: Recordstring, EventStateRegistry { // ... 已有注册表 mycomponent: MY_COMPONENT_STATE_REGISTRY, }; const customFieldRenderers: Recordstring, CustomFieldRenderer { // ... 已有渲染器 mycomponent: myComponentCustomFieldRenderer, };如果该组件属于某个特定应用则应按应用分组放入appMappers、appTriggerRenderers、appEventStateRegistries等双层结构中而不是顶层注册表。5.3 注册查找Lookup辅助当前仓库在 mapperLookup.ts 中实现了registerMapperLookups供组件实现内部互相引用如 approval、wait 中通过getTriggerRenderer、getState、getStateMap获取其他组件的渲染器与状态。新组件如需要在渲染逻辑中复用其他组件的状态或触发器渲染器同样应从./mapperLookup导入这些查询函数。六、最佳实践清单遵循命名约定组件文件名应与组件类型名一致扩展默认值始终通过展开运算符继承DEFAULT_EVENT_STATE_MAP而不是整体替换错误处理状态函数中务必包含错误状态与兜底回退参考defaultStateFunction的完整分支类型安全使用 types.ts 中定义的正式 TypeScript 类型性能将昂贵操作缓存在额外的数据构建器中如 approval 的getApprovalSpecs一致性遵循现有 UI 样式与交互模式Tailwind 颜色类、组件结构等。七、注册表辅助函数主注册表 index.ts 提供了若干查找辅助函数它们统一处理了命名空间解析形如github.create_issue的应用组件名会先按.切分取第一部分为应用名与默认回退逻辑函数位置行为getTriggerRenderer(name)index.ts获取触发器渲染器支持应用前缀未注册时回退defaultTriggerRenderergetComponentBaseMapper(name)index.ts获取组件映射器未注册时回退noopMappergetEventStateRegistry(name)index.ts获取状态注册表未注册时回退DEFAULT_STATE_REGISTRYgetStateMap(name)index.ts获取状态映射getState(name)index.ts获取状态函数getCustomFieldRenderer(name)index.ts获取自定义字段渲染器未注册返回undefinedgetExecutionDetails(...)index.ts获取组件执行详情值得注意的实现细节是这些函数返回的渲染器/映射器会经过 safeMappers.ts 中的createSafeComponentMapper、createSafeTriggerRenderer、createSafeCustomFieldRenderer包装捕获单个 mapper 抛出的异常避免单个组件渲染失败拖垮整个画布。这一防御性设计意味着新组件无需自行处理全局异常。此外getTriggerRenderer还通过withCustomName包装器将触发器的customName自定义名称注入事件标题展示。八、为 ComponentBase 添加新 Props当需要为组件增加新的视觉属性或行为时按三层结构同步修改。8.1 在 ComponentBaseProps 接口中新增 PropComponentBase是画布中组件卡片的通用 UI 容器定义在web_src/src/ui/componentBase/index.tsxexport interface ComponentBaseProps extends ComponentActionsProps { // ... 已有 props myCustomProp?: string; // 在此添加新 prop myCustomBehavior?: boolean; // 或按需添加多个 }8.2 在 ComponentBase 组件中使用在组件解构处接收新 prop并在 JSX 中消费export const ComponentBase: React.FCComponentBaseProps ({ // ... 已有 props myCustomProp, myCustomBehavior, // ... 其余 props }) { // 在组件逻辑或 JSX 中使用新 prop return ( div className{${myCustomBehavior ? custom-class : }} {myCustomProp span{myCustomProp}/span} {/* ... 组件其余部分 */} /div ); };8.3 更新各组件映射器在对应 mapper 的props方法中根据组件逻辑产出新 propexport const myComponentMapper: ComponentBaseMapper { props(context: ComponentBaseContext): ComponentBaseProps { return { // ... 已有 props myCustomProp: Custom value based on component logic, myCustomBehavior: context.lastExecutions.length 0, // ... 其余 props }; }, };8.4 完整示例新增状态徽章statusBadgePropStep 1接口中声明statusBadge对象类型export interface ComponentBaseProps extends ComponentActionsProps { // ... 已有 props statusBadge?: { text: string; color: string; }; }Step 2在组件 JSX 中条件渲染徽章export const ComponentBase: React.FCComponentBaseProps ({ // ... 已有 props statusBadge, }) { return ( div ComponentHeader /* ... */ / {statusBadge ( div className{px-2 py-1 text-xs font-semibold rounded ${statusBadge.color}} {statusBadge.text} /div )} {/* ... 组件其余部分 */} /div ); };Step 3在 mapper 中按执行状态动态生成徽章。参考 approval.ts 中props方法读取context.lastExecutions[0]的模式export const approvalMapper: ComponentBaseMapper { props(context: ComponentBaseContext): ComponentBaseProps { const lastExecution context.lastExecutions[0]; return { // ... 已有 props statusBadge: lastExecution?.state STATE_STARTED ? { text: Awaiting Approval, color: bg-orange-100 text-yellow-800 } : undefined, }; }, };九、快速参考路径为快速定位相关文件汇总如下主注册表web_src/src/pages/app/mappers/index.ts类型定义web_src/src/pages/app/mappers/types.ts查找辅助与安全包装web_src/src/pages/app/mappers/mapperLookup.ts、web_src/src/pages/app/mappers/safeMappers.ts默认状态逻辑web_src/src/pages/app/mappers/stateRegistry.ts默认触发器渲染器web_src/src/pages/app/mappers/default.ts自定义状态示例web_src/src/pages/app/mappers/approval.ts状态映射与状态函数见文件前部自定义字段示例web_src/src/pages/app/mappers/wait/index.tsx应用专属示例web_src/src/pages/app/mappers/semaphore/index.ts、web_src/src/pages/app/mappers/github/index.tsComponentBase UI 与 Props 接口web_src/src/ui/componentBase/index.tsx结语Superplane 的组件自定义体系以「注册表 映射器」为核心每个组件通过ComponentBaseMapper声明渲染行为通过EventStateRegistry定制状态样式通过CustomFieldRenderer扩展设置面板 UI最终统一在主注册表index.ts登记并由带默认回退与异常隔离的查找函数对外暴露。掌握这一套模式后无论是为现有组件补一个状态、加一段帮助 UI还是为新的外部应用接入整套组件与触发器都能沿着既有的架构路径快速落地。原文档与当前源码之间的差异目录更名、组件扩充、安全包装与查找层的引入也印证了这套体系仍在持续演进。赞分享【免费下载链接】superplaneOpen source factory for one-shot engineering项目地址https://gitcode.com/gh_mirrors/su/superplane点击查看免费下载相关推荐Element Plus自定义组件基于ElementPlus扩展开发Element Plus自定义组件基于ElementPlus扩展开发 引言 你是否曾经在使用Element Plus时遇到过这样的场景现有的组件无法完全满足前端UI组件React Suite自定义组件开发基于现有组件扩展功能React Suite自定义组件开发基于现有组件扩展功能 在React应用开发中我们经常需要根据业务需求对现有组件进行功能扩展。React SuiteRS前端UI组件bootstrap-vue自定义组件开发基于现有组件扩展新功能bootstrap vue自定义组件开发基于现有组件扩展新功能 在Web开发中我们经常需要根据项目需求定制UI组件。基于BootstrapVue的现有组件进前端UI组件上一篇TypeScript声明文件完全教程如何为JavaScript库编写类型定义下一篇终极iTop开源ITSM平台完整入门指南 - 快速部署与高效IT服务管理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表