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
9bd1618c
Commit
9bd1618c
authored
Jun 03, 2021
by
godkaikai
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
预览数据
parent
9cb959f9
Changes
44
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
44 changed files
with
2187 additions
and
137 deletions
+2187
-137
ClusterController.java
...src/main/java/com/dlink/controller/ClusterController.java
+1
-2
DocumentController.java
...rc/main/java/com/dlink/controller/DocumentController.java
+84
-0
StudioController.java
.../src/main/java/com/dlink/controller/StudioController.java
+10
-0
StudioDDLDTO.java
dlink-admin/src/main/java/com/dlink/dto/StudioDDLDTO.java
+18
-0
DocumentMapper.java
...-admin/src/main/java/com/dlink/mapper/DocumentMapper.java
+16
-0
Document.java
dlink-admin/src/main/java/com/dlink/model/Document.java
+26
-0
DocumentService.java
...dmin/src/main/java/com/dlink/service/DocumentService.java
+13
-0
StudioService.java
...-admin/src/main/java/com/dlink/service/StudioService.java
+3
-0
DocumentServiceImpl.java
...main/java/com/dlink/service/impl/DocumentServiceImpl.java
+18
-0
StudioServiceImpl.java
...c/main/java/com/dlink/service/impl/StudioServiceImpl.java
+30
-4
DocumentMapper.xml
dlink-admin/src/main/resources/mapper/DocumentMapper.xml
+62
-0
ExecutorSetting.java
...ore/src/main/java/com/dlink/executor/ExecutorSetting.java
+4
-0
LocalStreamExecutor.java
...src/main/java/com/dlink/executor/LocalStreamExecutor.java
+6
-0
RemoteStreamExecutor.java
...rc/main/java/com/dlink/executor/RemoteStreamExecutor.java
+6
-0
JobManager.java
dlink-core/src/main/java/com/dlink/job/JobManager.java
+13
-4
RunResult.java
dlink-core/src/main/java/com/dlink/result/RunResult.java
+9
-0
dlink.sql
dlink-doc/sql/dlink.sql
+215
-0
routes.ts
dlink-web/config/routes.ts
+4
-4
index.tsx
dlink-web/src/components/Studio/StudioConnector/index.tsx
+199
-0
index.tsx
.../components/Studio/StudioConsole/StudioDocument/index.tsx
+292
-0
index.tsx
...c/components/Studio/StudioConsole/StudioHistory/index.tsx
+36
-0
index.tsx
...b/src/components/Studio/StudioConsole/StudioMsg/index.tsx
+21
-15
index.tsx
...src/components/Studio/StudioConsole/StudioTable/index.tsx
+93
-4
index.tsx
dlink-web/src/components/Studio/StudioConsole/index.tsx
+4
-2
completion.ts
dlink-web/src/components/Studio/StudioEdit/completion.ts
+36
-1
data.d.ts
dlink-web/src/components/Studio/StudioEdit/data.d.ts
+7
-0
index.tsx
dlink-web/src/components/Studio/StudioEdit/index.tsx
+5
-3
index.tsx
dlink-web/src/components/Studio/StudioFX/index.tsx
+199
-0
index.tsx
dlink-web/src/components/Studio/StudioMenu/index.tsx
+29
-34
index.tsx
dlink-web/src/components/Studio/StudioSetting/index.tsx
+2
-1
Function.ts
dlink-web/src/components/Studio/StudioTree/Function.ts
+0
-37
index.tsx
dlink-web/src/components/Studio/StudioTree/index.tsx
+5
-1
index.tsx
dlink-web/src/components/Studio/index.tsx
+2
-1
pages.ts
dlink-web/src/locales/zh-CN/pages.ts
+7
-5
Admin.tsx
dlink-web/src/pages/Admin.tsx
+1
-1
CreateForm.tsx
dlink-web/src/pages/Document/components/CreateForm.tsx
+25
-0
UpdateForm.tsx
dlink-web/src/pages/Document/components/UpdateForm.tsx
+164
-0
data.d.ts
dlink-web/src/pages/Document/data.d.ts
+13
-0
index.less
dlink-web/src/pages/Document/index.less
+0
-0
index.tsx
dlink-web/src/pages/Document/index.tsx
+454
-0
model.ts
dlink-web/src/pages/FlinkSqlStudio/model.ts
+11
-0
service.ts
dlink-web/src/pages/FlinkSqlStudio/service.ts
+9
-0
Welcome.tsx
dlink-web/src/pages/Welcome.tsx
+32
-15
index.tsx
dlink-web/src/pages/user/Login/index.tsx
+3
-3
No files found.
dlink-admin/src/main/java/com/dlink/controller/ClusterController.java
View file @
9bd1618c
...
...
@@ -53,7 +53,6 @@ public class ClusterController {
@DeleteMapping
public
Result
deleteMul
(
@RequestBody
JsonNode
para
)
{
if
(
para
.
size
()>
0
){
boolean
isAdmin
=
false
;
List
<
Integer
>
error
=
new
ArrayList
<>();
for
(
final
JsonNode
item
:
para
){
Integer
id
=
item
.
asInt
();
...
...
@@ -61,7 +60,7 @@ public class ClusterController {
error
.
add
(
id
);
}
}
if
(
error
.
size
()==
0
&&!
isAdmin
)
{
if
(
error
.
size
()==
0
)
{
return
Result
.
succeed
(
"删除成功"
);
}
else
{
return
Result
.
succeed
(
"删除部分成功,但"
+
error
.
toString
()+
"删除失败,共"
+
error
.
size
()+
"次失败。"
);
...
...
dlink-admin/src/main/java/com/dlink/controller/DocumentController.java
0 → 100644
View file @
9bd1618c
package
com
.
dlink
.
controller
;
import
com.dlink.common.result.ProTableResult
;
import
com.dlink.common.result.Result
;
import
com.dlink.model.Document
;
import
com.dlink.service.DocumentService
;
import
com.fasterxml.jackson.databind.JsonNode
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.DeleteMapping
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.PutMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* DocumentController
*
* @author wenmo
* @since 2021/6/3
**/
@Slf4j
@RestController
@RequestMapping
(
"/api/document"
)
public
class
DocumentController
{
@Autowired
private
DocumentService
documentService
;
/**
* 新增或者更新
*/
@PutMapping
public
Result
saveOrUpdate
(
@RequestBody
Document
document
)
throws
Exception
{
if
(
documentService
.
saveOrUpdate
(
document
)){
return
Result
.
succeed
(
"新增成功"
);
}
else
{
return
Result
.
failed
(
"新增失败"
);
}
}
/**
* 动态查询列表
*/
@PostMapping
public
ProTableResult
<
Document
>
listDocuments
(
@RequestBody
JsonNode
para
)
{
return
documentService
.
selectForProTable
(
para
);
}
/**
* 批量删除
*/
@DeleteMapping
public
Result
deleteMul
(
@RequestBody
JsonNode
para
)
{
if
(
para
.
size
()>
0
){
List
<
Integer
>
error
=
new
ArrayList
<>();
for
(
final
JsonNode
item
:
para
){
Integer
id
=
item
.
asInt
();
if
(!
documentService
.
removeById
(
id
)){
error
.
add
(
id
);
}
}
if
(
error
.
size
()==
0
)
{
return
Result
.
succeed
(
"删除成功"
);
}
else
{
return
Result
.
succeed
(
"删除部分成功,但"
+
error
.
toString
()+
"删除失败,共"
+
error
.
size
()+
"次失败。"
);
}
}
else
{
return
Result
.
failed
(
"请选择要删除的记录"
);
}
}
/**
* 获取指定ID的信息
*/
@PostMapping
(
"/getOneById"
)
public
Result
getOneById
(
@RequestBody
Document
document
)
throws
Exception
{
document
=
documentService
.
getById
(
document
.
getId
());
return
Result
.
succeed
(
document
,
"获取成功"
);
}
}
dlink-admin/src/main/java/com/dlink/controller/StudioController.java
View file @
9bd1618c
package
com
.
dlink
.
controller
;
import
com.dlink.common.result.Result
;
import
com.dlink.dto.StudioDDLDTO
;
import
com.dlink.dto.StudioExecuteDTO
;
import
com.dlink.model.Task
;
import
com.dlink.result.RunResult
;
...
...
@@ -34,4 +35,13 @@ public class StudioController {
RunResult
runResult
=
studioService
.
executeSql
(
studioExecuteDTO
);
return
Result
.
succeed
(
runResult
,
"执行成功"
);
}
/**
* 进行DDL操作
*/
@PostMapping
(
"/executeDDL"
)
public
Result
executeDDL
(
@RequestBody
StudioDDLDTO
studioDDLDTO
)
throws
Exception
{
RunResult
runResult
=
studioService
.
executeDDL
(
studioDDLDTO
);
return
Result
.
succeed
(
runResult
,
"执行成功"
);
}
}
dlink-admin/src/main/java/com/dlink/dto/StudioDDLDTO.java
0 → 100644
View file @
9bd1618c
package
com
.
dlink
.
dto
;
import
lombok.Getter
;
import
lombok.Setter
;
/**
* StudioDDLDTO
*
* @author wenmo
* @since 2021/6/3
*/
@Getter
@Setter
public
class
StudioDDLDTO
{
private
String
session
;
private
String
statement
;
private
Integer
clusterId
=
0
;
}
dlink-admin/src/main/java/com/dlink/mapper/DocumentMapper.java
0 → 100644
View file @
9bd1618c
package
com
.
dlink
.
mapper
;
import
com.dlink.db.mapper.SuperMapper
;
import
com.dlink.model.Document
;
import
org.apache.ibatis.annotations.Mapper
;
/**
* DocumentMapper
*
* @author wenmo
* @since 2021/6/3 14:31
**/
@Mapper
public
interface
DocumentMapper
extends
SuperMapper
<
Document
>
{
}
dlink-admin/src/main/java/com/dlink/model/Document.java
0 → 100644
View file @
9bd1618c
package
com
.
dlink
.
model
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
com.dlink.db.model.SuperEntity
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
/**
* Document
*
* @author wenmo
* @since 2021/6/3 14:27
**/
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@TableName
(
"dlink_flink_document"
)
public
class
Document
extends
SuperEntity
{
private
static
final
long
serialVersionUID
=
-
6340080980759236641L
;
private
String
category
;
private
String
type
;
private
String
subtype
;
private
String
description
;
private
String
version
;
private
String
likeNum
;
}
dlink-admin/src/main/java/com/dlink/service/DocumentService.java
0 → 100644
View file @
9bd1618c
package
com
.
dlink
.
service
;
import
com.dlink.db.service.ISuperService
;
import
com.dlink.model.Document
;
/**
* DocumentService
*
* @author wenmo
* @since 2021/6/3 14:35
**/
public
interface
DocumentService
extends
ISuperService
<
Document
>
{
}
dlink-admin/src/main/java/com/dlink/service/StudioService.java
View file @
9bd1618c
package
com
.
dlink
.
service
;
import
com.dlink.dto.StudioDDLDTO
;
import
com.dlink.dto.StudioExecuteDTO
;
import
com.dlink.result.RunResult
;
...
...
@@ -11,4 +12,6 @@ import com.dlink.result.RunResult;
*/
public
interface
StudioService
{
RunResult
executeSql
(
StudioExecuteDTO
studioExecuteDTO
);
RunResult
executeDDL
(
StudioDDLDTO
studioDDLDTO
);
}
dlink-admin/src/main/java/com/dlink/service/impl/DocumentServiceImpl.java
0 → 100644
View file @
9bd1618c
package
com
.
dlink
.
service
.
impl
;
import
com.dlink.db.service.impl.SuperServiceImpl
;
import
com.dlink.mapper.DocumentMapper
;
import
com.dlink.model.Document
;
import
com.dlink.service.DocumentService
;
import
org.springframework.stereotype.Service
;
/**
* DocumentServiceImpl
*
* @author wenmo
* @since 2021/6/3 14:36
**/
@Service
public
class
DocumentServiceImpl
extends
SuperServiceImpl
<
DocumentMapper
,
Document
>
implements
DocumentService
{
}
dlink-admin/src/main/java/com/dlink/service/impl/StudioServiceImpl.java
View file @
9bd1618c
...
...
@@ -2,6 +2,7 @@ package com.dlink.service.impl;
import
com.dlink.assertion.Assert
;
import
com.dlink.cluster.FlinkCluster
;
import
com.dlink.dto.StudioDDLDTO
;
import
com.dlink.dto.StudioExecuteDTO
;
import
com.dlink.executor.Executor
;
import
com.dlink.executor.ExecutorSetting
;
...
...
@@ -27,13 +28,38 @@ public class StudioServiceImpl implements StudioService {
@Override
public
RunResult
executeSql
(
StudioExecuteDTO
studioExecuteDTO
)
{
studioExecuteDTO
.
setSession
(
studioExecuteDTO
.
getClusterId
()+
"_"
+
studioExecuteDTO
.
getSession
());
String
ExecuteType
=
Executor
.
REMOTE
;
String
host
=
null
;
Cluster
cluster
=
clusterService
.
getById
(
studioExecuteDTO
.
getClusterId
());
Assert
.
check
(
cluster
);
String
host
=
FlinkCluster
.
testFlinkJobManagerIP
(
cluster
.
getHosts
(),
cluster
.
getJobManagerHost
());
Assert
.
checkHost
(
host
);
if
(
studioExecuteDTO
.
getClusterId
()==
0
&&
cluster
==
null
){
ExecuteType
=
Executor
.
LOCAL
;
}
else
{
Assert
.
check
(
cluster
);
host
=
FlinkCluster
.
testFlinkJobManagerIP
(
cluster
.
getHosts
(),
cluster
.
getJobManagerHost
());
Assert
.
checkHost
(
host
);
}
JobManager
jobManager
=
new
JobManager
(
host
,
studioExecuteDTO
.
getSession
(),
studioExecuteDTO
.
getMaxRowNum
());
return
jobManager
.
execute
(
studioExecuteDTO
.
getStatement
(),
new
ExecutorSetting
(
Execut
or
.
REMOTE
,
studioExecuteDTO
.
getCheckPoint
(),
studioExecuteDTO
.
getParallelism
(),
Execut
eType
,
studioExecuteDTO
.
getCheckPoint
(),
studioExecuteDTO
.
getParallelism
(),
studioExecuteDTO
.
isFragment
(),
studioExecuteDTO
.
getSavePointPath
()));
}
@Override
public
RunResult
executeDDL
(
StudioDDLDTO
studioDDLDTO
)
{
studioDDLDTO
.
setSession
(
studioDDLDTO
.
getClusterId
()+
"_"
+
studioDDLDTO
.
getSession
());
String
ExecuteType
=
Executor
.
REMOTE
;
String
host
=
null
;
Cluster
cluster
=
clusterService
.
getById
(
studioDDLDTO
.
getClusterId
());
if
(
studioDDLDTO
.
getClusterId
()==
0
&&
cluster
==
null
){
ExecuteType
=
Executor
.
LOCAL
;
}
else
{
Assert
.
check
(
cluster
);
host
=
FlinkCluster
.
testFlinkJobManagerIP
(
cluster
.
getHosts
(),
cluster
.
getJobManagerHost
());
Assert
.
checkHost
(
host
);
}
JobManager
jobManager
=
new
JobManager
(
host
,
studioDDLDTO
.
getSession
(),
1000
);
return
jobManager
.
execute
(
studioDDLDTO
.
getStatement
(),
new
ExecutorSetting
(
ExecuteType
));
}
}
dlink-admin/src/main/resources/mapper/DocumentMapper.xml
0 → 100644
View file @
9bd1618c
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"com.dlink.mapper.DocumentMapper"
>
<!-- 通用查询映射结果 -->
<resultMap
id=
"BaseResultMap"
type=
"com.dlink.model.Document"
>
<id
column=
"id"
property=
"id"
/>
<result
column=
"name"
property=
"name"
/>
<result
column=
"category"
property=
"category"
/>
<result
column=
"type"
property=
"type"
/>
<result
column=
"subtype"
property=
"subtype"
/>
<result
column=
"description"
property=
"description"
/>
<result
column=
"version"
property=
"version"
/>
<result
column=
"like_num"
property=
"likeNum"
/>
<result
column=
"enabled"
property=
"enabled"
/>
<result
column=
"create_time"
property=
"createTime"
/>
<result
column=
"update_time"
property=
"updateTime"
/>
</resultMap>
<!-- 通用查询结果列 -->
<sql
id=
"Base_Column_List"
>
id, name, category, type,subtype,description, version,like_num, enabled, create_time, update_time
</sql>
<select
id=
"selectForProTable"
resultType=
"com.dlink.model.Document"
>
select
a.*
from
dlink_flink_document a
<where>
1=1
<if
test=
'param.name!=null and param.name!=""'
>
and a.name like "%${param.name}%"
</if>
<if
test=
'param.description!=null and param.description!=""'
>
and a.description like "%${param.description}%"
</if>
<if
test=
'param.type!=null and param.type!=""'
>
and a.type = #{param.type}
</if>
<if
test=
'param.subtype!=null and param.subtype!=""'
>
and a.subtype = #{param.subtype}
</if>
<if
test=
'param.version!=null and param.version!=""'
>
and a.version = #{param.version}
</if>
<if
test=
'param.createTime!=null and param.createTime!=""'
>
and a.create_time
<![CDATA[>=]]>
str_to_date( #{param.createTime},'%Y-%m-%d %H:%i:%s')
</if>
<if
test=
'param.updateTime!=null and param.updateTime!=""'
>
and a.update_time
<![CDATA[>=]]>
str_to_date( #{param.updateTime},'%Y-%m-%d %H:%i:%s')
</if>
<if
test=
'ew.sqlSegment!=null and ew.sqlSegment!="" and !ew.sqlSegment.startsWith(" ORDER BY")'
>
and
</if>
<if
test=
'ew.sqlSegment!=null and ew.sqlSegment!=""'
>
${ew.sqlSegment}
</if>
</where>
</select>
</mapper>
dlink-core/src/main/java/com/dlink/executor/ExecutorSetting.java
View file @
9bd1618c
...
...
@@ -36,6 +36,10 @@ public class ExecutorSetting {
this
.
savePointPath
=
savePointPath
;
}
public
boolean
isRemote
(){
return
type
.
equals
(
Executor
.
REMOTE
);
}
public
String
getType
()
{
return
type
;
}
...
...
dlink-core/src/main/java/com/dlink/executor/LocalStreamExecutor.java
View file @
9bd1618c
...
...
@@ -26,6 +26,12 @@ public class LocalStreamExecutor extends Executor {
public
LocalStreamExecutor
(
ExecutorSetting
executorSetting
)
{
this
.
executorSetting
=
executorSetting
;
this
.
environment
=
StreamExecutionEnvironment
.
createLocalEnvironment
();
if
(
executorSetting
.
getCheckpoint
()!=
null
&&
executorSetting
.
getCheckpoint
()>
0
){
environment
.
enableCheckpointing
(
executorSetting
.
getCheckpoint
());
}
if
(
executorSetting
.
getParallelism
()!=
null
&&
executorSetting
.
getParallelism
()>
0
){
environment
.
setParallelism
(
executorSetting
.
getParallelism
());
}
stEnvironment
=
CustomTableEnvironmentImpl
.
create
(
environment
);
if
(
executorSetting
.
isUseSqlFragment
()){
stEnvironment
.
useSqlFragment
();
...
...
dlink-core/src/main/java/com/dlink/executor/RemoteStreamExecutor.java
View file @
9bd1618c
...
...
@@ -30,6 +30,12 @@ public class RemoteStreamExecutor extends Executor {
this
.
executorSetting
=
executorSetting
;
synchronized
(
RemoteStreamExecutor
.
class
){
this
.
environment
=
StreamExecutionEnvironment
.
createRemoteEnvironment
(
environmentSetting
.
getHost
(),
environmentSetting
.
getPort
());
if
(
executorSetting
.
getCheckpoint
()!=
null
&&
executorSetting
.
getCheckpoint
()>
0
){
environment
.
enableCheckpointing
(
executorSetting
.
getCheckpoint
());
}
if
(
executorSetting
.
getParallelism
()!=
null
&&
executorSetting
.
getParallelism
()>
0
){
environment
.
setParallelism
(
executorSetting
.
getParallelism
());
}
if
(
stEnvironment
==
null
){
stEnvironment
=
CustomTableEnvironmentImpl
.
create
(
environment
);
}
...
...
dlink-core/src/main/java/com/dlink/job/JobManager.java
View file @
9bd1618c
...
...
@@ -78,7 +78,11 @@ public class JobManager {
if
(
executorEntity
!=
null
)
{
executor
=
executorEntity
.
getExecutor
();
}
else
{
executor
=
Executor
.
build
(
new
EnvironmentSetting
(
flinkHost
,
FlinkConstant
.
PORT
),
executorSetting
);
if
(
executorSetting
.
isRemote
())
{
executor
=
Executor
.
build
(
new
EnvironmentSetting
(
flinkHost
,
FlinkConstant
.
PORT
),
executorSetting
);
}
else
{
executor
=
Executor
.
build
(
null
,
executorSetting
);
}
SessionPool
.
push
(
new
ExecutorEntity
(
sessionId
,
executor
));
}
String
[]
Statements
=
statement
.
split
(
";"
);
...
...
@@ -99,18 +103,23 @@ public class JobManager {
runResult
.
setResult
(
result
);
runResult
.
setTime
(
timeElapsed
);
runResult
.
setFinishDate
(
LocalDateTime
.
now
());
runResult
.
setSuccess
(
true
);
if
(
tableResult
.
getJobClient
().
isPresent
())
{
runResult
.
setJobId
(
tableResult
.
getJobClient
().
get
().
getJobID
().
toString
());
runResult
.
setSuccess
(
tableResult
.
getJobClient
().
get
().
getJobStatus
().
isDone
());
}
else
{
runResult
.
setSuccess
(
true
);
}
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
StackTraceElement
[]
trace
=
e
.
getStackTrace
();
StringBuffer
resMsg
=
new
StringBuffer
(
""
);
for
(
StackTraceElement
s
:
trace
)
{
resMsg
.
append
(
"
</br>
"
+
s
+
" "
);
resMsg
.
append
(
"
\n
"
+
s
+
" "
);
}
runResult
.
setFinishDate
(
LocalDateTime
.
now
());
runResult
.
setSuccess
(
false
);
runResult
.
setError
(
LocalDateTime
.
now
().
toString
()
+
":"
+
"运行第"
+
currentIndex
+
"行sql时出现异常:"
+
e
.
getMessage
()
+
"
</br>
>>>堆栈信息<<<"
+
resMsg
.
toString
());
runResult
.
setError
(
LocalDateTime
.
now
().
toString
()
+
":"
+
"运行第"
+
currentIndex
+
"行sql时出现异常:"
+
e
.
getMessage
()
+
"
\n
>>>堆栈信息<<<"
+
resMsg
.
toString
());
return
runResult
;
}
return
runResult
;
...
...
dlink-core/src/main/java/com/dlink/result/RunResult.java
View file @
9bd1618c
...
...
@@ -12,6 +12,7 @@ import java.time.LocalDateTime;
**/
public
class
RunResult
{
private
String
sessionId
;
private
String
jobId
;
private
String
statement
;
private
String
flinkHost
;
private
Integer
flinkPort
;
...
...
@@ -34,6 +35,14 @@ public class RunResult {
this
.
setting
=
setting
;
}
public
String
getJobId
()
{
return
jobId
;
}
public
void
setJobId
(
String
jobId
)
{
this
.
jobId
=
jobId
;
}
public
ExecutorSetting
getSetting
()
{
return
setting
;
}
...
...
dlink-doc/sql/dlink.sql
View file @
9bd1618c
This diff is collapsed.
Click to expand it.
dlink-web/config/routes.ts
View file @
9bd1618c
...
...
@@ -45,7 +45,7 @@ export default [
icon
:
'cluster'
,
component
:
'./Cluster'
,
},
{
/*
{
path: '/dev',
name: 'dev',
icon: 'crown',
...
...
@@ -78,7 +78,7 @@ export default [
],
},
],
},
},
*/
/*{
path: '/admin',
name: 'admin',
...
...
@@ -94,7 +94,7 @@ export default [
},
],
},*/
{
/*
{
path: '/demo',
name: 'demo',
icon: 'crown',
...
...
@@ -139,7 +139,7 @@ export default [
],
},
],
},
},
*/
{
path
:
'/'
,
redirect
:
'/welcome'
,
...
...
dlink-web/src/components/Studio/StudioConnector/index.tsx
0 → 100644
View file @
9bd1618c
import
{
message
,
Input
,
Button
,
Space
,
Table
,
Dropdown
,
Menu
,
Empty
,
Divider
}
from
"antd"
;
import
{
StateType
}
from
"@/pages/FlinkSqlStudio/model"
;
import
{
connect
}
from
"umi"
;
import
{
useState
}
from
"react"
;
// import Highlighter from 'react-highlight-words';
import
{
SearchOutlined
,
DownOutlined
,
TableOutlined
}
from
'@ant-design/icons'
;
import
React
from
"react"
;
import
{
executeDDL
}
from
"@/pages/FlinkSqlStudio/service"
;
const
StudioConnector
=
(
props
:
any
)
=>
{
const
{
current
}
=
props
;
const
[
tableData
,
setTableData
]
=
useState
<
[]
>
([]);
const
[
loadings
,
setLoadings
]
=
useState
<
boolean
[]
>
([]);
const
[
searchText
,
setSearchText
]
=
useState
<
string
>
(
''
);
const
[
searchedColumn
,
setSearchedColumn
]
=
useState
<
string
>
(
''
);
const
getColumnSearchProps
=
(
dIndex
)
=>
({
filterDropdown
:
({
setSelectedKeys
,
selectedKeys
,
confirm
,
clearFilters
})
=>
(
<
div
style=
{
{
padding
:
8
}
}
>
<
Input
placeholder=
{
`Search ${dIndex}`
}
value=
{
selectedKeys
[
0
]
}
onChange=
{
e
=>
setSelectedKeys
(
e
.
target
.
value
?
[
e
.
target
.
value
]
:
[])
}
onPressEnter=
{
()
=>
handleSearch
(
selectedKeys
,
confirm
,
dIndex
)
}
style=
{
{
marginBottom
:
8
,
display
:
'block'
}
}
/>
<
Space
>
<
Button
type=
"primary"
onClick=
{
()
=>
handleSearch
(
selectedKeys
,
confirm
,
dIndex
)
}
icon=
{
<
SearchOutlined
/>
}
size=
"small"
style=
{
{
width
:
90
}
}
>
搜索
</
Button
>
<
Button
onClick=
{
()
=>
handleReset
(
clearFilters
)
}
size=
"small"
style=
{
{
width
:
90
}
}
>
重置
</
Button
>
<
Button
type=
"link"
size=
"small"
onClick=
{
()
=>
{
setSearchText
(
selectedKeys
[
0
]);
setSearchedColumn
(
dIndex
);
}
}
>
过滤
</
Button
>
</
Space
>
</
div
>
),
filterIcon
:
filtered
=>
<
SearchOutlined
style=
{
{
color
:
filtered
?
'#1890ff'
:
undefined
}
}
/>,
onFilter
:
(
value
,
record
)
=>
record
[
dIndex
]
?
record
[
dIndex
].
toString
().
toLowerCase
().
includes
(
value
.
toLowerCase
())
:
''
,
/*render: text =>
searchedColumn === dIndex ? (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[searchText]}
autoEscape
textToHighlight={text ? text.toString() : ''}
/>
) : (
text
),*/
});
const
handleSearch
=
(
selectedKeys
,
confirm
,
dIndex
)
=>
{
confirm
();
setSearchText
(
selectedKeys
[
0
]);
setSearchedColumn
(
dIndex
);
};
const
handleReset
=
(
clearFilters
)
=>
{
clearFilters
();
setSearchText
(
''
);
};
const
MoreBtn
:
React
.
FC
<
{
item
:
any
}
>
=
({
item
})
=>
(
<
Dropdown
overlay=
{
<
Menu
onClick=
{
({
key
})
=>
keyEvent
(
key
,
item
)
}
>
<
Menu
.
Item
key=
"desc"
>
描述
</
Menu
.
Item
>
<
Menu
.
Item
key=
"delete"
>
删除
</
Menu
.
Item
>
</
Menu
>
}
>
<
a
>
更多
<
DownOutlined
/>
</
a
>
</
Dropdown
>
);
const
keyEvent
=
(
key
,
item
)
=>
{
if
(
key
==
'delete'
){
let
newLoadings
=
[...
loadings
];
newLoadings
[
1
]
=
true
;
setLoadings
(
newLoadings
);
const
res
=
executeDDL
({
statement
:
"drop table "
+
item
.
tablename
,
clusterId
:
current
.
task
.
clusterId
,
session
:
current
.
task
.
session
,
});
res
.
then
((
result
)
=>
{
if
(
result
.
datas
.
success
){
let
newTableData
=
tableData
;
for
(
let
i
=
0
;
i
<
newTableData
.
length
;
i
++
)
{
if
(
newTableData
[
i
].
tablename
==
item
.
tablename
)
{
newTableData
.
splice
(
i
,
1
);
setTableData
(
newTableData
);
break
;
}
}
}
let
newLoadings
=
[...
loadings
];
newLoadings
[
1
]
=
false
;
setLoadings
(
newLoadings
);
});
}
else
{
message
.
warn
(
"敬请期待"
);
}
};
const
getTables
=
()
=>
{
let
newLoadings
=
[...
loadings
];
newLoadings
[
0
]
=
true
;
setLoadings
(
newLoadings
);
const
res
=
executeDDL
({
statement
:
"show tables"
,
clusterId
:
current
.
task
.
clusterId
,
session
:
current
.
task
.
session
,
});
res
.
then
((
result
)
=>
{
if
(
result
.
datas
.
result
.
rowData
.
length
>
0
){
setTableData
(
result
.
datas
.
result
.
rowData
);
}
else
{
setTableData
([]);
}
let
newLoadings
=
[...
loadings
];
newLoadings
[
0
]
=
false
;
setLoadings
(
newLoadings
);
});
};
const
getColumns
=
()
=>
{
let
columns
:
any
=
[{
title
:
"表名"
,
dataIndex
:
"tablename"
,
key
:
"tablename"
,
sorter
:
true
,
...
getColumnSearchProps
(
"tablename"
),
},{
title
:
'操作'
,
dataIndex
:
'option'
,
valueType
:
'option'
,
render
:
(
_
,
record
)
=>
[
<
a
onClick=
{
()
=>
{
message
.
warn
(
'敬请期待'
);
}
}
>
描述
</
a
>,<
Divider
type=
"vertical"
/>,<
a
onClick=
{
()
=>
{
keyEvent
(
'delete'
,
record
);
}
}
>
删除
</
a
>
],
},];
return
columns
;
};
return
(
<>
<
Button
type=
"primary"
icon=
{
<
TableOutlined
/>
}
loading=
{
loadings
[
0
]
}
onClick=
{
()
=>
getTables
()
}
>
获取Connectors
</
Button
>
{
tableData
.
length
>
0
?(<
Table
dataSource=
{
tableData
}
columns=
{
getColumns
()
}
/>):(<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>)
}
</>
);
};
export
default
connect
(({
Studio
}:
{
Studio
:
StateType
})
=>
({
current
:
Studio
.
current
,
}))(
StudioConnector
);
dlink-web/src/components/Studio/StudioConsole/StudioDocument/index.tsx
0 → 100644
View file @
9bd1618c
import
{
DownOutlined
,
HeartOutlined
,
PlusOutlined
,
UserOutlined
}
from
'@ant-design/icons'
;
import
{
Button
,
message
,
Input
,
Drawer
,
Modal
}
from
'antd'
;
import
React
,
{
useState
,
useRef
}
from
'react'
;
import
{
PageContainer
,
FooterToolbar
}
from
'@ant-design/pro-layout'
;
import
type
{
ProColumns
,
ActionType
}
from
'@ant-design/pro-table'
;
import
ProTable
from
'@ant-design/pro-table'
;
import
ProDescriptions
from
'@ant-design/pro-descriptions'
;
import
type
{
DocumentTableListItem
}
from
'@/pages/Document/data.d'
;
import
{
queryData
,}
from
"@/components/Common/crud"
;
import
{
connect
}
from
"umi"
;
import
{
StateType
}
from
"@/pages/FlinkSqlStudio/model"
;
const
url
=
'/api/document'
;
const
StudioDocument
=
()
=>
{
const
actionRef
=
useRef
<
ActionType
>
();
const
[
row
,
setRow
]
=
useState
<
DocumentTableListItem
>
();
const
columns
:
ProColumns
<
DocumentTableListItem
>
[]
=
[
{
title
:
'名称'
,
dataIndex
:
'name'
,
tip
:
'名称是唯一的'
,
sorter
:
true
,
width
:
'400px'
,
formItemProps
:
{
rules
:
[
{
required
:
true
,
message
:
'名称为必填项'
,
},
],
},
render
:
(
dom
,
entity
)
=>
{
return
<
a
onClick=
{
()
=>
setRow
(
entity
)
}
>
{
dom
}
</
a
>;
},
},
{
title
:
'文档ID'
,
dataIndex
:
'id'
,
hideInTable
:
true
,
hideInForm
:
true
,
hideInSearch
:
true
,
},
{
title
:
'文档类型'
,
sorter
:
true
,
dataIndex
:
'category'
,
hideInForm
:
false
,
hideInSearch
:
true
,
hideInTable
:
true
,
filters
:
[
{
text
:
'函数'
,
value
:
'function'
,
}
],
filterMultiple
:
false
,
valueEnum
:
{
'function'
:
{
text
:
'函数'
},
},
},
{
title
:
'类型'
,
sorter
:
true
,
dataIndex
:
'type'
,
hideInForm
:
false
,
hideInSearch
:
true
,
hideInTable
:
false
,
filters
:
[
{
text
:
'内置函数'
,
value
:
'内置函数'
,
},
{
text
:
'UDF'
,
value
:
'UDF'
,
},
],
filterMultiple
:
false
,
valueEnum
:
{
'内置函数'
:
{
text
:
'内置函数'
},
'UDF'
:
{
text
:
'UDF'
},
},
},
{
title
:
'子类型'
,
sorter
:
true
,
dataIndex
:
'subtype'
,
hideInForm
:
false
,
hideInSearch
:
true
,
hideInTable
:
false
,
filters
:
[
{
text
:
'比较函数'
,
value
:
'比较函数'
,
},
{
text
:
'逻辑函数'
,
value
:
'逻辑函数'
,
},{
text
:
'算术函数'
,
value
:
'算术函数'
,
},{
text
:
'字符串函数'
,
value
:
'字符串函数'
,
},{
text
:
'时间函数'
,
value
:
'时间函数'
,
},{
text
:
'条件函数'
,
value
:
'条件函数'
,
},{
text
:
'类型转换函数'
,
value
:
'类型转换函数'
,
},{
text
:
'Collection 函数'
,
value
:
'Collection 函数'
,
},{
text
:
'Value Collection 函数'
,
value
:
'Value Collection 函数'
,
},{
text
:
'Value Access 函数'
,
value
:
'Value Access 函数'
,
},{
text
:
'分组函数'
,
value
:
'分组函数'
,
},{
text
:
'hash函数'
,
value
:
'hash函数'
,
},{
text
:
'聚合函数'
,
value
:
'聚合函数'
,
},{
text
:
'列函数'
,
value
:
'列函数'
,
},{
text
:
'表值聚合函数'
,
value
:
'表值聚合函数'
,
},{
text
:
'其他函数'
,
value
:
'其他函数'
,
},
],
filterMultiple
:
false
,
valueEnum
:
{
'比较函数'
:
{
text
:
'比较函数'
},
'逻辑函数'
:
{
text
:
'逻辑函数'
},
'算术函数'
:
{
text
:
'算术函数'
},
'字符串函数'
:
{
text
:
'字符串函数'
},
'时间函数'
:
{
text
:
'时间函数'
},
'条件函数'
:
{
text
:
'条件函数'
},
'类型转换函数'
:
{
text
:
'类型转换函数'
},
'Collection 函数'
:
{
text
:
'Collection 函数'
},
'Value Collection 函数'
:
{
text
:
'Value Collection 函数'
},
'Value Access 函数'
:
{
text
:
'Value Access 函数'
},
'分组函数'
:
{
text
:
'分组函数'
},
'hash函数'
:
{
text
:
'hash函数'
},
'聚合函数'
:
{
text
:
'聚合函数'
},
'列函数'
:
{
text
:
'列函数'
},
'表值聚合函数'
:
{
text
:
'表值聚合函数'
},
'其他函数'
:
{
text
:
'其他函数'
},
},
},
{
title
:
'描述'
,
sorter
:
true
,
dataIndex
:
'description'
,
valueType
:
'textarea'
,
hideInForm
:
false
,
hideInSearch
:
false
,
hideInTable
:
false
,
width
:
'400px'
,
},
{
title
:
'版本'
,
sorter
:
true
,
dataIndex
:
'version'
,
hideInForm
:
true
,
hideInSearch
:
true
,
hideInTable
:
true
,
},{
title
:
'赞'
,
sorter
:
true
,
dataIndex
:
'likeNum'
,
hideInForm
:
true
,
hideInSearch
:
true
,
hideInTable
:
false
,
},
{
title
:
'是否启用'
,
dataIndex
:
'enabled'
,
hideInForm
:
true
,
hideInSearch
:
true
,
hideInTable
:
true
,
filters
:
[
{
text
:
'已启用'
,
value
:
1
,
},
{
text
:
'已禁用'
,
value
:
0
,
},
],
filterMultiple
:
false
,
valueEnum
:
{
true
:
{
text
:
'已启用'
,
status
:
'Success'
},
false
:
{
text
:
'已禁用'
,
status
:
'Error'
},
},
},
{
title
:
'创建时间'
,
dataIndex
:
'createTime'
,
sorter
:
true
,
valueType
:
'dateTime'
,
hideInForm
:
true
,
hideInTable
:
true
,
hideInSearch
:
true
,
renderFormItem
:
(
item
,
{
defaultRender
,
...
rest
},
form
)
=>
{
const
status
=
form
.
getFieldValue
(
'status'
);
if
(
`
${
status
}
`
===
'0'
)
{
return
false
;
}
if
(
`
${
status
}
`
===
'3'
)
{
return
<
Input
{
...
rest
}
placeholder=
"请输入异常原因!"
/>;
}
return
defaultRender
(
item
);
},
},
{
title
:
'最近更新时间'
,
dataIndex
:
'updateTime'
,
sorter
:
true
,
valueType
:
'dateTime'
,
hideInForm
:
true
,
hideInTable
:
true
,
hideInSearch
:
true
,
renderFormItem
:
(
item
,
{
defaultRender
,
...
rest
},
form
)
=>
{
const
status
=
form
.
getFieldValue
(
'status'
);
if
(
`
${
status
}
`
===
'0'
)
{
return
false
;
}
if
(
`
${
status
}
`
===
'3'
)
{
return
<
Input
{
...
rest
}
placeholder=
"请输入异常原因!"
/>;
}
return
defaultRender
(
item
);
},
},
];
return
(
<>
<
ProTable
<
DocumentTableListItem
>
headerTitle="文档管理"
actionRef=
{
actionRef
}
rowKey="id"
search=
{
{
labelWidth
:
120
,
}
}
request=
{
(
params
,
sorter
,
filter
)
=>
queryData
(
url
,{...
params
,
sorter
,
filter
})
}
columns=
{
columns
}
/
>
<
Drawer
width=
{
600
}
visible=
{
!!
row
}
onClose=
{
()
=>
{
setRow
(
undefined
);
}
}
closable=
{
false
}
>
{
row
?.
name
&&
(
<
ProDescriptions
<
DocumentTableListItem
>
column=
{
2
}
title=
{
row
?.
name
}
request=
{
async
()
=>
({
data
:
row
||
{},
})
}
params=
{
{
id
:
row
?.
name
,
}
}
columns=
{
columns
}
/
>
)
}
</
Drawer
>
</>
);
};
export default connect((
{
Studio
}
:
{
Studio
:
StateType
}
) =
>
(
{
current
:
Studio
.
current
,
}
))(StudioDocument);
dlink-web/src/components/Studio/StudioConsole/StudioHistory/index.tsx
0 → 100644
View file @
9bd1618c
import
{
Typography
,
Divider
,
Badge
,
Empty
}
from
"antd"
;
import
{
StateType
}
from
"@/pages/FlinkSqlStudio/model"
;
import
{
connect
}
from
"umi"
;
const
{
Title
,
Paragraph
,
Text
,
Link
}
=
Typography
;
const
StudioHistory
=
(
props
:
any
)
=>
{
const
{
current
}
=
props
;
return
(
<
Typography
>
{
current
.
console
.
result
.
map
((
item
)
=>
{
return
(<
Paragraph
>
<
blockquote
><
Link
href=
{
`http://${item.flinkHost}:${item.flinkPort}`
}
target=
"_blank"
>
[
{
item
.
sessionId
}
:
{
item
.
flinkHost
}
:
{
item
.
flinkPort
}
]
</
Link
>
<
Divider
type=
"vertical"
/>
{
item
.
finishDate
}
<
Divider
type=
"vertical"
/>
{
!
item
.
success
?
<><
Badge
status=
"error"
/><
Text
type=
"danger"
>
Error
</
Text
></>
:
<><
Badge
status=
"success"
/><
Text
type=
"success"
>
Success
</
Text
></>
}
<
Divider
type=
"vertical"
/>
{
item
.
jobId
&&
<
Text
code
>
{
item
.
jobId
}
</
Text
>
}
<
Text
keyboard
>
{
item
.
time
}
ms
</
Text
></
blockquote
>
{
item
.
statement
&&
(<
pre
style=
{
{
height
:
'40px'
}
}
>
{
item
.
statement
}
</
pre
>)
}
{
item
.
msg
?
item
.
msg
:
''
}
{
item
.
error
&&
(<
pre
style=
{
{
height
:
'100px'
}
}
>
{
item
.
error
}
</
pre
>)
}
</
Paragraph
>)
})
}
{
current
.
console
.
result
.
length
==
0
?<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>:
''
}
</
Typography
>
);
};
export
default
connect
(({
Studio
}:
{
Studio
:
StateType
})
=>
({
current
:
Studio
.
current
,
}))(
StudioHistory
);
dlink-web/src/components/Studio/StudioConsole/StudioMsg/index.tsx
View file @
9bd1618c
import
{
Typography
,
Divider
,
Badge
}
from
"antd"
;
import
{
Typography
,
Divider
,
Badge
,
Empty
}
from
"antd"
;
import
{
StateType
}
from
"@/pages/FlinkSqlStudio/model"
;
import
{
connect
}
from
"umi"
;
...
...
@@ -10,21 +10,27 @@ const StudioMsg = (props:any) => {
return
(
<
Typography
>
{
current
.
console
.
result
.
map
((
item
)
=>
{
return
(<
Paragraph
>
<
blockquote
><
Link
href=
{
`http://${item.flinkHost}:${item.flinkPort}`
}
target=
"_blank"
>
[
{
item
.
sessionId
}
:
{
item
.
flinkHost
}
:
{
item
.
flinkPort
}
]
</
Link
>
<
Divider
type=
"vertical"
/>
{
item
.
finishDate
}
<
Divider
type=
"vertical"
/>
{
!
item
.
success
?
<><
Badge
status=
"error"
/><
Text
type=
"danger"
>
Error
</
Text
></>
:
<><
Badge
status=
"success"
/><
Text
type=
"success"
>
Success
</
Text
></>
}
<
Divider
type=
"vertical"
/>
<
Text
keyboard
>
{
item
.
time
}
ms
</
Text
></
blockquote
>
{
item
.
statement
&&
(<
pre
style=
{
{
height
:
'40px'
}
}
>
{
item
.
statement
}
</
pre
>)
}
{
item
.
msg
?
item
.
msg
:
''
}
{
item
.
error
&&
(<
pre
style=
{
{
height
:
'100px'
}
}
>
{
item
.
error
}
</
pre
>)
}
</
Paragraph
>)
{
current
.
console
.
result
.
map
((
item
,
index
)
=>
{
if
(
index
==
0
)
{
return
(<
Paragraph
>
<
blockquote
><
Link
href=
{
`http://${item.flinkHost}:${item.flinkPort}`
}
target=
"_blank"
>
[
{
item
.
sessionId
}
:
{
item
.
flinkHost
}
:
{
item
.
flinkPort
}
]
</
Link
>
<
Divider
type=
"vertical"
/>
{
item
.
finishDate
}
<
Divider
type=
"vertical"
/>
{
!
item
.
success
?
<><
Badge
status=
"error"
/><
Text
type=
"danger"
>
Error
</
Text
></>
:
<><
Badge
status=
"success"
/><
Text
type=
"success"
>
Success
</
Text
></>
}
<
Divider
type=
"vertical"
/>
{
item
.
jobId
&&
<
Text
code
>
{
item
.
jobId
}
</
Text
>
}
<
Text
keyboard
>
{
item
.
time
}
ms
</
Text
></
blockquote
>
{
item
.
statement
&&
(<
pre
style=
{
{
height
:
'100px'
}
}
>
{
item
.
statement
}
</
pre
>)
}
{
item
.
msg
?
item
.
msg
:
''
}
{
item
.
error
&&
(<
pre
style=
{
{
height
:
'100px'
}
}
>
{
item
.
error
}
</
pre
>)
}
</
Paragraph
>)
}
else
{
return
''
;
}
})
}
{
current
.
console
.
result
.
length
==
0
?<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>:
''
}
</
Typography
>
);
};
...
...
dlink-web/src/components/Studio/StudioConsole/StudioTable/index.tsx
View file @
9bd1618c
import
{
Typography
,
Divider
,
Badge
,
Select
,
Tag
,
Form
}
from
"antd"
;
import
{
Typography
,
Input
,
Button
,
Space
,
Table
,
Select
,
Tag
,
Form
,
Empty
}
from
"antd"
;
import
{
StateType
}
from
"@/pages/FlinkSqlStudio/model"
;
import
{
connect
}
from
"umi"
;
import
{
useState
}
from
"react"
;
// import Highlighter from 'react-highlight-words';
import
{
SearchOutlined
}
from
'@ant-design/icons'
;
const
{
Option
}
=
Select
;
const
{
Title
,
Paragraph
,
Text
,
Link
}
=
Typography
;
...
...
@@ -9,16 +12,101 @@ const { Title, Paragraph, Text, Link } = Typography;
const
StudioTable
=
(
props
:
any
)
=>
{
const
{
current
}
=
props
;
const
[
dataIndex
,
setDataIndex
]
=
useState
<
number
>
(
0
);
const
[
searchText
,
setSearchText
]
=
useState
<
string
>
(
''
);
const
[
searchedColumn
,
setSearchedColumn
]
=
useState
<
string
>
(
''
);
const
getColumnSearchProps
=
(
dIndex
)
=>
({
filterDropdown
:
({
setSelectedKeys
,
selectedKeys
,
confirm
,
clearFilters
})
=>
(
<
div
style=
{
{
padding
:
8
}
}
>
<
Input
placeholder=
{
`Search ${dIndex}`
}
value=
{
selectedKeys
[
0
]
}
onChange=
{
e
=>
setSelectedKeys
(
e
.
target
.
value
?
[
e
.
target
.
value
]
:
[])
}
onPressEnter=
{
()
=>
handleSearch
(
selectedKeys
,
confirm
,
dIndex
)
}
style=
{
{
marginBottom
:
8
,
display
:
'block'
}
}
/>
<
Space
>
<
Button
type=
"primary"
onClick=
{
()
=>
handleSearch
(
selectedKeys
,
confirm
,
dIndex
)
}
icon=
{
<
SearchOutlined
/>
}
size=
"small"
style=
{
{
width
:
90
}
}
>
搜索
</
Button
>
<
Button
onClick=
{
()
=>
handleReset
(
clearFilters
)
}
size=
"small"
style=
{
{
width
:
90
}
}
>
重置
</
Button
>
<
Button
type=
"link"
size=
"small"
onClick=
{
()
=>
{
setSearchText
(
selectedKeys
[
0
]);
setSearchedColumn
(
dIndex
);
}
}
>
过滤
</
Button
>
</
Space
>
</
div
>
),
filterIcon
:
filtered
=>
<
SearchOutlined
style=
{
{
color
:
filtered
?
'#1890ff'
:
undefined
}
}
/>,
onFilter
:
(
value
,
record
)
=>
record
[
dIndex
]
?
record
[
dIndex
].
toString
().
toLowerCase
().
includes
(
value
.
toLowerCase
())
:
''
,
/*render: text =>
searchedColumn === dIndex ? (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[searchText]}
autoEscape
textToHighlight={text ? text.toString() : ''}
/>
) : (
text
),*/
});
const
handleSearch
=
(
selectedKeys
,
confirm
,
dIndex
)
=>
{
confirm
();
setSearchText
(
selectedKeys
[
0
]);
setSearchedColumn
(
dIndex
);
};
const
handleReset
=
(
clearFilters
)
=>
{
clearFilters
();
setSearchText
(
''
);
};
const
getColumns
=
(
columns
:[])
=>
{
let
datas
:
any
=
[];
columns
.
map
((
item
)
=>
{
datas
.
push
({
title
:
item
,
dataIndex
:
item
,
key
:
item
,
sorter
:
true
,
...
getColumnSearchProps
(
item
),
});
});
return
datas
;
};
const
onChange
=
(
val
:
number
)
=>
{
setDataIndex
(
val
);
};
return
(
<
Typography
>
<
Form
.
Item
label=
"当前执行记录"
tooltip=
"选择最近的执行记录,仅包含成功的记录"
>
<
Select
//mode="multiple"
style=
{
{
width
:
'100%'
}
}
placeholder=
"选择最近的执行记录"
defaultValue=
{
[
0
]
}
optionLabelProp=
"label"
onChange=
{
onChange
}
>
{
current
.
console
.
result
.
map
((
item
,
index
)
=>
{
if
(
item
.
success
)
{
...
...
@@ -31,8 +119,9 @@ const StudioTable = (props:any) => {
</
Option
>)
}
})
}
</
Select
>
</
Select
>
</
Form
.
Item
>
{
current
.
console
.
result
[
dataIndex
]
&&
current
.
console
.
result
[
dataIndex
].
result
?(<
Table
dataSource=
{
current
.
console
.
result
[
dataIndex
].
result
.
rowData
}
columns=
{
getColumns
(
current
.
console
.
result
[
dataIndex
].
result
.
columns
)
}
/>):(<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>)
}
</
Typography
>
);
};
...
...
dlink-web/src/components/Studio/StudioConsole/index.tsx
View file @
9bd1618c
...
...
@@ -6,6 +6,8 @@ import {connect} from "umi";
import
styles
from
"./index.less"
;
import
StudioMsg
from
"./StudioMsg"
;
import
StudioTable
from
"./StudioTable"
;
import
StudioHistory
from
"./StudioHistory"
;
import
StudioDocument
from
"./StudioDocument"
;
const
{
TabPane
}
=
Tabs
;
...
...
@@ -68,7 +70,7 @@ const StudioConsole = (props:any) => {
}
key=
"5"
>
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>
<
StudioHistory
/>
</
TabPane
>
<
TabPane
tab=
{
...
...
@@ -79,7 +81,7 @@ const StudioConsole = (props:any) => {
}
key=
"6"
>
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>
<
StudioDocument
/>
</
TabPane
>
<
TabPane
tab=
{
...
...
dlink-web/src/components/Studio/StudioEdit/completion.ts
View file @
9bd1618c
...
...
@@ -8,7 +8,42 @@ const Completion =[
insertText
:
'SUM(${1:})'
,
insertTextRules
:
monaco
.
languages
.
CompletionItemInsertTextRule
.
InsertAsSnippet
,
detail
:
'返回指定参数的求和'
}
},
{
label
:
'SQRT(number)'
,
kind
:
monaco
.
languages
.
CompletionItemKind
.
Function
,
insertText
:
'SQRT(${1:})'
,
insertTextRules
:
monaco
.
languages
.
CompletionItemInsertTextRule
.
InsertAsSnippet
,
detail
:
'返回指定参数的平方根'
},
{
label
:
'SIN(number)'
,
kind
:
monaco
.
languages
.
CompletionItemKind
.
Function
,
insertText
:
'SIN(${1:})'
,
insertTextRules
:
monaco
.
languages
.
CompletionItemInsertTextRule
.
InsertAsSnippet
,
detail
:
'返回指定参数的正弦值'
},
{
label
:
'SINH(number)'
,
kind
:
monaco
.
languages
.
CompletionItemKind
.
Function
,
insertText
:
'SINH(${1:})'
,
insertTextRules
:
monaco
.
languages
.
CompletionItemInsertTextRule
.
InsertAsSnippet
,
detail
:
'返回指定参数的双曲正弦值'
},
{
label
:
'SIGN(number)'
,
kind
:
monaco
.
languages
.
CompletionItemKind
.
Function
,
insertText
:
'SIGN(${1:})'
,
insertTextRules
:
monaco
.
languages
.
CompletionItemInsertTextRule
.
InsertAsSnippet
,
detail
:
'返回指定参数的符合'
},
{
label
:
'SUBSTRING(string,integer1,integer2)'
,
kind
:
monaco
.
languages
.
CompletionItemKind
.
Function
,
insertText
:
'SUBSTRING(${1:})'
,
insertTextRules
:
monaco
.
languages
.
CompletionItemInsertTextRule
.
InsertAsSnippet
,
detail
:
'返回指定字符串的子字符串'
},
];
export
default
Completion
;
dlink-web/src/components/Studio/StudioEdit/data.d.ts
View file @
9bd1618c
...
...
@@ -31,4 +31,11 @@ export type CompletionItem ={
}
export
type
StudioParam
=
{
statement
:
string
,
checkPoint
?:
number
,
savePointPath
?:
string
,
parallelism
?:
number
,
fragment
?:
boolean
,
clusterId
:
number
,
session
:
string
,
maxRowNum
?:
number
,
}
dlink-web/src/components/Studio/StudioEdit/index.tsx
View file @
9bd1618c
...
...
@@ -29,10 +29,9 @@ const FlinkSqlEditor = (props:any) => {
selectOnLineNumbers
:
true
,
renderSideBySide
:
false
,
},
sql
=
props
.
current
.
value
,
current
=
props
.
current
,
// sql,
dispatch
,
monaco
,
}
=
props
;
...
...
@@ -48,6 +47,7 @@ const FlinkSqlEditor = (props:any) => {
const
cache
:
any
=
useRef
(
code
.
current
);
const
[
refresh
,
setRefresh
]
=
React
.
useState
<
boolean
>
(
false
);
const
[
selectValue
,
setSelectValue
]
=
React
.
useState
<
string
>
(
""
);
useEffect
(
()
=>
()
=>
{
...
...
@@ -113,7 +113,6 @@ const FlinkSqlEditor = (props:any) => {
}
});
code
.
current
=
newSecondRightFields
;
// 数组长度永远为1
// 提示项设值
provider
=
monaco
.
languages
.
registerCompletionItemProvider
(
'sql'
,
{
provideCompletionItems
()
{
...
...
@@ -149,6 +148,7 @@ const FlinkSqlEditor = (props:any) => {
return
(
<
React
.
Fragment
>
<
MonacoEditor
ref=
{
monaco
}
width=
{
width
}
height=
{
height
}
language=
{
language
}
...
...
@@ -158,6 +158,7 @@ return (
theme=
"vs-dark"
editorDidMount=
{
editorDidMountHandle
}
/>
{
selectValue
}
</
React
.
Fragment
>
);
};
...
...
@@ -166,4 +167,5 @@ export default connect(({ Studio }: { Studio: StateType }) => ({
current
:
Studio
.
current
,
sql
:
Studio
.
sql
,
tabs
:
Studio
.
tabs
,
monaco
:
Studio
.
monaco
,
}))(
FlinkSqlEditor
);
dlink-web/src/components/Studio/StudioFX/index.tsx
0 → 100644
View file @
9bd1618c
import
{
message
,
Input
,
Button
,
Space
,
Table
,
Dropdown
,
Menu
,
Empty
,
Divider
}
from
"antd"
;
import
{
StateType
}
from
"@/pages/FlinkSqlStudio/model"
;
import
{
connect
}
from
"umi"
;
import
{
useState
}
from
"react"
;
// import Highlighter from 'react-highlight-words';
import
{
SearchOutlined
,
DownOutlined
,
TableOutlined
}
from
'@ant-design/icons'
;
import
React
from
"react"
;
import
{
executeDDL
}
from
"@/pages/FlinkSqlStudio/service"
;
const
StudioConnector
=
(
props
:
any
)
=>
{
const
{
current
}
=
props
;
const
[
tableData
,
setTableData
]
=
useState
<
[]
>
([]);
const
[
loadings
,
setLoadings
]
=
useState
<
boolean
[]
>
([]);
const
[
searchText
,
setSearchText
]
=
useState
<
string
>
(
''
);
const
[
searchedColumn
,
setSearchedColumn
]
=
useState
<
string
>
(
''
);
const
getColumnSearchProps
=
(
dIndex
)
=>
({
filterDropdown
:
({
setSelectedKeys
,
selectedKeys
,
confirm
,
clearFilters
})
=>
(
<
div
style=
{
{
padding
:
8
}
}
>
<
Input
placeholder=
{
`Search ${dIndex}`
}
value=
{
selectedKeys
[
0
]
}
onChange=
{
e
=>
setSelectedKeys
(
e
.
target
.
value
?
[
e
.
target
.
value
]
:
[])
}
onPressEnter=
{
()
=>
handleSearch
(
selectedKeys
,
confirm
,
dIndex
)
}
style=
{
{
marginBottom
:
8
,
display
:
'block'
}
}
/>
<
Space
>
<
Button
type=
"primary"
onClick=
{
()
=>
handleSearch
(
selectedKeys
,
confirm
,
dIndex
)
}
icon=
{
<
SearchOutlined
/>
}
size=
"small"
style=
{
{
width
:
90
}
}
>
搜索
</
Button
>
<
Button
onClick=
{
()
=>
handleReset
(
clearFilters
)
}
size=
"small"
style=
{
{
width
:
90
}
}
>
重置
</
Button
>
<
Button
type=
"link"
size=
"small"
onClick=
{
()
=>
{
setSearchText
(
selectedKeys
[
0
]);
setSearchedColumn
(
dIndex
);
}
}
>
过滤
</
Button
>
</
Space
>
</
div
>
),
filterIcon
:
filtered
=>
<
SearchOutlined
style=
{
{
color
:
filtered
?
'#1890ff'
:
undefined
}
}
/>,
onFilter
:
(
value
,
record
)
=>
record
[
dIndex
]
?
record
[
dIndex
].
toString
().
toLowerCase
().
includes
(
value
.
toLowerCase
())
:
''
,
/*render: text =>
searchedColumn === dIndex ? (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[searchText]}
autoEscape
textToHighlight={text ? text.toString() : ''}
/>
) : (
text
),*/
});
const
handleSearch
=
(
selectedKeys
,
confirm
,
dIndex
)
=>
{
confirm
();
setSearchText
(
selectedKeys
[
0
]);
setSearchedColumn
(
dIndex
);
};
const
handleReset
=
(
clearFilters
)
=>
{
clearFilters
();
setSearchText
(
''
);
};
const
MoreBtn
:
React
.
FC
<
{
item
:
any
}
>
=
({
item
})
=>
(
<
Dropdown
overlay=
{
<
Menu
onClick=
{
({
key
})
=>
keyEvent
(
key
,
item
)
}
>
<
Menu
.
Item
key=
"desc"
>
描述
</
Menu
.
Item
>
<
Menu
.
Item
key=
"delete"
>
删除
</
Menu
.
Item
>
</
Menu
>
}
>
<
a
>
更多
<
DownOutlined
/>
</
a
>
</
Dropdown
>
);
const
keyEvent
=
(
key
,
item
)
=>
{
if
(
key
==
'delete'
){
let
newLoadings
=
[...
loadings
];
newLoadings
[
1
]
=
true
;
setLoadings
(
newLoadings
);
const
res
=
executeDDL
({
statement
:
"drop table "
+
item
.
tablename
,
clusterId
:
current
.
task
.
clusterId
,
session
:
current
.
task
.
session
,
});
res
.
then
((
result
)
=>
{
if
(
result
.
datas
.
success
){
let
newTableData
=
tableData
;
for
(
let
i
=
0
;
i
<
newTableData
.
length
;
i
++
)
{
if
(
newTableData
[
i
].
tablename
==
item
.
tablename
)
{
newTableData
.
splice
(
i
,
1
);
setTableData
(
newTableData
);
break
;
}
}
}
let
newLoadings
=
[...
loadings
];
newLoadings
[
1
]
=
false
;
setLoadings
(
newLoadings
);
});
}
else
{
message
.
warn
(
"敬请期待"
);
}
};
const
getTables
=
()
=>
{
let
newLoadings
=
[...
loadings
];
newLoadings
[
0
]
=
true
;
setLoadings
(
newLoadings
);
const
res
=
executeDDL
({
statement
:
"show tables"
,
clusterId
:
current
.
task
.
clusterId
,
session
:
current
.
task
.
session
,
});
res
.
then
((
result
)
=>
{
if
(
result
.
datas
.
result
.
rowData
.
length
>
0
){
setTableData
(
result
.
datas
.
result
.
rowData
);
}
else
{
setTableData
([]);
}
let
newLoadings
=
[...
loadings
];
newLoadings
[
0
]
=
false
;
setLoadings
(
newLoadings
);
});
};
const
getColumns
=
()
=>
{
let
columns
:
any
=
[{
title
:
"表名"
,
dataIndex
:
"tablename"
,
key
:
"tablename"
,
sorter
:
true
,
...
getColumnSearchProps
(
"tablename"
),
},{
title
:
'操作'
,
dataIndex
:
'option'
,
valueType
:
'option'
,
render
:
(
_
,
record
)
=>
[
<
a
onClick=
{
()
=>
{
message
.
warn
(
'敬请期待'
);
}
}
>
描述
</
a
>,<
Divider
type=
"vertical"
/>,<
a
onClick=
{
()
=>
{
keyEvent
(
'delete'
,
record
);
}
}
>
删除
</
a
>
],
},];
return
columns
;
};
return
(
<>
<
Button
type=
"primary"
icon=
{
<
TableOutlined
/>
}
loading=
{
loadings
[
0
]
}
onClick=
{
()
=>
getTables
()
}
>
获取Connectors
</
Button
>
{
tableData
.
length
>
0
?(<
Table
dataSource=
{
tableData
}
columns=
{
getColumns
()
}
/>):(<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>)
}
</>
);
};
export
default
connect
(({
Studio
}:
{
Studio
:
StateType
})
=>
({
current
:
Studio
.
current
,
}))(
StudioConnector
);
dlink-web/src/components/Studio/StudioMenu/index.tsx
View file @
9bd1618c
import
styles
from
"./index.less"
;
import
{
Menu
,
Dropdown
,
Tooltip
,
Row
,
Col
,
Popconfirm
,
Badge
}
from
"antd"
;
import
{
Menu
,
Dropdown
,
Tooltip
,
Row
,
Col
,
Popconfirm
,
notification
}
from
"antd"
;
import
{
PauseCircleTwoTone
,
CopyTwoTone
,
DeleteTwoTone
,
PlayCircleTwoTone
,
DiffTwoTone
,
FileAddTwoTone
,
FolderOpenTwoTone
,
SafetyCertificateTwoTone
,
SaveTwoTone
,
FlagTwoTone
,
EnvironmentOutlined
}
from
"@ant-design/icons"
;
FileAddTwoTone
,
FolderOpenTwoTone
,
SafetyCertificateTwoTone
,
SaveTwoTone
,
FlagTwoTone
,
EnvironmentOutlined
,
SmileOutlined
}
from
"@ant-design/icons"
;
import
Space
from
"antd/es/space"
;
import
Divider
from
"antd/es/divider"
;
import
Button
from
"antd/es/button/button"
;
import
Breadcrumb
from
"antd/es/breadcrumb/Breadcrumb"
;
import
{
StateType
}
from
"@/pages/FlinkSqlStudio/model"
;
import
{
connect
}
from
"umi"
;
import
{
useEffect
,
useState
}
from
"react
"
;
import
{
handleAddOrUpdate
,
postAll
}
from
"@/components/Common/crud
"
;
import
{
postAll
}
from
"@/components/Common/crud
"
;
import
{
executeSql
}
from
"@/pages/FlinkSqlStudio/service
"
;
const
{
SubMenu
}
=
Menu
;
//<Button shape="circle" icon={<CaretRightOutlined />} />
const
menu
=
(
<
Menu
>
<
Menu
.
Item
>
1st menu item
</
Menu
.
Item
>
<
Menu
.
Item
>
2nd menu item
</
Menu
.
Item
>
<
SubMenu
title=
"sub menu"
>
<
Menu
.
Item
>
3rd menu item
</
Menu
.
Item
>
<
Menu
.
Item
>
4th menu item
</
Menu
.
Item
>
</
SubMenu
>
<
SubMenu
title=
"disabled sub menu"
disabled
>
<
Menu
.
Item
>
5d menu item
</
Menu
.
Item
>
<
Menu
.
Item
>
6th menu item
</
Menu
.
Item
>
</
SubMenu
>
<
Menu
.
Item
>
敬请期待
</
Menu
.
Item
>
</
Menu
>
);
const
StudioMenu
=
(
props
:
any
)
=>
{
const
{
tabs
,
current
,
currentPath
,
form
,
dispatch
}
=
props
;
const
[
pathItem
,
setPathItem
]
=
useState
<
[]
>
();
const
{
tabs
,
current
,
currentPath
,
form
,
dispatch
,
monaco
}
=
props
;
const
executeSql
=
()
=>
{
const
execute
=
()
=>
{
let
selection
=
monaco
.
current
.
editor
.
getSelection
();
let
selectsql
=
monaco
.
current
.
editor
.
getModel
().
getValueInRange
(
selection
);
if
(
selectsql
==
null
||
selectsql
==
''
){
selectsql
=
current
.
value
;
}
let
param
=
{
session
:
current
.
task
.
session
,
statement
:
current
.
value
,
statement
:
selectsql
,
clusterId
:
current
.
task
.
clusterId
,
checkPoint
:
current
.
task
.
checkPoint
,
parallelism
:
current
.
task
.
parallelism
,
...
...
@@ -46,8 +40,17 @@ const StudioMenu = (props: any) => {
savePointPath
:
current
.
task
.
savePointPath
,
};
const
key
=
current
.
key
;
const
result
=
postAll
(
'api/studio/executeSql'
,
param
);
const
taskKey
=
(
Math
.
random
()
*
1000
)
+
''
;
notification
.
success
({
message
:
`
${
param
.
clusterId
+
"_"
+
param
.
session
}
新任务正在执行`
,
description
:
param
.
statement
,
duration
:
null
,
key
:
taskKey
,
icon
:
<
SmileOutlined
style=
{
{
color
:
'#108ee9'
}
}
/>,
});
const
result
=
executeSql
(
param
);
result
.
then
(
res
=>
{
notification
.
close
(
taskKey
);
let
newTabs
=
tabs
;
for
(
let
i
=
0
;
i
<
newTabs
.
panes
.
length
;
i
++
){
if
(
newTabs
.
panes
[
i
].
key
==
key
){
...
...
@@ -59,7 +62,6 @@ const StudioMenu = (props: any) => {
break
;
}
}
console
.
log
(
newTabs
);
dispatch
&&
dispatch
({
type
:
"Studio/saveTabs"
,
payload
:
newTabs
,
...
...
@@ -67,14 +69,6 @@ const StudioMenu = (props: any) => {
})
};
const
buildMsg
=
(
res
)
=>
{
const
result
=
res
.
datas
;
let
msg
=
`[
${
result
.
sessionId
}
:
${
result
.
flinkHost
}
:
${
result
.
flinkPort
}
]
${
result
.
finishDate
}
${
result
.
success
?
'Success'
:
'Error'
}
[
${
result
.
time
}
ms]
${
result
.
msg
?
result
.
msg
:
''
}
${
result
.
error
?
result
.
error
:
''
}
\r\n
Statement:
${
result
.
statement
}
`
;
return
msg
;
};
const
saveSqlAndSettingToTask
=
async
()
=>
{
const
fieldsValue
=
await
form
.
validateFields
();
if
(
current
.
task
){
...
...
@@ -97,7 +91,7 @@ const StudioMenu = (props: any) => {
const
runMenu
=
(
<
Menu
>
<
Menu
.
Item
onClick=
{
execute
Sql
}
>
执行
</
Menu
.
Item
>
<
Menu
.
Item
onClick=
{
execute
}
>
执行
</
Menu
.
Item
>
</
Menu
>
);
...
...
@@ -177,7 +171,7 @@ const StudioMenu = (props: any) => {
type=
"text"
icon=
{
<
PlayCircleTwoTone
/>
}
//loading={loadings[2]}
onClick=
{
execute
Sql
}
onClick=
{
execute
}
/>
</
Tooltip
>
<
Popconfirm
...
...
@@ -188,12 +182,12 @@ const StudioMenu = (props: any) => {
cancelText=
"取消"
>
<
Tooltip
title=
"停止所有的 FlinkSql 任务"
>
<
Badge
size=
"small"
count=
{
1
}
offset=
{
[
-
5
,
5
]
}
>
{
/*<Badge size="small" count={1} offset={[-5, 5]}>*/
}
<
Button
type=
"text"
icon=
{
<
PauseCircleTwoTone
/>
}
/>
</
Badge
>
{
/*</Badge>*/
}
</
Tooltip
>
</
Popconfirm
>
<
Divider
type=
"vertical"
/>
...
...
@@ -220,4 +214,5 @@ export default connect(({Studio}: { Studio: StateType }) => ({
current
:
Studio
.
current
,
currentPath
:
Studio
.
currentPath
,
tabs
:
Studio
.
tabs
,
monaco
:
Studio
.
monaco
,
}))(
StudioMenu
);
dlink-web/src/components/Studio/StudioSetting/index.tsx
View file @
9bd1618c
...
...
@@ -15,7 +15,7 @@ const StudioSetting = (props: any) => {
const
getCluster
=
()
=>
{
cluster
.
then
(
value
=>
{
cluster
&&
cluster
.
then
(
value
=>
{
let
itemList
=
[];
for
(
let
item
of
value
){
let
tag
=
(<><
Tag
color=
{
item
.
enabled
?
"processing"
:
"error"
}
>
{
item
.
type
}
</
Tag
>
{
item
.
alias
}
</>);
...
...
@@ -134,6 +134,7 @@ const StudioSetting = (props: any) => {
className=
{
styles
.
form_item
}
>
<
Select
placeholder=
"选择会话"
defaultValue=
'admin'
dropdownRender=
{
menu
=>
(
<
div
>
{
menu
}
...
...
dlink-web/src/components/Studio/StudioTree/Function.ts
View file @
9bd1618c
...
...
@@ -34,40 +34,3 @@ export function convertToTreeData(data:TreeDataNode[], pid:number,path?:string[]
}
return
result
}
export
function
getLeafFromTree
(
data
:
DataType
[],
arr
:
DataType
[])
{
if
(
typeof
arr
==
'undefined'
)
{
arr
=
[];
}
for
(
let
i
=
0
;
i
<
data
.
length
;
i
++
)
{
let
sonList
=
data
[
i
].
children
;
if
(
sonList
)
{
if
(
sonList
.
length
==
0
)
{
arr
.
push
(
data
[
i
]);
}
else
{
getLeafFromTree
(
sonList
,
arr
);
}
}
else
{
arr
.
push
(
data
[
i
]);
}
}
return
arr
;
}
export
function
getChildFromTree
(
data
:
DataType
[],
arr
:
DataType
[])
{
if
(
typeof
arr
==
'undefined'
)
{
arr
=
[];
}
for
(
let
i
=
0
;
i
<
data
.
length
;
i
++
)
{
if
(
data
[
i
].
isParent
)
{
let
sonList
=
data
[
i
].
children
;
if
(
!
sonList
||
sonList
==
null
||
sonList
.
length
==
0
)
{
}
else
{
getChildFromTree
(
sonList
,
arr
);
}
}
else
{
arr
.
push
(
data
[
i
]);
}
}
return
arr
;
}
dlink-web/src/components/Studio/StudioTree/index.tsx
View file @
9bd1618c
...
...
@@ -107,7 +107,11 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
key
:
node
.
taskId
,
value
:(
result
.
datas
.
statement
?
result
.
datas
.
statement
:
''
),
closable
:
true
,
task
:
result
.
datas
,
task
:{
session
:
'admin'
,
maxRowNum
:
100
,
...
result
.
datas
},
console
:{
result
:[],
}
...
...
dlink-web/src/components/Studio/index.tsx
View file @
9bd1618c
...
...
@@ -12,6 +12,7 @@ import {StateType} from "@/pages/FlinkSqlStudio/model";
import
StudioConsole
from
"./StudioConsole"
;
import
StudioSetting
from
"./StudioSetting"
;
import
StudioEdit
from
"./StudioEdit"
;
import
StudioConnector
from
"./StudioConnector"
;
const
{
TabPane
}
=
Tabs
;
...
...
@@ -62,7 +63,7 @@ const Studio: React.FC<StudioProps> = ({sql}) => {
</
Tabs
>
<
Tabs
defaultActiveKey=
"1"
size=
"small"
>
<
TabPane
tab=
{
<
span
>
<
ApiOutlined
/>
连接器
</
span
>
}
key=
"1"
>
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>
<
StudioConnector
/>
</
TabPane
>
<
TabPane
tab=
{
<
span
>
<
DashboardOutlined
/>
总览
</
span
>
}
key=
"2"
>
<
Empty
image=
{
Empty
.
PRESENTED_IMAGE_SIMPLE
}
/>
...
...
dlink-web/src/locales/zh-CN/pages.ts
View file @
9bd1618c
export
default
{
'pages.layouts.userLayout.title'
:
'Dlink 是一款开源的
Apache Flink 开发运维
平台'
,
'pages.layouts.userLayout.title'
:
'Dlink 是一款开源的
基于 Apache Flink 的实时计算
平台'
,
'pages.login.accountLogin.tab'
:
'账户密码登录'
,
'pages.login.accountLogin.errorMessage'
:
'错误的用户名和密码(admin/ant.design)'
,
'pages.login.failure'
:
'登录失败,请重试!'
,
...
...
@@ -22,10 +22,12 @@ export default {
'pages.login.submit'
:
'登录'
,
'pages.login.loginWith'
:
'其他登录方式 :'
,
'pages.login.registerAccount'
:
'注册账户'
,
'pages.welcome.advancedComponent'
:
'高级表格'
,
'pages.welcome.link'
:
'欢迎使用'
,
'pages.welcome.advancedLayout'
:
'高级布局'
,
'pages.welcome.alertMessage'
:
'更快更强的重型组件,已经发布。'
,
'pages.welcome.Community'
:
'官方社区'
,
'pages.welcome.upgrade'
:
'更新日志'
,
'pages.welcome.link'
:
'欢迎加入'
,
'pages.welcome.star'
:
'欢迎 Star '
,
'pages.welcome.advancedLayout'
:
'Github'
,
'pages.welcome.alertMessage'
:
'实时计算平台 Dlink & Apache Flink 即将发布,目前为体验版,版本号为 0.1.0。'
,
'pages.admin.subPage.title'
:
' 这个页面只有 admin 权限才能查看'
,
'pages.admin.subPage.alertMessage'
:
'umi ui 现已发布,欢迎使用 npm run ui 启动体验。'
,
'pages.searchTable.createForm.newRule'
:
'新建规则'
,
...
...
dlink-web/src/pages/Admin.tsx
View file @
9bd1618c
...
...
@@ -17,7 +17,7 @@ export default (): React.ReactNode => {
<
Alert
message=
{
intl
.
formatMessage
({
id
:
'pages.welcome.alertMessage'
,
defaultMessage
:
'
更快更强的重型组件,已经发布
。'
,
defaultMessage
:
'
实时计算平台 Dlink & Apache Flink 即将发布,目前为体验版,版本号为0.1.0
。'
,
})
}
type=
"success"
showIcon
...
...
dlink-web/src/pages/Document/components/CreateForm.tsx
0 → 100644
View file @
9bd1618c
import
React
from
'react'
;
import
{
Modal
}
from
'antd'
;
type
CreateFormProps
=
{
modalVisible
:
boolean
;
onCancel
:
()
=>
void
;
};
const
CreateForm
:
React
.
FC
<
CreateFormProps
>
=
(
props
)
=>
{
const
{
modalVisible
,
onCancel
}
=
props
;
return
(
<
Modal
destroyOnClose
title=
"添加 Flink 集群"
visible=
{
modalVisible
}
onCancel=
{
()
=>
onCancel
()
}
footer=
{
null
}
>
{
props
.
children
}
</
Modal
>
);
};
export
default
CreateForm
;
dlink-web/src/pages/Document/components/UpdateForm.tsx
0 → 100644
View file @
9bd1618c
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
Form
,
Button
,
Input
,
Modal
,
Select
}
from
'antd'
;
import
Switch
from
"antd/es/switch"
;
import
TextArea
from
"antd/es/input/TextArea"
;
import
{
DocumentTableListItem
}
from
"@/pages/Document/data"
;
export
type
UpdateFormProps
=
{
onCancel
:
(
flag
?:
boolean
,
formVals
?:
Partial
<
DocumentTableListItem
>
)
=>
void
;
onSubmit
:
(
values
:
Partial
<
DocumentTableListItem
>
)
=>
void
;
updateModalVisible
:
boolean
;
values
:
Partial
<
DocumentTableListItem
>
;
};
const
FormItem
=
Form
.
Item
;
const
Option
=
Select
.
Option
;
const
formLayout
=
{
labelCol
:
{
span
:
7
},
wrapperCol
:
{
span
:
13
},
};
const
UpdateForm
:
React
.
FC
<
UpdateFormProps
>
=
(
props
)
=>
{
const
[
formVals
,
setFormVals
]
=
useState
<
Partial
<
DocumentTableListItem
>>
({
id
:
props
.
values
.
id
,
name
:
props
.
values
.
name
,
category
:
props
.
values
.
category
,
type
:
props
.
values
.
type
,
subtype
:
props
.
values
.
subtype
,
description
:
props
.
values
.
description
,
version
:
props
.
values
.
version
,
likeNum
:
props
.
values
.
likeNum
,
enabled
:
props
.
values
.
enabled
,
});
const
[
form
]
=
Form
.
useForm
();
const
{
onSubmit
:
handleUpdate
,
onCancel
:
handleUpdateModalVisible
,
updateModalVisible
,
values
,
}
=
props
;
const
submitForm
=
async
()
=>
{
const
fieldsValue
=
await
form
.
validateFields
();
setFormVals
({
...
formVals
,
...
fieldsValue
});
handleUpdate
({
...
formVals
,
...
fieldsValue
});
};
const
renderContent
=
(
formVals
)
=>
{
return
(
<>
<
FormItem
name=
"name"
label=
"名称"
rules=
{
[{
required
:
true
,
message
:
'请输入名称!'
}]
}
>
<
Input
placeholder=
"请输入"
/>
</
FormItem
>
<
FormItem
name=
"category"
label=
"文档类型"
>
<
Select
defaultValue=
"function"
allowClear
>
<
Option
value=
"function"
>
函数
</
Option
>
</
Select
>
</
FormItem
>
<
FormItem
name=
"type"
label=
"类型"
>
<
Select
defaultValue=
"内置函数"
allowClear
>
<
Option
value=
"内置函数"
>
内置函数
</
Option
>
<
Option
value=
"UDF"
>
UDF
</
Option
>
</
Select
>
</
FormItem
>
<
FormItem
name=
"subtype"
label=
"子类型"
>
<
Select
defaultValue=
"比较函数"
allowClear
>
<
Option
value=
"比较函数"
>
比较函数
</
Option
>
<
Option
value=
"逻辑函数"
>
逻辑函数
</
Option
>
<
Option
value=
"算术函数"
>
算术函数
</
Option
>
<
Option
value=
"字符串函数"
>
字符串函数
</
Option
>
<
Option
value=
"时间函数"
>
时间函数
</
Option
>
<
Option
value=
"条件函数"
>
条件函数
</
Option
>
<
Option
value=
"类型转换函数"
>
类型转换函数
</
Option
>
<
Option
value=
"Collection 函数"
>
Collection 函数
</
Option
>
<
Option
value=
"Value Collection 函数"
>
Value Collection 函数
</
Option
>
<
Option
value=
"Value Access 函数"
>
Value Access 函数
</
Option
>
<
Option
value=
"分组函数"
>
分组函数
</
Option
>
<
Option
value=
"hash函数"
>
hash函数
</
Option
>
<
Option
value=
"聚合函数"
>
聚合函数
</
Option
>
<
Option
value=
"列函数"
>
列函数
</
Option
>
<
Option
value=
"表值聚合函数"
>
表值聚合函数
</
Option
>
<
Option
value=
"其他函数"
>
其他函数
</
Option
>
</
Select
>
</
FormItem
>
<
FormItem
name=
"description"
label=
"描述"
>
<
TextArea
placeholder=
""
allowClear
autoSize=
{
{
minRows
:
3
,
maxRows
:
10
}
}
/>
</
FormItem
>
<
FormItem
name=
"version"
label=
"版本"
>
<
Input
placeholder=
"请输入"
/>
</
FormItem
>
<
FormItem
name=
"enabled"
label=
"是否启用"
rules=
{
[{
required
:
true
,
message
:
'请输入是否启用!'
}]
}
>
<
Switch
checkedChildren=
"启用"
unCheckedChildren=
"禁用"
defaultChecked=
{
formVals
.
enabled
}
/>
</
FormItem
>
</>
);
};
const
renderFooter
=
()
=>
{
return
(
<>
<
Button
onClick=
{
()
=>
handleUpdateModalVisible
(
false
,
values
)
}
>
取消
</
Button
>
<
Button
type=
"primary"
onClick=
{
()
=>
submitForm
()
}
>
完成
</
Button
>
</>
);
};
return
(
<
Modal
width=
{
640
}
bodyStyle=
{
{
padding
:
'32px 40px 48px'
}
}
destroyOnClose
title=
"编辑文档"
visible=
{
updateModalVisible
}
footer=
{
renderFooter
()
}
onCancel=
{
()
=>
handleUpdateModalVisible
()
}
>
<
Form
{
...
formLayout
}
form=
{
form
}
initialValues=
{
{
id
:
formVals
.
id
,
name
:
formVals
.
name
,
category
:
formVals
.
category
,
type
:
formVals
.
type
,
subtype
:
formVals
.
subtype
,
description
:
formVals
.
description
,
version
:
formVals
.
version
,
likeNum
:
formVals
.
likeNum
,
enabled
:
formVals
.
enabled
,
}
}
>
{
renderContent
(
formVals
)
}
</
Form
>
</
Modal
>
);
};
export
default
UpdateForm
;
dlink-web/src/pages/Document/data.d.ts
0 → 100644
View file @
9bd1618c
export
type
DocumentTableListItem
=
{
id
:
number
,
name
:
string
,
category
:
string
,
type
:
string
,
subtype
:
string
,
description
:
string
,
version
:
string
,
likeNum
:
number
,
enabled
:
boolean
,
createTime
:
Date
,
updateTime
:
Date
,
};
dlink-web/src/pages/Document/index.less
0 → 100644
View file @
9bd1618c
dlink-web/src/pages/Document/index.tsx
0 → 100644
View file @
9bd1618c
This diff is collapsed.
Click to expand it.
dlink-web/src/pages/FlinkSqlStudio/model.ts
View file @
9bd1618c
...
...
@@ -60,6 +60,7 @@ export type StateType = {
cluster
?:
ClusterType
[];
current
:
TabsItemType
;
sql
?:
string
;
monaco
?:
any
;
currentPath
?:
string
[];
tabs
:
TabsType
;
session
:
string
[];
...
...
@@ -74,6 +75,7 @@ export type ModelType = {
reducers
:
{
saveSql
:
Reducer
<
StateType
>
;
saveCurrentPath
:
Reducer
<
StateType
>
;
saveMonaco
:
Reducer
<
StateType
>
;
saveTabs
:
Reducer
<
StateType
>
;
changeActiveKey
:
Reducer
<
StateType
>
;
saveTaskData
:
Reducer
<
StateType
>
;
...
...
@@ -115,6 +117,7 @@ const Model: ModelType = {
}
},
sql
:
''
,
monaco
:
{},
currentPath
:
[],
tabs
:{
activeKey
:
0
,
...
...
@@ -177,6 +180,14 @@ const Model: ModelType = {
currentPath
:
payload
,
};
},
saveMonaco
(
state
,
{
payload
})
{
return
{
...
state
,
monaco
:{
...
payload
},
};
},
saveTabs
(
state
,
{
payload
})
{
let
newCurrent
=
state
.
current
;
for
(
let
i
=
0
;
i
<
payload
.
panes
.
length
;
i
++
){
...
...
dlink-web/src/pages/FlinkSqlStudio/service.ts
View file @
9bd1618c
...
...
@@ -10,6 +10,15 @@ export async function executeSql(params: StudioParam) {
});
}
export
async
function
executeDDL
(
params
:
StudioParam
)
{
return
request
<
API
.
Result
>
(
'/api/studio/executeDDL'
,
{
method
:
'POST'
,
data
:
{
...
params
,
},
});
}
export
async
function
getCatalogueTreeData
(
params
?:
StudioParam
)
{
return
request
<
API
.
Result
>
(
'/api/catalogue/getCatalogueTreeData'
,
{
method
:
'POST'
,
...
...
dlink-web/src/pages/Welcome.tsx
View file @
9bd1618c
import
React
from
'react'
;
import
{
PageContainer
}
from
'@ant-design/pro-layout'
;
import
{
Card
,
Alert
,
Typography
}
from
'antd'
;
import
{
Card
,
Alert
,
Typography
,
Timeline
}
from
'antd'
;
import
{
useIntl
,
FormattedMessage
}
from
'umi'
;
import
styles
from
'./Welcome.less'
;
const
{
Text
,
Link
,
Paragraph
}
=
Typography
;
const
CodePreview
:
React
.
FC
=
({
children
})
=>
(
<
pre
className=
{
styles
.
pre
}
>
<
code
>
...
...
@@ -31,32 +31,49 @@ export default (): React.ReactNode => {
}
}
/>
<
Typography
.
Text
strong
>
<
FormattedMessage
id=
"pages.welcome.advancedComponent"
defaultMessage=
"高级表格"
/>
{
' '
}
<
a
href=
"https://procomponents.ant.design/components/table"
rel=
"noopener noreferrer"
target=
"__blank"
>
<
FormattedMessage
id=
"pages.welcome.link"
defaultMessage=
"欢迎使用"
/>
</
a
>
<
FormattedMessage
id=
"pages.welcome.Community"
defaultMessage=
"官方社区"
/>
{
' '
}
<
FormattedMessage
id=
"pages.welcome.link"
defaultMessage=
"欢迎加入"
/>
</
Typography
.
Text
>
<
CodePreview
>
yarn add @ant-design/pro-table
</
CodePreview
>
<
CodePreview
>
微信公众号:Datalink数据中台
</
CodePreview
>
<
Typography
.
Text
strong
style=
{
{
marginBottom
:
12
,
}
}
>
<
FormattedMessage
id=
"pages.welcome.advancedLayout"
defaultMessage=
"
高级布局
"
/>
{
' '
}
<
FormattedMessage
id=
"pages.welcome.advancedLayout"
defaultMessage=
"
Github
"
/>
{
' '
}
<
a
href=
"https://
procomponents.ant.design/components/layout
"
href=
"https://
github.com/aiwenmo/dlink
"
rel=
"noopener noreferrer"
target=
"__blank"
>
<
FormattedMessage
id=
"pages.welcome.
link"
defaultMessage=
"欢迎使用
"
/>
<
FormattedMessage
id=
"pages.welcome.
star"
defaultMessage=
"欢迎 Star
"
/>
</
a
>
</
Typography
.
Text
>
<
CodePreview
>
yarn add @ant-design/pro-layout
</
CodePreview
>
<
Paragraph
>
<
Typography
.
Text
strong
>
<
FormattedMessage
id=
"pages.welcome.upgrade"
defaultMessage=
"更新日志"
/>
</
Typography
.
Text
>
</
Paragraph
>
<
p
>
</
p
>
<
Timeline
pending=
"Recording..."
reverse=
{
true
}
>
<
Timeline
.
Item
><
Text
code
>
0.1.0
</
Text
>
<
Text
type=
"secondary"
>
2015-09-01
</
Text
>
<
p
>
</
p
>
<
Paragraph
>
<
ul
>
<
li
>
<
Link
href=
""
>
FlinkSql Studio
</
Link
>
</
li
>
<
li
>
<
Link
href=
""
>
Flink 集群
</
Link
>
</
li
>
<
li
>
<
Link
href=
""
>
Flink 任务
</
Link
>
</
li
>
</
ul
>
</
Paragraph
>
</
Timeline
.
Item
>
</
Timeline
>
</
Card
>
</
PageContainer
>
);
...
...
dlink-web/src/pages/user/Login/index.tsx
View file @
9bd1618c
...
...
@@ -95,7 +95,7 @@ const Login: React.FC = () => {
<
div
className=
{
styles
.
header
}
>
<
Link
to=
"/"
>
<
img
alt=
"logo"
className=
{
styles
.
logo
}
src=
"/logo.svg"
/>
<
span
className=
{
styles
.
title
}
>
Dlink
</
span
>
<
span
className=
{
styles
.
title
}
>
Dlink
&
Apache Flink
</
span
>
</
Link
>
</
div
>
<
div
className=
{
styles
.
desc
}
>
...
...
@@ -139,7 +139,7 @@ const Login: React.FC = () => {
}
}
placeholder=
{
intl
.
formatMessage
({
id
:
'pages.login.username.placeholder'
,
defaultMessage
:
'用户名:
admin or user
'
,
defaultMessage
:
'用户名:'
,
})
}
rules=
{
[
{
...
...
@@ -161,7 +161,7 @@ const Login: React.FC = () => {
}
}
placeholder=
{
intl
.
formatMessage
({
id
:
'pages.login.password.placeholder'
,
defaultMessage
:
'密码:
ant.design
'
,
defaultMessage
:
'密码:'
,
})
}
rules=
{
[
{
...
...
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