peng.peng 2 years ago
parent
commit
fb84fd15be
  1. 4
      api/app/lib/controllers/latestMetadata/index.js
  2. 4
      web/client/src/sections/metadataManagement/actions/index.js
  3. 53
      web/client/src/sections/metadataManagement/actions/metadata.js
  4. 86
      web/client/src/sections/metadataManagement/components/resourceCatalogModal.js
  5. 150
      web/client/src/sections/metadataManagement/containers/latestMetadata.js
  6. 16
      web/client/src/sections/metadataManagement/containers/style.css
  7. 14
      web/client/src/sections/metadataManagement/containers/tagManagement.js
  8. 2
      web/client/src/sections/metadataManagement/routes.js
  9. 5
      web/client/src/utils/webapi.js

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

@ -77,6 +77,7 @@ async function delResourceCatalog(ctx) {
const { id } = ctx.params;
let resourceCatalogInfo = await models.ResourceCatalog.findOne({ where: { id } });
if (resourceCatalogInfo) {
let deletable = true;
let childResourceCatalogInfo = await models.ResourceCatalog.findOne({ where: { parent: id } });
let databaseInfo = await models.MetadataDatabase.findOne({ where: { catalog: id } });
let fileInfo = await models.MetadataFile.findOne({ where: { catalog: id } });
@ -86,7 +87,6 @@ async function delResourceCatalog(ctx) {
ctx.body = { message: '存在关联子类目录或元数据,请删除相关数据,再删除该资源目录' }
deletable = false;
}
}
if (deletable) {
await models.ResourceCatalog.destroy({
where: { id: id },
@ -95,7 +95,7 @@ async function delResourceCatalog(ctx) {
await transaction.commit();
ctx.status = 200;
ctx.body = { message: '删除资源目录成功' }
}
} else {
ctx.status = 400;
ctx.body = { message: '该资源目录不存在' }

4
web/client/src/sections/metadataManagement/actions/index.js

@ -3,9 +3,11 @@
import * as example from './example'
import * as tags from './tags'
import * as models from './model'
import * as metadata from './metadata'
export default {
...example,
...tags,
...models
...models,
...metadata
}

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

@ -0,0 +1,53 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function getResourceCatalog(params) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: params,
actionType: 'GET_RESOURCE_CATALOG',
url: ApiTable.getResourceCatalog,
msg: { error: '获取资源目录失败' },
reducer: { name: 'resourceCatalog' }
});
}
export function postResourceCatalog(data) {
return dispatch => basicAction({
type: 'post',
data: data,
dispatch: dispatch,
actionType: 'POST_RESOURCE_CATALOG',
url: ApiTable.postResourceCatalog,
msg: { option: '新增资源目录' },
reducer: {}
});
}
export function putResourceCatalog(id, data) {
return dispatch => basicAction({
type: 'put',
data: data,
dispatch,
actionType: 'PUT_RESOURCE_CATALOG',
url: ApiTable.putResourceCatalog.replace('{id}', id),
msg: {
option: '修改资源目录',
}
});
}
export function delResourceCatalog(id) {
return dispatch => basicAction({
type: 'del',
dispatch,
actionType: 'DELETE_RESOURCE_CATALOG',
url: ApiTable.delResourceCatalog.replace('{id}', id),
msg: {
option: '删除资源目录',
}
});
}

86
web/client/src/sections/metadataManagement/components/resourceCatalogModal.js

@ -0,0 +1,86 @@
import React, { useEffect, useState } from 'react';
import { Modal, Input, Form, message } from 'antd';
const { TextArea } = Input;
const ResourceCatalogModal = (props) => {
const { resourceCatalog, onConfirm, onCancel, editData } = props;
const [form] = Form.useForm();
useEffect(() => {
}, []);
const handleOk = () => {
form.validateFields().then(values => {
if (onConfirm) {
if (editData.add) {
//新建
let exist = resourceCatalog.find(rc => rc.name === values.name);
if (exist) {
message.error('该资源目录名称已存在');
return false;
}
exist = resourceCatalog.find(rc => rc.code === values.code);
if (exist) {
message.error('该资源目录代码已存在');
return false;
}
} else {//修改
let exist = resourceCatalog.find(rc => rc.name === values.name && rc.id != editData.record.id);
if (exist) {
message.error('该资源目录名称已存在');
return false;
}
exist = resourceCatalog.find(rc => rc.code === values.code && rc.id != editData.record.id);
if (exist) {
message.error('该资源目录代码已存在');
return false;
}
}
onConfirm(values)
}
})
}
const validatorNull = (rule, value, getFieldValue, validateFields) => {
if (!value || !value.trim().length) {
return Promise.reject(new Error(`${rule.field === 'name' ? '名称' : '代码'}不可空字符串`));
}
return Promise.resolve();
}
return (
<Modal title={editData.title} visible={true} destroyOnClose
okText='确定' width={800}
onOk={() => handleOk(null)}
onCancel={onCancel}>
<Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 18 }} initialValues={editData.record || {}}>
{editData.child ? <Form.Item
label='上级目录'
name='parentName'
>
<Input disabled style={{ width: '90%' }} />
</Form.Item> : null}
<Form.Item
label='名称'
name='name'
rules={[{ required: true, message: '' }, { max: 50, message: `名称不超过50个字符` },
({ getFieldValue, validateFields }) => ({
validator(_, value) { return validatorNull(_, value, getFieldValue, validateFields) }
})]}>
<Input style={{ width: '90%' }} placeholder={`请输入名称`} />
</Form.Item>
<Form.Item
label='代码'
name='code'
rules={[{ required: true, message: '' }, { max: 50, message: `代码不超过50个字符` },
({ getFieldValue, validateFields }) => ({
validator(_, value) { return validatorNull(_, value, getFieldValue, validateFields) }
})]}>
<Input style={{ width: '90%' }} placeholder={`请输入代码`} />
</Form.Item>
<Form.Item
label='描述'
name='description'
rules={[{ max: 255, message: `描述不超过255个字符` }]}>
<TextArea rows={4} style={{ width: '90%' }} placeholder={`请输入描述`} />
</Form.Item>
</Form>
</Modal>
)
}
export default ResourceCatalogModal;

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

