Commit a1e043c4 authored by 吴永生#A02208's avatar 吴永生#A02208

feat: flow组件优化拆分

parent 4af3d4d3
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56 * @Date: 2022-06-21 20:03:56
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-09 15:57:24 * @LastEditTime: 2022-07-12 11:51:17
* @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/interface.ts * @FilePath: /bkunyun/src/views/Project/ProjectSubmitWork/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -23,7 +23,8 @@ export interface IParameter { ...@@ -23,7 +23,8 @@ export interface IParameter {
tags: Array<string>; tags: Array<string>;
source: string; source: string;
productId: string; productId: string;
tasks: ITask[]; // tasks: ITask[];
isLine?: boolean;
validators: Array<IValidator>; validators: Array<IValidator>;
choices: Array<IChoice>; choices: Array<IChoice>;
error?: boolean; error?: boolean;
...@@ -93,7 +94,7 @@ export interface IEdge { ...@@ -93,7 +94,7 @@ export interface IEdge {
sourceHandle: string; sourceHandle: string;
target: string; target: string;
targetHandle: string; targetHandle: string;
label: string; label?: string;
} }
// 提交任务时的动态表单的数据结构 // 提交任务时的动态表单的数据结构
......
...@@ -20,16 +20,6 @@ ...@@ -20,16 +20,6 @@
border-left: 4px solid #ff4e4e; border-left: 4px solid #ff4e4e;
} }
.batchRotate {
transform: translateX(-50%) rotate(-90deg);
}
.flowNode {
background-color: #f5f6f7;
border-radius: 2px;
padding: 6px 12px;
}
.successDot { .successDot {
display: inline-block; display: inline-block;
line-height: 22px; line-height: 22px;
...@@ -45,3 +35,7 @@ ...@@ -45,3 +35,7 @@
border: 1px solid #1370ff; border: 1px solid #1370ff;
border-left: 4px solid #1370ff; border-left: 4px solid #1370ff;
} }
.batchRotate {
transform: translateX(-50%) rotate(-90deg);
}
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-12 11:20:29
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-12 11:28:57
* @FilePath: /bkunyun/src/views/Project/components/Flow/components/BatchNode.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { Tooltip } from "@mui/material";
import classNames from "classnames";
import { useMemo } from "react";
import { Handle, Position } from "react-flow-renderer";
import { uuid } from "@/utils/util";
import { IBatchNode } from "../../interface";
import styles from "./index.module.css";
/** 自定义batch节点 */
const BatchNode = (props: IBatchNode) => {
const { data } = props;
const {
style,
isFlowNode,
selectedStatus,
info: { title, isCheck, executionStatus, parameters },
} = data;
/** 获取输入参数数组 */
const inParamsArr = useMemo(() => {
return parameters.filter((item) => {
return item.parameterGroup === "in";
});
}, [parameters]);
/** 获取输出参数数组 */
const outParamsArr = useMemo(() => {
return parameters.filter((item) => {
return item.parameterGroup === "out";
});
}, [parameters]);
return (
<div
className={classNames({
[styles.batchNode]: true,
[styles.selectedBatchBox]: selectedStatus,
[styles.runBatchNode]: executionStatus === "Running",
[styles.doneBatchNode]: executionStatus === "Done",
[styles.errorBatchNode]: executionStatus === "Failed",
})}
style={style}
>
{inParamsArr?.length &&
inParamsArr.map((item, index) => {
return (
<Tooltip title={item.name} key={uuid()}>
<Handle
id={item.name}
style={{
background: "#fff ",
border: item.error
? "1px solid #FF4E4E"
: "1px solid #D1D6DE",
left: index * 20 + 20,
}}
type="target"
position={Position.Top}
/>
</Tooltip>
);
})}
<div
className={classNames({
[styles.batchRotate]: isFlowNode,
})}
>
{title || ""}
{isCheck && <span className={styles.successDot}></span>}
</div>
{outParamsArr?.length &&
outParamsArr.map((item, index) => {
return (
<Tooltip title={item.name} key={uuid()}>
<Handle
id={item.name}
style={{
background: "#fff ",
border: "1px solid #D1D6DE",
left: index * 20 + 20,
}}
type="source"
position={Position.Bottom}
/>
</Tooltip>
);
})}
</div>
);
};
export default BatchNode;
.flowNode {
background-color: #f5f6f7;
border-radius: 2px;
padding: 6px 12px;
}
.successDot {
display: inline-block;
line-height: 22px;
vertical-align: middle;
width: 8px;
height: 8px;
background-color: #0dd09b;
border-radius: 8px;
margin-left: 8px;
}
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-07-12 11:29:46
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-12 11:32:20
* @FilePath: /bkunyun/src/views/Project/components/Flow/components/FlowNode/index.tsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import classNames from "classnames";
import { Handle, Position } from "react-flow-renderer";
import { IExecutionStatus } from "@/views/Project/ProjectSubmitWork/interface";
import jobFail from "@/assets/project/jobFail.svg";
import jobRun from "@/assets/project/jobRun.svg";
import jobSue from "@/assets/project/jobSue.svg";
import styles from "./index.module.css";
/** 自定义flow节点 */
const FlowNode = (props: any) => {
/** 获取imgUrl */
const getImgUrl = (type: IExecutionStatus) => {
if (type === "Done") {
return jobSue;
}
if (type === "Failed") {
return jobFail;
}
if (type === "Running") {
return jobRun;
}
return undefined;
};
const { data } = props;
const {
dotStatus,
info: { title, isCheck, executionStatus },
} = data;
return (
<div
className={classNames({
[styles.flowNode]: true,
})}
>
{dotStatus?.isInput ? (
<Handle
style={{ background: "#C2C6CC ", left: 12 }}
type="target"
position={Position.Top}
/>
) : null}
<div style={{ display: "flex", alignItems: "center" }}>
{title || ""}
{isCheck && <span className={styles.successDot}></span>}
{getImgUrl(executionStatus) && (
<img
style={{ marginLeft: "6px" }}
src={getImgUrl(executionStatus)}
alt=""
/>
)}
</div>
{dotStatus?.isOutput ? (
<Handle
style={{ background: "#C2C6CC ", left: 12 }}
type="source"
position={Position.Bottom}
/>
) : null}
</div>
);
};
export default FlowNode;
...@@ -3,30 +3,18 @@ import ReactFlow, { ...@@ -3,30 +3,18 @@ import ReactFlow, {
Background, Background,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
Handle,
Position,
ReactFlowProps, ReactFlowProps,
Node, Node,
applyNodeChanges, Connection,
applyEdgeChanges,
EdgeChange,
NodeChange,
} from "react-flow-renderer"; } from "react-flow-renderer";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import classNames from "classnames";
import jobFail from "@/assets/project/jobFail.svg"; import { uuid } from "@/utils/util";
import jobRun from "@/assets/project/jobRun.svg"; import { IEdge, IParameter, ITask } from "../../ProjectSubmitWork/interface";
import jobSue from "@/assets/project/jobSue.svg"; import { ILine } from "./interface";
import { import BatchNode from "./components/BatchNode";
IEdge, import FlowNode from "./components/FlowNode";
IExecutionStatus,
ITask,
} from "../../ProjectSubmitWork/interface";
import { IBatchNode, ILine } from "./interface";
import styles from "./index.module.css";
import { Tooltip } from "@mui/material";
/* /*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-22 10:15:22 * @Date: 2022-06-22 10:15:22
...@@ -51,144 +39,6 @@ interface IProps extends ReactFlowProps { ...@@ -51,144 +39,6 @@ interface IProps extends ReactFlowProps {
onFlowNodeClick?: (val: string) => void; onFlowNodeClick?: (val: string) => void;
} }
/** 获取imgUrl */
const getImgUrl = (type: IExecutionStatus) => {
if (type === "Done") {
return jobSue;
}
if (type === "Failed") {
return jobFail;
}
if (type === "Running") {
return jobRun;
}
return undefined;
};
/** 自定义batch节点 */
const BatchNode = (props: IBatchNode) => {
const { data } = props;
const {
dotStatus,
style,
isFlowNode,
selectedStatus,
info: { title, isCheck, executionStatus, parameters },
} = data;
/** 获取输入参数数组 */
const inParamsArr = useMemo(() => {
return parameters.filter((item) => {
return item.parameterGroup === "in";
});
}, [parameters]);
/** 获取输出参数数组 */
const outParamsArr = useMemo(() => {
return parameters.filter((item) => {
return item.parameterGroup === "out";
});
}, [parameters]);
return (
<div
className={classNames({
[styles.batchNode]: true,
[styles.selectedBatchBox]: selectedStatus,
[styles.runBatchNode]: executionStatus === "Running",
[styles.doneBatchNode]: executionStatus === "Done",
[styles.errorBatchNode]: executionStatus === "Failed",
})}
style={style}
>
{inParamsArr?.length &&
inParamsArr.map((item, index) => {
return (
<Tooltip title={item.name}>
<Handle
// title={item.name}
style={{
background: "#fff ",
border: "1px solid #D1D6DE",
left: index * 20 + 20,
}}
type="target"
position={Position.Top}
/>
</Tooltip>
);
})}
<div
className={classNames({
[styles.batchRotate]: isFlowNode,
})}
>
{title || ""}
{isCheck && <span className={styles.successDot}></span>}
</div>
{outParamsArr?.length &&
outParamsArr.map((item, index) => {
return (
<Tooltip title={item.name}>
<Handle
style={{
background: "#fff ",
border: "1px solid #D1D6DE",
left: index * 20 + 20,
}}
type="source"
position={Position.Bottom}
/>
</Tooltip>
);
})}
</div>
);
};
/** 自定义flow节点 */
const FlowNode = (props: any) => {
const { data } = props;
const {
dotStatus,
info: { title, isCheck, executionStatus },
} = data;
return (
<div
className={classNames({
[styles.flowNode]: true,
})}
>
{dotStatus?.isInput ? (
<Handle
style={{ background: "#C2C6CC ", left: 12 }}
type="target"
position={Position.Top}
/>
) : null}
<div style={{ display: "flex", alignItems: "center" }}>
{title || ""}
{isCheck && <span className={styles.successDot}></span>}
{getImgUrl(executionStatus) && (
<img
style={{ marginLeft: "6px" }}
src={getImgUrl(executionStatus)}
alt=""
/>
)}
</div>
{dotStatus?.isOutput ? (
<Handle
style={{ background: "#C2C6CC ", left: 12 }}
type="source"
position={Position.Bottom}
id="a"
/>
) : null}
</div>
);
};
const Flow = (props: IProps) => { const Flow = (props: IProps) => {
const { const {
tasks, tasks,
...@@ -309,7 +159,6 @@ const Flow = (props: IProps) => { ...@@ -309,7 +159,6 @@ const Flow = (props: IProps) => {
type: item.type === "BATCH" ? "batchNode" : "flowNode", type: item.type === "BATCH" ? "batchNode" : "flowNode",
/** 每一项的数据 */ /** 每一项的数据 */
data: { data: {
// label: item.title || "",
info: item, info: item,
...(item.type === "BATCH" ...(item.type === "BATCH"
? { ? {
...@@ -321,10 +170,6 @@ const Flow = (props: IProps) => { ...@@ -321,10 +170,6 @@ const Flow = (props: IProps) => {
: inSideNodeId === item.id, : inSideNodeId === item.id,
} }
: {}), : {}),
// /** 是否选中 */
// isCheck: item.isCheck,
/** 运行状态 */
// executionStatus: item.executionStatus,
/** 输入输出圆点状态 */ /** 输入输出圆点状态 */
dotStatus: nodesInputAndOutputStatus(item.id), dotStatus: nodesInputAndOutputStatus(item.id),
...@@ -374,13 +219,11 @@ const Flow = (props: IProps) => { ...@@ -374,13 +219,11 @@ const Flow = (props: IProps) => {
val.push(newLine); val.push(newLine);
}, []); }, []);
}); });
return val.map((item: ILine) => { const a = val.map((item: ILine) => {
const newSelectId = selectedNodeId ? selectedNodeId : inSideNodeId; const newSelectId = selectedNodeId ? selectedNodeId : inSideNodeId;
return { return {
id: item.id, ...item,
source: item.source, // type: "smoothstep",
target: item.target,
type: "smoothstep",
...(item?.batchId === newSelectId ...(item?.batchId === newSelectId
? { style: { stroke: "#1370FF" }, animated: true } ? { style: { stroke: "#1370FF" }, animated: true }
: {}), : {}),
...@@ -389,6 +232,7 @@ const Flow = (props: IProps) => { ...@@ -389,6 +232,7 @@ const Flow = (props: IProps) => {
label: item.label ? `(${item.label})` : "", label: item.label ? `(${item.label})` : "",
}; };
}); });
return a;
}, [inSideNodeId, selectedNodeId, tasks]); }, [inSideNodeId, selectedNodeId, tasks]);
/** flowNode点击事件 */ /** flowNode点击事件 */
...@@ -421,9 +265,9 @@ const Flow = (props: IProps) => { ...@@ -421,9 +265,9 @@ const Flow = (props: IProps) => {
}; };
/** node节点 */ /** node节点 */
const [nodes, setNodes] = useNodesState(initialNodes); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
/** 连线数组 */ /** 连线数组 */
const [edges, setEdges] = useEdgesState(initialEdges); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
useEffect(() => { useEffect(() => {
setEdges(initialEdges); setEdges(initialEdges);
...@@ -433,20 +277,69 @@ const Flow = (props: IProps) => { ...@@ -433,20 +277,69 @@ const Flow = (props: IProps) => {
setNodes(initialNodes); setNodes(initialNodes);
}, [initialNodes, setNodes]); }, [initialNodes, setNodes]);
const onNodesChange = useCallback( /** 节点拖动停止 */
(changes: NodeChange[]) => { const onNodeDragStop = useCallback(
console.log(changes, "1111"); (event: React.MouseEvent, node: Node) => {
setNodes((ns) => applyNodeChanges(changes, ns)); const newVal =
(tasks?.length &&
tasks.map((item) => {
if (item.id === node.id) {
return {
...item,
position: node.position,
};
} else {
return item;
}
})) ||
[];
setTasks && setTasks(newVal);
}, },
[setNodes] [setTasks, tasks]
); );
const onEdgesChange = useCallback( const connectModifyParameters = useCallback(
(changes: EdgeChange[]) => { (parameters: IParameter[], edgeItem: Connection) => {
console.log(changes, "222222"); return parameters.map((item) => {
setEdges((es) => applyEdgeChanges(changes, es)); if (item.name === edgeItem.targetHandle) {
return { ...item, isLine: true };
} else {
return item;
}
});
},
[]
);
const onConnect = useCallback(
(val: Connection) => {
const newVal =
(tasks?.length &&
tasks?.map((item) => {
if (item.id === val.source) {
return {
...item,
edges: [
...item.edges,
{
...val,
id: uuid(),
},
],
};
} else if (item.id === val.target) {
return {
...item,
parameters: connectModifyParameters(item.parameters, val),
};
} else {
return item;
}
})) ||
[];
setTasks && setTasks(newVal as ITask[]);
}, },
[setEdges] [connectModifyParameters, setTasks, tasks]
); );
return ( return (
...@@ -456,8 +349,8 @@ const Flow = (props: IProps) => { ...@@ -456,8 +349,8 @@ const Flow = (props: IProps) => {
fitView={flowType === "default" ? true : false} fitView={flowType === "default" ? true : false}
onNodesChange={onNodesChange} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange} onEdgesChange={onEdgesChange}
deleteKeyCode={["13"]} onNodeDragStop={onNodeDragStop}
// onConnect={onConnect} onConnect={onConnect}
// proOptions={{ hideAttribution: true, account: "" }} // proOptions={{ hideAttribution: true, account: "" }}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
onPaneClick={handlePaneClick} onPaneClick={handlePaneClick}
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com * @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-23 11:00:29 * @Date: 2022-06-23 11:00:29
* @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com * @LastEditors: 吴永生#A02208 yongsheng.wu@wholion.com
* @LastEditTime: 2022-07-11 15:24:53 * @LastEditTime: 2022-07-12 00:26:48
* @FilePath: /bkunyun/src/views/Project/components/Flow/interface.ts * @FilePath: /bkunyun/src/views/Project/components/Flow/interface.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
...@@ -13,10 +13,12 @@ import { ITask } from "../../ProjectSubmitWork/interface"; ...@@ -13,10 +13,12 @@ import { ITask } from "../../ProjectSubmitWork/interface";
/** 线的参数 */ /** 线的参数 */
export interface ILine { export interface ILine {
id: string, id: string,
label: string, label?: string,
batchId?: string, batchId?: string,
source: string, source: string,
target: string, target: string,
sourceHandle: string,
targetHandle: string,
} }
export interface IDotStatus { export interface IDotStatus {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment