peng.peng
2 years ago
10 changed files with 618 additions and 21 deletions
@ -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); |
@ -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); |
@ -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) |
@ -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> |
||||
|
|
||||
|
<Popconfirm |
||||
|
title="是否确认删除该元数据?若确认删除则关联的数据将一并删除!" |
||||
|
onConfirm={() => confirmDelete(record.id)} |
||||
|
> <a>删除</a></Popconfirm> |
||||
|
|
||||
|
<a onClick={() => marking(record.id)}>打标</a> |
||||
|
|
||||
|
<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) |
@ -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> 库表 </span>, |
||||
|
key: 'databases' |
||||
|
}, |
||||
|
{ |
||||
|
label: <span> 文件 </span>, |
||||
|
key: 'files' |
||||
|
}, |
||||
|
{ |
||||
|
label: <span> 接口 </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) |
@ -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> |
||||
|
|
||||
|
<Popconfirm |
||||
|
title="是否确认删除该元数据?若确认删除则关联的数据将一并删除!" |
||||
|
onConfirm={() => confirmDelete(record.id)} |
||||
|
> <a>删除</a></Popconfirm> |
||||
|
|
||||
|
<a onClick={() => marking(record.id)}>打标</a> |
||||
|
|
||||
|
<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…
Reference in new issue