Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
D
dlink
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
zhaowei
dlink
Commits
b7d9b0dc
Commit
b7d9b0dc
authored
Mar 18, 2022
by
wenmo
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
作业剪切和粘贴
parent
cea2b80b
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
420 additions
and
320 deletions
+420
-320
CatalogueController.java
...c/main/java/com/dlink/controller/CatalogueController.java
+12
-0
CatalogueService.java
...min/src/main/java/com/dlink/service/CatalogueService.java
+2
-0
CatalogueServiceImpl.java
...ain/java/com/dlink/service/impl/CatalogueServiceImpl.java
+18
-7
DDL.ts
dlink-web/src/components/Studio/StudioEvent/DDL.ts
+55
-29
index.tsx
dlink-web/src/components/Studio/StudioMenu/index.tsx
+107
-100
index.tsx
dlink-web/src/components/Studio/StudioTree/index.tsx
+226
-184
No files found.
dlink-admin/src/main/java/com/dlink/controller/CatalogueController.java
View file @
b7d9b0dc
...
...
@@ -217,4 +217,16 @@ public class CatalogueController {
return
Result
.
failed
(
"重命名失败"
);
}
}
/**
* 重命名节点和作业
*/
@PutMapping
(
"/moveCatalogue"
)
public
Result
moveCatalogue
(
@RequestBody
Catalogue
catalogue
)
throws
Exception
{
if
(
catalogueService
.
moveCatalogue
(
catalogue
.
getId
(),
catalogue
.
getParentId
()))
{
return
Result
.
succeed
(
true
,
"移动成功"
);
}
else
{
return
Result
.
failed
(
false
,
"移动失败"
);
}
}
}
dlink-admin/src/main/java/com/dlink/service/CatalogueService.java
View file @
b7d9b0dc
...
...
@@ -25,4 +25,6 @@ public interface CatalogueService extends ISuperService<Catalogue> {
boolean
toRename
(
Catalogue
catalogue
);
boolean
removeCatalogueAndTaskById
(
Integer
id
);
boolean
moveCatalogue
(
Integer
id
,
Integer
parentId
);
}
dlink-admin/src/main/java/com/dlink/service/impl/CatalogueServiceImpl.java
View file @
b7d9b0dc
...
...
@@ -15,6 +15,9 @@ import org.springframework.transaction.annotation.Transactional;
import
java.util.List
;
import
static
com
.
dlink
.
assertion
.
Asserts
.
isNotNull
;
import
static
com
.
dlink
.
assertion
.
Asserts
.
isNull
;
/**
* CatalogueServiceImpl
*
...
...
@@ -29,9 +32,6 @@ public class CatalogueServiceImpl extends SuperServiceImpl<CatalogueMapper, Cata
@Autowired
private
StatementService
statementService
;
@Autowired
private
CatalogueMapper
catalogueMapper
;
@Override
public
List
<
Catalogue
>
getAllData
()
{
return
this
.
list
();
...
...
@@ -39,7 +39,7 @@ public class CatalogueServiceImpl extends SuperServiceImpl<CatalogueMapper, Cata
@Override
public
Catalogue
findByParentIdAndName
(
Integer
parent_id
,
String
name
)
{
return
catalogu
eMapper
.
selectOne
(
Wrappers
.<
Catalogue
>
query
().
eq
(
"parent_id"
,
parent_id
).
eq
(
"name"
,
name
));
return
bas
eMapper
.
selectOne
(
Wrappers
.<
Catalogue
>
query
().
eq
(
"parent_id"
,
parent_id
).
eq
(
"name"
,
name
));
}
@Transactional
(
rollbackFor
=
Exception
.
class
)
...
...
@@ -83,7 +83,7 @@ public class CatalogueServiceImpl extends SuperServiceImpl<CatalogueMapper, Cata
@Override
public
boolean
toRename
(
Catalogue
catalogue
)
{
Catalogue
oldCatalogue
=
this
.
getById
(
catalogue
.
getId
());
if
(
oldCatalogue
==
null
)
{
if
(
isNull
(
oldCatalogue
)
)
{
return
false
;
}
else
{
Task
task
=
new
Task
();
...
...
@@ -99,10 +99,10 @@ public class CatalogueServiceImpl extends SuperServiceImpl<CatalogueMapper, Cata
@Override
public
boolean
removeCatalogueAndTaskById
(
Integer
id
)
{
Catalogue
catalogue
=
this
.
getById
(
id
);
if
(
catalogue
==
null
)
{
if
(
isNull
(
catalogue
)
)
{
return
false
;
}
else
{
if
(
catalogue
.
getTaskId
()
!=
null
)
{
if
(
isNotNull
(
catalogue
.
getTaskId
())
)
{
taskService
.
removeById
(
catalogue
.
getTaskId
());
statementService
.
removeById
(
catalogue
.
getTaskId
());
}
...
...
@@ -110,4 +110,15 @@ public class CatalogueServiceImpl extends SuperServiceImpl<CatalogueMapper, Cata
return
true
;
}
}
@Override
public
boolean
moveCatalogue
(
Integer
id
,
Integer
parentId
)
{
Catalogue
catalogue
=
this
.
getById
(
id
);
if
(
isNull
(
catalogue
))
{
return
false
;
}
else
{
catalogue
.
setParentId
(
parentId
);
return
updateById
(
catalogue
);
}
}
}
dlink-web/src/components/Studio/StudioEvent/DDL.ts
View file @
b7d9b0dc
import
{
executeDDL
}
from
"@/pages/FlinkSqlStudio/service"
;
import
FlinkSQL
from
"./FlinkSQL"
;
import
{
SessionType
}
from
"@/pages/FlinkSqlStudio/model"
;
import
{
Modal
,
message
}
from
"antd"
;
import
{
Modal
,
message
}
from
"antd"
;
import
{
addOrUpdateData
,
getData
,
handleRemove
}
from
"@/components/Common/crud"
;
/*--- 保存sql ---*/
export
function
saveTask
(
current
:
any
,
dispatch
:
any
)
{
export
function
saveTask
(
current
:
any
,
dispatch
:
any
)
{
if
(
current
.
task
)
{
let
task
=
{
...
current
.
task
,
...
...
@@ -19,24 +19,26 @@ export function saveTask(current:any,dispatch: any){
}
/*--- 创建会话 ---*/
export
function
createSession
(
session
:
SessionType
,
dispatch
:
any
)
{
const
res
=
addOrUpdateData
(
"api/studio/createSession"
,
session
)
export
function
createSession
(
session
:
SessionType
,
dispatch
:
any
)
{
const
res
=
addOrUpdateData
(
"api/studio/createSession"
,
session
)
res
.
then
((
result
)
=>
{
message
.
success
(
`创建会话【
${
session
.
session
}
】成功!`
);
result
.
datas
&&
changeSession
(
result
.
datas
,
dispatch
);
result
.
datas
&&
changeSession
(
result
.
datas
,
dispatch
);
listSession
(
dispatch
);
});
}
/*--- 查询会话列表 ---*/
export
function
listSession
(
dispatch
:
any
)
{
const
res
=
getData
(
"api/studio/listSession"
);
res
.
then
((
result
)
=>
{
res
.
then
((
result
)
=>
{
dispatch
&&
dispatch
({
type
:
"Studio/saveSession"
,
payload
:
result
.
datas
,
});
});
}
/*--- 切换会话 ---*/
export
function
changeSession
(
session
:
SessionType
,
dispatch
:
any
)
{
dispatch
&&
dispatch
({
...
...
@@ -44,15 +46,17 @@ export function changeSession(session: SessionType, dispatch: any) {
payload
:
session
,
});
setTimeout
(
function
()
{
showTables
(
session
.
session
,
dispatch
);
},
200
);
showTables
(
session
.
session
,
dispatch
);
},
200
);
}
/*--- 退出会话 ---*/
export
function
quitSession
(
dispatch
:
any
)
{
export
function
quitSession
(
dispatch
:
any
)
{
dispatch
&&
dispatch
({
type
:
"Studio/quitCurrentSession"
,
});
}
/*--- 注销会话 ---*/
export
function
clearSession
(
session
:
string
,
dispatch
:
any
)
{
Modal
.
confirm
({
...
...
@@ -71,9 +75,10 @@ export function clearSession(session: string, dispatch: any) {
}
});
}
/*--- 刷新 Catalog Table ---*/
export
function
showTables
(
session
:
string
,
dispatch
:
any
)
{
if
(
session
==
null
||
session
==
''
)
{
if
(
session
==
null
||
session
==
''
)
{
return
;
}
const
res
=
executeDDL
({
...
...
@@ -90,11 +95,12 @@ export function showTables(session: string, dispatch: any) {
dispatch
&&
dispatch
({
type
:
"Studio/refreshCurrentSession"
,
payload
:
{
connectors
:
tableData
connectors
:
tableData
},
});
});
}
/*--- 移除 Catalog Table ---*/
export
function
removeTable
(
tablename
:
string
,
session
:
string
,
dispatch
:
any
)
{
Modal
.
confirm
({
...
...
@@ -114,6 +120,7 @@ export function removeTable(tablename: string, session: string, dispatch: any) {
}
});
}
/*--- 刷新 集群 ---*/
export
function
showCluster
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/cluster/listEnabledAll'
);
...
...
@@ -124,6 +131,7 @@ export function showCluster(dispatch: any) {
});
});
}
/*--- 刷新 Session集群 ---*/
export
function
showSessionCluster
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/cluster/listSessionEnable'
);
...
...
@@ -134,6 +142,7 @@ export function showSessionCluster(dispatch: any) {
});
});
}
/*--- 刷新 数据源 ---*/
export
function
showDataBase
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/database/listEnabledAll'
);
...
...
@@ -144,6 +153,7 @@ export function showDataBase(dispatch: any) {
});
});
}
/*--- 刷新 执行环境 ---*/
export
function
showEnv
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/task/listFlinkSQLEnv'
);
...
...
@@ -154,6 +164,7 @@ export function showEnv(dispatch: any) {
});
});
}
/*--- 刷新 自定义Jar ---*/
export
function
showJars
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/jar/listEnabledAll'
);
...
...
@@ -164,6 +175,7 @@ export function showJars(dispatch: any) {
});
});
}
/*--- 刷新 报警实例 ---*/
export
function
showAlertInstance
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/alertInstance/listEnabledAll'
);
...
...
@@ -174,6 +186,7 @@ export function showAlertInstance(dispatch: any) {
});
});
}
/*--- 刷新 报警组 ---*/
export
function
showAlertGroup
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/alertGroup/listEnabledAll'
);
...
...
@@ -184,29 +197,35 @@ export function showAlertGroup(dispatch: any) {
});
});
}
/*--- 刷新 元数据表 ---*/
export
function
showMetaDataTable
(
id
:
number
)
{
return
getData
(
'api/database/getSchemasAndTables'
,
{
id
:
id
});
export
function
showMetaDataTable
(
id
:
number
)
{
return
getData
(
'api/database/getSchemasAndTables'
,
{
id
:
id
});
}
/*--- 刷新 Flink Jobs ---*/
export
function
showFlinkJobs
(
clusterId
:
number
)
{
return
getData
(
'api/studio/listJobs'
,
{
clusterId
:
clusterId
});
export
function
showFlinkJobs
(
clusterId
:
number
)
{
return
getData
(
'api/studio/listJobs'
,
{
clusterId
:
clusterId
});
}
/*--- 停止 Flink Jobs ---*/
export
function
cancelJob
(
clusterId
:
number
,
jobId
:
string
)
{
return
getData
(
'api/studio/cancel'
,
{
clusterId
:
clusterId
,
jobId
:
jobId
});
export
function
cancelJob
(
clusterId
:
number
,
jobId
:
string
)
{
return
getData
(
'api/studio/cancel'
,
{
clusterId
:
clusterId
,
jobId
:
jobId
});
}
/*--- 重启 Flink Jobs ---*/
export
function
restartJob
(
id
:
number
)
{
return
getData
(
'api/task/restartTask'
,
{
id
});
export
function
restartJob
(
id
:
number
,
isOnLine
:
boolean
)
{
return
getData
(
'api/task/restartTask'
,
{
id
,
isOnLine
});
}
/*--- 停止 SavePoint Jobs ---*/
export
function
savepointJob
(
clusterId
:
number
,
jobId
:
string
,
savePointType
:
string
,
name
:
string
,
taskId
:
number
)
{
return
getData
(
'api/studio/savepoint'
,
{
clusterId
,
jobId
,
savePointType
,
name
,
taskId
});
export
function
savepointJob
(
clusterId
:
number
,
jobId
:
string
,
savePointType
:
string
,
name
:
string
,
taskId
:
number
)
{
return
getData
(
'api/studio/savepoint'
,
{
clusterId
,
jobId
,
savePointType
,
name
,
taskId
});
}
/*--- 根据版本号获取所有自动补全的文档 ---*/
export
function
getFillAllByVersion
(
version
:
string
,
dispatch
:
any
)
{
const
res
=
getData
(
'api/document/getFillAllByVersion'
,
{
version
:
version
});
export
function
getFillAllByVersion
(
version
:
string
,
dispatch
:
any
)
{
const
res
=
getData
(
'api/document/getFillAllByVersion'
,
{
version
:
version
});
res
.
then
((
result
)
=>
{
result
.
datas
&&
dispatch
&&
dispatch
({
type
:
"Document/saveAllFillDocuments"
,
...
...
@@ -214,6 +233,7 @@ export function getFillAllByVersion(version:string,dispatch: any) {
});
});
}
/*--- 刷新 集群 ---*/
export
function
showClusterConfiguration
(
dispatch
:
any
)
{
const
res
=
getData
(
'api/clusterConfiguration/listEnabledAll'
);
...
...
@@ -224,27 +244,33 @@ export function showClusterConfiguration(dispatch: any) {
});
});
}
/*--- 发布作业 ---*/
export
function
releaseTask
(
id
:
number
)
{
return
getData
(
'api/task/releaseTask'
,{
id
});
return
getData
(
'api/task/releaseTask'
,
{
id
});
}
/*--- 发布作业 ---*/
export
function
developTask
(
id
:
number
)
{
return
getData
(
'api/task/developTask'
,{
id
});
return
getData
(
'api/task/developTask'
,
{
id
});
}
/*--- 上线作业 ---*/
export
function
onLineTask
(
id
:
number
)
{
return
getData
(
'api/task/onLineTask'
,{
id
});
return
getData
(
'api/task/onLineTask'
,
{
id
});
}
/*--- 下线作业 ---*/
export
function
offLineTask
(
id
:
number
,
type
:
string
)
{
return
getData
(
'api/task/offLineTask'
,{
id
,
type
});
return
getData
(
'api/task/offLineTask'
,
{
id
,
type
});
}
/*--- 注销作业 ---*/
export
function
cancelTask
(
id
:
number
)
{
return
getData
(
'api/task/cancelTask'
,{
id
});
return
getData
(
'api/task/cancelTask'
,
{
id
});
}
/*--- 恢复作业 ---*/
export
function
recoveryTask
(
id
:
number
)
{
return
getData
(
'api/task/recoveryTask'
,{
id
});
return
getData
(
'api/task/recoveryTask'
,
{
id
});
}
dlink-web/src/components/Studio/StudioMenu/index.tsx
View file @
b7d9b0dc
import
styles
from
"./index.less"
;
import
{
Menu
,
Dropdown
,
Tooltip
,
Row
,
Col
,
notification
,
Modal
,
message
}
from
"antd"
;
import
{
PauseCircleTwoTone
,
CarryOutTwoTone
,
DeleteTwoTone
,
PlayCircleTwoTone
,
CameraTwoTone
,
SnippetsTwoTone
,
FileAddTwoTone
,
FolderOpenTwoTone
,
SafetyCertificateTwoTone
,
SaveTwoTone
,
FlagTwoTone
,
CodeTwoTone
,
PauseCircleTwoTone
,
CarryOutTwoTone
,
DeleteTwoTone
,
PlayCircleTwoTone
,
CameraTwoTone
,
SnippetsTwoTone
,
FileAddTwoTone
,
FolderOpenTwoTone
,
SafetyCertificateTwoTone
,
SaveTwoTone
,
FlagTwoTone
,
CodeTwoTone
,
EnvironmentOutlined
,
SmileOutlined
,
RocketTwoTone
,
QuestionCircleTwoTone
,
MessageOutlined
,
ClusterOutlined
,
EditTwoTone
,
RestTwoTone
}
from
"@ant-design/icons"
;
...
...
@@ -51,7 +51,7 @@ const menu = (
const
StudioMenu
=
(
props
:
any
)
=>
{
const
{
isFullScreen
,
tabs
,
current
,
currentPath
,
form
,
width
,
height
,
refs
,
dispatch
,
currentSession
}
=
props
;
const
{
isFullScreen
,
tabs
,
current
,
currentPath
,
form
,
width
,
height
,
refs
,
dispatch
,
currentSession
}
=
props
;
const
[
modalVisible
,
handleModalVisible
]
=
useState
<
boolean
>
(
false
);
const
[
exportModalVisible
,
handleExportModalVisible
]
=
useState
<
boolean
>
(
false
);
const
[
graphModalVisible
,
handleGraphModalVisible
]
=
useState
<
boolean
>
(
false
);
...
...
@@ -59,15 +59,15 @@ const StudioMenu = (props: any) => {
const
[
graphData
,
setGraphData
]
=
useState
();
const
onKeyDown
=
useCallback
((
e
)
=>
{
if
(
e
.
keyCode
===
83
&&
(
e
.
ctrlKey
===
true
||
e
.
metaKey
))
{
if
(
e
.
keyCode
===
83
&&
(
e
.
ctrlKey
===
true
||
e
.
metaKey
))
{
e
.
preventDefault
();
if
(
current
)
{
if
(
current
)
{
props
.
saveTask
(
current
);
}
}
if
(
e
.
keyCode
===
113
)
{
if
(
e
.
keyCode
===
113
)
{
e
.
preventDefault
();
if
(
current
)
{
if
(
current
)
{
// handleEditModalVisible(true);
props
.
changeFullScreen
(
true
);
}
...
...
@@ -82,7 +82,7 @@ const StudioMenu = (props: any) => {
},
[
current
]);
const
execute
=
()
=>
{
if
(
!
isSql
(
current
.
task
.
dialect
)
&&!
isOnline
(
current
.
task
.
type
))
{
if
(
!
isSql
(
current
.
task
.
dialect
)
&&
!
isOnline
(
current
.
task
.
type
))
{
message
.
warn
(
`该任务执行模式为【
${
current
.
task
.
type
}
】,不支持 SQL 查询,请手动保存后使用右侧按钮——作业提交`
);
return
;
}
...
...
@@ -97,6 +97,7 @@ const StudioMenu = (props: any) => {
let
useSession
=
!!
currentSession
.
session
;
let
param
=
{
...
current
.
task
,
taskId
:
current
.
task
.
id
,
useSession
:
useSession
,
session
:
currentSession
.
session
,
configJson
:
JSON
.
stringify
(
current
.
task
.
config
),
...
...
@@ -118,7 +119,7 @@ const StudioMenu = (props: any) => {
result
.
then
(
res
=>
{
notification
.
close
(
taskKey
);
if
(
res
.
datas
.
success
)
{
res
.
datas
?.
jobInstanceId
&&
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
res
.
datas
?.
jobInstanceId
);
res
.
datas
?.
jobInstanceId
&&
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
res
.
datas
?.
jobInstanceId
);
message
.
success
(
'执行成功'
);
}
else
{
message
.
error
(
'执行失败'
);
...
...
@@ -163,7 +164,7 @@ const StudioMenu = (props: any) => {
const
res
=
await
postDataArray
(
'/api/task/submit'
,
[
task
.
id
]);
notification
.
close
(
taskKey
);
if
(
res
.
datas
[
0
].
success
)
{
res
.
datas
[
0
].
jobInstanceId
&&
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
res
.
datas
[
0
].
jobInstanceId
);
res
.
datas
[
0
].
jobInstanceId
&&
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
res
.
datas
[
0
].
jobInstanceId
);
message
.
success
(
'异步提交成功'
);
}
else
{
message
.
success
(
'异步提交失败'
);
...
...
@@ -177,7 +178,7 @@ const StudioMenu = (props: any) => {
handleModalVisible
(
true
);
};
const
onGetStreamGraph
=
()
=>
{
const
onGetStreamGraph
=
()
=>
{
let
selectsql
=
null
;
if
(
current
.
monaco
.
current
)
{
let
selection
=
current
.
monaco
.
current
.
editor
.
getSelection
();
...
...
@@ -196,36 +197,38 @@ const StudioMenu = (props: any) => {
};
const
res
=
getJobPlan
(
param
);
handleGraphModalVisible
(
true
);
res
.
then
((
result
)
=>
{
if
(
result
.
code
==
0
)
{
res
.
then
((
result
)
=>
{
if
(
result
.
code
==
0
)
{
setGraphData
(
buildGraphData
(
result
.
datas
));
}
else
{
}
else
{
setGraphData
(
undefined
);
}
})
};
const
buildGraphData
=
(
data
)
=>
{
const
buildGraphData
=
(
data
)
=>
{
let
edges
=
[];
for
(
let
i
in
data
.
nodes
)
{
data
.
nodes
[
i
].
id
=
data
.
nodes
[
i
].
id
.
toString
();
data
.
nodes
[
i
].
value
=
{
title
:
data
.
nodes
[
i
].
pact
,
for
(
let
i
in
data
.
nodes
)
{
data
.
nodes
[
i
].
id
=
data
.
nodes
[
i
].
id
.
toString
();
data
.
nodes
[
i
].
value
=
{
title
:
data
.
nodes
[
i
].
pact
,
items
:
[
{
text
:
getRangeText
(
data
.
nodes
[
i
].
description
),
},
{
text
:
'
\
r
\
nParallelism: '
,
value
:
'
\
r
\
n '
+
data
.
nodes
[
i
].
parallelism
,
value
:
'
\
r
\
n '
+
data
.
nodes
[
i
].
parallelism
,
},
],
};
if
(
data
.
nodes
[
i
].
inputs
){
for
(
let
j
in
data
.
nodes
[
i
].
inputs
){
edges
.
push
({
source
:
data
.
nodes
[
i
].
inputs
[
j
].
id
.
toString
(),
if
(
data
.
nodes
[
i
].
inputs
)
{
for
(
let
j
in
data
.
nodes
[
i
].
inputs
)
{
edges
.
push
({
source
:
data
.
nodes
[
i
].
inputs
[
j
].
id
.
toString
(),
target
:
data
.
nodes
[
i
].
id
.
toString
(),
value
:
data
.
nodes
[
i
].
inputs
[
j
].
ship_strategy
})
value
:
data
.
nodes
[
i
].
inputs
[
j
].
ship_strategy
})
}
}
}
...
...
@@ -233,17 +236,17 @@ const StudioMenu = (props: any) => {
return
data
;
};
const
getRangeText
=
(
str
:
string
)
=>
{
const
getRangeText
=
(
str
:
string
)
=>
{
str
=
escape2Html
(
str
);
var
canvas
=
getRangeText
.
canvas
||
(
getRangeText
.
canvas
=
document
.
createElement
(
"canvas"
));
var
context
=
canvas
.
getContext
(
"2d"
);
context
.
font
=
"10px sans-serif"
;
let
result
=
''
;
let
count
=
1
;
for
(
let
i
=
0
,
len
=
str
.
length
;
i
<
len
;
i
++
)
{
for
(
let
i
=
0
,
len
=
str
.
length
;
i
<
len
;
i
++
)
{
result
+=
str
[
i
];
let
width
=
context
.
measureText
(
result
).
width
;
if
(
width
>=
110
*
count
)
{
if
(
width
>=
110
*
count
)
{
result
+=
'
\
r
\
n'
;
count
++
;
}
...
...
@@ -251,7 +254,7 @@ const StudioMenu = (props: any) => {
return
result
;
};
const
getTextWidth
=
(
text
:
string
,
font
:
string
)
=>
{
const
getTextWidth
=
(
text
:
string
,
font
:
string
)
=>
{
var
canvas
=
getTextWidth
.
canvas
||
(
getTextWidth
.
canvas
=
document
.
createElement
(
"canvas"
));
var
context
=
canvas
.
getContext
(
"2d"
);
context
.
font
=
font
;
...
...
@@ -259,13 +262,15 @@ const StudioMenu = (props: any) => {
return
metrics
.
width
;
}
const
escape2Html
=
(
str
:
string
)
=>
{
let
arrEntities
=
{
'lt'
:
'<'
,
'gt'
:
'>'
,
'nbsp'
:
' '
,
'amp'
:
'&'
,
'quot'
:
'"'
};
return
str
.
replace
(
/&
(
lt|gt|nbsp|amp|quot
)
;/ig
,
function
(
all
,
t
){
return
arrEntities
[
t
];});
const
escape2Html
=
(
str
:
string
)
=>
{
let
arrEntities
=
{
'lt'
:
'<'
,
'gt'
:
'>'
,
'nbsp'
:
' '
,
'amp'
:
'&'
,
'quot'
:
'"'
};
return
str
.
replace
(
/&
(
lt|gt|nbsp|amp|quot
)
;/ig
,
function
(
all
,
t
)
{
return
arrEntities
[
t
];
});
}
const
toFullScreen
=
()
=>
{
if
(
current
)
{
if
(
current
)
{
props
.
changeFullScreen
(
true
);
}
};
...
...
@@ -287,10 +292,10 @@ const StudioMenu = (props: any) => {
onOk
:
async
()
=>
{
const
res
=
releaseTask
(
current
.
task
.
id
);
res
.
then
((
result
)
=>
{
if
(
result
.
code
==
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
RELEASE
);
if
(
result
.
code
==
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
RELEASE
);
message
.
success
(
`发布作业【
${
current
.
task
.
alias
}
】成功`
);
}
else
{
}
else
{
message
.
error
(
`发布作业【
${
current
.
task
.
alias
}
】失败,原因:\n
${
result
.
msg
}
`
);
}
});
...
...
@@ -307,8 +312,8 @@ const StudioMenu = (props: any) => {
onOk
:
async
()
=>
{
const
res
=
developTask
(
current
.
task
.
id
);
res
.
then
((
result
)
=>
{
result
.
datas
&&
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
DEVELOP
);
if
(
result
.
code
==
CODE
.
SUCCESS
)
{
result
.
datas
&&
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
DEVELOP
);
if
(
result
.
code
==
CODE
.
SUCCESS
)
{
message
.
success
(
`维护作业【
${
current
.
task
.
alias
}
】成功`
);
}
});
...
...
@@ -325,11 +330,11 @@ const StudioMenu = (props: any) => {
onOk
:
async
()
=>
{
const
res
=
onLineTask
(
current
.
task
.
id
);
res
.
then
((
result
)
=>
{
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
ONLINE
);
result
.
datas
?.
jobInstanceId
&&
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
result
.
datas
?.
jobInstanceId
);
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
ONLINE
);
result
.
datas
?.
jobInstanceId
&&
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
result
.
datas
?.
jobInstanceId
);
message
.
success
(
`上线作业【
${
current
.
task
.
alias
}
】成功`
);
}
else
{
}
else
{
message
.
error
(
`上线作业【
${
current
.
task
.
alias
}
】失败,原因:\n
${
result
.
msg
}
`
);
}
});
...
...
@@ -344,15 +349,15 @@ const StudioMenu = (props: any) => {
okText
:
'确认'
,
cancelText
:
'取消'
,
onOk
:
async
()
=>
{
const
res
=
offLineTask
(
current
.
task
.
id
,
type
);
const
res
=
offLineTask
(
current
.
task
.
id
,
type
);
res
.
then
((
result
)
=>
{
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
if
(
current
.
task
.
step
===
JOB_LIFE_CYCLE
.
ONLINE
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
RELEASE
);
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
if
(
current
.
task
.
step
===
JOB_LIFE_CYCLE
.
ONLINE
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
RELEASE
);
}
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
0
);
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
0
);
message
.
success
(
`停止作业【
${
current
.
task
.
alias
}
】成功`
);
}
else
{
}
else
{
message
.
error
(
`停止作业【
${
current
.
task
.
alias
}
】失败,原因:\n
${
result
.
msg
}
`
);
}
});
...
...
@@ -367,13 +372,13 @@ const StudioMenu = (props: any) => {
okText
:
'确认'
,
cancelText
:
'取消'
,
onOk
:
async
()
=>
{
const
res
=
offLineTask
(
current
.
task
.
id
,
type
);
const
res
=
offLineTask
(
current
.
task
.
id
,
type
);
res
.
then
((
result
)
=>
{
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
RELEASE
);
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
0
);
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
RELEASE
);
props
.
changeTaskJobInstance
(
current
.
task
.
id
,
0
);
message
.
success
(
`下线作业【
${
current
.
task
.
alias
}
】成功`
);
}
else
{
}
else
{
message
.
error
(
`下线作业【
${
current
.
task
.
alias
}
】失败,原因:\n
${
result
.
msg
}
`
);
}
});
...
...
@@ -390,10 +395,10 @@ const StudioMenu = (props: any) => {
onOk
:
async
()
=>
{
const
res
=
cancelTask
(
current
.
task
.
id
);
res
.
then
((
result
)
=>
{
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
CANCEL
);
if
(
result
.
code
===
CODE
.
SUCCESS
)
{
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
CANCEL
);
message
.
success
(
`注销作业【
${
current
.
task
.
alias
}
】成功`
);
}
else
{
}
else
{
message
.
error
(
`注销作业【
${
current
.
task
.
alias
}
】失败,原因:\n
${
result
.
msg
}
`
);
}
});
...
...
@@ -410,8 +415,8 @@ const StudioMenu = (props: any) => {
onOk
:
async
()
=>
{
const
res
=
recoveryTask
(
current
.
task
.
id
);
res
.
then
((
result
)
=>
{
result
.
datas
&&
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
DEVELOP
);
if
(
result
.
code
==
CODE
.
SUCCESS
)
{
result
.
datas
&&
props
.
changeTaskStep
(
current
.
task
.
id
,
JOB_LIFE_CYCLE
.
DEVELOP
);
if
(
result
.
code
==
CODE
.
SUCCESS
)
{
message
.
success
(
`恢复作业【
${
current
.
task
.
alias
}
】成功`
);
}
});
...
...
@@ -420,19 +425,19 @@ const StudioMenu = (props: any) => {
};
const
isShowGetStreamGraphBtn
=
()
=>
{
return
(
!
current
.
task
.
dialect
||
current
.
task
.
dialect
===
DIALECT
.
FLINKSQL
);
return
(
!
current
.
task
.
dialect
||
current
.
task
.
dialect
===
DIALECT
.
FLINKSQL
);
};
const
isShowExecuteBtn
=
()
=>
{
return
!
isDeletedTask
(
current
.
task
.
step
)
&&
isExecuteSql
(
current
.
task
.
dialect
)
&&
!
isRunningTask
(
current
.
task
.
jobInstanceId
);
return
!
isDeletedTask
(
current
.
task
.
step
)
&&
isExecuteSql
(
current
.
task
.
dialect
)
&&
!
isRunningTask
(
current
.
task
.
jobInstanceId
);
};
const
isShowSubmitBtn
=
()
=>
{
return
!
isDeletedTask
(
current
.
task
.
step
)
&&
isTask
(
current
.
task
.
dialect
)
&&
!
isRunningTask
(
current
.
task
.
jobInstanceId
);
return
!
isDeletedTask
(
current
.
task
.
step
)
&&
isTask
(
current
.
task
.
dialect
)
&&
!
isRunningTask
(
current
.
task
.
jobInstanceId
);
};
const
isShowCancelTaskBtn
=
()
=>
{
return
!
isDeletedTask
(
current
.
task
.
step
)
&&
isTask
(
current
.
task
.
dialect
)
&&
isRunningTask
(
current
.
task
.
jobInstanceId
);
return
!
isDeletedTask
(
current
.
task
.
step
)
&&
isTask
(
current
.
task
.
dialect
)
&&
isRunningTask
(
current
.
task
.
jobInstanceId
);
};
const
runMenu
=
(
...
...
@@ -516,12 +521,12 @@ const StudioMenu = (props: any) => {
</
Breadcrumb
>
)
}
</
Col
>
{
current
?.
task
?
{
current
?.
task
?
<
Col
span=
{
8
}
>
<
Tooltip
title=
"全屏开发"
>
<
Button
type=
"text"
icon=
{
<
CodeTwoTone
/>
}
icon=
{
<
CodeTwoTone
/>
}
onClick=
{
toFullScreen
}
/>
</
Tooltip
>
...
...
@@ -543,7 +548,7 @@ const StudioMenu = (props: any) => {
<
Tooltip
title=
"导出当前的 Sql 及配置"
>
<
Button
type=
"text"
icon=
{
<
SnippetsTwoTone
/>
}
icon=
{
<
SnippetsTwoTone
/>
}
onClick=
{
exportSql
}
/>
</
Tooltip
>
...
...
@@ -555,7 +560,7 @@ const StudioMenu = (props: any) => {
onClick=
{
onCheckSql
}
/>
</
Tooltip
>
{
isShowGetStreamGraphBtn
()
&&
(
{
isShowGetStreamGraphBtn
()
&&
(
<
Tooltip
title=
"获取当前的 FlinkSql 的执行图"
>
<
Button
type=
"text"
...
...
@@ -563,7 +568,7 @@ const StudioMenu = (props: any) => {
onClick=
{
onGetStreamGraph
}
/>
</
Tooltip
>)
}
{
isShowExecuteBtn
()
&&
(
{
isShowExecuteBtn
()
&&
(
<
Tooltip
title=
"执行当前的 SQL"
>
<
Button
type=
"text"
...
...
@@ -572,7 +577,7 @@ const StudioMenu = (props: any) => {
onClick=
{
execute
}
/>
</
Tooltip
>)
}
{
isShowSubmitBtn
()
&&
(<>
{
isShowSubmitBtn
()
&&
(<>
<
Tooltip
title=
"提交当前的作业到集群,提交前请手动保存"
>
<
Button
type=
"text"
...
...
@@ -585,8 +590,8 @@ const StudioMenu = (props: any) => {
<
Tooltip
title=
"停止"
>
<
Button
type=
"text"
icon=
{
<
PauseCircleTwoTone
/>
}
onClick=
{
()
=>
handleCancelTask
(
'canceljob'
)
}
icon=
{
<
PauseCircleTwoTone
/>
}
onClick=
{
()
=>
handleCancelTask
(
'canceljob'
)
}
/>
</
Tooltip
>
}
...
...
@@ -598,46 +603,46 @@ const StudioMenu = (props: any) => {
icon=
{
<
CameraTwoTone
/>
}
onClick=
{
toReleaseTask
}
/>
</
Tooltip
>
:
undefined
</
Tooltip
>
:
undefined
}{
current
.
task
.
step
==
JOB_LIFE_CYCLE
.
RELEASE
?
<><
Tooltip
title=
"维护,点击进入编辑状态"
>
<
Button
type=
"text"
icon=
{
<
EditTwoTone
/>
}
icon=
{
<
EditTwoTone
/>
}
onClick=
{
toDevelopTask
}
/>
</
Tooltip
>
<
Tooltip
title=
"上线,上线后自动恢复、告警等将生效"
>
<
Button
type=
"text"
icon=
{
<
CarryOutTwoTone
/>
}
icon=
{
<
CarryOutTwoTone
/>
}
onClick=
{
toOnLineTask
}
/>
</
Tooltip
></>
:
undefined
</
Tooltip
></>
:
undefined
}{
current
.
task
.
step
==
JOB_LIFE_CYCLE
.
ONLINE
?
<
Tooltip
title=
"下线,将进入最新发布状态"
>
<
Button
type=
"text"
icon=
{
<
PauseCircleTwoTone
/>
}
onClick=
{
()
=>
toOffLineTask
(
'cancel'
)
}
icon=
{
<
PauseCircleTwoTone
/>
}
onClick=
{
()
=>
toOffLineTask
(
'cancel'
)
}
/>
</
Tooltip
>
:
undefined
</
Tooltip
>
:
undefined
}{
(
current
.
task
.
step
!=
JOB_LIFE_CYCLE
.
ONLINE
&&
current
.
task
.
step
!=
JOB_LIFE_CYCLE
.
CANCEL
)
?
<
Tooltip
title=
"注销,将进入回收站"
>
<
Button
type=
"text"
icon=
{
<
DeleteTwoTone
/>
}
icon=
{
<
DeleteTwoTone
/>
}
onClick=
{
toCancelTask
}
/>
</
Tooltip
>
:
undefined
</
Tooltip
>
:
undefined
}{
current
.
task
.
step
==
JOB_LIFE_CYCLE
.
CANCEL
?
<
Tooltip
title=
"恢复,将进入维护模式"
>
<
Button
type=
"text"
icon=
{
<
RestTwoTone
/>
}
icon=
{
<
RestTwoTone
/>
}
onClick=
{
toRecoveryTask
}
/>
</
Tooltip
>
:
undefined
</
Tooltip
>
:
undefined
}
<
Tooltip
title=
"查看使用帮助"
>
<
Button
...
...
@@ -646,12 +651,14 @@ const StudioMenu = (props: any) => {
onClick=
{
showHelp
}
/>
</
Tooltip
>
</
Col
>
:
undefined
}
</
Col
>
:
undefined
}
</
Row
>
</
Col
>
<
StudioExplain
modalVisible=
{
modalVisible
}
onClose=
{
()
=>
{
handleModalVisible
(
false
)}
}
onClose=
{
()
=>
{
handleModalVisible
(
false
)
}
}
/>
<
Modal
width=
{
1000
}
...
...
@@ -661,16 +668,16 @@ const StudioMenu = (props: any) => {
visible=
{
graphModalVisible
}
onCancel=
{
()
=>
handleGraphModalVisible
(
false
)
}
>
<
StudioGraph
data=
{
graphData
}
/>
<
StudioGraph
data=
{
graphData
}
/>
</
Modal
>
{
current
?.
task
?
{
current
?.
task
?
<
ModalForm
title=
{
`${current.task.alias} 的 ${current.task.dialect} 导出`
}
visible=
{
exportModalVisible
}
width=
{
1000
}
modalProps=
{
{
maskClosable
:
false
,
bodyStyle
:{
maskClosable
:
false
,
bodyStyle
:
{
padding
:
'5px'
}
}
}
...
...
@@ -683,12 +690,12 @@ const StudioMenu = (props: any) => {
},
}
}
>
<
SqlExport
id=
{
current
.
task
.
id
}
/>
</
ModalForm
>
:
undefined
}
{
current
&&
isFullScreen
?
<
Modal
<
SqlExport
id=
{
current
.
task
.
id
}
/>
</
ModalForm
>
:
undefined
}
{
current
&&
isFullScreen
?
<
Modal
width=
{
width
}
bodyStyle=
{
{
padding
:
0
}
}
style=
{
{
top
:
0
,
padding
:
0
,
margin
:
0
,
maxWidth
:
'100vw'
}
}
style=
{
{
top
:
0
,
padding
:
0
,
margin
:
0
,
maxWidth
:
'100vw'
}
}
destroyOnClose
maskClosable=
{
false
}
closable=
{
false
}
...
...
@@ -698,31 +705,31 @@ const StudioMenu = (props: any) => {
props
.
changeFullScreen
(
false
);
}
}
>
<
StudioTabs
width=
{
width
}
height=
{
height
}
/>
</
Modal
>
:
undefined
}
</
Modal
>
:
undefined
}
</
Row
>
);
};
const
mapDispatchToProps
=
(
dispatch
:
Dispatch
)
=>
({
saveTask
:
(
current
:
any
)
=>
dispatch
({
const
mapDispatchToProps
=
(
dispatch
:
Dispatch
)
=>
({
saveTask
:
(
current
:
any
)
=>
dispatch
({
type
:
"Studio/saveTask"
,
payload
:
current
.
task
,
}),
saveTabs
:(
tabs
:
any
)
=>
dispatch
({
}),
saveTabs
:
(
tabs
:
any
)
=>
dispatch
({
type
:
"Studio/saveTabs"
,
payload
:
tabs
,
}),
changeFullScreen
:(
isFull
:
boolean
)
=>
dispatch
({
}),
changeFullScreen
:
(
isFull
:
boolean
)
=>
dispatch
({
type
:
"Studio/changeFullScreen"
,
payload
:
isFull
,
}),
changeTaskStep
:(
id
:
number
,
step
:
number
)
=>
dispatch
({
}),
changeTaskStep
:
(
id
:
number
,
step
:
number
)
=>
dispatch
({
type
:
"Studio/changeTaskStep"
,
payload
:
{
id
,
step
id
,
step
},
}),
changeTaskJobInstance
:(
id
:
number
,
jobInstanceId
:
number
)
=>
dispatch
({
}),
changeTaskJobInstance
:
(
id
:
number
,
jobInstanceId
:
number
)
=>
dispatch
({
type
:
"Studio/changeTaskJobInstance"
,
payload
:
{
id
,
jobInstanceId
id
,
jobInstanceId
},
}),
});
...
...
@@ -734,4 +741,4 @@ export default connect(({Studio}: { Studio: StateType }) => ({
tabs
:
Studio
.
tabs
,
refs
:
Studio
.
refs
,
currentSession
:
Studio
.
currentSession
,
}),
mapDispatchToProps
)(
StudioMenu
);
}),
mapDispatchToProps
)(
StudioMenu
);
dlink-web/src/components/Studio/StudioTree/index.tsx
View file @
b7d9b0dc
import
React
,
{
useEffect
,
useState
,
Key
}
from
"react"
;
import
React
,
{
useEffect
,
useState
,
Key
}
from
"react"
;
import
{
connect
}
from
"umi"
;
import
{
DownOutlined
,
SwitcherOutlined
,
FolderAddOutlined
}
from
"@ant-design/icons"
;
import
{
Tree
,
Menu
,
Empty
,
Button
,
message
,
Modal
,
Tooltip
,
Row
,
Col
,
Input
}
from
'antd'
;
import
{
DownOutlined
,
SwitcherOutlined
,
FolderAddOutlined
}
from
"@ant-design/icons"
;
import
{
Tree
,
Menu
,
Empty
,
Button
,
message
,
Modal
,
Tooltip
,
Row
,
Col
,
Input
}
from
'antd'
;
import
{
getCatalogueTreeData
}
from
"@/pages/FlinkSqlStudio/service"
;
import
{
convertToTreeData
,
getTreeNodeByKey
,
TreeDataNode
}
from
"@/components/Studio/StudioTree/Function"
;
import
style
from
"./index.less"
;
...
...
@@ -11,18 +11,18 @@ import {
}
from
"@/components/Common/crud"
;
import
UpdateCatalogueForm
from
'./components/UpdateCatalogueForm'
;
import
SimpleTaskForm
from
"@/components/Studio/StudioTree/components/SimpleTaskForm"
;
import
{
Scrollbars
}
from
"react-custom-scrollbars"
;
import
{
Scrollbars
}
from
"react-custom-scrollbars"
;
import
{
getIcon
}
from
"@/components/Studio/icon"
;
import
{
showEnv
}
from
"@/components/Studio/StudioEvent/DDL"
;
import
UploadModal
from
"@/components/Studio/StudioTree/components/UploadModal"
;
type
StudioTreeProps
=
{
rightClickMenu
:
StateType
[
'rightClickMenu'
];
dispatch
:
any
;
tabs
:
StateType
[
'tabs'
];
current
:
StateType
[
'current'
];
toolHeight
:
number
;
refs
:
any
;
rightClickMenu
:
StateType
[
'rightClickMenu'
];
dispatch
:
any
;
tabs
:
StateType
[
'tabs'
];
current
:
StateType
[
'current'
];
toolHeight
:
number
;
refs
:
any
;
};
type
RightClickMenu
=
{
...
...
@@ -36,18 +36,18 @@ type RightClickMenu = {
const
generateList
=
(
data
:
any
,
list
:
any
[])
=>
{
for
(
let
i
=
0
;
i
<
data
.
length
;
i
++
)
{
const
node
=
data
[
i
];
const
{
name
,
id
,
parentId
,
level
}
=
node
;
list
.
push
({
name
,
id
,
key
:
id
,
title
:
name
,
parentId
,
level
});
const
{
name
,
id
,
parentId
,
level
}
=
node
;
list
.
push
({
name
,
id
,
key
:
id
,
title
:
name
,
parentId
,
level
});
if
(
node
.
children
)
{
generateList
(
node
.
children
,
list
);
}
}
return
list
}
}
;
// tree树 匹配方法
const
getParentKey
=
(
key
:
number
|
string
,
tree
:
any
):
any
=>
{
let
parentKey
let
parentKey
;
for
(
let
i
=
0
;
i
<
tree
.
length
;
i
++
)
{
const
node
=
tree
[
i
];
if
(
node
.
children
)
{
...
...
@@ -58,16 +58,18 @@ const getParentKey = (key: number | string, tree: any): any => {
}
}
}
// console.log(key, parentKey, tree,)
return
parentKey
;
}
};
const
{
DirectoryTree
}
=
Tree
;
const
{
Search
}
=
Input
;
const
StudioTree
:
React
.
FC
<
StudioTreeProps
>
=
(
props
)
=>
{
const
{
rightClickMenu
,
dispatch
,
tabs
,
refs
,
toolHeight
}
=
props
;
const
{
rightClickMenu
,
dispatch
,
tabs
,
refs
,
toolHeight
}
=
props
;
const
[
treeData
,
setTreeData
]
=
useState
<
TreeDataNode
[]
>
();
const
[
expandedKeys
,
setExpandedKeys
]
=
useState
<
Key
[]
>
();
const
[
defaultExpandedKeys
,
setDefaultExpandedKeys
]
=
useState
<
any
[]
>
([]);
const
[
rightClickNodeTreeItem
,
setRightClickNodeTreeItem
]
=
useState
<
RightClickMenu
>
();
const
[
rightClickNodeTreeItem
,
setRightClickNodeTreeItem
]
=
useState
<
RightClickMenu
>
();
const
[
updateCatalogueModalVisible
,
handleUpdateCatalogueModalVisible
]
=
useState
<
boolean
>
(
false
);
const
[
updateTaskModalVisible
,
handleUpdateTaskModalVisible
]
=
useState
<
boolean
>
(
false
);
const
[
isCreate
,
setIsCreate
]
=
useState
<
boolean
>
(
true
);
...
...
@@ -78,10 +80,9 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
const
[
isUploadModalVisible
,
setIsUploadModalVisible
]
=
useState
(
false
);
const
[
uploadNodeId
,
setUploadNodeId
]
=
useState
(
0
);
const
sref
:
any
=
React
.
createRef
<
Scrollbars
>
();
const
{
DirectoryTree
}
=
Tree
;
const
{
Search
}
=
Input
;
const
[
searchValue
,
setSearchValue
]
=
useState
(
''
)
const
[
searchValue
,
setSearchValue
]
=
useState
(
''
);
const
[
autoExpandParent
,
setAutoExpandParent
]
=
useState
(
true
);
const
[
cutId
,
setCutId
]
=
useState
<
number
|
undefined
>
(
undefined
);
const
getTreeData
=
async
()
=>
{
const
result
=
await
getCatalogueTreeData
();
...
...
@@ -95,51 +96,51 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
expendList
.
push
(
item
.
id
);
})
}
})
})
;
data
=
convertToTreeData
(
list
,
0
);
setTreeData
(
data
);
//默认展开所有
setExpandedKeys
(
expendList
||
[]);
setExpandedKeys
(
expendList
||
[]);
setDefaultExpandedKeys
(
expendList
||
[]);
};
const
onChange
=
(
e
:
any
)
=>
{
let
{
value
}
=
e
.
target
let
{
value
}
=
e
.
target
;
if
(
!
value
)
{
setExpandedKeys
(
defaultExpandedKeys
);
setSearchValue
(
value
)
setSearchValue
(
value
)
;
return
}
value
=
String
(
value
).
trim
()
const
expandList
:
any
[]
=
generateList
(
treeData
,
[])
value
=
String
(
value
).
trim
()
;
const
expandList
:
any
[]
=
generateList
(
treeData
,
[])
;
let
expandedKeys
:
any
=
expandList
.
map
((
item
:
any
)
=>
{
if
(
item
&&
item
.
name
.
indexOf
(
value
)
>
-
1
)
{
let
key
=
getParentKey
(
item
.
key
,
treeData
);
return
key
;
}
return
null
;
})
if
(
item
&&
item
.
name
.
indexOf
(
value
)
>
-
1
)
{
let
key
=
getParentKey
(
item
.
key
,
treeData
);
return
key
;
}
return
null
;
})
.
filter
((
item
:
any
,
i
:
number
,
self
:
any
)
=>
item
&&
self
.
indexOf
(
item
)
===
i
)
setExpandedKeys
(
expandedKeys
)
setSearchValue
(
value
)
setAutoExpandParent
(
true
)
}
const
openByKey
=
async
(
key
:
any
)
=>
{
const
openByKey
=
async
(
key
:
any
)
=>
{
const
result
=
await
getCatalogueTreeData
();
let
data
=
result
.
datas
;
let
list
=
data
;
for
(
let
i
=
0
;
i
<
list
.
length
;
i
++
)
{
list
[
i
].
title
=
list
[
i
].
name
;
list
[
i
].
key
=
list
[
i
].
id
;
if
(
list
[
i
].
isLeaf
)
{
for
(
let
i
=
0
;
i
<
list
.
length
;
i
++
)
{
list
[
i
].
title
=
list
[
i
].
name
;
list
[
i
].
key
=
list
[
i
].
id
;
if
(
list
[
i
].
isLeaf
)
{
list
[
i
].
icon
=
getIcon
(
list
[
i
].
type
);
}
}
data
=
convertToTreeData
(
list
,
0
);
setTreeData
(
data
);
let
node
=
getTreeNodeByKey
(
data
,
key
);
onSelect
([],
{
node
:
node
});
let
node
=
getTreeNodeByKey
(
data
,
key
);
onSelect
([],
{
node
:
node
});
};
useEffect
(()
=>
{
...
...
@@ -147,74 +148,80 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
},
[]);
const
handleMenuClick
=
(
key
:
string
)
=>
{
if
(
key
==
'Open'
)
{
const
handleMenuClick
=
(
key
:
string
)
=>
{
if
(
key
==
'Open'
)
{
toOpen
(
rightClickNode
);
}
else
if
(
key
==
'Submit'
)
{
}
else
if
(
key
==
'Submit'
)
{
toSubmit
(
rightClickNode
);
}
else
if
(
key
==
'CreateCatalogue'
)
{
}
else
if
(
key
==
'CreateCatalogue'
)
{
createCatalogue
(
rightClickNode
);
}
else
if
(
key
==
'CreateRootCatalogue'
)
{
}
else
if
(
key
==
'CreateRootCatalogue'
)
{
createRootCatalogue
();
}
else
if
(
key
==
'ShowUploadModal'
)
{
}
else
if
(
key
==
'ShowUploadModal'
)
{
showUploadModal
(
rightClickNode
);
}
else
if
(
key
==
'CreateTask'
)
{
}
else
if
(
key
==
'CreateTask'
)
{
createTask
(
rightClickNode
);
}
else
if
(
key
==
'Rename'
)
{
}
else
if
(
key
==
'Rename'
)
{
toRename
(
rightClickNode
);
}
else
if
(
key
==
'Delete'
)
{
}
else
if
(
key
==
'Delete'
)
{
toDelete
(
rightClickNode
);
}
else
if
(
key
==
'Cut'
)
{
toCut
(
rightClickNode
);
}
else
if
(
key
==
'Paste'
)
{
toPaste
(
rightClickNode
);
}
};
const
showUploadModal
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
node
==
undefined
)
return
;
const
showUploadModal
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
node
==
undefined
)
return
;
setUploadNodeId
(
node
.
id
);
setIsUploadModalVisible
(
true
);
}
}
;
const
toOpen
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
!
available
){
return
}
const
toOpen
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
!
available
)
{
return
}
setAvailable
(
false
);
setTimeout
(()
=>
{
setTimeout
(()
=>
{
setAvailable
(
true
);
},
200
);
},
200
);
if
(
node
?.
isLeaf
&&
node
.
taskId
)
{
for
(
let
item
of
tabs
.
panes
)
{
if
(
item
.
key
==
node
.
taskId
)
{
dispatch
&&
dispatch
({
if
(
node
?.
isLeaf
&&
node
.
taskId
)
{
for
(
let
item
of
tabs
.
panes
)
{
if
(
item
.
key
==
node
.
taskId
)
{
dispatch
&&
dispatch
({
type
:
"Studio/saveToolHeight"
,
payload
:
toolHeight
-
0.0001
,
payload
:
toolHeight
-
0.0001
,
});
dispatch
&&
dispatch
({
dispatch
&&
dispatch
({
type
:
"Studio/changeActiveKey"
,
payload
:
node
.
taskId
,
});
return
;
}
}
const
result
=
getInfoById
(
'/api/task'
,
node
.
taskId
);
result
.
then
(
result
=>
{
const
result
=
getInfoById
(
'/api/task'
,
node
.
taskId
);
result
.
then
(
result
=>
{
let
newTabs
=
tabs
;
let
newPane
:
any
=
{
let
newPane
:
any
=
{
title
:
<>
{
node
!
.
icon
}
{
node
!
.
name
}
</>,
key
:
node
!
.
taskId
,
value
:
(
result
.
datas
.
statement
?
result
.
datas
.
statement
:
''
),
value
:
(
result
.
datas
.
statement
?
result
.
datas
.
statement
:
''
),
closable
:
true
,
path
:
node
!
.
path
,
task
:{
session
:
''
,
task
:
{
session
:
''
,
maxRowNum
:
100
,
jobName
:
node
!
.
name
,
useResult
:
true
,
useChangeLog
:
false
,
useAutoCancel
:
false
,
useSession
:
false
,
useRemote
:
true
,
jobName
:
node
!
.
name
,
useResult
:
true
,
useChangeLog
:
false
,
useAutoCancel
:
false
,
useSession
:
false
,
useRemote
:
true
,
...
result
.
datas
,
},
console
:{
console
:
{
result
:
{},
chart
:
{},
},
...
...
@@ -222,7 +229,7 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
};
newTabs
!
.
activeKey
=
node
!
.
taskId
;
newTabs
!
.
panes
!
.
push
(
newPane
);
dispatch
&&
dispatch
({
dispatch
&&
dispatch
({
type
:
"Studio/saveTabs"
,
payload
:
newTabs
,
});
...
...
@@ -230,8 +237,8 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
}
};
const
createCatalogue
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
!
node
?.
isLeaf
)
{
const
createCatalogue
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
!
node
?.
isLeaf
)
{
handleUpdateCatalogueModalVisible
(
true
);
setIsCreate
(
true
);
setCatalogueFormValues
({
...
...
@@ -239,12 +246,12 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
parentId
:
node
?.
id
,
});
getTreeData
();
}
else
{
}
else
{
message
.
error
(
'只能在目录上创建目录'
);
}
};
const
createRootCatalogue
=
()
=>
{
const
createRootCatalogue
=
()
=>
{
handleUpdateCatalogueModalVisible
(
true
);
setIsCreate
(
true
);
setCatalogueFormValues
({
...
...
@@ -254,25 +261,25 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
getTreeData
();
};
const
toSubmit
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
const
toSubmit
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
Modal
.
confirm
({
title
:
'提交作业'
,
content
:
'确定提交该作业到其配置的集群吗?'
,
okText
:
'确认'
,
cancelText
:
'取消'
,
onOk
:
async
()
=>
{
onOk
:
async
()
=>
{
let
task
=
{
id
:
node
?.
taskId
,
id
:
node
?.
taskId
,
};
setTimeout
(()
=>
{
setTimeout
(()
=>
{
refs
?.
history
?.
current
?.
reload
();
},
2000
);
handleSubmit
(
'/api/task/submit'
,
'作业'
,
[
task
]);
},
2000
);
handleSubmit
(
'/api/task/submit'
,
'作业'
,
[
task
]);
}
});
};
const
toRename
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
const
toRename
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
handleUpdateCatalogueModalVisible
(
true
);
setIsCreate
(
false
);
setCatalogueFormValues
({
...
...
@@ -282,29 +289,45 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
getTreeData
();
};
const
createTask
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
!
node
?.
isLeaf
)
{
const
toCut
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
setCutId
(
node
?.
id
);
message
.
success
(
'剪切成功'
);
};
const
toPaste
=
async
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
cutId
==
0
)
{
return
;
}
const
datas
=
await
handleAddOrUpdateWithResult
(
'/api/catalogue/moveCatalogue'
,
{
id
:
cutId
,
parentId
:
node
?.
id
});
if
(
datas
)
{
setCutId
(
undefined
);
getTreeData
();
}
};
const
createTask
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
if
(
!
node
?.
isLeaf
)
{
handleUpdateTaskModalVisible
(
true
);
setIsCreate
(
true
);
setTaskFormValues
({
parentId
:
node
?.
id
,
});
//getTreeData();
}
else
{
}
else
{
message
.
error
(
'只能在目录上创建作业'
);
}
};
const
toDelete
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
let
label
=
(
node
?.
taskId
==
null
)?
'目录'
:
'作业'
;
const
toDelete
=
(
node
:
TreeDataNode
|
undefined
)
=>
{
let
label
=
(
node
?.
taskId
==
null
)
?
'目录'
:
'作业'
;
Modal
.
confirm
({
title
:
`删除
${
label
}
`
,
content
:
`确定删除该
${
label
}
【
${
node
?.
name
}
】吗?
`,
okText: '确认',
cancelText: '取消',
onOk:async () => {
await handleRemoveById('/api/catalogue',node!.id);
if(node?.taskId) {
onOk:
async () => {
await handleRemoveById('/api/catalogue',
node!.id);
if
(node?.taskId) {
dispatch({
type: "Studio/deleteTabByKey",
payload: node?.taskId,
...
...
@@ -317,33 +340,39 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
const getNodeTreeRightClickMenu = () => {
const {pageX, pageY} = {...rightClickNodeTreeItem};
const tmpStyle:any = {
const tmpStyle:
any = {
position: 'absolute',
left: pageX,
top: pageY,
};
let menuItems;
if
(rightClickNode&&rightClickNode.isLeaf)
{
menuItems
=
(<>
if
(rightClickNode && rightClickNode.isLeaf)
{
menuItems
=
(<>
<Menu.Item key='Open'>{'打开'}</Menu.Item>
<Menu.Item key='Submit'>{'异步提交'}</Menu.Item>
<Menu.Item key='Rename'>{'重命名'}</Menu.Item>
<Menu.Item key='Cut'>{'剪切'}</Menu.Item>
{cutId && <Menu.Item key='Paste'>{'粘贴'}</Menu.Item>}
<Menu.Item key='Delete'>{'删除'}</Menu.Item>
</>)
}
else if(rightClickNode&&rightClickNode.children&&rightClickNode.children.length>0)
{
menuItems
=
(<>
}
else if (rightClickNode && rightClickNode.children && rightClickNode.children.length > 0)
{
menuItems
=
(<>
<Menu.Item key='CreateCatalogue'>{'创建目录'}</Menu.Item>
<Menu.Item key='CreateRootCatalogue'>{'创建根目录'}</Menu.Item>
<Menu.Item key='ShowUploadModal'>{'上传zip包创建工程'}</Menu.Item>
<Menu.Item key='CreateTask'>{'创建作业'}</Menu.Item>
<Menu.Item key='Rename'>{'重命名'}</Menu.Item>
<Menu.Item key='Cut'>{'剪切'}</Menu.Item>
{cutId && <Menu.Item key='Paste'>{'粘贴'}</Menu.Item>}
<Menu.Item disabled>{'删除'}</Menu.Item>
</>)
}
else
{
menuItems
=
(<>
}
else
{
menuItems
=
(<>
<Menu.Item key='CreateCatalogue'>{'创建目录'}</Menu.Item>
<Menu.Item key='CreateTask'>{'创建作业'}</Menu.Item>
<Menu.Item key='Rename'>{'重命名'}</Menu.Item>
<Menu.Item key='Cut'>{'剪切'}</Menu.Item>
{cutId && <Menu.Item key='Paste'>{'粘贴'}</Menu.Item>}
<Menu.Item key='Delete'>{'删除'}</Menu.Item>
</>)
}
...
...
@@ -356,40 +385,40 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
{menuItems}
</Menu>
);
return rightClickMenu
? menu
: '';
return rightClickMenu
? menu
: '';
};
const getEmpty = () =>{
const empty = (<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}
><Button type="primary" onClick={() => {
const getEmpty = () =>
{
const empty = (<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}><Button type="primary" onClick={() => {
handleUpdateCatalogueModalVisible(true);
setIsCreate(true);
setCatalogueFormValues({
isLeaf:false,
parentId:0,
isLeaf:
false,
parentId:
0,
});
}}>创建目录</Button></Empty>);
return (treeData
&&treeData.length==0)?empty:
'';
return (treeData
&& treeData.length == 0) ? empty :
'';
};
const handleContextMenu = (e:any) => {
const handleContextMenu = (e:
any) => {
let position = e.event.currentTarget.getBoundingClientRect();
let scrollTop = document.documentElement.scrollTop;
setRightClickNode(e.node);
setRightClickNodeTreeItem({
pageX: e.event.pageX
-
40,
pageY: position.y
+sref.current.getScrollTop()+scrollTop-145-
position.height,
pageX: e.event.pageX
-
40,
pageY: position.y
+ sref.current.getScrollTop() + scrollTop - 145 -
position.height,
id: e.node.id,
categoryName: e.node.name
});
dispatch
&&
dispatch({
dispatch
&&
dispatch({
type: "Studio/showRightClickMenu",
payload: true,
});
};
//选中节点时触发
const onSelect = (selectedKeys:
Key[], e:
any) => {
if
(e.node&&
e.node.isLeaf) {
const onSelect = (selectedKeys:
Key[], e:
any) => {
if
(e.node &&
e.node.isLeaf) {
dispatch({
type: "Studio/saveCurrentPath",
payload: e.node.path,
...
...
@@ -398,18 +427,18 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
}
};
const offExpandAll = ()
=>
{
const offExpandAll = ()
=>
{
setExpandedKeys([]);
};
// 树节点展开/收缩
const onExpand
=(expandedKeys:Key[])=>
{
const onExpand
= (expandedKeys: Key[]) =>
{
setExpandedKeys(expandedKeys);
setAutoExpandParent(false)
};
const loop = (data:any) =>
data?.map((item:any) => {
const loop = (data:
any) =>
data?.map((item:
any) => {
const index = item.title.indexOf(searchValue);
const beforeStr = item.title.substr(0, index);
const afterStr = item.title.substr(index + searchValue.length);
...
...
@@ -425,109 +454,122 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
<span>{item.title}</span>
);
if (item.children) {
return {isLeaf:item.isLeaf,name:item.name,id:item.id,taskId:item.taskId,parentId:item.parentId,path:item.path,icon:item.isLeaf?item.icon:'', title, key: item.key, children: loop(item.children) };
return {
isLeaf: item.isLeaf,
name: item.name,
id: item.id,
taskId: item.taskId,
parentId: item.parentId,
path: item.path,
icon: item.isLeaf ? item.icon : '',
title,
key: item.key,
children: loop(item.children)
};
}
return {
isLeaf:item.isLeaf,
name:item.name,
id:item.id,
taskId:item.taskId,
parentId:item.parentId,
path:item.path,
icon:
item.isLeaf?item.icon:
'',
isLeaf:
item.isLeaf,
name:
item.name,
id:
item.id,
taskId:
item.taskId,
parentId:
item.parentId,
path:
item.path,
icon:
item.isLeaf ? item.icon :
'',
title,
key: item.key,
};
});
return (
<div className={style.tree_div}
>
<div className={style.tree_div}>
<Row>
<Col span={24}>
<Tooltip title="创建根目录">
<Button
type="text"
icon={<FolderAddOutlined
/>}
onClick={createRootCatalogue}
/>
</Tooltip>
<Tooltip title="折叠目录">
<Button
type="text"
icon={<SwitcherOutlined
/>}
onClick={offExpandAll}
/>
</Tooltip>
<Tooltip title="创建根目录">
<Button
type="text"
icon={<FolderAddOutlined
/>}
onClick={createRootCatalogue}
/>
</Tooltip>
<Tooltip title="折叠目录">
<Button
type="text"
icon={<SwitcherOutlined
/>}
onClick={offExpandAll}
/>
</Tooltip>
</Col>
</Row>
<Search style={{marginBottom: 8}} placeholder="Search" onChange={onChange} allowClear={true}/>
<Scrollbars
style={{height:(toolHeight-
72)}} ref={sref}>
<Scrollbars
style={{height: (toolHeight -
72)}} ref={sref}>
<DirectoryTree
multiple
onRightClick={handleContextMenu}
onSelect={onSelect}
switcherIcon={<DownOutlined/>}
treeData={loop(treeData)}
onExpand
={onExpand}
onExpand={onExpand}
autoExpandParent={autoExpandParent}
defaultExpandAll
expandedKeys={expandedKeys}
/>
{getNodeTreeRightClickMenu()}
{getEmpty()}
{updateCatalogueModalVisible? (
<UpdateCatalogueForm
onSubmit={async (value) => {
const success = await handleAddOrUpdate(
isCreate?'/api/catalogue':'/api/catalogue/toRename',value);
if (success) {
{getNodeTreeRightClickMenu()}
{getEmpty()}
{updateCatalogueModalVisible ? (
<UpdateCatalogueForm
onSubmit={async (value) => {
const success = await handleAddOrUpdate(
isCreate ? '/api/catalogue' : '/api/catalogue/toRename', value);
if (success) {
handleUpdateCatalogueModalVisible(false);
setCatalogueFormValues({});
getTreeData()
}
}}
onCancel={() => {
handleUpdateCatalogueModalVisible(false);
setCatalogueFormValues({});
getTreeData()
}
}}
onCancel={() => {
handleUpdateCatalogueModalVisible(false);
setCatalogueFormValues({});
}}
updateModalVisible={updateCatalogueModalVisible}
values={catalogueFormValues}
isCreate={isCreate}
/>
) : null}
{updateTaskModalVisible? (
<SimpleTaskForm
onSubmit={async (value) => {
const datas = await handleAddOrUpdateWithResult('/api/catalogue/createTask',value);
if (datas) {
}}
updateModalVisible={updateCatalogueModalVisible}
values={catalogueFormValues}
isCreate={isCreate}
/>
) : null}
{updateTaskModalVisible ? (
<SimpleTaskForm
onSubmit={async (value) => {
const datas = await handleAddOrUpdateWithResult('/api/catalogue/createTask', value);
if (datas) {
handleUpdateTaskModalVisible(false);
setTaskFormValues({});
openByKey(datas.id);
showEnv(dispatch);
}
}}
onCancel={() => {
handleUpdateTaskModalVisible(false);
setTaskFormValues({});
openByKey(datas.id);
showEnv(dispatch);
}
}}
onCancel={() => {
handleUpdateTaskModalVisible(false);
setTaskFormValues({});
}}
updateModalVisible={updateTaskModalVisible}
values={taskFormValues}
isCreate={isCreate}
/>
) : null}
}}
updateModalVisible={updateTaskModalVisible}
values={taskFormValues}
isCreate={isCreate}
/>
) : null}
</Scrollbars>
<UploadModal visible={isUploadModalVisible} action={`
/
api
/
catalogue
/
upload
/
$
{
uploadNodeId
}
`} handleOk={()
=>
{
<UploadModal visible={isUploadModalVisible} action={`
/
api
/
catalogue
/
upload
/
$
{
uploadNodeId
}
`} handleOk={()
=>
{
setIsUploadModalVisible(false);
setExpandedKeys(defaultExpandedKeys);
getTreeData();
}} onCancel={()=>{setIsUploadModalVisible(false)}} buttonTitle="上传zip包并创建工程" />
}} onCancel={() => {
setIsUploadModalVisible(false)
}} buttonTitle="上传zip包并创建工程"/>
</div>
);
};
export default connect(({Studio}: { Studio: StateType }) => ({
currentPath:Studio.currentPath,
currentPath:
Studio.currentPath,
tabs: Studio.tabs,
rightClickMenu: Studio.rightClickMenu,
refs: Studio.refs,
...
...
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