Commit 2b9841c5 authored by wenmo's avatar wenmo

会话管理

parent 6961f0f3
......@@ -7,6 +7,7 @@ import com.dlink.explainer.ca.TableCANode;
import com.dlink.job.JobResult;
import com.dlink.result.IResult;
import com.dlink.result.SelectResult;
import com.dlink.session.ExecutorEntity;
import com.dlink.session.SessionInfo;
import java.util.List;
......@@ -25,7 +26,7 @@ public interface StudioService {
SelectResult getJobData(String jobId);
boolean createSession(SessionDTO sessionDTO,String createUser);
SessionInfo createSession(SessionDTO sessionDTO, String createUser);
boolean clearSession(String session);
......
......@@ -13,6 +13,7 @@ import com.dlink.result.IResult;
import com.dlink.result.SelectResult;
import com.dlink.service.ClusterService;
import com.dlink.service.StudioService;
import com.dlink.session.ExecutorEntity;
import com.dlink.session.SessionConfig;
import com.dlink.session.SessionInfo;
import com.dlink.session.SessionPool;
......@@ -59,7 +60,7 @@ public class StudioServiceImpl implements StudioService {
}
@Override
public boolean createSession(SessionDTO sessionDTO,String createUser) {
public SessionInfo createSession(SessionDTO sessionDTO, String createUser) {
if(sessionDTO.isUseRemote()) {
Cluster cluster = clusterService.getById(sessionDTO.getClusterId());
SessionConfig sessionConfig = SessionConfig.build(
......
......@@ -271,9 +271,9 @@ public class JobManager extends RunTime {
return ResultPool.get(jobId);
}
public static boolean createSession(String session, SessionConfig sessionConfig,String createUser){
public static SessionInfo createSession(String session, SessionConfig sessionConfig,String createUser){
if(SessionPool.exist(session)){
return false;
return SessionPool.getInfo(session);
}
Executor sessionExecutor = null;
if (sessionConfig.isUseRemote()) {
......@@ -281,8 +281,9 @@ public class JobManager extends RunTime {
} else {
sessionExecutor = Executor.buildLocalExecutor(sessionConfig.getExecutorSetting());
}
SessionPool.push(new ExecutorEntity(session,sessionConfig,createUser,LocalDateTime.now(), sessionExecutor));
return true;
ExecutorEntity executorEntity = new ExecutorEntity(session, sessionConfig, createUser, LocalDateTime.now(), sessionExecutor);
SessionPool.push(executorEntity);
return SessionInfo.build(executorEntity);
}
public static List<SessionInfo> listSession(String createUser){
......
......@@ -72,4 +72,13 @@ public class SessionPool {
}
return sessionInfos;
}
public static SessionInfo getInfo(String sessionId){
ExecutorEntity executorEntity = get(sessionId);
if(executorEntity!=null){
return SessionInfo.build(executorEntity);
}else {
return null;
}
}
}
......@@ -157,7 +157,7 @@ const StudioHistory = (props: any) => {
{(row.status==2) ?
(<><Badge status="success"/><Text type="success">SUCCESS</Text></>):
(row.status==1) ?
<><Badge status="error"/><Text type="secondary">RUNNING</Text></> :
<><Badge status="success"/><Text type="secondary">RUNNING</Text></> :
(row.status==3) ?
<><Badge status="error"/><Text type="danger">FAILED</Text></> :
(row.status==4) ?
......
import {executeDDL} from "@/pages/FlinkSqlStudio/service";
import FlinkSQL from "./FlinkSQL";
import {SessionType, TaskType} from "@/pages/FlinkSqlStudio/model";
import {Modal} from "antd";
import {getData, handleRemove} from "@/components/Common/crud";
import {SessionType} from "@/pages/FlinkSqlStudio/model";
import {Modal,message} from "antd";
import {addOrUpdateData, getData, handleRemove} from "@/components/Common/crud";
/*--- 创建会话 ---*/
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);
listSession(dispatch);
});
}
/*--- 查询会话列表 ---*/
export function listSession(dispatch: any) {
const res = getData("api/studio/listSession");
res.then((result)=>{
console.log(result.datas);
dispatch && dispatch({
type: "Studio/saveSession",
payload: result.datas,
});
});
}
/*--- 切换会话 ---*/
export function changeSession(session: SessionType, dispatch: any) {
dispatch && dispatch({
type: "Studio/refreshCurrentSession",
......@@ -13,13 +33,31 @@ export function changeSession(session: SessionType, dispatch: any) {
showTables(session.session,dispatch);
},200);
}
/*--- 退出会话 ---*/
export function quitSession( dispatch: any) {
dispatch && dispatch({
type: "Studio/quitCurrentSession",
});
}
/*--- 注销会话 ---*/
export function clearSession(session: string, dispatch: any) {
Modal.confirm({
title: '确认注销会话【' + session + '】?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
let para = {
id: session,
};
const res = handleRemove('/api/studio/clearSession', [para]);
res.then((result) => {
quitSession(dispatch);
listSession(dispatch);
});
}
});
}
/*--- 刷新 Catalog Table ---*/
export function showTables(session: string, dispatch: any) {
if(session==null||session==''){
return;
......@@ -43,7 +81,7 @@ export function showTables(session: string, dispatch: any) {
});
});
}
/*--- 移除 Catalog Table ---*/
export function removeTable(tablename: string, session: string, dispatch: any) {
Modal.confirm({
title: '确定删除表【' + tablename + '】吗?',
......@@ -62,24 +100,7 @@ export function removeTable(tablename: string, session: string, dispatch: any) {
}
});
}
export function clearSession(session: string, dispatch: any) {
Modal.confirm({
title: '确认清空会话【' + session + '】?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
let para = {
id: session,
};
const res = handleRemove('/api/studio/clearSession', [para]);
res.then((result) => {
quitSession(dispatch);
});
}
});
}
/*--- 刷新 集群 ---*/
export function showCluster(dispatch: any) {
const res = getData('api/cluster/listEnabledAll');
res.then((result) => {
......
import {message, Input, Button, Space, Table, Dropdown, Menu, Empty,Divider,
Tooltip} from "antd";
import {
message, Input, Button, Space, Table, Dropdown, Menu, Empty, Divider,
Tooltip
} from "antd";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi";
import {useState} from "react";
import styles from "./index.less";
import { SearchOutlined,DownOutlined,DeleteOutlined,CommentOutlined ,PoweroffOutlined,PlusOutlined} from '@ant-design/icons';
import {
SearchOutlined,
DownOutlined,
DeleteOutlined,
CommentOutlined,
PoweroffOutlined,
PlusOutlined
} from '@ant-design/icons';
import React from "react";
import {removeTable, showTables, clearSession, changeSession, quitSession} from "@/components/Studio/StudioEvent/DDL";
import {
removeTable, showTables, clearSession, changeSession, quitSession,
createSession, listSession
} from "@/components/Studio/StudioEvent/DDL";
import {
ModalForm,
} from '@ant-design/pro-form';
......@@ -14,13 +26,13 @@ import ProDescriptions from '@ant-design/pro-descriptions';
import {getData, handleAddOrUpdate} from "@/components/Common/crud";
import SessionForm from "@/components/Studio/StudioLeftTool/StudioConnector/components/SessionForm";
const StudioConnector = (props:any) => {
const StudioConnector = (props: any) => {
const {current,dispatch,currentSession} = props;
const [tableData,setTableData] = useState<[]>([]);
const [loadings,setLoadings] = useState<boolean[]>([]);
const [searchText,setSearchText] = useState<string>('');
const [searchedColumn,setSearchedColumn] = useState<string>('');
const {current, dispatch, currentSession, session} = props;
const [tableData, setTableData] = useState<[]>([]);
const [loadings, setLoadings] = useState<boolean[]>([]);
const [searchText, setSearchText] = useState<string>('');
const [searchedColumn, setSearchedColumn] = useState<string>('');
const [modalVisit, setModalVisit] = useState(false);
const [type, setType] = useState<number>();
const [sessionData, setSessionData] = useState<{}>();
......@@ -28,26 +40,26 @@ const StudioConnector = (props:any) => {
const getColumnSearchProps = (dIndex) => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
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' }}
style={{marginBottom: 8, display: 'block'}}
/>
<Space>
<Button
type="primary"
onClick={() => handleSearch(selectedKeys, confirm, dIndex)}
icon={<SearchOutlined />}
icon={<SearchOutlined/>}
size="small"
style={{ width: 90 }}
style={{width: 90}}
>
搜索
</Button>
<Button onClick={() => handleReset(clearFilters)} size="small" style={{ width: 90 }}>
<Button onClick={() => handleReset(clearFilters)} size="small" style={{width: 90}}>
重置
</Button>
<Button
......@@ -63,7 +75,7 @@ const StudioConnector = (props:any) => {
</Space>
</div>
),
filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
filterIcon: filtered => <SearchOutlined style={{color: filtered ? '#1890ff' : undefined}}/>,
onFilter: (value, record) =>
record[dIndex]
? record[dIndex].toString().toLowerCase().includes(value.toLowerCase())
......@@ -93,7 +105,7 @@ const StudioConnector = (props:any) => {
};
const MoreBtn: React.FC<{
item:any
item: any
}> = ({item}) => (
<Dropdown
overlay={
......@@ -109,42 +121,42 @@ const StudioConnector = (props:any) => {
</Dropdown>
);
const keyEvent=(key, item)=>{
if(key=='delete'){
removeTable(item.tablename,currentSession.session,dispatch);
}else{
const keyEvent = (key, item) => {
if (key == 'delete') {
removeTable(item.tablename, currentSession.session, dispatch);
} else {
message.warn("敬请期待");
}
};
const keySessionsEvent=(key, item)=>{
if(key=='delete'){
clearSession(item.session,current.task,dispatch);
}else if(key=='connect'){
changeSession(item,dispatch);
message.success('连接共享会话【'+item.session+'】成功!');
const keySessionsEvent = (key, item) => {
if (key == 'delete') {
clearSession(item.session, dispatch);
} else if (key == 'connect') {
changeSession(item, dispatch);
message.success('连接共享会话【' + item.session + '】成功!');
setModalVisit(false);
}else{
} else {
message.warn("敬请期待");
}
};
const getTables = () => {
showTables(currentSession.session,dispatch);
showTables(currentSession.session, dispatch);
};
const onClearSession = () => {
clearSession(currentSession.session,dispatch);
clearSession(currentSession.session, dispatch);
};
const getColumns=()=>{
let columns:any=[{
const getColumns = () => {
let columns: any = [{
title: "表名",
dataIndex: "tablename",
key: "tablename",
sorter: true,
...getColumnSearchProps("tablename"),
},{
}, {
title: '操作',
dataIndex: 'option',
valueType: 'option',
......@@ -155,9 +167,9 @@ const StudioConnector = (props:any) => {
}}
>
描述
</a>,<Divider type="vertical" />,<a
</a>, <Divider type="vertical"/>, <a
onClick={() => {
keyEvent('delete',record);
keyEvent('delete', record);
}}
>
删除
......@@ -167,54 +179,54 @@ const StudioConnector = (props:any) => {
return columns;
};
const getSessionsColumns=()=>{
let columns:any=[{
const getSessionsColumns = () => {
let columns: any = [{
title: "会话 Key",
dataIndex: "session",
key: "session",
sorter: true,
...getColumnSearchProps("session"),
},{
}, {
title: "执行模式",
key: "useRemote",
sorter: true,
...getColumnSearchProps("useRemote"),
render:function(text, record, index) {
return record.sessionConfig.useRemote?'远程':'本地';
render: function (text, record, index) {
return record.sessionConfig.useRemote ? '远程' : '本地';
}
},{
}, {
title: "集群名",
key: "clusterName",
sorter: true,
...getColumnSearchProps("clusterName"),
render:function(text, record, index) {
render: function (text, record, index) {
return record.sessionConfig.clusterName;
}
},{
}, {
title: "创建人",
dataIndex: "createUser",
key: "createUser",
sorter: true,
...getColumnSearchProps("createUser"),
},{
}, {
title: "创建时间",
dataIndex: "createTime",
key: "createTime",
sorter: true,
},{
}, {
title: '操作',
dataIndex: 'option',
valueType: 'option',
render: (_, record) => [
<a
onClick={() => {
keySessionsEvent('connect',record);
keySessionsEvent('connect', record);
}}
>
连接
</a>,<Divider type="vertical" />,<a
</a>, <Divider type="vertical"/>, <a
onClick={() => {
keySessionsEvent('delete',record);
keySessionsEvent('delete', record);
}}
>
删除
......@@ -224,66 +236,69 @@ const StudioConnector = (props:any) => {
return columns;
};
const createSessions=()=>{
const createSessions = () => {
handleCreateSessionModalVisible(true);
};
const showSessions=()=>{
const showSessions = () => {
setModalVisit(true);
setType(1);
const res = getData("api/studio/listSession");
res.then((result)=>{
setSessionData(result.datas);
});
listSession(dispatch);
};
const quitSessions=()=>{
const quitSessions = () => {
quitSession(dispatch);
message.success('退出共享会话成功!');
};
return (
<>
<Tooltip title="新建会话">
<Button
type="text"
icon={<PlusOutlined/>}
onClick={createSessions}
/>
</Tooltip>
<div style={{float: "right"}}>
<Tooltip title="切换会话">
<Button
type="text"
icon={<CommentOutlined />}
onClick={showSessions}
/>
</Tooltip>
<Tooltip title="退出会话">
<Button
type="text"
icon={<PoweroffOutlined />}
onClick={quitSessions}
/>
</Tooltip>
<Tooltip title="新建会话">
<Button
type="text"
icon={<PlusOutlined />}
onClick={createSessions}
/>
</Tooltip>
<Tooltip title="刷新连接器">
<Button
type="text"
icon={<SearchOutlined />}
onClick={getTables}
/>
</Tooltip>
<Tooltip title="注销会话">
<Button
type="text"
icon={<DeleteOutlined />}
onClick={onClearSession}
/>
</Tooltip>
{session.length > 0 ? (
<Tooltip title="切换会话">
<Button
type="text"
icon={<CommentOutlined/>}
onClick={showSessions}
/>
</Tooltip>
) : ''}
{currentSession.session && (
<>
<Tooltip title="退出会话">
<Button
type="text"
icon={<PoweroffOutlined/>}
onClick={quitSessions}
/>
</Tooltip>
<Tooltip title="刷新连接器">
<Button
type="text"
icon={<SearchOutlined/>}
onClick={getTables}
/>
</Tooltip>
<Tooltip title="注销会话">
<Button
type="text"
icon={<DeleteOutlined/>}
onClick={onClearSession}
/>
</Tooltip>
</>)}
</div>
{currentSession.connectors&&currentSession.connectors.length>0?(<Table dataSource={currentSession.connectors} columns={getColumns()} size="small" />):(<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />)}
{currentSession.connectors && currentSession.connectors.length > 0 ? (
<Table dataSource={currentSession.connectors} columns={getColumns()} size="small"/>) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>)}
<ModalForm
// title="新建表单"
visible={modalVisit}
onFinish={async () => {
setSessionData(undefined);
......@@ -297,15 +312,15 @@ const StudioConnector = (props:any) => {
},
}}
>
{type==1&&
{type == 1 &&
(<ProDescriptions
column={2}
title='全部共享会话'
>
<ProDescriptions.Item span={2} >
{sessionData?
(<Table dataSource={sessionData} columns={getSessionsColumns()} size="small"
/>):(<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />)}
<ProDescriptions.Item span={2}>
{session.length > 0 ?
(<Table dataSource={session} columns={getSessionsColumns()} size="small"
/>) : (<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>)}
</ProDescriptions.Item>
</ProDescriptions>
)
......@@ -313,27 +328,25 @@ const StudioConnector = (props:any) => {
</ModalForm>
<SessionForm
onSubmit={async (value) => {
console.log(value);
const success = await handleAddOrUpdate("api/studio/createSession",value);
if (success) {
handleCreateSessionModalVisible(false);
}
createSession(value, dispatch);
handleCreateSessionModalVisible(false);
}}
onCancel={() => {
handleCreateSessionModalVisible(false);
}}
updateModalVisible={createSessionModalVisible}
values={{
session:'',
type:'PUBLIC',
useRemote:false,
session: '',
type: 'PUBLIC',
useRemote: true,
}}
/>
</>
</>
);
};
export default connect(({ Studio }: { Studio: StateType }) => ({
export default connect(({Studio}: { Studio: StateType }) => ({
current: Studio.current,
currentSession: Studio.currentSession,
session: Studio.session,
}))(StudioConnector);
......@@ -18,17 +18,17 @@ const StudioLeftTool = (props:any) => {
<TabPane tab={<span><BarsOutlined/> 目录</span>} key="StudioTree" >
<StudioTree/>
</TabPane>
<TabPane tab={<span><DatabaseOutlined /> 数据源</span>} key="DataSource" >
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
<TabPane tab={<span><MessageOutlined /> 会话</span>} key="Connectors" >
<StudioConnector />
</TabPane>
<TabPane tab={<span><AppstoreOutlined /> 元数据</span>} key="MetaData" >
<TabPane tab={<span><ClusterOutlined /> 集群</span>} key="Cluster" >
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</TabPane>
<TabPane tab={<span><ClusterOutlined /> 集群</span>} key="Cluster" >
<TabPane tab={<span><DatabaseOutlined /> 数据源</span>} key="DataSource" >
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</TabPane>
<TabPane tab={<span><MessageOutlined /> 会话</span>} key="Connectors" >
<StudioConnector />
<TabPane tab={<span><AppstoreOutlined /> 元数据</span>} key="MetaData" >
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</TabPane>
<TabPane tab={<span><FireOutlined /> 任务</span>} key="FlinkTask" >
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
......
import styles from "./index.less";
import {Menu, Dropdown, Tooltip, Row, Col, Popconfirm, notification, Modal,message} from "antd";
import {PauseCircleTwoTone, CopyTwoTone, DeleteTwoTone,PlayCircleTwoTone,DiffTwoTone,
FileAddTwoTone,FolderOpenTwoTone,SafetyCertificateTwoTone,SaveTwoTone,FlagTwoTone,
EnvironmentOutlined,SmileOutlined,RocketTwoTone,QuestionCircleTwoTone,MessageOutlined} from "@ant-design/icons";
import {Menu, Dropdown, Tooltip, Row, Col, Popconfirm, notification, Modal, message} from "antd";
import {
PauseCircleTwoTone, CopyTwoTone, DeleteTwoTone, PlayCircleTwoTone, DiffTwoTone,
FileAddTwoTone, FolderOpenTwoTone, SafetyCertificateTwoTone, SaveTwoTone, FlagTwoTone,
EnvironmentOutlined, SmileOutlined, RocketTwoTone, QuestionCircleTwoTone, MessageOutlined, ClusterOutlined
} 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 { postDataArray} from "@/components/Common/crud";
import {postDataArray} from "@/components/Common/crud";
import {executeSql} from "@/pages/FlinkSqlStudio/service";
import StudioHelp from "./StudioHelp";
import {showTables} from "@/components/Studio/StudioEvent/DDL";
......@@ -23,121 +25,121 @@ const menu = (
const StudioMenu = (props: any) => {
const {tabs,current,currentPath,form,refs,dispatch,currentSession} = props;
const {tabs, current, currentPath, form, refs, dispatch, currentSession} = props;
const execute = () => {
let selectsql =null;
if(current.monaco.current) {
let selectsql = null;
if (current.monaco.current) {
let selection = current.monaco.current.editor.getSelection();
selectsql = current.monaco.current.editor.getModel().getValueInRange(selection);
}
if(selectsql==null||selectsql==''){
selectsql=current.value;
if (selectsql == null || selectsql == '') {
selectsql = current.value;
}
let useSession = !!currentSession.session;
let param ={
useSession:useSession,
session:currentSession.session,
useRemote:current.task.useRemote,
clusterId:current.task.clusterId,
useResult:current.task.useResult,
maxRowNum:current.task.maxRowNum,
statement:selectsql,
fragment:current.task.fragment,
jobName:current.task.jobName,
parallelism:current.task.parallelism,
checkPoint:current.task.checkPoint,
savePointPath:current.task.savePointPath,
let param = {
useSession: useSession,
session: currentSession.session,
useRemote: current.task.useRemote,
clusterId: current.task.clusterId,
useResult: current.task.useResult,
maxRowNum: current.task.maxRowNum,
statement: selectsql,
fragment: current.task.fragment,
jobName: current.task.jobName,
parallelism: current.task.parallelism,
checkPoint: current.task.checkPoint,
savePointPath: current.task.savePointPath,
};
const key = current.key;
const taskKey = (Math.random()*1000)+'';
const taskKey = (Math.random() * 1000) + '';
notification.success({
message: `新任务【${param.jobName}】正在执行`,
description: param.statement.substring(0,40)+'...',
duration:null,
key:taskKey,
icon: <SmileOutlined style={{ color: '#108ee9' }} />,
description: param.statement.substring(0, 40) + '...',
duration: null,
key: taskKey,
icon: <SmileOutlined style={{color: '#108ee9'}}/>,
});
setTimeout(()=>{
setTimeout(() => {
refs?.history?.current?.reload();
},2000);
}, 2000);
const result = executeSql(param);
result.then(res=>{
result.then(res => {
notification.close(taskKey);
if(res.datas.success){
if (res.datas.success) {
message.success('执行成功');
}else{
} else {
message.success('执行失败');
}
let newTabs = tabs;
for(let i=0;i<newTabs.panes.length;i++){
if(newTabs.panes[i].key==key){
for (let i = 0; i < newTabs.panes.length; i++) {
if (newTabs.panes[i].key == key) {
/*let newResult = newTabs.panes[i].console.result;
newResult.unshift(res.datas);
newTabs.panes[i].console={
result:newResult,
};*/
newTabs.panes[i].console.result=res.datas;
newTabs.panes[i].console.result = res.datas;
break;
}
}
dispatch&&dispatch({
dispatch && dispatch({
type: "Studio/saveTabs",
payload: newTabs,
});
useSession&&showTables(currentSession.session,dispatch);
useSession && showTables(currentSession.session, dispatch);
})
};
const submit= () =>{
if(!current.task.id){
const submit = () => {
if (!current.task.id) {
message.error(`草稿【${current.title}】无法被提交,请创建或选择有效作业进行提交`);
return;
}
const taskKey = (Math.random()*1000)+'';
const taskKey = (Math.random() * 1000) + '';
Modal.confirm({
title: '异步提交作业',
content: `确定异步提交作业【${current.task.alias}】到其配置的集群吗?请确认您的作业是否已经被保存!`,
okText: '确认',
cancelText: '取消',
onOk:async () => {
onOk: async () => {
let task = {
id:current.task.id,
id: current.task.id,
};
notification.success({
message: `任务【${current.task.alias} 】正在异步提交`,
description: current.task.statement,
duration:null,
key:taskKey,
icon: <SmileOutlined style={{ color: '#108ee9' }} />,
duration: null,
key: taskKey,
icon: <SmileOutlined style={{color: '#108ee9'}}/>,
});
setTimeout(()=>{
setTimeout(() => {
refs?.history?.current?.reload();
},2000);
const res = await postDataArray('/api/task/submit',[task.id]);
notification.close(taskKey);
if(res.datas[0].success){
message.success('异步提交成功');
}else{
message.success('异步提交失败');
}
}, 2000);
const res = await postDataArray('/api/task/submit', [task.id]);
notification.close(taskKey);
if (res.datas[0].success) {
message.success('异步提交成功');
} else {
message.success('异步提交失败');
}
}
});
};
const saveSqlAndSettingToTask = async() => {
const saveSqlAndSettingToTask = async () => {
const fieldsValue = await form.validateFields();
if(current.task){
if (current.task) {
let task = {
...current.task,
statement:current.value,
statement: current.value,
...fieldsValue
};
dispatch&&dispatch({
dispatch && dispatch({
type: "Studio/saveTask",
payload: task,
});
}else{
} else {
}
};
......@@ -149,22 +151,23 @@ const StudioMenu = (props: any) => {
</Menu>
);
const getPathItem = (paths)=>{
const getPathItem = (paths) => {
let itemList = [];
for(let item of paths){
for (let item of paths) {
itemList.push(<Breadcrumb.Item>{item}</Breadcrumb.Item>)
}
return itemList;
};
const showHelp=()=>{
const showHelp = () => {
Modal.info({
title: '使用帮助',
width:1000,
width: 1000,
content: (
<StudioHelp />
<StudioHelp/>
),
onOk() {},
onOk() {
},
});
};
return (
......@@ -198,94 +201,102 @@ const StudioMenu = (props: any) => {
<Divider className={styles["ant-divider-horizontal-0"]}/>
<Col span={24}>
<Row>
<Col span={8}>
<Col span={4}>
<Breadcrumb className={styles["dw-path"]}>
<EnvironmentOutlined />
<Divider type="vertical" />
<EnvironmentOutlined/>
<Divider type="vertical"/>
{getPathItem(currentPath)}
{currentSession.session&&
(<>
<Divider type="vertical" />
<MessageOutlined />
<Divider type="vertical" />
<Breadcrumb.Item>{currentSession.session}</Breadcrumb.Item>
</>)
}
</Breadcrumb>
</Col>
<Col span={8} offset={8}>
<Col span={12}>
{currentSession.session &&
(
<Breadcrumb className={styles["dw-path"]}>
<Divider type="vertical"/>
<MessageOutlined/>
<Divider type="vertical"/>
{currentSession.session}
<Divider type="vertical"/>
<ClusterOutlined/>
<Divider type="vertical"/>
{currentSession.sessionConfig.useRemote ?
currentSession.sessionConfig.clusterName : '本地模式'}
</Breadcrumb>
)}
</Col>
<Col span={8}>
<Button
type="text"
icon={<FileAddTwoTone twoToneColor="#ddd" />}
icon={<FileAddTwoTone twoToneColor="#ddd"/>}
/>
<Button
type="text"
icon={<FolderOpenTwoTone twoToneColor="#ddd" />}
icon={<FolderOpenTwoTone twoToneColor="#ddd"/>}
/>
<Tooltip title="保存当前的 FlinkSql 及配置">
<Button
type="text"
icon={<SaveTwoTone />}
onClick={saveSqlAndSettingToTask}
/>
<Button
type="text"
icon={<SaveTwoTone/>}
onClick={saveSqlAndSettingToTask}
/>
</Tooltip>
<Divider type="vertical" />
<Divider type="vertical"/>
<Button
type="text"
icon={<SafetyCertificateTwoTone twoToneColor="#ddd" />}
icon={<SafetyCertificateTwoTone twoToneColor="#ddd"/>}
/>
<Button
type="text"
icon={<FlagTwoTone twoToneColor="#ddd" />}
icon={<FlagTwoTone twoToneColor="#ddd"/>}
/>
<Tooltip title="执行当前的 FlinkSql">
<Button
type="text"
icon={<PlayCircleTwoTone />}
//loading={loadings[2]}
onClick={execute}
/>
<Button
type="text"
icon={<PlayCircleTwoTone/>}
//loading={loadings[2]}
onClick={execute}
/>
</Tooltip>
<Tooltip title="提交当前的作业到集群">
<Button
type="text"
icon={<RocketTwoTone />}
onClick={submit}
/>
<Button
type="text"
icon={<RocketTwoTone/>}
onClick={submit}
/>
</Tooltip>
<Popconfirm
title="您确定要停止所有的 FlinkSql 任务吗?"
// onConfirm={confirm}
// onConfirm={confirm}
//onCancel={cancel}
okText="停止"
cancelText="取消"
>
<Tooltip title="停止所有的 FlinkSql 任务,暂不可用">
{/*<Badge size="small" count={1} offset={[-5, 5]}>*/}
<Button
type="text"
icon={<PauseCircleTwoTone twoToneColor="#ddd" />}
/>
<Button
type="text"
icon={<PauseCircleTwoTone twoToneColor="#ddd"/>}
/>
{/*</Badge>*/}
</Tooltip>
</Popconfirm>
<Divider type="vertical" />
<Divider type="vertical"/>
<Button
type="text"
icon={<DiffTwoTone twoToneColor="#ddd" />}
icon={<DiffTwoTone twoToneColor="#ddd"/>}
/>
<Button
type="text"
icon={<CopyTwoTone twoToneColor="#ddd" />}
icon={<CopyTwoTone twoToneColor="#ddd"/>}
/>
<Button
type="text"
icon={<DeleteTwoTone twoToneColor="#ddd" />}
icon={<DeleteTwoTone twoToneColor="#ddd"/>}
/>
<Tooltip title="查看使用帮助">
<Button
type="text"
icon={<QuestionCircleTwoTone />}
icon={<QuestionCircleTwoTone/>}
onClick={showHelp}
/>
</Tooltip>
......
import {connect} from "umi";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {Form, InputNumber,Input,Switch,Select,Tag,Row,Col,Divider,Tooltip,Button} from "antd";
import {
Form, InputNumber, Input, Switch, Select, Tag, Row, Col, Divider, Tooltip, Button, Badge,
Typography
} from "antd";
import {InfoCircleOutlined,PlusOutlined,MinusSquareOutlined} from "@ant-design/icons";
import styles from "./index.less";
import {useEffect, useState} from "react";
import {showTables} from "@/components/Studio/StudioEvent/DDL";
const { Option } = Select;
const { Text } = Typography;
const StudioConfig = (props: any) => {
......@@ -75,8 +79,11 @@ const StudioConfig = (props: any) => {
label="远程执行" className={styles.form_item} name="useRemote" valuePropName="checked"
tooltip={{ title: '开启远程执行,将在远程集群进行任务执行', icon: <InfoCircleOutlined /> }}
>
<Switch checkedChildren="启用" unCheckedChildren="禁用"
/>
{
currentSession.session?
(currentSession.sessionConfig&&currentSession.sessionConfig.useRemote?(<><Badge status="success"/><Text type="success">已启用</Text></>):(<><Badge status="error"/><Text type="danger">已禁用</Text></>)
):(<Switch checkedChildren="启用" unCheckedChildren="禁用"/>)
}
</Form.Item>
</Form>
</>
......
import {connect} from "umi";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {Form, InputNumber,Input,Switch,Select,Tag,Row,Col,Divider,Tooltip,Button} from "antd";
import {Form, InputNumber, Input, Switch, Select, Tag, Row, Col, Badge, Tooltip, Button, Typography} from "antd";
import {InfoCircleOutlined,PlusOutlined,MinusSquareOutlined} from "@ant-design/icons";
import styles from "./index.less";
import {useEffect, useState} from "react";
import {showCluster, showTables} from "@/components/Studio/StudioEvent/DDL";
const { Option } = Select;
const { Text } = Typography;
const StudioSetting = (props: any) => {
......@@ -111,15 +112,22 @@ const StudioSetting = (props: any) => {
<Col span={24}>
<Form.Item label="Flink集群" tooltip="选择Flink集群进行远程提交任务" name="clusterId"
className={styles.form_item}>
<Select
style={{ width: '100%' }}
placeholder="选择Flink集群"
defaultValue={0}
optionLabelProp="label"
onChange={onChangeClusterSession}
>
{getClusterOptions()}
</Select>
{
currentSession.session?
(currentSession.sessionConfig&&currentSession.sessionConfig.clusterId?
(<><Badge status="success"/><Text type="success">{currentSession.sessionConfig.clusterName}</Text></>)
:(<><Badge status="error"/><Text type="danger">本地模式</Text></>)
):(<Select
style={{ width: '100%' }}
placeholder="选择Flink集群"
defaultValue={0}
optionLabelProp="label"
onChange={onChangeClusterSession}
>
{getClusterOptions()}
</Select>)
}
</Form.Item>
</Col>
</Row>
......
......@@ -11,7 +11,7 @@ import {StateType} from "@/pages/FlinkSqlStudio/model";
import StudioConsole from "./StudioConsole";
import StudioLeftTool from "./StudioLeftTool";
import StudioRightTool from "./StudioRightTool";
import {showCluster} from "@/components/Studio/StudioEvent/DDL";
import {listSession, showCluster} from "@/components/Studio/StudioEvent/DDL";
type StudioProps = {
rightClickMenu:StateType['rightClickMenu'];
......@@ -24,6 +24,7 @@ const Studio: React.FC<StudioProps> = (props) => {
const [form] = Form.useForm();
showCluster(dispatch);
listSession(dispatch);
const onClick=()=>{
if(rightClickMenu){
......
......@@ -17,10 +17,12 @@ import {
handleAddOrUpdate, handleOption, handleRemove, queryData,
updateEnabled
} from "@/components/Common/crud";
import {showCluster} from "@/components/Studio/StudioEvent/DDL";
const url = '/api/cluster';
const ClusterTableList: React.FC<{}> = () => {
const ClusterTableList: React.FC<{}> = (props: any) => {
const {dispatch} = props;
const [createModalVisible, handleModalVisible] = useState<boolean>(false);
const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [formValues, setFormValues] = useState({});
......@@ -38,16 +40,16 @@ const ClusterTableList: React.FC<{}> = () => {
content: '确定删除该集群吗?',
okText: '确认',
cancelText: '取消',
onOk:async () => {
await handleRemove(url,[currentItem]);
onOk: async () => {
await handleRemove(url, [currentItem]);
actionRef.current?.reloadAndRest?.();
}
});
}
};
const checkHeartBeats = async ()=>{
await handleOption(url+'/heartbeats','心跳检测',null);
const checkHeartBeats = async () => {
await handleOption(url + '/heartbeats', '心跳检测', null);
actionRef.current?.reloadAndRest?.();
};
......@@ -122,9 +124,9 @@ const ClusterTableList: React.FC<{}> = () => {
],
filterMultiple: false,
valueEnum: {
'Yarn': { text: 'Yarn'},
'Standalone': { text: 'Standalone'},
'Others': { text: 'Others' },
'Yarn': {text: 'Yarn'},
'Standalone': {text: 'Standalone'},
'Others': {text: 'Others'},
},
},
{
......@@ -162,8 +164,8 @@ const ClusterTableList: React.FC<{}> = () => {
],
filterMultiple: false,
valueEnum: {
1: { text: '正常', status: 'Success' },
0: { text: '异常', status: 'Error' },
1: {text: '正常', status: 'Success'},
0: {text: '异常', status: 'Error'},
},
},
{
......@@ -193,8 +195,8 @@ const ClusterTableList: React.FC<{}> = () => {
],
filterMultiple: false,
valueEnum: {
true: { text: '已启用', status: 'Success' },
false: { text: '已禁用', status: 'Error' },
true: {text: '已启用', status: 'Success'},
false: {text: '已禁用', status: 'Error'},
},
},
{
......@@ -203,14 +205,14 @@ const ClusterTableList: React.FC<{}> = () => {
sorter: true,
valueType: 'dateTime',
hideInForm: true,
hideInTable:true,
renderFormItem: (item, { defaultRender, ...rest }, form) => {
hideInTable: true,
renderFormItem: (item, {defaultRender, ...rest}, form) => {
const status = form.getFieldValue('status');
if (`${status}` === '0') {
return false;
}
if (`${status}` === '3') {
return <Input {...rest} placeholder="请输入异常原因!" />;
return <Input {...rest} placeholder="请输入异常原因!"/>;
}
return defaultRender(item);
},
......@@ -221,13 +223,13 @@ const ClusterTableList: React.FC<{}> = () => {
sorter: true,
valueType: 'dateTime',
hideInForm: true,
renderFormItem: (item, { defaultRender, ...rest }, form) => {
renderFormItem: (item, {defaultRender, ...rest}, form) => {
const status = form.getFieldValue('status');
if (`${status}` === '0') {
return false;
}
if (`${status}` === '3') {
return <Input {...rest} placeholder="请输入异常原因!" />;
return <Input {...rest} placeholder="请输入异常原因!"/>;
}
return defaultRender(item);
},
......@@ -264,10 +266,10 @@ const ClusterTableList: React.FC<{}> = () => {
<PlusOutlined/> 新建
</Button>,
<Button type="primary" onClick={() => checkHeartBeats()}>
<HeartOutlined /> 心跳
<HeartOutlined/> 心跳
</Button>,
]}
request={(params, sorter, filter) => queryData(url,{...params, sorter, filter})}
request={(params, sorter, filter) => queryData(url, {...params, sorter, filter})}
columns={columns}
rowSelection={{
onChange: (_, selectedRows) => setSelectedRows(selectedRows),
......@@ -285,14 +287,14 @@ const ClusterTableList: React.FC<{}> = () => {
}
>
<Button type="primary" danger
onClick ={()=>{
onClick={() => {
Modal.confirm({
title: '删除集群',
content: '确定删除选中的集群吗?',
okText: '确认',
cancelText: '取消',
onOk:async () => {
await handleRemove(url,selectedRowsState);
onOk: async () => {
await handleRemove(url, selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
......@@ -302,14 +304,14 @@ const ClusterTableList: React.FC<{}> = () => {
批量删除
</Button>
<Button type="primary"
onClick ={()=>{
onClick={() => {
Modal.confirm({
title: '启用集群',
content: '确定启用选中的集群吗?',
okText: '确认',
cancelText: '取消',
onOk:async () => {
await updateEnabled(url,selectedRowsState, true);
onOk: async () => {
await updateEnabled(url, selectedRowsState, true);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
......@@ -317,14 +319,14 @@ const ClusterTableList: React.FC<{}> = () => {
}}
>批量启用</Button>
<Button danger
onClick ={()=>{
onClick={() => {
Modal.confirm({
title: '禁用集群',
content: '确定禁用选中的集群吗?',
okText: '确认',
cancelText: '取消',
onOk:async () => {
await updateEnabled(url,selectedRowsState, false);
onOk: async () => {
await updateEnabled(url, selectedRowsState, false);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
......@@ -336,12 +338,13 @@ const ClusterTableList: React.FC<{}> = () => {
<CreateForm onCancel={() => handleModalVisible(false)} modalVisible={createModalVisible}>
<ProTable<ClusterTableListItem, ClusterTableListItem>
onSubmit={async (value) => {
const success = await handleAddOrUpdate(url,value);
const success = await handleAddOrUpdate(url, value);
if (success) {
handleModalVisible(false);
if (actionRef.current) {
actionRef.current.reload();
}
showCluster(dispatch);
}
}}
rowKey="id"
......@@ -352,13 +355,14 @@ const ClusterTableList: React.FC<{}> = () => {
{formValues && Object.keys(formValues).length ? (
<UpdateForm
onSubmit={async (value) => {
const success = await handleAddOrUpdate(url,value);
const success = await handleAddOrUpdate(url, value);
if (success) {
handleUpdateModalVisible(false);
setFormValues({});
if (actionRef.current) {
actionRef.current.reload();
}
showCluster(dispatch);
}
}}
onCancel={() => {
......
......@@ -75,11 +75,13 @@ export type ConnectorType = {
export type SessionType = {
session?: string;
type?: string;
useRemote?: string;
clusterId?: number;
clusterName?: string;
address?: string;
sessionConfig?:{
type?: string;
useRemote?: boolean;
clusterId?: number;
clusterName?: string;
address?: string;
}
createUser?: string;
createTime?: string;
connectors: ConnectorType[];
......@@ -157,7 +159,7 @@ const Model: ModelType = {
},
sql: '',
monaco: {},
currentPath: [],
currentPath: ['草稿'],
tabs: {
activeKey: 0,
panes: [{
......@@ -314,23 +316,11 @@ const Model: ModelType = {
};
},
saveSession(state, {payload}) {
let newSession = state?.session;
if(newSession) {
for (let i = 0; i < newSession.length; i++) {
if (newSession[i].key == payload) {
return {};
}
}
newSession.push(payload);
return {
...state,
session: newSession,
};
}else {
return {
...state
};
}
console.log(payload);
return {
...state,
session: [...payload],
};
},
showRightClickMenu(state, {payload}) {
return {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment