Commit 5db8eb72 authored by wenmo's avatar wenmo

集群和数据源的studio交互

parent 242166a0
package com.dlink.controller; package com.dlink.controller;
import com.dlink.api.FlinkAPI; import com.dlink.api.FlinkAPI;
import com.dlink.assertion.Asserts;
import com.dlink.cluster.FlinkClusterInfo; import com.dlink.cluster.FlinkClusterInfo;
import com.dlink.common.result.ProTableResult; import com.dlink.common.result.ProTableResult;
import com.dlink.common.result.Result; import com.dlink.common.result.Result;
...@@ -35,7 +36,7 @@ public class ClusterController { ...@@ -35,7 +36,7 @@ public class ClusterController {
public Result saveOrUpdate(@RequestBody Cluster cluster) throws Exception { public Result saveOrUpdate(@RequestBody Cluster cluster) throws Exception {
cluster.setAutoRegisters(false); cluster.setAutoRegisters(false);
clusterService.registersCluster(cluster); clusterService.registersCluster(cluster);
return Result.succeed("新增成功"); return Result.succeed(Asserts.isNotNull(cluster.getId())?"修改成功":"新增成功");
} }
/** /**
......
import { import {
message, Button, Table, Empty, Divider, message, Button, Table, Empty, Divider,
Tooltip, Drawer Tooltip, Drawer, Modal
} from "antd"; } from "antd";
import ProDescriptions from '@ant-design/pro-descriptions'; import ProDescriptions from '@ant-design/pro-descriptions';
import {StateType} from "@/pages/FlinkSqlStudio/model"; import {StateType} from "@/pages/FlinkSqlStudio/model";
...@@ -13,15 +13,18 @@ import { ...@@ -13,15 +13,18 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import React from "react"; import React from "react";
import {showCluster} from "../../StudioEvent/DDL"; import {showCluster} from "../../StudioEvent/DDL";
import {handleAddOrUpdate} from "@/components/Common/crud"; import {handleAddOrUpdate, handleRemove} from "@/components/Common/crud";
import ClusterForm from "@/pages/Cluster/components/ClusterForm"; import ClusterForm from "@/pages/Cluster/components/ClusterForm";
import {Scrollbars} from 'react-custom-scrollbars'; import {Scrollbars} from 'react-custom-scrollbars';
const url = '/api/cluster';
const StudioCluster = (props: any) => { const StudioCluster = (props: any) => {
const {cluster, toolHeight, dispatch} = props; const {cluster, toolHeight, dispatch} = props;
const [createModalVisible, handleCreateModalVisible] = useState<boolean>(false); const [createModalVisible, handleCreateModalVisible] = useState<boolean>(false);
const [row, setRow] = useState<{}>(); const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
const [row, setRow] = useState<{}>({});
const getColumns = () => { const getColumns = () => {
return [{ return [{
...@@ -37,20 +40,18 @@ const StudioCluster = (props: any) => { ...@@ -37,20 +40,18 @@ const StudioCluster = (props: any) => {
const getAllColumns = () => { const getAllColumns = () => {
return [{ return [{
title: "集群名", title: "ID",
dataIndex: "alias", dataIndex: "id",
key: "alias", key: "id",
}, { }, {
title: '名称', title: '唯一标识',
dataIndex: 'name', dataIndex: 'name',
}, }, {
{ title: "集群名",
title: '集群ID', dataIndex: "alias",
dataIndex: 'id',
}, },
{ {
title: '类型', title: '类型',
sorter: true,
dataIndex: 'type', dataIndex: 'type',
filters: [ filters: [
{ {
...@@ -69,6 +70,14 @@ const StudioCluster = (props: any) => { ...@@ -69,6 +70,14 @@ const StudioCluster = (props: any) => {
text: 'Yarn Application', text: 'Yarn Application',
value: 'yarn-application', value: 'yarn-application',
}, },
{
text: 'Kubernetes Session',
value: 'kubernetes-session',
},
{
text: 'Kubernetes Application',
value: 'kubernetes-application',
},
], ],
filterMultiple: false, filterMultiple: false,
valueEnum: { valueEnum: {
...@@ -76,21 +85,20 @@ const StudioCluster = (props: any) => { ...@@ -76,21 +85,20 @@ const StudioCluster = (props: any) => {
'standalone': {text: 'Standalone'}, 'standalone': {text: 'Standalone'},
'yarn-per-job': {text: 'Yarn Per-Job'}, 'yarn-per-job': {text: 'Yarn Per-Job'},
'yarn-application': {text: 'Yarn Application'}, 'yarn-application': {text: 'Yarn Application'},
'kubernetes-session': {text: 'Kubernetes Session'},
'kubernetes-application': {text: 'Kubernetes Application'},
}, },
}, },
{ {
title: 'JobManager HA 地址', title: 'JobManager HA 地址',
sorter: true,
dataIndex: 'hosts', dataIndex: 'hosts',
valueType: 'textarea', valueType: 'textarea',
}, },
{ {
title: '当前 JobManager 地址', title: '当前 JobManager 地址',
sorter: true,
dataIndex: 'jobManagerHost', dataIndex: 'jobManagerHost',
}, { }, {
title: '版本', title: '版本',
sorter: true,
dataIndex: 'version', dataIndex: 'version',
}, },
{ {
...@@ -114,7 +122,6 @@ const StudioCluster = (props: any) => { ...@@ -114,7 +122,6 @@ const StudioCluster = (props: any) => {
}, },
{ {
title: '注释', title: '注释',
sorter: true,
valueType: 'textarea', valueType: 'textarea',
dataIndex: 'note', dataIndex: 'note',
}, },
...@@ -156,6 +163,13 @@ const StudioCluster = (props: any) => { ...@@ -156,6 +163,13 @@ const StudioCluster = (props: any) => {
false: {text: '手动', status: 'Error'}, false: {text: '手动', status: 'Error'},
}, },
}, },
{
title: '集群配置ID',
dataIndex: 'clusterConfigurationId',
},{
title: '作业ID',
dataIndex: 'taskId',
},
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'createTime', dataIndex: 'createTime',
...@@ -171,19 +185,11 @@ const StudioCluster = (props: any) => { ...@@ -171,19 +185,11 @@ const StudioCluster = (props: any) => {
dataIndex: 'option', dataIndex: 'option',
valueType: 'option', valueType: 'option',
render: (_, record) => [ render: (_, record) => [
<a <Button type="dashed" onClick={() => onModifyCluster(record)}>
onClick={() => { 配置
message.success('敬请期待'); </Button>, <Button danger onClick={() => onDeleteCluster(record)}>
}} 删除
> </Button>
详情
</a>, <Divider type="vertical"/>, <a
onClick={() => {
message.success('敬请期待');
}}
>
管理
</a>
], ],
},]; },];
}; };
...@@ -196,6 +202,25 @@ const StudioCluster = (props: any) => { ...@@ -196,6 +202,25 @@ const StudioCluster = (props: any) => {
handleCreateModalVisible(true); handleCreateModalVisible(true);
}; };
const onModifyCluster = (record) => {
setRow(record);
handleUpdateModalVisible(true);
};
const onDeleteCluster = (record) => {
Modal.confirm({
title: '删除集群',
content: `确定删除该集群【${record.alias}】吗?`,
okText: '确认',
cancelText: '取消',
onOk: async () => {
await handleRemove(url, [record]);
setRow({});
onRefreshCluster();
}
});
};
return ( return (
<> <>
<Tooltip title="新建 Flink 集群"> <Tooltip title="新建 Flink 集群">
...@@ -228,12 +253,28 @@ const StudioCluster = (props: any) => { ...@@ -228,12 +253,28 @@ const StudioCluster = (props: any) => {
handleCreateModalVisible(false); handleCreateModalVisible(false);
}} }}
modalVisible={createModalVisible} modalVisible={createModalVisible}
values={{}}
/> />
{row && Object.keys(row).length ? (<ClusterForm
onSubmit={async (value) => {
const success = await handleAddOrUpdate("api/cluster", value);
if (success) {
handleUpdateModalVisible(false);
setRow({});
onRefreshCluster();
}
}}
onCancel={() => {
handleUpdateModalVisible(false);
}}
modalVisible={updateModalVisible}
values={row}
/>):undefined}
<Drawer <Drawer
width={600} width={600}
visible={!!row} visible={!!row?.id}
onClose={() => { onClose={() => {
setRow(undefined); setRow({});
}} }}
closable={false} closable={false}
> >
......
import { import {
message, Button,Table, Empty, Divider, message, Button, Table, Empty, Divider,
Tooltip Tooltip, Drawer, Modal
} from "antd"; } from "antd";
import {StateType} from "@/pages/FlinkSqlStudio/model"; import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi"; import {connect} from "umi";
...@@ -14,40 +14,159 @@ import React from "react"; ...@@ -14,40 +14,159 @@ import React from "react";
import {showDataBase} from "../../StudioEvent/DDL"; import {showDataBase} from "../../StudioEvent/DDL";
import DBForm from "@/pages/DataBase/components/DBForm"; import DBForm from "@/pages/DataBase/components/DBForm";
import { Scrollbars } from 'react-custom-scrollbars'; import { Scrollbars } from 'react-custom-scrollbars';
import ProDescriptions from "@ant-design/pro-descriptions";
import {handleRemove} from "@/components/Common/crud";
const StudioDataBase = (props: any) => { const StudioDataBase = (props: any) => {
const {database,toolHeight, dispatch} = props; const {database,toolHeight, dispatch} = props;
const [chooseDBModalVisible, handleDBFormModalVisible] = useState<boolean>(false); const [chooseDBModalVisible, handleDBFormModalVisible] = useState<boolean>(false);
const [values, setValues] = useState<any>({}); const [values, setValues] = useState<any>({});
const [row, setRow] = useState<{}>();
const getColumns = () => { const getColumns = () => {
let columns: any = [{ return [{
title: "数据源名", title: "数据源名",
dataIndex: "alias", dataIndex: "alias",
key: "alias", key: "alias",
sorter: true, sorter: true,
render: (dom, entity) => {
return <a onClick={() => setRow(entity)}>{dom}</a>;
},
}];
};
const getAllColumns = () => {
return [{
title: "ID",
dataIndex: "id",
key: "id",
},{
title: "数据源名",
dataIndex: "alias",
}, {
title: '唯一标识',
dataIndex: 'name',
},
{
title: '分组类型',
dataIndex: 'groupName',
filters: [
{
text: '来源',
value: '来源',
},
{
text: '数仓',
value: '数仓',
},
{
text: '应用',
value: '应用',
},
{
text: '备份',
value: '备份',
},{
text: '其他',
value: '其他',
},
],
filterMultiple: false,
valueEnum: {
'yarn-session': {text: 'Yarn Session'},
'standalone': {text: 'Standalone'},
'yarn-per-job': {text: 'Yarn Per-Job'},
'yarn-application': {text: 'Yarn Application'},
},
},
{
title: 'URL',
dataIndex: 'url',
valueType: 'textarea',
},
{
title: '用户名',
dataIndex: 'username',
}, { }, {
title: '版本',
sorter: true,
dataIndex: 'dbVersion',
},
{
title: '状态',
dataIndex: 'status',
filters: [
{
text: '正常',
value: 1,
},
{
text: '异常',
value: 0,
},
],
filterMultiple: false,
valueEnum: {
1: {text: '正常', status: 'Success'},
0: {text: '异常', status: 'Error'},
},
},
{
title: '注释',
sorter: true,
valueType: 'textarea',
dataIndex: 'note',
},
{
title: '是否启用',
dataIndex: 'enabled',
filters: [
{
text: '已启用',
value: 1,
},
{
text: '已禁用',
value: 0,
},
],
filterMultiple: false,
valueEnum: {
true: {text: '已启用', status: 'Success'},
false: {text: '已禁用', status: 'Error'},
},
},
{
title: '最近的健康时间',
dataIndex: 'healthTime',
valueType: 'dateTime',
},{
title: '最近的心跳检测时间',
dataIndex: 'heartbeatTime',
valueType: 'dateTime',
},{
title: '创建时间',
dataIndex: 'createTime',
valueType: 'dateTime',
},
{
title: '最近更新时间',
dataIndex: 'updateTime',
valueType: 'dateTime',
},
{
title: '操作', title: '操作',
dataIndex: 'option', dataIndex: 'option',
valueType: 'option', valueType: 'option',
render: (_, record) => [ render: (_, record) => [
<a <Button type="dashed" onClick={() => onModifyDataBase(record)}>
onClick={() => { 配置
message.success('敬请期待'); </Button>, <Button danger onClick={() => onDeleteDataBase(record)}>
}} 删除
> </Button>
详情
</a>, <Divider type="vertical"/>, <a
onClick={() => {
message.success('敬请期待');
}}
>
管理
</a>
], ],
},]; },];
return columns;
}; };
const onRefreshDataBase = () => { const onRefreshDataBase = () => {
...@@ -59,6 +178,25 @@ const StudioDataBase = (props: any) => { ...@@ -59,6 +178,25 @@ const StudioDataBase = (props: any) => {
handleDBFormModalVisible(true); handleDBFormModalVisible(true);
}; };
const onModifyDataBase = (record) => {
setValues(record);
handleDBFormModalVisible(true);
};
const onDeleteDataBase = (record) => {
Modal.confirm({
title: '删除数据源',
content: `确定删除该数据源【${record.alias}】吗?`,
okText: '确认',
cancelText: '取消',
onOk: async () => {
await handleRemove('api/database', [record]);
setRow({});
onRefreshDataBase();
}
});
};
return ( return (
<> <>
<Tooltip title="新建数据源"> <Tooltip title="新建数据源">
...@@ -86,10 +224,33 @@ const StudioDataBase = (props: any) => { ...@@ -86,10 +224,33 @@ const StudioDataBase = (props: any) => {
}} }}
modalVisible={chooseDBModalVisible} modalVisible={chooseDBModalVisible}
onSubmit={() => { onSubmit={() => {
showDataBase(dispatch); setRow({});
onRefreshDataBase();
}} }}
values={values} values={values}
/> />
<Drawer
width={600}
visible={!!row?.id}
onClose={() => {
setRow(undefined);
}}
closable={false}
>
{row?.name && (
<ProDescriptions
column={2}
title={row?.name}
request={async () => ({
data: row || {},
})}
params={{
id: row?.name,
}}
columns={getAllColumns()}
/>
)}
</Drawer>
</Scrollbars> </Scrollbars>
</> </>
); );
......
...@@ -238,11 +238,11 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => { ...@@ -238,11 +238,11 @@ const StudioTree: React.FC<StudioTreeProps> = (props) => {
} }
}; };
const toDelete= (node:TreeDataNode|undefined)=>{ const toDelete= (node: TreeDataNode|undefined)=>{
let label = (node?.taskId==null)?'目录':'作业'; let label = (node?.taskId==null)?'目录':'作业';
Modal.confirm({ Modal.confirm({
title: `删除${label}`, title: `删除${label}`,
content: `确定删除该${label}吗?`, content: `确定删除该${label}${node?.name}吗?`,
okText: '确认', okText: '确认',
cancelText: '取消', cancelText: '取消',
onOk:async () => { onOk:async () => {
......
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {Form, Button, Input, Modal, Select} from 'antd'; import {Form, Button, Input, Modal, Select, Switch} from 'antd';
import {ClusterTableListItem} from "@/pages/Cluster/data"; import {ClusterTableListItem} from "@/pages/Cluster/data";
...@@ -7,6 +7,7 @@ export type ClusterFormProps = { ...@@ -7,6 +7,7 @@ export type ClusterFormProps = {
onCancel: (flag?: boolean) => void; onCancel: (flag?: boolean) => void;
onSubmit: (values: Partial<ClusterTableListItem>) => void; onSubmit: (values: Partial<ClusterTableListItem>) => void;
modalVisible: boolean; modalVisible: boolean;
values: Partial<ClusterTableListItem>;
}; };
const Option = Select.Option; const Option = Select.Option;
...@@ -18,6 +19,15 @@ const formLayout = { ...@@ -18,6 +19,15 @@ const formLayout = {
const ClusterForm: React.FC<ClusterFormProps> = (props) => { const ClusterForm: React.FC<ClusterFormProps> = (props) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const [formVals, setFormVals] = useState<Partial<ClusterTableListItem>>({
id: props.values.id,
name: props.values.name,
alias: props.values.alias,
type: props.values.type,
hosts: props.values.hosts,
note: props.values.note,
enabled: props.values.enabled,
});
const { const {
onSubmit: handleSubmit, onSubmit: handleSubmit,
...@@ -27,10 +37,12 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => { ...@@ -27,10 +37,12 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => {
const submitForm = async () => { const submitForm = async () => {
const fieldsValue = await form.validateFields(); const fieldsValue = await form.validateFields();
fieldsValue.id= formVals.id;
setFormVals(fieldsValue);
handleSubmit(fieldsValue); handleSubmit(fieldsValue);
}; };
const renderContent = () => { const renderContent = (formValsPara: Partial<ClusterTableListItem>) => {
return ( return (
<> <>
<Form.Item <Form.Item
...@@ -73,6 +85,12 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => { ...@@ -73,6 +85,12 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => {
<Input.TextArea placeholder="请输入文本注释" allowClear <Input.TextArea placeholder="请输入文本注释" allowClear
autoSize={{minRows: 3, maxRows: 10}}/> autoSize={{minRows: 3, maxRows: 10}}/>
</Form.Item> </Form.Item>
<Form.Item
name="enabled"
label="是否启用">
<Switch checkedChildren="启用" unCheckedChildren="禁用"
defaultChecked={formValsPara.enabled}/>
</Form.Item>
</> </>
); );
}; };
...@@ -93,7 +111,7 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => { ...@@ -93,7 +111,7 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => {
width={640} width={640}
bodyStyle={{padding: '32px 40px 48px'}} bodyStyle={{padding: '32px 40px 48px'}}
destroyOnClose destroyOnClose
title="创建集群" title={formVals.id?"修改集群":"创建集群"}
visible={modalVisible} visible={modalVisible}
footer={renderFooter()} footer={renderFooter()}
onCancel={() => handleModalVisible()} onCancel={() => handleModalVisible()}
...@@ -101,8 +119,9 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => { ...@@ -101,8 +119,9 @@ const ClusterForm: React.FC<ClusterFormProps> = (props) => {
<Form <Form
{...formLayout} {...formLayout}
form={form} form={form}
initialValues={formVals}
> >
{renderContent()} {renderContent(formVals)}
</Form> </Form>
</Modal> </Modal>
); );
......
...@@ -520,6 +520,9 @@ export default (): React.ReactNode => { ...@@ -520,6 +520,9 @@ export default (): React.ReactNode => {
<li> <li>
<Link>新增 FlinkSQL 及 SQL 导出</Link> <Link>新增 FlinkSQL 及 SQL 导出</Link>
</li> </li>
<li>
<Link>新增 集群与数据源的 Studio 管理交互</Link>
</li>
</ul> </ul>
</Paragraph> </Paragraph>
</Timeline.Item> </Timeline.Item>
......
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