Commit 6f815c16 by zhaochengxiang

增加数据源管理

parent 0235c15b
......@@ -22,13 +22,12 @@ export default class App extends React.Component {
<Switch>
<Route path={`${ContextPath}/login`} component={Signin} exact />
<Route path={`${ContextPath}/home`} component={Home} />
<Route path={`${ContextPath}/datasource-manage`} component={DatasourceManage} exact />
<Route path={`${ContextPath}/data-map`} component={Map} exact />
<Route path={`${ContextPath}/manage`} component={Manage} />
<Route path={`/center-home/view/data-model`} component={Model} exact />
<Route path={`/center-home/view/asset-map`} component={Map} exact />
<Route path={`/center-home/view/asset-manage`} component={AssetManage} exact />
<Route path={`/center-home/view/asset-recycle`} component={AssetRecycle} exact />
<Route path={'/center-home/view/datasource-manage'} component={DatasourceManage} exact />
<Route path={'/center-home/view/data-model'} component={Model} exact />
<Route path={'/center-home/view/asset-map'} component={Map} exact />
<Route path={'/center-home/view/asset-manage'} component={AssetManage} exact />
<Route path={'/center-home/view/asset-recycle'} component={AssetRecycle} exact />
</Switch>
</Router>
</React.Fragment>
......
import * as service from '../service/datasourcemanager';
import { call } from 'redux-saga/effects';
export function* getAllSupportedDatasourceTypies() {
return yield call(service.getAllSupportedDatasourceTypies);
}
export function* getSupportedTargetTypes() {
return yield call(service.getSupportedTargetTypes);
}
export function* getAllDatasources(payload) {
return yield call(service.getAllDatasources, payload);
}
export function* saveDatasource(payload) {
return yield call(service.saveDatasource, payload);
}
export function* validateDatasource(payload) {
return yield call(service.validateDatasource, payload);
}
export function* getDatasource(payload) {
return yield call(service.getDatasource, payload);
}
export function* deleteDatasource(payload) {
return yield call(service.deleteDatasource, payload);
}
export function* getTaskSettingsByDatasource(payload) {
return yield call(service.getTaskSettingsByDatasource, payload);
}
export function* getTasksByDatasourceId(payload) {
return yield call(service.getTasksByDatasourceId, payload);
}
export function* saveTask(payload) {
return yield call(service.saveTask, payload);
}
export function* getTask(payload) {
return yield call(service.getTask, payload);
}
export function* deleteTask(payload) {
return yield call(service.deleteTask, payload);
}
export function* startTask(payload) {
return yield call(service.startTask, payload);
}
......@@ -6,12 +6,12 @@ import { SetSource, Cancel } from '../util/axios'
import { Connect, showMessage } from '../util';
import { reducers } from './reducer';
import * as user from './user';
import * as datasource from './datasource';
import * as map from './map';
import * as datamodel from './datamodel';
import * as assetmanage from './assetmanage';
const funcs = Connect({ user, datamodel, map,assetmanage })
const funcs = Connect({ user, datamodel, map, assetmanage, datasource })
function* request(args) {
const { type, payload, callback, error } = args.args;
......
import { PostJSON, GetJSON, Post, Delete } from "../util/axios"
export function getAllSupportedDatasourceTypies() {
return GetJSON("/metadataharvester/datasource/getAllSupportedDatasourceTypies");
}
export function getSupportedTargetTypes() {
return GetJSON("/metadataharvester/datasource/getSupportedTargetTypes");
}
export function getAllDatasources(payload) {
return GetJSON("/metadataharvester/datasource/getAllDatasources", payload);
}
export function saveDatasource(payload) {
return PostJSON("/metadataharvester/datasource/saveDatasource", payload);
}
export function validateDatasource(payload) {
return Post("/metadataharvester/datasource/validateDatasource", payload);
}
export function getDatasource(payload) {
return GetJSON("/metadataharvester/datasource/getDatasource", payload);
}
export function deleteDatasource(payload) {
return Delete("/metadataharvester/datasource/deleteDatasource", payload);
}
export function getTaskSettingsByDatasource(payload) {
return GetJSON("/metadataharvester/task/getHarvestingTaskSettings", payload);
}
export function getTasksByDatasourceId(payload) {
return GetJSON("/metadataharvester/task/findHarvestingTasks", payload);
}
export function saveTask(payload) {
return PostJSON("/metadataharvester/task/saveHarvestingTask", payload);
}
export function getTask(payload) {
return GetJSON("/metadataharvester/task/getHarvestingTask", payload);
}
export function deleteTask(payload) {
return Delete("/metadataharvester/task/deleteHarvestingTask", payload);
}
export function startTask(payload) {
return PostJSON("/metadataharvester/task/startHarvestingTask", payload);
}
......@@ -120,6 +120,15 @@ export function GetJSON(url, params) {
)
}
export function Delete(url, params) {
const cancelToken = __source ? __source.token : null;
return instance.delete(url, {
params, cancelToken,
}).then(
callback
)
}
export function PostJSON(url, payload) {
const { params = null, data = null } = payload||{};
const cancelToken = __source ? __source.token : null;
......
import React, { useEffect, useState } from 'react';
import { Modal, Select, Form, Input, Divider, Space, Button } from 'antd';
import { dispatch } from '../../../../model';
import { showMessage } from '../../../../util';
const { Option } = Select;
const UpdateDatasourceModal = (props) => {
const { visible, onCancel, databases } = props;
const [ selectedDatabaseKey, setSelectedDatabaseKey ] = useState('');
const [ allSupportedDatasourceTypies, setAllSupportedDatasourceTypies ] = useState([]);
const [ currentSupportedDatasourceTypies, setCurrentSupportedDatasourceTypies ] = useState({});
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ validateLoading, setValidateLoading ] = useState(false);
const [ credentialForm ] = Form.useForm();
const [ datasourceForm ] = Form.useForm();
useEffect(() => {
getAllSupportedDatasourceTypies();
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const getAllSupportedDatasourceTypies = () => {
dispatch({
type: 'datasource.getAllSupportedDatasourceTypies',
callback: data => {
setAllSupportedDatasourceTypies(data||[]);
const _defaultKey = (data||[]).length>0?data[0].type:'';
setSelectedDatabaseKey(_defaultKey);
getCurrentSupportedDatasourceTypies(data||[], _defaultKey);
}
})
}
const getCurrentSupportedDatasourceTypies = (allTypies, type) => {
let _currentTypies = null;
(allTypies||[]).forEach(typies => {
if (typies.type === type) {
_currentTypies = {...typies};
}
})
setCurrentSupportedDatasourceTypies(_currentTypies);
}
const onDatabaseChange = (value) => {
setSelectedDatabaseKey(value);
getCurrentSupportedDatasourceTypies(allSupportedDatasourceTypies, value);
}
const onOk = async() => {
try {
const datasourceRow = await datasourceForm.validateFields();
const credentialRow = await credentialForm.validateFields();
const newDatasource = {...currentSupportedDatasourceTypies};
newDatasource && (newDatasource.targetParameters||[]).forEach(item => {
item.value = datasourceRow[item.name]||'';
});
newDatasource && newDatasource.credential && (newDatasource.credential.credentialParameters||[]).forEach(item => {
item.value = credentialRow[item.name];
})
setConfirmLoading(true);
dispatch({
type: 'datasource.saveDatasource',
payload: {
data: newDatasource
},
callback: data => {
setConfirmLoading(false);
onCancel && onCancel();
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
}
const onTest = async() => {
try {
const datasourceRow = await datasourceForm.validateFields();
const credentialRow = await credentialForm.validateFields();
const newDatasource = {...currentSupportedDatasourceTypies};
newDatasource && (newDatasource.targetParameters||[]).forEach(item => {
item.value = datasourceRow[item.name];
});
newDatasource && newDatasource.credential && (newDatasource.credential.credentialParameters||[]).forEach(item => {
item.value = credentialRow[item.name];
})
setValidateLoading(true);
dispatch({
type: 'datasource.validateDatasource',
payload: {
data: newDatasource
},
callback: data => {
setValidateLoading(false);
if (data === 'true') {
showMessage('success', '测试成功');
} else if (data === 'false') {
showMessage('error', '测试失败');
}
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
}
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 17 },
},
};
return (
<Modal
forceRender
title={'新增数据源'}
visible={visible}
width={600}
onCancel={() => { onCancel && onCancel() }}
footer={
<Space>
<Button type="primary" onClick={onOk} loading={confirmLoading}>保存</Button>
<Button onClick={() => onCancel && onCancel() }>返回</Button>
<Button type="primary" onClick={onTest} loading={validateLoading}>测试连接</Button>
</Space>
}
>
<div className='d-flex mb-5' style={{ alignItems: 'center' }}>
<span className='mr-3' style={{ marginLeft: 'auto' }}>数据库类型:</span>
<Select
value={selectedDatabaseKey}
style={{ width: 150 }}
onChange={onDatabaseChange}
>
{
databases && databases.map((item, index) => {
return (
<Option key={index} value={item.targetType}>{item.targetName}</Option>
);
})
}
</Select>
</div>
<Divider>数据源信息</Divider>
<Form {...formItemLayout} form={datasourceForm}>
{
currentSupportedDatasourceTypies && (currentSupportedDatasourceTypies.targetParameters||[]).map((item, index) => {
return (
<Form.Item
label={item.cnName||''}
name={item.name||''}
key={index}
>
<Input />
</Form.Item>
)
})
}
</Form>
<Divider>认证信息</Divider>
<Form {...formItemLayout} form={credentialForm}>
{
currentSupportedDatasourceTypies && currentSupportedDatasourceTypies.credential && (currentSupportedDatasourceTypies.credential.credentialParameters||[]).map((item, index) => {
return (
<Form.Item
label={item.cnName||''}
name={item.name||''}
key={index}
>
<Input />
</Form.Item>
)
})
}
</Form>
</Modal>
);
}
export default UpdateDatasourceModal;
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import { Modal, Checkbox, Row, Col, Divider, Input, Typography, Form } from 'antd';
import { dispatch } from '../../../../model';
import './UpdateTaskModal.less';
const UpdateTaskModal = (props) => {
const { visible, onCancel, datasourceId } = props;
const [ schemas, setSchemas ] = useState([]);
const [ filterSchemas, setFilterSchemas ] = useState([]);
const [ selectedSchemas, setSelectedSchemas ] = useState([]);
const [ taskSettings, setTaskSettings ] = useState({});
const [ keyword, setKeyword ] = useState('');
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [ form ] = Form.useForm();
useEffect(() => {
if ((datasourceId||'') !== '' ) {
getTaskSettings();
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [datasourceId])
useEffect(() => {
setFilterSchemas((schemas||[]).filter(schema => (schema||'').indexOf(keyword)!==-1));
}, [keyword, schemas])
const getTaskSettings = () => {
dispatch({
type: 'datasource.getTaskSettingsByDatasource',
payload: {
datasourceId
},
callback: data => {
setTaskSettings(data);
data && (data.targetConfParameters||[]).forEach(item => {
if (item.name === 'schema') {
setSchemas(item.selectItem||[]);
}
})
}
})
}
const onOk = async() => {
try {
const row = await form.validateFields();
const newTask = {...taskSettings};
newTask && (newTask.targetConfParameters||[]).forEach(item => {
if (item.name === 'schema') {
item.value = selectedSchemas.join(',');
} else {
item.value = row[item.name]||'';
}
});
setConfirmLoading(true);
dispatch({
type: 'datasource.saveTask',
payload: {
data: newTask
},
callback: data => {
setConfirmLoading(false);
onCancel && onCancel();
},
error: () => {
setConfirmLoading(false);
}
})
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
}
const onSearchInputChange = (e) => {
setKeyword(e.target.value||'');
}
const onCheckChange = (e) => {
if (e.target.checked) {
setSelectedSchemas([...selectedSchemas, e.target.value]);
} else {
const index = selectedSchemas.findIndex(key => key === e.target.value);
selectedSchemas.splice(index, 1)
setSelectedSchemas([...selectedSchemas]);
}
}
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 17 },
},
};
return (
<Modal
className='update-task-modal'
forceRender
title={'新增任务'}
visible={visible}
width={600}
onCancel={() => { onCancel && onCancel() }}
onOk={onOk}
confirmLoading={confirmLoading}
>
<Divider>schema信息</Divider>
<div className='mb-3'>
<span className='mr-3'>schema搜索:</span>
<Input
placeholder="请输入schema名称"
allowClear
value={keyword}
onChange={onSearchInputChange}
style={{ width: 230 }}
/>
</div>
<div style={{ maxHeight: 300, overflow: 'auto' }}>
<Row className='mb-3'>
{
(filterSchemas||[]).map((schema, index) => {
return (
<Col className='mt-1' key={index} md={8}>
<div className='d-flex'>
<Checkbox checked={ selectedSchemas.indexOf(schema)!==-1 } value={schema||''} onChange={onCheckChange} >
</Checkbox>
<Typography.Paragraph className='ml-1' title={schema||''} ellipsis>
{schema||''}
</Typography.Paragraph>
</div>
</Col>
);
})
}
</Row>
</div>
<Divider>过滤信息</Divider>
<Form {...formItemLayout} form={form}>
{
taskSettings && (taskSettings.targetConfParameters||[]).filter(item => item.name!=='schema').map((param, index) => {
return (
<Form.Item
label={param.cnName||''}
name={param.name||''}
key={index}
>
<Input />
</Form.Item>
)
})
}
</Form>
</Modal>
);
}
export default UpdateTaskModal;
\ No newline at end of file
.update-task-modal {
.yy-typography, .yy-typography p {
margin-bottom: 0 !important;
}
}
\ No newline at end of file
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Space, Select, Collapse, Tooltip, Button, Typography, Row, Col, Modal } from 'antd';
import { EditOutlined, DiffOutlined, DeleteOutlined, DatabaseOutlined } from '@ant-design/icons';
import { dispatch } from '../../../model';
import UpdateDatasourceModal from './Component/UpdateDatasourceModal';
import UpdateTaskModal from './Component/UpdateTaskModal';
import { showMessage } from '../../../util';
import { deleteDatasource } from '../../../model/datasource';
const { Option } = Select;
const { Panel } = Collapse;
const DatasourceManage = () => {
const [ rawSupportDatabases, setRawSupportDatabases ] = useState([]);
const [ supportDatabases, setSupportDatabases ] = useState([]);
const [ datasources, setDatasources ] = useState([]);
const [ selectedDatabaseKey, setSelectedDatabaseKey ] = useState('');
const [ loadingDatabases, setLoadingDatabases ] = useState(false);
const [ updateDatasourceModalVisible, setUpdateDatasourceModalVisible ] = useState(false);
const [ updateTaskModalVisible, setUpdateTaskModalVisible ] = useState(false);
const [ currentDatasourceId, setCurrentDatasourceId ] = useState('');
const [ tasksBindDatasourceId, setTasksBindDatasourceId ] = useState([]);
const [modal, contextHolder] = Modal.useModal();
useEffect(() => {
getSupportDatabasesThenGetAllDatasources();
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const getSupportDatabasesThenGetAllDatasources = () => {
setLoadingDatabases(true);
dispatch({
type: 'datasource.getSupportedTargetTypes',
callback: data => {
setLoadingDatabases(false);
setRawSupportDatabases(data||[]);
setSupportDatabases([{ targetType: 'all', targetName: '全部' }, ...(data||[])]);
setSelectedDatabaseKey('all');
getAllDatasources();
},
error: () => {
setLoadingDatabases(false);
}
})
}
const getAllDatasources = () => {
dispatch({
type: 'datasource.getAllDatasources',
payload: {
},
callback: data => {
setDatasources(data);
},
error: () => {
}
})
}
const getTasksByDatasourceId = (datasourceId) => {
dispatch({
type: 'datasource.getTasksByDatasourceId',
payload: {
datasourceId
},
callback: data => {
const index = (tasksBindDatasourceId||[]).findIndex(item => item.datasourceId === datasourceId);
if (index === -1) {
tasksBindDatasourceId.push({ datasourceId, tasks: data||[] });
} else {
tasksBindDatasourceId.splice(index, 1, { datasourceId, tasks: data||[] })
}
setTasksBindDatasourceId([...tasksBindDatasourceId]);
setDatasources([...datasources]);
},
error: () => {
}
})
}
const onDatabaseChange = (value) => {
setSelectedDatabaseKey(value);
}
const addDatasource = () => {
setUpdateDatasourceModalVisible(true);
}
const deleteDatasource = (datasource) => {
modal.confirm({
title: '是否确认删除该数据源?',
content: '',
onOk: () => {
dispatch({
type: 'datasource.deleteDatasource',
payload: {
datasourceId: datasource.id
},
callback: () => {
showMessage('success', '删除数据源成功');
}
});
}
});
}
const onCollapseChange = (keys) => {
let key = '';
if ((keys||[]).length>0) {
key = Number(keys[keys.length-1]);
}
const index = (tasksBindDatasourceId||[]).findIndex(item => item.datasourceId === key);
if (index === -1) {
getTasksByDatasourceId(key);
}
}
const onUpdateDatasourceModalCancel = () => {
setUpdateDatasourceModalVisible(false);
}
const addTask = (datasource) => {
setUpdateTaskModalVisible(true);
setCurrentDatasourceId(datasource.id||'');
}
const startTask = (task) => {
modal.confirm({
title: '是否执行该任务?',
content: '',
onOk: () => {
dispatch({
type: 'datasource.startTask',
payload: {
params: {
harvestingTaskId: task.id
}
},
callback: () => {
showMessage('success', '执行任务成功');
}
});
}
});
}
const deleteTask = (task) => {
modal.confirm({
title: '是否确认删除该任务?',
content: '',
onOk: () => {
dispatch({
type: 'datasource.deleteTask',
payload: {
harvestingTaskId: task.id
},
callback: () => {
showMessage('success', '删除任务成功');
}
});
}
});
}
const onUpdateTaskModalVisibleCancel = () => {
setUpdateTaskModalVisible(false);
}
const DatasourceManage = (props) => {
return (
<>
Datasource manage
</>
<div className='datasource-manage' style={{ backgroundColor: '#fff' }}>
<div
className='d-flex p-3'
style={{
borderBottom: '1px solid #EFEFEF',
justifyContent: 'space-between'
}}
>
<Space>
<span>数据库类型:</span>
<Select
loading={loadingDatabases}
value={selectedDatabaseKey}
style={{ width: 130 }}
onChange={onDatabaseChange}
>
{
supportDatabases && supportDatabases.map((item, index) => {
return (
<Option key={index} value={item.targetType}>{item.targetName}</Option>
);
})
}
</Select>
</Space>
<Space>
<Button type='primary' onClick={addDatasource}>新增数据源</Button>
</Space>
</div>
<Collapse onChange={onCollapseChange}>
{
(datasources||[]).map((datasource, index) => {
let _currentTasks = [];
(tasksBindDatasourceId||[]).forEach(item => {
if (item.datasourceId === datasource.id) {
_currentTasks = item.tasks||[];
}
})
return (
<Panel
key={datasource.id||''}
header={
<Row>
{
(datasource.targetParameters||[]).map((param, _index) => {
return (
<Col className='mt-3' key={_index} md={8}>
<Typography.Paragraph title={ `${param.cnName||''}: ${param.value||''}` } ellipsis>
{ `${param.cnName||''}: ${param.value||''}` }
</Typography.Paragraph>
</Col>
);
})
}
</Row>
}
extra={
<Space>
<Tooltip placement='bottom' title={'修改'}>
<Button icon={<EditOutlined />} size='small' onClick={() => { }} />
</Tooltip>
<Tooltip placement='bottom' title={'新增任务'}>
<Button icon={<DiffOutlined />} size='small' onClick={() => { addTask(datasource); }} />
</Tooltip>
<Tooltip placement='bottom' title={'删除'}>
<Button icon={<DeleteOutlined />} size='small' onClick={() => { deleteDatasource(datasource); }} />
</Tooltip>
</Space>
}
>
<>
{
(_currentTasks||[]).map((task, index) => {
return (
<div className='d-flex' key={index} style={{ justifyContent: 'space-between' }}>
<Row style={{ flex: 1 }}>
{
task && (task.targetConfParameters||[]).filter(param => param.name!=='schema').map((_param, _index) => {
return (
<Col key={_index} className='mt-3' md={8}>
<Typography.Paragraph title={ `${_param.cnName||''}: ${_param.value||''}` } ellipsis>
{ `${_param.cnName||''}: ${_param.value||''}` }
</Typography.Paragraph>
</Col>
)
})
}
</Row>
<div>
<Space>
<Tooltip placement='bottom' title='抽取'>
<Button icon={<DatabaseOutlined />} size='small' onClick={() => { startTask(task); }} />
</Tooltip>
<Tooltip placement='bottom' title='修改'>
<Button icon={<EditOutlined />} size='small' onClick={() => { }} />
</Tooltip>
<Tooltip placement='bottom' title='删除'>
<Button icon={<DeleteOutlined />} size='small' onClick={() => { deleteTask(task); }} />
</Tooltip>
</Space>
</div>
</div>
);
})
}
</>
</Panel>
);
})
}
</Collapse>
<UpdateDatasourceModal
visible={updateDatasourceModalVisible}
databases={rawSupportDatabases}
onCancel={onUpdateDatasourceModalCancel}
/>
<UpdateTaskModal
visible={updateTaskModalVisible}
datasourceId={currentDatasourceId}
onCancel={onUpdateTaskModalVisibleCancel}
/>
{contextHolder}
</div>
);
}
......
......@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
import { GetSession } from "../../util";
import { ManageLayout } from "../../layout";
import DatasourceManage from './DatasourceManage';
import Map from './Map';
import Model from './Model';
import AssetManage from './AssetManage';
......@@ -23,6 +24,7 @@ class Manage extends Component {
content={
session && session.userId ? (
<Switch>
<Route path={`${match.path}/datasource-manage`} component={DatasourceManage} />
<Route path={`${match.path}/data-model`} component={Model} />
<Route path={`${match.path}/asset-map`} component={Map} />
<Route path={`${match.path}/asset-manage`} component={AssetManage} />
......
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