Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
B
bkunyun
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
bkunyun
Commits
93b21ec8
Commit
93b21ec8
authored
Aug 05, 2022
by
chenshouchao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 新增批算子联调前提交
parent
86b7b820
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1226 additions
and
368 deletions
+1226
-368
api_manager.ts
src/api/api_manager.ts
+2
-0
project_api.ts
src/api/project_api.ts
+41
-1
index.module.css
...CommonComponents/RadioGroupOfButtonStyle/index.module.css
+3
-13
index.module.css
...s/CustomOperator/components/OperatorList/index.module.css
+17
-3
index.tsx
src/views/CustomOperator/components/OperatorList/index.tsx
+285
-336
index.tsx
src/views/CustomOperator/components/SaveOperator/index.tsx
+213
-0
index.tsx
src/views/CustomOperator/index.tsx
+155
-9
index.module.css
...onents/Flow/components/BatchOperatorFlow/index.module.css
+3
-0
index.tsx
...ct/components/Flow/components/BatchOperatorFlow/index.tsx
+440
-0
index.tsx
...ews/Project/components/Flow/components/FlowNode/index.tsx
+67
-6
No files found.
src/api/api_manager.ts
View file @
93b21ec8
...
...
@@ -41,6 +41,8 @@ const RESTAPI = {
API_SAVE_USERSPEC
:
`
${
BACKEND_API_URI_PREFIX
}
/cpp/workflow/saveuserspec`
,
// 保存用户自定义工作流模板
API_OVERVIEW_GET
:
`
${
BACKEND_API_URI_PREFIX
}
/cpp/basicInformation`
,
// 获取概览基本信息
API_TASK_OVERVIEW_LIST
:
`
${
BACKEND_API_URI_PREFIX
}
/cpp/workflowJobInformation`
,
// 查询任务概览
API_OPERATOR_LISTSTREAMACTORS
:
`
${
BACKEND_API_URI_PREFIX
}
/cpp/workflow/liststreamactors`
,
// 获取流算子列表,可用于模糊查询,返回所有版本流算子
API_SAVE_BATCHACTOR
:
`
${
BACKEND_API_URI_PREFIX
}
/cpp/workflow/savebatchactor`
,
// 保存批算子
};
export
default
RESTAPI
;
src/api/project_api.ts
View file @
93b21ec8
...
...
@@ -270,6 +270,44 @@ const getTaskOverview=(params:getTaskOverviewParams)=>{
})
}
// 获取流算子列表,可用于模糊查询,返回所有版本流算子
type
getOperatorListParams
=
{
productId
:
string
;
keyword
?:
string
;
page
?:
number
;
size
?:
number
;
};
const
getOperatorList
=
(
params
:
getOperatorListParams
)
=>
{
return
request
({
url
:
Api
.
API_OPERATOR_LISTSTREAMACTORS
,
method
:
"get"
,
params
,
})
}
type
saveBatchQuery
=
{
productId
:
string
;
batchName
:
string
;
batchVersion
:
string
;
description
?:
string
;
isEdit
?:
string
;
}
type
saveBatchBody
=
Array
<
any
>
type
saveBatchParams
=
{
query
:
saveBatchQuery
;
body
:
saveBatchBody
;
}
// 提交工作流
const
saveBatchActor
=
(
params
:
saveBatchParams
)
=>
{
return
request
({
url
:
Api
.
API_SAVE_BATCHACTOR
,
method
:
"post"
,
params
:
params
.
query
,
data
:
params
.
body
,
});
};
export
{
current
,
...
...
@@ -292,5 +330,7 @@ export {
submitWorkFlow
,
getworkFlowTaskInfo
,
getOverviewInfo
,
getTaskOverview
getTaskOverview
,
getOperatorList
,
saveBatchActor
};
src/components/CommonComponents/RadioGroupOfButtonStyle/index.module.css
View file @
93b21ec8
...
...
@@ -4,7 +4,7 @@
align-items
:
center
;
border
:
1px
solid
#e6e8eb
;
border-radius
:
4px
;
background-color
:
#
F0F2F
5
;
background-color
:
#
f0f2f
5
;
cursor
:
pointer
;
height
:
32px
;
box-sizing
:
border-box
;
...
...
@@ -12,7 +12,7 @@
}
.radio
{
position
:
relative
;
min-width
:
6
4
px
;
min-width
:
6
3
px
;
height
:
28px
;
box-sizing
:
border-box
;
font-size
:
14px
;
...
...
@@ -28,18 +28,8 @@
white-space
:
nowrap
;
}
.radio
:not
(
:last-child
)
::before
{
position
:
absolute
;
width
:
1px
;
height
:
16px
;
top
:
6px
;
right
:
0
;
content
:
''
;
background-color
:
#D1D6DE
;
}
.radioActive
{
color
:
#1370ff
;
background-color
:
#fff
;
box-shadow
:
2px
4px
12px
0px
rgba
(
0
,
27
,
63
,
0.06
);
box-shadow
:
2px
4px
12px
0px
rgba
(
0
,
27
,
63
,
0.06
);
}
src/views/CustomOperator/components/OperatorList/index.module.css
View file @
93b21ec8
...
...
@@ -38,22 +38,36 @@
}
.li
{
background-color
:
RGBA
(
240
,
242
,
245
,
1
);
padding
:
7px
7px
7px
28
px
;
padding
:
7px
9
px
;
color
:
rgba
(
30
,
38
,
51
,
1
);
font-size
:
14px
;
line-height
:
22px
;
margin-bottom
:
12px
;
position
:
relative
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.nameVersion
{
flex
:
1
;
/* text-align: left; */
margin-left
:
13px
;
word-wrap
:
break-word
;
/* text-overflow: clip; */
max-width
:
140px
;
}
.name
{
margin-right
:
8px
;
}
.version
{
white-space
:
nowrap
;
}
.icon
{
width
:
6px
;
height
:
10px
;
position
:
absolute
;
/*
position: absolute;
top: 13px;
left
:
9px
;
left: 9px;
*/
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
...
...
src/views/CustomOperator/components/OperatorList/index.tsx
View file @
93b21ec8
import
{
InputBase
,
IconButton
}
from
"@mui/material"
;
import
SearchIcon
from
"@mui/icons-material/Search"
;
import
styles
from
"./index.module.css"
;
import
cloneDeep
from
"lodash/cloneDeep"
;
import
{
getScrollLoader
}
from
"@/utils/util"
;
import
{
useState
}
from
"react"
;
import
{
useState
,
useCallback
,
useEffect
}
from
"react"
;
import
{
getOperatorList
}
from
"@/api/project_api"
;
import
useMyRequest
from
"@/hooks/useMyRequest"
;
import
{
useStores
}
from
"@/store"
;
import
{
toJS
}
from
"mobx"
;
import
{
ITask
}
from
"@/views/Project/ProjectSubmitWork/interface"
;
const
OperatorList
=
()
=>
{
const
[
name
,
setName
]
=
useState
(
""
);
const
nameChange
=
(
e
:
any
)
=>
{
setName
(
e
.
target
.
value
);
};
const
list
=
[
{
name
:
"这是中文"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
},
{
name
:
"asdf"
,
version
:
"1.0.0"
,
let
count
=
1
;
type
IProps
=
{
operatorList
:
any
;
setOperatorList
:
any
;
};
let
isInRequest
=
false
;
const
OperatorList
=
(
props
:
IProps
)
=>
{
const
{
operatorList
,
setOperatorList
}
=
props
;
// 流程图中流算子列表
const
{
currentProjectStore
}
=
useStores
();
const
[
list
,
setList
]
=
useState
<
ITask
[]
>
([]);
// 算子列表
const
productId
=
toJS
(
currentProjectStore
.
currentProductInfo
.
id
);
const
[
keyword
,
setKeyword
]
=
useState
(
""
);
// 搜索算子列表时的关键词
const
[
dragItem
,
setDragItem
]
=
useState
<
any
>
({});
// 拖拽的算子
const
[
page
,
setPage
]
=
useState
(
0
);
//
const
[
isLastPage
,
setIsLastPage
]
=
useState
(
false
);
const
{
run
:
getOperatorListRun
}
=
useMyRequest
(
getOperatorList
,
{
onSuccess
:
(
res
)
=>
{
isInRequest
=
false
;
console
.
log
(
"res"
,
res
);
let
arr
=
res
?.
data
?.
content
;
setIsLastPage
(
res
?.
data
?.
last
);
arr
.
forEach
((
item
:
any
,
index
:
number
)
=>
{
item
.
edges
=
[];
item
.
parameters
.
push
({
choices
:
[],
classType
:
"STRING"
,
classTypeName
:
"String"
,
defaultValue
:
null
,
description
:
""
,
domType
:
"INPUT"
,
hidden
:
false
,
isnull
:
false
,
level
:
null
,
linked
:
false
,
many
:
false
,
name
:
`in
${
index
+
1
}
`
,
order
:
0
,
parameterGroup
:
"in"
,
promoted
:
false
,
promotedName
:
null
,
required
:
false
,
title
:
null
,
validators
:
[],
});
});
arr
.
forEach
((
item
:
any
,
index
:
number
)
=>
{
item
.
parameters
.
push
({
choices
:
[],
classType
:
"STRING"
,
classTypeName
:
"String"
,
defaultValue
:
null
,
description
:
""
,
domType
:
"INPUT"
,
hidden
:
false
,
isnull
:
false
,
level
:
null
,
linked
:
false
,
many
:
false
,
name
:
`out
${
index
+
1
}
`
,
order
:
0
,
parameterGroup
:
"out"
,
promoted
:
false
,
promotedName
:
null
,
required
:
false
,
title
:
null
,
validators
:
[],
});
});
setList
(
arr
);
// setList(res?.data?.content || []);
},
onError
:
()
=>
{
isInRequest
=
false
;
},
});
const
getOperatorListFun
=
useCallback
(
(
keyword
:
string
=
""
,
page
=
0
)
=>
{
if
(
isInRequest
)
{
return
;
}
else
{
getOperatorListRun
({
productId
:
productId
as
string
,
keyword
,
page
,
size
:
5
,
});
}
},
];
[
productId
,
getOperatorListRun
]
);
window
.
onscroll
=
(
e
)
=>
{
console
.
log
(
e
);
useEffect
(()
=>
{
getOperatorListFun
();
},
[
getOperatorListFun
]);
const
handleKeywordKeyUp
=
(
e
:
any
)
=>
{
if
(
e
.
keyCode
===
13
)
{
setKeyword
(
e
.
target
.
value
);
setPage
(
0
);
getOperatorListFun
(
e
.
target
.
value
,
0
);
}
};
// const list = [
// {
// title: "这是中文",
// version: "1.0.0",
// },
// {
// allVersions: ["1.0.0", "2.0.0"],
// creator: "root",
// description: "这是一段Hitlist算子的描述",
// edges: [
// {
// id: "fdd26229-3a1f-46e7-a14a-dd55bcce55e4",
// label: null,
// source: "62c7965c9bf7ba39f2dcefcc",
// sourceHandle: null,
// target: "62c7965c9bf7ba39f2dcefce",
// targetHandle: null,
// },
// ],
// id: "62c7965c9bf7ba39f2dcefcc",
// parameters: [
// {
// name: "cpus",
// classType: "INT",
// classTypeName: "Int",
// required: false,
// defaultValue: "1",
// },
// ],
// // parentNode: "62c7965c9bf7ba39f2dcefc8",
// position: { x: 40, y: 200 },
// productId: "cadd",
// status: null,
// title: "Hitlist",
// type: "FLOW",
// version: "2.0.0",
// },
// {
// title: "asdf",
// version: "1.0.0",
// },
// {
// title: "asdf",
// version: "1.0.0",
// },
// {
// title: "asdf",
// version: "1.0.0",
// },
// ];
const
handleScroll
=
(
e
:
any
)
=>
{
console
.
log
(
e
);
if
(
getScrollLoader
(
e
))
{
console
.
log
(
"加载"
);
console
.
log
(
"加载1"
);
if
(
isInRequest
||
isLastPage
)
{
return
;
}
else
{
console
.
log
(
"加载2"
);
getOperatorListFun
(
keyword
,
page
+
1
);
setPage
(
page
+
1
);
}
}
};
...
...
@@ -362,6 +199,107 @@ const OperatorList = () => {
);
};
/** 拖拽开始 */
const
onDragStart
=
(
item
:
any
)
=>
{
setDragItem
(
item
);
console
.
log
(
"onDragStart"
);
// setIsDragStyle(true);
count
++
;
};
/** 生成批流副本 */
const
getNewOperatorItem
=
useCallback
(
(
task
:
ITask
,
x
:
number
,
y
:
number
)
=>
{
const
newEdges
=
task
?.
edges
&&
task
?.
edges
.
map
((
every
)
=>
{
return
{
...
every
,
source
:
`
${
every
.
source
}
_
${
count
}
`
,
target
:
`
${
every
.
target
}
_
${
count
}
`
,
};
});
return
{
...
task
,
id
:
`
${
task
.
id
}
_
${
count
}
`
,
position
:
{
x
:
x
,
y
:
y
,
},
edges
:
newEdges
.
length
?
newEdges
:
task
?.
edges
,
};
// const newVal = arr.map((item) => {
// const newEdges =
// item?.edges &&
// item?.edges.map((every) => {
// return {
// ...every,
// source: `${every.source}_${count}`,
// target: `${every.target}_${count}`,
// };
// });
// return {
// ...item,
// id: `${item.id}_${count}`,
// parentNode: item.parentNode ? `${item.parentNode}_${count}` : "",
// position: {
// x: item.position?.x,
// y: item.position?.y,
// },
// edges: newEdges.length ? newEdges : item?.edges,
// };
// });
// return newVal;
},
[]
);
/** 拖拽结束 */
const
onDragEnd
=
useCallback
(
(
e
:
React
.
DragEvent
<
HTMLDivElement
>
)
=>
{
const
dom
=
document
.
getElementById
(
"customOperatorFlow"
);
console
.
log
(
e
);
console
.
log
(
dom
);
const
clientX
=
e
.
clientX
;
const
clientY
=
e
.
clientY
;
const
upperLeftPointX
=
Number
(
dom
?.
offsetLeft
);
const
upperLeftPointY
=
Number
(
dom
?.
offsetTop
);
const
lowerRightX
=
Number
(
upperLeftPointX
)
+
Number
(
dom
?.
offsetWidth
);
const
lowerRightY
=
Number
(
upperLeftPointY
)
+
Number
(
dom
?.
offsetHeight
);
if
(
clientX
>
upperLeftPointX
&&
clientY
>
upperLeftPointY
&&
clientX
<
lowerRightX
&&
clientY
<
lowerRightY
)
{
// const batchFlowArr = getBatchFlowArr(info.id);
const
newDragItem
=
cloneDeep
(
dragItem
);
const
newOperatorItem
=
getNewOperatorItem
(
newDragItem
,
clientX
-
upperLeftPointX
,
clientY
-
upperLeftPointY
-
42
);
// const newOperatorItem = {
// ...newDragItem,
// id: `${newDragItem.id}_${count}`,
// source: `${newDragItem.source}_${count}`,
// target: `${newDragItem.target}_${count}`,
// position: {
// x: clientX - upperLeftPointX,
// y: clientY - upperLeftPointY,
// },
// };
const
newVal
=
cloneDeep
(
operatorList
);
newVal
.
push
(
newOperatorItem
);
console
.
log
(
newVal
);
setOperatorList
(
newVal
);
// setTemplateConfigInfo(newVal);
}
// setIsDragStyle(false);
},
[
dragItem
,
operatorList
,
setOperatorList
,
getNewOperatorItem
]
);
return
(
<
div
className=
{
styles
.
operatorList
}
>
<
div
className=
{
styles
.
searchBox
}
>
...
...
@@ -378,17 +316,28 @@ const OperatorList = () => {
<
InputBase
className=
{
styles
.
searchInput
}
placeholder=
"请输入算子名称"
value=
{
name
}
onChange=
{
nameChange
}
// value={keyword}
// onChange={keywordChange}
onKeyUp=
{
handleKeywordKeyUp
}
/>
</
div
>
<
div
className=
{
styles
.
list
}
onScroll=
{
(
e
)
=>
handleScroll
(
e
)
}
>
{
list
.
map
((
item
,
index
)
=>
{
return
(
<
div
key=
{
index
}
className=
{
styles
.
li
}
>
<
div
key=
{
index
}
className=
{
styles
.
li
}
draggable=
{
true
}
onDragStart=
{
()
=>
onDragStart
(
item
)
}
onDragEnd=
{
onDragEnd
}
>
{
randerIcon
()
}
<
span
className=
{
styles
.
name
}
>
{
item
.
name
}
</
span
>
<
span
className=
{
styles
.
version
}
>
{
item
.
version
}
</
span
>
<
div
className=
{
styles
.
nameVersion
}
>
<
span
className=
{
styles
.
name
}
>
{
item
.
title
}
</
span
>
<
span
className=
{
styles
.
version
}
>
v
{
item
.
version
}
</
span
>
</
div
>
{
/* <span className={styles.name}>{item.title}</span>
<span className={styles.version}>{item.version}</span> */
}
</
div
>
);
})
}
...
...
src/views/CustomOperator/components/SaveOperator/index.tsx
0 → 100644
View file @
93b21ec8
import
{
saveUserSpec
}
from
"@/api/workbench_api"
;
import
MyDialog
from
"@/components/mui/MyDialog"
;
import
MyInput
from
"@/components/mui/MyInput"
;
import
{
checkIsNumberLetterChinese
}
from
"@/utils/util"
;
import
{
useState
}
from
"react"
;
import
useMyRequest
from
"@/hooks/useMyRequest"
;
import
{
useStores
}
from
"@/store"
;
import
styles
from
"./index.module.css"
;
import
{
useMessage
}
from
"@/components/MySnackbar"
;
import
{
toJS
}
from
"mobx"
;
import
{
ITask
}
from
"@/views/Project/ProjectSubmitWork/interface"
;
interface
IProps
{
saveFormDialog
:
boolean
;
setSaveFormDialog
:
(
val
:
boolean
)
=>
void
;
onBack
?:
()
=>
void
;
title
:
string
;
setTitle
:
(
val
:
string
)
=>
void
;
version
:
string
;
setVersion
:
(
val
:
string
)
=>
void
;
description
:
string
;
setDescription
:
(
val
:
string
)
=>
void
;
creator
?:
string
;
templateConfigInfo
:
ITask
[];
id
?:
string
;
}
const
SaveOperator
=
(
props
:
IProps
)
=>
{
const
{
saveFormDialog
,
setSaveFormDialog
,
onBack
,
title
,
setTitle
,
version
,
setVersion
,
description
,
setDescription
,
templateConfigInfo
,
creator
,
id
,
}
=
props
;
const
{
currentProjectStore
}
=
useStores
();
const
Message
=
useMessage
();
const
productId
=
toJS
(
currentProjectStore
.
currentProductInfo
.
id
);
const
[
titleHelper
,
setTitleHelper
]
=
useState
({
// 算子名称错误提示
error
:
false
,
helperText
:
""
,
});
const
[
versionHelper
,
setVersionHelper
]
=
useState
({
// 算子版本错误提示
error
:
false
,
helperText
:
""
,
});
// 算子保存方法
const
{
run
:
saveUserSpecRun
}
=
useMyRequest
(
saveUserSpec
,
{
onSuccess
:
(
res
)
=>
{
Message
.
success
(
"保存成功!"
);
onBack
&&
onBack
();
},
});
// 关闭表单弹窗
const
handleCloseDialog
=
()
=>
{
setSaveFormDialog
(
false
);
};
// 算子名称
const
handleTitleChange
=
(
e
:
any
)
=>
{
const
title
=
e
.
target
.
value
;
setTitle
(
title
);
checkTitle
(
title
);
};
// 算子版本
const
handleVersionChange
=
(
e
:
any
)
=>
{
let
version
=
e
.
target
.
value
;
setVersion
(
version
);
checkVersion
(
version
);
};
// 算子描述
const
handleDescriptionChange
=
(
e
:
any
)
=>
{
let
description
=
e
.
target
.
value
;
if
(
description
.
length
<
301
)
{
setDescription
(
description
);
}
};
// 校验算子名称
const
checkTitle
=
(
title
:
string
)
=>
{
if
(
!
title
)
{
setTitleHelper
({
error
:
true
,
helperText
:
"必须输入算子名称"
,
});
return
false
;
}
else
if
(
title
.
length
>
15
)
{
setTitleHelper
({
error
:
true
,
helperText
:
"格式不正确,必须在15字符以内,仅限大小写字母、数字、中文"
,
});
return
false
;
}
else
if
(
!
checkIsNumberLetterChinese
(
title
))
{
setTitleHelper
({
error
:
true
,
helperText
:
"格式不正确,必须在15字符以内,仅限大小写字母、数字、中文"
,
});
return
false
;
}
else
{
setTitleHelper
({
error
:
false
,
helperText
:
""
,
});
return
true
;
}
};
// 校验版本号格式
const
checkVersion
=
(
version
:
string
)
=>
{
if
(
/^
[
1-9
]\d?(\.(
0|
[
1-9
]\d?)){2}
$/
.
test
(
version
))
{
setVersionHelper
({
error
:
false
,
helperText
:
""
,
});
}
else
{
setVersionHelper
({
error
:
true
,
helperText
:
"格式不正确,必须为X.Y.Z格式,且XYZ必须为0~99的正整数"
,
});
return
false
;
}
};
// 表单弹窗确定,新建算子
const
handleOncofirm
=
()
=>
{
if
(
checkTitle
(
title
)
&&
checkVersion
(
version
))
{
if
(
id
)
{
saveUserSpecRun
({
title
,
version
,
description
,
tasks
:
templateConfigInfo
,
productId
,
id
,
creator
,
});
}
else
{
saveUserSpecRun
({
title
,
version
,
description
,
tasks
:
templateConfigInfo
,
productId
,
});
}
}
};
return
(
<
MyDialog
open=
{
saveFormDialog
}
title=
"保存算子"
onClose=
{
handleCloseDialog
}
onConfirm=
{
handleOncofirm
}
>
<
div
className=
{
styles
.
saveBox
}
>
<
MyInput
value=
{
title
}
label=
"算子名称"
onChange=
{
handleTitleChange
}
required
error=
{
titleHelper
.
error
}
helperText=
{
titleHelper
.
helperText
}
style=
{
{
margin
:
"20px 0"
}
}
disabled=
{
id
?
true
:
false
}
></
MyInput
>
<
MyInput
value=
{
version
}
label=
"版本号"
onChange=
{
handleVersionChange
}
error=
{
versionHelper
.
error
}
helperText=
{
versionHelper
.
helperText
}
style=
{
{
marginBottom
:
"20px"
}
}
></
MyInput
>
<
div
style=
{
{
position
:
"relative"
}
}
>
<
MyInput
value=
{
description
}
id=
"desc"
label=
"模板描述"
placeholder=
"模板描述"
onChange=
{
handleDescriptionChange
}
multiline
rows=
{
4
}
/>
<
span
style=
{
{
position
:
"absolute"
,
bottom
:
"7px"
,
right
:
"12px"
,
color
:
description
.
length
>=
300
?
"#d32f2f"
:
"#C2C6CC"
,
}
}
>
{
description
.
length
}
/300
</
span
>
</
div
>
</
div
>
</
MyDialog
>
);
};
export
default
SaveOperator
;
src/views/CustomOperator/index.tsx
View file @
93b21ec8
...
...
@@ -3,7 +3,15 @@ import { observer } from "mobx-react-lite";
import
FullScreenDrawer
from
"@/components/CommonComponents/FullScreenDrawer"
;
import
MyButton
from
"@/components/mui/MyButton"
;
import
OperatorList
from
"./components/OperatorList"
;
import
Flow
from
"../Project/components/Flow"
;
// import Flow from "../Project/components/Flow";
import
useMyRequest
from
"@/hooks/useMyRequest"
;
import
{
saveBatchActor
}
from
"@/api/project_api"
;
import
{
useMessage
}
from
"@/components/MySnackbar"
;
import
{
useStores
}
from
"@/store"
;
import
BatchOperatorFlow
from
"../Project/components/Flow/components/BatchOperatorFlow"
;
import
{
toJS
}
from
"mobx"
;
import
{
ITask
}
from
"../Project/ProjectSubmitWork/interface"
;
import
_
from
"lodash"
;
import
styles
from
"./index.module.css"
;
type
IProps
=
{
...
...
@@ -12,24 +20,162 @@ type IProps = {
const
CustomOperator
=
observer
((
props
:
IProps
)
=>
{
const
{
setShowCustomOperator
}
=
props
;
const
Message
=
useMessage
();
const
[
operatorList
,
setOperatorList
]
=
useState
<
ITask
[]
>
([]);
const
{
currentProjectStore
}
=
useStores
();
const
productId
=
toJS
(
currentProjectStore
.
currentProductInfo
.
id
);
// const [showCustomOperator, setShowCustomOperator] = useState(false);
/** 设置选中唯一标识符 */
const
handleNodeClick
=
useCallback
((
val
:
string
)
=>
{
// setSelectTaskId(val);
// console.log(val);
},
[]);
// 保存批算子
const
{
run
:
saveBatchActorRun
}
=
useMyRequest
(
saveBatchActor
,
{
onSuccess
:
(
res
)
=>
{
console
.
log
(
"res"
,
res
);
},
});
const
handleSave
=
useCallback
(()
=>
{
saveBatchActorRun
({
query
:
{
productId
:
productId
as
string
,
batchName
:
"123456"
,
batchVersion
:
"1.0.0"
,
description
:
""
,
},
body
:
[],
});
},
[
saveBatchActorRun
,
productId
]);
// 判断 每个流算子必须至少有一条连接线。
const
checkHasOneLine
=
(
sourceArr
:
string
[],
targetArr
:
string
[])
=>
{
const
all
=
_
.
uniq
([...
sourceArr
,
...
targetArr
]);
if
(
all
.
length
===
operatorList
.
length
)
{
return
true
;
}
else
{
return
false
;
}
// _.uniq([2, 1, 2]);
};
// 判断 每个起始算子(可以有多个起始点)的输入必须为文件的路径输入或数据集的路径输入。
const
checkIn
=
(
targetArr
:
string
[])
=>
{
const
uniqTargetArr
=
_
.
uniq
(
targetArr
);
if
(
uniqTargetArr
.
length
===
operatorList
.
length
)
{
// 流节点连成一个圈了
return
false
;
}
let
check
=
true
;
operatorList
.
forEach
((
flowNode
)
=>
{
if
(
uniqTargetArr
.
indexOf
(
flowNode
.
id
)
===
-
1
)
{
// 该节点的输入没有连线 也就是说这个节点是起点
const
inArr
=
flowNode
.
parameters
.
filter
(
(
parameter
)
=>
parameter
.
parameterGroup
===
"in"
);
if
(
inArr
.
length
>
0
)
{
if
(
!
inArr
.
some
((
inItem
)
=>
{
return
inItem
.
domType
===
"dataset"
||
inItem
.
domType
===
"path"
;
})
)
{
check
=
false
;
}
}
else
{
// 起点没有输入
check
=
false
;
}
}
});
return
check
;
};
// 判断 起码有一个结尾算子(可以有多个结尾点)的输出必须为文件保存或数据集保存。
const
checkOut
=
(
sourceArr
:
string
[])
=>
{
const
uniqSourceArr
=
_
.
uniq
(
sourceArr
);
if
(
uniqSourceArr
.
length
===
operatorList
.
length
)
{
// 流节点连成一个圈了
return
false
;
}
let
check
=
true
;
operatorList
.
forEach
((
flowNode
)
=>
{
if
(
uniqSourceArr
.
indexOf
(
flowNode
.
id
)
===
-
1
)
{
// 该节点的输入没有连线 也就是说这个节点是起点
const
inArr
=
flowNode
.
parameters
.
filter
(
(
parameter
)
=>
parameter
.
parameterGroup
===
"in"
);
if
(
inArr
.
length
>
0
)
{
if
(
!
inArr
.
some
((
inItem
)
=>
{
return
inItem
.
domType
===
"dataset"
||
inItem
.
domType
===
"file"
;
})
)
{
check
=
false
;
}
}
else
{
// 起点没有输入
check
=
false
;
}
}
});
return
check
;
};
const
handleCheck
=
()
=>
{
if
(
operatorList
.
length
===
0
)
{
Message
.
error
(
"内容不能为空!"
);
return
;
}
let
sourceArr
:
string
[]
=
[];
let
targetArr
:
string
[]
=
[];
operatorList
.
forEach
((
flowNode
)
=>
{
flowNode
.
edges
.
forEach
((
edge
)
=>
{
edge
.
source
&&
sourceArr
.
push
(
edge
.
source
);
edge
.
target
&&
targetArr
.
push
(
edge
.
target
);
});
});
if
(
!
checkHasOneLine
([...
sourceArr
],
[...
targetArr
]))
{
Message
.
error
(
"内容校验未通过,请检查!"
);
return
;
}
if
(
!
checkIn
([...
targetArr
]))
{
Message
.
error
(
"内容校验未通过,请检查!"
);
return
;
}
if
(
!
checkOut
([...
sourceArr
]))
{
Message
.
error
(
"内容校验未通过,请检查!"
);
return
;
}
};
return
(
<
FullScreenDrawer
handleClose=
{
setShowCustomOperator
}
zIndex=
{
1100
}
>
<
div
className=
{
styles
.
customOperator
}
>
<
div
className=
{
styles
.
coTop
}
>
<
div
className=
{
styles
.
coTitle
}
>
添加算子
</
div
>
<
MyButton
text=
"添加"
></
MyButton
>
<
MyButton
text=
"添加"
onClick=
{
()
=>
{
handleCheck
();
}
}
></
MyButton
>
</
div
>
<
div
className=
{
styles
.
coContent
}
>
<
OperatorList
/>
<
Flow
showControls=
{
false
}
// tasks={templateConfigInfo}
// setTasks={setTemplateConfigInfo}
<
div
className=
{
styles
.
coContent
}
id=
"customOperatorFlow"
>
<
OperatorList
operatorList=
{
operatorList
}
setOperatorList=
{
setOperatorList
}
/>
<
BatchOperatorFlow
tasks=
{
operatorList
}
setTasks=
{
setOperatorList
}
type=
"edit"
// onFlowNodeClick={handleNodeClick}
onFlowNodeClick=
{
handleNodeClick
}
flowNodeDraggable=
{
true
}
// ListenState={!saveFormDialog}
showControls=
{
false
}
/>
</
div
>
</
div
>
...
...
src/views/Project/components/Flow/components/BatchOperatorFlow/index.module.css
0 → 100644
View file @
93b21ec8
.reactFlowBox
>
div
:last-child
{
display
:
none
;
}
src/views/Project/components/Flow/components/BatchOperatorFlow/index.tsx
0 → 100644
View file @
93b21ec8
// 自定义批算子时使用的流程图
import
ReactFlow
,
{
Controls
,
Background
,
useNodesState
,
useEdgesState
,
ReactFlowProps
,
Node
,
Connection
,
Edge
,
}
from
"react-flow-renderer"
;
import
{
useCallback
,
useEffect
,
useMemo
,
useState
}
from
"react"
;
import
{
uuid
}
from
"@/utils/util"
;
import
{
IEdge
,
IParameter
,
ITask
,
}
from
"../../../../ProjectSubmitWork/interface"
;
import
{
ILine
}
from
"../../interface"
;
import
BatchNode
from
"../BatchNode"
;
import
FlowNode
from
"../FlowNode"
;
import
{
getCustomTemplateParameterCheckResult
}
from
"@/views/WorkFlowEdit/util"
;
import
{
useMessage
}
from
"@/components/MySnackbar"
;
import
styles
from
"./index.module.css"
;
interface
IProps
extends
ReactFlowProps
{
tasks
?:
ITask
[];
/** 类型, edit为编辑类型 */
type
?:
"edit"
|
"default"
;
/** 设置组件数据 组件为编辑状态使用 */
setTasks
?:
(
val
:
ITask
[])
=>
void
;
/** 点击流程node 节点 返回唯一标识符 */
onFlowNodeClick
?:
(
val
:
string
)
=>
void
;
/** 监听事件的状态 */
ListenState
?:
boolean
;
/** 流节点是否可以拖拽 */
flowNodeDraggable
?:
boolean
;
// 是否显示Controls(放大缩小全屏等按钮)
showControls
?:
boolean
;
}
const
BatchOperatorFlow
=
(
props
:
IProps
)
=>
{
const
{
tasks
,
type
:
flowType
=
"default"
,
setTasks
,
onFlowNodeClick
,
ListenState
=
true
,
flowNodeDraggable
=
false
,
showControls
=
true
,
...
other
}
=
props
;
/** 自定义的节点类型 */
const
nodeTypes
=
useMemo
(()
=>
{
return
{
batchNode
:
BatchNode
,
flowNode
:
FlowNode
};
},
[]);
/** 内部维护的选择的flow节点Id */
const
[
inSideFlowNodeId
,
setInSideFlowNodeId
]
=
useState
<
string
>
(
""
);
/** 选中的线 */
const
[
selectedEdge
,
setSelectedEdge
]
=
useState
<
Edge
>
();
const
Message
=
useMessage
();
/** 原始数据删除线 */
const
tasksDeleteLine
=
useCallback
(
(
connection
:
Connection
|
Edge
)
=>
{
const
result
=
(
tasks
?.
length
&&
tasks
.
map
((
item
)
=>
{
/** 删除batch起始的edges中的一项 === 等于删除了一根连线 */
if
(
item
.
id
===
connection
.
source
&&
item
.
type
===
"BATCH"
)
{
const
newEdges
=
(
item
.
edges
?.
length
&&
item
.
edges
?.
filter
(
(
every
)
=>
every
.
sourceHandle
!==
connection
.
sourceHandle
))
||
[];
return
{
...
item
,
edges
:
newEdges
,
};
/** 选中batch结束位置&&更新校验值 */
}
else
if
(
item
.
id
===
connection
.
target
&&
item
.
type
===
"BATCH"
)
{
const
newParameters
=
(
item
.
parameters
?.
length
&&
item
.
parameters
.
map
((
every
)
=>
{
if
(
every
.
name
===
connection
.
targetHandle
)
{
const
{
error
,
helperText
}
=
getCustomTemplateParameterCheckResult
({
...
every
,
linked
:
false
,
hidden
:
false
,
});
return
{
...
every
,
hidden
:
false
,
error
,
helperText
,
};
}
else
{
return
every
;
}
}))
||
[];
return
{
...
item
,
parameters
:
newParameters
,
};
}
else
{
return
item
;
}
}))
||
[];
return
result
;
},
[
tasks
]
);
/** 删除流节点或者线 */
const
deleteSelectFlowNode
=
useCallback
(
(
e
:
any
)
=>
{
if
(
e
.
keyCode
===
8
&&
ListenState
)
{
/** 删除流节点逻辑 */
// inSideFlowNodeId
console
.
log
(
inSideFlowNodeId
);
if
(
inSideFlowNodeId
)
{
const
newVal
=
(
tasks
?.
length
&&
tasks
.
filter
((
item
)
=>
{
return
item
.
id
!==
inSideFlowNodeId
;
}))
||
[];
setTasks
&&
setTasks
(
newVal
);
}
if
(
selectedEdge
)
{
const
newVal
=
tasksDeleteLine
(
selectedEdge
);
setTasks
&&
setTasks
(
newVal
);
}
}
},
[
inSideFlowNodeId
,
ListenState
,
tasks
,
selectedEdge
,
setTasks
,
tasksDeleteLine
,
]
);
/** 监听鼠标按下事件 */
useEffect
(()
=>
{
window
.
addEventListener
(
"keyup"
,
deleteSelectFlowNode
);
return
()
=>
{
window
.
removeEventListener
(
"keyup"
,
deleteSelectFlowNode
);
};
},
[]);
/** 生成初始化node节点 */
const
initialNodes
=
useMemo
(()
=>
{
const
val
:
any
=
[];
tasks
?.
length
&&
tasks
.
forEach
((
item
)
=>
{
val
.
push
({
id
:
item
.
id
,
type
:
item
.
type
===
"BATCH"
?
"batchNode"
:
"flowNode"
,
/** 每一项的数据 */
data
:
{
info
:
item
,
...{
selectedStatus
:
inSideFlowNodeId
===
item
.
id
,
flowNodeStyle
:
{
backgroundColor
:
"#fff"
,
borderRadius
:
"4px"
,
},
inStyle
:
{
backgroundColor
:
"rgba(19, 112, 255, 1)"
,
border
:
"none"
,
left
:
12
,
},
outStyle
:
{
backgroundColor
:
"rgba(19, 112, 255, 1)"
,
border
:
"none"
,
left
:
12
,
},
},
/** 样式 */
style
:
{
padding
:
"20px"
,
},
},
/** 坐标 */
position
:
{
x
:
Number
(
item
.
position
?.
x
)
||
0
,
y
:
Number
(
item
.
position
?.
y
)
||
0
,
},
/**
* extent: "parent" 跟随父节点移动
* draggable: false 节点不可移动
*/
...(
item
.
type
===
"BATCH"
?
{
style
:
{
zIndex
:
-
1
},
extent
:
"parent"
}
:
{
draggable
:
flowNodeDraggable
}),
/** parentNode 父节点ID */
// ...(item.parentNode ? { parentNode: item.parentNode } : {}),
});
});
return
val
;
},
[
tasks
,
flowType
,
inSideFlowNodeId
,
flowNodeDraggable
]);
/** 生成初始化的连线节点 */
const
initialEdges
=
useMemo
(()
=>
{
const
val
:
ILine
[]
=
[];
tasks
?.
length
&&
tasks
.
forEach
((
item
)
=>
{
item
?.
edges
?.
length
&&
item
?.
edges
.
forEach
((
every
)
=>
{
const
newLine
=
{
...
every
,
batchId
:
item
.
parentNode
?
item
.
parentNode
:
item
.
id
,
};
val
.
push
(
newLine
);
},
[]);
});
return
val
.
map
((
item
:
ILine
)
=>
{
return
{
...
item
,
/** 点击线选中 */
...(
selectedEdge
?.
id
===
item
.
id
?
{
style
:
{
stroke
:
"#1370FF"
,
strokeWidth
:
2
},
animated
:
true
,
}
:
{}),
labelStyle
:
{
fill
:
"#8A9099"
},
labelBgStyle
:
{
fill
:
"#F7F8FA "
},
label
:
item
.
label
?
`(
${
item
.
label
}
)`
:
""
,
};
});
},
[
selectedEdge
?.
id
,
tasks
]);
/** 设置nodeId方法 */
// const setNodeIdFun = useCallback(
// (id: string) => {
// setInSideFlowNodeId("");
// document.getElementById(`point${id}`)?.scrollIntoView(true);
// },
// []
// );
/** flowNode点击事件 */
const
onNodeClick
=
(
e
:
any
,
node
:
Node
)
=>
{
console
.
log
(
tasks
);
console
.
log
(
node
);
tasks
?.
forEach
((
item
)
=>
{
if
(
item
.
id
===
node
.
id
)
{
setInSideFlowNodeId
(
node
.
id
);
}
});
if
(
onFlowNodeClick
)
{
onFlowNodeClick
(
node
.
id
);
}
/** 点击node统一清除选中的edge */
setSelectedEdge
(
undefined
);
};
// 点击面板、画布
const
handlePaneClick
=
()
=>
{
setInSideFlowNodeId
(
""
);
setSelectedEdge
(
undefined
);
};
/** node节点 */
const
[
nodes
,
setNodes
,
onNodesChange
]
=
useNodesState
(
initialNodes
);
/** 连线数组 */
const
[
edges
,
setEdges
,
onEdgesChange
]
=
useEdgesState
(
initialEdges
);
useEffect
(()
=>
{
setEdges
(
initialEdges
);
},
[
initialEdges
,
setEdges
]);
useEffect
(()
=>
{
setNodes
(
initialNodes
);
},
[
initialNodes
,
setNodes
]);
/** 节点拖动停止 */
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
)
{
const
{
error
,
helperText
}
=
getCustomTemplateParameterCheckResult
({
...
item
,
linked
:
true
,
hidden
:
true
,
});
return
{
...
item
,
linked
:
true
,
hidden
:
true
,
helperText
,
error
};
}
else
{
return
item
;
}
});
},
[]
);
/** 获取连接线的端点类型 */
const
getClassType
=
useCallback
(
(
connection
:
Connection
)
=>
{
let
inputClassType
=
""
,
outClassType
:
string
|
undefined
=
undefined
;
tasks
?.
length
&&
tasks
.
forEach
((
item
)
=>
{
if
([
connection
.
source
,
connection
.
target
].
includes
(
item
.
id
))
{
item
.
parameters
.
forEach
((
every
)
=>
{
if
(
every
.
name
===
connection
.
targetHandle
)
{
inputClassType
=
every
.
classType
;
}
if
(
every
.
name
===
connection
.
sourceHandle
)
{
outClassType
=
every
.
classType
;
}
});
}
});
return
{
inputClassType
,
outClassType
};
},
[
tasks
]
);
/** 连接校验并修改值 */
const
connectCheck
=
useCallback
(
(
connection
:
Connection
)
=>
{
const
newVal
=
(
tasks
?.
length
&&
tasks
?.
map
((
item
)
=>
{
if
(
item
.
id
===
connection
.
source
)
{
return
{
...
item
,
edges
:
[
...
item
.
edges
,
{
...
connection
,
id
:
uuid
(),
},
],
};
}
else
if
(
item
.
id
===
connection
.
target
)
{
return
{
...
item
,
parameters
:
connectModifyParameters
(
item
.
parameters
,
connection
),
};
}
else
{
return
item
;
}
}))
||
[];
return
newVal
;
},
[
connectModifyParameters
,
tasks
]
);
/** 已经连接线啦 */
const
onConnect
=
useCallback
(
(
connection
:
Connection
)
=>
{
const
{
inputClassType
,
outClassType
}
=
getClassType
(
connection
);
let
result
:
ITask
[]
=
[];
if
(
inputClassType
===
outClassType
)
{
result
=
connectCheck
(
connection
)
as
ITask
[];
}
else
{
Message
.
error
(
"端口数据类型不一致,无法连接!"
);
result
=
tasksDeleteLine
(
connection
);
}
setTasks
&&
setTasks
(
result
);
},
[
Message
,
connectCheck
,
getClassType
,
setTasks
,
tasksDeleteLine
]
);
/** 点击连线 */
const
onEdgeClick
=
useCallback
((
e
:
any
,
val
:
Edge
)
=>
{
setSelectedEdge
(
val
);
/** 点击连线清除选中的node ID */
setInSideFlowNodeId
(
""
);
},
[]);
const
reactFlowParams
=
flowType
===
"edit"
?
{
onNodesChange
,
onEdgesChange
,
onNodeDragStop
,
onConnect
,
onEdgeClick
,
}
:
{};
return
(
<
ReactFlow
className=
{
styles
.
reactFlowBox
}
nodes=
{
nodes
}
edges=
{
edges
}
fitView=
{
flowType
===
"default"
?
true
:
false
}
{
...
reactFlowParams
}
// proOptions={{ hideAttribution: true, account: "" }}
nodeTypes=
{
nodeTypes
}
onPaneClick=
{
handlePaneClick
}
onNodeClick=
{
onNodeClick
}
{
...
other
}
>
{
showControls
&&
<
Controls
/>
}
<
Background
color=
"#aaa"
gap=
{
16
}
/>
</
ReactFlow
>
);
};
export
default
BatchOperatorFlow
;
src/views/Project/components/Flow/components/FlowNode/index.tsx
View file @
93b21ec8
...
...
@@ -8,11 +8,15 @@
*/
import
classNames
from
"classnames"
;
import
{
Handle
,
Position
}
from
"react-flow-renderer"
;
import
{
useMemo
}
from
"react"
;
// import { IParameter } from "@/views/Project/ProjectSubmitWork/interface";
import
{
uuid
}
from
"@/utils/util"
;
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
MyTooltip
from
"@/components/mui/MyTooltip"
;
import
styles
from
"./index.module.css"
;
/** 自定义flow节点 */
...
...
@@ -35,23 +39,65 @@ const FlowNode = (props: any) => {
const
{
dotStatus
,
selectedStatus
,
info
:
{
title
,
isCheck
,
executionStatus
},
flowNodeStyle
=
{
display
:
"flex"
,
alignItems
:
"center"
},
// 样式
inStyle
=
{
background
:
"#C2C6CC "
,
left
:
12
},
// 样式
outStyle
=
{
background
:
"#C2C6CC "
,
left
:
12
},
// 样式
info
:
{
title
,
isCheck
,
executionStatus
,
parameters
},
}
=
data
;
/** 获取输入参数数组 */
const
inParamsArr
=
useMemo
(()
=>
{
return
(
(
parameters
?.
length
&&
parameters
?.
filter
((
item
:
any
)
=>
{
return
item
.
parameterGroup
===
"in"
;
}))
||
[]
);
},
[
parameters
]);
/** 获取输出参数数组 */
const
outParamsArr
=
useMemo
(()
=>
{
return
(
(
parameters
?.
length
&&
parameters
?.
filter
((
item
:
any
)
=>
{
return
item
.
parameterGroup
===
"out"
;
}))
||
[]
);
},
[
parameters
]);
return
(
<
div
style=
{
flowNodeStyle
}
className=
{
classNames
({
[
styles
.
flowNode
]:
true
,
[
styles
.
selectedFlowBox
]:
selectedStatus
,
})
}
>
{
dotStatus
?.
isInput
?
(
<
Handle
style=
{
inStyle
}
type=
"target"
position=
{
Position
.
Top
}
/>
)
:
null
}
{
inParamsArr
?.
length
?
inParamsArr
.
map
((
item
:
any
,
index
:
number
)
=>
{
return
(
<
MyTooltip
title=
{
item
.
name
}
key=
{
uuid
()
}
>
<
Handle
style=
{
{
background
:
"#C2C6CC "
,
left
:
12
}
}
id=
{
item
.
name
}
style=
{
{
background
:
"#fff "
,
border
:
"1px solid #D1D6DE"
,
left
:
index
*
20
+
20
,
...
inStyle
,
}
}
type=
"target"
position=
{
Position
.
Top
}
/>
)
:
null
}
<
div
style=
{
{
display
:
"flex"
,
alignItems
:
"center"
}
}
>
</
MyTooltip
>
);
})
:
null
}
<
div
>
{
title
||
""
}
{
isCheck
&&
<
span
className=
{
styles
.
successDot
}
></
span
>
}
{
getImgUrl
(
executionStatus
)
&&
(
...
...
@@ -63,12 +109,27 @@ const FlowNode = (props: any) => {
)
}
</
div
>
{
dotStatus
?.
isOutput
?
(
<
Handle
style=
{
outStyle
}
type=
"source"
position=
{
Position
.
Bottom
}
/>
)
:
null
}
{
outParamsArr
?.
length
?
outParamsArr
.
map
((
item
:
any
,
index
:
number
)
=>
{
return
(
<
MyTooltip
title=
{
item
.
name
}
key=
{
uuid
()
}
>
<
Handle
style=
{
{
background
:
"#C2C6CC "
,
left
:
12
}
}
id=
{
item
.
name
}
style=
{
{
background
:
"#fff "
,
border
:
"1px solid #D1D6DE"
,
left
:
index
*
20
+
20
,
...
outStyle
,
}
}
type=
"source"
position=
{
Position
.
Bottom
}
/>
)
:
null
}
</
MyTooltip
>
);
})
:
null
}
</
div
>
);
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment