peng.peng 2 years ago
parent
commit
a0d6084bdc
  1. 47
      api/app/lib/controllers/latestMetadata/index.js
  2. 2
      scripts/0.0.4/01_alter_t_metadata_database&t_resource_consumption.sql
  3. 51
      web/client/src/components/buttonGroup/index.js
  4. 4
      web/client/src/components/index.js
  5. 36
      web/client/src/sections/metadataManagement/actions/metadata.js
  6. 232
      web/client/src/sections/metadataManagement/containers/databasesTable.js
  7. 105
      web/client/src/sections/metadataManagement/containers/filesTable.js
  8. 9
      web/client/src/sections/metadataManagement/containers/latestMetadata.js
  9. 51
      web/client/src/sections/metadataManagement/containers/metadataTab.js
  10. 102
      web/client/src/sections/metadataManagement/containers/restapisTable.js

47
api/app/lib/controllers/latestMetadata/index.js

@ -114,13 +114,15 @@ async function getMetadataDatabases(ctx) {
const { catalog, limit, offset, keywords, orderBy = 'createAt', orderDirection = 'desc' } = ctx.query;
const where = { catalog: catalog };
if (keywords) {
where[$or] = [{ name: { $like: keywords } }, { code: { $like: keywords } }, { type: { $like: keywords } }]
where['$or'] = [{ name: { $iLike: `%${keywords}%` } },
{ code: { $iLike: `%${keywords}%` } },
{ type: { $iLike: `%${keywords}%` } }]
}
const rslt = await models.MetadataDatabase.findAll({
const findObj = {
include: [
{
model: models.User,
attributes: ['id', 'name'],
attributes: ['id', 'name', 'username'],
},
{
model: models.TagDatabase,
@ -130,10 +132,13 @@ async function getMetadataDatabases(ctx) {
}],
where: where,
order: [[orderBy, orderDirection]],
offset: Number(offset) * Number(limit),
limit: Number(limit),
distinct: true
});
}
if (Number(limit) > 0 && Number(offset) >= 0) {
findObj.offset = Number(offset) * Number(limit);
findObj.limit = Number(limit);
}
const rslt = await models.MetadataDatabase.findAndCountAll(findObj);
ctx.status = 200;
ctx.body = rslt;
} catch (error) {
@ -151,13 +156,13 @@ async function getMetadataFiles(ctx) {
const { catalog, limit, offset, keywords, orderBy = 'createAt', orderDirection = 'desc' } = ctx.query;
const where = { catalog: catalog };
if (keywords) {
where[$or] = [{ name: { $like: keywords } }, { type: { $like: keywords } }]
where['$or'] = [{ name: { $iLike: `%${keywords}%` } }, { type: { $iLike: `%${keywords}%` } }]
}
const rslt = await models.MetadataFile.findAll({
const findObj = {
include: [
{
model: models.User,
attributes: ['id', 'name'],
attributes: ['id', 'name', 'username'],
},
{
model: models.TagFile,
@ -167,10 +172,13 @@ async function getMetadataFiles(ctx) {
}],
where: where,
order: [[orderBy, orderDirection]],
offset: Number(offset) * Number(limit),
limit: Number(limit),
distinct: true
});
};
if (Number(limit) > 0 && Number(offset) >= 0) {
findObj.offset = Number(offset) * Number(limit);
findObj.limit = Number(limit);
}
const rslt = await models.MetadataFile.findAndCountAll(findObj);
ctx.status = 200;
ctx.body = rslt;
} catch (error) {
@ -188,13 +196,13 @@ async function getMetadataRestapis(ctx) {
const { catalog, limit, offset, keywords, orderBy = 'createAt', orderDirection = 'desc' } = ctx.query;
const where = { catalog: catalog };
if (keywords) {
where.name = { $like: keywords };
where.name = { $iLike: `%${keywords}%` };
}
const rslt = await models.MetadataRestapi.findAll({
const findObj = {
include: [
{
model: models.User,
attributes: ['id', 'name'],
attributes: ['id', 'name', 'username'],
},
{
model: models.TagRestapi,
@ -204,10 +212,13 @@ async function getMetadataRestapis(ctx) {
}],
where: where,
order: [[orderBy, orderDirection]],
offset: Number(offset) * Number(limit),
limit: Number(limit),
distinct: true
});
};
if (Number(limit) > 0 && Number(offset) >= 0) {
findObj.offset = Number(offset) * Number(limit);
findObj.limit = Number(limit);
}
const rslt = await models.MetadataRestapi.findAndCountAll(findObj);
ctx.status = 200;
ctx.body = rslt;
} catch (error) {

2
scripts/0.0.4/01_alter_t_metadata_database&t_resource_consumption.sql

@ -0,0 +1,2 @@
alter table t_metadata_database alter column type type varchar(255) using type::varchar(255);
alter table t_resource_consumption alter column approve_state type varchar(20) using approve_state::varchar(20);

51
web/client/src/components/buttonGroup/index.js

@ -0,0 +1,51 @@
'use strict';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Button, Popover, Icon } from 'antd';
import { EllipsisOutlined } from '@ant-design/icons';
class ButtonGroup extends Component {
constructor(props) {
super(props);
this.state = {
};
}
content = () => {
<Button></Button>
}
render_ = () => {
const { children } = this.props
return (
<div style={{ cursor: 'pointer' }}>
<Popover placement="bottomRight" content={children} arrowPointAtCenter>
<EllipsisOutlined style={{ fontSize: 20, fontWeight: 'bolder' }} />
</Popover>
</div >
)
}
render() {
const { children } = this.props
if (children) {
if (Array.isArray(children)) {
if (children.some(c => c)) {
return this.render_()
}
} else {
return this.render_()
}
}
return ''
}
}
function mapStateToProps(state) {
return {
}
}
export default connect(mapStateToProps)(ButtonGroup);

4
web/client/src/components/index.js

@ -4,11 +4,13 @@
import Upload from './Upload';
import Uploads from './Uploads';
import NoResource from './no-resource';
import ExportAndImport from './export'
import ExportAndImport from './export';
import ButtonGroup from './buttonGroup';
export {
Upload,
Uploads,
NoResource,
ExportAndImport,
ButtonGroup
};

36
web/client/src/sections/metadataManagement/actions/metadata.js

@ -50,4 +50,40 @@ export function delResourceCatalog(id) {
option: '删除资源目录',
}
});
}
export function getMetadataDatabases(params) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: params,
actionType: 'GET_METADATA_DATABASES_LIST',
url: ApiTable.getMetadataDatabases,
msg: { error: '获取库表元数据列表失败' },
reducer: { name: 'metadataDatabases' }
});
}
export function getMetadataFiles(params) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: params,
actionType: 'GET_METADATA_FILES_LIST',
url: ApiTable.getMetadataFiles,
msg: { error: '获取文件元数据列表失败' },
reducer: { name: 'metadataFiles' }
});
}
export function getMetadataRestapis(params) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: params,
actionType: 'GET_METADATA_RESTAPIS_LIST',
url: ApiTable.getMetadataRestapis,
msg: { error: '获取接口元数据列表失败' },
reducer: { name: 'metadataRestapis' }
});
}

232
web/client/src/sections/metadataManagement/containers/databasesTable.js

@ -0,0 +1,232 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Table, Popconfirm, Button, Input } from 'antd';
import { ButtonGroup } from '$components';
import moment from 'moment';
import FileSaver from 'file-saver';
const DatabaseTable = (props) => {
const { user, dispatch, actions, clientHeight, resourceCatalogId, isRequesting } = props;
const { metadataManagement } = actions;
const SortValues = { 'ascend': 'asc', 'descend': 'desc' };
const [tableData, setTableData] = useState([]);
const [tableDataCount, setTableDataCount] = useState(0);//Table数据
const [createAtSort, setCreateAtSort] = useState('descend');
const [keywords, setKeywords] = useState('');
const [limit, setLimit] = useState(10);
const [currentPage, setCurrentPage] = useState(1);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [selectedRows, setSelectedRows] = useState([]);
useEffect(() => {
setCreateAtSort('descend');
initData({ limit, offset: currentPage - 1, orderDirection: SortValues[createAtSort] });
}, [resourceCatalogId]);
const initData = (query = {}) => {
dispatch(metadataManagement.getMetadataDatabases({ catalog: resourceCatalogId, keywords, orderBy: 'createAt', ...query })).then(res => {
if (res.success) {
setTableData(res.payload.data.rows);
setTableDataCount(res.payload.data.count);
}
})
}
const onView = (id) => { }
const onEdit = (record) => { }
const confirmDelete = (id) => { }
const marking = (id) => { }
const applyResources = (id) => { }
const onTableChange = (pagination, filters, sorter) => {
let limit = Number.parseInt(pagination.pageSize);
let offset = Number.parseInt(pagination.current) - 1;
setCurrentPage(pagination.current);
setLimit(limit);
let query = { offset, limit, orderDirection: SortValues[createAtSort] };
if (sorter.columnKey === 'createAt') {
query.orderDirection = SortValues[sorter.order];
setCreateAtSort(sorter.order);
}
setSelectedRowKeys([]);
setSelectedRows([]);
initData(query);
}
const columns = [{
title: '名称',
dataIndex: 'name',
key: 'name',
width: '16%',
ellipsis: true
}, {
title: '代码',
dataIndex: 'code',
key: 'code',
width: '16%',
ellipsis: true
}, {
title: '元数据类型',
dataIndex: 'type',
key: 'type',
width: '10%'
}, {
title: '标签',
dataIndex: 'tags',
key: 'tags',
width: '18%',
ellipsis: true,
render: (text, record, index) => {
let tagName = record.tagDatabases.map(tagSet => tagSet.tag.name);
return tagName.join(',');
}
}, {
title: '创建者',
dataIndex: 'createBy',
key: 'createBy',
width: '14%',
render: (text, record, index) => {
return record.user.username
}
}, {
title: '创建时间',
dataIndex: 'createAt',
key: 'createAt',
width: '18%',
sortOrder: createAtSort,
sorter: (a, b) => moment(a.createAt).valueOf() - moment(b.createAt).valueOf(),
sortDirections: ['descend', 'ascend', 'descend'],
render: (text, record, index) => {
return moment(text).format('YYYY-MM-DD HH:mm:ss')
}
}, {
title: '操作',
dataIndex: 'action',
width: '8%',
render: (text, record) => {
return <ButtonGroup>
<a onClick={() => onView(id)}>查看</a>
<a style={{ marginLeft: 10 }} onClick={() => onEdit(record)}>编辑</a>
<Popconfirm
title="是否确认删除该元数据?若确认删除则关联的数据将一并删除!"
onConfirm={() => confirmDelete(record.id)}
> <a style={{ marginLeft: 10 }}>删除</a></Popconfirm>
<a style={{ marginLeft: 10 }} onClick={() => marking(record.id)}>打标</a>
<a style={{ marginLeft: 10 }} onClick={() => applyResources(record.id)}>申请资源</a>
</ButtonGroup>
}
}];
const onSearch = () => {
setSelectedRowKeys([]);
setSelectedRows([]);
setCurrentPage(1);
initData({ limit, offset: 0, orderDirection: SortValues[createAtSort] });
}
const handleExport = (isAll = false) => {
let tableHeader = `<tr>`;
columns.filter(c => c.dataIndex != 'action').map(c => { tableHeader += `<th><div>${c.title}</div></th>`; });
tableHeader += '</tr>';
if (isAll) {
dispatch(metadataManagement.getMetadataDatabases({ catalog: resourceCatalogId })).then(res => {
if (res.success) {
handleExportTable(tableHeader, res.payload.data.rows, isAll);
}
})
} else {
let data = []
if (createAtSort === 'descend') {
data = selectedRows.sort((a, b) => moment(b.createAt).valueOf() - moment(a.createAt).valueOf());
} else {
data = selectedRows.sort((a, b) => moment(a.createAt).valueOf() - moment(b.createAt).valueOf());
}
handleExportTable(tableHeader, data);
}
}
const handleExportTable = (tableHeader, contentData, isAll = false) => {
let tableContent = '';
contentData.map(cd => {
tableContent += `<tr>`;
tableContent += `<th style="font-weight:600"><div>${cd.name}</div></th>`;
tableContent += `<th style="font-weight:600"><div>${cd.code}</div></th>`;
tableContent += `<th style="font-weight:600"><div>${cd.type}</div></th>`;
let tagName = cd.tagDatabases.map(tagSet => tagSet.tag.name);
tableContent += `<th style="font-weight:600"><div>${tagName.join(',')}</div></th>`;
tableContent += `<th style="font-weight:600"><div>${cd.user.username}</div></th>`;
tableContent += `<th style="font-weight:600"><div>${moment(cd.createAt).format('YYYY-MM-DD HH:mm:ss')}</div></th>`;
tableContent += `</tr>`;
})
let exportTable = `\uFEFF<table border="1">
${tableHeader}
${tableContent}
</table>`;
let tempStr = new Blob([exportTable], { type: 'text/plain;charset=utf-8' });
FileSaver.saveAs(tempStr, `库表元数据导出.xls`);
}
return <Spin spinning={isRequesting}>
<div style={{ marginBottom: 16 }}>
<Button type='primary' onClick={() => {
setEditData({ title: '新建库表元数据' });
setModalVisible(true);
}}>新建</Button>
{tableDataCount == 0 ? <Button disabled={tableDataCount == 0} style={{ marginLeft: 16 }} onClick={() => handleExport()}>导出</Button> :
selectedRowKeys && selectedRowKeys.length ?
<Button disabled={tableDataCount == 0} style={{ marginLeft: 16 }} onClick={() => handleExport()}>导出</Button>
: <Popconfirm title={'是否导出全部?'} onConfirm={() => handleExport(true)} okText="确定" cancelText="取消">
<Button disabled={tableDataCount == 0} style={{ marginLeft: 16 }}> 导出</Button>
</Popconfirm>
}
<Button type='primary' style={{ marginLeft: 16, float: 'right' }} onClick={onSearch}>查询</Button>
<Input style={{ width: 220, float: 'right' }} placeholder="输入名称/代码/类型"
allowClear onPressEnter={onSearch} onChange={e => setKeywords(e.target.value || '')} />
</div>
<Table
scroll={{ y: clientHeight - 320 }}
rowKey='id'
columns={columns}
dataSource={tableData}
onChange={onTableChange}
pagination={{
current: currentPage,
pageSize: limit,
total: tableDataCount,
showSizeChanger: true,
// showQuickJumper: true,
showTotal: (total) => { return <span style={{ fontSize: 15 }}>{`${Math.ceil(total / limit)}页,${total}`}</span> },
onShowSizeChange: (currentPage, pageSize) => {
setCurrentPage(currentPage);
setLimit(pageSize);
},
onChange: (page, pageSize) => {
setSelectedRowKeys([]);
setSelectedRows([]);
setCurrentPage(page);
setLimit(pageSize);
let queryParams = {
orderDirection: SortValues[createAtSort],
page: page - 1,
size: pageSize
};
initData(queryParams);
}
}}
rowSelection={{
onChange: (selectedRowKeys, selectedRows) => {
setSelectedRowKeys(selectedRowKeys)
setSelectedRows(selectedRows);
},
selectedRowKeys: selectedRowKeys
}}
>
</Table>
</Spin>
}
function mapStateToProps(state) {
const { global, auth, metadataDatabases } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
isRequesting: metadataDatabases.isRequesting,
};
}
export default connect(mapStateToProps)(DatabaseTable)

