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

feat: flow组件优化拆分

parent 4af3d4d3
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-21 20:03:56
* @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
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -23,7 +23,8 @@ export interface IParameter {
tags: Array<string>;
source: string;
productId: string;
tasks: ITask[];
// tasks: ITask[];
isLine?: boolean;
validators: Array<IValidator>;
choices: Array<IChoice>;
error?: boolean;
......@@ -93,7 +94,7 @@ export interface IEdge {
sourceHandle: string;
target: string;
targetHandle: string;
label: string;
label?: string;
}
// 提交任务时的动态表单的数据结构
......
......@@ -20,16 +20,6 @@
border-left: 4px solid #ff4e4e;
}
.batchRotate {
transform: translateX(-50%) rotate(-90deg);
}
.flowNode {
background-color: #f5f6f7;
border-radius: 2px;
padding: 6px 12px;
}
.successDot {
display: inline-block;
line-height: 22px;
......@@ -45,3 +35,7 @@
border: 1px 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, {
Background,
useNodesState,
useEdgesState,
Handle,
Position,
ReactFlowProps,
Node,
applyNodeChanges,
applyEdgeChanges,
EdgeChange,
NodeChange,
Connection,
} from "react-flow-renderer";
import { useCallback, useEffect, useMemo, useState } from "react";
import classNames from "classnames";
import jobFail from "@/assets/project/jobFail.svg";
import jobRun from "@/assets/project/jobRun.svg";
import jobSue from "@/assets/project/jobSue.svg";
import {
IEdge,
IExecutionStatus,
ITask,
} from "../../ProjectSubmitWork/interface";
import { IBatchNode, ILine } from "./interface";
import { uuid } from "@/utils/util";
import { IEdge, IParameter, ITask } from "../../ProjectSubmitWork/interface";
import { ILine } from "./interface";
import BatchNode from "./components/BatchNode";
import FlowNode from "./components/FlowNode";
import styles from "./index.module.css";
import { Tooltip } from "@mui/material";
/*
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-22 10:15:22
......@@ -51,144 +39,6 @@ interface IProps extends ReactFlowProps {
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 {
tasks,
......@@ -309,7 +159,6 @@ const Flow = (props: IProps) => {
type: item.type === "BATCH" ? "batchNode" : "flowNode",
/** 每一项的数据 */
data: {
// label: item.title || "",
info: item,
...(item.type === "BATCH"
? {
......@@ -321,10 +170,6 @@ const Flow = (props: IProps) => {
: inSideNodeId === item.id,
}
: {}),
// /** 是否选中 */
// isCheck: item.isCheck,
/** 运行状态 */
// executionStatus: item.executionStatus,
/** 输入输出圆点状态 */
dotStatus: nodesInputAndOutputStatus(item.id),
......@@ -374,13 +219,11 @@ const Flow = (props: IProps) => {
val.push(newLine);
}, []);
});
return val.map((item: ILine) => {
const a = val.map((item: ILine) => {
const newSelectId = selectedNodeId ? selectedNodeId : inSideNodeId;
return {
id: item.id,
source: item.source,
target: item.target,
type: "smoothstep",
...item,
// type: "smoothstep",
...(item?.batchId === newSelectId
? { style: { stroke: "#1370FF" }, animated: true }
: {}),
......@@ -389,6 +232,7 @@ const Flow = (props: IProps) => {
label: item.label ? `(${item.label})` : "",
};
});
return a;
}, [inSideNodeId, selectedNodeId, tasks]);
/** flowNode点击事件 */
......@@ -421,9 +265,9 @@ const Flow = (props: IProps) => {
};
/** node节点 */
const [nodes, setNodes] = useNodesState(initialNodes);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
/** 连线数组 */
const [edges, setEdges] = useEdgesState(initialEdges);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
useEffect(() => {
setEdges(initialEdges);
......@@ -433,20 +277,69 @@ const Flow = (props: IProps) => {
setNodes(initialNodes);
}, [initialNodes, setNodes]);
const onNodesChange = useCallback(
(changes: NodeChange[]) => {
console.log(changes, "1111");
setNodes((ns) => applyNodeChanges(changes, ns));
/** 节点拖动停止 */
const onNodeDragStop = useCallback(
(event: React.MouseEvent, node: Node) => {
const newVal =
(tasks?.length &&
tasks.map((item) => {
if (item.id === node.id) {
return {
...item,
position: node.position,
};
} else {
return item;
}
})) ||
[];
setTasks && setTasks(newVal);
},
[setTasks, tasks]
);
const connectModifyParameters = useCallback(
(parameters: IParameter[], edgeItem: Connection) => {
return parameters.map((item) => {
if (item.name === edgeItem.targetHandle) {
return { ...item, isLine: true };
} else {
return item;
}
});
},
[setNodes]
[]
);
const onEdgesChange = useCallback(
(changes: EdgeChange[]) => {
console.log(changes, "222222");
setEdges((es) => applyEdgeChanges(changes, es));
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 (
......@@ -456,8 +349,8 @@ const Flow = (props: IProps) => {
fitView={flowType === "default" ? true : false}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
deleteKeyCode={["13"]}
// onConnect={onConnect}
onNodeDragStop={onNodeDragStop}
onConnect={onConnect}
// proOptions={{ hideAttribution: true, account: "" }}
nodeTypes={nodeTypes}
onPaneClick={handlePaneClick}
......
......@@ -2,7 +2,7 @@
* @Author: 吴永生#A02208 yongsheng.wu@wholion.com
* @Date: 2022-06-23 11:00:29
* @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
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -13,10 +13,12 @@ import { ITask } from "../../ProjectSubmitWork/interface";
/** 线的参数 */
export interface ILine {
id: string,
label: string,
label?: string,
batchId?: string,
source: string,
target: string,
sourceHandle: string,
targetHandle: string,
}
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