Commit a9565736 by zhaochengxiang

数据服务

parent 361facf3
......@@ -11,8 +11,9 @@ import * as datamodel from './datamodel';
import * as assetmanage from './assetmanage';
import * as tag from './tag';
import * as msd from './msd';
import * as psd from './psd';
const funcs = Connect({ user, datamodel, map, assetmanage, datasource, tag, msd })
const funcs = Connect({ user, datamodel, map, assetmanage, datasource, tag, msd, psd })
function* request(args) {
const { type, payload, callback, error } = args.args;
......
import * as psd from '../service/psd';
import { call } from 'redux-saga/effects';
export function* refreshCatalog(payload) {
return yield call(psd.refreshCatalog, payload);
}
export function* loadStateCatalog(payload) {
return yield call(psd.loadStateCatalog, payload);
}
export function* saveCatalog(payload) {
return yield call(psd.saveCatalog, payload);
}
export function* deleteCatalog(payload) {
return yield call(psd.deleteCatalog, payload);
}
export function* upDownCatalog(payload) {
return yield call(psd.upDownCatalog, payload);
}
export function* getServices(payload) {
return yield call(psd.getServices, payload);
}
export function* deleteService(payload) {
return yield call(psd.deleteService, payload);
}
export function* recatalogService(payload) {
return yield call(psd.recatalogService, payload);
}
export function* nextState(payload) {
return yield call(psd.nextState, payload);
}
\ No newline at end of file
......@@ -47,6 +47,10 @@ export const routes = [
name: 'msd-manage',
text: '主数据管理'
},
{
name: 'data-service',
text: '数据服务管理'
},
]
}
];
......
import { PostFile, GetJSON, PostJSON, Post, Get } from "../util/axios"
export function refreshCatalog() {
return GetJSON("/pdataservice/pdsCURD/refreshDataServiceCatalog")
}
export function loadStateCatalog() {
return GetJSON("/pdataservice/pdsCURD/loadDataServiceStateCatalog")
}
export function saveCatalog(payload) {
return PostJSON("/pdataservice/pdsCURD/saveDataServiceCatalog", payload)
}
export function deleteCatalog(payload) {
return PostJSON("/pdataservice/pdsCURD/deleteDataServiceCatalog", payload)
}
export function upDownCatalog(payload) {
return GetJSON("/pdataservice/pdsCURD/upDownDataServiceCatalog", payload)
}
export function getServices(payload) {
return GetJSON("/pdataservice/pdsCURD/getCurrentDataServiceCatalog", payload)
}
export function deleteService(payload) {
return PostJSON("/pdataservice/pdsCURD/deleteDataService", payload);
}
export function recatalogService(payload) {
return Post("/pdataservice/pdsCURD/recatalogDataService", payload);
}
export function nextState(payload) {
return GetJSON("/pdataservice/pdsCURD/nextState", payload);
}
import { useMemo, useState } from 'react';
import classNames from 'classnames';
import { ResizableBox } from 'react-resizable';
import { CaretLeftOutlined, CaretRightOutlined } from '@ant-design/icons';
import Tree from './tree'
import '../DataMaster/Define/index.less';
const FC = (props) => {
const [collapse, setCollapse] = useState(false);
const classes = useMemo(() => {
return classNames('data-master', {
'data-master-collapse': collapse,
});
}, [collapse]);
const onCollapseClick = () => {
setCollapse(!collapse);
}
return (
<div className={classes}>
<ResizableBox
className='left-wrap'
width={230}
height={Infinity}
axis='x'
minConstraints={[230, Infinity]} maxConstraints={[Infinity, Infinity]}
>
<Tree />
</ResizableBox>
<div className='left-collapse-wrap'>
<div className='left-collapse' onClick={onCollapseClick}>
{ collapse ? <CaretRightOutlined /> : <CaretLeftOutlined /> }
</div>
</div>
<div className='right-wrap'>
</div>
</div>
)
}
export default FC;
\ No newline at end of file
import React, { useState, useEffect, useContext } from "react";
import { Tooltip, Tree, Modal, Spin, Dropdown, Menu, Button, AutoComplete } from "antd";
import { PlusOutlined, SyncOutlined, ImportOutlined, UnorderedListOutlined, ReloadOutlined } from '@ant-design/icons';
import classnames from 'classnames';
import { useContextMenu, Menu as RcMenu, Item as RcItem } from "react-contexify";
import { dispatch } from '../../../model';
import { showMessage, getQueryParam, highlightSearchContentByTerms } from '../../../util';
import UpdateTreeItemModal from './update';
import '../Model/Component/ModelTree.less';
import 'react-contexify/dist/ReactContexify.css';
const { Option } = AutoComplete;
const MENU_ID = 'service-tree';
const viewModes = [
{ key: 'dir', name: '目录视角' },
{ key: 'state', name: '服务状态视角' }
];
const FC = (props) => {
const { refrence, onSelect, onViewChange } = props;
const { show } = useContextMenu({
id: MENU_ID,
});
const [ loading, setLoading ] = useState(false);
const [ treeData, setTreeData ] = useState(null);
const [ item, setItem ] = useState(null);
const [ rootId, setRootId ] = useState('');
const [ visible, setVisible ] = useState(false);
const [ type, setType ] = useState(null);
const [ expandedKeys, setExpandedKeys ] = useState([]);
const [ autoExpandParent, setAutoExpandParent ] = useState(false);
const [ viewSelectedKey, setViewSelectedKey ] = useState(viewModes[0].key);
const [ currentRightClickDir, setCurrentRightClickDir ] = useState({});
const [ searchKeyword, setSearchKeyword ] = useState('');
const [ dataList, setDataList ] = useState([]);
const [ options, setOptions ] = useState([]);
const [modal, contextHolder] = Modal.useModal();
useEffect(() => {
getDirTreeData();
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const getDirTreeData = (defaultSelectedId='') => {
setLoading(true);
dispatch({
type: 'psd.refreshCatalog',
callback: data => {
data.key = data.id||'';
data.title = data.name||'';
data.children = data.subCatalogs||[];
let defaultItem = null;
function recursion(subCatalogs) {
if ((subCatalogs||[]).length===0) return;
(subCatalogs||[]).forEach(catalog=> {
catalog.key = catalog.id||'';
catalog.title = catalog.name||'';
catalog.children = catalog.subCatalogs||[];
if (catalog.id === defaultSelectedId) {
defaultItem = catalog;
}
recursion(catalog.subCatalogs);
})
}
recursion(data.subCatalogs);
setLoading(false);
setTreeData(data.subCatalogs||[]);
setRootId(data.id||'');
const _dataList = [];
generateList(data.subCatalogs||[], _dataList);
setDataList(_dataList);
if (defaultItem) {
const expandedKeys = _dataList
.map(item => {
if (item.key.indexOf(defaultSelectedId) > -1) {
return getParentKey(item.key, data.subCatalogs||[]);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
setExpandedKeys([...expandedKeys, defaultSelectedId]);
setAutoExpandParent(true);
setItem(defaultItem);
onSelect && onSelect(defaultItem.key||'');
} else if (!refrence) {
const currentItem = (data.subCatalogs||[]).length>0?data.subCatalogs[0]: null;
setItem(currentItem);
if (currentItem && currentItem.key) {
setExpandedKeys([currentItem?.key]);
}
onSelect && onSelect(currentItem?(currentItem.key||''):'');
}
},
error: () => {
setLoading(false);
}
})
}
const getStateTreeData = () => {
setLoading(true);
dispatch({
type: 'psd.loadStateCatalog',
callback: data => {
setLoading(false);
let _treeData = data?.subCatalogs||[];
_treeData.forEach(item => {
item.title = item.cnName;
item.key = item.id;
})
setTreeData(_treeData);
setItem(_treeData.length>0?_treeData[0]:{});
onSelect && onSelect(_treeData.length>0?_treeData[0].key:'');
},
error: () => {
setLoading(false);
}
});
}
const generateList = (treeData, list, path = null) => {
for (let i = 0; i < treeData.length; i++) {
const node = treeData[i];
const { id, name } = node;
const currentPath = path ? `${path}/${name}` : name;
list.push({ key: id , title: currentPath, value: currentPath });
if (node.children) {
generateList(node.children, list, currentPath);
}
}
};
const getParentKey = (key, tree) => {
let parentKey;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some(item => item.id === key)) {
parentKey = node.id;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey;
};
const onExpand = (expandedKeys) => {
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
const onViewClick = ({ key }) => {
if (viewSelectedKey && viewSelectedKey===key ) return;
setViewSelectedKey(key);
setSearchKeyword('');
onViewChange && onViewChange(key);
if (key === 'dir') {
getDirTreeData();
} else {
getStateTreeData();
}
}
const onTreeSelect = (keys,data) => {
if ((keys||[]).length === 0) {
return;
}
const _item = {...data.selectedNodes[0]||[]};
setItem(_item);
onSelect && onSelect(_item.key);
}
const add = () => {
setVisible(true);
setType('add');
}
const update = () => {
setVisible(true);
setType('update');
}
const refresh = () => {
if (viewSelectedKey==='dir') {
getDirTreeData(item?.key||'');
} else {
getStateTreeData(item?.key||'');
}
}
const sync = () => {
getDirTreeData(item?.key||'', null, 'load');
}
const moveNode = (steps) => {
setLoading(true);
dispatch({
type: 'psd.upDownCatalog',
payload: {
modelCatalogId: currentRightClickDir.id,
steps
},
callback: () => {
showMessage('success', (steps===-1)?'上移目录成功':'下移目录成功');
getDirTreeData(item.id);
},
error: () => {
setLoading(false);
}
});
}
const deleteNode = () => {
modal.confirm({
title: '提示!',
content: '删除目录会删除相关的模型,您确定删除吗?',
onOk: () => {
setLoading(true);
dispatch({
type: 'psd.deleteCatalog',
payload: {
params: {
easyDataModelerCatalogId: currentRightClickDir.id
}
},
callback: () => {
showMessage('success', '删除目录成功');
if (item && currentRightClickDir && item.id===currentRightClickDir.id) {
getDirTreeData();
} else {
getDirTreeData(item.id);
}
},
error: () => {
setLoading(false);
}
});
}
});
}
const onUpdateTreeItemModalOk = (id, updateItem) => {
setVisible(false);
getDirTreeData(id);
}
const onUpdateTreeItemModalCancel = () => {
setVisible(false);
}
const treeDirectoryChanged = (did) => {
let defaultItem = null;
function recursion(subCatalogs) {
if ((subCatalogs||[]).length===0) return;
(subCatalogs||[]).forEach(catalog=> {
catalog.key = catalog.id||'';
catalog.title = catalog.name||'';
catalog.children = catalog.subCatalogs||[];
if (catalog.id === did) {
defaultItem = catalog;
}
recursion(catalog.subCatalogs);
})
}
recursion(treeData||[]);
if (defaultItem) {
const expandedKeys = dataList
.map(item => {
if (item.key.indexOf(did) > -1) {
return getParentKey(item.key, treeData||[]);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
setExpandedKeys([...expandedKeys, did]);
setAutoExpandParent(true);
setItem(defaultItem);
onSelect && onSelect(defaultItem.key||'');
}
}
const onAutoCompleteSearch = (searchText) => {
setOptions(
!searchText ? [] : (dataList||[]).filter(item => item.title.indexOf(searchText)!==-1),
);
};
const onAutoCompleteChange = (value) => {
setSearchKeyword(value);
}
const onAutoCompleteSelect = (value, option) => {
const paths = value.split('/');
setSearchKeyword(paths[paths.length-1]);
treeDirectoryChanged(option.key);
};
const displayMenu = (e) => {
show(e, {
position: {
x: e.clientX + 30,
y: e.clientY - 10
}
});
}
const exportMenu = (
<Menu selectedKeys={[viewSelectedKey]} onClick={onViewClick}>
{
viewModes && viewModes.map(item => {
return (
<Menu.Item key={item.key} value={item.key} >
<div style={{ textAlign: 'center' }}>
{item.name}
</div>
</Menu.Item>
)
})
}
</Menu>
);
const classes = classnames('model-tree', {
'model-tree-recatalog': (refrence === 'recatalog')
});
return (
<div className={classes}>
{
!refrence && <div
className='p-3'
style={{
display: 'flex',
borderBottom: "1px solid #EFEFEF",
height: 57,
alignItems: 'center',
}}
>
<Dropdown overlay={exportMenu} placement="bottomLeft">
<Tooltip title="视角">
<UnorderedListOutlined className='default' style={{ fontSize:16,cursor:'pointer' }} />
</Tooltip>
</Dropdown>
{
(viewSelectedKey==='dir') && (
<Tooltip title="新增目录" className='ml-6'>
<PlusOutlined className='default' onClick={add} style={{ fontSize:16,cursor:'pointer' }} />
</Tooltip>
)
}
<Tooltip title="刷新目录" className='ml-6'>
<Button type='text' icon={<ReloadOutlined className='default' />} size='small' onClick={refresh} />
</Tooltip>
</div>
}
<div className='p-3'>
<Spin spinning={loading} >
{
(viewSelectedKey==='dir') && <AutoComplete
allowClear
value={searchKeyword}
style={{ marginBottom: 10, width: '100%' }}
onSelect={onAutoCompleteSelect}
onSearch={onAutoCompleteSearch}
onChange={onAutoCompleteChange}
onClear={() => { setSearchKeyword(''); }}
>
{
(options||[]).map((item, index) => {
return (
<Option key={item.key} value={item.value}>
<div style={{ whiteSpace: 'normal' }}>
{highlightSearchContentByTerms(item.title, [searchKeyword])}
</div>
</Option>
);
})
}
</AutoComplete>
}
<Tree
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
showLine
showIcon={false}
onSelect={onTreeSelect}
treeData={treeData}
selectedKeys={[item?item.key:'']}
titleRender={(nodeData) => {
return <span title={nodeData?.remark||''}>{nodeData?.name||''}</span>;
}}
onRightClick={({event, node}) => {
if (viewSelectedKey==='dir') {
setCurrentRightClickDir(node);
displayMenu(event);
}
}}
/>
</Spin>
</div>
<UpdateTreeItemModal
visible={visible}
type={type}
item={(type==='add')?item:currentRightClickDir}
rootId={rootId}
onOk={onUpdateTreeItemModalOk}
onCancel={onUpdateTreeItemModalCancel}
/>
{
(refrence!=='recatalog') && <RcMenu id={MENU_ID}>
<RcItem id="edit" onClick={update}>
修改目录
</RcItem>
<RcItem id="up" onClick={() => { moveNode(-1); }}>
上移目录
</RcItem>
<RcItem id="down" onClick={() => { moveNode(1); }}>
下移目录
</RcItem>
<RcItem id="delete" onClick={deleteNode}>
删除目录
</RcItem>
</RcMenu>
}
{contextHolder}
</div>
);
}
export default FC;
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Modal, Form, Input, Radio } from 'antd';
import { dispatchLatest } from '../../../model';
class UpdateTreeItemForm extends React.Component {
constructor(props){
super(props);
this.state = {
radioDisable: false
}
}
componentDidMount() {
this.radioState();
}
componentDidUpdate(preProps, preState) {
const { item } = this.props;
if (item!==preProps.item) {
this.radioState();
}
}
radioState = () => {
const { item } = this.props;
this.setState({ radioDisable: item? false: true })
}
render() {
const { type, form } = this.props;
const { radioDisable } = this.state;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 },
},
};
return (
<Form
{...formItemLayout}
form={form}
>
{
type==='add'&&<Form.Item label="目录类型" name="action">
<Radio.Group disabled={radioDisable} >
<Radio value='root'>根目录</Radio>
<Radio value='sub'>子目录</Radio>
</Radio.Group>
</Form.Item>
}
<Form.Item
label="名称"
name="name"
rules={[{ required: true, message: '请输入名称!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="描述"
name="remark"
rules={[{ required: true, message: '请输入描述!' }]}
>
<Input />
</Form.Item>
</Form>
);
}
}
const FC = (props) => {
const { onOk, type, item, onCancel, visible, rootId } = props;
const [ confirmLoading, setConfirmLoading ] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
if (visible) {
let _action = '';
if (type === 'add') {
_action = item ? 'sub' : 'root';
}
form.setFields([{ name: 'name', errors: [] }, { name: 'remark', errors: [] }]);
if (type === 'add') {
form.setFieldsValue({ action: _action, name: '', remark: '' });
} else {
form.setFieldsValue({ action: '', name: item?item.name:'', remark: item?item.remark:'' });
}
}
//eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible])
const handleOk = async () => {
setConfirmLoading(true);
try {
const values = await form.validateFields();
let payload = null;
if (type === 'add' && values.action==='root') {
payload = {
name: values.name||'',
remark: values.remark||'',
parentId: rootId
};
} else if (type === 'add') {
payload = {
name: values.name||'',
remark: values.remark||'',
parentId: item.id
};
} else {
payload = {
...item,
name: values.name||'',
remark: values.remark||'',
}
}
dispatchLatest({
type: 'psd.saveCatalog',
payload: {
data: payload
},
callback: id => {
setConfirmLoading(false);
if (onOk) {
onOk(id, payload);
}
},
error: () => {
setConfirmLoading(false);
}
});
} catch (errInfo) {
setConfirmLoading(false);
}
}
return (
<Modal
forceRender
confirmLoading={confirmLoading}
visible={visible}
title={type==='add'?"新增目录":"更新目录"}
onOk={handleOk}
onCancel={() => {
setConfirmLoading(false);
onCancel && onCancel();
}}
>
<UpdateTreeItemForm form={form} item={item} type={type} />
</Modal>
);
}
export default FC;
\ No newline at end of file
......@@ -14,6 +14,7 @@ import AssetBrowse from './AssetBrowse';
import AssetRecycle from './AssetRecycle';
import DataMasterDefine from "./DataMaster/Define";
import DataMasterManage from "./DataMaster/Manage";
import DataServiceManage from './DataService';
class Manage extends Component {
......@@ -37,6 +38,7 @@ class Manage extends Component {
<Route path={`${match.path}/asset-recycle`} component={AssetRecycle} />
<Route path={`${match.path}/msd-define`} component={DataMasterDefine} />
<Route path={`${match.path}/msd-manage`} component={DataMasterManage} />
<Route path={`${match.path}/data-service`} component={DataServiceManage} />
</Switch>
) : (
<GetSession {...this.props} />
......
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