105
web/client/src/sections/metadataManagement/containers/filesTable.js

@ -0,0 +1,105 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Table, Popconfirm } from 'antd';
import moment from 'moment';
const FilesTable = (props) => {
const { user, dispatch, actions, clientHeight, resourceCatalogId, isRequesting } = props;
const { metadataManagement } = actions;
const [resourceCatalogData, setResourceCatalogData] = useState([]);
const [limit, setLimit] = useState(10)
const [offset, setOffset] = useState(0)
useEffect(() => {
initData(resourceCatalogId);
}, []);
const initData = (resourceCatalogId) => {
dispatch(metadataManagement.getMetadataFiles({ catalog: resourceCatalogId, limit, offset })).then(res => {
const { data } = res.payload;
if (res.success) {
}
})
}
const onEdit = (record) => { }
const confirmDelete = (id) => { }
const marking = (id) => { }
const applyResources = (id) => { }
const columns = [{
title: '文件名称',
dataIndex: 'name',
key: 'name',
width: '20%'
}, {
title: '文件描述',
dataIndex: 'description',
key: 'description',
width: '20%'
}, {
title: '文件类型',
dataIndex: 'type',
key: 'type',
width: '20%'
}, {
title: '标签',
dataIndex: 'tags',
key: 'tags',
width: '20%'
}, {
title: '大小',
dataIndex: 'size',
key: 'size',
width: '20%'
}, {
title: '创建者',
dataIndex: 'createBy',
key: 'createBy',
width: '10%',
}, {
title: '修改时间',
dataIndex: 'updateAt',
key: 'updateAt',
width: '20%',
render: (text, record, index) => {
return text ? moment(text).format('YYYY-MM-DD HH:mm') : ''
}
}, {
title: '操作',
dataIndex: 'action',
width: '20%',
render: (text, record) => {
return <div>
<a onClick={() => onEdit(record)}>编辑</a>
&nbsp;&nbsp;
<Popconfirm
title="是否确认删除该元数据?若确认删除则关联的数据将一并删除!"
onConfirm={() => confirmDelete(record.id)}
> <a>删除</a></Popconfirm>
&nbsp;&nbsp;
<a onClick={() => marking(record.id)}>打标</a>
&nbsp;&nbsp;
<a onClick={() => applyResources(record.id)}>申请资源</a>
</div>
}
}];
return <Spin spinning={isRequesting}>
<Table scroll={{ y: clientHeight - 320 }}
rowKey='filesId'
columns={columns}
dataSource={[]}>
</Table>
</Spin>
}
function mapStateToProps(state) {
const { global, auth, metadataFiles } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
isRequesting: metadataFiles.isRequesting
};
}
export default connect(mapStateToProps)(FilesTable)