@ -1,24 +1,160 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Button, } from 'antd'
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';
let expandedKeysData = [];
const LatestMetadata = (props) => {
const { user, dispatch, actions, history } = props;
const { user, dispatch, actions, clientHeight, resourceCatalog, isRequesting } = props;
const { metadataManagement } = actions;
const [resourceCatalogData, setResourceCatalogData] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]);
const [expandedKeys, setExpandedKeys] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [editData, setEditData] = useState({});
useEffect(() => {
initData(true);
}, [])
const initData = (configRefresh) => {
dispatch(metadataManagement.getResourceCatalog()).then(res => {
const { data } = res.payload;
if (res.success) {
const resourceCatalogData = getTreeNodeData(data, null, 'rc');
setResourceCatalogData(resourceCatalogData);
if (data.length) {
if (configRefresh) {
setExpandedKeys(expandedKeysData);
setSelectedKeys(expandedKeysData);
}
} else {
setExpandedKeys([]);
setSelectedKeys([]);
};
}
})
}
const getTreeNodeData = (dataSource, parent, key) => {
let treeData = [];
let data = [];
if (!parent) {
data = dataSource.filter(ds => !ds.parent);
if (!expandedKeysData.length && data.length) {
expandedKeysData.push(`rc-${data[0].id}`);
}
} else {
data = dataSource.filter(ds => ds.parent == parent);
}
treeData = data.map(ds => {
return { title: renderTreeNode(ds, dataSource), key: `${key}-${ds.id}`, id: ds.id }
});
for (let d of treeData) {
d.children = getTreeNodeData(dataSource, d.id, d.key);
}
return treeData
}
return <>最新元数据
<Button type="primary" onClick={() => { history.push(`/metadataManagement/latestMetadata/detail/${1}`) }} >查看详情</Button>
</>
const setTreeNodeTitle = (name) => {
let content = <span>{name}</span>
if (name.length > 10) {
content = <Tooltip title={name}>
{name.substring(0, 10) + '...'}
</Tooltip>
}
return content;
}
const renderTreeNode = (ds, dataSource) => {
return <div className={theStyle.icon}>
{setTreeNodeTitle(ds.name)}
<EditOutlined title='修改' style={{ marginLeft: 10 }} className={theStyle.tip} onClick={() => {
let record = JSON.parse(JSON.stringify(ds));
let parentData = dataSource.filter(rc => rc.id === record.parent);
record.parentName = parentData.length ? parentData[0].name : '';
setEditData({ record: record, title: '修改子类', child: ds.parent ? true : false, });
setModalVisible(true);
}} />
<Popconfirm placement="top" title={'确定删除该资源目录吗?'} onConfirm={() => {
dispatch(metadataManagement.delResourceCatalog(ds.id)).then((res) => {
if (res.success) {
expandedKeysData = []; initData(true);
}
setModalVisible(false);
});
}} okText="确定" cancelText="取消">
<MinusCircleOutlined title='删除' style={{ marginLeft: 10 }} className={theStyle.tip} />
</Popconfirm>
<PlusCircleOutlined title='新建' style={{ marginLeft: 10 }} className={theStyle.tip} onClick={() => {
setEditData({ record: { parent: ds.id, parentName: ds.name }, title: '新建子类', child: true, add: true });
setModalVisible(true);
}} />
</div >
};
//新建、修改
const onConfirm = (values) => {
let obj = { ...values }
if (editData.add) {
obj = { ...values, ...editData.record || {} }
dispatch(metadataManagement.postResourceCatalog(obj)).then(() => {
initData(); setModalVisible(false);
});
} else {
dispatch(metadataManagement.putResourceCatalog(editData.record.id, obj)).then(() => {
initData(); setModalVisible(false);
});
}
}
return <Spin spinning={isRequesting}>
<Row>
<Col style={{ width: 240 }}>
<Button type='primary' style={{ marginBottom: 16 }} onClick={() => {
setEditData({ title: '新建资源目录', add: true });
setModalVisible(true);
}}>新建资源目录</Button>
<Tree
// showLine
height={clientHeight - 180}
expandedKeys={expandedKeys}
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))
}
}}
onExpand={(keys) => {
setExpandedKeys(keys);
}}
treeData={resourceCatalogData}
/>
</Col>
<Col style={{ width: 'calc(100% - 240px)' }}>
</Col>
</Row>
{
modalVisible ?
<ResourceCatalogModal
resourceCatalog={resourceCatalog}
editData={editData}
onCancel={() => setModalVisible(false)}
onConfirm={onConfirm} /> : ''
}
</Spin>
}
function mapStateToProps(state) {
const { global, auth } = state;
const { global, auth, resourceCatalog } = state;
return {
clientHeight: global.clientHeight,
user: auth.user,
actions: global.actions,
clientHeight: global.clientHeight,
resourceCatalog: resourceCatalog?.data || [],
isRequesting: resourceCatalog.isRequesting
};
}
export default connect(mapStateToProps)(LatestMetadata)

16
web/client/src/sections/metadataManagement/containers/style.css

@ -0,0 +1,16 @@
.icon .tip {
margin-left: 10px;
-webkit-transition: opacity 0.1s 0.2s;
opacity: 0;
pointer-events: none;
}
.icon:hover .tip {
-webkit-transition: opacity 0.2s;
opacity: 1;
pointer-events: auto;
}
.icon .tip:hover {
-webkit-transition: none;
}

14
web/client/src/sections/metadataManagement/containers/tagManagement.js

@ -16,17 +16,17 @@ const TagManagement = (props) => {
const [editData, setEditData] = useState({ title: '', lable: '', setTagSet: null })
useEffect(() => {
initData();
initData(true);
}, [])
const initData = (configRefresh) => {
dispatch(metadataManagement.getTagList()).then(res => {
const { data } = res.payload;
if (res.success) {
if (configRefresh)
if (!configRefresh)
setModalVisible(false);
setTagData(data);
if (data.length) {
if (!configRefresh)
if (configRefresh)
setActiveKey(`tagSet-${data[0].tagSetId}`)
} else setActiveKey('');
}
@ -96,20 +96,20 @@ const TagManagement = (props) => {
if (editData.setEditData) {//标签集
if (editData.name)//修改
dispatch(metadataManagement.putTagSets(editData.id, { ...values })).then(() => {
initData(true);
initData();
});
else//新建
dispatch(metadataManagement.postTagSets({ ...values })).then(() => {
initData(true);
initData();
});
} else {
if (editData.name)//修改
dispatch(metadataManagement.putTags(editData.id, { ...values })).then(() => {
initData(true);
initData();
});
else//新建
dispatch(metadataManagement.postTags({ tagSetId: editData.tagSetId, ...values })).then(() => {
initData(true);
initData();
});
}
}

2
web/client/src/sections/metadataManagement/routes.js

@ -14,7 +14,7 @@ export default [{
breadcrumb: '最新元数据',
childRoutes: [{
path: '/detail/:id',
key: 'detail',
key: 'metadataDetail',
component: MetadataDetails,
breadcrumb: '元数据详情'
}]

5
web/client/src/utils/webapi.js

@ -17,6 +17,11 @@ export const ApiTable = {
getMetaModelList: 'meta/models',
addMetaModel: 'meta/model',
modifyMetaModel: 'meta/model/{id}',
//最新元数据-资源目录
getResourceCatalog: 'resource-catalog',
postResourceCatalog: 'resource-catalog',
putResourceCatalog: 'resource-catalog/{id}',
delResourceCatalog: 'resource-catalog/{id}',
//元数据采集
pgCheckConnect: 'adapter/check/connect',

Loading…
Cancel
Save