深圳培训手机网站建设,网站建设的公司系统规划,网站开发语言和数据库有几种,重庆家政网站建设在上一篇博客中#xff0c;我们介绍了如何启动React Flow Renderer并创建一个基本的工作流界面。本文将进一步深入#xff0c;着重讨论如何构建一个可拖动的操作栏#xff0c;它是用户与工作流交互的入口之一。
引言
操作栏是工作流界面的一部分#xff0c;通常位于界面的…在上一篇博客中我们介绍了如何启动React Flow Renderer并创建一个基本的工作流界面。本文将进一步深入着重讨论如何构建一个可拖动的操作栏它是用户与工作流交互的入口之一。
引言
操作栏是工作流界面的一部分通常位于界面的一侧或顶部。它包含了用户可以从中拖拽节点到画布上的组件列表。在我们的示例中操作栏将位于界面的左侧。
创建操作栏组件
首先让我们看一下如何创建操作栏组件。在我们的示例中我们使用了React组件名为Slider。这个组件接收一个名为components的属性该属性包含了可用的组件列表。
// Slider/index.jsximport React from react;
//项目中自定义的手风琴组件请你使用自己项目中的组件
import { CustomAccordion } from /components/CustomeAccordion;// 模拟节点
const mockComponent [{name: clear alarm,type: ACTION,clazz: action.TbClearAlarmNode},{name: create alarm,type: ACTION,clazz: action.TbCreateAlarmNode},{name: device profile,type: ACTION,clazz: profile.TbDeviceProfileNode},{name: log,type: ACTION,clazz: action.TbLogNode},{name: message type switch,type: FILTER,clazz: filter.TbMsgTypeSwitchNode},{name: rpc call request,type: ACTION,clazz: rpc.TbSendRPCRequestNode},{name: rule chain,type: FLOW,clazz: flow.TbRuleChainInputNode},{name: save attributes,type: ACTION,clazz: telemetry.TbMsgAttributesNode},{name: save timeseries,type: ACTION,clazz: telemetry.TbMsgTimeseriesNode},{name: script,type: TRANSFORMATION,clazz: transform.TbTransformMsgNode}
];export enum RuleNodeType {FILTER FILTER,ENRICHMENT ENRICHMENT,TRANSFORMATION TRANSFORMATION,ACTION ACTION,EXTERNAL EXTERNAL,FLOW FLOW,UNKNOWN UNKNOWN,INPUT INPUT,
}export const ruleNodeTypeDescriptors new MapRuleNodeType, any([[RuleNodeType.FILTER,{value: RuleNodeType.FILTER,name: rulenode.type-filter,details: rulenode.type-filter-details,nodeClass: tb-filter-type,icon: filter_list}],[RuleNodeType.ENRICHMENT,{value: RuleNodeType.ENRICHMENT,name: rulenode.type-enrichment,details: rulenode.type-enrichment-details,nodeClass: tb-enrichment-type,icon: playlist_add}],[RuleNodeType.TRANSFORMATION,{value: RuleNodeType.TRANSFORMATION,name: rulenode.type-transformation,details: rulenode.type-transformation-details,nodeClass: tb-transformation-type,icon: transform}],[RuleNodeType.ACTION,{value: RuleNodeType.ACTION,name: rulenode.type-action,details: rulenode.type-action-details,nodeClass: tb-action-type,icon: flash_on}],[RuleNodeType.EXTERNAL,{value: RuleNodeType.EXTERNAL,name: rulenode.type-external,details: rulenode.type-external-details,nodeClass: tb-external-type,icon: cloud_upload}],[RuleNodeType.FLOW,{value: RuleNodeType.FLOW,name: rulenode.type-flow,details: rulenode.type-flow-details,nodeClass: tb-flow-type,icon: settings_ethernet}],[RuleNodeType.INPUT,{value: RuleNodeType.INPUT,name: rulenode.type-input,details: rulenode.type-input-details,nodeClass: tb-input-type,icon: input,special: true}],[RuleNodeType.UNKNOWN,{value: RuleNodeType.UNKNOWN,name: rulenode.type-unknown,details: rulenode.type-unknown-details,nodeClass: tb-unknown-type,icon: help_outline}]]
);const classMap new Map([[ACTION, relation-node],[input, input-node],[FILTER, filter-node],[ENRICHMENT, enrichment-node],[TRANSFORMATION, transformation-node],[EXTERNAL, external-node],[FLOW, flow-node]
]);// const allowType ruleNodeTypeComponentTypes;
const allowNodesClazz [telemetry.TbMsgAttributesNode,filter.TbMsgTypeSwitchNode,action.TbLogNode,rpc.TbSendRPCRequestNode,profile.TbDeviceProfileNode,telemetry.TbMsgTimeseriesNode,action.TbCreateAlarmNode,action.TbClearAlarmNode,flow.TbRuleChainInputNode,transform.TbTransformMsgNode
];export default function Slider() {const [allowType, setAllowType] React.useStateany([input]);const [allowedNodes, setAllowedNodes] React.useStateany([]);React.useEffect(() {// 将组件按名称进行排序const sortedComponents mockComponent?.sort((a: any, b: any) a.name?.localeCompare(b.name));// 过滤出符合条件的组件并拼接到allowedNodes数组中const filteredComponents sortedComponents?.filter((component: any) allowNodesClazz.includes(component.clazz)) || [];const updatedAllowedNodes [...filteredComponents];// 获取所有组件的类型并和allowType数组进行合并const updatedTypes updatedAllowedNodes.map((component) component.type);// 去除重复的节点并更新allowedNodes状态setAllowedNodes(Array.from(new Set(updatedAllowedNodes)));// 去除重复的类型并更新allowType状态如果为空数组则设置为默认值setAllowType(Array.from(new Set(updatedTypes)) || []);}, []);return (div classNamesider{allowType.map((type: any) //自定义手风琴项目中使用的是mui你可以使用其他组件库,这里就不贴出手风琴的代码了请你根据你的项目使用对应的组件。如果不需要手风琴组件。可以拥div来代替CustomAccordiontitle{ruleNodeTypeDescriptors.get(type as any)?.name as string}key{type}div classNamenodes{allowedNodes.filter((node: any) node.type type).map((x: any, i: number) divkey{${x.type}-${i}}className{sider-node ${classMap.get(x.type) || default-node}}onDragStart{(e) onDragStart(e, x)}draggablediv{x.name}/div{/* 黑色遮罩层 */}div classNameoverlay/div/div)}/div/CustomAccordion)}/div);
}
在上述代码中我们定义了一个Slider组件它将组件列表映射到可展开的自定义组件中并为每个组件添加了拖拽支持。
拖拽事件处理
拖拽操作栏的核心功能在于如何处理拖拽事件。在我们的示例中我们使用了onDragStart函数来处理节点拖拽开始事件。该函数会设置被拖拽的节点的类型和名称并记录被拖拽节点的完整信息。
/*** 处理节点拖拽开始事件的回调函数* param {Event} evt - 拖拽事件对象* param {Object} node - 被拖拽的节点对象*/
const onDragStart (evt: any, node: any) {// 记录被拖拽的节点类型和名称evt.dataTransfer.setData(application/reactflow,node.type , node.name);// 记录被拖拽的节点的完整信息evt.dataTransfer.setData(application/reactflownode, JSON.stringify(node));// 设置拖拽效果为移动evt.dataTransfer.effectAllowed move;
};这个函数会在用户拖拽节点时被触发并且会设置相关的数据以便后续在画布上放置节点时使用。
总结
通过创建一个可拖动的操作栏用户可以方便地将节点拖放到工作流画布上。在本文中我们了解了如何创建操作栏组件处理拖拽事件并将组件列表展示给用户。下一篇博客中我们将继续深入研究工作流界面的其他方面包括画布的交互性和节点的定制。敬请期待