9
web/client/src/sections/metadataManagement/containers/latestMetadata.js

@ -4,6 +4,7 @@ import { Spin, Row, Col, Tree, Button, Tooltip, Popconfirm } from 'antd';
import { PlusCircleOutlined, EditOutlined, MinusCircleOutlined } from '@ant-design/icons';
import theStyle from './style.css';
import ResourceCatalogModal from '../components/resourceCatalogModal';
import MetadataTab from './metadataTab';
let expandedKeysData = [];
const LatestMetadata = (props) => {
@ -14,6 +15,7 @@ const LatestMetadata = (props) => {
const [expandedKeys, setExpandedKeys] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [editData, setEditData] = useState({});
const [resourceCatalogId, setResourceCatalogId] = useState('');
useEffect(() => {
initData(true);
@ -28,6 +30,8 @@ const LatestMetadata = (props) => {
if (configRefresh) {
setExpandedKeys(expandedKeysData);
setSelectedKeys(expandedKeysData);
let id = expandedKeysData[0].split('-').pop();
setResourceCatalogId(id);
}
} else {
setExpandedKeys([]);
@ -120,11 +124,12 @@ const LatestMetadata = (props) => {
selectedKeys={selectedKeys}
onSelect={(keys, e) => {
if (e.selected) {
// setCurrentPage(1);
setSelectedKeys(keys);
// let keyArr = allTreeNodeKeys.filter(ak => ak.key.includes(keys[0]));
// let ids = keyArr.map(key => key.id);
// console.log('all-' + JSON.stringify(ids))
let id = keys[0].split('-').pop();
setResourceCatalogId(id);
}
}}
onExpand={(keys) => {
@ -134,7 +139,7 @@ const LatestMetadata = (props) => {
/>
</Col>
<Col style={{ width: 'calc(100% - 240px)' }}>
<MetadataTab resourceCatalogId={resourceCatalogId} />
</Col>
</Row>
{

51
web/client/src/sections/metadataManagement/containers/metadataTab.js

@ -0,0 +1,51 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Tabs } from 'antd';
import DatabaseTable from './databasesTable';
import FilesTable from './filesTable';
import RestapisTable from './restapisTable';
const MetadataTab = (props) => {
const { resourceCatalogId, actions, dispatch } = props;
const [activeKey, setActiveKey] = useState('databases');
useEffect(() => {
setActiveKey('databases');
}, [resourceCatalogId]);
const onTabChange = (key) => {
setActiveKey(key)
}
return <>
<Tabs defaultActiveKey="databases"
onChange={onTabChange}
activeKey={activeKey}
items={[
{
label: <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;库表&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>,
key: 'databases'
},
{
label: <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;文件&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>,
key: 'files'
},
{
label: <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;接口&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>,
key: 'restapis'
}
]}>
</Tabs>
{
activeKey === 'databases' && resourceCatalogId ? <DatabaseTable resourceCatalogId={resourceCatalogId} /> :
activeKey === 'files' && resourceCatalogId ? <FilesTable resourceCatalogId={resourceCatalogId} /> :
activeKey === 'restapis' && resourceCatalogId ? < RestapisTable resourceCatalogId={resourceCatalogId} /> : null
}
</>
}
function mapStateToProps(state) {
const { global, auth } = state;
return {
user: auth.user,
actions: global.actions
};
}
export default connect(mapStateToProps)(MetadataTab)

102
web/client/src/sections/metadataManagement/containers/restapisTable.js

@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Spin, Table, Popconfirm } from 'antd';
import moment from 'moment';
const RestapisTable = (props) => {
const { user, dispatch, actions, clientHeight, resourceCatalogId, isRequesting } = props;
const { metadataManagement } = actions;
const [resourceCatalogData, setResourceCatalogData] = useState([]);
const [limit, setLimit] = useState(10)
const [offset, setOffset] = useState(0)
useEffect(() => {
initData();
}, []);
const initData = () => {
dispatch(metadataManagement.getMetadataRestapis({ catalog: resourceCatalogId, limit, offset })).then(res => {
const { data } = res.payload;
if (res.success) {
}
})
}
const onEdit = (record) => { }
const confirmDelete = (id) => { }
const marking = (id) => { }
const applyResources = (id) => { }
const columns = [{
title: '接口名称',
dataIndex: 'name',
key: 'name',
width: '20%'
}, {
title: '接口路由',
dataIndex: 'url',
key: 'url',
width: '20%'
}, {
title: '接口类型',
dataIndex: 'method',
key: 'method',
width: '10%'
}, {
title: '传参',
dataIndex: 'queryParam',
key: 'queryParam',
width: '20%'
}, {
title: '返回值',
dataIndex: 'return',
key: 'return',
width: '20%'
}, {
title: '标签',
dataIndex: 'tags',
key: 'tags',
width: '20%'
}, {
title: '状态',
dataIndex: 'enabled',
key: 'enabled',
width: '10%'
}, {
title: '操作',
dataIndex: 'action',
width: '20%',
render: (text, record) => {
return <div>
<a onClick={() => onEdit(record)}>编辑</a>
&nbsp;&nbsp;
<Popconfirm
title="是否确认删除该元数据?若确认删除则关联的数据将一并删除!"
onConfirm={() => confirmDelete(record.id)}
> <a>删除</a></Popconfirm>
&nbsp;&nbsp;
<a onClick={() => marking(record.id)}>打标</a>
&nbsp;&nbsp;
<a onClick={() => applyResources(record.id)}>申请资源</a>
</div>
}
}];
return <Spin spinning={isRequesting}>
<Table scroll={{ y: clientHeight - 320 }}
rowKey='restapisId'
columns={columns}
dataSource={[]}>
</Table>
</Spin>
}
function mapStateToProps(state) {
const { global, auth, metadataRestapis } = state;
return {
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
isRequesting: metadataRestapis.isRequesting
};
}
export default connect(mapStateToProps)(RestapisTable)
Loading…
Cancel
Save